@patternfly/react-core 4.221.13 → 4.221.16

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/esm/components/DualListSelector/DualListSelector.d.ts +4 -0
  3. package/dist/esm/components/DualListSelector/DualListSelector.d.ts.map +1 -1
  4. package/dist/esm/components/DualListSelector/DualListSelector.js +13 -2
  5. package/dist/esm/components/DualListSelector/DualListSelector.js.map +1 -1
  6. package/dist/js/components/DualListSelector/DualListSelector.d.ts +4 -0
  7. package/dist/js/components/DualListSelector/DualListSelector.d.ts.map +1 -1
  8. package/dist/js/components/DualListSelector/DualListSelector.js +13 -2
  9. package/dist/js/components/DualListSelector/DualListSelector.js.map +1 -1
  10. package/dist/umd/react-core.js +13 -2
  11. package/dist/umd/react-core.min.css +69 -69
  12. package/dist/umd/react-core.min.js +1 -1
  13. package/package.json +5 -5
  14. package/src/components/DualListSelector/DualListSelector.tsx +14 -2
  15. package/src/components/DualListSelector/examples/DualListSelector.md +38 -767
  16. package/src/components/DualListSelector/examples/DualListSelectorBasic.tsx +26 -0
  17. package/src/components/DualListSelector/examples/DualListSelectorBasicSearch.tsx +27 -0
  18. package/src/components/DualListSelector/examples/DualListSelectorBasicTooltips.tsx +34 -0
  19. package/src/components/DualListSelector/examples/DualListSelectorComplexOptionsActions.tsx +146 -0
  20. package/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +214 -0
  21. package/src/components/DualListSelector/examples/DualListSelectorComposableDragDrop.tsx +190 -0
  22. package/src/components/DualListSelector/examples/{ComposableTree.tsx → DualListSelectorComposableTree.tsx} +3 -3
  23. package/src/components/DualListSelector/examples/DualListSelectorTree.tsx +91 -0
  24. package/src/components/FileUpload/examples/FileUpload.md +6 -270
  25. package/src/components/FileUpload/examples/FileUploadCustomPreview.tsx +40 -0
  26. package/src/components/FileUpload/examples/FileUploadCustomUpload.tsx +116 -0
  27. package/src/components/FileUpload/examples/FileUploadSimpleFile.tsx +31 -0
  28. package/src/components/FileUpload/examples/FileUploadSimpleText.tsx +51 -0
  29. package/src/components/FileUpload/examples/FileUploadTextWithEdits.tsx +51 -0
  30. package/src/components/FileUpload/examples/FileUploadTextWithRestrictions.tsx +71 -0
  31. package/src/components/FormSelect/examples/FormSelect.md +5 -291
  32. package/src/components/FormSelect/examples/FormSelectBasic.tsx +28 -0
  33. package/src/components/FormSelect/examples/FormSelectDisabled.tsx +28 -0
  34. package/src/components/FormSelect/examples/FormSelectGrouped.tsx +49 -0
  35. package/src/components/FormSelect/examples/FormSelectIconSpriteVariant.tsx +57 -0
  36. package/src/components/FormSelect/examples/FormSelectValidated.tsx +69 -0
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { DualListSelector } from '@patternfly/react-core';
3
+
4
+ export const DualListSelectorBasic: React.FunctionComponent = () => {
5
+ const [availableOptions, setAvailableOptions] = React.useState<React.ReactNode[]>([
6
+ 'Option 1',
7
+ 'Option 2',
8
+ 'Option 3',
9
+ 'Option 4'
10
+ ]);
11
+ const [chosenOptions, setChosenOptions] = React.useState<React.ReactNode[]>([]);
12
+
13
+ const onListChange = (newAvailableOptions: React.ReactNode[], newChosenOptions: React.ReactNode[]) => {
14
+ setAvailableOptions(newAvailableOptions.sort());
15
+ setChosenOptions(newChosenOptions.sort());
16
+ };
17
+
18
+ return (
19
+ <DualListSelector
20
+ availableOptions={availableOptions}
21
+ chosenOptions={chosenOptions}
22
+ onListChange={onListChange}
23
+ id="dual-list-selector-basic"
24
+ />
25
+ );
26
+ };
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { DualListSelector } from '@patternfly/react-core';
3
+
4
+ export const DualListSelectorBasicSearch: React.FunctionComponent = () => {
5
+ const [availableOptions, setAvailableOptions] = React.useState<React.ReactNode[]>([
6
+ 'Option 1',
7
+ 'Option 2',
8
+ 'Option 3',
9
+ 'Option 4'
10
+ ]);
11
+ const [chosenOptions, setChosenOptions] = React.useState<React.ReactNode[]>([]);
12
+
13
+ const onListChange = (newAvailableOptions: React.ReactNode[], newChosenOptions: React.ReactNode[]) => {
14
+ setAvailableOptions(newAvailableOptions.sort());
15
+ setChosenOptions(newChosenOptions.sort());
16
+ };
17
+
18
+ return (
19
+ <DualListSelector
20
+ isSearchable
21
+ availableOptions={availableOptions}
22
+ chosenOptions={chosenOptions}
23
+ onListChange={onListChange}
24
+ id="dual-list-selector-basic-search"
25
+ />
26
+ );
27
+ };
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { DualListSelector } from '@patternfly/react-core';
3
+
4
+ export const DualListSelectorBasicTooltips: React.FunctionComponent = () => {
5
+ const [availableOptions, setAvailableOptions] = React.useState<React.ReactNode[]>([
6
+ 'Option 1',
7
+ 'Option 2',
8
+ 'Option 3',
9
+ 'Option 4'
10
+ ]);
11
+ const [chosenOptions, setChosenOptions] = React.useState<React.ReactNode[]>([]);
12
+
13
+ const onListChange = (newAvailableOptions: React.ReactNode[], newChosenOptions: React.ReactNode[]) => {
14
+ setAvailableOptions(newAvailableOptions.sort());
15
+ setChosenOptions(newChosenOptions.sort());
16
+ };
17
+
18
+ return (
19
+ <DualListSelector
20
+ availableOptions={availableOptions}
21
+ chosenOptions={chosenOptions}
22
+ onListChange={onListChange}
23
+ addAllTooltip="Add all options"
24
+ addAllTooltipProps={{ position: 'top' }}
25
+ addSelectedTooltip="Add selected options"
26
+ addSelectedTooltipProps={{ position: 'right' }}
27
+ removeSelectedTooltip="Remove selected options"
28
+ removeSelectedTooltipProps={{ position: 'left' }}
29
+ removeAllTooltip="Remove all options"
30
+ removeAllTooltipProps={{ position: 'bottom' }}
31
+ id="dual-list-selector-basic-tooltips"
32
+ />
33
+ );
34
+ };
@@ -0,0 +1,146 @@
1
+ import React from 'react';
2
+ import {
3
+ Button,
4
+ ButtonVariant,
5
+ Checkbox,
6
+ Dropdown,
7
+ DropdownItem,
8
+ DualListSelector,
9
+ KebabToggle
10
+ } from '@patternfly/react-core';
11
+ import PficonSortCommonAscIcon from '@patternfly/react-icons/dist/esm/icons/pficon-sort-common-asc-icon';
12
+
13
+ export const DualListSelectorComplexOptionsActions: React.FunctionComponent = () => {
14
+ const [availableOptions, setAvailableOptions] = React.useState<React.ReactNode[]>([
15
+ <span key="option1">Option 1</span>,
16
+ <span key="option3">Option 3</span>,
17
+ <span key="option4">Option 4</span>,
18
+ <span key="option2">Option 2</span>
19
+ ]);
20
+ const [chosenOptions, setChosenOptions] = React.useState<React.ReactNode[]>([]);
21
+ const [isAvailableKebabOpen, setIsAvailableKebabOpen] = React.useState(false);
22
+ const [isChosenKebabOpen, setIsChosenKebabOpen] = React.useState(false);
23
+ const [isDisabled, setIsDisabled] = React.useState(false);
24
+
25
+ const onListChange = (newAvailableOptions: React.ReactNode[], newChosenOptions: React.ReactNode[]) => {
26
+ setAvailableOptions(newAvailableOptions);
27
+ setChosenOptions(newChosenOptions);
28
+ };
29
+
30
+ const onSort = (pane: string) => {
31
+ const toSort = pane === 'available' ? [...availableOptions] : [...chosenOptions];
32
+ (toSort as React.ReactElement[]).sort((a, b) => {
33
+ if (a.props.children > b.props.children) {
34
+ return 1;
35
+ }
36
+ if (a.props.children < b.props.children) {
37
+ return -1;
38
+ }
39
+ return 0;
40
+ });
41
+
42
+ if (pane === 'available') {
43
+ setAvailableOptions(toSort);
44
+ } else {
45
+ setChosenOptions(toSort);
46
+ }
47
+ };
48
+
49
+ const onToggle = (isOpen: boolean, pane: string) => {
50
+ if (pane === 'available') {
51
+ setIsAvailableKebabOpen(isOpen);
52
+ } else {
53
+ setIsChosenKebabOpen(isOpen);
54
+ }
55
+ };
56
+
57
+ const filterOption = (option: React.ReactNode, input: string) =>
58
+ (option as React.ReactElement).props.children.includes(input);
59
+
60
+ const dropdownItems = [
61
+ <DropdownItem key="link">Link</DropdownItem>,
62
+ <DropdownItem key="action" component="button">
63
+ Action
64
+ </DropdownItem>,
65
+ <DropdownItem key="secondAction" component="button">
66
+ Second Action
67
+ </DropdownItem>
68
+ ];
69
+
70
+ const availableOptionsActions = [
71
+ <Button
72
+ variant={ButtonVariant.plain}
73
+ onClick={() => onSort('available')}
74
+ aria-label="Sort available options"
75
+ key="availableSortButton"
76
+ isDisabled={isDisabled}
77
+ >
78
+ <PficonSortCommonAscIcon />
79
+ </Button>,
80
+ <Dropdown
81
+ toggle={
82
+ <KebabToggle
83
+ isDisabled={isDisabled}
84
+ onToggle={isOpen => onToggle(isOpen, 'available')}
85
+ id="complex-available-toggle"
86
+ />
87
+ }
88
+ isOpen={isAvailableKebabOpen}
89
+ isPlain
90
+ dropdownItems={dropdownItems}
91
+ key="availableDropdown"
92
+ />
93
+ ];
94
+
95
+ const chosenOptionsActions = [
96
+ <Button
97
+ variant={ButtonVariant.plain}
98
+ onClick={() => onSort('chosen')}
99
+ aria-label="Sort chosen options"
100
+ key="chosenSortButton"
101
+ isDisabled={isDisabled}
102
+ >
103
+ <PficonSortCommonAscIcon />
104
+ </Button>,
105
+ <Dropdown
106
+ toggle={
107
+ <KebabToggle
108
+ isDisabled={isDisabled}
109
+ onToggle={isOpen => onToggle(isOpen, 'chosen')}
110
+ id="complex-chosen-toggle"
111
+ />
112
+ }
113
+ isOpen={isChosenKebabOpen}
114
+ isPlain
115
+ dropdownItems={dropdownItems}
116
+ key="chosenDropdown"
117
+ />
118
+ ];
119
+
120
+ return (
121
+ <React.Fragment>
122
+ <DualListSelector
123
+ isSearchable
124
+ availableOptions={availableOptions}
125
+ availableOptionsActions={availableOptionsActions}
126
+ chosenOptions={chosenOptions}
127
+ chosenOptionsActions={chosenOptionsActions}
128
+ addAll={onListChange}
129
+ removeAll={onListChange}
130
+ addSelected={onListChange}
131
+ removeSelected={onListChange}
132
+ filterOption={filterOption}
133
+ isDisabled={isDisabled}
134
+ id="dual-list-selector-complex"
135
+ />
136
+ <Checkbox
137
+ key="isDisabled"
138
+ id="isDisabled"
139
+ label="isDisabled"
140
+ aria-label="isDisabled"
141
+ isChecked={isDisabled}
142
+ onChange={() => setIsDisabled(!isDisabled)}
143
+ />
144
+ </React.Fragment>
145
+ );
146
+ };
@@ -0,0 +1,214 @@
1
+ import React from 'react';
2
+ import {
3
+ Button,
4
+ ButtonVariant,
5
+ DualListSelector,
6
+ DualListSelectorPane,
7
+ DualListSelectorList,
8
+ DualListSelectorListItem,
9
+ DualListSelectorControlsWrapper,
10
+ DualListSelectorControl,
11
+ SearchInput
12
+ } from '@patternfly/react-core';
13
+ import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon';
14
+ import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
15
+ import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
16
+ import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
17
+ import PficonSortCommonAscIcon from '@patternfly/react-icons/dist/esm/icons/pficon-sort-common-asc-icon';
18
+
19
+ export const DualListSelectorComposable: React.FunctionComponent = () => {
20
+ const [availableOptions, setAvailableOptions] = React.useState([
21
+ { text: 'Apple', selected: false, isVisible: true },
22
+ { text: 'Banana', selected: false, isVisible: true },
23
+ { text: 'Pineapple', selected: false, isVisible: true },
24
+ { text: 'Orange', selected: false, isVisible: true },
25
+ { text: 'Grape', selected: false, isVisible: true },
26
+ { text: 'Peach', selected: false, isVisible: true },
27
+ { text: 'Strawberry', selected: false, isVisible: true }
28
+ ]);
29
+ const [chosenOptions, setChosenOptions] = React.useState([]);
30
+ const [availableFilter, setAvailableFilter] = React.useState('');
31
+ const [chosenFilter, setChosenFilter] = React.useState('');
32
+
33
+ // callback for moving selected options between lists
34
+ const moveSelected = (fromAvailable: boolean) => {
35
+ const sourceOptions = fromAvailable ? availableOptions : chosenOptions;
36
+ const destinationOptions = fromAvailable ? chosenOptions : availableOptions;
37
+ for (let i = 0; i < sourceOptions.length; i++) {
38
+ const option = sourceOptions[i];
39
+ if (option.selected && option.isVisible) {
40
+ sourceOptions.splice(i, 1);
41
+ destinationOptions.push(option);
42
+ option.selected = false;
43
+ i--;
44
+ }
45
+ }
46
+ if (fromAvailable) {
47
+ setAvailableOptions([...sourceOptions]);
48
+ setChosenOptions([...destinationOptions]);
49
+ } else {
50
+ setChosenOptions([...sourceOptions]);
51
+ setAvailableOptions([...destinationOptions]);
52
+ }
53
+ };
54
+
55
+ // callback for moving all options between lists
56
+ const moveAll = (fromAvailable: boolean) => {
57
+ if (fromAvailable) {
58
+ setChosenOptions([...availableOptions.filter(option => option.isVisible), ...chosenOptions]);
59
+ setAvailableOptions([...availableOptions.filter(option => !option.isVisible)]);
60
+ } else {
61
+ setAvailableOptions([...chosenOptions.filter(option => option.isVisible), ...availableOptions]);
62
+ setChosenOptions([...chosenOptions.filter(option => !option.isVisible)]);
63
+ }
64
+ };
65
+
66
+ // callback when option is selected
67
+ const onOptionSelect = (
68
+ event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent,
69
+ index: number,
70
+ isChosen: boolean
71
+ ) => {
72
+ if (isChosen) {
73
+ const newChosen = [...chosenOptions];
74
+ newChosen[index].selected = !chosenOptions[index].selected;
75
+ setChosenOptions(newChosen);
76
+ } else {
77
+ const newAvailable = [...availableOptions];
78
+ newAvailable[index].selected = !availableOptions[index].selected;
79
+ setAvailableOptions(newAvailable);
80
+ }
81
+ };
82
+
83
+ // builds a search input - used in each dual list selector pane
84
+ const buildSearchInput = (isAvailable: boolean) => {
85
+ const onChange = (value: string) => {
86
+ isAvailable ? setAvailableFilter(value) : setChosenFilter(value);
87
+ const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions];
88
+ toFilter.forEach(option => {
89
+ option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase());
90
+ });
91
+ };
92
+
93
+ return (
94
+ <SearchInput
95
+ value={isAvailable ? availableFilter : chosenFilter}
96
+ onChange={onChange}
97
+ onClear={() => onChange('')}
98
+ />
99
+ );
100
+ };
101
+
102
+ // builds a sort control - passed to both dual list selector panes
103
+ const buildSort = (isAvailable: boolean) => {
104
+ const onSort = () => {
105
+ const toSort = isAvailable ? [...availableOptions] : [...chosenOptions];
106
+ toSort.sort((a, b) => {
107
+ if (a.text > b.text) {
108
+ return 1;
109
+ }
110
+ if (a.text < b.text) {
111
+ return -1;
112
+ }
113
+ return 0;
114
+ });
115
+ if (isAvailable) {
116
+ setAvailableOptions(toSort);
117
+ } else {
118
+ setChosenOptions(toSort);
119
+ }
120
+ };
121
+
122
+ return (
123
+ <Button variant={ButtonVariant.plain} onClick={onSort} aria-label="Sort" key="sortButton">
124
+ <PficonSortCommonAscIcon />
125
+ </Button>
126
+ );
127
+ };
128
+
129
+ return (
130
+ <DualListSelector>
131
+ <DualListSelectorPane
132
+ title="Available"
133
+ status={`${availableOptions.filter(option => option.selected && option.isVisible).length} of ${
134
+ availableOptions.filter(option => option.isVisible).length
135
+ } options selected`}
136
+ searchInput={buildSearchInput(true)}
137
+ actions={[buildSort(true)]}
138
+ >
139
+ <DualListSelectorList>
140
+ {availableOptions.map((option, index) =>
141
+ option.isVisible ? (
142
+ <DualListSelectorListItem
143
+ key={index}
144
+ isSelected={option.selected}
145
+ id={`composable-available-option-${index}`}
146
+ onOptionSelect={e => onOptionSelect(e, index, false)}
147
+ >
148
+ {option.text}
149
+ </DualListSelectorListItem>
150
+ ) : null
151
+ )}
152
+ </DualListSelectorList>
153
+ </DualListSelectorPane>
154
+ <DualListSelectorControlsWrapper aria-label="Selector controls">
155
+ <DualListSelectorControl
156
+ isDisabled={!availableOptions.some(option => option.selected)}
157
+ onClick={() => moveSelected(true)}
158
+ aria-label="Add selected"
159
+ tooltipContent="Add selected"
160
+ >
161
+ <AngleRightIcon />
162
+ </DualListSelectorControl>
163
+ <DualListSelectorControl
164
+ isDisabled={availableOptions.length === 0}
165
+ onClick={() => moveAll(true)}
166
+ aria-label="Add all"
167
+ tooltipContent="Add all"
168
+ >
169
+ <AngleDoubleRightIcon />
170
+ </DualListSelectorControl>
171
+ <DualListSelectorControl
172
+ isDisabled={chosenOptions.length === 0}
173
+ onClick={() => moveAll(false)}
174
+ aria-label="Remove all"
175
+ tooltipContent="Remove all"
176
+ >
177
+ <AngleDoubleLeftIcon />
178
+ </DualListSelectorControl>
179
+ <DualListSelectorControl
180
+ onClick={() => moveSelected(false)}
181
+ isDisabled={!chosenOptions.some(option => option.selected)}
182
+ aria-label="Remove selected"
183
+ tooltipContent="Remove selected"
184
+ >
185
+ <AngleLeftIcon />
186
+ </DualListSelectorControl>
187
+ </DualListSelectorControlsWrapper>
188
+ <DualListSelectorPane
189
+ title="Chosen"
190
+ status={`${chosenOptions.filter(option => option.selected && option.isVisible).length} of ${
191
+ chosenOptions.filter(option => option.isVisible).length
192
+ } options selected`}
193
+ searchInput={buildSearchInput(false)}
194
+ actions={[buildSort(false)]}
195
+ isChosen
196
+ >
197
+ <DualListSelectorList>
198
+ {chosenOptions.map((option, index) =>
199
+ option.isVisible ? (
200
+ <DualListSelectorListItem
201
+ key={index}
202
+ isSelected={option.selected}
203
+ id={`composable-chosen-option-${index}`}
204
+ onOptionSelect={e => onOptionSelect(e, index, true)}
205
+ >
206
+ {option.text}
207
+ </DualListSelectorListItem>
208
+ ) : null
209
+ )}
210
+ </DualListSelectorList>
211
+ </DualListSelectorPane>
212
+ </DualListSelector>
213
+ );
214
+ };
@@ -0,0 +1,190 @@
1
+ import React from 'react';
2
+ import {
3
+ DragDrop,
4
+ Droppable,
5
+ Draggable,
6
+ DualListSelector,
7
+ DualListSelectorPane,
8
+ DualListSelectorList,
9
+ DualListSelectorListItem,
10
+ DualListSelectorControlsWrapper,
11
+ DualListSelectorControl
12
+ } from '@patternfly/react-core';
13
+ import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon';
14
+ import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
15
+ import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
16
+ import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
17
+
18
+ interface SourceType {
19
+ droppableId: string;
20
+ index: number;
21
+ }
22
+
23
+ interface DestinationType extends SourceType {}
24
+
25
+ export const DualListSelectorComposableDragDrop: React.FunctionComponent = () => {
26
+ const [ignoreNextOptionSelect, setIgnoreNextOptionSelect] = React.useState(false);
27
+ const [availableOptions, setAvailableOptions] = React.useState([
28
+ { text: 'Apple', selected: false, isVisible: true },
29
+ { text: 'Banana', selected: false, isVisible: true },
30
+ { text: 'Pineapple', selected: false, isVisible: true }
31
+ ]);
32
+ const [chosenOptions, setChosenOptions] = React.useState([
33
+ { text: 'Orange', selected: false, isVisible: true },
34
+ { text: 'Grape', selected: false, isVisible: true },
35
+ { text: 'Peach', selected: false, isVisible: true },
36
+ { text: 'Strawberry', selected: false, isVisible: true }
37
+ ]);
38
+
39
+ const moveSelected = (fromAvailable: boolean) => {
40
+ const sourceOptions = fromAvailable ? availableOptions : chosenOptions;
41
+ const destinationOptions = fromAvailable ? chosenOptions : availableOptions;
42
+ for (let i = 0; i < sourceOptions.length; i++) {
43
+ const option = sourceOptions[i];
44
+ if (option.selected && option.isVisible) {
45
+ sourceOptions.splice(i, 1);
46
+ destinationOptions.push(option);
47
+ option.selected = false;
48
+ i--;
49
+ }
50
+ }
51
+ if (fromAvailable) {
52
+ setAvailableOptions([...sourceOptions]);
53
+ setChosenOptions([...destinationOptions]);
54
+ } else {
55
+ setChosenOptions([...sourceOptions]);
56
+ setAvailableOptions([...destinationOptions]);
57
+ }
58
+ };
59
+
60
+ const moveAll = (fromAvailable: boolean) => {
61
+ if (fromAvailable) {
62
+ setChosenOptions([...availableOptions.filter(option => option.isVisible), ...chosenOptions]);
63
+ setAvailableOptions([...availableOptions.filter(option => !option.isVisible)]);
64
+ } else {
65
+ setAvailableOptions([...chosenOptions.filter(option => option.isVisible), ...availableOptions]);
66
+ setChosenOptions([...chosenOptions.filter(option => !option.isVisible)]);
67
+ }
68
+ };
69
+
70
+ const onOptionSelect = (
71
+ event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent,
72
+ index: number,
73
+ isChosen: boolean
74
+ ) => {
75
+ if (ignoreNextOptionSelect) {
76
+ setIgnoreNextOptionSelect(false);
77
+ return;
78
+ }
79
+ if (isChosen) {
80
+ const newChosen = [...chosenOptions];
81
+ newChosen[index].selected = !chosenOptions[index].selected;
82
+ setChosenOptions(newChosen);
83
+ } else {
84
+ const newAvailable = [...availableOptions];
85
+ newAvailable[index].selected = !availableOptions[index].selected;
86
+ setAvailableOptions(newAvailable);
87
+ }
88
+ };
89
+
90
+ const onDrop = (source: SourceType, dest: DestinationType) => {
91
+ if (dest) {
92
+ const newList = [...chosenOptions];
93
+ const [removed] = newList.splice(source.index, 1);
94
+ newList.splice(dest.index, 0, removed);
95
+ setChosenOptions(newList);
96
+ return true;
97
+ }
98
+ return false;
99
+ };
100
+
101
+ return (
102
+ <DualListSelector>
103
+ <DualListSelectorPane
104
+ title="Available"
105
+ status={`${availableOptions.filter(option => option.selected && option.isVisible).length} of ${
106
+ availableOptions.filter(option => option.isVisible).length
107
+ } options selected`}
108
+ >
109
+ <DualListSelectorList>
110
+ {availableOptions.map((option, index) =>
111
+ option.isVisible ? (
112
+ <DualListSelectorListItem
113
+ key={index}
114
+ isSelected={option.selected}
115
+ id={`composable-drag-drop-available-option-${index}`}
116
+ onOptionSelect={e => onOptionSelect(e, index, false)}
117
+ >
118
+ {option.text}
119
+ </DualListSelectorListItem>
120
+ ) : null
121
+ )}
122
+ </DualListSelectorList>
123
+ </DualListSelectorPane>
124
+ <DualListSelectorControlsWrapper aria-label="Selector controls">
125
+ <DualListSelectorControl
126
+ isDisabled={!availableOptions.some(option => option.selected)}
127
+ onClick={() => moveSelected(true)}
128
+ aria-label="Add selected"
129
+ >
130
+ <AngleRightIcon />
131
+ </DualListSelectorControl>
132
+ <DualListSelectorControl
133
+ isDisabled={availableOptions.length === 0}
134
+ onClick={() => moveAll(true)}
135
+ aria-label="Add all"
136
+ >
137
+ <AngleDoubleRightIcon />
138
+ </DualListSelectorControl>
139
+ <DualListSelectorControl
140
+ isDisabled={chosenOptions.length === 0}
141
+ onClick={() => moveAll(false)}
142
+ aria-label="Remove all"
143
+ >
144
+ <AngleDoubleLeftIcon />
145
+ </DualListSelectorControl>
146
+ <DualListSelectorControl
147
+ onClick={() => moveSelected(false)}
148
+ isDisabled={!chosenOptions.some(option => option.selected)}
149
+ aria-label="Remove selected"
150
+ >
151
+ <AngleLeftIcon />
152
+ </DualListSelectorControl>
153
+ </DualListSelectorControlsWrapper>
154
+ <DragDrop
155
+ onDrag={() => {
156
+ setIgnoreNextOptionSelect(true);
157
+ return true;
158
+ }}
159
+ onDrop={onDrop}
160
+ >
161
+ <DualListSelectorPane
162
+ title="Chosen"
163
+ status={`${chosenOptions.filter(option => option.selected && option.isVisible).length} of ${
164
+ chosenOptions.filter(option => option.isVisible).length
165
+ } options selected`}
166
+ isChosen
167
+ >
168
+ <Droppable hasNoWrapper>
169
+ <DualListSelectorList>
170
+ {chosenOptions.map((option, index) =>
171
+ option.isVisible ? (
172
+ <Draggable key={index} hasNoWrapper>
173
+ <DualListSelectorListItem
174
+ isSelected={option.selected}
175
+ id={`composable-drag-drop-chosen-option-${index}`}
176
+ onOptionSelect={e => onOptionSelect(e, index, true)}
177
+ isDraggable
178
+ >
179
+ {option.text}
180
+ </DualListSelectorListItem>
181
+ </Draggable>
182
+ ) : null
183
+ )}
184
+ </DualListSelectorList>
185
+ </Droppable>
186
+ </DualListSelectorPane>
187
+ </DragDrop>
188
+ </DualListSelector>
189
+ );
190
+ };
@@ -24,7 +24,7 @@ interface ExampleProps {
24
24
  data: FoodNode[];
25
25
  }
26
26
 
27
- export const ComposableDualListSelectorTree: React.FunctionComponent<ExampleProps> = ({ data }: ExampleProps) => {
27
+ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProps> = ({ data }: ExampleProps) => {
28
28
  const [checkedLeafIds, setCheckedLeafIds] = React.useState<string[]>([]);
29
29
  const [chosenLeafIds, setChosenLeafIds] = React.useState<string[]>(['beans', 'beef', 'chicken', 'tofu']);
30
30
  const [chosenFilter, setChosenFilter] = React.useState<string>('');
@@ -290,8 +290,8 @@ export const ComposableDualListSelectorTree: React.FunctionComponent<ExampleProp
290
290
  );
291
291
  };
292
292
 
293
- export const ComposableDualListSelectorTreeExample: React.FunctionComponent = () => (
294
- <ComposableDualListSelectorTree
293
+ export const DualListSelectorComposableTreeExample: React.FunctionComponent = () => (
294
+ <DualListSelectorComposableTree
295
295
  data={[
296
296
  {
297
297
  id: 'fruits',