najm-kit 2.2.8 → 2.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +181 -195
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -12507,9 +12507,9 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
12507
12507
  const calculatePageSize = () => {
12508
12508
  const container2 = containerRef.current;
12509
12509
  if (!container2) return;
12510
+ if (error || hasNoData && !isFilteredEmpty && !isLoading) return;
12510
12511
  const bodyEl = container2.querySelector("[data-ntable-body]");
12511
12512
  const tableHeaderEl = container2.querySelector("[data-ntable-table-header]");
12512
- const loadingHeaderEl = container2.querySelector("[data-ntable-loading-header]");
12513
12513
  const cardsGridEl = Array.from(
12514
12514
  container2.querySelectorAll(
12515
12515
  "[data-ntable-loading-cards-grid], [data-ntable-cards-grid]"
@@ -12525,11 +12525,6 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
12525
12525
  const gap = Number.parseFloat(rootStyles.rowGap || rootStyles.gap || "0") || 0;
12526
12526
  bodyHeight = rootHeight - headerHeight - paginationHeight - gap * ROOT_SECTION_GAP_COUNT;
12527
12527
  }
12528
- if (loadingHeaderEl && bodyEl) {
12529
- const bodyStyles = window.getComputedStyle(bodyEl);
12530
- const bodyGap = Number.parseFloat(bodyStyles.rowGap || bodyStyles.gap || "0") || 0;
12531
- bodyHeight = Math.max(0, bodyHeight - loadingHeaderEl.offsetHeight - bodyGap);
12532
- }
12533
12528
  const tableHeaderHeight = tableHeaderEl?.offsetHeight ?? DEFAULT_TABLE_HEADER_HEIGHT;
12534
12529
  const newPageSize = calculateDynamicPageSize({ bodyHeight, tableHeaderHeight });
12535
12530
  const calculatedMaxHeight = tableHeaderHeight + newPageSize * ROW_HEIGHT;
@@ -12578,7 +12573,7 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
12578
12573
  "[data-ntable-header], [data-ntable-body], [data-ntable-pagination], [data-ntable-table-header]"
12579
12574
  ).forEach((el) => resizeObserver.observe(el));
12580
12575
  container.querySelectorAll(
12581
- "[data-ntable-loading-header], [data-ntable-loading-cards-grid], [data-ntable-loading-card], [data-ntable-cards-grid]"
12576
+ "[data-ntable-loading-cards-grid], [data-ntable-loading-card], [data-ntable-cards-grid]"
12582
12577
  ).forEach((el) => resizeObserver.observe(el));
12583
12578
  if (container.parentElement) resizeObserver.observe(container.parentElement);
12584
12579
  return () => resizeObserver.disconnect();
@@ -12612,6 +12607,7 @@ function useTable(effectiveViewModeOverride) {
12612
12607
  const onStateChange = useTableStore.use.onStateChange();
12613
12608
  const getRowId = useTableStore.use.getRowId();
12614
12609
  const manualPagination = useTableStore.use.manualPagination();
12610
+ const hasDataRows = useTableStore.use.hasData();
12615
12611
  const pageCount = useTableStore.use.pageCount();
12616
12612
  const rowCount = useTableStore.use.rowCount();
12617
12613
  const storePagination = useTableStore.use.pagination();
@@ -12725,6 +12721,11 @@ function useTable(effectiveViewModeOverride) {
12725
12721
  tableConfig.getRowCanExpand = userGetRowCanExpand ? (row) => userGetRowCanExpand(row.original) : () => true;
12726
12722
  }
12727
12723
  const table = useReactTable(tableConfig);
12724
+ const hasSeededTableRef = useRef(false);
12725
+ if (!hasSeededTableRef.current) {
12726
+ hasSeededTableRef.current = true;
12727
+ syncWithProps({ table });
12728
+ }
12728
12729
  useLayoutEffect(() => {
12729
12730
  syncWithProps({ table });
12730
12731
  }, [table]);
@@ -12745,12 +12746,17 @@ function useTable(effectiveViewModeOverride) {
12745
12746
  if (!dynamicPageSizeTarget || dynamicPageSizeTarget < 1) return;
12746
12747
  if (dynamicPageSizeTarget === storePagination.pageSize) return;
12747
12748
  const reported = reportedGeometryRef.current;
12749
+ if (reported?.key === geometryKey && reported.target === dynamicPageSizeTarget) return;
12748
12750
  if (reported?.key === geometryKey && reported.count >= MAX_PAGE_SIZE_REPORTS_PER_GEOMETRY) return;
12749
12751
  const commit = () => {
12750
12752
  const current = reportedGeometryRef.current;
12751
- reportedGeometryRef.current = current?.key === geometryKey ? { key: geometryKey, count: current.count + 1 } : { key: geometryKey, count: 1 };
12753
+ reportedGeometryRef.current = current?.key === geometryKey ? { key: geometryKey, count: current.count + 1, target: dynamicPageSizeTarget } : { key: geometryKey, count: 1, target: dynamicPageSizeTarget };
12752
12754
  setPagination({ pageIndex: storePagination.pageIndex, pageSize: dynamicPageSizeTarget });
12753
12755
  };
12756
+ if (!hasDataRows) {
12757
+ commit();
12758
+ return;
12759
+ }
12754
12760
  if (!reported) {
12755
12761
  commit();
12756
12762
  return;
@@ -12767,6 +12773,7 @@ function useTable(effectiveViewModeOverride) {
12767
12773
  dynamicPageSizeTarget,
12768
12774
  storePagination.pageIndex,
12769
12775
  storePagination.pageSize,
12776
+ hasDataRows,
12770
12777
  setPagination
12771
12778
  ]);
12772
12779
  return { table, finalColumns, sorting, setSorting, columnFilters, setColumnFilters, columnVisibility, setColumnVisibility, globalFilter, setGlobalFilter };
@@ -12960,6 +12967,11 @@ function NTableContent({ effectiveMode }) {
12960
12967
  const onCellEdit = useTableStore.use.onCellEdit();
12961
12968
  useTableStore.use.isLoading();
12962
12969
  const error = useTableStore.use.error();
12970
+ const contentDynamicHeight = useTableStore.use.dynamicHeight();
12971
+ const contentManualPagination = useTableStore.use.manualPagination();
12972
+ const contentCardPagination = useTableStore.use.cardPagination();
12973
+ const contentCalculatedPageSize = useTableStore.use.calculatedPageSize();
12974
+ const contentHasMeasuredLayout = useTableStore.use.hasMeasuredLayout();
12963
12975
  const hasNoData = useTableStore.use.hasNoData();
12964
12976
  const showContent = useTableStore.use.showContent();
12965
12977
  const classNames = useTableStore.use.classNames();
@@ -12981,6 +12993,9 @@ function NTableContent({ effectiveMode }) {
12981
12993
  if (error || hasNoData || !showContent || !table) return null;
12982
12994
  const isTableView = effectiveMode ? effectiveMode === "table" : storeIsTableView;
12983
12995
  if (!isTableView) return null;
12996
+ const allRows = table.getRowModel().rows ?? [];
12997
+ const clampRowsToMeasuredPage = contentDynamicHeight && contentManualPagination && contentCardPagination.mode === "paged" && contentHasMeasuredLayout && contentCalculatedPageSize > 0;
12998
+ const visibleRows = clampRowsToMeasuredPage ? allRows.slice(0, contentCalculatedPageSize) : allRows;
12984
12999
  const getSortIcon = (column) => {
12985
13000
  const dir = column.getIsSorted();
12986
13001
  if (dir === "asc") return /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" });
@@ -12993,13 +13008,22 @@ function NTableContent({ effectiveMode }) {
12993
13008
  axis: "both",
12994
13009
  "data-bordered": bordered === false ? "false" : bordered ? "true" : void 0,
12995
13010
  className: cn(
12996
- "min-h-0 flex-1 overflow-hidden rounded-md",
13011
+ "min-h-0 overflow-hidden rounded-md",
13012
+ // Under `dynamicHeight` the page size is the largest row count that
13013
+ // fits, so the rows are always a little shorter than the space they
13014
+ // were measured against — up to one row's worth. Growing into that
13015
+ // remainder leaves the container's bottom edge floating below the last
13016
+ // row. Sizing to content instead ends the border on the last row; the
13017
+ // leftover belongs to the page, not to the table. Without
13018
+ // `dynamicHeight` the row count is the caller's, so the container has
13019
+ // to keep filling its height to stay a scroll viewport.
13020
+ contentDynamicHeight ? "max-h-full shrink" : "flex-1",
12997
13021
  surface.className,
12998
13022
  classNames?.content
12999
13023
  ),
13000
13024
  style: surface.style,
13001
13025
  onContextMenu: handleBackgroundContextMenu,
13002
- children: /* @__PURE__ */ jsxs(Table, { children: [
13026
+ children: /* @__PURE__ */ jsxs(Table, { className: "table-fixed", children: [
13003
13027
  /* @__PURE__ */ jsx(TableHeader, { "data-ntable-table-header": true, className: cn("bg-card sticky top-0 z-10", headerClassName, bordered === true && "[&_tr]:border-border", classNames?.tableHeader), children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsxs(TableRow, { style: rowBorderStyle, className: cn("hover:bg-transparent", bordered === true && "border-border"), children: [
13004
13028
  showCheckbox && /* @__PURE__ */ jsx(
13005
13029
  TableHead,
@@ -13030,7 +13054,7 @@ function NTableContent({ effectiveMode }) {
13030
13054
  return /* @__PURE__ */ jsx(
13031
13055
  TableHead,
13032
13056
  {
13033
- className: cn("text-foreground h-12", responsiveClass),
13057
+ className: cn("text-foreground h-12 overflow-hidden text-ellipsis", responsiveClass),
13034
13058
  style: headerCellStyle,
13035
13059
  children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", header.column.getCanSort() && showSorting && "cursor-pointer select-none"), onClick: header.column.getToggleSortingHandler(), children: [
13036
13060
  flexRender(header.column.columnDef.header, header.getContext()),
@@ -13041,7 +13065,7 @@ function NTableContent({ effectiveMode }) {
13041
13065
  );
13042
13066
  })
13043
13067
  ] }, hg.id)) }),
13044
- /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => {
13068
+ /* @__PURE__ */ jsx(TableBody, { children: visibleRows.length ? visibleRows.map((row) => {
13045
13069
  const isSelectedByRowId = Boolean(selectedRowId && row.original?.id === selectedRowId);
13046
13070
  const canExpand = hasExpansion && row.getCanExpand();
13047
13071
  const isExpanded = canExpand && row.getIsExpanded();
@@ -13094,7 +13118,7 @@ function NTableContent({ effectiveMode }) {
13094
13118
  const meta = columnDef.meta || {};
13095
13119
  const isEditable = Boolean(onCellEdit) && Boolean(meta.editable);
13096
13120
  const responsiveClass = resolveHiddenBelowClass(meta.hiddenBelow);
13097
- return /* @__PURE__ */ jsx(TableCell, { className: cn("h-14", responsiveClass), children: isEditable ? /* @__PURE__ */ jsx(EditableCell, { cell, onCellEdit }) : flexRender(columnDef.cell, cell.getContext()) }, cell.id);
13121
+ return /* @__PURE__ */ jsx(TableCell, { title: typeof cell.getValue?.() === "string" ? cell.getValue() : void 0, className: cn("h-14 overflow-hidden text-ellipsis", responsiveClass), children: isEditable ? /* @__PURE__ */ jsx(EditableCell, { cell, onCellEdit }) : flexRender(columnDef.cell, cell.getContext()) }, cell.id);
13098
13122
  })
13099
13123
  ]
13100
13124
  }
@@ -13234,6 +13258,17 @@ function NDataCardShell({ row, onClick, onContextMenu, actions, children, classN
13234
13258
  var DEFAULT_ROWS2 = 6;
13235
13259
  var UNMEASURED_DYNAMIC_ROWS = 12;
13236
13260
  var DEFAULT_CARD_COUNT = 48;
13261
+ var VISUALLY_HIDDEN = {
13262
+ position: "absolute",
13263
+ width: 1,
13264
+ height: 1,
13265
+ margin: -1,
13266
+ padding: 0,
13267
+ overflow: "hidden",
13268
+ clip: "rect(0,0,0,0)",
13269
+ whiteSpace: "nowrap",
13270
+ borderWidth: 0
13271
+ };
13237
13272
  function NTableCardSkeleton({ surface }) {
13238
13273
  return /* @__PURE__ */ jsx(
13239
13274
  "div",
@@ -13293,75 +13328,6 @@ function NTableCardSkeleton({ surface }) {
13293
13328
  }
13294
13329
  );
13295
13330
  }
13296
- function NTableHeaderSkeleton() {
13297
- const filters = useTableStore.use.filters();
13298
- const showViewToggle = useTableStore.use.showViewToggle();
13299
- const showColumnVisibility = useTableStore.use.showColumnVisibility();
13300
- const showAddButton = useTableStore.use.showAddButton();
13301
- const hasHeaderSlot = Boolean(useTableStore.use.headerSlot());
13302
- const hasToolbar = Boolean(useTableStore.use.renderToolbar());
13303
- const filterCount = Math.min(Math.max(filters?.length ?? 0, 1), 3);
13304
- const hasActions = showViewToggle || showColumnVisibility || showAddButton || hasHeaderSlot || hasToolbar;
13305
- const hasSettings = showViewToggle || showColumnVisibility || hasHeaderSlot || hasToolbar;
13306
- if (!filters?.length && !hasActions) return null;
13307
- return /* @__PURE__ */ jsxs(
13308
- "div",
13309
- {
13310
- "data-ntable-loading-header": true,
13311
- className: "flex shrink-0 flex-wrap items-center justify-between gap-2",
13312
- children: [
13313
- filters?.length ? /* @__PURE__ */ jsx(
13314
- "div",
13315
- {
13316
- "data-ntable-loading-desktop-filters": true,
13317
- className: "hidden min-w-0 flex-1 flex-wrap gap-2 md:flex",
13318
- children: Array.from({ length: filterCount }).map((_, index) => /* @__PURE__ */ jsx(
13319
- NSkeleton,
13320
- {
13321
- className: cn("h-10 w-full rounded-lg", index < 2 ? "max-w-64" : "max-w-48")
13322
- },
13323
- index
13324
- ))
13325
- }
13326
- ) : /* @__PURE__ */ jsx("span", { className: "hidden min-w-0 flex-1 md:block" }),
13327
- /* @__PURE__ */ jsxs(
13328
- "div",
13329
- {
13330
- "data-ntable-loading-mobile-toolbar": true,
13331
- className: "flex w-full min-w-0 items-center gap-2 md:hidden",
13332
- children: [
13333
- filters?.length ? /* @__PURE__ */ jsx(
13334
- NSkeleton,
13335
- {
13336
- "data-ntable-loading-mobile-primary": true,
13337
- className: "h-10 min-w-0 flex-1 rounded-lg"
13338
- }
13339
- ) : null,
13340
- filters?.length > 1 ? /* @__PURE__ */ jsx(
13341
- NSkeleton,
13342
- {
13343
- "data-ntable-loading-mobile-filter-button": true,
13344
- className: "h-10 w-10 shrink-0 rounded-lg"
13345
- }
13346
- ) : null,
13347
- showAddButton ? /* @__PURE__ */ jsx(
13348
- NSkeleton,
13349
- {
13350
- "data-ntable-loading-mobile-add-button": true,
13351
- className: "h-10 w-10 shrink-0 rounded-lg"
13352
- }
13353
- ) : null
13354
- ]
13355
- }
13356
- ),
13357
- hasActions && /* @__PURE__ */ jsxs("div", { className: "hidden shrink-0 gap-2 md:flex", children: [
13358
- hasSettings && /* @__PURE__ */ jsx(NSkeleton, { className: "h-10 w-10 rounded-lg" }),
13359
- showAddButton && /* @__PURE__ */ jsx(NSkeleton, { className: "h-10 w-10 rounded-lg" })
13360
- ] })
13361
- ]
13362
- }
13363
- );
13364
- }
13365
13331
  function NTableLoadingSkeleton({ rows }) {
13366
13332
  const rawColumns = useTableStore.use.columns();
13367
13333
  const responsiveColumns = React__default.useMemo(() => filterResponsiveColumns(rawColumns), [rawColumns]);
@@ -13381,64 +13347,62 @@ function NTableLoadingSkeleton({ rows }) {
13381
13347
  const loadingText = useTableStore.use.loadingText();
13382
13348
  const rowCount = rows ?? (dynamicHeight ? bodyHeight > 0 ? skeletonRowCount : UNMEASURED_DYNAMIC_ROWS : DEFAULT_ROWS2);
13383
13349
  const renderHeaderLabel = (header) => typeof header === "string" ? header : null;
13384
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13385
- /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13386
- /* @__PURE__ */ jsxs(
13387
- "div",
13388
- {
13389
- "data-testid": "ntable-loading-skeleton",
13390
- "data-ntable-loading-row-count": rowCount,
13391
- "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13392
- "aria-busy": "true",
13393
- "aria-label": loadingText,
13394
- role: "status",
13395
- style: surface.style,
13396
- className: cn("min-h-0 flex-1 rounded-md p-0", surface.className, dynamicHeight ? "overflow-hidden" : "najm-overlay-scroll", classNames?.content),
13397
- children: [
13398
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13399
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: dynamicHeight ? "najm-overlay-scroll h-full" : void 0, children: /* @__PURE__ */ jsxs(Table, { children: [
13400
- /* @__PURE__ */ jsx(TableHeader, { "data-ntable-table-header": true, className: cn(headerClassName, "sticky top-0 z-10", classNames?.tableHeader), children: /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-muted/30", children: [
13401
- showCheckbox && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Select column", className: "w-10 text-foreground h-12" }),
13402
- hasExpansion && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Expand column", className: "w-10 text-foreground h-12" }),
13403
- columns.map((col, i) => /* @__PURE__ */ jsx(
13404
- TableHead,
13405
- {
13406
- className: cn("text-foreground h-12", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13407
- style: col?.size ? { width: col.size } : void 0,
13408
- children: renderHeaderLabel(col?.header)
13409
- },
13410
- col?.id ?? col?.accessorKey ?? i
13411
- ))
13412
- ] }) }),
13413
- /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rowCount }).map((_, r) => /* @__PURE__ */ jsxs(TableRow, { children: [
13414
- showCheckbox && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13415
- hasExpansion && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13416
- columns.map((col, c) => /* @__PURE__ */ jsx(
13417
- TableCell,
13418
- {
13419
- className: cn("h-14", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13420
- style: col?.size ? { width: col.size } : void 0,
13421
- children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-full" })
13422
- },
13423
- `skeleton-${r}-${col?.id ?? col?.accessorKey ?? c}`
13424
- ))
13425
- ] }, `skeleton-${r}`)) })
13426
- ] }) })
13427
- ]
13428
- }
13429
- )
13430
- ] });
13350
+ return /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: /* @__PURE__ */ jsxs(
13351
+ "div",
13352
+ {
13353
+ "data-testid": "ntable-loading-skeleton",
13354
+ "data-ntable-loading-row-count": rowCount,
13355
+ "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13356
+ "aria-busy": "true",
13357
+ "aria-label": loadingText,
13358
+ role: "status",
13359
+ style: surface.style,
13360
+ className: cn(
13361
+ "min-h-0 rounded-md p-0",
13362
+ surface.className,
13363
+ // Sizes to its rows for the same reason `NTableContent` does, and it
13364
+ // has to match: the skeleton draws exactly the row count the real
13365
+ // table will render, so a box that fills its container here and hugs
13366
+ // its rows there would visibly resize the instant the rows arrive
13367
+ // the one thing this whole layout is meant to avoid.
13368
+ dynamicHeight ? "max-h-full shrink overflow-hidden" : "flex-1 najm-overlay-scroll",
13369
+ classNames?.content
13370
+ ),
13371
+ children: [
13372
+ /* @__PURE__ */ jsx("span", { style: VISUALLY_HIDDEN, children: loadingText }),
13373
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: dynamicHeight ? "najm-overlay-scroll" : void 0, children: /* @__PURE__ */ jsxs(Table, { className: "table-fixed", children: [
13374
+ /* @__PURE__ */ jsx(TableHeader, { "data-ntable-table-header": true, className: cn(headerClassName, "sticky top-0 z-10", classNames?.tableHeader), children: /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-muted/30", children: [
13375
+ showCheckbox && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Select column", className: "w-10 text-foreground h-12" }),
13376
+ hasExpansion && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Expand column", className: "w-10 text-foreground h-12" }),
13377
+ columns.map((col, i) => /* @__PURE__ */ jsx(
13378
+ TableHead,
13379
+ {
13380
+ className: cn("text-foreground h-12", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13381
+ style: col?.size ? { width: col.size } : void 0,
13382
+ children: renderHeaderLabel(col?.header)
13383
+ },
13384
+ col?.id ?? col?.accessorKey ?? i
13385
+ ))
13386
+ ] }) }),
13387
+ /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rowCount }).map((_, r) => /* @__PURE__ */ jsxs(TableRow, { children: [
13388
+ showCheckbox && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13389
+ hasExpansion && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13390
+ columns.map((col, c) => /* @__PURE__ */ jsx(
13391
+ TableCell,
13392
+ {
13393
+ className: cn("h-14", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13394
+ style: col?.size ? { width: col.size } : void 0,
13395
+ children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-full" })
13396
+ },
13397
+ `skeleton-${r}-${col?.id ?? col?.accessorKey ?? c}`
13398
+ ))
13399
+ ] }, `skeleton-${r}`)) })
13400
+ ] }) })
13401
+ ]
13402
+ }
13403
+ ) });
13431
13404
  }
