@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.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/pm-plugins/main.js +3 -16
- package/dist/cjs/pm-plugins/utils/color.js +1 -24
- package/dist/cjs/ui/ColorAccessibilityMenuItem.compiled.css +0 -1
- package/dist/cjs/ui/ColorAccessibilityMenuItem.js +213 -153
- package/dist/cjs/ui/RemoveColorMenuItem.compiled.css +0 -1
- package/dist/cjs/ui/RemoveColorMenuItem.js +1 -1
- package/dist/cjs/ui/TextColorMenuItem.js +1 -1
- package/dist/cjs/ui/TextMenuSection.compiled.css +0 -1
- package/dist/cjs/ui/TextMenuSection.js +1 -1
- package/dist/es2019/pm-plugins/main.js +7 -28
- package/dist/es2019/pm-plugins/utils/color.js +0 -27
- package/dist/es2019/ui/ColorAccessibilityMenuItem.compiled.css +0 -1
- package/dist/es2019/ui/ColorAccessibilityMenuItem.js +191 -143
- package/dist/es2019/ui/RemoveColorMenuItem.compiled.css +0 -1
- package/dist/es2019/ui/RemoveColorMenuItem.js +1 -1
- package/dist/es2019/ui/TextColorMenuItem.js +1 -1
- package/dist/es2019/ui/TextMenuSection.compiled.css +0 -1
- package/dist/es2019/ui/TextMenuSection.js +1 -1
- package/dist/esm/pm-plugins/main.js +5 -18
- package/dist/esm/pm-plugins/utils/color.js +0 -23
- package/dist/esm/ui/ColorAccessibilityMenuItem.compiled.css +0 -1
- package/dist/esm/ui/ColorAccessibilityMenuItem.js +215 -155
- package/dist/esm/ui/RemoveColorMenuItem.compiled.css +0 -1
- package/dist/esm/ui/RemoveColorMenuItem.js +1 -1
- package/dist/esm/ui/TextColorMenuItem.js +1 -1
- package/dist/esm/ui/TextMenuSection.compiled.css +0 -1
- package/dist/esm/ui/TextMenuSection.js +1 -1
- package/dist/types/pm-plugins/main.d.ts +0 -6
- package/dist/types/pm-plugins/utils/color.d.ts +0 -5
- package/dist/types/ui/ColorAccessibilityMenuItem.d.ts +15 -0
- package/package.json +5 -5
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
-
import {
|
|
2
|
+
import { textColorPalette, textColorPaletteNew } from '@atlaskit/editor-common/ui-color';
|
|
3
3
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
4
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
6
|
-
import { getActiveColor
|
|
5
|
+
import { getActiveColor } from './utils/color';
|
|
7
6
|
import { DEFAULT_COLOR } from './utils/constants';
|
|
8
7
|
import { getDisabledState } from './utils/disabled';
|
|
9
8
|
function createInitialPluginState(editorState, pluginConfig) {
|
|
@@ -20,12 +19,7 @@ function createInitialPluginState(editorState, pluginConfig) {
|
|
|
20
19
|
disabled: getDisabledState(editorState),
|
|
21
20
|
palette,
|
|
22
21
|
defaultColor: defaultColor.color,
|
|
23
|
-
isPaletteOpen: false
|
|
24
|
-
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
25
|
-
...(fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
|
|
26
|
-
colorInNonActiveTheme: getTextColorInNonActiveTheme(color, defaultColor.color),
|
|
27
|
-
isMultiTextColor: isMultiTextColorSelection(editorState)
|
|
28
|
-
})
|
|
22
|
+
isPaletteOpen: false
|
|
29
23
|
};
|
|
30
24
|
return state;
|
|
31
25
|
}
|
|
@@ -51,12 +45,7 @@ export function createPlugin(dispatch, pluginConfig) {
|
|
|
51
45
|
case ACTIONS.RESET_COLOR:
|
|
52
46
|
nextState = {
|
|
53
47
|
...pluginState,
|
|
54
|
-
color: pluginState.defaultColor
|
|
55
|
-
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
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
|
-
})
|
|
48
|
+
color: pluginState.defaultColor
|
|
60
49
|
};
|
|
61
50
|
break;
|
|
62
51
|
case ACTIONS.SET_COLOR:
|
|
@@ -64,12 +53,7 @@ export function createPlugin(dispatch, pluginConfig) {
|
|
|
64
53
|
...pluginState,
|
|
65
54
|
color: meta.color,
|
|
66
55
|
disabled: false,
|
|
67
|
-
isPaletteOpen: false
|
|
68
|
-
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
69
|
-
...(fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
|
|
70
|
-
colorInNonActiveTheme: getTextColorInNonActiveTheme(meta.color, pluginState.defaultColor),
|
|
71
|
-
isMultiTextColor: false
|
|
72
|
-
})
|
|
56
|
+
isPaletteOpen: false
|
|
73
57
|
};
|
|
74
58
|
break;
|
|
75
59
|
case ACTIONS.DISABLE:
|
|
@@ -89,15 +73,10 @@ export function createPlugin(dispatch, pluginConfig) {
|
|
|
89
73
|
nextState = {
|
|
90
74
|
...pluginState,
|
|
91
75
|
color,
|
|
92
|
-
disabled: getDisabledState(newState)
|
|
93
|
-
// eslint-disable-next-line @atlaskit/platform/no-preconditioning
|
|
94
|
-
...(color !== pluginState.color && fg('platform_editor_lovability_text_bg_color_patch_1') && expValEqualsNoExposure('platform_editor_lovability_text_bg_color', 'isEnabled', true) && {
|
|
95
|
-
colorInNonActiveTheme: getTextColorInNonActiveTheme(color, pluginState.defaultColor),
|
|
96
|
-
isMultiTextColor: isMultiTextColorSelection(newState)
|
|
97
|
-
})
|
|
76
|
+
disabled: getDisabledState(newState)
|
|
98
77
|
};
|
|
99
78
|
}
|
|
100
|
-
if (pluginState && pluginState.color !== nextState.color || pluginState && pluginState.
|
|
79
|
+
if (pluginState && pluginState.color !== nextState.color || pluginState && pluginState.disabled !== nextState.disabled || pluginState && pluginState.isPaletteOpen !== nextState.isPaletteOpen) {
|
|
101
80
|
dispatch(pluginKey, nextState);
|
|
102
81
|
return nextState;
|
|
103
82
|
}
|
|
@@ -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,
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
/* ColorAccessibilityMenuItem.tsx generated by @compiled/babel-plugin v0.
|
|
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
|
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 {
|
|
11
|
-
import {
|
|
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,
|
|
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 =
|
|
31
|
-
if (!
|
|
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(
|
|
35
|
-
return resolveColorValue(colorValue,
|
|
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$
|
|
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
|
-
|
|
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,
|
|
141
|
+
const getContrastRatio = (defaultColor, backgroundColor, textColor) => {
|
|
52
142
|
try {
|
|
53
|
-
const contrastRatio = calcContrastRatio(getForegroundColor(textColor, defaultColor),
|
|
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
|
|
62
|
-
|
|
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),
|
|
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
|
-
*
|
|
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
|
|
91
|
-
* this
|
|
92
|
-
*
|
|
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
|
|
97
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
164
|
-
|
|
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
|
-
|
|
206
|
-
isMultiHighlightColor,
|
|
207
|
-
isMultiTextColor,
|
|
208
|
-
textColor,
|
|
209
|
-
textColorInNonActiveTheme
|
|
294
|
+
textColor
|
|
210
295
|
} = useColorAccessibilityState(api);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* RemoveColorMenuItem.tsx generated by @compiled/babel-plugin v0.
|
|
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.
|
|
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';
|
|
@@ -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 {
|
|
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
|
|
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 =
|
|
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
|
-
}
|
|
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.
|
|
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
|
}
|