najm-kit 2.11.3 → 2.11.5

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/CHANGELOG.md CHANGED
@@ -1,30 +1,44 @@
1
- # Changelog
2
-
3
- ## 2.11.3 - 2026-08-12
4
-
5
- - Fixed server-paginated card grids briefly rendering at a placeholder-derived
6
- page size before correcting to the real card height. Card mode now waits for
7
- real rows and publishes the measured fit before their first paint; fixed-row
8
- table sizing remains immediate during loading.
9
- - Fixed `NTable` replacing or clipping an explicit Rows/page choice when
10
- `dynamicHeight` was enabled. Automatic fit sizing still owns the initial page
11
- size; after the reader chooses a value, the table preserves it and scrolls
12
- its bounded body when the requested rows exceed the measured fit.
13
-
14
- ## 2.11.2 - 2026-08-11
15
-
16
- - Fixed `PasswordInput` forwarding native input attributes such as generated
17
- form IDs and ARIA metadata to the actual `<input>`, restoring programmatic
18
- label association for assistive technology and browser automation.
19
-
20
- ## 2.11.1 - 2026-08-11
21
-
22
- - Fixed `NajmThemeProvider` precedence so an explicit runtime `mode` overrides
23
- a preset stored in the design config. An explicit `preset` prop still wins,
24
- allowing deliberately fixed-preset subtrees without producing mixed light
25
- and dark application surfaces.
26
-
27
- ## 2.11.0 - 2026-08-10
1
+ # Changelog
2
+
3
+ ## 2.11.5 - 2026-08-14
4
+
5
+ - Fixed numbered lists staying on an invalid final page after a deletion or
6
+ another mutation reduced the server total. Paged lists now clamp before
7
+ slicing their row buffer, render the previous valid page immediately, and
8
+ reconcile the controlled pagination state without showing a false empty
9
+ state. A genuinely empty result still remains on page one.
10
+ - Fixed panel feedback states stretching their icon, copy, and action across
11
+ separate grid rows in tall table and dialog bodies. Panel content now stays
12
+ together in one centered vertical stack with consistent spacing.
13
+
14
+ ## 2.11.4 - 2026-08-12
15
+
16
+ - Fixed server-paginated card grids briefly rendering at a placeholder-derived
17
+ page size before correcting to the real card height. Card mode now waits for
18
+ real rows and publishes the measured fit before their first paint; fixed-row
19
+ table sizing remains immediate during loading.
20
+
21
+ ## 2.11.3 - 2026-08-12
22
+
23
+ - Fixed `NTable` replacing or clipping an explicit Rows/page choice when
24
+ `dynamicHeight` was enabled. Automatic fit sizing still owns the initial page
25
+ size; after the reader chooses a value, the table preserves it and scrolls
26
+ its bounded body when the requested rows exceed the measured fit.
27
+
28
+ ## 2.11.2 - 2026-08-11
29
+
30
+ - Fixed `PasswordInput` forwarding native input attributes such as generated
31
+ form IDs and ARIA metadata to the actual `<input>`, restoring programmatic
32
+ label association for assistive technology and browser automation.
33
+
34
+ ## 2.11.1 - 2026-08-11
35
+
36
+ - Fixed `NajmThemeProvider` precedence so an explicit runtime `mode` overrides
37
+ a preset stored in the design config. An explicit `preset` prop still wins,
38
+ allowing deliberately fixed-preset subtrees without producing mixed light
39
+ and dark application surfaces.
40
+
41
+ ## 2.11.0 - 2026-08-10
28
42
 
29
43
  - Gave every keyboard-focusable primitive a visible focus ring, from one shared
30
44
  source: `focusRingClasses` and `focusRingWithinClasses`, exported from the
package/README.md CHANGED
@@ -725,14 +725,14 @@ keyboard focus always reveals the action. Applications still decide which menu
725
725
  items exist through `menu`, `onView`, `onEdit`, and `onDelete`; visibility does
726
726
  not grant an action or replace server authorization.
727
727
 
