@atlaskit/link-picker 0.0.2 → 0.8.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 +152 -0
- package/README.md +27 -5
- package/dist/cjs/browser.js +39 -0
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/messages.js +102 -0
- package/dist/cjs/styles.js +90 -0
- package/dist/cjs/transformTimeStamp.js +55 -0
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/ui/LinkSearchList.js +112 -0
- package/dist/cjs/ui/LinkSearchListItem.js +101 -0
- package/dist/cjs/ui/PanelTextInput.js +119 -0
- package/dist/cjs/ui/announcer.js +64 -0
- package/dist/cjs/ui/link-picker.js +582 -0
- package/dist/cjs/url.js +104 -0
- package/dist/cjs/utils.js +40 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/browser.js +34 -0
- package/dist/es2019/index.js +1 -1
- package/dist/es2019/messages.js +93 -0
- package/dist/es2019/styles.js +93 -0
- package/dist/es2019/transformTimeStamp.js +41 -0
- package/dist/es2019/types.js +1 -0
- package/dist/es2019/ui/LinkSearchList.js +65 -0
- package/dist/es2019/ui/LinkSearchListItem.js +88 -0
- package/dist/es2019/ui/PanelTextInput.js +93 -0
- package/dist/es2019/ui/announcer.js +38 -0
- package/dist/es2019/ui/link-picker.js +481 -0
- package/dist/es2019/url.js +88 -0
- package/dist/es2019/utils.js +24 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/browser.js +34 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/messages.js +93 -0
- package/dist/esm/styles.js +46 -0
- package/dist/esm/transformTimeStamp.js +41 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/ui/LinkSearchList.js +87 -0
- package/dist/esm/ui/LinkSearchListItem.js +88 -0
- package/dist/esm/ui/PanelTextInput.js +93 -0
- package/dist/esm/ui/announcer.js +51 -0
- package/dist/esm/ui/link-picker.js +560 -0
- package/dist/esm/url.js +90 -0
- package/dist/esm/utils.js +24 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/browser.d.ts +15 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/messages.d.ts +92 -0
- package/dist/types/styles.d.ts +18 -0
- package/dist/types/transformTimeStamp.d.ts +3 -0
- package/dist/types/types.d.ts +37 -0
- package/dist/types/ui/LinkSearchList.d.ts +20 -0
- package/dist/types/ui/LinkSearchListItem.d.ts +17 -0
- package/dist/types/ui/PanelTextInput.d.ts +17 -0
- package/dist/types/ui/announcer.d.ts +22 -0
- package/dist/types/ui/link-picker.d.ts +51 -0
- package/dist/types/url.d.ts +5 -0
- package/dist/types/utils.d.ts +4 -0
- package/example-helpers/flight-deck/_manifest.json +9 -0
- package/example-helpers/flight-deck/mockFDResponse.json +133 -0
- package/example-helpers/mock-data.ts +316 -0
- package/example-helpers/providers.ts +39 -0
- package/package.json +35 -6
- package/dist/cjs/link-picker.js +0 -12
- package/dist/es2019/link-picker.js +0 -3
- package/dist/esm/link-picker.js +0 -3
- package/dist/types/link-picker.d.ts +0 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.relativeFontSizeToBase16 = exports.isUndoEvent = exports.isRedoEvent = void 0;
|
|
7
|
+
|
|
8
|
+
var _browser = require("./browser");
|
|
9
|
+
|
|
10
|
+
var relativeFontSizeToBase16 = function relativeFontSizeToBase16(px) {
|
|
11
|
+
if (typeof px === 'string') {
|
|
12
|
+
px = parseInt(px);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (isNaN(px)) {
|
|
16
|
+
throw new Error("Invalid font size: '".concat(px, "'"));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return "".concat(px / 16, "rem");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
exports.relativeFontSizeToBase16 = relativeFontSizeToBase16;
|
|
23
|
+
var KeyZCode = 90;
|
|
24
|
+
var KeyYCode = 89;
|
|
25
|
+
|
|
26
|
+
var isUndoEvent = function isUndoEvent(e) {
|
|
27
|
+
return e.keyCode === KeyZCode && ( // cmd + z for mac
|
|
28
|
+
_browser.browser.mac && e.metaKey && !e.shiftKey || // ctrl + z for non-mac
|
|
29
|
+
!_browser.browser.mac && e.ctrlKey);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
exports.isUndoEvent = isUndoEvent;
|
|
33
|
+
|
|
34
|
+
var isRedoEvent = function isRedoEvent(e) {
|
|
35
|
+
return (// ctrl + y for non-mac
|
|
36
|
+
!_browser.browser.mac && e.ctrlKey && e.keyCode === KeyYCode || _browser.browser.mac && e.metaKey && e.shiftKey && e.keyCode === KeyZCode || e.ctrlKey && e.shiftKey && e.keyCode === KeyZCode
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
exports.isRedoEvent = isRedoEvent;
|
package/dist/cjs/version.json
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const result = {
|
|
2
|
+
mac: false,
|
|
3
|
+
ie: false,
|
|
4
|
+
ie_version: 0,
|
|
5
|
+
gecko: false,
|
|
6
|
+
chrome: false,
|
|
7
|
+
chrome_version: 0,
|
|
8
|
+
android: false,
|
|
9
|
+
ios: false,
|
|
10
|
+
webkit: false,
|
|
11
|
+
safari: false,
|
|
12
|
+
supportsIntersectionObserver: false,
|
|
13
|
+
supportsResizeObserver: false
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (typeof navigator !== 'undefined') {
|
|
17
|
+
const ieEdge = /Edge\/(\d+)/.exec(navigator.userAgent);
|
|
18
|
+
const ieUpTo10 = /MSIE \d/.test(navigator.userAgent);
|
|
19
|
+
const ie11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
|
|
20
|
+
result.mac = /Mac/.test(navigator.platform);
|
|
21
|
+
let ie = result.ie = !!(ieUpTo10 || ie11up || ieEdge);
|
|
22
|
+
result.ie_version = ieUpTo10 ? document.documentMode || 6 : ie11up ? +ie11up[1] : ieEdge ? +ieEdge[1] : null;
|
|
23
|
+
result.gecko = !ie && /gecko\/\d/i.test(navigator.userAgent);
|
|
24
|
+
result.chrome = !ie && /Chrome\//.test(navigator.userAgent);
|
|
25
|
+
result.chrome_version = parseInt((navigator.userAgent.match(/Chrome\/(\d+)/) || [])[1], 10);
|
|
26
|
+
result.android = /Android \d/.test(navigator.userAgent);
|
|
27
|
+
result.ios = !ie && /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
|
|
28
|
+
result.webkit = !ie && !!document.documentElement && 'WebkitAppearance' in document.documentElement.style;
|
|
29
|
+
result.safari = Boolean(navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && navigator.userAgent.indexOf('CriOS') === -1 && navigator.userAgent.indexOf('FxiOS') === -1);
|
|
30
|
+
result.supportsIntersectionObserver = typeof window !== 'undefined' && 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype;
|
|
31
|
+
result.supportsResizeObserver = typeof window !== 'undefined' && 'ResizeObserver' in window && 'ResizeObserverEntry' in window;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { result as browser };
|
package/dist/es2019/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { LinkPicker } from './link-picker';
|
|
1
|
+
export { LinkPickerWithIntl as LinkPicker } from './ui/link-picker';
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl-next';
|
|
2
|
+
export const messages = defineMessages({
|
|
3
|
+
placeholder: {
|
|
4
|
+
id: 'fabric.linkPicker.hyperlinkToolbarPlaceholder',
|
|
5
|
+
defaultMessage: 'Search or paste a link',
|
|
6
|
+
description: 'Search or paste a link'
|
|
7
|
+
},
|
|
8
|
+
linkPlaceholder: {
|
|
9
|
+
id: 'fabric.linkPicker.linkPlaceholder',
|
|
10
|
+
defaultMessage: 'Paste link',
|
|
11
|
+
description: 'Create a new link by pasting a URL.'
|
|
12
|
+
},
|
|
13
|
+
linkTextPlaceholder: {
|
|
14
|
+
id: 'fabric.linkPicker.linkTextPlaceholder',
|
|
15
|
+
defaultMessage: 'Add a description',
|
|
16
|
+
description: 'Text to display'
|
|
17
|
+
},
|
|
18
|
+
linkLabel: {
|
|
19
|
+
id: 'fabric.linkPicker.linkAddress',
|
|
20
|
+
defaultMessage: 'Link',
|
|
21
|
+
description: 'Link Label'
|
|
22
|
+
},
|
|
23
|
+
linkInvalid: {
|
|
24
|
+
id: 'fabric.linkPicker.linkInvalid',
|
|
25
|
+
defaultMessage: 'Enter a valid URL.',
|
|
26
|
+
description: 'Input Error the URL is invalid'
|
|
27
|
+
},
|
|
28
|
+
linkTextLabel: {
|
|
29
|
+
id: 'fabric.linkPicker.displayText',
|
|
30
|
+
defaultMessage: 'Link text',
|
|
31
|
+
description: 'Link text label'
|
|
32
|
+
},
|
|
33
|
+
clearText: {
|
|
34
|
+
id: 'fabric.linkPicker.clearLinkText',
|
|
35
|
+
defaultMessage: 'Clear text',
|
|
36
|
+
description: 'Clears text on the link toolbar'
|
|
37
|
+
},
|
|
38
|
+
clearLink: {
|
|
39
|
+
id: 'fabric.linkPicker.clearLink',
|
|
40
|
+
defaultMessage: 'Clear link',
|
|
41
|
+
description: 'Clears link in the link toolbar'
|
|
42
|
+
},
|
|
43
|
+
searchLinkAriaDescription: {
|
|
44
|
+
id: 'fabric.linkPicker.hyperlink.searchLinkAriaDescription',
|
|
45
|
+
defaultMessage: 'Suggestions will appear below as you type into the field',
|
|
46
|
+
description: 'Describes what the search field does for screen reader users.'
|
|
47
|
+
},
|
|
48
|
+
searchLinkResults: {
|
|
49
|
+
id: 'fabric.linkPicker.hyperlink.searchLinkResults',
|
|
50
|
+
defaultMessage: '{count, plural, =0 {no results} one {# result} other {# results}} found',
|
|
51
|
+
description: 'Announce search results for screen-reader users.'
|
|
52
|
+
},
|
|
53
|
+
linkAriaLabel: {
|
|
54
|
+
id: 'fabric.linkPicker.hyperlink.linkAriaLabel',
|
|
55
|
+
defaultMessage: 'Link label',
|
|
56
|
+
description: 'aria label for a link'
|
|
57
|
+
},
|
|
58
|
+
timeUpdated: {
|
|
59
|
+
id: 'fabric.linkPicker.time.updated',
|
|
60
|
+
defaultMessage: 'Updated',
|
|
61
|
+
description: 'Time last updated'
|
|
62
|
+
},
|
|
63
|
+
timeViewed: {
|
|
64
|
+
id: 'fabric.linkPicker.time.viewed',
|
|
65
|
+
defaultMessage: 'Viewed',
|
|
66
|
+
description: 'Time last viewed'
|
|
67
|
+
},
|
|
68
|
+
timeAgo: {
|
|
69
|
+
id: 'fabric.linkPicker.time.ago',
|
|
70
|
+
defaultMessage: 'ago',
|
|
71
|
+
description: 'Some time ago'
|
|
72
|
+
},
|
|
73
|
+
titleRecentlyViewed: {
|
|
74
|
+
id: 'fabric.linkPicker.listTitle.recentlyViewed',
|
|
75
|
+
defaultMessage: 'Recently Viewed',
|
|
76
|
+
description: 'Describes type of items shown in the list for screen-reader users'
|
|
77
|
+
},
|
|
78
|
+
titleResults: {
|
|
79
|
+
id: 'fabric.linkPicker.listTitle.results',
|
|
80
|
+
defaultMessage: 'Results',
|
|
81
|
+
description: 'Describes type of items shown in the list for screen-reader users'
|
|
82
|
+
},
|
|
83
|
+
cancelButton: {
|
|
84
|
+
id: 'fabric.linkPicker.cancel.button',
|
|
85
|
+
defaultMessage: 'Cancel',
|
|
86
|
+
description: 'Button to cancel and dismiss the link picker'
|
|
87
|
+
},
|
|
88
|
+
insertButton: {
|
|
89
|
+
id: 'fabric.linkPicker.cancel.insert',
|
|
90
|
+
defaultMessage: 'Insert',
|
|
91
|
+
description: 'Button to insert searched or selected link'
|
|
92
|
+
}
|
|
93
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { token } from '@atlaskit/tokens';
|
|
3
|
+
import { css } from '@emotion/core';
|
|
4
|
+
import { N500, B50, N300, N800, N20, B400 } from '@atlaskit/theme/colors';
|
|
5
|
+
import { gridSize } from '@atlaskit/theme/constants';
|
|
6
|
+
import { relativeFontSizeToBase16 } from './utils'; // AFP-2532 TODO: Fix automatic suppressions below
|
|
7
|
+
// eslint-disable-next-line @atlassian/tangerine/import/entry-points
|
|
8
|
+
|
|
9
|
+
import { fontSizeSmall, fontSize, typography } from '@atlaskit/theme';
|
|
10
|
+
const LINK_PICKER_WIDTH_IN_PX = 342;
|
|
11
|
+
export const recentListStyles = css`
|
|
12
|
+
width: ${LINK_PICKER_WIDTH_IN_PX}px;
|
|
13
|
+
`;
|
|
14
|
+
export const Container = styled.div`
|
|
15
|
+
padding: ${gridSize() * 2}px;
|
|
16
|
+
width: 100%;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
line-height: initial;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
`;
|
|
21
|
+
export const ClearText = styled.button`
|
|
22
|
+
padding: 0;
|
|
23
|
+
margin-right: ${gridSize() / 2}px;
|
|
24
|
+
color: ${token('color.icon.subtle', N500)};
|
|
25
|
+
background: transparent;
|
|
26
|
+
border: none;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
`;
|
|
29
|
+
export const FieldWrapper = styled.div`
|
|
30
|
+
margin-bottom: ${gridSize() * 2}px;
|
|
31
|
+
`;
|
|
32
|
+
export const SearchIconWrapper = styled.span`
|
|
33
|
+
margin-left: ${gridSize() / 2}px;
|
|
34
|
+
color: ${token('color.icon', N500)};
|
|
35
|
+
cursor: default;
|
|
36
|
+
`;
|
|
37
|
+
export const ListTitleStyles = css`
|
|
38
|
+
${typography.h100()}
|
|
39
|
+
text-transform: uppercase;
|
|
40
|
+
margin-bottom: ${gridSize() / 2}px;
|
|
41
|
+
`;
|
|
42
|
+
export const ListItemWrapper = styled.li`
|
|
43
|
+
display: flex;
|
|
44
|
+
padding: ${gridSize()}px ${gridSize() * 2}px;
|
|
45
|
+
margin: 0 -${gridSize() * 2}px;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
background-color: ${props => {
|
|
48
|
+
switch (true) {
|
|
49
|
+
case props.selected:
|
|
50
|
+
return token('color.background.selected', B50);
|
|
51
|
+
|
|
52
|
+
case props.active:
|
|
53
|
+
return token('color.background.neutral.subtle.hovered', N20);
|
|
54
|
+
|
|
55
|
+
default:
|
|
56
|
+
return 'transparent';
|
|
57
|
+
}
|
|
58
|
+
}};
|
|
59
|
+
box-shadow: inset 2px 0px 0px
|
|
60
|
+
${props => {
|
|
61
|
+
switch (true) {
|
|
62
|
+
case props.active:
|
|
63
|
+
case props.selected:
|
|
64
|
+
return token('color.border.selected', B400);
|
|
65
|
+
|
|
66
|
+
default:
|
|
67
|
+
return 'transparent';
|
|
68
|
+
}
|
|
69
|
+
}};
|
|
70
|
+
`;
|
|
71
|
+
export const ItemNameWrapper = styled.span`
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
`;
|
|
74
|
+
export const ListItemName = styled.div`
|
|
75
|
+
color: ${token('color.text', N800)};
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
text-overflow: ellipsis;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
line-height: 20px;
|
|
80
|
+
`;
|
|
81
|
+
export const ListItemContext = styled.div`
|
|
82
|
+
color: ${token('color.text', N300)};
|
|
83
|
+
font-size: ${relativeFontSizeToBase16(fontSizeSmall())};
|
|
84
|
+
line-height: ${fontSize()}px;
|
|
85
|
+
`;
|
|
86
|
+
export const ItemIconWrapper = styled.span`
|
|
87
|
+
min-width: ${gridSize() * 2}px;
|
|
88
|
+
margin-top: 3px;
|
|
89
|
+
margin-right: 12px;
|
|
90
|
+
`;
|
|
91
|
+
export const ImgStyles = css`
|
|
92
|
+
max-width: ${gridSize() * 2}px;
|
|
93
|
+
`;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import formatDistance from 'date-fns/formatDistance';
|
|
2
|
+
import differenceInCalendarDays from 'date-fns/differenceInCalendarDays';
|
|
3
|
+
import format from 'date-fns/format';
|
|
4
|
+
import { messages } from './messages';
|
|
5
|
+
|
|
6
|
+
const renderAbsoluteOrRelativeDate = (timeStamp, pageAction, intl) => {
|
|
7
|
+
let pageActionText = '';
|
|
8
|
+
|
|
9
|
+
switch (pageAction) {
|
|
10
|
+
case 'updated':
|
|
11
|
+
pageActionText = intl.formatMessage(messages.timeUpdated);
|
|
12
|
+
break;
|
|
13
|
+
|
|
14
|
+
case 'viewed':
|
|
15
|
+
pageActionText = intl.formatMessage(messages.timeViewed);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (differenceInCalendarDays(timeStamp, Date.now()) < -7) {
|
|
20
|
+
return {
|
|
21
|
+
pageAction: pageActionText,
|
|
22
|
+
dateString: format(timeStamp, 'MMMM dd, yyyy')
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
pageAction: pageActionText,
|
|
28
|
+
dateString: formatDistance(timeStamp, Date.now()),
|
|
29
|
+
timeSince: intl.formatMessage(messages.timeAgo)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const transformTimeStamp = (intl, lastViewedDate, lastUpdatedDate) => {
|
|
34
|
+
if (lastViewedDate) {
|
|
35
|
+
return renderAbsoluteOrRelativeDate(lastViewedDate, 'viewed', intl);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (lastUpdatedDate) {
|
|
39
|
+
return renderAbsoluteOrRelativeDate(lastUpdatedDate, 'updated', intl);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PureComponent } from 'react';
|
|
3
|
+
import Spinner from '@atlaskit/spinner';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import LinkSearchListItem from './LinkSearchListItem';
|
|
6
|
+
const ListContainer = styled.div`
|
|
7
|
+
padding-top: 0;
|
|
8
|
+
`;
|
|
9
|
+
const SpinnerContainer = styled.div`
|
|
10
|
+
text-align: center;
|
|
11
|
+
min-height: 80px;
|
|
12
|
+
margin-top: 30px;
|
|
13
|
+
`;
|
|
14
|
+
export const List = styled.ul`
|
|
15
|
+
padding: 0;
|
|
16
|
+
list-style: none;
|
|
17
|
+
`;
|
|
18
|
+
export default class LinkSearchList extends PureComponent {
|
|
19
|
+
render() {
|
|
20
|
+
const {
|
|
21
|
+
onSelect,
|
|
22
|
+
onMouseEnter,
|
|
23
|
+
onMouseLeave,
|
|
24
|
+
items,
|
|
25
|
+
activeIndex,
|
|
26
|
+
selectedIndex,
|
|
27
|
+
isLoading,
|
|
28
|
+
ariaControls,
|
|
29
|
+
ariaLabelledBy,
|
|
30
|
+
role,
|
|
31
|
+
id
|
|
32
|
+
} = this.props;
|
|
33
|
+
let itemsContent;
|
|
34
|
+
let loadingContent;
|
|
35
|
+
|
|
36
|
+
if (items && items.length > 0) {
|
|
37
|
+
itemsContent = /*#__PURE__*/React.createElement(List, {
|
|
38
|
+
id: id,
|
|
39
|
+
role: role,
|
|
40
|
+
"aria-controls": ariaControls,
|
|
41
|
+
"aria-labelledby": ariaLabelledBy
|
|
42
|
+
}, items.map((item, index) => /*#__PURE__*/React.createElement(LinkSearchListItem, {
|
|
43
|
+
id: `link-search-list-item-${index}`,
|
|
44
|
+
role: role && 'option',
|
|
45
|
+
item: item,
|
|
46
|
+
selected: selectedIndex === index,
|
|
47
|
+
active: activeIndex === index,
|
|
48
|
+
onMouseEnter: onMouseEnter,
|
|
49
|
+
onMouseLeave: onMouseLeave,
|
|
50
|
+
onSelect: onSelect,
|
|
51
|
+
key: item.objectId
|
|
52
|
+
})));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isLoading) {
|
|
56
|
+
loadingContent = /*#__PURE__*/React.createElement(SpinnerContainer, null, /*#__PURE__*/React.createElement(Spinner, {
|
|
57
|
+
testId: "link-picker.results-loading-indicator",
|
|
58
|
+
size: "medium"
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return /*#__PURE__*/React.createElement(ListContainer, null, itemsContent, loadingContent);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import { jsx } from '@emotion/core';
|
|
4
|
+
import { injectIntl } from 'react-intl-next';
|
|
5
|
+
import { transformTimeStamp } from '../transformTimeStamp';
|
|
6
|
+
import { ItemNameWrapper, ItemIconWrapper, ListItemContext, ListItemName, ListItemWrapper, ImgStyles } from '../styles';
|
|
7
|
+
|
|
8
|
+
const LinkSearchListItem = ({
|
|
9
|
+
item,
|
|
10
|
+
selected,
|
|
11
|
+
active,
|
|
12
|
+
id,
|
|
13
|
+
role,
|
|
14
|
+
intl,
|
|
15
|
+
onSelect,
|
|
16
|
+
onMouseEnter,
|
|
17
|
+
onMouseLeave
|
|
18
|
+
}) => {
|
|
19
|
+
const handleSelect = () => onSelect(item.objectId);
|
|
20
|
+
|
|
21
|
+
const handleMouseEnter = () => onMouseEnter(item.objectId);
|
|
22
|
+
|
|
23
|
+
const handleMouseLeave = () => onMouseLeave(item.objectId);
|
|
24
|
+
|
|
25
|
+
return jsx(ListItemWrapper, {
|
|
26
|
+
role: role,
|
|
27
|
+
id: id,
|
|
28
|
+
"aria-selected": selected,
|
|
29
|
+
"data-testid": "link-search-list-item",
|
|
30
|
+
selected: selected,
|
|
31
|
+
active: active,
|
|
32
|
+
onMouseEnter: handleMouseEnter,
|
|
33
|
+
onMouseLeave: handleMouseLeave,
|
|
34
|
+
onClick: handleSelect
|
|
35
|
+
}, jsx(ListItemIcon, {
|
|
36
|
+
item: item,
|
|
37
|
+
intl: intl
|
|
38
|
+
}), jsx(ItemNameWrapper, null, jsx(ListItemName, null, item.name), jsx(ListItemContext, null, item.container, jsx(ItemTimeStamp, {
|
|
39
|
+
date: transformTimeStamp(intl, item.lastViewedDate, item.lastUpdatedDate)
|
|
40
|
+
}))));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default injectIntl(LinkSearchListItem);
|
|
44
|
+
|
|
45
|
+
const ItemTimeStamp = props => {
|
|
46
|
+
const {
|
|
47
|
+
date
|
|
48
|
+
} = props;
|
|
49
|
+
|
|
50
|
+
if (!date) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return jsx(Fragment, null, "\xA0 \u2022", jsx("span", {
|
|
55
|
+
className: "link-search-timestamp",
|
|
56
|
+
"data-test-id": "link-search-timestamp"
|
|
57
|
+
}, "\xA0 ", date.pageAction, " ", date.dateString, " ", date.timeSince || ''));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const ListItemIcon = props => {
|
|
61
|
+
const {
|
|
62
|
+
item,
|
|
63
|
+
intl
|
|
64
|
+
} = props;
|
|
65
|
+
const {
|
|
66
|
+
icon,
|
|
67
|
+
iconAlt
|
|
68
|
+
} = item;
|
|
69
|
+
|
|
70
|
+
if (!icon) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const alt = typeof iconAlt === 'string' ? iconAlt : intl.formatMessage(iconAlt);
|
|
75
|
+
|
|
76
|
+
if (typeof icon !== 'string') {
|
|
77
|
+
const Glyph = icon;
|
|
78
|
+
return jsx(ItemIconWrapper, null, jsx(Glyph, {
|
|
79
|
+
alt: alt
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return jsx(ItemIconWrapper, null, jsx("img", {
|
|
84
|
+
src: icon,
|
|
85
|
+
alt: alt,
|
|
86
|
+
css: ImgStyles
|
|
87
|
+
}));
|
|
88
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React, { useCallback, useRef } from 'react';
|
|
3
|
+
import { ErrorMessage, Field } from '@atlaskit/form';
|
|
4
|
+
import Tooltip from '@atlaskit/tooltip';
|
|
5
|
+
import Textfield from '@atlaskit/textfield';
|
|
6
|
+
import Selectclear from '@atlaskit/icon/glyph/select-clear';
|
|
7
|
+
import { ClearText, FieldWrapper } from '../styles';
|
|
8
|
+
import { isRedoEvent, isUndoEvent } from '../utils';
|
|
9
|
+
|
|
10
|
+
const PanelTextInput = props => {
|
|
11
|
+
const {
|
|
12
|
+
name,
|
|
13
|
+
label,
|
|
14
|
+
autoFocus,
|
|
15
|
+
onSubmit,
|
|
16
|
+
onRedo,
|
|
17
|
+
onUndo,
|
|
18
|
+
onChange,
|
|
19
|
+
onKeyDown,
|
|
20
|
+
onClear,
|
|
21
|
+
clearLabel,
|
|
22
|
+
error,
|
|
23
|
+
...restProps
|
|
24
|
+
} = props;
|
|
25
|
+
const inputRef = useRef(null);
|
|
26
|
+
const handleRef = useCallback(input => {
|
|
27
|
+
if (input) {
|
|
28
|
+
inputRef.current = input;
|
|
29
|
+
|
|
30
|
+
if (autoFocus) {
|
|
31
|
+
// Need this to prevent jumping when we render TextInput inside Portal @see ED-2992
|
|
32
|
+
input.focus({
|
|
33
|
+
preventScroll: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, [autoFocus]);
|
|
38
|
+
const handleChange = useCallback(e => {
|
|
39
|
+
onChange(e.currentTarget.value);
|
|
40
|
+
}, [onChange]);
|
|
41
|
+
const handleKeydown = useCallback(e => {
|
|
42
|
+
const value = e.currentTarget.value;
|
|
43
|
+
|
|
44
|
+
if (e.keyCode === 13 && onSubmit) {
|
|
45
|
+
e.preventDefault(); // Prevent from submitting if an editor is inside a form.
|
|
46
|
+
|
|
47
|
+
onSubmit(value);
|
|
48
|
+
} else if (typeof onUndo === 'function' && isUndoEvent(e)) {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
onUndo();
|
|
51
|
+
} else if (typeof onRedo === 'function' && isRedoEvent(e)) {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
onRedo();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (onKeyDown) {
|
|
57
|
+
onKeyDown(e);
|
|
58
|
+
}
|
|
59
|
+
}, [onSubmit, onUndo, onRedo, onKeyDown]);
|
|
60
|
+
const handleClear = useCallback(() => {
|
|
61
|
+
if (inputRef.current && onClear) {
|
|
62
|
+
inputRef.current.focus();
|
|
63
|
+
onClear();
|
|
64
|
+
}
|
|
65
|
+
}, [onClear]);
|
|
66
|
+
const clearText = restProps.value !== '' && /*#__PURE__*/React.createElement(Tooltip, {
|
|
67
|
+
content: clearLabel
|
|
68
|
+
}, /*#__PURE__*/React.createElement(ClearText, {
|
|
69
|
+
onClick: handleClear,
|
|
70
|
+
"data-testid": "clear-text"
|
|
71
|
+
}, /*#__PURE__*/React.createElement(Selectclear, {
|
|
72
|
+
size: "medium",
|
|
73
|
+
label: clearLabel || ''
|
|
74
|
+
})));
|
|
75
|
+
return /*#__PURE__*/React.createElement(FieldWrapper, null, /*#__PURE__*/React.createElement(Field, {
|
|
76
|
+
label: label,
|
|
77
|
+
name: name
|
|
78
|
+
}, ({
|
|
79
|
+
fieldProps
|
|
80
|
+
}) => {
|
|
81
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Textfield, _extends({}, fieldProps, restProps, {
|
|
82
|
+
onKeyDown: handleKeydown,
|
|
83
|
+
onChange: handleChange,
|
|
84
|
+
ref: handleRef,
|
|
85
|
+
elemAfterInput: clearText,
|
|
86
|
+
isInvalid: !!error
|
|
87
|
+
})), error && /*#__PURE__*/React.createElement(ErrorMessage, {
|
|
88
|
+
testId: "link-error"
|
|
89
|
+
}, error));
|
|
90
|
+
}));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default PanelTextInput;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { forwardRef, memo, useState, useEffect } from 'react'; // How to use:
|
|
2
|
+
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions#Preferring_specialized_live_region_roles
|
|
3
|
+
// Demo: https://dequeuniversity.com/library/aria/liveregion-playground
|
|
4
|
+
// Important: Strongly recommend test your solution in all supported screen readers
|
|
5
|
+
// if you use non default value of properties: ariaAtomic, ariaLive, ariaRelevant, role
|
|
6
|
+
// Note: Text won't be announced if the text message doesn't change after the render.
|
|
7
|
+
// For using a forced announcement in this case, set the 'key' attribute - key={Date.now()}
|
|
8
|
+
|
|
9
|
+
// Note: Flag 'contentRendered' resolves bug with duplicates messages (NVDA + Firefox)
|
|
10
|
+
// https://github.com/nvaccess/nvda/labels/bug%2Fdouble-speaking
|
|
11
|
+
let timer;
|
|
12
|
+
const Announcer = /*#__PURE__*/forwardRef(({
|
|
13
|
+
ariaAtomic = 'true',
|
|
14
|
+
ariaLive = 'polite',
|
|
15
|
+
ariaRelevant = 'all',
|
|
16
|
+
role = 'status',
|
|
17
|
+
text = '',
|
|
18
|
+
delay = 0
|
|
19
|
+
}, ref) => {
|
|
20
|
+
const [contentRendered, setContentRendered] = useState(false);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
setContentRendered(false);
|
|
24
|
+
timer = setTimeout(() => {
|
|
25
|
+
setContentRendered(true);
|
|
26
|
+
}, delay);
|
|
27
|
+
return () => clearTimeout(timer);
|
|
28
|
+
}, [text, delay]);
|
|
29
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
30
|
+
className: "assistive",
|
|
31
|
+
ref: ref,
|
|
32
|
+
role: role,
|
|
33
|
+
"aria-atomic": ariaAtomic,
|
|
34
|
+
"aria-relevant": ariaRelevant,
|
|
35
|
+
"aria-live": ariaLive
|
|
36
|
+
}, contentRendered && /*#__PURE__*/React.createElement("span", null, text));
|
|
37
|
+
});
|
|
38
|
+
export default /*#__PURE__*/memo(Announcer);
|