Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
37a76aa
fix(templates): auto gen slug through autosave
jacobsfletch Oct 1, 2025
6c095f1
restructure slug gen
jacobsfletch Oct 2, 2025
02937f3
Merge branch 'main' into fix/template-slug-gen
jacobsfletch Oct 2, 2025
1aab86f
wire conditional lp
jacobsfletch Oct 2, 2025
71f0709
way better
jacobsfletch Oct 2, 2025
11e4fca
replace slugify with payload util
jacobsfletch Oct 2, 2025
53439fd
more cleanup
jacobsfletch Oct 2, 2025
702cf70
hide gen field
jacobsfletch Oct 2, 2025
7f636a8
proof jsdocs
jacobsfletch Oct 2, 2025
33be964
move jsdocs and add clarity
jacobsfletch Oct 2, 2025
01903ca
fix for non autosave
jacobsfletch Oct 2, 2025
a363d2a
export slug field from ui and nest in single unnamed field
jacobsfletch Oct 3, 2025
fe54fb7
fix import
jacobsfletch Oct 3, 2025
40916e6
better overrides pattern
jacobsfletch Oct 3, 2025
f5be5c7
Merge branch 'main' into fix/template-slug-gen
jacobsfletch Oct 3, 2025
c81eff5
Merge branch 'main' into fix/template-slug-gen
jacobsfletch Oct 3, 2025
8a5dcd0
regen import maps
jacobsfletch Oct 3, 2025
59368d3
fix component paths
jacobsfletch Oct 3, 2025
900e108
simplify generate funcs
jacobsfletch Oct 3, 2025
62ac2ac
move field definitions to payload
jacobsfletch Oct 3, 2025
5c585d2
fix slugify util, rm unused code, type safety
jacobsfletch Oct 3, 2025
2b1473e
add experimental flag to slug field
jacobsfletch Oct 3, 2025
e4d8a1a
disable gen field from list view and fix seed
jacobsfletch Oct 4, 2025
c7b8b84
docs
jacobsfletch Oct 4, 2025
22b8c80
rm unused util
jacobsfletch Oct 4, 2025
8cce07d
docs cleanup
jacobsfletch Oct 6, 2025
6aee982
sidebar
jacobsfletch Oct 6, 2025
0fe318f
safely slugify
jacobsfletch Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/fields/collapsible.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ The Collapsible Field inherits all of the default admin options from the base [F

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/date.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ When only `pickerAppearance` is set, an equivalent format will be rendered in th

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/email.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ The Email Field inherits all of the default admin options from the base [Field A

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/group.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ The Group Field inherits all of the default admin options from the base [Field A

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/json.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ The JSON Field inherits all of the default admin options from the base [Field Ad

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/number.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ The Number Field inherits all of the default admin options from the base [Field

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/point.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ _\* An asterisk denotes that a property is required._

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/radio.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ The Radio Field inherits all of the default admin options from the base [Field A

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/row.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ _\* An asterisk denotes that a property is required._

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ The Select Field inherits all of the default admin options from the base [Field

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/tabs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ _\* An asterisk denotes that a property is required._

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
65 changes: 63 additions & 2 deletions docs/fields/text.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ The Text Field inherits all of the default admin options from the base [Field Ad

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down Expand Up @@ -183,3 +181,66 @@ export const CustomTextFieldLabelClient: TextFieldLabelClientComponent = ({
)
}
```

## Slug Field

<Banner type="warning">
The slug field is experimental and may change, or even be removed, in future
releases. Use at your own risk.
</Banner>

One common use case for the Text Field is to create a "slug" for a document. A slug is a unique, indexed, URL-friendly string that identifies a particular document, often used to construct the URL of a webpage.

Payload provides a built-in Slug Field so you don't have to built one from scratch. This field automatically generates a slug based on the value of another field, such as a title or name field. It provides UI to lock and unlock the field to protect its value, as well as to re-generate the slug on-demand.

To add a Slug Field, import the `slugField` into your field schema:

```ts
import { slugField } from 'payload'
import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
// ...
fields: [
// ...
// highlight-line
slugField(),
// highlight-line
],
}
```

The slug field exposes a few top-level config options for easy customization:

| Option | Description |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | To be used as the slug field's name. Defaults to `slug`. |
| `overrides` | A function that receives the default fields so you can override on a granular level. See example below. [More details](./slug-overrides). |
| `checkboxName` | To be used as the name for the `generateSlug` checkbox field. Defaults to `generateSlug`. |
| `fieldToUse` | The name of the field to use when generating the slug. This field must exist in the same collection. Defaults to `title`. |
| `position` | The position of the slug field. [More details](./overview#admin-options). |
| `required` | Require the slug field. Defaults to `true`. |

### Slug Overrides

If the above options aren't sufficient for your use case, you can use the `overrides` function to customize the slug field at a granular level. The `overrides` function receives the default fields that make up the slug field, and you can modify them to any extent you need.

```ts
import { slugField } from 'payload'
import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
// ...
fields: [
// ...
// highlight-line
slugField({
overrides: (defaultField) => {
defaultField.fields[1].label = 'Custom Slug Label'
return defaultField
},
}),
// highlight-line
],
}
```
2 changes: 0 additions & 2 deletions docs/fields/textarea.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ The Textarea Field inherits all of the default admin options from the base [Fiel

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ _\* An asterisk denotes that a property is required._

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/fields/upload.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ _\* An asterisk denotes that a property is required._

## Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 0 additions & 2 deletions docs/rich-text/slate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ Specifying custom `Type`s let you extend your custom elements by adding addition

### Example

`collections/ExampleCollection.ts`

```ts
import type { CollectionConfig } from 'payload'

Expand Down
2 changes: 1 addition & 1 deletion docs/versions/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ _Comparing an old version to a newer version of a document_
<Banner type="success">
Versions are extremely performant and totally opt-in. They don't change the
shape of your data at all. All versions are stored in a separate Collection
and can be turned on and off easily at your discretion.
and can be turned on and off easily at your risk.
</Banner>

## Options
Expand Down
4 changes: 2 additions & 2 deletions packages/payload/src/admin/forms/Form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export type FieldState = {
* Every time a field is changed locally, this flag is set to true. Prevents form state from server from overwriting local changes.
* After merging server form state, this flag is reset.
*
* @experimental This property is experimental and may change in the future. Use at your own discretion.
* @experimental This property is experimental and may change in the future. Use at your own risk.
*/
isModified?: boolean
/**
Expand Down Expand Up @@ -149,7 +149,7 @@ export type BuildFormStateArgs = {
* This will retrieve the client config in its entirety, even when unauthenticated.
* For example, the create-first-user view needs the entire config, but there is no user yet.
*
* @experimental This property is experimental and may change in the future. Use at your own discretion.
* @experimental This property is experimental and may change in the future. Use at your own risk.
*/
skipClientConfigAuth?: boolean
skipValidation?: boolean
Expand Down
4 changes: 2 additions & 2 deletions packages/payload/src/collections/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ export type CollectionAdminOptions = {
* If your cells require specific fields that may be unselected, such as within hooks, etc.,
* use `forceSelect` in conjunction with this property.
*
* @experimental This is an experimental feature and may change in the future. Use at your own discretion.
* @experimental This is an experimental feature and may change in the future. Use at your own risk.
*/
enableListViewSelectAPI?: boolean
enableRichTextLink?: boolean
Expand Down Expand Up @@ -428,7 +428,7 @@ export type CollectionAdminOptions = {
* @description Enable grouping by a field in the list view.
* Uses `payload.findDistinct` under the hood to populate the group-by options.
*
* @experimental This option is currently in beta and may change in future releases. Use at your own discretion.
* @experimental This option is currently in beta and may change in future releases. Use at your own risk.
*/
groupBy?: boolean
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/payload/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ export type Config = {
* Configure toast message behavior and appearance in the admin panel.
* Currently using [Sonner](https://sonner.emilkowal.ski) for toast notifications.
*
* @experimental This property is experimental and may change in future releases. Use at your own discretion.
* @experimental This property is experimental and may change in future releases. Use at your own risk.
*/
toast?: {
/**
Expand Down Expand Up @@ -1006,7 +1006,7 @@ export type Config = {
* For example, you may want to increase the `limits` imposed by the parser.
* Currently using @link {https://www.npmjs.com/package/busboy|busboy} under the hood.
*
* @experimental This property is experimental and may change in future releases. Use at your own discretion.
* @experimental This property is experimental and may change in future releases. Use at your own risk.
*/
bodyParser?: Partial<BusboyConfig>
/**
Expand Down
5 changes: 3 additions & 2 deletions packages/payload/src/exports/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,11 @@ export { extractID } from '../utilities/extractID.js'
export { flattenAllFields } from '../utilities/flattenAllFields.js'
export { flattenTopLevelFields } from '../utilities/flattenTopLevelFields.js'
export { formatAdminURL } from '../utilities/formatAdminURL.js'

export { formatLabels, toWords } from '../utilities/formatLabels.js'

export { getBestFitFromSizes } from '../utilities/getBestFitFromSizes.js'
export { getDataByPath } from '../utilities/getDataByPath.js'
export { getFieldPermissions } from '../utilities/getFieldPermissions.js'

export { getSafeRedirect } from '../utilities/getSafeRedirect.js'

export { getSelectMode } from '../utilities/getSelectMode.js'
Expand Down Expand Up @@ -116,6 +115,8 @@ export { sanitizeUserDataForEmail } from '../utilities/sanitizeUserDataForEmail.

export { setsAreEqual } from '../utilities/setsAreEqual.js'

export { slugify } from '../utilities/slugify.js'

export { toKebabCase } from '../utilities/toKebabCase.js'

export {
Expand Down
51 changes: 51 additions & 0 deletions packages/payload/src/fields/baseFields/slug/countVersions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type {
CollectionSlug,
DefaultDocumentIDType,
GlobalSlug,
PayloadRequest,
Where,
} from '../../../index.js'

/**
* This is a cross-entity way to count the number of versions for any given document.
* It will work for both collections and globals.
* @returns number of versions
*/
export const countVersions = async (args: {
collectionSlug?: CollectionSlug
globalSlug?: GlobalSlug
parentID?: DefaultDocumentIDType
req: PayloadRequest
}): Promise<number> => {
const { collectionSlug, globalSlug, parentID, req } = args

let countFn

const where: Where = {
parent: {
equals: parentID,
},
}

if (collectionSlug) {
countFn = () =>
req.payload.countVersions({
collection: collectionSlug,
depth: 0,
where,
})
}

if (globalSlug) {
countFn = () =>
req.payload.countGlobalVersions({
depth: 0,
global: globalSlug,
where,
})
}

const res = countFn ? (await countFn()?.then((res) => res.totalDocs || 0)) || 0 : 0

return res
}
Loading