Compare commits
25 Commits
v3.0.0-bet
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fb14cfebe | ||
|
|
2ada6fc58d | ||
|
|
cb3355b30f | ||
|
|
10c6ffafc3 | ||
|
|
6512d5ce69 | ||
|
|
57fcc9148e | ||
|
|
36f4f23463 | ||
|
|
7b7dc71845 | ||
|
|
ba513d5a97 | ||
|
|
a26d03190e | ||
|
|
9f525621c8 | ||
|
|
7309d474ee | ||
|
|
45e86832c2 | ||
|
|
1bd91b23ca | ||
|
|
ac34380eb8 | ||
|
|
17707852e0 | ||
|
|
8b95218577 | ||
|
|
a79d23c631 | ||
|
|
52c81ad525 | ||
|
|
8ec836737e | ||
|
|
e4a90294ea | ||
|
|
7c8d562f03 | ||
|
|
11c3a65e63 | ||
|
|
8dd5e4dc24 | ||
|
|
9bd9e7a986 |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -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}",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ export default withBundleAnalyzer(
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
}
|
||||
|
||||
return webpackConfig
|
||||
},
|
||||
}),
|
||||
|
||||
16
package.json
16
package.json
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
1
packages/graphql/src/exports/types.ts
Normal file
1
packages/graphql/src/exports/types.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { GraphQLJSON, GraphQLJSONObject } from '../packages/graphql-type-json/index.js'
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import './styles.scss';
|
||||
@import './toastify.scss';
|
||||
@import './toasts.scss';
|
||||
@import './colors.scss';
|
||||
|
||||
:root {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
packages/next/src/scss/toasts.scss
Normal file
111
packages/next/src/scss/toasts.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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: {},
|
||||
|
||||
@@ -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'))
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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() : []
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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}`)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,6 +221,7 @@ const collectionSchema = joi.object().keys({
|
||||
interval: joi.number(),
|
||||
}),
|
||||
),
|
||||
validate: joi.boolean(),
|
||||
}),
|
||||
joi.boolean(),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -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: {},
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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(),
|
||||
}),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ const globalSchema = joi
|
||||
interval: joi.number(),
|
||||
}),
|
||||
),
|
||||
validate: joi.boolean(),
|
||||
}),
|
||||
joi.boolean(),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
3
packages/payload/src/versions/defaults.ts
Normal file
3
packages/payload/src/versions/defaults.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const versionDefaults = {
|
||||
autosaveInterval: 2000,
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export const enforceMaxVersions = async ({
|
||||
}
|
||||
|
||||
await payload.db.deleteVersions({
|
||||
collection: collection?.slug,
|
||||
collection: slug,
|
||||
req,
|
||||
where: deleteQuery,
|
||||
})
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import 'styles';
|
||||
@import './toastify.scss';
|
||||
@import './toasts.scss';
|
||||
@import './colors.scss';
|
||||
|
||||
:root {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
packages/richtext-lexical/src/scss/toasts.scss
Normal file
111
packages/richtext-lexical/src/scss/toasts.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
@@ -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>({})
|
||||
|
||||
|
||||
@@ -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}`
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import 'styles';
|
||||
@import './toastify.scss';
|
||||
@import './toasts.scss';
|
||||
@import './colors.scss';
|
||||
|
||||
:root {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
packages/richtext-slate/src/scss/toasts.scss
Normal file
111
packages/richtext-slate/src/scss/toasts.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -213,6 +213,7 @@ export const clientTranslationKeys = createClientTranslationKeys([
|
||||
'general:stayOnThisPage',
|
||||
'general:submissionSuccessful',
|
||||
'general:submit',
|
||||
'general:submitting',
|
||||
'general:success',
|
||||
'general:successfullyCreated',
|
||||
'general:successfullyDuplicated',
|
||||
|
||||
@@ -271,6 +271,7 @@ export const arTranslations: DefaultTranslationsObject = {
|
||||
stayOnThisPage: 'البقاء على هذه الصفحة',
|
||||
submissionSuccessful: 'تمت الإرسال بنجاح.',
|
||||
submit: 'إرسال',
|
||||
submitting: 'جاري التقديم...',
|
||||
success: 'النجاح',
|
||||
successfullyCreated: '{{label}} تم إنشاؤها بنجاح.',
|
||||
successfullyDuplicated: '{{label}} تم استنساخها بنجاح.',
|
||||
|
||||
@@ -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ı.',
|
||||
|
||||
@@ -272,6 +272,7 @@ export const bgTranslations: DefaultTranslationsObject = {
|
||||
stayOnThisPage: 'Остани на тази страница',
|
||||
submissionSuccessful: 'Успешно подаване.',
|
||||
submit: 'Подай',
|
||||
submitting: 'Подаване...',
|
||||
success: 'Успех',
|
||||
successfullyCreated: '{{label}} успешно създаден.',
|
||||
successfullyDuplicated: '{{label}} успешно дупликиран.',
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -272,6 +272,7 @@ export const faTranslations: DefaultTranslationsObject = {
|
||||
stayOnThisPage: 'ماندن در این برگه',
|
||||
submissionSuccessful: 'با موفقیت ثبت شد.',
|
||||
submit: 'فرستادن',
|
||||
submitting: 'در حال ارسال...',
|
||||
success: 'موفقیت',
|
||||
successfullyCreated: '{{label}} با موفقیت ساخته شد.',
|
||||
successfullyDuplicated: '{{label}} با موفقیت رونوشت شد.',
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user