dce-reactkit 3.11.4 → 3.12.0

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.
@@ -14,6 +14,7 @@ type Props = {
14
14
  */
15
15
  onChanged: (updatedItems: PickableItem[]) => void;
16
16
  noBottomMargin?: boolean;
17
+ hideSelectAllOrNoneButtons?: boolean;
17
18
  };
18
19
  declare const ItemPicker: React.FC<Props>;
19
20
  export default ItemPicker;
@@ -6,6 +6,7 @@ import React from 'react';
6
6
  type Props = {
7
7
  title: React.ReactNode;
8
8
  children: React.ReactNode;
9
+ topRightChildren?: React.ReactNode;
9
10
  noBottomMargin?: boolean;
10
11
  noBottomPadding?: boolean;
11
12
  };
package/dist/index.d.ts CHANGED
@@ -227,6 +227,7 @@ declare const Modal: React$1.FC<ModalProps>;
227
227
  type Props$j = {
228
228
  title: React$1.ReactNode;
229
229
  children: React$1.ReactNode;
230
+ topRightChildren?: React$1.ReactNode;
230
231
  noBottomMargin?: boolean;
231
232
  noBottomPadding?: boolean;
232
233
  };
@@ -412,6 +413,7 @@ type Props$9 = {
412
413
  */
413
414
  onChanged: (updatedItems: PickableItem[]) => void;
414
415
  noBottomMargin?: boolean;
416
+ hideSelectAllOrNoneButtons?: boolean;
415
417
  };
416
418
  declare const ItemPicker: React$1.FC<Props$9>;
417
419
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.11.4",
3
+ "version": "3.12.0",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -33,6 +33,8 @@ type Props = {
33
33
  onChanged: (updatedItems: PickableItem[]) => void,
34
34
  // If true, don't add margin to bottom of item picker
35
35
  noBottomMargin?: boolean,
36
+ // If true, hide select all or none buttons
37
+ hideSelectAllOrNoneButtons?: boolean,
36
38
  };
37
39
 
38
40
  /*------------------------------------------------------------------------*/
@@ -52,12 +54,61 @@ const ItemPicker: React.FC<Props> = (props) => {
52
54
  items,
53
55
  onChanged,
54
56
  noBottomMargin,
57
+ hideSelectAllOrNoneButtons,
55
58
  } = props;
56
59
 
57
60
  /*------------------------------------------------------------------------*/
58
61
  /* ------------------------- Component Functions ------------------------ */
59
62
  /*------------------------------------------------------------------------*/
60
63
 
64
+ /**
65
+ * Updates the checked state of an item (including all children)
66
+ * @author Yuen Ler Chow
67
+ * @param item the item to update
68
+ * @param checked the new checked state
69
+ * @returns the updated item
70
+ */
71
+ const updateItemChecked = (
72
+ item: PickableItem,
73
+ checked: boolean,
74
+ ): PickableItem => {
75
+ if (item.isGroup) {
76
+ return {
77
+ ...item,
78
+ children: item.children.map((child) => {
79
+ return updateItemChecked(child, checked);
80
+ }),
81
+ };
82
+ }
83
+ return { ...item, checked };
84
+ };
85
+
86
+ /**
87
+ * Selects all items in the list
88
+ * @author Yuen Ler Chow
89
+ */
90
+ const handleSelectAll = () => {
91
+ const updatedItems = items.map(
92
+ (item) => {
93
+ return updateItemChecked(item, true);
94
+ },
95
+ );
96
+ onChanged(updatedItems);
97
+ };
98
+
99
+ /**
100
+ * Deselects all items in the list
101
+ * @author Yuen Ler Chow
102
+ */
103
+ const handleDeselectAll = () => {
104
+ const updatedItems = items.map(
105
+ (item) => {
106
+ return updateItemChecked(item, false);
107
+ },
108
+ );
109
+ onChanged(updatedItems);
110
+ };
111
+
61
112
  /*------------------------------------------------------------------------*/
62
113
  /* ------------------------------- Render ------------------------------- */
63
114
  /*------------------------------------------------------------------------*/
