@jbpark/ui-kit 10.0.2 → 10.1.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 (120) hide show
  1. package/README.ko.md +7 -0
  2. package/README.md +7 -0
  3. package/dist/Breadcrumb.d.mts +39 -0
  4. package/dist/Breadcrumb.d.mts.map +1 -0
  5. package/dist/Breadcrumb.mjs +103 -0
  6. package/dist/Breadcrumb.mjs.map +1 -0
  7. package/dist/Pagination.d.mts +53 -0
  8. package/dist/Pagination.d.mts.map +1 -0
  9. package/dist/Pagination.mjs +208 -0
  10. package/dist/Pagination.mjs.map +1 -0
  11. package/dist/Table.d.mts +48 -0
  12. package/dist/Table.d.mts.map +1 -0
  13. package/dist/Table.mjs +119 -0
  14. package/dist/Table.mjs.map +1 -0
  15. package/dist/components/atoms/checkbox/checkbox.mjs +2 -1
  16. package/dist/components/atoms/checkbox/checkbox.mjs.map +1 -1
  17. package/dist/components/atoms/checkbox/group.mjs +2 -1
  18. package/dist/components/atoms/checkbox/group.mjs.map +1 -1
  19. package/dist/components/atoms/color-picker.mjs +1 -1
  20. package/dist/components/atoms/date-picker.mjs +1 -1
  21. package/dist/components/atoms/float-button/back-top.mjs +1 -1
  22. package/dist/components/atoms/index.d.mts +2 -1
  23. package/dist/components/atoms/input/search.mjs +3 -1
  24. package/dist/components/atoms/input/search.mjs.map +1 -1
  25. package/dist/components/atoms/popover.mjs +1 -1
  26. package/dist/components/atoms/radio/group.mjs +1 -1
  27. package/dist/components/atoms/radio/radio.mjs +2 -1
  28. package/dist/components/atoms/radio/radio.mjs.map +1 -1
  29. package/dist/components/atoms/select-multiple.d.mts +29 -0
  30. package/dist/components/atoms/select-multiple.d.mts.map +1 -0
  31. package/dist/components/atoms/select-multiple.mjs +97 -0
  32. package/dist/components/atoms/select-multiple.mjs.map +1 -0
  33. package/dist/components/atoms/select-searchable-options.d.mts +21 -0
  34. package/dist/components/atoms/select-searchable-options.d.mts.map +1 -0
  35. package/dist/components/atoms/select-searchable-options.mjs +62 -0
  36. package/dist/components/atoms/select-searchable-options.mjs.map +1 -0
  37. package/dist/components/atoms/select-searchable.d.mts +22 -0
  38. package/dist/components/atoms/select-searchable.d.mts.map +1 -0
  39. package/dist/components/atoms/select-searchable.mjs +65 -0
  40. package/dist/components/atoms/select-searchable.mjs.map +1 -0
  41. package/dist/components/atoms/select.d.mts +8 -2
  42. package/dist/components/atoms/select.d.mts.map +1 -1
  43. package/dist/components/atoms/select.mjs +11 -1
  44. package/dist/components/atoms/select.mjs.map +1 -1
  45. package/dist/components/atoms/slider/slider.mjs +1 -1
  46. package/dist/components/atoms/switch.mjs +1 -1
  47. package/dist/components/atoms/tooltip.d.mts +21 -0
  48. package/dist/components/atoms/tooltip.d.mts.map +1 -0
  49. package/dist/components/atoms/tooltip.mjs +78 -0
  50. package/dist/components/atoms/tooltip.mjs.map +1 -0
  51. package/dist/components/atoms/typography/paragraph.mjs +1 -0
  52. package/dist/components/atoms/typography/paragraph.mjs.map +1 -1
  53. package/dist/components/atoms/typography/title.mjs +1 -0
  54. package/dist/components/atoms/typography/title.mjs.map +1 -1
  55. package/dist/components/molecules/dropdown.mjs +1 -1
  56. package/dist/components/molecules/index.d.mts +13 -10
  57. package/dist/components/molecules/marquees/marquees.mjs +1 -1
  58. package/dist/components/molecules/menu/menu.mjs +2 -1
  59. package/dist/components/molecules/menu/menu.mjs.map +1 -1
  60. package/dist/components/molecules/tabs.d.mts +37 -0
  61. package/dist/components/molecules/tabs.d.mts.map +1 -0
  62. package/dist/components/molecules/tabs.mjs +51 -0
  63. package/dist/components/molecules/tabs.mjs.map +1 -0
  64. package/dist/components/molecules/upload.mjs +2 -1
  65. package/dist/components/molecules/upload.mjs.map +1 -1
  66. package/dist/components/organisms/drawer.d.mts.map +1 -1
  67. package/dist/components/organisms/drawer.mjs +7 -2
  68. package/dist/components/organisms/drawer.mjs.map +1 -1
  69. package/dist/components/organisms/index.d.mts +6 -5
  70. package/dist/components/organisms/list/list.mjs +1 -1
  71. package/dist/components/organisms/modal.d.mts.map +1 -1
  72. package/dist/components/organisms/modal.mjs +7 -4
  73. package/dist/components/organisms/modal.mjs.map +1 -1
  74. package/dist/components/organisms/toast.d.mts.map +1 -1
  75. package/dist/components/organisms/toast.mjs +2 -1
  76. package/dist/components/organisms/toast.mjs.map +1 -1
  77. package/dist/components/templates/empty.mjs +1 -0
  78. package/dist/components/templates/empty.mjs.map +1 -1
  79. package/dist/components/templates/layout/content.mjs +1 -0
  80. package/dist/components/templates/layout/content.mjs.map +1 -1
  81. package/dist/components/templates/layout/footer.mjs +1 -0
  82. package/dist/components/templates/layout/footer.mjs.map +1 -1
  83. package/dist/components/templates/layout/header.mjs +1 -0
  84. package/dist/components/templates/layout/header.mjs.map +1 -1
  85. package/dist/components/templates/layout/layout.mjs +1 -0
  86. package/dist/components/templates/layout/layout.mjs.map +1 -1
  87. package/dist/components/templates/layout/sider.mjs +3 -1
  88. package/dist/components/templates/layout/sider.mjs.map +1 -1
  89. package/dist/components/templates/page-header.mjs +1 -0
  90. package/dist/components/templates/page-header.mjs.map +1 -1
  91. package/dist/components/templates/result.mjs +1 -0
  92. package/dist/components/templates/result.mjs.map +1 -1
  93. package/dist/core/badge.d.mts +1 -1
  94. package/dist/core/button.d.mts +2 -2
  95. package/dist/core/combobox.d.mts +7 -0
  96. package/dist/core/combobox.d.mts.map +1 -0
  97. package/dist/core/combobox.mjs +99 -0
  98. package/dist/core/combobox.mjs.map +1 -0
  99. package/dist/core/dialog.mjs.map +1 -1
  100. package/dist/core/drawer.mjs.map +1 -1
  101. package/dist/core/field.mjs +2 -4
  102. package/dist/core/field.mjs.map +1 -1
  103. package/dist/core/index.d.mts +4 -1
  104. package/dist/core/select.mjs +27 -22
  105. package/dist/core/select.mjs.map +1 -1
  106. package/dist/core/tabs.d.mts +9 -0
  107. package/dist/core/tabs.d.mts.map +1 -0
  108. package/dist/core/tabs.mjs +59 -0
  109. package/dist/core/tabs.mjs.map +1 -0
  110. package/dist/core/tooltip.d.mts +2 -0
  111. package/dist/core/tooltip.mjs +41 -0
  112. package/dist/core/tooltip.mjs.map +1 -0
  113. package/dist/index.d.mts +31 -26
  114. package/dist/index.mjs +7 -2
  115. package/dist/lib/z-layers.mjs +21 -0
  116. package/dist/lib/z-layers.mjs.map +1 -1
  117. package/dist/providers/config/dark-mode.mjs +2 -10
  118. package/dist/providers/config/dark-mode.mjs.map +1 -1
  119. package/dist/style.css +195 -16
  120. package/package.json +17 -2
