init commit for iPAL-kit plugin

This commit is contained in:
2026-07-18 20:30:23 +02:00
parent 10638127a9
commit 5733a9c8bf
119 changed files with 6892 additions and 411 deletions
+82
View File
@@ -0,0 +1,82 @@
import type { Field, FieldHook, TextField } from 'payload'
import slugify from 'slugify'
/**
* Normalizes a string into a URL slug. slugify handles diacritics out of the
* box (Polish included: "Strona główna" → "strona-glowna").
*/
export function toSlug(input: string): string {
return slugify(input, {
lower: true,
strict: true, // drop characters that aren't url-safe
trim: true,
})
}
type BuildSlugFieldOptions = {
/** Source field to derive the slug from. Default: 'title'. */
from?: string
/** Whether the slug is localized (per-locale). Default: true. */
localized?: boolean
/** Field name for the slug. Default: 'slug'. */
name?: string
/** Override or extend the generated field config. */
overrides?: Partial<TextField>
/** Whether the slug is required. Default: true. */
required?: boolean
}
/**
* Builds a slug field that auto-generates from a source field (default 'title')
* only when left empty — an editor's manual slug is never overwritten (mode 4a).
*
* Localized-safe: on a localized field Payload runs the hook per locale, so
* `value` is the slug for the active locale and `siblingData[from]` is the
* source in that same locale. Editing the doc in 'pl' fills slug.pl from
* title.pl; editing in 'en' fills slug.en from title.en — giving genuinely
* per-locale slugs (strona-glowna / homepage) that the language switcher needs.
*/
export function buildSlugField(options: BuildSlugFieldOptions = {}): Field {
const {
name = 'slug',
from = 'title',
localized = true,
overrides = {},
required = true,
} = options
const autoGenerate: FieldHook = ({ originalDoc, siblingData, value }) => {
// Respect a manually entered slug — only generate when empty (mode 4a).
if (typeof value === 'string' && value.trim()) {
return toSlug(value) // still normalize what the editor typed
}
// Derive from the source field in the current locale.
const source =
(siblingData as Record<string, unknown>)?.[from] ??
(originalDoc as Record<string, unknown>)?.[from]
if (typeof source === 'string' && source.trim()) {
return toSlug(source)
}
return value
}
return {
name,
type: 'text',
admin: {
description: 'Auto-generated from the title when left empty. You can override it.',
...overrides.admin,
},
hooks: {
beforeValidate: [autoGenerate],
},
index: true,
localized,
required,
...overrides,
} as TextField
}
+1
View File
@@ -0,0 +1 @@
export { buildSlugField, toSlug } from './buildSlugField.js'