@coldsmirk/ledger-mantine 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1087,6 +1087,10 @@ function resolveIcons(icons) {
1087
1087
  const defaultLabels = {
1088
1088
  paginationSummary: (from, to, total) => `${from}–${to} of ${total}`,
1089
1089
  rowsPerPage: "Rows per page",
1090
+ firstPage: "First page",
1091
+ previousPage: "Previous page",
1092
+ nextPage: "Next page",
1093
+ lastPage: "Last page",
1090
1094
  selectAllRows: "Select all rows",
1091
1095
  selectRow: "Select row",
1092
1096
  selectedCount: (count) => `${count} selected`,
@@ -1115,6 +1119,12 @@ const defaultLabels = {
1115
1119
  filterPlaceholder: "Filter…",
1116
1120
  filterRangeMin: "Min",
1117
1121
  filterRangeMax: "Max",
1122
+ previousMonth: "Previous month",
1123
+ nextMonth: "Next month",
1124
+ previousYear: "Previous year",
1125
+ nextYear: "Next year",
1126
+ previousDecade: "Previous decade",
1127
+ nextDecade: "Next decade",
1118
1128
  searchPlaceholder: "Search…",
1119
1129
  loading: "Loading",
1120
1130
  empty: "No data",
@@ -1600,6 +1610,12 @@ const DEFAULT_PAGE_SIZE_OPTIONS = [
1600
1610
  50,
1601
1611
  100
1602
1612
  ];
1613
+ const CONTROL_LABELS = {
1614
+ first: "firstPage",
1615
+ previous: "previousPage",
1616
+ next: "nextPage",
1617
+ last: "lastPage"
1618
+ };
1603
1619
  function PaginationBarCore({ table, labels, pageSizeOptions, size, className, style }) {
1604
1620
  const pageSizeLabelId = (0, react.useId)();
1605
1621
  const { pageIndex, pageSize } = table.atoms.pagination.get();
@@ -1615,7 +1631,7 @@ function PaginationBarCore({ table, labels, pageSizeOptions, size, className, st
1615
1631
  style,
1616
1632
  wrap: "wrap",
1617
1633
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Text, {
1618
- c: "dimmed",
1634
+ c: "var(--ledger-color-dimmed)",
1619
1635
  size,
1620
1636
  children: labels.paginationSummary(from, to, total)
1621
1637
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mantine_core.Group, {
@@ -1625,7 +1641,7 @@ function PaginationBarCore({ table, labels, pageSizeOptions, size, className, st
1625
1641
  gap: "xs",
1626
1642
  wrap: "nowrap",
1627
1643
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Text, {
1628
- c: "dimmed",
1644
+ c: "var(--ledger-color-dimmed)",
1629
1645
  id: pageSizeLabelId,
1630
1646
  size,
1631
1647
  children: labels.rowsPerPage
@@ -1639,6 +1655,9 @@ function PaginationBarCore({ table, labels, pageSizeOptions, size, className, st
1639
1655
  onChange: (value) => value !== null && table.setPageSize(Number(value))
1640
1656
  })]
1641
1657
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Pagination, {
1658
+ getControlProps: (control) => {
1659
+ return { "aria-label": labels[CONTROL_LABELS[control]] };
1660
+ },
1642
1661
  size: SIZES[size].control,
1643
1662
  total: pageCount,
1644
1663
  value: pageIndex + 1,
@@ -3060,6 +3079,14 @@ function autosizeColumn(setColumnSizing, { columnId, minSize, maxSize, hasFilter
3060
3079
  * a black box that may ship no clear of its own — keeps the popover-level fallback.
3061
3080
  */
3062
3081
  const FACETED_OPTIONS_CAP = 100;
3082
+ /**
3083
+ * The smallest pointer target WCAG 2.2 AA accepts without spacing around it, in px.
3084
+ */
3085
+ const MIN_TARGET_SIZE = 24;
3086
+ /**
3087
+ * The calendar's weekend color, as the variable Mantine's day rule reads (see DateRangeFilter).
3088
+ */
3089
+ const WEEKEND_COLOR = { "--mantine-color-red-6": "color-mix(in srgb, var(--mantine-color-red-text), var(--mantine-color-text) 30%)" };
3063
3090
  function FilterPopover({ column }) {
3064
3091
  const { labels, icons, getStyles, filterMode, size } = useDataTableContext();
3065
3092
  const filter = column.columnDef.meta?.filter;
@@ -3073,6 +3100,8 @@ function FilterPopover({ column }) {
3073
3100
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Popover.Target, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.ActionIcon, {
3074
3101
  "aria-label": name,
3075
3102
  "data-active": active || void 0,
3103
+ mih: MIN_TARGET_SIZE,
3104
+ miw: MIN_TARGET_SIZE,
3076
3105
  size: SIZES[size].control,
3077
3106
  variant: active ? "light" : "subtle",
3078
3107
  onClick: (event) => event.stopPropagation(),
@@ -3254,11 +3283,21 @@ function DateRangeFilter({ column, name }) {
3254
3283
  const filterValue = column.getFilterValue();
3255
3284
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_dates.DatePicker, {
3256
3285
  allowSingleDateInRange: true,
3286
+ hideOutsideDates: true,
3257
3287
  highlightToday: true,
3258
3288
  "aria-label": name,
3259
3289
  size: "xs",
3290
+ style: WEEKEND_COLOR,
3260
3291
  type: "range",
3261
3292
  value: filterValue ?? [null, null],
3293
+ ariaLabels: {
3294
+ previousMonth: labels.previousMonth,
3295
+ nextMonth: labels.nextMonth,
3296
+ previousYear: labels.previousYear,
3297
+ nextYear: labels.nextYear,
3298
+ previousDecade: labels.previousDecade,
3299
+ nextDecade: labels.nextDecade
3300
+ },
3262
3301
  onChange: (next) => column.setFilterValue(!next[0] && !next[1] ? void 0 : next)
3263
3302
  }), filterValue !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Group, {
3264
3303
  justify: "flex-end",
@@ -3836,6 +3875,28 @@ const builtInFilterFns = {
3836
3875
  startsWith: _tanstack_react_table.filterFn_startsWith,
3837
3876
  weakEquals: _tanstack_react_table.filterFn_weakEquals
3838
3877
  };
3878
+ /**
3879
+ * Typed through the exported definitions on purpose. Since 9.2.5 TanStack's published `.d.ts`
3880
+ * files no longer end in an `export { … }` list (rolldown-plugin-dts 0.28 inlines the exports and
3881
+ * drops the marker), so TypeScript treats every top-level declaration in them as exported: the
3882
+ * private `RangeValue` alias behind the min/max/extent value types becomes nameable only by a
3883
+ * deep path the package's `exports` map hides, and an inferred registry fails declaration emit
3884
+ * (TS2883). Naming each definition by its public export emits the type the upstream fix will
3885
+ * emit anyway. Upstream: sxzz/rolldown-plugin-dts#312 (fix: #310), rolldown/tsdown#1087.
3886
+ */
3887
+ const builtInAggregationFns = {
3888
+ count: _tanstack_react_table.aggregationFn_count,
3889
+ extent: _tanstack_react_table.aggregationFn_extent,
3890
+ first: _tanstack_react_table.aggregationFn_first,
3891
+ last: _tanstack_react_table.aggregationFn_last,
3892
+ max: _tanstack_react_table.aggregationFn_max,
3893
+ mean: _tanstack_react_table.aggregationFn_mean,
3894
+ median: _tanstack_react_table.aggregationFn_median,
3895
+ min: _tanstack_react_table.aggregationFn_min,
3896
+ sum: _tanstack_react_table.aggregationFn_sum,
3897
+ unique: _tanstack_react_table.aggregationFn_unique,
3898
+ uniqueCount: _tanstack_react_table.aggregationFn_uniqueCount
3899
+ };
3839
3900
  const ledgerFeatures = (0, _tanstack_react_table.tableFeatures)({
3840
3901
  cellSpanningFeature: _tanstack_react_table.cellSpanningFeature,
3841
3902
  columnFacetingFeature: _tanstack_react_table.columnFacetingFeature,
@@ -3860,19 +3921,7 @@ const ledgerFeatures = (0, _tanstack_react_table.tableFeatures)({
3860
3921
  groupedRowModel: (0, _tanstack_react_table.createGroupedRowModel)(),
3861
3922
  paginatedRowModel: (0, _tanstack_react_table.createPaginatedRowModel)(),
3862
3923
  sortedRowModel: (0, _tanstack_react_table.createSortedRowModel)(),
3863
- aggregationFns: {
3864
- count: _tanstack_react_table.aggregationFn_count,
3865
- extent: _tanstack_react_table.aggregationFn_extent,
3866
- first: _tanstack_react_table.aggregationFn_first,
3867
- last: _tanstack_react_table.aggregationFn_last,
3868
- max: _tanstack_react_table.aggregationFn_max,
3869
- mean: _tanstack_react_table.aggregationFn_mean,
3870
- median: _tanstack_react_table.aggregationFn_median,
3871
- min: _tanstack_react_table.aggregationFn_min,
3872
- sum: _tanstack_react_table.aggregationFn_sum,
3873
- unique: _tanstack_react_table.aggregationFn_unique,
3874
- uniqueCount: _tanstack_react_table.aggregationFn_uniqueCount
3875
- },
3924
+ aggregationFns: builtInAggregationFns,
3876
3925
  filterFns: {
3877
3926
  ...builtInFilterFns,
3878
3927
  ...ledgerFilterFns
@@ -6393,6 +6442,33 @@ function useStylesRevision(getStyles, selectors) {
6393
6442
  return revision;
6394
6443
  }
6395
6444
  //#endregion
6445
+ //#region src/use-viewport-overflow.ts
6446
+ /**
6447
+ * Whether the body viewport scrolls on either axis. A scroll container has to be reachable by
6448
+ * keyboard to be scrolled by it (WCAG 2.1.1), while a focus stop over content that fits is a
6449
+ * Tab press spent on nothing — so the viewport's focus stop follows this.
6450
+ *
6451
+ * Measured whenever the viewport or its content resizes: rows arriving, columns widening, or
6452
+ * the host resizing the table each change the answer without a scroll event. A 1px tolerance
6453
+ * absorbs fractional layout rounding, which would otherwise flicker the stop on and off.
6454
+ */
6455
+ function useViewportOverflow(viewport) {
6456
+ const [overflowing, setOverflowing] = (0, react.useState)(false);
6457
+ (0, react.useLayoutEffect)(() => {
6458
+ if (!viewport) return;
6459
+ const measure = () => {
6460
+ const next = viewport.scrollHeight - viewport.clientHeight > 1 || viewport.scrollWidth - viewport.clientWidth > 1;
6461
+ setOverflowing(next);
6462
+ };
6463
+ measure();
6464
+ const observer = new ResizeObserver(measure);
6465
+ observer.observe(viewport);
6466
+ if (viewport.firstElementChild) observer.observe(viewport.firstElementChild);
6467
+ return () => observer.disconnect();
6468
+ }, [viewport]);
6469
+ return overflowing;
6470
+ }
6471
+ //#endregion
6396
6472
  //#region src/data-table.tsx
6397
6473
  /**
6398
6474
  * The root component (docs/api.md): a generic Mantine factory with full Styles API,
@@ -6450,7 +6526,7 @@ const STYLE_SELECTORS = Object.keys(classes);
6450
6526
  */
6451
6527
  const NO_ARIA_ROWS = [];
6452
6528
  const EMPTY_STATE_STYLES = { title: {
6453
- color: "var(--mantine-color-dimmed)",
6529
+ color: "var(--ledger-color-dimmed)",
6454
6530
  fontWeight: 500
6455
6531
  } };
6456
6532
  const defaultProps = {
@@ -6642,6 +6718,7 @@ function DataTableCore({ presentation, stylesProps, table }) {
6642
6718
  viewportRef.current = element;
6643
6719
  setViewport(element);
6644
6720
  }, []);
6721
+ const viewportOverflows = useViewportOverflow(viewport);
6645
6722
  const startLeafColumns = table.getStartVisibleLeafColumns();
6646
6723
  const endLeafColumns = table.getEndVisibleLeafColumns();
6647
6724
  const displayColumns = [
@@ -6857,7 +6934,7 @@ function DataTableCore({ presentation, stylesProps, table }) {
6857
6934
  rowPinningActive,
6858
6935
  withDetailPanels
6859
6936
  ]);
6860
- const bodyAriaRowCount = loading && logicalDisplayRowCount === 0 ? skeletonRowCount : logicalDisplayRowCount + (loadMoreError || loadingMore ? 1 : 0);
6937
+ const bodyAriaRowCount = loading && logicalDisplayRowCount === 0 ? skeletonRowCount : logicalDisplayRowCount + (loadMoreError || loadingMore ? 1 : 0) + (isEmpty || errorActive ? 1 : 0);
6861
6938
  const footerRowCount = visibleFooterGroups(table).length;
6862
6939
  const ariaRowCount = headerRowCount + bodyAriaRowCount + footerRowCount;
6863
6940
  const footerAriaRowIndexStart = headerRowCount + bodyAriaRowCount + 1;
@@ -7231,11 +7308,10 @@ function DataTableCore({ presentation, stylesProps, table }) {
7231
7308
  },
7232
7309
  viewportProps: mergeElementProps(viewportProps, {
7233
7310
  style: { overscrollBehavior: "none" },
7311
+ role: "rowgroup",
7234
7312
  "aria-describedby": viewportDescribedBy,
7235
- ...activeRowEnabled && {
7236
- tabIndex: 0,
7237
- onKeyDown: handleActiveRowKeyDown
7238
- }
7313
+ tabIndex: activeRowEnabled || viewportOverflows ? 0 : void 0,
7314
+ ...activeRowEnabled && { onKeyDown: handleActiveRowKeyDown }
7239
7315
  }),
7240
7316
  onScrollPositionChange: handleScrollPositionChange,
7241
7317
  children: [
@@ -7258,43 +7334,59 @@ function DataTableCore({ presentation, stylesProps, table }) {
7258
7334
  })]
7259
7335
  }),
7260
7336
  isEmpty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7261
- ...getStyles("empty"),
7262
- "data-variant": filtersActive ? "no-results" : "no-data",
7263
- role: "status",
7264
- children: emptyState ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.EmptyState, {
7265
- withIndicatorBackground: true,
7266
- size: "sm",
7267
- styles: EMPTY_STATE_STYLES,
7268
- title: filtersActive ? labels.noResults : labels.empty,
7269
- icon: filtersActive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.noResults, {
7270
- size: 40,
7271
- strokeWidth: 1.5
7272
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.empty, {
7273
- size: 40,
7274
- strokeWidth: 1.5
7337
+ "aria-rowindex": virtualEnabled ? headerRowCount + bodyAriaRowCount : void 0,
7338
+ role: "row",
7339
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7340
+ ...getStyles("empty"),
7341
+ "aria-colspan": visibleLeafColumns.length,
7342
+ "data-variant": filtersActive ? "no-results" : "no-data",
7343
+ role: "cell",
7344
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7345
+ role: "status",
7346
+ children: emptyState ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.EmptyState, {
7347
+ withIndicatorBackground: true,
7348
+ size: "sm",
7349
+ styles: EMPTY_STATE_STYLES,
7350
+ title: filtersActive ? labels.noResults : labels.empty,
7351
+ icon: filtersActive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.noResults, {
7352
+ size: 40,
7353
+ strokeWidth: 1.5
7354
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.empty, {
7355
+ size: 40,
7356
+ strokeWidth: 1.5
7357
+ })
7358
+ })
7275
7359
  })
7276
7360
  })
7277
7361
  }),
7278
7362
  errorActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7279
- ...getStyles("empty"),
7280
- "data-over-rows": rowsLength > 0 || void 0,
7281
- "data-variant": "error",
7282
- role: "alert",
7283
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.EmptyState, {
7284
- withIndicatorBackground: true,
7285
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.error, {
7286
- size: 40,
7287
- strokeWidth: 1.5
7288
- }),
7289
- size: "sm",
7290
- styles: EMPTY_STATE_STYLES,
7291
- title: error === true ? labels.error : error,
7292
- children: onRetry && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Button, {
7293
- leftSection: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.retry, { size: 14 }),
7294
- size: "xs",
7295
- variant: "light",
7296
- onClick: onRetry,
7297
- children: labels.retry
7363
+ "aria-rowindex": virtualEnabled ? headerRowCount + bodyAriaRowCount : void 0,
7364
+ role: "row",
7365
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7366
+ ...getStyles("empty"),
7367
+ "aria-colspan": visibleLeafColumns.length,
7368
+ "data-over-rows": rowsLength > 0 || void 0,
7369
+ "data-variant": "error",
7370
+ role: "cell",
7371
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7372
+ role: "alert",
7373
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.EmptyState, {
7374
+ withIndicatorBackground: true,
7375
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.error, {
7376
+ size: 40,
7377
+ strokeWidth: 1.5
7378
+ }),
7379
+ size: "sm",
7380
+ styles: EMPTY_STATE_STYLES,
7381
+ title: error === true ? labels.error : error,
7382
+ children: onRetry && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mantine_core.Button, {
7383
+ leftSection: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(icons.retry, { size: 14 }),
7384
+ size: "xs",
7385
+ variant: "light",
7386
+ onClick: onRetry,
7387
+ children: labels.retry
7388
+ })
7389
+ })
7298
7390
  })
7299
7391
  })
7300
7392
  })