@performant-software/semantic-components 1.0.5 → 1.0.6-beta.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@performant-software/semantic-components",
3
- "version": "1.0.5",
3
+ "version": "1.0.6-beta.0",
4
4
  "description": "A package of shared components based on the Semantic UI Framework.",
5
5
  "license": "MIT",
6
6
  "main": "./build/index.js",
@@ -12,7 +12,7 @@
12
12
  "build": "webpack --mode production && flow-copy-source -v src types"
13
13
  },
14
14
  "dependencies": {
15
- "@performant-software/shared-components": "^1.0.5",
15
+ "@performant-software/shared-components": "^1.0.6-beta.0",
16
16
  "@react-google-maps/api": "^2.8.1",
17
17
  "axios": "^0.26.1",
18
18
  "citeproc": "^2.4.62",
@@ -3,6 +3,7 @@
3
3
  import React, {
4
4
  useCallback,
5
5
  useEffect,
6
+ useMemo,
6
7
  useState,
7
8
  type ComponentType,
8
9
  type Element
@@ -12,6 +13,7 @@ import {
12
13
  Form,
13
14
  Grid,
14
15
  Icon,
16
+ Message,
15
17
  Modal,
16
18
  Table
17
19
  } from 'semantic-ui-react';
@@ -35,7 +37,7 @@ type Props = {
35
37
  modal?: {
36
38
  onSave: (item: any) => Promise<any>
37
39
  },
38
- multiple?: boolean,
40
+ multiple?: boolean | number,
39
41
  onClose: () => void,
40
42
  onLoad: (params: any) => Promise<any>,
41
43
  onSave: (items: any) => void,
@@ -48,8 +50,13 @@ type GridProps = ListProps & {
48
50
  onInit: () => void,
49
51
  onItemSelection: (item: any) => void,
50
52
  onSelect: (item: any) => void,
51
- renderHeader: (params: any) => Element<any>,
52
- renderItem: (item: any) => Element<any>,
53
+ renderHeader?: (params: any) => Element<any>,
54
+ renderItem?: (item: any) => Element<any>,
55
+ renderItems?: ({
56
+ isSelected: (item: any) => boolean,
57
+ items: any,
58
+ onSelect: () => void
59
+ }) => Element<any>,
53
60
  selectedItem: any,
54
61
  selectedItems: Array<any>
55
62
  };
@@ -113,6 +120,14 @@ const SelectizeGrid = useDataList(useList((props: GridProps) => {
113
120
  return null;
114
121
  }
115
122
 
123
+ if (props.renderItems) {
124
+ return props.renderItems({
125
+ isSelected: props.isSelected,
126
+ items: props.items,
127
+ onSelect: props.onSelect
128
+ });
129
+ }
130
+
116
131
  return (
117
132
  <Table
118
133
  basic
@@ -161,7 +176,7 @@ const SelectizeGrid = useDataList(useList((props: GridProps) => {
161
176
  />
162
177
  </Grid.Row>
163
178
  <Grid.Row>
164
- { i18n.t('Selectize.noRecords') }
179
+ { i18n.t('Selectize.messages.noRecords') }
165
180
  </Grid.Row>
166
181
  </Grid.Column>
167
182
  </Grid>
@@ -182,9 +197,27 @@ const SelectizeGrid = useDataList(useList((props: GridProps) => {
182
197
  }));
183
198
 
184
199
  const Selectize = (props: Props) => {
200
+ const [error, setError] = useState(false);
185
201
  const [selectedItem, setSelectedItem] = useState();
186
202
  const [selectedItems, setSelectedItems] = useState(props.selectedItems || []);
187
203
 
204
+ /**
205
+ * Sets the allowMultiple prop to "true" if the multiple prop is a boolean or non-zero integer.
206
+ *
207
+ * @type {boolean}
208
+ */
209
+ const allowMultiple = useMemo(() => !!props.multiple, [props.multiple]);
210
+
211
+ /**
212
+ * Sets the allowed number of items that can be selected. If the multiple prop is a boolean, this will be
213
+ * and unlimited amount.
214
+ *
215
+ * @type {boolean|number}
216
+ */
217
+ const allowedCount = useMemo(() => (
218
+ _.isNumber(props.multiple) ? props.multiple : Number.MAX_SAFE_INTEGER
219
+ ), [props.multiple]);
220
+
188
221
  /**
189
222
  * Returns true if the passed item is selected.
190
223
  *
@@ -196,13 +229,17 @@ const Selectize = (props: Props) => {
196
229
  * If the passed item is selected, deselect the item. If we're not allowing multiple select, replace the selected
197
230
  * items with the passed item. Otherwise, append the passed item to the list of selected items.
198
231
  *
232
+ * If the user has already selected the maximum number allowed, display an error message.
233
+ *
199
234
  * @type {(function(*=): void)|*}
200
235
  */
201
236
  const onSelect = useCallback((item) => {
202
237
  if (isSelected(item)) {
203
238
  setSelectedItems((prevItems) => _.filter(prevItems, (i) => i.id !== item.id));
204
- } else if (!props.multiple) {
239
+ } else if (!allowMultiple) {
205
240
  setSelectedItems([item]);
241
+ } else if (selectedItems.length >= allowedCount) {
242
+ setError(true);
206
243
  } else {
207
244
  setSelectedItems((prevItems) => [
208
245
  ...prevItems,
@@ -254,6 +291,13 @@ const Selectize = (props: Props) => {
254
291
  content={props.title}
255
292
  />
256
293
  <Modal.Content>
294
+ <Message
295
+ content={i18n.t('Selectize.messages.maxSelected.content')}
296
+ error
297
+ header={i18n.t('Selectize.messages.maxSelected.header')}
298
+ onDismiss={() => setError(false)}
299
+ visible={error}
300
+ />
257
301
  <SelectizeGrid
258
302
  {...props}
259
303
  actions={[]}
package/src/i18n/en.json CHANGED
@@ -259,7 +259,13 @@
259
259
  }
260
260
  },
261
261
  "Selectize": {
262
- "noRecords": "No matching records."
262
+ "messages": {
263
+ "maxSelected": {
264
+ "content": "You have already selected the maximum number of items allowed.",
265
+ "header": "Maximum items selected"
266
+ },
267
+ "noRecords": "No matching records."
268
+ }
263
269
  },
264
270
  "VideoFrameSelector": {
265
271
  "buttons": {
@@ -3,6 +3,7 @@
3
3
  import React, {
4
4
  useCallback,
5
5
  useEffect,
6
+ useMemo,
6
7
  useState,
7
8
  type ComponentType,
8
9
  type Element
@@ -12,6 +13,7 @@ import {
12
13
  Form,
13
14
  Grid,
14
15
  Icon,
16
+ Message,
15
17
  Modal,
16
18
  Table
17
19
  } from 'semantic-ui-react';
@@ -35,7 +37,7 @@ type Props = {
35
37
  modal?: {
36
38
  onSave: (item: any) => Promise<any>
37
39
  },
38
- multiple?: boolean,
40
+ multiple?: boolean | number,
39
41
  onClose: () => void,
40
42
  onLoad: (params: any) => Promise<any>,
41
43
  onSave: (items: any) => void,
@@ -48,8 +50,13 @@ type GridProps = ListProps & {
48
50
  onInit: () => void,
49
51
  onItemSelection: (item: any) => void,
50
52
  onSelect: (item: any) => void,
51
- renderHeader: (params: any) => Element<any>,
52
- renderItem: (item: any) => Element<any>,
53
+ renderHeader?: (params: any) => Element<any>,
54
+ renderItem?: (item: any) => Element<any>,
55
+ renderItems?: ({
56
+ isSelected: (item: any) => boolean,
57
+ items: any,
58
+ onSelect: () => void
59
+ }) => Element<any>,
53
60
  selectedItem: any,
54
61
  selectedItems: Array<any>
55
62
  };
@@ -113,6 +120,14 @@ const SelectizeGrid = useDataList(useList((props: GridProps) => {
113
120
  return null;
114
121
  }
115
122
 
123
+ if (props.renderItems) {
124
+ return props.renderItems({
125
+ isSelected: props.isSelected,
126
+ items: props.items,
127
+ onSelect: props.onSelect
128
+ });
129
+ }
130
+
116
131
  return (
117
132
  <Table
118
133
  basic
@@ -161,7 +176,7 @@ const SelectizeGrid = useDataList(useList((props: GridProps) => {
161
176
  />
162
177
  </Grid.Row>
163
178
  <Grid.Row>
164
- { i18n.t('Selectize.noRecords') }
179
+ { i18n.t('Selectize.messages.noRecords') }
165
180
  </Grid.Row>
166
181
  </Grid.Column>
167
182
  </Grid>
@@ -182,9 +197,27 @@ const SelectizeGrid = useDataList(useList((props: GridProps) => {
182
197
  }));
183
198
 
184
199
  const Selectize = (props: Props) => {
200
+ const [error, setError] = useState(false);
185
201
  const [selectedItem, setSelectedItem] = useState();
186
202
  const [selectedItems, setSelectedItems] = useState(props.selectedItems || []);
187
203
 
204
+ /**
205
+ * Sets the allowMultiple prop to "true" if the multiple prop is a boolean or non-zero integer.
206
+ *
207
+ * @type {boolean}
208
+ */
209
+ const allowMultiple = useMemo(() => !!props.multiple, [props.multiple]);
210
+
211
+ /**
212
+ * Sets the allowed number of items that can be selected. If the multiple prop is a boolean, this will be
213
+ * and unlimited amount.
214
+ *
215
+ * @type {boolean|number}
216
+ */
217
+ const allowedCount = useMemo(() => (
218
+ _.isNumber(props.multiple) ? props.multiple : Number.MAX_SAFE_INTEGER
219
+ ), [props.multiple]);
220
+
188
221
  /**
189
222
  * Returns true if the passed item is selected.
190
223
  *
@@ -196,13 +229,17 @@ const Selectize = (props: Props) => {
196
229
  * If the passed item is selected, deselect the item. If we're not allowing multiple select, replace the selected
197
230
  * items with the passed item. Otherwise, append the passed item to the list of selected items.
198
231
  *
232
+ * If the user has already selected the maximum number allowed, display an error message.
233
+ *
199
234
  * @type {(function(*=): void)|*}
200
235
  */
201
236
  const onSelect = useCallback((item) => {
202
237
  if (isSelected(item)) {
203
238
  setSelectedItems((prevItems) => _.filter(prevItems, (i) => i.id !== item.id));
204
- } else if (!props.multiple) {
239
+ } else if (!allowMultiple) {
205
240
  setSelectedItems([item]);
241
+ } else if (selectedItems.length >= allowedCount) {
242
+ setError(true);
206
243
  } else {
207
244
  setSelectedItems((prevItems) => [
208
245
  ...prevItems,
@@ -254,6 +291,13 @@ const Selectize = (props: Props) => {
254
291
  content={props.title}
255
292
  />
256
293
  <Modal.Content>
294
+ <Message
295
+ content={i18n.t('Selectize.messages.maxSelected.content')}
296
+ error
297
+ header={i18n.t('Selectize.messages.maxSelected.header')}
298
+ onDismiss={() => setError(false)}
299
+ visible={error}
300
+ />
257
301
  <SelectizeGrid
258
302
  {...props}
259
303
  actions={[]}