@onehat/ui 0.2.48 → 0.2.50
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 +1 -1
- package/src/Components/Buttons/IconButton.js +3 -0
- package/src/Components/Buttons/PlusMinusButton.js +40 -0
- package/src/Components/Filter/NumberRange.js +2 -0
- package/src/Components/Form/Field/Combo/Combo.js +27 -7
- package/src/Components/Form/Field/Number.js +6 -1
- package/src/Components/Form/Field/Toggle.js +3 -1
- package/src/Components/Form/Form.js +29 -4
- package/src/Components/Hoc/withFilters.js +367 -365
- package/src/Components/Toolbar/Pagination.js +26 -19
- package/src/Components/Toolbar/PaginationToolbar.js +23 -2
- package/src/Components/index.js +2 -0
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Row,
|
|
4
|
+
Text,
|
|
5
|
+
} from 'native-base';
|
|
6
|
+
import IconButton from './IconButton.js';
|
|
7
|
+
import Plus from '../Icons/Plus.js';
|
|
8
|
+
import Minus from '../Icons/Minus.js';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const PlusMinusButton = React.forwardRef((props, ref) => {
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
isPlusDisabled = false,
|
|
15
|
+
isMinusDisabled = false,
|
|
16
|
+
plusHandler = () => {},
|
|
17
|
+
minusHandler = () => {},
|
|
18
|
+
} = props;
|
|
19
|
+
|
|
20
|
+
return <Row {...props}>
|
|
21
|
+
<Row alignItems="center">
|
|
22
|
+
<IconButton
|
|
23
|
+
icon={<Minus color="#fff" />}
|
|
24
|
+
onPress={minusHandler}
|
|
25
|
+
bg="primary.200"
|
|
26
|
+
isDisabled={isMinusDisabled}
|
|
27
|
+
/>
|
|
28
|
+
<IconButton
|
|
29
|
+
icon={<Plus color="#fff" />}
|
|
30
|
+
onPress={plusHandler}
|
|
31
|
+
bg="primary.200"
|
|
32
|
+
isDisabled={isPlusDisabled}
|
|
33
|
+
ml={1}
|
|
34
|
+
/>
|
|
35
|
+
</Row>
|
|
36
|
+
</Row>;
|
|
37
|
+
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export default PlusMinusButton;
|
|
@@ -74,6 +74,7 @@ import _ from 'lodash';
|
|
|
74
74
|
minValue={minValue}
|
|
75
75
|
maxValue={maxValue}
|
|
76
76
|
tooltip={(tooltip ? tooltip + ' ' : '') + 'Low'}
|
|
77
|
+
maxWidth={120}
|
|
77
78
|
/>
|
|
78
79
|
<Text px={2} userSelect="none">to</Text>
|
|
79
80
|
<Number
|
|
@@ -83,6 +84,7 @@ import _ from 'lodash';
|
|
|
83
84
|
minValue={minValue}
|
|
84
85
|
maxValue={maxValue}
|
|
85
86
|
tooltip={(tooltip ? tooltip + ' ' : '') + 'High'}
|
|
87
|
+
maxWidth={120}
|
|
86
88
|
/>
|
|
87
89
|
</Row>;
|
|
88
90
|
},
|
|
@@ -178,17 +178,32 @@ export function Combo(props) {
|
|
|
178
178
|
const {
|
|
179
179
|
relatedTarget
|
|
180
180
|
} = e;
|
|
181
|
+
|
|
182
|
+
// If user focused on the trigger and text is blank, clear the selection and close the menu
|
|
183
|
+
if ((triggerRef.current === relatedTarget || triggerRef.current.contains(relatedTarget)) && (_.isEmpty(textValue) || _.isNil(textValue))) {
|
|
184
|
+
setSelection([]); // delete current selection
|
|
185
|
+
hideMenu();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
181
188
|
|
|
182
|
-
// If user
|
|
183
|
-
if (
|
|
189
|
+
// If user focused on the menu or trigger, ignore this blur
|
|
190
|
+
if (triggerRef.current === relatedTarget ||
|
|
191
|
+
triggerRef.current.contains(relatedTarget) ||
|
|
192
|
+
menuRef.current=== relatedTarget ||
|
|
193
|
+
menuRef.current?.contains(relatedTarget)) {
|
|
184
194
|
return;
|
|
185
195
|
}
|
|
186
196
|
|
|
187
197
|
if (!relatedTarget ||
|
|
188
|
-
(
|
|
198
|
+
(
|
|
199
|
+
!inputRef.current.contains(relatedTarget) &&
|
|
200
|
+
triggerRef.current !== relatedTarget &&
|
|
201
|
+
(!menuRef.current || !menuRef.current.contains(relatedTarget))
|
|
202
|
+
)
|
|
203
|
+
) {
|
|
189
204
|
hideMenu();
|
|
190
205
|
}
|
|
191
|
-
if (textValue
|
|
206
|
+
if (_.isEmpty(textValue) || _.isNil(textValue)) {
|
|
192
207
|
setSelection([]); // delete current selection
|
|
193
208
|
|
|
194
209
|
} else if (isManuallyEnteringText) {
|
|
@@ -221,12 +236,17 @@ export function Combo(props) {
|
|
|
221
236
|
inputRef.current.focus();
|
|
222
237
|
},
|
|
223
238
|
onTriggerBlur = (e) => {
|
|
224
|
-
if (!isMenuShown) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
239
|
const {
|
|
228
240
|
relatedTarget
|
|
229
241
|
} = e;
|
|
242
|
+
|
|
243
|
+
if (_.isEmpty(textValue) || _.isNil(textValue)) {
|
|
244
|
+
setSelection([]); // delete current selection
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!isMenuShown) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
230
250
|
if (!relatedTarget ||
|
|
231
251
|
(!inputRef.current.contains(relatedTarget) && triggerRef.current !== relatedTarget && !menuRef.current.contains(relatedTarget))) {
|
|
232
252
|
hideMenu();
|
|
@@ -54,6 +54,11 @@ function NumberElement(props) {
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
onChangeText = (value) => {
|
|
57
|
+
if (value.match(/\.$/)) { // if value ends with a decimal point
|
|
58
|
+
setLocalValue(value);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
if (value === '') {
|
|
58
63
|
value = null; // empty string makes value null
|
|
59
64
|
} else {
|
|
@@ -114,7 +119,7 @@ function NumberElement(props) {
|
|
|
114
119
|
isIncrementDisabled = typeof maxValue !== 'undefined' && value === maxValue,
|
|
115
120
|
isDecrementDisabled = typeof minValue !== 'undefined' && (value === minValue || (!value && minValue === 0));
|
|
116
121
|
|
|
117
|
-
return <Row flex={1} h="100%" p={0} borderWidth={1} borderColor="trueGray.400" borderRadius={6}>
|
|
122
|
+
return <Row flex={1} h="100%" p={0} borderWidth={1} borderColor="trueGray.400" borderRadius={6} {...props}>
|
|
118
123
|
<IconButton
|
|
119
124
|
icon={<Icon as={Minus} color={isDecrementDisabled ? 'disabled' : 'trueGray.500'} />}
|
|
120
125
|
onPress={onDecrement}
|
|
@@ -67,7 +67,9 @@ const
|
|
|
67
67
|
{...propsToPass}
|
|
68
68
|
/>
|
|
69
69
|
</Pressable>
|
|
70
|
-
<
|
|
70
|
+
<Pressable onPress={onToggle}>
|
|
71
|
+
<Text ml={2} fontSize={styles.FORM_TOGGLE_FONTSIZE}>{_.isNil(value) ? 'N/A' : (!!value ? 'Yes' : 'No')}</Text>
|
|
72
|
+
</Pressable>
|
|
71
73
|
</Row>;
|
|
72
74
|
},
|
|
73
75
|
ToggleField = withValue(ToggleElement);
|
|
@@ -28,6 +28,7 @@ import inArray from '../../Functions/inArray.js';
|
|
|
28
28
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
29
29
|
import IconButton from '../Buttons/IconButton.js';
|
|
30
30
|
import AngleLeft from '../Icons/AngleLeft.js';
|
|
31
|
+
import Eye from '../Icons/Eye.js';
|
|
31
32
|
import Rotate from '../Icons/Rotate.js';
|
|
32
33
|
import Pencil from '../Icons/Pencil.js';
|
|
33
34
|
import Footer from '../Panel/Footer.js';
|
|
@@ -62,6 +63,9 @@ function Form(props) {
|
|
|
62
63
|
footerProps = {},
|
|
63
64
|
buttonGroupProps = {}, // buttons in footer
|
|
64
65
|
onBack,
|
|
66
|
+
onReset,
|
|
67
|
+
onViewMode,
|
|
68
|
+
additionalButtons = [],
|
|
65
69
|
ancillaryComponents = [],
|
|
66
70
|
|
|
67
71
|
// sizing of outer container
|
|
@@ -357,7 +361,12 @@ function Form(props) {
|
|
|
357
361
|
let element = <Element
|
|
358
362
|
name={name}
|
|
359
363
|
value={value}
|
|
360
|
-
onChangeValue={
|
|
364
|
+
onChangeValue={(value) => {
|
|
365
|
+
onChange(value); // form onChange handler
|
|
366
|
+
if (propsToPass.onChange) {
|
|
367
|
+
propsToPass.onChange(value); // item onChange handler
|
|
368
|
+
}
|
|
369
|
+
}}
|
|
361
370
|
onBlur={onBlur}
|
|
362
371
|
selectorId={selectorId}
|
|
363
372
|
selectorSelected={selectorSelected}
|
|
@@ -476,7 +485,8 @@ function Form(props) {
|
|
|
476
485
|
|
|
477
486
|
return <Column {...sizeProps} onLayout={onLayout}>
|
|
478
487
|
|
|
479
|
-
<Row p={2} alignItems="center">
|
|
488
|
+
<Row p={2} alignItems="center" justifyContent="flex-end">
|
|
489
|
+
{/* <Text mr={2} fontSize={18}>{editorModeF} Mode</Text> */}
|
|
480
490
|
{isSingle && editorMode === EDITOR_MODE__EDIT && onBack &&
|
|
481
491
|
<Button
|
|
482
492
|
key="backBtn"
|
|
@@ -484,8 +494,18 @@ function Form(props) {
|
|
|
484
494
|
leftIcon={<Icon as={AngleLeft} color="#fff" size="sm" />}
|
|
485
495
|
color="#fff"
|
|
486
496
|
>Back</Button>}
|
|
487
|
-
|
|
497
|
+
{isSingle && editorMode === EDITOR_MODE__EDIT && onViewMode &&
|
|
498
|
+
<Button
|
|
499
|
+
key="viewBtn"
|
|
500
|
+
onPress={onViewMode}
|
|
501
|
+
leftIcon={<Icon as={Eye} color="#fff" size="sm" />}
|
|
502
|
+
color="#fff"
|
|
503
|
+
>View</Button>}
|
|
488
504
|
</Row>
|
|
505
|
+
{!_.isEmpty(additionalButtons) &&
|
|
506
|
+
<Row p={2} alignItems="center" justifyContent="flex-end">
|
|
507
|
+
{additionalButtons}
|
|
508
|
+
</Row>}
|
|
489
509
|
|
|
490
510
|
{editor}
|
|
491
511
|
|
|
@@ -493,7 +513,12 @@ function Form(props) {
|
|
|
493
513
|
<Button.Group space={2} {...buttonGroupProps}>
|
|
494
514
|
{!isViewOnly && <IconButton
|
|
495
515
|
key="resetBtn"
|
|
496
|
-
onPress={() =>
|
|
516
|
+
onPress={() => {
|
|
517
|
+
if (onReset) {
|
|
518
|
+
onReset();
|
|
519
|
+
}
|
|
520
|
+
reset();
|
|
521
|
+
}}
|
|
497
522
|
icon={<Rotate color="#fff" />}
|
|
498
523
|
/>}
|
|
499
524
|
{!isViewOnly && onCancel && <Button
|
|
@@ -17,344 +17,385 @@ import _ from 'lodash';
|
|
|
17
17
|
|
|
18
18
|
// Filters only work with Repository; not data array
|
|
19
19
|
|
|
20
|
+
// Yet to do:
|
|
21
|
+
// - Save user choice in cookie for next time this component loads
|
|
22
|
+
//
|
|
23
|
+
// Model defaultFilters should adjust to this new arrangement
|
|
24
|
+
|
|
20
25
|
export default function withFilters(WrappedComponent) {
|
|
21
26
|
return (props) => {
|
|
22
27
|
const {
|
|
28
|
+
// config
|
|
23
29
|
useFilters = false,
|
|
24
30
|
searchAllText = true,
|
|
25
31
|
showLabels = true,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
filter1StartingValue = null,
|
|
32
|
-
filter2StartingValue = null,
|
|
33
|
-
filter3StartingValue = null,
|
|
34
|
-
filter4StartingValue = null,
|
|
35
|
-
filter5StartingValue = null,
|
|
32
|
+
showFilterSelector = true,
|
|
33
|
+
defaultFilters = [], // likely a list of field names, possibly could be of shape below
|
|
34
|
+
customFilters = [], // of shape: { title, type, field, value, getRepoFilters(value) }
|
|
35
|
+
minFilters = 3,
|
|
36
|
+
maxFilters = 6,
|
|
36
37
|
|
|
37
38
|
// withData
|
|
38
39
|
Repository,
|
|
39
|
-
} = props
|
|
40
|
-
styles = UiGlobals.styles;
|
|
40
|
+
} = props;
|
|
41
41
|
|
|
42
|
-
let modal,
|
|
42
|
+
let modal = null,
|
|
43
43
|
topToolbar = null;
|
|
44
44
|
|
|
45
45
|
if (useFilters && Repository) {
|
|
46
|
+
|
|
46
47
|
const
|
|
48
|
+
// aliases
|
|
49
|
+
{
|
|
50
|
+
defaultFilters: modelDefaultFilters,
|
|
51
|
+
filterTypes: modelFilterTypes,
|
|
52
|
+
titles: modelTitles,
|
|
53
|
+
virtualFields: modelVirtualFields,
|
|
54
|
+
excludeFields: modelExcludeFields,
|
|
55
|
+
filteringDisabled: modelFilteringDisabled,
|
|
56
|
+
} = Repository.getSchema().model,
|
|
57
|
+
|
|
58
|
+
// determine the starting filters
|
|
59
|
+
startingFilters = !_.isEmpty(customFilters) ? customFilters : // custom filters override component filters
|
|
60
|
+
!_.isEmpty(defaultFilters) ? defaultFilters : // component filters override model filters
|
|
61
|
+
!_.isEmpty(modelDefaultFilters) ? modelDefaultFilters : [],
|
|
62
|
+
isUsingCustomFilters = startingFilters === customFilters,
|
|
47
63
|
[isReady, setIsReady] = useState(false),
|
|
48
64
|
[isFilterSelectorShown, setIsFilterSelectorShown] = useState(false),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
[filter3Value, setFilter3Value] = useState(filter3StartingValue),
|
|
63
|
-
[filter4Value, setFilter4Value] = useState(filter4StartingValue),
|
|
64
|
-
[filter5Value, setFilter5Value] = useState(filter5StartingValue),
|
|
65
|
-
[filterFields, setFilterFields] = useState([]),
|
|
66
|
-
onFilterChange = (ix, value) => {
|
|
67
|
-
switch(ix) {
|
|
68
|
-
case 'q':
|
|
69
|
-
setFilterQValue(value);
|
|
70
|
-
break;
|
|
71
|
-
case 0:
|
|
72
|
-
setFilter1Value(value);
|
|
73
|
-
break;
|
|
74
|
-
case 1:
|
|
75
|
-
setFilter2Value(value);
|
|
76
|
-
break;
|
|
77
|
-
case 2:
|
|
78
|
-
setFilter3Value(value);
|
|
79
|
-
break;
|
|
80
|
-
case 3:
|
|
81
|
-
setFilter4Value(value);
|
|
82
|
-
break;
|
|
83
|
-
case 4:
|
|
84
|
-
setFilter5Value(value);
|
|
85
|
-
break;
|
|
86
|
-
default:
|
|
65
|
+
getFormattedFilter = (filter) => {
|
|
66
|
+
let formatted = null;
|
|
67
|
+
if (_.isString(filter)) {
|
|
68
|
+
const field = filter;
|
|
69
|
+
formatted = {
|
|
70
|
+
field,
|
|
71
|
+
title: modelTitles[field],
|
|
72
|
+
type: modelFilterTypes[field],
|
|
73
|
+
value: null, // value starts as null
|
|
74
|
+
};
|
|
75
|
+
} else if (_.isPlainObject(filter)) {
|
|
76
|
+
// already formatted
|
|
77
|
+
formatted = filter;
|
|
87
78
|
}
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
return formatted;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let formattedStartingFilters = [],
|
|
83
|
+
startingSlots = [];
|
|
84
|
+
if (!isReady) {
|
|
85
|
+
// Generate initial starting state
|
|
86
|
+
if (searchAllText) {
|
|
87
|
+
formattedStartingFilters.push({ field: 'q', title: 'Search all text fields', type: 'Input', value: null, });
|
|
88
|
+
}
|
|
89
|
+
_.each(startingFilters, (filter) => {
|
|
90
90
|
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
} else {
|
|
97
|
-
filterType = filterTypeDefinition.type;
|
|
91
|
+
formattedFilter = getFormattedFilter(filter),
|
|
92
|
+
field = formattedFilter.field;
|
|
93
|
+
formattedStartingFilters.push(formattedFilter);
|
|
94
|
+
if (!isUsingCustomFilters) {
|
|
95
|
+
startingSlots.push(field);
|
|
98
96
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
switch(ix) {
|
|
104
|
-
case 0:
|
|
105
|
-
field = filter1Field;
|
|
106
|
-
break;
|
|
107
|
-
case 1:
|
|
108
|
-
field = filter2Field;
|
|
109
|
-
break;
|
|
110
|
-
case 2:
|
|
111
|
-
field = filter3Field;
|
|
112
|
-
break;
|
|
113
|
-
case 3:
|
|
114
|
-
field = filter4Field;
|
|
115
|
-
break;
|
|
116
|
-
case 4:
|
|
117
|
-
field = filter5Field;
|
|
118
|
-
break;
|
|
119
|
-
default:
|
|
97
|
+
});
|
|
98
|
+
if (startingSlots.length < minFilters) {
|
|
99
|
+
for (let i = startingSlots.length; i < minFilters; i++) {
|
|
100
|
+
startingSlots.push(null);
|
|
120
101
|
}
|
|
121
|
-
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const
|
|
106
|
+
[filters, setFilters] = useState(formattedStartingFilters), // array of formatted filters
|
|
107
|
+
[slots, setSlots] = useState(startingSlots), // array of field names user is currently filtering on; blank slots have a null entry in array
|
|
108
|
+
[modalFilters, setModalFilters] = useState([]),
|
|
109
|
+
[modalSlots, setModalSlots] = useState([]),
|
|
110
|
+
[previousFilterNames, setPreviousFilterNames] = useState([]), // names of filters the repository used last query
|
|
111
|
+
canAddSlot = (() => {
|
|
112
|
+
let canAdd = true;
|
|
113
|
+
if (!!maxFilters && modalSlots.length >= maxFilters) {
|
|
114
|
+
canAdd = false; // maxFilters has been reached
|
|
115
|
+
}
|
|
116
|
+
if (canAdd) {
|
|
117
|
+
_.each(modalSlots, (field) => {
|
|
118
|
+
if (_.isNil(field)) {
|
|
119
|
+
canAdd = false; // at least one slot has no selected field to filter
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return canAdd;
|
|
125
|
+
})(),
|
|
126
|
+
canDeleteSlot = modalSlots.length > minFilters,
|
|
127
|
+
onAddSlot = () => {
|
|
128
|
+
if (!canAddSlot) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const newSlots = _.clone(modalSlots);
|
|
132
|
+
newSlots.push(null);
|
|
133
|
+
setModalSlots(newSlots);
|
|
122
134
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
case 'q':
|
|
127
|
-
value = filterQValue;
|
|
128
|
-
break;
|
|
129
|
-
case 0:
|
|
130
|
-
value = filter1Value;
|
|
131
|
-
break;
|
|
132
|
-
case 1:
|
|
133
|
-
value = filter2Value;
|
|
134
|
-
break;
|
|
135
|
-
case 2:
|
|
136
|
-
value = filter3Value;
|
|
137
|
-
break;
|
|
138
|
-
case 3:
|
|
139
|
-
value = filter4Value;
|
|
140
|
-
break;
|
|
141
|
-
case 4:
|
|
142
|
-
value = filter5Value;
|
|
143
|
-
break;
|
|
144
|
-
default:
|
|
135
|
+
onDeleteSlot = () => {
|
|
136
|
+
if (!canDeleteSlot) {
|
|
137
|
+
return;
|
|
145
138
|
}
|
|
146
|
-
|
|
139
|
+
const
|
|
140
|
+
newFilters = _.clone(modalFilters),
|
|
141
|
+
newSlots = _.clone(modalSlots);
|
|
142
|
+
newFilters.pop();
|
|
143
|
+
newSlots.pop();
|
|
144
|
+
setModalFilters(newFilters);
|
|
145
|
+
setModalSlots(newSlots);
|
|
146
|
+
},
|
|
147
|
+
onFilterChangeValue = (field, value) => {
|
|
148
|
+
// handler for when a filter value changes
|
|
149
|
+
const newFilters = [];
|
|
150
|
+
_.each(filters, (filter) => {
|
|
151
|
+
if (filter.field === field) {
|
|
152
|
+
filter.value = value;
|
|
153
|
+
}
|
|
154
|
+
newFilters.push(filter);
|
|
155
|
+
});
|
|
156
|
+
setFilters(newFilters);
|
|
157
|
+
},
|
|
158
|
+
onClearFilters = () => {
|
|
159
|
+
// Clears values for all active filters
|
|
160
|
+
const newFilters = [];
|
|
161
|
+
_.each(filters, (filter) => {
|
|
162
|
+
filter.value = null;
|
|
163
|
+
newFilters.push(filter);
|
|
164
|
+
});
|
|
165
|
+
setFilters(newFilters);
|
|
147
166
|
},
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
getFilterByField = (field) => {
|
|
168
|
+
return _.find(filters, (filter) => {
|
|
169
|
+
return filter.field === field;
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
getFilterValue = (field) => {
|
|
173
|
+
const filter = getFilterByField(field);
|
|
174
|
+
return filter?.value;
|
|
175
|
+
},
|
|
176
|
+
getFilterType = (field) => {
|
|
177
|
+
// Finds filter type for the field name, from active filters
|
|
178
|
+
const filter = getFilterByField(field);
|
|
179
|
+
return filter?.type;
|
|
180
|
+
},
|
|
181
|
+
getIsFilterRange = (field) => {
|
|
182
|
+
// determines if filter is a "range" filter
|
|
183
|
+
const filterType = getFilterType(field);
|
|
150
184
|
return inArray(filterType, ['NumberRange', 'DateRange']);
|
|
151
185
|
},
|
|
152
186
|
renderFilters = () => {
|
|
153
|
-
if (!Repository) {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
187
|
const
|
|
158
|
-
{
|
|
159
|
-
titles = [],
|
|
160
|
-
filterTypes = [],
|
|
161
|
-
virtualFields = [],
|
|
162
|
-
excludeFields = [],
|
|
163
|
-
} = Repository.getSchema().model,
|
|
164
188
|
filterProps = {
|
|
165
189
|
mx: 1,
|
|
166
190
|
},
|
|
167
|
-
filterElements = []
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
/>);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
if (inArray(fieldName, virtualFields) || inArray(fieldName, excludeFields)) {
|
|
184
|
-
return; // skip
|
|
185
|
-
}
|
|
186
|
-
const filterType = filterTypes[fieldName];
|
|
187
|
-
let Element,
|
|
188
|
-
modelProps = {};
|
|
189
|
-
if (_.isString(filterType)) {
|
|
190
|
-
Element = getComponentFromType(filterType);
|
|
191
|
-
} else if (_.isPlainObject(filterType)) {
|
|
192
|
-
const {
|
|
193
|
-
type,
|
|
194
|
-
...p
|
|
195
|
-
} = filterType;
|
|
196
|
-
modelProps = p;
|
|
197
|
-
Element = getComponentFromType(type);
|
|
198
|
-
}
|
|
199
|
-
if (!Element) {
|
|
200
|
-
debugger;
|
|
191
|
+
filterElements = [];
|
|
192
|
+
_.each(filters, (filter, ix) => {
|
|
193
|
+
let Element,
|
|
194
|
+
elementProps = {};
|
|
195
|
+
const {
|
|
196
|
+
field,
|
|
197
|
+
type: filterType,
|
|
198
|
+
} = filter;
|
|
199
|
+
|
|
200
|
+
if (_.isString(filterType)) {
|
|
201
|
+
Element = getComponentFromType(filterType);
|
|
202
|
+
if (filterType === 'Input') {
|
|
203
|
+
elementProps.autoSubmit = true;
|
|
201
204
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (showLabels) {
|
|
212
|
-
filterElement = <Row key={'label-' + ix} alignItems="center">
|
|
213
|
-
<Text ml={2} mr={1} fontSize={styles.FILTER_LABEL_FONTSIZE}>{titles[fieldName]}</Text>
|
|
214
|
-
{filterElement}
|
|
215
|
-
</Row>;
|
|
205
|
+
} else if (_.isPlainObject(filterType)) {
|
|
206
|
+
const {
|
|
207
|
+
type,
|
|
208
|
+
...p
|
|
209
|
+
} = filterType;
|
|
210
|
+
elementProps = p;
|
|
211
|
+
Element = getComponentFromType(type);
|
|
212
|
+
if (type === 'Input') {
|
|
213
|
+
elementProps.autoSubmit = true;
|
|
216
214
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
as: Ban,
|
|
242
|
-
}}
|
|
243
|
-
ml={1}
|
|
244
|
-
onPress={onClearFilters}
|
|
245
|
-
tooltip="Clear all filters"
|
|
246
|
-
/>);
|
|
247
|
-
filterElements.push(<IconButton
|
|
248
|
-
key="gear"
|
|
249
|
-
_icon={{
|
|
250
|
-
as: Gear,
|
|
251
|
-
}}
|
|
252
|
-
ml={1}
|
|
253
|
-
onPress={() => setIsFilterSelectorShown(true)}
|
|
254
|
-
tooltip="Swap filters"
|
|
255
|
-
/>);
|
|
256
|
-
|
|
215
|
+
}
|
|
216
|
+
if (field === 'q') {
|
|
217
|
+
elementProps.flex = 1;
|
|
218
|
+
elementProps.minWidth = 100;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const tooltip = filter.tooltip || filter.title || modelTitles[filter.field];
|
|
222
|
+
let filterElement = <Element
|
|
223
|
+
key={'filter-' + field}
|
|
224
|
+
tooltip={tooltip}
|
|
225
|
+
placeholder={tooltip}
|
|
226
|
+
value={getFilterValue(field)}
|
|
227
|
+
onChangeValue={(value) => onFilterChangeValue(field, value)}
|
|
228
|
+
{...filterProps}
|
|
229
|
+
{...elementProps}
|
|
230
|
+
/>;
|
|
231
|
+
if (showLabels && field !== 'q') {
|
|
232
|
+
filterElement = <Row key={'label-' + ix} alignItems="center">
|
|
233
|
+
<Text ml={2} mr={1} fontSize={UiGlobals.styles.FILTER_LABEL_FONTSIZE}>{modelTitles[field]}</Text>
|
|
234
|
+
{filterElement}
|
|
235
|
+
</Row>;
|
|
236
|
+
}
|
|
237
|
+
filterElements.push(filterElement);
|
|
238
|
+
});
|
|
257
239
|
return filterElements;
|
|
258
|
-
},
|
|
259
|
-
setFiltersOn = (ix, filters, newFilterFields) => {
|
|
260
|
-
const
|
|
261
|
-
filterIxField = getFilterField(ix),
|
|
262
|
-
filterIxValue = getFilterValue(ix),
|
|
263
|
-
isFilterRange = getIsFilterRange(ix);
|
|
264
|
-
let highValue,
|
|
265
|
-
lowValue,
|
|
266
|
-
highField,
|
|
267
|
-
lowField;
|
|
268
|
-
if (isFilterRange && !!filterIxValue) {
|
|
269
|
-
highValue = filterIxValue.high;
|
|
270
|
-
lowValue = filterIxValue.low;
|
|
271
|
-
highField = filterIxField + ' <=';
|
|
272
|
-
lowField = filterIxField + ' >=';
|
|
273
|
-
|
|
274
|
-
newFilterFields.push(highField);
|
|
275
|
-
newFilterFields.push(lowField);
|
|
276
|
-
filters.push({ name: highField, value: highValue, });
|
|
277
|
-
filters.push({ name: lowField, value: lowValue, });
|
|
278
|
-
} else {
|
|
279
|
-
newFilterFields.push(filterIxField);
|
|
280
|
-
filters.push({ name: filterIxField, value: filterIxValue, });
|
|
281
|
-
}
|
|
282
|
-
},
|
|
283
|
-
onClearFilters = () => {
|
|
284
|
-
setFilterQValue(null);
|
|
285
|
-
setFilter1Value(null);
|
|
286
|
-
setFilter2Value(null);
|
|
287
|
-
setFilter3Value(null);
|
|
288
|
-
setFilter4Value(null);
|
|
289
|
-
setFilter5Value(null);
|
|
290
240
|
};
|
|
291
241
|
|
|
292
242
|
useEffect(() => {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
newFilterFields = [];
|
|
243
|
+
// Whenever the filters change in some way, make repository conform to these new filters
|
|
244
|
+
const newRepoFilters = [];
|
|
296
245
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
246
|
+
if (isUsingCustomFilters) {
|
|
247
|
+
_.each(filters, (filter) => {
|
|
248
|
+
const repoFiltersFromFilter = filter.getRepoFilters(value);
|
|
249
|
+
_.each(repoFiltersFromFilter, (repoFilter) => { // one custom filter might generate multiple filters for the repository
|
|
250
|
+
newRepoFilters.push(repoFilter);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
} else {
|
|
254
|
+
const newFilterNames = [];
|
|
255
|
+
_.each(filters, (filter) => {
|
|
256
|
+
const {
|
|
257
|
+
field,
|
|
258
|
+
value,
|
|
259
|
+
} = filter,
|
|
260
|
+
isFilterRange = getIsFilterRange(field);
|
|
261
|
+
if (isFilterRange) {
|
|
262
|
+
if (!!value) {
|
|
263
|
+
const
|
|
264
|
+
highField = field + ' <=',
|
|
265
|
+
lowField = field + ' >=',
|
|
266
|
+
highValue = value.high,
|
|
267
|
+
lowValue = value.low;
|
|
268
|
+
newFilterNames.push(highField);
|
|
269
|
+
newFilterNames.push(lowField);
|
|
270
|
+
newRepoFilters.push({ name: highField, value: highValue, });
|
|
271
|
+
newRepoFilters.push({ name: lowField, value: lowValue, });
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
newFilterNames.push(field);
|
|
275
|
+
newRepoFilters.push({ name: field, value, });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Go through previousFilterNames and see if any are no longer used.
|
|
280
|
+
_.each(previousFilterNames, (name) => {
|
|
281
|
+
if (!inArray(name, newFilterNames)) {
|
|
282
|
+
newRepoFilters.push({ name, value: null, }); // no longer used, so set it to null so it'll be deleted
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
setPreviousFilterNames(newFilterNames);
|
|
320
286
|
}
|
|
321
|
-
setFilterFields(newFilterFields);
|
|
322
287
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
Repository.filter(filters, null, false); // false so other filters remain
|
|
288
|
+
Repository.filter(newRepoFilters, null, false); // false so other filters remain
|
|
289
|
+
|
|
290
|
+
if (searchAllText && Repository.searchAncillary && !Repository.hasBaseParam('searchAncillary')) {
|
|
291
|
+
Repository.setBaseParam('searchAncillary', true);
|
|
292
|
+
}
|
|
331
293
|
|
|
332
294
|
if (!isReady) {
|
|
333
295
|
setIsReady(true);
|
|
334
296
|
}
|
|
335
297
|
|
|
336
|
-
}, [
|
|
337
|
-
filter1Value, filter2Value, filter3Value, filter4Value, filter5Value,
|
|
338
|
-
filterQValue,]);
|
|
298
|
+
}, [filters]);
|
|
339
299
|
|
|
340
300
|
if (!isReady) {
|
|
341
301
|
return null;
|
|
342
302
|
}
|
|
343
303
|
|
|
304
|
+
const
|
|
305
|
+
renderedFilters = renderFilters(),
|
|
306
|
+
hasFilters = !!renderedFilters.length;
|
|
307
|
+
topToolbar = <Toolbar justifyContent="space-between" alignItems="center">
|
|
308
|
+
<Text pr={2} userSelect="none">Filters:{hasFilters ? '' : ' None'}</Text>
|
|
309
|
+
{renderedFilters}
|
|
310
|
+
<Row flex={hasFilters ? null : 1} justifyContent="flex-end">
|
|
311
|
+
<IconButton
|
|
312
|
+
key="clear"
|
|
313
|
+
_icon={{
|
|
314
|
+
as: Ban,
|
|
315
|
+
}}
|
|
316
|
+
ml={1}
|
|
317
|
+
onPress={onClearFilters}
|
|
318
|
+
tooltip="Clear all filters"
|
|
319
|
+
/>
|
|
320
|
+
{showFilterSelector && !isUsingCustomFilters && <IconButton
|
|
321
|
+
key="gear"
|
|
322
|
+
_icon={{
|
|
323
|
+
as: Gear,
|
|
324
|
+
}}
|
|
325
|
+
ml={1}
|
|
326
|
+
onPress={() => {
|
|
327
|
+
setModalFilters(filters);
|
|
328
|
+
setModalSlots(slots);
|
|
329
|
+
setIsFilterSelectorShown(true);
|
|
330
|
+
}}
|
|
331
|
+
tooltip="Swap filters"
|
|
332
|
+
/>}
|
|
333
|
+
</Row>
|
|
334
|
+
</Toolbar>;
|
|
344
335
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
336
|
+
if (isFilterSelectorShown) { // this is always false when isUsingCustomFilters
|
|
337
|
+
// Build the modal to select the filters
|
|
338
|
+
const
|
|
339
|
+
modalFilterElements = [],
|
|
340
|
+
usedFields = _.filter(_.map(modalFilters, (filter) => {
|
|
341
|
+
return filter?.field;
|
|
342
|
+
}), el => !_.isNil(el)),
|
|
343
|
+
formStartingValues = {};
|
|
344
|
+
|
|
345
|
+
_.each(modalSlots, (field, ix) => {
|
|
346
|
+
|
|
347
|
+
// Create the data for the combobox.
|
|
348
|
+
const data = [];
|
|
349
|
+
_.each(modelFilterTypes, (filterType, filterField) => {
|
|
350
|
+
if (inArray(filterField, usedFields) && field !== filterField) { // Show all filters not yet applied, but include the current filter
|
|
351
|
+
return; // skip, since it's already been used
|
|
352
|
+
}
|
|
353
|
+
data.push([ filterField, modelTitles[filterField] ]);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const
|
|
357
|
+
ixPlusOne = (ix +1),
|
|
358
|
+
filterName = 'filter' + ixPlusOne;
|
|
359
|
+
|
|
360
|
+
modalFilterElements.push({
|
|
361
|
+
key: filterName,
|
|
362
|
+
name: filterName,
|
|
363
|
+
type: 'Combo',
|
|
364
|
+
label: 'Filter ' + ixPlusOne,
|
|
365
|
+
data,
|
|
366
|
+
onChange: (value) => {
|
|
367
|
+
const
|
|
368
|
+
newFilters = _.clone(modalFilters),
|
|
369
|
+
newSlots = _.clone(modalSlots),
|
|
370
|
+
i = searchAllText ? ixPlusOne : ix; // compensate for 'q' filter's possible presence
|
|
371
|
+
|
|
372
|
+
if (newFilters[i]?.value) {
|
|
373
|
+
newFilters[i].value = value;
|
|
374
|
+
} else {
|
|
375
|
+
newFilters[i] = getFormattedFilter(value);
|
|
376
|
+
}
|
|
377
|
+
newSlots[ix] = value;
|
|
378
|
+
|
|
379
|
+
setModalFilters(newFilters);
|
|
380
|
+
setModalSlots(newSlots);
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
formStartingValues[filterName] = field;
|
|
353
385
|
});
|
|
354
|
-
topToolbar = <Toolbar justifyContent="space-between"><Text pt={2} pr={2} userSelect="none">Filters:</Text>{renderFilters()}</Toolbar>;
|
|
355
|
-
}
|
|
356
386
|
|
|
357
|
-
|
|
387
|
+
if (canAddSlot || canDeleteSlot) {
|
|
388
|
+
modalFilterElements.push({
|
|
389
|
+
type: 'PlusMinusButton',
|
|
390
|
+
name: 'plusMinusButton',
|
|
391
|
+
plusHandler: onAddSlot,
|
|
392
|
+
minusHandler: onDeleteSlot,
|
|
393
|
+
isPlusDisabled: !canAddSlot,
|
|
394
|
+
isMinusDisabled: !canDeleteSlot,
|
|
395
|
+
justifyContent: 'flex-end',
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
358
399
|
modal = <Modal
|
|
359
400
|
isOpen={true}
|
|
360
401
|
onClose={() => setIsFilterSelectorShown(false)}
|
|
@@ -362,99 +403,60 @@ export default function withFilters(WrappedComponent) {
|
|
|
362
403
|
<Column bg="#fff" w={500}>
|
|
363
404
|
<FormPanel
|
|
364
405
|
title="Filter Selector"
|
|
365
|
-
instructions="Please select which fields to filter by.
|
|
406
|
+
instructions="Please select which fields to filter by."
|
|
366
407
|
flex={1}
|
|
367
|
-
startingValues={
|
|
368
|
-
filter1: filter1Field,
|
|
369
|
-
filter2: filter2Field,
|
|
370
|
-
filter3: filter3Field,
|
|
371
|
-
filter4: filter4Field,
|
|
372
|
-
filter5: filter5Field,
|
|
373
|
-
}}
|
|
408
|
+
startingValues={formStartingValues}
|
|
374
409
|
items={[
|
|
375
410
|
{
|
|
376
411
|
type: 'Column',
|
|
377
412
|
flex: 1,
|
|
378
|
-
items:
|
|
379
|
-
|
|
380
|
-
type: 'Combo',
|
|
381
|
-
label: 'Filter 1',
|
|
382
|
-
name: 'filter1',
|
|
383
|
-
onChangeValue: (value) => {
|
|
384
|
-
setFilter1FieldForModal(value);
|
|
385
|
-
},
|
|
386
|
-
...filterComboProps,
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
type: 'Combo',
|
|
390
|
-
label: 'Filter 2',
|
|
391
|
-
name: 'filter2',
|
|
392
|
-
onChangeValue: (value) => {
|
|
393
|
-
setFilter2FieldForModal(value);
|
|
394
|
-
},
|
|
395
|
-
...filterComboProps,
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
type: 'Combo',
|
|
399
|
-
label: 'Filter 3',
|
|
400
|
-
name: 'filter3',
|
|
401
|
-
onChangeValue: (value) => {
|
|
402
|
-
setFilter3FieldForModal(value);
|
|
403
|
-
},
|
|
404
|
-
...filterComboProps,
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
type: 'Combo',
|
|
408
|
-
label: 'Filter 4',
|
|
409
|
-
name: 'filter4',
|
|
410
|
-
onChangeValue: (value) => {
|
|
411
|
-
setFilter4FieldForModal(value);
|
|
412
|
-
},
|
|
413
|
-
...filterComboProps,
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
type: 'Combo',
|
|
417
|
-
label: 'Filter 5',
|
|
418
|
-
name: 'filter5',
|
|
419
|
-
onChangeValue: (value) => {
|
|
420
|
-
setFilter5FieldForModal(value);
|
|
421
|
-
},
|
|
422
|
-
...filterComboProps,
|
|
423
|
-
},
|
|
424
|
-
],
|
|
425
|
-
}, // END Column
|
|
413
|
+
items: modalFilterElements,
|
|
414
|
+
},
|
|
426
415
|
]}
|
|
427
416
|
onCancel={(e) => {
|
|
428
|
-
|
|
429
|
-
setFilter2FieldForModal(filter2Field);
|
|
430
|
-
setFilter3FieldForModal(filter3Field);
|
|
431
|
-
setFilter4FieldForModal(filter4Field);
|
|
432
|
-
setFilter5FieldForModal(filter5Field);
|
|
417
|
+
// Just close the modal
|
|
433
418
|
setIsFilterSelectorShown(false);
|
|
434
419
|
}}
|
|
435
420
|
onSave={(data, e) => {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (filter3FieldForModal !== filter3Field) {
|
|
445
|
-
setFilter3Field(filter3FieldForModal);
|
|
446
|
-
setFilter3Value(null);
|
|
447
|
-
}
|
|
448
|
-
if (filter4FieldForModal !== filter4Field) {
|
|
449
|
-
setFilter4Field(filter4FieldForModal);
|
|
450
|
-
setFilter4Value(null);
|
|
421
|
+
// Conform filters to this new choice of filters
|
|
422
|
+
|
|
423
|
+
const
|
|
424
|
+
newFilters = [],
|
|
425
|
+
newSlots = [];
|
|
426
|
+
|
|
427
|
+
if (searchAllText) {
|
|
428
|
+
newFilters.push(filters[0]);
|
|
451
429
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
430
|
+
|
|
431
|
+
// Conform the filters to the modal selection
|
|
432
|
+
_.each(data, (field, ix) => {
|
|
433
|
+
if (_.isEmpty(field) || !ix.match(/^filter/)) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const newFilter = getFormattedFilter(field);
|
|
438
|
+
|
|
439
|
+
newFilters.push(newFilter);
|
|
440
|
+
newSlots.push(field);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
if (newSlots.length < minFilters) {
|
|
444
|
+
// Add more slots until we get to minFilters
|
|
445
|
+
for(let i = newSlots.length; i < minFilters; i++) {
|
|
446
|
+
newSlots.push(null);
|
|
447
|
+
}
|
|
455
448
|
}
|
|
449
|
+
|
|
450
|
+
setFilters(newFilters);
|
|
451
|
+
setSlots(newSlots);
|
|
452
|
+
|
|
453
|
+
// Close the modal
|
|
456
454
|
setIsFilterSelectorShown(false);
|
|
457
455
|
}}
|
|
456
|
+
onReset={() => {
|
|
457
|
+
setModalFilters(filters);
|
|
458
|
+
setModalSlots(slots);
|
|
459
|
+
}}
|
|
458
460
|
/>
|
|
459
461
|
</Column>
|
|
460
462
|
</Modal>;
|
|
@@ -16,6 +16,8 @@ import PageSizeCombo from '../Form/Field/Combo/PageSizeCombo.js';
|
|
|
16
16
|
|
|
17
17
|
export default function Pagination(props) {
|
|
18
18
|
const {
|
|
19
|
+
minimize = false,
|
|
20
|
+
|
|
19
21
|
// withData
|
|
20
22
|
Repository,
|
|
21
23
|
} = props,
|
|
@@ -70,23 +72,25 @@ export default function Pagination(props) {
|
|
|
70
72
|
onPress={() => Repository.prevPage()}
|
|
71
73
|
tooltip="Previous Page"
|
|
72
74
|
/>);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
if (!minimize) {
|
|
76
|
+
items.push(<Row
|
|
77
|
+
key="pageSelector"
|
|
78
|
+
mx={3}
|
|
79
|
+
justifyContent="center"
|
|
80
|
+
alignItems="center"
|
|
81
|
+
>
|
|
82
|
+
<Text mr={2}>Page</Text>
|
|
83
|
+
<Input
|
|
84
|
+
value={page}
|
|
85
|
+
onChangeValue={(value) => Repository.setPage(value)}
|
|
86
|
+
maxValue={totalPages}
|
|
87
|
+
isDisabled={totalPages === 1}
|
|
88
|
+
w={10}
|
|
89
|
+
tooltip="Set Page"
|
|
90
|
+
/>
|
|
91
|
+
<Text ml={2}>of {totalPages}</Text>
|
|
92
|
+
</Row>);
|
|
93
|
+
}
|
|
90
94
|
|
|
91
95
|
isDisabled = page === totalPages || totalPages <= 1;
|
|
92
96
|
items.push(<IconButton
|
|
@@ -115,7 +119,9 @@ export default function Pagination(props) {
|
|
|
115
119
|
/>);
|
|
116
120
|
}
|
|
117
121
|
|
|
118
|
-
|
|
122
|
+
if (!minimize) {
|
|
123
|
+
items.push(<PageSizeCombo key="pageSize" pageSize={pageSize} Repository={Repository} />);
|
|
124
|
+
}
|
|
119
125
|
|
|
120
126
|
let pageSpan = `${pageStart} – ${pageEnd}`;
|
|
121
127
|
if (pageStart === pageEnd) {
|
|
@@ -130,7 +136,7 @@ export default function Pagination(props) {
|
|
|
130
136
|
{...props}
|
|
131
137
|
>
|
|
132
138
|
{items}
|
|
133
|
-
<Text ml={3}>Displaying {pageSpan} of {total}</Text>
|
|
139
|
+
{!minimize && <Text ml={3}>Displaying {pageSpan} of {total}</Text>}
|
|
134
140
|
</Row>;
|
|
135
141
|
}, [
|
|
136
142
|
// Repository,
|
|
@@ -140,6 +146,7 @@ export default function Pagination(props) {
|
|
|
140
146
|
totalPages,
|
|
141
147
|
pageStart,
|
|
142
148
|
pageEnd,
|
|
149
|
+
minimize,
|
|
143
150
|
])
|
|
144
151
|
|
|
145
152
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
1
2
|
import {
|
|
2
3
|
Row,
|
|
3
4
|
} from 'native-base';
|
|
@@ -9,14 +10,34 @@ export default function PaginationToolbar(props) {
|
|
|
9
10
|
const {
|
|
10
11
|
toolbarItems = [],
|
|
11
12
|
} = props,
|
|
12
|
-
|
|
13
|
+
[minimize, setMinimize] = useState(false),
|
|
14
|
+
propsToPass = _.omit(props, 'toolbarItems'),
|
|
15
|
+
onLayout = (e) => {
|
|
16
|
+
// Note to future self: this is using hard-coded values.
|
|
17
|
+
// Eventually might want to make it responsive to actual sizes
|
|
18
|
+
|
|
19
|
+
// Also, eventually might useMediaQuery from NativeBase, but ReactNative is not yet supported,
|
|
20
|
+
// so have to do things the long way.
|
|
21
|
+
const
|
|
22
|
+
width = e.nativeEvent.layout.width,
|
|
23
|
+
pagingToolbarMinwidth = 576,
|
|
24
|
+
toolbarItemsMinwidth = 45 * toolbarItems.length,
|
|
25
|
+
threshold = pagingToolbarMinwidth + toolbarItemsMinwidth,
|
|
26
|
+
shouldMinimize = width < threshold;
|
|
27
|
+
|
|
28
|
+
if (shouldMinimize !== minimize) {
|
|
29
|
+
setMinimize(shouldMinimize);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
13
33
|
return <Toolbar
|
|
14
34
|
bg="trueGray.200"
|
|
15
35
|
borderTopWidth={1}
|
|
16
36
|
borderTopColor="trueGray.400"
|
|
17
37
|
w="100%"
|
|
38
|
+
onLayout={(e) => onLayout(e)}
|
|
18
39
|
>
|
|
19
|
-
<Pagination {...propsToPass} w={toolbarItems.length ? null : '100%'} />
|
|
40
|
+
<Pagination {...propsToPass} w={toolbarItems.length ? null : '100%'} minimize={minimize} />
|
|
20
41
|
{toolbarItems.length ? <Row flex={1} borderLeftWidth={1} borderLeftColor="trueGray.400" pl={3} ml={3}>{toolbarItems}</Row> : null}
|
|
21
42
|
</Toolbar>;
|
|
22
43
|
};
|
package/src/Components/index.js
CHANGED
|
@@ -35,6 +35,7 @@ import Number from './Form/Field/Number.js';
|
|
|
35
35
|
import NumberRange from './Filter/NumberRange.js';
|
|
36
36
|
import Panel from './Panel/Panel.js';
|
|
37
37
|
// import Picker from '../Components/Panel/Picker.js';
|
|
38
|
+
import PlusMinusButton from './Buttons/PlusMinusButton.js';
|
|
38
39
|
import RadioGroup from './Form/Field/RadioGroup/RadioGroup.js';
|
|
39
40
|
import TabPanel from './Panel/TabPanel.js';
|
|
40
41
|
import Tag from './Form/Field/Combo/Tag.js';
|
|
@@ -82,6 +83,7 @@ const components = {
|
|
|
82
83
|
NumberRange,
|
|
83
84
|
Panel,
|
|
84
85
|
// Picker,
|
|
86
|
+
PlusMinusButton,
|
|
85
87
|
RadioGroup,
|
|
86
88
|
TabPanel,
|
|
87
89
|
Tag,
|