Skip to content

Commit 6b28c62

Browse files
committed
improve view field perf
1 parent fd43d78 commit 6b28c62

30 files changed

+434
-233
lines changed

exampleVault/View Fields/View Field.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ object:
1313
file: Example Note with Embeds
1414
image: Other/Images/subfolder/img_frozen_branch.jpg
1515
someInputValue: 1
16-
someComputedValue: 2
16+
someComputedValue:
17+
value: 2
18+
error: false
1719
images:
1820
- Other/Images/img_flower.webp
1921
- Other/Images/img_butterfly.webp

packages/core/src/config/ButtonConfig.ts

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,27 @@ export interface InlineJSButtonAction {
126126
args?: Record<string, unknown>;
127127
}
128128

129-
export type ButtonAction =
130-
| CommandButtonAction
131-
| JSButtonAction
132-
| OpenButtonAction
133-
| InputButtonAction
134-
| SleepButtonAction
135-
| TemplaterCreateNoteButtonAction
136-
| UpdateMetadataButtonAction
137-
| CreateNoteButtonAction
138-
| ReplaceInNoteButtonAction
139-
| ReplaceSelfButtonAction
140-
| RegexpReplaceInNoteButtonAction
141-
| InsertIntoNoteButtonAction
142-
| InlineJSButtonAction
143-
| RunTemplaterFileButtonAction;
129+
/**
130+
* Maps action types to their respective action interfaces.
131+
*/
132+
export interface ButtonActionMap {
133+
[ButtonActionType.COMMAND]: CommandButtonAction;
134+
[ButtonActionType.JS]: JSButtonAction;
135+
[ButtonActionType.OPEN]: OpenButtonAction;
136+
[ButtonActionType.INPUT]: InputButtonAction;
137+
[ButtonActionType.SLEEP]: SleepButtonAction;
138+
[ButtonActionType.TEMPLATER_CREATE_NOTE]: TemplaterCreateNoteButtonAction;
139+
[ButtonActionType.UPDATE_METADATA]: UpdateMetadataButtonAction;
140+
[ButtonActionType.CREATE_NOTE]: CreateNoteButtonAction;
141+
[ButtonActionType.REPLACE_IN_NOTE]: ReplaceInNoteButtonAction;
142+
[ButtonActionType.REPLACE_SELF]: ReplaceSelfButtonAction;
143+
[ButtonActionType.REGEXP_REPLACE_IN_NOTE]: RegexpReplaceInNoteButtonAction;
144+
[ButtonActionType.INSERT_INTO_NOTE]: InsertIntoNoteButtonAction;
145+
[ButtonActionType.INLINE_JS]: InlineJSButtonAction;
146+
[ButtonActionType.RUN_TEMPLATER_FILE]: RunTemplaterFileButtonAction;
147+
}
148+
149+
export type ButtonAction = ButtonActionMap[ButtonActionType];
144150

145151
export interface ButtonConfig {
146152
/**
@@ -218,23 +224,3 @@ export enum ButtonClickType {
218224
*/
219225
MIDDLE = 'middle',
220226
}
221-
222-
/**
223-
* Maps action types to their respective action interfaces.
224-
*/
225-
export interface ButtonActionMap {
226-
[ButtonActionType.COMMAND]: CommandButtonAction;
227-
[ButtonActionType.JS]: JSButtonAction;
228-
[ButtonActionType.OPEN]: OpenButtonAction;
229-
[ButtonActionType.INPUT]: InputButtonAction;
230-
[ButtonActionType.SLEEP]: SleepButtonAction;
231-
[ButtonActionType.TEMPLATER_CREATE_NOTE]: TemplaterCreateNoteButtonAction;
232-
[ButtonActionType.UPDATE_METADATA]: UpdateMetadataButtonAction;
233-
[ButtonActionType.CREATE_NOTE]: CreateNoteButtonAction;
234-
[ButtonActionType.REPLACE_IN_NOTE]: ReplaceInNoteButtonAction;
235-
[ButtonActionType.REPLACE_SELF]: ReplaceSelfButtonAction;
236-
[ButtonActionType.REGEXP_REPLACE_IN_NOTE]: RegexpReplaceInNoteButtonAction;
237-
[ButtonActionType.INSERT_INTO_NOTE]: InsertIntoNoteButtonAction;
238-
[ButtonActionType.INLINE_JS]: InlineJSButtonAction;
239-
[ButtonActionType.RUN_TEMPLATER_FILE]: RunTemplaterFileButtonAction;
240-
}

