@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/README.md +181 -35
- package/dist/bin/init.js +79 -25
- package/dist/client.cjs +118 -65
- package/dist/client.js +142 -75
- package/dist/index.cjs +32 -15
- package/dist/index.js +32 -15
- package/package.json +2 -2
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
|
-
|
|
181
|
-
|
|
229
|
+
case "relationship": {
|
|
230
|
+
const collection = field.collection ?? "media";
|
|
231
|
+
return /* @__PURE__ */ React.createElement(
|
|
232
|
+
RelationshipPicker,
|
|
182
233
|
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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) },
|
|
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" },
|
|
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
|
-
|
|
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:
|
|
1544
|
-
textarea:
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
upload:
|
|
1552
|
-
|
|
1553
|
-
|
|
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" },
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
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
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
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,
|
|
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.
|
|
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",
|