@@ -66,10 +117,55 @@ const ItemPicker: React.FC<Props> = (props) => {
66
117
  /* --------------- Main UI -------------- */
67
118
  /*----------------------------------------*/
68
119
 
120
+ // Select all/none
121
+ const selectAllOrNone = (
122
+ !hideSelectAllOrNoneButtons
123
+ ? (
124
+ <div
125
+ className="d-flex h-100 align-items-end flex-row"
126
+ >
127
+ <div className="d-flex justify-content-end">
128
+ {/* Label */}
129
+ <div
130
+ className="me-2"
131
+ style={{ fontSize: '1.2rem' }}
132
+ >
133
+ Select
134
+ </div>
135
+ {/* Buttons */}
136
+ <div className="btn-group" role="group">
137
+ {/* All */}
138
+ <button
139
+ type="button"
140
+ style={{ borderRight: '0.1rem solid white' }}
141
+ aria-label="Select all contexts"
142
+ className="btn btn-secondary py-0"
143
+ onClick={handleSelectAll}
144
+ >
145
+ All
146
+ </button>
147
+ {/* None */}
148
+ <button
149
+ type="button"
150
+ aria-label="Deselect all contexts"
151
+ className="btn btn-secondary py-0"
152
+ onClick={handleDeselectAll}
153
+ >
154
+ None
155
+ </button>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ )
160
+ : undefined
161
+ );
162
+
163
+ // Main UI
69
164
  return (
70
165
  <TabBox
71
166
  title={title}
72
167
  noBottomMargin={noBottomMargin}
168
+ topRightChildren={selectAllOrNone}
73
169
  >
74
170
  <div style={{ overflowX: 'auto' }}>
75
171
  <NestableItemList
@@ -15,6 +15,8 @@ type Props = {
15
15
  title: React.ReactNode,
16
16
  // Children/contents inside the box
17
17
  children: React.ReactNode,
18
+ // Children to display on the top right of the tab box
19
+ topRightChildren?: React.ReactNode,
18
20
  // If true, don't add margin below the tab box
19
21
  noBottomMargin?: boolean,
20
22
  // If true, don't add padding to bottom of tab box
@@ -49,7 +51,9 @@ const style = `
49
51
 
50
52
  /* Container for Title */
51
53
  .TabBox-title-container {
52
- /* Place on Left */
54
+ display: flex;
55
+ justify-content: space-between;
56
+ align-items: center;
53
57
  position: relative;
54
58
  left: 0;
55
59
  text-align: left;
@@ -82,6 +86,19 @@ const style = `
82
86
  background: #fdfdfd;
83
87
  }
84
88
 
89
+ .TabBox-title-right-container {
90
+ display: flex;
91
+ flex-direction: row;
92
+ align-items: bottom;
93
+ height: 2.4rem;
94
+ overflow: visible;
95
+ }
96
+
97
+ .TabBox-title-right-contents {
98
+ margin-right: 0.5rem;
99
+ margin-bottom: 0.2rem;
100
+ }
101
+
85
102
  /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
86
103
  .TabBox-children {
87
104
  position: relative;
@@ -98,11 +115,10 @@ const TabBox: React.FC<Props> = (props) => {
98
115
  /* -------------------------------- Setup ------------------------------- */
99
116
  /*------------------------------------------------------------------------*/
100
117
 
101
- /* -------------- Props ------------- */
102
-
103
118
  const {
104
119
  title,
105
120
  children,
121
+ topRightChildren,
106
122
  noBottomPadding,
107
123
  noBottomMargin,
108
124
  } = props;
@@ -111,21 +127,23 @@ const TabBox: React.FC<Props> = (props) => {
111
127
  /* ------------------------------- Render ------------------------------- */
112
128
  /*------------------------------------------------------------------------*/
113
129
 
114
- /*----------------------------------------*/
115
- /* --------------- Main UI -------------- */
116
- /*----------------------------------------*/
117
-
118
- // Full UI
119
130
  return (
120
131
  <div className={`TabBox-container ${noBottomMargin ? '' : 'mb-2'}`}>
121
132
  {/* Style */}
122
133
  <style>{style}</style>
123
134
 
124
- {/* Title */}
135
+ {/* Title Row with Left and Right sections */}
125
136
  <div className="TabBox-title-container">
126
137
  <div className="TabBox-title">
127
138
  {title}
128
139
  </div>
140
+ {topRightChildren && (
141
+ <div className="TabBox-title-right-container">
142
+ <div className="TabBox-title-right-contents">
143
+ {topRightChildren}
144
+ </div>
145
+ </div>
146
+ )}
129
147
  </div>
130
148
 
131
149
  {/* Contents */}