@atlaskit/link-picker 1.11.1 → 1.12.0
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 +12 -0
- package/dist/cjs/analytics.codegen.js +1 -1
- package/dist/cjs/common/analytics/context.js +8 -2
- package/dist/cjs/ui/link-picker/index.js +32 -38
- package/dist/cjs/ui/link-picker/list-item/index.js +1 -1
- package/dist/cjs/ui/link-picker/text-input/index.js +8 -5
- package/dist/cjs/ui/link-picker/track-mount/index.js +47 -0
- package/dist/cjs/ui/link-picker/track-tab-viewed/index.js +32 -0
- package/dist/cjs/ui/scrolling-tabs/index.js +192 -0
- package/dist/cjs/ui/scrolling-tabs/scrolling-tabs.js +123 -0
- package/dist/cjs/ui/scrolling-tabs/styles.js +64 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/analytics.codegen.js +1 -1
- package/dist/es2019/common/analytics/context.js +8 -2
- package/dist/es2019/ui/link-picker/index.js +29 -33
- package/dist/es2019/ui/link-picker/list-item/index.js +1 -1
- package/dist/es2019/ui/link-picker/text-input/index.js +4 -1
- package/dist/es2019/ui/link-picker/track-mount/index.js +31 -0
- package/dist/es2019/ui/link-picker/track-tab-viewed/index.js +19 -0
- package/dist/es2019/ui/scrolling-tabs/index.js +127 -0
- package/dist/es2019/ui/scrolling-tabs/scrolling-tabs.js +90 -0
- package/dist/es2019/ui/scrolling-tabs/styles.js +49 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/analytics.codegen.js +1 -1
- package/dist/esm/common/analytics/context.js +8 -2
- package/dist/esm/ui/link-picker/index.js +32 -39
- package/dist/esm/ui/link-picker/list-item/index.js +1 -1
- package/dist/esm/ui/link-picker/text-input/index.js +4 -1
- package/dist/esm/ui/link-picker/track-mount/index.js +31 -0
- package/dist/esm/ui/link-picker/track-tab-viewed/index.js +19 -0
- package/dist/esm/ui/scrolling-tabs/index.js +165 -0
- package/dist/esm/ui/scrolling-tabs/scrolling-tabs.js +96 -0
- package/dist/esm/ui/scrolling-tabs/styles.js +49 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/analytics.codegen.d.ts +4 -1
- package/dist/types/ui/link-picker/index.d.ts +3 -2
- package/dist/types/ui/link-picker/link-search-list/index.d.ts +0 -1
- package/dist/types/ui/link-picker/text-input/index.d.ts +4 -0
- package/dist/types/ui/link-picker/track-mount/index.d.ts +5 -0
- package/dist/types/ui/link-picker/track-tab-viewed/index.d.ts +4 -0
- package/dist/types/ui/scrolling-tabs/index.d.ts +7 -0
- package/dist/types/ui/scrolling-tabs/scrolling-tabs.d.ts +11 -0
- package/dist/types/ui/scrolling-tabs/styles.d.ts +6 -0
- package/dist/types-ts4.0/analytics.codegen.d.ts +4 -1
- package/dist/types-ts4.0/ui/link-picker/index.d.ts +3 -2
- package/dist/types-ts4.0/ui/link-picker/link-search-list/index.d.ts +0 -1
- package/dist/types-ts4.0/ui/link-picker/text-input/index.d.ts +4 -0
- package/dist/types-ts4.0/ui/link-picker/track-mount/index.d.ts +5 -0
- package/dist/types-ts4.0/ui/link-picker/track-tab-viewed/index.d.ts +4 -0
- package/dist/types-ts4.0/ui/scrolling-tabs/index.d.ts +7 -0
- package/dist/types-ts4.0/ui/scrolling-tabs/scrolling-tabs.d.ts +11 -0
- package/dist/types-ts4.0/ui/scrolling-tabs/styles.d.ts +6 -0
- package/package.json +20 -15
- package/report.api.md +21 -12
- package/tmp/api-report-tmp.d.ts +95 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.scrollingContainerStyles = exports.nextButtonStyles = exports.containerStyles = exports.buttonContainerStyles = exports.backButtonStyles = void 0;
|
|
7
|
+
|
|
8
|
+
var _react = require("@emotion/react");
|
|
9
|
+
|
|
10
|
+
var _tokens = require("@atlaskit/tokens");
|
|
11
|
+
|
|
12
|
+
var _colors = require("@atlaskit/theme/colors");
|
|
13
|
+
|
|
14
|
+
var scrollingContainerStyles = (0, _react.css)({
|
|
15
|
+
overflowX: 'auto',
|
|
16
|
+
scrollBehavior: 'smooth',
|
|
17
|
+
scrollPadding: '0 24px',
|
|
18
|
+
scrollbarWidth: 'none',
|
|
19
|
+
'&::-webkit-scrollbar': {
|
|
20
|
+
display: 'none'
|
|
21
|
+
},
|
|
22
|
+
'&::before': {
|
|
23
|
+
content: '""',
|
|
24
|
+
borderRadius: 2,
|
|
25
|
+
bottom: 0,
|
|
26
|
+
margin: 0,
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
width: 'inherit',
|
|
29
|
+
left: 8,
|
|
30
|
+
right: 8,
|
|
31
|
+
height: 2,
|
|
32
|
+
backgroundColor: (0, _tokens.token)('color.border', _colors.N30)
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
exports.scrollingContainerStyles = scrollingContainerStyles;
|
|
36
|
+
var containerStyles = (0, _react.css)({
|
|
37
|
+
position: 'relative',
|
|
38
|
+
'[role="tablist"]': {
|
|
39
|
+
'&::before': {
|
|
40
|
+
display: 'none'
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
// Overrides Atlaskit tabs styles to stop overflowing with ellipsis
|
|
44
|
+
'[role="tab"]': {
|
|
45
|
+
overflow: 'unset !important',
|
|
46
|
+
textOverflow: 'unset !important'
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
exports.containerStyles = containerStyles;
|
|
50
|
+
var buttonContainerStyles = (0, _react.css)({
|
|
51
|
+
position: 'absolute',
|
|
52
|
+
top: 4,
|
|
53
|
+
zIndex: 999,
|
|
54
|
+
backgroundColor: (0, _tokens.token)('elevation.surface', _colors.N0)
|
|
55
|
+
});
|
|
56
|
+
exports.buttonContainerStyles = buttonContainerStyles;
|
|
57
|
+
var backButtonStyles = (0, _react.css)([buttonContainerStyles, {
|
|
58
|
+
left: 0
|
|
59
|
+
}]);
|
|
60
|
+
exports.backButtonStyles = backButtonStyles;
|
|
61
|
+
var nextButtonStyles = (0, _react.css)([buttonContainerStyles, {
|
|
62
|
+
right: 0
|
|
63
|
+
}]);
|
|
64
|
+
exports.nextButtonStyles = nextButtonStyles;
|
package/dist/cjs/version.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generates analytics utilities from the package analytics spec yaml
|
|
5
5
|
*
|
|
6
|
-
* @codegen <<SignedSource::
|
|
6
|
+
* @codegen <<SignedSource::c0fb3bb39f50f3a7c35ebd90e8903280>>
|
|
7
7
|
* @codegenCommand yarn workspace @atlaskit/link-picker run codegen-analytics
|
|
8
8
|
*/
|
|
9
9
|
function createEventPayload(eventKey, attributes) {
|
|
@@ -9,7 +9,8 @@ const DEFAULT_CONTEXT_ATTRIBUTES = {
|
|
|
9
9
|
linkFieldContentInputMethod: null,
|
|
10
10
|
linkFieldContentInputSource: null,
|
|
11
11
|
displayTextFieldContent: null,
|
|
12
|
-
displayTextFieldContentInputMethod: null
|
|
12
|
+
displayTextFieldContentInputMethod: null,
|
|
13
|
+
tab: null
|
|
13
14
|
};
|
|
14
15
|
const LinkPickerAnalyticsContext = /*#__PURE__*/React.createContext({
|
|
15
16
|
trackAttribute: () => {},
|
|
@@ -18,13 +19,18 @@ const LinkPickerAnalyticsContext = /*#__PURE__*/React.createContext({
|
|
|
18
19
|
});
|
|
19
20
|
/**
|
|
20
21
|
* TODO: Should we hook this into the form state/reducer?
|
|
22
|
+
* This function continues to duplicate / assume logic that runs in the picker
|
|
21
23
|
*/
|
|
22
24
|
|
|
23
25
|
const contextAttributesFromIntialProps = props => {
|
|
26
|
+
var _props$plugins$0$tabK, _props$plugins, _props$plugins$;
|
|
27
|
+
|
|
24
28
|
return {
|
|
25
29
|
linkState: normalizeUrl(props.url) ? 'editLink' : 'newLink',
|
|
26
30
|
linkFieldContent: normalizeUrl(props.url) ? 'url' : null,
|
|
27
|
-
displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null
|
|
31
|
+
displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null,
|
|
32
|
+
// TODO: Assumes we alway load the first plugin as the current tab
|
|
33
|
+
tab: (_props$plugins$0$tabK = (_props$plugins = props.plugins) === null || _props$plugins === void 0 ? void 0 : (_props$plugins$ = _props$plugins[0]) === null || _props$plugins$ === void 0 ? void 0 : _props$plugins$.tabKey) !== null && _props$plugins$0$tabK !== void 0 ? _props$plugins$0$tabK : null
|
|
28
34
|
};
|
|
29
35
|
};
|
|
30
36
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
|
|
3
3
|
/** @jsx jsx */
|
|
4
|
-
import { Fragment, useCallback,
|
|
4
|
+
import { Fragment, useCallback, useLayoutEffect, useReducer, memo } from 'react';
|
|
5
5
|
import { jsx } from '@emotion/react';
|
|
6
6
|
import { useIntl, FormattedMessage } from 'react-intl-next';
|
|
7
7
|
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
@@ -16,16 +16,17 @@ import { usePlugins } from '../../services/use-plugins';
|
|
|
16
16
|
import { useSearchQuery } from '../../services/use-search-query';
|
|
17
17
|
import useFixHeight from '../../controllers/use-fix-height';
|
|
18
18
|
import { searchMessages, linkMessages, formMessages, linkTextMessages } from './messages';
|
|
19
|
-
import TextInput from './text-input';
|
|
19
|
+
import TextInput, { testIds as textFieldTestIds } from './text-input';
|
|
20
20
|
import { rootContainerStyles, searchIconStyles, tabsWrapperStyles, flexColumnStyles, formFooterMargin } from './styled';
|
|
21
21
|
import { browser } from './browser';
|
|
22
22
|
import Announcer from './announcer';
|
|
23
|
+
import ScrollingTabList from '../scrolling-tabs';
|
|
23
24
|
import LinkSearchList, { testIds as listTestIds } from './link-search-list';
|
|
24
25
|
import LinkSearchError, { testIds as searchErrorTestIds } from './link-search-error';
|
|
25
26
|
import FormFooter, { testIds as formFooterTestIds } from './form-footer';
|
|
26
27
|
import { getDataSource, getScreenReaderText } from './utils';
|
|
27
|
-
import
|
|
28
|
-
import
|
|
28
|
+
import TrackTabViewed from './track-tab-viewed';
|
|
29
|
+
import TrackMount from './track-mount';
|
|
29
30
|
export const RECENT_SEARCH_LIST_SIZE = 5;
|
|
30
31
|
export const testIds = {
|
|
31
32
|
linkPickerRoot: 'link-picker-root',
|
|
@@ -36,8 +37,7 @@ export const testIds = {
|
|
|
36
37
|
...formFooterTestIds,
|
|
37
38
|
...searchErrorTestIds,
|
|
38
39
|
...listTestIds,
|
|
39
|
-
|
|
40
|
-
urlError: 'link-error',
|
|
40
|
+
...textFieldTestIds,
|
|
41
41
|
tabList: 'link-picker-tabs',
|
|
42
42
|
tabItem: 'link-picker-tab'
|
|
43
43
|
};
|
|
@@ -87,7 +87,8 @@ function LinkPicker({
|
|
|
87
87
|
onContentResize,
|
|
88
88
|
plugins,
|
|
89
89
|
url: initUrl,
|
|
90
|
-
displayText: initDisplayText
|
|
90
|
+
displayText: initDisplayText,
|
|
91
|
+
featureFlags
|
|
91
92
|
}) {
|
|
92
93
|
var _errorFallback;
|
|
93
94
|
|
|
@@ -126,20 +127,6 @@ function LinkPicker({
|
|
|
126
127
|
trackAttribute,
|
|
127
128
|
getAttributes
|
|
128
129
|
} = useLinkPickerAnalytics();
|
|
129
|
-
const linkPickerSessionId = useLinkPickerSessionId();
|
|
130
|
-
useLayoutEffect(() => {
|
|
131
|
-
succeedUfoExperience(ufoExperience.mounted, linkPickerSessionId);
|
|
132
|
-
}, [linkPickerSessionId]);
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
// Anything in here is fired on component mount.
|
|
135
|
-
const event = createAnalyticsEvent(createEventPayload('ui.inlineDialog.viewed.linkPicker', {}));
|
|
136
|
-
event.fire(ANALYTICS_CHANNEL);
|
|
137
|
-
return () => {
|
|
138
|
-
// Anything in here is fired on component unmount.
|
|
139
|
-
const event = createAnalyticsEvent(createEventPayload('ui.inlineDialog.closed.linkPicker', {}));
|
|
140
|
-
event.fire(ANALYTICS_CHANNEL);
|
|
141
|
-
};
|
|
142
|
-
}, [createAnalyticsEvent]);
|
|
143
130
|
useLayoutEffect(() => {
|
|
144
131
|
if (onContentResize) {
|
|
145
132
|
onContentResize();
|
|
@@ -285,6 +272,17 @@ function LinkPicker({
|
|
|
285
272
|
invalidUrl: true
|
|
286
273
|
});
|
|
287
274
|
}, [handleInsert, isSelectedItem, selectedItem, url]);
|
|
275
|
+
const handleTabChange = useCallback(activeTab => {
|
|
276
|
+
var _plugins$activeTab$ta, _plugins$activeTab;
|
|
277
|
+
|
|
278
|
+
dispatch({
|
|
279
|
+
selectedIndex: -1,
|
|
280
|
+
activeIndex: -1,
|
|
281
|
+
invalidUrl: false,
|
|
282
|
+
activeTab
|
|
283
|
+
});
|
|
284
|
+
trackAttribute('tab', (_plugins$activeTab$ta = plugins === null || plugins === void 0 ? void 0 : (_plugins$activeTab = plugins[activeTab]) === null || _plugins$activeTab === void 0 ? void 0 : _plugins$activeTab.tabKey) !== null && _plugins$activeTab$ta !== void 0 ? _plugins$activeTab$ta : null);
|
|
285
|
+
}, [dispatch, plugins, trackAttribute]);
|
|
288
286
|
const messages = isActivePlugin ? searchMessages : linkMessages;
|
|
289
287
|
const screenReaderDescriptionId = 'search-recent-links-field-description';
|
|
290
288
|
const linkSearchListId = 'link-picker-search-list';
|
|
@@ -300,11 +298,15 @@ function LinkPicker({
|
|
|
300
298
|
size: "medium",
|
|
301
299
|
label: ''
|
|
302
300
|
}));
|
|
301
|
+
const tabList = jsx(TabList, null, tabs.map(tab => jsx(Tab, {
|
|
302
|
+
key: tab.tabTitle,
|
|
303
|
+
testId: testIds.tabItem
|
|
304
|
+
}, tab.tabTitle)));
|
|
303
305
|
return jsx("form", {
|
|
304
306
|
"data-testid": testIds.linkPicker,
|
|
305
307
|
css: rootContainerStyles,
|
|
306
308
|
onSubmit: handleSubmit
|
|
307
|
-
}, screenReaderText && jsx(Announcer, {
|
|
309
|
+
}, jsx(TrackMount, null), screenReaderText && jsx(Announcer, {
|
|
308
310
|
ariaLive: "assertive",
|
|
309
311
|
text: screenReaderText,
|
|
310
312
|
ariaRelevant: "additions",
|
|
@@ -348,16 +350,11 @@ function LinkPicker({
|
|
|
348
350
|
}, jsx(Tabs, {
|
|
349
351
|
id: testIds.tabList,
|
|
350
352
|
testId: testIds.tabList,
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
})
|
|
357
|
-
}, jsx(TabList, null, tabs.map(tab => jsx(Tab, {
|
|
358
|
-
key: tab.tabTitle,
|
|
359
|
-
testId: testIds.tabItem
|
|
360
|
-
}, tab.tabTitle))))), jsx("div", _extends({
|
|
353
|
+
selected: activeTab,
|
|
354
|
+
onChange: handleTabChange
|
|
355
|
+
}, featureFlags !== null && featureFlags !== void 0 && featureFlags.scrollingTabs ? jsx(ScrollingTabList, null, tabList) : tabList), jsx(TrackTabViewed, {
|
|
356
|
+
activeTab: activeTab
|
|
357
|
+
})), jsx("div", _extends({
|
|
361
358
|
css: flexColumnStyles
|
|
362
359
|
}, fixListHeightProps), !error && jsx(LinkSearchList, {
|
|
363
360
|
id: linkSearchListId,
|
|
@@ -369,7 +366,6 @@ function LinkPicker({
|
|
|
369
366
|
onSelect: handleSelected,
|
|
370
367
|
onMouseEnter: handleMouseEnterResultItem,
|
|
371
368
|
onMouseLeave: handleMouseLeaveResultItem,
|
|
372
|
-
error: error,
|
|
373
369
|
hasSearchTerm: !!(queryState !== null && queryState !== void 0 && queryState.query.length)
|
|
374
370
|
}), error && ((_errorFallback = errorFallback === null || errorFallback === void 0 ? void 0 : errorFallback(error, retry)) !== null && _errorFallback !== void 0 ? _errorFallback : jsx(LinkSearchError, null)))), jsx(FormFooter, {
|
|
375
371
|
css: !queryState || !(plugins !== null && plugins !== void 0 && plugins.length) ? formFooterMargin : undefined,
|
|
@@ -4,7 +4,7 @@ import { jsx } from '@emotion/react';
|
|
|
4
4
|
import { injectIntl } from 'react-intl-next';
|
|
5
5
|
import { transformTimeStamp } from '../transformTimeStamp';
|
|
6
6
|
import { itemNameStyles, itemIconStyles, listItemContextStyles, listItemNameStyles, composeListItemStyles, imgStyles, listItemContainerStyles, listItemContainerInnerStyles } from './styled';
|
|
7
|
-
import { testIds } from '
|
|
7
|
+
import { testIds } from '..';
|
|
8
8
|
|
|
9
9
|
const LinkSearchListItem = ({
|
|
10
10
|
item,
|
|
@@ -8,8 +8,11 @@ import Tooltip from '@atlaskit/tooltip';
|
|
|
8
8
|
import Textfield from '@atlaskit/textfield';
|
|
9
9
|
import Selectclear from '@atlaskit/icon/glyph/select-clear';
|
|
10
10
|
import { isRedoEvent, isUndoEvent } from '../utils';
|
|
11
|
-
import { testIds } from '../';
|
|
12
11
|
import { clearTextButtonStyles, fieldStyles } from './styled';
|
|
12
|
+
export const testIds = {
|
|
13
|
+
urlError: 'link-error',
|
|
14
|
+
clearUrlButton: 'clear-text'
|
|
15
|
+
};
|
|
13
16
|
|
|
14
17
|
const TextInput = ({
|
|
15
18
|
name,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import { useLayoutEffect } from 'react';
|
|
3
|
+
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
4
|
+
import { succeedUfoExperience, ufoExperience } from '../../../common/analytics/experiences';
|
|
5
|
+
import createEventPayload from '../../../analytics.codegen';
|
|
6
|
+
import { ANALYTICS_CHANNEL } from '../../../common/constants';
|
|
7
|
+
import { useLinkPickerSessionId } from '../../../controllers/session-provider';
|
|
8
|
+
/**
|
|
9
|
+
* UFO + Analytics tracking for component mount (and unmount)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
function TrackMount() {
|
|
13
|
+
const {
|
|
14
|
+
createAnalyticsEvent
|
|
15
|
+
} = useAnalyticsEvents();
|
|
16
|
+
const linkPickerSessionId = useLinkPickerSessionId();
|
|
17
|
+
useLayoutEffect(() => {
|
|
18
|
+
succeedUfoExperience(ufoExperience.mounted, linkPickerSessionId);
|
|
19
|
+
}, [linkPickerSessionId]);
|
|
20
|
+
useLayoutEffect(() => {
|
|
21
|
+
// Anything in here is fired on component mount.
|
|
22
|
+
createAnalyticsEvent(createEventPayload('ui.inlineDialog.viewed.linkPicker', {})).fire(ANALYTICS_CHANNEL);
|
|
23
|
+
return () => {
|
|
24
|
+
// Anything in here is fired on component unmount.
|
|
25
|
+
createAnalyticsEvent(createEventPayload('ui.inlineDialog.closed.linkPicker', {})).fire(ANALYTICS_CHANNEL);
|
|
26
|
+
};
|
|
27
|
+
}, [createAnalyticsEvent]);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default TrackMount;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import { useLayoutEffect } from 'react';
|
|
3
|
+
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
4
|
+
import createEventPayload from '../../../analytics.codegen';
|
|
5
|
+
import { ANALYTICS_CHANNEL } from '../../../common/constants';
|
|
6
|
+
|
|
7
|
+
function TrackTabViewed({
|
|
8
|
+
activeTab
|
|
9
|
+
}) {
|
|
10
|
+
const {
|
|
11
|
+
createAnalyticsEvent
|
|
12
|
+
} = useAnalyticsEvents();
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
createAnalyticsEvent(createEventPayload('ui.tab.viewed', {})).fire(ANALYTICS_CHANNEL);
|
|
15
|
+
}, [activeTab, createAnalyticsEvent]);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default TrackTabViewed;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
|
|
3
|
+
/** @jsx jsx */
|
|
4
|
+
import React, { useRef, useMemo, useState, useCallback } from 'react';
|
|
5
|
+
import { jsx } from '@emotion/react';
|
|
6
|
+
import Button from '@atlaskit/button';
|
|
7
|
+
import ChevronLeftIcon from '@atlaskit/icon/glyph/chevron-left';
|
|
8
|
+
import ChevronRightIcon from '@atlaskit/icon/glyph/chevron-right';
|
|
9
|
+
import { scrollBack, scrollForward, calculateConditionalButtons, getTabList, createGhost } from './scrolling-tabs';
|
|
10
|
+
import { containerStyles, scrollingContainerStyles, nextButtonStyles, backButtonStyles } from './styles';
|
|
11
|
+
|
|
12
|
+
function isTouchDevice() {
|
|
13
|
+
return 'ontouchstart' in window || // eslint-disable-next-line compat/compat
|
|
14
|
+
navigator.maxTouchPoints > 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const initialConditionalButtonsState = {
|
|
18
|
+
back: false,
|
|
19
|
+
forward: false
|
|
20
|
+
};
|
|
21
|
+
export const ScrollingTabList = props => {
|
|
22
|
+
const ref = useRef(null);
|
|
23
|
+
const [conditionalButtons, setConditionalButtons] = useState(initialConditionalButtonsState);
|
|
24
|
+
const ghost = useMemo(() => createGhost(), []);
|
|
25
|
+
const onTabClick = useCallback(e => {
|
|
26
|
+
const target = e.currentTarget;
|
|
27
|
+
|
|
28
|
+
if (target) {
|
|
29
|
+
target.scrollIntoView({
|
|
30
|
+
behavior: 'smooth'
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
34
|
+
React.useLayoutEffect(() => {
|
|
35
|
+
const container = ref.current;
|
|
36
|
+
let scrollingContainer;
|
|
37
|
+
let tabs;
|
|
38
|
+
|
|
39
|
+
const handleConditionalButtonsChange = () => {
|
|
40
|
+
const buttons = calculateConditionalButtons(scrollingContainer, isTouchDevice());
|
|
41
|
+
setConditionalButtons(buttons);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const observerCallback = mutationList => {
|
|
45
|
+
const tablist = getTabList(ref);
|
|
46
|
+
|
|
47
|
+
for (const mutation of mutationList) {
|
|
48
|
+
if (mutation.type === 'childList') {
|
|
49
|
+
const addedNodes = Array.from(mutation.addedNodes);
|
|
50
|
+
const found = addedNodes.find(node => node.getAttribute('role') === 'tab');
|
|
51
|
+
|
|
52
|
+
if (found && tablist) {
|
|
53
|
+
ghost.remove();
|
|
54
|
+
const tabs = Array.from(tablist.children);
|
|
55
|
+
tabs.forEach(tab => {
|
|
56
|
+
tab.removeEventListener('click', onTabClick);
|
|
57
|
+
tab.addEventListener('click', onTabClick);
|
|
58
|
+
});
|
|
59
|
+
tablist.appendChild(ghost);
|
|
60
|
+
handleConditionalButtonsChange();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const observer = new MutationObserver(observerCallback);
|
|
67
|
+
|
|
68
|
+
if (container) {
|
|
69
|
+
const tablist = getTabList(ref);
|
|
70
|
+
scrollingContainer = tablist === null || tablist === void 0 ? void 0 : tablist.parentElement;
|
|
71
|
+
observer.observe(container, {
|
|
72
|
+
attributes: false,
|
|
73
|
+
childList: true,
|
|
74
|
+
subtree: true
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (scrollingContainer instanceof HTMLElement && tablist) {
|
|
78
|
+
tablist.appendChild(ghost);
|
|
79
|
+
tabs = Array.from(tablist.children);
|
|
80
|
+
tabs.forEach(tab => tab.addEventListener('click', onTabClick));
|
|
81
|
+
handleConditionalButtonsChange();
|
|
82
|
+
scrollingContainer.addEventListener('scroll', handleConditionalButtonsChange);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
if (scrollingContainer) {
|
|
87
|
+
scrollingContainer.removeEventListener('scroll', handleConditionalButtonsChange);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (tabs.length) {
|
|
91
|
+
tabs.forEach(tab => tab.removeEventListener('click', onTabClick));
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}, [onTabClick, ghost, ref]);
|
|
96
|
+
const buttonProps = {
|
|
97
|
+
appearance: 'subtle',
|
|
98
|
+
spacing: 'none'
|
|
99
|
+
};
|
|
100
|
+
return jsx("div", {
|
|
101
|
+
css: containerStyles,
|
|
102
|
+
ref: ref,
|
|
103
|
+
"data-testid": "scrolling-tabs"
|
|
104
|
+
}, conditionalButtons.back && jsx("div", {
|
|
105
|
+
className: "back",
|
|
106
|
+
css: backButtonStyles
|
|
107
|
+
}, jsx(Button, _extends({
|
|
108
|
+
"data-test-id": "back"
|
|
109
|
+
}, buttonProps, {
|
|
110
|
+
iconBefore: jsx(ChevronLeftIcon, {
|
|
111
|
+
label: "back"
|
|
112
|
+
}),
|
|
113
|
+
onClick: () => scrollBack(ref)
|
|
114
|
+
}))), jsx("div", {
|
|
115
|
+
css: scrollingContainerStyles
|
|
116
|
+
}, props.children), conditionalButtons.forward && jsx("div", {
|
|
117
|
+
css: nextButtonStyles
|
|
118
|
+
}, jsx(Button, _extends({
|
|
119
|
+
"data-test-id": "forward"
|
|
120
|
+
}, buttonProps, {
|
|
121
|
+
iconBefore: jsx(ChevronRightIcon, {
|
|
122
|
+
label: "forward"
|
|
123
|
+
}),
|
|
124
|
+
onClick: () => scrollForward(ref)
|
|
125
|
+
}))));
|
|
126
|
+
};
|
|
127
|
+
export default ScrollingTabList;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const BUTTON_SIZE = 24;
|
|
2
|
+
const GUTTER = 48;
|
|
3
|
+
const GHOST_WIDTH = 342 - GUTTER - 40;
|
|
4
|
+
export const scrollBack = ref => {
|
|
5
|
+
moveTabsScroll(ref, findPrevious);
|
|
6
|
+
};
|
|
7
|
+
export const scrollForward = ref => {
|
|
8
|
+
moveTabsScroll(ref, findNext);
|
|
9
|
+
};
|
|
10
|
+
export const calculateConditionalButtons = (container, hideButtons) => {
|
|
11
|
+
const initialState = {
|
|
12
|
+
back: false,
|
|
13
|
+
forward: false
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (!container || hideButtons) {
|
|
17
|
+
return initialState;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const maxScrollValue = container.scrollWidth - container.clientWidth;
|
|
21
|
+
return {
|
|
22
|
+
back: container.scrollLeft > 0,
|
|
23
|
+
forward: container.scrollLeft < maxScrollValue - GHOST_WIDTH
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export const createGhost = () => {
|
|
27
|
+
const ghost = document.createElement('div');
|
|
28
|
+
ghost.setAttribute('id', 'scrolling-tabs-ghost');
|
|
29
|
+
ghost.style.minWidth = `${GHOST_WIDTH}px`;
|
|
30
|
+
ghost.style.pointerEvents = 'none';
|
|
31
|
+
return ghost;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const moveTabsScroll = (ref, findFn) => {
|
|
35
|
+
const container = getContainer(ref);
|
|
36
|
+
|
|
37
|
+
if (container) {
|
|
38
|
+
const tablist = container.children[0];
|
|
39
|
+
const tabs = [...tablist.children];
|
|
40
|
+
|
|
41
|
+
if (tabs.length > 0) {
|
|
42
|
+
const target = findFn(container, tabs);
|
|
43
|
+
|
|
44
|
+
if (target) {
|
|
45
|
+
const left = container.scrollLeft + target.getBoundingClientRect().left - container.getBoundingClientRect().left - BUTTON_SIZE;
|
|
46
|
+
scrollTo(container, left);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const scrollTo = (container, left) => {
|
|
53
|
+
container.scrollTo({
|
|
54
|
+
left
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getTabList = ref => {
|
|
59
|
+
var _ref$current;
|
|
60
|
+
|
|
61
|
+
return (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector('[role="tablist"]');
|
|
62
|
+
};
|
|
63
|
+
export const getContainer = ref => {
|
|
64
|
+
var _getTabList;
|
|
65
|
+
|
|
66
|
+
return (_getTabList = getTabList(ref)) === null || _getTabList === void 0 ? void 0 : _getTabList.parentElement;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const findPrevious = (container, tabs) => {
|
|
70
|
+
const previousTabs = tabs.filter(tab => {
|
|
71
|
+
const [containerRect, tabRect] = [container.getBoundingClientRect(), tab.getBoundingClientRect()];
|
|
72
|
+
const [containerArea, tabArea] = [containerRect.left + container.scrollLeft - GUTTER, container.scrollLeft + tabRect.left + tabRect.width];
|
|
73
|
+
return containerArea > tabArea;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (previousTabs.length < 3) {
|
|
77
|
+
return previousTabs[0];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return previousTabs[previousTabs.length - 1];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const findNext = (container, tabs) => {
|
|
84
|
+
const containerRect = container.getBoundingClientRect();
|
|
85
|
+
return tabs.find(tab => {
|
|
86
|
+
const tabRect = tab.getBoundingClientRect();
|
|
87
|
+
const [containerArea, tabArea] = [container.clientWidth + containerRect.left - GUTTER, tabRect.left + tabRect.width];
|
|
88
|
+
return containerArea < tabArea;
|
|
89
|
+
});
|
|
90
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import { token } from '@atlaskit/tokens';
|
|
3
|
+
import { N0, N30 } from '@atlaskit/theme/colors';
|
|
4
|
+
export const scrollingContainerStyles = css({
|
|
5
|
+
overflowX: 'auto',
|
|
6
|
+
scrollBehavior: 'smooth',
|
|
7
|
+
scrollPadding: '0 24px',
|
|
8
|
+
scrollbarWidth: 'none',
|
|
9
|
+
'&::-webkit-scrollbar': {
|
|
10
|
+
display: 'none'
|
|
11
|
+
},
|
|
12
|
+
'&::before': {
|
|
13
|
+
content: '""',
|
|
14
|
+
borderRadius: 2,
|
|
15
|
+
bottom: 0,
|
|
16
|
+
margin: 0,
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
width: 'inherit',
|
|
19
|
+
left: 8,
|
|
20
|
+
right: 8,
|
|
21
|
+
height: 2,
|
|
22
|
+
backgroundColor: token('color.border', N30)
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
export const containerStyles = css({
|
|
26
|
+
position: 'relative',
|
|
27
|
+
'[role="tablist"]': {
|
|
28
|
+
'&::before': {
|
|
29
|
+
display: 'none'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
// Overrides Atlaskit tabs styles to stop overflowing with ellipsis
|
|
33
|
+
'[role="tab"]': {
|
|
34
|
+
overflow: 'unset !important',
|
|
35
|
+
textOverflow: 'unset !important'
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export const buttonContainerStyles = css({
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
top: 4,
|
|
41
|
+
zIndex: 999,
|
|
42
|
+
backgroundColor: token('elevation.surface', N0)
|
|
43
|
+
});
|
|
44
|
+
export const backButtonStyles = css([buttonContainerStyles, {
|
|
45
|
+
left: 0
|
|
46
|
+
}]);
|
|
47
|
+
export const nextButtonStyles = css([buttonContainerStyles, {
|
|
48
|
+
right: 0
|
|
49
|
+
}]);
|
package/dist/es2019/version.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generates analytics utilities from the package analytics spec yaml
|
|
5
5
|
*
|
|
6
|
-
* @codegen <<SignedSource::
|
|
6
|
+
* @codegen <<SignedSource::c0fb3bb39f50f3a7c35ebd90e8903280>>
|
|
7
7
|
* @codegenCommand yarn workspace @atlaskit/link-picker run codegen-analytics
|
|
8
8
|
*/
|
|
9
9
|
function createEventPayload(eventKey, attributes) {
|
|
@@ -16,7 +16,8 @@ var DEFAULT_CONTEXT_ATTRIBUTES = {
|
|
|
16
16
|
linkFieldContentInputMethod: null,
|
|
17
17
|
linkFieldContentInputSource: null,
|
|
18
18
|
displayTextFieldContent: null,
|
|
19
|
-
displayTextFieldContentInputMethod: null
|
|
19
|
+
displayTextFieldContentInputMethod: null,
|
|
20
|
+
tab: null
|
|
20
21
|
};
|
|
21
22
|
var LinkPickerAnalyticsContext = /*#__PURE__*/React.createContext({
|
|
22
23
|
trackAttribute: function trackAttribute() {},
|
|
@@ -27,13 +28,18 @@ var LinkPickerAnalyticsContext = /*#__PURE__*/React.createContext({
|
|
|
27
28
|
});
|
|
28
29
|
/**
|
|
29
30
|
* TODO: Should we hook this into the form state/reducer?
|
|
31
|
+
* This function continues to duplicate / assume logic that runs in the picker
|
|
30
32
|
*/
|
|
31
33
|
|
|
32
34
|
var contextAttributesFromIntialProps = function contextAttributesFromIntialProps(props) {
|
|
35
|
+
var _props$plugins$0$tabK, _props$plugins, _props$plugins$;
|
|
36
|
+
|
|
33
37
|
return {
|
|
34
38
|
linkState: normalizeUrl(props.url) ? 'editLink' : 'newLink',
|
|
35
39
|
linkFieldContent: normalizeUrl(props.url) ? 'url' : null,
|
|
36
|
-
displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null
|
|
40
|
+
displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null,
|
|
41
|
+
// TODO: Assumes we alway load the first plugin as the current tab
|
|
42
|
+
tab: (_props$plugins$0$tabK = (_props$plugins = props.plugins) === null || _props$plugins === void 0 ? void 0 : (_props$plugins$ = _props$plugins[0]) === null || _props$plugins$ === void 0 ? void 0 : _props$plugins$.tabKey) !== null && _props$plugins$0$tabK !== void 0 ? _props$plugins$0$tabK : null
|
|
37
43
|
};
|
|
38
44
|
};
|
|
39
45
|
/**
|