najm-kit 2.1.56 → 2.2.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.mjs CHANGED
@@ -12197,6 +12197,7 @@ var createTableStore = () => {
12197
12197
  addButtonText: "",
12198
12198
  pageSizeOptions: [10, 20, 30, 40, 50],
12199
12199
  calculatedPageSize: 10,
12200
+ calculatedCardPageSize: 0,
12200
12201
  skeletonRowCount: 6,
12201
12202
  maxHeight: null,
12202
12203
  bodyWidth: 0,
@@ -12354,6 +12355,7 @@ var DEFAULT_TABLE_HEADER_HEIGHT = 48;
12354
12355
  var DEFAULT_CARD_HEIGHT = 176;
12355
12356
  var DEFAULT_CARD_GAP = 12;
12356
12357
  var ROOT_SECTION_GAP_COUNT = 2;
12358
+ var DYNAMIC_PAGE_SIZE_DEBOUNCE_MS = 200;
12357
12359
  function useStoreSync(props) {
12358
12360
  const storeRef = useRef(null);
12359
12361
  const isControlled = props.mode !== void 0;
@@ -12441,6 +12443,14 @@ function calculateCardSkeletonCount(input) {
12441
12443
  const rows = Math.max(1, Math.ceil((input.bodyHeight + gap) / (cardHeight + gap)));
12442
12444
  return rows * columns;
12443
12445
  }
12446
+ function calculateCardPageSize(input) {
12447
+ const columns = Math.max(1, input.columnCount || 1);
12448
+ const cardHeight = Math.max(1, input.cardHeight ?? DEFAULT_CARD_HEIGHT);
12449
+ const gap = Math.max(0, input.gap ?? DEFAULT_CARD_GAP);
12450
+ if (input.bodyHeight <= 0) return columns;
12451
+ const rows = Math.max(1, Math.floor((input.bodyHeight + gap) / (cardHeight + gap)));
12452
+ return rows * columns;
12453
+ }
12444
12454
  function fallbackCardColumns(width) {
12445
12455
  if (width >= 1280) return 4;
12446
12456
  if (width >= 1024) return 3;
@@ -12493,8 +12503,19 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
12493
12503
  const cardGap = Number.parseFloat(gridStyles?.rowGap || gridStyles?.gap || "") || DEFAULT_CARD_GAP;
12494
12504
  const firstCard = cardsGridEl?.querySelector("[data-ntable-loading-card], [data-row]");
12495
12505
  const cardRowHeight = firstCard?.offsetHeight || firstCard?.getBoundingClientRect().height || DEFAULT_CARD_HEIGHT;
12506
+ const cardPageSize = calculateCardPageSize({
12507
+ bodyHeight,
12508
+ columnCount: cardColumnCount,
12509
+ cardHeight: cardRowHeight,
12510
+ gap: cardGap
12511
+ });
12496
12512
  const updates = {
12497
- ...!manualPagination ? { calculatedPageSize: newPageSize, maxHeight: calculatedMaxHeight } : {},
12513
+ // Measured page sizes are published in both pagination modes. Manual
12514
+ // pagination consumes them through onPaginationChange rather than by
12515
+ // mutating the table directly, so the consumer still owns fetching.
12516
+ calculatedPageSize: newPageSize,
12517
+ calculatedCardPageSize: cardPageSize,
12518
+ ...!manualPagination ? { maxHeight: calculatedMaxHeight } : {},
12498
12519
  skeletonRowCount: newPageSize,
12499
12520
  bodyWidth,
12500
12521
  bodyHeight,
@@ -12543,6 +12564,8 @@ function useTable(effectiveViewModeOverride) {
12543
12564
  const effectiveViewMode = useTableStore.use.effectiveViewMode();
12544
12565
  const cardPagination = useTableStore.use.cardPagination();
12545
12566
  const calculatedPageSize = useTableStore.use.calculatedPageSize();
12567
+ const calculatedCardPageSize = useTableStore.use.calculatedCardPageSize();
12568
+ const measuredBodyHeight = useTableStore.use.bodyHeight();
12546
12569
  const syncWithProps = useTableStore.use.syncWithProps();
12547
12570
  const onStateChange = useTableStore.use.onStateChange();
12548
12571
  const getRowId = useTableStore.use.getRowId();
@@ -12633,7 +12656,7 @@ function useTable(effectiveViewModeOverride) {
12633
12656
  }, [storePagination, storeRowSelection, setPagination, sorting, columnFilters, columnVisibility, globalFilter, notifyStateChange]);
12634
12657
  const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
12635
12658
  const renderedMode = effectiveViewModeOverride ?? effectiveViewMode ?? viewMode;
12636
- const renderAllSuppliedRows = renderedMode === "cards" && cardPagination.mode !== "paged";
12659
+ const renderAllSuppliedRows = cardPagination.mode === "all" || renderedMode === "cards" && cardPagination.mode !== "paged";
12637
12660
  const tableConfig = {
12638
12661
  data,
12639
12662
  columns: finalColumns,
@@ -12669,6 +12692,30 @@ function useTable(effectiveViewModeOverride) {
12669
12692
  table.setPageSize(data.length || 9999);
12670
12693
  }
12671
12694
  }, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination]);
12695
+ const dynamicPageSizeTarget = renderedMode === "cards" ? calculatedCardPageSize : calculatedPageSize;
12696
+ const lastRequestedDynamicSizeRef = useRef(null);
12697
+ useEffect(() => {
12698
+ if (!manualPagination || !dynamicHeight) return;
12699
+ if (cardPagination.mode !== "paged") return;
12700
+ if (measuredBodyHeight <= 0) return;
12701
+ if (!dynamicPageSizeTarget || dynamicPageSizeTarget < 1) return;
12702
+ if (dynamicPageSizeTarget === storePagination.pageSize) return;
12703
+ if (lastRequestedDynamicSizeRef.current === dynamicPageSizeTarget) return;
12704
+ const timer = setTimeout(() => {
12705
+ lastRequestedDynamicSizeRef.current = dynamicPageSizeTarget;
12706
+ setPagination({ pageIndex: storePagination.pageIndex, pageSize: dynamicPageSizeTarget });
12707
+ }, DYNAMIC_PAGE_SIZE_DEBOUNCE_MS);
12708
+ return () => clearTimeout(timer);
12709
+ }, [
12710
+ manualPagination,
12711
+ dynamicHeight,
12712
+ cardPagination.mode,
12713
+ measuredBodyHeight,
12714
+ dynamicPageSizeTarget,
12715
+ storePagination.pageIndex,
12716
+ storePagination.pageSize,
12717
+ setPagination
12718
+ ]);
12672
12719
  return { table, finalColumns, sorting, setSorting, columnFilters, setColumnFilters, columnVisibility, setColumnVisibility, globalFilter, setGlobalFilter };
12673
12720
  }
12674
12721
  function useTableKeyboard(options = {}) {
@@ -13131,268 +13178,642 @@ function NDataCardShell({ row, onClick, onContextMenu, actions, children, classN
13131
13178
  }
13132
13179
  );
13133
13180
  }
13134
- var ROW_CONTEXT_HANDLED2 = "__ntableRowContextHandled";
13135
- function markRowContextHandled(e) {
13136
- e.nativeEvent[ROW_CONTEXT_HANDLED2] = true;
13137
- }
13138
- function isRowContextHandled(e) {
13139
- return Boolean(e.nativeEvent[ROW_CONTEXT_HANDLED2]);
13140
- }
13141
- function NTableCards({ effectiveMode }) {
13142
- const table = useTableStore.use.table();
13143
- const onRowClick = useTableStore.use.onRowClick();
13144
- const onRowContextMenu = useTableStore.use.onRowContextMenu();
13145
- const onBackgroundContextMenu = useTableStore.use.onBackgroundContextMenu();
13146
- const getRowClassName = useTableStore.use.getRowClassName();
13147
- const openRowMenu = useTableStore.use.openRowMenu();
13148
- const menuButton = useTableStore.use.menuButton();
13149
- const onView = useTableStore.use.onView();
13150
- const onEdit = useTableStore.use.onEdit();
13151
- const onDelete = useTableStore.use.onDelete();
13152
- const showCheckbox = useTableStore.use.showCheckbox();
13153
- const selectedRowId = useTableStore.use.selectedRowId();
13154
- const CardComponent = useTableStore.use.CardComponent();
13155
- const storeIsCardView = useTableStore.use.isCardView();
13156
- const isLoading = useTableStore.use.isLoading();
13157
- const error = useTableStore.use.error();
13158
- const hasNoData = useTableStore.use.hasNoData();
13159
- const showContent = useTableStore.use.showContent();
13160
- const classNames = useTableStore.use.classNames();
13161
- const bordered = useTableStore.use.bordered();
13162
- const borderColor = useTableStore.use.borderColor();
13163
- const renderSubRow = useTableStore.use.renderSubRow();
13164
- const userGetRowCanExpand = useTableStore.use.getRowCanExpand();
13165
- const handleContainerContextMenu = useCallback((e) => {
13166
- if (isRowContextHandled(e)) return;
13167
- const gridContainer = e.currentTarget;
13168
- let el = e.target;
13169
- let gridChild = null;
13170
- while (el && el.parentElement && el.parentElement !== gridContainer) {
13171
- el = el.parentElement;
13172
- }
13173
- gridChild = el.parentElement === gridContainer ? el : null;
13174
- if (gridChild) {
13175
- const rows2 = table?.getRowModel()?.rows;
13176
- if (!rows2) return;
13177
- const children = Array.from(gridContainer.children);
13178
- const idx = children.indexOf(gridChild);
13179
- if (idx >= 0 && idx < rows2.length) {
13180
- const row = rows2[idx];
13181
- if (onRowContextMenu) {
13182
- markRowContextHandled(e);
13183
- onRowContextMenu(e, row.original);
13184
- }
13185
- return;
13186
- }
13187
- }
13188
- if (onBackgroundContextMenu) {
13189
- onBackgroundContextMenu(e);
13190
- }
13191
- }, [table, onRowContextMenu, onBackgroundContextMenu]);
13192
- if (isLoading || error || hasNoData || !showContent || !table) return null;
13193
- const isCardView = effectiveMode ? effectiveMode === "cards" : storeIsCardView;
13194
- if (!isCardView) return null;
13195
- const rows = table.getRowModel().rows;
13196
- if (!CardComponent) return /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-muted-foreground", children: "No CardComponent provided." });
13197
- const defaultContainerClass = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3";
13198
- const containerClass = classNames?.cards ?? defaultContainerClass;
13199
- const actions = !menuButton && (onView || onEdit || onDelete) ? { onView, onEdit, onDelete } : void 0;
13200
- return /* @__PURE__ */ jsx(NajmScroll, { axis: "y", className: "min-h-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx("div", { "data-ntable-cards-grid": true, className: cn(containerClass), onContextMenu: handleContainerContextMenu, children: rows.map((row) => {
13201
- const noShell = Boolean(row.original?.__smsNoShell);
13202
- const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
13203
- const canExpand = hasExpansion && row.getCanExpand();
13204
- const isExpanded = canExpand && row.getIsExpanded();
13205
- const handleClick = onRowClick ? () => onRowClick(row.original) : void 0;
13206
- const rowClassName = getRowClassName?.(row.original);
13207
- const handleCardContextMenu = onRowContextMenu ? (e) => {
13208
- if (isRowContextHandled(e)) return;
13209
- markRowContextHandled(e);
13210
- onRowContextMenu(e, row.original);
13211
- } : void 0;
13212
- if (noShell) {
13213
- return /* @__PURE__ */ jsxs(
13181
+ var DEFAULT_ROWS2 = 6;
13182
+ var DEFAULT_CARD_COUNT = 16;
13183
+ function NTableCardSkeleton({ surface }) {
13184
+ return /* @__PURE__ */ jsx(
13185
+ "div",
13186
+ {
13187
+ "data-ntable-loading-card": true,
13188
+ "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13189
+ style: surface.style,
13190
+ className: cn("rounded-lg p-3 sm:p-4", surface.className),
13191
+ children: /* @__PURE__ */ jsxs(
13214
13192
  "div",
13215
13193
  {
13216
- onContextMenu: handleCardContextMenu,
13217
- "data-row": "true",
13218
- "data-row-id": row.id,
13219
- className: cn("group relative text-card-foreground", rowClassName),
13194
+ "data-ntable-loading-card-layout": "responsive-avatar",
13195
+ className: "grid grid-cols-[80px_minmax(0,1fr)] gap-3 sm:grid-cols-[72px_minmax(0,1fr)]",
13220
13196
  children: [
13221
- menuButton && openRowMenu && /* @__PURE__ */ jsx(
13222
- "button",
13197
+ /* @__PURE__ */ jsx("div", { className: "col-start-1 row-start-1 flex items-start justify-center sm:justify-start", children: /* @__PURE__ */ jsx(
13198
+ NSkeleton,
13223
13199
  {
13224
- type: "button",
13225
- "aria-label": "Row actions",
13226
- onClick: (e) => {
13227
- e.stopPropagation();
13228
- openRowMenu(e, row.original);
13229
- },
13230
- "data-ntable-card-action": true,
13231
- className: "ntable-card-action absolute end-2 top-2 z-10 flex h-7 w-7 cursor-pointer items-center justify-center rounded-md p-0 text-muted-foreground transition-all duration-200 hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
13232
- children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" })
13200
+ "data-ntable-loading-card-avatar": true,
13201
+ className: "size-20 shrink-0 rounded-full sm:size-16"
13233
13202
  }
13234
- ),
13203
+ ) }),
13204
+ /* @__PURE__ */ jsxs("div", { className: "col-start-2 row-start-1 flex min-w-0 items-start justify-between gap-3", children: [
13205
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [
13206
+ /* @__PURE__ */ jsx(NSkeleton, { className: "h-5 w-36 max-w-full" }),
13207
+ /* @__PURE__ */ jsx(NSkeleton, { className: "hidden h-3 w-16 sm:block" })
13208
+ ] }),
13209
+ /* @__PURE__ */ jsx(
13210
+ NSkeleton,
13211
+ {
13212
+ "data-ntable-loading-card-status": true,
13213
+ className: "hidden h-6 w-14 shrink-0 rounded-full sm:block"
13214
+ }
13215
+ )
13216
+ ] }),
13235
13217
  /* @__PURE__ */ jsx(
13236
- CardComponent,
13218
+ "div",
13237
13219
  {
13238
- data: row.original,
13239
- row,
13240
- onClick: handleClick,
13241
- onContextMenu: handleCardContextMenu,
13242
- isExpanded,
13243
- onToggleExpanded: () => row.toggleExpanded(),
13244
- canExpand,
13245
- renderSubRow: canExpand && isExpanded ? renderSubRow : void 0,
13246
- "data-row": "true",
13247
- "data-row-id": row.id
13220
+ "data-ntable-loading-card-details": true,
13221
+ className: "col-start-2 row-start-2 space-y-1 sm:col-span-full sm:col-start-1 sm:space-y-2 sm:rounded-lg sm:bg-muted/50 sm:p-3",
13222
+ children: Array.from({ length: 3 }).map((_, detailIndex) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2", children: [
13223
+ /* @__PURE__ */ jsx(NSkeleton, { className: "size-3.5 shrink-0 rounded-sm sm:size-4" }),
13224
+ /* @__PURE__ */ jsx(
13225
+ NSkeleton,
13226
+ {
13227
+ className: cn(
13228
+ "h-3 max-w-full sm:h-4",
13229
+ detailIndex === 0 ? "w-full" : detailIndex === 1 ? "w-4/5" : "w-3/4"
13230
+ )
13231
+ }
13232
+ )
13233
+ ] }, detailIndex))
13248
13234
  }
13249
13235
  )
13250
13236
  ]
13251
- },
13252
- row.id
13253
- );
13237
+ }
13238
+ )
13254
13239
  }
13255
- return /* @__PURE__ */ jsx(
13256
- NDataCardShell,
13257
- {
13258
- row,
13259
- onClick: handleClick,
13260
- onContextMenu: handleCardContextMenu,
13261
- actions,
13262
- showCheckbox,
13263
- selectedRowId,
13264
- openRowMenu,
13265
- menuButton,
13266
- bordered,
13267
- borderColor,
13268
- className: rowClassName || void 0,
13269
- children: /* @__PURE__ */ jsx(
13270
- CardComponent,
13271
- {
13272
- data: row.original,
13273
- row,
13274
- isExpanded,
13275
- onToggleExpanded: () => row.toggleExpanded(),
13276
- canExpand,
13277
- renderSubRow: canExpand && isExpanded ? renderSubRow : void 0
13278
- }
13279
- )
13280
- },
13281
- row.id
13282
- );
13283
- }) }) });
13240
+ );
13284
13241
  }
