najm-kit 2.11.2 → 2.11.4
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 +30 -16
- package/README.md +3 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +42 -17
- package/dist/theme.css +5 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 2.11.
|
|
4
|
-
|
|
5
|
-
- Fixed
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.11.4 - 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
|
+
|
|
10
|
+
## 2.11.3 - 2026-08-12
|
|
11
|
+
|
|
12
|
+
- Fixed `NTable` replacing or clipping an explicit Rows/page choice when
|
|
13
|
+
`dynamicHeight` was enabled. Automatic fit sizing still owns the initial page
|
|
14
|
+
size; after the reader chooses a value, the table preserves it and scrolls
|
|
15
|
+
its bounded body when the requested rows exceed the measured fit.
|
|
16
|
+
|
|
17
|
+
## 2.11.2 - 2026-08-11
|
|
18
|
+
|
|
19
|
+
- Fixed `PasswordInput` forwarding native input attributes such as generated
|
|
20
|
+
form IDs and ARIA metadata to the actual `<input>`, restoring programmatic
|
|
21
|
+
label association for assistive technology and browser automation.
|
|
22
|
+
|
|
23
|
+
## 2.11.1 - 2026-08-11
|
|
24
|
+
|
|
25
|
+
- Fixed `NajmThemeProvider` precedence so an explicit runtime `mode` overrides
|
|
26
|
+
a preset stored in the design config. An explicit `preset` prop still wins,
|
|
27
|
+
allowing deliberately fixed-preset subtrees without producing mixed light
|
|
28
|
+
and dark application surfaces.
|
|
29
|
+
|
|
30
|
+
## 2.11.0 - 2026-08-10
|
|
17
31
|
|
|
18
32
|
- Gave every keyboard-focusable primitive a visible focus ring, from one shared
|
|
19
33
|
source: `focusRingClasses` and `focusRingWithinClasses`, exported from the
|
package/README.md
CHANGED
|
@@ -730,6 +730,9 @@ available body. Table rows use the same header/row geometry as dynamic page
|
|
|
730
730
|
sizing, while cards measure the active grid columns, card height, and gap. The
|
|
731
731
|
loading surface also follows the loaded `bordered`, design recipe, radius,
|
|
732
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.
|
|
733
736
|
|
|
734
737
|
Use `cardPagination` to choose pagination presentation whenever the effective
|
|
735
738
|
rendered mode is cards:
|
package/dist/index.d.ts
CHANGED
|
@@ -2969,10 +2969,13 @@ interface TableState {
|
|
|
2969
2969
|
pageIndex: number;
|
|
2970
2970
|
pageSize: number;
|
|
2971
2971
|
}) => void) | null;
|
|
2972
|
+
/** True after the reader explicitly chooses a Rows/page value. */
|
|
2973
|
+
isPageSizeUserSelected: boolean;
|
|
2972
2974
|
setPagination: (pagination: {
|
|
2973
2975
|
pageIndex: number;
|
|
2974
2976
|
pageSize: number;
|
|
2975
2977
|
}) => void;
|
|
2978
|
+
setPageSizeFromUser: (pageSize: number) => void;
|
|
2976
2979
|
hasSyncedPaginationFromProps: boolean;
|
|
2977
2980
|
rowSelection: Record<string, boolean>;
|
|
2978
2981
|
isRowSelectionControlled: boolean;
|
|
@@ -3110,10 +3113,12 @@ declare const createTableStore: (seed?: Partial<TableState>) => {
|
|
|
3110
3113
|
pageIndex: number;
|
|
3111
3114
|
pageSize: number;
|
|
3112
3115
|
}) => void;
|
|
3116
|
+
isPageSizeUserSelected: () => boolean;
|
|
3113
3117
|
setPagination: () => (pagination: {
|
|
3114
3118
|
pageIndex: number;
|
|
3115
3119
|
pageSize: number;
|
|
3116
3120
|
}) => void;
|
|
3121
|
+
setPageSizeFromUser: () => (pageSize: number) => void;
|
|
3117
3122
|
hasSyncedPaginationFromProps: () => boolean;
|
|
3118
3123
|
rowSelection: () => Record<string, boolean>;
|
|
3119
3124
|
isRowSelectionControlled: () => boolean;
|
|
@@ -3606,10 +3611,12 @@ declare const TableStoreContext: React$1.Context<{
|
|
|
3606
3611
|
pageIndex: number;
|
|
3607
3612
|
pageSize: number;
|
|
3608
3613
|
}) => void;
|
|
3614
|
+
isPageSizeUserSelected: () => boolean;
|
|
3609
3615
|
setPagination: () => (pagination: {
|
|
3610
3616
|
pageIndex: number;
|
|
3611
3617
|
pageSize: number;
|
|
3612
3618
|
}) => void;
|
|
3619
|
+
setPageSizeFromUser: () => (pageSize: number) => void;
|
|
3613
3620
|
hasSyncedPaginationFromProps: () => boolean;
|
|
3614
3621
|
rowSelection: () => Record<string, boolean>;
|
|
3615
3622
|
isRowSelectionControlled: () => boolean;
|
|
@@ -3742,10 +3749,12 @@ declare function useStoreSync(props: any): {
|
|
|
3742
3749
|
pageIndex: number;
|
|
3743
3750
|
pageSize: number;
|
|
3744
3751
|
}) => void;
|
|
3752
|
+
isPageSizeUserSelected: () => boolean;
|
|
3745
3753
|
setPagination: () => (pagination: {
|
|
3746
3754
|
pageIndex: number;
|
|
3747
3755
|
pageSize: number;
|
|
3748
3756
|
}) => void;
|
|
3757
|
+
setPageSizeFromUser: () => (pageSize: number) => void;
|
|
3749
3758
|
hasSyncedPaginationFromProps: () => boolean;
|
|
3750
3759
|
rowSelection: () => Record<string, boolean>;
|
|
3751
3760
|
isRowSelectionControlled: () => boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -11645,7 +11645,8 @@ var createTableStore = (seed) => {
|
|
|
11645
11645
|
pagination: { pageIndex: 0, pageSize: 10 },
|
|
11646
11646
|
isPaginationControlled: false,
|
|
11647
11647
|
onPaginationChange: null,
|
|
11648
|
-
|
|
11648
|
+
isPageSizeUserSelected: false,
|
|
11649
|
+
// Pagination navigation and automatic sizing.
|
|
11649
11650
|
setPagination: (pagination) => {
|
|
11650
11651
|
const { isPaginationControlled, onPaginationChange } = get();
|
|
11651
11652
|
onPaginationChange?.(pagination);
|
|
@@ -11653,6 +11654,18 @@ var createTableStore = (seed) => {
|
|
|
11653
11654
|
set({ pagination });
|
|
11654
11655
|
}
|
|
11655
11656
|
},
|
|
11657
|
+
// Rows/page is an explicit request to render that many rows. Keep the
|
|
11658
|
+
// bounded NTable body as the scroll viewport instead of letting the
|
|
11659
|
+
// dynamic fit measurement replace the reader's choice.
|
|
11660
|
+
setPageSizeFromUser: (pageSize) => {
|
|
11661
|
+
const { isPaginationControlled, onPaginationChange } = get();
|
|
11662
|
+
const next = { pageIndex: 0, pageSize };
|
|
11663
|
+
set({
|
|
11664
|
+
isPageSizeUserSelected: true,
|
|
11665
|
+
...!isPaginationControlled ? { pagination: next } : {}
|
|
11666
|
+
});
|
|
11667
|
+
onPaginationChange?.(next);
|
|
11668
|
+
},
|
|
11656
11669
|
// Track if we've ever synced pagination from props
|
|
11657
11670
|
hasSyncedPaginationFromProps: false,
|
|
11658
11671
|
// Row selection
|
|
@@ -11871,6 +11884,12 @@ function calculateCardPageSize(input) {
|
|
|
11871
11884
|
const rows = Math.max(1, Math.floor((input.bodyHeight + gap) / (cardHeight + gap)));
|
|
11872
11885
|
return rows * columns;
|
|
11873
11886
|
}
|
|
11887
|
+
function shouldDeferCardPageSizeUntilRows(renderedMode, hasDataRows) {
|
|
11888
|
+
return renderedMode === "cards" && !hasDataRows;
|
|
11889
|
+
}
|
|
11890
|
+
function shouldCommitDynamicPageSizeImmediately(reportedMode, renderedMode) {
|
|
11891
|
+
return reportedMode === null || reportedMode !== renderedMode;
|
|
11892
|
+
}
|
|
11874
11893
|
function fallbackCardColumns(width) {
|
|
11875
11894
|
if (width >= 1280) return 4;
|
|
11876
11895
|
if (width >= 1024) return 3;
|
|
@@ -11884,6 +11903,7 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
|
|
|
11884
11903
|
const isLoading = useTableStore.use.isLoading();
|
|
11885
11904
|
const error = useTableStore.use.error();
|
|
11886
11905
|
const hasNoData = useTableStore.use.hasNoData();
|
|
11906
|
+
const hasDataRows = useTableStore.use.hasData();
|
|
11887
11907
|
const isFilteredEmpty = useTableStore.use.isFilteredEmpty();
|
|
11888
11908
|
const syncWithProps = useTableStore.use.syncWithProps();
|
|
11889
11909
|
const lastMeasurementRef = useRef("");
|
|
@@ -11920,7 +11940,8 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
|
|
|
11920
11940
|
const cardGap = Number.parseFloat(gridStyles?.rowGap || gridStyles?.gap || "") || DEFAULT_CARD_GAP;
|
|
11921
11941
|
const firstCard = cardsGridEl?.querySelector("[data-ntable-loading-card], [data-row]");
|
|
11922
11942
|
const cardRowHeight = firstCard?.offsetHeight || firstCard?.getBoundingClientRect().height || DEFAULT_CARD_HEIGHT;
|
|
11923
|
-
const
|
|
11943
|
+
const measuredMode = effectiveViewMode ?? viewMode;
|
|
11944
|
+
const cardPageSize = shouldDeferCardPageSizeUntilRows(measuredMode, hasDataRows) ? 0 : calculateCardPageSize({
|
|
11924
11945
|
bodyHeight,
|
|
11925
11946
|
columnCount: cardColumnCount,
|
|
11926
11947
|
cardHeight: cardRowHeight,
|
|
@@ -11962,7 +11983,7 @@ function useDynamicPageSize(containerRef, effectiveViewMode) {
|
|
|
11962
11983
|
).forEach((el) => resizeObserver.observe(el));
|
|
11963
11984
|
if (container.parentElement) resizeObserver.observe(container.parentElement);
|
|
11964
11985
|
return () => resizeObserver.disconnect();
|
|
11965
|
-
}, [dynamicHeight, effectiveViewMode, viewMode, containerRef, syncWithProps, manualPagination, isLoading, error, hasNoData, isFilteredEmpty]);
|
|
11986
|
+
}, [dynamicHeight, effectiveViewMode, viewMode, containerRef, syncWithProps, manualPagination, isLoading, error, hasNoData, hasDataRows, isFilteredEmpty]);
|
|
11966
11987
|
}
|
|
11967
11988
|
function useTable(effectiveViewModeOverride) {
|
|
11968
11989
|
const [sorting, setSorting] = useState([]);
|
|
@@ -11997,6 +12018,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
11997
12018
|
const rowCount = useTableStore.use.rowCount();
|
|
11998
12019
|
const storePagination = useTableStore.use.pagination();
|
|
11999
12020
|
const isPaginationControlled = useTableStore.use.isPaginationControlled();
|
|
12021
|
+
const isPageSizeUserSelected = useTableStore.use.isPageSizeUserSelected();
|
|
12000
12022
|
const setPagination = useTableStore.use.setPagination();
|
|
12001
12023
|
const storeRowSelection = useTableStore.use.rowSelection();
|
|
12002
12024
|
const setRowSelection = useTableStore.use.setRowSelection();
|
|
@@ -12115,34 +12137,31 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12115
12137
|
syncWithProps({ table });
|
|
12116
12138
|
}, [table]);
|
|
12117
12139
|
useLayoutEffect(() => {
|
|
12118
|
-
if (manualPagination || isPaginationControlled) return;
|
|
12140
|
+
if (manualPagination || isPaginationControlled || isPageSizeUserSelected) return;
|
|
12119
12141
|
if (dynamicHeight && renderedMode === "table") table.setPageSize(calculatedPageSize);
|
|
12120
12142
|
if (viewMode === "cards" && cardPagination.mode === "paged") {
|
|
12121
12143
|
table.setPageSize(data.length || 9999);
|
|
12122
12144
|
}
|
|
12123
|
-
}, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled]);
|
|
12145
|
+
}, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled, isPageSizeUserSelected]);
|
|
12124
12146
|
const dynamicPageSizeTarget = renderedMode === "cards" ? calculatedCardPageSize : calculatedPageSize;
|
|
12125
12147
|
const geometryKey = `${Math.round(measuredBodyWidth / GEOMETRY_BUCKET_PX)}x${Math.round(measuredBodyHeight / GEOMETRY_BUCKET_PX)}`;
|
|
12126
12148
|
const reportedGeometryRef = useRef(null);
|
|
12127
12149
|
useLayoutEffect(() => {
|
|
12128
|
-
if (!manualPagination || !dynamicHeight) return;
|
|
12150
|
+
if (!manualPagination || !dynamicHeight || isPageSizeUserSelected) return;
|
|
12129
12151
|
if (cardPagination.mode !== "paged") return;
|
|
12130
12152
|
if (!hasMeasuredLayout || measuredBodyHeight <= 0) return;
|
|
12131
12153
|
if (!dynamicPageSizeTarget || dynamicPageSizeTarget < 1) return;
|
|
12132
12154
|
if (dynamicPageSizeTarget === storePagination.pageSize) return;
|
|
12155
|
+
if (shouldDeferCardPageSizeUntilRows(renderedMode, hasDataRows)) return;
|
|
12133
12156
|
const reported = reportedGeometryRef.current;
|
|
12134
|
-
if (reported?.key === geometryKey && reported.target === dynamicPageSizeTarget) return;
|
|
12135
|
-
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;
|
|
12136
12159
|
const commit = () => {
|
|
12137
12160
|
const current = reportedGeometryRef.current;
|
|
12138
|
-
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 };
|
|
12139
12162
|
setPagination({ pageIndex: storePagination.pageIndex, pageSize: dynamicPageSizeTarget });
|
|
12140
12163
|
};
|
|
12141
|
-
if (
|
|
12142
|
-
commit();
|
|
12143
|
-
return;
|
|
12144
|
-
}
|
|
12145
|
-
if (!reported) {
|
|
12164
|
+
if (shouldCommitDynamicPageSizeImmediately(reported?.mode ?? null, renderedMode)) {
|
|
12146
12165
|
commit();
|
|
12147
12166
|
return;
|
|
12148
12167
|
}
|
|
@@ -12151,6 +12170,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12151
12170
|
}, [
|
|
12152
12171
|
manualPagination,
|
|
12153
12172
|
dynamicHeight,
|
|
12173
|
+
isPageSizeUserSelected,
|
|
12154
12174
|
cardPagination.mode,
|
|
12155
12175
|
hasMeasuredLayout,
|
|
12156
12176
|
measuredBodyHeight,
|
|
@@ -12158,6 +12178,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12158
12178
|
dynamicPageSizeTarget,
|
|
12159
12179
|
storePagination.pageIndex,
|
|
12160
12180
|
storePagination.pageSize,
|
|
12181
|
+
renderedMode,
|
|
12161
12182
|
hasDataRows,
|
|
12162
12183
|
setPagination
|
|
12163
12184
|
]);
|
|
@@ -12357,6 +12378,8 @@ function NTableContent({ effectiveMode }) {
|
|
|
12357
12378
|
const contentCardPagination = useTableStore.use.cardPagination();
|
|
12358
12379
|
const contentCalculatedPageSize = useTableStore.use.calculatedPageSize();
|
|
12359
12380
|
const contentHasMeasuredLayout = useTableStore.use.hasMeasuredLayout();
|
|
12381
|
+
const isPageSizeUserSelected = useTableStore.use.isPageSizeUserSelected();
|
|
12382
|
+
const contentPagination = useTableStore.use.pagination();
|
|
12360
12383
|
const hasNoData = useTableStore.use.hasNoData();
|
|
12361
12384
|
const showContent = useTableStore.use.showContent();
|
|
12362
12385
|
const classNames = useTableStore.use.classNames();
|
|
@@ -12379,8 +12402,9 @@ function NTableContent({ effectiveMode }) {
|
|
|
12379
12402
|
const isTableView = effectiveMode ? effectiveMode === "table" : storeIsTableView;
|
|
12380
12403
|
if (!isTableView) return null;
|
|
12381
12404
|
const allRows = table.getRowModel().rows ?? [];
|
|
12382
|
-
const clampRowsToMeasuredPage = contentDynamicHeight && contentManualPagination && contentCardPagination.mode === "paged" && contentHasMeasuredLayout && contentCalculatedPageSize > 0;
|
|
12405
|
+
const clampRowsToMeasuredPage = contentDynamicHeight && contentManualPagination && !isPageSizeUserSelected && contentCardPagination.mode === "paged" && contentHasMeasuredLayout && contentCalculatedPageSize > 0;
|
|
12383
12406
|
const visibleRows = clampRowsToMeasuredPage ? allRows.slice(0, contentCalculatedPageSize) : allRows;
|
|
12407
|
+
const userPageNeedsScroll = isPageSizeUserSelected && contentPagination.pageSize > contentCalculatedPageSize;
|
|
12384
12408
|
const getSortIcon = (column) => {
|
|
12385
12409
|
const dir = column.getIsSorted();
|
|
12386
12410
|
if (dir === "asc") return /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" });
|
|
@@ -12402,7 +12426,7 @@ function NTableContent({ effectiveMode }) {
|
|
|
12402
12426
|
// leftover belongs to the page, not to the table. Without
|
|
12403
12427
|
// `dynamicHeight` the row count is the caller's, so the container has
|
|
12404
12428
|
// to keep filling its height to stay a scroll viewport.
|
|
12405
|
-
contentDynamicHeight ? "max-h-full shrink" : "flex-1",
|
|
12429
|
+
contentDynamicHeight && !userPageNeedsScroll ? "max-h-full shrink" : "flex-1",
|
|
12406
12430
|
surface.className,
|
|
12407
12431
|
classNames?.content
|
|
12408
12432
|
),
|
|
@@ -13333,6 +13357,7 @@ function NTablePagination() {
|
|
|
13333
13357
|
const rowCount = useTableStore.use.rowCount();
|
|
13334
13358
|
const suppliedHasNextPage = useTableStore.use.hasNextPage();
|
|
13335
13359
|
const setPagination = useTableStore.use.setPagination();
|
|
13360
|
+
const setPageSizeFromUser = useTableStore.use.setPageSizeFromUser();
|
|
13336
13361
|
const isPaginationControlled = useTableStore.use.isPaginationControlled();
|
|
13337
13362
|
const bordered = useTableStore.use.bordered();
|
|
13338
13363
|
const paginationVariant = useTableStore.use.paginationVariant();
|
|
@@ -13380,7 +13405,7 @@ function NTablePagination() {
|
|
|
13380
13405
|
};
|
|
13381
13406
|
const handlePageSizeChange = (value) => {
|
|
13382
13407
|
const newSize = Number(value);
|
|
13383
|
-
|
|
13408
|
+
setPageSizeFromUser(newSize);
|
|
13384
13409
|
};
|
|
13385
13410
|
const hasTrustworthyPageCount = manualPagination ? pageCount !== void 0 && pageCount > 0 || unbounded : effectivePageCount > 0;
|
|
13386
13411
|
const showNumbers = paginationVariant === "numbered" && hasTrustworthyPageCount;
|
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.
|