najm-kit 2.1.46 → 2.1.48

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
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import React__default, { createContext, useContext, useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
2
+ import React__default, { createContext, useRef, useMemo, useContext, useState, useEffect, useCallback, useLayoutEffect } from 'react';
3
3
  import { Slot } from '@radix-ui/react-slot';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { clsx } from 'clsx';
@@ -2168,7 +2168,7 @@ function NSheet({
2168
2168
  side,
2169
2169
  portalClassName: portal,
2170
2170
  className: cn(
2171
- "flex h-dvh max-h-dvh flex-col gap-0 overflow-hidden bg-sidebar p-0 text-foreground",
2171
+ "flex h-dvh max-h-dvh flex-col gap-0 overflow-hidden bg-background p-0 text-foreground",
2172
2172
  contentClassName,
2173
2173
  classNames?.content
2174
2174
  ),
@@ -7274,7 +7274,7 @@ function NSkeleton({ className, ...props }) {
7274
7274
  return /* @__PURE__ */ jsx(
7275
7275
  "div",
7276
7276
  {
7277
- className: cn("animate-pulse rounded-md bg-accent", className),
7277
+ className: cn("animate-pulse rounded-md bg-accent motion-reduce:animate-none", className),
7278
7278
  "aria-hidden": "true",
7279
7279
  ...props
7280
7280
  }
@@ -9296,6 +9296,149 @@ var PasswordInput = ({ value, onChange, placeholder = "", icon, showIcon = true,
9296
9296
  showPassword ? /* @__PURE__ */ jsx(Eye, { className: "absolute right-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground cursor-pointer", onClick: () => setShowPassword(false) }) : /* @__PURE__ */ jsx(EyeOff, { className: "absolute right-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground cursor-pointer", onClick: () => setShowPassword(true) })
9297
9297
  ] });
9298
9298
  };
9299
+ function normalizeCode(value, numeric, length) {
9300
+ const normalized = numeric ? value.replace(/\D/g, "") : value.replace(/\s/g, "");
9301
+ return normalized.slice(0, length);
9302
+ }
9303
+ var OtpInput = React__default.forwardRef(
9304
+ ({
9305
+ value,
9306
+ onChange,
9307
+ length = 6,
9308
+ numeric = true,
9309
+ ariaLabel = "One-time code",
9310
+ digitAriaLabel,
9311
+ autoFocus = false,
9312
+ autoComplete = "one-time-code",
9313
+ disabled = false,
9314
+ readOnly = false,
9315
+ status = "default",
9316
+ bordered = true,
9317
+ className,
9318
+ inputClassName,
9319
+ onComplete
9320
+ }, forwardedRef) => {
9321
+ const refs = useRef([]);
9322
+ const code = normalizeCode(value ?? "", numeric, length);
9323
+ const cells = useMemo(
9324
+ () => Array.from({ length }, (_, index) => code[index] ?? ""),
9325
+ [code, length]
9326
+ );
9327
+ const setRef = (index, node) => {
9328
+ refs.current[index] = node;
9329
+ if (index !== 0 || !forwardedRef) return;
9330
+ if (typeof forwardedRef === "function") forwardedRef(node);
9331
+ else forwardedRef.current = node;
9332
+ };
9333
+ const commit = (next) => {
9334
+ const normalized = normalizeCode(next, numeric, length);
9335
+ onChange(normalized);
9336
+ if (normalized.length === length) onComplete?.(normalized);
9337
+ return normalized;
9338
+ };
9339
+ const focusCell = (index) => {
9340
+ const target = refs.current[Math.max(0, Math.min(index, length - 1))];
9341
+ target?.focus();
9342
+ target?.select();
9343
+ };
9344
+ const replaceAt = (index, rawValue) => {
9345
+ const incoming = normalizeCode(rawValue, numeric, length);
9346
+ if (!incoming) {
9347
+ const next2 = `${code.slice(0, index)}${code.slice(index + 1)}`;
9348
+ commit(next2);
9349
+ return;
9350
+ }
9351
+ const next = `${code.slice(0, index)}${incoming}${code.slice(index + incoming.length)}`;
9352
+ const normalized = commit(next);
9353
+ focusCell(Math.min(index + incoming.length, Math.max(normalized.length, 1), length - 1));
9354
+ };
9355
+ return /* @__PURE__ */ jsx(
9356
+ "div",
9357
+ {
9358
+ role: "group",
9359
+ "aria-label": ariaLabel,
9360
+ "aria-invalid": status === "error" || void 0,
9361
+ "data-slot": "otp-input",
9362
+ "data-status": status,
9363
+ dir: "ltr",
9364
+ className: cn("flex w-full items-center justify-center gap-1.5 sm:gap-2", className),
9365
+ onPaste: (event) => {
9366
+ if (disabled || readOnly) return;
9367
+ const pasted = normalizeCode(event.clipboardData.getData("text"), numeric, length);
9368
+ if (!pasted) return;
9369
+ event.preventDefault();
9370
+ const normalized = commit(pasted);
9371
+ focusCell(Math.max(0, normalized.length - 1));
9372
+ },
9373
+ children: cells.map((character, index) => /* @__PURE__ */ jsx(
9374
+ "input",
9375
+ {
9376
+ ref: (node) => setRef(index, node),
9377
+ "data-slot": "otp-cell",
9378
+ type: "text",
9379
+ inputMode: numeric ? "numeric" : "text",
9380
+ pattern: numeric ? "[0-9]*" : void 0,
9381
+ autoComplete: index === 0 ? autoComplete : "off",
9382
+ autoFocus: autoFocus && index === 0,
9383
+ maxLength: length,
9384
+ value: character,
9385
+ disabled,
9386
+ readOnly,
9387
+ "aria-label": digitAriaLabel?.(index + 1, length) ?? `${ariaLabel} ${index + 1} of ${length}`,
9388
+ className: cn(
9389
+ "h-11 min-w-0 flex-1 rounded-md bg-card text-center text-lg font-semibold tabular-nums text-foreground outline-none transition-colors sm:h-12 sm:max-w-12",
9390
+ bordered !== false && "border border-input focus:border-ring focus:ring-2 focus:ring-ring/20",
9391
+ status === "error" && "border-destructive focus:border-destructive focus:ring-destructive/20",
9392
+ disabled && "cursor-not-allowed opacity-50",
9393
+ inputClassName
9394
+ ),
9395
+ onFocus: (event) => event.currentTarget.select(),
9396
+ onChange: (event) => {
9397
+ if (disabled || readOnly) return;
9398
+ const rawValue = event.currentTarget.value;
9399
+ if (numeric && rawValue && !/\d/.test(rawValue)) {
9400
+ event.currentTarget.value = character;
9401
+ return;
9402
+ }
9403
+ replaceAt(index, rawValue);
9404
+ },
9405
+ onKeyDown: (event) => {
9406
+ if (disabled || readOnly) return;
9407
+ if (event.key === "ArrowLeft") {
9408
+ event.preventDefault();
9409
+ focusCell(index - 1);
9410
+ } else if (event.key === "ArrowRight") {
9411
+ event.preventDefault();
9412
+ focusCell(index + 1);
9413
+ } else if (event.key === "Home") {
9414
+ event.preventDefault();
9415
+ focusCell(0);
9416
+ } else if (event.key === "End") {
9417
+ event.preventDefault();
9418
+ focusCell(length - 1);
9419
+ } else if (event.key === "Backspace") {
9420
+ event.preventDefault();
9421
+ if (character) {
9422
+ commit(`${code.slice(0, index)}${code.slice(index + 1)}`);
9423
+ focusCell(index);
9424
+ } else if (index > 0) {
9425
+ commit(`${code.slice(0, index - 1)}${code.slice(index)}`);
9426
+ focusCell(index - 1);
9427
+ }
9428
+ } else if (event.key === "Delete" && character) {
9429
+ event.preventDefault();
9430
+ commit(`${code.slice(0, index)}${code.slice(index + 1)}`);
9431
+ focusCell(index);
9432
+ }
9433
+ }
9434
+ },
9435
+ index
9436
+ ))
9437
+ }
9438
+ );
9439
+ }
9440
+ );
9441
+ OtpInput.displayName = "OtpInput";
9299
9442
  var DEFAULT_ROWS = 3;
9300
9443
  var MIN_ROWS = 2;
9301
9444
  var REM_PER_ROW = 1.5;
@@ -10778,6 +10921,7 @@ var Inputs = {
10778
10921
  text: TextInput,
10779
10922
  number: NumberInput,
10780
10923
  password: PasswordInput,
10924
+ otp: OtpInput,
10781
10925
  textarea: TextAreaInput,
10782
10926
  date: DateInput,
10783
10927
  file: FileInput,
@@ -11485,7 +11629,14 @@ var createTableStore = () => {
11485
11629
  addButtonText: "",
11486
11630
  pageSizeOptions: [10, 20, 30, 40, 50],
11487
11631
  calculatedPageSize: 10,
11632
+ skeletonRowCount: 6,
11488
11633
  maxHeight: null,
11634
+ bodyWidth: 0,
11635
+ bodyHeight: 0,
11636
+ tableHeaderHeight: 48,
11637
+ cardColumnCount: 1,
11638
+ cardRowHeight: 0,
11639
+ cardGap: 12,
11489
11640
  // JSON mode
11490
11641
  jsonValue: void 0,
11491
11642
  jsonColors: null,
@@ -11555,6 +11706,8 @@ var createTableStore = () => {
11555
11706
  // Responsive cards
11556
11707
  responsiveCards: true,
11557
11708
  isMobile: false,
11709
+ effectiveViewMode: "table",
11710
+ cardPagination: { mode: "paged" },
11558
11711
  // Empty states
11559
11712
  isEmpty: void 0,
11560
11713
  isFilteredEmpty: false,
@@ -11630,6 +11783,8 @@ function filterResponsiveColumns(columns) {
11630
11783
  // src/components/table/hooks.ts
11631
11784
  var ROW_HEIGHT = 56;
11632
11785
  var DEFAULT_TABLE_HEADER_HEIGHT = 48;
11786
+ var DEFAULT_CARD_HEIGHT = 176;
11787
+ var DEFAULT_CARD_GAP = 12;
11633
11788
  var ROOT_SECTION_GAP_COUNT = 2;
11634
11789
  function useStoreSync(props) {
11635
11790
  const storeRef = useRef(null);
@@ -11710,23 +11865,43 @@ function calculateDynamicPageSize(input) {
11710
11865
  if (availableRowsHeight <= 0) return 1;
11711
11866
  return Math.max(1, Math.floor(availableRowsHeight / rowHeight));
11712
11867
  }
11713
- function useDynamicPageSize(containerRef) {
11868
+ function calculateCardSkeletonCount(input) {
11869
+ const columns = Math.max(1, Math.floor(input.columnCount));
11870
+ const cardHeight = Math.max(1, input.cardHeight ?? DEFAULT_CARD_HEIGHT);
11871
+ const gap = Math.max(0, input.gap ?? DEFAULT_CARD_GAP);
11872
+ if (input.bodyHeight <= 0) return columns;
11873
+ const rows = Math.max(1, Math.ceil((input.bodyHeight + gap) / (cardHeight + gap)));
11874
+ return rows * columns;
11875
+ }
11876
+ function fallbackCardColumns(width) {
11877
+ if (width >= 1280) return 4;
11878
+ if (width >= 1024) return 3;
11879
+ if (width >= 640) return 2;
11880
+ return 1;
11881
+ }
11882
+ function useDynamicPageSize(containerRef, effectiveViewMode) {
11714
11883
  const dynamicHeight = useTableStore.use.dynamicHeight();
11715
- const viewMode = useTableStore.use.viewMode();
11884
+ const viewMode = useTableStore.use.effectiveViewMode();
11716
11885
  const manualPagination = useTableStore.use.manualPagination();
11717
11886
  const isLoading = useTableStore.use.isLoading();
11718
11887
  const error = useTableStore.use.error();
11719
11888
  const hasNoData = useTableStore.use.hasNoData();
11720
11889
  const isFilteredEmpty = useTableStore.use.isFilteredEmpty();
11721
11890
  const syncWithProps = useTableStore.use.syncWithProps();
11891
+ const lastMeasurementRef = useRef("");
11722
11892
  useLayoutEffect(() => {
11723
- if (!dynamicHeight || !containerRef.current || viewMode !== "table" || manualPagination) return;
11893
+ if (!dynamicHeight || !containerRef.current) return;
11724
11894
  const calculatePageSize = () => {
11725
11895
  const container2 = containerRef.current;
11726
11896
  if (!container2) return;
11727
11897
  const bodyEl = container2.querySelector("[data-ntable-body]");
11728
11898
  const tableHeaderEl = container2.querySelector("[data-ntable-table-header]");
11899
+ const loadingHeaderEl = container2.querySelector("[data-ntable-loading-header]");
11900
+ const cardsGridEl = container2.querySelector(
11901
+ "[data-ntable-loading-cards-grid], [data-ntable-cards-grid]"
11902
+ );
11729
11903
  let bodyHeight = bodyEl?.clientHeight ?? 0;
11904
+ const bodyWidth = bodyEl?.clientWidth ?? container2.clientWidth ?? 0;
11730
11905
  if (!bodyHeight) {
11731
11906
  const rootHeight = container2.clientHeight;
11732
11907
  const headerHeight = container2.querySelector("[data-ntable-header]")?.offsetHeight ?? 0;
@@ -11735,10 +11910,36 @@ function useDynamicPageSize(containerRef) {
11735
11910
  const gap = Number.parseFloat(rootStyles.rowGap || rootStyles.gap || "0") || 0;
11736
11911
  bodyHeight = rootHeight - headerHeight - paginationHeight - gap * ROOT_SECTION_GAP_COUNT;
11737
11912
  }
11913
+ if (loadingHeaderEl && bodyEl) {
11914
+ const bodyStyles = window.getComputedStyle(bodyEl);
11915
+ const bodyGap = Number.parseFloat(bodyStyles.rowGap || bodyStyles.gap || "0") || 0;
11916
+ bodyHeight = Math.max(0, bodyHeight - loadingHeaderEl.offsetHeight - bodyGap);
11917
+ }
11738
11918
  const tableHeaderHeight = tableHeaderEl?.offsetHeight ?? DEFAULT_TABLE_HEADER_HEIGHT;
11739
11919
  const newPageSize = calculateDynamicPageSize({ bodyHeight, tableHeaderHeight });
11740
11920
  const calculatedMaxHeight = tableHeaderHeight + newPageSize * ROW_HEIGHT;
11741
- syncWithProps({ calculatedPageSize: newPageSize, maxHeight: calculatedMaxHeight });
11921
+ const gridStyles = cardsGridEl ? window.getComputedStyle(cardsGridEl) : null;
11922
+ const gridTemplateColumns = gridStyles?.gridTemplateColumns;
11923
+ const gridColumns = gridTemplateColumns && gridTemplateColumns !== "none" ? gridTemplateColumns.split(" ").filter(Boolean).length : 0;
11924
+ const cardColumnCount = gridColumns || fallbackCardColumns(bodyWidth);
11925
+ const cardGap = Number.parseFloat(gridStyles?.rowGap || gridStyles?.gap || "") || DEFAULT_CARD_GAP;
11926
+ const firstCard = cardsGridEl?.querySelector("[data-ntable-loading-card], [data-row]");
11927
+ const cardRowHeight = firstCard?.offsetHeight || firstCard?.getBoundingClientRect().height || DEFAULT_CARD_HEIGHT;
11928
+ const updates = {
11929
+ ...!manualPagination ? { calculatedPageSize: newPageSize, maxHeight: calculatedMaxHeight } : {},
11930
+ skeletonRowCount: newPageSize,
11931
+ bodyWidth,
11932
+ bodyHeight,
11933
+ tableHeaderHeight,
11934
+ cardColumnCount,
11935
+ cardRowHeight,
11936
+ cardGap
11937
+ };
11938
+ const fingerprint = JSON.stringify(updates);
11939
+ if (fingerprint !== lastMeasurementRef.current) {
11940
+ lastMeasurementRef.current = fingerprint;
11941
+ syncWithProps(updates);
11942
+ }
11742
11943
  };
11743
11944
  calculatePageSize();
11744
11945
  const resizeObserver = new ResizeObserver(calculatePageSize);
@@ -11747,11 +11948,14 @@ function useDynamicPageSize(containerRef) {
11747
11948
  container.querySelectorAll(
11748
11949
  "[data-ntable-header], [data-ntable-body], [data-ntable-pagination], [data-ntable-table-header]"
11749
11950
  ).forEach((el) => resizeObserver.observe(el));
11951
+ container.querySelectorAll(
11952
+ "[data-ntable-loading-header], [data-ntable-loading-cards-grid], [data-ntable-loading-card], [data-ntable-cards-grid]"
11953
+ ).forEach((el) => resizeObserver.observe(el));
11750
11954
  if (container.parentElement) resizeObserver.observe(container.parentElement);
11751
11955
  return () => resizeObserver.disconnect();
11752
- }, [dynamicHeight, viewMode, containerRef, syncWithProps, manualPagination, isLoading, error, hasNoData, isFilteredEmpty]);
11956
+ }, [dynamicHeight, effectiveViewMode, viewMode, containerRef, syncWithProps, manualPagination, isLoading, error, hasNoData, isFilteredEmpty]);
11753
11957
  }
11754
- function useTable() {
11958
+ function useTable(effectiveViewModeOverride) {
11755
11959
  const [sorting, setSorting] = useState([]);
11756
11960
  const [columnFilters, setColumnFilters] = useState([]);
11757
11961
  const [columnVisibility, setColumnVisibility] = useState({});
@@ -11768,6 +11972,8 @@ function useTable() {
11768
11972
  const CardComponent = useTableStore.use.CardComponent();
11769
11973
  const dynamicHeight = useTableStore.use.dynamicHeight();
11770
11974
  const viewMode = useTableStore.use.viewMode();
11975
+ const effectiveViewMode = useTableStore.use.effectiveViewMode();
11976
+ const cardPagination = useTableStore.use.cardPagination();
11771
11977
  const calculatedPageSize = useTableStore.use.calculatedPageSize();
11772
11978
  const syncWithProps = useTableStore.use.syncWithProps();
11773
11979
  const onStateChange = useTableStore.use.onStateChange();
@@ -11858,6 +12064,8 @@ function useTable() {
11858
12064
  notifyStateChange({ sorting, columnFilters, columnVisibility, rowSelection: storeRowSelection, globalFilter });
11859
12065
  }, [storePagination, storeRowSelection, setPagination, sorting, columnFilters, columnVisibility, globalFilter, notifyStateChange]);
11860
12066
  const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
12067
+ const renderedMode = effectiveViewModeOverride ?? effectiveViewMode ?? viewMode;
12068
+ const renderAllSuppliedRows = renderedMode === "cards" && cardPagination.mode !== "paged";
11861
12069
  const tableConfig = {
11862
12070
  data,
11863
12071
  columns: finalColumns,
@@ -11874,7 +12082,7 @@ function useTable() {
11874
12082
  getPaginationRowModel: getPaginationRowModel(),
11875
12083
  getSortedRowModel: getSortedRowModel(),
11876
12084
  getExpandedRowModel: getExpandedRowModel(),
11877
- manualPagination,
12085
+ manualPagination: manualPagination || renderAllSuppliedRows,
11878
12086
  pageCount,
11879
12087
  rowCount
11880
12088
  };
@@ -11888,9 +12096,11 @@ function useTable() {
11888
12096
  }, [table]);
11889
12097
  useLayoutEffect(() => {
11890
12098
  if (manualPagination) return;
11891
- if (dynamicHeight && viewMode === "table") table.setPageSize(calculatedPageSize);
11892
- if (viewMode === "cards") table.setPageSize(data.length || 9999);
11893
- }, [calculatedPageSize, dynamicHeight, viewMode, table, data.length, manualPagination]);
12099
+ if (dynamicHeight && renderedMode === "table") table.setPageSize(calculatedPageSize);
12100
+ if (viewMode === "cards" && cardPagination.mode === "paged") {
12101
+ table.setPageSize(data.length || 9999);
12102
+ }
12103
+ }, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination]);
11894
12104
  return { table, finalColumns, sorting, setSorting, columnFilters, setColumnFilters, columnVisibility, setColumnVisibility, globalFilter, setGlobalFilter };
11895
12105
  }
11896
12106
  function useTableKeyboard(options = {}) {
@@ -11984,6 +12194,26 @@ function resolveTableColor(value, fallback) {
11984
12194
  }
11985
12195
  return color;
11986
12196
  }
12197
+
12198
+ // src/components/table/tableSurface.ts
12199
+ function useTableSurfaceAppearance(bordered, borderColor) {
12200
+ const recipe = useNajmComponentStyle("table");
12201
+ const recipeRadius = resolveRadiusValue(recipe?.radius);
12202
+ const resolvedBorderColor = resolveTableColor(borderColor, DEFAULT_TABLE_BORDER_COLOR);
12203
+ const style = recipeRadius || bordered !== false && (recipe?.borderWidth || borderColor) ? {
12204
+ ...recipeRadius ? { borderRadius: recipeRadius } : {},
12205
+ ...bordered !== false && recipe?.borderWidth ? { borderWidth: recipe.borderWidth } : {},
12206
+ ...bordered !== false && borderColor ? { borderColor: resolvedBorderColor } : {}
12207
+ } : void 0;
12208
+ return {
12209
+ bordered,
12210
+ style,
12211
+ className: cn(
12212
+ "bg-card",
12213
+ bordered === true ? surfaceBorderClasses(true) : "border-0 shadow-sm"
12214
+ )
12215
+ };
12216
+ }
11987
12217
  var ROW_CONTEXT_HANDLED = "__ntableRowContextHandled";
11988
12218
  function EditableCell({ cell, onCellEdit }) {
11989
12219
  const columnDef = cell.column.columnDef;
@@ -12038,12 +12268,6 @@ function EditableCell({ cell, onCellEdit }) {
12038
12268
  ] });
12039
12269
  }
12040
12270
  function NTableContent({ effectiveMode }) {
12041
- const recipe = useNajmComponentStyle("table");
12042
- const recipeRadius = resolveRadiusValue(recipe?.radius);
12043
- const recipeStyle = recipeRadius || recipe?.borderWidth ? {
12044
- ...recipeRadius ? { borderRadius: recipeRadius } : {},
12045
- ...recipe?.borderWidth ? { borderWidth: recipe.borderWidth } : {}
12046
- } : void 0;
12047
12271
  const table = useTableStore.use.table();
12048
12272
  const storeIsTableView = useTableStore.use.isTableView();
12049
12273
  const columns = useTableStore.use.columns();
@@ -12060,10 +12284,6 @@ function NTableContent({ effectiveMode }) {
12060
12284
  backgroundColor: resolvedHeaderColor,
12061
12285
  color: resolvedHeaderTextColor
12062
12286
  };
12063
- const contentStyle = recipeStyle || tableBorderColor ? {
12064
- ...recipeStyle ?? {},
12065
- ...tableBorderColor ? { borderColor: resolvedBorderColor } : {}
12066
- } : void 0;
12067
12287
  const rowBorderStyle = tableBorderColor ? { borderColor: resolvedBorderColor } : void 0;
12068
12288
  const onRowClick = useTableStore.use.onRowClick();
12069
12289
  const onRowContextMenu = useTableStore.use.onRowContextMenu();
@@ -12076,6 +12296,7 @@ function NTableContent({ effectiveMode }) {
12076
12296
  const showContent = useTableStore.use.showContent();
12077
12297
  const classNames = useTableStore.use.classNames();
12078
12298
  const bordered = useTableStore.use.bordered();
12299
+ const surface = useTableSurfaceAppearance(bordered, tableBorderColor);
12079
12300
  const showCheckbox = useTableStore.use.showCheckbox();
12080
12301
  const selectedRowId = useTableStore.use.selectedRowId();
12081
12302
  const renderSubRow = useTableStore.use.renderSubRow();
@@ -12104,11 +12325,11 @@ function NTableContent({ effectiveMode }) {
12104
12325
  axis: "both",
12105
12326
  "data-bordered": bordered === false ? "false" : bordered ? "true" : void 0,
12106
12327
  className: cn(
12107
- "min-h-0 flex-1 overflow-hidden rounded-md bg-card",
12108
- bordered === true ? surfaceBorderClasses(true) : "shadow-sm",
12328
+ "min-h-0 flex-1 overflow-hidden rounded-md",
12329
+ surface.className,
12109
12330
  classNames?.content
12110
12331
  ),
12111
- style: contentStyle,
12332
+ style: surface.style,
12112
12333
  onContextMenu: handleBackgroundContextMenu,
12113
12334
  children: /* @__PURE__ */ jsxs(Table, { children: [
12114
12335
  /* @__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: [
@@ -12217,13 +12438,8 @@ function NTableContent({ effectiveMode }) {
12217
12438
  }
12218
12439
  );
12219
12440
  }
12220
- function NDataCardShell({ row, onClick, onContextMenu, actions, children, className, showCheckbox = true, selectedRowId, openRowMenu, menuButton, bordered }) {
12221
- const recipe = useNajmComponentStyle("table");
12222
- const recipeRadius = resolveRadiusValue(recipe?.radius);
12223
- const recipeStyle = recipeRadius || recipe?.borderWidth ? {
12224
- ...recipeRadius ? { borderRadius: recipeRadius } : {},
12225
- ...recipe?.borderWidth ? { borderWidth: recipe.borderWidth } : {}
12226
- } : void 0;
12441
+ function NDataCardShell({ row, onClick, onContextMenu, actions, children, className, showCheckbox = true, selectedRowId, openRowMenu, menuButton, bordered, borderColor }) {
12442
+ const surface = useTableSurfaceAppearance(bordered, borderColor);
12227
12443
  const canExpand = row.getCanExpand();
12228
12444
  const isExpanded = canExpand && row.getIsExpanded();
12229
12445
  const isSelected = row.getIsSelected();
@@ -12238,10 +12454,10 @@ function NDataCardShell({ row, onClick, onContextMenu, actions, children, classN
12238
12454
  "data-bordered": bordered === false ? "false" : bordered ? "true" : void 0,
12239
12455
  onClick,
12240
12456
  onContextMenu,
12241
- style: recipeStyle,
12457
+ style: surface.style,
12242
12458
  className: cn(
12243
12459
  "relative group w-full rounded-lg bg-card text-card-foreground overflow-hidden",
12244
- surfaceBorderClasses(bordered),
12460
+ surface.className,
12245
12461
  isActive && (bordered ? "border-primary" : "ring-2 ring-primary ring-offset-1 ring-offset-background"),
12246
12462
  onClick && "cursor-pointer",
12247
12463
  className
@@ -12258,7 +12474,7 @@ function NDataCardShell({ row, onClick, onContextMenu, actions, children, classN
12258
12474
  className: "h-4 w-4"
12259
12475
  }
12260
12476
  ) }),
12261
- useMenuButton ? /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 h-auto z-10 opacity-0 transition-opacity duration-200 group-hover:opacity-100 focus-within:opacity-100", children: /* @__PURE__ */ jsx(
12477
+ useMenuButton ? /* @__PURE__ */ jsx("div", { "data-ntable-card-action": true, className: "ntable-card-action absolute end-2 top-2 z-10 h-auto transition-opacity duration-200", children: /* @__PURE__ */ jsx(
12262
12478
  "button",
12263
12479
  {
12264
12480
  type: "button",
@@ -12267,10 +12483,10 @@ function NDataCardShell({ row, onClick, onContextMenu, actions, children, classN
12267
12483
  e.stopPropagation();
12268
12484
  openRowMenu(e, row.original);
12269
12485
  },
12270
- className: "flex h-7 w-7 p-0 rounded-md cursor-pointer justify-center items-center text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
12486
+ className: "flex h-7 w-7 cursor-pointer items-center justify-center rounded-md p-0 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
12271
12487
  children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" })
12272
12488
  }
12273
- ) }) : actions && (actions.onView || actions.onEdit || actions.onDelete) ? /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 h-auto z-10 opacity-0 transition-opacity duration-200 group-hover:opacity-100 focus-within:opacity-100", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
12489
+ ) }) : actions && (actions.onView || actions.onEdit || actions.onDelete) ? /* @__PURE__ */ jsx("div", { "data-ntable-card-action": true, className: "ntable-card-action absolute end-2 top-2 z-10 h-auto transition-opacity duration-200", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
12274
12490
  /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
12275
12491
  "div",
12276
12492
  {
@@ -12375,6 +12591,7 @@ function NTableCards({ effectiveMode }) {
12375
12591
  const showContent = useTableStore.use.showContent();
12376
12592
  const classNames = useTableStore.use.classNames();
12377
12593
  const bordered = useTableStore.use.bordered();
12594
+ const borderColor = useTableStore.use.borderColor();
12378
12595
  const renderSubRow = useTableStore.use.renderSubRow();
12379
12596
  const userGetRowCanExpand = useTableStore.use.getRowCanExpand();
12380
12597
  const handleContainerContextMenu = useCallback((e) => {
@@ -12412,7 +12629,7 @@ function NTableCards({ effectiveMode }) {
12412
12629
  const defaultContainerClass = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3";
12413
12630
  const containerClass = classNames?.cards ?? defaultContainerClass;
12414
12631
  const actions = !menuButton && (onView || onEdit || onDelete) ? { onView, onEdit, onDelete } : void 0;
12415
- return /* @__PURE__ */ jsx(NajmScroll, { axis: "y", className: "min-h-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: cn(containerClass), onContextMenu: handleContainerContextMenu, children: rows.map((row) => {
12632
+ 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) => {
12416
12633
  const noShell = Boolean(row.original?.__smsNoShell);
12417
12634
  const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
12418
12635
  const canExpand = hasExpansion && row.getCanExpand();
@@ -12442,7 +12659,8 @@ function NTableCards({ effectiveMode }) {
12442
12659
  e.stopPropagation();
12443
12660
  openRowMenu(e, row.original);
12444
12661
  },
12445
- className: "absolute top-2 right-2 z-10 flex h-7 w-7 cursor-pointer items-center justify-center rounded-md p-0 text-muted-foreground opacity-0 transition-all duration-200 hover:bg-muted/50 hover:text-foreground group-hover:opacity-100 focus:opacity-100 focus-visible:opacity-100",
12662
+ "data-ntable-card-action": true,
12663
+ 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",
12446
12664
  children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" })
12447
12665
  }
12448
12666
  ),
@@ -12478,6 +12696,7 @@ function NTableCards({ effectiveMode }) {
12478
12696
  openRowMenu,
12479
12697
  menuButton,
12480
12698
  bordered,
12699
+ borderColor,
12481
12700
  className: rowClassName || void 0,
12482
12701
  children: /* @__PURE__ */ jsx(
12483
12702
  CardComponent,
@@ -12495,13 +12714,108 @@ function NTableCards({ effectiveMode }) {
12495
12714
  );
12496
12715
  }) }) });
12497
12716
  }
12717
+ function CardLoadMorePagination({
12718
+ config,
12719
+ rowCount,
12720
+ bordered,
12721
+ className
12722
+ }) {
12723
+ const [internalPending, setInternalPending] = React__default.useState(false);
12724
+ const [internalError, setInternalError] = React__default.useState(null);
12725
+ const [announcement, setAnnouncement] = React__default.useState("");
12726
+ const buttonRef = React__default.useRef(null);
12727
+ const pendingRef = React__default.useRef(false);
12728
+ const restoreFocusRef = React__default.useRef(false);
12729
+ const previousRowCountRef = React__default.useRef(rowCount);
12730
+ const errorId = React__default.useId();
12731
+ const pending = Boolean(config.loadingMore || internalPending);
12732
+ const error = config.loadMoreError ?? internalError;
12733
+ React__default.useEffect(() => {
12734
+ const previous = previousRowCountRef.current;
12735
+ if (rowCount > previous) {
12736
+ const appended = rowCount - previous;
12737
+ setAnnouncement(
12738
+ config.itemsLoadedLabel?.(appended) ?? `${appended} more ${appended === 1 ? "item" : "items"} loaded.`
12739
+ );
12740
+ }
12741
+ previousRowCountRef.current = rowCount;
12742
+ }, [config.itemsLoadedLabel, rowCount]);
12743
+ React__default.useEffect(() => {
12744
+ if (pending || !restoreFocusRef.current) return;
12745
+ restoreFocusRef.current = false;
12746
+ const frame = requestAnimationFrame(() => buttonRef.current?.focus());
12747
+ return () => cancelAnimationFrame(frame);
12748
+ }, [pending]);
12749
+ const loadMore = async () => {
12750
+ if (pendingRef.current || pending || !config.hasNextPage && !error) return;
12751
+ pendingRef.current = true;
12752
+ restoreFocusRef.current = document.activeElement === buttonRef.current;
12753
+ setInternalPending(true);
12754
+ setInternalError(null);
12755
+ const loadingAnnouncement = config.loadingMoreLabel ?? "Loading more items...";
12756
+ setAnnouncement(loadingAnnouncement);
12757
+ try {
12758
+ await config.onLoadMore();
12759
+ } catch {
12760
+ setInternalError(config.loadMoreErrorLabel ?? "Couldn't load more items.");
12761
+ setAnnouncement("");
12762
+ } finally {
12763
+ pendingRef.current = false;
12764
+ setInternalPending(false);
12765
+ setAnnouncement((current) => current === loadingAnnouncement ? "" : current);
12766
+ }
12767
+ };
12768
+ if (!config.hasNextPage && !pending && !error) {
12769
+ return /* @__PURE__ */ jsx(
12770
+ "div",
12771
+ {
12772
+ "data-ntable-load-more-end": true,
12773
+ role: "status",
12774
+ "aria-live": "polite",
12775
+ className: cn("py-2 text-center text-sm text-muted-foreground", className),
12776
+ children: config.endLabel ?? "No more items."
12777
+ }
12778
+ );
12779
+ }
12780
+ return /* @__PURE__ */ jsxs(
12781
+ "div",
12782
+ {
12783
+ "data-ntable-load-more": true,
12784
+ className: cn("flex min-w-0 flex-col items-center gap-2 py-2", className),
12785
+ children: [
12786
+ 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,
12787
+ /* @__PURE__ */ jsxs(
12788
+ Button,
12789
+ {
12790
+ ref: buttonRef,
12791
+ type: "button",
12792
+ bordered,
12793
+ variant: "outline",
12794
+ autoLoading: false,
12795
+ disabled: pending,
12796
+ "aria-describedby": error ? errorId : void 0,
12797
+ "aria-busy": pending ? "true" : void 0,
12798
+ onClick: loadMore,
12799
+ children: [
12800
+ pending ? /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "h-4 w-4 animate-spin motion-reduce:animate-none" }) : null,
12801
+ pending ? config.loadingMoreLabel ?? "Loading more..." : error ? config.retryLabel ?? "Retry" : config.loadMoreLabel ?? "Load more"
12802
+ ]
12803
+ }
12804
+ ),
12805
+ /* @__PURE__ */ jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement })
12806
+ ]
12807
+ }
12808
+ );
12809
+ }
12498
12810
  function NTablePagination() {
12499
12811
  const table = useTableStore.use.table();
12500
12812
  const showPagination = useTableStore.use.showPagination();
12501
12813
  const showContent = useTableStore.use.showContent();
12502
12814
  const pageSizeOptions = useTableStore.use.pageSizeOptions();
12503
12815
  const classNames = useTableStore.use.classNames();
12504
- const viewMode = useTableStore.use.viewMode();
12816
+ const effectiveViewMode = useTableStore.use.effectiveViewMode();
12817
+ const cardPagination = useTableStore.use.cardPagination();
12818
+ const data = useTableStore.use.data();
12505
12819
  const pagination = useTableStore.use.pagination();
12506
12820
  const manualPagination = useTableStore.use.manualPagination();
12507
12821
  const pageCount = useTableStore.use.pageCount();
@@ -12509,7 +12823,19 @@ function NTablePagination() {
12509
12823
  const setPagination = useTableStore.use.setPagination();
12510
12824
  const isPaginationControlled = useTableStore.use.isPaginationControlled();
12511
12825
  const bordered = useTableStore.use.bordered();
12512
- if (!table || !showContent || !showPagination || viewMode === "json" || viewMode === "files") return null;
12826
+ if (!table || !showContent || !showPagination || effectiveViewMode === "json" || effectiveViewMode === "files") return null;
12827
+ if (effectiveViewMode === "cards" && cardPagination.mode === "all") return null;
12828
+ if (effectiveViewMode === "cards" && cardPagination.mode === "load-more") {
12829
+ return /* @__PURE__ */ jsx(
12830
+ CardLoadMorePagination,
12831
+ {
12832
+ config: cardPagination,
12833
+ rowCount: data.length,
12834
+ bordered,
12835
+ className: classNames?.pagination
12836
+ }
12837
+ );
12838
+ }
12513
12839
  const filteredRows = table.getFilteredRowModel().rows;
12514
12840
  const selectedRows = table.getFilteredSelectedRowModel().rows;
12515
12841
  const { pageIndex, pageSize } = table.getState().pagination;
@@ -12947,7 +13273,7 @@ function NTableHeaderSkeleton() {
12947
13273
  }
12948
13274
  );
12949
13275
  }
12950
- function NTableLoadingSkeleton({ rows = DEFAULT_ROWS2 }) {
13276
+ function NTableLoadingSkeleton({ rows }) {
12951
13277
  const rawColumns = useTableStore.use.columns();
12952
13278
  const responsiveColumns = React__default.useMemo(() => filterResponsiveColumns(rawColumns), [rawColumns]);
12953
13279
  const columns = responsiveColumns;
@@ -12955,24 +13281,34 @@ function NTableLoadingSkeleton({ rows = DEFAULT_ROWS2 }) {
12955
13281
  const headerClassName = useTableStore.use.headerClassName();
12956
13282
  const classNames = useTableStore.use.classNames();
12957
13283
  const dynamicHeight = useTableStore.use.dynamicHeight();
13284
+ const bordered = useTableStore.use.bordered();
13285
+ const borderColor = useTableStore.use.borderColor();
13286
+ const surface = useTableSurfaceAppearance(bordered, borderColor);
13287
+ const bodyHeight = useTableStore.use.bodyHeight();
13288
+ const skeletonRowCount = useTableStore.use.skeletonRowCount();
12958
13289
  const renderSubRow = useTableStore.use.renderSubRow();
12959
13290
  const userGetRowCanExpand = useTableStore.use.getRowCanExpand();
12960
13291
  const hasExpansion = Boolean(renderSubRow || userGetRowCanExpand);
12961
13292
  const loadingText = useTableStore.use.loadingText();
13293
+ const rowCount = rows ?? (dynamicHeight && bodyHeight > 0 ? skeletonRowCount : DEFAULT_ROWS2);
12962
13294
  const renderHeaderLabel = (header) => typeof header === "string" ? header : null;
12963
13295
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
12964
13296
  /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
12965
13297
  /* @__PURE__ */ jsxs(
12966
- Card,
13298
+ "div",
12967
13299
  {
12968
13300
  "data-testid": "ntable-loading-skeleton",
13301
+ "data-ntable-loading-row-count": rowCount,
13302
+ "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
12969
13303
  "aria-busy": "true",
12970
13304
  "aria-label": loadingText,
12971
- className: cn("rounded-md p-0 border", dynamicHeight ? "overflow-hidden" : "najm-overlay-scroll", classNames?.content),
13305
+ role: "status",
13306
+ style: surface.style,
13307
+ className: cn("min-h-0 flex-1 rounded-md p-0", surface.className, dynamicHeight ? "overflow-hidden" : "najm-overlay-scroll", classNames?.content),
12972
13308
  children: [
12973
13309
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
12974
- /* @__PURE__ */ jsx("div", { className: dynamicHeight ? "najm-overlay-scroll" : void 0, children: /* @__PURE__ */ jsxs(Table, { children: [
12975
- /* @__PURE__ */ jsx(TableHeader, { className: cn(headerClassName, dynamicHeight && "sticky top-0 z-10", classNames?.tableHeader), children: /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-muted/30", children: [
13310
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: dynamicHeight ? "najm-overlay-scroll h-full" : void 0, children: /* @__PURE__ */ jsxs(Table, { children: [
13311
+ /* @__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: [
12976
13312
  showCheckbox && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Select column", className: "w-10 text-foreground h-12" }),
12977
13313
  hasExpansion && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Expand column", className: "w-10 text-foreground h-12" }),
12978
13314
  columns.map((col, i) => /* @__PURE__ */ jsx(
@@ -12985,7 +13321,7 @@ function NTableLoadingSkeleton({ rows = DEFAULT_ROWS2 }) {
12985
13321
  col?.id ?? col?.accessorKey ?? i
12986
13322
  ))
12987
13323
  ] }) }),
12988
- /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rows }).map((_, r) => /* @__PURE__ */ jsxs(TableRow, { children: [
13324
+ /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rowCount }).map((_, r) => /* @__PURE__ */ jsxs(TableRow, { children: [
12989
13325
  showCheckbox && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
12990
13326
  hasExpansion && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10", children: /* @__PURE__ */ jsx(NSkeleton, { className: "h-4 w-4" }) }),
12991
13327
  columns.map((col, c) => /* @__PURE__ */ jsx(
@@ -13016,74 +13352,105 @@ function NTableCardsLoadingSkeleton({ rows }) {
13016
13352
  );
13017
13353
  const classNames = useTableStore.use.classNames();
13018
13354
  const bordered = useTableStore.use.bordered();
13019
- const calculatedPageSize = useTableStore.use.calculatedPageSize();
13020
- const pagination = useTableStore.use.pagination();
13021
- const cardCount = rows ?? Math.max(1, calculatedPageSize || pagination?.pageSize || DEFAULT_CARD_COUNT);
13355
+ const borderColor = useTableStore.use.borderColor();
13356
+ const surface = useTableSurfaceAppearance(bordered, borderColor);
13357
+ const dynamicHeight = useTableStore.use.dynamicHeight();
13358
+ const bodyHeight = useTableStore.use.bodyHeight();
13359
+ const cardColumnCount = useTableStore.use.cardColumnCount();
13360
+ const cardRowHeight = useTableStore.use.cardRowHeight();
13361
+ const cardGap = useTableStore.use.cardGap();
13362
+ const loadingText = useTableStore.use.loadingText();
13363
+ const cardCount = rows ?? (dynamicHeight && bodyHeight > 0 ? calculateCardSkeletonCount({
13364
+ bodyHeight,
13365
+ columnCount: cardColumnCount,
13366
+ cardHeight: cardRowHeight,
13367
+ gap: cardGap
13368
+ }) : DEFAULT_CARD_COUNT);
13369
+ const defaultContainerClass = "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4";
13370
+ const containerClass = classNames?.cards ?? defaultContainerClass;
13022
13371
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-2", children: [
13023
13372
  hasHeaderSkeleton && /* @__PURE__ */ jsx(NTableHeaderSkeleton, {}),
13024
- /* @__PURE__ */ jsx(NajmScroll, { axis: "y", className: "min-h-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
13025
- "div",
13373
+ /* @__PURE__ */ jsxs(
13374
+ NajmScroll,
13026
13375
  {
13027
- "data-testid": "ntable-cards-loading-skeleton",
13376
+ axis: "y",
13028
13377
  "aria-busy": "true",
13029
- className: cn("grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", classNames?.cards),
13030
- children: Array.from({ length: cardCount }).map((_, index) => /* @__PURE__ */ jsx(
13031
- Card,
13032
- {
13033
- className: cn("rounded-lg bg-card p-3 shadow-none sm:p-4", surfaceBorderClasses(bordered)),
13034
- children: /* @__PURE__ */ jsxs(
13035
- "div",
13036
- {
13037
- "data-ntable-loading-card-layout": "responsive-avatar",
13038
- className: "grid grid-cols-[80px_minmax(0,1fr)] gap-3 sm:grid-cols-[72px_minmax(0,1fr)]",
13039
- children: [
13040
- /* @__PURE__ */ jsx("div", { className: "col-start-1 row-start-1 flex items-start justify-center sm:justify-start", children: /* @__PURE__ */ jsx(
13041
- NSkeleton,
13042
- {
13043
- "data-ntable-loading-card-avatar": true,
13044
- className: "size-20 shrink-0 rounded-full sm:size-16"
13045
- }
13046
- ) }),
13047
- /* @__PURE__ */ jsxs("div", { className: "col-start-2 row-start-1 flex min-w-0 items-start justify-between gap-3", children: [
13048
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [
13049
- /* @__PURE__ */ jsx(NSkeleton, { className: "h-5 w-36 max-w-full" }),
13050
- /* @__PURE__ */ jsx(NSkeleton, { className: "hidden h-3 w-16 sm:block" })
13051
- ] }),
13052
- /* @__PURE__ */ jsx(
13053
- NSkeleton,
13054
- {
13055
- "data-ntable-loading-card-status": true,
13056
- className: "hidden h-6 w-14 shrink-0 rounded-full sm:block"
13057
- }
13058
- )
13059
- ] }),
13060
- /* @__PURE__ */ jsx(
13378
+ "aria-label": loadingText,
13379
+ role: "status",
13380
+ className: "min-h-0 flex-1 overflow-hidden",
13381
+ children: [
13382
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: loadingText }),
13383
+ /* @__PURE__ */ jsx(
13384
+ "div",
13385
+ {
13386
+ "data-testid": "ntable-cards-loading-skeleton",
13387
+ "data-ntable-loading-cards-grid": true,
13388
+ "data-ntable-loading-card-count": cardCount,
13389
+ "aria-hidden": "true",
13390
+ className: cn(containerClass),
13391
+ children: Array.from({ length: cardCount }).map((_, index) => /* @__PURE__ */ jsx(
13392
+ "div",
13393
+ {
13394
+ "data-ntable-loading-card": true,
13395
+ "data-bordered": surface.bordered === false ? "false" : surface.bordered ? "true" : void 0,
13396
+ style: surface.style,
13397
+ className: cn("rounded-lg p-3 sm:p-4", surface.className),
13398
+ children: /* @__PURE__ */ jsxs(
13061
13399
  "div",
13062
13400
  {
13063
- "data-ntable-loading-card-details": true,
13064
- 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",
13065
- children: Array.from({ length: 3 }).map((_2, detailIndex) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2", children: [
13066
- /* @__PURE__ */ jsx(NSkeleton, { className: "size-3.5 shrink-0 rounded-sm sm:size-4" }),
13067
- /* @__PURE__ */ jsx(
13401
+ "data-ntable-loading-card-layout": "responsive-avatar",
13402
+ className: "grid grid-cols-[80px_minmax(0,1fr)] gap-3 sm:grid-cols-[72px_minmax(0,1fr)]",
13403
+ children: [
13404
+ /* @__PURE__ */ jsx("div", { className: "col-start-1 row-start-1 flex items-start justify-center sm:justify-start", children: /* @__PURE__ */ jsx(
13068
13405
  NSkeleton,
13069
13406
  {
13070
- className: cn(
13071
- "h-3 max-w-full sm:h-4",
13072
- detailIndex === 0 ? "w-full" : detailIndex === 1 ? "w-4/5" : "w-3/4"
13073
- )
13407
+ "data-ntable-loading-card-avatar": true,
13408
+ className: "size-20 shrink-0 rounded-full sm:size-16"
13409
+ }
13410
+ ) }),
13411
+ /* @__PURE__ */ jsxs("div", { className: "col-start-2 row-start-1 flex min-w-0 items-start justify-between gap-3", children: [
13412
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [
13413
+ /* @__PURE__ */ jsx(NSkeleton, { className: "h-5 w-36 max-w-full" }),
13414
+ /* @__PURE__ */ jsx(NSkeleton, { className: "hidden h-3 w-16 sm:block" })
13415
+ ] }),
13416
+ /* @__PURE__ */ jsx(
13417
+ NSkeleton,
13418
+ {
13419
+ "data-ntable-loading-card-status": true,
13420
+ className: "hidden h-6 w-14 shrink-0 rounded-full sm:block"
13421
+ }
13422
+ )
13423
+ ] }),
13424
+ /* @__PURE__ */ jsx(
13425
+ "div",
13426
+ {
13427
+ "data-ntable-loading-card-details": true,
13428
+ 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",
13429
+ children: Array.from({ length: 3 }).map((_2, detailIndex) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2", children: [
13430
+ /* @__PURE__ */ jsx(NSkeleton, { className: "size-3.5 shrink-0 rounded-sm sm:size-4" }),
13431
+ /* @__PURE__ */ jsx(
13432
+ NSkeleton,
13433
+ {
13434
+ className: cn(
13435
+ "h-3 max-w-full sm:h-4",
13436
+ detailIndex === 0 ? "w-full" : detailIndex === 1 ? "w-4/5" : "w-3/4"
13437
+ )
13438
+ }
13439
+ )
13440
+ ] }, detailIndex))
13074
13441
  }
13075
13442
  )
13076
- ] }, detailIndex))
13443
+ ]
13077
13444
  }
13078
13445
  )
13079
- ]
13080
- }
13081
- )
13082
- },
13083
- index
13084
- ))
13446
+ },
13447
+ index
13448
+ ))
13449
+ }
13450
+ )
13451
+ ]
13085
13452
  }
13086
- ) })
13453
+ )
13087
13454
  ] });
13088
13455
  }
13089
13456
  function TableStateSlot({ children }) {
@@ -13134,7 +13501,7 @@ function TableLayout(props) {
13134
13501
  const responsiveCards = useTableStore.use.responsiveCards();
13135
13502
  const isCustomMode = useTableStore.use.isCustomMode();
13136
13503
  const renderCustomMode = useTableStore.use.renderCustomMode();
13137
- const [isMobile, setIsMobile] = useState(false);
13504
+ const [isMobile, setIsMobile] = useState(() => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 639px)").matches : false);
13138
13505
  useEffect(() => {
13139
13506
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
13140
13507
  const mql = window.matchMedia("(max-width: 639px)");
@@ -13143,18 +13510,22 @@ function TableLayout(props) {
13143
13510
  mql.addEventListener("change", handler2);
13144
13511
  return () => mql.removeEventListener("change", handler2);
13145
13512
  }, []);
13146
- useDynamicPageSize(containerRef);
13147
- useTable();
13148
- useTableKeyboard({
13149
- scopeRef: containerRef,
13150
- contextMenuClose: props.contextMenuClose,
13151
- contextMenuOpen: props.contextMenuOpen
13152
- });
13153
13513
  const effectiveMode = (() => {
13154
13514
  if (viewMode === "json") return "json";
13155
13515
  if (isMobile && responsiveCards && CardComponent) return "cards";
13156
13516
  return viewMode;
13157
13517
  })();
13518
+ const syncWithProps = useTableStore.use.syncWithProps();
13519
+ useLayoutEffect(() => {
13520
+ syncWithProps({ isMobile, effectiveViewMode: effectiveMode });
13521
+ }, [effectiveMode, isMobile, syncWithProps]);
13522
+ useDynamicPageSize(containerRef, effectiveMode);
13523
+ useTable(effectiveMode);
13524
+ useTableKeyboard({
13525
+ scopeRef: containerRef,
13526
+ contextMenuClose: props.contextMenuClose,
13527
+ contextMenuOpen: props.contextMenuOpen
13528
+ });
13158
13529
  const showFilteredEmpty = isFilteredEmpty && !isLoading && !error;
13159
13530
  const showEmpty = hasNoData && !isLoading && !error && !showFilteredEmpty;
13160
13531
  const customRenderer = isCustomMode ? renderCustomMode?.[viewMode] : void 0;
@@ -13314,6 +13685,7 @@ function NTable(props) {
13314
13685
  pagination: props.pagination,
13315
13686
  defaultPagination: props.defaultPagination,
13316
13687
  onPaginationChange: props.onPaginationChange ?? null,
13688
+ cardPagination: props.cardPagination ?? { mode: "paged" },
13317
13689
  // Row selection
13318
13690
  rowSelection: props.rowSelection,
13319
13691
  defaultRowSelection: props.defaultRowSelection,
@@ -14681,4 +15053,4 @@ function NGridItem({
14681
15053
  return /* @__PURE__ */ jsx("div", { className: itemClassName, ...props, children });
14682
15054
  }
14683
15055
 
14684
- export { Alert, NCard as AsyncCard, Avatar, AvatarFallback, AvatarFormInput, AvatarGroup, AvatarImage, AvatarInput, AvatarStatus, Badge, BaseInput, Button, Calendar, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxGroupInput, CheckboxInput, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, ColorArrayInput, ColorPickerInput, Combobox, ComboboxInput, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, DEFAULT_THEME_FILE_NAME, DateInput, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicArray_default as DynamicArray, EmojiInput, FileImportButton, FileInput, Form, FormControl, FormDescription, FormField, FormInput, FormItem, FormLabel, FormMessage, IconButton, ImageInput, Indicator2 as Indicator, Input, Label2 as Label, LangInput, MultiSelectInput, NAJM_COMPONENT_NAMES, NAlert, NAppShell, NCard as NAsyncCard, NAvatar, NBadge, NBulkActionsBar, NButton, NCard, NCardAction, NCardFooter, NCardInfo, NCardMedia, NCardSection, NCommandPalette, NConfirmDialog, NContextMenu, NDataCardShell, NDeleteDialog, NDeleteDialogContent, NDetailCard, NDetailItem, NDetailList, NDialog, NDialogDescription, NDialogHeader, NDialogPrimaryButton, NDialogSecondaryButton, NDonutCard, NEditorTabs, NEmptyState, NErrorBoundary, NErrorState, NFileBrowser, NFileTypeIcon, NFilterBar, NFolderIcon, NForm, NFormSectionHeader, NGrid, NGridItem, NIcon, NIndicator, NInspectorSheet, NLoadingState, NMultiDialog, NNavbar, NPageHeader, NPageHeaderActions, NPageHeaderCompactActions, NPageHeaderFilters, NPageHeaderTop, NPageLayout, NPortalScopeProvider, NProgress, NRowActions, NSection, NSectionHeader, NSectionInfo, NSectionWithInfo, NSheet, NSidebar, NSidebarContent, NSidebarFooter, NSidebarHeader, NSidebarItem, NSidebarLogo, NSidebarMobile, NSidebarSection, NSkeleton, NSkeletonCalendar, NSkeletonChart, NSkeletonDonut, NSkeletonEventList, NSkeletonWidget, NSkeletonWidgets, NSlider, NSmartPasteDialog, NSpinner, NStatCard, NStatCardSkeleton, Swap as NSwap, NTable, NTableCardRoot, NTableCards, NTableContent, NTableHeader, NTableJson, NTableLoadingSkeleton, NTablePagination, NTableRowSkeleton, NTableSkeleton, NTabs, NThemeCustomizer, NUploader, NViewBody, NViewToggle, NajmDesignProvider, NajmScroll, NajmThemeProvider, NativeSelect, NumberInput, PasswordInput, PhoneInput, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrefixProvider, Progress, RADIUS_VALUE_MAP, RadioGroup2 as RadioGroup, RadioGroupInput, RadioGroupItem, RepeatingFields, ScrollArea, SearchField, SearchField as SearchInput, SegmentedControl, Select, SelectContent, SelectGroup, SelectInput, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator3 as Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SimpleTooltip, NSkeleton as Skeleton, Slider, SliderInput, StarRatingInput, StatusPill, StepIndicator, StepsHeader, StepsProgress, Swap, SwapIndeterminate, SwapOff, SwapOn, Switch, SwitchInput, TAB_COLORS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, TableStoreContext, Tabs, TabsContent, TabsList, TabsTrigger, TextAreaInput, TextInput, Textarea, TimeInput, TimeZoneInput, Toaster, Toggle, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, VariantProvider, WizardForm, alertVariants, avatarVariants, badgeColorVariants, badgeVariants, buildDefaultFileColumns, buttonVariants, cn, composePreset, createDialogStore, createTableStore, defineNajmDesignConfig, defineNajmThemeConfig, detectFormat, dialogVariants, filterResponsiveColumns, formatColor, formatFileBytes, formatFileRelative, getIconColorProps, getNextEditorTabValue, hiddenBelowClasses, indicatorVariants, inputBorderClasses, normalizeThemeFileName, parseColor, parseNajmDesignConfig, parseNajmThemeConfig, parseThemeFile, resolveHiddenBelowClass, resolvePreset, resolveRadiusValue, resolveSlot, resolveVariantAlias, sidebarBorderClasses, sliderVariants, stringifyNajmDesignConfig, stringifyNajmThemeConfig, stringifyThemeFile, surfaceBorderClasses, swapVariants, toPickerHex, toggleVariants, tooltipContentVariants, truncateByCharacters, useClickOutside, useContextMenu, useDebouncedValue, useDelayedLoading, useDialog, useDialogStore, useDynamicPageSize, useFormField, useFormSubmission, useInfiniteScroll, useKeyboard, useLocalStorageState, useNForm, useNPortalScope, useNajmAppearance, useNajmComponentStyle, useNajmDesign, useNajmThemeMode, usePrefix, useSelection, useStepNavigation, useStorageContextMenu, useStoreSync, useTable, useTableKeyboard, useTableStore, useVariant, useVariantPreset };
15056
+ export { Alert, NCard as AsyncCard, Avatar, AvatarFallback, AvatarFormInput, AvatarGroup, AvatarImage, AvatarInput, AvatarStatus, Badge, BaseInput, Button, Calendar, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxGroupInput, CheckboxInput, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, ColorArrayInput, ColorPickerInput, Combobox, ComboboxInput, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, DEFAULT_THEME_FILE_NAME, DateInput, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicArray_default as DynamicArray, EmojiInput, FileImportButton, FileInput, Form, FormControl, FormDescription, FormField, FormInput, FormItem, FormLabel, FormMessage, IconButton, ImageInput, Indicator2 as Indicator, Input, Label2 as Label, LangInput, MultiSelectInput, NAJM_COMPONENT_NAMES, NAlert, NAppShell, NCard as NAsyncCard, NAvatar, NBadge, NBulkActionsBar, NButton, NCard, NCardAction, NCardFooter, NCardInfo, NCardMedia, NCardSection, NCommandPalette, NConfirmDialog, NContextMenu, NDataCardShell, NDeleteDialog, NDeleteDialogContent, NDetailCard, NDetailItem, NDetailList, NDialog, NDialogDescription, NDialogHeader, NDialogPrimaryButton, NDialogSecondaryButton, NDonutCard, NEditorTabs, NEmptyState, NErrorBoundary, NErrorState, NFileBrowser, NFileTypeIcon, NFilterBar, NFolderIcon, NForm, NFormSectionHeader, NGrid, NGridItem, NIcon, NIndicator, NInspectorSheet, NLoadingState, NMultiDialog, NNavbar, NPageHeader, NPageHeaderActions, NPageHeaderCompactActions, NPageHeaderFilters, NPageHeaderTop, NPageLayout, NPortalScopeProvider, NProgress, NRowActions, NSection, NSectionHeader, NSectionInfo, NSectionWithInfo, NSheet, NSidebar, NSidebarContent, NSidebarFooter, NSidebarHeader, NSidebarItem, NSidebarLogo, NSidebarMobile, NSidebarSection, NSkeleton, NSkeletonCalendar, NSkeletonChart, NSkeletonDonut, NSkeletonEventList, NSkeletonWidget, NSkeletonWidgets, NSlider, NSmartPasteDialog, NSpinner, NStatCard, NStatCardSkeleton, Swap as NSwap, NTable, NTableCardRoot, NTableCards, NTableContent, NTableHeader, NTableJson, NTableLoadingSkeleton, NTablePagination, NTableRowSkeleton, NTableSkeleton, NTabs, NThemeCustomizer, NUploader, NViewBody, NViewToggle, NajmDesignProvider, NajmScroll, NajmThemeProvider, NativeSelect, NumberInput, OtpInput, PasswordInput, PhoneInput, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrefixProvider, Progress, RADIUS_VALUE_MAP, RadioGroup2 as RadioGroup, RadioGroupInput, RadioGroupItem, RepeatingFields, ScrollArea, SearchField, SearchField as SearchInput, SegmentedControl, Select, SelectContent, SelectGroup, SelectInput, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator3 as Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SimpleTooltip, NSkeleton as Skeleton, Slider, SliderInput, StarRatingInput, StatusPill, StepIndicator, StepsHeader, StepsProgress, Swap, SwapIndeterminate, SwapOff, SwapOn, Switch, SwitchInput, TAB_COLORS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, TableStoreContext, Tabs, TabsContent, TabsList, TabsTrigger, TextAreaInput, TextInput, Textarea, TimeInput, TimeZoneInput, Toaster, Toggle, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, VariantProvider, WizardForm, alertVariants, avatarVariants, badgeColorVariants, badgeVariants, buildDefaultFileColumns, buttonVariants, cn, composePreset, createDialogStore, createTableStore, defineNajmDesignConfig, defineNajmThemeConfig, detectFormat, dialogVariants, filterResponsiveColumns, formatColor, formatFileBytes, formatFileRelative, getIconColorProps, getNextEditorTabValue, hiddenBelowClasses, indicatorVariants, inputBorderClasses, normalizeThemeFileName, parseColor, parseNajmDesignConfig, parseNajmThemeConfig, parseThemeFile, resolveHiddenBelowClass, resolvePreset, resolveRadiusValue, resolveSlot, resolveVariantAlias, sidebarBorderClasses, sliderVariants, stringifyNajmDesignConfig, stringifyNajmThemeConfig, stringifyThemeFile, surfaceBorderClasses, swapVariants, toPickerHex, toggleVariants, tooltipContentVariants, truncateByCharacters, useClickOutside, useContextMenu, useDebouncedValue, useDelayedLoading, useDialog, useDialogStore, useDynamicPageSize, useFormField, useFormSubmission, useInfiniteScroll, useKeyboard, useLocalStorageState, useNForm, useNPortalScope, useNajmAppearance, useNajmComponentStyle, useNajmDesign, useNajmThemeMode, usePrefix, useSelection, useStepNavigation, useStorageContextMenu, useStoreSync, useTable, useTableKeyboard, useTableStore, useVariant, useVariantPreset };