najm-kit 2.11.7 → 2.11.9
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 -0
- package/dist/index.mjs +57 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.11.9 - 2026-08-30
|
|
4
|
+
|
|
5
|
+
- Fixed card grids that paginate client-side rendering their entire dataset on
|
|
6
|
+
one page. `useTable` pinned an uncontrolled paged card list to `data.length`,
|
|
7
|
+
a branch that predates the container measurement added in 2.2.0 and was never
|
|
8
|
+
reconciled with it: `calculatedCardPageSize` was measured and published, but
|
|
9
|
+
only ever applied to a server-paginated grid. A 500-row list rendered 500
|
|
10
|
+
cards on page one, reported a single page, and showed 500 as the current
|
|
11
|
+
Rows/page value. The measured size is now applied to an uncontrolled card grid
|
|
12
|
+
too, through the guards that already keep a server-paginated one stable —
|
|
13
|
+
geometry bucketing, the per-container report budget, and the deferral until
|
|
14
|
+
real cards have been measured. That loop is a property of the measurement
|
|
15
|
+
rather than of fetching: page size decides which cards render, and rendered
|
|
16
|
+
cards are what card height is measured from. Rendering every supplied row
|
|
17
|
+
stays the behavior wherever there is nothing to measure against, so
|
|
18
|
+
`dynamicHeight={false}` and a container that has not yet reported a layout are
|
|
19
|
+
unchanged, and a controlled `pagination` prop or an explicit Rows/page choice
|
|
20
|
+
still wins.
|
|
21
|
+
|
|
22
|
+
## 2.11.8 - 2026-08-28
|
|
23
|
+
|
|
24
|
+
- Fixed `NContextMenu` leaving keyboard focus on its opener after the menu
|
|
25
|
+
appeared. The first enabled action now receives focus, disabled actions are
|
|
26
|
+
skipped, Arrow Up/Down wrap, Home/End jump to the bounds, Tab dismisses the
|
|
27
|
+
menu, and Escape restores focus to the opener. Focused actions also receive
|
|
28
|
+
the same visual treatment as hovered actions.
|
|
29
|
+
|
|
3
30
|
## 2.11.7 - 2026-08-17
|
|
4
31
|
|
|
5
32
|
- Fixed date, combobox, and multiselect `FormInput` controls reaching the
|
package/dist/index.mjs
CHANGED
|
@@ -9879,6 +9879,7 @@ function NViewBody({ children, className, variant = "table" }) {
|
|
|
9879
9879
|
function NContextMenu({ x, y, items, onAction, onClose, className }) {
|
|
9880
9880
|
const [openSub, setOpenSub] = useState(null);
|
|
9881
9881
|
const menuRef = useRef(null);
|
|
9882
|
+
const returnFocusRef = useRef(null);
|
|
9882
9883
|
const [pos, setPos] = useState({ left: x, top: y });
|
|
9883
9884
|
useLayoutEffect(() => {
|
|
9884
9885
|
if (!menuRef.current) return;
|
|
@@ -9895,13 +9896,54 @@ function NContextMenu({ x, y, items, onAction, onClose, className }) {
|
|
|
9895
9896
|
(prev) => prev.left === next.left && prev.top === next.top ? prev : next
|
|
9896
9897
|
);
|
|
9897
9898
|
}, [x, y]);
|
|
9899
|
+
useLayoutEffect(() => {
|
|
9900
|
+
const activeElement = document.activeElement;
|
|
9901
|
+
if (activeElement instanceof HTMLElement && !menuRef.current?.contains(activeElement)) {
|
|
9902
|
+
returnFocusRef.current = activeElement;
|
|
9903
|
+
}
|
|
9904
|
+
menuRef.current?.querySelector("[data-context-menu-item]:not(:disabled)")?.focus();
|
|
9905
|
+
}, []);
|
|
9898
9906
|
useEffect(() => {
|
|
9899
9907
|
const onKey = (e) => {
|
|
9900
|
-
if (e.key
|
|
9908
|
+
if (e.key !== "Escape") return;
|
|
9909
|
+
e.preventDefault();
|
|
9910
|
+
onClose();
|
|
9911
|
+
queueMicrotask(() => returnFocusRef.current?.focus());
|
|
9901
9912
|
};
|
|
9902
9913
|
window.addEventListener("keydown", onKey);
|
|
9903
9914
|
return () => window.removeEventListener("keydown", onKey);
|
|
9904
9915
|
}, [onClose]);
|
|
9916
|
+
const onMenuKeyDown = (e) => {
|
|
9917
|
+
const enabledItems = Array.from(
|
|
9918
|
+
menuRef.current?.querySelectorAll(
|
|
9919
|
+
"[data-context-menu-item]:not(:disabled)"
|
|
9920
|
+
) ?? []
|
|
9921
|
+
);
|
|
9922
|
+
if (!enabledItems.length) return;
|
|
9923
|
+
const currentIndex = enabledItems.indexOf(document.activeElement);
|
|
9924
|
+
let nextIndex = null;
|
|
9925
|
+
switch (e.key) {
|
|
9926
|
+
case "ArrowDown":
|
|
9927
|
+
nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % enabledItems.length;
|
|
9928
|
+
break;
|
|
9929
|
+
case "ArrowUp":
|
|
9930
|
+
nextIndex = currentIndex < 0 ? enabledItems.length - 1 : (currentIndex - 1 + enabledItems.length) % enabledItems.length;
|
|
9931
|
+
break;
|
|
9932
|
+
case "Home":
|
|
9933
|
+
nextIndex = 0;
|
|
9934
|
+
break;
|
|
9935
|
+
case "End":
|
|
9936
|
+
nextIndex = enabledItems.length - 1;
|
|
9937
|
+
break;
|
|
9938
|
+
case "Tab":
|
|
9939
|
+
onClose();
|
|
9940
|
+
return;
|
|
9941
|
+
default:
|
|
9942
|
+
return;
|
|
9943
|
+
}
|
|
9944
|
+
e.preventDefault();
|
|
9945
|
+
enabledItems[nextIndex].focus();
|
|
9946
|
+
};
|
|
9905
9947
|
useEffect(() => {
|
|
9906
9948
|
const isInsideMenu = (target) => target instanceof Node && !!menuRef.current?.contains(target);
|
|
9907
9949
|
const onMouseDown = (e) => {
|
|
@@ -9930,6 +9972,7 @@ function NContextMenu({ x, y, items, onAction, onClose, className }) {
|
|
|
9930
9972
|
),
|
|
9931
9973
|
style: { left: pos.left, top: pos.top },
|
|
9932
9974
|
onContextMenu: (e) => e.preventDefault(),
|
|
9975
|
+
onKeyDown: onMenuKeyDown,
|
|
9933
9976
|
children: items.map((item) => {
|
|
9934
9977
|
const Icon2 = item.icon;
|
|
9935
9978
|
const hasSub = !!item.submenu?.length;
|
|
@@ -9947,6 +9990,8 @@ function NContextMenu({ x, y, items, onAction, onClose, className }) {
|
|
|
9947
9990
|
{
|
|
9948
9991
|
type: "button",
|
|
9949
9992
|
role: "menuitem",
|
|
9993
|
+
tabIndex: -1,
|
|
9994
|
+
"data-context-menu-item": true,
|
|
9950
9995
|
disabled: item.disabled,
|
|
9951
9996
|
onClick: () => {
|
|
9952
9997
|
if (hasSub) return;
|
|
@@ -9954,8 +9999,8 @@ function NContextMenu({ x, y, items, onAction, onClose, className }) {
|
|
|
9954
9999
|
onClose();
|
|
9955
10000
|
},
|
|
9956
10001
|
className: cn(
|
|
9957
|
-
"flex w-full cursor-pointer items-center justify-between gap-2 px-3 py-1.5 text-xs hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-40",
|
|
9958
|
-
item.danger && "text-destructive hover:text-destructive"
|
|
10002
|
+
"flex w-full cursor-pointer items-center justify-between gap-2 px-3 py-1.5 text-xs hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-40",
|
|
10003
|
+
item.danger && "text-destructive hover:text-destructive focus:text-destructive"
|
|
9959
10004
|
),
|
|
9960
10005
|
children: [
|
|
9961
10006
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
@@ -9974,14 +10019,15 @@ function NContextMenu({ x, y, items, onAction, onClose, className }) {
|
|
|
9974
10019
|
{
|
|
9975
10020
|
type: "button",
|
|
9976
10021
|
role: "menuitem",
|
|
10022
|
+
tabIndex: -1,
|
|
9977
10023
|
disabled: sub.disabled,
|
|
9978
10024
|
onClick: () => {
|
|
9979
10025
|
onAction(sub.id);
|
|
9980
10026
|
onClose();
|
|
9981
10027
|
},
|
|
9982
10028
|
className: cn(
|
|
9983
|
-
"flex w-full cursor-pointer items-center gap-2 px-3 py-1.5 text-xs hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-40",
|
|
9984
|
-
sub.danger && "text-destructive hover:text-destructive"
|
|
10029
|
+
"flex w-full cursor-pointer items-center gap-2 px-3 py-1.5 text-xs hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-40",
|
|
10030
|
+
sub.danger && "text-destructive hover:text-destructive focus:text-destructive"
|
|
9985
10031
|
),
|
|
9986
10032
|
children: [
|
|
9987
10033
|
SubIcon && /* @__PURE__ */ jsx(SubIcon, { size: 14 }),
|
|
@@ -12161,15 +12207,17 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12161
12207
|
useLayoutEffect(() => {
|
|
12162
12208
|
if (manualPagination || isPaginationControlled || isPageSizeUserSelected) return;
|
|
12163
12209
|
if (dynamicHeight && renderedMode === "table") table.setPageSize(calculatedPageSize);
|
|
12164
|
-
|
|
12210
|
+
const cardsAreUnmeasured = !dynamicHeight || !hasMeasuredLayout;
|
|
12211
|
+
if (viewMode === "cards" && cardPagination.mode === "paged" && cardsAreUnmeasured) {
|
|
12165
12212
|
table.setPageSize(data.length || 9999);
|
|
12166
12213
|
}
|
|
12167
|
-
}, [calculatedPageSize, dynamicHeight, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled, isPageSizeUserSelected]);
|
|
12214
|
+
}, [calculatedPageSize, dynamicHeight, hasMeasuredLayout, renderedMode, viewMode, cardPagination.mode, table, data.length, manualPagination, isPaginationControlled, isPageSizeUserSelected]);
|
|
12168
12215
|
const dynamicPageSizeTarget = renderedMode === "cards" ? calculatedCardPageSize : calculatedPageSize;
|
|
12169
12216
|
const geometryKey = `${Math.round(measuredBodyWidth / GEOMETRY_BUCKET_PX)}x${Math.round(measuredBodyHeight / GEOMETRY_BUCKET_PX)}`;
|
|
12170
12217
|
const reportedGeometryRef = useRef(null);
|
|
12171
12218
|
useLayoutEffect(() => {
|
|
12172
|
-
if (!
|
|
12219
|
+
if (!dynamicHeight || isPageSizeUserSelected) return;
|
|
12220
|
+
if (!manualPagination && (isPaginationControlled || renderedMode !== "cards")) return;
|
|
12173
12221
|
if (cardPagination.mode !== "paged") return;
|
|
12174
12222
|
if (!hasMeasuredLayout || measuredBodyHeight <= 0) return;
|
|
12175
12223
|
if (!dynamicPageSizeTarget || dynamicPageSizeTarget < 1) return;
|
|
@@ -12191,6 +12239,7 @@ function useTable(effectiveViewModeOverride) {
|
|
|
12191
12239
|
return () => clearTimeout(timer);
|
|
12192
12240
|
}, [
|
|
12193
12241
|
manualPagination,
|
|
12242
|
+
isPaginationControlled,
|
|
12194
12243
|
dynamicHeight,
|
|
12195
12244
|
isPageSizeUserSelected,
|
|
12196
12245
|
cardPagination.mode,
|