@bison-lab/payload-core 3.18.0 → 3.19.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 CHANGED
@@ -18,14 +18,15 @@ and `react` are needed for its admin fields; `@bison-lab/ui` and
18
18
  Pin the plugin to the same version as `payload`; Payload releases them in
19
19
  lockstep.
20
20
 
21
- ## Five entry points, and why
21
+ ## Six entry points, and why
22
22
 
23
23
  | Import | Contents | Runs where |
24
24
  | ---------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
25
- | `@bison-lab/payload-core` | `seoPlugin`, `createTheme`, `createBrandAssets`, `createRoles`, `createFeatures`, `createPages`, `createUsers`, `createMedia`, `adminOnlyApiTab`, `documentTitleActions`, `seedTheme`, `seedRoles`, `seedFeatures`, predicates, `lookField`, `colorTokenField`, import-map strings, title and text helpers, types | Node. What `payload.config.ts` imports; it loads the plugin. |
25
+ | `@bison-lab/payload-core` | `seoPlugin`, `createTheme`, `createBrandAssets`, `createRoles`, `createFeatures`, `createPages`, `createUsers`, `createMedia`, `createAdminNav`, `adminNav`, `createNavigation`, `adminOnlyApiTab`, `documentTitleActions`, `seedTheme`, `seedRoles`, `seedFeatures`, predicates, `lookField`, `colorTokenField`, import-map strings, title and text helpers, types | Node. What `payload.config.ts` imports; it loads the plugin. |
26
26
  | `@bison-lab/payload-core/metadata` | `pageMetadata`, the title helpers, the same types | Server. What a page route imports; it does not load the plugin. |
27
27
  | `@bison-lab/payload-core/theme` | `getPublishedTheme`, `getPublishedIdentity`, `themeConfigFromDoc`, `themeHead`, `themeHeadFromDoc` | Server. What a root layout imports; it does not load the plugin or React. |
28
- | `@bison-lab/payload-core/admin` | Theme fields, the Roles and Features matrices, and the Users Roles checklist | Admin. Referenced by import-map string; `generate:importmap` writes it. |
28
+ | `@bison-lab/payload-core/navigation` | `getNavigation`, the Header and Footer slugs | Server. What a site header imports; it does not load the plugin or React. |
29
+ | `@bison-lab/payload-core/admin` | Theme fields, the Roles and Features matrices, Admin nav, and the Users Roles checklist | Admin. Referenced by import-map string; `generate:importmap` writes it. |
29
30
  | `@bison-lab/payload-core/react` | `ThemePreview` (legacy; Theme has no preview pane) | Client. Kept so an older site import does not break. |
30
31
 
31
32
  ## What editors get
@@ -290,8 +291,8 @@ assets), and Users (Users, Roles). Features itself is not a row. The
290
291
  document API tab and Better Editor overlay settings are not rows either
291
292
  — they stay code-locked to Developer (`adminOnlyApiTab`,
292
293
  `developerOnlyAccess` / `hideUnlessDeveloper`). A site may pass `extras`
293
- into `createFeatures` — those rows appear only there, including a
294
- future Navigation global:
294
+ into `createFeatures` — those rows appear only there, including the
295
+ Navigation tick `createNavigation` reads:
295
296
 
296
297
  ```ts
297
298
  createFeatures({
@@ -300,10 +301,6 @@ createFeatures({
300
301
  { slug: "navigation", label: "Navigation" },
301
302
  ],
302
303
  });
303
-
304
- // On that Global:
305
- // access: { update: canUseFeature("navigation") }
306
- // Drafts use the same Publish tick as Pages to go live.
307
304
  ```
308
305
 
309
306
  `canUseFeature(slug)` is true when the row is released and a held role
@@ -340,10 +337,68 @@ settings (Better Editor) take `developerOnlyAccess` and
340
337
  `hideUnlessDeveloper` on the site — the package does not add that
341
338
  plugin as a peer.
342
339
 
