Skip to content

Commit 361dbb7

Browse files
committed
breaking things is my hobby
1 parent 68d3b18 commit 361dbb7

File tree

18 files changed

+456
-44
lines changed

18 files changed

+456
-44
lines changed

exampleVault/Input Fields/Slider.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
slider1: 23
3-
slider2: 13
4-
slider3: 319
2+
slider1: 51
3+
slider2: 2
4+
slider3: 233
55
---
66

77
### Simple Slider

exampleVault/Input Fields/Text.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
text: text
2+
text: test
33
textArea: textArea
44
---
55

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
---
2+
toggle2: 14
23
toggle1: false
3-
toggle2: 1
44
---
55

6+
67
```meta-bind
78
INPUT[toggle(showcase):toggle1]
89
```
910

1011
```meta-bind
11-
INPUT[toggle(showcase, onValue(1), offValue(0)):toggle2]
12+
INPUT[toggle(showcase, onValue(1), offValue(0), defaultValue(1)):toggle2]
1213
```

src/IPlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { IAPI } from './api/IAPI';
2+
import { MetaBindPluginSettings } from './settings/Settings';
23

34
export interface IPlugin {
45
api: IAPI;
6+
settings: MetaBindPluginSettings;
57
getFilePathsByName: (name: string) => string[];
68
}

