najm-kit 2.2.1 → 2.2.2

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.d.ts CHANGED
@@ -2871,6 +2871,8 @@ interface TableState {
2871
2871
  hasData: boolean;
2872
2872
  hasControls: boolean;
2873
2873
  hasNoData: boolean;
2874
+ /** Loading while rows are already on screen: a refresh, not a first load. */
2875
+ isRefreshing: boolean;
2874
2876
  dynamicHeight: boolean;
2875
2877
  CardComponent: ComponentType<any> | null;
2876
2878
  className: string;
@@ -2909,6 +2911,8 @@ interface TableState {
2909
2911
  calculatedPageSize: number;
2910
2912
  /** Whole card rows that fit the measured body, multiplied by the column count. */
2911
2913
  calculatedCardPageSize: number;
2914
+ /** True once a real layout measurement has replaced the seeded defaults. */
2915
+ hasMeasuredLayout: boolean;
2912
2916
  skeletonRowCount: number;
2913
2917
  maxHeight: number | null;
2914
2918
  bodyWidth: number;
@@ -2970,7 +2974,16 @@ interface TableState {
2970
2974
  hasSyncedExpandedFromProps: boolean;
2971
2975
  }
2972
2976
  type TableStore = ReturnType<typeof createTableStore>;
2973
- declare const createTableStore: () => {
2977
+ /**
2978
+ * `seed` becomes part of the store's *initial* state rather than a `set()` call
2979
+ * made after creation. Zustand serves `getInitialState()` as the snapshot for
2980
+ * server rendering and for the hydration render, so anything applied after
2981
+ * creation is invisible on the first paint: `isLoading` would read false while
2982
+ * the caller passed true (rendering the empty state instead of the skeleton),
2983
+ * and `manualPagination` would read false long enough for layout effects to
2984
+ * push a default page size back to a consumer that owns pagination.
2985
+ */
2986
+ declare const createTableStore: (seed?: Partial<TableState>) => {
2974
2987
  (): TableState;
2975
2988
  <U>(selector: (state: TableState) => U): U;
2976
2989
  } & StoreApi<TableState> & {
@@ -2998,6 +3011,7 @@ declare const createTableStore: () => {
2998
3011
  hasData: () => boolean;
2999
3012
  hasControls: () => boolean;
3000
3013
  hasNoData: () => boolean;
3014
+ isRefreshing: () => boolean;
3001
3015
  dynamicHeight: () => boolean;
3002
3016
  CardComponent: () => ComponentType<any>;
3003
3017
  className: () => string;
@@ -3035,6 +3049,7 @@ declare const createTableStore: () => {
3035
3049
  pageSizeOptions: () => number[];
3036
3050
  calculatedPageSize: () => number;
3037
3051
  calculatedCardPageSize: () => number;
3052
+ hasMeasuredLayout: () => boolean;
3038
3053
  skeletonRowCount: () => number;
3039
3054
  maxHeight: () => number;
3040
3055
  bodyWidth: () => number;
@@ -3417,6 +3432,7 @@ declare const TableStoreContext: React$1.Context<{
3417
3432
  hasData: () => boolean;
3418
3433
  hasControls: () => boolean;
3419
3434
  hasNoData: () => boolean;
3435
+ isRefreshing: () => boolean;
3420
3436
  dynamicHeight: () => boolean;
3421
3437
  CardComponent: () => React$1.ComponentType<any>;
3422
3438
  className: () => string;
@@ -3454,6 +3470,7 @@ declare const TableStoreContext: React$1.Context<{
3454
3470
  pageSizeOptions: () => number[];
3455
3471
  calculatedPageSize: () => number;
3456
3472
  calculatedCardPageSize: () => number;
3473
+ hasMeasuredLayout: () => boolean;
3457
3474
  skeletonRowCount: () => number;
3458
3475
  maxHeight: () => number;
3459
3476
  bodyWidth: () => number;
@@ -3547,6 +3564,7 @@ declare function useStoreSync(props: any): {
3547
3564
  hasData: () => boolean;
3548
3565
  hasControls: () => boolean;
3549
3566
  hasNoData: () => boolean;
3567
+ isRefreshing: () => boolean;
3550
3568
  dynamicHeight: () => boolean;
3551
3569
  CardComponent: () => React__default.ComponentType<any>;
3552
3570
  className: () => string;
@@ -3584,6 +3602,7 @@ declare function useStoreSync(props: any): {
3584
3602
  pageSizeOptions: () => number[];
3585
3603
  calculatedPageSize: () => number;
3586
3604
  calculatedCardPageSize: () => number;
3605
+ hasMeasuredLayout: () => boolean;
3587
3606
  skeletonRowCount: () => number;
3588
3607
  maxHeight: () => number;
3589
3608
  bodyWidth: () => number;
package/dist/index.mjs CHANGED
@@ -12125,8 +12125,9 @@ var computeFlags = (state) => {
12125
12125
  state.menuButton && state.openRowMenu || state.onView || state.onEdit || state.onDelete
12126
12126
  );
12127
12127
  const hasControls = Boolean(state.showColumnVisibility || state.showAddButton || state.showViewToggle);
12128
- const showContent = !state.isLoading && !state.error && !hasNoData && !isFilteredEmpty;
12129
- return { hasData, hasNoData, isTableView, isCardView, isJsonView, isFilesView, isCustomMode, hasActions, hasControls, showContent, showPagination: state.showPagination && showContent };
12128
+ const isRefreshing = Boolean(state.isLoading) && hasData;
12129
+ const showContent = !state.error && !hasNoData && !isFilteredEmpty && (!state.isLoading || hasData);
12130
+ return { hasData, hasNoData, isTableView, isCardView, isJsonView, isFilesView, isCustomMode, hasActions, hasControls, isRefreshing, showContent, showPagination: state.showPagination && showContent };
12130
12131
  };
12131
12132
  var createSelectors = (_store) => {
12132
12133
  const store = _store;
@@ -12136,177 +12137,192 @@ var createSelectors = (_store) => {
12136
12137
  }
12137
12138
  return store;
12138
12139
  };
12139
- var createTableStore = () => {
12140
- const store = create((set, get) => ({
12141
- table: null,
12142
- data: [],
12143
- columns: [],
12144
- filters: [],
12145
- isLoading: false,
12146
- error: null,
12147
- viewMode: "table",
12148
- showSorting: true,
12149
- showPagination: true,
12150
- showColumnVisibility: false,
12151
- showAddButton: true,
12152
- showViewToggle: true,
12153
- toolbarLabels: true,
12154
- dynamicHeight: true,
12155
- showContent: false,
12156
- isTableView: true,
12157
- isCardView: false,
12158
- isJsonView: false,
12159
- isFilesView: false,
12160
- isCustomMode: false,
12161
- hasActions: false,
12162
- hasData: false,
12163
- hasControls: true,
12164
- hasNoData: true,
12165
- onView: null,
12166
- onEdit: null,
12167
- onDelete: null,
12168
- onAddClick: null,
12169
- onRowClick: null,
12170
- onRowContextMenu: null,
12171
- onBackgroundContextMenu: null,
12172
- openRowMenu: null,
12173
- getRowClassName: null,
12174
- menuButton: false,
12175
- onCellClick: null,
12176
- onBulkDelete: null,
12177
- onRetry: null,
12178
- onCellEdit: null,
12179
- onStateChange: null,
12180
- getRowId: null,
12181
- renderToolbar: null,
12182
- CardComponent: null,
12183
- className: "",
12184
- classNames: {},
12185
- bordered: void 0,
12186
- headerClassName: "bg-card",
12187
- headerColor: void 0,
12188
- headerTextColor: void 0,
12189
- borderColor: void 0,
12190
- showCheckbox: true,
12191
- selectedRowId: null,
12192
- headerSlot: null,
12193
- noResultsText: "No results.",
12194
- filterPlaceholder: "",
12195
- loadingText: "Loading...",
12196
- noDataText: "No data available",
12197
- addButtonText: "",
12198
- pageSizeOptions: [10, 20, 30, 40, 50],
12199
- calculatedPageSize: 10,
12200
- calculatedCardPageSize: 0,
12201
- skeletonRowCount: 6,
12202
- maxHeight: null,
12203
- bodyWidth: 0,
12204
- bodyHeight: 0,
12205
- tableHeaderHeight: 48,
12206
- cardColumnCount: 1,
12207
- cardRowHeight: 0,
12208
- cardGap: 12,
12209
- // JSON mode
12210
- jsonValue: void 0,
12211
- jsonColors: null,
12212
- renderJson: null,
12213
- // Custom mode
12214
- renderCustomMode: null,
12215
- // Controlled mode
12216
- isModeControlled: false,
12217
- onModeChange: null,
12218
- // availableModes
12219
- availableModes: ["table", "cards", "json"],
12220
- // User-intent setter
12221
- setViewMode: (mode) => {
12222
- const { isModeControlled, onModeChange } = get();
12223
- onModeChange?.(mode);
12224
- if (!isModeControlled) {
12225
- const updates = { viewMode: mode };
12140
+ var createTableStore = (seed) => {
12141
+ const store = create((set, get) => {
12142
+ const defaults = {
12143
+ table: null,
12144
+ data: [],
12145
+ columns: [],
12146
+ filters: [],
12147
+ isLoading: false,
12148
+ error: null,
12149
+ viewMode: "table",
12150
+ showSorting: true,
12151
+ showPagination: true,
12152
+ showColumnVisibility: false,
12153
+ showAddButton: true,
12154
+ showViewToggle: true,
12155
+ toolbarLabels: true,
12156
+ dynamicHeight: true,
12157
+ showContent: false,
12158
+ isTableView: true,
12159
+ isCardView: false,
12160
+ isJsonView: false,
12161
+ isFilesView: false,
12162
+ isCustomMode: false,
12163
+ hasActions: false,
12164
+ hasData: false,
12165
+ hasControls: true,
12166
+ hasNoData: true,
12167
+ isRefreshing: false,
12168
+ onView: null,
12169
+ onEdit: null,
12170
+ onDelete: null,
12171
+ onAddClick: null,
12172
+ onRowClick: null,
12173
+ onRowContextMenu: null,
12174
+ onBackgroundContextMenu: null,
12175
+ openRowMenu: null,
12176
+ getRowClassName: null,
12177
+ menuButton: false,
12178
+ onCellClick: null,
12179
+ onBulkDelete: null,
12180
+ onRetry: null,
12181
+ onCellEdit: null,
12182
+ onStateChange: null,
12183
+ getRowId: null,
12184
+ renderToolbar: null,
12185
+ CardComponent: null,
12186
+ className: "",
12187
+ classNames: {},
12188
+ bordered: void 0,
12189
+ headerClassName: "bg-card",
12190
+ headerColor: void 0,
12191
+ headerTextColor: void 0,
12192
+ borderColor: void 0,
12193
+ showCheckbox: true,
12194
+ selectedRowId: null,
12195
+ headerSlot: null,
12196
+ noResultsText: "No results.",
12197
+ filterPlaceholder: "",
12198
+ loadingText: "Loading...",
12199
+ noDataText: "No data available",
12200
+ addButtonText: "",
12201
+ pageSizeOptions: [10, 20, 30, 40, 50],
12202
+ calculatedPageSize: 10,
12203
+ calculatedCardPageSize: 0,
12204
+ hasMeasuredLayout: false,
12205
+ skeletonRowCount: 6,
12206
+ maxHeight: null,
12207
+ bodyWidth: 0,
12208
+ bodyHeight: 0,
12209
+ tableHeaderHeight: 48,
12210
+ cardColumnCount: 1,
12211
+ cardRowHeight: 0,
12212
+ cardGap: 12,
12213
+ // JSON mode
12214
+ jsonValue: void 0,
12215
+ jsonColors: null,
12216
+ renderJson: null,
12217
+ // Custom mode
12218
+ renderCustomMode: null,
12219
+ // Controlled mode
12220
+ isModeControlled: false,
12221
+ onModeChange: null,
12222
+ // availableModes
12223
+ availableModes: ["table", "cards", "json"],
12224
+ // User-intent setter
12225
+ setViewMode: (mode) => {
12226
+ const { isModeControlled, onModeChange } = get();
12227
+ onModeChange?.(mode);
12228
+ if (!isModeControlled) {
12229
+ const updates = { viewMode: mode };
12230
+ const currentState = get();
12231
+ const mergedState = { ...currentState, ...updates };
12232
+ const flags = computeFlags(mergedState);
12233
+ set({ ...updates, ...flags });
12234
+ }
12235
+ },
12236
+ // Track if we've ever synced viewMode from props
12237
+ hasSyncedFromProps: false,
12238
+ // Server-side pagination
12239
+ manualPagination: false,
12240
+ pageCount: void 0,
12241
+ rowCount: void 0,
12242
+ pagination: { pageIndex: 0, pageSize: 10 },
12243
+ isPaginationControlled: false,
12244
+ onPaginationChange: null,
12245
+ // User-intent setter for pagination
12246
+ setPagination: (pagination) => {
12247
+ const { isPaginationControlled, onPaginationChange } = get();
12248
+ onPaginationChange?.(pagination);
12249
+ if (!isPaginationControlled) {
12250
+ set({ pagination });
12251
+ }
12252
+ },
12253
+ // Track if we've ever synced pagination from props
12254
+ hasSyncedPaginationFromProps: false,
12255
+ // Row selection
12256
+ rowSelection: {},
12257
+ isRowSelectionControlled: false,
12258
+ onRowSelectionChange: null,
12259
+ setRowSelection: (state) => {
12260
+ const { isRowSelectionControlled, onRowSelectionChange } = get();
12261
+ onRowSelectionChange?.(state);
12262
+ if (!isRowSelectionControlled) {
12263
+ set({ rowSelection: state });
12264
+ }
12265
+ },
12266
+ hasSyncedRowSelectionFromProps: false,
12267
+ // Sorting
12268
+ sorting: [],
12269
+ isSortingControlled: false,
12270
+ onSortingChange: null,
12271
+ setSorting: (state) => {
12272
+ const { isSortingControlled, onSortingChange } = get();
12273
+ onSortingChange?.(state);
12274
+ if (!isSortingControlled) {
12275
+ set({ sorting: state });
12276
+ }
12277
+ },
12278
+ hasSyncedSortingFromProps: false,
12279
+ // Responsive cards
12280
+ responsiveCards: true,
12281
+ isMobile: false,
12282
+ effectiveViewMode: "table",
12283
+ cardPagination: { mode: "paged" },
12284
+ // Empty states
12285
+ isEmpty: void 0,
12286
+ isFilteredEmpty: false,
12287
+ renderFilteredEmpty: null,
12288
+ // Row expansion
12289
+ expanded: {},
12290
+ isExpandedControlled: false,
12291
+ onExpandedChange: null,
12292
+ getRowCanExpand: null,
12293
+ renderSubRow: null,
12294
+ setExpanded: (next) => {
12295
+ const { isExpandedControlled, onExpandedChange } = get();
12296
+ onExpandedChange?.(next);
12297
+ if (!isExpandedControlled) {
12298
+ set({ expanded: next });
12299
+ }
12300
+ },
12301
+ hasSyncedExpandedFromProps: false,
12302
+ syncWithProps: (updates) => {
12226
12303
  const currentState = get();
12227
- const mergedState = { ...currentState, ...updates };
12304
+ const hasSyncedFromProps = currentState.hasSyncedFromProps || "viewMode" in updates;
12305
+ const hasSyncedPaginationFromProps = currentState.hasSyncedPaginationFromProps || "pagination" in updates;
12306
+ const hasSyncedRowSelectionFromProps = currentState.hasSyncedRowSelectionFromProps || "rowSelection" in updates;
12307
+ const hasSyncedExpandedFromProps = currentState.hasSyncedExpandedFromProps || "expanded" in updates;
12308
+ const hasSyncedSortingFromProps = currentState.hasSyncedSortingFromProps || "sorting" in updates;
12309
+ const mergedState = { ...currentState, ...updates};
12228
12310
  const flags = computeFlags(mergedState);
12229
- set({ ...updates, ...flags });
12311
+ set({ ...updates, ...flags, hasSyncedFromProps, hasSyncedPaginationFromProps, hasSyncedRowSelectionFromProps, hasSyncedExpandedFromProps, hasSyncedSortingFromProps });
12230
12312
  }
12231
- },
12232
- // Track if we've ever synced viewMode from props
12233
- hasSyncedFromProps: false,
12234
- // Server-side pagination
12235
- manualPagination: false,
12236
- pageCount: void 0,
12237
- rowCount: void 0,
12238
- pagination: { pageIndex: 0, pageSize: 10 },
12239
- isPaginationControlled: false,
12240
- onPaginationChange: null,
12241
- // User-intent setter for pagination
12242
- setPagination: (pagination) => {
12243
- const { isPaginationControlled, onPaginationChange } = get();
12244
- onPaginationChange?.(pagination);
12245
- if (!isPaginationControlled) {
12246
- set({ pagination });
12247
- }
12248
- },
12249
- // Track if we've ever synced pagination from props
12250
- hasSyncedPaginationFromProps: false,
12251
- // Row selection
12252
- rowSelection: {},
12253
- isRowSelectionControlled: false,
12254
- onRowSelectionChange: null,
12255
- setRowSelection: (state) => {
12256
- const { isRowSelectionControlled, onRowSelectionChange } = get();
12257
- onRowSelectionChange?.(state);
12258
- if (!isRowSelectionControlled) {
12259
- set({ rowSelection: state });
12260
- }
12261
- },
12262
- hasSyncedRowSelectionFromProps: false,
12263
- // Sorting
12264
- sorting: [],
12265
- isSortingControlled: false,
12266
- onSortingChange: null,
12267
- setSorting: (state) => {
12268
- const { isSortingControlled, onSortingChange } = get();
12269
- onSortingChange?.(state);
12270
- if (!isSortingControlled) {
12271
- set({ sorting: state });
12272
- }
12273
- },
12274
- hasSyncedSortingFromProps: false,
12275
- // Responsive cards
12276
- responsiveCards: true,
12277
- isMobile: false,
12278
- effectiveViewMode: "table",
12279
- cardPagination: { mode: "paged" },
12280
- // Empty states
12281
- isEmpty: void 0,
12282
- isFilteredEmpty: false,
12283
- renderFilteredEmpty: null,
12284
- // Row expansion
12285
- expanded: {},
12286
- isExpandedControlled: false,
12287
- onExpandedChange: null,
12288
- getRowCanExpand: null,
12289
- renderSubRow: null,
12290
- setExpanded: (next) => {
12291
- const { isExpandedControlled, onExpandedChange } = get();
12292
- onExpandedChange?.(next);
12293
- if (!isExpandedControlled) {
12294
- set({ expanded: next });
12295
- }
12296
- },
12297
- hasSyncedExpandedFromProps: false,
12298
- syncWithProps: (updates) => {
12299
- const currentState = get();
12300
- const hasSyncedFromProps = currentState.hasSyncedFromProps || "viewMode" in updates;
12301
- const hasSyncedPaginationFromProps = currentState.hasSyncedPaginationFromProps || "pagination" in updates;
12302
- const hasSyncedRowSelectionFromProps = currentState.hasSyncedRowSelectionFromProps || "rowSelection" in updates;
12303
- const hasSyncedExpandedFromProps = currentState.hasSyncedExpandedFromProps || "expanded" in updates;
12304
- const hasSyncedSortingFromProps = currentState.hasSyncedSortingFromProps || "sorting" in updates;
12305
- const mergedState = { ...currentState, ...updates};
12306
- const flags = computeFlags(mergedState);
12307
- set({ ...updates, ...flags, hasSyncedFromProps, hasSyncedPaginationFromProps, hasSyncedRowSelectionFromProps, hasSyncedExpandedFromProps, hasSyncedSortingFromProps });
12308
- }
12309
- }));
12313
+ };
12314
+ if (!seed) return defaults;
12315
+ const merged = {
12316
+ ...defaults,
12317
+ ...seed,
12318
+ hasSyncedFromProps: "viewMode" in seed,
12319
+ hasSyncedPaginationFromProps: "pagination" in seed,
12320
+ hasSyncedRowSelectionFromProps: "rowSelection" in seed,
12321
+ hasSyncedExpandedFromProps: "expanded" in seed,
12322
+ hasSyncedSortingFromProps: "sorting" in seed
12323
+ };
12324
+ return { ...merged, ...computeFlags(merged) };
12325
+ });
12310
12326
  return createSelectors(store);
12311
12327
  };
12312
12328
 
@@ -12355,7 +12371,9 @@ var DEFAULT_TABLE_HEADER_HEIGHT = 48;
12355
12371
  var DEFAULT_CARD_HEIGHT = 176;
12356
12372
  var DEFAULT_CARD_GAP = 12;
12357
12373
  var ROOT_SECTION_GAP_COUNT = 2;
12358
- var DYNAMIC_PAGE_SIZE_DEBOUNCE_MS = 200;
12374
+ var DYNAMIC_PAGE_SIZE_DEBOUNCE_MS = 800;
12375
+ var MAX_PAGE_SIZE_REPORTS_PER_GEOMETRY = 2;
12376
+ var GEOMETRY_BUCKET_PX = 32;
12359
12377
  function useStoreSync(props) {
12360
12378
  const storeRef = useRef(null);
12361
12379
  const isControlled = props.mode !== void 0;
@@ -12364,7 +12382,6 @@ function useStoreSync(props) {
12364
12382
  const isExpandedControlled = props.expanded !== void 0;
12365
12383
  const isSortingControlled = props.sorting !== void 0;
12366
12384
  if (!storeRef.current) {
12367
- storeRef.current = createTableStore();
12368
12385
  const syncSnapshot = { ...props, isModeControlled: isControlled, isPaginationControlled, isRowSelectionControlled, isExpandedControlled, isSortingControlled };
12369
12386
  delete syncSnapshot.pagination;
12370
12387
  delete syncSnapshot.defaultPagination;
@@ -12382,7 +12399,7 @@ function useStoreSync(props) {
12382
12399
  delete syncSnapshot.defaultSorting;
12383
12400
  if (props.sorting !== void 0) syncSnapshot.sorting = props.sorting;
12384
12401
  else if (props.defaultSorting !== void 0) syncSnapshot.sorting = props.defaultSorting;
12385
- storeRef.current.getState().syncWithProps(syncSnapshot);
12402
+ storeRef.current = createTableStore(syncSnapshot);
12386
12403
  }
12387
12404
  useLayoutEffect(() => {
12388
12405
  const syncData = { ...props };
@@ -12515,6 +12532,9 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
12515
12532
  // mutating the table directly, so the consumer still owns fetching.
12516
12533
  calculatedPageSize: newPageSize,
12517
12534
  calculatedCardPageSize: cardPageSize,
12535
+ // Distinguishes a real measurement from the seeded defaults, so a page
12536
+ // size is never reported before the container has been measured.
12537
+ hasMeasuredLayout: bodyHeight > 0,
12518
12538
  ...!manualPagination ? { maxHeight: calculatedMaxHeight } : {},
12519
12539
  skeletonRowCount: newPageSize,
12520
12540
  bodyWidth,
@@ -12567,6 +12587,7 @@ function useTable(effectiveViewModeOverride) {
12567
12587
  const calculatedCardPageSize = useTableStore.use.calculatedCardPageSize();
12568
12588
  const measuredBodyHeight = useTableStore.use.bodyHeight();
12569
12589
  const measuredBodyWidth = useTableStore.use.bodyWidth();
12590
+ const hasMeasuredLayout = useTableStore.use.hasMeasuredLayout();
12570
12591
  const syncWithProps = useTableStore.use.syncWithProps();
12571
12592
  const onStateChange = useTableStore.use.onStateChange();
12572
12593
  const getRowId = useTableStore.use.getRowId();
@@ -12574,6 +12595,7 @@ function useTable(effectiveViewModeOverride) {
12574
12595
  const pageCount = useTableStore.use.pageCount();
12575
12596
  const rowCount = useTableStore.use.rowCount();
12576
12597
  const storePagination = useTableStore.use.pagination();
12598
+ const isPaginationControlled = useTableStore.use.isPaginationControlled();
12577
12599
  const setPagination = useTableStore.use.setPagination();
12578
12600
  const storeRowSelection = useTableStore.use.rowSelection();
12579
12601
  const setRowSelection = useTableStore.use.setRowSelection();
@@ -12687,24 +12709,26 @@ function useTable(effectiveViewModeOverride) {
12687
12709
  syncWithProps({ table });
12688
12710
  }, [table]);
12689
12711
  useLayoutEffect(() => {
12690
- if (manualPagination) return;
12712
+ if (manualPagination || isPaginationControlled) return;
12691
12713
  if (dynamicHeight && renderedMode === "table") table.setPageSize(calculatedPageSize);
12692
12714
  if (viewMode === "cards" && cardPagination.mode === "paged") {
12693
12715
  table.setPageSize(data.length || 9999);
12694
12716
  }
12695
- }, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination]);
12717
+ }, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled]);
12696
12718
  const dynamicPageSizeTarget = renderedMode === "cards" ? calculatedCardPageSize : calculatedPageSize;
12697
- const geometryKey = `${renderedMode}:${measuredBodyWidth}x${measuredBodyHeight}`;
12719
+ const geometryKey = `${Math.round(measuredBodyWidth / GEOMETRY_BUCKET_PX)}x${Math.round(measuredBodyHeight / GEOMETRY_BUCKET_PX)}`;
12698
12720
  const reportedGeometryRef = useRef(null);
12699
12721
  useEffect(() => {
12700
12722
  if (!manualPagination || !dynamicHeight) return;
12701
12723
  if (cardPagination.mode !== "paged") return;
12702
- if (measuredBodyHeight <= 0) return;
12724
+ if (!hasMeasuredLayout || measuredBodyHeight <= 0) return;
12703
12725
  if (!dynamicPageSizeTarget || dynamicPageSizeTarget < 1) return;
12704
12726
  if (dynamicPageSizeTarget === storePagination.pageSize) return;
12705
- if (reportedGeometryRef.current === geometryKey) return;
12727
+ const reported = reportedGeometryRef.current;
12728
+ if (reported?.key === geometryKey && reported.count >= MAX_PAGE_SIZE_REPORTS_PER_GEOMETRY) return;
12706
12729
  const timer = setTimeout(() => {
12707
- reportedGeometryRef.current = geometryKey;
12730
+ const current = reportedGeometryRef.current;
12731
+ reportedGeometryRef.current = current?.key === geometryKey ? { key: geometryKey, count: current.count + 1 } : { key: geometryKey, count: 1 };
12708
12732
  setPagination({ pageIndex: storePagination.pageIndex, pageSize: dynamicPageSizeTarget });
12709
12733
  }, DYNAMIC_PAGE_SIZE_DEBOUNCE_MS);
12710
12734
  return () => clearTimeout(timer);
@@ -12712,6 +12736,7 @@ function useTable(effectiveViewModeOverride) {
12712
12736
  manualPagination,
12713
12737
  dynamicHeight,
12714
12738
  cardPagination.mode,
12739
+ hasMeasuredLayout,
12715
12740
  measuredBodyHeight,
12716
12741
  geometryKey,
12717
12742
  dynamicPageSizeTarget,
@@ -12908,7 +12933,7 @@ function NTableContent({ effectiveMode }) {
12908
12933
  const onBackgroundContextMenu = useTableStore.use.onBackgroundContextMenu();
12909
12934
  const getRowClassName = useTableStore.use.getRowClassName();
12910
12935
  const onCellEdit = useTableStore.use.onCellEdit();
12911
- const isLoading = useTableStore.use.isLoading();
12936
+ useTableStore.use.isLoading();
12912
12937
  const error = useTableStore.use.error();
12913
12938
  const hasNoData = useTableStore.use.hasNoData();
12914
12939
  const showContent = useTableStore.use.showContent();
@@ -12928,7 +12953,7 @@ function NTableContent({ effectiveMode }) {
12928
12953
  onBackgroundContextMenu(e);
12929
12954
  }
12930
12955
  }, [onBackgroundContextMenu]);
12931
- if (isLoading || error || hasNoData || !showContent || !table) return null;
12956
+ if (error || hasNoData || !showContent || !table) return null;
12932
12957
  const isTableView = effectiveMode ? effectiveMode === "table" : storeIsTableView;
12933
12958
  if (!isTableView) return null;
12934
12959
  const getSortIcon = (column) => {
@@ -13182,7 +13207,7 @@ function NDataCardShell({ row, onClick, onContextMenu, actions, children, classN
13182
13207
  );
13183
13208
  }
13184
13209
  var DEFAULT_ROWS2 = 6;
13185
- var DEFAULT_CARD_COUNT = 16;
13210
+ var DEFAULT_CARD_COUNT = 48;
13186
13211
  function NTableCardSkeleton({ surface }) {
13187
13212
  return /* @__PURE__ */ jsx(
13188
13213
  "div",
@@ -13485,13 +13510,13 @@ function useCardContinuation({
13485
13510
  });
13486
13511
  React__default.useEffect(() => {
13487
13512
  const previous = previousRowCountRef.current;
13488
- if (rowCount > previous) {
13489
- const appended = rowCount - previous;
13490
- setAnnouncement(
13491
- configRef.current?.itemsLoadedLabel?.(appended) ?? `${appended} more ${appended === 1 ? "item" : "items"} loaded.`
13492
- );
13493
- }
13494
13513
  previousRowCountRef.current = rowCount;
13514
+ if (!configRef.current) return;
13515
+ if (rowCount <= previous) return;
13516
+ const appended = rowCount - previous;
13517
+ setAnnouncement(
13518
+ configRef.current.itemsLoadedLabel?.(appended) ?? `${appended} more ${appended === 1 ? "item" : "items"} loaded.`
13519
+ );
13495
13520
  }, [rowCount]);
13496
13521
  React__default.useEffect(() => {
13497
13522
  if (!pending) doneLoading();
@@ -13537,7 +13562,7 @@ function NTableCards({ effectiveMode }) {
13537
13562
  const selectedRowId = useTableStore.use.selectedRowId();
13538
13563
  const CardComponent = useTableStore.use.CardComponent();
13539
13564
  const storeIsCardView = useTableStore.use.isCardView();
13540
- const isLoading = useTableStore.use.isLoading();
13565
+ useTableStore.use.isLoading();
13541
13566
  const error = useTableStore.use.error();
13542
13567
  const hasNoData = useTableStore.use.hasNoData();
13543
13568
  const showContent = useTableStore.use.showContent();
@@ -13583,7 +13608,7 @@ function NTableCards({ effectiveMode }) {
13583
13608
  onBackgroundContextMenu(e);
13584
13609
  }
13585
13610
  }, [table, onRowContextMenu, onBackgroundContextMenu]);
13586
- if (isLoading || error || hasNoData || !showContent || !table) return null;
13611
+ if (error || hasNoData || !showContent || !table) return null;
13587
13612
  const isCardView = effectiveMode ? effectiveMode === "cards" : storeIsCardView;
13588
13613
  if (!isCardView) return null;
13589
13614
  const rows = table.getRowModel().rows;
@@ -13702,7 +13727,16 @@ function NTableCards({ effectiveMode }) {
13702
13727
  }
13703
13728
  ) : null,
13704
13729
  continuation.active ? /* @__PURE__ */ jsx("div", { ref: continuation.sentinelRef, "data-ntable-cards-sentinel": true, "aria-hidden": "true" }) : null,
13705
- /* @__PURE__ */ jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: continuation.announcement })
13730
+ continuationConfig ? /* @__PURE__ */ jsx(
13731
+ "span",
13732
+ {
13733
+ role: "status",
13734
+ "aria-live": "polite",
13735
+ "aria-atomic": "true",
13736
+ className: "sr-only absolute -m-px h-px w-px overflow-hidden border-0 p-0 whitespace-nowrap [clip:rect(0,0,0,0)]",
13737
+ children: continuation.announcement
13738
+ }
13739
+ ) : null
13706
13740
  ] });
13707
13741
  }
13708
13742
  function CardLoadMorePagination({
@@ -14140,6 +14174,7 @@ function TableToolbarSlot() {
14140
14174
  function NTableHeader() {
14141
14175
  const hasControls = useTableStore.use.hasControls();
14142
14176
  const isLoading = useTableStore.use.isLoading();
14177
+ const isRefreshing = useTableStore.use.isRefreshing();
14143
14178
  const error = useTableStore.use.error();
14144
14179
  const hasNoData = useTableStore.use.hasNoData();
14145
14180
  const isFilteredEmpty = useTableStore.use.isFilteredEmpty();
@@ -14150,7 +14185,7 @@ function NTableHeader() {
14150
14185
  const isCustomMode = useTableStore.use.isCustomMode();
14151
14186
  const showViewToggle = useTableStore.use.showViewToggle();
14152
14187
  const showColumnVisibility = useTableStore.use.showColumnVisibility();
14153
- const hideDataChrome = isLoading || error || hasNoData && !isFilteredEmpty;
14188
+ const hideDataChrome = isLoading && !isRefreshing || error || hasNoData && !isFilteredEmpty;
14154
14189
  if (isCustomMode) {
14155
14190
  if (!showViewToggle && !showColumnVisibility && !headerSlot && !hasControls) return null;
14156
14191
  if (hideDataChrome) return null;
@@ -14232,6 +14267,7 @@ function TableLayout(props) {
14232
14267
  const className = useTableStore.use.className();
14233
14268
  useTableStore.use.dynamicHeight();
14234
14269
  const isLoading = useTableStore.use.isLoading();
14270
+ const isRefreshing = useTableStore.use.isRefreshing();
14235
14271
  const error = useTableStore.use.error();
14236
14272
  const hasNoData = useTableStore.use.hasNoData();
14237
14273
  const noDataText = useTableStore.use.noDataText();
@@ -14272,15 +14308,24 @@ function TableLayout(props) {
14272
14308
  const customRenderer = isCustomMode ? renderCustomMode?.[viewMode] : void 0;
14273
14309
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, "data-ntable-root": true, className: cn("flex h-full min-h-0 flex-1 w-full flex-col gap-2 overflow-hidden", classNames?.root, className), children: [
14274
14310
  /* @__PURE__ */ jsx(NTableHeader, {}),
14275
- /* @__PURE__ */ jsx("div", { "data-ntable-body": true, className: "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden", children: isCustomMode ? customRenderer ? customRenderer() : null : /* @__PURE__ */ jsxs(Fragment, { children: [
14276
- isLoading && (props.renderLoading ? /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderLoading() }) : effectiveMode === "table" ? /* @__PURE__ */ jsx(NTableLoadingSkeleton, {}) : effectiveMode === "cards" ? /* @__PURE__ */ jsx(NTableCardsLoadingSkeleton, {}) : /* @__PURE__ */ jsx(NTableLoadingSkeleton, {})),
14277
- error && !isLoading && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderError ? props.renderError(error) : /* @__PURE__ */ jsx(NErrorState, { message: typeof error === "string" ? error : "An error occurred" }) }),
14278
- showFilteredEmpty && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderFilteredEmpty ? props.renderFilteredEmpty() : renderFilteredEmpty ? renderFilteredEmpty() : props.renderEmpty ? props.renderEmpty() : /* @__PURE__ */ jsx(DefaultTableFilteredEmptyState, {}) }),
14279
- showEmpty && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderEmpty ? props.renderEmpty() : /* @__PURE__ */ jsx(DefaultTableEmptyState, { title: noDataText }) }),
14280
- /* @__PURE__ */ jsx(NTableContent, { effectiveMode }),
14281
- /* @__PURE__ */ jsx(NTableCards, { effectiveMode }),
14282
- /* @__PURE__ */ jsx(NTableJson, {})
14283
- ] }) }),
14311
+ /* @__PURE__ */ jsx(
14312
+ "div",
14313
+ {
14314
+ "data-ntable-body": true,
14315
+ "data-ntable-refreshing": isRefreshing ? "true" : void 0,
14316
+ "aria-busy": isRefreshing ? "true" : void 0,
14317
+ className: "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden",
14318
+ children: isCustomMode ? customRenderer ? customRenderer() : null : /* @__PURE__ */ jsxs(Fragment, { children: [
14319
+ isLoading && !isRefreshing && (props.renderLoading ? /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderLoading() }) : effectiveMode === "table" ? /* @__PURE__ */ jsx(NTableLoadingSkeleton, {}) : effectiveMode === "cards" ? /* @__PURE__ */ jsx(NTableCardsLoadingSkeleton, {}) : /* @__PURE__ */ jsx(NTableLoadingSkeleton, {})),
14320
+ error && !isLoading && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderError ? props.renderError(error) : /* @__PURE__ */ jsx(NErrorState, { message: typeof error === "string" ? error : "An error occurred" }) }),
14321
+ showFilteredEmpty && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderFilteredEmpty ? props.renderFilteredEmpty() : renderFilteredEmpty ? renderFilteredEmpty() : props.renderEmpty ? props.renderEmpty() : /* @__PURE__ */ jsx(DefaultTableFilteredEmptyState, {}) }),
14322
+ showEmpty && /* @__PURE__ */ jsx(TableStateSlot, { children: props.renderEmpty ? props.renderEmpty() : /* @__PURE__ */ jsx(DefaultTableEmptyState, { title: noDataText }) }),
14323
+ /* @__PURE__ */ jsx(NTableContent, { effectiveMode }),
14324
+ /* @__PURE__ */ jsx(NTableCards, { effectiveMode }),
14325
+ /* @__PURE__ */ jsx(NTableJson, {})
14326
+ ] })
14327
+ }
14328
+ ),
14284
14329
  /* @__PURE__ */ jsx("div", { "data-ntable-pagination": true, className: "min-w-0 shrink-0 bg-background text-foreground", children: /* @__PURE__ */ jsx(NTablePagination, {}) })
14285
14330
  ] });
14286
14331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Reusable React UI component package for Najm framework",