@atlaskit/editor-common 74.57.0 → 74.58.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 +13 -0
- package/dist/cjs/element-browser/ElementBrowser.js +152 -0
- package/dist/cjs/element-browser/ViewMore.js +39 -0
- package/dist/cjs/element-browser/components/CategoryList.js +115 -0
- package/dist/cjs/element-browser/components/ElementBrowserLoader.js +36 -0
- package/dist/cjs/element-browser/components/ElementList/ElementList.js +256 -0
- package/dist/cjs/element-browser/components/ElementList/EmptyState.js +47 -0
- package/dist/cjs/element-browser/components/ElementList/NotFoundIllustration.js +70 -0
- package/dist/cjs/element-browser/components/ElementList/cellSizeAndPositionGetter.js +42 -0
- package/dist/cjs/element-browser/components/ElementList/utils.js +54 -0
- package/dist/cjs/element-browser/components/ElementSearch.js +88 -0
- package/dist/cjs/element-browser/components/StatelessElementBrowser.js +269 -0
- package/dist/cjs/element-browser/constants.js +41 -0
- package/dist/cjs/element-browser/hooks/use-container-width.js +70 -0
- package/dist/cjs/element-browser/hooks/use-focus.js +51 -0
- package/dist/cjs/element-browser/hooks/use-select-and-focus-on-arrow-navigation.js +286 -0
- package/dist/cjs/element-browser/index.js +20 -0
- package/dist/cjs/element-browser/types.js +12 -0
- package/dist/cjs/monitoring/error.js +1 -1
- package/dist/cjs/ui/DropList/index.js +1 -1
- package/dist/cjs/utils/performance/measure-render.js +4 -3
- package/dist/cjs/utils/performance/measure-tti.js +9 -3
- package/dist/cjs/utils/validator.js +7 -0
- package/dist/es2019/element-browser/ElementBrowser.js +117 -0
- package/dist/es2019/element-browser/ViewMore.js +40 -0
- package/dist/es2019/element-browser/components/CategoryList.js +106 -0
- package/dist/es2019/element-browser/components/ElementBrowserLoader.js +21 -0
- package/dist/es2019/element-browser/components/ElementList/ElementList.js +320 -0
- package/dist/es2019/element-browser/components/ElementList/EmptyState.js +58 -0
- package/dist/es2019/element-browser/components/ElementList/NotFoundIllustration.js +65 -0
- package/dist/es2019/element-browser/components/ElementList/cellSizeAndPositionGetter.js +39 -0
- package/dist/es2019/element-browser/components/ElementList/utils.js +50 -0
- package/dist/es2019/element-browser/components/ElementSearch.js +117 -0
- package/dist/es2019/element-browser/components/StatelessElementBrowser.js +339 -0
- package/dist/es2019/element-browser/constants.js +23 -0
- package/dist/es2019/element-browser/hooks/use-container-width.js +59 -0
- package/dist/es2019/element-browser/hooks/use-focus.js +48 -0
- package/dist/es2019/element-browser/hooks/use-select-and-focus-on-arrow-navigation.js +284 -0
- package/dist/es2019/element-browser/index.js +2 -0
- package/dist/es2019/element-browser/types.js +5 -0
- package/dist/es2019/monitoring/error.js +1 -1
- package/dist/es2019/ui/DropList/index.js +1 -1
- package/dist/es2019/utils/performance/measure-render.js +2 -3
- package/dist/es2019/utils/performance/measure-tti.js +9 -3
- package/dist/es2019/utils/validator.js +7 -0
- package/dist/esm/element-browser/ElementBrowser.js +142 -0
- package/dist/esm/element-browser/ViewMore.js +31 -0
- package/dist/esm/element-browser/components/CategoryList.js +105 -0
- package/dist/esm/element-browser/components/ElementBrowserLoader.js +23 -0
- package/dist/esm/element-browser/components/ElementList/ElementList.js +241 -0
- package/dist/esm/element-browser/components/ElementList/EmptyState.js +40 -0
- package/dist/esm/element-browser/components/ElementList/NotFoundIllustration.js +63 -0
- package/dist/esm/element-browser/components/ElementList/cellSizeAndPositionGetter.js +37 -0
- package/dist/esm/element-browser/components/ElementList/utils.js +46 -0
- package/dist/esm/element-browser/components/ElementSearch.js +77 -0
- package/dist/esm/element-browser/components/StatelessElementBrowser.js +258 -0
- package/dist/esm/element-browser/constants.js +23 -0
- package/dist/esm/element-browser/hooks/use-container-width.js +61 -0
- package/dist/esm/element-browser/hooks/use-focus.js +46 -0
- package/dist/esm/element-browser/hooks/use-select-and-focus-on-arrow-navigation.js +278 -0
- package/dist/esm/element-browser/index.js +2 -0
- package/dist/esm/element-browser/types.js +5 -0
- package/dist/esm/monitoring/error.js +1 -1
- package/dist/esm/ui/DropList/index.js +1 -1
- package/dist/esm/utils/performance/measure-render.js +2 -3
- package/dist/esm/utils/performance/measure-tti.js +9 -3
- package/dist/esm/utils/validator.js +7 -0
- package/dist/types/analytics/types/general-events.d.ts +2 -1
- package/dist/types/element-browser/ElementBrowser.d.ts +37 -0
- package/dist/types/element-browser/ViewMore.d.ts +6 -0
- package/dist/types/element-browser/components/CategoryList.d.ts +10 -0
- package/dist/types/element-browser/components/ElementBrowserLoader.d.ts +6 -0
- package/dist/types/element-browser/components/ElementList/ElementList.d.ts +33 -0
- package/dist/types/element-browser/components/ElementList/EmptyState.d.ts +6 -0
- package/dist/types/element-browser/components/ElementList/NotFoundIllustration.d.ts +2 -0
- package/dist/types/element-browser/components/ElementList/cellSizeAndPositionGetter.d.ts +7 -0
- package/dist/types/element-browser/components/ElementList/utils.d.ts +12 -0
- package/dist/types/element-browser/components/ElementSearch.d.ts +18 -0
- package/dist/types/element-browser/components/StatelessElementBrowser.d.ts +23 -0
- package/dist/types/element-browser/constants.d.ts +19 -0
- package/dist/types/element-browser/hooks/use-container-width.d.ts +33 -0
- package/dist/types/element-browser/hooks/use-focus.d.ts +35 -0
- package/dist/types/element-browser/hooks/use-select-and-focus-on-arrow-navigation.d.ts +72 -0
- package/dist/types/element-browser/index.d.ts +2 -0
- package/dist/types/element-browser/types.d.ts +12 -0
- package/dist/types/types/quick-insert.d.ts +1 -0
- package/dist/types/utils/performance/measure-render.d.ts +6 -4
- package/dist/types/utils/performance/measure-tti.d.ts +1 -1
- package/dist/types-ts4.5/analytics/types/general-events.d.ts +2 -1
- package/dist/types-ts4.5/element-browser/ElementBrowser.d.ts +37 -0
- package/dist/types-ts4.5/element-browser/ViewMore.d.ts +6 -0
- package/dist/types-ts4.5/element-browser/components/CategoryList.d.ts +10 -0
- package/dist/types-ts4.5/element-browser/components/ElementBrowserLoader.d.ts +6 -0
- package/dist/types-ts4.5/element-browser/components/ElementList/ElementList.d.ts +33 -0
- package/dist/types-ts4.5/element-browser/components/ElementList/EmptyState.d.ts +6 -0
- package/dist/types-ts4.5/element-browser/components/ElementList/NotFoundIllustration.d.ts +2 -0
- package/dist/types-ts4.5/element-browser/components/ElementList/cellSizeAndPositionGetter.d.ts +7 -0
- package/dist/types-ts4.5/element-browser/components/ElementList/utils.d.ts +12 -0
- package/dist/types-ts4.5/element-browser/components/ElementSearch.d.ts +18 -0
- package/dist/types-ts4.5/element-browser/components/StatelessElementBrowser.d.ts +23 -0
- package/dist/types-ts4.5/element-browser/constants.d.ts +19 -0
- package/dist/types-ts4.5/element-browser/hooks/use-container-width.d.ts +33 -0
- package/dist/types-ts4.5/element-browser/hooks/use-focus.d.ts +35 -0
- package/dist/types-ts4.5/element-browser/hooks/use-select-and-focus-on-arrow-navigation.d.ts +72 -0
- package/dist/types-ts4.5/element-browser/index.d.ts +2 -0
- package/dist/types-ts4.5/element-browser/types.d.ts +12 -0
- package/dist/types-ts4.5/types/quick-insert.d.ts +1 -0
- package/dist/types-ts4.5/utils/performance/measure-render.d.ts +6 -4
- package/dist/types-ts4.5/utils/performance/measure-tti.d.ts +1 -1
- package/element-browser/package.json +15 -0
- package/package.json +6 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ELEMENT_LIST_PADDING, FLEX_ITEMS_CONTAINER_BREAKPOINT_NUMBERS, SCROLLBAR_WIDTH } from '../../constants';
|
|
2
|
+
export function getColumnCount(clientWidth) {
|
|
3
|
+
const {
|
|
4
|
+
small,
|
|
5
|
+
medium,
|
|
6
|
+
large
|
|
7
|
+
} = FLEX_ITEMS_CONTAINER_BREAKPOINT_NUMBERS;
|
|
8
|
+
switch (true) {
|
|
9
|
+
case clientWidth < small:
|
|
10
|
+
return 1;
|
|
11
|
+
case clientWidth >= small && clientWidth < medium:
|
|
12
|
+
return Math.floor(clientWidth / 200);
|
|
13
|
+
case clientWidth >= large:
|
|
14
|
+
return Math.floor(clientWidth / 248);
|
|
15
|
+
default:
|
|
16
|
+
return Math.floor(clientWidth / 220);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function generateVirtualizedContainerDatum(containerWidth, options) {
|
|
20
|
+
const {
|
|
21
|
+
scrollbarWidth
|
|
22
|
+
} = options;
|
|
23
|
+
const columnCount = getColumnCount(containerWidth);
|
|
24
|
+
const availableWidth = containerWidth - (scrollbarWidth + ELEMENT_LIST_PADDING);
|
|
25
|
+
return {
|
|
26
|
+
availableWidth,
|
|
27
|
+
columnCount
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
let CALCULATED_SCROLLBAR_WIDTH;
|
|
31
|
+
export function getScrollbarWidth() {
|
|
32
|
+
if (!CALCULATED_SCROLLBAR_WIDTH) {
|
|
33
|
+
var _container$parentNode;
|
|
34
|
+
const container = document.createElement('div');
|
|
35
|
+
container.style.visibility = 'hidden';
|
|
36
|
+
container.style.overflow = 'scroll';
|
|
37
|
+
document.body.appendChild(container);
|
|
38
|
+
const innerContainer = document.createElement('div');
|
|
39
|
+
container.appendChild(innerContainer);
|
|
40
|
+
const scrollbarWidth = container.offsetWidth - innerContainer.offsetWidth;
|
|
41
|
+
(_container$parentNode = container.parentNode) === null || _container$parentNode === void 0 ? void 0 : _container$parentNode.removeChild(container);
|
|
42
|
+
if (scrollbarWidth) {
|
|
43
|
+
CALCULATED_SCROLLBAR_WIDTH = scrollbarWidth;
|
|
44
|
+
return scrollbarWidth;
|
|
45
|
+
}
|
|
46
|
+
return SCROLLBAR_WIDTH;
|
|
47
|
+
} else {
|
|
48
|
+
return CALCULATED_SCROLLBAR_WIDTH;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import React, { memo } from 'react';
|
|
3
|
+
import { css, jsx } from '@emotion/react';
|
|
4
|
+
import { injectIntl } from 'react-intl-next';
|
|
5
|
+
import { withAnalyticsContext } from '@atlaskit/analytics-next';
|
|
6
|
+
import { relativeFontSizeToBase16 } from '@atlaskit/editor-shared-styles';
|
|
7
|
+
import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
|
|
8
|
+
import SearchIcon from '@atlaskit/icon/glyph/search';
|
|
9
|
+
import Textfield from '@atlaskit/textfield';
|
|
10
|
+
import { N200 } from '@atlaskit/theme/colors';
|
|
11
|
+
import { GRID_SIZE, SEARCH_ITEM_HEIGHT_WIDTH } from '../constants';
|
|
12
|
+
import useFocus from '../hooks/use-focus';
|
|
13
|
+
import { Modes } from '../types';
|
|
14
|
+
function ElementSearch({
|
|
15
|
+
onSearch,
|
|
16
|
+
mode,
|
|
17
|
+
intl: {
|
|
18
|
+
formatMessage
|
|
19
|
+
},
|
|
20
|
+
focus,
|
|
21
|
+
onClick,
|
|
22
|
+
onKeyDown,
|
|
23
|
+
searchTerm
|
|
24
|
+
}) {
|
|
25
|
+
const ref = useFocus(focus);
|
|
26
|
+
const onChange = ({
|
|
27
|
+
target: {
|
|
28
|
+
value
|
|
29
|
+
}
|
|
30
|
+
}) => {
|
|
31
|
+
onSearch(value);
|
|
32
|
+
};
|
|
33
|
+
const onFocus = e => {};
|
|
34
|
+
const onBlur = e => {};
|
|
35
|
+
return jsx("div", {
|
|
36
|
+
css: [wrapper, mode === Modes.inline && wrapperInline]
|
|
37
|
+
}, jsx(Textfield, {
|
|
38
|
+
ref: ref,
|
|
39
|
+
onChange: onChange,
|
|
40
|
+
onClick: onClick,
|
|
41
|
+
onFocus: onFocus,
|
|
42
|
+
onKeyDown: onKeyDown,
|
|
43
|
+
onBlur: onBlur,
|
|
44
|
+
elemBeforeInput: jsx("div", {
|
|
45
|
+
css: elementBeforeInput,
|
|
46
|
+
"data-testid": "element_search__element_before_input"
|
|
47
|
+
}, jsx(SearchIcon, {
|
|
48
|
+
size: "medium",
|
|
49
|
+
label: "Advanced search",
|
|
50
|
+
primaryColor: "inherit"
|
|
51
|
+
})),
|
|
52
|
+
elemAfterInput: jsx("div", {
|
|
53
|
+
css: elementAfterInput,
|
|
54
|
+
"data-testid": "element_search__element_after_input"
|
|
55
|
+
}, jsx("div", {
|
|
56
|
+
css: styledShortcut
|
|
57
|
+
}, "\u23CE ", formatMessage(elementAfterInputMessage))),
|
|
58
|
+
placeholder: formatMessage(placeHolderMessage),
|
|
59
|
+
"aria-label": "search",
|
|
60
|
+
value: searchTerm
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
const elementAfterInputMessage = {
|
|
64
|
+
id: 'fabric.editor.elementbrowser.searchbar.elementAfterInput',
|
|
65
|
+
defaultMessage: 'Enter',
|
|
66
|
+
description: 'Enter to insert'
|
|
67
|
+
};
|
|
68
|
+
const placeHolderMessage = {
|
|
69
|
+
id: 'fabric.editor.elementbrowser.searchbar.placeholder',
|
|
70
|
+
defaultMessage: 'Search',
|
|
71
|
+
description: 'Search field placeholder'
|
|
72
|
+
};
|
|
73
|
+
const styledShortcut = css`
|
|
74
|
+
${shortcutStyle}
|
|
75
|
+
padding: ${GRID_SIZE / 2}px ${GRID_SIZE}px;
|
|
76
|
+
width: ${GRID_SIZE * 6}px;
|
|
77
|
+
`;
|
|
78
|
+
const wrapper = css`
|
|
79
|
+
& > [data-ds--text-field--container] {
|
|
80
|
+
height: ${GRID_SIZE * 6}px;
|
|
81
|
+
border-radius: ${GRID_SIZE}px;
|
|
82
|
+
flex: 1 1 100%;
|
|
83
|
+
overflow: visible;
|
|
84
|
+
& > [data-ds--text-field--input] {
|
|
85
|
+
margin-bottom: 3px;
|
|
86
|
+
font-size: ${relativeFontSizeToBase16(14)};
|
|
87
|
+
padding: ${GRID_SIZE}px ${"var(--ds-space-075, 6px)"} ${GRID_SIZE}px 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
const wrapperInline = css`
|
|
92
|
+
& > [data-ds--text-field--container] {
|
|
93
|
+
height: ${GRID_SIZE * 5}px;
|
|
94
|
+
flex: none;
|
|
95
|
+
overflow: revert;
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const elementBeforeInput = css`
|
|
99
|
+
margin: 1px ${"var(--ds-space-075, 6px)"} 0 ${"var(--ds-space-100, 8px)"};
|
|
100
|
+
color: ${`var(--ds-icon, ${N200})`};
|
|
101
|
+
|
|
102
|
+
// Custom SearchIcon style
|
|
103
|
+
span,
|
|
104
|
+
svg {
|
|
105
|
+
height: 20px;
|
|
106
|
+
width: 20px;
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
const elementAfterInput = css`
|
|
110
|
+
margin: 0 ${"var(--ds-space-100, 8px)"};
|
|
111
|
+
height: ${SEARCH_ITEM_HEIGHT_WIDTH};
|
|
112
|
+
text-align: center;
|
|
113
|
+
`;
|
|
114
|
+
const MemoizedElementSearchWithAnalytics = /*#__PURE__*/memo(withAnalyticsContext({
|
|
115
|
+
component: 'Searchbar'
|
|
116
|
+
})(injectIntl(ElementSearch)));
|
|
117
|
+
export default MemoizedElementSearchWithAnalytics;
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
/** @jsx jsx */
|
|
3
|
+
import React, { memo, useCallback, useEffect, useState } from 'react';
|
|
4
|
+
import { css, jsx } from '@emotion/react';
|
|
5
|
+
import { FormattedMessage } from 'react-intl-next';
|
|
6
|
+
import { withAnalyticsContext, withAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
7
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, fireAnalyticsEvent } from '../../analytics';
|
|
8
|
+
import { DEVICE_BREAKPOINT_NUMBERS, GRID_SIZE, INLINE_SIDEBAR_HEIGHT, SIDEBAR_HEADING_PADDING_LEFT, SIDEBAR_HEADING_WRAPPER_HEIGHT, SIDEBAR_WIDTH } from '../constants';
|
|
9
|
+
import useContainerWidth from '../hooks/use-container-width';
|
|
10
|
+
import useSelectAndFocusOnArrowNavigation from '../hooks/use-select-and-focus-on-arrow-navigation';
|
|
11
|
+
import { ViewMore } from '../ViewMore';
|
|
12
|
+
import CategoryList from './CategoryList';
|
|
13
|
+
import ElementList from './ElementList/ElementList';
|
|
14
|
+
import ElementSearch from './ElementSearch';
|
|
15
|
+
const wrapper = css`
|
|
16
|
+
width: 100%;
|
|
17
|
+
max-height: inherit;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
`;
|
|
20
|
+
const baseBrowserContainerStyles = css`
|
|
21
|
+
display: flex;
|
|
22
|
+
height: 100%;
|
|
23
|
+
/** Needed for Safari to work with current css.
|
|
24
|
+
* 100% heights wont work and
|
|
25
|
+
* will default to auto if one of the containers doesn't have a specified height in pixels.
|
|
26
|
+
* Setting the min-height to fill available fits safari's needs and the above 100% height works on the rest of the browsers.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */
|
|
30
|
+
|
|
31
|
+
/* stylelint-disable-next-line */
|
|
32
|
+
min-height: -webkit-fill-available;
|
|
33
|
+
`;
|
|
34
|
+
const mobileElementBrowserContainer = css`
|
|
35
|
+
${baseBrowserContainerStyles};
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
`;
|
|
38
|
+
const elementBrowserContainer = css`
|
|
39
|
+
${baseBrowserContainerStyles};
|
|
40
|
+
flex-direction: row;
|
|
41
|
+
`;
|
|
42
|
+
const baseSidebarStyles = css`
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
|
|
46
|
+
overflow-x: auto;
|
|
47
|
+
overflow-y: hidden;
|
|
48
|
+
`;
|
|
49
|
+
const mobileSideBar = css`
|
|
50
|
+
${baseSidebarStyles};
|
|
51
|
+
flex: 0 0 ${INLINE_SIDEBAR_HEIGHT};
|
|
52
|
+
padding: ${"var(--ds-space-150, 12px)"} ${"var(--ds-space-150, 12px)"} 0
|
|
53
|
+
${"var(--ds-space-150, 12px)"};
|
|
54
|
+
`;
|
|
55
|
+
const mobileSideBarShowCategories = css`
|
|
56
|
+
flex: 0 0 auto;
|
|
57
|
+
`;
|
|
58
|
+
const sideBar = css`
|
|
59
|
+
${baseSidebarStyles};
|
|
60
|
+
flex: 0 0 'auto';
|
|
61
|
+
`;
|
|
62
|
+
const sideBarShowCategories = css`
|
|
63
|
+
${baseSidebarStyles};
|
|
64
|
+
flex: 0 0 ${SIDEBAR_WIDTH};
|
|
65
|
+
`;
|
|
66
|
+
const sidebarHeading = css`
|
|
67
|
+
flex: 0 0 ${SIDEBAR_HEADING_WRAPPER_HEIGHT};
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
padding-left: ${SIDEBAR_HEADING_PADDING_LEFT};
|
|
71
|
+
font-weight: 700;
|
|
72
|
+
`;
|
|
73
|
+
const mobileMainContent = css`
|
|
74
|
+
flex: 1 1 auto;
|
|
75
|
+
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
|
|
79
|
+
overflow-y: auto;
|
|
80
|
+
height: 100%;
|
|
81
|
+
`;
|
|
82
|
+
const mainContent = css`
|
|
83
|
+
${mobileMainContent}
|
|
84
|
+
margin-left: ${GRID_SIZE * 2}px;
|
|
85
|
+
// Needed for safari
|
|
86
|
+
height: auto;
|
|
87
|
+
`;
|
|
88
|
+
const searchContainer = css`
|
|
89
|
+
padding-bottom: ${GRID_SIZE * 2}px;
|
|
90
|
+
`;
|
|
91
|
+
const mobileCategoryListWrapper = css`
|
|
92
|
+
display: flex;
|
|
93
|
+
overflow-x: auto;
|
|
94
|
+
|
|
95
|
+
padding: ${GRID_SIZE}px 0 ${GRID_SIZE * 2}px 0;
|
|
96
|
+
min-height: ${GRID_SIZE * 4}px;
|
|
97
|
+
|
|
98
|
+
overflow: -moz-scrollbars-none;
|
|
99
|
+
::-webkit-scrollbar {
|
|
100
|
+
display: none;
|
|
101
|
+
}
|
|
102
|
+
scrollbar-width: none;
|
|
103
|
+
-ms-overflow-style: none;
|
|
104
|
+
`;
|
|
105
|
+
const categoryListWrapper = css`
|
|
106
|
+
${mobileCategoryListWrapper}
|
|
107
|
+
padding: 0;
|
|
108
|
+
margin-top: ${GRID_SIZE * 3}px;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
`;
|
|
111
|
+
function StatelessElementBrowser(props) {
|
|
112
|
+
const {
|
|
113
|
+
items,
|
|
114
|
+
onSelectItem,
|
|
115
|
+
onInsertItem,
|
|
116
|
+
viewMoreItem
|
|
117
|
+
} = props;
|
|
118
|
+
const {
|
|
119
|
+
containerWidth,
|
|
120
|
+
ContainerWidthMonitor
|
|
121
|
+
} = useContainerWidth();
|
|
122
|
+
const [columnCount, setColumnCount] = useState(1);
|
|
123
|
+
const {
|
|
124
|
+
selectedItemIndex,
|
|
125
|
+
focusedItemIndex,
|
|
126
|
+
setFocusedItemIndex,
|
|
127
|
+
focusOnSearch,
|
|
128
|
+
focusOnViewMore,
|
|
129
|
+
onKeyDown,
|
|
130
|
+
setFocusOnSearch
|
|
131
|
+
} = useSelectAndFocusOnArrowNavigation(items.length - 1, columnCount, !!viewMoreItem);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
fireAnalyticsEvent(props.createAnalyticsEvent)({
|
|
134
|
+
payload: {
|
|
135
|
+
action: ACTION.OPENED,
|
|
136
|
+
actionSubject: ACTION_SUBJECT.ELEMENT_BROWSER,
|
|
137
|
+
eventType: EVENT_TYPE.UI,
|
|
138
|
+
attributes: {
|
|
139
|
+
mode: props.mode
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
return () => {
|
|
144
|
+
fireAnalyticsEvent(props.createAnalyticsEvent)({
|
|
145
|
+
payload: {
|
|
146
|
+
action: ACTION.CLOSED,
|
|
147
|
+
actionSubject: ACTION_SUBJECT.ELEMENT_BROWSER,
|
|
148
|
+
eventType: EVENT_TYPE.UI,
|
|
149
|
+
attributes: {
|
|
150
|
+
mode: props.mode
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
}, [props.createAnalyticsEvent, props.mode]);
|
|
156
|
+
|
|
157
|
+
/* Only for hitting enter to select item when focused on search bar,
|
|
158
|
+
* The actual enter key press is handled on individual items level.
|
|
159
|
+
*/
|
|
160
|
+
const selectedItem = selectedItemIndex !== undefined ? items[selectedItemIndex] : null;
|
|
161
|
+
const onItemsEnterKeyPress = useCallback(e => {
|
|
162
|
+
if (e.key !== 'Enter') {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (onInsertItem && selectedItem != null) {
|
|
166
|
+
onInsertItem(selectedItem);
|
|
167
|
+
}
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
}, [onInsertItem, selectedItem]);
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* On arrow key selection and clicks the selectedItemIndex will change.
|
|
173
|
+
* Making sure to update parent component.
|
|
174
|
+
*/
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (onSelectItem && selectedItem != null) {
|
|
177
|
+
onSelectItem(selectedItem);
|
|
178
|
+
}
|
|
179
|
+
}, [onSelectItem, selectedItem]);
|
|
180
|
+
return jsx("div", {
|
|
181
|
+
css: wrapper,
|
|
182
|
+
"data-testid": "element-browser"
|
|
183
|
+
}, jsx(ContainerWidthMonitor, null), containerWidth < DEVICE_BREAKPOINT_NUMBERS.medium ? jsx(MobileBrowser, _extends({}, props, {
|
|
184
|
+
selectedItemIndex: selectedItemIndex,
|
|
185
|
+
focusedItemIndex: focusedItemIndex,
|
|
186
|
+
setFocusedItemIndex: setFocusedItemIndex,
|
|
187
|
+
focusOnSearch: focusOnSearch,
|
|
188
|
+
setColumnCount: setColumnCount,
|
|
189
|
+
setFocusOnSearch: setFocusOnSearch,
|
|
190
|
+
onKeyPress: onItemsEnterKeyPress,
|
|
191
|
+
onKeyDown: onKeyDown,
|
|
192
|
+
viewMoreItem: viewMoreItem,
|
|
193
|
+
focusOnViewMore: focusOnViewMore
|
|
194
|
+
})) : jsx(DesktopBrowser, _extends({}, props, {
|
|
195
|
+
selectedItemIndex: selectedItemIndex,
|
|
196
|
+
focusedItemIndex: focusedItemIndex,
|
|
197
|
+
setFocusedItemIndex: setFocusedItemIndex,
|
|
198
|
+
focusOnSearch: focusOnSearch,
|
|
199
|
+
setColumnCount: setColumnCount,
|
|
200
|
+
setFocusOnSearch: setFocusOnSearch,
|
|
201
|
+
onKeyPress: onItemsEnterKeyPress,
|
|
202
|
+
onKeyDown: onKeyDown
|
|
203
|
+
})));
|
|
204
|
+
}
|
|
205
|
+
function MobileBrowser({
|
|
206
|
+
showCategories,
|
|
207
|
+
showSearch,
|
|
208
|
+
onSearch,
|
|
209
|
+
mode,
|
|
210
|
+
categories,
|
|
211
|
+
onSelectCategory,
|
|
212
|
+
items,
|
|
213
|
+
onInsertItem,
|
|
214
|
+
selectedCategory,
|
|
215
|
+
selectedItemIndex,
|
|
216
|
+
focusedItemIndex,
|
|
217
|
+
setFocusedItemIndex,
|
|
218
|
+
focusOnSearch,
|
|
219
|
+
focusOnViewMore,
|
|
220
|
+
setColumnCount,
|
|
221
|
+
setFocusOnSearch,
|
|
222
|
+
onKeyPress,
|
|
223
|
+
onKeyDown,
|
|
224
|
+
searchTerm,
|
|
225
|
+
createAnalyticsEvent,
|
|
226
|
+
emptyStateHandler,
|
|
227
|
+
viewMoreItem
|
|
228
|
+
}) {
|
|
229
|
+
return jsx("div", {
|
|
230
|
+
css: mobileElementBrowserContainer,
|
|
231
|
+
onKeyDown: onKeyDown,
|
|
232
|
+
"data-testid": "mobile__element-browser"
|
|
233
|
+
}, jsx("div", {
|
|
234
|
+
css: showCategories ? [mobileSideBar, mobileSideBarShowCategories] : mobileSideBar
|
|
235
|
+
}, showSearch && jsx(ElementSearch, {
|
|
236
|
+
onSearch: onSearch,
|
|
237
|
+
onKeyDown: onKeyPress,
|
|
238
|
+
mode: mode,
|
|
239
|
+
focus: focusOnSearch,
|
|
240
|
+
onClick: setFocusOnSearch,
|
|
241
|
+
searchTerm: searchTerm
|
|
242
|
+
}), showCategories && jsx("nav", {
|
|
243
|
+
css: mobileCategoryListWrapper,
|
|
244
|
+
tabIndex: -1
|
|
245
|
+
}, jsx(CategoryList, {
|
|
246
|
+
categories: categories,
|
|
247
|
+
onSelectCategory: onSelectCategory,
|
|
248
|
+
selectedCategory: selectedCategory
|
|
249
|
+
}))), jsx("div", {
|
|
250
|
+
css: mobileMainContent
|
|
251
|
+
}, jsx(ElementList, {
|
|
252
|
+
items: items,
|
|
253
|
+
mode: mode,
|
|
254
|
+
onInsertItem: onInsertItem,
|
|
255
|
+
selectedItemIndex: selectedItemIndex,
|
|
256
|
+
focusedItemIndex: focusedItemIndex,
|
|
257
|
+
setFocusedItemIndex: setFocusedItemIndex,
|
|
258
|
+
setColumnCount: setColumnCount,
|
|
259
|
+
createAnalyticsEvent: createAnalyticsEvent,
|
|
260
|
+
emptyStateHandler: emptyStateHandler,
|
|
261
|
+
selectedCategory: selectedCategory,
|
|
262
|
+
searchTerm: searchTerm
|
|
263
|
+
})), viewMoreItem && jsx(ViewMore, {
|
|
264
|
+
item: viewMoreItem,
|
|
265
|
+
focus: focusOnViewMore
|
|
266
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
function DesktopBrowser({
|
|
269
|
+
showCategories,
|
|
270
|
+
showSearch,
|
|
271
|
+
onSearch,
|
|
272
|
+
mode,
|
|
273
|
+
categories,
|
|
274
|
+
onSelectCategory,
|
|
275
|
+
items,
|
|
276
|
+
onInsertItem,
|
|
277
|
+
selectedCategory,
|
|
278
|
+
selectedItemIndex,
|
|
279
|
+
focusedItemIndex,
|
|
280
|
+
setFocusedItemIndex,
|
|
281
|
+
focusOnSearch,
|
|
282
|
+
setColumnCount,
|
|
283
|
+
setFocusOnSearch,
|
|
284
|
+
onKeyPress,
|
|
285
|
+
onKeyDown,
|
|
286
|
+
searchTerm,
|
|
287
|
+
createAnalyticsEvent,
|
|
288
|
+
emptyStateHandler
|
|
289
|
+
}) {
|
|
290
|
+
return jsx("div", {
|
|
291
|
+
css: elementBrowserContainer,
|
|
292
|
+
"data-testid": "desktop__element-browser"
|
|
293
|
+
}, showCategories && jsx("div", {
|
|
294
|
+
css: showCategories ? sideBarShowCategories : sideBar
|
|
295
|
+
}, jsx("h2", {
|
|
296
|
+
css: sidebarHeading,
|
|
297
|
+
"data-testid": "sidebar-heading"
|
|
298
|
+
}, jsx(FormattedMessage, {
|
|
299
|
+
id: "fabric.editor.elementbrowser.sidebar.heading",
|
|
300
|
+
defaultMessage: "Browse",
|
|
301
|
+
description: "Sidebar heading"
|
|
302
|
+
})), jsx("nav", {
|
|
303
|
+
css: categoryListWrapper
|
|
304
|
+
}, jsx(CategoryList, {
|
|
305
|
+
categories: categories,
|
|
306
|
+
onSelectCategory: onSelectCategory,
|
|
307
|
+
selectedCategory: selectedCategory,
|
|
308
|
+
createAnalyticsEvent: createAnalyticsEvent
|
|
309
|
+
}))), jsx("div", {
|
|
310
|
+
css: mainContent,
|
|
311
|
+
onKeyDown: onKeyDown,
|
|
312
|
+
"data-testid": "main-content"
|
|
313
|
+
}, showSearch && jsx("div", {
|
|
314
|
+
css: searchContainer
|
|
315
|
+
}, jsx(ElementSearch, {
|
|
316
|
+
onSearch: onSearch,
|
|
317
|
+
onKeyDown: onKeyPress,
|
|
318
|
+
mode: mode,
|
|
319
|
+
focus: focusOnSearch,
|
|
320
|
+
onClick: setFocusOnSearch,
|
|
321
|
+
searchTerm: searchTerm
|
|
322
|
+
})), jsx(ElementList, {
|
|
323
|
+
items: items,
|
|
324
|
+
mode: mode,
|
|
325
|
+
onInsertItem: onInsertItem,
|
|
326
|
+
selectedItemIndex: selectedItemIndex,
|
|
327
|
+
focusedItemIndex: focusedItemIndex,
|
|
328
|
+
setFocusedItemIndex: setFocusedItemIndex,
|
|
329
|
+
setColumnCount: setColumnCount,
|
|
330
|
+
createAnalyticsEvent: createAnalyticsEvent,
|
|
331
|
+
emptyStateHandler: emptyStateHandler,
|
|
332
|
+
selectedCategory: selectedCategory,
|
|
333
|
+
searchTerm: searchTerm
|
|
334
|
+
})));
|
|
335
|
+
}
|
|
336
|
+
const MemoizedElementBrowser = /*#__PURE__*/memo(withAnalyticsContext({
|
|
337
|
+
source: 'ElementBrowser'
|
|
338
|
+
})(withAnalyticsEvents()(StatelessElementBrowser)));
|
|
339
|
+
export default MemoizedElementBrowser;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// eslint-disable-next-line @atlaskit/design-system/no-deprecated-imports
|
|
2
|
+
import { gridSize } from '@atlaskit/theme/constants';
|
|
3
|
+
// TODO: Migrate away from gridSize
|
|
4
|
+
// Recommendation: Replace gridSize with 8
|
|
5
|
+
export const GRID_SIZE = gridSize();
|
|
6
|
+
export const DEVICE_BREAKPOINT_NUMBERS = {
|
|
7
|
+
small: GRID_SIZE * 40,
|
|
8
|
+
medium: GRID_SIZE * 75,
|
|
9
|
+
large: GRID_SIZE * 128
|
|
10
|
+
};
|
|
11
|
+
export const FLEX_ITEMS_CONTAINER_BREAKPOINT_NUMBERS = {
|
|
12
|
+
small: GRID_SIZE * 50,
|
|
13
|
+
medium: DEVICE_BREAKPOINT_NUMBERS.medium,
|
|
14
|
+
large: DEVICE_BREAKPOINT_NUMBERS.large
|
|
15
|
+
};
|
|
16
|
+
export const SIDEBAR_WIDTH = `${GRID_SIZE * 25}px`;
|
|
17
|
+
export const SIDEBAR_HEADING_WRAPPER_HEIGHT = `${GRID_SIZE * 6}px`;
|
|
18
|
+
export const SIDEBAR_HEADING_PADDING_LEFT = '12px';
|
|
19
|
+
export const INLINE_SIDEBAR_HEIGHT = '54px';
|
|
20
|
+
export const SEARCH_ITEM_HEIGHT_WIDTH = '20px';
|
|
21
|
+
export const SCROLLBAR_WIDTH = 15;
|
|
22
|
+
export const ELEMENT_LIST_PADDING = 2;
|
|
23
|
+
export const ELEMENT_ITEM_HEIGHT = 75;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import React, { memo, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { css, jsx } from '@emotion/react';
|
|
4
|
+
import { WidthObserver } from '@atlaskit/width-detector';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* Problem:
|
|
9
|
+
* While using WidthObserver, there's no initial width.
|
|
10
|
+
* That may cause problems, but not limited to, something like a lag between
|
|
11
|
+
* renders for conditionally rendered components.
|
|
12
|
+
*
|
|
13
|
+
* solution:
|
|
14
|
+
* useContainerWidth() hook
|
|
15
|
+
* it pre-measures the width of a parent container on initial mount
|
|
16
|
+
* and gives you back the containerWidth.
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
* Example hook usage:
|
|
20
|
+
*
|
|
21
|
+
* const { containerWidth, ContainerWidthMonitor } = useContainerWidth();
|
|
22
|
+
*
|
|
23
|
+
* return (
|
|
24
|
+
* <>
|
|
25
|
+
* <ContainerWidthMonitor />
|
|
26
|
+
* {containerWidth < 600 ? <MobileComponent /> : <DesktopComponent />}
|
|
27
|
+
* </>
|
|
28
|
+
* );
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const widthObserverWrapper = css`
|
|
33
|
+
position: relative;
|
|
34
|
+
`;
|
|
35
|
+
export default function useContainerWidth() {
|
|
36
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
37
|
+
const ref = useRef(null);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const {
|
|
40
|
+
current
|
|
41
|
+
} = ref;
|
|
42
|
+
if (ref && current) {
|
|
43
|
+
setContainerWidth(current.getBoundingClientRect().width);
|
|
44
|
+
}
|
|
45
|
+
}, [ref]);
|
|
46
|
+
const ContainerWidthMonitor = /*#__PURE__*/memo(() => {
|
|
47
|
+
return jsx("div", {
|
|
48
|
+
css: widthObserverWrapper,
|
|
49
|
+
ref: ref,
|
|
50
|
+
tabIndex: -1
|
|
51
|
+
}, jsx(WidthObserver, {
|
|
52
|
+
setWidth: setContainerWidth
|
|
53
|
+
}));
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
containerWidth,
|
|
57
|
+
ContainerWidthMonitor
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A custom hook that handles focus on a DOM element.
|
|
5
|
+
* Takes in a boolean value and calls element.focus
|
|
6
|
+
*
|
|
7
|
+
* @param {boolean} focus
|
|
8
|
+
*
|
|
9
|
+
* Example usage:
|
|
10
|
+
*******************************************************************************
|
|
11
|
+
* const SearchBar = ({ focus }) => {
|
|
12
|
+
* const ref = useFocus(focus);
|
|
13
|
+
* return <input ref={ref} />
|
|
14
|
+
* }
|
|
15
|
+
*******************************************************************************
|
|
16
|
+
* const ItemList = ({ items, focus }) => (
|
|
17
|
+
* <ul>
|
|
18
|
+
* {items.map((item, index) => (
|
|
19
|
+
* <Item key={item.uuid} item={item} focus={focus} />
|
|
20
|
+
* ))}
|
|
21
|
+
* </ul>
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* const Item = ({ item, focus }) => {
|
|
25
|
+
* const ref = useFocus(focus);
|
|
26
|
+
* return (
|
|
27
|
+
* <li ref={ref}>
|
|
28
|
+
* {item.name}
|
|
29
|
+
* </li>
|
|
30
|
+
* )
|
|
31
|
+
* }
|
|
32
|
+
*******************************************************************************
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
export default function useFocus(focus) {
|
|
36
|
+
const ref = useRef(null);
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
const {
|
|
39
|
+
current
|
|
40
|
+
} = ref;
|
|
41
|
+
if (focus && current && document.activeElement !== current) {
|
|
42
|
+
current.focus({
|
|
43
|
+
preventScroll: true
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}, [focus]);
|
|
47
|
+
return ref;
|
|
48
|
+
}
|