diff --git a/packages/components/src/palette-edit/index.tsx b/packages/components/src/palette-edit/index.tsx index bd9818d5d8db2f..6f472a95b905b5 100644 --- a/packages/components/src/palette-edit/index.tsx +++ b/packages/components/src/palette-edit/index.tsx @@ -6,7 +6,13 @@ import { paramCase as kebabCase } from 'change-case'; /** * WordPress dependencies */ -import { useState, useRef, useEffect, useCallback } from '@wordpress/element'; +import { + useState, + useRef, + useEffect, + useCallback, + useMemo, +} from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { lineSolid, moreVertical, plus } from '@wordpress/icons'; import { @@ -106,13 +112,20 @@ function ColorPickerPopover< T extends Color | Gradient >( { isGradient, element, onChange, + popoverProps: receivedPopoverProps, onClose = () => {}, }: ColorPickerPopoverProps< T > ) { + const popoverProps: ColorPickerPopoverProps< T >[ 'popoverProps' ] = { + shift: true, + offset: 20, + placement: 'left-start', + ...receivedPopoverProps, + }; + return ( { ! isGradient && ( @@ -154,17 +167,31 @@ function Option< T extends Color | Gradient >( { onStartEditing, onRemove, onStopEditing, + popoverProps: receivedPopoverProps, slugPrefix, isGradient, }: OptionProps< T > ) { const focusOutsideProps = useFocusOutside( onStopEditing ); const value = isGradient ? element.gradient : element.color; + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + const popoverProps = useMemo( + () => ( { + ...receivedPopoverProps, + // Use the custom palette color item as the popover anchor. + anchor: popoverAnchor, + } ), + [ popoverAnchor, receivedPopoverProps ] + ); + return ( ( { isGradient={ isGradient } onChange={ onChange } element={ element } + popoverProps={ popoverProps } /> ) } @@ -244,6 +272,7 @@ function PaletteEditListView< T extends Color | Gradient >( { canOnlyChangeValues, slugPrefix, isGradient, + popoverProps, }: PaletteEditListViewProps< T > ) { // When unmounting the component if there are empty elements (the user did not complete the insertion) clean them. const elementsReference = useRef< typeof elements >(); @@ -317,6 +346,7 @@ function PaletteEditListView< T extends Color | Gradient >( { } } } slugPrefix={ slugPrefix } + popoverProps={ popoverProps } /> ) ) } @@ -356,6 +386,7 @@ export function PaletteEdit( { canOnlyChangeValues, canReset, slugPrefix = '', + popoverProps, }: PaletteEditProps ) { const isGradient = !! gradients; const elements = isGradient ? gradients : colors; @@ -541,6 +572,7 @@ export function PaletteEdit( { setEditingElement={ setEditingElement } slugPrefix={ slugPrefix } isGradient={ isGradient } + popoverProps={ popoverProps } /> ) } { ! isEditing && editingElement !== null && ( @@ -568,6 +600,7 @@ export function PaletteEdit( { ); } } element={ elements[ editingElement ?? -1 ] } + popoverProps={ popoverProps } /> ) } { ! isEditing && diff --git a/packages/components/src/palette-edit/types.ts b/packages/components/src/palette-edit/types.ts index 5513b945537784..56bff3a57dab6a 100644 --- a/packages/components/src/palette-edit/types.ts +++ b/packages/components/src/palette-edit/types.ts @@ -7,6 +7,7 @@ import type { Key, MouseEventHandler } from 'react'; * Internal dependencies */ import type { HeadingSize } from '../heading/types'; +import type { PopoverProps } from '../popover/types'; export type Color = { color: string; @@ -85,7 +86,9 @@ type PaletteEditGradients = { }; export type PaletteEditProps = BasePaletteEdit & - ( PaletteEditColors | PaletteEditGradients ); + ( PaletteEditColors | PaletteEditGradients ) & { + popoverProps?: Omit< PopoverProps, 'children' >; + }; type EditingElement = number | null; @@ -94,6 +97,7 @@ export type ColorPickerPopoverProps< T extends Color | Gradient > = { onChange: ( newElement: T ) => void; isGradient?: T extends Gradient ? true : false; onClose?: () => void; + popoverProps?: Omit< PopoverProps, 'children' >; }; export type NameInputProps = { @@ -112,6 +116,7 @@ export type OptionProps< T extends Color | Gradient > = { onRemove: MouseEventHandler< HTMLButtonElement >; onStartEditing: () => void; onStopEditing: () => void; + popoverProps?: Omit< PopoverProps, 'children' >; slugPrefix: string; }; @@ -121,6 +126,7 @@ export type PaletteEditListViewProps< T extends Color | Gradient > = { isGradient: T extends Gradient ? true : false; canOnlyChangeValues: PaletteEditProps[ 'canOnlyChangeValues' ]; editingElement?: EditingElement; + popoverProps?: Omit< PopoverProps, 'children' >; setEditingElement: ( newEditingElement?: EditingElement ) => void; slugPrefix: string; };