340
+ ## Admin nav
341
+
342
+ Settings → Admin nav is Developer chrome, not a Features row. A Developer
343
+ adds, renames, deletes, and drags group headers and the collections or
344
+ globals under each. An Admin never opens the editor. Every login can read
345
+ the document so the sidebar can paint. Save writes immediately.
346
+
347
+ `adminNav()` sets `admin.components.Nav` to the package Nav. A consuming
348
+ site must not write a Nav component. An empty document falls back to
349
+ Payload's first-seen walk (collections, then globals, bucketed by
350
+ `admin.group`). Once the document has groups, that list is the sidebar —
351
+ omitted collections and globals stay off it. `group: false` stays off.
352
+ `resolveAdminNav` is the layout source so a later skin (SPI-10) does not
353
+ invent a second one.
354
+
355
+ ```ts
356
+ import { adminNav, createAdminNav } from "@bison-lab/payload-core";
357
+
358
+ export default buildConfig({
359
+ plugins: [adminNav()],
360
+ globals: [createAdminNav(), createRoles(), createFeatures()],
361
+ });
362
+ ```
363
+
364
+ Then `payload generate:importmap` and `payload migrate:create`.
365
+
366
+ ## Header and Footer
367
+
368
+ `createNavigation({ blocks })` returns two Globals so each has its own
369
+ Save / Publish / drafts. Header keeps slug `navigation` and table `nav`
370
+ so an existing one-document row does not move. Footer is
371
+ `navigation-footer` / `navf` — short because nested footer arrays hit
372
+ Postgres's 63-character identifier cap the same way `nav` did. Both sit
373
+ in `admin.group: "Navigation"` until a Developer moves them from Admin
374
+ nav. One Features tick (`navigation`, passed as a `createFeatures`
375
+ extra). Publish still needs the Publish tick. `getNavigation({ payload,
376
+ draft })` finds both and returns `{ header, footer }`.
377
+
378
+ ```ts
379
+ import { createNavigation } from "@bison-lab/payload-core";
380
+ import { getNavigation } from "@bison-lab/payload-core/navigation";
381
+
382
+ globals: [
383
+ ...createNavigation({
384
+ blocks: [megaMenuBlock({ variants }), LinkBlock],
385
+ preview: () => "/preview",
386
+ }),
387
+ ];
388
+
389
+ // In a site header — the /navigation entry keeps the SEO plugin off the layout.
390
+ const { header, footer } = await getNavigation({ payload, draft });
391
+ ```
392
+
393
+ A site that already stored footer columns on the `nav` document copies
394
+ `footer.columns` onto the new global, then drops the footer fields from
395
+ `nav`. Bar behaviour (hide on scroll, viewport, default panel width) is
396
+ not this factory.
397
+
343
398
  ## No generated types
344
399
 
345
400
  The package cannot import a site's `payload-types`, so the shapes it reads
346
- (`SeoMeta`, `SeoImageDoc`, `SeoPage`, `ThemeDoc`, `RolesDoc`) are hand-written and
401
+ (`SeoMeta`, `SeoImageDoc`, `SeoPage`, `ThemeDoc`, `RolesDoc`, `AdminNavDoc`, `NavigationDoc`) are hand-written and
347
402
  structural. A generated `Page`, `Media` or Theme Global is assignable to
348
403
  them; nothing carries an index signature, since an interface will not assign
349
404
  to a type that has one.
@@ -351,6 +406,7 @@ to a type that has one.
351
406
  ## Changing a field
352
407
 
353
408
  `noIndexField`, the plugin's own fields, the Theme Global fields, the
354
- Roles Global fields, and the brand-assets collection fields are columns
409
+ Roles Global fields, the Admin nav fields, the Header and Footer fields,
410
+ and the brand-assets collection fields are columns
355
411
  in every consuming site. A change to them is a schema change: say "run
356
412
  `payload migrate:create`" in the changeset.
