@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.
Files changed (176) hide show
  1. package/README.ko.md +7 -0
  2. package/README.md +7 -0
  3. package/dist/Alert.d.mts +24 -0
  4. package/dist/Alert.d.mts.map +1 -0
  5. package/dist/Alert.mjs +68 -0
  6. package/dist/Alert.mjs.map +1 -0
  7. package/dist/Avatar.d.mts +19 -0
  8. package/dist/Avatar.d.mts.map +1 -0
  9. package/dist/Avatar.mjs +43 -0
  10. package/dist/Avatar.mjs.map +1 -0
  11. package/dist/Badge.d.mts +23 -0
  12. package/dist/Badge.d.mts.map +1 -0
  13. package/dist/Badge.mjs +41 -0
  14. package/dist/Badge.mjs.map +1 -0
  15. package/dist/Breadcrumb.d.mts +39 -0
  16. package/dist/Breadcrumb.d.mts.map +1 -0
  17. package/dist/Breadcrumb.mjs +103 -0
  18. package/dist/Breadcrumb.mjs.map +1 -0
  19. package/dist/Button.mjs +1 -1
  20. package/dist/Pagination.d.mts +53 -0
  21. package/dist/Pagination.d.mts.map +1 -0
  22. package/dist/Pagination.mjs +208 -0
  23. package/dist/Pagination.mjs.map +1 -0
  24. package/dist/Segmented.d.mts +26 -0
  25. package/dist/Segmented.d.mts.map +1 -0
  26. package/dist/Segmented.mjs +57 -0
  27. package/dist/Segmented.mjs.map +1 -0
  28. package/dist/Steps.d.mts +28 -0
  29. package/dist/Steps.d.mts.map +1 -0
  30. package/dist/Steps.mjs +70 -0
  31. package/dist/Steps.mjs.map +1 -0
  32. package/dist/Table.d.mts +48 -0
  33. package/dist/Table.d.mts.map +1 -0
  34. package/dist/Table.mjs +119 -0
  35. package/dist/Table.mjs.map +1 -0
  36. package/dist/components/atoms/checkbox/checkbox.mjs +3 -2
  37. package/dist/components/atoms/checkbox/checkbox.mjs.map +1 -1
  38. package/dist/components/atoms/checkbox/group.mjs +2 -1
  39. package/dist/components/atoms/checkbox/group.mjs.map +1 -1
  40. package/dist/components/atoms/color-picker.mjs +1 -1
  41. package/dist/components/atoms/date-picker.mjs +2 -2
  42. package/dist/components/atoms/float-button/back-top.mjs +2 -2
  43. package/dist/components/atoms/index.d.mts +6 -1
  44. package/dist/components/atoms/input/index.d.mts +3 -1
  45. package/dist/components/atoms/input/index.d.mts.map +1 -1
  46. package/dist/components/atoms/input/index.mjs +3 -1
  47. package/dist/components/atoms/input/index.mjs.map +1 -1
  48. package/dist/components/atoms/input/number.d.mts +23 -0
  49. package/dist/components/atoms/input/number.d.mts.map +1 -0
  50. package/dist/components/atoms/input/number.mjs +71 -0
  51. package/dist/components/atoms/input/number.mjs.map +1 -0
  52. package/dist/components/atoms/input/search.d.mts +0 -5
  53. package/dist/components/atoms/input/search.d.mts.map +1 -1
  54. package/dist/components/atoms/input/search.mjs +8 -8
  55. package/dist/components/atoms/input/search.mjs.map +1 -1
  56. package/dist/components/atoms/popover.mjs +1 -1
  57. package/dist/components/atoms/progress.d.mts +3 -1
  58. package/dist/components/atoms/progress.d.mts.map +1 -1
  59. package/dist/components/atoms/progress.mjs +2 -2
  60. package/dist/components/atoms/progress.mjs.map +1 -1
  61. package/dist/components/atoms/radio/group.mjs +1 -1
  62. package/dist/components/atoms/radio/radio.mjs +3 -2
  63. package/dist/components/atoms/radio/radio.mjs.map +1 -1
  64. package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts +6 -1
  65. package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts.map +1 -1
  66. package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs +7 -2
  67. package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs.map +1 -1
  68. package/dist/components/atoms/rich-text-editor/toolbar-presets.mjs +1 -1
  69. package/dist/components/atoms/select-multiple.d.mts +29 -0
  70. package/dist/components/atoms/select-multiple.d.mts.map +1 -0
  71. package/dist/components/atoms/select-multiple.mjs +97 -0
  72. package/dist/components/atoms/select-multiple.mjs.map +1 -0
  73. package/dist/components/atoms/select-searchable-options.d.mts +21 -0
  74. package/dist/components/atoms/select-searchable-options.d.mts.map +1 -0
  75. package/dist/components/atoms/select-searchable-options.mjs +62 -0
  76. package/dist/components/atoms/select-searchable-options.mjs.map +1 -0
  77. package/dist/components/atoms/select-searchable.d.mts +22 -0
  78. package/dist/components/atoms/select-searchable.d.mts.map +1 -0
  79. package/dist/components/atoms/select-searchable.mjs +65 -0
  80. package/dist/components/atoms/select-searchable.mjs.map +1 -0
  81. package/dist/components/atoms/select.d.mts +8 -2
  82. package/dist/components/atoms/select.d.mts.map +1 -1
  83. package/dist/components/atoms/select.mjs +12 -2
  84. package/dist/components/atoms/select.mjs.map +1 -1
  85. package/dist/components/atoms/skeleton/skeleton.d.mts +3 -1
  86. package/dist/components/atoms/skeleton/skeleton.d.mts.map +1 -1
  87. package/dist/components/atoms/skeleton/skeleton.mjs +2 -2
  88. package/dist/components/atoms/skeleton/skeleton.mjs.map +1 -1
  89. package/dist/components/atoms/slider/slider.mjs +1 -1
  90. package/dist/components/atoms/switch.mjs +1 -1
  91. package/dist/components/atoms/tooltip.d.mts +21 -0
  92. package/dist/components/atoms/tooltip.d.mts.map +1 -0
  93. package/dist/components/atoms/tooltip.mjs +78 -0
  94. package/dist/components/atoms/tooltip.mjs.map +1 -0
  95. package/dist/components/atoms/typography/paragraph.mjs +1 -0
  96. package/dist/components/atoms/typography/paragraph.mjs.map +1 -1
  97. package/dist/components/atoms/typography/title.mjs +1 -0
  98. package/dist/components/atoms/typography/title.mjs.map +1 -1
  99. package/dist/components/molecules/collapse.d.mts +8 -8
  100. package/dist/components/molecules/collapse.d.mts.map +1 -1
  101. package/dist/components/molecules/collapse.mjs +11 -2
  102. package/dist/components/molecules/collapse.mjs.map +1 -1
  103. package/dist/components/molecules/dropdown.mjs +1 -1
  104. package/dist/components/molecules/index.d.mts +15 -10
  105. package/dist/components/molecules/marquees/marquees.mjs +1 -1
  106. package/dist/components/molecules/menu/item/item.mjs +1 -1
  107. package/dist/components/molecules/menu/menu.d.mts +1 -1
  108. package/dist/components/molecules/menu/menu.d.mts.map +1 -1
  109. package/dist/components/molecules/menu/menu.mjs +5 -2
  110. package/dist/components/molecules/menu/menu.mjs.map +1 -1
  111. package/dist/components/molecules/tabs.d.mts +41 -0
  112. package/dist/components/molecules/tabs.d.mts.map +1 -0
  113. package/dist/components/molecules/tabs.mjs +55 -0
  114. package/dist/components/molecules/tabs.mjs.map +1 -0
  115. package/dist/components/molecules/upload.mjs +3 -2
  116. package/dist/components/molecules/upload.mjs.map +1 -1
  117. package/dist/components/organisms/drawer.d.mts +1 -1
  118. package/dist/components/organisms/drawer.d.mts.map +1 -1
  119. package/dist/components/organisms/drawer.mjs +10 -4
  120. package/dist/components/organisms/drawer.mjs.map +1 -1
  121. package/dist/components/organisms/index.d.mts +6 -5
  122. package/dist/components/organisms/list/list.mjs +1 -1
  123. package/dist/components/organisms/modal.d.mts.map +1 -1
  124. package/dist/components/organisms/modal.mjs +8 -5
  125. package/dist/components/organisms/modal.mjs.map +1 -1
  126. package/dist/components/organisms/toast.d.mts.map +1 -1
  127. package/dist/components/organisms/toast.mjs +3 -2
  128. package/dist/components/organisms/toast.mjs.map +1 -1
  129. package/dist/components/templates/empty.mjs +1 -0
  130. package/dist/components/templates/empty.mjs.map +1 -1
  131. package/dist/components/templates/layout/content.mjs +1 -0
  132. package/dist/components/templates/layout/content.mjs.map +1 -1
  133. package/dist/components/templates/layout/footer.mjs +1 -0
  134. package/dist/components/templates/layout/footer.mjs.map +1 -1
  135. package/dist/components/templates/layout/header.mjs +1 -0
  136. package/dist/components/templates/layout/header.mjs.map +1 -1
  137. package/dist/components/templates/layout/layout.mjs +1 -0
  138. package/dist/components/templates/layout/layout.mjs.map +1 -1
  139. package/dist/components/templates/layout/sider.mjs +4 -2
  140. package/dist/components/templates/layout/sider.mjs.map +1 -1
  141. package/dist/components/templates/page-header.mjs +1 -0
  142. package/dist/components/templates/page-header.mjs.map +1 -1
  143. package/dist/components/templates/result.mjs +1 -0
  144. package/dist/components/templates/result.mjs.map +1 -1
  145. package/dist/core/accordion.mjs +1 -1
  146. package/dist/core/button.d.mts +1 -1
  147. package/dist/core/calendar.mjs +1 -1
  148. package/dist/core/checkbox.mjs +1 -1
  149. package/dist/core/combobox.d.mts +7 -0
  150. package/dist/core/combobox.d.mts.map +1 -0
  151. package/dist/core/combobox.mjs +99 -0
  152. package/dist/core/combobox.mjs.map +1 -0
  153. package/dist/core/dialog.mjs +1 -1
  154. package/dist/core/dialog.mjs.map +1 -1
  155. package/dist/core/drawer.mjs.map +1 -1
  156. package/dist/core/field.mjs +2 -4
  157. package/dist/core/field.mjs.map +1 -1
  158. package/dist/core/index.d.mts +4 -1
  159. package/dist/core/radio-group.mjs +1 -1
  160. package/dist/core/select.mjs +28 -23
  161. package/dist/core/select.mjs.map +1 -1
  162. package/dist/core/tabs.d.mts +9 -0
  163. package/dist/core/tabs.d.mts.map +1 -0
  164. package/dist/core/tabs.mjs +59 -0
  165. package/dist/core/tabs.mjs.map +1 -0
  166. package/dist/core/tooltip.d.mts +2 -0
  167. package/dist/core/tooltip.mjs +41 -0
  168. package/dist/core/tooltip.mjs.map +1 -0
  169. package/dist/index.d.mts +40 -29
  170. package/dist/index.mjs +12 -2
  171. package/dist/lib/z-layers.mjs +21 -0
  172. package/dist/lib/z-layers.mjs.map +1 -1
  173. package/dist/providers/config/dark-mode.mjs +2 -10
  174. package/dist/providers/config/dark-mode.mjs.map +1 -1
  175. package/dist/style.css +388 -16
  176. 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"}
@@ -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"}
@@ -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