@bison-lab/payload-core 3.15.0 → 3.17.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
@@ -22,7 +22,7 @@ lockstep.
22
22
 
23
23
  | Import | Contents | Runs where |
24
24
  | ---------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
25
- | `@bison-lab/payload-core` | `seoPlugin`, `createTheme`, `createBrandAssets`, `createRoles`, `createFeatures`, `createPages`, `createUsers`, `createMedia`, `adminOnlyApiTab`, `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`, `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
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. |
@@ -173,10 +173,7 @@ export default buildConfig({
173
173
  mark: { url: "/logo-mark.svg", alt: "Acme mark" },
174
174
  },
175
175
  },
176
- colorUsages: {
177
- collections: ["pages"],
178
- globals: ["navigation"],
179
- },
176
+ colorUsages: true,
180
177
  onPublish: async () => {
181
178
  revalidateTag("theme");
182
179
  },
@@ -186,13 +183,15 @@ export default buildConfig({
186
183
  ```
187
184
 
188
185
  **Color usages.** `createTheme({ colorUsages })` is the only site wiring for
189
- "is this custom color assigned?" Name every collection and global that
190
- stores a `lookField` / `colorTokenField` (or a string token those fields
191
- wrote). The package scans those documents drafts included — and
192
- rewrites tokens when Colors Delete needs a replacement. Omit the option,
193
- or pass empty arrays, and every additional color is unused and deletes
194
- immediately. A new look surface is added to `colorUsages`, not copied
195
- into a site helper. Site adopt names the slugs (SPI-93).
186
+ "is this custom color assigned?" Pass `true` to scan every collection and
187
+ global Payload has registered when Colors Delete runs drafts included,
188
+ blocks included. Payload internals (`payload-*`) and Theme itself are
189
+ skipped, so the library row being deleted is not treated as a use, and a
190
+ later Navigation or Territories global joins without a site change. A
191
+ `{ collections, globals }` list still names slugs when a site wants a
192
+ narrower walk. Omit the option, or pass empty arrays, and every
193
+ additional color is unused and deletes immediately. The site does not
194
+ copy `findColorTokens`.
196
195
 
197
196
  Then `payload generate:importmap` (the Theme fields resolve from
198
197
  `@bison-lab/payload-core/admin`), `payload generate:types`, and
@@ -319,8 +318,9 @@ read from the live Features and Roles Globals) so Payload's
319
318
 
320
319
  ## Pages, users, and media
321
320
 
322
- `createPages`, `createUsers`, `createMedia`, `slugField`, and
323
- `adminOnlyApiTab` ship from the same React-free entry. Pages take the
321
+ `createPages`, `createUsers`, `createMedia`, `slugField`,
322
+ `adminOnlyApiTab`, and `documentTitleActions` ship from the same
323
+ React-free entry. Pages take the
324
324
  site's hero and layout blocks, reserved-slug predicate, and preview
325
325
  path. Create follows the Content tick. Update without Publish is
326
326
  constrained to `_status: draft` — an Author can save a draft and cannot
@@ -328,7 +328,12 @@ publish or edit a live page. Users sit under Settings next to Roles,
328
328
  take `secureCookies`, and offer whatever rows the Global (or the seed
329
329
  plus the same `extras`) has. `users.roles` and `users.allowedFeatures`
330
330
  set `saveToJWT: true`. The checklist labels are the editable names.
331
- `adminOnlyApiTab` shows the document API tab only to Developer. Overlay
331
+ `adminOnlyApiTab` shows the document API tab only to Developer.
332
+ `documentTitleActions` is one admin provider that puts every document's
333
+ primary actions on the title row — the same slot collection lists use
334
+ for Create New — and hides the dead Edit tab when it has no sibling.
335
+ One-list Globals pass `documentCreateNew("roles")` (or a site field
336
+ path) so Create New uses those same words, with no icon. Overlay
332
337
  settings (Better Editor) take `developerOnlyAccess` and
333
338
  `hideUnlessDeveloper` on the site — the package does not add that
334
339
  plugin as a peer.
package/dist/admin.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
 
2
2
  import { ThemeConfig } from "@bison-lab/tokens";
3
+ import { ReactNode } from "react";
3
4
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
5
  import { ArrayFieldClientComponent, GroupFieldClientComponent, JSONFieldClientComponent, SelectFieldClientComponent, TextFieldClientComponent, UIFieldClientComponent } from "payload";
5
6
 
@@ -155,5 +156,19 @@ declare const RolesGrantsField: JSONFieldClientComponent;
155
156
  */
156
157
  declare const FeaturesMatrixField: ArrayFieldClientComponent;
157
158
  //#endregion
