diff options
| -rw-r--r-- | admin/src/components/CheckboxListDefaultInput.tsx | 109 | ||||
| -rw-r--r-- | admin/src/components/CheckboxListInput.tsx | 2 | ||||
| -rw-r--r-- | admin/src/index.ts | 93 | ||||
| -rw-r--r-- | server/src/register.js | 4 | ||||
| -rw-r--r-- | server/src/register.ts | 4 |
5 files changed, 193 insertions, 19 deletions
diff --git a/admin/src/components/CheckboxListDefaultInput.tsx b/admin/src/components/CheckboxListDefaultInput.tsx new file mode 100644 index 0000000..465d0d3 --- /dev/null +++ b/admin/src/components/CheckboxListDefaultInput.tsx @@ -0,0 +1,109 @@ +import type { ReactNode } from 'react'; + +import { Box, Checkbox, Field, Flex, Typography } from '@strapi/design-system'; +import { useIntl } from 'react-intl'; + +type CheckboxListDefaultInputProps = { + name: string; + value?: unknown; + onChange: (eventOrPath: { target: { name: string; value: string[] } }, value?: unknown) => void; + intlLabel?: { + id: string; + defaultMessage: string; + values?: Record<string, string | number | boolean | null | undefined>; + }; + description?: { + id: string; + defaultMessage: string; + values?: Record<string, string | number | boolean | null | undefined>; + }; + labelAction?: ReactNode; + required?: boolean; + disabled?: boolean; + error?: string; + modifiedData?: { + enum?: string[]; + }; +}; + +const normalizeValue = (value: unknown): string[] => { + if (Array.isArray(value)) { + return value.filter((item): item is string => typeof item === 'string'); + } + + if (typeof value === 'string' && value.length > 0) { + return [value]; + } + + return []; +}; + +const CheckboxListDefaultInput = ({ + name, + value, + onChange, + intlLabel, + description, + labelAction, + required = false, + disabled = false, + error, + modifiedData, +}: CheckboxListDefaultInputProps) => { + const { formatMessage } = useIntl(); + const enumValues = Array.isArray(modifiedData?.enum) ? modifiedData.enum : []; + const selectedValues = normalizeValue(value); + + const label = intlLabel + ? formatMessage(intlLabel, intlLabel.values ?? {}) + : name; + const hint = description ? formatMessage(description, description.values ?? {}) : undefined; + + const handleToggle = (option: string, isChecked: boolean) => { + const nextValues = isChecked + ? Array.from(new Set([...selectedValues, option])) + : selectedValues.filter((item) => item !== option); + + onChange({ + target: { + name, + value: nextValues, + }, + }); + }; + + return ( + <Field.Root name={name} hint={hint} error={error} required={required}> + <Field.Label action={labelAction}>{label}</Field.Label> + {enumValues.length > 0 ? ( + <Flex direction="column" gap={2} paddingTop={1} alignItems="flex-start"> + {enumValues.map((option) => ( + <Checkbox + key={option} + checked={selectedValues.includes(option)} + disabled={disabled} + onCheckedChange={(checked: boolean | 'indeterminate') => + handleToggle(option, Boolean(checked)) + } + > + {option} + </Checkbox> + ))} + </Flex> + ) : ( + <Box paddingTop={1}> + <Typography variant="pi" textColor="neutral500"> + {formatMessage({ + id: 'checkbox-list.field.empty', + defaultMessage: 'No values configured yet.', + })} + </Typography> + </Box> + )} + <Field.Error /> + <Field.Hint /> + </Field.Root> + ); +}; + +export { CheckboxListDefaultInput }; diff --git a/admin/src/components/CheckboxListInput.tsx b/admin/src/components/CheckboxListInput.tsx index 6da9766..51a0d22 100644 --- a/admin/src/components/CheckboxListInput.tsx +++ b/admin/src/components/CheckboxListInput.tsx @@ -82,7 +82,7 @@ const CheckboxListInput = ({ <Field.Root name={name} hint={hint} error={error} required={required}> <Field.Label action={labelAction}>{label ?? name}</Field.Label> {enumValues.length > 0 ? ( - <Flex direction="column" gap={2} paddingTop={1}> + <Flex direction="column" gap={2} paddingTop={1} alignItems="flex-start"> {enumValues.map((option) => ( <Checkbox key={option} diff --git a/admin/src/index.ts b/admin/src/index.ts index f3e7a28..a1c0473 100644 --- a/admin/src/index.ts +++ b/admin/src/index.ts @@ -1,25 +1,10 @@ -import { Check } from '@strapi/icons'; +import { EnumerationField } from '@strapi/icons/symbols'; import { Initializer } from './components/Initializer'; -import { PluginIcon } from './components/PluginIcon'; +import { CheckboxListDefaultInput } from './components/CheckboxListDefaultInput'; import { PLUGIN_ID } from './pluginId'; -import { getTranslation } from './utils/getTranslation'; export default { register(app: any) { - app.addMenuLink({ - to: `plugins/${PLUGIN_ID}`, - icon: PluginIcon, - intlLabel: { - id: `${PLUGIN_ID}.plugin.name`, - defaultMessage: PLUGIN_ID, - }, - Component: async () => { - const { App } = await import('./pages/App'); - - return App; - }, - }); - app.registerPlugin({ id: PLUGIN_ID, initializer: Initializer, @@ -27,11 +12,20 @@ export default { name: PLUGIN_ID, }); + const ctbPlugin = app.getPlugin?.('content-type-builder'); + + if (ctbPlugin?.apis?.forms?.components?.add) { + ctbPlugin.apis.forms.components.add({ + id: 'checkbox-list-default', + component: CheckboxListDefaultInput, + }); + } + app.customFields.register({ name: 'checkbox-list', pluginId: PLUGIN_ID, type: 'json', - icon: Check, + icon: EnumerationField, intlLabel: { id: `${PLUGIN_ID}.customField.label`, defaultMessage: 'Checkbox list', @@ -71,6 +65,69 @@ export default { ], }, ], + advanced: [ + { + sectionTitle: null, + items: [ + { + name: 'default', + type: 'checkbox-list-default', + size: 6, + intlLabel: { + id: 'form.attribute.settings.default', + defaultMessage: 'Default value', + }, + validations: {}, + }, + { + name: 'options.enumName', + type: 'text', + size: 6, + intlLabel: { + id: 'form.attribute.item.enumeration.graphql', + defaultMessage: 'Name override for GraphQL', + }, + description: { + id: 'form.attribute.item.enumeration.graphql.description', + defaultMessage: 'Allows you to override the default generated name for GraphQL', + }, + validations: {}, + }, + ], + }, + { + sectionTitle: { + id: 'global.settings', + defaultMessage: 'Settings', + }, + items: [ + { + name: 'required', + type: 'checkbox', + intlLabel: { + id: 'form.attribute.item.requiredField', + defaultMessage: 'Required field', + }, + description: { + id: 'form.attribute.item.requiredField.description', + defaultMessage: "You won't be able to create an entry if this field is empty", + }, + }, + { + name: 'private', + type: 'checkbox', + intlLabel: { + id: 'form.attribute.item.privateField', + defaultMessage: 'Private field', + }, + description: { + id: 'form.attribute.item.privateField.description', + defaultMessage: 'This field will not show up in the API response', + }, + }, + ], + }, + ], }, }); }, diff --git a/server/src/register.js b/server/src/register.js index 205846a..587f7c9 100644 --- a/server/src/register.js +++ b/server/src/register.js @@ -6,6 +6,10 @@ const register = ({ strapi }) => { name: 'checkbox-list', plugin: 'checkbox-list', type: 'json', + inputSize: { + default: 6, + isResizable: true, + }, }); }; exports.default = register; diff --git a/server/src/register.ts b/server/src/register.ts index 0d0bbd9..55f60c9 100644 --- a/server/src/register.ts +++ b/server/src/register.ts @@ -6,6 +6,10 @@ const register = ({ strapi }: { strapi: Core.Strapi }) => { name: 'checkbox-list', plugin: 'checkbox-list', type: 'json', + inputSize: { + default: 6, + isResizable: true, + }, }); }; |
