Skip to content

Commit 153ab60

Browse files
committed
link view field
1 parent 1696734 commit 153ab60

File tree

14 files changed

+245
-196
lines changed

14 files changed

+245
-196
lines changed

.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const config = {
3232
'@typescript-eslint/no-empty-function': 'off',
3333
'@typescript-eslint/no-inferrable-types': 'off',
3434
'@typescript-eslint/explicit-function-return-type': ['warn'],
35+
'@typescript-eslint/require-await': 'off',
3536
},
3637
};
3738

exampleVault/View Fields/View Field.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Self Loop Error: `VIEW[**{computed}**][text():computed]`
4949

5050
`INPUT[suggester(optionQuery(#example-note), useLinks(false)):file]`
5151
`VIEW[\[\[{file}|link\]\]][text(renderMarkdown)]`
52+
`VIEW[{file}][link]`
5253

5354
```meta-bind
5455
INPUT[imageSuggester(optionQuery("Other/Images")):image]

src/metadata/MetadataManager.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,16 @@ export class MetadataManager {
110110
// const frontmatter = this.plugin.app.metadataCache.getFileCache(file)?.frontmatter;
111111
const { metadata, extraCache } = this.metadataAdapter.getMetadataAndExtraCache(subscription);
112112

113-
const newCache: MetadataManagerCacheItem = Object.assign(
114-
{},
115-
{
116-
extraCache: extraCache,
117-
metadata: metadata,
118-
listeners: [subscription],
119-
cyclesSinceLastChange: metadataCacheUpdateCycleThreshold + 1, // +1, so that is it bigger than the threshold
120-
cyclesSinceInactive: 0,
121-
inactive: false,
122-
changed: false,
123-
},
124-
);
113+
const newCache: MetadataManagerCacheItem = {
114+
extraCache: extraCache,
115+
metadata: metadata,
116+
listeners: [subscription],
117+
cyclesSinceLastChange: metadataCacheUpdateCycleThreshold + 1, // +1, so that is it bigger than the threshold
118+
cyclesSinceInactive: 0,
119+
inactive: false,
120+
changed: false,
121+
};
122+
125123
console.log(`meta-bind | MetadataManager >> loaded metadata for file ${subscription.bindTarget.filePath}`, newCache.metadata);
126124

127125
subscription.notify(traverseObjectByPath(subscription.bindTarget.metadataPath, newCache.metadata));

src/parsers/GeneralConfigs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ export const InputFieldArgumentConfigs: Record<InputFieldArgumentType, InputFiel
443443
export enum ViewFieldType {
444444
MATH = 'math',
445445
TEXT = 'text',
446+
LINK = 'link',
446447

447448
INVALID = 'invalid',
448449
}

src/parsers/MarkdownLinkParser.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import { P } from '@lemons_dev/parsinom/lib/ParsiNOM';
22
import { filePath } from './nomParsers/BindTargetParsers';
33
import { type Parser } from '@lemons_dev/parsinom/lib/Parser';
44
import { runParser } from './ParsingError';
5+
import { P_UTILS } from '@lemons_dev/parsinom/lib/ParserUtils';
56

67
const mdLinkInnerParser = P.sequence(
78
filePath, // the file path
89
P.string('#').then(P.manyNotOf('[]#|^:')).optional(), // the optional heading
9-
P.string('|').then(P.manyNotOf('[]').optional()), // the optional alias
10+
P.string('|').then(P.manyNotOf('[]')).optional(), // the optional alias
1011
);
1112

1213
const mdLinkParser: Parser<MarkdownLink> = P.sequenceMap(
@@ -22,6 +23,8 @@ const mdLinkParser: Parser<MarkdownLink> = P.sequenceMap(
2223
mdLinkInnerParser.wrapString('[[', ']]'),
2324
);
2425

26+
const mdLinkListParser: Parser<MarkdownLink[]> = P.separateBy(mdLinkParser, P.string(',').trim(P_UTILS.optionalWhitespace()));
27+
2528
export interface MarkdownLink {
2629
isEmbed: boolean;
2730
target: string;
@@ -33,6 +36,10 @@ export function parseMdLink(link: string): MarkdownLink {
3336
return runParser(mdLinkParser, link);
3437
}
3538

39+
export function parseMdLinkList(link: string): MarkdownLink[] {
40+
return runParser(mdLinkListParser, link);
41+
}
42+
3643
export function isMdLink(str: string): boolean {
3744
return (str.startsWith('![[') || str.startsWith('[[')) && str.endsWith(']]');
3845
}

src/renderChildren/ViewFieldMDRC.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,37 +73,10 @@ export class ViewFieldMDRC extends AbstractViewFieldMDRC {
7373
} catch (e) {
7474
this.errorCollection.add(e);
7575
}
76-
77-
// for (const variable of this.variables) {
78-
// variable.writeSignalListener = variable.writeSignal.registerListener({
79-
// callback: () => {
80-
// this.viewField?.update(this.variables);
81-
// },
82-
// });
83-
//
84-
// this.plugin.metadataManager.register(
85-
// variable.bindTargetDeclaration.filePath ?? this.filePath,
86-
// variable.writeSignal,
87-
// variable.bindTargetDeclaration.metadataPath,
88-
// variable.listenToChildren,
89-
// this.uuid + '/' + variable.uuid
90-
// );
91-
// }
9276
}
9377

9478
unregisterSelfFromMetadataManager(): void {
9579
this.metadataSubscription?.unsubscribe();
96-
97-
// for (const variable of this.variables) {
98-
// if (variable.writeSignalListener) {
99-
// variable.writeSignal.unregisterListener(variable.writeSignalListener);
100-
// }
101-
// this.plugin.metadataManager.unregister(variable.bindTargetDeclaration.filePath ?? this.filePath, this.uuid + '/' + variable.uuid);
102-
// }
103-
}
104-
105-
getInitialValue(): string {
106-
return '';
10780
}
10881

10982
getArguments<T extends ViewFieldArgumentType>(name: T): ViewFieldArgumentMapType<T>[] {

src/utils/LinkListComponent.svelte

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script lang="ts">
2+
import { MarkdownLink } from '../parsers/MarkdownLinkParser';
3+
import LinkComponent from './LinkComponent.svelte';
4+
5+
export let mdLinkList: MarkdownLink[];
6+
</script>
7+
8+
{#if mdLinkList.length === 0}
9+
<span></span>
10+
{:else if mdLinkList.length === 1}
11+
<LinkComponent mdLink={mdLinkList[0]}></LinkComponent>
12+
{:else}
13+
{#each mdLinkList.slice(0, mdLinkList.length - 1) as link}
14+
<LinkComponent mdLink={link}></LinkComponent>,
15+
{/each}
16+
<LinkComponent mdLink={mdLinkList[mdLinkList.length - 1]}></LinkComponent>
17+
{/if}

src/viewFields/AbstractViewField.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export abstract class AbstractViewField {
5252
this.container.removeClass('mb-error');
5353
} catch (e) {
5454
if (e instanceof Error) {
55+
console.error(e);
5556
this.container.innerText = e.message;
5657
this.container.addClass('mb-error');
5758
}

src/viewFields/ViewFieldFactory.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type AbstractViewField } from './AbstractViewField';
44
import { MathVF } from './fields/MathVF';
55
import { TextVF } from './fields/TextVF';
66
import { ViewFieldType } from '../parsers/GeneralConfigs';
7+
import { LinkVF } from './fields/LinkVF';
78

89
export class ViewFieldFactory {
910
plugin: IPlugin;
@@ -19,6 +20,8 @@ export class ViewFieldFactory {
1920
return new MathVF(renderChild);
2021
} else if (type === ViewFieldType.TEXT) {
2122
return new TextVF(renderChild);
23+
} else if (type === ViewFieldType.LINK) {
24+
return new LinkVF(renderChild);
2225
}
2326

2427
return undefined;

src/viewFields/fields/LinkVF.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { AbstractViewField } from '../AbstractViewField';
2+
import { type ViewFieldMDRC, type ViewFieldVariable } from '../../renderChildren/ViewFieldMDRC';
3+
import { type ViewFieldDeclaration } from '../../parsers/viewFieldParser/ViewFieldDeclaration';
4+
import { Signal } from '../../utils/Signal';
5+
import { getUUID } from '../../utils/Utils';
6+
import { ErrorLevel, MetaBindExpressionError, MetaBindValidationError } from '../../utils/errors/MetaBindErrors';
7+
import { type BindTargetDeclaration } from '../../parsers/inputFieldParser/InputFieldDeclaration';
8+
import { isMdLink, parseMdLinkList } from '../../parsers/MarkdownLinkParser';
9+
import LinkListComponent from '../../utils/LinkListComponent.svelte';
10+
11+
export class LinkVF extends AbstractViewField {
12+
component?: LinkListComponent;
13+
14+
constructor(renderChild: ViewFieldMDRC) {
15+
super(renderChild);
16+
}
17+
18+
public buildVariables(declaration: ViewFieldDeclaration): ViewFieldVariable[] {
19+
// filter out empty strings
20+
const entries: (string | BindTargetDeclaration)[] = declaration.templateDeclaration.filter(x => (typeof x === 'string' ? x : true));
21+
22+
if (entries.length !== 1) {
23+
throw new MetaBindValidationError({
24+
errorLevel: ErrorLevel.ERROR,
25+
effect: 'can not create view field',
26+
cause: 'link view filed only supports exactly a single bind target and not text content',
27+
});
28+
}
29+
30+
const firstEntry = entries[0];
31+
if (typeof firstEntry === 'string') {
32+
throw new MetaBindValidationError({
33+
errorLevel: ErrorLevel.ERROR,
34+
effect: 'can not create view field',
35+
cause: 'link view filed only supports exactly a single bind target and not text content',
36+
});
37+
}
38+
39+
const variable: ViewFieldVariable = {
40+
bindTargetDeclaration: firstEntry,
41+
inputSignal: new Signal<unknown>(undefined),
42+
uuid: getUUID(),
43+
contextName: `MB_VAR_0`,
44+
};
45+
46+
return [variable];
47+
}
48+
49+
protected _render(container: HTMLElement): void {
50+
this.component = new LinkListComponent({
51+
target: container,
52+
props: {
53+
mdLinkList: [],
54+
},
55+
});
56+
}
57+
58+
protected async _update(container: HTMLElement, text: string): Promise<void> {
59+
const linkList = parseMdLinkList(text);
60+
this.component = new LinkListComponent({
61+
target: container,
62+
props: {
63+
mdLinkList: linkList,
64+
},
65+
});
66+
}
67+
68+
public destroy(): void {
69+
this.component?.$destroy();
70+
}
71+
72+
computeValue(variables: ViewFieldVariable[]): string {
73+
if (variables.length !== 1) {
74+
throw new MetaBindExpressionError({
75+
errorLevel: ErrorLevel.CRITICAL,
76+
effect: 'failed to evaluate link view field',
77+
cause: 'there should be exactly one variable',
78+
});
79+
}
80+
81+
const variable = variables[0];
82+
const content = variable.inputSignal.get();
83+
84+
if (typeof content === 'string') {
85+
return this.convertToLink(content);
86+
} else if (Array.isArray(content)) {
87+
return (content.filter(x => typeof x === 'string') as string[]).map(x => this.convertToLink(x)).join(', ');
88+
} else {
89+
return '';
90+
}
91+
}
92+
93+
convertToLink(str: string): string {
94+
if (isMdLink(str)) {
95+
return str;
96+
} else {
97+
return `[[${str}]]`;
98+
}
99+
}
100+
101+
public getDefaultDisplayValue(): string {
102+
return '';
103+
}
104+
}

0 commit comments

Comments
 (0)