@atlaskit/editor-plugin-text-color 12.3.30 → 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.
@@ -1,4 +1,3 @@
1
- import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
1
  import { DEFAULT_COLOR } from './constants';
3
2
 
4
3
  /**
@@ -45,32 +44,6 @@ export const 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 const isMultiTextColorSelection = state => {
54
- const {
55
- selection,
56
- doc
57
- } = state;
58
- if (!(selection instanceof TextSelection)) {
59
- return false;
60
- }
61
- const {
62
- textColor
63
- } = doc.type.schema.marks;
64
- const colors = new Set();
65
- doc.nodesBetween(selection.from, selection.to, node => {
66
- if (node.isText) {
67
- var _mark$attrs$color;
68
- const mark = textColor.isInSet(node.marks);
69
- 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);
70
- }
71
- });
72
- return colors.size > 1;
73
- };
74
47
  export const getActiveColorNew = tr => {
75
48
  const {
76
49
  $from,
@@ -3,12 +3,15 @@ import "./ColorAccessibilityMenuItem.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React from 'react';
5
5
  import { useIntl } from 'react-intl';
6
+ import { PanelType } from '@atlaskit/adf-schema/panel';
6
7
  import { IconButton } from '@atlaskit/button/new';
7
8
  import { cx } from '@atlaskit/css';
8
9
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
9
10
  import { colorAccessibilityMessages as messages } from '@atlaskit/editor-common/messages';
10
- import { getHighlightColorInNonActiveTheme, getTextColorInNonActiveTheme, getTokenCSSVariableValue } from '@atlaskit/editor-common/ui-color';
11
- import { hexToEditorTextBackgroundPaletteColor, hexToEditorTextPaletteColor } from '@atlaskit/editor-palette';
11
+ import { getPanelTypeBackgroundNoTokens } from '@atlaskit/editor-common/panel';
12
+ import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
13
+ import { getTextColorInNonActiveTheme, getTokenCSSVariableValue, getTokenCSSVariableValueForNonActiveTheme } from '@atlaskit/editor-common/ui-color';
14
+ import { hexToEditorBackgroundPaletteColor, hexToEditorTextBackgroundPaletteColor, hexToEditorTextPaletteColor } from '@atlaskit/editor-palette';
12
15
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
13
16
  import AccessibilityIcon from '@atlaskit/icon/core/accessibility';
14
17
  import QuestionCircleIcon from '@atlaskit/icon/core/question-circle';
@@ -16,7 +19,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
16
19
  import { Box, Inline, Text } from '@atlaskit/primitives/compiled';
17
20
  import { getTokenValue } from '@atlaskit/tokens/get-token-value';
18
21
  import { getContrastRatio as calcContrastRatio } from '../pm-plugins/utils/color-contrast';
19
- import { DEFAULT_COLOR, DEFAULT_BACKGROUND_COLOR, TRANSPARENT_HIGHLIGHT_COLOR, ACCESSIBLE_CONTRAST_RATIO, DIFFICULT_CONTRAST_RATIO } from '../pm-plugins/utils/constants';
22
+ import { DEFAULT_COLOR, DEFAULT_BACKGROUND_COLOR, ACCESSIBLE_CONTRAST_RATIO, DIFFICULT_CONTRAST_RATIO } from '../pm-plugins/utils/constants';
20
23
  const resolveColorValue = (color, fallback) => {
21
24
  return getTokenCSSVariableValue(color) || (color.startsWith('var(') ? fallback : color);
22
25
  };
@@ -27,30 +30,117 @@ const getForegroundColor = (textColor, defaultColor) => {
27
30
  const colorValue = hexToEditorTextPaletteColor(textColor) || textColor;
28
31
  return resolveColorValue(colorValue, textColor);
29
32
  };
30
- const getBackgroundColor = highlightColor => {
31
- if (!highlightColor || highlightColor === TRANSPARENT_HIGHLIGHT_COLOR) {
33
+ const getBackgroundColor = (backgroundColor, palette = 'text') => {
34
+ if (!backgroundColor || backgroundColor === 'none') {
32
35
  return getTokenValue('elevation.surface', DEFAULT_BACKGROUND_COLOR);
33
36
  }
34
- const colorValue = hexToEditorTextBackgroundPaletteColor(highlightColor) || highlightColor;
35
- return resolveColorValue(colorValue, highlightColor);
37
+ const colorValue = palette === 'element' ? hexToEditorBackgroundPaletteColor(backgroundColor) || backgroundColor : hexToEditorTextBackgroundPaletteColor(backgroundColor) || backgroundColor;
38
+ return resolveColorValue(colorValue, backgroundColor);
39
+ };
40
+ const getBackgroundColorInNonActiveTheme = (backgroundColor, palette = 'text') => {
41
+ if (!backgroundColor || backgroundColor === 'none') {
42
+ return getTokenCSSVariableValueForNonActiveTheme("var(--ds-surface, #FFFFFF)", DEFAULT_BACKGROUND_COLOR);
43
+ }
44
+ const colorValue = palette === 'element' ? hexToEditorBackgroundPaletteColor(backgroundColor) || backgroundColor : hexToEditorTextBackgroundPaletteColor(backgroundColor) || backgroundColor;
45
+ return getTokenCSSVariableValueForNonActiveTheme(colorValue, backgroundColor);
46
+ };
47
+ const 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)]]);
48
+ const getResolvedBackgroundFromHighlight = highlightColor => {
49
+ if (!highlightColor) {
50
+ return {
51
+ color: null,
52
+ source: 'text'
53
+ };
54
+ }
55
+ return {
56
+ color: highlightColor,
57
+ source: 'text'
58
+ };
59
+ };
60
+
61
+ /**
62
+ * Resolves a background color from a container node that paints behind text.
63
+ * Callers walk ancestors so the nearest painted container wins.
64
+ */
65
+ const getSupportedAncestorBackgroundColor = node => {
66
+ let backgroundColor;
67
+ if (node.type.name === 'tableCell' || node.type.name === 'tableHeader') {
68
+ backgroundColor = node.attrs.background;
69
+ } else if (node.type.name === 'panel' || node.type.name === 'panel_c1') {
70
+ if (node.attrs.panelType === PanelType.CUSTOM) {
71
+ backgroundColor = node.attrs.panelColor;
72
+ } else {
73
+ backgroundColor = STANDARD_PANEL_BACKGROUND_COLORS.get(node.attrs.panelType);
74
+ }
75
+ }
76
+ return backgroundColor ? {
77
+ color: backgroundColor,
78
+ source: 'element'
79
+ } : null;
80
+ };
81
+
82
+ /**
83
+ * Resolves the visible background behind a text node.
84
+ * Text highlight wins before falling back to the nearest element background.
85
+ */
86
+ export const getResolvedBackgroundColorForTextNode = ($pos, node) => {
87
+ for (const mark of node.marks) {
88
+ if (mark.type.name === 'backgroundColor' && mark.attrs.color) {
89
+ return {
90
+ color: mark.attrs.color,
91
+ source: 'text'
92
+ };
93
+ }
94
+ }
95
+ for (let depth = $pos.depth; depth > 0; depth--) {
96
+ const resolvedBackgroundColor = getSupportedAncestorBackgroundColor($pos.node(depth));
97
+ if (resolvedBackgroundColor) {
98
+ return resolvedBackgroundColor;
99
+ }
100
+ }
101
+ return {
102
+ color: null,
103
+ source: 'text'
104
+ };
105
+ };
106
+ const getResolvedBackgroundColorForPosition = ($pos, highlightColor) => {
107
+ const highlightBackground = getResolvedBackgroundFromHighlight(highlightColor);
108
+ if (highlightColor) {
109
+ return highlightBackground;
110
+ }
111
+ for (let depth = $pos.depth; depth > 0; depth--) {
112
+ const resolvedBackgroundColor = getSupportedAncestorBackgroundColor($pos.node(depth));
113
+ if (resolvedBackgroundColor) {
114
+ return resolvedBackgroundColor;
115
+ }
116
+ }
117
+ return highlightBackground;
118
+ };
119
+ const getResolvedBackgroundColorValue = backgroundColor => {
120
+ if (backgroundColor.source === 'element') {
121
+ return getBackgroundColor(backgroundColor.color, 'element');
122
+ }
123
+ return getBackgroundColor(backgroundColor.color);
124
+ };
125
+ const getResolvedBackgroundColorValueInNonActiveTheme = backgroundColor => {
126
+ if (backgroundColor.source === 'element') {
127
+ return getBackgroundColorInNonActiveTheme(backgroundColor.color, 'element');
128
+ }
129
+ return getBackgroundColorInNonActiveTheme(backgroundColor.color);
36
130
  };
