@patternfly/react-docs 7.6.0-prerelease.8 → 7.6.0-prerelease.9

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 (163) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/package.json +12 -11
  4. package/patternfly-docs/generated/components/about-modal/react.js +0 -149
  5. package/patternfly-docs/generated/components/accordion/react.js +0 -262
  6. package/patternfly-docs/generated/components/action-list/react.js +0 -144
  7. package/patternfly-docs/generated/components/alert/react-demos.js +0 -56
  8. package/patternfly-docs/generated/components/alert/react.js +0 -1433
  9. package/patternfly-docs/generated/components/avatar/react.js +0 -166
  10. package/patternfly-docs/generated/components/back-to-top/react-demos.js +0 -60
  11. package/patternfly-docs/generated/components/back-to-top/react.js +0 -77
  12. package/patternfly-docs/generated/components/backdrop/react.js +0 -64
  13. package/patternfly-docs/generated/components/background-image/react.js +0 -62
  14. package/patternfly-docs/generated/components/badge/react.js +0 -97
  15. package/patternfly-docs/generated/components/banner/react-demos.js +0 -57
  16. package/patternfly-docs/generated/components/banner/react.js +0 -148
  17. package/patternfly-docs/generated/components/brand/react.js +0 -142
  18. package/patternfly-docs/generated/components/breadcrumb/react.js +0 -206
  19. package/patternfly-docs/generated/components/button/react-demos.js +0 -57
  20. package/patternfly-docs/generated/components/button/react.js +0 -826
  21. package/patternfly-docs/generated/components/card/react-demos.js +0 -201
  22. package/patternfly-docs/generated/components/card/react.js +0 -1015
  23. package/patternfly-docs/generated/components/charts/area-chart/-Victory.js +0 -1350
  24. package/patternfly-docs/generated/components/charts/bar-chart/-Victory.js +0 -1334
  25. package/patternfly-docs/generated/components/charts/box-plot-chart/-Victory.js +0 -1282
  26. package/patternfly-docs/generated/components/charts/bullet-chart/-Victory.js +0 -848
  27. package/patternfly-docs/generated/components/charts/colors-for-charts/-Victory.js +0 -192
  28. package/patternfly-docs/generated/components/charts/donut-chart/-Victory.js +0 -426
  29. package/patternfly-docs/generated/components/charts/donut-utilization-chart/-Victory.js +0 -804
  30. package/patternfly-docs/generated/components/charts/legends/-Victory.js +0 -3230
  31. package/patternfly-docs/generated/components/charts/line-chart/-Victory.js +0 -1178
  32. package/patternfly-docs/generated/components/charts/line-chart/ECharts.js +0 -525
  33. package/patternfly-docs/generated/components/charts/patterns/-Victory.js +0 -3382
  34. package/patternfly-docs/generated/components/charts/pie-chart/-Victory.js +0 -377
  35. package/patternfly-docs/generated/components/charts/resize-observer/-Victory.js +0 -2475
  36. package/patternfly-docs/generated/components/charts/sankey-chart/ECharts.js +0 -538
  37. package/patternfly-docs/generated/components/charts/scatter-chart/-Victory.js +0 -1551
  38. package/patternfly-docs/generated/components/charts/skeletons/-Victory.js +0 -4115
  39. package/patternfly-docs/generated/components/charts/sparkline-chart/-Victory.js +0 -955
  40. package/patternfly-docs/generated/components/charts/stack-chart/-Victory.js +0 -1173
  41. package/patternfly-docs/generated/components/charts/threshold-chart/-Victory.js +0 -1166
  42. package/patternfly-docs/generated/components/charts/tooltips/-Victory.js +0 -413
  43. package/patternfly-docs/generated/components/chip/react-deprecated.js +0 -323
  44. package/patternfly-docs/generated/components/clipboard-copy/react.js +0 -373
  45. package/patternfly-docs/generated/components/code-block/react.js +0 -148
  46. package/patternfly-docs/generated/components/code-editor/react.js +0 -659
  47. package/patternfly-docs/generated/components/compass/react-demos.js +0 -147
  48. package/patternfly-docs/generated/components/compass/react.js +0 -440
  49. package/patternfly-docs/generated/components/content/react.js +0 -248
  50. package/patternfly-docs/generated/components/data-list/react-demos.js +0 -90
  51. package/patternfly-docs/generated/components/data-list/react.js +0 -709
  52. package/patternfly-docs/generated/components/date-and-time/calendar-month/react.js +0 -283
  53. package/patternfly-docs/generated/components/date-and-time/date-and-time-picker/react-demos.js +0 -64
  54. package/patternfly-docs/generated/components/date-and-time/date-picker/react-demos.js +0 -83
  55. package/patternfly-docs/generated/components/date-and-time/date-picker/react.js +0 -395
  56. package/patternfly-docs/generated/components/date-and-time/time-picker/react.js +0 -241
  57. package/patternfly-docs/generated/components/description-list/react-demos.js +0 -58
  58. package/patternfly-docs/generated/components/description-list/react.js +0 -743
  59. package/patternfly-docs/generated/components/divider/react.js +0 -126
  60. package/patternfly-docs/generated/components/drag-and-drop/react-demos.js +0 -351
  61. package/patternfly-docs/generated/components/drag-and-drop/react-deprecated.js +0 -184
  62. package/patternfly-docs/generated/components/drag-and-drop/react.js +0 -137
  63. package/patternfly-docs/generated/components/drawer/react.js +0 -598
  64. package/patternfly-docs/generated/components/dual-list-selector/react-deprecated.js +0 -772
  65. package/patternfly-docs/generated/components/dual-list-selector/react.js +0 -594
  66. package/patternfly-docs/generated/components/empty-state/react.js +0 -199
  67. package/patternfly-docs/generated/components/expandable-section/react-demos.js +0 -65
  68. package/patternfly-docs/generated/components/expandable-section/react.js +0 -408
  69. package/patternfly-docs/generated/components/file-upload/multiple-file-upload/react-demos.js +0 -52
  70. package/patternfly-docs/generated/components/file-upload/multiple-file-upload/react.js +0 -398
  71. package/patternfly-docs/generated/components/file-upload/simple-file-upload/react.js +0 -749
  72. package/patternfly-docs/generated/components/forms/checkbox/react.js +0 -222
  73. package/patternfly-docs/generated/components/forms/form/react.js +0 -1106
  74. package/patternfly-docs/generated/components/forms/form-select/react.js +0 -208
  75. package/patternfly-docs/generated/components/forms/radio/react.js +0 -212
  76. package/patternfly-docs/generated/components/forms/text-area/react.js +0 -160
  77. package/patternfly-docs/generated/components/forms/text-input/react.js +0 -216
  78. package/patternfly-docs/generated/components/helper-text/react-demos.js +0 -180
  79. package/patternfly-docs/generated/components/helper-text/react.js +0 -164
  80. package/patternfly-docs/generated/components/hero/react.js +0 -88
  81. package/patternfly-docs/generated/components/hint/react.js +0 -169
  82. package/patternfly-docs/generated/components/icon/react.js +0 -215
  83. package/patternfly-docs/generated/components/input-group/react.js +0 -182
  84. package/patternfly-docs/generated/components/jump-links/react-demos.js +0 -154
  85. package/patternfly-docs/generated/components/jump-links/react.js +0 -212
  86. package/patternfly-docs/generated/components/label/react-demos.js +0 -57
  87. package/patternfly-docs/generated/components/label/react.js +0 -417
  88. package/patternfly-docs/generated/components/list/react.js +0 -175
  89. package/patternfly-docs/generated/components/login-page/react.js +0 -587
  90. package/patternfly-docs/generated/components/masthead/react-demos.js +0 -79
  91. package/patternfly-docs/generated/components/masthead/react.js +0 -291
  92. package/patternfly-docs/generated/components/menus/application-launcher/react-demos.js +0 -769
  93. package/patternfly-docs/generated/components/menus/context-selector/react-demos.js +0 -665
  94. package/patternfly-docs/generated/components/menus/custom-menus/react-demos.js +0 -187
  95. package/patternfly-docs/generated/components/menus/dropdown/react-templates.js +0 -163
  96. package/patternfly-docs/generated/components/menus/dropdown/react.js +0 -998
  97. package/patternfly-docs/generated/components/menus/menu/react.js +0 -1540
  98. package/patternfly-docs/generated/components/menus/menu-toggle/react.js +0 -747
  99. package/patternfly-docs/generated/components/menus/options-menu/react-demos.js +0 -508
  100. package/patternfly-docs/generated/components/menus/select/react-templates.js +0 -257
  101. package/patternfly-docs/generated/components/menus/select/react.js +0 -998
  102. package/patternfly-docs/generated/components/modal/react-deprecated.js +0 -554
  103. package/patternfly-docs/generated/components/modal/react.js +0 -597
  104. package/patternfly-docs/generated/components/navigation/react-demos.js +0 -356
  105. package/patternfly-docs/generated/components/navigation/react.js +0 -409
  106. package/patternfly-docs/generated/components/notification-badge/react.js +0 -196
  107. package/patternfly-docs/generated/components/notification-drawer/react-demos.js +0 -107
  108. package/patternfly-docs/generated/components/notification-drawer/react.js +0 -394
  109. package/patternfly-docs/generated/components/number-input/react.js +0 -210
  110. package/patternfly-docs/generated/components/overflow-menu/react.js +0 -274
  111. package/patternfly-docs/generated/components/page/react-demos.js +0 -149
  112. package/patternfly-docs/generated/components/page/react.js +0 -1352
  113. package/patternfly-docs/generated/components/pagination/react.js +0 -492
  114. package/patternfly-docs/generated/components/panel/react.js +0 -236
  115. package/patternfly-docs/generated/components/popover/react.js +0 -390
  116. package/patternfly-docs/generated/components/progress/react-demos.js +0 -59
  117. package/patternfly-docs/generated/components/progress/react.js +0 -283
  118. package/patternfly-docs/generated/components/progress-stepper/react-demos.js +0 -45
  119. package/patternfly-docs/generated/components/progress-stepper/react.js +0 -219
  120. package/patternfly-docs/generated/components/search-input/react-demos.js +0 -113
  121. package/patternfly-docs/generated/components/search-input/react.js +0 -263
  122. package/patternfly-docs/generated/components/sidebar/react.js +0 -236
  123. package/patternfly-docs/generated/components/simple-list/react.js +0 -200
  124. package/patternfly-docs/generated/components/skeleton/react-demos.js +0 -44
  125. package/patternfly-docs/generated/components/skeleton/react.js +0 -122
  126. package/patternfly-docs/generated/components/skip-to-content/react.js +0 -73
  127. package/patternfly-docs/generated/components/slider/react.js +0 -309
  128. package/patternfly-docs/generated/components/spinner/react.js +0 -111
  129. package/patternfly-docs/generated/components/switch/react.js +0 -163
  130. package/patternfly-docs/generated/components/table/react-demos.js +0 -355
  131. package/patternfly-docs/generated/components/table/react-deprecated.js +0 -1350
  132. package/patternfly-docs/generated/components/table/react.js +0 -3241
  133. package/patternfly-docs/generated/components/tabs/react-demos.js +0 -108
  134. package/patternfly-docs/generated/components/tabs/react.js +0 -1359
  135. package/patternfly-docs/generated/components/text-input-group/react-demos.js +0 -152
  136. package/patternfly-docs/generated/components/text-input-group/react.js +0 -278
  137. package/patternfly-docs/generated/components/tile/react-deprecated.js +0 -242
  138. package/patternfly-docs/generated/components/timestamp/react.js +0 -283
  139. package/patternfly-docs/generated/components/title/react.js +0 -94
  140. package/patternfly-docs/generated/components/toggle-group/react.js +0 -299
  141. package/patternfly-docs/generated/components/toolbar/react-demos.js +0 -66
  142. package/patternfly-docs/generated/components/toolbar/react.js +0 -932
  143. package/patternfly-docs/generated/components/tooltip/react.js +0 -241
  144. package/patternfly-docs/generated/components/tree-view/react.js +0 -429
  145. package/patternfly-docs/generated/components/truncate/react.js +0 -211
  146. package/patternfly-docs/generated/components/wizard/react-demos.js +0 -87
  147. package/patternfly-docs/generated/components/wizard/react-deprecated.js +0 -788
  148. package/patternfly-docs/generated/components/wizard/react.js +0 -986
  149. package/patternfly-docs/generated/developer-guides/open-ui-automation/react.js +0 -285
  150. package/patternfly-docs/generated/foundations-and-styles/layouts/bullseye/react.js +0 -70
  151. package/patternfly-docs/generated/foundations-and-styles/layouts/flex/react.js +0 -506
  152. package/patternfly-docs/generated/foundations-and-styles/layouts/gallery/react.js +0 -94
  153. package/patternfly-docs/generated/foundations-and-styles/layouts/grid/react.js +0 -272
  154. package/patternfly-docs/generated/foundations-and-styles/layouts/level/react.js +0 -87
  155. package/patternfly-docs/generated/foundations-and-styles/layouts/split/react.js +0 -124
  156. package/patternfly-docs/generated/foundations-and-styles/layouts/stack/react.js +0 -112
  157. package/patternfly-docs/generated/index.js +0 -1769
  158. package/patternfly-docs/generated/patterns/card-view/react-demos.js +0 -78
  159. package/patternfly-docs/generated/patterns/filters/react-demos.js +0 -141
  160. package/patternfly-docs/generated/patterns/password-generator/react-demos.js +0 -51
  161. package/patternfly-docs/generated/patterns/password-strength/react-demos.js +0 -61
  162. package/patternfly-docs/generated/patterns/primary-detail/react-demos.js +0 -124
  163. package/patternfly-docs/generated/patterns/right-to-left/react-demos.js +0 -81
