@omicronenergy/oscd-shell 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/colors.d.ts +1 -0
  2. package/dist/colors.js +60 -0
  3. package/dist/colors.js.map +1 -0
  4. package/dist/foundation/cyrb64.js +0 -3
  5. package/dist/foundation/cyrb64.js.map +1 -1
  6. package/dist/foundation/events.d.ts +17 -0
  7. package/dist/foundation/events.js +2 -0
  8. package/dist/foundation/events.js.map +1 -0
  9. package/dist/foundation.js.map +1 -1
  10. package/dist/landing-page/landing-page.d.ts +26 -0
  11. package/dist/landing-page/landing-page.js +163 -0
  12. package/dist/landing-page/landing-page.js.map +1 -0
  13. package/dist/landing-page/landing-page.spec.d.ts +1 -0
  14. package/dist/landing-page/landing-page.spec.js +87 -0
  15. package/dist/landing-page/landing-page.spec.js.map +1 -0
  16. package/dist/locales/de.d.ts +0 -5
  17. package/dist/locales/de.js +0 -5
  18. package/dist/locales/de.js.map +1 -1
  19. package/dist/locales.js.map +1 -1
  20. package/dist/localization.d.ts +15 -0
  21. package/dist/localization.js +22 -0
  22. package/dist/localization.js.map +1 -0
  23. package/dist/menus/files-menu.d.ts +27 -0
  24. package/dist/menus/files-menu.js +102 -0
  25. package/dist/menus/files-menu.js.map +1 -0
  26. package/dist/menus/files-menu.spec.d.ts +1 -0
  27. package/dist/menus/files-menu.spec.js +33 -0
  28. package/dist/menus/files-menu.spec.js.map +1 -0
  29. package/dist/menus/plugins-menu.d.ts +32 -0
  30. package/dist/menus/plugins-menu.js +128 -0
  31. package/dist/menus/plugins-menu.js.map +1 -0
  32. package/dist/menus/plugins-menu.spec.d.ts +1 -0
  33. package/dist/menus/plugins-menu.spec.js +65 -0
  34. package/dist/menus/plugins-menu.spec.js.map +1 -0
  35. package/dist/oscd-shell.d.ts +70 -84
  36. package/dist/oscd-shell.js +378 -549
  37. package/dist/oscd-shell.js.map +1 -1
  38. package/dist/side-panel/editor-plugins-panel.d.ts +29 -0
  39. package/dist/side-panel/editor-plugins-panel.js +151 -0
  40. package/dist/side-panel/editor-plugins-panel.js.map +1 -0
  41. package/dist/side-panel/editor-plugins-panel.spec.d.ts +1 -0
  42. package/dist/side-panel/editor-plugins-panel.spec.js +87 -0
  43. package/dist/side-panel/editor-plugins-panel.spec.js.map +1 -0
  44. package/dist/theming.d.ts +1 -0
  45. package/dist/theming.js +76 -0
  46. package/dist/theming.js.map +1 -0
  47. package/dist/utils/plugin-utils.d.ts +1 -1
  48. package/dist/utils/plugin-utils.js +8 -11
  49. package/dist/utils/plugin-utils.js.map +1 -1
  50. package/dist/utils/plugin-utils.spec.js.map +1 -1
  51. package/dist/utils/testing/plugin-helpers.d.ts +24 -0
  52. package/dist/utils/testing/plugin-helpers.js +74 -0
  53. package/dist/utils/testing/plugin-helpers.js.map +1 -0
  54. package/dist/utils/testing/test-doc-helpers.d.ts +7 -0
  55. package/dist/utils/testing/test-doc-helpers.js +28 -0
  56. package/dist/utils/testing/test-doc-helpers.js.map +1 -0
  57. package/dist/utils/testing/test-plugins.d.ts +44 -0
  58. package/dist/utils/testing/test-plugins.js +56 -0
  59. package/dist/utils/testing/test-plugins.js.map +1 -0
  60. package/package.json +61 -125
  61. package/dist/AddPlugins.js +0 -39
  62. package/dist/DemoPluginSrc.js +0 -7
  63. package/dist/embedded.html +0 -17
  64. package/dist/index.html +0 -112
