@nextbridgehq/payload-block-builder 0.1.6 → 0.1.8

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/client.js CHANGED
@@ -39,6 +39,56 @@ function MediaPicker({ label, required, value, onChange }) {
39
39
  "x"
40
40
  ))) : /* @__PURE__ */ React.createElement(ListDrawerToggler, { className: "bdf-upload-btn" }, "Choose from Media Library")), /* @__PURE__ */ React.createElement(ListDrawer, { onSelect: handleSelect }));
41
41
  }
42
+ function RelationshipPicker({ label, required, collection, value, onChange }) {
43
+ const changeRef = useRef(onChange);
44
+ const closeRef = useRef(() => {
45
+ });
46
+ useEffect(() => {
47
+ changeRef.current = onChange;
48
+ });
49
+ const handleSelect = useCallback(
50
+ ({ docID, doc }) => {
51
+ changeRef.current({
52
+ id: docID,
53
+ title: doc?.title ?? doc?.name ?? doc?.slug ?? null
54
+ });
55
+ closeRef.current();
56
+ },
57
+ []
58
+ );
59
+ const [ListDrawer, ListDrawerToggler, { closeDrawer }] = useListDrawer({
60
+ collectionSlugs: [collection]
61
+ });
62
+ closeRef.current = closeDrawer;
63
+ const relObj = value && typeof value === "object" ? value : null;
64
+ const relId = relObj?.id ?? (typeof value === "string" || typeof value === "number" ? value : null);
65
+ const relTitle = relObj?.title ? String(relObj.title) : null;
66
+ const [fetchedTitle, setFetchedTitle] = useState(null);
67
+ useEffect(() => {
68
+ if (!relId || relTitle) {
69
+ setFetchedTitle(null);
70
+ return;
71
+ }
72
+ fetch(`/api/${collection}/${String(relId)}?depth=0`, { credentials: "same-origin" }).then((r) => r.ok ? r.json() : null).then((doc) => {
73
+ if (doc) {
74
+ const t = doc.title ?? doc.name ?? doc.slug ?? null;
75
+ setFetchedTitle(t ? String(t) : null);
76
+ }
77
+ }).catch(() => {
78
+ });
79
+ }, [relId, relTitle, collection]);
80
+ const displayTitle = relTitle ?? fetchedTitle;
81
+ return /* @__PURE__ */ React.createElement("div", { className: "bdf-field" }, /* @__PURE__ */ React.createElement("label", { className: "bdf-label" }, label, required && /* @__PURE__ */ React.createElement("span", { className: "bdf-required" }, "*"), /* @__PURE__ */ React.createElement("span", { style: { marginLeft: 6, fontSize: 11, color: "var(--theme-elevation-400)", fontWeight: 400 } }, "(", collection, ")")), /* @__PURE__ */ React.createElement("div", { className: "bdf-upload-area" }, relId ? /* @__PURE__ */ React.createElement("div", { className: "bdf-upload-selected" }, /* @__PURE__ */ React.createElement("span", { className: "bdf-upload-name" }, displayTitle ?? `ID: ${String(relId)}`), /* @__PURE__ */ React.createElement("div", { className: "bdf-upload-actions" }, /* @__PURE__ */ React.createElement(ListDrawerToggler, { className: "bdf-upload-btn" }, "Change"), /* @__PURE__ */ React.createElement(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ className: "bdf-icon-btn bdf-icon-btn--danger",
86
+ title: "Remove",
87
+ onClick: () => onChange(null)
88
+ },
89
+ "\xD7"
90
+ ))) : /* @__PURE__ */ React.createElement(ListDrawerToggler, { className: "bdf-upload-btn" }, "Choose from ", collection)), /* @__PURE__ */ React.createElement(ListDrawer, { onSelect: handleSelect }));
91
+ }
42
92
  function SchemaForm({ schema, value, onChange }) {
43
93
  const set = useCallback(
44
94
  (key, val) => onChange({ ...value, [key]: val }),
@@ -176,17 +226,19 @@ function FieldInput({ field, value, onChange }) {
176
226
  onChange
177
227
  }
178
228
  );
179
- case "relationship":
180
- return /* @__PURE__ */ React.createElement("div", { className: "bdf-field" }, /* @__PURE__ */ React.createElement("label", { className: "bdf-label" }, label, field.required && /* @__PURE__ */ React.createElement("span", { className: "bdf-required" }, "*"), field.collection && /* @__PURE__ */ React.createElement("span", { style: { marginLeft: 6, fontSize: 11, color: "var(--theme-elevation-400)", fontWeight: 400 } }, "(", field.collection, ")")), /* @__PURE__ */ React.createElement(
181
- "input",
229
+ case "relationship": {
230
+ const collection = field.collection ?? "media";
231
+ return /* @__PURE__ */ React.createElement(
232
+ RelationshipPicker,
182
233
  {
183
- className: "bdf-input",
184
- type: "text",
185
- value: value ?? "",
186
- onChange: (e) => onChange(e.target.value),
187
- placeholder: "Enter document ID"
234
+ label,
235
+ required: field.required,
236
+ collection,
237
+ value,
238
+ onChange
188
239
  }
189
- ));
240
+ );
241
+ }
190
242
  case "json":
191
243
  return /* @__PURE__ */ React.createElement("div", { className: "bdf-field" }, /* @__PURE__ */ React.createElement("label", { className: "bdf-label" }, label, field.required && /* @__PURE__ */ React.createElement("span", { className: "bdf-required" }, "*"), /* @__PURE__ */ React.createElement("span", { style: { marginLeft: 6, fontSize: 11, color: "var(--theme-elevation-400)", fontWeight: 400 } }, "(JSON)")), /* @__PURE__ */ React.createElement(
192
244
  "textarea",
@@ -1134,6 +1186,7 @@ var useBuilderStore = create()(
1134
1186
 
1135
1187
  // src/block-builder/components/canvas/TopBar.tsx
1136
1188
  import React7, { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
1189
+ import { Blocks, ChevronDown, X } from "lucide-react";
1137
1190
 
1138
1191
  // src/block-builder/lib/mapToSaveRequest.ts
1139
1192
  var TYPE_MAP = {
@@ -1322,7 +1375,7 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1322
1375
  return () => document.removeEventListener("mousedown", handleClick);
1323
1376
  }, []);
1324
1377
  async function handlePublish() {
1325
- if (!activeBlock || isReadOnly) return;
1378
+ if (!activeBlock || isReadOnly) return false;
1326
1379
  setNotification({ status: "publishing" });
1327
1380
  try {
1328
1381
  const req = mapToSaveRequest(activeBlock);
@@ -1339,13 +1392,15 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1339
1392
  status: "success",
1340
1393
  msg: `v${json.versionNumber ?? "?"} published successfully!`
1341
1394
  });
1342
- await onAfterPublish();
1395
+ onAfterPublish();
1396
+ return true;
1343
1397
  } else {
1344
1398
  setNotification({
1345
1399
  status: "error",
1346
1400
  title: "Failed to publish block",
1347
1401
  errors: json.errors ?? ["An unknown error occurred."]
1348
1402
  });
1403
+ return false;
1349
1404
  }
1350
1405
  } catch (err) {
1351
1406
  setNotification({
@@ -1353,6 +1408,7 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1353
1408
  title: "Network error",
1354
1409
  errors: [err instanceof Error ? err.message : "Could not reach the server."]
1355
1410
  });
1411
+ return false;
1356
1412
  }
1357
1413
  }
1358
1414
  function handleExport() {
@@ -1380,9 +1436,9 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1380
1436
  className: "bb-block-picker__trigger",
1381
1437
  onClick: () => setBlockPickerOpen((o) => !o)
1382
1438
  },
1383
- /* @__PURE__ */ React7.createElement("span", { className: "bb-block-picker__icon" }, "B"),
1439
+ /* @__PURE__ */ React7.createElement(Blocks, { size: 14, strokeWidth: 1.75, className: "bb-block-picker__icon" }),
1384
1440
  /* @__PURE__ */ React7.createElement("span", null, activeBlockDef?.name ?? activeSlug ?? "Select a block"),
1385
- /* @__PURE__ */ React7.createElement("span", { className: "bb-version-selector__chevron" }, "v")
1441
+ /* @__PURE__ */ React7.createElement(ChevronDown, { size: 14, strokeWidth: 1.75, className: "bb-version-selector__chevron" })
1386
1442
  ), blockPickerOpen && /* @__PURE__ */ React7.createElement("div", { className: "bb-block-picker__dropdown" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-version-dropdown__header" }, "Block Definitions"), blockDefs.map((b) => /* @__PURE__ */ React7.createElement(
1387
1443
  "button",
1388
1444
  {
@@ -1406,7 +1462,7 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1406
1462
  /* @__PURE__ */ React7.createElement("span", { className: `bb-version-selector__dot${selectedVersion?.isCurrent ? " bb-version-selector__dot--current" : " bb-version-selector__dot--old"}` }),
1407
1463
  /* @__PURE__ */ React7.createElement("span", null, selectedVersion?.label ?? `v${selectedVersion?.versionNumber ?? "?"}`),
1408
1464
  selectedVersion?.isCurrent && /* @__PURE__ */ React7.createElement("span", { className: "bb-version-selector__badge" }, "current"),
1409
- /* @__PURE__ */ React7.createElement("span", { className: "bb-version-selector__chevron" }, "v")
1465
+ /* @__PURE__ */ React7.createElement(ChevronDown, { size: 14, strokeWidth: 1.75, className: "bb-version-selector__chevron" })
1410
1466
  ), versionDropdownOpen && /* @__PURE__ */ React7.createElement("div", { className: "bb-version-dropdown" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-version-dropdown__header" }, "Version History"), versions.map((v) => /* @__PURE__ */ React7.createElement(
1411
1467
  "button",
1412
1468
  {
@@ -1447,9 +1503,8 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1447
1503
  {
1448
1504
  type: "button",
1449
1505
  onClick: async () => {
1450
- await handlePublish();
1451
- setVersionMeta(null, false);
1452
- onRestoreVersion();
1506
+ const success = await handlePublish();
1507
+ if (success) onRestoreVersion();
1453
1508
  },
1454
1509
  disabled: notification?.status === "publishing" || !activeBlock,
1455
1510
  className: "bb-btn bb-btn--warning"
@@ -1464,11 +1519,12 @@ function TopBar({ blockDefs, activeSlug, onBlockSelect, versions, selectedVersio
1464
1519
  className: "bb-btn bb-btn--primary"
1465
1520
  },
1466
1521
  notification?.status === "publishing" ? "Publishing..." : "Publish to Payload"
1467
- ))), notification?.status === "publishing" && /* @__PURE__ */ React7.createElement("div", { className: "bb-notify bb-notify--publishing" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__box" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__spinner" }), /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__body" }, /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__title" }, isReadOnly ? "Restoring version..." : "Publishing to Payload..."), /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__sub" }, "Validating schema and saving block definition.")))), notification?.status === "success" && /* @__PURE__ */ React7.createElement("div", { className: "bb-notify bb-notify--success" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__box" }, /* @__PURE__ */ React7.createElement("span", { className: "bb-notify__icon" }, "OK"), /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__body" }, /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__title" }, notification.msg), /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__sub" }, "The block definition and version have been saved.")), /* @__PURE__ */ React7.createElement("button", { className: "bb-notify__close", onClick: () => setNotification(null) }, "x"))), notification?.status === "error" && /* @__PURE__ */ React7.createElement("div", { className: "bb-notify bb-notify--error" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__box" }, /* @__PURE__ */ React7.createElement("span", { className: "bb-notify__icon" }, "!"), /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__body" }, /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__title" }, notification.title), /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__sub" }, "Fix the following errors before publishing:"), /* @__PURE__ */ React7.createElement("ul", { className: "bb-notify__error-list" }, notification.errors.map((e, i) => /* @__PURE__ */ React7.createElement("li", { key: i }, e)))), /* @__PURE__ */ React7.createElement("button", { className: "bb-notify__close", onClick: () => setNotification(null) }, "x"))));
1522
+ ))), notification?.status === "publishing" && /* @__PURE__ */ React7.createElement("div", { className: "bb-notify bb-notify--publishing" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__box" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__spinner" }), /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__body" }, /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__title" }, isReadOnly ? "Restoring version..." : "Publishing to Payload..."), /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__sub" }, "Validating schema and saving block definition.")))), notification?.status === "success" && /* @__PURE__ */ React7.createElement("div", { className: "bb-notify bb-notify--success" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__box" }, /* @__PURE__ */ React7.createElement("span", { className: "bb-notify__icon" }, "OK"), /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__body" }, /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__title" }, notification.msg), /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__sub" }, "The block definition and version have been saved.")), /* @__PURE__ */ React7.createElement("button", { className: "bb-notify__close", onClick: () => setNotification(null) }, /* @__PURE__ */ React7.createElement(X, { size: 12, strokeWidth: 2 })))), notification?.status === "error" && /* @__PURE__ */ React7.createElement("div", { className: "bb-notify bb-notify--error" }, /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__box" }, /* @__PURE__ */ React7.createElement("span", { className: "bb-notify__icon" }, "!"), /* @__PURE__ */ React7.createElement("div", { className: "bb-notify__body" }, /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__title" }, notification.title), /* @__PURE__ */ React7.createElement("p", { className: "bb-notify__sub" }, "Fix the following errors before publishing:"), /* @__PURE__ */ React7.createElement("ul", { className: "bb-notify__error-list" }, notification.errors.map((e, i) => /* @__PURE__ */ React7.createElement("li", { key: i }, e)))), /* @__PURE__ */ React7.createElement("button", { className: "bb-notify__close", onClick: () => setNotification(null) }, /* @__PURE__ */ React7.createElement(X, { size: 12, strokeWidth: 2 })))));
1468
1523
  }
