baseui 10.8.0 → 10.9.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.
Files changed (170) hide show
  1. package/a11y/a11y.js +2 -2
  2. package/a11y/a11y.js.flow +3 -3
  3. package/button/styled-components.js +47 -18
  4. package/button/styled-components.js.flow +25 -5
  5. package/combobox/combobox.js +6 -3
  6. package/combobox/combobox.js.flow +4 -2
  7. package/combobox/types.js.flow +2 -0
  8. package/data-table/column-categorical.js +1 -1
  9. package/data-table/column-categorical.js.flow +2 -2
  10. package/data-table/column-numerical.js +307 -355
  11. package/data-table/column-numerical.js.flow +273 -287
  12. package/data-table/constants.js +17 -11
  13. package/data-table/constants.js.flow +11 -8
  14. package/data-table/data-table.js +53 -50
  15. package/data-table/data-table.js.flow +18 -13
  16. package/data-table/filter-shell.js +27 -4
  17. package/data-table/filter-shell.js.flow +33 -9
  18. package/data-table/locale.js +4 -2
  19. package/data-table/locale.js.flow +6 -2
  20. package/data-table/measure-column-widths.js +83 -121
  21. package/data-table/measure-column-widths.js.flow +87 -109
  22. package/datepicker/styled-components.js +1 -1
  23. package/datepicker/styled-components.js.flow +4 -1
  24. package/drawer/drawer.js +3 -1
  25. package/drawer/drawer.js.flow +7 -1
  26. package/es/a11y/a11y.js +2 -2
  27. package/es/button/styled-components.js +32 -2
  28. package/es/combobox/combobox.js +6 -3
  29. package/es/data-table/column-categorical.js +2 -2
  30. package/es/data-table/column-numerical.js +245 -317
  31. package/es/data-table/constants.js +12 -8
  32. package/es/data-table/data-table.js +18 -16
  33. package/es/data-table/filter-shell.js +26 -4
  34. package/es/data-table/locale.js +4 -2
  35. package/es/data-table/measure-column-widths.js +75 -86
  36. package/es/datepicker/styled-components.js +1 -1
  37. package/es/drawer/drawer.js +3 -1
  38. package/es/index.js +1 -1
  39. package/es/map-marker/badge-enhancer.js +61 -0
  40. package/es/map-marker/constants.js +146 -2
  41. package/es/map-marker/drag-shadow.js +32 -0
  42. package/es/map-marker/fixed-marker.js +54 -48
  43. package/es/map-marker/floating-marker.js +21 -12
  44. package/es/map-marker/index.js +1 -1
  45. package/es/map-marker/label-enhancer.js +39 -0
  46. package/es/map-marker/needle.js +26 -0
  47. package/es/map-marker/pin-head.js +42 -40
  48. package/es/map-marker/styled-components.js +177 -32
  49. package/es/map-marker/types.js +1 -1
  50. package/es/menu/maybe-child-menu.js +0 -2
  51. package/es/menu/nested-menus.js +49 -3
  52. package/es/menu/stateful-container.js +13 -12
  53. package/es/modal/modal.js +3 -1
  54. package/es/popover/popover.js +3 -1
  55. package/es/progress-bar/index.js +1 -1
  56. package/es/progress-bar/progressbar.js +25 -10
  57. package/es/progress-bar/styled-components.js +9 -5
  58. package/es/select/select-component.js +2 -10
  59. package/es/spinner/styled-components.js +34 -16
  60. package/es/table/filter.js +3 -1
  61. package/es/themes/dark-theme/color-component-tokens.js +4 -0
  62. package/es/themes/light-theme/color-component-tokens.js +4 -0
  63. package/es/timezonepicker/timezone-picker.js +53 -36
  64. package/es/timezonepicker/tzdata.js +2 -0
  65. package/es/timezonepicker/update-tzdata.js +69 -0
  66. package/esm/a11y/a11y.js +3 -3
  67. package/esm/button/styled-components.js +47 -18
  68. package/esm/combobox/combobox.js +6 -3
  69. package/esm/data-table/column-categorical.js +2 -2
  70. package/esm/data-table/column-numerical.js +304 -353
  71. package/esm/data-table/constants.js +12 -8
  72. package/esm/data-table/data-table.js +53 -50
  73. package/esm/data-table/filter-shell.js +26 -4
  74. package/esm/data-table/locale.js +4 -2
  75. package/esm/data-table/measure-column-widths.js +83 -121
  76. package/esm/datepicker/styled-components.js +1 -1
  77. package/esm/drawer/drawer.js +3 -1
  78. package/esm/index.js +1 -1
  79. package/esm/map-marker/badge-enhancer.js +79 -0
  80. package/esm/map-marker/constants.js +94 -4
  81. package/esm/map-marker/drag-shadow.js +53 -0
  82. package/esm/map-marker/fixed-marker.js +84 -80
  83. package/esm/map-marker/floating-marker.js +22 -13
  84. package/esm/map-marker/index.js +1 -1
  85. package/esm/map-marker/label-enhancer.js +60 -0
  86. package/esm/map-marker/needle.js +43 -0
  87. package/esm/map-marker/pin-head.js +77 -66
  88. package/esm/map-marker/styled-components.js +182 -51
  89. package/esm/map-marker/types.js +1 -1
  90. package/esm/menu/maybe-child-menu.js +0 -2
  91. package/esm/menu/nested-menus.js +66 -5
  92. package/esm/menu/stateful-container.js +15 -13
  93. package/esm/modal/modal.js +3 -1
  94. package/esm/popover/popover.js +3 -1
  95. package/esm/progress-bar/index.js +1 -1
  96. package/esm/progress-bar/progressbar.js +32 -10
  97. package/esm/progress-bar/styled-components.js +9 -4
  98. package/esm/select/select-component.js +2 -11
  99. package/esm/spinner/styled-components.js +35 -16
  100. package/esm/table/filter.js +3 -1
  101. package/esm/themes/dark-theme/color-component-tokens.js +4 -0
  102. package/esm/themes/light-theme/color-component-tokens.js +4 -0
  103. package/esm/timezonepicker/timezone-picker.js +64 -36
  104. package/esm/timezonepicker/tzdata.js +2 -0
  105. package/esm/timezonepicker/update-tzdata.js +160 -0
  106. package/index.js +6 -0
  107. package/index.js.flow +1 -1
  108. package/map-marker/badge-enhancer.js +90 -0
  109. package/map-marker/badge-enhancer.js.flow +86 -0
  110. package/map-marker/constants.js +103 -5
  111. package/map-marker/constants.js.flow +152 -0
  112. package/map-marker/drag-shadow.js +64 -0
  113. package/map-marker/drag-shadow.js.flow +52 -0
  114. package/map-marker/fixed-marker.js +84 -78
  115. package/map-marker/fixed-marker.js.flow +78 -66
  116. package/map-marker/floating-marker.js +22 -13
  117. package/map-marker/floating-marker.js.flow +30 -17
  118. package/map-marker/index.d.ts +125 -24
  119. package/map-marker/index.js +18 -0
  120. package/map-marker/index.js.flow +3 -0
  121. package/map-marker/label-enhancer.js +71 -0
  122. package/map-marker/label-enhancer.js.flow +63 -0
  123. package/map-marker/needle.js +54 -0
  124. package/map-marker/needle.js.flow +29 -0
  125. package/map-marker/pin-head.js +80 -69
  126. package/map-marker/pin-head.js.flow +122 -84
  127. package/map-marker/styled-components.js +200 -62
  128. package/map-marker/styled-components.js.flow +172 -22
  129. package/map-marker/types.js.flow +69 -20
  130. package/menu/index.d.ts +9 -4
  131. package/menu/maybe-child-menu.js +0 -2
  132. package/menu/maybe-child-menu.js.flow +0 -2
  133. package/menu/nested-menus.js +66 -5
  134. package/menu/nested-menus.js.flow +50 -5
  135. package/menu/stateful-container.js +15 -13
  136. package/menu/stateful-container.js.flow +19 -13
  137. package/menu/types.js.flow +7 -1
  138. package/modal/modal.js +3 -1
  139. package/modal/modal.js.flow +2 -0
  140. package/package.json +5 -4
  141. package/popover/popover.js +3 -1
  142. package/popover/popover.js.flow +2 -0
  143. package/progress-bar/index.d.ts +2 -0
  144. package/progress-bar/index.js +6 -0
  145. package/progress-bar/index.js.flow +1 -0
  146. package/progress-bar/progressbar.js +32 -10
  147. package/progress-bar/progressbar.js.flow +35 -9
  148. package/progress-bar/styled-components.js +9 -4
  149. package/progress-bar/styled-components.js.flow +15 -4
  150. package/progress-bar/types.js.flow +12 -2
  151. package/select/select-component.js +2 -11
  152. package/select/select-component.js.flow +5 -7
  153. package/spinner/styled-components.js +35 -16
  154. package/spinner/styled-components.js.flow +37 -19
  155. package/spinner/types.js.flow +10 -0
  156. package/styles/index.js.flow +1 -1
  157. package/table/filter.js +3 -1
  158. package/table/filter.js.flow +5 -1
  159. package/themes/dark-theme/color-component-tokens.js +4 -0
  160. package/themes/dark-theme/color-component-tokens.js.flow +4 -0
  161. package/themes/light-theme/color-component-tokens.js +4 -0
  162. package/themes/light-theme/color-component-tokens.js.flow +4 -0
  163. package/themes/types.js.flow +4 -0
  164. package/timezonepicker/timezone-picker.js +69 -41
  165. package/timezonepicker/timezone-picker.js.flow +52 -46
  166. package/timezonepicker/types.js.flow +1 -1
  167. package/timezonepicker/tzdata.js +10 -0
  168. package/timezonepicker/tzdata.js.flow +347 -0
  169. package/timezonepicker/update-tzdata.js +164 -0
  170. package/timezonepicker/update-tzdata.js.flow +70 -0