728
- When `dynamicHeight` is enabled, table and card loading skeletons measure the
729
- available body. Table rows use the same header/row geometry as dynamic page
730
- sizing, while cards measure the active grid columns, card height, and gap. The
731
- loading surface also follows the loaded `bordered`, design recipe, radius,
732
- border color, shadow, and `classNames.content`/`classNames.cards` contract.
733
- The measured fit owns the initial page size. Once a reader explicitly chooses
734
- Rows/page, `NTable` preserves that choice and scrolls the bounded table body
735
- when the requested rows exceed the available height.
728
+ When `dynamicHeight` is enabled, table and card loading skeletons measure the
729
+ available body. Table rows use the same header/row geometry as dynamic page
730
+ sizing, while cards measure the active grid columns, card height, and gap. The
731
+ loading surface also follows the loaded `bordered`, design recipe, radius,
732
+ border color, shadow, and `classNames.content`/`classNames.cards` contract.
733
+ The measured fit owns the initial page size. Once a reader explicitly chooses
734
+ Rows/page, `NTable` preserves that choice and scrolls the bounded table body
735
+ when the requested rows exceed the available height.
736
736
 
737
737
  Use `cardPagination` to choose pagination presentation whenever the effective
738
738
  rendered mode is cards:
@@ -1,6 +1,6 @@
1
1
  'use client';
2
- import { FormDevToolsProvider, NBrandingStateProvider, NajmFormatProvider } from '../chunk-ZXIVSNXV.mjs';
3
- export { NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState } from '../chunk-ZXIVSNXV.mjs';
2
+ import { FormDevToolsProvider, NBrandingStateProvider, NajmFormatProvider } from '../chunk-A7BKZZJ4.mjs';
3
+ export { NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState } from '../chunk-A7BKZZJ4.mjs';
4
4
  import { NajmNextUIProvider } from '../chunk-T6RL7TSQ.mjs';
5
5
  import '../chunk-6UDJNTJ6.mjs';
6
6
  import '../chunk-ONE7GLIH.mjs';
@@ -184,7 +184,7 @@ var INLINE_PADDING = {
184
184
  error: "py-8",
185
185
  empty: "py-12"
186
186
  };
187
- var PANEL_BASE = "grid place-items-center min-h-64 w-full";
187
+ var PANEL_BASE = "flex min-h-64 w-full flex-col items-center justify-center gap-3";
188
188
  var PAGE_MIN = "min-h-[60vh]";
