From 84affbac034346bc3ceab52f288e29ab4ec899b9 Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Mon, 30 Oct 2023 10:37:57 +0100 Subject: [PATCH] feat(facelift): create menu item StudioUI --- .eslintrc.cjs | 2 +- dev/test-studio/schema/book.ts | 12 ++ .../collapseMenu/CollapseOverflowMenu.tsx | 3 +- .../field/actions/FieldActionMenuItem.tsx | 6 +- .../object/BlockObjectActionsMenu.tsx | 12 +- .../PortableText/toolbar/BlockStyleSelect.tsx | 5 +- .../inputs/ReferenceInput/ReferenceField.tsx | 2 +- .../inputs/ReferenceInput/ReferenceItem.tsx | 2 +- .../ArrayOfObjectsFunctions.tsx | 13 +- .../ArrayOfObjectsInput/Grid/ErrorItem.tsx | 3 +- .../ArrayOfObjectsInput/Grid/GridItem.tsx | 14 +- .../arrays/ArrayOfObjectsInput/InsertMenu.tsx | 3 +- .../ArrayOfObjectsInput/List/ErrorItem.tsx | 5 +- .../ArrayOfObjectsInput/List/PreviewItem.tsx | 14 +- .../arrays/ArrayOfPrimitivesInput/ItemRow.tsx | 3 +- .../form/inputs/files/FileInput/FileInput.tsx | 12 +- .../inputs/files/ImageInput/ImageInput.tsx | 2 +- .../form/inputs/files/common/ActionsMenu.tsx | 4 +- .../FileInputMenuItem.styled.tsx | 2 +- .../FileInputMenuItem/FileInputMenuItem.tsx | 79 +++------ .../form/studio/assetSource/AssetMenu.tsx | 3 +- .../navbar/presence/PresenceMenu.tsx | 3 +- .../navbar/presence/PresenceMenuItem.tsx | 44 ++--- .../navbar/resources/ResourcesMenuItems.tsx | 14 +- .../navbar/search/components/SortMenu.tsx | 22 +-- .../filters/filter/OperatorsMenuButton.tsx | 26 ++- .../filters/filter/inputs/asset/Asset.tsx | 4 +- .../filter/inputs/string/StringList.tsx | 25 ++- .../components/navbar/userMenu/UserMenu.tsx | 2 +- .../navbar/workspace/WorkspaceMenuButton.tsx | 5 +- .../DocumentPane/DocumentActions.tsx | 12 +- .../PaneHeaderCreateButton.tsx | 3 +- .../document/statusBar/ActionMenuButton.tsx | 91 ++++------ packages/sanity/src/ui/index.ts | 1 + packages/sanity/src/ui/menuItem/MenuItem.tsx | 161 ++++++++++++++++++ packages/sanity/src/ui/menuItem/index.ts | 1 + 36 files changed, 315 insertions(+), 300 deletions(-) create mode 100644 packages/sanity/src/ui/menuItem/MenuItem.tsx create mode 100644 packages/sanity/src/ui/menuItem/index.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1bc86895255..1811bc9898d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -221,7 +221,7 @@ module.exports = { paths: [ { name: '@sanity/ui', - importNames: ['Tooltip', 'TooltipProps'], + importNames: ['Tooltip', 'TooltipProps', 'MenuItem', 'MenuItemProps'], message: 'Please use the (more opinionated) exported components in sanity/src/ui instead.', }, diff --git a/dev/test-studio/schema/book.ts b/dev/test-studio/schema/book.ts index 738957b4c37..b9d29e24272 100644 --- a/dev/test-studio/schema/book.ts +++ b/dev/test-studio/schema/book.ts @@ -88,6 +88,18 @@ export default { }, ], }, + { + name: 'genre', + title: 'Genre', + type: 'string', + options: { + list: [ + {title: 'Fiction', value: 'fiction'}, + {title: 'Non Fiction', value: 'nonfiction'}, + {title: 'Poetry', value: 'poetry'}, + ], + }, + }, ], orderings: [ { diff --git a/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx b/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx index 3b37eb3cd02..01d4d1c0b49 100644 --- a/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx +++ b/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx @@ -1,5 +1,6 @@ -import {Menu, MenuButton, MenuButtonProps, MenuDivider, MenuItem} from '@sanity/ui' +import {Menu, MenuButton, MenuButtonProps, MenuDivider} from '@sanity/ui' import React from 'react' +import {MenuItem} from '../../../ui' import {CollapseMenuProps} from './CollapseMenu' const MENU_BUTTON_POPOVER_PROPS: MenuButtonProps['popover'] = { diff --git a/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx b/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx index 88cc5017289..497bd052de2 100644 --- a/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx +++ b/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx @@ -1,8 +1,9 @@ -import {MenuItem, Text} from '@sanity/ui' +import {Text} from '@sanity/ui' import React, {useCallback} from 'react' import {CheckmarkIcon} from '@sanity/icons' import {TooltipOfDisabled} from '../../../components' import {DocumentFieldActionItem} from '../../../config' +import {MenuItem} from '../../../../ui' export function FieldActionMenuItem(props: {action: DocumentFieldActionItem}) { const {action} = props @@ -19,13 +20,10 @@ export function FieldActionMenuItem(props: {action: DocumentFieldActionItem}) { diff --git a/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx b/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx index 39483104ad7..a34d03095a2 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx @@ -1,14 +1,5 @@ import {EditIcon, LinkIcon, TrashIcon, EyeOpenIcon, EllipsisVerticalIcon} from '@sanity/icons' -import { - Box, - Button, - Flex, - Menu, - MenuButton, - MenuButtonProps, - MenuItem, - useGlobalKeyDown, -} from '@sanity/ui' +import {Box, Button, Flex, Menu, MenuButton, MenuButtonProps, useGlobalKeyDown} from '@sanity/ui' import React, { forwardRef, ReactElement, @@ -20,6 +11,7 @@ import React, { PropsWithChildren, } from 'react' import {PortableTextBlock, isReference} from '@sanity/types' +import {MenuItem} from '../../../../../ui' import {IntentLink} from 'sanity/router' interface BlockObjectActionsMenuProps extends PropsWithChildren { diff --git a/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx b/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx index 0289fe7ac95..d04e56fe0ce 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx @@ -1,6 +1,6 @@ import React, {memo, useCallback, useMemo} from 'react' import {PortableTextEditor, usePortableTextEditor} from '@sanity/portable-text-editor' -import {Button, Menu, MenuButton, MenuButtonProps, MenuItem, Text} from '@sanity/ui' +import {Button, Menu, MenuButton, MenuButtonProps, Text} from '@sanity/ui' import {SelectIcon} from '@sanity/icons' import styled from 'styled-components' import { @@ -13,6 +13,7 @@ import { BlockQuote, Normal, } from '../text/textStyles' +import {MenuItem} from '../../../../../ui' import {useActiveStyleKeys, useFocusBlock} from './hooks' import {BlockStyleItem} from './types' @@ -145,6 +146,8 @@ export const BlockStyleSelect = memo(function BlockStyleSelect( // eslint-disable-next-line react/jsx-no-bind onClick={_disabled ? undefined : () => handleChange(item)} > + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore: This item uses a specific render of the menu item.*/} {renderOption(item.style, item?.title || item.style)} ) diff --git a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx index 2eeebc6f66b..be5d8b332b9 100644 --- a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx +++ b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx @@ -17,7 +17,6 @@ import { Menu, MenuButton, MenuDivider, - MenuItem, Stack, Text, } from '@sanity/ui' @@ -36,6 +35,7 @@ import {AlertStrip} from '../../components/AlertStrip' import {FieldActionsResolver} from '../../field' import {DocumentFieldActionNode} from '../../../config' import {useFormPublishedId} from '../../useFormPublishedId' +import {MenuItem} from '../../../../ui' import {useReferenceInput} from './useReferenceInput' import {useReferenceInfo} from './useReferenceInfo' import {PreviewReferenceValue} from './PreviewReferenceValue' diff --git a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx index 9ab53889c0a..3069a4761f2 100644 --- a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx +++ b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx @@ -8,7 +8,6 @@ import { Menu, MenuButton, MenuDivider, - MenuItem, Spinner, Stack, Text, @@ -36,6 +35,7 @@ import {AlertStrip} from '../../components/AlertStrip' import {set, unset} from '../../patch' import {createProtoArrayValue} from '../arrays/ArrayOfObjectsInput/createProtoArrayValue' import {InsertMenu} from '../arrays/ArrayOfObjectsInput/InsertMenu' +import {MenuItem} from '../../../../ui' import {useReferenceInfo} from './useReferenceInfo' import {PreviewReferenceValue} from './PreviewReferenceValue' import {useReferenceInput} from './useReferenceInput' 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 23e18210219..b25a8576690 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx @@ -1,18 +1,9 @@ import {ArraySchemaType, isReferenceSchemaType} from '@sanity/types' import {AddIcon} from '@sanity/icons' import React, {useId, useCallback} from 'react' -import { - Box, - Button, - Grid, - Menu, - MenuButton, - MenuItem, - Tooltip, - Text, - MenuButtonProps, -} from '@sanity/ui' +import {Box, Button, Grid, Menu, MenuButton, Tooltip, Text, MenuButtonProps} from '@sanity/ui' import {ArrayInputFunctionsProps, ObjectItem} from '../../../types' +import {MenuItem} from '../../../../../ui' const POPOVER_PROPS: MenuButtonProps['popover'] = { constrainSize: true, diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx index cef935910e3..d1a7bb30fad 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx @@ -1,10 +1,11 @@ import React, {useCallback, useId} from 'react' import {EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' -import {Button, Menu, MenuButton, MenuItem} from '@sanity/ui' +import {Button, Menu, MenuButton} from '@sanity/ui' import {ArrayItemError} from '../../../../store' import {useFormCallbacks} from '../../../../studio/contexts/FormCallbacks' import {PatchEvent, unset} from '../../../../patch' import {CellLayout} from '../../layouts/CellLayout' +import {MenuItem} from '../../../../../../ui' import {IncompatibleItemType} from './IncompatibleItemType' const MENU_POPOVER_PROPS = {portal: true, tone: 'default'} as const diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx index f663756ff43..32a24dc9652 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx @@ -1,15 +1,4 @@ -import { - Box, - Button, - Card, - CardTone, - Flex, - Menu, - MenuButton, - MenuItem, - Spinner, - Text, -} from '@sanity/ui' +import {Box, Button, Card, CardTone, Flex, Menu, MenuButton, Spinner, Text} from '@sanity/ui' import React, {useCallback, useMemo, useRef} from 'react' import {SchemaType} from '@sanity/types' import {CopyIcon as DuplicateIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' @@ -29,6 +18,7 @@ import {createProtoArrayValue} from '../createProtoArrayValue' import {InsertMenu} from '../InsertMenu' import {FIXME} from '../../../../../FIXME' import {EditPortal} from '../../../../components/EditPortal' +import {MenuItem} from '../../../../../../ui' type GridItemProps = Omit, 'renderDefault'> diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx index 507511b649d..b2b62830570 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx @@ -1,8 +1,9 @@ /* eslint-disable react/jsx-no-bind */ import {SchemaType} from '@sanity/types' import React, {ComponentProps, memo} from 'react' -import {MenuGroup, MenuItem, PopoverProps} from '@sanity/ui' +import {MenuGroup, PopoverProps} from '@sanity/ui' import {InsertAboveIcon, InsertBelowIcon} from '@sanity/icons' +import {MenuItem} from '../../../../../ui' interface Props { types?: SchemaType[] diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx index d3a72602c6d..7b895e9c706 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx @@ -1,6 +1,7 @@ -import React, {useCallback, useId} from 'react' +import React, {useId} from 'react' import {EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' -import {Box, Button, Menu, MenuButton, MenuItem} from '@sanity/ui' +import {Box, Button, Menu, MenuButton} from '@sanity/ui' +import {MenuItem} from '../../../../../../ui' import {ArrayItemError} from '../../../../store' import {RowLayout} from '../../layouts/RowLayout' import {IncompatibleItemType} from './IncompatibleItemType' diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx index 64e9c890564..0d4ac3b93ae 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx @@ -1,15 +1,4 @@ -import { - Box, - Button, - Card, - CardTone, - Flex, - Menu, - MenuButton, - MenuItem, - Spinner, - Text, -} from '@sanity/ui' +import {Box, Button, Card, CardTone, Flex, Menu, MenuButton, Spinner, Text} from '@sanity/ui' import React, {useCallback, useMemo, useRef} from 'react' import {SchemaType} from '@sanity/types' import {CopyIcon as DuplicateIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' @@ -27,6 +16,7 @@ import {RowLayout} from '../../layouts/RowLayout' import {createProtoArrayValue} from '../createProtoArrayValue' import {InsertMenu} from '../InsertMenu' import {EditPortal} from '../../../../components/EditPortal' +import {MenuItem} from '../../../../../../ui' type PreviewItemProps = Omit, 'renderDefault'> 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 ec19d867195..e28e7272031 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx @@ -1,5 +1,5 @@ import React, {useCallback, useMemo} from 'react' -import {Box, Button, Flex, Menu, MenuButton, MenuItem} from '@sanity/ui' +import {Box, Button, Flex, Menu, MenuButton} from '@sanity/ui' import {SchemaType} from '@sanity/types' import {CopyIcon as DuplicateIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' import {FormFieldValidationStatus} from '../../../components/formField' @@ -7,6 +7,7 @@ import {InsertMenu} from '../ArrayOfObjectsInput/InsertMenu' import {PrimitiveItemProps} from '../../../types/itemProps' import {RowLayout} from '../layouts/RowLayout' import {FieldPresence} from '../../../../presence' +import {MenuItem} from '../../../../../ui' import {getEmptyValue} from './getEmptyValue' export type DefaultItemProps = Omit & { diff --git a/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx b/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx index d1f463ca7ac..6574ccfc77e 100644 --- a/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx +++ b/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx @@ -13,16 +13,7 @@ import { UploadState, } from '@sanity/types' import {ImageIcon, SearchIcon} from '@sanity/icons' -import { - Box, - Button, - Card, - Menu, - MenuButton, - MenuItem, - ThemeColorToneKey, - ToastParams, -} from '@sanity/ui' +import {Box, Button, Card, Menu, MenuButton, ThemeColorToneKey, ToastParams} from '@sanity/ui' import {SanityClient} from '@sanity/client' import {isFileSource} from '@sanity/asset-utils' import {WithReferencedAsset} from '../../../utils/WithReferencedAsset' @@ -38,6 +29,7 @@ import {InputProps, ObjectInputProps} from '../../../types' import {PatchEvent, setIfMissing, unset} from '../../../patch' import {MemberField, MemberFieldError, MemberFieldSet} from '../../../members' import {ImperativeToast} from '../../../../components' +import {MenuItem} from '../../../../../ui' import {ChangeIndicator} from '../../../../changeIndicators' import {CardOverlay, FlexContainer} from './styles' import {FileActionsMenu} from './FileActionsMenu' diff --git a/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx b/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx index 41de27bfa8e..4ec20508227 100644 --- a/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx +++ b/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx @@ -9,7 +9,6 @@ import { Menu, MenuButton, MenuButtonProps, - MenuItem, Stack, ToastParams, } from '@sanity/ui' @@ -52,6 +51,7 @@ import {PresenceOverlay} from '../../../../presence' import {FIXME} from '../../../../FIXME' import {ImperativeToast} from '../../../../components' import {ChangeIndicator} from '../../../../changeIndicators' +import {MenuItem} from '../../../../../ui' import {ImageActionsMenu} from './ImageActionsMenu' import {ImagePreview} from './ImagePreview' import {InvalidImageWarning} from './InvalidImageWarning' diff --git a/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx b/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx index d254d1bd653..2c0502a4574 100644 --- a/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx +++ b/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx @@ -1,7 +1,8 @@ import React, {MouseEventHandler, useCallback} from 'react' import {UploadIcon, CopyIcon, ResetIcon, DownloadIcon} from '@sanity/icons' -import {Box, MenuItem, MenuDivider, Label, useToast} from '@sanity/ui' +import {Box, MenuDivider, Label, useToast} from '@sanity/ui' +import {MenuItem} from '../../../../../ui' import {FileInputMenuItem} from './FileInputMenuItem/FileInputMenuItem' interface Props { @@ -40,7 +41,6 @@ export function ActionsMenu(props: Props) { text="Upload" data-testid="file-input-upload-button" disabled={readOnly || !directUploads} - fontSize={2} /> {browse} diff --git a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx index a6b29dd6acd..45a574b13b2 100644 --- a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx +++ b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx @@ -1,5 +1,5 @@ -import {MenuItem} from '@sanity/ui' import styled from 'styled-components' +import {MenuItem} from '../../../../../../ui' export const FileMenuItem = styled(MenuItem)` position: relative; diff --git a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx index de864737015..3f1c12b4a21 100644 --- a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx +++ b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx @@ -1,4 +1,4 @@ -import React, {createElement, isValidElement, useId} from 'react' +import React, {createElement, isValidElement, useCallback, useId} from 'react' import {isValidElementType} from 'react-is' import {Box, ButtonProps, Flex, Text} from '@sanity/ui' import {FileMenuItem} from './FileInputMenuItem.styled' @@ -16,21 +16,7 @@ export const FileInputMenuItem = React.forwardRef(function FileInputMenuItem( Omit, 'as' | 'ref' | 'type' | 'value' | 'onSelect'>, forwardedRef: React.ForwardedRef, ) { - const { - icon, - id: idProp, - accept, - capture, - fontSize, - multiple, - onSelect, - padding = 3, - space = 3, - textAlign, - text, - disabled, - ...rest - } = props + const {icon, id: idProp, accept, capture, multiple, onSelect, text, disabled, ...rest} = props const id = `${idProp || ''}-${useId()}` const handleChange = React.useCallback( @@ -42,50 +28,35 @@ export const FileInputMenuItem = React.forwardRef(function FileInputMenuItem( [onSelect], ) - const content = ( - - {/* Icon */} - {icon && ( - - - {isValidElement(icon) && icon} - {isValidElementType(icon) && createElement(icon)} - - - )} - - {/* Text */} - {text && ( - - {text} - - )} - + const renderMenuItem = useCallback( + (item: React.JSX.Element) => ( +
+ {item} + {/* Visibly hidden input */} + +
+ ), + [accept, capture, disabled, handleChange, id, multiple], ) - return ( - {content} - - {/* Visibly hidden input */} - - + icon={icon} + text={text} + renderMenuItem={renderMenuItem} + /> ) }) diff --git a/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx b/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx index 8a21b03382c..fb379fd9c80 100644 --- a/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx +++ b/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx @@ -1,6 +1,7 @@ import React from 'react' import {LinkIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' -import {Button, Menu, MenuItem, MenuButton} from '@sanity/ui' +import {Button, Menu, MenuButton} from '@sanity/ui' +import {MenuItem} from '../../../../ui' import {AssetMenuAction} from './types' export function AssetMenu({ diff --git a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx index d5eb2eb940d..6ca5e19d1cb 100644 --- a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx +++ b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx @@ -7,7 +7,6 @@ import { Menu, MenuButton, MenuDivider, - MenuItem, Stack, Text, } from '@sanity/ui' @@ -16,6 +15,7 @@ import styled from 'styled-components' import {StatusButton, UserAvatar} from '../../../../components' import {useGlobalPresence} from '../../../../store' import {useColorScheme} from '../../../colorScheme' +import {MenuItem} from '../../../../../ui' import {useWorkspace} from '../../../workspace' import {PresenceMenuItem} from './PresenceMenuItem' @@ -131,7 +131,6 @@ export function PresenceMenu(props: PresenceMenuProps) { href={`https://sanity.io/manage/project/${projectId}`} iconRight={CogIcon} onFocus={handleClearFocusedItem} - paddingY={4} rel="noopener noreferrer" target="_blank" text="Manage members" diff --git a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx index 581adbec073..375e435bb41 100644 --- a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx +++ b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx @@ -1,17 +1,11 @@ import React, {forwardRef, memo, useCallback, useEffect, useMemo, useState} from 'react' import {orderBy} from 'lodash' import * as PathUtils from '@sanity/util/paths' -import {Card, Flex, MenuItem, Stack, Text} from '@sanity/ui' -import styled from 'styled-components' import {GlobalPresence} from '../../../../store' import {UserAvatar} from '../../../../components' +import {MenuItem} from '../../../../../ui' import {IntentLink} from 'sanity/router' -const AvatarCard = styled(Card)` - background: transparent; - margin: calc((-35px + 11px) / 2); -` - interface PresenceListRowProps { focused: boolean onFocus: (id: string) => void @@ -69,33 +63,21 @@ export const PresenceMenuItem = memo(function PresenceMenuItem(props: PresenceLi return ( - - - - - - - {presence.user.displayName} - {!hasLink && ( - - Not in a document - - )} - - - + text={presence.user.displayName} + subText={hasLink ? undefined : 'Not in a document'} + avatar={ + + } + /> ) }) diff --git a/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx b/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx index a106b0fec1e..6166d1f2700 100644 --- a/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx +++ b/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx @@ -1,6 +1,7 @@ -import {Box, Card, Flex, Label, MenuDivider, MenuItem, Spinner, Text} from '@sanity/ui' +import {Box, Card, Flex, Label, MenuDivider, Spinner, Text} from '@sanity/ui' import React from 'react' import {SANITY_VERSION} from '../../../../version' +import {MenuItem} from '../../../../../ui' import {ResourcesResponse, Section} from './helper-functions/types' interface ResourcesMenuItemProps { @@ -54,26 +55,20 @@ const fallbackLinks = ( @@ -100,7 +95,6 @@ function SubSection({subSection}: {subSection: Section}) { tone="default" key={item._key} text={item.title} - size={0} href={item.url} target="_blank" /> @@ -108,9 +102,7 @@ function SubSection({subSection}: {subSection: Section}) { case 'internalAction': // TODO: Add support for internal actions (MVI-2) if (!item.type) return null return ( - item.type === 'show-welcome-modal' && ( - - ) + item.type === 'show-welcome-modal' && ) default: diff --git a/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx b/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx index 0b8b844ce58..57e16f75650 100644 --- a/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx +++ b/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx @@ -1,22 +1,12 @@ import {SortIcon} from '@sanity/icons' -import { - Box, - Button, - Card, - Flex, - Inline, - Menu, - MenuButton, - MenuDivider, - MenuItem, - Text, -} from '@sanity/ui' +import {Box, Button, Card, Flex, Inline, Menu, MenuButton, MenuDivider, Text} from '@sanity/ui' import isEqual from 'lodash/isEqual' import React, {useCallback, useId, useMemo} from 'react' import styled from 'styled-components' import {ORDERINGS} from '../definitions/orderings' import {useSearchState} from '../contexts/search/useSearchState' import type {SearchOrdering} from '../types' +import {MenuItem} from '../../../../../../ui' interface SearchDivider { type: 'divider' @@ -53,13 +43,7 @@ function CustomMenuItem({ordering}: {ordering: SearchOrdering}) { const isSelected = useMemo(() => isEqual(currentOrdering, ordering), [currentOrdering, ordering]) return ( - - - - {ordering.title} - - - + ) } diff --git a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx index 588fd80c362..2b752aa5c87 100644 --- a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx +++ b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx @@ -1,11 +1,12 @@ import {SelectIcon} from '@sanity/icons' -import {Box, Button, Flex, Inline, Menu, MenuButton, MenuDivider, MenuItem, Text} from '@sanity/ui' -import React, {createElement, useCallback, useId} from 'react' +import {Box, Button, Flex, Inline, Menu, MenuButton, MenuDivider, Text} from '@sanity/ui' +import React, {useCallback, useId} from 'react' import {useSearchState} from '../../../contexts/search/useSearchState' import {getFilterDefinition} from '../../../definitions/filters' import {getOperatorDefinition, SearchOperatorDefinition} from '../../../definitions/operators' import type {SearchFilter} from '../../../types' import {getFilterKey} from '../../../utils/filterUtils' +import {MenuItem} from '../../../../../../../../ui' interface OperatorsMenuButtonProps { filter: SearchFilter @@ -24,20 +25,13 @@ function CustomMenuItem({ const handleClick = useCallback(() => onClick(operator.type), [onClick, operator.type]) return ( - - - - - {operator.label} - - - {operator?.icon && ( - - {createElement(operator.icon)} - - )} - - + ) } diff --git a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx index 914953d0347..2ecff253bc3 100644 --- a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx +++ b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx @@ -1,6 +1,6 @@ import {ChevronDownIcon, ImageIcon, SearchIcon, UndoIcon} from '@sanity/icons' import type {AssetFromSource, AssetSource, ReferenceValue} from '@sanity/types' -import {Box, Button, Flex, Menu, MenuButton, MenuItem, Portal, Stack} from '@sanity/ui' +import {Box, Button, Flex, Menu, MenuButton, Portal, Stack} from '@sanity/ui' import {get} from 'lodash' import React, {useCallback, useEffect, useId, useMemo, useState} from 'react' import styled from 'styled-components' @@ -11,6 +11,7 @@ import {DEFAULT_STUDIO_CLIENT_OPTIONS} from '../../../../../../../../../studioCl import {useSource} from '../../../../../../../../source' import {useSearchState} from '../../../../../contexts/search/useSearchState' import type {OperatorInputComponentProps} from '../../../../../definitions/operators/operatorTypes' +import {MenuItem} from '../../../../../../../../../../ui' import {AssetSourceError} from './AssetSourceError' import {AssetPreview} from './preview/AssetPreview' @@ -149,7 +150,6 @@ export function SearchFilterAssetInput(type?: AssetType) { {assetSources.map((source) => ( , 'title'> { title: (number | string)[] @@ -27,20 +28,14 @@ function CustomMenuItem({ const handleClick = useCallback(() => onClick(value), [onClick, value]) return ( - - - - - {title} - - {value && ( - - {value} - - )} - - - + ) } diff --git a/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx b/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx index ad170be1d69..d8ce3d43f48 100644 --- a/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx +++ b/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx @@ -9,7 +9,6 @@ import { MenuButton, MenuButtonProps, MenuDivider, - MenuItem, Stack, Text, Tooltip, @@ -26,6 +25,7 @@ import { } from '../../../colorScheme' import {useWorkspace} from '../../../workspace' import {userHasRole} from '../../../../util/userHasRole' +import {MenuItem} from '../../../../../ui' import {LoginProviderLogo} from './LoginProviderLogo' const AVATAR_SIZE = 1 diff --git a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx index 67fc4f3d7dd..9c5dedabdbb 100644 --- a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx +++ b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx @@ -2,7 +2,6 @@ import {SelectIcon} from '@sanity/icons' import { Button, MenuButton, - MenuItem, Menu, MenuButtonProps, Box, @@ -16,6 +15,7 @@ import styled from 'styled-components' import {useActiveWorkspace} from '../../../activeWorkspaceMatcher' import {useColorScheme} from '../../../colorScheme' import {useWorkspaces} from '../../../workspaces' +import {MenuItem} from '../../../../../ui' import {useWorkspaceAuthStates} from './hooks' import {WorkspacePreview} from './WorkspacePreview' import {useRouter} from 'sanity/router' @@ -112,9 +112,10 @@ export function WorkspaceMenuButton(props: WorkspaceMenuButtonProps) { key={workspace.name} // eslint-disable-next-line react/jsx-no-bind onClick={handleSelectWorkspace} - padding={2} pressed={workspace.name === activeWorkspace.name} > + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore - Workspace preview hasn't been redesigned for facelift */} - {actionState.title} - + const menuItemContent = useCallback( + (item: React.JSX.Element) => { + // TODO: Once the tooltip changes land, we can use the new `content` prop instead + const tooltipContent = actionState.title && ( + + {actionState.title} + + ) + return ( + + {item} + + ) + }, + [actionState.title], ) - return ( - - - - {actionState.icon && ( - - - {isValidElement(actionState.icon) && actionState.icon} - {isValidElementType(actionState.icon) && createElement(actionState.icon)} - - - )} - - {actionState.label} - - - {actionState.shortcut && ( - - s.slice(0, 1).toUpperCase() + s.slice(1))} - /> - - )} - - - + icon={actionState.icon} + text={actionState.label} + hotkeys={ + actionState.shortcut + ? String(actionState.shortcut) + .split('+') + .map((s) => s.slice(0, 1).toUpperCase() + s.slice(1)) + : undefined + } + renderMenuItem={menuItemContent} + /> ) } diff --git a/packages/sanity/src/ui/index.ts b/packages/sanity/src/ui/index.ts index fa5a7e52455..d1786efffbb 100644 --- a/packages/sanity/src/ui/index.ts +++ b/packages/sanity/src/ui/index.ts @@ -1 +1,2 @@ export * from './tooltip' +export * from './menuItem' diff --git a/packages/sanity/src/ui/menuItem/MenuItem.tsx b/packages/sanity/src/ui/menuItem/MenuItem.tsx new file mode 100644 index 00000000000..f8d9e376698 --- /dev/null +++ b/packages/sanity/src/ui/menuItem/MenuItem.tsx @@ -0,0 +1,161 @@ +import { + Flex, + MenuItem as UIMenuItem, + MenuItemProps as UIMenuItemProps, + Box, + Text, + Badge, + Stack, + Hotkeys, +} from '@sanity/ui' +import React, {createElement, forwardRef, isValidElement, useMemo} from 'react' +import {isValidElementType} from 'react-is' + +interface LargeMenuItem { + size: 'large' + subText?: string + badgeText?: string + avatar?: React.ReactNode + /** + * Hotkeys are only supported in `size="small"` menu items. + */ + hotkeys?: undefined + /** + * Icon is only supported in `size="small"` menu items. + */ + icon?: undefined +} + +interface SmallMenuItem { + size?: 'small' + hotkeys?: UIMenuItemProps['hotkeys'] + /** + * Sub text is only supported in `size="large"` menu items. + */ + subText?: undefined + /** + * Badge text is only supported in `size="large"` menu items. + */ + badgeText?: undefined + /** + * Avatar is only supported in `size="large"` menu items. + */ + avatar?: undefined +} + +const fontSize = 1 + +/** @internal */ +export type MenuItemProps = Pick< + UIMenuItemProps, + 'as' | 'icon' | 'iconRight' | 'pressed' | 'selected' | 'text' | 'tone' +> & + (LargeMenuItem | SmallMenuItem) & + Omit< + React.HTMLProps, + 'as' | 'height' | 'ref' | 'selected' | 'tabIndex' | 'size' + > & { + /** + * Allows to add wrappers to the menu item, e.g. `Tooltip`. + */ + renderMenuItem?: (menuItem: React.JSX.Element) => React.ReactNode + /** + * Usage of `children` is not recommended but still supported. + * Try using `renderMenuItem` instead. + * To use children opt out with `@ts-ignore`. + */ + children?: undefined + } + +/** + * Studio UI . + * + * Studio UI components are opinionated `@sanity/ui` components meant for internal use only. + * Props and options are intentionally limited to ensure consistency and ease of use. + * + * @internal + */ +export const MenuItem = forwardRef(function MenuItem( + { + size = 'small', + badgeText, + subText, + text, + avatar = null, + icon, + iconRight, + hotkeys, + children, + ...props + }: MenuItemProps, + ref: React.Ref, +) { + const menuItemContent = useMemo(() => { + if (size === 'large') { + return ( + + {avatar} + {(text || subText) && ( + + {text && ( + + {text} + + )} + {subText && ( + + {subText} + + )} + + )} + {badgeText && ( + + {badgeText} + + )} + + ) + } + + return ( + + {icon && ( + + {isValidElement(icon) && icon} + {isValidElementType(icon) && createElement(icon)} + + )} + + {text && ( + + + {text} + + + )} + + {hotkeys && ( + + )} + + {iconRight && ( + + {isValidElement(iconRight) && iconRight} + {isValidElementType(iconRight) && createElement(iconRight)} + + )} + + ) + }, [size, icon, text, hotkeys, iconRight, avatar, subText, badgeText]) + + return ( + + {/* Not recommended, should opt out with ts-ignore to use it. */} + {typeof children !== 'undefined' && children} + {typeof children === 'undefined' && typeof props.renderMenuItem === 'function' + ? props.renderMenuItem(menuItemContent) + : menuItemContent} + + ) +}) diff --git a/packages/sanity/src/ui/menuItem/index.ts b/packages/sanity/src/ui/menuItem/index.ts new file mode 100644 index 00000000000..7f0d25e4824 --- /dev/null +++ b/packages/sanity/src/ui/menuItem/index.ts @@ -0,0 +1 @@ +export * from './MenuItem'