@bison-lab/payload-core 3.18.0 → 3.19.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 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,67 @@ 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`). `group: false` stays off the sidebar. `resolveAdminNav`
351
+ is the layout source so a later skin (SPI-10) does not invent a second
352
+ one.
353
+
354
+ ```ts
355
+ import { adminNav, createAdminNav } from "@bison-lab/payload-core";
356
+
357
+ export default buildConfig({
358
+ plugins: [adminNav()],
359
+ globals: [createAdminNav(), createRoles(), createFeatures()],
360
+ });
361
+ ```
362
+
363
+ Then `payload generate:importmap` and `payload migrate:create`.
364
+
365
+ ## Header and Footer
366
+
367
+ `createNavigation({ blocks })` returns two Globals so each has its own
368
+ Save / Publish / drafts. Header keeps slug `navigation` and table `nav`
369
+ so an existing one-document row does not move. Footer is
370
+ `navigation-footer` / `navf` — short because nested footer arrays hit
371
+ Postgres's 63-character identifier cap the same way `nav` did. Both sit
372
+ in `admin.group: "Navigation"` until a Developer moves them from Admin
373
+ nav. One Features tick (`navigation`, passed as a `createFeatures`
374
+ extra). Publish still needs the Publish tick. `getNavigation({ payload,
375
+ draft })` finds both and returns `{ header, footer }`.
376
+
377
+ ```ts
378
+ import { createNavigation } from "@bison-lab/payload-core";
379
+ import { getNavigation } from "@bison-lab/payload-core/navigation";
380
+
381
+ globals: [
382
+ ...createNavigation({
383
+ blocks: [megaMenuBlock({ variants }), LinkBlock],
384
+ preview: () => "/preview",
385
+ }),
386
+ ];
387
+
388
+ // In a site header — the /navigation entry keeps the SEO plugin off the layout.
389
+ const { header, footer } = await getNavigation({ payload, draft });
390
+ ```
391
+
392
+ A site that already stored footer columns on the `nav` document copies
393
+ `footer.columns` onto the new global, then drops the footer fields from
394
+ `nav`. Bar behaviour (hide on scroll, viewport, default panel width) is
395
+ not this factory.
396
+
343
397
  ## No generated types
344
398
 
345
399
  The package cannot import a site's `payload-types`, so the shapes it reads
346
- (`SeoMeta`, `SeoImageDoc`, `SeoPage`, `ThemeDoc`, `RolesDoc`) are hand-written and
400
+ (`SeoMeta`, `SeoImageDoc`, `SeoPage`, `ThemeDoc`, `RolesDoc`, `AdminNavDoc`, `NavigationDoc`) are hand-written and
347
401
  structural. A generated `Page`, `Media` or Theme Global is assignable to
348
402
  them; nothing carries an index signature, since an interface will not assign
349
403
  to a type that has one.
@@ -351,6 +405,7 @@ to a type that has one.
351
405
  ## Changing a field
352
406
 
353
407
  `noIndexField`, the plugin's own fields, the Theme Global fields, the
354
- Roles Global fields, and the brand-assets collection fields are columns
408
+ Roles Global fields, the Admin nav fields, the Header and Footer fields,
409
+ and the brand-assets collection fields are columns
355
410
  in every consuming site. A change to them is a schema change: say "run
356
411
  `payload migrate:create`" in the changeset.
package/dist/admin.d.mts CHANGED
@@ -170,5 +170,42 @@ 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-entity-field.d.ts
199
+ /**
200
+ * A select of collections or globals from this site's config. Type on the
201
+ * sibling row decides which list. `group: false` stays off — those entities
202
+ * are not on the sidebar.
203
+ */
204
+ declare function AdminNavEntityField({
205
+ path
206
+ }: {
207
+ path: string;
208
+ }): react_jsx_runtime0.JSX.Element;
209
+ //#endregion
210
+ export { AdminNav, AdminNavEntityField, 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
211
  //# 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-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;;;;;;;;iBCEhB,mBAAA,CAAA;EAAsB;AAAA;EAAU,IAAA;AAAA,IAAc,kBAAA,CAAA,GAAA,CAAA,OAAA"}
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, 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,263 @@ 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
+ const groups = (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
+ const seen = new Set(groups.flatMap((group) => group.items.map((item) => keyOf(item.type, item.slug))));
3479
+ for (const entity of entities) {
3480
+ if (entity.group === false) continue;
3481
+ const key = keyOf(entity.type, entity.slug);
3482
+ if (seen.has(key)) continue;
3483
+ let group = groups.find((entry) => entry.label === entity.group);
3484
+ if (!group) {
3485
+ group = {
3486
+ label: entity.group,
3487
+ items: []
3488
+ };
3489
+ groups.push(group);
3490
+ }
3491
+ group.items.push({
3492
+ type: entity.type,
3493
+ slug: entity.slug,
3494
+ label: entity.label
3495
+ });
3496
+ seen.add(key);
3497
+ }
3498
+ return groups;
3499
+ }
3500
+ /**
3501
+ * Layout for the package Nav (and later SPI-10). Permissions are
3502
+ * `visibleEntities` — this function does not re-derive access.
3503
+ */
3504
+ function resolveAdminNav({ config, doc, visibleEntities }) {
3505
+ const entities = catalog(config);
3506
+ const saved = Array.isArray(doc?.groups) ? doc.groups : [];
3507
+ let groups = saved.length === 0 ? defaultGroups(entities) : fromDocument({ groups: saved }, entities);
3508
+ if (visibleEntities) {
3509
+ const collections = new Set(visibleEntities.collections ?? []);
3510
+ const globals = new Set(visibleEntities.globals ?? []);
3511
+ groups = groups.map((group) => ({
3512
+ ...group,
3513
+ items: group.items.filter((item) => item.type === "collection" ? collections.has(item.slug) : globals.has(item.slug))
3514
+ }));
3515
+ }
3516
+ return groups;
3517
+ }
3518
+ //#endregion
3519
+ //#region src/admin/admin-nav.tsx
3520
+ const BASE = "nav";
3521
+ function isAbortError(error) {
3522
+ return error instanceof DOMException ? error.name === "AbortError" : error instanceof Error && error.name === "AbortError";
3523
+ }
3524
+ /**
3525
+ * Payload's DefaultNav roots in an `@internal` NavWrapper. Copy the
3526
+ * class contract here — this package does not depend on `@payloadcms/next`.
3527
+ * Without `nav--nav-open`, Payload's CSS keeps `.nav` at opacity 0.
3528
+ */
3529
+ function AdminNavShell({ children }) {
3530
+ const { hydrated, navOpen, navRef, shouldAnimate } = useNav();
3531
+ return /* @__PURE__ */ jsx("aside", {
3532
+ className: [
3533
+ BASE,
3534
+ navOpen && `${BASE}--nav-open`,
3535
+ shouldAnimate && `${BASE}--nav-animate`,
3536
+ hydrated && `${BASE}--nav-hydrated`
3537
+ ].filter(Boolean).join(" "),
3538
+ inert: navOpen ? void 0 : true,
3539
+ children: /* @__PURE__ */ jsx("div", {
3540
+ className: `${BASE}__scroll`,
3541
+ ref: navRef,
3542
+ children
3543
+ })
3544
+ });
3545
+ }
3546
+ /**
3547
+ * Default admin sidebar. Reads the Admin nav Global and paints
3548
+ * `resolveAdminNav`. SPI-10 may replace this component; it should keep
3549
+ * calling that function so the document stays the one layout source.
3550
+ */
3551
+ function AdminNav({ visibleEntities }) {
3552
+ const { config } = useConfig();
3553
+ const [doc, setDoc] = useState(null);
3554
+ const adminRoute = config.routes.admin ?? "/admin";
3555
+ const apiRoute = config.routes.api ?? "/api";
3556
+ useEffect(() => {
3557
+ const controller = new AbortController();
3558
+ const url = `${config.serverURL ?? ""}${apiRoute}/globals/${ADMIN_NAV_SLUG}`;
3559
+ fetch(url, {
3560
+ credentials: "include",
3561
+ signal: controller.signal
3562
+ }).then((response) => response.ok ? response.json() : null).then((payload) => {
3563
+ if (controller.signal.aborted) return;
3564
+ setDoc(payload);
3565
+ }).catch((error) => {
3566
+ if (isAbortError(error) || controller.signal.aborted) return;
3567
+ setDoc(null);
3568
+ });
3569
+ return () => controller.abort();
3570
+ }, [apiRoute, config.serverURL]);
3571
+ const groups = useMemo(() => resolveAdminNav({
3572
+ config,
3573
+ doc,
3574
+ visibleEntities
3575
+ }), [
3576
+ config,
3577
+ doc,
3578
+ visibleEntities
3579
+ ]);
3580
+ return /* @__PURE__ */ jsxs(AdminNavShell, { children: [/* @__PURE__ */ jsxs("nav", {
3581
+ className: `${BASE}__wrap`,
3582
+ "aria-label": "Admin",
3583
+ children: [/* @__PURE__ */ jsx("div", {
3584
+ className: `${BASE}__links`,
3585
+ children: groups.map((group, index) => {
3586
+ const items = group.items.map((item) => {
3587
+ return /* @__PURE__ */ jsx(Link, {
3588
+ href: item.type === "collection" ? `${adminRoute}/collections/${item.slug}` : `${adminRoute}/globals/${item.slug}`,
3589
+ className: `${BASE}__link`,
3590
+ children: item.label
3591
+ }, `${item.type}:${item.slug}`);
3592
+ });
3593
+ if (!group.label) return /* @__PURE__ */ jsx("div", {
3594
+ className: `${BASE}__ungrouped`,
3595
+ children: items
3596
+ }, `ungrouped-${index}`);
3597
+ return /* @__PURE__ */ jsx(NavGroup, {
3598
+ label: group.label,
3599
+ children: items
3600
+ }, `${group.label}-${index}`);
3601
+ })
3602
+ }), /* @__PURE__ */ jsx("div", {
3603
+ className: `${BASE}__controls`,
3604
+ children: /* @__PURE__ */ jsx(Logout, {})
3605
+ })]
3606
+ }), /* @__PURE__ */ jsx("div", {
3607
+ className: `${BASE}__header`,
3608
+ children: /* @__PURE__ */ jsx("div", {
3609
+ className: `${BASE}__header-content`,
3610
+ children: /* @__PURE__ */ jsx(NavToggler, {})
3611
+ })
3612
+ })] });
3613
+ }
3614
+ //#endregion
3615
+ //#region src/admin/admin-nav-row-label.tsx
3616
+ /**
3617
+ * Array row header. Shows the group label, or “Group” when empty.
3618
+ */
3619
+ function AdminNavRowLabel() {
3620
+ const { data } = useRowLabel();
3621
+ return /* @__PURE__ */ jsx("span", { children: typeof data.label === "string" && data.label.trim() ? data.label.trim() : "Group" });
3622
+ }
3623
+ //#endregion
3624
+ //#region src/admin/admin-nav-entity-field.tsx
3625
+ /**
3626
+ * A select of collections or globals from this site's config. Type on the
3627
+ * sibling row decides which list. `group: false` stays off — those entities
3628
+ * are not on the sidebar.
3629
+ */
3630
+ function AdminNavEntityField({ path }) {
3631
+ const { value, setValue } = useField({ path });
3632
+ const { getDataByPath } = useForm();
3633
+ const type = getDataByPath(path.replace(/\.slug$/, ".type"));
3634
+ const { config } = useConfig();
3635
+ const options = type === "global" ? (config.globals ?? []).filter((entity) => entity.admin?.group !== false).map((entity) => ({
3636
+ value: entity.slug,
3637
+ label: typeof entity.label === "string" ? entity.label : entity.slug
3638
+ })) : (config.collections ?? []).filter((entity) => entity.admin?.group !== false).map((entity) => ({
3639
+ value: entity.slug,
3640
+ label: typeof entity.labels?.plural === "string" ? entity.labels.plural : entity.slug
3641
+ }));
3642
+ return /* @__PURE__ */ jsxs("label", { children: ["Slug", /* @__PURE__ */ jsxs("select", {
3643
+ value: typeof value === "string" ? value : "",
3644
+ onChange: (event) => setValue(event.target.value),
3645
+ children: [/* @__PURE__ */ jsx("option", {
3646
+ value: "",
3647
+ children: "Select…"
3648
+ }), options.map((option) => /* @__PURE__ */ jsx("option", {
3649
+ value: option.value,
3650
+ children: option.label
3651
+ }, option.value))]
3652
+ })] });
3653
+ }
3654
+ //#endregion
3655
+ export { AdminNav, AdminNavEntityField, 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
3656
 
3400
3657
  //# sourceMappingURL=admin.mjs.map