@jbpark/live-editor 1.0.3 → 1.1.0

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.
package/dist/index.mjs CHANGED
@@ -23,7 +23,7 @@ import prettierPluginTypeScript from "prettier/plugins/typescript";
23
23
  import Placeholder from "@tiptap/extension-placeholder";
24
24
  import { EditorContent, useEditor } from "@tiptap/react";
25
25
  import StarterKit from "@tiptap/starter-kit";
26
- import { useDebounce } from "@jbpark/use-hooks";
26
+ import { useDebounce, useMultiSelect } from "@jbpark/use-hooks";
27
27
 
28
28
  //#region src/components/context/states.ts
29
29
  const PreviewContext = createContext({
@@ -11435,207 +11435,81 @@ const Tiptap = ({ value = "", placeholder = "Enter text...", className, onChange
11435
11435
  };
11436
11436
 
11437
11437
  //#endregion
11438
- //#region src/components/dnd/panel/children.tsx
11439
- const Children = ({ value, onChange, onNodeChange }) => {
11440
- const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
11441
- const editableChildrenMap = useMemo(() => {
11442
- const map = /* @__PURE__ */ new Map();
11443
- items.forEach((item, index) => {
11444
- const editableNodes = findEditableChildren(item);
11445
- if (editableNodes.length > 0) map.set(index, editableNodes);
11446
- });
11447
- return map;
11448
- }, [items]);
11449
- const moveItem = (fromIndex, toIndex) => {
11450
- const nextItems = [...items];
11451
- const [movedItem] = nextItems.splice(fromIndex, 1);
11452
- nextItems.splice(toIndex, 0, movedItem);
11453
- onChange?.(JSON.stringify(nextItems));
11454
- };
11455
- const deleteItem = (index) => {
11456
- if (items.length <= 1) return;
11457
- const nextItems = items.filter((_, i) => i !== index);
11458
- onChange?.(JSON.stringify(nextItems));
11459
- };
11460
- const addItem = () => {
11461
- const newItem = cloneDataAttrNode(items[0] || createDefaultItem());
11462
- const nextItems = [...items, newItem];
11463
- onChange?.(JSON.stringify(nextItems));
11438
+ //#region src/components/dnd/panel/selection.ts
11439
+ var import_lib$1 = /* @__PURE__ */ __toESM(require_lib$1());
11440
+ var import_lib = require_lib();
11441
+ const removeIndices = (items, indices) => {
11442
+ return items.filter((_, index) => !indices.has(index));
11443
+ };
11444
+ /**
11445
+ * Shifts every selected index up/down by one step as a block, preserving
11446
+ * relative order — scattered selections stop moving individually once they
11447
+ * hit an unselected neighbor, so the whole group slides together instead of
11448
+ * items passing through each other.
11449
+ */
11450
+ const moveSelectedIndices = (items, indices, direction) => {
11451
+ const next = [...items];
11452
+ const nextIndices = new Set(indices);
11453
+ const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
11454
+ for (const index of ordered) {
11455
+ const target = direction === "up" ? index - 1 : index + 1;
11456
+ if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
11457
+ [next[index], next[target]] = [next[target], next[index]];
11458
+ nextIndices.delete(index);
11459
+ nextIndices.add(target);
11460
+ }
11461
+ return {
11462
+ items: next,
11463
+ indices: nextIndices
11464
11464
  };
11465
- const createDefaultItem = () => ({
11466
- id: nanoid(6),
11467
- tagName: "div",
11468
- attributes: [{
11469
- name: "data-id",
11470
- value: nanoid(6)
11471
- }, {
11472
- name: "data-item",
11473
- value: "true"
11474
- }],
11475
- dataAttributes: [{
11476
- name: "data-id",
11477
- value: nanoid(6)
11478
- }, {
11479
- name: "data-item",
11480
- value: "true"
11481
- }],
11482
- textContent: "",
11483
- children: []
11484
- });
11485
- const cloneDataAttrNode = (node) => ({
11486
- ...node,
11487
- id: nanoid(6),
11488
- attributes: node.attributes.map((attr) => ({
11489
- ...attr,
11490
- value: attr.name === "data-id" ? nanoid(6) : attr.value
11491
- })),
11492
- dataAttributes: node.dataAttributes.map((attr) => ({
11493
- ...attr,
11494
- value: attr.name === "data-id" ? nanoid(6) : attr.value
11495
- })),
11496
- children: node.children?.map(cloneDataAttrNode)
11497
- });
11465
+ };
11466
+
11467
+ //#endregion
11468
+ //#region src/components/dnd/panel/items.tsx
11469
+ const BulkActionsBar = ({ count, onDuplicate, onMoveUp, onMoveDown, onDelete, onClear }) => {
11470
+ if (count === 0) return null;
11498
11471
  return /* @__PURE__ */ jsxs("div", {
11499
- className: "space-y-3",
11472
+ className: "flex items-center justify-between rounded border\n border-blue-200 bg-blue-50 p-2",
11500
11473
  children: [/* @__PURE__ */ jsxs("div", {
11501
- className: "flex items-center justify-between",
11502
- children: [/* @__PURE__ */ jsxs("div", {
11503
- className: "text-sm font-semibold text-green-700",
11504
- children: [
11505
- "Children Items (",
11506
- items.length,
11507
- ")"
11508
- ]
11509
- }), /* @__PURE__ */ jsx(Button, {
11510
- size: "small",
11511
- color: "green",
11512
- icon: /* @__PURE__ */ jsx(Plus, {}),
11513
- onClick: addItem,
11514
- children: "Add Child"
11515
- })]
11516
- }), items.map((item, itemIndex) => /* @__PURE__ */ jsxs("div", {
11517
- className: "space-y-2 rounded border border-green-200 bg-green-50 p-3",
11474
+ className: "text-xs font-medium text-blue-700",
11475
+ children: [count, " selected"]
11476
+ }), /* @__PURE__ */ jsxs("div", {
11477
+ className: "flex items-center space-x-1",
11518
11478
  children: [
11519
- /* @__PURE__ */ jsxs("div", {
11520
- className: "flex items-center justify-between",
11521
- children: [/* @__PURE__ */ jsxs("div", {
11522
- className: "text-xs font-medium text-green-800",
11523
- children: [
11524
- "Child ",
11525
- itemIndex + 1,
11526
- " (",
11527
- item.tagName || "fragment",
11528
- ")"
11529
- ]
11530
- }), /* @__PURE__ */ jsxs("div", {
11531
- className: "flex space-x-1",
11532
- children: [
11533
- /* @__PURE__ */ jsx(Button, {
11534
- size: "small",
11535
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
11536
- disabled: itemIndex === 0,
11537
- onClick: () => moveItem(itemIndex, itemIndex - 1)
11538
- }),
11539
- /* @__PURE__ */ jsx(Button, {
11540
- size: "small",
11541
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
11542
- disabled: itemIndex === items.length - 1,
11543
- onClick: () => moveItem(itemIndex, itemIndex + 1)
11544
- }),
11545
- /* @__PURE__ */ jsx(Button, {
11546
- title: items.length <= 1 ? "At least 1 item is required" : "Delete item",
11547
- danger: true,
11548
- size: "small",
11549
- icon: /* @__PURE__ */ jsx(X, {}),
11550
- disabled: items.length <= 1,
11551
- onClick: () => deleteItem(itemIndex)
11552
- })
11553
- ]
11554
- })]
11479
+ /* @__PURE__ */ jsx(Button, {
11480
+ size: "small",
11481
+ icon: /* @__PURE__ */ jsx(Copy, {}),
11482
+ title: "Duplicate selected",
11483
+ onClick: onDuplicate
11555
11484
  }),
11556
- editableChildrenMap.has(itemIndex) && /* @__PURE__ */ jsxs("div", {
11557
- className: "space-y-2",
11558
- children: [/* @__PURE__ */ jsx("div", {
11559
- className: "text-xs font-medium text-green-700",
11560
- children: "Editable Bindings:"
11561
- }), editableChildrenMap.get(itemIndex).map((editableNode, idx) => {
11562
- const nodeId = editableNode.dataAttributes.find((a) => a.name === "data-id")?.value;
11563
- return /* @__PURE__ */ jsxs("div", {
11564
- className: "rounded border border-green-100 bg-white p-2",
11565
- children: [/* @__PURE__ */ jsx("div", {
11566
- className: "mb-1 text-xs text-green-600",
11567
- children: editableNode.tagName
11568
- }), /* @__PURE__ */ jsx(Node, {
11569
- data: editableNode,
11570
- onChange: onNodeChange
11571
- })]
11572
- }, `editable-${itemIndex}-${nodeId || idx}`);
11573
- })]
11485
+ /* @__PURE__ */ jsx(Button, {
11486
+ size: "small",
11487
+ icon: /* @__PURE__ */ jsx(ArrowUp, {}),
11488
+ title: "Move selected up",
11489
+ onClick: onMoveUp
11574
11490
  }),
11575
- !!item.children && !editableChildrenMap.has(itemIndex) && /* @__PURE__ */ jsxs("div", {
11576
- className: "space-y-1",
11577
- children: [/* @__PURE__ */ jsx("div", {
11578
- className: "text-xs font-medium text-green-700",
11579
- children: "Child Nodes:"
11580
- }), item.children.map((node, nodeIndex) => /* @__PURE__ */ jsx("div", {
11581
- className: "ml-2 rounded border border-green-100 bg-white p-2",
11582
- children: /* @__PURE__ */ jsx(Node, {
11583
- data: node,
11584
- onChange: onNodeChange
11585
- })
11586
- }, `node-${itemIndex}-${nodeIndex}`))]
11491
+ /* @__PURE__ */ jsx(Button, {
11492
+ size: "small",
11493
+ icon: /* @__PURE__ */ jsx(ArrowDown, {}),
11494
+ title: "Move selected down",
11495
+ onClick: onMoveDown
11496
+ }),
11497
+ /* @__PURE__ */ jsx(Button, {
11498
+ danger: true,
11499
+ size: "small",
11500
+ icon: /* @__PURE__ */ jsx(X, {}),
11501
+ title: "Delete selected",
11502
+ onClick: onDelete
11503
+ }),
11504
+ /* @__PURE__ */ jsx(Button, {
11505
+ size: "small",
11506
+ onClick: onClear,
11507
+ children: "Clear"
11587
11508
  })
11588
11509
  ]
11589
- }, item.id || itemIndex))]
11510
+ })]
11590
11511
  });
11591
11512
  };
11592
-
11593
- //#endregion
11594
- //#region src/components/dnd/panel/icon-map.ts
11595
- const ICON_MAP = {
11596
- AlertCircle,
11597
- ArrowDown,
11598
- ArrowLeft,
11599
- ArrowRight,
11600
- ArrowUp,
11601
- Bell,
11602
- Bookmark,
11603
- Calendar,
11604
- Camera,
11605
- Check,
11606
- ChevronDown,
11607
- ChevronRight,
11608
- Clock,
11609
- Download,
11610
- Edit,
11611
- Eye,
11612
- Filter,
11613
- Heart,
11614
- Home,
11615
- Image,
11616
- Info,
11617
- Link,
11618
- Mail,
11619
- Map: Map$1,
11620
- Menu,
11621
- MessageCircle,
11622
- Phone,
11623
- Plus,
11624
- Search,
11625
- Settings,
11626
- Share,
11627
- ShoppingCart,
11628
- Star,
11629
- Trash,
11630
- Upload: Upload$1,
11631
- User,
11632
- X
11633
- };
11634
-
11635
- //#endregion
11636
- //#region src/components/dnd/panel/items.tsx
11637
- var import_lib = require_lib();
11638
- var import_lib$1 = /* @__PURE__ */ __toESM(require_lib$1());
11639
11513
  const Items = ({ value, render, onChange, onChildChange }) => {
11640
11514
  const { objectItems, primitiveItems, allElements } = useMemo(() => {
11641
11515
  const ast = parseArrayExpression(value);
@@ -11694,6 +11568,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
11694
11568
  };
11695
11569
  }, [value]);
11696
11570
  const isPrimitive = primitiveItems.length > 0 && objectItems.length === 0;
11571
+ const selection = useMultiSelect(isPrimitive ? primitiveItems.length : objectItems.length);
11697
11572
  const updatePrimitive = (index, next) => {
11698
11573
  const ast = parseArrayExpression(value);
11699
11574
  if (!ast) return;
@@ -11711,11 +11586,12 @@ const Items = ({ value, render, onChange, onChildChange }) => {
11711
11586
  nextElements.splice(toIndex, 0, moved);
11712
11587
  onChange?.(generateCode(import_lib$1.arrayExpression(nextElements)));
11713
11588
  };
11714
- const deletePrimitive = (index) => {
11715
- if (allElements.length <= 1) return;
11716
- const nextElements = allElements.filter((_, i) => i !== index);
11589
+ const deleteSelectedPrimitives = (indices) => {
11590
+ if (allElements.length - indices.size < 1) return;
11591
+ const nextElements = removeIndices(allElements, indices);
11717
11592
  onChange?.(generateCode(import_lib$1.arrayExpression(nextElements)));
11718
11593
  };
11594
+ const deletePrimitive = (index) => deleteSelectedPrimitives(new Set([index]));
11719
11595
  const addPrimitive = () => {
11720
11596
  const first = primitiveItems[0];
11721
11597
  if (!first) return;
@@ -11723,6 +11599,16 @@ const Items = ({ value, render, onChange, onChildChange }) => {
11723
11599
  if (!newNode) return;
11724
11600
  onChange?.(generateCode(import_lib$1.arrayExpression([...allElements, newNode])));
11725
11601
  };
11602
+ const duplicateSelectedPrimitives = (indices) => {
11603
+ const clones = [...indices].sort((a, b) => a - b).map((index) => allElements[index]).filter((node) => Boolean(node)).map((node) => clone(node));
11604
+ if (clones.length === 0) return;
11605
+ onChange?.(generateCode(import_lib$1.arrayExpression([...allElements, ...clones])));
11606
+ };
11607
+ const moveSelectedPrimitives = (indices, direction) => {
11608
+ const { items: nextElements, indices: nextIndices } = moveSelectedIndices(allElements, indices, direction);
11609
+ selection.replace(nextIndices);
11610
+ onChange?.(generateCode(import_lib$1.arrayExpression(nextElements)));
11611
+ };
11726
11612
  const moveItem = (fromIndex, toIndex) => {
11727
11613
  const nextItems = [...objectItems];
11728
11614
  const [movedItem] = nextItems.splice(fromIndex, 1);
@@ -11777,18 +11663,18 @@ const Items = ({ value, render, onChange, onChildChange }) => {
11777
11663
  for (const [placeholder, code] of jsxPlaceholders) nextValue = nextValue.replace(placeholder, () => code);
11778
11664
  onChange?.(nextValue);
11779
11665
  };
11780
- const deleteItem = (index) => {
11781
- if (objectItems.length <= 1) return;
11782
- const nextValue = arrayExpressionToCode(objectItems.filter((_, i) => i !== index).map((item) => item.originalElement));
11666
+ const deleteSelectedItems = (indices) => {
11667
+ if (objectItems.length - indices.size < 1) return;
11668
+ const nextValue = arrayExpressionToCode(removeIndices(objectItems, indices).map((item) => item.originalElement));
11783
11669
  onChange?.(nextValue);
11784
11670
  };
11785
- const addItem = () => {
11786
- const firstItem = objectItems[0];
11787
- const clonedElement = clone(firstItem.originalElement);
11671
+ const deleteItem = (index) => deleteSelectedItems(new Set([index]));
11672
+ const cloneObjectItemElement = (source) => {
11673
+ const clonedElement = clone(source.originalElement);
11788
11674
  clonedElement.properties.forEach((prop) => {
11789
11675
  if (import_lib$1.isObjectProperty(prop) && import_lib$1.isIdentifier(prop.key)) {
11790
11676
  const key = prop.key.name;
11791
- const editableProp = firstItem.editableProperties[key];
11677
+ const editableProp = source.editableProperties[key];
11792
11678
  if (key === "key" && import_lib$1.isStringLiteral(prop.value)) {
11793
11679
  const uniqueKey = `${prop.value.value}-${nanoid(6)}`;
11794
11680
  prop.value = import_lib$1.stringLiteral(uniqueKey);
@@ -11800,6 +11686,11 @@ const Items = ({ value, render, onChange, onChildChange }) => {
11800
11686
  }
11801
11687
  }
11802
11688
  });
11689
+ return clonedElement;
11690
+ };
11691
+ const addItem = () => {
11692
+ const firstItem = objectItems[0];
11693
+ const clonedElement = cloneObjectItemElement(firstItem);
11803
11694
  const editableProperties = extractObjectProperties(clonedElement);
11804
11695
  const nextItems = [...objectItems];
11805
11696
  const newItemData = {
@@ -11813,179 +11704,475 @@ const Items = ({ value, render, onChange, onChildChange }) => {
11813
11704
  const nextValue = arrayExpressionToCode(nextItems.map((item) => item.originalElement));
11814
11705
  onChange?.(nextValue);
11815
11706
  };
11707
+ const duplicateSelectedItems = (indices) => {
11708
+ const sources = [...indices].sort((a, b) => a - b).map((index) => objectItems[index]).filter((item) => Boolean(item));
11709
+ if (sources.length === 0) return;
11710
+ const clonedElements = sources.map(cloneObjectItemElement);
11711
+ const nextValue = arrayExpressionToCode([...objectItems.map((item) => item.originalElement), ...clonedElements]);
11712
+ onChange?.(nextValue);
11713
+ };
11714
+ const moveSelectedItems = (indices, direction) => {
11715
+ const { items: nextItems, indices: nextIndices } = moveSelectedIndices(objectItems, indices, direction);
11716
+ selection.replace(nextIndices);
11717
+ const nextValue = arrayExpressionToCode(nextItems.map((item) => item.originalElement));
11718
+ onChange?.(nextValue);
11719
+ };
11816
11720
  if (isPrimitive) return /* @__PURE__ */ jsxs("div", {
11817
11721
  className: "space-y-4",
11818
- children: [/* @__PURE__ */ jsxs("div", {
11819
- className: "flex items-center justify-between",
11820
- children: [/* @__PURE__ */ jsxs("div", {
11821
- className: "text-sm font-semibold",
11822
- children: [
11823
- "Items (",
11824
- primitiveItems.length,
11825
- ")"
11826
- ]
11827
- }), /* @__PURE__ */ jsx(Button, {
11828
- size: "small",
11829
- icon: /* @__PURE__ */ jsx(Plus, {}),
11830
- variant: "solid",
11831
- color: "green",
11832
- onClick: addPrimitive,
11833
- children: "Add Item"
11834
- })]
11835
- }), primitiveItems.map((item, i) => /* @__PURE__ */ jsxs("div", {
11836
- className: "space-y-2 rounded border border-gray-100 bg-gray-50 p-2",
11837
- children: [/* @__PURE__ */ jsxs("div", {
11838
- className: "flex justify-end space-x-1",
11839
- children: [
11840
- /* @__PURE__ */ jsx(Button, {
11841
- size: "small",
11842
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
11843
- disabled: i === 0,
11844
- onClick: () => movePrimitive(item.index, item.index - 1)
11845
- }),
11846
- /* @__PURE__ */ jsx(Button, {
11847
- size: "small",
11848
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
11849
- disabled: i === primitiveItems.length - 1,
11850
- onClick: () => movePrimitive(item.index, item.index + 1)
11851
- }),
11852
- /* @__PURE__ */ jsx(Button, {
11853
- danger: true,
11854
- size: "small",
11855
- icon: /* @__PURE__ */ jsx(X, {}),
11856
- disabled: primitiveItems.length <= 1,
11857
- onClick: () => deletePrimitive(item.index)
11858
- })
11859
- ]
11860
- }), /* @__PURE__ */ jsx(Field, {
11861
- binding: {
11862
- label: `item-${i}`,
11863
- property: item.type
11722
+ children: [
11723
+ /* @__PURE__ */ jsxs("div", {
11724
+ className: "flex items-center justify-between",
11725
+ children: [/* @__PURE__ */ jsxs("div", {
11726
+ className: "text-sm font-semibold",
11727
+ children: [
11728
+ "Items (",
11729
+ primitiveItems.length,
11730
+ ")"
11731
+ ]
11732
+ }), /* @__PURE__ */ jsx(Button, {
11733
+ size: "small",
11734
+ icon: /* @__PURE__ */ jsx(Plus, {}),
11735
+ variant: "solid",
11736
+ color: "green",
11737
+ onClick: addPrimitive,
11738
+ children: "Add Item"
11739
+ })]
11740
+ }),
11741
+ /* @__PURE__ */ jsx(BulkActionsBar, {
11742
+ count: selection.selected.size,
11743
+ onDuplicate: () => duplicateSelectedPrimitives(selection.selected),
11744
+ onMoveUp: () => moveSelectedPrimitives(selection.selected, "up"),
11745
+ onMoveDown: () => moveSelectedPrimitives(selection.selected, "down"),
11746
+ onDelete: () => {
11747
+ deleteSelectedPrimitives(selection.selected);
11748
+ selection.clear();
11864
11749
  },
11865
- id: `primitive-${item.id}`,
11866
- value: String(item.value ?? ""),
11867
- onChange: ({ value: next }) => updatePrimitive(item.index, next)
11868
- })]
11869
- }, item.id))]
11870
- });
11871
- return /* @__PURE__ */ jsxs("div", {
11872
- className: "space-y-4",
11873
- children: [/* @__PURE__ */ jsxs("div", {
11874
- className: "flex items-center justify-between",
11875
- children: [/* @__PURE__ */ jsxs("div", {
11876
- className: "text-sm font-semibold",
11877
- children: [
11878
- "Items (",
11879
- objectItems.length,
11880
- ")"
11881
- ]
11882
- }), /* @__PURE__ */ jsx(Button, {
11883
- size: "small",
11884
- icon: /* @__PURE__ */ jsx(Plus, {}),
11885
- variant: "solid",
11886
- color: "green",
11887
- onClick: addItem,
11888
- children: "Add Item"
11889
- })]
11890
- }), objectItems.map((item) => /* @__PURE__ */ jsxs("div", {
11891
- className: "space-y-3 rounded border bg-gray-50 p-3",
11892
- children: [
11893
- /* @__PURE__ */ jsxs("div", {
11894
- className: "flex items-center justify-between",
11895
- children: [/* @__PURE__ */ jsxs("div", {
11896
- className: "text-xs font-medium",
11897
- children: ["Item ", item.index + 1]
11750
+ onClear: selection.clear
11751
+ }),
11752
+ primitiveItems.map((item, i) => /* @__PURE__ */ jsxs("div", {
11753
+ className: "space-y-2 rounded border border-gray-100 bg-gray-50 p-2",
11754
+ children: [/* @__PURE__ */ jsxs("div", {
11755
+ className: "flex items-center justify-between space-x-1",
11756
+ children: [/* @__PURE__ */ jsx("div", {
11757
+ onClick: (e) => selection.toggle(item.index, e.shiftKey),
11758
+ className: "inline-flex",
11759
+ children: /* @__PURE__ */ jsx(Checkbox, {
11760
+ checked: selection.isSelected(item.index),
11761
+ onChange: () => {}
11762
+ })
11898
11763
  }), /* @__PURE__ */ jsxs("div", {
11899
11764
  className: "flex space-x-1",
11900
11765
  children: [
11901
11766
  /* @__PURE__ */ jsx(Button, {
11902
11767
  size: "small",
11903
11768
  icon: /* @__PURE__ */ jsx(ArrowUp, {}),
11904
- disabled: item.index === 0,
11905
- onClick: () => moveItem(item.index, item.index - 1)
11769
+ disabled: i === 0,
11770
+ onClick: () => movePrimitive(item.index, item.index - 1)
11906
11771
  }),
11907
11772
  /* @__PURE__ */ jsx(Button, {
11908
11773
  size: "small",
11909
11774
  icon: /* @__PURE__ */ jsx(ArrowDown, {}),
11910
- disabled: item.index === objectItems.length - 1,
11911
- onClick: () => moveItem(item.index, item.index + 1)
11775
+ disabled: i === primitiveItems.length - 1,
11776
+ onClick: () => movePrimitive(item.index, item.index + 1)
11912
11777
  }),
11913
11778
  /* @__PURE__ */ jsx(Button, {
11914
11779
  danger: true,
11915
11780
  size: "small",
11916
11781
  icon: /* @__PURE__ */ jsx(X, {}),
11917
- disabled: objectItems.length <= 1,
11918
- onClick: () => deleteItem(item.index)
11782
+ disabled: primitiveItems.length <= 1,
11783
+ onClick: () => deletePrimitive(item.index)
11919
11784
  })
11920
11785
  ]
11921
11786
  })]
11922
- }),
11923
- /* @__PURE__ */ jsx("div", {
11924
- className: "space-y-2",
11925
- children: Object.entries(item.editableProperties).map(([key, prop]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
11926
- className: "flex flex-col space-y-2",
11927
- children: [/* @__PURE__ */ jsx("label", {
11928
- className: "w-20 shrink-0 text-xs font-medium",
11929
- children: key
11930
- }), /* @__PURE__ */ jsx(Field, {
11931
- binding: {
11932
- label: key,
11933
- property: render?.[key] && "type" in render[key] ? render[key].property ?? render[key].type : key,
11934
- type: render?.[key] && "type" in render[key] ? render[key].type : void 0,
11935
- render: render?.[key] && "type" in render[key] ? render[key].render : render?.[key] && !("type" in render[key]) ? render[key] : void 0
11936
- },
11937
- id: `item-${item.id}-${key}`,
11938
- value: String(prop.value),
11939
- onChange: ({ value: next }) => updateProperty(item.index, key, parseValue(next))
11787
+ }), /* @__PURE__ */ jsx(Field, {
11788
+ binding: {
11789
+ label: `item-${i}`,
11790
+ property: item.type
11791
+ },
11792
+ id: `primitive-${item.id}`,
11793
+ value: String(item.value ?? ""),
11794
+ onChange: ({ value: next }) => updatePrimitive(item.index, next)
11795
+ })]
11796
+ }, item.id))
11797
+ ]
11798
+ });
11799
+ return /* @__PURE__ */ jsxs("div", {
11800
+ className: "space-y-4",
11801
+ children: [
11802
+ /* @__PURE__ */ jsxs("div", {
11803
+ className: "flex items-center justify-between",
11804
+ children: [/* @__PURE__ */ jsxs("div", {
11805
+ className: "text-sm font-semibold",
11806
+ children: [
11807
+ "Items (",
11808
+ objectItems.length,
11809
+ ")"
11810
+ ]
11811
+ }), /* @__PURE__ */ jsx(Button, {
11812
+ size: "small",
11813
+ icon: /* @__PURE__ */ jsx(Plus, {}),
11814
+ variant: "solid",
11815
+ color: "green",
11816
+ onClick: addItem,
11817
+ children: "Add Item"
11818
+ })]
11819
+ }),
11820
+ /* @__PURE__ */ jsx(BulkActionsBar, {
11821
+ count: selection.selected.size,
11822
+ onDuplicate: () => duplicateSelectedItems(selection.selected),
11823
+ onMoveUp: () => moveSelectedItems(selection.selected, "up"),
11824
+ onMoveDown: () => moveSelectedItems(selection.selected, "down"),
11825
+ onDelete: () => {
11826
+ deleteSelectedItems(selection.selected);
11827
+ selection.clear();
11828
+ },
11829
+ onClear: selection.clear
11830
+ }),
11831
+ objectItems.map((item) => /* @__PURE__ */ jsxs("div", {
11832
+ className: "space-y-3 rounded border bg-gray-50 p-3",
11833
+ children: [
11834
+ /* @__PURE__ */ jsxs("div", {
11835
+ className: "flex items-center justify-between",
11836
+ children: [/* @__PURE__ */ jsxs("div", {
11837
+ className: "flex items-center space-x-2",
11838
+ children: [/* @__PURE__ */ jsx("div", {
11839
+ onClick: (e) => selection.toggle(item.index, e.shiftKey),
11840
+ className: "inline-flex",
11841
+ children: /* @__PURE__ */ jsx(Checkbox, {
11842
+ checked: selection.isSelected(item.index),
11843
+ onChange: () => {}
11844
+ })
11845
+ }), /* @__PURE__ */ jsxs("div", {
11846
+ className: "text-xs font-medium",
11847
+ children: ["Item ", item.index + 1]
11848
+ })]
11849
+ }), /* @__PURE__ */ jsxs("div", {
11850
+ className: "flex space-x-1",
11851
+ children: [
11852
+ /* @__PURE__ */ jsx(Button, {
11853
+ size: "small",
11854
+ icon: /* @__PURE__ */ jsx(ArrowUp, {}),
11855
+ disabled: item.index === 0,
11856
+ onClick: () => moveItem(item.index, item.index - 1)
11857
+ }),
11858
+ /* @__PURE__ */ jsx(Button, {
11859
+ size: "small",
11860
+ icon: /* @__PURE__ */ jsx(ArrowDown, {}),
11861
+ disabled: item.index === objectItems.length - 1,
11862
+ onClick: () => moveItem(item.index, item.index + 1)
11863
+ }),
11864
+ /* @__PURE__ */ jsx(Button, {
11865
+ danger: true,
11866
+ size: "small",
11867
+ icon: /* @__PURE__ */ jsx(X, {}),
11868
+ disabled: objectItems.length <= 1,
11869
+ onClick: () => deleteItem(item.index)
11870
+ })
11871
+ ]
11940
11872
  })]
11941
- }), /* @__PURE__ */ jsxs("span", {
11942
- className: "text-right text-xs text-gray-500",
11943
- children: [
11944
- "(",
11945
- prop.type,
11946
- ")"
11947
- ]
11948
- })] }, `${item.id}-${key}`))
11949
- }),
11950
- /* @__PURE__ */ jsx("div", {
11951
- className: "space-y-3 border-t pt-2",
11952
- children: Object.entries(item.jsxBindings).length > 0 ? Object.entries(item.jsxBindings).map(([propertyName, bindings]) => /* @__PURE__ */ jsxs("div", {
11873
+ }),
11874
+ /* @__PURE__ */ jsx("div", {
11953
11875
  className: "space-y-2",
11876
+ children: Object.entries(item.editableProperties).map(([key, prop]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
11877
+ className: "flex flex-col space-y-2",
11878
+ children: [/* @__PURE__ */ jsx("label", {
11879
+ className: "w-20 shrink-0 text-xs font-medium",
11880
+ children: key
11881
+ }), /* @__PURE__ */ jsx(Field, {
11882
+ binding: {
11883
+ label: key,
11884
+ property: render?.[key] && "type" in render[key] ? render[key].property ?? render[key].type : key,
11885
+ type: render?.[key] && "type" in render[key] ? render[key].type : void 0,
11886
+ render: render?.[key] && "type" in render[key] ? render[key].render : render?.[key] && !("type" in render[key]) ? render[key] : void 0
11887
+ },
11888
+ id: `item-${item.id}-${key}`,
11889
+ value: String(prop.value),
11890
+ onChange: ({ value: next }) => updateProperty(item.index, key, parseValue(next))
11891
+ })]
11892
+ }), /* @__PURE__ */ jsxs("span", {
11893
+ className: "text-right text-xs text-gray-500",
11894
+ children: [
11895
+ "(",
11896
+ prop.type,
11897
+ ")"
11898
+ ]
11899
+ })] }, `${item.id}-${key}`))
11900
+ }),
11901
+ /* @__PURE__ */ jsx("div", {
11902
+ className: "space-y-3 border-t pt-2",
11903
+ children: Object.entries(item.jsxBindings).length > 0 ? Object.entries(item.jsxBindings).map(([propertyName, bindings]) => /* @__PURE__ */ jsxs("div", {
11904
+ className: "space-y-2",
11905
+ children: [/* @__PURE__ */ jsxs("div", {
11906
+ className: "text-xs font-medium text-blue-700",
11907
+ children: [
11908
+ propertyName,
11909
+ " Bindings (",
11910
+ bindings.length,
11911
+ "):"
11912
+ ]
11913
+ }), bindings.map((bindingNode, idx) => {
11914
+ const nodeId = bindingNode.dataAttributes.find((a) => a.name === "data-id")?.value;
11915
+ return /* @__PURE__ */ jsxs("div", {
11916
+ className: "rounded border border-blue-100 bg-blue-50\n p-2",
11917
+ children: [/* @__PURE__ */ jsxs("div", {
11918
+ className: "mb-1 text-xs text-blue-600",
11919
+ children: [
11920
+ "<",
11921
+ bindingNode.tagName || "element",
11922
+ ">"
11923
+ ]
11924
+ }), /* @__PURE__ */ jsx(Node, {
11925
+ data: bindingNode,
11926
+ onChange: onChildChange
11927
+ })]
11928
+ }, `binding-${item.id}-${propertyName}-${nodeId || idx}`);
11929
+ })]
11930
+ }, propertyName)) : /* @__PURE__ */ jsx("div", {
11931
+ className: "text-xs text-gray-500",
11932
+ children: "✓ No JSX bindings found"
11933
+ })
11934
+ })
11935
+ ]
11936
+ }, item.id))
11937
+ ]
11938
+ });
11939
+ };
11940
+
11941
+ //#endregion
11942
+ //#region src/components/dnd/panel/children.tsx
11943
+ const Children = ({ value, onChange, onNodeChange }) => {
11944
+ const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
11945
+ const selection = useMultiSelect(items.length);
11946
+ const editableChildrenMap = useMemo(() => {
11947
+ const map = /* @__PURE__ */ new Map();
11948
+ items.forEach((item, index) => {
11949
+ const editableNodes = findEditableChildren(item);
11950
+ if (editableNodes.length > 0) map.set(index, editableNodes);
11951
+ });
11952
+ return map;
11953
+ }, [items]);
11954
+ const moveItem = (fromIndex, toIndex) => {
11955
+ const nextItems = [...items];
11956
+ const [movedItem] = nextItems.splice(fromIndex, 1);
11957
+ nextItems.splice(toIndex, 0, movedItem);
11958
+ onChange?.(JSON.stringify(nextItems));
11959
+ };
11960
+ const moveSelectedItems = (indices, direction) => {
11961
+ const { items: nextItems, indices: nextIndices } = moveSelectedIndices(items, indices, direction);
11962
+ selection.replace(nextIndices);
11963
+ onChange?.(JSON.stringify(nextItems));
11964
+ };
11965
+ const deleteSelectedItems = (indices) => {
11966
+ if (items.length - indices.size < 1) return;
11967
+ const nextItems = removeIndices(items, indices);
11968
+ onChange?.(JSON.stringify(nextItems));
11969
+ };
11970
+ const deleteItem = (index) => deleteSelectedItems(new Set([index]));
11971
+ const addItem = () => {
11972
+ const newItem = cloneDataAttrNode(items[0] || createDefaultItem());
11973
+ const nextItems = [...items, newItem];
11974
+ onChange?.(JSON.stringify(nextItems));
11975
+ };
11976
+ const duplicateSelectedItems = (indices) => {
11977
+ const sources = [...indices].sort((a, b) => a - b).map((index) => items[index]).filter((item) => Boolean(item));
11978
+ if (sources.length === 0) return;
11979
+ const clones = sources.map(cloneDataAttrNode);
11980
+ onChange?.(JSON.stringify([...items, ...clones]));
11981
+ };
11982
+ const createDefaultItem = () => ({
11983
+ id: nanoid(6),
11984
+ tagName: "div",
11985
+ attributes: [{
11986
+ name: "data-id",
11987
+ value: nanoid(6)
11988
+ }, {
11989
+ name: "data-item",
11990
+ value: "true"
11991
+ }],
11992
+ dataAttributes: [{
11993
+ name: "data-id",
11994
+ value: nanoid(6)
11995
+ }, {
11996
+ name: "data-item",
11997
+ value: "true"
11998
+ }],
11999
+ textContent: "",
12000
+ children: []
12001
+ });
12002
+ const cloneDataAttrNode = (node) => ({
12003
+ ...node,
12004
+ id: nanoid(6),
12005
+ attributes: node.attributes.map((attr) => ({
12006
+ ...attr,
12007
+ value: attr.name === "data-id" ? nanoid(6) : attr.value
12008
+ })),
12009
+ dataAttributes: node.dataAttributes.map((attr) => ({
12010
+ ...attr,
12011
+ value: attr.name === "data-id" ? nanoid(6) : attr.value
12012
+ })),
12013
+ children: node.children?.map(cloneDataAttrNode)
12014
+ });
12015
+ return /* @__PURE__ */ jsxs("div", {
12016
+ className: "space-y-3",
12017
+ children: [
12018
+ /* @__PURE__ */ jsxs("div", {
12019
+ className: "flex items-center justify-between",
12020
+ children: [/* @__PURE__ */ jsxs("div", {
12021
+ className: "text-sm font-semibold text-green-700",
12022
+ children: [
12023
+ "Children Items (",
12024
+ items.length,
12025
+ ")"
12026
+ ]
12027
+ }), /* @__PURE__ */ jsx(Button, {
12028
+ size: "small",
12029
+ color: "green",
12030
+ icon: /* @__PURE__ */ jsx(Plus, {}),
12031
+ onClick: addItem,
12032
+ children: "Add Child"
12033
+ })]
12034
+ }),
12035
+ /* @__PURE__ */ jsx(BulkActionsBar, {
12036
+ count: selection.selected.size,
12037
+ onDuplicate: () => duplicateSelectedItems(selection.selected),
12038
+ onMoveUp: () => moveSelectedItems(selection.selected, "up"),
12039
+ onMoveDown: () => moveSelectedItems(selection.selected, "down"),
12040
+ onDelete: () => {
12041
+ deleteSelectedItems(selection.selected);
12042
+ selection.clear();
12043
+ },
12044
+ onClear: selection.clear
12045
+ }),
12046
+ items.map((item, itemIndex) => /* @__PURE__ */ jsxs("div", {
12047
+ className: "space-y-2 rounded border border-green-200 bg-green-50 p-3",
12048
+ children: [
12049
+ /* @__PURE__ */ jsxs("div", {
12050
+ className: "flex items-center justify-between",
11954
12051
  children: [/* @__PURE__ */ jsxs("div", {
11955
- className: "text-xs font-medium text-blue-700",
12052
+ className: "flex items-center space-x-2",
12053
+ children: [/* @__PURE__ */ jsx("div", {
12054
+ onClick: (e) => selection.toggle(itemIndex, e.shiftKey),
12055
+ className: "inline-flex",
12056
+ children: /* @__PURE__ */ jsx(Checkbox, {
12057
+ checked: selection.isSelected(itemIndex),
12058
+ onChange: () => {}
12059
+ })
12060
+ }), /* @__PURE__ */ jsxs("div", {
12061
+ className: "text-xs font-medium text-green-800",
12062
+ children: [
12063
+ "Child ",
12064
+ itemIndex + 1,
12065
+ " (",
12066
+ item.tagName || "fragment",
12067
+ ")"
12068
+ ]
12069
+ })]
12070
+ }), /* @__PURE__ */ jsxs("div", {
12071
+ className: "flex space-x-1",
11956
12072
  children: [
11957
- propertyName,
11958
- " Bindings (",
11959
- bindings.length,
11960
- "):"
12073
+ /* @__PURE__ */ jsx(Button, {
12074
+ size: "small",
12075
+ icon: /* @__PURE__ */ jsx(ArrowUp, {}),
12076
+ disabled: itemIndex === 0,
12077
+ onClick: () => moveItem(itemIndex, itemIndex - 1)
12078
+ }),
12079
+ /* @__PURE__ */ jsx(Button, {
12080
+ size: "small",
12081
+ icon: /* @__PURE__ */ jsx(ArrowDown, {}),
12082
+ disabled: itemIndex === items.length - 1,
12083
+ onClick: () => moveItem(itemIndex, itemIndex + 1)
12084
+ }),
12085
+ /* @__PURE__ */ jsx(Button, {
12086
+ title: items.length <= 1 ? "At least 1 item is required" : "Delete item",
12087
+ danger: true,
12088
+ size: "small",
12089
+ icon: /* @__PURE__ */ jsx(X, {}),
12090
+ disabled: items.length <= 1,
12091
+ onClick: () => deleteItem(itemIndex)
12092
+ })
11961
12093
  ]
11962
- }), bindings.map((bindingNode, idx) => {
11963
- const nodeId = bindingNode.dataAttributes.find((a) => a.name === "data-id")?.value;
12094
+ })]
12095
+ }),
12096
+ editableChildrenMap.has(itemIndex) && /* @__PURE__ */ jsxs("div", {
12097
+ className: "space-y-2",
12098
+ children: [/* @__PURE__ */ jsx("div", {
12099
+ className: "text-xs font-medium text-green-700",
12100
+ children: "Editable Bindings:"
12101
+ }), editableChildrenMap.get(itemIndex).map((editableNode, idx) => {
12102
+ const nodeId = editableNode.dataAttributes.find((a) => a.name === "data-id")?.value;
11964
12103
  return /* @__PURE__ */ jsxs("div", {
11965
- className: "rounded border border-blue-100 bg-blue-50\n p-2",
11966
- children: [/* @__PURE__ */ jsxs("div", {
11967
- className: "mb-1 text-xs text-blue-600",
11968
- children: [
11969
- "<",
11970
- bindingNode.tagName || "element",
11971
- ">"
11972
- ]
12104
+ className: "rounded border border-green-100 bg-white p-2",
12105
+ children: [/* @__PURE__ */ jsx("div", {
12106
+ className: "mb-1 text-xs text-green-600",
12107
+ children: editableNode.tagName
11973
12108
  }), /* @__PURE__ */ jsx(Node, {
11974
- data: bindingNode,
11975
- onChange: onChildChange
12109
+ data: editableNode,
12110
+ onChange: onNodeChange
11976
12111
  })]
11977
- }, `binding-${item.id}-${propertyName}-${nodeId || idx}`);
12112
+ }, `editable-${itemIndex}-${nodeId || idx}`);
11978
12113
  })]
11979
- }, propertyName)) : /* @__PURE__ */ jsx("div", {
11980
- className: "text-xs text-gray-500",
11981
- children: "✓ No JSX bindings found"
12114
+ }),
12115
+ !!item.children && !editableChildrenMap.has(itemIndex) && /* @__PURE__ */ jsxs("div", {
12116
+ className: "space-y-1",
12117
+ children: [/* @__PURE__ */ jsx("div", {
12118
+ className: "text-xs font-medium text-green-700",
12119
+ children: "Child Nodes:"
12120
+ }), item.children.map((node, nodeIndex) => /* @__PURE__ */ jsx("div", {
12121
+ className: "ml-2 rounded border border-green-100 bg-white p-2",
12122
+ children: /* @__PURE__ */ jsx(Node, {
12123
+ data: node,
12124
+ onChange: onNodeChange
12125
+ })
12126
+ }, `node-${itemIndex}-${nodeIndex}`))]
11982
12127
  })
11983
- })
11984
- ]
11985
- }, item.id))]
12128
+ ]
12129
+ }, item.id || itemIndex))
12130
+ ]
11986
12131
  });
11987
12132
  };
11988
12133
 
12134
+ //#endregion
12135
+ //#region src/components/dnd/panel/icon-map.ts
12136
+ const ICON_MAP = {
12137
+ AlertCircle,
12138
+ ArrowDown,
12139
+ ArrowLeft,
12140
+ ArrowRight,
12141
+ ArrowUp,
12142
+ Bell,
12143
+ Bookmark,
12144
+ Calendar,
12145
+ Camera,
12146
+ Check,
12147
+ ChevronDown,
12148
+ ChevronRight,
12149
+ Clock,
12150
+ Download,
12151
+ Edit,
12152
+ Eye,
12153
+ Filter,
12154
+ Heart,
12155
+ Home,
12156
+ Image,
12157
+ Info,
12158
+ Link,
12159
+ Mail,
12160
+ Map: Map$1,
12161
+ Menu,
12162
+ MessageCircle,
12163
+ Phone,
12164
+ Plus,
12165
+ Search,
12166
+ Settings,
12167
+ Share,
12168
+ ShoppingCart,
12169
+ Star,
12170
+ Trash,
12171
+ Upload: Upload$1,
12172
+ User,
12173
+ X
12174
+ };
12175
+
11989
12176
  //#endregion
11990
12177
  //#region src/components/dnd/panel/field.tsx
11991
12178
  const isColorProperty = (propertyName) => {