@atlaskit/link-picker 0.11.0 → 1.1.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 +18 -0
- package/example-helpers/mock-data.ts +0 -2
- package/package.json +7 -4
- package/report.api.md +65 -0
- package/dist/cjs/browser.js +0 -39
- package/dist/cjs/index.js +0 -13
- package/dist/cjs/messages.js +0 -102
- package/dist/cjs/transformTimeStamp.js +0 -55
- package/dist/cjs/types.js +0 -5
- package/dist/cjs/ui/announcer.js +0 -64
- package/dist/cjs/ui/link-picker.js +0 -604
- package/dist/cjs/ui/link-search-list/index.js +0 -102
- package/dist/cjs/ui/link-search-list/styled.js +0 -21
- package/dist/cjs/ui/list-item/index.js +0 -103
- package/dist/cjs/ui/list-item/styled.js +0 -59
- package/dist/cjs/ui/styled.js +0 -32
- package/dist/cjs/ui/text-input/index.js +0 -116
- package/dist/cjs/ui/text-input/styled.js +0 -25
- package/dist/cjs/url.js +0 -104
- package/dist/cjs/utils.js +0 -27
- package/dist/cjs/version.json +0 -5
- package/dist/es2019/browser.js +0 -34
- package/dist/es2019/index.js +0 -1
- package/dist/es2019/messages.js +0 -93
- package/dist/es2019/transformTimeStamp.js +0 -41
- package/dist/es2019/types.js +0 -1
- package/dist/es2019/ui/announcer.js +0 -38
- package/dist/es2019/ui/link-picker.js +0 -493
- package/dist/es2019/ui/link-search-list/index.js +0 -60
- package/dist/es2019/ui/link-search-list/styled.js +0 -13
- package/dist/es2019/ui/list-item/index.js +0 -88
- package/dist/es2019/ui/list-item/styled.js +0 -65
- package/dist/es2019/ui/styled.js +0 -28
- package/dist/es2019/ui/text-input/index.js +0 -99
- package/dist/es2019/ui/text-input/styled.js +0 -15
- package/dist/es2019/url.js +0 -88
- package/dist/es2019/utils.js +0 -13
- package/dist/es2019/version.json +0 -5
- package/dist/esm/browser.js +0 -34
- package/dist/esm/index.js +0 -1
- package/dist/esm/messages.js +0 -93
- package/dist/esm/transformTimeStamp.js +0 -41
- package/dist/esm/types.js +0 -1
- package/dist/esm/ui/announcer.js +0 -51
- package/dist/esm/ui/link-picker.js +0 -582
- package/dist/esm/ui/link-search-list/index.js +0 -88
- package/dist/esm/ui/link-search-list/styled.js +0 -8
- package/dist/esm/ui/list-item/index.js +0 -90
- package/dist/esm/ui/list-item/styled.js +0 -37
- package/dist/esm/ui/styled.js +0 -16
- package/dist/esm/ui/text-input/index.js +0 -99
- package/dist/esm/ui/text-input/styled.js +0 -10
- package/dist/esm/url.js +0 -90
- package/dist/esm/utils.js +0 -13
- package/dist/esm/version.json +0 -5
- package/dist/types/browser.d.ts +0 -15
- package/dist/types/index.d.ts +0 -3
- package/dist/types/messages.d.ts +0 -92
- package/dist/types/transformTimeStamp.d.ts +0 -3
- package/dist/types/types.d.ts +0 -40
- package/dist/types/ui/announcer.d.ts +0 -22
- package/dist/types/ui/link-picker.d.ts +0 -51
- package/dist/types/ui/link-search-list/index.d.ts +0 -20
- package/dist/types/ui/link-search-list/styled.d.ts +0 -3
- package/dist/types/ui/list-item/index.d.ts +0 -17
- package/dist/types/ui/list-item/styled.d.ts +0 -7
- package/dist/types/ui/styled.d.ts +0 -4
- package/dist/types/ui/text-input/index.d.ts +0 -17
- package/dist/types/ui/text-input/styled.d.ts +0 -2
- package/dist/types/url.d.ts +0 -5
- package/dist/types/utils.d.ts +0 -3
package/dist/es2019/messages.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
};
|
package/dist/es2019/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,38 +0,0 @@
|
|
|
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);
|
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
|
|
3
|
-
/** @jsx jsx */
|
|
4
|
-
import React, { PureComponent } from 'react';
|
|
5
|
-
import { jsx } from '@emotion/react';
|
|
6
|
-
import { injectIntl } from 'react-intl-next';
|
|
7
|
-
import { messages } from '../messages';
|
|
8
|
-
import PanelTextInput from './text-input';
|
|
9
|
-
import LinkSearchList from './link-search-list';
|
|
10
|
-
import { normalizeUrl, isSafeUrl } from '../url';
|
|
11
|
-
import { recentListStyles, containerStyles, searchIconStyles, listTitleStyles } from './styled';
|
|
12
|
-
import { browser } from '../browser';
|
|
13
|
-
import { transformTimeStamp } from '../transformTimeStamp';
|
|
14
|
-
import Announcer from './announcer';
|
|
15
|
-
import Button, { ButtonGroup } from '@atlaskit/button';
|
|
16
|
-
import { FormFooter } from '@atlaskit/form';
|
|
17
|
-
import EditorSearchIcon from '@atlaskit/icon/glyph/editor/search';
|
|
18
|
-
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
19
|
-
export const RECENT_SEARCH_LIST_SIZE = 5;
|
|
20
|
-
|
|
21
|
-
class LinkPicker extends PureComponent {
|
|
22
|
-
constructor(props) {
|
|
23
|
-
super(props);
|
|
24
|
-
|
|
25
|
-
_defineProperty(this, "wrapperRef", /*#__PURE__*/React.createRef());
|
|
26
|
-
|
|
27
|
-
_defineProperty(this, "queryVersion", 0);
|
|
28
|
-
|
|
29
|
-
_defineProperty(this, "updateInput", async input => {
|
|
30
|
-
// If input is a valid URL, do not search or display link suggestions
|
|
31
|
-
if (input.length && isSafeUrl(input)) {
|
|
32
|
-
return this.setState({
|
|
33
|
-
url: input,
|
|
34
|
-
selectedIndex: -1,
|
|
35
|
-
items: [],
|
|
36
|
-
isLoading: false,
|
|
37
|
-
invalidUrl: null
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return this.setState({
|
|
42
|
-
url: input,
|
|
43
|
-
// Unset selection on any input change
|
|
44
|
-
selectedIndex: -1,
|
|
45
|
-
invalidUrl: null
|
|
46
|
-
}, () => {
|
|
47
|
-
this.updateResults();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
_defineProperty(this, "createClearHandler", field => {
|
|
52
|
-
return () => {
|
|
53
|
-
switch (field) {
|
|
54
|
-
case 'url':
|
|
55
|
-
{
|
|
56
|
-
this.updateInput('');
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
case 'displayText':
|
|
61
|
-
{
|
|
62
|
-
this.setState({
|
|
63
|
-
[field]: ''
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
_defineProperty(this, "getScreenReaderText", () => {
|
|
71
|
-
const {
|
|
72
|
-
intl
|
|
73
|
-
} = this.props;
|
|
74
|
-
const {
|
|
75
|
-
items,
|
|
76
|
-
selectedIndex
|
|
77
|
-
} = this.state;
|
|
78
|
-
|
|
79
|
-
if (items.length && selectedIndex > -1) {
|
|
80
|
-
const {
|
|
81
|
-
name,
|
|
82
|
-
container,
|
|
83
|
-
lastUpdatedDate,
|
|
84
|
-
lastViewedDate
|
|
85
|
-
} = items[selectedIndex];
|
|
86
|
-
const date = transformTimeStamp(intl, lastViewedDate, lastUpdatedDate);
|
|
87
|
-
const formattedDate = [date === null || date === void 0 ? void 0 : date.pageAction, date === null || date === void 0 ? void 0 : date.dateString, date === null || date === void 0 ? void 0 : date.timeSince].filter(Boolean).join(' ');
|
|
88
|
-
return [name, container, formattedDate].filter(Boolean).join(', ');
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
_defineProperty(this, "isUrlPopulatedWithSelectedItem", () => {
|
|
93
|
-
/**
|
|
94
|
-
* When we use ArrowKey to navigate through result items,
|
|
95
|
-
* the URL field will be populated with the content of
|
|
96
|
-
* selected item.
|
|
97
|
-
* This function will check if the URL field is populated
|
|
98
|
-
* with selected item.
|
|
99
|
-
* It can be useful to detect whether we want to insert a
|
|
100
|
-
* smartlink or a hyperlink with customized title
|
|
101
|
-
*/
|
|
102
|
-
const {
|
|
103
|
-
items,
|
|
104
|
-
selectedIndex,
|
|
105
|
-
url
|
|
106
|
-
} = this.state;
|
|
107
|
-
const selectedItem = items[selectedIndex];
|
|
108
|
-
|
|
109
|
-
if (selectedItem && selectedItem.url === url) {
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return false;
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
_defineProperty(this, "handleSelected", objectId => {
|
|
117
|
-
const {
|
|
118
|
-
items
|
|
119
|
-
} = this.state;
|
|
120
|
-
const selectedItem = items.find(item => item.objectId === objectId);
|
|
121
|
-
|
|
122
|
-
if (selectedItem) {
|
|
123
|
-
const {
|
|
124
|
-
url,
|
|
125
|
-
name
|
|
126
|
-
} = selectedItem;
|
|
127
|
-
this.handleInsert(url, name, 'typeAhead');
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
_defineProperty(this, "handleInsert", (url, title, inputType) => {
|
|
132
|
-
const {
|
|
133
|
-
onSubmit
|
|
134
|
-
} = this.props;
|
|
135
|
-
const {
|
|
136
|
-
url: rawUrl,
|
|
137
|
-
displayText
|
|
138
|
-
} = this.state;
|
|
139
|
-
|
|
140
|
-
if (onSubmit) {
|
|
141
|
-
onSubmit({
|
|
142
|
-
url,
|
|
143
|
-
displayText: displayText || null,
|
|
144
|
-
title: title || null,
|
|
145
|
-
meta: {
|
|
146
|
-
inputMethod: inputType
|
|
147
|
-
},
|
|
148
|
-
...(inputType === 'manual' ? {
|
|
149
|
-
rawUrl
|
|
150
|
-
} : {})
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
_defineProperty(this, "handleMouseEnterResultItem", objectId => {
|
|
156
|
-
const {
|
|
157
|
-
items
|
|
158
|
-
} = this.state;
|
|
159
|
-
const index = findIndex(items, item => item.objectId === objectId);
|
|
160
|
-
this.setState({
|
|
161
|
-
activeIndex: index
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
_defineProperty(this, "handleMouseLeaveResultItem", objectId => {
|
|
166
|
-
const {
|
|
167
|
-
items,
|
|
168
|
-
activeIndex
|
|
169
|
-
} = this.state;
|
|
170
|
-
const index = findIndex(items, item => item.objectId === objectId); // This is to avoid updating index that was set by other mouseenter event
|
|
171
|
-
|
|
172
|
-
if (activeIndex === index) {
|
|
173
|
-
this.setState({
|
|
174
|
-
activeIndex: -1
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
_defineProperty(this, "handleSubmit", () => {
|
|
180
|
-
const {
|
|
181
|
-
url,
|
|
182
|
-
selectedIndex,
|
|
183
|
-
items
|
|
184
|
-
} = this.state;
|
|
185
|
-
const selectedItem = items[selectedIndex];
|
|
186
|
-
|
|
187
|
-
if (this.isUrlPopulatedWithSelectedItem()) {
|
|
188
|
-
this.handleInsert(selectedItem.url, selectedItem.name, 'typeAhead');
|
|
189
|
-
} else if (url.length > 0) {
|
|
190
|
-
const normalized = normalizeUrl(url);
|
|
191
|
-
|
|
192
|
-
if (normalized) {
|
|
193
|
-
this.handleInsert(normalized, null, 'manual');
|
|
194
|
-
} else {
|
|
195
|
-
const {
|
|
196
|
-
intl
|
|
197
|
-
} = this.props;
|
|
198
|
-
const linkInvalid = intl.formatMessage(messages.linkInvalid);
|
|
199
|
-
this.setState({
|
|
200
|
-
invalidUrl: linkInvalid
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
_defineProperty(this, "handleKeyDown", event => {
|
|
207
|
-
const {
|
|
208
|
-
items,
|
|
209
|
-
activeIndex
|
|
210
|
-
} = this.state;
|
|
211
|
-
const {
|
|
212
|
-
keyCode
|
|
213
|
-
} = event;
|
|
214
|
-
const KEY_CODE_ARROW_DOWN = 40;
|
|
215
|
-
const KEY_CODE_ARROW_UP = 38;
|
|
216
|
-
|
|
217
|
-
if (!items || !items.length) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
let updatedIndex = activeIndex;
|
|
222
|
-
|
|
223
|
-
switch (keyCode) {
|
|
224
|
-
case KEY_CODE_ARROW_DOWN:
|
|
225
|
-
// down
|
|
226
|
-
event.preventDefault();
|
|
227
|
-
updatedIndex = (activeIndex + 1) % items.length;
|
|
228
|
-
break;
|
|
229
|
-
|
|
230
|
-
case KEY_CODE_ARROW_UP:
|
|
231
|
-
// up
|
|
232
|
-
event.preventDefault();
|
|
233
|
-
updatedIndex = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if ([KEY_CODE_ARROW_DOWN, KEY_CODE_ARROW_UP].includes(keyCode) && items[updatedIndex]) {
|
|
238
|
-
this.setState({
|
|
239
|
-
activeIndex: updatedIndex,
|
|
240
|
-
selectedIndex: updatedIndex,
|
|
241
|
-
url: items[updatedIndex].url,
|
|
242
|
-
invalidUrl: null
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
_defineProperty(this, "handleCancel", () => {
|
|
248
|
-
const {
|
|
249
|
-
onCancel
|
|
250
|
-
} = this.props;
|
|
251
|
-
|
|
252
|
-
if (onCancel) {
|
|
253
|
-
onCancel();
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
_defineProperty(this, "updateTextInput", displayText => {
|
|
258
|
-
this.setState({
|
|
259
|
-
displayText: displayText
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
this.state = {
|
|
264
|
-
selectedIndex: -1,
|
|
265
|
-
activeIndex: -1,
|
|
266
|
-
isLoading: false,
|
|
267
|
-
url: normalizeUrl(props.url) || '',
|
|
268
|
-
displayText: props.displayText || '',
|
|
269
|
-
items: [],
|
|
270
|
-
invalidUrl: null
|
|
271
|
-
};
|
|
272
|
-
/* Cache functions */
|
|
273
|
-
|
|
274
|
-
this.handleClearUrl = this.createClearHandler('url');
|
|
275
|
-
this.handleClearDisplayText = this.createClearHandler('displayText');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
componentDidMount() {
|
|
279
|
-
this.loadInitialLinkSearchResult();
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
loadInitialLinkSearchResult() {
|
|
283
|
-
const {
|
|
284
|
-
url
|
|
285
|
-
} = this.state;
|
|
286
|
-
|
|
287
|
-
if (!url) {
|
|
288
|
-
this.updateResults();
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
getActivePlugin() {
|
|
293
|
-
if (!this.props.plugins || this.props.plugins.length === 0) {
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return this.props.plugins[0];
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Interface between link picker and plugins to update results
|
|
301
|
-
*/
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
async updateResults() {
|
|
305
|
-
const queryVersion = ++this.queryVersion;
|
|
306
|
-
const activePlugin = this.getActivePlugin();
|
|
307
|
-
|
|
308
|
-
if (!activePlugin) {
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const updates = activePlugin.resolve({
|
|
313
|
-
query: this.state.url
|
|
314
|
-
});
|
|
315
|
-
this.setState({
|
|
316
|
-
isLoading: true,
|
|
317
|
-
items: []
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
try {
|
|
321
|
-
while (queryVersion === this.queryVersion) {
|
|
322
|
-
let done = false;
|
|
323
|
-
let value;
|
|
324
|
-
|
|
325
|
-
if (updates instanceof Promise) {
|
|
326
|
-
done = true;
|
|
327
|
-
value = await updates;
|
|
328
|
-
} else {
|
|
329
|
-
var _result$done;
|
|
330
|
-
|
|
331
|
-
const result = await updates.next();
|
|
332
|
-
done = (_result$done = result.done) !== null && _result$done !== void 0 ? _result$done : false;
|
|
333
|
-
value = result.value;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (queryVersion !== this.queryVersion) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
this.setState({
|
|
341
|
-
items: limit(value.data),
|
|
342
|
-
isLoading: !done
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
if (done) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
} catch {
|
|
350
|
-
this.setState({
|
|
351
|
-
isLoading: false
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
render() {
|
|
357
|
-
const {
|
|
358
|
-
items,
|
|
359
|
-
isLoading,
|
|
360
|
-
activeIndex,
|
|
361
|
-
selectedIndex,
|
|
362
|
-
url,
|
|
363
|
-
displayText
|
|
364
|
-
} = this.state;
|
|
365
|
-
const {
|
|
366
|
-
intl: {
|
|
367
|
-
formatMessage
|
|
368
|
-
}
|
|
369
|
-
} = this.props;
|
|
370
|
-
const activePlugin = this.getActivePlugin();
|
|
371
|
-
const linkPlaceHolder = formatMessage(activePlugin ? messages.placeholder : messages.linkPlaceholder);
|
|
372
|
-
const linkLabel = formatMessage(messages.linkLabel);
|
|
373
|
-
const linkTextLabel = formatMessage(messages.linkTextLabel);
|
|
374
|
-
const linkTextPlaceHolder = formatMessage(messages.linkTextPlaceholder);
|
|
375
|
-
const screenReaderDescriptionId = 'search-recent-links-field-description';
|
|
376
|
-
const linkSearchListId = 'link-picker-search-list';
|
|
377
|
-
const ariaActiveDescendant = selectedIndex > -1 ? `link-search-list-item-${selectedIndex}` : ''; // Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
|
|
378
|
-
// as the Aria design pattern for combobox does not work in this case
|
|
379
|
-
// for details: https://a11y-internal.atlassian.net/browse/AK-740
|
|
380
|
-
|
|
381
|
-
const screenReaderText = browser.safari && this.getScreenReaderText();
|
|
382
|
-
const searchIcon = activePlugin && jsx("span", {
|
|
383
|
-
css: searchIconStyles,
|
|
384
|
-
"data-testid": "link-picker-search-icon"
|
|
385
|
-
}, jsx(EditorSearchIcon, {
|
|
386
|
-
size: "medium",
|
|
387
|
-
label: ''
|
|
388
|
-
}));
|
|
389
|
-
return jsx("div", {
|
|
390
|
-
"data-testid": "link-picker",
|
|
391
|
-
className: "recent-list",
|
|
392
|
-
css: recentListStyles
|
|
393
|
-
}, jsx("div", {
|
|
394
|
-
css: containerStyles,
|
|
395
|
-
ref: this.wrapperRef
|
|
396
|
-
}, screenReaderText && jsx(Announcer, {
|
|
397
|
-
ariaLive: "assertive",
|
|
398
|
-
text: screenReaderText,
|
|
399
|
-
ariaRelevant: "additions",
|
|
400
|
-
delay: 250
|
|
401
|
-
}), jsx(VisuallyHidden, null, jsx("span", {
|
|
402
|
-
"aria-hidden": "true",
|
|
403
|
-
id: screenReaderDescriptionId
|
|
404
|
-
}, formatMessage(messages.searchLinkAriaDescription))), jsx(PanelTextInput, {
|
|
405
|
-
name: "link",
|
|
406
|
-
testId: "link-url",
|
|
407
|
-
role: "combobox",
|
|
408
|
-
label: linkLabel,
|
|
409
|
-
placeholder: linkPlaceHolder,
|
|
410
|
-
value: url,
|
|
411
|
-
autoFocus: true,
|
|
412
|
-
autoComplete: "off",
|
|
413
|
-
elemBeforeInput: searchIcon,
|
|
414
|
-
onSubmit: this.handleSubmit,
|
|
415
|
-
onChange: this.updateInput,
|
|
416
|
-
onKeyDown: this.handleKeyDown,
|
|
417
|
-
onClear: this.handleClearUrl,
|
|
418
|
-
error: this.state.invalidUrl,
|
|
419
|
-
clearLabel: formatMessage(messages.clearLink),
|
|
420
|
-
"aria-controls": linkSearchListId,
|
|
421
|
-
"aria-autocomplete": "list",
|
|
422
|
-
"aria-activedescendant": ariaActiveDescendant,
|
|
423
|
-
"aria-describedby": screenReaderDescriptionId,
|
|
424
|
-
"aria-expanded": true
|
|
425
|
-
}), jsx(PanelTextInput, {
|
|
426
|
-
name: "link-text",
|
|
427
|
-
testId: "link-text",
|
|
428
|
-
autoComplete: "off",
|
|
429
|
-
label: linkTextLabel,
|
|
430
|
-
placeholder: linkTextPlaceHolder,
|
|
431
|
-
"aria-label": formatMessage(messages.linkAriaLabel),
|
|
432
|
-
value: displayText,
|
|
433
|
-
onSubmit: this.handleSubmit,
|
|
434
|
-
onKeyDown: this.handleKeyDown,
|
|
435
|
-
onChange: this.updateTextInput,
|
|
436
|
-
onClear: this.handleClearDisplayText,
|
|
437
|
-
clearLabel: formatMessage(messages.clearText)
|
|
438
|
-
}), !!items.length && jsx("div", {
|
|
439
|
-
css: listTitleStyles,
|
|
440
|
-
id: "link-picker-list-title",
|
|
441
|
-
"data-testid": "link-picker-list-title"
|
|
442
|
-
}, formatMessage(this.state.url ? messages.titleResults : messages.titleRecentlyViewed)), jsx(VisuallyHidden, null, jsx("span", {
|
|
443
|
-
"aria-live": "polite",
|
|
444
|
-
"aria-atomic": "true",
|
|
445
|
-
id: "fabric.smartcard.linkpicker.suggested.results"
|
|
446
|
-
}, url && formatMessage(messages.searchLinkResults, {
|
|
447
|
-
count: items.length
|
|
448
|
-
}))), jsx(LinkSearchList, {
|
|
449
|
-
ariaLabelledBy: "link-picker-list-title",
|
|
450
|
-
ariaControls: "fabric.smartcard.linkpicker.suggested.results",
|
|
451
|
-
id: linkSearchListId,
|
|
452
|
-
role: "listbox",
|
|
453
|
-
items: items,
|
|
454
|
-
isLoading: isLoading,
|
|
455
|
-
selectedIndex: selectedIndex,
|
|
456
|
-
activeIndex: activeIndex,
|
|
457
|
-
onSelect: this.handleSelected,
|
|
458
|
-
onMouseEnter: this.handleMouseEnterResultItem,
|
|
459
|
-
onMouseLeave: this.handleMouseLeaveResultItem
|
|
460
|
-
}), jsx(FormFooter, {
|
|
461
|
-
align: "end"
|
|
462
|
-
}, jsx(ButtonGroup, null, jsx(Button, {
|
|
463
|
-
appearance: "default",
|
|
464
|
-
onClick: this.handleCancel,
|
|
465
|
-
testId: "link-picker-cancel-button"
|
|
466
|
-
}, formatMessage(messages.cancelButton)), jsx(Button, {
|
|
467
|
-
type: "submit",
|
|
468
|
-
appearance: "primary",
|
|
469
|
-
onClick: this.handleSubmit,
|
|
470
|
-
testId: "link-picker-insert-button"
|
|
471
|
-
}, formatMessage(messages.insertButton))))));
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function findIndex(array, predicate) {
|
|
477
|
-
let index = -1;
|
|
478
|
-
array.some((item, i) => {
|
|
479
|
-
if (predicate(item)) {
|
|
480
|
-
index = i;
|
|
481
|
-
return true;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return false;
|
|
485
|
-
});
|
|
486
|
-
return index;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function limit(items) {
|
|
490
|
-
return items.slice(0, RECENT_SEARCH_LIST_SIZE);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export const LinkPickerWithIntl = injectIntl(LinkPicker);
|