@@ -45,6 +45,10 @@ import type {
4545 Plugin as CorePlugin ,
4646 EditCompletedEvent ,
4747} from '@openscd/core' ;
48+ import { InstalledOfficialPlugin , MenuPosition , PluginKind , Plugin } from "./plugin.js"
49+ import { ConfigurePluginEvent , ConfigurePluginDetail , newConfigurePluginEvent } from './plugin.events.js' ;
50+ import { newLogEvent } from '@openscd/core/foundation/deprecated/history' ;
51+
4852
4953// HOSTING INTERFACES
5054
@@ -173,28 +177,6 @@ function staticTagHtml(
173177 return html ( < TemplateStringsArray > strings , ...args ) ;
174178}
175179
176- export type PluginKind = 'editor' | 'menu' | 'validator' ;
177- export const menuPosition = [ 'top' , 'middle' , 'bottom' ] as const ;
178- export type MenuPosition = ( typeof menuPosition ) [ number ] ;
179-
180- export type Plugin = {
181- name : string ;
182- src : string ;
183- icon ?: string ;
184- default ?: boolean ;
185- kind : PluginKind ;
186- requireDoc ?: boolean ;
187- position ?: MenuPosition ;
188- installed : boolean ;
189- official ?: boolean ;
190- content ?: TemplateResult ;
191- } ;
192-
193- type InstalledOfficialPlugin = {
194- src : string ;
195- official : true ;
196- installed : boolean ;
197- } ;
198180
199181function withoutContent < P extends Plugin | InstalledOfficialPlugin > (
200182 plugin : P
@@ -278,15 +260,53 @@ export class OpenSCD extends LitElement {
278260 if ( src . startsWith ( 'blob:' ) ) URL . revokeObjectURL ( src ) ;
279261 }
280262
263+ /**
264+ *
265+ * @deprecated Use `handleConfigurationPluginEvent` instead
266+ */
267+ public handleAddExternalPlugin ( e : AddExternalPluginEvent ) {
268+ this . addExternalPlugin ( e . detail . plugin ) ;
269+ const { name, kind} = e . detail . plugin
270+
271+ const event = newConfigurePluginEvent ( name , kind , e . detail . plugin )
272+
273+ this . handleConfigurationPluginEvent ( event )
274+ }
275+
276+
277+ public handleConfigurationPluginEvent ( e : ConfigurePluginEvent ) {
278+ const { name, kind, config } = e . detail ;
279+
280+ const hasPlugin = this . hasPlugin ( name , kind ) ;
281+ const hasConfig = config !== null ;
282+ const isChangeEvent = hasPlugin && hasConfig ;
283+ const isRemoveEvent = hasPlugin && ! hasConfig ;
284+ const isAddEvent = ! hasPlugin && hasConfig ;
285+
286+ // the `&& config`is only because typescript
287+ // cannot infer that `isChangeEvent` and `isAddEvent` implies `config !== null`
288+ if ( isChangeEvent && config ) {
289+ this . changePlugin ( config ) ;
290+
291+ } else if ( isRemoveEvent ) {
292+ this . removePlugin ( name , kind ) ;
293+
294+ } else if ( isAddEvent && config ) {
295+ this . addPlugin ( config ) ;
296+
297+ } else {
298+ const event = newLogEvent ( {
299+ kind : "error" ,
300+ title : "Invalid plugin configuration event" ,
301+ message : JSON . stringify ( { name, kind, config} ) ,
302+ } ) ;
303+ this . dispatchEvent ( event ) ;
304+ }
305+ }
306+
281307 connectedCallback ( ) : void {
282308 super . connectedCallback ( ) ;
283309 this . addEventListener ( 'reset-plugins' , this . resetPlugins ) ;
284- this . addEventListener (
285- 'add-external-plugin' ,
286- ( e : AddExternalPluginEvent ) => {
287- this . addExternalPlugin ( e . detail . plugin ) ;
288- }
289- ) ;
290310 this . addEventListener ( 'set-plugins' , ( e : SetPluginsEvent ) => {
291311 this . setPlugins ( e . detail . indices ) ;
292312 } ) ;
@@ -320,6 +340,8 @@ export class OpenSCD extends LitElement {
320340 .editCount=${ this . editCount }
321341 >
322342 <oscd-layout
343+ @add-external-plugin=${ this . handleAddExternalPlugin }
344+ @oscd-configure-plugin=${ this . handleConfigurationPluginEvent }
323345 .host=${ this }
324346 .doc=${ this . doc }
325347 .docName=${ this . docName }
@@ -341,6 +363,61 @@ export class OpenSCD extends LitElement {
341363 ) ;
342364 this . requestUpdate ( ) ;
343365 }
366+
367+ /**
368+ *
369+ * @param name
370+ * @param kind
371+ * @returns the index of the plugin in the stored plugin list
372+ */
373+ private findPluginIndex ( name : string , kind : PluginKind ) : number {
374+ return this . storedPlugins . findIndex ( p => p . name === name && p . kind === kind ) ;
375+ }
376+
377+ private hasPlugin ( name : string , kind : PluginKind ) : boolean {
378+ return this . findPluginIndex ( name , kind ) > - 1 ;
379+ }
380+
381+ private removePlugin ( name : string , kind : PluginKind ) {
382+ const newPlugins = this . storedPlugins . filter (
383+ p => p . name !== name || p . kind !== kind
384+ ) ;
385+ this . storePlugins ( newPlugins ) ;
386+ }
387+
388+ private addPlugin ( plugin : Plugin ) {
389+ const newPlugins = [ ...this . storedPlugins , plugin ] ;
390+ this . storePlugins ( newPlugins ) ;
391+ }
392+
393+ /**
394+ *
395+ * @param plugin
396+ * @throws if the plugin is not found
397+ */
398+ private changePlugin ( plugin : Plugin ) {
399+ const storedPlugins = this . storedPlugins ;
400+ const { name, kind} = plugin ;
401+ const pluginIndex = this . findPluginIndex ( name , kind ) ;
402+
403+ if ( pluginIndex < 0 ) {
404+ const event = newLogEvent ( {
405+ kind : "error" ,
406+ title : "Plugin not found, stopping change process" ,
407+ message : JSON . stringify ( { name, kind} ) ,
408+ } )
409+ this . dispatchEvent ( event ) ;
410+ return ;
411+ }
412+
413+ const pluginToChange = storedPlugins [ pluginIndex ]
414+ const changedPlugin = { ...pluginToChange , ...plugin }
415+ const newPlugins = [ ...storedPlugins ]
416+ newPlugins . splice ( pluginIndex , 1 , changedPlugin )
417+
418+ this . storePlugins ( newPlugins ) ;
419+ }
420+
344421 private resetPlugins ( ) : void {
345422 this . storePlugins (
346423 ( builtinPlugins as Plugin [ ] ) . concat ( this . parsedPlugins ) . map ( plugin => {
0 commit comments