@justanarthur/payload-www 0.1.2 → 0.3.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.
@@ -76,6 +76,14 @@ type CreatePagesCollectionOptions = {
76
76
  * when omitted.
77
77
  */
78
78
  defaultLocale?: CreateRevalidateCollectionHookOptions["defaultLocale"];
79
+ /**
80
+ * Enable nested (hierarchical) slugs. When `true`, the slug field
81
+ * additionally accepts the `_` nesting divider (so `about_us`
82
+ * renders at `/about/us`) and the admin description documents the
83
+ * convention. Pair with `createCollectionPageExports({ nested: true })`
84
+ * on the matching route. Default: `false` (flat, hyphen-only slugs).
85
+ */
86
+ nested?: boolean;
79
87
  };
80
88
  declare const createPagesCollection: (blocks: Block[], options?: Omit<CreatePagesCollectionOptions, "blocks">) => CollectionConfig;
81
89
  import { CollectionConfig as CollectionConfig2 } from "payload";
@@ -134,7 +142,7 @@ type CreateStaticPagesCollectionOptions = {
134
142
  blocks: Block2[];
135
143
  };
136
144
  declare const createStaticPagesCollection: (blocks: Block2[]) => CollectionConfig3;
137
- import { GlobalConfig } from "payload";
145
+ import { Field, GlobalConfig } from "payload";
138
146
  type CreateHeaderGlobalOptions = {
139
147
  /**
140
148
  * Optional override for the `custom.path` Payload uses to resolve
@@ -148,6 +156,17 @@ type CreateHeaderGlobalOptions = {
148
156
  * different list when your host has additional linkable collections.
149
157
  */
150
158
  linkRelationTo?: string[];
159
+ /**
160
+ * Extra fields appended to each `navColumn` link — e.g. a
161
+ * `description` text field or a `navHover` group for a mega-menu.
162
+ * Lets hosts enrich the column-link schema without redefining the
163
+ * whole nav.
164
+ */
165
+ navColumnLinkFields?: Field[];
166
+ /**
167
+ * Extra fields appended to each top-level `navItem` link.
168
+ */
169
+ navItemLinkFields?: Field[];
151
170
  };
152
171
  /**
153
172
  * Build the Header global. The lib's default shape: a `nav` blocks
@@ -155,7 +174,7 @@ type CreateHeaderGlobalOptions = {
155
174
  * and `navItem` (a single link).
156
175
  */
157
176
  declare const createHeaderGlobal: (options?: CreateHeaderGlobalOptions) => GlobalConfig;
158
- import { GlobalConfig as GlobalConfig2 } from "payload";
177
+ import { Field as Field2, GlobalConfig as GlobalConfig2 } from "payload";
159
178
  type CreateFooterGlobalOptions = {
160
179
  /**
161
180
  * Optional override for the `custom.path` Payload uses to resolve
@@ -169,6 +188,16 @@ type CreateFooterGlobalOptions = {
169
188
  * different list when your host has additional linkable collections.
170
189
  */
171
190
  linkRelationTo?: string[];
191
+ /**
192
+ * Extra fields appended to each `navColumn` link — e.g. a
193
+ * `description` text field. Lets hosts enrich the column-link
194
+ * schema without redefining the whole nav.
195
+ */
196
+ navColumnLinkFields?: Field2[];
197
+ /**
198
+ * Extra fields appended to each top-level `navItem` link.
199
+ */
200
+ navItemLinkFields?: Field2[];
172
201
  };
