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.
@@ -17,6 +17,8 @@ import { LocaleContext } from '../locale/index.js'; // consider pulling this out
17
17
 
18
18
  const HEADER_ROW_HEIGHT = 48;
19
19
 
20
+ const sum = ns => ns.reduce((s, n) => s + n, 0);
21
+
20
22
  function CellPlacement({
21
23
  columnIndex,
22
24
  rowIndex,
@@ -255,7 +257,7 @@ function Header(props) {
255
257
  }))));
256
258
  }
257
259
 
258
- function Headers(props) {
260
+ function Headers() {
259
261
  const [css, theme] = useStyletron();
260
262
  const locale = React.useContext(LocaleContext);
261
263
  const ctx = React.useContext(HeaderContext);
@@ -265,7 +267,7 @@ function Headers(props) {
265
267
  position: 'sticky',
266
268
  top: 0,
267
269
  left: 0,
268
- width: `${ctx.widths.reduce((sum, w) => sum + w, 0)}px`,
270
+ width: `${sum(ctx.widths)}px`,
269
271
  height: `${HEADER_ROW_HEIGHT}px`,
270
272
  display: 'flex',
271
273
  // this feels bad.. the absolutely positioned children elements
@@ -467,8 +469,10 @@ export function DataTable({
467
469
  }
468
470
 
469
471
  return rowHeight;
470
- }, [rowHeight]);
471
- const gridRef = React.useRef(null);
472
+ }, [rowHeight]); // We use state for our ref, to allow hooks to update when the ref changes.
473
+ // eslint-disable-next-line flowtype/no-weak-types
474
+
475
+ const [gridRef, setGridRef] = React.useState(null);
472
476
  const [measuredWidths, setMeasuredWidths] = React.useState(columns.map(() => 0));
473
477
  const [resizeDeltas, setResizeDeltas] = React.useState(columns.map(() => 0));
474
478
  React.useEffect(() => {
@@ -480,11 +484,11 @@ export function DataTable({
480
484
  });
481
485
  }, [columns]);
