Skip to content

Commit 9be83ab

Browse files
refactor(foundation): rename Update to Replace
1 parent f389c2c commit 9be83ab

35 files changed

+967
-431
lines changed

src/Editing.ts

Lines changed: 101 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
isDelete,
1212
isMove,
1313
isSimple,
14-
isUpdate,
14+
isReplace,
1515
LitElementConstructor,
1616
Mixin,
1717
Move,
@@ -20,7 +20,9 @@ import {
2020
OpenDocEvent,
2121
SCLTag,
2222
SimpleAction,
23+
Replace,
2324
Update,
25+
isUpdate,
2426
} from './foundation.js';
2527

2628
/** Mixin that edits an `XML` `doc`, listening to [[`EditorActionEvent`]]s */
@@ -42,12 +44,19 @@ export function Editing<TBase extends LitElementConstructor>(Base: TBase) {
4244
private checkCreateValidity(create: Create): boolean {
4345
if (create.checkValidity !== undefined) return create.checkValidity();
4446

47+
if (
48+
!(create.new.element instanceof Element) ||
49+
!(create.new.parent instanceof Element)
50+
)
51+
return true;
52+
4553
const invalid =
4654
create.new.element.hasAttribute('name') &&
4755
Array.from(create.new.parent.children).some(
4856
elm =>
49-
elm.tagName === create.new.element.tagName &&
50-
elm.getAttribute('name') === create.new.element.getAttribute('name')
57+
elm.tagName === (<Element>create.new.element).tagName &&
58+
elm.getAttribute('name') ===
59+
(<Element>create.new.element).getAttribute('name')
5160
);
5261

5362
if (invalid)
@@ -74,24 +83,32 @@ export function Editing<TBase extends LitElementConstructor>(Base: TBase) {
7483
private onCreate(action: Create) {
7584
if (!this.checkCreateValidity(action)) return false;
7685

77-
if (action.new.reference === undefined)
86+
if (
87+
action.new.reference === undefined &&
88+
action.new.element instanceof Element &&
89+
action.new.parent instanceof Element
90+
)
7891
action.new.reference = getReference(
7992
action.new.parent,
8093
<SCLTag>action.new.element.tagName
8194
);
95+
else action.new.reference = action.new.reference ?? null;
8296

8397
action.new.parent.insertBefore(action.new.element, action.new.reference);
8498
return true;
8599
}
86100

87101
private logCreate(action: Create) {
102+
const name =
103+
action.new.element instanceof Element
104+
? action.new.element.tagName
105+
: get('editing.node');
106+
88107
this.dispatchEvent(
89108
newLogEvent({
90109
kind: 'action',
91-
title: get('editing.created', {
92-
name: action.new.element.tagName,
93-
}),
94-
action: action,
110+
title: get('editing.created', { name }),
111+
action,
95112
})
96113
);
97114
}
@@ -100,18 +117,23 @@ export function Editing<TBase extends LitElementConstructor>(Base: TBase) {
100117
if (!action.old.reference)
101118
action.old.reference = action.old.element.nextSibling;
102119

103-
action.old.element.remove();
120+
if (action.old.element.parentNode !== action.old.parent) return false;
121+
122+
action.old.parent.removeChild(action.old.element);
104123
return true;
105124
}
106125

107126
private logDelete(action: Delete) {
127+
const name =
128+
action.old.element instanceof Element
129+
? action.old.element.tagName
130+
: get('editing.node');
131+
108132
this.dispatchEvent(
109133
newLogEvent({
110134
kind: 'action',
111-
title: get('editing.deleted', {
112-
name: action.old.element.tagName,
113-
}),
114-
action: action,
135+
title: get('editing.deleted', { name }),
136+
action,
115137
})
116138
);
117139
}
@@ -174,68 +196,118 @@ export function Editing<TBase extends LitElementConstructor>(Base: TBase) {
174196
);
175197
}
176198

177-
private checkUpdateValidity(update: Update): boolean {
178-
if (update.checkValidity !== undefined) return update.checkValidity();
199+
private checkReplaceValidity(replace: Replace): boolean {
200+
if (replace.checkValidity !== undefined) return replace.checkValidity();
179201

180202
const invalid =
181-
update.new.element.hasAttribute('name') &&
182-
update.new.element.getAttribute('name') !==
183-
update.old.element.getAttribute('name') &&
184-
Array.from(update.old.element.parentElement?.children ?? []).some(
203+
replace.new.element.hasAttribute('name') &&
204+
replace.new.element.getAttribute('name') !==
205+
replace.old.element.getAttribute('name') &&
206+
Array.from(replace.old.element.parentElement?.children ?? []).some(
185207
elm =>
186-
elm.tagName === update.new.element.tagName &&
187-
elm.getAttribute('name') === update.new.element.getAttribute('name')
208+
elm.tagName === replace.new.element.tagName &&
209+
elm.getAttribute('name') ===
210+
replace.new.element.getAttribute('name')
188211
);
189212

190213
if (invalid)
191214
this.dispatchEvent(
192215
newLogEvent({
193216
kind: 'error',
194217
title: get('editing.error.update', {
195-
name: update.new.element.tagName,
218+
name: replace.new.element.tagName,
196219
}),
197220
message: get('editing.error.nameClash', {
198-
parent: update.old.element.parentElement!.tagName,
199-
child: update.new.element.tagName,
200-
name: update.new.element.getAttribute('name')!,
221+
parent: replace.old.element.parentElement!.tagName,
222+
child: replace.new.element.tagName,
223+
name: replace.new.element.getAttribute('name')!,
201224
}),
202225
})
203226
);
204227

205228
return !invalid;
206229
}
207230

208-
private onUpdate(action: Update) {
209-
if (!this.checkUpdateValidity(action)) return false;
231+
private onReplace(action: Replace) {
232+
if (!this.checkReplaceValidity(action)) return false;
210233

211234
action.new.element.append(...Array.from(action.old.element.children));
212235
action.old.element.replaceWith(action.new.element);
213236
return true;
214237
}
215238

216-
private logUpdate(action: Update) {
239+
private logUpdate(action: Replace | Update) {
240+
const name = isReplace(action)
241+
? action.new.element.tagName
242+
: (action as Update).element.tagName;
243+
217244
this.dispatchEvent(
218245
newLogEvent({
219246
kind: 'action',
220247
title: get('editing.updated', {
221-
name: action.new.element.tagName,
248+
name,
222249
}),
223250
action: action,
224251
})
225252
);
226253
}
227254

255+
private checkUpdateValidity(update: Update): boolean {
256+
if (update.checkValidity !== undefined) return update.checkValidity();
257+
258+
const invalid = Array.from(
259+
update.element.parentElement?.children ?? []
260+
).some(
261+
elm =>
262+
elm.tagName === update.element.tagName &&
263+
elm.getAttribute('name') === update.newAttributes['name']
264+
);
265+
266+
if (invalid)
267+
this.dispatchEvent(
268+
newLogEvent({
269+
kind: 'error',
270+
title: get('editing.error.update', {
271+
name: update.element.tagName,
272+
}),
273+
message: get('editing.error.nameClash', {
274+
parent: update.element.parentElement!.tagName,
275+
child: update.element.tagName,
276+
name: update.newAttributes['name']!,
277+
}),
278+
})
279+
);
280+
281+
return !invalid;
282+
}
283+
284+
private onUpdate(action: Update) {
285+
if (!this.checkUpdateValidity(action)) return false;
286+
287+
Array.from(action.element.attributes).forEach(attr =>
288+
action.element.removeAttributeNode(attr)
289+
);
290+
291+
Object.entries(action.newAttributes).forEach(([key, value]) => {
292+
if (value) action.element.setAttribute(key, value);
293+
});
294+
295+
return true;
296+
}
297+
228298
private onSimpleAction(action: SimpleAction) {
229299
if (isMove(action)) return this.onMove(action as Move);
230300
else if (isCreate(action)) return this.onCreate(action as Create);
231301
else if (isDelete(action)) return this.onDelete(action as Delete);
302+
else if (isReplace(action)) return this.onReplace(action as Replace);
232303
else if (isUpdate(action)) return this.onUpdate(action as Update);
233304
}
234305

235306
private logSimpleAction(action: SimpleAction) {
236307
if (isMove(action)) this.logMove(action as Move);
237308
else if (isCreate(action)) this.logCreate(action as Create);
238309
else if (isDelete(action)) this.logDelete(action as Delete);
310+
else if (isReplace(action)) this.logUpdate(action as Replace);
239311
else if (isUpdate(action)) this.logUpdate(action as Update);
240312
}
241313

src/editors/templates/foundation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ function containsCreateAction(actions: Create[], newAction: Create): boolean {
5151
return !actions.some(
5252
action =>
5353
action.new.parent === newAction.new.parent &&
54-
action.new.element.getAttribute('id') ===
55-
newAction.new.element.getAttribute('id')
54+
(<Element>action.new.element).getAttribute('id') ===
55+
(<Element>newAction.new.element).getAttribute('id')
5656
);
5757
}
5858

src/foundation.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { WizardTextField } from './wizard-textfield.js';
1010
import { WizardSelect } from './wizard-select.js';
1111
import { WizardCheckbox } from './wizard-checkbox.js';
1212

13-
export type SimpleAction = Create | Update | Delete | Move;
13+
export type SimpleAction = Update | Create | Replace | Delete | Move;
1414
export type ComplexAction = {
1515
actions: SimpleAction[];
1616
title: string;
@@ -20,13 +20,13 @@ export type ComplexAction = {
2020
export type EditorAction = SimpleAction | ComplexAction;
2121
/** Inserts `new.element` to `new.parent` before `new.reference`. */
2222
export interface Create {
23-
new: { parent: Element; element: Element; reference?: Node | null };
23+
new: { parent: Node; element: Node; reference?: Node | null };
2424
derived?: boolean;
2525
checkValidity?: () => boolean;
2626
}
2727
/** Removes `old.element` from `old.parent` before `old.reference`. */
2828
export interface Delete {
29-
old: { parent: Element; element: Element; reference?: Node | null };
29+
old: { parent: Node; element: Node; reference?: Node | null };
3030
derived?: boolean;
3131
checkValidity?: () => boolean;
3232
}
@@ -38,16 +38,24 @@ export interface Move {
3838
checkValidity?: () => boolean;
3939
}
4040
/** Replaces `old.element` with `new.element`, keeping element children. */
41-
export interface Update {
41+
export interface Replace {
4242
old: { element: Element };
4343
new: { element: Element };
4444
derived?: boolean;
4545
checkValidity?: () => boolean;
4646
}
47+
/** Swaps `element`s `oldAttributes` with `newAttributes` */
48+
export interface Update {
49+
element: Element;
50+
oldAttributes: Record<string, string | null>;
51+
newAttributes: Record<string, string | null>;
52+
derived?: boolean;
53+
checkValidity?: () => boolean;
54+
}
4755

4856
export function isCreate(action: EditorAction): action is Create {
4957
return (
50-
(action as Update).old === undefined &&
58+
(action as Replace).old === undefined &&
5159
(action as Create).new?.parent !== undefined &&
5260
(action as Create).new?.element !== undefined
5361
);
@@ -56,23 +64,32 @@ export function isDelete(action: EditorAction): action is Delete {
5664
return (
5765
(action as Delete).old?.parent !== undefined &&
5866
(action as Delete).old?.element !== undefined &&
59-
(action as Update).new === undefined
67+
(action as Replace).new === undefined
6068
);
6169
}
6270
export function isMove(action: EditorAction): action is Move {
6371
return (
6472
(action as Move).old?.parent !== undefined &&
6573
(action as Move).old?.element !== undefined &&
6674
(action as Move).new?.parent !== undefined &&
67-
(action as Update).new?.element == undefined
75+
(action as Replace).new?.element == undefined
6876
);
6977
}
70-
export function isUpdate(action: EditorAction): action is Update {
78+
export function isReplace(action: EditorAction): action is Replace {
7179
return (
7280
(action as Move).old?.parent === undefined &&
73-
(action as Update).old?.element !== undefined &&
81+
(action as Replace).old?.element !== undefined &&
7482
(action as Move).new?.parent === undefined &&
75-
(action as Update).new?.element !== undefined
83+
(action as Replace).new?.element !== undefined
84+
);
85+
}
86+
export function isUpdate(action: EditorAction): action is Update {
87+
return (
88+
(action as Replace).old === undefined &&
89+
(action as Replace).new === undefined &&
90+
(action as Update).element !== undefined &&
91+
(action as Update).newAttributes !== undefined &&
92+
(action as Update).oldAttributes !== undefined
7693
);
7794
}
7895
export function isSimple(action: EditorAction): action is SimpleAction {
@@ -109,10 +126,29 @@ export function invert(action: EditorAction): EditorAction {
109126
new: { parent: action.old.parent, reference: action.old.reference },
110127
...metaData,
111128
};
112-
else if (isUpdate(action))
129+
else if (isReplace(action))
113130
return { new: action.old, old: action.new, ...metaData };
131+
else if (isUpdate(action))
132+
return {
133+
element: action.element,
134+
oldAttributes: action.newAttributes,
135+
newAttributes: action.oldAttributes,
136+
...metaData,
137+
};
114138
else return unreachable('Unknown EditorAction type in invert.');
115139
}
140+
//** return `Update` action for `element` adding `oldAttributes` */
141+
export function createUpdateAction(
142+
element: Element,
143+
newAttributes: Record<string, string | null>
144+
): Update {
145+
const oldAttributes: Record<string, string | null> = {};
146+
Array.from(element.attributes).forEach(attr => {
147+
oldAttributes[attr.name] = attr.value;
148+
});
149+
150+
return { element, oldAttributes, newAttributes };
151+
}
116152

117153
/** Represents some intended modification of a `Document` being edited. */
118154
export interface EditorActionDetail<T extends EditorAction> {

src/translations/de.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const de: Translations = {
8787
smvcontrol: 'Sampled Values anzeigen',
8888
},
8989
editing: {
90+
node: 'Benutzerdefiniertes Objekt',
9091
created: '{{ name }} hinzugefügt',
9192
deleted: '{{ name }} entfernt',
9293
moved: '{{ name }} verschoben',

src/translations/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export const en = {
8585
smvcontrol: 'Show all Sampled Values',
8686
},
8787
editing: {
88+
node: 'User defined object',
8889
created: 'Added {{ name }}',
8990
deleted: 'Removed {{ name }}',
9091
moved: 'Moved {{ name }}',

src/wizards/address.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export function updateAddress(
9292
const oldAddress = parent.querySelector('Address');
9393

9494
if (oldAddress !== null && !isEqualAddress(oldAddress, newAddress)) {
95-
// We cannot use updateAction on address as both address child elements P are changed
95+
//address & child elements P are changed: cannot use replace editor action
9696
actions.push({
9797
old: {
9898
parent: parent,

0 commit comments

Comments
 (0)