package/dist/admin.d.mts CHANGED
@@ -170,5 +170,45 @@ declare function DocumentCreateNew({
170
170
  path: string;
171
171
  }): react_jsx_runtime0.JSX.Element;
172
172
  //#endregion
173
- export { AppearanceField, ColorField, ColorScaleField, ContrastReport, DocumentCreateNew, DocumentTitleActions, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, ThemeDocumentControls, ThemeSaveButton };
173
+ //#region src/admin-nav/types.d.ts
174
+ interface AdminNavVisibleEntities {
175
+ collections?: readonly string[];
176
+ globals?: readonly string[];
177
+ }
178
+ //#endregion
179
+ //#region src/admin/admin-nav.d.ts
180
+ interface AdminNavProps {
181
+ visibleEntities?: AdminNavVisibleEntities;
182
+ }
183
+ /**
184
+ * Default admin sidebar. Reads the Admin nav Global and paints
185
+ * `resolveAdminNav`. SPI-10 may replace this component; it should keep
186
+ * calling that function so the document stays the one layout source.
187
+ */
188
+ declare function AdminNav({
189
+ visibleEntities
190
+ }: AdminNavProps): react_jsx_runtime0.JSX.Element;
191
+ //#endregion
192
+ //#region src/admin/admin-nav-row-label.d.ts
193
+ /**
194
+ * Array row header. Shows the group label, or “Group” when empty.
195
+ */
196
+ declare function AdminNavRowLabel(): react_jsx_runtime0.JSX.Element;
197
+ //#endregion
198
+ //#region src/admin/admin-nav-item-row-label.d.ts
199
+ /**
200
+ * Item accordion title. Shows the chosen collection or global name
201
+ * (Pages), or “Item” when the row is still empty.
202
+ */
203
+ declare function AdminNavItemRowLabel(): react_jsx_runtime0.JSX.Element;
204
+ //#endregion
205
+ //#region src/admin/admin-nav-entity-field.d.ts
206
+ /**
207
+ * Payload's select of collections or globals. Subscribes to sibling Type
208
+ * with `useField` so the list updates when Type changes — `getDataByPath`
209
+ * does not re-render this field.
210
+ */
211
+ declare const AdminNavEntityField: TextFieldClientComponent;
212
+ //#endregion
213
+ 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, ThemeDocumentControls, ThemeSaveButton };
174
214
  //# sourceMappingURL=admin.d.mts.map
