@aduptive/strapi-blockscene 1.0.0-alpha.1 → 2.0.0-alpha.1
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/admin.cjs +955 -155
- package/dist/admin.mjs +976 -153
- package/package.json +9 -9
package/dist/admin.cjs
CHANGED
|
@@ -4,6 +4,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __getProtoOf = Object.getPrototypeOf;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
7
10
|
var __export = (target, all) => {
|
|
8
11
|
for (var name in all)
|
|
9
12
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -26,15 +29,122 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
29
|
));
|
|
27
30
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
31
|
|
|
29
|
-
//
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
// server/groups.js
|
|
33
|
+
var require_groups = __commonJS({
|
|
34
|
+
"server/groups.js"(exports, module2) {
|
|
35
|
+
"use strict";
|
|
36
|
+
var UID = /^[a-z0-9-]+\.[a-z0-9-]+$/;
|
|
37
|
+
function safeGroups2(value, knownUids) {
|
|
38
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const [open, close] of Object.entries(value)) {
|
|
41
|
+
if (!UID.test(open) || typeof close !== "string" || !UID.test(close) || open === close) continue;
|
|
42
|
+
if (knownUids && (!knownUids.includes(open) || !knownUids.includes(close))) continue;
|
|
43
|
+
out[open] = close;
|
|
44
|
+
}
|
|
45
|
+
for (const close of Object.values(out)) if (out[close]) return null;
|
|
46
|
+
return Object.keys(out).length ? out : null;
|
|
47
|
+
}
|
|
48
|
+
var uidOf = (row) => typeof row?.__component === "string" ? row.__component : "";
|
|
49
|
+
var isOpener = (row, g) => Boolean(g) && Object.prototype.hasOwnProperty.call(g, uidOf(row));
|
|
50
|
+
var isClose = (row, g) => Boolean(g) && Object.values(g).includes(uidOf(row));
|
|
51
|
+
var blockKey2 = (row) => row && row.id != null ? `${row.__component}#${row.id}` : row && row.__temp_key__ != null ? String(row.__temp_key__) : null;
|
|
52
|
+
function validateGroups2(rows, g) {
|
|
53
|
+
const errors = [];
|
|
54
|
+
if (!g || !Array.isArray(rows)) return errors;
|
|
55
|
+
const stack = [];
|
|
56
|
+
rows.forEach((row, index) => {
|
|
57
|
+
const uid = uidOf(row);
|
|
58
|
+
if (isOpener(row, g)) {
|
|
59
|
+
stack.push({ index, uid, close: g[uid] });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!isClose(row, g)) return;
|
|
63
|
+
const top = stack[stack.length - 1];
|
|
64
|
+
if (!top) errors.push({ code: "closeBeforeOpen", index, uid });
|
|
65
|
+
else if (top.close !== uid) errors.push({ code: "mismatch", index, uid, expected: top.close, open: top.uid, openIndex: top.index });
|
|
66
|
+
else stack.pop();
|
|
67
|
+
});
|
|
68
|
+
for (const open of stack) errors.push({ code: "unclosed", index: open.index, uid: open.uid, expected: open.close });
|
|
69
|
+
return errors;
|
|
70
|
+
}
|
|
71
|
+
function groupRange2(rows, start, g) {
|
|
72
|
+
const stack = [g[uidOf(rows[start])]];
|
|
73
|
+
for (let i = start + 1; i < rows.length; i++) {
|
|
74
|
+
const row = rows[i];
|
|
75
|
+
if (isOpener(row, g)) stack.push(g[uidOf(row)]);
|
|
76
|
+
else if (isClose(row, g) && uidOf(row) === stack[stack.length - 1]) {
|
|
77
|
+
stack.pop();
|
|
78
|
+
if (!stack.length) return [start, i];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return [start, rows.length - 1];
|
|
82
|
+
}
|
|
83
|
+
function groupRows2(rows, g, depth = 0, offset = 0) {
|
|
84
|
+
const out = [];
|
|
85
|
+
let i = 0;
|
|
86
|
+
while (i < rows.length) {
|
|
87
|
+
const row = rows[i];
|
|
88
|
+
if (isOpener(row, g)) {
|
|
89
|
+
const [, end] = groupRange2(rows, i, g);
|
|
90
|
+
const closed = uidOf(rows[end]) === g[uidOf(row)] && end > i;
|
|
91
|
+
out.push({
|
|
92
|
+
type: "group",
|
|
93
|
+
key: blockKey2(row),
|
|
94
|
+
closeKey: closed ? blockKey2(rows[end]) : null,
|
|
95
|
+
row,
|
|
96
|
+
index: offset + i,
|
|
97
|
+
depth,
|
|
98
|
+
children: groupRows2(rows.slice(i + 1, closed ? end : end + 1), g, depth + 1, offset + i + 1)
|
|
99
|
+
});
|
|
100
|
+
i = end + 1;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
out.push({ type: "block", key: blockKey2(row), row, index: offset + i, depth, stray: isClose(row, g) });
|
|
104
|
+
i++;
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
function topLevelRanges2(rows, g) {
|
|
109
|
+
const ranges = [];
|
|
110
|
+
let i = 0;
|
|
111
|
+
while (i < rows.length) {
|
|
112
|
+
const end = isOpener(rows[i], g) ? groupRange2(rows, i, g)[1] : i;
|
|
113
|
+
ranges.push([i, end]);
|
|
114
|
+
i = end + 1;
|
|
115
|
+
}
|
|
116
|
+
return ranges;
|
|
117
|
+
}
|
|
118
|
+
function moveGroup2(rows, key, direction, g) {
|
|
119
|
+
if (!g) return null;
|
|
120
|
+
const ranges = topLevelRanges2(rows, g);
|
|
121
|
+
const at = ranges.findIndex(([s]) => blockKey2(rows[s]) === String(key));
|
|
122
|
+
const to = at + (direction === "up" ? -1 : 1);
|
|
123
|
+
if (at < 0 || to < 0 || to >= ranges.length) return null;
|
|
124
|
+
const [a, b] = direction === "up" ? [ranges[to], ranges[at]] : [ranges[at], ranges[to]];
|
|
125
|
+
return [...rows.slice(0, a[0]), ...rows.slice(b[0], b[1] + 1), ...rows.slice(a[0], a[1] + 1), ...rows.slice(b[1] + 1)];
|
|
126
|
+
}
|
|
127
|
+
function removeGroup2(rows, key, g) {
|
|
128
|
+
if (!g) return null;
|
|
129
|
+
const start = rows.findIndex((row) => blockKey2(row) === String(key));
|
|
130
|
+
if (start < 0 || !isOpener(rows[start], g)) return null;
|
|
131
|
+
const [, end] = groupRange2(rows, start, g);
|
|
132
|
+
return [...rows.slice(0, start), ...rows.slice(end + 1)];
|
|
133
|
+
}
|
|
134
|
+
module2.exports = { safeGroups: safeGroups2, validateGroups: validateGroups2, groupRange: groupRange2, groupRows: groupRows2, topLevelRanges: topLevelRanges2, moveGroup: moveGroup2, removeGroup: removeGroup2, blockKey: blockKey2, isOpener, isClose };
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// admin/strapi5.tsx
|
|
139
|
+
var strapi5_exports = {};
|
|
140
|
+
__export(strapi5_exports, {
|
|
141
|
+
default: () => strapi5_default
|
|
33
142
|
});
|
|
34
|
-
module.exports = __toCommonJS(
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
143
|
+
module.exports = __toCommonJS(strapi5_exports);
|
|
144
|
+
var React6 = __toESM(require("react"));
|
|
145
|
+
var import_design_system6 = require("@strapi/design-system");
|
|
146
|
+
var import_admin3 = require("@strapi/strapi/admin");
|
|
147
|
+
var import_strapi_admin2 = require("@strapi/content-manager/strapi-admin");
|
|
38
148
|
|
|
39
149
|
// admin/Gallery.tsx
|
|
40
150
|
var React = __toESM(require("react"));
|
|
@@ -44,6 +154,25 @@ var import_styled_components = __toESM(require("styled-components"));
|
|
|
44
154
|
|
|
45
155
|
// admin/model.mjs
|
|
46
156
|
var import_fractional_indexing = require("fractional-indexing");
|
|
157
|
+
function componentDefaults(schema, components, stack = []) {
|
|
158
|
+
if (!schema || stack.includes(schema)) throw new Error("Invalid recursive component defaults");
|
|
159
|
+
const row = {};
|
|
160
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
161
|
+
if (Object.hasOwn(attr, "default")) {
|
|
162
|
+
const value = structuredClone(attr.default);
|
|
163
|
+
if (attr.type === "password") row[name] = "";
|
|
164
|
+
else if (attr.type === "relation") row[name] = { connect: [], disconnect: [] };
|
|
165
|
+
else if (value !== null || attr.type === "boolean") row[name] = value;
|
|
166
|
+
} else if (attr.type === "component" && attr.required) {
|
|
167
|
+
const create = () => componentDefaults(components[attr.component], components, [...stack, schema]);
|
|
168
|
+
if (attr.repeatable) {
|
|
169
|
+
const keys = (0, import_fractional_indexing.generateNKeysBetween)(null, null, attr.min || 0);
|
|
170
|
+
row[name] = keys.map((key) => ({ ...create(), __temp_key__: key }));
|
|
171
|
+
} else row[name] = create();
|
|
172
|
+
} else if (attr.type === "dynamiczone" && attr.required) row[name] = [];
|
|
173
|
+
}
|
|
174
|
+
return row;
|
|
175
|
+
}
|
|
47
176
|
function editableZones(schema, values, canEdit, disabled = false) {
|
|
48
177
|
if (disabled) return [];
|
|
49
178
|
return Object.entries(schema?.attributes || {}).filter(([name, attr]) => attr.type === "dynamiczone" && !attr.conditions && canEdit(name)).map(([name, attr]) => {
|
|
@@ -1217,7 +1346,10 @@ function Card({ entry, palette, showFields, onSelect }) {
|
|
|
1217
1346
|
showFields && entry.fields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_design_system.Typography, { variant: "pi", textColor: "neutral500", style: { fontStyle: "italic" }, "data-testid": "block-fields", children: entry.fields.map((f) => `${f.name} (${f.type})`).join(", ") })
|
|
1218
1347
|
] });
|
|
1219
1348
|
}
|
|
1220
|
-
function
|
|
1349
|
+
function PickerModal({ zone, components, open, onOpenChange, onSelect, Modal: Modal3, Toggle, get }) {
|
|
1350
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ZoneGallery, { zone, components, Modal: Modal3, Toggle, get, controlled: { open, onOpenChange, onSelect } });
|
|
1351
|
+
}
|
|
1352
|
+
function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, controlled }) {
|
|
1221
1353
|
const t = useMessages();
|
|
1222
1354
|
const [ownOpen, setOwnOpen] = React.useState(false);
|
|
1223
1355
|
const open = controlled ? controlled.open : ownOpen;
|
|
@@ -1281,7 +1413,7 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
|
|
|
1281
1413
|
remember(SHOW_FIELDS_KEY, String(value));
|
|
1282
1414
|
};
|
|
1283
1415
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1284
|
-
|
|
1416
|
+
Modal3,
|
|
1285
1417
|
{
|
|
1286
1418
|
open,
|
|
1287
1419
|
onOpenChange: setOpen,
|
|
@@ -1410,46 +1542,708 @@ function Gallery({ zones, editor, docKey, contentType, userId, ...props }) {
|
|
|
1410
1542
|
] }, zone.name)) });
|
|
1411
1543
|
}
|
|
1412
1544
|
|
|
1413
|
-
// admin/
|
|
1545
|
+
// admin/PagePreview.tsx
|
|
1546
|
+
var React3 = __toESM(require("react"));
|
|
1547
|
+
var import_react_dom2 = require("react-dom");
|
|
1548
|
+
var import_styled_components2 = __toESM(require("styled-components"));
|
|
1549
|
+
var import_design_system3 = require("@strapi/design-system");
|
|
1550
|
+
var import_admin2 = require("@strapi/strapi/admin");
|
|
1551
|
+
var import_design_system4 = require("@strapi/design-system");
|
|
1552
|
+
var import_strapi_admin = require("@strapi/content-manager/strapi-admin");
|
|
1553
|
+
|
|
1554
|
+
// admin/FieldEditorModal.tsx
|
|
1414
1555
|
var React2 = __toESM(require("react"));
|
|
1556
|
+
var import_admin = require("@strapi/strapi/admin");
|
|
1415
1557
|
var import_design_system2 = require("@strapi/design-system");
|
|
1416
|
-
var import_styled_components2 = __toESM(require("styled-components"));
|
|
1417
1558
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1559
|
+
var Actions = ({ onApply, onCancel }) => {
|
|
1560
|
+
const t = useMessages();
|
|
1561
|
+
const value = (0, import_admin.useForm)("BlocksceneFieldEditor", (state) => state.values.value);
|
|
1562
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.Flex, { gap: 2, justifyContent: "flex-end", children: [
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Button, { variant: "tertiary", onClick: onCancel, children: t.cancel }),
|
|
1564
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Button, { onClick: () => onApply(value ?? null), "data-testid": "field-editor-apply", children: t.apply })
|
|
1565
|
+
] });
|
|
1566
|
+
};
|
|
1567
|
+
function FieldEditorModal({ label, attribute, value, onApply, onCancel }) {
|
|
1568
|
+
const t = useMessages();
|
|
1569
|
+
const customFields = (0, import_admin.useStrapiApp)("BlocksceneFieldEditor", (state) => state.customFields);
|
|
1570
|
+
const [Input, setInput] = React2.useState(void 0);
|
|
1571
|
+
const isCustom = Boolean(attribute?.customField);
|
|
1572
|
+
const isText = attribute?.type === "string" || attribute?.type === "text";
|
|
1573
|
+
React2.useEffect(() => {
|
|
1574
|
+
let active = true;
|
|
1575
|
+
if (!isCustom) {
|
|
1576
|
+
setInput(null);
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
const field = customFields?.get?.(attribute.customField);
|
|
1580
|
+
if (!field?.components?.Input) {
|
|
1581
|
+
setInput(null);
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
Promise.resolve(field.components.Input()).then((mod) => {
|
|
1585
|
+
if (active) setInput(() => mod.default || mod);
|
|
1586
|
+
}).catch(() => {
|
|
1587
|
+
if (active) setInput(null);
|
|
1588
|
+
});
|
|
1589
|
+
return () => {
|
|
1590
|
+
active = false;
|
|
1591
|
+
};
|
|
1592
|
+
}, [attribute, customFields, isCustom]);
|
|
1593
|
+
const supported = isText || isCustom && Input;
|
|
1594
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Modal.Root, { open: true, onOpenChange: (open) => {
|
|
1595
|
+
if (!open) onCancel();
|
|
1596
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.Modal.Content, { "aria-label": `${t.editText} ${label}`, children: [
|
|
1597
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Modal.Header, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.Modal.Title, { children: [
|
|
1598
|
+
t.editText,
|
|
1599
|
+
" \xB7 ",
|
|
1600
|
+
label
|
|
1601
|
+
] }) }),
|
|
1602
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_admin.Form, { initialValues: { value: value ?? (isText ? "" : null) }, method: "PUT", onSubmit: () => {
|
|
1603
|
+
}, children: [
|
|
1604
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Modal.Body, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.Flex, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "field-editor-modal", children: [
|
|
1605
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Typography, { variant: "pi", textColor: "neutral600", children: t.editHelp }),
|
|
1606
|
+
isCustom && Input === void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Typography, { children: t.loading }),
|
|
1607
|
+
isCustom && Input && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Input, { name: "value", attribute, label, type: attribute.type }),
|
|
1608
|
+
isText && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_admin.InputRenderer, { type: attribute.type === "text" ? "text" : "string", name: "value", label }),
|
|
1609
|
+
(isCustom && Input === null || !isCustom && !isText) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Typography, { role: "alert", textColor: "danger600", children: t.editUnsupported })
|
|
1610
|
+
] }) }),
|
|
1611
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Modal.Footer, { children: supported ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Actions, { onApply, onCancel }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Button, { variant: "tertiary", onClick: onCancel, children: t.close }) })
|
|
1612
|
+
] })
|
|
1613
|
+
] }) });
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// admin/preview.mjs
|
|
1617
|
+
var import_groups = __toESM(require_groups(), 1);
|
|
1618
|
+
var PROTOCOL = "blockscene:page-preview:v1";
|
|
1619
|
+
var TEXT_TYPES = ["string", "text"];
|
|
1620
|
+
var RICH_TYPES = ["richtext", "blocks", "customField"];
|
|
1621
|
+
var EDITABLE_TYPES = [...TEXT_TYPES, ...RICH_TYPES];
|
|
1622
|
+
var SCALAR = ["string", "text", "richtext", "email", "enumeration", "integer", "biginteger", "float", "decimal", "boolean", "date", "datetime", "time", "json", "blocks", "uid", "customField"];
|
|
1623
|
+
var blockKey = (row) => row && row.id != null ? `${row.__component}#${row.id}` : row && row.__temp_key__ != null ? String(row.__temp_key__) : null;
|
|
1624
|
+
function projectRow(row, schema, components, cmsOrigin, depth = 0) {
|
|
1625
|
+
if (!row || !schema || depth > 3) return null;
|
|
1626
|
+
const out = {};
|
|
1627
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
1628
|
+
const value = row[name];
|
|
1629
|
+
if (attr.type === "password" || attr.type === "relation" || attr.private) continue;
|
|
1630
|
+
if (SCALAR.includes(attr.type)) {
|
|
1631
|
+
if (value !== void 0) out[name] = typeof value === "string" ? value.slice(0, 2e5) : value;
|
|
1632
|
+
} else if (attr.type === "media") {
|
|
1633
|
+
const files = (attr.multiple ? Array.isArray(value) ? value : [] : value ? [value] : []).map((file) => projectMedia(file, cmsOrigin)).filter(Boolean);
|
|
1634
|
+
out[name] = attr.multiple ? files : files[0] || null;
|
|
1635
|
+
} else if (attr.type === "component") {
|
|
1636
|
+
const child = components?.[attr.component];
|
|
1637
|
+
out[name] = attr.repeatable ? (Array.isArray(value) ? value : []).slice(0, 100).map((item, i) => ({ ...projectRow(item, child, components, cmsOrigin, depth + 1), __key: blockKey(item) ?? String(i) })) : projectRow(value, child, components, cmsOrigin, depth + 1);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return out;
|
|
1641
|
+
}
|
|
1642
|
+
function projectMedia(file, cmsOrigin) {
|
|
1643
|
+
if (!file || typeof file.url !== "string") return null;
|
|
1644
|
+
try {
|
|
1645
|
+
const url = new URL(file.url, cmsOrigin);
|
|
1646
|
+
if (!["http:", "https:"].includes(url.protocol)) return null;
|
|
1647
|
+
return { url: url.href, alternativeText: String(file.alternativeText || ""), caption: String(file.caption || ""), width: Number(file.width) || 0, height: Number(file.height) || 0, mime: String(file.mime || "") };
|
|
1648
|
+
} catch {
|
|
1649
|
+
return null;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
function resolveField(uid, field, components) {
|
|
1653
|
+
if (typeof field !== "string" || field.length > 200) return null;
|
|
1654
|
+
let schema = components?.[uid];
|
|
1655
|
+
const parts = field.split(".");
|
|
1656
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1657
|
+
const attr = schema?.attributes?.[parts[i]];
|
|
1658
|
+
if (!attr || attr.private === true) return null;
|
|
1659
|
+
if (i === parts.length - 1) return attr;
|
|
1660
|
+
if (attr.type !== "component") return null;
|
|
1661
|
+
if (attr.repeatable) {
|
|
1662
|
+
if (!/^\d{1,3}$/.test(parts[i + 1] || "")) return null;
|
|
1663
|
+
i++;
|
|
1664
|
+
}
|
|
1665
|
+
schema = components?.[attr.component];
|
|
1666
|
+
}
|
|
1667
|
+
return null;
|
|
1668
|
+
}
|
|
1669
|
+
function editableFields(uid, row, components) {
|
|
1670
|
+
const schema = components?.[uid];
|
|
1671
|
+
const out = [];
|
|
1672
|
+
const SYSTEM = ["id", "documentId", "locale", "createdAt", "updatedAt", "publishedAt", "createdBy", "updatedBy", "localizations", "__component", "__temp_key__"];
|
|
1673
|
+
const push = (name, attr, label) => {
|
|
1674
|
+
if (!SYSTEM.includes(name.split(".").pop()) && attr?.private !== true && EDITABLE_TYPES.includes(attr?.type)) out.push({ name, type: attr.type, label });
|
|
1675
|
+
};
|
|
1676
|
+
for (const [name, attr] of Object.entries(schema?.attributes || {})) {
|
|
1677
|
+
if (attr.type === "component" && attr.repeatable) {
|
|
1678
|
+
(Array.isArray(row?.[name]) ? row[name] : []).slice(0, 50).forEach((_, i) => {
|
|
1679
|
+
for (const [child, childAttr] of Object.entries(components?.[attr.component]?.attributes || {})) push(`${name}.${i}.${child}`, childAttr, `${name} ${i + 1} \xB7 ${child}`);
|
|
1680
|
+
});
|
|
1681
|
+
} else push(name, attr, name);
|
|
1682
|
+
}
|
|
1683
|
+
return out;
|
|
1684
|
+
}
|
|
1685
|
+
function mediaFields(uid, row, components) {
|
|
1686
|
+
const media = {};
|
|
1687
|
+
for (const [name, attr] of Object.entries(components?.[uid]?.attributes || {})) {
|
|
1688
|
+
if (attr.type === "media" && attr.private !== true) media[name] = { required: attr.required === true, multiple: attr.multiple === true, present: attr.multiple ? Array.isArray(row?.[name]) && row[name].length > 0 : Boolean(row?.[name]) };
|
|
1689
|
+
}
|
|
1690
|
+
return media;
|
|
1691
|
+
}
|
|
1692
|
+
function projectPage(rows, components, cmsOrigin) {
|
|
1693
|
+
return (Array.isArray(rows) ? rows : []).slice(0, 200).flatMap((row, index) => {
|
|
1694
|
+
const uid = row?.__component;
|
|
1695
|
+
const key = blockKey(row);
|
|
1696
|
+
const schema = components?.[uid];
|
|
1697
|
+
if (!uid || key == null || !schema) return [];
|
|
1698
|
+
return [{
|
|
1699
|
+
key,
|
|
1700
|
+
index,
|
|
1701
|
+
uid,
|
|
1702
|
+
label: schema.info?.displayName || uid,
|
|
1703
|
+
data: projectRow(row, schema, components, cmsOrigin),
|
|
1704
|
+
fields: editableFields(uid, row, components),
|
|
1705
|
+
media: mediaFields(uid, row, components)
|
|
1706
|
+
}];
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
var validateFocus = (uid, field, components) => {
|
|
1710
|
+
const attr = resolveField(uid, field, components);
|
|
1711
|
+
return attr && EDITABLE_TYPES.includes(attr.type) ? attr : null;
|
|
1712
|
+
};
|
|
1713
|
+
function validateEdit(uid, field, value, components) {
|
|
1714
|
+
if (typeof value !== "string" || value.length > 5e3) return null;
|
|
1715
|
+
const attr = resolveField(uid, field, components);
|
|
1716
|
+
return attr && TEXT_TYPES.includes(attr.type) ? value.replace(/[\r\n]+/g, " ") : null;
|
|
1717
|
+
}
|
|
1718
|
+
var mediaAttribute = (uid, field, components) => {
|
|
1719
|
+
const attr = resolveField(uid, field, components);
|
|
1720
|
+
return attr?.type === "media" ? attr : null;
|
|
1721
|
+
};
|
|
1722
|
+
function isPreviewMessage(event, origin, source, channel, type) {
|
|
1723
|
+
return event.origin === origin && event.source === source && event.data?.protocol === PROTOCOL && event.data?.channel === channel && event.data?.type === type;
|
|
1724
|
+
}
|
|
1725
|
+
function insertIndex(rows, after) {
|
|
1726
|
+
if (after === null || after === void 0) return 0;
|
|
1727
|
+
const index = (Array.isArray(rows) ? rows : []).findIndex((row) => blockKey(row) === String(after));
|
|
1728
|
+
return index < 0 ? -1 : index + 1;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// admin/PagePreview.tsx
|
|
1732
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1733
|
+
var MODE_KEY = "blockscene:page-mode";
|
|
1734
|
+
var RATIO_KEY = "blockscene:page-split-ratio";
|
|
1735
|
+
var MIN_PANE = 360;
|
|
1736
|
+
var MIN_FORM = 520;
|
|
1737
|
+
var NARROW = 960;
|
|
1738
|
+
var KEY_STEP = 32;
|
|
1739
|
+
var MODAL_TYPES = ["customField", "string", "text"];
|
|
1740
|
+
var read = (key) => {
|
|
1741
|
+
try {
|
|
1742
|
+
return localStorage.getItem(key);
|
|
1743
|
+
} catch {
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
var write = (key, value) => {
|
|
1748
|
+
try {
|
|
1749
|
+
localStorage.setItem(key, value);
|
|
1750
|
+
} catch {
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
var clampWidth = (width, vw) => Math.round(Math.min(Math.max(width, MIN_PANE), Math.max(MIN_PANE, vw - MIN_FORM)));
|
|
1754
|
+
var getIn = (object, path) => path.split(".").reduce((acc, part) => acc == null ? acc : acc[part], object);
|
|
1755
|
+
var Pane = import_styled_components2.default.aside`
|
|
1756
|
+
position: fixed; top: 0; bottom: 0; z-index: 2; display: flex; flex-direction: column;
|
|
1757
|
+
background: ${({ theme }) => theme.colors.neutral0}; border-left: 1px solid ${({ theme }) => theme.colors.neutral200};
|
|
1758
|
+
box-shadow: ${({ theme }) => theme.shadows.popupShadow};
|
|
1759
|
+
`;
|
|
1760
|
+
function useMainRect() {
|
|
1761
|
+
const [rect, setRect] = React3.useState(() => ({ left: 0, width: window.innerWidth }));
|
|
1762
|
+
React3.useEffect(() => {
|
|
1763
|
+
const main = document.getElementById("main-content");
|
|
1764
|
+
const measure = () => {
|
|
1765
|
+
const r = main?.getBoundingClientRect();
|
|
1766
|
+
setRect(r && r.width > 0 ? { left: Math.round(r.left), width: Math.round(r.width) } : { left: 0, width: window.innerWidth });
|
|
1767
|
+
};
|
|
1768
|
+
measure();
|
|
1769
|
+
const observer = main && "ResizeObserver" in window ? new ResizeObserver(measure) : null;
|
|
1770
|
+
if (main) observer?.observe(main);
|
|
1771
|
+
window.addEventListener("resize", measure);
|
|
1772
|
+
return () => {
|
|
1773
|
+
observer?.disconnect();
|
|
1774
|
+
window.removeEventListener("resize", measure);
|
|
1775
|
+
};
|
|
1776
|
+
}, []);
|
|
1777
|
+
return rect;
|
|
1778
|
+
}
|
|
1779
|
+
var Handle = import_styled_components2.default.div`
|
|
1780
|
+
position: absolute; top: 0; bottom: 0; left: -5px; width: 10px; cursor: col-resize; z-index: 3; touch-action: none;
|
|
1781
|
+
&::after { content: ''; position: absolute; top: 0; bottom: 0; left: 4px; width: 2px; }
|
|
1782
|
+
&:hover::after, &[data-dragging='true']::after, &:focus-visible::after { background: ${({ theme }) => theme.colors.primary600}; }
|
|
1783
|
+
&:focus-visible { outline: none; }
|
|
1784
|
+
`;
|
|
1785
|
+
var Frame2 = import_styled_components2.default.iframe` flex: 1; width: 100%; border: 0; background: white; `;
|
|
1786
|
+
var SPLIT_STYLE = `
|
|
1787
|
+
body.bp-split #main-content { padding-right: calc(var(--bp-pane, 50vw) + 1.6rem) !important; }
|
|
1788
|
+
body.bp-split [data-bp-grid] { display: flex !important; flex-direction: column-reverse; gap: 1.6rem; }
|
|
1789
|
+
body.bp-split [data-bp-panels] { width: 100%; }
|
|
1790
|
+
body.bp-split [data-bp-panels] > div { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: 1.2rem; }
|
|
1791
|
+
body.bp-split [data-bp-panels] > div > * { flex: 1 1 24rem; }`;
|
|
1792
|
+
var markLayout = (anchor) => {
|
|
1793
|
+
let item = anchor;
|
|
1794
|
+
while (item?.parentElement && getComputedStyle(item.parentElement).display !== "grid") item = item.parentElement;
|
|
1795
|
+
if (!item?.parentElement) return;
|
|
1796
|
+
item.dataset.bpPanels = "";
|
|
1797
|
+
item.parentElement.dataset.bpGrid = "";
|
|
1798
|
+
};
|
|
1799
|
+
function usePreviewBase(override, model, documentId, locale) {
|
|
1800
|
+
const { get } = (0, import_admin2.useFetchClient)();
|
|
1801
|
+
const [native, setNative] = React3.useState(null);
|
|
1802
|
+
React3.useEffect(() => {
|
|
1803
|
+
if (override || !model) return;
|
|
1804
|
+
let active = true;
|
|
1805
|
+
const query = new URLSearchParams({ status: "draft", ...documentId ? { documentId } : {}, ...locale ? { locale } : {} });
|
|
1806
|
+
get(`/content-manager/preview/url/${model}?${query}`).then(({ data }) => {
|
|
1807
|
+
if (active && typeof data?.data?.url === "string") setNative(`${new URL(data.data.url).origin}/block-preview/page`);
|
|
1808
|
+
}).catch(() => {
|
|
1809
|
+
if (active) setNative(null);
|
|
1810
|
+
});
|
|
1811
|
+
return () => {
|
|
1812
|
+
active = false;
|
|
1813
|
+
};
|
|
1814
|
+
}, [get, model, documentId, locale, override]);
|
|
1815
|
+
return override ? { base: override, source: "custom" } : native ? { base: native, source: "native" } : { base: null, source: "none" };
|
|
1816
|
+
}
|
|
1817
|
+
function PickerDialog({ open, onOpenChange, trigger, title, children }) {
|
|
1818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system4.Modal.Root, { open, onOpenChange, children: [
|
|
1819
|
+
trigger && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system4.Modal.Trigger, { children: trigger }),
|
|
1820
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system4.Modal.Content, { children: [
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system4.Modal.Header, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system4.Modal.Title, { children: title }) }),
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system4.Modal.Body, { children })
|
|
1823
|
+
] })
|
|
1824
|
+
] });
|
|
1825
|
+
}
|
|
1826
|
+
var ACTION_TYPES = ["update", "publish"];
|
|
1827
|
+
var nativeButtons = () => document.querySelectorAll('#main-content button:not([data-testid="page-preview-pane"] button)');
|
|
1828
|
+
function ToolbarActions({ model, collectionType, documentId, locale }) {
|
|
1829
|
+
const plugins = (0, import_admin2.useStrapiApp)("BlocksceneToolbar", (state) => state.plugins);
|
|
1830
|
+
const [{ query }] = (0, import_admin2.useQueryParams)();
|
|
1831
|
+
const { document: doc, meta } = (0, import_strapi_admin.unstable_useDocument)({ model, collectionType, documentId, params: { locale } }, { skip: !documentId });
|
|
1832
|
+
const { toggleNotification } = (0, import_admin2.useNotification)();
|
|
1833
|
+
const t = useMessages();
|
|
1834
|
+
const descriptions = (plugins["content-manager"]?.apis?.getDocumentActions?.("panel") || []).filter((d) => ACTION_TYPES.includes(d.type));
|
|
1835
|
+
const props = { activeTab: query.status || "draft", model, documentId, document: doc, meta, collectionType };
|
|
1836
|
+
const run = async (action, event) => {
|
|
1837
|
+
const native = [...nativeButtons()].find((b) => b.textContent?.trim() === action.label);
|
|
1838
|
+
if (native) {
|
|
1839
|
+
native.click();
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
const mute = await action.onClick?.(event);
|
|
1843
|
+
if (action.dialog && !mute && action.dialog.type === "notification") toggleNotification({ title: action.dialog.title, message: action.dialog.content, type: action.dialog.status });
|
|
1844
|
+
};
|
|
1845
|
+
const typeOf = (action) => action.type || descriptions.find((d) => typeof action.id === "string" && action.id.startsWith(`${d.name || d.displayName}-`))?.type;
|
|
1846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_admin2.DescriptionComponentRenderer, { props, descriptions, children: (actions) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Flex, { gap: 1, "data-testid": "page-preview-actions", children: [...actions].sort((a, b) => ACTION_TYPES.indexOf(typeOf(a)) - ACTION_TYPES.indexOf(typeOf(b))).map((action) => {
|
|
1847
|
+
const reason = action.disabled ? typeOf(action) === "publish" ? t.publishDisabledHint : t.saveDisabledHint : void 0;
|
|
1848
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { title: reason, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Button, { size: "S", variant: action.variant || "default", disabled: action.disabled, loading: action.loading, "aria-description": reason, onClick: (e) => run(action, e), children: action.label }) }, action.id);
|
|
1849
|
+
}) }) });
|
|
1850
|
+
}
|
|
1851
|
+
function PagePreview({ editor, groups = null, Toggle }) {
|
|
1852
|
+
const t = useMessages();
|
|
1853
|
+
const c = (0, import_strapi_admin.unstable_useContentManagerContext)();
|
|
1854
|
+
const rbac = (0, import_strapi_admin.useDocumentRBAC)("BlockscenePagePreview", (state) => state);
|
|
1855
|
+
const values = (0, import_admin2.useForm)("BlockscenePagePreview", (state) => state.values);
|
|
1856
|
+
const onChange = (0, import_admin2.useForm)("BlockscenePagePreview", (state) => state.onChange);
|
|
1857
|
+
const components = (0, import_admin2.useStrapiApp)("BlockscenePagePreview", (state) => state.components);
|
|
1858
|
+
const MediaLibraryDialog = components?.["media-library"];
|
|
1859
|
+
const addFieldRow = (0, import_admin2.useForm)("BlockscenePagePreview", (state) => state.addFieldRow);
|
|
1860
|
+
const fields = c.layout?.edit?.layout?.flat(3) || [];
|
|
1861
|
+
const readable = (name) => c.isCreatingEntry || (rbac.canReadFields || []).includes(name);
|
|
1862
|
+
const zone = React3.useMemo(() => editableZones(c.contentType, values, readable, c.isLoading)[0]?.name, [c.contentType, values, c.isLoading, rbac.canReadFields, c.isCreatingEntry]);
|
|
1863
|
+
const zoneLabel = fields.find((field) => field.name === zone)?.label || zone || "";
|
|
1864
|
+
const canEdit = Boolean(zone && ((c.isCreatingEntry ? rbac.canCreateFields : rbac.canUpdateFields) || []).includes(zone) && fields.find((field) => field.name === zone)?.disabled !== true && !c.form?.disabled && typeof addFieldRow === "function");
|
|
1865
|
+
const [mode, setModeState] = React3.useState(() => {
|
|
1866
|
+
const v = read(MODE_KEY);
|
|
1867
|
+
return v === "split" || v === "preview" ? v : editor?.previewMode || "form";
|
|
1868
|
+
});
|
|
1869
|
+
const setMode = (next) => {
|
|
1870
|
+
setModeState(next);
|
|
1871
|
+
write(MODE_KEY, next);
|
|
1872
|
+
};
|
|
1873
|
+
const [ratio, setRatio] = React3.useState(() => {
|
|
1874
|
+
const v = Number(read(RATIO_KEY));
|
|
1875
|
+
return v > 0 && v < 1 ? v : 0.5;
|
|
1876
|
+
});
|
|
1877
|
+
const main = useMainRect();
|
|
1878
|
+
const vw = main.width;
|
|
1879
|
+
const [dragging, setDragging] = React3.useState(false);
|
|
1880
|
+
const [ready, setReady] = React3.useState(false);
|
|
1881
|
+
const [failed, setFailed] = React3.useState(false);
|
|
1882
|
+
const [attempt, retry] = React3.useReducer((n) => n + 1, 0);
|
|
1883
|
+
const [picking, setPicking] = React3.useState(null);
|
|
1884
|
+
React3.useEffect(() => {
|
|
1885
|
+
setEditing(null);
|
|
1886
|
+
setPicking(null);
|
|
1887
|
+
setInserting(null);
|
|
1888
|
+
}, [c.id, c.form?.initialValues?.locale]);
|
|
1889
|
+
const [editing, setEditing] = React3.useState(null);
|
|
1890
|
+
const [inserting, setInserting] = React3.useState(null);
|
|
1891
|
+
const { get } = (0, import_admin2.useFetchClient)();
|
|
1892
|
+
const { toggleNotification } = (0, import_admin2.useNotification)();
|
|
1893
|
+
const zoneAttr = zone ? c.contentType?.attributes?.[zone] : null;
|
|
1894
|
+
const iframe = React3.useRef(null);
|
|
1895
|
+
const loaded = React3.useRef(false);
|
|
1896
|
+
const anchor = React3.useRef(null);
|
|
1897
|
+
const channel = React3.useMemo(() => crypto.randomUUID(), []);
|
|
1898
|
+
const { base, source } = usePreviewBase(editor?.previewUrl || "", c.model, c.isCreatingEntry ? void 0 : c.id, c.form?.initialValues?.locale);
|
|
1899
|
+
const url = base ? new URL(base) : null;
|
|
1900
|
+
url?.searchParams.set("channel", channel);
|
|
1901
|
+
const origin = url?.origin || "";
|
|
1902
|
+
const narrow = vw < NARROW;
|
|
1903
|
+
const paneWidth = clampWidth(ratio * vw, vw);
|
|
1904
|
+
const resizable = mode === "split" && !narrow;
|
|
1905
|
+
const active = mode !== "form" && Boolean(url) && Boolean(zone);
|
|
1906
|
+
const rows = zone && Array.isArray(values?.[zone]) ? values[zone] : [];
|
|
1907
|
+
const latest = React3.useRef(rows);
|
|
1908
|
+
latest.current = rows;
|
|
1909
|
+
const live = React3.useRef({ mode, canEdit, zoneLabel, components: c.components, setMode, onChange });
|
|
1910
|
+
live.current = { mode, canEdit, zoneLabel, components: c.components, setMode, onChange };
|
|
1911
|
+
const send = React3.useCallback(() => {
|
|
1912
|
+
iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "update-page", blocks: projectPage(latest.current, live.current.components, window.location.origin), groups, locale: t.locale, mode: live.current.mode }, origin);
|
|
1913
|
+
}, [channel, origin]);
|
|
1914
|
+
const indexOf = (key) => latest.current.findIndex((row) => blockKey(row) === key);
|
|
1915
|
+
const path = (key, field) => `${zone}.${indexOf(key)}.${field}`;
|
|
1916
|
+
React3.useEffect(() => {
|
|
1917
|
+
if (resizable) markLayout(anchor.current);
|
|
1918
|
+
document.body.classList.toggle("bp-split", resizable);
|
|
1919
|
+
document.documentElement.style.setProperty("--bp-pane", `${paneWidth}px`);
|
|
1920
|
+
return () => document.body.classList.remove("bp-split");
|
|
1921
|
+
}, [resizable, paneWidth]);
|
|
1922
|
+
const applyWidth = (width, persist) => {
|
|
1923
|
+
const next = clampWidth(width, vw) / vw;
|
|
1924
|
+
setRatio(next);
|
|
1925
|
+
if (persist) write(RATIO_KEY, String(next));
|
|
1926
|
+
};
|
|
1927
|
+
const focusField = (index, field) => {
|
|
1928
|
+
const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[index];
|
|
1929
|
+
if (!item) return;
|
|
1930
|
+
const header = toggles(item.parentElement)[index];
|
|
1931
|
+
if (header?.getAttribute("aria-expanded") === "false") header.click();
|
|
1932
|
+
const parts = field.split(".");
|
|
1933
|
+
const escaped = CSS.escape(`${zone}.${index}.${field}`);
|
|
1934
|
+
let tries = 0;
|
|
1935
|
+
const timer = setInterval(() => {
|
|
1936
|
+
if (++tries > 40) return clearInterval(timer);
|
|
1937
|
+
let scope = item;
|
|
1938
|
+
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
1939
|
+
const nested = [...scope.querySelectorAll("button[aria-expanded][data-radix-collection-item]")].filter((b) => b !== header);
|
|
1940
|
+
const row = nested[Number(parts[i + 1])];
|
|
1941
|
+
if (!row) return;
|
|
1942
|
+
if (row.getAttribute("aria-expanded") === "false") row.click();
|
|
1943
|
+
scope = row.closest("li, div") || scope;
|
|
1944
|
+
}
|
|
1945
|
+
const target = document.querySelector(`[name="${escaped}"], label[for="${escaped}"]`);
|
|
1946
|
+
if (!target) return;
|
|
1947
|
+
const INPUT = '.ck-editor__editable, textarea, input:not([type="hidden"]), select';
|
|
1948
|
+
let container = target.matches(INPUT) ? target : target.parentElement;
|
|
1949
|
+
for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++) container = container.parentElement;
|
|
1950
|
+
const editable = container?.matches(INPUT) ? container : container?.querySelector(INPUT);
|
|
1951
|
+
if (!editable) return;
|
|
1952
|
+
if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable) return;
|
|
1953
|
+
editable.focus();
|
|
1954
|
+
if (document.activeElement !== editable) return;
|
|
1955
|
+
clearInterval(timer);
|
|
1956
|
+
editable.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
1957
|
+
}, 150);
|
|
1958
|
+
};
|
|
1959
|
+
React3.useEffect(() => {
|
|
1960
|
+
if (!active) return;
|
|
1961
|
+
setReady(false);
|
|
1962
|
+
setFailed(false);
|
|
1963
|
+
const timeout = setTimeout(() => setFailed(true), 15e3);
|
|
1964
|
+
const receive = (event) => {
|
|
1965
|
+
const is = (type) => isPreviewMessage(event, origin, iframe.current?.contentWindow, channel, type);
|
|
1966
|
+
const { mode: mode2, canEdit: canEdit2, zoneLabel: zoneLabel2, components: components2, setMode: setMode2, onChange: onChange2 } = live.current;
|
|
1967
|
+
if (is("ready")) {
|
|
1968
|
+
clearTimeout(timeout);
|
|
1969
|
+
setReady(true);
|
|
1970
|
+
setFailed(false);
|
|
1971
|
+
send();
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (is("insert") && canEdit2) {
|
|
1975
|
+
const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
|
|
1976
|
+
if (after !== null && insertIndex(latest.current, after) < 0) return;
|
|
1977
|
+
if (latest.current.length >= (zoneAttr?.max ?? Infinity)) {
|
|
1978
|
+
toggleNotification({ type: "info", message: t.zoneFull });
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
setInserting({ after });
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
if (is("move-group") && canEdit2 && groups) {
|
|
1985
|
+
const next = (0, import_groups.moveGroup)(latest.current, String(event.data.key), event.data.direction === "up" ? "up" : "down", groups);
|
|
1986
|
+
if (next) onChange2(zone, next);
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
if (is("delete-group") && canEdit2 && groups) {
|
|
1990
|
+
const next = (0, import_groups.removeGroup)(latest.current, String(event.data.key), groups);
|
|
1991
|
+
if (next) onChange2(zone, next);
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
if (is("insert-group") && canEdit2 && groups) {
|
|
1995
|
+
const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
|
|
1996
|
+
const index2 = insertIndex(latest.current, after);
|
|
1997
|
+
const uid2 = String(event.data.uid || "");
|
|
1998
|
+
const close = groups[uid2];
|
|
1999
|
+
const allowed = zoneAttr?.components || [];
|
|
2000
|
+
if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close)) return;
|
|
2001
|
+
if (latest.current.length + 2 > (zoneAttr?.max ?? Infinity)) {
|
|
2002
|
+
toggleNotification({ type: "info", message: t.zoneFull });
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
addFieldRow(zone, { ...componentDefaults(components2[uid2], components2), __component: uid2 }, index2);
|
|
2006
|
+
addFieldRow(zone, { __component: close }, index2 + 1);
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
const key = typeof event.data?.key === "string" ? event.data.key : "";
|
|
2010
|
+
const index = key ? indexOf(key) : -1;
|
|
2011
|
+
const uid = index >= 0 ? latest.current[index]?.__component : "";
|
|
2012
|
+
if (index < 0) return;
|
|
2013
|
+
if (is("select")) {
|
|
2014
|
+
const list = findZoneList(zoneLabel2);
|
|
2015
|
+
const item = list?.querySelectorAll(":scope > li")[index];
|
|
2016
|
+
const header = list ? toggles(list)[index] : null;
|
|
2017
|
+
if (header?.getAttribute("aria-expanded") === "false") header.click();
|
|
2018
|
+
item?.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
2019
|
+
if (item) {
|
|
2020
|
+
item.style.outline = "2px solid #4945ff";
|
|
2021
|
+
setTimeout(() => {
|
|
2022
|
+
item.style.outline = "";
|
|
2023
|
+
}, 1500);
|
|
2024
|
+
}
|
|
2025
|
+
if (mode2 === "preview") setMode2("split");
|
|
2026
|
+
iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "highlight", key }, origin);
|
|
2027
|
+
} else if (is("focus")) {
|
|
2028
|
+
const attr = validateFocus(uid, event.data.field, components2);
|
|
2029
|
+
if (!attr) return;
|
|
2030
|
+
if (mode2 === "preview" && canEdit2 && (attr.customField || MODAL_TYPES.includes(attr.type))) setEditing({ key, field: event.data.field, attr, label: `${components2[uid]?.info?.displayName || uid} \xB7 ${event.data.field}`, value: getIn(latest.current[index], event.data.field) });
|
|
2031
|
+
else focusField(index, event.data.field);
|
|
2032
|
+
} else if (is("media") && canEdit2) {
|
|
2033
|
+
const attr = mediaAttribute(uid, event.data.field, components2);
|
|
2034
|
+
if (attr) setPicking({ key, field: event.data.field, attr });
|
|
2035
|
+
} else if (is("media-remove") && canEdit2) {
|
|
2036
|
+
const attr = mediaAttribute(uid, event.data.field, components2);
|
|
2037
|
+
if (attr && !attr.required) onChange2(path(key, event.data.field), null);
|
|
2038
|
+
} else if (is("edit") && canEdit2) {
|
|
2039
|
+
const value = validateEdit(uid, event.data.field, event.data.value, components2);
|
|
2040
|
+
if (value !== null) onChange2(path(key, event.data.field), value);
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
window.addEventListener("message", receive);
|
|
2044
|
+
if (loaded.current) iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "ping" }, origin);
|
|
2045
|
+
return () => {
|
|
2046
|
+
clearTimeout(timeout);
|
|
2047
|
+
window.removeEventListener("message", receive);
|
|
2048
|
+
};
|
|
2049
|
+
}, [active, attempt, channel, origin, groups]);
|
|
2050
|
+
React3.useEffect(() => {
|
|
2051
|
+
if (!ready || !active) return;
|
|
2052
|
+
const timer = setTimeout(send, 120);
|
|
2053
|
+
return () => clearTimeout(timer);
|
|
2054
|
+
}, [values, c.components, ready, active, send, mode]);
|
|
2055
|
+
if (!zone) return null;
|
|
2056
|
+
const modes = [["form", t.modeForm], ["split", t.modeSplit], ["preview", t.modePreview]];
|
|
2057
|
+
const switcher = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Flex, { gap: 1, wrap: "wrap", "data-testid": "page-preview-modes", role: "group", "aria-label": t.modeGroup, children: modes.map(([value, label]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Button, { type: "button", size: "S", variant: mode === value ? "default" : "tertiary", "aria-pressed": mode === value, onClick: () => setMode(value), disabled: !url && value !== "form", children: label }, value)) });
|
|
2058
|
+
const onKey = (event) => {
|
|
2059
|
+
const map = { ArrowLeft: paneWidth + KEY_STEP, ArrowRight: paneWidth - KEY_STEP, Home: MIN_PANE, End: vw - MIN_FORM };
|
|
2060
|
+
if (event.key in map) {
|
|
2061
|
+
event.preventDefault();
|
|
2062
|
+
applyWidth(map[event.key], true);
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
const problems = groups ? (0, import_groups.validateGroups)(rows, groups) : [];
|
|
2066
|
+
const insertClose = (error) => {
|
|
2067
|
+
const end = (0, import_groups.groupRange)(latest.current, error.index, groups)[1];
|
|
2068
|
+
addFieldRow(zone, { __component: error.expected }, end + 1);
|
|
2069
|
+
};
|
|
2070
|
+
const diagnostics = problems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Box, { role: "alert", "data-testid": "page-preview-diagnostics", padding: 2, background: "danger100", hasRadius: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { children: [
|
|
2071
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Typography, { variant: "pi", fontWeight: "bold", textColor: "danger700", children: t.f("diagSummary", { count: problems.length }) }) }),
|
|
2072
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { style: { margin: "6px 0 0 16px", padding: 0 }, children: problems.map((error) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { style: { marginBottom: 6 }, children: [
|
|
2073
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Typography, { variant: "pi", textColor: "danger700", children: t.f(error.code === "closeBeforeOpen" ? "diagCloseBeforeOpen" : error.code === "mismatch" ? "diagMismatch" : "diagUnclosed", { n: error.index + 1, uid: error.uid, open: error.open || "", expected: error.expected || "" }) }),
|
|
2074
|
+
error.code === "unclosed" && canEdit && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Box, { paddingTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Button, { size: "S", variant: "danger-light", "data-testid": `diag-insert-close-${error.index}`, onClick: () => insertClose(error), children: t.f("diagInsertClose", { n: error.index + 1, expected: error.expected }) }) }),
|
|
2075
|
+
error.code === "closeBeforeOpen" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Typography, { variant: "pi", textColor: "neutral600", children: t.diagStrayHint })
|
|
2076
|
+
] }, `${error.code}-${error.index}`)) })
|
|
2077
|
+
] }) });
|
|
2078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.Flex, { direction: "column", alignItems: "stretch", gap: 2, ref: anchor, children: [
|
|
2079
|
+
switcher,
|
|
2080
|
+
diagnostics,
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Typography, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
|
|
2082
|
+
active && (0, import_react_dom2.createPortal)(
|
|
2083
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2084
|
+
Pane,
|
|
2085
|
+
{
|
|
2086
|
+
"data-testid": "page-preview-pane",
|
|
2087
|
+
"aria-label": t.previewPane,
|
|
2088
|
+
style: { left: mode === "preview" || narrow ? main.left : main.left + main.width - paneWidth, width: mode === "preview" || narrow ? main.width : paneWidth, userSelect: dragging ? "none" : void 0 },
|
|
2089
|
+
children: [
|
|
2090
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: SPLIT_STYLE }),
|
|
2091
|
+
resizable && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2092
|
+
Handle,
|
|
2093
|
+
{
|
|
2094
|
+
role: "separator",
|
|
2095
|
+
"aria-orientation": "vertical",
|
|
2096
|
+
"aria-label": t.resize,
|
|
2097
|
+
tabIndex: 0,
|
|
2098
|
+
"aria-valuemin": MIN_PANE,
|
|
2099
|
+
"aria-valuemax": Math.max(MIN_PANE, vw - MIN_FORM),
|
|
2100
|
+
"aria-valuenow": paneWidth,
|
|
2101
|
+
"data-testid": "page-preview-resizer",
|
|
2102
|
+
"data-dragging": dragging,
|
|
2103
|
+
onPointerDown: (e) => {
|
|
2104
|
+
e.preventDefault();
|
|
2105
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
2106
|
+
setDragging(true);
|
|
2107
|
+
},
|
|
2108
|
+
onPointerMove: (e) => {
|
|
2109
|
+
if (dragging) applyWidth(main.left + main.width - e.clientX, false);
|
|
2110
|
+
},
|
|
2111
|
+
onPointerUp: (e) => {
|
|
2112
|
+
if (!dragging) return;
|
|
2113
|
+
setDragging(false);
|
|
2114
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
2115
|
+
applyWidth(main.left + main.width - e.clientX, true);
|
|
2116
|
+
},
|
|
2117
|
+
onPointerCancel: () => setDragging(false),
|
|
2118
|
+
onKeyDown: onKey
|
|
2119
|
+
}
|
|
2120
|
+
),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.Flex, { padding: 2, gap: 2, justifyContent: "space-between", alignItems: "center", background: "neutral100", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 2 }, children: [
|
|
2122
|
+
switcher,
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.Flex, { gap: 2, alignItems: "center", wrap: "wrap", style: { marginLeft: "auto" }, children: [
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Typography, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
|
|
2125
|
+
failed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Button, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolbarActions, { model: c.model, collectionType: c.collectionType, documentId: c.isCreatingEntry ? void 0 : c.id, locale: c.form?.initialValues?.locale })
|
|
2127
|
+
] })
|
|
2128
|
+
] }),
|
|
2129
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2130
|
+
Frame2,
|
|
2131
|
+
{
|
|
2132
|
+
ref: iframe,
|
|
2133
|
+
title: t.previewPane,
|
|
2134
|
+
src: url.href,
|
|
2135
|
+
sandbox: "allow-scripts allow-same-origin",
|
|
2136
|
+
referrerPolicy: "no-referrer",
|
|
2137
|
+
onLoad: () => {
|
|
2138
|
+
loaded.current = true;
|
|
2139
|
+
},
|
|
2140
|
+
style: dragging ? { pointerEvents: "none" } : void 0
|
|
2141
|
+
},
|
|
2142
|
+
attempt
|
|
2143
|
+
)
|
|
2144
|
+
]
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
document.body
|
|
2148
|
+
),
|
|
2149
|
+
inserting && zoneAttr && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2150
|
+
PickerModal,
|
|
2151
|
+
{
|
|
2152
|
+
zone: { name: zone, components: zoneAttr.components || [], max: zoneAttr.max },
|
|
2153
|
+
components: c.components,
|
|
2154
|
+
Modal: PickerDialog,
|
|
2155
|
+
Toggle,
|
|
2156
|
+
get,
|
|
2157
|
+
open: true,
|
|
2158
|
+
onOpenChange: (open) => {
|
|
2159
|
+
if (!open) setInserting(null);
|
|
2160
|
+
},
|
|
2161
|
+
onSelect: (uid) => {
|
|
2162
|
+
const index = insertIndex(latest.current, inserting.after);
|
|
2163
|
+
setInserting(null);
|
|
2164
|
+
const close = groups?.[uid];
|
|
2165
|
+
if (index < 0 || !(zoneAttr.components || []).includes(uid) || close && !(zoneAttr.components || []).includes(close) || latest.current.length + (close ? 2 : 1) > (zoneAttr.max ?? Infinity)) {
|
|
2166
|
+
toggleNotification({ type: "warning", message: close ? t.zoneFull : t.insertMoved });
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
addFieldRow(zone, { ...componentDefaults(c.components[uid], c.components), __component: uid }, index);
|
|
2170
|
+
if (close) addFieldRow(zone, { __component: close }, index + 1);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
),
|
|
2174
|
+
editing && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2175
|
+
FieldEditorModal,
|
|
2176
|
+
{
|
|
2177
|
+
label: editing.label,
|
|
2178
|
+
attribute: editing.attr,
|
|
2179
|
+
value: editing.value,
|
|
2180
|
+
onCancel: () => setEditing(null),
|
|
2181
|
+
onApply: (value) => {
|
|
2182
|
+
const index = indexOf(editing.key);
|
|
2183
|
+
setEditing(null);
|
|
2184
|
+
if (index >= 0) onChange(`${zone}.${index}.${editing.field}`, value);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
),
|
|
2188
|
+
picking && MediaLibraryDialog && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2189
|
+
MediaLibraryDialog,
|
|
2190
|
+
{
|
|
2191
|
+
allowedTypes: picking.attr.allowedTypes || ["images"],
|
|
2192
|
+
multiple: picking.attr.multiple === true,
|
|
2193
|
+
onClose: () => setPicking(null),
|
|
2194
|
+
onSelectAssets: (assets) => {
|
|
2195
|
+
const index = indexOf(picking.key);
|
|
2196
|
+
setPicking(null);
|
|
2197
|
+
if (index < 0 || !assets?.length) return;
|
|
2198
|
+
const current = getIn(latest.current[index], picking.field);
|
|
2199
|
+
onChange(`${zone}.${index}.${picking.field}`, picking.attr.multiple ? [...Array.isArray(current) ? current : [], ...assets] : assets[0]);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
),
|
|
2203
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Box, { hidden: true, "data-testid": "page-preview-state", "data-mode": mode, "data-ready": ready, "data-failed": failed, "data-pane-width": paneWidth, "data-narrow": narrow, "data-preview-source": source, "data-editing": editing ? editing.field : "" })
|
|
2204
|
+
] });
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
// admin/Settings.tsx
|
|
2208
|
+
var React4 = __toESM(require("react"));
|
|
2209
|
+
var import_design_system5 = require("@strapi/design-system");
|
|
2210
|
+
var import_styled_components3 = __toESM(require("styled-components"));
|
|
2211
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1418
2212
|
var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
|
|
1419
|
-
var Grid =
|
|
1420
|
-
var Card2 =
|
|
2213
|
+
var Grid = import_styled_components3.default.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
|
|
2214
|
+
var Card2 = import_styled_components3.default.div`
|
|
1421
2215
|
display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: 4px;
|
|
1422
2216
|
border: 1px solid ${({ theme }) => theme.colors.neutral200}; background: ${({ theme }) => theme.colors.neutral0};
|
|
1423
2217
|
`;
|
|
1424
|
-
var Bar =
|
|
2218
|
+
var Bar = import_styled_components3.default.div`
|
|
1425
2219
|
position: fixed; top: 0; z-index: 4; padding: 12px 32px;
|
|
1426
2220
|
background: ${({ theme }) => theme.colors.neutral100}; border-bottom: 1px solid ${({ theme }) => theme.colors.neutral150};
|
|
1427
2221
|
box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
|
|
1428
2222
|
`;
|
|
1429
|
-
function
|
|
1430
|
-
const [rect, setRect] =
|
|
1431
|
-
|
|
1432
|
-
const
|
|
2223
|
+
function useMainRect2(anchor) {
|
|
2224
|
+
const [rect, setRect] = React4.useState({ left: 0, width: 0 });
|
|
2225
|
+
React4.useEffect(() => {
|
|
2226
|
+
const read2 = () => {
|
|
1433
2227
|
let host = document.getElementById("main-content");
|
|
1434
2228
|
for (let el = anchor.current?.parentElement; !host && el; el = el.parentElement) if (["auto", "scroll"].includes(getComputedStyle(el).overflowY) && el.scrollHeight > el.clientHeight) host = el;
|
|
1435
2229
|
const r = (host || document.querySelector("main"))?.getBoundingClientRect();
|
|
1436
2230
|
if (r) setRect({ left: Math.round(r.left), width: Math.round(r.width) });
|
|
1437
2231
|
};
|
|
1438
|
-
|
|
1439
|
-
window.addEventListener("resize",
|
|
1440
|
-
return () => window.removeEventListener("resize",
|
|
2232
|
+
read2();
|
|
2233
|
+
window.addEventListener("resize", read2);
|
|
2234
|
+
return () => window.removeEventListener("resize", read2);
|
|
1441
2235
|
});
|
|
1442
2236
|
return rect;
|
|
1443
2237
|
}
|
|
1444
|
-
var Swatches =
|
|
1445
|
-
var Label =
|
|
2238
|
+
var Swatches = import_styled_components3.default.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; `;
|
|
2239
|
+
var Label = import_styled_components3.default.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
|
|
1446
2240
|
function ColorField({ name, label, value, onChange, disabled }) {
|
|
1447
2241
|
const valid = COLOR2.test(value);
|
|
1448
|
-
return /* @__PURE__ */ (0,
|
|
1449
|
-
/* @__PURE__ */ (0,
|
|
1450
|
-
/* @__PURE__ */ (0,
|
|
1451
|
-
/* @__PURE__ */ (0,
|
|
1452
|
-
/* @__PURE__ */ (0,
|
|
2242
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Label, { children: [
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
|
|
2244
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { gap: 2, children: [
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "color", "aria-label": label, value: valid ? value : "#000000", disabled, onChange: (e) => onChange(e.target.value.toUpperCase()), style: { width: 40, height: 32, padding: 0, border: 0, background: "none" } }),
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1453
2247
|
"input",
|
|
1454
2248
|
{
|
|
1455
2249
|
name,
|
|
@@ -1465,22 +2259,22 @@ function ColorField({ name, label, value, onChange, disabled }) {
|
|
|
1465
2259
|
] });
|
|
1466
2260
|
}
|
|
1467
2261
|
var permissions = { read: [{ action: "plugin::blockscene.settings.read", subject: null }], update: [{ action: "plugin::blockscene.settings.update", subject: null }] };
|
|
1468
|
-
function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: MediaPicker2, ToggleField: ToggleField2, SelectField: SelectField2, TextField, previewSupported = false }) {
|
|
2262
|
+
function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: MediaPicker2, ToggleField: ToggleField2, SelectField: SelectField2, TextField: TextField2, previewSupported = false }) {
|
|
1469
2263
|
const t = useMessages();
|
|
1470
2264
|
const { get, put } = useClient();
|
|
1471
2265
|
const { canRead, canUpdate, isLoading } = usePermissions2();
|
|
1472
|
-
const [data, setData] =
|
|
1473
|
-
const [catalog, setCatalog] =
|
|
1474
|
-
const [settings, setSettings] =
|
|
1475
|
-
const [media, setMedia] =
|
|
1476
|
-
const [sources, setSources] =
|
|
1477
|
-
const [dirty, setDirty] =
|
|
1478
|
-
const [saving, setSaving] =
|
|
1479
|
-
const [status, setStatus] =
|
|
1480
|
-
const [failed, setFailed] =
|
|
1481
|
-
const [filter, setFilter] =
|
|
1482
|
-
const [picking, setPicking] =
|
|
1483
|
-
|
|
2266
|
+
const [data, setData] = React4.useState(null);
|
|
2267
|
+
const [catalog, setCatalog] = React4.useState(null);
|
|
2268
|
+
const [settings, setSettings] = React4.useState(null);
|
|
2269
|
+
const [media, setMedia] = React4.useState({});
|
|
2270
|
+
const [sources, setSources] = React4.useState({});
|
|
2271
|
+
const [dirty, setDirty] = React4.useState(false);
|
|
2272
|
+
const [saving, setSaving] = React4.useState(false);
|
|
2273
|
+
const [status, setStatus] = React4.useState("");
|
|
2274
|
+
const [failed, setFailed] = React4.useState(false);
|
|
2275
|
+
const [filter, setFilter] = React4.useState("");
|
|
2276
|
+
const [picking, setPicking] = React4.useState(null);
|
|
2277
|
+
React4.useEffect(() => {
|
|
1484
2278
|
if (!canRead || isLoading) return;
|
|
1485
2279
|
let active = true;
|
|
1486
2280
|
Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
|
|
@@ -1528,35 +2322,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1528
2322
|
return index === 0 && manual ? t.manual : t.automatic;
|
|
1529
2323
|
};
|
|
1530
2324
|
const ready = !isLoading && canRead && !failed && settings && catalog;
|
|
1531
|
-
const header =
|
|
1532
|
-
const [floating, setFloating] =
|
|
1533
|
-
const main =
|
|
1534
|
-
|
|
2325
|
+
const header = React4.useRef(null);
|
|
2326
|
+
const [floating, setFloating] = React4.useState(false);
|
|
2327
|
+
const main = useMainRect2(header);
|
|
2328
|
+
React4.useEffect(() => {
|
|
1535
2329
|
const el = header.current;
|
|
1536
2330
|
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
1537
2331
|
const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
|
|
1538
2332
|
observer.observe(el);
|
|
1539
2333
|
return () => observer.disconnect();
|
|
1540
2334
|
}, [ready]);
|
|
1541
|
-
const controls = (suffix) => ready && /* @__PURE__ */ (0,
|
|
1542
|
-
/* @__PURE__ */ (0,
|
|
1543
|
-
/* @__PURE__ */ (0,
|
|
2335
|
+
const controls = (suffix) => ready && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
|
|
2336
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
|
|
2337
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Button, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
|
|
1544
2338
|
] });
|
|
1545
|
-
return /* @__PURE__ */ (0,
|
|
1546
|
-
/* @__PURE__ */ (0,
|
|
1547
|
-
/* @__PURE__ */ (0,
|
|
2339
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Box, { padding: 8, background: "neutral100", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { direction: "column", alignItems: "stretch", gap: 5, children: [
|
|
2340
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: header, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
|
|
2341
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
|
|
1548
2342
|
controls("")
|
|
1549
2343
|
] }) }),
|
|
1550
|
-
floating && main.width > 0 && /* @__PURE__ */ (0,
|
|
1551
|
-
/* @__PURE__ */ (0,
|
|
2344
|
+
floating && main.width > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "beta", tag: "p", children: t.settingsTitle }),
|
|
1552
2346
|
controls("-floating")
|
|
1553
2347
|
] }) }),
|
|
1554
|
-
/* @__PURE__ */ (0,
|
|
1555
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
1556
|
-
data.disabled && /* @__PURE__ */ (0,
|
|
1557
|
-
/* @__PURE__ */ (0,
|
|
1558
|
-
/* @__PURE__ */ (0,
|
|
1559
|
-
/* @__PURE__ */ (0,
|
|
2348
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { textColor: "neutral600", children: t.settingsIntro }),
|
|
2349
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
2350
|
+
data.disabled && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Box, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
|
|
2351
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Box, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "beta", tag: "h2", children: t.palette }),
|
|
2353
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1560
2354
|
ColorField,
|
|
1561
2355
|
{
|
|
1562
2356
|
name: `palette-${key}`,
|
|
@@ -1570,33 +2364,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1570
2364
|
},
|
|
1571
2365
|
key
|
|
1572
2366
|
)) }),
|
|
1573
|
-
invalid && /* @__PURE__ */ (0,
|
|
1574
|
-
/* @__PURE__ */ (0,
|
|
1575
|
-
/* @__PURE__ */ (0,
|
|
1576
|
-
/* @__PURE__ */ (0,
|
|
1577
|
-
/* @__PURE__ */ (0,
|
|
2367
|
+
invalid && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { role: "alert", textColor: "danger600", children: t.invalidColor }),
|
|
2368
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { direction: "column", gap: 1, children: [
|
|
2370
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Box, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Wireframe, { template, palette: settings.palette }) }),
|
|
2371
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
|
|
1578
2372
|
] }, template)) }),
|
|
1579
|
-
/* @__PURE__ */ (0,
|
|
2373
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Flex, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Button, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
|
|
1580
2374
|
s.palette = { ...DEFAULT_PALETTE };
|
|
1581
2375
|
return s;
|
|
1582
2376
|
}), children: t.resetPalette }) })
|
|
1583
2377
|
] }) }),
|
|
1584
|
-
/* @__PURE__ */ (0,
|
|
1585
|
-
/* @__PURE__ */ (0,
|
|
1586
|
-
/* @__PURE__ */ (0,
|
|
2378
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Box, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2379
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "beta", tag: "h2", children: t.editor }),
|
|
2380
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1587
2381
|
s.editor.enabled = v;
|
|
1588
2382
|
return s;
|
|
1589
2383
|
}) }),
|
|
1590
|
-
/* @__PURE__ */ (0,
|
|
1591
|
-
/* @__PURE__ */ (0,
|
|
2384
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
|
|
2385
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1592
2386
|
s.editor.showOpenAll = v;
|
|
1593
2387
|
return s;
|
|
1594
2388
|
}) }),
|
|
1595
|
-
/* @__PURE__ */ (0,
|
|
2389
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1596
2390
|
s.editor.showCloseAll = v;
|
|
1597
2391
|
return s;
|
|
1598
2392
|
}) }),
|
|
1599
|
-
/* @__PURE__ */ (0,
|
|
2393
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1600
2394
|
SelectField2,
|
|
1601
2395
|
{
|
|
1602
2396
|
name: "editor-initialState",
|
|
@@ -1610,10 +2404,10 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1610
2404
|
})
|
|
1611
2405
|
}
|
|
1612
2406
|
),
|
|
1613
|
-
/* @__PURE__ */ (0,
|
|
1614
|
-
previewSupported ? /* @__PURE__ */ (0,
|
|
1615
|
-
/* @__PURE__ */ (0,
|
|
1616
|
-
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
|
|
2408
|
+
previewSupported ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
2409
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2410
|
+
TextField2,
|
|
1617
2411
|
{
|
|
1618
2412
|
name: "editor-previewUrl",
|
|
1619
2413
|
label: t.previewUrl,
|
|
@@ -1626,8 +2420,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1626
2420
|
})
|
|
1627
2421
|
}
|
|
1628
2422
|
),
|
|
1629
|
-
/* @__PURE__ */ (0,
|
|
1630
|
-
/* @__PURE__ */ (0,
|
|
2423
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
|
|
2424
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1631
2425
|
SelectField2,
|
|
1632
2426
|
{
|
|
1633
2427
|
name: "editor-previewMode",
|
|
@@ -1641,26 +2435,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1641
2435
|
})
|
|
1642
2436
|
}
|
|
1643
2437
|
),
|
|
1644
|
-
/* @__PURE__ */ (0,
|
|
1645
|
-
data.blockPreviewAvailable && /* @__PURE__ */ (0,
|
|
1646
|
-
/* @__PURE__ */ (0,
|
|
2438
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
|
|
2439
|
+
data.blockPreviewAvailable && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1647
2441
|
s.editor.blockPreviewInForm = v;
|
|
1648
2442
|
return s;
|
|
1649
2443
|
}) }),
|
|
1650
|
-
/* @__PURE__ */ (0,
|
|
2444
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
|
|
1651
2445
|
] })
|
|
1652
|
-
] }) : /* @__PURE__ */ (0,
|
|
2446
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
|
|
1653
2447
|
] }) }),
|
|
1654
|
-
/* @__PURE__ */ (0,
|
|
1655
|
-
/* @__PURE__ */ (0,
|
|
1656
|
-
/* @__PURE__ */ (0,
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
1658
|
-
/* @__PURE__ */ (0,
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Box, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "beta", tag: "h2", children: t.components }),
|
|
2450
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Searchbar, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
|
|
2451
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
|
|
2452
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Grid, { "data-testid": "components-grid", children: components.map((component) => {
|
|
1659
2453
|
const entry = settings.components[component.uid] || {};
|
|
1660
2454
|
const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
|
|
1661
2455
|
const candidates = candidatesFor(component.uid, meta, catalog);
|
|
1662
|
-
return /* @__PURE__ */ (0,
|
|
1663
|
-
/* @__PURE__ */ (0,
|
|
2456
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Card2, { "data-testid": `settings-${component.uid}`, children: [
|
|
2457
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1664
2458
|
Thumb,
|
|
1665
2459
|
{
|
|
1666
2460
|
candidates,
|
|
@@ -1671,23 +2465,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1671
2465
|
onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
|
|
1672
2466
|
}
|
|
1673
2467
|
),
|
|
1674
|
-
/* @__PURE__ */ (0,
|
|
1675
|
-
/* @__PURE__ */ (0,
|
|
1676
|
-
/* @__PURE__ */ (0,
|
|
2468
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
|
|
2470
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
1677
2471
|
component.uid,
|
|
1678
2472
|
" \xB7 ",
|
|
1679
2473
|
component.category
|
|
1680
2474
|
] }),
|
|
1681
|
-
/* @__PURE__ */ (0,
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Typography, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
|
|
1682
2476
|
t.source,
|
|
1683
2477
|
": ",
|
|
1684
2478
|
sourceOf(component.uid, Boolean(meta.manualImage)),
|
|
1685
2479
|
meta.manualName ? ` (${meta.manualName})` : ""
|
|
1686
2480
|
] }),
|
|
1687
|
-
meta.manualMissing && /* @__PURE__ */ (0,
|
|
1688
|
-
/* @__PURE__ */ (0,
|
|
1689
|
-
/* @__PURE__ */ (0,
|
|
1690
|
-
entry.mediaId && /* @__PURE__ */ (0,
|
|
2481
|
+
meta.manualMissing && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Typography, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
|
|
2482
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system5.Flex, { gap: 2, wrap: "wrap", children: [
|
|
2483
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Button, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
|
|
2484
|
+
entry.mediaId && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system5.Button, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
|
|
1691
2485
|
update((s) => {
|
|
1692
2486
|
delete s.components[component.uid]?.mediaId;
|
|
1693
2487
|
if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
|
|
@@ -1700,7 +2494,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1700
2494
|
});
|
|
1701
2495
|
}, children: t.useAutomatic })
|
|
1702
2496
|
] }),
|
|
1703
|
-
/* @__PURE__ */ (0,
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1704
2498
|
SelectField2,
|
|
1705
2499
|
{
|
|
1706
2500
|
name: `template-${component.uid}`,
|
|
@@ -1718,7 +2512,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1718
2512
|
] }, component.uid);
|
|
1719
2513
|
}) })
|
|
1720
2514
|
] }) }),
|
|
1721
|
-
picking && /* @__PURE__ */ (0,
|
|
2515
|
+
picking && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
|
|
1722
2516
|
const uid = picking;
|
|
1723
2517
|
setPicking(null);
|
|
1724
2518
|
if (!asset?.id) return;
|
|
@@ -1745,10 +2539,10 @@ function register(app, Component) {
|
|
|
1745
2539
|
}
|
|
1746
2540
|
|
|
1747
2541
|
// admin/catalog.ts
|
|
1748
|
-
var
|
|
2542
|
+
var React5 = __toESM(require("react"));
|
|
1749
2543
|
function useCatalog(get) {
|
|
1750
|
-
const [catalog, setCatalog] =
|
|
1751
|
-
|
|
2544
|
+
const [catalog, setCatalog] = React5.useState(null);
|
|
2545
|
+
React5.useEffect(() => {
|
|
1752
2546
|
let active = true;
|
|
1753
2547
|
get("/blockscene/catalog").then(({ data }) => {
|
|
1754
2548
|
if (active) setCatalog(data);
|
|
@@ -1762,77 +2556,83 @@ function useCatalog(get) {
|
|
|
1762
2556
|
return catalog;
|
|
1763
2557
|
}
|
|
1764
2558
|
|
|
1765
|
-
// admin/
|
|
1766
|
-
var
|
|
1767
|
-
function
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
/* @__PURE__ */ (0,
|
|
1773
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.ModalBody, { children })
|
|
2559
|
+
// admin/strapi5.tsx
|
|
2560
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2561
|
+
function Modal2({ open, onOpenChange, trigger, title, children }) {
|
|
2562
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system6.Modal.Root, { open, onOpenChange, children: [
|
|
2563
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Modal.Trigger, { children: trigger }),
|
|
2564
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system6.Modal.Content, { style: { width: "80vw", maxWidth: "80vw" }, children: [
|
|
2565
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Modal.Header, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Modal.Title, { children: title }) }),
|
|
2566
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Modal.Body, { children })
|
|
1774
2567
|
] })
|
|
1775
2568
|
] });
|
|
1776
2569
|
}
|
|
1777
|
-
function
|
|
1778
|
-
const c = (0,
|
|
1779
|
-
const
|
|
2570
|
+
function Panel() {
|
|
2571
|
+
const c = (0, import_strapi_admin2.unstable_useContentManagerContext)();
|
|
2572
|
+
const rbac = (0, import_strapi_admin2.useDocumentRBAC)("Blockscene", (state) => state);
|
|
2573
|
+
const { get } = (0, import_admin3.useFetchClient)();
|
|
2574
|
+
const t = useMessages();
|
|
2575
|
+
const user = (0, import_admin3.useAuth)("Blockscene", (state) => state.user);
|
|
2576
|
+
const formValues = (0, import_admin3.useForm)("Blockscene", (state) => state.values);
|
|
1780
2577
|
const catalog = useCatalog(get);
|
|
1781
|
-
const
|
|
1782
|
-
const
|
|
1783
|
-
const
|
|
1784
|
-
const zones = editableZones(
|
|
2578
|
+
const creating = !c.id && !c.form?.initialValues?.id && !c.form?.initialValues?.documentId;
|
|
2579
|
+
const allowed = (creating ? rbac.canCreateFields : rbac.canUpdateFields) || [];
|
|
2580
|
+
const fields = c.layout?.edit?.layout?.flat(3) || [];
|
|
2581
|
+
const zones = editableZones(
|
|
2582
|
+
c.contentType,
|
|
2583
|
+
formValues ?? c.form?.values,
|
|
2584
|
+
(name) => allowed.includes(name) && (creating || (rbac.canReadFields || []).includes(name)) && fields.find((field) => field.name === name)?.disabled !== true,
|
|
2585
|
+
c.isLoading || c.form?.disabled || !c.form?.addFieldRow
|
|
2586
|
+
).map((zone) => ({ ...zone, label: fields.find((field) => field.name === zone.name)?.label || zone.name }));
|
|
1785
2587
|
const add = (zone, uid) => {
|
|
2588
|
+
const values = formValues ?? c.form.values;
|
|
1786
2589
|
const close = catalog?.groups?.[uid];
|
|
1787
|
-
if (!canInsert(zone, uid, c.
|
|
1788
|
-
|
|
1789
|
-
|
|
2590
|
+
if (c.form.disabled || !canInsert(zone, uid, values, c.components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
|
|
2591
|
+
const at = Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0;
|
|
2592
|
+
c.form.addFieldRow(zone.name, { ...componentDefaults(c.components[uid], c.components), __component: uid }, at);
|
|
2593
|
+
if (close) c.form.addFieldRow(zone.name, { __component: close }, at + 1);
|
|
1790
2594
|
return true;
|
|
1791
2595
|
};
|
|
1792
|
-
if (!catalog?.editor?.enabled) return null;
|
|
1793
|
-
const docKey = `${c.
|
|
1794
|
-
return /* @__PURE__ */ (0,
|
|
1795
|
-
Gallery,
|
|
1796
|
-
{
|
|
1797
|
-
|
|
1798
|
-
components,
|
|
1799
|
-
add,
|
|
1800
|
-
Modal,
|
|
1801
|
-
Toggle: ToggleField,
|
|
1802
|
-
get,
|
|
1803
|
-
editor: catalog.editor,
|
|
1804
|
-
docKey,
|
|
1805
|
-
contentType: c.slug,
|
|
1806
|
-
userId: import_helper_plugin.auth.getUserInfo?.()?.id
|
|
1807
|
-
}
|
|
1808
|
-
);
|
|
2596
|
+
if (!zones.length || !catalog?.editor?.enabled) return null;
|
|
2597
|
+
const docKey = `${c.model}:${creating ? "new" : c.id}:${c.form?.initialValues?.locale || ""}`;
|
|
2598
|
+
return { title: t.gallery, content: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system6.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Gallery, { zones, components: c.components, add, Modal: Modal2, Toggle: ToggleField, get, editor: catalog.editor, docKey, contentType: c.model, userId: user?.id }),
|
|
2600
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PagePreview, { editor: catalog.editor, groups: catalog.groups || null, Toggle: ToggleField })
|
|
2601
|
+
] }) };
|
|
1809
2602
|
}
|
|
1810
|
-
var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ (0,
|
|
1811
|
-
/* @__PURE__ */ (0,
|
|
1812
|
-
/* @__PURE__ */ (0,
|
|
2603
|
+
var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system6.Flex, { gap: 3, children: [
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Switch, { name, "aria-label": label, checked: Boolean(value), disabled, onCheckedChange: onChange }),
|
|
2605
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Typography, { children: label })
|
|
2606
|
+
] });
|
|
2607
|
+
var TextField = ({ name, label, value, onChange, disabled, placeholder }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system6.Field.Root, { name, children: [
|
|
2608
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Field.Label, { children: label }),
|
|
2609
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.TextInput, { name, value, disabled, placeholder, onChange: (e) => onChange(e.target.value) })
|
|
2610
|
+
] });
|
|
2611
|
+
var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system6.Field.Root, { name, children: [
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.Field.Label, { children: label }),
|
|
2613
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.SingleSelect, { value, disabled, onChange: (v) => onChange(String(v)), children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system6.SingleSelectOption, { value: o.value, children: o.label }, o.value)) })
|
|
1813
2614
|
] });
|
|
1814
|
-
var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.SingleSelect, { name, label, value, disabled, onChange: (v) => onChange(String(v)), children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.SingleSelectOption, { value: o.value, children: o.label }, o.value)) });
|
|
1815
2615
|
function MediaPicker({ onClose, onSelect }) {
|
|
1816
|
-
const
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1819
|
-
onClose();
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
return /* @__PURE__ */ (0,
|
|
2616
|
+
const components = (0, import_admin3.useStrapiApp)("Blockscene", (state) => state.components);
|
|
2617
|
+
const Dialog3 = components?.["media-library"];
|
|
2618
|
+
React6.useEffect(() => {
|
|
2619
|
+
if (!Dialog3) onClose();
|
|
2620
|
+
}, [Dialog3, onClose]);
|
|
2621
|
+
if (!Dialog3) return null;
|
|
2622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Dialog3, { onClose, allowedTypes: ["images"], multiple: false, onSelectAssets: (assets) => onSelect(assets?.[0]) });
|
|
1823
2623
|
}
|
|
1824
2624
|
function usePermissions() {
|
|
1825
|
-
const { allowedActions, isLoading } = (0,
|
|
2625
|
+
const { allowedActions, isLoading } = (0, import_admin3.useRBAC)(permissions);
|
|
1826
2626
|
return { canRead: allowedActions.canRead, canUpdate: allowedActions.canUpdate, isLoading };
|
|
1827
2627
|
}
|
|
1828
|
-
var SettingsPage = () => /* @__PURE__ */ (0,
|
|
1829
|
-
var
|
|
2628
|
+
var SettingsPage = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Settings, { useClient: import_admin3.useFetchClient, usePermissions, MediaPicker, ToggleField, SelectField, TextField, previewSupported: true });
|
|
2629
|
+
var strapi5_default = {
|
|
1830
2630
|
register(app) {
|
|
1831
2631
|
register(app, SettingsPage);
|
|
1832
2632
|
app.registerPlugin({ id: "blockscene", name: "Blockscene" });
|
|
1833
2633
|
},
|
|
1834
2634
|
registerTrads,
|
|
1835
2635
|
bootstrap(app) {
|
|
1836
|
-
app.
|
|
2636
|
+
app.getPlugin("content-manager").apis.addEditViewSidePanel([Panel]);
|
|
1837
2637
|
}
|
|
1838
2638
|
};
|