baseui 10.9.0 → 10.9.1
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/data-table/column-numerical.js +355 -307
- package/data-table/column-numerical.js.flow +287 -273
- package/data-table/constants.js +11 -17
- package/data-table/constants.js.flow +8 -11
- package/data-table/data-table.js +50 -53
- package/data-table/data-table.js.flow +13 -18
- package/data-table/filter-shell.js +4 -27
- package/data-table/filter-shell.js.flow +9 -33
- package/data-table/locale.js +2 -4
- package/data-table/locale.js.flow +2 -6
- package/data-table/measure-column-widths.js +121 -83
- package/data-table/measure-column-widths.js.flow +109 -87
- package/es/data-table/column-numerical.js +317 -245
- package/es/data-table/constants.js +8 -12
- package/es/data-table/data-table.js +16 -18
- package/es/data-table/filter-shell.js +4 -26
- package/es/data-table/locale.js +2 -4
- package/es/data-table/measure-column-widths.js +86 -75
- package/es/form-control/form-control.js +58 -7
- package/es/form-control/styled-components.js +27 -6
- package/es/popover/popover.js +1 -1
- package/esm/data-table/column-numerical.js +353 -304
- package/esm/data-table/constants.js +8 -12
- package/esm/data-table/data-table.js +50 -53
- package/esm/data-table/filter-shell.js +4 -26
- package/esm/data-table/locale.js +2 -4
- package/esm/data-table/measure-column-widths.js +121 -83
- package/esm/form-control/form-control.js +60 -9
- package/esm/form-control/styled-components.js +23 -3
- package/esm/popover/popover.js +1 -1
- package/form-control/form-control.js +61 -8
- package/form-control/form-control.js.flow +82 -10
- package/form-control/index.d.ts +1 -0
- package/form-control/styled-components.js +27 -5
- package/form-control/styled-components.js.flow +25 -3
- package/form-control/types.js.flow +20 -8
- package/package.json +1 -2
- package/popover/popover.js +1 -1
- package/popover/popover.js.flow +1 -1
|
@@ -12,25 +12,26 @@ import {Button, SIZE} from '../button/index.js';
|
|
|
12
12
|
import {ButtonGroup, MODE} from '../button-group/index.js';
|
|
13
13
|
import {Input, SIZE as INPUT_SIZE} from '../input/index.js';
|
|
14
14
|
import {useStyletron} from '../styles/index.js';
|
|
15
|
+
import {ParagraphXSmall} from '../typography/index.js';
|
|
15
16
|
|
|
16
17
|
import Column from './column.js';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
NUMERICAL_FORMATS,
|
|
20
|
-
MAX_BIN_COUNT,
|
|
21
|
-
HISTOGRAM_SIZE,
|
|
22
|
-
} from './constants.js';
|
|
23
|
-
import FilterShell, {type ExcludeKind} from './filter-shell.js';
|
|
18
|
+
import {COLUMNS, NUMERICAL_FORMATS, NUMERICAL_OPERATIONS} from './constants.js';
|
|
19
|
+
import FilterShell from './filter-shell.js';
|
|
24
20
|
import type {ColumnT, SharedColumnOptionsT} from './types.js';
|
|
25
21
|
import {LocaleContext} from '../locale/index.js';
|
|
26
|
-
import {bin, max as maxFunc, extent, scaleLinear, median, bisector} from 'd3';
|
|
27
|
-
import {Slider} from '../slider/index.js';
|
|
28
22
|
|
|
29
23
|
type NumericalFormats =
|
|
30
24
|
| typeof NUMERICAL_FORMATS.DEFAULT
|
|
31
25
|
| typeof NUMERICAL_FORMATS.ACCOUNTING
|
|
32
26
|
| typeof NUMERICAL_FORMATS.PERCENTAGE;
|
|
33
27
|
|
|
28
|
+
type NumericalOperations =
|
|
29
|
+
| typeof NUMERICAL_OPERATIONS.EQ
|
|
30
|
+
| typeof NUMERICAL_OPERATIONS.GT
|
|
31
|
+
| typeof NUMERICAL_OPERATIONS.GTE
|
|
32
|
+
| typeof NUMERICAL_OPERATIONS.LT
|
|
33
|
+
| typeof NUMERICAL_OPERATIONS.LTE;
|
|
34
|
+
|
|
34
35
|
type OptionsT = {|
|
|
35
36
|
...SharedColumnOptionsT<number>,
|
|
36
37
|
format?: NumericalFormats | ((value: number) => string),
|
|
@@ -39,11 +40,12 @@ type OptionsT = {|
|
|
|
39
40
|
|};
|
|
40
41
|
|
|
41
42
|
type FilterParametersT = {|
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
comparisons: Array<{|
|
|
44
|
+
value: number,
|
|
45
|
+
operation: NumericalOperations,
|
|
46
|
+
|}>,
|
|
44
47
|
description: string,
|
|
45
48
|
exclude: boolean,
|
|
46
|
-
excludeKind: ExcludeKind,
|
|
47
49
|
|};
|
|
48
50
|
|
|
49
51
|
type NumericalColumnT = ColumnT<number, FilterParametersT>;
|
|
@@ -84,197 +86,224 @@ function validateInput(input) {
|
|
|
84
86
|
return Boolean(parseFloat(input)) || input === '' || input === '-';
|
|
85
87
|
}
|
|
86
88
|
|
|
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
|
-
|
|
89
|
+
function filterParamsToInitialState(filterParams) {
|
|
90
|
+
if (filterParams) {
|
|
91
|
+
if (filterParams.comparisons.length > 1) {
|
|
92
|
+
if (
|
|
93
|
+
filterParams.comparisons[0].operation === NUMERICAL_OPERATIONS.LT &&
|
|
94
|
+
filterParams.comparisons[1].operation === NUMERICAL_OPERATIONS.GT
|
|
95
|
+
) {
|
|
96
|
+
return {
|
|
97
|
+
exclude: !filterParams.exclude,
|
|
98
|
+
comparatorIndex: 0,
|
|
99
|
+
operatorIndex: 4,
|
|
100
|
+
right: filterParams.comparisons[1].value.toString(),
|
|
101
|
+
left: filterParams.comparisons[0].value.toString(),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
const comparison = filterParams.comparisons[0];
|
|
106
|
+
if (comparison.operation === NUMERICAL_OPERATIONS.LT) {
|
|
107
|
+
return {
|
|
108
|
+
exclude: filterParams.exclude,
|
|
109
|
+
comparatorIndex: 0,
|
|
110
|
+
operatorIndex: 0,
|
|
111
|
+
left: '',
|
|
112
|
+
right: comparison.value.toString(),
|
|
113
|
+
};
|
|
114
|
+
} else if (comparison.operation === NUMERICAL_OPERATIONS.GT) {
|
|
115
|
+
return {
|
|
116
|
+
exclude: filterParams.exclude,
|
|
117
|
+
comparatorIndex: 0,
|
|
118
|
+
operatorIndex: 1,
|
|
119
|
+
left: comparison.value.toString(),
|
|
120
|
+
right: '',
|
|
121
|
+
};
|
|
122
|
+
} else if (comparison.operation === NUMERICAL_OPERATIONS.LTE) {
|
|
123
|
+
return {
|
|
124
|
+
exclude: filterParams.exclude,
|
|
125
|
+
comparatorIndex: 0,
|
|
126
|
+
operatorIndex: 2,
|
|
127
|
+
left: '',
|
|
128
|
+
right: comparison.value.toString(),
|
|
129
|
+
};
|
|
130
|
+
} else if (comparison.operation === NUMERICAL_OPERATIONS.GTE) {
|
|
131
|
+
return {
|
|
132
|
+
exclude: filterParams.exclude,
|
|
133
|
+
comparatorIndex: 0,
|
|
134
|
+
operatorIndex: 3,
|
|
135
|
+
left: comparison.value.toString(),
|
|
136
|
+
right: '',
|
|
137
|
+
};
|
|
138
|
+
} else if (comparison.operation === NUMERICAL_OPERATIONS.EQ) {
|
|
139
|
+
return {
|
|
140
|
+
exclude: filterParams.exclude,
|
|
141
|
+
comparatorIndex: 1,
|
|
142
|
+
operatorIndex: 0,
|
|
143
|
+
left: comparison.value.toString(),
|
|
144
|
+
right: '',
|
|
145
|
+
};
|
|
146
|
+
}
|
|
118
147
|
}
|
|
119
|
-
|
|
120
|
-
}, [isRange, data, lower, upper]);
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<div
|
|
124
|
-
className={css({
|
|
125
|
-
display: 'flex',
|
|
126
|
-
marginTop: theme.sizing.scale600,
|
|
127
|
-
marginLeft: theme.sizing.scale200,
|
|
128
|
-
marginRight: 0,
|
|
129
|
-
marginBottom: theme.sizing.scale400,
|
|
130
|
-
justifyContent: 'space-between',
|
|
131
|
-
overflow: 'visible',
|
|
132
|
-
})}
|
|
133
|
-
>
|
|
134
|
-
<svg {...HISTOGRAM_SIZE}>
|
|
135
|
-
{bins.map((d, index) => {
|
|
136
|
-
const x = xScale(d.x0) + 1;
|
|
137
|
-
const y = yScale(d.length);
|
|
138
|
-
const width = Math.max(0, xScale(d.x1) - xScale(d.x0) - 1);
|
|
139
|
-
const height = yScale(0) - yScale(d.length);
|
|
140
|
-
|
|
141
|
-
let included;
|
|
142
|
-
if (singleIndexNearest != null) {
|
|
143
|
-
included = index === singleIndexNearest;
|
|
144
|
-
} else {
|
|
145
|
-
const withinLower = d.x1 > lower;
|
|
146
|
-
const withinUpper = d.x0 <= upper;
|
|
147
|
-
included = withinLower && withinUpper;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (exclude) {
|
|
151
|
-
included = !included;
|
|
152
|
-
}
|
|
148
|
+
}
|
|
153
149
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
/>
|
|
163
|
-
);
|
|
164
|
-
})}
|
|
165
|
-
</svg>
|
|
166
|
-
</div>
|
|
167
|
-
);
|
|
168
|
-
});
|
|
150
|
+
return {
|
|
151
|
+
exclude: false,
|
|
152
|
+
comparatorIndex: 0,
|
|
153
|
+
operatorIndex: 0,
|
|
154
|
+
left: '',
|
|
155
|
+
right: '',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
169
158
|
|
|
170
159
|
function NumericalFilter(props) {
|
|
171
160
|
const [css, theme] = useStyletron();
|
|
172
161
|
const locale = React.useContext(LocaleContext);
|
|
173
162
|
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
// The state handling of this component could be refactored and cleaned up if we used useReducer.
|
|
177
|
-
const initialState = React.useMemo(() => {
|
|
178
|
-
return (
|
|
179
|
-
props.filterParams || {
|
|
180
|
-
exclude: false,
|
|
181
|
-
excludeKind: 'range',
|
|
182
|
-
comparatorIndex: 0,
|
|
183
|
-
lowerValue: null,
|
|
184
|
-
upperValue: null,
|
|
185
|
-
}
|
|
186
|
-
);
|
|
187
|
-
}, [props.filterParams]);
|
|
188
|
-
|
|
163
|
+
const initialState = filterParamsToInitialState(props.filterParams);
|
|
189
164
|
const [exclude, setExclude] = React.useState(initialState.exclude);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// TODO look into allowing semantic names, similar to the radio component. Tricky part would be backwards compat
|
|
193
|
-
const [comparatorIndex, setComparatorIndex] = React.useState(() => {
|
|
194
|
-
switch (initialState.excludeKind) {
|
|
195
|
-
case 'value':
|
|
196
|
-
return 1;
|
|
197
|
-
case 'range':
|
|
198
|
-
default:
|
|
199
|
-
// fallthrough
|
|
200
|
-
return 0;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// We use the d3 function to get the extent as it's a little more robust to null, -Infinity, etc.
|
|
205
|
-
const [min, max] = React.useMemo(() => extent(props.data), [props.data]);
|
|
206
|
-
|
|
207
|
-
const [lv, setLower] = React.useState<number>(() =>
|
|
208
|
-
roundToFixed(initialState.lowerValue || min, precision),
|
|
165
|
+
const [comparatorIndex, setComparatorIndex] = React.useState(
|
|
166
|
+
initialState.comparatorIndex,
|
|
209
167
|
);
|
|
210
|
-
const [
|
|
211
|
-
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
// We keep a separate value for the single select, to give a user the ability to toggle between
|
|
215
|
-
// the range and single values without losing their previous input.
|
|
216
|
-
const [sv, setSingle] = React.useState<number>(() =>
|
|
217
|
-
roundToFixed(initialState.lowerValue || median(props.data), precision),
|
|
168
|
+
const [operatorIndex, setOperatorIndex] = React.useState(
|
|
169
|
+
initialState.operatorIndex,
|
|
218
170
|
);
|
|
171
|
+
const [left, setLeft] = React.useState(initialState.left);
|
|
172
|
+
const [right, setRight] = React.useState(initialState.right);
|
|
219
173
|
|
|
220
|
-
// This is the only conditional which we want to use to determine
|
|
221
|
-
// if we are in range or single value mode.
|
|
222
|
-
// Don't derive it via something else, e.g. lowerValue === upperValue, etc.
|
|
223
174
|
const isRange = comparatorIndex === 0;
|
|
175
|
+
const min = React.useMemo(() => Math.min(...props.data), [props.data]);
|
|
176
|
+
const max = React.useMemo(() => Math.max(...props.data), [props.data]);
|
|
224
177
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// if we don't do this, a user can't input partial numbers, e.g. "-", or "3."
|
|
229
|
-
const [focused, setFocus] = React.useState(false);
|
|
230
|
-
const [inputValueLower, inputValueUpper] = React.useMemo(() => {
|
|
231
|
-
if (focused) {
|
|
232
|
-
return [isRange ? lv : sv, uv];
|
|
178
|
+
React.useEffect(() => {
|
|
179
|
+
if (!left) {
|
|
180
|
+
setLeft(min.toString());
|
|
233
181
|
}
|
|
182
|
+
if (!right) {
|
|
183
|
+
setRight(max.toString());
|
|
184
|
+
}
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
const [leftDisabled, rightDisabled] = React.useMemo(() => {
|
|
188
|
+
if (!isRange) return [false, false];
|
|
189
|
+
switch (operatorIndex) {
|
|
190
|
+
case 4:
|
|
191
|
+
return [false, false];
|
|
192
|
+
case 0:
|
|
193
|
+
case 2:
|
|
194
|
+
return [true, false];
|
|
195
|
+
case 1:
|
|
196
|
+
case 3:
|
|
197
|
+
return [false, true];
|
|
198
|
+
default:
|
|
199
|
+
return [true, true];
|
|
200
|
+
}
|
|
201
|
+
}, [operatorIndex, isRange]);
|
|
202
|
+
|
|
203
|
+
const leftInputRef = React.useRef(null);
|
|
204
|
+
const rightInputRef = React.useRef(null);
|
|
205
|
+
React.useEffect(() => {
|
|
206
|
+
if (!leftDisabled && leftInputRef.current) {
|
|
207
|
+
leftInputRef.current.focus({preventScroll: true});
|
|
208
|
+
} else if (!rightDisabled && rightInputRef.current) {
|
|
209
|
+
rightInputRef.current.focus({preventScroll: true});
|
|
210
|
+
}
|
|
211
|
+
}, [leftDisabled, rightDisabled, comparatorIndex]);
|
|
234
212
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (isRange && sliderValue[0] > sliderValue[1]) {
|
|
251
|
-
sliderValue = [sliderValue[1], sliderValue[0]];
|
|
252
|
-
}
|
|
213
|
+
React.useEffect(() => {
|
|
214
|
+
switch (operatorIndex) {
|
|
215
|
+
case 4:
|
|
216
|
+
default:
|
|
217
|
+
break;
|
|
218
|
+
case 1:
|
|
219
|
+
case 3:
|
|
220
|
+
setRight(max.toString());
|
|
221
|
+
break;
|
|
222
|
+
case 0:
|
|
223
|
+
case 2:
|
|
224
|
+
setLeft(min.toString());
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}, [operatorIndex]);
|
|
253
228
|
|
|
254
229
|
return (
|
|
255
230
|
<FilterShell
|
|
256
231
|
exclude={exclude}
|
|
257
232
|
onExcludeChange={() => setExclude(!exclude)}
|
|
258
|
-
excludeKind={excludeKind}
|
|
259
233
|
onApply={() => {
|
|
260
234
|
if (isRange) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
235
|
+
switch (operatorIndex) {
|
|
236
|
+
case 0: {
|
|
237
|
+
const value = parseFloat(right);
|
|
238
|
+
const operation = NUMERICAL_OPERATIONS.LT;
|
|
239
|
+
props.setFilter({
|
|
240
|
+
comparisons: [{value, operation}],
|
|
241
|
+
description: `< ${value}`,
|
|
242
|
+
exclude,
|
|
243
|
+
});
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
case 1: {
|
|
247
|
+
const value = parseFloat(left);
|
|
248
|
+
const operation = NUMERICAL_OPERATIONS.GT;
|
|
249
|
+
props.setFilter({
|
|
250
|
+
comparisons: [{value, operation}],
|
|
251
|
+
description: `> ${value}`,
|
|
252
|
+
exclude,
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
case 2: {
|
|
257
|
+
const value = parseFloat(right);
|
|
258
|
+
const operation = NUMERICAL_OPERATIONS.LTE;
|
|
259
|
+
props.setFilter({
|
|
260
|
+
comparisons: [{value, operation}],
|
|
261
|
+
description: `≤ ${value}`,
|
|
262
|
+
exclude,
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
case 3: {
|
|
267
|
+
const value = parseFloat(left);
|
|
268
|
+
const operation = NUMERICAL_OPERATIONS.GTE;
|
|
269
|
+
props.setFilter({
|
|
270
|
+
comparisons: [{value, operation}],
|
|
271
|
+
description: `≥ ${value}`,
|
|
272
|
+
exclude,
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case 4: {
|
|
277
|
+
// 'between' case is interesting since if we want less than 10 plus greater than 5
|
|
278
|
+
// comparators, the filter will include _all_ numbers.
|
|
279
|
+
const leftValue = parseFloat(left);
|
|
280
|
+
const rightValue = parseFloat(right);
|
|
281
|
+
props.setFilter({
|
|
282
|
+
comparisons: [
|
|
283
|
+
{
|
|
284
|
+
value: leftValue,
|
|
285
|
+
operation: NUMERICAL_OPERATIONS.LT,
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
value: rightValue,
|
|
289
|
+
operation: NUMERICAL_OPERATIONS.GT,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
description: `≥ ${leftValue} & ≤ ${rightValue}`,
|
|
293
|
+
exclude: !exclude,
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
default:
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
270
300
|
} else {
|
|
271
|
-
const value = parseFloat(
|
|
301
|
+
const value = parseFloat(left);
|
|
302
|
+
const operation = NUMERICAL_OPERATIONS.EQ;
|
|
272
303
|
props.setFilter({
|
|
304
|
+
comparisons: [{value, operation}],
|
|
273
305
|
description: `= ${value}`,
|
|
274
|
-
exclude
|
|
275
|
-
lowerValue: inputValueLower,
|
|
276
|
-
upperValue: inputValueLower,
|
|
277
|
-
excludeKind,
|
|
306
|
+
exclude,
|
|
278
307
|
});
|
|
279
308
|
}
|
|
280
309
|
|
|
@@ -282,7 +311,7 @@ function NumericalFilter(props) {
|
|
|
282
311
|
}}
|
|
283
312
|
>
|
|
284
313
|
<ButtonGroup
|
|
285
|
-
size={SIZE.
|
|
314
|
+
size={SIZE.compact}
|
|
286
315
|
mode={MODE.radio}
|
|
287
316
|
selected={comparatorIndex}
|
|
288
317
|
onClick={(_, index) => setComparatorIndex(index)}
|
|
@@ -295,130 +324,100 @@ function NumericalFilter(props) {
|
|
|
295
324
|
<Button
|
|
296
325
|
type="button"
|
|
297
326
|
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
298
|
-
aria-label={locale.datatable.numericalFilterRange}
|
|
299
327
|
>
|
|
300
328
|
{locale.datatable.numericalFilterRange}
|
|
301
329
|
</Button>
|
|
302
330
|
<Button
|
|
303
331
|
type="button"
|
|
304
332
|
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
305
|
-
aria-label={locale.datatable.numericalFilterSingleValue}
|
|
306
333
|
>
|
|
307
334
|
{locale.datatable.numericalFilterSingleValue}
|
|
308
335
|
</Button>
|
|
309
336
|
</ButtonGroup>
|
|
310
337
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
precision={props.options.precision}
|
|
318
|
-
/>
|
|
319
|
-
|
|
320
|
-
<div className={css({display: 'flex', justifyContent: 'space-between'})}>
|
|
321
|
-
<Slider
|
|
322
|
-
// The slider throws errors when switching between single and two values
|
|
323
|
-
// when it tries to read getThumbDistance on a thumb which is not there anymore
|
|
324
|
-
// if we create a new instance these errors are prevented.
|
|
325
|
-
key={isRange.toString()}
|
|
326
|
-
min={min}
|
|
327
|
-
max={max}
|
|
328
|
-
value={sliderValue}
|
|
329
|
-
onChange={({value}) => {
|
|
330
|
-
if (!value) {
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
if (isRange) {
|
|
334
|
-
const [lowerValue, upperValue] = value;
|
|
335
|
-
setLower(lowerValue);
|
|
336
|
-
setUpper(upperValue);
|
|
337
|
-
} else {
|
|
338
|
-
const [singleValue] = value;
|
|
339
|
-
setSingle(singleValue);
|
|
340
|
-
}
|
|
341
|
-
}}
|
|
338
|
+
{isRange && (
|
|
339
|
+
<ButtonGroup
|
|
340
|
+
size={SIZE.compact}
|
|
341
|
+
mode={MODE.radio}
|
|
342
|
+
selected={operatorIndex}
|
|
343
|
+
onClick={(_, index) => setOperatorIndex(index)}
|
|
342
344
|
overrides={{
|
|
343
|
-
InnerThumb: function InnerThumb({$value, $thumbIndex}) {
|
|
344
|
-
return <React.Fragment>{$value[$thumbIndex]}</React.Fragment>;
|
|
345
|
-
},
|
|
346
|
-
TickBar: ({$min, $max}) => null, // we don't want the ticks
|
|
347
|
-
ThumbValue: () => null,
|
|
348
345
|
Root: {
|
|
349
|
-
style: () => ({
|
|
350
|
-
// Aligns the center of the slider handles with the histogram bars
|
|
351
|
-
width: 'calc(100% + 14px)',
|
|
352
|
-
margin: '0 -7px',
|
|
353
|
-
}),
|
|
354
|
-
},
|
|
355
|
-
InnerTrack: {
|
|
356
|
-
style: ({$theme}) => {
|
|
357
|
-
if (!isRange) {
|
|
358
|
-
return {
|
|
359
|
-
// For range selection we use the color as is, but when selecting the single value,
|
|
360
|
-
// we don't want the track standing out, so mute its color
|
|
361
|
-
background: theme.colors.mono400,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
Thumb: {
|
|
367
|
-
style: () => ({
|
|
368
|
-
// Slider handles are small enough to visually be centered within each histogram bar
|
|
369
|
-
height: '18px',
|
|
370
|
-
width: '18px',
|
|
371
|
-
fontSize: '0px',
|
|
372
|
-
}),
|
|
346
|
+
style: ({$theme}) => ({marginBottom: $theme.sizing.scale500}),
|
|
373
347
|
},
|
|
374
348
|
}}
|
|
375
|
-
|
|
376
|
-
|
|
349
|
+
>
|
|
350
|
+
<Button
|
|
351
|
+
type="button"
|
|
352
|
+
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
353
|
+
>
|
|
354
|
+
<
|
|
355
|
+
</Button>
|
|
356
|
+
<Button
|
|
357
|
+
type="button"
|
|
358
|
+
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
359
|
+
>
|
|
360
|
+
>
|
|
361
|
+
</Button>
|
|
362
|
+
<Button
|
|
363
|
+
type="button"
|
|
364
|
+
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
365
|
+
>
|
|
366
|
+
≤
|
|
367
|
+
</Button>
|
|
368
|
+
<Button
|
|
369
|
+
type="button"
|
|
370
|
+
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
371
|
+
>
|
|
372
|
+
≥
|
|
373
|
+
</Button>
|
|
374
|
+
<Button
|
|
375
|
+
type="button"
|
|
376
|
+
overrides={{BaseButton: {style: {width: '100%'}}}}
|
|
377
|
+
>
|
|
378
|
+
=
|
|
379
|
+
</Button>
|
|
380
|
+
</ButtonGroup>
|
|
381
|
+
)}
|
|
382
|
+
|
|
377
383
|
<div
|
|
378
384
|
className={css({
|
|
379
385
|
display: 'flex',
|
|
380
|
-
marginTop: theme.sizing.scale400,
|
|
381
|
-
// This % gap is visually appealing given the filter box width
|
|
382
|
-
gap: '30%',
|
|
383
386
|
justifyContent: 'space-between',
|
|
387
|
+
marginLeft: theme.sizing.scale300,
|
|
388
|
+
marginRight: theme.sizing.scale300,
|
|
384
389
|
})}
|
|
385
390
|
>
|
|
391
|
+
<ParagraphXSmall>{format(min, props.options)}</ParagraphXSmall>{' '}
|
|
392
|
+
<ParagraphXSmall>{format(max, props.options)}</ParagraphXSmall>
|
|
393
|
+
</div>
|
|
394
|
+
|
|
395
|
+
<div className={css({display: 'flex', justifyContent: 'space-between'})}>
|
|
386
396
|
<Input
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
value={
|
|
397
|
+
size={INPUT_SIZE.compact}
|
|
398
|
+
overrides={{Root: {style: {width: isRange ? '152px' : '100%'}}}}
|
|
399
|
+
disabled={leftDisabled}
|
|
400
|
+
inputRef={leftInputRef}
|
|
401
|
+
value={left}
|
|
392
402
|
onChange={event => {
|
|
393
403
|
if (validateInput(event.target.value)) {
|
|
394
|
-
|
|
395
|
-
? // $FlowFixMe - we know it is a number by now
|
|
396
|
-
setLower(event.target.value)
|
|
397
|
-
: // $FlowFixMe - we know it is a number by now
|
|
398
|
-
setSingle(event.target.value);
|
|
404
|
+
setLeft(event.target.value);
|
|
399
405
|
}
|
|
400
406
|
}}
|
|
401
|
-
onFocus={() => setFocus(true)}
|
|
402
|
-
onBlur={() => setFocus(false)}
|
|
403
407
|
/>
|
|
408
|
+
|
|
404
409
|
{isRange && (
|
|
405
410
|
<Input
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
Root: {style: {width: '100%'}},
|
|
412
|
-
}}
|
|
413
|
-
value={inputValueUpper}
|
|
411
|
+
size={INPUT_SIZE.compact}
|
|
412
|
+
overrides={{Root: {style: {width: '152px'}}}}
|
|
413
|
+
disabled={rightDisabled}
|
|
414
|
+
inputRef={rightInputRef}
|
|
415
|
+
value={right}
|
|
414
416
|
onChange={event => {
|
|
415
417
|
if (validateInput(event.target.value)) {
|
|
416
|
-
|
|
417
|
-
setUpper(event.target.value);
|
|
418
|
+
setRight(event.target.value);
|
|
418
419
|
}
|
|
419
420
|
}}
|
|
420
|
-
onFocus={() => setFocus(true)}
|
|
421
|
-
onBlur={() => setFocus(false)}
|
|
422
421
|
/>
|
|
423
422
|
)}
|
|
424
423
|
</div>
|
|
@@ -481,9 +480,24 @@ function NumericalColumn(options: OptionsT): NumericalColumnT {
|
|
|
481
480
|
kind: COLUMNS.NUMERICAL,
|
|
482
481
|
buildFilter: function(params) {
|
|
483
482
|
return function(data) {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
483
|
+
const included = params.comparisons.some(c => {
|
|
484
|
+
const left = roundToFixed(data, normalizedOptions.precision);
|
|
485
|
+
const right = roundToFixed(c.value, normalizedOptions.precision);
|
|
486
|
+
switch (c.operation) {
|
|
487
|
+
case NUMERICAL_OPERATIONS.EQ:
|
|
488
|
+
return left === right;
|
|
489
|
+
case NUMERICAL_OPERATIONS.GT:
|
|
490
|
+
return left > right;
|
|
491
|
+
case NUMERICAL_OPERATIONS.GTE:
|
|
492
|
+
return left >= right;
|
|
493
|
+
case NUMERICAL_OPERATIONS.LT:
|
|
494
|
+
return left < right;
|
|
495
|
+
case NUMERICAL_OPERATIONS.LTE:
|
|
496
|
+
return left <= right;
|
|
497
|
+
default:
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
});
|
|
487
501
|
return params.exclude ? !included : included;
|
|
488
502
|
};
|
|
489
503
|
},
|