@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/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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Table.mjs","names":[],"sources":["../src/components/organisms/table.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Skeleton from '../atoms/skeleton';\nimport Pagination, {\n type Props as PaginationProps,\n} from '../molecules/pagination';\nimport Empty from '../templates/empty';\n\nexport type PaginationOptions = Omit<PaginationProps, 'total'> &\n ({ mode?: 'client'; total?: never } | { mode: 'server'; total: number });\n\nexport interface Column<T> {\n key: React.Key;\n title: React.ReactNode;\n /** Field displayed when render is omitted. */\n dataIndex?: keyof T;\n render?: (record: T, index: number) => React.ReactNode;\n align?: 'left' | 'center' | 'right';\n width?: string | number;\n className?: string;\n headerClassName?: string;\n}\n\nexport interface Props<T> extends Omit<\n React.ComponentPropsWithoutRef<'table'>,\n 'children'\n> {\n columns: Column<T>[];\n data?: T[];\n /** Use a stable key when rows may be reordered or paged. */\n rowKey?: (record: T, index: number) => React.Key;\n caption?: React.ReactNode;\n loading?: boolean;\n empty?: React.ReactNode;\n size?: 'small' | 'middle' | 'large';\n bordered?: boolean;\n striped?: boolean;\n hoverable?: boolean;\n /** true paginates data locally; server mode expects current-page data. */\n pagination?: boolean | PaginationOptions;\n classNames?: {\n container?: string;\n header?: string;\n body?: string;\n row?: string;\n cell?: string;\n empty?: string;\n pagination?: 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 Table = <T,>({\n columns,\n data = [],\n rowKey,\n loading = false,\n empty,\n size,\n bordered = false,\n striped = false,\n hoverable = true,\n pagination = false,\n caption,\n className,\n classNames,\n ...props\n}: Props<T>) => {\n const { componentSize } = useConfig();\n const paginationOptions: PaginationOptions =\n typeof pagination === 'object' ? pagination : {};\n const {\n mode = 'client',\n total: serverTotal,\n page: controlledPage,\n defaultPage = 1,\n pageSize: controlledPageSize,\n defaultPageSize = 10,\n onChange,\n onPageSizeChange,\n ...paginationProps\n } = paginationOptions;\n const [internal, setInternal] = useState(() => ({\n page: defaultPage,\n pageSize: normalizePageSize(defaultPageSize),\n pageCount: 0,\n }));\n const pageSize = normalizePageSize(controlledPageSize ?? internal.pageSize);\n const total = mode === 'server' ? (serverTotal ?? 0) : data.length;\n const pageCount = Math.ceil(total / pageSize);\n\n if (\n pagination &&\n controlledPage === undefined &&\n internal.pageCount !== pageCount\n ) {\n setInternal(current => ({\n ...current,\n page: clampPage(current.page, pageCount),\n pageCount,\n }));\n }\n\n const page = clampPage(controlledPage ?? internal.page, pageCount);\n const visibleData =\n pagination && mode === 'client'\n ? data.slice((page - 1) * pageSize, page * pageSize)\n : data;\n const resolvedSize = size ?? componentSize ?? 'middle';\n const cellPadding = {\n small: 'px-3 py-2',\n middle: 'px-4 py-3',\n large: 'px-5 py-4',\n }[resolvedSize];\n\n const handlePageChange = (next: number) => {\n if (controlledPage === undefined) {\n setInternal(current => ({ ...current, page: next }));\n }\n\n onChange?.(next);\n };\n\n const handlePageSizeChange = (next: number) => {\n if (controlledPageSize === undefined || controlledPage === undefined) {\n setInternal(current => ({\n ...current,\n page: controlledPage === undefined ? 1 : current.page,\n pageSize: controlledPageSize === undefined ? next : current.pageSize,\n pageCount: Math.ceil(total / next),\n }));\n }\n\n onPageSizeChange?.(next);\n };\n\n return (\n <>\n <div\n data-slot=\"table-container\"\n className={cn(\n 'w-full overflow-x-auto',\n bordered && 'rounded-md border',\n classNames?.container,\n //\n )}\n >\n <table\n {...props}\n data-slot=\"table\"\n aria-busy={loading || undefined}\n className={cn(\n 'w-full min-w-max border-collapse text-sm',\n className,\n //\n )}\n >\n {caption && <caption className=\"mb-2 text-start\">{caption}</caption>}\n <thead\n data-slot=\"table-header\"\n className={cn(\n 'bg-muted/50 border-b',\n classNames?.header,\n //\n )}\n >\n <tr>\n {columns.map(column => (\n <th\n key={column.key}\n scope=\"col\"\n style={{ width: column.width }}\n className={cn(\n 'text-foreground font-medium',\n cellPadding,\n column.align === 'center' && 'text-center',\n column.align === 'right' && 'text-right',\n column.align === undefined && 'text-start',\n bordered && 'border-e last:border-e-0',\n classNames?.cell,\n column.headerClassName,\n //\n )}\n >\n {column.title}\n </th>\n ))}\n </tr>\n </thead>\n <tbody data-slot=\"table-body\" className={classNames?.body}>\n {loading ? (\n <tr>\n <td\n colSpan={Math.max(columns.length, 1)}\n className={cellPadding}\n >\n <Skeleton count={3} className=\"py-2\" />\n </td>\n </tr>\n ) : visibleData.length === 0 ? (\n <tr>\n <td\n colSpan={Math.max(columns.length, 1)}\n className={cn(cellPadding, classNames?.empty)}\n >\n {empty ?? <Empty />}\n </td>\n </tr>\n ) : (\n visibleData.map((record, index) => (\n <tr\n key={rowKey?.(record, index) ?? index}\n data-slot=\"table-row\"\n className={cn(\n 'border-b last:border-b-0',\n striped && 'even:bg-muted/30',\n hoverable && 'hover:bg-muted/50',\n classNames?.row,\n //\n )}\n >\n {columns.map(column => {\n const value =\n column.dataIndex === undefined\n ? undefined\n : record[column.dataIndex];\n\n return (\n <td\n key={column.key}\n data-slot=\"table-cell\"\n className={cn(\n cellPadding,\n column.align === 'center' && 'text-center',\n column.align === 'right' && 'text-right',\n bordered && 'border-e last:border-e-0',\n classNames?.cell,\n column.className,\n //\n )}\n >\n {column.render\n ? column.render(record, index)\n : value == null\n ? null\n : String(value)}\n </td>\n );\n })}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n {pagination && (\n <div\n data-slot=\"table-pagination\"\n className={cn(\n 'mt-4 flex justify-end',\n classNames?.pagination,\n //\n )}\n >\n <Pagination\n {...paginationProps}\n total={total}\n page={page}\n pageSize={pageSize}\n onChange={handlePageChange}\n onPageSizeChange={handlePageSizeChange}\n />\n </div>\n )}\n </>\n );\n};\n\nexport default Table;\n"],"mappings":";;;;;;;;;AAwDA,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,SAAa,EACjB,SACA,OAAO,CAAC,GACR,QACA,UAAU,OACV,OACA,MACA,WAAW,OACX,UAAU,OACV,YAAY,MACZ,aAAa,OACb,SACA,WACA,YACA,GAAG,YACW;CACd,MAAM,EAAE,kBAAkB,UAAU;CAGpC,MAAM,EACJ,OAAO,UACP,OAAO,aACP,MAAM,gBACN,cAAc,GACd,UAAU,oBACV,kBAAkB,IAClB,UACA,kBACA,GAAG,oBAVH,OAAO,eAAe,WAAW,aAAa,CAAC;CAYjD,MAAM,CAAC,UAAU,eAAe,gBAAgB;EAC9C,MAAM;EACN,UAAU,kBAAkB,eAAe;EAC3C,WAAW;CACb,EAAE;CACF,MAAM,WAAW,kBAAkB,sBAAsB,SAAS,QAAQ;CAC1E,MAAM,QAAQ,SAAS,WAAY,eAAe,IAAK,KAAK;CAC5D,MAAM,YAAY,KAAK,KAAK,QAAQ,QAAQ;CAE5C,IACE,cACA,mBAAmB,KAAA,KACnB,SAAS,cAAc,WAEvB,aAAY,aAAY;EACtB,GAAG;EACH,MAAM,UAAU,QAAQ,MAAM,SAAS;EACvC;CACF,EAAE;CAGJ,MAAM,OAAO,UAAU,kBAAkB,SAAS,MAAM,SAAS;CACjE,MAAM,cACJ,cAAc,SAAS,WACnB,KAAK,OAAO,OAAO,KAAK,UAAU,OAAO,QAAQ,IACjD;CAEN,MAAM,cAAc;EAClB,OAAO;EACP,QAAQ;EACR,OAAO;CACT,EALqB,QAAQ,iBAAiB;CAO9C,MAAM,oBAAoB,SAAiB;EACzC,IAAI,mBAAmB,KAAA,GACrB,aAAY,aAAY;GAAE,GAAG;GAAS,MAAM;EAAK,EAAE;EAGrD,WAAW,IAAI;CACjB;CAEA,MAAM,wBAAwB,SAAiB;EAC7C,IAAI,uBAAuB,KAAA,KAAa,mBAAmB,KAAA,GACzD,aAAY,aAAY;GACtB,GAAG;GACH,MAAM,mBAAmB,KAAA,IAAY,IAAI,QAAQ;GACjD,UAAU,uBAAuB,KAAA,IAAY,OAAO,QAAQ;GAC5D,WAAW,KAAK,KAAK,QAAQ,IAAI;EACnC,EAAE;EAGJ,mBAAmB,IAAI;CACzB;CAEA,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,0BACA,YAAY,qBACZ,YAAY,SAEd;EAEA,UAAA,qBAAC,SAAD;GACE,GAAI;GACJ,aAAU;GACV,aAAW,WAAW,KAAA;GACtB,WAAW,GACT,4CACA,SAEF;GARF,UAAA;IAUG,WAAW,oBAAC,WAAD;KAAS,WAAU;KAAmB,UAAA;IAAiB,CAAA;IACnE,oBAAC,SAAD;KACE,aAAU;KACV,WAAW,GACT,wBACA,YAAY,MAEd;KAEA,UAAA,oBAAC,MAAD,EAAA,UACG,QAAQ,KAAI,WACX,oBAAC,MAAD;MAEE,OAAM;MACN,OAAO,EAAE,OAAO,OAAO,MAAM;MAC7B,WAAW,GACT,+BACA,aACA,OAAO,UAAU,YAAY,eAC7B,OAAO,UAAU,WAAW,cAC5B,OAAO,UAAU,KAAA,KAAa,cAC9B,YAAY,4BACZ,YAAY,MACZ,OAAO,eAET;MAEC,UAAA,OAAO;KACN,GAhBG,OAAO,GAgBV,CACL,EACC,CAAA;IACC,CAAA;IACP,oBAAC,SAAD;KAAO,aAAU;KAAa,WAAW,YAAY;KAClD,UAAA,UACC,oBAAC,MAAD,EAAA,UACE,oBAAC,MAAD;MACE,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;MACnC,WAAW;MAEX,UAAA,oBAAC,UAAD;OAAU,OAAO;OAAG,WAAU;MAAQ,CAAA;KACpC,CAAA,EACF,CAAA,IACF,YAAY,WAAW,IACzB,oBAAC,MAAD,EAAA,UACE,oBAAC,MAAD;MACE,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;MACnC,WAAW,GAAG,aAAa,YAAY,KAAK;MAE3C,UAAA,SAAS,oBAAC,OAAD,CAAQ,CAAA;KAChB,CAAA,EACF,CAAA,IAEJ,YAAY,KAAK,QAAQ,UACvB,oBAAC,MAAD;MAEE,aAAU;MACV,WAAW,GACT,4BACA,WAAW,oBACX,aAAa,qBACb,YAAY,GAEd;MAEC,UAAA,QAAQ,KAAI,WAAU;OACrB,MAAM,QACJ,OAAO,cAAc,KAAA,IACjB,KAAA,IACA,OAAO,OAAO;OAEpB,OACE,oBAAC,MAAD;QAEE,aAAU;QACV,WAAW,GACT,aACA,OAAO,UAAU,YAAY,eAC7B,OAAO,UAAU,WAAW,cAC5B,YAAY,4BACZ,YAAY,MACZ,OAAO,SAET;QAEC,UAAA,OAAO,SACJ,OAAO,OAAO,QAAQ,KAAK,IAC3B,SAAS,OACP,OACA,OAAO,KAAK;OAChB,GAjBG,OAAO,GAiBV;MAER,CAAC;KACC,GAtCG,SAAS,QAAQ,KAAK,KAAK,KAsC9B,CACL;IAEE,CAAA;GACF;;CACJ,CAAA,GACJ,cACC,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,yBACA,YAAY,UAEd;EAEA,UAAA,oBAAC,YAAD;GACE,GAAI;GACG;GACD;GACI;GACV,UAAU;GACV,kBAAkB;EACnB,CAAA;CACE,CAAA,CAEP,EAAA,CAAA;AAEN"}
@@ -4,8 +4,8 @@ import { checkbox_exports } from "../../../core/checkbox.mjs";
4
4
  import { label_exports } from "../../../core/label.mjs";
5
5
  import { useId, useRef } from "react";
6
6
  import { Square, SquareCheck } from "lucide-react";
7
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
7
  import { useControllableState, useKeyPress } from "@jbpark/use-hooks";
8
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/components/atoms/checkbox/checkbox.tsx
10
10
  const { Checkbox: CoreCheckbox } = checkbox_exports;
11
11
  const { Label: CoreLabel } = label_exports;
@@ -74,6 +74,7 @@ const Checkbox = ({ placement = "left", value: _value = "", children, className,
74
74
  children
75
75
  })] });