13432
13405
  function NTableCardsLoadingSkeleton({ rows }) {
13433
- const filters = useTableStore.use.filters();
13434
- const showViewToggle = useTableStore.use.showViewToggle();
13435
- const showColumnVisibility = useTableStore.use.showColumnVisibility();
13436
- const showAddButton = useTableStore.use.showAddButton();
13437
- const hasHeaderSlot = Boolean(useTableStore.use.headerSlot());
13438
- const hasToolbar = Boolean(useTableStore.use.renderToolbar());
13439
- const hasHeaderSkeleton = Boolean(
13440
- filters?.length || showViewToggle || showColumnVisibility || showAddButton || hasHeaderSlot || hasToolbar
13441
- );
13442
13406
  const classNames = useTableStore.use.classNames();
13443
13407
  const bordered = useTableStore.use.bordered();
13444
13408
  const borderColor = useTableStore.use.borderColor();
@@ -13458,33 +13422,30 @@ function NTableCardsLoadingSkeleton({ rows }) {
13458
13422
  }) : DEFAULT_CARD_COUNT);
13459
13423
  const defaultContainerClass = "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4";
13460
13424
  const containerClass = classNames?.cards ?? defaultContainerClass;
13461
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13462
- hasHeaderSkeleton && /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13463
- /* @__PURE__ */ jsxs(
13464
- NajmScroll,
13465
- {
13466
- axis: "y",
13467
- "aria-busy": "true",
13468
- "aria-label": loadingText,
13469
- role: "status",
13470
- className: "min-h-0 flex-1 overflow-hidden",
13471
- children: [
13472
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13473
- /* @__PURE__ */ jsx(
13474
- "div",
13475
- {
13476
- "data-testid": "ntable-cards-loading-skeleton",
13477
- "data-ntable-loading-cards-grid": true,
13478
- "data-ntable-loading-card-count": cardCount,
13479
- "aria-hidden": "true",
13480
- className: cn(containerClass),
13481
- children: Array.from({ length: cardCount }).map((_, index) => CardSkeletonComponent ? /* @__PURE__ */ jsx("div", { "data-ntable-loading-card": true, children: /* @__PURE__ */ jsx(CardSkeletonComponent, {}) }, index) : /* @__PURE__ */ jsx(NTableCardSkeleton, { surface }, index))
13482
- }
13483
- )
13484
- ]
13485
- }
13486
- )
13487
- ] });
13425
+ return /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: /* @__PURE__ */ jsxs(
13426
+ NajmScroll,
13427
+ {
13428
+ axis: "y",
13429
+ "aria-busy": "true",
13430
+ "aria-label": loadingText,
13431
+ role: "status",
13432
+ className: "min-h-0 flex-1 overflow-hidden",
13433
+ children: [
13434
+ /* @__PURE__ */ jsx("span", { style: VISUALLY_HIDDEN, children: loadingText }),
13435
+ /* @__PURE__ */ jsx(
13436
+ "div",
13437
+ {
13438
+ "data-testid": "ntable-cards-loading-skeleton",
13439
+ "data-ntable-loading-cards-grid": true,
13440
+ "data-ntable-loading-card-count": cardCount,
13441
+ "aria-hidden": "true",
13442
+ className: cn(containerClass),
13443
+ children: Array.from({ length: cardCount }).map((_, index) => CardSkeletonComponent ? /* @__PURE__ */ jsx("div", { "data-ntable-loading-card": true, children: /* @__PURE__ */ jsx(CardSkeletonComponent, {}) }, index) : /* @__PURE__ */ jsx(NTableCardSkeleton, { surface }, index))
13444
+ }
13445
+ )
13446
+ ]
13447
+ }
13448
+ ) });
13488
13449
  }
