Skip to content

Commit 3e6c13f

Browse files
committed
fix editor input clearing, improve performance, reduce input field notify events
1 parent a6a340e commit 3e6c13f

File tree

11 files changed

+142
-68
lines changed

11 files changed

+142
-68
lines changed

exampleVault/examples fg.md

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,21 @@ rating: 31
33
title: test title test test
44
completed: false
55
toggle1: true
6-
slider1: 71
6+
slider1: 17
77
slider2: 8
88
text1: Testa
99
text_area1: test test test
1010
date1: Wednesday, July 20th 2022
11-
select: option b
11+
select: option c
1212
nested:
1313
object: dfgdf
1414
multi_select:
15+
- option a
1516
- option b
17+
- option c
1618
time1: 10:17
1719
suggest: test
18-
editor: |-
19-
test **test**
20-
[[other note]]
21-
22-
## test
23-
24-
> [!INFO]
25-
> this is an info
26-
27-
`INPUT[slider(addLabels):slider1]`
28-
29-
`$= dv.current().rating`
20+
editor: test **test**
3021
---
3122

3223
## Components
@@ -93,10 +84,14 @@ option(option d)
9384
```
9485

9586
### Suggester
96-
`INPUT[suggester(suggestOption(test), suggestOptionQuery(#test)):suggest]`
87+
```meta-bind
88+
INPUT[suggester(suggestOption(test), suggestOptionQuery(#test)):suggest]
89+
```
9790

9891
### Editor
99-
`INPUT[editor:editor]`
92+
```meta-bind
93+
INPUT[editor:editor]
94+
```
10095

10196
## In callouts
10297
> quote

exampleVault/other note.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
tags: test
33
title: "test test test "
4-
select: option d
4+
select: option c
55
date: Tuesday, June 21st 2022
66
time: 19:20
77
multi-select:
@@ -10,4 +10,27 @@ multi-select:
1010
---
1111

1212
## This is another note
13-
This note is to test syncing to another note.
13+
This note is to test syncing to another note.
14+
15+
16+
### Select
17+
Select
18+
```meta-bind
19+
INPUT[select(
20+
option(option a),
21+
option(option b),
22+
option(option c),
23+
option(option d)
24+
):select]
25+
```
26+
27+
Select with title
28+
```meta-bind
29+
INPUT[select(
30+
title(select with title),
31+
option(option a),
32+
option(option b),
33+
option(option c),
34+
option(option d)
35+
):select]
36+
```

src/InputFieldMarkdownRenderChild.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { InputFieldFactory } from './inputFields/InputFieldFactory';
55
import { InputFieldArgumentType, InputFieldDeclaration, InputFieldDeclarationParser } from './parsers/InputFieldDeclarationParser';
66
import { AbstractInputFieldArgument } from './inputFieldArguments/AbstractInputFieldArgument';
77
import { ClassInputFieldArgument } from './inputFieldArguments/ClassInputFieldArgument';
8-
import { traverseObject, validatePath as validateObjectPath } from '@opd-libs/opd-metadata-lib/lib/Utils';
8+
import { parsePath, traverseObjectByPath } from '@opd-libs/opd-metadata-lib/lib/Utils';
99
import { MetaBindBindTargetError, MetaBindInternalError } from './utils/MetaBindErrors';
1010
import { MetadataFileCache } from './MetadataManager';
1111

@@ -26,7 +26,7 @@ export class InputFieldMarkdownRenderChild extends MarkdownRenderChild {
2626
fullDeclaration: string;
2727
inputFieldDeclaration: InputFieldDeclaration | undefined;
2828
bindTargetFile: TFile | undefined;
29-
bindTargetMetadataField: string | undefined;
29+
bindTargetMetadataPath: string[] | undefined;
3030

3131
intervalCounter: number;
3232
metadataValueUpdateQueue: any[];
@@ -86,15 +86,13 @@ export class InputFieldMarkdownRenderChild extends MarkdownRenderChild {
8686
}
8787

8888
try {
89-
validateObjectPath(bindTargetMetadataFieldName);
89+
this.bindTargetMetadataPath = parsePath(bindTargetMetadataFieldName);
9090
} catch (e) {
9191
if (e instanceof Error) {
9292
throw new MetaBindBindTargetError(`bind target parsing error: ${e?.message}`);
9393
}
9494
}
9595

96-
this.bindTargetMetadataField = bindTargetMetadataFieldName;
97-
9896
const files: TFile[] = this.plugin.getFilesByName(bindTargetFileName);
9997
if (files.length === 0) {
10098
throw new MetaBindBindTargetError('bind target file not found');
@@ -106,45 +104,45 @@ export class InputFieldMarkdownRenderChild extends MarkdownRenderChild {
106104
}
107105

108106
registerSelfToMetadataManager(): MetadataFileCache | undefined {
109-
if (!this.inputFieldDeclaration?.isBound || !this.bindTargetFile || !this.bindTargetMetadataField) {
107+
if (!this.inputFieldDeclaration?.isBound || !this.bindTargetFile || !this.bindTargetMetadataPath || this.bindTargetMetadataPath?.length === 0) {
110108
return;
111109
}
112110

113111
return this.plugin.metadataManager.register(
114112
this.bindTargetFile,
115-
metadata => {
113+
value => {
116114
if (!this.inputField) {
117115
throw new MetaBindInternalError('inputField is undefined, can not update inputField');
118116
}
119117

120-
const value = traverseObject(this.bindTargetMetadataField as string, metadata);
121118
if (!this.inputField.isEqualValue(value)) {
122119
this.inputField.setValue(value);
123120
}
124121
},
122+
this.bindTargetMetadataPath,
125123
this.uuid
126124
);
127125
}
128126

129127
unregisterSelfFromMetadataManager(): void {
130-
if (!this.inputFieldDeclaration?.isBound || !this.bindTargetFile || !this.bindTargetMetadataField) {
128+
if (!this.inputFieldDeclaration?.isBound || !this.bindTargetFile || !this.bindTargetMetadataPath || this.bindTargetMetadataPath?.length === 0) {
131129
return;
132130
}
133131

134132
this.plugin.metadataManager.unregister(this.bindTargetFile, this.uuid);
135133
}
136134

137135
updateMetadataManager(value: any): void {
138-
if (!this.inputFieldDeclaration?.isBound || !this.bindTargetFile || !this.bindTargetMetadataField) {
136+
if (!this.inputFieldDeclaration?.isBound || !this.bindTargetFile || !this.bindTargetMetadataPath || this.bindTargetMetadataPath?.length === 0) {
139137
return;
140138
}
141139

142-
this.plugin.metadataManager.updatePropertyInMetadataFileCache(value, this.bindTargetMetadataField, this.bindTargetFile, this.uuid);
140+
this.plugin.metadataManager.updatePropertyInMetadataFileCache(value, this.bindTargetMetadataPath, this.bindTargetFile, this.uuid);
143141
}
144142

145143
getInitialValue(): any | undefined {
146-
if (this.inputFieldDeclaration?.isBound && this.bindTargetMetadataField) {
147-
const value = traverseObject(this.bindTargetMetadataField, this.metadataCache?.metadata);
144+
if (this.inputFieldDeclaration?.isBound && this.bindTargetMetadataPath) {
145+
const value = traverseObjectByPath(this.bindTargetMetadataPath, this.metadataCache?.metadata);
148146
console.debug(`meta-bind | InputFieldMarkdownRenderChild >> setting initial value to ${value} (typeof ${typeof value}) for input field ${this.uuid}`);
149147
return value ?? this.inputField?.getDefaultValue();
150148
}

src/MetadataManager.ts

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { CachedMetadata, TFile } from 'obsidian';
22
import MetaBindPlugin from './main';
33
import { setFrontmatterOfTFile } from '@opd-libs/opd-metadata-lib/lib/API';
4-
import { traverseObjectToParent } from '@opd-libs/opd-metadata-lib/lib/Utils';
4+
import { traverseObjectByPath } from '@opd-libs/opd-metadata-lib/lib/Utils';
55
import { Internal } from '@opd-libs/opd-metadata-lib/lib/Internal';
6+
import { arrayEquals, traverseObjectToParentByPath } from './utils/Utils';
67
import getMetaDataFromFileContent = Internal.getMetaDataFromFileContent;
78

89
export interface MetadataFileCache {
910
file: TFile;
1011
metadata: Record<string, any>;
1112
listeners: {
12-
onCacheUpdate: (metadata: Record<string, any>) => void;
13+
onCacheUpdate: (value: any | undefined) => void;
14+
metadataPath: string[];
1315
uuid: string;
1416
}[];
1517
cyclesSinceLastUpdate: number;
@@ -32,24 +34,25 @@ export class MetadataManager {
3234
this.interval = window.setInterval(() => this.update(), this.plugin.settings.syncInterval);
3335
}
3436

35-
register(file: TFile, onCacheUpdate: (metadata: Record<string, any>) => void, uuid: string): MetadataFileCache {
37+
register(file: TFile, onCacheUpdate: (value: any) => void, metadataPath: string[], uuid: string): MetadataFileCache {
3638
const fileCache = this.getCacheForFile(file);
3739
if (fileCache) {
38-
console.debug(`meta-bind | MetadataManager >> registered ${uuid} to existing file cache ${file.path}`);
39-
fileCache.listeners.push({ onCacheUpdate, uuid });
40+
console.debug(`meta-bind | MetadataManager >> registered ${uuid} to existing file cache ${file.path} -> ${metadataPath}`);
41+
fileCache.listeners.push({ onCacheUpdate, metadataPath, uuid });
4042
return fileCache;
4143
} else {
42-
console.debug(`meta-bind | MetadataManager >> registered ${uuid} to newly created file cache ${file.path}`);
44+
console.debug(`meta-bind | MetadataManager >> registered ${uuid} to newly created file cache ${file.path} -> ${metadataPath}`);
4345
const c: MetadataFileCache = {
4446
file: file,
4547
metadata: {},
46-
listeners: [{ onCacheUpdate, uuid }],
48+
listeners: [{ onCacheUpdate, metadataPath, uuid }],
4749
cyclesSinceLastUpdate: 0,
4850
changed: false,
4951
};
5052

5153
this.plugin.app.vault.cachedRead(file).then(value => {
5254
c.metadata = getMetaDataFromFileContent(value);
55+
console.log(`meta-bind | MetadataManager >> loaded metadata for file ${file.path}`, c.metadata);
5356
this.notifyListeners(c);
5457
});
5558

@@ -106,28 +109,34 @@ export class MetadataManager {
106109
fileCache.metadata = metadata;
107110
fileCache.cyclesSinceLastUpdate = 0;
108111

109-
this.notifyListeners(fileCache, uuid);
112+
this.notifyListeners(fileCache, undefined, uuid);
110113
}
111114

112-
updatePropertyInMetadataFileCache(value: any, path: string, file: TFile, uuid?: string | undefined): void {
113-
console.debug(`meta-bind | MetadataManager >> updating ${path} in ${file.path} metadata cache to`, value);
115+
updatePropertyInMetadataFileCache(value: any, pathParts: string[], file: TFile, uuid?: string | undefined): void {
116+
console.debug(`meta-bind | MetadataManager >> updating ${pathParts} in ${file.path} metadata cache to`, value);
117+
console.trace();
114118

115119
const fileCache = this.getCacheForFile(file);
116120
if (!fileCache) {
117121
return;
118122
}
119123

120-
const { parent, child } = traverseObjectToParent(path, fileCache.metadata);
124+
const { parent, child } = traverseObjectToParentByPath(pathParts, fileCache.metadata);
121125

122126
if (parent.value === undefined) {
123-
throw Error(`The parent of "${path}" does not exist in Object, please create the parent first`);
127+
throw Error(`The parent of "${pathParts}" does not exist in Object, please create the parent first`);
128+
}
129+
130+
if (child.value === value) {
131+
console.debug(`meta-bind | MetadataManager >> skipping redundant update of ${pathParts} in ${file.path} metadata cache`, value);
132+
return;
124133
}
125134

126135
parent.value[child.key] = value;
127136
fileCache.cyclesSinceLastUpdate = 0;
128137
fileCache.changed = true;
129138

130-
this.notifyListeners(fileCache, uuid);
139+
this.notifyListeners(fileCache, pathParts, uuid);
131140
}
132141

133142
updateMetadataFileCacheOnFrontmatterUpdate(file: TFile, data: string, cache: CachedMetadata): void {
@@ -152,13 +161,26 @@ export class MetadataManager {
152161
this.notifyListeners(fileCache);
153162
}
154163

155-
notifyListeners(fileCache: MetadataFileCache, exceptUuid?: string | undefined): void {
164+
notifyListeners(fileCache: MetadataFileCache, metadataPath?: string[] | undefined, exceptUuid?: string | undefined): void {
165+
let value;
166+
if (metadataPath) {
167+
value = traverseObjectByPath(metadataPath, fileCache.metadata);
168+
}
169+
156170
for (const listener of fileCache.listeners) {
157171
if (exceptUuid && exceptUuid === listener.uuid) {
158172
continue;
159173
}
160-
console.debug(`meta-bind | MetadataManager >> notifying input field ${listener.uuid} of updated metadata`);
161-
listener.onCacheUpdate(fileCache.metadata);
174+
175+
if (metadataPath) {
176+
if (arrayEquals(metadataPath, listener.metadataPath)) {
177+
console.debug(`meta-bind | MetadataManager >> notifying input field ${listener.uuid} of updated metadata`);
178+
listener.onCacheUpdate(value);
179+
}
180+
} else {
181+
console.debug(`meta-bind | MetadataManager >> notifying input field ${listener.uuid} of updated metadata`);
182+
listener.onCacheUpdate(traverseObjectByPath(listener.metadataPath, fileCache.metadata));
183+
}
162184
}
163185
}
164186
}

src/inputFields/DatePicker/DatePickerInputField.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,8 @@ export class DatePickerInputField extends AbstractInputField {
8282
props: {
8383
dateFormat: this.inputFieldMarkdownRenderChild.plugin.settings.preferredDateFormat,
8484
showDatePicker: () => this.showDatePicker(),
85+
selectedDate: this.date,
8586
},
8687
});
87-
88-
this.component.updateValue(this.date);
8988
}
9089
}

src/inputFields/Editor/EditorInput.svelte

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
66
export let onValueChange: (value: any) => void;
77
export let editorInput: EditorInputField;
8+
export let value: string;
89
9-
let value: string;
1010
let focus: boolean = false;
1111
let renderEl: HTMLElement;
1212
let inputEl: HTMLElement;
@@ -20,18 +20,17 @@
2020
2121
export function updateValue(v: string): void {
2222
value = v;
23-
renderEl.innerHTML = '';
2423
render();
2524
}
2625
27-
function render() {
26+
export function render() {
27+
renderEl.innerHTML = '';
2828
MarkdownRenderer.renderMarkdown(value, renderEl, editorInput.inputFieldMarkdownRenderChild.filePath, editorInput.inputFieldMarkdownRenderChild);
2929
}
3030
3131
function focusOut(event: MouseEvent) {
3232
renderEl.toggleVisibility(true);
3333
inputEl.toggleVisibility(false);
34-
renderEl.innerHTML = '';
3534
render();
3635
}
3736
@@ -41,18 +40,15 @@
4140
inputEl.toggleVisibility(true);
4241
inputEl.focus();
4342
}
44-
45-
function log() {
46-
console.log('focus');
47-
}
4843
</script>
4944

5045
<style>
5146
.editor-input {
52-
width: 100%;
53-
height: 500px;
54-
padding: 0;
55-
position: relative;
47+
background: var(--background-secondary);
48+
width: 100%;
49+
height: 500px;
50+
padding: 0;
51+
position: relative;
5652
}
5753
5854
.editor-input > textarea {

src/inputFields/Editor/EditorInputField.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InputFieldMarkdownRenderChild } from '../../InputFieldMarkdownRenderChi
44
import { MetaBindInternalError } from '../../utils/MetaBindErrors';
55

66
export class EditorInputField extends AbstractInputField {
7+
static allowInlineCodeBlock: boolean = false;
78
container: HTMLDivElement | undefined;
89
component: EditorInput | undefined;
910
value: string;
@@ -51,9 +52,10 @@ export class EditorInputField extends AbstractInputField {
5152
props: {
5253
onValueChange: this.onValueChange.bind(this),
5354
editorInput: this,
55+
value: this.value,
5456
},
5557
});
5658

57-
this.component.updateValue(this.value);
59+
this.component.render();
5860
}
5961
}

0 commit comments

Comments
 (0)