173
202
  /**
174
203
  * Build the Footer global. The lib's default shape mirrors the
@@ -23,7 +23,6 @@ var authenticatedOrPublished = ({ req: { user } }) => {
23
23
  return true;
24
24
  return { _status: { equals: "published" } };
25
25
  };
26
-
27
26
  // src/render/_locale.ts
28
27
  function prefixFor(locale, defaultLocale, mode) {
29
28
  if (mode === "never")
@@ -195,24 +194,9 @@ var createPagesCollection = (blocks, options = {}) => {
195
194
  renderPath = PAGES_RENDER_PATH,
196
195
  slug: collectionSlug = PAGES_SLUG2,
197
196
  localePrefix,
198
- defaultLocale
197
+ defaultLocale,
198
+ nested = false
199
199
  } = options;
200
- const slugField = {
201
- name: "slug",
202
- type: "text",
203
- required: true,
204
- unique: true,
205
- index: true,
206
- admin: { position: "sidebar" },
207
- validate: (value) => {
208
- if (typeof value !== "string")
209
- return "Slug must be a string";
210
- if (value !== "" && !/^[a-z0-9-]+$/.test(value)) {
211
- return "Slug must be lowercase, with hyphens (no spaces or special characters)";
212
- }
213
- return true;
214
- }
215
- };
216
200
  const baseFields = [
217
201
  {
218
202
  name: "title",
@@ -242,7 +226,7 @@ var createPagesCollection = (blocks, options = {}) => {
242
226
  type: "date",
243
227
  admin: { position: "sidebar" }
244
228
  },
245
- slugField
229
+ slugField({ nested })
246
230
  ];
247
231
  const { afterChange: revalidateAfterChange, afterDelete: revalidateAfterDelete } = createRevalidateCollectionHook({
248
232
  collectionSlug,
@@ -277,14 +261,6 @@ var createPostsCollection = (options = {}) => {
277
261
  localePrefix,
278
262
  defaultLocale
279
263
  } = options;
280
- const slugField = {
281
- name: "slug",
282
- type: "text",
283
- required: true,
284
- unique: true,
285
- index: true,
286
- admin: { position: "sidebar" }
287
- };
288
264
  const baseFields = [
289
265
  {
290
266
  name: "title",
@@ -309,7 +285,7 @@ var createPostsCollection = (options = {}) => {
309
285
  type: "date",
310
286
  admin: { position: "sidebar" }
311
287
  },
312
- slugField
288
+ slugField()
313
289
  ];
314
290
  const { afterChange, afterDelete } = createRevalidateCollectionHook({
315
291
  collectionSlug,
@@ -410,17 +386,18 @@ var createStaticPagesCollection = (blocks) => {
410
386
  };
411
387
 
412
388
  // src/core/fields/link.ts
413
- var appearanceOptions = {
389
+ var appearanceOptions2 = {
414
390
  default: { label: "Default", value: "default" },
415
391
  outline: { label: "Outline", value: "outline" }
416
392
  };
417
- var link = (options = {}) => {
393
+ var link2 = (options = {}) => {
418
394
  const {
419
395
  appearances,
420
396
  disableLabel = false,
421
397
  relationTo = [PAGES_SLUG],
422
398
  localized = false,
423
- overrides = {}
399
+ overrides = {},
400
+ extraFields = []
424
401
  } = options;
425
402
  const result = {
426
403
  name: "link",
@@ -488,7 +465,7 @@ var link = (options = {}) => {
488
465
  result.fields = [...result.fields, ...linkTypes];
489
466
  }
490
467
  if (appearances !== false) {
491
- const opts = appearances ? appearances.map((a) => appearanceOptions[a]) : [appearanceOptions.default, appearanceOptions.outline];
468
+ const opts = appearances ? appearances.map((a) => appearanceOptions2[a]) : [appearanceOptions2.default, appearanceOptions2.outline];
492
469
  result.fields.push({
493
470
  name: "appearance",
494
471
  type: "select",
@@ -497,6 +474,9 @@ var link = (options = {}) => {
497
474
  options: opts
498
475
  });
499
476
  }
477
+ if (extraFields.length) {
478
+ result.fields.push(...extraFields);
479
+ }
500
480
  return { ...result, ...overrides };
501
481
  };
502
482
 
@@ -516,17 +496,22 @@ function createRevalidateGlobalHook(slug) {
516
496
 
517
497
  // src/data/collections/globals/Header/config.ts
518
498
  var createHeaderGlobal = (options = {}) => {
519
- const { renderPath = HEADER_RENDER_PATH, linkRelationTo = [PAGES_SLUG] } = options;
499
+ const {
500
+ renderPath = HEADER_RENDER_PATH,
501
+ linkRelationTo = [PAGES_SLUG],
502
+ navColumnLinkFields = [],
503
+ navItemLinkFields = []
504
+ } = options;
520
505
  const navColumnBlock = {
521
506
  slug: "navColumn",
522
507
  fields: [
523
508
  { name: "title", type: "text", required: true, localized: true },
524
- { name: "links", type: "array", fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })] }
509
+ { name: "links", type: "array", fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navColumnLinkFields })] }
525
510
  ]
526
511
  };
527
512
  const navItemBlock = {
528
513
  slug: "navItem",
529
- fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })]
514
+ fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navItemLinkFields })]
530
515
  };
531
516
  return {
532
517
  slug: "header",
@@ -548,17 +533,22 @@ var createHeaderGlobal = (options = {}) => {
548
533
 
549
534
  // src/data/collections/globals/Footer/config.ts
550
535
  var createFooterGlobal = (options = {}) => {
551
- const { renderPath = FOOTER_RENDER_PATH, linkRelationTo = [PAGES_SLUG] } = options;
536
+ const {
537
+ renderPath = FOOTER_RENDER_PATH,
538
+ linkRelationTo = [PAGES_SLUG],
539
+ navColumnLinkFields = [],
540
+ navItemLinkFields = []
541
+ } = options;
552
542
  const navColumnBlock = {
553
543
  slug: "navColumn",
554
544
  fields: [
555
545
  { name: "title", type: "text", required: true, localized: true },
556
- { name: "links", type: "array", fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })] }
546
+ { name: "links", type: "array", fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navColumnLinkFields })] }
557
547
  ]
558
548
  };
559
549
  const navItemBlock = {
560
550
  slug: "navItem",
561
- fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })]
551
+ fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navItemLinkFields })]
562
552
  };
563
553
  return {
564
554
  slug: "footer",
package/dist/data-test.js CHANGED
@@ -29,7 +29,6 @@ var authenticatedOrPublished = ({ req: { user } }) => {
29
29
  return true;
30
30
  return { _status: { equals: "published" } };
31
31
  };
32
-
33
32
  // src/render/_locale.ts
34
33
  function prefixFor(locale, defaultLocale, mode) {
35
34
  if (mode === "never")
@@ -201,24 +200,9 @@ var createPagesCollection = (blocks, options = {}) => {
201
200
  renderPath = PAGES_RENDER_PATH,
202
201
  slug: collectionSlug = PAGES_SLUG2,
203
202
  localePrefix,
204
- defaultLocale
203
+ defaultLocale,
204
+ nested = false
205
205
  } = options;
206
- const slugField = {
207
- name: "slug",
208
- type: "text",
209
- required: true,
210
- unique: true,
211
- index: true,
212
- admin: { position: "sidebar" },
213
- validate: (value) => {
214
- if (typeof value !== "string")
215
- return "Slug must be a string";
216
- if (value !== "" && !/^[a-z0-9-]+$/.test(value)) {
217
- return "Slug must be lowercase, with hyphens (no spaces or special characters)";
218
- }
219
- return true;
220
- }
221
- };
222
206
  const baseFields = [
223
207
  {
224
208
  name: "title",
@@ -248,7 +232,7 @@ var createPagesCollection = (blocks, options = {}) => {
248
232
  type: "date",
249
233
  admin: { position: "sidebar" }
250
234
  },
251
- slugField
235
+ slugField({ nested })
252
236
  ];
253
237
  const { afterChange: revalidateAfterChange, afterDelete: revalidateAfterDelete } = createRevalidateCollectionHook({
254
238
  collectionSlug,
@@ -283,14 +267,6 @@ var createPostsCollection = (options = {}) => {
283
267
  localePrefix,
284
268
  defaultLocale
285
269
  } = options;
286
- const slugField = {
287
- name: "slug",
288
- type: "text",
289
- required: true,
290
- unique: true,
291
- index: true,
292
- admin: { position: "sidebar" }
293
- };
294
270
  const baseFields = [
295
271
  {
296
272
  name: "title",
@@ -315,7 +291,7 @@ var createPostsCollection = (options = {}) => {
315
291
  type: "date",
316
292
  admin: { position: "sidebar" }
317
293
  },
318
- slugField
294
+ slugField()
319
295
  ];
320
296
  const { afterChange, afterDelete } = createRevalidateCollectionHook({
321
297
  collectionSlug,
@@ -416,17 +392,18 @@ var createStaticPagesCollection = (blocks) => {
416
392
  };
417
393
 
418
394
  // src/core/fields/link.ts
419
- var appearanceOptions = {
395
+ var appearanceOptions2 = {
420
396
  default: { label: "Default", value: "default" },
421
397
  outline: { label: "Outline", value: "outline" }
422
398
  };
423
- var link = (options = {}) => {
399
+ var link2 = (options = {}) => {
424
400
  const {
425
401
  appearances,
426
402
  disableLabel = false,
427
403
  relationTo = [PAGES_SLUG],
428
404
  localized = false,
429
- overrides = {}
405
+ overrides = {},
406
+ extraFields = []
430
407
  } = options;
431
408
  const result = {
432
409
  name: "link",
@@ -494,7 +471,7 @@ var link = (options = {}) => {
494
471
  result.fields = [...result.fields, ...linkTypes];
495
472
  }
496
473
  if (appearances !== false) {
497
- const opts = appearances ? appearances.map((a) => appearanceOptions[a]) : [appearanceOptions.default, appearanceOptions.outline];
474
+ const opts = appearances ? appearances.map((a) => appearanceOptions2[a]) : [appearanceOptions2.default, appearanceOptions2.outline];
498
475
  result.fields.push({
499
476
  name: "appearance",
500
477
  type: "select",
@@ -503,6 +480,9 @@ var link = (options = {}) => {
503
480
  options: opts
504
481
  });
505
482
  }
483
+ if (extraFields.length) {
484
+ result.fields.push(...extraFields);
485
+ }
506
486
  return { ...result, ...overrides };
507
487
  };
508
488
 
@@ -522,17 +502,22 @@ function createRevalidateGlobalHook(slug) {
522
502
 
523
503
  // src/data/collections/globals/Header/config.ts
524
504
  var createHeaderGlobal = (options = {}) => {
525
- const { renderPath = HEADER_RENDER_PATH, linkRelationTo = [PAGES_SLUG] } = options;
505
+ const {
506
+ renderPath = HEADER_RENDER_PATH,
507
+ linkRelationTo = [PAGES_SLUG],
508
+ navColumnLinkFields = [],
509
+ navItemLinkFields = []
510
+ } = options;
526
511
  const navColumnBlock = {
527
512
  slug: "navColumn",
528
513
  fields: [
529
514
  { name: "title", type: "text", required: true, localized: true },
530
- { name: "links", type: "array", fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })] }
515
+ { name: "links", type: "array", fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navColumnLinkFields })] }
531
516
  ]
532
517
  };
533
518
  const navItemBlock = {
534
519
  slug: "navItem",
535
- fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })]
520
+ fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navItemLinkFields })]
536
521
  };
537
522
  return {
538
523
  slug: "header",
@@ -554,17 +539,22 @@ var createHeaderGlobal = (options = {}) => {
554
539
 
555
540
  // src/data/collections/globals/Footer/config.ts
556
541
  var createFooterGlobal = (options = {}) => {
557
- const { renderPath = FOOTER_RENDER_PATH, linkRelationTo = [PAGES_SLUG] } = options;
542
+ const {
543
+ renderPath = FOOTER_RENDER_PATH,
544
+ linkRelationTo = [PAGES_SLUG],
545
+ navColumnLinkFields = [],
546
+ navItemLinkFields = []
547
+ } = options;
558
548
  const navColumnBlock = {
559
549
  slug: "navColumn",
560
550
  fields: [
561
551
  { name: "title", type: "text", required: true, localized: true },
562
- { name: "links", type: "array", fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })] }
552
+ { name: "links", type: "array", fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navColumnLinkFields })] }
563
553
  ]
564
554
  };
565
555
  const navItemBlock = {
566
556
  slug: "navItem",
567
- fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo })]
557
+ fields: [link2({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navItemLinkFields })]
568
558
  };
569
559
  return {
570
560
  slug: "footer",
@@ -653,7 +643,7 @@ function getFromImportMap(key, importMap) {
653
643
  var queryDocBySlug = cache(async function queryDocBySlug2({
654
644
  collectionSlug,
655
645
  slug,
656
- slugField = "slug",
646
+ slugField: slugField2 = "slug",
657
647
  locale,
658
648
  draft = false,
659
649
  config
@@ -665,14 +655,14 @@ var queryDocBySlug = cache(async function queryDocBySlug2({
665
655
  limit: 1,
666
656
  pagination: false,
667
657
  overrideAccess: draft,
668
- where: { [slugField]: { equals: slug } },
658
+ where: { [slugField2]: { equals: slug } },
669
659
  locale
670
660
  });
671
661
  return result.docs?.[0] ?? null;
672
662
  });
673
663
  var queryAllDocs = cache(async function queryAllDocs2({
674
664
  collectionSlug,
675
- slugField = "slug",
665
+ slugField: slugField2 = "slug",
676
666
  locale,
677
667
  config
678
668
  }) {
@@ -683,7 +673,7 @@ var queryAllDocs = cache(async function queryAllDocs2({
683
673
  limit: 1000,
684
674
  pagination: false,
685
675
  overrideAccess: false,
686
- select: { [slugField]: true },
676
+ select: { [slugField2]: true },
687
677
  locale
688
678
  });
689
679
  return result.docs ?? [];
@@ -691,7 +681,7 @@ var queryAllDocs = cache(async function queryAllDocs2({
691
681
  var queryAllLocaleSlugs = cache(async function queryAllLocaleSlugs2({
692
682
  collectionSlug,
693
683
  slug,
694
- slugField = "slug",
684
+ slugField: slugField2 = "slug",
695
685
  locale,
696
686
  config
697
687
  }) {
@@ -703,13 +693,25 @@ var queryAllLocaleSlugs = cache(async function queryAllLocaleSlugs2({
703
693
  pagination: false,
704
694
  overrideAccess: false,
705
695
  locale,
706
- where: { [slugField]: { equals: slug } },
707
- select: { [slugField]: true }
696
+ where: { [slugField2]: { equals: slug } },
697
+ select: { [slugField2]: true }
708
698
  });
709
699
  const doc = result.docs?.[0];
710
700
  if (!doc)
711
701
  return;
712
- const fieldValue = doc?.[slugField];
702
+ let fieldValue = doc[slugField2];
703
+ if (doc.id != null) {
704
+ const allLocales2 = await payload.findByID({
705
+ collection: collectionSlug,
706
+ id: doc.id,
707
+ locale: "all",
708
+ draft: false,
709
+ overrideAccess: false,
710
+ select: { [slugField2]: true }
711
+ }).catch(() => null);
712
+ if (allLocales2)
713
+ fieldValue = allLocales2[slugField2];
714
+ }
713
715
  if (fieldValue && typeof fieldValue === "object") {
714
716
  return fieldValue;
715
717
  }
@@ -762,6 +764,7 @@ function createSitemapFile(options) {
762
764
  const collectionDefaults = options.perCollection?.[collectionSlug] ?? {};
763
765
  const urlPrefix = (options.urlPrefixes?.[collectionSlug] ?? "").replace(/\/$/, "");
764
766
  const siteUrl = options.getServerSideURL().replace(/\/$/, "");
767
+ const isNested = options.nested?.[collectionSlug] ?? false;
765
768
  for (const locale of activeLocales) {
766
769
  const docs = await queryAllDocs({
767
770
  collectionSlug,
@@ -777,13 +780,14 @@ function createSitemapFile(options) {
777
780
  if (seen.has(dedupeKey))
778
781
  continue;
779
782
  seen.add(dedupeKey);
780
- const url = `${siteUrl}${prefixFor(locale, defaultLocale, options.localePrefix ?? "always")}${urlPrefix}/${storedSlug}`;
783
+ const slugPath = isNested ? storedSlug.replaceAll("_", "/") : storedSlug;
784
+ const url = `${siteUrl}${prefixFor(locale, defaultLocale, options.localePrefix ?? "always")}${urlPrefix}/${slugPath}`;
781
785
  const languages = await buildHreflangAlternates({
782
786
  siteUrl,
783
787
  locale,
784
788
  urlPrefix,
785
789
  storedSlug,
786
- nested: false,
790
+ nested: isNested,
787
791
  homeSlug: "",
788
792
  defaultLocale,
789
793
  locales: activeLocales,
@@ -814,7 +818,7 @@ function createSitemapFile(options) {
814
818
  }
815
819
  // src/config/createWWWConfig.ts
816
820
  function createWWWConfig(options) {
817
- const { locales, routing, blocks, linkRelationTo, registerPosts = true, defaultPlugins } = options;
821
+ const { locales, routing, blocks, linkRelationTo, registerPosts = true, nested = false, defaultPlugins } = options;
818
822
  const defaultLocale = routing?.defaultLocale ?? locales[0] ?? "";
819
823
  const localePrefixMode = (() => {
820
824
  const raw = routing?.localePrefix;
@@ -827,7 +831,8 @@ function createWWWConfig(options) {
827
831
  }
828
832
  const buildPagesCollection = () => createPagesCollection(blocks, {
829
833
  localePrefix: localePrefixMode,
830
- defaultLocale
834
+ defaultLocale,
835
+ nested
831
836
  });
832
837
  const buildPostsCollection = () => createPostsCollection({
833
838
  localePrefix: localePrefixMode,
package/dist/fields.d.ts CHANGED
@@ -20,10 +20,46 @@ type LinkOptions = {
20
20
  overrides?: Partial<GroupField>;
21
21
  /** Legacy alias for the host's custom field overrides. */
