najm-kit 2.2.9 → 2.4.0
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 +12 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.mjs +120 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0
|
|
4
|
+
|
|
5
|
+
- `NTablePagination` renders numbered page buttons instead of `Page X of Y`. The window shows the first and last page, the current page, and one page either side, collapsing the rest into at most two gaps. The slot count is constant for any result longer than the window, so the bar does not change width as the reader pages through it, and a gap never stands in for a single page — that slot goes to the page instead.
|
|
6
|
+
- Added `paginationVariant`, defaulting to `"numbered"`. Pass `"compact"` to keep the previous position text with first/previous/next/last controls. **This changes the default appearance of every paginated `NTable`.**
|
|
7
|
+
- The numbered variant drops the first/last double chevrons, because page 1 and page N are now single-click targets of their own. Previous and next remain. The compact variant is unchanged.
|
|
8
|
+
- Numbered pages fall back to compact on their own when the page count is not trustworthy — that is, under `manualPagination` with no `pageCount` supplied, where TanStack infers a count from the rows it happens to hold rather than from a result total. Numbering that would invite clicks on pages that may not exist is not rendered.
|
|
9
|
+
- Below the `sm` breakpoint the numbers give way to the position text; seven page buttons plus the rows-per-page select do not fit a phone.
|
|
10
|
+
- Added `paginationLabels` so the bar can be localized: `rowsPerPage`, `pagination`, `goToPage`, `currentPage`, `firstPage`, `previousPage`, `nextPage`, `lastPage`, `pageOf`, and `rowsSelected`. All optional, all falling back to the previous English strings.
|
|
11
|
+
- Pagination chevrons now mirror under `dir="rtl"`. They previously pointed against the reading direction in right-to-left layouts.
|
|
12
|
+
- The page controls are wrapped in a labelled `nav`, and the current page carries `aria-current="page"`.
|
|
13
|
+
- Exported `buildPageItems` and `NTablePageItem` for consumers that need the same windowing outside the table.
|
|
14
|
+
|
|
3
15
|
## 2.2.1
|
|
4
16
|
|
|
5
17
|
- Fixed a regression in 2.2.0: the dynamic page size reported under `manualPagination` could oscillate. Card row height is measured from rendered cards, so it grows as images decode; feeding that back into the page size refetched, re-rendered, re-measured, and refetched again. A list visibly settled from one page size to another with the loading skeleton flashing twice. The report is now allowed once per container geometry, which does not depend on the rows inside it, so it terminates. A resize still re-arms it, and the debounce still waits for the measurement to settle before reporting.
|
package/dist/index.d.ts
CHANGED
|
@@ -2771,6 +2771,42 @@ type StepSubmitResult = {
|
|
|
2771
2771
|
};
|
|
2772
2772
|
declare function useFormSubmission({ steps, schema, defaultValues, onSubmit, currentStep, isLastStep, handleNext, markStepCompleted, reset, }: UseFormSubmissionOptions): FormSubmissionState;
|
|
2773
2773
|
|
|
2774
|
+
/**
|
|
2775
|
+
* How the page controls present position within the result.
|
|
2776
|
+
*
|
|
2777
|
+
* `numbered` renders a windowed list of page buttons. `compact` renders the
|
|
2778
|
+
* `Page X of Y` text with first/previous/next/last controls.
|
|
2779
|
+
*
|
|
2780
|
+
* `numbered` needs a trustworthy page count. Under `manualPagination` that
|
|
2781
|
+
* means the application must pass a `pageCount` derived from a real result
|
|
2782
|
+
* total; without one, the bar falls back to `compact` on its own rather than
|
|
2783
|
+
* inviting clicks on pages that may not exist.
|
|
2784
|
+
*/
|
|
2785
|
+
type NTablePaginationVariant = "numbered" | "compact";
|
|
2786
|
+
/**
|
|
2787
|
+
* Accessible names and visible copy for the page controls.
|
|
2788
|
+
*
|
|
2789
|
+
* Every field is optional and falls back to English. Supply them to localize —
|
|
2790
|
+
* the numbered variant is mostly digits, but its controls still need names.
|
|
2791
|
+
*/
|
|
2792
|
+
interface NTablePaginationLabels {
|
|
2793
|
+
/** Labels the rows-per-page select. Defaults to `"Rows/page"`. */
|
|
2794
|
+
rowsPerPage?: string;
|
|
2795
|
+
/** Accessible name of the whole page control group. Defaults to `"Pagination"`. */
|
|
2796
|
+
pagination?: string;
|
|
2797
|
+
/** Accessible name for one page button, given a 1-based page. */
|
|
2798
|
+
goToPage?: (page: number) => string;
|
|
2799
|
+
/** Accessible name of the current page button, given a 1-based page. */
|
|
2800
|
+
currentPage?: (page: number) => string;
|
|
2801
|
+
firstPage?: string;
|
|
2802
|
+
previousPage?: string;
|
|
2803
|
+
nextPage?: string;
|
|
2804
|
+
lastPage?: string;
|
|
2805
|
+
/** The `compact` variant's position text, given 1-based values. */
|
|
2806
|
+
pageOf?: (page: number, pageCount: number) => string;
|
|
2807
|
+
/** The selection summary, given selected and total row counts. */
|
|
2808
|
+
rowsSelected?: (selected: number, total: number) => string;
|
|
2809
|
+
}
|
|
2774
2810
|
interface NTableLoadMorePagination {
|
|
2775
2811
|
/** Render the supplied rows as one card list with an explicit continuation control. */
|
|
2776
2812
|
mode: "load-more";
|
|
@@ -2933,6 +2969,8 @@ interface TableState {
|
|
|
2933
2969
|
availableModes: readonly ViewMode[];
|
|
2934
2970
|
setViewMode: (mode: ViewMode) => void;
|
|
2935
2971
|
hasSyncedFromProps: boolean;
|
|
2972
|
+
paginationVariant: NTablePaginationVariant;
|
|
2973
|
+
paginationLabels: NTablePaginationLabels;
|
|
2936
2974
|
manualPagination: boolean;
|
|
2937
2975
|
pageCount: number | undefined;
|
|
2938
2976
|
rowCount: number | undefined;
|
|
@@ -3071,6 +3109,8 @@ declare const createTableStore: (seed?: Partial<TableState>) => {
|
|
|
3071
3109
|
availableModes: () => readonly ViewMode[];
|
|
3072
3110
|
setViewMode: () => (mode: ViewMode) => void;
|
|
3073
3111
|
hasSyncedFromProps: () => boolean;
|
|
3112
|
+
paginationVariant: () => NTablePaginationVariant;
|
|
3113
|
+
paginationLabels: () => NTablePaginationLabels;
|
|
3074
3114
|
manualPagination: () => boolean;
|
|
3075
3115
|
pageCount: () => number;
|
|
3076
3116
|
rowCount: () => number;
|
|
@@ -3262,6 +3302,14 @@ interface NTableProps<T = any, M extends ViewMode = ViewMode> {
|
|
|
3262
3302
|
}) => void;
|
|
3263
3303
|
/** Pagination presentation used whenever NTable is actually rendering cards. */
|
|
3264
3304
|
cardPagination?: NTableCardPagination;
|
|
3305
|
+
/**
|
|
3306
|
+
* How the page controls present position. Defaults to `"numbered"`, which
|
|
3307
|
+
* falls back to `"compact"` on its own when the page count is not
|
|
3308
|
+
* trustworthy. Pass `"compact"` for the `Page X of Y` text everywhere.
|
|
3309
|
+
*/
|
|
3310
|
+
paginationVariant?: NTablePaginationVariant;
|
|
3311
|
+
/** Accessible names and visible copy for the page controls. */
|
|
3312
|
+
paginationLabels?: NTablePaginationLabels;
|
|
3265
3313
|
rowSelection?: RowSelectionState;
|
|
3266
3314
|
defaultRowSelection?: RowSelectionState;
|
|
3267
3315
|
onRowSelectionChange?: (state: RowSelectionState) => void;
|
|
@@ -3519,6 +3567,8 @@ declare const TableStoreContext: React$1.Context<{
|
|
|
3519
3567
|
availableModes: () => readonly ViewMode[];
|
|
3520
3568
|
setViewMode: () => (mode: ViewMode) => void;
|
|
3521
3569
|
hasSyncedFromProps: () => boolean;
|
|
3570
|
+
paginationVariant: () => NTablePaginationVariant;
|
|
3571
|
+
paginationLabels: () => NTablePaginationLabels;
|
|
3522
3572
|
manualPagination: () => boolean;
|
|
3523
3573
|
pageCount: () => number;
|
|
3524
3574
|
rowCount: () => number;
|
|
@@ -3652,6 +3702,8 @@ declare function useStoreSync(props: any): {
|
|
|
3652
3702
|
availableModes: () => readonly ViewMode[];
|
|
3653
3703
|
setViewMode: () => (mode: ViewMode) => void;
|
|
3654
3704
|
hasSyncedFromProps: () => boolean;
|
|
3705
|
+
paginationVariant: () => NTablePaginationVariant;
|
|
3706
|
+
paginationLabels: () => NTablePaginationLabels;
|
|
3655
3707
|
manualPagination: () => boolean;
|
|
3656
3708
|
pageCount: () => number;
|
|
3657
3709
|
rowCount: () => number;
|
package/dist/index.mjs
CHANGED
|
@@ -12222,6 +12222,8 @@ var createTableStore = (seed) => {
|
|
|
12222
12222
|
hasMeasuredLayout: false,
|
|
12223
12223
|
skeletonRowCount: 6,
|
|
12224
12224
|
maxHeight: null,
|
|
12225
|
+
paginationVariant: "numbered",
|
|
12226
|
+
paginationLabels: {},
|
|
12225
12227
|
bodyWidth: 0,
|
|
12226
12228
|
bodyHeight: 0,
|
|
12227
12229
|
tableHeaderHeight: 48,
|
|
@@ -13728,6 +13730,51 @@ function NTableCards({ effectiveMode }) {
|
|
|
13728
13730
|
) : null
|
|
13729
13731
|
] });
|
|
13730
13732
|
}
|
|
13733
|
+
|
|
13734
|
+
// src/components/table/paginationPages.ts
|
|
13735
|
+
function buildPageItems(pageIndex, pageCount, siblingCount = 1) {
|
|
13736
|
+
const pages = Math.max(0, Math.floor(pageCount));
|
|
13737
|
+
if (pages <= 0) return [];
|
|
13738
|
+
const current = Math.min(Math.max(0, Math.floor(pageIndex)), pages - 1);
|
|
13739
|
+
const siblings = Math.max(0, Math.floor(siblingCount));
|
|
13740
|
+
const lastIndex = pages - 1;
|
|
13741
|
+
const windowSize = siblings * 2 + 5;
|
|
13742
|
+
if (pages <= windowSize) {
|
|
13743
|
+
return range(0, lastIndex);
|
|
13744
|
+
}
|
|
13745
|
+
const left = Math.max(current - siblings, 0);
|
|
13746
|
+
const right = Math.min(current + siblings, lastIndex);
|
|
13747
|
+
const showStartGap = left - 1 >= 2;
|
|
13748
|
+
const showEndGap = lastIndex - 1 - right >= 2;
|
|
13749
|
+
const runLength = siblings * 2 + 3;
|
|
13750
|
+
if (!showStartGap && showEndGap) {
|
|
13751
|
+
return [
|
|
13752
|
+
...range(0, runLength - 1),
|
|
13753
|
+
{ type: "gap", key: "end" },
|
|
13754
|
+
page(lastIndex)
|
|
13755
|
+
];
|
|
13756
|
+
}
|
|
13757
|
+
if (showStartGap && !showEndGap) {
|
|
13758
|
+
return [
|
|
13759
|
+
page(0),
|
|
13760
|
+
{ type: "gap", key: "start" },
|
|
13761
|
+
...range(lastIndex - runLength + 1, lastIndex)
|
|
13762
|
+
];
|
|
13763
|
+
}
|
|
13764
|
+
return [
|
|
13765
|
+
page(0),
|
|
13766
|
+
{ type: "gap", key: "start" },
|
|
13767
|
+
...range(left, right),
|
|
13768
|
+
{ type: "gap", key: "end" },
|
|
13769
|
+
page(lastIndex)
|
|
13770
|
+
];
|
|
13771
|
+
}
|
|
13772
|
+
function page(pageIndex) {
|
|
13773
|
+
return { type: "page", pageIndex };
|
|
13774
|
+
}
|
|
13775
|
+
function range(from, to) {
|
|
13776
|
+
return Array.from({ length: to - from + 1 }, (_, index) => page(from + index));
|
|
13777
|
+
}
|
|
13731
13778
|
function CardLoadMorePagination({
|
|
13732
13779
|
config,
|
|
13733
13780
|
rowCount,
|
|
@@ -13821,6 +13868,44 @@ function CardLoadMorePagination({
|
|
|
13821
13868
|
}
|
|
13822
13869
|
);
|
|
13823
13870
|
}
|
|
13871
|
+
var navButtonClass = "h-8 w-8 p-0 text-foreground disabled:text-muted-foreground disabled:opacity-70";
|
|
13872
|
+
var chevronClass = "h-4 w-4 rtl:-scale-x-100";
|
|
13873
|
+
function PageNumbers({
|
|
13874
|
+
pageIndex,
|
|
13875
|
+
pageCount,
|
|
13876
|
+
bordered,
|
|
13877
|
+
labels,
|
|
13878
|
+
onSelect
|
|
13879
|
+
}) {
|
|
13880
|
+
return /* @__PURE__ */ jsx(Fragment, { children: buildPageItems(pageIndex, pageCount).map((item) => {
|
|
13881
|
+
if (item.type === "gap") {
|
|
13882
|
+
return /* @__PURE__ */ jsx(
|
|
13883
|
+
"span",
|
|
13884
|
+
{
|
|
13885
|
+
"aria-hidden": "true",
|
|
13886
|
+
className: "flex h-8 w-8 items-center justify-center text-sm text-muted-foreground",
|
|
13887
|
+
children: "\u2026"
|
|
13888
|
+
},
|
|
13889
|
+
`gap-${item.key}`
|
|
13890
|
+
);
|
|
13891
|
+
}
|
|
13892
|
+
const page2 = item.pageIndex + 1;
|
|
13893
|
+
const isCurrent = item.pageIndex === pageIndex;
|
|
13894
|
+
return /* @__PURE__ */ jsx(
|
|
13895
|
+
Button,
|
|
13896
|
+
{
|
|
13897
|
+
bordered,
|
|
13898
|
+
variant: isCurrent ? "default" : "outline",
|
|
13899
|
+
className: cn(navButtonClass, "tabular-nums"),
|
|
13900
|
+
"aria-label": isCurrent ? labels.currentPage?.(page2) ?? `Page ${page2}, current page` : labels.goToPage?.(page2) ?? `Go to page ${page2}`,
|
|
13901
|
+
"aria-current": isCurrent ? "page" : void 0,
|
|
13902
|
+
onClick: () => onSelect(item.pageIndex),
|
|
13903
|
+
children: page2
|
|
13904
|
+
},
|
|
13905
|
+
item.pageIndex
|
|
13906
|
+
);
|
|
13907
|
+
}) });
|
|
13908
|
+
}
|
|
13824
13909
|
function NTablePagination() {
|
|
13825
13910
|
const table = useTableStore.use.table();
|
|
13826
13911
|
const showPagination = useTableStore.use.showPagination();
|
|
@@ -13837,6 +13922,8 @@ function NTablePagination() {
|
|
|
13837
13922
|
const setPagination = useTableStore.use.setPagination();
|
|
13838
13923
|
const isPaginationControlled = useTableStore.use.isPaginationControlled();
|
|
13839
13924
|
const bordered = useTableStore.use.bordered();
|
|
13925
|
+
const paginationVariant = useTableStore.use.paginationVariant();
|
|
13926
|
+
const labels = useTableStore.use.paginationLabels();
|
|
13840
13927
|
if (!showPagination || effectiveViewMode === "json" || effectiveViewMode === "files") return null;
|
|
13841
13928
|
if (cardPagination.mode === "all") return null;
|
|
13842
13929
|
if (effectiveViewMode === "cards" && cardPagination.mode === "infinite") return null;
|
|
@@ -13879,10 +13966,15 @@ function NTablePagination() {
|
|
|
13879
13966
|
const newSize = Number(value);
|
|
13880
13967
|
setPagination({ pageIndex: 0, pageSize: newSize });
|
|
13881
13968
|
};
|
|
13969
|
+
const hasTrustworthyPageCount = manualPagination ? pageCount !== void 0 && pageCount > 0 : effectivePageCount > 0;
|
|
13970
|
+
const showNumbers = paginationVariant === "numbered" && hasTrustworthyPageCount;
|
|
13971
|
+
const canPrevious = (table?.getCanPreviousPage?.() ?? pageIndex > 0) && pageIndex > 0;
|
|
13972
|
+
const canNext = (table?.getCanNextPage?.() ?? true) && pageIndex < effectivePageCount - 1;
|
|
13973
|
+
const selectedTotal = manualPagination && rowCount !== void 0 ? rowCount : filteredRows.length;
|
|
13882
13974
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full min-w-0 flex-wrap items-center justify-between gap-x-4 gap-y-2 py-1 text-foreground", classNames?.pagination), children: [
|
|
13883
13975
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-4 lg:gap-6", children: [
|
|
13884
13976
|
(!isPaginationControlled || manualPagination) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-foreground", children: [
|
|
13885
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Rows/page" }),
|
|
13977
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: labels.rowsPerPage ?? "Rows/page" }),
|
|
13886
13978
|
/* @__PURE__ */ jsxs(Select, { value: `${pageSize}`, onValueChange: handlePageSizeChange, children: [
|
|
13887
13979
|
/* @__PURE__ */ jsx(
|
|
13888
13980
|
SelectTrigger,
|
|
@@ -13895,25 +13987,32 @@ function NTablePagination() {
|
|
|
13895
13987
|
/* @__PURE__ */ jsx(SelectContent, { side: "top", children: currentPageSizeOptions.map((size) => /* @__PURE__ */ jsx(SelectItem, { value: `${size}`, children: size }, size)) })
|
|
13896
13988
|
] })
|
|
13897
13989
|
] }),
|
|
13898
|
-
/* @__PURE__ */
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13990
|
+
/* @__PURE__ */ jsx("div", { className: cn("text-sm font-medium text-foreground", showNumbers && "sm:hidden"), children: labels.pageOf?.(pageIndex + 1, effectivePageCount) ?? `Page ${pageIndex + 1} of ${effectivePageCount}` }),
|
|
13991
|
+
/* @__PURE__ */ jsxs(
|
|
13992
|
+
"nav",
|
|
13993
|
+
{
|
|
13994
|
+
"aria-label": labels.pagination ?? "Pagination",
|
|
13995
|
+
className: "flex items-center gap-2",
|
|
13996
|
+
children: [
|
|
13997
|
+
!showNumbers && /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: cn(navButtonClass, "hidden lg:flex"), "aria-label": labels.firstPage ?? "First page", onClick: () => navigate("first"), disabled: !canPrevious, children: /* @__PURE__ */ jsx(ChevronsLeft, { className: chevronClass }) }),
|
|
13998
|
+
/* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: navButtonClass, "aria-label": labels.previousPage ?? "Previous", onClick: () => navigate("prev"), disabled: !canPrevious, children: /* @__PURE__ */ jsx(ChevronLeft, { className: chevronClass }) }),
|
|
13999
|
+
showNumbers && /* @__PURE__ */ jsx("div", { className: "hidden items-center gap-2 sm:flex", children: /* @__PURE__ */ jsx(
|
|
14000
|
+
PageNumbers,
|
|
14001
|
+
{
|
|
14002
|
+
pageIndex,
|
|
14003
|
+
pageCount: effectivePageCount,
|
|
14004
|
+
bordered,
|
|
14005
|
+
labels,
|
|
14006
|
+
onSelect: (next) => setPagination({ ...currentPagination, pageIndex: next })
|
|
14007
|
+
}
|
|
14008
|
+
) }),
|
|
14009
|
+
/* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: navButtonClass, "aria-label": labels.nextPage ?? "Next", onClick: () => navigate("next"), disabled: !canNext, children: /* @__PURE__ */ jsx(ChevronRight, { className: chevronClass }) }),
|
|
14010
|
+
!showNumbers && /* @__PURE__ */ jsx(Button, { bordered, variant: "outline", className: cn(navButtonClass, "hidden lg:flex"), "aria-label": labels.lastPage ?? "Last page", onClick: () => navigate("last"), disabled: !canNext, children: /* @__PURE__ */ jsx(ChevronsRight, { className: chevronClass }) })
|
|
14011
|
+
]
|
|
14012
|
+
}
|
|
14013
|
+
)
|
|
13910
14014
|
] }),
|
|
13911
|
-
/* @__PURE__ */
|
|
13912
|
-
selectedRows.length,
|
|
13913
|
-
" of ",
|
|
13914
|
-
manualPagination && rowCount !== void 0 ? rowCount : filteredRows.length,
|
|
13915
|
-
" row(s) selected."
|
|
13916
|
-
] })
|
|
14015
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-none whitespace-nowrap text-sm text-muted-foreground max-sm:hidden", children: labels.rowsSelected?.(selectedRows.length, selectedTotal) ?? `${selectedRows.length} of ${selectedTotal} row(s) selected.` })
|
|
13917
14016
|
] });
|
|
13918
14017
|
}
|
|
13919
14018
|
function PendingFilter({ placeholder, icon, bordered }) {
|
|
@@ -14501,6 +14600,8 @@ function NTable(props) {
|
|
|
14501
14600
|
defaultPagination: props.defaultPagination,
|
|
14502
14601
|
onPaginationChange: props.onPaginationChange ?? null,
|
|
14503
14602
|
cardPagination: props.cardPagination ?? { mode: "paged" },
|
|
14603
|
+
paginationVariant: props.paginationVariant ?? "numbered",
|
|
14604
|
+
paginationLabels: props.paginationLabels ?? {},
|
|
14504
14605
|
// Row selection
|
|
14505
14606
|
rowSelection: props.rowSelection,
|
|
14506
14607
|
defaultRowSelection: props.defaultRowSelection,
|