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