-
Notifications
You must be signed in to change notification settings - Fork 2
/
SelectableContent.tsx
122 lines (113 loc) · 3.69 KB
/
SelectableContent.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module SelectableContent
*/
import "./SelectableContent.scss";
import * as React from "react";
import type { SelectOption } from "@itwin/itwinui-react";
import { Select } from "@itwin/itwinui-react";
/**
* A definition for content displayed in [[ControlledSelectableContent]] and
* [[SelectableContent]] components.
* @public
*/
export interface SelectableContentDefinition {
id: string;
label: string;
render: () => React.ReactNode;
}
/**
* [[ControlledSelectableContent]] component properties
* @public
*/
export interface ControlledSelectableContentProps {
selectedContentId: string;
onSelectedContentIdChanged?: (contentId: string) => void;
children: SelectableContentDefinition[];
selectAriaLabel?: string;
disabled?: boolean;
}
/**
* A fully-controlled component that accepts a list of child components with ids and labels and
* renders a select box at the top, allowing to choose which of the provided child components
* should be rendered at the bottom.
* @public
*/
export function ControlledSelectableContent(
props: ControlledSelectableContentProps
) {
const { onSelectedContentIdChanged, disabled } = props;
const onContentIdSelected = React.useCallback(
(newValue: string): void => {
onSelectedContentIdChanged && onSelectedContentIdChanged(newValue);
},
[onSelectedContentIdChanged]
);
const selectedContent =
props.children.find(
(contentDef) => contentDef.id === props.selectedContentId
) ?? props.children[0];
const options = React.useMemo(() => {
return props.children.map((componentDef) => ({
label: componentDef.label,
value: componentDef.id,
})) as SelectOption<string>[];
}, [props.children]);
return (
<div className="components-selectable-content">
<div className="components-selectable-content-header">
{options.length > 0 && (
<Select
onChange={onContentIdSelected}
size="small"
className="components-selectable-content-selector"
aria-label={props.selectAriaLabel}
value={selectedContent.id}
options={options}
disabled={disabled}
/>
)}
</div>
<div className="components-selectable-content-wrapper">
{selectedContent?.render()}
</div>
</div>
);
}
/**
* [[SelectableContent]] component properties
* @public
*/
export interface SelectableContentProps {
defaultSelectedContentId: string;
children: SelectableContentDefinition[];
selectAriaLabel?: string;
disabled?: boolean;
}
/**
* An uncontrolled component that accepts a list of child components with ids and labels and
* renders a select box at the top, allowing to choose which of the provided child components
* should be rendered at the bottom.
* @public
*/
export function SelectableContent(props: SelectableContentProps) {
const [selectedContentId, setSelectedContentId] = React.useState(
props.defaultSelectedContentId
);
const onSelectedContentIdChanged = React.useCallback((id: string) => {
setSelectedContentId(id);
}, []);
return (
<ControlledSelectableContent
selectedContentId={selectedContentId}
onSelectedContentIdChanged={onSelectedContentIdChanged}
selectAriaLabel={props.selectAriaLabel}
disabled={props.disabled}
>
{props.children}
</ControlledSelectableContent>
);
}