@compas-oscd/scl-template-update 0.0.1

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.
@@ -0,0 +1,196 @@
1
+ import { __decorate } from "tslib";
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ import { LitElement, html, css } from 'lit';
4
+ import { property, state } from 'lit/decorators.js';
5
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
6
+ import { OscdFilledButton } from '@omicronenergy/oscd-ui/button/OscdFilledButton.js';
7
+ import { OscdOutlinedTextField } from '@omicronenergy/oscd-ui/textfield/OscdOutlinedTextField.js';
8
+ import { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';
9
+ import { OscdListItem } from '@omicronenergy/oscd-ui/list/OscdListItem.js';
10
+ export class LNodeTypeSidebar extends ScopedElementsMixin(LitElement) {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.lNodeTypes = [];
14
+ this.filter = '';
15
+ this.sortedLNodeTypes = [];
16
+ }
17
+ static { this.scopedElements = {
18
+ 'oscd-filled-button': OscdFilledButton,
19
+ 'oscd-outlined-textfield': OscdOutlinedTextField,
20
+ 'oscd-list': OscdList,
21
+ 'oscd-list-item': OscdListItem,
22
+ }; }
23
+ static sortLNodeTypes(nodes) {
24
+ return [...nodes].sort((a, b) => {
25
+ const aId = a.getAttribute('id')?.toLowerCase() || '';
26
+ const bId = b.getAttribute('id')?.toLowerCase() || '';
27
+ return aId.localeCompare(bId);
28
+ });
29
+ }
30
+ willUpdate(changedProperties) {
31
+ super.willUpdate(changedProperties);
32
+ if (changedProperties.has('lNodeTypes')) {
33
+ this.sortedLNodeTypes = LNodeTypeSidebar.sortLNodeTypes(this.lNodeTypes);
34
+ }
35
+ }
36
+ handleInput(e) {
37
+ const { value } = e.target;
38
+ clearTimeout(this.debounceTimer);
39
+ this.debounceTimer = window.setTimeout(() => {
40
+ this.filter = value;
41
+ }, 300);
42
+ }
43
+ clearFilter() {
44
+ this.filter = '';
45
+ }
46
+ handleClick(id) {
47
+ this.dispatchEvent(new CustomEvent('lnodetype-select', {
48
+ detail: { id },
49
+ bubbles: true,
50
+ composed: true,
51
+ }));
52
+ }
53
+ get filteredLNodeTypes() {
54
+ const sortedLNTypes = this.sortedLNodeTypes;
55
+ if (!this.filter.trim())
56
+ return sortedLNTypes;
57
+ // If the filter includes words separated by &, treat as a single AND group (e.g. 'a & b', 'a&b', 'a &b', 'a& b').
58
+ // Otherwise, split on comma or space (unless adjacent to &), so 'a b' and 'a,b' are separate OR groups.
59
+ let groups = [];
60
+ const filter = this.filter.toLowerCase().trim();
61
+ if (/^\s*\w+\s*&\s*\w+\s*$/.test(filter)) {
62
+ groups = [
63
+ filter
64
+ .replace(/\s*&\s*/g, '&')
65
+ .split('&')
66
+ .filter(Boolean),
67
+ ];
68
+ }
69
+ else {
70
+ groups = filter
71
+ .split(/(?<!&)[ ,]+(?!&)/)
72
+ .map(group => group
73
+ .replace(/\s*&\s*/g, '&')
74
+ .split('&')
75
+ .filter(Boolean))
76
+ .filter(group => group.length > 0);
77
+ }
78
+ if (groups.length === 0)
79
+ return sortedLNTypes;
80
+ return sortedLNTypes.filter(ln => {
81
+ const id = ln.getAttribute('id')?.toLowerCase() || '';
82
+ const desc = ln.getAttribute('desc')?.toLowerCase() || '';
83
+ return groups.some(group => group.every(term => id.includes(term) || desc.includes(term)));
84
+ });
85
+ }
86
+ render() {
87
+ return html `<div class="sidebar">
88
+ <div class="actions">
89
+ <oscd-filled-button class="clear-all" @click=${this.clearFilter}>
90
+ Clear filter
91
+ </oscd-filled-button>
92
+ </div>
93
+ <div class="search-filter">
94
+ <div class="search-container">
95
+ <oscd-outlined-textfield
96
+ label="Filter Logical Node Types"
97
+ type="text"
98
+ placeholder="e.g.: TCTR, TVTR&amp;protection"
99
+ .value=${this.filter}
100
+ @input=${this.handleInput}
101
+ aria-label="Filter Logical Node Types"
102
+ supporting-text="Search by ID or description. Use commas/spaces for OR, use &amp; for AND."
103
+ ></oscd-outlined-textfield>
104
+ </div>
105
+ </div>
106
+ <oscd-list>
107
+ ${this.filteredLNodeTypes.map(ln => {
108
+ const id = ln.getAttribute('id') || '';
109
+ const desc = ln.getAttribute('desc') || '';
110
+ const isSelected = this.selectedId === id;
111
+ return html `
112
+ <oscd-list-item
113
+ type="button"
114
+ ?selected=${isSelected}
115
+ @click=${() => this.handleClick(id)}
116
+ >
117
+ <span slot="headline" title=${id}>${id}</span>
118
+ <span slot="supporting-text">${desc}</span>
119
+ </oscd-list-item>
120
+ `;
121
+ })}
122
+ </oscd-list>
123
+ </div>`;
124
+ }
125
+ static { this.styles = css `
126
+ .sidebar {
127
+ display: flex;
128
+ flex-direction: column;
129
+ flex: 1 1 auto;
130
+ min-height: 0;
131
+ padding: 1rem;
132
+ overflow: hidden;
133
+ background-color: var(--oscd-base3);
134
+ }
135
+ oscd-list {
136
+ min-height: 0;
137
+ max-height: calc(100vh - var(--header-height) - 1rem - 134px);
138
+ overflow-y: auto;
139
+ scrollbar-width: thin;
140
+ padding: 0;
141
+ }
142
+ oscd-list::-webkit-scrollbar {
143
+ width: 8px;
144
+ }
145
+ oscd-list::-webkit-scrollbar-thumb {
146
+ border-radius: 4px;
147
+ }
148
+ oscd-list::-webkit-scrollbar-track {
149
+ background: transparent;
150
+ }
151
+ oscd-list-item {
152
+ box-sizing: border-box;
153
+ }
154
+ oscd-list-item[selected] {
155
+ background: var(--md-sys-color-primary);
156
+ }
157
+ oscd-list-item[selected] span[slot='headline'],
158
+ oscd-list-item[selected] span[slot='supporting-text'] {
159
+ color: var(--md-sys-color-on-primary, #ffffff);
160
+ }
161
+ oscd-outlined-textfield {
162
+ width: 100%;
163
+ }
164
+ .actions {
165
+ display: flex;
166
+ flex-direction: row-reverse;
167
+ margin-bottom: 0.5rem;
168
+ }
169
+ .search-filter {
170
+ display: flex;
171
+ align-items: center;
172
+ gap: 1rem;
173
+ margin-bottom: 1rem;
174
+ }
175
+ .search-container {
176
+ flex: 1;
177
+ position: relative;
178
+ }
179
+ .clear-all {
180
+ cursor: pointer;
181
+ color: var(--md-sys-color-primary, #0078d4);
182
+ text-decoration: underline;
183
+ font-size: 0.95em;
184
+ }
185
+ `; }
186
+ }
187
+ __decorate([
188
+ property({ type: Array })
189
+ ], LNodeTypeSidebar.prototype, "lNodeTypes", void 0);
190
+ __decorate([
191
+ property({ type: String })
192
+ ], LNodeTypeSidebar.prototype, "selectedId", void 0);
193
+ __decorate([
194
+ state()
195
+ ], LNodeTypeSidebar.prototype, "filter", void 0);
196
+ //# sourceMappingURL=lnodetype-sidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lnodetype-sidebar.js","sourceRoot":"","sources":["../../components/lnodetype-sidebar.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { LitElement, html, css, PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';\n\nimport { OscdFilledButton } from '@omicronenergy/oscd-ui/button/OscdFilledButton.js';\nimport { OscdOutlinedTextField } from '@omicronenergy/oscd-ui/textfield/OscdOutlinedTextField.js';\nimport { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';\nimport { OscdListItem } from '@omicronenergy/oscd-ui/list/OscdListItem.js';\n\nexport class LNodeTypeSidebar extends ScopedElementsMixin(LitElement) {\n static scopedElements = {\n 'oscd-filled-button': OscdFilledButton,\n 'oscd-outlined-textfield': OscdOutlinedTextField,\n 'oscd-list': OscdList,\n 'oscd-list-item': OscdListItem,\n };\n\n @property({ type: Array })\n lNodeTypes: Element[] = [];\n\n @property({ type: String })\n selectedId?: string;\n\n @state()\n filter: string = '';\n\n private sortedLNodeTypes: Element[] = [];\n\n private debounceTimer?: number;\n\n private static sortLNodeTypes(nodes: Element[]): Element[] {\n return [...nodes].sort((a, b) => {\n const aId = a.getAttribute('id')?.toLowerCase() || '';\n const bId = b.getAttribute('id')?.toLowerCase() || '';\n return aId.localeCompare(bId);\n });\n }\n\n protected willUpdate(changedProperties: PropertyValues<this>) {\n super.willUpdate(changedProperties);\n\n if (changedProperties.has('lNodeTypes')) {\n this.sortedLNodeTypes = LNodeTypeSidebar.sortLNodeTypes(this.lNodeTypes);\n }\n }\n\n private handleInput(e: Event) {\n const { value } = e.target as HTMLInputElement;\n clearTimeout(this.debounceTimer);\n this.debounceTimer = window.setTimeout(() => {\n this.filter = value;\n }, 300);\n }\n\n private clearFilter() {\n this.filter = '';\n }\n\n private handleClick(id: string) {\n this.dispatchEvent(\n new CustomEvent('lnodetype-select', {\n detail: { id },\n bubbles: true,\n composed: true,\n })\n );\n }\n\n get filteredLNodeTypes(): Element[] {\n const sortedLNTypes = this.sortedLNodeTypes;\n\n if (!this.filter.trim()) return sortedLNTypes;\n // If the filter includes words separated by &, treat as a single AND group (e.g. 'a & b', 'a&b', 'a &b', 'a& b').\n // Otherwise, split on comma or space (unless adjacent to &), so 'a b' and 'a,b' are separate OR groups.\n let groups: string[][] = [];\n const filter = this.filter.toLowerCase().trim();\n if (/^\\s*\\w+\\s*&\\s*\\w+\\s*$/.test(filter)) {\n groups = [\n filter\n .replace(/\\s*&\\s*/g, '&')\n .split('&')\n .filter(Boolean),\n ];\n } else {\n groups = filter\n .split(/(?<!&)[ ,]+(?!&)/)\n .map(group =>\n group\n .replace(/\\s*&\\s*/g, '&')\n .split('&')\n .filter(Boolean)\n )\n .filter(group => group.length > 0);\n }\n\n if (groups.length === 0) return sortedLNTypes;\n\n return sortedLNTypes.filter(ln => {\n const id = ln.getAttribute('id')?.toLowerCase() || '';\n const desc = ln.getAttribute('desc')?.toLowerCase() || '';\n return groups.some(group =>\n group.every(term => id.includes(term) || desc.includes(term))\n );\n });\n }\n\n render() {\n return html`<div class=\"sidebar\">\n <div class=\"actions\">\n <oscd-filled-button class=\"clear-all\" @click=${this.clearFilter}>\n Clear filter\n </oscd-filled-button>\n </div>\n <div class=\"search-filter\">\n <div class=\"search-container\">\n <oscd-outlined-textfield\n label=\"Filter Logical Node Types\"\n type=\"text\"\n placeholder=\"e.g.: TCTR, TVTR&amp;protection\"\n .value=${this.filter}\n @input=${this.handleInput}\n aria-label=\"Filter Logical Node Types\"\n supporting-text=\"Search by ID or description. Use commas/spaces for OR, use &amp; for AND.\"\n ></oscd-outlined-textfield>\n </div>\n </div>\n <oscd-list>\n ${this.filteredLNodeTypes.map(ln => {\n const id = ln.getAttribute('id') || '';\n const desc = ln.getAttribute('desc') || '';\n const isSelected = this.selectedId === id;\n return html`\n <oscd-list-item\n type=\"button\"\n ?selected=${isSelected}\n @click=${() => this.handleClick(id)}\n >\n <span slot=\"headline\" title=${id}>${id}</span>\n <span slot=\"supporting-text\">${desc}</span>\n </oscd-list-item>\n `;\n })}\n </oscd-list>\n </div>`;\n }\n\n static styles = css`\n .sidebar {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n padding: 1rem;\n overflow: hidden;\n background-color: var(--oscd-base3);\n }\n oscd-list {\n min-height: 0;\n max-height: calc(100vh - var(--header-height) - 1rem - 134px);\n overflow-y: auto;\n scrollbar-width: thin;\n padding: 0;\n }\n oscd-list::-webkit-scrollbar {\n width: 8px;\n }\n oscd-list::-webkit-scrollbar-thumb {\n border-radius: 4px;\n }\n oscd-list::-webkit-scrollbar-track {\n background: transparent;\n }\n oscd-list-item {\n box-sizing: border-box;\n }\n oscd-list-item[selected] {\n background: var(--md-sys-color-primary);\n }\n oscd-list-item[selected] span[slot='headline'],\n oscd-list-item[selected] span[slot='supporting-text'] {\n color: var(--md-sys-color-on-primary, #ffffff);\n }\n oscd-outlined-textfield {\n width: 100%;\n }\n .actions {\n display: flex;\n flex-direction: row-reverse;\n margin-bottom: 0.5rem;\n }\n .search-filter {\n display: flex;\n align-items: center;\n gap: 1rem;\n margin-bottom: 1rem;\n }\n .search-container {\n flex: 1;\n position: relative;\n }\n .clear-all {\n cursor: pointer;\n color: var(--md-sys-color-primary, #0078d4);\n text-decoration: underline;\n font-size: 0.95em;\n }\n `;\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import { LitElement } from 'lit';
2
+ import { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';
3
+ import { OscdTextButton } from '@omicronenergy/oscd-ui/button/OscdTextButton.js';
4
+ import { OscdRadio } from '@omicronenergy/oscd-ui/radio/OscdRadio.js';
5
+ export declare enum UpdateSetting {
6
+ Swap = "swap",
7
+ Update = "update"
8
+ }
9
+ declare const SettingsDialog_base: typeof LitElement & import("@open-wc/scoped-elements/lit-element.js").ScopedElementsHostConstructor;
10
+ export declare class SettingsDialog extends SettingsDialog_base {
11
+ static scopedElements: {
12
+ 'oscd-dialog': typeof OscdDialog;
13
+ 'oscd-text-button': typeof OscdTextButton;
14
+ 'oscd-radio': typeof OscdRadio;
15
+ };
16
+ dialog: OscdDialog;
17
+ private updateSetting;
18
+ connectedCallback(): void;
19
+ private loadSettings;
20
+ private saveSettings;
21
+ get open(): boolean;
22
+ show(): void;
23
+ close(): void;
24
+ private handleRadioChange;
25
+ private handleConfirm;
26
+ private handleCancel;
27
+ render(): import("lit").TemplateResult<1>;
28
+ static styles: import("lit").CSSResult;
29
+ }
30
+ export {};
@@ -0,0 +1,144 @@
1
+ import { __decorate } from "tslib";
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
4
+ import { LitElement, html, css } from 'lit';
5
+ import { query, state } from 'lit/decorators.js';
6
+ import { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';
7
+ import { OscdTextButton } from '@omicronenergy/oscd-ui/button/OscdTextButton.js';
8
+ import { OscdRadio } from '@omicronenergy/oscd-ui/radio/OscdRadio.js';
9
+ import { TEMPLATE_UPDATE_SETTING_STORAGE_KEY } from '../foundation/constants.js';
10
+ // eslint-disable-next-line no-shadow
11
+ export var UpdateSetting;
12
+ (function (UpdateSetting) {
13
+ UpdateSetting["Swap"] = "swap";
14
+ UpdateSetting["Update"] = "update";
15
+ })(UpdateSetting || (UpdateSetting = {}));
16
+ export class SettingsDialog extends ScopedElementsMixin(LitElement) {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.updateSetting = UpdateSetting.Update;
20
+ }
21
+ static { this.scopedElements = {
22
+ 'oscd-dialog': OscdDialog,
23
+ 'oscd-text-button': OscdTextButton,
24
+ 'oscd-radio': OscdRadio,
25
+ }; }
26
+ connectedCallback() {
27
+ super.connectedCallback();
28
+ this.loadSettings();
29
+ }
30
+ loadSettings() {
31
+ const stored = localStorage.getItem(TEMPLATE_UPDATE_SETTING_STORAGE_KEY);
32
+ if (stored && Object.values(UpdateSetting).includes(stored)) {
33
+ this.updateSetting = stored;
34
+ }
35
+ else {
36
+ this.updateSetting = UpdateSetting.Update;
37
+ }
38
+ }
39
+ saveSettings() {
40
+ localStorage.setItem(TEMPLATE_UPDATE_SETTING_STORAGE_KEY, this.updateSetting);
41
+ }
42
+ get open() {
43
+ return this.dialog?.open ?? false;
44
+ }
45
+ show() {
46
+ this.loadSettings();
47
+ this.dialog?.show();
48
+ }
49
+ close() {
50
+ this.dialog?.close();
51
+ }
52
+ handleRadioChange(event) {
53
+ const target = event.target;
54
+ if (target.checked) {
55
+ this.updateSetting = target.value;
56
+ }
57
+ }
58
+ handleConfirm() {
59
+ this.saveSettings();
60
+ this.close();
61
+ }
62
+ handleCancel() {
63
+ this.loadSettings();
64
+ this.close();
65
+ }
66
+ render() {
67
+ return html `
68
+ <oscd-dialog @closed=${() => this.dialog?.close()}>
69
+ <div slot="headline">LNodeType update behaviour</div>
70
+ <div slot="content">
71
+ <div class="radio-group">
72
+ <label class="radio-item">
73
+ <oscd-radio
74
+ name="update-setting"
75
+ value=${UpdateSetting.Update}
76
+ .checked=${this.updateSetting === UpdateSetting.Update}
77
+ @change=${this.handleRadioChange}
78
+ ></oscd-radio>
79
+ <span class="radio-label">Update logical node type </span>
80
+ </label>
81
+ <label class="radio-item">
82
+ <oscd-radio
83
+ name="update-setting"
84
+ value=${UpdateSetting.Swap}
85
+ .checked=${this.updateSetting === UpdateSetting.Swap}
86
+ @change=${this.handleRadioChange}
87
+ ></oscd-radio>
88
+ <span class="radio-label">Swap logical node type </span>
89
+ </label>
90
+ </div>
91
+ </div>
92
+ <div slot="actions">
93
+ <oscd-text-button @click=${this.handleCancel} type="button">
94
+ Cancel
95
+ </oscd-text-button>
96
+ <oscd-text-button @click=${this.handleConfirm} type="button">
97
+ Save
98
+ </oscd-text-button>
99
+ </div>
100
+ </oscd-dialog>
101
+ `;
102
+ }
103
+ static { this.styles = css `
104
+ oscd-dialog {
105
+ --md-dialog-container-max-width: 400px;
106
+ }
107
+
108
+ .radio-group {
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: 12px;
112
+ }
113
+
114
+ .radio-item {
115
+ display: flex;
116
+ align-items: flex-start;
117
+ gap: 12px;
118
+ cursor: pointer;
119
+ padding: 8px;
120
+ }
121
+
122
+ .radio-item:hover {
123
+ background-color: rgba(0, 0, 0, 0.04);
124
+ }
125
+
126
+ .radio-label {
127
+ display: flex;
128
+ flex-direction: column;
129
+ gap: 4px;
130
+ flex: 1;
131
+ }
132
+
133
+ oscd-text-button {
134
+ text-transform: uppercase;
135
+ }
136
+ `; }
137
+ }
138
+ __decorate([
139
+ query('oscd-dialog')
140
+ ], SettingsDialog.prototype, "dialog", void 0);
141
+ __decorate([
142
+ state()
143
+ ], SettingsDialog.prototype, "updateSetting", void 0);
144
+ //# sourceMappingURL=settings-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-dialog.js","sourceRoot":"","sources":["../../components/settings-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';\nimport { LitElement, html, css } from 'lit';\nimport { query, state } from 'lit/decorators.js';\nimport { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';\nimport { OscdTextButton } from '@omicronenergy/oscd-ui/button/OscdTextButton.js';\nimport { OscdRadio } from '@omicronenergy/oscd-ui/radio/OscdRadio.js';\nimport { TEMPLATE_UPDATE_SETTING_STORAGE_KEY } from '../foundation/constants.js';\n\n// eslint-disable-next-line no-shadow\nexport enum UpdateSetting {\n Swap = 'swap',\n Update = 'update',\n}\n\nexport class SettingsDialog extends ScopedElementsMixin(LitElement) {\n static scopedElements = {\n 'oscd-dialog': OscdDialog,\n 'oscd-text-button': OscdTextButton,\n 'oscd-radio': OscdRadio,\n };\n\n @query('oscd-dialog')\n dialog!: OscdDialog;\n\n @state()\n private updateSetting: UpdateSetting = UpdateSetting.Update;\n\n connectedCallback() {\n super.connectedCallback();\n this.loadSettings();\n }\n\n private loadSettings() {\n const stored = localStorage.getItem(\n TEMPLATE_UPDATE_SETTING_STORAGE_KEY\n ) as UpdateSetting;\n if (stored && Object.values(UpdateSetting).includes(stored)) {\n this.updateSetting = stored;\n } else {\n this.updateSetting = UpdateSetting.Update;\n }\n }\n\n private saveSettings() {\n localStorage.setItem(\n TEMPLATE_UPDATE_SETTING_STORAGE_KEY,\n this.updateSetting\n );\n }\n\n get open() {\n return this.dialog?.open ?? false;\n }\n\n show() {\n this.loadSettings();\n this.dialog?.show();\n }\n\n close() {\n this.dialog?.close();\n }\n\n private handleRadioChange(event: Event) {\n const target = event.target as OscdRadio;\n if (target.checked) {\n this.updateSetting = target.value as UpdateSetting;\n }\n }\n\n private handleConfirm() {\n this.saveSettings();\n this.close();\n }\n\n private handleCancel() {\n this.loadSettings();\n this.close();\n }\n\n render() {\n return html`\n <oscd-dialog @closed=${() => this.dialog?.close()}>\n <div slot=\"headline\">LNodeType update behaviour</div>\n <div slot=\"content\">\n <div class=\"radio-group\">\n <label class=\"radio-item\">\n <oscd-radio\n name=\"update-setting\"\n value=${UpdateSetting.Update}\n .checked=${this.updateSetting === UpdateSetting.Update}\n @change=${this.handleRadioChange}\n ></oscd-radio>\n <span class=\"radio-label\">Update logical node type </span>\n </label>\n <label class=\"radio-item\">\n <oscd-radio\n name=\"update-setting\"\n value=${UpdateSetting.Swap}\n .checked=${this.updateSetting === UpdateSetting.Swap}\n @change=${this.handleRadioChange}\n ></oscd-radio>\n <span class=\"radio-label\">Swap logical node type </span>\n </label>\n </div>\n </div>\n <div slot=\"actions\">\n <oscd-text-button @click=${this.handleCancel} type=\"button\">\n Cancel\n </oscd-text-button>\n <oscd-text-button @click=${this.handleConfirm} type=\"button\">\n Save\n </oscd-text-button>\n </div>\n </oscd-dialog>\n `;\n }\n\n static styles = css`\n oscd-dialog {\n --md-dialog-container-max-width: 400px;\n }\n\n .radio-group {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .radio-item {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n padding: 8px;\n }\n\n .radio-item:hover {\n background-color: rgba(0, 0, 0, 0.04);\n }\n\n .radio-label {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n }\n\n oscd-text-button {\n text-transform: uppercase;\n }\n `;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const cdClasses: readonly ["ACD", "ACT", "APC", "ASG", "BAC", "BCR", "BSC", "CMV", "DEL", "DPC", "DPL", "HDEL", "HMV", "HST", "HWYE", "INC", "INS", "ISC", "LPL", "MV", "ORG", "ORS", "SAV", "SEC", "SEQ", "SPC", "SPG", "SPS", "TCS", "TSG", "VSG", "VSS", "WYE"];
2
+ export declare const TEMPLATE_UPDATE_SETTING_STORAGE_KEY = "template-update-setting";
@@ -0,0 +1,37 @@
1
+ export const cdClasses = [
2
+ 'ACD',
3
+ 'ACT',
4
+ 'APC',
5
+ 'ASG',
6
+ 'BAC',
7
+ 'BCR',
8
+ 'BSC',
9
+ 'CMV',
10
+ 'DEL',
11
+ 'DPC',
12
+ 'DPL',
13
+ 'HDEL',
14
+ 'HMV',
15
+ 'HST',
16
+ 'HWYE',
17
+ 'INC',
18
+ 'INS',
19
+ 'ISC',
20
+ 'LPL',
21
+ 'MV',
22
+ 'ORG',
23
+ 'ORS',
24
+ 'SAV',
25
+ 'SEC',
26
+ 'SEQ',
27
+ 'SPC',
28
+ 'SPG',
29
+ 'SPS',
30
+ 'TCS',
31
+ 'TSG',
32
+ 'VSG',
33
+ 'VSS',
34
+ 'WYE',
35
+ ];
36
+ export const TEMPLATE_UPDATE_SETTING_STORAGE_KEY = 'template-update-setting';
37
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../foundation/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,IAAI;IACJ,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;CACG,CAAC;AAEX,MAAM,CAAC,MAAM,mCAAmC,GAAG,yBAAyB,CAAC","sourcesContent":["export const cdClasses = [\n 'ACD',\n 'ACT',\n 'APC',\n 'ASG',\n 'BAC',\n 'BCR',\n 'BSC',\n 'CMV',\n 'DEL',\n 'DPC',\n 'DPL',\n 'HDEL',\n 'HMV',\n 'HST',\n 'HWYE',\n 'INC',\n 'INS',\n 'ISC',\n 'LPL',\n 'MV',\n 'ORG',\n 'ORS',\n 'SAV',\n 'SEC',\n 'SEQ',\n 'SPC',\n 'SPG',\n 'SPS',\n 'TCS',\n 'TSG',\n 'VSG',\n 'VSS',\n 'WYE',\n] as const;\n\nexport const TEMPLATE_UPDATE_SETTING_STORAGE_KEY = 'template-update-setting';\n"]}
@@ -0,0 +1,4 @@
1
+ export declare function buildLNodeTree(selectedLNodeTypeClass: string, lNodeType: Element, doc: XMLDocument): {
2
+ tree: any;
3
+ unsupportedDOs: string[];
4
+ };
@@ -0,0 +1,57 @@
1
+ import { nsdToJson } from '@openscd/scl-lib';
2
+ import { cdClasses } from './constants.js';
3
+ function getCDCForDOType(doc, doType) {
4
+ const doTypeElement = doc.querySelector(`:root > DataTypeTemplates > DOType[id="${doType}"]`);
5
+ return doTypeElement?.getAttribute('cdc') ?? undefined;
6
+ }
7
+ function isSupportedCDC(cdc) {
8
+ return !!cdc && cdClasses.includes(cdc);
9
+ }
10
+ function buildDataObject(doName, cdc) {
11
+ const cdcChildren = nsdToJson(cdc);
12
+ return {
13
+ [doName]: {
14
+ tagName: 'DataObject',
15
+ type: cdc,
16
+ descID: '',
17
+ presCond: 'O',
18
+ children: cdcChildren,
19
+ },
20
+ };
21
+ }
22
+ /**
23
+ * Merges user-defined Data Objects in the NSD tree. This ensures that
24
+ * any custom or extra DOs present in the user's SCL are preserved
25
+ * in the tree, even if they are not part of the NSD definition.
26
+ */
27
+ function mergeUserDOsIntoTree(tree, lNodeType, doc) {
28
+ if (!lNodeType || !doc)
29
+ return { tree, unsupportedDOs: [] };
30
+ const result = { ...tree };
31
+ const doElements = Array.from(lNodeType.querySelectorAll(':scope > DO'));
32
+ const standardDONames = tree ? Object.keys(tree) : [];
33
+ const unsupportedDOs = [];
34
+ doElements.forEach(doEl => {
35
+ const doName = doEl.getAttribute('name');
36
+ const doType = doEl.getAttribute('type');
37
+ if (!doName || !doType)
38
+ return;
39
+ if (!standardDONames.includes(doName)) {
40
+ const cdc = getCDCForDOType(doc, doType);
41
+ if (isSupportedCDC(cdc)) {
42
+ Object.assign(result, buildDataObject(doName, cdc));
43
+ }
44
+ else {
45
+ unsupportedDOs.push(doName);
46
+ }
47
+ }
48
+ });
49
+ return { tree: result, unsupportedDOs };
50
+ }
51
+ export function buildLNodeTree(selectedLNodeTypeClass, lNodeType, doc) {
52
+ const tree = nsdToJson(selectedLNodeTypeClass);
53
+ if (!tree)
54
+ return { tree: undefined, unsupportedDOs: [] };
55
+ return mergeUserDOsIntoTree(tree, lNodeType, doc);
56
+ }
57
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../../foundation/tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,SAAS,eAAe,CAAC,GAAgB,EAAE,MAAc;IACvD,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CACrC,0CAA0C,MAAM,IAAI,CACrD,CAAC;IACF,OAAO,aAAa,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC;AACzD,CAAC;AAED,SAAS,cAAc,CAAC,GAAuB;IAC7C,OAAO,CAAC,CAAC,GAAG,IAAK,SAAmC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,GAAW;IAClD,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO;QACL,CAAC,MAAM,CAAC,EAAE;YACR,OAAO,EAAE,YAAY;YACrB,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE,GAAG;YACb,QAAQ,EAAE,WAAW;SACtB;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAC3B,IAAS,EACT,SAAkB,EAClB,GAAgB;IAEhB,IAAI,CAAC,SAAS,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAC5D,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC;IACzE,MAAM,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;YAAE,OAAO;QAC/B,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACzC,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,GAAI,CAAC,CAAC,CAAC;YACvD,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,sBAA8B,EAC9B,SAAkB,EAClB,GAAgB;IAEhB,MAAM,IAAI,GAAG,SAAS,CAAC,sBAAsB,CAAQ,CAAC;IACtD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAC1D,OAAO,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,CAAC","sourcesContent":["import { nsdToJson, LNodeDescription } from '@openscd/scl-lib';\nimport { cdClasses } from './constants.js';\n\nfunction getCDCForDOType(doc: XMLDocument, doType: string): string | undefined {\n const doTypeElement = doc.querySelector(\n `:root > DataTypeTemplates > DOType[id=\"${doType}\"]`\n );\n return doTypeElement?.getAttribute('cdc') ?? undefined;\n}\n\nfunction isSupportedCDC(cdc: string | undefined): boolean {\n return !!cdc && (cdClasses as ReadonlyArray<string>).includes(cdc);\n}\n\nfunction buildDataObject(doName: string, cdc: string) {\n const cdcChildren = nsdToJson(cdc);\n return {\n [doName]: {\n tagName: 'DataObject',\n type: cdc,\n descID: '',\n presCond: 'O',\n children: cdcChildren,\n },\n };\n}\n\n/**\n * Merges user-defined Data Objects in the NSD tree. This ensures that\n * any custom or extra DOs present in the user's SCL are preserved\n * in the tree, even if they are not part of the NSD definition.\n */\nfunction mergeUserDOsIntoTree(\n tree: any,\n lNodeType: Element,\n doc: XMLDocument\n): { tree: LNodeDescription; unsupportedDOs: string[] } {\n if (!lNodeType || !doc) return { tree, unsupportedDOs: [] };\n const result = { ...tree };\n const doElements = Array.from(lNodeType.querySelectorAll(':scope > DO'));\n const standardDONames = tree ? Object.keys(tree) : [];\n const unsupportedDOs: string[] = [];\n\n doElements.forEach(doEl => {\n const doName = doEl.getAttribute('name');\n const doType = doEl.getAttribute('type');\n if (!doName || !doType) return;\n if (!standardDONames.includes(doName)) {\n const cdc = getCDCForDOType(doc, doType);\n if (isSupportedCDC(cdc)) {\n Object.assign(result, buildDataObject(doName, cdc!));\n } else {\n unsupportedDOs.push(doName);\n }\n }\n });\n return { tree: result, unsupportedDOs };\n}\n\nexport function buildLNodeTree(\n selectedLNodeTypeClass: string,\n lNodeType: Element,\n doc: XMLDocument\n): { tree: any; unsupportedDOs: string[] } {\n const tree = nsdToJson(selectedLNodeTypeClass) as any;\n if (!tree) return { tree: undefined, unsupportedDOs: [] };\n return mergeUserDOsIntoTree(tree, lNodeType, doc);\n}\n"]}
@@ -0,0 +1,48 @@
1
+ import { Tree, TreeSelection } from '@openenergytools/tree-grid';
2
+ import { EditV2 } from '@openscd/oscd-api';
3
+ export type MissingMandatoryField = {
4
+ path: string[];
5
+ kind: 'subfield';
6
+ };
7
+ export type EmptyReferencedElement = {
8
+ tagName: 'DOType' | 'DAType' | 'EnumType';
9
+ id: string;
10
+ referencePath: string;
11
+ };
12
+ /**
13
+ * Returns an array of the mandatory fields that are missing from the selection.
14
+ * @param tree The tree structure representing the data model
15
+ * @param selection The current selection in the tree
16
+ * @returns An array of MissingMandatoryField objects
17
+ */
18
+ export declare function getMissingMandatoryFields(tree: Tree, selection: TreeSelection): MissingMandatoryField[];
19
+ /**
20
+ * Finds all referenced elements of an LNodeType that are empty (i.e., have no children).
21
+ * @param lNodeType The LNodeType element to analyse
22
+ * @param missingMandatoryFields An optional array of MissingMandatoryField objects to filter out empty elements that are part of mandatory fields
23
+ * @returns An array of EmptyReferencedElement objects representing the empty referenced elements
24
+ */
25
+ export declare function getEmptyReferencedElements(lNodeType: Element, missingMandatoryFields?: MissingMandatoryField[]): EmptyReferencedElement[];
26
+ export declare function getLNodeTypes(doc: XMLDocument | undefined): Element[];
27
+ export declare function getSelectedLNodeType(doc: XMLDocument, selected: string): Element | undefined;
28
+ export declare function isLNodeTypeReferenced(doc: XMLDocument, selectedLNodeTypeID: string | null): boolean;
29
+ export declare function filterSelection(tree: Tree, selection: TreeSelection): TreeSelection;
30
+ /**
31
+ * Creates a clone of an LNodeType with only the DOs that are present in the selection.
32
+ * DOs not included in the selection are removed from the cloned element.
33
+ * @param lNodeType - The LNodeType element to filter
34
+ * @param selection - The tree selection containing the DO names to keep
35
+ * @returns A cloned LNodeType element containing only the selected DOs
36
+ */
37
+ export declare function removeDOsNotInSelection(lNodeType: Element, selection: TreeSelection): Element;
38
+ /**
39
+ * Builds the Remove edits needed when an LNodeType is replaced.
40
+ * Clones the DataTypeTemplates once, adds the incoming new nodes, removes
41
+ * the old LNodeType, then cascades to find which pre-existing sub-types are
42
+ * now unreferenced. Returns the old LNodeType remove plus all orphan removes.
43
+ * @param dataTypeTemplates The live DataTypeTemplates element to base the remove calculations on
44
+ * @param oldLNodeTypeId The ID of the LNodeType being replaced
45
+ * @param newNodes The new nodes being added (cloned before passing in)
46
+ * @returns An array of EditV2 objects representing the nodes to remove
47
+ */
48
+ export declare function computeOrphanedRemoves(dataTypeTemplates: Element, oldLNodeTypeId: string, newNodes: Element[]): EditV2[];