@jbpark/ui-kit 10.0.1 → 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 +40 -25
  2. package/README.md +40 -25
  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 +1 -1
  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
@@ -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"}
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { combobox_exports } from "../../core/combobox.mjs";
3
+ import { useMemo } from "react";
4
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/atoms/select-searchable-options.tsx
6
+ const { ComboboxCollection, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxItem, ComboboxList, createItems } = combobox_exports;
7
+ const isGroup = (option) => {
8
+ return "options" in option && Array.isArray(option.options);
9
+ };
10
+ const useSelectItems = (options) => {
11
+ const groups = useMemo(() => {
12
+ const result = [];
13
+ options.forEach((option, index) => {
14
+ if (isGroup(option)) result.push({
15
+ key: index,
16
+ value: option.label,
17
+ items: option.options,
18
+ grouped: true
19
+ });
20
+ else {
21
+ const previous = result.at(-1);
22
+ if (previous && !previous.grouped) previous.items.push(option);
23
+ else result.push({
24
+ key: index,
25
+ value: "",
26
+ items: [option],
27
+ grouped: false
28
+ });
29
+ }
30
+ });
31
+ return result;
32
+ }, [options]);
33
+ return useMemo(() => createItems(groups, {
34
+ getValue: (option) => option.value,
35
+ getLabel: (option) => option.label
36
+ }), [groups]);
37
+ };
38
+ const SearchableOptions = ({ emptyText, classNames }) => {
39
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, {
40
+ className: classNames?.empty,
41
+ children: emptyText
42
+ }), /* @__PURE__ */ jsx(ComboboxList, {
43
+ className: classNames?.list,
44
+ children: (group) => /* @__PURE__ */ jsxs(ComboboxGroup, {
45
+ items: group.items,
46
+ children: [group.grouped && /* @__PURE__ */ jsx(ComboboxGroupLabel, {
47
+ "data-slot": "combobox-group-label",
48
+ className: "text-muted-foreground px-2 py-1.5 text-xs",
49
+ children: group.value
50
+ }), /* @__PURE__ */ jsx(ComboboxCollection, { children: (option) => /* @__PURE__ */ jsx(ComboboxItem, {
51
+ value: option.value,
52
+ disabled: option.disabled,
53
+ className: classNames?.item,
54
+ children: option.label
55
+ }, option.value) })]
56
+ }, group.key)
57
+ })] });
58
+ };
59
+ //#endregion
60
+ export { SearchableOptions as default, useSelectItems };
61
+
62
+ //# sourceMappingURL=select-searchable-options.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-searchable-options.mjs","names":["combobox"],"sources":["../../../src/components/atoms/select-searchable-options.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode, useMemo } from 'react';\n\nimport { combobox } from '../../core';\n\nconst {\n ComboboxCollection,\n ComboboxEmpty,\n ComboboxGroup,\n ComboboxGroupLabel,\n ComboboxItem,\n ComboboxList,\n createItems,\n} = combobox;\n\nexport interface Option {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface OptionGroup {\n label: string;\n options: Option[];\n}\n\nexport interface ClassNames {\n input?: string;\n content?: string;\n list?: string;\n item?: string;\n empty?: string;\n}\n\ninterface ItemsGroup {\n key: number;\n value: string;\n items: Option[];\n grouped: boolean;\n}\n\nconst isGroup = (option: Option | OptionGroup): option is OptionGroup => {\n return 'options' in option && Array.isArray(option.options);\n};\n\nexport const useSelectItems = (options: (Option | OptionGroup)[]) => {\n const groups = useMemo(() => {\n const result: ItemsGroup[] = [];\n\n options.forEach((option, index) => {\n if (isGroup(option)) {\n result.push({\n key: index,\n value: option.label,\n items: option.options,\n grouped: true,\n });\n } else {\n const previous = result.at(-1);\n\n if (previous && !previous.grouped) {\n previous.items.push(option);\n } else {\n result.push({\n key: index,\n value: '',\n items: [option],\n grouped: false,\n });\n }\n }\n });\n\n return result;\n }, [options]);\n const items = useMemo(\n () =>\n createItems<Option, string>(groups, {\n getValue: option => option.value,\n getLabel: option => option.label,\n }),\n [groups],\n );\n\n return items;\n};\n\ninterface Props {\n emptyText: ReactNode;\n classNames?: ClassNames;\n}\n\nconst SearchableOptions = ({ emptyText, classNames }: Props) => {\n return (\n <>\n <ComboboxEmpty className={classNames?.empty}>{emptyText}</ComboboxEmpty>\n <ComboboxList className={classNames?.list}>\n {(group: ItemsGroup) => (\n <ComboboxGroup key={group.key} items={group.items}>\n {group.grouped && (\n <ComboboxGroupLabel\n data-slot=\"combobox-group-label\"\n className=\"text-muted-foreground px-2 py-1.5 text-xs\"\n >\n {group.value}\n </ComboboxGroupLabel>\n )}\n <ComboboxCollection>\n {(option: Option) => (\n <ComboboxItem\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n className={classNames?.item}\n >\n {option.label}\n </ComboboxItem>\n )}\n </ComboboxCollection>\n </ComboboxGroup>\n )}\n </ComboboxList>\n </>\n );\n};\n\nexport default SearchableOptions;\n"],"mappings":";;;;;AAMA,MAAM,EACJ,oBACA,eACA,eACA,oBACA,cACA,cACA,gBACEA;AA4BJ,MAAM,WAAW,WAAwD;CACvE,OAAO,aAAa,UAAU,MAAM,QAAQ,OAAO,OAAO;AAC5D;AAEA,MAAa,kBAAkB,YAAsC;CACnE,MAAM,SAAS,cAAc;EAC3B,MAAM,SAAuB,CAAC;EAE9B,QAAQ,SAAS,QAAQ,UAAU;GACjC,IAAI,QAAQ,MAAM,GAChB,OAAO,KAAK;IACV,KAAK;IACL,OAAO,OAAO;IACd,OAAO,OAAO;IACd,SAAS;GACX,CAAC;QACI;IACL,MAAM,WAAW,OAAO,GAAG,EAAE;IAE7B,IAAI,YAAY,CAAC,SAAS,SACxB,SAAS,MAAM,KAAK,MAAM;SAE1B,OAAO,KAAK;KACV,KAAK;KACL,OAAO;KACP,OAAO,CAAC,MAAM;KACd,SAAS;IACX,CAAC;GAEL;EACF,CAAC;EAED,OAAO;CACT,GAAG,CAAC,OAAO,CAAC;CAUZ,OATc,cAEV,YAA4B,QAAQ;EAClC,WAAU,WAAU,OAAO;EAC3B,WAAU,WAAU,OAAO;CAC7B,CAAC,GACH,CAAC,MAAM,CAGE;AACb;AAOA,MAAM,qBAAqB,EAAE,WAAW,iBAAwB;CAC9D,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,eAAD;EAAe,WAAW,YAAY;EAAQ,UAAA;CAAyB,CAAA,GACvE,oBAAC,cAAD;EAAc,WAAW,YAAY;EACjC,WAAA,UACA,qBAAC,eAAD;GAA+B,OAAO,MAAM;GAA5C,UAAA,CACG,MAAM,WACL,oBAAC,oBAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA,MAAM;GACW,CAAA,GAEtB,oBAAC,oBAAD,EAAA,WACI,WACA,oBAAC,cAAD;IAEE,OAAO,OAAO;IACd,UAAU,OAAO;IACjB,WAAW,YAAY;IAEtB,UAAA,OAAO;GACI,GANP,OAAO,KAMA,EAEE,CAAA,CACP;EArBK,GAAA,MAAM,GAqBX;CAEL,CAAA,CACd,EAAA,CAAA;AAEN"}
@@ -0,0 +1,22 @@
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-searchable.d.ts
6
+ interface Props extends Omit<RootProps<string, Option>, 'children' | 'items' | 'multiple' | 'filteredItems' | 'onValueChange' | 'itemToStringLabel' | 'itemToStringValue' | 'isItemEqualToValue'> {
7
+ searchable: true;
8
+ multiple?: false;
9
+ /** Accessible name for the trigger and search popup. */
10
+ label: string;
11
+ options: (Option | OptionGroup)[];
12
+ placeholder?: string;
13
+ searchPlaceholder?: string;
14
+ emptyText?: ReactNode;
15
+ allowClear?: boolean;
16
+ className?: string;
17
+ classNames?: ClassNames;
18
+ onChange?: (value: string | null) => void;
19
+ }
20
+ //#endregion
21
+ export { Props };
22
+ //# sourceMappingURL=select-searchable.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-searchable.d.mts","names":[],"sources":["../../../src/components/atoms/select-searchable.tsx"],"mappings":";;;;;UA6BiB,cAAc,KAC7B,kBAA2B;EAU3B;EACA;;EAEA;EACA,UAAU,SAAS;EACnB;EACA;EACA,YAAY;EACZ;EACA;EACA,aAAa;EACb,YAAY"}
@@ -0,0 +1,65 @@
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 { ChevronDownIcon, XIcon } from "lucide-react";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ //#region src/components/atoms/select-searchable.tsx
12
+ const { Combobox: Core, ComboboxClear, ComboboxContent, ComboboxIcon, ComboboxInput, ComboboxTrigger, ComboboxValue } = combobox_exports;
13
+ const SearchableSelect = ({ label, options, placeholder, searchPlaceholder = "Search...", emptyText = "No results found.", allowClear = true, className, classNames, onChange, searchable: _searchable, multiple: _multiple, ...props }) => {
14
+ const { locale } = useConfig();
15
+ const items = useSelectItems(options);
16
+ return /* @__PURE__ */ jsxs(Core, {
17
+ items,
18
+ onValueChange: (value) => onChange?.(value),
19
+ ...props,
20
+ children: [/* @__PURE__ */ jsxs(ComboboxTrigger, {
21
+ "data-slot": "select-trigger",
22
+ "data-size": "default",
23
+ "aria-label": label,
24
+ className: cn(selectTriggerStyles, "w-full max-w-48", className),
25
+ children: [/* @__PURE__ */ jsx("span", {
26
+ "data-slot": "select-value",
27
+ className: "min-w-0 flex-1 truncate text-start",
28
+ children: /* @__PURE__ */ jsx(ComboboxValue, { placeholder })
29
+ }), /* @__PURE__ */ jsx(ComboboxIcon, {
30
+ className: "opacity-50",
31
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
32
+ })]
33
+ }), /* @__PURE__ */ jsxs(ComboboxContent, {
34
+ "aria-label": label,
35
+ className: classNames?.content,
36
+ children: [/* @__PURE__ */ jsx("div", {
37
+ className: "border-b p-1",
38
+ children: /* @__PURE__ */ jsxs("div", {
39
+ className: "relative flex items-center",
40
+ children: [/* @__PURE__ */ jsx(ComboboxInput, { render: /* @__PURE__ */ jsx(Input, {
41
+ "aria-label": `${label} search`,
42
+ placeholder: searchPlaceholder,
43
+ className: cn(allowClear && "pe-8", classNames?.input)
44
+ }) }), allowClear && /* @__PURE__ */ jsx(ComboboxClear, {
45
+ "data-slot": "combobox-clear",
46
+ render: /* @__PURE__ */ jsx(Button, {
47
+ variant: "text",
48
+ size: "icon-sm",
49
+ icon: /* @__PURE__ */ jsx(XIcon, {}),
50
+ "aria-label": locale.clear ?? DEFAULT_LOCALE.clear,
51
+ className: "absolute end-0"
52
+ })
53
+ })]
54
+ })
55
+ }), /* @__PURE__ */ jsx(SearchableOptions, {
56
+ emptyText,
57
+ classNames
58
+ })]
59
+ })]
60
+ });
61
+ };
62
+ //#endregion
63
+ export { SearchableSelect as default };
64
+
65
+ //# sourceMappingURL=select-searchable.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-searchable.mjs","names":["combobox","select.selectTriggerStyles"],"sources":["../../../src/components/atoms/select-searchable.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode } 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 ComboboxClear,\n ComboboxContent,\n ComboboxIcon,\n ComboboxInput,\n ComboboxTrigger,\n ComboboxValue,\n} = combobox;\n\nexport interface Props extends Omit<\n combobox.RootProps<string, Option>,\n | 'children'\n | 'items'\n | 'multiple'\n | 'filteredItems'\n | 'onValueChange'\n | 'itemToStringLabel'\n | 'itemToStringValue'\n | 'isItemEqualToValue'\n> {\n searchable: true;\n multiple?: false;\n /** Accessible name for the trigger and search popup. */\n label: string;\n options: (Option | OptionGroup)[];\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: ReactNode;\n allowClear?: boolean;\n className?: string;\n classNames?: ClassNames;\n onChange?: (value: string | null) => void;\n}\n\nconst SearchableSelect = ({\n label,\n options,\n placeholder,\n searchPlaceholder = 'Search...',\n emptyText = 'No results found.',\n allowClear = true,\n className,\n classNames,\n onChange,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Do not forward the public mode switch to Base UI.\n searchable: _searchable,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- The single-select primitive fixes this mode internally.\n multiple: _multiple,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n const items = useSelectItems(options);\n\n return (\n <Core items={items} onValueChange={value => onChange?.(value)} {...props}>\n <ComboboxTrigger\n data-slot=\"select-trigger\"\n data-size=\"default\"\n aria-label={label}\n className={cn(select.selectTriggerStyles, 'w-full max-w-48', className)}\n >\n <span\n data-slot=\"select-value\"\n className=\"min-w-0 flex-1 truncate text-start\"\n >\n <ComboboxValue placeholder={placeholder} />\n </span>\n <ComboboxIcon className=\"opacity-50\">\n <ChevronDownIcon />\n </ComboboxIcon>\n </ComboboxTrigger>\n <ComboboxContent aria-label={label} className={classNames?.content}>\n <div className=\"border-b p-1\">\n <div className=\"relative flex items-center\">\n <ComboboxInput\n render={\n <Input\n aria-label={`${label} search`}\n placeholder={searchPlaceholder}\n className={cn(allowClear && 'pe-8', classNames?.input)}\n />\n }\n />\n {allowClear && (\n <ComboboxClear\n data-slot=\"combobox-clear\"\n render={\n <Button\n variant=\"text\"\n size=\"icon-sm\"\n icon={<XIcon />}\n aria-label={locale.clear ?? DEFAULT_LOCALE.clear}\n className=\"absolute end-0\"\n />\n }\n />\n )}\n </div>\n </div>\n <SearchableOptions emptyText={emptyText} classNames={classNames} />\n </ComboboxContent>\n </Core>\n );\n};\n\nexport default SearchableSelect;\n"],"mappings":";;;;;;;;;;;AAmBA,MAAM,EACJ,UAAU,MACV,eACA,iBACA,cACA,eACA,iBACA,kBACEA;AA2BJ,MAAM,oBAAoB,EACxB,OACA,SACA,aACA,oBAAoB,aACpB,YAAY,qBACZ,aAAa,MACb,WACA,YACA,UAEA,YAAY,aAEZ,UAAU,WACV,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,QAAQ,eAAe,OAAO;CAEpC,OACE,qBAAC,MAAD;EAAa;EAAO,gBAAe,UAAS,WAAW,KAAK;EAAG,GAAI;EAAnE,UAAA,CACE,qBAAC,iBAAD;GACE,aAAU;GACV,aAAU;GACV,cAAY;GACZ,WAAW,GAAGC,qBAA4B,mBAAmB,SAAS;GAJxE,UAAA,CAME,oBAAC,QAAD;IACE,aAAU;IACV,WAAU;IAEV,UAAA,oBAAC,eAAD,EAA4B,YAAc,CAAA;GACtC,CAAA,GACN,oBAAC,cAAD;IAAc,WAAU;IACtB,UAAA,oBAAC,iBAAD,CAAkB,CAAA;GACN,CAAA,CACC;EACjB,CAAA,GAAA,qBAAC,iBAAD;GAAiB,cAAY;GAAO,WAAW,YAAY;GAA3D,UAAA,CACE,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,oBAAC,eAAD,EACE,QACE,oBAAC,OAAD;MACE,cAAY,GAAG,MAAM;MACrB,aAAa;MACb,WAAW,GAAG,cAAc,QAAQ,YAAY,KAAK;KACtD,CAAA,EAEJ,CAAA,GACA,cACC,oBAAC,eAAD;MACE,aAAU;MACV,QACE,oBAAC,QAAD;OACE,SAAQ;OACR,MAAK;OACL,MAAM,oBAAC,OAAD,CAAQ,CAAA;OACd,cAAY,OAAO,SAAS,eAAe;OAC3C,WAAU;MACX,CAAA;KAEJ,CAAA,CAEA;;GACF,CAAA,GACL,oBAAC,mBAAD;IAA8B;IAAuB;GAAa,CAAA,CACnD;EACb,CAAA,CAAA;;AAEV"}
@@ -1,23 +1,29 @@
1
1
  import { Select as Select$1 } from "../../core/select.mjs";
