dce-reactkit 3.1.2 → 3.1.5

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/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ import React from 'react';
7
7
  * @author Gabe Abrams
8
8
  */
9
9
 
10
- declare type Props$c = {
10
+ declare type Props$d = {
11
11
  children: React.ReactNode;
12
12
  sendRequest: SendRequestFunction;
13
13
  dark?: boolean;
@@ -52,7 +52,7 @@ declare const confirm: (title: string, text: string) => Promise<boolean>;
52
52
  * @param [errorTitle] title of the error box
53
53
  */
54
54
  declare const showFatalError: (error: any, errorTitle?: string) => undefined;
55
- declare const AppWrapper: React.FC<Props$c>;
55
+ declare const AppWrapper: React.FC<Props$d>;
56
56
 
57
57
  /**
58
58
  * Loading spinner/indicator
@@ -65,12 +65,12 @@ declare const LoadingSpinner: () => JSX.Element;
65
65
  * @author Gabe Abrams
66
66
  */
67
67
 
68
- declare type Props$b = {
68
+ declare type Props$c = {
69
69
  error: any;
70
70
  title?: string;
71
71
  onClose?: () => void;
72
72
  };
73
- declare const ErrorBox: React.FC<Props$b>;
73
+ declare const ErrorBox: React.FC<Props$c>;
74
74
 
75
75
  /**
76
76
  * Bootstrap variants
@@ -136,7 +136,7 @@ declare enum ModalType {
136
136
  * @author Gabe Abrams
137
137
  */
138
138
 
139
- declare type Props$a = {
139
+ declare type Props$b = {
140
140
  type?: ModalType;
141
141
  size?: ModalSize;
142
142
  title?: React.ReactNode;
@@ -165,27 +165,27 @@ declare type Props$a = {
165
165
  confirmVariant?: Variant;
166
166
  onTopOfOtherModals?: boolean;
167
167
  };
168
- declare const Modal: React.FC<Props$a>;
168
+ declare const Modal: React.FC<Props$b>;
169
169
 
170
170
  /**
171
171
  * A box with a tab on the top that holds buttons and other content
172
172
  * @author Gabe Abrams
173
173
  */
174
174
 
175
- declare type Props$9 = {
175
+ declare type Props$a = {
176
176
  title: React.ReactNode;
177
177
  children: React.ReactNode;
178
178
  noBottomMargin?: boolean;
179
179
  noBottomPadding?: boolean;
180
180
  };
181
- declare const TabBox: React.FC<Props$9>;
181
+ declare const TabBox: React.FC<Props$a>;
182
182
 
183
183
  /**
184
184
  * A radio selection button
185
185
  * @author Gabe Abrams
186
186
  */
187
187
 
188
- declare type Props$8 = {
188
+ declare type Props$9 = {
189
189
  text: string;
190
190
  onSelected: () => void;
191
191
  ariaLabel: string;
@@ -197,46 +197,47 @@ declare type Props$8 = {
197
197
  unselectedVariant?: Variant;
198
198
  small?: boolean;
199
199
  };
200
- declare const RadioButton: React.FC<Props$8>;
200
+ declare const RadioButton: React.FC<Props$9>;
201
201
 
202
202
  /**
203
203
  * A checkbox button
204
204
  * @author Gabe Abrams
205
205
  */
206
206
 
207
- declare type Props$7 = {
207
+ declare type Props$8 = {
208
208
  text: string;
209
209
  onChanged: (checked: boolean) => void;
210
210
  ariaLabel: string;
211
211
  title?: string;
212
212
  checked?: boolean;
213
213
  id?: string;
214
+ className?: string;
214
215
  noMarginOnRight?: boolean;
215
216
  checkedVariant?: Variant;
216
217
  uncheckedVariant?: Variant;
217
218
  small?: boolean;
218
219
  dashed?: boolean;
219
220
  };
220
- declare const CheckboxButton: React.FC<Props$7>;
221
+ declare const CheckboxButton: React.FC<Props$8>;
221
222
 
222
223
  /**
223
224
  * Input group with a title and space for buttons
224
225
  * @author Gabe Abrams
225
226
  */
226
227
 
227
- declare type Props$6 = {
228
+ declare type Props$7 = {
228
229
  label: string;
229
230
  minLabelWidth?: string;
230
231
  children: React.ReactNode;
231
232
  };
232
- declare const ButtonInputGroup: React.FC<Props$6>;
233
+ declare const ButtonInputGroup: React.FC<Props$7>;
233
234
 
234
235
  /**
235
236
  * A very simple, lightweight date chooser
236
237
  * @author Gabe Abrams
237
238
  */
238
239
 
239
- declare type Props$5 = {
240
+ declare type Props$6 = {
240
241
  ariaLabel: string;
241
242
  name: string;
242
243
  month: number;
@@ -251,60 +252,60 @@ declare type Props$5 = {
251
252
  onChange: (month: number, day: number, year: number) => void;
252
253
  numMonthsToShow?: number;
253
254
  };
254
- declare const SimpleDateChooser: React.FC<Props$5>;
255
+ declare const SimpleDateChooser: React.FC<Props$6>;
255
256
 
256
257
  /**
257
258
  * Drawer container
258
259
  * @author Gabe Abrams
259
260
  */
260
261
 
261
- declare type Props$4 = {
262
+ declare type Props$5 = {
262
263
  children: React.ReactNode;
263
264
  };
264
- declare const Drawer: React.FC<Props$4>;
265
+ declare const Drawer: React.FC<Props$5>;
265
266
 
266
267
  /**
267
268
  * Success checkmark that pops into view
268
269
  * @author Gabe Abrams
269
270
  */
270
271
 
271
- declare type Props$3 = {
272
+ declare type Props$4 = {
272
273
  sizeRem?: number;
273
274
  circleVariant?: string;
274
275
  checkVariant?: string;
275
276
  };
276
- declare const PopSuccessMark: React.FC<Props$3>;
277
+ declare const PopSuccessMark: React.FC<Props$4>;
277
278
 
278
279
  /**
279
280
  * Failure x mark that pops into view
280
281
  * @author Gabe Abrams
281
282
  */
282
283
 
283
- declare type Props$2 = {
284
+ declare type Props$3 = {
284
285
  sizeRem?: number;
285
286
  circleVariant?: string;
286
287
  xVariant?: string;
287
288
  };
288
- declare const PopFailureMark: React.FC<Props$2>;
289
+ declare const PopFailureMark: React.FC<Props$3>;
289
290
 
290
291
  /**
291
292
  * Failure pending that pops into view
292
293
  * @author Gabe Abrams
293
294
  */
294
295
 
295
- declare type Props$1 = {
296
+ declare type Props$2 = {
296
297
  sizeRem?: number;
297
298
  circleVariant?: string;
298
299
  hourglassVariant?: string;
299
300
  };
300
- declare const PopPendingMark: React.FC<Props$1>;
301
+ declare const PopPendingMark: React.FC<Props$2>;
301
302
 
302
303
  /**
303
304
  * Copiable text box
304
305
  * @author Gabe Abrams
305
306
  */
306
307
 
307
- declare type Props = {
308
+ declare type Props$1 = {
308
309
  text: string;
309
310
  maxTextWidthRem?: number;
310
311
  label: string;
@@ -316,7 +317,40 @@ declare type Props = {
316
317
  textAreaId?: string;
317
318
  copyButtonId?: string;
318
319
  };
319
- declare const CopiableBox: React.FC<Props>;
320
+ declare const CopiableBox: React.FC<Props$1>;
321
+
322
+ /**
323
+ * An item that can be chosen (for use within ItemPicker)
324
+ * @author Gabe Abrams
325
+ */
326
+ declare type PickableItem = ({
327
+ id: number | string;
328
+ name: string;
329
+ link?: string;
330
+ } & ({
331
+ isGroup: false;
332
+ checked: boolean;
333
+ } | {
334
+ isGroup: true;
335
+ children: PickableItem[];
336
+ }));
337
+
338
+ /**
339
+ * Reusable nested item picker
340
+ * @author Yuen Ler Chow
341
+ */
342
+
343
+ declare type Props = {
344
+ title: string;
345
+ items: PickableItem[];
346
+ /**
347
+ * Handler to call when item selection is changed
348
+ * @param updatedItems an updated copy of the list of items with checked
349
+ * values updated
350
+ */
351
+ onChanged: (updatedItems: PickableItem[]) => void;
352
+ };
353
+ declare const ItemPicker: React.FC<Props>;
320
354
 
321
355
  /**
322
356
  * An error with a code
@@ -613,6 +647,14 @@ declare const getPartOfDay: () => string;
613
647
  */
614
648
  declare const stringsToHumanReadableList: (items: string[]) => string;
615
649
 
650
+ /**
651
+ * Given a string, only keep the letters inside it
652
+ * @author Gabe Abrams
653
+ * @param str the string to parse
654
+ * @returns only the letters inside of the string
655
+ */
656
+ declare const onlyKeepLetters: (str: string) => string;
657
+
616
658
  /**
617
659
  * List of error codes built into the react kit
618
660
  * @author Gabe Abrams
@@ -644,4 +686,4 @@ declare enum DayOfWeek {
644
686
  Sunday = "u"
645
687
  }
646
688
 
647
- export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, LoadingSpinner, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initServer, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
689
+ export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initServer, onlyKeepLetters, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.1.2",
3
+ "version": "3.1.5",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -31,6 +31,8 @@ type Props = {
31
31
  checked?: boolean,
32
32
  // The id of the button
33
33
  id?: string,
34
+ // Custom class name of the button
35
+ className?: string,
34
36
  // If true, no margin on right
35
37
  noMarginOnRight?: boolean,
36
38
  // Variant for when checkbox is checked
@@ -61,6 +63,7 @@ const CheckboxButton: React.FC<Props> = (props) => {
61
63
  title,
62
64
  checked,
63
65
  id,
66
+ className,
64
67
  noMarginOnRight,
65
68
  checkedVariant = Variant.Secondary,
66
69
  uncheckedVariant = Variant.Light,
@@ -94,7 +97,7 @@ const CheckboxButton: React.FC<Props> = (props) => {
94
97
  type="button"
95
98
  id={id}
96
99
  title={title}
97
- className={`CheckboxButton-status-${checked ? 'checked' : 'unchecked'} ${dashed ? 'CheckboxButton-dashed ' : ''}btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`}
100
+ className={`CheckboxButton-status-${checked ? 'checked' : 'unchecked'} ${dashed ? 'CheckboxButton-dashed ' : ''}btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'} ${className ?? ''}`}
98
101
  aria-label={`${ariaLabel}${checked ? ': currently checked' : ''}`}
99
102
  onClick={() => {
100
103
  onChanged(!checked);
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Reusable nested item picker
3
+ * @author Yuen Ler Chow
4
+ */
5
+
6
+ // Import React
7
+ import React, { useReducer } from 'react';
8
+
9
+ // Import FontAwesome
10
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
11
+ import {
12
+ faChevronRight,
13
+ faChevronDown,
14
+ } from '@fortawesome/free-solid-svg-icons';
15
+
16
+ // Import shared components
17
+ import CheckboxButton from '../CheckboxButton';
18
+ import Variant from '../../types/Variant';
19
+
20
+ // Import types
21
+ import PickableItem from './types/PickableItem';
22
+
23
+ /*------------------------------------------------------------------------*/
24
+ /* Types */
25
+ /*------------------------------------------------------------------------*/
26
+
27
+ // Props definition
28
+ type Props = {
29
+ // List of items to show
30
+ items: PickableItem[],
31
+ /**
32
+ * Handler to call when item selection is changed
33
+ * @param updatedItems an updated copy of the list of items with checked
34
+ * values updated
35
+ */
36
+ onChanged: (updatedItems: PickableItem[]) => void,
37
+ };
38
+
39
+ /*------------------------------------------------------------------------*/
40
+ /* State */
41
+ /*------------------------------------------------------------------------*/
42
+
43
+ /* -------- State Definition -------- */
44
+
45
+ type State = {
46
+ // True if children are being shown
47
+ isShowingItems: boolean,
48
+ };
49
+
50
+ /* ------------- Actions ------------ */
51
+
52
+ // Types of actions
53
+ enum ActionType {
54
+ // Toggle whether the children are being shown
55
+ ToggleItems = 'toggle-items',
56
+ }
57
+
58
+ // Action definitions
59
+ type Action = (
60
+ | {
61
+ // Action type
62
+ type: ActionType.ToggleItems,
63
+ }
64
+ );
65
+
66
+ /**
67
+ * Reducer that executes actions
68
+ * @author Yuen Ler Chow
69
+ * @param state current state
70
+ * @param action action to execute
71
+ */
72
+ const reducer = (state: State, action: Action): State => {
73
+ switch (action.type) {
74
+ case ActionType.ToggleItems: {
75
+ return { isShowingItems: !state.isShowingItems };
76
+ }
77
+ default: {
78
+ return state;
79
+ }
80
+ }
81
+ };
82
+
83
+ /*------------------------------------------------------------------------*/
84
+ /* Component */
85
+ /*------------------------------------------------------------------------*/
86
+
87
+ const NestableItemList: React.FC<Props> = (props) => {
88
+ /*------------------------------------------------------------------------*/
89
+ /* Setup */
90
+ /*------------------------------------------------------------------------*/
91
+
92
+ /* -------------- Props ------------- */
93
+
94
+ // Destructure all props
95
+ const {
96
+ items,
97
+ onChanged,
98
+ } = props;
99
+
100
+ /* -------------- State ------------- */
101
+
102
+ // Initial state
103
+ const initialState: State = {
104
+ isShowingItems: true,
105
+ };
106
+
107
+ // Initialize state
108
+ const [state, dispatch] = useReducer(reducer, initialState);
109
+
110
+ // Destructure common state
111
+ const {
112
+ isShowingItems,
113
+ } = state;
114
+
115
+ /*------------------------------------------------------------------------*/
116
+ /* Component Functions */
117
+ /*------------------------------------------------------------------------*/
118
+
119
+ /**
120
+ * Checks if all items in a list are checked
121
+ * @author Yuen Ler Chow
122
+ * @param pickableItems a list of items
123
+ * @returns true if all items are checked. If any item is a group,
124
+ * we recursively check its children
125
+ */
126
+ const allChecked = (pickableItems: PickableItem[]): boolean => {
127
+ return pickableItems.every((item: PickableItem) => {
128
+ if (item.isGroup) {
129
+ return allChecked(item.children);
130
+ }
131
+ return item.checked;
132
+ });
133
+ };
134
+
135
+ /**
136
+ * Checks if none of the items in a list are checked
137
+ * @author Yuen Ler Chow
138
+ * @param pickableItems a list of items
139
+ * @returns true if all items are unchecked. If any item is a group, we
140
+ * recursively check its children
141
+ */
142
+ const noneChecked = (pickableItems: PickableItem[]): boolean => {
143
+ return pickableItems.every((item: PickableItem) => {
144
+ if (item.isGroup) {
145
+ return noneChecked(item.children);
146
+ }
147
+ return !item.checked;
148
+ });
149
+ };
150
+
151
+ /**
152
+ * Change whether specific items are checked
153
+ * @author Yuen Ler Chow
154
+ * @param id the id of the item we want to check or uncheck OR true if
155
+ * we are checking/unchecking all items, independent of their id
156
+ * @param checked if true, the item will be checked.
157
+ * @param pickableItems the list of items we iterate through to find the item
158
+ * we want to check/uncheck
159
+ * @returns a list of items with the item now checked/unchecked.
160
+ * If it is a group, its children will also become checked/unchecked
161
+ */
162
+ const changeChecked = (id: string | number | true, checked: boolean, pickableItems: PickableItem[]): PickableItem[] => {
163
+ const updatedItems: PickableItem[] = pickableItems.map((item: PickableItem) => {
164
+ if (item.id === id || id === true) {
165
+ if (item.isGroup) {
166
+ return {
167
+ ...item,
168
+ children: changeChecked(true, checked, item.children),
169
+ };
170
+ }
171
+ return {
172
+ ...item,
173
+ checked,
174
+ };
175
+ }
176
+ if (item.isGroup) {
177
+ return {
178
+ ...item,
179
+ children: changeChecked(id, checked, item.children),
180
+ };
181
+ }
182
+ return item;
183
+ });
184
+ return updatedItems;
185
+ };
186
+
187
+ /**
188
+ * Within a tree of items, swap in the list of updated items into the spot
189
+ * indicated by the id
190
+ * @author Yuen Ler Chow
191
+ * @param id the id of the item group we want to change
192
+ * @param updatedItems the list of items we want to change the items in the group to
193
+ * @returns a list of all items containing the updated items
194
+ */
195
+ const changeItems = (id: string | number, updatedItems: PickableItem[]): PickableItem[] => {
196
+ return items.map((item: PickableItem) => {
197
+ if (item.id === id) {
198
+ return {
199
+ ...item,
200
+ children: updatedItems,
201
+ };
202
+ }
203
+ return item;
204
+ });
205
+ };
206
+
207
+ /*------------------------------------------------------------------------*/
208
+ /* Render */
209
+ /*------------------------------------------------------------------------*/
210
+
211
+ return (
212
+ <div>
213
+ {
214
+ items.map((item) => {
215
+ return (
216
+ <div key={item.id}>
217
+ {/* Dropdown Button */}
218
+ <span
219
+ className="NestableItemList-dropdown-button-container d-inline-block"
220
+ style={{
221
+ minWidth: '2rem',
222
+ }}
223
+ >
224
+ {item.isGroup && (
225
+ <button
226
+ className={`NestableItemList-dropdown-button NestableItemList-dropdown-button-${item.id}`}
227
+ style={{
228
+ border: 0,
229
+ backgroundColor: 'transparent',
230
+ }}
231
+ type="button"
232
+ onClick={() => {
233
+ dispatch({
234
+ type: ActionType.ToggleItems,
235
+ });
236
+ }}
237
+ aria-label={`${isShowingItems ? 'Hide' : 'Show'} items in ${item.name}`}
238
+ >
239
+ <FontAwesomeIcon
240
+ icon={isShowingItems ? faChevronDown : faChevronRight}
241
+ />
242
+ </button>
243
+ )}
244
+ </span>
245
+
246
+ {/* Checkbox and Text */}
247
+ <CheckboxButton
248
+ className={`NestableItemList-CheckboxButton-${item.id}`}
249
+ text={item.name}
250
+ checked={item.isGroup ? allChecked(item.children) : item.checked}
251
+ dashed={item.isGroup ? !noneChecked(item.children) : false}
252
+ onChanged={(checked) => {
253
+ onChanged(changeChecked(item.id, checked, items));
254
+ }}
255
+ ariaLabel={`Select ${item.name}`}
256
+ checkedVariant={Variant.Light}
257
+ />
258
+
259
+ {/* Children */}
260
+ {item.isGroup && isShowingItems && (
261
+ <div
262
+ className="NestableItemList-children-container"
263
+ style={{
264
+ paddingLeft: '2.2rem',
265
+ }}
266
+ >
267
+ <NestableItemList
268
+ items={item.children}
269
+ onChanged={(updatedItems) => {
270
+ onChanged(changeItems(item.id, updatedItems));
271
+ }}
272
+ />
273
+ </div>
274
+ )}
275
+ </div>
276
+ );
277
+ })
278
+ }
279
+ </div>
280
+ );
281
+ };
282
+
283
+ /*------------------------------------------------------------------------*/
284
+ /* Wrap Up */
285
+ /*------------------------------------------------------------------------*/
286
+
287
+ // Export component
288
+ export default NestableItemList;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Reusable nested item picker
3
+ * @author Yuen Ler Chow
4
+ */
5
+
6
+ // Import React
7
+ import React from 'react';
8
+
9
+ // Import shared components
10
+ import TabBox from '../TabBox';
11
+
12
+ // Import types
13
+ import PickableItem from './types/PickableItem';
14
+
15
+ // Import sub-components
16
+ import NestableItemList from './NestableItemList';
17
+
18
+ /*------------------------------------------------------------------------*/
19
+ /* Types */
20
+ /*------------------------------------------------------------------------*/
21
+
22
+ // Props definition
23
+ type Props = {
24
+ // Title of the picker
25
+ title: string,
26
+ // List of items to show
27
+ items: PickableItem[],
28
+ /**
29
+ * Handler to call when item selection is changed
30
+ * @param updatedItems an updated copy of the list of items with checked
31
+ * values updated
32
+ */
33
+ onChanged: (updatedItems: PickableItem[]) => void,
34
+ };
35
+
36
+ /*------------------------------------------------------------------------*/
37
+ /* Component */
38
+ /*------------------------------------------------------------------------*/
39
+
40
+ const ItemPicker: React.FC<Props> = (props) => {
41
+ /*------------------------------------------------------------------------*/
42
+ /* Setup */
43
+ /*------------------------------------------------------------------------*/
44
+
45
+ /* -------------- Props ------------- */
46
+
47
+ // Destructure all props
48
+ const {
49
+ title,
50
+ items,
51
+ onChanged,
52
+ } = props;
53
+
54
+ /*------------------------------------------------------------------------*/
55
+ /* Component Functions */
56
+ /*------------------------------------------------------------------------*/
57
+
58
+ /*------------------------------------------------------------------------*/
59
+ /* Render */
60
+ /*------------------------------------------------------------------------*/
61
+
62
+ /*----------------------------------------*/
63
+ /* Main UI */
64
+ /*----------------------------------------*/
65
+
66
+ return (
67
+ <TabBox
68
+ title={title}
69
+ >
70
+ <div style={{ overflowX: 'scroll' }}>
71
+ <NestableItemList
72
+ items={items}
73
+ onChanged={onChanged}
74
+ />
75
+ </div>
76
+ </TabBox>
77
+ );
78
+ };
79
+
80
+ /*------------------------------------------------------------------------*/
81
+ /* Wrap Up */
82
+ /*------------------------------------------------------------------------*/
83
+
84
+ // Export component
85
+ export default ItemPicker;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * An item that can be chosen (for use within ItemPicker)
3
+ * @author Gabe Abrams
4
+ */
5
+ type PickableItem = (
6
+ {
7
+ // Unique id of the item
8
+ id: number | string,
9
+ // Name of the item (human readable)
10
+ name: string,
11
+ // Link to view the item in the browser
12
+ link?: string,
13
+ }
14
+ & (
15
+ | {
16
+ // True if this is a group of items
17
+ isGroup: false,
18
+ // True if item is checked
19
+ checked: boolean,
20
+ }
21
+ | {
22
+ // True if this is a group of items
23
+ isGroup: true,
24
+ // List of sub-items
25
+ children: PickableItem[],
26
+ }
27
+ )
28
+ );
29
+
30
+ export default PickableItem;