diff --git a/packages/external-plugins/oscd-publisher b/packages/external-plugins/oscd-publisher deleted file mode 160000 index afb25a6e1e..0000000000 --- a/packages/external-plugins/oscd-publisher +++ /dev/null @@ -1 +0,0 @@ -Subproject commit afb25a6e1ea741a9ba0ff261e1233376d1153953 diff --git a/packages/external-plugins/oscd-subscriber-later-binding b/packages/external-plugins/oscd-subscriber-later-binding deleted file mode 160000 index 8ad609be13..0000000000 --- a/packages/external-plugins/oscd-subscriber-later-binding +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8ad609be13a12c6f2dce7cd7dd0a2debff5c6161 diff --git a/packages/openscd/src/Wizarding.ts b/packages/openscd/src/Wizarding.ts index 018876114b..2e4b379ee3 100644 --- a/packages/openscd/src/Wizarding.ts +++ b/packages/openscd/src/Wizarding.ts @@ -1,4 +1,4 @@ -import { html, state, TemplateResult, query } from 'lit-element'; +import { html, query, state, TemplateResult } from 'lit-element'; import { ifImplemented, LitElementConstructor, @@ -6,9 +6,32 @@ import { WizardEvent, WizardFactory, } from './foundation.js'; - import './wizard-dialog.js'; import { WizardDialog } from './wizard-dialog.js'; +import { CreateWizardRequest, EditWizardRequest } from './scl-wizarding.js'; + +function adaptV3Wizard(v3Def: unknown): WizardFactory | null { + const v3 = v3Def as { + steps: Array<{ + title: string; + render: () => TemplateResult; + }>; + }; + if (!v3?.steps?.length) return null; + + return () => + v3.steps.map(step => ({ + title: step.title, + content: [step.render()], + actions: [ + { + label: 'Close', + icon: 'close', + action: () => [], + }, + ], + })); +} /** `LitElement` mixin that adds a `workflow` property which [[`Wizard`]]s are * queued onto on incoming [[`WizardEvent`]]s, first come first displayed. */ @@ -16,27 +39,78 @@ export type WizardingElement = Mixin; export function Wizarding(Base: TBase) { class WizardingElement extends Base { - /** FIFO queue of [[`Wizard`]]s to display. */ + /** FIFO queue of Wizard to display. */ @state() workflow: WizardFactory[] = []; - @query('wizard-dialog') wizardUI!: WizardDialog; + @query('wizard-dialog') + wizardUI!: WizardDialog; + + private onWizard(e: WizardEvent) { + const { wizard, subwizard, v3Wizard } = e.detail; + if (v3Wizard) { + const adapted = adaptV3Wizard(v3Wizard); + if (adapted === null) { + this.workflow.shift(); + } else if (subwizard) { + this.workflow.unshift(adapted); + } else { + this.workflow.push(adapted); + } + } else if (wizard === null) { + this.workflow.shift(); + } else if (wizard) { + subwizard ? this.workflow.unshift(wizard) : this.workflow.push(wizard); + } + + this.updateWizards(); + } + + private onWizardRequest( + e: CustomEvent + ) { + const detail = e.detail as (EditWizardRequest | CreateWizardRequest) & { + wizard?: WizardFactory; + }; + + if ('wizard' in detail && detail.wizard) { + const wf = detail.wizard as WizardFactory; + detail.subWizard ? this.workflow.unshift(wf) : this.workflow.push(wf); + } else { + console.warn('[WizardingElement] No wizard provided, skipping...'); + } + this.updateWizards(); + } + + private onCloseWizard() { + this.workflow.shift(); + this.updateWizards(); + } - private onWizard(we: WizardEvent) { - const wizard = we.detail.wizard; - if (wizard === null) this.workflow.shift(); - else if (we.detail.subwizard) this.workflow.unshift(wizard); - else this.workflow.push(wizard); + private updateWizards() { this.requestUpdate('workflow'); - this.updateComplete.then(() => - this.wizardUI.updateComplete.then(() => - this.wizardUI.dialog?.updateComplete.then(() => - this.wizardUI.dialog?.focus() - ) - ) - ); + this.updateComplete + .then(() => this.wizardUI.updateComplete) + .then(() => this.wizardUI.dialog?.updateComplete) + .then(() => this.wizardUI.dialog?.focus()); } + /*constructor(...args: any[]) { + super(...args); + + this.addEventListener('wizard', this.onWizard as EventListener); + this.addEventListener('oscd-edit-wizard-request', (e: Event) => + this.onWizardRequest(e as CustomEvent) + ); + this.addEventListener('oscd-create-wizard-request', (e: Event) => + this.onWizardRequest(e as CustomEvent) + ); + this.addEventListener('oscd-close-wizard', () => this.onCloseWizard()); + this.addEventListener('editor-action', () => + this.wizardUI?.requestUpdate() + ); + }*/ + constructor(...args: any[]) { super(...args); @@ -47,8 +121,15 @@ export function Wizarding(Base: TBase) { } render(): TemplateResult { - return html`${ifImplemented(super.render())} - `; + return html` + ${ifImplemented(super.render())} + + + `; } } diff --git a/packages/openscd/src/addons/Wizards.ts b/packages/openscd/src/addons/Wizards.ts index 1cfad52a93..3e0a5d84a3 100644 --- a/packages/openscd/src/addons/Wizards.ts +++ b/packages/openscd/src/addons/Wizards.ts @@ -1,58 +1,127 @@ import { - html, - state, - TemplateResult, - query, customElement, + html, LitElement, property, + query, + state, + TemplateResult, } from 'lit-element'; import { WizardEvent, WizardFactory } from '../foundation.js'; - import '../wizard-dialog.js'; import { WizardDialog } from '../wizard-dialog.js'; +import { CreateWizardRequest, EditWizardRequest } from '../scl-wizarding.js'; + +function adaptV3Wizard(v3Def: unknown): WizardFactory | null { + const v3 = v3Def as { + steps: Array<{ + title: string; + render: () => TemplateResult; + }>; + }; + if (!v3?.steps?.length) return null; + + return () => + v3.steps.map(step => ({ + title: step.title, + content: [step.render()], + actions: [ + { + label: 'Close', + icon: 'close', + action: () => [], + }, + ], + })); +} /** `LitElement` mixin that adds a `workflow` property which [[`Wizard`]]s are * queued onto on incoming [[`WizardEvent`]]s, first come first displayed. */ @customElement('oscd-wizards') export class OscdWizards extends LitElement { - @property({ - type: Object, - }) + @property({ type: Object }) host!: HTMLElement; - /** FIFO queue of [[`Wizard`]]s to display. */ + /** FIFO queue of WizardFactories to display */ @state() workflow: WizardFactory[] = []; - @query('wizard-dialog') wizardUI!: WizardDialog; + @query('wizard-dialog') + wizardUI!: WizardDialog; - private onWizard(we: WizardEvent) { - const wizard = we.detail.wizard; - if (wizard === null) this.workflow.shift(); - else if (we.detail.subwizard) this.workflow.unshift(wizard); - else this.workflow.push(wizard); + private onWizard(event: WizardEvent) { + const { wizard, subwizard, v3Wizard } = event.detail; + if (v3Wizard) { + const adapted = adaptV3Wizard(v3Wizard); + if (adapted === null) { + this.workflow.shift(); + } else if (subwizard) { + this.workflow.unshift(adapted); + } else { + this.workflow.push(adapted); + } + } else if (wizard === null) { + this.workflow.shift(); + } else if (wizard) { + if (subwizard) { + this.workflow.unshift(wizard); + } else { + this.workflow.push(wizard); + } + } + + this.updateWizards(); + } + + private onWizardRequest( + e: CustomEvent + ) { + const detail = e.detail as (EditWizardRequest | CreateWizardRequest) & { + wizard?: WizardFactory; + }; + if ('wizard' in detail && detail.wizard) { + const wf = detail.wizard as WizardFactory; + detail.subWizard ? this.workflow.unshift(wf) : this.workflow.push(wf); + } else { + console.log('[oscd-wizards] No wizard provided, skipping...'); + } + + this.updateWizards(); + } + + private onCloseWizard() { + this.workflow.shift(); + this.updateWizards(); + } + + private updateWizards() { this.requestUpdate('workflow'); - this.updateComplete.then(() => - this.wizardUI.updateComplete.then(() => - this.wizardUI.dialog?.updateComplete.then(() => - this.wizardUI.dialog?.focus() - ) - ) - ); + this.updateComplete + .then(() => this.wizardUI.updateComplete) + .then(() => this.wizardUI.dialog?.updateComplete) + .then(() => this.wizardUI.dialog?.focus()); } connectedCallback() { super.connectedCallback(); this.host.addEventListener('wizard', this.onWizard.bind(this)); + this.host.addEventListener('oscd-edit-wizard-request', (e: Event) => + this.onWizardRequest(e as CustomEvent) + ); + this.host.addEventListener('oscd-create-wizard-request', (e: Event) => + this.onWizardRequest(e as CustomEvent) + ); + this.host.addEventListener('oscd-close-wizard', () => this.onCloseWizard()); this.host.addEventListener('editor-action', () => this.wizardUI.requestUpdate() ); } render(): TemplateResult { - return html` - `; + return html` + + + `; } } diff --git a/packages/openscd/src/foundation.ts b/packages/openscd/src/foundation.ts index 17996e42fe..fcc341396d 100644 --- a/packages/openscd/src/foundation.ts +++ b/packages/openscd/src/foundation.ts @@ -183,6 +183,7 @@ export type WizardFactory = () => Wizard; export interface WizardDetail { wizard: WizardFactory | null; subwizard?: boolean; + v3Wizard?: unknown; } export type WizardEvent = CustomEvent; export function newWizardEvent( diff --git a/packages/openscd/src/scl-wizarding.ts b/packages/openscd/src/scl-wizarding.ts new file mode 100644 index 0000000000..d436716b22 --- /dev/null +++ b/packages/openscd/src/scl-wizarding.ts @@ -0,0 +1,45 @@ +interface WizardRequestBase { + subWizard?: boolean; +} +export interface EditWizardRequest extends WizardRequestBase { + element: Element; +} +export interface CreateWizardRequest extends WizardRequestBase { + parent: Element; + tagName: string; +} + +type CreateWizardEvent = CustomEvent; +type EditWizardEvent = CustomEvent; + +export function newCreateWizardEvent( + parent: Element, + tagName: string, + subWizard?: boolean, + eventInitDict?: CustomEventInit> +): CreateWizardEvent { + return new CustomEvent('oscd-create-wizard-request', { + bubbles: true, + composed: true, + ...eventInitDict, + detail: { + parent, + tagName, + subWizard, + ...eventInitDict?.detail, + }, + }); +} + +export function newEditWizardEvent( + element: Element, + subWizard?: boolean, + eventInitDict?: CustomEventInit> +): EditWizardEvent { + return new CustomEvent('oscd-edit-wizard-request', { + bubbles: true, + composed: true, + ...eventInitDict, + detail: { element, subWizard, ...eventInitDict?.detail }, + }); +}