13489
13450
  var DEFAULT_ROOT_MARGIN = "80px";
13490
13451
  function useCardContinuation({
@@ -13876,7 +13837,7 @@ function NTablePagination() {
13876
13837
  const setPagination = useTableStore.use.setPagination();
13877
13838
  const isPaginationControlled = useTableStore.use.isPaginationControlled();
13878
13839
  const bordered = useTableStore.use.bordered();
13879
- if (!table || !showPagination || effectiveViewMode === "json" || effectiveViewMode === "files") return null;
13840
+ if (!showPagination || effectiveViewMode === "json" || effectiveViewMode === "files") return null;
13880
13841
  if (cardPagination.mode === "all") return null;
13881
13842
  if (effectiveViewMode === "cards" && cardPagination.mode === "infinite") return null;
13882
13843
  if (effectiveViewMode === "cards" && cardPagination.mode === "load-more") {
@@ -13890,10 +13851,10 @@ function NTablePagination() {
13890
13851
  }
13891
13852
  );
13892
13853
  }
13893
- const filteredRows = table.getFilteredRowModel().rows;
13894
- const selectedRows = table.getFilteredSelectedRowModel().rows;
13895
- const { pageIndex, pageSize } = table.getState().pagination;
13896
- const effectivePageCount = manualPagination && pageCount !== void 0 ? pageCount : table.getPageCount();
13854
+ const filteredRows = table ? table.getFilteredRowModel().rows : [];
13855
+ const selectedRows = table ? table.getFilteredSelectedRowModel().rows : [];
13856
+ const { pageIndex, pageSize } = table ? table.getState().pagination : pagination;
13857
+ const effectivePageCount = manualPagination && pageCount !== void 0 ? pageCount : table ? table.getPageCount() : 1;
13897
13858
  const currentPagination = pagination ?? { pageIndex, pageSize };
13898
13859
  const currentPageSizeOptions = pageSizeOptions.includes(pageSize) ? pageSizeOptions : [...pageSizeOptions, pageSize].sort((a, b) => a - b);
13899
13860
  const navigate = (direction) => {
@@ -13941,10 +13902,10 @@ function NTablePagination() {
13941
13902
  effectivePageCount
13942
13903
  ] }),
13943
13904
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
13944
- /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "hidden h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70 lg:flex", "aria-label": "First page", onClick: () => navigate("first"), disabled: !table.getCanPreviousPage?.() || pageIndex === 0, children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" }) }),
13945
- /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70", "aria-label": "Previous", onClick: () => navigate("prev"), disabled: !table.getCanPreviousPage?.() || pageIndex === 0, children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) }),
13946
- /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70", "aria-label": "Next", onClick: () => navigate("next"), disabled: !table.getCanNextPage?.() || pageIndex >= effectivePageCount - 1, children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) }),
13947
- /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "hidden h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70 lg:flex", "aria-label": "Last page", onClick: () => navigate("last"), disabled: !table.getCanNextPage?.() || pageIndex >= effectivePageCount - 1, children: /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" }) })
13905
+ /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "hidden h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70 lg:flex", "aria-label": "First page", onClick: () => navigate("first"), disabled: !table?.getCanPreviousPage?.() || pageIndex === 0, children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" }) }),
13906
+ /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70", "aria-label": "Previous", onClick: () => navigate("prev"), disabled: !table?.getCanPreviousPage?.() || pageIndex === 0, children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) }),
13907
+ /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70", "aria-label": "Next", onClick: () => navigate("next"), disabled: !table?.getCanNextPage?.() || pageIndex >= effectivePageCount - 1, children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) }),
13908
+ /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: "hidden h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70 lg:flex", "aria-label": "Last page", onClick: () => navigate("last"), disabled: !table?.getCanNextPage?.() || pageIndex >= effectivePageCount - 1, children: /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" }) })
13948
13909
  ] })
