@atlaskit/editor-common 70.3.0 → 71.0.1
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 +50 -0
- package/dist/cjs/analytics/types/enums.js +1 -0
- package/dist/cjs/keymaps/index.js +5 -8
- package/dist/cjs/messages/insert-block.js +12 -7
- package/dist/cjs/panel.js +6 -0
- package/dist/cjs/styles/shared/code-block.js +5 -5
- package/dist/cjs/styles/shared/panel.js +54 -17
- package/dist/cjs/styles/shared/table.js +12 -7
- package/dist/cjs/styles/shared/text-color.js +1 -1
- package/dist/cjs/ui/DropList/index.js +1 -1
- package/dist/cjs/ui-color/ColorPalette/Color/index.js +6 -2
- package/dist/cjs/ui-color/ColorPalette/index.js +21 -11
- package/dist/cjs/ui-menu/Dropdown/index.js +16 -2
- package/dist/cjs/ui-menu/DropdownMenu/index.js +27 -8
- package/dist/cjs/ui-menu/MenuArrowKeyNavigationProvider/index.js +162 -0
- package/dist/cjs/ui-react/with-react-editor-view-outer-listeners.js +22 -13
- package/dist/cjs/utils/index.js +6 -0
- package/dist/cjs/utils/performance/measure-render.js +44 -23
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/analytics/types/enums.js +1 -0
- package/dist/es2019/keymaps/index.js +4 -6
- package/dist/es2019/messages/insert-block.js +12 -7
- package/dist/es2019/panel.js +1 -1
- package/dist/es2019/styles/shared/code-block.js +39 -25
- package/dist/es2019/styles/shared/panel.js +48 -18
- package/dist/es2019/styles/shared/table.js +24 -13
- package/dist/es2019/styles/shared/text-color.js +1 -1
- package/dist/es2019/ui/DropList/index.js +1 -1
- package/dist/es2019/ui-color/ColorPalette/Color/index.js +6 -3
- package/dist/es2019/ui-color/ColorPalette/index.js +19 -11
- package/dist/es2019/ui-menu/Dropdown/index.js +13 -2
- package/dist/es2019/ui-menu/DropdownMenu/index.js +41 -6
- package/dist/es2019/ui-menu/MenuArrowKeyNavigationProvider/index.js +146 -0
- package/dist/es2019/ui-react/with-react-editor-view-outer-listeners.js +20 -13
- package/dist/es2019/utils/index.js +1 -1
- package/dist/es2019/utils/performance/measure-render.js +43 -23
- package/dist/es2019/version.json +1 -1
- package/dist/esm/analytics/types/enums.js +1 -0
- package/dist/esm/keymaps/index.js +3 -6
- package/dist/esm/messages/insert-block.js +12 -7
- package/dist/esm/panel.js +1 -1
- package/dist/esm/styles/shared/code-block.js +5 -5
- package/dist/esm/styles/shared/panel.js +49 -18
- package/dist/esm/styles/shared/table.js +12 -9
- package/dist/esm/styles/shared/text-color.js +1 -1
- package/dist/esm/ui/DropList/index.js +1 -1
- package/dist/esm/ui-color/ColorPalette/Color/index.js +5 -2
- package/dist/esm/ui-color/ColorPalette/index.js +20 -11
- package/dist/esm/ui-menu/Dropdown/index.js +15 -2
- package/dist/esm/ui-menu/DropdownMenu/index.js +28 -9
- package/dist/esm/ui-menu/MenuArrowKeyNavigationProvider/index.js +147 -0
- package/dist/esm/ui-react/with-react-editor-view-outer-listeners.js +22 -13
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/performance/measure-render.js +42 -23
- package/dist/esm/version.json +1 -1
- package/dist/types/analytics/types/enums.d.ts +1 -0
- package/dist/types/analytics/types/general-events.d.ts +2 -1
- package/dist/types/keymaps/index.d.ts +0 -1
- package/dist/types/messages/insert-block.d.ts +7 -2
- package/dist/types/panel.d.ts +1 -1
- package/dist/types/styles/shared/code-block.d.ts +1 -0
- package/dist/types/styles/shared/panel.d.ts +1 -0
- package/dist/types/types/feature-flags.d.ts +27 -6
- package/dist/types/types/floating-toolbar.d.ts +4 -0
- package/dist/types/ui-color/ColorPalette/Color/index.d.ts +5 -0
- package/dist/types/ui-color/ColorPalette/index.d.ts +8 -0
- package/dist/types/ui-menu/Dropdown/index.d.ts +3 -0
- package/dist/types/ui-menu/DropdownMenu/index.d.ts +1 -0
- package/dist/types/ui-menu/DropdownMenu/types.d.ts +7 -0
- package/dist/types/ui-menu/MenuArrowKeyNavigationProvider/index.d.ts +15 -0
- package/dist/types/ui-react/with-react-editor-view-outer-listeners.d.ts +2 -0
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/performance/measure-render.d.ts +12 -0
- package/package.json +8 -7
- package/report.api.md +12 -5
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
|
|
2
|
-
// TODO: https://product-fabric.atlassian.net/browse/DSP-4066
|
|
3
1
|
import { css } from '@emotion/react';
|
|
4
2
|
import { PanelType } from '@atlaskit/adf-schema';
|
|
5
3
|
import { akEditorTableCellMinWidth, blockNodesVerticalMargin } from '@atlaskit/editor-shared-styles';
|
|
@@ -8,7 +6,19 @@ import { emojiImage, emojiSprite } from '@atlaskit/emoji';
|
|
|
8
6
|
import * as colors from '@atlaskit/theme/colors';
|
|
9
7
|
import { themed } from '@atlaskit/theme/components';
|
|
10
8
|
import { borderRadius, gridSize } from '@atlaskit/theme/constants';
|
|
9
|
+
import { token } from '@atlaskit/tokens';
|
|
10
|
+
const tokenPanelColor = {
|
|
11
|
+
info: 'color.background.information',
|
|
12
|
+
note: 'color.background.discovery',
|
|
13
|
+
tip: 'color.background.success',
|
|
14
|
+
success: 'color.background.success',
|
|
15
|
+
warning: 'color.background.warning',
|
|
16
|
+
error: 'color.background.danger'
|
|
17
|
+
};
|
|
11
18
|
const lightPanelColor = {
|
|
19
|
+
// TODO: https://product-fabric.atlassian.net/browse/DSP-4066
|
|
20
|
+
|
|
21
|
+
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
|
|
12
22
|
info: colors.B50,
|
|
13
23
|
note: colors.P50,
|
|
14
24
|
tip: colors.G50,
|
|
@@ -18,7 +28,7 @@ const lightPanelColor = {
|
|
|
18
28
|
};
|
|
19
29
|
export const darkPanelColors = {
|
|
20
30
|
// standard panels
|
|
21
|
-
info:
|
|
31
|
+
info: `#0C294F`,
|
|
22
32
|
error: `#441C13`,
|
|
23
33
|
warning: `#413001`,
|
|
24
34
|
tip: `#052E21`,
|
|
@@ -78,26 +88,31 @@ export const darkPanelColors = {
|
|
|
78
88
|
LightGray: '#5A6977',
|
|
79
89
|
TextColor: '#D9DDE3'
|
|
80
90
|
};
|
|
91
|
+
/* eslint-enable @atlaskit/design-system/ensure-design-token-usage */
|
|
92
|
+
|
|
81
93
|
const lightIconColor = {
|
|
82
|
-
info: colors.B400,
|
|
83
|
-
note: colors.P400,
|
|
84
|
-
tip: colors.G400,
|
|
85
|
-
success: colors.G400,
|
|
86
|
-
warning: colors.Y400,
|
|
87
|
-
error: colors.R400
|
|
94
|
+
info: token('color.icon.information', colors.B400),
|
|
95
|
+
note: token('color.icon.discovery', colors.P400),
|
|
96
|
+
tip: token('color.icon.success', colors.G400),
|
|
97
|
+
success: token('color.icon.success', colors.G400),
|
|
98
|
+
warning: token('color.icon.warning', colors.Y400),
|
|
99
|
+
error: token('color.icon.danger', colors.R400)
|
|
88
100
|
};
|
|
89
101
|
const darkIconColor = {
|
|
90
|
-
info: colors.B100,
|
|
91
|
-
note: colors.P100,
|
|
92
|
-
tip: colors.G200,
|
|
93
|
-
success: colors.G200,
|
|
94
|
-
warning: colors.Y100,
|
|
95
|
-
error: colors.R200
|
|
102
|
+
info: token('color.icon.information', colors.B100),
|
|
103
|
+
note: token('color.icon.discovery', colors.P100),
|
|
104
|
+
tip: token('color.icon.success', colors.G200),
|
|
105
|
+
success: token('color.icon.success', colors.G200),
|
|
106
|
+
warning: token('color.icon.warning', colors.Y100),
|
|
107
|
+
error: token('color.icon.danger', colors.R200)
|
|
96
108
|
}; // New custom icons are a little smaller than predefined icons.
|
|
97
109
|
// To fix alignment issues with custom icons, vertical alignment is updated.
|
|
98
110
|
|
|
99
111
|
const panelEmojiSpriteVerticalAlignment = -(gridSize() * 3 - akEditorCustomIconSize) / 2;
|
|
100
|
-
const panelEmojiImageVerticalAlignment = panelEmojiSpriteVerticalAlignment - 1;
|
|
112
|
+
const panelEmojiImageVerticalAlignment = panelEmojiSpriteVerticalAlignment - 1; // TODO: https://product-fabric.atlassian.net/browse/DSP-4066
|
|
113
|
+
|
|
114
|
+
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
|
|
115
|
+
|
|
101
116
|
const panelDarkModeColors = [[colors.B50, darkPanelColors.B1200S], [colors.B75, darkPanelColors.B900], [colors.B100, darkPanelColors.B800S], [colors.N0, darkPanelColors.LightGray], [colors.N20, darkPanelColors.Gray], [colors.N60, darkPanelColors.DarkGray], [colors.T50, darkPanelColors.T1200S], [colors.T75, darkPanelColors.T900], [colors.T100, darkPanelColors.T900S], [colors.G50, darkPanelColors.G1200S], [colors.G75, darkPanelColors.G900], [colors.G200, darkPanelColors.G900S], [colors.Y50, darkPanelColors.Y1200S], [colors.Y75, darkPanelColors.Y900], [colors.Y200, darkPanelColors.Y800S], [colors.R50, darkPanelColors.R1200S], [colors.R75, darkPanelColors.R900], [colors.R100, darkPanelColors.R800S], [colors.P50, darkPanelColors.P1200S], [colors.P75, darkPanelColors.P900], [colors.P100, darkPanelColors.P800S]];
|
|
102
117
|
export const getPanelDarkColor = panelColor => {
|
|
103
118
|
const colorObject = panelDarkModeColors.find(color => color[0] === panelColor || color[1] === panelColor);
|
|
@@ -152,9 +167,10 @@ const iconDynamicStyles = panelType => props => {
|
|
|
152
167
|
return `
|
|
153
168
|
color: ${color};
|
|
154
169
|
`;
|
|
155
|
-
};
|
|
170
|
+
}; // Provides the color without tokens, used when converting to a custom panel
|
|
156
171
|
|
|
157
|
-
|
|
172
|
+
|
|
173
|
+
export const getPanelTypeBackgroundNoTokens = (panelType, props = {}) => {
|
|
158
174
|
const light = lightPanelColor[panelType];
|
|
159
175
|
const dark = darkPanelColors[panelType];
|
|
160
176
|
const background = themed({
|
|
@@ -163,6 +179,20 @@ export const getPanelTypeBackground = (panelType, props = {}) => {
|
|
|
163
179
|
})(props);
|
|
164
180
|
return background || 'none';
|
|
165
181
|
};
|
|
182
|
+
export const getPanelTypeBackground = (panelType, props = {}) => {
|
|
183
|
+
// TODO: https://product-fabric.atlassian.net/browse/DSP-4066
|
|
184
|
+
|
|
185
|
+
/* eslint-disable @atlaskit/design-system/no-unsafe-design-token-usage */
|
|
186
|
+
const light = token(tokenPanelColor[panelType], lightPanelColor[panelType]);
|
|
187
|
+
const dark = token(tokenPanelColor[panelType], darkPanelColors[panelType]);
|
|
188
|
+
/* eslint-disable @atlaskit/design-system/no-unsafe-design-token-usage */
|
|
189
|
+
|
|
190
|
+
const background = themed({
|
|
191
|
+
light,
|
|
192
|
+
dark
|
|
193
|
+
})(props);
|
|
194
|
+
return background || 'none';
|
|
195
|
+
};
|
|
166
196
|
|
|
167
197
|
const mainDynamicStyles = panelType => props => {
|
|
168
198
|
const background = getPanelTypeBackground(panelType, props);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
|
|
2
2
|
// TODO: https://product-fabric.atlassian.net/browse/DSP-4118
|
|
3
|
-
// TODO: https://product-fabric.atlassian.net/browse/DSP-4153
|
|
4
3
|
import { css } from '@emotion/react';
|
|
5
4
|
import { tableCellContentDomSelector, tableCellSelector, tableHeaderSelector, tablePrefixSelector } from '@atlaskit/adf-schema';
|
|
6
|
-
import { akEditorBreakoutPadding, akEditorFullWidthLayoutWidth, akEditorTableBorder, akEditorTableBorderDark, akEditorTableNumberColumnWidth, akEditorTableToolbar, akEditorTableToolbarDark, akEditorWideLayoutWidth, getTableCellBackgroundDarkModeColors, overflowShadow } from '@atlaskit/editor-shared-styles';
|
|
5
|
+
import { akEditorBreakoutPadding, akEditorFullWidthLayoutWidth, akEditorSelectedNodeClassName, akEditorTableBorder, akEditorTableBorderDark, akEditorTableNumberColumnWidth, akEditorTableToolbar, akEditorTableToolbarDark, akEditorWideLayoutWidth, getTableCellBackgroundDarkModeColors, overflowShadow } from '@atlaskit/editor-shared-styles';
|
|
7
6
|
import { DN20 } from '@atlaskit/theme/colors';
|
|
8
7
|
import { themed } from '@atlaskit/theme/components';
|
|
9
8
|
import { gridSize } from '@atlaskit/theme/constants';
|
|
10
9
|
import { token } from '@atlaskit/tokens';
|
|
11
10
|
import browser from '../../utils/browser';
|
|
11
|
+
import { CodeBlockSharedCssClassName } from './code-block';
|
|
12
12
|
export const tableMarginTop = 24;
|
|
13
13
|
export const tableMarginBottom = 16;
|
|
14
14
|
export const tableMarginSides = 8;
|
|
@@ -140,23 +140,34 @@ const tableSharedStyle = props => css`
|
|
|
140
140
|
|
|
141
141
|
/* only apply this styling to codeblocks in default background headercells */
|
|
142
142
|
/* TODO this needs to be overhauled as it relies on unsafe selectors */
|
|
143
|
-
&:not([style]) {
|
|
144
|
-
.
|
|
145
|
-
background-
|
|
143
|
+
&:not([style]):not(.danger) {
|
|
144
|
+
.${CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER}:not(.danger) {
|
|
145
|
+
background-color: ${themed({
|
|
146
|
+
light: token('elevation.surface.raised', 'rgb(235, 237, 240)'),
|
|
147
|
+
dark: token('elevation.surface.raised', 'rgb(36, 47, 66)')
|
|
148
|
+
})(props)};
|
|
149
|
+
|
|
150
|
+
:not(.${akEditorSelectedNodeClassName}) {
|
|
151
|
+
box-shadow: 0px 0px 0px 1px
|
|
152
|
+
${token('color.border', 'transparent')};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.${CodeBlockSharedCssClassName.CODEBLOCK_CONTENT_WRAPPER} {
|
|
156
|
+
background-image: ${overflowShadow({
|
|
146
157
|
background: themed({
|
|
147
|
-
light: 'rgb(235, 237, 240)',
|
|
148
|
-
dark: 'rgb(36, 47, 66)'
|
|
158
|
+
light: token('color.background.neutral', 'rgb(235, 237, 240)'),
|
|
159
|
+
dark: token('color.background.neutral', 'rgb(36, 47, 66)')
|
|
149
160
|
})(props),
|
|
150
161
|
width: `${gridSize()}px`
|
|
151
162
|
})};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
background-color: ${themed({
|
|
163
|
+
|
|
164
|
+
background-color: ${themed({
|
|
155
165
|
light: token('color.background.neutral', 'rgb(235, 237, 240)'),
|
|
156
166
|
dark: token('color.background.neutral', 'rgb(36, 47, 66)')
|
|
157
167
|
})(props)};
|
|
168
|
+
}
|
|
158
169
|
|
|
159
|
-
.
|
|
170
|
+
.${CodeBlockSharedCssClassName.CODEBLOCK_LINE_NUMBER_GUTTER} {
|
|
160
171
|
background-color: ${themed({
|
|
161
172
|
light: token('color.background.neutral', 'rgb(226, 229, 233)'),
|
|
162
173
|
dark: token('color.background.neutral', DN20)
|
|
@@ -167,8 +178,8 @@ const tableSharedStyle = props => css`
|
|
|
167
178
|
> [data-ds--code--code-block] {
|
|
168
179
|
background-image: ${overflowShadow({
|
|
169
180
|
background: themed({
|
|
170
|
-
light: 'rgb(235, 237, 240)',
|
|
171
|
-
dark: 'rgb(36, 47, 66)'
|
|
181
|
+
light: token('color.background.neutral', 'rgb(235, 237, 240)'),
|
|
182
|
+
dark: token('color.background.neutral', 'rgb(36, 47, 66)')
|
|
172
183
|
})(props),
|
|
173
184
|
width: `${gridSize()}px`
|
|
174
185
|
})}!important;
|
|
@@ -10,7 +10,7 @@ import { borderRadius, gridSize } from '@atlaskit/theme/constants';
|
|
|
10
10
|
import { token } from '@atlaskit/tokens';
|
|
11
11
|
import Layer from '../Layer';
|
|
12
12
|
const packageName = "@atlaskit/editor-common";
|
|
13
|
-
const packageVersion = "
|
|
13
|
+
const packageVersion = "71.0.1";
|
|
14
14
|
const halfFocusRing = 1;
|
|
15
15
|
const dropOffset = `0, ${gridSize()}px`;
|
|
16
16
|
|
|
@@ -3,6 +3,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
3
3
|
/** @jsx jsx */
|
|
4
4
|
import React, { PureComponent } from 'react';
|
|
5
5
|
import { jsx } from '@emotion/react';
|
|
6
|
+
import { hexToEditorTextPaletteColor } from '@atlaskit/editor-palette';
|
|
6
7
|
import EditorDoneIcon from '@atlaskit/icon/glyph/editor/done';
|
|
7
8
|
import { N0 } from '@atlaskit/theme/colors';
|
|
8
9
|
import Tooltip from '@atlaskit/tooltip';
|
|
@@ -39,10 +40,12 @@ class Color extends PureComponent {
|
|
|
39
40
|
/** this is not new usage - old code extracted from editor-core */
|
|
40
41
|
|
|
41
42
|
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
|
|
42
|
-
checkMarkColor = N0
|
|
43
|
-
/* eslint-enable @atlaskit/design-system/ensure-design-token-usage */
|
|
43
|
+
checkMarkColor = N0,
|
|
44
44
|
|
|
45
|
+
/* eslint-enable @atlaskit/design-system/ensure-design-token-usage */
|
|
46
|
+
useDesignTokens
|
|
45
47
|
} = this.props;
|
|
48
|
+
const colorStyle = useDesignTokens ? hexToEditorTextPaletteColor(value) : value;
|
|
46
49
|
return jsx(Tooltip, {
|
|
47
50
|
content: label
|
|
48
51
|
}, jsx("span", {
|
|
@@ -57,7 +60,7 @@ class Color extends PureComponent {
|
|
|
57
60
|
tabIndex: tabIndex,
|
|
58
61
|
className: `${isSelected ? 'selected' : ''}`,
|
|
59
62
|
style: {
|
|
60
|
-
backgroundColor:
|
|
63
|
+
backgroundColor: colorStyle || 'transparent',
|
|
61
64
|
border: `1px solid ${borderColor}`
|
|
62
65
|
},
|
|
63
66
|
autoFocus: autoFocus
|
|
@@ -4,6 +4,7 @@ import { jsx } from '@emotion/react';
|
|
|
4
4
|
import chromatism from 'chromatism';
|
|
5
5
|
import { injectIntl } from 'react-intl-next';
|
|
6
6
|
import { N0, N500 } from '@atlaskit/theme/colors';
|
|
7
|
+
import { token } from '@atlaskit/tokens';
|
|
7
8
|
import Color from './Color';
|
|
8
9
|
import { colorPaletteWrapper } from './styles';
|
|
9
10
|
|
|
@@ -14,8 +15,19 @@ import { colorPaletteWrapper } from './styles';
|
|
|
14
15
|
* @param color color string, suppports HEX, RGB, RGBA etc.
|
|
15
16
|
* @return Highest contrast color in pool
|
|
16
17
|
*/
|
|
17
|
-
function
|
|
18
|
-
|
|
18
|
+
function getCheckMarkColor(color, textPalette) {
|
|
19
|
+
// eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage
|
|
20
|
+
const contrastColor = [N0, N500].sort((a, b) => chromatism.difference(b, color) - chromatism.difference(a, color))[0];
|
|
21
|
+
|
|
22
|
+
if (!textPalette) {
|
|
23
|
+
return contrastColor;
|
|
24
|
+
} // Use of these token comes from guidance from designers in the Design System team
|
|
25
|
+
// they are only intended for use with text colors (and there are different tokens
|
|
26
|
+
// planned to be used when this extended to be used with other palettes).
|
|
27
|
+
// eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
return contrastColor === N0 ? token('color.icon.inverse', N0) : token('color.icon', N500);
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
const ColorPalette = props => {
|
|
@@ -27,7 +39,8 @@ const ColorPalette = props => {
|
|
|
27
39
|
className,
|
|
28
40
|
intl: {
|
|
29
41
|
formatMessage
|
|
30
|
-
}
|
|
42
|
+
},
|
|
43
|
+
textPalette = false
|
|
31
44
|
} = props;
|
|
32
45
|
const colorsPerRow = React.useMemo(() => {
|
|
33
46
|
return palette.reduce((resultArray, item, index) => {
|
|
@@ -54,14 +67,9 @@ const ColorPalette = props => {
|
|
|
54
67
|
borderColor: border,
|
|
55
68
|
label: message ? formatMessage(message) : label,
|
|
56
69
|
onClick: onClick,
|
|
57
|
-
isSelected: value === selectedColor
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
|
|
61
|
-
,
|
|
62
|
-
checkMarkColor: getContrastColor(value, [N0, N500])
|
|
63
|
-
/* eslint-enable @atlaskit/design-system/ensure-design-token-usage */
|
|
64
|
-
|
|
70
|
+
isSelected: value === selectedColor,
|
|
71
|
+
checkMarkColor: getCheckMarkColor(value, textPalette),
|
|
72
|
+
useDesignTokens: textPalette === true
|
|
65
73
|
})))));
|
|
66
74
|
};
|
|
67
75
|
|
|
@@ -3,6 +3,7 @@ import React, { PureComponent } from 'react';
|
|
|
3
3
|
import { withReactEditorViewOuterListeners } from '../../ui-react';
|
|
4
4
|
import DropdownList from '../../ui/DropList';
|
|
5
5
|
import Popup from '../../ui/Popup';
|
|
6
|
+
import { MenuArrowKeyNavigationProvider } from '../MenuArrowKeyNavigationProvider';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Wrapper around @atlaskit/droplist which uses Popup and Portal to render
|
|
@@ -44,7 +45,9 @@ export class Dropdown extends PureComponent {
|
|
|
44
45
|
onOpenChange,
|
|
45
46
|
fitHeight,
|
|
46
47
|
fitWidth,
|
|
47
|
-
zIndex
|
|
48
|
+
zIndex,
|
|
49
|
+
disableArrowKeyNavigation,
|
|
50
|
+
keyDownHandlerContext
|
|
48
51
|
} = this.props;
|
|
49
52
|
return /*#__PURE__*/React.createElement(Popup, {
|
|
50
53
|
target: target,
|
|
@@ -55,6 +58,14 @@ export class Dropdown extends PureComponent {
|
|
|
55
58
|
fitHeight: fitHeight,
|
|
56
59
|
fitWidth: fitWidth,
|
|
57
60
|
zIndex: zIndex
|
|
61
|
+
}, /*#__PURE__*/React.createElement(MenuArrowKeyNavigationProvider, {
|
|
62
|
+
disableArrowKeyNavigation: disableArrowKeyNavigation,
|
|
63
|
+
keyDownHandlerContext: keyDownHandlerContext,
|
|
64
|
+
closeonTab: true,
|
|
65
|
+
handleClose: event => onOpenChange && onOpenChange({
|
|
66
|
+
isOpen: false,
|
|
67
|
+
event
|
|
68
|
+
})
|
|
58
69
|
}, /*#__PURE__*/React.createElement("div", {
|
|
59
70
|
style: {
|
|
60
71
|
height: 0,
|
|
@@ -65,7 +76,7 @@ export class Dropdown extends PureComponent {
|
|
|
65
76
|
onOpenChange: onOpenChange,
|
|
66
77
|
position: popupPlacement.join(' '),
|
|
67
78
|
shouldFitContainer: true
|
|
68
|
-
}, children)));
|
|
79
|
+
}, children))));
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
render() {
|
|
@@ -6,19 +6,24 @@ import React, { PureComponent } from 'react';
|
|
|
6
6
|
import { css, jsx } from '@emotion/react';
|
|
7
7
|
import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
|
|
8
8
|
import { CustomItem, MenuGroup } from '@atlaskit/menu';
|
|
9
|
-
import { DN600, DN80, N70, N900 } from '@atlaskit/theme/colors';
|
|
9
|
+
import { B100, DN600, DN80, N70, N900 } from '@atlaskit/theme/colors';
|
|
10
10
|
import { themed } from '@atlaskit/theme/components';
|
|
11
11
|
import { token } from '@atlaskit/tokens';
|
|
12
12
|
import Tooltip from '@atlaskit/tooltip';
|
|
13
13
|
import { withReactEditorViewOuterListeners } from '../../ui-react';
|
|
14
14
|
import DropList from '../../ui/DropList';
|
|
15
15
|
import Popup from '../../ui/Popup';
|
|
16
|
+
import { MenuArrowKeyNavigationProvider } from '../MenuArrowKeyNavigationProvider';
|
|
16
17
|
const wrapper = css`
|
|
17
18
|
/* tooltip in ToolbarButton is display:block */
|
|
18
19
|
& > div > div {
|
|
19
20
|
display: flex;
|
|
20
21
|
}
|
|
21
22
|
`;
|
|
23
|
+
const focusedMenuItemStyle = css`
|
|
24
|
+
box-shadow: inset 0px 0px 0px 2px ${token('color.border.focused', B100)};
|
|
25
|
+
outline: none;
|
|
26
|
+
`;
|
|
22
27
|
|
|
23
28
|
const buttonStyles = isActive => theme => {
|
|
24
29
|
if (isActive) {
|
|
@@ -32,6 +37,13 @@ const buttonStyles = isActive => theme => {
|
|
|
32
37
|
background: ${token('color.background.selected', '#6c798f')};
|
|
33
38
|
color: ${token('color.text', '#fff')};
|
|
34
39
|
}
|
|
40
|
+
:focus > span[aria-disabled='false'] {
|
|
41
|
+
${focusedMenuItemStyle};
|
|
42
|
+
}
|
|
43
|
+
:focus-visible,
|
|
44
|
+
:focus-visible > span[aria-disabled='false'] {
|
|
45
|
+
outline: none;
|
|
46
|
+
}
|
|
35
47
|
`;
|
|
36
48
|
} else {
|
|
37
49
|
return css`
|
|
@@ -57,7 +69,14 @@ const buttonStyles = isActive => theme => {
|
|
|
57
69
|
dark: token('color.text.disabled', DN80)
|
|
58
70
|
})(theme)};
|
|
59
71
|
}
|
|
60
|
-
|
|
72
|
+
:focus > span[aria-disabled='false'] {
|
|
73
|
+
${focusedMenuItemStyle};
|
|
74
|
+
}
|
|
75
|
+
:focus-visible,
|
|
76
|
+
:focus-visible > span[aria-disabled='false'] {
|
|
77
|
+
outline: none;
|
|
78
|
+
}
|
|
79
|
+
`; // The deafut focus-visible style is removed to ensure consistency across browsers
|
|
61
80
|
}
|
|
62
81
|
};
|
|
63
82
|
|
|
@@ -95,6 +114,13 @@ export default class DropdownMenuWrapper extends PureComponent {
|
|
|
95
114
|
}
|
|
96
115
|
});
|
|
97
116
|
|
|
117
|
+
_defineProperty(this, "handleCloseandFocus", () => {
|
|
118
|
+
var _this$state$target, _this$state$target$qu;
|
|
119
|
+
|
|
120
|
+
this.handleClose();
|
|
121
|
+
(_this$state$target = this.state.target) === null || _this$state$target === void 0 ? void 0 : (_this$state$target$qu = _this$state$target.querySelector('button')) === null || _this$state$target$qu === void 0 ? void 0 : _this$state$target$qu.focus();
|
|
122
|
+
});
|
|
123
|
+
|
|
98
124
|
_defineProperty(this, "handleClose", () => {
|
|
99
125
|
if (this.props.onOpenChange) {
|
|
100
126
|
this.props.onOpenChange({
|
|
@@ -119,7 +145,9 @@ export default class DropdownMenuWrapper extends PureComponent {
|
|
|
119
145
|
fitWidth,
|
|
120
146
|
isOpen,
|
|
121
147
|
zIndex,
|
|
122
|
-
shouldUseDefaultRole
|
|
148
|
+
shouldUseDefaultRole,
|
|
149
|
+
disableArrowKeyNavigation,
|
|
150
|
+
keyDownHandlerContext
|
|
123
151
|
} = this.props;
|
|
124
152
|
return jsx(Popup, {
|
|
125
153
|
target: isOpen ? target : undefined,
|
|
@@ -131,6 +159,11 @@ export default class DropdownMenuWrapper extends PureComponent {
|
|
|
131
159
|
fitWidth: fitWidth,
|
|
132
160
|
zIndex: zIndex || akEditorFloatingPanelZIndex,
|
|
133
161
|
offset: offset
|
|
162
|
+
}, jsx(MenuArrowKeyNavigationProvider, {
|
|
163
|
+
disableArrowKeyNavigation: disableArrowKeyNavigation,
|
|
164
|
+
handleClose: this.handleCloseandFocus,
|
|
165
|
+
keyDownHandlerContext: keyDownHandlerContext,
|
|
166
|
+
closeonTab: true
|
|
134
167
|
}, jsx(DropListWithOutsideListeners, {
|
|
135
168
|
isOpen: true,
|
|
136
169
|
appearance: "tall",
|
|
@@ -139,7 +172,8 @@ export default class DropdownMenuWrapper extends PureComponent {
|
|
|
139
172
|
shouldFitContainer: true,
|
|
140
173
|
isTriggerNotTabbable: true,
|
|
141
174
|
handleClickOutside: this.handleClose,
|
|
142
|
-
handleEscapeKeydown: this.
|
|
175
|
+
handleEscapeKeydown: this.handleCloseandFocus,
|
|
176
|
+
targetRef: this.state.target
|
|
143
177
|
}, jsx("div", {
|
|
144
178
|
style: {
|
|
145
179
|
height: 0,
|
|
@@ -159,7 +193,7 @@ export default class DropdownMenuWrapper extends PureComponent {
|
|
|
159
193
|
onMouseEnter: this.props.onMouseEnter,
|
|
160
194
|
onMouseLeave: this.props.onMouseLeave
|
|
161
195
|
});
|
|
162
|
-
})))));
|
|
196
|
+
}))))));
|
|
163
197
|
}
|
|
164
198
|
|
|
165
199
|
render() {
|
|
@@ -205,7 +239,8 @@ function DropdownMenuItem({
|
|
|
205
239
|
const dropListItem = jsx("div", {
|
|
206
240
|
css: theme => buttonStyles(item.isActive)({
|
|
207
241
|
theme
|
|
208
|
-
})
|
|
242
|
+
}),
|
|
243
|
+
tabIndex: -1
|
|
209
244
|
}, jsx(CustomItem, {
|
|
210
245
|
item: item,
|
|
211
246
|
key: (_item$key2 = item.key) !== null && _item$key2 !== void 0 ? _item$key2 : String(item.content),
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import React, { useLayoutEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This component is a wrapper of vertical menus which listens to keydown events of children
|
|
5
|
+
* and handles up/down arrow key navigation
|
|
6
|
+
*/
|
|
7
|
+
export const MenuArrowKeyNavigationProvider = ({
|
|
8
|
+
children,
|
|
9
|
+
handleClose,
|
|
10
|
+
disableArrowKeyNavigation,
|
|
11
|
+
keyDownHandlerContext,
|
|
12
|
+
closeonTab
|
|
13
|
+
}) => {
|
|
14
|
+
const wrapperRef = useRef(null);
|
|
15
|
+
const currentSelectedItemIndex = useRef(-1);
|
|
16
|
+
|
|
17
|
+
const incrementIndex = list => {
|
|
18
|
+
if (currentSelectedItemIndex.current === list.length - 1 || currentSelectedItemIndex.current === -1) {
|
|
19
|
+
currentSelectedItemIndex.current = 0;
|
|
20
|
+
} else {
|
|
21
|
+
currentSelectedItemIndex.current = currentSelectedItemIndex.current + 1;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const decrementIndex = list => {
|
|
26
|
+
if (currentSelectedItemIndex.current === 0 || currentSelectedItemIndex.current === -1) {
|
|
27
|
+
currentSelectedItemIndex.current = list.length - 1;
|
|
28
|
+
} else {
|
|
29
|
+
currentSelectedItemIndex.current = currentSelectedItemIndex.current - 1;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
useLayoutEffect(() => {
|
|
34
|
+
if (disableArrowKeyNavigation) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* To handle the key events on the list
|
|
39
|
+
* @param event
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const handleKeyDown = event => {
|
|
44
|
+
var _wrapperRef$current, _focusableElements$cu, _focusableElements$cu2;
|
|
45
|
+
|
|
46
|
+
const targetElement = event.target; //Tab key on menu items can be handled in the parent components of dropdown menus with KeydownHandlerContext
|
|
47
|
+
|
|
48
|
+
if (event.key === 'Tab' && closeonTab) {
|
|
49
|
+
handleClose(event);
|
|
50
|
+
keyDownHandlerContext === null || keyDownHandlerContext === void 0 ? void 0 : keyDownHandlerContext.handleTab();
|
|
51
|
+
return;
|
|
52
|
+
} //To trap the focus inside the toolbar using left and right arrow keys
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const focusableElements = getEnabledElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
56
|
+
|
|
57
|
+
if (!focusableElements || (focusableElements === null || focusableElements === void 0 ? void 0 : focusableElements.length) === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!((_wrapperRef$current = wrapperRef.current) !== null && _wrapperRef$current !== void 0 && _wrapperRef$current.contains(targetElement))) {
|
|
62
|
+
currentSelectedItemIndex.current = -1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
switch (event.key) {
|
|
66
|
+
case 'ArrowDown':
|
|
67
|
+
//If ArrowDown pressed
|
|
68
|
+
//If on last item
|
|
69
|
+
// Focus last item
|
|
70
|
+
//Else
|
|
71
|
+
// Focus next item
|
|
72
|
+
incrementIndex(focusableElements);
|
|
73
|
+
(_focusableElements$cu = focusableElements[currentSelectedItemIndex.current]) === null || _focusableElements$cu === void 0 ? void 0 : _focusableElements$cu.focus();
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'ArrowUp':
|
|
78
|
+
//ArrowUP pressed
|
|
79
|
+
//If on First item
|
|
80
|
+
// Focus last item
|
|
81
|
+
//Else
|
|
82
|
+
// Focus previous item
|
|
83
|
+
decrementIndex(focusableElements);
|
|
84
|
+
(_focusableElements$cu2 = focusableElements[currentSelectedItemIndex.current]) === null || _focusableElements$cu2 === void 0 ? void 0 : _focusableElements$cu2.focus();
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
break;
|
|
87
|
+
//ArrowLeft/Right on the menu should close the menus
|
|
88
|
+
//then logic to retain the focus can be handled in the parent components with KeydownHandlerContext
|
|
89
|
+
|
|
90
|
+
case 'ArrowLeft':
|
|
91
|
+
//Filter out the events from outside the menu
|
|
92
|
+
if (!targetElement.closest('.custom-key-handler-wrapper')) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
handleClose(event);
|
|
97
|
+
keyDownHandlerContext === null || keyDownHandlerContext === void 0 ? void 0 : keyDownHandlerContext.handleArrowLeft();
|
|
98
|
+
break;
|
|
99
|
+
|
|
100
|
+
case 'ArrowRight':
|
|
101
|
+
//Filter out the events from outside the menu
|
|
102
|
+
if (!targetElement.closest('.custom-key-handler-wrapper')) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
handleClose(event);
|
|
107
|
+
keyDownHandlerContext === null || keyDownHandlerContext === void 0 ? void 0 : keyDownHandlerContext.handleArrowRight();
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
case 'Escape':
|
|
111
|
+
handleClose(event);
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
default:
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
120
|
+
return () => {
|
|
121
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
122
|
+
};
|
|
123
|
+
}, [currentSelectedItemIndex, wrapperRef, handleClose, disableArrowKeyNavigation, keyDownHandlerContext, closeonTab]);
|
|
124
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
125
|
+
className: "custom-key-handler-wrapper",
|
|
126
|
+
ref: wrapperRef
|
|
127
|
+
}, children);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
function getFocusableElements(rootNode) {
|
|
131
|
+
if (!rootNode) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const focusableModalElements = rootNode.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, div[tabindex="-1"]') || [];
|
|
136
|
+
return Array.from(focusableModalElements);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* This method filters out all the disabled menu items
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
function getEnabledElements(rootNode) {
|
|
144
|
+
const focusableElements = getFocusableElements(rootNode);
|
|
145
|
+
return focusableElements.filter(element => !element.querySelector('[role="button"][aria-disabled="true"]') && !element.querySelector('[role="menuitem"][aria-disabled="true"]'));
|
|
146
|
+
}
|