Skip to content

Commit

Permalink
breaking things is my hobby
Browse files Browse the repository at this point in the history
  • Loading branch information
mProjectsCode committed Oct 6, 2023
1 parent 68d3b18 commit 361dbb7
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 44 deletions.
6 changes: 3 additions & 3 deletions exampleVault/Input Fields/Slider.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
slider1: 23
slider2: 13
slider3: 319
slider1: 51
slider2: 2
slider3: 233
---

### Simple Slider
Expand Down
2 changes: 1 addition & 1 deletion exampleVault/Input Fields/Text.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
text: text
text: test
textArea: textArea
---

Expand Down
5 changes: 3 additions & 2 deletions exampleVault/Input Fields/Toggle.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
---
toggle2: 14
toggle1: false
toggle2: 1
---


```meta-bind
INPUT[toggle(showcase):toggle1]
```

```meta-bind
INPUT[toggle(showcase, onValue(1), offValue(0)):toggle2]
INPUT[toggle(showcase, onValue(1), offValue(0), defaultValue(1)):toggle2]
```
2 changes: 2 additions & 0 deletions src/IPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { IAPI } from './api/IAPI';
import { MetaBindPluginSettings } from './settings/Settings';

export interface IPlugin {
api: IAPI;
settings: MetaBindPluginSettings;
getFilePathsByName: (name: string) => string[];
}
12 changes: 8 additions & 4 deletions src/api/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,29 @@ import { BindTargetDeclaration, InputFieldDeclaration, UnvalidatedInputFieldDecl
import { Signal } from '../utils/Signal';
import { BindTargetScope } from '../metadata/BindTargetScope';
import { MetaBindTable } from '../metaBindTable/MetaBindTable';
import { NewInputFieldFactory } from '../inputFields/_new/NewInputFieldFactory';

export class API implements IAPI {
public plugin: MetaBindPlugin;
public readonly inputField: InputFieldAPI;

// public inputFieldParser: InputFieldDeclarationParser;
public readonly inputFieldParser: InputFieldDeclarationParser;
public readonly viewFieldParser: ViewFieldDeclarationParser;
public readonly bindTargetParser: BindTargetParser;

public readonly inputField: InputFieldAPI;
public readonly inputFieldFactory: NewInputFieldFactory;

constructor(plugin: MetaBindPlugin) {
this.plugin = plugin;
this.inputField = new InputFieldAPI(this);

// this.inputFieldParser = new InputFieldDeclarationParser();
this.inputFieldParser = new InputFieldDeclarationParser(this.plugin);
this.viewFieldParser = new ViewFieldDeclarationParser(this.plugin);
this.bindTargetParser = new BindTargetParser(this.plugin);

this.inputField = new InputFieldAPI(this);
this.inputFieldFactory = new NewInputFieldFactory(this.plugin);
}

public createInputField(
Expand Down Expand Up @@ -64,13 +68,13 @@ export class API implements IAPI {
filePath: string,
containerEl: HTMLElement,
component: Component | MarkdownPostProcessorContext,
scope: BindTargetScope
scope: BindTargetScope | undefined
): InputFieldMDRC | ExcludedMDRC {
if (this.plugin.isFilePathExcluded(filePath)) {
return this.createExcludedField(containerEl, filePath, component);
}

const declaration: InputFieldDeclaration = this.inputFieldParser.parseString(fullDeclaration);
const declaration: InputFieldDeclaration = this.inputFieldParser.parseString(fullDeclaration, scope);

const inputField = new InputFieldMDRC(containerEl, renderType, declaration, this.plugin, filePath, self.crypto.randomUUID());
component.addChild(inputField);
Expand Down
6 changes: 5 additions & 1 deletion src/api/IAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { ViewFieldDeclarationParser } from '../parsers/ViewFieldDeclarationParse
import { BindTargetParser } from '../parsers/BindTargetParser';
import { IPlugin } from '../IPlugin';
import { InputFieldAPI } from './InputFieldAPI';
import { NewInputFieldFactory } from '../inputFields/_new/NewInputFieldFactory';

export interface IAPI {
readonly plugin: IPlugin;
readonly inputField: InputFieldAPI;

readonly inputFieldParser: InputFieldDeclarationParser;
readonly viewFieldParser: ViewFieldDeclarationParser;
readonly bindTargetParser: BindTargetParser;
readonly inputField: InputFieldAPI;

readonly inputFieldFactory: NewInputFieldFactory;
}
56 changes: 56 additions & 0 deletions src/inputFields/_new/InputFieldComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { SvelteComponent } from 'svelte';
import { Listener, Notifier } from '../../utils/Signal';

export class InputFieldComponent<Value> extends Notifier<Value, Listener<Value>> {
private readonly svelteComponent: typeof SvelteComponent;
private svelteComponentInstance?: SvelteComponent;

constructor(svelteComponent: typeof SvelteComponent) {
super();

this.svelteComponent = svelteComponent;
}

/**
* This sets the value without triggering an update.
*
* @param value
*/
public setValue(value: Value): void {
this.svelteComponentInstance?.setValue(value);
}

/**
* This mounts the component to the container element.
* Don't forget to call unmount.
*
* @param container
* @param initialValue
* @param mountArgs
*/
public mount(container: HTMLElement, initialValue: Value, mountArgs: Record<string, any> = {}): void {
const props = Object.assign(
{
value: initialValue,
onValueChange: (value: Value) => {
console.log('prop value change');
this.notifyListeners(value);
},
},
mountArgs
);

this.svelteComponentInstance = new this.svelteComponent({
target: container,
props: props,
});
}

/**
* This unmounts the component.
*/
public unmount(): void {
this.listeners = [];
this.svelteComponentInstance?.$destroy();
}
}
95 changes: 95 additions & 0 deletions src/inputFields/_new/NewAbstractInputField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { InputFieldMDRC } from '../../renderChildren/InputFieldMDRC';
import { InputFieldComponent } from './InputFieldComponent';
import { SvelteComponent } from 'svelte';
import { ComputedSignal, Listener, Notifier } from '../../utils/Signal';
import { InputFieldArgumentType } from '../InputFieldConfigs';
import { DefaultValueInputFieldArgument } from '../../inputFieldArguments/arguments/DefaultValueInputFieldArgument';

export abstract class NewAbstractInputField<MetadataValueType, ComponentValueType> extends Notifier<MetadataValueType, Listener<MetadataValueType>> {
readonly renderChild: InputFieldMDRC;
readonly inputFieldComponent: InputFieldComponent<ComponentValueType>;
readonly signal: ComputedSignal<any, MetadataValueType>;

protected constructor(renderChild: InputFieldMDRC) {
super();

this.renderChild = renderChild;
this.inputFieldComponent = new InputFieldComponent<ComponentValueType>(this.getSvelteComponent());

this.signal = new ComputedSignal<any, MetadataValueType>(this.renderChild.writeSignal, (value: any): MetadataValueType => {
const filteredValue = this.filterValue(value);
return filteredValue !== undefined ? filteredValue : this.getDefaultValue();
});

this.signal.registerListener({
callback: value => this.inputFieldComponent.setValue(this.reverseMapValue(value)),
});

this.inputFieldComponent.registerListener({
callback: value => {
console.log('input field component change', value);
this.notifyListeners(this.mapValue(value));
},
});
}

protected abstract getSvelteComponent(): typeof SvelteComponent;

/**
* Takes in any value and returns the value if it is type of `T`, `undefined` otherwise.
*
* @param value
*/
protected abstract filterValue(value: any): MetadataValueType | undefined;

protected abstract getFallbackDefaultValue(): ComponentValueType;

/**
* Maps a metadata value to a component value. If the value can't be mapped, the function should return `undefined`.
*
* @param value
*/
protected abstract rawReverseMapValue(value: MetadataValueType): ComponentValueType | undefined;
protected abstract rawMapValue(value: ComponentValueType): MetadataValueType;

private reverseMapValue(value: MetadataValueType): ComponentValueType {
const mappedValue = this.rawReverseMapValue(value);
if (mappedValue !== undefined) {
return mappedValue;
}
const mappedDefaultValue = this.rawReverseMapValue(this.getDefaultValue());
if (mappedDefaultValue !== undefined) {
return mappedDefaultValue;
}
return this.getFallbackDefaultValue();
}

private mapValue(value: ComponentValueType): MetadataValueType {
return this.rawMapValue(value);
}

private getValue(): MetadataValueType {
return this.signal.get();
}

private getDefaultValue(): MetadataValueType {
const defaultValueArgument = this.renderChild.getArgument(InputFieldArgumentType.DEFAULT_VALUE) as DefaultValueInputFieldArgument | undefined;
if (!defaultValueArgument) {
return this.mapValue(this.getFallbackDefaultValue());
}
const filteredValue = this.filterValue(defaultValueArgument.value);
return filteredValue !== undefined ? filteredValue : this.mapValue(this.getFallbackDefaultValue());
}

protected getMountArgs(): Record<string, any> {
return {};
}

public mount(container: HTMLElement): void {
this.inputFieldComponent.mount(container, this.reverseMapValue(this.getValue()), this.getMountArgs());
}

public unmount(): void {
this.inputFieldComponent.unmount();
}
}
47 changes: 47 additions & 0 deletions src/inputFields/_new/NewInputFieldFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { InputFieldConfig, InputFieldConfigs, InputFieldType } from '../InputFieldConfigs';
import { InputFieldMDRC, RenderChildType } from '../../renderChildren/InputFieldMDRC';
import { ErrorLevel, MetaBindParsingError } from '../../utils/errors/MetaBindErrors';
import { IPlugin } from '../../IPlugin';
import { Toggle } from './fields/Toggle/Toggle';
import { Text } from './fields/Text/Text';
import { Slider } from './fields/Slider/Slider';

export type NewInputField = Toggle | Slider | Text;

export class NewInputFieldFactory {
plugin: IPlugin;

constructor(plugin: IPlugin) {
this.plugin = plugin;
}

createInputField(type: InputFieldType, renderChildType: RenderChildType, renderChild: InputFieldMDRC): NewInputField | undefined {
if (type !== InputFieldType.INVALID) {
this.checkRenderChildTypeAllowed(type, renderChildType);
}

if (type === InputFieldType.TOGGLE) {
return new Toggle(renderChild);
} else if (type === InputFieldType.SLIDER) {
return new Slider(renderChild);
} else if (type === InputFieldType.TEXT) {
return new Text(renderChild);
}

return undefined;
}

checkRenderChildTypeAllowed(type: InputFieldType, renderChildType: RenderChildType): void {
if (this.plugin.settings.ignoreCodeBlockRestrictions) {
return;
}

const inputFieldConfig: InputFieldConfig = InputFieldConfigs[type];
if (renderChildType === RenderChildType.BLOCK && !inputFieldConfig.allowInBlock) {
throw new MetaBindParsingError(ErrorLevel.CRITICAL, 'can not create input field', `'${type}' is not allowed as code block`);
}
if (renderChildType === RenderChildType.INLINE && !inputFieldConfig.allowInline) {
throw new MetaBindParsingError(ErrorLevel.CRITICAL, 'can not create input field', `'${type}' is not allowed as inline code block`);
}
}
}
57 changes: 57 additions & 0 deletions src/inputFields/_new/fields/Slider/Slider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { NewAbstractInputField } from '../../NewAbstractInputField';
import { clamp } from '../../../../utils/Utils';
import { InputFieldMDRC } from '../../../../renderChildren/InputFieldMDRC';
import { SvelteComponent } from 'svelte';
import SliderComponent from './SliderComponent.svelte';
import { InputFieldArgumentType } from '../../../InputFieldConfigs';

export class Slider extends NewAbstractInputField<number, number> {
minValue: number;
maxValue: number;

constructor(renderChild: InputFieldMDRC) {
super(renderChild);

this.minValue = this.renderChild.getArgument(InputFieldArgumentType.MIN_VALUE)?.value ?? 0;
this.maxValue = this.renderChild.getArgument(InputFieldArgumentType.MAX_VALUE)?.value ?? 100;
}

protected filterValue(value: any): number | undefined {
if (typeof value === 'number') {
return clamp(value, this.minValue, this.maxValue);
} else if (typeof value === 'string') {
const v = Number.parseFloat(value);
if (Number.isNaN(v)) {
return undefined;
} else {
return clamp(v, this.minValue, this.maxValue);
}
} else {
return undefined;
}
}

protected getFallbackDefaultValue(): number {
return this.minValue;
}

protected getSvelteComponent(): typeof SvelteComponent {
return SliderComponent;
}

protected rawReverseMapValue(value: number): number | undefined {
return value;
}

protected rawMapValue(value: number): number {
return value;
}

protected getMountArgs(): Record<string, any> {
return {
minValue: this.minValue,
maxValue: this.maxValue,
addLabels: this.renderChild.getArgument(InputFieldArgumentType.ADD_LABELS)?.value === true,
};
}
}
21 changes: 21 additions & 0 deletions src/inputFields/_new/fields/Slider/SliderComponent.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
export let value: number;
export let minValue: number;
export let maxValue: number;
export let addLabels: boolean;
export let onValueChange: (value: number) => void;
export function setValue(v: number): void {
value = v;
}
</script>

{#if addLabels}
<span class="meta-bind-plugin-slider-input-label">{minValue}</span>
<input type="range" tabindex="0" min={minValue} max={maxValue} bind:value={value} on:input={() => onValueChange(value)}>
<span class="meta-bind-plugin-slider-input-label">{maxValue}</span>
{:else}
<input type="range" tabindex="0" min={minValue} max={maxValue} bind:value={value} on:input={() => onValueChange(value)}>
{/if}


Loading

0 comments on commit 361dbb7

Please sign in to comment.