@@ -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/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/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"],"mappings":";;;;;;;;;;;cAWa,UAAA,EAAY,wBAAA;;;;;;;cCsBZ,eAAA,EAAiB,yBAAA;;;;;;;;;cCNjB,YAAA,EAAc,yBAAA;;;;;;;;;cCTd,SAAA,EAAW,0BAAA;;;;;;;cCNX,YAAA,EAAc,sBAAA;;;;;;;cCCd,eAAA,EAAiB,yBAAA;;;ALF9B;;;;AAAA,cMyDa,cAAA,EAAgB,0BAAA;;;;;;;cChDhB,cAAA,EAAgB,sBAAA;;;;;;cCbhB,cAAA,EAAgB,sBAAA;;;cCgJhB,YAAA,EAAc,sBAAA;;;;;;;;iBC3IX,qBAAA,CAAA,GAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA;;AVDrC;;;iBUgBgB,eAAA,CAAA,GAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;;iBA0Bf,gBAAA,CAAA,GAAgB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;cC7CnB,gBAAA,EAAkB,sBAAA;;;;;;;cCIlB,SAAA,EAAW,wBAAA;;;;;;;;cCqBX,gBAAA,EAAkB,yBAAA;;;;;;iBC1Bf,aAAA,CAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;cCChB,aAAA,EAAe,wBAAA;;;;;;;cCCf,aAAA,EAAe,wBAAA;;;;;;;;cC8Cf,UAAA,EAAY,0BAAA;;;;;;;;cChCZ,gBAAA,EAAkB,wBAAA;;;;;;;;;AlBZ/B;;cmB0Ca,mBAAA,EAAqB,yBAAA;;;iBCjBlB,oBAAA,CAAA;EAAuB;AAAA;EAAc,QAAA,GAAW,SAAA;AAAA,IAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCtB3D,iBAAA,CAAA;EAAoB;AAAA;EAAU,IAAA;AAAA,IAAc,kBAAA,CAAA,GAAA,CAAA,OAAA"}
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/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/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;;;;;;;;;cCNjB,YAAA,EAAc,yBAAA;;;;;;;;;cCTd,SAAA,EAAW,0BAAA;;;;;;;cCNX,YAAA,EAAc,sBAAA;;;;;;;cCCd,eAAA,EAAiB,yBAAA;;;ALF9B;;;;AAAA,cMyDa,cAAA,EAAgB,0BAAA;;;;;;;cChDhB,cAAA,EAAgB,sBAAA;;;;;;cCbhB,cAAA,EAAgB,sBAAA;;;cCgJhB,YAAA,EAAc,sBAAA;;;;;;;;iBC3IX,qBAAA,CAAA,GAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA;;AVDrC;;;iBUgBgB,eAAA,CAAA,GAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;;iBA0Bf,gBAAA,CAAA,GAAgB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;cC7CnB,gBAAA,EAAkB,sBAAA;;;;;;;cCIlB,SAAA,EAAW,wBAAA;;;;;;;;cCqBX,gBAAA,EAAkB,yBAAA;;;;;;iBC1Bf,aAAA,CAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;cCChB,aAAA,EAAe,wBAAA;;;;;;;cCCf,aAAA,EAAe,wBAAA;;;;;;;;cC8Cf,UAAA,EAAY,0BAAA;;;;;;;;cChCZ,gBAAA,EAAkB,wBAAA;;;;;;;;;AlBZ/B;;cmB0Ca,mBAAA,EAAqB,yBAAA;;;iBCjBlB,oBAAA,CAAA;EAAuB;AAAA;EAAc,QAAA,GAAW,SAAA;AAAA,IAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCtB3D,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;;;;AvBGpB;;iBuBsCgB,QAAA,CAAA;EAAW;AAAA,GAAmB,aAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;iBC5C3C,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, fieldBaseClass, useAuth, useConfig, useField, useForm, useFormFields, useRowLabel } from "@payloadcms/ui";
3
+ import { ArrayField, Button, CheckboxInput, 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";
@@ -3395,6 +3395,280 @@ function DocumentCreateNew({ path }) {
3395
3395
  })] });
3396
3396
  }
3397
3397
  //#endregion
