@intlayer/design-system 8.10.1 → 8.11.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 (215) hide show
  1. package/dist/esm/api/hooks/ai.mjs +66 -0
  2. package/dist/esm/api/hooks/ai.mjs.map +1 -0
  3. package/dist/esm/api/hooks/audit.mjs +34 -0
  4. package/dist/esm/api/hooks/audit.mjs.map +1 -0
  5. package/dist/esm/api/hooks/auth.mjs +217 -0
  6. package/dist/esm/api/hooks/auth.mjs.map +1 -0
  7. package/dist/esm/api/hooks/bitbucket.mjs +39 -0
  8. package/dist/esm/api/hooks/bitbucket.mjs.map +1 -0
  9. package/dist/esm/api/hooks/dictionary.mjs +105 -0
  10. package/dist/esm/api/hooks/dictionary.mjs.map +1 -0
  11. package/dist/esm/api/hooks/discussions.mjs +37 -0
  12. package/dist/esm/api/hooks/discussions.mjs.map +1 -0
  13. package/dist/esm/api/hooks/editor.mjs +24 -0
  14. package/dist/esm/api/hooks/editor.mjs.map +1 -0
  15. package/dist/esm/api/hooks/github.mjs +54 -0
  16. package/dist/esm/api/hooks/github.mjs.map +1 -0
  17. package/dist/esm/api/hooks/gitlab.mjs +43 -0
  18. package/dist/esm/api/hooks/gitlab.mjs.map +1 -0
  19. package/dist/esm/api/hooks/newsletter.mjs +31 -0
  20. package/dist/esm/api/hooks/newsletter.mjs.map +1 -0
  21. package/dist/esm/api/hooks/organization.mjs +123 -0
  22. package/dist/esm/api/hooks/organization.mjs.map +1 -0
  23. package/dist/esm/api/hooks/project.mjs +187 -0
  24. package/dist/esm/api/hooks/project.mjs.map +1 -0
  25. package/dist/esm/api/hooks/reviewer.mjs +288 -0
  26. package/dist/esm/api/hooks/reviewer.mjs.map +1 -0
  27. package/dist/esm/api/hooks/search.mjs +18 -0
  28. package/dist/esm/api/hooks/search.mjs.map +1 -0
  29. package/dist/esm/api/hooks/showcaseProject.mjs +95 -0
  30. package/dist/esm/api/hooks/showcaseProject.mjs.map +1 -0
  31. package/dist/esm/api/hooks/stripe.mjs +261 -0
  32. package/dist/esm/api/hooks/stripe.mjs.map +1 -0
  33. package/dist/esm/api/hooks/tag.mjs +45 -0
  34. package/dist/esm/api/hooks/tag.mjs.map +1 -0
  35. package/dist/esm/api/hooks/translate.mjs +47 -0
  36. package/dist/esm/api/hooks/translate.mjs.map +1 -0
  37. package/dist/esm/api/hooks/user.mjs +58 -0
  38. package/dist/esm/api/hooks/user.mjs.map +1 -0
  39. package/dist/esm/api/hooks/utils.mjs +30 -0
  40. package/dist/esm/api/hooks/utils.mjs.map +1 -0
  41. package/dist/esm/api/index.mjs +26 -0
  42. package/dist/esm/{hooks → api}/useAuth/useAuth.mjs +1 -1
  43. package/dist/esm/api/useAuth/useAuth.mjs.map +1 -0
  44. package/dist/esm/{hooks → api}/useAuth/useOAuth2.mjs +3 -3
  45. package/dist/esm/api/useAuth/useOAuth2.mjs.map +1 -0
  46. package/dist/esm/{hooks → api}/useAuth/useSession.mjs +3 -4
  47. package/dist/esm/api/useAuth/useSession.mjs.map +1 -0
  48. package/dist/esm/api/useIntlayerAPI.mjs +123 -0
  49. package/dist/esm/api/useIntlayerAPI.mjs.map +1 -0
  50. package/dist/esm/{hooks → api}/useUser/index.mjs +2 -2
  51. package/dist/esm/api/useUser/index.mjs.map +1 -0
  52. package/dist/esm/components/Badge/index.mjs +1 -3
  53. package/dist/esm/components/Badge/index.mjs.map +1 -1
  54. package/dist/esm/components/Breadcrumb/index.mjs +0 -1
  55. package/dist/esm/components/Breadcrumb/index.mjs.map +1 -1
  56. package/dist/esm/components/Button/Button.mjs +3 -5
  57. package/dist/esm/components/Button/Button.mjs.map +1 -1
  58. package/dist/esm/components/Carousel/index.mjs +3 -3
  59. package/dist/esm/components/Carousel/index.mjs.map +1 -1
  60. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
  61. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs.map +1 -1
  62. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +1 -1
  63. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
  64. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +3 -2
  65. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs.map +1 -1
  66. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +4 -2
  67. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
  68. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -2
  69. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
  70. package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs +2 -2
  71. package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs.map +1 -1
  72. package/dist/esm/components/IDE/FileList.mjs +1 -1
  73. package/dist/esm/components/IDE/FileList.mjs.map +1 -1
  74. package/dist/esm/components/IDE/IDE.mjs +1 -1
  75. package/dist/esm/components/IDE/IDE.mjs.map +1 -1
  76. package/dist/esm/components/Input/Checkbox.mjs +1 -3
  77. package/dist/esm/components/Input/Checkbox.mjs.map +1 -1
  78. package/dist/esm/components/Label/index.mjs +2 -2
  79. package/dist/esm/components/Label/index.mjs.map +1 -1
  80. package/dist/esm/components/LanguageBackground/LanguageSection.mjs +8 -6
  81. package/dist/esm/components/LanguageBackground/LanguageSection.mjs.map +1 -1
  82. package/dist/esm/components/LanguageBackground/index.mjs +13 -5
  83. package/dist/esm/components/LanguageBackground/index.mjs.map +1 -1
  84. package/dist/esm/components/Link/Link.mjs +0 -7
  85. package/dist/esm/components/Link/Link.mjs.map +1 -1
  86. package/dist/esm/components/Loader/spinner.mjs +1 -1
  87. package/dist/esm/components/Loader/spinner.mjs.map +1 -1
  88. package/dist/esm/components/Pagination/Pagination.mjs +1 -2
  89. package/dist/esm/components/Pagination/Pagination.mjs.map +1 -1
  90. package/dist/esm/components/Popover/dynamic.mjs +2 -2
  91. package/dist/esm/components/Popover/dynamic.mjs.map +1 -1
  92. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +3 -5
  93. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs.map +1 -1
  94. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +3 -3
  95. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs.map +1 -1
  96. package/dist/esm/components/TabSelector/TabSelector.mjs +3 -3
  97. package/dist/esm/components/TabSelector/TabSelector.mjs.map +1 -1
  98. package/dist/esm/components/Tag/index.mjs +1 -1
  99. package/dist/esm/components/Tag/index.mjs.map +1 -1
  100. package/dist/esm/components/Terminal/Terminal.mjs +1 -1
  101. package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
  102. package/dist/esm/components/TextArea/AutoSizeTextArea.mjs +2 -2
  103. package/dist/esm/components/TextArea/AutoSizeTextArea.mjs.map +1 -1
  104. package/dist/esm/components/TextArea/ContentEditableTextArea.mjs +3 -3
  105. package/dist/esm/components/TextArea/ContentEditableTextArea.mjs.map +1 -1
  106. package/dist/esm/components/Toaster/Toast.mjs +3 -3
  107. package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
  108. package/dist/esm/components/WithResizer/index.mjs +7 -2
  109. package/dist/esm/components/WithResizer/index.mjs.map +1 -1
  110. package/dist/esm/hooks/index.mjs +1 -7
  111. package/dist/esm/libs/auth.mjs +1 -1
  112. package/dist/esm/routes.mjs +23 -1
  113. package/dist/esm/routes.mjs.map +1 -1
  114. package/dist/types/api/hooks/ai.d.ts +12 -0
  115. package/dist/types/api/hooks/ai.d.ts.map +1 -0
  116. package/dist/types/api/hooks/audit.d.ts +10 -0
  117. package/dist/types/api/hooks/audit.d.ts.map +1 -0
  118. package/dist/types/api/hooks/auth.d.ts +29 -0
  119. package/dist/types/api/hooks/auth.d.ts.map +1 -0
  120. package/dist/types/api/hooks/bitbucket.d.ts +8 -0
  121. package/dist/types/api/hooks/bitbucket.d.ts.map +1 -0
  122. package/dist/types/api/hooks/dictionary.d.ts +15 -0
  123. package/dist/types/api/hooks/dictionary.d.ts.map +1 -0
  124. package/dist/types/api/hooks/discussions.d.ts +8 -0
  125. package/dist/types/api/hooks/discussions.d.ts.map +1 -0
  126. package/dist/types/api/hooks/editor.d.ts +6 -0
  127. package/dist/types/api/hooks/editor.d.ts.map +1 -0
  128. package/dist/types/api/hooks/github.d.ts +12 -0
  129. package/dist/types/api/hooks/github.d.ts.map +1 -0
  130. package/dist/types/api/hooks/gitlab.d.ts +8 -0
  131. package/dist/types/api/hooks/gitlab.d.ts.map +1 -0
  132. package/dist/types/api/hooks/newsletter.d.ts +7 -0
  133. package/dist/types/api/hooks/newsletter.d.ts.map +1 -0
  134. package/dist/types/api/hooks/organization.d.ts +16 -0
  135. package/dist/types/api/hooks/organization.d.ts.map +1 -0
  136. package/dist/types/api/hooks/project.d.ts +23 -0
  137. package/dist/types/api/hooks/project.d.ts.map +1 -0
  138. package/dist/types/api/hooks/reviewer.d.ts +39 -0
  139. package/dist/types/api/hooks/reviewer.d.ts.map +1 -0
  140. package/dist/types/api/hooks/search.d.ts +7 -0
  141. package/dist/types/api/hooks/search.d.ts.map +1 -0
  142. package/dist/types/api/hooks/showcaseProject.d.ts +16 -0
  143. package/dist/types/api/hooks/showcaseProject.d.ts.map +1 -0
  144. package/dist/types/api/hooks/stripe.d.ts +33 -0
  145. package/dist/types/api/hooks/stripe.d.ts.map +1 -0
  146. package/dist/types/api/hooks/tag.d.ts +11 -0
  147. package/dist/types/api/hooks/tag.d.ts.map +1 -0
  148. package/dist/types/api/hooks/translate.d.ts +8 -0
  149. package/dist/types/api/hooks/translate.d.ts.map +1 -0
  150. package/dist/types/api/hooks/user.d.ts +13 -0
  151. package/dist/types/api/hooks/user.d.ts.map +1 -0
  152. package/dist/types/api/hooks/utils.d.ts +23 -0
  153. package/dist/types/api/hooks/utils.d.ts.map +1 -0
  154. package/dist/types/api/index.d.ts +25 -0
  155. package/dist/types/{hooks → api}/useAuth/useAuth.d.ts +1 -1
  156. package/dist/types/api/useAuth/useAuth.d.ts.map +1 -0
  157. package/dist/types/{hooks → api}/useAuth/useOAuth2.d.ts +1 -1
  158. package/dist/types/api/useAuth/useOAuth2.d.ts.map +1 -0
  159. package/dist/types/{hooks → api}/useAuth/useSession.d.ts +1 -1
  160. package/dist/types/api/useAuth/useSession.d.ts.map +1 -0
  161. package/dist/types/api/useIntlayerAPI.d.ts +297 -0
  162. package/dist/types/api/useIntlayerAPI.d.ts.map +1 -0
  163. package/dist/types/{hooks → api}/useUser/index.d.ts +1 -1
  164. package/dist/types/api/useUser/index.d.ts.map +1 -0
  165. package/dist/types/components/Badge/index.d.ts +3 -4
  166. package/dist/types/components/Badge/index.d.ts.map +1 -1
  167. package/dist/types/components/Breadcrumb/index.d.ts.map +1 -1
  168. package/dist/types/components/Button/Button.d.ts +5 -6
  169. package/dist/types/components/Button/Button.d.ts.map +1 -1
  170. package/dist/types/components/Carousel/index.d.ts.map +1 -1
  171. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +1 -1
  172. package/dist/types/components/Container/index.d.ts +4 -4
  173. package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
  174. package/dist/types/components/ExpandCollapse/ExpandCollapse.d.ts.map +1 -1
  175. package/dist/types/components/Input/Checkbox.d.ts +1 -2
  176. package/dist/types/components/Input/Checkbox.d.ts.map +1 -1
  177. package/dist/types/components/LanguageBackground/index.d.ts.map +1 -1
  178. package/dist/types/components/Link/Link.d.ts +2 -3
  179. package/dist/types/components/Link/Link.d.ts.map +1 -1
  180. package/dist/types/components/Loader/spinner.d.ts +1 -1
  181. package/dist/types/components/Pagination/Pagination.d.ts +1 -1
  182. package/dist/types/components/Pagination/Pagination.d.ts.map +1 -1
  183. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts +2 -3
  184. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts.map +1 -1
  185. package/dist/types/components/SwitchSelector/VerticalSwitchSelector.d.ts +1 -1
  186. package/dist/types/components/SwitchSelector/VerticalSwitchSelector.d.ts.map +1 -1
  187. package/dist/types/components/TabSelector/TabSelector.d.ts +2 -2
  188. package/dist/types/components/TabSelector/TabSelector.d.ts.map +1 -1
  189. package/dist/types/components/Tag/index.d.ts +2 -2
  190. package/dist/types/components/TextArea/ContentEditableTextArea.d.ts.map +1 -1
  191. package/dist/types/components/Toaster/Toast.d.ts +1 -1
  192. package/dist/types/hooks/index.d.ts +1 -7
  193. package/dist/types/providers/ReactQueryProvider.d.ts +1 -1
  194. package/dist/types/routes.d.ts +23 -1
  195. package/dist/types/routes.d.ts.map +1 -1
  196. package/package.json +35 -30
  197. package/tailwind.css +0 -19
  198. package/dist/esm/hooks/reactQuery.mjs +0 -1187
  199. package/dist/esm/hooks/reactQuery.mjs.map +0 -1
  200. package/dist/esm/hooks/useAuth/useAuth.mjs.map +0 -1
  201. package/dist/esm/hooks/useAuth/useOAuth2.mjs.map +0 -1
  202. package/dist/esm/hooks/useAuth/useSession.mjs.map +0 -1
  203. package/dist/esm/hooks/useIntlayerAPI.mjs +0 -22
  204. package/dist/esm/hooks/useIntlayerAPI.mjs.map +0 -1
  205. package/dist/esm/hooks/useUser/index.mjs.map +0 -1
  206. package/dist/types/hooks/reactQuery.d.ts +0 -188
  207. package/dist/types/hooks/reactQuery.d.ts.map +0 -1
  208. package/dist/types/hooks/useAuth/useAuth.d.ts.map +0 -1
  209. package/dist/types/hooks/useAuth/useOAuth2.d.ts.map +0 -1
  210. package/dist/types/hooks/useAuth/useSession.d.ts.map +0 -1
  211. package/dist/types/hooks/useIntlayerAPI.d.ts +0 -14
  212. package/dist/types/hooks/useIntlayerAPI.d.ts.map +0 -1
  213. package/dist/types/hooks/useUser/index.d.ts.map +0 -1
  214. /package/dist/esm/{hooks → api}/useAuth/index.mjs +0 -0
  215. /package/dist/types/{hooks → api}/useAuth/index.d.ts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"SwitchSelector.mjs","names":[],"sources":["../../../../src/components/SwitchSelector/SwitchSelector.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { useSwitchSelector } from './useSwitchSelector';\n\nexport type SwitchSelectorChoice<T = boolean> = {\n content: ReactNode;\n value: T;\n} & HTMLAttributes<HTMLButtonElement>;\nexport type SwitchSelectorChoices<T> = SwitchSelectorChoice<T>[];\n\nexport const defaultChoices: SwitchSelectorChoices<boolean> = [\n { content: 'Off', value: false },\n { content: 'On', value: true },\n];\n\nexport enum SwitchSelectorColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n DESTRUCTIVE = 'destructive',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n}\n\nexport enum SwitchSelectorSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n}\n\nexport type SwitchSelectorBaseProps<T = boolean> = {\n choices?: SwitchSelectorChoices<T>;\n value?: T;\n defaultValue?: T;\n onChange?: (choice: T) => void;\n className?: string;\n itemClassName?: string;\n hoverable?: boolean;\n disabled?: boolean;\n [key: string]: any;\n};\n\nexport type SwitchSelectorProps<T = boolean> = SwitchSelectorBaseProps<T> &\n VariantProps<typeof switchSelectorVariant> &\n VariantProps<typeof choiceVariant>;\n\nexport const switchSelectorVariant = cva(\n 'flex h-fit w-fit cursor-pointer flex-row gap-2 rounded-full border-[1.3px] p-[1.5px]',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]: 'border-primary text-primary',\n [`${SwitchSelectorColor.SECONDARY}`]: 'border-secondary text-secondary',\n [`${SwitchSelectorColor.DESTRUCTIVE}`]:\n 'border-destructive bg-destructive text-destructive',\n [`${SwitchSelectorColor.NEUTRAL}`]: 'border-neutral text-neutral',\n [`${SwitchSelectorColor.LIGHT}`]: 'border-white text-white',\n [`${SwitchSelectorColor.DARK}`]: 'border-neutral-800 text-neutral-800',\n [`${SwitchSelectorColor.TEXT}`]: 'border-text text-text',\n },\n disabled: {\n true: 'cursor-not-allowed opacity-50',\n false: '',\n },\n },\n defaultVariants: {\n color: `${SwitchSelectorColor.PRIMARY}`,\n disabled: false,\n },\n }\n);\n\nexport const choiceVariant = cva(\n 'z-1 flex-1 cursor-pointer font-medium text-sm transition-all duration-300 ease-in-out aria-selected:cursor-default data-[indicator=true]:text-text-opposite motion-reduce:transition-none',\n {\n variants: {\n size: {\n [`${SwitchSelectorSize.XS}`]: 'px-2 py-0.5 text-xs',\n [`${SwitchSelectorSize.SM}`]: 'px-2 py-1 text-xs',\n [`${SwitchSelectorSize.MD}`]: 'p-2 text-sm',\n [`${SwitchSelectorSize.LG}`]: 'p-4 text-base',\n },\n },\n defaultVariants: {\n size: `${SwitchSelectorSize.MD}`,\n },\n }\n);\n\nexport const indicatorVariant = cva(\n 'absolute top-0 z-0 h-full w-auto rounded-full transition-all duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]:\n 'bg-primary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.SECONDARY}`]:\n 'bg-secondary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.DESTRUCTIVE}`]:\n 'bg-destructive data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.NEUTRAL}`]:\n 'bg-neutral data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.LIGHT}`]:\n 'bg-white data-[indicator=true]:text-black',\n [`${SwitchSelectorColor.DARK}`]:\n 'bg-neutral-800 data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.TEXT}`]:\n 'bg-text data-[indicator=true]:text-text-opposite',\n },\n },\n }\n);\n\n/**\n * Component that allows the user to select one of the provided choices.\n * This component is horizontal.\n */\nexport const SwitchSelector = <T,>(props: SwitchSelectorProps<T>) => {\n const {\n choices = defaultChoices as SwitchSelectorChoices<T>,\n color = SwitchSelectorColor.PRIMARY,\n size = SwitchSelectorSize.MD,\n className,\n itemClassName,\n ...baseProps\n } = props;\n\n const {\n selectedIndex,\n indicatorIndex,\n handleChange,\n optionsRefs,\n indicatorRef,\n choiceIndicatorPosition,\n setHoveredIndex,\n disabled,\n } = useSwitchSelector(\n {\n choices,\n value: props.value,\n defaultValue: props.defaultValue,\n onChange: props.onChange,\n hoverable: props.hoverable,\n disabled: props.disabled,\n },\n 'horizontal'\n );\n\n return (\n <div\n className={switchSelectorVariant({\n color,\n disabled,\n className,\n })}\n role=\"tablist\"\n aria-disabled={disabled ? 'true' : undefined}\n >\n <div className=\"relative flex h-full w-full flex-row items-center justify-center\">\n {choices.map((choice, index) => {\n const { content, value, ...buttonProps } = choice;\n\n const isKeyOfKey =\n typeof value === 'string' || typeof value === 'number';\n\n const isSelected = index === selectedIndex;\n const isIndicatorOwner = index === indicatorIndex;\n\n return (\n <button\n {...buttonProps}\n className={cn(\n choiceVariant({\n size,\n }),\n disabled && 'cursor-not-allowed',\n itemClassName\n )}\n key={isKeyOfKey ? value : index}\n role=\"tab\"\n onClick={() => handleChange(value)}\n aria-selected={isSelected ? 'true' : undefined}\n data-indicator={isIndicatorOwner ? 'true' : undefined}\n disabled={disabled}\n tabIndex={isSelected ? 0 : -1}\n ref={(el) => {\n optionsRefs.current[index] = el!;\n }}\n onMouseEnter={() => !disabled && setHoveredIndex(index)}\n onMouseLeave={() => !disabled && setHoveredIndex(null)}\n >\n {content}\n </button>\n );\n })}\n\n {choiceIndicatorPosition && (\n <div\n className={cn(\n indicatorVariant({\n color,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n />\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;AAaA,MAAa,iBAAiD,CAC5D;CAAE,SAAS;CAAO,OAAO;AAAM,GAC/B;CAAE,SAAS;CAAM,OAAO;AAAK,CAC/B;AAEA,IAAY,sBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,IAAY,qBAAL;CACL;CACA;CACA;CACA;;AACF;AAkBA,MAAa,wBAAwB,IACnC,wFACA;CACE,UAAU;EACR,OAAO;IACJ,YAAmC;IACnC,cAAqC;IACrC,gBACC;IACD,YAAmC;IACnC,UAAiC;IACjC,SAAgC;IAChC,SAAgC;EACnC;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,OAAO;EACP,UAAU;CACZ;AACF,CACF;AAEA,MAAa,gBAAgB,IAC3B,6LACA;CACE,UAAU,EACR,MAAM;GACH,OAA6B;GAC7B,OAA6B;GAC7B,OAA6B;GAC7B,OAA6B;CAChC,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CACF;AAEA,MAAa,mBAAmB,IAC9B,uHACA,EACE,UAAU,EACR,OAAO;EACJ,YACC;EACD,cACC;EACD,gBACC;EACD,YACC;EACD,UACC;EACD,SACC;EACD,SACC;AACJ,EACF,EACF,CACF;;;;;AAMA,MAAa,kBAAsB,UAAkC;CACnE,MAAM,EACJ,UAAU,gBACV,mBACA,aACA,WACA,eACA,GAAG,cACD;CAEJ,MAAM,EACJ,eACA,gBACA,cACA,aACA,cACA,yBACA,iBACA,aACE,kBACF;EACE;EACA,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,UAAU,MAAM;CAClB,GACA,YACF;CAEA,OACE,oBAAC,OAAD;EACE,WAAW,sBAAsB;GAC/B;GACA;GACA;EACF,CAAC;EACD,MAAK;EACL,iBAAe,WAAW,SAAS;YAEnC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,QAAQ,KAAK,QAAQ,UAAU;IAC9B,MAAM,EAAE,SAAS,OAAO,GAAG,gBAAgB;IAE3C,MAAM,aACJ,OAAO,UAAU,YAAY,OAAO,UAAU;IAEhD,MAAM,aAAa,UAAU;IAC7B,MAAM,mBAAmB,UAAU;IAEnC,OACE,8BAAC,UAAD;KACE,GAAI;KACJ,WAAW,GACT,cAAc,EACZ,KACF,CAAC,GACD,YAAY,sBACZ,aACF;KACA,KAAK,aAAa,QAAQ;KAC1B,MAAK;KACL,eAAe,aAAa,KAAK;KACjC,iBAAe,aAAa,SAAS;KACrC,kBAAgB,mBAAmB,SAAS;KAClC;KACV,UAAU,aAAa,IAAI;KAC3B,MAAM,OAAO;MACX,YAAY,QAAQ,SAAS;KAC/B;KACA,oBAAoB,CAAC,YAAY,gBAAgB,KAAK;KACtD,oBAAoB,CAAC,YAAY,gBAAgB,IAAI;IAG/C,GADL,OACK;GAEZ,CAAC,GAEA,2BACC,oBAAC,OAAD;IACE,WAAW,GACT,iBAAiB,EACf,MACF,CAAC,CACH;IACA,OAAO;IACP,KAAK;GACN,EAEA;;CACF;AAET"}
