UNPKG

@wordpress/block-editor

Version:
543 lines (537 loc) 19.3 kB
/** * External dependencies */ import clsx from 'clsx'; /** * WordPress dependencies */ import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalHStack as HStack, __experimentalZStack as ZStack, __experimentalDropdownContentWrapper as DropdownContentWrapper, ColorIndicator, Flex, FlexItem, Dropdown, Button, privateApis as componentsPrivateApis } from '@wordpress/components'; import { useCallback, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import ColorGradientControl from '../colors-gradients/control'; import { useColorsPerOrigin, useGradientsPerOrigin } from './hooks'; import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; import { unlock } from '../../lock-unlock'; import { reset as resetIcon } from '@wordpress/icons'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; export function useHasColorPanel(settings) { const hasTextPanel = useHasTextPanel(settings); const hasBackgroundPanel = useHasBackgroundColorPanel(settings); const hasLinkPanel = useHasLinkPanel(settings); const hasHeadingPanel = useHasHeadingPanel(settings); const hasButtonPanel = useHasButtonPanel(settings); const hasCaptionPanel = useHasCaptionPanel(settings); return hasTextPanel || hasBackgroundPanel || hasLinkPanel || hasHeadingPanel || hasButtonPanel || hasCaptionPanel; } export function useHasTextPanel(settings) { const colors = useColorsPerOrigin(settings); return settings?.color?.text && (colors?.length > 0 || settings?.color?.custom); } export function useHasLinkPanel(settings) { const colors = useColorsPerOrigin(settings); return settings?.color?.link && (colors?.length > 0 || settings?.color?.custom); } export function useHasCaptionPanel(settings) { const colors = useColorsPerOrigin(settings); return settings?.color?.caption && (colors?.length > 0 || settings?.color?.custom); } export function useHasHeadingPanel(settings) { const colors = useColorsPerOrigin(settings); const gradients = useGradientsPerOrigin(settings); return settings?.color?.heading && (colors?.length > 0 || settings?.color?.custom || gradients?.length > 0 || settings?.color?.customGradient); } export function useHasButtonPanel(settings) { const colors = useColorsPerOrigin(settings); const gradients = useGradientsPerOrigin(settings); return settings?.color?.button && (colors?.length > 0 || settings?.color?.custom || gradients?.length > 0 || settings?.color?.customGradient); } export function useHasBackgroundColorPanel(settings) { const colors = useColorsPerOrigin(settings); const gradients = useGradientsPerOrigin(settings); return settings?.color?.background && (colors?.length > 0 || settings?.color?.custom || gradients?.length > 0 || settings?.color?.customGradient); } function ColorToolsPanel({ resetAllFilter, onChange, value, panelId, children }) { const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter(value); onChange(updatedValue); }; return /*#__PURE__*/_jsx(ToolsPanel, { label: __('Elements'), resetAll: resetAll, panelId: panelId, hasInnerWrapper: true, headingLevel: 3, className: "color-block-support-panel", __experimentalFirstVisibleItemClass: "first", __experimentalLastVisibleItemClass: "last", dropdownMenuProps: dropdownMenuProps, children: /*#__PURE__*/_jsx("div", { className: "color-block-support-panel__inner-wrapper", children: children }) }); } const DEFAULT_CONTROLS = { text: true, background: true, link: true, heading: true, button: true, caption: true }; const popoverProps = { placement: 'left-start', offset: 36, shift: true }; const { Tabs } = unlock(componentsPrivateApis); const LabeledColorIndicators = ({ indicators, label }) => /*#__PURE__*/_jsxs(HStack, { justify: "flex-start", children: [/*#__PURE__*/_jsx(ZStack, { isLayered: false, offset: -8, children: indicators.map((indicator, index) => /*#__PURE__*/_jsx(Flex, { expanded: false, children: /*#__PURE__*/_jsx(ColorIndicator, { colorValue: indicator }) }, index)) }), /*#__PURE__*/_jsx(FlexItem, { className: "block-editor-panel-color-gradient-settings__color-name", children: label })] }); function ColorPanelTab({ isGradient, inheritedValue, userValue, setValue, colorGradientControlSettings }) { return /*#__PURE__*/_jsx(ColorGradientControl, { ...colorGradientControlSettings, showTitle: false, enableAlpha: true, __experimentalIsRenderedInSidebar: true, colorValue: isGradient ? undefined : inheritedValue, gradientValue: isGradient ? inheritedValue : undefined, onColorChange: isGradient ? undefined : setValue, onGradientChange: isGradient ? setValue : undefined, clearable: inheritedValue === userValue, headingLevel: 3 }); } function ColorPanelDropdown({ label, hasValue, resetValue, isShownByDefault, indicators, tabs, colorGradientControlSettings, panelId }) { var _tabs$; const currentTab = tabs.find(tab => tab.userValue !== undefined); const { key: firstTabKey, ...firstTab } = (_tabs$ = tabs[0]) !== null && _tabs$ !== void 0 ? _tabs$ : {}; const colorGradientDropdownButtonRef = useRef(undefined); return /*#__PURE__*/_jsx(ToolsPanelItem, { className: "block-editor-tools-panel-color-gradient-settings__item", hasValue: hasValue, label: label, onDeselect: resetValue, isShownByDefault: isShownByDefault, panelId: panelId, children: /*#__PURE__*/_jsx(Dropdown, { popoverProps: popoverProps, className: "block-editor-tools-panel-color-gradient-settings__dropdown", renderToggle: ({ onToggle, isOpen }) => { const toggleProps = { onClick: onToggle, className: clsx('block-editor-panel-color-gradient-settings__dropdown', { 'is-open': isOpen }), 'aria-expanded': isOpen, ref: colorGradientDropdownButtonRef }; return /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsx(Button, { ...toggleProps, __next40pxDefaultSize: true, children: /*#__PURE__*/_jsx(LabeledColorIndicators, { indicators: indicators, label: label }) }), hasValue() && /*#__PURE__*/_jsx(Button, { __next40pxDefaultSize: true, label: __('Reset'), className: "block-editor-panel-color-gradient-settings__reset", size: "small", icon: resetIcon, onClick: () => { resetValue(); if (isOpen) { onToggle(); } // Return focus to parent button colorGradientDropdownButtonRef.current?.focus(); } })] }); }, renderContent: () => /*#__PURE__*/_jsx(DropdownContentWrapper, { paddingSize: "none", children: /*#__PURE__*/_jsxs("div", { className: "block-editor-panel-color-gradient-settings__dropdown-content", children: [tabs.length === 1 && /*#__PURE__*/_jsx(ColorPanelTab, { ...firstTab, colorGradientControlSettings: colorGradientControlSettings }, firstTabKey), tabs.length > 1 && /*#__PURE__*/_jsxs(Tabs, { defaultTabId: currentTab?.key, children: [/*#__PURE__*/_jsx(Tabs.TabList, { children: tabs.map(tab => /*#__PURE__*/_jsx(Tabs.Tab, { tabId: tab.key, children: tab.label }, tab.key)) }), tabs.map(tab => { const { key: tabKey, ...restTabProps } = tab; return /*#__PURE__*/_jsx(Tabs.TabPanel, { tabId: tabKey, focusable: false, children: /*#__PURE__*/_jsx(ColorPanelTab, { ...restTabProps, colorGradientControlSettings: colorGradientControlSettings }, tabKey) }, tabKey); })] })] }) }) }) }); } export default function ColorPanel({ as: Wrapper = ColorToolsPanel, value, onChange, inheritedValue = value, settings, panelId, defaultControls = DEFAULT_CONTROLS, children }) { const colors = useColorsPerOrigin(settings); const gradients = useGradientsPerOrigin(settings); const areCustomSolidsEnabled = settings?.color?.custom; const areCustomGradientsEnabled = settings?.color?.customGradient; const hasSolidColors = colors.length > 0 || areCustomSolidsEnabled; const hasGradientColors = gradients.length > 0 || areCustomGradientsEnabled; const decodeValue = rawValue => getValueFromVariable({ settings }, '', rawValue); const encodeColorValue = colorValue => { const allColors = colors.flatMap(({ colors: originColors }) => originColors); const colorObject = allColors.find(({ color }) => color === colorValue); return colorObject ? 'var:preset|color|' + colorObject.slug : colorValue; }; const encodeGradientValue = gradientValue => { const allGradients = gradients.flatMap(({ gradients: originGradients }) => originGradients); const gradientObject = allGradients.find(({ gradient }) => gradient === gradientValue); return gradientObject ? 'var:preset|gradient|' + gradientObject.slug : gradientValue; }; // BackgroundColor const showBackgroundPanel = useHasBackgroundColorPanel(settings); const backgroundColor = decodeValue(inheritedValue?.color?.background); const userBackgroundColor = decodeValue(value?.color?.background); const gradient = decodeValue(inheritedValue?.color?.gradient); const userGradient = decodeValue(value?.color?.gradient); const hasBackground = () => !!userBackgroundColor || !!userGradient; const setBackgroundColor = newColor => { const newValue = setImmutably(value, ['color', 'background'], encodeColorValue(newColor)); newValue.color.gradient = undefined; onChange(newValue); }; const setGradient = newGradient => { const newValue = setImmutably(value, ['color', 'gradient'], encodeGradientValue(newGradient)); newValue.color.background = undefined; onChange(newValue); }; const resetBackground = () => { const newValue = setImmutably(value, ['color', 'background'], undefined); newValue.color.gradient = undefined; onChange(newValue); }; // Links const showLinkPanel = useHasLinkPanel(settings); const linkColor = decodeValue(inheritedValue?.elements?.link?.color?.text); const userLinkColor = decodeValue(value?.elements?.link?.color?.text); const setLinkColor = newColor => { onChange(setImmutably(value, ['elements', 'link', 'color', 'text'], encodeColorValue(newColor))); }; const hoverLinkColor = decodeValue(inheritedValue?.elements?.link?.[':hover']?.color?.text); const userHoverLinkColor = decodeValue(value?.elements?.link?.[':hover']?.color?.text); const setHoverLinkColor = newColor => { onChange(setImmutably(value, ['elements', 'link', ':hover', 'color', 'text'], encodeColorValue(newColor))); }; const hasLink = () => !!userLinkColor || !!userHoverLinkColor; const resetLink = () => { let newValue = setImmutably(value, ['elements', 'link', ':hover', 'color', 'text'], undefined); newValue = setImmutably(newValue, ['elements', 'link', 'color', 'text'], undefined); onChange(newValue); }; // Text Color const showTextPanel = useHasTextPanel(settings); const textColor = decodeValue(inheritedValue?.color?.text); const userTextColor = decodeValue(value?.color?.text); const hasTextColor = () => !!userTextColor; const setTextColor = newColor => { let changedObject = setImmutably(value, ['color', 'text'], encodeColorValue(newColor)); if (textColor === linkColor) { changedObject = setImmutably(changedObject, ['elements', 'link', 'color', 'text'], encodeColorValue(newColor)); } onChange(changedObject); }; const resetTextColor = () => setTextColor(undefined); // Elements const elements = [{ name: 'caption', label: __('Captions'), showPanel: useHasCaptionPanel(settings) }, { name: 'button', label: __('Button'), showPanel: useHasButtonPanel(settings) }, { name: 'heading', label: __('Heading'), showPanel: useHasHeadingPanel(settings) }, { name: 'h1', label: __('H1'), showPanel: useHasHeadingPanel(settings) }, { name: 'h2', label: __('H2'), showPanel: useHasHeadingPanel(settings) }, { name: 'h3', label: __('H3'), showPanel: useHasHeadingPanel(settings) }, { name: 'h4', label: __('H4'), showPanel: useHasHeadingPanel(settings) }, { name: 'h5', label: __('H5'), showPanel: useHasHeadingPanel(settings) }, { name: 'h6', label: __('H6'), showPanel: useHasHeadingPanel(settings) }]; const resetAllFilter = useCallback(previousValue => { return { ...previousValue, color: undefined, elements: { ...previousValue?.elements, link: { ...previousValue?.elements?.link, color: undefined, ':hover': { color: undefined } }, ...elements.reduce((acc, element) => { return { ...acc, [element.name]: { ...previousValue?.elements?.[element.name], color: undefined } }; }, {}) } }; }, []); const items = [showTextPanel && { key: 'text', label: __('Text'), hasValue: hasTextColor, resetValue: resetTextColor, isShownByDefault: defaultControls.text, indicators: [textColor], tabs: [{ key: 'text', label: __('Text'), inheritedValue: textColor, setValue: setTextColor, userValue: userTextColor }] }, showBackgroundPanel && { key: 'background', label: __('Background'), hasValue: hasBackground, resetValue: resetBackground, isShownByDefault: defaultControls.background, indicators: [gradient !== null && gradient !== void 0 ? gradient : backgroundColor], tabs: [hasSolidColors && { key: 'background', label: __('Color'), inheritedValue: backgroundColor, setValue: setBackgroundColor, userValue: userBackgroundColor }, hasGradientColors && { key: 'gradient', label: __('Gradient'), inheritedValue: gradient, setValue: setGradient, userValue: userGradient, isGradient: true }].filter(Boolean) }, showLinkPanel && { key: 'link', label: __('Link'), hasValue: hasLink, resetValue: resetLink, isShownByDefault: defaultControls.link, indicators: [linkColor, hoverLinkColor], tabs: [{ key: 'link', label: __('Default'), inheritedValue: linkColor, setValue: setLinkColor, userValue: userLinkColor }, { key: 'hover', label: __('Hover'), inheritedValue: hoverLinkColor, setValue: setHoverLinkColor, userValue: userHoverLinkColor }] }].filter(Boolean); elements.forEach(({ name, label, showPanel }) => { if (!showPanel) { return; } const elementBackgroundColor = decodeValue(inheritedValue?.elements?.[name]?.color?.background); const elementGradient = decodeValue(inheritedValue?.elements?.[name]?.color?.gradient); const elementTextColor = decodeValue(inheritedValue?.elements?.[name]?.color?.text); const elementBackgroundUserColor = decodeValue(value?.elements?.[name]?.color?.background); const elementGradientUserColor = decodeValue(value?.elements?.[name]?.color?.gradient); const elementTextUserColor = decodeValue(value?.elements?.[name]?.color?.text); const hasElement = () => !!(elementTextUserColor || elementBackgroundUserColor || elementGradientUserColor); const resetElement = () => { const newValue = setImmutably(value, ['elements', name, 'color', 'background'], undefined); newValue.elements[name].color.gradient = undefined; newValue.elements[name].color.text = undefined; onChange(newValue); }; const setElementTextColor = newTextColor => { onChange(setImmutably(value, ['elements', name, 'color', 'text'], encodeColorValue(newTextColor))); }; const setElementBackgroundColor = newBackgroundColor => { const newValue = setImmutably(value, ['elements', name, 'color', 'background'], encodeColorValue(newBackgroundColor)); newValue.elements[name].color.gradient = undefined; onChange(newValue); }; const setElementGradient = newGradient => { const newValue = setImmutably(value, ['elements', name, 'color', 'gradient'], encodeGradientValue(newGradient)); newValue.elements[name].color.background = undefined; onChange(newValue); }; const supportsTextColor = true; // Background color is not supported for `caption` // as there isn't yet a way to set padding for the element. const supportsBackground = name !== 'caption'; items.push({ key: name, label, hasValue: hasElement, resetValue: resetElement, isShownByDefault: defaultControls[name], indicators: supportsTextColor && supportsBackground ? [elementTextColor, elementGradient !== null && elementGradient !== void 0 ? elementGradient : elementBackgroundColor] : [supportsTextColor ? elementTextColor : elementGradient !== null && elementGradient !== void 0 ? elementGradient : elementBackgroundColor], tabs: [hasSolidColors && supportsTextColor && { key: 'text', label: __('Text'), inheritedValue: elementTextColor, setValue: setElementTextColor, userValue: elementTextUserColor }, hasSolidColors && supportsBackground && { key: 'background', label: __('Background'), inheritedValue: elementBackgroundColor, setValue: setElementBackgroundColor, userValue: elementBackgroundUserColor }, hasGradientColors && supportsBackground && { key: 'gradient', label: __('Gradient'), inheritedValue: elementGradient, setValue: setElementGradient, userValue: elementGradientUserColor, isGradient: true }].filter(Boolean) }); }); return /*#__PURE__*/_jsxs(Wrapper, { resetAllFilter: resetAllFilter, value: value, onChange: onChange, panelId: panelId, children: [items.map(item => { const { key, ...restItem } = item; return /*#__PURE__*/_jsx(ColorPanelDropdown, { ...restItem, colorGradientControlSettings: { colors, disableCustomColors: !areCustomSolidsEnabled, gradients, disableCustomGradients: !areCustomGradientsEnabled }, panelId: panelId }, key); }), children] }); } //# sourceMappingURL=color-panel.js.map