@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 +67 -11
- package/dist/admin.d.mts +41 -1
- package/dist/admin.d.mts.map +1 -1
- package/dist/admin.mjs +276 -2
- package/dist/admin.mjs.map +1 -1
- package/dist/index.d.mts +106 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +429 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{looks-Dk0l0mif.d.mts → looks-CQ_DiSQO.d.mts} +1 -1
- package/dist/{looks-Dk0l0mif.d.mts.map → looks-CQ_DiSQO.d.mts.map} +1 -1
- package/dist/metadata.d.mts +1 -1
- package/dist/navigation.d.mts +2 -0
- package/dist/navigation.mjs +2 -0
- package/dist/published-ByS86dhW.mjs +43 -0
- package/dist/published-ByS86dhW.mjs.map +1 -0
- package/dist/published-C49gmzAL.d.mts +68 -0
- package/dist/published-C49gmzAL.d.mts.map +1 -0
- package/dist/theme.d.mts +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 +5 -1
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
|
-
##
|
|
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/
|
|
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
|
|
294
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
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/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
|
-
|
|
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
|