@atlaskit/editor-plugin-text-color 12.3.29 → 12.3.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/pm-plugins/main.js +3 -16
  3. package/dist/cjs/pm-plugins/utils/color.js +1 -24
  4. package/dist/cjs/ui/ColorAccessibilityMenuItem.compiled.css +0 -1
  5. package/dist/cjs/ui/ColorAccessibilityMenuItem.js +213 -153
  6. package/dist/cjs/ui/RemoveColorMenuItem.compiled.css +0 -1
  7. package/dist/cjs/ui/RemoveColorMenuItem.js +1 -1
  8. package/dist/cjs/ui/TextColorMenuItem.js +1 -1
  9. package/dist/cjs/ui/TextMenuSection.compiled.css +0 -1
  10. package/dist/cjs/ui/TextMenuSection.js +1 -1
  11. package/dist/es2019/pm-plugins/main.js +7 -28
  12. package/dist/es2019/pm-plugins/utils/color.js +0 -27
  13. package/dist/es2019/ui/ColorAccessibilityMenuItem.compiled.css +0 -1
  14. package/dist/es2019/ui/ColorAccessibilityMenuItem.js +191 -143
  15. package/dist/es2019/ui/RemoveColorMenuItem.compiled.css +0 -1
  16. package/dist/es2019/ui/RemoveColorMenuItem.js +1 -1
  17. package/dist/es2019/ui/TextColorMenuItem.js +1 -1
  18. package/dist/es2019/ui/TextMenuSection.compiled.css +0 -1
  19. package/dist/es2019/ui/TextMenuSection.js +1 -1
  20. package/dist/esm/pm-plugins/main.js +5 -18
  21. package/dist/esm/pm-plugins/utils/color.js +0 -23
  22. package/dist/esm/ui/ColorAccessibilityMenuItem.compiled.css +0 -1
  23. package/dist/esm/ui/ColorAccessibilityMenuItem.js +215 -155
  24. package/dist/esm/ui/RemoveColorMenuItem.compiled.css +0 -1
  25. package/dist/esm/ui/RemoveColorMenuItem.js +1 -1
  26. package/dist/esm/ui/TextColorMenuItem.js +1 -1
  27. package/dist/esm/ui/TextMenuSection.compiled.css +0 -1
  28. package/dist/esm/ui/TextMenuSection.js +1 -1
  29. package/dist/types/pm-plugins/main.d.ts +0 -6
  30. package/dist/types/pm-plugins/utils/color.d.ts +0 -5
  31. package/dist/types/ui/ColorAccessibilityMenuItem.d.ts +15 -0
  32. package/package.json +5 -5
@@ -1,4 +1,3 @@
1
- import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
1
  import { DEFAULT_COLOR } from './constants';
3
2
 
