Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Edit api v3 #1615

Merged
merged 16 commits into from
Jan 27, 2025
2 changes: 1 addition & 1 deletion .github/workflows/test-and-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on: pull_request

jobs:
test-and-build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/[email protected]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/[email protected]
Expand Down
116 changes: 113 additions & 3 deletions docs/core-api/edit-api.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,123 @@
# Edit Event API
# Edit Event API v2

trusz marked this conversation as resolved.
Show resolved Hide resolved
Open SCD offers an API for editing the scd document which can be used with [Html Custom Events](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent). The main Open SCD components listens to events of the type `oscd-edit`, applies the changes to the `doc` and updates the `editCount` property.
Open SCD offers an API for editing the scd document which can be used with [Html Custom Events](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent). 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.

## Event factory

Open SCD core exports a factory function for edit events, so you do not have to build them manually.

```ts
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;
};
```

### EditEventOptionsV2

* `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 updated `doc` before applying each edit. Defaults to `false`.
* `createHistoryEntry` decides whether a history for the `edit` should be created. Defaults to `true`.

### Insert

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.

```ts
interface InsertV2 {
parent: Node;
node: Node;
reference: Node | null;
}
```

### Remove

This event will remove the node from the document.

```ts
interface RemoveV2 {
node: Node;
}
```

### SetAttributes

Sets attributes for the element, can set both regular and namespaced attributes.

```ts
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`.

```ts
const setNamespacedAttributes: SetAttributesV2 = {
element,
attributes: {},
attributesNS: {
"https://example.com": {
"exa:type": "secondary"
}
}
}
```

### SetTextContent

Sets the text content of the element, removes any other children. To remove text content you can pass `null` as value for `textContent`.

```ts
interface SetTextContentV2 {
element: Element;
textContent: string;
}
```

### Complex edits

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.

```ts
import { newEditEventV2 } from '@openscd/core';

const complexEditEvent = newEditEventV2([ insert, update, remove ]);

someComponent.dispatchEvent(complexEditEvent);

```

## History

All edit events with the option `createHistoryEntry` will create a history log entry and can be undone and redone through the history addon.


# Archives

## Edit Event API v1 (deprecated)

The edit event API v1 is still available and listens to events of the type `oscd-edit`.

## Event factory

Open SCD core exports a factory function for edit events, so you do not have to build them manually.

```ts
function newEditEvent<E extends Edit>(
edit: E,
Expand Down Expand Up @@ -158,7 +268,7 @@ With open SCD version **v0.36.0** and higher some editor action features are no

---

# Archives - Editor Action API (deprecated)
## Editor Action API (deprecated)

### Event factory

Expand Down
26 changes: 25 additions & 1 deletion packages/core/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export {
isNamespaced,
isUpdate,
isRemove,
} from './foundation/edit-event.js';
} from './foundation/deprecated/edit-event.js';
export type {
EditEvent,
Edit,
Expand All @@ -22,7 +22,31 @@ export type {
NamespacedAttributeValue,
Update,
Remove,
} from './foundation/deprecated/edit-event.js';

export type {
EditV2,
InsertV2,
RemoveV2,
SetTextContentV2,
SetAttributesV2,
} from './foundation/edit.js';
export {
isEditV2,
isRemoveV2,
isInsertV2,
isComplexV2,
isSetAttributesV2,
isSetTextContentV2
} from './foundation/edit.js';
export type {
EditEventV2,
EditEventOptionsV2,
EditDetailV2
} from './foundation/edit-event.js';
export { newEditEventV2 } from './foundation/edit-event.js';

export { handleEditV2 } from './foundation/handle-edit.js';

export { cyrb64 } from './foundation/cyrb64.js';

Expand Down
119 changes: 119 additions & 0 deletions packages/core/foundation/deprecated/edit-event.ts
clepski marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* @deprecated Use the new edit event V2 API instead.
*/
export type Initiator = 'user' | 'system' | 'undo' | 'redo' | string;

/**
* @deprecated Use the new edit event V2 API instead.
*/
export type Insert = {
parent: Node;
node: Node;
reference: Node | null;
};

/**
* @deprecated Use the new edit event V2 API instead.
*/
export type NamespacedAttributeValue = {
value: string | null;
namespaceURI: string | null;
};
/**
* @deprecated Use the new edit event V2 API instead.
*/
export type AttributeValue = string | null | NamespacedAttributeValue;
/**
* @deprecated Use the new edit event V2 API instead.
*/
export type Update = {
element: Element;
attributes: Partial<Record<string, AttributeValue>>;
};

/**
* @deprecated Use the new edit event V2 API instead.
*/
export type Remove = {
node: Node;
};

/**
* @deprecated Use the new edit event V2 API instead.
*/
export type Edit = Insert | Update | Remove | Edit[];
trusz marked this conversation as resolved.
Show resolved Hide resolved

/**
* @deprecated Use the new edit event V2 API instead.
*/
export function isComplex(edit: Edit): edit is Edit[] {
return edit instanceof Array;
}

/**
* @deprecated Use the new edit event V2 API instead.
*/
export function isInsert(edit: Edit): edit is Insert {
return (edit as Insert).parent !== undefined;
}

/**
* @deprecated Use the new edit event V2 API instead.
*/
export function isNamespaced(
value: AttributeValue
): value is NamespacedAttributeValue {
return value !== null && typeof value !== 'string';
}

/**
* @deprecated Use the new edit event V2 API instead.
*/
export function isUpdate(edit: Edit): edit is Update {
return (edit as Update).element !== undefined;
}

/**
* @deprecated Use the new edit event V2 API instead.
*/
export function isRemove(edit: Edit): edit is Remove {
return (
(edit as Insert).parent === undefined && (edit as Remove).node !== undefined
);
}

/**
* @deprecated Use the new edit event V2 API instead.
*/
export interface EditEventDetail<E extends Edit = Edit> {
edit: E;
initiator: Initiator;
}

/**
* @deprecated Use the new edit event V2 API instead.
*/
export type EditEvent = CustomEvent<EditEventDetail>;

/**
* @deprecated Use the new edit event V2 API instead.
*/
export function newEditEvent<E extends Edit>(
edit: E,
initiator: Initiator = 'user'
): EditEvent {
return new CustomEvent<EditEventDetail>('oscd-edit', {
composed: true,
bubbles: true,
detail: {
edit: edit,
initiator: initiator,
},
});
}

declare global {
interface ElementEventMap {
['oscd-edit']: EditEvent;
}
}
2 changes: 1 addition & 1 deletion packages/core/foundation/deprecated/editor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Initiator } from '../edit-event.js';
import { Initiator } from './edit-event.js';

/** Inserts `new.element` to `new.parent` before `new.reference`. */
export interface Create {
Expand Down
7 changes: 4 additions & 3 deletions packages/core/foundation/deprecated/history.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Edit } from '../edit-event.js';
import { EditV2 } from '../edit.js';

type InfoEntryKind = 'info' | 'warning' | 'error';

Expand All @@ -12,8 +12,9 @@ export interface LogDetailBase {
/** The [[`LogEntry`]] for a committed [[`EditorAction`]]. */
export interface CommitDetail extends LogDetailBase {
kind: 'action';
redo: Edit;
undo: Edit;
redo: EditV2;
undo: EditV2;
squash?: boolean;
}
/** A [[`LogEntry`]] for notifying the user. */
export interface InfoDetail extends LogDetailBase {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/foundation/edit-completed-event.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Edit, Initiator } from './edit-event.js';
import { Edit, Initiator } from './deprecated/edit-event.js';

import { EditorAction } from './deprecated/editor.js';

Expand Down
Loading
Loading