2
2
  import "../../core/index.mjs";
3
+ import { Props as Props$1 } from "./select-multiple.mjs";
4
+ import { Props as Props$2 } from "./select-searchable.mjs";
3
5
  import React from "react";
4
6
  //#region src/components/atoms/select.d.ts
5
7
  declare const Core: typeof Select$1;
6
8
  interface Option {
7
9
  label: React.ReactNode;
8
10
  value: string;
11
+ disabled?: boolean;
9
12
  }
10
13
  interface OptionGroup {
11
14
  label: React.ReactNode;
12
15
  options: Option[];
13
16
  }
14
- interface Props extends Omit<React.ComponentProps<typeof Core>, 'onValueChange'> {
17
+ interface StaticProps extends Omit<React.ComponentProps<typeof Core>, 'onValueChange'> {
18
+ searchable?: false;
15
19
  placeholder?: string;
16
20
  className?: string;
17
21
  options?: (Option | OptionGroup)[];
18
22
  onChange?: (value: string) => void;
23
+ 'aria-label'?: string;
19
24
  }
20
- declare const Select: ({ className, placeholder, options, onChange, ...props }: Props) => React.JSX.Element;
25
+ type Props = StaticProps | Props$2 | Props$1;
26
+ declare const Select: (props: Props) => React.JSX.Element;
21
27
  //#endregion
22
28
  export { Props, Select as default };
23
29
  //# sourceMappingURL=select.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"select.d.mts","names":[],"sources":["../../../src/components/atoms/select.tsx"],"mappings":";;;;cAOU,aAAI;UASJ;EACR,OAAO,MAAM;EACb;;UAGQ;EACR,OAAO,MAAM;EACb,SAAS;;UAOM,cAAc,KAC7B,MAAM,sBAAsB;EAG5B;EACA;EACA,WAAW,SAAS;EACpB,YAAY;;cAGR,WAAU,WAAA,aAAA,SAAA,aAAA,SAMb,UAAK,MAAA,IAAA"}
1
+ {"version":3,"file":"select.d.mts","names":[],"sources":["../../../src/components/atoms/select.tsx"],"mappings":";;;;;;cAWU,aAAI;UASJ;EACR,OAAO,MAAM;EACb;EACA;;UAGQ;EACR,OAAO,MAAM;EACb,SAAS;;UAOD,oBAAoB,KAC5B,MAAM,sBAAsB;EAG5B;EACA;EACA;EACA,WAAW,SAAS;EACpB,YAAY;EACZ;;KAGU,QAAQ,cAAc,UAAkB;cA8D9C,SAAU,OAAO,UAAK,MAAA,IAAA"}
@@ -1,5 +1,7 @@
1
1
  import { cn } from "../../utils.mjs";
2
2
  import { select_exports } from "../../core/select.mjs";
3
+ import MultipleSelect from "./select-multiple.mjs";
4
+ import SearchableSelect from "./select-searchable.mjs";
3
5
  import "react";
4
6
  import { jsx, jsxs } from "react/jsx-runtime";
5
7
  //#region src/components/atoms/select.tsx
@@ -7,7 +9,7 @@ const { Select: Core, SelectContent, SelectGroup, SelectItem, SelectLabel, Selec
7
9
  const isGroup = (option) => {
8
10
  return "options" in option && Array.isArray(option.options);
9
11
  };
10
- const Select = ({ className, placeholder, options, onChange, ...props }) => {
12
+ const StaticSelect = ({ searchable: _searchable, className, placeholder, options, onChange, "aria-label": ariaLabel, ...props }) => {
11
13
  const items = options?.flatMap((option) => isGroup(option) ? option.options : option);
12
14
  return /* @__PURE__ */ jsxs(Core, {
13
15
  items,
@@ -16,17 +18,25 @@ const Select = ({ className, placeholder, options, onChange, ...props }) => {
16
18
  },
17
19
  ...props,
18
20
  children: [/* @__PURE__ */ jsx(SelectTrigger, {
21
+ "aria-label": ariaLabel,
19
22
  className: cn("w-full max-w-48", className),
20
23
  children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
21
24
  }), /* @__PURE__ */ jsx(SelectContent, { children: options?.map((option, index) => isGroup(option) ? /* @__PURE__ */ jsxs(SelectGroup, { children: [/* @__PURE__ */ jsx(SelectLabel, { children: option.label }), option.options.map((item) => /* @__PURE__ */ jsx(SelectItem, {
22
25
  value: item.value,
26
+ disabled: item.disabled,
23
27
  children: item.label
24
28
  }, item.value))] }, index) : /* @__PURE__ */ jsx(SelectItem, {
25
29
  value: option.value,
30
+ disabled: option.disabled,
26
31
  children: option.label
27
32
  }, option.value)) })]
28
33
  });
29
34
  };
35
+ const Select = (props) => {
36
+ if (props.multiple) return /* @__PURE__ */ jsx(MultipleSelect, { ...props });
37
+ if (props.searchable) return /* @__PURE__ */ jsx(SearchableSelect, { ...props });
38
+ return /* @__PURE__ */ jsx(StaticSelect, { ...props });
39
+ };
30
40
  //#endregion
31
41
  export { Select as default };
32
42
 
@@ -1 +1 @@
1
- {"version":3,"file":"select.mjs","names":["select"],"sources":["../../../src/components/atoms/select.tsx"],"sourcesContent":["import React from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { select } from '../../core';\n\nconst {\n Select: Core,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} = select;\n\ninterface Option {\n label: React.ReactNode;\n value: string;\n}\n\ninterface OptionGroup {\n label: React.ReactNode;\n options: Option[];\n}\n\nconst isGroup = (option: Option | OptionGroup): option is OptionGroup => {\n return 'options' in option && Array.isArray(option.options);\n};\n\nexport interface Props extends Omit<\n React.ComponentProps<typeof Core>,\n 'onValueChange'\n> {\n placeholder?: string;\n className?: string;\n options?: (Option | OptionGroup)[];\n onChange?: (value: string) => void;\n}\n\nconst Select = ({\n className,\n placeholder,\n options,\n onChange,\n ...props\n}: Props) => {\n const items = options?.flatMap(option =>\n isGroup(option) ? option.options : option,\n );\n\n return (\n <Core\n items={items}\n onValueChange={value => {\n if (value !== null) {\n onChange?.(value);\n }\n }}\n {...props}\n >\n <SelectTrigger className={cn('w-full max-w-48', className)}>\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options?.map((option, index) =>\n isGroup(option) ? (\n <SelectGroup key={index}>\n <SelectLabel>{option.label}</SelectLabel>\n {option.options.map(item => (\n <SelectItem key={item.value} value={item.value}>\n {item.label}\n </SelectItem>\n ))}\n </SelectGroup>\n ) : (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ),\n )}\n </SelectContent>\n </Core>\n );\n};\n\nexport default Select;\n"],"mappings":";;;;;AAMA,MAAM,EACJ,QAAQ,MACR,eACA,aACA,YACA,aACA,eACA,gBACEA;AAYJ,MAAM,WAAW,WAAwD;CACvE,OAAO,aAAa,UAAU,MAAM,QAAQ,OAAO,OAAO;AAC5D;AAYA,MAAM,UAAU,EACd,WACA,aACA,SACA,UACA,GAAG,YACQ;CACX,MAAM,QAAQ,SAAS,SAAQ,WAC7B,QAAQ,MAAM,IAAI,OAAO,UAAU,MACrC;CAEA,OACE,qBAAC,MAAD;EACS;EACP,gBAAe,UAAS;GACtB,IAAI,UAAU,MACZ,WAAW,KAAK;EAEpB;EACA,GAAI;EAPN,UAAA,CASE,oBAAC,eAAD;GAAe,WAAW,GAAG,mBAAmB,SAAS;GACvD,UAAA,oBAAC,aAAD,EAA0B,YAAc,CAAA;EAC3B,CAAA,GACf,oBAAC,eAAD,EAAA,UACG,SAAS,KAAK,QAAQ,UACrB,QAAQ,MAAM,IACZ,qBAAC,aAAD,EAAA,UAAA,CACE,oBAAC,aAAD,EAAA,UAAc,OAAO,MAAmB,CAAA,GACvC,OAAO,QAAQ,KAAI,SAClB,oBAAC,YAAD;GAA6B,OAAO,KAAK;GACtC,UAAA,KAAK;EACI,GAFK,KAAK,KAEV,CACb,CACU,EAAA,GAPK,KAOL,IAEb,oBAAC,YAAD;GAA+B,OAAO,OAAO;GAC1C,UAAA,OAAO;EACE,GAFK,OAAO,KAEZ,CAEhB,EACa,CAAA,CACX;;AAEV"}
1
+ {"version":3,"file":"select.mjs","names":["select"],"sources":["../../../src/components/atoms/select.tsx"],"sourcesContent":["import React from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { select } from '../../core';\nimport MultipleSelect, { type Props as MultipleProps } from './select-multiple';\nimport SearchableSelect, {\n type Props as SearchableProps,\n} from './select-searchable';\n\nconst {\n Select: Core,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} = select;\n\ninterface Option {\n label: React.ReactNode;\n value: string;\n disabled?: boolean;\n}\n\ninterface OptionGroup {\n label: React.ReactNode;\n options: Option[];\n}\n\nconst isGroup = (option: Option | OptionGroup): option is OptionGroup => {\n return 'options' in option && Array.isArray(option.options);\n};\n\ninterface StaticProps extends Omit<\n React.ComponentProps<typeof Core>,\n 'onValueChange'\n> {\n searchable?: false;\n placeholder?: string;\n className?: string;\n options?: (Option | OptionGroup)[];\n onChange?: (value: string) => void;\n 'aria-label'?: string;\n}\n\nexport type Props = StaticProps | SearchableProps | MultipleProps;\n\nconst StaticSelect = ({\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Do not forward the public mode switch to Base UI.\n searchable: _searchable,\n className,\n placeholder,\n options,\n onChange,\n 'aria-label': ariaLabel,\n ...props\n}: StaticProps) => {\n const items = options?.flatMap(option =>\n isGroup(option) ? option.options : option,\n );\n\n return (\n <Core\n items={items}\n onValueChange={value => {\n if (value !== null) {\n onChange?.(value);\n }\n }}\n {...props}\n >\n <SelectTrigger\n aria-label={ariaLabel}\n className={cn('w-full max-w-48', className)}\n >\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options?.map((option, index) =>\n isGroup(option) ? (\n <SelectGroup key={index}>\n <SelectLabel>{option.label}</SelectLabel>\n {option.options.map(item => (\n <SelectItem\n key={item.value}\n value={item.value}\n disabled={item.disabled}\n >\n {item.label}\n </SelectItem>\n ))}\n </SelectGroup>\n ) : (\n <SelectItem\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n >\n {option.label}\n </SelectItem>\n ),\n )}\n </SelectContent>\n </Core>\n );\n};\n\nconst Select = (props: Props) => {\n if (props.multiple) {\n return <MultipleSelect {...props} />;\n }\n\n if (props.searchable) {\n return <SearchableSelect {...props} />;\n }\n\n return <StaticSelect {...props} />;\n};\n\nexport default Select;\n"],"mappings":";;;;;;;AAUA,MAAM,EACJ,QAAQ,MACR,eACA,aACA,YACA,aACA,eACA,gBACEA;AAaJ,MAAM,WAAW,WAAwD;CACvE,OAAO,aAAa,UAAU,MAAM,QAAQ,OAAO,OAAO;AAC5D;AAgBA,MAAM,gBAAgB,EAEpB,YAAY,aACZ,WACA,aACA,SACA,UACA,cAAc,WACd,GAAG,YACc;CACjB,MAAM,QAAQ,SAAS,SAAQ,WAC7B,QAAQ,MAAM,IAAI,OAAO,UAAU,MACrC;CAEA,OACE,qBAAC,MAAD;EACS;EACP,gBAAe,UAAS;GACtB,IAAI,UAAU,MACZ,WAAW,KAAK;EAEpB;EACA,GAAI;EAPN,UAAA,CASE,oBAAC,eAAD;GACE,cAAY;GACZ,WAAW,GAAG,mBAAmB,SAAS;GAE1C,UAAA,oBAAC,aAAD,EAA0B,YAAc,CAAA;EAC3B,CAAA,GACf,oBAAC,eAAD,EAAA,UACG,SAAS,KAAK,QAAQ,UACrB,QAAQ,MAAM,IACZ,qBAAC,aAAD,EAAA,UAAA,CACE,oBAAC,aAAD,EAAA,UAAc,OAAO,MAAmB,CAAA,GACvC,OAAO,QAAQ,KAAI,SAClB,oBAAC,YAAD;GAEE,OAAO,KAAK;GACZ,UAAU,KAAK;GAEd,UAAA,KAAK;EACI,GALL,KAAK,KAKA,CACb,CACU,EAAA,GAXK,KAWL,IAEb,oBAAC,YAAD;GAEE,OAAO,OAAO;GACd,UAAU,OAAO;GAEhB,UAAA,OAAO;EACE,GALL,OAAO,KAKF,CAEhB,EACa,CAAA,CACX;;AAEV;AAEA,MAAM,UAAU,UAAiB;CAC/B,IAAI,MAAM,UACR,OAAO,oBAAC,gBAAD,EAAgB,GAAI,MAAQ,CAAA;CAGrC,IAAI,MAAM,YACR,OAAO,oBAAC,kBAAD,EAAkB,GAAI,MAAQ,CAAA;CAGvC,OAAO,oBAAC,cAAD,EAAc,GAAI,MAAQ,CAAA;AACnC"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../../../utils.mjs";
3
3
  import { slider_exports } from "../../../core/slider.mjs";
4
- import { jsx } from "react/jsx-runtime";
5
4
  import { useControllableState } from "@jbpark/use-hooks";
5
+ import { jsx } from "react/jsx-runtime";
6
6
  //#region src/components/atoms/slider/slider.tsx
7
7
  const { Slider: Core } = slider_exports;
8
8
  const toArray = (value) => {
@@ -2,8 +2,8 @@
2
2
  import { cn } from "../../utils.mjs";
3
3
  import { useConfig } from "../../providers/config/context.mjs";
4
4
  import { switch_exports } from "../../core/switch.mjs";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
5
  import { useControllableState } from "@jbpark/use-hooks";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/components/atoms/switch.tsx
8
8
  const { Switch: Core } = switch_exports;
9
9
  const sizeConfig = {
@@ -0,0 +1,21 @@
1
+ import { ReactElement, ReactNode } from "react";
2
+ //#region src/components/atoms/tooltip.d.ts
3
+ declare const Core: <Payload>(props: import("@base-ui/react").TooltipRoot.Props<Payload>) => React.JSX.Element;
4
+ type Placement = 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom';
5
+ interface Props extends Omit<React.ComponentProps<typeof Core>, 'children' | 'handle' | 'triggerId' | 'defaultTriggerId'> {
6
+ children: ReactElement;
7
+ /** Supplementary visual hint; keep essential information visible elsewhere. */
8
+ content: ReactNode;
9
+ /** Popup class; the child retains its own className. */
10
+ className?: string;
11
+ placement?: Placement;
12
+ /** Distance between the trigger and popup in pixels. */
13
+ offset?: number;
14
+ /** Hover delay in milliseconds. Keyboard focus opens without this delay. */
15
+ delay?: number;
16
+ closeDelay?: number;
17
+ }
18
+ declare const Tooltip: ({ children, content, className, placement, offset, delay, closeDelay, ...props }: Props) => import("react").JSX.Element;
19
+ //#endregion
20
+ export { Props, Tooltip as default };
21
+ //# sourceMappingURL=tooltip.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.d.mts","names":[],"sources":["../../../src/components/atoms/tooltip.tsx"],"mappings":";;cAMiB,OAAI,SAAA,gCAAA,YAAA,MAAA,aAAA,MAAA,IAAA;KAEhB;UAgCY,cAAc,KAC7B,MAAM,sBAAsB;EAG5B,UAAU;;EAEV,SAAS;;EAET;EACA,YAAY;;EAEZ;;EAEA;EACA;;cAGI,YAAW,UAAA,SAAA,WAAA,WAAA,QAAA,OAAA,eAAA,SASd,0BAAK,IAAA"}