@bison-lab/payload-core 3.13.0 → 3.14.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/README.md +23 -16
- package/dist/access-B3LglN2J.mjs +662 -0
- package/dist/access-B3LglN2J.mjs.map +1 -0
- package/dist/admin.d.mts +25 -5
- package/dist/admin.d.mts.map +1 -1
- package/dist/admin.mjs +443 -178
- package/dist/admin.mjs.map +1 -1
- package/dist/{identity-LAzSeorC.mjs → identity-Dwyq8519.mjs} +1 -1
- package/dist/{identity-LAzSeorC.mjs.map → identity-Dwyq8519.mjs.map} +1 -1
- package/dist/index.d.mts +73 -46
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +55 -557
- package/dist/index.mjs.map +1 -1
- package/dist/{looks-C6kTjvk3.d.mts → looks-Xs6v5vYU.d.mts} +1 -1
- package/dist/{looks-C6kTjvk3.d.mts.map → looks-Xs6v5vYU.d.mts.map} +1 -1
- package/dist/metadata.d.mts +1 -1
- package/dist/metadata.mjs +1 -1
- package/dist/{share-image-C4ILz4p2.mjs → share-image-BlFAJLnd.mjs} +1 -1
- package/dist/{share-image-C4ILz4p2.mjs.map → share-image-BlFAJLnd.mjs.map} +1 -1
- package/dist/theme.d.mts +1 -1
- package/dist/theme.mjs +1 -1
- package/dist/{title-B2-gWQZd.d.mts → title-sWwcCqHT.d.mts} +1 -1
- package/dist/{title-B2-gWQZd.d.mts.map → title-sWwcCqHT.d.mts.map} +1 -1
- package/package.json +3 -3
package/dist/admin.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { a as colorDocFromState, i as SYSTEM_COLOR_KEYS, n as pageEditorLooks, r as pageEditorTokens, s as stateFromColorDoc } from "./looks-BbL309kO.mjs";
|
|
3
|
-
import { ArrayField, CheckboxInput, FieldDescription, FieldError, FieldLabel, fieldBaseClass, useConfig, useField, useForm, useFormFields, useRowLabel } from "@payloadcms/ui";
|
|
3
|
+
import { ArrayField, CheckboxInput, FieldDescription, FieldError, FieldLabel, fieldBaseClass, useAuth, useConfig, useField, useForm, useFormFields, useRowLabel } from "@payloadcms/ui";
|
|
4
4
|
import { GREY_SCALES, SHADE_STEPS, contrastForeground, contrastRatio, createColorScale, deriveDarkPalette, deriveLightPalette, hexToHSL, hslToString, includedShadeSteps, isThemeHex, presetHints, setColorScaleInclude } from "@bison-lab/tokens";
|
|
5
5
|
import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -2515,6 +2515,159 @@ const LookField = ({ field, path, readOnly }) => {
|
|
|
2515
2515
|
});
|
|
2516
2516
|
};
|
|
2517
2517
|
//#endregion
|
|
2518
|
+
//#region src/features/types.ts
|
|
2519
|
+
const FEATURES_SLUG = "features";
|
|
2520
|
+
const FEATURE_GROUPS = [
|
|
2521
|
+
"pages",
|
|
2522
|
+
"media",
|
|
2523
|
+
"theme",
|
|
2524
|
+
"users"
|
|
2525
|
+
];
|
|
2526
|
+
const FEATURE_GROUP_LABELS = {
|
|
2527
|
+
pages: "Pages",
|
|
2528
|
+
media: "Media",
|
|
2529
|
+
theme: "Theme",
|
|
2530
|
+
users: "Users"
|
|
2531
|
+
};
|
|
2532
|
+
/**
|
|
2533
|
+
* Package catalogue. Features itself is not a row — that screen is
|
|
2534
|
+
* Developer-only in code. Empty Global falls back to these defaults.
|
|
2535
|
+
*/
|
|
2536
|
+
const PACKAGE_FEATURES = [
|
|
2537
|
+
{
|
|
2538
|
+
slug: "content",
|
|
2539
|
+
label: "Content",
|
|
2540
|
+
group: "pages",
|
|
2541
|
+
defaultReleased: true
|
|
2542
|
+
},
|
|
2543
|
+
{
|
|
2544
|
+
slug: "publish",
|
|
2545
|
+
label: "Publish",
|
|
2546
|
+
group: "pages",
|
|
2547
|
+
defaultReleased: true
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
slug: "api-tab",
|
|
2551
|
+
label: "API tab",
|
|
2552
|
+
group: "pages",
|
|
2553
|
+
defaultReleased: false
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
slug: "media",
|
|
2557
|
+
label: "Media",
|
|
2558
|
+
group: "media",
|
|
2559
|
+
defaultReleased: true
|
|
2560
|
+
},
|
|
2561
|
+
{
|
|
2562
|
+
slug: "theme-colors",
|
|
2563
|
+
label: "Colors",
|
|
2564
|
+
group: "theme",
|
|
2565
|
+
defaultReleased: true
|
|
2566
|
+
},
|
|
2567
|
+
{
|
|
2568
|
+
slug: "theme-typography",
|
|
2569
|
+
label: "Typography",
|
|
2570
|
+
group: "theme",
|
|
2571
|
+
defaultReleased: true
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
slug: "theme-appearance",
|
|
2575
|
+
label: "Appearance",
|
|
2576
|
+
group: "theme",
|
|
2577
|
+
defaultReleased: true
|
|
2578
|
+
},
|
|
2579
|
+
{
|
|
2580
|
+
slug: "theme-identity",
|
|
2581
|
+
label: "Identity",
|
|
2582
|
+
group: "theme",
|
|
2583
|
+
defaultReleased: true
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
slug: "brand-assets",
|
|
2587
|
+
label: "Brand assets",
|
|
2588
|
+
group: "theme",
|
|
2589
|
+
defaultReleased: true
|
|
2590
|
+
},
|
|
2591
|
+
{
|
|
2592
|
+
slug: "users",
|
|
2593
|
+
label: "Users",
|
|
2594
|
+
group: "users",
|
|
2595
|
+
defaultReleased: true
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
slug: "roles",
|
|
2599
|
+
label: "Roles",
|
|
2600
|
+
group: "users",
|
|
2601
|
+
defaultReleased: true
|
|
2602
|
+
}
|
|
2603
|
+
];
|
|
2604
|
+
function isFeatureGroupId(value) {
|
|
2605
|
+
return typeof value === "string" && FEATURE_GROUPS.includes(value);
|
|
2606
|
+
}
|
|
2607
|
+
function isFeatureSlug(value) {
|
|
2608
|
+
return typeof value === "string" && /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(value);
|
|
2609
|
+
}
|
|
2610
|
+
//#endregion
|
|
2611
|
+
//#region src/features/matrix.ts
|
|
2612
|
+
const MISSING_PACKAGE_FEATURES_MESSAGE = "Features must include Content, Publish, API tab, Media, Theme screens, Brand assets, Users, and Roles.";
|
|
2613
|
+
function featureCatalogue(extras = []) {
|
|
2614
|
+
return [...PACKAGE_FEATURES.map((feature) => ({ ...feature })), ...extras.map((extra) => ({
|
|
2615
|
+
slug: extra.slug,
|
|
2616
|
+
label: extra.label,
|
|
2617
|
+
group: extra.group ?? "users",
|
|
2618
|
+
defaultReleased: extra.defaultReleased ?? false
|
|
2619
|
+
}))];
|
|
2620
|
+
}
|
|
2621
|
+
function defaultFeaturesFieldValue(extras = []) {
|
|
2622
|
+
return featureCatalogue(extras).map((feature) => ({
|
|
2623
|
+
id: feature.slug,
|
|
2624
|
+
slug: feature.slug,
|
|
2625
|
+
label: feature.label,
|
|
2626
|
+
group: feature.group,
|
|
2627
|
+
released: feature.defaultReleased
|
|
2628
|
+
}));
|
|
2629
|
+
}
|
|
2630
|
+
function parseFeaturesMatrix(value) {
|
|
2631
|
+
if (!Array.isArray(value) || value.length === 0) return {
|
|
2632
|
+
ok: false,
|
|
2633
|
+
message: MISSING_PACKAGE_FEATURES_MESSAGE
|
|
2634
|
+
};
|
|
2635
|
+
const rows = [];
|
|
2636
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2637
|
+
for (const item of value) {
|
|
2638
|
+
if (!item || typeof item !== "object") continue;
|
|
2639
|
+
const slug = "slug" in item ? item.slug : void 0;
|
|
2640
|
+
if (!isFeatureSlug(slug) || seen.has(slug)) continue;
|
|
2641
|
+
seen.add(slug);
|
|
2642
|
+
const pack = PACKAGE_FEATURES.find((feature) => feature.slug === slug);
|
|
2643
|
+
const label = "label" in item && typeof item.label === "string" ? item.label : pack?.label ?? slug;
|
|
2644
|
+
const group = "group" in item && isFeatureGroupId(item.group) ? item.group : pack?.group ?? "users";
|
|
2645
|
+
rows.push({
|
|
2646
|
+
id: slug,
|
|
2647
|
+
slug,
|
|
2648
|
+
label,
|
|
2649
|
+
group,
|
|
2650
|
+
released: Boolean("released" in item && item.released)
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
if (PACKAGE_FEATURES.some((feature) => !seen.has(feature.slug))) return {
|
|
2654
|
+
ok: false,
|
|
2655
|
+
message: MISSING_PACKAGE_FEATURES_MESSAGE
|
|
2656
|
+
};
|
|
2657
|
+
return {
|
|
2658
|
+
ok: true,
|
|
2659
|
+
rows
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
function featureGroupLabel(group) {
|
|
2663
|
+
return isFeatureGroupId(group) ? FEATURE_GROUP_LABELS[group] : group;
|
|
2664
|
+
}
|
|
2665
|
+
function packageFeatureLabel(slug, extras = []) {
|
|
2666
|
+
const pack = PACKAGE_FEATURES.find((feature) => feature.slug === slug);
|
|
2667
|
+
if (pack) return pack.label;
|
|
2668
|
+
return extras.find((feature) => feature.slug === slug)?.label ?? slug;
|
|
2669
|
+
}
|
|
2670
|
+
//#endregion
|
|
2518
2671
|
//#region src/roles/types.ts
|
|
2519
2672
|
/**
|
|
2520
2673
|
* The Roles Global a site's generated types will describe. Optional and
|
|
@@ -2533,75 +2686,90 @@ const ROLE_LABELS = {
|
|
|
2533
2686
|
designer: "Designer",
|
|
2534
2687
|
author: "Author"
|
|
2535
2688
|
};
|
|
2536
|
-
const
|
|
2537
|
-
"
|
|
2538
|
-
"
|
|
2539
|
-
"
|
|
2540
|
-
"
|
|
2689
|
+
const THEME_FEATURE_SLUGS = [
|
|
2690
|
+
"theme-colors",
|
|
2691
|
+
"theme-typography",
|
|
2692
|
+
"theme-appearance",
|
|
2693
|
+
"theme-identity",
|
|
2694
|
+
"brand-assets"
|
|
2541
2695
|
];
|
|
2696
|
+
const DEFAULT_ROLE_GRANTS = {
|
|
2697
|
+
developer: [],
|
|
2698
|
+
admin: [
|
|
2699
|
+
"content",
|
|
2700
|
+
"publish",
|
|
2701
|
+
"media",
|
|
2702
|
+
"users",
|
|
2703
|
+
"roles"
|
|
2704
|
+
],
|
|
2705
|
+
designer: [
|
|
2706
|
+
"content",
|
|
2707
|
+
"media",
|
|
2708
|
+
...THEME_FEATURE_SLUGS,
|
|
2709
|
+
"users",
|
|
2710
|
+
"roles"
|
|
2711
|
+
],
|
|
2712
|
+
author: ["content", "media"]
|
|
2713
|
+
};
|
|
2542
2714
|
function isRole(value) {
|
|
2543
2715
|
return typeof value === "string" && ROLES.includes(value);
|
|
2544
2716
|
}
|
|
2545
|
-
/** Lowercase slug: `
|
|
2717
|
+
/** Lowercase slug from a letters-and-spaces name: `designer`, `the-designer`. */
|
|
2546
2718
|
function isRoleSlug(value) {
|
|
2547
|
-
return typeof value === "string" && /^[a-z]
|
|
2719
|
+
return typeof value === "string" && /^[a-z]+(-[a-z]+)*$/.test(value);
|
|
2720
|
+
}
|
|
2721
|
+
/** Strip digits and punctuation as the name is typed. Trailing space stays. */
|
|
2722
|
+
function sanitizeRoleNameInput(value) {
|
|
2723
|
+
return value.replace(/[^A-Za-z ]/g, "").replace(/ {2,}/g, " ");
|
|
2548
2724
|
}
|
|
2549
2725
|
function roleLabel(role, label) {
|
|
2550
2726
|
const trimmed = typeof label === "string" ? label.trim() : "";
|
|
2551
2727
|
if (trimmed) return trimmed;
|
|
2552
2728
|
return isRole(role) ? ROLE_LABELS[role] : role;
|
|
2553
2729
|
}
|
|
2730
|
+
function defaultGrantsForRole(role, extraGrants) {
|
|
2731
|
+
if (isRole(role)) return [...DEFAULT_ROLE_GRANTS[role]];
|
|
2732
|
+
return extraGrants ? [...extraGrants] : [];
|
|
2733
|
+
}
|
|
2554
2734
|
//#endregion
|
|
2555
2735
|
//#region src/roles/matrix.ts
|
|
2556
2736
|
const ROLES_SLUG = "roles";
|
|
2557
2737
|
/**
|
|
2558
|
-
* Default rank and
|
|
2559
|
-
*
|
|
2738
|
+
* Default rank and grants. Developer is implicit (empty grants). Brand
|
|
2739
|
+
* screens default to Designer. The API tab is not granted until released.
|
|
2560
2740
|
*/
|
|
2561
|
-
const DEFAULT_ROLE_MATRIX =
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
publish: true,
|
|
2567
|
-
users: true
|
|
2568
|
-
},
|
|
2569
|
-
{
|
|
2570
|
-
role: "admin",
|
|
2571
|
-
content: true,
|
|
2572
|
-
brand: false,
|
|
2573
|
-
publish: true,
|
|
2574
|
-
users: true
|
|
2575
|
-
},
|
|
2576
|
-
{
|
|
2577
|
-
role: "designer",
|
|
2578
|
-
content: false,
|
|
2579
|
-
brand: true,
|
|
2580
|
-
publish: false,
|
|
2581
|
-
users: false
|
|
2582
|
-
},
|
|
2583
|
-
{
|
|
2584
|
-
role: "author",
|
|
2585
|
-
content: true,
|
|
2586
|
-
brand: false,
|
|
2587
|
-
publish: false,
|
|
2588
|
-
users: false
|
|
2589
|
-
}
|
|
2590
|
-
];
|
|
2591
|
-
const DEVELOPER_DESCRIPTION = "Everything Admin can do, plus the document API tab.";
|
|
2741
|
+
const DEFAULT_ROLE_MATRIX = ROLES.map((role) => ({
|
|
2742
|
+
role,
|
|
2743
|
+
grants: defaultGrantsForRole(role)
|
|
2744
|
+
}));
|
|
2745
|
+
const DEVELOPER_DESCRIPTION = "Everything, including features not released for assignment.";
|
|
2592
2746
|
const MISSING_SEED_ROLES_MESSAGE = "Roles must include Developer, Admin, Designer, and Author.";
|
|
2593
2747
|
const DUPLICATE_ROLE_SLUG_MESSAGE = "Each role needs a unique slug.";
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2748
|
+
function uniqueSlugs(values) {
|
|
2749
|
+
const slugs = [];
|
|
2750
|
+
for (const value of values) if (typeof value === "string" && isFeatureSlug(value) && !slugs.includes(value)) slugs.push(value);
|
|
2751
|
+
return slugs;
|
|
2752
|
+
}
|
|
2753
|
+
/** Read stored grants, or rebuild them from the old capability ticks. */
|
|
2754
|
+
function grantsFromStoredRole(item) {
|
|
2755
|
+
if ("grants" in item && Array.isArray(item.grants)) return uniqueSlugs(item.grants);
|
|
2756
|
+
const grants = [];
|
|
2757
|
+
if ("content" in item && item.content) grants.push("content", "media");
|
|
2758
|
+
if ("publish" in item && item.publish) grants.push("publish");
|
|
2759
|
+
if ("users" in item && item.users) grants.push("users", "roles");
|
|
2760
|
+
if ("brand" in item && item.brand) grants.push(...THEME_FEATURE_SLUGS);
|
|
2761
|
+
return uniqueSlugs(grants);
|
|
2762
|
+
}
|
|
2600
2763
|
function seedRoleRows(extras = []) {
|
|
2601
2764
|
return [...DEFAULT_ROLE_MATRIX.map((row) => ({
|
|
2602
2765
|
...row,
|
|
2766
|
+
grants: [...row.grants],
|
|
2603
2767
|
label: isRole(row.role) ? ROLE_LABELS[row.role] : row.role
|
|
2604
|
-
})), ...extras.map((extra) => ({
|
|
2768
|
+
})), ...extras.map((extra) => ({
|
|
2769
|
+
role: extra.role,
|
|
2770
|
+
label: extra.label,
|
|
2771
|
+
grants: extra.grants ? [...extra.grants] : []
|
|
2772
|
+
}))];
|
|
2605
2773
|
}
|
|
2606
2774
|
function defaultRolesFieldValue(extras = []) {
|
|
2607
2775
|
return seedRoleRows(extras).map((row) => ({
|
|
@@ -2617,10 +2785,7 @@ function readRoleRow(item) {
|
|
|
2617
2785
|
id: role,
|
|
2618
2786
|
role,
|
|
2619
2787
|
label: roleLabel(role, "label" in item && typeof item.label === "string" ? item.label : void 0),
|
|
2620
|
-
|
|
2621
|
-
brand: Boolean("brand" in item && item.brand),
|
|
2622
|
-
publish: Boolean("publish" in item && item.publish),
|
|
2623
|
-
users: Boolean("users" in item && item.users)
|
|
2788
|
+
grants: grantsFromStoredRole(item)
|
|
2624
2789
|
};
|
|
2625
2790
|
}
|
|
2626
2791
|
function parseRolesMatrix(value) {
|
|
@@ -2632,6 +2797,8 @@ function parseRolesMatrix(value) {
|
|
|
2632
2797
|
const seen = /* @__PURE__ */ new Set();
|
|
2633
2798
|
for (const item of value) {
|
|
2634
2799
|
if (!item || typeof item !== "object") continue;
|
|
2800
|
+
const role = "role" in item ? item.role : void 0;
|
|
2801
|
+
if (typeof role !== "string" || role.trim() === "") continue;
|
|
2635
2802
|
const parsed = readRoleRow(item);
|
|
2636
2803
|
if ("error" in parsed) return {
|
|
2637
2804
|
ok: false,
|
|
@@ -2659,15 +2826,15 @@ function roleSelectOptions(matrix = DEFAULT_ROLE_MATRIX) {
|
|
|
2659
2826
|
value: row.role
|
|
2660
2827
|
}));
|
|
2661
2828
|
}
|
|
2662
|
-
/**
|
|
2829
|
+
/** Grant copy only. Developer names the unreleased catalogue; nothing about MCP or seed. */
|
|
2663
2830
|
function roleDescription(role, matrix = DEFAULT_ROLE_MATRIX) {
|
|
2664
2831
|
if (role === "developer") return DEVELOPER_DESCRIPTION;
|
|
2665
2832
|
const row = matrix.find((entry) => entry.role === role);
|
|
2666
2833
|
if (!row) return "No capabilities";
|
|
2667
|
-
return
|
|
2834
|
+
return row.grants.map((slug) => packageFeatureLabel(slug)).join(", ") || "No capabilities";
|
|
2668
2835
|
}
|
|
2669
2836
|
/**
|
|
2670
|
-
* Seed
|
|
2837
|
+
* Seed grants and package labels come back; extra rows stay as they are.
|
|
2671
2838
|
*/
|
|
2672
2839
|
function resetRolesMatrix(current) {
|
|
2673
2840
|
const extras = [];
|
|
@@ -2681,57 +2848,71 @@ function resetRolesMatrix(current) {
|
|
|
2681
2848
|
}
|
|
2682
2849
|
//#endregion
|
|
2683
2850
|
//#region src/admin/roles-matrix-field.tsx
|
|
2684
|
-
const HIDE_ROW_CHROME = `.bl-roles-matrix .array-actions__add,.bl-roles-matrix .array-actions__duplicate,.bl-roles-matrix .array-actions__copy{display:none}.bl-roles-matrix__toolbar{display:flex;justify-content:flex-end;margin-block-end:.75rem}.bl-roles-matrix__reset{min-height:2.25rem;padding:.35rem .9rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px;background:transparent;color:inherit;font:inherit;cursor:pointer}body.bl-roles-matrix-seed-menu .array-actions__remove{display:none}`;
|
|
2851
|
+
const HIDE_ROW_CHROME = `.bl-roles-matrix .array-actions__add,.bl-roles-matrix .array-actions__duplicate,.bl-roles-matrix .array-actions__copy{display:none}.bl-roles-matrix .array-field__add-row{display:none}.bl-roles-matrix--hide-developer .array-field__row:has([data-role-seed="developer"]),.bl-roles-matrix--hide-developer .collapsible:has([data-role-seed="developer"]){display:none}.bl-roles-matrix__toolbar{display:flex;justify-content:flex-end;gap:.5rem;margin-block-end:.75rem}.bl-roles-matrix__reset,.bl-roles-matrix__add{min-height:2.25rem;padding:.35rem .9rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px;background:transparent;color:inherit;font:inherit;cursor:pointer}body.bl-roles-matrix-seed-menu .array-actions__remove{display:none}`;
|
|
2685
2852
|
function seedRoleFromTrigger(trigger) {
|
|
2686
2853
|
let node = trigger.parentElement;
|
|
2687
2854
|
while (node) {
|
|
2688
2855
|
const slugs = node.querySelectorAll("[data-role-slug]");
|
|
2689
|
-
if (slugs.length === 1) return isRole(slugs[0]?.getAttribute("data-role-slug"));
|
|
2856
|
+
if (slugs.length === 1) return isRole(slugs[0]?.getAttribute("data-role-seed") || slugs[0]?.getAttribute("data-role-slug"));
|
|
2690
2857
|
if (slugs.length > 1) return false;
|
|
2691
2858
|
node = node.parentElement;
|
|
2692
2859
|
}
|
|
2693
2860
|
return false;
|
|
2694
2861
|
}
|
|
2862
|
+
function viewerIsDeveloper(user) {
|
|
2863
|
+
if (!user || typeof user !== "object" || !("roles" in user)) return false;
|
|
2864
|
+
const roles = user.roles;
|
|
2865
|
+
return Array.isArray(roles) && roles.includes("developer");
|
|
2866
|
+
}
|
|
2695
2867
|
/**
|
|
2696
2868
|
* Payload's array field: drag rank stays, add is for custom rows, seed
|
|
2697
|
-
* rows have no remove.
|
|
2869
|
+
* rows have no remove. Developer stays in the saved array and is shown
|
|
2870
|
+
* only to a Developer. Reset restores seed ticks and package labels.
|
|
2698
2871
|
*/
|
|
2699
2872
|
const RolesMatrixField = (props) => {
|
|
2700
2873
|
const { setValue, value } = useField({ path: props.path });
|
|
2874
|
+
const { user } = useAuth();
|
|
2875
|
+
const hideDeveloper = !viewerIsDeveloper(user);
|
|
2701
2876
|
const rootRef = useRef(null);
|
|
2702
2877
|
const seedMenu = useRef(false);
|
|
2878
|
+
const onClickCapture = useCallback((event) => {
|
|
2879
|
+
const target = event.target;
|
|
2880
|
+
if (!(target instanceof Element)) return;
|
|
2881
|
+
const trigger = target.closest(".array-actions__button");
|
|
2882
|
+
if (trigger) {
|
|
2883
|
+
const owner = trigger.closest(".array-field");
|
|
2884
|
+
seedMenu.current = owner !== null && owner === rootRef.current?.querySelector(".array-field") && seedRoleFromTrigger(trigger);
|
|
2885
|
+
document.body.classList.toggle("bl-roles-matrix-seed-menu", seedMenu.current);
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
if (!target.closest(".array-actions__remove")) return;
|
|
2889
|
+
if (!seedMenu.current) return;
|
|
2890
|
+
event.preventDefault();
|
|
2891
|
+
event.stopPropagation();
|
|
2892
|
+
}, []);
|
|
2703
2893
|
return /* @__PURE__ */ jsxs("div", {
|
|
2704
|
-
className: "bl-roles-matrix",
|
|
2894
|
+
className: ["bl-roles-matrix", hideDeveloper && "bl-roles-matrix--hide-developer"].filter(Boolean).join(" "),
|
|
2705
2895
|
ref: rootRef,
|
|
2706
|
-
onClickCapture
|
|
2707
|
-
const target = event.target;
|
|
2708
|
-
if (!(target instanceof Element)) return;
|
|
2709
|
-
const trigger = target.closest(".array-actions__button");
|
|
2710
|
-
if (trigger) {
|
|
2711
|
-
const owner = trigger.closest(".array-field");
|
|
2712
|
-
seedMenu.current = owner !== null && owner === rootRef.current?.querySelector(".array-field") && seedRoleFromTrigger(trigger);
|
|
2713
|
-
document.body.classList.toggle("bl-roles-matrix-seed-menu", seedMenu.current);
|
|
2714
|
-
return;
|
|
2715
|
-
}
|
|
2716
|
-
if (!target.closest(".array-actions__remove")) return;
|
|
2717
|
-
if (!seedMenu.current) return;
|
|
2718
|
-
event.preventDefault();
|
|
2719
|
-
event.stopPropagation();
|
|
2720
|
-
}, []),
|
|
2896
|
+
onClickCapture,
|
|
2721
2897
|
children: [
|
|
2722
2898
|
/* @__PURE__ */ jsx("style", {
|
|
2723
2899
|
href: "bl-roles-matrix",
|
|
2724
2900
|
precedence: "default",
|
|
2725
2901
|
children: HIDE_ROW_CHROME
|
|
2726
2902
|
}),
|
|
2727
|
-
/* @__PURE__ */
|
|
2903
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2728
2904
|
className: "bl-roles-matrix__toolbar",
|
|
2729
|
-
children: /* @__PURE__ */ jsx("button", {
|
|
2905
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
2906
|
+
type: "button",
|
|
2907
|
+
className: "bl-roles-matrix__add",
|
|
2908
|
+
onClick: () => rootRef.current?.querySelector(".array-field__add-row")?.click(),
|
|
2909
|
+
children: "Add Role"
|
|
2910
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
2730
2911
|
type: "button",
|
|
2731
2912
|
className: "bl-roles-matrix__reset",
|
|
2732
2913
|
onClick: () => setValue(resetRolesMatrix(value)),
|
|
2733
2914
|
children: "Reset to defaults"
|
|
2734
|
-
})
|
|
2915
|
+
})]
|
|
2735
2916
|
}),
|
|
2736
2917
|
/* @__PURE__ */ jsx(ArrayField, { ...props })
|
|
2737
2918
|
]
|
|
@@ -2745,9 +2926,11 @@ const RolesMatrixField = (props) => {
|
|
|
2745
2926
|
function RolesRowLabel() {
|
|
2746
2927
|
const { data } = useRowLabel();
|
|
2747
2928
|
const role = typeof data.role === "string" ? data.role : "";
|
|
2929
|
+
const seed = typeof data.seed === "string" && data.seed ? data.seed : isRole(role) ? role : "";
|
|
2748
2930
|
const label = roleLabel(role, data.label) || "Role";
|
|
2749
2931
|
return /* @__PURE__ */ jsx("span", {
|
|
2750
2932
|
"data-role-slug": role,
|
|
2933
|
+
"data-role-seed": seed,
|
|
2751
2934
|
children: label
|
|
2752
2935
|
});
|
|
2753
2936
|
}
|
|
@@ -2801,8 +2984,56 @@ const RoleSlugField = ({ field, path, readOnly }) => {
|
|
|
2801
2984
|
});
|
|
2802
2985
|
};
|
|
2803
2986
|
//#endregion
|
|
2987
|
+
//#region src/admin/role-name-field.tsx
|
|
2988
|
+
/**
|
|
2989
|
+
* Role name. Letters and spaces only, so the hidden slug stays
|
|
2990
|
+
* `the-greatest-designer` from `The Greatest Designer`.
|
|
2991
|
+
*/
|
|
2992
|
+
const RoleNameField = ({ field, path, readOnly }) => {
|
|
2993
|
+
const { value, setValue, showError, errorMessage } = useField({ path });
|
|
2994
|
+
const required = Boolean(field.required);
|
|
2995
|
+
const id = `field-${path.replace(/\./g, "__")}`;
|
|
2996
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2997
|
+
className: [
|
|
2998
|
+
fieldBaseClass,
|
|
2999
|
+
"text",
|
|
3000
|
+
readOnly && "read-only"
|
|
3001
|
+
].filter(Boolean).join(" "),
|
|
3002
|
+
children: [
|
|
3003
|
+
/* @__PURE__ */ jsx(FieldLabel, {
|
|
3004
|
+
htmlFor: id,
|
|
3005
|
+
label: field.label,
|
|
3006
|
+
required
|
|
3007
|
+
}),
|
|
3008
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3009
|
+
className: `${fieldBaseClass}__wrap`,
|
|
3010
|
+
children: [/* @__PURE__ */ jsx(FieldError, {
|
|
3011
|
+
message: errorMessage,
|
|
3012
|
+
path,
|
|
3013
|
+
showError
|
|
3014
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
3015
|
+
id,
|
|
3016
|
+
type: "text",
|
|
3017
|
+
autoComplete: "off",
|
|
3018
|
+
spellCheck: false,
|
|
3019
|
+
readOnly: Boolean(readOnly),
|
|
3020
|
+
disabled: Boolean(readOnly),
|
|
3021
|
+
value: value ?? "",
|
|
3022
|
+
onChange: (event) => {
|
|
3023
|
+
if (!readOnly) setValue(sanitizeRoleNameInput(event.target.value));
|
|
3024
|
+
}
|
|
3025
|
+
})]
|
|
3026
|
+
}),
|
|
3027
|
+
/* @__PURE__ */ jsx(FieldDescription, {
|
|
3028
|
+
description: field.admin?.description,
|
|
3029
|
+
path
|
|
3030
|
+
})
|
|
3031
|
+
]
|
|
3032
|
+
});
|
|
3033
|
+
};
|
|
3034
|
+
//#endregion
|
|
2804
3035
|
//#region src/admin/roles-field.tsx
|
|
2805
|
-
const GRID_CSS
|
|
3036
|
+
const GRID_CSS = `.bl-roles__grid{display:grid;gap:.75rem;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));margin-block:.35rem 0}.bl-roles__option{display:flex;flex-direction:column;gap:.25rem;padding:.65rem .75rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px}.bl-roles__option.is-checked{border-color:var(--theme-elevation-400,#94a3b8)}.bl-roles__option.is-included{opacity:.7}.bl-roles__what{font-size:.8rem;line-height:1.35;color:var(--theme-elevation-500,#64748b)}`;
|
|
2806
3037
|
function optionValue(option) {
|
|
2807
3038
|
return typeof option === "string" ? option : String(option.value);
|
|
2808
3039
|
}
|
|
@@ -2881,7 +3112,7 @@ const RolesField = ({ field, path: pathFromProps, readOnly, validate }) => {
|
|
|
2881
3112
|
/* @__PURE__ */ jsx("style", {
|
|
2882
3113
|
href: "bl-roles",
|
|
2883
3114
|
precedence: "default",
|
|
2884
|
-
children: GRID_CSS
|
|
3115
|
+
children: GRID_CSS
|
|
2885
3116
|
}),
|
|
2886
3117
|
/* @__PURE__ */ jsx(FieldLabel, {
|
|
2887
3118
|
as: "span",
|
|
@@ -2933,135 +3164,169 @@ const RolesField = ({ field, path: pathFromProps, readOnly, validate }) => {
|
|
|
2933
3164
|
});
|
|
2934
3165
|
};
|
|
2935
3166
|
//#endregion
|
|
2936
|
-
//#region src/
|
|
3167
|
+
//#region src/admin/roles-grants-field.tsx
|
|
3168
|
+
const LIST_CSS$1 = `.bl-role-grants{margin-block:.2rem 0;font-size:16px}.bl-role-grants__group{margin-block:.75rem 0}.bl-role-grants__group-title{font-size:13px;letter-spacing:.02em;color:var(--theme-elevation-400,#94a3b8);font-weight:650;margin-block-end:.25rem}.bl-role-grants__row{display:flex;align-items:center;gap:.65rem;padding:.35rem 0}.bl-role-grants__row.is-implicit{opacity:.85}.bl-role-grants input{inline-size:1.15rem;block-size:1.15rem}.bl-role-grants input:disabled{cursor:default}.bl-role-grants__note{margin-block:.35rem 0;font-size:13px;color:var(--theme-elevation-500,#64748b)}`;
|
|
3169
|
+
function asGrants(value) {
|
|
3170
|
+
if (!Array.isArray(value)) return [];
|
|
3171
|
+
return value.filter((entry) => typeof entry === "string");
|
|
3172
|
+
}
|
|
3173
|
+
function rolePathFromGrants(path) {
|
|
3174
|
+
return path.replace(/\.grants$/, ".role");
|
|
3175
|
+
}
|
|
2937
3176
|
/**
|
|
2938
|
-
*
|
|
2939
|
-
*
|
|
2940
|
-
*
|
|
3177
|
+
* Released catalogue rows as ticks. Unreleased features do not appear,
|
|
3178
|
+
* except on Developer: every catalogue row is shown granted and locked
|
|
3179
|
+
* so a new Feature is visibly included.
|
|
2941
3180
|
*/
|
|
2942
|
-
const
|
|
2943
|
-
{
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
slug
|
|
2969
|
-
|
|
2970
|
-
fallback: "users",
|
|
2971
|
-
locked: true
|
|
2972
|
-
},
|
|
2973
|
-
{
|
|
2974
|
-
slug: "roles",
|
|
2975
|
-
label: "Roles",
|
|
2976
|
-
fallback: "users",
|
|
2977
|
-
locked: true
|
|
2978
|
-
},
|
|
2979
|
-
{
|
|
2980
|
-
slug: "features",
|
|
2981
|
-
label: "Features",
|
|
2982
|
-
fallback: "users",
|
|
2983
|
-
locked: true
|
|
3181
|
+
const RolesGrantsField = (props) => {
|
|
3182
|
+
const { setValue, value } = useField({ path: props.path });
|
|
3183
|
+
const { value: role } = useField({ path: rolePathFromGrants(props.path) });
|
|
3184
|
+
const { config } = useConfig();
|
|
3185
|
+
const [catalogue, setCatalogue] = useState(defaultFeaturesFieldValue());
|
|
3186
|
+
const api = config?.routes?.api;
|
|
3187
|
+
const serverURL = config?.serverURL ?? "";
|
|
3188
|
+
const implicitAll = role === "developer";
|
|
3189
|
+
useEffect(() => {
|
|
3190
|
+
if (!api) return;
|
|
3191
|
+
let cancelled = false;
|
|
3192
|
+
fetch(`${serverURL}${api}/globals/${FEATURES_SLUG}?depth=0`, { credentials: "include" }).then((response) => response.ok ? response.json() : null).then((doc) => {
|
|
3193
|
+
if (cancelled || !doc) return;
|
|
3194
|
+
const parsed = parseFeaturesMatrix(doc.features);
|
|
3195
|
+
if (parsed.ok) setCatalogue(parsed.rows);
|
|
3196
|
+
}).catch(() => void 0);
|
|
3197
|
+
return () => {
|
|
3198
|
+
cancelled = true;
|
|
3199
|
+
};
|
|
3200
|
+
}, [api, serverURL]);
|
|
3201
|
+
const grants = asGrants(value);
|
|
3202
|
+
const shown = implicitAll ? catalogue : catalogue.filter((row) => row.released);
|
|
3203
|
+
function toggle(slug, checked) {
|
|
3204
|
+
if (implicitAll) return;
|
|
3205
|
+
const next = new Set(grants);
|
|
3206
|
+
if (checked) next.add(slug);
|
|
3207
|
+
else next.delete(slug);
|
|
3208
|
+
setValue([...next]);
|
|
2984
3209
|
}
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3210
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3211
|
+
className: "bl-role-grants",
|
|
3212
|
+
children: [
|
|
3213
|
+
/* @__PURE__ */ jsx("style", {
|
|
3214
|
+
href: "bl-role-grants",
|
|
3215
|
+
precedence: "default",
|
|
3216
|
+
children: LIST_CSS$1
|
|
3217
|
+
}),
|
|
3218
|
+
implicitAll ? /* @__PURE__ */ jsx("p", {
|
|
3219
|
+
className: "bl-role-grants__note",
|
|
3220
|
+
children: "Always granted, including features not yet released."
|
|
3221
|
+
}) : null,
|
|
3222
|
+
FEATURE_GROUPS.map((group) => {
|
|
3223
|
+
const members = shown.filter((row) => (row.group ?? "users") === group);
|
|
3224
|
+
if (members.length === 0) return null;
|
|
3225
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3226
|
+
className: "bl-role-grants__group",
|
|
3227
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3228
|
+
className: "bl-role-grants__group-title",
|
|
3229
|
+
children: featureGroupLabel(group)
|
|
3230
|
+
}), members.map((row) => /* @__PURE__ */ jsxs("label", {
|
|
3231
|
+
className: ["bl-role-grants__row", implicitAll && "is-implicit"].filter(Boolean).join(" "),
|
|
3232
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
3233
|
+
type: "checkbox",
|
|
3234
|
+
"aria-label": `${row.label || row.slug}`,
|
|
3235
|
+
checked: implicitAll || grants.includes(row.slug),
|
|
3236
|
+
disabled: implicitAll,
|
|
3237
|
+
onChange: (event) => toggle(row.slug, event.target.checked)
|
|
3238
|
+
}), /* @__PURE__ */ jsx("span", { children: row.label || row.slug })]
|
|
3239
|
+
}, row.slug))]
|
|
3240
|
+
}, group);
|
|
3241
|
+
})
|
|
3242
|
+
]
|
|
3243
|
+
});
|
|
3244
|
+
};
|
|
2990
3245
|
//#endregion
|
|
2991
3246
|
//#region src/admin/features-matrix-field.tsx
|
|
2992
|
-
const
|
|
3247
|
+
const LIST_CSS = `.bl-features{margin-block:.35rem 0;font-size:16px}.bl-features__group{margin-block:1rem 0}.bl-features__group-title{font-size:14px;letter-spacing:.02em;color:var(--theme-elevation-400,#94a3b8);font-weight:650;margin-block-end:.35rem}.bl-features__row{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.55rem 0;border-bottom:1px solid var(--theme-elevation-150,#e2e8f0)}.bl-features__row:last-child{border-bottom:0}.bl-features input{inline-size:1.15rem;block-size:1.15rem}`;
|
|
2993
3248
|
function asRows(value) {
|
|
2994
3249
|
return Array.isArray(value) ? value : [];
|
|
2995
3250
|
}
|
|
3251
|
+
function FeatureReleaseRow({ path, index, row, rows, setRows }) {
|
|
3252
|
+
const { setValue: setReleased } = useField({ path: `${path}.${index}.released` });
|
|
3253
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3254
|
+
className: "bl-features__row",
|
|
3255
|
+
children: [/* @__PURE__ */ jsx("span", { children: row.label || row.slug }), /* @__PURE__ */ jsx("input", {
|
|
3256
|
+
type: "checkbox",
|
|
3257
|
+
"aria-label": `Release ${row.label || row.slug}`,
|
|
3258
|
+
checked: Boolean(row.released),
|
|
3259
|
+
onChange: (event) => {
|
|
3260
|
+
const released = event.target.checked;
|
|
3261
|
+
setReleased(released);
|
|
3262
|
+
setRows(rows.map((entry) => entry.slug === row.slug ? {
|
|
3263
|
+
...entry,
|
|
3264
|
+
released
|
|
3265
|
+
} : entry));
|
|
3266
|
+
}
|
|
3267
|
+
})]
|
|
3268
|
+
});
|
|
3269
|
+
}
|
|
2996
3270
|
/**
|
|
2997
|
-
*
|
|
2998
|
-
*
|
|
3271
|
+
* One release switch per catalogue row, grouped. Off hides the tick on
|
|
3272
|
+
* Roles; Developer still has the feature.
|
|
3273
|
+
*
|
|
3274
|
+
* Writes both the array value and each row's `released` checkbox path.
|
|
3275
|
+
* Payload still registers those child fields; toggling only the parent
|
|
3276
|
+
* array leaves them false and Save stores the old flags.
|
|
2999
3277
|
*/
|
|
3000
3278
|
const FeaturesMatrixField = (props) => {
|
|
3001
3279
|
const { setValue, value } = useField({ path: props.path });
|
|
3002
3280
|
const { config } = useConfig();
|
|
3003
|
-
const [columns, setColumns] = useState(roleSelectOptions(seedRoleRows()));
|
|
3004
3281
|
const api = config?.routes?.api;
|
|
3005
3282
|
const serverURL = config?.serverURL ?? "";
|
|
3006
3283
|
useEffect(() => {
|
|
3007
|
-
if (!api) return;
|
|
3284
|
+
if (!api || asRows(value).length > 0) return;
|
|
3008
3285
|
let cancelled = false;
|
|
3009
|
-
fetch(`${serverURL}${api}/globals/${
|
|
3286
|
+
fetch(`${serverURL}${api}/globals/${FEATURES_SLUG}?depth=0`, { credentials: "include" }).then((response) => response.ok ? response.json() : null).then((doc) => {
|
|
3010
3287
|
if (cancelled || !doc) return;
|
|
3011
|
-
const
|
|
3012
|
-
if (
|
|
3288
|
+
const incoming = asRows(doc.features);
|
|
3289
|
+
if (incoming.length > 0) setValue(incoming, true);
|
|
3013
3290
|
}).catch(() => void 0);
|
|
3014
3291
|
return () => {
|
|
3015
3292
|
cancelled = true;
|
|
3016
3293
|
};
|
|
3017
|
-
}, [
|
|
3294
|
+
}, [
|
|
3295
|
+
api,
|
|
3296
|
+
serverURL,
|
|
3297
|
+
setValue,
|
|
3298
|
+
value
|
|
3299
|
+
]);
|
|
3018
3300
|
const rows = asRows(value);
|
|
3019
|
-
function toggle(slug, role, checked) {
|
|
3020
|
-
if (role === "developer") return;
|
|
3021
|
-
setValue(rows.map((row) => {
|
|
3022
|
-
if (row.slug !== slug || isLockedFeature(row.slug, row.locked)) return row;
|
|
3023
|
-
const roles = new Set(row.roles ?? []);
|
|
3024
|
-
if (checked) roles.add(role);
|
|
3025
|
-
else roles.delete(role);
|
|
3026
|
-
roles.add("developer");
|
|
3027
|
-
return {
|
|
3028
|
-
...row,
|
|
3029
|
-
roles: [...roles]
|
|
3030
|
-
};
|
|
3031
|
-
}));
|
|
3032
|
-
}
|
|
3033
3301
|
return /* @__PURE__ */ jsxs("div", {
|
|
3034
3302
|
className: "bl-features",
|
|
3035
3303
|
children: [/* @__PURE__ */ jsx("style", {
|
|
3036
3304
|
href: "bl-features",
|
|
3037
3305
|
precedence: "default",
|
|
3038
|
-
children:
|
|
3039
|
-
}),
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
}) }, column.value);
|
|
3060
|
-
})] }, row.slug);
|
|
3061
|
-
}) })] })]
|
|
3306
|
+
children: LIST_CSS
|
|
3307
|
+
}), FEATURE_GROUPS.map((group) => {
|
|
3308
|
+
const members = rows.map((row, index) => ({
|
|
3309
|
+
row,
|
|
3310
|
+
index
|
|
3311
|
+
})).filter(({ row }) => (row.group ?? "users") === group);
|
|
3312
|
+
if (members.length === 0) return null;
|
|
3313
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3314
|
+
className: "bl-features__group",
|
|
3315
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3316
|
+
className: "bl-features__group-title",
|
|
3317
|
+
children: featureGroupLabel(group)
|
|
3318
|
+
}), members.map(({ row, index }) => /* @__PURE__ */ jsx(FeatureReleaseRow, {
|
|
3319
|
+
path: props.path,
|
|
3320
|
+
index,
|
|
3321
|
+
row,
|
|
3322
|
+
rows,
|
|
3323
|
+
setRows: (next) => setValue(next)
|
|
3324
|
+
}, row.slug))]
|
|
3325
|
+
}, group);
|
|
3326
|
+
})]
|
|
3062
3327
|
});
|
|
3063
3328
|
};
|
|
3064
3329
|
//#endregion
|
|
3065
|
-
export { AppearanceField, ColorField, ColorScaleField, ContrastReport, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleSlugField, RolesField, RolesMatrixField, RolesRowLabel, SectionHeading, ThemeDocumentControls, ThemeSaveButton };
|
|
3330
|
+
export { AppearanceField, ColorField, ColorScaleField, ContrastReport, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, ThemeDocumentControls, ThemeSaveButton };
|
|
3066
3331
|
|
|
3067
3332
|
//# sourceMappingURL=admin.mjs.map
|