158
- export { AppearanceField, ColorField, ColorScaleField, ContrastReport, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, ThemeDocumentControls, ThemeSaveButton };
159
+ //#region src/admin/document-title-actions.d.ts
160
+ declare function DocumentTitleActions({
161
+ children
162
+ }: {
163
+ children?: ReactNode;
164
+ }): react_jsx_runtime0.JSX.Element;
165
+ //#endregion
166
+ //#region src/admin/document-create-new.d.ts
167
+ declare function DocumentCreateNew({
168
+ path
169
+ }: {
170
+ path: string;
171
+ }): react_jsx_runtime0.JSX.Element;
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 };
159
174
  //# 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"],"mappings":";;;;;;;;;;cAWa,UAAA,EAAY,wBAAA;;;;;;;cCsBZ,eAAA,EAAiB,yBAAA;;;;;;;;ADtB9B;cEgBa,YAAA,EAAc,yBAAA;;;;;;;;AFhB3B;cGOa,SAAA,EAAW,0BAAA;;;;;;;cCNX,YAAA,EAAc,sBAAA;;;;;;;cCCd,eAAA,EAAiB,yBAAA;;;;;;;cCuDjB,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;;;;AAAA,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;;;;;;;;cCsBX,gBAAA,EAAkB,yBAAA;;;;;;iBC3Bf,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"}
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;;;;;;;;cCsBX,gBAAA,EAAkB,yBAAA;;;;;;iBC3Bf,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"}
package/dist/admin.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { a as colorDocFromState, i as SYSTEM_COLOR_KEYS, n as pageEditorLooks, r as pageEditorTokens, s as stateFromColorDoc } from "./looks-BbL309kO.mjs";
3
- import { ArrayField, CheckboxInput, FieldDescription, FieldError, FieldLabel, fieldBaseClass, useAuth, useConfig, useField, useForm, useFormFields, useRowLabel } from "@payloadcms/ui";
3
+ import { ArrayField, Button, CheckboxInput, FieldDescription, FieldError, FieldLabel, fieldBaseClass, useAuth, useConfig, useField, useForm, useFormFields, useRowLabel } from "@payloadcms/ui";
4
4
  import { GREY_SCALES, SHADE_STEPS, contrastForeground, contrastRatio, createColorScale, deriveDarkPalette, deriveLightPalette, hexToHSL, hslToString, includedShadeSteps, isThemeHex, presetHints, setColorScaleInclude } from "@bison-lab/tokens";
5
- import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
5
+ import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { createPortal } from "react-dom";
8
8
  import { findFont, fontFaceCss, isFontId } from "@bison-lab/fonts";
@@ -2842,7 +2842,7 @@ function resetRolesMatrix(current) {
2842
2842
  }
2843
2843
  //#endregion
2844
2844
  //#region src/admin/roles-matrix-field.tsx
2845
- const HIDE_ROW_CHROME = `.bl-roles-matrix .array-actions__add,.bl-roles-matrix .array-actions__duplicate,.bl-roles-matrix .array-actions__copy{display:none}.bl-roles-matrix .array-field__add-row{display:none}.bl-roles-matrix--hide-developer .array-field__row:has([data-role-seed="developer"]),.bl-roles-matrix--hide-developer .collapsible:has([data-role-seed="developer"]){display:none}.bl-roles-matrix__toolbar{display:flex;justify-content:flex-end;gap:.5rem;margin-block-end:.75rem}.bl-roles-matrix__reset,.bl-roles-matrix__add{min-height:2.25rem;padding:.35rem .9rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px;background:transparent;color:inherit;font:inherit;cursor:pointer}body.bl-roles-matrix-seed-menu .array-actions__remove{display:none}`;
2845
+ const HIDE_ROW_CHROME = `.bl-roles-matrix .array-actions__add,.bl-roles-matrix .array-actions__duplicate,.bl-roles-matrix .array-actions__copy{display:none}.bl-roles-matrix .array-field__add-row{display:none}.bl-roles-matrix--hide-developer .array-field__row:has([data-role-seed="developer"]),.bl-roles-matrix--hide-developer .collapsible:has([data-role-seed="developer"]){display:none}.bl-roles-matrix__toolbar{display:flex;justify-content:flex-end;gap:.5rem;margin-block-end:.75rem}.bl-roles-matrix__reset{min-height:2.25rem;padding:.35rem .9rem;border:1px solid var(--theme-elevation-150,#e2e8f0);border-radius:8px;background:transparent;color:inherit;font:inherit;cursor:pointer}body.bl-roles-matrix-seed-menu .array-actions__remove{display:none}`;
2846
2846
  function seedRoleFromTrigger(trigger) {
2847
2847
  let node = trigger.parentElement;
2848
2848
  while (node) {
@@ -2894,19 +2894,14 @@ const RolesMatrixField = (props) => {
2894
2894
  precedence: "default",
2895
2895
  children: HIDE_ROW_CHROME
2896
2896
  }),
2897
- /* @__PURE__ */ jsxs("div", {
2897
+ /* @__PURE__ */ jsx("div", {
2898
2898
  className: "bl-roles-matrix__toolbar",
2899
- children: [/* @__PURE__ */ jsx("button", {
2900
- type: "button",
2901
- className: "bl-roles-matrix__add",
2902
- onClick: () => rootRef.current?.querySelector(".array-field__add-row")?.click(),
2903
- children: "Add Role"
2904
- }), /* @__PURE__ */ jsx("button", {
2899
+ children: /* @__PURE__ */ jsx("button", {
2905
2900
  type: "button",
2906
2901
  className: "bl-roles-matrix__reset",
2907
2902
  onClick: () => setValue(resetRolesMatrix(value)),
2908
2903
  children: "Reset to defaults"
2909
- })]
2904
+ })
2910
2905
  }),
2911
2906
  /* @__PURE__ */ jsx(ArrayField, { ...props })
2912
2907
  ]
@@ -3321,6 +3316,85 @@ const FeaturesMatrixField = (props) => {
3321
3316
  });
3322
3317
  };
