@zuilib/data-grid 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  // src/data-grid.tsx
4
4
  import { useMemo as useMemo3 } from "react";
5
- import { cn as cn8 } from "@zuilib/components/lib/cn";
5
+ import { cn as cn8 } from "@zuilib/primitives/lib/cn";
6
6
 
7
7
  // src/column-manager.tsx
8
8
  import { Fragment } from "react";
9
- import Button2 from "@zuilib/components/button";
10
- import Checkbox2 from "@zuilib/components/checkbox";
11
- import Popover from "@zuilib/components/popover";
12
- import { cn as cn2 } from "@zuilib/components/lib/cn";
9
+ import Button2 from "@zuilib/primitives/button";
10
+ import Checkbox2 from "@zuilib/primitives/checkbox";
11
+ import Popover from "@zuilib/primitives/popover";
12
+ import { cn as cn2 } from "@zuilib/primitives/lib/cn";
13
13
 
14
14
  // src/column-extras.ts
15
15
  function columnExtras(column) {
@@ -39,9 +39,9 @@ function useDataGridContext(part) {
39
39
  }
40
40
 
41
41
  // src/expand-column.tsx
42
- import Button from "@zuilib/components/button";
43
- import { cn } from "@zuilib/components/lib/cn";
44
- import { ChevronDownIcon } from "@zuilib/components/lib/icons";
42
+ import Button from "@zuilib/primitives/button";
43
+ import { cn } from "@zuilib/primitives/lib/cn";
44
+ import { ChevronDownIcon } from "@zuilib/primitives/lib/icons";
45
45
  import { jsx } from "react/jsx-runtime";
46
46
  var EXPAND_COLUMN_ID = "__expand";
47
47
  function ExpandToggle({ row }) {
@@ -95,7 +95,7 @@ function createExpandColumn() {
95
95
  }
96
96
 
97
97
  // src/select-column.tsx
98
- import Checkbox from "@zuilib/components/checkbox";
98
+ import Checkbox from "@zuilib/primitives/checkbox";
99
99
  import { jsx as jsx2 } from "react/jsx-runtime";
100
100
  var SELECT_COLUMN_ID = "__select";
101
101
  function SelectAll({ table }) {
@@ -195,6 +195,7 @@ function fillLabel(template, values) {
195
195
  // src/column-manager.tsx
196
196
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
197
197
  var FIXED_COLUMN_IDS = [SELECT_COLUMN_ID, EXPAND_COLUMN_ID];
198
+ var rowButtonClasses = "size-7 p-0 pointer-coarse:size-11";
198
199
  function ColumnsIcon({ className }) {
199
200
  return /* @__PURE__ */ jsx3("svg", { "aria-hidden": "true", focusable: "false", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: cn2("size-[1em] shrink-0", className), children: /* @__PURE__ */ jsx3("path", { d: "M14 17h2.75A2.25 2.25 0 0 0 19 14.75v-9.5A2.25 2.25 0 0 0 16.75 3H14v14ZM12.5 3h-5v14h5V3ZM3.25 3H6v14H3.25A2.25 2.25 0 0 1 1 14.75v-9.5A2.25 2.25 0 0 1 3.25 3Z" }) });
200
201
  }
@@ -205,7 +206,7 @@ function DataGridColumnManagerRow({ column, index, count, onMove }) {
205
206
  const pinned = column.getIsPinned();
206
207
  const setPin = (position) => column.pin(pinned === position ? false : position);
207
208
  return /* @__PURE__ */ jsxs("li", { "data-slot": "data-grid-column-manager-row", "data-column": column.id, className: "flex items-center gap-2 py-1", children: [
208
- /* @__PURE__ */ jsx3(
209
+ /* @__PURE__ */ jsx3("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx3(
209
210
  Checkbox2,
210
211
  {
211
212
  size: "sm",
@@ -214,10 +215,10 @@ function DataGridColumnManagerRow({ column, index, count, onMove }) {
214
215
  disabled: !column.getCanHide(),
215
216
  onCheckedChange: (checked) => column.toggleVisibility(checked)
216
217
  }
217
- ),
218
- /* @__PURE__ */ jsxs("span", { className: "ms-auto inline-flex items-center gap-0.5", children: [
219
- /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "sm", "aria-label": fillLabel(labels.moveUp, { column: label }), disabled: index === 0, onClick: () => onMove(column, -1), className: "size-7 p-0", children: /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: "\u2191" }) }),
220
- /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "sm", "aria-label": fillLabel(labels.moveDown, { column: label }), disabled: index === count - 1, onClick: () => onMove(column, 1), className: "size-7 p-0", children: /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: "\u2193" }) }),
218
+ ) }),
219
+ /* @__PURE__ */ jsxs("span", { className: "ms-auto inline-flex shrink-0 items-center gap-0.5", children: [
220
+ /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "sm", "aria-label": fillLabel(labels.moveUp, { column: label }), disabled: index === 0, onClick: () => onMove(column, -1), className: rowButtonClasses, children: /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: "\u2191" }) }),
221
+ /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "sm", "aria-label": fillLabel(labels.moveDown, { column: label }), disabled: index === count - 1, onClick: () => onMove(column, 1), className: rowButtonClasses, children: /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: "\u2193" }) }),
221
222
  ["left", "right"].map((side) => /* @__PURE__ */ jsx3(
222
223
  Button2,
223
224
  {
@@ -226,7 +227,7 @@ function DataGridColumnManagerRow({ column, index, count, onMove }) {
226
227
  "aria-label": pinned === side ? `${labels.unpin} ${label}` : `${pinLabels[side]}: ${label}`,
227
228
  "aria-pressed": pinned === side,
228
229
  onClick: () => setPin(side),
229
- className: cn2("size-7 p-0 text-xs", pinned === side && "text-primary"),
230
+ className: cn2(rowButtonClasses, "text-xs", pinned === side && "text-primary"),
230
231
  children: /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: side === "left" ? "\u21E4" : "\u21E5" })
231
232
  },
232
233
  side
@@ -249,9 +250,9 @@ function DataGridColumnManager({ label: labelProp, className, addable, onAddColu
249
250
  table.setColumnOrder([...FIXED_COLUMN_IDS.filter((id) => present.has(id)), ...order]);
250
251
  };
251
252
  return /* @__PURE__ */ jsxs(Popover, { "data-slot": "data-grid-column-manager", className: cn2("inline-flex", className), children: [
252
- /* @__PURE__ */ jsx3(Popover.Button, { as: Fragment, children: /* @__PURE__ */ jsx3(Button2, { variant: "outline", size: "sm", leadingIcon: /* @__PURE__ */ jsx3(ColumnsIcon, {}), "data-slot": "data-grid-column-manager-button", children: label }) }),
253
- /* @__PURE__ */ jsxs(Popover.Panel, { "data-slot": "data-grid-column-manager-panel", anchor: "bottom end", className: "min-w-64", children: [
254
- /* @__PURE__ */ jsx3("ul", { "data-slot": "data-grid-column-manager-list", "aria-label": label, className: "m-0 flex list-none flex-col p-0", children: columns.map((column, index) => /* @__PURE__ */ jsx3(DataGridColumnManagerRow, { column, index, count: columns.length, onMove: move }, column.id)) }),
253
+ /* @__PURE__ */ jsx3(Popover.Button, { as: Fragment, children: /* @__PURE__ */ jsx3(Button2, { variant: "outline", size: "sm", leadingIcon: /* @__PURE__ */ jsx3(ColumnsIcon, {}), "data-slot": "data-grid-column-manager-button", children: /* @__PURE__ */ jsx3("span", { className: "sr-only @sm:not-sr-only", children: label }) }) }),
254
+ /* @__PURE__ */ jsxs(Popover.Panel, { "data-slot": "data-grid-column-manager-panel", anchor: "bottom end", className: "min-w-[min(16rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)]", children: [
255
+ /* @__PURE__ */ jsx3("ul", { "data-slot": "data-grid-column-manager-list", "aria-label": label, className: "m-0 flex max-h-[min(60dvh,24rem)] list-none flex-col overflow-y-auto p-0", children: columns.map((column, index) => /* @__PURE__ */ jsx3(DataGridColumnManagerRow, { column, index, count: columns.length, onMove: move }, column.id)) }),
255
256
  onAddColumn && addable && addable.length > 0 ? /* @__PURE__ */ jsxs("div", { "data-slot": "data-grid-column-manager-add", className: "mt-2 border-t border-border pt-2", children: [
256
257
  /* @__PURE__ */ jsx3("p", { className: "m-0 pb-1 text-xs font-medium text-muted-foreground", children: labels.addColumnSection }),
257
258
  /* @__PURE__ */ jsx3("ul", { "aria-label": labels.addColumnSection, className: "m-0 flex list-none flex-col p-0", children: addable.map((field) => /* @__PURE__ */ jsx3("li", { "data-slot": "data-grid-column-manager-add-row", "data-column": field.id, className: "flex items-center gap-2 py-0.5", children: /* @__PURE__ */ jsxs(
@@ -275,14 +276,15 @@ function DataGridColumnManager({ label: labelProp, className, addable, onAddColu
275
276
 
276
277
  // src/data-grid-body.tsx
277
278
  import { flexRender as flexRender2 } from "@tanstack/react-table";
278
- import { Fragment as Fragment2, useEffect, useRef as useRef2, useState as useState3 } from "react";
279
- import Alert from "@zuilib/components/alert";
280
- import EmptyState from "@zuilib/components/empty-state";
281
- import Skeleton from "@zuilib/components/skeleton";
282
- import Table from "@zuilib/components/table";
283
- import { cn as cn4 } from "@zuilib/components/lib/cn";
284
- import { focusOutlineInsetClasses, focusRingInsetClasses } from "@zuilib/components/lib/focus";
285
- import { nativeControlResetClasses } from "@zuilib/components/lib/reset";
279
+ import { Fragment as Fragment3, useEffect, useRef as useRef2, useState as useState3 } from "react";
280
+ import Alert from "@zuilib/primitives/alert";
281
+ import EmptyState from "@zuilib/primitives/empty-state";
282
+ import Skeleton from "@zuilib/primitives/skeleton";
283
+ import Table from "@zuilib/primitives/table";
284
+ import { cn as cn4 } from "@zuilib/primitives/lib/cn";
285
+ import { focusOutlineInsetClasses, focusRingInsetClasses } from "@zuilib/primitives/lib/focus";
286
+ import { nativeControlResetClasses } from "@zuilib/primitives/lib/reset";
287
+ import { touchTargetClasses } from "@zuilib/primitives/lib/sizes";
286
288
 
287
289
  // src/dev.ts
288
290
  function isDev() {
@@ -302,7 +304,7 @@ function warnOnce(key, message) {
302
304
  // src/editable-cell.tsx
303
305
  import { flexRender } from "@tanstack/react-table";
304
306
  import { useRef, useState } from "react";
305
- import Input from "@zuilib/components/input";
307
+ import Input from "@zuilib/primitives/input";
306
308
  import { jsx as jsx4 } from "react/jsx-runtime";
307
309
  function EditableCell({ cell, editing, onEditEnd, commitOnBlur, onCellCommit, labels }) {
308
310
  const value = cell.getValue();
@@ -357,12 +359,13 @@ function EditableCell({ cell, editing, onEditEnd, commitOnBlur, onCellCommit, la
357
359
 
358
360
  // src/filter-popover.tsx
359
361
  import { useState as useState2 } from "react";
360
- import Button3 from "@zuilib/components/button";
361
- import Checkbox3 from "@zuilib/components/checkbox";
362
- import Input2 from "@zuilib/components/input";
363
- import Popover2 from "@zuilib/components/popover";
364
- import { cn as cn3 } from "@zuilib/components/lib/cn";
365
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
362
+ import Button3 from "@zuilib/primitives/button";
363
+ import Checkbox3 from "@zuilib/primitives/checkbox";
364
+ import Input2 from "@zuilib/primitives/input";
365
+ import Popover2 from "@zuilib/primitives/popover";
366
+ import { cn as cn3 } from "@zuilib/primitives/lib/cn";
367
+ import { touchHitAreaClasses } from "@zuilib/primitives/lib/sizes";
368
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
366
369
  function FilterIcon({ className }) {
367
370
  return /* @__PURE__ */ jsx5(
368
371
  "svg",
@@ -447,7 +450,7 @@ function SelectFilterForm({ filter, value, labels, onApply, onClear }) {
447
450
  onApply(draft);
448
451
  },
449
452
  children: [
450
- /* @__PURE__ */ jsx5("div", { role: "group", "aria-label": labels.filterOptions, className: "flex max-h-60 flex-col gap-2 overflow-auto", children: filter.options.map((option) => /* @__PURE__ */ jsx5(
453
+ /* @__PURE__ */ jsx5("div", { role: "group", "aria-label": labels.filterOptions, className: "flex max-h-[min(50dvh,15rem)] flex-col gap-2 overflow-auto", children: filter.options.map((option) => /* @__PURE__ */ jsx5(
451
454
  Checkbox3,
452
455
  {
453
456
  size: "sm",
@@ -511,7 +514,7 @@ function FilterPopover({ column, filter }) {
511
514
  };
512
515
  return filter.control === "text" ? /* @__PURE__ */ jsx5(TextFilterForm, { filter, value, labels, onApply: apply, onClear: clear }) : filter.control === "select" ? /* @__PURE__ */ jsx5(SelectFilterForm, { filter, value, labels, onApply: apply, onClear: clear }) : /* @__PURE__ */ jsx5(DateRangeFilterForm, { filter, value, labels, onApply: apply, onClear: clear });
513
516
  };
514
- return /* @__PURE__ */ jsxs2(Popover2, { "data-slot": "data-grid-filter", className: "inline-flex shrink-0", children: [
517
+ return /* @__PURE__ */ jsx5(Popover2, { "data-slot": "data-grid-filter", className: "inline-flex shrink-0", children: ({ close }) => /* @__PURE__ */ jsxs2(Fragment2, { children: [
515
518
  /* @__PURE__ */ jsx5(
516
519
  Popover2.Button,
517
520
  {
@@ -521,17 +524,20 @@ function FilterPopover({ column, filter }) {
521
524
  className: cn3(
522
525
  "inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground",
523
526
  "transition-colors duration-(--duration-fast) hover:bg-accent hover:text-accent-foreground",
524
- "data-[active]:text-primary data-[open]:bg-accent"
527
+ "data-[active]:text-primary data-[open]:bg-accent",
528
+ /* A Base UI button, not the Button primitive, so it positions itself for the overlay. */
529
+ "relative",
530
+ touchHitAreaClasses
525
531
  ),
526
532
  children: /* @__PURE__ */ jsx5(FilterIcon, { className: "size-3.5" })
527
533
  }
528
534
  ),
529
- /* @__PURE__ */ jsx5(Popover2.Panel, { "data-slot": "data-grid-filter-panel", anchor: "bottom start", className: "min-w-56", children: ({ close }) => form(close) })
530
- ] });
535
+ /* @__PURE__ */ jsx5(Popover2.Panel, { "data-slot": "data-grid-filter-panel", anchor: "bottom start", className: "min-w-[min(14rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)]", children: form(close) })
536
+ ] }) });
531
537
  }
532
538
 
533
539
  // src/data-grid-body.tsx
534
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
540
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
535
541
  var densityToSize = { compact: "sm", comfortable: "md" };
536
542
  function pinnedSide(column) {
537
543
  const pinned = column.getIsPinned();
@@ -677,7 +683,7 @@ function HeaderCell({ header, stickyHeader, active, onFocus }) {
677
683
  activate: sortable ? () => column.toggleSorting(void 0, column.getCanMultiSort()) : void 0
678
684
  }),
679
685
  children: [
680
- /* @__PURE__ */ jsxs3("div", { "data-slot": "data-grid-header-content", className: cn4("flex items-center gap-1", extras.align === "end" && "flex-row-reverse"), children: [
686
+ /* @__PURE__ */ jsxs3("div", { "data-slot": "data-grid-header-content", className: cn4("flex items-center gap-1", touchTargetClasses, extras.align === "end" && "flex-row-reverse"), children: [
681
687
  sortable ? /* @__PURE__ */ jsxs3(
682
688
  "button",
683
689
  {
@@ -688,6 +694,7 @@ function HeaderCell({ header, stickyHeader, active, onFocus }) {
688
694
  className: cn4(
689
695
  nativeControlResetClasses,
690
696
  "flex min-w-0 flex-1 cursor-pointer items-center gap-1 px-(--table-cell-px) py-(--table-cell-py)",
697
+ touchTargetClasses,
691
698
  "transition-colors duration-(--duration-fast) motion-reduce:transition-none",
692
699
  "hover:text-foreground data-[sort=ascending]:text-foreground data-[sort=descending]:text-foreground",
693
700
  focusRingInsetClasses,
@@ -732,7 +739,9 @@ function HeaderCell({ header, stickyHeader, active, onFocus }) {
732
739
  "absolute top-0 end-0 h-full w-1.5 cursor-col-resize touch-none select-none",
733
740
  "opacity-0 transition-opacity duration-(--duration-fast) group-hover/head:opacity-100 focus-visible:opacity-100",
734
741
  "bg-border hover:bg-primary focus-visible:bg-primary focus:outline-none",
735
- column.getIsResizing() && "bg-primary opacity-100"
742
+ /* No hover on a touch screen: the handle is a visible 1px divider with a 12px grip. */
743
+ "pointer-coarse:w-3 pointer-coarse:border-e pointer-coarse:border-border pointer-coarse:bg-transparent pointer-coarse:opacity-100",
744
+ column.getIsResizing() && "bg-primary opacity-100 pointer-coarse:bg-primary"
736
745
  )
737
746
  }
738
747
  ) : null
@@ -800,7 +809,7 @@ function DataRow({ row, ariaRowIndex, activeCol, onFocus, onRowClick }) {
800
809
  const expanded = expandable && row.getIsExpanded();
801
810
  const ref = useRef2(null);
802
811
  const cells = row.getVisibleCells();
803
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
812
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
804
813
  /* @__PURE__ */ jsx6(
805
814
  Table.Row,
806
815
  {
@@ -836,7 +845,7 @@ function DataRow({ row, ariaRowIndex, activeCol, onFocus, onRowClick }) {
836
845
  ] });
837
846
  }
838
847
  function SkeletonRows({ count, columnCount }) {
839
- return /* @__PURE__ */ jsx6(Fragment3, { children: Array.from({ length: count }, (_, rowIndex) => /* @__PURE__ */ jsx6(Table.Row, { "data-slot": "data-grid-skeleton-row", "aria-hidden": "true", children: Array.from({ length: columnCount }, (_2, cellIndex) => /* @__PURE__ */ jsx6(Table.Cell, { "data-slot": "data-grid-cell", children: /* @__PURE__ */ jsx6(Skeleton, { shape: "text", width: `${55 + (rowIndex * 7 + cellIndex * 13) % 40}%` }) }, cellIndex)) }, rowIndex)) });
848
+ return /* @__PURE__ */ jsx6(Fragment4, { children: Array.from({ length: count }, (_, rowIndex) => /* @__PURE__ */ jsx6(Table.Row, { "data-slot": "data-grid-skeleton-row", "aria-hidden": "true", children: Array.from({ length: columnCount }, (_2, cellIndex) => /* @__PURE__ */ jsx6(Table.Cell, { "data-slot": "data-grid-cell", children: /* @__PURE__ */ jsx6(Skeleton, { shape: "text", width: `${55 + (rowIndex * 7 + cellIndex * 13) % 40}%` }) }, cellIndex)) }, rowIndex)) });
840
849
  }
841
850
  function DataGridBody({
842
851
  stickyHeader = false,
@@ -876,7 +885,7 @@ function DataGridBody({
876
885
  stickyHeader,
877
886
  fullWidth: true,
878
887
  className: cn4("table-fixed", className),
879
- scrollAreaClassName: cn4("rounded-md border border-border", scrollAreaClassName),
888
+ scrollAreaClassName: cn4("min-w-0 overscroll-x-contain rounded-md border border-border", scrollAreaClassName),
880
889
  style: { width: table.getTotalSize(), minWidth: "100%" },
881
890
  ...aria,
882
891
  children: [
@@ -920,10 +929,12 @@ function DataGridBody({
920
929
 
921
930
  // src/pagination.tsx
922
931
  import { useMemo } from "react";
923
- import Button4 from "@zuilib/components/button";
924
- import NativeSelect from "@zuilib/components/native-select";
925
- import { cn as cn5 } from "@zuilib/components/lib/cn";
932
+ import Button4 from "@zuilib/primitives/button";
933
+ import NativeSelect from "@zuilib/primitives/native-select";
934
+ import { cn as cn5 } from "@zuilib/primitives/lib/cn";
935
+ import { touchSquareTargetClasses } from "@zuilib/primitives/lib/sizes";
926
936
  import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
937
+ var wideOnlyButtonClasses = "hidden @sm:inline-flex";
927
938
  function useNumberFormat(locale) {
928
939
  return useMemo(() => {
929
940
  try {
@@ -956,7 +967,7 @@ function DataGridPagination({ locale: localeProp, className, ...props }) {
956
967
  ...props,
957
968
  children: [
958
969
  /* @__PURE__ */ jsxs4("label", { "data-slot": "data-grid-page-size", className: "flex items-center gap-2", children: [
959
- labels.rowsPerPage,
970
+ /* @__PURE__ */ jsx7("span", { className: "sr-only @sm:not-sr-only", children: labels.rowsPerPage }),
960
971
  /* @__PURE__ */ jsx7(
961
972
  NativeSelect,
962
973
  {
@@ -967,17 +978,17 @@ function DataGridPagination({ locale: localeProp, className, ...props }) {
967
978
  }
968
979
  )
969
980
  ] }),
970
- /* @__PURE__ */ jsx7("p", { "data-slot": "data-grid-range", "aria-live": "polite", className: "m-0 ms-auto tabular-nums", children: loading ? labels.loading : fillLabel(labels.range, { first: numberFormat.format(first), last: numberFormat.format(last), total: numberFormat.format(rowCount) }) }),
981
+ /* @__PURE__ */ jsx7("p", { "data-slot": "data-grid-range", "aria-live": "polite", className: "m-0 ms-auto whitespace-nowrap tabular-nums", children: loading ? labels.loading : fillLabel(labels.range, { first: numberFormat.format(first), last: numberFormat.format(last), total: numberFormat.format(rowCount) }) }),
971
982
  /* @__PURE__ */ jsxs4("div", { "data-slot": "data-grid-page-buttons", className: "flex items-center gap-1", children: [
972
- /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.firstPage, disabled: !canPrevious, onClick: () => table.setPageIndex(0), children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xAB" }) }),
973
- /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.previousPage, disabled: !canPrevious, onClick: () => table.previousPage(), children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2039" }) }),
974
- /* @__PURE__ */ jsxs4("span", { "data-slot": "data-grid-page-indicator", className: "px-1 tabular-nums", children: [
983
+ /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.firstPage, disabled: !canPrevious, onClick: () => table.setPageIndex(0), className: cn5(touchSquareTargetClasses, wideOnlyButtonClasses), children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xAB" }) }),
984
+ /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.previousPage, disabled: !canPrevious, onClick: () => table.previousPage(), className: touchSquareTargetClasses, children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2039" }) }),
985
+ /* @__PURE__ */ jsxs4("span", { "data-slot": "data-grid-page-indicator", className: "hidden px-1 tabular-nums @sm:inline", children: [
975
986
  pageIndex + 1,
976
987
  " / ",
977
988
  pageCount
978
989
  ] }),
979
- /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.nextPage, disabled: !canNext, onClick: () => table.nextPage(), children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u203A" }) }),
980
- /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.lastPage, disabled: !canNext, onClick: () => table.setPageIndex(pageCount - 1), children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xBB" }) })
990
+ /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.nextPage, disabled: !canNext, onClick: () => table.nextPage(), className: touchSquareTargetClasses, children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u203A" }) }),
991
+ /* @__PURE__ */ jsx7(Button4, { variant: "ghost", size: "sm", "aria-label": labels.lastPage, disabled: !canNext, onClick: () => table.setPageIndex(pageCount - 1), className: cn5(touchSquareTargetClasses, wideOnlyButtonClasses), children: /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xBB" }) })
981
992
  ] })
982
993
  ]
983
994
  }
@@ -986,8 +997,8 @@ function DataGridPagination({ locale: localeProp, className, ...props }) {
986
997
 
987
998
  // src/selection-bar.tsx
988
999
  import { useEffect as useEffect2 } from "react";
989
- import Button5 from "@zuilib/components/button";
990
- import { cn as cn6 } from "@zuilib/components/lib/cn";
1000
+ import Button5 from "@zuilib/primitives/button";
1001
+ import { cn as cn6 } from "@zuilib/primitives/lib/cn";
991
1002
  import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
992
1003
  function DataGridSelectionBar({ className, ...props }) {
993
1004
  const { table, state, setState, selectionActions, rowCount, labels, locale, selectAllMatching } = useDataGridContext("SelectionBar");
@@ -1029,7 +1040,7 @@ function DataGridSelectionBar({ className, ...props }) {
1029
1040
  children: fillLabel(labels.selectAllMatching, { count: numberFormat.format(rowCount) })
1030
1041
  }
1031
1042
  ) : null,
1032
- /* @__PURE__ */ jsx8("span", { className: "flex flex-wrap items-center gap-1", children: selectionActions.map((action) => /* @__PURE__ */ jsx8(
1043
+ /* @__PURE__ */ jsx8("span", { "data-slot": "data-grid-selection-actions", className: "flex min-w-0 flex-wrap items-center gap-1", children: selectionActions.map((action) => /* @__PURE__ */ jsx8(
1033
1044
  Button5,
1034
1045
  {
1035
1046
  "data-slot": "data-grid-selection-action",
@@ -1050,9 +1061,9 @@ function DataGridSelectionBar({ className, ...props }) {
1050
1061
 
1051
1062
  // src/toolbar.tsx
1052
1063
  import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
1053
- import Badge from "@zuilib/components/badge";
1054
- import SearchInput from "@zuilib/components/search-input";
1055
- import { cn as cn7 } from "@zuilib/components/lib/cn";
1064
+ import Badge from "@zuilib/primitives/badge";
1065
+ import SearchInput from "@zuilib/primitives/search-input";
1066
+ import { cn as cn7 } from "@zuilib/primitives/lib/cn";
1056
1067
  import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1057
1068
  function DataGridToolbar({
1058
1069
  showSearch = true,
@@ -1101,7 +1112,7 @@ function DataGridToolbar({
1101
1112
  value: draft,
1102
1113
  onChange: (event) => changeSearch(event.target.value),
1103
1114
  onSubmit: commitSearch,
1104
- className: "w-64 max-w-full"
1115
+ className: "w-full max-w-full @sm:w-64"
1105
1116
  }
1106
1117
  ) : null,
1107
1118
  showFilterChips && activeFilters.length > 0 ? /* @__PURE__ */ jsx9("ul", { "data-slot": "data-grid-filter-chips", "aria-label": labels.activeFilters, className: "m-0 flex list-none flex-wrap items-center gap-1 p-0", children: activeFilters.map((filter) => {
@@ -1514,7 +1525,7 @@ function DataGridRoot({
1514
1525
  "data-slot": "data-grid",
1515
1526
  "data-density": density,
1516
1527
  "data-loading": loading ? "" : void 0,
1517
- className: cn8("flex flex-col gap-3 text-foreground", className),
1528
+ className: cn8("@container flex w-full min-w-0 flex-col gap-3 text-foreground", className),
1518
1529
  ...props,
1519
1530
  children: [
1520
1531
  children,
@@ -1548,7 +1559,7 @@ DataGrid.Pagination = DataGridPagination;
1548
1559
  var data_grid_default = DataGrid;
1549
1560
 
1550
1561
  // src/cells.tsx
1551
- import { cn as cn9 } from "@zuilib/components/lib/cn";
1562
+ import { cn as cn9 } from "@zuilib/primitives/lib/cn";
1552
1563
  import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1553
1564
  function toDate(value) {
1554
1565
  if (value === null || value === void 0 || value === "") return null;
@@ -1,6 +1,6 @@
1
- import { e as DataGridState } from './state-9r5WYKo_.js';
1
+ import { e as DataGridState } from './state-BK7izXrC.js';
2
2
  import '@tanstack/react-table';
3
- import '@zuilib/components/button';
3
+ import '@zuilib/primitives/button';
4
4
 
5
5
  /**
6
6
  * The grid state as a plain request object: what most list endpoints take.
@@ -1,5 +1,5 @@
1
1
  import { PaginationState, SortingState, ColumnFiltersState, VisibilityState, ColumnOrderState, RowSelectionState, ExpandedState, ColumnSizingState } from '@tanstack/react-table';
2
- import { ButtonVariant, ButtonTone } from '@zuilib/components/button';
2
+ import { ButtonVariant, ButtonTone } from '@zuilib/primitives/button';
3
3
 
4
4
  /**
5
5
  * Which columns stick to each edge. Logical directions: `start` is the
@@ -0,0 +1,2 @@
1
+ /** Tailwind v4 source registration for @zuilib/data-grid. */
2
+ @source "./**/*.js";
@@ -1,7 +1,7 @@
1
1
  import { Row, TableOptions, Table, Updater } from '@tanstack/react-table';
2
2
  import { b as DataGridColumnDef } from './columns-Def2RYjD.js';
3
- import { e as DataGridState } from './state-9r5WYKo_.js';
4
- import '@zuilib/components/button';
3
+ import { e as DataGridState } from './state-BK7izXrC.js';
4
+ import '@zuilib/primitives/button';
5
5
 
6
6
  /** Called with the whole next state on every interaction; see `onStateChange`. */
7
7
  type DataGridStateChangeHandler = (state: DataGridState) => void;
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@zuilib/data-grid",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "ZUI — an enterprise, server-driven data grid on TanStack Table, styled through ZUI tokens",
5
5
  "type": "module",
6
- "sideEffects": false,
6
+ "sideEffects": [
7
+ "**/*.css"
8
+ ],
7
9
  "files": [
8
10
  "dist"
9
11
  ],
@@ -28,21 +30,18 @@
28
30
  "types": "./dist/cells.d.ts",
29
31
  "import": "./dist/cells.js"
30
32
  },
33
+ "./tailwind.css": "./dist/tailwind.css",
31
34
  "./package.json": "./package.json"
32
35
  },
33
36
  "peerDependencies": {
34
- "@headlessui/react": "^2.2.0",
35
- "@tanstack/react-table": "^8.0.0",
36
37
  "react": "^18.0.0 || ^19.0.0",
37
- "react-dom": "^18.0.0 || ^19.0.0",
38
- "@zuilib/tokens": "^0.2.0"
38
+ "react-dom": "^18.0.0 || ^19.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@zuilib/components": "^0.2.0"
41
+ "@tanstack/react-table": "^8.0.0",
42
+ "@zuilib/primitives": "^0.3.0"
42
43
  },
43
44
  "devDependencies": {
44
- "@headlessui/react": "^2.2.9",
45
- "@tanstack/react-table": "^8.21.3",
46
45
  "@testing-library/dom": "^10.4.1",
47
46
  "@testing-library/jest-dom": "^7.0.1",
48
47
  "@testing-library/react": "^16.3.3",
@@ -54,8 +53,7 @@
54
53
  "react": "^18.2.0",
55
54
  "react-dom": "^18.2.0",
56
55
  "typescript": "^6.0.2",
57
- "vitest": "^4.1.11",
58
- "@zuilib/tokens": "^0.2.0"
56
+ "vitest": "^4.1.11"
59
57
  },
60
58
  "keywords": [
61
59
  "data-grid",
@@ -1,97 +0,0 @@
1
- import { ColumnDef, PaginationState, SortingState, ColumnFiltersState, VisibilityState, ColumnOrderState, ColumnPinningState, RowSelectionState, ExpandedState, ColumnSizingState } from '@tanstack/react-table';
2
-
3
- /**
4
- * Everything the grid can change. The consumer owns it: every interaction
5
- * calls `onStateChange` with the whole next state, and the grid renders
6
- * whatever comes back (a server round-trip in between is the point).
7
- */
8
- interface DataGridState {
9
- pagination: PaginationState;
10
- sorting: SortingState;
11
- columnFilters: ColumnFiltersState;
12
- globalFilter: string;
13
- columnVisibility: VisibilityState;
14
- columnOrder: ColumnOrderState;
15
- columnPinning: ColumnPinningState;
16
- rowSelection: RowSelectionState;
17
- /** Expanded rows, keyed by row id. UI-only: `toQuery` leaves it out. */
18
- expanded: ExpandedState;
19
- /**
20
- * Column widths in px, keyed by column id. Optional: left out, the grid
21
- * keeps the widths itself; passed (even `{}`), resizes go out through
22
- * `onStateChange` so they can be persisted. UI-only: `toQuery` leaves it out.
23
- */
24
- columnSizing?: ColumnSizingState;
25
- }
26
- declare const DEFAULT_PAGE_SIZE = 25;
27
- declare const defaultDataGridState: DataGridState;
28
- /** Fills the keys a consumer left out, so the grid always sees a full state. */
29
- declare function resolveDataGridState(state: Partial<DataGridState> | undefined): DataGridState;
30
- type DataGridAlign = 'start' | 'center' | 'end';
31
- /** A text filter: one string, matched however the server matches it. */
32
- interface DataGridTextFilter {
33
- type: 'text';
34
- placeholder?: string;
35
- }
36
- /** A facet filter: a list of options, of which any number may be picked. */
37
- interface DataGridSelectFilter {
38
- type: 'select';
39
- options: readonly {
40
- value: string;
41
- label?: string;
42
- }[];
43
- }
44
- /** A date range: ISO `YYYY-MM-DD` bounds, either side optional. */
45
- interface DataGridDateRangeFilter {
46
- type: 'date-range';
47
- }
48
- type DataGridFilter = DataGridTextFilter | DataGridSelectFilter | DataGridDateRangeFilter;
49
- interface DataGridDateRange {
50
- from?: string;
51
- to?: string;
52
- }
53
- /** The ZUI extras on a TanStack column definition. */
54
- interface DataGridColumnExtras {
55
- /** Double-click or Enter on a cell turns it into an input; see `onCellEdit`. */
56
- enableInlineEdit?: boolean;
57
- /** Horizontal alignment of the head and the cells; `end` for numbers. */
58
- align?: DataGridAlign;
59
- /** Initial width in px (TanStack `size`); the user can resize from there. */
60
- width?: number;
61
- /** Declares the column's filter popover. */
62
- filter?: DataGridFilter;
63
- }
64
- type DataGridColumnDef<TData, TValue = unknown> = ColumnDef<TData, TValue> & DataGridColumnExtras;
65
- /** The extras may also be declared on `meta` (the TanStack way of extending a column). */
66
- interface DataGridColumnMeta extends DataGridColumnExtras {
67
- /** The column's name in the column manager, the filter chips and the pin labels, when `header` is not a string. */
68
- label?: string;
69
- }
70
- /** The grid's own strings, overridable for another language. */
71
- interface DataGridLabels {
72
- /** @default 'Pin left' */
73
- pinLeft: string;
74
- /** @default 'Pin right' */
75
- pinRight: string;
76
- /** @default 'Unpin' */
77
- unpin: string;
78
- /** @default 'Expand row' */
79
- expandRow: string;
80
- /** @default 'Collapse row' */
81
- collapseRow: string;
82
- }
83
- declare const defaultDataGridLabels: DataGridLabels;
84
- interface DataGridCellEdit {
85
- rowId: string;
86
- columnId: string;
87
- value: string;
88
- }
89
- interface DataGridBulkAction {
90
- id: string;
91
- label: string;
92
- variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost';
93
- onAction: (selectedIds: string[]) => void;
94
- }
95
- type DataGridDensity = 'compact' | 'comfortable';
96
-
97
- export { type DataGridState as D, type DataGridDensity as a, type DataGridCellEdit as b, type DataGridBulkAction as c, type DataGridLabels as d, DEFAULT_PAGE_SIZE as e, type DataGridAlign as f, type DataGridColumnDef as g, type DataGridColumnExtras as h, type DataGridColumnMeta as i, type DataGridDateRange as j, type DataGridDateRangeFilter as k, type DataGridFilter as l, type DataGridSelectFilter as m, type DataGridTextFilter as n, defaultDataGridLabels as o, defaultDataGridState as p, resolveDataGridState as r };