diff --git a/packages/core/src/components/Editor/EditorControlPane/EditorControl.tsx b/packages/core/src/components/Editor/EditorControlPane/EditorControl.tsx index 232db0da..808ef1a1 100644 --- a/packages/core/src/components/Editor/EditorControlPane/EditorControl.tsx +++ b/packages/core/src/components/Editor/EditorControlPane/EditorControl.tsx @@ -21,6 +21,7 @@ import { borders, colors, lengths, transitions } from '@staticcms/core/component import { transientOptions } from '@staticcms/core/lib'; import useMemoCompare from '@staticcms/core/lib/hooks/useMemoCompare'; import useUUID from '@staticcms/core/lib/hooks/useUUID'; +import { isFieldDuplicate, isFieldHidden } from '@staticcms/core/lib/i18n'; import { resolveWidget } from '@staticcms/core/lib/registry'; import { getFieldLabel } from '@staticcms/core/lib/util/field.util'; import { isNotNullish } from '@staticcms/core/lib/util/null.util'; @@ -144,10 +145,11 @@ const EditorControl = ({ fieldsErrors, submitted, getAsset, - isDisabled, - isFieldDuplicate, - isFieldHidden, - isHidden = false, + isDisabled = false, + isParentDuplicate = false, + isFieldDuplicate: deprecatedIsFieldDuplicate, + isParentHidden = false, + isFieldHidden: deprecatedIsFieldHidden, locale, mediaPaths, openMediaLibrary, @@ -191,6 +193,15 @@ const EditorControl = ({ [collection], ); + const isDuplicate = useMemo( + () => isParentDuplicate || isFieldDuplicate(field, locale, i18n?.defaultLocale), + [field, i18n?.defaultLocale, isParentDuplicate, locale], + ); + const isHidden = useMemo( + () => isParentHidden || isFieldHidden(field, locale, i18n?.defaultLocale), + [field, i18n?.defaultLocale, isParentHidden, locale], + ); + useEffect(() => { if ((!dirty && !submitted) || isHidden) { return; @@ -257,9 +268,11 @@ const EditorControl = ({ fieldsErrors, submitted, getAsset: handleGetAsset, - isDisabled: isDisabled ?? false, - isFieldDuplicate, - isFieldHidden, + isDisabled: isDisabled || isDuplicate, + isDuplicate, + isFieldDuplicate: deprecatedIsFieldDuplicate, + isHidden, + isFieldHidden: deprecatedIsFieldHidden, label: getFieldLabel(field, t), locale, mediaPaths, @@ -330,9 +343,16 @@ interface EditorControlOwnProps { fieldsErrors: FieldsErrors; submitted: boolean; isDisabled?: boolean; + isParentDuplicate?: boolean; + /** + * @deprecated use isDuplicate instead + */ isFieldDuplicate?: (field: Field) => boolean; + isParentHidden?: boolean; + /** + * @deprecated use isHidden instead + */ isFieldHidden?: (field: Field) => boolean; - isHidden?: boolean; locale?: string; parentPath: string; value: ValueOrNestedValue; diff --git a/packages/core/src/components/Editor/EditorControlPane/EditorControlPane.tsx b/packages/core/src/components/Editor/EditorControlPane/EditorControlPane.tsx index 02ccd7a1..50cf10b8 100644 --- a/packages/core/src/components/Editor/EditorControlPane/EditorControlPane.tsx +++ b/packages/core/src/components/Editor/EditorControlPane/EditorControlPane.tsx @@ -195,8 +195,6 @@ const EditorControlPane = ({ ) : null} {fields.map(field => { const isTranslatable = isFieldTranslatable(field, locale, i18n?.defaultLocale); - const isDuplicate = isFieldDuplicate(field, locale, i18n?.defaultLocale); - const isHidden = isFieldHidden(field, locale, i18n?.defaultLocale); const key = i18n ? `field-${locale}_${field.name}` : `field-${field.name}`; return ( @@ -206,8 +204,6 @@ const EditorControlPane = ({ value={getFieldValue(field, entry, isTranslatable, locale)} fieldsErrors={fieldsErrors} submitted={submitted} - isDisabled={isDuplicate} - isHidden={isHidden} isFieldDuplicate={field => isFieldDuplicate(field, locale, i18n?.defaultLocale)} isFieldHidden={field => isFieldHidden(field, locale, i18n?.defaultLocale)} locale={locale} diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 804824a9..b3bc9dfc 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -235,7 +235,7 @@ export interface DisplayURLState { export type TranslatedProps = T & ReactPolyglotTranslateProps; /** - * @deprecated Should use `useMediaAsset` React hook instead + * @deprecated Use `useMediaAsset` React hook instead. Will be removed in v2.0.0 */ export type GetAssetFunction = ( path: string, @@ -251,11 +251,19 @@ export interface WidgetControlProps { submitted: boolean; forList: boolean; /** - * @deprecated Should use `useMediaAsset` React hook instead + * @deprecated Use `useMediaAsset` React hook instead. Will be removed in v2.0.0 */ getAsset: GetAssetFunction; isDisabled: boolean; + isDuplicate: boolean; + /** + * @deprecated Use `isDuplicate` instead. Will be removed in v2.0.0 + */ isFieldDuplicate: EditorControlProps['isFieldDuplicate']; + isHidden: boolean; + /** + * @deprecated Use `isHidden` instead. Will be removed in v2.0.0 + */ isFieldHidden: EditorControlProps['isFieldHidden']; label: string; locale: string | undefined; diff --git a/packages/core/src/lib/test-utils/ControlWrapper.tsx b/packages/core/src/lib/test-utils/ControlWrapper.tsx index 06da9400..79c16f47 100644 --- a/packages/core/src/lib/test-utils/ControlWrapper.tsx +++ b/packages/core/src/lib/test-utils/ControlWrapper.tsx @@ -38,7 +38,9 @@ const createControlWrapper = ({ return Promise.resolve(null) as any; }, isDisabled = false, + isDuplicate = false, isFieldDuplicate = () => false, + isHidden = false, isFieldHidden = () => false, label = defaultLabel, locale = 'en', @@ -75,7 +77,9 @@ const createControlWrapper = ({ forList={forList} getAsset={getAsset} isDisabled={isDisabled} + isDuplicate={isDuplicate} isFieldDuplicate={isFieldDuplicate} + isHidden={isHidden} isFieldHidden={isFieldHidden} label={label} locale={locale} diff --git a/packages/core/src/widgets/boolean/BooleanControl.tsx b/packages/core/src/widgets/boolean/BooleanControl.tsx index c3b1b743..b09cd5c4 100644 --- a/packages/core/src/widgets/boolean/BooleanControl.tsx +++ b/packages/core/src/widgets/boolean/BooleanControl.tsx @@ -1,7 +1,7 @@ import { red } from '@mui/material/colors'; import FormControlLabel from '@mui/material/FormControlLabel'; import Switch from '@mui/material/Switch'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import type { BooleanField, WidgetControlProps } from '@staticcms/core/interface'; import type { ChangeEvent, FC } from 'react'; @@ -9,10 +9,15 @@ import type { ChangeEvent, FC } from 'react'; const BooleanControl: FC> = ({ value, label, + isDuplicate, onChange, hasErrors, }) => { - const [internalValue, setInternalValue] = useState(value); + const [internalRawValue, setInternalValue] = useState(value); + const internalValue = useMemo( + () => (isDuplicate ? value : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const handleChange = useCallback( (event: ChangeEvent) => { diff --git a/packages/core/src/widgets/code/CodeControl.tsx b/packages/core/src/widgets/code/CodeControl.tsx index db4ee761..bfbefddf 100644 --- a/packages/core/src/widgets/code/CodeControl.tsx +++ b/packages/core/src/widgets/code/CodeControl.tsx @@ -60,6 +60,7 @@ function valueToOption(val: string | { name: string; label?: string }): { const CodeControl: FC> = ({ field, + isDuplicate, onChange, hasErrors, value, @@ -77,7 +78,12 @@ const CodeControl: FC Boolean(!field.output_code_only), [field.output_code_only]); - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); + const [lang, setLang] = useState(null); const [collapsed, setCollapsed] = useState(false); diff --git a/packages/core/src/widgets/colorstring/ColorControl.tsx b/packages/core/src/widgets/colorstring/ColorControl.tsx index 19958ad6..afc55219 100644 --- a/packages/core/src/widgets/colorstring/ColorControl.tsx +++ b/packages/core/src/widgets/colorstring/ColorControl.tsx @@ -3,7 +3,7 @@ import IconButton from '@mui/material/IconButton'; import InputAdornment from '@mui/material/InputAdornment'; import { styled } from '@mui/material/styles'; import TextField from '@mui/material/TextField'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { ChromePicker } from 'react-color'; import validateColor from 'validate-color'; @@ -104,6 +104,7 @@ const ClickOutsideDiv = styled('div')` const ColorControl: FC> = ({ field, + isDuplicate, onChange, value, hasErrors, @@ -116,7 +117,11 @@ const ColorControl: FC> = ({ }, [collapsed]); const [showColorPicker, setShowColorPicker] = useState(false); - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); // show/hide color picker const handleClick = useCallback(() => { diff --git a/packages/core/src/widgets/datetime/DateTimeControl.tsx b/packages/core/src/widgets/datetime/DateTimeControl.tsx index 698d5846..318da44f 100644 --- a/packages/core/src/widgets/datetime/DateTimeControl.tsx +++ b/packages/core/src/widgets/datetime/DateTimeControl.tsx @@ -62,6 +62,7 @@ const DateTimeControl: FC> = ({ value, t, isDisabled, + isDuplicate, onChange, hasErrors, }) => { @@ -118,7 +119,11 @@ const DateTimeControl: FC> = ({ : field.default; }, [field.default, field.picker_utc, format, inputFormat, timezoneOffset]); - const [internalValue, setInternalValue] = useState(value); + const [internalRawValue, setInternalValue] = useState(value); + const internalValue = useMemo( + () => (isDuplicate ? value : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const dateValue: Date = useMemo(() => { let valueToParse = internalValue; diff --git a/packages/core/src/widgets/file/withFileControl.tsx b/packages/core/src/widgets/file/withFileControl.tsx index 77c7863a..7795ed61 100644 --- a/packages/core/src/widgets/file/withFileControl.tsx +++ b/packages/core/src/widgets/file/withFileControl.tsx @@ -213,6 +213,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { collection, field, entry, + isDuplicate, onChange, openMediaLibrary, clearMediaControl, @@ -222,7 +223,11 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { }) => { const controlID = useUUID(); const [collapsed, setCollapsed] = useState(false); - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const handleOnChange = useCallback( (newValue: string | string[]) => { diff --git a/packages/core/src/widgets/list/ListControl.tsx b/packages/core/src/widgets/list/ListControl.tsx index 09050eff..11e580c3 100644 --- a/packages/core/src/widgets/list/ListControl.tsx +++ b/packages/core/src/widgets/list/ListControl.tsx @@ -68,7 +68,9 @@ interface SortableItemProps { field: ListField; fieldsErrors: FieldsErrors; submitted: boolean; + isDuplicate: boolean; isFieldDuplicate: ((field: Field) => boolean) | undefined; + isHidden: boolean; isFieldHidden: ((field: Field) => boolean) | undefined; locale: string | undefined; path: string; @@ -86,7 +88,9 @@ const SortableItem: FC = ({ field, fieldsErrors, submitted, + isDuplicate, isFieldDuplicate, + isHidden, isFieldHidden, locale, path, @@ -118,7 +122,9 @@ const SortableItem: FC = ({ field={field} fieldsErrors={fieldsErrors} submitted={submitted} + isDuplicate={isDuplicate} isFieldDuplicate={isFieldDuplicate} + isHidden={isHidden} isFieldHidden={isFieldHidden} locale={locale} path={path} @@ -188,7 +194,9 @@ const ListControl: FC> = ({ field, fieldsErrors, submitted, + isDuplicate, isFieldDuplicate, + isHidden, isFieldHidden, locale, onChange, @@ -356,7 +364,9 @@ const ListControl: FC> = ({ field={field} fieldsErrors={fieldsErrors} submitted={submitted} + isDuplicate={isDuplicate} isFieldDuplicate={isFieldDuplicate} + isHidden={isHidden} isFieldHidden={isFieldHidden} locale={locale} path={path} diff --git a/packages/core/src/widgets/list/ListItem.tsx b/packages/core/src/widgets/list/ListItem.tsx index d0d670f6..dc475735 100644 --- a/packages/core/src/widgets/list/ListItem.tsx +++ b/packages/core/src/widgets/list/ListItem.tsx @@ -93,7 +93,9 @@ interface ListItemProps | 'field' | 'fieldsErrors' | 'submitted' + | 'isDuplicate' | 'isFieldDuplicate' + | 'isHidden' | 'isFieldHidden' | 'locale' | 'path' @@ -114,7 +116,9 @@ const ListItem: FC = ({ field, fieldsErrors, submitted, + isDuplicate, isFieldDuplicate, + isHidden, isFieldHidden, locale, path, @@ -197,9 +201,6 @@ const ListItem: FC = ({ [collapsed], ); - const isDuplicate = isFieldDuplicate && isFieldDuplicate(field); - const isHidden = isFieldHidden && isFieldHidden(field); - const finalValue = useMemo(() => { if (field.fields && field.fields.length === 1) { return { @@ -232,8 +233,9 @@ const ListItem: FC = ({ submitted={submitted} parentPath={path} isDisabled={isDuplicate} - isHidden={isHidden} + isParentDuplicate={isDuplicate} isFieldDuplicate={isFieldDuplicate} + isParentHidden={isHidden} isFieldHidden={isFieldHidden} locale={locale} i18n={i18n} diff --git a/packages/core/src/widgets/markdown/withMarkdownControl.tsx b/packages/core/src/widgets/markdown/withMarkdownControl.tsx index 6d2061c7..87d09cd0 100644 --- a/packages/core/src/widgets/markdown/withMarkdownControl.tsx +++ b/packages/core/src/widgets/markdown/withMarkdownControl.tsx @@ -39,9 +39,14 @@ export interface WithMarkdownControlProps { const withMarkdownControl = ({ useMdx }: WithMarkdownControlProps) => { const MarkdownControl: FC> = controlProps => { - const { label, value, onChange, hasErrors, collection, entry, field } = controlProps; + const { label, value, isDuplicate, onChange, hasErrors, collection, entry, field } = + controlProps; - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const [hasFocus, setHasFocus] = useState(false); const debouncedFocus = useDebounce(hasFocus, 150); diff --git a/packages/core/src/widgets/number/NumberControl.tsx b/packages/core/src/widgets/number/NumberControl.tsx index c45c4f1b..565c1237 100644 --- a/packages/core/src/widgets/number/NumberControl.tsx +++ b/packages/core/src/widgets/number/NumberControl.tsx @@ -1,5 +1,5 @@ import TextField from '@mui/material/TextField'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import type { FieldError, NumberField, WidgetControlProps } from '@staticcms/core/interface'; import type { ChangeEvent, FC } from 'react'; @@ -62,10 +62,15 @@ const NumberControl: FC> = ({ label, field, value, + isDuplicate, onChange, hasErrors, }) => { - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const handleChange = useCallback( (e: ChangeEvent) => { diff --git a/packages/core/src/widgets/object/ObjectControl.tsx b/packages/core/src/widgets/object/ObjectControl.tsx index 8959106d..8cdd7d70 100644 --- a/packages/core/src/widgets/object/ObjectControl.tsx +++ b/packages/core/src/widgets/object/ObjectControl.tsx @@ -53,7 +53,9 @@ const ObjectControl: FC> = ({ fieldsErrors, submitted, forList, + isDuplicate, isFieldDuplicate, + isHidden, isFieldHidden, locale, path, @@ -96,9 +98,6 @@ const ObjectControl: FC> = ({ parentPath = splitPath.join('.'); } - const isDuplicate = isFieldDuplicate && isFieldDuplicate(field); - const isHidden = isFieldHidden && isFieldHidden(field); - return ( > = ({ submitted={submitted} parentPath={parentPath} isDisabled={isDuplicate} - isHidden={isHidden} + isParentDuplicate={isDuplicate} isFieldDuplicate={isFieldDuplicate} + isParentHidden={isHidden} isFieldHidden={isFieldHidden} locale={locale} i18n={i18n} @@ -122,8 +122,10 @@ const ObjectControl: FC> = ({ fieldsErrors, forList, i18n, + isDuplicate, isFieldDuplicate, isFieldHidden, + isHidden, locale, multiFields, path, diff --git a/packages/core/src/widgets/relation/RelationControl.tsx b/packages/core/src/widgets/relation/RelationControl.tsx index 97ddeb95..532d762b 100644 --- a/packages/core/src/widgets/relation/RelationControl.tsx +++ b/packages/core/src/widgets/relation/RelationControl.tsx @@ -118,13 +118,18 @@ function getSelectedValue( const RelationControl: FC> = ({ value, field, + isDuplicate, onChange, config, locale, label, hasErrors, }) => { - const [internalValue, setInternalValue] = useState(value); + const [internalRawValue, setInternalValue] = useState(value); + const internalValue = useMemo( + () => (isDuplicate ? value : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const [initialOptions, setInitialOptions] = useState([]); const searchCollectionSelector = useMemo( diff --git a/packages/core/src/widgets/select/SelectControl.tsx b/packages/core/src/widgets/select/SelectControl.tsx index 9aa8a1e1..61fc2a61 100644 --- a/packages/core/src/widgets/select/SelectControl.tsx +++ b/packages/core/src/widgets/select/SelectControl.tsx @@ -31,9 +31,14 @@ const SelectControl: FC { - const [internalValue, setInternalValue] = useState(value); + const [internalRawValue, setInternalValue] = useState(value); + const internalValue = useMemo( + () => (isDuplicate ? value : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const fieldOptions: (string | number | Option)[] = useMemo(() => field.options, [field.options]); const isMultiple = useMemo(() => field.multiple ?? false, [field.multiple]); diff --git a/packages/core/src/widgets/string/StringControl.tsx b/packages/core/src/widgets/string/StringControl.tsx index 6371f0b0..371ac172 100644 --- a/packages/core/src/widgets/string/StringControl.tsx +++ b/packages/core/src/widgets/string/StringControl.tsx @@ -1,5 +1,5 @@ import TextField from '@mui/material/TextField'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import type { StringOrTextField, WidgetControlProps } from '@staticcms/core/interface'; import type { ChangeEvent, FC } from 'react'; @@ -7,10 +7,15 @@ import type { ChangeEvent, FC } from 'react'; const StringControl: FC> = ({ value, label, + isDuplicate, onChange, hasErrors, }) => { - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const handleChange = useCallback( (event: ChangeEvent) => { diff --git a/packages/core/src/widgets/text/TextControl.tsx b/packages/core/src/widgets/text/TextControl.tsx index 23334f87..d0e0ca66 100644 --- a/packages/core/src/widgets/text/TextControl.tsx +++ b/packages/core/src/widgets/text/TextControl.tsx @@ -1,5 +1,5 @@ import TextField from '@mui/material/TextField'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import type { StringOrTextField, WidgetControlProps } from '@staticcms/core/interface'; import type { ChangeEvent, FC } from 'react'; @@ -7,10 +7,15 @@ import type { ChangeEvent, FC } from 'react'; const TextControl: FC> = ({ label, value, + isDuplicate, onChange, hasErrors, }) => { - const [internalValue, setInternalValue] = useState(value ?? ''); + const [internalRawValue, setInternalValue] = useState(value ?? ''); + const internalValue = useMemo( + () => (isDuplicate ? value ?? '' : internalRawValue), + [internalRawValue, isDuplicate, value], + ); const handleChange = useCallback( (event: ChangeEvent) => { diff --git a/packages/docs/content/docs/custom-widgets.mdx b/packages/docs/content/docs/custom-widgets.mdx index 1a6f60d4..2232d610 100644 --- a/packages/docs/content/docs/custom-widgets.mdx +++ b/packages/docs/content/docs/custom-widgets.mdx @@ -55,10 +55,12 @@ The react component that renders the control. It receives the following props: | fieldsErrors | object | Key/value object of field names mapping to validation errors | | isDisabled | boolean | Specifies if the widget control should be disabled | | submitted | boolean | Specifies if a save attempt has been made in the editor session | -| forList | boolean | Specifices if the widget is within a `list` widget | -| isFieldDuplicate | function | Function that given a field configuration, returns if that field is a duplicate | -| isFieldHidden | function | Function that given a field configuration, returns if that field is hidden | -| getAsset | Async function | __Deprecated__ Function that given a url returns (as a promise) a loaded asset | +| forList | boolean | Specifies if the widget is within a `list` widget | +| isDuplicate | function | Specifies if that field is an i18n duplicate | +| isFieldDuplicate | function | **Deprecated** Function that given a field configuration, returns if that field is a duplicate | +| isHidden | function | Specifies if that field should be hidden | +| isFieldHidden | function | **Deprecated** Function that given a field configuration, returns if that field is hidden | +| getAsset | Async function | **Deprecated** Function that given a url returns (as a promise) a loaded asset | | locale | string
\| undefined | The current locale of the editor | | mediaPaths | object | Key/value object of control IDs (passed to the media library) mapping to media paths | | clearMediaControl | function | Clears a control ID's value from the internal store | @@ -107,7 +109,7 @@ The react component that renders the preview. It receives the following props: | collection | object | The collection configuration for the current widget. See [Collections](/docs/collection-overview) | | config | object | The current Static CMS config. See [configuration options](/docs/configuration-options) | | entry | object | Object with a `data` field that contains the current value of all widgets in the editor | -| getAsset | Async function | __Deprecated__ Function that given a url returns (as a promise) a loaded asset | +| getAsset | Async function | **Deprecated** Function that given a url returns (as a promise) a loaded asset | ### Options @@ -167,9 +169,12 @@ import CMS from '@staticcms/core'; const CategoriesControl = ({ label, value, field, onChange }) => { const separator = useMemo(() => field.separator ?? ', ', [field.separator]); - const handleChange = useCallback((e) => { - onChange(e.target.value.split(separator).map(e => e.trim())); - }, [separator, onChange]); + const handleChange = useCallback( + e => { + onChange(e.target.value.split(separator).map(e => e.trim())); + }, + [separator, onChange], + ); return (
@@ -178,7 +183,8 @@ const CategoriesControl = ({ label, value, field, onChange }) => { id="inputId" type="text" value={value ? value.join(separator) : ''} - onChange={handleChange} /> + onChange={handleChange} + />
); }; @@ -208,15 +214,23 @@ import CMS from '@staticcms/core'; import type { WidgetControlProps, WidgetPreviewProps } from '@staticcms/core'; interface CategoriesField { - widget: 'categories' + widget: 'categories'; } -const CategoriesControl = ({ label, value, field, onChange }: WidgetControlProps) => { +const CategoriesControl = ({ + label, + value, + field, + onChange, +}: WidgetControlProps) => { const separator = useMemo(() => field.separator ?? ', ', [field.separator]); - const handleChange = useCallback((e) => { - onChange(e.target.value.split(separator).map(e => e.trim())); - }, [separator, onChange]); + const handleChange = useCallback( + e => { + onChange(e.target.value.split(separator).map(e => e.trim())); + }, + [separator, onChange], + ); return (
@@ -225,7 +239,8 @@ const CategoriesControl = ({ label, value, field, onChange }: WidgetControlProps id="inputId" type="text" value={value ? value.join(separator) : ''} - onChange={handleChange} /> + onChange={handleChange} + />
); }; @@ -367,7 +382,7 @@ const FileControl = ({ collection, field, value, entry, onChange }) => { return [ h('button', { type: 'button', onClick: handleOpenMediaLibrary }, 'Upload'), - h('img', { role: 'presentation', src: assetSource }) + h('img', { role: 'presentation', src: assetSource }), ]; }; ```