package/README.ko.md CHANGED
@@ -145,6 +145,13 @@ function App() {
145
145
  해당 요소에 `isolation: isolate`를 적용해 Base UI 포털이 앱 내부의 z-index와
146
146
  경쟁하지 않고 페이지 콘텐츠 위에 표시되도록 합니다.
147
147
 
148
+ 반대 방향으로도 효과가 있습니다. 오버레이(Modal, Drawer, Select, Popover)와
149
+ 페이지 흐름 안에 고정되는 요소(sticky/fixed `Layout.Header`, `FloatButton`,
150
+ Dropdown 메뉴)는 모두 `z-index: 1000`을 씁니다. 루트를 격리하지 않으면 이
151
+ 요소들이 z-index가 더 낮은 앱 자체의 다이얼로그(예: shadcn의 `z-50`)를 덮습니다.
152
+ `data-ui-root`를 지정하면 `body`로 포털되는 앱의 오버레이가 이 요소들을 포함한
153
+ 앱 전체를 덮습니다.
154
+
148
155
  ### 개별 컴포넌트 import
149
156
 
150
157
  ```tsx
package/README.md CHANGED
@@ -145,6 +145,13 @@ Put `data-ui-root` on your application layout root. The included stylesheet
145
145
  applies `isolation: isolate` there so Base UI portals render above page content
146
146
  without competing with z-index values inside the app.
147
147
 
148
+ It also works in the other direction. Overlays (Modal, Drawer, Select,
149
+ Popover) and in-flow chrome (sticky/fixed `Layout.Header`, `FloatButton`,
150
+ Dropdown menus) share `z-index: 1000`. Without an isolated root, that
151
+ chrome paints over your own dialogs if they use a lower z-index (for example
152
+ shadcn's `z-50`). With `data-ui-root`, any overlay you portal to `body`
153
+ covers the whole app, including this chrome.
154
+
148
155
  ### Import Individual Components
149
156
 
150
157
  ```tsx
@@ -0,0 +1,39 @@
1
+ //#region src/components/molecules/breadcrumb.d.ts
2
+ interface Item {
3
+ key?: React.Key;
4
+ title: React.ReactNode;
5
+ href?: string;
6
+ target?: React.HTMLAttributeAnchorTarget;
7
+ rel?: string;
8
+ onClick?: React.MouseEventHandler<HTMLElement>;
9
+ className?: string;
10
+ 'aria-label'?: string;
11
+ /** Decorative icon placed before the title. */
12
+ icon?: React.ReactNode;
13
+ /** Overrides the separator after this item. */
14
+ separator?: React.ReactNode;
15
+ }
16
+ interface Props extends Omit<React.ComponentPropsWithoutRef<'nav'>, 'children'> {
17
+ items: Item[];
18
+ separator?: React.ReactNode;
19
+ /** Collapse middle items when the trail has more than this many entries. */
20
+ maxItems?: number;
21
+ itemsBeforeCollapse?: number;
22
+ itemsAfterCollapse?: number;
23
+ expandText?: string;
24
+ /** Render custom router links. The renderer owns aria-current for the last item. */
25
+ itemRender?: (item: Item, index: number, isCurrent: boolean) => React.ReactNode;
26
+ classNames?: {
27
+ list?: string;
28
+ item?: string;
29
+ link?: string;
30
+ current?: string;
31
+ separator?: string;
32
+ collapsed?: string;
33
+ popup?: string;
34
+ };
35
+ }
36
+ declare const Breadcrumb: ({ items, separator, maxItems, itemsBeforeCollapse, itemsAfterCollapse, expandText, itemRender, className, classNames, dir, "aria-label": ariaLabel, ...props }: Props) => import("react").JSX.Element;
37
+ //#endregion
38
+ export { Item, Props, Breadcrumb as default };
39
+ //# sourceMappingURL=Breadcrumb.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Breadcrumb.d.mts","names":[],"sources":["../src/components/molecules/breadcrumb.tsx"],"mappings":";UAUiB;EACf,MAAM,MAAM;EACZ,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA,UAAU,MAAM,kBAAkB;EAClC;EACA;;EAEA,OAAO,MAAM;;EAEb,YAAY,MAAM;;UAGH,cAAc,KAC7B,MAAM;EAGN,OAAO;EACP,YAAY,MAAM;;EAElB;EACA;EACA;EACA;;EAEA,cACE,MAAM,MACN,eACA,uBACG,MAAM;EACX;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;;cAOE,eAAc,OAAA,WAAA,UAAA,qBAAA,oBAAA,YAAA,YAAA,WAAA,YAAA,mBAAA,cAAA,SAajB,0BAAK,IAAA"}
@@ -0,0 +1,103 @@
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 Popover from "./components/atoms/popover.mjs";
6
+ import { Fragment } from "react";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/components/molecules/breadcrumb.tsx
9
+ const safeCount = (value, minimum) => Number.isFinite(value) ? Math.max(minimum, Math.floor(value)) : minimum;
10
+ const Breadcrumb = ({ items, separator = "/", maxItems = 8, itemsBeforeCollapse = 1, itemsAfterCollapse = 1, expandText = "Show path", itemRender, className, classNames, dir, "aria-label": ariaLabel, ...props }) => {
11
+ const { direction } = useConfig();
12
+ const limit = safeCount(maxItems, 3);
13
+ const before = Math.min(safeCount(itemsBeforeCollapse, 1), limit - 2);
14
+ const after = Math.min(safeCount(itemsAfterCollapse, 1), limit - before - 1);
15
+ const collapsed = items.length > limit && items.length - before - after > 0;
16
+ const visibleIndices = collapsed ? [...Array.from({ length: before }, (_, index) => index), ...Array.from({ length: after }, (_, index) => items.length - after + index)] : items.map((_, index) => index);
17
+ const hiddenIndices = collapsed ? Array.from({ length: items.length - before - after }, (_, index) => before + index) : [];
18
+ const renderItem = (index) => {
19
+ const item = items[index];
20
+ const isCurrent = index === items.length - 1;
21
+ if (itemRender) return itemRender(item, index, isCurrent);
22
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [item.icon != null && /* @__PURE__ */ jsx("span", {
23
+ "data-slot": "breadcrumb-icon",
24
+ "aria-hidden": "true",
25
+ className: "inline-flex shrink-0",
26
+ children: item.icon
27
+ }), item.title] });
28
+ const itemClassName = cn("inline-flex items-center gap-1.5 rounded-sm", (item.href !== void 0 || item.onClick) && "hover:text-foreground focus-visible:outline-ring focus-visible:outline-2", isCurrent ? "text-foreground font-medium" : "text-muted-foreground", (item.href !== void 0 || item.onClick) && classNames?.link, isCurrent && classNames?.current, item.className);
29
+ if (item.href !== void 0) return /* @__PURE__ */ jsx("a", {
30
+ href: item.href,
31
+ target: item.target,
32
+ rel: item.rel,
33
+ onClick: item.onClick,
34
+ "aria-label": item["aria-label"],
35
+ "aria-current": isCurrent ? "page" : void 0,
36
+ className: itemClassName,
37
+ children: content
38
+ });
39
+ if (item.onClick) return /* @__PURE__ */ jsx(Button, {
40
+ variant: "text",
41
+ size: "small",
42
+ onClick: item.onClick,
43
+ "aria-label": item["aria-label"],
44
+ "aria-current": isCurrent ? "page" : void 0,
45
+ className: cn("h-auto px-0 py-0 font-normal", itemClassName),
46
+ children: content
47
+ });
48
+ return /* @__PURE__ */ jsx("span", {
49
+ "aria-label": item["aria-label"],
50
+ "aria-current": isCurrent ? "page" : void 0,
51
+ className: itemClassName,
52
+ children: content
53
+ });
54
+ };
55
+ const renderSeparator = (index) => /* @__PURE__ */ jsx("li", {
56
+ "data-slot": "breadcrumb-separator",
57
+ "aria-hidden": "true",
58
+ className: cn("text-muted-foreground mx-2 shrink-0", classNames?.separator),
59
+ children: items[index]?.separator ?? separator
60
+ });
61
+ return /* @__PURE__ */ jsx("nav", {
62
+ ...props,
63
+ "data-slot": "breadcrumb",
64
+ dir: dir ?? direction,
65
+ "aria-label": ariaLabel ?? "Breadcrumb",
66
+ className: cn("text-sm", className),
67
+ children: /* @__PURE__ */ jsx("ol", {
68
+ "data-slot": "breadcrumb-list",
69
+ className: cn("m-0 flex min-w-0 flex-wrap items-center p-0", classNames?.list),
70
+ children: visibleIndices.map((index, position) => /* @__PURE__ */ jsxs(Fragment, { children: [
71
+ position > 0 && renderSeparator(collapsed && position === before ? index - 1 : visibleIndices[position - 1]),
72
+ /* @__PURE__ */ jsx("li", {
73
+ "data-slot": "breadcrumb-item",
74
+ className: cn("inline-flex min-w-0 items-center", classNames?.item),
75
+ children: renderItem(index)
76
+ }),
77
+ collapsed && position === before - 1 && /* @__PURE__ */ jsxs(Fragment$1, { children: [renderSeparator(index), /* @__PURE__ */ jsx("li", {
78
+ "data-slot": "breadcrumb-collapsed",
79
+ className: classNames?.collapsed,
80
+ children: /* @__PURE__ */ jsx(Popover, {
81
+ placement: "bottom",
82
+ className: classNames?.popup,
83
+ content: /* @__PURE__ */ jsx("ol", {
84
+ className: "m-0 grid list-none gap-1 p-0",
85
+ children: hiddenIndices.map((hiddenIndex) => /* @__PURE__ */ jsx("li", { children: renderItem(hiddenIndex) }, items[hiddenIndex]?.key ?? hiddenIndex))
86
+ }),
87
+ children: /* @__PURE__ */ jsx(Button, {
88
+ variant: "text",
89
+ size: "small",
90
+ "aria-label": expandText,
91
+ className: "h-auto px-1",
92
+ children: "…"
93
+ })
94
+ })
95
+ })] })
96
+ ] }, items[index]?.key ?? index))
97
+ })
98
+ });
99
+ };
100
+ //#endregion
101
+ export { Breadcrumb as default };
102
+
103
+ //# sourceMappingURL=Breadcrumb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Breadcrumb.mjs","names":[],"sources":["../src/components/molecules/breadcrumb.tsx"],"sourcesContent":["'use client';\n\nimport { Fragment } from 'react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport Popover from '../atoms/popover';\n\nexport interface Item {\n key?: React.Key;\n title: React.ReactNode;\n href?: string;\n target?: React.HTMLAttributeAnchorTarget;\n rel?: string;\n onClick?: React.MouseEventHandler<HTMLElement>;\n className?: string;\n 'aria-label'?: string;\n /** Decorative icon placed before the title. */\n icon?: React.ReactNode;\n /** Overrides the separator after this item. */\n separator?: React.ReactNode;\n}\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'nav'>,\n 'children'\n> {\n items: Item[];\n separator?: React.ReactNode;\n /** Collapse middle items when the trail has more than this many entries. */\n maxItems?: number;\n itemsBeforeCollapse?: number;\n itemsAfterCollapse?: number;\n expandText?: string;\n /** Render custom router links. The renderer owns aria-current for the last item. */\n itemRender?: (\n item: Item,\n index: number,\n isCurrent: boolean,\n ) => React.ReactNode;\n classNames?: {\n list?: string;\n item?: string;\n link?: string;\n current?: string;\n separator?: string;\n collapsed?: string;\n popup?: string;\n };\n}\n\nconst safeCount = (value: number, minimum: number) =>\n Number.isFinite(value) ? Math.max(minimum, Math.floor(value)) : minimum;\n\nconst Breadcrumb = ({\n items,\n separator = '/',\n maxItems = 8,\n itemsBeforeCollapse = 1,\n itemsAfterCollapse = 1,\n expandText = 'Show path',\n itemRender,\n className,\n classNames,\n dir,\n 'aria-label': ariaLabel,\n ...props\n}: Props) => {\n const { direction } = useConfig();\n const limit = safeCount(maxItems, 3);\n const before = Math.min(safeCount(itemsBeforeCollapse, 1), limit - 2);\n const after = Math.min(safeCount(itemsAfterCollapse, 1), limit - before - 1);\n const collapsed = items.length > limit && items.length - before - after > 0;\n const visibleIndices = collapsed\n ? [\n ...Array.from({ length: before }, (_, index) => index),\n ...Array.from(\n { length: after },\n (_, index) => items.length - after + index,\n ),\n ]\n : items.map((_, index) => index);\n const hiddenIndices = collapsed\n ? Array.from(\n { length: items.length - before - after },\n (_, index) => before + index,\n )\n : [];\n\n const renderItem = (index: number) => {\n const item = items[index]!;\n const isCurrent = index === items.length - 1;\n\n if (itemRender) {\n return itemRender(item, index, isCurrent);\n }\n\n const content = (\n <>\n {item.icon != null && (\n <span\n data-slot=\"breadcrumb-icon\"\n aria-hidden=\"true\"\n className=\"inline-flex shrink-0\"\n >\n {item.icon}\n </span>\n )}\n {item.title}\n </>\n );\n const itemClassName = cn(\n 'inline-flex items-center gap-1.5 rounded-sm',\n (item.href !== undefined || item.onClick) &&\n 'hover:text-foreground focus-visible:outline-ring focus-visible:outline-2',\n isCurrent ? 'text-foreground font-medium' : 'text-muted-foreground',\n (item.href !== undefined || item.onClick) && classNames?.link,\n isCurrent && classNames?.current,\n item.className,\n //\n );\n\n if (item.href !== undefined) {\n return (\n <a\n href={item.href}\n target={item.target}\n rel={item.rel}\n onClick={item.onClick}\n aria-label={item['aria-label']}\n aria-current={isCurrent ? 'page' : undefined}\n className={itemClassName}\n >\n {content}\n </a>\n );\n }\n\n if (item.onClick) {\n return (\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={item.onClick}\n aria-label={item['aria-label']}\n aria-current={isCurrent ? 'page' : undefined}\n className={cn('h-auto px-0 py-0 font-normal', itemClassName)}\n >\n {content}\n </Button>\n );\n }\n\n return (\n <span\n aria-label={item['aria-label']}\n aria-current={isCurrent ? 'page' : undefined}\n className={itemClassName}\n >\n {content}\n </span>\n );\n };\n\n const renderSeparator = (index: number) => (\n <li\n data-slot=\"breadcrumb-separator\"\n aria-hidden=\"true\"\n className={cn(\n 'text-muted-foreground mx-2 shrink-0',\n classNames?.separator,\n //\n )}\n >\n {items[index]?.separator ?? separator}\n </li>\n );\n\n return (\n <nav\n {...props}\n data-slot=\"breadcrumb\"\n dir={dir ?? direction}\n aria-label={ariaLabel ?? 'Breadcrumb'}\n className={cn(\n 'text-sm',\n className, //\n )}\n >\n <ol\n data-slot=\"breadcrumb-list\"\n className={cn(\n 'm-0 flex min-w-0 flex-wrap items-center p-0',\n classNames?.list,\n //\n )}\n >\n {visibleIndices.map((index, position) => (\n <Fragment key={items[index]?.key ?? index}>\n {position > 0 &&\n renderSeparator(\n collapsed && position === before\n ? index - 1\n : visibleIndices[position - 1]!,\n )}\n <li\n data-slot=\"breadcrumb-item\"\n className={cn(\n 'inline-flex min-w-0 items-center',\n classNames?.item, //\n )}\n >\n {renderItem(index)}\n </li>\n {collapsed && position === before - 1 && (\n <>\n {renderSeparator(index)}\n <li\n data-slot=\"breadcrumb-collapsed\"\n className={classNames?.collapsed}\n >\n <Popover\n placement=\"bottom\"\n className={classNames?.popup}\n content={\n <ol className=\"m-0 grid list-none gap-1 p-0\">\n {hiddenIndices.map(hiddenIndex => (\n <li key={items[hiddenIndex]?.key ?? hiddenIndex}>\n {renderItem(hiddenIndex)}\n </li>\n ))}\n </ol>\n }\n >\n <Button\n variant=\"text\"\n size=\"small\"\n aria-label={expandText}\n className=\"h-auto px-1\"\n >\n …\n </Button>\n </Popover>\n </li>\n </>\n )}\n </Fragment>\n ))}\n </ol>\n </nav>\n );\n};\n\nexport default Breadcrumb;\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ //#region src/components/molecules/pagination.d.ts
2
+ interface Props extends Omit<React.ComponentPropsWithoutRef<'nav'>, 'children' | 'onChange'> {
3
+ /** Total number of items, not pages. */
4
+ total: number;
5
+ /** Controlled number of items per page. */
6
+ pageSize?: number;
7
+ defaultPageSize?: number;
8
+ onPageSizeChange?: (pageSize: number) => void;
9
+ pageSizeOptions?: number[];
10
+ showSizeChanger?: boolean;
11
+ /** Render the total and the 1-based inclusive range of visible items. */
12
+ showTotal?: (total: number, range: [number, number]) => React.ReactNode;
13
+ showQuickJumper?: boolean;
14
+ /** 1-based controlled page. */
15
+ page?: number;
16
+ defaultPage?: number;
17
+ onChange?: (page: number) => void;
18
+ siblingCount?: number;
19
+ boundaryCount?: number;
20
+ showFirstLast?: boolean;
21
+ hideOnSinglePage?: boolean;
22
+ /** Render available page controls as links to these URLs. */
23
+ getPageHref?: (page: number) => string;
24
+ disabled?: boolean;
25
+ size?: 'small' | 'middle' | 'large';
26
+ labels?: {
27
+ navigation?: string;
28
+ page?: (page: number) => string;
29
+ previous?: string;
30
+ next?: string;
31
+ first?: string;
32
+ last?: string;
33
+ pageSize?: string;
34
+ jump?: string;
35
+ go?: string;
36
+ };
37
+ classNames?: {
38
+ item?: string;
39
+ active?: string;
40
+ previous?: string;
41
+ next?: string;
42
+ first?: string;
43
+ last?: string;
44
+ ellipsis?: string;
45
+ total?: string;
46
+ sizeChanger?: string;
47
+ jumper?: string;
48
+ };
49
+ }
50
+ declare const Pagination: ({ total, pageSize, defaultPageSize, onPageSizeChange, pageSizeOptions, showSizeChanger, showTotal, showQuickJumper, page, defaultPage, onChange, siblingCount, boundaryCount, showFirstLast, hideOnSinglePage, getPageHref, disabled, size, labels, className, classNames, "aria-label": ariaLabel, ...props }: Props) => import("react").JSX.Element | null;
51
+ //#endregion
52
+ export { Props, Pagination as default };
53
+ //# sourceMappingURL=Pagination.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.d.mts","names":[],"sources":["../src/components/molecules/pagination.tsx"],"mappings":";UAoBiB,cAAc,KAC7B,MAAM;;EAIN;;EAEA;EACA;EACA,oBAAoB;EACpB;EACA;;EAEA,aAAa,eAAe,4BAA4B,MAAM;EAC9D;;EAEA;EACA;EACA,YAAY;EACZ;EACA;EACA;EACA;;EAEA,eAAe;EACf;EACA;EACA;IACE;IACA,QAAQ;IACR;IACA;IACA;IACA;IACA;IACA;IACA;;EAEF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;cAkEE,eAAc,OAAA,UAAA,iBAAA,kBAAA,iBAAA,iBAAA,WAAA,iBAAA,MAAA,aAAA,UAAA,cAAA,eAAA,eAAA,kBAAA,aAAA,UAAA,MAAA,QAAA,WAAA,0BAAA,cAAA,SAwBjB,0BAAK,IAAA"}
@@ -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 { useState } from "react";
8
+ import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-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":";;;;;;;;;;AAwEA,MAAM,aAAa,MAAc,cAC/B,KAAK,IACH,KAAK,IAAI,GAAG,OAAO,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,GACxD,KAAK,IAAI,GAAG,SAAS,CACvB;AAEF,MAAM,qBAAqB,UACzB,OAAO,SAAS,KAAK,KAAK,SAAS,IAAI,KAAK,MAAM,KAAK,IAAI;AAE7D,MAAM,gBACJ,MACA,WACA,cACA,kBACe;CACf,IAAI,cAAc,GAChB,OAAO,CAAC;CAGV,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,IAAI,QAAQ,GAAG,SAAS,iBAAiB,SAAS,WAAW,SAAS;EACzE,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,YAAY,QAAQ,CAAC;CACjC;CAEA,KACE,IAAI,QAAQ,KAAK,IAAI,GAAG,OAAO,YAAY,GAC3C,SAAS,KAAK,IAAI,WAAW,OAAO,YAAY,GAChD,SAEA,MAAM,IAAI,KAAK;CAGjB,MAAM,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAC9C,MAAM,QAAoB,CAAC;CAC3B,IAAI,WAAW;CAEf,KAAK,MAAM,WAAW,QAAQ;EAC5B,MAAM,MAAM,UAAU;EAEtB,IAAI,QAAQ,GACV,MAAM,KAAK,WAAW,CAAC;OAClB,IAAI,MAAM,GACf,MAAM,KAAK,gBAAgB;EAG7B,MAAM,KAAK,OAAO;EAClB,WAAW;CACb;CAEA,MAAM,WAAW,YAAY;CAE7B,IAAI,aAAa,GACf,MAAM,KAAK,SAAS;MACf,IAAI,WAAW,GACpB,MAAM,KAAK,cAAc;CAG3B,OAAO;AACT;AAEA,MAAM,cAAc,EAClB,OACA,UACA,kBAAkB,IAClB,kBACA,kBAAkB;CAAC;CAAI;CAAI;CAAI;AAAG,GAClC,kBAAkB,OAClB,WACA,kBAAkB,OAClB,MACA,cAAc,GACd,UACA,eAAe,GACf,gBAAgB,GAChB,gBAAgB,OAChB,mBAAmB,OACnB,aACA,WAAW,OACX,MACA,QACA,WACA,YACA,cAAc,WACd,GAAG,YACQ;CACX,MAAM,EAAE,eAAe,cAAc,UAAU;CAC/C,MAAM,YAAY,OAAO,SAAS,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,IAAI;CAC5E,MAAM,CAAC,kBAAkB,uBAAuB,eAC9C,kBAAkB,eAAe,CACnC;CACA,MAAM,eAAe,kBAAkB,YAAY,gBAAgB;CACnE,MAAM,YAAY,KAAK,KAAK,YAAY,YAAY;CACpD,MAAM,CAAC,WAAW,gBAAgB,SAAS,EAAE;CAC7C,MAAM,CAAC,UAAU,eAAe,SAAS;EACvC,MAAM,UAAU,aAAa,SAAS;EACtC;CACF,CAAC;CAID,IAAI,SAAS,KAAA,KAAa,SAAS,cAAc,WAC/C,YAAY;EAAE,MAAM,UAAU,SAAS,MAAM,SAAS;EAAG;CAAU,CAAC;CAGtE,MAAM,cAAc,UAAU,QAAQ,SAAS,MAAM,SAAS;CAC9D,MAAM,eAAe,QAAQ,iBAAiB;CAO9C,MAAM,QAAQ,aACZ,aACA,WARuB,OAAO,SAAS,YAAY,IACjD,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,CAAC,IACpC,GACsB,OAAO,SAAS,aAAa,IACnD,KAAK,IAAI,GAAG,KAAK,MAAM,aAAa,CAAC,IACrC,CAMJ;CACA,MAAM,cAAc,MAAM,KACxB,IAAI,IACF,CAAC,GAAG,iBAAiB,YAAY,CAAC,CAAC,QACjC,UAAS,OAAO,UAAU,KAAK,KAAK,QAAQ,CAC9C,CACF,CACF,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CACtB,MAAM,QACJ,cAAc,IACV,CAAC,GAAG,CAAC,IACL,EACG,cAAc,KAAK,eAAe,GACnC,KAAK,IAAI,cAAc,cAAc,SAAS,CAChD;CAEN,MAAM,YAAY,SAAiB;EACjC,IACE,YACA,cAAc,KACd,OAAO,KACP,OAAO,aACP,SAAS,aAET;EAGF,IAAI,SAAS,KAAA,GACX,YAAY;GAAE,MAAM;GAAM;EAAU,CAAC;EAGvC,WAAW,IAAI;CACjB;CAEA,MAAM,kBAAkB,SAAiB;EACvC,IAAI,YAAY,SAAS,cACvB;EAGF,IAAI,aAAa,KAAA,GACf,oBAAoB,IAAI;EAG1B,IAAI,SAAS,KAAA,GACX,YAAY;GAAE,MAAM;GAAG,WAAW,KAAK,KAAK,YAAY,IAAI;EAAE,CAAC;EAGjE,mBAAmB,IAAI;EAEvB,IAAI,gBAAgB,GAClB,WAAW,CAAC;CAEhB;CAEA,MAAM,cAAc,UAA4C;EAC9D,MAAM,eAAe;EAErB,IAAI,CAAC,QAAQ,KAAK,UAAU,KAAK,CAAC,GAChC;EAGF,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;EAEpC,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,OAAO,WACpD;EAGF,SAAS,IAAI;EACb,aAAa,EAAE;CACjB;CAEA,MAAM,mBAAmB,MAAc,UAA4B;EACjE,IACE,gBACC,MAAM,WACL,MAAM,WACN,MAAM,YACN,MAAM,UACN,MAAM,WAAW,IAEnB;EAGF,IAAI,eAAe,SAAS,aAC1B,MAAM,eAAe;EAGvB,SAAS,IAAI;CACf;CAEA,MAAM,aAAa,QAAgB,eACjC,eAAe,CAAC,aACZ;EACE,QAAQ,oBAAC,KAAD,EAAG,MAAM,YAAY,MAAM,EAAI,CAAA;EACvC,cAAc;EACd,MAAM;CACR,IACA,CAAC;CAEP,IAAI,oBAAoB,aAAa,GACnC,OAAO;CAGT,OACE,qBAAC,OAAD;EACE,GAAI;EACJ,aAAU;EACV,cAAY,aAAa,QAAQ,cAAc;EAC/C,WAAW,GACT,qCACA,SAEF;EARF,UAAA;GAUG,aACC,oBAAC,QAAD;IACE,aAAU;IACV,WAAW,GACT,sCACA,YAAY,KAEd;IAEC,UAAA,UAAU,WAAW,KAAK;GACvB,CAAA;GAEP,iBACC,oBAAC,QAAD;IACE,GAAI,UAAU,GAAG,YAAY,cAAc,KAAK,gBAAgB,CAAC;IACjE,MAAM,cAAc,QAAQ,oBAAC,eAAD,CAAgB,CAAA,IAAI,oBAAC,cAAD,CAAe,CAAA;IAC/D,MAAM;IACN,SAAQ;IACR,cAAY,QAAQ,SAAS;IAC7B,UAAU,YAAY,cAAc,KAAK,gBAAgB;IACzD,WAAW,GACT,YAAY,MACZ,YAAY,KAEd;IACA,UAAS,UAAS,gBAAgB,GAAG,KAAK;GAC3C,CAAA;GAEH,oBAAC,QAAD;IACE,GAAI,UACF,cAAc,GACd,YAAY,cAAc,KAAK,gBAAgB,CACjD;IACA,MAAM,cAAc,QAAQ,oBAAC,cAAD,CAAe,CAAA,IAAI,oBAAC,aAAD,CAAc,CAAA;IAC7D,MAAM;IACN,SAAQ;IACR,cAAY,QAAQ,YAAY;IAChC,UAAU,YAAY,cAAc,KAAK,gBAAgB;IACzD,WAAW,GACT,YAAY,MACZ,YAAY,QAEd;IACA,UAAS,UAAS,gBAAgB,cAAc,GAAG,KAAK;GACzD,CAAA;GACA,MAAM,KAAK,MAAM,UAChB,OAAO,SAAS,WACd,oBAAC,QAAD;IAEE,GAAI,UAAU,MAAM,QAAQ;IAC5B,MAAM;IACN,SAAS,SAAS,cAAc,UAAU;IAC1C,OAAO,SAAS,cAAc,YAAY;IAC1C,gBAAc,SAAS,cAAc,SAAS,KAAA;IAC9C,cAAY,QAAQ,OAAO,IAAI,KAAK,QAAQ;IAClC;IACV,WAAW,GACT,WACA,YAAY,MACZ,SAAS,eAAe,YAAY,MAEtC;IACA,UAAS,UAAS,gBAAgB,MAAM,KAAK;IAE5C,UAAA;GACK,GAjBD,IAiBC,IAER,oBAAC,QAAD;IAEE,eAAY;IACZ,WAAW,GACT;iCAEA,YAAY,QAEd;IACD,UAAA;GAEK,GAVC,GAAG,KAAK,GAAG,OAUZ,CAEV;GACA,oBAAC,QAAD;IACE,GAAI,UACF,cAAc,GACd,YAAY,cAAc,KAAK,gBAAgB,SACjD;IACA,MAAM,cAAc,QAAQ,oBAAC,aAAD,CAAc,CAAA,IAAI,oBAAC,cAAD,CAAe,CAAA;IAC7D,MAAM;IACN,SAAQ;IACR,cAAY,QAAQ,QAAQ;IAC5B,UAAU,YAAY,cAAc,KAAK,gBAAgB;IACzD,WAAW,GACT,YAAY,MACZ,YAAY,IAEd;IACA,UAAS,UAAS,gBAAgB,cAAc,GAAG,KAAK;GACzD,CAAA;GACA,iBACC,oBAAC,QAAD;IACE,GAAI,UACF,WACA,YAAY,cAAc,KAAK,gBAAgB,SACjD;IACA,MAAM,cAAc,QAAQ,oBAAC,cAAD,CAAe,CAAA,IAAI,oBAAC,eAAD,CAAgB,CAAA;IAC/D,MAAM;IACN,SAAQ;IACR,cAAY,QAAQ,QAAQ;IAC5B,UAAU,YAAY,cAAc,KAAK,gBAAgB;IACzD,WAAW,GACT,YAAY,MACZ,YAAY,IAEd;IACA,UAAS,UAAS,gBAAgB,WAAW,KAAK;GACnD,CAAA;GAEF,mBACC,oBAAC,OAAD;IACE,aAAU;IACV,WAAW,GACT,iBACA,YAAY,WAEd;IAEA,UAAA,oBAAC,QAAD;KACE,OAAO,OAAO,YAAY;KAC1B,SAAS,YAAY,KAAI,WAAU;MACjC,OAAO,OAAO,KAAK;MACnB,OAAO,GAAG,MAAM;KAClB,EAAE;KACF,WAAU,UAAS,eAAe,OAAO,KAAK,CAAC;KACrC;KACV,cAAY,QAAQ,YAAY;IACjC,CAAA;GACE,CAAA;GAEN,mBACC,qBAAC,QAAD;IACE,aAAU;IACV,WAAW,GACT,gCACA,YAAY,MAEd;IACA,UAAU;IACV,YAAA;IARF,UAAA,CAUE,oBAAC,OAAD;KACE,MAAK;KACL,WAAU;KACV,cAAa;KACb,cAAY,QAAQ,QAAQ;KAC5B,OAAO;KACP,WAAU,UAAS,aAAa,MAAM,OAAO,KAAK;KAClD,UAAU,YAAY,cAAc;KACpC,WAAU;IACX,CAAA,GACD,oBAAC,QAAD;KACE,UAAS;KACT,MAAM;KACN,SAAQ;KACR,UAAU,YAAY,cAAc;KAEnC,UAAA,QAAQ,MAAM;IACT,CAAA,CACJ;;EAEL;;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"}