@@ -9,18 +9,22 @@ import {
99 css ,
1010} from 'lit-element' ;
1111import { get } from 'lit-translate' ;
12+ import { classMap } from 'lit-html/directives/class-map.js' ;
1213import { newPendingStateEvent } from '@compas-oscd/core' ;
1314import { newSettingsUIEvent } from '@compas-oscd/core' ;
14- import { XMLEditor } from '@compas-oscd/core' ;
15+ import { XMLEditor , OscdApi } from '@compas-oscd/core' ;
1516import {
1617 MenuItem ,
1718 Validator ,
1819 MenuPlugin ,
1920 pluginIcons ,
21+ OpenSCD
2022} from '../open-scd.js' ;
2123
2224import {
2325 Plugin ,
26+ ContentContext ,
27+ PluginKind
2428} from "../plugin.js"
2529
2630import {
@@ -48,6 +52,51 @@ import "./plugin-manager/custom-plugin-dialog.js";
4852import "./menu-tabs/menu-tabs.js" ;
4953import { TabActivatedEvent } from "./menu-tabs/menu-tabs.js" ;
5054
55+ /**
56+ * This is a template literal tag function. See:
57+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
58+ *
59+ * Passes its arguments to LitElement's `html` tag after combining the first and
60+ * last expressions with the first two and last two static strings.
61+ * Throws unless the first and last expressions are identical strings.
62+ *
63+ * We need this to get around the expression location limitations documented in
64+ * https://lit.dev/docs/templates/expressions/#expression-locations
65+ *
66+ * After upgrading to Lit 2 we can use their static HTML functions instead:
67+ * https://lit.dev/docs/api/static-html/
68+ */
69+ function staticTagHtml (
70+ oldStrings : ReadonlyArray < string > ,
71+ ...oldArgs : unknown [ ]
72+ ) : TemplateResult {
73+ const args = [ ...oldArgs ] ;
74+ const firstArg = args . shift ( ) ;
75+ const lastArg = args . pop ( ) ;
76+
77+ if ( firstArg !== lastArg )
78+ throw new Error (
79+ `Opening tag <${ firstArg } > does not match closing tag </${ lastArg } >.`
80+ ) ;
81+
82+ const strings = [ ...oldStrings ] as string [ ] & { raw : string [ ] } ;
83+ const firstString = strings . shift ( ) ;
84+ const secondString = strings . shift ( ) ;
85+
86+ const lastString = strings . pop ( ) ;
87+ const penultimateString = strings . pop ( ) ;
88+
89+ strings . unshift ( `${ firstString } ${ firstArg } ${ secondString } ` ) ;
90+ strings . push ( `${ penultimateString } ${ lastArg } ${ lastString } ` ) ;
91+
92+ return html ( < TemplateStringsArray > strings , ...args ) ;
93+ }
94+
95+ interface RenderAblePlugin {
96+ src ?: string ;
97+ kind : string ;
98+ content ?: ContentContext ;
99+ }
51100
52101@customElement ( 'oscd-layout' )
53102export class OscdLayout extends LitElement {
@@ -66,7 +115,7 @@ export class OscdLayout extends LitElement {
66115 @property ( { type : Array } ) plugins : Plugin [ ] = [ ] ;
67116
68117 /** The open-scd host element */
69- @property ( { type : Object } ) host ! : HTMLElement ;
118+ @property ( { type : Object } ) host ! : OpenSCD ;
70119
71120 @state ( ) validated : Promise < void > = Promise . resolve ( ) ;
72121 @state ( ) shouldValidate = false ;
@@ -93,6 +142,10 @@ export class OscdLayout extends LitElement {
93142 ` ;
94143 }
95144
145+ protected componentHtml ( strings : TemplateStringsArray , ...values : unknown [ ] ) : TemplateResult {
146+ return html ( strings , ...values ) ;
147+ }
148+
96149
97150 private renderPlugging ( ) : TemplateResult {
98151 return html ` ${ this . renderPluginUI ( ) } ${ this . renderDownloadUI ( ) } ` ;
@@ -165,7 +218,7 @@ export class OscdLayout extends LitElement {
165218 } ,
166219 disabled : ( ) : boolean => ! this . editor . canUndo ,
167220 kind : 'static' ,
168- content : ( ) => html `` ,
221+ content : { tag : '' } ,
169222 } ,
170223 {
171224 icon : 'redo' ,
@@ -176,7 +229,7 @@ export class OscdLayout extends LitElement {
176229 } ,
177230 disabled : ( ) : boolean => ! this . editor . canRedo ,
178231 kind : 'static' ,
179- content : ( ) => html `` ,
232+ content : { tag : '' } ,
180233 } ,
181234 ...validators ,
182235 {
@@ -187,7 +240,7 @@ export class OscdLayout extends LitElement {
187240 this . dispatchEvent ( newHistoryUIEvent ( true , HistoryUIKind . log ) ) ;
188241 } ,
189242 kind : 'static' ,
190- content : ( ) => html `` ,
243+ content : { tag : '' } ,
191244 } ,
192245 {
193246 icon : 'history' ,
@@ -197,7 +250,7 @@ export class OscdLayout extends LitElement {
197250 this . dispatchEvent ( newHistoryUIEvent ( true , HistoryUIKind . history ) ) ;
198251 } ,
199252 kind : 'static' ,
200- content : ( ) => html `` ,
253+ content : { tag : '' } ,
201254 } ,
202255 {
203256 icon : 'rule' ,
@@ -207,7 +260,7 @@ export class OscdLayout extends LitElement {
207260 this . dispatchEvent ( newHistoryUIEvent ( true , HistoryUIKind . diagnostic ) ) ;
208261 } ,
209262 kind : 'static' ,
210- content : ( ) => html `` ,
263+ content : { tag : '' } ,
211264 } ,
212265 'divider' ,
213266 ...middleMenu ,
@@ -218,15 +271,15 @@ export class OscdLayout extends LitElement {
218271 this . dispatchEvent ( newSettingsUIEvent ( true ) ) ;
219272 } ,
220273 kind : 'static' ,
221- content : ( ) => html `` ,
274+ content : { tag : '' } ,
222275 } ,
223276 ...bottomMenu ,
224277 {
225278 icon : 'extension' ,
226279 name : 'plugins.heading' ,
227280 action : ( ) : void => this . pluginUI . show ( ) ,
228281 kind : 'static' ,
229- content : ( ) => html `` ,
282+ content : { tag : '' } ,
230283 } ,
231284 ] ;
232285 }
@@ -311,10 +364,7 @@ export class OscdLayout extends LitElement {
311364 this . dispatchEvent ( newPendingStateEvent ( ( menuContentElement as unknown as MenuPlugin ) . run ( ) ) )
312365 } ,
313366 disabled : ( ) : boolean => plugin . requireDoc ! && this . doc === null ,
314- content : ( ) => {
315- if ( plugin . content ) { return plugin . content ( ) ; }
316- return html `` ;
317- } ,
367+ content : plugin . content ?? { tag : '' } ,
318368 kind : kind ,
319369 }
320370 } )
@@ -337,7 +387,7 @@ export class OscdLayout extends LitElement {
337387 this . dispatchEvent ( newPendingStateEvent ( ( menuContentElement as unknown as Validator ) . validate ( ) ) )
338388 } ,
339389 disabled : ( ) : boolean => this . doc === null ,
340- content : plugin . content ?? ( ( ) => html `` ) ,
390+ content : plugin . content ?? { tag : '' } ,
341391 kind : 'validator' ,
342392 }
343393 } ) ;
@@ -419,7 +469,7 @@ export class OscdLayout extends LitElement {
419469 ${
420470 this . menu
421471 . filter ( p => ( p as MenuItem ) . content )
422- . map ( p => ( p as MenuItem ) . content ( ) )
472+ . map ( p => this . renderPluginContent ( ( p as MenuItem ) ) )
423473 }
424474 </ div>
425475 ` ;
@@ -487,6 +537,17 @@ export class OscdLayout extends LitElement {
487537 const hasActiveEditors = activeEditors . length > 0 ;
488538 if ( ! hasActiveEditors ) { return html `` ; }
489539
540+ const renderEditorContent = ( doc : XMLDocument | null , activeEditor ?: Plugin ) => {
541+ const editor = activeEditor ;
542+ const requireDoc = editor ?. requireDoc
543+ if ( requireDoc && ! doc ) { return html `` }
544+
545+ const tag = editor ?. content ?. tag ;
546+ if ( ! tag ) { return html `` }
547+
548+ return this . renderPluginContent ( editor ) ;
549+ }
550+
490551 return html `
491552 <oscd- menu- tabs
492553 .editors = ${ this . calcActiveEditors ( ) }
@@ -496,17 +557,6 @@ export class OscdLayout extends LitElement {
496557 </ oscd- menu- tabs>
497558 ${ renderEditorContent ( this . doc , this . activeEditor , ) }
498559 ` ;
499-
500- function renderEditorContent ( doc : XMLDocument | null , activeEditor ?: Plugin ) {
501- const editor = activeEditor ;
502- const requireDoc = editor ?. requireDoc
503- if ( requireDoc && ! doc ) { return html `` }
504-
505- const content = editor ?. content ;
506- if ( ! content ) { return html `` }
507-
508- return html `${ content ( ) } ` ;
509- }
510560 }
511561
512562 private handleEditorTabActivated ( e : TabActivatedEvent ) {
@@ -578,6 +628,35 @@ export class OscdLayout extends LitElement {
578628 }
579629 }
580630
631+ protected renderPluginContent ( plugin : RenderAblePlugin ) : TemplateResult {
632+ const tag = plugin . content ?. tag ?? '' ;
633+
634+ if ( ! tag ) {
635+ return html `` ;
636+ }
637+
638+ const osdcApi = new OscdApi ( tag ) ;
639+ return staticTagHtml `<${ tag }
640+ .doc= ${ this . doc }
641+ .docName = ${ this . docName }
642+ .editCount = ${ this . editCount }
643+ .plugins = ${ this . host . storedPlugins }
644+ .docId = ${ this . host . docId }
645+ .pluginId = ${ plugin . src }
646+ .nsdoc = ${ this . host . nsdoc }
647+ .docs = ${ this . host . docs }
648+ .locale = ${ this . host . locale }
649+ .oscdApi = ${ osdcApi }
650+ .editor = ${ this . editor }
651+ class= "${ classMap ( {
652+ plugin : true ,
653+ menu : plugin . kind === 'menu' ,
654+ validator : plugin . kind === 'validator' ,
655+ editor : plugin . kind === 'editor' ,
656+ } ) } "
657+ > </ ${ tag } > `
658+ }
659+
581660
582661
583662
@@ -630,6 +709,10 @@ export class OscdLayout extends LitElement {
630709 font-weight : 300 ;
631710 }
632711
712+ # menuContent {
713+ display : none;
714+ }
715+
633716 .landing {
634717 position : absolute;
635718 text-align : center;
0 commit comments