22
22
  linkOverrides?: Partial<GroupField>;
23
+ /**
24
+ * Extra fields appended to the link group — e.g. a `description`
25
+ * text field or a `navHover` group for a mega-menu. Lets hosts
26
+ * extend the nav link schema without re-implementing the whole
27
+ * `link` field. Appended after the standard type/label/appearance
28
+ * fields.
29
+ */
30
+ extraFields?: Field[];
23
31
  };
24
32
  declare const link: (options?: LinkOptions) => Field;
25
33
  import { Field as Field2 } from "payload";
26
34
  declare const linkGroup: (options?: LinkOptions) => Field2;
35
+ import { Field as Field3 } from "payload";
36
+ type SlugFieldOptions = {
37
+ /** Field name. Default: `'slug'`. */
38
+ name?: string;
39
+ /**
40
+ * Allow the `_` nesting divider in the slug so `about_us` renders at
41
+ * `/about/us`. Relaxes validation and documents the convention in the
42
+ * admin UI. Pair with `createCollectionPageExports({ nested: true })`.
43
+ * Default: `false`.
44
+ */
45
+ nested?: boolean;
46
+ /**
47
+ * Localize the slug — one slug per locale (`/about` in `en`, `/o-nas`
48
+ * in `sk`), stored in the collection's `_locales` table. This is the
49
+ * default: the lib's collections are multilingual, so per-locale slugs
50
+ * power localized URLs and hreflang alternates. Pass `false` for a
51
+ * single shared slug.
52
+ */
53
+ localized?: boolean;
54
+ };
55
+ /**
56
+ * The slug field shared by the lib's Pages / Posts collections: a
57
+ * required, unique, indexed, sidebar text field, localized by default.
58
+ *
59
+ * The empty string is reserved for the home page (rendered at
60
+ * `/${locale}`), so validation only rejects non-empty malformed values.
61
+ */
62
+ declare const slugField: (options?: SlugFieldOptions) => Field3;
27
63
  import { Field as Field_14espm } from "payload";