packages/core/src/fields/inputFields/AbstractInputField.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export abstract class AbstractInputField<
118118
}
119119

120120
private notifySubscription(value: MetadataValueType): void {
121-
this.metadataSubscription?.update(value);
121+
this.metadataSubscription?.write(value);
122122
}
123123

124124
public getDefaultValue(): MetadataValueType {

packages/core/src/fields/inputFields/fields/Suggester/SuggesterHelper.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ export type ImageSuggesterLikeIPF = ImageSuggesterIPF | ImageListSuggesterIPF;
1111
export class SuggesterOption<T> {
1212
value: T;
1313
displayValue: string;
14+
displayDescription?: string;
1415

15-
constructor(value: T, displayValue: string) {
16+
constructor(value: T, displayValue: string, displayDescription?: string) {
1617
this.value = value;
1718
this.displayValue = displayValue;
19+
this.displayDescription = displayDescription;
1820
}
1921

2022
valueAsString(): string {

packages/core/src/fields/metaBindTable/TableMountable.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class TableMountable extends FieldMountable {
104104
}
105105

106106
updateMetadataManager(value: unknown): void {
107-
this.metadataSubscription?.update(value);
107+
this.metadataSubscription?.write(value);
108108
}
109109

110110
updateDisplayValue(values: T | undefined): void {

packages/core/src/fields/viewFields/AbstractViewField.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,17 @@ import { ViewFieldArgumentType } from 'packages/core/src/config/FieldConfigs';
22
import type { ViewFieldMountable } from 'packages/core/src/fields/viewFields/ViewFieldMountable';
33
import type { ViewFieldVariable } from 'packages/core/src/fields/viewFields/ViewFieldVariable';
44
import type { IPlugin } from 'packages/core/src/IPlugin';
5-
import type {
6-
ComputedMetadataSubscription,
7-
ComputedSubscriptionDependency,
8-
} from 'packages/core/src/metadata/ComputedMetadataSubscription';
5+
import type { DerivedMetadataSubscription } from 'packages/core/src/metadata/DerivedMetadataSubscription';
96
import { Mountable } from 'packages/core/src/utils/Mountable';
107
import { Signal } from 'packages/core/src/utils/Signal';
118
import { DomHelpers } from 'packages/core/src/utils/Utils';
129

1310
export abstract class AbstractViewField<T> extends Mountable {
1411
readonly plugin: IPlugin;
1512
readonly mountable: ViewFieldMountable;
16-
readonly inputSignal: Signal<T | undefined>;
13+
readonly metadataSignal: Signal<T | undefined>;
1714

18-
private metadataSubscription?: ComputedMetadataSubscription;
15+
private metadataSubscription?: DerivedMetadataSubscription;
1916

2017
variables: ViewFieldVariable[];
2118

@@ -27,7 +24,7 @@ export abstract class AbstractViewField<T> extends Mountable {
2724

2825
this.mountable = mountable;
2926
this.plugin = mountable.plugin;
30-
this.inputSignal = new Signal<T | undefined>(undefined);
27+
this.metadataSignal = new Signal<T | undefined>(undefined);
3128

3229
this.variables = [];
3330

@@ -38,6 +35,8 @@ export abstract class AbstractViewField<T> extends Mountable {
3835

3936
protected abstract computeValue(): T | Promise<T>;
4037

38+
protected abstract mapValue(value: T): unknown;
39+
4140
private async initialRender(targetEl: HTMLElement): Promise<void> {
4241
DomHelpers.addClass(targetEl, 'mb-view-text');
4342

@@ -66,27 +65,26 @@ export abstract class AbstractViewField<T> extends Mountable {
6665
protected onMount(targetEl: HTMLElement): void {
6766
this.buildVariables();
6867

69-
this.inputSignal.registerListener({ callback: value => void this.rerender(targetEl, value) });
68+
this.metadataSignal.registerListener({ callback: value => void this.rerender(targetEl, value) });
7069

71-
this.metadataSubscription = this.mountable.plugin.metadataManager.subscribeComputed(
70+
this.metadataSubscription = this.mountable.plugin.metadataManager.subscribeDerived(
7271
this.mountable.getUuid(),
73-
this.inputSignal,
7472
this.mountable.getDeclaration().writeToBindTarget,
75-
this.variables.map((x): ComputedSubscriptionDependency => {
76-
return {
77-
bindTarget: x.bindTargetDeclaration,
78-
callbackSignal: x.inputSignal,
79-
};
80-
}),
81-
async () => await this.computeValue(),
73+
this.variables.map(x => x.bindTargetDeclaration),
74+
this.variables.map(x => x.metadataSignal),
75+
async () => {
76+
const value = await this.computeValue();
77+
void this.rerender(targetEl, value);
78+
return this.mapValue(value);
79+
},
8280
() => this.mountable.unmount(),
8381
);
8482

8583
void this.initialRender(targetEl);
8684
}
8785

8886
protected onUnmount(): void {
89-
this.inputSignal.unregisterAllListeners();
87+
this.metadataSignal.unregisterAllListeners();
9088
this.metadataSubscription?.unsubscribe();
9189
}
9290
}

packages/core/src/fields/viewFields/JsViewFieldMountable.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { FieldMountable } from 'packages/core/src/fields/FieldMountable';
22
import type { ViewFieldVariable } from 'packages/core/src/fields/viewFields/ViewFieldVariable';
33
import type { IPlugin } from 'packages/core/src/IPlugin';
4-
import type {
5-
ComputedMetadataSubscription,
6-
ComputedSubscriptionDependency,
7-
} from 'packages/core/src/metadata/ComputedMetadataSubscription';
4+
import type { DerivedMetadataSubscription } from 'packages/core/src/metadata/DerivedMetadataSubscription';
85
import type { JsViewFieldDeclaration } from 'packages/core/src/parsers/viewFieldParser/ViewFieldDeclaration';
96
import { ErrorCollection } from 'packages/core/src/utils/errors/ErrorCollection';
107
import { ErrorLevel, MetaBindJsError } from 'packages/core/src/utils/errors/MetaBindErrors';
@@ -19,7 +16,7 @@ export class JsViewFieldMountable extends FieldMountable {
1916
declaration: JsViewFieldDeclaration;
2017

2118
variables: ViewFieldVariable[];
22-
metadataSubscription: ComputedMetadataSubscription | undefined;
19+
metadataSubscription: DerivedMetadataSubscription | undefined;
2320
jsRenderer: IJsRenderer | undefined;
2421

2522
constructor(plugin: IPlugin, uuid: string, filePath: string, declaration: JsViewFieldDeclaration) {
@@ -43,7 +40,7 @@ export class JsViewFieldMountable extends FieldMountable {
4340
for (const bindTargetMapping of this.declaration.bindTargetMappings ?? []) {
4441
this.variables.push({
4542
bindTargetDeclaration: bindTargetMapping.bindTarget,
46-
inputSignal: new Signal<unknown>(undefined),
43+
metadataSignal: new Signal<unknown>(undefined),
4744
uuid: getUUID(),
4845
contextName: bindTargetMapping.name,
4946
});
@@ -57,11 +54,11 @@ export class JsViewFieldMountable extends FieldMountable {
5754
private buildContext(): Record<string, unknown> {
5855
const context: Record<string, unknown> = {};
5956
for (const variable of this.variables ?? []) {
60-
if (!variable.contextName || !variable.inputSignal) {
57+
if (!variable.contextName || !variable.metadataSignal) {
6158
continue;
6259
}
6360

64-
context[variable.contextName] = variable.inputSignal.get() ?? '';
61+
context[variable.contextName] = variable.metadataSignal.get() ?? '';
6562
}
6663

6764
return context;
@@ -74,18 +71,11 @@ export class JsViewFieldMountable extends FieldMountable {
7471
}
7572

7673
private registerSelfToMetadataManager(): void {
77-
const updateSignal = new Signal<unknown>(undefined);
78-
79-
this.metadataSubscription = this.plugin.metadataManager.subscribeComputed(
74+
this.metadataSubscription = this.plugin.metadataManager.subscribeDerived(
8075
this.getUuid(),
81-
updateSignal,
8276
this.declaration.writeToBindTarget,
83-
this.variables.map((x): ComputedSubscriptionDependency => {
84-
return {
85-
bindTarget: x.bindTargetDeclaration,
86-
callbackSignal: x.inputSignal,
87-
};
88-
}),
77+
this.variables.map(x => x.bindTargetDeclaration),
78+
this.variables.map(x => x.metadataSignal),
8979
async () => await this.evaluate(),
9080
() => this.unmount(),
9181
);

packages/core/src/fields/viewFields/ViewFieldVariable.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Signal } from 'packages/core/src/utils/Signal';
33

44
export interface ViewFieldVariable {
55
bindTargetDeclaration: BindTargetDeclaration;
6-
inputSignal: Signal<unknown>;
6+
metadataSignal: Signal<unknown>;
77
uuid: string;
88
contextName: string | undefined;
99
}

packages/core/src/fields/viewFields/fields/ImageVF.ts

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { AbstractViewField } from 'packages/core/src/fields/viewFields/AbstractV
22
import type { ViewFieldMountable } from 'packages/core/src/fields/viewFields/ViewFieldMountable';
33
import type { ViewFieldVariable } from 'packages/core/src/fields/viewFields/ViewFieldVariable';
44
import type { BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration';
5+
import type { MarkdownLink } from 'packages/core/src/parsers/MarkdownLinkParser';
56
import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser';
67
import ImageGrid from 'packages/core/src/utils/components/ImageGrid.svelte';
78
import { ErrorLevel, MetaBindValidationError } from 'packages/core/src/utils/errors/MetaBindErrors';
89
import { Signal } from 'packages/core/src/utils/Signal';
9-
import { getUUID } from 'packages/core/src/utils/Utils';
10-
import type { Component as SvelteComponent } from 'svelte';
10+
import { getUUID, toArray } from 'packages/core/src/utils/Utils';
1111
import { mount, unmount } from 'svelte';
1212

13-
export class ImageVF extends AbstractViewField<string> {
14-
component?: ReturnType<SvelteComponent>;
13+
export class ImageVF extends AbstractViewField<MarkdownLink | MarkdownLink[] | undefined> {
14+
component?: ReturnType<typeof ImageGrid>;
1515
linkVariable?: ViewFieldVariable;
1616

1717
constructor(mountable: ViewFieldMountable) {
@@ -45,28 +45,33 @@ export class ImageVF extends AbstractViewField<string> {
4545

4646
this.linkVariable = {
4747
bindTargetDeclaration: linkEntry,
48-
inputSignal: new Signal<unknown>(undefined),
48+
metadataSignal: new Signal<unknown>(undefined),
4949
uuid: getUUID(),
5050
contextName: `MB_VAR_0`,
5151
};
5252

5353
this.variables.push(this.linkVariable);
5454
}
5555

56-
protected computeValue(): string {
57-
const linkContent = this.linkVariable!.inputSignal.get();
56+
protected computeValue(): MarkdownLink | MarkdownLink[] | undefined {
57+
const linkContent = this.linkVariable!.metadataSignal.get();
5858

59-
// we want the return value to be a human-readable string, since someone could save this to the frontmatter
6059
if (typeof linkContent === 'string') {
61-
return MDLinkParser.toLinkString(linkContent);
60+
return [MDLinkParser.parseLink(linkContent)];
6261
} else if (Array.isArray(linkContent)) {
63-
const strings = linkContent.filter(x => typeof x === 'string');
64-
return strings
65-
.map(x => MDLinkParser.toLinkString(x))
66-
.filter(x => x !== '')
67-
.join(', ');
62+
return linkContent.filter(x => typeof x === 'string').map(x => MDLinkParser.parseLink(x));
6863
} else {
64+
return undefined;
65+
}
66+
}
67+
68+
protected mapValue(value: MarkdownLink | MarkdownLink[] | undefined): unknown {
69+
if (value === undefined) {
6970
return '';
71+
} else if (Array.isArray(value)) {
72+
return value.map(x => x.toString());
73+
} else {
74+
return value.toString();
7075
}
7176
}
7277

@@ -80,18 +85,9 @@ export class ImageVF extends AbstractViewField<string> {
8085
});
8186
}
8287

83-
protected async onRerender(container: HTMLElement, value: string | undefined): Promise<void> {
84-
const linkList = value ? MDLinkParser.parseLinkList(value) : [];
85-
if (this.component) {
86-
void unmount(this.component);
87-
}
88-
this.component = mount(ImageGrid, {
89-
target: container,
90-
props: {
91-
images: linkList.map(x => ({ link: x.target, internal: x.internal })),
92-
plugin: this.mountable.plugin,
93-
},
94-
});
88+
protected async onRerender(_: HTMLElement, value: MarkdownLink | MarkdownLink[] | undefined): Promise<void> {
89+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
90+
this.component?.updateImages(toArray(value).map(x => ({ link: x.target, internal: x.internal })));
9591
}
9692

9793
protected onUnmount(): void {

0 commit comments

Comments
 (0)