37
131
  const useColorAccessibilityState = api => {
38
132
  return useSharedPluginStateWithSelector(api, ['textColor', 'highlight'], states => {
39
- var _states$textColorStat, _states$highlightStat, _states$highlightStat2, _states$highlightStat3, _states$textColorStat2, _states$textColorStat3, _states$textColorStat4;
133
+ var _states$textColorStat, _states$highlightStat, _states$textColorStat2;
40
134
  return {
41
135
  defaultColor: (_states$textColorStat = states.textColorState) === null || _states$textColorStat === void 0 ? void 0 : _states$textColorStat.defaultColor,
42
136
  highlightColor: (_states$highlightStat = states.highlightState) === null || _states$highlightStat === void 0 ? void 0 : _states$highlightStat.activeColor,
43
- highlightColorInNonActiveTheme: (_states$highlightStat2 = states.highlightState) === null || _states$highlightStat2 === void 0 ? void 0 : _states$highlightStat2.activeColorInNonActiveTheme,
44
- isMultiHighlightColor: (_states$highlightStat3 = states.highlightState) === null || _states$highlightStat3 === void 0 ? void 0 : _states$highlightStat3.isMultiHighlightColor,
45
- isMultiTextColor: (_states$textColorStat2 = states.textColorState) === null || _states$textColorStat2 === void 0 ? void 0 : _states$textColorStat2.isMultiTextColor,
46
- textColor: (_states$textColorStat3 = states.textColorState) === null || _states$textColorStat3 === void 0 ? void 0 : _states$textColorStat3.color,
47
- textColorInNonActiveTheme: (_states$textColorStat4 = states.textColorState) === null || _states$textColorStat4 === void 0 ? void 0 : _states$textColorStat4.colorInNonActiveTheme
137
+ textColor: (_states$textColorStat2 = states.textColorState) === null || _states$textColorStat2 === void 0 ? void 0 : _states$textColorStat2.color
48
138
  };
49
139
  });
50
140
  };