28
64
  declare const _default: {
29
65
  link: (options?: LinkOptions) => Field_14espm;
@@ -32,5 +68,6 @@ declare const _default: {
32
68
  label: string;
33
69
  value: string;
34
70
  }>;
71
+ slugField: (options?: SlugFieldOptions) => Field_14espm;
35
72
  };
36
- export { linkGroup, link, _default as default, appearanceOptions, LinkOptions, LinkAppearances };
73
+ export { slugField, linkGroup, link, _default as default, appearanceOptions, SlugFieldOptions, LinkOptions, LinkAppearances };
package/dist/fields.js CHANGED
@@ -36,7 +36,8 @@ var link = (options = {}) => {
36
36
  disableLabel = false,
37
37
  relationTo = [PAGES_SLUG],
38
38
  localized = false,
39
- overrides = {}
39
+ overrides = {},
40
+ extraFields = []
40
41
  } = options;
41
42
  const result = {
42
43
  name: "link",
@@ -113,6 +114,9 @@ var link = (options = {}) => {
113
114
  options: opts
114
115
  });
115
116
  }
117
+ if (extraFields.length) {
118
+ result.fields.push(...extraFields);
119
+ }
116
120
  return { ...result, ...overrides };
117
121
  };
118
122
 
@@ -124,9 +128,38 @@ var linkGroup = (options = {}) => ({
124
128
  admin: { initCollapsed: true }
125
129
  });