src/api/API.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,29 @@ import { BindTargetDeclaration, InputFieldDeclaration, UnvalidatedInputFieldDecl
1818
import { Signal } from '../utils/Signal';
1919
import { BindTargetScope } from '../metadata/BindTargetScope';
2020
import { MetaBindTable } from '../metaBindTable/MetaBindTable';
21+
import { NewInputFieldFactory } from '../inputFields/_new/NewInputFieldFactory';
2122

2223
export class API implements IAPI {
2324
public plugin: MetaBindPlugin;
25+
public readonly inputField: InputFieldAPI;
26+
2427
// public inputFieldParser: InputFieldDeclarationParser;
2528
public readonly inputFieldParser: InputFieldDeclarationParser;
2629
public readonly viewFieldParser: ViewFieldDeclarationParser;
2730
public readonly bindTargetParser: BindTargetParser;
2831

29-
public readonly inputField: InputFieldAPI;
32+
public readonly inputFieldFactory: NewInputFieldFactory;
3033

3134
constructor(plugin: MetaBindPlugin) {
3235
this.plugin = plugin;
36+
this.inputField = new InputFieldAPI(this);
3337

3438
// this.inputFieldParser = new InputFieldDeclarationParser();
3539
this.inputFieldParser = new InputFieldDeclarationParser(this.plugin);
3640
this.viewFieldParser = new ViewFieldDeclarationParser(this.plugin);
3741
this.bindTargetParser = new BindTargetParser(this.plugin);
3842

39-
this.inputField = new InputFieldAPI(this);
43+
this.inputFieldFactory = new NewInputFieldFactory(this.plugin);
4044
}
4145

4246
public createInputField(
@@ -64,13 +68,13 @@ export class API implements IAPI {
6468
filePath: string,
6569
containerEl: HTMLElement,
6670
component: Component | MarkdownPostProcessorContext,
67-
scope: BindTargetScope
71+
scope: BindTargetScope | undefined
6872
): InputFieldMDRC | ExcludedMDRC {
6973
if (this.plugin.isFilePathExcluded(filePath)) {
7074
return this.createExcludedField(containerEl, filePath, component);
7175
}
7276

73-
const declaration: InputFieldDeclaration = this.inputFieldParser.parseString(fullDeclaration);
77+
const declaration: InputFieldDeclaration = this.inputFieldParser.parseString(fullDeclaration, scope);
7478

7579
const inputField = new InputFieldMDRC(containerEl, renderType, declaration, this.plugin, filePath, self.crypto.randomUUID());
7680
component.addChild(inputField);

src/api/IAPI.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import { ViewFieldDeclarationParser } from '../parsers/ViewFieldDeclarationParse
33
import { BindTargetParser } from '../parsers/BindTargetParser';
44
import { IPlugin } from '../IPlugin';
55
import { InputFieldAPI } from './InputFieldAPI';
6+
import { NewInputFieldFactory } from '../inputFields/_new/NewInputFieldFactory';
67

78
export interface IAPI {
89
readonly plugin: IPlugin;
10+
readonly inputField: InputFieldAPI;
11+
912
readonly inputFieldParser: InputFieldDeclarationParser;
1013
readonly viewFieldParser: ViewFieldDeclarationParser;
1114
readonly bindTargetParser: BindTargetParser;
12-
readonly inputField: InputFieldAPI;
15+
16+
readonly inputFieldFactory: NewInputFieldFactory;
1317
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { SvelteComponent } from 'svelte';
2+
import { Listener, Notifier } from '../../utils/Signal';
3+
4+
export class InputFieldComponent<Value> extends Notifier<Value, Listener<Value>> {
5+
private readonly svelteComponent: typeof SvelteComponent;
6+
private svelteComponentInstance?: SvelteComponent;
7+
8+
constructor(svelteComponent: typeof SvelteComponent) {
9+
super();
10+
11+
this.svelteComponent = svelteComponent;
12+
}
13+
14+
/**
15+
* This sets the value without triggering an update.
16+
*
17+
* @param value
18+
*/
19+
public setValue(value: Value): void {
20+
this.svelteComponentInstance?.setValue(value);
21+
}
22+
23+
/**
24+
* This mounts the component to the container element.
25+
* Don't forget to call unmount.
26+
*
27+
* @param container
28+
* @param initialValue
29+
* @param mountArgs
30+
*/
31+
public mount(container: HTMLElement, initialValue: Value, mountArgs: Record<string, any> = {}): void {
32+
const props = Object.assign(
33+
{
34+
value: initialValue,
35+
onValueChange: (value: Value) => {
36+
console.log('prop value change');
37+
this.notifyListeners(value);
38+
},
39+
},
40+
mountArgs
41+
);
42+
43+
this.svelteComponentInstance = new this.svelteComponent({
44+
target: container,
45+
props: props,
46+
});
47+
}
48+
49+
/**
50+
* This unmounts the component.
51+
*/
52+
public unmount(): void {
53+
this.listeners = [];
54+
this.svelteComponentInstance?.$destroy();
55+
}
56+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { InputFieldMDRC } from '../../renderChildren/InputFieldMDRC';
2+
import { InputFieldComponent } from './InputFieldComponent';
3+
import { SvelteComponent } from 'svelte';
4+
import { ComputedSignal, Listener, Notifier } from '../../utils/Signal';
5+
import { InputFieldArgumentType } from '../InputFieldConfigs';
6+
import { DefaultValueInputFieldArgument } from '../../inputFieldArguments/arguments/DefaultValueInputFieldArgument';
7+
8+
export abstract class NewAbstractInputField<MetadataValueType, ComponentValueType> extends Notifier<MetadataValueType, Listener<MetadataValueType>> {
9+
readonly renderChild: InputFieldMDRC;
10+
readonly inputFieldComponent: InputFieldComponent<ComponentValueType>;
11+
readonly signal: ComputedSignal<any, MetadataValueType>;
12+
13+
protected constructor(renderChild: InputFieldMDRC) {
14+
super();
15+
16+
this.renderChild = renderChild;
17+
this.inputFieldComponent = new InputFieldComponent<ComponentValueType>(this.getSvelteComponent());
18+
19+
this.signal = new ComputedSignal<any, MetadataValueType>(this.renderChild.writeSignal, (value: any): MetadataValueType => {
20+
const filteredValue = this.filterValue(value);
21+
return filteredValue !== undefined ? filteredValue : this.getDefaultValue();
22+
});
23+
24+
this.signal.registerListener({
25+
callback: value => this.inputFieldComponent.setValue(this.reverseMapValue(value)),
26+
});
27+
28+
this.inputFieldComponent.registerListener({
29+
callback: value => {
30+
console.log('input field component change', value);
31+
this.notifyListeners(this.mapValue(value));
32+
},
33+
});
34+
}
35+
36+
protected abstract getSvelteComponent(): typeof SvelteComponent;
37+
38+
/**
39+
* Takes in any value and returns the value if it is type of `T`, `undefined` otherwise.
40+
*
41+
* @param value
42+
*/
43+
protected abstract filterValue(value: any): MetadataValueType | undefined;
44+
45+
protected abstract getFallbackDefaultValue(): ComponentValueType;
46+
47+
/**
48+
* Maps a metadata value to a component value. If the value can't be mapped, the function should return `undefined`.
49+
*
50+
* @param value
51+
*/
52+
protected abstract rawReverseMapValue(value: MetadataValueType): ComponentValueType | undefined;
53+
protected abstract rawMapValue(value: ComponentValueType): MetadataValueType;
54+
55+
private reverseMapValue(value: MetadataValueType): ComponentValueType {
56+
const mappedValue = this.rawReverseMapValue(value);
57+
if (mappedValue !== undefined) {
58+
return mappedValue;
59+
}
60+
const mappedDefaultValue = this.rawReverseMapValue(this.getDefaultValue());
61+
if (mappedDefaultValue !== undefined) {
62+
return mappedDefaultValue;
63+
}
64+
return this.getFallbackDefaultValue();
65+
}
66+
67+
private mapValue(value: ComponentValueType): MetadataValueType {
68+
return this.rawMapValue(value);
69+
}
70+
71+
private getValue(): MetadataValueType {
72+
return this.signal.get();
73+
}
74+
75+
private getDefaultValue(): MetadataValueType {
76+
const defaultValueArgument = this.renderChild.getArgument(InputFieldArgumentType.DEFAULT_VALUE) as DefaultValueInputFieldArgument | undefined;
77+
if (!defaultValueArgument) {
78+
return this.mapValue(this.getFallbackDefaultValue());
79+
}
80+
const filteredValue = this.filterValue(defaultValueArgument.value);
81+
return filteredValue !== undefined ? filteredValue : this.mapValue(this.getFallbackDefaultValue());
82+
}
83+
84+
protected getMountArgs(): Record<string, any> {
85+
return {};
86+
}
87+
88+
public mount(container: HTMLElement): void {
89+
this.inputFieldComponent.mount(container, this.reverseMapValue(this.getValue()), this.getMountArgs());
90+
}
91+
92+
public unmount(): void {
93+
this.inputFieldComponent.unmount();
94+
}
95+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { InputFieldConfig, InputFieldConfigs, InputFieldType } from '../InputFieldConfigs';
2+
import { InputFieldMDRC, RenderChildType } from '../../renderChildren/InputFieldMDRC';
3+
import { ErrorLevel, MetaBindParsingError } from '../../utils/errors/MetaBindErrors';
4+
import { IPlugin } from '../../IPlugin';
5+
import { Toggle } from './fields/Toggle/Toggle';
6+
import { Text } from './fields/Text/Text';
7+
import { Slider } from './fields/Slider/Slider';
8+
9+
export type NewInputField = Toggle | Slider | Text;
10+
11+
export class NewInputFieldFactory {
12+
plugin: IPlugin;
13+
14+
constructor(plugin: IPlugin) {
15+
this.plugin = plugin;
16+
}
17+
18+
createInputField(type: InputFieldType, renderChildType: RenderChildType, renderChild: InputFieldMDRC): NewInputField | undefined {
19+
if (type !== InputFieldType.INVALID) {
20+
this.checkRenderChildTypeAllowed(type, renderChildType);
21+
}
22+
23+
if (type === InputFieldType.TOGGLE) {
24+
return new Toggle(renderChild);
25+
} else if (type === InputFieldType.SLIDER) {
26+
return new Slider(renderChild);
27+
} else if (type === InputFieldType.TEXT) {
28+
return new Text(renderChild);
29+
}
30+
31+
return undefined;
32+
}
33+
34+
checkRenderChildTypeAllowed(type: InputFieldType, renderChildType: RenderChildType): void {
35+
if (this.plugin.settings.ignoreCodeBlockRestrictions) {
36+
return;
37+
}
38+
39+
const inputFieldConfig: InputFieldConfig = InputFieldConfigs[type];
40+
if (renderChildType === RenderChildType.BLOCK && !inputFieldConfig.allowInBlock) {
41+
throw new MetaBindParsingError(ErrorLevel.CRITICAL, 'can not create input field', `'${type}' is not allowed as code block`);
42+
}
43+
if (renderChildType === RenderChildType.INLINE && !inputFieldConfig.allowInline) {
44+
throw new MetaBindParsingError(ErrorLevel.CRITICAL, 'can not create input field', `'${type}' is not allowed as inline code block`);
45+
}
46+
}
47+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { NewAbstractInputField } from '../../NewAbstractInputField';
2+
import { clamp } from '../../../../utils/Utils';
3+
import { InputFieldMDRC } from '../../../../renderChildren/InputFieldMDRC';
4+
import { SvelteComponent } from 'svelte';
5+
import SliderComponent from './SliderComponent.svelte';
6+
import { InputFieldArgumentType } from '../../../InputFieldConfigs';
7+
8+
export class Slider extends NewAbstractInputField<number, number> {
9+
minValue: number;
10+
maxValue: number;
11+
12+
constructor(renderChild: InputFieldMDRC) {
13+
super(renderChild);
14+
15+
this.minValue = this.renderChild.getArgument(InputFieldArgumentType.MIN_VALUE)?.value ?? 0;
16+
this.maxValue = this.renderChild.getArgument(InputFieldArgumentType.MAX_VALUE)?.value ?? 100;
17+
}
18+
19+
protected filterValue(value: any): number | undefined {
20+
if (typeof value === 'number') {
21+
return clamp(value, this.minValue, this.maxValue);
22+
} else if (typeof value === 'string') {
23+
const v = Number.parseFloat(value);
24+
if (Number.isNaN(v)) {
25+
return undefined;
26+
} else {
27+
return clamp(v, this.minValue, this.maxValue);
28+
}
29+
} else {
30+
return undefined;
31+
}
32+
}
33+
34+
protected getFallbackDefaultValue(): number {
35+
return this.minValue;
36+
}
37+
38+
protected getSvelteComponent(): typeof SvelteComponent {
39+
return SliderComponent;
40+
}
41+
42+
protected rawReverseMapValue(value: number): number | undefined {
43+
return value;
44+
}
45+
46+
protected rawMapValue(value: number): number {
47+
return value;
48+
}
49+
50+
protected getMountArgs(): Record<string, any> {
51+
return {
52+
minValue: this.minValue,
53+
maxValue: this.maxValue,
54+
addLabels: this.renderChild.getArgument(InputFieldArgumentType.ADD_LABELS)?.value === true,
55+
};
56+
}
57+
}

0 commit comments

Comments
 (0)