13949
13910
  ] }),
13950
13911
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-none whitespace-nowrap text-sm text-muted-foreground max-sm:hidden", children: [
@@ -13955,10 +13916,14 @@ function NTablePagination() {
13955
13916
  ] })
13956
13917
  ] });
13957
13918
  }
13919
+ function PendingFilter({ placeholder, icon, bordered }) {
13920
+ return /* @__PURE__ */ jsx(TextInput, { icon, value: "", onChange: () => {
13921
+ }, placeholder, bordered, disabled: true });
13922
+ }
13958
13923
  function SearchFilter({ placeholder }) {
13959
13924
  const table = useTableStore.use.table();
13960
13925
  const bordered = useTableStore.use.bordered();
13961
- if (!table) return null;
13926
+ if (!table) return /* @__PURE__ */ jsx(PendingFilter, { icon: Search, placeholder: placeholder ?? "Search\u2026", bordered });
13962
13927
  const value = table.getState().globalFilter ?? "";
13963
13928
  return /* @__PURE__ */ jsx(TextInput, { icon: Search, value, onChange: (v) => table.setGlobalFilter(v), placeholder: placeholder ?? "Search\u2026", bordered });
13964
13929
  }
@@ -13966,14 +13931,14 @@ function TextFilter({ name, placeholder, icon }) {
13966
13931
  const table = useTableStore.use.table();
13967
13932
  const bordered = useTableStore.use.bordered();
13968
13933
  const column = table?.getColumn?.(name);
13969
- if (!column) return null;
13934
+ if (!column) return /* @__PURE__ */ jsx(PendingFilter, { icon, placeholder, bordered });
13970
13935
  return /* @__PURE__ */ jsx(TextInput, { icon, value: column.getFilterValue() ?? "", onChange: (value) => column.setFilterValue(value), placeholder, bordered });
13971
13936
  }
