Compare commits
32 Commits
v3.0.0-bet
...
chore/v3-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ab1c336b | ||
|
|
c974256f15 | ||
|
|
db613f35b2 | ||
|
|
2dd162269a | ||
|
|
9c13089a2f | ||
|
|
b642cb2d93 | ||
|
|
9e5d521567 | ||
|
|
6eabc99e01 | ||
|
|
ea917dd811 | ||
|
|
070d8e1731 | ||
|
|
664c60d2bc | ||
|
|
e25814e1ee | ||
|
|
27ea117731 | ||
|
|
7ab156e117 | ||
|
|
f2d415663f | ||
|
|
bdf08a19d1 | ||
|
|
cb90e9f622 | ||
|
|
b05a5e1fb6 | ||
|
|
92a5da1006 | ||
|
|
75a95469b2 | ||
|
|
c0ae287d46 | ||
|
|
a2b92aa3ff | ||
|
|
544a2285d3 | ||
|
|
8c39950ea3 | ||
|
|
6d642fe9b9 | ||
|
|
f175a741bc | ||
|
|
3290376f80 | ||
|
|
2b5c1ba99a | ||
|
|
bcb3f08386 | ||
|
|
b729b9bebd | ||
|
|
26ee91eb48 | ||
|
|
43a17f67a0 |
42
.github/workflows/main.yml
vendored
42
.github/workflows/main.yml
vendored
@@ -13,6 +13,8 @@ concurrency:
|
||||
env:
|
||||
NODE_VERSION: 18.20.2
|
||||
PNPM_VERSION: 8.15.7
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -89,6 +91,8 @@ jobs:
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:all
|
||||
env:
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
@@ -253,7 +257,8 @@ jobs:
|
||||
- plugin-seo
|
||||
- versions
|
||||
- uploads
|
||||
|
||||
env:
|
||||
SUITE_NAME: ${{ matrix.suite }}
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
@@ -281,11 +286,33 @@ jobs:
|
||||
run: pnpm docker:start
|
||||
if: ${{ matrix.suite == 'plugin-cloud-storage' }}
|
||||
|
||||
- name: Install Playwright
|
||||
run: pnpm exec playwright install --with-deps
|
||||
- name: Store Playwright's Version
|
||||
run: |
|
||||
# Extract the version number using a more targeted regex pattern with awk
|
||||
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --depth=0 | awk '/@playwright\/test/ {print $2}')
|
||||
echo "Playwright's Version: $PLAYWRIGHT_VERSION"
|
||||
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Cache Playwright Browsers for Playwright's Version
|
||||
id: cache-playwright-browsers
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-browsers-${{ env.PLAYWRIGHT_VERSION }}
|
||||
|
||||
- name: Setup Playwright - Browsers and Dependencies
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
|
||||
run: pnpm exec playwright install --with-deps chromium
|
||||
|
||||
- name: Setup Playwright - Dependencies-only
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
|
||||
- name: E2E Tests
|
||||
run: pnpm test:e2e ${{ matrix.suite }}
|
||||
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e ${{ matrix.suite }}
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
@@ -295,6 +322,13 @@ jobs:
|
||||
if-no-files-found: ignore
|
||||
retention-days: 1
|
||||
|
||||
# Disabled until this is fixed: https://github.com/daun/playwright-report-summary/issues/156
|
||||
# - uses: daun/playwright-report-summary@v3
|
||||
# with:
|
||||
# report-file: results_${{ matrix.suite }}.json
|
||||
# report-tag: ${{ matrix.suite }}
|
||||
# job-summary: true
|
||||
|
||||
app-build-with-packed:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
2
.github/workflows/pr-title.yml
vendored
2
.github/workflows/pr-title.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
translations
|
||||
ui
|
||||
templates
|
||||
examples
|
||||
examples(\/(\w|-)+)?
|
||||
deps
|
||||
|
||||
# Disallow uppercase letters at the beginning of the subject
|
||||
|
||||
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -58,6 +58,13 @@
|
||||
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js collections-graphql",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev GraphQL",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
@@ -49,7 +49,8 @@ export default buildConfig({
|
||||
{
|
||||
label: 'Arabic',
|
||||
code: 'ar',
|
||||
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
|
||||
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
|
||||
// when current locale is rtl
|
||||
rtl: true,
|
||||
},
|
||||
],
|
||||
@@ -134,13 +135,9 @@ to support localization, you need to specify each field that you would like to l
|
||||
```js
|
||||
{
|
||||
name: 'title',
|
||||
type
|
||||
:
|
||||
'text',
|
||||
// highlight-start
|
||||
localized
|
||||
:
|
||||
true,
|
||||
type: 'text',
|
||||
// highlight-start
|
||||
localized: true,
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
@@ -20,7 +20,8 @@ The initial request made to Payload will begin a new transaction and attach it t
|
||||
|
||||
```ts
|
||||
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
// because req.transactionID is assigned from Payload and passed through, my-slug will only persist if the entire request is successful
|
||||
// because req.transactionID is assigned from Payload and passed through,
|
||||
// my-slug will only persist if the entire request is successful
|
||||
await req.payload.create({
|
||||
req,
|
||||
collection: 'my-slug',
|
||||
|
||||
@@ -196,7 +196,8 @@ const Pages: CollectionConfig = {
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
// The HTMLConverter Feature is the feature which manages the HTML serializers. If you do not pass any arguments to it, it will use the default serializers.
|
||||
// The HTMLConverter Feature is the feature which manages the HTML serializers.
|
||||
// If you do not pass any arguments to it, it will use the default serializers.
|
||||
HTMLConverterFeature({}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -107,7 +107,7 @@
|
||||
"copyfiles": "2.4.1",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "8.6.0",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-kit": "0.20.18-08d50a4",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"escape-html": "^1.0.3",
|
||||
"eslint-plugin-payload": "workspace:*",
|
||||
@@ -154,7 +154,7 @@
|
||||
"tempy": "^1.0.1",
|
||||
"ts-node": "10.9.1",
|
||||
"tsx": "^4.7.1",
|
||||
"turbo": "^1.13.2",
|
||||
"turbo": "^1.13.3",
|
||||
"typescript": "5.4.5",
|
||||
"uuid": "^9.0.1",
|
||||
"yocto-queue": "^1.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"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.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -43,7 +43,7 @@
|
||||
"dependencies": {
|
||||
"@libsql/client": "^0.5.2",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-kit": "0.20.18-08d50a4",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"pg": "8.11.3",
|
||||
"prompts": "2.4.2",
|
||||
|
||||
@@ -3,13 +3,10 @@ import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { CreateMigration } from 'payload/database'
|
||||
|
||||
import fs from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
const migrationTemplate = (
|
||||
upSQL?: string,
|
||||
downSQL?: string,
|
||||
@@ -45,12 +42,12 @@ const getDefaultDrizzleSnapshot = (): DrizzleSnapshotJSON => ({
|
||||
schemas: {},
|
||||
tables: {},
|
||||
},
|
||||
dialect: 'pg',
|
||||
dialect: 'postgresql',
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
schemas: {},
|
||||
tables: {},
|
||||
version: '5',
|
||||
version: '6',
|
||||
})
|
||||
|
||||
export const createMigration: CreateMigration = async function createMigration(
|
||||
@@ -62,7 +59,9 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
fs.mkdirSync(dir)
|
||||
}
|
||||
|
||||
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
|
||||
const { generateDrizzleJson, generateMigration, upPgSnapshot } = await import(
|
||||
'drizzle-kit/payload'
|
||||
)
|
||||
|
||||
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
|
||||
const formattedDate = yyymmdd.replace(/\D/g, '')
|
||||
@@ -86,11 +85,15 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
.reverse()?.[0]
|
||||
|
||||
if (latestSnapshot) {
|
||||
const latestSnapshotJSON = JSON.parse(
|
||||
const latestSnapshotJSON: DrizzleSnapshotJSON = JSON.parse(
|
||||
fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'),
|
||||
) as DrizzleSnapshotJSON
|
||||
|
||||
drizzleJsonBefore = latestSnapshotJSON
|
||||
if (latestSnapshotJSON.version < drizzleJsonBefore.version) {
|
||||
drizzleJsonBefore = upPgSnapshot(latestSnapshotJSON)
|
||||
} else {
|
||||
drizzleJsonBefore = latestSnapshotJSON
|
||||
}
|
||||
}
|
||||
|
||||
const drizzleJsonAfter = generateDrizzleJson(this.schema)
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { Payload } from 'payload'
|
||||
import type { Migration } from 'payload/database'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
|
||||
import { createRequire } from 'module'
|
||||
import {
|
||||
commitTransaction,
|
||||
initTransaction,
|
||||
@@ -18,8 +17,6 @@ import { createMigrationTable } from './utilities/createMigrationTable.js'
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
@@ -85,7 +82,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
}
|
||||
|
||||
async function runMigrationFile(payload: Payload, migration: Migration, batch: number) {
|
||||
const { generateDrizzleJson } = require('drizzle-kit/payload')
|
||||
const { generateDrizzleJson } = await import('drizzle-kit/payload')
|
||||
|
||||
const start = Date.now()
|
||||
const req = { payload } as PayloadRequestWithData
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { numeric, timestamp, varchar } from 'drizzle-orm/pg-core'
|
||||
import { createRequire } from 'module'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
/**
|
||||
* Pushes the development schema to the database using Drizzle.
|
||||
*
|
||||
@@ -14,7 +11,7 @@ const require = createRequire(import.meta.url)
|
||||
* @returns {Promise<void>} - A promise that resolves once the schema push is complete.
|
||||
*/
|
||||
export const pushDevSchema = async (db: PostgresAdapter) => {
|
||||
const { pushSchema } = require('drizzle-kit/payload')
|
||||
const { pushSchema } = await import('drizzle-kit/payload')
|
||||
|
||||
// This will prompt if clarifications are needed for Drizzle to push new schema
|
||||
const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -29,14 +29,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"graphql-scalars": "1.22.2",
|
||||
"pluralize": "8.0.0"
|
||||
"pluralize": "8.0.0",
|
||||
"ts-essentials": "7.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/pluralize": "^0.0.33",
|
||||
"graphql-http": "^1.22.0",
|
||||
"payload": "workspace:*",
|
||||
"ts-essentials": "7.0.3"
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^16.8.1",
|
||||
|
||||
@@ -28,6 +28,7 @@ export default function countResolver(collection: Collection): Resolver {
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function getDeleteResolver<TSlug extends keyof GeneratedTypes['co
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function duplicateResolver<T extends keyof GeneratedTypes['collec
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -34,6 +34,7 @@ export default function findResolver(collection: Collection): Resolver {
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
|
||||
@@ -31,6 +31,7 @@ export default function findByIDResolver<T extends keyof GeneratedTypes['collect
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function findVersionByIDResolver(collection: Collection): Resolve
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -31,6 +31,7 @@ export default function findVersionsResolver(collection: Collection): Resolver {
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
|
||||
@@ -32,6 +32,7 @@ export default function updateResolver<TSlug extends keyof GeneratedTypes['colle
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -5,3 +5,4 @@ export { createPayloadRequest } from '../utilities/createPayloadRequest.js'
|
||||
export { getNextRequestI18n } from '../utilities/getNextRequestI18n.js'
|
||||
export { getPayloadHMR, reload } from '../utilities/getPayloadHMR.js'
|
||||
export { headersWithCors } from '../utilities/headersWithCors.js'
|
||||
export { initPage } from '../utilities/initPage/index.js'
|
||||
|
||||
@@ -218,6 +218,8 @@ export const buildFormState = async ({ req }: { req: PayloadRequestWithData }) =
|
||||
!req.payload.collections[collectionSlug].config.auth.disableLocalStrategy
|
||||
) {
|
||||
if (formState.password) result.password = formState.password
|
||||
if (formState['confirm-password'])
|
||||
result['confirm-password'] = formState['confirm-password']
|
||||
if (formState.email) result.email = formState.email
|
||||
}
|
||||
}
|
||||
|
||||
59
packages/next/src/utilities/initPage/handleAdminPage.ts
Normal file
59
packages/next/src/utilities/initPage/handleAdminPage.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { Permissions } from 'payload/auth'
|
||||
import type {
|
||||
SanitizedCollectionConfig,
|
||||
SanitizedConfig,
|
||||
SanitizedGlobalConfig,
|
||||
} from 'payload/types'
|
||||
|
||||
import { notFound } from 'next/navigation.js'
|
||||
|
||||
import { isAdminAuthRoute, isAdminRoute } from './shared.js'
|
||||
|
||||
export const handleAdminPage = ({
|
||||
adminRoute,
|
||||
config,
|
||||
permissions,
|
||||
route,
|
||||
}: {
|
||||
adminRoute: string
|
||||
config: SanitizedConfig
|
||||
permissions: Permissions
|
||||
route: string
|
||||
}) => {
|
||||
if (isAdminRoute(route, adminRoute)) {
|
||||
const routeSegments = route.replace(adminRoute, '').split('/').filter(Boolean)
|
||||
const [entityType, entitySlug, createOrID] = routeSegments
|
||||
const collectionSlug = entityType === 'collections' ? entitySlug : undefined
|
||||
const globalSlug = entityType === 'globals' ? entitySlug : undefined
|
||||
const docID = collectionSlug && createOrID !== 'create' ? createOrID : undefined
|
||||
|
||||
let collectionConfig: SanitizedCollectionConfig | undefined
|
||||
let globalConfig: SanitizedGlobalConfig | undefined
|
||||
|
||||
if (collectionSlug) {
|
||||
collectionConfig = config.collections.find((collection) => collection.slug === collectionSlug)
|
||||
|
||||
if (!collectionConfig) {
|
||||
notFound()
|
||||
}
|
||||
}
|
||||
|
||||
if (globalSlug) {
|
||||
globalConfig = config.globals.find((global) => global.slug === globalSlug)
|
||||
|
||||
if (!globalConfig) {
|
||||
notFound()
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissions.canAccessAdmin && !isAdminAuthRoute(route, adminRoute)) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return {
|
||||
collectionConfig,
|
||||
docID,
|
||||
globalConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
47
packages/next/src/utilities/initPage/handleAuthRedirect.ts
Normal file
47
packages/next/src/utilities/initPage/handleAuthRedirect.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { redirect } from 'next/navigation.js'
|
||||
import QueryString from 'qs'
|
||||
|
||||
import { isAdminAuthRoute, isAdminRoute } from './shared.js'
|
||||
|
||||
export const handleAuthRedirect = ({
|
||||
adminRoute,
|
||||
redirectUnauthenticatedUser,
|
||||
route,
|
||||
searchParams,
|
||||
}: {
|
||||
adminRoute: string
|
||||
redirectUnauthenticatedUser: boolean | string
|
||||
route: string
|
||||
searchParams: { [key: string]: string | string[] }
|
||||
}) => {
|
||||
if (!isAdminAuthRoute(route, adminRoute)) {
|
||||
if (searchParams && 'redirect' in searchParams) delete searchParams.redirect
|
||||
|
||||
const redirectRoute = encodeURIComponent(
|
||||
route + Object.keys(searchParams ?? {}).length
|
||||
? `${QueryString.stringify(searchParams, { addQueryPrefix: true })}`
|
||||
: undefined,
|
||||
)
|
||||
|
||||
const adminLoginRoute = `${adminRoute}/login`
|
||||
|
||||
const customLoginRoute =
|
||||
typeof redirectUnauthenticatedUser === 'string' ? redirectUnauthenticatedUser : undefined
|
||||
|
||||
const loginRoute = isAdminRoute(route, adminRoute)
|
||||
? adminLoginRoute
|
||||
: customLoginRoute || '/login'
|
||||
|
||||
const parsedLoginRouteSearchParams = QueryString.parse(loginRoute.split('?')[1] ?? '')
|
||||
|
||||
const searchParamsWithRedirect = `${QueryString.stringify(
|
||||
{
|
||||
...parsedLoginRouteSearchParams,
|
||||
...(redirectRoute ? { redirect: redirectRoute } : {}),
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)}`
|
||||
|
||||
redirect(`${loginRoute.split('?')[0]}${searchParamsWithRedirect}`)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,18 @@
|
||||
import type {
|
||||
InitPageResult,
|
||||
PayloadRequestWithData,
|
||||
SanitizedCollectionConfig,
|
||||
SanitizedConfig,
|
||||
SanitizedGlobalConfig,
|
||||
VisibleEntities,
|
||||
} from 'payload/types'
|
||||
import type { InitPageResult, PayloadRequestWithData, VisibleEntities } from 'payload/types'
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
import { findLocaleFromCode } from '@payloadcms/ui/utilities/findLocaleFromCode'
|
||||
import { headers as getHeaders } from 'next/headers.js'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import { parseCookies } from 'payload/auth'
|
||||
import { createLocalReq, isEntityHidden } from 'payload/utilities'
|
||||
import qs from 'qs'
|
||||
|
||||
import { getPayloadHMR } from '../utilities/getPayloadHMR.js'
|
||||
import { getRequestLanguage } from './getRequestLanguage.js'
|
||||
import type { Args } from './types.js'
|
||||
|
||||
type Args = {
|
||||
config: Promise<SanitizedConfig> | SanitizedConfig
|
||||
redirectUnauthenticatedUser?: boolean
|
||||
route: string
|
||||
searchParams: { [key: string]: string | string[] | undefined }
|
||||
}
|
||||
|
||||
const authRoutes = [
|
||||
'/login',
|
||||
'/logout',
|
||||
'/create-first-user',
|
||||
'/forgot',
|
||||
'/reset',
|
||||
'/verify',
|
||||
'/logout-inactivity',
|
||||
]
|
||||
import { getPayloadHMR } from '../getPayloadHMR.js'
|
||||
import { getRequestLanguage } from '../getRequestLanguage.js'
|
||||
import { handleAdminPage } from './handleAdminPage.js'
|
||||
import { handleAuthRedirect } from './handleAuthRedirect.js'
|
||||
|
||||
export const initPage = async ({
|
||||
config: configPromise,
|
||||
@@ -44,7 +23,14 @@ export const initPage = async ({
|
||||
const headers = getHeaders()
|
||||
const localeParam = searchParams?.locale as string
|
||||
const payload = await getPayloadHMR({ config: configPromise })
|
||||
const { collections, globals, localization, routes } = payload.config
|
||||
|
||||
const {
|
||||
collections,
|
||||
globals,
|
||||
i18n: i18nConfig,
|
||||
localization,
|
||||
routes: { admin: adminRoute },
|
||||
} = payload.config
|
||||
|
||||
const queryString = `${qs.stringify(searchParams ?? {}, { addQueryPrefix: true })}`
|
||||
const defaultLocale =
|
||||
@@ -55,7 +41,7 @@ export const initPage = async ({
|
||||
const language = getRequestLanguage({ config: payload.config, cookies, headers })
|
||||
|
||||
const i18n = await initI18n({
|
||||
config: payload.config.i18n,
|
||||
config: i18nConfig,
|
||||
context: 'client',
|
||||
language,
|
||||
})
|
||||
@@ -81,58 +67,29 @@ export const initPage = async ({
|
||||
req.user = user
|
||||
|
||||
const visibleEntities: VisibleEntities = {
|
||||
collections: payload.config.collections
|
||||
collections: collections
|
||||
.map(({ slug, admin: { hidden } }) => (!isEntityHidden({ hidden, user }) ? slug : null))
|
||||
.filter(Boolean),
|
||||
globals: payload.config.globals
|
||||
globals: globals
|
||||
.map(({ slug, admin: { hidden } }) => (!isEntityHidden({ hidden, user }) ? slug : null))
|
||||
.filter(Boolean),
|
||||
}
|
||||
|
||||
const {
|
||||
routes: { admin: adminRoute },
|
||||
} = payload.config
|
||||
|
||||
const routeSegments = route.replace(adminRoute, '').split('/').filter(Boolean)
|
||||
const [entityType, entitySlug, createOrID] = routeSegments
|
||||
const collectionSlug = entityType === 'collections' ? entitySlug : undefined
|
||||
const globalSlug = entityType === 'globals' ? entitySlug : undefined
|
||||
const docID = collectionSlug && createOrID !== 'create' ? createOrID : undefined
|
||||
|
||||
const isAuthRoute = authRoutes.some((r) => r === route.replace(adminRoute, ''))
|
||||
|
||||
if (redirectUnauthenticatedUser && !user && !isAuthRoute) {
|
||||
if (searchParams && 'redirect' in searchParams) delete searchParams.redirect
|
||||
|
||||
const stringifiedSearchParams = Object.keys(searchParams ?? {}).length
|
||||
? `?${qs.stringify(searchParams)}`
|
||||
: ''
|
||||
|
||||
redirect(`${routes.admin}/login?redirect=${route + stringifiedSearchParams}`)
|
||||
if (redirectUnauthenticatedUser && !user) {
|
||||
handleAuthRedirect({
|
||||
adminRoute,
|
||||
redirectUnauthenticatedUser,
|
||||
route,
|
||||
searchParams,
|
||||
})
|
||||
}
|
||||
|
||||
if (!permissions.canAccessAdmin && !isAuthRoute) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
let collectionConfig: SanitizedCollectionConfig
|
||||
let globalConfig: SanitizedGlobalConfig
|
||||
|
||||
if (collectionSlug) {
|
||||
collectionConfig = collections.find((collection) => collection.slug === collectionSlug)
|
||||
|
||||
if (!collectionConfig) {
|
||||
notFound()
|
||||
}
|
||||
}
|
||||
|
||||
if (globalSlug) {
|
||||
globalConfig = globals.find((global) => global.slug === globalSlug)
|
||||
|
||||
if (!globalConfig) {
|
||||
notFound()
|
||||
}
|
||||
}
|
||||
const { collectionConfig, docID, globalConfig } = handleAdminPage({
|
||||
adminRoute,
|
||||
config: payload.config,
|
||||
permissions,
|
||||
route,
|
||||
})
|
||||
|
||||
return {
|
||||
collectionConfig,
|
||||
17
packages/next/src/utilities/initPage/shared.ts
Normal file
17
packages/next/src/utilities/initPage/shared.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export const authRoutes = [
|
||||
'/login',
|
||||
'/logout',
|
||||
'/create-first-user',
|
||||
'/forgot',
|
||||
'/reset',
|
||||
'/verify',
|
||||
'/logout-inactivity',
|
||||
]
|
||||
|
||||
export const isAdminRoute = (route: string, adminRoute: string) => {
|
||||
return route.startsWith(adminRoute)
|
||||
}
|
||||
|
||||
export const isAdminAuthRoute = (route: string, adminRoute: string) => {
|
||||
return authRoutes.some((r) => route.replace(adminRoute, '').startsWith(r))
|
||||
}
|
||||
22
packages/next/src/utilities/initPage/types.ts
Normal file
22
packages/next/src/utilities/initPage/types.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
export type Args = {
|
||||
/**
|
||||
* Your sanitized Payload config.
|
||||
* If unresolved, this function will await the promise.
|
||||
*/
|
||||
config: Promise<SanitizedConfig> | SanitizedConfig
|
||||
/**
|
||||
* If true, redirects unauthenticated users to the admin login page.
|
||||
* If a string is provided, the user will be redirected to that specific URL.
|
||||
*/
|
||||
redirectUnauthenticatedUser?: boolean | string
|
||||
/**
|
||||
* The current route, i.e. `/admin/collections/posts`.
|
||||
*/
|
||||
route: string
|
||||
/**
|
||||
* The search parameters of the current route provided to all pages in Next.js.
|
||||
*/
|
||||
searchParams: { [key: string]: string | string[] | undefined }
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { getNextRequestI18n } from '../../utilities/getNextRequestI18n.js'
|
||||
import { initPage } from '../../utilities/initPage.js'
|
||||
import { initPage } from '../../utilities/initPage/index.js'
|
||||
import { NotFoundClient } from './index.client.js'
|
||||
|
||||
export const generatePageMetadata = async ({
|
||||
@@ -46,10 +46,13 @@ export const NotFoundPage = async ({
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
}) => {
|
||||
const config = await configPromise
|
||||
const { routes: { admin: adminRoute } = {} } = config
|
||||
|
||||
const initPageResult = await initPage({
|
||||
config: configPromise,
|
||||
config,
|
||||
redirectUnauthenticatedUser: true,
|
||||
route: '/not-found',
|
||||
route: `${adminRoute}/not-found`,
|
||||
searchParams,
|
||||
})
|
||||
|
||||
|
||||
103
packages/next/src/views/ResetPassword/index.client.tsx
Normal file
103
packages/next/src/views/ResetPassword/index.client.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
'use client'
|
||||
import type { FormState } from 'payload/types'
|
||||
|
||||
import { ConfirmPassword } from '@payloadcms/ui/fields/ConfirmPassword'
|
||||
import { HiddenInput } from '@payloadcms/ui/fields/HiddenInput'
|
||||
import { Password } from '@payloadcms/ui/fields/Password'
|
||||
import { Form, useFormFields } from '@payloadcms/ui/forms/Form'
|
||||
import { FormSubmit } from '@payloadcms/ui/forms/Submit'
|
||||
import { useAuth } from '@payloadcms/ui/providers/Auth'
|
||||
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'
|
||||
|
||||
type Args = {
|
||||
token: string
|
||||
}
|
||||
|
||||
const initialState: FormState = {
|
||||
'confirm-password': {
|
||||
initialValue: '',
|
||||
valid: false,
|
||||
value: '',
|
||||
},
|
||||
password: {
|
||||
initialValue: '',
|
||||
valid: false,
|
||||
value: '',
|
||||
},
|
||||
}
|
||||
|
||||
export const ResetPasswordClient: React.FC<Args> = ({ token }) => {
|
||||
const i18n = useTranslation()
|
||||
const {
|
||||
admin: { user: userSlug },
|
||||
routes: { admin, api },
|
||||
serverURL,
|
||||
} = useConfig()
|
||||
|
||||
const history = useRouter()
|
||||
|
||||
const { fetchFullUser } = useAuth()
|
||||
|
||||
const onSuccess = React.useCallback(
|
||||
async (data) => {
|
||||
if (data.token) {
|
||||
await fetchFullUser()
|
||||
history.push(`${admin}`)
|
||||
} else {
|
||||
history.push(`${admin}/login`)
|
||||
toast.success(i18n.t('general:updatedSuccessfully'), { autoClose: 3000 })
|
||||
}
|
||||
},
|
||||
[fetchFullUser, history, admin, i18n],
|
||||
)
|
||||
|
||||
return (
|
||||
<Form
|
||||
action={`${serverURL}${api}/${userSlug}/reset-password`}
|
||||
initialState={initialState}
|
||||
method="POST"
|
||||
onSuccess={onSuccess}
|
||||
>
|
||||
<PasswordToConfirm />
|
||||
<ConfirmPassword />
|
||||
<HiddenInput forceUsePathFromProps name="token" value={token} />
|
||||
<FormSubmit>{i18n.t('authentication:resetPassword')}</FormSubmit>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
const PasswordToConfirm = () => {
|
||||
const { t } = useTranslation()
|
||||
const { value: confirmValue } = useFormFields(([fields]) => {
|
||||
return fields['confirm-password']
|
||||
})
|
||||
|
||||
const validate = React.useCallback(
|
||||
(value: string) => {
|
||||
if (!value) {
|
||||
return t('validation:required')
|
||||
}
|
||||
|
||||
if (value === confirmValue) {
|
||||
return true
|
||||
}
|
||||
|
||||
return t('fields:passwordsDoNotMatch')
|
||||
},
|
||||
[confirmValue, t],
|
||||
)
|
||||
|
||||
return (
|
||||
<Password
|
||||
autoComplete="off"
|
||||
label={t('authentication:newPassword')}
|
||||
name="password"
|
||||
required
|
||||
validate={validate}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,5 @@
|
||||
.reset-password {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
min-height: 100vh;
|
||||
|
||||
&__wrap {
|
||||
margin: 0 auto var(--base);
|
||||
width: 100%;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
}
|
||||
form > .field-type {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,11 @@ import type { AdminViewProps } from 'payload/types'
|
||||
|
||||
import { Button } from '@payloadcms/ui/elements/Button'
|
||||
import { Translation } from '@payloadcms/ui/elements/Translation'
|
||||
import { ConfirmPassword } from '@payloadcms/ui/fields/ConfirmPassword'
|
||||
import { HiddenInput } from '@payloadcms/ui/fields/HiddenInput'
|
||||
import { Password } from '@payloadcms/ui/fields/Password'
|
||||
import { Form } from '@payloadcms/ui/forms/Form'
|
||||
import { FormSubmit } from '@payloadcms/ui/forms/Submit'
|
||||
import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal'
|
||||
import LinkImport from 'next/link.js'
|
||||
import React from 'react'
|
||||
|
||||
import { ResetPasswordClient } from './index.client.js'
|
||||
import './index.scss'
|
||||
|
||||
export const resetPasswordBaseClass = 'reset-password'
|
||||
@@ -22,7 +18,9 @@ export { generateResetPasswordMetadata } from './meta.js'
|
||||
export const ResetPassword: React.FC<AdminViewProps> = ({ initPageResult, params }) => {
|
||||
const { req } = initPageResult
|
||||
|
||||
const { token } = params
|
||||
const {
|
||||
segments: [_, token],
|
||||
} = params
|
||||
|
||||
const {
|
||||
i18n,
|
||||
@@ -31,21 +29,9 @@ export const ResetPassword: React.FC<AdminViewProps> = ({ initPageResult, params
|
||||
} = req
|
||||
|
||||
const {
|
||||
admin: { user: userSlug },
|
||||
routes: { admin, api },
|
||||
serverURL,
|
||||
routes: { admin },
|
||||
} = config
|
||||
|
||||
// const onSuccess = async (data) => {
|
||||
// if (data.token) {
|
||||
// await fetchFullUser()
|
||||
// history.push(`${admin}`)
|
||||
// } else {
|
||||
// history.push(`${admin}/login`)
|
||||
// toast.success(i18n.t('general:updatedSuccessfully'), { autoClose: 3000 })
|
||||
// }
|
||||
// }
|
||||
|
||||
if (user) {
|
||||
return (
|
||||
<MinimalTemplate className={resetPasswordBaseClass}>
|
||||
@@ -73,22 +59,7 @@ export const ResetPassword: React.FC<AdminViewProps> = ({ initPageResult, params
|
||||
<MinimalTemplate className={resetPasswordBaseClass}>
|
||||
<div className={`${resetPasswordBaseClass}__wrap`}>
|
||||
<h1>{i18n.t('authentication:resetPassword')}</h1>
|
||||
<Form
|
||||
action={`${serverURL}${api}/${userSlug}/reset-password`}
|
||||
method="POST"
|
||||
// onSuccess={onSuccess}
|
||||
redirect={admin}
|
||||
>
|
||||
<Password
|
||||
autoComplete="off"
|
||||
label={i18n.t('authentication:newPassword')}
|
||||
name="password"
|
||||
required
|
||||
/>
|
||||
<ConfirmPassword />
|
||||
<HiddenInput forceUsePathFromProps name="token" value={token} />
|
||||
<FormSubmit>{i18n.t('authentication:resetPassword')}</FormSubmit>
|
||||
</Form>
|
||||
<ResetPasswordClient token={token} />
|
||||
</div>
|
||||
</MinimalTemplate>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
import type { AdminViewComponent } from 'payload/types'
|
||||
|
||||
import type { initPage } from '../../utilities/initPage.js'
|
||||
import type { initPage } from '../../utilities/initPage/index.js'
|
||||
|
||||
import { Account } from '../Account/index.js'
|
||||
import { CreateFirstUserView } from '../CreateFirstUser/index.js'
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { initPage } from '../../utilities/initPage.js'
|
||||
import { initPage } from '../../utilities/initPage/index.js'
|
||||
import { getViewFromConfig } from './getViewFromConfig.js'
|
||||
|
||||
export { generatePageMetadata } from './meta.js'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
@@ -115,6 +115,7 @@
|
||||
"sanitize-filename": "1.6.3",
|
||||
"scheduler": "0.23.0",
|
||||
"scmp": "2.1.0",
|
||||
"ts-essentials": "7.0.3",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -158,8 +159,7 @@
|
||||
"passport-strategy": "1.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
"serve-static": "1.15.0",
|
||||
"sharp": "0.32.6",
|
||||
"ts-essentials": "7.0.3"
|
||||
"sharp": "0.32.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": "^1.4.13",
|
||||
|
||||
@@ -141,6 +141,10 @@ export const loginOperation = async <TSlug extends keyof GeneratedTypes['collect
|
||||
user,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeLogin - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeLogin.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { cloudStorage } from './plugin.js'
|
||||
export { cloudStoragePlugin } from './plugin.js'
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getBeforeChangeHook } from './hooks/beforeChange.js'
|
||||
|
||||
// Optionally, the adapter can specify any Webpack config overrides if they are necessary.
|
||||
|
||||
export const cloudStorage =
|
||||
export const cloudStoragePlugin =
|
||||
(pluginOptions: PluginOptions) =>
|
||||
(incomingConfig: Config): Config => {
|
||||
const { collections: allCollectionOptions, enabled } = pluginOptions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { payloadCloud } from './plugin.js'
|
||||
export { payloadCloudPlugin } from './plugin.js'
|
||||
export { createKey } from './utilities/createKey.js'
|
||||
export { getStorageClient } from './utilities/getStorageClient.js'
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Payload } from 'payload'
|
||||
import nodemailer from 'nodemailer'
|
||||
import { defaults } from 'payload/config'
|
||||
|
||||
import { payloadCloud } from './plugin.js'
|
||||
import { payloadCloudPlugin } from './plugin.js'
|
||||
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
|
||||
|
||||
const mockedPayload: Payload = jest.fn() as unknown as Payload
|
||||
@@ -34,7 +34,7 @@ describe('plugin', () => {
|
||||
describe('not in Payload Cloud', () => {
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should return unmodified config', async () => {
|
||||
const plugin = payloadCloud()
|
||||
const plugin = payloadCloudPlugin()
|
||||
const config = await plugin(createConfig())
|
||||
|
||||
assertNoCloudStorage(config)
|
||||
@@ -52,7 +52,7 @@ describe('plugin', () => {
|
||||
describe('storage', () => {
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should default to using payload cloud storage', async () => {
|
||||
const plugin = payloadCloud()
|
||||
const plugin = payloadCloudPlugin()
|
||||
const config = await plugin(createConfig())
|
||||
|
||||
assertCloudStorage(config)
|
||||
@@ -60,7 +60,7 @@ describe('plugin', () => {
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should allow opt-out', async () => {
|
||||
const plugin = payloadCloud({ storage: false })
|
||||
const plugin = payloadCloudPlugin({ storage: false })
|
||||
const config = await plugin(createConfig())
|
||||
|
||||
assertNoCloudStorage(config)
|
||||
@@ -70,7 +70,7 @@ describe('plugin', () => {
|
||||
describe('email', () => {
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should default to using payload cloud email', async () => {
|
||||
const plugin = payloadCloud()
|
||||
const plugin = payloadCloudPlugin()
|
||||
const config = await plugin(createConfig())
|
||||
|
||||
expect(createTransportSpy).toHaveBeenCalledWith(
|
||||
@@ -82,7 +82,7 @@ describe('plugin', () => {
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should allow opt-out', async () => {
|
||||
const plugin = payloadCloud({ email: false })
|
||||
const plugin = payloadCloudPlugin({ email: false })
|
||||
const config = await plugin(createConfig())
|
||||
|
||||
expect(config.email).toBeUndefined()
|
||||
@@ -93,7 +93,7 @@ describe('plugin', () => {
|
||||
delete process.env.PAYLOAD_CLOUD_EMAIL_API_KEY
|
||||
delete process.env.PAYLOAD_CLOUD_DEFAULT_DOMAIN
|
||||
|
||||
const plugin = payloadCloud()
|
||||
const plugin = payloadCloudPlugin()
|
||||
const config = await plugin(createConfig())
|
||||
|
||||
expect(config.email).toBeUndefined()
|
||||
@@ -120,7 +120,7 @@ describe('plugin', () => {
|
||||
}),
|
||||
})
|
||||
|
||||
const plugin = payloadCloud()
|
||||
const plugin = payloadCloudPlugin()
|
||||
const config = await plugin(configWithTransport)
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
@@ -141,7 +141,7 @@ describe('plugin', () => {
|
||||
}),
|
||||
})
|
||||
|
||||
const plugin = payloadCloud()
|
||||
const plugin = payloadCloudPlugin()
|
||||
const config = await plugin(configWithPartialEmail)
|
||||
const emailConfig = config.email as Awaited<ReturnType<typeof nodemailerAdapter>>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from './hooks/uploadCache.js'
|
||||
import { getStaticHandler } from './staticHandler.js'
|
||||
|
||||
export const payloadCloud =
|
||||
export const payloadCloudPlugin =
|
||||
(pluginOptions?: PluginOptions) =>
|
||||
async (incomingConfig: Config): Promise<Config> => {
|
||||
let config = { ...incomingConfig }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { generateFormCollection } from './collections/Forms/index.js'
|
||||
export { fields } from './collections/Forms/fields.js'
|
||||
export { getPaymentTotal } from './utilities/getPaymentTotal.js'
|
||||
|
||||
const FormBuilder =
|
||||
export const formBuilderPlugin =
|
||||
(incomingFormConfig: PluginConfig) =>
|
||||
(config: Config): Config => {
|
||||
const formConfig: PluginConfig = {
|
||||
@@ -51,5 +51,3 @@ const FormBuilder =
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
export default FormBuilder
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { populateBreadcrumbs } from './utilities/populateBreadcrumbs.js'
|
||||
|
||||
export { createBreadcrumbsField, createParentField }
|
||||
|
||||
export const nestedDocs =
|
||||
export const nestedDocsPlugin =
|
||||
(pluginConfig: PluginConfig): Plugin =>
|
||||
(config) => ({
|
||||
...config,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Redirects plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PluginConfig } from './types.js'
|
||||
|
||||
import deepMerge from './deepMerge.js'
|
||||
|
||||
const redirects =
|
||||
export const redirectsPlugin =
|
||||
(pluginConfig: PluginConfig) =>
|
||||
(incomingConfig: Config): Config => ({
|
||||
...incomingConfig,
|
||||
@@ -78,5 +78,3 @@ const redirects =
|
||||
),
|
||||
],
|
||||
})
|
||||
|
||||
export { redirects }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Search plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -6,7 +6,7 @@ import deleteFromSearch from './Search/hooks/deleteFromSearch.js'
|
||||
import syncWithSearch from './Search/hooks/syncWithSearch.js'
|
||||
import { generateSearchCollection } from './Search/index.js'
|
||||
|
||||
const Search =
|
||||
export const searchPlugin =
|
||||
(incomingSearchConfig: SearchConfig) =>
|
||||
(config: Config): Config => {
|
||||
const { collections } = config
|
||||
@@ -61,5 +61,3 @@ const Search =
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export default Search
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { sentry } from './plugin'
|
||||
export type { PluginOptions } from './types'
|
||||
export { sentryPlugin } from './plugin.js'
|
||||
export type { PluginOptions } from './types.js'
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import type { Config } from 'payload/config'
|
||||
import { defaults } from 'payload/config'
|
||||
|
||||
import { sentry } from './plugin'
|
||||
import { sentryPlugin } from './plugin'
|
||||
|
||||
describe('plugin', () => {
|
||||
it('should run the plugin', () => {
|
||||
const plugin = sentry({ enabled: true, dsn: 'asdf' })
|
||||
const plugin = sentryPlugin({ enabled: true, dsn: 'asdf' })
|
||||
const config = plugin(createConfig())
|
||||
|
||||
assertPluginRan(config)
|
||||
})
|
||||
|
||||
it('should default enable: true', () => {
|
||||
const plugin = sentry({ dsn: 'asdf' })
|
||||
const plugin = sentryPlugin({ dsn: 'asdf' })
|
||||
const config = plugin(createConfig())
|
||||
|
||||
assertPluginRan(config)
|
||||
})
|
||||
|
||||
it('should not run if dsn is not provided', () => {
|
||||
const plugin = sentry({ enabled: true, dsn: null })
|
||||
const plugin = sentryPlugin({ enabled: true, dsn: null })
|
||||
const config = plugin(createConfig())
|
||||
|
||||
assertPluginDidNotRun(config)
|
||||
})
|
||||
|
||||
it('should respect enabled: false', () => {
|
||||
const plugin = sentry({ enabled: false, dsn: null })
|
||||
const plugin = sentryPlugin({ enabled: false, dsn: null })
|
||||
const config = plugin(createConfig())
|
||||
|
||||
assertPluginDidNotRun(config)
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { PluginOptions } from './types.js'
|
||||
import { captureException } from './captureException.js'
|
||||
import { startSentry } from './startSentry.js'
|
||||
|
||||
export const sentry =
|
||||
export const sentryPlugin =
|
||||
(pluginOptions: PluginOptions) =>
|
||||
(incomingConfig: Config): Config => {
|
||||
const config = { ...incomingConfig }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "SEO plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -20,7 +20,7 @@ import { translations } from './translations/index.js'
|
||||
import { Overview } from './ui/Overview.js'
|
||||
import { Preview } from './ui/Preview.js'
|
||||
|
||||
const seo =
|
||||
export const seoPlugin =
|
||||
(pluginConfig: PluginConfig) =>
|
||||
(config: Config): Config => {
|
||||
const seoFields: GroupField[] = [
|
||||
@@ -297,5 +297,3 @@ const seo =
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export { seo }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-stripe",
|
||||
"version": "0.0.16",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "Stripe plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
@@ -24,8 +24,8 @@
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
@@ -36,12 +36,14 @@
|
||||
"types.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "echo \"Build temporarily disabled.\" && exit 0",
|
||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo run build && pnpm test",
|
||||
"test": "echo 'No tests available.'"
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
@@ -51,17 +53,19 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@payloadcms/next": "workspace:*",
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/lodash.get": "^4.4.7",
|
||||
"@types/react": "18.2.74",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"payload": "workspace:*",
|
||||
"prettier": "^2.7.1",
|
||||
"webpack": "^5.78.0"
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
@@ -72,6 +76,7 @@
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"homepage:": "https://payloadcms.com"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Config } from 'payload/config'
|
||||
|
||||
import type { SanitizedStripeConfig, StripeConfig } from './types'
|
||||
import type { SanitizedStripeConfig, StripeConfig } from './types.js'
|
||||
|
||||
import { getFields } from './fields/getFields'
|
||||
import { getFields } from './fields/getFields.js'
|
||||
|
||||
const stripePlugin =
|
||||
export const stripePlugin =
|
||||
(incomingStripeConfig: StripeConfig) =>
|
||||
(config: Config): Config => {
|
||||
const { collections } = config
|
||||
@@ -42,5 +42,3 @@ const stripePlugin =
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
export default stripePlugin
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { CollectionConfig, Field } from 'payload/types'
|
||||
|
||||
import type { SanitizedStripeConfig } from '../types'
|
||||
import type { SanitizedStripeConfig } from '../types.js'
|
||||
|
||||
import { LinkToDoc } from '../ui/LinkToDoc'
|
||||
import { LinkToDoc } from '../ui/LinkToDoc.js'
|
||||
|
||||
interface Args {
|
||||
collection: CollectionConfig
|
||||
@@ -39,13 +39,12 @@ export const getFields = ({ collection, stripeConfig, syncConfig }: Args): Field
|
||||
type: 'ui',
|
||||
admin: {
|
||||
components: {
|
||||
Field: (args) =>
|
||||
LinkToDoc({
|
||||
...args,
|
||||
isTestKey: stripeConfig.isTestKey,
|
||||
nameOfIDField: 'stripeID',
|
||||
stripeResourceType: syncConfig.stripeResourceType,
|
||||
}),
|
||||
Field: LinkToDoc,
|
||||
},
|
||||
custom: {
|
||||
isTestKey: stripeConfig.isTestKey,
|
||||
nameOfIDField: 'stripeID',
|
||||
stripeResourceType: syncConfig.stripeResourceType,
|
||||
},
|
||||
position: 'sidebar',
|
||||
},
|
||||
|
||||
@@ -3,11 +3,12 @@ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload/typ
|
||||
import { APIError } from 'payload/errors'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import type { StripeConfig } from '../types'
|
||||
import type { StripeConfig } from '../types.js'
|
||||
|
||||
import { deepen } from '../utilities/deepen'
|
||||
import { deepen } from '../utilities/deepen.js'
|
||||
|
||||
const stripeSecretKey = process.env.STRIPE_SECRET_KEY
|
||||
// api version can only be the latest, stripe recommends ts ignoring it
|
||||
const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' })
|
||||
|
||||
type HookArgsWithCustomCollection = Omit<
|
||||
@@ -52,12 +53,15 @@ export const createNewInStripe: CollectionBeforeValidateHookWithArgs = async (ar
|
||||
|
||||
if (syncConfig) {
|
||||
// combine all fields of this object and match their respective values within the document
|
||||
let syncedFields = syncConfig.fields.reduce((acc, field) => {
|
||||
const { fieldPath, stripeProperty } = field
|
||||
let syncedFields = syncConfig.fields.reduce(
|
||||
(acc, field) => {
|
||||
const { fieldPath, stripeProperty } = field
|
||||
|
||||
acc[stripeProperty] = dataRef[fieldPath]
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
acc[stripeProperty] = dataRef[fieldPath]
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
)
|
||||
|
||||
syncedFields = deepen(syncedFields)
|
||||
|
||||
@@ -72,6 +76,7 @@ export const createNewInStripe: CollectionBeforeValidateHookWithArgs = async (ar
|
||||
try {
|
||||
// NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
|
||||
const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(
|
||||
// @ts-expect-error
|
||||
syncedFields,
|
||||
)
|
||||
|
||||
@@ -105,6 +110,7 @@ export const createNewInStripe: CollectionBeforeValidateHookWithArgs = async (ar
|
||||
|
||||
// NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
|
||||
const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(
|
||||
// @ts-expect-error
|
||||
syncedFields,
|
||||
)
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ import type { CollectionAfterDeleteHook, CollectionConfig } from 'payload/types'
|
||||
import { APIError } from 'payload/errors'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import type { StripeConfig } from '../types'
|
||||
import type { StripeConfig } from '../types.js'
|
||||
|
||||
const stripeSecretKey = process.env.STRIPE_SECRET_KEY
|
||||
// api version can only be the latest, stripe recommends ts ignoring it
|
||||
const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' })
|
||||
|
||||
type HookArgsWithCustomCollection = Omit<Parameters<CollectionAfterDeleteHook>[0], 'collection'> & {
|
||||
|
||||
@@ -3,11 +3,12 @@ import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload/types
|
||||
import { APIError } from 'payload/errors'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import type { StripeConfig } from '../types'
|
||||
import type { StripeConfig } from '../types.js'
|
||||
|
||||
import { deepen } from '../utilities/deepen'
|
||||
import { deepen } from '../utilities/deepen.js'
|
||||
|
||||
const stripeSecretKey = process.env.STRIPE_SECRET_KEY
|
||||
// api version can only be the latest, stripe recommends ts ignoring it
|
||||
const stripe = new Stripe(stripeSecretKey || '', { apiVersion: '2022-08-01' })
|
||||
|
||||
type HookArgsWithCustomCollection = Omit<
|
||||
@@ -39,12 +40,15 @@ export const syncExistingWithStripe: CollectionBeforeChangeHookWithArgs = async
|
||||
if (syncConfig) {
|
||||
if (operation === 'update') {
|
||||
// combine all fields of this object and match their respective values within the document
|
||||
let syncedFields = syncConfig.fields.reduce((acc, field) => {
|
||||
const { fieldPath, stripeProperty } = field
|
||||
let syncedFields = syncConfig.fields.reduce(
|
||||
(acc, field) => {
|
||||
const { fieldPath, stripeProperty } = field
|
||||
|
||||
acc[stripeProperty] = data[fieldPath]
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
acc[stripeProperty] = data[fieldPath]
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
)
|
||||
|
||||
syncedFields = deepen(syncedFields)
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ import { syncExistingWithStripe } from './hooks/syncExistingWithStripe.js'
|
||||
import { stripeREST } from './routes/rest.js'
|
||||
import { stripeWebhooks } from './routes/webhooks.js'
|
||||
|
||||
const stripePlugin =
|
||||
export { LinkToDoc } from './ui/LinkToDoc.js'
|
||||
export { stripeProxy } from './utilities/stripeProxy.js'
|
||||
|
||||
export const stripePlugin =
|
||||
(incomingStripeConfig: StripeConfig) =>
|
||||
(config: Config): Config => {
|
||||
const { collections } = config
|
||||
@@ -112,5 +115,3 @@ const stripePlugin =
|
||||
endpoints,
|
||||
}
|
||||
}
|
||||
|
||||
export default stripePlugin
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export const createNewInStripe = () => null
|
||||
export const deleteFromStripe = () => null
|
||||
export const stripeREST = () => null
|
||||
export const stripeWebhooks = () => null
|
||||
export const syncExistingWithStripe = () => null
|
||||
|
||||
export default {
|
||||
raw: () => {}, // mock express fn
|
||||
}
|
||||
@@ -33,7 +33,9 @@ export const stripeREST = async (args: {
|
||||
}
|
||||
|
||||
responseJSON = await stripeProxy({
|
||||
// @ts-expect-error
|
||||
stripeArgs,
|
||||
// @ts-expect-error
|
||||
stripeMethod,
|
||||
stripeSecretKey,
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ export const stripeWebhooks = async (args: {
|
||||
|
||||
if (stripeWebhooksEndpointSecret) {
|
||||
const stripe = new Stripe(stripeSecretKey, {
|
||||
// api version can only be the latest, stripe recommends ts ignoring it
|
||||
apiVersion: '2022-08-01',
|
||||
appInfo: {
|
||||
name: 'Stripe Payload Plugin',
|
||||
@@ -26,17 +27,14 @@ export const stripeWebhooks = async (args: {
|
||||
},
|
||||
})
|
||||
|
||||
const body = await req.text()
|
||||
const stripeSignature = req.headers.get('stripe-signature')
|
||||
|
||||
if (stripeSignature) {
|
||||
let event: Stripe.Event | undefined
|
||||
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(
|
||||
await req.text(),
|
||||
stripeSignature,
|
||||
stripeWebhooksEndpointSecret,
|
||||
)
|
||||
event = stripe.webhooks.constructEvent(body, stripeSignature, stripeWebhooksEndpointSecret)
|
||||
} catch (err: unknown) {
|
||||
const msg: string = err instanceof Error ? err.message : JSON.stringify(err)
|
||||
req.payload.logger.error(`Error constructing Stripe event: ${msg}`)
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
'use client'
|
||||
import type { CustomComponent } from 'payload/config'
|
||||
import type { UIField } from 'payload/types'
|
||||
|
||||
import { useFieldProps } from '@payloadcms/ui/forms/FieldPropsProvider'
|
||||
// import CopyToClipboard from 'payload/dist/admin/components/elements/CopyToClipboard'
|
||||
import { useFormFields } from '@payloadcms/ui/forms/Form'
|
||||
import React from 'react'
|
||||
|
||||
export const LinkToDoc: React.FC<
|
||||
UIField & {
|
||||
isTestKey: boolean
|
||||
nameOfIDField: string
|
||||
stripeResourceType: string
|
||||
}
|
||||
> = (props) => {
|
||||
const { isTestKey, nameOfIDField, stripeResourceType } = props
|
||||
export const LinkToDoc: CustomComponent<UIField> = () => {
|
||||
const { custom } = useFieldProps()
|
||||
const { isTestKey, nameOfIDField, stripeResourceType } = custom
|
||||
|
||||
const field = useFormFields(([fields]) => fields[nameOfIDField])
|
||||
const { value: stripeID } = field || {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import lodashGet from 'lodash.get'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
import type { StripeProxy } from '../types'
|
||||
import type { StripeProxy } from '../types.js'
|
||||
|
||||
export const stripeProxy: StripeProxy = async ({ stripeArgs, stripeMethod, stripeSecretKey }) => {
|
||||
const stripe = new Stripe(stripeSecretKey, {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types'
|
||||
import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types.js'
|
||||
|
||||
import { deepen } from '../utilities/deepen'
|
||||
import { deepen } from '../utilities/deepen.js'
|
||||
|
||||
type HandleCreatedOrUpdated = (
|
||||
args: Parameters<StripeWebhookHandler>[0] & {
|
||||
@@ -62,12 +62,15 @@ export const handleCreatedOrUpdated: HandleCreatedOrUpdated = async (args) => {
|
||||
const foundDoc = payloadQuery.docs[0] as any
|
||||
|
||||
// combine all properties of the Stripe doc and match their respective fields within the document
|
||||
let syncedData = syncConfig.fields.reduce((acc, field) => {
|
||||
const { fieldPath, stripeProperty } = field
|
||||
let syncedData = syncConfig.fields.reduce(
|
||||
(acc, field) => {
|
||||
const { fieldPath, stripeProperty } = field
|
||||
|
||||
acc[fieldPath] = stripeDoc[stripeProperty]
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
acc[fieldPath] = stripeDoc[stripeProperty]
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
)
|
||||
|
||||
syncedData = deepen({
|
||||
...syncedData,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types'
|
||||
import type { SanitizedStripeConfig, StripeWebhookHandler } from '../types.js'
|
||||
|
||||
type HandleDeleted = (
|
||||
args: Parameters<StripeWebhookHandler>[0] & {
|
||||
@@ -58,6 +58,7 @@ export const handleDeleted: HandleDeleted = async (args) => {
|
||||
if (logs) payload.logger.info(`- Deleting Payload document with ID: '${foundDoc.id}'...`)
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
payload.delete({
|
||||
id: foundDoc.id,
|
||||
collection: collectionSlug,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { StripeWebhookHandler } from '../types'
|
||||
import type { StripeWebhookHandler } from '../types.js'
|
||||
|
||||
import { handleCreatedOrUpdated } from './handleCreatedOrUpdated'
|
||||
import { handleDeleted } from './handleDeleted'
|
||||
import { handleCreatedOrUpdated } from './handleCreatedOrUpdated.js'
|
||||
import { handleDeleted } from './handleDeleted.js'
|
||||
|
||||
export const handleWebhooks: StripeWebhookHandler = async (args) => {
|
||||
export const handleWebhooks: StripeWebhookHandler = (args) => {
|
||||
const { event, payload, stripeConfig } = args
|
||||
|
||||
if (stripeConfig?.logs)
|
||||
@@ -21,7 +21,7 @@ export const handleWebhooks: StripeWebhookHandler = async (args) => {
|
||||
if (syncConfig) {
|
||||
switch (method) {
|
||||
case 'created': {
|
||||
await handleCreatedOrUpdated({
|
||||
void handleCreatedOrUpdated({
|
||||
...args,
|
||||
resourceType,
|
||||
stripeConfig,
|
||||
@@ -30,7 +30,7 @@ export const handleWebhooks: StripeWebhookHandler = async (args) => {
|
||||
break
|
||||
}
|
||||
case 'updated': {
|
||||
await handleCreatedOrUpdated({
|
||||
void handleCreatedOrUpdated({
|
||||
...args,
|
||||
resourceType,
|
||||
stripeConfig,
|
||||
@@ -39,7 +39,7 @@ export const handleWebhooks: StripeWebhookHandler = async (args) => {
|
||||
break
|
||||
}
|
||||
case 'deleted': {
|
||||
await handleDeleted({
|
||||
void handleDeleted({
|
||||
...args,
|
||||
resourceType,
|
||||
stripeConfig,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "3.0.0-beta.21",
|
||||
"version": "3.0.0-beta.23",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -2,5 +2,5 @@ export { RichTextCell } from '../cell/index.js'
|
||||
export { RichTextField } from '../field/index.js'
|
||||
|
||||
export { defaultEditorLexicalConfig } from '../field/lexical/config/client/default.js'
|
||||
export { ToolbarButton } from '../field/lexical/plugins/FloatingSelectToolbar/ToolbarButton/index.js'
|
||||
export { ToolbarDropdown } from '../field/lexical/plugins/FloatingSelectToolbar/ToolbarDropdown/index.js'
|
||||
export { ToolbarButton } from '../field/lexical/plugins/toolbars/inline/ToolbarButton/index.js'
|
||||
export { ToolbarDropdown } from '../field/lexical/plugins/toolbars/inline/ToolbarDropdown/index.js'
|
||||
|
||||
@@ -9,58 +9,52 @@ import { AlignJustifyIcon } from '../../lexical/ui/icons/AlignJustify/index.js'
|
||||
import { AlignLeftIcon } from '../../lexical/ui/icons/AlignLeft/index.js'
|
||||
import { AlignRightIcon } from '../../lexical/ui/icons/AlignRight/index.js'
|
||||
import { createClientComponent } from '../createClientComponent.js'
|
||||
import { AlignDropdownSectionWithEntries } from './floatingSelectToolbarAlignDropdownSection.js'
|
||||
import { alignGroupWithItems } from './inlineToolbarAlignGroup.js'
|
||||
|
||||
const AlignFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
feature: () => ({
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
AlignDropdownSectionWithEntries([
|
||||
toolbarInline: {
|
||||
groups: [
|
||||
alignGroupWithItems([
|
||||
{
|
||||
ChildComponent: AlignLeftIcon,
|
||||
isActive: () => false,
|
||||
key: 'align-left',
|
||||
key: 'alignLeft',
|
||||
label: `Align Left`,
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'left')
|
||||
},
|
||||
order: 1,
|
||||
},
|
||||
]),
|
||||
AlignDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: AlignCenterIcon,
|
||||
isActive: () => false,
|
||||
key: 'align-center',
|
||||
key: 'alignCenter',
|
||||
label: `Align Center`,
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'center')
|
||||
},
|
||||
order: 2,
|
||||
},
|
||||
]),
|
||||
AlignDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: AlignRightIcon,
|
||||
isActive: () => false,
|
||||
key: 'align-right',
|
||||
key: 'alignRight',
|
||||
label: `Align Right`,
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'right')
|
||||
},
|
||||
order: 3,
|
||||
},
|
||||
]),
|
||||
AlignDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: AlignJustifyIcon,
|
||||
isActive: () => false,
|
||||
key: 'align-justify',
|
||||
key: 'alignJustify',
|
||||
label: `Align Justify`,
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'justify')
|
||||
},
|
||||
order: 4,
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import type {
|
||||
FloatingToolbarSection,
|
||||
FloatingToolbarSectionEntry,
|
||||
} from '../../lexical/plugins/FloatingSelectToolbar/types.js'
|
||||
|
||||
import { AlignLeftIcon } from '../../lexical/ui/icons/AlignLeft/index.js'
|
||||
|
||||
export const AlignDropdownSectionWithEntries = (
|
||||
entries: FloatingToolbarSectionEntry[],
|
||||
): FloatingToolbarSection => {
|
||||
return {
|
||||
type: 'dropdown',
|
||||
ChildComponent: AlignLeftIcon,
|
||||
entries,
|
||||
key: 'dropdown-align',
|
||||
order: 2,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type {
|
||||
InlineToolbarGroup,
|
||||
InlineToolbarGroupItem,
|
||||
} from '../../lexical/plugins/toolbars/inline/types.js'
|
||||
|
||||
import { AlignLeftIcon } from '../../lexical/ui/icons/AlignLeft/index.js'
|
||||
|
||||
export const alignGroupWithItems = (items: InlineToolbarGroupItem[]): InlineToolbarGroup => {
|
||||
return {
|
||||
type: 'dropdown',
|
||||
ChildComponent: AlignLeftIcon,
|
||||
items,
|
||||
key: 'align',
|
||||
order: 2,
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,9 @@ import { $getSelection } from 'lexical'
|
||||
|
||||
import type { FeatureProviderProviderClient } from '../types.js'
|
||||
|
||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types.js'
|
||||
import { BlockquoteIcon } from '../../lexical/ui/icons/Blockquote/index.js'
|
||||
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection/index.js'
|
||||
import { createClientComponent } from '../createClientComponent.js'
|
||||
import { inlineToolbarTextDropdownGroupWithItems } from '../shared/inlineToolbar/textDropdownGroup.js'
|
||||
import { MarkdownTransformer } from './markdownTransformer.js'
|
||||
|
||||
const BlockQuoteFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
@@ -17,15 +16,40 @@ const BlockQuoteFeatureClient: FeatureProviderProviderClient<undefined> = (props
|
||||
clientFeatureProps: props,
|
||||
feature: () => ({
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
TextDropdownSectionWithEntries([
|
||||
markdownTransformers: [MarkdownTransformer],
|
||||
nodes: [QuoteNode],
|
||||
|
||||
slashMenu: {
|
||||
groups: [
|
||||
{
|
||||
displayName: 'Basic',
|
||||
items: [
|
||||
{
|
||||
Icon: BlockquoteIcon,
|
||||
displayName: 'Blockquote',
|
||||
key: 'blockquote',
|
||||
keywords: ['quote', 'blockquote'],
|
||||
onSelect: ({ editor }) => {
|
||||
editor.update(() => {
|
||||
const selection = $getSelection()
|
||||
$setBlocksType(selection, () => $createQuoteNode())
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
key: 'basic',
|
||||
},
|
||||
],
|
||||
},
|
||||
toolbarInline: {
|
||||
groups: [
|
||||
inlineToolbarTextDropdownGroupWithItems([
|
||||
{
|
||||
ChildComponent: BlockquoteIcon,
|
||||
isActive: () => false,
|
||||
key: 'blockquote',
|
||||
label: `Blockquote`,
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.update(() => {
|
||||
const selection = $getSelection()
|
||||
$setBlocksType(selection, () => $createQuoteNode())
|
||||
@@ -36,28 +60,6 @@ const BlockQuoteFeatureClient: FeatureProviderProviderClient<undefined> = (props
|
||||
]),
|
||||
],
|
||||
},
|
||||
markdownTransformers: [MarkdownTransformer],
|
||||
|
||||
nodes: [QuoteNode],
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
displayName: 'Basic',
|
||||
key: 'basic',
|
||||
options: [
|
||||
new SlashMenuOption(`blockquote`, {
|
||||
Icon: BlockquoteIcon,
|
||||
displayName: `Blockquote`,
|
||||
keywords: ['quote', 'blockquote'],
|
||||
onSelect: () => {
|
||||
const selection = $getSelection()
|
||||
$setBlocksType(selection, () => $createQuoteNode())
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { getTranslation } from '@payloadcms/translations'
|
||||
|
||||
import type { FeatureProviderProviderClient } from '../types.js'
|
||||
|
||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types.js'
|
||||
import { BlockIcon } from '../../lexical/ui/icons/Block/index.js'
|
||||
import { createClientComponent } from '../createClientComponent.js'
|
||||
import { BlockNode } from './nodes/BlocksNode.js'
|
||||
@@ -30,32 +29,31 @@ const BlocksFeatureClient: FeatureProviderProviderClient<BlocksFeatureClientProp
|
||||
},
|
||||
],
|
||||
slashMenu: {
|
||||
options: [
|
||||
groups: [
|
||||
{
|
||||
displayName: 'Blocks',
|
||||
key: 'blocks',
|
||||
options: [
|
||||
...props.reducedBlocks.map((block) => {
|
||||
return new SlashMenuOption('block-' + block.slug, {
|
||||
Icon: BlockIcon,
|
||||
displayName: ({ i18n }) => {
|
||||
if (!block.labels.singular) {
|
||||
return block.slug
|
||||
}
|
||||
items: props.reducedBlocks.map((block) => {
|
||||
return {
|
||||
Icon: BlockIcon,
|
||||
displayName: ({ i18n }) => {
|
||||
if (!block.labels.singular) {
|
||||
return block.slug
|
||||
}
|
||||
|
||||
return getTranslation(block.labels.singular, i18n)
|
||||
},
|
||||
keywords: ['block', 'blocks', block.slug],
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_BLOCK_COMMAND, {
|
||||
id: null,
|
||||
blockName: '',
|
||||
blockType: block.slug,
|
||||
})
|
||||
},
|
||||
})
|
||||
}),
|
||||
],
|
||||
return getTranslation(block.labels.singular, i18n)
|
||||
},
|
||||
key: 'block-' + block.slug,
|
||||
keywords: ['block', 'blocks', block.slug],
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_BLOCK_COMMAND, {
|
||||
id: null,
|
||||
blockName: '',
|
||||
blockType: block.slug,
|
||||
})
|
||||
},
|
||||
}
|
||||
}),
|
||||
key: 'blocks',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type {
|
||||
FloatingToolbarSection,
|
||||
FloatingToolbarSectionEntry,
|
||||
} from '../../../lexical/plugins/FloatingSelectToolbar/types.js'
|
||||
|
||||
export const FeaturesSectionWithEntries = (
|
||||
entries: FloatingToolbarSectionEntry[],
|
||||
): FloatingToolbarSection => {
|
||||
return {
|
||||
type: 'buttons',
|
||||
entries,
|
||||
key: 'features',
|
||||
order: 5,
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import type {
|
||||
FloatingToolbarSection,
|
||||
FloatingToolbarSectionEntry,
|
||||
} from '../../../lexical/plugins/FloatingSelectToolbar/types.js'
|
||||
|
||||
import { TextIcon } from '../../../lexical/ui/icons/Text/index.js'
|
||||
|
||||
export const TextDropdownSectionWithEntries = (
|
||||
entries: FloatingToolbarSectionEntry[],
|
||||
): FloatingToolbarSection => {
|
||||
return {
|
||||
type: 'dropdown',
|
||||
ChildComponent: TextIcon,
|
||||
entries,
|
||||
key: 'dropdown-text',
|
||||
order: 1,
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export const TestRecorderFeature: FeatureProviderProviderServer<undefined, undef
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'testrecorder',
|
||||
key: 'testRecorder',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export const TreeViewFeature: FeatureProviderProviderServer<undefined, undefined
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'treeview',
|
||||
key: 'treeView',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import type { FeatureProviderProviderClient } from '../../types.js'
|
||||
|
||||
import { BoldIcon } from '../../../lexical/ui/icons/Bold/index.js'
|
||||
import { createClientComponent } from '../../createClientComponent.js'
|
||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection.js'
|
||||
import { inlineToolbarFormatGroupWithItems } from '../shared/inlineToolbarFormatGroup.js'
|
||||
import {
|
||||
BOLD_ITALIC_STAR,
|
||||
BOLD_ITALIC_UNDERSCORE,
|
||||
@@ -24,9 +24,10 @@ const BoldFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
SectionWithEntries([
|
||||
markdownTransformers,
|
||||
toolbarInline: {
|
||||
groups: [
|
||||
inlineToolbarFormatGroupWithItems([
|
||||
{
|
||||
ChildComponent: BoldIcon,
|
||||
isActive: ({ selection }) => {
|
||||
@@ -36,7 +37,7 @@ const BoldFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
return false
|
||||
},
|
||||
key: 'bold',
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold')
|
||||
},
|
||||
order: 1,
|
||||
@@ -44,7 +45,6 @@ const BoldFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
]),
|
||||
],
|
||||
},
|
||||
markdownTransformers,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type {
|
||||
FloatingToolbarSection,
|
||||
FloatingToolbarSectionEntry,
|
||||
} from '../../../lexical/plugins/FloatingSelectToolbar/types.js'
|
||||
|
||||
export const SectionWithEntries = (
|
||||
entries: FloatingToolbarSectionEntry[],
|
||||
): FloatingToolbarSection => {
|
||||
return {
|
||||
type: 'buttons',
|
||||
entries,
|
||||
key: 'format',
|
||||
order: 4,
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import type { FeatureProviderProviderClient } from '../../types.js'
|
||||
|
||||
import { CodeIcon } from '../../../lexical/ui/icons/Code/index.js'
|
||||
import { createClientComponent } from '../../createClientComponent.js'
|
||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection.js'
|
||||
import { inlineToolbarFormatGroupWithItems } from '../shared/inlineToolbarFormatGroup.js'
|
||||
import { INLINE_CODE } from './markdownTransformers.js'
|
||||
|
||||
const InlineCodeFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
@@ -15,9 +15,11 @@ const InlineCodeFeatureClient: FeatureProviderProviderClient<undefined> = (props
|
||||
feature: () => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
SectionWithEntries([
|
||||
markdownTransformers: [INLINE_CODE],
|
||||
|
||||
toolbarInline: {
|
||||
groups: [
|
||||
inlineToolbarFormatGroupWithItems([
|
||||
{
|
||||
ChildComponent: CodeIcon,
|
||||
isActive: ({ selection }) => {
|
||||
@@ -26,8 +28,8 @@ const InlineCodeFeatureClient: FeatureProviderProviderClient<undefined> = (props
|
||||
}
|
||||
return false
|
||||
},
|
||||
key: 'code',
|
||||
onClick: ({ editor }) => {
|
||||
key: 'inlineCode',
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code')
|
||||
},
|
||||
order: 7,
|
||||
@@ -35,8 +37,6 @@ const InlineCodeFeatureClient: FeatureProviderProviderClient<undefined> = (props
|
||||
]),
|
||||
],
|
||||
},
|
||||
|
||||
markdownTransformers: [INLINE_CODE],
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export const InlineCodeFeature: FeatureProviderProviderServer<undefined, undefin
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'inlinecode',
|
||||
key: 'inlineCode',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import type { FeatureProviderProviderClient } from '../../types.js'
|
||||
|
||||
import { ItalicIcon } from '../../../lexical/ui/icons/Italic/index.js'
|
||||
import { createClientComponent } from '../../createClientComponent.js'
|
||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection.js'
|
||||
import { inlineToolbarFormatGroupWithItems } from '../shared/inlineToolbarFormatGroup.js'
|
||||
import { ITALIC_STAR, ITALIC_UNDERSCORE } from './markdownTransformers.js'
|
||||
|
||||
const ItalicFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
@@ -16,9 +16,10 @@ const ItalicFeatureClient: FeatureProviderProviderClient<undefined> = (props) =>
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
SectionWithEntries([
|
||||
markdownTransformers: [ITALIC_STAR, ITALIC_UNDERSCORE],
|
||||
toolbarInline: {
|
||||
groups: [
|
||||
inlineToolbarFormatGroupWithItems([
|
||||
{
|
||||
ChildComponent: ItalicIcon,
|
||||
isActive: ({ selection }) => {
|
||||
@@ -28,7 +29,7 @@ const ItalicFeatureClient: FeatureProviderProviderClient<undefined> = (props) =>
|
||||
return false
|
||||
},
|
||||
key: 'italic',
|
||||
onClick: ({ editor }) => {
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic')
|
||||
},
|
||||
order: 2,
|
||||
@@ -36,7 +37,6 @@ const ItalicFeatureClient: FeatureProviderProviderClient<undefined> = (props) =>
|
||||
]),
|
||||
],
|
||||
},
|
||||
markdownTransformers: [ITALIC_STAR, ITALIC_UNDERSCORE],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import type {
|
||||
InlineToolbarGroup,
|
||||
InlineToolbarGroupItem,
|
||||
} from '../../../lexical/plugins/toolbars/inline/types.js'
|
||||
|
||||
export const inlineToolbarFormatGroupWithItems = (
|
||||
items: InlineToolbarGroupItem[],
|
||||
): InlineToolbarGroup => {
|
||||
return {
|
||||
type: 'buttons',
|
||||
items,
|
||||
key: 'format',
|
||||
order: 4,
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user