482
486
  const resetAfterColumnIndex = React.useCallback(columnIndex => {
483
- if (gridRef.current) {
487
+ if (gridRef) {
484
488
  // $FlowFixMe trigger react-window to layout the elements again
485
- gridRef.current.resetAfterColumnIndex(columnIndex, true);
489
+ gridRef.resetAfterColumnIndex(columnIndex, true);
486
490
  }
487
- }, [gridRef.current]);
491
+ }, [gridRef]);
488
492
  const handleWidthsChange = React.useCallback(nextWidths => {
489
493
  setMeasuredWidths(nextWidths);
490
494
  resetAfterColumnIndex(0);
@@ -594,13 +598,10 @@ export function DataTable({
594
598
  }, [sortedIndices, filteredIndices, onIncludedRowsChange, allRows]);
595
599
  const [browserScrollbarWidth, setBrowserScrollbarWidth] = React.useState(0);
596
600
  const normalizedWidths = React.useMemo(() => {
597
- const sum = ns => ns.reduce((s, n) => s + n, 0);
598
-
599
601
  const resizedWidths = measuredWidths.map((w, i) => Math.floor(w) + Math.floor(resizeDeltas[i]));
600
602
 
601
- if (gridRef.current) {
602
- // $FlowFixMe
603
- const gridProps = gridRef.current.props;
603
+ if (gridRef) {
604
+ const gridProps = gridRef.props;
604
605
  let isContentTallerThanContainer = false;
605
606
  let visibleRowHeight = 0;
606
607
 
@@ -635,7 +636,7 @@ export function DataTable({
635
636
  }
636
637
 
637
638
  return resizedWidths;
638
- }, [measuredWidths, resizeDeltas, browserScrollbarWidth, rows.length, columns]);
639
+ }, [gridRef, measuredWidths, resizeDeltas, browserScrollbarWidth, rows.length, columns]);
639
640
  const isSelectable = batchActions ? !!batchActions.length : false;
640
641
  const isSelectedAll = React.useMemo(() => {
641
642
  if (!selectedRowIds) {
@@ -684,10 +685,10 @@ export function DataTable({
684
685
  function handleRowHighlightIndexChange(nextIndex) {
685
686
  setRowHighlightIndex(nextIndex);
686
687
 
687
- if (gridRef.current) {
688
+ if (gridRef) {
688
689
  if (nextIndex >= 0) {
689
690
  // $FlowFixMe - unable to get react-window types
690
- gridRef.current.scrollToItem({
691
+ gridRef.scrollToItem({
691
692
  rowIndex: nextIndex
692
693
  });
693
694
  }
@@ -776,8 +777,9 @@ export function DataTable({
776
777
  }
777
778
  }, /*#__PURE__*/React.createElement(VariableSizeGrid // eslint-disable-next-line flowtype/no-weak-types
778
779
  , {
779
- ref: gridRef,
780
+ ref: setGridRef,
780
781
  overscanRowCount: 10,
782
+ overscanColumnCount: 5,
781
783
  innerElementType: InnerTableElement,
782
784
  columnCount: columns.length,
783
785
  columnWidth: columnIndex => normalizedWidths[columnIndex],
@@ -9,10 +9,26 @@ import { Button, SIZE as BUTTON_SIZE } from '../button/index.js';
9
9
  import { Checkbox, STYLE_TYPE } from '../checkbox/index.js';
10
10
  import { useStyletron } from '../styles/index.js';
11
11
  import { LocaleContext } from '../locale/index.js';
12
+ import { FILTER_SHELL_WIDTH } from './constants.js';
12
13
 
13
14
  function FilterShell(props) {
14
15
  const [css, theme] = useStyletron();
15
16
  const locale = React.useContext(LocaleContext);
17
+ let excludeText;
18
+
19
+ switch (props.excludeKind) {
20
+ case 'value':
21
+ excludeText = locale.datatable.filterExcludeValue;
22
+ break;
23
+
24
+ case 'range':
25
+ excludeText = locale.datatable.filterExcludeRange;
26
+ break;
27
+
28
+ default:
29
+ excludeText = locale.datatable.filterExclude;
30
+ }
31
+
16
32
  return /*#__PURE__*/React.createElement("form", {
17
33
  className: css({
18
34
  backgroundColor: theme.colors.backgroundPrimary,
@@ -20,7 +36,7 @@ function FilterShell(props) {
20
36
  paddingRight: theme.sizing.scale600,
21
37
  paddingBottom: theme.sizing.scale600,
22
38
  paddingLeft: theme.sizing.scale600,
23
- width: '320px'
39
+ width: FILTER_SHELL_WIDTH
24
40
  }),
25
41
  onSubmit: event => {
26
42
  event.preventDefault();
@@ -28,17 +44,23 @@ function FilterShell(props) {
28
44
  }
29
45
  }, props.children, /*#__PURE__*/React.createElement("div", {
30
46
  className: css({
31
- alignItems: 'center',
32
47
  display: 'flex',
48
+ flexDirection: 'column',
33
49
  justifyContent: 'space-between',
34
- marginTop: theme.sizing.scale600
50
+ alignItems: 'flex-end',
51
+ marginTop: theme.sizing.scale600,
52
+ gap: theme.sizing.scale200
53
+ })
54
+ }, /*#__PURE__*/React.createElement("div", {
55
+ className: css({
56
+ alignSelf: 'flex-start'
35
57
  })
36
58
  }, /*#__PURE__*/React.createElement(Checkbox, {
37
59
  checked: props.exclude,
38
60
  onChange: props.onExcludeChange,
39
61
  checkmarkType: STYLE_TYPE.toggle_round,
40
62
  labelPlacement: "right"
41
- }, locale.datatable.filterExclude), /*#__PURE__*/React.createElement(Button, {
63
+ }, excludeText)), /*#__PURE__*/React.createElement(Button, {
42
64
  size: BUTTON_SIZE.compact,
43
65
  type: "submit"
44
66
  }, locale.datatable.filterApply)));
@@ -6,18 +6,20 @@ LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  const locale = {
8
8
  emptyState: 'No rows match the filter criteria defined. Please remove one or more filters to view more data.',
9
- loadingState: 'Loading Rows.',
9
+ loadingState: 'Loading rows.',
10
10
  searchAriaLabel: 'Search by text',
11
11
  filterAdd: 'Add Filter',
12
12
  filterExclude: 'Exclude',
13
13
  filterApply: 'Apply',
14
+ filterExcludeRange: 'Exclude range',
15
+ filterExcludeValue: 'Exclude value',
14
16
  filterAppliedTo: 'filter applied to',
15
17
  optionsLabel: 'Select column to filter by',
16
18
  optionsSearch: 'Search for a column to filter by...',
17
19
  optionsEmpty: 'No columns available.',
18
20
  categoricalFilterSelectAll: 'Select All',
19
21
  categoricalFilterSelectClear: 'Clear',
20
- categoricalFilterEmpty: 'No Categories Found',
22
+ categoricalFilterEmpty: 'No categories found',
21
23
  datetimeFilterRange: 'Range',
22
24
  datetimeFilterRangeDatetime: 'Date, Time',
23
25
  datetimeFilterRangeDate: 'Date',
@@ -7,40 +7,61 @@ LICENSE file in the root directory of this source tree.
7
7
  import * as React from 'react';
8
8
  import { useStyletron } from '../styles/index.js';
9
9
  import HeaderCell from './header-cell.js';
10
+ import { useRef } from 'react'; // Measures the column header + sampled data
10
11
 
11
- // https://github.com/Swizec/useDimensions
12
- function useDimensions() {
13
- const [dimensions, setDimensions] = React.useState({});
14
- const [node, setNode] = React.useState(null);
15
- const ref = React.useCallback(node => {
16
- setNode(node);
17
- }, []);
12
+ function MeasureColumn({
13
+ sampleIndexes,
14
+ column,
15
+ columnIndex,
16
+ rows,
17
+ isSelectable,
18
+ onLayout
19
+ }) {
20
+ const [css] = useStyletron();
21
+ const ref = useRef();
18
22
  React.useEffect(() => {
19
23
  if (typeof document !== 'undefined') {
20
- if (node) {
21
- window.requestAnimationFrame(() => {
22
- setDimensions(node.getBoundingClientRect());
23
- });
24
+ if (ref.current) {
25
+ onLayout(columnIndex, ref.current.getBoundingClientRect());
24
26
  }
25
27
  }
26
- }, [node]);
27
- return [ref, dimensions];
28
- }
29
-
30
- function ElementMeasurer(props) {
31
- const {
32
- onDimensionsChange
33
- } = props;
34
- const [ref, dimensions] = useDimensions();
35
- React.useEffect(() => {
36
- onDimensionsChange(dimensions);
37
- }, [dimensions, onDimensionsChange]);
38
- return /*#__PURE__*/React.cloneElement(props.item, {
39
- ref
40
- });
28
+ }, []);
29
+ return /*#__PURE__*/React.createElement("div", {
30
+ ref: ref,
31
+ className: css({
32
+ display: 'flex',
33
+ flexDirection: 'column',
34
+ width: 'fit-content'
35
+ })
36
+ }, /*#__PURE__*/React.createElement(HeaderCell, {
37
+ index: columnIndex,
38
+ isHovered: true,
39
+ isMeasured: true,
40
+ isSelectedAll: false,
41
+ isSelectedIndeterminate: false,
42
+ onMouseEnter: () => {},
43
+ onMouseLeave: () => {},
44
+ onSelectAll: () => {},
45
+ onSelectNone: () => {},
46
+ onSort: i => {},
47
+ sortable: column.sortable,
48
+ sortDirection: null,
49
+ title: column.title,
50
+ isSelectable: isSelectable
51
+ }), sampleIndexes.map((rowIndex, i) => {
52
+ const Cell = column.renderCell;
53
+ return /*#__PURE__*/React.createElement(Cell, {
54
+ key: `measure-${i}`,
55
+ value: column.mapDataToValue(rows[rowIndex].data),
56
+ isSelectable: isSelectable,
57
+ isMeasured: true,
58
+ sortable: column.sortable,
59
+ x: 0,
60
+ y: rowIndex
61
+ });
62
+ }));
41
63
  }
42
64
 
43
- // sample size could likely be generated based on row count, to have higher confidence
44
65
  const MAX_SAMPLE_SIZE = 50;
45
66
 
46
67
  function generateSampleIndices(inputMin, inputMax, maxSamples) {
@@ -77,43 +98,35 @@ export default function MeasureColumnWidths({
77
98
  onWidthsChange
78
99
  }) {
79
100
  const [css] = useStyletron();
80
- const measurementCount = React.useRef(0);
81
- const dimensionsCache = React.useRef(widths);
101
+ const widthMap = React.useMemo(() => {
102
+ return new Map();
103
+ }, []);
82
104
  const sampleSize = rows.length < MAX_SAMPLE_SIZE ? rows.length : MAX_SAMPLE_SIZE;
83
105
  const finishedMeasurementCount = (sampleSize + 1) * columns.length;
84
- const sampleRowIndicesByColumn = React.useMemo(() => {
85
- measurementCount.current = 0;
86
- dimensionsCache.current = widths;
87
- const indices = generateSampleIndices(0, rows.length - 1, sampleSize);
88
- return columns.map(() => indices);
106
+ const sampleIndexes = React.useMemo(() => {
107
+ return generateSampleIndices(0, rows.length - 1, sampleSize);
89
108
  }, [columns, rows, widths, sampleSize]);
90
- const handleDimensionsChange = React.useCallback((columnIndex, rowIndex, dimensions) => {
91
- if (dimensions.width === undefined) return;
92
-
93
- if (columns[columnIndex] === undefined || dimensionsCache.current[columnIndex] === undefined) {
94
- return;
95
- }
96
-
97
- measurementCount.current += 1;
98
- const nextWidth = Math.min(Math.max(columns[columnIndex].minWidth || 0, dimensionsCache.current[columnIndex], dimensions.width + 1), columns[columnIndex].maxWidth || Infinity);
109
+ const handleDimensionsChange = React.useCallback((columnIndex, dimensions) => {
110
+ const nextWidth = Math.min(Math.max(columns[columnIndex].minWidth || 0, widthMap.get(columnIndex) || 0, dimensions.width + 1), columns[columnIndex].maxWidth || Infinity);
99
111
 
100
- if (nextWidth !== dimensionsCache.current[columnIndex]) {
101
- const nextWidths = [...dimensionsCache.current];
102
- nextWidths[columnIndex] = nextWidth;
103
- dimensionsCache.current = nextWidths;
112
+ if (nextWidth !== widthMap.get(columnIndex)) {
113
+ widthMap.set(columnIndex, nextWidth);
104
114
  }
105
115
 
106
- if (measurementCount.current >= finishedMeasurementCount) {
107
- onWidthsChange(dimensionsCache.current);
116
+ if ( // Refresh at 100% of done
117
+ widthMap.size === columns.length || // ...50%
118
+ widthMap.size === Math.floor(columns.length / 2) || // ...25%
119
+ widthMap.size === Math.floor(columns.length / 4)) {
120
+ onWidthsChange(Array.from(widthMap.values()));
108
121
  }
109
122
  }, [columns, finishedMeasurementCount, onWidthsChange]);
110
123
  const hiddenStyle = css({
111
124
  position: 'absolute',
112
125
  overflow: 'hidden',
113
126
  height: 0
114
- });
127
+ }); // Remove the measurement nodes after we are done updating our column width
115
128
 
116
- if (measurementCount.current >= finishedMeasurementCount) {
129
+ if (widthMap.size === columns.length) {
117
130
  return null;
118
131
  }
119
132
 
@@ -124,38 +137,16 @@ export default function MeasureColumnWidths({
124
137
  className: hiddenStyle,
125
138
  "aria-hidden": true,
126
139
  role: "none"
127
- }, sampleRowIndicesByColumn.map((rowIndices, columnIndex) => {
128
- const Cell = columns[columnIndex].renderCell;
129
- return rowIndices.map(rowIndex => /*#__PURE__*/React.createElement(ElementMeasurer, {
130
- key: `measure-${columnIndex}-${rowIndex}`,
131
- onDimensionsChange: dimensions => handleDimensionsChange(columnIndex, rowIndex, dimensions),
132
- item: /*#__PURE__*/React.createElement(Cell, {
133
- value: columns[columnIndex].mapDataToValue(rows[rowIndex].data),
134
- isMeasured: true,
135
- onSelect: isSelectable && columnIndex === 0 ? () => {} : undefined,
136
- x: columnIndex,
137
- y: rowIndex
138
- })
139
- }));
140
- }), columns.map((column, columnIndex) => /*#__PURE__*/React.createElement(ElementMeasurer, {
141
- key: `measure-column-${columnIndex}`,
142
- onDimensionsChange: dimensions => handleDimensionsChange(columnIndex, -1, dimensions),
143
- item: /*#__PURE__*/React.createElement(HeaderCell, {
144
- index: columnIndex,
145
- isHovered: true,
146
- isMeasured: true,
147
- isSelectable: isSelectable && columnIndex === 0,
148
- isSelectedAll: false,
149
- isSelectedIndeterminate: false,
150
- onMouseEnter: () => {},
151
- onMouseLeave: () => {},
152
- onSelectAll: () => {},
153
- onSelectNone: () => {},
154
- onSort: i => {},
155
- sortable: column.sortable,
156
- sortDirection: null,
157
- title: column.title
158
- })
159
- })))
140
+ }, columns.map((column, i) => {
141
+ return /*#__PURE__*/React.createElement(MeasureColumn, {
142
+ key: column.title + i,
143
+ column: column,
144
+ rows: rows,
145
+ isSelectable: isSelectable,
146
+ onLayout: handleDimensionsChange,
147
+ columnIndex: i,
148
+ sampleIndexes: sampleIndexes
149
+ });
150
+ }))
160
151
  );
161
152
  }
@@ -32,7 +32,7 @@ class TimezonePicker extends React.Component {
32
32
  try {
33
33
  const offset = getTimezoneOffset(zoneName, compareDate) / 3_600_000;
34
34
  const offsetFormatted = `${offset >= 0 ? '+' : '-'}${Math.abs(offset)}`;
35
- let label = `(GMT${offsetFormatted}) ${zoneName.replace('_', ' ')}`;
35
+ let label = `(GMT${offsetFormatted}) ${zoneName.replace(/_/g, ' ')}`;
36
36
 
37
37
  if (this.props.includeAbbreviations) {
38
38
  const abbreviation = format(compareDate, 'zzz', {