@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.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,708 @@ 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 fields = c.layout?.edit?.layout?.flat(3) || [];
|
|
1851
|
+
const readable = (name) => c.isCreatingEntry || (rbac.canReadFields || []).includes(name);
|
|
1852
|
+
const zone = React3.useMemo(() => editableZones(c.contentType, values, readable, c.isLoading)[0]?.name, [c.contentType, values, c.isLoading, rbac.canReadFields, c.isCreatingEntry]);
|
|
1853
|
+
const zoneLabel = fields.find((field) => field.name === zone)?.label || zone || "";
|
|
1854
|
+
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");
|
|
1855
|
+
const [mode, setModeState] = React3.useState(() => {
|
|
1856
|
+
const v = read(MODE_KEY);
|
|
1857
|
+
return v === "split" || v === "preview" ? v : editor?.previewMode || "form";
|
|
1858
|
+
});
|
|
1859
|
+
const setMode = (next) => {
|
|
1860
|
+
setModeState(next);
|
|
1861
|
+
write(MODE_KEY, next);
|
|
1862
|
+
};
|
|
1863
|
+
const [ratio, setRatio] = React3.useState(() => {
|
|
1864
|
+
const v = Number(read(RATIO_KEY));
|
|
1865
|
+
return v > 0 && v < 1 ? v : 0.5;
|
|
1866
|
+
});
|
|
1867
|
+
const main = useMainRect();
|
|
1868
|
+
const vw = main.width;
|
|
1869
|
+
const [dragging, setDragging] = React3.useState(false);
|
|
1870
|
+
const [ready, setReady] = React3.useState(false);
|
|
1871
|
+
const [failed, setFailed] = React3.useState(false);
|
|
1872
|
+
const [attempt, retry] = React3.useReducer((n) => n + 1, 0);
|
|
1873
|
+
const [picking, setPicking] = React3.useState(null);
|
|
1874
|
+
React3.useEffect(() => {
|
|
1875
|
+
setEditing(null);
|
|
1876
|
+
setPicking(null);
|
|
1877
|
+
setInserting(null);
|
|
1878
|
+
}, [c.id, c.form?.initialValues?.locale]);
|
|
1879
|
+
const [editing, setEditing] = React3.useState(null);
|
|
1880
|
+
const [inserting, setInserting] = React3.useState(null);
|
|
1881
|
+
const { get } = useFetchClient();
|
|
1882
|
+
const { toggleNotification } = useNotification();
|
|
1883
|
+
const zoneAttr = zone ? c.contentType?.attributes?.[zone] : null;
|
|
1884
|
+
const iframe = React3.useRef(null);
|
|
1885
|
+
const loaded = React3.useRef(false);
|
|
1886
|
+
const anchor = React3.useRef(null);
|
|
1887
|
+
const channel = React3.useMemo(() => crypto.randomUUID(), []);
|
|
1888
|
+
const { base, source } = usePreviewBase(editor?.previewUrl || "", c.model, c.isCreatingEntry ? void 0 : c.id, c.form?.initialValues?.locale);
|
|
1889
|
+
const url = base ? new URL(base) : null;
|
|
1890
|
+
url?.searchParams.set("channel", channel);
|
|
1891
|
+
const origin = url?.origin || "";
|
|
1892
|
+
const narrow = vw < NARROW;
|
|
1893
|
+
const paneWidth = clampWidth(ratio * vw, vw);
|
|
1894
|
+
const resizable = mode === "split" && !narrow;
|
|
1895
|
+
const active = mode !== "form" && Boolean(url) && Boolean(zone);
|
|
1896
|
+
const rows = zone && Array.isArray(values?.[zone]) ? values[zone] : [];
|
|
1897
|
+
const latest = React3.useRef(rows);
|
|
1898
|
+
latest.current = rows;
|
|
1899
|
+
const live = React3.useRef({ mode, canEdit, zoneLabel, components: c.components, setMode, onChange });
|
|
1900
|
+
live.current = { mode, canEdit, zoneLabel, components: c.components, setMode, onChange };
|
|
1901
|
+
const send = React3.useCallback(() => {
|
|
1902
|
+
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);
|
|
1903
|
+
}, [channel, origin]);
|
|
1904
|
+
const indexOf = (key) => latest.current.findIndex((row) => blockKey(row) === key);
|
|
1905
|
+
const path = (key, field) => `${zone}.${indexOf(key)}.${field}`;
|
|
1906
|
+
React3.useEffect(() => {
|
|
1907
|
+
if (resizable) markLayout(anchor.current);
|
|
1908
|
+
document.body.classList.toggle("bp-split", resizable);
|
|
1909
|
+
document.documentElement.style.setProperty("--bp-pane", `${paneWidth}px`);
|
|
1910
|
+
return () => document.body.classList.remove("bp-split");
|
|
1911
|
+
}, [resizable, paneWidth]);
|
|
1912
|
+
const applyWidth = (width, persist) => {
|
|
1913
|
+
const next = clampWidth(width, vw) / vw;
|
|
1914
|
+
setRatio(next);
|
|
1915
|
+
if (persist) write(RATIO_KEY, String(next));
|
|
1916
|
+
};
|
|
1917
|
+
const focusField = (index, field) => {
|
|
1918
|
+
const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[index];
|
|
1919
|
+
if (!item) return;
|
|
1920
|
+
const header = toggles(item.parentElement)[index];
|
|
1921
|
+
if (header?.getAttribute("aria-expanded") === "false") header.click();
|
|
1922
|
+
const parts = field.split(".");
|
|
1923
|
+
const escaped = CSS.escape(`${zone}.${index}.${field}`);
|
|
1924
|
+
let tries = 0;
|
|
1925
|
+
const timer = setInterval(() => {
|
|
1926
|
+
if (++tries > 40) return clearInterval(timer);
|
|
1927
|
+
let scope = item;
|
|
1928
|
+
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
1929
|
+
const nested = [...scope.querySelectorAll("button[aria-expanded][data-radix-collection-item]")].filter((b) => b !== header);
|
|
1930
|
+
const row = nested[Number(parts[i + 1])];
|
|
1931
|
+
if (!row) return;
|
|
1932
|
+
if (row.getAttribute("aria-expanded") === "false") row.click();
|
|
1933
|
+
scope = row.closest("li, div") || scope;
|
|
1934
|
+
}
|
|
1935
|
+
const target = document.querySelector(`[name="${escaped}"], label[for="${escaped}"]`);
|
|
1936
|
+
if (!target) return;
|
|
1937
|
+
const INPUT = '.ck-editor__editable, textarea, input:not([type="hidden"]), select';
|
|
1938
|
+
let container = target.matches(INPUT) ? target : target.parentElement;
|
|
1939
|
+
for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++) container = container.parentElement;
|
|
1940
|
+
const editable = container?.matches(INPUT) ? container : container?.querySelector(INPUT);
|
|
1941
|
+
if (!editable) return;
|
|
1942
|
+
if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable) return;
|
|
1943
|
+
editable.focus();
|
|
1944
|
+
if (document.activeElement !== editable) return;
|
|
1945
|
+
clearInterval(timer);
|
|
1946
|
+
editable.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
1947
|
+
}, 150);
|
|
1948
|
+
};
|
|
1949
|
+
React3.useEffect(() => {
|
|
1950
|
+
if (!active) return;
|
|
1951
|
+
setReady(false);
|
|
1952
|
+
setFailed(false);
|
|
1953
|
+
const timeout = setTimeout(() => setFailed(true), 15e3);
|
|
1954
|
+
const receive = (event) => {
|
|
1955
|
+
const is = (type) => isPreviewMessage(event, origin, iframe.current?.contentWindow, channel, type);
|
|
1956
|
+
const { mode: mode2, canEdit: canEdit2, zoneLabel: zoneLabel2, components: components2, setMode: setMode2, onChange: onChange2 } = live.current;
|
|
1957
|
+
if (is("ready")) {
|
|
1958
|
+
clearTimeout(timeout);
|
|
1959
|
+
setReady(true);
|
|
1960
|
+
setFailed(false);
|
|
1961
|
+
send();
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
if (is("insert") && canEdit2) {
|
|
1965
|
+
const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
|
|
1966
|
+
if (after !== null && insertIndex(latest.current, after) < 0) return;
|
|
1967
|
+
if (latest.current.length >= (zoneAttr?.max ?? Infinity)) {
|
|
1968
|
+
toggleNotification({ type: "info", message: t.zoneFull });
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
setInserting({ after });
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (is("move-group") && canEdit2 && groups) {
|
|
1975
|
+
const next = (0, import_groups.moveGroup)(latest.current, String(event.data.key), event.data.direction === "up" ? "up" : "down", groups);
|
|
1976
|
+
if (next) onChange2(zone, next);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
if (is("delete-group") && canEdit2 && groups) {
|
|
1980
|
+
const next = (0, import_groups.removeGroup)(latest.current, String(event.data.key), groups);
|
|
1981
|
+
if (next) onChange2(zone, next);
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
if (is("insert-group") && canEdit2 && groups) {
|
|
1985
|
+
const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
|
|
1986
|
+
const index2 = insertIndex(latest.current, after);
|
|
1987
|
+
const uid2 = String(event.data.uid || "");
|
|
1988
|
+
const close = groups[uid2];
|
|
1989
|
+
const allowed = zoneAttr?.components || [];
|
|
1990
|
+
if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close)) return;
|
|
1991
|
+
if (latest.current.length + 2 > (zoneAttr?.max ?? Infinity)) {
|
|
1992
|
+
toggleNotification({ type: "info", message: t.zoneFull });
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1995
|
+
addFieldRow(zone, { ...componentDefaults(components2[uid2], components2), __component: uid2 }, index2);
|
|
1996
|
+
addFieldRow(zone, { __component: close }, index2 + 1);
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
const key = typeof event.data?.key === "string" ? event.data.key : "";
|
|
2000
|
+
const index = key ? indexOf(key) : -1;
|
|
2001
|
+
const uid = index >= 0 ? latest.current[index]?.__component : "";
|
|
2002
|
+
if (index < 0) return;
|
|
2003
|
+
if (is("select")) {
|
|
2004
|
+
const list = findZoneList(zoneLabel2);
|
|
2005
|
+
const item = list?.querySelectorAll(":scope > li")[index];
|
|
2006
|
+
const header = list ? toggles(list)[index] : null;
|
|
2007
|
+
if (header?.getAttribute("aria-expanded") === "false") header.click();
|
|
2008
|
+
item?.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
2009
|
+
if (item) {
|
|
2010
|
+
item.style.outline = "2px solid #4945ff";
|
|
2011
|
+
setTimeout(() => {
|
|
2012
|
+
item.style.outline = "";
|
|
2013
|
+
}, 1500);
|
|
2014
|
+
}
|
|
2015
|
+
if (mode2 === "preview") setMode2("split");
|
|
2016
|
+
iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "highlight", key }, origin);
|
|
2017
|
+
} else if (is("focus")) {
|
|
2018
|
+
const attr = validateFocus(uid, event.data.field, components2);
|
|
2019
|
+
if (!attr) return;
|
|
2020
|
+
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) });
|
|
2021
|
+
else focusField(index, event.data.field);
|
|
2022
|
+
} else if (is("media") && canEdit2) {
|
|
2023
|
+
const attr = mediaAttribute(uid, event.data.field, components2);
|
|
2024
|
+
if (attr) setPicking({ key, field: event.data.field, attr });
|
|
2025
|
+
} else if (is("media-remove") && canEdit2) {
|
|
2026
|
+
const attr = mediaAttribute(uid, event.data.field, components2);
|
|
2027
|
+
if (attr && !attr.required) onChange2(path(key, event.data.field), null);
|
|
2028
|
+
} else if (is("edit") && canEdit2) {
|
|
2029
|
+
const value = validateEdit(uid, event.data.field, event.data.value, components2);
|
|
2030
|
+
if (value !== null) onChange2(path(key, event.data.field), value);
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
window.addEventListener("message", receive);
|
|
2034
|
+
if (loaded.current) iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "ping" }, origin);
|
|
2035
|
+
return () => {
|
|
2036
|
+
clearTimeout(timeout);
|
|
2037
|
+
window.removeEventListener("message", receive);
|
|
2038
|
+
};
|
|
2039
|
+
}, [active, attempt, channel, origin, groups]);
|
|
2040
|
+
React3.useEffect(() => {
|
|
2041
|
+
if (!ready || !active) return;
|
|
2042
|
+
const timer = setTimeout(send, 120);
|
|
2043
|
+
return () => clearTimeout(timer);
|
|
2044
|
+
}, [values, c.components, ready, active, send, mode]);
|
|
2045
|
+
if (!zone) return null;
|
|
2046
|
+
const modes = [["form", t.modeForm], ["split", t.modeSplit], ["preview", t.modePreview]];
|
|
2047
|
+
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)) });
|
|
2048
|
+
const onKey = (event) => {
|
|
2049
|
+
const map = { ArrowLeft: paneWidth + KEY_STEP, ArrowRight: paneWidth - KEY_STEP, Home: MIN_PANE, End: vw - MIN_FORM };
|
|
2050
|
+
if (event.key in map) {
|
|
2051
|
+
event.preventDefault();
|
|
2052
|
+
applyWidth(map[event.key], true);
|
|
2053
|
+
}
|
|
2054
|
+
};
|
|
2055
|
+
const problems = groups ? (0, import_groups.validateGroups)(rows, groups) : [];
|
|
2056
|
+
const insertClose = (error) => {
|
|
2057
|
+
const end = (0, import_groups.groupRange)(latest.current, error.index, groups)[1];
|
|
2058
|
+
addFieldRow(zone, { __component: error.expected }, end + 1);
|
|
2059
|
+
};
|
|
2060
|
+
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: [
|
|
2061
|
+
/* @__PURE__ */ jsx4("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ jsx4(Typography3, { variant: "pi", fontWeight: "bold", textColor: "danger700", children: t.f("diagSummary", { count: problems.length }) }) }),
|
|
2062
|
+
/* @__PURE__ */ jsx4("ul", { style: { margin: "6px 0 0 16px", padding: 0 }, children: problems.map((error) => /* @__PURE__ */ jsxs3("li", { style: { marginBottom: 6 }, children: [
|
|
2063
|
+
/* @__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 || "" }) }),
|
|
2064
|
+
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 }) }) }),
|
|
2065
|
+
error.code === "closeBeforeOpen" && /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: t.diagStrayHint })
|
|
2066
|
+
] }, `${error.code}-${error.index}`)) })
|
|
2067
|
+
] }) });
|
|
2068
|
+
return /* @__PURE__ */ jsxs3(Flex3, { direction: "column", alignItems: "stretch", gap: 2, ref: anchor, children: [
|
|
2069
|
+
switcher,
|
|
2070
|
+
diagnostics,
|
|
2071
|
+
/* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
|
|
2072
|
+
active && createPortal(
|
|
2073
|
+
/* @__PURE__ */ jsxs3(
|
|
2074
|
+
Pane,
|
|
2075
|
+
{
|
|
2076
|
+
"data-testid": "page-preview-pane",
|
|
2077
|
+
"aria-label": t.previewPane,
|
|
2078
|
+
style: { left: mode === "preview" || narrow ? main.left : main.left + main.width - paneWidth, width: mode === "preview" || narrow ? main.width : paneWidth, userSelect: dragging ? "none" : void 0 },
|
|
2079
|
+
children: [
|
|
2080
|
+
/* @__PURE__ */ jsx4("style", { children: SPLIT_STYLE }),
|
|
2081
|
+
resizable && /* @__PURE__ */ jsx4(
|
|
2082
|
+
Handle,
|
|
2083
|
+
{
|
|
2084
|
+
role: "separator",
|
|
2085
|
+
"aria-orientation": "vertical",
|
|
2086
|
+
"aria-label": t.resize,
|
|
2087
|
+
tabIndex: 0,
|
|
2088
|
+
"aria-valuemin": MIN_PANE,
|
|
2089
|
+
"aria-valuemax": Math.max(MIN_PANE, vw - MIN_FORM),
|
|
2090
|
+
"aria-valuenow": paneWidth,
|
|
2091
|
+
"data-testid": "page-preview-resizer",
|
|
2092
|
+
"data-dragging": dragging,
|
|
2093
|
+
onPointerDown: (e) => {
|
|
2094
|
+
e.preventDefault();
|
|
2095
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
2096
|
+
setDragging(true);
|
|
2097
|
+
},
|
|
2098
|
+
onPointerMove: (e) => {
|
|
2099
|
+
if (dragging) applyWidth(main.left + main.width - e.clientX, false);
|
|
2100
|
+
},
|
|
2101
|
+
onPointerUp: (e) => {
|
|
2102
|
+
if (!dragging) return;
|
|
2103
|
+
setDragging(false);
|
|
2104
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
2105
|
+
applyWidth(main.left + main.width - e.clientX, true);
|
|
2106
|
+
},
|
|
2107
|
+
onPointerCancel: () => setDragging(false),
|
|
2108
|
+
onKeyDown: onKey
|
|
2109
|
+
}
|
|
2110
|
+
),
|
|
2111
|
+
/* @__PURE__ */ jsxs3(Flex3, { padding: 2, gap: 2, justifyContent: "space-between", alignItems: "center", background: "neutral100", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 2 }, children: [
|
|
2112
|
+
switcher,
|
|
2113
|
+
/* @__PURE__ */ jsxs3(Flex3, { gap: 2, alignItems: "center", wrap: "wrap", style: { marginLeft: "auto" }, children: [
|
|
2114
|
+
/* @__PURE__ */ jsx4(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
|
|
2115
|
+
failed && /* @__PURE__ */ jsx4(Button3, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
|
|
2116
|
+
/* @__PURE__ */ jsx4(ToolbarActions, { model: c.model, collectionType: c.collectionType, documentId: c.isCreatingEntry ? void 0 : c.id, locale: c.form?.initialValues?.locale })
|
|
2117
|
+
] })
|
|
2118
|
+
] }),
|
|
2119
|
+
/* @__PURE__ */ jsx4(
|
|
2120
|
+
Frame2,
|
|
2121
|
+
{
|
|
2122
|
+
ref: iframe,
|
|
2123
|
+
title: t.previewPane,
|
|
2124
|
+
src: url.href,
|
|
2125
|
+
sandbox: "allow-scripts allow-same-origin",
|
|
2126
|
+
referrerPolicy: "no-referrer",
|
|
2127
|
+
onLoad: () => {
|
|
2128
|
+
loaded.current = true;
|
|
2129
|
+
},
|
|
2130
|
+
style: dragging ? { pointerEvents: "none" } : void 0
|
|
2131
|
+
},
|
|
2132
|
+
attempt
|
|
2133
|
+
)
|
|
2134
|
+
]
|
|
2135
|
+
}
|
|
2136
|
+
),
|
|
2137
|
+
document.body
|
|
2138
|
+
),
|
|
2139
|
+
inserting && zoneAttr && /* @__PURE__ */ jsx4(
|
|
2140
|
+
PickerModal,
|
|
2141
|
+
{
|
|
2142
|
+
zone: { name: zone, components: zoneAttr.components || [], max: zoneAttr.max },
|
|
2143
|
+
components: c.components,
|
|
2144
|
+
Modal: PickerDialog,
|
|
2145
|
+
Toggle,
|
|
2146
|
+
get,
|
|
2147
|
+
open: true,
|
|
2148
|
+
onOpenChange: (open) => {
|
|
2149
|
+
if (!open) setInserting(null);
|
|
2150
|
+
},
|
|
2151
|
+
onSelect: (uid) => {
|
|
2152
|
+
const index = insertIndex(latest.current, inserting.after);
|
|
2153
|
+
setInserting(null);
|
|
2154
|
+
const close = groups?.[uid];
|
|
2155
|
+
if (index < 0 || !(zoneAttr.components || []).includes(uid) || close && !(zoneAttr.components || []).includes(close) || latest.current.length + (close ? 2 : 1) > (zoneAttr.max ?? Infinity)) {
|
|
2156
|
+
toggleNotification({ type: "warning", message: close ? t.zoneFull : t.insertMoved });
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
addFieldRow(zone, { ...componentDefaults(c.components[uid], c.components), __component: uid }, index);
|
|
2160
|
+
if (close) addFieldRow(zone, { __component: close }, index + 1);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
),
|
|
2164
|
+
editing && /* @__PURE__ */ jsx4(
|
|
2165
|
+
FieldEditorModal,
|
|
2166
|
+
{
|
|
2167
|
+
label: editing.label,
|
|
2168
|
+
attribute: editing.attr,
|
|
2169
|
+
value: editing.value,
|
|
2170
|
+
onCancel: () => setEditing(null),
|
|
2171
|
+
onApply: (value) => {
|
|
2172
|
+
const index = indexOf(editing.key);
|
|
2173
|
+
setEditing(null);
|
|
2174
|
+
if (index >= 0) onChange(`${zone}.${index}.${editing.field}`, value);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
),
|
|
2178
|
+
picking && MediaLibraryDialog && /* @__PURE__ */ jsx4(
|
|
2179
|
+
MediaLibraryDialog,
|
|
2180
|
+
{
|
|
2181
|
+
allowedTypes: picking.attr.allowedTypes || ["images"],
|
|
2182
|
+
multiple: picking.attr.multiple === true,
|
|
2183
|
+
onClose: () => setPicking(null),
|
|
2184
|
+
onSelectAssets: (assets) => {
|
|
2185
|
+
const index = indexOf(picking.key);
|
|
2186
|
+
setPicking(null);
|
|
2187
|
+
if (index < 0 || !assets?.length) return;
|
|
2188
|
+
const current = getIn(latest.current[index], picking.field);
|
|
2189
|
+
onChange(`${zone}.${index}.${picking.field}`, picking.attr.multiple ? [...Array.isArray(current) ? current : [], ...assets] : assets[0]);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
),
|
|
2193
|
+
/* @__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 : "" })
|
|
2194
|
+
] });
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// admin/Settings.tsx
|
|
2198
|
+
import * as React4 from "react";
|
|
2199
|
+
import { Box as Box3, Button as Button4, Flex as Flex4, Searchbar as Searchbar2, Typography as Typography4 } from "@strapi/design-system";
|
|
2200
|
+
import styled3 from "styled-components";
|
|
2201
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1385
2202
|
var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
|
|
1386
|
-
var Grid =
|
|
1387
|
-
var Card2 =
|
|
2203
|
+
var Grid = styled3.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
|
|
2204
|
+
var Card2 = styled3.div`
|
|
1388
2205
|
display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: 4px;
|
|
1389
2206
|
border: 1px solid ${({ theme }) => theme.colors.neutral200}; background: ${({ theme }) => theme.colors.neutral0};
|
|
1390
2207
|
`;
|
|
1391
|
-
var Bar =
|
|
2208
|
+
var Bar = styled3.div`
|
|
1392
2209
|
position: fixed; top: 0; z-index: 4; padding: 12px 32px;
|
|
1393
2210
|
background: ${({ theme }) => theme.colors.neutral100}; border-bottom: 1px solid ${({ theme }) => theme.colors.neutral150};
|
|
1394
2211
|
box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
|
|
1395
2212
|
`;
|
|
1396
|
-
function
|
|
1397
|
-
const [rect, setRect] =
|
|
1398
|
-
|
|
1399
|
-
const
|
|
2213
|
+
function useMainRect2(anchor) {
|
|
2214
|
+
const [rect, setRect] = React4.useState({ left: 0, width: 0 });
|
|
2215
|
+
React4.useEffect(() => {
|
|
2216
|
+
const read2 = () => {
|
|
1400
2217
|
let host = document.getElementById("main-content");
|
|
1401
2218
|
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
2219
|
const r = (host || document.querySelector("main"))?.getBoundingClientRect();
|
|
1403
2220
|
if (r) setRect({ left: Math.round(r.left), width: Math.round(r.width) });
|
|
1404
2221
|
};
|
|
1405
|
-
|
|
1406
|
-
window.addEventListener("resize",
|
|
1407
|
-
return () => window.removeEventListener("resize",
|
|
2222
|
+
read2();
|
|
2223
|
+
window.addEventListener("resize", read2);
|
|
2224
|
+
return () => window.removeEventListener("resize", read2);
|
|
1408
2225
|
});
|
|
1409
2226
|
return rect;
|
|
1410
2227
|
}
|
|
1411
|
-
var Swatches =
|
|
1412
|
-
var Label =
|
|
2228
|
+
var Swatches = styled3.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; `;
|
|
2229
|
+
var Label = styled3.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
|
|
1413
2230
|
function ColorField({ name, label, value, onChange, disabled }) {
|
|
1414
2231
|
const valid = COLOR2.test(value);
|
|
1415
|
-
return /* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1418
|
-
/* @__PURE__ */
|
|
1419
|
-
/* @__PURE__ */
|
|
2232
|
+
return /* @__PURE__ */ jsxs4(Label, { children: [
|
|
2233
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
|
|
2234
|
+
/* @__PURE__ */ jsxs4(Flex4, { gap: 2, children: [
|
|
2235
|
+
/* @__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" } }),
|
|
2236
|
+
/* @__PURE__ */ jsx5(
|
|
1420
2237
|
"input",
|
|
1421
2238
|
{
|
|
1422
2239
|
name,
|
|
@@ -1432,22 +2249,22 @@ function ColorField({ name, label, value, onChange, disabled }) {
|
|
|
1432
2249
|
] });
|
|
1433
2250
|
}
|
|
1434
2251
|
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 }) {
|
|
2252
|
+
function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: MediaPicker2, ToggleField: ToggleField2, SelectField: SelectField2, TextField: TextField2, previewSupported = false }) {
|
|
1436
2253
|
const t = useMessages();
|
|
1437
2254
|
const { get, put } = useClient();
|
|
1438
2255
|
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
|
-
|
|
2256
|
+
const [data, setData] = React4.useState(null);
|
|
2257
|
+
const [catalog, setCatalog] = React4.useState(null);
|
|
2258
|
+
const [settings, setSettings] = React4.useState(null);
|
|
2259
|
+
const [media, setMedia] = React4.useState({});
|
|
2260
|
+
const [sources, setSources] = React4.useState({});
|
|
2261
|
+
const [dirty, setDirty] = React4.useState(false);
|
|
2262
|
+
const [saving, setSaving] = React4.useState(false);
|
|
2263
|
+
const [status, setStatus] = React4.useState("");
|
|
2264
|
+
const [failed, setFailed] = React4.useState(false);
|
|
2265
|
+
const [filter, setFilter] = React4.useState("");
|
|
2266
|
+
const [picking, setPicking] = React4.useState(null);
|
|
2267
|
+
React4.useEffect(() => {
|
|
1451
2268
|
if (!canRead || isLoading) return;
|
|
1452
2269
|
let active = true;
|
|
1453
2270
|
Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
|
|
@@ -1495,35 +2312,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1495
2312
|
return index === 0 && manual ? t.manual : t.automatic;
|
|
1496
2313
|
};
|
|
1497
2314
|
const ready = !isLoading && canRead && !failed && settings && catalog;
|
|
1498
|
-
const header =
|
|
1499
|
-
const [floating, setFloating] =
|
|
1500
|
-
const main =
|
|
1501
|
-
|
|
2315
|
+
const header = React4.useRef(null);
|
|
2316
|
+
const [floating, setFloating] = React4.useState(false);
|
|
2317
|
+
const main = useMainRect2(header);
|
|
2318
|
+
React4.useEffect(() => {
|
|
1502
2319
|
const el = header.current;
|
|
1503
2320
|
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
1504
2321
|
const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
|
|
1505
2322
|
observer.observe(el);
|
|
1506
2323
|
return () => observer.disconnect();
|
|
1507
2324
|
}, [ready]);
|
|
1508
|
-
const controls = (suffix) => ready && /* @__PURE__ */
|
|
1509
|
-
/* @__PURE__ */
|
|
1510
|
-
/* @__PURE__ */
|
|
2325
|
+
const controls = (suffix) => ready && /* @__PURE__ */ jsxs4(Flex4, { gap: 3, alignItems: "center", wrap: "wrap", children: [
|
|
2326
|
+
/* @__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 }),
|
|
2327
|
+
/* @__PURE__ */ jsx5(Button4, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
|
|
1511
2328
|
] });
|
|
1512
|
-
return /* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
2329
|
+
return /* @__PURE__ */ jsx5(Box3, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 5, children: [
|
|
2330
|
+
/* @__PURE__ */ jsx5("div", { ref: header, children: /* @__PURE__ */ jsxs4(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
|
|
2331
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
|
|
1515
2332
|
controls("")
|
|
1516
2333
|
] }) }),
|
|
1517
|
-
floating && main.width > 0 && /* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
2334
|
+
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: [
|
|
2335
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "p", children: t.settingsTitle }),
|
|
1519
2336
|
controls("-floating")
|
|
1520
2337
|
] }) }),
|
|
1521
|
-
/* @__PURE__ */
|
|
1522
|
-
isLoading ? /* @__PURE__ */
|
|
1523
|
-
data.disabled && /* @__PURE__ */
|
|
1524
|
-
/* @__PURE__ */
|
|
1525
|
-
/* @__PURE__ */
|
|
1526
|
-
/* @__PURE__ */
|
|
2338
|
+
/* @__PURE__ */ jsx5(Typography4, { textColor: "neutral600", children: t.settingsIntro }),
|
|
2339
|
+
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: [
|
|
2340
|
+
data.disabled && /* @__PURE__ */ jsx5(Box3, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
|
|
2341
|
+
/* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2342
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.palette }),
|
|
2343
|
+
/* @__PURE__ */ jsx5(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx5(
|
|
1527
2344
|
ColorField,
|
|
1528
2345
|
{
|
|
1529
2346
|
name: `palette-${key}`,
|
|
@@ -1537,33 +2354,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1537
2354
|
},
|
|
1538
2355
|
key
|
|
1539
2356
|
)) }),
|
|
1540
|
-
invalid && /* @__PURE__ */
|
|
1541
|
-
/* @__PURE__ */
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
2357
|
+
invalid && /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "danger600", children: t.invalidColor }),
|
|
2358
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
|
|
2359
|
+
/* @__PURE__ */ jsx5(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs4(Flex4, { direction: "column", gap: 1, children: [
|
|
2360
|
+
/* @__PURE__ */ jsx5(Box3, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx5(Wireframe, { template, palette: settings.palette }) }),
|
|
2361
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
|
|
1545
2362
|
] }, template)) }),
|
|
1546
|
-
/* @__PURE__ */
|
|
2363
|
+
/* @__PURE__ */ jsx5(Flex4, { children: /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
|
|
1547
2364
|
s.palette = { ...DEFAULT_PALETTE };
|
|
1548
2365
|
return s;
|
|
1549
2366
|
}), children: t.resetPalette }) })
|
|
1550
2367
|
] }) }),
|
|
1551
|
-
/* @__PURE__ */
|
|
1552
|
-
/* @__PURE__ */
|
|
1553
|
-
/* @__PURE__ */
|
|
2368
|
+
/* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2369
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.editor }),
|
|
2370
|
+
/* @__PURE__ */ jsx5(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1554
2371
|
s.editor.enabled = v;
|
|
1555
2372
|
return s;
|
|
1556
2373
|
}) }),
|
|
1557
|
-
/* @__PURE__ */
|
|
1558
|
-
/* @__PURE__ */
|
|
2374
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
|
|
2375
|
+
/* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1559
2376
|
s.editor.showOpenAll = v;
|
|
1560
2377
|
return s;
|
|
1561
2378
|
}) }),
|
|
1562
|
-
/* @__PURE__ */
|
|
2379
|
+
/* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1563
2380
|
s.editor.showCloseAll = v;
|
|
1564
2381
|
return s;
|
|
1565
2382
|
}) }),
|
|
1566
|
-
/* @__PURE__ */
|
|
2383
|
+
/* @__PURE__ */ jsx5(
|
|
1567
2384
|
SelectField2,
|
|
1568
2385
|
{
|
|
1569
2386
|
name: "editor-initialState",
|
|
@@ -1577,10 +2394,10 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1577
2394
|
})
|
|
1578
2395
|
}
|
|
1579
2396
|
),
|
|
1580
|
-
/* @__PURE__ */
|
|
1581
|
-
previewSupported ? /* @__PURE__ */
|
|
1582
|
-
/* @__PURE__ */
|
|
1583
|
-
|
|
2397
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
|
|
2398
|
+
previewSupported ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
2399
|
+
/* @__PURE__ */ jsx5(
|
|
2400
|
+
TextField2,
|
|
1584
2401
|
{
|
|
1585
2402
|
name: "editor-previewUrl",
|
|
1586
2403
|
label: t.previewUrl,
|
|
@@ -1593,8 +2410,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1593
2410
|
})
|
|
1594
2411
|
}
|
|
1595
2412
|
),
|
|
1596
|
-
/* @__PURE__ */
|
|
1597
|
-
/* @__PURE__ */
|
|
2413
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
|
|
2414
|
+
/* @__PURE__ */ jsx5(
|
|
1598
2415
|
SelectField2,
|
|
1599
2416
|
{
|
|
1600
2417
|
name: "editor-previewMode",
|
|
@@ -1608,26 +2425,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1608
2425
|
})
|
|
1609
2426
|
}
|
|
1610
2427
|
),
|
|
1611
|
-
/* @__PURE__ */
|
|
1612
|
-
data.blockPreviewAvailable && /* @__PURE__ */
|
|
1613
|
-
/* @__PURE__ */
|
|
2428
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
|
|
2429
|
+
data.blockPreviewAvailable && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
2430
|
+
/* @__PURE__ */ jsx5(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
|
|
1614
2431
|
s.editor.blockPreviewInForm = v;
|
|
1615
2432
|
return s;
|
|
1616
2433
|
}) }),
|
|
1617
|
-
/* @__PURE__ */
|
|
2434
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
|
|
1618
2435
|
] })
|
|
1619
|
-
] }) : /* @__PURE__ */
|
|
2436
|
+
] }) : /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
|
|
1620
2437
|
] }) }),
|
|
1621
|
-
/* @__PURE__ */
|
|
1622
|
-
/* @__PURE__ */
|
|
1623
|
-
/* @__PURE__ */
|
|
1624
|
-
/* @__PURE__ */
|
|
1625
|
-
/* @__PURE__ */
|
|
2438
|
+
/* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2439
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.components }),
|
|
2440
|
+
/* @__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 }),
|
|
2441
|
+
/* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
|
|
2442
|
+
/* @__PURE__ */ jsx5(Grid, { "data-testid": "components-grid", children: components.map((component) => {
|
|
1626
2443
|
const entry = settings.components[component.uid] || {};
|
|
1627
2444
|
const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
|
|
1628
2445
|
const candidates = candidatesFor(component.uid, meta, catalog);
|
|
1629
|
-
return /* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
2446
|
+
return /* @__PURE__ */ jsxs4(Card2, { "data-testid": `settings-${component.uid}`, children: [
|
|
2447
|
+
/* @__PURE__ */ jsx5(
|
|
1631
2448
|
Thumb,
|
|
1632
2449
|
{
|
|
1633
2450
|
candidates,
|
|
@@ -1638,23 +2455,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1638
2455
|
onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
|
|
1639
2456
|
}
|
|
1640
2457
|
),
|
|
1641
|
-
/* @__PURE__ */
|
|
1642
|
-
/* @__PURE__ */
|
|
1643
|
-
/* @__PURE__ */
|
|
2458
|
+
/* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "flex-start", gap: 2, children: [
|
|
2459
|
+
/* @__PURE__ */ jsx5(Typography4, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
|
|
2460
|
+
/* @__PURE__ */ jsxs4(Typography4, { variant: "pi", textColor: "neutral600", children: [
|
|
1644
2461
|
component.uid,
|
|
1645
2462
|
" \xB7 ",
|
|
1646
2463
|
component.category
|
|
1647
2464
|
] }),
|
|
1648
|
-
/* @__PURE__ */
|
|
2465
|
+
/* @__PURE__ */ jsxs4(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
|
|
1649
2466
|
t.source,
|
|
1650
2467
|
": ",
|
|
1651
2468
|
sourceOf(component.uid, Boolean(meta.manualImage)),
|
|
1652
2469
|
meta.manualName ? ` (${meta.manualName})` : ""
|
|
1653
2470
|
] }),
|
|
1654
|
-
meta.manualMissing && /* @__PURE__ */
|
|
1655
|
-
/* @__PURE__ */
|
|
1656
|
-
/* @__PURE__ */
|
|
1657
|
-
entry.mediaId && /* @__PURE__ */
|
|
2471
|
+
meta.manualMissing && /* @__PURE__ */ jsx5(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
|
|
2472
|
+
/* @__PURE__ */ jsxs4(Flex4, { gap: 2, wrap: "wrap", children: [
|
|
2473
|
+
/* @__PURE__ */ jsx5(Button4, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
|
|
2474
|
+
entry.mediaId && /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
|
|
1658
2475
|
update((s) => {
|
|
1659
2476
|
delete s.components[component.uid]?.mediaId;
|
|
1660
2477
|
if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
|
|
@@ -1667,7 +2484,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1667
2484
|
});
|
|
1668
2485
|
}, children: t.useAutomatic })
|
|
1669
2486
|
] }),
|
|
1670
|
-
/* @__PURE__ */
|
|
2487
|
+
/* @__PURE__ */ jsx5(
|
|
1671
2488
|
SelectField2,
|
|
1672
2489
|
{
|
|
1673
2490
|
name: `template-${component.uid}`,
|
|
@@ -1685,7 +2502,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
|
|
|
1685
2502
|
] }, component.uid);
|
|
1686
2503
|
}) })
|
|
1687
2504
|
] }) }),
|
|
1688
|
-
picking && /* @__PURE__ */
|
|
2505
|
+
picking && /* @__PURE__ */ jsx5(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
|
|
1689
2506
|
const uid = picking;
|
|
1690
2507
|
setPicking(null);
|
|
1691
2508
|
if (!asset?.id) return;
|
|
@@ -1712,10 +2529,10 @@ function register(app, Component) {
|
|
|
1712
2529
|
}
|
|
1713
2530
|
|
|
1714
2531
|
// admin/catalog.ts
|
|
1715
|
-
import * as
|
|
2532
|
+
import * as React5 from "react";
|
|
1716
2533
|
function useCatalog(get) {
|
|
1717
|
-
const [catalog, setCatalog] =
|
|
1718
|
-
|
|
2534
|
+
const [catalog, setCatalog] = React5.useState(null);
|
|
2535
|
+
React5.useEffect(() => {
|
|
1719
2536
|
let active = true;
|
|
1720
2537
|
get("/blockscene/catalog").then(({ data }) => {
|
|
1721
2538
|
if (active) setCatalog(data);
|
|
@@ -1729,80 +2546,86 @@ function useCatalog(get) {
|
|
|
1729
2546
|
return catalog;
|
|
1730
2547
|
}
|
|
1731
2548
|
|
|
1732
|
-
// admin/
|
|
1733
|
-
import {
|
|
1734
|
-
function
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
/* @__PURE__ */
|
|
1740
|
-
/* @__PURE__ */ jsx4(ModalBody, { children })
|
|
2549
|
+
// admin/strapi5.tsx
|
|
2550
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2551
|
+
function Modal2({ open, onOpenChange, trigger, title, children }) {
|
|
2552
|
+
return /* @__PURE__ */ jsxs5(Dialog2.Root, { open, onOpenChange, children: [
|
|
2553
|
+
/* @__PURE__ */ jsx6(Dialog2.Trigger, { children: trigger }),
|
|
2554
|
+
/* @__PURE__ */ jsxs5(Dialog2.Content, { style: { width: "80vw", maxWidth: "80vw" }, children: [
|
|
2555
|
+
/* @__PURE__ */ jsx6(Dialog2.Header, { children: /* @__PURE__ */ jsx6(Dialog2.Title, { children: title }) }),
|
|
2556
|
+
/* @__PURE__ */ jsx6(Dialog2.Body, { children })
|
|
1741
2557
|
] })
|
|
1742
2558
|
] });
|
|
1743
2559
|
}
|
|
1744
|
-
function
|
|
1745
|
-
const c =
|
|
1746
|
-
const
|
|
2560
|
+
function Panel() {
|
|
2561
|
+
const c = useContext2();
|
|
2562
|
+
const rbac = useDocumentRBAC2("Blockscene", (state) => state);
|
|
2563
|
+
const { get } = useFetchClient2();
|
|
2564
|
+
const t = useMessages();
|
|
2565
|
+
const user = useAuth("Blockscene", (state) => state.user);
|
|
2566
|
+
const formValues = useForm3("Blockscene", (state) => state.values);
|
|
1747
2567
|
const catalog = useCatalog(get);
|
|
1748
|
-
const
|
|
1749
|
-
const
|
|
1750
|
-
const
|
|
1751
|
-
const zones = editableZones(
|
|
2568
|
+
const creating = !c.id && !c.form?.initialValues?.id && !c.form?.initialValues?.documentId;
|
|
2569
|
+
const allowed = (creating ? rbac.canCreateFields : rbac.canUpdateFields) || [];
|
|
2570
|
+
const fields = c.layout?.edit?.layout?.flat(3) || [];
|
|
2571
|
+
const zones = editableZones(
|
|
2572
|
+
c.contentType,
|
|
2573
|
+
formValues ?? c.form?.values,
|
|
2574
|
+
(name) => allowed.includes(name) && (creating || (rbac.canReadFields || []).includes(name)) && fields.find((field) => field.name === name)?.disabled !== true,
|
|
2575
|
+
c.isLoading || c.form?.disabled || !c.form?.addFieldRow
|
|
2576
|
+
).map((zone) => ({ ...zone, label: fields.find((field) => field.name === zone.name)?.label || zone.name }));
|
|
1752
2577
|
const add = (zone, uid) => {
|
|
2578
|
+
const values = formValues ?? c.form.values;
|
|
1753
2579
|
const close = catalog?.groups?.[uid];
|
|
1754
|
-
if (!canInsert(zone, uid, c.
|
|
1755
|
-
|
|
1756
|
-
|
|
2580
|
+
if (c.form.disabled || !canInsert(zone, uid, values, c.components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
|
|
2581
|
+
const at = Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0;
|
|
2582
|
+
c.form.addFieldRow(zone.name, { ...componentDefaults(c.components[uid], c.components), __component: uid }, at);
|
|
2583
|
+
if (close) c.form.addFieldRow(zone.name, { __component: close }, at + 1);
|
|
1757
2584
|
return true;
|
|
1758
2585
|
};
|
|
1759
|
-
if (!catalog?.editor?.enabled) return null;
|
|
1760
|
-
const docKey = `${c.
|
|
1761
|
-
return /* @__PURE__ */
|
|
1762
|
-
Gallery,
|
|
1763
|
-
{
|
|
1764
|
-
|
|
1765
|
-
components,
|
|
1766
|
-
add,
|
|
1767
|
-
Modal,
|
|
1768
|
-
Toggle: ToggleField,
|
|
1769
|
-
get,
|
|
1770
|
-
editor: catalog.editor,
|
|
1771
|
-
docKey,
|
|
1772
|
-
contentType: c.slug,
|
|
1773
|
-
userId: auth.getUserInfo?.()?.id
|
|
1774
|
-
}
|
|
1775
|
-
);
|
|
2586
|
+
if (!zones.length || !catalog?.editor?.enabled) return null;
|
|
2587
|
+
const docKey = `${c.model}:${creating ? "new" : c.id}:${c.form?.initialValues?.locale || ""}`;
|
|
2588
|
+
return { title: t.gallery, content: /* @__PURE__ */ jsxs5(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
2589
|
+
/* @__PURE__ */ jsx6(Gallery, { zones, components: c.components, add, Modal: Modal2, Toggle: ToggleField, get, editor: catalog.editor, docKey, contentType: c.model, userId: user?.id }),
|
|
2590
|
+
/* @__PURE__ */ jsx6(PagePreview, { editor: catalog.editor, groups: catalog.groups || null, Toggle: ToggleField })
|
|
2591
|
+
] }) };
|
|
1776
2592
|
}
|
|
1777
|
-
var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */
|
|
1778
|
-
/* @__PURE__ */
|
|
1779
|
-
/* @__PURE__ */
|
|
2593
|
+
var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs5(Flex5, { gap: 3, children: [
|
|
2594
|
+
/* @__PURE__ */ jsx6(Switch, { name, "aria-label": label, checked: Boolean(value), disabled, onCheckedChange: onChange }),
|
|
2595
|
+
/* @__PURE__ */ jsx6(Typography5, { children: label })
|
|
2596
|
+
] });
|
|
2597
|
+
var TextField = ({ name, label, value, onChange, disabled, placeholder }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
|
|
2598
|
+
/* @__PURE__ */ jsx6(Field.Label, { children: label }),
|
|
2599
|
+
/* @__PURE__ */ jsx6(TextInput, { name, value, disabled, placeholder, onChange: (e) => onChange(e.target.value) })
|
|
2600
|
+
] });
|
|
2601
|
+
var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
|
|
2602
|
+
/* @__PURE__ */ jsx6(Field.Label, { children: label }),
|
|
2603
|
+
/* @__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)) })
|
|
1780
2604
|
] });
|
|
1781
|
-
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)) });
|
|
1782
2605
|
function MediaPicker({ onClose, onSelect }) {
|
|
1783
|
-
const
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1786
|
-
onClose();
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
return /* @__PURE__ */
|
|
2606
|
+
const components = useStrapiApp3("Blockscene", (state) => state.components);
|
|
2607
|
+
const Dialog3 = components?.["media-library"];
|
|
2608
|
+
React6.useEffect(() => {
|
|
2609
|
+
if (!Dialog3) onClose();
|
|
2610
|
+
}, [Dialog3, onClose]);
|
|
2611
|
+
if (!Dialog3) return null;
|
|
2612
|
+
return /* @__PURE__ */ jsx6(Dialog3, { onClose, allowedTypes: ["images"], multiple: false, onSelectAssets: (assets) => onSelect(assets?.[0]) });
|
|
1790
2613
|
}
|
|
1791
2614
|
function usePermissions() {
|
|
1792
2615
|
const { allowedActions, isLoading } = useRBAC(permissions);
|
|
1793
2616
|
return { canRead: allowedActions.canRead, canUpdate: allowedActions.canUpdate, isLoading };
|
|
1794
2617
|
}
|
|
1795
|
-
var SettingsPage = () => /* @__PURE__ */
|
|
1796
|
-
var
|
|
2618
|
+
var SettingsPage = () => /* @__PURE__ */ jsx6(Settings, { useClient: useFetchClient2, usePermissions, MediaPicker, ToggleField, SelectField, TextField, previewSupported: true });
|
|
2619
|
+
var strapi5_default = {
|
|
1797
2620
|
register(app) {
|
|
1798
2621
|
register(app, SettingsPage);
|
|
1799
2622
|
app.registerPlugin({ id: "blockscene", name: "Blockscene" });
|
|
1800
2623
|
},
|
|
1801
2624
|
registerTrads,
|
|
1802
2625
|
bootstrap(app) {
|
|
1803
|
-
app.
|
|
2626
|
+
app.getPlugin("content-manager").apis.addEditViewSidePanel([Panel]);
|
|
1804
2627
|
}
|
|
1805
2628
|
};
|
|
1806
2629
|
export {
|
|
1807
|
-
|
|
2630
|
+
strapi5_default as default
|
|
1808
2631
|
};
|