diff --git a/dev/test-studio/schema/debug/simpleArrayOfObjects.js b/dev/test-studio/schema/debug/simpleArrayOfObjects.js index 16d004219f8..e06eb162793 100644 --- a/dev/test-studio/schema/debug/simpleArrayOfObjects.js +++ b/dev/test-studio/schema/debug/simpleArrayOfObjects.js @@ -13,7 +13,11 @@ export const simpleArrayOfObjects = { }, { name: 'arrayWithObjects', - options: {collapsible: true, collapsed: true}, + options: { + collapsible: true, + collapsed: true, + disableActions: ['add'], + }, title: 'Array with named objects', description: 'This array contains objects of type as defined inline', type: 'array', diff --git a/packages/@sanity/types/src/schema/definition/type/array.ts b/packages/@sanity/types/src/schema/definition/type/array.ts index efecfdc1cfb..e2d024885b6 100644 --- a/packages/@sanity/types/src/schema/definition/type/array.ts +++ b/packages/@sanity/types/src/schema/definition/type/array.ts @@ -12,6 +12,38 @@ import {type BaseSchemaDefinition, type SearchConfiguration, type TitledListValu export type {InsertMenuOptions} +/** + * Types of array actions that can be performed + * @beta + */ +export type ArrayActionName = + /** + * Add any item to the array at any position + */ + | 'add' + /** + * Add item after an existing item + */ + | 'addBefore' + + /** + * Add item after an existing item + */ + | 'addAfter' + /** + * Remove any item + */ + | 'remove' + /** + * Duplicate item + */ + | 'duplicate' + + /** + * Copy item + */ + | 'copy' + /** @public */ export interface ArrayOptions extends SearchConfiguration { list?: TitledListValue[] | V[] @@ -30,6 +62,13 @@ export interface ArrayOptions extends SearchConfiguration { * @deprecated tree editing beta feature has been disabled */ treeEditing?: boolean + + /** + * A list of array actions to disable + * Possible options are defined by {@link ArrayActionName} + * @beta + */ + disableActions?: ArrayActionName[] } /** @public */ diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx index 532a9b11632..ea2a126968a 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx @@ -65,6 +65,10 @@ export function ArrayOfObjectsFunctions< }, }) + if (schemaType.options?.disableActions?.includes('add')) { + return null + } + if (readOnly) { return ( diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuGroups.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuGroups.tsx index 55a935ed191..ca8921927e0 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuGroups.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuGroups.tsx @@ -41,7 +41,7 @@ export const InsertMenuGroups = memo(function InsertMenuGroups(props: Props) { ) }) -function InsertMenuGroup( +export function InsertMenuGroup( props: Props & { pos: 'before' | 'after' text: ComponentProps['text'] diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuMenuItems.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuMenuItems.tsx index 9789da8dc8c..4a056eb316c 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuMenuItems.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenuMenuItems.tsx @@ -78,6 +78,7 @@ export function useInsertMenuMenuItems(props: InsertMenuItemsProps) { () => types ? ( types ? ( (props: PreviewItemProps) { const { schemaType, @@ -144,9 +144,55 @@ export function PreviewItem(props: Preview referenceElement: contextMenuButtonElement, }) + const disableActions = parentSchemaType.options?.disableActions || EMPTY_ARRAY + + const menuItems = useMemo(() => { + return [ + !disableActions.includes('remove') && ( + + ), + !disableActions.includes('copy') && ( + + ), + !disableActions.includes('duplicate') && ( + + ), + !disableActions.includes('add') && + !disableActions.includes('addBefore') && + insertBefore.menuItem, + !disableActions.includes('add') && + !disableActions.includes('addAfter') && + insertAfter.menuItem, + ].filter(Boolean) + }, [ + disableActions, + handleCopy, + handleDuplicate, + insertAfter.menuItem, + insertBefore.menuItem, + onRemove, + t, + ]) + const menu = useMemo( () => - readOnly ? null : ( + readOnly || menuItems.length === 0 ? null : ( <> (props: Preview /> } id={`${props.inputId}-menuButton`} - menu={ - - - - - {insertBefore.menuItem} - {insertAfter.menuItem} - - } + menu={{menuItems}} popover={MENU_POPOVER_PROPS} /> {insertBefore.popover} {insertAfter.popover} ), - [readOnly, insertBefore, insertAfter, props.inputId, t, onRemove, handleCopy, handleDuplicate], + [menuItems, readOnly, insertBefore, insertAfter, props.inputId], ) const tone = getTone({readOnly, hasErrors, hasWarnings}) diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ArrayOfPrimitivesFunctions.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ArrayOfPrimitivesFunctions.tsx index 36f6b9cbe4e..30da871f565 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ArrayOfPrimitivesFunctions.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ArrayOfPrimitivesFunctions.tsx @@ -39,6 +39,10 @@ export function ArrayOfPrimitivesFunctions< ? 'inputs.array.action.add-item-select-type' : 'inputs.array.action.add-item' + if (schemaType.options?.disableActions?.includes('add')) { + return null + } + if (readOnly) { return ( diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx index b5f70aac64e..a1843317965 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx @@ -1,4 +1,4 @@ -import {AddDocumentIcon, CopyIcon, TrashIcon} from '@sanity/icons' +import {AddDocumentIcon, CopyIcon, InsertAboveIcon, InsertBelowIcon, TrashIcon} from '@sanity/icons' import {type SchemaType} from '@sanity/types' import {Box, Flex, Menu} from '@sanity/ui' import {type ForwardedRef, forwardRef, useCallback, useMemo} from 'react' @@ -9,7 +9,7 @@ import {useTranslation} from '../../../../i18n' import {FieldPresence} from '../../../../presence' import {FormFieldValidationStatus} from '../../../components/formField' import {type PrimitiveItemProps} from '../../../types/itemProps' -import {InsertMenuGroups} from '../ArrayOfObjectsInput/InsertMenuGroups' +import {InsertMenuGroup} from '../ArrayOfObjectsInput/InsertMenuGroups' import {RowLayout} from '../layouts/RowLayout' import {getEmptyValue} from './getEmptyValue' @@ -33,6 +33,7 @@ export const ItemRow = forwardRef(function ItemRow( onRemove, readOnly, inputId, + parentSchemaType, validation, children, presence, @@ -72,28 +73,61 @@ export const ItemRow = forwardRef(function ItemRow( const {t} = useTranslation() + const disableActions = parentSchemaType.options?.disableActions || [] + + const menuItems = [ + !disableActions.includes('remove') && ( + + ), + !disableActions.includes('copy') && ( + + ), + !disableActions.includes('duplicate') && ( + + ), + !(disableActions.includes('add') || disableActions.includes('addBefore')) && ( + + ), + !disableActions.includes('add') && + !(disableActions.includes('addAfter') && disableActions.includes('addBefore')) && ( + + ), + ] + const menu = ( } id={`${inputId}-menuButton`} popover={MENU_BUTTON_POPOVER_PROPS} - menu={ - - - - - - - } + menu={{menuItems}} /> )