UNPKG

@wordpress/block-editor

Version:
223 lines (206 loc) 5.21 kB
/** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { __experimentalVStack as VStack, __experimentalHeading as Heading, __experimentalHStack as HStack, __experimentalDropdownContentWrapper as DropdownContentWrapper, Button, FlexItem, Dropdown, Composite, Tooltip, } from '@wordpress/components'; import { useMemo, useRef } from '@wordpress/element'; import { shadow as shadowIcon, Icon, check, reset } from '@wordpress/icons'; /** * External dependencies */ import clsx from 'clsx'; /** * Shared reference to an empty array for cases where it is important to avoid * returning a new array reference on every invocation. * * @type {Array} */ const EMPTY_ARRAY = []; export function ShadowPopoverContainer( { shadow, onShadowChange, settings } ) { const shadows = useShadowPresets( settings ); return ( <div className="block-editor-global-styles__shadow-popover-container"> <VStack spacing={ 4 }> <Heading level={ 5 }>{ __( 'Drop shadow' ) }</Heading> <ShadowPresets presets={ shadows } activeShadow={ shadow } onSelect={ onShadowChange } /> <div className="block-editor-global-styles__clear-shadow"> <Button __next40pxDefaultSize variant="tertiary" onClick={ () => onShadowChange( undefined ) } disabled={ ! shadow } accessibleWhenDisabled > { __( 'Clear' ) } </Button> </div> </VStack> </div> ); } export function ShadowPresets( { presets, activeShadow, onSelect } ) { return ! presets ? null : ( <Composite role="listbox" className="block-editor-global-styles__shadow__list" aria-label={ __( 'Drop shadows' ) } > { presets.map( ( { name, slug, shadow } ) => ( <ShadowIndicator key={ slug } label={ name } isActive={ shadow === activeShadow } type={ slug === 'unset' ? 'unset' : 'preset' } onSelect={ () => onSelect( shadow === activeShadow ? undefined : shadow ) } shadow={ shadow } /> ) ) } </Composite> ); } export function ShadowIndicator( { type, label, isActive, onSelect, shadow } ) { return ( <Tooltip text={ label }> <Composite.Item role="option" aria-label={ label } aria-selected={ isActive } className={ clsx( 'block-editor-global-styles__shadow__item', { 'is-active': isActive, } ) } render={ <button className={ clsx( 'block-editor-global-styles__shadow-indicator', { unset: type === 'unset', } ) } onClick={ onSelect } style={ { boxShadow: shadow } } aria-label={ label } > { isActive && <Icon icon={ check } /> } </button> } /> </Tooltip> ); } export function ShadowPopover( { shadow, onShadowChange, settings } ) { const popoverProps = { placement: 'left-start', offset: 36, shift: true, }; return ( <Dropdown popoverProps={ popoverProps } className="block-editor-global-styles__shadow-dropdown" renderToggle={ renderShadowToggle( shadow, onShadowChange ) } renderContent={ () => ( <DropdownContentWrapper paddingSize="medium"> <ShadowPopoverContainer shadow={ shadow } onShadowChange={ onShadowChange } settings={ settings } /> </DropdownContentWrapper> ) } /> ); } function renderShadowToggle( shadow, onShadowChange ) { return ( { onToggle, isOpen } ) => { const shadowButtonRef = useRef( undefined ); const toggleProps = { onClick: onToggle, className: clsx( 'block-editor-global-styles__shadow-dropdown-toggle', { 'is-open': isOpen } ), 'aria-expanded': isOpen, ref: shadowButtonRef, }; const removeButtonProps = { onClick: () => { if ( isOpen ) { onToggle(); } onShadowChange( undefined ); // Return focus to parent button. shadowButtonRef.current?.focus(); }, className: clsx( 'block-editor-global-styles__shadow-editor__remove-button', { 'is-open': isOpen } ), label: __( 'Remove' ), }; return ( <> <Button __next40pxDefaultSize { ...toggleProps }> <HStack justify="flex-start"> <Icon className="block-editor-global-styles__toggle-icon" icon={ shadowIcon } size={ 24 } /> <FlexItem>{ __( 'Drop shadow' ) }</FlexItem> </HStack> </Button> { !! shadow && ( <Button __next40pxDefaultSize size="small" icon={ reset } { ...removeButtonProps } /> ) } </> ); }; } export function useShadowPresets( settings ) { return useMemo( () => { if ( ! settings?.shadow ) { return EMPTY_ARRAY; } const defaultPresetsEnabled = settings?.shadow?.defaultPresets; const { default: defaultShadows, theme: themeShadows, custom: customShadows, } = settings?.shadow?.presets ?? {}; const unsetShadow = { name: __( 'Unset' ), slug: 'unset', shadow: 'none', }; const shadowPresets = [ ...( ( defaultPresetsEnabled && defaultShadows ) || EMPTY_ARRAY ), ...( themeShadows || EMPTY_ARRAY ), ...( customShadows || EMPTY_ARRAY ), ]; if ( shadowPresets.length ) { shadowPresets.unshift( unsetShadow ); } return shadowPresets; }, [ settings ] ); }