UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

206 lines (203 loc) • 7.75 kB
/** @jsx jsx */ import React from 'react'; import { css, jsx } from '@emotion/react'; import { withAnalyticsContext, withAnalyticsEvents } from '@atlaskit/analytics-next'; import Button from '@atlaskit/button'; import ExpandIcon from '@atlaskit/icon/glyph/chevron-down'; import { N0, N30A, N60A } from '@atlaskit/theme/colors'; import Tooltip from '@atlaskit/tooltip'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, editorAnalyticsChannel, EVENT_TYPE } from '../../analytics'; import { ColorPalette, DEFAULT_BORDER_COLOR, getSelectedRowAndColumnFromPalette } from '../../ui-color'; import { default as Popup } from '../../ui/Popup'; import { default as withOuterListeners } from '../../ui/with-outer-listeners'; import { ArrowKeyNavigationProvider } from '../ArrowKeyNavigationProvider'; import { ArrowKeyNavigationType } from '../ArrowKeyNavigationProvider/types'; // helps adjusts position of popup const colorPickerButtonWrapper = css({ position: 'relative' }); const colorPickerExpandContainer = css({ margin: `0px ${"var(--ds-space-negative-050, -4px)"}` }); // Control the size of color picker buttons and preview // TODO: https://product-fabric.atlassian.net/browse/DSP-4134 /* eslint-disable @atlaskit/design-system/ensure-design-token-usage */ const colorPickerWrapper = () => css({ borderRadius: "var(--ds-border-radius, 3px)", backgroundColor: `var(--ds-surface-overlay, ${N0})`, boxShadow: `var(--ds-shadow-overlay, ${`0 4px 8px -2px ${N60A}, 0 0 1px ${N60A}`})`, padding: `${"var(--ds-space-100, 8px)"} 0px` }); /* eslint-enable @atlaskit/design-system/ensure-design-token-usage */ const ColorPaletteWithListeners = withOuterListeners(ColorPalette); const ColorPickerButton = props => { const buttonRef = React.useRef(null); const [isPopupOpen, setIsPopupOpen] = React.useState(false); const [isPopupPositioned, setIsPopupPositioned] = React.useState(false); const [isOpenedByKeyboard, setIsOpenedByKeyboard] = React.useState(false); const togglePopup = () => { setIsPopupOpen(!isPopupOpen); if (!isPopupOpen) { setIsOpenedByKeyboard(false); setIsPopupPositioned(false); } }; React.useEffect(() => { if (props.setDisableParentScroll) { props.setDisableParentScroll(isPopupOpen); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isPopupOpen]); const focusButton = () => { var _buttonRef$current; (_buttonRef$current = buttonRef.current) === null || _buttonRef$current === void 0 ? void 0 : _buttonRef$current.focus(); }; const handleEsc = React.useCallback(() => { setIsOpenedByKeyboard(false); setIsPopupPositioned(false); setIsPopupOpen(false); focusButton(); }, []); const onPositionCalculated = React.useCallback(position => { setIsPopupPositioned(true); return position; }, []); const { onChange, createAnalyticsEvent, colorPalette, placement, skipFocusButtonAfterPick } = props; const onColorSelected = React.useCallback((color, label) => { setIsOpenedByKeyboard(false); setIsPopupOpen(false); setIsPopupPositioned(false); if (onChange) { if (createAnalyticsEvent) { // fire analytics const payload = { action: ACTION.UPDATED, actionSubject: ACTION_SUBJECT.PICKER, actionSubjectId: ACTION_SUBJECT_ID.PICKER_COLOR, attributes: { color, label, placement: placement }, eventType: EVENT_TYPE.TRACK }; createAnalyticsEvent(payload).fire(editorAnalyticsChannel); } const newPalette = colorPalette.find(colorPalette => colorPalette.value === color); newPalette && onChange(newPalette); } if (!skipFocusButtonAfterPick) { focusButton(); } }, [colorPalette, onChange, createAnalyticsEvent, placement, skipFocusButtonAfterPick]); const renderPopup = () => { if (!isPopupOpen || !buttonRef.current) { return; } const selectedColor = props.currentColor || null; const { selectedRowIndex, selectedColumnIndex } = getSelectedRowAndColumnFromPalette(props.colorPalette, selectedColor, props.cols); return jsx(Popup, { target: buttonRef.current, fitHeight: 350, fitWidth: 350, offset: [0, 10], alignX: props.alignX, mountTo: props.setDisableParentScroll ? props.mountPoint : undefined, absoluteOffset: props.absoluteOffset // Confluence inline comment editor has z-index: 500 // if the toolbar is scrollable, this will be mounted in the root editor // we need an index of > 500 to display over it , zIndex: props.setDisableParentScroll ? 600 : undefined, ariaLabel: "Color picker popup", onPositionCalculated: onPositionCalculated }, jsx("div", { css: colorPickerWrapper, "data-test-id": "color-picker-menu" }, jsx(ArrowKeyNavigationProvider, { type: ArrowKeyNavigationType.COLOR, selectedRowIndex: selectedRowIndex, selectedColumnIndex: selectedColumnIndex, closeOnTab: true, handleClose: () => setIsPopupOpen(false), isOpenedByKeyboard: isOpenedByKeyboard, isPopupPositioned: isPopupPositioned }, jsx(ColorPaletteWithListeners, { cols: props.cols, selectedColor: selectedColor, onClick: onColorSelected, handleClickOutside: togglePopup, handleEscapeKeydown: handleEsc, paletteOptions: { palette: props.colorPalette, hexToPaletteColor: props.hexToPaletteColor, paletteColorTooltipMessages: props.paletteColorTooltipMessages } })))); }; const title = props.title || ''; const currentColor = props.currentColor && props.hexToPaletteColor ? props.hexToPaletteColor(props.currentColor) : props.currentColor; const buttonStyle = () => { var _props$size, _props$size2, _props$size3; return css({ padding: `${"var(--ds-space-075, 6px)"} 10px`, backgroundColor: "var(--ds-background-neutral-subtle, transparent)", height: `${!!((_props$size = props.size) !== null && _props$size !== void 0 && _props$size.height) ? 'inherit' : ''}`, '&:before': { display: 'flex', justifyContent: 'center', alignItems: 'center', alignSelf: 'center', content: "''", border: `1px solid ${DEFAULT_BORDER_COLOR}`, borderRadius: "var(--ds-border-radius, 3px)", backgroundColor: currentColor || 'transparent', width: ((_props$size2 = props.size) === null || _props$size2 === void 0 ? void 0 : _props$size2.width) || '14px', height: ((_props$size3 = props.size) === null || _props$size3 === void 0 ? void 0 : _props$size3.height) || '14px', padding: 0, margin: `0px ${"var(--ds-space-025, 2px)"}` }, '&:hover': { background: `var(--ds-background-neutral-subtle-hovered, ${N30A})` } }); }; return jsx("div", { css: colorPickerButtonWrapper }, jsx(Tooltip, { content: title, position: "top" }, jsx(Button, { ref: buttonRef, "aria-label": title, "aria-expanded": props.isAriaExpanded ? isPopupOpen : undefined, spacing: "compact", onClick: togglePopup, onKeyDown: event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); togglePopup(); setIsOpenedByKeyboard(true); } }, css: buttonStyle, iconAfter: jsx("span", { css: colorPickerExpandContainer }, jsx(ExpandIcon, { label: "" })), "data-selected-color": props.currentColor })), renderPopup()); }; export default withAnalyticsContext({ source: 'ConfigPanel' })(withAnalyticsEvents()(ColorPickerButton));