189
189
  function NFeedbackStateFrame({
190
190
  surface = "inline",
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { resolveAvatarSrc } from './chunk-BEGOH366.mjs';
2
2
  export { isPlaceholderAvatar, resolveAvatarSrc } from './chunk-BEGOH366.mjs';
3
- import { NLoadingState, NErrorState, NEmptyState, useResolvedFormDevTools, useNBranding } from './chunk-ZXIVSNXV.mjs';
4
- export { FormDevToolsProvider, NBrandingProvider, NBrandingStateProvider, NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState, NPageLayout, NSpinner, NajmFormatProvider, buildFormFill, normalizeBranding, useNBranding, useNBrandingEditor, useNajmFormat, useNajmFormatContext } from './chunk-ZXIVSNXV.mjs';
3
+ import { NLoadingState, NErrorState, NEmptyState, useResolvedFormDevTools, useNBranding } from './chunk-A7BKZZJ4.mjs';
4
+ export { FormDevToolsProvider, NBrandingProvider, NBrandingStateProvider, NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState, NPageLayout, NSpinner, NajmFormatProvider, buildFormFill, normalizeBranding, useNBranding, useNBrandingEditor, useNajmFormat, useNajmFormatContext } from './chunk-A7BKZZJ4.mjs';
5
5
  import { normalizeImageSources, useImageChain, useNBadgeDefaults, mergeBadgeMaps, resolveStatusColor, resolveBadgeStatusLabel, colorTextClass, useResolvedPaginationLabels } from './chunk-6UDJNTJ6.mjs';
6
6
  export { DEFAULT_PAGINATION_KEY_PREFIX, DEFAULT_TIME_ZONE, EMPTY_DESIGN, NAJM_COLOR_TEXT_CLASSES, NAJM_STATUS_COLORS, NTableDefaultsProvider, NajmDesignEditorProvider, NajmPreferencesProvider, NajmUIProvider, buildPaginationLabels, colorTextClass, findStatusColor, normalizeStatusToken, resolveStatusColor, statusTextClass, useNTableDefaults, useNajmDesignEditor, useNajmPreferencesContext, useNajmTheme, useNajmTimeZone } from './chunk-6UDJNTJ6.mjs';
7
7
  import { NajmScroll, useNajmScrollViewport, useTableStore, TableStoreContext, NTableJson } from './chunk-5JVFMOLI.mjs';
@@ -11887,6 +11887,9 @@ function calculateCardPageSize(input) {
11887
11887
  function shouldDeferCardPageSizeUntilRows(renderedMode, hasDataRows) {
11888
11888
  return renderedMode === "cards" && !hasDataRows;
11889
11889
  }
11890
+ function shouldCommitDynamicPageSizeImmediately(reportedMode, renderedMode) {
11891
+ return reportedMode === null || reportedMode !== renderedMode;
11892
+ }
11890
11893
  function fallbackCardColumns(width) {
11891
11894
  if (width >= 1280) return 4;
11892
11895
  if (width >= 1024) return 3;
@@ -11900,6 +11903,7 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
11900
11903
  const isLoading = useTableStore.use.isLoading();
11901
11904
  const error = useTableStore.use.error();
11902
11905
  const hasNoData = useTableStore.use.hasNoData();
11906
+ const hasDataRows = useTableStore.use.hasData();
11903
11907
  const isFilteredEmpty = useTableStore.use.isFilteredEmpty();
11904
11908
  const syncWithProps = useTableStore.use.syncWithProps();
11905
11909
  const lastMeasurementRef = useRef("");
@@ -11936,7 +11940,8 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
11936
11940
  const cardGap = Number.parseFloat(gridStyles?.rowGap || gridStyles?.gap || "") || DEFAULT_CARD_GAP;
11937
11941
  const firstCard = cardsGridEl?.querySelector("[data-ntable-loading-card], [data-row]");
11938
11942
  const cardRowHeight = firstCard?.offsetHeight || firstCard?.getBoundingClientRect().height || DEFAULT_CARD_HEIGHT;
11939
- const cardPageSize = calculateCardPageSize({
11943
+ const measuredMode = effectiveViewMode ?? viewMode;
11944
+ const cardPageSize = shouldDeferCardPageSizeUntilRows(measuredMode, hasDataRows) ? 0 : calculateCardPageSize({
11940
11945
  bodyHeight,
11941
11946
  columnCount: cardColumnCount,
11942
11947
  cardHeight: cardRowHeight,
@@ -11978,7 +11983,7 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
11978
11983
  ).forEach((el) => resizeObserver.observe(el));
11979
11984
  if (container.parentElement) resizeObserver.observe(container.parentElement);
11980
11985
  return () => resizeObserver.disconnect();
11981
- }, [dynamicHeight, effectiveViewMode, viewMode, containerRef, syncWithProps, manualPagination, isLoading, error, hasNoData, isFilteredEmpty]);
11986
+ }, [dynamicHeight, effectiveViewMode, viewMode, containerRef, syncWithProps, manualPagination, isLoading, error, hasNoData, hasDataRows, isFilteredEmpty]);
11982
11987
  }
11983
11988
  function useTable(effectiveViewModeOverride) {
11984
11989
  const [sorting, setSorting] = useState([]);
@@ -12149,18 +12154,14 @@ function useTable(effectiveViewModeOverride) {
12149
12154
  if (dynamicPageSizeTarget === storePagination.pageSize) return;
12150
12155
  if (shouldDeferCardPageSizeUntilRows(renderedMode, hasDataRows)) return;
12151
12156
  const reported = reportedGeometryRef.current;
12152
- if (reported?.key === geometryKey && reported.target === dynamicPageSizeTarget) return;
12153
- if (reported?.key === geometryKey && reported.count >= MAX_PAGE_SIZE_REPORTS_PER_GEOMETRY) return;
12157
+ if (reported?.key === geometryKey && reported.mode === renderedMode && reported.target === dynamicPageSizeTarget) return;
12158
+ if (reported?.key === geometryKey && reported.mode === renderedMode && reported.count >= MAX_PAGE_SIZE_REPORTS_PER_GEOMETRY) return;
12154
12159
  const commit = () => {
12155
12160
  const current = reportedGeometryRef.current;
12156
- reportedGeometryRef.current = current?.key === geometryKey ? { key: geometryKey, count: current.count + 1, target: dynamicPageSizeTarget } : { key: geometryKey, count: 1, target: dynamicPageSizeTarget };
12161
+ reportedGeometryRef.current = current?.key === geometryKey && current.mode === renderedMode ? { key: geometryKey, count: current.count + 1, target: dynamicPageSizeTarget, mode: renderedMode } : { key: geometryKey, count: 1, target: dynamicPageSizeTarget, mode: renderedMode };
12157
12162
  setPagination({ pageIndex: storePagination.pageIndex, pageSize: dynamicPageSizeTarget });
12158
12163
  };
12159
- if (!hasDataRows) {
12160
- commit();
12161
- return;
12162
- }
12163
- if (!reported) {
12164
+ if (shouldCommitDynamicPageSizeImmediately(reported?.mode ?? null, renderedMode)) {
12164
12165
  commit();
12165
12166
  return;
12166
12167
  }
package/dist/query.mjs CHANGED
@@ -73,7 +73,13 @@ function useResponsiveOffsetList({
73
73
  }
74
74
  }, [allDowngraded, queryIdentity, maxLimit]);
75
75
  const mode = wantsAll && !allDowngraded ? "all" : wantsInfinite || allDowngraded ? "infinite" : "paged";
76
- const start = pagination.pageIndex * pagination.pageSize;
76
+ const bufferedPages = Math.max(
77
+ 1,
78
+ Math.ceil(rows.length / pagination.pageSize)
79
+ );
80
+ const pageCount = buffer.total !== null ? Math.max(1, Math.ceil(buffer.total / pagination.pageSize)) : buffer.hasNextPage ? bufferedPages + 1 : bufferedPages;
81
+ const pageIndex = mode === "paged" ? Math.min(pagination.pageIndex, pageCount - 1) : pagination.pageIndex;
82
+ const start = pageIndex * pagination.pageSize;
77
83
  const data = mode === "paged" ? rows.slice(start, start + pagination.pageSize) : rows;
78
84
  const { fetchNextPage, hasNextPage, isFetchingNextPage } = buffer;
79
85
  useEffect(() => {
@@ -101,11 +107,14 @@ function useResponsiveOffsetList({
101
107
  },
102
108
  [queryIdentity]
103
109
  );
104
- const bufferedPages = Math.max(
105
- 1,
106
- Math.ceil(rows.length / pagination.pageSize)
107
- );
108
- const pageCount = buffer.total !== null ? Math.max(1, Math.ceil(buffer.total / pagination.pageSize)) : buffer.hasNextPage ? bufferedPages + 1 : bufferedPages;
110
+ useEffect(() => {
111
+ if (pagination.pageIndex === pageIndex) return;
112
+ setPagination((current) => ({
113
+ ...current,
114
+ pageIndex: Math.min(current.pageIndex, pageCount - 1),
115
+ queryIdentity
116
+ }));
117
+ }, [pageCount, pageIndex, pagination.pageIndex, queryIdentity]);
109
118
  return {
110
119
  cardViewport,
111
120
  mode,
@@ -123,7 +132,7 @@ function useResponsiveOffsetList({
123
132
  onPaginationChange,
124
133
  pageCount,
125
134
  pagination: {
126
- pageIndex: pagination.pageIndex,
135
+ pageIndex,
127
136
  pageSize: pagination.pageSize
128
137
  },
129
138
  refetch: buffer.refetch
package/dist/theme.css CHANGED
@@ -307,10 +307,10 @@ input:autofill {
307
307
  overflow-x: auto;
308
308
  scrollbar-width: none;
309
309
  }
310
- .najm-overlay-scroll-x::-webkit-scrollbar {
311
- display: none;
312
- }
313
-
310
+ .najm-overlay-scroll-x::-webkit-scrollbar {
311
+ display: none;
312
+ }
313
+
314
314
  /* Responsive table actions stay discoverable on touch and tablet layouts.
315
315
  Fine-pointer desktops may keep the quieter hover/focus reveal treatment. */
316
316
  .ntable-card-action {
@@ -367,7 +367,7 @@ input:autofill {
367
367
  .nimage-input-compact-overlay:focus-visible {
368
368
  opacity: 1;
369
369
  }
370
- }
370
+ }
371
371
 
372
372
  /* OverlayScrollbars theme used by the <NajmScroll> component — a thin,
373
373
  translucent slate bar that floats over content with no reserved space.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "2.11.3",
3
+ "version": "2.11.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Reusable React UI component package for Najm framework",