13285
- function CardLoadMorePagination({
13286
- config,
13287
- rowCount,
13288
- bordered,
13289
- className
13290
- }) {
13291
- const [internalPending, setInternalPending] = React__default.useState(false);
13292
- const [internalError, setInternalError] = React__default.useState(null);
13293
- const [announcement, setAnnouncement] = React__default.useState("");
13294
- const buttonRef = React__default.useRef(null);
13295
- const pendingRef = React__default.useRef(false);
13296
- const restoreFocusRef = React__default.useRef(false);
13297
- const previousRowCountRef = React__default.useRef(rowCount);
13298
- const errorId = React__default.useId();
13299
- const pending = Boolean(config.loadingMore || internalPending);
13300
- const error = config.loadMoreError ?? internalError;
13301
- React__default.useEffect(() => {
13302
- const previous = previousRowCountRef.current;
13303
- if (rowCount > previous) {
13304
- const appended = rowCount - previous;
13305
- setAnnouncement(
13306
- config.itemsLoadedLabel?.(appended) ?? `${appended} more ${appended === 1 ? "item" : "items"} loaded.`
13307
- );
13308
- }
13309
- previousRowCountRef.current = rowCount;
13310
- }, [config.itemsLoadedLabel, rowCount]);
13311
- React__default.useEffect(() => {
13312
- if (pending || !restoreFocusRef.current) return;
13313
- restoreFocusRef.current = false;
13314
- const frame = requestAnimationFrame(() => buttonRef.current?.focus());
13315
- return () => cancelAnimationFrame(frame);
13316
- }, [pending]);
13317
- const loadMore = async () => {
13318
- if (pendingRef.current || pending || !config.hasNextPage && !error) return;
13319
- pendingRef.current = true;
13320
- restoreFocusRef.current = document.activeElement === buttonRef.current;
13321
- setInternalPending(true);
13322
- setInternalError(null);
13323
- const loadingAnnouncement = config.loadingMoreLabel ?? "Loading more items...";
13324
- setAnnouncement(loadingAnnouncement);
13325
- try {
13326
- await config.onLoadMore();
13327
- } catch {
13328
- setInternalError(config.loadMoreErrorLabel ?? "Couldn't load more items.");
13329
- setAnnouncement("");
13330
- } finally {
13331
- pendingRef.current = false;
13332
- setInternalPending(false);
13333
- setAnnouncement((current) => current === loadingAnnouncement ? "" : current);
13334
- }
13335
- };
13336
- if (!config.hasNextPage && !pending && !error) {
13337
- return /* @__PURE__ */ jsx(
13338
- "div",
13339
- {
13340
- "data-ntable-load-more-end": true,
13341
- role: "status",
13342
- "aria-live": "polite",
13343
- className: cn("py-2 text-center text-sm text-muted-foreground", className),
13344
- children: config.endLabel ?? "No more items."
13345
- }
13346
- );
13347
- }
13242
+ function NTableHeaderSkeleton() {
13243
+ const filters = useTableStore.use.filters();
13244
+ const showViewToggle = useTableStore.use.showViewToggle();
13245
+ const showColumnVisibility = useTableStore.use.showColumnVisibility();
13246
+ const showAddButton = useTableStore.use.showAddButton();
13247
+ const hasHeaderSlot = Boolean(useTableStore.use.headerSlot());
13248
+ const hasToolbar = Boolean(useTableStore.use.renderToolbar());
13249
+ const filterCount = Math.min(Math.max(filters?.length ?? 0, 1), 3);
13250
+ const hasActions = showViewToggle || showColumnVisibility || showAddButton || hasHeaderSlot || hasToolbar;
13251
+ const hasSettings = showViewToggle || showColumnVisibility || hasHeaderSlot || hasToolbar;
13252
+ if (!filters?.length && !hasActions) return null;
13348
13253
  return /* @__PURE__ */ jsxs(
13349
13254
  "div",
13350
13255
  {
13351
- "data-ntable-load-more": true,
13352
- className: cn("flex min-w-0 flex-col items-center gap-2 py-2", className),
13256
+ "data-ntable-loading-header": true,
13257
+ className: "flex shrink-0 flex-wrap items-center justify-between gap-2",
13353
13258
  children: [
13354
- error ? /* @__PURE__ */ jsx("div", { id: errorId, role: "alert", className: "text-center text-sm text-destructive", children: error === true ? config.loadMoreErrorLabel ?? "Couldn't load more items." : error }) : null,
13259
+ filters?.length ? /* @__PURE__ */ jsx(
13260
+ "div",
13261
+ {
13262
+ "data-ntable-loading-desktop-filters": true,
13263
+ className: "hidden min-w-0 flex-1 flex-wrap gap-2 md:flex",
13264
+ children: Array.from({ length: filterCount }).map((_, index) => /* @__PURE__ */ jsx(
13265
+ NSkeleton,
13266
+ {
13267
+ className: cn("h-10 w-full rounded-lg", index < 2 ? "max-w-64" : "max-w-48")
13268
+ },
13269
+ index
13270
+ ))
13271
+ }
13272
+ ) : /* @__PURE__ */ jsx("span", { className: "hidden min-w-0 flex-1 md:block" }),
13355
13273
  /* @__PURE__ */ jsxs(
13356
- Button,
13274
+ "div",
13357
13275
  {
13358
- ref: buttonRef,
13359
- type: "button",
13360
- bordered,
13361
- variant: "outline",
13362
- autoLoading: false,
13363
- disabled: pending,
13364
- "aria-describedby": error ? errorId : void 0,
13365
- "aria-busy": pending ? "true" : void 0,
13366
- onClick: loadMore,
13276
+ "data-ntable-loading-mobile-toolbar": true,
13277
+ className: "flex w-full min-w-0 items-center gap-2 md:hidden",
13367
13278
  children: [
13368
- pending ? /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "h-4 w-4 animate-spin motion-reduce:animate-none" }) : null,
13369
- pending ? config.loadingMoreLabel ?? "Loading more..." : error ? config.retryLabel ?? "Retry" : config.loadMoreLabel ?? "Load more"
13370
- ]
13371
- }
13372
- ),
13373
- /* @__PURE__ */ jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement })
13374
- ]
13375
- }
13376
- );
13377
- }
13378
- function NTablePagination() {
13379
- const table = useTableStore.use.table();
13380
- const showPagination = useTableStore.use.showPagination();
13381
- const showContent = useTableStore.use.showContent();
13382
- const pageSizeOptions = useTableStore.use.pageSizeOptions();
13383
- const classNames = useTableStore.use.classNames();
13384
- const effectiveViewMode = useTableStore.use.effectiveViewMode();
13385
- const cardPagination = useTableStore.use.cardPagination();
13386
- const data = useTableStore.use.data();
13387
- const pagination = useTableStore.use.pagination();
13388
- const manualPagination = useTableStore.use.manualPagination();
13389
- const pageCount = useTableStore.use.pageCount();
13279
+ filters?.length ? /* @__PURE__ */ jsx(
13280
+ NSkeleton,
13281
+ {
13282
+ "data-ntable-loading-mobile-primary": true,
13283
+ className: "h-10 min-w-0 flex-1 rounded-lg"
13284
+ }
13285
+ ) : null,
13286
+ filters?.length > 1 ? /* @__PURE__ */ jsx(
13287
+ NSkeleton,
13288
+ {
13289
+ "data-ntable-loading-mobile-filter-button": true,
13290
+ className: "h-10 w-10 shrink-0 rounded-lg"
13291
+ }
13292
+ ) : null,
13293
+ showAddButton ? /* @__PURE__ */ jsx(
13294
+ NSkeleton,
13295
+ {
13296
+ "data-ntable-loading-mobile-add-button": true,
13297
+ className: "h-10 w-10 shrink-0 rounded-lg"
13298
+ }
13299
+ ) : null
13300
+ ]
13301
+ }
13302
+ ),
13303
+ hasActions && /* @__PURE__ */ jsxs("div", { className: "hidden shrink-0 gap-2 md:flex", children: [
13304
+ hasSettings && /* @__PURE__ */ jsx(NSkeleton, { className: "h-10 w-10 rounded-lg" }),
13305
+ showAddButton && /* @__PURE__ */ jsx(NSkeleton, { className: "h-10 w-10 rounded-lg" })
13306
+ ] })
13307
+ ]
13308
+ }
13309
+ );
13310
+ }
13311
+ function NTableLoadingSkeleton({ rows }) {
13312
+ const rawColumns = useTableStore.use.columns();
13313
+ const responsiveColumns = React__default.useMemo(() => filterResponsiveColumns(rawColumns), [rawColumns]);
13314
+ const columns = responsiveColumns;
13315
+ const showCheckbox = useTableStore.use.showCheckbox();
13316
+ const headerClassName = useTableStore.use.headerClassName();
13317
+ const classNames = useTableStore.use.classNames();
13318
+ const dynamicHeight = useTableStore.use.dynamicHeight();
13319
+ const bordered = useTableStore.use.bordered();
13320
+ const borderColor = useTableStore.use.borderColor();
13321
+ const surface = useTableSurfaceAppearance(bordered, borderColor);
13322
+ const bodyHeight = useTableStore.use.bodyHeight();
13323
+ const skeletonRowCount = useTableStore.use.skeletonRowCount();
13324
+ const renderSubRow = useTableStore.use.renderSubRow();
13325
+ const userGetRowCanExpand = useTableStore.use.getRowCanExpand();
13326
+ const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
13327
+ const loadingText = useTableStore.use.loadingText();
13328
+ const rowCount = rows ?? (dynamicHeight && bodyHeight > 0 ? skeletonRowCount : DEFAULT_ROWS2);
13329
+ const renderHeaderLabel = (header) => typeof header === "string" ? header : null;
13330
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13331
+ /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13332
+ /* @__PURE__ */ jsxs(
13333
+ "div",
13334
+ {
13335
+ "data-testid": "ntable-loading-skeleton",
13336
+ "data-ntable-loading-row-count": rowCount,
13337
+ "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13338
+ "aria-busy": "true",
13339
+ "aria-label": loadingText,
13340
+ role: "status",
13341
+ style: surface.style,
13342
+ className: cn("min-h-0 flex-1 rounded-md p-0", surface.className, dynamicHeight ? "overflow-hidden" : "najm-overlay-scroll", classNames?.content),
13343
+ children: [
13344
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13345
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: dynamicHeight ? "najm-overlay-scroll h-full" : void 0, children: /* @__PURE__ */ jsxs(Table, { children: [
13346
+ /* @__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: [
13347
+ showCheckbox && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Select column", className: "w-10 text-foreground h-12" }),
13348
+ hasExpansion && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Expand column", className: "w-10 text-foreground h-12" }),
13349
+ columns.map((col, i) => /* @__PURE__ */ jsx(
13350
+ TableHead,
13351
+ {
13352
+ className: cn("text-foreground h-12", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13353
+ style: col?.size ? { width: col.size } : void 0,
13354
+ children: renderHeaderLabel(col?.header)
13355
+ },
13356
+ col?.id ?? col?.accessorKey ?? i
13357
+ ))
13358
+ ] }) }),
13359
+ /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rowCount }).map((_, r) => /* @__PURE__ */ jsxs(TableRow, { children: [
13360
+ showCheckbox && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13361
+ hasExpansion && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13362
+ columns.map((col, c) => /* @__PURE__ */ jsx(
13363
+ TableCell,
13364
+ {
13365
+ className: cn("h-14", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13366
+ style: col?.size ? { width: col.size } : void 0,
13367
+ children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-full" })
13368
+ },
13369
+ `skeleton-${r}-${col?.id ?? col?.accessorKey ?? c}`
13370
+ ))
13371
+ ] }, `skeleton-${r}`)) })
13372
+ ] }) })
13373
+ ]
13374
+ }
13375
+ )
13376
+ ] });
13377
+ }
13378
+ function NTableCardsLoadingSkeleton({ rows }) {
13379
+ const filters = useTableStore.use.filters();
13380
+ const showViewToggle = useTableStore.use.showViewToggle();
13381
+ const showColumnVisibility = useTableStore.use.showColumnVisibility();
13382
+ const showAddButton = useTableStore.use.showAddButton();
13383
+ const hasHeaderSlot = Boolean(useTableStore.use.headerSlot());
13384
+ const hasToolbar = Boolean(useTableStore.use.renderToolbar());
13385
+ const hasHeaderSkeleton = Boolean(
13386
+ filters?.length || showViewToggle || showColumnVisibility || showAddButton || hasHeaderSlot || hasToolbar
13387
+ );
13388
+ const classNames = useTableStore.use.classNames();
13389
+ const bordered = useTableStore.use.bordered();
13390
+ const borderColor = useTableStore.use.borderColor();
13391
+ const surface = useTableSurfaceAppearance(bordered, borderColor);
13392
+ const dynamicHeight = useTableStore.use.dynamicHeight();
13393
+ const bodyHeight = useTableStore.use.bodyHeight();
13394
+ const cardColumnCount = useTableStore.use.cardColumnCount();
13395
+ const cardRowHeight = useTableStore.use.cardRowHeight();
13396
+ const cardGap = useTableStore.use.cardGap();
13397
+ const loadingText = useTableStore.use.loadingText();
13398
+ const cardCount = rows ?? (dynamicHeight && bodyHeight > 0 ? calculateCardSkeletonCount({
13399
+ bodyHeight,
13400
+ columnCount: cardColumnCount,
13401
+ cardHeight: cardRowHeight,
13402
+ gap: cardGap
13403
+ }) : DEFAULT_CARD_COUNT);
13404
+ const defaultContainerClass = "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4";
13405
+ const containerClass = classNames?.cards ?? defaultContainerClass;
13406
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13407
+ hasHeaderSkeleton && /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13408
+ /* @__PURE__ */ jsxs(
13409
+ NajmScroll,
13410
+ {
13411
+ axis: "y",
13412
+ "aria-busy": "true",
13413
+ "aria-label": loadingText,
13414
+ role: "status",
13415
+ className: "min-h-0 flex-1 overflow-hidden",
13416
+ children: [
13417
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13418
+ /* @__PURE__ */ jsx(
13419
+ "div",
13420
+ {
13421
+ "data-testid": "ntable-cards-loading-skeleton",
13422
+ "data-ntable-loading-cards-grid": true,
13423
+ "data-ntable-loading-card-count": cardCount,
13424
+ "aria-hidden": "true",
13425
+ className: cn(containerClass),
13426
+ children: Array.from({ length: cardCount }).map((_, index) => /* @__PURE__ */ jsx(NTableCardSkeleton, { surface }, index))
13427
+ }
13428
+ )
13429
+ ]
13430
+ }
13431
+ )
13432
+ ] });
13433
+ }
13434
+ var DEFAULT_ROOT_MARGIN = "80px";
13435
+ function useCardContinuation({
13436
+ config,
13437
+ rowCount,
13438
+ viewportRef
13439
+ }) {
13440
+ const [internalPending, setInternalPending] = React__default.useState(false);
13441
+ const [internalError, setInternalError] = React__default.useState(null);
13442
+ const [announcement, setAnnouncement] = React__default.useState("");
13443
+ const [viewportReady, setViewportReady] = React__default.useState(false);
13444
+ const pendingRef = React__default.useRef(false);
13445
+ const previousRowCountRef = React__default.useRef(rowCount);
13446
+ const configRef = React__default.useRef(config);
13447
+ configRef.current = config;
13448
+ const enabled = Boolean(config);
13449
+ const pending = Boolean(config?.loadingMore || internalPending);
13450
+ const error = config?.loadMoreError ?? internalError;
13451
+ const hasNextPage = Boolean(config?.hasNextPage);
13452
+ const load = React__default.useCallback(async () => {
13453
+ const current = configRef.current;
13454
+ if (!current) return;
13455
+ if (pendingRef.current || current.loadingMore || !current.hasNextPage) return;
13456
+ pendingRef.current = true;
13457
+ setInternalPending(true);
13458
+ setInternalError(null);
13459
+ const loadingAnnouncement = current.loadingMoreLabel ?? "Loading more items...";
13460
+ setAnnouncement(loadingAnnouncement);
13461
+ try {
13462
+ await current.onLoadMore();
13463
+ } catch {
13464
+ setInternalError(current.loadMoreErrorLabel ?? "Couldn't load more items.");
13465
+ setAnnouncement("");
13466
+ } finally {
13467
+ pendingRef.current = false;
13468
+ setInternalPending(false);
13469
+ setAnnouncement((value) => value === loadingAnnouncement ? "" : value);
13470
+ }
13471
+ }, []);
13472
+ const { sentinelRef, scrollContainerRef, observe, doneLoading } = useInfiniteScroll(
13473
+ enabled && hasNextPage && !error,
13474
+ load,
13475
+ { rootMargin: config?.rootMargin ?? DEFAULT_ROOT_MARGIN }
13476
+ );
13477
+ React__default.useLayoutEffect(() => {
13478
+ const node = viewportRef.current ?? null;
13479
+ scrollContainerRef.current = node;
13480
+ const ready = Boolean(node);
13481
+ setViewportReady((value) => value === ready ? value : ready);
13482
+ });
13483
+ React__default.useEffect(() => {
13484
+ const previous = previousRowCountRef.current;
13485
+ if (rowCount > previous) {
13486
+ const appended = rowCount - previous;
13487
+ setAnnouncement(
13488
+ configRef.current?.itemsLoadedLabel?.(appended) ?? `${appended} more ${appended === 1 ? "item" : "items"} loaded.`
13489
+ );
13490
+ }
13491
+ previousRowCountRef.current = rowCount;
13492
+ }, [rowCount]);
13493
+ React__default.useEffect(() => {
13494
+ if (!pending) doneLoading();
13495
+ }, [pending, doneLoading]);
13496
+ React__default.useEffect(() => {
13497
+ if (!enabled || !hasNextPage || error) return;
13498
+ return observe();
13499
+ }, [enabled, hasNextPage, error, observe, viewportReady, rowCount]);
13500
+ return {
13501
+ /** Attach to an element rendered after the last card. */
13502
+ sentinelRef,
13503
+ /** True while a page is in flight; render shaped placeholders. */
13504
+ pending,
13505
+ /** Present only after an append failure; render the retry target. */
13506
+ error,
13507
+ /** Retry an append. Also used as the manual escape hatch after failure. */
13508
+ retry: load,
13509
+ /** Polite live-region text. Never rendered visibly. */
13510
+ announcement,
13511
+ /** Whether a sentinel should exist at all. */
13512
+ active: enabled && hasNextPage && !error
13513
+ };
13514
+ }
13515
+ var ROW_CONTEXT_HANDLED2 = "__ntableRowContextHandled";
13516
+ function markRowContextHandled(e) {
13517
+ e.nativeEvent[ROW_CONTEXT_HANDLED2] = true;
13518
+ }
13519
+ function isRowContextHandled(e) {
13520
+ return Boolean(e.nativeEvent[ROW_CONTEXT_HANDLED2]);
13521
+ }
13522
+ function NTableCards({ effectiveMode }) {
13523
+ const table = useTableStore.use.table();
13524
+ const onRowClick = useTableStore.use.onRowClick();
13525
+ const onRowContextMenu = useTableStore.use.onRowContextMenu();
13526
+ const onBackgroundContextMenu = useTableStore.use.onBackgroundContextMenu();
13527
+ const getRowClassName = useTableStore.use.getRowClassName();
13528
+ const openRowMenu = useTableStore.use.openRowMenu();
13529
+ const menuButton = useTableStore.use.menuButton();
13530
+ const onView = useTableStore.use.onView();
13531
+ const onEdit = useTableStore.use.onEdit();
13532
+ const onDelete = useTableStore.use.onDelete();
13533
+ const showCheckbox = useTableStore.use.showCheckbox();
13534
+ const selectedRowId = useTableStore.use.selectedRowId();
13535
+ const CardComponent = useTableStore.use.CardComponent();
13536
+ const storeIsCardView = useTableStore.use.isCardView();
13537
+ const isLoading = useTableStore.use.isLoading();
13538
+ const error = useTableStore.use.error();
13539
+ const hasNoData = useTableStore.use.hasNoData();
13540
+ const showContent = useTableStore.use.showContent();
13541
+ const classNames = useTableStore.use.classNames();
13542
+ const bordered = useTableStore.use.bordered();
13543
+ const borderColor = useTableStore.use.borderColor();
13544
+ const renderSubRow = useTableStore.use.renderSubRow();
13545
+ const userGetRowCanExpand = useTableStore.use.getRowCanExpand();
13546
+ const cardPagination = useTableStore.use.cardPagination();
13547
+ const cardColumnCount = useTableStore.use.cardColumnCount();
13548
+ const surface = useTableSurfaceAppearance(bordered, borderColor);
13549
+ const viewportRef = React__default.useRef(null);
13550
+ const continuationConfig = cardPagination.mode === "infinite" ? cardPagination : null;
13551
+ const continuation = useCardContinuation({
13552
+ config: continuationConfig,
13553
+ rowCount: table?.getRowModel().rows.length ?? 0,
13554
+ viewportRef
13555
+ });
13556
+ const handleContainerContextMenu = useCallback((e) => {
13557
+ if (isRowContextHandled(e)) return;
13558
+ const gridContainer = e.currentTarget;
13559
+ let el = e.target;
13560
+ let gridChild = null;
13561
+ while (el && el.parentElement && el.parentElement !== gridContainer) {
13562
+ el = el.parentElement;
13563
+ }
13564
+ gridChild = el.parentElement === gridContainer ? el : null;
13565
+ if (gridChild) {
13566
+ const rows2 = table?.getRowModel()?.rows;
13567
+ if (!rows2) return;
13568
+ const children = Array.from(gridContainer.children);
13569
+ const idx = children.indexOf(gridChild);
13570
+ if (idx >= 0 && idx < rows2.length) {
13571
+ const row = rows2[idx];
13572
+ if (onRowContextMenu) {
13573
+ markRowContextHandled(e);
13574
+ onRowContextMenu(e, row.original);
13575
+ }
13576
+ return;
13577
+ }
13578
+ }
13579
+ if (onBackgroundContextMenu) {
13580
+ onBackgroundContextMenu(e);
13581
+ }
13582
+ }, [table, onRowContextMenu, onBackgroundContextMenu]);
13583
+ if (isLoading || error || hasNoData || !showContent || !table) return null;
13584
+ const isCardView = effectiveMode ? effectiveMode === "cards" : storeIsCardView;
13585
+ if (!isCardView) return null;
13586
+ const rows = table.getRowModel().rows;
13587
+ if (!CardComponent) return /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-muted-foreground", children: "No CardComponent provided." });
13588
+ const defaultContainerClass = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3";
13589
+ const containerClass = classNames?.cards ?? defaultContainerClass;
13590
+ const actions = !menuButton && (onView || onEdit || onDelete) ? { onView, onEdit, onDelete } : void 0;
13591
+ return /* @__PURE__ */ jsxs(NajmScroll, { axis: "y", viewportRef, className: "min-h-0 flex-1 overflow-hidden", children: [
13592
+ /* @__PURE__ */ jsxs("div", { "data-ntable-cards-grid": true, className: cn(containerClass), onContextMenu: handleContainerContextMenu, children: [
13593
+ rows.map((row) => {
13594
+ const noShell = Boolean(row.original?.__smsNoShell);
13595
+ const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
13596
+ const canExpand = hasExpansion && row.getCanExpand();
13597
+ const isExpanded = canExpand && row.getIsExpanded();
13598
+ const handleClick = onRowClick ? () => onRowClick(row.original) : void 0;
13599
+ const rowClassName = getRowClassName?.(row.original);
13600
+ const handleCardContextMenu = onRowContextMenu ? (e) => {
13601
+ if (isRowContextHandled(e)) return;
13602
+ markRowContextHandled(e);
13603
+ onRowContextMenu(e, row.original);
13604
+ } : void 0;
13605
+ if (noShell) {
13606
+ return /* @__PURE__ */ jsxs(
13607
+ "div",
13608
+ {
13609
+ onContextMenu: handleCardContextMenu,
13610
+ "data-row": "true",
13611
+ "data-row-id": row.id,
13612
+ className: cn("group relative text-card-foreground", rowClassName),
13613
+ children: [
13614
+ menuButton && openRowMenu && /* @__PURE__ */ jsx(
13615
+ "button",
13616
+ {
13617
+ type: "button",
13618
+ "aria-label": "Row actions",
13619
+ onClick: (e) => {
13620
+ e.stopPropagation();
13621
+ openRowMenu(e, row.original);
13622
+ },
13623
+ "data-ntable-card-action": true,
13624
+ className: "ntable-card-action absolute end-2 top-2 z-10 flex h-7 w-7 cursor-pointer items-center justify-center rounded-md p-0 text-muted-foreground transition-all duration-200 hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
13625
+ children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" })
13626
+ }
13627
+ ),
13628
+ /* @__PURE__ */ jsx(
13629
+ CardComponent,
13630
+ {
13631
+ data: row.original,
13632
+ row,
13633
+ onClick: handleClick,
13634
+ onContextMenu: handleCardContextMenu,
13635
+ isExpanded,
13636
+ onToggleExpanded: () => row.toggleExpanded(),
13637
+ canExpand,
13638
+ renderSubRow: canExpand && isExpanded ? renderSubRow : void 0,
13639
+ "data-row": "true",
13640
+ "data-row-id": row.id
13641
+ }
13642
+ )
13643
+ ]
13644
+ },
13645
+ row.id
13646
+ );
13647
+ }
13648
+ return /* @__PURE__ */ jsx(
13649
+ NDataCardShell,
13650
+ {
13651
+ row,
13652
+ onClick: handleClick,
13653
+ onContextMenu: handleCardContextMenu,
13654
+ actions,
13655
+ showCheckbox,
13656
+ selectedRowId,
13657
+ openRowMenu,
13658
+ menuButton,
13659
+ bordered,
13660
+ borderColor,
13661
+ className: rowClassName || void 0,
13662
+ children: /* @__PURE__ */ jsx(
13663
+ CardComponent,
13664
+ {
13665
+ data: row.original,
13666
+ row,
13667
+ isExpanded,
13668
+ onToggleExpanded: () => row.toggleExpanded(),
13669
+ canExpand,
13670
+ renderSubRow: canExpand && isExpanded ? renderSubRow : void 0
13671
+ }
13672
+ )
13673
+ },
13674
+ row.id
13675
+ );
13676
+ }),
13677
+ continuation.pending ? Array.from({ length: Math.max(1, cardColumnCount || 1) }).map((_, index) => /* @__PURE__ */ jsx(NTableCardSkeleton, { surface }, `ntable-continuation-skeleton-${index}`)) : null
13678
+ ] }),
13679
+ continuation.error ? /* @__PURE__ */ jsxs(
13680
+ "div",
13681
+ {
13682
+ "data-ntable-cards-continuation-error": true,
13683
+ className: "flex flex-col items-center gap-2 py-3",
13684
+ children: [
13685
+ /* @__PURE__ */ jsx("div", { role: "alert", className: "text-center text-sm text-destructive", children: continuation.error === true ? continuationConfig?.loadMoreErrorLabel ?? "Couldn't load more items." : continuation.error }),
13686
+ /* @__PURE__ */ jsx(
13687
+ Button,
13688
+ {
13689
+ type: "button",
13690
+ bordered,
13691
+ variant: "outline",
13692
+ autoLoading: false,
13693
+ disabled: continuation.pending,
13694
+ onClick: continuation.retry,
13695
+ children: continuationConfig?.retryLabel ?? "Retry"
13696
+ }
13697
+ )
13698
+ ]
13699
+ }
13700
+ ) : null,
13701
+ continuation.active ? /* @__PURE__ */ jsx("div", { ref: continuation.sentinelRef, "data-ntable-cards-sentinel": true, "aria-hidden": "true" }) : null,
13702
+ /* @__PURE__ */ jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: continuation.announcement })
13703
+ ] });
13704
+ }
13705
+ function CardLoadMorePagination({
13706
+ config,
13707
+ rowCount,
13708
+ bordered,
13709
+ className
13710
+ }) {
13711
+ const [internalPending, setInternalPending] = React__default.useState(false);
13712
+ const [internalError, setInternalError] = React__default.useState(null);
13713
+ const [announcement, setAnnouncement] = React__default.useState("");
13714
+ const buttonRef = React__default.useRef(null);
13715
+ const pendingRef = React__default.useRef(false);
13716
+ const restoreFocusRef = React__default.useRef(false);
13717
+ const previousRowCountRef = React__default.useRef(rowCount);
13718
+ const errorId = React__default.useId();
13719
+ const pending = Boolean(config.loadingMore || internalPending);
13720
+ const error = config.loadMoreError ?? internalError;
13721
+ React__default.useEffect(() => {
13722
+ const previous = previousRowCountRef.current;
13723
+ if (rowCount > previous) {
13724
+ const appended = rowCount - previous;
13725
+ setAnnouncement(
13726
+ config.itemsLoadedLabel?.(appended) ?? `${appended} more ${appended === 1 ? "item" : "items"} loaded.`
13727
+ );
13728
+ }
13729
+ previousRowCountRef.current = rowCount;
13730
+ }, [config.itemsLoadedLabel, rowCount]);
13731
+ React__default.useEffect(() => {
13732
+ if (pending || !restoreFocusRef.current) return;
13733
+ restoreFocusRef.current = false;
13734
+ const frame = requestAnimationFrame(() => buttonRef.current?.focus());
13735
+ return () => cancelAnimationFrame(frame);
13736
+ }, [pending]);
13737
+ const loadMore = async () => {
13738
+ if (pendingRef.current || pending || !config.hasNextPage && !error) return;
13739
+ pendingRef.current = true;
13740
+ restoreFocusRef.current = document.activeElement === buttonRef.current;
13741
+ setInternalPending(true);
13742
+ setInternalError(null);
13743
+ const loadingAnnouncement = config.loadingMoreLabel ?? "Loading more items...";
13744
+ setAnnouncement(loadingAnnouncement);
13745
+ try {
13746
+ await config.onLoadMore();
13747
+ } catch {
13748
+ setInternalError(config.loadMoreErrorLabel ?? "Couldn't load more items.");
13749
+ setAnnouncement("");
13750
+ } finally {
13751
+ pendingRef.current = false;
13752
+ setInternalPending(false);
13753
+ setAnnouncement((current) => current === loadingAnnouncement ? "" : current);
13754
+ }
13755
+ };
13756
+ if (!config.hasNextPage && !pending && !error) {
13757
+ return /* @__PURE__ */ jsx(
13758
+ "div",
13759
+ {
13760
+ "data-ntable-load-more-end": true,
13761
+ role: "status",
13762
+ "aria-live": "polite",
13763
+ className: cn("py-2 text-center text-sm text-muted-foreground", className),
13764
+ children: config.endLabel ?? "No more items."
13765
+ }
13766
+ );
13767
+ }
13768
+ return /* @__PURE__ */ jsxs(
13769
+ "div",
13770
+ {
13771
+ "data-ntable-load-more": true,
13772
+ className: cn("flex min-w-0 flex-col items-center gap-2 py-2", className),
13773
+ children: [
13774
+ error ? /* @__PURE__ */ jsx("div", { id: errorId, role: "alert", className: "text-center text-sm text-destructive", children: error === true ? config.loadMoreErrorLabel ?? "Couldn't load more items." : error }) : null,
13775
+ /* @__PURE__ */ jsxs(
13776
+ Button,
13777
+ {
13778
+ ref: buttonRef,
13779
+ type: "button",
13780
+ bordered,
13781
+ variant: "outline",
13782
+ autoLoading: false,
13783
+ disabled: pending,
13784
+ "aria-describedby": error ? errorId : void 0,
13785
+ "aria-busy": pending ? "true" : void 0,
13786
+ onClick: loadMore,
13787
+ children: [
13788
+ pending ? /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "h-4 w-4 animate-spin motion-reduce:animate-none" }) : null,
13789
+ pending ? config.loadingMoreLabel ?? "Loading more..." : error ? config.retryLabel ?? "Retry" : config.loadMoreLabel ?? "Load more"
13790
+ ]
13791
+ }
13792
+ ),
13793
+ /* @__PURE__ */ jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement })
13794
+ ]
13795
+ }
13796
+ );
13797
+ }
13798
+ function NTablePagination() {
13799
+ const table = useTableStore.use.table();
13800
+ const showPagination = useTableStore.use.showPagination();
13801
+ const showContent = useTableStore.use.showContent();
13802
+ const pageSizeOptions = useTableStore.use.pageSizeOptions();
13803
+ const classNames = useTableStore.use.classNames();
13804
+ const effectiveViewMode = useTableStore.use.effectiveViewMode();
13805
+ const cardPagination = useTableStore.use.cardPagination();
13806
+ const data = useTableStore.use.data();
13807
+ const pagination = useTableStore.use.pagination();
13808
+ const manualPagination = useTableStore.use.manualPagination();
13809
+ const pageCount = useTableStore.use.pageCount();
13390
13810
  const rowCount = useTableStore.use.rowCount();
13391
13811
  const setPagination = useTableStore.use.setPagination();
13392
13812
  const isPaginationControlled = useTableStore.use.isPaginationControlled();
13393
13813
  const bordered = useTableStore.use.bordered();
13394
13814
  if (!table || !showContent || !showPagination || effectiveViewMode === "json" || effectiveViewMode === "files") return null;
13395
- if (effectiveViewMode === "cards" && cardPagination.mode === "all") return null;
13815
+ if (cardPagination.mode === "all") return null;
13816
+ if (effectiveViewMode === "cards" && cardPagination.mode === "infinite") return null;
13396
13817
  if (effectiveViewMode === "cards" && cardPagination.mode === "load-more") {
13397
13818
  return /* @__PURE__ */ jsx(
13398
13819
  CardLoadMorePagination,
@@ -13770,257 +14191,6 @@ function NTableJson() {
13770
14191
  if (viewMode !== "json") return null;
13771
14192
  return /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col min-h-0 overflow-hidden", children: renderJson?.() ?? /* @__PURE__ */ jsx(NajmScroll, { axis: "both", className: "h-full min-h-0 rounded-md border border-border bg-muted/40", children: /* @__PURE__ */ jsx("pre", { className: "p-4 font-mono text-xs leading-relaxed text-foreground", children: formatJsonValue(jsonValue) }) }) });
13772
14193
  }
13773
- var DEFAULT_ROWS2 = 6;
13774
- var DEFAULT_CARD_COUNT = 16;
13775
- function NTableHeaderSkeleton() {
13776
- const filters = useTableStore.use.filters();
13777
- const showViewToggle = useTableStore.use.showViewToggle();
13778
- const showColumnVisibility = useTableStore.use.showColumnVisibility();
13779
- const showAddButton = useTableStore.use.showAddButton();
13780
- const hasHeaderSlot = Boolean(useTableStore.use.headerSlot());
13781
- const hasToolbar = Boolean(useTableStore.use.renderToolbar());
13782
- const filterCount = Math.min(Math.max(filters?.length ?? 0, 1), 3);
13783
- const hasActions = showViewToggle || showColumnVisibility || showAddButton || hasHeaderSlot || hasToolbar;
13784
- const hasSettings = showViewToggle || showColumnVisibility || hasHeaderSlot || hasToolbar;
13785
- if (!filters?.length && !hasActions) return null;
13786
- return /* @__PURE__ */ jsxs(
13787
- "div",
13788
- {
13789
- "data-ntable-loading-header": true,
13790
- className: "flex shrink-0 flex-wrap items-center justify-between gap-2",
13791
- children: [
13792
- filters?.length ? /* @__PURE__ */ jsx(
13793
- "div",
13794
- {
13795
- "data-ntable-loading-desktop-filters": true,
13796
- className: "hidden min-w-0 flex-1 flex-wrap gap-2 md:flex",
13797
- children: Array.from({ length: filterCount }).map((_, index) => /* @__PURE__ */ jsx(
13798
- NSkeleton,
13799
- {
13800
- className: cn("h-10 w-full rounded-lg", index < 2 ? "max-w-64" : "max-w-48")
13801
- },
13802
- index
13803
- ))
13804
- }
13805
- ) : /* @__PURE__ */ jsx("span", { className: "hidden min-w-0 flex-1 md:block" }),
13806
- /* @__PURE__ */ jsxs(
13807
- "div",
13808
- {
13809
- "data-ntable-loading-mobile-toolbar": true,
13810
- className: "flex w-full min-w-0 items-center gap-2 md:hidden",
13811
- children: [
13812
- filters?.length ? /* @__PURE__ */ jsx(
13813
- NSkeleton,
13814
- {
13815
- "data-ntable-loading-mobile-primary": true,
13816
- className: "h-10 min-w-0 flex-1 rounded-lg"
13817
- }
13818
- ) : null,
13819
- filters?.length > 1 ? /* @__PURE__ */ jsx(
13820
- NSkeleton,
13821
- {
13822
- "data-ntable-loading-mobile-filter-button": true,
13823
- className: "h-10 w-10 shrink-0 rounded-lg"
13824
- }
13825
- ) : null,
13826
- showAddButton ? /* @__PURE__ */ jsx(
13827
- NSkeleton,
13828
- {
13829
- "data-ntable-loading-mobile-add-button": true,
13830
- className: "h-10 w-10 shrink-0 rounded-lg"
13831
- }
13832
- ) : null
13833
- ]
13834
- }
13835
- ),
13836
- hasActions && /* @__PURE__ */ jsxs("div", { className: "hidden shrink-0 gap-2 md:flex", children: [
13837
- hasSettings && /* @__PURE__ */ jsx(NSkeleton, { className: "h-10 w-10 rounded-lg" }),
13838
- showAddButton && /* @__PURE__ */ jsx(NSkeleton, { className: "h-10 w-10 rounded-lg" })
13839
- ] })
13840
- ]
13841
- }
13842
- );
13843
- }
13844
- function NTableLoadingSkeleton({ rows }) {
13845
- const rawColumns = useTableStore.use.columns();
13846
- const responsiveColumns = React__default.useMemo(() => filterResponsiveColumns(rawColumns), [rawColumns]);
13847
- const columns = responsiveColumns;
13848
- const showCheckbox = useTableStore.use.showCheckbox();
13849
- const headerClassName = useTableStore.use.headerClassName();
13850
- const classNames = useTableStore.use.classNames();
13851
- const dynamicHeight = useTableStore.use.dynamicHeight();
13852
- const bordered = useTableStore.use.bordered();
13853
- const borderColor = useTableStore.use.borderColor();
13854
- const surface = useTableSurfaceAppearance(bordered, borderColor);
13855
- const bodyHeight = useTableStore.use.bodyHeight();
13856
- const skeletonRowCount = useTableStore.use.skeletonRowCount();
13857
- const renderSubRow = useTableStore.use.renderSubRow();
13858
- const userGetRowCanExpand = useTableStore.use.getRowCanExpand();
13859
- const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
13860
- const loadingText = useTableStore.use.loadingText();
13861
- const rowCount = rows ?? (dynamicHeight && bodyHeight > 0 ? skeletonRowCount : DEFAULT_ROWS2);
13862
- const renderHeaderLabel = (header) => typeof header === "string" ? header : null;
13863
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13864
- /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13865
- /* @__PURE__ */ jsxs(
13866
- "div",
13867
- {
13868
- "data-testid": "ntable-loading-skeleton",
13869
- "data-ntable-loading-row-count": rowCount,
13870
- "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13871
- "aria-busy": "true",
13872
- "aria-label": loadingText,
13873
- role: "status",
13874
- style: surface.style,
13875
- className: cn("min-h-0 flex-1 rounded-md p-0", surface.className, dynamicHeight ? "overflow-hidden" : "najm-overlay-scroll", classNames?.content),
13876
- children: [
13877
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13878
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: dynamicHeight ? "najm-overlay-scroll h-full" : void 0, children: /* @__PURE__ */ jsxs(Table, { children: [
13879
- /* @__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: [
13880
- showCheckbox && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Select column", className: "w-10 text-foreground h-12" }),
13881
- hasExpansion && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Expand column", className: "w-10 text-foreground h-12" }),
13882
- columns.map((col, i) => /* @__PURE__ */ jsx(
13883
- TableHead,
13884
- {
13885
- className: cn("text-foreground h-12", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13886
- style: col?.size ? { width: col.size } : void 0,
13887
- children: renderHeaderLabel(col?.header)
13888
- },
13889
- col?.id ?? col?.accessorKey ?? i
13890
- ))
13891
- ] }) }),
13892
- /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rowCount }).map((_, r) => /* @__PURE__ */ jsxs(TableRow, { children: [
13893
- showCheckbox && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13894
- hasExpansion && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
13895
- columns.map((col, c) => /* @__PURE__ */ jsx(
13896
- TableCell,
13897
- {
13898
- className: cn("h-14", resolveHiddenBelowClass(col?.meta?.hiddenBelow)),
13899
- style: col?.size ? { width: col.size } : void 0,
13900
- children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-full" })
13901
- },
13902
- `skeleton-${r}-${col?.id ?? col?.accessorKey ?? c}`
13903
- ))
13904
- ] }, `skeleton-${r}`)) })
13905
- ] }) })
13906
- ]
13907
- }
13908
- )
13909
- ] });
13910
- }
13911
- function NTableCardsLoadingSkeleton({ rows }) {
13912
- const filters = useTableStore.use.filters();
13913
- const showViewToggle = useTableStore.use.showViewToggle();
13914
- const showColumnVisibility = useTableStore.use.showColumnVisibility();
13915
- const showAddButton = useTableStore.use.showAddButton();
13916
- const hasHeaderSlot = Boolean(useTableStore.use.headerSlot());
13917
- const hasToolbar = Boolean(useTableStore.use.renderToolbar());
13918
- const hasHeaderSkeleton = Boolean(
13919
- filters?.length || showViewToggle || showColumnVisibility || showAddButton || hasHeaderSlot || hasToolbar
13920
- );
13921
- const classNames = useTableStore.use.classNames();
13922
- const bordered = useTableStore.use.bordered();
13923
- const borderColor = useTableStore.use.borderColor();
13924
- const surface = useTableSurfaceAppearance(bordered, borderColor);
13925
- const dynamicHeight = useTableStore.use.dynamicHeight();
13926
- const bodyHeight = useTableStore.use.bodyHeight();
13927
- const cardColumnCount = useTableStore.use.cardColumnCount();
13928
- const cardRowHeight = useTableStore.use.cardRowHeight();
13929
- const cardGap = useTableStore.use.cardGap();
13930
- const loadingText = useTableStore.use.loadingText();
13931
- const cardCount = rows ?? (dynamicHeight && bodyHeight > 0 ? calculateCardSkeletonCount({
13932
- bodyHeight,
13933
- columnCount: cardColumnCount,
13934
- cardHeight: cardRowHeight,
13935
- gap: cardGap
13936
- }) : DEFAULT_CARD_COUNT);
13937
- const defaultContainerClass = "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4";
13938
- const containerClass = classNames?.cards ?? defaultContainerClass;
13939
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13940
- hasHeaderSkeleton && /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13941
- /* @__PURE__ */ jsxs(
13942
- NajmScroll,
13943
- {
13944
- axis: "y",
13945
- "aria-busy": "true",
13946
- "aria-label": loadingText,
13947
- role: "status",
13948
- className: "min-h-0 flex-1 overflow-hidden",
13949
- children: [
13950
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13951
- /* @__PURE__ */ jsx(
13952
- "div",
13953
- {
13954
- "data-testid": "ntable-cards-loading-skeleton",
13955
- "data-ntable-loading-cards-grid": true,
13956
- "data-ntable-loading-card-count": cardCount,
13957
- "aria-hidden": "true",
13958
- className: cn(containerClass),
13959
- children: Array.from({ length: cardCount }).map((_, index) => /* @__PURE__ */ jsx(
13960
- "div",
13961
- {
13962
- "data-ntable-loading-card": true,
13963
- "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13964
- style: surface.style,
13965
- className: cn("rounded-lg p-3 sm:p-4", surface.className),
13966
- children: /* @__PURE__ */ jsxs(
13967
- "div",
13968
- {
13969
- "data-ntable-loading-card-layout": "responsive-avatar",
13970
- className: "grid grid-cols-[80px_minmax(0,1fr)] gap-3 sm:grid-cols-[72px_minmax(0,1fr)]",
13971
- children: [
13972
- /* @__PURE__ */ jsx("div", { className: "col-start-1 row-start-1 flex items-start justify-center sm:justify-start", children: /* @__PURE__ */ jsx(
13973
- NSkeleton,
13974
- {
13975
- "data-ntable-loading-card-avatar": true,
13976
- className: "size-20 shrink-0 rounded-full sm:size-16"
13977
- }
13978
- ) }),
13979
- /* @__PURE__ */ jsxs("div", { className: "col-start-2 row-start-1 flex min-w-0 items-start justify-between gap-3", children: [
13980
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [
13981
- /* @__PURE__ */ jsx(NSkeleton, { className: "h-5 w-36 max-w-full" }),
13982
- /* @__PURE__ */ jsx(NSkeleton, { className: "hidden h-3 w-16 sm:block" })
13983
- ] }),
13984
- /* @__PURE__ */ jsx(
13985
- NSkeleton,
13986
- {
13987
- "data-ntable-loading-card-status": true,
13988
- className: "hidden h-6 w-14 shrink-0 rounded-full sm:block"
13989
- }
13990
- )
13991
- ] }),
13992
- /* @__PURE__ */ jsx(
13993
- "div",
13994
- {
13995
- "data-ntable-loading-card-details": true,
13996
- className: "col-start-2 row-start-2 space-y-1 sm:col-span-full sm:col-start-1 sm:space-y-2 sm:rounded-lg sm:bg-muted/50 sm:p-3",
13997
- children: Array.from({ length: 3 }).map((_2, detailIndex) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2", children: [
13998
- /* @__PURE__ */ jsx(NSkeleton, { className: "size-3.5 shrink-0 rounded-sm sm:size-4" }),
13999
- /* @__PURE__ */ jsx(
14000
- NSkeleton,
14001
- {
14002
- className: cn(
14003
- "h-3 max-w-full sm:h-4",
14004
- detailIndex === 0 ? "w-full" : detailIndex === 1 ? "w-4/5" : "w-3/4"
14005
- )
14006
- }
14007
- )
14008
- ] }, detailIndex))
14009
- }
14010
- )
14011
- ]
14012
- }
14013
- )
14014
- },
14015
- index
14016
- ))
14017
- }
14018
- )
14019
- ]
14020
- }
14021
- )
14022
- ] });
14023
- }
14024
14194
  function TableStateSlot({ children }) {
14025
14195
  return /* @__PURE__ */ jsx("div", { className: "flex min-h-64 flex-1 items-center justify-center", children });
14026
14196
  }