3323
3318
  //#endregion
3324
- export { AppearanceField, ColorField, ColorScaleField, ContrastReport, FeaturesMatrixField, FontField, GreyScaleField, HiddenSaveButton, IdentityFallback, LibraryField, LookField, PairingField, PublishChild, RoleNameField, RoleSlugField, RolesField, RolesGrantsField, RolesMatrixField, RolesRowLabel, SectionHeading, ThemeDocumentControls, ThemeSaveButton };
3319
+ //#region src/admin/document-title-actions.tsx
3320
+ /**
3321
+ * Shared document header: title on the left, primary actions on the
3322
+ * right — the same slot collection lists use for Create New.
3323
+ *
3324
+ * Payload parks Edit / Versions / API in that slot and puts Save one
3325
+ * row down. This moves `.doc-controls__controls-wrapper` (Save,
3326
+ * Preview, Create New, and the kebab) onto the title row, hides the
3327
+ * active Edit tab (you are already on the form), and collapses the
3328
+ * leftover controls band when it has no meta.
3329
+ *
3330
+ * Mounted as `admin.components.providers` so Account and every
3331
+ * collection / global share one lift. A MutationObserver re-applies
3332
+ * after Payload redraws the controls.
3333
+ */
3334
+ const TITLE_ACTIONS_CSS = `
3335
+ .doc-header__header{display:flex;align-items:center;justify-content:flex-start;width:100%;gap:10px}
3336
+ .doc-header__title{flex:0 1 auto;min-width:0}
3337
+ .doc-header__header > .doc-controls__controls-wrapper{margin-left:auto;display:flex;align-items:center;gap:8px}
3338
+ .doc-tab.doc-tab--active.btn--disabled{display:none}
3339
+ .doc-tabs:not(:has(.doc-tab:not(.doc-tab--active))){display:none}
3340
+ .doc-controls:has(.doc-controls__meta:empty):not(:has(.doc-controls__controls-wrapper)){height:0;min-height:0;padding:0;overflow:hidden}
3341
+ .doc-controls:has(.doc-controls__meta:empty):not(:has(.doc-controls__controls-wrapper)) .doc-controls__divider{display:none}
3342
+ `;
3343
+ function liftTitleActions() {
3344
+ const header = document.querySelector(".doc-header__header");
3345
+ const wrapper = document.querySelector(".doc-controls__controls-wrapper");
3346
+ if (!header || !wrapper) return;
3347
+ if (wrapper.parentElement === header) return;
3348
+ header.appendChild(wrapper);
3349
+ }
3350
+ function DocumentTitleActions({ children }) {
3351
+ useLayoutEffect(() => {
3352
+ liftTitleActions();
3353
+ const observer = new MutationObserver(liftTitleActions);
3354
+ observer.observe(document.body, {
3355
+ childList: true,
3356
+ subtree: true
3357
+ });
3358
+ return () => observer.disconnect();
3359
+ }, []);
3360
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", {
3361
+ href: "bl-doc-title-actions",
3362
+ precedence: "default",
3363
+ children: TITLE_ACTIONS_CSS
3364
+ }), children] });
3365
+ }
3366
+ //#endregion
3367
+ //#region src/admin/document-create-new.tsx
3368
+ /**
3369
+ * Same words as a collection list Create New — no icon, no "Area" or
3370
+ * "Role." The page title already says what is created. Primary so a
3371
+ * site's admin skin can paint it with Theme `--primary`.
3372
+ *
3373
+ * Hides the array's own Add while this control is mounted so the
3374
+ * action is not offered twice.
3375
+ */
3376
+ const HIDE_FIELD_ADD = `.array-field__add-row{display:none}`;
3377
+ function DocumentCreateNew({ path }) {
3378
+ const { addFieldRow, disabled } = useForm();
3379
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", {
3380
+ href: "bl-doc-create-new",
3381
+ precedence: "default",
3382
+ children: HIDE_FIELD_ADD
3383
+ }), /* @__PURE__ */ jsx(Button, {
3384
+ buttonStyle: "primary",
3385
+ size: "medium",
3386
+ type: "button",
3387
+ disabled,
3388
+ onClick: () => {
3389
+ addFieldRow({
3390
+ path,
3391
+ schemaPath: path
3392
+ });
3393
+ },
3394
+ children: "Create New"
3395
+ })] });
3396
+ }
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 };
3325
3399
 
3326
3400
  //# sourceMappingURL=admin.mjs.map