Open SCD offers an API for editing the scd document which can be used with Html Custom Events. The main Open SCD components listens to events of the type oscd-edit-v2
, applies the changes to the doc
and updates the editCount
property.
The edits to the doc
will be done in place, e.g. the doc
changes but will keep the same reference. If your plugin needs to react to changes in the doc, you should listen to changes in the editCount
property.
Open SCD core exports a factory function for edit events, so you do not have to build them manually.
function newEditEventV2<E extends EditV2>(
edit: E,
options?: EditEventOptionsV2
): EditEventV2
type EditV2 = InsertV2 | SetAttributesV2 | SetTextContentV2 | RemoveV2 | EditV2[];
interface EditEventOptionsV2 = {
title?: string;
squash?: boolean;
createHistoryEntry?: boolean;
};
title
set a title to be shown in the history.squash
squash edit with previous history entry, this is useful if you want to create multiple edits based on an user action, but need the updateddoc
before applying each edit. Defaults tofalse
.createHistoryEntry
decides whether a history for theedit
should be created. Defaults totrue
.
Insert events can be used to add new nodes or move existing nodes in the document. Since a node can only have one parent, using an insert on an existing node will replace it's previous parent with the new parent, essentially moving the node to a different position in the xml tree.
If the reference is not null
, the node will be inserted before the reference node. The reference has to be a child node of the parent. And if the reference is null
the node will be added as the last child of the parent.
interface InsertV2 {
parent: Node;
node: Node;
reference: Node | null;
}
This event will remove the node from the document.
interface RemoveV2 {
node: Node;
}
Sets attributes for the element, can set both regular and namespaced attributes.
interface SetAttributesV2 {
element: Element;
attributes: Partial<Record<string, string | null>>;
attributesNS: Partial<Record<string, Partial<Record<string, string | null>>>>;
}
To set a namespaced attribute see the following example. Here we are setting the attribute exa:type
for the namespace https://example.com
to secondary
.
const setNamespacedAttributes: SetAttributesV2 = {
element,
attributes: {},
attributesNS: {
"https://example.com": {
"exa:type": "secondary"
}
}
}
Sets the text content of the element, removes any other children. To remove text content you can pass null
as value for textContent
.
interface SetTextContentV2 {
element: Element;
textContent: string;
}
Complex edits can be used to apply multiple edits as a single event. This will create a single entry in the history. You can create complex edit events by passing an array of edit events to the newEditEventV2
factory function.
import { newEditEventV2 } from '@openscd/core';
const complexEditEvent = newEditEventV2([ insert, update, remove ]);
someComponent.dispatchEvent(complexEditEvent);
All edit events with the option createHistoryEntry
will create a history log entry and can be undone and redone through the history addon.
The edit event API v1 is still available and listens to events of the type oscd-edit
.
Open SCD core exports a factory function for edit events, so you do not have to build them manually.
function newEditEvent<E extends Edit>(
edit: E,
initiator: Initiator = 'user'
): EditEvent
type Edit = Insert | Update | Remove | Edit[];
type Initiator = 'user' | 'system' | 'undo' | 'redo' | string;
Example for remove.
import { newEditEvent, Remove } from '@openscd/core';
const remove: Remove = { node: someNode };
const removeEvent = newEditEvent(remove);
someComponent.dispatchEvent(removeEvent);
Insert events can be used to add new nodes or move existing nodes in the document. Since a node can only have one parent, using an insert on an existing node will replace it's previous parent with the new parent, essentially moving the node to a different position in the xml tree.
If the reference is not null
, the node will be inserted before the reference node. The reference has to be a child node of the parent. And if the reference is null
the node will be added as the last child of the parent.
interface Insert {
parent: Node;
node: Node;
reference: Node | null;
}
This event will remove the node from the document.
interface Remove {
node: Node;
}
Update can add, remove or change attributes on an existing node. Existing attributes will only be removed, if null
is passed as value in the event's attributes
property.
interface Update {
element: Element;
attributes: Partial<Record<string, AttributeValue>>;
}
// Attirubte value
type AttributeValue = string | null | NamespacedAttributeValue;
type NamespacedAttributeValue = {
value: string | null;
namespaceURI: string | null;
};
Example for adding and changing values.
const update: Update = {
element: elementToUpdate,
attributes: {
name: 'new name',
value: 'new value'
}
};
To remove an existing value pass null
as value.
const update: Update = {
element: elementToUpdate,
attributes: {
attributeToRemove: null
}
};
Update also supports Xml namespaces for attributes. To change namespaced attributes you need to pass an NamespacedAttributeValue
instead of a plain string
.
const update: Update = {
element: elementToUpdate,
attributes: {
name: {
value: 'namespaced name',
namespaceURI: 'http://www.iec.ch/61850/2003/SCLcoordinates'
},
type: {
value: 'namespaced type',
namespaceURI: 'http://www.iec.ch/61850/2003/SCLcoordinates'
},
}
};
Adding, updating and removing attributes with and without namespaces can be combined in a single Update
.
Complex edits can be used to apply multiple edits as a single event. This will create a single entry in the history. You can create complex edit events by passing an array of edit events to the newEditEvent
factory function.
import { newEditEvent } from '@openscd/core';
const complexEditEvent = newEditEvent([ insert, update, remove ]);
someComponent.dispatchEvent(complexEditEvent);
All edit events with initiator user
will create a history log entry and can be undone and redone through the history addon.
Before the edit event API the editor action API was used to edit the doc
. It is also custom event based and listens to the events of the type editor-action
.
For backwards compatibility the API is still supported, but it is recommended to use the edit event API instead. Internally editor actions are converted to edit events.
With open SCD version v0.36.0 and higher some editor action features are no longer supported see Deprecated Editor Action API.
- The editor action properties
derived
andcheckValidity
do not have any effect. - All validation checks have been removed (i.e. check for unique
id
attribute on element before create). - The
title
forComplexAction
does not have any effect.
function newActionEvent<T extends EditorAction>(
action: T,
initiator: Initiator = 'user',
eventInitDict?: CustomEventInit<Partial<EditorActionDetail<T>>>
): EditorActionEvent<T>
type SimpleAction = Update | Create | Replace | Delete | Move;
type ComplexAction = {
actions: SimpleAction[];
title: string;
derived?: boolean;
};
type EditorAction = SimpleAction | ComplexAction;
Create
actions are converted to Insert
events.
interface Create {
new: { parent: Node; element: Node; reference?: Node | null };
derived?: boolean;
checkValidity?: () => boolean;
}
Move
actions are converted to Insert
events.
interface Move {
old: { parent: Element; element: Element; reference?: Node | null };
new: { parent: Element; reference?: Node | null };
derived?: boolean;
checkValidity?: () => boolean;
}
Delete
actions are converted to Remove
events.
interface Delete {
old: { parent: Node; element: Node; reference?: Node | null };
derived?: boolean;
checkValidity?: () => boolean;
}
Update
actions are converted to Update
events.
interface Update {
element: Element;
oldAttributes: Record<string, string | null>;
newAttributes: Record<string, string | null>;
derived?: boolean;
checkValidity?: () => boolean;
}
Replace
actions are converted to a complex event with Remove
and Insert
events.
interface Replace {
old: { element: Element };
new: { element: Element };
derived?: boolean;
checkValidity?: () => boolean;
}