feat(ui): split up Select component into Select and SelectInput (#6471)
This commit is contained in:
@@ -7,7 +7,7 @@ import { FieldLabel } from '../../forms/FieldLabel/index.js'
|
||||
import { Check } from '../../icons/Check/index.js'
|
||||
import { Line } from '../../icons/Line/index.js'
|
||||
|
||||
type Props = {
|
||||
export type CheckboxInputProps = {
|
||||
AfterInput?: React.ReactNode
|
||||
BeforeInput?: React.ReactNode
|
||||
CustomLabel?: React.ReactNode
|
||||
@@ -26,7 +26,7 @@ type Props = {
|
||||
|
||||
export const inputBaseClass = 'checkbox-input'
|
||||
|
||||
export const CheckboxInput: React.FC<Props> = ({
|
||||
export const CheckboxInput: React.FC<CheckboxInputProps> = ({
|
||||
id,
|
||||
name,
|
||||
AfterInput,
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { CheckboxInputProps } from './Input.js'
|
||||
import type { CheckboxFieldProps } from './types.js'
|
||||
|
||||
import { FieldDescription } from '../../forms/FieldDescription/index.js'
|
||||
@@ -19,7 +20,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'checkbox'
|
||||
|
||||
export { CheckboxFieldProps, CheckboxInput }
|
||||
export { CheckboxFieldProps, CheckboxInput, type CheckboxInputProps }
|
||||
|
||||
const CheckboxField: React.FC<CheckboxFieldProps> = (props) => {
|
||||
const {
|
||||
|
||||
131
packages/ui/src/fields/Select/Input.tsx
Normal file
131
packages/ui/src/fields/Select/Input.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
'use client'
|
||||
import type { OptionObject } from 'payload/types'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React from 'react'
|
||||
|
||||
import type { SelectFieldProps } from './index.js'
|
||||
|
||||
import { ReactSelect } from '../../elements/ReactSelect/index.js'
|
||||
import { FieldDescription } from '../../forms/FieldDescription/index.js'
|
||||
import { FieldError } from '../../forms/FieldError/index.js'
|
||||
import { FieldLabel } from '../../forms/FieldLabel/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { fieldBaseClass } from '../shared/index.js'
|
||||
import './index.scss'
|
||||
|
||||
export type SelectInputProps = Omit<
|
||||
SelectFieldProps,
|
||||
| 'custom'
|
||||
| 'disabled'
|
||||
| 'docPreferences'
|
||||
| 'locale'
|
||||
| 'localized'
|
||||
| 'options'
|
||||
| 'rtl'
|
||||
| 'type'
|
||||
| 'user'
|
||||
| 'validate'
|
||||
| 'value'
|
||||
> & {
|
||||
options?: OptionObject[]
|
||||
showError?: boolean
|
||||
value?: string | string[]
|
||||
}
|
||||
|
||||
export const SelectInput: React.FC<SelectInputProps> = (props) => {
|
||||
const {
|
||||
AfterInput,
|
||||
BeforeInput,
|
||||
CustomDescription,
|
||||
CustomError,
|
||||
CustomLabel,
|
||||
className,
|
||||
descriptionProps,
|
||||
errorProps,
|
||||
hasMany = false,
|
||||
isClearable = true,
|
||||
isSortable = true,
|
||||
label,
|
||||
labelProps,
|
||||
onChange,
|
||||
options,
|
||||
path,
|
||||
readOnly,
|
||||
required,
|
||||
showError,
|
||||
style,
|
||||
value,
|
||||
width,
|
||||
} = props
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
let valueToRender
|
||||
|
||||
if (hasMany && Array.isArray(value)) {
|
||||
valueToRender = value.map((val) => {
|
||||
const matchingOption = options.find((option) => option.value === val)
|
||||
return {
|
||||
label: matchingOption ? getTranslation(matchingOption.label, i18n) : val,
|
||||
value: matchingOption?.value ?? val,
|
||||
}
|
||||
})
|
||||
} else if (value) {
|
||||
const matchingOption = options.find((option) => option.value === value)
|
||||
valueToRender = {
|
||||
label: matchingOption ? getTranslation(matchingOption.label, i18n) : value,
|
||||
value: matchingOption?.value ?? value,
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
fieldBaseClass,
|
||||
'select',
|
||||
className,
|
||||
showError && 'error',
|
||||
readOnly && 'read-only',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
id={`field-${path.replace(/\./g, '__')}`}
|
||||
style={{
|
||||
...style,
|
||||
width,
|
||||
}}
|
||||
>
|
||||
<FieldLabel
|
||||
CustomLabel={CustomLabel}
|
||||
label={label}
|
||||
required={required}
|
||||
{...(labelProps || {})}
|
||||
/>
|
||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||
<div className={`${fieldBaseClass}__wrap`}>
|
||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||
{BeforeInput}
|
||||
<ReactSelect
|
||||
disabled={readOnly}
|
||||
isClearable={isClearable}
|
||||
isMulti={hasMany}
|
||||
isSortable={isSortable}
|
||||
onChange={onChange}
|
||||
options={options.map((option) => ({
|
||||
...option,
|
||||
label: getTranslation(option.label, i18n),
|
||||
}))}
|
||||
showError={showError}
|
||||
value={valueToRender as OptionObject}
|
||||
/>
|
||||
{AfterInput}
|
||||
</div>
|
||||
{CustomDescription !== undefined ? (
|
||||
CustomDescription
|
||||
) : (
|
||||
<FieldDescription {...(descriptionProps || {})} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,21 +2,15 @@
|
||||
'use client'
|
||||
import type { ClientValidate, Option, OptionObject } from 'payload/types'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import type { FormFieldBase } from '../shared/index.js'
|
||||
import type { SelectInputProps } from './Input.js'
|
||||
|
||||
import { ReactSelect } from '../../elements/ReactSelect/index.js'
|
||||
import { FieldDescription } from '../../forms/FieldDescription/index.js'
|
||||
import { FieldError } from '../../forms/FieldError/index.js'
|
||||
import { FieldLabel } from '../../forms/FieldLabel/index.js'
|
||||
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
|
||||
import { useField } from '../../forms/useField/index.js'
|
||||
import { withCondition } from '../../forms/withCondition/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { fieldBaseClass } from '../shared/index.js'
|
||||
import './index.scss'
|
||||
import { SelectInput } from './Input.js'
|
||||
|
||||
export type SelectFieldProps = FormFieldBase & {
|
||||
hasMany?: boolean
|
||||
@@ -68,8 +62,6 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
|
||||
width,
|
||||
} = props
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const [options] = useState(formatOptions(optionsFromProps))
|
||||
|
||||
const memoizedValidate: ClientValidate = useCallback(
|
||||
@@ -88,24 +80,6 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
|
||||
validate: memoizedValidate,
|
||||
})
|
||||
|
||||
let valueToRender
|
||||
|
||||
if (hasMany && Array.isArray(value)) {
|
||||
valueToRender = value.map((val) => {
|
||||
const matchingOption = options.find((option) => option.value === val)
|
||||
return {
|
||||
label: matchingOption ? getTranslation(matchingOption.label, i18n) : val,
|
||||
value: matchingOption?.value ?? val,
|
||||
}
|
||||
})
|
||||
} else if (value) {
|
||||
const matchingOption = options.find((option) => option.value === value)
|
||||
valueToRender = {
|
||||
label: matchingOption ? getTranslation(matchingOption.label, i18n) : value,
|
||||
value: matchingOption?.value ?? value,
|
||||
}
|
||||
}
|
||||
|
||||
const onChange = useCallback(
|
||||
(selectedOption) => {
|
||||
if (!readOnly) {
|
||||
@@ -133,54 +107,34 @@ const SelectField: React.FC<SelectFieldProps> = (props) => {
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
fieldBaseClass,
|
||||
'select',
|
||||
className,
|
||||
showError && 'error',
|
||||
readOnly && 'read-only',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
id={`field-${path.replace(/\./g, '__')}`}
|
||||
style={{
|
||||
...style,
|
||||
width,
|
||||
}}
|
||||
>
|
||||
<FieldLabel
|
||||
CustomLabel={CustomLabel}
|
||||
label={label}
|
||||
required={required}
|
||||
{...(labelProps || {})}
|
||||
/>
|
||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||
<div className={`${fieldBaseClass}__wrap`}>
|
||||
<FieldError CustomError={CustomError} path={path} {...(errorProps || {})} />
|
||||
{BeforeInput}
|
||||
<ReactSelect
|
||||
disabled={readOnly}
|
||||
isClearable={isClearable}
|
||||
isMulti={hasMany}
|
||||
isSortable={isSortable}
|
||||
onChange={onChange}
|
||||
options={options.map((option) => ({
|
||||
...option,
|
||||
label: getTranslation(option.label, i18n),
|
||||
}))}
|
||||
showError={showError}
|
||||
value={valueToRender as OptionObject}
|
||||
/>
|
||||
{AfterInput}
|
||||
</div>
|
||||
{CustomDescription !== undefined ? (
|
||||
CustomDescription
|
||||
) : (
|
||||
<FieldDescription {...(descriptionProps || {})} />
|
||||
)}
|
||||
</div>
|
||||
<SelectInput
|
||||
AfterInput={AfterInput}
|
||||
BeforeInput={BeforeInput}
|
||||
CustomDescription={CustomDescription}
|
||||
CustomError={CustomError}
|
||||
CustomLabel={CustomLabel}
|
||||
className={className}
|
||||
descriptionProps={descriptionProps}
|
||||
errorProps={errorProps}
|
||||
hasMany={hasMany}
|
||||
isClearable={isClearable}
|
||||
isSortable={isSortable}
|
||||
label={label}
|
||||
labelProps={labelProps}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
path={path}
|
||||
readOnly={readOnly}
|
||||
required={required}
|
||||
showError={showError}
|
||||
style={style}
|
||||
value={value as string | string[]}
|
||||
width={width}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const Select = withCondition(SelectField)
|
||||
|
||||
export { SelectInput, type SelectInputProps }
|
||||
|
||||
Reference in New Issue
Block a user