11import { AbstractViewField } from 'packages/core/src/fields/viewFields/AbstractViewField' ;
22import type { ViewFieldMountable } from 'packages/core/src/fields/viewFields/ViewFieldMountable' ;
3+ import type { ViewFieldVariable } from 'packages/core/src/fields/viewFields/ViewFieldVariable' ;
34import type { BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration' ;
45import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser' ;
56import 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' ;
119import { Signal } from 'packages/core/src/utils/Signal' ;
1210import { getUUID } from 'packages/core/src/utils/Utils' ;
1311import type { Component as SvelteComponent } from 'svelte' ;
1412import { mount , unmount } from 'svelte' ;
1513
16- export class LinkVF extends AbstractViewField {
14+ export class LinkVF extends AbstractViewField < string > {
1715 component ?: ReturnType < SvelteComponent > ;
16+ linkVariable ?: ViewFieldVariable ;
17+ aliasVariable ?: ViewFieldVariable | string ;
1818
1919 constructor ( mountable : ViewFieldMountable ) {
2020 super ( mountable ) ;
@@ -26,52 +26,106 @@ export class LinkVF extends AbstractViewField {
2626 . getDeclaration ( )
2727 . templateDeclaration . filter ( x => ( typeof x === 'string' ? x : true ) ) ;
2828
29- if ( entries . length !== 1 ) {
29+ if ( entries . length !== 1 && entries . length !== 2 && entries . length !== 3 ) {
3030 throw new MetaBindValidationError ( {
3131 errorLevel : ErrorLevel . ERROR ,
3232 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}" ' ,
3434 } ) ;
3535 }
3636
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+ }
4551
46- firstEntry . listenToChildren = true ;
52+ linkEntry . listenToChildren = true ;
4753
48- this . variables = [
49- {
50- bindTargetDeclaration : firstEntry ,
54+ this . linkVariable = {
55+ bindTargetDeclaration : linkEntry ,
5156 inputSignal : new Signal < unknown > ( undefined ) ,
5257 uuid : getUUID ( ) ,
5358 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+ }
56103 }
57104
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+ ) ;
65117 }
118+ }
66119
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 ( ) ;
69123
70124 // 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' ) ;
75129 return strings
76130 . map ( x => MDLinkParser . toLinkString ( x ) )
77131 . filter ( x => x !== '' )
@@ -90,8 +144,8 @@ export class LinkVF extends AbstractViewField {
90144 } ) ;
91145 }
92146
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 ) : [ ] ;
95149 this . component = mount ( LinkListComponent , {
96150 target : container ,
97151 props : {
0 commit comments