UNPKG

@wordpress/block-editor

Version:
660 lines (659 loc) 20.6 kB
// packages/block-editor/src/components/global-styles/color-panel.js import clsx from "clsx"; 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"; import { getValueFromVariable } from "@wordpress/global-styles-engine"; import ColorGradientControl from "../colors-gradients/control"; import { useColorsPerOrigin, useGradientsPerOrigin } from "./hooks"; import { useToolsPanelDropdownMenuProps } from "./utils"; import { setImmutably } from "../../utils/object"; import { unlock } from "../../lock-unlock"; import { reset as resetIcon } from "@wordpress/icons"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; 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; } function useHasTextPanel(settings) { const colors = useColorsPerOrigin(settings); return settings?.color?.text && (colors?.length > 0 || settings?.color?.custom); } function useHasLinkPanel(settings) { const colors = useColorsPerOrigin(settings); return settings?.color?.link && (colors?.length > 0 || settings?.color?.custom); } function useHasCaptionPanel(settings) { const colors = useColorsPerOrigin(settings); return settings?.color?.caption && (colors?.length > 0 || settings?.color?.custom); } 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); } 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); } 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, label }) { const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter(value); onChange(updatedValue); }; return /* @__PURE__ */ jsx( ToolsPanel, { label: label || __("Elements"), resetAll, panelId, hasInnerWrapper: true, headingLevel: 3, className: "color-block-support-panel", __experimentalFirstVisibleItemClass: "first", __experimentalLastVisibleItemClass: "last", dropdownMenuProps, children: /* @__PURE__ */ jsx("div", { className: "color-block-support-panel__inner-wrapper", children }) } ); } var DEFAULT_CONTROLS = { text: true, background: true, link: true, heading: true, button: true, caption: true }; var popoverProps = { placement: "left-start", offset: 36, shift: true, flip: true, resize: false }; var { Tabs } = unlock(componentsPrivateApis); var 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 ? void 0 : inheritedValue, gradientValue: isGradient ? inheritedValue : void 0, onColorChange: isGradient ? void 0 : setValue, onGradientChange: isGradient ? setValue : void 0, clearable: inheritedValue === userValue, headingLevel: 3 } ); } function ColorPanelDropdown({ label, hasValue, resetValue, isShownByDefault, indicators, tabs, colorGradientControlSettings, panelId }) { const currentTab = tabs.find((tab) => tab.userValue !== void 0); const { key: firstTabKey, ...firstTab } = tabs[0] ?? {}; const colorGradientDropdownButtonRef = useRef(void 0); return /* @__PURE__ */ jsx( ToolsPanelItem, { className: "block-editor-tools-panel-color-gradient-settings__item", hasValue, label, onDeselect: resetValue, isShownByDefault, panelId, children: /* @__PURE__ */ jsx( Dropdown, { 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, 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(); } 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 }, 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 }, tabKey ) }, tabKey ); }) ] }) ] }) }) } ) } ); } function ColorPanel({ as: Wrapper = ColorToolsPanel, value, onChange, inheritedValue = value, settings, panelId, defaultControls = DEFAULT_CONTROLS, label, 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: gradient2 }) => gradient2 === gradientValue ); return gradientObject ? "var:preset|gradient|" + gradientObject.slug : gradientValue; }; 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 = void 0; onChange(newValue); }; const setGradient = (newGradient) => { const newValue = setImmutably( value, ["color", "gradient"], encodeGradientValue(newGradient) ); newValue.color.background = void 0; onChange(newValue); }; const resetBackground = () => { const newValue = setImmutably( value, ["color", "background"], void 0 ); newValue.color.gradient = void 0; onChange(newValue); }; 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"], void 0 ); newValue = setImmutably( newValue, ["elements", "link", "color", "text"], void 0 ); onChange(newValue); }; 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(void 0); 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: void 0, elements: { ...previousValue?.elements, link: { ...previousValue?.elements?.link, color: void 0, ":hover": { color: void 0 } }, ...elements.reduce((acc, element) => { return { ...acc, [element.name]: { ...previousValue?.elements?.[element.name], color: void 0 } }; }, {}) } }; }, [elements] ); 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 ?? 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: elementLabel, 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"], void 0 ); newValue.elements[name].color.gradient = void 0; newValue.elements[name].color.text = void 0; 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 = void 0; onChange(newValue); }; const setElementGradient = (newGradient) => { const newValue = setImmutably( value, ["elements", name, "color", "gradient"], encodeGradientValue(newGradient) ); newValue.elements[name].color.background = void 0; onChange(newValue); }; const supportsTextColor = true; const supportsBackground = name !== "caption"; items.push({ key: name, label: elementLabel, hasValue: hasElement, resetValue: resetElement, isShownByDefault: defaultControls[name], indicators: supportsTextColor && supportsBackground ? [ elementTextColor, elementGradient ?? elementBackgroundColor ] : [ supportsTextColor ? elementTextColor : 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, value, onChange, panelId, label, children: [ items.map((item) => { const { key, ...restItem } = item; return /* @__PURE__ */ jsx( ColorPanelDropdown, { ...restItem, colorGradientControlSettings: { colors, disableCustomColors: !areCustomSolidsEnabled, gradients, disableCustomGradients: !areCustomGradientsEnabled }, panelId }, key ); }), children ] } ); } export { ColorToolsPanel, ColorPanel as default, useHasBackgroundColorPanel, useHasButtonPanel, useHasCaptionPanel, useHasColorPanel, useHasHeadingPanel, useHasLinkPanel, useHasTextPanel }; //# sourceMappingURL=color-panel.js.map