@bison-lab/payload-core 3.25.0 → 3.26.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/README.md +10 -3
- package/dist/admin.d.mts +13 -4
- package/dist/admin.d.mts.map +1 -1
- package/dist/admin.mjs +289 -55
- package/dist/admin.mjs.map +1 -1
- package/dist/index.d.mts +47 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +186 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -321,9 +321,16 @@ read from the live Features and Roles Globals) so Payload's
|
|
|
321
321
|
`createPages`, `createUsers`, `createMedia`, `slugField`,
|
|
322
322
|
`createRedirects`, `adminOnlyApiTab`, and `documentTitleActions` ship from the same
|
|
323
323
|
React-free entry. Pages take the
|
|
324
|
-
site's hero and layout blocks, reserved-slug predicate,
|
|
325
|
-
path
|
|
326
|
-
|
|
324
|
+
site's hero and layout blocks, reserved-slug predicate, preview
|
|
325
|
+
path, and optional `folderRoots` (`patients`, `providers`, …). Parent
|
|
326
|
+
on the page editor is where the page lives: site root, those roots,
|
|
327
|
+
published CMS pages, folders created by earlier saves, or New folder….
|
|
328
|
+
The address is that folder plus a title leaf until first publish, then
|
|
329
|
+
locks. Layout carries the same standing copy as the hero — a page cannot
|
|
330
|
+
be published without a section — and Publish names that fix instead of
|
|
331
|
+
only `Content → Layout`. Title-follow does not wipe the folder prefix. A taken address is
|
|
332
|
+
refused — never rewritten to `slug-2`. Unlock + a parent change writes a
|
|
333
|
+
308 the same way a typed slug change does. `createRedirects()`
|
|
327
334
|
wraps `@payloadcms/plugin-redirects` for `pages` only (permanent 308) and
|
|
328
335
|
collapses chains when a published slug changes. The plugin stores the
|
|
329
336
|
rows; the site still has to serve them. Pin the plugin to the same
|
package/dist/admin.d.mts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { ThemeConfig } from "@bison-lab/tokens";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
|
-
import { ArrayFieldClientComponent, GroupFieldClientComponent, JSONFieldClientComponent, SelectFieldClientComponent, TextFieldClientComponent, UIFieldClientComponent } from "payload";
|
|
5
|
+
import { ArrayFieldClientComponent, DefaultCellComponentProps, GroupFieldClientComponent, JSONFieldClientComponent, SelectFieldClientComponent, TextFieldClientComponent, UIFieldClientComponent } from "payload";
|
|
6
6
|
|
|
7
7
|
//#region src/admin/color-field.d.ts
|
|
8
8
|
/**
|
|
@@ -128,11 +128,20 @@ declare const RoleSlugField: TextFieldClientComponent;
|
|
|
128
128
|
//#endregion
|
|
129
129
|
//#region src/admin/slug-field.d.ts
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
131
|
+
* Parent, new-folder name, composed address, and Unlock. Slug follows the
|
|
132
|
+
* title leaf until first publish; the folder prefix is not wiped. After
|
|
133
|
+
* publish the cluster locks until Unlock, which warns that the old address
|
|
134
|
+
* will redirect.
|
|
133
135
|
*/
|
|
134
136
|
declare const SlugField: TextFieldClientComponent;
|
|
135
137
|
//#endregion
|
|
138
|
+
//#region src/admin/page-title-cell.d.ts
|
|
139
|
+
/**
|
|
140
|
+
* Indent a Pages list title by how deep its slug sits under a parent folder.
|
|
141
|
+
* DefaultCell still draws the document link — a custom Cell replaces that wrap.
|
|
142
|
+
*/
|
|
143
|
+
declare function PageTitleCell(props: DefaultCellComponentProps): react_jsx_runtime0.JSX.Element;
|
|
144
|
+
//#endregion
|
|
136
145
|
//#region src/admin/role-name-field.d.ts
|
|
137
146
|
/**
|
|
138
147
|
* Role name. Letters and spaces only, so the hidden slug stays
|
|
@@ -221,5 +230,5 @@ declare function AdminNavItemRowLabel(): react_jsx_runtime0.JSX.Element;
|
|
|
221
230
|
*/
|
|
222
231
|
declare const AdminNavEntityField: TextFieldClientComponent;
|
|
223
232
|
//#endregion
|
|
224
|
-
export { AdminNav, AdminNavEntityField, AdminNavItemRowLabel, AdminNavRowLabel, AppearanceField, ColorField, ColorScaleField, ContrastReport, DocumentCreateNew, DocumentTitleActions, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, SlugField, ThemeDocumentControls, ThemeSaveButton, setThemePreviewMode };
|
|
233
|
+
export { AdminNav, AdminNavEntityField, AdminNavItemRowLabel, AdminNavRowLabel, AppearanceField, ColorField, ColorScaleField, ContrastReport, DocumentCreateNew, DocumentTitleActions, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PageTitleCell, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, SlugField, ThemeDocumentControls, ThemeSaveButton, setThemePreviewMode };
|
|
225
234
|
//# sourceMappingURL=admin.d.mts.map
|
package/dist/admin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.d.mts","names":[],"sources":["../src/admin/color-field.tsx","../src/admin/color-scale-field.tsx","../src/admin/library-field.tsx","../src/admin/font-field.tsx","../src/admin/pairing-field.tsx","../src/admin/appearance-field.tsx","../src/admin/grey-scale-field.tsx","../src/admin/contrast-report.tsx","../src/admin/section-heading.tsx","../src/admin/publish-child.tsx","../src/admin/save-button.tsx","../src/admin/preview-mode.tsx","../src/admin/identity-fallback.tsx","../src/admin/look-field.tsx","../src/admin/roles-matrix-field.tsx","../src/admin/roles-row-label.tsx","../src/admin/role-slug-field.tsx","../src/admin/slug-field.tsx","../src/admin/role-name-field.tsx","../src/admin/roles-field.tsx","../src/admin/roles-grants-field.tsx","../src/admin/features-matrix-field.tsx","../src/admin/document-title-actions.tsx","../src/admin/document-create-new.tsx","../src/admin-nav/types.ts","../src/admin/admin-nav.tsx","../src/admin/admin-nav-row-label.tsx","../src/admin/admin-nav-item-row-label.tsx","../src/admin/admin-nav-entity-field.tsx"],"mappings":";;;;;;;;;;;cAWa,UAAA,EAAY,wBAAA;;;;;;;cCsBZ,eAAA,EAAiB,yBAAA;;;;;;;;;cCOjB,YAAA,EAAc,yBAAA;;;;;;;;;cCtBd,SAAA,EAAW,0BAAA;;;;;;;cCNX,YAAA,EAAc,sBAAA;;;;;;;cCCd,eAAA,EAAiB,yBAAA;;;ALF9B;;;;AAAA,cM4Da,cAAA,EAAgB,0BAAA;;;;;;;cCnDhB,cAAA,EAAgB,sBAAA;;;;;;cCbhB,cAAA,EAAgB,sBAAA;;;cC0IhB,YAAA,EAAc,sBAAA;;;;;;;;iBCtIX,qBAAA,CAAA;;AVAhB;;;iBUQgB,eAAA,CAAA,GAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;;iBA0Bf,gBAAA,CAAA,GAAgB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;KCrCpB,gBAAA;AAAA,iBAkCI,mBAAA,CAAoB,IAAA,EAAM,gBAAA;;;;;;;cClC7B,gBAAA,EAAkB,sBAAA;;;;;;;cCIlB,SAAA,EAAW,wBAAA;;;;;;;;cCqBX,gBAAA,EAAkB,yBAAA;;;;;;iBC1Bf,aAAA,CAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;cCChB,aAAA,EAAe,wBAAA
|
|
1
|
+
{"version":3,"file":"admin.d.mts","names":[],"sources":["../src/admin/color-field.tsx","../src/admin/color-scale-field.tsx","../src/admin/library-field.tsx","../src/admin/font-field.tsx","../src/admin/pairing-field.tsx","../src/admin/appearance-field.tsx","../src/admin/grey-scale-field.tsx","../src/admin/contrast-report.tsx","../src/admin/section-heading.tsx","../src/admin/publish-child.tsx","../src/admin/save-button.tsx","../src/admin/preview-mode.tsx","../src/admin/identity-fallback.tsx","../src/admin/look-field.tsx","../src/admin/roles-matrix-field.tsx","../src/admin/roles-row-label.tsx","../src/admin/role-slug-field.tsx","../src/admin/slug-field.tsx","../src/admin/page-title-cell.tsx","../src/admin/role-name-field.tsx","../src/admin/roles-field.tsx","../src/admin/roles-grants-field.tsx","../src/admin/features-matrix-field.tsx","../src/admin/document-title-actions.tsx","../src/admin/document-create-new.tsx","../src/admin-nav/types.ts","../src/admin/admin-nav.tsx","../src/admin/admin-nav-row-label.tsx","../src/admin/admin-nav-item-row-label.tsx","../src/admin/admin-nav-entity-field.tsx"],"mappings":";;;;;;;;;;;cAWa,UAAA,EAAY,wBAAA;;;;;;;cCsBZ,eAAA,EAAiB,yBAAA;;;;;;;;;cCOjB,YAAA,EAAc,yBAAA;;;;;;;;;cCtBd,SAAA,EAAW,0BAAA;;;;;;;cCNX,YAAA,EAAc,sBAAA;;;;;;;cCCd,eAAA,EAAiB,yBAAA;;;ALF9B;;;;AAAA,cM4Da,cAAA,EAAgB,0BAAA;;;;;;;cCnDhB,cAAA,EAAgB,sBAAA;;;;;;cCbhB,cAAA,EAAgB,sBAAA;;;cC0IhB,YAAA,EAAc,sBAAA;;;;;;;;iBCtIX,qBAAA,CAAA;;AVAhB;;;iBUQgB,eAAA,CAAA,GAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;;iBA0Bf,gBAAA,CAAA,GAAgB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;KCrCpB,gBAAA;AAAA,iBAkCI,mBAAA,CAAoB,IAAA,EAAM,gBAAA;;;;;;;cClC7B,gBAAA,EAAkB,sBAAA;;;;;;;cCIlB,SAAA,EAAW,wBAAA;;;;;;;;cCqBX,gBAAA,EAAkB,yBAAA;;;;;;iBC1Bf,aAAA,CAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;cCChB,aAAA,EAAe,wBAAA;;;;;;;;;cC2Bf,SAAA,EAAW,wBAAA;;;;;;;iBC1BR,aAAA,CAAc,KAAA,EAAO,yBAAA,GAAyB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;cCAjD,aAAA,EAAe,wBAAA;;;;;;;;cC8Cf,UAAA,EAAY,0BAAA;;;;;;;;cChCZ,gBAAA,EAAkB,wBAAA;;;;;;;;;ArBZ/B;;csB0Ca,mBAAA,EAAqB,yBAAA;;;iBCflB,oBAAA,CAAA;EAAuB;AAAA;EAAc,QAAA,GAAW,SAAA;AAAA,IAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCxB3D,iBAAA,CAAA;EAAoB;AAAA;EAAU,IAAA;AAAA,IAAc,kBAAA,CAAA,GAAA,CAAA,OAAA;;;UCmB3C,uBAAA;EACf,WAAA;EACA,OAAA;AAAA;;;UC5Be,aAAA;EACf,eAAA,GAAkB,uBAAA;AAAA;;;A1BGpB;;;iB0B6CgB,QAAA,CAAA;EAAW;AAAA,GAAmB,aAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;iBCnD3C,gBAAA,CAAA,GAAgB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;iBCGhB,oBAAA,CAAA,GAAoB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;;cCEvB,mBAAA,EAAqB,wBAAA"}
|
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, Button, CheckboxInput, FieldDescription, FieldError, FieldLabel, Link, Logout, NavGroup, NavToggler, SelectField, fieldBaseClass, useAuth, useConfig, useField, useForm, useFormFields, useNav, useRowLabel } from "@payloadcms/ui";
|
|
3
|
+
import { ArrayField, Button, CheckboxInput, DefaultCell, FieldDescription, FieldError, FieldLabel, Link, Logout, NavGroup, NavToggler, SelectField, fieldBaseClass, useAuth, useConfig, useField, useForm, useFormFields, useNav, 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, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -3006,6 +3006,8 @@ const RoleSlugField = ({ field, path, readOnly }) => {
|
|
|
3006
3006
|
};
|
|
3007
3007
|
//#endregion
|
|
3008
3008
|
//#region src/fields/slug.ts
|
|
3009
|
+
/** Sentinel the Parent control writes while the editor is naming a new folder. */
|
|
3010
|
+
const NEW_FOLDER_PARENT = "__new__";
|
|
3009
3011
|
const SLUG_UNLOCK_WARNING = "This page is live. Changing the address will send visitors from the old URL to the new one.";
|
|
3010
3012
|
function slugifySegment(segment) {
|
|
3011
3013
|
return segment.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -3017,78 +3019,310 @@ function slugifySegment(segment) {
|
|
|
3017
3019
|
function normalizeSlug(value) {
|
|
3018
3020
|
return value.split("/").map(slugifySegment).filter(Boolean).join("/");
|
|
3019
3021
|
}
|
|
3022
|
+
/** The folder prefix Parent stores. `__new__` resolves through `newFolder`. */
|
|
3023
|
+
function parentPrefix(parent, newFolder) {
|
|
3024
|
+
if (parent === "__new__") return normalizeSlug(newFolder ?? "");
|
|
3025
|
+
return typeof parent === "string" ? normalizeSlug(parent) : "";
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* Address from a parent prefix and a title (or an existing leaf). Title-follow
|
|
3029
|
+
* uses this so a folder is not wiped when the title changes.
|
|
3030
|
+
*/
|
|
3031
|
+
function composeSlug(parent, title, newFolder) {
|
|
3032
|
+
const prefix = parentPrefix(parent, newFolder);
|
|
3033
|
+
const leaf = slugifySegment(title);
|
|
3034
|
+
return prefix ? `${prefix}/${leaf}` : leaf;
|
|
3035
|
+
}
|
|
3036
|
+
/** Prefixes that exist only because a child slug lives under them. */
|
|
3037
|
+
function inferredFolders(slugs) {
|
|
3038
|
+
const folders = /* @__PURE__ */ new Set();
|
|
3039
|
+
for (const slug of slugs) {
|
|
3040
|
+
const parts = normalizeSlug(slug).split("/");
|
|
3041
|
+
for (let i = 1; i < parts.length; i += 1) folders.add(parts.slice(0, i).join("/"));
|
|
3042
|
+
}
|
|
3043
|
+
return [...folders].sort();
|
|
3044
|
+
}
|
|
3045
|
+
/** Slash count after the first segment — how far to indent a list row. */
|
|
3046
|
+
function slugDepth(slug) {
|
|
3047
|
+
const normalized = normalizeSlug(slug);
|
|
3048
|
+
if (!normalized) return 0;
|
|
3049
|
+
return normalized.split("/").length - 1;
|
|
3050
|
+
}
|
|
3020
3051
|
//#endregion
|
|
3021
3052
|
//#region src/admin/slug-field.tsx
|
|
3053
|
+
const SEARCH_DELAY_MS = 200;
|
|
3054
|
+
const SEARCH_LIMIT = 8;
|
|
3022
3055
|
/**
|
|
3023
|
-
*
|
|
3024
|
-
*
|
|
3056
|
+
* Parent, new-folder name, composed address, and Unlock. Slug follows the
|
|
3057
|
+
* title leaf until first publish; the folder prefix is not wiped. After
|
|
3058
|
+
* publish the cluster locks until Unlock, which warns that the old address
|
|
3059
|
+
* will redirect.
|
|
3025
3060
|
*/
|
|
3026
3061
|
const SlugField = ({ field, path, readOnly }) => {
|
|
3027
|
-
const
|
|
3028
|
-
const
|
|
3062
|
+
const slug = useField({ path });
|
|
3063
|
+
const parent = useField({ path: "parent" });
|
|
3064
|
+
const newFolder = useField({ path: "newFolder" });
|
|
3065
|
+
const unlock = useField({ path: "slugUnlock" });
|
|
3029
3066
|
const title = useFormFields(([fields]) => fields.title?.value);
|
|
3030
3067
|
const slugLocked = Boolean(useFormFields(([fields]) => fields.slugLocked?.value));
|
|
3031
|
-
const locked = Boolean(readOnly) || slugLocked && !unlock;
|
|
3032
|
-
const
|
|
3033
|
-
|
|
3068
|
+
const locked = Boolean(readOnly) || slugLocked && !unlock.value;
|
|
3069
|
+
const folderRoots = useMemo(() => {
|
|
3070
|
+
return ((field.admin?.custom ?? {}).folderRoots ?? []).map(normalizeSlug).filter(Boolean);
|
|
3071
|
+
}, [field.admin?.custom]);
|
|
3072
|
+
const { config } = useConfig();
|
|
3073
|
+
const collection = "pages";
|
|
3074
|
+
const titleKey = config.collections.find((entry) => entry.slug === collection)?.admin?.useAsTitle ?? "title";
|
|
3075
|
+
const endpoint = `${config.serverURL}${config.routes.api}/${collection}`;
|
|
3076
|
+
const id = useId();
|
|
3077
|
+
const parentInputId = `${id}-parent`;
|
|
3078
|
+
const folderInputId = `${id}-folder`;
|
|
3079
|
+
const addressId = `${id}-address`;
|
|
3080
|
+
const listId = `${id}-list`;
|
|
3081
|
+
const [query, setQuery] = useState("");
|
|
3082
|
+
const [open, setOpen] = useState(false);
|
|
3083
|
+
const [results, setResults] = useState([]);
|
|
3084
|
+
const [allSlugs, setAllSlugs] = useState([]);
|
|
3085
|
+
const [active, setActive] = useState(-1);
|
|
3086
|
+
const creatingFolder = parent.value === NEW_FOLDER_PARENT;
|
|
3087
|
+
const setSlug = slug.setValue;
|
|
3088
|
+
const slugValue = slug.value ?? "";
|
|
3034
3089
|
useEffect(() => {
|
|
3035
3090
|
if (slugLocked || typeof title !== "string") return;
|
|
3036
|
-
const next =
|
|
3037
|
-
if (next !==
|
|
3091
|
+
const next = composeSlug(parent.value, title, newFolder.value);
|
|
3092
|
+
if (next !== slugValue) setSlug(next);
|
|
3038
3093
|
}, [
|
|
3039
|
-
|
|
3094
|
+
newFolder.value,
|
|
3095
|
+
parent.value,
|
|
3096
|
+
setSlug,
|
|
3040
3097
|
slugLocked,
|
|
3041
|
-
|
|
3042
|
-
|
|
3098
|
+
slugValue,
|
|
3099
|
+
title
|
|
3043
3100
|
]);
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3101
|
+
useEffect(() => {
|
|
3102
|
+
if (!open) return;
|
|
3103
|
+
const seq = { cancelled: false };
|
|
3104
|
+
const timer = setTimeout(() => {
|
|
3105
|
+
const params = new URLSearchParams({
|
|
3106
|
+
depth: "0",
|
|
3107
|
+
limit: String(SEARCH_LIMIT),
|
|
3108
|
+
sort: titleKey,
|
|
3109
|
+
"where[_status][equals]": "published"
|
|
3110
|
+
});
|
|
3111
|
+
const trimmed = query.trim();
|
|
3112
|
+
if (trimmed) params.set(`where[${titleKey}][like]`, trimmed);
|
|
3113
|
+
fetch(`${endpoint}?${params.toString()}`, { credentials: "include" }).then((response) => response.ok ? response.json() : { docs: [] }).then((body) => {
|
|
3114
|
+
if (seq.cancelled) return;
|
|
3115
|
+
setResults(body.docs ?? []);
|
|
3116
|
+
}).catch(() => {
|
|
3117
|
+
if (!seq.cancelled) setResults([]);
|
|
3118
|
+
});
|
|
3119
|
+
}, SEARCH_DELAY_MS);
|
|
3120
|
+
return () => {
|
|
3121
|
+
seq.cancelled = true;
|
|
3122
|
+
clearTimeout(timer);
|
|
3123
|
+
};
|
|
3124
|
+
}, [
|
|
3125
|
+
endpoint,
|
|
3126
|
+
open,
|
|
3127
|
+
query,
|
|
3128
|
+
titleKey
|
|
3129
|
+
]);
|
|
3130
|
+
useEffect(() => {
|
|
3131
|
+
let cancelled = false;
|
|
3132
|
+
fetch(`${endpoint}?depth=0&limit=100&sort=slug`, { credentials: "include" }).then((response) => response.ok ? response.json() : { docs: [] }).then((body) => {
|
|
3133
|
+
if (cancelled) return;
|
|
3134
|
+
setAllSlugs((body.docs ?? []).flatMap((doc) => doc.slug ? [doc.slug] : []));
|
|
3135
|
+
}).catch(() => {
|
|
3136
|
+
if (!cancelled) setAllSlugs([]);
|
|
3137
|
+
});
|
|
3138
|
+
return () => {
|
|
3139
|
+
cancelled = true;
|
|
3140
|
+
};
|
|
3141
|
+
}, [endpoint]);
|
|
3142
|
+
const folders = useMemo(() => {
|
|
3143
|
+
const inferred = inferredFolders(allSlugs);
|
|
3144
|
+
return [...new Set([...folderRoots, ...inferred])].sort();
|
|
3145
|
+
}, [allSlugs, folderRoots]);
|
|
3146
|
+
const options = useMemo(() => {
|
|
3147
|
+
const trimmed = query.trim().toLowerCase();
|
|
3148
|
+
const matches = (label, path) => !trimmed || label.toLowerCase().includes(trimmed) || (path ?? "").toLowerCase().includes(trimmed);
|
|
3149
|
+
const list = [{
|
|
3150
|
+
kind: "root",
|
|
3151
|
+
id: "root",
|
|
3152
|
+
label: "Site root"
|
|
3153
|
+
}];
|
|
3154
|
+
for (const folder of folders) if (matches(folder, folder)) list.push({
|
|
3155
|
+
kind: "folder",
|
|
3156
|
+
id: folder,
|
|
3157
|
+
label: folder,
|
|
3158
|
+
path: `/${folder}`
|
|
3159
|
+
});
|
|
3160
|
+
for (const doc of results) {
|
|
3161
|
+
const label = String(doc[titleKey] ?? doc.id);
|
|
3162
|
+
const path = doc.slug ? `/${doc.slug}` : "";
|
|
3163
|
+
if (matches(label, path)) list.push({
|
|
3164
|
+
kind: "page",
|
|
3165
|
+
id: `page:${doc.id}`,
|
|
3166
|
+
label,
|
|
3167
|
+
path,
|
|
3168
|
+
page: doc
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
3171
|
+
list.push({
|
|
3172
|
+
kind: "new",
|
|
3173
|
+
id: NEW_FOLDER_PARENT,
|
|
3174
|
+
label: "New folder…"
|
|
3175
|
+
});
|
|
3176
|
+
return list;
|
|
3177
|
+
}, [
|
|
3178
|
+
folders,
|
|
3179
|
+
query,
|
|
3180
|
+
results,
|
|
3181
|
+
titleKey
|
|
3182
|
+
]);
|
|
3183
|
+
function choose(option) {
|
|
3184
|
+
if (option.kind === "root") {
|
|
3185
|
+
parent.setValue("");
|
|
3186
|
+
newFolder.setValue("");
|
|
3187
|
+
} else if (option.kind === "folder") {
|
|
3188
|
+
parent.setValue(option.id);
|
|
3189
|
+
newFolder.setValue("");
|
|
3190
|
+
} else if (option.kind === "page") {
|
|
3191
|
+
parent.setValue(option.page.slug ?? "");
|
|
3192
|
+
newFolder.setValue("");
|
|
3193
|
+
} else parent.setValue(NEW_FOLDER_PARENT);
|
|
3194
|
+
setQuery("");
|
|
3195
|
+
setOpen(false);
|
|
3196
|
+
setActive(-1);
|
|
3197
|
+
}
|
|
3198
|
+
function onKeyDown(event) {
|
|
3199
|
+
if (event.key === "ArrowDown") {
|
|
3200
|
+
event.preventDefault();
|
|
3201
|
+
if (!open) {
|
|
3202
|
+
setOpen(true);
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
setActive((index) => options.length ? (index + 1) % options.length : -1);
|
|
3206
|
+
} else if (event.key === "ArrowUp") {
|
|
3207
|
+
event.preventDefault();
|
|
3208
|
+
setActive((index) => options.length ? index <= 0 ? options.length - 1 : index - 1 : -1);
|
|
3209
|
+
} else if (event.key === "Enter") {
|
|
3210
|
+
if (!open) return;
|
|
3211
|
+
event.preventDefault();
|
|
3212
|
+
const pick = options[active] ?? options[0];
|
|
3213
|
+
if (pick) choose(pick);
|
|
3214
|
+
} else if (event.key === "Escape") {
|
|
3215
|
+
if (!open) return;
|
|
3216
|
+
event.preventDefault();
|
|
3217
|
+
setOpen(false);
|
|
3218
|
+
setActive(-1);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
const selectedPage = options.find((option) => option.kind === "page" && option.page.slug === parent.value);
|
|
3222
|
+
const selectedFolder = parent.value && parent.value !== "__new__" ? parent.value : "";
|
|
3223
|
+
const parentChip = creatingFolder ? "New folder" : selectedPage ? selectedPage.label : selectedFolder ? selectedFolder : "Site root";
|
|
3224
|
+
const parentKind = creatingFolder ? "Folder" : selectedPage ? "Page" : selectedFolder ? "Folder" : "Root";
|
|
3225
|
+
const address = slug.value ? `/${slug.value}` : "/";
|
|
3226
|
+
const activeId = active >= 0 && open ? `${id}-option-${active}` : void 0;
|
|
3227
|
+
return /* @__PURE__ */ jsx(KitProvider, { children: /* @__PURE__ */ jsxs("div", {
|
|
3228
|
+
className: "bl-page-address",
|
|
3050
3229
|
children: [
|
|
3051
|
-
/* @__PURE__ */ jsx(
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3230
|
+
/* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(Label, { children: "Parent" }), /* @__PURE__ */ jsx("div", {
|
|
3231
|
+
className: "bl-nav-kit__picker",
|
|
3232
|
+
children: locked ? /* @__PURE__ */ jsxs("div", {
|
|
3233
|
+
className: "bl-nav-kit__picker-control",
|
|
3234
|
+
children: [/* @__PURE__ */ jsx(Badge, {
|
|
3235
|
+
variant: "neutral-subtle",
|
|
3236
|
+
children: parentKind
|
|
3237
|
+
}), /* @__PURE__ */ jsx("span", { children: parentChip })]
|
|
3238
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Input, {
|
|
3239
|
+
id: parentInputId,
|
|
3240
|
+
role: "combobox",
|
|
3241
|
+
"aria-label": "Parent",
|
|
3242
|
+
placeholder: "Site root, a folder, or a published page",
|
|
3243
|
+
autoComplete: "off",
|
|
3244
|
+
value: open ? query : parentChip,
|
|
3245
|
+
"aria-expanded": open,
|
|
3246
|
+
"aria-controls": listId,
|
|
3247
|
+
"aria-autocomplete": "list",
|
|
3248
|
+
"aria-activedescendant": activeId,
|
|
3249
|
+
onChange: (event) => {
|
|
3250
|
+
setQuery(event.target.value);
|
|
3251
|
+
setActive(-1);
|
|
3252
|
+
setOpen(true);
|
|
3253
|
+
},
|
|
3254
|
+
onFocus: () => setOpen(true),
|
|
3255
|
+
onBlur: () => setOpen(false),
|
|
3256
|
+
onKeyDown
|
|
3257
|
+
}), open ? /* @__PURE__ */ jsx("ul", {
|
|
3258
|
+
id: listId,
|
|
3259
|
+
role: "listbox",
|
|
3260
|
+
className: "bl-nav-kit__picker-menu",
|
|
3261
|
+
children: options.map((option, index) => /* @__PURE__ */ jsxs("li", {
|
|
3262
|
+
id: `${id}-option-${index}`,
|
|
3263
|
+
role: "option",
|
|
3264
|
+
"aria-selected": index === active,
|
|
3265
|
+
className: "bl-nav-kit__picker-option",
|
|
3266
|
+
onMouseDown: (event) => {
|
|
3267
|
+
event.preventDefault();
|
|
3268
|
+
choose(option);
|
|
3269
|
+
},
|
|
3270
|
+
onMouseEnter: () => setActive(index),
|
|
3271
|
+
children: [/* @__PURE__ */ jsx("span", { children: option.label }), "path" in option && option.path ? /* @__PURE__ */ jsx("span", {
|
|
3272
|
+
className: "bl-nav-kit__picker-path",
|
|
3273
|
+
children: option.path
|
|
3274
|
+
}) : null]
|
|
3275
|
+
}, option.id))
|
|
3276
|
+
}) : null] })
|
|
3277
|
+
})] }),
|
|
3278
|
+
creatingFolder && !locked ? /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(Label, { children: "New folder name" }), /* @__PURE__ */ jsx(Input, {
|
|
3279
|
+
id: folderInputId,
|
|
3280
|
+
"aria-label": "New folder name",
|
|
3281
|
+
value: newFolder.value ?? "",
|
|
3282
|
+
autoComplete: "off",
|
|
3283
|
+
onChange: (event) => newFolder.setValue(event.target.value)
|
|
3284
|
+
})] }) : null,
|
|
3285
|
+
/* @__PURE__ */ jsxs(Field, { children: [
|
|
3286
|
+
/* @__PURE__ */ jsx(Label, { children: "Address" }),
|
|
3287
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3288
|
+
className: "bl-nav-kit__picker-address",
|
|
3289
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
3290
|
+
id: addressId,
|
|
3291
|
+
readOnly: true,
|
|
3070
3292
|
disabled: locked,
|
|
3071
|
-
value:
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
}),
|
|
3076
|
-
slugLocked && !unlock ? /* @__PURE__ */ jsx(Button$1, {
|
|
3293
|
+
value: address,
|
|
3294
|
+
autoComplete: "off",
|
|
3295
|
+
spellCheck: false
|
|
3296
|
+
}), slugLocked && !unlock.value ? /* @__PURE__ */ jsx(Button$1, {
|
|
3077
3297
|
variant: "outline",
|
|
3078
|
-
onClick: () =>
|
|
3298
|
+
onClick: () => unlock.setValue(true),
|
|
3079
3299
|
children: "Unlock"
|
|
3080
|
-
}) : null
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3300
|
+
}) : null]
|
|
3301
|
+
}),
|
|
3302
|
+
unlock.value ? /* @__PURE__ */ jsx("p", { children: SLUG_UNLOCK_WARNING }) : null,
|
|
3303
|
+
/* @__PURE__ */ jsx(FieldError, {
|
|
3304
|
+
message: slug.errorMessage,
|
|
3305
|
+
path,
|
|
3306
|
+
showError: slug.showError
|
|
3307
|
+
})
|
|
3308
|
+
] })
|
|
3088
3309
|
]
|
|
3089
|
-
});
|
|
3310
|
+
}) });
|
|
3090
3311
|
};
|
|
3091
3312
|
//#endregion
|
|
3313
|
+
//#region src/admin/page-title-cell.tsx
|
|
3314
|
+
/**
|
|
3315
|
+
* Indent a Pages list title by how deep its slug sits under a parent folder.
|
|
3316
|
+
* DefaultCell still draws the document link — a custom Cell replaces that wrap.
|
|
3317
|
+
*/
|
|
3318
|
+
function PageTitleCell(props) {
|
|
3319
|
+
return /* @__PURE__ */ jsx("span", {
|
|
3320
|
+
className: "bl-page-title-cell",
|
|
3321
|
+
style: { paddingInlineStart: `${slugDepth(typeof props.rowData?.slug === "string" ? props.rowData.slug : "") * 1.25}rem` },
|
|
3322
|
+
children: /* @__PURE__ */ jsx(DefaultCell, { ...props })
|
|
3323
|
+
});
|
|
3324
|
+
}
|
|
3325
|
+
//#endregion
|
|
3092
3326
|
//#region src/admin/role-name-field.tsx
|
|
3093
3327
|
/**
|
|
3094
3328
|
* Role name. Letters and spaces only, so the hidden slug stays
|
|
@@ -3828,6 +4062,6 @@ const AdminNavEntityField = ({ field, path, permissions, readOnly, schemaPath })
|
|
|
3828
4062
|
});
|
|
3829
4063
|
};
|
|
3830
4064
|
//#endregion
|
|
3831
|
-
export { AdminNav, AdminNavEntityField, AdminNavItemRowLabel, AdminNavRowLabel, AppearanceField, ColorField, ColorScaleField, ContrastReport, DocumentCreateNew, DocumentTitleActions, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, SlugField, ThemeDocumentControls, ThemeSaveButton, setThemePreviewMode };
|
|
4065
|
+
export { AdminNav, AdminNavEntityField, AdminNavItemRowLabel, AdminNavRowLabel, AppearanceField, ColorField, ColorScaleField, ContrastReport, DocumentCreateNew, DocumentTitleActions, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PageTitleCell, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, SlugField, ThemeDocumentControls, ThemeSaveButton, setThemePreviewMode };
|
|
3832
4066
|
|
|
3833
4067
|
//# sourceMappingURL=admin.mjs.map
|