Skip to content

Commit d940924

Browse files
committed
Merge branch 'main' into fix/publish-npm-openscd
2 parents 2e02bb2 + 1cd643d commit d940924

File tree

4 files changed

+301
-4
lines changed

4 files changed

+301
-4
lines changed

docs/how-to/wizarding.md

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
# Wizarding in OpenSCD
2+
3+
## OpenSCD
4+
5+
The current wizarding functionality in OpenSCD consists of these key components:
6+
7+
1. **`<oscd-wizards>`** (`packages/openscd/src/addons/Wizards.ts`)
8+
- Central wizard queue manager
9+
- Listens for `wizard` events on the host element
10+
- Manages FIFO workflow of wizard factories
11+
12+
2. **`<wizard-dialog>`** (`packages/openscd/src/wizard-dialog.ts`)
13+
- Renders wizard pages and handles user interactions
14+
- Dispatches editor actions when wizard completes
15+
16+
3. **Host Integration** (`packages/openscd/src/open-scd.ts`)
17+
```typescript
18+
render(): TemplateResult {
19+
return html`<oscd-waiter>
20+
<oscd-settings .host=${this}>
21+
<oscd-wizards .host=${this}>
22+
<oscd-history .host=${this}>
23+
<!-- ... rest of the app -->
24+
```
25+
26+
**How it works:**
27+
28+
1. **Wizard creation**: plugins create wizard definitions
29+
```typescript
30+
// Example from ied-container.ts
31+
private openEditWizard(): void {
32+
const wizard = wizards['IED'].edit(this.element);
33+
if (wizard) this.dispatchEvent(newWizardEvent(wizard));
34+
}
35+
```
36+
37+
2. **Event dispatching**: wizards are triggered by dispatching `wizard` events
38+
```typescript
39+
// From foundation.ts
40+
export function newWizardEvent(
41+
wizardOrFactory?: Wizard | WizardFactory,
42+
eventInitDict?: CustomEventInit<Partial<WizardDetail>>
43+
): WizardEvent
44+
45+
// Usage examples:
46+
this.dispatchEvent(newWizardEvent(wizardFactory)); // Open wizard
47+
this.dispatchEvent(newWizardEvent()); // Close wizard
48+
this.dispatchEvent(newWizardEvent(subWizard, { detail: { subwizard: true } })); // Subwizard
49+
```
50+
51+
3. **Central processing**: The `<oscd-wizards>` component catches all wizard events
52+
```typescript
53+
// From Wizards.ts
54+
private onWizard(we: WizardEvent) {
55+
const wizard = we.detail.wizard;
56+
if (wizard === null) this.workflow.shift();
57+
else if (we.detail.subwizard) this.workflow.unshift(wizard);
58+
else this.workflow.push(wizard);
59+
}
60+
```
61+
62+
4. **Dialog rendering**: The central `<wizard-dialog>` renders the current wizard
63+
64+
## scl-wizarding plugin
65+
66+
The [scl-wizarding plugin](https://github.com/OpenEnergyTools/scl-wizarding) is an alternative wizarding system designed specifically for SCL operations with simplified events.
67+
68+
**Key differences from OpenSCD:**
69+
70+
- **Simplified Events**: Uses element-focused events instead of wizard factories
71+
```typescript
72+
// scl-wizarding events:
73+
this.dispatchEvent(newEditWizardEvent(element)); // Edit element
74+
this.dispatchEvent(newCreateWizardEvent(parent, tagName)); // Create element
75+
76+
// vs OpenSCD events:
77+
this.dispatchEvent(newWizardEvent(wizardFactory));
78+
```
79+
80+
- **Pre-built wizard registry**: SCL element types with ready-made wizards
81+
```typescript
82+
// Comprehensive wizard coverage for SCL elements:
83+
// Substation, VoltageLevel, Bay, ConductingEquipment, PowerTransformer,
84+
// IED, LDevice, LNode, ConnectedAP, LNodeType, DOType, DAType, etc.
85+
```
86+
87+
88+
**Usage:**
89+
```typescript
90+
// Edit existing SCL element
91+
private editElement(): void {
92+
this.dispatchEvent(newEditWizardEvent(this.element));
93+
}
94+
95+
// Create new SCL element
96+
private createElement(): void {
97+
this.dispatchEvent(newCreateWizardEvent(parentElement, 'ConductingEquipment'));
98+
}
99+
```
100+
101+
## Moving away from centralised wizarding
102+
103+
Both the OpenSCD centralised wizarding and the scl-wizarding plugin represent centralised approaches that should be migrated away from in favour of plugin-based solutions.
104+
105+
### Migration strategy
106+
107+
**Step 1: Identify current usage**
108+
```typescript
109+
// Look for these patterns in your plugins:
110+
111+
// OpenSCD centralised:
112+
this.dispatchEvent(newWizardEvent(wizard));
113+
114+
// scl-wizarding:
115+
this.dispatchEvent(newEditWizardEvent(element));
116+
this.dispatchEvent(newCreateWizardEvent(parent, tagName));
117+
```
118+
119+
**Step 2: Replace with direct dialog management**
120+
121+
Instead of dispatching events to a central wizard system, manage dialog state directly in your plugin component:
122+
123+
```typescript
124+
// BEFORE: Centralized approach - events go to external wizard system
125+
export class MyPlugin extends LitElement {
126+
private openEditWizard(): void {
127+
// Event gets handled by <oscd-wizards> or scl-wizarding
128+
this.dispatchEvent(newWizardEvent(wizard)); // OpenSCD
129+
// OR
130+
this.dispatchEvent(newEditWizardEvent(this.element)); // scl-wizarding
131+
}
132+
133+
render() {
134+
return html`
135+
<mwc-icon-button icon="edit" @click=${this.openEditWizard}></mwc-icon-button>
136+
<!-- No dialog here - it's handled by external system -->
137+
`;
138+
}
139+
}
140+
141+
// AFTER: Direct management - plugin controls its own dialogs
142+
export class MyPlugin extends LitElement {
143+
@state() private showEditDialog = false;
144+
@state() private currentElement: Element | null = null;
145+
146+
private openEditor(element: Element): void {
147+
// No events dispatched - just update local state
148+
this.currentElement = element;
149+
this.showEditDialog = true;
150+
}
151+
152+
render() {
153+
return html`
154+
<mwc-icon-button icon="edit" @click=${() => this.openEditor(element)}></mwc-icon-button>
155+
156+
<!-- Plugin renders its own dialog directly -->
157+
${this.showEditDialog ? html`
158+
<my-edit-dialog
159+
.element=${this.currentElement}
160+
@close=${() => this.showEditDialog = false}
161+
></my-edit-dialog>
162+
` : nothing}
163+
`;
164+
}
165+
}
166+
```
167+
168+
**Key difference:** Instead of sending events to external systems, your plugin directly controls when dialogs open/close using its own state.
169+
170+
**Step 3: Implement plugin-specific dialogs**
171+
172+
Here are minimal examples showing the modern approach with `newEditEvent`:
173+
174+
**Edit dialog example:**
175+
```typescript
176+
// simple-edit-dialog.ts
177+
import { LitElement, html } from 'lit';
178+
import { property, customElement } from 'lit/decorators.js';
179+
import { newEditEvent } from '@openscd/open-scd-core';
180+
181+
@customElement('simple-edit-dialog')
182+
export class SimpleEditDialog extends LitElement {
183+
@property({ attribute: false }) element!: Element;
184+
@property() open = false;
185+
186+
private onSave() {
187+
const name = this.shadowRoot?.querySelector('#name')?.value;
188+
if (!name) return;
189+
190+
const update = {
191+
element: this.element,
192+
attributes: { name }
193+
};
194+
this.dispatchEvent(newEditEvent(update));
195+
this.close();
196+
}
197+
198+
private close() {
199+
this.dispatchEvent(new CustomEvent('close'));
200+
}
201+
202+
render() {
203+
return html`
204+
<mwc-dialog .open=${this.open} @closed=${this.close}>
205+
<input
206+
id="name"
207+
placeholder="Name"
208+
.value=${this.element.getAttribute('name') ?? ''}
209+
/>
210+
<mwc-button slot="secondaryAction" @click=${this.close}>Cancel</mwc-button>
211+
<mwc-button slot="primaryAction" @click=${this.onSave}>Save</mwc-button>
212+
</mwc-dialog>
213+
`;
214+
}
215+
}
216+
```
217+
218+
**Create dialog example:**
219+
```typescript
220+
// simple-create-dialog.ts
221+
import { LitElement, html } from 'lit';
222+
import { property, customElement } from 'lit/decorators.js';
223+
import { newEditEvent, Insert } from '@openscd/open-scd-core';
224+
225+
@customElement('simple-create-dialog')
226+
export class SimpleCreateDialog extends LitElement {
227+
@property({ attribute: false }) parent!: Element;
228+
@property() tagName!: string;
229+
@property() open = false;
230+
231+
private onCreate() {
232+
const name = this.shadowRoot?.querySelector('#name')?.value;
233+
if (!name) return;
234+
235+
const doc = this.parent.ownerDocument!;
236+
const newElement = doc.createElement(this.tagName);
237+
newElement.setAttribute('name', name);
238+
239+
const insert: Insert = {
240+
parent: this.parent,
241+
node: newElement,
242+
reference: null
243+
};
244+
this.dispatchEvent(newEditEvent(insert));
245+
this.close();
246+
}
247+
248+
private close() {
249+
this.dispatchEvent(new CustomEvent('close'));
250+
}
251+
252+
render() {
253+
return html`
254+
<mwc-dialog .open=${this.open} @closed=${this.close}>
255+
<input id="name" placeholder="Name" />
256+
<mwc-button slot="secondaryAction" @click=${this.close}>Cancel</mwc-button>
257+
<mwc-button slot="primaryAction" @click=${this.onCreate}>Create</mwc-button>
258+
</mwc-dialog>
259+
`;
260+
}
261+
}
262+
```
263+
264+
**Usage in plugin:**
265+
```typescript
266+
// In your plugin component
267+
@state() private showEditDialog = false;
268+
@state() private showCreateDialog = false;
269+
@state() private editElement?: Element;
270+
271+
private openEditor(element: Element) {
272+
this.editElement = element;
273+
this.showEditDialog = true;
274+
}
275+
276+
private openCreator() {
277+
this.showCreateDialog = true;
278+
}
279+
280+
render() {
281+
return html`
282+
<mwc-icon-button icon="edit" @click=${() => this.openEditor(element)}></mwc-icon-button>
283+
<mwc-icon-button icon="add" @click=${() => this.openCreator()}></mwc-icon-button>
284+
285+
<simple-edit-dialog
286+
.element=${this.editElement}
287+
.open=${this.showEditDialog}
288+
@close=${() => this.showEditDialog = false}
289+
></simple-edit-dialog>
290+
291+
<simple-create-dialog
292+
.parent=${this.parentElement}
293+
tagName="Function"
294+
.open=${this.showCreateDialog}
295+
@close=${() => this.showCreateDialog = false}
296+
></simple-create-dialog>
297+
`;
298+
}
299+
```

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/foundation/deprecated/history.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { EditV2 } from '../edit.js';
2-
31
type InfoEntryKind = 'info' | 'warning' | 'error';
42

53
export type LogEntryType = 'info' | 'warning' | 'error' | 'action' | 'reset';

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@compas-oscd/core",
3-
"version": "0.1.21",
3+
"version": "0.1.23",
44
"description": "The core editor component of open-scd, without any extensions pre-installed.",
55
"author": "Open-SCD",
66
"license": "Apache-2.0",

0 commit comments

Comments
 (0)