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