4
3
  /**
@@ -45,28 +44,6 @@ export var getActiveColor = function getActiveColor(state) {
45
44
  }
46
45
  return marksWithColor.length ? marksWithColor[0].attrs.color : DEFAULT_COLOR.color;
47
46
  };
48
-
49
- /**
50
- * Returns true when the selection contains two or more distinct text colors,
51
- * counting unmarked text as the default color.
52
- */
53
- export var isMultiTextColorSelection = function isMultiTextColorSelection(state) {
54
- var selection = state.selection,
55
- doc = state.doc;
56
- if (!(selection instanceof TextSelection)) {
57
- return false;
58
- }
59
- var textColor = doc.type.schema.marks.textColor;
60
- var colors = new Set();
61
- doc.nodesBetween(selection.from, selection.to, function (node) {
62
- if (node.isText) {
63
- var _mark$attrs$color;
64
- var mark = textColor.isInSet(node.marks);
65
- colors.add((_mark$attrs$color = mark === null || mark === void 0 ? void 0 : mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : DEFAULT_COLOR.color);
66
- }
67
- });
68
- return colors.size > 1;
69
- };
70
47
  export var getActiveColorNew = function getActiveColorNew(tr) {
71
48
  var _ref3 = tr.selection,
72
49
  $from = _ref3.$from,
@@ -1,4 +1,3 @@
1
-
2
1
  ._19itglyw{border:none}._19bvu2gc{padding-left:var(--ds-space-100,8px)}
3
2
  ._19bvze3t{padding-left:var(--ds-space-0,0)}
4
3
  ._19pku2gc{margin-top:var(--ds-space-100,8px)}
@@ -1,4 +1,4 @@
1
- /* ColorAccessibilityMenuItem.tsx generated by @compiled/babel-plugin v0.39.1 */
1
+ /* ColorAccessibilityMenuItem.tsx generated by @compiled/babel-plugin v0.40.0 */
2
2
  import "./ColorAccessibilityMenuItem.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  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; } } }; }
@@ -6,12 +6,15 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
6
6
  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; }
7
7
  import React from 'react';
8
8
  import { useIntl } from 'react-intl';
9
+ import { PanelType } from '@atlaskit/adf-schema/panel';
9
10
  import { IconButton } from '@atlaskit/button/new';
10
11
  import { cx } from '@atlaskit/css';
11
12
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
12
13
  import { colorAccessibilityMessages as messages } from '@atlaskit/editor-common/messages';
13
- import { getHighlightColorInNonActiveTheme, getTextColorInNonActiveTheme, getTokenCSSVariableValue } from '@atlaskit/editor-common/ui-color';
14
- import { hexToEditorTextBackgroundPaletteColor, hexToEditorTextPaletteColor } from '@atlaskit/editor-palette';
14
+ import { getPanelTypeBackgroundNoTokens } from '@atlaskit/editor-common/panel';
15
+ import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
16
+ import { getTextColorInNonActiveTheme, getTokenCSSVariableValue, getTokenCSSVariableValueForNonActiveTheme } from '@atlaskit/editor-common/ui-color';
17
+ import { hexToEditorBackgroundPaletteColor, hexToEditorTextBackgroundPaletteColor, hexToEditorTextPaletteColor } from '@atlaskit/editor-palette';
15
18
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
16
19
  import AccessibilityIcon from '@atlaskit/icon/core/accessibility';
17
20
  import QuestionCircleIcon from '@atlaskit/icon/core/question-circle';
@@ -19,7 +22,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
19
22
  import { Box, Inline, Text } from '@atlaskit/primitives/compiled';
20
23
  import { getTokenValue } from '@atlaskit/tokens/get-token-value';
21
24
  import { getContrastRatio as calcContrastRatio } from '../pm-plugins/utils/color-contrast';
22
- import { DEFAULT_COLOR, DEFAULT_BACKGROUND_COLOR, TRANSPARENT_HIGHLIGHT_COLOR, ACCESSIBLE_CONTRAST_RATIO, DIFFICULT_CONTRAST_RATIO } from '../pm-plugins/utils/constants';
25
+ import { DEFAULT_COLOR, DEFAULT_BACKGROUND_COLOR, ACCESSIBLE_CONTRAST_RATIO, DIFFICULT_CONTRAST_RATIO } from '../pm-plugins/utils/constants';
23
26
  var resolveColorValue = function resolveColorValue(color, fallback) {
24
27
  return getTokenCSSVariableValue(color) || (color.startsWith('var(') ? fallback : color);
25
28
  };
@@ -30,30 +33,128 @@ var getForegroundColor = function getForegroundColor(textColor, defaultColor) {
30
33
  var colorValue = hexToEditorTextPaletteColor(textColor) || textColor;
31
34
  return resolveColorValue(colorValue, textColor);
32
35
  };
33
- var getBackgroundColor = function getBackgroundColor(highlightColor) {
34
- if (!highlightColor || highlightColor === TRANSPARENT_HIGHLIGHT_COLOR) {
36
+ var getBackgroundColor = function getBackgroundColor(backgroundColor) {
37
+ var palette = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'text';
38
+ if (!backgroundColor || backgroundColor === 'none') {
35
39
  return getTokenValue('elevation.surface', DEFAULT_BACKGROUND_COLOR);
36
40
  }
37
- var colorValue = hexToEditorTextBackgroundPaletteColor(highlightColor) || highlightColor;
38
- return resolveColorValue(colorValue, highlightColor);
41
+ var colorValue = palette === 'element' ? hexToEditorBackgroundPaletteColor(backgroundColor) || backgroundColor : hexToEditorTextBackgroundPaletteColor(backgroundColor) || backgroundColor;
42
+ return resolveColorValue(colorValue, backgroundColor);
43
+ };
44
+ var getBackgroundColorInNonActiveTheme = function getBackgroundColorInNonActiveTheme(backgroundColor) {
45
+ var palette = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'text';
46
+ if (!backgroundColor || backgroundColor === 'none') {
47
+ return getTokenCSSVariableValueForNonActiveTheme("var(--ds-surface, #FFFFFF)", DEFAULT_BACKGROUND_COLOR);
48
+ }
49
+ var colorValue = palette === 'element' ? hexToEditorBackgroundPaletteColor(backgroundColor) || backgroundColor : hexToEditorTextBackgroundPaletteColor(backgroundColor) || backgroundColor;
50
+ return getTokenCSSVariableValueForNonActiveTheme(colorValue, backgroundColor);
51
+ };
52
+ var STANDARD_PANEL_BACKGROUND_COLORS = new Map([[PanelType.INFO, getPanelTypeBackgroundNoTokens(PanelType.INFO)], [PanelType.NOTE, getPanelTypeBackgroundNoTokens(PanelType.NOTE)], [PanelType.SUCCESS, getPanelTypeBackgroundNoTokens(PanelType.SUCCESS)], [PanelType.WARNING, getPanelTypeBackgroundNoTokens(PanelType.WARNING)], [PanelType.ERROR, getPanelTypeBackgroundNoTokens(PanelType.ERROR)]]);
53
+ var getResolvedBackgroundFromHighlight = function getResolvedBackgroundFromHighlight(highlightColor) {
54
+ if (!highlightColor) {
55
+ return {
56
+ color: null,
57
+ source: 'text'
58
+ };
59
+ }
60
+ return {
61
+ color: highlightColor,
62
+ source: 'text'
63
+ };
64
+ };
65
+
66
+ /**
67
+ * Resolves a background color from a container node that paints behind text.
68
+ * Callers walk ancestors so the nearest painted container wins.
69
+ */
70
+ var getSupportedAncestorBackgroundColor = function getSupportedAncestorBackgroundColor(node) {
71
+ var backgroundColor;
72
+ if (node.type.name === 'tableCell' || node.type.name === 'tableHeader') {
73
+ backgroundColor = node.attrs.background;
74
+ } else if (node.type.name === 'panel' || node.type.name === 'panel_c1') {
75
+ if (node.attrs.panelType === PanelType.CUSTOM) {
76
+ backgroundColor = node.attrs.panelColor;
77
+ } else {
78
+ backgroundColor = STANDARD_PANEL_BACKGROUND_COLORS.get(node.attrs.panelType);
79
+ }
80
+ }
81
+ return backgroundColor ? {
82
+ color: backgroundColor,
83
+ source: 'element'
84
+ } : null;
85
+ };
86
+
87
+ /**
88
+ * Resolves the visible background behind a text node.
89
+ * Text highlight wins before falling back to the nearest element background.
90
+ */
91
+ export var getResolvedBackgroundColorForTextNode = function getResolvedBackgroundColorForTextNode($pos, node) {
92
+ var _iterator = _createForOfIteratorHelper(node.marks),
93
+ _step;
94
+ try {
95
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
96
+ var mark = _step.value;
97
+ if (mark.type.name === 'backgroundColor' && mark.attrs.color) {
98
+ return {
99
+ color: mark.attrs.color,
100
+ source: 'text'
101
+ };
102
+ }
103
+ }
104
+ } catch (err) {
105
+ _iterator.e(err);
106
+ } finally {
107
+ _iterator.f();
108
+ }
109
+ for (var depth = $pos.depth; depth > 0; depth--) {
110
+ var resolvedBackgroundColor = getSupportedAncestorBackgroundColor($pos.node(depth));
111
+ if (resolvedBackgroundColor) {
112
+ return resolvedBackgroundColor;
113
+ }
114
+ }
115
+ return {
116
+ color: null,
117
+ source: 'text'
118
+ };
119
+ };
120
+ var getResolvedBackgroundColorForPosition = function getResolvedBackgroundColorForPosition($pos, highlightColor) {
121
+ var highlightBackground = getResolvedBackgroundFromHighlight(highlightColor);
122
+ if (highlightColor) {
123
+ return highlightBackground;
124
+ }
125
+ for (var depth = $pos.depth; depth > 0; depth--) {
126
+ var resolvedBackgroundColor = getSupportedAncestorBackgroundColor($pos.node(depth));
127
+ if (resolvedBackgroundColor) {
128
+ return resolvedBackgroundColor;
129
+ }
130
+ }
131
+ return highlightBackground;
132
+ };
133
+ var getResolvedBackgroundColorValue = function getResolvedBackgroundColorValue(backgroundColor) {
134
+ if (backgroundColor.source === 'element') {
135
+ return getBackgroundColor(backgroundColor.color, 'element');
136
+ }
137
+ return getBackgroundColor(backgroundColor.color);
138
+ };
139
+ var getResolvedBackgroundColorValueInNonActiveTheme = function getResolvedBackgroundColorValueInNonActiveTheme(backgroundColor) {
140
+ if (backgroundColor.source === 'element') {
141
+ return getBackgroundColorInNonActiveTheme(backgroundColor.color, 'element');
142
+ }
143
+ return getBackgroundColorInNonActiveTheme(backgroundColor.color);
39
144
  };
40
145
  var useColorAccessibilityState = function useColorAccessibilityState(api) {
41
146
  return useSharedPluginStateWithSelector(api, ['textColor', 'highlight'], function (states) {
42
- var _states$textColorStat, _states$highlightStat, _states$highlightStat2, _states$highlightStat3, _states$textColorStat2, _states$textColorStat3, _states$textColorStat4;
147
+ var _states$textColorStat, _states$highlightStat, _states$textColorStat2;
43
148
  return {
44
149
  defaultColor: (_states$textColorStat = states.textColorState) === null || _states$textColorStat === void 0 ? void 0 : _states$textColorStat.defaultColor,
45
150
  highlightColor: (_states$highlightStat = states.highlightState) === null || _states$highlightStat === void 0 ? void 0 : _states$highlightStat.activeColor,
46
- highlightColorInNonActiveTheme: (_states$highlightStat2 = states.highlightState) === null || _states$highlightStat2 === void 0 ? void 0 : _states$highlightStat2.activeColorInNonActiveTheme,
47
- isMultiHighlightColor: (_states$highlightStat3 = states.highlightState) === null || _states$highlightStat3 === void 0 ? void 0 : _states$highlightStat3.isMultiHighlightColor,
48
- isMultiTextColor: (_states$textColorStat2 = states.textColorState) === null || _states$textColorStat2 === void 0 ? void 0 : _states$textColorStat2.isMultiTextColor,
49
- textColor: (_states$textColorStat3 = states.textColorState) === null || _states$textColorStat3 === void 0 ? void 0 : _states$textColorStat3.color,
50
- textColorInNonActiveTheme: (_states$textColorStat4 = states.textColorState) === null || _states$textColorStat4 === void 0 ? void 0 : _states$textColorStat4.colorInNonActiveTheme
151
+ textColor: (_states$textColorStat2 = states.textColorState) === null || _states$textColorStat2 === void 0 ? void 0 : _states$textColorStat2.color
51
152
  };
52
153
  });
53
154
  };
54
- var getContrastRatio = function getContrastRatio(defaultColor, highlightColor, textColor) {
155
+ var getContrastRatio = function getContrastRatio(defaultColor, backgroundColor, textColor) {
55
156
  try {
56
- var contrastRatio = calcContrastRatio(getForegroundColor(textColor, defaultColor), getBackgroundColor(highlightColor));
157
+ var contrastRatio = calcContrastRatio(getForegroundColor(textColor, defaultColor), getResolvedBackgroundColorValue(backgroundColor));
57
158
  return contrastRatio;
58
159
  } catch (_unused) {
59
160
  // if we failed to calculate the contrast ratio, return null
@@ -61,16 +162,10 @@ var getContrastRatio = function getContrastRatio(defaultColor, highlightColor, t
61
162
  }
62
163
  };
63
164
 
64
- // Computes the contrast ratio for a (textColor, highlightColor) pair as it would
65
- // appear in the non-active theme, using the shared resolvers so the walker
66
- // evaluates the same colors the single-pair plugin-state path does. Returns null
67
- // if it cannot be calculated.
68
- var getNonActiveThemeContrastRatio = function getNonActiveThemeContrastRatio(defaultColor, highlightColor, textColor) {
165
+ // Computes the contrast ratio as it would appear in the non-active theme.
166
+ var getNonActiveThemeContrastRatio = function getNonActiveThemeContrastRatio(defaultColor, backgroundColor, textColor) {
69
167
  try {
70
- return calcContrastRatio(getTextColorInNonActiveTheme(textColor !== null && textColor !== void 0 ? textColor : null, defaultColor || DEFAULT_COLOR.color), getHighlightColorInNonActiveTheme(highlightColor !== null && highlightColor !== void 0 ? highlightColor : null, {
71
- defaultBackgroundColor: DEFAULT_BACKGROUND_COLOR,
72
- transparentColor: TRANSPARENT_HIGHLIGHT_COLOR
73
- }));
168
+ return calcContrastRatio(getTextColorInNonActiveTheme(textColor !== null && textColor !== void 0 ? textColor : null, defaultColor || DEFAULT_COLOR.color), getResolvedBackgroundColorValueInNonActiveTheme(backgroundColor));
74
169
  } catch (_unused2) {
75
170
  // if we failed to calculate the contrast ratio, return null
76
171
  return null;
@@ -87,96 +182,103 @@ var getAccessibilityStatus = function getAccessibilityStatus(contrastRatio) {
87
182
  };
88
183
 
89
184
  /**
90
- * Collects the worst accessibility status across all unique (textColor, highlightColor)
185
+ * Computes status from active foreground/background colors only.
186
+ * Used by the gate-off path so legacy behavior stays isolated.
187
+ */
188
+ var getAccessibilityStatusFromColorPair = function getAccessibilityStatusFromColorPair(defaultColor, backgroundColor, textColor) {
189
+ var contrastRatio = getContrastRatio(defaultColor, backgroundColor, textColor);
190
+ return contrastRatio === null ? null : getAccessibilityStatus(contrastRatio);
191
+ };
192
+
193
+ /**
194
+ * Returns the lowest contrast ratio across active and non-active themes.
195
+ */
196
+ var getMostCriticalContrastRatioAcrossThemes = function getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor) {
197
+ var contrastRatio = getContrastRatio(defaultColor, backgroundColor, textColor);
198
+ var nonActiveThemeContrastRatio = getNonActiveThemeContrastRatio(defaultColor, backgroundColor, textColor);
199
+ if (contrastRatio !== null && nonActiveThemeContrastRatio !== null) {
200
+ return Math.min(contrastRatio, nonActiveThemeContrastRatio);
201
+ }
202
+ return contrastRatio;
203
+ };
204
+ var getAccessibilityStatusFromColorPairAcrossThemes = function getAccessibilityStatusFromColorPairAcrossThemes(defaultColor, backgroundColor, textColor) {
205
+ var mostCriticalContrastRatio = getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor);
206
+ return mostCriticalContrastRatio === null ? null : getAccessibilityStatus(mostCriticalContrastRatio);
207
+ };
208
+
209
+ /**
210
+ * Collects the worst accessibility status across all unique foreground/background
91
211
  * combinations in the current selection.
92
212
  *
93
- * When the selection spans multiple text colors and/or highlight colors,
94
- * this walks the document between selection boundaries and checks every
95
- * text node's color marks, computing the contrast ratio for each unique pair
96
- * (across both the active and non-active theme) and returning the worst
97
- * status found.
213
+ * When the selection spans multiple text colors and/or resolved backgrounds,
214
+ * this checks each unique pair across both the active and non-active theme and
215
+ * returns the worst status found.
98
216
  */
99
- var getWorstAccessibilityStatusFromSelection = function getWorstAccessibilityStatusFromSelection(api, defaultColor) {
100
- if (!(api !== null && api !== void 0 && api.core)) {
217
+ var getWorstAccessibilityStatusFromEditorState = function getWorstAccessibilityStatusFromEditorState(state, defaultColor) {
218
+ var worstContrastRatio = null;
219
+
220
+ // Text/highlight colors only apply to a text range. If the selection is
221
+ // not a text selection (e.g. a node, cell or all selection) there is no
222
+ // meaningful range of colored text to evaluate.
223
+ if (!(state.selection instanceof TextSelection) || state.selection.empty) {
101
224
  return null;
102
225
  }
103
-
104
- // Track the lowest (worst) contrast ratio across all pairs; a lower ratio
105
- // means a worse accessibility status, so a single numeric comparison is
106
- // enough to find the worst pair.
107
- var worstContrastRatio = null;
108
- api.core.actions.execute(function (_ref) {
109
- var tr = _ref.tr;
110
- // Text/highlight colors only apply to a text range. If the selection is
111
- // not a text selection (e.g. a node, cell or all selection) there is no
112
- // meaningful range of colored text to evaluate.
113
- if (!(tr.selection instanceof TextSelection)) {
114
- return null;
226
+ var _state$selection = state.selection,
227
+ from = _state$selection.from,
228
+ to = _state$selection.to;
229
+ var seen = new Set();
230
+ state.doc.nodesBetween(from, to, function (node, pos) {
231
+ var _backgroundColor$colo;
232
+ if (!node.isText) {
233
+ return !node.isLeaf;
115
234
  }
116
- var _tr$selection = tr.selection,
117
- from = _tr$selection.from,
118
- to = _tr$selection.to;
119
- var seen = new Set();
120
- tr.doc.nodesBetween(from, to, function (node) {
121
- // Text and highlight color marks only apply to text nodes, so skip
122
- // any non-text leaves (e.g. emoji, mentions, inline cards). Continue
123
- // descending into container nodes to reach their text children.
124
- if (!node.isText) {
125
- return !node.isLeaf;
126
- }
127
-
128
- // Extract both color marks in a single pass over the node's marks
129
- // rather than two `Array.find` scans.
130
- var textColor = null;
131
- var highlightColor = null;
132
- var _iterator = _createForOfIteratorHelper(node.marks),
133
- _step;
134
- try {
135
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
136
- var mark = _step.value;
137
- if (mark.type.name === 'textColor') {
138
- var _mark$attrs$color;
139
- textColor = (_mark$attrs$color = mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : null;
140
- } else if (mark.type.name === 'backgroundColor') {
141
- var _mark$attrs$color2;
142
- highlightColor = (_mark$attrs$color2 = mark.attrs.color) !== null && _mark$attrs$color2 !== void 0 ? _mark$attrs$color2 : null;
143
- }
235
+ var textColor = null;
236
+ var _iterator2 = _createForOfIteratorHelper(node.marks),
237
+ _step2;
238
+ try {
239
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
240
+ var mark = _step2.value;
241
+ if (mark.type.name === 'textColor') {
242
+ var _mark$attrs$color;
243
+ textColor = (_mark$attrs$color = mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : null;
144
244
  }
145
- } catch (err) {
146
- _iterator.e(err);
147
- } finally {
148
- _iterator.f();
149
- }
150
- var pairKey = "".concat(textColor !== null && textColor !== void 0 ? textColor : 'default', "|").concat(highlightColor !== null && highlightColor !== void 0 ? highlightColor : 'default');
151
- // Skip pairs we have already evaluated so the expensive contrast
152
- // calculations run at most once per unique (text, highlight) pair.
153
- if (seen.has(pairKey)) {
154
- return;
155
- }
156
- seen.add(pairKey);
157
- var contrastRatio = getContrastRatio(defaultColor, highlightColor, textColor);
158
- if (contrastRatio === null) {
159
- return;
160
- }
161
-
162
- // Account for the non-active theme so the worst-case (least
163
- // accessible) contrast across both themes is used for each pair,
164
- // keeping multi-color selections consistent with the single-pair path.
165
- var nonActiveThemeContrastRatio = getNonActiveThemeContrastRatio(defaultColor, highlightColor, textColor);
166
- var mostCriticalContrastRatio = nonActiveThemeContrastRatio !== null ? Math.min(contrastRatio, nonActiveThemeContrastRatio) : contrastRatio;
167
- if (worstContrastRatio === null || mostCriticalContrastRatio < worstContrastRatio) {
168
- worstContrastRatio = mostCriticalContrastRatio;
169
245
  }
170
- });
171
-
172
- // Read-only operation — do not dispatch
173
- return null;
246
+ } catch (err) {
247
+ _iterator2.e(err);
248
+ } finally {
249
+ _iterator2.f();
250
+ }
251
+ var backgroundColor = getResolvedBackgroundColorForTextNode(state.doc.resolve(pos), node);
252
+ 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');
253
+ // Skip foreground/background pairs already evaluated. The source is
254
+ // part of the key because text highlights and element backgrounds use
255
+ // different editor palettes.
256
+ if (seen.has(pairKey)) {
257
+ return;
258
+ }
259
+ seen.add(pairKey);
260
+ var mostCriticalContrastRatio = getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor);
261
+ if (mostCriticalContrastRatio === null) {
262
+ return;
263
+ }
264
+ if (worstContrastRatio === null || mostCriticalContrastRatio < worstContrastRatio) {
265
+ worstContrastRatio = mostCriticalContrastRatio;
266
+ }
174
267
  });
175
268
  return worstContrastRatio === null ? null : getAccessibilityStatus(worstContrastRatio);
176
269
  };
177
- var AccessibilityStatus = function AccessibilityStatus(_ref2) {
178
- var accessibilityStatus = _ref2.accessibilityStatus,
179
- formatMessage = _ref2.formatMessage;
270
+ export var getAccessibilityStatusForCurrentSelection = function getAccessibilityStatusForCurrentSelection(state, defaultColor, highlightColor, textColor) {
271
+ if (state.selection instanceof TextSelection) {
272
+ if (!state.selection.empty) {
273
+ return getWorstAccessibilityStatusFromEditorState(state, defaultColor);
274
+ }
275
+ return getAccessibilityStatusFromColorPairAcrossThemes(defaultColor, getResolvedBackgroundColorForPosition(state.selection.$from, highlightColor), textColor);
276
+ }
277
+ return null;
278
+ };
279
+ var AccessibilityStatus = function AccessibilityStatus(_ref) {
280
+ var accessibilityStatus = _ref.accessibilityStatus,
281
+ formatMessage = _ref.formatMessage;
180
282
  if (accessibilityStatus === 'accessible') {
181
283
  return /*#__PURE__*/React.createElement(Text, {
182
284
  as: "span",
@@ -201,62 +303,20 @@ var styles = {
201
303
  container: "_19itglyw _19pku2gc _ca0q1b66 _n3td1b66 _19bvu2gc _u5f31b66 _1e0c1txw _4cvr1h6o _1bah1yb4",
202
304
  containerPatch: "_19pkze3t _ca0qze3t _n3tdze3t _19bvze3t _u5f3ze3t"
203
305
  };
204
- export var ColorAccessibilityMenuItem = function ColorAccessibilityMenuItem(_ref3) {
205
- var api = _ref3.api;
306
+ export var ColorAccessibilityMenuItem = function ColorAccessibilityMenuItem(_ref2) {
307
+ var api = _ref2.api;
206
308
  var _useIntl = useIntl(),
207
309
  formatMessage = _useIntl.formatMessage;
208
310
  var _useColorAccessibilit = useColorAccessibilityState(api),
209
311
  defaultColor = _useColorAccessibilit.defaultColor,
210
312
  highlightColor = _useColorAccessibilit.highlightColor,
211
- highlightColorInNonActiveTheme = _useColorAccessibilit.highlightColorInNonActiveTheme,
212
- isMultiHighlightColor = _useColorAccessibilit.isMultiHighlightColor,
213
- isMultiTextColor = _useColorAccessibilit.isMultiTextColor,
214
- textColor = _useColorAccessibilit.textColor,
215
- textColorInNonActiveTheme = _useColorAccessibilit.textColorInNonActiveTheme;
216
- // The multi-color selection handling (walking every unique text/highlight
217
- // pair across the active and non-active theme) is only performed when the
218
- // patch gate is on. When the gate is off we fall back to the legacy behavior
219
- // of only evaluating the single active text/highlight color in the current
220
- // active theme
221
- var isMultiColorSelection = Boolean(isMultiTextColor || isMultiHighlightColor) && fg('platform_editor_lovability_text_bg_color_patch_1');
222
-
223
- // The complement of `isMultiColorSelection`: the single active text/highlight
224
- // pair is evaluated whenever we are not walking a multi-color selection. This
225
- // covers both a genuinely single-color selection (gate on) and every
226
- // selection when the gate is off (legacy behavior).
227
- var isSingleColorSelection = !isMultiColorSelection;
228
-
229
- // Memoize the selection walk so we don't re-traverse the entire ProseMirror
230
- // document on every render. It only needs to recompute when the multi-color
231
- // selection state or the inputs to the walk change (the selection flags,
232
- // default color, or the plugin api).
233
- var multiColorStatus = React.useMemo(function () {
234
- return isMultiColorSelection ? getWorstAccessibilityStatusFromSelection(api, defaultColor) : null;
235
- },
236
- // eslint-disable-next-line react-hooks/exhaustive-deps
237
- [isMultiColorSelection, isMultiTextColor, isMultiHighlightColor, defaultColor, api]);
238
-
239
- // The single active text/highlight pair path. Everything below only applies
240
- // when we are not walking a multi-color selection
241
- var singleColorStatus = null;
242
- if (isSingleColorSelection) {
243
- var singleColorContrastRatio = getContrastRatio(defaultColor, highlightColor, textColor);
244
-
245
- // Also account for the non-active theme (gate on only) so the worst-case
246
- // (least accessible) status across both themes is shown.
247
- var nonActiveThemeSingleColorRatio = null;
248
- if (textColorInNonActiveTheme && highlightColorInNonActiveTheme && fg('platform_editor_lovability_text_bg_color_patch_1')) {
249
- try {
250
- nonActiveThemeSingleColorRatio = calcContrastRatio(textColorInNonActiveTheme, highlightColorInNonActiveTheme);
251
- } catch (_unused3) {
252
- // if we failed to calculate the contrast ratio, leave as null
253
- nonActiveThemeSingleColorRatio = null;
254
- }
255
- }
256
- var mostCriticalContrastRatio = singleColorContrastRatio !== null && nonActiveThemeSingleColorRatio !== null ? Math.min(singleColorContrastRatio, nonActiveThemeSingleColorRatio) : singleColorContrastRatio;
257
- singleColorStatus = mostCriticalContrastRatio !== null ? getAccessibilityStatus(mostCriticalContrastRatio) : null;
258
- }
259
- var accessibilityStatus = isSingleColorSelection ? singleColorStatus : multiColorStatus;
313
+ textColor = _useColorAccessibilit.textColor;
314
+ var _useEditorToolbar = useEditorToolbar(),
315
+ editorView = _useEditorToolbar.editorView;
316
+ var editorState = editorView === null || editorView === void 0 ? void 0 : editorView.state;
317
+ var accessibilityStatus = React.useMemo(function () {
318
+ return editorState && fg('platform_editor_lovability_text_bg_color_patch_1') ? getAccessibilityStatusForCurrentSelection(editorState, defaultColor, highlightColor, textColor) : getAccessibilityStatusFromColorPair(defaultColor, getResolvedBackgroundFromHighlight(highlightColor), textColor);
319
+ }, [defaultColor, editorState, highlightColor, textColor]);
260
320
  if (accessibilityStatus === null) {
261
321
  return /*#__PURE__*/React.createElement(React.Fragment, null);
262
322
  }
@@ -1,4 +1,3 @@
1
-
2
1
  ._2rko12b0{border-radius:var(--ds-radius-small,4px)}
3
2
  ._zulp12x7{gap:var(--ds-space-075,6px)}._189ee4h9{border-width:var(--ds-border-width,1px)}
4
3
  ._195gv77o{margin-inline:var(--ds-space-025,2px)}
@@ -1,4 +1,4 @@
1
- /* RemoveColorMenuItem.tsx generated by @compiled/babel-plugin v0.39.1 */
1
+ /* RemoveColorMenuItem.tsx generated by @compiled/babel-plugin v0.40.0 */
2
2
  import "./RemoveColorMenuItem.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
@@ -1,4 +1,4 @@
1
- /* TextColorMenuItem.tsx generated by @compiled/babel-plugin v0.39.1 */
1
+ /* TextColorMenuItem.tsx generated by @compiled/babel-plugin v0.40.0 */
2
2
  import "./TextColorMenuItem.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React, { useCallback } from 'react';
@@ -1,4 +1,3 @@
1
-
2
1
  ._zulpu2gc{gap:var(--ds-space-100,8px)}._18zru2gc{padding-inline:var(--ds-space-100,8px)}
3
2
  ._18zrutpp{padding-inline:var(--ds-space-150,9pt)}
4
3
  ._1rjc12x7{padding-block:var(--ds-space-075,6px)}
@@ -1,4 +1,4 @@
1
- /* TextMenuSection.tsx generated by @compiled/babel-plugin v0.39.1 */
1
+ /* TextMenuSection.tsx generated by @compiled/babel-plugin v0.40.0 */
2
2
  import "./TextMenuSection.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React from 'react';
@@ -5,14 +5,8 @@ import type { PaletteColor } from '@atlaskit/editor-common/ui-color';
5
5
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
6
  export type TextColorPluginState = {
7
7
  color: string | null;
8
- colorInNonActiveTheme?: string;
9
8
  defaultColor: string;
10
9
  disabled?: boolean;
11
- /**
12
- * True when the current selection spans more than one text color. Only
13
- * populated behind the lovability text/bg color patch gate + experiment.
14
- */
15
- isMultiTextColor?: boolean;
16
10
  isPaletteOpen?: boolean;
17
11
  palette: Array<PaletteColor>;
18
12
  };
@@ -3,9 +3,4 @@ import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/stat
3
3
  * Use getDisabledStateNew instead and pass in `tr`
4
4
  */
5
5
  export declare const getActiveColor: (state: EditorState) => string | null;
6
- /**
7
- * Returns true when the selection contains two or more distinct text colors,
8
- * counting unmarked text as the default color.
9
- */
10
- export declare const isMultiTextColorSelection: (state: EditorState) => boolean;
11
6
  export declare const getActiveColorNew: (tr: Transaction) => string | null;
@@ -1,8 +1,23 @@
1
1
  import React from 'react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { Node, ResolvedPos } from '@atlaskit/editor-prosemirror/model';
4
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
3
5
  import type { TextColorPlugin } from '../textColorPluginType';
4
6
  type ColorAccessibilityMenuItemProps = {
5
7
  api: ExtractInjectionAPI<TextColorPlugin> | undefined;
6
8
  };
9
+ type BackgroundColorSource = 'element' | 'text';
10
+ export type ResolvedBackgroundColor = {
11
+ color: string | null;
12
+ source: BackgroundColorSource;
13
+ };
14
+ type ColorAccessibilityEditorState = Pick<EditorState, 'doc' | 'selection'>;
15
+ /**
16
+ * Resolves the visible background behind a text node.
17
+ * Text highlight wins before falling back to the nearest element background.
18
+ */
19
+ export declare const getResolvedBackgroundColorForTextNode: ($pos: ResolvedPos, node: Node) => ResolvedBackgroundColor;
20
+ type AccessibilityStatusKey = 'accessible' | 'difficultToRead' | 'inaccessible';
21
+ export declare const getAccessibilityStatusForCurrentSelection: (state: ColorAccessibilityEditorState, defaultColor: string | undefined, highlightColor: string | null | undefined, textColor: string | null | undefined) => AccessibilityStatusKey | null;
7
22
  export declare const ColorAccessibilityMenuItem: ({ api, }: ColorAccessibilityMenuItemProps) => React.JSX.Element;
8
23
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-text-color",
3
- "version": "12.3.29",
3
+ "version": "12.3.31",
4
4
  "description": "Text color plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -36,18 +36,18 @@
36
36
  "@atlaskit/editor-toolbar": "^2.3.0",
37
37
  "@atlaskit/editor-toolbar-model": "^1.1.0",
38
38
  "@atlaskit/heading": "^6.2.0",
39
- "@atlaskit/icon": "^37.0.0",
40
- "@atlaskit/icon-lab": "^7.3.0",
39
+ "@atlaskit/icon": "^37.1.0",
40
+ "@atlaskit/icon-lab": "^7.4.0",
41
41
  "@atlaskit/platform-feature-flags": "^2.0.0",
42
42
  "@atlaskit/platform-feature-flags-react": "^1.1.0",
43
43
  "@atlaskit/primitives": "^22.0.0",
44
- "@atlaskit/tmp-editor-statsig": "^129.0.0",
44
+ "@atlaskit/tmp-editor-statsig": "^130.1.0",
45
45
  "@atlaskit/tokens": "^16.1.0",
46
46
  "@babel/runtime": "^7.0.0",
47
47
  "@emotion/react": "^11.7.1"
48
48
  },
49
49
  "peerDependencies": {
50
- "@atlaskit/editor-common": "^116.33.0",
50
+ "@atlaskit/editor-common": "^116.34.0",
51
51
  "react": "^18.2.0",
52
52
  "react-dom": "^18.2.0",
53
53
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"