3398
- export { AppearanceField, ColorField, ColorScaleField, ContrastReport, DocumentCreateNew, DocumentTitleActions, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, ThemeDocumentControls, ThemeSaveButton };
3398
+ //#region src/admin-nav/types.ts
3399
+ /**
3400
+ * Developer-owned admin sidebar. SPI-10 skins this with AppSidebarBlock;
3401
+ * this package ships the document and the default Nav. A consuming site
3402
+ * waits for SPI-97 after this and BIS-110 publish.
3403
+ */
3404
+ const ADMIN_NAV_SLUG = "admin-nav";
3405
+ //#endregion
3406
+ //#region src/admin-nav/resolve.ts
3407
+ function sidebarGroup(group) {
3408
+ if (group === false) return false;
3409
+ if (typeof group === "string") return group;
3410
+ return "";
3411
+ }
3412
+ function asLabel(value, fallback) {
3413
+ return typeof value === "string" && value ? value : fallback;
3414
+ }
3415
+ function entityLabel(entity, type) {
3416
+ if (type === "collection") return asLabel(entity.labels?.plural, asLabel(entity.labels?.singular, entity.slug));
3417
+ return asLabel(entity.label, entity.slug);
3418
+ }
3419
+ function keyOf(type, slug) {
3420
+ return `${type}:${slug}`;
3421
+ }
3422
+ function catalog(config) {
3423
+ const collections = (config.collections ?? []).map((entity) => ({
3424
+ type: "collection",
3425
+ slug: entity.slug,
3426
+ label: entityLabel(entity, "collection"),
3427
+ group: sidebarGroup(entity.admin?.group)
3428
+ }));
3429
+ const globals = (config.globals ?? []).map((entity) => ({
3430
+ type: "global",
3431
+ slug: entity.slug,
3432
+ label: entityLabel(entity, "global"),
3433
+ group: sidebarGroup(entity.admin?.group)
3434
+ }));
3435
+ return [...collections, ...globals];
3436
+ }
3437
+ function defaultGroups(entities) {
3438
+ const groups = [];
3439
+ for (const entity of entities) {
3440
+ if (entity.group === false) continue;
3441
+ let group = groups.find((entry) => entry.label === entity.group);
3442
+ if (!group) {
3443
+ group = {
3444
+ label: entity.group,
3445
+ items: []
3446
+ };
3447
+ groups.push(group);
3448
+ }
3449
+ group.items.push({
3450
+ type: entity.type,
3451
+ slug: entity.slug,
3452
+ label: entity.label
3453
+ });
3454
+ }
3455
+ return groups;
3456
+ }
3457
+ function itemFromSaved(item, byKey) {
3458
+ const type = item.type === "global" || item.type === "collection" ? item.type : null;
3459
+ const slug = typeof item.slug === "string" ? item.slug : "";
3460
+ if (!type || !slug) return null;
3461
+ const entity = byKey.get(keyOf(type, slug));
3462
+ if (!entity || entity.group === false) return null;
3463
+ return {
3464
+ type,
3465
+ slug,
3466
+ label: entity.label
3467
+ };
3468
+ }
3469
+ function fromDocument(doc, entities) {
3470
+ const byKey = new Map(entities.map((entity) => [keyOf(entity.type, entity.slug), entity]));
3471
+ return (doc.groups ?? []).map((row) => ({
3472
+ label: typeof row.label === "string" ? row.label : "",
3473
+ items: (row.items ?? []).flatMap((item) => {
3474
+ const resolved = itemFromSaved(item ?? {}, byKey);
3475
+ return resolved ? [resolved] : [];
3476
+ })
3477
+ }));
3478
+ }
3479
+ /**
3480
+ * Layout for the package Nav (and later SPI-10). Permissions are
3481
+ * `visibleEntities` — this function does not re-derive access.
3482
+ */
3483
+ function resolveAdminNav({ config, doc, visibleEntities }) {
3484
+ const entities = catalog(config);
3485
+ const saved = Array.isArray(doc?.groups) ? doc.groups : [];
3486
+ let groups = saved.length === 0 ? defaultGroups(entities) : fromDocument({ groups: saved }, entities);
3487
+ if (visibleEntities) {
3488
+ const collections = new Set(visibleEntities.collections ?? []);
3489
+ const globals = new Set(visibleEntities.globals ?? []);
3490
+ groups = groups.map((group) => ({
3491
+ ...group,
3492
+ items: group.items.filter((item) => item.type === "collection" ? collections.has(item.slug) : globals.has(item.slug))
3493
+ }));
3494
+ }
3495
+ return groups;
3496
+ }
3497
+ //#endregion
3498
+ //#region src/admin/admin-nav.tsx
3499
+ const BASE = "nav";
3500
+ function isAbortError(error) {
3501
+ return error instanceof DOMException ? error.name === "AbortError" : error instanceof Error && error.name === "AbortError";
3502
+ }
3503
+ /**
3504
+ * Payload's DefaultNav roots in an `@internal` NavWrapper. Copy the
3505
+ * class contract here — this package does not depend on `@payloadcms/next`.
3506
+ * Without `nav--nav-open`, Payload's CSS keeps `.nav` at opacity 0.
3507
+ */
3508
+ function AdminNavShell({ children }) {
3509
+ const { hydrated, navOpen, navRef, shouldAnimate } = useNav();
3510
+ return /* @__PURE__ */ jsx("aside", {
3511
+ className: [
3512
+ BASE,
3513
+ navOpen && `${BASE}--nav-open`,
3514
+ shouldAnimate && `${BASE}--nav-animate`,
3515
+ hydrated && `${BASE}--nav-hydrated`
3516
+ ].filter(Boolean).join(" "),
3517
+ inert: navOpen ? void 0 : true,
3518
+ children: /* @__PURE__ */ jsx("div", {
3519
+ className: `${BASE}__scroll`,
3520
+ ref: navRef,
3521
+ children
3522
+ })
3523
+ });
3524
+ }
3525
+ /**
3526
+ * Default admin sidebar. Reads the Admin nav Global and paints
3527
+ * `resolveAdminNav`. SPI-10 may replace this component; it should keep
3528
+ * calling that function so the document stays the one layout source.
3529
+ */
3530
+ function AdminNav({ visibleEntities }) {
3531
+ const { config } = useConfig();
3532
+ const [doc, setDoc] = useState(null);
3533
+ const adminRoute = config.routes.admin ?? "/admin";
3534
+ const apiRoute = config.routes.api ?? "/api";
3535
+ useEffect(() => {
3536
+ const controller = new AbortController();
3537
+ const url = `${config.serverURL ?? ""}${apiRoute}/globals/${ADMIN_NAV_SLUG}`;
3538
+ fetch(url, {
3539
+ credentials: "include",
3540
+ signal: controller.signal
3541
+ }).then((response) => response.ok ? response.json() : null).then((payload) => {
3542
+ if (controller.signal.aborted) return;
3543
+ setDoc(payload);
3544
+ }).catch((error) => {
3545
+ if (isAbortError(error) || controller.signal.aborted) return;
3546
+ setDoc(null);
3547
+ });
3548
+ return () => controller.abort();
3549
+ }, [apiRoute, config.serverURL]);
3550
+ const groups = useMemo(() => resolveAdminNav({
3551
+ config,
3552
+ doc,
3553
+ visibleEntities
3554
+ }), [
3555
+ config,
3556
+ doc,
3557
+ visibleEntities
3558
+ ]);
3559
+ return /* @__PURE__ */ jsxs(AdminNavShell, { children: [/* @__PURE__ */ jsxs("nav", {
3560
+ className: `${BASE}__wrap`,
3561
+ "aria-label": "Admin",
3562
+ children: [/* @__PURE__ */ jsx("div", {
3563
+ className: `${BASE}__links`,
3564
+ children: groups.map((group, index) => {
3565
+ const items = group.items.map((item) => {
3566
+ const href = item.type === "collection" ? `${adminRoute}/collections/${item.slug}` : `${adminRoute}/globals/${item.slug}`;
3567
+ return /* @__PURE__ */ jsx(Link, {
3568
+ className: `${BASE}__link`,
3569
+ href,
3570
+ prefetch: false,
3571
+ children: /* @__PURE__ */ jsx("span", {
3572
+ className: `${BASE}__link-label`,
3573
+ children: item.label
3574
+ })
3575
+ }, `${item.type}:${item.slug}`);
3576
+ });
3577
+ if (!group.label) return /* @__PURE__ */ jsx("div", {
3578
+ className: `${BASE}__ungrouped`,
3579
+ children: items
3580
+ }, `ungrouped-${index}`);
3581
+ return /* @__PURE__ */ jsx(NavGroup, {
3582
+ label: group.label,
3583
+ children: items
3584
+ }, `${group.label}-${index}`);
3585
+ })
3586
+ }), /* @__PURE__ */ jsx("div", {
3587
+ className: `${BASE}__controls`,
3588
+ children: /* @__PURE__ */ jsx(Logout, {})
3589
+ })]
3590
+ }), /* @__PURE__ */ jsx("div", {
3591
+ className: `${BASE}__header`,
3592
+ children: /* @__PURE__ */ jsx("div", {
3593
+ className: `${BASE}__header-content`,
3594
+ children: /* @__PURE__ */ jsx(NavToggler, {})
3595
+ })
3596
+ })] });
3597
+ }
3598
+ //#endregion
3599
+ //#region src/admin/admin-nav-row-label.tsx
3600
+ /**
3601
+ * Array row header. Shows the group label, or “Group” when empty.
3602
+ */
3603
+ function AdminNavRowLabel() {
3604
+ const { data } = useRowLabel();
3605
+ return /* @__PURE__ */ jsx("span", { children: typeof data.label === "string" && data.label.trim() ? data.label.trim() : "Group" });
3606
+ }
3607
+ //#endregion
3608
+ //#region src/admin-nav/entity-options.ts
3609
+ function entityType(type) {
3610
+ return type === "global" ? "global" : "collection";
3611
+ }
3612
+ /**
3613
+ * Collections or globals a Developer may pin. Sibling Type decides which
3614
+ * list. `group: false` stays off — those entities are not on the sidebar.
3615
+ */
3616
+ function adminNavEntityOptions(config, type) {
3617
+ const kind = entityType(type);
3618
+ return (kind === "global" ? config.globals ?? [] : config.collections ?? []).filter((entity) => entity.admin?.group !== false).map((entity) => ({
3619
+ value: entity.slug,
3620
+ label: kind === "global" ? typeof entity.label === "string" && entity.label ? entity.label : entity.slug : typeof entity.labels?.plural === "string" && entity.labels.plural ? entity.labels.plural : entity.slug
3621
+ }));
3622
+ }
3623
+ /** Array row title. Empty slug falls back to “Item”, never “Item 01”. */
3624
+ function adminNavEntityLabel(config, type, slug) {
3625
+ if (typeof slug !== "string" || !slug) return "Item";
3626
+ return adminNavEntityOptions(config, type).find((option) => option.value === slug)?.label ?? slug;
3627
+ }
3628
+ //#endregion
3629
+ //#region src/admin/admin-nav-item-row-label.tsx
3630
+ /**
3631
+ * Item accordion title. Shows the chosen collection or global name
3632
+ * (Pages), or “Item” when the row is still empty.
3633
+ */
3634
+ function AdminNavItemRowLabel() {
3635
+ const { data } = useRowLabel();
3636
+ const { config } = useConfig();
3637
+ return /* @__PURE__ */ jsx("span", { children: adminNavEntityLabel(config, data.type, data.slug) });
3638
+ }
3639
+ //#endregion
3640
+ //#region src/admin/admin-nav-entity-field.tsx
3641
+ /**
3642
+ * Payload's select of collections or globals. Subscribes to sibling Type
3643
+ * with `useField` so the list updates when Type changes — `getDataByPath`
3644
+ * does not re-render this field.
3645
+ */
3646
+ const AdminNavEntityField = ({ field, path, permissions, readOnly, schemaPath }) => {
3647
+ const { value: type } = useField({ path: path.replace(/\.slug$/, ".type") });
3648
+ const { config } = useConfig();
3649
+ const options = adminNavEntityOptions(config, type);
3650
+ return /* @__PURE__ */ jsx(SelectField, {
3651
+ field: {
3652
+ name: field.name,
3653
+ label: field.label,
3654
+ required: field.required,
3655
+ admin: {
3656
+ ...field.admin,
3657
+ isClearable: false,
3658
+ isSortable: false,
3659
+ placeholder: "Select…"
3660
+ },
3661
+ type: "select",
3662
+ hasMany: false,
3663
+ options
3664
+ },
3665
+ path,
3666
+ permissions,
3667
+ readOnly,
3668
+ schemaPath
3669
+ });
3670
+ };
3671
+ //#endregion
3672
+ 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, ThemeDocumentControls, ThemeSaveButton };
3399
3673
 
3400
3674
  //# sourceMappingURL=admin.mjs.map