From 7ea53ed73b87349fa7df9fcb29bd04e7efe68793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Russ?= Date: Fri, 13 Dec 2024 17:25:13 +0100 Subject: [PATCH] feat: add event to activate editor tab --- packages/openscd/src/addons/Layout.ts | 68 +++++++---- packages/openscd/src/open-scd.ts | 170 +++++++++++++++----------- packages/openscd/src/plugin.ts | 2 +- 3 files changed, 141 insertions(+), 99 deletions(-) diff --git a/packages/openscd/src/addons/Layout.ts b/packages/openscd/src/addons/Layout.ts index fec380051..1a8db74e9 100644 --- a/packages/openscd/src/addons/Layout.ts +++ b/packages/openscd/src/addons/Layout.ts @@ -52,6 +52,7 @@ import '@material/mwc-dialog'; import '@material/mwc-switch'; import '@material/mwc-select'; import '@material/mwc-textfield'; +import { nothing } from 'lit'; @customElement('oscd-layout') @@ -61,7 +62,7 @@ export class OscdLayout extends LitElement { return html`
this.pluginDownloadUI.show()} - @activate-tab=${this.handleActivatedEditorTabByEvent} + @oscd-activate-editor=${this.handleActivateEditorByEvent} > ${this.renderHeader()} ${this.renderAside()} ${this.renderContent()} @@ -156,6 +157,7 @@ export class OscdLayout extends LitElement { }, disabled: (): boolean => !this.historyState.canUndo, kind: 'static', + content: () => html``, }, { icon: 'redo', @@ -166,6 +168,7 @@ export class OscdLayout extends LitElement { }, disabled: (): boolean => !this.historyState.canRedo, kind: 'static', + content: () => html``, }, ...validators, { @@ -176,6 +179,7 @@ export class OscdLayout extends LitElement { this.dispatchEvent(newHistoryUIEvent(true, HistoryUIKind.log)); }, kind: 'static', + content: () => html``, }, { icon: 'history', @@ -185,6 +189,7 @@ export class OscdLayout extends LitElement { this.dispatchEvent(newHistoryUIEvent(true, HistoryUIKind.history)); }, kind: 'static', + content: () => html``, }, { icon: 'rule', @@ -194,6 +199,7 @@ export class OscdLayout extends LitElement { this.dispatchEvent(newHistoryUIEvent(true, HistoryUIKind.diagnostic)); }, kind: 'static', + content: () => html``, }, 'divider', ...middleMenu, @@ -204,6 +210,7 @@ export class OscdLayout extends LitElement { this.dispatchEvent(newSettingsUIEvent(true)); }, kind: 'static', + content: () => html``, }, ...bottomMenu, { @@ -211,6 +218,7 @@ export class OscdLayout extends LitElement { name: 'plugins.heading', action: (): void => this.pluginUI.show(), kind: 'static', + content: () => html``, }, ]; } @@ -337,7 +345,10 @@ export class OscdLayout extends LitElement { ); }, disabled: (): boolean => plugin.requireDoc! && this.doc === null, - content: plugin.content, + content: () => { + if(plugin.content){ return plugin.content(); } + return nothing + }, kind: kind, } }) @@ -362,15 +373,18 @@ export class OscdLayout extends LitElement { ); }, disabled: (): boolean => this.doc === null, - content: plugin.content, + content: plugin.content ?? (() => html``), kind: 'validator', } }); } private renderMenuItem(me: MenuItem | 'divider'): TemplateResult { - if (me === 'divider') { return html`
  • `; } - if (me.actionItem){ return html``; } + const isDivider = me === 'divider'; + const hasActionItem = me !== 'divider' && me.actionItem; + + if (isDivider) { return html`
  • `; } + if (hasActionItem){ return html``; } return html` ${me.hint}` : ''} - ${me.content ?? ''} + ${me.content ? me.content() : nothing} `; } @@ -459,18 +473,23 @@ export class OscdLayout extends LitElement { } + private calcActiveEditors(){ + const hasActiveDoc = Boolean(this.doc); + + return this.editors + .filter(editor => { + // this is necessary because `requireDoc` can be undefined + // and that is not the same as false + const doesNotRequireDoc = editor.requireDoc === false + return doesNotRequireDoc || hasActiveDoc + }) + } + /** Renders the enabled editor plugins and a tab bar to switch between them*/ protected renderContent(): TemplateResult { - const hasActiveDoc = Boolean(this.doc); - const activeEditors = this.editors - .filter(editor => { - // this is necessary because `requireDoc` can be undefined - // and that is not the same as false - const doesNotRequireDoc = editor.requireDoc === false - return doesNotRequireDoc || hasActiveDoc - }) - .map(this.renderEditorTab) + const activeEditors = this.calcActiveEditors() + .map(this.renderEditorTab) const hasActiveEditors = activeEditors.length > 0; if(!hasActiveEditors){ return html``; } @@ -493,7 +512,7 @@ export class OscdLayout extends LitElement { const content = editor?.content; if(!content) { return html`` } - return html`${content}`; + return html`${content()}`; } } @@ -502,16 +521,13 @@ export class OscdLayout extends LitElement { this.activateTab(tabIndex); } - /** - * - * - * @param e Custom event, where the detail is the index number of the tab - */ - // Note: this function is for now the same as `handleActivatedEditorTabByUser` - // I would keep it spearated for now, because I think they will be different. - private handleActivatedEditorTabByEvent(e: CustomEvent<{index:number}>): void { - const tabIndex = e.detail.index - this.activateTab(tabIndex) + private handleActivateEditorByEvent(e: CustomEvent<{name: string, src: string}>): void { + const {name, src} = e.detail; + const editors = this.calcActiveEditors() + const wantedEditorIndex = editors.findIndex(editor => editor.name === name || editor.src === src) + if(wantedEditorIndex < 0){ return; } // TODO: log error + + this.activateTab(wantedEditorIndex); } private activateTab(index: number){ diff --git a/packages/openscd/src/open-scd.ts b/packages/openscd/src/open-scd.ts index d252056db..fc3a2645e 100644 --- a/packages/openscd/src/open-scd.ts +++ b/packages/openscd/src/open-scd.ts @@ -81,7 +81,7 @@ export class OpenSCD extends LitElement { .docName=${this.docName} .editCount=${this.historyState.editCount} .historyState=${this.historyState} - .plugins=${this.sortedStoredPlugins} + .plugins=${this.storedPlugins} > @@ -181,15 +181,13 @@ export class OpenSCD extends LitElement { connectedCallback(): void { super.connectedCallback(); - this.loadPluginsFromLocalStorage() + this.loadPlugins() + + // TODO: let Lit handle the event listeners, move to render() this.addEventListener('reset-plugins', this.resetPlugins); this.addEventListener('set-plugins', (e: SetPluginsEvent) => { this.setPlugins(e.detail.indices); }); - - this.updatePlugins(); - this.requestUpdate(); - this.addEventListener(historyStateEvent, (e: CustomEvent) => { this.historyState = e.detail; this.requestUpdate(); @@ -199,11 +197,8 @@ export class OpenSCD extends LitElement { private storePlugins(plugins: Array) { - localStorage.setItem( - 'plugins', - JSON.stringify(plugins.map(withoutContent)) - ); - this.requestUpdate(); + const pluginConfigs = JSON.stringify(plugins.map(withoutContent)) + localStorage.setItem('plugins', pluginConfigs); } /** @@ -264,9 +259,8 @@ export class OpenSCD extends LitElement { this.storePlugins( (builtinPlugins as Plugin[]).concat(this.parsedPlugins).map(plugin => { return { - src: plugin.src, + ...plugin, installed: plugin.default ?? false, - official: true, }; }) ); @@ -305,31 +299,12 @@ export class OpenSCD extends LitElement { return allPlugnis } - @state() private sortedStoredPlugins: Plugin[] = []; - private updateSortedStoredPlugins(newPlugins: Plugin[]) { - const mergedPlugins = newPlugins.map(plugin => { - if (!plugin.official){ return plugin }; - - const officialPlugin = (builtinPlugins as Plugin[]) - .concat(this.parsedPlugins) - .find(needle => needle.src === plugin.src); - - return { - ...officialPlugin, - ...plugin, - }; - }) - this.sortedStoredPlugins = mergedPlugins; - // return mergedPlugins; - // return mergedPlugins - // .sort(compareNeedsDoc) - // .sort(menuCompare); - } @state() private storedPlugins: Plugin[] = []; private updateStoredPlugins(newPlugins: Plugin[]) { - // const pluginsConfigStr = localStorage.getItem('plugins') ?? '[]' - // const storedPlugins = JSON.parse(pluginsConfigStr) as Plugin[] + // + // Generate content of each plugin + // const plugins = newPlugins.map(plugin => { const isInstalled = plugin.src && plugin.installed if(!isInstalled) { return plugin } @@ -337,14 +312,36 @@ export class OpenSCD extends LitElement { return this.addContent(plugin) }) - this.storedPlugins = plugins; + // + // Merge built-in plugins + // + const mergedPlugins = plugins.map(plugin => { + const isBuiltIn = !plugin?.official + if (!isBuiltIn){ return plugin }; + + const builtInPlugin = [...builtinPlugins, ...this.parsedPlugins] + .find(p => p.src === plugin.src); + + return { + ...builtInPlugin, + ...plugin, + }; + }) + + + this.storedPlugins = mergedPlugins; + this.storePlugins(this.storedPlugins); + } + + private getPluginConfigsFromLocalStorage(): Plugin[] { + const pluginsConfigStr = localStorage.getItem('plugins') ?? '[]' + return JSON.parse(pluginsConfigStr) as Plugin[] } private loadPluginsFromLocalStorage() { const pluginsConfigStr = localStorage.getItem('plugins') ?? '[]' const pluginsConfig = JSON.parse(pluginsConfigStr) as Plugin[] this.updateStoredPlugins(pluginsConfig) - this.updateSortedStoredPlugins(this.storedPlugins) } @@ -363,48 +360,76 @@ export class OpenSCD extends LitElement { } private setPlugins(indices: Set) { - const newPlugins = this.sortedStoredPlugins.map((plugin, index) => { + const newPlugins = this.storedPlugins.map((plugin, index) => { return { ...plugin, installed: indices.has(index) }; }); this.updateStoredPlugins(newPlugins); - - this.updateSortedStoredPlugins(this.sortedStoredPlugins); - this.storePlugins(this.sortedStoredPlugins); } - private updatePlugins() { + private loadPlugins(){ + const localPluginConfigs = this.getPluginConfigsFromLocalStorage() - const stored: Plugin[] = this.storedPlugins; - const officialStored = stored.filter(p => p.official); - const newOfficial: Array = ( - builtinPlugins as Plugin[] - ) - .concat(this.parsedPlugins) - .filter(p => !officialStored.find(o => o.src === p.src)) - .map(plugin => { - return { - src: plugin.src, - installed: plugin.default ?? false, - official: true as const, - }; - }); + const overwritesOfBultInPlugins = localPluginConfigs.filter((p) => { + return builtinPlugins.some(b => b.src === p.src) + }) - const oldOfficial = officialStored.filter( - p => - !(builtinPlugins as Plugin[]) - .concat(this.parsedPlugins) - .find(o => p.src === o.src) - ); - const newPlugins: Array = stored.filter( - p => !oldOfficial.find(o => p.src === o.src) - ); - newOfficial.map(p => newPlugins.push(p)); - this.storePlugins(newPlugins); + const userInstalledPlugins = localPluginConfigs.filter((p) => { + return !builtinPlugins.some(b => b.src === p.src) + }) + + const mergedBuiltInPlugins = builtinPlugins.map((builtInPlugin) => { + const noopOverwrite = {} + const overwrite = overwritesOfBultInPlugins + .find(p => p.src === builtInPlugin.src) + ?? noopOverwrite + + return { + ...builtInPlugin, + ...overwrite, + installed: true, // TODO: is this correct? should we decide it based on something? + } + }) + + const mergedPlugins = [...mergedBuiltInPlugins, ...userInstalledPlugins] + + // TODO: kind is string and enum, figour out later + // @ts-expect-error + this.updateStoredPlugins(mergedPlugins) } + // private updatePlugins() { + + // const stored: Plugin[] = this.storedPlugins; + // const officialStored = stored.filter(p => p.official); + // const newOfficial: Array = ( + // builtinPlugins as Plugin[] + // ) + // .concat(this.parsedPlugins) + // .filter(p => !officialStored.find(o => o.src === p.src)) + // .map(plugin => { + // return { + // src: plugin.src, + // installed: plugin.default ?? false, + // official: true as const, + // }; + // }); + + // const oldOfficial = officialStored.filter( + // p => + // !(builtinPlugins as Plugin[]) + // .concat(this.parsedPlugins) + // .find(o => p.src === o.src) + // ); + // const newPlugins: Array = stored.filter( + // p => !oldOfficial.find(o => p.src === o.src) + // ); + // newOfficial.map(p => newPlugins.push(p)); + // this.storePlugins(newPlugins); + // } + private async addExternalPlugin( plugin: Omit ): Promise { @@ -424,26 +449,27 @@ export class OpenSCD extends LitElement { customElements.define(tag, mod.default) }) } - return { ...plugin, - content: staticTagHtml`<${tag} + content: () => { + return staticTagHtml`<${tag} .doc=${this.doc} .docName=${this.docName} .editCount=${this.historyState.editCount} + .plugins=${this.storedPlugins} .docId=${this.docId} .pluginId=${plugin.src} .nsdoc=${this.nsdoc} .docs=${this.docs} .locale=${this.locale} - .plugins=${this.sortedStoredPlugins} class="${classMap({ plugin: true, menu: plugin.kind === 'menu', validator: plugin.kind === 'validator', editor: plugin.kind === 'editor', })}" - >`, + >` + }, }; } @@ -502,7 +528,7 @@ export interface MenuItem { actionItem?: boolean; action?: (event: CustomEvent) => void; disabled?: () => boolean; - content?: TemplateResult; + content: () => TemplateResult; kind: string; } diff --git a/packages/openscd/src/plugin.ts b/packages/openscd/src/plugin.ts index cfc4ffddb..0ff10bb78 100644 --- a/packages/openscd/src/plugin.ts +++ b/packages/openscd/src/plugin.ts @@ -10,7 +10,7 @@ export type Plugin = { position?: MenuPosition; installed: boolean; official?: boolean; - content?: TemplateResult; + content?: () => TemplateResult; }; export type InstalledOfficialPlugin = {