13972
13937
  function SelectFilter({ name, options, placeholder, inputType }) {
13973
13938
  const table = useTableStore.use.table();
13974
13939
  const bordered = useTableStore.use.bordered();
13975
13940
  const column = table?.getColumn?.(name);
13976
- if (!column) return null;
13941
+ if (!column) return /* @__PURE__ */ jsx(PendingFilter, { placeholder: placeholder || "Filter...", bordered });
13977
13942
  const allOptions = [{ value: "__clear__", label: "All" }, ...options.map((o) => typeof o === "string" ? { value: o, label: o } : o)];
13978
13943
  const InputComponent = inputType === "combobox" ? ComboboxInput : SelectInput;
13979
13944
  return /* @__PURE__ */ jsx(InputComponent, { value: column.getFilterValue() ?? "", onChange: (value) => column.setFilterValue(value === "" || value === "__clear__" ? void 0 : value), items: allOptions, placeholder: placeholder || "Filter...", bordered });
@@ -13982,7 +13947,7 @@ function defaultWrapperClass(filter) {
13982
13947
  return filter.type === "search" ? "flex-1 min-w-[160px] max-w-sm" : "w-full sm:w-40 xl:w-56 shrink-0";
13983
13948
  }
13984
13949
  function RenderFilter({ filter, mobilePrimary = false }) {
13985
- const table = useTableStore.use.table();
13950
+ useTableStore.use.table();
13986
13951
  const bordered = useTableStore.use.bordered();
13987
13952
  if (filter.type === "search") {
13988
13953
  return /* @__PURE__ */ jsx(SearchFilter, { placeholder: filter.placeholder });
@@ -14049,20 +14014,24 @@ function RenderFilter({ filter, mobilePrimary = false }) {
14049
14014
  if (filter.type === "text") {
14050
14015
  return /* @__PURE__ */ jsx(TextFilter, { name: filter.name, placeholder: filter.placeholder, icon: mobilePrimary ? Search : void 0 });
14051
14016
  }
14052
- if (!table) return null;
14053
14017
  return /* @__PURE__ */ jsx(SelectFilter, { name: filter.name, options: filter.options || [], placeholder: filter.placeholder, inputType: filter.type });
14054
14018
  }
14055
14019
  function TableFilters() {
14056
14020
  const filters = useTableStore.use.filters();
14057
14021
  if (!filters?.length) return null;
14058
- return /* @__PURE__ */ jsx("div", { "data-ntable-desktop-filters": true, className: "hidden flex-1 min-w-0 flex-wrap items-center gap-2 md:flex", children: filters.map((filter) => /* @__PURE__ */ jsx(
14059
- "div",
14060
- {
14061
- className: cn(defaultWrapperClass(filter), filter.className),
14062
- children: /* @__PURE__ */ jsx(RenderFilter, { filter })
14063
- },
14064
- filter.name
14065
- )) });
14022
+ return (
14023
+ // `min-h-10` is the height of one control row. Even if every filter inside
14024
+ // resolves to nothing, the row cannot collapse and change what the body
14025
+ // measures.
14026
+ /* @__PURE__ */ jsx("div", { "data-ntable-desktop-filters": true, className: "hidden min-h-10 flex-1 min-w-0 flex-wrap items-center gap-2 md:flex", children: filters.map((filter) => /* @__PURE__ */ jsx(
14027
+ "div",
14028
+ {
14029
+ className: cn(defaultWrapperClass(filter), filter.className),
14030
+ children: /* @__PURE__ */ jsx(RenderFilter, { filter })
14031
+ },
14032
+ filter.name
14033
+ )) })
14034
+ );
14066
14035
  }
14067
14036
  function TableAddButton({ mobile = false }) {
14068
14037
  const onAddClick = useTableStore.use.onAddClick();
@@ -14213,12 +14182,14 @@ function NTableHeader() {
14213
14182
  const isCustomMode = useTableStore.use.isCustomMode();
14214
14183
  const showViewToggle = useTableStore.use.showViewToggle();
14215
14184
  const showColumnVisibility = useTableStore.use.showColumnVisibility();
14216
- const hideDataChrome = isLoading && !isRefreshing || error || hasNoData && !isFilteredEmpty;
14185
+ const hideDataChrome = error || hasNoData && !isFilteredEmpty && !isLoading;
14186
+ const isFirstLoad = Boolean(isLoading) && !isRefreshing;
14187
+ const firstLoadChromeClass = isFirstLoad ? "pointer-events-none opacity-60" : void 0;
14217
14188
  if (isCustomMode) {
14218
14189
  if (!showViewToggle && !showColumnVisibility && !headerSlot && !hasControls) return null;
14219
14190
  if (hideDataChrome) return null;
14220
14191
  const justify2 = headerSlot ? "justify-between" : "justify-end";
14221
- return /* @__PURE__ */ jsxs("div", { "data-ntable-header": true, className: cn("flex shrink-0 items-center gap-0 lg:gap-3 flex-wrap lg:flex-nowrap", justify2, classNames?.header), children: [
14192
+ return /* @__PURE__ */ jsxs("div", { "data-ntable-header": true, "aria-busy": isFirstLoad ? "true" : void 0, className: cn("flex shrink-0 items-center gap-0 lg:gap-3 flex-wrap lg:flex-nowrap", justify2, firstLoadChromeClass, classNames?.header), children: [
14222
14193
  headerSlot && /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: headerSlot }),
14223
14194
  hasControls && /* @__PURE__ */ jsxs("div", { className: "flex gap-2 shrink-0", children: [
14224
14195
  /* @__PURE__ */ jsx("span", { className: "hidden md:contents", children: /* @__PURE__ */ jsx(TableSettingsMenu, {}) }),
@@ -14231,7 +14202,7 @@ function NTableHeader() {
14231
14202
  if (!hasFilters && !hasControls && !headerSlot && !hasToolbar) return null;
14232
14203
  if (hideDataChrome) return null;
14233
14204
  const justify = hasControls || headerSlot || hasToolbar ? "justify-between" : "justify-start";
14234
- return /* @__PURE__ */ jsxs("div", { "data-ntable-header": true, className: cn("flex shrink-0 items-center gap-0 lg:gap-3 flex-wrap lg:flex-nowrap", justify, classNames?.header), children: [
14205
+ return /* @__PURE__ */ jsxs("div", { "data-ntable-header": true, "aria-busy": isFirstLoad ? "true" : void 0, className: cn("flex shrink-0 items-center gap-0 lg:gap-3 flex-wrap lg:flex-nowrap", justify, firstLoadChromeClass, classNames?.header), children: [
14235
14206
  /* @__PURE__ */ jsx(TableFilters, {}),
14236
14207
  /* @__PURE__ */ jsx(TableMobileToolbar, {}),
14237
14208
  headerSlot && /* @__PURE__ */ jsx("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: headerSlot }),
@@ -14342,12 +14313,27 @@ function TableLayout(props) {
14342
14313
  "data-ntable-body": true,
14343
14314
  "data-ntable-refreshing": isRefreshing ? "true" : void 0,
14344
14315
  "aria-busy": isRefreshing ? "true" : void 0,
14345
- className: "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden",
14316
+ className: "relative flex min-h-0 flex-1 flex-col gap-2 overflow-hidden",
14346
14317
  children: isCustomMode ? customRenderer ? customRenderer() : null : /* @__PURE__ */ jsxs(Fragment, { children: [
14347
- isLoading && !isRefreshing && (props.renderLoading ? /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderLoading() }) : props.responsiveSkeleton ? /* @__PURE__ */ jsxs(Fragment, { children: [
14348
- /* @__PURE__ */ jsx("div", { "data-ntable-skeleton-variant": "table", className: "hidden min-h-0 flex-1 flex-col lg:flex", children: /* @__PURE__ */ jsx(NTableLoadingSkeleton, {}) }),
14349
- /* @__PURE__ */ jsx("div", { "data-ntable-skeleton-variant": "cards", className: "flex min-h-0 flex-1 flex-col lg:hidden", children: /* @__PURE__ */ jsx(NTableCardsLoadingSkeleton, {}) })
14350
- ] }) : effectiveMode === "table" ? /* @__PURE__ */ jsx(NTableLoadingSkeleton, {}) : effectiveMode === "cards" ? /* @__PURE__ */ jsx(NTableCardsLoadingSkeleton, {}) : /* @__PURE__ */ jsx(NTableLoadingSkeleton, {})),
14318
+ isLoading && !isRefreshing && // Absolutely positioned, the way every grid that measures its own
14319
+ // page size does it: AG Grid paints its loading state as an
14320
+ // overlay, and MUI's DataGrid renders `GridOverlay` on top of the
14321
+ // rows rather than in place of them. A skeleton that occupies a
14322
+ // slot in the layout is a second layout — the one the container
14323
+ // gets measured in — and the page size derived from it is a page
14324
+ // size for a table that no longer exists once rows land. Taking
14325
+ // the skeleton out of flow leaves exactly one layout to measure.
14326
+ /* @__PURE__ */ jsx(
14327
+ "div",
14328
+ {
14329
+ "data-ntable-loading-overlay": true,
14330
+ className: "absolute inset-0 z-10 flex min-h-0 flex-col bg-background",
14331
+ children: props.renderLoading ? /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderLoading() }) : props.responsiveSkeleton ? /* @__PURE__ */ jsxs(Fragment, { children: [
14332
+ /* @__PURE__ */ jsx("div", { "data-ntable-skeleton-variant": "table", className: "hidden min-h-0 flex-1 flex-col lg:flex", children: /* @__PURE__ */ jsx(NTableLoadingSkeleton, {}) }),
14333
+ /* @__PURE__ */ jsx("div", { "data-ntable-skeleton-variant": "cards", className: "flex min-h-0 flex-1 flex-col lg:hidden", children: /* @__PURE__ */ jsx(NTableCardsLoadingSkeleton, {}) })
14334
+ ] }) : effectiveMode === "cards" ? /* @__PURE__ */ jsx(NTableCardsLoadingSkeleton, {}) : /* @__PURE__ */ jsx(NTableLoadingSkeleton, {})
14335
+ }
14336
+ ),
14351
14337
  error && !isLoading && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderError ? props.renderError(error) : /* @__PURE__ */ jsx(NErrorState, { message: typeof error === "string" ? error : "An error occurred" }) }),
14352
14338
  showFilteredEmpty && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderFilteredEmpty ? props.renderFilteredEmpty() : renderFilteredEmpty ? renderFilteredEmpty() : props.renderEmpty ? props.renderEmpty() : /* @__PURE__ */ jsx(DefaultTableFilteredEmptyState, {}) }),
14353
14339
  showEmpty && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderEmpty ? props.renderEmpty() : /* @__PURE__ */ jsx(DefaultTableEmptyState, { title: noDataText }) }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "2.2.8",
3
+ "version": "2.2.9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Reusable React UI component package for Najm framework",