chore(next): ssr field validations (#4700)
This commit is contained in:
@@ -4,7 +4,7 @@ import { DocumentLayout } from '@payloadcms/next/layouts/Document'
|
|||||||
import configPromise from 'payload-config'
|
import configPromise from 'payload-config'
|
||||||
|
|
||||||
export default async ({ children, params }: { children: React.ReactNode; params }) => (
|
export default async ({ children, params }: { children: React.ReactNode; params }) => (
|
||||||
<DocumentLayout config={configPromise} collectionSlug={params.collection} id={params.id}>
|
<DocumentLayout config={configPromise} collectionSlug={params.collection}>
|
||||||
{children}
|
{children}
|
||||||
</DocumentLayout>
|
</DocumentLayout>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,13 +15,11 @@ export const DocumentLayout = async ({
|
|||||||
config: configPromise,
|
config: configPromise,
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
globalSlug,
|
globalSlug,
|
||||||
id,
|
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
config: Promise<SanitizedConfig>
|
config: Promise<SanitizedConfig>
|
||||||
collectionSlug?: string
|
collectionSlug?: string
|
||||||
globalSlug?: string
|
globalSlug?: string
|
||||||
id?: string
|
|
||||||
}) => {
|
}) => {
|
||||||
const { user, permissions, config } = await initPage(configPromise)
|
const { user, permissions, config } = await initPage(configPromise)
|
||||||
|
|
||||||
@@ -36,14 +34,9 @@ export const DocumentLayout = async ({
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<DocumentHeader
|
<DocumentHeader
|
||||||
// apiURL={apiURL}
|
|
||||||
config={config}
|
config={config}
|
||||||
collectionConfig={collectionConfig}
|
collectionConfig={collectionConfig}
|
||||||
globalConfig={globalConfig}
|
globalConfig={globalConfig}
|
||||||
// customHeader={customHeader}
|
|
||||||
// data={data}
|
|
||||||
id={id}
|
|
||||||
// isEditing={isEditing}
|
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export const Account = async ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HydrateClientUser user={user} />
|
<HydrateClientUser user={user} permissions={permissions} />
|
||||||
<RenderCustomComponent
|
<RenderCustomComponent
|
||||||
CustomComponent={
|
CustomComponent={
|
||||||
typeof CustomAccountComponent === 'function' ? CustomAccountComponent : undefined
|
typeof CustomAccountComponent === 'function' ? CustomAccountComponent : undefined
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ import {
|
|||||||
FormQueryParamsProvider,
|
FormQueryParamsProvider,
|
||||||
QueryParamTypes,
|
QueryParamTypes,
|
||||||
HydrateClientUser,
|
HydrateClientUser,
|
||||||
|
DocumentInfoProvider,
|
||||||
} from '@payloadcms/ui'
|
} from '@payloadcms/ui'
|
||||||
import queryString from 'qs'
|
import queryString from 'qs'
|
||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
|
import { TFunction } from 'i18next'
|
||||||
|
|
||||||
export const CollectionEdit = async ({
|
export const CollectionEdit = async ({
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
@@ -99,7 +101,7 @@ export const CollectionEdit = async ({
|
|||||||
locale,
|
locale,
|
||||||
operation: isEditing ? 'update' : 'create',
|
operation: isEditing ? 'update' : 'create',
|
||||||
preferences,
|
preferences,
|
||||||
// t,
|
t: ((key: string) => key) as TFunction, // TODO: i18n
|
||||||
user,
|
user,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -136,16 +138,24 @@ export const CollectionEdit = async ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HydrateClientUser user={user} />
|
<HydrateClientUser user={user} permissions={permissions} />
|
||||||
<EditDepthProvider depth={1}>
|
<DocumentInfoProvider
|
||||||
<FormQueryParamsProvider formQueryParams={formQueryParams}>
|
collectionSlug={collectionConfig.slug}
|
||||||
<RenderCustomComponent
|
id={id}
|
||||||
CustomComponent={typeof CustomEdit === 'function' ? CustomEdit : undefined}
|
key={`${collectionSlug}-${locale}`}
|
||||||
DefaultComponent={DefaultEditView}
|
versionsEnabled={Boolean(collectionConfig.versions)}
|
||||||
componentProps={componentProps}
|
draftsEnabled={Boolean(collectionConfig.versions?.drafts)}
|
||||||
/>
|
>
|
||||||
</FormQueryParamsProvider>
|
<EditDepthProvider depth={1}>
|
||||||
</EditDepthProvider>
|
<FormQueryParamsProvider formQueryParams={formQueryParams}>
|
||||||
|
<RenderCustomComponent
|
||||||
|
CustomComponent={typeof CustomEdit === 'function' ? CustomEdit : undefined}
|
||||||
|
DefaultComponent={DefaultEditView}
|
||||||
|
componentProps={componentProps}
|
||||||
|
/>
|
||||||
|
</FormQueryParamsProvider>
|
||||||
|
</EditDepthProvider>
|
||||||
|
</DocumentInfoProvider>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const CollectionList = async ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HydrateClientUser user={user} />
|
<HydrateClientUser user={user} permissions={permissions} />
|
||||||
<RenderCustomComponent
|
<RenderCustomComponent
|
||||||
CustomComponent={ListToRender}
|
CustomComponent={ListToRender}
|
||||||
DefaultComponent={DefaultList}
|
DefaultComponent={DefaultList}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ export const Dashboard = async ({
|
|||||||
config: Promise<SanitizedConfig>
|
config: Promise<SanitizedConfig>
|
||||||
searchParams: { [key: string]: string | string[] | undefined }
|
searchParams: { [key: string]: string | string[] | undefined }
|
||||||
}) => {
|
}) => {
|
||||||
const { config, user } = await initPage(configPromise, true)
|
const { config, user, permissions } = await initPage(configPromise, true)
|
||||||
|
|
||||||
const CustomDashboardComponent = config.admin.components?.views?.Dashboard
|
const CustomDashboardComponent = config.admin.components?.views?.Dashboard
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HydrateClientUser user={user} />
|
<HydrateClientUser user={user} permissions={permissions} />
|
||||||
<RenderCustomComponent
|
<RenderCustomComponent
|
||||||
CustomComponent={
|
CustomComponent={
|
||||||
typeof CustomDashboardComponent === 'function' ? CustomDashboardComponent : undefined
|
typeof CustomDashboardComponent === 'function' ? CustomDashboardComponent : undefined
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
FormQueryParamsProvider,
|
FormQueryParamsProvider,
|
||||||
QueryParamTypes,
|
QueryParamTypes,
|
||||||
HydrateClientUser,
|
HydrateClientUser,
|
||||||
|
DocumentInfoProvider,
|
||||||
} from '@payloadcms/ui'
|
} from '@payloadcms/ui'
|
||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
import { Metadata } from 'next'
|
import { Metadata } from 'next'
|
||||||
@@ -126,7 +127,7 @@ export const Global = async ({
|
|||||||
globalConfig,
|
globalConfig,
|
||||||
data,
|
data,
|
||||||
fieldTypes,
|
fieldTypes,
|
||||||
initialState: state,
|
state,
|
||||||
permissions: globalPermission,
|
permissions: globalPermission,
|
||||||
updatedAt: data?.updatedAt?.toString(),
|
updatedAt: data?.updatedAt?.toString(),
|
||||||
user,
|
user,
|
||||||
@@ -135,16 +136,23 @@ export const Global = async ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HydrateClientUser user={user} />
|
<HydrateClientUser user={user} permissions={permissions} />
|
||||||
<EditDepthProvider depth={1}>
|
<DocumentInfoProvider
|
||||||
<FormQueryParamsProvider formQueryParams={formQueryParams}>
|
collectionSlug={globalConfig.slug}
|
||||||
<RenderCustomComponent
|
key={`${globalSlug}-${locale}`}
|
||||||
CustomComponent={typeof CustomEdit === 'function' ? CustomEdit : undefined}
|
versionsEnabled={Boolean(globalConfig.versions)}
|
||||||
DefaultComponent={DefaultGlobalView}
|
draftsEnabled={Boolean(globalConfig.versions?.drafts)}
|
||||||
componentProps={componentProps}
|
>
|
||||||
/>
|
<EditDepthProvider depth={1}>
|
||||||
</FormQueryParamsProvider>
|
<FormQueryParamsProvider formQueryParams={formQueryParams}>
|
||||||
</EditDepthProvider>
|
<RenderCustomComponent
|
||||||
|
CustomComponent={typeof CustomEdit === 'function' ? CustomEdit : undefined}
|
||||||
|
DefaultComponent={DefaultGlobalView}
|
||||||
|
componentProps={componentProps}
|
||||||
|
/>
|
||||||
|
</FormQueryParamsProvider>
|
||||||
|
</EditDepthProvider>
|
||||||
|
</DocumentInfoProvider>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { DefaultVersionsView } from './Default'
|
|||||||
import { SanitizedConfig } from 'payload/types'
|
import { SanitizedConfig } from 'payload/types'
|
||||||
import { initPage } from '../../utilities/initPage'
|
import { initPage } from '../../utilities/initPage'
|
||||||
import { DefaultVersionsViewProps } from './Default/types'
|
import { DefaultVersionsViewProps } from './Default/types'
|
||||||
|
import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
export const VersionsView = async ({
|
export const VersionsView = async ({
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
@@ -46,28 +47,40 @@ export const VersionsView = async ({
|
|||||||
let versionsData
|
let versionsData
|
||||||
|
|
||||||
if (collectionSlug) {
|
if (collectionSlug) {
|
||||||
data = await payload.findByID({
|
try {
|
||||||
collection: collectionSlug,
|
data = await payload.findByID({
|
||||||
id,
|
collection: collectionSlug,
|
||||||
depth: 0,
|
id,
|
||||||
user,
|
depth: 0,
|
||||||
// draft: true,
|
user,
|
||||||
})
|
// draft: true,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
versionsData = await payload.findVersions({
|
if (!data) {
|
||||||
collection: collectionSlug,
|
return notFound()
|
||||||
depth: 0,
|
}
|
||||||
user,
|
|
||||||
page: page ? parseInt(page as string, 10) : undefined,
|
try {
|
||||||
sort: sort as string,
|
versionsData = await payload.findVersions({
|
||||||
// TODO: why won't this work?!
|
collection: collectionSlug,
|
||||||
// throws an `unsupported BSON` error
|
depth: 0,
|
||||||
// where: {
|
user,
|
||||||
// parent: {
|
page: page ? parseInt(page as string, 10) : undefined,
|
||||||
// equals: id,
|
sort: sort as string,
|
||||||
// },
|
// TODO: why won't this work?!
|
||||||
// },
|
// throws an `unsupported BSON` error
|
||||||
})
|
// where: {
|
||||||
|
// parent: {
|
||||||
|
// equals: id,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
docURL = `${serverURL}${api}/${slug}/${id}`
|
docURL = `${serverURL}${api}/${slug}/${id}`
|
||||||
// entityLabel = getTranslation(collectionConfig.labels.singular, i18n)
|
// entityLabel = getTranslation(collectionConfig.labels.singular, i18n)
|
||||||
@@ -97,25 +110,41 @@ export const VersionsView = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (globalSlug) {
|
if (globalSlug) {
|
||||||
data = await payload.findGlobal({
|
try {
|
||||||
slug: globalSlug,
|
data = await payload.findGlobal({
|
||||||
depth: 0,
|
slug: globalSlug,
|
||||||
user,
|
depth: 0,
|
||||||
// draft: true,
|
user,
|
||||||
})
|
// draft: true,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
versionsData = await payload.findGlobalVersions({
|
if (!data) {
|
||||||
slug: globalSlug,
|
return notFound()
|
||||||
depth: 0,
|
}
|
||||||
user,
|
|
||||||
page: page ? parseInt(page as string, 10) : undefined,
|
try {
|
||||||
sort: sort as string,
|
versionsData = await payload.findGlobalVersions({
|
||||||
where: {
|
slug: globalSlug,
|
||||||
parent: {
|
depth: 0,
|
||||||
equals: id,
|
user,
|
||||||
|
page: page ? parseInt(page as string, 10) : undefined,
|
||||||
|
sort: sort as string,
|
||||||
|
where: {
|
||||||
|
parent: {
|
||||||
|
equals: id,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!versionsData) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
|
|
||||||
docURL = `${serverURL}${api}/globals/${globalSlug}`
|
docURL = `${serverURL}${api}/globals/${globalSlug}`
|
||||||
// entityLabel = getTranslation(globalConfig.label, i18n)
|
// entityLabel = getTranslation(globalConfig.label, i18n)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { Modal, useModal } from '@faceless-ui/modal'
|
import { Modal, useModal } from '@faceless-ui/modal'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import { useHistory } from 'react-router-dom'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
@@ -11,22 +10,17 @@ import { getTranslation } from 'payload/utilities'
|
|||||||
// import { requests } from '../../../api'
|
// import { requests } from '../../../api'
|
||||||
import useTitle from '../../hooks/useTitle'
|
import useTitle from '../../hooks/useTitle'
|
||||||
import { useForm } from '../../forms/Form/context'
|
import { useForm } from '../../forms/Form/context'
|
||||||
import { Minimal as MinimalTemplate } from '../../templates/Minimal'
|
import { MinimalTemplate } from '../../templates/Minimal'
|
||||||
import { useConfig } from '../../providers/Config'
|
import { useConfig } from '../../providers/Config'
|
||||||
import { Button } from '../Button'
|
import { Button } from '../Button'
|
||||||
import * as PopupList from '../Popup/PopupButtonList'
|
import * as PopupList from '../Popup/PopupButtonList'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
|
||||||
const baseClass = 'delete-document'
|
const baseClass = 'delete-document'
|
||||||
|
|
||||||
const DeleteDocument: React.FC<Props> = (props) => {
|
const DeleteDocument: React.FC<Props> = (props) => {
|
||||||
const {
|
const { id, buttonId, useAsTitle, collectionSlug, singularLabel, title: titleFromProps } = props
|
||||||
id,
|
|
||||||
buttonId,
|
|
||||||
collection: { labels: { singular } = {}, slug } = {},
|
|
||||||
collection,
|
|
||||||
title: titleFromProps,
|
|
||||||
} = props
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
routes: { admin, api },
|
routes: { admin, api },
|
||||||
@@ -36,9 +30,12 @@ const DeleteDocument: React.FC<Props> = (props) => {
|
|||||||
const { setModified } = useForm()
|
const { setModified } = useForm()
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
const { toggleModal } = useModal()
|
const { toggleModal } = useModal()
|
||||||
const history = useHistory()
|
const history = useRouter()
|
||||||
const { i18n, t } = useTranslation('general')
|
const { i18n, t } = useTranslation('general')
|
||||||
const title = useTitle({ collection })
|
const title = useTitle({
|
||||||
|
useAsTitle,
|
||||||
|
})
|
||||||
|
|
||||||
const titleToRender = titleFromProps || title || id
|
const titleToRender = titleFromProps || title || id
|
||||||
|
|
||||||
const modalSlug = `delete-${id}`
|
const modalSlug = `delete-${id}`
|
||||||
@@ -86,12 +83,12 @@ const DeleteDocument: React.FC<Props> = (props) => {
|
|||||||
setModified,
|
setModified,
|
||||||
serverURL,
|
serverURL,
|
||||||
api,
|
api,
|
||||||
slug,
|
collectionSlug,
|
||||||
id,
|
id,
|
||||||
toggleModal,
|
toggleModal,
|
||||||
modalSlug,
|
modalSlug,
|
||||||
t,
|
t,
|
||||||
singular,
|
singularLabel,
|
||||||
i18n,
|
i18n,
|
||||||
title,
|
title,
|
||||||
history,
|
history,
|
||||||
@@ -118,7 +115,7 @@ const DeleteDocument: React.FC<Props> = (props) => {
|
|||||||
<Trans
|
<Trans
|
||||||
i18nKey="aboutToDelete"
|
i18nKey="aboutToDelete"
|
||||||
t={t}
|
t={t}
|
||||||
values={{ label: getTranslation(singular, i18n), title: titleToRender }}
|
values={{ label: getTranslation(singularLabel, i18n), title: titleToRender }}
|
||||||
>
|
>
|
||||||
aboutToDelete
|
aboutToDelete
|
||||||
<strong>{titleToRender}</strong>
|
<strong>{titleToRender}</strong>
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import type { SanitizedCollectionConfig } from 'payload/types'
|
|||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
buttonId?: string
|
buttonId?: string
|
||||||
collection?: SanitizedCollectionConfig
|
useAsTitle: SanitizedCollectionConfig['admin']['useAsTitle']
|
||||||
|
collectionSlug: SanitizedCollectionConfig['slug']
|
||||||
|
singularLabel: SanitizedCollectionConfig['labels']['singular']
|
||||||
id?: string
|
id?: string
|
||||||
title?: string
|
title?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ export const DocumentControls: React.FC<{
|
|||||||
{collectionConfig && !isEditing && !isAccountView && (
|
{collectionConfig && !isEditing && !isAccountView && (
|
||||||
<li className={`${baseClass}__list-item`}>
|
<li className={`${baseClass}__list-item`}>
|
||||||
<p className={`${baseClass}__value`}>
|
<p className={`${baseClass}__value`}>
|
||||||
|
Creating new{' '}
|
||||||
|
{typeof collectionConfig?.labels?.singular === 'string'
|
||||||
|
? collectionConfig.labels.singular
|
||||||
|
: 'Doc'}
|
||||||
{/* {t('creatingNewLabel', {
|
{/* {t('creatingNewLabel', {
|
||||||
label:
|
label:
|
||||||
typeof collectionConfig?.labels?.singular === 'string'
|
typeof collectionConfig?.labels?.singular === 'string'
|
||||||
@@ -221,25 +225,31 @@ export const DocumentControls: React.FC<{
|
|||||||
<PopupList.ButtonGroup>
|
<PopupList.ButtonGroup>
|
||||||
{hasCreatePermission && (
|
{hasCreatePermission && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{/* <PopupList.Button
|
<PopupList.Button
|
||||||
id="action-create"
|
id="action-create"
|
||||||
to={`${adminRoute}/collections/${collectionConfig?.slug}/create`}
|
to={`${adminRoute}/collections/${collectionConfig?.slug}/create`}
|
||||||
>
|
>
|
||||||
{t('createNew')}
|
Create New
|
||||||
</PopupList.Button> */}
|
{/* {t('createNew')} */}
|
||||||
|
</PopupList.Button>
|
||||||
{/* {!collectionConfig?.admin?.disableDuplicate && isEditing && (
|
{!collectionConfig?.admin?.disableDuplicate && isEditing && (
|
||||||
<DuplicateDocument
|
<DuplicateDocument
|
||||||
collection={collectionConfig}
|
singularLabel={collectionConfig?.labels?.singular}
|
||||||
id={id}
|
id={id}
|
||||||
slug={collectionConfig?.slug}
|
slug={collectionConfig?.slug}
|
||||||
/>
|
/>
|
||||||
)} */}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
{/* {hasDeletePermission && (
|
{hasDeletePermission && (
|
||||||
<DeleteDocument buttonId="action-delete" collection={collectionConfig} id={id} />
|
<DeleteDocument
|
||||||
)} */}
|
buttonId="action-delete"
|
||||||
|
collectionSlug={collectionConfig?.slug}
|
||||||
|
useAsTitle={collectionConfig?.admin?.useAsTitle}
|
||||||
|
singularLabel={collectionConfig?.labels?.singular}
|
||||||
|
id={id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</PopupList.ButtonGroup>
|
</PopupList.ButtonGroup>
|
||||||
</Popup>
|
</Popup>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const DocumentHeader: React.FC<{
|
|||||||
data={data}
|
data={data}
|
||||||
isDate={titleFieldConfig?.type === 'date'}
|
isDate={titleFieldConfig?.type === 'date'}
|
||||||
dateFormat={
|
dateFormat={
|
||||||
'date' in titleFieldConfig?.admin
|
titleFieldConfig && 'date' in titleFieldConfig?.admin
|
||||||
? titleFieldConfig?.admin?.date?.displayFormat
|
? titleFieldConfig?.admin?.date?.displayFormat
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { Modal, useModal } from '@faceless-ui/modal'
|
import { Modal, useModal } from '@faceless-ui/modal'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useHistory } from 'react-router-dom'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
@@ -10,16 +9,17 @@ import type { Props } from './types'
|
|||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
// import { requests } from '../../../api'
|
// import { requests } from '../../../api'
|
||||||
import { useForm, useFormModified } from '../../forms/Form/context'
|
import { useForm, useFormModified } from '../../forms/Form/context'
|
||||||
import { Minimal as MinimalTemplate } from '../../templates/Minimal'
|
import { MinimalTemplate } from '../../templates/Minimal'
|
||||||
import { useConfig } from '../../providers/Config'
|
import { useConfig } from '../../providers/Config'
|
||||||
import { Button } from '../Button'
|
import { Button } from '../Button'
|
||||||
import * as PopupList from '../Popup/PopupButtonList'
|
import * as PopupList from '../Popup/PopupButtonList'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
|
||||||
const baseClass = 'duplicate'
|
const baseClass = 'duplicate'
|
||||||
|
|
||||||
const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
|
const Duplicate: React.FC<Props> = ({ id, slug, singularLabel }) => {
|
||||||
const { push } = useHistory()
|
const { push } = useRouter()
|
||||||
const modified = useFormModified()
|
const modified = useFormModified()
|
||||||
const { toggleModal } = useModal()
|
const { toggleModal } = useModal()
|
||||||
const { setModified } = useForm()
|
const { setModified } = useForm()
|
||||||
@@ -66,13 +66,14 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
|
|||||||
|
|
||||||
// let data = await response.json()
|
// let data = await response.json()
|
||||||
|
|
||||||
if (typeof collection.admin.hooks?.beforeDuplicate === 'function') {
|
// TODO: convert this into a server action
|
||||||
data = await collection.admin.hooks.beforeDuplicate({
|
// if (typeof collection.admin.hooks?.beforeDuplicate === 'function') {
|
||||||
collection,
|
// data = await collection.admin.hooks.beforeDuplicate({
|
||||||
data,
|
// collection,
|
||||||
locale,
|
// data,
|
||||||
})
|
// locale,
|
||||||
}
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
if (!duplicateID) {
|
if (!duplicateID) {
|
||||||
if ('createdAt' in data) delete data.createdAt
|
if ('createdAt' in data) delete data.createdAt
|
||||||
@@ -133,10 +134,9 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success(
|
toast.success(t('successfullyDuplicated', { label: getTranslation(singularLabel, i18n) }), {
|
||||||
t('successfullyDuplicated', { label: getTranslation(collection.labels.singular, i18n) }),
|
autoClose: 3000,
|
||||||
{ autoClose: 3000 },
|
})
|
||||||
)
|
|
||||||
|
|
||||||
if (localeErrors.length > 0) {
|
if (localeErrors.length > 0) {
|
||||||
toast.error(
|
toast.error(
|
||||||
@@ -151,9 +151,7 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
|
|||||||
setModified(false)
|
setModified(false)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
push({
|
push(`${admin}/collections/${slug}/${duplicateID}`)
|
||||||
pathname: `${admin}/collections/${slug}/${duplicateID}`,
|
|
||||||
})
|
|
||||||
}, 10)
|
}, 10)
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -161,7 +159,6 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
|
|||||||
localization,
|
localization,
|
||||||
t,
|
t,
|
||||||
i18n,
|
i18n,
|
||||||
collection,
|
|
||||||
setModified,
|
setModified,
|
||||||
toggleModal,
|
toggleModal,
|
||||||
modalSlug,
|
modalSlug,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
|
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
collection: SanitizedCollectionConfig
|
singularLabel: SanitizedCollectionConfig['labels']['singular']
|
||||||
id: string
|
id: string
|
||||||
slug: string
|
slug: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,18 @@
|
|||||||
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useAuth } from '../../providers/Auth'
|
import { useAuth } from '../../providers/Auth'
|
||||||
|
import { Permissions, User } from 'payload/auth'
|
||||||
|
|
||||||
export const HydrateClientUser: React.FC<{ user: any }> = ({ user }) => {
|
export const HydrateClientUser: React.FC<{ user: User; permissions: Permissions }> = ({
|
||||||
const { setUser } = useAuth()
|
user,
|
||||||
|
permissions,
|
||||||
|
}) => {
|
||||||
|
const { setUser, setPermissions } = useAuth()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUser(user)
|
setUser(user)
|
||||||
}, [user])
|
setPermissions(permissions)
|
||||||
|
}, [user, permissions, setUser, setPermissions])
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,14 +28,18 @@ export const LoginForm: React.FC<{
|
|||||||
action={`${api}/${userSlug}/login`}
|
action={`${api}/${userSlug}/login`}
|
||||||
className={`${baseClass}__form`}
|
className={`${baseClass}__form`}
|
||||||
disableSuccessStatus
|
disableSuccessStatus
|
||||||
initialData={
|
initialState={{
|
||||||
prefillForm
|
email: {
|
||||||
? {
|
initialValue: prefillForm ? autoLogin.email : undefined,
|
||||||
email: autoLogin.email,
|
value: prefillForm ? autoLogin.email : undefined,
|
||||||
password: autoLogin.password,
|
valid: true,
|
||||||
}
|
},
|
||||||
: undefined
|
password: {
|
||||||
}
|
initialValue: prefillForm ? autoLogin.password : undefined,
|
||||||
|
value: prefillForm ? autoLogin.password : undefined,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
}}
|
||||||
method="POST"
|
method="POST"
|
||||||
redirect={`${admin}${searchParams?.redirect || ''}`}
|
redirect={`${admin}${searchParams?.redirect || ''}`}
|
||||||
waitForAutocomplete
|
waitForAutocomplete
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use client'
|
||||||
import type { LinkProps } from 'react-router-dom'
|
import type { LinkProps } from 'react-router-dom'
|
||||||
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
@@ -6,6 +7,7 @@ import Link from 'next/link' // TODO: abstract this out to support all routers
|
|||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'popup-button-list'
|
const baseClass = 'popup-button-list'
|
||||||
|
|
||||||
export const ButtonGroup: React.FC<{
|
export const ButtonGroup: React.FC<{
|
||||||
buttonSize?: 'default' | 'small'
|
buttonSize?: 'default' | 'small'
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
@@ -31,6 +33,7 @@ type MenuButtonProps = {
|
|||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
to?: LinkProps['to']
|
to?: LinkProps['to']
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Button: React.FC<MenuButtonProps> = ({
|
export const Button: React.FC<MenuButtonProps> = ({
|
||||||
id,
|
id,
|
||||||
active,
|
active,
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ export { useLocale } from '../providers/Locale'
|
|||||||
export { useActions } from '../providers/ActionsProvider'
|
export { useActions } from '../providers/ActionsProvider'
|
||||||
export { useAuth } from '../providers/Auth'
|
export { useAuth } from '../providers/Auth'
|
||||||
export { useDocumentInfo } from '../providers/DocumentInfo'
|
export { useDocumentInfo } from '../providers/DocumentInfo'
|
||||||
|
export { DocumentInfoProvider } from '../providers/DocumentInfo'
|
||||||
|
|||||||
@@ -45,12 +45,12 @@ export const addFieldStatePromise = async ({
|
|||||||
user,
|
user,
|
||||||
}: Args): Promise<void> => {
|
}: Args): Promise<void> => {
|
||||||
if (fieldAffectsData(field)) {
|
if (fieldAffectsData(field)) {
|
||||||
|
const validate = operation === 'update' ? field.validate : undefined
|
||||||
|
|
||||||
const fieldState: FormField = {
|
const fieldState: FormField = {
|
||||||
// condition: field.admin?.condition,
|
|
||||||
initialValue: undefined,
|
initialValue: undefined,
|
||||||
passesCondition,
|
passesCondition,
|
||||||
valid: true,
|
valid: true,
|
||||||
// validate: field.validate,
|
|
||||||
value: undefined,
|
value: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,18 +67,18 @@ export const addFieldStatePromise = async ({
|
|||||||
|
|
||||||
let validationResult: boolean | string = true
|
let validationResult: boolean | string = true
|
||||||
|
|
||||||
// if (typeof fieldState.validate === 'function') {
|
if (typeof validate === 'function') {
|
||||||
// validationResult = await fieldState.validate(data?.[field.name], {
|
validationResult = await validate(data?.[field.name], {
|
||||||
// ...field,
|
...field,
|
||||||
// id,
|
id,
|
||||||
// config,
|
config,
|
||||||
// data: fullData,
|
data: fullData,
|
||||||
// operation,
|
operation,
|
||||||
// siblingData: data,
|
siblingData: data,
|
||||||
// t,
|
t,
|
||||||
// user,
|
user,
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (typeof validationResult === 'string') {
|
if (typeof validationResult === 'string') {
|
||||||
fieldState.errorMessage = validationResult
|
fieldState.errorMessage = validationResult
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import equal from 'deep-equal'
|
|||||||
import type { FieldAction, Fields, FormField } from './types'
|
import type { FieldAction, Fields, FormField } from './types'
|
||||||
|
|
||||||
import { deepCopyObject } from 'payload/utilities'
|
import { deepCopyObject } from 'payload/utilities'
|
||||||
import getSiblingData from './getSiblingData'
|
|
||||||
import reduceFieldsToValues from './reduceFieldsToValues'
|
|
||||||
import { flattenRows, separateRows } from './rows'
|
import { flattenRows, separateRows } from './rows'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,53 +40,14 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
|
|||||||
return newState
|
return newState
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'MODIFY_CONDITION': {
|
|
||||||
const { path, result, user } = action
|
|
||||||
|
|
||||||
return Object.entries(state).reduce((newState, [fieldPath, field]) => {
|
|
||||||
if (fieldPath === path || fieldPath.indexOf(`${path}.`) === 0) {
|
|
||||||
let passesCondition = result
|
|
||||||
|
|
||||||
// If a condition is being set to true,
|
|
||||||
// Set all conditions to true
|
|
||||||
// Besides those who still fail their own conditions
|
|
||||||
|
|
||||||
if (passesCondition && field.condition) {
|
|
||||||
passesCondition = field.condition(
|
|
||||||
reduceFieldsToValues(state, true),
|
|
||||||
getSiblingData(state, path),
|
|
||||||
{ user },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...newState,
|
|
||||||
[fieldPath]: {
|
|
||||||
...field,
|
|
||||||
passesCondition,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...newState,
|
|
||||||
[fieldPath]: {
|
|
||||||
...field,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'UPDATE': {
|
case 'UPDATE': {
|
||||||
const newField = Object.entries(action).reduce(
|
const newField = Object.entries(action).reduce(
|
||||||
(field, [key, value]) => {
|
(field, [key, value]) => {
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
'condition',
|
|
||||||
'disableFormData',
|
'disableFormData',
|
||||||
'errorMessage',
|
'errorMessage',
|
||||||
'initialValue',
|
'initialValue',
|
||||||
'passesCondition',
|
|
||||||
'rows',
|
'rows',
|
||||||
'valid',
|
'valid',
|
||||||
'validate',
|
'validate',
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import { useLocale } from '../../providers/Locale'
|
|||||||
import { useOperation } from '../../providers/OperationProvider'
|
import { useOperation } from '../../providers/OperationProvider'
|
||||||
import { WatchFormErrors } from './WatchFormErrors'
|
import { WatchFormErrors } from './WatchFormErrors'
|
||||||
import { buildFieldSchemaMap } from './buildFieldSchemaMap'
|
import { buildFieldSchemaMap } from './buildFieldSchemaMap'
|
||||||
import buildInitialState from './buildInitialState'
|
|
||||||
import buildStateFromSchema from './buildStateFromSchema'
|
import buildStateFromSchema from './buildStateFromSchema'
|
||||||
import {
|
import {
|
||||||
FormContext,
|
FormContext,
|
||||||
@@ -51,7 +50,7 @@ import reduceFieldsToValues from './reduceFieldsToValues'
|
|||||||
const baseClass = 'form'
|
const baseClass = 'form'
|
||||||
|
|
||||||
const Form: React.FC<Props> = (props) => {
|
const Form: React.FC<Props> = (props) => {
|
||||||
const { id, collection, getDocPreferences, global } = useDocumentInfo()
|
const { id, collectionSlug, getDocPreferences, globalSlug } = useDocumentInfo()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
action,
|
action,
|
||||||
@@ -59,9 +58,9 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
className,
|
className,
|
||||||
disableSuccessStatus,
|
disableSuccessStatus,
|
||||||
disabled,
|
disabled,
|
||||||
fields: fieldsFromProps = collection?.fields || global?.fields,
|
fields: fieldsFromProps,
|
||||||
|
// fields: fieldsFromProps = collection?.fields || global?.fields,
|
||||||
handleResponse,
|
handleResponse,
|
||||||
initialData, // values only, paths are required as key - form should build initial state as convenience
|
|
||||||
initialState, // fully formed initial field state
|
initialState, // fully formed initial field state
|
||||||
method,
|
method,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
@@ -83,17 +82,10 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
const [modified, setModified] = useState(false)
|
const [modified, setModified] = useState(false)
|
||||||
const [processing, setProcessing] = useState(false)
|
const [processing, setProcessing] = useState(false)
|
||||||
const [submitted, setSubmitted] = useState(false)
|
const [submitted, setSubmitted] = useState(false)
|
||||||
const [formattedInitialData, setFormattedInitialData] = useState(buildInitialState(initialData))
|
|
||||||
|
|
||||||
const formRef = useRef<HTMLFormElement>(null)
|
const formRef = useRef<HTMLFormElement>(null)
|
||||||
const contextRef = useRef({} as FormContextType)
|
const contextRef = useRef({} as FormContextType)
|
||||||
|
|
||||||
let initialFieldState = {}
|
const fieldsReducer = useReducer(fieldReducer, {}, () => initialState)
|
||||||
|
|
||||||
if (formattedInitialData) initialFieldState = formattedInitialData
|
|
||||||
if (initialState) initialFieldState = initialState
|
|
||||||
|
|
||||||
const fieldsReducer = useReducer(fieldReducer, {}, () => initialFieldState)
|
|
||||||
/**
|
/**
|
||||||
* `fields` is the current, up-to-date state/data of all fields in the form. It can be modified by using dispatchFields,
|
* `fields` is the current, up-to-date state/data of all fields in the form. It can be modified by using dispatchFields,
|
||||||
* which calls the fieldReducer, which then updates the state.
|
* which calls the fieldReducer, which then updates the state.
|
||||||
@@ -642,15 +634,6 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
}, [initialState, dispatchFields])
|
}, [initialState, dispatchFields])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialData) {
|
|
||||||
contextRef.current = { ...initContextState } as FormContextType
|
|
||||||
const builtState = buildInitialState(initialData)
|
|
||||||
setFormattedInitialData(builtState)
|
|
||||||
dispatchFields({ state: builtState, type: 'REPLACE_STATE' })
|
|
||||||
}
|
|
||||||
}, [initialData, dispatchFields])
|
|
||||||
|
|
||||||
useThrottledEffect(
|
useThrottledEffect(
|
||||||
() => {
|
() => {
|
||||||
refreshCookie()
|
refreshCookie()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type React from 'react'
|
|||||||
import type { Dispatch } from 'react'
|
import type { Dispatch } from 'react'
|
||||||
|
|
||||||
import type { User } from 'payload/auth'
|
import type { User } from 'payload/auth'
|
||||||
import type { Condition, Field, Field as FieldConfig, Validate } from 'payload/types'
|
import type { Field, Field as FieldConfig, Validate } from 'payload/types'
|
||||||
|
|
||||||
export type Row = {
|
export type Row = {
|
||||||
blockType?: string
|
blockType?: string
|
||||||
@@ -12,15 +12,14 @@ export type Row = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type FormField = {
|
export type FormField = {
|
||||||
// condition?: Condition
|
|
||||||
disableFormData?: boolean
|
disableFormData?: boolean
|
||||||
errorMessage?: string
|
errorMessage?: string
|
||||||
initialValue: unknown
|
initialValue: unknown
|
||||||
passesCondition?: boolean
|
passesCondition?: boolean
|
||||||
rows?: Row[]
|
rows?: Row[]
|
||||||
valid: boolean
|
valid: boolean
|
||||||
// validate?: Validate
|
|
||||||
value: unknown
|
value: unknown
|
||||||
|
validate?: Validate
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Fields = {
|
export type Fields = {
|
||||||
@@ -48,7 +47,6 @@ export type Props = {
|
|||||||
*/
|
*/
|
||||||
fields?: Field[]
|
fields?: Field[]
|
||||||
handleResponse?: (res: Response) => void
|
handleResponse?: (res: Response) => void
|
||||||
initialData?: Data
|
|
||||||
initialState?: Fields
|
initialState?: Fields
|
||||||
log?: boolean
|
log?: boolean
|
||||||
method?: 'DELETE' | 'GET' | 'PATCH' | 'POST'
|
method?: 'DELETE' | 'GET' | 'PATCH' | 'POST'
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ const RenderFields: React.FC<Props> = (props) => {
|
|||||||
permissions: fieldPermissions,
|
permissions: fieldPermissions,
|
||||||
data,
|
data,
|
||||||
user,
|
user,
|
||||||
|
valid: fieldState?.valid,
|
||||||
|
errorMessage: fieldState?.errorMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field) {
|
if (field) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { array } from 'payload/fields/validations'
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import { scrollToID } from '../../../utilities/scrollToID'
|
import { scrollToID } from '../../../utilities/scrollToID'
|
||||||
import Banner from '../../../elements/Banner'
|
import Banner from '../../../elements/Banner'
|
||||||
@@ -40,7 +39,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
|||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
permissions,
|
permissions,
|
||||||
required,
|
required,
|
||||||
validate = array,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
@@ -93,7 +92,6 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
|||||||
valid,
|
valid,
|
||||||
value,
|
value,
|
||||||
} = useField<number>({
|
} = useField<number>({
|
||||||
condition,
|
|
||||||
hasRows: true,
|
hasRows: true,
|
||||||
path,
|
path,
|
||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { blocks as blocksValidator } from 'payload/fields/validations'
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import { scrollToID } from '../../../utilities/scrollToID'
|
import { scrollToID } from '../../../utilities/scrollToID'
|
||||||
import Banner from '../../../elements/Banner'
|
import Banner from '../../../elements/Banner'
|
||||||
@@ -34,7 +33,7 @@ const BlocksField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
admin: { className, condition, description, readOnly },
|
admin: { className, description, readOnly },
|
||||||
blocks,
|
blocks,
|
||||||
fieldTypes,
|
fieldTypes,
|
||||||
forceRender = false,
|
forceRender = false,
|
||||||
@@ -47,7 +46,7 @@ const BlocksField: React.FC<Props> = (props) => {
|
|||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
permissions,
|
permissions,
|
||||||
required,
|
required,
|
||||||
validate = blocksValidator,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
@@ -92,7 +91,6 @@ const BlocksField: React.FC<Props> = (props) => {
|
|||||||
valid,
|
valid,
|
||||||
value,
|
value,
|
||||||
} = useField<number>({
|
} = useField<number>({
|
||||||
condition,
|
|
||||||
hasRows: true,
|
hasRows: true,
|
||||||
path,
|
path,
|
||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useCallback } from 'react'
|
import React, { Fragment, useCallback } from 'react'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
const baseClass = 'checkbox-input'
|
import { Check } from '../../../icons/Check'
|
||||||
|
import { Line } from '../../../icons/Line'
|
||||||
|
|
||||||
type CheckboxInputProps = {
|
type CheckboxInputProps = {
|
||||||
'aria-label'?: string
|
'aria-label'?: string
|
||||||
@@ -15,10 +15,12 @@ type CheckboxInputProps = {
|
|||||||
label?: string
|
label?: string
|
||||||
name?: string
|
name?: string
|
||||||
onChange?: (value: boolean) => void
|
onChange?: (value: boolean) => void
|
||||||
partialChecked?: boolean
|
|
||||||
readOnly?: boolean
|
readOnly?: boolean
|
||||||
required?: boolean
|
required?: boolean
|
||||||
path: string
|
path: string
|
||||||
|
validate?: Validate
|
||||||
|
partialChecked?: boolean
|
||||||
|
iconClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
|
export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
|
||||||
@@ -28,24 +30,27 @@ export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
|
|||||||
'aria-label': ariaLabel,
|
'aria-label': ariaLabel,
|
||||||
checked: checkedFromProps,
|
checked: checkedFromProps,
|
||||||
className,
|
className,
|
||||||
|
iconClassName,
|
||||||
inputRef,
|
inputRef,
|
||||||
onChange: onChangeFromProps,
|
onChange: onChangeFromProps,
|
||||||
readOnly,
|
readOnly,
|
||||||
required,
|
required,
|
||||||
path,
|
path,
|
||||||
|
validate,
|
||||||
|
partialChecked,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
// (value, options) => {
|
(value, options) => {
|
||||||
// return validate(value, { ...options, required })
|
if (typeof validate === 'function') return validate(value, { ...options, required })
|
||||||
// },
|
},
|
||||||
// [validate, required],
|
[validate, required],
|
||||||
// )
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField({
|
const { setValue, value } = useField({
|
||||||
// disableFormData,
|
// disableFormData,
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
const onToggle = useCallback(() => {
|
const onToggle = useCallback(() => {
|
||||||
@@ -58,17 +63,27 @@ export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
|
|||||||
const checked = checkedFromProps || Boolean(value)
|
const checked = checkedFromProps || Boolean(value)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<Fragment>
|
||||||
className={className}
|
<input
|
||||||
aria-label={ariaLabel}
|
className={className}
|
||||||
defaultChecked={checked}
|
aria-label={ariaLabel}
|
||||||
disabled={readOnly}
|
defaultChecked={Boolean(checked)}
|
||||||
id={id}
|
disabled={readOnly}
|
||||||
name={name}
|
id={id}
|
||||||
onInput={onToggle}
|
name={name}
|
||||||
ref={inputRef}
|
onInput={onToggle}
|
||||||
type="checkbox"
|
ref={inputRef}
|
||||||
required={required}
|
type="checkbox"
|
||||||
/>
|
required={required}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={[iconClassName, !value && partialChecked ? 'check' : 'partial']
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
{value && <Check />}
|
||||||
|
{!value && partialChecked && <Line />}
|
||||||
|
</span>
|
||||||
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
30
packages/ui/src/forms/field-types/Checkbox/Wrapper.tsx
Normal file
30
packages/ui/src/forms/field-types/Checkbox/Wrapper.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
import { useFormFields } from '../../Form/context'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
export const CheckboxWrapper: React.FC<{
|
||||||
|
path: string
|
||||||
|
children: React.ReactNode
|
||||||
|
readOnly?: boolean
|
||||||
|
baseClass?: string
|
||||||
|
}> = (props) => {
|
||||||
|
const { path, children, readOnly, baseClass } = props
|
||||||
|
|
||||||
|
const { value: checked } = useFormFields(([fields]) => fields[path])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
baseClass,
|
||||||
|
checked && `${baseClass}--checked`,
|
||||||
|
readOnly && `${baseClass}--read-only`,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,15 +2,16 @@ import React from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { checkbox } from 'payload/fields/validations'
|
|
||||||
import DefaultError from '../../Error'
|
import DefaultError from '../../Error'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
import { fieldBaseClass } from '../shared'
|
import { fieldBaseClass } from '../shared'
|
||||||
import { CheckboxInput } from './Input'
|
import { CheckboxInput } from './Input'
|
||||||
import DefaultLabel from '../../Label'
|
import DefaultLabel from '../../Label'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { CheckboxWrapper } from './Wrapper'
|
||||||
|
|
||||||
const baseClass = 'checkbox'
|
const baseClass = 'checkbox'
|
||||||
|
const inputBaseClass = 'checkbox-input'
|
||||||
|
|
||||||
const Checkbox: React.FC<Props> = (props) => {
|
const Checkbox: React.FC<Props> = (props) => {
|
||||||
const {
|
const {
|
||||||
@@ -25,10 +26,11 @@ const Checkbox: React.FC<Props> = (props) => {
|
|||||||
} = {},
|
} = {},
|
||||||
disableFormData,
|
disableFormData,
|
||||||
label,
|
label,
|
||||||
onChange,
|
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = checkbox,
|
valid,
|
||||||
|
errorMessage,
|
||||||
|
value,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
@@ -43,9 +45,9 @@ const Checkbox: React.FC<Props> = (props) => {
|
|||||||
className={[
|
className={[
|
||||||
fieldBaseClass,
|
fieldBaseClass,
|
||||||
baseClass,
|
baseClass,
|
||||||
// showError && 'error',
|
!valid && 'error',
|
||||||
className,
|
className,
|
||||||
// value && `${baseClass}--checked`,
|
value && `${baseClass}--checked`,
|
||||||
readOnly && `${baseClass}--read-only`,
|
readOnly && `${baseClass}--read-only`,
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -56,23 +58,10 @@ const Checkbox: React.FC<Props> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__error-wrap`}>
|
<div className={`${baseClass}__error-wrap`}>
|
||||||
<ErrorComp
|
<ErrorComp alignCaret="left" message={errorMessage} showError={!valid} />
|
||||||
alignCaret="left"
|
|
||||||
// message={errorMessage}
|
|
||||||
// showError={showError}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<CheckboxWrapper path={path} readOnly={readOnly} baseClass={inputBaseClass}>
|
||||||
className={[
|
<div className={`${inputBaseClass}__input`}>
|
||||||
baseClass,
|
|
||||||
className,
|
|
||||||
// (checked || partialChecked) && `${baseClass}--checked`,
|
|
||||||
readOnly && `${baseClass}--read-only`,
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(' ')}
|
|
||||||
>
|
|
||||||
<div className={`${baseClass}__input`}>
|
|
||||||
{Array.isArray(beforeInput) && beforeInput.map((Component, i) => <Component key={i} />)}
|
{Array.isArray(beforeInput) && beforeInput.map((Component, i) => <Component key={i} />)}
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
id={fieldID}
|
id={fieldID}
|
||||||
@@ -82,20 +71,13 @@ const Checkbox: React.FC<Props> = (props) => {
|
|||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
required={required}
|
required={required}
|
||||||
path={path}
|
path={path}
|
||||||
|
iconClassName={`${inputBaseClass}__icon`}
|
||||||
/>
|
/>
|
||||||
{Array.isArray(afterInput) && afterInput.map((Component, i) => <Component key={i} />)}
|
{Array.isArray(afterInput) && afterInput.map((Component, i) => <Component key={i} />)}
|
||||||
{/* <span className={`${baseClass}__icon ${!partialChecked ? 'check' : 'partial'}`}>
|
|
||||||
{!partialChecked && <Check />}
|
|
||||||
{partialChecked && <Line />}
|
|
||||||
</span> */}
|
|
||||||
</div>
|
</div>
|
||||||
{label && <LabelComp htmlFor={fieldID} label={label} required={required} />}
|
{label && <LabelComp htmlFor={fieldID} label={label} required={required} />}
|
||||||
</div>
|
</CheckboxWrapper>
|
||||||
<FieldDescription
|
<FieldDescription description={description} path={path} value={value} />
|
||||||
description={description}
|
|
||||||
path={path}
|
|
||||||
// value={value}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { CheckboxField } from 'payload/types'
|
import type { CheckboxField } from 'payload/types'
|
||||||
|
import { FormFieldBase } from '../Text/types'
|
||||||
|
|
||||||
export type Props = Omit<CheckboxField, 'type'> & {
|
export type Props = FormFieldBase &
|
||||||
disableFormData?: boolean
|
Omit<CheckboxField, 'type'> & {
|
||||||
onChange?: (val: boolean) => void
|
disableFormData?: boolean
|
||||||
path?: string
|
onChange?: (val: boolean) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { useCallback } from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { code } from 'payload/fields/validations'
|
|
||||||
import { CodeEditor } from '../../../elements/CodeEditor'
|
import { CodeEditor } from '../../../elements/CodeEditor'
|
||||||
import DefaultError from '../../Error'
|
import DefaultError from '../../Error'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
@@ -35,7 +34,7 @@ const Code: React.FC<Props> = (props) => {
|
|||||||
label,
|
label,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = code,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const ErrorComp = Error || DefaultError
|
const ErrorComp = Error || DefaultError
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import type { DateField } from 'payload/types'
|
import type { DateField, Validate } from 'payload/types'
|
||||||
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import DatePicker from '../../../elements/DatePicker'
|
import DatePicker from '../../../elements/DatePicker'
|
||||||
@@ -18,21 +18,20 @@ export type DateTimeInputProps = Omit<DateField, 'admin' | 'name' | 'type'> & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DateTimeInput: React.FC<DateTimeInputProps> = (props) => {
|
export const DateTimeInput: React.FC<DateTimeInputProps> = (props) => {
|
||||||
const { path, readOnly, placeholder, datePickerProps, style, width } = props
|
const { path, readOnly, placeholder, datePickerProps, style, width, validate, required } = props
|
||||||
|
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
// (value, options) => {
|
(value, options) => {
|
||||||
// return validate(value, { ...options, required })
|
if (typeof validate === 'function') return validate(value, { ...options, required })
|
||||||
// },
|
},
|
||||||
// [validate, required],
|
[validate, required],
|
||||||
// )
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField<Date>({
|
const { errorMessage, setValue, showError, value } = useField<Date>({
|
||||||
// condition,
|
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { date as dateValidation } from 'payload/fields/validations'
|
|
||||||
import { DateTimeInput } from './Input'
|
import { DateTimeInput } from './Input'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
@@ -28,7 +27,6 @@ const DateTime: React.FC<Props> = (props) => {
|
|||||||
label,
|
label,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = dateValidation,
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
|
|
||||||
export const EmailInput: React.FC<{
|
export const EmailInput: React.FC<{
|
||||||
name: string
|
name: string
|
||||||
@@ -13,6 +14,7 @@ export const EmailInput: React.FC<{
|
|||||||
path: string
|
path: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
placeholder?: Record<string, string> | string
|
placeholder?: Record<string, string> | string
|
||||||
|
validate?: Validate
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
@@ -20,7 +22,7 @@ export const EmailInput: React.FC<{
|
|||||||
readOnly,
|
readOnly,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
// validate = email,
|
validate,
|
||||||
placeholder,
|
placeholder,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
@@ -28,12 +30,12 @@ export const EmailInput: React.FC<{
|
|||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
// (value, options) => {
|
(value, options) => {
|
||||||
// return validate(value, { ...options, required })
|
if (typeof validate === 'function') return validate(value, { ...options, required })
|
||||||
// },
|
},
|
||||||
// [validate, required],
|
[validate, required],
|
||||||
// )
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
// errorMessage,
|
// errorMessage,
|
||||||
@@ -42,7 +44,7 @@ export const EmailInput: React.FC<{
|
|||||||
value,
|
value,
|
||||||
} = useField({
|
} = useField({
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { email } from 'payload/fields/validations'
|
|
||||||
import DefaultError from '../../Error'
|
import DefaultError from '../../Error'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
import DefaultLabel from '../../Label'
|
import DefaultLabel from '../../Label'
|
||||||
@@ -25,7 +24,6 @@ export const Email: React.FC<Props> = (props) => {
|
|||||||
label,
|
label,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = email,
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
@@ -59,7 +57,6 @@ export const Email: React.FC<Props> = (props) => {
|
|||||||
<EmailInput
|
<EmailInput
|
||||||
name={name}
|
name={name}
|
||||||
autoComplete={autoComplete}
|
autoComplete={autoComplete}
|
||||||
// condition={condition}
|
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
path={path}
|
path={path}
|
||||||
required={required}
|
required={required}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { useCallback, useEffect, useState } from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { json } from 'payload/fields/validations'
|
|
||||||
import { CodeEditor } from '../../../elements/CodeEditor'
|
import { CodeEditor } from '../../../elements/CodeEditor'
|
||||||
import DefaultError from '../../Error'
|
import DefaultError from '../../Error'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
@@ -11,6 +10,7 @@ import DefaultLabel from '../../Label'
|
|||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import { fieldBaseClass } from '../shared'
|
import { fieldBaseClass } from '../shared'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
|
|
||||||
const baseClass = 'json-field'
|
const baseClass = 'json-field'
|
||||||
|
|
||||||
@@ -20,7 +20,6 @@ const JSONField: React.FC<Props> = (props) => {
|
|||||||
admin: {
|
admin: {
|
||||||
className,
|
className,
|
||||||
components: { Error, Label } = {},
|
components: { Error, Label } = {},
|
||||||
condition,
|
|
||||||
description,
|
description,
|
||||||
editorOptions,
|
editorOptions,
|
||||||
readOnly,
|
readOnly,
|
||||||
@@ -30,7 +29,7 @@ const JSONField: React.FC<Props> = (props) => {
|
|||||||
label,
|
label,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = json,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const ErrorComp = Error || DefaultError
|
const ErrorComp = Error || DefaultError
|
||||||
@@ -41,15 +40,15 @@ const JSONField: React.FC<Props> = (props) => {
|
|||||||
const [jsonError, setJsonError] = useState<string>()
|
const [jsonError, setJsonError] = useState<string>()
|
||||||
const [hasLoadedValue, setHasLoadedValue] = useState(false)
|
const [hasLoadedValue, setHasLoadedValue] = useState(false)
|
||||||
|
|
||||||
const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
(value, options) => {
|
(value, options) => {
|
||||||
return validate(value, { ...options, jsonError, required })
|
if (typeof validate === 'function')
|
||||||
|
return validate(value, { ...options, jsonError, required })
|
||||||
},
|
},
|
||||||
[validate, required, jsonError],
|
[validate, required],
|
||||||
)
|
)
|
||||||
|
|
||||||
const { errorMessage, initialValue, setValue, showError, value } = useField<string>({
|
const { errorMessage, initialValue, setValue, showError, value } = useField<string>({
|
||||||
condition,
|
|
||||||
path,
|
path,
|
||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
|
|
||||||
export const NumberInput: React.FC<{
|
export const NumberInput: React.FC<{
|
||||||
path: string
|
path: string
|
||||||
@@ -16,6 +17,7 @@ export const NumberInput: React.FC<{
|
|||||||
step?: number
|
step?: number
|
||||||
hasMany?: boolean
|
hasMany?: boolean
|
||||||
name?: string
|
name?: string
|
||||||
|
validate?: Validate
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
@@ -27,23 +29,23 @@ export const NumberInput: React.FC<{
|
|||||||
min,
|
min,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const memoizedValidate = useCallback(
|
||||||
// (value, options) => {
|
(value, options) => {
|
||||||
// return validate(value, { ...options, max, min, required })
|
return validate(value, { ...options, max, min, required })
|
||||||
// },
|
},
|
||||||
// [validate, min, max, required],
|
[validate, min, max, required],
|
||||||
// )
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField<number | number[]>({
|
const { errorMessage, setValue, showError, value } = useField<number | number[]>({
|
||||||
// condition,
|
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import type { Option } from '../../../elements/ReactSelect/types'
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { number } from 'payload/fields/validations'
|
|
||||||
import { isNumber } from 'payload/utilities'
|
import { isNumber } from 'payload/utilities'
|
||||||
import ReactSelect from '../../../elements/ReactSelect'
|
import ReactSelect from '../../../elements/ReactSelect'
|
||||||
import DefaultError from '../../Error'
|
import DefaultError from '../../Error'
|
||||||
@@ -36,7 +34,7 @@ const NumberField: React.FC<Props> = (props) => {
|
|||||||
minRows,
|
minRows,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = number,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const ErrorComp = Error || DefaultError
|
const ErrorComp = Error || DefaultError
|
||||||
@@ -44,10 +42,16 @@ const NumberField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|
||||||
|
const memoizedValidate = React.useCallback(
|
||||||
|
(value, options) => {
|
||||||
|
return validate(value, { ...options, required })
|
||||||
|
},
|
||||||
|
[validate, required],
|
||||||
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField<number | number[]>({
|
const { errorMessage, setValue, showError, value } = useField<number | number[]>({
|
||||||
condition,
|
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
|
|
||||||
import type { Props } from './types'
|
|
||||||
|
|
||||||
import { password } from 'payload/fields/validations'
|
|
||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
|
|
||||||
export const PasswordInput: React.FC<{
|
export const PasswordInput: React.FC<{
|
||||||
name: string
|
name: string
|
||||||
@@ -13,28 +11,22 @@ export const PasswordInput: React.FC<{
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
path: string
|
path: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
validate?: Validate
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const {
|
const { name, autoComplete, disabled, path: pathFromProps, required, validate } = props
|
||||||
name,
|
|
||||||
autoComplete,
|
|
||||||
disabled,
|
|
||||||
path: pathFromProps,
|
|
||||||
// required,
|
|
||||||
} = props
|
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
// (value, options) => {
|
(value, options) => {
|
||||||
// const validationResult = validate(value, { ...options, required })
|
if (typeof validate === 'function') return validate(value, { ...options, required })
|
||||||
// return validationResult
|
},
|
||||||
// },
|
[validate, required],
|
||||||
// [validate, required],
|
)
|
||||||
// )
|
|
||||||
|
|
||||||
const { errorMessage, formProcessing, setValue, showError, value } = useField({
|
const { errorMessage, formProcessing, setValue, showError, value } = useField({
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { password } from 'payload/fields/validations'
|
|
||||||
import Error from '../../Error'
|
import Error from '../../Error'
|
||||||
import Label from '../../Label'
|
import Label from '../../Label'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
@@ -19,7 +18,6 @@ export const Password: React.FC<Props> = (props) => {
|
|||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
style,
|
style,
|
||||||
validate = password,
|
|
||||||
width,
|
width,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { point } from 'payload/fields/validations'
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import DefaultError from '../../Error'
|
import DefaultError from '../../Error'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
@@ -12,6 +11,7 @@ import DefaultLabel from '../../Label'
|
|||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import { fieldBaseClass } from '../shared'
|
import { fieldBaseClass } from '../shared'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
|
|
||||||
const baseClass = 'point'
|
const baseClass = 'point'
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ const PointField: React.FC<Props> = (props) => {
|
|||||||
label,
|
label,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = point,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const ErrorComp = Error || DefaultError
|
const ErrorComp = Error || DefaultError
|
||||||
@@ -42,9 +42,9 @@ const PointField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const { i18n, t } = useTranslation('fields')
|
const { i18n, t } = useTranslation('fields')
|
||||||
|
|
||||||
const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
(value, options) => {
|
(value, options) => {
|
||||||
return validate(value, { ...options, required })
|
if (typeof validate === 'function') return validate(value, { ...options, required })
|
||||||
},
|
},
|
||||||
[validate, required],
|
[validate, required],
|
||||||
)
|
)
|
||||||
@@ -55,7 +55,6 @@ const PointField: React.FC<Props> = (props) => {
|
|||||||
showError,
|
showError,
|
||||||
value = [null, null],
|
value = [null, null],
|
||||||
} = useField<[number, number]>({
|
} = useField<[number, number]>({
|
||||||
condition,
|
|
||||||
path,
|
path,
|
||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { useCallback } from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { radio } from 'payload/fields/validations'
|
|
||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import RadioGroupInput from './Input'
|
import RadioGroupInput from './Input'
|
||||||
|
|
||||||
@@ -24,20 +23,20 @@ const RadioGroup: React.FC<Props> = (props) => {
|
|||||||
options,
|
options,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = radio,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|
||||||
const memoizedValidate = useCallback(
|
const memoizedValidate = useCallback(
|
||||||
(value, validationOptions) => {
|
(value, validationOptions) => {
|
||||||
return validate(value, { ...validationOptions, options, required })
|
if (typeof validate === 'function')
|
||||||
|
return validate(value, { ...validationOptions, options, required })
|
||||||
},
|
},
|
||||||
[validate, options, required],
|
[validate, options, required],
|
||||||
)
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField<string>({
|
const { errorMessage, setValue, showError, value } = useField<string>({
|
||||||
condition,
|
|
||||||
path,
|
path,
|
||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import type { Where } from 'payload/types'
|
|||||||
import type { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types'
|
import type { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types'
|
||||||
import type { FilterOptionsResult, GetResults, Option, Props, Value } from './types'
|
import type { FilterOptionsResult, GetResults, Option, Props, Value } from './types'
|
||||||
|
|
||||||
import { relationship } from 'payload/fields/validations'
|
|
||||||
import { wordBoundariesRegex } from 'payload/utilities'
|
import { wordBoundariesRegex } from 'payload/utilities'
|
||||||
import { useDebouncedCallback } from '../../../hooks/useDebouncedCallback'
|
import { useDebouncedCallback } from '../../../hooks/useDebouncedCallback'
|
||||||
import ReactSelect from '../../../elements/ReactSelect'
|
import ReactSelect from '../../../elements/ReactSelect'
|
||||||
@@ -55,7 +54,7 @@ const Relationship: React.FC<Props> = (props) => {
|
|||||||
path,
|
path,
|
||||||
relationTo,
|
relationTo,
|
||||||
required,
|
required,
|
||||||
validate = relationship,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const ErrorComp = Error || DefaultError
|
const ErrorComp = Error || DefaultError
|
||||||
@@ -94,7 +93,6 @@ const Relationship: React.FC<Props> = (props) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const { errorMessage, initialValue, setValue, showError, value } = useField<Value | Value[]>({
|
const { errorMessage, initialValue, setValue, showError, value } = useField<Value | Value[]>({
|
||||||
condition,
|
|
||||||
path: pathOrName,
|
path: pathOrName,
|
||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import type { OptionObject } from 'payload/types'
|
import type { OptionObject, Validate } from 'payload/types'
|
||||||
import type { Option } from '../../../elements/ReactSelect/types'
|
import type { Option } from '../../../elements/ReactSelect/types'
|
||||||
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
@@ -17,19 +17,22 @@ const SelectInput: React.FC<{
|
|||||||
isSortable: boolean
|
isSortable: boolean
|
||||||
options: OptionObject[]
|
options: OptionObject[]
|
||||||
path: string
|
path: string
|
||||||
}> = ({ readOnly, isClearable, hasMany, isSortable, options, path }) => {
|
validate?: Validate
|
||||||
|
required?: boolean
|
||||||
|
}> = ({ readOnly, isClearable, hasMany, isSortable, options, path, validate, required }) => {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const memoizedValidate: Validate = useCallback(
|
||||||
// (value, validationOptions) => {
|
(value, validationOptions) => {
|
||||||
// return validate(value, { ...validationOptions, hasMany, options, required })
|
if (typeof validate === 'function')
|
||||||
// },
|
return validate(value, { ...validationOptions, hasMany, options, required })
|
||||||
// [validate, required, hasMany, options],
|
},
|
||||||
// )
|
[validate, required],
|
||||||
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField({
|
const { errorMessage, setValue, showError, value } = useField({
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
let valueToRender
|
let valueToRender
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export const Select: React.FC<Props> = (props) => {
|
|||||||
options,
|
options,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
// validate = select,
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import type { SanitizedConfig } from 'payload/types'
|
import type { SanitizedConfig, Validate } from 'payload/types'
|
||||||
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import { isFieldRTL } from '../shared'
|
import { isFieldRTL } from '../shared'
|
||||||
@@ -22,6 +22,8 @@ export const TextInput: React.FC<{
|
|||||||
rtl?: boolean
|
rtl?: boolean
|
||||||
maxLength?: number
|
maxLength?: number
|
||||||
minLength?: number
|
minLength?: number
|
||||||
|
validate?: Validate
|
||||||
|
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const {
|
const {
|
||||||
path,
|
path,
|
||||||
@@ -30,29 +32,30 @@ export const TextInput: React.FC<{
|
|||||||
localized,
|
localized,
|
||||||
localizationConfig,
|
localizationConfig,
|
||||||
rtl,
|
rtl,
|
||||||
// maxLength,
|
maxLength,
|
||||||
// minLength,
|
minLength,
|
||||||
|
validate,
|
||||||
|
required,
|
||||||
|
onKeyDown,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
const locale = useLocale()
|
const locale = useLocale()
|
||||||
|
|
||||||
const {
|
const memoizedValidate: Validate = useCallback(
|
||||||
// errorMessage,
|
(value, options) => {
|
||||||
setValue,
|
if (typeof validate === 'function')
|
||||||
// showError,
|
return validate(value, { ...options, maxLength, minLength, required })
|
||||||
value,
|
},
|
||||||
} = useField({
|
[validate, minLength, maxLength, required],
|
||||||
|
)
|
||||||
|
|
||||||
|
const field = useField({
|
||||||
path,
|
path,
|
||||||
// validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
// const memoizedValidate = useCallback(
|
const { setValue, value } = field
|
||||||
// (value, options) => {
|
|
||||||
// return validate(value, { ...options, maxLength, minLength, required })
|
|
||||||
// },
|
|
||||||
// [validate, minLength, maxLength, required],
|
|
||||||
// )
|
|
||||||
|
|
||||||
const renderRTL = isFieldRTL({
|
const renderRTL = isFieldRTL({
|
||||||
fieldLocalized: localized,
|
fieldLocalized: localized,
|
||||||
@@ -70,7 +73,7 @@ export const TextInput: React.FC<{
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue(e.target.value)
|
setValue(e.target.value)
|
||||||
}}
|
}}
|
||||||
// onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
// ref={inputRef}
|
// ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { text } from 'payload/fields/validations'
|
|
||||||
import { fieldBaseClass } from '../shared'
|
import { fieldBaseClass } from '../shared'
|
||||||
import { TextInput } from './Input'
|
import { TextInput } from './Input'
|
||||||
import FieldDescription from '../../FieldDescription'
|
import FieldDescription from '../../FieldDescription'
|
||||||
@@ -28,7 +27,8 @@ const Text: React.FC<Props> = (props) => {
|
|||||||
minLength,
|
minLength,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = text,
|
valid,
|
||||||
|
errorMessage,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const path = pathFromProps || name
|
const path = pathFromProps || name
|
||||||
@@ -38,12 +38,7 @@ const Text: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={[
|
className={[fieldBaseClass, 'text', className, !valid && 'error', readOnly && 'read-only']
|
||||||
fieldBaseClass,
|
|
||||||
'text',
|
|
||||||
className,
|
|
||||||
// showError && 'error', readOnly && 'read-only'
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
style={{
|
style={{
|
||||||
@@ -51,10 +46,7 @@ const Text: React.FC<Props> = (props) => {
|
|||||||
width,
|
width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ErrorComp
|
<ErrorComp message={errorMessage} showError={!valid} />
|
||||||
// message={errorMessage}
|
|
||||||
// showError={showError}
|
|
||||||
/>
|
|
||||||
<LabelComp htmlFor={`field-${path.replace(/\./g, '__')}`} label={label} required={required} />
|
<LabelComp htmlFor={`field-${path.replace(/\./g, '__')}`} label={label} required={required} />
|
||||||
<div className="input-wrapper">
|
<div className="input-wrapper">
|
||||||
{Array.isArray(beforeInput) && beforeInput.map((Component, i) => <Component key={i} />)}
|
{Array.isArray(beforeInput) && beforeInput.map((Component, i) => <Component key={i} />)}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import type { TextField } from 'payload/types'
|
import type { TextField } from 'payload/types'
|
||||||
|
|
||||||
export type Props = Omit<TextField, 'type'> & {
|
export type FormFieldBase = {
|
||||||
inputRef?: React.MutableRefObject<HTMLInputElement>
|
|
||||||
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>
|
|
||||||
path?: string
|
path?: string
|
||||||
value?: string
|
value?: string
|
||||||
|
valid?: boolean
|
||||||
|
errorMessage?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Props = FormFieldBase &
|
||||||
|
Omit<TextField, 'type'> & {
|
||||||
|
inputRef?: React.MutableRefObject<HTMLInputElement>
|
||||||
|
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { textarea } from 'payload/fields/validations'
|
|
||||||
import { getTranslation } from 'payload/utilities'
|
import { getTranslation } from 'payload/utilities'
|
||||||
import { useConfig } from '../../../providers/Config'
|
import { useConfig } from '../../../providers/Config'
|
||||||
import { useLocale } from '../../../providers/Locale'
|
import { useLocale } from '../../../providers/Locale'
|
||||||
@@ -12,6 +11,7 @@ import useField from '../../useField'
|
|||||||
import { isFieldRTL } from '../shared'
|
import { isFieldRTL } from '../shared'
|
||||||
import TextareaInput from './Input'
|
import TextareaInput from './Input'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { Validate } from 'payload/types'
|
||||||
|
|
||||||
const Textarea: React.FC<Props> = (props) => {
|
const Textarea: React.FC<Props> = (props) => {
|
||||||
const {
|
const {
|
||||||
@@ -33,7 +33,7 @@ const Textarea: React.FC<Props> = (props) => {
|
|||||||
minLength,
|
minLength,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
validate = textarea,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
@@ -49,11 +49,13 @@ const Textarea: React.FC<Props> = (props) => {
|
|||||||
locale,
|
locale,
|
||||||
localizationConfig: localization || undefined,
|
localizationConfig: localization || undefined,
|
||||||
})
|
})
|
||||||
const memoizedValidate = useCallback(
|
|
||||||
|
const memoizedValidate: Validate = useCallback(
|
||||||
(value, options) => {
|
(value, options) => {
|
||||||
return validate(value, { ...options, maxLength, minLength, required })
|
if (typeof validate === 'function')
|
||||||
|
return validate(value, { ...options, maxLength, minLength, required })
|
||||||
},
|
},
|
||||||
[validate, required, maxLength, minLength],
|
[validate, required],
|
||||||
)
|
)
|
||||||
|
|
||||||
const { errorMessage, setValue, showError, value } = useField({
|
const { errorMessage, setValue, showError, value } = useField({
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { useCallback } from 'react'
|
|||||||
|
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import { upload } from 'payload/fields/validations'
|
|
||||||
import { useConfig } from '../../../providers/Config'
|
import { useConfig } from '../../../providers/Config'
|
||||||
import useField from '../../useField'
|
import useField from '../../useField'
|
||||||
import UploadInput from './Input'
|
import UploadInput from './Input'
|
||||||
@@ -20,7 +19,6 @@ const Upload: React.FC<Props> = (props) => {
|
|||||||
name,
|
name,
|
||||||
admin: {
|
admin: {
|
||||||
className,
|
className,
|
||||||
condition,
|
|
||||||
description,
|
description,
|
||||||
readOnly,
|
readOnly,
|
||||||
style,
|
style,
|
||||||
@@ -33,14 +31,14 @@ const Upload: React.FC<Props> = (props) => {
|
|||||||
path,
|
path,
|
||||||
relationTo,
|
relationTo,
|
||||||
required,
|
required,
|
||||||
validate = upload,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const collection = collections.find((coll) => coll.slug === relationTo)
|
const collection = collections.find((coll) => coll.slug === relationTo)
|
||||||
|
|
||||||
const memoizedValidate = useCallback(
|
const memoizedValidate = useCallback(
|
||||||
(value, options) => {
|
(value, options) => {
|
||||||
return validate(value, { ...options, required })
|
if (typeof validate === 'function') return validate(value, { ...options, required })
|
||||||
},
|
},
|
||||||
[validate, required],
|
[validate, required],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -102,9 +102,9 @@ const useField = <T,>(options: Options): FieldType<T> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let errorMessage: string | undefined
|
let errorMessage: string | undefined
|
||||||
let valid: boolean | string = false
|
let valid: boolean | string = prevValid.current
|
||||||
|
|
||||||
const validationResult =
|
const isValid =
|
||||||
typeof validate === 'function'
|
typeof validate === 'function'
|
||||||
? await validate(valueToValidate, {
|
? await validate(valueToValidate, {
|
||||||
id,
|
id,
|
||||||
@@ -117,11 +117,11 @@ const useField = <T,>(options: Options): FieldType<T> => {
|
|||||||
})
|
})
|
||||||
: true
|
: true
|
||||||
|
|
||||||
if (typeof validationResult === 'string') {
|
if (typeof isValid === 'string') {
|
||||||
errorMessage = validationResult
|
|
||||||
valid = false
|
valid = false
|
||||||
} else {
|
errorMessage = isValid
|
||||||
valid = validationResult
|
} else if (typeof isValid === 'boolean') {
|
||||||
|
valid = isValid
|
||||||
errorMessage = undefined
|
errorMessage = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
setUser,
|
setUser,
|
||||||
token: tokenInMemory,
|
token: tokenInMemory,
|
||||||
user,
|
user,
|
||||||
|
setPermissions,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export type AuthContext<T = User> = {
|
|||||||
setUser: (user: T) => void
|
setUser: (user: T) => void
|
||||||
token?: string
|
token?: string
|
||||||
user?: T | null
|
user?: T | null
|
||||||
|
setPermissions: (permissions: Permissions) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useParams } from 'react-router-dom'
|
|
||||||
|
|
||||||
import type { TypeWithTimestamps } from 'payload/dist/collections/config/types'
|
import type { TypeWithTimestamps } from 'payload/dist/collections/config/types'
|
||||||
import type { PaginatedDocs } from 'payload/database'
|
import type { PaginatedDocs } from 'payload/database'
|
||||||
@@ -13,6 +12,7 @@ import { useAuth } from '../Auth'
|
|||||||
import { useConfig } from '../Config'
|
import { useConfig } from '../Config'
|
||||||
import { useLocale } from '../Locale'
|
import { useLocale } from '../Locale'
|
||||||
import { usePreferences } from '../Preferences'
|
import { usePreferences } from '../Preferences'
|
||||||
|
import { useParams } from 'next/navigation'
|
||||||
|
|
||||||
const Context = createContext({} as ContextType)
|
const Context = createContext({} as ContextType)
|
||||||
|
|
||||||
@@ -21,12 +21,14 @@ export const useDocumentInfo = (): ContextType => useContext(Context)
|
|||||||
export const DocumentInfoProvider: React.FC<Props> = ({
|
export const DocumentInfoProvider: React.FC<Props> = ({
|
||||||
id: idFromProps,
|
id: idFromProps,
|
||||||
children,
|
children,
|
||||||
collection,
|
collectionSlug,
|
||||||
global,
|
globalSlug,
|
||||||
idFromParams: getIDFromParams,
|
idFromParams: getIDFromParams,
|
||||||
|
draftsEnabled,
|
||||||
|
versionsEnabled,
|
||||||
}) => {
|
}) => {
|
||||||
const { id: idFromParams } = useParams<{ id: string }>()
|
const { id: idFromParams } = useParams()
|
||||||
const id = idFromProps || (getIDFromParams ? idFromParams : null)
|
const id = idFromProps || (getIDFromParams ? (idFromParams as string) : null)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
routes: { api },
|
routes: { api },
|
||||||
@@ -46,14 +48,14 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
let pluralType: 'collections' | 'globals'
|
let pluralType: 'collections' | 'globals'
|
||||||
let preferencesKey: string
|
let preferencesKey: string
|
||||||
|
|
||||||
if (global) {
|
if (globalSlug) {
|
||||||
slug = global.slug
|
slug = globalSlug
|
||||||
pluralType = 'globals'
|
pluralType = 'globals'
|
||||||
preferencesKey = `global-${slug}`
|
preferencesKey = `global-${slug}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collection) {
|
if (collectionSlug) {
|
||||||
slug = collection.slug
|
slug = collectionSlug
|
||||||
pluralType = 'collections'
|
pluralType = 'collections'
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
@@ -64,8 +66,7 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
const getVersions = useCallback(async () => {
|
const getVersions = useCallback(async () => {
|
||||||
let versionFetchURL
|
let versionFetchURL
|
||||||
let publishedFetchURL
|
let publishedFetchURL
|
||||||
let draftsEnabled = false
|
let shouldFetchVersions = versionsEnabled
|
||||||
let shouldFetchVersions = false
|
|
||||||
let unpublishedVersionJSON = null
|
let unpublishedVersionJSON = null
|
||||||
let versionJSON = null
|
let versionJSON = null
|
||||||
let shouldFetch = true
|
let shouldFetch = true
|
||||||
@@ -100,19 +101,13 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global) {
|
if (globalSlug) {
|
||||||
draftsEnabled = Boolean(global?.versions?.drafts)
|
versionFetchURL = `${baseURL}/globals/${globalSlug}/versions`
|
||||||
shouldFetchVersions = Boolean(global?.versions)
|
publishedFetchURL = `${baseURL}/globals/${globalSlug}?${qs.stringify(publishedVersionParams)}`
|
||||||
versionFetchURL = `${baseURL}/globals/${global.slug}/versions`
|
|
||||||
publishedFetchURL = `${baseURL}/globals/${global.slug}?${qs.stringify(
|
|
||||||
publishedVersionParams,
|
|
||||||
)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collection) {
|
if (collectionSlug) {
|
||||||
draftsEnabled = Boolean(collection?.versions?.drafts)
|
versionFetchURL = `${baseURL}/${collectionSlug}/versions`
|
||||||
shouldFetchVersions = Boolean(collection?.versions)
|
|
||||||
versionFetchURL = `${baseURL}/${collection.slug}/versions`
|
|
||||||
|
|
||||||
publishedVersionParams.where.and.push({
|
publishedVersionParams.where.and.push({
|
||||||
id: {
|
id: {
|
||||||
@@ -120,7 +115,7 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
publishedFetchURL = `${baseURL}/${collection.slug}?${qs.stringify(publishedVersionParams)}`
|
publishedFetchURL = `${baseURL}/${collectionSlug}?${qs.stringify(publishedVersionParams)}`
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
shouldFetch = false
|
shouldFetch = false
|
||||||
@@ -144,7 +139,7 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
}).then((res) => res.json())
|
}).then((res) => res.json())
|
||||||
|
|
||||||
if (collection) {
|
if (collectionSlug) {
|
||||||
publishedJSON = publishedJSON?.docs?.[0]
|
publishedJSON = publishedJSON?.docs?.[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +189,7 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
setVersions(versionJSON)
|
setVersions(versionJSON)
|
||||||
setUnpublishedVersions(unpublishedVersionJSON)
|
setUnpublishedVersions(unpublishedVersionJSON)
|
||||||
}
|
}
|
||||||
}, [i18n, global, collection, id, baseURL, code])
|
}, [i18n, globalSlug, collectionSlug, id, baseURL, code, versionsEnabled, draftsEnabled])
|
||||||
|
|
||||||
const getDocPermissions = React.useCallback(async () => {
|
const getDocPermissions = React.useCallback(async () => {
|
||||||
let docAccessURL: string
|
let docAccessURL: string
|
||||||
@@ -219,7 +214,7 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
} else {
|
} else {
|
||||||
// fallback to permissions from the entity type
|
// fallback to permissions from the entity type
|
||||||
// (i.e. create has no id)
|
// (i.e. create has no id)
|
||||||
setDocPermissions(permissions[pluralType][slug])
|
setDocPermissions(permissions?.[pluralType]?.[slug])
|
||||||
}
|
}
|
||||||
}, [serverURL, api, pluralType, slug, id, permissions, i18n.language, code])
|
}, [serverURL, api, pluralType, slug, id, permissions, i18n.language, code])
|
||||||
|
|
||||||
@@ -261,18 +256,19 @@ export const DocumentInfoProvider: React.FC<Props> = ({
|
|||||||
|
|
||||||
const value: ContextType = {
|
const value: ContextType = {
|
||||||
id,
|
id,
|
||||||
collection,
|
collectionSlug,
|
||||||
docPermissions,
|
docPermissions,
|
||||||
getDocPermissions,
|
getDocPermissions,
|
||||||
getDocPreferences,
|
getDocPreferences,
|
||||||
getVersions,
|
getVersions,
|
||||||
global,
|
globalSlug,
|
||||||
preferencesKey,
|
preferencesKey,
|
||||||
publishedDoc,
|
publishedDoc,
|
||||||
setDocFieldPreferences,
|
setDocFieldPreferences,
|
||||||
slug,
|
slug,
|
||||||
unpublishedVersions,
|
unpublishedVersions,
|
||||||
versions,
|
versionsEnabled,
|
||||||
|
draftsEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Context.Provider value={value}>{children}</Context.Provider>
|
return <Context.Provider value={value}>{children}</Context.Provider>
|
||||||
|
|||||||
@@ -11,25 +11,29 @@ export type Version = TypeWithVersion<any>
|
|||||||
export type DocumentPermissions = CollectionPermission | GlobalPermission | null
|
export type DocumentPermissions = CollectionPermission | GlobalPermission | null
|
||||||
|
|
||||||
export type ContextType = {
|
export type ContextType = {
|
||||||
collection?: SanitizedCollectionConfig
|
collectionSlug?: SanitizedCollectionConfig['slug']
|
||||||
docPermissions: DocumentPermissions
|
docPermissions: DocumentPermissions
|
||||||
getDocPermissions: () => Promise<void>
|
getDocPermissions: () => Promise<void>
|
||||||
getDocPreferences: () => Promise<{ [key: string]: unknown }>
|
getDocPreferences: () => Promise<{ [key: string]: unknown }>
|
||||||
getVersions: () => Promise<void>
|
getVersions: () => Promise<void>
|
||||||
global?: SanitizedGlobalConfig
|
globalSlug?: SanitizedGlobalConfig['slug']
|
||||||
id?: number | string
|
id?: number | string
|
||||||
preferencesKey?: string
|
preferencesKey?: string
|
||||||
publishedDoc?: TypeWithID & TypeWithTimestamps & { _status?: string }
|
publishedDoc?: TypeWithID & TypeWithTimestamps & { _status?: string }
|
||||||
setDocFieldPreferences: (field: string, fieldPreferences: { [key: string]: unknown }) => void
|
setDocFieldPreferences: (field: string, fieldPreferences: { [key: string]: unknown }) => void
|
||||||
slug?: string
|
slug?: string
|
||||||
unpublishedVersions?: PaginatedDocs<Version>
|
unpublishedVersions?: PaginatedDocs<Version>
|
||||||
versions?: PaginatedDocs<Version>
|
versionsCount?: PaginatedDocs<Version>
|
||||||
|
draftsEnabled?: boolean
|
||||||
|
versionsEnabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
collection?: SanitizedCollectionConfig
|
collectionSlug?: SanitizedCollectionConfig['slug']
|
||||||
global?: SanitizedGlobalConfig
|
globalSlug?: SanitizedGlobalConfig['slug']
|
||||||
id?: number | string
|
id?: number | string
|
||||||
idFromParams?: boolean
|
idFromParams?: boolean
|
||||||
|
draftsEnabled?: boolean
|
||||||
|
versionsEnabled?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const DefaultGlobalEdit: React.FC<
|
|||||||
fieldTypes: FieldTypes
|
fieldTypes: FieldTypes
|
||||||
}
|
}
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const { apiURL, data, fieldTypes, globalConfig, permissions, config } = props
|
const { apiURL, data, fieldTypes, globalConfig, permissions, config, user, state } = props
|
||||||
|
|
||||||
const { admin: { description } = {}, fields, label } = globalConfig
|
const { admin: { description } = {}, fields, label } = globalConfig
|
||||||
|
|
||||||
@@ -40,6 +40,9 @@ export const DefaultGlobalEdit: React.FC<
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
hasSavePermission={hasSavePermission}
|
hasSavePermission={hasSavePermission}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
|
user={user}
|
||||||
|
state={state}
|
||||||
|
data={data}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const DefaultGlobalView: React.FC<DefaultGlobalViewProps> = (props) => {
|
|||||||
// disableRoutes,
|
// disableRoutes,
|
||||||
// fieldTypes,
|
// fieldTypes,
|
||||||
globalConfig,
|
globalConfig,
|
||||||
initialState,
|
state,
|
||||||
// onSave,
|
// onSave,
|
||||||
permissions,
|
permissions,
|
||||||
} = props
|
} = props
|
||||||
@@ -93,7 +93,7 @@ export const DefaultGlobalView: React.FC<DefaultGlobalViewProps> = (props) => {
|
|||||||
action={action}
|
action={action}
|
||||||
className={`${baseClass}__form`}
|
className={`${baseClass}__form`}
|
||||||
disabled={!hasSavePermission}
|
disabled={!hasSavePermission}
|
||||||
initialState={initialState}
|
initialState={state}
|
||||||
method="POST"
|
method="POST"
|
||||||
// onSuccess={onSave}
|
// onSuccess={onSave}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export type CollectionEditViewProps = BaseEditViewProps & {
|
|||||||
export type GlobalEditViewProps = BaseEditViewProps & {
|
export type GlobalEditViewProps = BaseEditViewProps & {
|
||||||
config: SanitizedConfig
|
config: SanitizedConfig
|
||||||
globalConfig: SanitizedGlobalConfig
|
globalConfig: SanitizedGlobalConfig
|
||||||
initialState?: Fields
|
state?: Fields
|
||||||
permissions: GlobalPermission | null
|
permissions: GlobalPermission | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user