superdesk-ui-framework 2.4.12 → 2.4.17
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/app/scripts/modals.js +1 -2
- package/app/styles/app.scss +1 -0
- package/app/styles/form-elements/_select-grid.scss +77 -0
- package/app-typescript/components/Dropdown.tsx +0 -1
- package/app-typescript/components/IconPicker.tsx +277 -0
- package/app-typescript/components/SelectGrid.tsx +233 -0
- package/app-typescript/index.ts +2 -0
- package/dist/examples.bundle.js +3128 -1902
- package/dist/playgrounds/react-playgrounds/SamsPlayground.tsx +14 -1
- package/dist/react/IconFont.tsx +7 -6
- package/dist/react/IconPicker.tsx +65 -0
- package/dist/react/Index.tsx +10 -0
- package/dist/react/SelectGrid.tsx +121 -0
- package/dist/react/Tags.tsx +1 -1
- package/dist/superdesk-ui.bundle.css +3327 -0
- package/dist/superdesk-ui.bundle.js +2329 -1398
- package/dist/vendor.bundle.js +13699 -13695
- package/examples/pages/playgrounds/react-playgrounds/SamsPlayground.tsx +14 -1
- package/examples/pages/react/IconFont.tsx +7 -6
- package/examples/pages/react/IconPicker.tsx +65 -0
- package/examples/pages/react/Index.tsx +10 -0
- package/examples/pages/react/SelectGrid.tsx +121 -0
- package/examples/pages/react/Tags.tsx +1 -1
- package/package.json +1 -1
- package/react/components/Dropdown.js +0 -1
- package/react/components/IconPicker.d.ts +24 -0
- package/react/components/IconPicker.js +283 -0
- package/react/components/SelectGrid.d.ts +45 -0
- package/react/components/SelectGrid.js +179 -0
- package/react/index.d.ts +2 -0
- package/react/index.js +4 -0
package/app/scripts/modals.js
CHANGED
package/app/styles/app.scss
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
.select-grid__overlay-panel {
|
2
|
+
z-index: 1500 !important;
|
3
|
+
margin-top: 1px;
|
4
|
+
}
|
5
|
+
|
6
|
+
.select-grid__panel {
|
7
|
+
width: 450px;
|
8
|
+
max-height: 550px;
|
9
|
+
background-color: var(--color-bg-00);
|
10
|
+
display: flex;
|
11
|
+
flex-direction: column;
|
12
|
+
}
|
13
|
+
|
14
|
+
.select-grid__header {
|
15
|
+
padding: 1rem;
|
16
|
+
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.1);
|
17
|
+
}
|
18
|
+
|
19
|
+
.select-grid__body {
|
20
|
+
overflow-y: auto;
|
21
|
+
margin: 0;
|
22
|
+
padding: 1rem;
|
23
|
+
position: relative;
|
24
|
+
}
|
25
|
+
|
26
|
+
.select-grid__item {
|
27
|
+
display: flex;
|
28
|
+
flex-direction: column;
|
29
|
+
align-items: center;
|
30
|
+
color: $sd-text;
|
31
|
+
|
32
|
+
&:hover, &:focus {
|
33
|
+
color: $sd-blue;
|
34
|
+
cursor: pointer;
|
35
|
+
|
36
|
+
i {
|
37
|
+
color: $sd-blue;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
&:hover {
|
42
|
+
background: $sd-colour-background__menu-item--hover;
|
43
|
+
}
|
44
|
+
|
45
|
+
&:focus {
|
46
|
+
outline: 2px solid $sd-colour-interactive__base;
|
47
|
+
outline-offset: -2px;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
.sd-input--grid-select {
|
52
|
+
.btn {
|
53
|
+
grid-row: 2/3;
|
54
|
+
grid-column: 2/4;
|
55
|
+
background-color: var(--color-input-bg);
|
56
|
+
border-bottom: 1px solid var(--color-input-border);
|
57
|
+
width: 3.2rem;
|
58
|
+
}
|
59
|
+
|
60
|
+
.btn:hover {
|
61
|
+
background-color: var(--color-input-bg--hover);
|
62
|
+
border-color: var(--color-input-border-hover);
|
63
|
+
}
|
64
|
+
|
65
|
+
.btn:focus {
|
66
|
+
background-color: var(--color-input-bg--focus);
|
67
|
+
box-shadow: 0 1px 0 0 $sd-blue;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
.form__item--auto-width {
|
72
|
+
.sd-input--grid-select {
|
73
|
+
.btn {
|
74
|
+
margin-top: 0;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
@@ -0,0 +1,277 @@
|
|
1
|
+
import * as React from 'react';
|
2
|
+
// @ts-ignore
|
3
|
+
import * as iconFont from '../../app/styles/_icon-font.scss';
|
4
|
+
import { Button } from './Button';
|
5
|
+
import { Icon } from './Icon';
|
6
|
+
import { IItem, SelectGrid } from "./SelectGrid";
|
7
|
+
|
8
|
+
interface IProps {
|
9
|
+
label?: string;
|
10
|
+
filterPlaceholder?: string;
|
11
|
+
translateFunction?: (text: string) => string;
|
12
|
+
value: string;
|
13
|
+
onChange(icon: string): void;
|
14
|
+
}
|
15
|
+
|
16
|
+
interface IState {
|
17
|
+
icons: Array<IItem>;
|
18
|
+
}
|
19
|
+
|
20
|
+
export class IconPicker extends React.PureComponent<IProps, IState> {
|
21
|
+
|
22
|
+
constructor(props: IProps) {
|
23
|
+
super(props);
|
24
|
+
this.state = { icons: [] };
|
25
|
+
}
|
26
|
+
|
27
|
+
componentDidMount() {
|
28
|
+
const translateFunction = this.props.translateFunction ?
|
29
|
+
this.props.translateFunction : (text: string): string => text;
|
30
|
+
this.setState({
|
31
|
+
icons: getIcons(translateFunction),
|
32
|
+
});
|
33
|
+
}
|
34
|
+
|
35
|
+
getItems = (searchString: string | null): Promise<Array<IItem>> => {
|
36
|
+
return new Promise((resolve) => {
|
37
|
+
let icons = [...this.state.icons];
|
38
|
+
|
39
|
+
if (searchString) {
|
40
|
+
icons = icons.filter(
|
41
|
+
(icon) => (
|
42
|
+
icon.value.toLowerCase().includes(searchString) ||
|
43
|
+
icon.label.toLowerCase().includes(searchString)
|
44
|
+
),
|
45
|
+
);
|
46
|
+
}
|
47
|
+
resolve(icons);
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
onChange = (item: IItem) => {
|
52
|
+
this.props.onChange(item.value);
|
53
|
+
}
|
54
|
+
|
55
|
+
triggerTemplate = (props: any) => <Button
|
56
|
+
icon={this.props.value}
|
57
|
+
text={this.props.value}
|
58
|
+
onClick={(e) => { props.onClick(e); }}
|
59
|
+
iconOnly={true} />
|
60
|
+
|
61
|
+
itemTemplate = ({ item }: { item: IItem | null }) => item ?
|
62
|
+
(<>
|
63
|
+
<Icon name={item.value} />
|
64
|
+
<span className="sd-text__normal sd-padding-t--1">
|
65
|
+
{item.label}
|
66
|
+
</span>
|
67
|
+
</>) : null
|
68
|
+
|
69
|
+
render() {
|
70
|
+
return (
|
71
|
+
<SelectGrid
|
72
|
+
label={this.props.label || "Icon"}
|
73
|
+
filterPlaceholder={this.props.filterPlaceholder || "Search..."}
|
74
|
+
getItems={this.getItems}
|
75
|
+
onChange={this.onChange}
|
76
|
+
itemTemplate={this.itemTemplate}
|
77
|
+
triggerTemplate={this.triggerTemplate}
|
78
|
+
/>
|
79
|
+
);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
const getIcons = (translateFunction: (text: string) => string): Array<IItem> => {
|
84
|
+
const translatedIconNameMap: any = {
|
85
|
+
'add-gallery': 'Add Gallery',
|
86
|
+
'add-image': 'Add Image',
|
87
|
+
'adjust': 'Adjust',
|
88
|
+
'align-center': 'Align Center',
|
89
|
+
'align-justify': 'Align Justify',
|
90
|
+
'align-left': 'Align Left',
|
91
|
+
'align-right': 'Align Right',
|
92
|
+
'amp': 'AMP',
|
93
|
+
'analytics': 'Analytics',
|
94
|
+
'archive': 'Archive',
|
95
|
+
'arrow-left': 'Arrow Left',
|
96
|
+
'arrow-right': 'Arrow Right',
|
97
|
+
'arrow-small': 'Arrow Small',
|
98
|
+
'ascending': 'Ascending',
|
99
|
+
'assign': 'Assign',
|
100
|
+
'attachment': 'Attachment',
|
101
|
+
'attachment-large': 'Attachment Large',
|
102
|
+
'audio': 'Audio',
|
103
|
+
'backward-thin': 'Backward Thin',
|
104
|
+
'ban-circle': 'Ban Circle',
|
105
|
+
'bell': 'Bell',
|
106
|
+
'bold': 'Bold',
|
107
|
+
'broadcast': 'Broadcast',
|
108
|
+
'broadcast-create': 'Broadcast Create',
|
109
|
+
'business': 'Business',
|
110
|
+
'calendar': 'Calendar',
|
111
|
+
'calendar-list': 'Calendar List',
|
112
|
+
'chevron-down-thin': 'Chevron Down Thin',
|
113
|
+
'chevron-left-thin': 'Chevron Left Thin',
|
114
|
+
'chevron-right-thin': 'Chevron Right Thin',
|
115
|
+
'chevron-up-thin': 'Chevron Up Thin',
|
116
|
+
'clear-all': 'Clear All',
|
117
|
+
'clear-format': 'Clear Format',
|
118
|
+
'close-small': 'Close Small',
|
119
|
+
'close-thick': 'Close Thick',
|
120
|
+
'code': 'Code',
|
121
|
+
'collapse': 'Collapse',
|
122
|
+
'comment': 'Comment',
|
123
|
+
'composite': 'Composite',
|
124
|
+
'copy': 'Copy',
|
125
|
+
'crop': 'Crop',
|
126
|
+
'cut': 'Cut',
|
127
|
+
'descending': 'Descending',
|
128
|
+
'dots': 'Dots',
|
129
|
+
'dots-vertical': 'Dots Vertical',
|
130
|
+
'download': 'Download',
|
131
|
+
'download-alt': 'Download Alternate',
|
132
|
+
'edit-line': 'Edit Line',
|
133
|
+
'envelope': 'Envelope',
|
134
|
+
'event': 'Event',
|
135
|
+
'exclamation-sign': 'Exclamation Sign',
|
136
|
+
'expand': 'Expand',
|
137
|
+
'expand-thin': 'Expand Thin',
|
138
|
+
'external': 'External',
|
139
|
+
'eye-open': 'Eye Open',
|
140
|
+
'facebook': 'Facebook',
|
141
|
+
'facebook-circle': 'Facebook Circle',
|
142
|
+
'fast_forward': 'Fast Forward',
|
143
|
+
'fast_rewind': 'Fast Rewind',
|
144
|
+
'fetch-as': 'Fetch As',
|
145
|
+
'file': 'File',
|
146
|
+
'filter-large': 'Filter Large',
|
147
|
+
'flip-horizontal': 'Flip Horizontal',
|
148
|
+
'flip-vertical': 'Flip Vertical',
|
149
|
+
'folder-close': 'Folder Close',
|
150
|
+
'folder-open': 'Folder Open',
|
151
|
+
'font': 'Font',
|
152
|
+
'forward-thin': 'Forward Thin',
|
153
|
+
'fullscreen': 'Fullscreen',
|
154
|
+
'globe': 'Globe',
|
155
|
+
'graphic': 'Graphic',
|
156
|
+
'grid-view': 'Grid View',
|
157
|
+
'grid-view-large': 'Grid View Large',
|
158
|
+
'heading-1': 'Heading 1',
|
159
|
+
'heading-2': 'Heading 2',
|
160
|
+
'heading-3': 'Heading 3',
|
161
|
+
'heading-4': 'Heading 4',
|
162
|
+
'heading-5': 'Heading 5',
|
163
|
+
'heading-6': 'Heading 6',
|
164
|
+
'heart': 'Heart',
|
165
|
+
'help-large': 'Help Large',
|
166
|
+
'highlight-package': 'Highlight Package',
|
167
|
+
'home': 'Home',
|
168
|
+
'indent-left': 'Indent Left',
|
169
|
+
'indent-right': 'Indent Right',
|
170
|
+
'info-large': 'Info Large',
|
171
|
+
'info-sign': 'Info Sign',
|
172
|
+
'ingest': 'Ingest',
|
173
|
+
'instagram': 'Instagram',
|
174
|
+
'italic': 'Italic',
|
175
|
+
'kanban-view': 'Kanban View',
|
176
|
+
'kill': 'Kill',
|
177
|
+
'link': 'Link',
|
178
|
+
'linked-in': 'LinkedIn',
|
179
|
+
'linked-in-circle': 'LinkedIn Circle',
|
180
|
+
'list-alt': 'List Alternate',
|
181
|
+
'list-menu': 'List Menu',
|
182
|
+
'list-plus': 'List Plus',
|
183
|
+
'list-view': 'List View',
|
184
|
+
'lock': 'Lock',
|
185
|
+
'map-marker': 'Map Marker',
|
186
|
+
'minus-sign': 'Minus Sign',
|
187
|
+
'minus-small': 'Minus Small',
|
188
|
+
'mobile': 'Mobile',
|
189
|
+
'move': 'Move',
|
190
|
+
'multi-star': 'Multi Start',
|
191
|
+
'multiedit': 'Multi Edit',
|
192
|
+
'new-doc': 'New Document',
|
193
|
+
'ok': 'Okay',
|
194
|
+
'ordered-list': 'Ordered List',
|
195
|
+
'package-create': 'Package Create',
|
196
|
+
'package-plus': 'Package Plus',
|
197
|
+
'paragraph': 'Paragraph',
|
198
|
+
'paste': 'Paste',
|
199
|
+
'pause': 'Pause',
|
200
|
+
'paywall': 'Paywall',
|
201
|
+
'pencil': 'Pencil',
|
202
|
+
'phone': 'Phone',
|
203
|
+
'photo': 'Photo',
|
204
|
+
'pick': 'Pick',
|
205
|
+
'picture': 'Picture',
|
206
|
+
'pin': 'Pin',
|
207
|
+
'play': 'Play',
|
208
|
+
'plus-large': 'Plus Large',
|
209
|
+
'plus-sign': 'Plus Sign',
|
210
|
+
'plus-small': 'Plus Small',
|
211
|
+
'post': 'Post',
|
212
|
+
'preformatted': 'Preformatted',
|
213
|
+
'preview-mode': 'Preview Mode',
|
214
|
+
'print': 'Print',
|
215
|
+
'question-sign': 'Question Sign',
|
216
|
+
'quote': 'Quote',
|
217
|
+
'random': 'Random',
|
218
|
+
'recurring': 'Recurring',
|
219
|
+
'redo': 'Redo',
|
220
|
+
'refresh': 'Refresh',
|
221
|
+
'remove-sign': 'Remove Sign',
|
222
|
+
'repeat': 'Repeat',
|
223
|
+
'retweet': 'Retweet',
|
224
|
+
'revert': 'Revert',
|
225
|
+
'rotate-left': 'Rotate Left',
|
226
|
+
'rotate-right': 'Rotate Right',
|
227
|
+
'search': 'Search',
|
228
|
+
'settings': 'Settings',
|
229
|
+
'share-alt': 'Share Alternate',
|
230
|
+
'signal': 'Signal',
|
231
|
+
'skip_next': 'Skip Next',
|
232
|
+
'skip_previous': 'Skip Previous',
|
233
|
+
'slideshow': 'Slideshow',
|
234
|
+
'star': 'Star',
|
235
|
+
'star-empty': 'Star Empty',
|
236
|
+
'stop': 'Stop',
|
237
|
+
'stream': 'Stream',
|
238
|
+
'strikethrough': 'Strikethrough',
|
239
|
+
'subscript': 'Subscript',
|
240
|
+
'suggestion': 'Suggestion',
|
241
|
+
'superscript': 'Superscript',
|
242
|
+
'switches': 'Switches',
|
243
|
+
'table': 'Table',
|
244
|
+
'takes-package': 'Takes Package',
|
245
|
+
'tasks': 'Tasks',
|
246
|
+
'text': 'Text',
|
247
|
+
'text-format': 'Text Format',
|
248
|
+
'th': 'Table Header',
|
249
|
+
'th-large': 'Table Header Large',
|
250
|
+
'th-list': 'Table Header List',
|
251
|
+
'time': 'Time',
|
252
|
+
'to-lowercase': 'To Lowercase',
|
253
|
+
'to-uppercase': 'To Uppercase',
|
254
|
+
'trash': 'Trash',
|
255
|
+
'twitter': 'Twitter',
|
256
|
+
'twitter-circle': 'Twitter Circle',
|
257
|
+
'underline': 'Underline',
|
258
|
+
'undo': 'Undo',
|
259
|
+
'unlocked': 'Unlocked',
|
260
|
+
'unordered-list': 'Unordered List',
|
261
|
+
'unspike': 'Unspike',
|
262
|
+
'upload': 'Upload',
|
263
|
+
'user': 'User',
|
264
|
+
'video': 'Video',
|
265
|
+
'warning-sign': 'Warning Sign',
|
266
|
+
'zoom-in': 'Zoom In',
|
267
|
+
'zoom-out': 'Zoom Out',
|
268
|
+
};
|
269
|
+
|
270
|
+
return iconFont.icon
|
271
|
+
.split(', ')
|
272
|
+
.sort()
|
273
|
+
.map((icon: string) => ({
|
274
|
+
value: icon,
|
275
|
+
label: translatedIconNameMap[icon] ? translateFunction(translatedIconNameMap[icon]) : icon,
|
276
|
+
}));
|
277
|
+
};
|
@@ -0,0 +1,233 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import nextId from "react-id-generator";
|
3
|
+
import { OverlayPanel } from '@superdesk/primereact/overlaypanel';
|
4
|
+
import { Loader } from "./Loader";
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @ngdoc react
|
8
|
+
* @name SelectGrid
|
9
|
+
* @description searchable select component with grid view of items
|
10
|
+
*/
|
11
|
+
|
12
|
+
export interface IItem {
|
13
|
+
value: string;
|
14
|
+
label: string;
|
15
|
+
[extra: string]: any;
|
16
|
+
}
|
17
|
+
|
18
|
+
interface IProps {
|
19
|
+
getItems(searchString: string | null): Promise<Array<IItem>>;
|
20
|
+
onChange(value: IItem): void;
|
21
|
+
itemTemplate: React.ComponentType<{ item: IItem | null }>;
|
22
|
+
triggerTemplate: React.ComponentType<any>;
|
23
|
+
label: string;
|
24
|
+
filterPlaceholder?: string;
|
25
|
+
}
|
26
|
+
|
27
|
+
interface IState {
|
28
|
+
items: Array<IItem>;
|
29
|
+
loading: boolean;
|
30
|
+
isOpen: boolean;
|
31
|
+
}
|
32
|
+
|
33
|
+
export class SelectGrid extends React.PureComponent<IProps, IState> {
|
34
|
+
htmlId = nextId();
|
35
|
+
buttonContainer: React.RefObject<HTMLDivElement>;
|
36
|
+
overlayPanel: React.RefObject<OverlayPanel>;
|
37
|
+
searchInput: React.RefObject<HTMLInputElement>;
|
38
|
+
gridContainer: React.RefObject<HTMLDivElement>;
|
39
|
+
|
40
|
+
constructor(props: IProps) {
|
41
|
+
super(props);
|
42
|
+
|
43
|
+
this.state = { items: [], loading: true, isOpen: false };
|
44
|
+
|
45
|
+
this.buttonContainer = React.createRef();
|
46
|
+
this.overlayPanel = React.createRef();
|
47
|
+
this.searchInput = React.createRef();
|
48
|
+
this.gridContainer = React.createRef();
|
49
|
+
}
|
50
|
+
|
51
|
+
componentDidMount() {
|
52
|
+
this.props.getItems(null).then((items) => {
|
53
|
+
this.setState({ items, loading: false });
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
componentWillUnmount() {
|
58
|
+
document.removeEventListener('keydown', this.handleKeydown);
|
59
|
+
}
|
60
|
+
|
61
|
+
togglePopup = (event?: React.SyntheticEvent) => {
|
62
|
+
if (!event) {
|
63
|
+
// @ts-ignore
|
64
|
+
this.overlayPanel.current.hide();
|
65
|
+
} else {
|
66
|
+
// @ts-ignore
|
67
|
+
this.overlayPanel.current.toggle(event);
|
68
|
+
}
|
69
|
+
|
70
|
+
setTimeout(() => {
|
71
|
+
// Now that the popup has (dis)appeared handle items and events
|
72
|
+
|
73
|
+
if (this.state.isOpen) {
|
74
|
+
document.removeEventListener('keydown', this.handleKeydown);
|
75
|
+
// @ts-ignore
|
76
|
+
this.buttonContainer.current.querySelector('button')?.focus();
|
77
|
+
} else {
|
78
|
+
document.addEventListener('keydown', this.handleKeydown);
|
79
|
+
this.loadItems();
|
80
|
+
// @ts-ignore
|
81
|
+
this.searchInput.current.focus();
|
82
|
+
}
|
83
|
+
|
84
|
+
this.setState({ isOpen: !this.state.isOpen });
|
85
|
+
});
|
86
|
+
|
87
|
+
}
|
88
|
+
|
89
|
+
search = (event: React.ChangeEvent<HTMLInputElement>) => {
|
90
|
+
const searchString: string = event.target.value.toLowerCase();
|
91
|
+
this.loadItems(searchString);
|
92
|
+
}
|
93
|
+
|
94
|
+
loadItems = (searchString: string | null = null) => {
|
95
|
+
this.setState({ loading: true });
|
96
|
+
this.props.getItems(searchString).then((items) => {
|
97
|
+
this.setState({ items, loading: false });
|
98
|
+
});
|
99
|
+
}
|
100
|
+
|
101
|
+
select = (item: IItem) => {
|
102
|
+
this.props.onChange(item);
|
103
|
+
this.togglePopup();
|
104
|
+
}
|
105
|
+
|
106
|
+
getItemElement = (index: number): HTMLDivElement | null | undefined => {
|
107
|
+
return this.gridContainer.current?.querySelector(`[data-item-index="${index}"]`);
|
108
|
+
}
|
109
|
+
|
110
|
+
handleKeydown = (event: KeyboardEvent) => {
|
111
|
+
const navKeys = [
|
112
|
+
"Enter",
|
113
|
+
"ArrowRight",
|
114
|
+
"ArrowLeft",
|
115
|
+
"ArrowUp",
|
116
|
+
"ArrowDown",
|
117
|
+
"PageDown",
|
118
|
+
"PageUp",
|
119
|
+
];
|
120
|
+
const activeElement = document.activeElement;
|
121
|
+
|
122
|
+
if (event.code === "Escape") {
|
123
|
+
event.preventDefault();
|
124
|
+
event.stopPropagation();
|
125
|
+
this.togglePopup();
|
126
|
+
} else if (activeElement === this.searchInput?.current) {
|
127
|
+
if (event.code === "ArrowDown") {
|
128
|
+
event.preventDefault();
|
129
|
+
this.getItemElement(0)?.focus();
|
130
|
+
} else if (event.code === "Enter" && this.state.items.length === 1) {
|
131
|
+
event.preventDefault();
|
132
|
+
this.select(this.state.items[0]);
|
133
|
+
}
|
134
|
+
// @ts-ignore
|
135
|
+
} else if (document.activeElement.getAttribute('data-item-index') && navKeys.includes(event.code)) {
|
136
|
+
// @ts-ignore
|
137
|
+
let itemIndex = parseInt(activeElement.getAttribute('data-item-index'), 10);
|
138
|
+
|
139
|
+
// Prevent default behaviour, such as scrolling
|
140
|
+
event.preventDefault();
|
141
|
+
if (event.code === "Enter") {
|
142
|
+
this.select(this.state.items[itemIndex]);
|
143
|
+
return;
|
144
|
+
} else if (event.code === "ArrowRight") {
|
145
|
+
itemIndex += 1;
|
146
|
+
} else if (event.code === "ArrowLeft") {
|
147
|
+
itemIndex -= 1;
|
148
|
+
} else if (event.code === "ArrowDown") {
|
149
|
+
itemIndex += 4;
|
150
|
+
} else if (event.code === "ArrowUp") {
|
151
|
+
if (itemIndex === 0) {
|
152
|
+
this.searchInput?.current?.focus();
|
153
|
+
return;
|
154
|
+
}
|
155
|
+
itemIndex -= 4;
|
156
|
+
} else if (event.code === "PageDown") {
|
157
|
+
itemIndex += 16;
|
158
|
+
} else if (event.code === "PageUp") {
|
159
|
+
itemIndex -= 16;
|
160
|
+
}
|
161
|
+
|
162
|
+
if (itemIndex < 0) {
|
163
|
+
itemIndex = 0;
|
164
|
+
} else if (itemIndex >= this.state.items.length) {
|
165
|
+
itemIndex = this.state.items.length - 1;
|
166
|
+
}
|
167
|
+
|
168
|
+
this.getItemElement(itemIndex)?.focus();
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
render() {
|
173
|
+
const ItemTemplate = this.props.itemTemplate;
|
174
|
+
const TriggerTemplate = this.props.triggerTemplate;
|
175
|
+
|
176
|
+
return (
|
177
|
+
<React.Fragment>
|
178
|
+
<div
|
179
|
+
className="sd-input sd-input--grid-select"
|
180
|
+
ref={this.buttonContainer}
|
181
|
+
aria-label={this.props.label}
|
182
|
+
>
|
183
|
+
<label className="sd-input__label">
|
184
|
+
{this.props.label}
|
185
|
+
</label>
|
186
|
+
<TriggerTemplate onClick={this.togglePopup} />
|
187
|
+
|
188
|
+
</div>
|
189
|
+
<OverlayPanel
|
190
|
+
ref={this.overlayPanel}
|
191
|
+
dismissable={true}
|
192
|
+
className="select-grid__overlay-panel"
|
193
|
+
appendTo={document.body} // making it work inside `overflow:hidden`
|
194
|
+
>
|
195
|
+
<div className="sd-shadow--z3 select-grid__panel">
|
196
|
+
<div className="select-grid__header">
|
197
|
+
<div className="sd-searchbar sd-searchbar--boxed">
|
198
|
+
<label className="sd-searchbar__icon" />
|
199
|
+
<input
|
200
|
+
className="sd-searchbar__input"
|
201
|
+
placeholder={this.props.filterPlaceholder || 'Search...'}
|
202
|
+
type="text"
|
203
|
+
onChange={this.search}
|
204
|
+
ref={this.searchInput}
|
205
|
+
/>
|
206
|
+
</div>
|
207
|
+
</div>
|
208
|
+
<div
|
209
|
+
className="select-grid__body flex-grid flex-grid--wrap-items flex-grid--small-4 flex-grid--boxed"
|
210
|
+
ref={this.gridContainer}
|
211
|
+
|
212
|
+
>
|
213
|
+
<Loader overlay={this.state.loading} />
|
214
|
+
{this.state.items.map((item, index) => (
|
215
|
+
<div
|
216
|
+
key={this.htmlId + item.label}
|
217
|
+
data-item-index={index}
|
218
|
+
className="flex-grid__item select-grid__item sd-padding-y--2"
|
219
|
+
tabIndex={0}
|
220
|
+
role="button"
|
221
|
+
aria-label={item.name}
|
222
|
+
onClick={() => this.select(item)}
|
223
|
+
>
|
224
|
+
<ItemTemplate item={item} />
|
225
|
+
</div>
|
226
|
+
))}
|
227
|
+
</div>
|
228
|
+
</div>
|
229
|
+
</OverlayPanel>
|
230
|
+
</React.Fragment>
|
231
|
+
);
|
232
|
+
}
|
233
|
+
}
|
package/app-typescript/index.ts
CHANGED
@@ -63,6 +63,8 @@ export { GridItem, GridItemContent, GridItemMedia, GridItemFooter, GridItemConte
|
|
63
63
|
export { toasted } from './components/Toast';
|
64
64
|
export { Menu } from './components/Menu';
|
65
65
|
export { ToggleBox } from './components/Togglebox';
|
66
|
+
export { SelectGrid } from './components/SelectGrid';
|
67
|
+
export { IconPicker } from './components/IconPicker';
|
66
68
|
|
67
69
|
export { Skeleton } from './components/Skeleton';
|
68
70
|
export { ListItemLoader } from './components/ListItemLoader';
|