@@ -1,594 +0,0 @@
1
- import React from 'react';
2
- import { AutoLinkHeader, Example, Link as PatternflyThemeLink } from '@patternfly/documentation-framework/components';
3
- import { Fragment, useEffect, useMemo, useState } from 'react';
4
- import RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';
5
- import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
6
- import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
7
- import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
8
- import RhMicronsSortDownSmallToLargeIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-sort-down-small-to-large-icon';
9
- import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
10
- import RhUiEllipsisVerticalFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ellipsis-vertical-fill-icon';
11
- import { DragDropSort, DragDropContainer, Droppable as NewDroppable } from '@patternfly/react-drag-drop';
12
- const pageData = {
13
- "id": "Dual list selector",
14
- "section": "components",
15
- "subsection": "",
16
- "deprecated": false,
17
- "template": false,
18
- "beta": false,
19
- "demo": false,
20
- "newImplementationLink": false,
21
- "source": "react",
22
- "tabName": null,
23
- "slug": "/components/dual-list-selector/react",
24
- "sourceLink": "https://github.com/patternfly/patternfly-react/blob/main/packages/react-core/src/components/DualListSelector/examples/DualListSelector.md",
25
- "relPath": "packages/react-core/src/components/DualListSelector/examples/DualListSelector.md",
26
- "propComponents": [
27
- {
28
- "name": "DualListSelector",
29
- "description": "Acts as a container for all other DualListSelector sub-components when using a\ncomposable dual list selector.",
30
- "props": [
31
- {
32
- "name": "children",
33
- "type": "React.ReactNode",
34
- "description": "Content to be rendered in the dual list selector."
35
- },
36
- {
37
- "name": "className",
38
- "type": "string",
39
- "description": "Additional classes applied to the dual list selector."
40
- },
41
- {
42
- "name": "hasAnimations",
43
- "type": "boolean",
44
- "description": "Flag indicating whether a tree dual list selector has animations. This will always render\nnested dual list selector items rather than dynamically rendering them. This prop will be removed in\nthe next breaking change release in favor of defaulting to always-rendered items."
45
- },
46
- {
47
- "name": "id",
48
- "type": "string",
49
- "description": "ID of the dual list selector."
50
- },
51
- {
52
- "name": "isTree",
53
- "type": "boolean",
54
- "description": "Flag indicating if the dual list selector uses trees instead of simple lists.",
55
- "defaultValue": "false"
56
- }
57
- ]
58
- },
59
- {
60
- "name": "DualListSelectorPane",
61
- "description": "Acts as the container for a list of options that are either available or chosen,\ndepending on the pane type (available or chosen). A search input and other actions,\nsuch as sorting, can also be passed into this sub-component.",
62
- "props": [
63
- {
64
- "name": "actions",
65
- "type": "React.ReactNode[]",
66
- "description": "Actions to place above the pane."
67
- },
68
- {
69
- "name": "children",
70
- "type": "React.ReactNode",
71
- "description": "A dual list selector list or dual list selector tree to be rendered in the pane."
72
- },
73
- {
74
- "name": "className",
75
- "type": "string",
76
- "description": "Additional classes applied to the dual list selector pane.",
77
- "defaultValue": "''"
78
- },
79
- {
80
- "name": "id",
81
- "type": "string",
82
- "description": "ID of the pane."
83
- },
84
- {
85
- "name": "isChosen",
86
- "type": "boolean",
87
- "description": "Flag indicating if this pane is the chosen pane.",
88
- "defaultValue": "false"
89
- },
90
- {
91
- "name": "isDisabled",
92
- "type": "boolean",
93
- "description": "Flag indicating whether the component is disabled.",
94
- "defaultValue": "false"
95
- },
96
- {
97
- "name": "listMinHeight",
98
- "type": "string",
99
- "description": "Minimum height of the list of options rendered in the pane. *"
100
- },
101
- {
102
- "name": "onSearch",
103
- "type": "(event: React.ChangeEvent<HTMLInputElement>) => void",
104
- "description": "Callback for search input. To be used when isSearchable is true."
105
- },
106
- {
107
- "name": "searchInput",
108
- "type": "React.ReactNode",
109
- "description": "A search input placed above the list at the top of the pane, before actions."
110
- },
111
- {
112
- "name": "status",
113
- "type": "string",
114
- "description": "Status to display above the pane.",
115
- "defaultValue": "''"
116
- },
117
- {
118
- "name": "title",
119
- "type": "React.ReactNode",
120
- "description": "Title of the pane.",
121
- "defaultValue": "''"
122
- }
123
- ]
124
- },
125
- {
126
- "name": "DualListSelectorList",
127
- "description": "Acts as the container for DualListSelectorListItem sub-components.",
128
- "props": [
129
- {
130
- "name": "children",
131
- "type": "React.ReactNode",
132
- "description": "Content rendered inside the dual list selector list."
133
- }
134
- ]
135
- },
136
- {
137
- "name": "DualListSelectorListItem",
138
- "description": "Creates an individual option that can be selected and moved between the\ndual list selector panes. This is contained within the DualListSelectorList sub-component.",
139
- "props": [
140
- {
141
- "name": "children",
142
- "type": "React.ReactNode",
143
- "description": "Content rendered inside the dual list selector."
144
- },
145
- {
146
- "name": "className",
147
- "type": "string",
148
- "description": "Additional classes applied to the dual list selector."
149
- },
150
- {
151
- "name": "draggableButtonAriaLabel",
152
- "type": "string",
153
- "description": "Accessible label for the draggable button on draggable list items."
154
- },
155
- {
156
- "name": "id",
157
- "type": "string",
158
- "description": "ID of the option."
159
- },
160
- {
161
- "name": "isDisabled",
162
- "type": "boolean",
163
- "description": "Flag indicating if the dual list selector is in a disabled state."
164
- },
165
- {
166
- "name": "isDraggable",
167
- "type": "boolean",
168
- "description": "Flag indicating this item is draggable for reordering."
169
- },
170
- {
171
- "name": "isSelected",
172
- "type": "boolean",
173
- "description": "Flag indicating the list item is currently selected."
174
- },
175
- {
176
- "name": "onOptionSelect",
177
- "type": "(event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent, id?: string) => void",
178
- "description": "Callback fired when an option is selected."
179
- }
180
- ]
181
- },
182
- {
183
- "name": "DualListSelectorControlsWrapper",
184
- "description": "Acts as the container for the DualListSelectorControl sub-components.",
185
- "props": [
186
- {
187
- "name": "aria-label",
188
- "type": "string",
189
- "description": "Accessible label for the dual list selector controls wrapper."
190
- },
191
- {
192
- "name": "children",
193
- "type": "React.ReactNode",
194
- "description": "Content to be rendered inside of the controls wrapper."
195
- },
196
- {
197
- "name": "className",
198
- "type": "string",
199
- "description": "Additional classes added to the wrapper."
200
- }
201
- ]
202
- },
203
- {
204
- "name": "DualListSelectorControl",
205
- "description": "Renders an individual control button for moving selected options between each\ndual list selector pane.",
206
- "props": [
207
- {
208
- "name": "aria-label",
209
- "type": "string",
210
- "description": "Accessible label for the dual list selector control."
211
- },
212
- {
213
- "name": "children",
214
- "type": "React.ReactNode",
215
- "description": "Content to be rendered in the dual list selector control."
216
- },
217
- {
218
- "name": "className",
219
- "type": "string",
220
- "description": "Additional classes applied to the dual list selector control."
221
- },
222
- {
223
- "name": "icon",
224
- "type": "React.ReactNode",
225
- "description": "Icon to be rendered in the dual list selector control."
226
- },
227
- {
228
- "name": "isDisabled",
229
- "type": "boolean",
230
- "description": "Flag indicating the control is disabled."
231
- },
232
- {
233
- "name": "onClick",
234
- "type": "(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void",
235
- "description": "Callback fired when dual list selector control is selected."
236
- },
237
- {
238
- "name": "tooltipContent",
239
- "type": "React.ReactNode",
240
- "description": "Content to be displayed in a tooltip on hover of control."
241
- },
242
- {
243
- "name": "tooltipProps",
244
- "type": "any",
245
- "description": "Additional tooltip properties passed to the tooltip."
246
- }
247
- ]
248
- },
249
- {
250
- "name": "DualListSelectorTree",
251
- "description": "Used in place of the DualListSelectorListItem sub-component when building a\ncomposable dual list selector with a tree.",
252
- "props": [
253
- {
254
- "name": "data",
255
- "type": "DualListSelectorTreeItemData[] | (() => DualListSelectorTreeItemData[])",
256
- "description": "Data of the tree view.",
257
- "required": true
258
- },
259
- {
260
- "name": "defaultAllExpanded",
261
- "type": "boolean",
262
- "description": "Sets the default expanded behavior.",
263
- "defaultValue": "false"
264
- },
265
- {
266
- "name": "hasBadges",
267
- "type": "boolean",
268
- "description": "Flag indicating if all options should have badges.",
269
- "defaultValue": "false"
270
- },
271
- {
272
- "name": "id",
273
- "type": "string",
274
- "description": "ID of the tree view."
275
- },
276
- {
277
- "name": "isDisabled",
278
- "type": "boolean",
279
- "description": "Flag indicating if the dual list selector tree is in the disabled state.",
280
- "defaultValue": "false"
281
- },
282
- {
283
- "name": "onOptionCheck",
284
- "type": "(\n event: React.MouseEvent | React.ChangeEvent<HTMLInputElement> | React.KeyboardEvent,\n isChecked: boolean,\n itemData: DualListSelectorTreeItemData\n) => void",
285
- "description": "Callback fired when an option is checked."
286
- }
287
- ]
288
- },
289
- {
290
- "name": "DualListSelectorTreeItemData",
291
- "description": "",
292
- "props": [
293
- {
294
- "name": "badgeProps",
295
- "type": "any",
296
- "description": "Additional properties to pass to the option badge."
297
- },
298
- {
299
- "name": "checkProps",
300
- "type": "any",
301
- "description": "Additional properties to pass to the option checkbox."
302
- },
303
- {
304
- "name": "children",
305
- "type": "DualListSelectorTreeItemData[]",
306
- "description": "Content rendered inside the dual list selector."
307
- },
308
- {
309
- "name": "className",
310
- "type": "string",
311
- "description": "Additional classes applied to the dual list selector."
312
- },
313
- {
314
- "name": "defaultExpanded",
315
- "type": "boolean",
316
- "description": "Flag indicating this option is expanded by default."
317
- },
318
- {
319
- "name": "hasBadge",
320
- "type": "boolean",
321
- "description": "Flag indicating this option has a badge."
322
- },
323
- {
324
- "name": "id",
325
- "type": "string",
326
- "description": "ID of the option.",
327
- "required": true
328
- },
329
- {
330
- "name": "isChecked",
331
- "type": "boolean",
332
- "description": "Checked state of the option.",
333
- "required": true
334
- },
335
- {
336
- "name": "isDisabled",
337
- "type": "boolean",
338
- "description": "Flag indicating whether the component is disabled."
339
- },
340
- {
341
- "name": "onOptionCheck",
342
- "type": "(\n event: React.MouseEvent | React.ChangeEvent<HTMLInputElement> | React.KeyboardEvent,\n isChecked: boolean,\n isChosen: boolean,\n itemData: DualListSelectorTreeItemData\n ) => void",
343
- "description": "Callback fired when an option is checked."
344
- },
345
- {
346
- "name": "parentId",
347
- "type": "string",
348
- "description": "Parent ID of an option."
349
- },
350
- {
351
- "name": "text",
352
- "type": "string",
353
- "description": "Text of the option.",
354
- "required": true
355
- }
356
- ]
357
- }
358
- ],
359
- "cssPrefix": [
360
- "pf-v6-c-dual-list-selector"
361
- ],
362
- "examples": [
363
- "Basic",
364
- "Basic with tooltips",
365
- "Basic with search",
366
- "Using more complex options with actions",
367
- "With tree",
368
- "Draggable",
369
- "Draggable with multiple drop zones"
370
- ]
371
- };
372
- pageData.liveContext = {
373
- Fragment,
374
- useEffect,
375
- useMemo,
376
- useState,
377
- RhMicronsDoubleCaretLeftIcon,
378
- AngleLeftIcon,
379
- AngleDoubleRightIcon,
380
- AngleRightIcon,
381
- RhMicronsSortDownSmallToLargeIcon,
382
- SearchIcon,
383
- RhUiEllipsisVerticalFillIcon,
384
- DragDropSort,
385
- DragDropContainer,
386
- NewDroppable
387
- };
388
- pageData.examples = {
389
- 'Basic': props =>
390
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport {\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorListItem,\n DualListSelectorControlsWrapper,\n DualListSelectorControl\n} from '@patternfly/react-core';\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\n\ninterface Option {\n text: string;\n selected: boolean;\n isVisible: boolean;\n}\n\nexport const DualListSelectorBasic: React.FunctionComponent = () => {\n const [availableOptions, setAvailableOptions] = useState<Option[]>([\n { text: 'Option 1', selected: false, isVisible: true },\n { text: 'Option 2', selected: false, isVisible: true },\n { text: 'Option 3', selected: false, isVisible: true },\n { text: 'Option 4', selected: false, isVisible: true }\n ]);\n const [chosenOptions, setChosenOptions] = useState<Option[]>([]);\n\n // callback for moving selected options between lists\n const moveSelected = (fromAvailable: boolean) => {\n const sourceOptions = fromAvailable ? availableOptions : chosenOptions;\n const destinationOptions = fromAvailable ? chosenOptions : availableOptions;\n for (let i = 0; i < sourceOptions.length; i++) {\n const option = sourceOptions[i];\n if (option.selected && option.isVisible) {\n sourceOptions.splice(i, 1);\n destinationOptions.push(option);\n option.selected = false;\n i--;\n }\n }\n if (fromAvailable) {\n setAvailableOptions([...sourceOptions]);\n setChosenOptions([...destinationOptions]);\n } else {\n setChosenOptions([...sourceOptions]);\n setAvailableOptions([...destinationOptions]);\n }\n };\n\n // callback for moving all options between lists\n const moveAll = (fromAvailable: boolean) => {\n if (fromAvailable) {\n setChosenOptions([...availableOptions.filter((option) => option.isVisible), ...chosenOptions]);\n setAvailableOptions([...availableOptions.filter((option) => !option.isVisible)]);\n } else {\n setAvailableOptions([...chosenOptions.filter((option) => option.isVisible), ...availableOptions]);\n setChosenOptions([...chosenOptions.filter((option) => !option.isVisible)]);\n }\n };\n\n // callback when option is selected\n const onOptionSelect = (\n event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent,\n index: number,\n isChosen: boolean\n ) => {\n if (isChosen) {\n const newChosen = [...chosenOptions];\n newChosen[index].selected = !chosenOptions[index].selected;\n setChosenOptions(newChosen);\n } else {\n const newAvailable = [...availableOptions];\n newAvailable[index].selected = !availableOptions[index].selected;\n setAvailableOptions(newAvailable);\n }\n };\n\n return (\n <DualListSelector>\n <DualListSelectorPane\n title=\"Available options\"\n status={`${availableOptions.filter((option) => option.selected && option.isVisible).length} of ${\n availableOptions.filter((option) => option.isVisible).length\n } options selected`}\n >\n <DualListSelectorList>\n {availableOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`basic-available-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, false)}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n </DualListSelectorPane>\n <DualListSelectorControlsWrapper>\n <DualListSelectorControl\n isDisabled={!availableOptions.some((option) => option.selected)}\n onClick={() => moveSelected(true)}\n aria-label=\"Add selected\"\n icon={<AngleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={availableOptions.length === 0}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n icon={<AngleDoubleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenOptions.length === 0}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n icon={<RhMicronsDoubleCaretLeftIcon />}\n />\n <DualListSelectorControl\n onClick={() => moveSelected(false)}\n isDisabled={!chosenOptions.some((option) => option.selected)}\n aria-label=\"Remove selected\"\n icon={<AngleLeftIcon />}\n />\n </DualListSelectorControlsWrapper>\n <DualListSelectorPane\n title=\"Chosen options\"\n status={`${chosenOptions.filter((option) => option.selected && option.isVisible).length} of ${\n chosenOptions.filter((option) => option.isVisible).length\n } options selected`}\n isChosen\n >\n <DualListSelectorList>\n {chosenOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`composable-basic-chosen-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, true)}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n </DualListSelectorPane>\n </DualListSelector>\n );\n};\n","title":"Basic","lang":"ts","className":""}}>
391
-
392
- </Example>,
393
- 'Basic with tooltips': props =>
394
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport {\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorListItem,\n DualListSelectorControlsWrapper,\n DualListSelectorControl\n} from '@patternfly/react-core';\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\n\ninterface Option {\n text: string;\n selected: boolean;\n isVisible: boolean;\n}\n\nexport const DualListSelectorBasic: React.FunctionComponent = () => {\n const [availableOptions, setAvailableOptions] = useState<Option[]>([\n { text: 'Option 1', selected: false, isVisible: true },\n { text: 'Option 2', selected: false, isVisible: true },\n { text: 'Option 3', selected: false, isVisible: true },\n { text: 'Option 4', selected: false, isVisible: true }\n ]);\n const [chosenOptions, setChosenOptions] = useState<Option[]>([]);\n\n // callback for moving selected options between lists\n const moveSelected = (fromAvailable: boolean) => {\n const sourceOptions = fromAvailable ? availableOptions : chosenOptions;\n const destinationOptions = fromAvailable ? chosenOptions : availableOptions;\n for (let i = 0; i < sourceOptions.length; i++) {\n const option = sourceOptions[i];\n if (option.selected && option.isVisible) {\n sourceOptions.splice(i, 1);\n destinationOptions.push(option);\n option.selected = false;\n i--;\n }\n }\n if (fromAvailable) {\n setAvailableOptions([...sourceOptions]);\n setChosenOptions([...destinationOptions]);\n } else {\n setChosenOptions([...sourceOptions]);\n setAvailableOptions([...destinationOptions]);\n }\n };\n\n // callback for moving all options between lists\n const moveAll = (fromAvailable: boolean) => {\n if (fromAvailable) {\n setChosenOptions([...availableOptions.filter((option) => option.isVisible), ...chosenOptions]);\n setAvailableOptions([...availableOptions.filter((option) => !option.isVisible)]);\n } else {\n setAvailableOptions([...chosenOptions.filter((option) => option.isVisible), ...availableOptions]);\n setChosenOptions([...chosenOptions.filter((option) => !option.isVisible)]);\n }\n };\n\n // callback when option is selected\n const onOptionSelect = (\n event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent,\n index: number,\n isChosen: boolean\n ) => {\n if (isChosen) {\n const newChosen = [...chosenOptions];\n newChosen[index].selected = !chosenOptions[index].selected;\n setChosenOptions(newChosen);\n } else {\n const newAvailable = [...availableOptions];\n newAvailable[index].selected = !availableOptions[index].selected;\n setAvailableOptions(newAvailable);\n }\n };\n\n return (\n <DualListSelector>\n <DualListSelectorPane\n title=\"Available options\"\n status={`${availableOptions.filter((option) => option.selected && option.isVisible).length} of ${\n availableOptions.filter((option) => option.isVisible).length\n } options selected`}\n >\n <DualListSelectorList>\n {availableOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`tooltips-available-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, false)}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n </DualListSelectorPane>\n <DualListSelectorControlsWrapper>\n <DualListSelectorControl\n isDisabled={!availableOptions.some((option) => option.selected)}\n onClick={() => moveSelected(true)}\n aria-label=\"Add selected\"\n tooltipContent=\"Add selected\"\n tooltipProps={{ position: 'top', 'aria-live': 'off' }}\n icon={<AngleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={availableOptions.length === 0}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n tooltipContent=\"Add all\"\n tooltipProps={{ position: 'right', 'aria-live': 'off' }}\n icon={<AngleDoubleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenOptions.length === 0}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n tooltipContent=\"Remove all\"\n tooltipProps={{ position: 'left', 'aria-live': 'off' }}\n icon={<RhMicronsDoubleCaretLeftIcon />}\n />\n <DualListSelectorControl\n onClick={() => moveSelected(false)}\n isDisabled={!chosenOptions.some((option) => option.selected)}\n aria-label=\"Remove selected\"\n tooltipContent=\"Remove selected\"\n tooltipProps={{ position: 'bottom', 'aria-live': 'off' }}\n icon={<AngleLeftIcon />}\n />\n </DualListSelectorControlsWrapper>\n <DualListSelectorPane\n title=\"Chosen options\"\n status={`${chosenOptions.filter((option) => option.selected && option.isVisible).length} of ${\n chosenOptions.filter((option) => option.isVisible).length\n } options selected`}\n isChosen\n >\n <DualListSelectorList>\n {chosenOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`composable-tooltips-chosen-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, true)}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n </DualListSelectorPane>\n </DualListSelector>\n );\n};\n","title":"Basic with tooltips","lang":"ts","className":""}}>
395
-
396
- </Example>,
397
- 'Basic with search': props =>
398
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport {\n Button,\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorListItem,\n DualListSelectorControlsWrapper,\n DualListSelectorControl,\n SearchInput,\n EmptyState,\n EmptyStateVariant,\n EmptyStateFooter,\n EmptyStateBody,\n EmptyStateActions\n} from '@patternfly/react-core';\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\nimport SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';\n\ninterface Option {\n text: string;\n selected: boolean;\n isVisible: boolean;\n}\n\nexport const DualListSelectorSearch: React.FunctionComponent = () => {\n const [availableOptions, setAvailableOptions] = useState<Option[]>([\n { text: 'Option 1', selected: false, isVisible: true },\n { text: 'Option 2', selected: false, isVisible: true },\n { text: 'Option 3', selected: false, isVisible: true },\n { text: 'Option 4', selected: false, isVisible: true }\n ]);\n\n const [chosenOptions, setChosenOptions] = useState<Option[]>([]);\n const [availableFilter, setAvailableFilter] = useState('');\n const [chosenFilter, setChosenFilter] = useState('');\n\n // callback for moving selected options between lists\n const moveSelected = (fromAvailable: boolean) => {\n const sourceOptions = fromAvailable ? availableOptions : chosenOptions;\n const destinationOptions = fromAvailable ? chosenOptions : availableOptions;\n for (let i = 0; i < sourceOptions.length; i++) {\n const option = sourceOptions[i];\n if (option.selected && option.isVisible) {\n sourceOptions.splice(i, 1);\n destinationOptions.push(option);\n option.selected = false;\n i--;\n }\n }\n if (fromAvailable) {\n setAvailableOptions([...sourceOptions]);\n setChosenOptions([...destinationOptions]);\n } else {\n setChosenOptions([...sourceOptions]);\n setAvailableOptions([...destinationOptions]);\n }\n };\n\n // callback for moving all options between lists\n const moveAll = (fromAvailable: boolean) => {\n if (fromAvailable) {\n setChosenOptions([...availableOptions.filter((option) => option.isVisible), ...chosenOptions]);\n setAvailableOptions([...availableOptions.filter((option) => !option.isVisible)]);\n } else {\n setAvailableOptions([...chosenOptions.filter((option) => option.isVisible), ...availableOptions]);\n setChosenOptions([...chosenOptions.filter((option) => !option.isVisible)]);\n }\n };\n\n // callback when option is selected\n const onOptionSelect = (\n event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent,\n index: number,\n isChosen: boolean\n ) => {\n if (isChosen) {\n const newChosen = [...chosenOptions];\n newChosen[index].selected = !chosenOptions[index].selected;\n setChosenOptions(newChosen);\n } else {\n const newAvailable = [...availableOptions];\n newAvailable[index].selected = !availableOptions[index].selected;\n setAvailableOptions(newAvailable);\n }\n };\n\n const onFilterChange = (value: string, isAvailable: boolean) => {\n isAvailable ? setAvailableFilter(value) : setChosenFilter(value);\n const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions];\n toFilter.forEach((option) => {\n option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase());\n });\n };\n\n // builds a search input - used in each dual list selector pane\n const buildSearchInput = (isAvailable: boolean) => (\n <SearchInput\n value={isAvailable ? availableFilter : chosenFilter}\n onChange={(_event, value) => onFilterChange(value, isAvailable)}\n onClear={() => onFilterChange('', isAvailable)}\n aria-label={isAvailable ? 'Search available options' : 'Search chosen options'}\n />\n );\n\n const buildEmptyState = (isAvailable: boolean) => (\n <EmptyState titleText=\"No results found\" variant={EmptyStateVariant.sm} headingLevel=\"h4\" icon={SearchIcon}>\n <EmptyStateBody>No results match the filter criteria. Clear all filters and try again.</EmptyStateBody>\n <EmptyStateFooter>\n <EmptyStateActions>\n <Button variant=\"link\" onClick={() => onFilterChange('', isAvailable)}>\n Clear all filters\n </Button>\n </EmptyStateActions>\n </EmptyStateFooter>\n </EmptyState>\n );\n\n return (\n <DualListSelector>\n <DualListSelectorPane\n title=\"Available options\"\n status={`${availableOptions.filter((option) => option.selected && option.isVisible).length} of ${\n availableOptions.filter((option) => option.isVisible).length\n } options selected`}\n searchInput={buildSearchInput(true)}\n listMinHeight=\"300px\"\n >\n {availableFilter !== '' &&\n availableOptions.filter((option) => option.isVisible).length === 0 &&\n buildEmptyState(true)}\n\n <DualListSelectorList>\n {availableOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`search-available-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, false)}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n </DualListSelectorPane>\n <DualListSelectorControlsWrapper>\n <DualListSelectorControl\n isDisabled={!availableOptions.some((option) => option.selected)}\n onClick={() => moveSelected(true)}\n aria-label=\"Add selected\"\n icon={<AngleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={availableOptions.length === 0}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n icon={<AngleDoubleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenOptions.length === 0}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n icon={<RhMicronsDoubleCaretLeftIcon />}\n />\n <DualListSelectorControl\n onClick={() => moveSelected(false)}\n isDisabled={!chosenOptions.some((option) => option.selected)}\n aria-label=\"Remove selected\"\n icon={<AngleLeftIcon />}\n />\n </DualListSelectorControlsWrapper>\n <DualListSelectorPane\n title=\"Chosen options\"\n status={`${chosenOptions.filter((option) => option.selected && option.isVisible).length} of ${\n chosenOptions.filter((option) => option.isVisible).length\n } options selected`}\n searchInput={buildSearchInput(false)}\n listMinHeight=\"300px\"\n isChosen\n >\n {chosenFilter !== '' &&\n chosenOptions.filter((option) => option.isVisible).length === 0 &&\n buildEmptyState(false)}\n {chosenOptions.filter((option) => option.isVisible).length > 0 && (\n <DualListSelectorList>\n {chosenOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`composable-search-chosen-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, true)}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n )}\n </DualListSelectorPane>\n </DualListSelector>\n );\n};\n","title":"Basic with search","lang":"ts","className":""}}>
399
-
400
- </Example>,
401
- 'Using more complex options with actions': props =>
402
- <Example {...pageData} {...props} {...{"code":"import { Fragment, useState } from 'react';\nimport {\n Button,\n ButtonVariant,\n Checkbox,\n Dropdown,\n DropdownList,\n DropdownItem,\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorListItem,\n DualListSelectorControlsWrapper,\n DualListSelectorControl,\n SearchInput,\n EmptyState,\n EmptyStateVariant,\n EmptyStateFooter,\n EmptyStateBody,\n EmptyStateActions,\n MenuToggle,\n MenuToggleElement\n} from '@patternfly/react-core';\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\nimport RhMicronsSortDownSmallToLargeIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-sort-down-small-to-large-icon';\nimport SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';\nimport RhUiEllipsisVerticalFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ellipsis-vertical-fill-icon';\n\ninterface Option {\n text: string;\n selected: boolean;\n isVisible: boolean;\n}\n\nexport const DualListSelectorComplexOptionsActionsNext: React.FunctionComponent = () => {\n const [availableOptions, setAvailableOptions] = useState<Option[]>([\n { text: 'Option 1', selected: false, isVisible: true },\n { text: 'Option 2', selected: false, isVisible: true },\n { text: 'Option 3', selected: false, isVisible: true },\n { text: 'Option 4', selected: false, isVisible: true }\n ]);\n\n const [chosenOptions, setChosenOptions] = useState<Option[]>([]);\n const [isAvailableKebabOpen, setIsAvailableKebabOpen] = useState(false);\n const [isChosenKebabOpen, setIsChosenKebabOpen] = useState(false);\n const [availableFilter, setAvailableFilter] = useState('');\n const [chosenFilter, setChosenFilter] = useState('');\n const [isDisabled, setIsDisabled] = useState(false);\n\n // callback for moving selected options between lists\n const moveSelected = (fromAvailable: boolean) => {\n const sourceOptions = fromAvailable ? availableOptions : chosenOptions;\n const destinationOptions = fromAvailable ? chosenOptions : availableOptions;\n for (let i = 0; i < sourceOptions.length; i++) {\n const option = sourceOptions[i];\n if (option.selected && option.isVisible) {\n sourceOptions.splice(i, 1);\n destinationOptions.push(option);\n option.selected = false;\n i--;\n }\n }\n if (fromAvailable) {\n setAvailableOptions([...sourceOptions]);\n setChosenOptions([...destinationOptions]);\n } else {\n setChosenOptions([...sourceOptions]);\n setAvailableOptions([...destinationOptions]);\n }\n };\n\n // callback for moving all options between lists\n const moveAll = (fromAvailable: boolean) => {\n if (fromAvailable) {\n setChosenOptions([...availableOptions.filter((option) => option.isVisible), ...chosenOptions]);\n setAvailableOptions([...availableOptions.filter((option) => !option.isVisible)]);\n } else {\n setAvailableOptions([...chosenOptions.filter((option) => option.isVisible), ...availableOptions]);\n setChosenOptions([...chosenOptions.filter((option) => !option.isVisible)]);\n }\n };\n\n // callback when option is selected\n const onOptionSelect = (\n event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent,\n index: number,\n isChosen: boolean\n ) => {\n if (isChosen) {\n const newChosen = [...chosenOptions];\n newChosen[index].selected = !chosenOptions[index].selected;\n setChosenOptions(newChosen);\n } else {\n const newAvailable = [...availableOptions];\n newAvailable[index].selected = !availableOptions[index].selected;\n setAvailableOptions(newAvailable);\n }\n };\n\n const onFilterChange = (value: string, isAvailable: boolean) => {\n isAvailable ? setAvailableFilter(value) : setChosenFilter(value);\n const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions];\n toFilter.forEach((option) => {\n option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase());\n });\n };\n\n // builds a search input - used in each dual list selector pane\n const buildSearchInput = (isAvailable: boolean) => (\n <SearchInput\n value={isAvailable ? availableFilter : chosenFilter}\n onChange={(_event, value) => onFilterChange(value, isAvailable)}\n onClear={() => onFilterChange('', isAvailable)}\n isDisabled={isDisabled}\n aria-label={isAvailable ? 'Search available options' : 'Search chosen options'}\n />\n );\n\n // builds a sort control - passed to both dual list selector panes\n const buildSort = (isAvailable: boolean) => {\n const onSort = () => {\n const toSort = isAvailable ? [...availableOptions] : [...chosenOptions];\n toSort.sort((a, b) => {\n if (a.text > b.text) {\n return 1;\n }\n if (a.text < b.text) {\n return -1;\n }\n return 0;\n });\n if (isAvailable) {\n setAvailableOptions(toSort);\n } else {\n setChosenOptions(toSort);\n }\n };\n\n const onToggle = (pane: string) => {\n if (pane === 'available') {\n setIsAvailableKebabOpen(!isAvailableKebabOpen);\n } else {\n setIsChosenKebabOpen(!isChosenKebabOpen);\n }\n };\n\n return isAvailable\n ? [\n <Button\n variant={ButtonVariant.plain}\n onClick={onSort}\n aria-label=\"Sort Available\"\n key=\"availableSortButton\"\n isDisabled={isDisabled}\n icon={<RhMicronsSortDownSmallToLargeIcon />}\n />,\n <Dropdown\n toggle={(toggleRef: React.Ref<MenuToggleElement>) => (\n <MenuToggle\n ref={toggleRef}\n isDisabled={isDisabled}\n isExpanded={isAvailableKebabOpen}\n onClick={() => onToggle('available')}\n variant=\"plain\"\n id=\"complex-available-toggle\"\n aria-label=\"Complex actions example available kebab toggle\"\n icon={<RhUiEllipsisVerticalFillIcon />}\n />\n )}\n isOpen={isAvailableKebabOpen}\n onOpenChange={(isOpen: boolean) => setIsAvailableKebabOpen(isOpen)}\n onSelect={() => setIsAvailableKebabOpen(false)}\n key=\"availableDropdown\"\n >\n <DropdownList>\n <DropdownItem key=\"available action\">Available Action</DropdownItem>\n <DropdownItem key=\"available link\" to=\"#\" onClick={(event: any) => event.preventDefault()}>\n Available Link\n </DropdownItem>\n </DropdownList>\n </Dropdown>\n ]\n : [\n <Button\n variant={ButtonVariant.plain}\n onClick={onSort}\n aria-label=\"Sort Chosen\"\n key=\"chosenSortButton\"\n isDisabled={isDisabled}\n icon={<RhMicronsSortDownSmallToLargeIcon />}\n />,\n <Dropdown\n toggle={(toggleRef: React.Ref<MenuToggleElement>) => (\n <MenuToggle\n ref={toggleRef}\n isDisabled={isDisabled}\n isExpanded={isChosenKebabOpen}\n onClick={() => onToggle('chosen')}\n variant=\"plain\"\n id=\"complex-chosen-toggle\"\n aria-label=\"Complex actions example chosen kebab toggle\"\n icon={<RhUiEllipsisVerticalFillIcon />}\n />\n )}\n isOpen={isChosenKebabOpen}\n onOpenChange={(isOpen: boolean) => setIsChosenKebabOpen(isOpen)}\n onSelect={() => setIsChosenKebabOpen(false)}\n key=\"chosenDropdown\"\n >\n <DropdownList>\n <DropdownItem key=\"chosen action\">Chosen Action</DropdownItem>\n <DropdownItem key=\"chosen link\" to=\"#\" onClick={(event: any) => event.preventDefault()}>\n Chosen Link\n </DropdownItem>\n </DropdownList>\n </Dropdown>\n ];\n };\n\n const buildEmptyState = (isAvailable: boolean) => (\n <EmptyState headingLevel=\"h4\" titleText=\"No results found\" icon={SearchIcon} variant={EmptyStateVariant.sm}>\n <EmptyStateBody>No results match the filter criteria. Clear all filters and try again.</EmptyStateBody>\n <EmptyStateFooter>\n <EmptyStateActions>\n <Button variant=\"link\" onClick={() => onFilterChange('', isAvailable)}>\n Clear all filters\n </Button>\n </EmptyStateActions>\n </EmptyStateFooter>\n </EmptyState>\n );\n\n return (\n <Fragment>\n <DualListSelector>\n <DualListSelectorPane\n title=\"Available options\"\n status={`${availableOptions.filter((option) => option.selected && option.isVisible).length} of ${\n availableOptions.filter((option) => option.isVisible).length\n } options selected`}\n searchInput={buildSearchInput(true)}\n actions={[buildSort(true)]}\n listMinHeight=\"300px\"\n isDisabled={isDisabled}\n >\n {availableFilter !== '' &&\n availableOptions.filter((option) => option.isVisible).length === 0 &&\n buildEmptyState(true)}\n\n <DualListSelectorList>\n {availableOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`complex-available-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, false)}\n isDisabled={isDisabled}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n </DualListSelectorPane>\n <DualListSelectorControlsWrapper>\n <DualListSelectorControl\n isDisabled={!availableOptions.some((option) => option.selected) || isDisabled}\n onClick={() => moveSelected(true)}\n aria-label=\"Add selected\"\n icon={<AngleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={availableOptions.length === 0 || isDisabled}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n icon={<AngleDoubleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenOptions.length === 0 || isDisabled}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n icon={<RhMicronsDoubleCaretLeftIcon />}\n />\n <DualListSelectorControl\n onClick={() => moveSelected(false)}\n isDisabled={!chosenOptions.some((option) => option.selected) || isDisabled}\n aria-label=\"Remove selected\"\n icon={<AngleLeftIcon />}\n />\n </DualListSelectorControlsWrapper>\n <DualListSelectorPane\n title=\"Chosen options\"\n status={`${chosenOptions.filter((option) => option.selected && option.isVisible).length} of ${\n chosenOptions.filter((option) => option.isVisible).length\n } options selected`}\n searchInput={buildSearchInput(false)}\n actions={[buildSort(false)]}\n listMinHeight=\"300px\"\n isChosen\n >\n {chosenFilter !== '' &&\n chosenOptions.filter((option) => option.isVisible).length === 0 &&\n buildEmptyState(false)}\n {chosenOptions.filter((option) => option.isVisible).length > 0 && (\n <DualListSelectorList>\n {chosenOptions.map((option, index) =>\n option.isVisible ? (\n <DualListSelectorListItem\n key={index}\n isSelected={option.selected}\n id={`composable-complex-chosen-option-${index}`}\n onOptionSelect={(e) => onOptionSelect(e, index, true)}\n isDisabled={isDisabled}\n >\n {option.text}\n </DualListSelectorListItem>\n ) : null\n )}\n </DualListSelectorList>\n )}\n </DualListSelectorPane>\n </DualListSelector>\n <Checkbox\n key=\"isDisabled\"\n id=\"isDisabled\"\n label=\"isDisabled\"\n aria-label=\"isDisabled\"\n isChecked={isDisabled}\n onChange={() => setIsDisabled(!isDisabled)}\n />\n </Fragment>\n );\n};\n","title":"Using more complex options with actions","lang":"ts","className":""}}>
403
-
404
- </Example>,
405
- 'With tree': props =>
406
- <Example {...pageData} {...props} {...{"code":"import { useMemo, useState } from 'react';\nimport {\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorControlsWrapper,\n DualListSelectorControl,\n DualListSelectorTree,\n DualListSelectorTreeItemData,\n SearchInput,\n Button,\n EmptyState,\n EmptyStateVariant,\n EmptyStateFooter,\n EmptyStateBody,\n EmptyStateActions\n} from '@patternfly/react-core';\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\nimport SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';\n\ninterface FoodNode {\n id: string;\n text: string;\n children?: FoodNode[];\n}\n\ninterface ExampleProps {\n data: FoodNode[];\n}\n\nexport const DualListSelectorComposableTree: React.FunctionComponent<ExampleProps> = ({ data }: ExampleProps) => {\n const [checkedLeafIds, setCheckedLeafIds] = useState<string[]>([]);\n const [chosenLeafIds, setChosenLeafIds] = useState<string[]>(['beans', 'beef', 'chicken', 'tofu']);\n const [chosenFilter, setChosenFilter] = useState<string>('');\n const [availableFilter, setAvailableFilter] = useState<string>('');\n\n // helper function to build memoized lists\n const buildTextById = (node: FoodNode): { [key: string]: string } => {\n let textById = {};\n if (!node) {\n return textById;\n }\n textById[node.id] = node.text;\n if (node.children) {\n node.children.forEach((child) => {\n textById = { ...textById, ...buildTextById(child) };\n });\n }\n return textById;\n };\n\n // helper function to build memoized lists\n const getDescendantLeafIds = (node: FoodNode): string[] => {\n if (!node.children || !node.children.length) {\n return [node.id];\n } else {\n let childrenIds: string[] = [];\n node.children.forEach((child) => {\n childrenIds = [...childrenIds, ...getDescendantLeafIds(child)];\n });\n return childrenIds;\n }\n };\n\n // helper function to build memoized lists\n const getLeavesById = (node: FoodNode): { [key: string]: string[] } => {\n let leavesById = {};\n if (!node.children || !node.children.length) {\n leavesById[node.id] = [node.id];\n } else {\n node.children.forEach((child) => {\n leavesById[node.id] = getDescendantLeafIds(node);\n leavesById = { ...leavesById, ...getLeavesById(child) };\n });\n }\n return leavesById;\n };\n\n // Builds a map of child leaf nodes by node id - memoized so that it only rebuilds the list if the data changes.\n const { memoizedLeavesById, memoizedAllLeaves, memoizedNodeTexts } = useMemo(() => {\n let leavesById = {};\n let allLeaves: string[] = [];\n let nodeTexts = {};\n data.forEach((foodNode) => {\n nodeTexts = { ...nodeTexts, ...buildTextById(foodNode) };\n leavesById = { ...leavesById, ...getLeavesById(foodNode) };\n allLeaves = [...allLeaves, ...getDescendantLeafIds(foodNode)];\n });\n return {\n memoizedLeavesById: leavesById,\n memoizedAllLeaves: allLeaves,\n memoizedNodeTexts: nodeTexts\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data]);\n\n const matchesFilter = (value: string, filter: string) => value.toLowerCase().includes(filter.trim().toLowerCase());\n\n const getVisibleLeafIds = (leafIds: string[], filter: string) => {\n const filterMatchingNodeIds = Object.keys(memoizedLeavesById).filter((nodeId) =>\n matchesFilter(memoizedNodeTexts[nodeId], filter)\n );\n const filterMatchingLeafIds = filterMatchingNodeIds.map((nodeId) => memoizedLeavesById[nodeId]).flat();\n return leafIds.filter((leafId) => filterMatchingLeafIds.includes(leafId));\n };\n\n const availableLeafIds = memoizedAllLeaves.filter((leafId) => !chosenLeafIds.includes(leafId));\n const visibleChosenLeafIds = getVisibleLeafIds(chosenLeafIds, chosenFilter);\n const visibleAvailableLeafIds = getVisibleLeafIds(availableLeafIds, availableFilter);\n\n const moveChecked = (toChosen: boolean) => {\n const visibleCheckedChosenLeafIds = checkedLeafIds.filter((leafId) => visibleChosenLeafIds.includes(leafId));\n const visibleCheckedAvailableLeafIds = checkedLeafIds.filter((leafId) => visibleAvailableLeafIds.includes(leafId));\n\n setChosenLeafIds(\n (prevChosenIds) =>\n toChosen\n ? [...prevChosenIds, ...visibleCheckedAvailableLeafIds] // add visible checked ids to chosen list\n : prevChosenIds.filter((x) => !visibleCheckedChosenLeafIds.includes(x)) // remove visible checked ids from chosen list\n );\n\n // uncheck checked ids that just moved\n setCheckedLeafIds((prevChecked) =>\n toChosen\n ? prevChecked.filter((x) => !visibleCheckedAvailableLeafIds.includes(x))\n : prevChecked.filter((x) => !visibleCheckedChosenLeafIds.includes(x))\n );\n };\n\n const moveAll = (toChosen: boolean) => {\n if (toChosen) {\n setChosenLeafIds((prevChosenIds) => [...prevChosenIds, ...visibleAvailableLeafIds]);\n } else {\n setChosenLeafIds((prevChosenIds) => prevChosenIds.filter((id) => !visibleChosenLeafIds.includes(id)));\n }\n };\n\n const areAllDescendantsSelected = (node: FoodNode, isChosen: boolean) =>\n memoizedLeavesById[node.id].every(\n (id) => checkedLeafIds.includes(id) && (isChosen ? chosenLeafIds.includes(id) : !chosenLeafIds.includes(id))\n );\n const areSomeDescendantsSelected = (node: FoodNode, isChosen: boolean) =>\n memoizedLeavesById[node.id].some(\n (id) => checkedLeafIds.includes(id) && (isChosen ? chosenLeafIds.includes(id) : !chosenLeafIds.includes(id))\n );\n\n const isNodeChecked = (node: FoodNode, isChosen: boolean) => {\n if (areAllDescendantsSelected(node, isChosen)) {\n return true;\n }\n if (areSomeDescendantsSelected(node, isChosen)) {\n return false;\n }\n return false;\n };\n\n const onOptionCheck = (\n event: React.MouseEvent | React.ChangeEvent<HTMLInputElement> | React.KeyboardEvent,\n isChecked: boolean,\n node: DualListSelectorTreeItemData,\n isChosen: boolean\n ) => {\n const nodeIdsToCheck = memoizedLeavesById[node.id].filter((id) =>\n isChosen ? visibleChosenLeafIds.includes(id) : visibleAvailableLeafIds.includes(id)\n );\n\n setCheckedLeafIds((prevChecked) => {\n const otherCheckedNodeNames = prevChecked.filter((id) => !nodeIdsToCheck.includes(id));\n return !isChecked ? otherCheckedNodeNames : [...otherCheckedNodeNames, ...nodeIdsToCheck];\n });\n };\n\n // builds a search input - used in each dual list selector pane\n const buildSearchInput = (isChosen: boolean) => {\n const onChange = (value) => (isChosen ? setChosenFilter(value) : setAvailableFilter(value));\n\n return (\n <SearchInput\n value={isChosen ? chosenFilter : availableFilter}\n onChange={(_event, value) => onChange(value)}\n onClear={() => onChange('')}\n aria-label={isChosen ? 'Search chosen items' : 'Search available items'}\n />\n );\n };\n\n // Builds the DualListSelectorTreeItems from the FoodNodes\n const buildOptions = (\n isChosen: boolean,\n [node, ...remainingNodes]: FoodNode[],\n hasParentMatch: boolean\n ): DualListSelectorTreeItemData[] => {\n if (!node) {\n return [];\n }\n\n const isChecked = isNodeChecked(node, isChosen);\n\n const filterValue = isChosen ? chosenFilter : availableFilter;\n const descendentLeafIds = memoizedLeavesById[node.id];\n const descendentsOnThisPane = isChosen\n ? descendentLeafIds.filter((id) => chosenLeafIds.includes(id))\n : descendentLeafIds.filter((id) => !chosenLeafIds.includes(id));\n\n const hasMatchingChildren =\n filterValue && descendentsOnThisPane.some((id) => matchesFilter(memoizedNodeTexts[id], filterValue));\n const isFilterMatch = filterValue && matchesFilter(node.text, filterValue) && descendentsOnThisPane.length > 0;\n\n // A node is displayed if either of the following is true:\n // - There is no filter value and this node or its descendents belong on this pane\n // - There is a filter value and this node or one of this node's descendents or ancestors match on this pane\n const isDisplayed =\n (!filterValue && descendentsOnThisPane.length > 0) ||\n hasMatchingChildren ||\n (hasParentMatch && descendentsOnThisPane.length > 0) ||\n isFilterMatch;\n\n return [\n ...(isDisplayed\n ? [\n {\n id: node.id,\n text: node.text,\n isChecked,\n checkProps: { 'aria-label': `Select ${node.text}` },\n hasBadge: node.children && node.children.length > 0,\n badgeProps: { isRead: true },\n defaultExpanded: isChosen ? !!chosenFilter : !!availableFilter,\n children: node.children\n ? buildOptions(isChosen, node.children, isFilterMatch || hasParentMatch)\n : undefined\n }\n ]\n : []),\n ...(!isDisplayed && node.children && node.children.length\n ? buildOptions(isChosen, node.children, hasParentMatch)\n : []),\n ...(remainingNodes ? buildOptions(isChosen, remainingNodes, hasParentMatch) : [])\n ];\n };\n\n const buildPane = (isChosen: boolean): React.ReactNode => {\n const options: DualListSelectorTreeItemData[] = buildOptions(isChosen, data, false);\n const numOptions = isChosen ? visibleChosenLeafIds.length : visibleAvailableLeafIds.length;\n const numSelected = checkedLeafIds.filter((id) =>\n isChosen ? visibleChosenLeafIds.includes(id) : visibleAvailableLeafIds.includes(id)\n ).length;\n const status = `${numSelected} of ${numOptions} options selected`;\n const filterApplied = isChosen ? chosenFilter !== '' : availableFilter !== '';\n return (\n <DualListSelectorPane\n title={isChosen ? 'Chosen' : 'Available'}\n status={status}\n searchInput={buildSearchInput(isChosen)}\n isChosen={isChosen}\n listMinHeight=\"300px\"\n >\n {filterApplied && options.length === 0 && (\n <EmptyState headingLevel=\"h4\" titleText=\"No results found\" icon={SearchIcon} variant={EmptyStateVariant.sm}>\n <EmptyStateBody>No results match the filter criteria. Clear all filters and try again.</EmptyStateBody>\n <EmptyStateFooter>\n <EmptyStateActions>\n <Button variant=\"link\" onClick={() => (isChosen ? setChosenFilter('') : setAvailableFilter(''))}>\n Clear all filters\n </Button>\n </EmptyStateActions>\n </EmptyStateFooter>\n </EmptyState>\n )}\n {options.length > 0 && (\n <DualListSelectorList>\n <DualListSelectorTree\n data={options}\n onOptionCheck={(e, isChecked, itemData) => onOptionCheck(e, isChecked, itemData, isChosen)}\n />\n </DualListSelectorList>\n )}\n </DualListSelectorPane>\n );\n };\n\n return (\n <DualListSelector hasAnimations isTree>\n {buildPane(false)}\n <DualListSelectorControlsWrapper>\n <DualListSelectorControl\n isDisabled={!checkedLeafIds.filter((x) => visibleAvailableLeafIds.includes(x)).length}\n onClick={() => moveChecked(true)}\n aria-label=\"Add selected\"\n icon={<AngleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenLeafIds.length === memoizedAllLeaves.length}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n icon={<AngleDoubleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenLeafIds.length === 0}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n icon={<RhMicronsDoubleCaretLeftIcon />}\n />\n <DualListSelectorControl\n onClick={() => moveChecked(false)}\n isDisabled={!checkedLeafIds.filter((x) => visibleChosenLeafIds.includes(x)).length}\n aria-label=\"Remove selected\"\n icon={<AngleLeftIcon />}\n />\n </DualListSelectorControlsWrapper>\n {buildPane(true)}\n </DualListSelector>\n );\n};\n\nexport const DualListSelectorComposableTreeExample: React.FunctionComponent = () => (\n <DualListSelectorComposableTree\n data={[\n {\n id: 'fruits',\n text: 'Fruits',\n children: [\n { id: 'apple', text: 'Apple' },\n {\n id: 'berries',\n text: 'Berries',\n children: [\n { id: 'blueberry', text: 'Blueberry' },\n { id: 'strawberry', text: 'Strawberry' }\n ]\n },\n { id: 'banana', text: 'Banana' }\n ]\n },\n { id: 'bread', text: 'Bread' },\n {\n id: 'vegetables',\n text: 'Vegetables',\n children: [\n { id: 'broccoli', text: 'Broccoli' },\n { id: 'cauliflower', text: 'Cauliflower' }\n ]\n },\n {\n id: 'proteins',\n text: 'Proteins',\n children: [\n { id: 'beans', text: 'Beans' },\n {\n id: 'meats',\n text: 'Meats',\n children: [\n {\n id: 'beef',\n text: 'Beef'\n },\n {\n id: 'chicken',\n text: 'Chicken'\n }\n ]\n },\n { id: 'tofu', text: 'Tofu' }\n ]\n }\n ]}\n />\n);\n","title":"With tree","lang":"ts","className":""}}>
407
-
408
- </Example>,
409
- 'Draggable': props =>
410
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport {\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorListItem,\n DualListSelectorControlsWrapper,\n DualListSelectorControl\n} from '@patternfly/react-core';\nimport { DragDropSort, DraggableObject } from '@patternfly/react-drag-drop';\n\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\n\nexport const ComposableDualListSelector: React.FunctionComponent = () => {\n const [ignoreNextOptionSelect, setIgnoreNextOptionSelect] = useState(false);\n const [availableOptions, setAvailableOptions] = useState<DraggableObject[]>([\n { id: 'Apple', content: 'Apple', props: { key: 'Apple', isSelected: false } },\n { id: 'Banana', content: 'Banana', props: { key: 'Banana', isSelected: false } },\n { id: 'Pineapple', content: 'Pineapple', props: { key: 'Pineapple', isSelected: false } }\n ]);\n\n const [chosenOptions, setChosenOptions] = useState<DraggableObject[]>([\n { id: 'Orange', content: 'Orange', props: { key: 'Orange', isSelected: false } },\n { id: 'Grape', content: 'Grape', props: { key: 'Grape', isSelected: false } },\n { id: 'Peach', content: 'Peach', props: { key: 'Peach', isSelected: false } },\n { id: 'Strawberry', content: 'Strawberry', props: { key: 'Strawberry', isSelected: false } }\n ]);\n\n const moveSelected = (fromAvailable) => {\n const sourceOptions = fromAvailable ? availableOptions : chosenOptions;\n const destinationOptions = fromAvailable ? chosenOptions : availableOptions;\n for (let i = 0; i < sourceOptions.length; i++) {\n const option = sourceOptions[i];\n if (option.props.isSelected) {\n sourceOptions.splice(i, 1);\n destinationOptions.push(option);\n option.props.isSelected = false;\n i--;\n }\n }\n if (fromAvailable) {\n setAvailableOptions([...sourceOptions]);\n setChosenOptions([...destinationOptions]);\n } else {\n setChosenOptions([...sourceOptions]);\n setAvailableOptions([...destinationOptions]);\n }\n };\n\n const moveAll = (fromAvailable) => {\n if (fromAvailable) {\n setChosenOptions([...availableOptions, ...chosenOptions]);\n setAvailableOptions([]);\n } else {\n setAvailableOptions([...chosenOptions, ...availableOptions]);\n setChosenOptions([]);\n }\n };\n\n const onOptionSelect = (event, index, isChosen) => {\n if (ignoreNextOptionSelect) {\n setIgnoreNextOptionSelect(false);\n return;\n }\n if (isChosen) {\n const newChosen = [...chosenOptions];\n newChosen[index].props.isSelected = !chosenOptions[index].props.isSelected;\n setChosenOptions(newChosen);\n } else {\n const newAvailable = [...availableOptions];\n newAvailable[index].props.isSelected = !availableOptions[index].props.isSelected;\n setAvailableOptions(newAvailable);\n }\n };\n\n return (\n <DualListSelector>\n <DualListSelectorPane\n title=\"Available\"\n status={`${availableOptions.filter((x) => x.props.isSelected).length} of ${\n availableOptions.length\n } options selected`}\n >\n <DualListSelectorList>\n {availableOptions.map((option, index) => (\n <DualListSelectorListItem\n key={index}\n isSelected={option.props.isSelected}\n id={`composable-available-option-${option.content}`}\n onOptionSelect={(e) => onOptionSelect(e, index, false)}\n >\n {option.content}\n </DualListSelectorListItem>\n ))}\n </DualListSelectorList>\n </DualListSelectorPane>\n <DualListSelectorControlsWrapper aria-label=\"Selector controls\">\n <DualListSelectorControl\n isDisabled={!availableOptions.some((option) => option.props.isSelected)}\n onClick={() => moveSelected(true)}\n aria-label=\"Add selected\"\n icon={<AngleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={availableOptions.length === 0}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n icon={<AngleDoubleRightIcon />}\n />\n <DualListSelectorControl\n isDisabled={chosenOptions.length === 0}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n icon={<RhMicronsDoubleCaretLeftIcon />}\n />\n <DualListSelectorControl\n onClick={() => moveSelected(false)}\n isDisabled={!chosenOptions.some((option) => option.props.isSelected)}\n aria-label=\"Remove selected\"\n icon={<AngleLeftIcon />}\n />\n </DualListSelectorControlsWrapper>\n <DualListSelectorPane\n title=\"Chosen\"\n status={`${chosenOptions.filter((x) => x.props.isSelected).length} of ${chosenOptions.length} options selected`}\n isChosen\n >\n <DragDropSort\n items={chosenOptions.map((option, index) => ({\n ...option,\n props: {\n key: option.props.key,\n isSelected: option.props.isSelected,\n onOptionSelect: (e) => onOptionSelect(e, index, true)\n }\n }))}\n onDrop={(_, newItems) => {\n setChosenOptions(newItems);\n }}\n variant=\"DualListSelectorList\"\n >\n <DualListSelectorList />\n </DragDropSort>\n </DualListSelectorPane>\n </DualListSelector>\n );\n};\n","title":"Draggable","lang":"ts","className":""}}>
411
-
412
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
413
- {`To enable drag and drop, wrap the `}
414
-
415
- <code {...{"className":"ws-code "}}>
416
- {`<DualListSelectorList>`}
417
- </code>
418
- {` component with `}
419
-
420
- <code {...{"className":"ws-code "}}>
421
- {`<DragDropSort>`}
422
- </code>
423
- {`, define the `}
424
-
425
- <code {...{"className":"ws-code "}}>
426
- {`variant`}
427
- </code>
428
- {` property as "DualListSelectorList", and pass both the sortable items and `}
429
-
430
- <code {...{"className":"ws-code "}}>
431
- {`onDrop`}
432
- </code>
433
- {` callback to `}
434
-
435
- <code {...{"className":"ws-code "}}>
436
- {`<DragDropSort>`}
437
- </code>
438
- {`. `}
439
-
440
- <code {...{"className":"ws-code "}}>
441
- {`<DragDropSort>`}
442
- </code>
443
- {` will create the component's usual children internally based on the items property, so children inside the `}
444
-
445
- <code {...{"className":"ws-code "}}>
446
- {`<DualListSelectorList>`}
447
- </code>
448
- {` should not be passed to the wrapped component.`}
449
- </p>
450
-
451
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
452
- {`Full drag and drop details can be found on the `}
453
-
454
- <PatternflyThemeLink {...{"to":"/components/drag-and-drop","className":""}}>
455
- {`drag and drop`}
456
- </PatternflyThemeLink>
457
- {` component page.`}
458
- </p>
459
- </Example>,
460
- 'Draggable with multiple drop zones': props =>
461
- <Example {...pageData} {...props} {...{"code":"import { useEffect, useState } from 'react';\nimport {\n DualListSelector,\n DualListSelectorPane,\n DualListSelectorList,\n DualListSelectorControlsWrapper,\n DualListSelectorControl\n} from '@patternfly/react-core';\nimport { DragDropContainer, DraggableObject, Droppable as NewDroppable } from '@patternfly/react-drag-drop';\n\nimport RhMicronsDoubleCaretLeftIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-double-caret-left-icon';\nimport AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';\nimport AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';\nimport AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';\n\nexport const DragDropContainerDualListSelector: React.FunctionComponent = () => {\n const [ignoreNextOptionSelect, setIgnoreNextOptionSelect] = useState(false);\n const [availableOptions, setAvailableOptions] = useState<DraggableObject[]>([\n { id: 'Apple', content: 'Apple', props: { key: 'Apple', isSelected: false } },\n { id: 'Banana', content: 'Banana', props: { key: 'Banana', isSelected: false } },\n { id: 'Pineapple', content: 'Pineapple', props: { key: 'Pineapple', isSelected: false } }\n ]);\n\n const [chosenOptions, setChosenOptions] = useState<DraggableObject[]>([\n { id: 'Orange', content: 'Orange', props: { key: 'Orange', isSelected: false } },\n { id: 'Grape', content: 'Grape', props: { key: 'Grape', isSelected: false } },\n { id: 'Peach', content: 'Peach', props: { key: 'Peach', isSelected: false } },\n { id: 'Strawberry', content: 'Strawberry', props: { key: 'Strawberry', isSelected: false } }\n ]);\n\n const [allDraggableItems, setAllItems] = useState<Record<string, DraggableObject[]>>({\n available: availableOptions.map((option, index) => ({\n ...option,\n props: {\n key: option.props.key,\n isSelected: option.props.isSelected,\n onOptionSelect: (e) => onOptionSelect(e, index, false)\n }\n })),\n chosen: chosenOptions.map((option, index) => ({\n ...option,\n props: {\n key: option.props.key,\n isSelected: option.props.isSelected,\n onOptionSelect: (e) => onOptionSelect(e, index, true)\n }\n }))\n });\n\n const handleDragOperation = (_event: any, items: Record<string, DraggableObject[]>) => {\n setAvailableOptions(items.available);\n setChosenOptions(items.chosen);\n setAllItems(items);\n };\n\n useEffect(() => {\n setAllItems({\n available: availableOptions.map((option, index) => ({\n ...option,\n props: {\n key: option.props.key,\n isSelected: option.props.isSelected,\n onOptionSelect: (e) => onOptionSelect(e, index, false)\n }\n })),\n chosen: chosenOptions.map((option, index) => ({\n ...option,\n props: {\n key: option.props.key,\n isSelected: option.props.isSelected,\n onOptionSelect: (e) => onOptionSelect(e, index, true)\n }\n }))\n });\n }, [availableOptions, chosenOptions]);\n\n const moveSelected = (fromAvailable) => {\n const sourceOptions = fromAvailable ? availableOptions : chosenOptions;\n const destinationOptions = fromAvailable ? chosenOptions : availableOptions;\n for (let i = 0; i < sourceOptions.length; i++) {\n const option = sourceOptions[i];\n if (option.props.isSelected) {\n sourceOptions.splice(i, 1);\n destinationOptions.push(option);\n option.props.isSelected = false;\n i--;\n }\n }\n if (fromAvailable) {\n setAvailableOptions([...sourceOptions]);\n setChosenOptions([...destinationOptions]);\n } else {\n setChosenOptions([...sourceOptions]);\n setAvailableOptions([...destinationOptions]);\n }\n };\n\n const moveAll = (fromAvailable) => {\n if (fromAvailable) {\n setChosenOptions([...availableOptions, ...chosenOptions]);\n setAvailableOptions([]);\n } else {\n setAvailableOptions([...chosenOptions, ...availableOptions]);\n setChosenOptions([]);\n }\n };\n\n const onOptionSelect = (event, index, isChosen) => {\n if (ignoreNextOptionSelect) {\n setIgnoreNextOptionSelect(false);\n return;\n }\n if (isChosen) {\n const newChosen = [...chosenOptions];\n newChosen[index].props.isSelected = !chosenOptions[index].props.isSelected;\n setChosenOptions(newChosen);\n } else {\n const newAvailable = [...availableOptions];\n newAvailable[index].props.isSelected = !availableOptions[index].props.isSelected;\n setAvailableOptions(newAvailable);\n }\n };\n\n return (\n <DragDropContainer\n items={allDraggableItems}\n onDrop={handleDragOperation}\n onContainerMove={handleDragOperation}\n onCancel={handleDragOperation}\n variant=\"DualListSelectorList\"\n >\n <DualListSelector>\n <DualListSelectorPane\n title=\"Available\"\n status={`${availableOptions.filter((x) => x.props.isSelected).length} of ${\n availableOptions.length\n } options selected`}\n >\n <NewDroppable\n id=\"available\"\n items={allDraggableItems.available}\n variant=\"DualListSelectorList\"\n wrapper={<DualListSelectorList />}\n />\n </DualListSelectorPane>\n <DualListSelectorControlsWrapper aria-label=\"Selector controls\">\n <DualListSelectorControl\n isDisabled={!availableOptions.some((option) => option.props.isSelected)}\n onClick={() => moveSelected(true)}\n aria-label=\"Add selected\"\n >\n <AngleRightIcon />\n </DualListSelectorControl>\n <DualListSelectorControl\n isDisabled={availableOptions.length === 0}\n onClick={() => moveAll(true)}\n aria-label=\"Add all\"\n >\n <AngleDoubleRightIcon />\n </DualListSelectorControl>\n <DualListSelectorControl\n isDisabled={chosenOptions.length === 0}\n onClick={() => moveAll(false)}\n aria-label=\"Remove all\"\n >\n <RhMicronsDoubleCaretLeftIcon />\n </DualListSelectorControl>\n <DualListSelectorControl\n onClick={() => moveSelected(false)}\n isDisabled={!chosenOptions.some((option) => option.props.isSelected)}\n aria-label=\"Remove selected\"\n >\n <AngleLeftIcon />\n </DualListSelectorControl>\n </DualListSelectorControlsWrapper>\n <DualListSelectorPane\n title=\"Chosen\"\n status={`${chosenOptions.filter((x) => x.props.isSelected).length} of ${chosenOptions.length} options selected`}\n isChosen\n >\n <NewDroppable\n id=\"chosen\"\n items={allDraggableItems.chosen}\n variant=\"DualListSelectorList\"\n wrapper={<DualListSelectorList />}\n />\n </DualListSelectorPane>\n </DualListSelector>\n </DragDropContainer>\n );\n};\n","title":"Draggable with multiple drop zones","lang":"ts","className":""}}>
462
-
463
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
464
- {`To enable multiple drop zones, wrap the `}
465
-
466
- <code {...{"className":"ws-code "}}>
467
- {`<DualListSelector>`}
468
- </code>
469
- {` component with `}
470
-
471
- <code {...{"className":"ws-code "}}>
472
- {`<DragDropContainer>`}
473
- </code>
474
- {`, and create the desired amount of `}
475
-
476
- <code {...{"className":"ws-code "}}>
477
- {`<Droppable>`}
478
- </code>
479
- {` components within `}
480
-
481
- <code {...{"className":"ws-code "}}>
482
- {`<DragDropContainer>`}
483
- </code>
484
- {`. `}
485
-
486
- <code {...{"className":"ws-code "}}>
487
- {`<Droppable>`}
488
- </code>
489
- {` components should be located where `}
490
-
491
- <code {...{"className":"ws-code "}}>
492
- {`<DualListSelectorList>`}
493
- </code>
494
- {` usually would go for each pane to be made draggable.`}
495
- </p>
496
-
497
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
498
- {`Each `}
499
-
500
- <code {...{"className":"ws-code "}}>
501
- {`<Droppable>`}
502
- </code>
503
- {` should define the `}
504
-
505
- <code {...{"className":"ws-code "}}>
506
- {`wrapper`}
507
- </code>
508
- {` property as the component that acts as the drop zone, `}
509
-
510
- <code {...{"className":"ws-code "}}>
511
- {`<DualListSelectorList>`}
512
- </code>
513
- {`, and the `}
514
-
515
- <code {...{"className":"ws-code "}}>
516
- {`items`}
517
- </code>
518
- {` property of their respective draggable items as an array of `}
519
-
520
- <code {...{"className":"ws-code "}}>
521
- {`<DraggableObject>`}
522
- </code>
523
- {` data. `}
524
-
525
- <code {...{"className":"ws-code "}}>
526
- {`<DragDropContainer>`}
527
- </code>
528
- {` should be passed the `}
529
-
530
- <code {...{"className":"ws-code "}}>
531
- {`onDrop`}
532
- </code>
533
- {`, `}
534
-
535
- <code {...{"className":"ws-code "}}>
536
- {`onContainerMove`}
537
- </code>
538
- {`, and `}
539
-
540
- <code {...{"className":"ws-code "}}>
541
- {`onCancel`}
542
- </code>
543
- {` callbacks to handle items being dropped, items moving between droppable containers, and what happens if the drag is cancelled respectively. `}
544
-
545
- <code {...{"className":"ws-code "}}>
546
- {`<DragDropContainer>`}
547
- </code>
548
- {` should also be given a `}
549
-
550
- <code {...{"className":"ws-code "}}>
551
- {`Record`}
552
- </code>
553
- {` representing all sortable drop zones' items. Both components should define the `}
554
-
555
- <code {...{"className":"ws-code "}}>
556
- {`variant`}
557
- </code>
558
- {` property as "DualListSelectorList".`}
559
- </p>
560
-
561
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
562
- {`Full drag and drop details can be found on the `}
563
-
564
- <PatternflyThemeLink {...{"to":"/components/drag-and-drop","className":""}}>
565
- {`drag and drop`}
566
- </PatternflyThemeLink>
567
- {` component page.`}
568
- </p>
569
- </Example>
570
- };
571
-
572
- const Component = () => (
573
- <React.Fragment>
574
- <AutoLinkHeader {...{"id":"examples","headingLevel":"h2","className":"ws-title ws-h2"}}>
575
- {`Examples`}
576
- </AutoLinkHeader>
577
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
578
- {`The dual list selector is built in a composable manner to make customization easier. The standard sub-component relationships are arranged as follows:`}
579
- </p>
580
- <Example {...{"code":"<DualListSelector>\n <DualListSelectorPane>\n <DualListSelectorList>\n <DualListSelectorListItem />\n </DualListSelectorList>\n </DualListSelectorPane>\n\n <DualListSelectorControlsWrapper>\n <DualListSelectorControl /> /* A standard Dual list selector has 4 controls */\n </DualListSelectorControlsWrapper>\n\n <DualListSelectorPane isChosen>\n <DualListSelectorList>\n <DualListSelectorListItem />\n </DualListSelectorList>\n </DualListSelectorPane>\n</DualListSelector>","lang":"noLive","className":""}}>
581
- </Example>
582
- {React.createElement(pageData.examples["Basic"])}
583
- {React.createElement(pageData.examples["Basic with tooltips"])}
584
- {React.createElement(pageData.examples["Basic with search"])}
585
- {React.createElement(pageData.examples["Using more complex options with actions"])}
586
- {React.createElement(pageData.examples["With tree"])}
587
- {React.createElement(pageData.examples["Draggable"])}
588
- {React.createElement(pageData.examples["Draggable with multiple drop zones"])}
589
- </React.Fragment>
590
- );
591
- Component.displayName = 'ComponentsDualListSelectorReactDocs';
592
- Component.pageData = pageData;
593
-
594
- export default Component;