1
1
import { AbstractViewField } from 'packages/core/src/fields/viewFields/AbstractViewField' ;
2
2
import type { ViewFieldMountable } from 'packages/core/src/fields/viewFields/ViewFieldMountable' ;
3
+ import type { ViewFieldVariable } from 'packages/core/src/fields/viewFields/ViewFieldVariable' ;
3
4
import type { BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration' ;
4
5
import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser' ;
5
6
import LinkListComponent from 'packages/core/src/utils/components/LinkListComponent.svelte' ;
6
- import {
7
- ErrorLevel ,
8
- MetaBindExpressionError ,
9
- MetaBindValidationError ,
10
- } from 'packages/core/src/utils/errors/MetaBindErrors' ;
7
+ import { ErrorLevel , MetaBindValidationError } from 'packages/core/src/utils/errors/MetaBindErrors' ;
8
+ import { stringifyUnknown } from 'packages/core/src/utils/Literal' ;
11
9
import { Signal } from 'packages/core/src/utils/Signal' ;
12
10
import { getUUID } from 'packages/core/src/utils/Utils' ;
13
11
import type { Component as SvelteComponent } from 'svelte' ;
14
12
import { mount , unmount } from 'svelte' ;
15
13
16
- export class LinkVF extends AbstractViewField {
14
+ export class LinkVF extends AbstractViewField < string > {
17
15
component ?: ReturnType < SvelteComponent > ;
16
+ linkVariable ?: ViewFieldVariable ;
17
+ aliasVariable ?: ViewFieldVariable | string ;
18
18
19
19
constructor ( mountable : ViewFieldMountable ) {
20
20
super ( mountable ) ;
@@ -26,52 +26,106 @@ export class LinkVF extends AbstractViewField {
26
26
. getDeclaration ( )
27
27
. templateDeclaration . filter ( x => ( typeof x === 'string' ? x : true ) ) ;
28
28
29
- if ( entries . length !== 1 ) {
29
+ if ( entries . length !== 1 && entries . length !== 2 && entries . length !== 3 ) {
30
30
throw new MetaBindValidationError ( {
31
31
errorLevel : ErrorLevel . ERROR ,
32
32
effect : 'can not create view field' ,
33
- cause : 'link view filed only supports exactly a single bind target and not text content ' ,
33
+ cause : 'link view field must be of form "{bindTarget}" or "{bindTarget}|{bindTarget}" ' ,
34
34
} ) ;
35
35
}
36
36
37
- const firstEntry = entries [ 0 ] ;
38
- if ( typeof firstEntry === 'string' ) {
39
- throw new MetaBindValidationError ( {
40
- errorLevel : ErrorLevel . ERROR ,
41
- effect : 'can not create view field' ,
42
- cause : 'link view filed only supports exactly a single bind target and not text content' ,
43
- } ) ;
44
- }
37
+ const linkEntry = entries [ 0 ] ;
38
+ const separatorEntry = entries [ 1 ] ;
39
+ const linkTextEntry = entries [ 2 ] ;
40
+
41
+ this . variables = [ ] ;
42
+
43
+ if ( entries . length === 1 ) {
44
+ if ( typeof linkEntry === 'string' ) {
45
+ throw new MetaBindValidationError ( {
46
+ errorLevel : ErrorLevel . ERROR ,
47
+ effect : 'can not create view field' ,
48
+ cause : 'link view field must be of form "{bindTarget}" or "{bindTarget}|{bindTarget}"' ,
49
+ } ) ;
50
+ }
45
51
46
- firstEntry . listenToChildren = true ;
52
+ linkEntry . listenToChildren = true ;
47
53
48
- this . variables = [
49
- {
50
- bindTargetDeclaration : firstEntry ,
54
+ this . linkVariable = {
55
+ bindTargetDeclaration : linkEntry ,
51
56
inputSignal : new Signal < unknown > ( undefined ) ,
52
57
uuid : getUUID ( ) ,
53
58
contextName : `MB_VAR_0` ,
54
- } ,
55
- ] ;
59
+ } ;
60
+
61
+ this . variables . push ( this . linkVariable ) ;
62
+ } else if ( entries . length === 2 || entries . length === 3 ) {
63
+ if ( typeof linkEntry === 'string' || typeof separatorEntry !== 'string' ) {
64
+ throw new MetaBindValidationError ( {
65
+ errorLevel : ErrorLevel . ERROR ,
66
+ effect : 'can not create view field' ,
67
+ cause : 'link view field must be of form "{bindTarget}", "{bindTarget}|alias", or "{bindTarget}|{bindTarget}"' ,
68
+ } ) ;
69
+ }
70
+
71
+ linkEntry . listenToChildren = true ;
72
+
73
+ this . linkVariable = {
74
+ bindTargetDeclaration : linkEntry ,
75
+ inputSignal : new Signal < unknown > ( undefined ) ,
76
+ uuid : getUUID ( ) ,
77
+ contextName : `MB_VAR_0` ,
78
+ } ;
79
+
80
+ this . variables . push ( this . linkVariable ) ;
81
+
82
+ if ( entries . length === 2 ) {
83
+ this . aliasVariable = separatorEntry . slice ( 1 ) ;
84
+ } else {
85
+ if ( typeof linkTextEntry === 'string' ) {
86
+ this . aliasVariable = linkTextEntry ;
87
+ } else {
88
+ linkTextEntry . listenToChildren = true ;
89
+
90
+ this . aliasVariable = {
91
+ bindTargetDeclaration : linkTextEntry ,
92
+ inputSignal : new Signal < unknown > ( undefined ) ,
93
+ uuid : getUUID ( ) ,
94
+ contextName : `MB_VAR_1` ,
95
+ } ;
96
+
97
+ this . variables . push ( this . aliasVariable ) ;
98
+ }
99
+ }
100
+ } else {
101
+ throw new Error ( 'unreachable' ) ;
102
+ }
56
103
}
57
104
58
- protected computeValue ( ) : string {
59
- if ( this . variables . length !== 1 ) {
60
- throw new MetaBindExpressionError ( {
61
- errorLevel : ErrorLevel . CRITICAL ,
62
- effect : 'failed to evaluate link view field' ,
63
- cause : 'there should be exactly one variable' ,
64
- } ) ;
105
+ private getAlias ( ) : string | undefined {
106
+ if ( ! this . aliasVariable ) {
107
+ return undefined ;
108
+ }
109
+
110
+ if ( typeof this . aliasVariable === 'string' ) {
111
+ return this . aliasVariable ;
112
+ } else {
113
+ return stringifyUnknown (
114
+ this . aliasVariable . inputSignal . get ( ) ,
115
+ this . mountable . plugin . settings . viewFieldDisplayNullAsEmpty ,
116
+ ) ;
65
117
}
118
+ }
66
119
67
- const variable = this . variables [ 0 ] ;
68
- const content = variable . inputSignal . get ( ) ;
120
+ protected computeValue ( ) : string {
121
+ const linkContent = this . linkVariable ! . inputSignal . get ( ) ;
122
+ const alias = this . getAlias ( ) ;
69
123
70
124
// we want the return value to be a human-readable string, since someone could save this to the frontmatter
71
- if ( typeof content === 'string' ) {
72
- return MDLinkParser . toLinkString ( content ) ;
73
- } else if ( Array . isArray ( content ) ) {
74
- const strings = content . filter ( x => typeof x === 'string' ) ;
125
+ if ( typeof linkContent === 'string' ) {
126
+ return MDLinkParser . toLinkString ( linkContent , alias ) ;
127
+ } else if ( Array . isArray ( linkContent ) ) {
128
+ const strings = linkContent . filter ( x => typeof x === 'string' ) ;
75
129
return strings
76
130
. map ( x => MDLinkParser . toLinkString ( x ) )
77
131
. filter ( x => x !== '' )
@@ -90,8 +144,8 @@ export class LinkVF extends AbstractViewField {
90
144
} ) ;
91
145
}
92
146
93
- protected async onRerender ( container : HTMLElement , text : string ) : Promise < void > {
94
- const linkList = MDLinkParser . parseLinkList ( text ) ;
147
+ protected async onRerender ( container : HTMLElement , value : string | undefined ) : Promise < void > {
148
+ const linkList = value ? MDLinkParser . parseLinkList ( value ) : [ ] ;
95
149
this . component = mount ( LinkListComponent , {
96
150
target : container ,
97
151
props : {
0 commit comments