76
76
  return /* @__PURE__ */ jsx("div", {
77
+ "data-slot": "checkbox-field",
77
78
  className: cn("flex items-center gap-x-2", placement === "right" && "flex-row-reverse", cursorClassName, className),
78
79
  ...props,
79
80
  children: renderContent
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.mjs","names":["checkbox","label"],"sources":["../../../../src/components/atoms/checkbox/checkbox.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useRef } from 'react';\n\nimport { useControllableState, useKeyPress } from '@jbpark/use-hooks';\nimport { Square, SquareCheck } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { checkbox, label } from '../../../core';\nimport { type OptionValue } from './group';\n\nconst { Checkbox: CoreCheckbox } = checkbox;\nconst { Label: CoreLabel } = label;\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange'\n> {\n placement?: 'left' | 'right';\n defaultChecked?: boolean;\n checked?: boolean;\n value?: OptionValue;\n /** Native form field name — participates in `FormData` when set. */\n name?: string;\n disabled?: boolean;\n icons?: Partial<{ checked: React.ReactNode; unchecked: React.ReactNode }>;\n children?: React.ReactNode;\n onChange?: (checked: boolean) => void;\n}\n\nconst Checkbox = ({\n placement = 'left',\n value: _value = '',\n children,\n className,\n icons,\n disabled,\n defaultChecked,\n checked: _checked,\n name,\n onChange: _onChange = () => {},\n ...props\n}: Props) => {\n const [checked, setChecked] = useControllableState<boolean>({\n value: _checked,\n defaultValue: defaultChecked ?? false,\n onChange: _onChange,\n });\n\n const id = useId();\n const itemValue = String(_value);\n\n const cursorClassName = disabled ? 'cursor-not-allowed' : 'cursor-pointer';\n\n const onChange = (next: boolean) => {\n if (disabled) {\n return;\n }\n\n setChecked(next);\n };\n\n const iconTriggerRef = useRef<HTMLSpanElement>(null);\n\n useKeyPress(\n ['Enter', 'space'],\n event => {\n event.preventDefault();\n onChange(!checked);\n },\n { target: iconTriggerRef, enabled: !!icons && !disabled },\n );\n\n const renderContent = icons ? (\n <>\n <input\n id={id}\n hidden\n type=\"checkbox\"\n name={name}\n value={itemValue}\n checked={checked}\n disabled={disabled}\n onChange={e => {\n onChange(e.target.checked);\n }}\n />\n <span\n ref={iconTriggerRef}\n role=\"checkbox\"\n aria-checked={checked}\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cn(cursorClassName, disabled && 'opacity-50')}\n onClick={() => {\n onChange(!checked);\n }}\n >\n {checked\n ? (icons.checked ?? <SquareCheck />)\n : (icons.unchecked ?? <Square />)}\n </span>\n {children && (\n <label\n className={cn(cursorClassName, disabled && 'opacity-50')}\n htmlFor={id}\n >\n {children}\n </label>\n )}\n </>\n ) : (\n <>\n <CoreCheckbox\n id={id}\n name={name}\n value={itemValue}\n checked={checked}\n disabled={disabled}\n // Keep the atom's established 4px radius after the Base UI migration;\n // tailwind-merge lets this override the core primitive's radius (#361).\n className={cn('rounded-sm', cursorClassName)}\n onCheckedChange={onChange}\n />\n {children && (\n <CoreLabel htmlFor={id} className={cn(cursorClassName, 'text-left')}>\n {children}\n </CoreLabel>\n )}\n </>\n );\n\n return (\n <div\n className={cn(\n 'flex items-center gap-x-2',\n placement === 'right' && 'flex-row-reverse',\n cursorClassName,\n className,\n //\n )}\n {...props}\n >\n {renderContent}\n </div>\n );\n};\n\nexport default Checkbox;\n"],"mappings":";;;;;;;;;AAYA,MAAM,EAAE,UAAU,iBAAiBA;AACnC,MAAM,EAAE,OAAO,cAAcC;AAkB7B,MAAM,YAAY,EAChB,YAAY,QACZ,OAAO,SAAS,IAChB,UACA,WACA,OACA,UACA,gBACA,SAAS,UACT,MACA,UAAU,kBAAkB,CAAC,GAC7B,GAAG,YACQ;CACX,MAAM,CAAC,SAAS,cAAc,qBAA8B;EAC1D,OAAO;EACP,cAAc,kBAAkB;EAChC,UAAU;CACZ,CAAC;CAED,MAAM,KAAK,MAAM;CACjB,MAAM,YAAY,OAAO,MAAM;CAE/B,MAAM,kBAAkB,WAAW,uBAAuB;CAE1D,MAAM,YAAY,SAAkB;EAClC,IAAI,UACF;EAGF,WAAW,IAAI;CACjB;CAEA,MAAM,iBAAiB,OAAwB,IAAI;CAEnD,YACE,CAAC,SAAS,OAAO,IACjB,UAAS;EACP,MAAM,eAAe;EACrB,SAAS,CAAC,OAAO;CACnB,GACA;EAAE,QAAQ;EAAgB,SAAS,CAAC,CAAC,SAAS,CAAC;CAAS,CAC1D;CAEA,MAAM,gBAAgB,QACpB,qBAAA,YAAA,EAAA,UAAA;EACE,oBAAC,SAAD;GACM;GACJ,QAAA;GACA,MAAK;GACC;GACN,OAAO;GACE;GACC;GACV,WAAU,MAAK;IACb,SAAS,EAAE,OAAO,OAAO;GAC3B;EACD,CAAA;EACD,oBAAC,QAAD;GACE,KAAK;GACL,MAAK;GACL,gBAAc;GACd,iBAAe;GACf,UAAU,WAAW,KAAK;GAC1B,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,eAAe;IACb,SAAS,CAAC,OAAO;GACnB;GAEC,UAAA,UACI,MAAM,WAAW,oBAAC,aAAD,CAAc,CAAA,IAC/B,MAAM,aAAa,oBAAC,QAAD,CAAS,CAAA;EAC7B,CAAA;EACL,YACC,oBAAC,SAAD;GACE,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,SAAS;GAER;EACI,CAAA;CAET,EAAA,CAAA,IAEF,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,cAAD;EACM;EACE;EACN,OAAO;EACE;EACC;EAGV,WAAW,GAAG,cAAc,eAAe;EAC3C,iBAAiB;CAClB,CAAA,GACA,YACC,oBAAC,WAAD;EAAW,SAAS;EAAI,WAAW,GAAG,iBAAiB,WAAW;EAC/D;CACQ,CAAA,CAEb,EAAA,CAAA;CAGJ,OACE,oBAAC,OAAD;EACE,WAAW,GACT,6BACA,cAAc,WAAW,oBACzB,iBACA,SAEF;EACA,GAAI;EAEH,UAAA;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"checkbox.mjs","names":["checkbox","label"],"sources":["../../../../src/components/atoms/checkbox/checkbox.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useRef } from 'react';\n\nimport { useControllableState, useKeyPress } from '@jbpark/use-hooks';\nimport { Square, SquareCheck } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { checkbox, label } from '../../../core';\nimport { type OptionValue } from './group';\n\nconst { Checkbox: CoreCheckbox } = checkbox;\nconst { Label: CoreLabel } = label;\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange'\n> {\n placement?: 'left' | 'right';\n defaultChecked?: boolean;\n checked?: boolean;\n value?: OptionValue;\n /** Native form field name — participates in `FormData` when set. */\n name?: string;\n disabled?: boolean;\n icons?: Partial<{ checked: React.ReactNode; unchecked: React.ReactNode }>;\n children?: React.ReactNode;\n onChange?: (checked: boolean) => void;\n}\n\nconst Checkbox = ({\n placement = 'left',\n value: _value = '',\n children,\n className,\n icons,\n disabled,\n defaultChecked,\n checked: _checked,\n name,\n onChange: _onChange = () => {},\n ...props\n}: Props) => {\n const [checked, setChecked] = useControllableState<boolean>({\n value: _checked,\n defaultValue: defaultChecked ?? false,\n onChange: _onChange,\n });\n\n const id = useId();\n const itemValue = String(_value);\n\n const cursorClassName = disabled ? 'cursor-not-allowed' : 'cursor-pointer';\n\n const onChange = (next: boolean) => {\n if (disabled) {\n return;\n }\n\n setChecked(next);\n };\n\n const iconTriggerRef = useRef<HTMLSpanElement>(null);\n\n useKeyPress(\n ['Enter', 'space'],\n event => {\n event.preventDefault();\n onChange(!checked);\n },\n { target: iconTriggerRef, enabled: !!icons && !disabled },\n );\n\n const renderContent = icons ? (\n <>\n <input\n id={id}\n hidden\n type=\"checkbox\"\n name={name}\n value={itemValue}\n checked={checked}\n disabled={disabled}\n onChange={e => {\n onChange(e.target.checked);\n }}\n />\n <span\n ref={iconTriggerRef}\n role=\"checkbox\"\n aria-checked={checked}\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cn(cursorClassName, disabled && 'opacity-50')}\n onClick={() => {\n onChange(!checked);\n }}\n >\n {checked\n ? (icons.checked ?? <SquareCheck />)\n : (icons.unchecked ?? <Square />)}\n </span>\n {children && (\n <label\n className={cn(cursorClassName, disabled && 'opacity-50')}\n htmlFor={id}\n >\n {children}\n </label>\n )}\n </>\n ) : (\n <>\n <CoreCheckbox\n id={id}\n name={name}\n value={itemValue}\n checked={checked}\n disabled={disabled}\n // Keep the atom's established 4px radius after the Base UI migration;\n // tailwind-merge lets this override the core primitive's radius (#361).\n className={cn('rounded-sm', cursorClassName)}\n onCheckedChange={onChange}\n />\n {children && (\n <CoreLabel htmlFor={id} className={cn(cursorClassName, 'text-left')}>\n {children}\n </CoreLabel>\n )}\n </>\n );\n\n return (\n <div\n data-slot=\"checkbox-field\"\n className={cn(\n 'flex items-center gap-x-2',\n placement === 'right' && 'flex-row-reverse',\n cursorClassName,\n className,\n //\n )}\n {...props}\n >\n {renderContent}\n </div>\n );\n};\n\nexport default Checkbox;\n"],"mappings":";;;;;;;;;AAYA,MAAM,EAAE,UAAU,iBAAiBA;AACnC,MAAM,EAAE,OAAO,cAAcC;AAkB7B,MAAM,YAAY,EAChB,YAAY,QACZ,OAAO,SAAS,IAChB,UACA,WACA,OACA,UACA,gBACA,SAAS,UACT,MACA,UAAU,kBAAkB,CAAC,GAC7B,GAAG,YACQ;CACX,MAAM,CAAC,SAAS,cAAc,qBAA8B;EAC1D,OAAO;EACP,cAAc,kBAAkB;EAChC,UAAU;CACZ,CAAC;CAED,MAAM,KAAK,MAAM;CACjB,MAAM,YAAY,OAAO,MAAM;CAE/B,MAAM,kBAAkB,WAAW,uBAAuB;CAE1D,MAAM,YAAY,SAAkB;EAClC,IAAI,UACF;EAGF,WAAW,IAAI;CACjB;CAEA,MAAM,iBAAiB,OAAwB,IAAI;CAEnD,YACE,CAAC,SAAS,OAAO,IACjB,UAAS;EACP,MAAM,eAAe;EACrB,SAAS,CAAC,OAAO;CACnB,GACA;EAAE,QAAQ;EAAgB,SAAS,CAAC,CAAC,SAAS,CAAC;CAAS,CAC1D;CAEA,MAAM,gBAAgB,QACpB,qBAAA,YAAA,EAAA,UAAA;EACE,oBAAC,SAAD;GACM;GACJ,QAAA;GACA,MAAK;GACC;GACN,OAAO;GACE;GACC;GACV,WAAU,MAAK;IACb,SAAS,EAAE,OAAO,OAAO;GAC3B;EACD,CAAA;EACD,oBAAC,QAAD;GACE,KAAK;GACL,MAAK;GACL,gBAAc;GACd,iBAAe;GACf,UAAU,WAAW,KAAK;GAC1B,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,eAAe;IACb,SAAS,CAAC,OAAO;GACnB;GAEC,UAAA,UACI,MAAM,WAAW,oBAAC,aAAD,CAAc,CAAA,IAC/B,MAAM,aAAa,oBAAC,QAAD,CAAS,CAAA;EAC7B,CAAA;EACL,YACC,oBAAC,SAAD;GACE,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,SAAS;GAER;EACI,CAAA;CAET,EAAA,CAAA,IAEF,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,cAAD;EACM;EACE;EACN,OAAO;EACE;EACC;EAGV,WAAW,GAAG,cAAc,eAAe;EAC3C,iBAAiB;CAClB,CAAA,GACA,YACC,oBAAC,WAAD;EAAW,SAAS;EAAI,WAAW,GAAG,iBAAiB,WAAW;EAC/D;CACQ,CAAA,CAEb,EAAA,CAAA;CAGJ,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,6BACA,cAAc,WAAW,oBACzB,iBACA,SAEF;EACA,GAAI;EAEH,UAAA;CACE,CAAA;AAET"}
@@ -2,8 +2,8 @@
2
2
  import { cn } from "../../../utils.mjs";
3
3
  import Checkbox from "./checkbox.mjs";
4
4
  import { normalizeOptions, warnOnDuplicateOptionValues } from "../option-group.mjs";
5
- import { jsx } from "react/jsx-runtime";
6
5
  import { useControllableState } from "@jbpark/use-hooks";
6
+ import { jsx } from "react/jsx-runtime";
7
7
  //#region src/components/atoms/checkbox/group.tsx
8
8
  const Group = ({ orientation = "vertical", placement = "left", className, classNames = {}, options: _options = [], name, disabled, defaultValue, value: _value, onChange: _onChange = () => {}, ...props }) => {
9
9
  const [value, setValue] = useControllableState({
@@ -18,6 +18,7 @@ const Group = ({ orientation = "vertical", placement = "left", className, classN
18
18
  setValue(nextValue);
19
19
  };
20
20
  return /* @__PURE__ */ jsx("ul", {
21
+ "data-slot": "checkbox-group",
21
22
  ...props,
22
23
  className: cn("m-0 flex list-none gap-2 p-0", orientation === "vertical" && "flex-col", className),
23
24
  children: options.map((item, index) => {
@@ -1 +1 @@
1
- {"version":3,"file":"group.mjs","names":[],"sources":["../../../../src/components/atoms/checkbox/group.tsx"],"sourcesContent":["'use client';\n\nimport { useControllableState } from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport {\n type Option,\n type OptionGroupClassNames,\n type OptionValue,\n type Options,\n normalizeOptions,\n warnOnDuplicateOptionValues,\n} from '../option-group';\nimport Checkbox from './checkbox';\n\nexport type { OptionValue };\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'ul'>,\n 'onChange' | 'defaultValue' | 'value'\n> {\n options?: Options;\n orientation?: 'vertical' | 'horizontal';\n placement?: 'left' | 'right';\n classNames?: OptionGroupClassNames;\n defaultValue?: OptionValue[];\n value?: OptionValue[];\n /** Native form field name, shared across every option's checkbox. */\n name?: string;\n disabled?: boolean;\n onChange?: (values: OptionValue[]) => void;\n}\n\nconst Group = ({\n orientation = 'vertical',\n placement = 'left',\n className,\n classNames = {},\n options: _options = [],\n name,\n disabled,\n defaultValue,\n value: _value,\n onChange: _onChange = () => {},\n ...props\n}: Props) => {\n const [value, setValue] = useControllableState<OptionValue[]>({\n value: _value,\n defaultValue: defaultValue ?? [],\n onChange: _onChange,\n });\n\n const options: Option[] = normalizeOptions(_options);\n warnOnDuplicateOptionValues(options, 'Checkbox.Group');\n\n const onChange = (checked: boolean, optionValue: OptionValue) => {\n const nextValue = checked\n ? [...value, optionValue]\n : value.filter(v => v !== optionValue);\n\n setValue(nextValue);\n };\n\n return (\n <ul\n {...props}\n className={cn(\n // Spacing comes from `gap`, not `space-y`'s child margins, so the\n // per-item `m-0` reset below can neutralise host list styles without\n // also wiping out the group's own spacing.\n 'm-0 flex list-none gap-2 p-0',\n orientation === 'vertical' && 'flex-col',\n className,\n )}\n >\n {options.map((item: Option, index) => {\n const checked = value.includes(item.value);\n\n return (\n <li\n key={`${index}-${String(item.value)}`}\n // `m-0` completes the list reset the `ul` above starts (#253) —\n // see Radio.Group for the host rule this guards against.\n className={cn('m-0 flex', classNames?.wrapper)}\n >\n <Checkbox\n placement={placement}\n className={cn(classNames?.item)}\n name={name}\n value={item.value}\n checked={checked}\n disabled={disabled || item.disabled}\n onChange={checked => onChange(checked, item.value)}\n >\n {item.label}\n </Checkbox>\n </li>\n );\n })}\n </ul>\n );\n};\n\nexport default Group;\n"],"mappings":";;;;;;;AAkCA,MAAM,SAAS,EACb,cAAc,YACd,YAAY,QACZ,WACA,aAAa,CAAC,GACd,SAAS,WAAW,CAAC,GACrB,MACA,UACA,cACA,OAAO,QACP,UAAU,kBAAkB,CAAC,GAC7B,GAAG,YACQ;CACX,MAAM,CAAC,OAAO,YAAY,qBAAoC;EAC5D,OAAO;EACP,cAAc,gBAAgB,CAAC;EAC/B,UAAU;CACZ,CAAC;CAED,MAAM,UAAoB,iBAAiB,QAAQ;CACnD,4BAA4B,SAAS,gBAAgB;CAErD,MAAM,YAAY,SAAkB,gBAA6B;EAC/D,MAAM,YAAY,UACd,CAAC,GAAG,OAAO,WAAW,IACtB,MAAM,QAAO,MAAK,MAAM,WAAW;EAEvC,SAAS,SAAS;CACpB;CAEA,OACE,oBAAC,MAAD;EACE,GAAI;EACJ,WAAW,GAIT,gCACA,gBAAgB,cAAc,YAC9B,SACF;EAEC,UAAA,QAAQ,KAAK,MAAc,UAAU;GACpC,MAAM,UAAU,MAAM,SAAS,KAAK,KAAK;GAEzC,OACE,oBAAC,MAAD;IAIE,WAAW,GAAG,YAAY,YAAY,OAAO;IAE7C,UAAA,oBAAC,UAAD;KACa;KACX,WAAW,GAAG,YAAY,IAAI;KACxB;KACN,OAAO,KAAK;KACH;KACT,UAAU,YAAY,KAAK;KAC3B,WAAU,YAAW,SAAS,SAAS,KAAK,KAAK;KAEhD,UAAA,KAAK;IACE,CAAA;GACR,GAhBG,GAAG,MAAM,GAAG,OAAO,KAAK,KAAK,GAgBhC;EAER,CAAC;CACC,CAAA;AAER"}
1
+ {"version":3,"file":"group.mjs","names":[],"sources":["../../../../src/components/atoms/checkbox/group.tsx"],"sourcesContent":["'use client';\n\nimport { useControllableState } from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport {\n type Option,\n type OptionGroupClassNames,\n type OptionValue,\n type Options,\n normalizeOptions,\n warnOnDuplicateOptionValues,\n} from '../option-group';\nimport Checkbox from './checkbox';\n\nexport type { OptionValue };\n\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'ul'>,\n 'onChange' | 'defaultValue' | 'value'\n> {\n options?: Options;\n orientation?: 'vertical' | 'horizontal';\n placement?: 'left' | 'right';\n classNames?: OptionGroupClassNames;\n defaultValue?: OptionValue[];\n value?: OptionValue[];\n /** Native form field name, shared across every option's checkbox. */\n name?: string;\n disabled?: boolean;\n onChange?: (values: OptionValue[]) => void;\n}\n\nconst Group = ({\n orientation = 'vertical',\n placement = 'left',\n className,\n classNames = {},\n options: _options = [],\n name,\n disabled,\n defaultValue,\n value: _value,\n onChange: _onChange = () => {},\n ...props\n}: Props) => {\n const [value, setValue] = useControllableState<OptionValue[]>({\n value: _value,\n defaultValue: defaultValue ?? [],\n onChange: _onChange,\n });\n\n const options: Option[] = normalizeOptions(_options);\n warnOnDuplicateOptionValues(options, 'Checkbox.Group');\n\n const onChange = (checked: boolean, optionValue: OptionValue) => {\n const nextValue = checked\n ? [...value, optionValue]\n : value.filter(v => v !== optionValue);\n\n setValue(nextValue);\n };\n\n return (\n <ul\n data-slot=\"checkbox-group\"\n {...props}\n className={cn(\n // Spacing comes from `gap`, not `space-y`'s child margins, so the\n // per-item `m-0` reset below can neutralise host list styles without\n // also wiping out the group's own spacing.\n 'm-0 flex list-none gap-2 p-0',\n orientation === 'vertical' && 'flex-col',\n className,\n )}\n >\n {options.map((item: Option, index) => {\n const checked = value.includes(item.value);\n\n return (\n <li\n key={`${index}-${String(item.value)}`}\n // `m-0` completes the list reset the `ul` above starts (#253) —\n // see Radio.Group for the host rule this guards against.\n className={cn('m-0 flex', classNames?.wrapper)}\n >\n <Checkbox\n placement={placement}\n className={cn(classNames?.item)}\n name={name}\n value={item.value}\n checked={checked}\n disabled={disabled || item.disabled}\n onChange={checked => onChange(checked, item.value)}\n >\n {item.label}\n </Checkbox>\n </li>\n );\n })}\n </ul>\n );\n};\n\nexport default Group;\n"],"mappings":";;;;;;;AAkCA,MAAM,SAAS,EACb,cAAc,YACd,YAAY,QACZ,WACA,aAAa,CAAC,GACd,SAAS,WAAW,CAAC,GACrB,MACA,UACA,cACA,OAAO,QACP,UAAU,kBAAkB,CAAC,GAC7B,GAAG,YACQ;CACX,MAAM,CAAC,OAAO,YAAY,qBAAoC;EAC5D,OAAO;EACP,cAAc,gBAAgB,CAAC;EAC/B,UAAU;CACZ,CAAC;CAED,MAAM,UAAoB,iBAAiB,QAAQ;CACnD,4BAA4B,SAAS,gBAAgB;CAErD,MAAM,YAAY,SAAkB,gBAA6B;EAC/D,MAAM,YAAY,UACd,CAAC,GAAG,OAAO,WAAW,IACtB,MAAM,QAAO,MAAK,MAAM,WAAW;EAEvC,SAAS,SAAS;CACpB;CAEA,OACE,oBAAC,MAAD;EACE,aAAU;EACV,GAAI;EACJ,WAAW,GAIT,gCACA,gBAAgB,cAAc,YAC9B,SACF;EAEC,UAAA,QAAQ,KAAK,MAAc,UAAU;GACpC,MAAM,UAAU,MAAM,SAAS,KAAK,KAAK;GAEzC,OACE,oBAAC,MAAD;IAIE,WAAW,GAAG,YAAY,YAAY,OAAO;IAE7C,UAAA,oBAAC,UAAD;KACa;KACX,WAAW,GAAG,YAAY,IAAI;KACxB;KACN,OAAO,KAAK;KACH;KACT,UAAU,YAAY,KAAK;KAC3B,WAAU,YAAW,SAAS,SAAS,KAAK,KAAK;KAEhD,UAAA,KAAK;IACE,CAAA;GACR,GAhBG,GAAG,MAAM,GAAG,OAAO,KAAK,KAAK,GAgBhC;EAER,CAAC;CACC,CAAA;AAER"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../../utils.mjs";
3
3
  import Popover from "./popover.mjs";
4
- import { jsx, jsxs } from "react/jsx-runtime";
5
4
  import { useControllableState } from "@jbpark/use-hooks";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { HexColorPicker } from "react-colorful";
7
7
  //#region src/components/atoms/color-picker.tsx
8
8
  const ColorPicker = ({ defaultValue, value: _value, showText, disabled, className, onChange: _onChange = () => {}, open: _open, defaultOpen, onOpenChange, ...props }) => {
@@ -5,8 +5,8 @@ import Button from "../../Button.mjs";
5
5
  import Popover from "./popover.mjs";
6
6
  import { useState } from "react";
7
7
  import { CalendarIcon } from "lucide-react";
8
- import { jsx } from "react/jsx-runtime";
9
8
  import { useControllableState } from "@jbpark/use-hooks";
9
+ import { jsx } from "react/jsx-runtime";
10
10
  import { format } from "date-fns";
11
11
  //#region src/components/atoms/date-picker.tsx
12
12
  const { Calendar } = calendar_exports;
@@ -4,8 +4,8 @@ import { DEFAULT_LOCALE, useConfig } from "../../../providers/config/context.mjs
4
4
  import FloatButton from "./float-button.mjs";
5
5
  import { useRef } from "react";
6
6
  import { ArrowUp } from "lucide-react";
7
- import { jsx } from "react/jsx-runtime";
8
7
  import { useMergedRef, useWindowScroll } from "@jbpark/use-hooks";
8
+ import { jsx } from "react/jsx-runtime";
9
9
  //#region src/components/atoms/float-button/back-top.tsx
10
10
  const BackTop = ({ ref, visibilityHeight = 450, className, onClick, ...props }) => {
11
11
  const { locale } = useConfig();
@@ -16,4 +16,5 @@ import Skeleton from "./skeleton/index.mjs";
16
16
  import Slider from "./slider/slider.mjs";
17
17
  import Spin from "./spin.mjs";
18
18
  import Switch from "./switch.mjs";
19
- export { Button, Checkbox, ColorPicker, DatePicker, FloatButton, Input, Popover, Progress, Radio, RichTextEditor, Select, Skeleton, Slider, Spin, Switch, Tag, Typography };
19
+ import Tooltip from "./tooltip.mjs";
20
+ export { Button, Checkbox, ColorPicker, DatePicker, FloatButton, Input, Popover, Progress, Radio, RichTextEditor, Select, Skeleton, Slider, Spin, Switch, Tag, Tooltip, Typography };
@@ -5,8 +5,8 @@ import { input_exports } from "../../../core/input.mjs";
5
5
  import Button from "../../../Button.mjs";
6
6
  import { useRef, useState } from "react";
7
7
  import { CircleX, Search } from "lucide-react";
8
- import { jsx, jsxs } from "react/jsx-runtime";
9
8
  import { useMergedRef } from "@jbpark/use-hooks";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
10
  //#region src/components/atoms/input/search.tsx
11
11
  const { Input: Core } = input_exports;
12
12
  const Search$1 = ({ ref, className, defaultValue, value, disabled, allowClear = true, onChange, onKeyDown: _onKeyDown, onSearch: _onSearch, ...props }) => {
@@ -28,6 +28,7 @@ const Search$1 = ({ ref, className, defaultValue, value, disabled, allowClear =
28
28
  if (inputRef.current) _onSearch?.(inputRef.current.value);
29
29
  };
30
30
  return /* @__PURE__ */ jsxs("div", {
31
+ "data-slot": "input-search",
31
32
  className: cn("flex items-center", "rounded-md", className),
32
33
  children: [/* @__PURE__ */ jsxs("div", {
33
34
  className: cn("relative grow", "inline-flex items-center"),
@@ -51,6 +52,7 @@ const Search$1 = ({ ref, className, defaultValue, value, disabled, allowClear =
51
52
  }
52
53
  }), /* @__PURE__ */ jsx("button", {
53
54
  type: "button",
55
+ "data-slot": "input-search-clear",
54
56
  "aria-label": locale.clear ?? DEFAULT_LOCALE.clear,
55
57
  disabled,
56
58
  className: cn("absolute right-2", "shrink-0 cursor-pointer", "disabled:cursor-not-allowed disabled:opacity-50", (!allowClear || !hasValue) && "hidden"),
@@ -1 +1 @@
1
- {"version":3,"file":"search.mjs","names":["input","Search","SearchOutlined"],"sources":["../../../../src/components/atoms/input/search.tsx"],"sourcesContent":["'use client';\n\nimport { useRef, useState } from 'react';\n\nimport { useMergedRef } from '@jbpark/use-hooks';\nimport { CircleX, Search as SearchOutlined } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { input } from '../../../core';\nimport Button from '../button';\n\nconst { Input: Core } = input;\n\nexport interface Props extends React.ComponentPropsWithRef<'input'> {\n allowClear?: boolean;\n onChange?: (\n e: React.ChangeEvent<HTMLInputElement> | { target: { value: string } },\n ) => void;\n onSearch?: (value: string) => void;\n}\n\nconst Search = ({\n ref,\n className,\n defaultValue,\n value,\n disabled,\n allowClear = true,\n onChange,\n onKeyDown: _onKeyDown,\n onSearch: _onSearch,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n const inputRef = useRef<HTMLInputElement>(null);\n\n const mergedRef = useMergedRef(inputRef, ref);\n\n const [uncontrolledHasValue, setUncontrolledHasValue] =\n useState(!!defaultValue);\n const hasValue = value !== undefined ? !!value : uncontrolledHasValue;\n\n const onClear = () => {\n if (!inputRef.current) {\n return;\n }\n\n if (value === undefined) {\n inputRef.current.value = '';\n setUncontrolledHasValue(false);\n }\n\n onChange?.({ target: { value: '' } });\n };\n\n const onSearch = () => {\n if (disabled) {\n return;\n }\n if (inputRef.current) {\n _onSearch?.(inputRef.current.value);\n }\n };\n\n return (\n <div\n className={cn(\n 'flex items-center',\n 'rounded-md',\n className,\n //\n )}\n >\n <div\n className={cn(\n 'relative grow',\n 'inline-flex items-center',\n //\n )}\n >\n <Core\n {...props}\n ref={mergedRef}\n inputMode=\"search\"\n type=\"search\"\n enterKeyHint=\"search\"\n disabled={disabled}\n className={cn(\n 'rounded-r-none',\n '[&::-webkit-search-cancel-button]:hidden',\n '[&::-webkit-search-decoration]:hidden',\n //\n )}\n value={value}\n defaultValue={defaultValue}\n onChange={e => {\n if (value === undefined) {\n setUncontrolledHasValue(!!e.target.value);\n }\n onChange?.(e);\n }}\n onKeyDown={e => {\n _onKeyDown?.(e);\n if (e.key === 'Enter') {\n onSearch();\n }\n }}\n />\n <button\n type=\"button\"\n aria-label={locale.clear ?? DEFAULT_LOCALE.clear}\n disabled={disabled}\n className={cn(\n 'absolute right-2',\n 'shrink-0 cursor-pointer',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n (!allowClear || !hasValue) && 'hidden',\n )}\n onClick={onClear}\n >\n <CircleX className=\"size-4\" />\n </button>\n </div>\n <Button\n icon={<SearchOutlined />}\n disabled={disabled}\n className={cn(\n 'rounded-l-none',\n 'size-9',\n //\n )}\n onClick={onSearch}\n />\n </div>\n );\n};\n\nSearch.displayName = 'Search';\n\nexport default Search;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,EAAE,OAAO,SAASA;AAUxB,MAAMC,YAAU,EACd,KACA,WACA,cACA,OACA,UACA,aAAa,MACb,UACA,WAAW,YACX,UAAU,WACV,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,WAAW,OAAyB,IAAI;CAE9C,MAAM,YAAY,aAAa,UAAU,GAAG;CAE5C,MAAM,CAAC,sBAAsB,2BAC3B,SAAS,CAAC,CAAC,YAAY;CACzB,MAAM,WAAW,UAAU,KAAA,IAAY,CAAC,CAAC,QAAQ;CAEjD,MAAM,gBAAgB;EACpB,IAAI,CAAC,SAAS,SACZ;EAGF,IAAI,UAAU,KAAA,GAAW;GACvB,SAAS,QAAQ,QAAQ;GACzB,wBAAwB,KAAK;EAC/B;EAEA,WAAW,EAAE,QAAQ,EAAE,OAAO,GAAG,EAAE,CAAC;CACtC;CAEA,MAAM,iBAAiB;EACrB,IAAI,UACF;EAEF,IAAI,SAAS,SACX,YAAY,SAAS,QAAQ,KAAK;CAEtC;CAEA,OACE,qBAAC,OAAD;EACE,WAAW,GACT,qBACA,cACA,SAEF;EANF,UAAA,CAQE,qBAAC,OAAD;GACE,WAAW,GACT,iBACA,0BAEF;GALF,UAAA,CAOE,oBAAC,MAAD;IACE,GAAI;IACJ,KAAK;IACL,WAAU;IACV,MAAK;IACL,cAAa;IACH;IACV,WAAW,GACT,kBACA,4CACA,uCAEF;IACO;IACO;IACd,WAAU,MAAK;KACb,IAAI,UAAU,KAAA,GACZ,wBAAwB,CAAC,CAAC,EAAE,OAAO,KAAK;KAE1C,WAAW,CAAC;IACd;IACA,YAAW,MAAK;KACd,aAAa,CAAC;KACd,IAAI,EAAE,QAAQ,SACZ,SAAS;IAEb;GACD,CAAA,GACD,oBAAC,UAAD;IACE,MAAK;IACL,cAAY,OAAO,SAAS,eAAe;IACjC;IACV,WAAW,GACT,oBACA,2BACA,oDACC,CAAC,cAAc,CAAC,aAAa,QAChC;IACA,SAAS;IAET,UAAA,oBAAC,SAAD,EAAS,WAAU,SAAU,CAAA;GACvB,CAAA,CACL;EACL,CAAA,GAAA,oBAAC,QAAD;GACE,MAAM,oBAACC,QAAD,CAAiB,CAAA;GACb;GACV,WAAW,GACT,kBACA,QAEF;GACA,SAAS;EACV,CAAA,CACE;;AAET;AAEA,SAAO,cAAc"}
1
+ {"version":3,"file":"search.mjs","names":["input","Search","SearchOutlined"],"sources":["../../../../src/components/atoms/input/search.tsx"],"sourcesContent":["'use client';\n\nimport { useRef, useState } from 'react';\n\nimport { useMergedRef } from '@jbpark/use-hooks';\nimport { CircleX, Search as SearchOutlined } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { input } from '../../../core';\nimport Button from '../button';\n\nconst { Input: Core } = input;\n\nexport interface Props extends React.ComponentPropsWithRef<'input'> {\n allowClear?: boolean;\n onChange?: (\n e: React.ChangeEvent<HTMLInputElement> | { target: { value: string } },\n ) => void;\n onSearch?: (value: string) => void;\n}\n\nconst Search = ({\n ref,\n className,\n defaultValue,\n value,\n disabled,\n allowClear = true,\n onChange,\n onKeyDown: _onKeyDown,\n onSearch: _onSearch,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n const inputRef = useRef<HTMLInputElement>(null);\n\n const mergedRef = useMergedRef(inputRef, ref);\n\n const [uncontrolledHasValue, setUncontrolledHasValue] =\n useState(!!defaultValue);\n const hasValue = value !== undefined ? !!value : uncontrolledHasValue;\n\n const onClear = () => {\n if (!inputRef.current) {\n return;\n }\n\n if (value === undefined) {\n inputRef.current.value = '';\n setUncontrolledHasValue(false);\n }\n\n onChange?.({ target: { value: '' } });\n };\n\n const onSearch = () => {\n if (disabled) {\n return;\n }\n if (inputRef.current) {\n _onSearch?.(inputRef.current.value);\n }\n };\n\n return (\n <div\n data-slot=\"input-search\"\n className={cn(\n 'flex items-center',\n 'rounded-md',\n className,\n //\n )}\n >\n <div\n className={cn(\n 'relative grow',\n 'inline-flex items-center',\n //\n )}\n >\n <Core\n {...props}\n ref={mergedRef}\n inputMode=\"search\"\n type=\"search\"\n enterKeyHint=\"search\"\n disabled={disabled}\n className={cn(\n 'rounded-r-none',\n '[&::-webkit-search-cancel-button]:hidden',\n '[&::-webkit-search-decoration]:hidden',\n //\n )}\n value={value}\n defaultValue={defaultValue}\n onChange={e => {\n if (value === undefined) {\n setUncontrolledHasValue(!!e.target.value);\n }\n onChange?.(e);\n }}\n onKeyDown={e => {\n _onKeyDown?.(e);\n if (e.key === 'Enter') {\n onSearch();\n }\n }}\n />\n <button\n type=\"button\"\n data-slot=\"input-search-clear\"\n aria-label={locale.clear ?? DEFAULT_LOCALE.clear}\n disabled={disabled}\n className={cn(\n 'absolute right-2',\n 'shrink-0 cursor-pointer',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n (!allowClear || !hasValue) && 'hidden',\n )}\n onClick={onClear}\n >\n <CircleX className=\"size-4\" />\n </button>\n </div>\n <Button\n icon={<SearchOutlined />}\n disabled={disabled}\n className={cn(\n 'rounded-l-none',\n 'size-9',\n //\n )}\n onClick={onSearch}\n />\n </div>\n );\n};\n\nSearch.displayName = 'Search';\n\nexport default Search;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,EAAE,OAAO,SAASA;AAUxB,MAAMC,YAAU,EACd,KACA,WACA,cACA,OACA,UACA,aAAa,MACb,UACA,WAAW,YACX,UAAU,WACV,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,WAAW,OAAyB,IAAI;CAE9C,MAAM,YAAY,aAAa,UAAU,GAAG;CAE5C,MAAM,CAAC,sBAAsB,2BAC3B,SAAS,CAAC,CAAC,YAAY;CACzB,MAAM,WAAW,UAAU,KAAA,IAAY,CAAC,CAAC,QAAQ;CAEjD,MAAM,gBAAgB;EACpB,IAAI,CAAC,SAAS,SACZ;EAGF,IAAI,UAAU,KAAA,GAAW;GACvB,SAAS,QAAQ,QAAQ;GACzB,wBAAwB,KAAK;EAC/B;EAEA,WAAW,EAAE,QAAQ,EAAE,OAAO,GAAG,EAAE,CAAC;CACtC;CAEA,MAAM,iBAAiB;EACrB,IAAI,UACF;EAEF,IAAI,SAAS,SACX,YAAY,SAAS,QAAQ,KAAK;CAEtC;CAEA,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,qBACA,cACA,SAEF;EAPF,UAAA,CASE,qBAAC,OAAD;GACE,WAAW,GACT,iBACA,0BAEF;GALF,UAAA,CAOE,oBAAC,MAAD;IACE,GAAI;IACJ,KAAK;IACL,WAAU;IACV,MAAK;IACL,cAAa;IACH;IACV,WAAW,GACT,kBACA,4CACA,uCAEF;IACO;IACO;IACd,WAAU,MAAK;KACb,IAAI,UAAU,KAAA,GACZ,wBAAwB,CAAC,CAAC,EAAE,OAAO,KAAK;KAE1C,WAAW,CAAC;IACd;IACA,YAAW,MAAK;KACd,aAAa,CAAC;KACd,IAAI,EAAE,QAAQ,SACZ,SAAS;IAEb;GACD,CAAA,GACD,oBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,cAAY,OAAO,SAAS,eAAe;IACjC;IACV,WAAW,GACT,oBACA,2BACA,oDACC,CAAC,cAAc,CAAC,aAAa,QAChC;IACA,SAAS;IAET,UAAA,oBAAC,SAAD,EAAS,WAAU,SAAU,CAAA;GACvB,CAAA,CACL;EACL,CAAA,GAAA,oBAAC,QAAD;GACE,MAAM,oBAACC,QAAD,CAAiB,CAAA;GACb;GACV,WAAW,GACT,kBACA,QAEF;GACA,SAAS;EACV,CAAA,CACE;;AAET;AAEA,SAAO,cAAc"}
@@ -3,8 +3,8 @@ import { cn, renderConditional } from "../../utils.mjs";
3
3
  import { popover_exports } from "../../core/popover.mjs";
4
4
  import Typography from "../../Typography.mjs";
5
5
  import { Children } from "react";
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
6
  import { useControllableState } from "@jbpark/use-hooks";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/components/atoms/popover.tsx
9
9
  const { Popover: CorePopover, PopoverContent, PopoverTrigger, PopoverArrow, PopoverTitle } = popover_exports;
10
10
  const placementMap = {
@@ -6,9 +6,9 @@ import { normalizeOptions, warnOnDuplicateOptionValues } from "../option-group.m
6
6
  import { RadioGroupContext } from "./context.mjs";
7
7
  import Radio$1 from "./radio.mjs";
8
8
  import { useId } from "react";
9
+ import { useControllableState } from "@jbpark/use-hooks";
9
10
  import { jsx } from "react/jsx-runtime";
10
11
  import { Radio } from "@base-ui/react/radio";
11
- import { useControllableState } from "@jbpark/use-hooks";
12
12
  //#region src/components/atoms/radio/group.tsx
13
13
  const { RadioGroup: Core } = radio_group_exports;
14
14
  const RadioGroup = ({ orientation = "horizontal", placement = "left", className, classNames = {}, options: _options = [], name, optionType = "default", buttonStyle = "solid", size, disabled, defaultValue, value: _value, onChange: _onChange = () => {}, ...props }) => {
@@ -5,8 +5,8 @@ import { radio_group_exports } from "../../../core/radio-group.mjs";
5
5
  import { RadioGroupContext } from "./context.mjs";
6
6
  import { useContext, useId, useRef } from "react";
7
7
  import { Circle, CircleCheck } from "lucide-react";
8
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
8
  import { useControllableState, useKeyPress } from "@jbpark/use-hooks";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
10
  //#region src/components/atoms/radio/radio.tsx
11
11
  const { RadioGroup: Core, RadioGroupItem: Item } = radio_group_exports;
12
12
  const { Field, FieldLabel } = field_exports;
@@ -84,6 +84,7 @@ const Radio = ({ placement = "left", value: _value = "", children, className, ic
84
84
  children: renderField
85
85
  });
86
86
  return /* @__PURE__ */ jsx("div", {
87
+ "data-slot": "radio-field",
87
88
  className: cn("flex items-center", icons && placement === "right" && "flex-row-reverse", cursorClassName, className),
88
89
  ...props,
89
90
  children: renderContent
@@ -1 +1 @@
1
- {"version":3,"file":"radio.mjs","names":["radio","field"],"sources":["../../../../src/components/atoms/radio/radio.tsx"],"sourcesContent":["'use client';\n\nimport { useContext, useId, useRef } from 'react';\n\nimport { useControllableState, useKeyPress } from '@jbpark/use-hooks';\nimport { Circle, CircleCheck } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { field, radio } from '../../../core';\nimport { RadioGroupContext } from './context';\nimport { type OptionValue } from './group';\n\nconst { RadioGroup: Core, RadioGroupItem: Item } = radio;\nconst { Field, FieldLabel } = field;\n\n// Spreads onto the wrapper `<div>`, so the host props are div-based. (Base UI's\n// `RadioGroup` — `Core` here — types `style`/`className` as state callbacks,\n// which a native `<div>` can't take; the meaningful control props below are\n// re-declared explicitly anyway.)\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange'\n> {\n placement?: 'left' | 'right';\n defaultChecked?: boolean;\n checked?: boolean;\n value?: OptionValue;\n /** Native form field name — participates in `FormData` when set. */\n name?: string;\n disabled?: boolean;\n /**\n * Id shared by the underlying control and its label. Generated internally\n * when omitted. `RadioGroup` passes a deterministic id so that the group's\n * single Base UI root can address each option.\n */\n id?: string;\n icons?: Partial<{ checked: React.ReactNode; unchecked: React.ReactNode }>;\n onChange?: (checked: boolean) => void;\n}\n\nconst Radio = ({\n placement = 'left',\n value: _value = '',\n children,\n className,\n icons,\n disabled,\n defaultChecked,\n checked: _checked,\n id: _id,\n name,\n onChange: _onChange = () => {},\n ...props\n}: Props) => {\n const [checked, setChecked] = useControllableState<boolean>({\n value: _checked,\n defaultValue: defaultChecked ?? false,\n onChange: _onChange,\n });\n\n const generatedId = useId();\n const id = _id ?? generatedId;\n // Base UI identifies/submits an item by its `value`, which is separate\n // from `id` (used only for the DOM id/label pairing) — stringified since\n // the group value is a string, while `OptionValue` also allows\n // number/boolean.\n const itemValue = String(_value);\n const grouped = useContext(RadioGroupContext);\n\n const cursorClassName = disabled ? 'cursor-not-allowed' : 'cursor-pointer';\n\n const onChange = (next: boolean) => {\n if (disabled) {\n return;\n }\n\n setChecked(next);\n };\n\n const iconTriggerRef = useRef<HTMLSpanElement>(null);\n\n useKeyPress(\n ['Enter', 'space'],\n event => {\n event.preventDefault();\n onChange(true);\n },\n { target: iconTriggerRef, enabled: !!icons && !disabled },\n );\n\n // When rendered inside a `RadioGroup`, the group owns the single shared\n // `Core` (Base UI RadioGroup root) so that arrow keys move focus across\n // every option. A standalone `Radio` still has to provide its own root.\n const renderField = (\n <Field\n orientation=\"horizontal\"\n className={cn('flex gap-2', placement === 'right' && 'flex-row-reverse')}\n data-disabled={disabled}\n >\n <Item value={itemValue} id={id} disabled={disabled} />\n <FieldLabel htmlFor={id} className={cursorClassName}>\n {children}\n </FieldLabel>\n </Field>\n );\n\n const renderContent = icons ? (\n <>\n <input\n id={id}\n hidden\n type=\"radio\"\n name={name}\n value={itemValue}\n checked={checked}\n disabled={disabled}\n onChange={e => {\n onChange(e.target.checked);\n }}\n />\n <span\n ref={iconTriggerRef}\n role=\"radio\"\n aria-checked={checked}\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cn(cursorClassName, disabled && 'opacity-50')}\n onClick={() => {\n onChange(true);\n }}\n >\n {checked\n ? (icons.checked ?? <CircleCheck />)\n : (icons.unchecked ?? <Circle />)}\n </span>\n {children && (\n <label\n className={cn(cursorClassName, disabled && 'opacity-50')}\n htmlFor={id}\n >\n {children}\n </label>\n )}\n </>\n ) : grouped ? (\n renderField\n ) : (\n <Core\n name={name}\n value={checked ? itemValue : ''}\n onValueChange={() => onChange(true)}\n >\n {renderField}\n </Core>\n );\n\n return (\n <div\n className={cn(\n 'flex items-center',\n icons && placement === 'right' && 'flex-row-reverse',\n cursorClassName,\n className,\n //\n )}\n {...props}\n >\n {renderContent}\n </div>\n );\n};\n\nexport default Radio;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,EAAE,YAAY,MAAM,gBAAgB,SAASA;AACnD,MAAM,EAAE,OAAO,eAAeC;AA2B9B,MAAM,SAAS,EACb,YAAY,QACZ,OAAO,SAAS,IAChB,UACA,WACA,OACA,UACA,gBACA,SAAS,UACT,IAAI,KACJ,MACA,UAAU,kBAAkB,CAAC,GAC7B,GAAG,YACQ;CACX,MAAM,CAAC,SAAS,cAAc,qBAA8B;EAC1D,OAAO;EACP,cAAc,kBAAkB;EAChC,UAAU;CACZ,CAAC;CAED,MAAM,cAAc,MAAM;CAC1B,MAAM,KAAK,OAAO;CAKlB,MAAM,YAAY,OAAO,MAAM;CAC/B,MAAM,UAAU,WAAW,iBAAiB;CAE5C,MAAM,kBAAkB,WAAW,uBAAuB;CAE1D,MAAM,YAAY,SAAkB;EAClC,IAAI,UACF;EAGF,WAAW,IAAI;CACjB;CAEA,MAAM,iBAAiB,OAAwB,IAAI;CAEnD,YACE,CAAC,SAAS,OAAO,IACjB,UAAS;EACP,MAAM,eAAe;EACrB,SAAS,IAAI;CACf,GACA;EAAE,QAAQ;EAAgB,SAAS,CAAC,CAAC,SAAS,CAAC;CAAS,CAC1D;CAKA,MAAM,cACJ,qBAAC,OAAD;EACE,aAAY;EACZ,WAAW,GAAG,cAAc,cAAc,WAAW,kBAAkB;EACvE,iBAAe;EAHjB,UAAA,CAKE,oBAAC,MAAD;GAAM,OAAO;GAAe;GAAc;EAAW,CAAA,GACrD,oBAAC,YAAD;GAAY,SAAS;GAAI,WAAW;GACjC;EACS,CAAA,CACP;;CAGT,MAAM,gBAAgB,QACpB,qBAAA,YAAA,EAAA,UAAA;EACE,oBAAC,SAAD;GACM;GACJ,QAAA;GACA,MAAK;GACC;GACN,OAAO;GACE;GACC;GACV,WAAU,MAAK;IACb,SAAS,EAAE,OAAO,OAAO;GAC3B;EACD,CAAA;EACD,oBAAC,QAAD;GACE,KAAK;GACL,MAAK;GACL,gBAAc;GACd,iBAAe;GACf,UAAU,WAAW,KAAK;GAC1B,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,eAAe;IACb,SAAS,IAAI;GACf;GAEC,UAAA,UACI,MAAM,WAAW,oBAAC,aAAD,CAAc,CAAA,IAC/B,MAAM,aAAa,oBAAC,QAAD,CAAS,CAAA;EAC7B,CAAA;EACL,YACC,oBAAC,SAAD;GACE,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,SAAS;GAER;EACI,CAAA;CAET,EAAA,CAAA,IACA,UACF,cAEA,oBAAC,MAAD;EACQ;EACN,OAAO,UAAU,YAAY;EAC7B,qBAAqB,SAAS,IAAI;EAEjC,UAAA;CACG,CAAA;CAGR,OACE,oBAAC,OAAD;EACE,WAAW,GACT,qBACA,SAAS,cAAc,WAAW,oBAClC,iBACA,SAEF;EACA,GAAI;EAEH,UAAA;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"radio.mjs","names":["radio","field"],"sources":["../../../../src/components/atoms/radio/radio.tsx"],"sourcesContent":["'use client';\n\nimport { useContext, useId, useRef } from 'react';\n\nimport { useControllableState, useKeyPress } from '@jbpark/use-hooks';\nimport { Circle, CircleCheck } from 'lucide-react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { field, radio } from '../../../core';\nimport { RadioGroupContext } from './context';\nimport { type OptionValue } from './group';\n\nconst { RadioGroup: Core, RadioGroupItem: Item } = radio;\nconst { Field, FieldLabel } = field;\n\n// Spreads onto the wrapper `<div>`, so the host props are div-based. (Base UI's\n// `RadioGroup` — `Core` here — types `style`/`className` as state callbacks,\n// which a native `<div>` can't take; the meaningful control props below are\n// re-declared explicitly anyway.)\nexport interface Props extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange'\n> {\n placement?: 'left' | 'right';\n defaultChecked?: boolean;\n checked?: boolean;\n value?: OptionValue;\n /** Native form field name — participates in `FormData` when set. */\n name?: string;\n disabled?: boolean;\n /**\n * Id shared by the underlying control and its label. Generated internally\n * when omitted. `RadioGroup` passes a deterministic id so that the group's\n * single Base UI root can address each option.\n */\n id?: string;\n icons?: Partial<{ checked: React.ReactNode; unchecked: React.ReactNode }>;\n onChange?: (checked: boolean) => void;\n}\n\nconst Radio = ({\n placement = 'left',\n value: _value = '',\n children,\n className,\n icons,\n disabled,\n defaultChecked,\n checked: _checked,\n id: _id,\n name,\n onChange: _onChange = () => {},\n ...props\n}: Props) => {\n const [checked, setChecked] = useControllableState<boolean>({\n value: _checked,\n defaultValue: defaultChecked ?? false,\n onChange: _onChange,\n });\n\n const generatedId = useId();\n const id = _id ?? generatedId;\n // Base UI identifies/submits an item by its `value`, which is separate\n // from `id` (used only for the DOM id/label pairing) — stringified since\n // the group value is a string, while `OptionValue` also allows\n // number/boolean.\n const itemValue = String(_value);\n const grouped = useContext(RadioGroupContext);\n\n const cursorClassName = disabled ? 'cursor-not-allowed' : 'cursor-pointer';\n\n const onChange = (next: boolean) => {\n if (disabled) {\n return;\n }\n\n setChecked(next);\n };\n\n const iconTriggerRef = useRef<HTMLSpanElement>(null);\n\n useKeyPress(\n ['Enter', 'space'],\n event => {\n event.preventDefault();\n onChange(true);\n },\n { target: iconTriggerRef, enabled: !!icons && !disabled },\n );\n\n // When rendered inside a `RadioGroup`, the group owns the single shared\n // `Core` (Base UI RadioGroup root) so that arrow keys move focus across\n // every option. A standalone `Radio` still has to provide its own root.\n const renderField = (\n <Field\n orientation=\"horizontal\"\n className={cn('flex gap-2', placement === 'right' && 'flex-row-reverse')}\n data-disabled={disabled}\n >\n <Item value={itemValue} id={id} disabled={disabled} />\n <FieldLabel htmlFor={id} className={cursorClassName}>\n {children}\n </FieldLabel>\n </Field>\n );\n\n const renderContent = icons ? (\n <>\n <input\n id={id}\n hidden\n type=\"radio\"\n name={name}\n value={itemValue}\n checked={checked}\n disabled={disabled}\n onChange={e => {\n onChange(e.target.checked);\n }}\n />\n <span\n ref={iconTriggerRef}\n role=\"radio\"\n aria-checked={checked}\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cn(cursorClassName, disabled && 'opacity-50')}\n onClick={() => {\n onChange(true);\n }}\n >\n {checked\n ? (icons.checked ?? <CircleCheck />)\n : (icons.unchecked ?? <Circle />)}\n </span>\n {children && (\n <label\n className={cn(cursorClassName, disabled && 'opacity-50')}\n htmlFor={id}\n >\n {children}\n </label>\n )}\n </>\n ) : grouped ? (\n renderField\n ) : (\n <Core\n name={name}\n value={checked ? itemValue : ''}\n onValueChange={() => onChange(true)}\n >\n {renderField}\n </Core>\n );\n\n return (\n <div\n data-slot=\"radio-field\"\n className={cn(\n 'flex items-center',\n icons && placement === 'right' && 'flex-row-reverse',\n cursorClassName,\n className,\n //\n )}\n {...props}\n >\n {renderContent}\n </div>\n );\n};\n\nexport default Radio;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,EAAE,YAAY,MAAM,gBAAgB,SAASA;AACnD,MAAM,EAAE,OAAO,eAAeC;AA2B9B,MAAM,SAAS,EACb,YAAY,QACZ,OAAO,SAAS,IAChB,UACA,WACA,OACA,UACA,gBACA,SAAS,UACT,IAAI,KACJ,MACA,UAAU,kBAAkB,CAAC,GAC7B,GAAG,YACQ;CACX,MAAM,CAAC,SAAS,cAAc,qBAA8B;EAC1D,OAAO;EACP,cAAc,kBAAkB;EAChC,UAAU;CACZ,CAAC;CAED,MAAM,cAAc,MAAM;CAC1B,MAAM,KAAK,OAAO;CAKlB,MAAM,YAAY,OAAO,MAAM;CAC/B,MAAM,UAAU,WAAW,iBAAiB;CAE5C,MAAM,kBAAkB,WAAW,uBAAuB;CAE1D,MAAM,YAAY,SAAkB;EAClC,IAAI,UACF;EAGF,WAAW,IAAI;CACjB;CAEA,MAAM,iBAAiB,OAAwB,IAAI;CAEnD,YACE,CAAC,SAAS,OAAO,IACjB,UAAS;EACP,MAAM,eAAe;EACrB,SAAS,IAAI;CACf,GACA;EAAE,QAAQ;EAAgB,SAAS,CAAC,CAAC,SAAS,CAAC;CAAS,CAC1D;CAKA,MAAM,cACJ,qBAAC,OAAD;EACE,aAAY;EACZ,WAAW,GAAG,cAAc,cAAc,WAAW,kBAAkB;EACvE,iBAAe;EAHjB,UAAA,CAKE,oBAAC,MAAD;GAAM,OAAO;GAAe;GAAc;EAAW,CAAA,GACrD,oBAAC,YAAD;GAAY,SAAS;GAAI,WAAW;GACjC;EACS,CAAA,CACP;;CAGT,MAAM,gBAAgB,QACpB,qBAAA,YAAA,EAAA,UAAA;EACE,oBAAC,SAAD;GACM;GACJ,QAAA;GACA,MAAK;GACC;GACN,OAAO;GACE;GACC;GACV,WAAU,MAAK;IACb,SAAS,EAAE,OAAO,OAAO;GAC3B;EACD,CAAA;EACD,oBAAC,QAAD;GACE,KAAK;GACL,MAAK;GACL,gBAAc;GACd,iBAAe;GACf,UAAU,WAAW,KAAK;GAC1B,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,eAAe;IACb,SAAS,IAAI;GACf;GAEC,UAAA,UACI,MAAM,WAAW,oBAAC,aAAD,CAAc,CAAA,IAC/B,MAAM,aAAa,oBAAC,QAAD,CAAS,CAAA;EAC7B,CAAA;EACL,YACC,oBAAC,SAAD;GACE,WAAW,GAAG,iBAAiB,YAAY,YAAY;GACvD,SAAS;GAER;EACI,CAAA;CAET,EAAA,CAAA,IACA,UACF,cAEA,oBAAC,MAAD;EACQ;EACN,OAAO,UAAU,YAAY;EAC7B,qBAAqB,SAAS,IAAI;EAEjC,UAAA;CACG,CAAA;CAGR,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,qBACA,SAAS,cAAc,WAAW,oBAClC,iBACA,SAEF;EACA,GAAI;EAEH,UAAA;CACE,CAAA;AAET"}
@@ -0,0 +1,29 @@
1
+ import { RootProps } from "../../core/combobox.mjs";
2
+ import "../../core/index.mjs";
3
+ import { ClassNames, Option, OptionGroup } from "./select-searchable-options.mjs";
4
+ import { ReactNode } from "react";
5
+ //#region src/components/atoms/select-multiple.d.ts
6
+ interface Props extends Omit<RootProps<string, Option, true>, 'children' | 'items' | 'filteredItems' | 'onValueChange' | 'itemToStringLabel' | 'itemToStringValue' | 'isItemEqualToValue'> {
7
+ searchable: true;
8
+ multiple: true;
9
+ /** Accessible name for the multi-select input. */
10
+ label: string;
11
+ options: (Option | OptionGroup)[];
12
+ placeholder?: string;
13
+ searchPlaceholder?: string;
14
+ emptyText?: ReactNode;
15
+ allowClear?: boolean;
16
+ /** Instructions announced when a selected chip receives focus. */
17
+ chipDescription?: string;
18
+ /** Instructions announced on the input when chips are selected. */
19
+ inputDescription?: (selectedCount: number) => string;
20
+ className?: string;
21
+ classNames?: ClassNames & {
22
+ chips?: string;
23
+ chip?: string;
24
+ };
25
+ onChange?: (values: string[]) => void;
26
+ }
27
+ //#endregion
28
+ export { Props };
29
+ //# sourceMappingURL=select-multiple.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-multiple.d.mts","names":[],"sources":["../../../src/components/atoms/select-multiple.tsx"],"mappings":";;;;;UAgCiB,cAAc,KAC7B,kBAA2B;EAS3B;EACA;;EAEA;EACA,UAAU,SAAS;EACnB;EACA;EACA,YAAY;EACZ;;EAEA;;EAEA,oBAAoB;EACpB;EACA,aAAa;IACX;IACA;;EAEF,YAAY"}
@@ -0,0 +1,97 @@
1
+ "use client";
2
+ import { cn } from "../../utils.mjs";
3
+ import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
4
+ import { combobox_exports } from "../../core/combobox.mjs";
5
+ import { selectTriggerStyles } from "../../core/select.mjs";
6
+ import Button from "../../Button.mjs";
7
+ import Input from "./input/index.mjs";
8
+ import SearchableOptions, { useSelectItems } from "./select-searchable-options.mjs";
9
+ import { useMemo } from "react";
10
+ import { ChevronDownIcon, XIcon } from "lucide-react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ //#region src/components/atoms/select-multiple.tsx
13
+ const { Combobox: Core, ComboboxChip, ComboboxChipRemove, ComboboxChips, ComboboxClear, ComboboxContent, ComboboxInput, ComboboxInputGroup, ComboboxTrigger, ComboboxValue } = combobox_exports;
14
+ const MultipleSelect = ({ label, options, placeholder = "Select options", searchPlaceholder = "Search...", emptyText = "No results found.", allowClear = true, chipDescription = "Press Backspace or Delete to remove", inputDescription = (count) => `${count} selected. From the start of the input, press Left Arrow to focus selected options`, className, classNames, onChange, onOpenChange, searchable: _searchable, multiple, ...props }) => {
15
+ const { locale } = useConfig();
16
+ const items = useSelectItems(options);
17
+ const labels = useMemo(() => {
18
+ const result = /* @__PURE__ */ new Map();
19
+ options.forEach((option) => {
20
+ if ("options" in option) option.options.forEach((item) => result.set(item.value, item.label));
21
+ else result.set(option.value, option.label);
22
+ });
23
+ return result;
24
+ }, [options]);
25
+ return /* @__PURE__ */ jsxs(Core, {
26
+ items,
27
+ multiple,
28
+ onValueChange: (values) => onChange?.(values),
29
+ onOpenChange: (open, details) => {
30
+ if (!open && details.reason === "item-press") details.cancel();
31
+ onOpenChange?.(open, details);
32
+ },
33
+ ...props,
34
+ children: [/* @__PURE__ */ jsxs(ComboboxInputGroup, {
35
+ "data-slot": "select-multiple-input-group",
36
+ className: cn(selectTriggerStyles, "h-auto min-h-9 w-full max-w-64 flex-wrap gap-1 py-1", className),
37
+ children: [
38
+ /* @__PURE__ */ jsx(ComboboxValue, { children: (values) => /* @__PURE__ */ jsxs(ComboboxChips, {
39
+ "aria-label": values.length > 0 ? `${label} selected options` : void 0,
40
+ className: cn("flex min-w-0 flex-1 flex-wrap items-center gap-1", classNames?.chips),
41
+ children: [values.map((value) => {
42
+ const name = labels.get(value) ?? value;
43
+ return /* @__PURE__ */ jsxs(ComboboxChip, {
44
+ "data-slot": "select-chip",
45
+ "aria-label": name,
46
+ "aria-description": chipDescription,
47
+ className: cn(`bg-accent text-accent-foreground flex min-w-0
48
+ items-center gap-1 rounded-sm ps-2 pe-1 text-xs
49
+ outline-none`, "focus-visible:ring-ring/50 focus-visible:ring-[3px]", classNames?.chip),
50
+ children: [/* @__PURE__ */ jsx("span", {
51
+ className: "max-w-40 truncate",
52
+ children: name
53
+ }), /* @__PURE__ */ jsx(ComboboxChipRemove, {
54
+ "data-slot": "select-chip-remove",
55
+ "aria-label": `${locale.clear ?? DEFAULT_LOCALE.clear} ${name}`,
56
+ className: "hover:bg-background/70 flex size-5 shrink-0\n items-center justify-center rounded-sm",
57
+ children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
58
+ })]
59
+ }, value);
60
+ }), /* @__PURE__ */ jsx(ComboboxInput, { render: /* @__PURE__ */ jsx(Input, {
61
+ "aria-label": label,
62
+ "aria-description": values.length > 0 ? inputDescription(values.length) : void 0,
63
+ placeholder: values.length > 0 ? searchPlaceholder : placeholder,
64
+ className: cn(`h-6 min-w-16 flex-1 border-0 bg-transparent px-0 py-0
65
+ shadow-none focus-visible:border-0 focus-visible:ring-0`, classNames?.input)
66
+ }) })]
67
+ }) }),
68
+ allowClear && /* @__PURE__ */ jsx(ComboboxClear, {
69
+ "data-slot": "select-multiple-clear",
70
+ render: /* @__PURE__ */ jsx(Button, {
71
+ variant: "text",
72
+ size: "icon-sm",
73
+ icon: /* @__PURE__ */ jsx(XIcon, {}),
74
+ "aria-label": locale.clear ?? DEFAULT_LOCALE.clear
75
+ })
76
+ }),
77
+ /* @__PURE__ */ jsx(ComboboxTrigger, { render: /* @__PURE__ */ jsx(Button, {
78
+ variant: "text",
79
+ size: "icon-sm",
80
+ icon: /* @__PURE__ */ jsx(ChevronDownIcon, {}),
81
+ "aria-label": locale.expand ?? DEFAULT_LOCALE.expand
82
+ }) })
83
+ ]
84
+ }), /* @__PURE__ */ jsx(ComboboxContent, {
85
+ "aria-label": label,
86
+ className: classNames?.content,
87
+ children: /* @__PURE__ */ jsx(SearchableOptions, {
88
+ emptyText,
89
+ classNames
90
+ })
91
+ })]
92
+ });
93
+ };
94
+ //#endregion
95
+ export { MultipleSelect as default };
96
+
97
+ //# sourceMappingURL=select-multiple.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-multiple.mjs","names":["combobox","select.selectTriggerStyles"],"sources":["../../../src/components/atoms/select-multiple.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode, useMemo } from 'react';\n\nimport { ChevronDownIcon, XIcon } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { combobox, select } from '../../core';\nimport Button from './button';\nimport Input from './input';\nimport SearchableOptions, {\n type ClassNames,\n type Option,\n type OptionGroup,\n useSelectItems,\n} from './select-searchable-options';\n\nconst {\n Combobox: Core,\n ComboboxChip,\n ComboboxChipRemove,\n ComboboxChips,\n ComboboxClear,\n ComboboxContent,\n ComboboxInput,\n ComboboxInputGroup,\n ComboboxTrigger,\n ComboboxValue,\n} = combobox;\n\nexport interface Props extends Omit<\n combobox.RootProps<string, Option, true>,\n | 'children'\n | 'items'\n | 'filteredItems'\n | 'onValueChange'\n | 'itemToStringLabel'\n | 'itemToStringValue'\n | 'isItemEqualToValue'\n> {\n searchable: true;\n multiple: true;\n /** Accessible name for the multi-select input. */\n label: string;\n options: (Option | OptionGroup)[];\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: ReactNode;\n allowClear?: boolean;\n /** Instructions announced when a selected chip receives focus. */\n chipDescription?: string;\n /** Instructions announced on the input when chips are selected. */\n inputDescription?: (selectedCount: number) => string;\n className?: string;\n classNames?: ClassNames & {\n chips?: string;\n chip?: string;\n };\n onChange?: (values: string[]) => void;\n}\n\nconst MultipleSelect = ({\n label,\n options,\n placeholder = 'Select options',\n searchPlaceholder = 'Search...',\n emptyText = 'No results found.',\n allowClear = true,\n chipDescription = 'Press Backspace or Delete to remove',\n inputDescription = count =>\n `${count} selected. From the start of the input, press Left Arrow to focus selected options`,\n className,\n classNames,\n onChange,\n onOpenChange,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Do not forward the public mode switch to Base UI.\n searchable: _searchable,\n multiple,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n const items = useSelectItems(options);\n const labels = useMemo(() => {\n const result = new Map<string, string>();\n\n options.forEach(option => {\n if ('options' in option) {\n option.options.forEach(item => result.set(item.value, item.label));\n } else {\n result.set(option.value, option.label);\n }\n });\n\n return result;\n }, [options]);\n\n return (\n <Core\n items={items}\n multiple={multiple}\n onValueChange={values => onChange?.(values)}\n onOpenChange={(open, details) => {\n if (!open && details.reason === 'item-press') {\n details.cancel();\n }\n\n onOpenChange?.(open, details);\n }}\n {...props}\n >\n <ComboboxInputGroup\n data-slot=\"select-multiple-input-group\"\n className={cn(\n select.selectTriggerStyles,\n 'h-auto min-h-9 w-full max-w-64 flex-wrap gap-1 py-1',\n className,\n )}\n >\n <ComboboxValue>\n {(values: string[]) => (\n <ComboboxChips\n aria-label={\n values.length > 0 ? `${label} selected options` : undefined\n }\n className={cn(\n 'flex min-w-0 flex-1 flex-wrap items-center gap-1',\n classNames?.chips,\n )}\n >\n {values.map(value => {\n const name = labels.get(value) ?? value;\n\n return (\n <ComboboxChip\n key={value}\n data-slot=\"select-chip\"\n aria-label={name}\n aria-description={chipDescription}\n className={cn(\n `bg-accent text-accent-foreground flex min-w-0\n items-center gap-1 rounded-sm ps-2 pe-1 text-xs\n outline-none`,\n 'focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n classNames?.chip,\n )}\n >\n <span className=\"max-w-40 truncate\">{name}</span>\n <ComboboxChipRemove\n data-slot=\"select-chip-remove\"\n aria-label={`${locale.clear ?? DEFAULT_LOCALE.clear} ${name}`}\n className=\"hover:bg-background/70 flex size-5 shrink-0\n items-center justify-center rounded-sm\"\n >\n <XIcon className=\"size-3\" />\n </ComboboxChipRemove>\n </ComboboxChip>\n );\n })}\n <ComboboxInput\n render={\n <Input\n aria-label={label}\n aria-description={\n values.length > 0\n ? inputDescription(values.length)\n : undefined\n }\n placeholder={\n values.length > 0 ? searchPlaceholder : placeholder\n }\n className={cn(\n `h-6 min-w-16 flex-1 border-0 bg-transparent px-0 py-0\n shadow-none focus-visible:border-0 focus-visible:ring-0`,\n classNames?.input,\n )}\n />\n }\n />\n </ComboboxChips>\n )}\n </ComboboxValue>\n {allowClear && (\n <ComboboxClear\n data-slot=\"select-multiple-clear\"\n render={\n <Button\n variant=\"text\"\n size=\"icon-sm\"\n icon={<XIcon />}\n aria-label={locale.clear ?? DEFAULT_LOCALE.clear}\n />\n }\n />\n )}\n <ComboboxTrigger\n render={\n <Button\n variant=\"text\"\n size=\"icon-sm\"\n icon={<ChevronDownIcon />}\n aria-label={locale.expand ?? DEFAULT_LOCALE.expand}\n />\n }\n />\n </ComboboxInputGroup>\n <ComboboxContent aria-label={label} className={classNames?.content}>\n <SearchableOptions emptyText={emptyText} classNames={classNames} />\n </ComboboxContent>\n </Core>\n );\n};\n\nexport default MultipleSelect;\n"],"mappings":";;;;;;;;;;;;AAmBA,MAAM,EACJ,UAAU,MACV,cACA,oBACA,eACA,eACA,iBACA,eACA,oBACA,iBACA,kBACEA;AAiCJ,MAAM,kBAAkB,EACtB,OACA,SACA,cAAc,kBACd,oBAAoB,aACpB,YAAY,qBACZ,aAAa,MACb,kBAAkB,uCAClB,oBAAmB,UACjB,GAAG,MAAM,qFACX,WACA,YACA,UACA,cAEA,YAAY,aACZ,UACA,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,QAAQ,eAAe,OAAO;CACpC,MAAM,SAAS,cAAc;EAC3B,MAAM,yBAAS,IAAI,IAAoB;EAEvC,QAAQ,SAAQ,WAAU;GACxB,IAAI,aAAa,QACf,OAAO,QAAQ,SAAQ,SAAQ,OAAO,IAAI,KAAK,OAAO,KAAK,KAAK,CAAC;QAEjE,OAAO,IAAI,OAAO,OAAO,OAAO,KAAK;EAEzC,CAAC;EAED,OAAO;CACT,GAAG,CAAC,OAAO,CAAC;CAEZ,OACE,qBAAC,MAAD;EACS;EACG;EACV,gBAAe,WAAU,WAAW,MAAM;EAC1C,eAAe,MAAM,YAAY;GAC/B,IAAI,CAAC,QAAQ,QAAQ,WAAW,cAC9B,QAAQ,OAAO;GAGjB,eAAe,MAAM,OAAO;EAC9B;EACA,GAAI;EAXN,UAAA,CAaE,qBAAC,oBAAD;GACE,aAAU;GACV,WAAW,GACTC,qBACA,uDACA,SACF;GANF,UAAA;IAQE,oBAAC,eAAD,EAAA,WACI,WACA,qBAAC,eAAD;KACE,cACE,OAAO,SAAS,IAAI,GAAG,MAAM,qBAAqB,KAAA;KAEpD,WAAW,GACT,oDACA,YAAY,KACd;KAPF,UAAA,CASG,OAAO,KAAI,UAAS;MACnB,MAAM,OAAO,OAAO,IAAI,KAAK,KAAK;MAElC,OACE,qBAAC,cAAD;OAEE,aAAU;OACV,cAAY;OACZ,oBAAkB;OAClB,WAAW,GACT;;qCAGA,uDACA,YAAY,IACd;OAXF,UAAA,CAaE,oBAAC,QAAD;QAAM,WAAU;QAAqB,UAAA;OAAW,CAAA,GAChD,oBAAC,oBAAD;QACE,aAAU;QACV,cAAY,GAAG,OAAO,SAAS,eAAe,MAAM,GAAG;QACvD,WAAU;QAGV,UAAA,oBAAC,OAAD,EAAO,WAAU,SAAU,CAAA;OACT,CAAA,CACR;MArBP,GAAA,KAqBO;KAElB,CAAC,GACD,oBAAC,eAAD,EACE,QACE,oBAAC,OAAD;MACE,cAAY;MACZ,oBACE,OAAO,SAAS,IACZ,iBAAiB,OAAO,MAAM,IAC9B,KAAA;MAEN,aACE,OAAO,SAAS,IAAI,oBAAoB;MAE1C,WAAW,GACT;gFAEA,YAAY,KACd;KACD,CAAA,EAEJ,CAAA,CACY;IAEJ,CAAA,EAAA,CAAA;IACd,cACC,oBAAC,eAAD;KACE,aAAU;KACV,QACE,oBAAC,QAAD;MACE,SAAQ;MACR,MAAK;MACL,MAAM,oBAAC,OAAD,CAAQ,CAAA;MACd,cAAY,OAAO,SAAS,eAAe;KAC5C,CAAA;IAEJ,CAAA;IAEH,oBAAC,iBAAD,EACE,QACE,oBAAC,QAAD;KACE,SAAQ;KACR,MAAK;KACL,MAAM,oBAAC,iBAAD,CAAkB,CAAA;KACxB,cAAY,OAAO,UAAU,eAAe;IAC7C,CAAA,EAEJ,CAAA;GACiB;EACpB,CAAA,GAAA,oBAAC,iBAAD;GAAiB,cAAY;GAAO,WAAW,YAAY;GACzD,UAAA,oBAAC,mBAAD;IAA8B;IAAuB;GAAa,CAAA;EACnD,CAAA,CACb;;AAEV"}
@@ -0,0 +1,21 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/atoms/select-searchable-options.d.ts
3
+ interface Option {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+ interface OptionGroup {
9
+ label: string;
10
+ options: Option[];
11
+ }
12
+ interface ClassNames {
13
+ input?: string;
14
+ content?: string;
15
+ list?: string;
16
+ item?: string;
17
+ empty?: string;
18
+ }
19
+ //#endregion
20
+ export { ClassNames, Option, OptionGroup };
21
+ //# sourceMappingURL=select-searchable-options.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-searchable-options.d.mts","names":[],"sources":["../../../src/components/atoms/select-searchable-options.tsx"],"mappings":";;UAgBiB;EACf;EACA;EACA;;UAGe;EACf;EACA,SAAS;;UAGM;EACf;EACA;EACA;EACA;EACA"}