@goodfellow-cms/admin 0.6.0 → 0.8.0
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/editor.js +723 -23
- package/dist/{entry-values-BrY3L0Ai.js → entry-values-Bu8ljnoX.js} +765 -409
- package/dist/es-Dxyy-khR.js +2122 -0
- package/dist/index.d.ts +222 -8
- package/dist/index.js +4703 -3043
- package/dist/plugin-protocol-BgYL9WSm.js +458 -0
- package/dist/plugin.js +38 -3
- package/package.json +6 -5
- package/plugin.css +43 -39
- package/styles.css +483 -12
- package/dist/plugin-protocol-DkvBDqsF.js +0 -119
package/dist/editor.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { COLOR_SCHEME_ATTRIBUTE, DRAWN_SLOT_ELEMENT, MEDIA_DIR, WEEKDAYS, allPages, canFormatMarkdown, cleanEventValue, contentInLanguage, datePart, escapeStyleText, findEntry, formatDate, formatOccurrence, getPageHead, googleFontsUrl, hasLanguages, isAllDay, isEventLike, languageHome, languageLinks, mainLanguage, neverIndexed, pageLinks, pageView, pathLanguage, renderedProblems, shownOccurrence, sitePages, themeToCss, todayIn, wallClock, weekdayName, weekdayOf } from "@goodfellow-cms/core";
|
|
4
|
-
import {
|
|
1
|
+
import { J as editedMarkdown, U as storedData, W as storedEntryFields, Y as formattedHtml, g as mediaUrl, i as showMediaInPreview, n as createMediaPreviews, o as createChannel } from "./plugin-protocol-BgYL9WSm.js";
|
|
2
|
+
import { J as EditorTextContext, T as SETTINGS_SECTIONS, Y as useEditorText, a as AdminContext, b as MediaChoiceContext, d as setThemeChoice, g as ScheduleFields, i as usePreviewStyles, it as StringsProvider, l as useSiteContent, lt as findPage, p as useDarkMode, q as READY_MESSAGE, s as useAdmin, st as useStrings, t as SCHEDULE_PROP, x as MediaChooser } from "./entry-values-Bu8ljnoX.js";
|
|
3
|
+
import { COLOR_SCHEME_ATTRIBUTE, DEFAULT_THEME_COLORS, DRAWN_SLOT_ELEMENT, FONT_SIZES, MEDIA_DIR, MINIMUM_CONTRAST, RADII, SCREEN_SIZES, SIDES, SPACING_STEPS, TEXT_ALIGNS, THEME_COLORS, WEEKDAYS, allPages, canFormatMarkdown, cleanEventValue, colorSlug, contentInLanguage, contrastRatio, customLength, datePart, escapeStyleText, findEntry, formatDate, formatOccurrence, getPageHead, googleFontsUrl, hasLanguages, isAllDay, isCustomColor, isEventLike, isOwnersOnlyField, languageHome, languageLinks, mainLanguage, neverIndexed, pageLinks, pageView, pathLanguage, readBlockStyle, renderedProblems, shownOccurrence, sitePages, themeColorOf, themeToCss, todayIn, wallClock, weekdayName, weekdayOf, withBlockStyle } from "@goodfellow-cms/core";
|
|
4
|
+
import { Box } from "lucide-react";
|
|
5
|
+
import { createContext, createElement, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
6
|
import { createRoot } from "react-dom/client";
|
|
6
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { PageBody, PageContent, SiteProvider, adminSite, applyEntry, createPuckConfig, createPuckConfigs, cx, isTableField, mediaFieldKind, preparePage, siteMetadata, tableCells } from "@goodfellow-cms/react";
|
|
8
|
-
import { AutoField, FieldLabel, Puck, Render, RichTextMenu, createUsePuck, legacySideBarPlugin, migrate, registerOverlayPortal, resolveAllData, useGetPuck } from "@puckeditor/core";
|
|
8
|
+
import { PageBody, PageContent, SiteProvider, adminSite, applyEntry, blockIcon, blockKeywords, createPuckConfig, createPuckConfigs, cx, isStyleField, isTableField, mediaFieldKind, preparePage, siteMetadata, tableCells } from "@goodfellow-cms/react";
|
|
9
|
+
import { AutoField, Drawer, FieldLabel, Puck, Render, RichTextMenu, createUsePuck, legacySideBarPlugin, migrate, registerOverlayPortal, resolveAllData, useGetPuck } from "@puckeditor/core";
|
|
9
10
|
import { createPortal, flushSync } from "react-dom";
|
|
10
11
|
const CANVAS = `.gf-canvas`;
|
|
11
12
|
const CANVAS_CONTAINER = "gf-canvas";
|
|
@@ -299,6 +300,128 @@ function SharePreview({ path }) {
|
|
|
299
300
|
});
|
|
300
301
|
}
|
|
301
302
|
//#endregion
|
|
303
|
+
//#region src/frame/block-icon.tsx
|
|
304
|
+
/** A block's icon, at the block list's size, in the text's color. */
|
|
305
|
+
function BlockIconSvg({ icon }) {
|
|
306
|
+
if (!icon) return /* @__PURE__ */ jsx(Box, {
|
|
307
|
+
className: "gfa-block-icon",
|
|
308
|
+
size: 16,
|
|
309
|
+
strokeWidth: 2,
|
|
310
|
+
"aria-hidden": "true"
|
|
311
|
+
});
|
|
312
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
313
|
+
className: "gfa-block-icon",
|
|
314
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
315
|
+
width: "16",
|
|
316
|
+
height: "16",
|
|
317
|
+
viewBox: "0 0 24 24",
|
|
318
|
+
fill: "none",
|
|
319
|
+
stroke: "currentColor",
|
|
320
|
+
strokeWidth: "2",
|
|
321
|
+
strokeLinecap: "round",
|
|
322
|
+
strokeLinejoin: "round",
|
|
323
|
+
"aria-hidden": "true",
|
|
324
|
+
children: icon.map(([shape, attributes], index) => createElement(shape, {
|
|
325
|
+
key: index,
|
|
326
|
+
...attributes
|
|
327
|
+
}))
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/** Puck's block list item, with the block's icon before its name. */
|
|
331
|
+
function drawerItemWithIcon(icons) {
|
|
332
|
+
return function DrawerItemWithIcon({ children, name }) {
|
|
333
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
334
|
+
className: "gfa-drawer-item",
|
|
335
|
+
children: [/* @__PURE__ */ jsx(BlockIconSvg, { icon: icons.get(name) }), children]
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/frame/block-search.tsx
|
|
341
|
+
/** The blocks of a Puck config, with their groups and the site's keywords for them, in the list's order. */
|
|
342
|
+
function searchableBlocks(config, siteConfig) {
|
|
343
|
+
const groups = /* @__PURE__ */ new Map();
|
|
344
|
+
const order = [];
|
|
345
|
+
for (const [key, category] of Object.entries(config.categories ?? {})) for (const name of category?.components ?? []) {
|
|
346
|
+
const block = String(name);
|
|
347
|
+
if (!config.components[block] || groups.has(block)) continue;
|
|
348
|
+
groups.set(block, category?.title ?? key);
|
|
349
|
+
order.push(block);
|
|
350
|
+
}
|
|
351
|
+
for (const name of Object.keys(config.components)) if (!groups.has(name)) order.push(name);
|
|
352
|
+
return order.map((name) => {
|
|
353
|
+
const block = siteConfig?.blocks[name];
|
|
354
|
+
return {
|
|
355
|
+
name,
|
|
356
|
+
label: config.components[name]?.label ?? name,
|
|
357
|
+
...groups.has(name) && { group: groups.get(name) },
|
|
358
|
+
keywords: (block && blockKeywords(block)) ?? []
|
|
359
|
+
};
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/** A block's name as words: `MediaText` is "media text". */
|
|
363
|
+
function nameWords(name) {
|
|
364
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ");
|
|
365
|
+
}
|
|
366
|
+
/** Whether every word typed starts a word of `text`. */
|
|
367
|
+
function startsWords(text, words) {
|
|
368
|
+
const lower = ` ${text.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ")}`;
|
|
369
|
+
return words.every((word) => lower.includes(` ${word}`));
|
|
370
|
+
}
|
|
371
|
+
/** The blocks `query` finds, best first: by label, then by name or keyword, then by group. */
|
|
372
|
+
function searchBlocks(query, blocks) {
|
|
373
|
+
const words = query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
374
|
+
if (words.length === 0) return [...blocks];
|
|
375
|
+
const rank = (block) => {
|
|
376
|
+
if (block.label.toLowerCase().startsWith(query.trim().toLowerCase())) return 0;
|
|
377
|
+
if (startsWords(block.label, words)) return 1;
|
|
378
|
+
if (startsWords(nameWords(block.name), words) || block.keywords.some((keyword) => startsWords(keyword, words))) return 2;
|
|
379
|
+
if (block.group && startsWords(`${block.group} ${block.label}`, words)) return 3;
|
|
380
|
+
};
|
|
381
|
+
return blocks.map((block, index) => ({
|
|
382
|
+
block,
|
|
383
|
+
index,
|
|
384
|
+
rank: rank(block)
|
|
385
|
+
})).filter((found) => found.rank !== void 0).sort((a, b) => a.rank - b.rank || a.index - b.index).map(({ block }) => block);
|
|
386
|
+
}
|
|
387
|
+
/** Puck's `drawer` override: the search box, then Puck's own list or what's found. */
|
|
388
|
+
function BlockSearch({ blocks, item, children }) {
|
|
389
|
+
const t = useStrings();
|
|
390
|
+
const [query, setQuery] = useState("");
|
|
391
|
+
const id = useId();
|
|
392
|
+
const found = query.trim() ? searchBlocks(query, blocks) : void 0;
|
|
393
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
394
|
+
className: "gfa-block-search",
|
|
395
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
396
|
+
className: "gfa-block-search-box",
|
|
397
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
398
|
+
id,
|
|
399
|
+
type: "search",
|
|
400
|
+
className: "gfa-block-search-input",
|
|
401
|
+
"aria-label": t("editor.blockSearch"),
|
|
402
|
+
placeholder: t("editor.blockSearch"),
|
|
403
|
+
value: query,
|
|
404
|
+
onChange: (event) => setQuery(event.target.value),
|
|
405
|
+
onKeyDown: (event) => {
|
|
406
|
+
if (event.key === "Escape") setQuery("");
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
}), found === void 0 ? children : found.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
410
|
+
className: "gfa-block-search-none",
|
|
411
|
+
role: "status",
|
|
412
|
+
children: t("editor.blockSearchNone")
|
|
413
|
+
}) : /* @__PURE__ */ jsx("section", {
|
|
414
|
+
className: "gfa-block-search-results",
|
|
415
|
+
"aria-label": t("editor.blockSearchFound"),
|
|
416
|
+
children: /* @__PURE__ */ jsx(Drawer, { children: found.map((block) => /* @__PURE__ */ jsx(Drawer.Item, {
|
|
417
|
+
name: block.name,
|
|
418
|
+
label: block.label,
|
|
419
|
+
children: item
|
|
420
|
+
}, block.name)) })
|
|
421
|
+
})]
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
//#endregion
|
|
302
425
|
//#region src/event-field.tsx
|
|
303
426
|
const EVERY_LABELS = {
|
|
304
427
|
none: "event.repeat.none",
|
|
@@ -1199,6 +1322,7 @@ function FrameProviders({ config, client, init, site, children }) {
|
|
|
1199
1322
|
drafts: {
|
|
1200
1323
|
kept: true,
|
|
1201
1324
|
names: [],
|
|
1325
|
+
reviews: [],
|
|
1202
1326
|
open: async () => refused(),
|
|
1203
1327
|
publishAll: async () => refused(),
|
|
1204
1328
|
submit: async () => refused(),
|
|
@@ -1230,17 +1354,24 @@ function FrameProviders({ config, client, init, site, children }) {
|
|
|
1230
1354
|
kind,
|
|
1231
1355
|
value
|
|
1232
1356
|
}), [client]);
|
|
1357
|
+
const text = useMemo(() => ({
|
|
1358
|
+
puck: init.puck ?? {},
|
|
1359
|
+
labels: init.labels ?? {}
|
|
1360
|
+
}), [init.puck, init.labels]);
|
|
1233
1361
|
return /* @__PURE__ */ jsx(StringsProvider, {
|
|
1234
1362
|
strings: init.strings,
|
|
1235
|
-
children: /* @__PURE__ */ jsx(
|
|
1236
|
-
value:
|
|
1237
|
-
children: /* @__PURE__ */ jsx(
|
|
1238
|
-
value:
|
|
1239
|
-
children: /* @__PURE__ */ jsx(
|
|
1240
|
-
value:
|
|
1241
|
-
children: /* @__PURE__ */ jsx(
|
|
1242
|
-
value:
|
|
1243
|
-
children
|
|
1363
|
+
children: /* @__PURE__ */ jsx(EditorTextContext.Provider, {
|
|
1364
|
+
value: text,
|
|
1365
|
+
children: /* @__PURE__ */ jsx(AdminClientContext.Provider, {
|
|
1366
|
+
value: client,
|
|
1367
|
+
children: /* @__PURE__ */ jsx(FrameCodeContext.Provider, {
|
|
1368
|
+
value: code,
|
|
1369
|
+
children: /* @__PURE__ */ jsx(AdminContext.Provider, {
|
|
1370
|
+
value: admin,
|
|
1371
|
+
children: /* @__PURE__ */ jsx(MediaChoiceContext.Provider, {
|
|
1372
|
+
value: chooseMedia,
|
|
1373
|
+
children
|
|
1374
|
+
})
|
|
1244
1375
|
})
|
|
1245
1376
|
})
|
|
1246
1377
|
})
|
|
@@ -1248,6 +1379,121 @@ function FrameProviders({ config, client, init, site, children }) {
|
|
|
1248
1379
|
});
|
|
1249
1380
|
}
|
|
1250
1381
|
//#endregion
|
|
1382
|
+
//#region src/frame/labels.ts
|
|
1383
|
+
/** A label in the admin panel's language, or as it is where the table has none. */
|
|
1384
|
+
function translated(labels, text) {
|
|
1385
|
+
return text === void 0 ? text : labels[text] ?? text;
|
|
1386
|
+
}
|
|
1387
|
+
function withFieldLabels(field, labels) {
|
|
1388
|
+
return {
|
|
1389
|
+
...field,
|
|
1390
|
+
...field.label !== void 0 && { label: translated(labels, field.label) },
|
|
1391
|
+
...typeof field.placeholder === "string" && { placeholder: translated(labels, field.placeholder) },
|
|
1392
|
+
...Array.isArray(field.options) && { options: field.options.map((option) => ({
|
|
1393
|
+
...option,
|
|
1394
|
+
label: translated(labels, option.label) ?? option.label
|
|
1395
|
+
})) },
|
|
1396
|
+
...field.arrayFields && { arrayFields: withFieldsLabels(field.arrayFields, labels) },
|
|
1397
|
+
...field.objectFields && { objectFields: withFieldsLabels(field.objectFields, labels) }
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
function withFieldsLabels(fields, labels) {
|
|
1401
|
+
return Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, withFieldLabels(field, labels)]));
|
|
1402
|
+
}
|
|
1403
|
+
function withComponentLabels(component, labels) {
|
|
1404
|
+
if (!component) return component;
|
|
1405
|
+
const { resolveFields, label } = component;
|
|
1406
|
+
return {
|
|
1407
|
+
...component,
|
|
1408
|
+
...label !== void 0 && { label: translated(labels, label) },
|
|
1409
|
+
...component.fields && { fields: withFieldsLabels(component.fields, labels) },
|
|
1410
|
+
...resolveFields && { resolveFields: async (...args) => withFieldsLabels(await resolveFields(...args), labels) }
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* The config with blocks' names, groups and settings' labels, choices and
|
|
1415
|
+
* placeholders in the admin panel's language, looked up by their English
|
|
1416
|
+
* wording, so a site's own blocks that word theirs the same way are
|
|
1417
|
+
* translated too. Only what the editor shows changes: props, choices' values
|
|
1418
|
+
* and what blocks render stay as they are.
|
|
1419
|
+
*/
|
|
1420
|
+
function withLabels(config, labels) {
|
|
1421
|
+
if (Object.keys(labels).length === 0) return config;
|
|
1422
|
+
const components = Object.fromEntries(Object.entries(config.components).map(([name, component]) => [name, withComponentLabels(component, labels)]));
|
|
1423
|
+
const categories = config.categories && Object.fromEntries(Object.entries(config.categories).map(([name, category]) => [name, {
|
|
1424
|
+
...category,
|
|
1425
|
+
...category.title !== void 0 && { title: translated(labels, category.title) }
|
|
1426
|
+
}]));
|
|
1427
|
+
return {
|
|
1428
|
+
...config,
|
|
1429
|
+
components,
|
|
1430
|
+
...categories && { categories },
|
|
1431
|
+
...config.root && { root: withComponentLabels(config.root, labels) }
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
//#endregion
|
|
1435
|
+
//#region src/frame/owners-only.tsx
|
|
1436
|
+
/**
|
|
1437
|
+
* Block settings only owners may change (`metadata: { owners: true }`, such as
|
|
1438
|
+
* Custom HTML used exactly as written), shown to editors on full-stack sites
|
|
1439
|
+
* as they are, without a way to change them. Their server refuses such a
|
|
1440
|
+
* change anyway (`ownersOnlyChanges()`); this only says so up front.
|
|
1441
|
+
*/
|
|
1442
|
+
function valueLabel(field, value) {
|
|
1443
|
+
const option = ("options" in field && Array.isArray(field.options) ? field.options : []).find((choice) => JSON.stringify(choice.value) === JSON.stringify(value));
|
|
1444
|
+
if (option) return String(option.label);
|
|
1445
|
+
return value === void 0 || value === null ? "" : String(value);
|
|
1446
|
+
}
|
|
1447
|
+
function LockedField({ field, value }) {
|
|
1448
|
+
const t = useStrings();
|
|
1449
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1450
|
+
className: "gfa-locked-field",
|
|
1451
|
+
children: [
|
|
1452
|
+
field.label && /* @__PURE__ */ jsx("div", {
|
|
1453
|
+
className: "gfa-locked-field-label",
|
|
1454
|
+
children: field.label
|
|
1455
|
+
}),
|
|
1456
|
+
/* @__PURE__ */ jsx("div", {
|
|
1457
|
+
className: "gfa-locked-field-value",
|
|
1458
|
+
children: valueLabel(field, value)
|
|
1459
|
+
}),
|
|
1460
|
+
/* @__PURE__ */ jsx("p", {
|
|
1461
|
+
className: "gfa-locked-field-note",
|
|
1462
|
+
children: t("owners.only")
|
|
1463
|
+
})
|
|
1464
|
+
]
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
function locked(fields) {
|
|
1468
|
+
return Object.fromEntries(Object.entries(fields).map(([name, field]) => {
|
|
1469
|
+
if (!isOwnersOnlyField(field)) return [name, field];
|
|
1470
|
+
return [name, {
|
|
1471
|
+
type: "custom",
|
|
1472
|
+
...field.label !== void 0 && { label: field.label },
|
|
1473
|
+
...field.metadata !== void 0 && { metadata: field.metadata },
|
|
1474
|
+
render: ({ value }) => /* @__PURE__ */ jsx(LockedField, {
|
|
1475
|
+
field,
|
|
1476
|
+
value
|
|
1477
|
+
})
|
|
1478
|
+
}];
|
|
1479
|
+
}));
|
|
1480
|
+
}
|
|
1481
|
+
/** The config with owners-only settings shown but not changeable, for editors who aren't owners. */
|
|
1482
|
+
function withOwnersOnlyLocked(config) {
|
|
1483
|
+
const components = Object.fromEntries(Object.entries(config.components).map(([name, component]) => {
|
|
1484
|
+
const fields = component.fields ?? {};
|
|
1485
|
+
if (!Object.values(fields).some(isOwnersOnlyField)) return [name, component];
|
|
1486
|
+
return [name, {
|
|
1487
|
+
...component,
|
|
1488
|
+
fields: locked(fields)
|
|
1489
|
+
}];
|
|
1490
|
+
}));
|
|
1491
|
+
return {
|
|
1492
|
+
...config,
|
|
1493
|
+
components
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
//#endregion
|
|
1251
1497
|
//#region src/frame/page-settings.ts
|
|
1252
1498
|
/** For pages and templates, whose settings include `noIndex`; `path` is the page's, for pages. */
|
|
1253
1499
|
function shownSettings(original, path) {
|
|
@@ -1304,6 +1550,420 @@ function shownSettings(original, path) {
|
|
|
1304
1550
|
};
|
|
1305
1551
|
}
|
|
1306
1552
|
//#endregion
|
|
1553
|
+
//#region src/frame/style-field.tsx
|
|
1554
|
+
/**
|
|
1555
|
+
* A block's Style field (`classNameField` in `@goodfellow-cms/react`), which
|
|
1556
|
+
* Puck's editor shows as controls for its look in place of a box of classes:
|
|
1557
|
+
* text alignment, size and color, background, space inside and around it,
|
|
1558
|
+
* rounded corners and the sizes of screen it's hidden on, written into its `className` as Tailwind classes
|
|
1559
|
+
* (`withBlockStyle()` in `@goodfellow-cms/core`), with any other classes kept.
|
|
1560
|
+
*/
|
|
1561
|
+
/** A color's value to show and compare: a theme color's, or the editor's own. */
|
|
1562
|
+
function shownColor(value, colors) {
|
|
1563
|
+
if (!value) return void 0;
|
|
1564
|
+
const theme = themeColorOf(value);
|
|
1565
|
+
return theme ? colors[theme] ?? DEFAULT_THEME_COLORS[theme] : value;
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* A choice from a scale, or a size of the editor's own: the scale's steps,
|
|
1569
|
+
* "As the block has it" and "Your own…", which shows a box for a length.
|
|
1570
|
+
*/
|
|
1571
|
+
function ScaleChoice({ label, value, steps, stepLabel, readOnly, onChange }) {
|
|
1572
|
+
const t = useStrings();
|
|
1573
|
+
const id = useId();
|
|
1574
|
+
const custom = value !== void 0 && !steps.includes(value);
|
|
1575
|
+
const [ownChosen, setOwnChosen] = useState(custom);
|
|
1576
|
+
const [typed, setTyped] = useState(custom ? value : "");
|
|
1577
|
+
useEffect(() => {
|
|
1578
|
+
if (value !== void 0 && !steps.includes(value)) {
|
|
1579
|
+
setOwnChosen(true);
|
|
1580
|
+
setTyped(value);
|
|
1581
|
+
}
|
|
1582
|
+
}, [value, steps]);
|
|
1583
|
+
const own = custom || ownChosen;
|
|
1584
|
+
const invalid = own && typed.trim() !== "" && customLength(typed) === void 0;
|
|
1585
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1586
|
+
className: "gfa-style-choice",
|
|
1587
|
+
children: [
|
|
1588
|
+
/* @__PURE__ */ jsx("label", {
|
|
1589
|
+
className: "gfa-style-label",
|
|
1590
|
+
htmlFor: id,
|
|
1591
|
+
children: label
|
|
1592
|
+
}),
|
|
1593
|
+
/* @__PURE__ */ jsxs("select", {
|
|
1594
|
+
id,
|
|
1595
|
+
className: "gfa-input",
|
|
1596
|
+
disabled: readOnly,
|
|
1597
|
+
value: own ? "custom" : value ?? "",
|
|
1598
|
+
onChange: (event) => {
|
|
1599
|
+
const chosen = event.target.value;
|
|
1600
|
+
if (chosen === "custom") {
|
|
1601
|
+
setOwnChosen(true);
|
|
1602
|
+
const length = customLength(typed);
|
|
1603
|
+
if (length) onChange(length);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
setOwnChosen(false);
|
|
1607
|
+
onChange(chosen === "" ? void 0 : chosen);
|
|
1608
|
+
},
|
|
1609
|
+
children: [
|
|
1610
|
+
/* @__PURE__ */ jsx("option", {
|
|
1611
|
+
value: "",
|
|
1612
|
+
children: t("style.default")
|
|
1613
|
+
}),
|
|
1614
|
+
steps.map((step) => /* @__PURE__ */ jsx("option", {
|
|
1615
|
+
value: step,
|
|
1616
|
+
children: stepLabel(step)
|
|
1617
|
+
}, step)),
|
|
1618
|
+
/* @__PURE__ */ jsx("option", {
|
|
1619
|
+
value: "custom",
|
|
1620
|
+
children: t("style.custom")
|
|
1621
|
+
})
|
|
1622
|
+
]
|
|
1623
|
+
}),
|
|
1624
|
+
own && /* @__PURE__ */ jsx("input", {
|
|
1625
|
+
className: "gfa-input",
|
|
1626
|
+
value: typed,
|
|
1627
|
+
readOnly,
|
|
1628
|
+
"aria-label": `${label}: ${t("style.customLength")}`,
|
|
1629
|
+
placeholder: t("style.customLength"),
|
|
1630
|
+
"aria-invalid": invalid,
|
|
1631
|
+
onChange: (event) => {
|
|
1632
|
+
setTyped(event.target.value);
|
|
1633
|
+
const length = customLength(event.target.value);
|
|
1634
|
+
if (length) onChange(length);
|
|
1635
|
+
}
|
|
1636
|
+
}),
|
|
1637
|
+
invalid && /* @__PURE__ */ jsx("p", {
|
|
1638
|
+
className: "gfa-error-text",
|
|
1639
|
+
children: t("style.customLengthInvalid")
|
|
1640
|
+
})
|
|
1641
|
+
]
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
/** Spacing steps as people read them: `4` is 16px. */
|
|
1645
|
+
function spacingLabel(t, step) {
|
|
1646
|
+
return step === "0" ? t("style.none") : `${Number(step) * 4}px`;
|
|
1647
|
+
}
|
|
1648
|
+
/** Space on every side at once, or on each side. */
|
|
1649
|
+
function SidesChoice({ label, sides, readOnly, onChange }) {
|
|
1650
|
+
const t = useStrings();
|
|
1651
|
+
const values = SIDES.map((side) => sides?.[side]);
|
|
1652
|
+
const same = values.every((value) => value === values[0]);
|
|
1653
|
+
const [each, setEach] = useState(!same);
|
|
1654
|
+
useEffect(() => {
|
|
1655
|
+
if (!same) setEach(true);
|
|
1656
|
+
}, [same]);
|
|
1657
|
+
const set = (next) => {
|
|
1658
|
+
const kept = Object.fromEntries(Object.entries(next).filter(([, value]) => value !== void 0));
|
|
1659
|
+
onChange(Object.keys(kept).length > 0 ? kept : void 0);
|
|
1660
|
+
};
|
|
1661
|
+
const stepLabel = (step) => spacingLabel(t, step);
|
|
1662
|
+
return /* @__PURE__ */ jsxs("fieldset", {
|
|
1663
|
+
className: "gfa-style-group",
|
|
1664
|
+
children: [
|
|
1665
|
+
/* @__PURE__ */ jsx("legend", {
|
|
1666
|
+
className: "gfa-style-label",
|
|
1667
|
+
children: label
|
|
1668
|
+
}),
|
|
1669
|
+
each ? /* @__PURE__ */ jsx("div", {
|
|
1670
|
+
className: "gfa-style-sides",
|
|
1671
|
+
children: SIDES.map((side) => /* @__PURE__ */ jsx(ScaleChoice, {
|
|
1672
|
+
label: t(`style.side.${side}`),
|
|
1673
|
+
value: sides?.[side],
|
|
1674
|
+
steps: SPACING_STEPS,
|
|
1675
|
+
stepLabel,
|
|
1676
|
+
readOnly,
|
|
1677
|
+
onChange: (value) => set({
|
|
1678
|
+
...sides,
|
|
1679
|
+
[side]: value
|
|
1680
|
+
})
|
|
1681
|
+
}, side))
|
|
1682
|
+
}) : /* @__PURE__ */ jsx(ScaleChoice, {
|
|
1683
|
+
label: t("style.allSides"),
|
|
1684
|
+
value: values[0],
|
|
1685
|
+
steps: SPACING_STEPS,
|
|
1686
|
+
stepLabel,
|
|
1687
|
+
readOnly,
|
|
1688
|
+
onChange: (value) => set(Object.fromEntries(SIDES.map((side) => [side, value])))
|
|
1689
|
+
}),
|
|
1690
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1691
|
+
className: "gfa-style-check",
|
|
1692
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1693
|
+
type: "checkbox",
|
|
1694
|
+
checked: each,
|
|
1695
|
+
disabled: readOnly || each && !same,
|
|
1696
|
+
onChange: (event) => setEach(event.target.checked)
|
|
1697
|
+
}), t("style.eachSide")]
|
|
1698
|
+
})
|
|
1699
|
+
]
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
/** The theme's colors as swatches, a color of the editor's own, and back to the block's. */
|
|
1703
|
+
function ColorChoice({ label, value, colors, readOnly, onChange }) {
|
|
1704
|
+
const t = useStrings();
|
|
1705
|
+
const own = value !== void 0 && isCustomColor(value);
|
|
1706
|
+
return /* @__PURE__ */ jsxs("fieldset", {
|
|
1707
|
+
className: "gfa-style-group",
|
|
1708
|
+
children: [/* @__PURE__ */ jsx("legend", {
|
|
1709
|
+
className: "gfa-style-label",
|
|
1710
|
+
children: label
|
|
1711
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1712
|
+
className: "gfa-style-swatches",
|
|
1713
|
+
children: [
|
|
1714
|
+
/* @__PURE__ */ jsx("button", {
|
|
1715
|
+
type: "button",
|
|
1716
|
+
className: "gfa-style-swatch gfa-style-swatch-none",
|
|
1717
|
+
"aria-pressed": value === void 0,
|
|
1718
|
+
disabled: readOnly,
|
|
1719
|
+
title: t("style.colorDefault"),
|
|
1720
|
+
"aria-label": t("style.colorDefault"),
|
|
1721
|
+
onClick: () => onChange(void 0)
|
|
1722
|
+
}),
|
|
1723
|
+
THEME_COLORS.map((color) => {
|
|
1724
|
+
const slug = colorSlug(color);
|
|
1725
|
+
const name = t(`theme.color.${color}`);
|
|
1726
|
+
return /* @__PURE__ */ jsx("button", {
|
|
1727
|
+
type: "button",
|
|
1728
|
+
className: "gfa-style-swatch",
|
|
1729
|
+
style: { background: colors[color] ?? DEFAULT_THEME_COLORS[color] },
|
|
1730
|
+
"aria-pressed": value === slug,
|
|
1731
|
+
disabled: readOnly,
|
|
1732
|
+
title: name,
|
|
1733
|
+
"aria-label": name,
|
|
1734
|
+
onClick: () => onChange(slug)
|
|
1735
|
+
}, color);
|
|
1736
|
+
}),
|
|
1737
|
+
/* @__PURE__ */ jsx("label", {
|
|
1738
|
+
className: "gfa-style-swatch gfa-style-swatch-own",
|
|
1739
|
+
title: t("style.colorCustom"),
|
|
1740
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
1741
|
+
type: "color",
|
|
1742
|
+
value: own ? value : "#000000",
|
|
1743
|
+
disabled: readOnly,
|
|
1744
|
+
"aria-label": `${label}: ${t("style.colorCustom")}`,
|
|
1745
|
+
onChange: (event) => onChange(event.target.value.toLowerCase())
|
|
1746
|
+
})
|
|
1747
|
+
})
|
|
1748
|
+
]
|
|
1749
|
+
})]
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
/** The sizes of screen a block is hidden on, a box for each. */
|
|
1753
|
+
function HiddenOnChoice({ hiddenOn, readOnly, onChange }) {
|
|
1754
|
+
const t = useStrings();
|
|
1755
|
+
const hidden = new Set(hiddenOn);
|
|
1756
|
+
return /* @__PURE__ */ jsxs("fieldset", {
|
|
1757
|
+
className: "gfa-style-group",
|
|
1758
|
+
children: [
|
|
1759
|
+
/* @__PURE__ */ jsx("legend", {
|
|
1760
|
+
className: "gfa-style-label",
|
|
1761
|
+
children: t("style.hiddenOn")
|
|
1762
|
+
}),
|
|
1763
|
+
SCREEN_SIZES.map((size) => /* @__PURE__ */ jsxs("label", {
|
|
1764
|
+
className: "gfa-style-check",
|
|
1765
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1766
|
+
type: "checkbox",
|
|
1767
|
+
checked: hidden.has(size),
|
|
1768
|
+
disabled: readOnly,
|
|
1769
|
+
onChange: (event) => {
|
|
1770
|
+
const next = SCREEN_SIZES.filter((candidate) => candidate === size ? event.target.checked : hidden.has(candidate));
|
|
1771
|
+
onChange(next.length > 0 ? next : void 0);
|
|
1772
|
+
}
|
|
1773
|
+
}), t(`style.hiddenOn.${size}`)]
|
|
1774
|
+
}, size)),
|
|
1775
|
+
hidden.size > 0 && /* @__PURE__ */ jsx("p", {
|
|
1776
|
+
className: "gfa-hint",
|
|
1777
|
+
children: t(hidden.size === SCREEN_SIZES.length ? "style.hiddenOnAll" : "style.hiddenOnHint")
|
|
1778
|
+
})
|
|
1779
|
+
]
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
/** Other classes, kept as typed while the box has focus, so a class isn't taken into the controls halfway. */
|
|
1783
|
+
function OtherClasses({ other, readOnly, onChange }) {
|
|
1784
|
+
const t = useStrings();
|
|
1785
|
+
const id = useId();
|
|
1786
|
+
const joined = other.join(" ");
|
|
1787
|
+
const [typed, setTyped] = useState(joined);
|
|
1788
|
+
const [focused, setFocused] = useState(false);
|
|
1789
|
+
useEffect(() => {
|
|
1790
|
+
if (!focused) setTyped(joined);
|
|
1791
|
+
}, [joined, focused]);
|
|
1792
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1793
|
+
className: "gfa-style-choice",
|
|
1794
|
+
children: [
|
|
1795
|
+
/* @__PURE__ */ jsx("label", {
|
|
1796
|
+
className: "gfa-style-label",
|
|
1797
|
+
htmlFor: id,
|
|
1798
|
+
children: t("style.other")
|
|
1799
|
+
}),
|
|
1800
|
+
/* @__PURE__ */ jsx("input", {
|
|
1801
|
+
id,
|
|
1802
|
+
className: "gfa-input gfa-code",
|
|
1803
|
+
value: typed,
|
|
1804
|
+
readOnly,
|
|
1805
|
+
spellCheck: false,
|
|
1806
|
+
onFocus: () => setFocused(true),
|
|
1807
|
+
onBlur: () => setFocused(false),
|
|
1808
|
+
onChange: (event) => {
|
|
1809
|
+
setTyped(event.target.value);
|
|
1810
|
+
onChange(event.target.value.split(/\s+/).filter(Boolean));
|
|
1811
|
+
}
|
|
1812
|
+
}),
|
|
1813
|
+
/* @__PURE__ */ jsx("p", {
|
|
1814
|
+
className: "gfa-hint",
|
|
1815
|
+
children: t("style.otherHint")
|
|
1816
|
+
})
|
|
1817
|
+
]
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
function StyleControls({ value, colors, readOnly, label, ownAlign, block, onChange }) {
|
|
1821
|
+
const t = useStrings();
|
|
1822
|
+
const { style, other } = readBlockStyle(value);
|
|
1823
|
+
const set = (next) => onChange(withBlockStyle(other, next));
|
|
1824
|
+
const change = (key, next) => {
|
|
1825
|
+
const { [key]: _old, ...rest } = style;
|
|
1826
|
+
set(next === void 0 ? rest : {
|
|
1827
|
+
...rest,
|
|
1828
|
+
[key]: next
|
|
1829
|
+
});
|
|
1830
|
+
};
|
|
1831
|
+
const text = shownColor(style.color, colors);
|
|
1832
|
+
const background = shownColor(style.background, colors);
|
|
1833
|
+
const ratio = text && background ? contrastRatio(text, background) : void 0;
|
|
1834
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1835
|
+
className: "gfa-style-field",
|
|
1836
|
+
children: [
|
|
1837
|
+
label && /* @__PURE__ */ jsx("div", {
|
|
1838
|
+
className: "gfa-style-title",
|
|
1839
|
+
children: label
|
|
1840
|
+
}),
|
|
1841
|
+
!ownAlign && /* @__PURE__ */ jsxs("fieldset", {
|
|
1842
|
+
className: "gfa-style-group",
|
|
1843
|
+
children: [/* @__PURE__ */ jsx("legend", {
|
|
1844
|
+
className: "gfa-style-label",
|
|
1845
|
+
children: t("style.align")
|
|
1846
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1847
|
+
className: "gfa-style-segments",
|
|
1848
|
+
children: [void 0, ...TEXT_ALIGNS].map((align) => /* @__PURE__ */ jsx("button", {
|
|
1849
|
+
type: "button",
|
|
1850
|
+
className: "gfa-style-segment",
|
|
1851
|
+
"aria-pressed": style.align === align,
|
|
1852
|
+
disabled: readOnly,
|
|
1853
|
+
onClick: () => change("align", align),
|
|
1854
|
+
children: t(`style.align.${align ?? "default"}`)
|
|
1855
|
+
}, align ?? "default"))
|
|
1856
|
+
})]
|
|
1857
|
+
}),
|
|
1858
|
+
/* @__PURE__ */ jsx(ScaleChoice, {
|
|
1859
|
+
label: t("style.fontSize"),
|
|
1860
|
+
value: style.fontSize,
|
|
1861
|
+
steps: FONT_SIZES,
|
|
1862
|
+
stepLabel: (step) => t(`style.fontSize.${step}`),
|
|
1863
|
+
readOnly,
|
|
1864
|
+
onChange: (size) => change("fontSize", size)
|
|
1865
|
+
}),
|
|
1866
|
+
/* @__PURE__ */ jsx(ColorChoice, {
|
|
1867
|
+
label: t("style.color"),
|
|
1868
|
+
value: style.color,
|
|
1869
|
+
colors,
|
|
1870
|
+
readOnly,
|
|
1871
|
+
onChange: (color) => change("color", color)
|
|
1872
|
+
}),
|
|
1873
|
+
/* @__PURE__ */ jsx(ColorChoice, {
|
|
1874
|
+
label: t("style.background"),
|
|
1875
|
+
value: style.background,
|
|
1876
|
+
colors,
|
|
1877
|
+
readOnly,
|
|
1878
|
+
onChange: (color) => change("background", color)
|
|
1879
|
+
}),
|
|
1880
|
+
ratio !== void 0 && ratio < MINIMUM_CONTRAST && /* @__PURE__ */ jsx("p", {
|
|
1881
|
+
className: "gfa-hint gfa-style-warning",
|
|
1882
|
+
role: "status",
|
|
1883
|
+
children: t("style.contrast")
|
|
1884
|
+
}),
|
|
1885
|
+
/* @__PURE__ */ jsx(SidesChoice, {
|
|
1886
|
+
label: t("style.padding"),
|
|
1887
|
+
sides: style.padding,
|
|
1888
|
+
readOnly,
|
|
1889
|
+
onChange: (sides) => change("padding", sides)
|
|
1890
|
+
}),
|
|
1891
|
+
/* @__PURE__ */ jsx(SidesChoice, {
|
|
1892
|
+
label: t("style.margin"),
|
|
1893
|
+
sides: style.margin,
|
|
1894
|
+
readOnly,
|
|
1895
|
+
onChange: (sides) => change("margin", sides)
|
|
1896
|
+
}),
|
|
1897
|
+
/* @__PURE__ */ jsx(ScaleChoice, {
|
|
1898
|
+
label: t("style.radius"),
|
|
1899
|
+
value: style.radius,
|
|
1900
|
+
steps: RADII,
|
|
1901
|
+
stepLabel: (step) => t(`style.radius.${step}`),
|
|
1902
|
+
readOnly,
|
|
1903
|
+
onChange: (radius) => change("radius", radius)
|
|
1904
|
+
}),
|
|
1905
|
+
(block || style.hiddenOn) && /* @__PURE__ */ jsx(HiddenOnChoice, {
|
|
1906
|
+
hiddenOn: style.hiddenOn,
|
|
1907
|
+
readOnly,
|
|
1908
|
+
onChange: (hiddenOn) => change("hiddenOn", hiddenOn)
|
|
1909
|
+
}),
|
|
1910
|
+
/* @__PURE__ */ jsx(OtherClasses, {
|
|
1911
|
+
other,
|
|
1912
|
+
readOnly,
|
|
1913
|
+
onChange: (next) => onChange(withBlockStyle(next, style))
|
|
1914
|
+
}),
|
|
1915
|
+
/* @__PURE__ */ jsx("button", {
|
|
1916
|
+
type: "button",
|
|
1917
|
+
className: "gfa-button gfa-button-secondary",
|
|
1918
|
+
disabled: readOnly || Object.keys(style).length === 0 && other.length === 0,
|
|
1919
|
+
onClick: () => onChange(""),
|
|
1920
|
+
children: t("style.reset")
|
|
1921
|
+
})
|
|
1922
|
+
]
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
function withStyleField(field, colors, ownAlign, block) {
|
|
1926
|
+
if (field.type !== "text" || !isStyleField(field)) return field;
|
|
1927
|
+
const render = ({ value, onChange, readOnly }) => /* @__PURE__ */ jsx(StyleControls, {
|
|
1928
|
+
value,
|
|
1929
|
+
colors,
|
|
1930
|
+
readOnly,
|
|
1931
|
+
label: field.label,
|
|
1932
|
+
ownAlign,
|
|
1933
|
+
block,
|
|
1934
|
+
onChange
|
|
1935
|
+
});
|
|
1936
|
+
return {
|
|
1937
|
+
type: "custom",
|
|
1938
|
+
label: field.label,
|
|
1939
|
+
metadata: field.metadata,
|
|
1940
|
+
render
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function withStyleFieldsIn(fields, colors, block) {
|
|
1944
|
+
if (!fields) return fields;
|
|
1945
|
+
const ownAlign = "align" in fields;
|
|
1946
|
+
return Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, withStyleField(field, colors, ownAlign, block)]));
|
|
1947
|
+
}
|
|
1948
|
+
function withStyleComponent(component, colors, block) {
|
|
1949
|
+
if (!component) return component;
|
|
1950
|
+
const { resolveFields } = component;
|
|
1951
|
+
return {
|
|
1952
|
+
...component,
|
|
1953
|
+
...component.fields && { fields: withStyleFieldsIn(component.fields, colors, block) },
|
|
1954
|
+
...resolveFields && { resolveFields: async (...args) => withStyleFieldsIn(await resolveFields(...args), colors, block) }
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
/** The config with every block's Style field, and the page's, drawn as a style's controls. */
|
|
1958
|
+
function withStyleFields(config, colors) {
|
|
1959
|
+
const components = Object.fromEntries(Object.entries(config.components).map(([name, component]) => [name, withStyleComponent(component, colors, true)]));
|
|
1960
|
+
return {
|
|
1961
|
+
...config,
|
|
1962
|
+
components,
|
|
1963
|
+
...config.root && { root: withStyleComponent(config.root, colors, false) }
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
//#endregion
|
|
1307
1967
|
//#region src/frame/table-field.tsx
|
|
1308
1968
|
/**
|
|
1309
1969
|
* The grid of cells a table's field is edited in (`tableField()` in
|
|
@@ -1430,7 +2090,8 @@ const ADMIN_PLACES = {
|
|
|
1430
2090
|
"contact-settings": `#/settings/general?section=${SETTINGS_SECTIONS.contact}`,
|
|
1431
2091
|
"forms-settings": `#/settings/general?section=${SETTINGS_SECTIONS.forms}`,
|
|
1432
2092
|
"theme-settings": "#/settings/theme",
|
|
1433
|
-
"language-settings": "#/settings/languages"
|
|
2093
|
+
"language-settings": "#/settings/languages",
|
|
2094
|
+
"comments-settings": "#/settings/comments"
|
|
1434
2095
|
};
|
|
1435
2096
|
/** Puck's text field, with the media library's chooser below it for fields that hold a media address. */
|
|
1436
2097
|
function TextFieldWithMedia({ field, value, onChange, readOnly, children }) {
|
|
@@ -1608,6 +2269,14 @@ const ENTRY_UI = {
|
|
|
1608
2269
|
};
|
|
1609
2270
|
const entryPlugins = [legacySideBarPlugin()];
|
|
1610
2271
|
/** Puck, set up for one page, header, footer, template or item: site context, previews and the bridge. */
|
|
2272
|
+
/** Puck's permissions for something shown without editing: nothing can be changed, added, moved or removed. */
|
|
2273
|
+
const READ_ONLY = {
|
|
2274
|
+
drag: false,
|
|
2275
|
+
duplicate: false,
|
|
2276
|
+
delete: false,
|
|
2277
|
+
edit: false,
|
|
2278
|
+
insert: false
|
|
2279
|
+
};
|
|
1611
2280
|
function FrameEditor({ open, onOpened }) {
|
|
1612
2281
|
const client = useAdminClient();
|
|
1613
2282
|
const { config: siteConfig, pageConfig, layoutConfig, templateConfig } = useFrameCode();
|
|
@@ -1716,15 +2385,16 @@ function FrameEditor({ open, onOpened }) {
|
|
|
1716
2385
|
callPlugin
|
|
1717
2386
|
]);
|
|
1718
2387
|
const entry = kind === "entry" && collection && open.entry ? findEntry(loaded, collection.id, open.entry) : void 0;
|
|
2388
|
+
const { puck: dictionary, labels } = useEditorText();
|
|
1719
2389
|
const config = useMemo(() => {
|
|
1720
2390
|
if (kind === "entry") {
|
|
1721
2391
|
if (!collection || !entry) throw new Error("The item isn't in the site.");
|
|
1722
|
-
return entryConfig(collection, entry.entry, content, {
|
|
2392
|
+
return withLabels(entryConfig(collection, entry.entry, content, {
|
|
1723
2393
|
templateConfig,
|
|
1724
2394
|
layoutConfig
|
|
1725
|
-
});
|
|
2395
|
+
}), labels);
|
|
1726
2396
|
}
|
|
1727
|
-
return withTableFields(canvasConfig(kind, kind === "page" ? pageConfig : kind === "template" ? templateConfig : layoutConfig, layoutConfig, content.header.data, content.footer.data, site));
|
|
2397
|
+
return withLabels(withTableFields(canvasConfig(kind, kind === "page" ? pageConfig : kind === "template" ? templateConfig : layoutConfig, layoutConfig, content.header.data, content.footer.data, site)), labels);
|
|
1728
2398
|
}, [
|
|
1729
2399
|
kind,
|
|
1730
2400
|
collection,
|
|
@@ -1733,9 +2403,19 @@ function FrameEditor({ open, onOpened }) {
|
|
|
1733
2403
|
templateConfig,
|
|
1734
2404
|
layoutConfig,
|
|
1735
2405
|
content,
|
|
1736
|
-
site
|
|
2406
|
+
site,
|
|
2407
|
+
labels
|
|
1737
2408
|
]);
|
|
1738
2409
|
const described = useMemo(() => describeConfig(config), [config]);
|
|
2410
|
+
const puckConfig = useMemo(() => {
|
|
2411
|
+
const styled = kind === "entry" ? config : withStyleFields(config, content.settings.theme.colors);
|
|
2412
|
+
return open.ownersOnlyLocked ? withOwnersOnlyLocked(styled) : styled;
|
|
2413
|
+
}, [
|
|
2414
|
+
kind,
|
|
2415
|
+
config,
|
|
2416
|
+
content.settings.theme.colors,
|
|
2417
|
+
open.ownersOnlyLocked
|
|
2418
|
+
]);
|
|
1739
2419
|
const told = useRef(void 0);
|
|
1740
2420
|
useEffect(() => {
|
|
1741
2421
|
if (told.current === described) return;
|
|
@@ -1753,32 +2433,51 @@ function FrameEditor({ open, onOpened }) {
|
|
|
1753
2433
|
...kind === "entry" ? ENTRY_UI : {},
|
|
1754
2434
|
...open.ui
|
|
1755
2435
|
}), [kind, open.ui]);
|
|
2436
|
+
const searchable = useMemo(() => searchableBlocks(config, siteConfig), [config, siteConfig]);
|
|
2437
|
+
const drawerItem = useMemo(() => {
|
|
2438
|
+
const icons = /* @__PURE__ */ new Map();
|
|
2439
|
+
for (const [name, block] of Object.entries(siteConfig.blocks)) {
|
|
2440
|
+
const icon = blockIcon(block);
|
|
2441
|
+
if (icon) icons.set(name, icon);
|
|
2442
|
+
}
|
|
2443
|
+
return drawerItemWithIcon(icons);
|
|
2444
|
+
}, [siteConfig]);
|
|
1756
2445
|
const overrides = useMemo(() => ({
|
|
1757
2446
|
header: () => /* @__PURE__ */ jsx(Bridge, {
|
|
1758
2447
|
channel: client.channel,
|
|
1759
2448
|
settings
|
|
1760
2449
|
}),
|
|
2450
|
+
drawer: ({ children }) => /* @__PURE__ */ jsx(BlockSearch, {
|
|
2451
|
+
blocks: searchable,
|
|
2452
|
+
item: drawerItem,
|
|
2453
|
+
children
|
|
2454
|
+
}),
|
|
2455
|
+
drawerItem,
|
|
1761
2456
|
fieldTypes: { text: TextFieldWithMedia },
|
|
1762
2457
|
...kind === "page" && path && { fields: ({ children, itemSelector }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [children, !itemSelector && /* @__PURE__ */ jsx(SharePreview, { path })] }) }
|
|
1763
2458
|
}), [
|
|
1764
2459
|
client,
|
|
1765
2460
|
settings,
|
|
1766
2461
|
kind,
|
|
1767
|
-
path
|
|
2462
|
+
path,
|
|
2463
|
+
searchable,
|
|
2464
|
+
drawerItem
|
|
1768
2465
|
]);
|
|
1769
2466
|
return /* @__PURE__ */ jsx("div", {
|
|
1770
2467
|
className: `gfa-editor-puck gfa-editor-for-${kind}`,
|
|
1771
2468
|
children: /* @__PURE__ */ jsx(SiteProvider, {
|
|
1772
2469
|
value: site,
|
|
1773
2470
|
children: /* @__PURE__ */ jsx(Puck, {
|
|
1774
|
-
config,
|
|
2471
|
+
config: puckConfig,
|
|
1775
2472
|
data: initialData,
|
|
1776
2473
|
metadata: siteMetadata(site),
|
|
1777
2474
|
headerTitle: title,
|
|
2475
|
+
dictionary,
|
|
1778
2476
|
height: "100%",
|
|
1779
2477
|
iframe: { enabled: false },
|
|
1780
2478
|
ui,
|
|
1781
2479
|
...kind === "entry" && { plugins: entryPlugins },
|
|
2480
|
+
...open.readOnly && { permissions: READ_ONLY },
|
|
1782
2481
|
overrides,
|
|
1783
2482
|
onChange: updateView
|
|
1784
2483
|
})
|
|
@@ -1896,7 +2595,8 @@ function editorKey(open) {
|
|
|
1896
2595
|
open.path,
|
|
1897
2596
|
open.collection,
|
|
1898
2597
|
open.entry,
|
|
1899
|
-
open.language
|
|
2598
|
+
open.language,
|
|
2599
|
+
open.reopened
|
|
1900
2600
|
]);
|
|
1901
2601
|
}
|
|
1902
2602
|
/** Opens an editor when the admin panel's page asks, and tells it Puck's config once it's open. */
|