51
- const getContrastRatio = (defaultColor, highlightColor, textColor) => {
141
+ const getContrastRatio = (defaultColor, backgroundColor, textColor) => {
52
142
  try {
53
- const contrastRatio = calcContrastRatio(getForegroundColor(textColor, defaultColor), getBackgroundColor(highlightColor));
143
+ const contrastRatio = calcContrastRatio(getForegroundColor(textColor, defaultColor), getResolvedBackgroundColorValue(backgroundColor));
54
144
  return contrastRatio;
55
145
  } catch {
56
146
  // if we failed to calculate the contrast ratio, return null
@@ -58,16 +148,10 @@ const getContrastRatio = (defaultColor, highlightColor, textColor) => {
58
148
  }
59
149
  };
60
150
 
61
- // Computes the contrast ratio for a (textColor, highlightColor) pair as it would
62
- // appear in the non-active theme, using the shared resolvers so the walker
63
- // evaluates the same colors the single-pair plugin-state path does. Returns null
64
- // if it cannot be calculated.
65
- const getNonActiveThemeContrastRatio = (defaultColor, highlightColor, textColor) => {
151
+ // Computes the contrast ratio as it would appear in the non-active theme.
152
+ const getNonActiveThemeContrastRatio = (defaultColor, backgroundColor, textColor) => {
66
153
  try {
67
- return calcContrastRatio(getTextColorInNonActiveTheme(textColor !== null && textColor !== void 0 ? textColor : null, defaultColor || DEFAULT_COLOR.color), getHighlightColorInNonActiveTheme(highlightColor !== null && highlightColor !== void 0 ? highlightColor : null, {
68
- defaultBackgroundColor: DEFAULT_BACKGROUND_COLOR,
69
- transparentColor: TRANSPARENT_HIGHLIGHT_COLOR
70
- }));
154
+ return calcContrastRatio(getTextColorInNonActiveTheme(textColor !== null && textColor !== void 0 ? textColor : null, defaultColor || DEFAULT_COLOR.color), getResolvedBackgroundColorValueInNonActiveTheme(backgroundColor));
71
155
  } catch {
72
156
  // if we failed to calculate the contrast ratio, return null
73
157
  return null;
@@ -84,87 +168,92 @@ const getAccessibilityStatus = contrastRatio => {
84
168
  };
85
169
 
86
170
  /**
87
- * Collects the worst accessibility status across all unique (textColor, highlightColor)
171
+ * Computes status from active foreground/background colors only.
172
+ * Used by the gate-off path so legacy behavior stays isolated.
173
+ */
174
+ const getAccessibilityStatusFromColorPair = (defaultColor, backgroundColor, textColor) => {
175
+ const contrastRatio = getContrastRatio(defaultColor, backgroundColor, textColor);
176
+ return contrastRatio === null ? null : getAccessibilityStatus(contrastRatio);
177
+ };
178
+
179
+ /**
180
+ * Returns the lowest contrast ratio across active and non-active themes.
181
+ */
182
+ const getMostCriticalContrastRatioAcrossThemes = (defaultColor, backgroundColor, textColor) => {
183
+ const contrastRatio = getContrastRatio(defaultColor, backgroundColor, textColor);
184
+ const nonActiveThemeContrastRatio = getNonActiveThemeContrastRatio(defaultColor, backgroundColor, textColor);
185
+ if (contrastRatio !== null && nonActiveThemeContrastRatio !== null) {
186
+ return Math.min(contrastRatio, nonActiveThemeContrastRatio);
187
+ }
188
+ return contrastRatio;
189
+ };
190
+ const getAccessibilityStatusFromColorPairAcrossThemes = (defaultColor, backgroundColor, textColor) => {
191
+ const mostCriticalContrastRatio = getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor);
192
+ return mostCriticalContrastRatio === null ? null : getAccessibilityStatus(mostCriticalContrastRatio);
193
+ };
194
+
195
+ /**
196
+ * Collects the worst accessibility status across all unique foreground/background
88
197
  * combinations in the current selection.
89
198
  *
90
- * When the selection spans multiple text colors and/or highlight colors,
91
- * this walks the document between selection boundaries and checks every
92
- * text node's color marks, computing the contrast ratio for each unique pair
93
- * (across both the active and non-active theme) and returning the worst
94
- * status found.
199
+ * When the selection spans multiple text colors and/or resolved backgrounds,
200
+ * this checks each unique pair across both the active and non-active theme and
201
+ * returns the worst status found.
95
202
  */
96
- const getWorstAccessibilityStatusFromSelection = (api, defaultColor) => {
97
- if (!(api !== null && api !== void 0 && api.core)) {
203
+ const getWorstAccessibilityStatusFromEditorState = (state, defaultColor) => {
204
+ let worstContrastRatio = null;
205
+
206
+ // Text/highlight colors only apply to a text range. If the selection is
207
+ // not a text selection (e.g. a node, cell or all selection) there is no
208
+ // meaningful range of colored text to evaluate.
209
+ if (!(state.selection instanceof TextSelection) || state.selection.empty) {
98
210
  return null;
99
211
  }
100
-
101
- // Track the lowest (worst) contrast ratio across all pairs; a lower ratio
102
- // means a worse accessibility status, so a single numeric comparison is
103
- // enough to find the worst pair.
104
- let worstContrastRatio = null;
105
- api.core.actions.execute(({
106
- tr
107
- }) => {
108
- // Text/highlight colors only apply to a text range. If the selection is
109
- // not a text selection (e.g. a node, cell or all selection) there is no
110
- // meaningful range of colored text to evaluate.
111
- if (!(tr.selection instanceof TextSelection)) {
112
- return null;
212
+ const {
213
+ from,
214
+ to
215
+ } = state.selection;
216
+ const seen = new Set();
217
+ state.doc.nodesBetween(from, to, (node, pos) => {
218
+ var _textColor, _backgroundColor$colo;
219
+ if (!node.isText) {
220
+ return !node.isLeaf;
113
221
  }
114
- const {
115
- from,
116
- to
117
- } = tr.selection;
118
- const seen = new Set();
119
- tr.doc.nodesBetween(from, to, node => {
120
- var _textColor, _highlightColor;
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
- let textColor = null;
131
- let highlightColor = null;
132
- for (const mark of node.marks) {
133
- if (mark.type.name === 'textColor') {
134
- var _mark$attrs$color;
135
- textColor = (_mark$attrs$color = mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : null;
136
- } else if (mark.type.name === 'backgroundColor') {
137
- var _mark$attrs$color2;
138
- highlightColor = (_mark$attrs$color2 = mark.attrs.color) !== null && _mark$attrs$color2 !== void 0 ? _mark$attrs$color2 : null;
139
- }
140
- }
141
- const pairKey = `${(_textColor = textColor) !== null && _textColor !== void 0 ? _textColor : 'default'}|${(_highlightColor = highlightColor) !== null && _highlightColor !== void 0 ? _highlightColor : 'default'}`;
142
- // Skip pairs we have already evaluated so the expensive contrast
143
- // calculations run at most once per unique (text, highlight) pair.
144
- if (seen.has(pairKey)) {
145
- return;
146
- }
147
- seen.add(pairKey);
148
- const contrastRatio = getContrastRatio(defaultColor, highlightColor, textColor);
149
- if (contrastRatio === null) {
150
- return;
151
- }
152
-
153
- // Account for the non-active theme so the worst-case (least
154
- // accessible) contrast across both themes is used for each pair,
155
- // keeping multi-color selections consistent with the single-pair path.
156
- const nonActiveThemeContrastRatio = getNonActiveThemeContrastRatio(defaultColor, highlightColor, textColor);
157
- const mostCriticalContrastRatio = nonActiveThemeContrastRatio !== null ? Math.min(contrastRatio, nonActiveThemeContrastRatio) : contrastRatio;
158
- if (worstContrastRatio === null || mostCriticalContrastRatio < worstContrastRatio) {
159
- worstContrastRatio = mostCriticalContrastRatio;
222
+ let textColor = null;
223
+ for (const mark of node.marks) {
224
+ if (mark.type.name === 'textColor') {
225
+ var _mark$attrs$color;
226
+ textColor = (_mark$attrs$color = mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : null;
160
227
  }
161
- });
162
-
163
- // Read-only operation — do not dispatch
164
- return null;
228
+ }
229
+ const backgroundColor = getResolvedBackgroundColorForTextNode(state.doc.resolve(pos), node);
230
+ const pairKey = `${(_textColor = textColor) !== null && _textColor !== void 0 ? _textColor : 'default'}|${backgroundColor.source}|${(_backgroundColor$colo = backgroundColor.color) !== null && _backgroundColor$colo !== void 0 ? _backgroundColor$colo : 'default'}`;
231
+ // Skip foreground/background pairs already evaluated. The source is
232
+ // part of the key because text highlights and element backgrounds use
233
+ // different editor palettes.
234
+ if (seen.has(pairKey)) {
235
+ return;
236
+ }
237
+ seen.add(pairKey);
238
+ const mostCriticalContrastRatio = getMostCriticalContrastRatioAcrossThemes(defaultColor, backgroundColor, textColor);
239
+ if (mostCriticalContrastRatio === null) {
240
+ return;
241
+ }
242
+ if (worstContrastRatio === null || mostCriticalContrastRatio < worstContrastRatio) {
243
+ worstContrastRatio = mostCriticalContrastRatio;
244
+ }
165
245
  });
166
246
  return worstContrastRatio === null ? null : getAccessibilityStatus(worstContrastRatio);
167
247
  };
248
+ export const getAccessibilityStatusForCurrentSelection = (state, defaultColor, highlightColor, textColor) => {
249
+ if (state.selection instanceof TextSelection) {
250
+ if (!state.selection.empty) {
251
+ return getWorstAccessibilityStatusFromEditorState(state, defaultColor);
252
+ }
253
+ return getAccessibilityStatusFromColorPairAcrossThemes(defaultColor, getResolvedBackgroundColorForPosition(state.selection.$from, highlightColor), textColor);
254
+ }
255
+ return null;
256
+ };
168
257
  const AccessibilityStatus = ({
169
258
  accessibilityStatus,
170
259
  formatMessage
@@ -202,54 +291,13 @@ export const ColorAccessibilityMenuItem = ({
202
291
  const {
203
292
  defaultColor,
204
293
  highlightColor,
205
- highlightColorInNonActiveTheme,
206
- isMultiHighlightColor,
207
- isMultiTextColor,
208
- textColor,
209
- textColorInNonActiveTheme
294
+ textColor
210
295
  } = useColorAccessibilityState(api);
211
- // The multi-color selection handling (walking every unique text/highlight
212
- // pair across the active and non-active theme) is only performed when the
213
- // patch gate is on. When the gate is off we fall back to the legacy behavior
214
- // of only evaluating the single active text/highlight color in the current
215
- // active theme
216
- const isMultiColorSelection = Boolean(isMultiTextColor || isMultiHighlightColor) && fg('platform_editor_lovability_text_bg_color_patch_1');
217
-
218
- // The complement of `isMultiColorSelection`: the single active text/highlight
219
- // pair is evaluated whenever we are not walking a multi-color selection. This
220
- // covers both a genuinely single-color selection (gate on) and every
221
- // selection when the gate is off (legacy behavior).
222
- const isSingleColorSelection = !isMultiColorSelection;
223
-
224
- // Memoize the selection walk so we don't re-traverse the entire ProseMirror
225
- // document on every render. It only needs to recompute when the multi-color
226
- // selection state or the inputs to the walk change (the selection flags,
227
- // default color, or the plugin api).
228
- const multiColorStatus = React.useMemo(() => isMultiColorSelection ? getWorstAccessibilityStatusFromSelection(api, defaultColor) : null,
229
- // eslint-disable-next-line react-hooks/exhaustive-deps
230
- [isMultiColorSelection, isMultiTextColor, isMultiHighlightColor, defaultColor, api]);
231
-
232
- // The single active text/highlight pair path. Everything below only applies
233
- // when we are not walking a multi-color selection
234
- let singleColorStatus = null;
235
- if (isSingleColorSelection) {
236
- const singleColorContrastRatio = getContrastRatio(defaultColor, highlightColor, textColor);
237
-
238
- // Also account for the non-active theme (gate on only) so the worst-case
239
- // (least accessible) status across both themes is shown.
240
- let nonActiveThemeSingleColorRatio = null;
241
- if (textColorInNonActiveTheme && highlightColorInNonActiveTheme && fg('platform_editor_lovability_text_bg_color_patch_1')) {
242
- try {
243
- nonActiveThemeSingleColorRatio = calcContrastRatio(textColorInNonActiveTheme, highlightColorInNonActiveTheme);
244
- } catch {
245
- // if we failed to calculate the contrast ratio, leave as null
246
- nonActiveThemeSingleColorRatio = null;
247
- }
248
- }
249
- const mostCriticalContrastRatio = singleColorContrastRatio !== null && nonActiveThemeSingleColorRatio !== null ? Math.min(singleColorContrastRatio, nonActiveThemeSingleColorRatio) : singleColorContrastRatio;
250
- singleColorStatus = mostCriticalContrastRatio !== null ? getAccessibilityStatus(mostCriticalContrastRatio) : null;
251
- }
252
- const accessibilityStatus = isSingleColorSelection ? singleColorStatus : multiColorStatus;
296
+ const {
297
+ editorView
298
+ } = useEditorToolbar();
299
+ const editorState = editorView === null || editorView === void 0 ? void 0 : editorView.state;
300
+ const accessibilityStatus = React.useMemo(() => editorState && fg('platform_editor_lovability_text_bg_color_patch_1') ? getAccessibilityStatusForCurrentSelection(editorState, defaultColor, highlightColor, textColor) : getAccessibilityStatusFromColorPair(defaultColor, getResolvedBackgroundFromHighlight(highlightColor), textColor), [defaultColor, editorState, highlightColor, textColor]);
253
301
  if (accessibilityStatus === null) {
254
302
  return /*#__PURE__*/React.createElement(React.Fragment, null);
255
303
  }
@@ -3,11 +3,10 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
- import { getTextColorInNonActiveTheme, textColorPalette, textColorPaletteNew } from '@atlaskit/editor-common/ui-color';
6
+ import { textColorPalette, textColorPaletteNew } from '@atlaskit/editor-common/ui-color';
7
7
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
8
- import { fg } from '@atlaskit/platform-feature-flags';
9
8
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
10
- import { getActiveColor, isMultiTextColorSelection } from './utils/color';
9
+ import { getActiveColor } from './utils/color';
11
10
  import { DEFAULT_COLOR } from './utils/constants';
12
11
  import { getDisabledState } from './utils/disabled';
13
12
  function createInitialPluginState(editorState, pluginConfig) {
@@ -19,16 +18,13 @@ function createInitialPluginState(editorState, pluginConfig) {
19
18
  border: "var(--ds-border, #0B120E24)"
20
19
  }].concat(_toConsumableArray(paletteColors));
21
20
  var color = getActiveColor(editorState);
22
- var state = _objectSpread({
21
+ var state = {
23
22
  color: color,
24
23
  disabled: getDisabledState(editorState),
25
24
  palette: palette,
26
25
  defaultColor: defaultColor.color,
27
26
  isPaletteOpen: false
28
- }, fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
29
- colorInNonActiveTheme: getTextColorInNonActiveTheme(color, defaultColor.color),
30
- isMultiTextColor: isMultiTextColorSelection(editorState)
31
- });
27
+ };
32
28
  return state;
33
29
  }
34
30
  export var ACTIONS = /*#__PURE__*/function (ACTIONS) {
@@ -53,9 +49,6 @@ export function createPlugin(dispatch, pluginConfig) {
53
49
  case ACTIONS.RESET_COLOR:
54
50
  nextState = _objectSpread(_objectSpread({}, pluginState), {}, {
55
51
  color: pluginState.defaultColor
56
- }, fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
57
- colorInNonActiveTheme: getTextColorInNonActiveTheme(pluginState.defaultColor, pluginState.defaultColor),
58
- isMultiTextColor: false
59
52
  });
60
53
  break;
61
54
  case ACTIONS.SET_COLOR:
@@ -63,9 +56,6 @@ export function createPlugin(dispatch, pluginConfig) {
63
56
  color: meta.color,
64
57
  disabled: false,
65
58
  isPaletteOpen: false
66
- }, fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
67
- colorInNonActiveTheme: getTextColorInNonActiveTheme(meta.color, pluginState.defaultColor),
68
- isMultiTextColor: false
69
59
  });
70
60
  break;
71
61
  case ACTIONS.DISABLE:
@@ -83,12 +73,9 @@ export function createPlugin(dispatch, pluginConfig) {
83
73
  nextState = _objectSpread(_objectSpread({}, pluginState), {}, {
84
74
  color: color,
85
75
  disabled: getDisabledState(newState)
86
- }, color !== pluginState.color && fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
87
- colorInNonActiveTheme: getTextColorInNonActiveTheme(color, pluginState.defaultColor),
88
- isMultiTextColor: isMultiTextColorSelection(newState)
89
76
  });
90
77
  }
91
- if (pluginState && pluginState.color !== nextState.color || pluginState && pluginState.colorInNonActiveTheme !== nextState.colorInNonActiveTheme || pluginState && pluginState.disabled !== nextState.disabled || pluginState && pluginState.isMultiTextColor !== nextState.isMultiTextColor || pluginState && pluginState.isPaletteOpen !== nextState.isPaletteOpen) {
78
+ if (pluginState && pluginState.color !== nextState.color || pluginState && pluginState.disabled !== nextState.disabled || pluginState && pluginState.isPaletteOpen !== nextState.isPaletteOpen) {
92
79
  dispatch(pluginKey, nextState);
93
80
  return nextState;
94
81
  }
@@ -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,