1
+ {"version":3,"file":"SwitchSelector.mjs","names":[],"sources":["../../../../src/components/SwitchSelector/SwitchSelector.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { useSwitchSelector } from './useSwitchSelector';\n\nexport type SwitchSelectorChoice<T = boolean> = {\n content: ReactNode;\n value: T;\n} & HTMLAttributes<HTMLButtonElement>;\nexport type SwitchSelectorChoices<T> = SwitchSelectorChoice<T>[];\n\nexport const defaultChoices: SwitchSelectorChoices<boolean> = [\n { content: 'Off', value: false },\n { content: 'On', value: true },\n];\n\nexport enum SwitchSelectorColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n}\n\nexport enum SwitchSelectorSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n}\n\nexport type SwitchSelectorBaseProps<T = boolean> = {\n choices?: SwitchSelectorChoices<T>;\n value?: T;\n defaultValue?: T;\n onChange?: (choice: T) => void;\n className?: string;\n itemClassName?: string;\n hoverable?: boolean;\n disabled?: boolean;\n [key: string]: any;\n};\n\nexport type SwitchSelectorProps<T = boolean> = SwitchSelectorBaseProps<T> &\n VariantProps<typeof switchSelectorVariant> &\n VariantProps<typeof choiceVariant>;\n\nexport const switchSelectorVariant = cva(\n 'flex h-fit w-fit cursor-pointer flex-row gap-2 rounded-full border-[1.3px] p-[1.5px]',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]: 'border-primary text-primary',\n [`${SwitchSelectorColor.SECONDARY}`]: 'border-secondary text-secondary',\n [`${SwitchSelectorColor.NEUTRAL}`]: 'border-neutral text-neutral',\n [`${SwitchSelectorColor.LIGHT}`]: 'border-white text-white',\n [`${SwitchSelectorColor.DARK}`]: 'border-neutral-800 text-neutral-800',\n [`${SwitchSelectorColor.TEXT}`]: 'border-text text-text',\n },\n disabled: {\n true: 'cursor-not-allowed opacity-50',\n false: '',\n },\n },\n defaultVariants: {\n color: `${SwitchSelectorColor.TEXT}`,\n disabled: false,\n },\n }\n);\n\nexport const choiceVariant = cva(\n 'z-1 flex-1 cursor-pointer font-medium text-sm transition-all duration-300 ease-in-out aria-selected:cursor-default data-[indicator=true]:text-text-opposite motion-reduce:transition-none',\n {\n variants: {\n size: {\n [`${SwitchSelectorSize.XS}`]: 'px-2 py-0.5 text-xs',\n [`${SwitchSelectorSize.SM}`]: 'px-2 py-1 text-xs',\n [`${SwitchSelectorSize.MD}`]: 'p-2 text-sm',\n [`${SwitchSelectorSize.LG}`]: 'p-4 text-base',\n },\n },\n defaultVariants: {\n size: `${SwitchSelectorSize.MD}`,\n },\n }\n);\n\nexport const indicatorVariant = cva(\n 'absolute top-0 z-0 h-full w-auto rounded-full transition-all duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]:\n 'bg-primary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.SECONDARY}`]:\n 'bg-secondary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.ERROR}`]:\n 'bg-error data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.NEUTRAL}`]:\n 'bg-neutral data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.LIGHT}`]:\n 'bg-white data-[indicator=true]:text-black',\n [`${SwitchSelectorColor.DARK}`]:\n 'bg-neutral-800 data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.TEXT}`]:\n 'bg-text data-[indicator=true]:text-text-opposite',\n },\n },\n }\n);\n\n/**\n * Component that allows the user to select one of the provided choices.\n * This component is horizontal.\n */\nexport const SwitchSelector = <T,>(props: SwitchSelectorProps<T>) => {\n const {\n choices = defaultChoices as SwitchSelectorChoices<T>,\n color = SwitchSelectorColor.PRIMARY,\n size = SwitchSelectorSize.MD,\n className,\n itemClassName,\n } = props;\n\n const {\n selectedIndex,\n indicatorIndex,\n handleChange,\n optionsRefs,\n indicatorRef,\n choiceIndicatorPosition,\n setHoveredIndex,\n disabled,\n } = useSwitchSelector(\n {\n choices,\n value: props.value,\n defaultValue: props.defaultValue,\n onChange: props.onChange,\n hoverable: props.hoverable,\n disabled: props.disabled,\n },\n 'horizontal'\n );\n\n return (\n <div\n className={switchSelectorVariant({\n color,\n disabled,\n className,\n })}\n role=\"tablist\"\n aria-disabled={disabled ? 'true' : undefined}\n >\n <div className=\"relative flex h-full w-full flex-row items-center justify-center\">\n {choices.map((choice, index) => {\n const { content, value, ...buttonProps } = choice;\n\n const isKeyOfKey =\n typeof value === 'string' || typeof value === 'number';\n\n const isSelected = index === selectedIndex;\n const isIndicatorOwner = index === indicatorIndex;\n\n return (\n <button\n {...buttonProps}\n className={cn(\n choiceVariant({\n size,\n }),\n disabled && 'cursor-not-allowed',\n itemClassName\n )}\n key={isKeyOfKey ? value : index}\n role=\"tab\"\n onClick={() => handleChange(value)}\n aria-selected={isSelected ? 'true' : undefined}\n data-indicator={isIndicatorOwner ? 'true' : undefined}\n disabled={disabled}\n tabIndex={isSelected ? 0 : -1}\n ref={(el) => {\n optionsRefs.current[index] = el!;\n }}\n onMouseEnter={() => !disabled && setHoveredIndex(index)}\n onMouseLeave={() => !disabled && setHoveredIndex(null)}\n >\n {content}\n </button>\n );\n })}\n\n {choiceIndicatorPosition && (\n <div\n className={cn(\n indicatorVariant({\n color,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n />\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;AAaA,MAAa,iBAAiD,CAC5D;CAAE,SAAS;CAAO,OAAO;AAAM,GAC/B;CAAE,SAAS;CAAM,OAAO;AAAK,CAC/B;AAEA,IAAY,sBAAL;CACL;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,IAAY,qBAAL;CACL;CACA;CACA;CACA;;AACF;AAkBA,MAAa,wBAAwB,IACnC,wFACA;CACE,UAAU;EACR,OAAO;IACJ,YAAmC;IACnC,cAAqC;IACrC,YAAmC;IACnC,UAAiC;IACjC,SAAgC;IAChC,SAAgC;EACnC;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,OAAO;EACP,UAAU;CACZ;AACF,CACF;AAEA,MAAa,gBAAgB,IAC3B,6LACA;CACE,UAAU,EACR,MAAM;GACH,OAA6B;GAC7B,OAA6B;GAC7B,OAA6B;GAC7B,OAA6B;CAChC,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CACF;AAEA,MAAa,mBAAmB,IAC9B,uHACA,EACE,UAAU,EACR,OAAO;EACJ,YACC;EACD,cACC;EACD,GAAG,oBAAoB,UACtB;EACD,YACC;EACD,UACC;EACD,SACC;EACD,SACC;AACJ,EACF,EACF,CACF;;;;;AAMA,MAAa,kBAAsB,UAAkC;CACnE,MAAM,EACJ,UAAU,gBACV,mBACA,aACA,WACA,kBACE;CAEJ,MAAM,EACJ,eACA,gBACA,cACA,aACA,cACA,yBACA,iBACA,aACE,kBACF;EACE;EACA,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,UAAU,MAAM;CAClB,GACA,YACF;CAEA,OACE,oBAAC,OAAD;EACE,WAAW,sBAAsB;GAC/B;GACA;GACA;EACF,CAAC;EACD,MAAK;EACL,iBAAe,WAAW,SAAS;YAEnC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,QAAQ,KAAK,QAAQ,UAAU;IAC9B,MAAM,EAAE,SAAS,OAAO,GAAG,gBAAgB;IAE3C,MAAM,aACJ,OAAO,UAAU,YAAY,OAAO,UAAU;IAEhD,MAAM,aAAa,UAAU;IAC7B,MAAM,mBAAmB,UAAU;IAEnC,OACE,8BAAC,UAAD;KACE,GAAI;KACJ,WAAW,GACT,cAAc,EACZ,KACF,CAAC,GACD,YAAY,sBACZ,aACF;KACA,KAAK,aAAa,QAAQ;KAC1B,MAAK;KACL,eAAe,aAAa,KAAK;KACjC,iBAAe,aAAa,SAAS;KACrC,kBAAgB,mBAAmB,SAAS;KAClC;KACV,UAAU,aAAa,IAAI;KAC3B,MAAM,OAAO;MACX,YAAY,QAAQ,SAAS;KAC/B;KACA,oBAAoB,CAAC,YAAY,gBAAgB,KAAK;KACtD,oBAAoB,CAAC,YAAY,gBAAgB,IAAI;IAG/C,GADL,OACK;GAEZ,CAAC,GAEA,2BACC,oBAAC,OAAD;IACE,WAAW,GACT,iBAAiB,EACf,MACF,CAAC,CACH;IACA,OAAO;IACP,KAAK;GACN,EAEA;;CACF;AAET"}
@@ -13,7 +13,7 @@ const verticalSwitchSelectorVariant = cva("flex h-fit w-fit cursor-pointer flex-
13
13
  color: {
14
14
  [`${"primary"}`]: "border-primary text-primary",
15
15
  [`${"secondary"}`]: "border-secondary text-secondary",
16
- [`${"destructive"}`]: "border-destructive bg-destructive text-destructive",
16
+ [`${SwitchSelectorColor.ERROR}`]: "border-error bg-error text-error",
17
17
  [`${"neutral"}`]: "border-neutral text-neutral",
18
18
  [`${"light"}`]: "border-white text-white",
19
19
  [`${"dark"}`]: "border-neutral-800 text-neutral-800",
@@ -40,7 +40,7 @@ const verticalChoiceVariant = cva("z-1 w-full cursor-pointer font-medium text-sm
40
40
  const verticalIndicatorVariant = cva("absolute left-0 z-0 h-auto w-full rounded-xl transition-all duration-300 ease-in-out motion-reduce:transition-none", { variants: { color: {
41
41
  [`${"primary"}`]: "bg-primary data-[indicator=true]:text-text",
42
42
  [`${"secondary"}`]: "bg-secondary data-[indicator=true]:text-text",
43
- [`${"destructive"}`]: "bg-destructive data-[indicator=true]:text-text",
43
+ [`${SwitchSelectorColor.ERROR}`]: "bg-error data-[indicator=true]:text-text",
44
44
  [`${"neutral"}`]: "bg-neutral data-[indicator=true]:text-white",
45
45
  [`${"light"}`]: "bg-white data-[indicator=true]:text-black",
46
46
  [`${"dark"}`]: "bg-neutral-800 data-[indicator=true]:text-white",
@@ -51,7 +51,7 @@ const verticalIndicatorVariant = cva("absolute left-0 z-0 h-auto w-full rounded-
51
51
  * This component is vertical.
52
52
  */
53
53
  const VerticalSwitchSelector = (props) => {
54
- const { choices = defaultChoices, color = "primary", size = "md", className, itemClassName, ...baseProps } = props;
54
+ const { choices = defaultChoices, color = "primary", size = "md", className, itemClassName } = props;
55
55
  const { selectedIndex, indicatorIndex, handleChange, optionsRefs, indicatorRef, choiceIndicatorPosition, setHoveredIndex, disabled } = useSwitchSelector({
56
56
  choices,
57
57
  value: props.value,
@@ -1 +1 @@
1
- {"version":3,"file":"VerticalSwitchSelector.mjs","names":[],"sources":["../../../../src/components/SwitchSelector/VerticalSwitchSelector.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n defaultChoices,\n type SwitchSelectorBaseProps,\n type SwitchSelectorChoices,\n SwitchSelectorColor,\n SwitchSelectorSize,\n} from './SwitchSelector';\nimport { useSwitchSelector } from './useSwitchSelector';\n\nconst verticalSwitchSelectorVariant = cva(\n 'flex h-fit w-fit cursor-pointer flex-col gap-2 rounded-2xl border-[1.3px] p-1',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]: 'border-primary text-primary',\n [`${SwitchSelectorColor.SECONDARY}`]: 'border-secondary text-secondary',\n [`${SwitchSelectorColor.DESTRUCTIVE}`]:\n 'border-destructive bg-destructive text-destructive',\n [`${SwitchSelectorColor.NEUTRAL}`]: 'border-neutral text-neutral',\n [`${SwitchSelectorColor.LIGHT}`]: 'border-white text-white',\n [`${SwitchSelectorColor.DARK}`]: 'border-neutral-800 text-neutral-800',\n [`${SwitchSelectorColor.TEXT}`]: 'border-text text-text',\n },\n disabled: {\n true: 'cursor-not-allowed opacity-50',\n false: '',\n },\n },\n defaultVariants: {\n color: `${SwitchSelectorColor.PRIMARY}`,\n disabled: false,\n },\n }\n);\n\nconst verticalChoiceVariant = cva(\n 'z-1 w-full cursor-pointer font-medium text-sm transition-all duration-300 ease-in-out aria-selected:cursor-default data-[indicator=true]:text-text-opposite motion-reduce:transition-none',\n {\n variants: {\n size: {\n [`${SwitchSelectorSize.SM}`]: 'px-2 py-1 text-xs',\n [`${SwitchSelectorSize.MD}`]: 'p-2 text-sm',\n [`${SwitchSelectorSize.LG}`]: 'p-4 text-base',\n },\n },\n defaultVariants: {\n size: `${SwitchSelectorSize.MD}`,\n },\n }\n);\n\nconst verticalIndicatorVariant = cva(\n 'absolute left-0 z-0 h-auto w-full rounded-xl transition-all duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]:\n 'bg-primary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.SECONDARY}`]:\n 'bg-secondary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.DESTRUCTIVE}`]:\n 'bg-destructive data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.NEUTRAL}`]:\n 'bg-neutral data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.LIGHT}`]:\n 'bg-white data-[indicator=true]:text-black',\n [`${SwitchSelectorColor.DARK}`]:\n 'bg-neutral-800 data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.TEXT}`]:\n 'bg-text data-[indicator=true]:text-text-opposite',\n },\n },\n }\n);\n\nexport type VerticalSwitchSelectorProps<T = boolean> =\n SwitchSelectorBaseProps<T> &\n VariantProps<typeof verticalSwitchSelectorVariant> &\n VariantProps<typeof verticalChoiceVariant>;\n\n/**\n * Component that allows the user to select one of the provided choices.\n * This component is vertical.\n */\nexport const VerticalSwitchSelector = <T,>(\n props: VerticalSwitchSelectorProps<T>\n) => {\n const {\n choices = defaultChoices as SwitchSelectorChoices<T>,\n color = SwitchSelectorColor.PRIMARY,\n size = SwitchSelectorSize.MD,\n className,\n itemClassName,\n ...baseProps\n } = props;\n\n const {\n selectedIndex,\n indicatorIndex,\n handleChange,\n optionsRefs,\n indicatorRef,\n choiceIndicatorPosition,\n setHoveredIndex,\n disabled,\n } = useSwitchSelector(\n {\n choices,\n value: props.value,\n defaultValue: props.defaultValue,\n onChange: props.onChange,\n hoverable: props.hoverable,\n disabled: props.disabled,\n },\n 'vertical'\n );\n\n return (\n <div\n className={verticalSwitchSelectorVariant({\n color,\n disabled,\n className,\n })}\n role=\"tablist\"\n aria-disabled={disabled ? 'true' : undefined}\n >\n <div className=\"relative flex h-fit w-full flex-col items-center justify-center\">\n {choices.map((choice, index) => {\n const { content, value, ...buttonProps } = choice;\n\n const isKeyOfKey =\n typeof value === 'string' || typeof value === 'number';\n\n const isSelected = index === selectedIndex;\n const isIndicatorOwner = index === indicatorIndex;\n\n return (\n <button\n {...buttonProps}\n className={cn(\n verticalChoiceVariant({\n size,\n }),\n disabled && 'cursor-not-allowed',\n itemClassName\n )}\n key={isKeyOfKey ? value : index}\n role=\"tab\"\n onClick={() => handleChange(value)}\n aria-selected={isSelected ? 'true' : undefined}\n data-indicator={isIndicatorOwner ? 'true' : undefined}\n disabled={disabled || isSelected}\n tabIndex={isSelected ? 0 : -1}\n ref={(el) => {\n optionsRefs.current[index] = el!;\n }}\n onMouseEnter={() => !disabled && setHoveredIndex(index)}\n onMouseLeave={() => !disabled && setHoveredIndex(null)}\n >\n {content}\n </button>\n );\n })}\n\n {choiceIndicatorPosition && (\n <div\n className={cn(\n verticalIndicatorVariant({\n color,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n />\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AAaA,MAAM,gCAAgC,IACpC,iFACA;CACE,UAAU;EACR,OAAO;IACJ,iBAAmC;IACnC,mBAAqC;IACrC,qBACC;IACD,iBAAmC;IACnC,eAAiC;IACjC,cAAgC;IAChC,cAAgC;EACnC;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,OAAO;EACP,UAAU;CACZ;AACF,CACF;AAEA,MAAM,wBAAwB,IAC5B,6LACA;CACE,UAAU,EACR,MAAM;GACH,YAA6B;GAC7B,YAA6B;GAC7B,YAA6B;CAChC,EACF;CACA,iBAAiB,EACf,MAAM,UACR;AACF,CACF;AAEA,MAAM,2BAA2B,IAC/B,sHACA,EACE,UAAU,EACR,OAAO;EACJ,iBACC;EACD,mBACC;EACD,qBACC;EACD,iBACC;EACD,eACC;EACD,cACC;EACD,cACC;AACJ,EACF,EACF,CACF;;;;;AAWA,MAAa,0BACX,UACG;CACH,MAAM,EACJ,UAAU,gBACV,mBACA,aACA,WACA,eACA,GAAG,cACD;CAEJ,MAAM,EACJ,eACA,gBACA,cACA,aACA,cACA,yBACA,iBACA,aACE,kBACF;EACE;EACA,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,UAAU,MAAM;CAClB,GACA,UACF;CAEA,OACE,oBAAC,OAAD;EACE,WAAW,8BAA8B;GACvC;GACA;GACA;EACF,CAAC;EACD,MAAK;EACL,iBAAe,WAAW,SAAS;YAEnC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,QAAQ,KAAK,QAAQ,UAAU;IAC9B,MAAM,EAAE,SAAS,OAAO,GAAG,gBAAgB;IAE3C,MAAM,aACJ,OAAO,UAAU,YAAY,OAAO,UAAU;IAEhD,MAAM,aAAa,UAAU;IAC7B,MAAM,mBAAmB,UAAU;IAEnC,OACE,8BAAC,UAAD;KACE,GAAI;KACJ,WAAW,GACT,sBAAsB,EACpB,KACF,CAAC,GACD,YAAY,sBACZ,aACF;KACA,KAAK,aAAa,QAAQ;KAC1B,MAAK;KACL,eAAe,aAAa,KAAK;KACjC,iBAAe,aAAa,SAAS;KACrC,kBAAgB,mBAAmB,SAAS;KAC5C,UAAU,YAAY;KACtB,UAAU,aAAa,IAAI;KAC3B,MAAM,OAAO;MACX,YAAY,QAAQ,SAAS;KAC/B;KACA,oBAAoB,CAAC,YAAY,gBAAgB,KAAK;KACtD,oBAAoB,CAAC,YAAY,gBAAgB,IAAI;IAG/C,GADL,OACK;GAEZ,CAAC,GAEA,2BACC,oBAAC,OAAD;IACE,WAAW,GACT,yBAAyB,EACvB,MACF,CAAC,CACH;IACA,OAAO;IACP,KAAK;GACN,EAEA;;CACF;AAET"}
1
+ {"version":3,"file":"VerticalSwitchSelector.mjs","names":[],"sources":["../../../../src/components/SwitchSelector/VerticalSwitchSelector.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n defaultChoices,\n type SwitchSelectorBaseProps,\n type SwitchSelectorChoices,\n SwitchSelectorColor,\n SwitchSelectorSize,\n} from './SwitchSelector';\nimport { useSwitchSelector } from './useSwitchSelector';\n\nconst verticalSwitchSelectorVariant = cva(\n 'flex h-fit w-fit cursor-pointer flex-col gap-2 rounded-2xl border-[1.3px] p-1',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]: 'border-primary text-primary',\n [`${SwitchSelectorColor.SECONDARY}`]: 'border-secondary text-secondary',\n [`${SwitchSelectorColor.ERROR}`]: 'border-error bg-error text-error',\n [`${SwitchSelectorColor.NEUTRAL}`]: 'border-neutral text-neutral',\n [`${SwitchSelectorColor.LIGHT}`]: 'border-white text-white',\n [`${SwitchSelectorColor.DARK}`]: 'border-neutral-800 text-neutral-800',\n [`${SwitchSelectorColor.TEXT}`]: 'border-text text-text',\n },\n disabled: {\n true: 'cursor-not-allowed opacity-50',\n false: '',\n },\n },\n defaultVariants: {\n color: `${SwitchSelectorColor.PRIMARY}`,\n disabled: false,\n },\n }\n);\n\nconst verticalChoiceVariant = cva(\n 'z-1 w-full cursor-pointer font-medium text-sm transition-all duration-300 ease-in-out aria-selected:cursor-default data-[indicator=true]:text-text-opposite motion-reduce:transition-none',\n {\n variants: {\n size: {\n [`${SwitchSelectorSize.SM}`]: 'px-2 py-1 text-xs',\n [`${SwitchSelectorSize.MD}`]: 'p-2 text-sm',\n [`${SwitchSelectorSize.LG}`]: 'p-4 text-base',\n },\n },\n defaultVariants: {\n size: `${SwitchSelectorSize.MD}`,\n },\n }\n);\n\nconst verticalIndicatorVariant = cva(\n 'absolute left-0 z-0 h-auto w-full rounded-xl transition-all duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n [`${SwitchSelectorColor.PRIMARY}`]:\n 'bg-primary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.SECONDARY}`]:\n 'bg-secondary data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.ERROR}`]:\n 'bg-error data-[indicator=true]:text-text',\n [`${SwitchSelectorColor.NEUTRAL}`]:\n 'bg-neutral data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.LIGHT}`]:\n 'bg-white data-[indicator=true]:text-black',\n [`${SwitchSelectorColor.DARK}`]:\n 'bg-neutral-800 data-[indicator=true]:text-white',\n [`${SwitchSelectorColor.TEXT}`]:\n 'bg-text data-[indicator=true]:text-text-opposite',\n },\n },\n }\n);\n\nexport type VerticalSwitchSelectorProps<T = boolean> =\n SwitchSelectorBaseProps<T> &\n VariantProps<typeof verticalSwitchSelectorVariant> &\n VariantProps<typeof verticalChoiceVariant>;\n\n/**\n * Component that allows the user to select one of the provided choices.\n * This component is vertical.\n */\nexport const VerticalSwitchSelector = <T,>(\n props: VerticalSwitchSelectorProps<T>\n) => {\n const {\n choices = defaultChoices as SwitchSelectorChoices<T>,\n color = SwitchSelectorColor.PRIMARY,\n size = SwitchSelectorSize.MD,\n className,\n itemClassName,\n } = props;\n\n const {\n selectedIndex,\n indicatorIndex,\n handleChange,\n optionsRefs,\n indicatorRef,\n choiceIndicatorPosition,\n setHoveredIndex,\n disabled,\n } = useSwitchSelector(\n {\n choices,\n value: props.value,\n defaultValue: props.defaultValue,\n onChange: props.onChange,\n hoverable: props.hoverable,\n disabled: props.disabled,\n },\n 'vertical'\n );\n\n return (\n <div\n className={verticalSwitchSelectorVariant({\n color,\n disabled,\n className,\n })}\n role=\"tablist\"\n aria-disabled={disabled ? 'true' : undefined}\n >\n <div className=\"relative flex h-fit w-full flex-col items-center justify-center\">\n {choices.map((choice, index) => {\n const { content, value, ...buttonProps } = choice;\n\n const isKeyOfKey =\n typeof value === 'string' || typeof value === 'number';\n\n const isSelected = index === selectedIndex;\n const isIndicatorOwner = index === indicatorIndex;\n\n return (\n <button\n {...buttonProps}\n className={cn(\n verticalChoiceVariant({\n size,\n }),\n disabled && 'cursor-not-allowed',\n itemClassName\n )}\n key={isKeyOfKey ? value : index}\n role=\"tab\"\n onClick={() => handleChange(value)}\n aria-selected={isSelected ? 'true' : undefined}\n data-indicator={isIndicatorOwner ? 'true' : undefined}\n disabled={disabled || isSelected}\n tabIndex={isSelected ? 0 : -1}\n ref={(el) => {\n optionsRefs.current[index] = el!;\n }}\n onMouseEnter={() => !disabled && setHoveredIndex(index)}\n onMouseLeave={() => !disabled && setHoveredIndex(null)}\n >\n {content}\n </button>\n );\n })}\n\n {choiceIndicatorPosition && (\n <div\n className={cn(\n verticalIndicatorVariant({\n color,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n />\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AAaA,MAAM,gCAAgC,IACpC,iFACA;CACE,UAAU;EACR,OAAO;IACJ,iBAAmC;IACnC,mBAAqC;IACrC,GAAG,oBAAoB,UAAU;IACjC,iBAAmC;IACnC,eAAiC;IACjC,cAAgC;IAChC,cAAgC;EACnC;EACA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,OAAO;EACP,UAAU;CACZ;AACF,CACF;AAEA,MAAM,wBAAwB,IAC5B,6LACA;CACE,UAAU,EACR,MAAM;GACH,YAA6B;GAC7B,YAA6B;GAC7B,YAA6B;CAChC,EACF;CACA,iBAAiB,EACf,MAAM,UACR;AACF,CACF;AAEA,MAAM,2BAA2B,IAC/B,sHACA,EACE,UAAU,EACR,OAAO;EACJ,iBACC;EACD,mBACC;EACD,GAAG,oBAAoB,UACtB;EACD,iBACC;EACD,eACC;EACD,cACC;EACD,cACC;AACJ,EACF,EACF,CACF;;;;;AAWA,MAAa,0BACX,UACG;CACH,MAAM,EACJ,UAAU,gBACV,mBACA,aACA,WACA,kBACE;CAEJ,MAAM,EACJ,eACA,gBACA,cACA,aACA,cACA,yBACA,iBACA,aACE,kBACF;EACE;EACA,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,UAAU,MAAM;CAClB,GACA,UACF;CAEA,OACE,oBAAC,OAAD;EACE,WAAW,8BAA8B;GACvC;GACA;GACA;EACF,CAAC;EACD,MAAK;EACL,iBAAe,WAAW,SAAS;YAEnC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,QAAQ,KAAK,QAAQ,UAAU;IAC9B,MAAM,EAAE,SAAS,OAAO,GAAG,gBAAgB;IAE3C,MAAM,aACJ,OAAO,UAAU,YAAY,OAAO,UAAU;IAEhD,MAAM,aAAa,UAAU;IAC7B,MAAM,mBAAmB,UAAU;IAEnC,OACE,8BAAC,UAAD;KACE,GAAI;KACJ,WAAW,GACT,sBAAsB,EACpB,KACF,CAAC,GACD,YAAY,sBACZ,aACF;KACA,KAAK,aAAa,QAAQ;KAC1B,MAAK;KACL,eAAe,aAAa,KAAK;KACjC,iBAAe,aAAa,SAAS;KACrC,kBAAgB,mBAAmB,SAAS;KAC5C,UAAU,YAAY;KACtB,UAAU,aAAa,IAAI;KAC3B,MAAM,OAAO;MACX,YAAY,QAAQ,SAAS;KAC/B;KACA,oBAAoB,CAAC,YAAY,gBAAgB,KAAK;KACtD,oBAAoB,CAAC,YAAY,gBAAgB,IAAI;IAG/C,GADL,OACK;GAEZ,CAAC,GAEA,2BACC,oBAAC,OAAD;IACE,WAAW,GACT,yBAAyB,EACvB,MACF,CAAC,CACH;IACA,OAAO;IACP,KAAK;GACN,EAEA;;CACF;AAET"}
@@ -10,7 +10,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
10
10
  let TabSelectorColor = /* @__PURE__ */ function(TabSelectorColor) {
11
11
  TabSelectorColor["PRIMARY"] = "primary";
12
12
  TabSelectorColor["SECONDARY"] = "secondary";
13
- TabSelectorColor["DESTRUCTIVE"] = "destructive";
13
+ TabSelectorColor["ERROR"] = "error";
14
14
  TabSelectorColor["NEUTRAL"] = "neutral";
15
15
  TabSelectorColor["LIGHT"] = "light";
16
16
  TabSelectorColor["DARK"] = "dark";
@@ -21,7 +21,7 @@ const tabSelectorVariant = cva("relative z-0 flex size-full flex-row items-cente
21
21
  variants: { color: {
22
22
  primary: "border-primary text-primary",
23
23
  secondary: "border-secondary text-secondary",
24
- destructive: "border-destructive bg-destructive text-destructive",
24
+ error: "border-error bg-error text-error",
25
25
  neutral: "border-neutral text-neutral",
26
26
  light: "border-white text-white",
27
27
  dark: "border-neutral-800 text-neutral-800",
@@ -34,7 +34,7 @@ const indicatorVariant = cva("absolute -z-1 rounded-lg duration-300 ease-in-out
34
34
  color: {
35
35
  primary: "bg-primary/10 aria-selected:text-text",
36
36
  secondary: "bg-secondary/10 aria-selected:text-text",
37
- destructive: "bg-destructive/10 aria-selected:text-text",
37
+ error: "bg-error/10 aria-selected:text-text",
38
38
  neutral: "bg-neutral/10 aria-selected:text-white/10",
39
39
  light: "bg-white/10 aria-selected:text-black",
40
40
  dark: "bg-neutral-800/10 aria-selected:text-white",
@@ -1 +1 @@
1
- {"version":3,"file":"TabSelector.mjs","names":[],"sources":["../../../../src/components/TabSelector/TabSelector.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ItemSelectorOrientation,\n useItemSelector,\n} from '@hooks/useItemSelector';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n cloneElement,\n type HTMLAttributes,\n type MouseEvent,\n type ReactElement,\n useEffect,\n useRef,\n} from 'react';\n\nexport enum TabSelectorColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n DESTRUCTIVE = 'destructive',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n}\n\nconst tabSelectorVariant = cva(\n 'relative z-0 flex size-full flex-row items-center gap-2',\n {\n variants: {\n color: {\n primary: 'border-primary text-primary',\n secondary: 'border-secondary text-secondary',\n destructive: 'border-destructive bg-destructive text-destructive',\n neutral: 'border-neutral text-neutral',\n light: 'border-white text-white',\n dark: 'border-neutral-800 text-neutral-800',\n text: 'border-text text-text',\n },\n },\n defaultVariants: {\n color: 'primary',\n },\n }\n);\n\nconst indicatorVariant = cva(\n 'absolute -z-1 rounded-lg duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n primary: 'bg-primary/10 aria-selected:text-text',\n secondary: 'bg-secondary/10 aria-selected:text-text',\n destructive: 'bg-destructive/10 aria-selected:text-text',\n neutral: 'bg-neutral/10 aria-selected:text-white/10',\n light: 'bg-white/10 aria-selected:text-black',\n dark: 'bg-neutral-800/10 aria-selected:text-white',\n text: 'bg-text/10 aria-selected:text-text-opposite',\n },\n orientation: {\n horizontal: 'top-0 h-full w-auto transition-[left,width]',\n vertical: 'left-0 h-auto w-full transition-[top,height]',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n }\n);\n\nexport type TabSelectorItemProps = HTMLAttributes<HTMLElement> & {\n key: string | number;\n};\n\nexport type TabSelectorProps<T extends TabSelectorItemProps> = {\n tabs: ReactElement<T>[];\n selectedChoice: T['key'];\n onTabClick?: (choice: T['key']) => void;\n hoverable?: boolean;\n orientation?: ItemSelectorOrientation;\n} & HTMLAttributes<HTMLElement> &\n Omit<VariantProps<typeof tabSelectorVariant>, 'color'> & {\n color?: TabSelectorColor | `${TabSelectorColor}`;\n };\n\n/**\n *\n * Component that allows the user to select one of the provided choices.\n *\n * Example:\n * ```jsx\n * <TabSelector\n * selectedChoice=\"option1\"\n * onTabClick={(choice) => console.log(choice)}\n * hoverable={true}\n * >\n * <Button key=\"option1\"/>\n * <Button key=\"option2\"/>\n * <Button key=\"option3\"/>\n * </TabSelector>\n * ```\n */\nexport const TabSelector = <T extends TabSelectorItemProps>({\n tabs,\n selectedChoice,\n onTabClick,\n color = TabSelectorColor.PRIMARY,\n hoverable = false,\n orientation = 'horizontal',\n className,\n}: TabSelectorProps<T>) => {\n const optionsRefs = useRef<HTMLElement[]>([]);\n const indicatorRef = useRef<HTMLDivElement | null>(null);\n const { choiceIndicatorPosition, calculatePosition } = useItemSelector(\n optionsRefs,\n { isHoverable: hoverable, orientation }\n );\n\n useEffect(() => {\n calculatePosition();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedChoice]);\n\n return (\n <div\n className={cn(\n tabSelectorVariant({\n color,\n }),\n className\n )}\n aria-orientation={orientation}\n aria-multiselectable=\"false\"\n role=\"tablist\"\n >\n {tabs.map((Tab, index) => {\n const key = Tab.key!;\n\n const isSelected = selectedChoice === key;\n\n return cloneElement(Tab, {\n key: key ?? index,\n role: 'tab',\n onClick: (e: MouseEvent<HTMLElement>) => {\n Tab.props?.onClick?.(e);\n onTabClick?.(key);\n },\n 'aria-selected': isSelected,\n 'data-active': isSelected as unknown as string,\n tabIndex: isSelected ? 0 : -1,\n ref: (el: HTMLElement) => {\n optionsRefs.current[index] = el!;\n },\n } as unknown as T);\n })}\n {choiceIndicatorPosition && (\n <div\n className={cn(\n indicatorVariant({\n color,\n orientation,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n key={`${selectedChoice}${JSON.stringify(tabs.map((tab) => tab.key))}`}\n />\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;AAiBA,IAAY,mBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,MAAM,qBAAqB,IACzB,2DACA;CACE,UAAU,EACR,OAAO;EACL,SAAS;EACT,WAAW;EACX,aAAa;EACb,SAAS;EACT,OAAO;EACP,MAAM;EACN,MAAM;CACR,EACF;CACA,iBAAiB,EACf,OAAO,UACT;AACF,CACF;AAEA,MAAM,mBAAmB,IACvB,mFACA;CACE,UAAU;EACR,OAAO;GACL,SAAS;GACT,WAAW;GACX,aAAa;GACb,SAAS;GACT,OAAO;GACP,MAAM;GACN,MAAM;EACR;EACA,aAAa;GACX,YAAY;GACZ,UAAU;EACZ;CACF;CACA,iBAAiB,EACf,aAAa,aACf;AACF,CACF;;;;;;;;;;;;;;;;;;AAkCA,MAAa,eAA+C,EAC1D,MACA,gBACA,YACA,mBACA,YAAY,OACZ,cAAc,cACd,gBACyB;CACzB,MAAM,cAAc,OAAsB,CAAC,CAAC;CAC5C,MAAM,eAAe,OAA8B,IAAI;CACvD,MAAM,EAAE,yBAAyB,sBAAsB,gBACrD,aACA;EAAE,aAAa;EAAW;CAAY,CACxC;CAEA,gBAAgB;EACd,kBAAkB;CAEpB,GAAG,CAAC,cAAc,CAAC;CAEnB,OACE,qBAAC,OAAD;EACE,WAAW,GACT,mBAAmB,EACjB,MACF,CAAC,GACD,SACF;EACA,oBAAkB;EAClB,wBAAqB;EACrB,MAAK;YATP,CAWG,KAAK,KAAK,KAAK,UAAU;GACxB,MAAM,MAAM,IAAI;GAEhB,MAAM,aAAa,mBAAmB;GAEtC,OAAO,aAAa,KAAK;IACvB,KAAK,OAAO;IACZ,MAAM;IACN,UAAU,MAA+B;KACvC,IAAI,OAAO,UAAU,CAAC;KACtB,aAAa,GAAG;IAClB;IACA,iBAAiB;IACjB,eAAe;IACf,UAAU,aAAa,IAAI;IAC3B,MAAM,OAAoB;KACxB,YAAY,QAAQ,SAAS;IAC/B;GACF,CAAiB;EACnB,CAAC,GACA,2BACC,oBAAC,OAAD;GACE,WAAW,GACT,iBAAiB;IACf;IACA;GACF,CAAC,CACH;GACA,OAAO;GACP,KAAK;EAEN,GADM,GAAG,iBAAiB,KAAK,UAAU,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,GACnE,CAEA;;AAET"}
1
+ {"version":3,"file":"TabSelector.mjs","names":[],"sources":["../../../../src/components/TabSelector/TabSelector.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ItemSelectorOrientation,\n useItemSelector,\n} from '@hooks/useItemSelector';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n cloneElement,\n type HTMLAttributes,\n type MouseEvent,\n type ReactElement,\n useEffect,\n useRef,\n} from 'react';\n\nexport enum TabSelectorColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n ERROR = 'error',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n}\n\nconst tabSelectorVariant = cva(\n 'relative z-0 flex size-full flex-row items-center gap-2',\n {\n variants: {\n color: {\n primary: 'border-primary text-primary',\n secondary: 'border-secondary text-secondary',\n error: 'border-error bg-error text-error',\n neutral: 'border-neutral text-neutral',\n light: 'border-white text-white',\n dark: 'border-neutral-800 text-neutral-800',\n text: 'border-text text-text',\n },\n },\n defaultVariants: {\n color: 'primary',\n },\n }\n);\n\nconst indicatorVariant = cva(\n 'absolute -z-1 rounded-lg duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n primary: 'bg-primary/10 aria-selected:text-text',\n secondary: 'bg-secondary/10 aria-selected:text-text',\n error: 'bg-error/10 aria-selected:text-text',\n neutral: 'bg-neutral/10 aria-selected:text-white/10',\n light: 'bg-white/10 aria-selected:text-black',\n dark: 'bg-neutral-800/10 aria-selected:text-white',\n text: 'bg-text/10 aria-selected:text-text-opposite',\n },\n orientation: {\n horizontal: 'top-0 h-full w-auto transition-[left,width]',\n vertical: 'left-0 h-auto w-full transition-[top,height]',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n }\n);\n\nexport type TabSelectorItemProps = HTMLAttributes<HTMLElement> & {\n key: string | number;\n};\n\nexport type TabSelectorProps<T extends TabSelectorItemProps> = {\n tabs: ReactElement<T>[];\n selectedChoice: T['key'];\n onTabClick?: (choice: T['key']) => void;\n hoverable?: boolean;\n orientation?: ItemSelectorOrientation;\n} & HTMLAttributes<HTMLElement> &\n Omit<VariantProps<typeof tabSelectorVariant>, 'color'> & {\n color?: TabSelectorColor | `${TabSelectorColor}`;\n };\n\n/**\n *\n * Component that allows the user to select one of the provided choices.\n *\n * Example:\n * ```jsx\n * <TabSelector\n * selectedChoice=\"option1\"\n * onTabClick={(choice) => console.log(choice)}\n * hoverable={true}\n * >\n * <Button key=\"option1\"/>\n * <Button key=\"option2\"/>\n * <Button key=\"option3\"/>\n * </TabSelector>\n * ```\n */\nexport const TabSelector = <T extends TabSelectorItemProps>({\n tabs,\n selectedChoice,\n onTabClick,\n color = TabSelectorColor.PRIMARY,\n hoverable = false,\n orientation = 'horizontal',\n className,\n}: TabSelectorProps<T>) => {\n const optionsRefs = useRef<HTMLElement[]>([]);\n const indicatorRef = useRef<HTMLDivElement | null>(null);\n const { choiceIndicatorPosition, calculatePosition } = useItemSelector(\n optionsRefs,\n { isHoverable: hoverable, orientation }\n );\n\n useEffect(() => {\n calculatePosition();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedChoice]);\n\n return (\n <div\n className={cn(\n tabSelectorVariant({\n color,\n }),\n className\n )}\n aria-orientation={orientation}\n aria-multiselectable=\"false\"\n role=\"tablist\"\n >\n {tabs.map((Tab, index) => {\n const key = Tab.key!;\n\n const isSelected = selectedChoice === key;\n\n return cloneElement(Tab, {\n key: key ?? index,\n role: 'tab',\n onClick: (e: MouseEvent<HTMLElement>) => {\n Tab.props?.onClick?.(e);\n onTabClick?.(key);\n },\n 'aria-selected': isSelected,\n 'data-active': isSelected as unknown as string,\n tabIndex: isSelected ? 0 : -1,\n ref: (el: HTMLElement) => {\n optionsRefs.current[index] = el!;\n },\n } as unknown as T);\n })}\n {choiceIndicatorPosition && (\n <div\n className={cn(\n indicatorVariant({\n color,\n orientation,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n key={`${selectedChoice}${JSON.stringify(tabs.map((tab) => tab.key))}`}\n />\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;AAiBA,IAAY,mBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,MAAM,qBAAqB,IACzB,2DACA;CACE,UAAU,EACR,OAAO;EACL,SAAS;EACT,WAAW;EACX,OAAO;EACP,SAAS;EACT,OAAO;EACP,MAAM;EACN,MAAM;CACR,EACF;CACA,iBAAiB,EACf,OAAO,UACT;AACF,CACF;AAEA,MAAM,mBAAmB,IACvB,mFACA;CACE,UAAU;EACR,OAAO;GACL,SAAS;GACT,WAAW;GACX,OAAO;GACP,SAAS;GACT,OAAO;GACP,MAAM;GACN,MAAM;EACR;EACA,aAAa;GACX,YAAY;GACZ,UAAU;EACZ;CACF;CACA,iBAAiB,EACf,aAAa,aACf;AACF,CACF;;;;;;;;;;;;;;;;;;AAkCA,MAAa,eAA+C,EAC1D,MACA,gBACA,YACA,mBACA,YAAY,OACZ,cAAc,cACd,gBACyB;CACzB,MAAM,cAAc,OAAsB,CAAC,CAAC;CAC5C,MAAM,eAAe,OAA8B,IAAI;CACvD,MAAM,EAAE,yBAAyB,sBAAsB,gBACrD,aACA;EAAE,aAAa;EAAW;CAAY,CACxC;CAEA,gBAAgB;EACd,kBAAkB;CAEpB,GAAG,CAAC,cAAc,CAAC;CAEnB,OACE,qBAAC,OAAD;EACE,WAAW,GACT,mBAAmB,EACjB,MACF,CAAC,GACD,SACF;EACA,oBAAkB;EAClB,wBAAqB;EACrB,MAAK;YATP,CAWG,KAAK,KAAK,KAAK,UAAU;GACxB,MAAM,MAAM,IAAI;GAEhB,MAAM,aAAa,mBAAmB;GAEtC,OAAO,aAAa,KAAK;IACvB,KAAK,OAAO;IACZ,MAAM;IACN,UAAU,MAA+B;KACvC,IAAI,OAAO,UAAU,CAAC;KACtB,aAAa,GAAG;IAClB;IACA,iBAAiB;IACjB,eAAe;IACf,UAAU,aAAa,IAAI;IAC3B,MAAM,OAAoB;KACxB,YAAY,QAAQ,SAAS;IAC/B;GACF,CAAiB;EACnB,CAAC,GACA,2BACC,oBAAC,OAAD;GACE,WAAW,GACT,iBAAiB;IACf;IACA;GACF,CAAC,CACH;GACA,OAAO;GACP,KAAK;EAEN,GADM,GAAG,iBAAiB,KAAK,UAAU,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,GACnE,CAEA;;AAET"}
@@ -178,7 +178,7 @@ const containerVariants = cva("w-fit backdrop-blur", {
178
178
  [`blue`]: "border-blue-500 bg-blue-500/10 text-blue-500 dark:text-blue-300",
179
179
  [`yellow`]: "border-yellow-500 bg-yellow-500/10 text-yellow-500 dark:text-yellow-300",
180
180
  [`green`]: "border-green-500 bg-green-500/10 text-green-500 dark:text-green-300",
181
- [`red`]: "border-red-500 bg-red-500/10 text-red-500 dark:text-red-300",
181
+ [`red`]: "border-error bg-error/10 text-error dark:text-red-300",
182
182
  [`orange`]: "border-orange-500 bg-orange-500/10 text-orange-500 dark:text-orange-300",
183
183
  [`purple`]: "border-purple-500 bg-purple-500/10 text-purple-500 dark:text-purple-300",
184
184
  [`pink`]: "border-pink-500 bg-pink-500/10 text-pink-500 dark:text-pink-300",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Tag/index.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport type { FC, HTMLAttributes, PropsWithChildren } from 'react';\n\n/**\n * Properties for the Tag component extending HTML div attributes and variant options\n *\n * @interface TagProps\n * @extends {PropsWithChildren<VariantProps<typeof containerVariants>>}\n * @extends {HTMLAttributes<HTMLDivElement>}\n *\n * @property {ReactNode} children - The content to display inside the tag\n * @property {TagColor} [color] - Color theme variant of the tag\n * @property {TagRoundedSize} [roundedSize] - Border radius size of the tag\n * @property {TagSize} [size] - Size variant affecting padding and font size\n * @property {TagBorder} [border] - Whether to show a border around the tag\n * @property {TagBackground} [background] - Background visibility option\n * @property [className] - Additional CSS classes for custom styling\n *\n * @example\n * ```tsx\n * // Basic tag\n * <Tag>Default Tag</Tag>\n *\n * // Success tag with border\n * <Tag color={TagColor.SUCCESS} border={TagBorder.WITH}>\n * Success Status\n * </Tag>\n *\n * // Large warning tag\n * <Tag color={TagColor.WARNING} size={TagSize.LG}>\n * Important Warning\n * </Tag>\n * ```\n */\ntype TagProps = PropsWithChildren<VariantProps<typeof containerVariants>> &\n HTMLAttributes<HTMLDivElement>;\n\n/**\n * Enumeration for tag border radius sizes\n *\n * Controls the roundedness of tag corners, from sharp edges to fully rounded pills.\n *\n * @enum TagRoundedSize\n * @property NONE - 'none' - No border radius (sharp corners)\n * @property SM - 'sm' - Small border radius (2px)\n * @property MD - 'md' - Medium border radius (6px)\n * @property LG - 'lg' - Large border radius (8px)\n * @property XL - 'xl' - Extra large border radius (12px)\n * @property XXL - '2xl' - 2x large border radius (16px)\n * @property XXXL - '3xl' - 3x large border radius (24px)\n * @property FULL - 'full' - Fully rounded (50% border radius, pill shape)\n *\n * @example\n * ```tsx\n * // Sharp corners\n * <Tag roundedSize={TagRoundedSize.NONE}>Sharp Tag</Tag>\n *\n * // Pill-shaped tag\n * <Tag roundedSize={TagRoundedSize.FULL}>Pill Tag</Tag>\n *\n * // Medium rounded corners\n * <Tag roundedSize={TagRoundedSize.MD}>Rounded Tag</Tag>\n * ```\n */\nexport enum TagRoundedSize {\n NONE = 'none',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n XXL = '2xl',\n XXXL = '3xl',\n FULL = 'full',\n}\n\n/**\n * Enumeration for tag color themes\n *\n * Provides semantic color options for different tag purposes and meanings.\n * Each color includes background, border, and text color variations.\n *\n * @enum TagColor\n * @property SUCCESS - 'success' - Green theme for positive states, success messages, or completed items\n * @property ERROR - 'error' - Red theme for error states, warnings, or failed operations\n * @property WARNING - 'warning' - Yellow/orange theme for caution, pending states, or important notices\n * @property NEUTRAL - 'neutral' - Gray theme for neutral information or secondary content\n * @property TEXT - 'text' - Default text color theme for general purpose tags\n *\n * @example\n * ```tsx\n * // Status indicators\n * <Tag color={TagColor.SUCCESS}>Completed</Tag>\n * <Tag color={TagColor.ERROR}>Failed</Tag>\n * <Tag color={TagColor.WARNING}>Pending</Tag>\n *\n * // Category tags\n * <Tag color={TagColor.NEUTRAL}>Category</Tag>\n * <Tag color={TagColor.TEXT}>General</Tag>\n * ```\n */\nexport enum TagColor {\n PRIMARY = 'primary',\n SUCCESS = 'success',\n ERROR = 'error',\n WARNING = 'warning',\n NEUTRAL = 'neutral',\n TEXT = 'text',\n BLUE = 'blue',\n YELLOW = 'yellow',\n GREEN = 'green',\n RED = 'red',\n ORANGE = 'orange',\n PURPLE = 'purple',\n PINK = 'pink',\n BROWN = 'brown',\n GRAY = 'gray',\n BLACK = 'black',\n WHITE = 'white',\n}\n\n/**\n * Enumeration for tag size variants\n *\n * Controls the overall size of tags including padding, font size, and border thickness.\n * Sizes are designed to maintain visual hierarchy and readability.\n *\n * @enum TagSize\n * @property XS - 'xs' - Extra small (0.5rem padding, text-xs, 1.2px border)\n * @property SM - 'sm' - Small (0.5rem padding, text-sm, 1.5px border)\n * @property MD - 'md' - Medium (1rem padding, text-base, 2px border) - Default size\n * @property LG - 'lg' - Large (2rem padding, text-lg, 2px border)\n * @property XL - 'xl' - Extra large (4rem padding, text-xl, 2px border)\n *\n * @example\n * ```tsx\n * // Different sizes for hierarchy\n * <Tag size={TagSize.XS}>Small detail</Tag>\n * <Tag size={TagSize.SM}>Minor category</Tag>\n * <Tag size={TagSize.MD}>Standard tag</Tag>\n * <Tag size={TagSize.LG}>Important label</Tag>\n * <Tag size={TagSize.XL}>Hero tag</Tag>\n * ```\n */\nexport enum TagSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n}\n\n/**\n * Enumeration for tag border visibility\n *\n * Controls whether a border is displayed around the tag.\n *\n * @enum TagBorder\n * @property NONE - 'none' - No border (default)\n * @property WITH - 'with' - Show border with 1.5px thickness\n *\n * @example\n * ```tsx\n * <Tag border={TagBorder.NONE}>Borderless</Tag>\n * <Tag border={TagBorder.WITH}>With Border</Tag>\n * ```\n */\nexport enum TagBorder {\n NONE = 'none',\n WITH = 'with',\n}\n\n/**\n * Enumeration for tag background visibility\n *\n * Controls the background styling of the tag.\n *\n * @enum TagBackground\n * @property NONE - 'none' - No background styling\n * @property WITH - 'with' - Apply background styling\n *\n * @example\n * ```tsx\n * <Tag background={TagBackground.NONE}>No Background</Tag>\n * <Tag background={TagBackground.WITH}>With Background</Tag>\n * ```\n */\nexport enum TagBackground {\n NONE = 'none',\n WITH = 'with',\n}\n\nconst containerVariants = cva('w-fit backdrop-blur', {\n variants: {\n roundedSize: {\n [`${TagRoundedSize.NONE}`]: 'rounded-none',\n [`${TagRoundedSize.SM}`]: 'rounded-sm',\n [`${TagRoundedSize.MD}`]: 'rounded-md',\n [`${TagRoundedSize.LG}`]: 'rounded-lg',\n [`${TagRoundedSize.XL}`]: 'rounded-xl',\n [`${TagRoundedSize.XXL}`]: 'rounded-2xl',\n [`${TagRoundedSize.XXXL}`]: 'rounded-3xl',\n [`${TagRoundedSize.FULL}`]: 'rounded-full',\n },\n color: {\n [`${TagColor.PRIMARY}`]: 'border-primary bg-primary/10 text-primary',\n [`${TagColor.SUCCESS}`]: 'border-success bg-success/10 text-success',\n [`${TagColor.ERROR}`]: 'border-error bg-error/10 text-error',\n [`${TagColor.WARNING}`]: 'border-warning bg-warning/10 text-warning',\n [`${TagColor.NEUTRAL}`]: '/10 border-neutral bg-neutral/10 text-neutral',\n [`${TagColor.TEXT}`]: 'border-text bg-text/10 text-text',\n [`${TagColor.BLUE}`]:\n 'border-blue-500 bg-blue-500/10 text-blue-500 dark:text-blue-300',\n [`${TagColor.YELLOW}`]:\n 'border-yellow-500 bg-yellow-500/10 text-yellow-500 dark:text-yellow-300',\n [`${TagColor.GREEN}`]:\n 'border-green-500 bg-green-500/10 text-green-500 dark:text-green-300',\n [`${TagColor.RED}`]:\n 'border-red-500 bg-red-500/10 text-red-500 dark:text-red-300',\n [`${TagColor.ORANGE}`]:\n 'border-orange-500 bg-orange-500/10 text-orange-500 dark:text-orange-300',\n [`${TagColor.PURPLE}`]:\n 'border-purple-500 bg-purple-500/10 text-purple-500 dark:text-purple-300',\n [`${TagColor.PINK}`]:\n 'border-pink-500 bg-pink-500/10 text-pink-500 dark:text-pink-300',\n [`${TagColor.BROWN}`]:\n 'border-brown-500 bg-brown-500/10 text-brown-500 dark:text-brown-300',\n [`${TagColor.GRAY}`]:\n 'border-gray-500 bg-gray-500/10 text-gray-500 dark:text-gray-300',\n [`${TagColor.BLACK}`]: 'border-black bg-black/10 text-black',\n [`${TagColor.WHITE}`]: 'border-white bg-white/10 text-white',\n },\n size: {\n [`${TagSize.XS}`]: 'border-[1.2px] px-2 py-0.5 text-xs',\n [`${TagSize.SM}`]: 'border-[1.3px] px-2 py-0.5 text-sm',\n [`${TagSize.MD}`]: 'border-2 px-2 py-1 text-base',\n [`${TagSize.LG}`]: 'border-2 px-3 py-2 text-lg',\n [`${TagSize.XL}`]: 'border-2 px-3 py-2 text-xl',\n },\n border: {\n [`${TagBorder.NONE}`]: 'border-none',\n [`${TagBorder.WITH}`]: 'border-[1.3px] border-text',\n },\n background: {\n [`${TagBackground.NONE}`]: 'bg-none',\n [`${TagBackground.WITH}`]: '',\n },\n },\n\n defaultVariants: {\n roundedSize: TagRoundedSize.FULL,\n border: TagBorder.NONE,\n color: TagColor.TEXT,\n size: TagSize.MD,\n },\n});\n\n/**\n * Tag component for displaying labels, categories, status indicators, and badges\n *\n * The Tag component is a versatile labeling element that supports multiple visual variants\n * for different use cases. It provides semantic color options, flexible sizing, and\n * customizable styling options for borders and backgrounds.\n *\n * ## Features\n * - **Semantic Colors**: Success, error, warning, neutral, and text color themes\n * - **Flexible Sizing**: Five size variants from extra small to extra large\n * - **Border Radius Options**: Eight rounding options from none to fully rounded\n * - **Border Control**: Optional borders for enhanced visual separation\n * - **Background Control**: Configurable background styling\n * - **Accessibility**: Proper HTML semantics and keyboard navigation support\n *\n * ## Use Cases\n * - **Status Indicators**: Show completion, error, or pending states\n * - **Category Labels**: Organize and categorize content\n * - **Badges**: Display counts, notifications, or achievements\n * - **Keywords**: Tag content with relevant keywords or topics\n * - **Metadata**: Show additional information like dates, authors, or types\n *\n * ## Design Principles\n * - Maintains readability across all size and color combinations\n * - Uses backdrop blur effect for subtle transparency\n * - Follows consistent spacing and typography scales\n * - Provides sufficient color contrast for accessibility\n *\n * @param {TagProps} props - The properties for the Tag component\n * @returns {JSX.Element} The rendered tag element\n *\n * @example\n * ```tsx\n * // Basic status tags\n * <Tag color={TagColor.SUCCESS}>Completed</Tag>\n * <Tag color={TagColor.ERROR}>Failed</Tag>\n * <Tag color={TagColor.WARNING}>In Progress</Tag>\n *\n * // Category tags with borders\n * <Tag color={TagColor.NEUTRAL} border={TagBorder.WITH}>\n * Technology\n * </Tag>\n * <Tag color={TagColor.TEXT} border={TagBorder.WITH}>\n * Design\n * </Tag>\n *\n * // Size variations for hierarchy\n * <div className=\"flex items-center gap-2\">\n * <Tag size={TagSize.XS} color={TagColor.NEUTRAL}>Minor</Tag>\n * <Tag size={TagSize.SM} color={TagColor.TEXT}>Standard</Tag>\n * <Tag size={TagSize.LG} color={TagColor.SUCCESS}>Important</Tag>\n * </div>\n *\n * // Rounded variations\n * <div className=\"flex gap-2\">\n * <Tag roundedSize={TagRoundedSize.NONE}>Sharp</Tag>\n * <Tag roundedSize={TagRoundedSize.MD}>Rounded</Tag>\n * <Tag roundedSize={TagRoundedSize.FULL}>Pill</Tag>\n * </div>\n *\n * // Custom styled tag\n * <Tag\n * color={TagColor.WARNING}\n * size={TagSize.LG}\n * border={TagBorder.WITH}\n * roundedSize={TagRoundedSize.LG}\n * className=\"font-bold uppercase tracking-wide\"\n * >\n * Custom Style\n * </Tag>\n *\n * // Interactive tags with click handlers\n * <Tag\n * color={TagColor.SUCCESS}\n * onClick={() => console.log('Tag clicked')}\n * className=\"cursor-pointer hover:opacity-80 transition-opacity\"\n * >\n * Clickable Tag\n * </Tag>\n * ```\n *\n * @see {@link TagColor} - Available color theme options\n * @see {@link TagSize} - Available size variants\n * @see {@link TagRoundedSize} - Available border radius options\n * @see {@link TagBorder} - Border visibility options\n * @see {@link TagBackground} - Background styling options\n */\nexport const Tag: FC<TagProps> = ({\n children,\n color,\n roundedSize,\n size,\n border,\n background,\n className,\n ...props\n}) => {\n return (\n <div\n className={containerVariants({\n color,\n roundedSize,\n size,\n border,\n background,\n className,\n })}\n {...props}\n >\n {children}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAY,iBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAY,WAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAY,UAAL;CACL;CACA;CACA;CACA;CACA;;AACF;;;;;;;;;;;;;;;;AAiBA,IAAY,YAAL;CACL;CACA;;AACF;;;;;;;;;;;;;;;;AAiBA,IAAY,gBAAL;CACL;CACA;;AACF;AAEA,MAAM,oBAAoB,IAAI,uBAAuB;CACnD,UAAU;EACR,aAAa;IACV,SAA2B;IAC3B,OAAyB;IACzB,OAAyB;IACzB,OAAyB;IACzB,OAAyB;IACzB,QAA0B;IAC1B,QAA2B;IAC3B,SAA2B;EAC9B;EACA,OAAO;IACJ,YAAwB;IACxB,YAAwB;IACxB,UAAsB;IACtB,YAAwB;IACxB,YAAwB;IACxB,SAAqB;IACrB,SACC;IACD,WACC;IACD,UACC;IACD,QACC;IACD,WACC;IACD,WACC;IACD,SACC;IACD,UACC;IACD,SACC;IACD,UAAsB;IACtB,UAAsB;EACzB;EACA,MAAM;IACH,OAAkB;IAClB,OAAkB;IAClB,OAAkB;IAClB,OAAkB;IAClB,OAAkB;EACrB;EACA,QAAQ;IACL,SAAsB;IACtB,SAAsB;EACzB;EACA,YAAY;IACT,SAA0B;IAC1B,SAA0B;EAC7B;CACF;CAEA,iBAAiB;EACf;EACA;EACA;EACA;CACF;AACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFD,MAAa,OAAqB,EAChC,UACA,OACA,aACA,MACA,QACA,YACA,WACA,GAAG,YACC;CACJ,OACE,oBAAC,OAAD;EACE,WAAW,kBAAkB;GAC3B;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,GAAI;EAEH;CACE;AAET"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Tag/index.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport type { FC, HTMLAttributes, PropsWithChildren } from 'react';\n\n/**\n * Properties for the Tag component extending HTML div attributes and variant options\n *\n * @interface TagProps\n * @extends {PropsWithChildren<VariantProps<typeof containerVariants>>}\n * @extends {HTMLAttributes<HTMLDivElement>}\n *\n * @property {ReactNode} children - The content to display inside the tag\n * @property {TagColor} [color] - Color theme variant of the tag\n * @property {TagRoundedSize} [roundedSize] - Border radius size of the tag\n * @property {TagSize} [size] - Size variant affecting padding and font size\n * @property {TagBorder} [border] - Whether to show a border around the tag\n * @property {TagBackground} [background] - Background visibility option\n * @property [className] - Additional CSS classes for custom styling\n *\n * @example\n * ```tsx\n * // Basic tag\n * <Tag>Default Tag</Tag>\n *\n * // Success tag with border\n * <Tag color={TagColor.SUCCESS} border={TagBorder.WITH}>\n * Success Status\n * </Tag>\n *\n * // Large warning tag\n * <Tag color={TagColor.WARNING} size={TagSize.LG}>\n * Important Warning\n * </Tag>\n * ```\n */\ntype TagProps = PropsWithChildren<VariantProps<typeof containerVariants>> &\n HTMLAttributes<HTMLDivElement>;\n\n/**\n * Enumeration for tag border radius sizes\n *\n * Controls the roundedness of tag corners, from sharp edges to fully rounded pills.\n *\n * @enum TagRoundedSize\n * @property NONE - 'none' - No border radius (sharp corners)\n * @property SM - 'sm' - Small border radius (2px)\n * @property MD - 'md' - Medium border radius (6px)\n * @property LG - 'lg' - Large border radius (8px)\n * @property XL - 'xl' - Extra large border radius (12px)\n * @property XXL - '2xl' - 2x large border radius (16px)\n * @property XXXL - '3xl' - 3x large border radius (24px)\n * @property FULL - 'full' - Fully rounded (50% border radius, pill shape)\n *\n * @example\n * ```tsx\n * // Sharp corners\n * <Tag roundedSize={TagRoundedSize.NONE}>Sharp Tag</Tag>\n *\n * // Pill-shaped tag\n * <Tag roundedSize={TagRoundedSize.FULL}>Pill Tag</Tag>\n *\n * // Medium rounded corners\n * <Tag roundedSize={TagRoundedSize.MD}>Rounded Tag</Tag>\n * ```\n */\nexport enum TagRoundedSize {\n NONE = 'none',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n XXL = '2xl',\n XXXL = '3xl',\n FULL = 'full',\n}\n\n/**\n * Enumeration for tag color themes\n *\n * Provides semantic color options for different tag purposes and meanings.\n * Each color includes background, border, and text color variations.\n *\n * @enum TagColor\n * @property SUCCESS - 'success' - Green theme for positive states, success messages, or completed items\n * @property ERROR - 'error' - Red theme for error states, warnings, or failed operations\n * @property WARNING - 'warning' - Yellow/orange theme for caution, pending states, or important notices\n * @property NEUTRAL - 'neutral' - Gray theme for neutral information or secondary content\n * @property TEXT - 'text' - Default text color theme for general purpose tags\n *\n * @example\n * ```tsx\n * // Status indicators\n * <Tag color={TagColor.SUCCESS}>Completed</Tag>\n * <Tag color={TagColor.ERROR}>Failed</Tag>\n * <Tag color={TagColor.WARNING}>Pending</Tag>\n *\n * // Category tags\n * <Tag color={TagColor.NEUTRAL}>Category</Tag>\n * <Tag color={TagColor.TEXT}>General</Tag>\n * ```\n */\nexport enum TagColor {\n PRIMARY = 'primary',\n SUCCESS = 'success',\n ERROR = 'error',\n WARNING = 'warning',\n NEUTRAL = 'neutral',\n TEXT = 'text',\n BLUE = 'blue',\n YELLOW = 'yellow',\n GREEN = 'green',\n RED = 'red',\n ORANGE = 'orange',\n PURPLE = 'purple',\n PINK = 'pink',\n BROWN = 'brown',\n GRAY = 'gray',\n BLACK = 'black',\n WHITE = 'white',\n}\n\n/**\n * Enumeration for tag size variants\n *\n * Controls the overall size of tags including padding, font size, and border thickness.\n * Sizes are designed to maintain visual hierarchy and readability.\n *\n * @enum TagSize\n * @property XS - 'xs' - Extra small (0.5rem padding, text-xs, 1.2px border)\n * @property SM - 'sm' - Small (0.5rem padding, text-sm, 1.5px border)\n * @property MD - 'md' - Medium (1rem padding, text-base, 2px border) - Default size\n * @property LG - 'lg' - Large (2rem padding, text-lg, 2px border)\n * @property XL - 'xl' - Extra large (4rem padding, text-xl, 2px border)\n *\n * @example\n * ```tsx\n * // Different sizes for hierarchy\n * <Tag size={TagSize.XS}>Small detail</Tag>\n * <Tag size={TagSize.SM}>Minor category</Tag>\n * <Tag size={TagSize.MD}>Standard tag</Tag>\n * <Tag size={TagSize.LG}>Important label</Tag>\n * <Tag size={TagSize.XL}>Hero tag</Tag>\n * ```\n */\nexport enum TagSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n}\n\n/**\n * Enumeration for tag border visibility\n *\n * Controls whether a border is displayed around the tag.\n *\n * @enum TagBorder\n * @property NONE - 'none' - No border (default)\n * @property WITH - 'with' - Show border with 1.5px thickness\n *\n * @example\n * ```tsx\n * <Tag border={TagBorder.NONE}>Borderless</Tag>\n * <Tag border={TagBorder.WITH}>With Border</Tag>\n * ```\n */\nexport enum TagBorder {\n NONE = 'none',\n WITH = 'with',\n}\n\n/**\n * Enumeration for tag background visibility\n *\n * Controls the background styling of the tag.\n *\n * @enum TagBackground\n * @property NONE - 'none' - No background styling\n * @property WITH - 'with' - Apply background styling\n *\n * @example\n * ```tsx\n * <Tag background={TagBackground.NONE}>No Background</Tag>\n * <Tag background={TagBackground.WITH}>With Background</Tag>\n * ```\n */\nexport enum TagBackground {\n NONE = 'none',\n WITH = 'with',\n}\n\nconst containerVariants = cva('w-fit backdrop-blur', {\n variants: {\n roundedSize: {\n [`${TagRoundedSize.NONE}`]: 'rounded-none',\n [`${TagRoundedSize.SM}`]: 'rounded-sm',\n [`${TagRoundedSize.MD}`]: 'rounded-md',\n [`${TagRoundedSize.LG}`]: 'rounded-lg',\n [`${TagRoundedSize.XL}`]: 'rounded-xl',\n [`${TagRoundedSize.XXL}`]: 'rounded-2xl',\n [`${TagRoundedSize.XXXL}`]: 'rounded-3xl',\n [`${TagRoundedSize.FULL}`]: 'rounded-full',\n },\n color: {\n [`${TagColor.PRIMARY}`]: 'border-primary bg-primary/10 text-primary',\n [`${TagColor.SUCCESS}`]: 'border-success bg-success/10 text-success',\n [`${TagColor.ERROR}`]: 'border-error bg-error/10 text-error',\n [`${TagColor.WARNING}`]: 'border-warning bg-warning/10 text-warning',\n [`${TagColor.NEUTRAL}`]: '/10 border-neutral bg-neutral/10 text-neutral',\n [`${TagColor.TEXT}`]: 'border-text bg-text/10 text-text',\n [`${TagColor.BLUE}`]:\n 'border-blue-500 bg-blue-500/10 text-blue-500 dark:text-blue-300',\n [`${TagColor.YELLOW}`]:\n 'border-yellow-500 bg-yellow-500/10 text-yellow-500 dark:text-yellow-300',\n [`${TagColor.GREEN}`]:\n 'border-green-500 bg-green-500/10 text-green-500 dark:text-green-300',\n [`${TagColor.RED}`]:\n 'border-error bg-error/10 text-error dark:text-red-300',\n [`${TagColor.ORANGE}`]:\n 'border-orange-500 bg-orange-500/10 text-orange-500 dark:text-orange-300',\n [`${TagColor.PURPLE}`]:\n 'border-purple-500 bg-purple-500/10 text-purple-500 dark:text-purple-300',\n [`${TagColor.PINK}`]:\n 'border-pink-500 bg-pink-500/10 text-pink-500 dark:text-pink-300',\n [`${TagColor.BROWN}`]:\n 'border-brown-500 bg-brown-500/10 text-brown-500 dark:text-brown-300',\n [`${TagColor.GRAY}`]:\n 'border-gray-500 bg-gray-500/10 text-gray-500 dark:text-gray-300',\n [`${TagColor.BLACK}`]: 'border-black bg-black/10 text-black',\n [`${TagColor.WHITE}`]: 'border-white bg-white/10 text-white',\n },\n size: {\n [`${TagSize.XS}`]: 'border-[1.2px] px-2 py-0.5 text-xs',\n [`${TagSize.SM}`]: 'border-[1.3px] px-2 py-0.5 text-sm',\n [`${TagSize.MD}`]: 'border-2 px-2 py-1 text-base',\n [`${TagSize.LG}`]: 'border-2 px-3 py-2 text-lg',\n [`${TagSize.XL}`]: 'border-2 px-3 py-2 text-xl',\n },\n border: {\n [`${TagBorder.NONE}`]: 'border-none',\n [`${TagBorder.WITH}`]: 'border-[1.3px] border-text',\n },\n background: {\n [`${TagBackground.NONE}`]: 'bg-none',\n [`${TagBackground.WITH}`]: '',\n },\n },\n\n defaultVariants: {\n roundedSize: TagRoundedSize.FULL,\n border: TagBorder.NONE,\n color: TagColor.TEXT,\n size: TagSize.MD,\n },\n});\n\n/**\n * Tag component for displaying labels, categories, status indicators, and badges\n *\n * The Tag component is a versatile labeling element that supports multiple visual variants\n * for different use cases. It provides semantic color options, flexible sizing, and\n * customizable styling options for borders and backgrounds.\n *\n * ## Features\n * - **Semantic Colors**: Success, error, warning, neutral, and text color themes\n * - **Flexible Sizing**: Five size variants from extra small to extra large\n * - **Border Radius Options**: Eight rounding options from none to fully rounded\n * - **Border Control**: Optional borders for enhanced visual separation\n * - **Background Control**: Configurable background styling\n * - **Accessibility**: Proper HTML semantics and keyboard navigation support\n *\n * ## Use Cases\n * - **Status Indicators**: Show completion, error, or pending states\n * - **Category Labels**: Organize and categorize content\n * - **Badges**: Display counts, notifications, or achievements\n * - **Keywords**: Tag content with relevant keywords or topics\n * - **Metadata**: Show additional information like dates, authors, or types\n *\n * ## Design Principles\n * - Maintains readability across all size and color combinations\n * - Uses backdrop blur effect for subtle transparency\n * - Follows consistent spacing and typography scales\n * - Provides sufficient color contrast for accessibility\n *\n * @param {TagProps} props - The properties for the Tag component\n * @returns {JSX.Element} The rendered tag element\n *\n * @example\n * ```tsx\n * // Basic status tags\n * <Tag color={TagColor.SUCCESS}>Completed</Tag>\n * <Tag color={TagColor.ERROR}>Failed</Tag>\n * <Tag color={TagColor.WARNING}>In Progress</Tag>\n *\n * // Category tags with borders\n * <Tag color={TagColor.NEUTRAL} border={TagBorder.WITH}>\n * Technology\n * </Tag>\n * <Tag color={TagColor.TEXT} border={TagBorder.WITH}>\n * Design\n * </Tag>\n *\n * // Size variations for hierarchy\n * <div className=\"flex items-center gap-2\">\n * <Tag size={TagSize.XS} color={TagColor.NEUTRAL}>Minor</Tag>\n * <Tag size={TagSize.SM} color={TagColor.TEXT}>Standard</Tag>\n * <Tag size={TagSize.LG} color={TagColor.SUCCESS}>Important</Tag>\n * </div>\n *\n * // Rounded variations\n * <div className=\"flex gap-2\">\n * <Tag roundedSize={TagRoundedSize.NONE}>Sharp</Tag>\n * <Tag roundedSize={TagRoundedSize.MD}>Rounded</Tag>\n * <Tag roundedSize={TagRoundedSize.FULL}>Pill</Tag>\n * </div>\n *\n * // Custom styled tag\n * <Tag\n * color={TagColor.WARNING}\n * size={TagSize.LG}\n * border={TagBorder.WITH}\n * roundedSize={TagRoundedSize.LG}\n * className=\"font-bold uppercase tracking-wide\"\n * >\n * Custom Style\n * </Tag>\n *\n * // Interactive tags with click handlers\n * <Tag\n * color={TagColor.SUCCESS}\n * onClick={() => console.log('Tag clicked')}\n * className=\"cursor-pointer hover:opacity-80 transition-opacity\"\n * >\n * Clickable Tag\n * </Tag>\n * ```\n *\n * @see {@link TagColor} - Available color theme options\n * @see {@link TagSize} - Available size variants\n * @see {@link TagRoundedSize} - Available border radius options\n * @see {@link TagBorder} - Border visibility options\n * @see {@link TagBackground} - Background styling options\n */\nexport const Tag: FC<TagProps> = ({\n children,\n color,\n roundedSize,\n size,\n border,\n background,\n className,\n ...props\n}) => {\n return (\n <div\n className={containerVariants({\n color,\n roundedSize,\n size,\n border,\n background,\n className,\n })}\n {...props}\n >\n {children}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAY,iBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAY,WAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAY,UAAL;CACL;CACA;CACA;CACA;CACA;;AACF;;;;;;;;;;;;;;;;AAiBA,IAAY,YAAL;CACL;CACA;;AACF;;;;;;;;;;;;;;;;AAiBA,IAAY,gBAAL;CACL;CACA;;AACF;AAEA,MAAM,oBAAoB,IAAI,uBAAuB;CACnD,UAAU;EACR,aAAa;IACV,SAA2B;IAC3B,OAAyB;IACzB,OAAyB;IACzB,OAAyB;IACzB,OAAyB;IACzB,QAA0B;IAC1B,QAA2B;IAC3B,SAA2B;EAC9B;EACA,OAAO;IACJ,YAAwB;IACxB,YAAwB;IACxB,UAAsB;IACtB,YAAwB;IACxB,YAAwB;IACxB,SAAqB;IACrB,SACC;IACD,WACC;IACD,UACC;IACD,QACC;IACD,WACC;IACD,WACC;IACD,SACC;IACD,UACC;IACD,SACC;IACD,UAAsB;IACtB,UAAsB;EACzB;EACA,MAAM;IACH,OAAkB;IAClB,OAAkB;IAClB,OAAkB;IAClB,OAAkB;IAClB,OAAkB;EACrB;EACA,QAAQ;IACL,SAAsB;IACtB,SAAsB;EACzB;EACA,YAAY;IACT,SAA0B;IAC1B,SAA0B;EAC7B;CACF;CAEA,iBAAiB;EACf;EACA;EACA;EACA;CACF;AACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFD,MAAa,OAAqB,EAChC,UACA,OACA,aACA,MACA,QACA,YACA,WACA,GAAG,YACC;CACJ,OACE,oBAAC,OAAD;EACE,WAAW,kBAAkB;GAC3B;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,GAAI;EAEH;CACE;AAET"}
@@ -107,7 +107,7 @@ const Terminal = ({ className, children, isDarkMode = false, title = "bash", onC
107
107
  children: [/* @__PURE__ */ jsxs("div", {
108
108
  className: "mx-2 flex items-center justify-start gap-2 p-1",
109
109
  children: [
110
- /* @__PURE__ */ jsx("div", { className: "size-3 rounded-full bg-red-500" }),
110
+ /* @__PURE__ */ jsx("div", { className: "size-3 rounded-full bg-error" }),
111
111
  /* @__PURE__ */ jsx("div", { className: "size-3 rounded-full bg-yellow-500" }),
112
112
  /* @__PURE__ */ jsx("div", { className: "size-3 rounded-full bg-green-500" })
113
113
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"Terminal.mjs","names":[],"sources":["../../../../src/components/Terminal/Terminal.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { Input } from '@components/Input';\nimport { cn } from '@utils/cn';\nimport {\n type FC,\n type HTMLAttributes,\n type KeyboardEvent,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\n\n// ANSI color code mappings to CSS colors\nconst ANSI_COLORS: Record<string, { light: string; dark: string }> = {\n '\\x1b[0m': { light: '', dark: '' }, // RESET\n '\\x1b[90m': { light: 'text-gray-500', dark: 'text-gray-400' }, // GREY\n '\\x1b[38;5;239m': { light: 'text-gray-600', dark: 'text-gray-500' }, // GREY_DARK\n '\\x1b[38;5;252m': { light: 'text-gray-300', dark: 'text-gray-300' }, // GREY_LIGHT\n '\\x1b[34m': { light: 'text-blue-600', dark: 'text-blue-400' }, // BLUE\n '\\x1b[31m': { light: 'text-red-600', dark: 'text-red-400' }, // RED\n '\\x1b[32m': { light: 'text-green-600', dark: 'text-green-400' }, // GREEN\n '\\x1b[38;5;226m': { light: 'text-yellow-500', dark: 'text-yellow-300' }, // YELLOW\n '\\x1b[35m': { light: 'text-purple-600', dark: 'text-purple-400' }, // MAGENTA\n '\\x1b[38;5;3m': { light: 'text-amber-600', dark: 'text-amber-300' }, // BEIGE\n '\\x1b[38;5;208m': { light: 'text-orange-600', dark: 'text-orange-400' }, // ORANGE\n '\\x1b[36m': { light: 'text-cyan-600', dark: 'text-cyan-400' }, // CYAN\n '\\x1b[37m': { light: 'text-gray-800', dark: 'text-gray-200' }, // WHITE\n '\\x1b[1m': { light: 'font-bold', dark: 'font-bold' }, // BOLD\n};\n\ninterface AnsiSegment {\n text: string;\n color?: string;\n isBold?: boolean;\n}\n\nconst parseAnsiCodes = (text: string, isDarkMode: boolean): AnsiSegment[] => {\n const segments: AnsiSegment[] = [];\n // biome-ignore lint/suspicious/noControlCharactersInRegex: we need to parse ANSI codes\n const ansiRegex = /(\\x1b\\[[0-9;]*m)/g;\n const parts = text.split(ansiRegex);\n\n let currentColor: string | undefined;\n let isBold = false;\n\n for (const part of parts) {\n if (ansiRegex.test(part)) {\n // This is an ANSI code\n const colorMapping = ANSI_COLORS[part];\n if (colorMapping) {\n if (part === '\\x1b[0m') {\n // RESET\n currentColor = undefined;\n isBold = false;\n } else if (part === '\\x1b[1m') {\n // BOLD\n isBold = true;\n } else {\n currentColor = isDarkMode ? colorMapping.dark : colorMapping.light;\n }\n }\n } else if (part) {\n // This is actual text\n segments.push({\n text: part,\n color: currentColor,\n isBold,\n });\n }\n }\n\n return segments;\n};\n\nexport type TerminalProps = {\n children: string;\n isDarkMode?: boolean;\n title?: string;\n onClose?: () => void;\n onSubmit?: (value: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onSubmit'>;\n\nexport const Terminal: FC<TerminalProps> = ({\n className,\n children,\n isDarkMode = false,\n title = 'bash',\n onClose,\n onSubmit,\n ...props\n}) => {\n const content = useIntlayer('terminal');\n const lines = typeof children === 'string' ? children.split('\\n') : [];\n const [inputValue, setInputValue] = useState('');\n\n const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' && inputValue.trim()) {\n onSubmit?.(inputValue);\n setInputValue('');\n }\n };\n\n // Explicitly type the container props to avoid type conflicts\n const containerProps = props as Omit<\n HTMLAttributes<HTMLDivElement>,\n 'children' | 'onSubmit'\n >;\n\n return (\n <Container\n roundedSize=\"2xl\"\n className={cn(\n 'flex min-w-0 max-w-full flex-col overflow-hidden font-mono',\n className\n )}\n {...containerProps}\n >\n {/* Tab bar */}\n <div className=\"flex w-full flex-row items-center justify-start gap-1 bg-neutral-200 text-neutral text-xs dark:bg-neutral-950\">\n <div className=\"mx-2 flex items-center justify-start gap-2 p-1\">\n <div className=\"size-3 rounded-full bg-red-500\" />\n <div className=\"size-3 rounded-full bg-yellow-500\" />\n <div className=\"size-3 rounded-full bg-green-500\" />\n </div>\n <div className=\"flex size-full overflow-y-auto\">\n <div className=\"flex h-8 min-w-20 items-center justify-between gap-2 bg-card px-3 py-1\">\n <span>{title}</span>\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-neutral transition-colors hover:text-text\"\n aria-label={content.closeTab.value}\n >\n ×\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Terminal content - hide scrollbar */}\n <pre className=\"min-w-0 max-w-full overflow-x-auto overflow-y-auto border-neutral/30 border-b p-3 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <code>\n {lines.map((line, lineIndex) => {\n const segments = parseAnsiCodes(line, isDarkMode);\n\n return (\n <span className=\"line block w-full\" key={`line-${lineIndex}`}>\n {segments.length === 0\n ? '\\n'\n : segments.map((segment, segIndex) => (\n <span\n key={`seg-${lineIndex}-${segIndex}`}\n className={cn(segment.color, {\n 'font-bold': segment.isBold,\n })}\n >\n {segment.text}\n </span>\n ))}\n </span>\n );\n })}\n </code>\n </pre>\n\n {/* Input area */}\n <Container className=\"p-2\">\n <span className=\"text-neutral\">~/Desktop/MyApp</span>\n <Input\n className=\"m-0.5 w-full\"\n variant=\"invisible\"\n value={inputValue}\n aria-label={content.terminalInput.value}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n />\n </Container>\n </Container>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Terminal.mjs","names":[],"sources":["../../../../src/components/Terminal/Terminal.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { Input } from '@components/Input';\nimport { cn } from '@utils/cn';\nimport {\n type FC,\n type HTMLAttributes,\n type KeyboardEvent,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\n\n// ANSI color code mappings to CSS colors\nconst ANSI_COLORS: Record<string, { light: string; dark: string }> = {\n '\\x1b[0m': { light: '', dark: '' }, // RESET\n '\\x1b[90m': { light: 'text-gray-500', dark: 'text-gray-400' }, // GREY\n '\\x1b[38;5;239m': { light: 'text-gray-600', dark: 'text-gray-500' }, // GREY_DARK\n '\\x1b[38;5;252m': { light: 'text-gray-300', dark: 'text-gray-300' }, // GREY_LIGHT\n '\\x1b[34m': { light: 'text-blue-600', dark: 'text-blue-400' }, // BLUE\n '\\x1b[31m': { light: 'text-red-600', dark: 'text-red-400' }, // RED\n '\\x1b[32m': { light: 'text-green-600', dark: 'text-green-400' }, // GREEN\n '\\x1b[38;5;226m': { light: 'text-yellow-500', dark: 'text-yellow-300' }, // YELLOW\n '\\x1b[35m': { light: 'text-purple-600', dark: 'text-purple-400' }, // MAGENTA\n '\\x1b[38;5;3m': { light: 'text-amber-600', dark: 'text-amber-300' }, // BEIGE\n '\\x1b[38;5;208m': { light: 'text-orange-600', dark: 'text-orange-400' }, // ORANGE\n '\\x1b[36m': { light: 'text-cyan-600', dark: 'text-cyan-400' }, // CYAN\n '\\x1b[37m': { light: 'text-gray-800', dark: 'text-gray-200' }, // WHITE\n '\\x1b[1m': { light: 'font-bold', dark: 'font-bold' }, // BOLD\n};\n\ninterface AnsiSegment {\n text: string;\n color?: string;\n isBold?: boolean;\n}\n\nconst parseAnsiCodes = (text: string, isDarkMode: boolean): AnsiSegment[] => {\n const segments: AnsiSegment[] = [];\n // biome-ignore lint/suspicious/noControlCharactersInRegex: we need to parse ANSI codes\n const ansiRegex = /(\\x1b\\[[0-9;]*m)/g;\n const parts = text.split(ansiRegex);\n\n let currentColor: string | undefined;\n let isBold = false;\n\n for (const part of parts) {\n if (ansiRegex.test(part)) {\n // This is an ANSI code\n const colorMapping = ANSI_COLORS[part];\n if (colorMapping) {\n if (part === '\\x1b[0m') {\n // RESET\n currentColor = undefined;\n isBold = false;\n } else if (part === '\\x1b[1m') {\n // BOLD\n isBold = true;\n } else {\n currentColor = isDarkMode ? colorMapping.dark : colorMapping.light;\n }\n }\n } else if (part) {\n // This is actual text\n segments.push({\n text: part,\n color: currentColor,\n isBold,\n });\n }\n }\n\n return segments;\n};\n\nexport type TerminalProps = {\n children: string;\n isDarkMode?: boolean;\n title?: string;\n onClose?: () => void;\n onSubmit?: (value: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onSubmit'>;\n\nexport const Terminal: FC<TerminalProps> = ({\n className,\n children,\n isDarkMode = false,\n title = 'bash',\n onClose,\n onSubmit,\n ...props\n}) => {\n const content = useIntlayer('terminal');\n const lines = typeof children === 'string' ? children.split('\\n') : [];\n const [inputValue, setInputValue] = useState('');\n\n const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' && inputValue.trim()) {\n onSubmit?.(inputValue);\n setInputValue('');\n }\n };\n\n // Explicitly type the container props to avoid type conflicts\n const containerProps = props as Omit<\n HTMLAttributes<HTMLDivElement>,\n 'children' | 'onSubmit'\n >;\n\n return (\n <Container\n roundedSize=\"2xl\"\n className={cn(\n 'flex min-w-0 max-w-full flex-col overflow-hidden font-mono',\n className\n )}\n {...containerProps}\n >\n {/* Tab bar */}\n <div className=\"flex w-full flex-row items-center justify-start gap-1 bg-neutral-200 text-neutral text-xs dark:bg-neutral-950\">\n <div className=\"mx-2 flex items-center justify-start gap-2 p-1\">\n <div className=\"size-3 rounded-full bg-error\" />\n <div className=\"size-3 rounded-full bg-yellow-500\" />\n <div className=\"size-3 rounded-full bg-green-500\" />\n </div>\n <div className=\"flex size-full overflow-y-auto\">\n <div className=\"flex h-8 min-w-20 items-center justify-between gap-2 bg-card px-3 py-1\">\n <span>{title}</span>\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-neutral transition-colors hover:text-text\"\n aria-label={content.closeTab.value}\n >\n ×\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Terminal content - hide scrollbar */}\n <pre className=\"min-w-0 max-w-full overflow-x-auto overflow-y-auto border-neutral/30 border-b p-3 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <code>\n {lines.map((line, lineIndex) => {\n const segments = parseAnsiCodes(line, isDarkMode);\n\n return (\n <span className=\"line block w-full\" key={`line-${lineIndex}`}>\n {segments.length === 0\n ? '\\n'\n : segments.map((segment, segIndex) => (\n <span\n key={`seg-${lineIndex}-${segIndex}`}\n className={cn(segment.color, {\n 'font-bold': segment.isBold,\n })}\n >\n {segment.text}\n </span>\n ))}\n </span>\n );\n })}\n </code>\n </pre>\n\n {/* Input area */}\n <Container className=\"p-2\">\n <span className=\"text-neutral\">~/Desktop/MyApp</span>\n <Input\n className=\"m-0.5 w-full\"\n variant=\"invisible\"\n value={inputValue}\n aria-label={content.terminalInput.value}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n />\n </Container>\n </Container>\n );\n};\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
4
  import { TextArea } from "./TextArea.mjs";
5
- import { useEffect, useImperativeHandle, useRef } from "react";
5
+ import { useImperativeHandle, useLayoutEffect, useRef } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/components/TextArea/AutoSizeTextArea.tsx
@@ -79,7 +79,7 @@ const AutoSizedTextArea = ({ className, autoSize = true, onChange, maxRows = 999
79
79
  const minHeight = LINE_HEIGHT + LINE_PADDING;
80
80
  textAreaStyle.height = `${Math.max(Math.min(scrollHeight, maxHeight), minHeight)}px`;
81
81
  };
82
- useEffect(() => {
82
+ useLayoutEffect(() => {
83
83
  adjustHeight();
84
84
  }, [
85
85
  props.value,
@@ -1 +1 @@
1
- {"version":3,"file":"AutoSizeTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/AutoSizeTextArea.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n type ChangeEventHandler,\n type FC,\n useEffect,\n useImperativeHandle,\n useRef,\n} from 'react';\nimport { TextArea, type TextAreaProps } from './TextArea';\n\n/**\n * Props for the AutoSizedTextArea component.\n *\n * Extends TextAreaProps with auto-sizing functionality and row limitations.\n *\n * @example\n * ```tsx\n * // Auto-sizing textarea that grows with content\n * <AutoSizedTextArea\n * placeholder=\"Start typing and watch it grow...\"\n * autoSize={true}\n * maxRows={10}\n * />\n *\n * // Limited height with scroll when exceeded\n * <AutoSizedTextArea\n * value={longText}\n * onChange={handleChange}\n * autoSize={true}\n * maxRows={5}\n * className=\"max-h-[120px]\"\n * />\n *\n * // Disable auto-sizing for fixed height\n * <AutoSizedTextArea\n * autoSize={false}\n * rows={3}\n * placeholder=\"Fixed height textarea\"\n * />\n * ```\n */\nexport type AutoSizedTextAreaProps = TextAreaProps & {\n /** Whether to automatically adjust height based on content */\n autoSize?: boolean;\n /** Maximum number of rows before scrolling is enabled */\n maxRows?: number;\n};\n\nconst LINE_HEIGHT = 24; // px\nconst LINE_PADDING = 12; // px\n\n/**\n * AutoSizedTextArea Component\n *\n * An enhanced textarea that automatically adjusts its height based on content,\n * providing a smooth user experience for variable-length text input.\n *\n * ## Features\n * - **Auto-Sizing**: Dynamically grows and shrinks based on content\n * - **Row Limits**: Configurable maximum rows before scrolling\n * - **Smooth Transitions**: Seamless height adjustments as user types\n * - **Scroll Management**: Automatic overflow handling when max height reached\n * - **Performance Optimized**: Efficient height calculations and updates\n *\n * ## Technical Details\n * - Line height: 24px with 12px padding\n * - Height calculation: `scrollHeight` vs `maxRows * lineHeight + padding`\n * - Resize: Disabled when auto-sizing is active for smooth experience\n * - Ref forwarding: Supports imperative access to textarea element\n *\n * ## Use Cases\n * - Chat message composition with dynamic sizing\n * - Comment forms that expand with content\n * - Note-taking interfaces with variable length\n * - Social media post creation\n * - Code snippet input with growth limits\n *\n * @example\n * ```tsx\n * // Chat-style auto-expanding textarea\n * const [message, setMessage] = useState('');\n *\n * <AutoSizedTextArea\n * value={message}\n * onChange={(e) => setMessage(e.target.value)}\n * placeholder=\"Type your message...\"\n * autoSize={true}\n * maxRows={8}\n * className=\"min-h-[40px]\"\n * onKeyDown={(e) => {\n * if (e.key === 'Enter' && !e.shiftKey) {\n * e.preventDefault();\n * sendMessage(message);\n * setMessage('');\n * }\n * }}\n * />\n *\n * // Note-taking with generous height limits\n * <AutoSizedTextArea\n * defaultValue={note.content}\n * onChange={handleNoteChange}\n * placeholder=\"Write your notes here...\"\n * autoSize={true}\n * maxRows={20}\n * variant={InputVariant.DEFAULT}\n * />\n * ```\n */\nexport const AutoSizedTextArea: FC<AutoSizedTextAreaProps> = ({\n className,\n autoSize = true,\n onChange,\n maxRows = 999,\n ref,\n ...props\n}) => {\n const textAreaRef = useRef<HTMLTextAreaElement | null>(null);\n\n useImperativeHandle(ref, () => textAreaRef.current!);\n\n const adjustHeight = () => {\n const textAreaEl = textAreaRef.current;\n\n if (!textAreaEl || !autoSize) return;\n\n const textAreaStyle = textAreaEl.style;\n\n // Reset height to get accurate scrollHeight\n textAreaStyle.height = 'auto';\n const scrollHeight = textAreaEl.scrollHeight;\n const maxHeight = LINE_HEIGHT * maxRows + LINE_PADDING;\n const minHeight = LINE_HEIGHT + LINE_PADDING;\n\n // Set the new height\n textAreaStyle.height = `${Math.max(Math.min(scrollHeight, maxHeight), minHeight)}px`;\n };\n\n useEffect(() => {\n adjustHeight();\n }, [props.value, props.defaultValue, adjustHeight]);\n\n const handleChange: ChangeEventHandler<HTMLTextAreaElement> = (e) => {\n onChange?.(e);\n adjustHeight();\n };\n\n const setRef = (el: HTMLTextAreaElement | null) => {\n textAreaRef.current = el;\n if (el) {\n adjustHeight();\n }\n };\n\n return (\n <TextArea\n ref={setRef}\n onChange={handleChange}\n className={cn('overflow-y-auto', autoSize && 'resize-none', className)}\n {...props}\n />\n );\n};\n"],"mappings":";;;;;;;;AAkDA,MAAM,cAAc;AACpB,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DrB,MAAa,qBAAiD,EAC5D,WACA,WAAW,MACX,UACA,UAAU,KACV,KACA,GAAG,YACC;CACJ,MAAM,cAAc,OAAmC,IAAI;CAE3D,oBAAoB,WAAW,YAAY,OAAQ;CAEnD,MAAM,qBAAqB;EACzB,MAAM,aAAa,YAAY;EAE/B,IAAI,CAAC,cAAc,CAAC,UAAU;EAE9B,MAAM,gBAAgB,WAAW;EAGjC,cAAc,SAAS;EACvB,MAAM,eAAe,WAAW;EAChC,MAAM,YAAY,cAAc,UAAU;EAC1C,MAAM,YAAY,cAAc;EAGhC,cAAc,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,cAAc,SAAS,GAAG,SAAS,EAAE;CACnF;CAEA,gBAAgB;EACd,aAAa;CACf,GAAG;EAAC,MAAM;EAAO,MAAM;EAAc;CAAY,CAAC;CAElD,MAAM,gBAAyD,MAAM;EACnE,WAAW,CAAC;EACZ,aAAa;CACf;CAEA,MAAM,UAAU,OAAmC;EACjD,YAAY,UAAU;EACtB,IAAI,IACF,aAAa;CAEjB;CAEA,OACE,oBAAC,UAAD;EACE,KAAK;EACL,UAAU;EACV,WAAW,GAAG,mBAAmB,YAAY,eAAe,SAAS;EACrE,GAAI;CACL;AAEL"}
1
+ {"version":3,"file":"AutoSizeTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/AutoSizeTextArea.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n type ChangeEventHandler,\n type FC,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n} from 'react';\nimport { TextArea, type TextAreaProps } from './TextArea';\n\n/**\n * Props for the AutoSizedTextArea component.\n *\n * Extends TextAreaProps with auto-sizing functionality and row limitations.\n *\n * @example\n * ```tsx\n * // Auto-sizing textarea that grows with content\n * <AutoSizedTextArea\n * placeholder=\"Start typing and watch it grow...\"\n * autoSize={true}\n * maxRows={10}\n * />\n *\n * // Limited height with scroll when exceeded\n * <AutoSizedTextArea\n * value={longText}\n * onChange={handleChange}\n * autoSize={true}\n * maxRows={5}\n * className=\"max-h-[120px]\"\n * />\n *\n * // Disable auto-sizing for fixed height\n * <AutoSizedTextArea\n * autoSize={false}\n * rows={3}\n * placeholder=\"Fixed height textarea\"\n * />\n * ```\n */\nexport type AutoSizedTextAreaProps = TextAreaProps & {\n /** Whether to automatically adjust height based on content */\n autoSize?: boolean;\n /** Maximum number of rows before scrolling is enabled */\n maxRows?: number;\n};\n\nconst LINE_HEIGHT = 24; // px\nconst LINE_PADDING = 12; // px\n\n/**\n * AutoSizedTextArea Component\n *\n * An enhanced textarea that automatically adjusts its height based on content,\n * providing a smooth user experience for variable-length text input.\n *\n * ## Features\n * - **Auto-Sizing**: Dynamically grows and shrinks based on content\n * - **Row Limits**: Configurable maximum rows before scrolling\n * - **Smooth Transitions**: Seamless height adjustments as user types\n * - **Scroll Management**: Automatic overflow handling when max height reached\n * - **Performance Optimized**: Efficient height calculations and updates\n *\n * ## Technical Details\n * - Line height: 24px with 12px padding\n * - Height calculation: `scrollHeight` vs `maxRows * lineHeight + padding`\n * - Resize: Disabled when auto-sizing is active for smooth experience\n * - Ref forwarding: Supports imperative access to textarea element\n *\n * ## Use Cases\n * - Chat message composition with dynamic sizing\n * - Comment forms that expand with content\n * - Note-taking interfaces with variable length\n * - Social media post creation\n * - Code snippet input with growth limits\n *\n * @example\n * ```tsx\n * // Chat-style auto-expanding textarea\n * const [message, setMessage] = useState('');\n *\n * <AutoSizedTextArea\n * value={message}\n * onChange={(e) => setMessage(e.target.value)}\n * placeholder=\"Type your message...\"\n * autoSize={true}\n * maxRows={8}\n * className=\"min-h-[40px]\"\n * onKeyDown={(e) => {\n * if (e.key === 'Enter' && !e.shiftKey) {\n * e.preventDefault();\n * sendMessage(message);\n * setMessage('');\n * }\n * }}\n * />\n *\n * // Note-taking with generous height limits\n * <AutoSizedTextArea\n * defaultValue={note.content}\n * onChange={handleNoteChange}\n * placeholder=\"Write your notes here...\"\n * autoSize={true}\n * maxRows={20}\n * variant={InputVariant.DEFAULT}\n * />\n * ```\n */\nexport const AutoSizedTextArea: FC<AutoSizedTextAreaProps> = ({\n className,\n autoSize = true,\n onChange,\n maxRows = 999,\n ref,\n ...props\n}) => {\n const textAreaRef = useRef<HTMLTextAreaElement | null>(null);\n\n useImperativeHandle(ref, () => textAreaRef.current!);\n\n const adjustHeight = () => {\n const textAreaEl = textAreaRef.current;\n\n if (!textAreaEl || !autoSize) return;\n\n const textAreaStyle = textAreaEl.style;\n\n // Reset height to get accurate scrollHeight\n textAreaStyle.height = 'auto';\n const scrollHeight = textAreaEl.scrollHeight;\n const maxHeight = LINE_HEIGHT * maxRows + LINE_PADDING;\n const minHeight = LINE_HEIGHT + LINE_PADDING;\n\n // Set the new height\n textAreaStyle.height = `${Math.max(Math.min(scrollHeight, maxHeight), minHeight)}px`;\n };\n\n useLayoutEffect(() => {\n adjustHeight();\n }, [props.value, props.defaultValue, adjustHeight]);\n\n const handleChange: ChangeEventHandler<HTMLTextAreaElement> = (e) => {\n onChange?.(e);\n adjustHeight();\n };\n\n const setRef = (el: HTMLTextAreaElement | null) => {\n textAreaRef.current = el;\n if (el) {\n adjustHeight();\n }\n };\n\n return (\n <TextArea\n ref={setRef}\n onChange={handleChange}\n className={cn('overflow-y-auto', autoSize && 'resize-none', className)}\n {...props}\n />\n );\n};\n"],"mappings":";;;;;;;;AAkDA,MAAM,cAAc;AACpB,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DrB,MAAa,qBAAiD,EAC5D,WACA,WAAW,MACX,UACA,UAAU,KACV,KACA,GAAG,YACC;CACJ,MAAM,cAAc,OAAmC,IAAI;CAE3D,oBAAoB,WAAW,YAAY,OAAQ;CAEnD,MAAM,qBAAqB;EACzB,MAAM,aAAa,YAAY;EAE/B,IAAI,CAAC,cAAc,CAAC,UAAU;EAE9B,MAAM,gBAAgB,WAAW;EAGjC,cAAc,SAAS;EACvB,MAAM,eAAe,WAAW;EAChC,MAAM,YAAY,cAAc,UAAU;EAC1C,MAAM,YAAY,cAAc;EAGhC,cAAc,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,cAAc,SAAS,GAAG,SAAS,EAAE;CACnF;CAEA,sBAAsB;EACpB,aAAa;CACf,GAAG;EAAC,MAAM;EAAO,MAAM;EAAc;CAAY,CAAC;CAElD,MAAM,gBAAyD,MAAM;EACnE,WAAW,CAAC;EACZ,aAAa;CACf;CAEA,MAAM,UAAU,OAAmC;EACjD,YAAY,UAAU;EACtB,IAAI,IACF,aAAa;CAEjB;CAEA,OACE,oBAAC,UAAD;EACE,KAAK;EACL,UAAU;EACV,WAAW,GAAG,mBAAmB,YAAY,eAAe,SAAS;EACrE,GAAI;CACL;AAEL"}
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
4
  import { inputVariants } from "../Input/Input.mjs";
5
- import { useEffect, useImperativeHandle, useRef, useState } from "react";
5
+ import { useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/components/TextArea/ContentEditableTextArea.tsx
@@ -157,7 +157,7 @@ const useContentEditable = ({ value, defaultValue, onChange, disabled = false })
157
157
  sel.addRange(range);
158
158
  }
159
159
  };
160
- useEffect(() => {
160
+ useLayoutEffect(() => {
161
161
  if (pendingCaretRef.current && containerRef.current) {
162
162
  setCaretPosition(pendingCaretRef.current);
163
163
  pendingCaretRef.current = null;
@@ -382,7 +382,7 @@ const ContentEditableTextArea = ({ value, defaultValue, onChange, placeholder, d
382
382
  setCaretPosition(caretFromFlatOffset(offset, lines));
383
383
  }
384
384
  }));
385
- useEffect(() => {
385
+ useLayoutEffect(() => {
386
386
  if (!autoSize || !elRef.current) return;
387
387
  const el = elRef.current;
388
388
  const max = LINE_HEIGHT * maxRows + LINE_PADDING;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentEditableTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/ContentEditableTextArea.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { VariantProps } from 'class-variance-authority';\nimport {\n type ClipboardEvent,\n type DragEvent,\n type FC,\n type HTMLAttributes,\n type InputEvent,\n type KeyboardEvent,\n type MutableRefObject,\n type Ref,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react';\nimport { type InputVariant, inputVariants } from '../Input';\n\ntype CaretPosition = {\n line: number;\n offset: number;\n};\n\ntype UseContentEditableOptions = {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n disabled?: boolean;\n};\n\nconst ZERO_WIDTH_SPACE = '\\u200B';\n\nconst getTextFromContainer = (container: HTMLDivElement): string => {\n const lineEls = container.querySelectorAll('[data-line]');\n if (lineEls.length === 0) {\n return (container.textContent ?? '').split(ZERO_WIDTH_SPACE).join('');\n }\n\n return Array.from(lineEls)\n .map((el) => {\n const editable = el.querySelector('[data-editable]');\n const raw = editable?.textContent ?? el.textContent ?? '';\n return raw === ZERO_WIDTH_SPACE\n ? ''\n : raw.split(ZERO_WIDTH_SPACE).join('');\n })\n .join('\\n');\n};\n\nconst splitLines = (text: string): string[] => {\n const lines = text.split('\\n');\n return lines.length === 0 ? [''] : lines;\n};\n\n// Cached Intl.Segmenter for grapheme-aware deletion (emoji, CJK, etc.)\n// Intl.Segmenter is ES2022+ but we feature-detect at runtime.\ntype GraphemeSegmenter = {\n segment: (input: string) => Iterable<{ segment: string }>;\n};\n\nconst createGraphemeSegmenter = (): GraphemeSegmenter | null => {\n if (typeof Intl === 'undefined' || !('Segmenter' in Intl)) return null;\n const SegmenterCtor = (\n Intl as unknown as Record<\n string,\n new (\n ...args: unknown[]\n ) => GraphemeSegmenter\n >\n ).Segmenter;\n return new SegmenterCtor(undefined, { granularity: 'grapheme' });\n};\n\nconst graphemeSegmenter = createGraphemeSegmenter();\n\n/**\n * Find the previous grapheme cluster boundary for safe deletion.\n * Falls back to code-point-aware deletion if Intl.Segmenter is unavailable.\n */\nconst prevGraphemeBoundary = (text: string, offset: number): number => {\n if (offset <= 0) return 0;\n\n if (graphemeSegmenter) {\n const segments = [...graphemeSegmenter.segment(text.slice(0, offset))];\n const last = segments[segments.length - 1];\n return last ? offset - last.segment.length : offset - 1;\n }\n\n // Fallback: handle surrogate pairs\n const code = text.charCodeAt(offset - 1);\n if (code >= 0xdc00 && code <= 0xdfff && offset >= 2) {\n return offset - 2;\n }\n return offset - 1;\n};\n\n/**\n * Find the next grapheme cluster boundary for safe forward deletion.\n */\nconst nextGraphemeBoundary = (text: string, offset: number): number => {\n if (offset >= text.length) return text.length;\n\n if (graphemeSegmenter) {\n const segments = [...graphemeSegmenter.segment(text.slice(offset))];\n const first = segments[0];\n return first ? offset + first.segment.length : offset + 1;\n }\n\n // Fallback: handle surrogate pairs\n const code = text.charCodeAt(offset);\n if (code >= 0xd800 && code <= 0xdbff && offset + 1 < text.length) {\n return offset + 2;\n }\n return offset + 1;\n};\n\n/**\n * Find the previous word boundary for Option+Backspace.\n */\nconst prevWordBoundary = (text: string, offset: number): number => {\n if (offset <= 0) return 0;\n let i = offset - 1;\n // Skip whitespace\n while (i > 0 && /\\s/.test(text[i - 1])) i--;\n // Skip word characters\n while (i > 0 && /\\S/.test(text[i - 1])) i--;\n return i;\n};\n\n/**\n * Find the next word boundary for Option+Delete.\n */\nconst nextWordBoundary = (text: string, offset: number): number => {\n if (offset >= text.length) return text.length;\n let i = offset;\n // Skip word characters\n while (i < text.length && /\\S/.test(text[i])) i++;\n // Skip whitespace\n while (i < text.length && /\\s/.test(text[i])) i++;\n return i;\n};\n\n/**\n * Find the start of the current line (for Cmd+Backspace).\n */\nconst lineStart = (text: string, offset: number): number => {\n const before = text.slice(0, offset);\n const lastNewline = before.lastIndexOf('\\n');\n return lastNewline + 1;\n};\n\n/**\n * Find the end of the current line (for Cmd+Delete).\n */\nconst lineEnd = (text: string, offset: number): number => {\n const nextNewline = text.indexOf('\\n', offset);\n return nextNewline === -1 ? text.length : nextNewline;\n};\n\nexport const useContentEditable = ({\n value,\n defaultValue,\n onChange,\n disabled = false,\n}: UseContentEditableOptions) => {\n const initialValue = value ?? defaultValue ?? '';\n const [lines, setLines] = useState<string[]>(() => splitLines(initialValue));\n const containerRef = useRef<HTMLDivElement | null>(null);\n const pendingCaretRef = useRef<CaretPosition | null>(null);\n const isControlled = value !== undefined;\n\n // Keep a ref to the latest lines to avoid stale closures in rapid typing\n const linesRef = useRef(lines);\n linesRef.current = lines;\n\n useEffect(() => {\n if (isControlled && value !== undefined) {\n setLines(splitLines(value));\n }\n }, [value, isControlled]);\n\n const getText = () => linesRef.current.join('\\n');\n\n const getCaretPosition = (): CaretPosition | null => {\n const sel = window.getSelection();\n if (!sel?.rangeCount || !containerRef.current) return null;\n\n const range = sel.getRangeAt(0);\n const lineEls = containerRef.current.querySelectorAll('[data-line]');\n\n for (let i = 0; i < lineEls.length; i++) {\n if (lineEls[i].contains(range.startContainer)) {\n return { line: i, offset: range.startOffset };\n }\n }\n return null;\n };\n\n const getSelectionOffsets = (): {\n start: number;\n end: number;\n hasSelection: boolean;\n } | null => {\n const sel = window.getSelection();\n if (!sel?.rangeCount || !containerRef.current) return null;\n\n const range = sel.getRangeAt(0);\n const lineEls = containerRef.current.querySelectorAll('[data-line]');\n const currentLines = linesRef.current;\n\n const findOffset = (node: Node, nodeOffset: number): number => {\n for (let i = 0; i < lineEls.length; i++) {\n if (lineEls[i].contains(node)) {\n let flat = 0;\n for (let j = 0; j < i; j++) {\n flat += currentLines[j].length + 1;\n }\n return flat + Math.min(nodeOffset, currentLines[i]?.length ?? 0);\n }\n }\n // Selection is on the root container (e.g. select-all)\n if (node === containerRef.current) {\n if (nodeOffset === 0) return 0;\n return currentLines.join('\\n').length;\n }\n return 0;\n };\n\n const start = findOffset(range.startContainer, range.startOffset);\n const end = findOffset(range.endContainer, range.endOffset);\n\n return {\n start: Math.min(start, end),\n end: Math.max(start, end),\n hasSelection: !range.collapsed,\n };\n };\n\n const setCaretPosition = (pos: CaretPosition) => {\n if (!containerRef.current) return;\n\n const lineEls = containerRef.current.querySelectorAll('[data-line]');\n const lineEl = lineEls[pos.line];\n if (!lineEl) return;\n\n const editable = lineEl.querySelector('[data-editable]');\n const node =\n editable?.firstChild ?? editable ?? lineEl.firstChild ?? lineEl;\n\n const sel = window.getSelection();\n if (!sel) return;\n\n const range = document.createRange();\n const maxOff = Math.min(pos.offset, node.textContent?.length ?? 0);\n\n try {\n range.setStart(node, maxOff);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n } catch {\n range.selectNodeContents(node);\n range.collapse(false);\n sel.removeAllRanges();\n sel.addRange(range);\n }\n };\n\n useEffect(() => {\n if (pendingCaretRef.current && containerRef.current) {\n setCaretPosition(pendingCaretRef.current);\n pendingCaretRef.current = null;\n }\n });\n\n const flatOffsetFromCaret = (pos: CaretPosition): number => {\n const currentLines = linesRef.current;\n let offset = 0;\n for (let i = 0; i < pos.line; i++) {\n offset += currentLines[i].length + 1;\n }\n return offset + pos.offset;\n };\n\n const caretFromFlatOffset = (\n flat: number,\n targetLines: string[]\n ): CaretPosition => {\n let rem = flat;\n for (let i = 0; i < targetLines.length; i++) {\n if (rem <= targetLines[i].length) {\n return { line: i, offset: rem };\n }\n rem -= targetLines[i].length + 1;\n }\n return {\n line: targetLines.length - 1,\n offset: targetLines[targetLines.length - 1]?.length ?? 0,\n };\n };\n\n const getCursorOffset = (): number => {\n const pos = getCaretPosition();\n if (!pos) return 0;\n return flatOffsetFromCaret(pos);\n };\n\n /**\n * Applies a text mutation: computes new lines, sets pending caret, updates state.\n */\n const applyTextChange = (newText: string, caretOffset: number) => {\n const newLines = splitLines(newText);\n pendingCaretRef.current = caretFromFlatOffset(caretOffset, newLines);\n setLines(newLines);\n onChange?.(newText);\n };\n\n const handleInput = () => {\n if (pendingCaretRef.current !== null) return;\n if (disabled || !containerRef.current) return;\n\n const caretPos = getCaretPosition();\n const newText = getTextFromContainer(containerRef.current);\n const newLines = splitLines(newText);\n\n pendingCaretRef.current = caretPos;\n setLines(newLines);\n onChange?.(newText);\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n\n // Don't intercept during IME composition (CJK input)\n if (e.nativeEvent.isComposing) return;\n\n // Block undo/redo - browser would mutate DOM out of sync with React\n if ((e.metaKey || e.ctrlKey) && e.key === 'z') {\n e.preventDefault();\n return;\n }\n\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n\n if (e.key === 'Enter') {\n e.preventDefault();\n const newText =\n currentText.slice(0, selInfo.start) +\n '\\n' +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + 1);\n return;\n }\n\n if (e.key === 'Backspace') {\n e.preventDefault();\n\n if (selInfo.hasSelection) {\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n } else {\n if (selInfo.start === 0) return;\n\n let deleteFrom: number;\n if (e.metaKey) {\n // Cmd+Backspace: delete to start of line\n deleteFrom = lineStart(currentText, selInfo.start);\n } else if (e.altKey) {\n // Option+Backspace: delete previous word\n deleteFrom = prevWordBoundary(currentText, selInfo.start);\n } else {\n // Regular backspace: delete one grapheme\n deleteFrom = prevGraphemeBoundary(currentText, selInfo.start);\n }\n\n const newText =\n currentText.slice(0, deleteFrom) + currentText.slice(selInfo.start);\n applyTextChange(newText, deleteFrom);\n }\n return;\n }\n\n if (e.key === 'Delete') {\n e.preventDefault();\n\n if (selInfo.hasSelection) {\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n } else {\n if (selInfo.start >= currentText.length) return;\n\n let deleteTo: number;\n if (e.metaKey) {\n // Cmd+Delete: delete to end of line\n deleteTo = lineEnd(currentText, selInfo.start);\n } else if (e.altKey) {\n // Option+Delete: delete next word\n deleteTo = nextWordBoundary(currentText, selInfo.start);\n } else {\n // Regular delete: delete one grapheme\n deleteTo = nextGraphemeBoundary(currentText, selInfo.start);\n }\n\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(deleteTo);\n applyTextChange(newText, selInfo.start);\n }\n return;\n }\n };\n\n const handleCut = (e: ClipboardEvent<HTMLDivElement>) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const selInfo = getSelectionOffsets();\n if (!selInfo?.hasSelection) return;\n\n const currentText = linesRef.current.join('\\n');\n const selectedText = currentText.slice(selInfo.start, selInfo.end);\n\n // Write selected text to clipboard\n e.clipboardData.setData('text/plain', selectedText);\n\n // Delete the selected text\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n };\n\n const handlePaste = (e: ClipboardEvent<HTMLDivElement>) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const pastedText = e.clipboardData.getData('text/plain');\n if (!pastedText) return;\n\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n const newText =\n currentText.slice(0, selInfo.start) +\n pastedText +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + pastedText.length);\n };\n\n const handleBeforeInput = (e: InputEvent<HTMLDivElement>) => {\n if (disabled) return;\n\n const inputEvent = e.nativeEvent as InputEvent;\n\n // Don't intercept during IME composition (CJK input)\n if (inputEvent.isComposing) return;\n\n const inputType = inputEvent.inputType;\n\n // Skip types handled by handleKeyDown (when keydown fires)\n if (inputType === 'insertParagraph' || inputType === 'insertLineBreak') {\n return;\n }\n\n // Handle deletions as fallback for mobile keyboards that don't fire keydown\n if (\n inputType === 'deleteContentBackward' ||\n inputType === 'deleteContentForward'\n ) {\n e.preventDefault();\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n\n if (selInfo.hasSelection) {\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n } else if (inputType === 'deleteContentBackward') {\n if (selInfo.start === 0) return;\n const deleteFrom = prevGraphemeBoundary(currentText, selInfo.start);\n const newText =\n currentText.slice(0, deleteFrom) + currentText.slice(selInfo.start);\n applyTextChange(newText, deleteFrom);\n } else {\n if (selInfo.start >= currentText.length) return;\n const deleteTo = nextGraphemeBoundary(currentText, selInfo.start);\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(deleteTo);\n applyTextChange(newText, selInfo.start);\n }\n return;\n }\n\n // Handle spell-check replacements\n if (inputType === 'insertReplacementText') {\n e.preventDefault();\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n const replacement =\n inputEvent.data ?? inputEvent.dataTransfer?.getData('text/plain') ?? '';\n const newText =\n currentText.slice(0, selInfo.start) +\n replacement +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + replacement.length);\n return;\n }\n\n if (inputType === 'insertText' && inputEvent.data) {\n e.preventDefault();\n\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n const inserted = inputEvent.data;\n const newText =\n currentText.slice(0, selInfo.start) +\n inserted +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + inserted.length);\n }\n };\n\n const handleDrop = (e: DragEvent<HTMLDivElement>) => {\n // Block drag-and-drop to prevent uncontrolled DOM mutations\n e.preventDefault();\n };\n\n const handleDragOver = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n };\n\n return {\n lines,\n containerRef,\n getText,\n handleInput,\n handleBeforeInput,\n handleKeyDown,\n handleCut,\n handlePaste,\n handleDrop,\n handleDragOver,\n getCaretPosition,\n setCaretPosition,\n getCursorOffset,\n caretFromFlatOffset,\n };\n};\n\ntype LineProps = {\n index: number;\n text: string;\n isLast: boolean;\n ghostText?: string;\n};\n\nconst Line: FC<LineProps> = ({ index, text, isLast, ghostText }) => (\n <span data-line={index} className=\"block min-h-[1.5rem]\">\n <span data-editable>{text || '\\u200B'}</span>\n {ghostText && (\n <span\n data-ghost\n className=\"pointer-events-none select-none text-neutral\"\n aria-hidden=\"true\"\n >\n {ghostText}\n </span>\n )}\n {!isLast && <br />}\n </span>\n);\n\nexport type ContentEditableTextAreaHandle = {\n getContainer: () => HTMLDivElement | null;\n getText: () => string;\n focus: () => void;\n getCursorOffset: () => number;\n setCursorAtOffset: (offset: number) => void;\n};\n\nexport type ContentEditableTextAreaProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue'\n> & {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n placeholder?: string;\n disabled?: boolean;\n minRows?: number;\n maxRows?: number;\n autoSize?: boolean;\n validationStyleEnabled?: boolean;\n variant?: InputVariant | `${InputVariant}`;\n ghostText?: string;\n ghostLine?: number;\n ghostOffset?: number;\n ref?: Ref<ContentEditableTextAreaHandle>;\n dir?: 'ltr' | 'rtl' | 'auto';\n} & Omit<\n VariantProps<typeof inputVariants>,\n 'validationStyleEnabled' | 'variant'\n >;\n\nconst LINE_HEIGHT = 24;\nconst LINE_PADDING = 12;\n\nexport const ContentEditableTextArea: FC<ContentEditableTextAreaProps> = ({\n value,\n defaultValue,\n onChange,\n placeholder,\n disabled = false,\n minRows = 1,\n maxRows = 999,\n autoSize = true,\n validationStyleEnabled = false,\n variant,\n ghostText,\n ghostLine,\n ghostOffset,\n onClick,\n className,\n dir = 'auto',\n ref,\n ...rest\n}) => {\n const {\n lines,\n containerRef,\n getText,\n handleInput,\n handleBeforeInput,\n handleKeyDown,\n handleCut,\n handlePaste,\n handleDrop,\n handleDragOver,\n getCursorOffset,\n setCaretPosition,\n caretFromFlatOffset,\n } = useContentEditable({ value, defaultValue, onChange, disabled });\n\n const elRef = useRef<HTMLDivElement | null>(null);\n\n const setRef = (el: HTMLDivElement | null) => {\n elRef.current = el;\n (containerRef as MutableRefObject<HTMLDivElement | null>).current = el;\n };\n\n useImperativeHandle(ref, () => ({\n getContainer: () => elRef.current,\n getText,\n focus: () => elRef.current?.focus(),\n getCursorOffset,\n setCursorAtOffset: (offset: number) => {\n setCaretPosition(caretFromFlatOffset(offset, lines));\n },\n }));\n\n useEffect(() => {\n if (!autoSize || !elRef.current) return;\n\n const el = elRef.current;\n const max = LINE_HEIGHT * maxRows + LINE_PADDING;\n const min = LINE_HEIGHT * minRows + LINE_PADDING;\n\n el.style.height = 'auto';\n const sh = el.scrollHeight;\n el.style.height = `${Math.max(Math.min(sh, max), min)}px`;\n el.style.overflowY = sh > max ? 'auto' : 'hidden';\n }, [lines, autoSize, maxRows, minRows]);\n\n const isEmpty = lines.length === 1 && lines[0] === '';\n const hasGhost =\n ghostText && ghostLine !== undefined && ghostOffset !== undefined;\n\n return (\n <div className=\"relative w-full\">\n {isEmpty && placeholder && (\n <div\n className=\"pointer-events-none absolute inset-0 select-none px-2 py-3 text-base text-neutral-400 leading-[1.5rem] md:py-2 md:text-sm\"\n aria-hidden=\"true\"\n >\n {placeholder}\n </div>\n )}\n\n <div\n {...rest}\n ref={setRef}\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-placeholder={placeholder}\n aria-disabled={disabled}\n aria-autocomplete={hasGhost ? 'inline' : undefined}\n tabIndex={disabled ? -1 : 0}\n contentEditable={!disabled}\n suppressContentEditableWarning\n dir={dir}\n onInput={handleInput}\n onBeforeInput={handleBeforeInput}\n onKeyDown={handleKeyDown}\n onCut={handleCut}\n onPaste={handlePaste}\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onClick={onClick}\n className={cn(\n 'resize-none whitespace-pre-wrap break-words outline-none',\n inputVariants({\n variant,\n validationStyleEnabled: validationStyleEnabled\n ? 'enabled'\n : 'disabled',\n }),\n autoSize && 'overflow-y-auto',\n className\n )}\n >\n {lines.map((text, i) => (\n <Line\n key={i}\n index={i}\n text={text}\n isLast={i === lines.length - 1}\n ghostText={hasGhost && ghostLine === i ? ghostText : undefined}\n />\n ))}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContentEditableTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/ContentEditableTextArea.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { VariantProps } from 'class-variance-authority';\nimport {\n type ClipboardEvent,\n type DragEvent,\n type FC,\n type HTMLAttributes,\n type InputEvent,\n type KeyboardEvent,\n type MutableRefObject,\n type Ref,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { type InputVariant, inputVariants } from '../Input';\n\ntype CaretPosition = {\n line: number;\n offset: number;\n};\n\ntype UseContentEditableOptions = {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n disabled?: boolean;\n};\n\nconst ZERO_WIDTH_SPACE = '\\u200B';\n\nconst getTextFromContainer = (container: HTMLDivElement): string => {\n const lineEls = container.querySelectorAll('[data-line]');\n if (lineEls.length === 0) {\n return (container.textContent ?? '').split(ZERO_WIDTH_SPACE).join('');\n }\n\n return Array.from(lineEls)\n .map((el) => {\n const editable = el.querySelector('[data-editable]');\n const raw = editable?.textContent ?? el.textContent ?? '';\n return raw === ZERO_WIDTH_SPACE\n ? ''\n : raw.split(ZERO_WIDTH_SPACE).join('');\n })\n .join('\\n');\n};\n\nconst splitLines = (text: string): string[] => {\n const lines = text.split('\\n');\n return lines.length === 0 ? [''] : lines;\n};\n\n// Cached Intl.Segmenter for grapheme-aware deletion (emoji, CJK, etc.)\n// Intl.Segmenter is ES2022+ but we feature-detect at runtime.\ntype GraphemeSegmenter = {\n segment: (input: string) => Iterable<{ segment: string }>;\n};\n\nconst createGraphemeSegmenter = (): GraphemeSegmenter | null => {\n if (typeof Intl === 'undefined' || !('Segmenter' in Intl)) return null;\n const SegmenterCtor = (\n Intl as unknown as Record<\n string,\n new (\n ...args: unknown[]\n ) => GraphemeSegmenter\n >\n ).Segmenter;\n return new SegmenterCtor(undefined, { granularity: 'grapheme' });\n};\n\nconst graphemeSegmenter = createGraphemeSegmenter();\n\n/**\n * Find the previous grapheme cluster boundary for safe deletion.\n * Falls back to code-point-aware deletion if Intl.Segmenter is unavailable.\n */\nconst prevGraphemeBoundary = (text: string, offset: number): number => {\n if (offset <= 0) return 0;\n\n if (graphemeSegmenter) {\n const segments = [...graphemeSegmenter.segment(text.slice(0, offset))];\n const last = segments[segments.length - 1];\n return last ? offset - last.segment.length : offset - 1;\n }\n\n // Fallback: handle surrogate pairs\n const code = text.charCodeAt(offset - 1);\n if (code >= 0xdc00 && code <= 0xdfff && offset >= 2) {\n return offset - 2;\n }\n return offset - 1;\n};\n\n/**\n * Find the next grapheme cluster boundary for safe forward deletion.\n */\nconst nextGraphemeBoundary = (text: string, offset: number): number => {\n if (offset >= text.length) return text.length;\n\n if (graphemeSegmenter) {\n const segments = [...graphemeSegmenter.segment(text.slice(offset))];\n const first = segments[0];\n return first ? offset + first.segment.length : offset + 1;\n }\n\n // Fallback: handle surrogate pairs\n const code = text.charCodeAt(offset);\n if (code >= 0xd800 && code <= 0xdbff && offset + 1 < text.length) {\n return offset + 2;\n }\n return offset + 1;\n};\n\n/**\n * Find the previous word boundary for Option+Backspace.\n */\nconst prevWordBoundary = (text: string, offset: number): number => {\n if (offset <= 0) return 0;\n let i = offset - 1;\n // Skip whitespace\n while (i > 0 && /\\s/.test(text[i - 1])) i--;\n // Skip word characters\n while (i > 0 && /\\S/.test(text[i - 1])) i--;\n return i;\n};\n\n/**\n * Find the next word boundary for Option+Delete.\n */\nconst nextWordBoundary = (text: string, offset: number): number => {\n if (offset >= text.length) return text.length;\n let i = offset;\n // Skip word characters\n while (i < text.length && /\\S/.test(text[i])) i++;\n // Skip whitespace\n while (i < text.length && /\\s/.test(text[i])) i++;\n return i;\n};\n\n/**\n * Find the start of the current line (for Cmd+Backspace).\n */\nconst lineStart = (text: string, offset: number): number => {\n const before = text.slice(0, offset);\n const lastNewline = before.lastIndexOf('\\n');\n return lastNewline + 1;\n};\n\n/**\n * Find the end of the current line (for Cmd+Delete).\n */\nconst lineEnd = (text: string, offset: number): number => {\n const nextNewline = text.indexOf('\\n', offset);\n return nextNewline === -1 ? text.length : nextNewline;\n};\n\nexport const useContentEditable = ({\n value,\n defaultValue,\n onChange,\n disabled = false,\n}: UseContentEditableOptions) => {\n const initialValue = value ?? defaultValue ?? '';\n const [lines, setLines] = useState<string[]>(() => splitLines(initialValue));\n const containerRef = useRef<HTMLDivElement | null>(null);\n const pendingCaretRef = useRef<CaretPosition | null>(null);\n const isControlled = value !== undefined;\n\n // Keep a ref to the latest lines to avoid stale closures in rapid typing\n const linesRef = useRef(lines);\n linesRef.current = lines;\n\n useEffect(() => {\n if (isControlled && value !== undefined) {\n setLines(splitLines(value));\n }\n }, [value, isControlled]);\n\n const getText = () => linesRef.current.join('\\n');\n\n const getCaretPosition = (): CaretPosition | null => {\n const sel = window.getSelection();\n if (!sel?.rangeCount || !containerRef.current) return null;\n\n const range = sel.getRangeAt(0);\n const lineEls = containerRef.current.querySelectorAll('[data-line]');\n\n for (let i = 0; i < lineEls.length; i++) {\n if (lineEls[i].contains(range.startContainer)) {\n return { line: i, offset: range.startOffset };\n }\n }\n return null;\n };\n\n const getSelectionOffsets = (): {\n start: number;\n end: number;\n hasSelection: boolean;\n } | null => {\n const sel = window.getSelection();\n if (!sel?.rangeCount || !containerRef.current) return null;\n\n const range = sel.getRangeAt(0);\n const lineEls = containerRef.current.querySelectorAll('[data-line]');\n const currentLines = linesRef.current;\n\n const findOffset = (node: Node, nodeOffset: number): number => {\n for (let i = 0; i < lineEls.length; i++) {\n if (lineEls[i].contains(node)) {\n let flat = 0;\n for (let j = 0; j < i; j++) {\n flat += currentLines[j].length + 1;\n }\n return flat + Math.min(nodeOffset, currentLines[i]?.length ?? 0);\n }\n }\n // Selection is on the root container (e.g. select-all)\n if (node === containerRef.current) {\n if (nodeOffset === 0) return 0;\n return currentLines.join('\\n').length;\n }\n return 0;\n };\n\n const start = findOffset(range.startContainer, range.startOffset);\n const end = findOffset(range.endContainer, range.endOffset);\n\n return {\n start: Math.min(start, end),\n end: Math.max(start, end),\n hasSelection: !range.collapsed,\n };\n };\n\n const setCaretPosition = (pos: CaretPosition) => {\n if (!containerRef.current) return;\n\n const lineEls = containerRef.current.querySelectorAll('[data-line]');\n const lineEl = lineEls[pos.line];\n if (!lineEl) return;\n\n const editable = lineEl.querySelector('[data-editable]');\n const node =\n editable?.firstChild ?? editable ?? lineEl.firstChild ?? lineEl;\n\n const sel = window.getSelection();\n if (!sel) return;\n\n const range = document.createRange();\n const maxOff = Math.min(pos.offset, node.textContent?.length ?? 0);\n\n try {\n range.setStart(node, maxOff);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n } catch {\n range.selectNodeContents(node);\n range.collapse(false);\n sel.removeAllRanges();\n sel.addRange(range);\n }\n };\n\n useLayoutEffect(() => {\n if (pendingCaretRef.current && containerRef.current) {\n setCaretPosition(pendingCaretRef.current);\n pendingCaretRef.current = null;\n }\n });\n\n const flatOffsetFromCaret = (pos: CaretPosition): number => {\n const currentLines = linesRef.current;\n let offset = 0;\n for (let i = 0; i < pos.line; i++) {\n offset += currentLines[i].length + 1;\n }\n return offset + pos.offset;\n };\n\n const caretFromFlatOffset = (\n flat: number,\n targetLines: string[]\n ): CaretPosition => {\n let rem = flat;\n for (let i = 0; i < targetLines.length; i++) {\n if (rem <= targetLines[i].length) {\n return { line: i, offset: rem };\n }\n rem -= targetLines[i].length + 1;\n }\n return {\n line: targetLines.length - 1,\n offset: targetLines[targetLines.length - 1]?.length ?? 0,\n };\n };\n\n const getCursorOffset = (): number => {\n const pos = getCaretPosition();\n if (!pos) return 0;\n return flatOffsetFromCaret(pos);\n };\n\n /**\n * Applies a text mutation: computes new lines, sets pending caret, updates state.\n */\n const applyTextChange = (newText: string, caretOffset: number) => {\n const newLines = splitLines(newText);\n pendingCaretRef.current = caretFromFlatOffset(caretOffset, newLines);\n setLines(newLines);\n onChange?.(newText);\n };\n\n const handleInput = () => {\n if (pendingCaretRef.current !== null) return;\n if (disabled || !containerRef.current) return;\n\n const caretPos = getCaretPosition();\n const newText = getTextFromContainer(containerRef.current);\n const newLines = splitLines(newText);\n\n pendingCaretRef.current = caretPos;\n setLines(newLines);\n onChange?.(newText);\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n\n // Don't intercept during IME composition (CJK input)\n if (e.nativeEvent.isComposing) return;\n\n // Block undo/redo - browser would mutate DOM out of sync with React\n if ((e.metaKey || e.ctrlKey) && e.key === 'z') {\n e.preventDefault();\n return;\n }\n\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n\n if (e.key === 'Enter') {\n e.preventDefault();\n const newText =\n currentText.slice(0, selInfo.start) +\n '\\n' +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + 1);\n return;\n }\n\n if (e.key === 'Backspace') {\n e.preventDefault();\n\n if (selInfo.hasSelection) {\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n } else {\n if (selInfo.start === 0) return;\n\n let deleteFrom: number;\n if (e.metaKey) {\n // Cmd+Backspace: delete to start of line\n deleteFrom = lineStart(currentText, selInfo.start);\n } else if (e.altKey) {\n // Option+Backspace: delete previous word\n deleteFrom = prevWordBoundary(currentText, selInfo.start);\n } else {\n // Regular backspace: delete one grapheme\n deleteFrom = prevGraphemeBoundary(currentText, selInfo.start);\n }\n\n const newText =\n currentText.slice(0, deleteFrom) + currentText.slice(selInfo.start);\n applyTextChange(newText, deleteFrom);\n }\n return;\n }\n\n if (e.key === 'Delete') {\n e.preventDefault();\n\n if (selInfo.hasSelection) {\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n } else {\n if (selInfo.start >= currentText.length) return;\n\n let deleteTo: number;\n if (e.metaKey) {\n // Cmd+Delete: delete to end of line\n deleteTo = lineEnd(currentText, selInfo.start);\n } else if (e.altKey) {\n // Option+Delete: delete next word\n deleteTo = nextWordBoundary(currentText, selInfo.start);\n } else {\n // Regular delete: delete one grapheme\n deleteTo = nextGraphemeBoundary(currentText, selInfo.start);\n }\n\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(deleteTo);\n applyTextChange(newText, selInfo.start);\n }\n return;\n }\n };\n\n const handleCut = (e: ClipboardEvent<HTMLDivElement>) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const selInfo = getSelectionOffsets();\n if (!selInfo?.hasSelection) return;\n\n const currentText = linesRef.current.join('\\n');\n const selectedText = currentText.slice(selInfo.start, selInfo.end);\n\n // Write selected text to clipboard\n e.clipboardData.setData('text/plain', selectedText);\n\n // Delete the selected text\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n };\n\n const handlePaste = (e: ClipboardEvent<HTMLDivElement>) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const pastedText = e.clipboardData.getData('text/plain');\n if (!pastedText) return;\n\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n const newText =\n currentText.slice(0, selInfo.start) +\n pastedText +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + pastedText.length);\n };\n\n const handleBeforeInput = (e: InputEvent<HTMLDivElement>) => {\n if (disabled) return;\n\n const inputEvent = e.nativeEvent as InputEvent;\n\n // Don't intercept during IME composition (CJK input)\n if (inputEvent.isComposing) return;\n\n const inputType = inputEvent.inputType;\n\n // Skip types handled by handleKeyDown (when keydown fires)\n if (inputType === 'insertParagraph' || inputType === 'insertLineBreak') {\n return;\n }\n\n // Handle deletions as fallback for mobile keyboards that don't fire keydown\n if (\n inputType === 'deleteContentBackward' ||\n inputType === 'deleteContentForward'\n ) {\n e.preventDefault();\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n\n if (selInfo.hasSelection) {\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start);\n } else if (inputType === 'deleteContentBackward') {\n if (selInfo.start === 0) return;\n const deleteFrom = prevGraphemeBoundary(currentText, selInfo.start);\n const newText =\n currentText.slice(0, deleteFrom) + currentText.slice(selInfo.start);\n applyTextChange(newText, deleteFrom);\n } else {\n if (selInfo.start >= currentText.length) return;\n const deleteTo = nextGraphemeBoundary(currentText, selInfo.start);\n const newText =\n currentText.slice(0, selInfo.start) + currentText.slice(deleteTo);\n applyTextChange(newText, selInfo.start);\n }\n return;\n }\n\n // Handle spell-check replacements\n if (inputType === 'insertReplacementText') {\n e.preventDefault();\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n const replacement =\n inputEvent.data ?? inputEvent.dataTransfer?.getData('text/plain') ?? '';\n const newText =\n currentText.slice(0, selInfo.start) +\n replacement +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + replacement.length);\n return;\n }\n\n if (inputType === 'insertText' && inputEvent.data) {\n e.preventDefault();\n\n const selInfo = getSelectionOffsets();\n if (!selInfo) return;\n\n const currentText = linesRef.current.join('\\n');\n const inserted = inputEvent.data;\n const newText =\n currentText.slice(0, selInfo.start) +\n inserted +\n currentText.slice(selInfo.end);\n applyTextChange(newText, selInfo.start + inserted.length);\n }\n };\n\n const handleDrop = (e: DragEvent<HTMLDivElement>) => {\n // Block drag-and-drop to prevent uncontrolled DOM mutations\n e.preventDefault();\n };\n\n const handleDragOver = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n };\n\n return {\n lines,\n containerRef,\n getText,\n handleInput,\n handleBeforeInput,\n handleKeyDown,\n handleCut,\n handlePaste,\n handleDrop,\n handleDragOver,\n getCaretPosition,\n setCaretPosition,\n getCursorOffset,\n caretFromFlatOffset,\n };\n};\n\ntype LineProps = {\n index: number;\n text: string;\n isLast: boolean;\n ghostText?: string;\n};\n\nconst Line: FC<LineProps> = ({ index, text, isLast, ghostText }) => (\n <span data-line={index} className=\"block min-h-[1.5rem]\">\n <span data-editable>{text || '\\u200B'}</span>\n {ghostText && (\n <span\n data-ghost\n className=\"pointer-events-none select-none text-neutral\"\n aria-hidden=\"true\"\n >\n {ghostText}\n </span>\n )}\n {!isLast && <br />}\n </span>\n);\n\nexport type ContentEditableTextAreaHandle = {\n getContainer: () => HTMLDivElement | null;\n getText: () => string;\n focus: () => void;\n getCursorOffset: () => number;\n setCursorAtOffset: (offset: number) => void;\n};\n\nexport type ContentEditableTextAreaProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue'\n> & {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n placeholder?: string;\n disabled?: boolean;\n minRows?: number;\n maxRows?: number;\n autoSize?: boolean;\n validationStyleEnabled?: boolean;\n variant?: InputVariant | `${InputVariant}`;\n ghostText?: string;\n ghostLine?: number;\n ghostOffset?: number;\n ref?: Ref<ContentEditableTextAreaHandle>;\n dir?: 'ltr' | 'rtl' | 'auto';\n} & Omit<\n VariantProps<typeof inputVariants>,\n 'validationStyleEnabled' | 'variant'\n >;\n\nconst LINE_HEIGHT = 24;\nconst LINE_PADDING = 12;\n\nexport const ContentEditableTextArea: FC<ContentEditableTextAreaProps> = ({\n value,\n defaultValue,\n onChange,\n placeholder,\n disabled = false,\n minRows = 1,\n maxRows = 999,\n autoSize = true,\n validationStyleEnabled = false,\n variant,\n ghostText,\n ghostLine,\n ghostOffset,\n onClick,\n className,\n dir = 'auto',\n ref,\n ...rest\n}) => {\n const {\n lines,\n containerRef,\n getText,\n handleInput,\n handleBeforeInput,\n handleKeyDown,\n handleCut,\n handlePaste,\n handleDrop,\n handleDragOver,\n getCursorOffset,\n setCaretPosition,\n caretFromFlatOffset,\n } = useContentEditable({ value, defaultValue, onChange, disabled });\n\n const elRef = useRef<HTMLDivElement | null>(null);\n\n const setRef = (el: HTMLDivElement | null) => {\n elRef.current = el;\n (containerRef as MutableRefObject<HTMLDivElement | null>).current = el;\n };\n\n useImperativeHandle(ref, () => ({\n getContainer: () => elRef.current,\n getText,\n focus: () => elRef.current?.focus(),\n getCursorOffset,\n setCursorAtOffset: (offset: number) => {\n setCaretPosition(caretFromFlatOffset(offset, lines));\n },\n }));\n\n useLayoutEffect(() => {\n if (!autoSize || !elRef.current) return;\n\n const el = elRef.current;\n const max = LINE_HEIGHT * maxRows + LINE_PADDING;\n const min = LINE_HEIGHT * minRows + LINE_PADDING;\n\n el.style.height = 'auto';\n const sh = el.scrollHeight;\n el.style.height = `${Math.max(Math.min(sh, max), min)}px`;\n el.style.overflowY = sh > max ? 'auto' : 'hidden';\n }, [lines, autoSize, maxRows, minRows]);\n\n const isEmpty = lines.length === 1 && lines[0] === '';\n const hasGhost =\n ghostText && ghostLine !== undefined && ghostOffset !== undefined;\n\n return (\n <div className=\"relative w-full\">\n {isEmpty && placeholder && (\n <div\n className=\"pointer-events-none absolute inset-0 select-none px-2 py-3 text-base text-neutral-400 leading-[1.5rem] md:py-2 md:text-sm\"\n aria-hidden=\"true\"\n >\n {placeholder}\n </div>\n )}\n\n <div\n {...rest}\n ref={setRef}\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-placeholder={placeholder}\n aria-disabled={disabled}\n aria-autocomplete={hasGhost ? 'inline' : undefined}\n tabIndex={disabled ? -1 : 0}\n contentEditable={!disabled}\n suppressContentEditableWarning\n dir={dir}\n onInput={handleInput}\n onBeforeInput={handleBeforeInput}\n onKeyDown={handleKeyDown}\n onCut={handleCut}\n onPaste={handlePaste}\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onClick={onClick}\n className={cn(\n 'resize-none whitespace-pre-wrap break-words outline-none',\n inputVariants({\n variant,\n validationStyleEnabled: validationStyleEnabled\n ? 'enabled'\n : 'disabled',\n }),\n autoSize && 'overflow-y-auto',\n className\n )}\n >\n {lines.map((text, i) => (\n <Line\n key={i}\n index={i}\n text={text}\n isLast={i === lines.length - 1}\n ghostText={hasGhost && ghostLine === i ? ghostText : undefined}\n />\n ))}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -97,7 +97,7 @@ const Toast = ({ className, variant, ...props }) => {
97
97
  * ## Features
98
98
  * - **Accessibility**: Requires `altText` prop for screen readers
99
99
  * - **Visual States**: Hover, focus, and disabled state styling
100
- * - **Theme Integration**: Supports destructive and default themes
100
+ * - **Theme Integration**: Supports error and default themes
101
101
  * - **Keyboard Navigation**: Full keyboard accessibility support
102
102
  *
103
103
  * ## Usage Guidelines
@@ -125,11 +125,11 @@ const Toast = ({ className, variant, ...props }) => {
125
125
  * ```
126
126
  */
127
127
  const ToastAction = ({ className, ...props }) => /* @__PURE__ */ jsx(ToastPrimitives.Action, {
128
- className: cn("inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 font-medium text-sm transition-colors hover:bg-text focus:outline-hidden focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:focus:ring-destructive group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground", className),
128
+ className: cn("inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 font-medium text-sm transition-colors hover:bg-text focus:outline-hidden focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.error]:border-muted/40 group-[.error]:focus:ring-error group-[.error]:hover:border-error/30 group-[.error]:hover:bg-error group-[.error]:hover:text-error-foreground", className),
129
129
  ...props
130
130
  });
131
131
  const ToastClose = ({ className, ...props }) => /* @__PURE__ */ jsx(ToastPrimitives.Close, {
132
- className: cn("absolute top-1 right-1 rounded-md p-1 text-text/50 opacity-0 transition-opacity hover:text-text/80 focus:opacity-100 focus:outline-hidden focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600 group-[.destructive]:hover:text-red-50", className),
132
+ className: cn("absolute top-1 right-1 rounded-md p-1 text-text/50 opacity-0 transition-opacity hover:text-text/80 focus:opacity-100 focus:outline-hidden focus:ring-1 group-hover:opacity-100 group-[.error]:text-red-300 group-[.error]:focus:ring-red-400 group-[.error]:focus:ring-offset-red-600 group-[.error]:hover:text-red-50", className),
133
133
  "toast-close": "",
134
134
  ...props,
135
135
  children: /* @__PURE__ */ jsx(X, { className: "size-5" })