@majoo-ui/react 2.1.2 → 2.1.3

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.
@@ -10,6 +10,10 @@ import { styled } from '../../config/stitches.config.esm.js';
10
10
  import Box from '../../layout/box/Box.esm.js';
11
11
  import Flex from '../../layout/flex/Flex.esm.js';
12
12
  import { TableContent, TableHead, TableHeaderCellContainer, TableHeadCellContent, TableBody, addOnCellStyle, TableFoot, TableFootRow, TableFootCell } from './Table.esm.js';
13
+ import { resolveCellCss } from './utils/columnStyles.esm.js';
14
+ import { footerValueOf } from './utils/footerValue.esm.js';
15
+ import { HeaderTooltip } from './utils/HeaderTooltip.esm.js';
16
+ import { isHeaderSortable, getAriaSort } from './utils/headerSort.esm.js';
13
17
  import Text from '../text/Text.esm.js';
14
18
  import { ConditionalWrapper } from '../../wrapper/ConditionalWrapper.esm.js';
15
19
  import { ContextMenuTrigger } from '../contextMenu/ContextMenu.esm.js';
@@ -80,7 +84,11 @@ const MobileTable = /*#__PURE__*/React__default.forwardRef(({
80
84
  hideHeader,
81
85
  decoration,
82
86
  rowVirtualizer,
83
- virtualized
87
+ virtualized,
88
+ footerData,
89
+ footerColumnIds,
90
+ headerRowSpan,
91
+ keyId = 'id'
84
92
  }, ref) => {
85
93
  var _a, _b, _c, _d;
86
94
  const {
@@ -178,26 +186,29 @@ const MobileTable = /*#__PURE__*/React__default.forwardRef(({
178
186
  children: jsxs(TableContent, Object.assign({}, getTableProps(), {
179
187
  children: [jsx(TableHead, {
180
188
  hiddenHeader: hideHeader,
181
- children: headerGroups.map(headerGroup => jsx(TableHeadRow, Object.assign({}, headerGroup.getHeaderGroupProps(), {
189
+ children: (headerRowSpan ? headerGroups.slice(-1) : headerGroups).map(headerGroup => jsx(TableHeadRow, Object.assign({}, headerGroup.getHeaderGroupProps(), {
182
190
  children: headerGroup.headers.filter((column, index) => filterMobileHeader(column, index)).map(column => {
183
191
  const headerRender = column.render('Header');
184
- return jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(!column.unsortable ? column.getSortByToggleProps({
192
+ return jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(isHeaderSortable(column) ? column.getSortByToggleProps({
185
193
  title: t('sortColumnTitle', 'Urutkan') || 'Urutkan'
186
194
  }) : {
187
195
  onClick: column.onToggle
188
196
  }), {
189
- children: typeof headerRender === 'string' ? jsx(TableHeaderCellContainer, {
197
+ "aria-sort": getAriaSort(column),
198
+ children: typeof headerRender === 'string' ? jsxs(TableHeaderCellContainer, {
190
199
  isMobile: true,
191
200
  isSortedDesc: column.isSortedDesc,
192
- unsortable: column.unsortable,
201
+ unsortable: !isHeaderSortable(column),
193
202
  isToggleable: !!column.onToggle,
194
203
  isToggled: column.isToggled,
195
- children: jsx(TableHeadCellContent, {
204
+ children: [jsx(TableHeadCellContent, {
196
205
  isMobile: true,
197
206
  heading: "headerTable",
198
207
  color: "secondary",
199
208
  children: headerRender.toUpperCase()
200
- })
209
+ }), column.headerTooltip && jsx(HeaderTooltip, {
210
+ label: column.headerTooltip
211
+ })]
201
212
  }) : headerRender
202
213
  }));
203
214
  })
@@ -281,6 +292,7 @@ const MobileTable = /*#__PURE__*/React__default.forwardRef(({
281
292
  children: cell.render('Cell')
282
293
  });
283
294
  return jsx(TableCell, Object.assign({}, cell.getCellProps(addOnCellStyle(cell.column.id)), {
295
+ css: resolveCellCss(cell),
284
296
  children: cell.column.accessor ? jsxs(Flex, {
285
297
  justify: "between",
286
298
  align: "center",
@@ -341,12 +353,12 @@ const MobileTable = /*#__PURE__*/React__default.forwardRef(({
341
353
  align: "center",
342
354
  justify: "between",
343
355
  gap: 5,
344
- css: {
356
+ css: Object.assign({
345
357
  backgroundColor: '$bgGray',
346
358
  padding: `$spacing-04 $spacing-05 ${'selection' in row.values ? '$spacing-04 $spacing-07' : ''}`,
347
359
  borderBottom: '1pt solid $bgBorder',
348
360
  alignItems: cell.column.accessor ? getAlign(cellAlign) : 'center'
349
- },
361
+ }, resolveCellCss(cell)),
350
362
  children: [jsx(Heading, {
351
363
  css: {
352
364
  padding: '3px 0px'
@@ -383,6 +395,48 @@ const MobileTable = /*#__PURE__*/React__default.forwardRef(({
383
395
  }) : null]
384
396
  }, row.id);
385
397
  })
398
+ }), footerData === null || footerData === void 0 ? void 0 : footerData.map((footerRow, footerRowIndex) => {
399
+ var _a;
400
+ return jsx(TableRow, {
401
+ children: jsx(TableCell, {
402
+ colSpan: visibleColumns.length,
403
+ css: {
404
+ backgroundColor: '$bgGray'
405
+ },
406
+ children: jsx(Flex, {
407
+ direction: "column",
408
+ children: visibleColumns.map((column, index) => {
409
+ // A column without a Footer of its own would only add an empty line.
410
+ if (column.isSubRow || !(footerColumnIds === null || footerColumnIds === void 0 ? void 0 : footerColumnIds.has(column.id))) return null;
411
+ const isTitle = getIsMobileHeader() ? column.isMobileHeader : index === indexDefaultHeader;
412
+ return jsxs(Flex, {
413
+ align: "center",
414
+ justify: "between",
415
+ gap: 5,
416
+ css: Object.assign({
417
+ padding: '$spacing-04 $spacing-05',
418
+ borderBottom: '1pt solid $bgBorder'
419
+ }, column.footerCss),
420
+ children: [!isTitle && jsx(Heading, {
421
+ css: {
422
+ padding: '3px 0px'
423
+ },
424
+ heading: "headerTable",
425
+ color: "secondary",
426
+ children: typeof column.Header === 'string' ? column.Header.toUpperCase() : column.render('Header')
427
+ }), jsx(Paragraph, {
428
+ paragraph: "shortContentBold",
429
+ children: column.render('Footer', {
430
+ footerRow,
431
+ footerRowIndex,
432
+ value: footerValueOf(column, footerRow, footerRowIndex)
433
+ })
434
+ })]
435
+ }, column.id);
436
+ })
437
+ })
438
+ })
439
+ }, (_a = footerRow[keyId]) !== null && _a !== void 0 ? _a : `footer-${footerRowIndex}`);
386
440
  }), (_d = decoration === null || decoration === void 0 ? void 0 : decoration.footerRender) === null || _d === void 0 ? void 0 : _d.call(decoration), isInfiniteScroll && paddingBottom > 0 && jsx("tr", {
387
441
  children: jsx("td", {
388
442
  style: {
@@ -4,6 +4,7 @@ import 'core-js/modules/es.iterator.constructor.js';
4
4
  import 'core-js/modules/es.iterator.filter.js';
5
5
  import 'core-js/modules/es.iterator.for-each.js';
6
6
  import 'core-js/modules/es.iterator.map.js';
7
+ import 'core-js/modules/es.iterator.some.js';
7
8
  import 'core-js/modules/es.json.parse.js';
8
9
  import 'core-js/modules/es.json.stringify.js';
9
10
  import 'core-js/modules/es.map.get-or-insert.js';
@@ -15,12 +16,14 @@ import 'core-js/modules/es.set.is-subset-of.v2.js';
15
16
  import 'core-js/modules/es.set.is-superset-of.v2.js';
16
17
  import 'core-js/modules/es.set.symmetric-difference.v2.js';
17
18
  import 'core-js/modules/es.set.union.v2.js';
19
+ import 'core-js/modules/es.weak-map.get-or-insert.js';
20
+ import 'core-js/modules/es.weak-map.get-or-insert-computed.js';
18
21
  import { __rest } from 'tslib';
19
22
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
20
23
  import * as React from 'react';
21
24
  import { useState, useCallback, useMemo, useRef, useLayoutEffect, useEffect } from 'react';
22
25
  import InfiniteScroll from 'react-infinite-scroller';
23
- import { useTable, useSortBy, useExpanded, usePagination, useRowSelect, useColumnOrder, useMountedLayoutEffect } from 'react-table';
26
+ import { useTable, useSortBy, useExpanded, usePagination, useRowSelect, useColumnOrder, useMountedLayoutEffect, emptyRenderer } from 'react-table';
24
27
  import { SortableElement, SortableContainer, arrayMove } from 'react-sortable-hoc';
25
28
  import Text from '../text/Text.esm.js';
26
29
  import Paragraph from '../paragraph/Paragraph.esm.js';
@@ -37,6 +40,10 @@ import useMediaQuery from '../../utils/useMediaQuery.esm.js';
37
40
  import { ConditionalWrapper } from '../../wrapper/ConditionalWrapper.esm.js';
38
41
  import { ContextMenuTrigger, ContextMenu, ContextMenuItem, ContextMenuSeparator } from '../contextMenu/ContextMenu.esm.js';
39
42
  import MobileTable from './MobileTable.esm.js';
43
+ import { alignCss, headerAlignCss, resolveCellCss } from './utils/columnStyles.esm.js';
44
+ import { footerValueOf } from './utils/footerValue.esm.js';
45
+ import { HeaderTooltip } from './utils/HeaderTooltip.esm.js';
46
+ import { isHeaderSortable, getAriaSort } from './utils/headerSort.esm.js';
40
47
  import { useSticky, getStickyValue, getMarginRight } from 'react-table-sticky';
41
48
  import { useTranslation } from 'react-i18next';
42
49
  import { useVirtual } from 'react-virtual';
@@ -283,6 +290,10 @@ const TableRow = styled('tr', {
283
290
  borderBottom: '1pt solid $bgBorder',
284
291
  '&:hover': {
285
292
  backgroundColor: '$bgGray',
293
+ // Body cells paint a background of their own (white, or a column's cellCss) over the row's.
294
+ '& > td': {
295
+ backgroundColor: '$bgGray'
296
+ },
286
297
  '& *[data-sticky-td="true"]': {
287
298
  backgroundColor: '$bgGray'
288
299
  }
@@ -389,12 +400,6 @@ const stickyShadowStyling = (column, tableScrollPosition, scrollAreaWidth) => {
389
400
  }
390
401
  return {};
391
402
  };
392
- const resolveCellCss = cell => {
393
- const {
394
- cellCss
395
- } = cell.column;
396
- return typeof cellCss === 'function' ? cellCss(cell) : cellCss;
397
- };
398
403
  /**
399
404
  * react-table-sticky only decorates header and body cells, so footer cells work out the same
400
405
  * sticky offset themselves.
@@ -412,6 +417,23 @@ const getStickyFooterProps = (column, flatHeaders) => {
412
417
  'data-sticky-td': true
413
418
  };
414
419
  };
420
+ /**
421
+ * react-table-sticky offsets a sticky column by react-table's `width` of the columns beside it, which
422
+ * stays at its 150px default unless set, so a numeric `colWidth` is passed on as that width.
423
+ */
424
+ const withStickyWidths = columns => {
425
+ const needsWidth = column => {
426
+ var _a;
427
+ return typeof column.colWidth === 'number' && column.width === undefined || ((_a = column.columns) === null || _a === void 0 ? void 0 : _a.some(needsWidth));
428
+ };
429
+ if (!columns.some(needsWidth)) return columns;
430
+ return columns.map(column => Object.assign(Object.assign(Object.assign({}, column), typeof column.colWidth === 'number' && column.width === undefined && {
431
+ width: column.colWidth
432
+ }), column.columns && {
433
+ columns: withStickyWidths(column.columns)
434
+ }));
435
+ };
436
+ const isMissingRowId = value => value === undefined || value === null || value === '';
415
437
  /**
416
438
  * Cells per header row. With `spanRows`, a column outside any group is drawn once across every
417
439
  * header row instead of under the empty placeholder cells react-table puts above it.
@@ -443,15 +465,19 @@ const buildHeaderRows = (headerGroups, spanRows) => {
443
465
  /**
444
466
  * rowSpan of the body cells whose column sets `rowSpan`, looked up by column id then row id. The
445
467
  * first row of a run of equal keys spans the run; the rows it covers get 0 and render no cell. A
446
- * run never crosses an expanded row, whose sub rows sit in between.
468
+ * run never crosses an expanded row, whose sub rows sit in between. `anchors` maps every row of a
469
+ * run to its first row, the one whose cell a hover anywhere in the run highlights.
447
470
  */
448
471
  const getCellRowSpans = (rows, spanColumns) => {
449
472
  const spans = new Map();
473
+ const anchors = new Map();
450
474
  spanColumns.forEach(column => {
451
475
  // Nested by column: joining ids into one string would let row `0` + column `1.x` collide with
452
476
  // sub row `0.1` + column `x`.
453
477
  const columnSpans = new Map();
478
+ const columnAnchors = new Map();
454
479
  spans.set(column.id, columnSpans);
480
+ anchors.set(column.id, columnAnchors);
455
481
  let anchorId = null;
456
482
  let anchorKey;
457
483
  rows.forEach((row, index) => {
@@ -462,14 +488,19 @@ const getCellRowSpans = (rows, spanColumns) => {
462
488
  if (anchorId !== null && hasKey && key === anchorKey && !((_a = rows[index - 1]) === null || _a === void 0 ? void 0 : _a.isExpanded)) {
463
489
  columnSpans.set(anchorId, columnSpans.get(anchorId) + 1);
464
490
  columnSpans.set(row.id, 0);
491
+ columnAnchors.set(row.id, anchorId);
465
492
  return;
466
493
  }
467
494
  anchorId = hasKey ? row.id : null;
468
495
  anchorKey = key;
469
496
  columnSpans.set(row.id, 1);
497
+ columnAnchors.set(row.id, row.id);
470
498
  });
471
499
  });
472
- return spans;
500
+ return {
501
+ spans,
502
+ anchors
503
+ };
473
504
  };
474
505
  const formattingData = (rawData, rowGroup) => {
475
506
  if ((rowGroup === null || rowGroup === void 0 ? void 0 : rowGroup.groupKey) && !(rowGroup === null || rowGroup === void 0 ? void 0 : rowGroup.isManuallyFormatted)) {
@@ -734,6 +765,7 @@ function Table({
734
765
  }, [filterHeader]);
735
766
  const [filteredHeader, setFilteredHeader] = useState([]);
736
767
  const [hasDarkParent, setHasDarkParent] = useState(false);
768
+ const [hoveredRowId, setHoveredRowId] = useState(null);
737
769
  const {
738
770
  t
739
771
  } = useTranslation('MajooUI/table');
@@ -761,18 +793,47 @@ function Table({
761
793
  sortBy: initialSortBy
762
794
  });
763
795
  }
796
+ // Otherwise react-table's default of 10 rows shows until the pageSize effect below runs.
797
+ const initialPageSize = withoutLimit ? totalData : rowLimit;
798
+ if (typeof initialPageSize === 'number') {
799
+ Object.assign(initialState, {
800
+ pageSize: initialPageSize
801
+ });
802
+ }
764
803
  return Object.keys(initialState).length > 0 ? initialState : undefined;
765
804
  };
766
805
  const records = useMemo(() => formattingData(rawData, rowGroup), [rawData, rowGroup]);
806
+ // A row without a `keyId` is identified by its JSON; identical rows would share that id, so every
807
+ // repeat gets an occurrence suffix.
808
+ const fallbackRowIds = useMemo(() => {
809
+ const ids = new WeakMap();
810
+ const occurrences = new Map();
811
+ const visit = rowList => {
812
+ if (!Array.isArray(rowList)) return;
813
+ rowList.forEach(row => {
814
+ var _a;
815
+ if (!row || typeof row !== 'object') return;
816
+ if (isMissingRowId(row[keyId])) {
817
+ const json = JSON.stringify(row);
818
+ const count = (_a = occurrences.get(json)) !== null && _a !== void 0 ? _a : 0;
819
+ occurrences.set(json, count + 1);
820
+ ids.set(row, count ? `${json}#${count}` : json);
821
+ }
822
+ visit(row[subRows]);
823
+ });
824
+ };
825
+ visit(records);
826
+ return ids;
827
+ }, [records, keyId, subRows]);
767
828
  const filteredColumns = useMemo(() => {
768
829
  if (draggable) {
769
- return [rowDraggableColumn,
830
+ return withStickyWidths([rowDraggableColumn,
770
831
  // remove all action column
771
832
  ...columns.filter(item => !['menu', 'expander', 'multiple-expander', 'selection'].includes(item.id))].map(col => Object.assign(Object.assign({}, col), {
772
833
  unsortable: false
773
- }));
834
+ })));
774
835
  }
775
- return columns;
836
+ return withStickyWidths(columns);
776
837
  }, [draggable, columns]);
777
838
  const instance = useTable({
778
839
  columns: filteredColumns,
@@ -791,7 +852,10 @@ function Table({
791
852
  onExpandedChange,
792
853
  autoResetPage: false,
793
854
  autoResetSelectedRows: false,
794
- getRowId: showDuplicatedData ? undefined : row => row[keyId] || JSON.stringify(row),
855
+ getRowId: showDuplicatedData ? undefined : row => {
856
+ var _a;
857
+ return isMissingRowId(row[keyId]) ? (_a = fallbackRowIds.get(row)) !== null && _a !== void 0 ? _a : JSON.stringify(row) : row[keyId];
858
+ },
795
859
  getSubRows: row => row[subRows] || []
796
860
  }, useSortBy, useExpanded, usePagination, useRowSelect, useSticky, useColumnOrder);
797
861
  const {
@@ -994,7 +1058,7 @@ function Table({
994
1058
  return jsx(TableHeadRow, Object.assign({}, headerGroup.getHeaderGroupProps(), {
995
1059
  children: visibleHeaders.map((column, index) => {
996
1060
  const headerRender = column.render('Header');
997
- return jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(props => [props, !column.unsortable ? column.getSortByToggleProps({
1061
+ return jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(props => [props, isHeaderSortable(column) ? column.getSortByToggleProps({
998
1062
  title: t('sortColumnTitle', 'Urutkan') || 'Urutkan'
999
1063
  }) : {
1000
1064
  onClick: column.onToggle
@@ -1008,7 +1072,7 @@ function Table({
1008
1072
  css: Object.assign(Object.assign(Object.assign({}, stickyPositionStyling(column)), stickyShadowStyling(column, tableScrollPosition, scrollAreaWidth)), column.headerCss),
1009
1073
  children: typeof headerRender === 'string' ? jsx(TableHeaderCellContainer, {
1010
1074
  isSortedDesc: column.isSortedDesc,
1011
- unsortable: column.unsortable,
1075
+ unsortable: !isHeaderSortable(column),
1012
1076
  isToggleable: !!column.onToggle,
1013
1077
  isToggled: column.isToggled,
1014
1078
  darkTheme: hasDarkParent,
@@ -1040,18 +1104,15 @@ function Table({
1040
1104
  var _a;
1041
1105
  return jsx(TableFootRow, {
1042
1106
  children: footerColumns.map(column => jsx(TableCell, Object.assign({}, getStickyFooterProps(column, flatHeaders), {
1043
- css: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, cssTableCell), {
1107
+ css: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, cssTableCell), {
1044
1108
  padding: '20px 8px',
1045
1109
  fontWeight: '600',
1046
1110
  verticalAlign: cellAlign
1047
- }), stickyPositionStyling(column)), stickyShadowStyling(column, tableScrollPosition, scrollAreaWidth)), column.footerCss),
1111
+ }), stickyPositionStyling(column)), stickyShadowStyling(column, tableScrollPosition, scrollAreaWidth)), alignCss(column)), column.footerCss),
1048
1112
  children: column.render('Footer', {
1049
1113
  footerRow,
1050
1114
  footerRowIndex,
1051
- value: column.accessor ? column.accessor(footerRow, footerRowIndex, {
1052
- subRows: [],
1053
- depth: 0
1054
- }) : undefined
1115
+ value: footerValueOf(column, footerRow, footerRowIndex)
1055
1116
  })
1056
1117
  }), column.id))
1057
1118
  }, (_a = footerRow[keyId]) !== null && _a !== void 0 ? _a : footerRowIndex);
@@ -1241,7 +1302,7 @@ function Table({
1241
1302
  rowSpan
1242
1303
  }, index) => {
1243
1304
  const headerRender = column.render('Header');
1244
- return jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(props => [props, !column.unsortable ? column.getSortByToggleProps({
1305
+ return jsx(TableHeadCell, Object.assign({}, column.getHeaderProps(props => [props, isHeaderSortable(column) ? column.getSortByToggleProps({
1245
1306
  title: t('sortColumnTitle', 'Urutkan') || 'Urutkan'
1246
1307
  }) : {
1247
1308
  onClick: column.onToggle
@@ -1252,27 +1313,33 @@ function Table({
1252
1313
  minWidth: column.colMinWidth
1253
1314
  }
1254
1315
  }]), {
1316
+ "aria-sort": getAriaSort(column),
1255
1317
  rowSpan: rowSpan > 1 ? rowSpan : undefined,
1256
1318
  css: Object.assign(Object.assign(Object.assign(Object.assign({}, stickyPositionStyling(column)), stickyShadowStyling(column, tableScrollPosition, scrollAreaWidth)), column.headerCss), {
1257
1319
  position: 'sticky',
1258
1320
  zIndex: column.sticky ? '5 !important' : 3,
1259
1321
  top: 0
1260
1322
  }),
1261
- children: typeof headerRender === 'string' ? jsx(TableHeaderCellContainer, {
1323
+ children: typeof headerRender === 'string' ? jsxs(TableHeaderCellContainer, {
1262
1324
  isSortedDesc: column.isSortedDesc,
1263
- unsortable: id ? true : column.unsortable,
1325
+ unsortable: !isHeaderSortable(column),
1264
1326
  isToggleable: !!column.onToggle,
1265
1327
  isToggled: column.isToggled,
1266
1328
  darkTheme: hasDarkParent,
1267
- css: {
1329
+ css: Object.assign({
1268
1330
  width: column.colWidth,
1269
1331
  maxWidth: column.colMaxWidth,
1270
1332
  minWidth: column.colMinWidth
1271
- },
1272
- children: jsx(TableHeadCellContent, {
1333
+ }, headerAlignCss(column)),
1334
+ children: [jsx(TableHeadCellContent, {
1273
1335
  heading: "headerTable",
1336
+ css: Object.assign({
1337
+ color: 'inherit'
1338
+ }, alignCss(column)),
1274
1339
  children: headerRender.toUpperCase()
1275
- })
1340
+ }), column.headerTooltip && jsx(HeaderTooltip, {
1341
+ label: column.headerTooltip
1342
+ })]
1276
1343
  }) : headerRender
1277
1344
  }));
1278
1345
  })
@@ -1320,13 +1387,13 @@ function Table({
1320
1387
  const collId = (_b = cell === null || cell === void 0 ? void 0 : cell.column) === null || _b === void 0 ? void 0 : _b.id;
1321
1388
  const exceptionId = (_c = [].concat(additionalExceptionIds)) === null || _c === void 0 ? void 0 : _c.includes(collId);
1322
1389
  return jsx(TableCell, Object.assign({}, cell.getCellProps(addOnCellStyle(cell.column.id)), {
1323
- css: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, cssTableCell), {
1390
+ css: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, cssTableCell), {
1324
1391
  padding: !isContextMenu || exceptionId ? collId === 'expander' || collId === 'multiple-expander' ? '20px 4px' : '20px 8px' : '',
1325
1392
  verticalAlign: cell.column.accessor ? cellAlign : 'center',
1326
1393
  cursor: onRowClick ? 'pointer' : 'unset'
1327
1394
  }), stickyPositionStyling(cell.column)), stickyShadowStyling(cell.column, tableScrollPosition, scrollAreaWidth)), id && {
1328
1395
  paddingRight: index + 1 === _row.length ? 30 : null
1329
- }), resolveCellCss(cell)),
1396
+ }), alignCss(cell.column)), resolveCellCss(cell)),
1330
1397
  onClick: () => {
1331
1398
  if (!exceptionId) {
1332
1399
  handleRowClick(row, i);
@@ -1359,6 +1426,7 @@ function Table({
1359
1426
  }, row.id);
1360
1427
  })
1361
1428
  })) : jsxs(TableBody, Object.assign({}, getTableBodyProps(), {
1429
+ onMouseLeave: cellRowSpans ? () => setHoveredRowId(null) : undefined,
1362
1430
  children: [isInfiniteScroll && paddingTop > 0 && jsx("tr", {
1363
1431
  children: jsx("td", {
1364
1432
  style: {
@@ -1378,7 +1446,10 @@ function Table({
1378
1446
  wrapper: child => tableRowData && tableRowData.length > 0 ? jsx(TableRow, {
1379
1447
  css: {
1380
1448
  '&:hover': {
1381
- backgroundColor: 'unset'
1449
+ backgroundColor: 'unset',
1450
+ '& > td': {
1451
+ backgroundColor: 'unset'
1452
+ }
1382
1453
  }
1383
1454
  },
1384
1455
  children: jsx(TableCell, {
@@ -1434,9 +1505,10 @@ function Table({
1434
1505
  })), jsx(TableRow, Object.assign({
1435
1506
  css: customStyle
1436
1507
  }, row && (row === null || row === void 0 ? void 0 : row.getRowProps) ? row === null || row === void 0 ? void 0 : row.getRowProps() : {}, {
1508
+ onMouseEnter: cellRowSpans ? () => setHoveredRowId(row.id) : undefined,
1437
1509
  children: row.cells.filter(cell => filteredHeader.length > 0 || filterHeader.length > 0 ? filteredHeader.includes(cell.column.id) || filterHeader.includes(cell.column.id) : cell).filter(cell => !cell.column.isSubRow).map((cell, index, _row) => {
1438
- var _a, _b, _c, _d;
1439
- const cellRowSpan = (_a = cellRowSpans === null || cellRowSpans === void 0 ? void 0 : cellRowSpans.get(cell.column.id)) === null || _a === void 0 ? void 0 : _a.get(row.id);
1510
+ var _a, _b, _c, _d, _e;
1511
+ const cellRowSpan = (_a = cellRowSpans === null || cellRowSpans === void 0 ? void 0 : cellRowSpans.spans.get(cell.column.id)) === null || _a === void 0 ? void 0 : _a.get(row.id);
1440
1512
  if (cellRowSpan === 0) return null;
1441
1513
  let visibleHeaders = (_b = headerGroups === null || headerGroups === void 0 ? void 0 : headerGroups[0]) === null || _b === void 0 ? void 0 : _b.headers.filter(column => !column.isSubRow);
1442
1514
  if (filterHeader.length > 0) {
@@ -1446,13 +1518,15 @@ function Table({
1446
1518
  const exceptionId = (_d = ['expander', 'multiple-expander', 'selection', 'menu', 'eye'].concat(additionalExceptionIds)) === null || _d === void 0 ? void 0 : _d.includes(collId);
1447
1519
  return jsx(TableCell, Object.assign({}, cell.getCellProps(addOnCellStyle(cell.column.id)), {
1448
1520
  rowSpan: cellRowSpan > 1 ? cellRowSpan : undefined,
1449
- css: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, cssTableCell), {
1521
+ css: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, cssTableCell), {
1450
1522
  padding: !isContextMenu || exceptionId ? collId === 'expander' || collId === 'multiple-expander' ? '20px 4px' : '20px 8px' : '',
1451
1523
  verticalAlign: cell.column.accessor ? cellAlign : 'center',
1452
1524
  cursor: onRowClick ? 'pointer' : 'unset'
1453
1525
  }), stickyPositionStyling(cell.column)), stickyShadowStyling(cell.column, tableScrollPosition, scrollAreaWidth)), id && {
1454
1526
  paddingRight: index + 1 === _row.length ? 30 : null
1455
- }), resolveCellCss(cell)),
1527
+ }), alignCss(cell.column)), resolveCellCss(cell)), cellRowSpan > 1 && ((_e = cellRowSpans.anchors.get(cell.column.id)) === null || _e === void 0 ? void 0 : _e.get(hoveredRowId)) === row.id && {
1528
+ backgroundColor: '$bgGray'
1529
+ }),
1456
1530
  onClick: () => {
1457
1531
  if (!exceptionId) {
1458
1532
  handleRowClick(row, i);
@@ -1617,7 +1691,11 @@ function Table({
1617
1691
  additionalExceptionIds: additionalExceptionIds,
1618
1692
  hideHeader: hideHeader,
1619
1693
  decoration: decoration,
1620
- rowVirtualizer: rowVirtualizer
1694
+ rowVirtualizer: rowVirtualizer,
1695
+ footerData: footerData,
1696
+ footerColumnIds: (footerData === null || footerData === void 0 ? void 0 : footerData.length) ? new Set(visibleColumns.filter(column => column.Footer !== emptyRenderer).map(column => column.id)) : undefined,
1697
+ headerRowSpan: headerRowSpan,
1698
+ keyId: keyId
1621
1699
  })]
1622
1700
  });
1623
1701
  }
@@ -0,0 +1,26 @@
1
+ import 'react';
2
+ import { CircleQuestionOutline } from '@majoo-ui/icons';
3
+ import Box from '../../../layout/box/Box.esm.js';
4
+ import { jsx } from 'react/jsx-runtime';
5
+ import Tooltip from '../../tooltip/Tooltip.esm.js';
6
+
7
+ const HeaderTooltip = ({
8
+ label
9
+ }) => /*#__PURE__*/jsx(Tooltip, {
10
+ label: label,
11
+ side: "top",
12
+ children: /*#__PURE__*/jsx(Box, {
13
+ as: "span",
14
+ css: {
15
+ display: 'flex',
16
+ cursor: 'help'
17
+ },
18
+ onClick: event => event.stopPropagation(),
19
+ children: /*#__PURE__*/jsx(CircleQuestionOutline, {
20
+ size: 16,
21
+ color: "currentColor"
22
+ })
23
+ })
24
+ });
25
+
26
+ export { HeaderTooltip };
@@ -0,0 +1,22 @@
1
+ const JUSTIFY_CONTENT = {
2
+ left: 'flex-start',
3
+ center: 'center',
4
+ right: 'flex-end'
5
+ };
6
+ /** A column's `cellCss` for one cell, called with the cell when it is a function. */
7
+ const resolveCellCss = cell => {
8
+ const {
9
+ cellCss
10
+ } = cell.column;
11
+ return typeof cellCss === 'function' ? cellCss(cell) : cellCss;
12
+ };
13
+ /** Horizontal alignment of a column's body cells, footer cells and header label text. */
14
+ const alignCss = column => column.align ? {
15
+ textAlign: column.align
16
+ } : undefined;
17
+ /** The header label sits in a flex container, which follows the column's alignment through justify-content. */
18
+ const headerAlignCss = column => column.align ? {
19
+ justifyContent: JUSTIFY_CONTENT[column.align]
20
+ } : undefined;
21
+
22
+ export { alignCss, headerAlignCss, resolveCellCss };
@@ -0,0 +1,7 @@
1
+ /** What a column's accessor reads from a footer row: the `value` its `Footer` receives. */
2
+ const footerValueOf = (column, footerRow, footerRowIndex) => typeof column.accessor === 'function' ? column.accessor(footerRow, footerRowIndex, {
3
+ subRows: [],
4
+ depth: 0
5
+ }) : undefined;
6
+
7
+ export { footerValueOf };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Whether a header click should toggle the sort. react-table's `defaultCanSort` makes every header
3
+ * sortable, but toggling a group header or the placeholder above an ungrouped column throws (neither
4
+ * is in `allColumns`), and a column without an accessor has nothing to sort on unless it brings its
5
+ * own `sortType`.
6
+ */
7
+ const isHeaderSortable = column => !column.unsortable && !column.columns && !column.placeholderOf && (Boolean(column.accessor) || typeof column.sortType === 'function');
8
+ /** `aria-sort` of a header cell; only a header that can sort announces a sort state. */
9
+ const getAriaSort = column => {
10
+ if (!isHeaderSortable(column)) return undefined;
11
+ if (!column.isSorted) return 'none';
12
+ return column.isSortedDesc ? 'descending' : 'ascending';
13
+ };
14
+
15
+ export { getAriaSort, isHeaderSortable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@majoo-ui/react",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "main": "./index.esm.js",
5
5
  "sideEffects": [
6
6
  "*.css",
@@ -34,6 +34,10 @@ interface MobileTableProps {
34
34
  decoration?: Decoration;
35
35
  rowVirtualizer: IRowVirtualizer;
36
36
  virtualized?: boolean;
37
+ footerData?: Record<string, any>[];
38
+ footerColumnIds?: Set<string>;
39
+ headerRowSpan?: boolean;
40
+ keyId?: string;
37
41
  }
38
42
  declare const MobileTable: React.ForwardRefExoticComponent<MobileTableProps & React.RefAttributes<HTMLDivElement>>;
39
43
  export default MobileTable;
@@ -0,0 +1,10 @@
1
+ /** A column's `cellCss` for one cell, called with the cell when it is a function. */
2
+ export declare const resolveCellCss: (cell: any) => any;
3
+ /** Horizontal alignment of a column's body cells, footer cells and header label text. */
4
+ export declare const alignCss: (column: any) => {
5
+ textAlign: any;
6
+ } | undefined;
7
+ /** The header label sits in a flex container, which follows the column's alignment through justify-content. */
8
+ export declare const headerAlignCss: (column: any) => {
9
+ justifyContent: any;
10
+ } | undefined;
@@ -0,0 +1,2 @@
1
+ /** What a column's accessor reads from a footer row: the `value` its `Footer` receives. */
2
+ export declare const footerValueOf: (column: any, footerRow: any, footerRowIndex: any) => any;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Whether a header click should toggle the sort. react-table's `defaultCanSort` makes every header
3
+ * sortable, but toggling a group header or the placeholder above an ungrouped column throws (neither
4
+ * is in `allColumns`), and a column without an accessor has nothing to sort on unless it brings its
5
+ * own `sortType`.
6
+ */
7
+ export declare const isHeaderSortable: (column: any) => boolean;
8
+ /** `aria-sort` of a header cell; only a header that can sort announces a sort state. */
9
+ export declare const getAriaSort: (column: any) => "none" | "ascending" | "descending" | undefined;