@instructure/ui-drilldown 10.0.1-snapshot-11 → 10.0.1-snapshot-13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/package.json +20 -20
- package/src/Drilldown/README.md +1602 -626
package/src/Drilldown/README.md
CHANGED
|
@@ -166,67 +166,204 @@ type: example
|
|
|
166
166
|
|
|
167
167
|
Additionally, the `renderLabelInfo` prop can render text or other elements next to the label.
|
|
168
168
|
|
|
169
|
-
```js
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
- ```js
|
|
170
|
+
class VideoSettingsExample extends React.Component {
|
|
171
|
+
state = {
|
|
172
|
+
selectedCaption: 'English',
|
|
173
|
+
selectedSpeed: 'Normal',
|
|
174
|
+
selectedQuality: '720p',
|
|
175
|
+
isCommentsOn: true
|
|
176
|
+
}
|
|
173
177
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
selectedSpeed: 'Normal',
|
|
178
|
-
selectedQuality: '720p',
|
|
179
|
-
isCommentsOn: true
|
|
180
|
-
}
|
|
178
|
+
renderTrigger() {
|
|
179
|
+
return <Button renderIcon={IconSettingsSolid}>Video Settings</Button>
|
|
180
|
+
}
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
renderSelected(props, value) {
|
|
183
|
+
return (
|
|
184
|
+
<span
|
|
185
|
+
style={{ color: props.variant === 'default' ? '#6B7780' : undefined }}
|
|
186
|
+
>
|
|
187
|
+
{value}
|
|
188
|
+
</span>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
renderSelectGroup(options, stateName) {
|
|
193
|
+
return (
|
|
194
|
+
<Drilldown.Group
|
|
195
|
+
id={`${stateName}Group`}
|
|
196
|
+
selectableType="single"
|
|
197
|
+
defaultSelected={[this.state[stateName]]}
|
|
198
|
+
onSelect={(_event, { value }) => {
|
|
199
|
+
this.setState({ [stateName]: value[0] })
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
{options.map((option, idx) => (
|
|
203
|
+
<Drilldown.Option
|
|
204
|
+
key={idx}
|
|
205
|
+
id={option}
|
|
206
|
+
value={option}
|
|
207
|
+
onOptionClick={(_event, { goToPreviousPage }) => {
|
|
208
|
+
goToPreviousPage()
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
{option}
|
|
212
|
+
</Drilldown.Option>
|
|
213
|
+
))}
|
|
214
|
+
</Drilldown.Group>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
197
217
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
render() {
|
|
219
|
+
const { captionOptions, speedOptions, qualityOptions } = this.props
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<Drilldown
|
|
223
|
+
rootPageId="videoSettings"
|
|
224
|
+
width="18.5rem"
|
|
225
|
+
maxHeight="26.5rem"
|
|
226
|
+
shouldHideOnSelect={false}
|
|
227
|
+
trigger={this.renderTrigger()}
|
|
228
|
+
onToggle={(_event, { shown, goToPage }) => {
|
|
229
|
+
shown && goToPage('videoSettings')
|
|
230
|
+
}}
|
|
231
|
+
>
|
|
232
|
+
<Drilldown.Page
|
|
233
|
+
id="videoSettings"
|
|
234
|
+
renderTitle="Video Settings"
|
|
235
|
+
withoutHeaderSeparator
|
|
216
236
|
>
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
237
|
+
<Drilldown.Option
|
|
238
|
+
id="captions"
|
|
239
|
+
subPageId="captions"
|
|
240
|
+
renderLabelInfo={(props) =>
|
|
241
|
+
this.renderSelected(props, this.state.selectedCaption)
|
|
242
|
+
}
|
|
243
|
+
>
|
|
244
|
+
Captions
|
|
245
|
+
</Drilldown.Option>
|
|
246
|
+
<Drilldown.Option
|
|
247
|
+
id="speed"
|
|
248
|
+
subPageId="speed"
|
|
249
|
+
renderLabelInfo={(props) =>
|
|
250
|
+
this.renderSelected(props, this.state.selectedSpeed)
|
|
251
|
+
}
|
|
252
|
+
>
|
|
253
|
+
Speed
|
|
254
|
+
</Drilldown.Option>
|
|
255
|
+
<Drilldown.Option
|
|
256
|
+
id="quality"
|
|
257
|
+
subPageId="quality"
|
|
258
|
+
renderLabelInfo={(props) =>
|
|
259
|
+
this.renderSelected(props, this.state.selectedQuality)
|
|
260
|
+
}
|
|
261
|
+
>
|
|
262
|
+
Quality
|
|
263
|
+
</Drilldown.Option>
|
|
264
|
+
<Drilldown.Option
|
|
265
|
+
id="comments"
|
|
266
|
+
themeOverride={{ padding: '0.25rem 0.75rem' }}
|
|
267
|
+
afterLabelContentVAlign="center"
|
|
268
|
+
onOptionClick={() => {
|
|
269
|
+
this.setState((state) => ({
|
|
270
|
+
isCommentsOn: !state.isCommentsOn
|
|
271
|
+
}))
|
|
272
|
+
}}
|
|
273
|
+
role="checkbox"
|
|
274
|
+
aria-checked={this.state.isCommentsOn ? 'true' : 'false'}
|
|
275
|
+
// prevents reading the label of the checkbox too (duplicated)
|
|
276
|
+
aria-describedby={['']}
|
|
277
|
+
renderLabelInfo={
|
|
278
|
+
<Checkbox
|
|
279
|
+
label={<ScreenReaderContent>Comments</ScreenReaderContent>}
|
|
280
|
+
variant="toggle"
|
|
281
|
+
readOnly
|
|
282
|
+
checked={this.state.isCommentsOn}
|
|
283
|
+
onChange={() => {
|
|
284
|
+
// needed for controlled Checkbox,
|
|
285
|
+
// but the state is handled on the Drilldown.Option
|
|
286
|
+
}}
|
|
287
|
+
labelPlacement="start"
|
|
288
|
+
tabIndex={-1}
|
|
289
|
+
/>
|
|
290
|
+
}
|
|
291
|
+
>
|
|
292
|
+
Comments
|
|
293
|
+
</Drilldown.Option>
|
|
294
|
+
</Drilldown.Page>
|
|
295
|
+
|
|
296
|
+
<Drilldown.Page id="captions" renderTitle="Captions">
|
|
297
|
+
{this.renderSelectGroup(captionOptions, 'selectedCaption')}
|
|
298
|
+
</Drilldown.Page>
|
|
299
|
+
|
|
300
|
+
<Drilldown.Page id="speed" renderTitle="Speed">
|
|
301
|
+
{this.renderSelectGroup(speedOptions, 'selectedSpeed')}
|
|
302
|
+
</Drilldown.Page>
|
|
303
|
+
|
|
304
|
+
<Drilldown.Page id="quality" renderTitle="Quality">
|
|
305
|
+
{this.renderSelectGroup(qualityOptions, 'selectedQuality')}
|
|
306
|
+
</Drilldown.Page>
|
|
307
|
+
</Drilldown>
|
|
308
|
+
)
|
|
309
|
+
}
|
|
222
310
|
}
|
|
223
311
|
|
|
224
|
-
render(
|
|
225
|
-
|
|
226
|
-
captionOptions,
|
|
227
|
-
speedOptions,
|
|
228
|
-
qualityOptions
|
|
229
|
-
|
|
312
|
+
render(
|
|
313
|
+
<VideoSettingsExample
|
|
314
|
+
captionOptions={['Off', 'English', 'German', 'Spanish', 'Durtch']}
|
|
315
|
+
speedOptions={['0.5x', 'Normal', '1.25x', '1.5x', '2x']}
|
|
316
|
+
qualityOptions={['Auto', '360p', '480p', '720p', '1080p']}
|
|
317
|
+
/>
|
|
318
|
+
)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
- ```js
|
|
322
|
+
const VideoSettingsExample = (props) => {
|
|
323
|
+
const [selectedCaption, setSelectedCaption] = useState('English')
|
|
324
|
+
const [selectedSpeed, setSelectedSpeed] = useState('Normal')
|
|
325
|
+
const [selectedQuality, setSelectedQuality] = useState('720p')
|
|
326
|
+
const [isCommentsOn, setIsCommentsOn] = useState(true)
|
|
327
|
+
|
|
328
|
+
const renderTrigger = () => {
|
|
329
|
+
return <Button renderIcon={IconSettingsSolid}>Video Settings</Button>
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const renderSelected = (props, value) => {
|
|
333
|
+
return (
|
|
334
|
+
<span
|
|
335
|
+
style={{ color: props.variant === 'default' ? '#6B7780' : undefined }}
|
|
336
|
+
>
|
|
337
|
+
{value}
|
|
338
|
+
</span>
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const renderSelectGroup = (options, selectedState, setSelectedState) => {
|
|
343
|
+
return (
|
|
344
|
+
<Drilldown.Group
|
|
345
|
+
id={`${selectedState}Group`}
|
|
346
|
+
selectableType="single"
|
|
347
|
+
defaultSelected={[selectedState]}
|
|
348
|
+
onSelect={(_event, { value }) => {
|
|
349
|
+
setSelectedState(value[0])
|
|
350
|
+
}}
|
|
351
|
+
>
|
|
352
|
+
{options.map((option, idx) => (
|
|
353
|
+
<Drilldown.Option
|
|
354
|
+
key={idx}
|
|
355
|
+
id={option}
|
|
356
|
+
value={option}
|
|
357
|
+
onOptionClick={(_event, { goToPreviousPage }) => {
|
|
358
|
+
goToPreviousPage()
|
|
359
|
+
}}
|
|
360
|
+
>
|
|
361
|
+
{option}
|
|
362
|
+
</Drilldown.Option>
|
|
363
|
+
))}
|
|
364
|
+
</Drilldown.Group>
|
|
365
|
+
)
|
|
366
|
+
}
|
|
230
367
|
|
|
231
368
|
return (
|
|
232
369
|
<Drilldown
|
|
@@ -234,7 +371,7 @@ class VideoSettingsExample extends React.Component {
|
|
|
234
371
|
width="18.5rem"
|
|
235
372
|
maxHeight="26.5rem"
|
|
236
373
|
shouldHideOnSelect={false}
|
|
237
|
-
trigger={
|
|
374
|
+
trigger={renderTrigger()}
|
|
238
375
|
onToggle={(_event, { shown, goToPage }) => {
|
|
239
376
|
shown && goToPage('videoSettings')
|
|
240
377
|
}}
|
|
@@ -247,27 +384,21 @@ class VideoSettingsExample extends React.Component {
|
|
|
247
384
|
<Drilldown.Option
|
|
248
385
|
id="captions"
|
|
249
386
|
subPageId="captions"
|
|
250
|
-
renderLabelInfo={
|
|
251
|
-
(props) => this.renderSelected(props, this.state.selectedCaption)
|
|
252
|
-
}
|
|
387
|
+
renderLabelInfo={(props) => renderSelected(props, selectedCaption)}
|
|
253
388
|
>
|
|
254
389
|
Captions
|
|
255
390
|
</Drilldown.Option>
|
|
256
391
|
<Drilldown.Option
|
|
257
392
|
id="speed"
|
|
258
393
|
subPageId="speed"
|
|
259
|
-
renderLabelInfo={
|
|
260
|
-
(props) => this.renderSelected(props, this.state.selectedSpeed)
|
|
261
|
-
}
|
|
394
|
+
renderLabelInfo={(props) => renderSelected(props, selectedSpeed)}
|
|
262
395
|
>
|
|
263
396
|
Speed
|
|
264
397
|
</Drilldown.Option>
|
|
265
398
|
<Drilldown.Option
|
|
266
399
|
id="quality"
|
|
267
400
|
subPageId="quality"
|
|
268
|
-
renderLabelInfo={
|
|
269
|
-
(props) => this.renderSelected(props, this.state.selectedQuality)
|
|
270
|
-
}
|
|
401
|
+
renderLabelInfo={(props) => renderSelected(props, selectedQuality)}
|
|
271
402
|
>
|
|
272
403
|
Quality
|
|
273
404
|
</Drilldown.Option>
|
|
@@ -276,19 +407,18 @@ class VideoSettingsExample extends React.Component {
|
|
|
276
407
|
themeOverride={{ padding: '0.25rem 0.75rem' }}
|
|
277
408
|
afterLabelContentVAlign="center"
|
|
278
409
|
onOptionClick={() => {
|
|
279
|
-
|
|
410
|
+
setIsCommentsOn((state) => !state)
|
|
280
411
|
}}
|
|
281
412
|
role="checkbox"
|
|
282
|
-
aria-checked={
|
|
413
|
+
aria-checked={isCommentsOn ? 'true' : 'false'}
|
|
283
414
|
// prevents reading the label of the checkbox too (duplicated)
|
|
284
415
|
aria-describedby={['']}
|
|
285
|
-
|
|
286
416
|
renderLabelInfo={
|
|
287
417
|
<Checkbox
|
|
288
418
|
label={<ScreenReaderContent>Comments</ScreenReaderContent>}
|
|
289
|
-
variant=
|
|
419
|
+
variant="toggle"
|
|
290
420
|
readOnly
|
|
291
|
-
checked={
|
|
421
|
+
checked={isCommentsOn}
|
|
292
422
|
onChange={() => {
|
|
293
423
|
// needed for controlled Checkbox,
|
|
294
424
|
// but the state is handled on the Drilldown.Option
|
|
@@ -302,221 +432,388 @@ class VideoSettingsExample extends React.Component {
|
|
|
302
432
|
</Drilldown.Option>
|
|
303
433
|
</Drilldown.Page>
|
|
304
434
|
|
|
305
|
-
<Drilldown.Page
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
435
|
+
<Drilldown.Page id="captions" renderTitle="Captions">
|
|
436
|
+
{renderSelectGroup(
|
|
437
|
+
props.captionOptions,
|
|
438
|
+
selectedCaption,
|
|
439
|
+
setSelectedCaption
|
|
440
|
+
)}
|
|
310
441
|
</Drilldown.Page>
|
|
311
442
|
|
|
312
|
-
<Drilldown.Page
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
443
|
+
<Drilldown.Page id="speed" renderTitle="Speed">
|
|
444
|
+
{renderSelectGroup(
|
|
445
|
+
props.speedOptions,
|
|
446
|
+
selectedSpeed,
|
|
447
|
+
setSelectedSpeed
|
|
448
|
+
)}
|
|
317
449
|
</Drilldown.Page>
|
|
318
450
|
|
|
319
|
-
<Drilldown.Page
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
451
|
+
<Drilldown.Page id="quality" renderTitle="Quality">
|
|
452
|
+
{renderSelectGroup(
|
|
453
|
+
props.qualityOptions,
|
|
454
|
+
selectedQuality,
|
|
455
|
+
setSelectedQuality
|
|
456
|
+
)}
|
|
324
457
|
</Drilldown.Page>
|
|
325
458
|
</Drilldown>
|
|
326
459
|
)
|
|
327
460
|
}
|
|
328
|
-
}
|
|
329
461
|
|
|
330
|
-
render(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
462
|
+
render(
|
|
463
|
+
<VideoSettingsExample
|
|
464
|
+
captionOptions={['Off', 'English', 'German', 'Spanish', 'Durtch']}
|
|
465
|
+
speedOptions={['0.5x', 'Normal', '1.25x', '1.5x', '2x']}
|
|
466
|
+
qualityOptions={['Auto', '360p', '480p', '720p', '1080p']}
|
|
467
|
+
/>
|
|
468
|
+
)
|
|
469
|
+
```
|
|
336
470
|
|
|
337
471
|
### Displaying Drilldown in a Popover
|
|
338
472
|
|
|
339
473
|
Just like [Menu](#Menu), Drilldown accepts a `trigger` property: it will render a toggle button which, when clicked, shows or hides the Drilldown in a [Popover](#Popover). Drilldown passes many of its props to Popover in this case (`mountNode`, `shouldContainFocus`, `withArrow`, etc.).
|
|
340
474
|
|
|
341
|
-
```js
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
475
|
+
- ```js
|
|
476
|
+
class SelectContactsExample extends React.Component {
|
|
477
|
+
state = {
|
|
478
|
+
selected: []
|
|
479
|
+
}
|
|
345
480
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
481
|
+
getCategoryById(id) {
|
|
482
|
+
return this.props.contactsData.find((cat) => cat.id === id)
|
|
483
|
+
}
|
|
350
484
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
485
|
+
selectContacts(values) {
|
|
486
|
+
this.setState({ selected: values })
|
|
487
|
+
}
|
|
354
488
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
489
|
+
renderContacts(contacts) {
|
|
490
|
+
return contacts.map((contact, idx) => {
|
|
491
|
+
const { id, name, email } = contact
|
|
358
492
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
493
|
+
return (
|
|
494
|
+
<Drilldown.Option
|
|
495
|
+
key={idx}
|
|
496
|
+
id={id}
|
|
497
|
+
value={name}
|
|
498
|
+
description={
|
|
499
|
+
<TruncateText>
|
|
500
|
+
#{idx + 1} | {email}
|
|
501
|
+
</TruncateText>
|
|
502
|
+
}
|
|
503
|
+
onOptionClick={() => {
|
|
504
|
+
this.selectContacts([contact])
|
|
505
|
+
}}
|
|
506
|
+
>
|
|
507
|
+
{name}
|
|
508
|
+
</Drilldown.Option>
|
|
509
|
+
)
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
renderSubCategoryOptions(subCategories = []) {
|
|
514
|
+
return subCategories.map((subCatId, idx) => {
|
|
515
|
+
const category = this.getCategoryById(subCatId)
|
|
516
|
+
const { id, title } = category
|
|
517
|
+
|
|
518
|
+
return (
|
|
519
|
+
<Drilldown.Option key={idx} id={id} subPageId={id}>
|
|
520
|
+
{title}
|
|
521
|
+
</Drilldown.Option>
|
|
522
|
+
)
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
renderPage(category, key) {
|
|
527
|
+
const { id, title, subCategories = [], options = [] } = category
|
|
528
|
+
|
|
529
|
+
const allContacts = this.getAllContactsFromCategory(category)
|
|
362
530
|
|
|
363
531
|
return (
|
|
364
|
-
<Drilldown.
|
|
365
|
-
key={
|
|
532
|
+
<Drilldown.Page
|
|
533
|
+
key={key}
|
|
366
534
|
id={id}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
</TruncateText>
|
|
372
|
-
}
|
|
373
|
-
onOptionClick={() => {
|
|
374
|
-
this.selectContacts([contact])
|
|
535
|
+
renderTitle={title}
|
|
536
|
+
renderActionLabel={`Send to All (${allContacts.length})`}
|
|
537
|
+
onHeaderActionClicked={() => {
|
|
538
|
+
this.selectContacts(allContacts)
|
|
375
539
|
}}
|
|
376
540
|
>
|
|
377
|
-
{
|
|
378
|
-
|
|
541
|
+
{[
|
|
542
|
+
...this.renderSubCategoryOptions(subCategories),
|
|
543
|
+
...this.renderContacts(options)
|
|
544
|
+
]}
|
|
545
|
+
</Drilldown.Page>
|
|
379
546
|
)
|
|
380
|
-
}
|
|
381
|
-
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
getAllContactsFromCategory(category) {
|
|
550
|
+
let allContacts = []
|
|
382
551
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
552
|
+
const addOptions = (cat) => {
|
|
553
|
+
const { subCategories = [], options = [] } = cat
|
|
554
|
+
allContacts.push(...options)
|
|
555
|
+
|
|
556
|
+
subCategories.forEach((subCatId) => {
|
|
557
|
+
addOptions(this.getCategoryById(subCatId))
|
|
558
|
+
})
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
addOptions(category)
|
|
562
|
+
return allContacts
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
render() {
|
|
566
|
+
const { contactsData } = this.props
|
|
567
|
+
const { selected = [] } = this.state
|
|
387
568
|
|
|
388
569
|
return (
|
|
389
|
-
<
|
|
390
|
-
|
|
391
|
-
|
|
570
|
+
<Flex alignItems="start">
|
|
571
|
+
<Flex.Item width="20rem" textAlign="center" padding="x-small 0 0">
|
|
572
|
+
<Drilldown
|
|
573
|
+
rootPageId="contacts"
|
|
574
|
+
width="18.5rem"
|
|
575
|
+
maxHeight="26.5rem"
|
|
576
|
+
trigger={<Button>Select Contacts</Button>}
|
|
577
|
+
onToggle={(_event, args) => {
|
|
578
|
+
args.shown && selectContacts([])
|
|
579
|
+
}}
|
|
580
|
+
>
|
|
581
|
+
{contactsData.map((page, idx) => this.renderPage(page, idx))}
|
|
582
|
+
</Drilldown>
|
|
583
|
+
</Flex.Item>
|
|
584
|
+
|
|
585
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
586
|
+
<View as="div" padding="small" background="primary">
|
|
587
|
+
<div>
|
|
588
|
+
<Text weight="bold">
|
|
589
|
+
Selected ({selected ? selected.length : 0}):
|
|
590
|
+
</Text>
|
|
591
|
+
</div>
|
|
592
|
+
<div>{selected.map((a) => a.name).join(', ')}</div>
|
|
593
|
+
</View>
|
|
594
|
+
</Flex.Item>
|
|
595
|
+
</Flex>
|
|
392
596
|
)
|
|
393
|
-
}
|
|
597
|
+
}
|
|
394
598
|
}
|
|
395
599
|
|
|
396
|
-
|
|
397
|
-
|
|
600
|
+
const generateCategory = (name, count) => {
|
|
601
|
+
return Array(count)
|
|
602
|
+
.fill(name)
|
|
603
|
+
.map((category, idx) => {
|
|
604
|
+
const id = category.replace(/(-|\s)/g, '').toLowerCase()
|
|
605
|
+
return {
|
|
606
|
+
id: `${id}${idx + 1}`,
|
|
607
|
+
category,
|
|
608
|
+
name: `${category} ${idx + 1}`,
|
|
609
|
+
email: `${id}${idx + 1}@randommail.com`
|
|
610
|
+
}
|
|
611
|
+
})
|
|
612
|
+
}
|
|
398
613
|
|
|
399
|
-
|
|
614
|
+
const contactsData = [
|
|
615
|
+
{
|
|
616
|
+
id: 'contacts',
|
|
617
|
+
title: 'Contacts',
|
|
618
|
+
subCategories: ['administrators', 'teachers', 'students']
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
id: 'administrators',
|
|
622
|
+
title: 'Administrators',
|
|
623
|
+
subCategories: ['accountAdmins', 'subAccountAdmins']
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
id: 'accountAdmins',
|
|
627
|
+
title: 'Account Admins',
|
|
628
|
+
options: generateCategory('Account Admin', 8)
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
id: 'subAccountAdmins',
|
|
632
|
+
title: 'Sub-Account Admins',
|
|
633
|
+
options: generateCategory('Sub-Account Admin', 12)
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
id: 'teachers',
|
|
637
|
+
title: 'Teachers',
|
|
638
|
+
options: generateCategory('Teacher', 23)
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
id: 'students',
|
|
642
|
+
title: 'Students',
|
|
643
|
+
options: generateCategory('Student', 34)
|
|
644
|
+
}
|
|
645
|
+
]
|
|
400
646
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
onHeaderActionClicked={() => {
|
|
408
|
-
this.selectContacts(allContacts)
|
|
409
|
-
}}
|
|
410
|
-
>
|
|
411
|
-
{[
|
|
412
|
-
...this.renderSubCategoryOptions(subCategories),
|
|
413
|
-
...this.renderContacts(options)
|
|
414
|
-
]}
|
|
415
|
-
</Drilldown.Page>
|
|
416
|
-
)
|
|
417
|
-
}
|
|
647
|
+
render(<SelectContactsExample contactsData={contactsData} />)
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
- ```js
|
|
651
|
+
const SelectContactsExample = (props) => {
|
|
652
|
+
const [selected, setSelected] = useState([])
|
|
418
653
|
|
|
419
|
-
|
|
420
|
-
|
|
654
|
+
const getCategoryById = (id) => {
|
|
655
|
+
return props.contactsData.find((cat) => cat.id === id)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const selectContacts = (values) => {
|
|
659
|
+
setSelected(values)
|
|
660
|
+
}
|
|
421
661
|
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
662
|
+
const renderContacts = (contacts) => {
|
|
663
|
+
return contacts.map((contact, idx) => {
|
|
664
|
+
const { id, name, email } = contact
|
|
665
|
+
|
|
666
|
+
return (
|
|
667
|
+
<Drilldown.Option
|
|
668
|
+
key={idx}
|
|
669
|
+
id={id}
|
|
670
|
+
value={name}
|
|
671
|
+
description={
|
|
672
|
+
<TruncateText>
|
|
673
|
+
#{idx + 1} | {email}
|
|
674
|
+
</TruncateText>
|
|
675
|
+
}
|
|
676
|
+
onOptionClick={() => {
|
|
677
|
+
selectContacts([contact])
|
|
678
|
+
}}
|
|
679
|
+
>
|
|
680
|
+
{name}
|
|
681
|
+
</Drilldown.Option>
|
|
682
|
+
)
|
|
683
|
+
})
|
|
684
|
+
}
|
|
425
685
|
|
|
686
|
+
const renderSubCategoryOptions = (subCategories = []) => {
|
|
687
|
+
return subCategories.map((subCatId, idx) => {
|
|
688
|
+
const category = getCategoryById(subCatId)
|
|
689
|
+
const { id, title } = category
|
|
426
690
|
|
|
427
|
-
|
|
428
|
-
|
|
691
|
+
return (
|
|
692
|
+
<Drilldown.Option key={idx} id={id} subPageId={id}>
|
|
693
|
+
{title}
|
|
694
|
+
</Drilldown.Option>
|
|
695
|
+
)
|
|
429
696
|
})
|
|
430
697
|
}
|
|
431
698
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
699
|
+
const renderPage = (category, key) => {
|
|
700
|
+
const { id, title, subCategories = [], options = [] } = category
|
|
701
|
+
|
|
702
|
+
const allContacts = getAllContactsFromCategory(category)
|
|
703
|
+
|
|
704
|
+
return (
|
|
705
|
+
<Drilldown.Page
|
|
706
|
+
key={key}
|
|
707
|
+
id={id}
|
|
708
|
+
renderTitle={title}
|
|
709
|
+
renderActionLabel={`Send to All (${allContacts.length})`}
|
|
710
|
+
onHeaderActionClicked={() => {
|
|
711
|
+
selectContacts(allContacts)
|
|
712
|
+
}}
|
|
713
|
+
>
|
|
714
|
+
{[
|
|
715
|
+
...renderSubCategoryOptions(subCategories),
|
|
716
|
+
...renderContacts(options)
|
|
717
|
+
]}
|
|
718
|
+
</Drilldown.Page>
|
|
719
|
+
)
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const getAllContactsFromCategory = (category) => {
|
|
723
|
+
let allContacts = []
|
|
724
|
+
|
|
725
|
+
const addOptions = (cat) => {
|
|
726
|
+
const { subCategories = [], options = [] } = cat
|
|
727
|
+
allContacts.push(...options)
|
|
728
|
+
|
|
729
|
+
subCategories.forEach((subCatId) => {
|
|
730
|
+
addOptions(getCategoryById(subCatId))
|
|
731
|
+
})
|
|
732
|
+
}
|
|
435
733
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
734
|
+
addOptions(category)
|
|
735
|
+
return allContacts
|
|
736
|
+
}
|
|
439
737
|
|
|
440
738
|
return (
|
|
441
|
-
<Flex alignItems=
|
|
442
|
-
<Flex.Item width="20rem" textAlign="center" padding=
|
|
739
|
+
<Flex alignItems="start">
|
|
740
|
+
<Flex.Item width="20rem" textAlign="center" padding="x-small 0 0">
|
|
443
741
|
<Drilldown
|
|
444
742
|
rootPageId="contacts"
|
|
445
743
|
width="18.5rem"
|
|
446
744
|
maxHeight="26.5rem"
|
|
447
745
|
trigger={<Button>Select Contacts</Button>}
|
|
448
746
|
onToggle={(_event, args) => {
|
|
449
|
-
args.shown &&
|
|
747
|
+
args.shown && selectContacts([])
|
|
450
748
|
}}
|
|
451
749
|
>
|
|
452
|
-
{contactsData.map((page, idx) =>
|
|
750
|
+
{props.contactsData.map((page, idx) => renderPage(page, idx))}
|
|
453
751
|
</Drilldown>
|
|
454
752
|
</Flex.Item>
|
|
455
753
|
|
|
456
|
-
<Flex.Item padding=
|
|
457
|
-
<View as="div" padding=
|
|
458
|
-
<div>
|
|
459
|
-
<Text weight='bold'>Selected ({selected ? selected.length : 0}):</Text>
|
|
460
|
-
</div>
|
|
754
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
755
|
+
<View as="div" padding="small" background="primary">
|
|
461
756
|
<div>
|
|
462
|
-
|
|
757
|
+
<Text weight="bold">
|
|
758
|
+
Selected ({selected ? selected.length : 0}):
|
|
759
|
+
</Text>
|
|
463
760
|
</div>
|
|
761
|
+
<div>{selected.map((a) => a.name).join(', ')}</div>
|
|
464
762
|
</View>
|
|
465
763
|
</Flex.Item>
|
|
466
764
|
</Flex>
|
|
467
765
|
)
|
|
468
766
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
})
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const contactsData = [
|
|
486
|
-
{
|
|
487
|
-
id: 'contacts',
|
|
488
|
-
title: 'Contacts',
|
|
489
|
-
subCategories: ['administrators', 'teachers', 'students']
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
id: 'administrators',
|
|
493
|
-
title: 'Administrators',
|
|
494
|
-
subCategories: ['accountAdmins', 'subAccountAdmins']
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
id: 'accountAdmins',
|
|
498
|
-
title: 'Account Admins',
|
|
499
|
-
options: generateCategory('Account Admin', 8)
|
|
500
|
-
},
|
|
501
|
-
{
|
|
502
|
-
id: 'subAccountAdmins',
|
|
503
|
-
title: 'Sub-Account Admins',
|
|
504
|
-
options: generateCategory('Sub-Account Admin', 12)
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
id: 'teachers',
|
|
508
|
-
title: 'Teachers',
|
|
509
|
-
options: generateCategory('Teacher', 23)
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
id: 'students',
|
|
513
|
-
title: 'Students',
|
|
514
|
-
options: generateCategory('Student', 34)
|
|
767
|
+
|
|
768
|
+
const generateCategory = (name, count) => {
|
|
769
|
+
return Array(count)
|
|
770
|
+
.fill(name)
|
|
771
|
+
.map((category, idx) => {
|
|
772
|
+
const id = category.replace(/(-|\s)/g, '').toLowerCase()
|
|
773
|
+
return {
|
|
774
|
+
id: `${id}${idx + 1}`,
|
|
775
|
+
category,
|
|
776
|
+
name: `${category} ${idx + 1}`,
|
|
777
|
+
email: `${id}${idx + 1}@randommail.com`
|
|
778
|
+
}
|
|
779
|
+
})
|
|
515
780
|
}
|
|
516
|
-
]
|
|
517
781
|
|
|
518
|
-
|
|
519
|
-
|
|
782
|
+
const contactsData = [
|
|
783
|
+
{
|
|
784
|
+
id: 'contacts',
|
|
785
|
+
title: 'Contacts',
|
|
786
|
+
subCategories: ['administrators', 'teachers', 'students']
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
id: 'administrators',
|
|
790
|
+
title: 'Administrators',
|
|
791
|
+
subCategories: ['accountAdmins', 'subAccountAdmins']
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
id: 'accountAdmins',
|
|
795
|
+
title: 'Account Admins',
|
|
796
|
+
options: generateCategory('Account Admin', 8)
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
id: 'subAccountAdmins',
|
|
800
|
+
title: 'Sub-Account Admins',
|
|
801
|
+
options: generateCategory('Sub-Account Admin', 12)
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
id: 'teachers',
|
|
805
|
+
title: 'Teachers',
|
|
806
|
+
options: generateCategory('Teacher', 23)
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: 'students',
|
|
810
|
+
title: 'Students',
|
|
811
|
+
options: generateCategory('Student', 34)
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
|
|
815
|
+
render(<SelectContactsExample contactsData={contactsData} />)
|
|
816
|
+
```
|
|
520
817
|
|
|
521
818
|
### Page header
|
|
522
819
|
|
|
@@ -538,36 +835,137 @@ The label can be changed with `renderBackButtonLabel` prop (defaults to "Back").
|
|
|
538
835
|
|
|
539
836
|
This option will always display on the page when needed and cannot be disabled.
|
|
540
837
|
|
|
541
|
-
```js
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
838
|
+
- ```js
|
|
839
|
+
class BackNavigationExample extends React.Component {
|
|
840
|
+
state = {
|
|
841
|
+
showTitle: true,
|
|
842
|
+
showAction: true,
|
|
843
|
+
showAlternativeBackLabel: false
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
render() {
|
|
847
|
+
const { showTitle, showAction, showAlternativeBackLabel } = this.state
|
|
848
|
+
|
|
849
|
+
return (
|
|
850
|
+
<Flex alignItems="start">
|
|
851
|
+
<Flex.Item>
|
|
852
|
+
<Drilldown rootPageId="root" width="20rem" maxHeight="30rem">
|
|
853
|
+
<Drilldown.Page
|
|
854
|
+
id="root"
|
|
855
|
+
renderTitle={showTitle && 'Root page'}
|
|
856
|
+
renderActionLabel={showAction && 'Action!'}
|
|
857
|
+
>
|
|
858
|
+
<Drilldown.Option
|
|
859
|
+
id="BackNavigationRoot1"
|
|
860
|
+
subPageId="secondPage"
|
|
861
|
+
>
|
|
862
|
+
Option
|
|
863
|
+
</Drilldown.Option>
|
|
864
|
+
<Drilldown.Option
|
|
865
|
+
id="BackNavigationRoot2"
|
|
866
|
+
subPageId="secondPage"
|
|
867
|
+
>
|
|
868
|
+
Option
|
|
869
|
+
</Drilldown.Option>
|
|
870
|
+
<Drilldown.Option
|
|
871
|
+
id="BackNavigationRoot3"
|
|
872
|
+
subPageId="secondPage"
|
|
873
|
+
>
|
|
874
|
+
Option
|
|
875
|
+
</Drilldown.Option>
|
|
876
|
+
</Drilldown.Page>
|
|
877
|
+
|
|
878
|
+
<Drilldown.Page
|
|
879
|
+
id="secondPage"
|
|
880
|
+
renderTitle={showTitle && 'Second page'}
|
|
881
|
+
renderActionLabel={showAction && 'Action!'}
|
|
882
|
+
renderBackButtonLabel={
|
|
883
|
+
showAlternativeBackLabel
|
|
884
|
+
? (prevPageTitle) =>
|
|
885
|
+
prevPageTitle ? `Back to ${prevPageTitle}` : 'Back'
|
|
886
|
+
: undefined
|
|
887
|
+
}
|
|
888
|
+
>
|
|
889
|
+
<Drilldown.Option id="BackNavigationSecondPage1">
|
|
890
|
+
Option
|
|
891
|
+
</Drilldown.Option>
|
|
892
|
+
<Drilldown.Option id="BackNavigationSecondPage2">
|
|
893
|
+
Option
|
|
894
|
+
</Drilldown.Option>
|
|
895
|
+
<Drilldown.Option id="BackNavigationSecondPage3">
|
|
896
|
+
Option
|
|
897
|
+
</Drilldown.Option>
|
|
898
|
+
</Drilldown.Page>
|
|
899
|
+
</Drilldown>
|
|
900
|
+
</Flex.Item>
|
|
901
|
+
|
|
902
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
903
|
+
<FormFieldGroup description="Drilldown Group example settings">
|
|
904
|
+
<Checkbox
|
|
905
|
+
checked={
|
|
906
|
+
!this.state.showTitle
|
|
907
|
+
? false
|
|
908
|
+
: this.state.showAlternativeBackLabel
|
|
909
|
+
}
|
|
910
|
+
disabled={!this.state.showTitle}
|
|
911
|
+
label='Display previous page title in back navigation ("Show page title" needs to be turned on)'
|
|
912
|
+
variant="toggle"
|
|
913
|
+
onChange={() => {
|
|
914
|
+
this.setState({
|
|
915
|
+
showAlternativeBackLabel:
|
|
916
|
+
!this.state.showAlternativeBackLabel
|
|
917
|
+
})
|
|
918
|
+
}}
|
|
919
|
+
/>
|
|
920
|
+
<Checkbox
|
|
921
|
+
checked={this.state.showTitle}
|
|
922
|
+
label="Show page title"
|
|
923
|
+
variant="toggle"
|
|
924
|
+
onChange={() => {
|
|
925
|
+
this.setState({ showTitle: !this.state.showTitle })
|
|
926
|
+
}}
|
|
927
|
+
/>
|
|
928
|
+
<Checkbox
|
|
929
|
+
checked={this.state.showAction}
|
|
930
|
+
label="Show action label"
|
|
931
|
+
variant="toggle"
|
|
932
|
+
onChange={() => {
|
|
933
|
+
this.setState({ showAction: !this.state.showAction })
|
|
934
|
+
}}
|
|
935
|
+
/>
|
|
936
|
+
</FormFieldGroup>
|
|
937
|
+
</Flex.Item>
|
|
938
|
+
</Flex>
|
|
939
|
+
)
|
|
940
|
+
}
|
|
550
941
|
}
|
|
551
942
|
|
|
552
|
-
render()
|
|
553
|
-
|
|
943
|
+
render(<BackNavigationExample />)
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
- ```js
|
|
947
|
+
const BackNavigationExample = () => {
|
|
948
|
+
const [showTitle, setShowTitle] = useState(true)
|
|
949
|
+
const [showAction, setShowAction] = useState(true)
|
|
950
|
+
const [showAlternativeBackLabel, setShowAlternativeBackLabel] =
|
|
951
|
+
useState(false)
|
|
554
952
|
|
|
555
953
|
return (
|
|
556
|
-
<Flex alignItems=
|
|
954
|
+
<Flex alignItems="start">
|
|
557
955
|
<Flex.Item>
|
|
558
|
-
<Drilldown rootPageId=
|
|
956
|
+
<Drilldown rootPageId="root" width="20rem" maxHeight="30rem">
|
|
559
957
|
<Drilldown.Page
|
|
560
958
|
id="root"
|
|
561
959
|
renderTitle={showTitle && 'Root page'}
|
|
562
960
|
renderActionLabel={showAction && 'Action!'}
|
|
563
961
|
>
|
|
564
|
-
<Drilldown.Option id="BackNavigationRoot1" subPageId=
|
|
962
|
+
<Drilldown.Option id="BackNavigationRoot1" subPageId="secondPage">
|
|
565
963
|
Option
|
|
566
964
|
</Drilldown.Option>
|
|
567
|
-
<Drilldown.Option id="BackNavigationRoot2" subPageId=
|
|
965
|
+
<Drilldown.Option id="BackNavigationRoot2" subPageId="secondPage">
|
|
568
966
|
Option
|
|
569
967
|
</Drilldown.Option>
|
|
570
|
-
<Drilldown.Option id="BackNavigationRoot3" subPageId=
|
|
968
|
+
<Drilldown.Option id="BackNavigationRoot3" subPageId="secondPage">
|
|
571
969
|
Option
|
|
572
970
|
</Drilldown.Option>
|
|
573
971
|
</Drilldown.Page>
|
|
@@ -576,9 +974,12 @@ class BackNavigationExample extends React.Component {
|
|
|
576
974
|
id="secondPage"
|
|
577
975
|
renderTitle={showTitle && 'Second page'}
|
|
578
976
|
renderActionLabel={showAction && 'Action!'}
|
|
579
|
-
renderBackButtonLabel={
|
|
580
|
-
|
|
581
|
-
|
|
977
|
+
renderBackButtonLabel={
|
|
978
|
+
showAlternativeBackLabel
|
|
979
|
+
? (prevPageTitle) =>
|
|
980
|
+
prevPageTitle ? `Back to ${prevPageTitle}` : 'Back'
|
|
981
|
+
: undefined
|
|
982
|
+
}
|
|
582
983
|
>
|
|
583
984
|
<Drilldown.Option id="BackNavigationSecondPage1">
|
|
584
985
|
Option
|
|
@@ -596,70 +997,128 @@ class BackNavigationExample extends React.Component {
|
|
|
596
997
|
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
597
998
|
<FormFieldGroup description="Drilldown Group example settings">
|
|
598
999
|
<Checkbox
|
|
599
|
-
checked={!
|
|
600
|
-
disabled={!
|
|
1000
|
+
checked={!showTitle ? false : showAlternativeBackLabel}
|
|
1001
|
+
disabled={!showTitle}
|
|
601
1002
|
label='Display previous page title in back navigation ("Show page title" needs to be turned on)'
|
|
602
1003
|
variant="toggle"
|
|
603
|
-
onChange={
|
|
604
|
-
()
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
}
|
|
1004
|
+
onChange={() => {
|
|
1005
|
+
setShowAlternativeBackLabel(!showAlternativeBackLabel)
|
|
1006
|
+
}}
|
|
608
1007
|
/>
|
|
609
1008
|
<Checkbox
|
|
610
|
-
checked={
|
|
1009
|
+
checked={showTitle}
|
|
611
1010
|
label="Show page title"
|
|
612
1011
|
variant="toggle"
|
|
613
|
-
onChange={
|
|
614
|
-
()
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
}
|
|
1012
|
+
onChange={() => {
|
|
1013
|
+
setShowTitle(!showTitle)
|
|
1014
|
+
}}
|
|
618
1015
|
/>
|
|
619
1016
|
<Checkbox
|
|
620
|
-
checked={
|
|
1017
|
+
checked={showAction}
|
|
621
1018
|
label="Show action label"
|
|
622
1019
|
variant="toggle"
|
|
623
|
-
onChange={
|
|
624
|
-
()
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
}
|
|
1020
|
+
onChange={() => {
|
|
1021
|
+
setShowAction(!showAction)
|
|
1022
|
+
}}
|
|
628
1023
|
/>
|
|
629
1024
|
</FormFieldGroup>
|
|
630
1025
|
</Flex.Item>
|
|
631
1026
|
</Flex>
|
|
632
1027
|
)
|
|
633
1028
|
}
|
|
634
|
-
}
|
|
635
1029
|
|
|
636
|
-
render(<BackNavigationExample />)
|
|
637
|
-
```
|
|
1030
|
+
render(<BackNavigationExample />)
|
|
1031
|
+
```
|
|
638
1032
|
|
|
639
1033
|
### Option Groups
|
|
640
1034
|
|
|
641
1035
|
Wrapping the Options in `<Drilldown.Group>` will separate these options with separators. These separators can be hidden, and you can provide a label with the `renderGroupTitle` prop.
|
|
642
1036
|
|
|
643
|
-
```js
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1037
|
+
- ```js
|
|
1038
|
+
class GroupsExample extends React.Component {
|
|
1039
|
+
state = {
|
|
1040
|
+
showSeparators: true,
|
|
1041
|
+
showTitles: true
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
render() {
|
|
1045
|
+
return (
|
|
1046
|
+
<Flex alignItems="start">
|
|
1047
|
+
<Flex.Item>
|
|
1048
|
+
<Drilldown rootPageId="root" width="20rem" maxHeight="30rem">
|
|
1049
|
+
<Drilldown.Page id="root">
|
|
1050
|
+
<Drilldown.Group
|
|
1051
|
+
id="italy"
|
|
1052
|
+
renderGroupTitle={this.state.showTitles && 'Italy'}
|
|
1053
|
+
withoutSeparators={!this.state.showSeparators}
|
|
1054
|
+
>
|
|
1055
|
+
<Drilldown.Option id="item11">Milano</Drilldown.Option>
|
|
1056
|
+
<Drilldown.Option id="item12">Florence</Drilldown.Option>
|
|
1057
|
+
</Drilldown.Group>
|
|
1058
|
+
|
|
1059
|
+
<Drilldown.Group
|
|
1060
|
+
id="france"
|
|
1061
|
+
renderGroupTitle={this.state.showTitles && 'France'}
|
|
1062
|
+
withoutSeparators={!this.state.showSeparators}
|
|
1063
|
+
>
|
|
1064
|
+
<Drilldown.Option id="item21">Lyon</Drilldown.Option>
|
|
1065
|
+
<Drilldown.Option id="item22">Bordeaux</Drilldown.Option>
|
|
1066
|
+
</Drilldown.Group>
|
|
1067
|
+
|
|
1068
|
+
<Drilldown.Group
|
|
1069
|
+
id="germany"
|
|
1070
|
+
renderGroupTitle={this.state.showTitles && 'Germany'}
|
|
1071
|
+
withoutSeparators={!this.state.showSeparators}
|
|
1072
|
+
>
|
|
1073
|
+
<Drilldown.Option id="item31">Frankfurt</Drilldown.Option>
|
|
1074
|
+
<Drilldown.Option id="item32">Dortmund</Drilldown.Option>
|
|
1075
|
+
</Drilldown.Group>
|
|
1076
|
+
</Drilldown.Page>
|
|
1077
|
+
</Drilldown>
|
|
1078
|
+
</Flex.Item>
|
|
1079
|
+
|
|
1080
|
+
<Flex.Item padding="0 0 0 large">
|
|
1081
|
+
<FormFieldGroup description="Drilldown Group example settings">
|
|
1082
|
+
<Checkbox
|
|
1083
|
+
checked={this.state.showSeparators}
|
|
1084
|
+
label="Show separators"
|
|
1085
|
+
variant="toggle"
|
|
1086
|
+
onChange={() => {
|
|
1087
|
+
this.setState({ showSeparators: !this.state.showSeparators })
|
|
1088
|
+
}}
|
|
1089
|
+
/>
|
|
1090
|
+
<Checkbox
|
|
1091
|
+
checked={this.state.showTitles}
|
|
1092
|
+
label="Show group titles"
|
|
1093
|
+
variant="toggle"
|
|
1094
|
+
onChange={() => {
|
|
1095
|
+
this.setState({ showTitles: !this.state.showTitles })
|
|
1096
|
+
}}
|
|
1097
|
+
/>
|
|
1098
|
+
</FormFieldGroup>
|
|
1099
|
+
</Flex.Item>
|
|
1100
|
+
</Flex>
|
|
1101
|
+
)
|
|
1102
|
+
}
|
|
651
1103
|
}
|
|
652
1104
|
|
|
653
|
-
render()
|
|
1105
|
+
render(<GroupsExample />)
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
- ```js
|
|
1109
|
+
const GroupsExample = () => {
|
|
1110
|
+
const [showSeparators, setShowSeparators] = useState(true)
|
|
1111
|
+
const [showTitles, setShowTitles] = useState(true)
|
|
1112
|
+
|
|
654
1113
|
return (
|
|
655
|
-
<Flex alignItems=
|
|
1114
|
+
<Flex alignItems="start">
|
|
656
1115
|
<Flex.Item>
|
|
657
|
-
<Drilldown rootPageId=
|
|
1116
|
+
<Drilldown rootPageId="root" width="20rem" maxHeight="30rem">
|
|
658
1117
|
<Drilldown.Page id="root">
|
|
659
1118
|
<Drilldown.Group
|
|
660
1119
|
id="italy"
|
|
661
|
-
renderGroupTitle={
|
|
662
|
-
withoutSeparators={!
|
|
1120
|
+
renderGroupTitle={showTitles && 'Italy'}
|
|
1121
|
+
withoutSeparators={!showSeparators}
|
|
663
1122
|
>
|
|
664
1123
|
<Drilldown.Option id="item11">Milano</Drilldown.Option>
|
|
665
1124
|
<Drilldown.Option id="item12">Florence</Drilldown.Option>
|
|
@@ -667,8 +1126,8 @@ class GroupsExample extends React.Component {
|
|
|
667
1126
|
|
|
668
1127
|
<Drilldown.Group
|
|
669
1128
|
id="france"
|
|
670
|
-
renderGroupTitle={
|
|
671
|
-
withoutSeparators={!
|
|
1129
|
+
renderGroupTitle={showTitles && 'France'}
|
|
1130
|
+
withoutSeparators={!showSeparators}
|
|
672
1131
|
>
|
|
673
1132
|
<Drilldown.Option id="item21">Lyon</Drilldown.Option>
|
|
674
1133
|
<Drilldown.Option id="item22">Bordeaux</Drilldown.Option>
|
|
@@ -676,8 +1135,8 @@ class GroupsExample extends React.Component {
|
|
|
676
1135
|
|
|
677
1136
|
<Drilldown.Group
|
|
678
1137
|
id="germany"
|
|
679
|
-
renderGroupTitle={
|
|
680
|
-
withoutSeparators={!
|
|
1138
|
+
renderGroupTitle={showTitles && 'Germany'}
|
|
1139
|
+
withoutSeparators={!showSeparators}
|
|
681
1140
|
>
|
|
682
1141
|
<Drilldown.Option id="item31">Frankfurt</Drilldown.Option>
|
|
683
1142
|
<Drilldown.Option id="item32">Dortmund</Drilldown.Option>
|
|
@@ -689,34 +1148,29 @@ class GroupsExample extends React.Component {
|
|
|
689
1148
|
<Flex.Item padding="0 0 0 large">
|
|
690
1149
|
<FormFieldGroup description="Drilldown Group example settings">
|
|
691
1150
|
<Checkbox
|
|
692
|
-
checked={
|
|
1151
|
+
checked={showSeparators}
|
|
693
1152
|
label="Show separators"
|
|
694
1153
|
variant="toggle"
|
|
695
|
-
onChange={
|
|
696
|
-
()
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
}
|
|
1154
|
+
onChange={() => {
|
|
1155
|
+
setShowSeparators(!showSeparators)
|
|
1156
|
+
}}
|
|
700
1157
|
/>
|
|
701
1158
|
<Checkbox
|
|
702
|
-
checked={
|
|
1159
|
+
checked={showTitles}
|
|
703
1160
|
label="Show group titles"
|
|
704
1161
|
variant="toggle"
|
|
705
|
-
onChange={
|
|
706
|
-
()
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
}
|
|
1162
|
+
onChange={() => {
|
|
1163
|
+
setShowTitles(!showTitles)
|
|
1164
|
+
}}
|
|
710
1165
|
/>
|
|
711
1166
|
</FormFieldGroup>
|
|
712
1167
|
</Flex.Item>
|
|
713
1168
|
</Flex>
|
|
714
1169
|
)
|
|
715
1170
|
}
|
|
716
|
-
}
|
|
717
1171
|
|
|
718
|
-
render(<GroupsExample />)
|
|
719
|
-
```
|
|
1172
|
+
render(<GroupsExample />)
|
|
1173
|
+
```
|
|
720
1174
|
|
|
721
1175
|
#### Selectable Groups
|
|
722
1176
|
|
|
@@ -759,80 +1213,263 @@ type: example
|
|
|
759
1213
|
|
|
760
1214
|
##### Multi-select Group
|
|
761
1215
|
|
|
762
|
-
```js
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
1216
|
+
- ```js
|
|
1217
|
+
class SelectMembersExample extends React.Component {
|
|
1218
|
+
state = {
|
|
1219
|
+
selected: {}
|
|
1220
|
+
}
|
|
766
1221
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1222
|
+
selectMembers(groupValues) {
|
|
1223
|
+
this.setState(({ selected }) => {
|
|
1224
|
+
return { selected: { ...selected, ...groupValues } }
|
|
1225
|
+
})
|
|
1226
|
+
}
|
|
771
1227
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
})
|
|
776
|
-
}
|
|
1228
|
+
renderGroups(groups) {
|
|
1229
|
+
return groups.map((group, idx) => {
|
|
1230
|
+
const { id, label, members, selectableType } = group
|
|
777
1231
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1232
|
+
return (
|
|
1233
|
+
<Drilldown.Group
|
|
1234
|
+
id={id}
|
|
1235
|
+
key={idx}
|
|
1236
|
+
renderGroupTitle={label}
|
|
1237
|
+
selectableType={selectableType}
|
|
1238
|
+
onSelect={(_e, { value }) => {
|
|
1239
|
+
this.selectMembers({ [id]: value })
|
|
1240
|
+
}}
|
|
1241
|
+
>
|
|
1242
|
+
{members.map((member, idx) => {
|
|
1243
|
+
const { id, name } = member
|
|
1244
|
+
return (
|
|
1245
|
+
<Drilldown.Option key={idx} id={id} value={name}>
|
|
1246
|
+
{name}
|
|
1247
|
+
</Drilldown.Option>
|
|
1248
|
+
)
|
|
1249
|
+
})}
|
|
1250
|
+
</Drilldown.Group>
|
|
1251
|
+
)
|
|
1252
|
+
})
|
|
1253
|
+
}
|
|
781
1254
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
key={idx}
|
|
786
|
-
renderGroupTitle={label}
|
|
787
|
-
selectableType={selectableType}
|
|
788
|
-
onSelect={(_e, { value }) => {
|
|
789
|
-
this.selectMembers({ [id]: value })
|
|
790
|
-
}}
|
|
791
|
-
>
|
|
792
|
-
{members.map((member, idx) => {
|
|
793
|
-
const { id, name } = member
|
|
794
|
-
return (
|
|
795
|
-
<Drilldown.Option key={idx} id={id} value={name}>
|
|
796
|
-
{name}
|
|
797
|
-
</Drilldown.Option>
|
|
798
|
-
)
|
|
799
|
-
})}
|
|
1255
|
+
renderSubPageOptions(subPages = []) {
|
|
1256
|
+
return subPages.map((subPage, idx) => {
|
|
1257
|
+
const { id, label } = subPage
|
|
800
1258
|
|
|
801
|
-
|
|
1259
|
+
return (
|
|
1260
|
+
<Drilldown.Option key={idx} id={id} subPageId={id}>
|
|
1261
|
+
{label}
|
|
1262
|
+
</Drilldown.Option>
|
|
1263
|
+
)
|
|
1264
|
+
})
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
renderPage(category, key) {
|
|
1268
|
+
const { id, subPages = [], groups = [] } = category
|
|
1269
|
+
|
|
1270
|
+
return (
|
|
1271
|
+
<Drilldown.Page key={key} id={id}>
|
|
1272
|
+
{subPages.length > 0 && this.renderSubPageOptions(subPages)}
|
|
1273
|
+
{groups.length > 0 && this.renderGroups(groups)}
|
|
1274
|
+
</Drilldown.Page>
|
|
802
1275
|
)
|
|
803
|
-
}
|
|
804
|
-
}
|
|
1276
|
+
}
|
|
805
1277
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
const {
|
|
1278
|
+
render() {
|
|
1279
|
+
const { pages } = this.props
|
|
1280
|
+
const { selected = {} } = this.state
|
|
809
1281
|
|
|
810
1282
|
return (
|
|
811
|
-
<
|
|
812
|
-
|
|
813
|
-
|
|
1283
|
+
<Flex alignItems="start">
|
|
1284
|
+
<Flex.Item width="20rem" textAlign="center" padding="x-small 0 0">
|
|
1285
|
+
<Drilldown
|
|
1286
|
+
rootPageId="root"
|
|
1287
|
+
width="18.5rem"
|
|
1288
|
+
maxHeight="26.5rem"
|
|
1289
|
+
trigger={<Button>Select Members</Button>}
|
|
1290
|
+
shouldHideOnSelect={false}
|
|
1291
|
+
onToggle={(_event, { shown }) => {
|
|
1292
|
+
shown && this.selectMembers({})
|
|
1293
|
+
}}
|
|
1294
|
+
>
|
|
1295
|
+
{pages.map((page, idx) => this.renderPage(page, idx))}
|
|
1296
|
+
</Drilldown>
|
|
1297
|
+
</Flex.Item>
|
|
1298
|
+
|
|
1299
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
1300
|
+
<View as="div" padding="small" background="primary">
|
|
1301
|
+
<div>
|
|
1302
|
+
<Text weight="bold">Selected members:</Text>
|
|
1303
|
+
</div>
|
|
1304
|
+
<div>
|
|
1305
|
+
<List>
|
|
1306
|
+
{Object.entries(selected).map(([groupId, values], idx) => {
|
|
1307
|
+
return values.length > 0 ? (
|
|
1308
|
+
<List.Item key={idx}>
|
|
1309
|
+
<b>{groupId}</b>: {values.join(', ')}
|
|
1310
|
+
</List.Item>
|
|
1311
|
+
) : null
|
|
1312
|
+
})}
|
|
1313
|
+
</List>
|
|
1314
|
+
</div>
|
|
1315
|
+
</View>
|
|
1316
|
+
</Flex.Item>
|
|
1317
|
+
</Flex>
|
|
814
1318
|
)
|
|
815
|
-
}
|
|
1319
|
+
}
|
|
816
1320
|
}
|
|
817
1321
|
|
|
818
|
-
|
|
819
|
-
|
|
1322
|
+
const pages = [
|
|
1323
|
+
{
|
|
1324
|
+
id: 'root',
|
|
1325
|
+
subPages: [
|
|
1326
|
+
{ id: 'courses', label: 'Courses' },
|
|
1327
|
+
{ id: 'groups', label: 'Groups' },
|
|
1328
|
+
{ id: 'consortiums', label: 'Consortiums' }
|
|
1329
|
+
]
|
|
1330
|
+
},
|
|
1331
|
+
{
|
|
1332
|
+
id: 'courses',
|
|
1333
|
+
groups: [
|
|
1334
|
+
{
|
|
1335
|
+
id: 'course1',
|
|
1336
|
+
label: 'Course 1',
|
|
1337
|
+
selectableType: 'multiple',
|
|
1338
|
+
members: [
|
|
1339
|
+
{ id: 'course1_1', name: 'Hanna Septimus' },
|
|
1340
|
+
{ id: 'course1_2', name: 'Kadin Press' },
|
|
1341
|
+
{ id: 'course1_3', name: 'Kaiya Botosh' }
|
|
1342
|
+
]
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
id: 'course2',
|
|
1346
|
+
label: 'Course 2',
|
|
1347
|
+
selectableType: 'multiple',
|
|
1348
|
+
members: [
|
|
1349
|
+
{ id: 'course2_1', name: 'Leo Calzoni' },
|
|
1350
|
+
{ id: 'course2_2', name: 'Gretchen Gouse' }
|
|
1351
|
+
]
|
|
1352
|
+
}
|
|
1353
|
+
]
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
id: 'groups',
|
|
1357
|
+
groups: [
|
|
1358
|
+
{
|
|
1359
|
+
id: 'group1',
|
|
1360
|
+
label: 'Group 1',
|
|
1361
|
+
selectableType: 'multiple',
|
|
1362
|
+
members: [
|
|
1363
|
+
{ id: 'groups1_1', name: 'Penka Okabe' },
|
|
1364
|
+
{ id: 'groups1_2', name: 'Ausma Meggyesfalvi' },
|
|
1365
|
+
{ id: 'groups1_3', name: 'Endrit Höfler' },
|
|
1366
|
+
{ id: 'groups1_4', name: 'Ryuu Carey' },
|
|
1367
|
+
{ id: 'groups1_5', name: 'Daphne Dioli' }
|
|
1368
|
+
]
|
|
1369
|
+
}
|
|
1370
|
+
]
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
id: 'consortiums',
|
|
1374
|
+
groups: [
|
|
1375
|
+
{
|
|
1376
|
+
id: 'consortium1',
|
|
1377
|
+
label: 'Consortium 1',
|
|
1378
|
+
selectableType: 'multiple',
|
|
1379
|
+
members: [
|
|
1380
|
+
{ id: 'consortium1_1', name: 'Brahim Gustavsson' },
|
|
1381
|
+
{ id: 'consortium1_2', name: 'Elodia Dreschner' }
|
|
1382
|
+
]
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
id: 'consortium2',
|
|
1386
|
+
label: 'Consortium 2',
|
|
1387
|
+
selectableType: 'multiple',
|
|
1388
|
+
members: [
|
|
1389
|
+
{ id: 'consortium2_1', name: 'Darwin Peter' },
|
|
1390
|
+
{ id: 'consortium2_2', name: 'Sukhrab Burnham' }
|
|
1391
|
+
]
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
id: 'consortium3',
|
|
1395
|
+
label: 'Consortium 3',
|
|
1396
|
+
selectableType: 'multiple',
|
|
1397
|
+
members: [
|
|
1398
|
+
{ id: 'consortium3_1', name: 'Jeffry Antonise' },
|
|
1399
|
+
{ id: 'consortium3_2', name: 'Bia Regenbogen' }
|
|
1400
|
+
]
|
|
1401
|
+
}
|
|
1402
|
+
]
|
|
1403
|
+
}
|
|
1404
|
+
]
|
|
820
1405
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
)
|
|
827
|
-
|
|
1406
|
+
render(<SelectMembersExample pages={pages} />)
|
|
1407
|
+
```
|
|
1408
|
+
|
|
1409
|
+
- ```js
|
|
1410
|
+
const SelectMembersExample = (props) => {
|
|
1411
|
+
const [selected, setSelected] = useState({})
|
|
1412
|
+
|
|
1413
|
+
const selectMembers = (groupValues) => {
|
|
1414
|
+
setSelected((selected) => ({
|
|
1415
|
+
...selected,
|
|
1416
|
+
...groupValues
|
|
1417
|
+
}))
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
const renderGroups = (groups) => {
|
|
1421
|
+
return groups.map((group, idx) => {
|
|
1422
|
+
const { id, label, members, selectableType } = group
|
|
1423
|
+
|
|
1424
|
+
return (
|
|
1425
|
+
<Drilldown.Group
|
|
1426
|
+
id={id}
|
|
1427
|
+
key={idx}
|
|
1428
|
+
renderGroupTitle={label}
|
|
1429
|
+
selectableType={selectableType}
|
|
1430
|
+
onSelect={(_e, { value }) => {
|
|
1431
|
+
selectMembers({ [id]: value })
|
|
1432
|
+
}}
|
|
1433
|
+
>
|
|
1434
|
+
{members.map((member, idx) => {
|
|
1435
|
+
const { id, name } = member
|
|
1436
|
+
return (
|
|
1437
|
+
<Drilldown.Option key={idx} id={id} value={name}>
|
|
1438
|
+
{name}
|
|
1439
|
+
</Drilldown.Option>
|
|
1440
|
+
)
|
|
1441
|
+
})}
|
|
1442
|
+
</Drilldown.Group>
|
|
1443
|
+
)
|
|
1444
|
+
})
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
const renderSubPageOptions = (subPages = []) => {
|
|
1448
|
+
return subPages.map((subPage, idx) => {
|
|
1449
|
+
const { id, label } = subPage
|
|
1450
|
+
|
|
1451
|
+
return (
|
|
1452
|
+
<Drilldown.Option key={idx} id={id} subPageId={id}>
|
|
1453
|
+
{label}
|
|
1454
|
+
</Drilldown.Option>
|
|
1455
|
+
)
|
|
1456
|
+
})
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const renderPage = (category, key) => {
|
|
1460
|
+
const { id, subPages = [], groups = [] } = category
|
|
828
1461
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1462
|
+
return (
|
|
1463
|
+
<Drilldown.Page key={key} id={id}>
|
|
1464
|
+
{subPages.length > 0 && renderSubPageOptions(subPages)}
|
|
1465
|
+
{groups.length > 0 && renderGroups(groups)}
|
|
1466
|
+
</Drilldown.Page>
|
|
1467
|
+
)
|
|
1468
|
+
}
|
|
832
1469
|
|
|
833
1470
|
return (
|
|
834
|
-
<Flex alignItems=
|
|
835
|
-
<Flex.Item width="20rem" textAlign="center" padding=
|
|
1471
|
+
<Flex alignItems="start">
|
|
1472
|
+
<Flex.Item width="20rem" textAlign="center" padding="x-small 0 0">
|
|
836
1473
|
<Drilldown
|
|
837
1474
|
rootPageId="root"
|
|
838
1475
|
width="18.5rem"
|
|
@@ -840,21 +1477,21 @@ class SelectMembersExample extends React.Component {
|
|
|
840
1477
|
trigger={<Button>Select Members</Button>}
|
|
841
1478
|
shouldHideOnSelect={false}
|
|
842
1479
|
onToggle={(_event, { shown }) => {
|
|
843
|
-
shown &&
|
|
1480
|
+
shown && selectMembers({})
|
|
844
1481
|
}}
|
|
845
1482
|
>
|
|
846
|
-
{pages.map((page, idx) =>
|
|
1483
|
+
{props.pages.map((page, idx) => renderPage(page, idx))}
|
|
847
1484
|
</Drilldown>
|
|
848
1485
|
</Flex.Item>
|
|
849
1486
|
|
|
850
|
-
<Flex.Item padding=
|
|
851
|
-
<View as="div" padding=
|
|
1487
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
1488
|
+
<View as="div" padding="small" background="primary">
|
|
852
1489
|
<div>
|
|
853
|
-
<Text weight=
|
|
1490
|
+
<Text weight="bold">Selected members:</Text>
|
|
854
1491
|
</div>
|
|
855
1492
|
<div>
|
|
856
1493
|
<List>
|
|
857
|
-
{Object.entries(selected).map(([
|
|
1494
|
+
{Object.entries(selected).map(([groupId, values], idx) => {
|
|
858
1495
|
return values.length > 0 ? (
|
|
859
1496
|
<List.Item key={idx}>
|
|
860
1497
|
<b>{groupId}</b>: {values.join(', ')}
|
|
@@ -868,111 +1505,228 @@ class SelectMembersExample extends React.Component {
|
|
|
868
1505
|
</Flex>
|
|
869
1506
|
)
|
|
870
1507
|
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const pages = [
|
|
874
|
-
{
|
|
875
|
-
id: 'root',
|
|
876
|
-
subPages: [
|
|
877
|
-
{ id: 'courses', label: 'Courses' },
|
|
878
|
-
{ id: 'groups', label: 'Groups' },
|
|
879
|
-
{ id: 'consortiums', label: 'Consortiums' }
|
|
880
|
-
]
|
|
881
|
-
},
|
|
882
|
-
{
|
|
883
|
-
id: 'courses',
|
|
884
|
-
groups: [
|
|
885
|
-
{
|
|
886
|
-
id: 'course1',
|
|
887
|
-
label: 'Course 1',
|
|
888
|
-
selectableType: 'multiple',
|
|
889
|
-
members: [
|
|
890
|
-
{ id: 'course1_1', name: 'Hanna Septimus' },
|
|
891
|
-
{ id: 'course1_2', name: 'Kadin Press' },
|
|
892
|
-
{ id: 'course1_3', name: 'Kaiya Botosh' },
|
|
893
|
-
]
|
|
894
|
-
},
|
|
895
|
-
{
|
|
896
|
-
id: 'course2',
|
|
897
|
-
label: 'Course 2',
|
|
898
|
-
selectableType: 'multiple',
|
|
899
|
-
members: [
|
|
900
|
-
{ id: 'course2_1', name: 'Leo Calzoni' },
|
|
901
|
-
{ id: 'course2_2', name: 'Gretchen Gouse' },
|
|
902
|
-
]
|
|
903
|
-
}
|
|
904
|
-
]
|
|
905
|
-
},
|
|
906
|
-
{
|
|
907
|
-
id: 'groups',
|
|
908
|
-
groups: [
|
|
909
|
-
{
|
|
910
|
-
id: 'group1',
|
|
911
|
-
label: 'Group 1',
|
|
912
|
-
selectableType: 'multiple',
|
|
913
|
-
members: [
|
|
914
|
-
{ id: 'groups1_1', name: 'Penka Okabe' },
|
|
915
|
-
{ id: 'groups1_2', name: 'Ausma Meggyesfalvi' },
|
|
916
|
-
{ id: 'groups1_3', name: 'Endrit Höfler' },
|
|
917
|
-
{ id: 'groups1_4', name: 'Ryuu Carey' },
|
|
918
|
-
{ id: 'groups1_5', name: 'Daphne Dioli' },
|
|
919
|
-
]
|
|
920
|
-
}
|
|
921
|
-
]
|
|
922
|
-
},
|
|
923
|
-
{
|
|
924
|
-
id: 'consortiums',
|
|
925
|
-
groups: [
|
|
926
|
-
{
|
|
927
|
-
id: 'consortium1',
|
|
928
|
-
label: 'Consortium 1',
|
|
929
|
-
selectableType: 'multiple',
|
|
930
|
-
members: [
|
|
931
|
-
{ id: 'consortium1_1', name: 'Brahim Gustavsson' },
|
|
932
|
-
{ id: 'consortium1_2', name: 'Elodia Dreschner' },
|
|
933
|
-
]
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
id: 'consortium2',
|
|
937
|
-
label: 'Consortium 2',
|
|
938
|
-
selectableType: 'multiple',
|
|
939
|
-
members: [
|
|
940
|
-
{ id: 'consortium2_1', name: 'Darwin Peter' },
|
|
941
|
-
{ id: 'consortium2_2', name: 'Sukhrab Burnham' },
|
|
942
|
-
]
|
|
943
|
-
},
|
|
944
|
-
{
|
|
945
|
-
id: 'consortium3',
|
|
946
|
-
label: 'Consortium 3',
|
|
947
|
-
selectableType: 'multiple',
|
|
948
|
-
members: [
|
|
949
|
-
{ id: 'consortium3_1', name: 'Jeffry Antonise' },
|
|
950
|
-
{ id: 'consortium3_2', name: 'Bia Regenbogen' },
|
|
951
|
-
]
|
|
952
|
-
}
|
|
953
|
-
]
|
|
954
|
-
}
|
|
955
|
-
]
|
|
956
1508
|
|
|
957
|
-
|
|
958
|
-
|
|
1509
|
+
const pages = [
|
|
1510
|
+
{
|
|
1511
|
+
id: 'root',
|
|
1512
|
+
subPages: [
|
|
1513
|
+
{ id: 'courses', label: 'Courses' },
|
|
1514
|
+
{ id: 'groups', label: 'Groups' },
|
|
1515
|
+
{ id: 'consortiums', label: 'Consortiums' }
|
|
1516
|
+
]
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
id: 'courses',
|
|
1520
|
+
groups: [
|
|
1521
|
+
{
|
|
1522
|
+
id: 'course1',
|
|
1523
|
+
label: 'Course 1',
|
|
1524
|
+
selectableType: 'multiple',
|
|
1525
|
+
members: [
|
|
1526
|
+
{ id: 'course1_1', name: 'Hanna Septimus' },
|
|
1527
|
+
{ id: 'course1_2', name: 'Kadin Press' },
|
|
1528
|
+
{ id: 'course1_3', name: 'Kaiya Botosh' }
|
|
1529
|
+
]
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
id: 'course2',
|
|
1533
|
+
label: 'Course 2',
|
|
1534
|
+
selectableType: 'multiple',
|
|
1535
|
+
members: [
|
|
1536
|
+
{ id: 'course2_1', name: 'Leo Calzoni' },
|
|
1537
|
+
{ id: 'course2_2', name: 'Gretchen Gouse' }
|
|
1538
|
+
]
|
|
1539
|
+
}
|
|
1540
|
+
]
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
id: 'groups',
|
|
1544
|
+
groups: [
|
|
1545
|
+
{
|
|
1546
|
+
id: 'group1',
|
|
1547
|
+
label: 'Group 1',
|
|
1548
|
+
selectableType: 'multiple',
|
|
1549
|
+
members: [
|
|
1550
|
+
{ id: 'groups1_1', name: 'Penka Okabe' },
|
|
1551
|
+
{ id: 'groups1_2', name: 'Ausma Meggyesfalvi' },
|
|
1552
|
+
{ id: 'groups1_3', name: 'Endrit Höfler' },
|
|
1553
|
+
{ id: 'groups1_4', name: 'Ryuu Carey' },
|
|
1554
|
+
{ id: 'groups1_5', name: 'Daphne Dioli' }
|
|
1555
|
+
]
|
|
1556
|
+
}
|
|
1557
|
+
]
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
id: 'consortiums',
|
|
1561
|
+
groups: [
|
|
1562
|
+
{
|
|
1563
|
+
id: 'consortium1',
|
|
1564
|
+
label: 'Consortium 1',
|
|
1565
|
+
selectableType: 'multiple',
|
|
1566
|
+
members: [
|
|
1567
|
+
{ id: 'consortium1_1', name: 'Brahim Gustavsson' },
|
|
1568
|
+
{ id: 'consortium1_2', name: 'Elodia Dreschner' }
|
|
1569
|
+
]
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
id: 'consortium2',
|
|
1573
|
+
label: 'Consortium 2',
|
|
1574
|
+
selectableType: 'multiple',
|
|
1575
|
+
members: [
|
|
1576
|
+
{ id: 'consortium2_1', name: 'Darwin Peter' },
|
|
1577
|
+
{ id: 'consortium2_2', name: 'Sukhrab Burnham' }
|
|
1578
|
+
]
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
id: 'consortium3',
|
|
1582
|
+
label: 'Consortium 3',
|
|
1583
|
+
selectableType: 'multiple',
|
|
1584
|
+
members: [
|
|
1585
|
+
{ id: 'consortium3_1', name: 'Jeffry Antonise' },
|
|
1586
|
+
{ id: 'consortium3_2', name: 'Bia Regenbogen' }
|
|
1587
|
+
]
|
|
1588
|
+
}
|
|
1589
|
+
]
|
|
1590
|
+
}
|
|
1591
|
+
]
|
|
1592
|
+
|
|
1593
|
+
render(<SelectMembersExample pages={pages} />)
|
|
1594
|
+
```
|
|
959
1595
|
|
|
960
1596
|
### Disabled prop
|
|
961
1597
|
|
|
962
1598
|
You can disable the whole Drilldown or its sub-components with the `disabled` prop. The only option that can not be disabled is the Back navigation.
|
|
963
1599
|
|
|
964
|
-
```js
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1600
|
+
- ```js
|
|
1601
|
+
class DisabledExample extends React.Component {
|
|
1602
|
+
state = {
|
|
1603
|
+
disabled: 'None',
|
|
1604
|
+
withTrigger: false
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
render() {
|
|
1608
|
+
const { disabled, withTrigger } = this.state
|
|
1609
|
+
|
|
1610
|
+
const disabledDrilldown = disabled === 'Drilldown'
|
|
1611
|
+
const disabledPages = disabled === 'Pages'
|
|
1612
|
+
const disabledGroups = disabled === 'Groups'
|
|
1613
|
+
const disabledOptions = disabled === 'Options'
|
|
1614
|
+
|
|
1615
|
+
return (
|
|
1616
|
+
<Flex alignItems="start">
|
|
1617
|
+
<Flex.Item width="20rem" textAlign="center" padding="x-small 0 0">
|
|
1618
|
+
<View textAlign="start">
|
|
1619
|
+
<Drilldown
|
|
1620
|
+
rootPageId="root"
|
|
1621
|
+
width="20rem"
|
|
1622
|
+
maxHeight="30rem"
|
|
1623
|
+
trigger={withTrigger && <Button>Toggle button</Button>}
|
|
1624
|
+
disabled={disabledDrilldown}
|
|
1625
|
+
>
|
|
1626
|
+
<Drilldown.Page
|
|
1627
|
+
id="root"
|
|
1628
|
+
renderTitle="Root page"
|
|
1629
|
+
renderActionLabel="Action"
|
|
1630
|
+
disabled={disabledPages}
|
|
1631
|
+
>
|
|
1632
|
+
<Drilldown.Option
|
|
1633
|
+
id="disabledOption1"
|
|
1634
|
+
subPageId="secondPage"
|
|
1635
|
+
disabled={disabledOptions}
|
|
1636
|
+
>
|
|
1637
|
+
Option with subPage navigation
|
|
1638
|
+
</Drilldown.Option>
|
|
1639
|
+
<Drilldown.Option
|
|
1640
|
+
id="disabledOption2"
|
|
1641
|
+
disabled={disabledOptions}
|
|
1642
|
+
>
|
|
1643
|
+
Option
|
|
1644
|
+
</Drilldown.Option>
|
|
1645
|
+
<Drilldown.Group
|
|
1646
|
+
id="group1"
|
|
1647
|
+
renderGroupTitle="Selectable Group"
|
|
1648
|
+
selectableType="multiple"
|
|
1649
|
+
disabled={disabledGroups}
|
|
1650
|
+
>
|
|
1651
|
+
{['Apple', 'Orange', 'Banana', 'Strawberry'].map((item) => (
|
|
1652
|
+
<Drilldown.Option
|
|
1653
|
+
id={item}
|
|
1654
|
+
key={item}
|
|
1655
|
+
value={item}
|
|
1656
|
+
defaultSelected={item === 'Apple'}
|
|
1657
|
+
disabled={disabledOptions}
|
|
1658
|
+
>
|
|
1659
|
+
{item}
|
|
1660
|
+
</Drilldown.Option>
|
|
1661
|
+
))}
|
|
1662
|
+
</Drilldown.Group>
|
|
1663
|
+
</Drilldown.Page>
|
|
1664
|
+
|
|
1665
|
+
<Drilldown.Page
|
|
1666
|
+
id="secondPage"
|
|
1667
|
+
renderTitle="Second page"
|
|
1668
|
+
disabled={disabledPages}
|
|
1669
|
+
>
|
|
1670
|
+
{['Option 1', 'Option 2', 'Option 3', 'Option 4'].map(
|
|
1671
|
+
(item) => (
|
|
1672
|
+
<Drilldown.Option
|
|
1673
|
+
id={item}
|
|
1674
|
+
key={item}
|
|
1675
|
+
disabled={disabledOptions}
|
|
1676
|
+
>
|
|
1677
|
+
{item}
|
|
1678
|
+
</Drilldown.Option>
|
|
1679
|
+
)
|
|
1680
|
+
)}
|
|
1681
|
+
</Drilldown.Page>
|
|
1682
|
+
</Drilldown>
|
|
1683
|
+
</View>
|
|
1684
|
+
</Flex.Item>
|
|
1685
|
+
|
|
1686
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
1687
|
+
<FormFieldGroup
|
|
1688
|
+
description={<ScreenReaderContent>Settings</ScreenReaderContent>}
|
|
1689
|
+
colSpacing="medium"
|
|
1690
|
+
layout="columns"
|
|
1691
|
+
vAlign="top"
|
|
1692
|
+
>
|
|
1693
|
+
<RadioInputGroup
|
|
1694
|
+
onChange={(_event, value) => {
|
|
1695
|
+
this.setState({ disabled: value })
|
|
1696
|
+
}}
|
|
1697
|
+
defaultValue="None"
|
|
1698
|
+
name="disabledPart"
|
|
1699
|
+
description="Select disabled"
|
|
1700
|
+
>
|
|
1701
|
+
{['None', 'Drilldown', 'Pages', 'Groups', 'Options'].map(
|
|
1702
|
+
(part) => (
|
|
1703
|
+
<RadioInput key={part} value={part} label={part} />
|
|
1704
|
+
)
|
|
1705
|
+
)}
|
|
1706
|
+
</RadioInputGroup>
|
|
1707
|
+
|
|
1708
|
+
<Checkbox
|
|
1709
|
+
checked={this.state.withTrigger}
|
|
1710
|
+
label="With toggle button"
|
|
1711
|
+
variant="toggle"
|
|
1712
|
+
onChange={() => {
|
|
1713
|
+
this.setState({ withTrigger: !this.state.withTrigger })
|
|
1714
|
+
}}
|
|
1715
|
+
/>
|
|
1716
|
+
</FormFieldGroup>
|
|
1717
|
+
</Flex.Item>
|
|
1718
|
+
</Flex>
|
|
1719
|
+
)
|
|
1720
|
+
}
|
|
972
1721
|
}
|
|
973
1722
|
|
|
974
|
-
render()
|
|
975
|
-
|
|
1723
|
+
render(<DisabledExample />)
|
|
1724
|
+
```
|
|
1725
|
+
|
|
1726
|
+
- ```js
|
|
1727
|
+
const DisabledExample = () => {
|
|
1728
|
+
const [disabled, setDisabled] = useState('None')
|
|
1729
|
+
const [withTrigger, setWithTrigger] = useState(false)
|
|
976
1730
|
|
|
977
1731
|
const disabledDrilldown = disabled === 'Drilldown'
|
|
978
1732
|
const disabledPages = disabled === 'Pages'
|
|
@@ -980,11 +1734,11 @@ class DisabledExample extends React.Component {
|
|
|
980
1734
|
const disabledOptions = disabled === 'Options'
|
|
981
1735
|
|
|
982
1736
|
return (
|
|
983
|
-
<Flex alignItems=
|
|
984
|
-
<Flex.Item width="20rem" textAlign=
|
|
985
|
-
<View textAlign=
|
|
1737
|
+
<Flex alignItems="start">
|
|
1738
|
+
<Flex.Item width="20rem" textAlign="center" padding="x-small 0 0">
|
|
1739
|
+
<View textAlign="start">
|
|
986
1740
|
<Drilldown
|
|
987
|
-
rootPageId=
|
|
1741
|
+
rootPageId="root"
|
|
988
1742
|
width="20rem"
|
|
989
1743
|
maxHeight="30rem"
|
|
990
1744
|
trigger={withTrigger && <Button>Toggle button</Button>}
|
|
@@ -998,33 +1752,34 @@ class DisabledExample extends React.Component {
|
|
|
998
1752
|
>
|
|
999
1753
|
<Drilldown.Option
|
|
1000
1754
|
id="disabledOption1"
|
|
1001
|
-
subPageId=
|
|
1755
|
+
subPageId="secondPage"
|
|
1002
1756
|
disabled={disabledOptions}
|
|
1003
1757
|
>
|
|
1004
1758
|
Option with subPage navigation
|
|
1005
1759
|
</Drilldown.Option>
|
|
1006
|
-
<Drilldown.Option
|
|
1760
|
+
<Drilldown.Option
|
|
1761
|
+
id="disabledOption2"
|
|
1762
|
+
disabled={disabledOptions}
|
|
1763
|
+
>
|
|
1007
1764
|
Option
|
|
1008
1765
|
</Drilldown.Option>
|
|
1009
1766
|
<Drilldown.Group
|
|
1010
1767
|
id="group1"
|
|
1011
1768
|
renderGroupTitle="Selectable Group"
|
|
1012
|
-
selectableType=
|
|
1769
|
+
selectableType="multiple"
|
|
1013
1770
|
disabled={disabledGroups}
|
|
1014
1771
|
>
|
|
1015
|
-
{['Apple', 'Orange', 'Banana', 'Strawberry'].map(
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
)
|
|
1027
|
-
)}
|
|
1772
|
+
{['Apple', 'Orange', 'Banana', 'Strawberry'].map((item) => (
|
|
1773
|
+
<Drilldown.Option
|
|
1774
|
+
id={item}
|
|
1775
|
+
key={item}
|
|
1776
|
+
value={item}
|
|
1777
|
+
defaultSelected={item === 'Apple'}
|
|
1778
|
+
disabled={disabledOptions}
|
|
1779
|
+
>
|
|
1780
|
+
{item}
|
|
1781
|
+
</Drilldown.Option>
|
|
1782
|
+
))}
|
|
1028
1783
|
</Drilldown.Group>
|
|
1029
1784
|
</Drilldown.Page>
|
|
1030
1785
|
|
|
@@ -1034,7 +1789,7 @@ class DisabledExample extends React.Component {
|
|
|
1034
1789
|
disabled={disabledPages}
|
|
1035
1790
|
>
|
|
1036
1791
|
{['Option 1', 'Option 2', 'Option 3', 'Option 4'].map(
|
|
1037
|
-
item => (
|
|
1792
|
+
(item) => (
|
|
1038
1793
|
<Drilldown.Option
|
|
1039
1794
|
id={item}
|
|
1040
1795
|
key={item}
|
|
@@ -1058,34 +1813,35 @@ class DisabledExample extends React.Component {
|
|
|
1058
1813
|
>
|
|
1059
1814
|
<RadioInputGroup
|
|
1060
1815
|
onChange={(_event, value) => {
|
|
1061
|
-
|
|
1816
|
+
setDisabled(value)
|
|
1062
1817
|
}}
|
|
1063
1818
|
defaultValue="None"
|
|
1064
1819
|
name="disabledPart"
|
|
1065
1820
|
description="Select disabled"
|
|
1066
1821
|
>
|
|
1067
|
-
{['None', 'Drilldown', 'Pages', 'Groups', 'Options'].map(
|
|
1822
|
+
{['None', 'Drilldown', 'Pages', 'Groups', 'Options'].map(
|
|
1823
|
+
(part) => (
|
|
1824
|
+
<RadioInput key={part} value={part} label={part} />
|
|
1825
|
+
)
|
|
1826
|
+
)}
|
|
1068
1827
|
</RadioInputGroup>
|
|
1069
1828
|
|
|
1070
1829
|
<Checkbox
|
|
1071
|
-
checked={
|
|
1830
|
+
checked={withTrigger}
|
|
1072
1831
|
label="With toggle button"
|
|
1073
1832
|
variant="toggle"
|
|
1074
|
-
onChange={
|
|
1075
|
-
()
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1833
|
+
onChange={() => {
|
|
1834
|
+
setWithTrigger(!withTrigger)
|
|
1835
|
+
}}
|
|
1079
1836
|
/>
|
|
1080
1837
|
</FormFieldGroup>
|
|
1081
1838
|
</Flex.Item>
|
|
1082
1839
|
</Flex>
|
|
1083
1840
|
)
|
|
1084
1841
|
}
|
|
1085
|
-
}
|
|
1086
1842
|
|
|
1087
|
-
render(<DisabledExample />)
|
|
1088
|
-
```
|
|
1843
|
+
render(<DisabledExample />)
|
|
1844
|
+
```
|
|
1089
1845
|
|
|
1090
1846
|
### shouldHideOnSelect
|
|
1091
1847
|
|
|
@@ -1232,179 +1988,400 @@ type: example
|
|
|
1232
1988
|
|
|
1233
1989
|
The following example showcases an editable drilldown that can add or delete options.
|
|
1234
1990
|
|
|
1235
|
-
```js
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1991
|
+
- ```js
|
|
1992
|
+
class EditableStructureExample extends React.Component {
|
|
1993
|
+
constructor(props) {
|
|
1994
|
+
super(props)
|
|
1995
|
+
|
|
1996
|
+
this.state = {
|
|
1997
|
+
districtsData: {
|
|
1998
|
+
d1: {
|
|
1999
|
+
label: 'District 1',
|
|
2000
|
+
schools: ['s1']
|
|
2001
|
+
}
|
|
2002
|
+
},
|
|
2003
|
+
schoolsData: {
|
|
2004
|
+
s1: {
|
|
2005
|
+
label: 'School 1',
|
|
2006
|
+
districtId: 'd1',
|
|
2007
|
+
isSelected: false
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1239
2011
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
2012
|
+
this.districtCounter = Object.keys(this.state.districtsData).length
|
|
2013
|
+
this.schoolCounter = Object.keys(this.state.schoolsData).length
|
|
2014
|
+
}
|
|
1243
2015
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
2016
|
+
toggleSelectedSchool(id, school) {
|
|
2017
|
+
this.setState({
|
|
2018
|
+
schoolsData: {
|
|
2019
|
+
...this.state.schoolsData,
|
|
2020
|
+
[id]: { ...school, isSelected: !school.isSelected }
|
|
1249
2021
|
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
2022
|
+
})
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
renderSelectedIcon(isSelected) {
|
|
2026
|
+
return <IconCheckSolid style={{ opacity: isSelected ? 1 : 0 }} />
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
renderAddAction(label) {
|
|
2030
|
+
return (
|
|
2031
|
+
<span>
|
|
2032
|
+
<IconAddSolid />
|
|
2033
|
+
<View as="span" margin="0 0 0 x-small">
|
|
2034
|
+
New {label}
|
|
2035
|
+
</View>
|
|
2036
|
+
</span>
|
|
2037
|
+
)
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
addDistrict() {
|
|
2041
|
+
const { districtsData } = this.state
|
|
2042
|
+
this.districtCounter++
|
|
2043
|
+
const districtNumber = this.districtCounter
|
|
2044
|
+
const districtId = `d${districtNumber}`
|
|
2045
|
+
|
|
2046
|
+
this.setState({
|
|
2047
|
+
districtsData: {
|
|
2048
|
+
...districtsData,
|
|
2049
|
+
[districtId]: {
|
|
2050
|
+
label: `District ${districtNumber}`,
|
|
2051
|
+
schools: []
|
|
2052
|
+
}
|
|
1256
2053
|
}
|
|
1257
|
-
}
|
|
2054
|
+
})
|
|
1258
2055
|
}
|
|
1259
2056
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
2057
|
+
addSchool(districtId) {
|
|
2058
|
+
const { districtsData, schoolsData } = this.state
|
|
2059
|
+
const district = districtsData[districtId]
|
|
1263
2060
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
[id]: { ...school, isSelected: !school.isSelected }
|
|
1268
|
-
} })
|
|
1269
|
-
}
|
|
2061
|
+
this.schoolCounter++
|
|
2062
|
+
const schoolNumber = this.schoolCounter
|
|
2063
|
+
const schoolId = `s${schoolNumber}`
|
|
1270
2064
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
2065
|
+
this.setState({
|
|
2066
|
+
districtsData: {
|
|
2067
|
+
...districtsData,
|
|
2068
|
+
[districtId]: {
|
|
2069
|
+
...district,
|
|
2070
|
+
schools: [...district.schools, schoolId]
|
|
2071
|
+
}
|
|
2072
|
+
},
|
|
2073
|
+
schoolsData: {
|
|
2074
|
+
...schoolsData,
|
|
2075
|
+
[schoolId]: {
|
|
2076
|
+
label: `School ${schoolNumber}`,
|
|
2077
|
+
districtId,
|
|
2078
|
+
isSelected: false
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
})
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
renderDeleteOption(type, label, idToDelete) {
|
|
2085
|
+
const id = type === 'school' ? 'deleteSchool' : 'deleteDistrict'
|
|
2086
|
+
const callback =
|
|
2087
|
+
type === 'school' ? this.deleteSchool : this.deleteDistrict
|
|
2088
|
+
const separatorId = `${idToDelete}__separator`
|
|
2089
|
+
|
|
2090
|
+
return [
|
|
2091
|
+
<Drilldown.Separator id={separatorId} key={separatorId} />,
|
|
2092
|
+
<Drilldown.Option
|
|
2093
|
+
id={id}
|
|
2094
|
+
key={`${idToDelete}__${id}`}
|
|
2095
|
+
onOptionClick={() => {
|
|
2096
|
+
callback(idToDelete)
|
|
2097
|
+
}}
|
|
2098
|
+
themeOverride={(_componentTheme, currentTheme) => {
|
|
2099
|
+
return { color: currentTheme.colors.textDanger }
|
|
2100
|
+
}}
|
|
2101
|
+
>
|
|
2102
|
+
<IconTrashLine />
|
|
2103
|
+
<View as="span" margin="0 0 0 x-small">
|
|
2104
|
+
Delete {label}
|
|
2105
|
+
</View>
|
|
2106
|
+
</Drilldown.Option>
|
|
2107
|
+
]
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
deleteSchool = (id) => {
|
|
2111
|
+
const { districtsData, schoolsData } = this.state
|
|
2112
|
+
const { [id]: school, ...restSchools } = schoolsData
|
|
2113
|
+
const { districtId } = school
|
|
2114
|
+
const district = districtsData[districtId]
|
|
2115
|
+
|
|
2116
|
+
this.setState({
|
|
2117
|
+
schoolsData: restSchools,
|
|
2118
|
+
districtsData: {
|
|
2119
|
+
...districtsData,
|
|
2120
|
+
[districtId]: {
|
|
2121
|
+
...district,
|
|
2122
|
+
schools: district.schools.filter((schoolId) => schoolId !== id)
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
})
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
deleteDistrict = (id) => {
|
|
2129
|
+
const { districtsData, schoolsData } = this.state
|
|
2130
|
+
const { [id]: district, ...restDistricts } = districtsData
|
|
2131
|
+
|
|
2132
|
+
const filteredSchools = {}
|
|
2133
|
+
|
|
2134
|
+
Object.entries(schoolsData).forEach(([schoolId, school]) => {
|
|
2135
|
+
if (school.districtId !== id) {
|
|
2136
|
+
filteredSchools[schoolId] = school
|
|
2137
|
+
}
|
|
2138
|
+
})
|
|
2139
|
+
|
|
2140
|
+
this.setState({
|
|
2141
|
+
districtsData: restDistricts,
|
|
2142
|
+
schoolsData: filteredSchools
|
|
2143
|
+
})
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
render() {
|
|
2147
|
+
const { districtsData, schoolsData } = this.state
|
|
2148
|
+
const districts = Object.entries(districtsData)
|
|
2149
|
+
const schools = Object.entries(schoolsData)
|
|
2150
|
+
|
|
2151
|
+
return (
|
|
2152
|
+
<Drilldown rootPageId="root" width="18.5rem" maxHeight="26.5rem">
|
|
2153
|
+
<Drilldown.Page
|
|
2154
|
+
id="root"
|
|
2155
|
+
renderTitle="Districts"
|
|
2156
|
+
renderActionLabel={this.renderAddAction('District')}
|
|
2157
|
+
onHeaderActionClicked={() => {
|
|
2158
|
+
this.addDistrict()
|
|
2159
|
+
}}
|
|
2160
|
+
>
|
|
2161
|
+
{districts.map(([id, district]) => {
|
|
2162
|
+
const { label } = district
|
|
2163
|
+
return (
|
|
2164
|
+
<Drilldown.Option
|
|
2165
|
+
key={`districtOption__${id}`}
|
|
2166
|
+
id={id}
|
|
2167
|
+
subPageId={id}
|
|
2168
|
+
>
|
|
2169
|
+
{label}
|
|
2170
|
+
</Drilldown.Option>
|
|
2171
|
+
)
|
|
2172
|
+
})}
|
|
2173
|
+
</Drilldown.Page>
|
|
2174
|
+
|
|
2175
|
+
{districts.map(([districtId, district]) => {
|
|
2176
|
+
const { label, schools } = district
|
|
2177
|
+
return (
|
|
2178
|
+
<Drilldown.Page
|
|
2179
|
+
key={`districtPage__${districtId}`}
|
|
2180
|
+
id={districtId}
|
|
2181
|
+
renderTitle={label}
|
|
2182
|
+
renderActionLabel={this.renderAddAction('School')}
|
|
2183
|
+
onHeaderActionClicked={() => {
|
|
2184
|
+
this.addSchool(districtId)
|
|
2185
|
+
}}
|
|
2186
|
+
>
|
|
2187
|
+
{schools.map((id) => {
|
|
2188
|
+
const { label, isSelected } = this.state.schoolsData[id]
|
|
2189
|
+
return (
|
|
2190
|
+
<Drilldown.Option
|
|
2191
|
+
key={`schoolOption__${districtId}--${id}`}
|
|
2192
|
+
id={id}
|
|
2193
|
+
subPageId={id}
|
|
2194
|
+
renderBeforeLabel={this.renderSelectedIcon(isSelected)}
|
|
2195
|
+
>
|
|
2196
|
+
{label}
|
|
2197
|
+
</Drilldown.Option>
|
|
2198
|
+
)
|
|
2199
|
+
})}
|
|
2200
|
+
{this.renderDeleteOption('district', label, districtId)}
|
|
2201
|
+
</Drilldown.Page>
|
|
2202
|
+
)
|
|
2203
|
+
})}
|
|
2204
|
+
|
|
2205
|
+
{schools.map(([id, school]) => {
|
|
2206
|
+
const { label, isSelected } = school
|
|
2207
|
+
return (
|
|
2208
|
+
<Drilldown.Page
|
|
2209
|
+
key={`schoolPage__${id}`}
|
|
2210
|
+
id={id}
|
|
2211
|
+
withoutHeaderSeparator
|
|
2212
|
+
>
|
|
2213
|
+
<Drilldown.Option
|
|
2214
|
+
id="toggleSchool"
|
|
2215
|
+
renderBeforeLabel={this.renderSelectedIcon(isSelected)}
|
|
2216
|
+
onOptionClick={() => {
|
|
2217
|
+
this.toggleSelectedSchool(id, school)
|
|
2218
|
+
}}
|
|
2219
|
+
>
|
|
2220
|
+
{isSelected ? 'Deselect' : 'Select'} {label}
|
|
2221
|
+
</Drilldown.Option>
|
|
2222
|
+
{this.renderDeleteOption('school', label, id)}
|
|
2223
|
+
</Drilldown.Page>
|
|
2224
|
+
)
|
|
2225
|
+
})}
|
|
2226
|
+
</Drilldown>
|
|
2227
|
+
)
|
|
2228
|
+
}
|
|
1277
2229
|
}
|
|
1278
2230
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
2231
|
+
render(<EditableStructureExample />)
|
|
2232
|
+
```
|
|
2233
|
+
|
|
2234
|
+
- ```js
|
|
2235
|
+
const EditableStructureExample = () => {
|
|
2236
|
+
const [districtsData, setDistrictsData] = useState({
|
|
2237
|
+
d1: {
|
|
2238
|
+
label: 'District 1',
|
|
2239
|
+
schools: ['s1']
|
|
2240
|
+
}
|
|
2241
|
+
})
|
|
2242
|
+
const [schoolsData, setSchoolsData] = useState({
|
|
2243
|
+
s1: {
|
|
2244
|
+
label: 'School 1',
|
|
2245
|
+
districtId: 'd1',
|
|
2246
|
+
isSelected: false
|
|
2247
|
+
}
|
|
2248
|
+
})
|
|
2249
|
+
|
|
2250
|
+
const [districtCounter, setDistrictCounter] = useState(
|
|
2251
|
+
Object.keys(districtsData).length
|
|
1285
2252
|
)
|
|
1286
|
-
|
|
2253
|
+
const [schoolCounter, setSchoolCounter] = useState(
|
|
2254
|
+
Object.keys(schoolsData).length
|
|
2255
|
+
)
|
|
2256
|
+
|
|
2257
|
+
const districts = Object.entries(districtsData)
|
|
2258
|
+
const schools = Object.entries(schoolsData)
|
|
2259
|
+
|
|
2260
|
+
const toggleSelectedSchool = (id, school) => {
|
|
2261
|
+
setSchoolsData({
|
|
2262
|
+
...schoolsData,
|
|
2263
|
+
[id]: { ...school, isSelected: !school.isSelected }
|
|
2264
|
+
})
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
const renderSelectedIcon = (isSelected) => {
|
|
2268
|
+
return <IconCheckSolid style={{ opacity: isSelected ? 1 : 0 }} />
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
const renderAddAction = (label) => {
|
|
2272
|
+
return (
|
|
2273
|
+
<span>
|
|
2274
|
+
<IconAddSolid />
|
|
2275
|
+
<View as="span" margin="0 0 0 x-small">
|
|
2276
|
+
New {label}
|
|
2277
|
+
</View>
|
|
2278
|
+
</span>
|
|
2279
|
+
)
|
|
2280
|
+
}
|
|
1287
2281
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
2282
|
+
const addDistrict = () => {
|
|
2283
|
+
const newDistrictCounter = districtCounter + 1
|
|
2284
|
+
const districtNumber = newDistrictCounter
|
|
2285
|
+
const districtId = `d${newDistrictCounter}`
|
|
2286
|
+
setDistrictCounter(newDistrictCounter)
|
|
1293
2287
|
|
|
1294
|
-
|
|
1295
|
-
districtsData: {
|
|
2288
|
+
setDistrictsData((districtsData) => ({
|
|
1296
2289
|
...districtsData,
|
|
1297
2290
|
[districtId]: {
|
|
1298
2291
|
label: `District ${districtNumber}`,
|
|
1299
2292
|
schools: []
|
|
1300
2293
|
}
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
addSchool(districtId) {
|
|
1306
|
-
const { districtsData, schoolsData } = this.state
|
|
1307
|
-
const district = districtsData[districtId]
|
|
2294
|
+
}))
|
|
2295
|
+
}
|
|
1308
2296
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
2297
|
+
const addSchool = (districtId) => {
|
|
2298
|
+
const newSchoolCounter = schoolCounter + 1
|
|
2299
|
+
const district = districtsData[districtId]
|
|
2300
|
+
const schoolNumber = newSchoolCounter
|
|
2301
|
+
const schoolId = `s${newSchoolCounter}`
|
|
2302
|
+
setSchoolCounter(newSchoolCounter)
|
|
1312
2303
|
|
|
1313
|
-
|
|
1314
|
-
districtsData: {
|
|
2304
|
+
setDistrictsData((districtsData) => ({
|
|
1315
2305
|
...districtsData,
|
|
1316
2306
|
[districtId]: {
|
|
1317
|
-
...
|
|
2307
|
+
...districtsData[districtId],
|
|
1318
2308
|
schools: [...district.schools, schoolId]
|
|
1319
2309
|
}
|
|
1320
|
-
}
|
|
1321
|
-
|
|
2310
|
+
}))
|
|
2311
|
+
|
|
2312
|
+
setSchoolsData((schoolsData) => ({
|
|
1322
2313
|
...schoolsData,
|
|
1323
2314
|
[schoolId]: {
|
|
1324
2315
|
label: `School ${schoolNumber}`,
|
|
1325
2316
|
districtId,
|
|
1326
2317
|
isSelected: false
|
|
1327
2318
|
}
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
2319
|
+
}))
|
|
2320
|
+
}
|
|
1331
2321
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
2322
|
+
const renderDeleteOption = (type, label, idToDelete) => {
|
|
2323
|
+
const id = type === 'school' ? 'deleteSchool' : 'deleteDistrict'
|
|
2324
|
+
const callback = type === 'school' ? deleteSchool : deleteDistrict
|
|
2325
|
+
const separatorId = `${idToDelete}__separator`
|
|
2326
|
+
|
|
2327
|
+
return [
|
|
2328
|
+
<Drilldown.Separator id={separatorId} key={separatorId} />,
|
|
2329
|
+
<Drilldown.Option
|
|
2330
|
+
id={id}
|
|
2331
|
+
key={`${idToDelete}__${id}`}
|
|
2332
|
+
onOptionClick={() => {
|
|
2333
|
+
callback(idToDelete)
|
|
2334
|
+
}}
|
|
2335
|
+
themeOverride={(_componentTheme, currentTheme) => {
|
|
2336
|
+
return { color: currentTheme.colors.textDanger }
|
|
2337
|
+
}}
|
|
2338
|
+
>
|
|
2339
|
+
<IconTrashLine />
|
|
2340
|
+
<View as="span" margin="0 0 0 x-small">
|
|
2341
|
+
Delete {label}
|
|
2342
|
+
</View>
|
|
2343
|
+
</Drilldown.Option>
|
|
2344
|
+
]
|
|
2345
|
+
}
|
|
1354
2346
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
const district = districtsData[districtId]
|
|
2347
|
+
const deleteSchool = (id) => {
|
|
2348
|
+
const { [id]: school, ...restSchools } = schoolsData
|
|
2349
|
+
const { districtId } = school
|
|
2350
|
+
const district = districtsData[districtId]
|
|
1360
2351
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
districtsData: {
|
|
2352
|
+
setSchoolsData(restSchools)
|
|
2353
|
+
setDistrictsData({
|
|
1364
2354
|
...districtsData,
|
|
1365
2355
|
[districtId]: {
|
|
1366
2356
|
...district,
|
|
1367
|
-
schools: district.schools.filter(schoolId => schoolId !== id)
|
|
2357
|
+
schools: district.schools.filter((schoolId) => schoolId !== id)
|
|
1368
2358
|
}
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
deleteDistrict = (id) => {
|
|
1374
|
-
const { districtsData, schoolsData } = this.state
|
|
1375
|
-
const { [id]: district, ...restDistricts } = districtsData
|
|
2359
|
+
})
|
|
2360
|
+
}
|
|
1376
2361
|
|
|
1377
|
-
const
|
|
2362
|
+
const deleteDistrict = (id) => {
|
|
2363
|
+
const { [id]: district, ...restDistricts } = districtsData
|
|
1378
2364
|
|
|
1379
|
-
|
|
1380
|
-
if (school.districtId !== id) {
|
|
1381
|
-
filteredSchools[schoolId] = school
|
|
1382
|
-
}
|
|
1383
|
-
})
|
|
2365
|
+
const filteredSchools = {}
|
|
1384
2366
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
2367
|
+
Object.entries(schoolsData).forEach(([schoolId, school]) => {
|
|
2368
|
+
if (school.districtId !== id) {
|
|
2369
|
+
filteredSchools[schoolId] = school
|
|
2370
|
+
}
|
|
2371
|
+
})
|
|
1390
2372
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
const schools = Object.entries(schoolsData)
|
|
2373
|
+
setDistrictsData(restDistricts)
|
|
2374
|
+
setSchoolsData(filteredSchools)
|
|
2375
|
+
}
|
|
1395
2376
|
|
|
1396
2377
|
return (
|
|
1397
|
-
<Drilldown
|
|
1398
|
-
rootPageId="root"
|
|
1399
|
-
width="18.5rem"
|
|
1400
|
-
maxHeight="26.5rem"
|
|
1401
|
-
>
|
|
2378
|
+
<Drilldown rootPageId="root" width="18.5rem" maxHeight="26.5rem">
|
|
1402
2379
|
<Drilldown.Page
|
|
1403
2380
|
id="root"
|
|
1404
2381
|
renderTitle="Districts"
|
|
1405
|
-
renderActionLabel={
|
|
2382
|
+
renderActionLabel={renderAddAction('District')}
|
|
1406
2383
|
onHeaderActionClicked={() => {
|
|
1407
|
-
|
|
2384
|
+
addDistrict()
|
|
1408
2385
|
}}
|
|
1409
2386
|
>
|
|
1410
2387
|
{districts.map(([id, district]) => {
|
|
@@ -1421,32 +2398,32 @@ class EditableStructureExample extends React.Component {
|
|
|
1421
2398
|
})}
|
|
1422
2399
|
</Drilldown.Page>
|
|
1423
2400
|
|
|
1424
|
-
{districts.map(([districtId
|
|
2401
|
+
{districts.map(([districtId, district]) => {
|
|
1425
2402
|
const { label, schools } = district
|
|
1426
2403
|
return (
|
|
1427
2404
|
<Drilldown.Page
|
|
1428
2405
|
key={`districtPage__${districtId}`}
|
|
1429
2406
|
id={districtId}
|
|
1430
2407
|
renderTitle={label}
|
|
1431
|
-
renderActionLabel={
|
|
2408
|
+
renderActionLabel={renderAddAction('School')}
|
|
1432
2409
|
onHeaderActionClicked={() => {
|
|
1433
|
-
|
|
2410
|
+
addSchool(districtId)
|
|
1434
2411
|
}}
|
|
1435
2412
|
>
|
|
1436
2413
|
{schools.map((id) => {
|
|
1437
|
-
const { label, isSelected } =
|
|
2414
|
+
const { label, isSelected } = schoolsData[id]
|
|
1438
2415
|
return (
|
|
1439
2416
|
<Drilldown.Option
|
|
1440
2417
|
key={`schoolOption__${districtId}--${id}`}
|
|
1441
2418
|
id={id}
|
|
1442
2419
|
subPageId={id}
|
|
1443
|
-
renderBeforeLabel={
|
|
2420
|
+
renderBeforeLabel={renderSelectedIcon(isSelected)}
|
|
1444
2421
|
>
|
|
1445
2422
|
{label}
|
|
1446
2423
|
</Drilldown.Option>
|
|
1447
2424
|
)
|
|
1448
2425
|
})}
|
|
1449
|
-
{
|
|
2426
|
+
{renderDeleteOption('district', label, districtId)}
|
|
1450
2427
|
</Drilldown.Page>
|
|
1451
2428
|
)
|
|
1452
2429
|
})}
|
|
@@ -1461,24 +2438,23 @@ class EditableStructureExample extends React.Component {
|
|
|
1461
2438
|
>
|
|
1462
2439
|
<Drilldown.Option
|
|
1463
2440
|
id="toggleSchool"
|
|
1464
|
-
renderBeforeLabel={
|
|
2441
|
+
renderBeforeLabel={renderSelectedIcon(isSelected)}
|
|
1465
2442
|
onOptionClick={() => {
|
|
1466
|
-
|
|
2443
|
+
toggleSelectedSchool(id, school)
|
|
1467
2444
|
}}
|
|
1468
2445
|
>
|
|
1469
2446
|
{isSelected ? 'Deselect' : 'Select'} {label}
|
|
1470
2447
|
</Drilldown.Option>
|
|
1471
|
-
{
|
|
2448
|
+
{renderDeleteOption('school', label, id)}
|
|
1472
2449
|
</Drilldown.Page>
|
|
1473
2450
|
)
|
|
1474
2451
|
})}
|
|
1475
2452
|
</Drilldown>
|
|
1476
2453
|
)
|
|
1477
2454
|
}
|
|
1478
|
-
}
|
|
1479
2455
|
|
|
1480
|
-
render(<EditableStructureExample />)
|
|
1481
|
-
```
|
|
2456
|
+
render(<EditableStructureExample />)
|
|
2457
|
+
```
|
|
1482
2458
|
|
|
1483
2459
|
### Guidelines
|
|
1484
2460
|
|