1469
1524
 
1470
1525
  // src/block-builder/components/canvas/BlockList.tsx
1471
1526
  import React8 from "react";
1527
+ import { Copy, Trash2, Plus } from "lucide-react";
1472
1528
  function BlockList() {
1473
1529
  const blocks = useBuilderStore((s) => s.blocks);
1474
1530
  const activeBlockId = useBuilderStore((s) => s.activeBlockId);
@@ -1476,7 +1532,7 @@ function BlockList() {
1476
1532
  const removeBlock = useBuilderStore((s) => s.removeBlock);
1477
1533
  const duplicateBlock = useBuilderStore((s) => s.duplicateBlock);
1478
1534
  const setActiveBlock = useBuilderStore((s) => s.setActiveBlock);
1479
- return /* @__PURE__ */ React8.createElement("div", { className: "bb-sidebar bb-sidebar--200 bb-sidebar--blocks" }, /* @__PURE__ */ React8.createElement("div", { className: "bb-sidebar__header" }, /* @__PURE__ */ React8.createElement("span", { className: "bb-sidebar__title" }, "Blocks"), /* @__PURE__ */ React8.createElement("button", { type: "button", onClick: addBlock, title: "Add block", className: "bb-sidebar__add" }, "+")), /* @__PURE__ */ React8.createElement("div", { className: "bb-sidebar__body" }, blocks.length === 0 && /* @__PURE__ */ React8.createElement("div", { className: "bb-block-empty" }, "No blocks yet.", /* @__PURE__ */ React8.createElement("br", null), "Click + to create one."), blocks.map((block) => {
1535
+ return /* @__PURE__ */ React8.createElement("div", { className: "bb-sidebar bb-sidebar--200 bb-sidebar--blocks" }, /* @__PURE__ */ React8.createElement("div", { className: "bb-sidebar__header" }, /* @__PURE__ */ React8.createElement("span", { className: "bb-sidebar__title" }, "Blocks"), /* @__PURE__ */ React8.createElement("button", { type: "button", onClick: addBlock, title: "Add block", className: "bb-sidebar__add" }, /* @__PURE__ */ React8.createElement(Plus, { size: 14, strokeWidth: 2 }))), /* @__PURE__ */ React8.createElement("div", { className: "bb-sidebar__body" }, blocks.length === 0 && /* @__PURE__ */ React8.createElement("div", { className: "bb-block-empty" }, "No blocks yet.", /* @__PURE__ */ React8.createElement("br", null), "Click + to create one."), blocks.map((block) => {
1480
1536
  const isActive = block.id === activeBlockId;
1481
1537
  return /* @__PURE__ */ React8.createElement(
1482
1538
  "div",
@@ -1501,7 +1557,7 @@ function BlockList() {
1501
1557
  className: "bb-block-action",
1502
1558
  title: "Duplicate"
1503
1559
  },
1504
- "="
1560
+ /* @__PURE__ */ React8.createElement(Copy, { size: 12, strokeWidth: 1.75 })
1505
1561
  ),
1506
1562
  /* @__PURE__ */ React8.createElement(
1507
1563
  "button",
@@ -1511,7 +1567,7 @@ function BlockList() {
1511
1567
  className: "bb-block-action bb-block-action--danger",
1512
1568
  title: "Delete"
1513
1569
  },
1514
- "x"
1570
+ /* @__PURE__ */ React8.createElement(Trash2, { size: 12, strokeWidth: 1.75 })
1515
1571
  )
1516
1572
  )
1517
1573
  );
@@ -1539,24 +1595,32 @@ import { restrictToVerticalAxis, restrictToParentElement } from "@dnd-kit/modifi
1539
1595
  import React9 from "react";
1540
1596
  import { useSortable } from "@dnd-kit/sortable";
1541
1597
  import { CSS } from "@dnd-kit/utilities";
1598
+ import {
1599
+ Type,
1600
+ AlignLeft,
1601
+ Hash,
1602
+ Mail,
1603
+ Calendar,
1604
+ CheckSquare,
1605
+ ChevronDown as ChevronDown2,
1606
+ Circle,
1607
+ Upload,
1608
+ Link,
1609
+ Braces,
1610
+ X as X2
1611
+ } from "lucide-react";
1542
1612
  var ICON_MAP = {
1543
- text: "T",
1544
- textarea: "Tx",
1545
- richText: "RT",
1546
- number: "#",
1547
- checkbox: "[x]",
1548
- select: "v",
1549
- radio: "(o)",
1550
- date: "D",
1551
- upload: "^",
1552
- email: "@",
1553
- code: "<>",
1554
- point: "P",
1555
- relationship: "->>",
1556
- array: "[]",
1557
- group: "{ }",
1558
- json: "{ }",
1559
- ui: "UI"
1613
+ text: Type,
1614
+ textarea: AlignLeft,
1615
+ number: Hash,
1616
+ email: Mail,
1617
+ date: Calendar,
1618
+ checkbox: CheckSquare,
1619
+ select: ChevronDown2,
1620
+ radio: Circle,
1621
+ upload: Upload,
1622
+ relationship: Link,
1623
+ json: Braces
1560
1624
  };
1561
1625
  function SortableFieldCard({ field, blockId, index }) {
1562
1626
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
@@ -1565,6 +1629,7 @@ function SortableFieldCard({ field, blockId, index }) {
1565
1629
  const activeFieldId = useBuilderStore((s) => s.activeFieldId);
1566
1630
  const setActiveField = useBuilderStore((s) => s.setActiveField);
1567
1631
  const removeField = useBuilderStore((s) => s.removeField);
1632
+ const isReadOnly = useBuilderStore((s) => s.isReadOnly);
1568
1633
  const isActive = activeFieldId === field.id;
1569
1634
  const wrapStyle = {
1570
1635
  transform: CSS.Transform.toString(transform),
@@ -1585,10 +1650,10 @@ function SortableFieldCard({ field, blockId, index }) {
1585
1650
  className: `bb-field-card${isActive ? " bb-field-card--active" : ""}`,
1586
1651
  onClick: () => setActiveField(isActive ? null : field.id)
1587
1652
  },
1588
- /* @__PURE__ */ React9.createElement("span", { className: "bb-field-card__icon" }, ICON_MAP[field.type] ?? "?"),
1653
+ /* @__PURE__ */ React9.createElement("span", { className: "bb-field-card__icon" }, /* @__PURE__ */ React9.createElement(FieldIcon, { type: field.type })),
1589
1654
  /* @__PURE__ */ React9.createElement("div", { className: "bb-field-card__body" }, /* @__PURE__ */ React9.createElement("div", { className: "bb-field-card__name" }, field.name || /* @__PURE__ */ React9.createElement("span", { className: "bb-field-card__name--empty" }, "unnamed")), /* @__PURE__ */ React9.createElement("div", { className: "bb-field-card__type" }, field.type, field.required && /* @__PURE__ */ React9.createElement("span", { className: "bb-field-card__required" }, "*"))),
1590
1655
  /* @__PURE__ */ React9.createElement("span", { className: "bb-field-card__index" }, "#", index + 1),
1591
- /* @__PURE__ */ React9.createElement(
1656
+ !isReadOnly && /* @__PURE__ */ React9.createElement(
1592
1657
  "button",
1593
1658
  {
1594
1659
  type: "button",
@@ -1600,11 +1665,15 @@ function SortableFieldCard({ field, blockId, index }) {
1600
1665
  className: "bb-field-card__delete",
1601
1666
  title: "Remove field"
1602
1667
  },
1603
- "x"
1668
+ /* @__PURE__ */ React9.createElement(X2, { size: 12, strokeWidth: 2 })
1604
1669
  )
1605
1670
  )
1606
1671
  );
1607
1672
  }
1673
+ function FieldIcon({ type }) {
1674
+ const Icon = ICON_MAP[type];
1675
+ return Icon ? /* @__PURE__ */ React9.createElement(Icon, { size: 13, strokeWidth: 1.75 }) : null;
1676
+ }
1608
1677
 
1609
1678
  // src/block-builder/components/canvas/BuilderCanvas.tsx
1610
1679
  function BuilderCanvas() {
@@ -1721,21 +1790,15 @@ import React12 from "react";
1721
1790
  var ALL_TYPES = [
1722
1791
  "text",
1723
1792
  "textarea",
1724
- "richText",
1725
1793
  "number",
1794
+ "email",
1795
+ "date",
1726
1796
  "checkbox",
1727
1797
  "select",
1728
1798
  "radio",
1729
- "date",
1730
1799
  "upload",
1731
- "email",
1732
- "code",
1733
- "point",
1734
1800
  "relationship",
1735
- "array",
1736
- "group",
1737
- "json",
1738
- "ui"
1801
+ "json"
1739
1802
  ];
1740
1803
  function FieldConfig() {
1741
1804
  const activeBlockId = useBuilderStore((s) => s.activeBlockId);
@@ -1908,19 +1971,20 @@ function ConfigPanel() {
1908
1971
  // src/block-builder/components/sidebar/FieldPalette.tsx
1909
1972
  import React14, { useState as useState6 } from "react";
1910
1973
  import {
1911
- Type,
1912
- AlignLeft,
1913
- Hash,
1914
- Mail,
1915
- Calendar,
1916
- CheckSquare,
1917
- ChevronDown,
1918
- Circle,
1919
- Upload,
1920
- Link,
1974
+ Type as Type2,
1975
+ AlignLeft as AlignLeft2,
1976
+ AlignJustify,
1977
+ Hash as Hash2,
1978
+ Mail as Mail2,
1979
+ Calendar as Calendar2,
1980
+ CheckSquare as CheckSquare2,
1981
+ ChevronDown as ChevronDown3,
1982
+ Circle as Circle2,
1983
+ Upload as Upload2,
1984
+ Link as Link2,
1921
1985
  List,
1922
1986
  Folder,
1923
- Braces
1987
+ Braces as Braces2
1924
1988
  } from "lucide-react";
1925
1989
 
1926
1990
  // src/block-builder/lib/field-palette.ts
@@ -1955,19 +2019,20 @@ function getFieldMeta(type) {
1955
2019
 
1956
2020
  // src/block-builder/components/sidebar/FieldPalette.tsx
1957
2021
  var ICON_MAP2 = {
1958
- Type,
1959
- AlignLeft,
1960
- Hash,
1961
- Mail,
1962
- Calendar,
1963
- CheckSquare,
1964
- ChevronDown,
1965
- Circle,
1966
- Upload,
1967
- Link,
2022
+ Type: Type2,
2023
+ AlignLeft: AlignLeft2,
2024
+ AlignJustify,
2025
+ Hash: Hash2,
2026
+ Mail: Mail2,
2027
+ Calendar: Calendar2,
2028
+ CheckSquare: CheckSquare2,
2029
+ ChevronDown: ChevronDown3,
2030
+ Circle: Circle2,
2031
+ Upload: Upload2,
2032
+ Link: Link2,
1968
2033
  List,
1969
2034
  Folder,
1970
- Braces
2035
+ Braces: Braces2
1971
2036
  };
1972
2037
  function FieldPalette() {
1973
2038
  const [search, setSearch] = useState6("");
@@ -2320,7 +2385,9 @@ function BuilderShell({ loadSlug }) {
2320
2385
  setBlockDefs(
2321
2386
  (json.docs ?? []).map((d) => ({ id: String(d.id), slug: d.slug, name: d.name }))
2322
2387
  );
2323
- }).catch(() => {
2388
+ }).catch((err) => {
2389
+ console.error("[block-builder] Failed to load block definitions:", err);
2390
+ setLoadError("Could not load block definitions. Please refresh the page.");
2324
2391
  });
2325
2392
  }, []);
2326
2393
  const loadVersionsForSlug = useCallback5(async (slug) => {
@@ -2437,10 +2504,10 @@ function BuilderShell({ loadSlug }) {
2437
2504
 
2438
2505
  // src/components/BlockBuilderNavLink/index.tsx
2439
2506
  import React17 from "react";
2440
- import Link2 from "next/link";
2507
+ import Link3 from "next/link";
2441
2508
  function BlockBuilderNavLink() {
2442
2509
  return /* @__PURE__ */ React17.createElement("div", { style: { padding: "0 16px", marginTop: "8px" } }, /* @__PURE__ */ React17.createElement(
2443
- Link2,
2510
+ Link3,
2444
2511
  {
2445
2512
  href: "/block-builder",
2446
2513
  target: "_blank",
package/dist/index.cjs CHANGED
@@ -157,18 +157,23 @@ function dbLayoutField(fieldName = "dbLayout", tabLabel = "DB Layout") {
157
157
  },
158
158
  fields: [
159
159
  {
160
- name: "blockDefinition",
161
- type: "relationship",
162
- relationTo: "block-definitions",
163
- required: true,
164
- admin: { description: "Which block type to use." }
165
- },
166
- {
167
- name: "blockVersion",
168
- type: "relationship",
169
- relationTo: "block-definition-versions",
170
- required: true,
171
- admin: { description: "Which schema version to use." }
160
+ type: "row",
161
+ fields: [
162
+ {
163
+ name: "blockDefinition",
164
+ type: "relationship",
165
+ relationTo: "block-definitions",
166
+ required: true,
167
+ admin: { description: "Which block type to use.", width: "50%" }
168
+ },
169
+ {
170
+ name: "blockVersion",
171
+ type: "relationship",
172
+ relationTo: "block-definition-versions",
173
+ required: true,
174
+ admin: { description: "Which schema version to use.", width: "50%" }
175
+ }
176
+ ]
172
177
  },
173
178
  {
174
179
  name: "instanceId",
@@ -360,7 +365,15 @@ var generateEndpoint = async (req) => {
360
365
  var import_uuid = require("uuid");
361
366
  var REVERSE_TYPE_MAP = {
362
367
  richtext: "richText",
363
- image: "upload"
368
+ image: "upload",
369
+ file: "upload",
370
+ // file and image both map to upload in the builder
371
+ multiselect: "select",
372
+ // builder has no multiselect — nearest equivalent
373
+ url: "text",
374
+ // builder has no url field — falls back to text
375
+ color: "text"
376
+ // builder has no color field — falls back to text
364
377
  };
365
378
  var VALID_BUILDER_TYPES = /* @__PURE__ */ new Set([
366
379
  "text",
@@ -387,7 +400,11 @@ var VALID_BUILDER_TYPES = /* @__PURE__ */ new Set([
387
400
  function fieldToBuilderField(raw) {
388
401
  const rawType = String(raw.type ?? "text");
389
402
  const mappedType = REVERSE_TYPE_MAP[rawType] ?? rawType;
390
- const fieldType = VALID_BUILDER_TYPES.has(mappedType) ? mappedType : "text";
403
+ const isKnown = VALID_BUILDER_TYPES.has(mappedType);
404
+ if (!isKnown) {
405
+ console.warn(`[block-builder] Unknown field type "${rawType}" \u2014 rendering as "text". Add a mapping in REVERSE_TYPE_MAP.`);
406
+ }
407
+ const fieldType = isKnown ? mappedType : "text";
391
408
  const field = {
392
409
  id: (0, import_uuid.v4)(),
393
410
  type: fieldType,
@@ -424,7 +441,7 @@ function fieldToBuilderField(raw) {
424
441
  function slugToInterfaceName(slug) {
425
442
  return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
426
443
  }
427
- function schemaToBuilderBlock(slug, name, labels, schemaFields) {
444
+ function schemaToBuilderBlock(slug, _name, labels, schemaFields) {
428
445
  return {
429
446
  id: (0, import_uuid.v4)(),
430
447
  slug,
package/dist/index.js CHANGED
@@ -129,18 +129,23 @@ function dbLayoutField(fieldName = "dbLayout", tabLabel = "DB Layout") {
129
129
  },
130
130
  fields: [
131
131
  {
132
- name: "blockDefinition",
133
- type: "relationship",
134
- relationTo: "block-definitions",
135
- required: true,
136
- admin: { description: "Which block type to use." }
137
- },
138
- {
139
- name: "blockVersion",
140
- type: "relationship",
141
- relationTo: "block-definition-versions",
142
- required: true,
143
- admin: { description: "Which schema version to use." }
132
+ type: "row",
133
+ fields: [
134
+ {
135
+ name: "blockDefinition",
136
+ type: "relationship",
137
+ relationTo: "block-definitions",
138
+ required: true,
139
+ admin: { description: "Which block type to use.", width: "50%" }
140
+ },
141
+ {
142
+ name: "blockVersion",
143
+ type: "relationship",
144
+ relationTo: "block-definition-versions",
145
+ required: true,
146
+ admin: { description: "Which schema version to use.", width: "50%" }
147
+ }
148
+ ]
144
149
  },
145
150
  {
146
151
  name: "instanceId",
@@ -332,7 +337,15 @@ var generateEndpoint = async (req) => {
332
337
  import { v4 as uuidv4 } from "uuid";
333
338
  var REVERSE_TYPE_MAP = {
334
339
  richtext: "richText",
335
- image: "upload"
340
+ image: "upload",
341
+ file: "upload",
342
+ // file and image both map to upload in the builder
343
+ multiselect: "select",
344
+ // builder has no multiselect — nearest equivalent
345
+ url: "text",
346
+ // builder has no url field — falls back to text
347
+ color: "text"
348
+ // builder has no color field — falls back to text
336
349
  };
337
350
  var VALID_BUILDER_TYPES = /* @__PURE__ */ new Set([
338
351
  "text",
@@ -359,7 +372,11 @@ var VALID_BUILDER_TYPES = /* @__PURE__ */ new Set([
359
372
  function fieldToBuilderField(raw) {
360
373
  const rawType = String(raw.type ?? "text");
361
374
  const mappedType = REVERSE_TYPE_MAP[rawType] ?? rawType;
362
- const fieldType = VALID_BUILDER_TYPES.has(mappedType) ? mappedType : "text";
375
+ const isKnown = VALID_BUILDER_TYPES.has(mappedType);
376
+ if (!isKnown) {
377
+ console.warn(`[block-builder] Unknown field type "${rawType}" \u2014 rendering as "text". Add a mapping in REVERSE_TYPE_MAP.`);
378
+ }
379
+ const fieldType = isKnown ? mappedType : "text";
363
380
  const field = {
364
381
  id: uuidv4(),
365
382
  type: fieldType,
@@ -396,7 +413,7 @@ function fieldToBuilderField(raw) {
396
413
  function slugToInterfaceName(slug) {
397
414
  return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
398
415
  }
399
- function schemaToBuilderBlock(slug, name, labels, schemaFields) {
416
+ function schemaToBuilderBlock(slug, _name, labels, schemaFields) {
400
417
  return {
401
418
  id: uuidv4(),
402
419
  slug,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@nextbridgehq/payload-block-builder",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Block Builder for Payload CMS",
5
- "keywords": ["payload", "payload-plugin", "cms", "block-builder", "dynamic-blocks"],
5
+ "keywords": ["payload", "payloadcms", "payload-plugin", "cms", "block-builder", "dynamic-blocks"],
6
6
  "homepage": "https://github.com/nextbridgehq/block-builder",
7
7
  "repository": {
8
8
  "type": "git",