126
130
 
131
+ // src/core/fields/slug.ts
132
+ var FLAT_PATTERN = /^[a-z0-9-]+$/;
133
+ var NESTED_PATTERN = /^[a-z0-9_-]+$/;
134
+ var slugField = (options = {}) => {
135
+ const { name = "slug", nested = false, localized = true } = options;
136
+ const pattern = nested ? NESTED_PATTERN : FLAT_PATTERN;
137
+ const invalidMessage = nested ? "Slug must be lowercase, with hyphens or the `_` nesting divider (no spaces or other special characters)" : "Slug must be lowercase, with hyphens (no spaces or special characters)";
138
+ return {
139
+ name,
140
+ type: "text",
141
+ required: true,
142
+ unique: true,
143
+ index: true,
144
+ localized,
145
+ admin: {
146
+ position: "sidebar",
147
+ ...nested ? { description: "Lowercase, hyphens for words. Use the `_` divider for nesting, e.g. `about_us` → `/about/us`." } : {}
148
+ },
149
+ validate: (value) => {
150
+ if (typeof value !== "string")
151
+ return "Slug must be a string";
152
+ if (value !== "" && !pattern.test(value))
153
+ return invalidMessage;
154
+ return true;
155
+ }
156
+ };
157
+ };
158
+
127
159
  // src/exports/fields.ts