@@ -13,7 +13,7 @@ import { Checkbox, StyledLabel } from '../checkbox/index.js';
13
13
  import Search from '../icon/search.js';
14
14
  import { Input, SIZE as INPUT_SIZE } from '../input/index.js';
15
15
  import { useStyletron, withStyle } from '../styles/index.js';
16
- import { Label3 } from '../typography/index.js';
16
+ import { LabelSmall } from '../typography/index.js';
17
17
  import Column from './column.js';
18
18
  import { COLUMNS } from './constants.js';
19
19
  import { LocaleContext } from '../locale/index.js';
@@ -139,7 +139,7 @@ export function CategoricalFilter(props) {
139
139
  overflowY: 'auto',
140
140
  marginTop: theme.sizing.scale600
141
141
  })
142
- }, !filteredCategories.length && /*#__PURE__*/React.createElement(Label3, null, locale.datatable.categoricalFilterEmpty), Boolean(filteredCategories.length) && filteredCategories.map((category, i) => /*#__PURE__*/React.createElement("div", {
142
+ }, !filteredCategories.length && /*#__PURE__*/React.createElement(LabelSmall, null, locale.datatable.categoricalFilterEmpty), Boolean(filteredCategories.length) && filteredCategories.map((category, i) => /*#__PURE__*/React.createElement("div", {
143
143
  className: checkboxStyles,
144
144
  key: i
145
145
  }, /*#__PURE__*/React.createElement(Checkbox, {
@@ -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 { Paragraph4 } from '../typography/index.js';
15
14
  import Column from './column.js';
16
- import { COLUMNS, NUMERICAL_FORMATS, NUMERICAL_OPERATIONS } from './constants.js';
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,170 @@ function validateInput(input) {
62
63
  return Boolean(parseFloat(input)) || input === '' || input === '-';
63
64
  }
64
65
 
65
- function filterParamsToInitialState(filterParams) {
66
- if (filterParams) {
67
- if (filterParams.comparisons.length > 1) {
68
- if (filterParams.comparisons[0].operation === NUMERICAL_OPERATIONS.LT && filterParams.comparisons[1].operation === NUMERICAL_OPERATIONS.GT) {
69
- return {
70
- exclude: !filterParams.exclude,
71
- comparatorIndex: 0,
72
- operatorIndex: 4,
73
- right: filterParams.comparisons[1].value.toString(),
74
- left: filterParams.comparisons[0].value.toString()
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 comparison = filterParams.comparisons[0];
79
-
80
- if (comparison.operation === NUMERICAL_OPERATIONS.LT) {
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
- return {
125
- exclude: false,
126
- comparatorIndex: 0,
127
- operatorIndex: 0,
128
- left: '',
129
- right: ''
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 initialState = filterParamsToInitialState(props.filterParams);
137
- const [exclude, setExclude] = React.useState(initialState.exclude);
138
- const [comparatorIndex, setComparatorIndex] = React.useState(initialState.comparatorIndex);
139
- const [operatorIndex, setOperatorIndex] = React.useState(initialState.operatorIndex);
140
- const [left, setLeft] = React.useState(initialState.left);
141
- const [right, setRight] = React.useState(initialState.right);
142
- const isRange = comparatorIndex === 0;
143
- const min = React.useMemo(() => Math.min(...props.data), [props.data]);
144
- const max = React.useMemo(() => Math.max(...props.data), [props.data]);
145
- React.useEffect(() => {
146
- if (!left) {
147
- setLeft(min.toString());
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;
148
164
  }
165
+ }); // We use the d3 function to get the extent as it's a little more robust to null, -Infinity, etc.
149
166
 
150
- if (!right) {
151
- setRight(max.toString());
152
- }
153
- }, []);
154
- const [leftDisabled, rightDisabled] = React.useMemo(() => {
155
- if (!isRange) return [false, false];
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.
156
171
 
157
- switch (operatorIndex) {
158
- case 4:
159
- return [false, false];
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.
160
175
 
161
- case 0:
162
- case 2:
163
- return [true, false];
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."
164
179
 
165
- case 1:
166
- case 3:
167
- return [false, true];
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
168
186
 
169
- default:
170
- return [true, true];
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;
191
187
 
192
- case 1:
193
- case 3:
194
- setRight(max.toString());
195
- break;
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 bound the values within our min and max even if a user enters a huge number
193
+
194
+ let sliderValue = isRange ? [Math.max(inputValueLower, min), Math.min(inputValueUpper, max)] : [Math.min(Math.max(inputValueLower, min), max)]; // keep the slider happy by sorting the two values
195
+
196
+ if (isRange && sliderValue[0] > sliderValue[1]) {
197
+ sliderValue = [sliderValue[1], sliderValue[0]];
198
+ }
196
199
 
197
- case 0:
198
- case 2:
199
- setLeft(min.toString());
200
- break;
201
- }
202
- }, [operatorIndex]);
203
200
  return /*#__PURE__*/React.createElement(FilterShell, {
204
201
  exclude: exclude,
205
202
  onExcludeChange: () => setExclude(!exclude),
203
+ excludeKind: excludeKind,
206
204
  onApply: () => {
207
205
  if (isRange) {
208
- switch (operatorIndex) {
209
- case 0:
210
- {
211
- const value = parseFloat(right);
212
- const operation = NUMERICAL_OPERATIONS.LT;
213
- props.setFilter({
214
- comparisons: [{
215
- value,
216
- operation
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
- }
206
+ const lowerValue = parseFloat(inputValueLower);
207
+ const upperValue = parseFloat(inputValueUpper);
208
+ props.setFilter({
209
+ description: `≥ ${lowerValue} and ≤ ${upperValue}`,
210
+ exclude: exclude,
211
+ lowerValue,
212
+ upperValue,
213
+ excludeKind
214
+ });
292
215
  } else {
293
- const value = parseFloat(left);
294
- const operation = NUMERICAL_OPERATIONS.EQ;
216
+ const value = parseFloat(inputValueLower);
295
217
  props.setFilter({
296
- comparisons: [{
297
- value,
298
- operation
299
- }],
300
218
  description: `= ${value}`,
301
- exclude
219
+ exclude: exclude,
220
+ lowerValue: inputValueLower,
221
+ upperValue: inputValueLower,
222
+ excludeKind
302
223
  });
303
224
  }
304
225
 
305
226
  props.close();
306
227
  }
307
228
  }, /*#__PURE__*/React.createElement(ButtonGroup, {
308
- size: SIZE.compact,
229
+ size: SIZE.mini,
309
230
  mode: MODE.radio,
310
231
  selected: comparatorIndex,
311
232
  onClick: (_, index) => setComparatorIndex(index),
@@ -326,7 +247,8 @@ function NumericalFilter(props) {
326
247
  width: '100%'
327
248
  }
328
249
  }
329
- }
250
+ },
251
+ "aria-label": locale.datatable.numericalFilterRange
330
252
  }, locale.datatable.numericalFilterRange), /*#__PURE__*/React.createElement(Button, {
331
253
  type: "button",
332
254
  overrides: {
@@ -335,112 +257,140 @@ function NumericalFilter(props) {
335
257
  width: '100%'
336
258
  }
337
259
  }
338
- }
339
- }, locale.datatable.numericalFilterSingleValue)), isRange && /*#__PURE__*/React.createElement(ButtonGroup, {
340
- size: SIZE.compact,
341
- mode: MODE.radio,
342
- selected: operatorIndex,
343
- onClick: (_, index) => setOperatorIndex(index),
260
+ },
261
+ "aria-label": locale.datatable.numericalFilterSingleValue
262
+ }, locale.datatable.numericalFilterSingleValue)), /*#__PURE__*/React.createElement(Histogram, {
263
+ data: props.data,
264
+ lower: inputValueLower,
265
+ upper: inputValueUpper,
266
+ isRange: isRange,
267
+ exclude: exclude,
268
+ precision: props.options.precision
269
+ }), /*#__PURE__*/React.createElement("div", {
270
+ className: css({
271
+ display: 'flex',
272
+ justifyContent: 'space-between'
273
+ })
274
+ }, /*#__PURE__*/React.createElement(Slider // The slider throws errors when switching between single and two values
275
+ // when it tries to read getThumbDistance on a thumb which is not there anymore
276
+ // if we create a new instance these errors are prevented.
277
+ , {
278
+ key: isRange.toString(),
279
+ min: min,
280
+ max: max,
281
+ value: sliderValue,
282
+ onChange: ({
283
+ value
284
+ }) => {
285
+ if (!value) {
286
+ return;
287
+ }
288
+
289
+ if (isRange) {
290
+ const [lowerValue, upperValue] = value;
291
+ setLower(lowerValue);
292
+ setUpper(upperValue);
293
+ } else {
294
+ const [singleValue] = value;
295
+ setSingle(singleValue);
296
+ }
297
+ },
344
298
  overrides: {
299
+ InnerThumb: function InnerThumb({
300
+ $value,
301
+ $thumbIndex
302
+ }) {
303
+ return /*#__PURE__*/React.createElement(React.Fragment, null, $value[$thumbIndex]);
304
+ },
305
+ TickBar: ({
306
+ $min,
307
+ $max
308
+ }) => null,
309
+ // we don't want the ticks
310
+ ThumbValue: () => null,
345
311
  Root: {
312
+ style: () => ({
313
+ // Aligns the center of the slider handles with the histogram bars
314
+ width: 'calc(100% + 14px)',
315
+ margin: '0 -7px'
316
+ })
317
+ },
318
+ InnerTrack: {
346
319
  style: ({
347
320
  $theme
348
- }) => ({
349
- marginBottom: $theme.sizing.scale500
350
- })
351
- }
352
- }
353
- }, /*#__PURE__*/React.createElement(Button, {
354
- type: "button",
355
- overrides: {
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%'
321
+ }) => {
322
+ if (!isRange) {
323
+ return {
324
+ // For range selection we use the color as is, but when selecting the single value,
325
+ // we don't want the track standing out, so mute its color
326
+ background: theme.colors.mono400
327
+ };
328
+ }
395
329
  }
330
+ },
331
+ Thumb: {
332
+ style: () => ({
333
+ // Slider handles are small enough to visually be centered within each histogram bar
334
+ height: '18px',
335
+ width: '18px',
336
+ fontSize: '0px'
337
+ })
396
338
  }
397
339
  }
398
- }, "=")), /*#__PURE__*/React.createElement("div", {
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(Paragraph4, null, format(min, props.options)), ' ', /*#__PURE__*/React.createElement(Paragraph4, null, format(max, props.options))), /*#__PURE__*/React.createElement("div", {
340
+ })), /*#__PURE__*/React.createElement("div", {
406
341
  className: css({
407
342
  display: 'flex',
343
+ marginTop: theme.sizing.scale400,
344
+ // This % gap is visually appealing given the filter box width
345
+ gap: '30%',
408
346
  justifyContent: 'space-between'
409
347
  })
410
348
  }, /*#__PURE__*/React.createElement(Input, {
411
- size: INPUT_SIZE.compact,
349
+ min: min,
350
+ max: max,
351
+ size: INPUT_SIZE.mini,
412
352
  overrides: {
413
353
  Root: {
414
354
  style: {
415
- width: isRange ? '152px' : '100%'
355
+ width: '100%'
416
356
  }
417
357
  }
418
358
  },
419
- disabled: leftDisabled,
420
- inputRef: leftInputRef,
421
- value: left,
359
+ value: inputValueLower,
422
360
  onChange: event => {
423
361
  if (validateInput(event.target.value)) {
424
- setLeft(event.target.value);
362
+ isRange ? // $FlowFixMe - we know it is a number by now
363
+ setLower(event.target.value) : // $FlowFixMe - we know it is a number by now
364
+ setSingle(event.target.value);
425
365
  }
426
- }
366
+ },
367
+ onFocus: () => setFocus(true),
368
+ onBlur: () => setFocus(false)
427
369
  }), isRange && /*#__PURE__*/React.createElement(Input, {
428
- size: INPUT_SIZE.compact,
370
+ min: min,
371
+ max: max,
372
+ size: INPUT_SIZE.mini,
429
373
  overrides: {
374
+ Input: {
375
+ style: {
376
+ textAlign: 'right'
377
+ }
378
+ },
430
379
  Root: {
431
380
  style: {
432
- width: '152px'
381
+ width: '100%'
433
382
  }
434
383
  }
435
384
  },
436
- disabled: rightDisabled,
437
- inputRef: rightInputRef,
438
- value: right,
385
+ value: inputValueUpper,
439
386
  onChange: event => {
440
387
  if (validateInput(event.target.value)) {
441
- setRight(event.target.value);
388
+ // $FlowFixMe - we know it is a number by now
389
+ setUpper(event.target.value);
442
390
  }
443
- }
391
+ },
392
+ onFocus: () => setFocus(true),
393
+ onBlur: () => setFocus(false)
444
394
  })));
445
395
  }
446
396
 
@@ -485,30 +435,8 @@ function NumericalColumn(options) {
485
435
  kind: COLUMNS.NUMERICAL,
486
436
  buildFilter: function (params) {
487
437
  return function (data) {
488
- const included = params.comparisons.some(c => {
489
- const left = roundToFixed(data, normalizedOptions.precision);
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
- });
438
+ const value = roundToFixed(data, normalizedOptions.precision);
439
+ const included = value >= params.lowerValue && value <= params.upperValue;
512
440
  return params.exclude ? !included : included;
513
441
  };
514
442
  },