najm-kit 2.11.1 → 2.11.3
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 +27 -10
- package/README.md +3 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.mjs +34 -10
- package/dist/theme.css +5 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 2.11.
|
|
4
|
-
|
|
5
|
-
- Fixed
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
11
28
|
|
|
12
29
|
- Gave every keyboard-focusable primitive a visible focus ring, from one shared
|
|
13
30
|
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
|
@@ -2203,7 +2203,7 @@ interface NumberInputProps extends BaseProps {
|
|
|
2203
2203
|
icon?: InputIcon;
|
|
2204
2204
|
showIcon?: boolean;
|
|
2205
2205
|
}
|
|
2206
|
-
interface PasswordInputProps extends BaseProps {
|
|
2206
|
+
interface PasswordInputProps extends BaseProps, Omit<InputHTMLAttributes<HTMLInputElement>, "className" | "onChange" | "placeholder" | "type" | "value"> {
|
|
2207
2207
|
value: string;
|
|
2208
2208
|
onChange: (value: string) => void;
|
|
2209
2209
|
placeholder?: string;
|
|
@@ -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
|
@@ -4349,13 +4349,13 @@ var NumberInput = ({ value, onChange, placeholder = "", icon, showIcon = true, i
|
|
|
4349
4349
|
/* @__PURE__ */ jsx(Input, { type: "number", placeholder, value: value ?? "", onChange: (ev) => onChange(Number(ev.target.value)), className: "p-0 border-0 shadow-none bg-transparent dark:bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 text-foreground placeholder:text-muted-foreground" })
|
|
4350
4350
|
] });
|
|
4351
4351
|
};
|
|
4352
|
-
var PasswordInput = ({ value, onChange, placeholder = "", icon, showIcon = true, iconColor, className = "", variant = "default", status = "default", bordered, borderColor }) => {
|
|
4352
|
+
var PasswordInput = ({ value, onChange, placeholder = "", icon, showIcon = true, iconColor, className = "", variant = "default", status = "default", bordered, borderColor, disabled = false, ...props }) => {
|
|
4353
4353
|
const [showPassword, setShowPassword] = useState(false);
|
|
4354
4354
|
const shouldDisplayIcon = Boolean(icon) && showIcon;
|
|
4355
4355
|
const iconProps = getIconColorProps(iconColor, "h-4 w-4");
|
|
4356
|
-
return /* @__PURE__ */ jsxs(BaseInput, { variant, status, bordered, borderColor, className: cn("gap-2", className), children: [
|
|
4356
|
+
return /* @__PURE__ */ jsxs(BaseInput, { variant, status, bordered, borderColor, className: cn("gap-2", className), disabled, children: [
|
|
4357
4357
|
shouldDisplayIcon && /* @__PURE__ */ jsx("span", { className: iconProps.className, style: iconProps.style, children: resolveIcon(icon) }),
|
|
4358
|
-
/* @__PURE__ */ jsx(Input, { type: showPassword ? "text" : "password", placeholder, value, onChange: (ev) => onChange(ev.target.value), className: "p-0 border-0 shadow-none bg-transparent dark:bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 text-foreground placeholder:text-muted-foreground" }),
|
|
4358
|
+
/* @__PURE__ */ jsx(Input, { type: showPassword ? "text" : "password", placeholder, value, onChange: (ev) => onChange(ev.target.value), className: "p-0 border-0 shadow-none bg-transparent dark:bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 text-foreground placeholder:text-muted-foreground", disabled, ...props }),
|
|
4359
4359
|
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) })
|
|
4360
4360
|
] });
|
|
4361
4361
|
};
|
|
@@ -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,9 @@ 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
|
+
}
|
|
11874
11890
|
function fallbackCardColumns(width) {
|
|
11875
11891
|
if (width >= 1280) return 4;
|
|
11876
11892
|
if (width >= 1024) return 3;
|
|
@@ -11997,6 +12013,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
11997
12013
|
const rowCount = useTableStore.use.rowCount();
|
|
11998
12014
|
const storePagination = useTableStore.use.pagination();
|
|
11999
12015
|
const isPaginationControlled = useTableStore.use.isPaginationControlled();
|
|
12016
|
+
const isPageSizeUserSelected = useTableStore.use.isPageSizeUserSelected();
|
|
12000
12017
|
const setPagination = useTableStore.use.setPagination();
|
|
12001
12018
|
const storeRowSelection = useTableStore.use.rowSelection();
|
|
12002
12019
|
const setRowSelection = useTableStore.use.setRowSelection();
|
|
@@ -12115,21 +12132,22 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12115
12132
|
syncWithProps({ table });
|
|
12116
12133
|
}, [table]);
|
|
12117
12134
|
useLayoutEffect(() => {
|
|
12118
|
-
if (manualPagination || isPaginationControlled) return;
|
|
12135
|
+
if (manualPagination || isPaginationControlled || isPageSizeUserSelected) return;
|
|
12119
12136
|
if (dynamicHeight && renderedMode === "table") table.setPageSize(calculatedPageSize);
|
|
12120
12137
|
if (viewMode === "cards" && cardPagination.mode === "paged") {
|
|
12121
12138
|
table.setPageSize(data.length || 9999);
|
|
12122
12139
|
}
|
|
12123
|
-
}, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled]);
|
|
12140
|
+
}, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled, isPageSizeUserSelected]);
|
|
12124
12141
|
const dynamicPageSizeTarget = renderedMode === "cards" ? calculatedCardPageSize : calculatedPageSize;
|
|
12125
12142
|
const geometryKey = `${Math.round(measuredBodyWidth / GEOMETRY_BUCKET_PX)}x${Math.round(measuredBodyHeight / GEOMETRY_BUCKET_PX)}`;
|
|
12126
12143
|
const reportedGeometryRef = useRef(null);
|
|
12127
12144
|
useLayoutEffect(() => {
|
|
12128
|
-
if (!manualPagination || !dynamicHeight) return;
|
|
12145
|
+
if (!manualPagination || !dynamicHeight || isPageSizeUserSelected) return;
|
|
12129
12146
|
if (cardPagination.mode !== "paged") return;
|
|
12130
12147
|
if (!hasMeasuredLayout || measuredBodyHeight <= 0) return;
|
|
12131
12148
|
if (!dynamicPageSizeTarget || dynamicPageSizeTarget < 1) return;
|
|
12132
12149
|
if (dynamicPageSizeTarget === storePagination.pageSize) return;
|
|
12150
|
+
if (shouldDeferCardPageSizeUntilRows(renderedMode, hasDataRows)) return;
|
|
12133
12151
|
const reported = reportedGeometryRef.current;
|
|
12134
12152
|
if (reported?.key === geometryKey && reported.target === dynamicPageSizeTarget) return;
|
|
12135
12153
|
if (reported?.key === geometryKey && reported.count >= MAX_PAGE_SIZE_REPORTS_PER_GEOMETRY) return;
|
|
@@ -12151,6 +12169,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12151
12169
|
}, [
|
|
12152
12170
|
manualPagination,
|
|
12153
12171
|
dynamicHeight,
|
|
12172
|
+
isPageSizeUserSelected,
|
|
12154
12173
|
cardPagination.mode,
|
|
12155
12174
|
hasMeasuredLayout,
|
|
12156
12175
|
measuredBodyHeight,
|
|
@@ -12158,6 +12177,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12158
12177
|
dynamicPageSizeTarget,
|
|
12159
12178
|
storePagination.pageIndex,
|
|
12160
12179
|
storePagination.pageSize,
|
|
12180
|
+
renderedMode,
|
|
12161
12181
|
hasDataRows,
|
|
12162
12182
|
setPagination
|
|
12163
12183
|
]);
|
|
@@ -12357,6 +12377,8 @@ function NTableContent({ effectiveMode }) {
|
|
|
12357
12377
|
const contentCardPagination = useTableStore.use.cardPagination();
|
|
12358
12378
|
const contentCalculatedPageSize = useTableStore.use.calculatedPageSize();
|
|
12359
12379
|
const contentHasMeasuredLayout = useTableStore.use.hasMeasuredLayout();
|
|
12380
|
+
const isPageSizeUserSelected = useTableStore.use.isPageSizeUserSelected();
|
|
12381
|
+
const contentPagination = useTableStore.use.pagination();
|
|
12360
12382
|
const hasNoData = useTableStore.use.hasNoData();
|
|
12361
12383
|
const showContent = useTableStore.use.showContent();
|
|
12362
12384
|
const classNames = useTableStore.use.classNames();
|
|
@@ -12379,8 +12401,9 @@ function NTableContent({ effectiveMode }) {
|
|
|
12379
12401
|
const isTableView = effectiveMode ? effectiveMode === "table" : storeIsTableView;
|
|
12380
12402
|
if (!isTableView) return null;
|
|
12381
12403
|
const allRows = table.getRowModel().rows ?? [];
|
|
12382
|
-
const clampRowsToMeasuredPage = contentDynamicHeight && contentManualPagination && contentCardPagination.mode === "paged" && contentHasMeasuredLayout && contentCalculatedPageSize > 0;
|
|
12404
|
+
const clampRowsToMeasuredPage = contentDynamicHeight && contentManualPagination && !isPageSizeUserSelected && contentCardPagination.mode === "paged" && contentHasMeasuredLayout && contentCalculatedPageSize > 0;
|
|
12383
12405
|
const visibleRows = clampRowsToMeasuredPage ? allRows.slice(0, contentCalculatedPageSize) : allRows;
|
|
12406
|
+
const userPageNeedsScroll = isPageSizeUserSelected && contentPagination.pageSize > contentCalculatedPageSize;
|
|
12384
12407
|
const getSortIcon = (column) => {
|
|
12385
12408
|
const dir = column.getIsSorted();
|
|
12386
12409
|
if (dir === "asc") return /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" });
|
|
@@ -12402,7 +12425,7 @@ function NTableContent({ effectiveMode }) {
|
|
|
12402
12425
|
// leftover belongs to the page, not to the table. Without
|
|
12403
12426
|
// `dynamicHeight` the row count is the caller's, so the container has
|
|
12404
12427
|
// to keep filling its height to stay a scroll viewport.
|
|
12405
|
-
contentDynamicHeight ? "max-h-full shrink" : "flex-1",
|
|
12428
|
+
contentDynamicHeight && !userPageNeedsScroll ? "max-h-full shrink" : "flex-1",
|
|
12406
12429
|
surface.className,
|
|
12407
12430
|
classNames?.content
|
|
12408
12431
|
),
|
|
@@ -13333,6 +13356,7 @@ function NTablePagination() {
|
|
|
13333
13356
|
const rowCount = useTableStore.use.rowCount();
|
|
13334
13357
|
const suppliedHasNextPage = useTableStore.use.hasNextPage();
|
|
13335
13358
|
const setPagination = useTableStore.use.setPagination();
|
|
13359
|
+
const setPageSizeFromUser = useTableStore.use.setPageSizeFromUser();
|
|
13336
13360
|
const isPaginationControlled = useTableStore.use.isPaginationControlled();
|
|
13337
13361
|
const bordered = useTableStore.use.bordered();
|
|
13338
13362
|
const paginationVariant = useTableStore.use.paginationVariant();
|
|
@@ -13380,7 +13404,7 @@ function NTablePagination() {
|
|
|
13380
13404
|
};
|
|
13381
13405
|
const handlePageSizeChange = (value) => {
|
|
13382
13406
|
const newSize = Number(value);
|
|
13383
|
-
|
|
13407
|
+
setPageSizeFromUser(newSize);
|
|
13384
13408
|
};
|
|
13385
13409
|
const hasTrustworthyPageCount = manualPagination ? pageCount !== void 0 && pageCount > 0 || unbounded : effectivePageCount > 0;
|
|
13386
13410
|
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.
|