UNPKG

@atlaskit/renderer

Version:
296 lines (293 loc) • 11 kB
import _extends from "@babel/runtime/helpers/extends"; import React from 'react'; import { tableBackgroundColorPalette, getDarkModeLCHColor } from '@atlaskit/adf-schema'; import { useThemeObserver } from '@atlaskit/tokens'; import { SortOrder } from '@atlaskit/editor-common/types'; import { hexToEditorBackgroundPaletteRawValue } from '@atlaskit/editor-palette'; import { SortingIcon } from '@atlaskit/editor-common/table'; import { MODE, PLATFORM } from '../../analytics/events'; import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { RendererCssClassName } from '../../consts'; import { useIntl } from 'react-intl'; import { tableCellMessages } from '../../messages'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure'; const IgnoreSorting = ['LABEL', 'INPUT']; const nextStatusOrder = currentSortOrder => { switch (currentSortOrder) { case SortOrder.NO_ORDER: return SortOrder.ASC; case SortOrder.ASC: return SortOrder.DESC; case SortOrder.DESC: return SortOrder.NO_ORDER; } return SortOrder.NO_ORDER; }; const getSortOrderLabel = (intl, currentSortOrder) => { const { noneSortingLabel, ascSortingLabel, descSortingLabel } = tableCellMessages; switch (currentSortOrder) { case SortOrder.NO_ORDER: return intl.formatMessage(noneSortingLabel); case SortOrder.ASC: return intl.formatMessage(ascSortingLabel); case SortOrder.DESC: return intl.formatMessage(descSortingLabel); default: return intl.formatMessage(noneSortingLabel); } }; const getDataAttributes = (colwidth, background, cellEdgeProps, valign) => { const attrs = {}; if (colwidth) { attrs['data-colwidth'] = colwidth.join(','); } if (expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true)) { if (cellEdgeProps !== null && cellEdgeProps !== void 0 && cellEdgeProps.reachesTop) { attrs['data-reaches-top'] = true; } if (cellEdgeProps !== null && cellEdgeProps !== void 0 && cellEdgeProps.reachesBottom) { attrs['data-reaches-bottom'] = true; } if (cellEdgeProps !== null && cellEdgeProps !== void 0 && cellEdgeProps.reachesLeft) { attrs['data-reaches-left'] = true; } if (cellEdgeProps !== null && cellEdgeProps !== void 0 && cellEdgeProps.reachesRight) { attrs['data-reaches-right'] = true; } } /** * Storing hex code in data-cell-background because * we want to have DST token (css variable) or * DST token value (value (hex code) of css variable) in * inline style to correct render table cell background * based on selected theme. * Currently we rely on background color hex code stored in * inline style. * Because of that when we copy and paste table, we end up * having DST token or DST token value in ADF instead of * original hex code which we map to DST token. * So, introducing data-cell-background. * More details at https://product-fabric.atlassian.net/wiki/spaces/EUXQ/pages/3472556903/Tokenising+tableCell+background+colors#Update-toDom-and-parseDom-to-store-and-read-background-color-from-data-cell-background-attribute.4 */ if (background) { attrs['data-cell-background'] = background; } if (valign && expValEqualsNoExposure('platform_editor_table_menu_updates', 'isEnabled', true)) { attrs['data-valign'] = valign; } return attrs; }; // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp const cssVariablePattern = /^var\(--.*\)$/; const getStyle = ({ background, colGroupWidth, offsetTop, colorMode, valign }) => { const style = {}; if (background && // ignore setting inline styles if ds neutral token is detected !background.includes('--ds-background-neutral')) { /** * The Editor supports users pasting content from external sources with custom table cell backgrounds and having those * backgrounds persisted. * * This feature predates the introduction of dark mode. * * Without the inversion logic below, tokenised content (ie. text), can be hard to read when the user loads the page in dark mode. * * This introduces inversion of the presentation of the custom background color when the user is in dark mode. * * This can be done without additional changes to account for users copying and pasting content inside the Editor, because of * how we detect table cell background colors copied from external editor sources. Where we load the background color from a * seperate attribute (data-cell-background), instead of the inline style. * * See the following document for more details on this behaviour * https://hello.atlassian.net/wiki/spaces/CCECO/pages/2892247168/Unsupported+custom+table+cell+background+colors+in+dark+theme+Editor+Job+Story */ const tokenColor = hexToEditorBackgroundPaletteRawValue(background); if (tokenColor) { style.backgroundColor = tokenColor; } else if ( /** * There was previously a bug in dark mode where we would attempt to invert * a design token in `getDarkModeLCHColor` causing issues. * If it's a design token we should return it as is. */ cssVariablePattern.test(background)) { style.backgroundColor = background; } else { // if we have a custom color, we need to check if we are in dark mode if (colorMode === 'dark') { // if we are in dark mode, we need to invert the color style.backgroundColor = getDarkModeLCHColor(background); } else { // if we are in light mode, we can just set the color style.backgroundColor = background; } } } if (colGroupWidth) { style.width = colGroupWidth; style.minWidth = colGroupWidth; } if (offsetTop !== undefined) { style.top = offsetTop; } if (valign && expValEqualsNoExposure('platform_editor_table_menu_updates', 'isEnabled', true)) { style.verticalAlign = valign; } return style; }; const getWithCellProps = WrapperComponent => { return function WithCellProps(props) { const { colorMode } = useThemeObserver(); const { children, className, onClick, colwidth, colGroupWidth, rowspan, colspan, background, offsetTop, ariaSort, reachesTop, reachesBottom, reachesLeft, reachesRight, valign } = props; // This is used to set the background color of the cell // to a dark mode color in mobile dark mode const colorName = background ? tableBackgroundColorPalette.get(background) : ''; return /*#__PURE__*/React.createElement(WrapperComponent, _extends({ rowSpan: rowspan, colSpan: colspan // Note: When content from a renderer is pasted into an editor // the background color is not taken from the inline style. // Instead it is taken from the data-cell-background attribute // (added via getDataAttributes below). // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 , style: getStyle({ background, colGroupWidth, offsetTop, colorMode, valign }), colorname: colorName, onClick: onClick // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: className // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, getDataAttributes(colwidth, background, { reachesTop, reachesBottom, reachesLeft, reachesRight }, valign), { "aria-sort": ariaSort }), children); }; }; const TH = getWithCellProps('th'); export const withSortableColumn = WrapperComponent => { return function THWithSortableColumn(props) { const intl = useIntl(); const { allowColumnSorting, onSorting, children, sortOrdered, isHeaderRow } = props; const sortOrderedClassName = sortOrdered === SortOrder.NO_ORDER ? RendererCssClassName.SORTABLE_COLUMN_NO_ORDER : ''; if (!allowColumnSorting || !isHeaderRow) { // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading return /*#__PURE__*/React.createElement(WrapperComponent, props); } return /*#__PURE__*/React.createElement(WrapperComponent // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , _extends({}, props, { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: RendererCssClassName.SORTABLE_COLUMN_WRAPPER, ariaSort: getSortOrderLabel(intl, sortOrdered) }), /*#__PURE__*/React.createElement("div", { className: RendererCssClassName.SORTABLE_COLUMN }, children, /*#__PURE__*/React.createElement("figure", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: `${RendererCssClassName.SORTABLE_COLUMN_ICON_WRAPPER} ${sortOrderedClassName}` }, /*#__PURE__*/React.createElement(SortingIcon, { isSortingAllowed: !!onSorting, sortOrdered: sortOrdered, onClick: sort, onKeyDown: onKeyPress })))); function onKeyPress(event) { const keys = [' ', 'Enter', 'Spacebar']; // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting const { tagName } = event.target; // trigger sorting if space or enter are clicked but not when in an input field (template variables) if (keys.includes(event.key) && !IgnoreSorting.includes(tagName)) { event.preventDefault(); sort(); } } function sort() { const { fireAnalyticsEvent, onSorting, columnIndex, sortOrdered } = props; if (onSorting && columnIndex != null) { const sortOrder = nextStatusOrder(sortOrdered); onSorting(columnIndex, sortOrder); fireAnalyticsEvent && fireAnalyticsEvent({ action: ACTION.SORT_COLUMN, actionSubject: ACTION_SUBJECT.TABLE, attributes: { platform: PLATFORM.WEB, mode: MODE.RENDERER, columnIndex, sortOrder }, eventType: EVENT_TYPE.TRACK }); } else { fireAnalyticsEvent && fireAnalyticsEvent({ action: ACTION.SORT_COLUMN_NOT_ALLOWED, actionSubject: ACTION_SUBJECT.TABLE, attributes: { platform: PLATFORM.WEB, mode: MODE.RENDERER }, eventType: EVENT_TYPE.TRACK }); } } }; }; export const TableHeader = withSortableColumn(TH); const TD = getWithCellProps('td'); export const TableCell = TD;