@cloudcannon/editable-regions 0.0.3 → 0.0.5

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.
@@ -6,11 +6,12 @@ export { default as EditableImageComponent } from "./editable-image-component.js
6
6
  export { default as EditableSourceComponent } from "./editable-source-component.js";
7
7
  export { default as EditableSnippetComponent } from "./editable-snippet-component.js";
8
8
 
9
- import { loadedPromise } from "../helpers/cloudcannon.js";
9
+ import { apiLoadedPromise } from "../helpers/cloudcannon.js";
10
10
  import {
11
11
  dehydrateDataEditableRegions,
12
12
  hydrateDataEditableRegions,
13
13
  } from "../helpers/hydrate-editable-regions";
14
+ import { completeLoading } from "../helpers/loading.js";
14
15
 
15
16
  const observer = new MutationObserver((mutations) => {
16
17
  mutations.forEach((mutation) => {
@@ -28,8 +29,17 @@ const observer = new MutationObserver((mutations) => {
28
29
  });
29
30
  });
30
31
 
32
+ hydrateDataEditableRegions(document.body);
33
+
31
34
  observer.observe(document, { childList: true, subtree: true });
32
35
 
33
- loadedPromise.then(() => {
34
- hydrateDataEditableRegions(document.body);
35
- });
36
+ Promise.all([
37
+ apiLoadedPromise,
38
+ customElements.whenDefined("editable-array-item"),
39
+ customElements.whenDefined("editable-array"),
40
+ customElements.whenDefined("editable-text"),
41
+ customElements.whenDefined("editable-component"),
42
+ customElements.whenDefined("editable-image"),
43
+ customElements.whenDefined("editable-source"),
44
+ customElements.whenDefined("editable-snippet"),
45
+ ]).then(() => completeLoading());
@@ -11,11 +11,14 @@ export default class EditableArrayItemControls extends EditableComponentControls
11
11
  private _disableMoveBackward = false;
12
12
  private _disableRemove = false;
13
13
  private _disableReorder = false;
14
+ private _disableAdd = false;
14
15
 
15
16
  private moveForwardButton?: HTMLButtonElement;
16
17
  private moveBackwardButton?: HTMLButtonElement;
17
18
  private deleteButton?: HTMLButtonElement;
18
19
  private dragHandle?: HTMLButtonElement;
20
+ private duplicateButton?: HTMLButtonElement;
21
+ private addButton?: HTMLButtonElement;
19
22
 
20
23
  set disableMoveForward(value: boolean) {
21
24
  this._disableMoveForward = value;
@@ -37,7 +40,20 @@ export default class EditableArrayItemControls extends EditableComponentControls
37
40
  this.update();
38
41
  }
39
42
 
43
+ set disableAdd(value: boolean) {
44
+ this._disableAdd = value;
45
+ this.update();
46
+ }
47
+
40
48
  update() {
49
+ if (this.addButton) {
50
+ this.addButton.disabled = this._disableAdd;
51
+ }
52
+
53
+ if (this.duplicateButton) {
54
+ this.duplicateButton.disabled = this._disableAdd;
55
+ }
56
+
41
57
  if (this.dragHandle) {
42
58
  this.dragHandle.draggable = !this._disableReorder;
43
59
  this.dragHandle.innerHTML = `<cc-icon name="${this.dragHandle.draggable ? "drag_indicator" : "more_vert"}"></cc-icon>`;
@@ -58,6 +74,20 @@ export default class EditableArrayItemControls extends EditableComponentControls
58
74
  }
59
75
  }
60
76
 
77
+ addContextMenuButton(
78
+ icon: string,
79
+ text: string,
80
+ onClick: (e: PointerEvent) => void,
81
+ ): HTMLButtonElement {
82
+ const button = document.createElement("button");
83
+ button.onclick = onClick;
84
+ button.innerHTML = `<cc-icon name="${icon}"></cc-icon> ${text}`;
85
+ const buttonContainer = document.createElement("li");
86
+ buttonContainer.append(button);
87
+ this.contextMenu?.append(buttonContainer);
88
+ return button;
89
+ }
90
+
61
91
  render(shadow: ShadowRoot): void {
62
92
  super.render(shadow);
63
93
 
@@ -78,34 +108,41 @@ export default class EditableArrayItemControls extends EditableComponentControls
78
108
  };
79
109
  this.buttonRow?.append(this.dragHandle);
80
110
 
111
+ this.addButton = this.addContextMenuButton("add", "Add", (e) => {
112
+ this.dispatchEvent(
113
+ new CustomEvent("add", { detail: { originalEvent: e } }),
114
+ );
115
+ });
116
+
117
+ this.duplicateButton = this.addContextMenuButton(
118
+ "library_add",
119
+ "Duplicate",
120
+ () => {
121
+ this.dispatchEvent(new CustomEvent("duplicate", { detail: this }));
122
+ },
123
+ );
124
+
81
125
  const backwardIconName = this.moveBackwardText === "up" ? "north" : "west";
82
- this.moveBackwardButton = document.createElement("button");
83
- this.moveBackwardButton.innerHTML = `<cc-icon name="${backwardIconName}"></cc-icon> Move ${this.moveBackwardText}`;
84
- this.moveBackwardButton.onclick = () => {
85
- this.dispatchEvent(new CustomEvent("move-backward", { detail: this }));
86
- };
87
- const moveBackwardContainer = document.createElement("li");
88
- moveBackwardContainer.append(this.moveBackwardButton);
89
- this.contextMenu?.append(moveBackwardContainer);
126
+ this.moveBackwardButton = this.addContextMenuButton(
127
+ backwardIconName,
128
+ `Move ${this.moveBackwardText}`,
129
+ () => {
130
+ this.dispatchEvent(new CustomEvent("move-backward", { detail: this }));
131
+ },
132
+ );
90
133
 
91
134
  const forwardIconName = this.moveForwardText === "down" ? "south" : "east";
92
- this.moveForwardButton = document.createElement("button");
93
- this.moveForwardButton.innerHTML = `<cc-icon name="${forwardIconName}"></cc-icon> Move ${this.moveForwardText}`;
94
- this.moveForwardButton.onclick = (): void => {
95
- this.dispatchEvent(new CustomEvent("move-forward", { detail: this }));
96
- };
97
- const moveForwardContainer = document.createElement("li");
98
- moveForwardContainer.append(this.moveForwardButton);
99
- this.contextMenu?.append(moveForwardContainer);
100
-
101
- this.deleteButton = document.createElement("button");
102
- this.deleteButton.innerHTML = '<cc-icon name="delete"></cc-icon> Delete';
103
- this.deleteButton.onclick = () => {
135
+ this.moveForwardButton = this.addContextMenuButton(
136
+ forwardIconName,
137
+ `Move ${this.moveForwardText}`,
138
+ () => {
139
+ this.dispatchEvent(new CustomEvent("move-forward", { detail: this }));
140
+ },
141
+ );
142
+
143
+ this.deleteButton = this.addContextMenuButton("delete", "Delete", () => {
104
144
  this.dispatchEvent(new CustomEvent("delete", { detail: this }));
105
- };
106
- const deleteContainer = document.createElement("li");
107
- deleteContainer.append(this.deleteButton);
108
- this.contextMenu?.append(deleteContainer);
145
+ });
109
146
 
110
147
  this.update();
111
148
  }
package/helpers/checks.ts CHANGED
@@ -48,6 +48,14 @@ export const hasEditableArrayItem = <T extends object>(
48
48
  return "editable" in el && el.editable instanceof EditableArrayItem;
49
49
  };
50
50
 
51
+ export const isEditableWebcomponent = (el: unknown): boolean => {
52
+ if (!(el instanceof HTMLElement)) {
53
+ return false;
54
+ }
55
+
56
+ return TAG_NAMES.includes(el.tagName);
57
+ };
58
+
51
59
  export const isEditableElement = (el: unknown): boolean => {
52
60
  if (!(el instanceof HTMLElement)) {
53
61
  return false;
@@ -92,35 +100,3 @@ export const isEditableArrayItem = (el?: Element | null): boolean => {
92
100
  (el instanceof HTMLElement && el.dataset.editable === "array-item")
93
101
  );
94
102
  };
95
-
96
- export const areEqualNodes = (a: ChildNode, b: ChildNode) => {
97
- if (a.nodeName !== b.nodeName) {
98
- return false;
99
- }
100
-
101
- if (a instanceof Element && b instanceof Element) {
102
- if (a.className !== b.className) {
103
- return false;
104
- }
105
-
106
- if (a.id !== b.id) {
107
- return false;
108
- }
109
-
110
- if (a.attributes.length !== b.attributes.length) {
111
- return false;
112
- }
113
-
114
- for (let i = 0; i < a.attributes.length; i++) {
115
- if (
116
- a.attributes[i].name !== b.attributes[i].name ||
117
- a.attributes[i].value !== b.attributes[i].value
118
- ) {
119
- return false;
120
- }
121
- }
122
-
123
- return true;
124
- }
125
- return a.isEqualNode(b);
126
- };
@@ -14,7 +14,7 @@ declare const window: CloudCannonEditorWindow & {
14
14
 
15
15
  let _cloudcannon: CloudCannonJavaScriptV1API;
16
16
 
17
- const apiLoadedPromise = new Promise<void>((resolve) => {
17
+ export const apiLoadedPromise = new Promise<void>((resolve) => {
18
18
  if (window.CloudCannonAPI) {
19
19
  _cloudcannon = window.CloudCannonAPI.useVersion("v1", true) as any;
20
20
  resolve();
@@ -32,17 +32,6 @@ const apiLoadedPromise = new Promise<void>((resolve) => {
32
32
  }
33
33
  });
34
34
 
35
- export const loadedPromise = Promise.all([
36
- apiLoadedPromise,
37
- customElements.whenDefined("editable-array-item"),
38
- customElements.whenDefined("editable-array"),
39
- customElements.whenDefined("editable-text"),
40
- customElements.whenDefined("editable-component"),
41
- customElements.whenDefined("editable-image"),
42
- customElements.whenDefined("editable-source"),
43
- customElements.whenDefined("editable-snippet"),
44
- ]);
45
-
46
35
  export const addEditableComponentRenderer = (
47
36
  key: string,
48
37
  renderer: ComponentRenderer,
@@ -64,4 +53,22 @@ export const addEditableSnippetRenderer = (
64
53
  export const getEditableComponentRenderers = () => window.cc_components ?? {};
65
54
  export const getEditableSnippetRenderers = () => window.cc_snippets ?? {};
66
55
 
56
+ export const realizeAPIValue = async (value: unknown): Promise<unknown> => {
57
+ if (_cloudcannon.isAPICollection(value)) {
58
+ const items = await value.items();
59
+ return Promise.all(items.map(realizeAPIValue));
60
+ }
61
+ if (_cloudcannon.isAPIFile(value)) {
62
+ return value.data.get();
63
+ }
64
+ if (_cloudcannon.isAPIDataset(value)) {
65
+ const items = await value.items();
66
+ if (Array.isArray(items)) {
67
+ return Promise.all(items.map(realizeAPIValue));
68
+ }
69
+ return items.data.get();
70
+ }
71
+ return value;
72
+ };
73
+
67
74
  export { _cloudcannon as CloudCannon };
@@ -1,5 +1,5 @@
1
1
  import {
2
- type Editable,
2
+ Editable,
3
3
  EditableArray,
4
4
  EditableArrayItem,
5
5
  EditableComponent,
@@ -7,7 +7,7 @@ import {
7
7
  EditableSource,
8
8
  EditableText,
9
9
  } from "../nodes";
10
- import { hasEditable } from "./checks";
10
+ import { hasEditable, isEditableWebcomponent } from "./checks";
11
11
 
12
12
  const editableMap: Record<string, typeof Editable | undefined> = {
13
13
  array: EditableArray,
@@ -34,17 +34,21 @@ export const hydrateDataEditableRegions = (root: Element) => {
34
34
  if (
35
35
  root instanceof HTMLElement &&
36
36
  root.dataset.editable &&
37
- !("editable" in root)
37
+ !isEditableWebcomponent(root)
38
38
  ) {
39
- const Editable = editableMap[root.dataset.editable];
40
- if (Editable) {
41
- const editable = new Editable(root);
42
- editable.connect();
39
+ if ("editable" in root && root.editable instanceof Editable) {
40
+ root.editable.connect();
41
+ } else {
42
+ const Editable = editableMap[root.dataset.editable];
43
+ if (Editable) {
44
+ const editable = new Editable(root);
45
+ editable.connect();
46
+ }
43
47
  }
44
48
  }
45
49
 
46
50
  root.querySelectorAll("[data-editable]").forEach((element) => {
47
- if (!(element instanceof HTMLElement) || "editable" in element) {
51
+ if (!(element instanceof HTMLElement) || isEditableWebcomponent(element)) {
48
52
  return;
49
53
  }
50
54
 
@@ -64,8 +68,11 @@ export const hydrateDataEditableRegions = (root: Element) => {
64
68
  return;
65
69
  }
66
70
 
67
- const editable = new Editable(element);
68
-
69
- editable.connect();
71
+ if ("editable" in element && element.editable instanceof Editable) {
72
+ element.editable.connect();
73
+ } else {
74
+ const editable = new Editable(element);
75
+ editable.connect();
76
+ }
70
77
  });
71
78
  };
@@ -0,0 +1,7 @@
1
+ let completeLoading: () => void;
2
+
3
+ export const loadingPromise = new Promise<void>((resolve) => {
4
+ completeLoading = resolve;
5
+ });
6
+
7
+ export { completeLoading };
@@ -1,4 +1,5 @@
1
1
  import { dirname, join } from "node:path";
2
+ import { fileURLToPath } from "node:url";
2
3
 
3
4
  const SUPPORTED_VIRTUAL_MODULES = [
4
5
  "actions",
@@ -57,15 +58,10 @@ export default () => {
57
58
  if (typeof __dirname !== "undefined") {
58
59
  dir = __dirname;
59
60
  } else {
60
- dir = dirname(import.meta.url);
61
+ dir = dirname(fileURLToPath(import.meta.url));
61
62
  }
62
63
 
63
- const path = join(dir, "modules", `${type}.js`).replace(
64
- "file:",
65
- "",
66
- );
67
-
68
- return path;
64
+ return join(dir, "modules", `${type}.js`);
69
65
  }
70
66
  },
71
67
 
@@ -127,7 +127,7 @@ export const registerAstroComponent = (key, component) => {
127
127
  ["visible", "editable-region-placeholder"],
128
128
  ["media", "editable-region-placeholder"],
129
129
  ]),
130
- slots: null,
130
+ slots: {},
131
131
  props,
132
132
  resolve: () => "editable-region-placeholder",
133
133
  /**
@@ -162,7 +162,7 @@ export const registerAstroComponent = (key, component) => {
162
162
  },
163
163
  };
164
164
  // Render the Astro component to HTML string
165
- const result = await renderToString(SSRResult, component, props, null);
165
+ const result = await renderToString(SSRResult, component, props, {});
166
166
  const doc = document.implementation.createHTMLDocument();
167
167
  doc.body.innerHTML = result;
168
168
 
@@ -4,7 +4,7 @@ import {
4
4
  hasEditableArrayItem,
5
5
  isEditableArrayItem,
6
6
  } from "../helpers/checks.js";
7
- import { CloudCannon } from "../helpers/cloudcannon.js";
7
+ import { CloudCannon, realizeAPIValue } from "../helpers/cloudcannon.js";
8
8
  import EditableArray from "./editable-array.js";
9
9
  import EditableComponent from "./editable-component.js";
10
10
 
@@ -15,6 +15,10 @@ export default class EditableArrayItem extends EditableComponent {
15
15
 
16
16
  private inputConfig?: any;
17
17
 
18
+ shouldMount(): boolean {
19
+ return this.value !== undefined;
20
+ }
21
+
18
22
  validateConfiguration(): boolean {
19
23
  const key = this.element.dataset.component;
20
24
  if (key) {
@@ -44,25 +48,36 @@ export default class EditableArrayItem extends EditableComponent {
44
48
  return true;
45
49
  }
46
50
 
47
- onHover(e: DragEvent): void {
48
- const source = this.parent?.resolveSource();
51
+ isValidDropzone(e: DragEvent): boolean {
52
+ const source = this.parent?.contextBase?.filePath;
49
53
  if (!source || !e.dataTransfer) {
50
- return;
54
+ return false;
51
55
  }
52
56
 
57
+ const dragType = this.getDragType();
58
+
53
59
  if (
54
- !e.dataTransfer?.types.includes(source) &&
55
- !e.dataTransfer.types.includes(this.getDragType())
60
+ !e.dataTransfer?.types.includes(source.toLowerCase()) &&
61
+ (!dragType || !e.dataTransfer.types.includes(dragType))
56
62
  ) {
63
+ return false;
64
+ }
65
+
66
+ return true;
67
+ }
68
+
69
+ onHover(e: DragEvent): void {
70
+ if (!this.isValidDropzone(e)) {
57
71
  return;
58
72
  }
59
73
 
60
74
  e.preventDefault();
75
+ e.stopPropagation();
61
76
  this.element.classList.add("dragover");
62
77
  this.element.style.boxShadow = this.getDraggingBoxShadow(e);
63
78
  }
64
79
 
65
- getDragType(): string {
80
+ getDragType(): string | undefined {
66
81
  if (this.inputConfig?.options?.structures?.values?.length) {
67
82
  return "cc:structure";
68
83
  }
@@ -72,7 +87,14 @@ export default class EditableArrayItem extends EditableComponent {
72
87
  return `cc:${currentArraySubtype}`;
73
88
  }
74
89
 
75
- const type = CloudCannon.getInputType(this.resolveSource(), this.value);
90
+ const type = CloudCannon.getInputType(
91
+ this.contextBase?.filePath,
92
+ this.value,
93
+ this.inputConfig,
94
+ );
95
+ if (type === "array" || type === "object") {
96
+ return undefined;
97
+ }
76
98
  return `cc:${type}`;
77
99
  }
78
100
 
@@ -127,12 +149,13 @@ export default class EditableArrayItem extends EditableComponent {
127
149
  return isBefore ? "before" : "after";
128
150
  }
129
151
 
130
- dispatchArrayMove(fromIndex: number, toIndex: number) {
152
+ dispatchArrayMove(fromIndex: number, toIndex: number, fromSlug?: string) {
131
153
  this.element.dispatchEvent(
132
154
  new CustomEvent("cloudcannon-api", {
133
155
  bubbles: true,
134
156
  detail: {
135
157
  action: "move-array-item",
158
+ fromSlug,
136
159
  fromIndex,
137
160
  toIndex,
138
161
  },
@@ -153,13 +176,14 @@ export default class EditableArrayItem extends EditableComponent {
153
176
  );
154
177
  }
155
178
 
156
- dispatchArrayAdd(newIndex: number, value: unknown) {
179
+ dispatchArrayAdd(newIndex: number, value: unknown, sourceIndex?: number) {
157
180
  this.element.dispatchEvent(
158
181
  new CustomEvent("cloudcannon-api", {
159
182
  bubbles: true,
160
183
  detail: {
161
184
  action: "add-array-item",
162
185
  newIndex,
186
+ sourceIndex,
163
187
  value,
164
188
  },
165
189
  }),
@@ -213,6 +237,34 @@ export default class EditableArrayItem extends EditableComponent {
213
237
  this.dispatchEdit(this.element.dataset.prop);
214
238
  });
215
239
 
240
+ this.controlsElement.addEventListener("add", () => {
241
+ const fromIndex = Number(this.element.dataset.prop);
242
+ const arrayDirection = this.parent?.arrayDirection ?? "column";
243
+ const reversed = arrayDirection.endsWith("reverse");
244
+
245
+ this.dispatchArrayAdd(
246
+ reversed ? fromIndex - 1 : fromIndex + 1,
247
+ undefined,
248
+ fromIndex,
249
+ );
250
+ });
251
+
252
+ this.controlsElement.addEventListener("duplicate", async () => {
253
+ const value = await realizeAPIValue(this.value);
254
+ const fromIndex = Number(this.element.dataset.prop);
255
+ const arrayDirection = this.parent?.arrayDirection ?? "column";
256
+ const reversed = arrayDirection.endsWith("reverse");
257
+
258
+ this.dispatchArrayAdd(
259
+ reversed ? fromIndex - 1 : fromIndex + 1,
260
+ value,
261
+ fromIndex,
262
+ );
263
+
264
+ this.controlsElement?.removeAttribute("open");
265
+ this.element.after(this.element.cloneNode(true));
266
+ });
267
+
216
268
  this.controlsElement.addEventListener("move-backward", () => {
217
269
  const fromIndex = Number(this.element.dataset.prop);
218
270
  const arrayDirection = this.parent?.arrayDirection ?? "column";
@@ -249,7 +301,7 @@ export default class EditableArrayItem extends EditableComponent {
249
301
  });
250
302
 
251
303
  this.controlsElement.addEventListener("dragstart", (e: DragEvent) => {
252
- const source = this.parent?.resolveSource();
304
+ const source = this.parent?.contextBase?.filePath;
253
305
  if (!source || !e.dataTransfer || !this.element.dataset.prop) {
254
306
  return;
255
307
  }
@@ -261,7 +313,6 @@ export default class EditableArrayItem extends EditableComponent {
261
313
 
262
314
  e.dataTransfer.setDragImage(this.element, clientRect.width - 35, 35);
263
315
  e.dataTransfer.effectAllowed = "move";
264
- e.dataTransfer?.setData(source, this.element.dataset.prop);
265
316
 
266
317
  const id = Math.random().toString(36).slice(2);
267
318
  this.element.id = id;
@@ -279,7 +330,12 @@ export default class EditableArrayItem extends EditableComponent {
279
330
  );
280
331
  }
281
332
 
282
- e.dataTransfer?.setData(this.getDragType(), JSON.stringify(data));
333
+ const payload = JSON.stringify(data);
334
+ e.dataTransfer?.setData(source, payload);
335
+ const dragType = this.getDragType();
336
+ if (dragType) {
337
+ e.dataTransfer?.setData(dragType, payload);
338
+ }
283
339
  });
284
340
 
285
341
  this.updateControls();
@@ -298,6 +354,8 @@ export default class EditableArrayItem extends EditableComponent {
298
354
  (inputConfig as any)?.options?.disable_reorder ?? false;
299
355
  this.controlsElement.disableRemove =
300
356
  (inputConfig as any)?.options?.disable_remove ?? false;
357
+ this.controlsElement.disableAdd =
358
+ (inputConfig as any)?.options?.disable_add ?? false;
301
359
 
302
360
  this.inputConfig = inputConfig;
303
361
  this.element.append(this.controlsElement);
@@ -313,6 +371,9 @@ export default class EditableArrayItem extends EditableComponent {
313
371
  this.element.ondragover = this.onHover.bind(this);
314
372
 
315
373
  this.element.ondragleave = (e: DragEvent): void => {
374
+ if (!this.isValidDropzone(e)) {
375
+ return;
376
+ }
316
377
  e.stopPropagation();
317
378
 
318
379
  this.element.classList.remove("dragover");
@@ -320,6 +381,10 @@ export default class EditableArrayItem extends EditableComponent {
320
381
  };
321
382
 
322
383
  this.element.ondrop = (e: DragEvent): void => {
384
+ if (!this.isValidDropzone(e)) {
385
+ return;
386
+ }
387
+
323
388
  this.element.classList.remove("dragover");
324
389
  this.element.style.boxShadow = "";
325
390
 
@@ -327,14 +392,16 @@ export default class EditableArrayItem extends EditableComponent {
327
392
  return;
328
393
  }
329
394
 
330
- const source = this.parent?.resolveSource();
395
+ const source = this.parent?.contextBase?.filePath;
331
396
  if (!source) {
332
397
  throw new Error("Source not found");
333
398
  }
334
399
 
335
400
  const dragType = this.getDragType();
336
401
  const sameArrayData = e.dataTransfer.getData(source);
337
- const otherArrayData = e.dataTransfer.getData(dragType);
402
+ const otherArrayData = dragType
403
+ ? e.dataTransfer.getData(dragType)
404
+ : undefined;
338
405
 
339
406
  const position = this.getDragPosition(e);
340
407
  let newIndex =
@@ -343,20 +410,14 @@ export default class EditableArrayItem extends EditableComponent {
343
410
  : Number(this.element.dataset.prop);
344
411
 
345
412
  if (sameArrayData) {
346
- const fromIndex = Number(sameArrayData);
413
+ const { index: fromIndex, sourceId } = JSON.parse(sameArrayData);
414
+
347
415
  if (fromIndex < newIndex) {
348
416
  newIndex -= 1;
349
417
  }
350
418
 
351
- e.preventDefault();
352
- e.stopPropagation();
353
- e.dataTransfer.dropEffect = "move";
354
-
355
419
  if (fromIndex !== newIndex) {
356
- this.dispatchArrayMove(fromIndex, newIndex);
357
- const sourceElement = this.parent?.element.querySelector(
358
- `[data-prop="${fromIndex}"]`,
359
- );
420
+ const sourceElement = document.getElementById(sourceId);
360
421
  if (sourceElement) {
361
422
  if (position === "after") {
362
423
  this.element.after(sourceElement);
@@ -364,13 +425,14 @@ export default class EditableArrayItem extends EditableComponent {
364
425
  this.element.before(sourceElement);
365
426
  }
366
427
  }
428
+ this.dispatchArrayMove(fromIndex, newIndex);
367
429
  }
368
430
  } else if (otherArrayData) {
369
431
  const { index, sourceId, value, structure } =
370
432
  JSON.parse(otherArrayData);
371
433
  if (dragType === "cc:structure") {
372
434
  if (!this.inputConfig?.options?.structures?.values) {
373
- throw new Error("No structures found");
435
+ return;
374
436
  }
375
437
 
376
438
  const targetStructure = CloudCannon.findStructure(
@@ -378,41 +440,35 @@ export default class EditableArrayItem extends EditableComponent {
378
440
  this.value,
379
441
  );
380
442
  if (!targetStructure) {
381
- throw new Error("No target structure found");
443
+ return;
382
444
  }
383
445
 
384
446
  if (JSON.stringify(structure) !== JSON.stringify(targetStructure)) {
385
- throw new Error("Structures do not match");
447
+ return;
386
448
  }
387
449
  }
388
450
 
389
451
  const sourceElement = document.getElementById(sourceId);
390
452
  if (sourceElement && hasEditableArrayItem(sourceElement)) {
391
- if (Array.isArray(sourceElement.editable.parent?.value)) {
392
- sourceElement.editable.parent.value = structuredClone(
393
- sourceElement.editable.parent.value,
394
- );
395
- sourceElement.editable.parent.value.splice(index, 1);
453
+ const parentValue = this.parent?.value;
454
+ if (Array.isArray(parentValue)) {
455
+ parentValue.splice(newIndex, 0, value);
396
456
  }
397
- sourceElement.editable.dispatchArrayRemove(index);
457
+ sourceElement.dataset.prop = `${newIndex}`;
458
+ const fromSlug = sourceElement.editable.parent?.contextBase?.fullPath;
398
459
  if (position === "after") {
399
460
  this.element.after(sourceElement);
400
461
  } else {
401
462
  this.element.before(sourceElement);
402
463
  }
403
- sourceElement.editable.parent?.update();
404
- }
405
- if (Array.isArray(this.parent?.value)) {
406
- this.parent.value = structuredClone(this.parent.value);
407
- this.parent.value.splice(newIndex, 0, value);
408
- }
409
- this.dispatchArrayAdd(newIndex, value);
410
- this.parent?.update();
411
464
 
412
- e.preventDefault();
413
- e.stopPropagation();
414
- e.dataTransfer.dropEffect = "move";
465
+ this.dispatchArrayMove(index, newIndex, fromSlug);
466
+ }
415
467
  }
468
+
469
+ e.preventDefault();
470
+ e.stopPropagation();
471
+ e.dataTransfer.dropEffect = "move";
416
472
  };
417
473
 
418
474
  if (this.value !== undefined) {
@@ -422,6 +478,10 @@ export default class EditableArrayItem extends EditableComponent {
422
478
 
423
479
  setupListeners(): void {
424
480
  super.setupListeners();
425
- this.parent?.registerListener({ editable: this });
481
+ if (!this.element.dataset.prop) {
482
+ this.parent?.registerListener({
483
+ editable: this,
484
+ });
485
+ }
426
486
  }
427
487
  }