Compare commits

..

25 Commits

Author SHA1 Message Date
Elliot DeNolf
0fb14cfebe chore(release): v3.0.0-beta.45 [skip ci] 2024-06-11 15:09:41 -04:00
Paul
2ada6fc58d fix: toasts padding and button placement by 1px (#6730)
## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-06-11 18:42:17 +00:00
Alessio Gravili
cb3355b30f feat!: move from react-toastify to sonner (#6682)
**BREAKING:** We now export toast from `sonner` instead of
`react-toastify`. If you send out toasts from your own projects, make
sure to use our `toast` export, or install `sonner`. React-toastify
toasts will no longer work anymore. The Toast APIs are mostly similar,
but there are some differences if you provide options to your toast

CSS styles have been changed from Toastify

```css
/* before */
.Toastify


/* current */
.payload-toast-container
.payload-toast-item
.payload-toast-close-button

/* individual toast items will also have these classes depending on the state */
.toast-info
.toast-warning
.toast-success
.toast-error
```


https://github.com/payloadcms/payload/assets/70709113/da3e732e-aafc-4008-9469-b10f4eb06b35

---------

Co-authored-by: Paul Popus <paul@nouance.io>
2024-06-11 14:12:59 -04:00
Patrik
10c6ffafc3 fix: only use metadata.pages for height if animated (#6728)
## Description

### Issue: 

Non-animated webp / gif files were using `metadata.pages` to calculate
it's resized heights for `imageSizes` or `cropping`.

### Fix: 

It should only use this to calculate it's height if the file's
`metadata` contains `metadata.pages`. Non-animated webps and gifs would
not have this.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-06-11 13:45:49 -04:00
Patrik
6512d5ce69 fix: create sharp file for fileHasAdjustments files or fileIsAnimated files (#6708)
## Description

Fixes #6694 

Previously we were only creating sharp files for files that have file
adjustments but instead a sharp file should be created for animated
images even if there are no file adjustments - i.e

`const fileHasAdjustments = fileSupportsResize && Boolean(resizeOptions
|| formatOptions || imageSizes || trimOptions || file.tempFilePath)`

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-06-11 10:55:51 -04:00
Jarrod Flesch
57fcc9148e fix: corrects field-paths that were incorrectly being set (#6724)
Fixes https://github.com/payloadcms/payload/issues/6650

Similar to [6712](https://github.com/payloadcms/payload/pull/6712). Field paths were
not accounting for the 4 scenarios:
- both parentPath & fieldName
- only parentPath
- only fieldName
- neither parentPath or fieldName (top level rows, etc)
2024-06-11 10:17:40 -04:00
Elliot DeNolf
36f4f23463 chore(release): v3.0.0-beta.44 [skip ci] 2024-06-11 09:46:31 -04:00
Alessio Gravili
7b7dc71845 fix: get auto type-gen to work on turbo, by running type gen in a child process outside turbo/webpack (#6714)
Before on turbo: https://github.com/vercel/next.js/issues/66723
2024-06-10 22:03:12 +00:00
Jarrod Flesch
ba513d5a97 fix: corrects tab paths when nested within other row like fields (#6712)
Fixes https://github.com/payloadcms/payload/issues/6637

There was an issue where tab paths were being generated based on 2
scenarios when there are 3 possible scenarios:
- A path is provided and the tab is named
- A path is **not** provided but the tab is named
- Neither a path or a tab name are provided
2024-06-10 16:06:09 -04:00
Jarrod Flesch
a26d03190e fix: re-exports graphql json types for external use (#6711)
Fixes https://github.com/payloadcms/payload/issues/6683

Exports import `GraphQLJSON` and `GraphQLJSONObject` from
`@payloadcms/graphql/types`

```ts
import { GraphQLJSON, GraphQLJSONObject } from '@payloadcms/graphql/types'
```
2024-06-10 14:53:31 -04:00
Patrik
9f525621c8 fix(ui): removes array & blocks & group fields from sort (#6576)
## Description

V2 PR [here](https://github.com/payloadcms/payload/pull/6574)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-06-10 14:09:50 -04:00
Elliot DeNolf
7309d474ee feat!: type auto-generation (#6657)
Types are now auto-generated by default.

You can opt-out of this behavior by setting:
```ts
buildConfig({
  // Rest of config
  typescript: {
    autoGenerate: false
  },
})
```
2024-06-10 13:42:44 -04:00
Jarrod Flesch
45e86832c2 fix: global draft validations (#6709)
- Extends draft validation from https://github.com/payloadcms/payload/pull/6677 to work with globals as
well

- Fixes bug from https://github.com/payloadcms/payload/pull/6677 where
autosave was not saving properly after first autosave
2024-06-10 12:31:22 -04:00
Alessio Gravili
1bd91b23ca chore: improved clean commands which work on windows and output pretty summary (#6685) 2024-06-09 05:21:11 +00:00
Alessio Gravili
ac34380eb8 fix(ui): set checkbox htmlFor by default, fixing some checkbox labels not toggling the checkbox (#6684) 2024-06-08 19:34:26 +00:00
Jacob Fletcher
17707852e0 chore: migrates @faceless-ui imports to esm (#6681) 2024-06-07 22:59:39 -04:00
Elliot DeNolf
8b95218577 chore(release): v3.0.0-beta.43 [skip ci] 2024-06-07 17:45:28 -04:00
Jarrod Flesch
a79d23c631 chore: adjusts test config for draft validation (#6678) 2024-06-07 16:01:03 -04:00
Jarrod Flesch
52c81ad525 feat: adds draft validation option (#6677)
## Description

Allows draft validation to be enabled at the config level.

You can enable this by:
```ts
// ...collectionConfig
versions: {
  drafts: {
    validate: true // defaults to false
  }
}
```
2024-06-07 15:22:03 -04:00
Paul
8ec836737e chore: add turbo resolveAlias mock alias to hide webpack warnings (#6676) 2024-06-07 17:23:35 +00:00
Paul
e4a90294ea feat(plugin-redirects)!: update fields overrides to use a function (#6675)
## Description

Updates the `fields` override in plugin redirects to allow for
overriding

```ts
// before
overrides: {
  fields: [
    {
      type: 'text',
      name: 'customField',
    },
  ],
},

// current
overrides: {
  fields: ({ defaultFields }) => {
    return [
      ...defaultFields,
      {
        type: 'text',
        name: 'customField',
      },
    ]
  },
},
```


## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
2024-06-07 14:41:09 +00:00
Jacob Fletcher
7c8d562f03 fix(next): live preview device position when using zoom (#6665) 2024-06-07 10:17:49 -04:00
Alessio Gravili
11c3a65e63 feat(richtext-*): allow omitting the root editor property (#6660)
No need to add lexical/slate to the bundle if someone decides not to
make use of richText fields within payload at all
2024-06-06 17:57:03 +00:00
Paul
8dd5e4dc24 fix: max versions config not being respected on globals (#6654)
Closes https://github.com/payloadcms/payload/issues/6646

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-06-06 17:21:32 +00:00
Alessio Gravili
9bd9e7a986 feat!: upgrade minimum node 20 version from 20.6.0 to 20.9.0 (#6659)
**BREAKING**:
- This bumps the minimum required node version from node 20.6.0 to node
20.9.0. This is because 20.6.0 breaks type generation due to a CJS node
bug, and 20.9.0 is the next v20 LTS version. The minimum node 18 version
stays the same (18.20.2)
2024-06-06 17:15:21 +00:00
279 changed files with 3073 additions and 895 deletions

7
.vscode/launch.json vendored
View File

@@ -111,6 +111,13 @@
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js field-error-states",
"cwd": "${workspaceFolder}",
"name": "Run Dev Field Error States",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run test:int live-preview",
"cwd": "${workspaceFolder}",

View File

@@ -22,6 +22,7 @@ Collections and Globals both support the same options for configuring drafts. Yo
| Draft Option | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `autosave` | Enable `autosave` to automatically save progress while documents are edited. To enable, set to `true` or pass an object with [options](/docs/versions/autosave). |
| `validate` | Set `validate` to `true` to validate draft documents when saved. Default is `false`. |
## Database changes

View File

@@ -33,6 +33,7 @@ export default withBundleAnalyzer(
'.js': ['.ts', '.tsx', '.js', '.jsx'],
'.mjs': ['.mts', '.mjs'],
}
return webpackConfig
},
}),

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"private": true,
"type": "module",
"scripts": {
@@ -9,6 +9,7 @@
"build:app": "next build",
"build:app:analyze": "cross-env ANALYZE=true next build",
"build:core": "turbo build --filter \"!@payloadcms/plugin-*\"",
"build:core:force": "pnpm clean:build && turbo build --filter \"!@payloadcms/plugin-*\" --no-cache --force",
"build:create-payload-app": "turbo build --filter create-payload-app",
"build:db-mongodb": "turbo build --filter db-mongodb",
"build:db-postgres": "turbo build --filter db-postgres",
@@ -42,21 +43,21 @@
"build:translations": "turbo build --filter translations",
"build:ui": "turbo build --filter ui",
"clean": "turbo clean",
"clean:all": "find . \\( -type d \\( -name node_modules -o -name dist -o -name .cache -o -name .next -o -name .turbo \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} +",
"clean:build": "find . \\( -type d \\( -name dist -o -name .cache -o -name .next -o -name .turbo \\) -o -type f -name tsconfig.tsbuildinfo \\) -not -path '*/node_modules/*' -exec rm -rf {} +",
"clean:cache": "rimraf node_modules/.cache && rimraf packages/payload/node_modules/.cache && rimraf .next",
"clean:all": "node ./scripts/delete-recursively.js '@node_modules' 'media' '**/dist' '**/.cache' '**/.next' '**/.turbo' '**/tsconfig.tsbuildinfo' '**/payload*.tgz'",
"clean:build": "node ./scripts/delete-recursively.js 'media' '**/dist' '**/.cache' '**/.next' '**/.turbo' '**/tsconfig.tsbuildinfo' '**/payload*.tgz'",
"clean:cache": "node ./scripts/delete-recursively.js node_modules/.cache! packages/payload/node_modules/.cache! .next",
"dev": "cross-env NODE_OPTIONS=--no-deprecation node ./test/dev.js",
"dev:generate-graphql-schema": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/generateGraphQLSchema.ts",
"dev:generate-types": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/generateTypes.ts",
"dev:postgres": "cross-env NODE_OPTIONS=--no-deprecation PAYLOAD_DATABASE=postgres node ./test/dev.js",
"devsafe": "rimraf .next && pnpm dev",
"devsafe": "node ./scripts/delete-recursively.js '**/.next' && pnpm dev",
"docker:restart": "pnpm docker:stop --remove-orphans && pnpm docker:start",
"docker:start": "docker compose -f packages/plugin-cloud-storage/docker-compose.yml up -d",
"docker:stop": "docker compose -f packages/plugin-cloud-storage/docker-compose.yml down",
"fix": "eslint \"packages/**/*.ts\" --fix",
"lint": "eslint \"packages/**/*.ts\"",
"lint-staged": "lint-staged",
"obliterate-playwright-cache": "rm -rf ~/Library/Caches/ms-playwright && find /System/Volumes/Data/private/var/folders -type d -name 'playwright*' -exec rm -rf {} +",
"obliterate-playwright-cache-macos": "rm -rf ~/Library/Caches/ms-playwright && find /System/Volumes/Data/private/var/folders -type d -name 'playwright*' -exec rm -rf {} +",
"prepare": "husky install",
"reinstall": "pnpm clean:all && pnpm install",
"release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha",
@@ -123,6 +124,7 @@
"husky": "^8.0.3",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"json-schema-to-typescript": "11.0.3",
"lint-staged": "^14.0.1",
"minimist": "1.2.8",
"mongodb-memory-server": "^9.0",
@@ -158,7 +160,7 @@
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2",
"node": "^18.20.2 || >=20.9.0",
"pnpm": "^8.15.7"
},
"pnpm": {

View File

@@ -1,6 +1,6 @@
{
"name": "create-payload-app",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The officially supported MongoDB database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The officially supported Postgres database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-nodemailer",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload Nodemailer Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {
@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-resend",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload Resend Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {
@@ -40,7 +40,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/graphql",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -0,0 +1 @@
export { GraphQLJSON, GraphQLJSONObject } from '../packages/graphql-type-json/index.js'

View File

@@ -41,6 +41,7 @@ import {
GraphQLUnionType,
} from 'graphql'
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars'
import { MissingEditorProp } from 'payload/errors'
import { tabHasName } from 'payload/types'
import { createDataloaderCacheKey, toWords } from 'payload/utilities'
@@ -476,6 +477,10 @@ function buildObjectType({
async resolve(parent, args, context: Context) {
let depth = config.defaultDepth
if (typeof args.depth !== 'undefined') depth = args.depth
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The official live preview React SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The official live preview JavaScript SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -54,8 +54,8 @@
"path-to-regexp": "^6.2.1",
"qs": "6.11.2",
"react-diff-viewer-continued": "3.2.6",
"react-toastify": "10.0.5",
"sass": "1.77.4",
"sonner": "^1.5.0",
"ws": "^8.16.0"
},
"devDependencies": {
@@ -84,7 +84,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -11,7 +11,6 @@ import { headers as getHeaders, cookies as nextCookies } from 'next/headers.js'
import { parseCookies } from 'payload/auth'
import { createClientConfig } from 'payload/config'
import React from 'react'
import 'react-toastify/dist/ReactToastify.css'
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'

View File

@@ -1,5 +1,5 @@
@import './styles.scss';
@import './toastify.scss';
@import './toasts.scss';
@import './colors.scss';
:root {

View File

@@ -1,58 +0,0 @@
@import 'vars';
.Toastify {
.Toastify__toast-container {
left: base(5);
transform: none;
right: base(5);
width: auto;
}
.Toastify__toast {
padding: base(0.5);
border-radius: $style-radius-m;
font-weight: 600;
}
.Toastify__close-button {
align-self: center;
opacity: 0.7;
&:hover {
opacity: 1;
}
}
.Toastify__toast--success {
color: var(--color-success-900);
background: var(--color-success-500);
.Toastify__progress-bar {
background-color: var(--color-success-900);
}
}
.Toastify__close-button--success {
color: var(--color-success-900);
}
.Toastify__toast--error {
background: var(--theme-error-500);
color: #fff;
.Toastify__progress-bar {
background-color: #fff;
}
}
.Toastify__close-button--light {
color: inherit;
}
@include mid-break {
.Toastify__toast-container {
left: $baseline;
right: $baseline;
}
}
}

View File

@@ -0,0 +1,111 @@
.payload-toast-container {
.payload-toast-close-button {
left: unset;
right: 0.5rem;
top: 1.55rem;
color: var(--theme-elevation-400);
background: unset;
border: none;
display: flex;
width: 1.25rem;
height: 1.25rem;
justify-content: center;
align-items: center;
&:hover {
background: none;
}
svg {
width: 2rem;
height: 2rem;
}
[dir='RTL'] & {
right: unset;
left: 0.5rem;
}
}
.payload-toast-item {
padding: 1rem 2.5rem 1rem 1rem;
color: var(--theme-text);
font-style: normal;
font-weight: 600;
display: flex;
gap: 1rem;
align-items: center;
width: 100%;
border-radius: 0.15rem;
border: 1px solid var(--theme-border-color);
background: var(--theme-input-bg);
box-shadow:
0px 10px 4px -8px rgba(0, 2, 4, 0.02),
0px 2px 3px 0px rgba(0, 2, 4, 0.05);
.toast-content {
transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
&[data-front='false'] {
.toast-content {
opacity: 0;
}
}
&[data-expanded='true'] {
.toast-content {
opacity: 1;
}
}
.toast-icon {
svg {
width: 2.4rem;
height: 2.4rem;
}
}
&.toast-warning {
border-color: var(--theme-warning-200);
background-color: var(--theme-warning-100);
}
&.toast-error {
border-color: var(--theme-error-300);
background-color: var(--theme-error-150);
}
&.toast-success {
border-color: var(--theme-success-200);
background-color: var(--theme-success-100);
}
&.toast-info {
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
}
[data-theme='light'] & {
&.toast-warning {
border-color: var(--theme-warning-550);
background-color: var(--theme-warning-100);
}
&.toast-error {
border-color: var(--theme-error-200);
background-color: var(--theme-error-50);
}
&.toast-success {
border-color: var(--theme-success-550);
background-color: var(--theme-success-50);
}
&.toast-info {
border-color: var(--theme-border-color);
background-color: var(--theme-elevation-50);
}
}
}
}

View File

@@ -36,6 +36,16 @@ export const reload = async (config: SanitizedConfig, payload: Payload): Promise
// TODO: support HMR for other props in the future (see payload/src/index init()) hat may change on Payload singleton
// Generate types
if (config.typescript.autoGenerate !== false) {
// We cannot run it directly here, as generate-types imports json-schema-to-typescript, which breaks on turbopack.
// see: https://github.com/vercel/next.js/issues/66723
void payload.bin({
args: ['generate:types'],
log: false,
})
}
await payload.db.init()
if (payload.db.connect) {
await payload.db.connect({ hotReload: true })

View File

@@ -15,7 +15,7 @@ import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { useSearchParams } from 'next/navigation.js'
import qs from 'qs'
import * as React from 'react'
import { toast } from 'react-toastify'
import { toast } from 'sonner'
import { SetDocumentStepNav } from '../Edit/Default/SetDocumentStepNav/index.js'
import { LocaleSelector } from './LocaleSelector/index.js'

View File

@@ -151,8 +151,10 @@ export const Document: React.FC<AdminViewProps> = async ({
hasSavePermission &&
((collectionConfig?.versions?.drafts && collectionConfig?.versions?.drafts?.autosave) ||
(globalConfig?.versions?.drafts && globalConfig?.versions?.drafts?.autosave))
const validateDraftData =
collectionConfig?.versions?.drafts && collectionConfig?.versions?.drafts?.validate
if (shouldAutosave && !id && collectionSlug) {
if (shouldAutosave && !validateDraftData && !id && collectionSlug) {
const doc = await payload.create({
collection: collectionSlug,
data: {},

View File

@@ -10,7 +10,7 @@ import { useConfig } from '@payloadcms/ui/providers/Config'
import { useDocumentInfo } from '@payloadcms/ui/providers/DocumentInfo'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import React, { useCallback, useEffect, useState } from 'react'
import { toast } from 'react-toastify'
import { toast } from 'sonner'
import type { Props } from './types.js'
@@ -71,7 +71,7 @@ export const Auth: React.FC<Props> = (props) => {
})
if (response.status === 200) {
toast.success(t('authentication:successfullyUnlocked'), { autoClose: 3000 })
toast.success(t('authentication:successfullyUnlocked'))
} else {
toast.error(t('authentication:failedToUnlock'))
}

View File

@@ -9,7 +9,7 @@ import { useConfig } from '@payloadcms/ui/providers/Config'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { email } from 'payload/fields/validations'
import React, { Fragment, useState } from 'react'
import { toast } from 'react-toastify'
import { toast } from 'sonner'
export const ForgotPasswordForm: React.FC = () => {
const config = useConfig()

View File

@@ -10,16 +10,20 @@ export const DeviceContainer: React.FC<{
const { children } = props
const deviceFrameRef = React.useRef<HTMLDivElement>(null)
const outerFrameRef = React.useRef<HTMLDivElement>(null)
const { breakpoint, setMeasuredDeviceSize, size, zoom } = useLivePreviewContext()
const { breakpoint, setMeasuredDeviceSize, size: desiredSize, zoom } = useLivePreviewContext()
// Keep an accurate measurement of the actual device size as it is truly rendered
// This is helpful when `sizes` are non-number units like percentages, etc.
const { size: measuredDeviceSize } = useResize(deviceFrameRef)
const { size: measuredDeviceSize } = useResize(deviceFrameRef.current)
const { size: outerFrameSize } = useResize(outerFrameRef.current)
let deviceIsLargerThanFrame: boolean = false
// Sync the measured device size with the context so that other components can use it
// This happens from the bottom up so that as this component mounts and unmounts,
// Its size is freshly populated again upon re-mounting, i.e. going from iframe->popup->iframe
// its size is freshly populated again upon re-mounting, i.e. going from iframe->popup->iframe
useEffect(() => {
if (measuredDeviceSize) {
setMeasuredDeviceSize(measuredDeviceSize)
@@ -34,13 +38,34 @@ export const DeviceContainer: React.FC<{
if (
typeof zoom === 'number' &&
typeof size.width === 'number' &&
typeof size.height === 'number'
typeof desiredSize.width === 'number' &&
typeof desiredSize.height === 'number' &&
typeof measuredDeviceSize.width === 'number' &&
typeof measuredDeviceSize.height === 'number'
) {
const scaledWidth = size.width / zoom
const difference = scaledWidth - size.width
x = `${difference / 2}px`
margin = '0 auto'
const scaledDesiredWidth = desiredSize.width / zoom
const scaledDeviceWidth = measuredDeviceSize.width * zoom
const scaledDeviceDifferencePixels = scaledDesiredWidth - desiredSize.width
deviceIsLargerThanFrame = scaledDeviceWidth > outerFrameSize.width
if (deviceIsLargerThanFrame) {
if (zoom > 1) {
const differenceFromDeviceToFrame = measuredDeviceSize.width - outerFrameSize.width
if (differenceFromDeviceToFrame < 0) x = `${differenceFromDeviceToFrame / 2}px`
else x = '0'
} else {
x = '0'
}
} else {
if (zoom >= 1) {
x = `${scaledDeviceDifferencePixels / 2}px`
} else {
const differenceFromDeviceToFrame = outerFrameSize.width - scaledDeviceWidth
x = `${differenceFromDeviceToFrame / 2}px`
margin = '0'
}
}
}
}
@@ -48,21 +73,29 @@ export const DeviceContainer: React.FC<{
let height = zoom ? `${100 / zoom}%` : '100%'
if (breakpoint !== 'responsive') {
width = `${size?.width / (typeof zoom === 'number' ? zoom : 1)}px`
height = `${size?.height / (typeof zoom === 'number' ? zoom : 1)}px`
width = `${desiredSize?.width / (typeof zoom === 'number' ? zoom : 1)}px`
height = `${desiredSize?.height / (typeof zoom === 'number' ? zoom : 1)}px`
}
return (
<div
ref={deviceFrameRef}
ref={outerFrameRef}
style={{
height,
margin,
transform: `translate3d(${x}, 0, 0)`,
width,
height: '100%',
width: '100%',
}}
>
{children}
<div
ref={deviceFrameRef}
style={{
height,
margin,
transform: `translate3d(${x}, 0, 0)`,
width,
}}
>
{children}
</div>
</div>
)
}

View File

@@ -11,7 +11,7 @@ import { useConfig } from '@payloadcms/ui/providers/Config'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { useRouter } from 'next/navigation.js'
import React from 'react'
import { toast } from 'react-toastify'
import { toast } from 'sonner'
type Args = {
token: string
@@ -49,7 +49,7 @@ export const ResetPasswordClient: React.FC<Args> = ({ token }) => {
history.push(`${admin}`)
} else {
history.push(`${admin}/login`)
toast.success(i18n.t('general:updatedSuccessfully'), { autoClose: 3000 })
toast.success(i18n.t('general:updatedSuccessfully'))
}
},
[fetchFullUser, history, admin, i18n],

View File

@@ -9,7 +9,7 @@ import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal'
import { requests } from '@payloadcms/ui/utilities/api'
import { useRouter } from 'next/navigation.js'
import React, { Fragment, useCallback, useState } from 'react'
import { toast } from 'react-toastify'
import { toast } from 'sonner'
import type { Props } from './types.js'

View File

@@ -28,6 +28,13 @@ export const withPayload = (nextConfig = {}) => {
'libsql',
],
},
turbo: {
...(nextConfig?.experimental?.turbo || {}),
resolveAlias: {
...(nextConfig?.experimental?.turbo?.resolveAlias || {}),
'payload-mock-package': 'payload-mock-package',
},
},
},
headers: async () => {
const headersFromConfig = 'headers' in nextConfig ? await nextConfig.headers() : []

View File

@@ -4,8 +4,6 @@ import { register } from 'node:module'
import path from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { bin } from './dist/bin/index.js'
// Allow disabling SWC for debugging
if (process.env.DISABLE_SWC !== 'true') {
const filename = fileURLToPath(import.meta.url)
@@ -15,4 +13,9 @@ if (process.env.DISABLE_SWC !== 'true') {
register('./dist/bin/loader/index.js', url)
}
bin()
const start = async () => {
const { bin } = await import('./dist/bin/index.js')
bin()
}
void start()

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
"keywords": [
"admin panel",
@@ -137,7 +137,7 @@
}
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -6,11 +6,16 @@ import type { SanitizedConfig } from '../config/types.js'
import { configToJSONSchema } from '../utilities/configToJSONSchema.js'
import Logger from '../utilities/logger.js'
export async function generateTypes(config: SanitizedConfig): Promise<void> {
export async function generateTypes(
config: SanitizedConfig,
options?: { log: boolean },
): Promise<void> {
const logger = Logger()
const outputFile = process.env.PAYLOAD_TS_OUTPUT_PATH || config.typescript.outputFile
logger.info('Compiling TS types for Collections and Globals...')
const shouldLog = options?.log ?? true
if (shouldLog) logger.info('Compiling TS types for Collections and Globals...')
const jsonSchema = configToJSONSchema(config, config.db.defaultIDType)
@@ -37,5 +42,5 @@ export async function generateTypes(config: SanitizedConfig): Promise<void> {
}
}
fs.writeFileSync(outputFile, compiled)
logger.info(`Types written to ${outputFile}`)
if (shouldLog) logger.info(`Types written to ${outputFile}`)
}

View File

@@ -15,6 +15,7 @@ import { getBaseUploadFields } from '../../uploads/getBaseFields.js'
import { formatLabels } from '../../utilities/formatLabels.js'
import { isPlainObject } from '../../utilities/isPlainObject.js'
import baseVersionFields from '../../versions/baseFields.js'
import { versionDefaults } from '../../versions/defaults.js'
import { authDefaults, defaults } from './defaults.js'
export const sanitizeCollection = async (
@@ -84,15 +85,20 @@ export const sanitizeCollection = async (
if (sanitized.versions.drafts === true) {
sanitized.versions.drafts = {
autosave: false,
validate: false,
}
}
if (sanitized.versions.drafts.autosave === true) {
sanitized.versions.drafts.autosave = {
interval: 2000,
interval: versionDefaults.autosaveInterval,
}
}
if (sanitized.versions.drafts.validate === undefined) {
sanitized.versions.drafts.validate = false
}
sanitized.fields = mergeBaseFields(sanitized.fields, baseVersionFields)
}
}

View File

@@ -221,6 +221,7 @@ const collectionSchema = joi.object().keys({
interval: joi.number(),
}),
),
validate: joi.boolean(),
}),
joi.boolean(),
),

View File

@@ -165,14 +165,6 @@ export const createOperation = async <TSlug extends keyof GeneratedTypes['collec
Promise.resolve(),
)
// /////////////////////////////////////
// Write files to local storage
// /////////////////////////////////////
// if (!collectionConfig.upload.disableLocalStorage) {
// await uploadFiles(payload, filesToUpload, req.t)
// }
// /////////////////////////////////////
// beforeChange - Collection
// /////////////////////////////////////
@@ -203,7 +195,10 @@ export const createOperation = async <TSlug extends keyof GeneratedTypes['collec
global: null,
operation: 'create',
req,
skipValidation: shouldSaveDraft,
skipValidation:
shouldSaveDraft &&
collectionConfig.versions.drafts &&
!collectionConfig.versions.drafts.validate,
})
// /////////////////////////////////////

View File

@@ -205,7 +205,10 @@ export const duplicateOperation = async <TSlug extends keyof GeneratedTypes['col
global: null,
operation,
req,
skipValidation: shouldSaveDraft,
skipValidation:
shouldSaveDraft &&
collectionConfig.versions.drafts &&
!collectionConfig.versions.drafts.validate,
})
// set req.locale back to the original locale

View File

@@ -270,7 +270,10 @@ export const updateOperation = async <TSlug extends keyof GeneratedTypes['collec
operation: 'update',
req,
skipValidation:
Boolean(collectionConfig.versions?.drafts) && data._status !== 'published',
shouldSaveDraft &&
collectionConfig.versions.drafts &&
!collectionConfig.versions.drafts.validate &&
data._status !== 'published',
})
// /////////////////////////////////////

View File

@@ -242,7 +242,11 @@ export const updateByIDOperation = async <TSlug extends keyof GeneratedTypes['co
global: null,
operation: 'update',
req,
skipValidation: Boolean(collectionConfig.versions?.drafts) && data._status !== 'published',
skipValidation:
shouldSaveDraft &&
collectionConfig.versions.drafts &&
!collectionConfig.versions.drafts.validate &&
data._status !== 'published',
})
// /////////////////////////////////////

View File

@@ -49,6 +49,7 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
serverURL: '',
telemetry: true,
typescript: {
autoGenerate: true,
outputFile: `${typeof process?.cwd === 'function' ? process.cwd() : ''}/payload-types.ts`,
},
upload: {},

View File

@@ -1,5 +1,5 @@
import { findUpSync, pathExistsSync } from 'find-up'
import fs from 'fs'
import { getTsconfig } from 'get-tsconfig'
import path from 'path'
/**
@@ -12,37 +12,30 @@ const getTSConfigPaths = (): {
outPath?: string
rootPath?: string
srcPath?: string
tsConfigPath?: string
} => {
const tsConfigPath = findUpSync('tsconfig.json')
if (!tsConfigPath) {
return {
rootPath: process.cwd(),
}
}
const tsConfigResult = getTsconfig()
const tsConfig = tsConfigResult.config
const tsConfigDir = path.dirname(tsConfigResult.path)
try {
// Read the file as a string and remove trailing commas
const rawTsConfig = fs
.readFileSync(tsConfigPath, 'utf-8')
.replace(/,\s*\]/g, ']')
.replace(/,\s*\}/g, '}')
const tsConfig = JSON.parse(rawTsConfig)
const rootPath = process.cwd()
const rootConfigDir = path.resolve(tsConfigDir, tsConfig.compilerOptions.baseUrl || '')
const srcPath = tsConfig.compilerOptions?.rootDir || path.resolve(process.cwd(), 'src')
const outPath = tsConfig.compilerOptions?.outDir || path.resolve(process.cwd(), 'dist')
const tsConfigDir = path.dirname(tsConfigPath)
let configPath = tsConfig.compilerOptions?.paths?.['@payload-config']?.[0]
let configPath = path.resolve(
rootConfigDir,
tsConfig.compilerOptions?.paths?.['@payload-config']?.[0],
)
if (configPath) {
configPath = path.resolve(tsConfigDir, configPath)
configPath = path.resolve(rootConfigDir, configPath)
}
return {
configPath,
outPath,
rootPath,
rootPath: rootConfigDir,
srcPath,
tsConfigPath: tsConfigResult.path,
}
} catch (error) {
console.error(`Error parsing tsconfig.json: ${error}`) // Do not throw the error, as we can still continue with the other config path finding methods
@@ -70,6 +63,11 @@ export const findConfig = (): string => {
const { configPath, outPath, rootPath, srcPath } = getTSConfigPaths()
// if configPath is absolute file, not folder, return it
if (path.extname(configPath) === '.js' || path.extname(configPath) === '.ts') {
return configPath
}
const searchPaths =
process.env.NODE_ENV === 'production'
? [configPath, outPath, srcPath, rootPath]

View File

@@ -101,7 +101,7 @@ export default joi.object({
defaultMaxTextLength: joi.number(),
editor: joi
.object()
.required()
.optional()
.keys({
CellComponent: componentSchema.optional(),
FieldComponent: componentSchema.optional(),
@@ -189,6 +189,7 @@ export default joi.object({
sharp: joi.any(),
telemetry: joi.boolean(),
typescript: joi.object({
autoGenerate: joi.boolean(),
declare: joi.alternatives().try(joi.boolean(), joi.object({ ignoreTSError: joi.boolean() })),
outputFile: joi.string(),
}),

View File

@@ -615,7 +615,7 @@ export type Config = {
*/
defaultMaxTextLength?: number
/** Default richtext editor to use for richText fields */
editor: RichTextAdapterProvider<any, any, any>
editor?: RichTextAdapterProvider<any, any, any>
/**
* Email Adapter
*
@@ -719,6 +719,12 @@ export type Config = {
telemetry?: boolean
/** Control how typescript interfaces are generated from your collections. */
typescript?: {
/**
* Automatically generate types during development
* @default true
*/
autoGenerate?: boolean
/** Disable declare block in generated types file */
declare?:
| {
@@ -732,6 +738,7 @@ export type Config = {
ignoreTSError?: boolean
}
| false
/** Filename to write the generated types to */
outputFile?: string
}
@@ -747,7 +754,7 @@ export type SanitizedConfig = Omit<
> & {
collections: SanitizedCollectionConfig[]
/** Default richtext editor to use for richText fields */
editor: RichTextAdapter<any, any, any>
editor?: RichTextAdapter<any, any, any>
endpoints: Endpoint[]
globals: SanitizedGlobalConfig[]
i18n: Required<I18nOptions>

View File

@@ -12,6 +12,7 @@ export { InvalidFieldName } from './InvalidFieldName.js'
export { InvalidFieldRelationship } from './InvalidFieldRelationship.js'
export { LockedAuth } from './LockedAuth.js'
export { MissingCollectionLabel } from './MissingCollectionLabel.js'
export { MissingEditorProp } from './MissingEditorProp.js'
export { MissingFieldInputOptions } from './MissingFieldInputOptions.js'
export { MissingFieldType } from './MissingFieldType.js'
export { MissingFile } from './MissingFile.js'

View File

@@ -2,8 +2,10 @@ export {
APIError,
AuthenticationError,
DuplicateCollection,
DuplicateFieldName,
DuplicateGlobal,
ErrorDeletingFile,
FileRetrievalError,
FileUploadError,
Forbidden,
InvalidConfiguration,
@@ -11,6 +13,7 @@ export {
InvalidFieldRelationship,
LockedAuth,
MissingCollectionLabel,
MissingEditorProp,
MissingFieldInputOptions,
MissingFieldType,
MissingFile,

View File

@@ -1,5 +1,6 @@
export { buildVersionCollectionFields } from '../versions/buildCollectionFields.js'
export { buildVersionGlobalFields } from '../versions/buildGlobalFields.js'
export { versionDefaults } from '../versions/defaults.js'
export { deleteCollectionVersions } from '../versions/deleteCollectionVersions.js'
export { enforceMaxVersions } from '../versions/enforceMaxVersions.js'
export { getLatestCollectionVersion } from '../versions/getLatestCollectionVersion.js'

View File

@@ -158,7 +158,7 @@ export const sanitizeFields = async ({
// config.editor should be sanitized at this point
field.editor = _config.editor
} else {
throw new MissingEditorProp(field)
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
}

View File

@@ -5,6 +5,7 @@ import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
import type { PayloadRequestWithData, RequestContext } from '../../../types/index.js'
import type { Field, TabAsField } from '../../config/types.js'
import { MissingEditorProp } from '../../../errors/index.js'
import { fieldAffectsData, tabHasName } from '../../config/types.js'
import getValueWithDefault from '../../getDefaultValue.js'
import { relationshipPopulationPromise } from './relationshipPopulationPromise.js'
@@ -143,6 +144,9 @@ export const promise = async ({
}
case 'richText': {
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -268,6 +268,9 @@ export const richText: Validate<object, unknown, unknown, RichTextField> = async
value,
options,
) => {
if (!options?.editor) {
throw new Error('richText field has no editor property.')
}
if (typeof options?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -7,6 +7,7 @@ import { fieldAffectsData } from '../../fields/config/types.js'
import mergeBaseFields from '../../fields/mergeBaseFields.js'
import { toWords } from '../../utilities/formatLabels.js'
import baseVersionFields from '../../versions/baseFields.js'
import { versionDefaults } from '../../versions/defaults.js'
export const sanitizeGlobals = async (
config: Config,
@@ -47,15 +48,20 @@ export const sanitizeGlobals = async (
if (global.versions.drafts === true) {
global.versions.drafts = {
autosave: false,
validate: false,
}
}
if (global.versions.drafts.autosave === true) {
global.versions.drafts.autosave = {
interval: 2000,
interval: versionDefaults.autosaveInterval,
}
}
if (global.versions.drafts.validate === undefined) {
global.versions.drafts.validate = false
}
global.fields = mergeBaseFields(global.fields, baseVersionFields)
}
}

View File

@@ -90,6 +90,7 @@ const globalSchema = joi
interval: joi.number(),
}),
),
validate: joi.boolean(),
}),
joi.boolean(),
),

View File

@@ -167,7 +167,8 @@ export const updateOperation = async <TSlug extends keyof GeneratedTypes['global
global: globalConfig,
operation: 'update',
req,
skipValidation: shouldSaveDraft,
skipValidation:
shouldSaveDraft && globalConfig.versions.drafts && !globalConfig.versions.drafts.validate,
})
// /////////////////////////////////////

View File

@@ -2,7 +2,10 @@ import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http'
import type pino from 'pino'
import { spawn } from 'child_process'
import crypto from 'crypto'
import { fileURLToPath } from 'node:url'
import path from 'path'
import type { AuthArgs } from './auth/operations/auth.js'
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword.js'
@@ -56,6 +59,9 @@ import flattenFields from './utilities/flattenTopLevelFields.js'
import Logger from './utilities/logger.js'
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
/**
* @description Payload
*/
@@ -301,6 +307,31 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
[slug: string]: any // TODO: Type this
} = {}
async bin({
args,
cwd,
log,
}: {
args: string[]
cwd?: string
log?: boolean
}): Promise<{ code: number }> {
return new Promise((resolve, reject) => {
const spawned = spawn('node', [path.resolve(dirname, '../bin.js'), ...args], {
cwd,
stdio: log || log === undefined ? 'inherit' : 'ignore',
})
spawned.on('exit', (code) => {
resolve({ code })
})
spawned.on('error', (error) => {
reject(error)
})
})
}
/**
* @description delete one or more documents
* @param options
@@ -363,6 +394,16 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
}
})
// Generate types on startup
if (process.env.NODE_ENV !== 'production' && this.config.typescript.autoGenerate !== false) {
// We cannot run it directly here, as generate-types imports json-schema-to-typescript, which breaks on turbopack.
// see: https://github.com/vercel/next.js/issues/66723
void this.bin({
args: ['generate:types'],
log: false,
})
}
this.db = this.config.db.init({ payload: this })
this.db.payload = this

View File

@@ -133,21 +133,23 @@ export const generateFileData = async <T>({
if (fileIsAnimated) sharpOptions.animated = true
if (fileHasAdjustments && sharp) {
if (sharp && (fileIsAnimated || fileHasAdjustments)) {
if (file.tempFilePath) {
sharpFile = sharp(file.tempFilePath, sharpOptions).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
} else {
sharpFile = sharp(file.data, sharpOptions).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
}
if (resizeOptions) {
sharpFile = sharpFile.resize(resizeOptions)
}
if (formatOptions) {
sharpFile = sharpFile.toFormat(formatOptions.format, formatOptions.options)
}
if (trimOptions) {
sharpFile = sharpFile.trim(trimOptions)
if (fileHasAdjustments) {
if (resizeOptions) {
sharpFile = sharpFile.resize(resizeOptions)
}
if (formatOptions) {
sharpFile = sharpFile.toFormat(formatOptions.format, formatOptions.options)
}
if (trimOptions) {
sharpFile = sharpFile.trim(trimOptions)
}
}
}
@@ -201,7 +203,6 @@ export const generateFileData = async <T>({
let fileForResize = file
if (cropData && sharp) {
const metadata = await sharpFile.metadata()
const { data: croppedImage, info } = await cropImage({ cropData, dimensions, file, sharp })
filesToSave.push({
@@ -215,7 +216,11 @@ export const generateFileData = async <T>({
size: info.size,
}
fileData.width = info.width
fileData.height = fileIsAnimated ? info.height / metadata.pages : info.height
fileData.height = info.height
if (fileIsAnimated) {
const metadata = await sharpFile.metadata()
fileData.height = metadata.pages ? info.height / metadata.pages : info.height
}
fileData.filesize = info.size
if (file.tempFilePath) {

View File

@@ -364,7 +364,7 @@ export async function resizeAndTransformImageSizes({
name: imageResizeConfig.name,
filename: imageNameWithDimensions,
filesize: size,
height: fileIsAnimated ? height / metadata.pages : height,
height: fileIsAnimated && metadata.pages ? height / metadata.pages : height,
mimeType: mimeInfo?.mime || mimeType,
sizesToSave: [{ buffer: bufferData, path: imagePath }],
width,

View File

@@ -8,6 +8,7 @@ import type { SanitizedConfig } from '../config/types.js'
import type { Field, FieldAffectingData, Option } from '../fields/config/types.js'
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
import { MissingEditorProp } from '../errors/MissingEditorProp.js'
import { fieldAffectsData, tabHasName } from '../fields/config/types.js'
import { deepCopyObject } from './deepCopyObject.js'
import { toWords } from './formatLabels.js'
@@ -195,6 +196,9 @@ export function fieldsToJSONSchema(
}
case 'richText': {
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -0,0 +1,3 @@
export const versionDefaults = {
autosaveInterval: 2000,
}

View File

@@ -68,7 +68,7 @@ export const enforceMaxVersions = async ({
}
await payload.db.deleteVersions({
collection: collection?.slug,
collection: slug,
req,
where: deleteQuery,
})

View File

@@ -4,10 +4,12 @@ export type Autosave = {
export type IncomingDrafts = {
autosave?: Autosave | boolean
validate?: boolean
}
export type SanitizedDrafts = {
autosave: Autosave | false
validate: boolean
}
export type IncomingCollectionVersions = {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud-storage",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The official cloud storage plugin for Payload CMS",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The official Payload Cloud plugin",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-form-builder",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Form builder plugin for Payload CMS",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-nested-docs",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The official Nested Docs plugin for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-redirects",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Redirects plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,34 +0,0 @@
// @ts-nocheck
/**
* Simple object check.
* @param item
* @returns {boolean}
*/
export function isObject(item: unknown): boolean {
return item && typeof item === 'object' && !Array.isArray(item)
}
/**
* Deep merge two objects.
* @param target
* @param ...sources
*/
export default function deepMerge<T, R>(target: T, source: R): T {
const output = { ...target }
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach((key) => {
if (isObject(source[key])) {
if (!(key in target)) {
Object.assign(output, { [key]: source[key] })
} else {
output[key] = deepMerge(target[key], source[key])
}
} else {
Object.assign(output, { [key]: source[key] })
}
})
}
return output
}

View File

@@ -1,80 +1,85 @@
import type { Config } from 'payload/config'
import type { CollectionConfig, Field } from 'payload/types'
import type { RedirectsPluginConfig } from './types.js'
import deepMerge from './deepMerge.js'
export const redirectsPlugin =
(pluginConfig: RedirectsPluginConfig) =>
(incomingConfig: Config): Config => ({
...incomingConfig,
collections: [
...(incomingConfig?.collections || []),
deepMerge(
{
slug: 'redirects',
access: {
read: (): boolean => true,
},
admin: {
defaultColumns: ['from', 'to.type', 'createdAt'],
},
fields: [
{
name: 'from',
type: 'text',
index: true,
label: 'From URL',
required: true,
(incomingConfig: Config): Config => {
const defaultFields: Field[] = [
{
name: 'from',
type: 'text',
index: true,
label: 'From URL',
required: true,
},
{
name: 'to',
type: 'group',
fields: [
{
name: 'type',
type: 'radio',
admin: {
layout: 'horizontal',
},
{
name: 'to',
type: 'group',
fields: [
{
name: 'type',
type: 'radio',
admin: {
layout: 'horizontal',
},
defaultValue: 'reference',
label: 'To URL Type',
options: [
{
label: 'Internal link',
value: 'reference',
},
{
label: 'Custom URL',
value: 'custom',
},
],
},
{
name: 'reference',
type: 'relationship',
admin: {
condition: (_, siblingData) => siblingData?.type === 'reference',
},
label: 'Document to redirect to',
relationTo: pluginConfig?.collections || [],
required: true,
},
{
name: 'url',
type: 'text',
admin: {
condition: (_, siblingData) => siblingData?.type === 'custom',
},
label: 'Custom URL',
required: true,
},
],
label: false,
defaultValue: 'reference',
label: 'To URL Type',
options: [
{
label: 'Internal link',
value: 'reference',
},
{
label: 'Custom URL',
value: 'custom',
},
],
},
{
name: 'reference',
type: 'relationship',
admin: {
condition: (_, siblingData) => siblingData?.type === 'reference',
},
],
},
pluginConfig?.overrides || {},
),
],
})
label: 'Document to redirect to',
relationTo: pluginConfig?.collections || [],
required: true,
},
{
name: 'url',
type: 'text',
admin: {
condition: (_, siblingData) => siblingData?.type === 'custom',
},
label: 'Custom URL',
required: true,
},
],
label: false,
},
]
const redirectsCollection: CollectionConfig = {
...(pluginConfig?.overrides || {}),
slug: pluginConfig?.overrides?.slug || 'redirects',
access: {
read: () => true,
...(pluginConfig?.overrides?.access || {}),
},
admin: {
defaultColumns: ['from', 'to.type', 'createdAt'],
...(pluginConfig?.overrides?.admin || {}),
},
fields:
pluginConfig?.overrides?.fields && typeof pluginConfig?.overrides?.fields === 'function'
? pluginConfig?.overrides.fields({ defaultFields })
: defaultFields,
}
return {
...incomingConfig,
collections: [...(incomingConfig?.collections || []), redirectsCollection],
}
}

View File

@@ -1,6 +1,8 @@
import type { CollectionConfig } from 'payload/types'
import type { CollectionConfig, Field } from 'payload/types'
export type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]
export type RedirectsPluginConfig = {
collections?: string[]
overrides?: Partial<CollectionConfig>
overrides?: Partial<Omit<CollectionConfig, 'fields'>> & { fields: FieldsOverride }
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-relationship-object-ids",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-search",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Search plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-seo",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "SEO plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-stripe",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Stripe plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-lexical",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The officially supported Lexical richtext adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {
@@ -41,8 +41,8 @@
"translateNewKeys": "tsx scripts/translateNewKeys.ts"
},
"dependencies": {
"@faceless-ui/modal": "2.1.0-rc.0",
"@faceless-ui/scroll-info": "1.4.0-rc.0",
"@faceless-ui/modal": "3.0.0-beta.0",
"@faceless-ui/scroll-info": "2.0.0-beta.0",
"@lexical/headless": "0.16.0",
"@lexical/link": "0.16.0",
"@lexical/list": "0.16.0",
@@ -75,8 +75,8 @@
"payload": "workspace:*"
},
"peerDependencies": {
"@faceless-ui/modal": "2.1.0-rc.0",
"@faceless-ui/scroll-info": "1.4.0-rc.0",
"@faceless-ui/modal": "3.0.0-beta.0",
"@faceless-ui/scroll-info": "2.0.0-beta.0",
"@lexical/headless": "0.16.0",
"@lexical/link": "0.16.0",
"@lexical/list": "0.16.0",
@@ -95,7 +95,7 @@
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,7 +1,7 @@
'use client'
import type { LexicalCommand, LexicalEditor } from 'lexical'
import * as facelessUIImport from '@faceless-ui/modal'
import { useModal } from '@faceless-ui/modal'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { formatDrawerSlug } from '@payloadcms/ui/elements/Drawer'
import { BlocksDrawer } from '@payloadcms/ui/fields/Blocks/BlocksDrawer'
@@ -54,8 +54,6 @@ const insertBlock = ({
}
export const BlocksDrawerComponent: React.FC = () => {
const { useModal } = facelessUIImport
const [editor] = useLexicalComposerContext()
const { editorConfig, uuid } = useEditorConfigContext()

View File

@@ -3,7 +3,7 @@ import type { LexicalNode } from 'lexical'
import type { FormState } from 'payload/types'
import type { Data } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import { useModal } from '@faceless-ui/modal'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { $findMatchingParent, mergeRegister } from '@lexical/utils'
import { getTranslation } from '@payloadcms/translations'
@@ -36,8 +36,6 @@ import { $isLinkNode, TOGGLE_LINK_COMMAND } from '../../../nodes/LinkNode.js'
import { TOGGLE_LINK_WITH_MODAL_COMMAND } from './commands.js'
export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.ReactNode {
const { useModal } = facelessUIImport
const [editor] = useLexicalComposerContext()
const editorRef = useRef<HTMLDivElement | null>(null)

View File

@@ -2,7 +2,7 @@
import type { FormProps } from '@payloadcms/ui/forms/Form'
import type { ClientCollectionConfig, FormState } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import { useModal } from '@faceless-ui/modal'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { getTranslation } from '@payloadcms/translations'
import { Drawer } from '@payloadcms/ui/elements/Drawer'
@@ -34,8 +34,6 @@ export const ExtraFieldsUploadDrawer: React.FC<
relatedCollection: ClientCollectionConfig
}
> = (props) => {
const { useModal } = facelessUIImport
const {
data: { fields, relationTo, value },
drawerSlug,

View File

@@ -1,5 +1,5 @@
@import 'styles';
@import './toastify.scss';
@import './toasts.scss';
@import './colors.scss';
:root {

View File

@@ -1,58 +0,0 @@
@import 'vars';
.Toastify {
.Toastify__toast-container {
left: base(5);
transform: none;
right: base(5);
width: auto;
}
.Toastify__toast {
padding: base(0.5);
border-radius: $style-radius-m;
font-weight: 600;
}
.Toastify__close-button {
align-self: center;
opacity: 0.7;
&:hover {
opacity: 1;
}
}
.Toastify__toast--success {
color: var(--color-success-900);
background: var(--color-success-500);
.Toastify__progress-bar {
background-color: var(--color-success-900);
}
}
.Toastify__close-button--success {
color: var(--color-success-900);
}
.Toastify__toast--error {
background: var(--theme-error-500);
color: #fff;
.Toastify__progress-bar {
background-color: #fff;
}
}
.Toastify__close-button--light {
color: inherit;
}
@include mid-break {
.Toastify__toast-container {
left: $baseline;
right: $baseline;
}
}
}

View File

@@ -0,0 +1,111 @@
.payload-toast-container {
.payload-toast-close-button {
left: unset;
right: 0.5rem;
top: 1.55rem;
color: var(--theme-elevation-400);
background: unset;
border: none;
display: flex;
width: 1.25rem;
height: 1.25rem;
justify-content: center;
align-items: center;
&:hover {
background: none;
}
svg {
width: 2rem;
height: 2rem;
}
[dir='RTL'] & {
right: unset;
left: 0.5rem;
}
}
.payload-toast-item {
padding: 1rem 2.5rem 1rem 1rem;
color: var(--theme-text);
font-style: normal;
font-weight: 600;
display: flex;
gap: 1rem;
align-items: center;
width: 100%;
border-radius: 0.15rem;
border: 1px solid var(--theme-border-color);
background: var(--theme-input-bg);
box-shadow:
0px 10px 4px -8px rgba(0, 2, 4, 0.02),
0px 2px 3px 0px rgba(0, 2, 4, 0.05);
.toast-content {
transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
&[data-front='false'] {
.toast-content {
opacity: 0;
}
}
&[data-expanded='true'] {
.toast-content {
opacity: 1;
}
}
.toast-icon {
svg {
width: 2.4rem;
height: 2.4rem;
}
}
&.toast-warning {
border-color: var(--theme-warning-200);
background-color: var(--theme-warning-100);
}
&.toast-error {
border-color: var(--theme-error-300);
background-color: var(--theme-error-150);
}
&.toast-success {
border-color: var(--theme-success-200);
background-color: var(--theme-success-100);
}
&.toast-info {
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
}
[data-theme='light'] & {
&.toast-warning {
border-color: var(--theme-warning-550);
background-color: var(--theme-warning-100);
}
&.toast-error {
border-color: var(--theme-error-200);
background-color: var(--theme-error-50);
}
&.toast-success {
border-color: var(--theme-success-550);
background-color: var(--theme-success-50);
}
&.toast-info {
border-color: var(--theme-border-color);
background-color: var(--theme-elevation-50);
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-slate",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "The officially supported Slate richtext adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {
@@ -32,7 +32,7 @@
"prepublishOnly": "pnpm clean && pnpm turbo build"
},
"dependencies": {
"@faceless-ui/modal": "2.1.0-rc.0",
"@faceless-ui/modal": "3.0.0-beta.0",
"is-hotkey": "0.2.0",
"slate": "0.91.4",
"slate-history": "0.86.0",
@@ -55,7 +55,7 @@
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -2,11 +2,10 @@
import type { FormState } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import { useModal } from '@faceless-ui/modal'
import { useDrawerSlug } from '@payloadcms/ui/elements/Drawer'
import { useFieldProps } from '@payloadcms/ui/forms/FieldPropsProvider'
import { useConfig } from '@payloadcms/ui/providers/Config'
import { useDocumentInfo } from '@payloadcms/ui/providers/DocumentInfo'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { getFormState } from '@payloadcms/ui/utilities/getFormState'
import { reduceFieldsToValues } from '@payloadcms/ui/utilities/reduceFieldsToValues'
@@ -61,8 +60,6 @@ const insertLink = (editor, fields) => {
}
export const LinkButton: React.FC = () => {
const { useModal } = facelessUIImport
const { fieldProps } = useElementButton()
const [initialState, setInitialState] = useState<FormState>({})

View File

@@ -2,7 +2,7 @@
import type { FormState } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import { useModal } from '@faceless-ui/modal'
import { getTranslation } from '@payloadcms/translations'
import { Button } from '@payloadcms/ui/elements/Button'
import { useDrawerSlug } from '@payloadcms/ui/elements/Drawer'
@@ -60,7 +60,6 @@ const insertChange = (editor, fields) => {
export const LinkElement = () => {
const { attributes, children, editorRef, element, fieldProps, schemaPath } =
useElement<LinkElementType>()
const { useModal } = facelessUIImport
const fieldMapPath = `${schemaPath}.${linkFieldsSchemaPath}`

View File

@@ -3,7 +3,7 @@
import type { FormFieldBase } from '@payloadcms/ui/fields/shared'
import type { ClientCollectionConfig } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import { useModal } from '@faceless-ui/modal'
import { getTranslation } from '@payloadcms/translations'
import { Drawer } from '@payloadcms/ui/elements/Drawer'
import { Form, type FormProps } from '@payloadcms/ui/forms/Form'
@@ -34,8 +34,6 @@ export const UploadDrawer: React.FC<{
relatedCollection: ClientCollectionConfig
schemaPath: string
}> = (props) => {
const { useModal } = facelessUIImport
const editor = useSlateStatic()
const { drawerSlug, element, fieldProps, relatedCollection, schemaPath } = props

View File

@@ -1,5 +1,5 @@
@import 'styles';
@import './toastify.scss';
@import './toasts.scss';
@import './colors.scss';
:root {

View File

@@ -1,58 +0,0 @@
@import 'vars';
.Toastify {
.Toastify__toast-container {
left: base(5);
transform: none;
right: base(5);
width: auto;
}
.Toastify__toast {
padding: base(0.5);
border-radius: $style-radius-m;
font-weight: 600;
}
.Toastify__close-button {
align-self: center;
opacity: 0.7;
&:hover {
opacity: 1;
}
}
.Toastify__toast--success {
color: var(--color-success-900);
background: var(--color-success-500);
.Toastify__progress-bar {
background-color: var(--color-success-900);
}
}
.Toastify__close-button--success {
color: var(--color-success-900);
}
.Toastify__toast--error {
background: var(--theme-error-500);
color: #fff;
.Toastify__progress-bar {
background-color: #fff;
}
}
.Toastify__close-button--light {
color: inherit;
}
@include mid-break {
.Toastify__toast-container {
left: $baseline;
right: $baseline;
}
}
}

View File

@@ -0,0 +1,111 @@
.payload-toast-container {
.payload-toast-close-button {
left: unset;
right: 0.5rem;
top: 1.55rem;
color: var(--theme-elevation-400);
background: unset;
border: none;
display: flex;
width: 1.25rem;
height: 1.25rem;
justify-content: center;
align-items: center;
&:hover {
background: none;
}
svg {
width: 2rem;
height: 2rem;
}
[dir='RTL'] & {
right: unset;
left: 0.5rem;
}
}
.payload-toast-item {
padding: 1rem 2.5rem 1rem 1rem;
color: var(--theme-text);
font-style: normal;
font-weight: 600;
display: flex;
gap: 1rem;
align-items: center;
width: 100%;
border-radius: 0.15rem;
border: 1px solid var(--theme-border-color);
background: var(--theme-input-bg);
box-shadow:
0px 10px 4px -8px rgba(0, 2, 4, 0.02),
0px 2px 3px 0px rgba(0, 2, 4, 0.05);
.toast-content {
transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
&[data-front='false'] {
.toast-content {
opacity: 0;
}
}
&[data-expanded='true'] {
.toast-content {
opacity: 1;
}
}
.toast-icon {
svg {
width: 2.4rem;
height: 2.4rem;
}
}
&.toast-warning {
border-color: var(--theme-warning-200);
background-color: var(--theme-warning-100);
}
&.toast-error {
border-color: var(--theme-error-300);
background-color: var(--theme-error-150);
}
&.toast-success {
border-color: var(--theme-success-200);
background-color: var(--theme-success-100);
}
&.toast-info {
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
}
[data-theme='light'] & {
&.toast-warning {
border-color: var(--theme-warning-550);
background-color: var(--theme-warning-100);
}
&.toast-error {
border-color: var(--theme-error-200);
background-color: var(--theme-error-50);
}
&.toast-success {
border-color: var(--theme-success-550);
background-color: var(--theme-success-50);
}
&.toast-info {
border-color: var(--theme-border-color);
background-color: var(--theme-elevation-50);
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-azure",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload storage adapter for Azure Blob Storage",
"homepage": "https://payloadcms.com",
"repository": {
@@ -45,7 +45,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-gcs",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload storage adapter for Google Cloud Storage",
"homepage": "https://payloadcms.com",
"repository": {
@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-s3",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload storage adapter for Amazon S3",
"homepage": "https://payloadcms.com",
"repository": {
@@ -43,7 +43,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-uploadthing",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload storage adapter for uploadthing",
"homepage": "https://payloadcms.com",
"repository": {
@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-vercel-blob",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"description": "Payload storage adapter for Vercel Blob Storage",
"homepage": "https://payloadcms.com",
"repository": {
@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/translations",
"version": "3.0.0-beta.42",
"version": "3.0.0-beta.45",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -213,6 +213,7 @@ export const clientTranslationKeys = createClientTranslationKeys([
'general:stayOnThisPage',
'general:submissionSuccessful',
'general:submit',
'general:submitting',
'general:success',
'general:successfullyCreated',
'general:successfullyDuplicated',

View File

@@ -271,6 +271,7 @@ export const arTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'البقاء على هذه الصفحة',
submissionSuccessful: 'تمت الإرسال بنجاح.',
submit: 'إرسال',
submitting: 'جاري التقديم...',
success: 'النجاح',
successfullyCreated: '{{label}} تم إنشاؤها بنجاح.',
successfullyDuplicated: '{{label}} تم استنساخها بنجاح.',

View File

@@ -273,6 +273,7 @@ export const azTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'Bu səhifədə qal',
submissionSuccessful: 'Təqdimat uğurlu oldu.',
submit: 'Təqdim et',
submitting: 'Təqdim olunur...',
success: 'Uğur',
successfullyCreated: '{{label}} uğurla yaradıldı.',
successfullyDuplicated: '{{label}} uğurla dublikatlandı.',

View File

@@ -272,6 +272,7 @@ export const bgTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'Остани на тази страница',
submissionSuccessful: 'Успешно подаване.',
submit: 'Подай',
submitting: 'Подаване...',
success: 'Успех',
successfullyCreated: '{{label}} успешно създаден.',
successfullyDuplicated: '{{label}} успешно дупликиран.',

View File

@@ -272,6 +272,7 @@ export const csTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'Zůstat na této stránce',
submissionSuccessful: 'Odeslání úspěšné.',
submit: 'Odeslat',
submitting: 'Odesílání...',
success: 'Úspěch',
successfullyCreated: '{{label}} úspěšně vytvořeno.',
successfullyDuplicated: '{{label}} úspěšně duplikováno.',

View File

@@ -235,8 +235,8 @@ export const deTranslations: DefaultTranslationsObject = {
light: 'Hell',
livePreview: 'Vorschau',
loading: 'Lädt',
locale: 'Sprachumgebung',
locales: 'Sprachumgebungen',
locale: 'Sprache',
locales: 'Sprachen',
menu: 'Menü',
moveDown: 'Nach unten bewegen',
moveUp: 'Nach oben bewegen',
@@ -276,6 +276,7 @@ export const deTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'Auf dieser Seite bleiben',
submissionSuccessful: 'Einrichung erfolgreich.',
submit: 'Senden',
submitting: 'Wird aktualisiert...',
success: 'Erfolg',
successfullyCreated: '{{label}} erfolgreich erstellt.',
successfullyDuplicated: '{{label}} wurde erfolgreich dupliziert.',
@@ -389,13 +390,13 @@ export const deTranslations: DefaultTranslationsObject = {
publishing: 'Veröffentlichung',
restoreThisVersion: 'Diese Version wiederherstellen',
restoredSuccessfully: 'Erfolgreich wiederhergestellt.',
restoring: 'wiederherstellen...',
revertToPublished: 'Auf Veröffentlicht zurücksetzen',
reverting: 'zurücksetzen...',
restoring: 'Wiederherstellen...',
revertToPublished: 'Auf veröffentlichte Version zurücksetzen',
reverting: 'Zurücksetzen...',
saveDraft: 'Entwurf speichern',
selectLocales: 'Wähle anzuzeigende Sprachumgebungen',
selectLocales: 'Wähle anzuzeigende Sprachen',
selectVersionToCompare: 'Wähle Version zum Vergleich',
showLocales: 'Sprachumgebungen anzeigen:',
showLocales: 'Sprachen anzeigen:',
showingVersionsFor: 'Versionen anzeigen für:',
status: 'Status',
unpublish: 'Auf Entwurf setzen',

View File

@@ -273,6 +273,7 @@ export const enTranslations = {
stayOnThisPage: 'Stay on this page',
submissionSuccessful: 'Submission Successful.',
submit: 'Submit',
submitting: 'Submitting...',
success: 'Success',
successfullyCreated: '{{label}} successfully created.',
successfullyDuplicated: '{{label}} successfully duplicated.',

View File

@@ -275,6 +275,7 @@ export const esTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'Permanecer en esta página',
submissionSuccessful: 'Envío realizado correctamente.',
submit: 'Enviar',
submitting: 'Enviando...',
success: 'Éxito',
successfullyCreated: '{{label}} creado correctamente.',
successfullyDuplicated: '{{label}} duplicado correctamente.',

View File

@@ -272,6 +272,7 @@ export const faTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'ماندن در این برگه',
submissionSuccessful: 'با موفقیت ثبت شد.',
submit: 'فرستادن',
submitting: 'در حال ارسال...',
success: 'موفقیت',
successfullyCreated: '{{label}} با موفقیت ساخته شد.',
successfullyDuplicated: '{{label}} با موفقیت رونوشت شد.',

View File

@@ -279,6 +279,7 @@ export const frTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'Rester sur cette page',
submissionSuccessful: 'Soumission réussie.',
submit: 'Soumettre',
submitting: 'Soumission...',
success: 'Succès',
successfullyCreated: '{{label}} créé(e) avec succès.',
successfullyDuplicated: '{{label}} dupliqué(e) avec succès.',

View File

@@ -267,6 +267,7 @@ export const heTranslations: DefaultTranslationsObject = {
stayOnThisPage: 'הישאר בדף זה',
submissionSuccessful: 'נשלח בהצלחה.',
submit: 'שלח',
submitting: 'מגיש...',
success: 'הצלחה',
successfullyCreated: '{{label}} נוצר בהצלחה.',
successfullyDuplicated: '{{label}} שוכפל בהצלחה.',

Some files were not shown because too many files have changed in this diff Show More