@@ -1,186 +1,87 @@
1
- var _OpenSCD_plugins, _OpenSCD_actions;
2
- import { __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib";
3
- import '@webcomponents/scoped-custom-element-registry';
4
- import { configureLocalization, localized, msg } from '@lit/localize';
1
+ import { __decorate } from "tslib";
5
2
  import { css, html, LitElement, nothing } from 'lit';
6
- import { customElement, property, query, state } from 'lit/decorators.js';
3
+ import { customElement, property, query, queryAssignedNodes, state, } from 'lit/decorators.js';
4
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
5
+ import { localized, msg } from '@lit/localize';
7
6
  import { html as staticHtml, unsafeStatic } from 'lit/static-html.js';
8
- import '@omicronenergy/oscd-ui/app-bar/oscd-app-bar.js';
9
- import '@omicronenergy/oscd-ui/dialog/oscd-dialog.js';
10
- import '@omicronenergy/oscd-ui/divider/oscd-divider.js';
11
- import '@omicronenergy/oscd-ui/iconbutton/oscd-filled-icon-button.js';
12
- import '@omicronenergy/oscd-ui/select/oscd-filled-select.js';
13
- import '@omicronenergy/oscd-ui/textfield/oscd-filled-text-field.js';
14
- import '@omicronenergy/oscd-ui/icon/oscd-icon.js';
15
- import '@omicronenergy/oscd-ui/list/oscd-list.js';
16
- import '@omicronenergy/oscd-ui/list/oscd-list-item.js';
17
- import '@omicronenergy/oscd-ui/menu/oscd-menu.js';
18
- import '@omicronenergy/oscd-ui/menu/oscd-menu-item.js';
19
- import '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer.js';
20
- import '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer-header.js';
21
- import '@omicronenergy/oscd-ui/tabs/oscd-secondary-tab.js';
22
- import '@omicronenergy/oscd-ui/select/oscd-select-option.js';
23
- import '@omicronenergy/oscd-ui/tabs/oscd-tabs.js';
24
- import '@omicronenergy/oscd-ui/button/oscd-text-button.js';
7
+ import { OscdFilledIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdFilledIconButton.js';
25
8
  import { XMLEditor } from '@omicronenergy/oscd-editor';
26
- import { sourceLocale, targetLocales } from './locales.js';
27
9
  import { loadSourcedPlugins } from './utils/plugin-utils.js';
28
- const _customElementsDefine = window.customElements.define;
29
- window.customElements.define = (name, cl, conf) => {
30
- if (!customElements.get(name)) {
31
- _customElementsDefine.call(window.customElements, name, cl, conf);
32
- }
33
- };
34
- const { getLocale, setLocale } = configureLocalization({
35
- sourceLocale,
36
- targetLocales,
37
- loadLocale: locale => import(new URL(`./locales/${locale}.js`, import.meta.url).href),
38
- });
39
- function renderActionItem(control, slot = 'actionItems') {
40
- return html `<oscd-filled-icon-button
41
- slot="${slot}"
42
- aria-label="${control.getName()}"
43
- ?disabled=${control.isDisabled()}
44
- @click=${control.action}
45
- ><oscd-icon>${control.icon}</oscd-icon></oscd-filled-icon-button
46
- >`;
47
- }
48
- function renderMenuItem(control) {
49
- return html `
50
- <oscd-list-item
51
- type="button"
52
- .disabled=${control.isDisabled()}
53
- @click=${control.action}
54
- >
55
- <oscd-icon slot="start">${control.icon}</oscd-icon>
56
- <span>${control.getName()}</span>
57
- </oscd-list-item>
58
- `;
59
- }
60
- let OpenSCD = class OpenSCD extends LitElement {
61
- isEditable(docName) {
62
- return !!this.editable.find(ext => docName.toLowerCase().endsWith(`.${ext}`));
63
- }
64
- get editableDocs() {
65
- return Object.keys(this.docs).filter(name => this.isEditable(name));
66
- }
67
- /** The set of `XMLDocument`s currently loaded */
68
- get docs() {
69
- return this._docs;
70
- }
71
- set docs(newDocs) {
72
- this._docs = newDocs;
73
- this.onDocsChanged();
74
- }
75
- onDocsChanged() {
76
- this.docVersion += 1;
77
- }
78
- get doc() {
79
- return this.docs[this.docName];
80
- }
81
- get last() {
82
- return this.xmlEditor.past.length - 1;
83
- }
84
- get canUndo() {
85
- return this.xmlEditor.past.length >= 0;
10
+ import { getLocale, setLocale, } from './localization.js';
11
+ import { theming } from './theming.js';
12
+ import { EditorPluginsPanel } from './side-panel/editor-plugins-panel.js';
13
+ import { PluginsMenu } from './menus/plugins-menu.js';
14
+ import { LandingPage } from './landing-page/landing-page.js';
15
+ import { FilesMenu } from './menus/files-menu.js';
16
+ import { OscdAppBar } from '@omicronenergy/oscd-ui/app-bar/OscdAppBar.js';
17
+ import { OscdIcon } from '@omicronenergy/oscd-ui/icon/OscdIcon.js';
18
+ let OscdShell = class OscdShell extends ScopedElementsMixin(LitElement) {
19
+ get locale() {
20
+ return getLocale();
86
21
  }
87
- get canRedo() {
88
- return this.xmlEditor.future.length >= 0;
22
+ set locale(tag) {
23
+ try {
24
+ if (tag) {
25
+ setLocale(tag);
26
+ }
27
+ }
28
+ catch {
29
+ // don't change locale if tag is invalid
30
+ }
89
31
  }
90
32
  get plugins() {
91
- return __classPrivateFieldGet(this, _OpenSCD_plugins, "f");
33
+ return this._plugins;
92
34
  }
93
35
  set plugins(plugins) {
94
- __classPrivateFieldSet(this, _OpenSCD_plugins, Object.entries(plugins).reduce((acc, [pluginType, kind]) => {
95
- const convertedPlugins = loadSourcedPlugins(kind);
36
+ this._plugins = Object.entries(plugins).reduce((acc, [pluginType, kind]) => {
37
+ const convertedPlugins = loadSourcedPlugins(kind, this.registry);
96
38
  acc[pluginType] = convertedPlugins;
97
39
  return acc;
98
- }, { menu: [], editor: [], background: [] }), "f");
40
+ }, { menu: [], editor: [], background: [] });
99
41
  }
100
- handleOpenDoc({ detail: { docName, doc } }) {
101
- this.docs = {
102
- ...this.docs,
103
- [docName]: doc,
104
- };
105
- if (this.isEditable(docName)) {
106
- this.docName = docName;
107
- }
108
- this.requestUpdate();
42
+ /*
43
+ * States
44
+ */
45
+ get canRedo() {
46
+ return this.xmlEditor.future.length >= 1;
109
47
  }
110
- /** Undo the last `n` [[Edit]]s committed */
111
- undo(n = 1) {
112
- if (!this.canUndo || n < 1) {
113
- return;
114
- }
115
- this.xmlEditor.undo();
116
- if (n > 1) {
117
- this.undo(n - 1);
118
- }
119
- this.requestUpdate();
48
+ get canUndo() {
49
+ return this.xmlEditor.past.length >= 1;
120
50
  }
121
- /** Redo the last `n` [[Edit]]s that have been undone */
122
- redo(n = 1) {
123
- if (!this.canRedo || n < 1) {
124
- return;
125
- }
126
- this.xmlEditor.redo();
127
- if (n > 1) {
128
- this.redo(n - 1);
129
- }
130
- this.requestUpdate();
51
+ get editor() {
52
+ return this.plugins.editor[this.editorIndex]?.tagName ?? '';
131
53
  }
132
- get locale() {
133
- return getLocale();
54
+ get doc() {
55
+ return this.docs[this.docName];
134
56
  }
135
- set locale(tag) {
136
- try {
137
- setLocale(tag);
138
- }
139
- catch {
140
- // don't change locale if tag is invalid
141
- }
57
+ get docs() {
58
+ return this._docs;
142
59
  }
143
- get editor() {
144
- return this.editors[this.editorIndex]?.tagName ?? '';
145
- }
146
- get menu() {
147
- return (this.plugins.menu
148
- ?.map(({ name, tagName, requireDoc, icon, translations }) => ({
149
- icon,
150
- getName: () => translations?.[this.locale] ||
151
- name,
152
- isDisabled: () => (requireDoc && !this.docName) ?? false,
153
- tagName,
154
- action: () => {
155
- if (tagName) {
156
- this.shadowRoot.querySelector(tagName).run?.();
157
- this.menuUI.opened = false;
158
- }
159
- },
160
- }))
161
- .filter(p => p !== undefined)).concat(__classPrivateFieldGet(this, _OpenSCD_actions, "f"));
162
- }
163
- get editors() {
164
- return this.plugins.editor?.map(({ name, tagName, requireDoc, icon, translations, }) => ({
165
- icon,
166
- getName: () => translations?.[this.locale] ||
167
- name,
168
- isDisabled: () => (requireDoc && !this.docName) ?? false,
169
- tagName,
170
- }));
171
- }
172
- handleKeyPress(e) {
173
- if (!e.ctrlKey) {
174
- return;
175
- }
176
- if (!Object.prototype.hasOwnProperty.call(this.hotkeys, e.key)) {
177
- return;
178
- }
179
- this.hotkeys[e.key]();
180
- e.preventDefault();
60
+ set docs(newDocs) {
61
+ this._docs = newDocs;
62
+ this.onDocsChanged();
63
+ }
64
+ get editableDocs() {
65
+ return Object.keys(this.docs).filter(name => this.isEditable(name));
181
66
  }
67
+ get last() {
68
+ return this.xmlEditor.past.length - 1;
69
+ }
70
+ /*
71
+ * Constructor & life cycle methods
72
+ */
182
73
  constructor() {
183
74
  super();
75
+ /*
76
+ * Properties
77
+ */
78
+ /**
79
+ * Url to the app icon displayed in the app bar
80
+ */
81
+ this.appIcon = '';
82
+ this.appTitle = 'OpenSCD';
83
+ this.landingPageHeading = 'Welcome to OpenSCD';
84
+ this.landingPageSubHeading = 'Open Source IEC-61850-6 SCL Editing Platform';
184
85
  /** The file endings of editable files */
185
86
  this.editable = [
186
87
  'cid',
@@ -190,72 +91,129 @@ let OpenSCD = class OpenSCD extends LitElement {
190
91
  'sed',
191
92
  'ssd',
192
93
  ];
193
- this._docs = {};
94
+ this._plugins = { menu: [], editor: [], background: [] };
95
+ this.editorIndex = 0;
194
96
  /** The name of the [[`doc`]] currently being edited */
195
97
  this.docName = '';
196
- this.xmlEditor = new XMLEditor();
98
+ /** The set of `XMLDocument`s currently loaded */
99
+ this._docs = {};
197
100
  this.docVersion = -1;
198
- _OpenSCD_plugins.set(this, { menu: [], editor: [], background: [] });
199
- this.editorIndex = 0;
200
- this.controls = {
201
- undo: {
202
- icon: 'undo',
203
- getName: () => msg('Undo'),
204
- action: () => {
205
- this.undo();
206
- this.menuUI.opened = false;
207
- },
208
- isDisabled: () => !this.canUndo,
209
- },
210
- redo: {
211
- icon: 'redo',
212
- getName: () => msg('Redo'),
213
- action: () => {
214
- this.redo();
215
- this.menuUI.opened = false;
216
- },
217
- isDisabled: () => !this.canRedo,
218
- },
219
- menu: {
220
- icon: 'menu',
221
- getName: () => msg('Menu'),
222
- action: async () => {
223
- this.menuUI.opened = !this.menuUI.opened;
224
- await this.menuUI.updateComplete;
225
- if (this.menuUI.opened) {
226
- this.menuUI.querySelector('oscd-list').focus();
227
- }
228
- },
229
- isDisabled: () => false,
230
- },
101
+ this.xmlEditor = new XMLEditor();
102
+ /*
103
+ * Event Handlers
104
+ */
105
+ this.handleOpenDoc = ({ detail: { docName, doc } }) => {
106
+ this.docs = {
107
+ ...this.docs,
108
+ [docName]: doc,
109
+ };
110
+ if (this.isEditable(docName)) {
111
+ this.docName = docName;
112
+ }
113
+ this.requestUpdate();
231
114
  };
232
- _OpenSCD_actions.set(this, [this.controls.undo, this.controls.redo]);
233
- this.hotkeys = {
234
- m: this.controls.menu.action,
235
- z: this.controls.undo.action,
236
- y: this.controls.redo.action,
237
- Z: this.controls.redo.action,
115
+ this.handleRenameDoc = (customEvent) => {
116
+ const { oldName, newName } = customEvent.detail;
117
+ if (!this.docs[oldName] || newName === oldName || this.docs[newName]) {
118
+ return;
119
+ }
120
+ const doc = this.docs[oldName];
121
+ delete this.docs[oldName];
122
+ this.docs = {
123
+ ...this.docs,
124
+ [newName]: doc,
125
+ };
126
+ this.docName = newName;
238
127
  };
239
- document.addEventListener('keydown', event => this.handleKeyPress(event));
240
- this.addEventListener('oscd-open', event => this.handleOpenDoc(event));
241
- this.addEventListener('oscd-edit-v2', (event) => {
128
+ this.handleEditV2 = (event) => {
242
129
  const { edit, title, squash } = event.detail;
243
130
  this.xmlEditor.commit(edit, { title, squash });
244
- });
131
+ };
132
+ this.handleCloseDoc = (event) => {
133
+ const docName = event.detail.docName;
134
+ delete this.docs[docName];
135
+ if (this.docName === docName) {
136
+ this.docName = this.editableDocs[0] || '';
137
+ }
138
+ };
139
+ this.handleUndo = () => {
140
+ this.undo();
141
+ };
142
+ this.handleRedo = () => {
143
+ this.redo();
144
+ };
145
+ this.handleKeyPress = (e) => {
146
+ if (!e.ctrlKey) {
147
+ return;
148
+ }
149
+ if (!Object.prototype.hasOwnProperty.call(this.hotkeys, e.key)) {
150
+ return;
151
+ }
152
+ this.hotkeys[e.key].call(this);
153
+ e.preventDefault();
154
+ };
155
+ this.handleOpenPluginMenu = () => {
156
+ this.pluginsMenu.open();
157
+ };
158
+ /** Undo the last `n` [[Edit]]s committed */
159
+ this.undo = (n = 1) => {
160
+ if (!this.canUndo || n < 1) {
161
+ return;
162
+ }
163
+ this.xmlEditor.undo();
164
+ if (n > 1) {
165
+ this.undo(n - 1);
166
+ }
167
+ this.requestUpdate();
168
+ };
169
+ /** Redo the last `n` [[Edit]]s that have been undone */
170
+ this.redo = (n = 1) => {
171
+ if (!this.canRedo || n < 1) {
172
+ return;
173
+ }
174
+ this.xmlEditor.redo();
175
+ if (n > 1) {
176
+ this.redo(n - 1);
177
+ }
178
+ this.requestUpdate();
179
+ };
180
+ this.hotkeys = {
181
+ m: this.handleOpenPluginMenu,
182
+ z: this.undo,
183
+ y: this.redo,
184
+ Z: this.redo,
185
+ };
245
186
  // Catch all edits (from commits AND events) and trigger an update
246
187
  this.xmlEditor.subscribe(() => {
247
188
  this.docVersion += 1;
248
189
  });
249
190
  }
250
- updated(changedProps) {
251
- super.updated(changedProps);
252
- this.updateComplete.then(() => {
253
- // Ensure the active tab is set after tabs are rendered
254
- const oscdTabs = this.shadowRoot.querySelector('oscd-tabs');
255
- if (oscdTabs && oscdTabs.activeTabIndex !== this.editorIndex) {
256
- oscdTabs.activeTabIndex = this.editorIndex;
257
- }
258
- });
191
+ connectedCallback() {
192
+ super.connectedCallback();
193
+ document.addEventListener('keydown', this.handleKeyPress);
194
+ this.addEventListener('oscd-open', this.handleOpenDoc);
195
+ this.addEventListener('oscd-rename', this.handleRenameDoc);
196
+ this.addEventListener('oscd-close', this.handleCloseDoc);
197
+ this.addEventListener('oscd-edit-v2', this.handleEditV2);
198
+ this.addEventListener('oscd-undo', this.handleUndo);
199
+ this.addEventListener('oscd-redo', this.handleRedo);
200
+ }
201
+ disconnectedCallback() {
202
+ super.disconnectedCallback();
203
+ // Remove event listeners
204
+ document.removeEventListener('keydown', this.handleKeyPress);
205
+ this.removeEventListener('oscd-open', this.handleOpenDoc);
206
+ this.removeEventListener('oscd-rename', this.handleRenameDoc);
207
+ this.removeEventListener('oscd-edit-v2', this.handleEditV2);
208
+ this.removeEventListener('oscd-undo', this.handleUndo);
209
+ this.removeEventListener('oscd-redo', this.handleRedo);
210
+ this.removeEventListener('oscd-close', this.handleCloseDoc);
211
+ }
212
+ isEditable(docName) {
213
+ return !!this.editable.find(ext => docName.toLowerCase().endsWith(`.${ext}`));
214
+ }
215
+ onDocsChanged() {
216
+ this.docVersion += 1;
259
217
  }
260
218
  renderPlugin(tagName) {
261
219
  const tag = unsafeStatic(tagName);
@@ -269,310 +227,199 @@ let OpenSCD = class OpenSCD extends LitElement {
269
227
  .editor=${this.xmlEditor}>
270
228
  </${tag}>`;
271
229
  }
272
- render() {
273
- return html `<oscd-app-bar slot="appContent">
274
- ${renderActionItem(this.controls.menu, 'actionStart')}
275
- <div slot="title" id="title">
276
- ${this.editableDocs.length > 1
277
- ? html `<oscd-filled-icon-button
278
- id="fileMenuButton"
279
- @click=${() => this.fileMenuUI.show()}
280
- ><oscd-icon>arrow_drop_down</oscd-icon></oscd-filled-icon-button
281
- >`
282
- : nothing}
283
- ${this.docName}
284
- ${this.docName
285
- ? html `<oscd-filled-icon-button
286
- @click=${() => this.editFileUI.show()}
287
- ><oscd-icon>edit</oscd-icon></oscd-filled-icon-button
288
- >`
289
- : nothing}
290
- <oscd-menu
291
- fixed
292
- id="fileMenu"
293
- anchor="fileMenuButton"
294
- corner="BOTTOM_END"
295
- >
296
- ${this.editableDocs.map((name, index) => html `<oscd-menu-item
297
- @click=${() => {
298
- this.docName = name;
299
- }}
300
- ?disabled=${!this.isEditable(name)}
301
- ?selected=${this.docName === name}
302
- id=${index}
303
- >${name}</oscd-menu-item
304
- >`)}
305
- </oscd-menu>
230
+ onMenuPluginSelect(customEvent) {
231
+ const plugin = customEvent.detail.plugin;
232
+ if (plugin.tagName) {
233
+ this.shadowRoot.querySelector(plugin.tagName).run?.();
234
+ }
235
+ }
236
+ renderOffScreenPlugins() {
237
+ return html `
238
+ <section class="off-screen-plugin-container" aria-hidden="true">
239
+ <div class="menu-plugins">
240
+ ${this.plugins.menu
241
+ .filter(plugin => !plugin.requireDoc || !!this.docName)
242
+ .map(plugin => this.renderPlugin(plugin.tagName))}
306
243
  </div>
307
-
308
- <div slot="actionEnd">
309
- ${__classPrivateFieldGet(this, _OpenSCD_actions, "f").map(op => renderActionItem(op))}
244
+ <div class="background-plugins">
245
+ ${this.plugins.background
246
+ .filter(plugin => !plugin.requireDoc || !!this.docName)
247
+ .map(plugin => this.renderPlugin(plugin.tagName))}
310
248
  </div>
311
-
312
- <oscd-tabs
313
- .activeTabIndex=${this.editorIndex}
249
+ </section>
250
+ `;
251
+ }
252
+ renderDefaultLandingPage() {
253
+ return html `
254
+ <landing-page
255
+ heading=${this.landingPageHeading}
256
+ subHeading=${this.landingPageSubHeading}
257
+ .menuPlugins=${this.plugins.menu.filter(plugin => !plugin.requireDoc || !!this.docName)}
258
+ .locale=${this.locale}
259
+ @menu-plugin-select=${(event) => this.onMenuPluginSelect(event)}
260
+ >
261
+ </landing-page>
262
+ `;
263
+ }
264
+ render() {
265
+ const hasCustomLandingPage = !!this._landingPageNodes?.length;
266
+ if (this.editableDocs.length === 0) {
267
+ return html ` <slot
268
+ name="landing-page"
269
+ @slotchange=${() => this.requestUpdate()}
270
+ ></slot>
271
+ ${!hasCustomLandingPage ? this.renderDefaultLandingPage() : nothing}
272
+ ${this.renderOffScreenPlugins()}`;
273
+ }
274
+ return html ` <oscd-app-bar>
275
+ <plugins-menu
276
+ slot="alignStart"
277
+ appTitle=${this.appTitle}
278
+ appIcon=${this.appIcon}
279
+ .editableDocs=${this.editableDocs}
280
+ .menuPlugins=${this.plugins.menu}
281
+ .locale=${this.locale}
282
+ @menu-plugin-select=${(event) => this.onMenuPluginSelect(event)}
283
+ ></plugins-menu>
284
+
285
+ <files-menu
286
+ slot="alignMiddle"
287
+ .selectedDocName=${this.docName}
288
+ .editableDocs=${this.editableDocs}
289
+ .locale=${this.locale}
314
290
  @change=${(event) => {
315
- const tabs = event.currentTarget;
316
- this.editorIndex = tabs.activeTabIndex;
317
- }}
318
- >
319
- ${this.editors.map(editor => editor.isDisabled()
320
- ? nothing
321
- : html `<oscd-secondary-tab>
322
- <oscd-icon slot="icon">${editor.icon}</oscd-icon>
323
- ${editor.getName()}
324
- </oscd-secondary-tab>`)}
325
- </oscd-tabs>
326
- </oscd-app-bar>
327
-
328
- <oscd-navigation-drawer hasheader type="modal" id="menu">
329
- <oscd-navigation-drawer-header>
330
- <span slot="headline">${msg('Menu')}</span>
331
- ${this.docName
332
- ? html `<span slot="supporting-text">${this.docName}</span>`
333
- : ''}
334
- </oscd-navigation-drawer-header>
335
- <oscd-list>
336
- <oscd-divider></oscd-divider>
337
- ${this.menu.map(renderMenuItem)}
338
- </oscd-list>
339
- </oscd-navigation-drawer>
340
-
341
- ${this.editor ? this.renderPlugin(this.editor) : nothing}
342
-
343
- <oscd-dialog
344
- id="editFile"
345
- @closed=${(event) => {
346
- const dialog = event.target;
347
- if (dialog.returnValue === 'remove') {
348
- delete this.docs[this.docName];
349
- this.docName = this.editableDocs[0] || '';
350
- }
291
+ const name = event.detail.name;
292
+ this.docName = name;
351
293
  }}
352
- >
353
- <div slot="headline">
354
- <oscd-icon>description</oscd-icon>${this.docName}
355
- </div>
356
- <form slot="content" id="edit-file-form" method="dialog">
357
- <oscd-filled-text-field
358
- id="fileName"
359
- label="${msg('Filename')}"
360
- value="${this.docName.replace(/\.[^.]+$/, '')}"
361
- @input=${(event) => {
362
- const input = event.target;
363
- const { value } = input;
364
- const name = `${value}.${this.fileExtensionUI.value}`;
365
- if (name in this.docs && name !== this.docName) {
366
- input.setCustomValidity('File already exists!');
367
- }
368
- else {
369
- input.setCustomValidity('');
370
- }
371
- input.reportValidity();
294
+ ></files-menu>
295
+
296
+ <div slot="alignEnd">
297
+ <oscd-filled-icon-button
298
+ aria-label="${msg('Undo')}"
299
+ ?disabled=${!this.canUndo}
300
+ @click=${async () => {
301
+ this.dispatchEvent(new CustomEvent('oscd-undo', {
302
+ bubbles: true,
303
+ composed: true,
304
+ }));
372
305
  }}
373
- ></oscd-filled-text-field>
374
- <oscd-filled-select
375
- label="${msg('Extension')}"
376
- fixedMenuPosition
377
- id="fileExtension"
378
- @selected=${() => this.fileNameUI.reportValidity()}
379
- >
380
- ${this.editable.map(ext => html `<oscd-select-option
381
- ?selected=${this.docName.endsWith(`.${ext}`)}
382
- value="${ext}"
383
- >${ext}</oscd-select-option
384
- >`)}
385
- </oscd-filled-select>
386
- </form>
387
- <div slot="actions">
388
- <oscd-text-button
389
- form="edit-file-form"
390
- class="edit-dialog-remove-button"
391
- value="remove"
306
+ ><oscd-icon>undo</oscd-icon></oscd-filled-icon-button
392
307
  >
393
- <oscd-icon slot="icon">delete</oscd-icon>
394
- ${msg('Close file')}
395
- </oscd-text-button>
396
- <oscd-text-button form="edit-file-form" value="close">
397
- ${msg('Cancel')}
398
- </oscd-text-button>
399
- <oscd-text-button
400
- @click=${() => {
401
- const valid = this.fileNameUI.checkValidity();
402
- if (!valid) {
403
- this.fileNameUI.reportValidity();
404
- return;
405
- }
406
- const newDocName = `${this.fileNameUI.value}.${this.fileExtensionUI.value}`;
407
- const doc = this.docs[this.docName];
408
- delete this.docs[this.docName];
409
- this.docs = {
410
- ...this.docs,
411
- [newDocName]: doc,
412
- };
413
- this.docName = newDocName;
414
- this.editFileUI.close();
308
+ <oscd-filled-icon-button
309
+ aria-label="${msg('Redo')}"
310
+ ?disabled=${!this.canRedo}
311
+ @click=${async () => {
312
+ this.dispatchEvent(new CustomEvent('oscd-redo', {
313
+ bubbles: true,
314
+ composed: true,
315
+ }));
415
316
  }}
416
- trailing-icon
317
+ ><oscd-icon>redo</oscd-icon></oscd-filled-icon-button
417
318
  >
418
- <oscd-icon slot="icon">edit</oscd-icon>
419
- ${msg('Rename')}
420
- </oscd-text-button>
421
319
  </div>
422
- </oscd-dialog>
320
+ </oscd-app-bar>
423
321
 
424
- <aside>
425
- <div class="menu-plugins">
426
- ${this.plugins.menu.map(plugin => this.renderPlugin(plugin.tagName))}
427
- </div>
428
- <div class="background-plugins">
429
- ${this.plugins.background
430
- .filter(plugin => !plugin.requireDoc || !!this.docName)
431
- .map(plugin => this.renderPlugin(plugin.tagName))}
432
- </div>
433
- </aside> `;
434
- }
435
- firstUpdated() {
436
- const background = getComputedStyle(this.menuUI).getPropertyValue('--oscd-base2');
437
- document.body.style.background = background;
322
+ <main>
323
+ <section class="editors-side-panel-section">
324
+ <editor-plugins-panel
325
+ .editors=${this.plugins.editor}
326
+ .editorIndex=${this.editorIndex}
327
+ .locale=${this.locale}
328
+ @editor-select=${(e) => {
329
+ this.editorIndex = e.detail.index;
330
+ }}
331
+ ></editor-plugins-panel>
332
+ </section>
333
+
334
+ <section class="editor-container">
335
+ ${this.editor ? this.renderPlugin(this.editor) : nothing}
336
+ </section>
337
+
338
+ ${this.renderOffScreenPlugins()}
339
+ </main>`;
438
340
  }
439
341
  };
440
- _OpenSCD_plugins = new WeakMap();
441
- _OpenSCD_actions = new WeakMap();
442
- OpenSCD.styles = css `
443
- .fileext {
444
- opacity: 0.81;
342
+ OscdShell.scopedElements = {
343
+ 'oscd-app-bar': OscdAppBar,
344
+ 'oscd-filled-icon-button': OscdFilledIconButton,
345
+ 'oscd-icon': OscdIcon,
346
+ 'files-menu': FilesMenu,
347
+ 'plugins-menu': PluginsMenu,
348
+ 'editor-plugins-panel': EditorPluginsPanel,
349
+ 'landing-page': LandingPage,
350
+ };
351
+ OscdShell.styles = css `
352
+ ${theming}
353
+
354
+ * {
355
+ --app-bar-height: 54px;
356
+ --side-panel-width: 280px;
445
357
  }
446
358
 
447
- .filename {
448
- caret-color: var(--oscd-secondary);
359
+ :host {
360
+ height: 100%;
361
+ display: grid;
362
+ grid-template-rows: min-content 1fr;
363
+ grid-template-columns: 1fr;
364
+ grid-template-areas:
365
+ 'header'
366
+ 'main';
449
367
  }
450
368
 
451
- .filename:focus {
452
- outline: none;
369
+ oscd-app-bar {
370
+ grid-area: header;
371
+ box-shadow: var(--md-sys-elevation-level-2);
372
+ z-index: 10;
453
373
  }
454
374
 
455
- aside {
456
- position: absolute;
457
- top: 0;
458
- left: 0;
459
- width: 0;
460
- height: 0;
375
+ main {
376
+ grid-area: main;
377
+ display: grid;
378
+ grid-template-columns: var(--side-panel-width) 1fr;
379
+ grid-template-areas: 'sidebar editor';
461
380
  overflow: hidden;
462
- margin: 0;
463
- padding: 0;
464
381
  }
465
382
 
466
- abbr {
467
- text-decoration: none;
383
+ /* Side panel collapsed state */
384
+ main.sidebar-collapsed {
385
+ grid-template-columns: 0 1fr;
468
386
  }
469
387
 
470
- @media (prefers-color-scheme: light) {
471
- * {
472
- --oscd-primary: var(--oscd-theme-primary, #2aa198);
473
- --oscd-secondary: var(--oscd-theme-secondary, #6c71c4);
474
- --oscd-base03: var(--oscd-theme-base03, #002b36);
475
- --oscd-base02: var(--oscd-theme-base02, #073642);
476
- --oscd-base01: var(--oscd-theme-base01, #586e75);
477
- --oscd-base00: var(--oscd-theme-base00, #657b83);
478
- --oscd-base0: var(--oscd-theme-base0, #839496);
479
- --oscd-base1: var(--oscd-theme-base1, #93a1a1);
480
- --oscd-base2: var(--oscd-theme-base2, #eee8d5);
481
- --oscd-base3: var(--oscd-theme-base3, #fdf6e3);
482
-
483
- --mdc-theme-text-disabled-on-light: rgba(255, 255, 255, 0.38);
484
- }
388
+ section.editors-side-panel-section {
389
+ grid-area: sidebar;
390
+ overflow-y: auto;
391
+ overflow-x: hidden;
392
+ transition: transform 0.3s ease-in-out;
485
393
  }
486
394
 
487
- @media (prefers-color-scheme: dark) {
488
- * {
489
- --oscd-primary: var(--oscd-theme-secondary, #6c71c4);
490
- --oscd-secondary: var(--oscd-theme-primary, #2aa198);
491
- --oscd-base03: var(--oscd-theme-base3, #fdf6e3);
492
- --oscd-base02: var(--oscd-theme-base2, #eee8d5);
493
- --oscd-base01: var(--oscd-theme-base1, #93a1a1);
494
- --oscd-base00: var(--oscd-theme-base0, #839496);
495
- --oscd-base0: var(--oscd-theme-base00, #657b83);
496
- --oscd-base1: var(--oscd-theme-base01, #586e75);
497
- --oscd-base2: var(--oscd-theme-base02, #073642);
498
- --oscd-base3: var(--oscd-theme-base03, #002b36);
499
- }
395
+ /* Hide side panel when collapsed */
396
+ main.sidebar-collapsed section.editors-side-panel-section {
397
+ transform: translateX(-100%);
500
398
  }
501
399
 
502
- * {
503
- --oscd-error: var(--oscd-theme-error, #dc322f);
504
-
505
- --oscd-text-font: var(--oscd-theme-text-font, 'Roboto');
506
- --oscd-icon-font: var(--oscd-theme-icon-font, 'Material Icons');
507
- --oscd-text-font-mono: var(--oscd-theme-text-font-mono, 'Roboto Mono');
508
-
509
- --oscd-warning: var(--oscd-theme-warning, #b58900);
510
- --md-sys-color-primary: var(--oscd-primary);
511
- --md-sys-color-on-primary: var(--oscd-base3);
512
-
513
- --md-sys-color-secondary: var(--oscd-secondary);
514
- --md-sys-color-on-secondary: var(--oscd-base3);
515
- --md-sys-color-secondary-container: var(--oscd-base2);
516
-
517
- --md-sys-color-surface: var(--oscd-base3);
518
- --md-sys-color-on-surface: var(--oscd-base00);
519
- --md-sys-color-surface-variant: var(--oscd-base3);
520
- --md-sys-color-on-surface-variant: var(--oscd-base00);
521
- --md-sys-color-surface-bright: var(--oscd-base2);
522
- --md-sys-color-surface-container: var(--oscd-base3);
523
- --md-sys-color-surface-container-high: var(--oscd-base3);
524
- --md-sys-color-surface-container-highest: var(--oscd-base3);
525
-
526
- --md-sys-color-outline-variant: var(--oscd-primary);
527
- --md-sys-color-scrim: #000000;
528
- --md-sys-color-error: var(--oscd-error);
529
- --md-sys-color-on-error: var(--oscd-base3);
530
-
531
- /* --md-menu-item-selected-label-text-color: var(--oscd-base01); */
532
- --md-icon-button-disabled-icon-color: var(--oscd-base3);
533
-
534
- /* MDC Theme Colors
535
- * Needed for supporting any pluggins still using the depricated MWC Components
536
- */
537
- --mdc-theme-primary: var(--oscd-primary);
538
- --mdc-theme-secondary: var(--oscd-secondary);
539
- --mdc-theme-background: var(--oscd-base3);
540
- --mdc-theme-surface: var(--oscd-base3);
541
- --mdc-theme-on-primary: var(--oscd-base2);
542
- --mdc-theme-on-secondary: var(--oscd-base02);
543
- --mdc-theme-on-background: var(--oscd-base00);
544
- --mdc-theme-on-surface: var(--oscd-base00);
545
- --mdc-theme-text-primary-on-background: var(--oscd-base01);
546
- --mdc-theme-text-secondary-on-background: var(--oscd-base00);
547
- --mdc-theme-text-icon-on-background: var(--oscd-base00);
548
- --mdc-theme-error: var(--oscd-error);
549
-
550
- --mdc-button-disabled-ink-color: var(--oscd-base1);
551
-
552
- --mdc-drawer-heading-ink-color: var(--oscd-base00);
553
-
554
- --mdc-dialog-heading-ink-color: var(--oscd-base00);
555
-
556
- --mdc-text-field-fill-color: var(--oscd-base2);
557
- --mdc-text-field-ink-color: var(--oscd-base02);
558
- --mdc-text-field-label-ink-color: var(--oscd-base01);
559
- --mdc-text-field-idle-line-color: var(--oscd-base00);
560
- --mdc-text-field-hover-line-color: var(--oscd-base02);
561
-
562
- --mdc-select-fill-color: var(--oscd-base2);
563
- --mdc-select-ink-color: var(--oscd-base02);
564
- --mdc-select-label-ink-color: var(--oscd-base01);
565
- --mdc-select-idle-line-color: var(--oscd-base00);
566
- --mdc-select-hover-line-color: var(--oscd-base02);
567
- --mdc-select-dropdown-icon-color: var(--oscd-base01);
400
+ section.editor-container {
401
+ grid-area: editor;
402
+ background-color: var(--slate-100);
403
+ padding: 8px;
404
+ overflow: auto;
405
+ position: relative;
406
+ }
568
407
 
569
- --mdc-typography-font-family: var(--oscd-text-font);
570
- --mdc-icon-font: var(--oscd-icon-font);
408
+ .off-screen-plugin-container {
409
+ position: absolute;
410
+ top: 0;
411
+ left: 0;
412
+ width: 0;
413
+ height: 0;
414
+ overflow: hidden;
415
+ margin: 0;
416
+ padding: 0;
571
417
  }
572
418
 
573
419
  oscd-navigation-drawer-header {
574
420
  --md-list-item-supporting-text-color: var(--md-sys-color-on-surface);
575
421
  }
422
+
576
423
  oscd-app-bar * {
577
424
  --md-filled-icon-button-disabled-container-opacity: 0;
578
425
  --md-filled-icon-button-disabled-icon-color: var(
@@ -580,107 +427,89 @@ OpenSCD.styles = css `
580
427
  );
581
428
  }
582
429
 
583
- oscd-tabs {
584
- display: flex;
585
- flex-grow: 1;
586
- --md-secondary-tab-container-color: var(--oscd-primary);
587
- }
588
- oscd-secondary-tab {
589
- --md-sys-color-on-surface: var(--md-sys-color-on-primary);
590
- --md-sys-color-on-surface-variant: var(--md-sys-color-on-primary);
591
- --md-secondary-tab-active-indicator-color: var(--oscd-base2);
592
- }
593
-
594
430
  .edit-dialog-remove-button {
595
431
  --md-text-button-icon-color: var(--oscd-error);
596
432
  --md-text-button-label-text-color: var(--oscd-error);
597
-
598
433
  --md-text-button-focus-label-text-color: var(--oscd-error);
599
434
  --md-text-button-focus-icon-color: var(--oscd-error);
600
-
601
435
  --md-text-button-hover-label-text-color: var(--oscd-error);
602
436
  --md-text-button-hover-state-layer-color: var(--oscd-error);
603
437
  --md-text-button-hover-icon-color: var(--oscd-error);
604
-
605
438
  --md-text-button-pressed-label-text-color: var(--oscd-error);
606
439
  --md-text-button-pressed-state-layer-color: var(--oscd-error);
607
440
  --md-text-button-pressed-icon-color: var(--oscd-error);
608
441
  }
609
-
610
442
  #title {
611
443
  position: relative;
612
444
  }
613
445
  `;
614
446
  __decorate([
615
- property({ type: Array, reflect: true })
616
- ], OpenSCD.prototype, "editable", void 0);
447
+ property({ type: String })
448
+ ], OscdShell.prototype, "appIcon", void 0);
617
449
  __decorate([
618
- state()
619
- ], OpenSCD.prototype, "editableDocs", null);
450
+ property({ type: String })
451
+ ], OscdShell.prototype, "appTitle", void 0);
620
452
  __decorate([
621
- state()
622
- ], OpenSCD.prototype, "docs", null);
453
+ property({ type: String })
454
+ ], OscdShell.prototype, "landingPageHeading", void 0);
455
+ __decorate([
456
+ property({ type: String })
457
+ ], OscdShell.prototype, "landingPageSubHeading", void 0);
458
+ __decorate([
459
+ property({ type: Array, reflect: true })
460
+ ], OscdShell.prototype, "editable", void 0);
623
461
  __decorate([
624
462
  property({ type: String, reflect: true })
625
- ], OpenSCD.prototype, "docName", void 0);
463
+ ], OscdShell.prototype, "locale", null);
626
464
  __decorate([
627
- state()
628
- /** The `XMLDocument` currently being edited */
629
- ], OpenSCD.prototype, "doc", null);
465
+ property({ type: Object })
466
+ ], OscdShell.prototype, "plugins", null);
630
467
  __decorate([
631
468
  state()
632
- ], OpenSCD.prototype, "xmlEditor", void 0);
469
+ ], OscdShell.prototype, "canRedo", null);
633
470
  __decorate([
634
471
  state()
635
- ], OpenSCD.prototype, "docVersion", void 0);
472
+ ], OscdShell.prototype, "canUndo", null);
636
473
  __decorate([
637
474
  state()
638
- ], OpenSCD.prototype, "last", null);
475
+ ], OscdShell.prototype, "editor", null);
639
476
  __decorate([
640
477
  state()
641
- ], OpenSCD.prototype, "canUndo", null);
478
+ ], OscdShell.prototype, "editorIndex", void 0);
642
479
  __decorate([
643
480
  state()
644
- ], OpenSCD.prototype, "canRedo", null);
645
- __decorate([
646
- property({ type: Object })
647
- ], OpenSCD.prototype, "plugins", null);
648
- __decorate([
649
- query('#editFile')
650
- ], OpenSCD.prototype, "editFileUI", void 0);
651
- __decorate([
652
- query('#menu')
653
- ], OpenSCD.prototype, "menuUI", void 0);
654
- __decorate([
655
- query('#fileName')
656
- ], OpenSCD.prototype, "fileNameUI", void 0);
657
- __decorate([
658
- query('#fileExtension')
659
- ], OpenSCD.prototype, "fileExtensionUI", void 0);
660
- __decorate([
661
- query('#fileMenu')
662
- ], OpenSCD.prototype, "fileMenuUI", void 0);
663
- __decorate([
664
- query('#fileMenuButton')
665
- ], OpenSCD.prototype, "fileMenuButtonUI", void 0);
481
+ /** The `XMLDocument` currently being edited */
482
+ ], OscdShell.prototype, "doc", null);
666
483
  __decorate([
667
484
  property({ type: String, reflect: true })
668
- ], OpenSCD.prototype, "locale", null);
485
+ ], OscdShell.prototype, "docName", void 0);
486
+ __decorate([
487
+ state()
488
+ ], OscdShell.prototype, "docs", null);
669
489
  __decorate([
670
490
  state()
671
- ], OpenSCD.prototype, "editorIndex", void 0);
491
+ ], OscdShell.prototype, "docVersion", void 0);
672
492
  __decorate([
673
493
  state()
674
- ], OpenSCD.prototype, "editor", null);
494
+ ], OscdShell.prototype, "editableDocs", null);
675
495
  __decorate([
676
496
  state()
677
- ], OpenSCD.prototype, "menu", null);
497
+ ], OscdShell.prototype, "last", null);
678
498
  __decorate([
679
499
  state()
680
- ], OpenSCD.prototype, "editors", null);
681
- OpenSCD = __decorate([
682
- customElement('oscd-shell'),
683
- localized()
684
- ], OpenSCD);
685
- export { OpenSCD };
500
+ ], OscdShell.prototype, "xmlEditor", void 0);
501
+ __decorate([
502
+ query('plugins-menu')
503
+ ], OscdShell.prototype, "pluginsMenu", void 0);
504
+ __decorate([
505
+ query('editor-plugins-panel')
506
+ ], OscdShell.prototype, "editorPluginsPanel", void 0);
507
+ __decorate([
508
+ queryAssignedNodes({ slot: 'landing-page' })
509
+ ], OscdShell.prototype, "_landingPageNodes", void 0);
510
+ OscdShell = __decorate([
511
+ localized(),
512
+ customElement('oscd-shell')
513
+ ], OscdShell);
514
+ export { OscdShell };
686
515
  //# sourceMappingURL=oscd-shell.js.map