@atlaskit/editor-plugin-text-color
Version:
Text color plugin for @atlaskit/editor-core
368 lines (360 loc) • 19.4 kB
JavaScript
/* ColorAccessibilityMenuItem.tsx generated by @compiled/babel-plugin v2.0.0 */
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getResolvedBackgroundColorForTextNode = exports.getAccessibilityStatusForCurrentSelection = exports.ColorAccessibilityMenuItem = void 0;
require("./ColorAccessibilityMenuItem.compiled.css");
var _runtime = require("@compiled/react/runtime");
var _react = _interopRequireDefault(require("react"));
var _reactIntl = require("react-intl");
var _panel = require("@atlaskit/adf-schema/panel");
var _button = _interopRequireDefault(require("@atlaskit/button/icon/button"));
var _css = require("@atlaskit/css");
var _hooks = require("@atlaskit/editor-common/hooks");
var _messages = require("@atlaskit/editor-common/messages");
var _panel2 = require("@atlaskit/editor-common/panel");
var _toolbar = require("@atlaskit/editor-common/toolbar");
var _uiColor = require("@atlaskit/editor-common/ui-color");
var _editorPalette = require("@atlaskit/editor-palette");
var _state = require("@atlaskit/editor-prosemirror/state");
var _accessibility = _interopRequireDefault(require("@atlaskit/icon/core/accessibility"));
var _questionCircle = _interopRequireDefault(require("@atlaskit/icon/core/question-circle"));
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _compiled = require("@atlaskit/primitives/compiled");
var _getTokenValue = require("@atlaskit/tokens/get-token-value");
var _colorContrast = require("../pm-plugins/utils/color-contrast");
var _constants = require("../pm-plugins/utils/constants");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
var resolveColorValue = function resolveColorValue(color, fallback) {
return (0, _uiColor.getTokenCSSVariableValue)(color) || (color.startsWith('var(') ? fallback : color);
};
var getForegroundColor = function getForegroundColor(textColor, defaultColor) {
if (!textColor || defaultColor && textColor === defaultColor) {
return (0, _getTokenValue.getTokenValue)('color.text', defaultColor || _constants.DEFAULT_COLOR.color);
}
var colorValue = (0, _editorPalette.hexToEditorTextPaletteColor)(textColor) || textColor;
return resolveColorValue(colorValue, textColor);
};
var getBackgroundColor = function getBackgroundColor(backgroundColor) {
var palette = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'text';
if (!backgroundColor || backgroundColor === 'none') {
return (0, _getTokenValue.getTokenValue)('elevation.surface', _constants.DEFAULT_BACKGROUND_COLOR);
}
var colorValue = palette === 'element' ? (0, _editorPalette.hexToEditorBackgroundPaletteColor)(backgroundColor) || backgroundColor : (0, _editorPalette.hexToEditorTextBackgroundPaletteColor)(backgroundColor) || backgroundColor;
return resolveColorValue(colorValue, backgroundColor);
};
var getBackgroundColorInNonActiveTheme = function getBackgroundColorInNonActiveTheme(backgroundColor) {
var palette = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'text';
if (!backgroundColor || backgroundColor === 'none') {
return (0, _uiColor.getTokenCSSVariableValueForNonActiveTheme)("var(--ds-surface, #FFFFFF)", _constants.DEFAULT_BACKGROUND_COLOR);
}
var colorValue = palette === 'element' ? (0, _editorPalette.hexToEditorBackgroundPaletteColor)(backgroundColor) || backgroundColor : (0, _editorPalette.hexToEditorTextBackgroundPaletteColor)(backgroundColor) || backgroundColor;
return (0, _uiColor.getTokenCSSVariableValueForNonActiveTheme)(colorValue, backgroundColor);
};
var STANDARD_PANEL_BACKGROUND_COLORS = new Map([[_panel.PanelType.INFO, (0, _panel2.getPanelTypeBackgroundNoTokens)(_panel.PanelType.INFO)], [_panel.PanelType.NOTE, (0, _panel2.getPanelTypeBackgroundNoTokens)(_panel.PanelType.NOTE)], [_panel.PanelType.SUCCESS, (0, _panel2.getPanelTypeBackgroundNoTokens)(_panel.PanelType.SUCCESS)], [_panel.PanelType.WARNING, (0, _panel2.getPanelTypeBackgroundNoTokens)(_panel.PanelType.WARNING)], [_panel.PanelType.ERROR, (0, _panel2.getPanelTypeBackgroundNoTokens)(_panel.PanelType.ERROR)]]);
var getResolvedBackgroundFromHighlight = function getResolvedBackgroundFromHighlight(highlightColor) {
if (!highlightColor) {
return {
color: null,
source: 'text'
};
}
return {
color: highlightColor,
source: 'text'
};
};
/**
* Resolves a background color from a container node that paints behind text.
* Callers walk ancestors so the nearest painted container wins.
*/
var getSupportedAncestorBackgroundColor = function getSupportedAncestorBackgroundColor(node) {
var backgroundColor;
if (node.type.name === 'tableCell' || node.type.name === 'tableHeader') {
backgroundColor = node.attrs.background;
} else if (node.type.name === 'panel' || node.type.name === 'panel_c1') {
if (node.attrs.panelType === _panel.PanelType.CUSTOM) {
backgroundColor = node.attrs.panelColor;
} else {
backgroundColor = STANDARD_PANEL_BACKGROUND_COLORS.get(node.attrs.panelType);
}
}
return backgroundColor ? {
color: backgroundColor,
source: 'element'
} : null;
};
/**
* Resolves the visible background behind a text node.
* Text highlight wins before falling back to the nearest element background.
*/
var getResolvedBackgroundColorForTextNode = exports.getResolvedBackgroundColorForTextNode = function getResolvedBackgroundColorForTextNode($pos, node) {
var _iterator = _createForOfIteratorHelper(node.marks),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var mark = _step.value;
if (mark.type.name === 'backgroundColor' && mark.attrs.color) {
return {
color: mark.attrs.color,
source: 'text'
};
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
for (var depth = $pos.depth; depth > 0; depth--) {
var resolvedBackgroundColor = getSupportedAncestorBackgroundColor($pos.node(depth));
if (resolvedBackgroundColor) {
return resolvedBackgroundColor;
}
}
return {
color: null,
source: 'text'
};
};
var getResolvedBackgroundColorForPosition = function getResolvedBackgroundColorForPosition($pos, highlightColor) {
var highlightBackground = getResolvedBackgroundFromHighlight(highlightColor);
if (highlightColor) {
return highlightBackground;
}
for (var depth = $pos.depth; depth > 0; depth--) {
var resolvedBackgroundColor = getSupportedAncestorBackgroundColor($pos.node(depth));
if (resolvedBackgroundColor) {
return resolvedBackgroundColor;
}
}
return highlightBackground;
};
var getResolvedBackgroundColorValue = function getResolvedBackgroundColorValue(backgroundColor) {
if (backgroundColor.source === 'element') {
return getBackgroundColor(backgroundColor.color, 'element');
}
return getBackgroundColor(backgroundColor.color);
};
var getResolvedBackgroundColorValueInNonActiveTheme = function getResolvedBackgroundColorValueInNonActiveTheme(backgroundColor) {
if (backgroundColor.source === 'element') {
return getBackgroundColorInNonActiveTheme(backgroundColor.color, 'element');
}
return getBackgroundColorInNonActiveTheme(backgroundColor.color);
};
var useColorAccessibilityState = function useColorAccessibilityState(api) {
return (0, _hooks.useSharedPluginStateWithSelector)(api, ['textColor', 'highlight'], function (states) {
var _states$textColorStat, _states$highlightStat, _states$textColorStat2;
return {
defaultColor: (_states$textColorStat = states.textColorState) === null || _states$textColorStat === void 0 ? void 0 : _states$textColorStat.defaultColor,
highlightColor: (_states$highlightStat = states.highlightState) === null || _states$highlightStat === void 0 ? void 0 : _states$highlightStat.activeColor,
textColor: (_states$textColorStat2 = states.textColorState) === null || _states$textColorStat2 === void 0 ? void 0 : _states$textColorStat2.color
};
});
};
var getContrastRatio = function getContrastRatio(defaultColor, backgroundColor, textColor) {
try {
var contrastRatio = (0, _colorContrast.getContrastRatio)(getForegroundColor(textColor, defaultColor), getResolvedBackgroundColorValue(backgroundColor));
return contrastRatio;
} catch (_unused) {
// if we failed to calculate the contrast ratio, return null
return null;
}
};
// Computes the contrast ratio as it would appear in the non-active theme.
var getNonActiveThemeContrastRatio = function getNonActiveThemeContrastRatio(defaultColor, backgroundColor, textColor) {
try {
return (0, _colorContrast.getContrastRatio)((0, _uiColor.getTextColorInNonActiveTheme)(textColor !== null && textColor !== void 0 ? textColor : null, defaultColor || _constants.DEFAULT_COLOR.color), getResolvedBackgroundColorValueInNonActiveTheme(backgroundColor));
} catch (_unused2) {
// if we failed to calculate the contrast ratio, return null
return null;
}
};
var getAccessibilityStatus = function getAccessibilityStatus(contrastRatio) {
if (contrastRatio >= _constants.ACCESSIBLE_CONTRAST_RATIO) {
return 'accessible';
} else if (contrastRatio >= _constants.DIFFICULT_CONTRAST_RATIO) {
return 'difficultToRead';
} else {
return 'inaccessible';
}
};
/**
* Computes status from active foreground/background colors only.
* Used by the gate-off path so legacy behavior stays isolated.
*/
var getAccessibilityStatusFromColorPair = function getAccessibilityStatusFromColorPair(defaultColor, backgroundColor, textColor) {
var contrastRatio = getContrastRatio(defaultColor, backgroundColor, textColor);
return contrastRatio === null ? null : getAccessibilityStatus(contrastRatio);
};
/**
* Returns the lowest contrast ratio across active and non-active themes.
*/
var getMostCriticalContrastRatioAcrossThemes = function getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor) {
var contrastRatio = getContrastRatio(defaultColor, backgroundColor, textColor);
var nonActiveThemeContrastRatio = getNonActiveThemeContrastRatio(defaultColor, backgroundColor, textColor);
if (contrastRatio !== null && nonActiveThemeContrastRatio !== null) {
return Math.min(contrastRatio, nonActiveThemeContrastRatio);
}
return contrastRatio;
};
var getAccessibilityStatusFromColorPairAcrossThemes = function getAccessibilityStatusFromColorPairAcrossThemes(defaultColor, backgroundColor, textColor) {
var mostCriticalContrastRatio = getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor);
return mostCriticalContrastRatio === null ? null : getAccessibilityStatus(mostCriticalContrastRatio);
};
/**
* Collects the worst accessibility status across all unique foreground/background
* combinations in the current selection.
*
* When the selection spans multiple text colors and/or resolved backgrounds,
* this checks each unique pair across both the active and non-active theme and
* returns the worst status found.
*/
var getWorstAccessibilityStatusFromEditorState = function getWorstAccessibilityStatusFromEditorState(state, defaultColor) {
var worstContrastRatio = null;
// Text/highlight colors only apply to a text range. If the selection is
// not a text selection (e.g. a node, cell or all selection) there is no
// meaningful range of colored text to evaluate.
if (!(state.selection instanceof _state.TextSelection) || state.selection.empty) {
return null;
}
var _state$selection = state.selection,
from = _state$selection.from,
to = _state$selection.to;
var seen = new Set();
state.doc.nodesBetween(from, to, function (node, pos) {
var _backgroundColor$colo;
if (!node.isText) {
return !node.isLeaf;
}
var textColor = null;
var _iterator2 = _createForOfIteratorHelper(node.marks),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var mark = _step2.value;
if (mark.type.name === 'textColor') {
var _mark$attrs$color;
textColor = (_mark$attrs$color = mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : null;
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
var backgroundColor = getResolvedBackgroundColorForTextNode(state.doc.resolve(pos), node);
var pairKey = "".concat(textColor !== null && textColor !== void 0 ? textColor : 'default', "|").concat(backgroundColor.source, "|").concat((_backgroundColor$colo = backgroundColor.color) !== null && _backgroundColor$colo !== void 0 ? _backgroundColor$colo : 'default');
// Skip foreground/background pairs already evaluated. The source is
// part of the key because text highlights and element backgrounds use
// different editor palettes.
if (seen.has(pairKey)) {
return;
}
seen.add(pairKey);
var mostCriticalContrastRatio = getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor);
if (mostCriticalContrastRatio === null) {
return;
}
if (worstContrastRatio === null || mostCriticalContrastRatio < worstContrastRatio) {
worstContrastRatio = mostCriticalContrastRatio;
}
});
return worstContrastRatio === null ? null : getAccessibilityStatus(worstContrastRatio);
};
var getAccessibilityStatusForCurrentSelection = exports.getAccessibilityStatusForCurrentSelection = function getAccessibilityStatusForCurrentSelection(state, defaultColor, highlightColor, textColor) {
if (state.selection instanceof _state.TextSelection) {
if (!state.selection.empty) {
return getWorstAccessibilityStatusFromEditorState(state, defaultColor);
}
return getAccessibilityStatusFromColorPairAcrossThemes(defaultColor, getResolvedBackgroundColorForPosition(state.selection.$from, highlightColor), textColor);
}
return null;
};
var AccessibilityStatus = function AccessibilityStatus(_ref) {
var accessibilityStatus = _ref.accessibilityStatus,
formatMessage = _ref.formatMessage;
if (accessibilityStatus === 'accessible') {
return /*#__PURE__*/_react.default.createElement(_compiled.Text, {
as: "span",
size: "small",
color: "color.text.success"
}, formatMessage(_messages.colorAccessibilityMessages.accessibleLabel));
} else if (accessibilityStatus === 'difficultToRead') {
return /*#__PURE__*/_react.default.createElement(_compiled.Text, {
as: "span",
size: "small",
color: "color.text.warning"
}, formatMessage(_messages.colorAccessibilityMessages.difficultToReadLabel));
} else {
return /*#__PURE__*/_react.default.createElement(_compiled.Text, {
as: "span",
size: "small",
color: "color.text.danger"
}, formatMessage(_messages.colorAccessibilityMessages.inaccessibleLabel));
}
};
var styles = {
container: "_19itglyw _19pku2gc _ca0q1b66 _n3td1b66 _19bvu2gc _u5f31b66 _1e0c1txw _4cvr1h6o _1bah1yb4",
containerPatch: "_19pkze3t _ca0qze3t _n3tdze3t _19bvze3t _u5f3ze3t"
};
var ColorAccessibilityMenuItem = exports.ColorAccessibilityMenuItem = function ColorAccessibilityMenuItem(_ref2) {
var api = _ref2.api;
var _useIntl = (0, _reactIntl.useIntl)(),
formatMessage = _useIntl.formatMessage;
var _useColorAccessibilit = useColorAccessibilityState(api),
defaultColor = _useColorAccessibilit.defaultColor,
highlightColor = _useColorAccessibilit.highlightColor,
textColor = _useColorAccessibilit.textColor;
var _useEditorToolbar = (0, _toolbar.useEditorToolbar)(),
editorView = _useEditorToolbar.editorView;
var editorState = editorView === null || editorView === void 0 ? void 0 : editorView.state;
var accessibilityStatus = _react.default.useMemo(function () {
return editorState && (0, _fg.fg)('platform_editor_lovability_text_bg_color_patch_1') ? getAccessibilityStatusForCurrentSelection(editorState, defaultColor, highlightColor, textColor) : getAccessibilityStatusFromColorPair(defaultColor, getResolvedBackgroundFromHighlight(highlightColor), textColor);
}, [defaultColor, editorState, highlightColor, textColor]);
if (accessibilityStatus === null) {
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null);
}
var tooltipContent = function tooltipContent(accessibilityStatus) {
if (accessibilityStatus === 'accessible') {
return formatMessage(_messages.colorAccessibilityMessages.accessibleTooltip);
} else if (accessibilityStatus === 'difficultToRead') {
return formatMessage(_messages.colorAccessibilityMessages.difficultToReadTooltip);
} else {
return formatMessage(_messages.colorAccessibilityMessages.inaccessibleTooltip);
}
};
return /*#__PURE__*/_react.default.createElement(_compiled.Box, {
xcss: (0, _css.cx)(styles.container, (0, _fg.fg)('platform_editor_lovability_text_bg_color_patch_1') && styles.containerPatch)
}, /*#__PURE__*/_react.default.createElement(_compiled.Inline, {
alignBlock: "center",
space: "space.050"
}, /*#__PURE__*/_react.default.createElement(_accessibility.default, {
label: "",
size: "medium",
color: (0, _fg.fg)('platform_editor_lovability_text_bg_color_patch_1') ? "var(--ds-icon-subtle, #505258)" : undefined
}), /*#__PURE__*/_react.default.createElement(_compiled.Text, {
as: "span",
size: "small",
color: "color.text.subtle"
}, formatMessage(_messages.colorAccessibilityMessages.accessibility)), /*#__PURE__*/_react.default.createElement(_compiled.Text, {
as: "span",
size: "small",
color: "color.text.subtle",
"aria-hidden": "true"
}, "\u2022"), /*#__PURE__*/_react.default.createElement(AccessibilityStatus, {
accessibilityStatus: accessibilityStatus,
formatMessage: formatMessage
})), /*#__PURE__*/_react.default.createElement(_button.default, {
icon: _questionCircle.default,
shape: "circle",
label: tooltipContent(accessibilityStatus),
isTooltipDisabled: false,
spacing: "compact",
appearance: "subtle"
}));
};