@jbpark/ui-kit 10.0.2 → 10.2.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/README.ko.md +7 -0
- package/README.md +7 -0
- package/dist/Alert.d.mts +24 -0
- package/dist/Alert.d.mts.map +1 -0
- package/dist/Alert.mjs +68 -0
- package/dist/Alert.mjs.map +1 -0
- package/dist/Avatar.d.mts +19 -0
- package/dist/Avatar.d.mts.map +1 -0
- package/dist/Avatar.mjs +43 -0
- package/dist/Avatar.mjs.map +1 -0
- package/dist/Badge.d.mts +23 -0
- package/dist/Badge.d.mts.map +1 -0
- package/dist/Badge.mjs +41 -0
- package/dist/Badge.mjs.map +1 -0
- package/dist/Breadcrumb.d.mts +39 -0
- package/dist/Breadcrumb.d.mts.map +1 -0
- package/dist/Breadcrumb.mjs +103 -0
- package/dist/Breadcrumb.mjs.map +1 -0
- package/dist/Button.mjs +1 -1
- package/dist/Pagination.d.mts +53 -0
- package/dist/Pagination.d.mts.map +1 -0
- package/dist/Pagination.mjs +208 -0
- package/dist/Pagination.mjs.map +1 -0
- package/dist/Segmented.d.mts +26 -0
- package/dist/Segmented.d.mts.map +1 -0
- package/dist/Segmented.mjs +57 -0
- package/dist/Segmented.mjs.map +1 -0
- package/dist/Steps.d.mts +28 -0
- package/dist/Steps.d.mts.map +1 -0
- package/dist/Steps.mjs +70 -0
- package/dist/Steps.mjs.map +1 -0
- package/dist/Table.d.mts +48 -0
- package/dist/Table.d.mts.map +1 -0
- package/dist/Table.mjs +119 -0
- package/dist/Table.mjs.map +1 -0
- package/dist/components/atoms/checkbox/checkbox.mjs +3 -2
- package/dist/components/atoms/checkbox/checkbox.mjs.map +1 -1
- package/dist/components/atoms/checkbox/group.mjs +2 -1
- package/dist/components/atoms/checkbox/group.mjs.map +1 -1
- package/dist/components/atoms/color-picker.mjs +1 -1
- package/dist/components/atoms/date-picker.mjs +2 -2
- package/dist/components/atoms/float-button/back-top.mjs +2 -2
- package/dist/components/atoms/index.d.mts +6 -1
- package/dist/components/atoms/input/index.d.mts +3 -1
- package/dist/components/atoms/input/index.d.mts.map +1 -1
- package/dist/components/atoms/input/index.mjs +3 -1
- package/dist/components/atoms/input/index.mjs.map +1 -1
- package/dist/components/atoms/input/number.d.mts +23 -0
- package/dist/components/atoms/input/number.d.mts.map +1 -0
- package/dist/components/atoms/input/number.mjs +71 -0
- package/dist/components/atoms/input/number.mjs.map +1 -0
- package/dist/components/atoms/input/search.d.mts +0 -5
- package/dist/components/atoms/input/search.d.mts.map +1 -1
- package/dist/components/atoms/input/search.mjs +8 -8
- package/dist/components/atoms/input/search.mjs.map +1 -1
- package/dist/components/atoms/popover.mjs +1 -1
- package/dist/components/atoms/progress.d.mts +3 -1
- package/dist/components/atoms/progress.d.mts.map +1 -1
- package/dist/components/atoms/progress.mjs +2 -2
- package/dist/components/atoms/progress.mjs.map +1 -1
- package/dist/components/atoms/radio/group.mjs +1 -1
- package/dist/components/atoms/radio/radio.mjs +3 -2
- package/dist/components/atoms/radio/radio.mjs.map +1 -1
- package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts +6 -1
- package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts.map +1 -1
- package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs +7 -2
- package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs.map +1 -1
- package/dist/components/atoms/rich-text-editor/toolbar-presets.mjs +1 -1
- package/dist/components/atoms/select-multiple.d.mts +29 -0
- package/dist/components/atoms/select-multiple.d.mts.map +1 -0
- package/dist/components/atoms/select-multiple.mjs +97 -0
- package/dist/components/atoms/select-multiple.mjs.map +1 -0
- package/dist/components/atoms/select-searchable-options.d.mts +21 -0
- package/dist/components/atoms/select-searchable-options.d.mts.map +1 -0
- package/dist/components/atoms/select-searchable-options.mjs +62 -0
- package/dist/components/atoms/select-searchable-options.mjs.map +1 -0
- package/dist/components/atoms/select-searchable.d.mts +22 -0
- package/dist/components/atoms/select-searchable.d.mts.map +1 -0
- package/dist/components/atoms/select-searchable.mjs +65 -0
- package/dist/components/atoms/select-searchable.mjs.map +1 -0
- package/dist/components/atoms/select.d.mts +8 -2
- package/dist/components/atoms/select.d.mts.map +1 -1
- package/dist/components/atoms/select.mjs +12 -2
- package/dist/components/atoms/select.mjs.map +1 -1
- package/dist/components/atoms/skeleton/skeleton.d.mts +3 -1
- package/dist/components/atoms/skeleton/skeleton.d.mts.map +1 -1
- package/dist/components/atoms/skeleton/skeleton.mjs +2 -2
- package/dist/components/atoms/skeleton/skeleton.mjs.map +1 -1
- package/dist/components/atoms/slider/slider.mjs +1 -1
- package/dist/components/atoms/switch.mjs +1 -1
- package/dist/components/atoms/tooltip.d.mts +21 -0
- package/dist/components/atoms/tooltip.d.mts.map +1 -0
- package/dist/components/atoms/tooltip.mjs +78 -0
- package/dist/components/atoms/tooltip.mjs.map +1 -0
- package/dist/components/atoms/typography/paragraph.mjs +1 -0
- package/dist/components/atoms/typography/paragraph.mjs.map +1 -1
- package/dist/components/atoms/typography/title.mjs +1 -0
- package/dist/components/atoms/typography/title.mjs.map +1 -1
- package/dist/components/molecules/collapse.d.mts +8 -8
- package/dist/components/molecules/collapse.d.mts.map +1 -1
- package/dist/components/molecules/collapse.mjs +11 -2
- package/dist/components/molecules/collapse.mjs.map +1 -1
- package/dist/components/molecules/dropdown.mjs +1 -1
- package/dist/components/molecules/index.d.mts +15 -10
- package/dist/components/molecules/marquees/marquees.mjs +1 -1
- package/dist/components/molecules/menu/item/item.mjs +1 -1
- package/dist/components/molecules/menu/menu.d.mts +1 -1
- package/dist/components/molecules/menu/menu.d.mts.map +1 -1
- package/dist/components/molecules/menu/menu.mjs +5 -2
- package/dist/components/molecules/menu/menu.mjs.map +1 -1
- package/dist/components/molecules/tabs.d.mts +41 -0
- package/dist/components/molecules/tabs.d.mts.map +1 -0
- package/dist/components/molecules/tabs.mjs +55 -0
- package/dist/components/molecules/tabs.mjs.map +1 -0
- package/dist/components/molecules/upload.mjs +3 -2
- package/dist/components/molecules/upload.mjs.map +1 -1
- package/dist/components/organisms/drawer.d.mts +1 -1
- package/dist/components/organisms/drawer.d.mts.map +1 -1
- package/dist/components/organisms/drawer.mjs +10 -4
- package/dist/components/organisms/drawer.mjs.map +1 -1
- package/dist/components/organisms/index.d.mts +6 -5
- package/dist/components/organisms/list/list.mjs +1 -1
- package/dist/components/organisms/modal.d.mts.map +1 -1
- package/dist/components/organisms/modal.mjs +8 -5
- package/dist/components/organisms/modal.mjs.map +1 -1
- package/dist/components/organisms/toast.d.mts.map +1 -1
- package/dist/components/organisms/toast.mjs +3 -2
- package/dist/components/organisms/toast.mjs.map +1 -1
- package/dist/components/templates/empty.mjs +1 -0
- package/dist/components/templates/empty.mjs.map +1 -1
- package/dist/components/templates/layout/content.mjs +1 -0
- package/dist/components/templates/layout/content.mjs.map +1 -1
- package/dist/components/templates/layout/footer.mjs +1 -0
- package/dist/components/templates/layout/footer.mjs.map +1 -1
- package/dist/components/templates/layout/header.mjs +1 -0
- package/dist/components/templates/layout/header.mjs.map +1 -1
- package/dist/components/templates/layout/layout.mjs +1 -0
- package/dist/components/templates/layout/layout.mjs.map +1 -1
- package/dist/components/templates/layout/sider.mjs +4 -2
- package/dist/components/templates/layout/sider.mjs.map +1 -1
- package/dist/components/templates/page-header.mjs +1 -0
- package/dist/components/templates/page-header.mjs.map +1 -1
- package/dist/components/templates/result.mjs +1 -0
- package/dist/components/templates/result.mjs.map +1 -1
- package/dist/core/accordion.mjs +1 -1
- package/dist/core/button.d.mts +1 -1
- package/dist/core/calendar.mjs +1 -1
- package/dist/core/checkbox.mjs +1 -1
- package/dist/core/combobox.d.mts +7 -0
- package/dist/core/combobox.d.mts.map +1 -0
- package/dist/core/combobox.mjs +99 -0
- package/dist/core/combobox.mjs.map +1 -0
- package/dist/core/dialog.mjs +1 -1
- package/dist/core/dialog.mjs.map +1 -1
- package/dist/core/drawer.mjs.map +1 -1
- package/dist/core/field.mjs +2 -4
- package/dist/core/field.mjs.map +1 -1
- package/dist/core/index.d.mts +4 -1
- package/dist/core/radio-group.mjs +1 -1
- package/dist/core/select.mjs +28 -23
- package/dist/core/select.mjs.map +1 -1
- package/dist/core/tabs.d.mts +9 -0
- package/dist/core/tabs.d.mts.map +1 -0
- package/dist/core/tabs.mjs +59 -0
- package/dist/core/tabs.mjs.map +1 -0
- package/dist/core/tooltip.d.mts +2 -0
- package/dist/core/tooltip.mjs +41 -0
- package/dist/core/tooltip.mjs.map +1 -0
- package/dist/index.d.mts +40 -29
- package/dist/index.mjs +12 -2
- package/dist/lib/z-layers.mjs +21 -0
- package/dist/lib/z-layers.mjs.map +1 -1
- package/dist/providers/config/dark-mode.mjs +2 -10
- package/dist/providers/config/dark-mode.mjs.map +1 -1
- package/dist/style.css +388 -16
- package/package.json +42 -2
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./utils.mjs";
|
|
3
|
+
import { useConfig } from "./providers/config/context.mjs";
|
|
4
|
+
import Button from "./Button.mjs";
|
|
5
|
+
import Input from "./components/atoms/input/index.mjs";
|
|
6
|
+
import Select from "./components/atoms/select.mjs";
|
|
7
|
+
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
//#region src/components/molecules/pagination.tsx
|
|
11
|
+
const clampPage = (page, pageCount) => Math.min(Math.max(1, Number.isFinite(page) ? Math.floor(page) : 1), Math.max(1, pageCount));
|
|
12
|
+
const normalizePageSize = (value) => Number.isFinite(value) && value >= 1 ? Math.floor(value) : 10;
|
|
13
|
+
const getPageItems = (page, pageCount, siblingCount, boundaryCount) => {
|
|
14
|
+
if (pageCount === 0) return [];
|
|
15
|
+
const pages = /* @__PURE__ */ new Set();
|
|
16
|
+
for (let index = 1; index <= boundaryCount && index <= pageCount; index++) {
|
|
17
|
+
pages.add(index);
|
|
18
|
+
pages.add(pageCount - index + 1);
|
|
19
|
+
}
|
|
20
|
+
for (let index = Math.max(1, page - siblingCount); index <= Math.min(pageCount, page + siblingCount); index++) pages.add(index);
|
|
21
|
+
const sorted = [...pages].sort((a, b) => a - b);
|
|
22
|
+
const items = [];
|
|
23
|
+
let previous = 0;
|
|
24
|
+
for (const current of sorted) {
|
|
25
|
+
const gap = current - previous;
|
|
26
|
+
if (gap === 2) items.push(previous + 1);
|
|
27
|
+
else if (gap > 2) items.push("start-ellipsis");
|
|
28
|
+
items.push(current);
|
|
29
|
+
previous = current;
|
|
30
|
+
}
|
|
31
|
+
const finalGap = pageCount - previous;
|
|
32
|
+
if (finalGap === 1) items.push(pageCount);
|
|
33
|
+
else if (finalGap > 1) items.push("end-ellipsis");
|
|
34
|
+
return items;
|
|
35
|
+
};
|
|
36
|
+
const Pagination = ({ total, pageSize, defaultPageSize = 10, onPageSizeChange, pageSizeOptions = [
|
|
37
|
+
10,
|
|
38
|
+
20,
|
|
39
|
+
50,
|
|
40
|
+
100
|
|
41
|
+
], showSizeChanger = false, showTotal, showQuickJumper = false, page, defaultPage = 1, onChange, siblingCount = 1, boundaryCount = 1, showFirstLast = false, hideOnSinglePage = false, getPageHref, disabled = false, size, labels, className, classNames, "aria-label": ariaLabel, ...props }) => {
|
|
42
|
+
const { componentSize, direction } = useConfig();
|
|
43
|
+
const safeTotal = Number.isFinite(total) ? Math.max(0, Math.floor(total)) : 0;
|
|
44
|
+
const [internalPageSize, setInternalPageSize] = useState(() => normalizePageSize(defaultPageSize));
|
|
45
|
+
const safePageSize = normalizePageSize(pageSize ?? internalPageSize);
|
|
46
|
+
const pageCount = Math.ceil(safeTotal / safePageSize);
|
|
47
|
+
const [jumpValue, setJumpValue] = useState("");
|
|
48
|
+
const [internal, setInternal] = useState({
|
|
49
|
+
page: clampPage(defaultPage, pageCount),
|
|
50
|
+
pageCount
|
|
51
|
+
});
|
|
52
|
+
if (page === void 0 && internal.pageCount !== pageCount) setInternal({
|
|
53
|
+
page: clampPage(internal.page, pageCount),
|
|
54
|
+
pageCount
|
|
55
|
+
});
|
|
56
|
+
const currentPage = clampPage(page ?? internal.page, pageCount);
|
|
57
|
+
const resolvedSize = size ?? componentSize ?? "middle";
|
|
58
|
+
const items = getPageItems(currentPage, pageCount, Number.isFinite(siblingCount) ? Math.max(0, Math.floor(siblingCount)) : 1, Number.isFinite(boundaryCount) ? Math.max(0, Math.floor(boundaryCount)) : 1);
|
|
59
|
+
const sizeOptions = Array.from(new Set([...pageSizeOptions, safePageSize].filter((value) => Number.isInteger(value) && value > 0))).sort((a, b) => a - b);
|
|
60
|
+
const range = safeTotal === 0 ? [0, 0] : [(currentPage - 1) * safePageSize + 1, Math.min(currentPage * safePageSize, safeTotal)];
|
|
61
|
+
const goToPage = (next) => {
|
|
62
|
+
if (disabled || pageCount === 0 || next < 1 || next > pageCount || next === currentPage) return;
|
|
63
|
+
if (page === void 0) setInternal({
|
|
64
|
+
page: next,
|
|
65
|
+
pageCount
|
|
66
|
+
});
|
|
67
|
+
onChange?.(next);
|
|
68
|
+
};
|
|
69
|
+
const changePageSize = (next) => {
|
|
70
|
+
if (disabled || next === safePageSize) return;
|
|
71
|
+
if (pageSize === void 0) setInternalPageSize(next);
|
|
72
|
+
if (page === void 0) setInternal({
|
|
73
|
+
page: 1,
|
|
74
|
+
pageCount: Math.ceil(safeTotal / next)
|
|
75
|
+
});
|
|
76
|
+
onPageSizeChange?.(next);
|
|
77
|
+
if (currentPage !== 1) onChange?.(1);
|
|
78
|
+
};
|
|
79
|
+
const jumpToPage = (event) => {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
if (!/^\d+$/.test(jumpValue.trim())) return;
|
|
82
|
+
const next = Number(jumpValue.trim());
|
|
83
|
+
if (!Number.isSafeInteger(next) || next < 1 || next > pageCount) return;
|
|
84
|
+
goToPage(next);
|
|
85
|
+
setJumpValue("");
|
|
86
|
+
};
|
|
87
|
+
const handlePageClick = (next, event) => {
|
|
88
|
+
if (getPageHref && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0)) return;
|
|
89
|
+
if (getPageHref && next === currentPage) event.preventDefault();
|
|
90
|
+
goToPage(next);
|
|
91
|
+
};
|
|
92
|
+
const linkProps = (target, isDisabled) => getPageHref && !isDisabled ? {
|
|
93
|
+
render: /* @__PURE__ */ jsx("a", { href: getPageHref(target) }),
|
|
94
|
+
nativeButton: false,
|
|
95
|
+
role: "link"
|
|
96
|
+
} : {};
|
|
97
|
+
if (hideOnSinglePage && pageCount <= 1) return null;
|
|
98
|
+
return /* @__PURE__ */ jsxs("nav", {
|
|
99
|
+
...props,
|
|
100
|
+
"data-slot": "pagination",
|
|
101
|
+
"aria-label": ariaLabel ?? labels?.navigation ?? "Pagination",
|
|
102
|
+
className: cn("flex flex-wrap items-center gap-1", className),
|
|
103
|
+
children: [
|
|
104
|
+
showTotal && /* @__PURE__ */ jsx("span", {
|
|
105
|
+
"data-slot": "pagination-total",
|
|
106
|
+
className: cn("text-muted-foreground me-2 text-sm", classNames?.total),
|
|
107
|
+
children: showTotal(safeTotal, range)
|
|
108
|
+
}),
|
|
109
|
+
showFirstLast && /* @__PURE__ */ jsx(Button, {
|
|
110
|
+
...linkProps(1, disabled || pageCount === 0 || currentPage === 1),
|
|
111
|
+
icon: direction === "rtl" ? /* @__PURE__ */ jsx(ChevronsRight, {}) : /* @__PURE__ */ jsx(ChevronsLeft, {}),
|
|
112
|
+
size: resolvedSize,
|
|
113
|
+
variant: "outlined",
|
|
114
|
+
"aria-label": labels?.first ?? "First page",
|
|
115
|
+
disabled: disabled || pageCount === 0 || currentPage === 1,
|
|
116
|
+
className: cn(classNames?.item, classNames?.first),
|
|
117
|
+
onClick: (event) => handlePageClick(1, event)
|
|
118
|
+
}),
|
|
119
|
+
/* @__PURE__ */ jsx(Button, {
|
|
120
|
+
...linkProps(currentPage - 1, disabled || pageCount === 0 || currentPage === 1),
|
|
121
|
+
icon: direction === "rtl" ? /* @__PURE__ */ jsx(ChevronRight, {}) : /* @__PURE__ */ jsx(ChevronLeft, {}),
|
|
122
|
+
size: resolvedSize,
|
|
123
|
+
variant: "outlined",
|
|
124
|
+
"aria-label": labels?.previous ?? "Previous page",
|
|
125
|
+
disabled: disabled || pageCount === 0 || currentPage === 1,
|
|
126
|
+
className: cn(classNames?.item, classNames?.previous),
|
|
127
|
+
onClick: (event) => handlePageClick(currentPage - 1, event)
|
|
128
|
+
}),
|
|
129
|
+
items.map((item, index) => typeof item === "number" ? /* @__PURE__ */ jsx(Button, {
|
|
130
|
+
...linkProps(item, disabled),
|
|
131
|
+
size: resolvedSize,
|
|
132
|
+
variant: item === currentPage ? "solid" : "outlined",
|
|
133
|
+
color: item === currentPage ? "primary" : "default",
|
|
134
|
+
"aria-current": item === currentPage ? "page" : void 0,
|
|
135
|
+
"aria-label": labels?.page?.(item) ?? `Page ${item}`,
|
|
136
|
+
disabled,
|
|
137
|
+
className: cn("min-w-8", classNames?.item, item === currentPage && classNames?.active),
|
|
138
|
+
onClick: (event) => handlePageClick(item, event),
|
|
139
|
+
children: item
|
|
140
|
+
}, item) : /* @__PURE__ */ jsx("span", {
|
|
141
|
+
"aria-hidden": "true",
|
|
142
|
+
className: cn(`text-muted-foreground inline-flex min-w-6 items-center
|
|
143
|
+
justify-center`, classNames?.ellipsis),
|
|
144
|
+
children: "…"
|
|
145
|
+
}, `${item}-${index}`)),
|
|
146
|
+
/* @__PURE__ */ jsx(Button, {
|
|
147
|
+
...linkProps(currentPage + 1, disabled || pageCount === 0 || currentPage === pageCount),
|
|
148
|
+
icon: direction === "rtl" ? /* @__PURE__ */ jsx(ChevronLeft, {}) : /* @__PURE__ */ jsx(ChevronRight, {}),
|
|
149
|
+
size: resolvedSize,
|
|
150
|
+
variant: "outlined",
|
|
151
|
+
"aria-label": labels?.next ?? "Next page",
|
|
152
|
+
disabled: disabled || pageCount === 0 || currentPage === pageCount,
|
|
153
|
+
className: cn(classNames?.item, classNames?.next),
|
|
154
|
+
onClick: (event) => handlePageClick(currentPage + 1, event)
|
|
155
|
+
}),
|
|
156
|
+
showFirstLast && /* @__PURE__ */ jsx(Button, {
|
|
157
|
+
...linkProps(pageCount, disabled || pageCount === 0 || currentPage === pageCount),
|
|
158
|
+
icon: direction === "rtl" ? /* @__PURE__ */ jsx(ChevronsLeft, {}) : /* @__PURE__ */ jsx(ChevronsRight, {}),
|
|
159
|
+
size: resolvedSize,
|
|
160
|
+
variant: "outlined",
|
|
161
|
+
"aria-label": labels?.last ?? "Last page",
|
|
162
|
+
disabled: disabled || pageCount === 0 || currentPage === pageCount,
|
|
163
|
+
className: cn(classNames?.item, classNames?.last),
|
|
164
|
+
onClick: (event) => handlePageClick(pageCount, event)
|
|
165
|
+
}),
|
|
166
|
+
showSizeChanger && /* @__PURE__ */ jsx("div", {
|
|
167
|
+
"data-slot": "pagination-size-changer",
|
|
168
|
+
className: cn("ms-2 min-w-24", classNames?.sizeChanger),
|
|
169
|
+
children: /* @__PURE__ */ jsx(Select, {
|
|
170
|
+
value: String(safePageSize),
|
|
171
|
+
options: sizeOptions.map((value) => ({
|
|
172
|
+
value: String(value),
|
|
173
|
+
label: `${value} / page`
|
|
174
|
+
})),
|
|
175
|
+
onChange: (value) => changePageSize(Number(value)),
|
|
176
|
+
disabled,
|
|
177
|
+
"aria-label": labels?.pageSize ?? "Items per page"
|
|
178
|
+
})
|
|
179
|
+
}),
|
|
180
|
+
showQuickJumper && /* @__PURE__ */ jsxs("form", {
|
|
181
|
+
"data-slot": "pagination-jumper",
|
|
182
|
+
className: cn("ms-2 flex items-center gap-1", classNames?.jumper),
|
|
183
|
+
onSubmit: jumpToPage,
|
|
184
|
+
noValidate: true,
|
|
185
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
186
|
+
type: "text",
|
|
187
|
+
inputMode: "numeric",
|
|
188
|
+
autoComplete: "off",
|
|
189
|
+
"aria-label": labels?.jump ?? "Jump to page",
|
|
190
|
+
value: jumpValue,
|
|
191
|
+
onChange: (event) => setJumpValue(event.target.value),
|
|
192
|
+
disabled: disabled || pageCount === 0,
|
|
193
|
+
className: "w-16"
|
|
194
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
195
|
+
htmlType: "submit",
|
|
196
|
+
size: resolvedSize,
|
|
197
|
+
variant: "outlined",
|
|
198
|
+
disabled: disabled || pageCount === 0,
|
|
199
|
+
children: labels?.go ?? "Go"
|
|
200
|
+
})]
|
|
201
|
+
})
|
|
202
|
+
]
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
//#endregion
|
|
206
|
+
export { Pagination as default };
|
|
207
|
+
|
|
208
|
+
//# sourceMappingURL=Pagination.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pagination.mjs","names":[],"sources":["../src/components/molecules/pagination.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\n\nimport {\n ChevronLeft,\n ChevronRight,\n ChevronsLeft,\n ChevronsRight,\n} from 'lucide-react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport Input from '../atoms/input';\nimport Select from '../atoms/select';\n\ntype PageItem = number | 'start-ellipsis' | 'end-ellipsis';\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'nav'>,\n 'children' | 'onChange'\n> {\n /** Total number of items, not pages. */\n total: number;\n /** Controlled number of items per page. */\n pageSize?: number;\n defaultPageSize?: number;\n onPageSizeChange?: (pageSize: number) => void;\n pageSizeOptions?: number[];\n showSizeChanger?: boolean;\n /** Render the total and the 1-based inclusive range of visible items. */\n showTotal?: (total: number, range: [number, number]) => React.ReactNode;\n showQuickJumper?: boolean;\n /** 1-based controlled page. */\n page?: number;\n defaultPage?: number;\n onChange?: (page: number) => void;\n siblingCount?: number;\n boundaryCount?: number;\n showFirstLast?: boolean;\n hideOnSinglePage?: boolean;\n /** Render available page controls as links to these URLs. */\n getPageHref?: (page: number) => string;\n disabled?: boolean;\n size?: 'small' | 'middle' | 'large';\n labels?: {\n navigation?: string;\n page?: (page: number) => string;\n previous?: string;\n next?: string;\n first?: string;\n last?: string;\n pageSize?: string;\n jump?: string;\n go?: string;\n };\n classNames?: {\n item?: string;\n active?: string;\n previous?: string;\n next?: string;\n first?: string;\n last?: string;\n ellipsis?: string;\n total?: string;\n sizeChanger?: string;\n jumper?: string;\n };\n}\n\nconst clampPage = (page: number, pageCount: number) =>\n Math.min(\n Math.max(1, Number.isFinite(page) ? Math.floor(page) : 1),\n Math.max(1, pageCount),\n );\n\nconst normalizePageSize = (value: number) =>\n Number.isFinite(value) && value >= 1 ? Math.floor(value) : 10;\n\nconst getPageItems = (\n page: number,\n pageCount: number,\n siblingCount: number,\n boundaryCount: number,\n): PageItem[] => {\n if (pageCount === 0) {\n return [];\n }\n\n const pages = new Set<number>();\n\n for (let index = 1; index <= boundaryCount && index <= pageCount; index++) {\n pages.add(index);\n pages.add(pageCount - index + 1);\n }\n\n for (\n let index = Math.max(1, page - siblingCount);\n index <= Math.min(pageCount, page + siblingCount);\n index++\n ) {\n pages.add(index);\n }\n\n const sorted = [...pages].sort((a, b) => a - b);\n const items: PageItem[] = [];\n let previous = 0;\n\n for (const current of sorted) {\n const gap = current - previous;\n\n if (gap === 2) {\n items.push(previous + 1);\n } else if (gap > 2) {\n items.push('start-ellipsis');\n }\n\n items.push(current);\n previous = current;\n }\n\n const finalGap = pageCount - previous;\n\n if (finalGap === 1) {\n items.push(pageCount);\n } else if (finalGap > 1) {\n items.push('end-ellipsis');\n }\n\n return items;\n};\n\nconst Pagination = ({\n total,\n pageSize,\n defaultPageSize = 10,\n onPageSizeChange,\n pageSizeOptions = [10, 20, 50, 100],\n showSizeChanger = false,\n showTotal,\n showQuickJumper = false,\n page,\n defaultPage = 1,\n onChange,\n siblingCount = 1,\n boundaryCount = 1,\n showFirstLast = false,\n hideOnSinglePage = false,\n getPageHref,\n disabled = false,\n size,\n labels,\n className,\n classNames,\n 'aria-label': ariaLabel,\n ...props\n}: Props) => {\n const { componentSize, direction } = useConfig();\n const safeTotal = Number.isFinite(total) ? Math.max(0, Math.floor(total)) : 0;\n const [internalPageSize, setInternalPageSize] = useState(() =>\n normalizePageSize(defaultPageSize),\n );\n const safePageSize = normalizePageSize(pageSize ?? internalPageSize);\n const pageCount = Math.ceil(safeTotal / safePageSize);\n const [jumpValue, setJumpValue] = useState('');\n const [internal, setInternal] = useState({\n page: clampPage(defaultPage, pageCount),\n pageCount,\n });\n\n // React retries this render with the adjusted state when the available\n // range changes. Controlled callers still own their page prop.\n if (page === undefined && internal.pageCount !== pageCount) {\n setInternal({ page: clampPage(internal.page, pageCount), pageCount });\n }\n\n const currentPage = clampPage(page ?? internal.page, pageCount);\n const resolvedSize = size ?? componentSize ?? 'middle';\n const safeSiblingCount = Number.isFinite(siblingCount)\n ? Math.max(0, Math.floor(siblingCount))\n : 1;\n const safeBoundaryCount = Number.isFinite(boundaryCount)\n ? Math.max(0, Math.floor(boundaryCount))\n : 1;\n const items = getPageItems(\n currentPage,\n pageCount,\n safeSiblingCount,\n safeBoundaryCount,\n );\n const sizeOptions = Array.from(\n new Set(\n [...pageSizeOptions, safePageSize].filter(\n value => Number.isInteger(value) && value > 0,\n ),\n ),\n ).sort((a, b) => a - b);\n const range: [number, number] =\n safeTotal === 0\n ? [0, 0]\n : [\n (currentPage - 1) * safePageSize + 1,\n Math.min(currentPage * safePageSize, safeTotal),\n ];\n\n const goToPage = (next: number) => {\n if (\n disabled ||\n pageCount === 0 ||\n next < 1 ||\n next > pageCount ||\n next === currentPage\n ) {\n return;\n }\n\n if (page === undefined) {\n setInternal({ page: next, pageCount });\n }\n\n onChange?.(next);\n };\n\n const changePageSize = (next: number) => {\n if (disabled || next === safePageSize) {\n return;\n }\n\n if (pageSize === undefined) {\n setInternalPageSize(next);\n }\n\n if (page === undefined) {\n setInternal({ page: 1, pageCount: Math.ceil(safeTotal / next) });\n }\n\n onPageSizeChange?.(next);\n\n if (currentPage !== 1) {\n onChange?.(1);\n }\n };\n\n const jumpToPage = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!/^\\d+$/.test(jumpValue.trim())) {\n return;\n }\n\n const next = Number(jumpValue.trim());\n\n if (!Number.isSafeInteger(next) || next < 1 || next > pageCount) {\n return;\n }\n\n goToPage(next);\n setJumpValue('');\n };\n\n const handlePageClick = (next: number, event: React.MouseEvent) => {\n if (\n getPageHref &&\n (event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey ||\n event.button !== 0)\n ) {\n return;\n }\n\n if (getPageHref && next === currentPage) {\n event.preventDefault();\n }\n\n goToPage(next);\n };\n\n const linkProps = (target: number, isDisabled: boolean) =>\n getPageHref && !isDisabled\n ? {\n render: <a href={getPageHref(target)} />,\n nativeButton: false,\n role: 'link',\n }\n : {};\n\n if (hideOnSinglePage && pageCount <= 1) {\n return null;\n }\n\n return (\n <nav\n {...props}\n data-slot=\"pagination\"\n aria-label={ariaLabel ?? labels?.navigation ?? 'Pagination'}\n className={cn(\n 'flex flex-wrap items-center gap-1',\n className,\n //\n )}\n >\n {showTotal && (\n <span\n data-slot=\"pagination-total\"\n className={cn(\n 'text-muted-foreground me-2 text-sm',\n classNames?.total,\n //\n )}\n >\n {showTotal(safeTotal, range)}\n </span>\n )}\n {showFirstLast && (\n <Button\n {...linkProps(1, disabled || pageCount === 0 || currentPage === 1)}\n icon={direction === 'rtl' ? <ChevronsRight /> : <ChevronsLeft />}\n size={resolvedSize}\n variant=\"outlined\"\n aria-label={labels?.first ?? 'First page'}\n disabled={disabled || pageCount === 0 || currentPage === 1}\n className={cn(\n classNames?.item,\n classNames?.first,\n //\n )}\n onClick={event => handlePageClick(1, event)}\n />\n )}\n <Button\n {...linkProps(\n currentPage - 1,\n disabled || pageCount === 0 || currentPage === 1,\n )}\n icon={direction === 'rtl' ? <ChevronRight /> : <ChevronLeft />}\n size={resolvedSize}\n variant=\"outlined\"\n aria-label={labels?.previous ?? 'Previous page'}\n disabled={disabled || pageCount === 0 || currentPage === 1}\n className={cn(\n classNames?.item,\n classNames?.previous,\n //\n )}\n onClick={event => handlePageClick(currentPage - 1, event)}\n />\n {items.map((item, index) =>\n typeof item === 'number' ? (\n <Button\n key={item}\n {...linkProps(item, disabled)}\n size={resolvedSize}\n variant={item === currentPage ? 'solid' : 'outlined'}\n color={item === currentPage ? 'primary' : 'default'}\n aria-current={item === currentPage ? 'page' : undefined}\n aria-label={labels?.page?.(item) ?? `Page ${item}`}\n disabled={disabled}\n className={cn(\n 'min-w-8',\n classNames?.item,\n item === currentPage && classNames?.active,\n //\n )}\n onClick={event => handlePageClick(item, event)}\n >\n {item}\n </Button>\n ) : (\n <span\n key={`${item}-${index}`}\n aria-hidden=\"true\"\n className={cn(\n `text-muted-foreground inline-flex min-w-6 items-center\n justify-center`,\n classNames?.ellipsis,\n //\n )}\n >\n …\n </span>\n ),\n )}\n <Button\n {...linkProps(\n currentPage + 1,\n disabled || pageCount === 0 || currentPage === pageCount,\n )}\n icon={direction === 'rtl' ? <ChevronLeft /> : <ChevronRight />}\n size={resolvedSize}\n variant=\"outlined\"\n aria-label={labels?.next ?? 'Next page'}\n disabled={disabled || pageCount === 0 || currentPage === pageCount}\n className={cn(\n classNames?.item,\n classNames?.next,\n //\n )}\n onClick={event => handlePageClick(currentPage + 1, event)}\n />\n {showFirstLast && (\n <Button\n {...linkProps(\n pageCount,\n disabled || pageCount === 0 || currentPage === pageCount,\n )}\n icon={direction === 'rtl' ? <ChevronsLeft /> : <ChevronsRight />}\n size={resolvedSize}\n variant=\"outlined\"\n aria-label={labels?.last ?? 'Last page'}\n disabled={disabled || pageCount === 0 || currentPage === pageCount}\n className={cn(\n classNames?.item,\n classNames?.last,\n //\n )}\n onClick={event => handlePageClick(pageCount, event)}\n />\n )}\n {showSizeChanger && (\n <div\n data-slot=\"pagination-size-changer\"\n className={cn(\n 'ms-2 min-w-24',\n classNames?.sizeChanger,\n //\n )}\n >\n <Select\n value={String(safePageSize)}\n options={sizeOptions.map(value => ({\n value: String(value),\n label: `${value} / page`,\n }))}\n onChange={value => changePageSize(Number(value))}\n disabled={disabled}\n aria-label={labels?.pageSize ?? 'Items per page'}\n />\n </div>\n )}\n {showQuickJumper && (\n <form\n data-slot=\"pagination-jumper\"\n className={cn(\n 'ms-2 flex items-center gap-1',\n classNames?.jumper,\n //\n )}\n onSubmit={jumpToPage}\n noValidate\n >\n <Input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n aria-label={labels?.jump ?? 'Jump to page'}\n value={jumpValue}\n onChange={event => setJumpValue(event.target.value)}\n disabled={disabled || pageCount === 0}\n className=\"w-16\"\n />\n <Button\n htmlType=\"submit\"\n size={resolvedSize}\n variant=\"outlined\"\n disabled={disabled || pageCount === 0}\n >\n {labels?.go ?? 'Go'}\n </Button>\n </form>\n )}\n </nav>\n );\n};\n\nexport default Pagination;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ComponentSize } from "./providers/config/types.mjs";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
import { ToggleGroup } from "@base-ui/react/toggle-group";
|
|
4
|
+
//#region src/components/atoms/segmented.d.ts
|
|
5
|
+
interface Option {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string;
|
|
8
|
+
icon?: React$1.ReactNode;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface Props extends Omit<ToggleGroup.Props, 'children' | 'value' | 'defaultValue' | 'onValueChange' | 'onChange' | 'multiple'> {
|
|
12
|
+
options: readonly (string | Option)[];
|
|
13
|
+
value?: string;
|
|
14
|
+
defaultValue?: string;
|
|
15
|
+
/** Clicking the selected item keeps it selected. */
|
|
16
|
+
onChange?: (value: string) => void;
|
|
17
|
+
size?: ComponentSize;
|
|
18
|
+
classNames?: {
|
|
19
|
+
item?: string;
|
|
20
|
+
activeItem?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
declare const Segmented: ({ options, value, defaultValue, onChange, size, className, classNames, disabled, orientation, ...props }: Props) => React$1.JSX.Element;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Option, Props, Segmented as default };
|
|
26
|
+
//# sourceMappingURL=Segmented.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Segmented.d.mts","names":[],"sources":["../src/components/atoms/segmented.tsx"],"mappings":";;;;UAUiB;EACf;EACA;EACA,OAAO,QAAM;EACb;;UASe,cAAc,KAC7B,YAAY;EAGZ,4BAA4B;EAC5B;EACA;;EAEA,YAAY;EACZ,OAAO;EACP;IACE;IACA;;;cAIE,cAAa,SAAA,OAAA,cAAA,UAAA,MAAA,WAAA,YAAA,UAAA,gBAAA,SAWhB,UAAK,QAAA,IAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./utils.mjs";
|
|
3
|
+
import { useConfig } from "./providers/config/context.mjs";
|
|
4
|
+
import * as React$1 from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Toggle } from "@base-ui/react/toggle";
|
|
7
|
+
import { ToggleGroup } from "@base-ui/react/toggle-group";
|
|
8
|
+
//#region src/components/atoms/segmented.tsx
|
|
9
|
+
const sizeClasses = {
|
|
10
|
+
small: "min-h-7 px-2 text-xs",
|
|
11
|
+
middle: "min-h-8 px-3 text-sm",
|
|
12
|
+
large: "min-h-10 px-4 text-base"
|
|
13
|
+
};
|
|
14
|
+
const Segmented = ({ options, value, defaultValue, onChange, size, className, classNames, disabled, orientation = "horizontal", ...props }) => {
|
|
15
|
+
const normalizedOptions = options.map((option) => typeof option === "string" ? {
|
|
16
|
+
label: option,
|
|
17
|
+
value: option,
|
|
18
|
+
disabled: false,
|
|
19
|
+
icon: null
|
|
20
|
+
} : option);
|
|
21
|
+
const firstEnabled = normalizedOptions.find((option) => !option.disabled)?.value;
|
|
22
|
+
const [internalValue, setInternalValue] = React$1.useState(defaultValue ?? firstEnabled);
|
|
23
|
+
const selectedValue = value !== void 0 ? value : internalValue;
|
|
24
|
+
const { componentSize } = useConfig();
|
|
25
|
+
const resolvedSize = size ?? componentSize ?? "middle";
|
|
26
|
+
const handleValueChange = (values) => {
|
|
27
|
+
const nextValue = values[0];
|
|
28
|
+
if (nextValue === void 0 || nextValue === selectedValue) return;
|
|
29
|
+
if (value === void 0) setInternalValue(nextValue);
|
|
30
|
+
onChange?.(nextValue);
|
|
31
|
+
};
|
|
32
|
+
return /* @__PURE__ */ jsx(ToggleGroup, {
|
|
33
|
+
...props,
|
|
34
|
+
"data-slot": "segmented",
|
|
35
|
+
disabled,
|
|
36
|
+
orientation,
|
|
37
|
+
value: selectedValue === void 0 ? [] : [selectedValue],
|
|
38
|
+
onValueChange: handleValueChange,
|
|
39
|
+
className: cn(`bg-muted inline-flex w-fit gap-0.5 rounded-lg p-1
|
|
40
|
+
data-[orientation=vertical]:flex-col`, className),
|
|
41
|
+
children: normalizedOptions.map((option) => /* @__PURE__ */ jsxs(Toggle, {
|
|
42
|
+
value: option.value,
|
|
43
|
+
disabled: option.disabled,
|
|
44
|
+
className: cn(`text-muted-foreground hover:text-foreground
|
|
45
|
+
focus-visible:ring-ring/50 data-pressed:bg-background
|
|
46
|
+
data-pressed:text-foreground inline-flex items-center justify-center
|
|
47
|
+
gap-1.5 rounded-md font-medium whitespace-nowrap outline-none
|
|
48
|
+
focus-visible:ring-[3px] disabled:cursor-not-allowed
|
|
49
|
+
disabled:opacity-50 data-pressed:shadow-xs`, sizeClasses[resolvedSize], classNames?.item, selectedValue === option.value && classNames?.activeItem),
|
|
50
|
+
children: [option.icon, option.label]
|
|
51
|
+
}, option.value))
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
export { Segmented as default };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=Segmented.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Segmented.mjs","names":["React"],"sources":["../src/components/atoms/segmented.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Toggle } from '@base-ui/react/toggle';\nimport { ToggleGroup } from '@base-ui/react/toggle-group';\n\nimport { type ComponentSize, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nexport interface Option {\n label: string;\n value: string;\n icon?: React.ReactNode;\n disabled?: boolean;\n}\n\nconst sizeClasses: Record<ComponentSize, string> = {\n small: 'min-h-7 px-2 text-xs',\n middle: 'min-h-8 px-3 text-sm',\n large: 'min-h-10 px-4 text-base',\n};\n\nexport interface Props extends Omit<\n ToggleGroup.Props,\n 'children' | 'value' | 'defaultValue' | 'onValueChange' | 'onChange' | 'multiple'\n> {\n options: readonly (string | Option)[];\n value?: string;\n defaultValue?: string;\n /** Clicking the selected item keeps it selected. */\n onChange?: (value: string) => void;\n size?: ComponentSize;\n classNames?: {\n item?: string;\n activeItem?: string;\n };\n}\n\nconst Segmented = ({\n options,\n value,\n defaultValue,\n onChange,\n size,\n className,\n classNames,\n disabled,\n orientation = 'horizontal',\n ...props\n}: Props) => {\n const normalizedOptions = options.map(option =>\n typeof option === 'string'\n ? { label: option, value: option, disabled: false, icon: null }\n : option,\n );\n const firstEnabled = normalizedOptions.find(\n option => !option.disabled,\n )?.value;\n const [internalValue, setInternalValue] = React.useState(\n defaultValue ?? firstEnabled,\n );\n const selectedValue = value !== undefined ? value : internalValue;\n const { componentSize } = useConfig();\n const resolvedSize = size ?? componentSize ?? 'middle';\n\n const handleValueChange = (values: string[]) => {\n const nextValue = values[0];\n\n if (nextValue === undefined || nextValue === selectedValue) {\n return;\n }\n\n if (value === undefined) {\n setInternalValue(nextValue);\n }\n\n onChange?.(nextValue);\n };\n\n return (\n <ToggleGroup\n {...props}\n data-slot=\"segmented\"\n disabled={disabled}\n orientation={orientation}\n value={selectedValue === undefined ? [] : [selectedValue]}\n onValueChange={handleValueChange}\n className={cn(\n `bg-muted inline-flex w-fit gap-0.5 rounded-lg p-1\n data-[orientation=vertical]:flex-col`,\n className,\n //\n )}\n >\n {normalizedOptions.map(option => (\n <Toggle\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n className={cn(\n `text-muted-foreground hover:text-foreground\n focus-visible:ring-ring/50 data-pressed:bg-background\n data-pressed:text-foreground inline-flex items-center justify-center\n gap-1.5 rounded-md font-medium whitespace-nowrap outline-none\n focus-visible:ring-[3px] disabled:cursor-not-allowed\n disabled:opacity-50 data-pressed:shadow-xs`,\n sizeClasses[resolvedSize],\n classNames?.item,\n selectedValue === option.value && classNames?.activeItem,\n //\n )}\n >\n {option.icon}\n {option.label}\n </Toggle>\n ))}\n </ToggleGroup>\n );\n};\n\nexport default Segmented;\n"],"mappings":";;;;;;;;AAiBA,MAAM,cAA6C;CACjD,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAkBA,MAAM,aAAa,EACjB,SACA,OACA,cACA,UACA,MACA,WACA,YACA,UACA,cAAc,cACd,GAAG,YACQ;CACX,MAAM,oBAAoB,QAAQ,KAAI,WACpC,OAAO,WAAW,WACd;EAAE,OAAO;EAAQ,OAAO;EAAQ,UAAU;EAAO,MAAM;CAAK,IAC5D,MACN;CACA,MAAM,eAAe,kBAAkB,MACrC,WAAU,CAAC,OAAO,QACpB,CAAC,EAAE;CACH,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAC9C,gBAAgB,YAClB;CACA,MAAM,gBAAgB,UAAU,KAAA,IAAY,QAAQ;CACpD,MAAM,EAAE,kBAAkB,UAAU;CACpC,MAAM,eAAe,QAAQ,iBAAiB;CAE9C,MAAM,qBAAqB,WAAqB;EAC9C,MAAM,YAAY,OAAO;EAEzB,IAAI,cAAc,KAAA,KAAa,cAAc,eAC3C;EAGF,IAAI,UAAU,KAAA,GACZ,iBAAiB,SAAS;EAG5B,WAAW,SAAS;CACtB;CAEA,OACE,oBAAC,aAAD;EACE,GAAI;EACJ,aAAU;EACA;EACG;EACb,OAAO,kBAAkB,KAAA,IAAY,CAAC,IAAI,CAAC,aAAa;EACxD,eAAe;EACf,WAAW,GACT;+CAEA,SAEF;EAEC,UAAA,kBAAkB,KAAI,WACrB,qBAAC,QAAD;GAEE,OAAO,OAAO;GACd,UAAU,OAAO;GACjB,WAAW,GACT;;;;;yDAMA,YAAY,eACZ,YAAY,MACZ,kBAAkB,OAAO,SAAS,YAAY,UAEhD;GAfF,UAAA,CAiBG,OAAO,MACP,OAAO,KACF;EAlBD,GAAA,OAAO,KAkBN,CACT;CACU,CAAA;AAEjB"}
|
package/dist/Steps.d.mts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
//#region src/components/molecules/steps.d.ts
|
|
3
|
+
interface Item {
|
|
4
|
+
key?: React$1.Key;
|
|
5
|
+
title: React$1.ReactNode;
|
|
6
|
+
description?: React$1.ReactNode;
|
|
7
|
+
icon?: React$1.ReactNode;
|
|
8
|
+
/** Mark a failed step regardless of its position. */
|
|
9
|
+
status?: 'error';
|
|
10
|
+
}
|
|
11
|
+
interface Props extends Omit<React$1.ComponentPropsWithoutRef<'nav'>, 'children'> {
|
|
12
|
+
items: readonly Item[];
|
|
13
|
+
/** Zero-based index of the current step. */
|
|
14
|
+
current?: number;
|
|
15
|
+
orientation?: 'horizontal' | 'vertical';
|
|
16
|
+
classNames?: {
|
|
17
|
+
list?: string;
|
|
18
|
+
item?: string;
|
|
19
|
+
indicator?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
connector?: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
declare const Steps: ({ items, current, orientation, className, classNames, dir, "aria-label": ariaLabel, ...props }: Props) => React$1.JSX.Element;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { Item, Props, Steps as default };
|
|
28
|
+
//# sourceMappingURL=Steps.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Steps.d.mts","names":[],"sources":["../src/components/molecules/steps.tsx"],"mappings":";;UASiB;EACf,MAAM,QAAM;EACZ,OAAO,QAAM;EACb,cAAc,QAAM;EACpB,OAAO,QAAM;;EAEb;;UAGe,cAAc,KAC7B,QAAM;EAGN,gBAAgB;;EAEhB;EACA;EACA;IACE;IACA;IACA;IACA;IACA;IACA;;;cAIE,UAAS,OAAA,SAAA,aAAA,WAAA,YAAA,mBAAA,cAAA,SASZ,UAAK,QAAA,IAAA"}
|
package/dist/Steps.mjs
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./utils.mjs";
|
|
3
|
+
import { useConfig } from "./providers/config/context.mjs";
|
|
4
|
+
import { Check, TriangleAlert } from "lucide-react";
|
|
5
|
+
import "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/molecules/steps.tsx
|
|
8
|
+
const Steps = ({ items, current = 0, orientation = "horizontal", className, classNames, dir, "aria-label": ariaLabel, ...props }) => {
|
|
9
|
+
const { direction } = useConfig();
|
|
10
|
+
const currentIndex = items.length ? Math.min(Math.max(Number.isFinite(current) ? Math.floor(current) : 0, 0), items.length - 1) : -1;
|
|
11
|
+
return /* @__PURE__ */ jsx("nav", {
|
|
12
|
+
...props,
|
|
13
|
+
"data-slot": "steps",
|
|
14
|
+
"data-orientation": orientation,
|
|
15
|
+
dir: dir ?? direction,
|
|
16
|
+
"aria-label": ariaLabel ?? "Progress",
|
|
17
|
+
className: cn("w-full", className),
|
|
18
|
+
children: /* @__PURE__ */ jsx("ol", {
|
|
19
|
+
"data-slot": "steps-list",
|
|
20
|
+
className: cn("m-0 flex list-none p-0", orientation === "vertical" && "flex-col", classNames?.list),
|
|
21
|
+
children: items.map((item, index) => {
|
|
22
|
+
const status = item.status === "error" ? "error" : index < currentIndex ? "complete" : index === currentIndex ? "current" : "upcoming";
|
|
23
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
24
|
+
"data-slot": "steps-item",
|
|
25
|
+
"data-status": status,
|
|
26
|
+
"aria-current": index === currentIndex ? "step" : void 0,
|
|
27
|
+
className: cn("m-0 flex min-w-0", orientation === "horizontal" ? "flex-1 items-start" : "flex-col", classNames?.item),
|
|
28
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "flex min-w-0 items-start gap-2.5",
|
|
30
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
31
|
+
"data-slot": "steps-indicator",
|
|
32
|
+
"aria-hidden": "true",
|
|
33
|
+
className: cn(`border-border text-muted-foreground flex size-7 shrink-0
|
|
34
|
+
items-center justify-center rounded-full border text-sm
|
|
35
|
+
font-medium`, status === "complete" && "border-primary bg-primary text-primary-foreground", status === "current" && "border-primary text-primary", status === "error" && `border-destructive bg-destructive
|
|
36
|
+
text-destructive-foreground`, classNames?.indicator),
|
|
37
|
+
children: item.icon ?? (status === "complete" ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : status === "error" ? /* @__PURE__ */ jsx(TriangleAlert, { className: "size-4" }) : index + 1)
|
|
38
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
39
|
+
className: "min-w-0",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx("span", {
|
|
42
|
+
"data-slot": "steps-title",
|
|
43
|
+
className: cn("block text-sm font-medium", status === "upcoming" ? "text-muted-foreground" : status === "error" ? "text-destructive" : "text-foreground", classNames?.title),
|
|
44
|
+
children: item.title
|
|
45
|
+
}),
|
|
46
|
+
item.description != null && /* @__PURE__ */ jsx("span", {
|
|
47
|
+
"data-slot": "steps-description",
|
|
48
|
+
className: cn("text-muted-foreground block text-xs", classNames?.description),
|
|
49
|
+
children: item.description
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsx("span", {
|
|
52
|
+
className: "sr-only",
|
|
53
|
+
children: status === "complete" ? "Completed" : status === "current" ? "Current step" : status === "error" ? "Error" : "Upcoming"
|
|
54
|
+
})
|
|
55
|
+
]
|
|
56
|
+
})]
|
|
57
|
+
}), index < items.length - 1 && /* @__PURE__ */ jsx("span", {
|
|
58
|
+
"data-slot": "steps-connector",
|
|
59
|
+
"aria-hidden": "true",
|
|
60
|
+
className: cn("bg-border shrink-0", orientation === "horizontal" ? "mx-3 mt-3.5 h-px min-w-3 flex-1" : "my-2 ms-3.5 h-7 w-px", index < currentIndex && "bg-primary", classNames?.connector)
|
|
61
|
+
})]
|
|
62
|
+
}, item.key ?? index);
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
//#endregion
|
|
68
|
+
export { Steps as default };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=Steps.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Steps.mjs","names":[],"sources":["../src/components/molecules/steps.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Check, TriangleAlert } from 'lucide-react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nexport interface Item {\n key?: React.Key;\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n /** Mark a failed step regardless of its position. */\n status?: 'error';\n}\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'nav'>,\n 'children'\n> {\n items: readonly Item[];\n /** Zero-based index of the current step. */\n current?: number;\n orientation?: 'horizontal' | 'vertical';\n classNames?: {\n list?: string;\n item?: string;\n indicator?: string;\n title?: string;\n description?: string;\n connector?: string;\n };\n}\n\nconst Steps = ({\n items,\n current = 0,\n orientation = 'horizontal',\n className,\n classNames,\n dir,\n 'aria-label': ariaLabel,\n ...props\n}: Props) => {\n const { direction } = useConfig();\n const currentIndex = items.length\n ? Math.min(\n Math.max(Number.isFinite(current) ? Math.floor(current) : 0, 0),\n items.length - 1,\n )\n : -1;\n\n return (\n <nav\n {...props}\n data-slot=\"steps\"\n data-orientation={orientation}\n dir={dir ?? direction}\n aria-label={ariaLabel ?? 'Progress'}\n className={cn('w-full', className)}\n >\n <ol\n data-slot=\"steps-list\"\n className={cn(\n 'm-0 flex list-none p-0',\n orientation === 'vertical' && 'flex-col',\n classNames?.list,\n //\n )}\n >\n {items.map((item, index) => {\n const status =\n item.status === 'error'\n ? 'error'\n : index < currentIndex\n ? 'complete'\n : index === currentIndex\n ? 'current'\n : 'upcoming';\n const isCurrent = index === currentIndex;\n\n return (\n <li\n key={item.key ?? index}\n data-slot=\"steps-item\"\n data-status={status}\n aria-current={isCurrent ? 'step' : undefined}\n className={cn(\n 'm-0 flex min-w-0',\n orientation === 'horizontal'\n ? 'flex-1 items-start'\n : 'flex-col',\n classNames?.item,\n //\n )}\n >\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span\n data-slot=\"steps-indicator\"\n aria-hidden=\"true\"\n className={cn(\n `border-border text-muted-foreground flex size-7 shrink-0\n items-center justify-center rounded-full border text-sm\n font-medium`,\n status === 'complete' &&\n 'border-primary bg-primary text-primary-foreground',\n status === 'current' && 'border-primary text-primary',\n status === 'error' &&\n `border-destructive bg-destructive\n text-destructive-foreground`,\n classNames?.indicator,\n //\n )}\n >\n {item.icon ??\n (status === 'complete' ? (\n <Check className=\"size-4\" />\n ) : status === 'error' ? (\n <TriangleAlert className=\"size-4\" />\n ) : (\n index + 1\n ))}\n </span>\n <span className=\"min-w-0\">\n <span\n data-slot=\"steps-title\"\n className={cn(\n 'block text-sm font-medium',\n status === 'upcoming'\n ? 'text-muted-foreground'\n : status === 'error'\n ? 'text-destructive'\n : 'text-foreground',\n classNames?.title,\n //\n )}\n >\n {item.title}\n </span>\n {item.description != null && (\n <span\n data-slot=\"steps-description\"\n className={cn(\n 'text-muted-foreground block text-xs',\n classNames?.description,\n )}\n >\n {item.description}\n </span>\n )}\n <span className=\"sr-only\">\n {status === 'complete'\n ? 'Completed'\n : status === 'current'\n ? 'Current step'\n : status === 'error'\n ? 'Error'\n : 'Upcoming'}\n </span>\n </span>\n </div>\n {index < items.length - 1 && (\n <span\n data-slot=\"steps-connector\"\n aria-hidden=\"true\"\n className={cn(\n 'bg-border shrink-0',\n orientation === 'horizontal'\n ? 'mx-3 mt-3.5 h-px min-w-3 flex-1'\n : 'my-2 ms-3.5 h-7 w-px',\n index < currentIndex && 'bg-primary',\n classNames?.connector,\n //\n )}\n />\n )}\n </li>\n );\n })}\n </ol>\n </nav>\n );\n};\n\nexport default Steps;\n"],"mappings":";;;;;;;AAoCA,MAAM,SAAS,EACb,OACA,UAAU,GACV,cAAc,cACd,WACA,YACA,KACA,cAAc,WACd,GAAG,YACQ;CACX,MAAM,EAAE,cAAc,UAAU;CAChC,MAAM,eAAe,MAAM,SACvB,KAAK,IACH,KAAK,IAAI,OAAO,SAAS,OAAO,IAAI,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,GAC9D,MAAM,SAAS,CACjB,IACA;CAEJ,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,aAAU;EACV,oBAAkB;EAClB,KAAK,OAAO;EACZ,cAAY,aAAa;EACzB,WAAW,GAAG,UAAU,SAAS;EAEjC,UAAA,oBAAC,MAAD;GACE,aAAU;GACV,WAAW,GACT,0BACA,gBAAgB,cAAc,YAC9B,YAAY,IAEd;GAEC,UAAA,MAAM,KAAK,MAAM,UAAU;IAC1B,MAAM,SACJ,KAAK,WAAW,UACZ,UACA,QAAQ,eACN,aACA,UAAU,eACR,YACA;IAGV,OACE,qBAAC,MAAD;KAEE,aAAU;KACV,eAAa;KACb,gBAPc,UAAU,eAOE,SAAS,KAAA;KACnC,WAAW,GACT,oBACA,gBAAgB,eACZ,uBACA,YACJ,YAAY,IAEd;KAZF,UAAA,CAcE,qBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,oBAAC,QAAD;OACE,aAAU;OACV,eAAY;OACZ,WAAW,GACT;;kCAGA,WAAW,cACT,qDACF,WAAW,aAAa,+BACxB,WAAW,WACT;oDAEF,YAAY,SAEd;OAEC,UAAA,KAAK,SACH,WAAW,aACV,oBAAC,OAAD,EAAO,WAAU,SAAU,CAAA,IACzB,WAAW,UACb,oBAAC,eAAD,EAAe,WAAU,SAAU,CAAA,IAEnC,QAAQ;MAER,CAAA,GACN,qBAAC,QAAD;OAAM,WAAU;OAAhB,UAAA;QACE,oBAAC,QAAD;SACE,aAAU;SACV,WAAW,GACT,6BACA,WAAW,aACP,0BACA,WAAW,UACT,qBACA,mBACN,YAAY,KAEd;SAEC,UAAA,KAAK;QACF,CAAA;QACL,KAAK,eAAe,QACnB,oBAAC,QAAD;SACE,aAAU;SACV,WAAW,GACT,uCACA,YAAY,WACd;SAEC,UAAA,KAAK;QACF,CAAA;QAER,oBAAC,QAAD;SAAM,WAAU;SACb,UAAA,WAAW,aACR,cACA,WAAW,YACT,iBACA,WAAW,UACT,UACA;QACJ,CAAA;OACF;MACH,CAAA,CAAA;KACJ,CAAA,GAAA,QAAQ,MAAM,SAAS,KACtB,oBAAC,QAAD;MACE,aAAU;MACV,eAAY;MACZ,WAAW,GACT,sBACA,gBAAgB,eACZ,oCACA,wBACJ,QAAQ,gBAAgB,cACxB,YAAY,SAEd;KACD,CAAA,CAED;IA7FG,GAAA,KAAK,OAAO,KA6Ff;GAER,CAAC;EACC,CAAA;CACD,CAAA;AAET"}
|
package/dist/Table.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Props as Props$1 } from "./Pagination.mjs";
|
|
2
|
+
//#region src/components/organisms/table.d.ts
|
|
3
|
+
type PaginationOptions = Omit<Props$1, 'total'> & ({
|
|
4
|
+
mode?: 'client';
|
|
5
|
+
total?: never;
|
|
6
|
+
} | {
|
|
7
|
+
mode: 'server';
|
|
8
|
+
total: number;
|
|
9
|
+
});
|
|
10
|
+
interface Column<T> {
|
|
11
|
+
key: React.Key;
|
|
12
|
+
title: React.ReactNode;
|
|
13
|
+
/** Field displayed when render is omitted. */
|
|
14
|
+
dataIndex?: keyof T;
|
|
15
|
+
render?: (record: T, index: number) => React.ReactNode;
|
|
16
|
+
align?: 'left' | 'center' | 'right';
|
|
17
|
+
width?: string | number;
|
|
18
|
+
className?: string;
|
|
19
|
+
headerClassName?: string;
|
|
20
|
+
}
|
|
21
|
+
interface Props<T> extends Omit<React.ComponentPropsWithoutRef<'table'>, 'children'> {
|
|
22
|
+
columns: Column<T>[];
|
|
23
|
+
data?: T[];
|
|
24
|
+
/** Use a stable key when rows may be reordered or paged. */
|
|
25
|
+
rowKey?: (record: T, index: number) => React.Key;
|
|
26
|
+
caption?: React.ReactNode;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
empty?: React.ReactNode;
|
|
29
|
+
size?: 'small' | 'middle' | 'large';
|
|
30
|
+
bordered?: boolean;
|
|
31
|
+
striped?: boolean;
|
|
32
|
+
hoverable?: boolean;
|
|
33
|
+
/** true paginates data locally; server mode expects current-page data. */
|
|
34
|
+
pagination?: boolean | PaginationOptions;
|
|
35
|
+
classNames?: {
|
|
36
|
+
container?: string;
|
|
37
|
+
header?: string;
|
|
38
|
+
body?: string;
|
|
39
|
+
row?: string;
|
|
40
|
+
cell?: string;
|
|
41
|
+
empty?: string;
|
|
42
|
+
pagination?: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
declare const Table: <T>({ columns, data, rowKey, loading, empty, size, bordered, striped, hoverable, pagination, caption, className, classNames, ...props }: Props<T>) => import("react").JSX.Element;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Column, PaginationOptions, Props, Table as default };
|
|
48
|
+
//# sourceMappingURL=Table.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Table.d.mts","names":[],"sources":["../src/components/organisms/table.tsx"],"mappings":";;KAaY,oBAAoB,KAAK;EAChC;EAAiB;;EAAoB;EAAgB;;UAEzC,OAAO;EACtB,KAAK,MAAM;EACX,OAAO,MAAM;;EAEb,kBAAkB;EAClB,UAAU,QAAQ,GAAG,kBAAkB,MAAM;EAC7C;EACA;EACA;EACA;;UAGe,MAAM,WAAW,KAChC,MAAM;EAGN,SAAS,OAAO;EAChB,OAAO;;EAEP,UAAU,QAAQ,GAAG,kBAAkB,MAAM;EAC7C,UAAU,MAAM;EAChB;EACA,QAAQ,MAAM;EACd;EACA;EACA;EACA;;EAEA,uBAAuB;EACvB;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;;cAaE,QAAS,KAAI,SAAA,MAAA,QAAA,SAAA,OAAA,MAAA,UAAA,SAAA,WAAA,YAAA,SAAA,WAAA,eAAA,SAehB,MAAM,uBAAE,IAAA"}
|
package/dist/Table.mjs
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./utils.mjs";
|
|
3
|
+
import { useConfig } from "./providers/config/context.mjs";
|
|
4
|
+
import Skeleton from "./components/atoms/skeleton/index.mjs";
|
|
5
|
+
import Pagination from "./Pagination.mjs";
|
|
6
|
+
import Empty from "./components/templates/empty.mjs";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/components/organisms/table.tsx
|
|
10
|
+
const clampPage = (page, pageCount) => Math.min(Math.max(1, Number.isFinite(page) ? Math.floor(page) : 1), Math.max(1, pageCount));
|
|
11
|
+
const normalizePageSize = (value) => Number.isFinite(value) && value >= 1 ? Math.floor(value) : 10;
|
|
12
|
+
const Table = ({ columns, data = [], rowKey, loading = false, empty, size, bordered = false, striped = false, hoverable = true, pagination = false, caption, className, classNames, ...props }) => {
|
|
13
|
+
const { componentSize } = useConfig();
|
|
14
|
+
const { mode = "client", total: serverTotal, page: controlledPage, defaultPage = 1, pageSize: controlledPageSize, defaultPageSize = 10, onChange, onPageSizeChange, ...paginationProps } = typeof pagination === "object" ? pagination : {};
|
|
15
|
+
const [internal, setInternal] = useState(() => ({
|
|
16
|
+
page: defaultPage,
|
|
17
|
+
pageSize: normalizePageSize(defaultPageSize),
|
|
18
|
+
pageCount: 0
|
|
19
|
+
}));
|
|
20
|
+
const pageSize = normalizePageSize(controlledPageSize ?? internal.pageSize);
|
|
21
|
+
const total = mode === "server" ? serverTotal ?? 0 : data.length;
|
|
22
|
+
const pageCount = Math.ceil(total / pageSize);
|
|
23
|
+
if (pagination && controlledPage === void 0 && internal.pageCount !== pageCount) setInternal((current) => ({
|
|
24
|
+
...current,
|
|
25
|
+
page: clampPage(current.page, pageCount),
|
|
26
|
+
pageCount
|
|
27
|
+
}));
|
|
28
|
+
const page = clampPage(controlledPage ?? internal.page, pageCount);
|
|
29
|
+
const visibleData = pagination && mode === "client" ? data.slice((page - 1) * pageSize, page * pageSize) : data;
|
|
30
|
+
const cellPadding = {
|
|
31
|
+
small: "px-3 py-2",
|
|
32
|
+
middle: "px-4 py-3",
|
|
33
|
+
large: "px-5 py-4"
|
|
34
|
+
}[size ?? componentSize ?? "middle"];
|
|
35
|
+
const handlePageChange = (next) => {
|
|
36
|
+
if (controlledPage === void 0) setInternal((current) => ({
|
|
37
|
+
...current,
|
|
38
|
+
page: next
|
|
39
|
+
}));
|
|
40
|
+
onChange?.(next);
|
|
41
|
+
};
|
|
42
|
+
const handlePageSizeChange = (next) => {
|
|
43
|
+
if (controlledPageSize === void 0 || controlledPage === void 0) setInternal((current) => ({
|
|
44
|
+
...current,
|
|
45
|
+
page: controlledPage === void 0 ? 1 : current.page,
|
|
46
|
+
pageSize: controlledPageSize === void 0 ? next : current.pageSize,
|
|
47
|
+
pageCount: Math.ceil(total / next)
|
|
48
|
+
}));
|
|
49
|
+
onPageSizeChange?.(next);
|
|
50
|
+
};
|
|
51
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
52
|
+
"data-slot": "table-container",
|
|
53
|
+
className: cn("w-full overflow-x-auto", bordered && "rounded-md border", classNames?.container),
|
|
54
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
55
|
+
...props,
|
|
56
|
+
"data-slot": "table",
|
|
57
|
+
"aria-busy": loading || void 0,
|
|
58
|
+
className: cn("w-full min-w-max border-collapse text-sm", className),
|
|
59
|
+
children: [
|
|
60
|
+
caption && /* @__PURE__ */ jsx("caption", {
|
|
61
|
+
className: "mb-2 text-start",
|
|
62
|
+
children: caption
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ jsx("thead", {
|
|
65
|
+
"data-slot": "table-header",
|
|
66
|
+
className: cn("bg-muted/50 border-b", classNames?.header),
|
|
67
|
+
children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx("th", {
|
|
68
|
+
scope: "col",
|
|
69
|
+
style: { width: column.width },
|
|
70
|
+
className: cn("text-foreground font-medium", cellPadding, column.align === "center" && "text-center", column.align === "right" && "text-right", column.align === void 0 && "text-start", bordered && "border-e last:border-e-0", classNames?.cell, column.headerClassName),
|
|
71
|
+
children: column.title
|
|
72
|
+
}, column.key)) })
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ jsx("tbody", {
|
|
75
|
+
"data-slot": "table-body",
|
|
76
|
+
className: classNames?.body,
|
|
77
|
+
children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
|
|
78
|
+
colSpan: Math.max(columns.length, 1),
|
|
79
|
+
className: cellPadding,
|
|
80
|
+
children: /* @__PURE__ */ jsx(Skeleton, {
|
|
81
|
+
count: 3,
|
|
82
|
+
className: "py-2"
|
|
83
|
+
})
|
|
84
|
+
}) }) : visibleData.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
|
|
85
|
+
colSpan: Math.max(columns.length, 1),
|
|
86
|
+
className: cn(cellPadding, classNames?.empty),
|
|
87
|
+
children: empty ?? /* @__PURE__ */ jsx(Empty, {})
|
|
88
|
+
}) }) : visibleData.map((record, index) => /* @__PURE__ */ jsx("tr", {
|
|
89
|
+
"data-slot": "table-row",
|
|
90
|
+
className: cn("border-b last:border-b-0", striped && "even:bg-muted/30", hoverable && "hover:bg-muted/50", classNames?.row),
|
|
91
|
+
children: columns.map((column) => {
|
|
92
|
+
const value = column.dataIndex === void 0 ? void 0 : record[column.dataIndex];
|
|
93
|
+
return /* @__PURE__ */ jsx("td", {
|
|
94
|
+
"data-slot": "table-cell",
|
|
95
|
+
className: cn(cellPadding, column.align === "center" && "text-center", column.align === "right" && "text-right", bordered && "border-e last:border-e-0", classNames?.cell, column.className),
|
|
96
|
+
children: column.render ? column.render(record, index) : value == null ? null : String(value)
|
|
97
|
+
}, column.key);
|
|
98
|
+
})
|
|
99
|
+
}, rowKey?.(record, index) ?? index))
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
})
|
|
103
|
+
}), pagination && /* @__PURE__ */ jsx("div", {
|
|
104
|
+
"data-slot": "table-pagination",
|
|
105
|
+
className: cn("mt-4 flex justify-end", classNames?.pagination),
|
|
106
|
+
children: /* @__PURE__ */ jsx(Pagination, {
|
|
107
|
+
...paginationProps,
|
|
108
|
+
total,
|
|
109
|
+
page,
|
|
110
|
+
pageSize,
|
|
111
|
+
onChange: handlePageChange,
|
|
112
|
+
onPageSizeChange: handlePageSizeChange
|
|
113
|
+
})
|
|
114
|
+
})] });
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { Table as default };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=Table.mjs.map
|