128
- var fields_default = { link, linkGroup, appearanceOptions };
160
+ var fields_default = { link, linkGroup, appearanceOptions, slugField };
129
161
  export {
162
+ slugField,
130
163
  linkGroup,
131
164
  link,
132
165
  fields_default as default,
package/dist/globals.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GlobalConfig } from "payload";
1
+ import { Field, GlobalConfig } from "payload";
2
2
  type CreateHeaderGlobalOptions = {
3
3
  /**
4
4
  * Optional override for the `custom.path` Payload uses to resolve
@@ -12,6 +12,17 @@ type CreateHeaderGlobalOptions = {
12
12
  * different list when your host has additional linkable collections.
13
13
  */
14
14
  linkRelationTo?: string[];
15
+ /**
16
+ * Extra fields appended to each `navColumn` link — e.g. a
17
+ * `description` text field or a `navHover` group for a mega-menu.
18
+ * Lets hosts enrich the column-link schema without redefining the
19
+ * whole nav.
20
+ */
21
+ navColumnLinkFields?: Field[];
22
+ /**
23
+ * Extra fields appended to each top-level `navItem` link.
24
+ */
25
+ navItemLinkFields?: Field[];
15
26
  };
16
27
  /**
17
28
  * Build the Header global. The lib's default shape: a `nav` blocks
@@ -19,7 +30,7 @@ type CreateHeaderGlobalOptions = {
19
30
  * and `navItem` (a single link).
20
31
  */
21
32
  declare const createHeaderGlobal: (options?: CreateHeaderGlobalOptions) => GlobalConfig;
22
- import { GlobalConfig as GlobalConfig2 } from "payload";
33
+ import { Field as Field2, GlobalConfig as GlobalConfig2 } from "payload";
23
34
  type CreateFooterGlobalOptions = {
24
35
  /**
25
36
  * Optional override for the `custom.path` Payload uses to resolve
@@ -33,6 +44,16 @@ type CreateFooterGlobalOptions = {
33
44
  * different list when your host has additional linkable collections.
34
45
  */
35
46
  linkRelationTo?: string[];
47
+ /**
48
+ * Extra fields appended to each `navColumn` link — e.g. a
49
+ * `description` text field. Lets hosts enrich the column-link
50
+ * schema without redefining the whole nav.
51
+ */
52
+ navColumnLinkFields?: Field2[];
53
+ /**
54
+ * Extra fields appended to each top-level `navItem` link.
55
+ */
56
+ navItemLinkFields?: Field2[];
36
57
  };
37
58
  /**
38
59
  * Build the Footer global. The lib's default shape mirrors the