@intlayer/design-system 9.3.0 → 9.3.2

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 (185) hide show
  1. package/README.md +22 -22
  2. package/dist/esm/api/hooks/utils.mjs +2 -1
  3. package/dist/esm/api/hooks/utils.mjs.map +1 -1
  4. package/dist/esm/api/index.mjs +2 -2
  5. package/dist/esm/api/useAuth/useOAuth2.mjs +14 -4
  6. package/dist/esm/api/useAuth/useOAuth2.mjs.map +1 -1
  7. package/dist/esm/api/useAuth/useSession.mjs +7 -5
  8. package/dist/esm/api/useAuth/useSession.mjs.map +1 -1
  9. package/dist/esm/components/Accordion/Accordion.mjs +1 -1
  10. package/dist/esm/components/Avatar/index.mjs +1 -1
  11. package/dist/esm/components/Badge/index.mjs +1 -1
  12. package/dist/esm/components/Breadcrumb/index.mjs +1 -1
  13. package/dist/esm/components/Browser/Browser.mjs +1 -1
  14. package/dist/esm/components/Button/Button.mjs +1 -1
  15. package/dist/esm/components/Carousel/index.mjs +1 -1
  16. package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs +1 -1
  17. package/dist/esm/components/Command/index.mjs +1 -1
  18. package/dist/esm/components/Container/index.mjs +1 -1
  19. package/dist/esm/components/ContentEditor/ContentEditor.mjs +1 -1
  20. package/dist/esm/components/ContentEditor/ContentEditorInput.mjs +1 -1
  21. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +2 -2
  22. package/dist/esm/components/CopyButton/index.mjs +1 -1
  23. package/dist/esm/components/CopyToClipboard/index.mjs +1 -1
  24. package/dist/esm/components/DictionaryEditor/NodeWrapper/BooleanWrapper.mjs +2 -1
  25. package/dist/esm/components/DictionaryEditor/NodeWrapper/BooleanWrapper.mjs.map +1 -1
  26. package/dist/esm/components/DictionaryEditor/NodeWrapper/NumberWrapper.mjs +2 -1
  27. package/dist/esm/components/DictionaryEditor/NodeWrapper/NumberWrapper.mjs.map +1 -1
  28. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs +2 -2
  29. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +3 -3
  30. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
  31. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs +1 -1
  32. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs.map +1 -1
  33. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +7 -7
  34. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.mjs +1 -1
  35. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.mjs.map +1 -1
  36. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +2 -2
  37. package/dist/esm/components/DictionaryFieldEditor/KeyPathBreadcrumb.mjs +8 -7
  38. package/dist/esm/components/DictionaryFieldEditor/KeyPathBreadcrumb.mjs.map +1 -1
  39. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +8 -5
  40. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs.map +1 -1
  41. package/dist/esm/components/DictionaryFieldEditor/NodeTypeSelector.mjs +2 -1
  42. package/dist/esm/components/DictionaryFieldEditor/NodeTypeSelector.mjs.map +1 -1
  43. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -3
  44. package/dist/esm/components/DictionaryFieldEditor/StructureEditor.mjs +2 -1
  45. package/dist/esm/components/DictionaryFieldEditor/StructureEditor.mjs.map +1 -1
  46. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +2 -2
  47. package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs +1 -1
  48. package/dist/esm/components/EditableField/EditableFieldInput.mjs +6 -5
  49. package/dist/esm/components/EditableField/EditableFieldInput.mjs.map +1 -1
  50. package/dist/esm/components/EditableField/EditableFieldLayout.mjs +1 -1
  51. package/dist/esm/components/EditableField/EditableFieldTextArea.mjs +2 -1
  52. package/dist/esm/components/EditableField/EditableFieldTextArea.mjs.map +1 -1
  53. package/dist/esm/components/Form/FormBase.mjs.map +1 -1
  54. package/dist/esm/components/Form/elements/FormElementWrapper.mjs +1 -1
  55. package/dist/esm/components/Form/elements/FormElementWrapper.mjs.map +1 -1
  56. package/dist/esm/components/Form/elements/OTPElement.mjs +1 -1
  57. package/dist/esm/components/HTMLRender/HTMLRender.mjs +5 -3
  58. package/dist/esm/components/HTMLRender/HTMLRender.mjs.map +1 -1
  59. package/dist/esm/components/HideShow/index.mjs +1 -1
  60. package/dist/esm/components/IDE/Code.mjs.map +1 -1
  61. package/dist/esm/components/IDE/CodeBlockClient.mjs.map +1 -1
  62. package/dist/esm/components/IDE/CodeBlockHighlight.mjs +3 -14
  63. package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -1
  64. package/dist/esm/components/IDE/CodeBlockServer.mjs +14 -12
  65. package/dist/esm/components/IDE/CodeBlockServer.mjs.map +1 -1
  66. package/dist/esm/components/IDE/CodeBlockShiki.mjs +32 -86
  67. package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
  68. package/dist/esm/components/IDE/FileTree.mjs +3 -2
  69. package/dist/esm/components/IDE/FileTree.mjs.map +1 -1
  70. package/dist/esm/components/IDE/MarkDownRender.mjs.map +1 -1
  71. package/dist/esm/components/IDE/shikiLanguages.mjs +122 -0
  72. package/dist/esm/components/IDE/shikiLanguages.mjs.map +1 -0
  73. package/dist/esm/components/Input/Checkbox.mjs +1 -1
  74. package/dist/esm/components/Input/Input.mjs +1 -1
  75. package/dist/esm/components/Input/InputPassword.mjs +1 -1
  76. package/dist/esm/components/Input/OTPInput.mjs +5 -4
  77. package/dist/esm/components/Input/OTPInput.mjs.map +1 -1
  78. package/dist/esm/components/Input/Radio.mjs +1 -1
  79. package/dist/esm/components/Input/SearchInput.mjs +1 -1
  80. package/dist/esm/components/LanguageBackground/LanguageBackground.mjs +2 -1
  81. package/dist/esm/components/LanguageBackground/LanguageBackground.mjs.map +1 -1
  82. package/dist/esm/components/Link/Link.mjs +1 -1
  83. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +7 -6
  84. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs.map +1 -1
  85. package/dist/esm/components/LocaleSwitcherDropDown/LocaleSwitcher.mjs +1 -1
  86. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +1 -1
  87. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +12 -7
  88. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  89. package/dist/esm/components/MarkdownEditor/generative/AICompletionCommands.mjs +1 -1
  90. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs +1 -1
  91. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs +1 -1
  92. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs +1 -1
  93. package/dist/esm/components/MarkdownEditor/selectors/ColorSelector.mjs +1 -1
  94. package/dist/esm/components/MarkdownEditor/selectors/LinkSelector.mjs +1 -1
  95. package/dist/esm/components/MarkdownEditor/selectors/NodeSelector.mjs +1 -1
  96. package/dist/esm/components/MarkdownEditor/selectors/TextButtons.mjs +1 -1
  97. package/dist/esm/components/MarkdownEditor/slashCommand.mjs +1 -1
  98. package/dist/esm/components/Modal/Modal.mjs +3 -3
  99. package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
  100. package/dist/esm/components/Pagination/Pagination.mjs +2 -2
  101. package/dist/esm/components/PressableSpan/PressableSpan.mjs +0 -2
  102. package/dist/esm/components/PressableSpan/PressableSpan.mjs.map +1 -1
  103. package/dist/esm/components/RightDrawer/RightDrawer.mjs +4 -4
  104. package/dist/esm/components/Select/Multiselect.mjs +3 -5
  105. package/dist/esm/components/Select/Multiselect.mjs.map +1 -1
  106. package/dist/esm/components/Select/Select.mjs +1 -1
  107. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +1 -1
  108. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +1 -1
  109. package/dist/esm/components/Tab/Tab.mjs +2 -2
  110. package/dist/esm/components/TabSelector/TabSelector.mjs +1 -1
  111. package/dist/esm/components/Table/ExpandButton.mjs +1 -1
  112. package/dist/esm/components/Tag/index.mjs +1 -1
  113. package/dist/esm/components/Terminal/Terminal.mjs +7 -5
  114. package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
  115. package/dist/esm/components/TextArea/AutocompleteTextArea.mjs +6 -3
  116. package/dist/esm/components/TextArea/AutocompleteTextArea.mjs.map +1 -1
  117. package/dist/esm/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.mjs +1 -1
  118. package/dist/esm/components/ThemeSwitcherDropDown/MobileThemeSwitcher.mjs +1 -1
  119. package/dist/esm/components/Toaster/Toast.mjs +1 -1
  120. package/dist/esm/components/Toaster/useToast.mjs +1 -1
  121. package/dist/esm/components/Toaster/useToast.mjs.map +1 -1
  122. package/dist/esm/hooks/index.mjs +8 -8
  123. package/dist/esm/hooks/useScrollDetection.mjs +4 -2
  124. package/dist/esm/hooks/useScrollDetection.mjs.map +1 -1
  125. package/dist/esm/providers/DeferredAuthProvider.mjs +75 -0
  126. package/dist/esm/providers/DeferredAuthProvider.mjs.map +1 -0
  127. package/dist/esm/providers/ReactQueryProvider.mjs +4 -4
  128. package/dist/esm/providers/ReactQueryProvider.mjs.map +1 -1
  129. package/dist/esm/providers/index.mjs +2 -1
  130. package/dist/esm/routes.mjs +12 -11
  131. package/dist/esm/routes.mjs.map +1 -1
  132. package/dist/esm/structured-data/buildAuthorJsonLd.mjs +6 -3
  133. package/dist/esm/structured-data/buildAuthorJsonLd.mjs.map +1 -1
  134. package/dist/esm/structured-data/buildBreadcrumbsJsonLd.mjs +4 -1
  135. package/dist/esm/structured-data/buildBreadcrumbsJsonLd.mjs.map +1 -1
  136. package/dist/esm/structured-data/buildCreativeWorkJsonLd.mjs +7 -5
  137. package/dist/esm/structured-data/buildCreativeWorkJsonLd.mjs.map +1 -1
  138. package/dist/esm/structured-data/buildOrganizationJsonLd.mjs +27 -21
  139. package/dist/esm/structured-data/buildOrganizationJsonLd.mjs.map +1 -1
  140. package/dist/esm/structured-data/buildProductJsonLd.mjs +5 -2
  141. package/dist/esm/structured-data/buildProductJsonLd.mjs.map +1 -1
  142. package/dist/esm/structured-data/buildSoftwareApplicationJsonLd.mjs +59 -51
  143. package/dist/esm/structured-data/buildSoftwareApplicationJsonLd.mjs.map +1 -1
  144. package/dist/esm/structured-data/buildWebsiteJsonLd.mjs +31 -25
  145. package/dist/esm/structured-data/buildWebsiteJsonLd.mjs.map +1 -1
  146. package/dist/esm/structured-data/index.mjs +2 -1
  147. package/dist/esm/structured-data/normalizeJsonLdUrl.mjs +34 -0
  148. package/dist/esm/structured-data/normalizeJsonLdUrl.mjs.map +1 -0
  149. package/dist/esm/tailwind.config.mjs +4 -2
  150. package/dist/esm/tailwind.config.mjs.map +1 -1
  151. package/dist/types/api/index.d.ts +2 -2
  152. package/dist/types/api/useAuth/index.d.ts +2 -2
  153. package/dist/types/api/useAuth/useOAuth2.d.ts +12 -3
  154. package/dist/types/api/useAuth/useOAuth2.d.ts.map +1 -1
  155. package/dist/types/api/useAuth/useSession.d.ts.map +1 -1
  156. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.d.ts +1 -1
  157. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.d.ts +1 -1
  158. package/dist/types/components/Form/FormBase.d.ts +1 -1
  159. package/dist/types/components/Form/elements/FormElementWrapper.d.ts +1 -1
  160. package/dist/types/components/IDE/Code.d.ts +2 -2
  161. package/dist/types/components/IDE/CodeBlockClient.d.ts +2 -2
  162. package/dist/types/components/IDE/CodeBlockHighlight.d.ts +2 -2
  163. package/dist/types/components/IDE/CodeBlockHighlight.d.ts.map +1 -1
  164. package/dist/types/components/IDE/CodeBlockShiki.d.ts +2 -2
  165. package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
  166. package/dist/types/components/IDE/index.d.ts +2 -1
  167. package/dist/types/components/IDE/shikiLanguages.d.ts +138 -0
  168. package/dist/types/components/IDE/shikiLanguages.d.ts.map +1 -0
  169. package/dist/types/components/index.d.ts +2 -1
  170. package/dist/types/providers/DeferredAuthProvider.d.ts +33 -0
  171. package/dist/types/providers/DeferredAuthProvider.d.ts.map +1 -0
  172. package/dist/types/providers/index.d.ts +2 -1
  173. package/dist/types/routes.d.ts +17 -16
  174. package/dist/types/routes.d.ts.map +1 -1
  175. package/dist/types/structured-data/buildAuthorJsonLd.d.ts.map +1 -1
  176. package/dist/types/structured-data/buildBreadcrumbsJsonLd.d.ts.map +1 -1
  177. package/dist/types/structured-data/buildCreativeWorkJsonLd.d.ts.map +1 -1
  178. package/dist/types/structured-data/buildOrganizationJsonLd.d.ts.map +1 -1
  179. package/dist/types/structured-data/buildProductJsonLd.d.ts.map +1 -1
  180. package/dist/types/structured-data/buildSoftwareApplicationJsonLd.d.ts.map +1 -1
  181. package/dist/types/structured-data/buildWebsiteJsonLd.d.ts.map +1 -1
  182. package/dist/types/structured-data/index.d.ts +2 -1
  183. package/dist/types/structured-data/normalizeJsonLdUrl.d.ts +28 -0
  184. package/dist/types/structured-data/normalizeJsonLdUrl.d.ts.map +1 -0
  185. package/package.json +43 -43
@@ -1,15 +1,15 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { Button } from "../Button/Button.mjs";
5
- import { Container } from "../Container/index.mjs";
6
4
  import { useGetElementOrWindow } from "../../hooks/useGetElementOrWindow.mjs";
7
5
  import { useScrollBlockage } from "../../hooks/useScrollBlockage/index.mjs";
6
+ import { Button } from "../Button/Button.mjs";
7
+ import { Container } from "../Container/index.mjs";
8
8
  import { H3 } from "../Headers/index.mjs";
9
9
  import { useEffect } from "react";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
11
  import { cva } from "class-variance-authority";
11
12
  import { X } from "lucide-react";
12
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
13
  import { motion } from "framer-motion";
14
14
  import { createPortal } from "react-dom";
15
15
 
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { MaxHeightSmoother } from "../MaxHeightSmoother/index.mjs";
5
4
  import { useScrollBlockage } from "../../hooks/useScrollBlockage/index.mjs";
6
5
  import { useScrollDetection } from "../../hooks/useScrollDetection.mjs";
6
+ import { MaxHeightSmoother } from "../MaxHeightSmoother/index.mjs";
7
7
  import { Burger } from "./Burger.mjs";
8
8
  import { useRef, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { Button } from "../Button/Button.mjs";
5
4
  import { useItemSelector } from "../../hooks/useItemSelector.mjs";
5
+ import { Button } from "../Button/Button.mjs";
6
6
  import { useEffect, useRef } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
7
8
  import { cva } from "class-variance-authority";
8
9
  import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
9
- import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { useIntlayer } from "react-intlayer";
11
11
 
12
12
  //#region src/components/Pagination/Pagination.tsx
@@ -128,8 +128,6 @@ const PressableSpan = ({ children, onPress: onSelect, onClickOutside: onUnselect
128
128
  e.preventDefault();
129
129
  setIsSelectingState(false);
130
130
  onUnselect?.();
131
- break;
132
- default: break;
133
131
  }
134
132
  };
135
133
  const isCurrentlySelecting = isSelectingProp ?? isSelectingState;
@@ -1 +1 @@
1
- {"version":3,"file":"PressableSpan.mjs","names":[],"sources":["../../../../src/components/PressableSpan/PressableSpan.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n type FC,\n type HTMLAttributes,\n type MouseEventHandler,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nconst DEFAULT_PRESS_DETECT_DURATION = 400;\n\n/**\n * Props for the PressableSpan component\n */\ntype PressableSpanProps = {\n /**\n * Callback function triggered when a long press is detected\n * @example\n * ```tsx\n * <PressableSpan onPress={() => console.log('Long pressed!')}>\n * Press and hold me\n * </PressableSpan>\n * ```\n */\n onPress: () => void;\n\n /**\n * Optional callback function triggered when clicking outside the component while it's in selecting state\n * @example\n * ```tsx\n * <PressableSpan\n * onPress={() => setIsEditing(true)}\n * onClickOutside={() => setIsEditing(false)}\n * >\n * Click outside to deselect\n * </PressableSpan>\n * ```\n */\n onClickOutside?: () => void;\n\n /**\n * Duration in milliseconds for long press detection\n * @default 400\n * @example\n * ```tsx\n * <PressableSpan pressDuration={800} onPress={() => {}}>\n * Longer press required\n * </PressableSpan>\n * ```\n */\n pressDuration?: number;\n\n /**\n * External control for the selecting state\n * @example\n * ```tsx\n * <PressableSpan isSelecting={isEditing} onPress={() => {}}>\n * Externally controlled\n * </PressableSpan>\n * ```\n */\n isSelecting?: boolean;\n} & HTMLAttributes<HTMLSpanElement>;\n\n/**\n * PressableSpan - An interactive span element that responds to long press gestures\n *\n * A versatile component that detects long press interactions and provides visual feedback.\n * Commonly used for text editing interfaces, selection systems, and interactive content\n * that requires differentiation between quick clicks and intentional long presses.\n *\n * ## Key Features\n * - **Long Press Detection**: Configurable press duration for different interaction patterns\n * - **Visual Feedback**: Smooth outline transitions to indicate interactive and selected states\n * - **Click Outside Detection**: Automatically deselects when clicking outside the component\n * - **Touch Support**: Works seamlessly on both desktop and mobile devices\n * - **Accessible**: Keyboard navigation support and proper ARIA attributes\n *\n * ## Use Cases\n * - Text editing interfaces where long press activates edit mode\n * - Content selection systems with visual feedback\n * - Interactive cards or elements that need press-and-hold activation\n * - Mobile-friendly interfaces requiring long press gestures\n *\n * ## Accessibility\n * - Uses semantic `role=\"button\"` for proper screen reader announcement\n * - Keyboard navigable with `tabIndex={0}`\n * - Focus management with proper blur handling\n * - Visual outline indicators for focus and selection states\n *\n * @example\n * Basic usage with long press detection:\n * ```tsx\n * <PressableSpan onPress={() => setIsEditing(true)}>\n * Press and hold to edit this text\n * </PressableSpan>\n * ```\n *\n * @example\n * With custom press duration and click outside handling:\n * ```tsx\n * <PressableSpan\n * pressDuration={600}\n * onPress={() => setIsEditing(true)}\n * onClickOutside={() => setIsEditing(false)}\n * isSelecting={isEditing}\n * >\n * Custom behavior configuration\n * </PressableSpan>\n * ```\n *\n * @example\n * In a content editing context:\n * ```tsx\n * <PressableSpan\n * onPress={() => startEditingContent(contentId)}\n * onClickOutside={() => saveAndExitEditing()}\n * className=\"prose-text\"\n * >\n * {editableContent}\n * </PressableSpan>\n * ```\n */\n\nexport const PressableSpan: FC<PressableSpanProps> = ({\n children,\n onPress: onSelect,\n onClickOutside: onUnselect,\n pressDuration = DEFAULT_PRESS_DETECT_DURATION,\n isSelecting: isSelectingProp,\n ...props\n}) => {\n const elementRef = useRef<HTMLSpanElement>(null);\n const [isSelectingState, setIsSelectingState] = useState(isSelectingProp);\n const pressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const handleOnLongPress = () => {\n setIsSelectingState(true);\n onSelect();\n };\n\n const startPressTimer = () => {\n pressTimerRef.current = setTimeout(() => {\n handleOnLongPress();\n }, pressDuration);\n };\n\n const clearPressTimer = () => {\n if (pressTimerRef.current) {\n clearTimeout(pressTimerRef.current);\n pressTimerRef.current = null;\n }\n };\n\n const handleMouseDown = () => {\n clearPressTimer(); // Ensure any previous timer is cleared\n startPressTimer();\n };\n\n const handleMouseUp = () => {\n clearPressTimer();\n };\n\n // Use useCallback to ensure the function identity remains stable\n const handleClickOutside = useCallback(\n (event: MouseEvent) => {\n if (\n elementRef.current &&\n !elementRef.current.contains(event.target as Node)\n ) {\n setIsSelectingState(false);\n onUnselect?.();\n }\n },\n [onUnselect]\n );\n\n useEffect(() => {\n // Attach click outside listener\n document.addEventListener('mousedown', handleClickOutside);\n\n return () => {\n // Cleanup\n document.removeEventListener('mousedown', handleClickOutside);\n // clearPressTimer(); // Ensure to clear the timer when component unmounts\n };\n }, [handleClickOutside]);\n\n const handleOnClick: MouseEventHandler<HTMLDivElement> = (e) => {\n if (isSelectingState) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n\n const handleOnBlur = () => {\n // Stop editing when the element loses focus\n setIsSelectingState(false);\n };\n\n /**\n * Handle keyboard interactions\n * - Enter/Space: Trigger long press action immediately\n * - Escape: Cancel selection/editing state\n */\n const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {\n switch (e.key) {\n case 'Enter':\n case ' ':\n e.preventDefault();\n handleOnLongPress();\n break;\n case 'Escape':\n e.preventDefault();\n setIsSelectingState(false);\n onUnselect?.();\n break;\n default:\n break;\n }\n };\n\n const isCurrentlySelecting = isSelectingProp ?? isSelectingState;\n\n return (\n <span\n className={cn(\n 'inline cursor-pointer select-none rounded-md outline outline-2 outline-transparent outline-offset-4 transition-all delay-100 duration-200',\n isCurrentlySelecting ? 'outline-inherit' : 'hover:outline-inherit'\n )}\n role=\"button\"\n tabIndex={0}\n aria-pressed={isCurrentlySelecting ? 'true' : 'false'}\n aria-label={`${isCurrentlySelecting ? 'Selected' : 'Selectable'} content`}\n onKeyDown={handleKeyDown}\n onClick={handleOnClick}\n onMouseDown={handleMouseDown}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n onTouchStart={handleMouseDown}\n onTouchEnd={handleMouseUp}\n onTouchCancel={handleMouseUp}\n onBlur={handleOnBlur}\n ref={elementRef}\n {...props}\n >\n {children}\n </span>\n );\n};\n"],"mappings":";;;;;;;AAaA,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHtC,MAAa,iBAAyC,EACpD,UACA,SAAS,UACT,gBAAgB,YAChB,gBAAgB,+BAChB,aAAa,iBACb,GAAG,YACC;CACJ,MAAM,aAAa,OAAwB,IAAI;CAC/C,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,eAAe;CACxE,MAAM,gBAAgB,OAA6C,IAAI;CAEvE,MAAM,0BAA0B;EAC9B,oBAAoB,IAAI;EACxB,SAAS;CACX;CAEA,MAAM,wBAAwB;EAC5B,cAAc,UAAU,iBAAiB;GACvC,kBAAkB;EACpB,GAAG,aAAa;CAClB;CAEA,MAAM,wBAAwB;EAC5B,IAAI,cAAc,SAAS;GACzB,aAAa,cAAc,OAAO;GAClC,cAAc,UAAU;EAC1B;CACF;CAEA,MAAM,wBAAwB;EAC5B,gBAAgB;EAChB,gBAAgB;CAClB;CAEA,MAAM,sBAAsB;EAC1B,gBAAgB;CAClB;CAGA,MAAM,qBAAqB,aACxB,UAAsB;EACrB,IACE,WAAW,WACX,CAAC,WAAW,QAAQ,SAAS,MAAM,MAAc,GACjD;GACA,oBAAoB,KAAK;GACzB,aAAa;EACf;CACF,GACA,CAAC,UAAU,CACb;CAEA,gBAAgB;EAEd,SAAS,iBAAiB,aAAa,kBAAkB;EAEzD,aAAa;GAEX,SAAS,oBAAoB,aAAa,kBAAkB;EAE9D;CACF,GAAG,CAAC,kBAAkB,CAAC;CAEvB,MAAM,iBAAoD,MAAM;EAC9D,IAAI,kBAAkB;GACpB,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB;CACF;CAEA,MAAM,qBAAqB;EAEzB,oBAAoB,KAAK;CAC3B;;;;;;CAOA,MAAM,iBAAiB,MAA4C;EACjE,QAAQ,EAAE,KAAV;GACE,KAAK;GACL,KAAK;IACH,EAAE,eAAe;IACjB,kBAAkB;IAClB;GACF,KAAK;IACH,EAAE,eAAe;IACjB,oBAAoB,KAAK;IACzB,aAAa;IACb;GACF,SACE;EACJ;CACF;CAEA,MAAM,uBAAuB,mBAAmB;CAEhD,OACE,oBAAC,QAAD;EACE,WAAW,GACT,6IACA,uBAAuB,oBAAoB,uBAC7C;EACA,MAAK;EACL,UAAU;EACV,gBAAc,uBAAuB,SAAS;EAC9C,cAAY,GAAG,uBAAuB,aAAa,aAAa;EAChE,WAAW;EACX,SAAS;EACT,aAAa;EACb,WAAW;EACX,cAAc;EACd,cAAc;EACd,YAAY;EACZ,eAAe;EACf,QAAQ;EACR,KAAK;EACL,GAAI;EAEH;CACG;AAEV"}
1
+ {"version":3,"file":"PressableSpan.mjs","names":[],"sources":["../../../../src/components/PressableSpan/PressableSpan.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n type FC,\n type HTMLAttributes,\n type MouseEventHandler,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nconst DEFAULT_PRESS_DETECT_DURATION = 400;\n\n/**\n * Props for the PressableSpan component\n */\ntype PressableSpanProps = {\n /**\n * Callback function triggered when a long press is detected\n * @example\n * ```tsx\n * <PressableSpan onPress={() => console.log('Long pressed!')}>\n * Press and hold me\n * </PressableSpan>\n * ```\n */\n onPress: () => void;\n\n /**\n * Optional callback function triggered when clicking outside the component while it's in selecting state\n * @example\n * ```tsx\n * <PressableSpan\n * onPress={() => setIsEditing(true)}\n * onClickOutside={() => setIsEditing(false)}\n * >\n * Click outside to deselect\n * </PressableSpan>\n * ```\n */\n onClickOutside?: () => void;\n\n /**\n * Duration in milliseconds for long press detection\n * @default 400\n * @example\n * ```tsx\n * <PressableSpan pressDuration={800} onPress={() => {}}>\n * Longer press required\n * </PressableSpan>\n * ```\n */\n pressDuration?: number;\n\n /**\n * External control for the selecting state\n * @example\n * ```tsx\n * <PressableSpan isSelecting={isEditing} onPress={() => {}}>\n * Externally controlled\n * </PressableSpan>\n * ```\n */\n isSelecting?: boolean;\n} & HTMLAttributes<HTMLSpanElement>;\n\n/**\n * PressableSpan - An interactive span element that responds to long press gestures\n *\n * A versatile component that detects long press interactions and provides visual feedback.\n * Commonly used for text editing interfaces, selection systems, and interactive content\n * that requires differentiation between quick clicks and intentional long presses.\n *\n * ## Key Features\n * - **Long Press Detection**: Configurable press duration for different interaction patterns\n * - **Visual Feedback**: Smooth outline transitions to indicate interactive and selected states\n * - **Click Outside Detection**: Automatically deselects when clicking outside the component\n * - **Touch Support**: Works seamlessly on both desktop and mobile devices\n * - **Accessible**: Keyboard navigation support and proper ARIA attributes\n *\n * ## Use Cases\n * - Text editing interfaces where long press activates edit mode\n * - Content selection systems with visual feedback\n * - Interactive cards or elements that need press-and-hold activation\n * - Mobile-friendly interfaces requiring long press gestures\n *\n * ## Accessibility\n * - Uses semantic `role=\"button\"` for proper screen reader announcement\n * - Keyboard navigable with `tabIndex={0}`\n * - Focus management with proper blur handling\n * - Visual outline indicators for focus and selection states\n *\n * @example\n * Basic usage with long press detection:\n * ```tsx\n * <PressableSpan onPress={() => setIsEditing(true)}>\n * Press and hold to edit this text\n * </PressableSpan>\n * ```\n *\n * @example\n * With custom press duration and click outside handling:\n * ```tsx\n * <PressableSpan\n * pressDuration={600}\n * onPress={() => setIsEditing(true)}\n * onClickOutside={() => setIsEditing(false)}\n * isSelecting={isEditing}\n * >\n * Custom behavior configuration\n * </PressableSpan>\n * ```\n *\n * @example\n * In a content editing context:\n * ```tsx\n * <PressableSpan\n * onPress={() => startEditingContent(contentId)}\n * onClickOutside={() => saveAndExitEditing()}\n * className=\"prose-text\"\n * >\n * {editableContent}\n * </PressableSpan>\n * ```\n */\n\nexport const PressableSpan: FC<PressableSpanProps> = ({\n children,\n onPress: onSelect,\n onClickOutside: onUnselect,\n pressDuration = DEFAULT_PRESS_DETECT_DURATION,\n isSelecting: isSelectingProp,\n ...props\n}) => {\n const elementRef = useRef<HTMLSpanElement>(null);\n const [isSelectingState, setIsSelectingState] = useState(isSelectingProp);\n const pressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const handleOnLongPress = () => {\n setIsSelectingState(true);\n onSelect();\n };\n\n const startPressTimer = () => {\n pressTimerRef.current = setTimeout(() => {\n handleOnLongPress();\n }, pressDuration);\n };\n\n const clearPressTimer = () => {\n if (pressTimerRef.current) {\n clearTimeout(pressTimerRef.current);\n pressTimerRef.current = null;\n }\n };\n\n const handleMouseDown = () => {\n clearPressTimer(); // Ensure any previous timer is cleared\n startPressTimer();\n };\n\n const handleMouseUp = () => {\n clearPressTimer();\n };\n\n // Use useCallback to ensure the function identity remains stable\n const handleClickOutside = useCallback(\n (event: MouseEvent) => {\n if (\n elementRef.current &&\n !elementRef.current.contains(event.target as Node)\n ) {\n setIsSelectingState(false);\n onUnselect?.();\n }\n },\n [onUnselect]\n );\n\n useEffect(() => {\n // Attach click outside listener\n document.addEventListener('mousedown', handleClickOutside);\n\n return () => {\n // Cleanup\n document.removeEventListener('mousedown', handleClickOutside);\n // clearPressTimer(); // Ensure to clear the timer when component unmounts\n };\n }, [handleClickOutside]);\n\n const handleOnClick: MouseEventHandler<HTMLDivElement> = (e) => {\n if (isSelectingState) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n\n const handleOnBlur = () => {\n // Stop editing when the element loses focus\n setIsSelectingState(false);\n };\n\n /**\n * Handle keyboard interactions\n * - Enter/Space: Trigger long press action immediately\n * - Escape: Cancel selection/editing state\n */\n const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {\n switch (e.key) {\n case 'Enter':\n case ' ':\n e.preventDefault();\n handleOnLongPress();\n break;\n case 'Escape':\n e.preventDefault();\n setIsSelectingState(false);\n onUnselect?.();\n break;\n default:\n break;\n }\n };\n\n const isCurrentlySelecting = isSelectingProp ?? isSelectingState;\n\n return (\n <span\n className={cn(\n 'inline cursor-pointer select-none rounded-md outline outline-2 outline-transparent outline-offset-4 transition-all delay-100 duration-200',\n isCurrentlySelecting ? 'outline-inherit' : 'hover:outline-inherit'\n )}\n role=\"button\"\n tabIndex={0}\n aria-pressed={isCurrentlySelecting ? 'true' : 'false'}\n aria-label={`${isCurrentlySelecting ? 'Selected' : 'Selectable'} content`}\n onKeyDown={handleKeyDown}\n onClick={handleOnClick}\n onMouseDown={handleMouseDown}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n onTouchStart={handleMouseDown}\n onTouchEnd={handleMouseUp}\n onTouchCancel={handleMouseUp}\n onBlur={handleOnBlur}\n ref={elementRef}\n {...props}\n >\n {children}\n </span>\n );\n};\n"],"mappings":";;;;;;;AAaA,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHtC,MAAa,iBAAyC,EACpD,UACA,SAAS,UACT,gBAAgB,YAChB,gBAAgB,+BAChB,aAAa,iBACb,GAAG,YACC;CACJ,MAAM,aAAa,OAAwB,IAAI;CAC/C,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,eAAe;CACxE,MAAM,gBAAgB,OAA6C,IAAI;CAEvE,MAAM,0BAA0B;EAC9B,oBAAoB,IAAI;EACxB,SAAS;CACX;CAEA,MAAM,wBAAwB;EAC5B,cAAc,UAAU,iBAAiB;GACvC,kBAAkB;EACpB,GAAG,aAAa;CAClB;CAEA,MAAM,wBAAwB;EAC5B,IAAI,cAAc,SAAS;GACzB,aAAa,cAAc,OAAO;GAClC,cAAc,UAAU;EAC1B;CACF;CAEA,MAAM,wBAAwB;EAC5B,gBAAgB;EAChB,gBAAgB;CAClB;CAEA,MAAM,sBAAsB;EAC1B,gBAAgB;CAClB;CAGA,MAAM,qBAAqB,aACxB,UAAsB;EACrB,IACE,WAAW,WACX,CAAC,WAAW,QAAQ,SAAS,MAAM,MAAc,GACjD;GACA,oBAAoB,KAAK;GACzB,aAAa;EACf;CACF,GACA,CAAC,UAAU,CACb;CAEA,gBAAgB;EAEd,SAAS,iBAAiB,aAAa,kBAAkB;EAEzD,aAAa;GAEX,SAAS,oBAAoB,aAAa,kBAAkB;EAE9D;CACF,GAAG,CAAC,kBAAkB,CAAC;CAEvB,MAAM,iBAAoD,MAAM;EAC9D,IAAI,kBAAkB;GACpB,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB;CACF;CAEA,MAAM,qBAAqB;EAEzB,oBAAoB,KAAK;CAC3B;;;;;;CAOA,MAAM,iBAAiB,MAA4C;EACjE,QAAQ,EAAE,KAAV;GACE,KAAK;GACL,KAAK;IACH,EAAE,eAAe;IACjB,kBAAkB;IAClB;GACF,KAAK;IACH,EAAE,eAAe;IACjB,oBAAoB,KAAK;IACzB,aAAa;EAIjB;CACF;CAEA,MAAM,uBAAuB,mBAAmB;CAEhD,OACE,oBAAC,QAAD;EACE,WAAW,GACT,6IACA,uBAAuB,oBAAoB,uBAC7C;EACA,MAAK;EACL,UAAU;EACV,gBAAc,uBAAuB,SAAS;EAC9C,cAAY,GAAG,uBAAuB,aAAa,aAAa;EAChE,WAAW;EACX,SAAS;EACT,aAAa;EACb,WAAW;EACX,cAAc;EACd,cAAc;EACd,YAAY;EACZ,eAAe;EACf,QAAQ;EACR,KAAK;EACL,GAAI;EAEH;CACG;AAEV"}
@@ -1,18 +1,18 @@
1
1
  'use client';
2
2
 
3
+ import { useGetElementOrWindow } from "../../hooks/useGetElementOrWindow.mjs";
4
+ import { useDevice } from "../../hooks/useDevice.mjs";
5
+ import { useScrollBlockage } from "../../hooks/useScrollBlockage/index.mjs";
3
6
  import { Button } from "../Button/Button.mjs";
4
7
  import { Container } from "../Container/index.mjs";
5
- import { useDevice } from "../../hooks/useDevice.mjs";
6
8
  import { KeyboardShortcut } from "../KeyboardShortcut/KeyboardShortcut.mjs";
7
9
  import { Popover } from "../Popover/dynamic.mjs";
8
- import { useGetElementOrWindow } from "../../hooks/useGetElementOrWindow.mjs";
9
- import { useScrollBlockage } from "../../hooks/useScrollBlockage/index.mjs";
10
10
  import { MaxWidthSmoother } from "../MaxWidthSmoother/index.mjs";
11
11
  import { isElementAtTopAndNotCovered } from "./isElementAtTopAndNotCovered.mjs";
12
12
  import { useRightDrawer } from "./useRightDrawer.mjs";
13
13
  import { useEffect, useRef } from "react";
14
- import { ChevronLeft, X } from "lucide-react";
15
14
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
15
+ import { ChevronLeft, X } from "lucide-react";
16
16
  import { useIntlayer } from "react-intlayer";
17
17
  import { createPortal } from "react-dom";
18
18
 
@@ -4,8 +4,8 @@ import { cn } from "../../utils/cn.mjs";
4
4
  import { Badge } from "../Badge/index.mjs";
5
5
  import { Command, CommandRoot } from "../Command/index.mjs";
6
6
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
7
- import { Check, X } from "lucide-react";
8
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { Check, X } from "lucide-react";
9
9
 
10
10
  //#region src/components/Select/Multiselect.tsx
11
11
  const MultiSelectContext = createContext(null);
@@ -195,10 +195,8 @@ const MultiSelectRoot = ({ values: valuesProp, defaultValues, onValueChange, loo
195
195
  case "Enter":
196
196
  setOpen(true);
197
197
  break;
198
- case "Escape":
199
- if (activeIndex !== -1) setActiveIndex(-1);
200
- else if (open) setOpen(false);
201
- break;
198
+ case "Escape": if (activeIndex !== -1) setActiveIndex(-1);
199
+ else if (open) setOpen(false);
202
200
  }
203
201
  }, [
204
202
  value,
@@ -1 +1 @@
1
- {"version":3,"file":"Multiselect.mjs","names":["RemoveIcon"],"sources":["../../../../src/components/Select/Multiselect.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { Check, X as RemoveIcon } from 'lucide-react';\nimport {\n type ComponentProps,\n createContext,\n type Dispatch,\n type FC,\n type HTMLAttributes,\n type KeyboardEvent,\n type LegacyRef,\n type MouseEventHandler,\n type RefObject,\n type SetStateAction,\n type SyntheticEvent,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { Badge } from '../Badge';\nimport { Command, CommandRoot } from '../Command';\n\n/**\n * Context properties for MultiSelect component state management\n *\n * @interface MultiSelectContextProps\n */\ntype MultiSelectContextProps = {\n /** Array of currently selected values */\n value: string[];\n /** Handler for value changes */\n onValueChange: (value: string) => void;\n /** Whether the dropdown is currently open */\n open: boolean;\n /** Function to set the open state */\n setOpen: (value: boolean) => void;\n /** Current input field value for filtering */\n inputValue: string;\n /** Function to set the input value */\n setInputValue: Dispatch<SetStateAction<string>>;\n /** Index of currently focused option for keyboard navigation */\n activeIndex: number;\n /** Function to set the active index */\n setActiveIndex: Dispatch<SetStateAction<number>>;\n /** Ref to the input element */\n ref: RefObject<HTMLInputElement | null>;\n /** Handler for option selection */\n handleSelect: (e: SyntheticEvent<HTMLInputElement>) => void;\n};\n\nconst MultiSelectContext = createContext<MultiSelectContextProps | null>(null);\n\n/**\n * Custom hook to access MultiSelect context\n *\n * Provides access to the internal state and methods of the MultiSelect component.\n * Must be used within a MultiSelect component tree.\n *\n * @returns MultiSelectContextProps - All context properties and methods\n * @throws Error when used outside of MultiSelect component\n *\n * @example\n * ```tsx\n * function CustomMultiSelectItem() {\n * const { value, onValueChange, open } = useMultiSelect();\n * // Use context properties...\n * }\n * ```\n */\nconst useMultiSelect = () => {\n const context = useContext(MultiSelectContext);\n if (!context) {\n throw new Error('useMultiSelect must be used within MultiSelectProvider');\n }\n return context;\n};\n\n/**\n * Props interface for the main MultiSelect component\n *\n * @interface MultiSelectProps\n */\ntype MultiSelectProps = ComponentProps<typeof CommandRoot> & {\n /**\n * Array of selected values (controlled mode)\n * @example\n * ```tsx\n * const [selected, setSelected] = useState(['react', 'vue']);\n * <MultiSelect values={selected} onValueChange={setSelected} />\n * ```\n */\n values?: string[];\n\n /**\n * Default selected values for uncontrolled mode\n * @example\n * ```tsx\n * <MultiSelect defaultValues={['react']} />\n * ```\n */\n defaultValues?: string[];\n\n /**\n * Callback fired when selection changes\n * @param value - New array of selected values\n * @example\n * ```tsx\n * <MultiSelect onValueChange={(values) => console.log('Selected:', values)} />\n * ```\n */\n onValueChange?: (value: string[]) => void;\n\n /**\n * Whether keyboard navigation should loop through options\n * @default false\n * @example\n * ```tsx\n * <MultiSelect loop /> // Arrow keys wrap around at list boundaries\n * ```\n */\n loop?: boolean;\n};\n\n/**\n * MultiSelect - A comprehensive multi-selection dropdown component\n *\n * An advanced multi-select component that combines the functionality of a searchable dropdown\n * with the ability to select multiple values. Built on top of Command component primitives,\n * it provides filtering, keyboard navigation, and visual feedback through badges.\n *\n * ## Key Features\n * - **Multi-Selection**: Select multiple options with visual badge representation\n * - **Searchable**: Built-in filtering to quickly find options in large lists\n * - **Keyboard Navigation**: Full arrow key navigation with optional looping\n * - **Accessibility**: Screen reader support, ARIA attributes, and focus management\n * - **Flexible State**: Both controlled and uncontrolled usage patterns\n * - **Rich UI**: Customizable badges, icons, and content layout\n *\n * ## Use Cases\n * - Tag/category selection in forms\n * - Multi-user assignment interfaces\n * - Feature/permission selection\n * - Filter selection in search interfaces\n * - Any multi-choice selection requirement\n *\n * ## Architecture\n * The component follows a compound pattern similar to Select:\n * - `MultiSelect` (root): Manages state and provides context\n * - `MultiSelect.Trigger`: Container for input and selected badges\n * - `MultiSelect.Input`: Searchable input field with filtering\n * - `MultiSelect.Content`: Dropdown container for options\n * - `MultiSelect.List`: Options container with keyboard navigation\n * - `MultiSelect.Item`: Individual selectable options\n *\n * ## Accessibility\n * - **Keyboard Navigation**: Arrow keys, Enter to select, Backspace to remove\n * - **Screen Readers**: Proper ARIA labels and live region announcements\n * - **Focus Management**: Clear focus indicators and logical tab flow\n * - **Search**: Real-time filtering with screen reader announcements\n *\n * @example\n * Basic multi-select usage:\n * ```tsx\n * const [frameworks, setFrameworks] = useState<string[]>([]);\n *\n * <MultiSelect values={frameworks} onValueChange={setFrameworks}>\n * <MultiSelect.Trigger>\n * <MultiSelect.Input placeholder=\"Select frameworks...\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"react\">React</MultiSelect.Item>\n * <MultiSelect.Item value=\"vue\">Vue</MultiSelect.Item>\n * <MultiSelect.Item value=\"svelte\">Svelte</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * ```\n *\n * @example\n * Advanced usage with keyboard looping:\n * ```tsx\n * <MultiSelect defaultValues={['react']} loop>\n * <MultiSelect.Trigger>\n * <MultiSelect.Input placeholder=\"Choose technologies...\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"react\">⚛️ React</MultiSelect.Item>\n * <MultiSelect.Item value=\"vue\">💚 Vue</MultiSelect.Item>\n * <MultiSelect.Item value=\"angular\">🔴 Angular</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * ```\n *\n * @example\n * Form integration with validation:\n * ```tsx\n * <form>\n * <MultiSelect\n * values={selectedSkills}\n * onValueChange={setSelectedSkills}\n * required\n * >\n * <MultiSelect.Trigger className=\"min-h-[2.5rem]\">\n * <MultiSelect.Input placeholder=\"Select your skills...\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"javascript\">JavaScript</MultiSelect.Item>\n * <MultiSelect.Item value=\"typescript\">TypeScript</MultiSelect.Item>\n * <MultiSelect.Item value=\"python\">Python</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * </form>\n * ```\n */\nconst MultiSelectRoot: FC<MultiSelectProps> = ({\n values: valuesProp,\n defaultValues,\n onValueChange,\n loop = false,\n className,\n children,\n dir,\n ...props\n}) => {\n const [value, setValue] = useState<string[]>(defaultValues ?? []);\n const [inputValue, setInputValue] = useState('');\n const [open, setOpen] = useState<boolean>(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const inputRef = useRef<HTMLInputElement>(null);\n const [isValueSelected, setIsValueSelected] = useState(false);\n const [selectedValue, setSelectedValue] = useState('');\n\n useEffect(() => {\n if (valuesProp) {\n setValue(valuesProp);\n }\n }, [valuesProp]);\n\n const onValueChangeHandler = useCallback(\n (val: string) => {\n if (value.includes(val)) {\n const newValue = value.filter((item) => item !== val);\n setValue(newValue);\n onValueChange?.(newValue);\n } else {\n const newValue = [...value, val];\n setValue(newValue);\n onValueChange?.(newValue);\n }\n },\n\n [value]\n );\n\n const handleSelect = useCallback(\n (e: SyntheticEvent<HTMLInputElement>) => {\n e.preventDefault();\n const target = e.currentTarget;\n const selection = target.value.substring(\n target.selectionStart ?? 0,\n target.selectionEnd ?? 0\n );\n\n setSelectedValue(selection);\n setIsValueSelected(selection === inputValue);\n },\n [inputValue]\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n e.stopPropagation();\n const target = inputRef.current;\n\n if (!target) return;\n\n const moveNext = () => {\n const nextIndex = activeIndex + 1;\n setActiveIndex(\n nextIndex > value.length - 1 ? (loop ? 0 : -1) : nextIndex\n );\n };\n\n const movePrev = () => {\n const prevIndex = activeIndex - 1;\n setActiveIndex(prevIndex < 0 ? value.length - 1 : prevIndex);\n };\n\n const moveCurrent = () => {\n const newIndex =\n activeIndex - 1 <= 0\n ? value.length - 1 === 0\n ? -1\n : 0\n : activeIndex - 1;\n setActiveIndex(newIndex);\n };\n\n switch (e.key) {\n case 'ArrowLeft':\n if (dir === 'rtl') {\n if (value.length > 0 && (activeIndex !== -1 || loop)) {\n moveNext();\n }\n } else if (value.length > 0 && target.selectionStart === 0) {\n movePrev();\n }\n break;\n\n case 'ArrowRight':\n if (dir === 'rtl') {\n if (value.length > 0 && target.selectionStart === 0) {\n movePrev();\n }\n } else if (value.length > 0 && (activeIndex !== -1 || loop)) {\n moveNext();\n }\n break;\n\n case 'Backspace':\n case 'Delete':\n if (value.length > 0) {\n if (activeIndex !== -1 && activeIndex < value.length) {\n onValueChangeHandler(value[activeIndex]);\n moveCurrent();\n } else if (\n (target.selectionStart === 0 && selectedValue === inputValue) ||\n isValueSelected\n ) {\n onValueChangeHandler(value[value.length - 1]);\n }\n }\n break;\n\n case 'Enter':\n setOpen(true);\n break;\n\n case 'Escape':\n if (activeIndex !== -1) {\n setActiveIndex(-1);\n } else if (open) {\n setOpen(false);\n }\n break;\n }\n },\n\n [value, inputValue, activeIndex, loop]\n );\n\n const memoValue = useMemo(\n () => ({\n value,\n onValueChange: onValueChangeHandler,\n open,\n setOpen,\n inputValue,\n setInputValue,\n activeIndex,\n setActiveIndex,\n ref: inputRef,\n handleSelect,\n }),\n [\n value,\n onValueChangeHandler,\n open,\n setOpen,\n inputValue,\n setInputValue,\n activeIndex,\n setActiveIndex,\n inputRef,\n handleSelect,\n ]\n );\n\n return (\n <MultiSelectContext value={memoValue}>\n <CommandRoot\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex w-full flex-col gap-2 overflow-visible bg-transparent',\n className\n )}\n dir={dir}\n {...props}\n >\n {children}\n </CommandRoot>\n </MultiSelectContext>\n );\n};\n\nconst MultiSelectTrigger: FC<\n HTMLAttributes<HTMLDivElement> & {\n getBadgeValue?: (value: string) => string;\n validationStyleEnabled?: boolean;\n }\n> = ({\n className,\n getBadgeValue = (value) => value,\n validationStyleEnabled = false,\n children,\n ...props\n}) => {\n const { value, onValueChange, activeIndex } = useMultiSelect();\n\n const mousePreventDefault: MouseEventHandler<HTMLButtonElement> = useCallback(\n (e) => {\n e.preventDefault();\n e.stopPropagation();\n },\n []\n );\n\n return (\n <div\n className={cn(\n // Base layout\n 'flex w-full flex-col gap-3',\n 'cursor-pointer select-text text-base shadow-none outline-none md:text-sm',\n\n // Corner shape\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n\n // Spacing\n 'px-2 py-3 md:py-2',\n\n // Background and text\n 'bg-neutral-50 dark:bg-neutral-950',\n 'text-text',\n\n // Focus ring\n 'ring-0',\n 'focus-within:outline-none',\n 'focus-within:ring-3',\n 'focus-within:ring-neutral-200',\n 'dark:focus-within:ring-neutral-500',\n\n 'focus-within:ring-offset-white',\n 'dark:focus-within:ring-offset-neutral-500',\n\n // Remove box-shadow\n '[box-shadow:none]',\n\n // States\n 'disabled:cursor-not-allowed disabled:opacity-50',\n 'aria-invalid:border-error',\n\n // Validation styles\n validationStyleEnabled && 'valid:border-success invalid:border-error',\n\n className\n )}\n {...props}\n >\n {value.length > 0 && (\n <div className=\"flex w-full flex-wrap gap-1\">\n {value.map((item, index) => (\n <Badge\n key={item}\n className={cn(\n 'flex items-center gap-1 rounded-xl px-1',\n activeIndex === index && 'ring-2 ring-muted-foreground'\n )}\n color=\"text\"\n >\n <span className=\"text-xs\">{getBadgeValue(item)}</span>\n <button\n aria-label={`Remove ${item} option`}\n aria-roledescription=\"button to remove option\"\n onMouseDown={mousePreventDefault}\n onClick={() => onValueChange(item)}\n >\n <span className=\"sr-only\">Remove {item} option</span>\n <RemoveIcon className=\"size-4 cursor-pointer\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n {children}\n </div>\n );\n};\n\nconst MultiSelectInput: FC<ComponentProps<typeof Command.Input>> = ({\n className,\n ...props\n}) => {\n const {\n setOpen,\n inputValue,\n setInputValue,\n activeIndex,\n setActiveIndex,\n handleSelect,\n ref: inputRef,\n } = useMultiSelect();\n\n return (\n <Command.Input\n {...props}\n tabIndex={0}\n ref={inputRef as LegacyRef<HTMLInputElement>}\n value={inputValue}\n onValueChange={activeIndex === -1 ? setInputValue : undefined}\n onSelect={handleSelect}\n onBlur={() => setOpen(false)}\n onFocus={() => setOpen(true)}\n onClick={() => setActiveIndex(-1)}\n className={cn(\n 'ml-2 flex-1 cursor-pointer outline-hidden',\n className,\n activeIndex !== -1 && 'caret-transparent'\n )}\n />\n );\n};\n\nconst MultiSelectContent: FC<HTMLAttributes<HTMLDivElement>> = ({\n children,\n}) => {\n const { open } = useMultiSelect();\n return <div className=\"relative\">{open && children}</div>;\n};\n\nconst MultiSelectList: typeof Command.List = ({ className, children }) => (\n <Command.List\n className={cn(\n // Base layout\n 'absolute top-0 z-10 flex w-full flex-col gap-2',\n 'rounded-xl p-2 shadow-md',\n\n // Background and text\n 'bg-white dark:bg-neutral-950',\n 'text-text',\n\n // Border\n 'border border-neutral-200 dark:border-neutral-800',\n\n // Transitions\n 'transition-colors',\n\n className\n )}\n >\n {children}\n <Command.Empty>\n <span className=\"text-muted-foreground\">No results found</span>\n </Command.Empty>\n </Command.List>\n);\n\nconst MultiSelectItem: FC<\n { value: string } & ComponentProps<typeof Command.Item>\n> = ({ className, value, children, ...props }) => {\n const { value: Options, onValueChange, setInputValue } = useMultiSelect();\n\n const mousePreventDefault: MouseEventHandler<HTMLDivElement> = useCallback(\n (e) => {\n e.preventDefault();\n e.stopPropagation();\n },\n []\n );\n\n const isIncluded = Options.includes(value);\n return (\n <Command.Item\n {...props}\n onSelect={() => {\n onValueChange(value);\n setInputValue('');\n }}\n className={cn(\n // Base layout\n 'flex cursor-pointer justify-between',\n 'rounded-lg px-2 py-1',\n\n // Hover and transitions\n 'transition-colors',\n 'hover:bg-neutral/10',\n\n // States\n isIncluded && 'opacity-50',\n props.disabled && 'cursor-not-allowed opacity-50',\n\n className\n )}\n onMouseDown={mousePreventDefault}\n >\n {children}\n {isIncluded && <Check className=\"size-4\" />}\n </Command.Item>\n );\n};\n\ntype MultiSelectType = typeof MultiSelectRoot & {\n Trigger: typeof MultiSelectTrigger;\n Input: typeof MultiSelectInput;\n Content: typeof MultiSelectContent;\n List: typeof MultiSelectList;\n Item: typeof MultiSelectItem;\n};\n\n/**\n *\n * Usage example:\n * ```jsx\n * <MultiSelect\n * values={value}\n * onValuesChange={setValue}\n * loop\n * >\n * <MultiSelect.Trigger>\n * <MultiSelect.Input placeholder=\"Select your framework\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"React\">React</MultiSelect.Item>\n * <MultiSelect.Item value=\"Vue\">Vue</MultiSelect.Item>\n * <MultiSelect.Item value=\"Svelte\">Svelte</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * ```\n */\nexport const MultiSelect = MultiSelectRoot as MultiSelectType;\nMultiSelect.Trigger = MultiSelectTrigger;\nMultiSelect.Input = MultiSelectInput;\nMultiSelect.Content = MultiSelectContent;\nMultiSelect.List = MultiSelectList;\nMultiSelect.Item = MultiSelectItem;\n"],"mappings":";;;;;;;;;;AAsDA,MAAM,qBAAqB,cAA8C,IAAI;;;;;;;;;;;;;;;;;;AAmB7E,MAAM,uBAAuB;CAC3B,MAAM,UAAU,WAAW,kBAAkB;CAC7C,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,wDAAwD;CAE1E,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgJA,MAAM,mBAAyC,EAC7C,QAAQ,YACR,eACA,eACA,OAAO,OACP,WACA,UACA,KACA,GAAG,YACC;CACJ,MAAM,CAAC,OAAO,YAAY,SAAmB,iBAAiB,CAAC,CAAC;CAChE,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,CAAC,MAAM,WAAW,SAAkB,KAAK;CAC/C,MAAM,CAAC,aAAa,kBAAkB,SAAiB,EAAE;CACzD,MAAM,WAAW,OAAyB,IAAI;CAC9C,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,KAAK;CAC5D,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CAErD,gBAAgB;EACd,IAAI,YACF,SAAS,UAAU;CAEvB,GAAG,CAAC,UAAU,CAAC;CAEf,MAAM,uBAAuB,aAC1B,QAAgB;EACf,IAAI,MAAM,SAAS,GAAG,GAAG;GACvB,MAAM,WAAW,MAAM,QAAQ,SAAS,SAAS,GAAG;GACpD,SAAS,QAAQ;GACjB,gBAAgB,QAAQ;EAC1B,OAAO;GACL,MAAM,WAAW,CAAC,GAAG,OAAO,GAAG;GAC/B,SAAS,QAAQ;GACjB,gBAAgB,QAAQ;EAC1B;CACF,GAEA,CAAC,KAAK,CACR;CAEA,MAAM,eAAe,aAClB,MAAwC;EACvC,EAAE,eAAe;EACjB,MAAM,SAAS,EAAE;EACjB,MAAM,YAAY,OAAO,MAAM,UAC7B,OAAO,kBAAkB,GACzB,OAAO,gBAAgB,CACzB;EAEA,iBAAiB,SAAS;EAC1B,mBAAmB,cAAc,UAAU;CAC7C,GACA,CAAC,UAAU,CACb;CAEA,MAAM,gBAAgB,aACnB,MAAqC;EACpC,EAAE,gBAAgB;EAClB,MAAM,SAAS,SAAS;EAExB,IAAI,CAAC,QAAQ;EAEb,MAAM,iBAAiB;GACrB,MAAM,YAAY,cAAc;GAChC,eACE,YAAY,MAAM,SAAS,IAAK,OAAO,IAAI,KAAM,SACnD;EACF;EAEA,MAAM,iBAAiB;GACrB,MAAM,YAAY,cAAc;GAChC,eAAe,YAAY,IAAI,MAAM,SAAS,IAAI,SAAS;EAC7D;EAEA,MAAM,oBAAoB;GACxB,MAAM,WACJ,cAAc,KAAK,IACf,MAAM,SAAS,MAAM,IACnB,KACA,IACF,cAAc;GACpB,eAAe,QAAQ;EACzB;EAEA,QAAQ,EAAE,KAAV;GACE,KAAK;IACH,IAAI,QAAQ,OACV;SAAI,MAAM,SAAS,MAAM,gBAAgB,MAAM,OAC7C,SAAS;IACX,OACK,IAAI,MAAM,SAAS,KAAK,OAAO,mBAAmB,GACvD,SAAS;IAEX;GAEF,KAAK;IACH,IAAI,QAAQ,OACV;SAAI,MAAM,SAAS,KAAK,OAAO,mBAAmB,GAChD,SAAS;IACX,OACK,IAAI,MAAM,SAAS,MAAM,gBAAgB,MAAM,OACpD,SAAS;IAEX;GAEF,KAAK;GACL,KAAK;IACH,IAAI,MAAM,SAAS,GACjB;SAAI,gBAAgB,MAAM,cAAc,MAAM,QAAQ;MACpD,qBAAqB,MAAM,YAAY;MACvC,YAAY;KACd,OAAO,IACJ,OAAO,mBAAmB,KAAK,kBAAkB,cAClD,iBAEA,qBAAqB,MAAM,MAAM,SAAS,EAAE;IAC9C;IAEF;GAEF,KAAK;IACH,QAAQ,IAAI;IACZ;GAEF,KAAK;IACH,IAAI,gBAAgB,IAClB,eAAe,EAAE;SACZ,IAAI,MACT,QAAQ,KAAK;IAEf;EACJ;CACF,GAEA;EAAC;EAAO;EAAY;EAAa;CAAI,CACvC;CAEA,MAAM,YAAY,eACT;EACL;EACA,eAAe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,KAAK;EACL;CACF,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,OACE,oBAAC,oBAAD;EAAoB,OAAO;YACzB,oBAAC,aAAD;GACE,WAAW;GACX,WAAW,GACT,8DACA,SACF;GACK;GACL,GAAI;GAEH;EACU;CACK;AAExB;AAEA,MAAM,sBAKD,EACH,WACA,iBAAiB,UAAU,OAC3B,yBAAyB,OACzB,UACA,GAAG,YACC;CACJ,MAAM,EAAE,OAAO,eAAe,gBAAgB,eAAe;CAE7D,MAAM,sBAA4D,aAC/D,MAAM;EACL,EAAE,eAAe;EACjB,EAAE,gBAAgB;CACpB,GACA,CAAC,CACH;CAEA,OACE,qBAAC,OAAD;EACE,WAAW,GAET,8BACA,4EAGA,mFAGA,qBAGA,qCACA,aAGA,UACA,6BACA,uBACA,iCACA,sCAEA,kCACA,6CAGA,qBAGA,mDACA,6BAGA,0BAA0B,6CAE1B,SACF;EACA,GAAI;YAtCN,CAwCG,MAAM,SAAS,KACd,oBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,MAAM,UAChB,qBAAC,OAAD;IAEE,WAAW,GACT,2CACA,gBAAgB,SAAS,8BAC3B;IACA,OAAM;cANR,CAQE,oBAAC,QAAD;KAAM,WAAU;eAAW,cAAc,IAAI;IAAQ,IACrD,qBAAC,UAAD;KACE,cAAY,UAAU,KAAK;KAC3B,wBAAqB;KACrB,aAAa;KACb,eAAe,cAAc,IAAI;eAJnC,CAME,qBAAC,QAAD;MAAM,WAAU;gBAAhB;OAA0B;OAAQ;OAAK;MAAa;SACpD,oBAACA,GAAD,EAAY,WAAU,wBAAyB,EACzC;MACH;MAjBA,IAiBA,CACR;EACE,IAEN,QACE;;AAET;AAEA,MAAM,oBAA8D,EAClE,WACA,GAAG,YACC;CACJ,MAAM,EACJ,SACA,YACA,eACA,aACA,gBACA,cACA,KAAK,aACH,eAAe;CAEnB,OACE,oBAAC,QAAQ,OAAT;EACE,GAAI;EACJ,UAAU;EACV,KAAK;EACL,OAAO;EACP,eAAe,gBAAgB,KAAK,gBAAgB;EACpD,UAAU;EACV,cAAc,QAAQ,KAAK;EAC3B,eAAe,QAAQ,IAAI;EAC3B,eAAe,eAAe,EAAE;EAChC,WAAW,GACT,6CACA,WACA,gBAAgB,MAAM,mBACxB;CACD;AAEL;AAEA,MAAM,sBAA0D,EAC9D,eACI;CACJ,MAAM,EAAE,SAAS,eAAe;CAChC,OAAO,oBAAC,OAAD;EAAK,WAAU;YAAY,QAAQ;CAAc;AAC1D;AAEA,MAAM,mBAAwC,EAAE,WAAW,eACzD,qBAAC,QAAQ,MAAT;CACE,WAAW,GAET,kDACA,4BAGA,gCACA,aAGA,qDAGA,qBAEA,SACF;WAjBF,CAmBG,UACD,oBAAC,QAAQ,OAAT,YACE,oBAAC,QAAD;EAAM,WAAU;YAAwB;CAAsB,GACjD,EACH;;AAGhB,MAAM,mBAED,EAAE,WAAW,OAAO,UAAU,GAAG,YAAY;CAChD,MAAM,EAAE,OAAO,SAAS,eAAe,kBAAkB,eAAe;CAExE,MAAM,sBAAyD,aAC5D,MAAM;EACL,EAAE,eAAe;EACjB,EAAE,gBAAgB;CACpB,GACA,CAAC,CACH;CAEA,MAAM,aAAa,QAAQ,SAAS,KAAK;CACzC,OACE,qBAAC,QAAQ,MAAT;EACE,GAAI;EACJ,gBAAgB;GACd,cAAc,KAAK;GACnB,cAAc,EAAE;EAClB;EACA,WAAW,GAET,uCACA,wBAGA,qBACA,uBAGA,cAAc,cACd,MAAM,YAAY,iCAElB,SACF;EACA,aAAa;YArBf,CAuBG,UACA,cAAc,oBAAC,OAAD,EAAO,WAAU,SAAU,EAC9B;;AAElB;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,cAAc;AAC3B,YAAY,UAAU;AACtB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,OAAO;AACnB,YAAY,OAAO"}
1
+ {"version":3,"file":"Multiselect.mjs","names":["RemoveIcon"],"sources":["../../../../src/components/Select/Multiselect.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { Check, X as RemoveIcon } from 'lucide-react';\nimport {\n type ComponentProps,\n createContext,\n type Dispatch,\n type FC,\n type HTMLAttributes,\n type KeyboardEvent,\n type LegacyRef,\n type MouseEventHandler,\n type RefObject,\n type SetStateAction,\n type SyntheticEvent,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { Badge } from '../Badge';\nimport { Command, CommandRoot } from '../Command';\n\n/**\n * Context properties for MultiSelect component state management\n *\n * @interface MultiSelectContextProps\n */\ntype MultiSelectContextProps = {\n /** Array of currently selected values */\n value: string[];\n /** Handler for value changes */\n onValueChange: (value: string) => void;\n /** Whether the dropdown is currently open */\n open: boolean;\n /** Function to set the open state */\n setOpen: (value: boolean) => void;\n /** Current input field value for filtering */\n inputValue: string;\n /** Function to set the input value */\n setInputValue: Dispatch<SetStateAction<string>>;\n /** Index of currently focused option for keyboard navigation */\n activeIndex: number;\n /** Function to set the active index */\n setActiveIndex: Dispatch<SetStateAction<number>>;\n /** Ref to the input element */\n ref: RefObject<HTMLInputElement | null>;\n /** Handler for option selection */\n handleSelect: (e: SyntheticEvent<HTMLInputElement>) => void;\n};\n\nconst MultiSelectContext = createContext<MultiSelectContextProps | null>(null);\n\n/**\n * Custom hook to access MultiSelect context\n *\n * Provides access to the internal state and methods of the MultiSelect component.\n * Must be used within a MultiSelect component tree.\n *\n * @returns MultiSelectContextProps - All context properties and methods\n * @throws Error when used outside of MultiSelect component\n *\n * @example\n * ```tsx\n * function CustomMultiSelectItem() {\n * const { value, onValueChange, open } = useMultiSelect();\n * // Use context properties...\n * }\n * ```\n */\nconst useMultiSelect = () => {\n const context = useContext(MultiSelectContext);\n if (!context) {\n throw new Error('useMultiSelect must be used within MultiSelectProvider');\n }\n return context;\n};\n\n/**\n * Props interface for the main MultiSelect component\n *\n * @interface MultiSelectProps\n */\ntype MultiSelectProps = ComponentProps<typeof CommandRoot> & {\n /**\n * Array of selected values (controlled mode)\n * @example\n * ```tsx\n * const [selected, setSelected] = useState(['react', 'vue']);\n * <MultiSelect values={selected} onValueChange={setSelected} />\n * ```\n */\n values?: string[];\n\n /**\n * Default selected values for uncontrolled mode\n * @example\n * ```tsx\n * <MultiSelect defaultValues={['react']} />\n * ```\n */\n defaultValues?: string[];\n\n /**\n * Callback fired when selection changes\n * @param value - New array of selected values\n * @example\n * ```tsx\n * <MultiSelect onValueChange={(values) => console.log('Selected:', values)} />\n * ```\n */\n onValueChange?: (value: string[]) => void;\n\n /**\n * Whether keyboard navigation should loop through options\n * @default false\n * @example\n * ```tsx\n * <MultiSelect loop /> // Arrow keys wrap around at list boundaries\n * ```\n */\n loop?: boolean;\n};\n\n/**\n * MultiSelect - A comprehensive multi-selection dropdown component\n *\n * An advanced multi-select component that combines the functionality of a searchable dropdown\n * with the ability to select multiple values. Built on top of Command component primitives,\n * it provides filtering, keyboard navigation, and visual feedback through badges.\n *\n * ## Key Features\n * - **Multi-Selection**: Select multiple options with visual badge representation\n * - **Searchable**: Built-in filtering to quickly find options in large lists\n * - **Keyboard Navigation**: Full arrow key navigation with optional looping\n * - **Accessibility**: Screen reader support, ARIA attributes, and focus management\n * - **Flexible State**: Both controlled and uncontrolled usage patterns\n * - **Rich UI**: Customizable badges, icons, and content layout\n *\n * ## Use Cases\n * - Tag/category selection in forms\n * - Multi-user assignment interfaces\n * - Feature/permission selection\n * - Filter selection in search interfaces\n * - Any multi-choice selection requirement\n *\n * ## Architecture\n * The component follows a compound pattern similar to Select:\n * - `MultiSelect` (root): Manages state and provides context\n * - `MultiSelect.Trigger`: Container for input and selected badges\n * - `MultiSelect.Input`: Searchable input field with filtering\n * - `MultiSelect.Content`: Dropdown container for options\n * - `MultiSelect.List`: Options container with keyboard navigation\n * - `MultiSelect.Item`: Individual selectable options\n *\n * ## Accessibility\n * - **Keyboard Navigation**: Arrow keys, Enter to select, Backspace to remove\n * - **Screen Readers**: Proper ARIA labels and live region announcements\n * - **Focus Management**: Clear focus indicators and logical tab flow\n * - **Search**: Real-time filtering with screen reader announcements\n *\n * @example\n * Basic multi-select usage:\n * ```tsx\n * const [frameworks, setFrameworks] = useState<string[]>([]);\n *\n * <MultiSelect values={frameworks} onValueChange={setFrameworks}>\n * <MultiSelect.Trigger>\n * <MultiSelect.Input placeholder=\"Select frameworks...\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"react\">React</MultiSelect.Item>\n * <MultiSelect.Item value=\"vue\">Vue</MultiSelect.Item>\n * <MultiSelect.Item value=\"svelte\">Svelte</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * ```\n *\n * @example\n * Advanced usage with keyboard looping:\n * ```tsx\n * <MultiSelect defaultValues={['react']} loop>\n * <MultiSelect.Trigger>\n * <MultiSelect.Input placeholder=\"Choose technologies...\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"react\">⚛️ React</MultiSelect.Item>\n * <MultiSelect.Item value=\"vue\">💚 Vue</MultiSelect.Item>\n * <MultiSelect.Item value=\"angular\">🔴 Angular</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * ```\n *\n * @example\n * Form integration with validation:\n * ```tsx\n * <form>\n * <MultiSelect\n * values={selectedSkills}\n * onValueChange={setSelectedSkills}\n * required\n * >\n * <MultiSelect.Trigger className=\"min-h-[2.5rem]\">\n * <MultiSelect.Input placeholder=\"Select your skills...\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"javascript\">JavaScript</MultiSelect.Item>\n * <MultiSelect.Item value=\"typescript\">TypeScript</MultiSelect.Item>\n * <MultiSelect.Item value=\"python\">Python</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * </form>\n * ```\n */\nconst MultiSelectRoot: FC<MultiSelectProps> = ({\n values: valuesProp,\n defaultValues,\n onValueChange,\n loop = false,\n className,\n children,\n dir,\n ...props\n}) => {\n const [value, setValue] = useState<string[]>(defaultValues ?? []);\n const [inputValue, setInputValue] = useState('');\n const [open, setOpen] = useState<boolean>(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const inputRef = useRef<HTMLInputElement>(null);\n const [isValueSelected, setIsValueSelected] = useState(false);\n const [selectedValue, setSelectedValue] = useState('');\n\n useEffect(() => {\n if (valuesProp) {\n setValue(valuesProp);\n }\n }, [valuesProp]);\n\n const onValueChangeHandler = useCallback(\n (val: string) => {\n if (value.includes(val)) {\n const newValue = value.filter((item) => item !== val);\n setValue(newValue);\n onValueChange?.(newValue);\n } else {\n const newValue = [...value, val];\n setValue(newValue);\n onValueChange?.(newValue);\n }\n },\n\n [value]\n );\n\n const handleSelect = useCallback(\n (e: SyntheticEvent<HTMLInputElement>) => {\n e.preventDefault();\n const target = e.currentTarget;\n const selection = target.value.substring(\n target.selectionStart ?? 0,\n target.selectionEnd ?? 0\n );\n\n setSelectedValue(selection);\n setIsValueSelected(selection === inputValue);\n },\n [inputValue]\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n e.stopPropagation();\n const target = inputRef.current;\n\n if (!target) return;\n\n const moveNext = () => {\n const nextIndex = activeIndex + 1;\n setActiveIndex(\n nextIndex > value.length - 1 ? (loop ? 0 : -1) : nextIndex\n );\n };\n\n const movePrev = () => {\n const prevIndex = activeIndex - 1;\n setActiveIndex(prevIndex < 0 ? value.length - 1 : prevIndex);\n };\n\n const moveCurrent = () => {\n const newIndex =\n activeIndex - 1 <= 0\n ? value.length - 1 === 0\n ? -1\n : 0\n : activeIndex - 1;\n setActiveIndex(newIndex);\n };\n\n switch (e.key) {\n case 'ArrowLeft':\n if (dir === 'rtl') {\n if (value.length > 0 && (activeIndex !== -1 || loop)) {\n moveNext();\n }\n } else if (value.length > 0 && target.selectionStart === 0) {\n movePrev();\n }\n break;\n\n case 'ArrowRight':\n if (dir === 'rtl') {\n if (value.length > 0 && target.selectionStart === 0) {\n movePrev();\n }\n } else if (value.length > 0 && (activeIndex !== -1 || loop)) {\n moveNext();\n }\n break;\n\n case 'Backspace':\n case 'Delete':\n if (value.length > 0) {\n if (activeIndex !== -1 && activeIndex < value.length) {\n onValueChangeHandler(value[activeIndex]);\n moveCurrent();\n } else if (\n (target.selectionStart === 0 && selectedValue === inputValue) ||\n isValueSelected\n ) {\n onValueChangeHandler(value[value.length - 1]);\n }\n }\n break;\n\n case 'Enter':\n setOpen(true);\n break;\n\n case 'Escape':\n if (activeIndex !== -1) {\n setActiveIndex(-1);\n } else if (open) {\n setOpen(false);\n }\n break;\n }\n },\n\n [value, inputValue, activeIndex, loop]\n );\n\n const memoValue = useMemo(\n () => ({\n value,\n onValueChange: onValueChangeHandler,\n open,\n setOpen,\n inputValue,\n setInputValue,\n activeIndex,\n setActiveIndex,\n ref: inputRef,\n handleSelect,\n }),\n [\n value,\n onValueChangeHandler,\n open,\n setOpen,\n inputValue,\n setInputValue,\n activeIndex,\n setActiveIndex,\n inputRef,\n handleSelect,\n ]\n );\n\n return (\n <MultiSelectContext value={memoValue}>\n <CommandRoot\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex w-full flex-col gap-2 overflow-visible bg-transparent',\n className\n )}\n dir={dir}\n {...props}\n >\n {children}\n </CommandRoot>\n </MultiSelectContext>\n );\n};\n\nconst MultiSelectTrigger: FC<\n HTMLAttributes<HTMLDivElement> & {\n getBadgeValue?: (value: string) => string;\n validationStyleEnabled?: boolean;\n }\n> = ({\n className,\n getBadgeValue = (value) => value,\n validationStyleEnabled = false,\n children,\n ...props\n}) => {\n const { value, onValueChange, activeIndex } = useMultiSelect();\n\n const mousePreventDefault: MouseEventHandler<HTMLButtonElement> = useCallback(\n (e) => {\n e.preventDefault();\n e.stopPropagation();\n },\n []\n );\n\n return (\n <div\n className={cn(\n // Base layout\n 'flex w-full flex-col gap-3',\n 'cursor-pointer select-text text-base shadow-none outline-none md:text-sm',\n\n // Corner shape\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n\n // Spacing\n 'px-2 py-3 md:py-2',\n\n // Background and text\n 'bg-neutral-50 dark:bg-neutral-950',\n 'text-text',\n\n // Focus ring\n 'ring-0',\n 'focus-within:outline-none',\n 'focus-within:ring-3',\n 'focus-within:ring-neutral-200',\n 'dark:focus-within:ring-neutral-500',\n\n 'focus-within:ring-offset-white',\n 'dark:focus-within:ring-offset-neutral-500',\n\n // Remove box-shadow\n '[box-shadow:none]',\n\n // States\n 'disabled:cursor-not-allowed disabled:opacity-50',\n 'aria-invalid:border-error',\n\n // Validation styles\n validationStyleEnabled && 'valid:border-success invalid:border-error',\n\n className\n )}\n {...props}\n >\n {value.length > 0 && (\n <div className=\"flex w-full flex-wrap gap-1\">\n {value.map((item, index) => (\n <Badge\n key={item}\n className={cn(\n 'flex items-center gap-1 rounded-xl px-1',\n activeIndex === index && 'ring-2 ring-muted-foreground'\n )}\n color=\"text\"\n >\n <span className=\"text-xs\">{getBadgeValue(item)}</span>\n <button\n aria-label={`Remove ${item} option`}\n aria-roledescription=\"button to remove option\"\n onMouseDown={mousePreventDefault}\n onClick={() => onValueChange(item)}\n >\n <span className=\"sr-only\">Remove {item} option</span>\n <RemoveIcon className=\"size-4 cursor-pointer\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n {children}\n </div>\n );\n};\n\nconst MultiSelectInput: FC<ComponentProps<typeof Command.Input>> = ({\n className,\n ...props\n}) => {\n const {\n setOpen,\n inputValue,\n setInputValue,\n activeIndex,\n setActiveIndex,\n handleSelect,\n ref: inputRef,\n } = useMultiSelect();\n\n return (\n <Command.Input\n {...props}\n tabIndex={0}\n ref={inputRef as LegacyRef<HTMLInputElement>}\n value={inputValue}\n onValueChange={activeIndex === -1 ? setInputValue : undefined}\n onSelect={handleSelect}\n onBlur={() => setOpen(false)}\n onFocus={() => setOpen(true)}\n onClick={() => setActiveIndex(-1)}\n className={cn(\n 'ml-2 flex-1 cursor-pointer outline-hidden',\n className,\n activeIndex !== -1 && 'caret-transparent'\n )}\n />\n );\n};\n\nconst MultiSelectContent: FC<HTMLAttributes<HTMLDivElement>> = ({\n children,\n}) => {\n const { open } = useMultiSelect();\n return <div className=\"relative\">{open && children}</div>;\n};\n\nconst MultiSelectList: typeof Command.List = ({ className, children }) => (\n <Command.List\n className={cn(\n // Base layout\n 'absolute top-0 z-10 flex w-full flex-col gap-2',\n 'rounded-xl p-2 shadow-md',\n\n // Background and text\n 'bg-white dark:bg-neutral-950',\n 'text-text',\n\n // Border\n 'border border-neutral-200 dark:border-neutral-800',\n\n // Transitions\n 'transition-colors',\n\n className\n )}\n >\n {children}\n <Command.Empty>\n <span className=\"text-muted-foreground\">No results found</span>\n </Command.Empty>\n </Command.List>\n);\n\nconst MultiSelectItem: FC<\n { value: string } & ComponentProps<typeof Command.Item>\n> = ({ className, value, children, ...props }) => {\n const { value: Options, onValueChange, setInputValue } = useMultiSelect();\n\n const mousePreventDefault: MouseEventHandler<HTMLDivElement> = useCallback(\n (e) => {\n e.preventDefault();\n e.stopPropagation();\n },\n []\n );\n\n const isIncluded = Options.includes(value);\n return (\n <Command.Item\n {...props}\n onSelect={() => {\n onValueChange(value);\n setInputValue('');\n }}\n className={cn(\n // Base layout\n 'flex cursor-pointer justify-between',\n 'rounded-lg px-2 py-1',\n\n // Hover and transitions\n 'transition-colors',\n 'hover:bg-neutral/10',\n\n // States\n isIncluded && 'opacity-50',\n props.disabled && 'cursor-not-allowed opacity-50',\n\n className\n )}\n onMouseDown={mousePreventDefault}\n >\n {children}\n {isIncluded && <Check className=\"size-4\" />}\n </Command.Item>\n );\n};\n\ntype MultiSelectType = typeof MultiSelectRoot & {\n Trigger: typeof MultiSelectTrigger;\n Input: typeof MultiSelectInput;\n Content: typeof MultiSelectContent;\n List: typeof MultiSelectList;\n Item: typeof MultiSelectItem;\n};\n\n/**\n *\n * Usage example:\n * ```jsx\n * <MultiSelect\n * values={value}\n * onValuesChange={setValue}\n * loop\n * >\n * <MultiSelect.Trigger>\n * <MultiSelect.Input placeholder=\"Select your framework\" />\n * </MultiSelect.Trigger>\n * <MultiSelect.Content>\n * <MultiSelect.List>\n * <MultiSelect.Item value=\"React\">React</MultiSelect.Item>\n * <MultiSelect.Item value=\"Vue\">Vue</MultiSelect.Item>\n * <MultiSelect.Item value=\"Svelte\">Svelte</MultiSelect.Item>\n * </MultiSelect.List>\n * </MultiSelect.Content>\n * </MultiSelect>\n * ```\n */\nexport const MultiSelect = MultiSelectRoot as MultiSelectType;\nMultiSelect.Trigger = MultiSelectTrigger;\nMultiSelect.Input = MultiSelectInput;\nMultiSelect.Content = MultiSelectContent;\nMultiSelect.List = MultiSelectList;\nMultiSelect.Item = MultiSelectItem;\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon, ChevronsUpDown } from "lucide-react";
5
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon, ChevronsUpDown } from "lucide-react";
6
6
  import * as SelectPrimitive from "@radix-ui/react-select";
7
7
 
8
8
  //#region src/components/Select/Select.tsx
@@ -3,8 +3,8 @@
3
3
  import { cn } from "../../utils/cn.mjs";
4
4
  import { useSwitchSelector } from "./useSwitchSelector.mjs";
5
5
  import { createElement } from "react";
6
- import { cva } from "class-variance-authority";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { cva } from "class-variance-authority";
8
8
 
9
9
  //#region src/components/SwitchSelector/SwitchSelector.tsx
10
10
  const defaultChoices = [{
@@ -4,8 +4,8 @@ import { cn } from "../../utils/cn.mjs";
4
4
  import { useSwitchSelector } from "./useSwitchSelector.mjs";
5
5
  import { defaultChoices } from "./SwitchSelector.mjs";
6
6
  import { createElement } from "react";
7
- import { cva } from "class-variance-authority";
8
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { cva } from "class-variance-authority";
9
9
 
10
10
  //#region src/components/SwitchSelector/VerticalSwitchSelector.tsx
11
11
  const verticalSwitchSelectorVariant = cva("flex h-fit w-fit cursor-pointer flex-col gap-2 rounded-2xl border-[1.3px] p-1", {
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { TabSelector } from "../TabSelector/TabSelector.mjs";
5
4
  import { useHorizontalSwipe } from "../../hooks/useHorizontalSwipe.mjs";
5
+ import { TabSelector } from "../TabSelector/TabSelector.mjs";
6
6
  import { useTabContext } from "./TabContext.mjs";
7
7
  import { Children, createContext, isValidElement, useState } from "react";
8
- import { cva } from "class-variance-authority";
9
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { cva } from "class-variance-authority";
10
10
 
11
11
  //#region src/components/Tab/Tab.tsx
12
12
  const TabContext = createContext(void 0);
@@ -3,8 +3,8 @@
3
3
  import { cn } from "../../utils/cn.mjs";
4
4
  import { useItemSelector } from "../../hooks/useItemSelector.mjs";
5
5
  import { cloneElement, useEffect, useRef } from "react";
6
- import { cva } from "class-variance-authority";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { cva } from "class-variance-authority";
8
8
 
9
9
  //#region src/components/TabSelector/TabSelector.tsx
10
10
  const tabSelectorVariant = cva("relative z-0 flex size-full flex-row items-center gap-2", {
@@ -1,7 +1,7 @@
1
1
  import { Button } from "../Button/Button.mjs";
2
2
  import { Popover } from "../Popover/dynamic.mjs";
3
- import { MoveDiagonal } from "lucide-react";
4
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { MoveDiagonal } from "lucide-react";
5
5
  import { useIntlayer } from "react-intlayer";
6
6
 
7
7
  //#region src/components/Table/ExpandButton.tsx
@@ -1,5 +1,5 @@
1
- import { cva } from "class-variance-authority";
2
1
  import { jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
3
 
4
4
  //#region src/components/Tag/index.tsx
5
5
  const containerVariants = cva("w-fit backdrop-blur", {
@@ -74,11 +74,13 @@ const parseAnsiCodes = (text, isDarkMode) => {
74
74
  let isBold = false;
75
75
  for (const part of parts) if (ansiRegex.test(part)) {
76
76
  const colorMapping = ANSI_COLORS[part];
77
- if (colorMapping) if (part === "\x1B[0m") {
78
- currentColor = void 0;
79
- isBold = false;
80
- } else if (part === "\x1B[1m") isBold = true;
81
- else currentColor = isDarkMode ? colorMapping.dark : colorMapping.light;
77
+ if (colorMapping) {
78
+ if (part === "\x1B[0m") {
79
+ currentColor = void 0;
80
+ isBold = false;
81
+ } else if (part === "\x1B[1m") isBold = true;
82
+ else currentColor = isDarkMode ? colorMapping.dark : colorMapping.light;
83
+ }
82
84
  } else if (part) segments.push({
83
85
  text: part,
84
86
  color: currentColor,
@@ -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-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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"}
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":";;;;;;;;;;AAcA,MAAM,cAA+D;CACnE,WAAW;EAAE,OAAO;EAAI,MAAM;CAAG;CACjC,YAAY;EAAE,OAAO;EAAiB,MAAM;CAAgB;CAC5D,kBAAkB;EAAE,OAAO;EAAiB,MAAM;CAAgB;CAClE,kBAAkB;EAAE,OAAO;EAAiB,MAAM;CAAgB;CAClE,YAAY;EAAE,OAAO;EAAiB,MAAM;CAAgB;CAC5D,YAAY;EAAE,OAAO;EAAgB,MAAM;CAAe;CAC1D,YAAY;EAAE,OAAO;EAAkB,MAAM;CAAiB;CAC9D,kBAAkB;EAAE,OAAO;EAAmB,MAAM;CAAkB;CACtE,YAAY;EAAE,OAAO;EAAmB,MAAM;CAAkB;CAChE,gBAAgB;EAAE,OAAO;EAAkB,MAAM;CAAiB;CAClE,kBAAkB;EAAE,OAAO;EAAmB,MAAM;CAAkB;CACtE,YAAY;EAAE,OAAO;EAAiB,MAAM;CAAgB;CAC5D,YAAY;EAAE,OAAO;EAAiB,MAAM;CAAgB;CAC5D,WAAW;EAAE,OAAO;EAAa,MAAM;CAAY;AACrD;AAQA,MAAM,kBAAkB,MAAc,eAAuC;CAC3E,MAAM,WAA0B,CAAC;CAEjC,MAAM,YAAY;CAClB,MAAM,QAAQ,KAAK,MAAM,SAAS;CAElC,IAAI;CACJ,IAAI,SAAS;CAEb,KAAK,MAAM,QAAQ,OACjB,IAAI,UAAU,KAAK,IAAI,GAAG;EAExB,MAAM,eAAe,YAAY;EACjC,IAAI,cAAc;GAChB,IAAI,SAAS,WAAW;IAEtB,eAAe;IACf,SAAS;GACX,OAAO,IAAI,SAAS,WAElB,SAAS;QAET,eAAe,aAAa,aAAa,OAAO,aAAa;EAEjE;CACF,OAAO,IAAI,MAET,SAAS,KAAK;EACZ,MAAM;EACN,OAAO;EACP;CACF,CAAC;CAIL,OAAO;AACT;AAUA,MAAa,YAA+B,EAC1C,WACA,UACA,aAAa,OACb,QAAQ,QACR,SACA,UACA,GAAG,YACC;CACJ,MAAM,UAAU,YAAY,UAAU;CACtC,MAAM,QAAQ,OAAO,aAAa,WAAW,SAAS,MAAM,IAAI,IAAI,CAAC;CACrE,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAE/C,MAAM,iBAAiB,MAAuC;EAC5D,IAAI,EAAE,QAAQ,WAAW,WAAW,KAAK,GAAG;GAC1C,WAAW,UAAU;GACrB,cAAc,EAAE;EAClB;CACF;CAGA,MAAM,iBAAiB;CAKvB,OACE,qBAAC,WAAD;EACE,aAAY;EACZ,WAAW,GACT,8DACA,SACF;EACA,GAAI;YANN;GASE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,oBAAC,OAAD,EAAK,WAAU,+BAAgC;MAC/C,oBAAC,OAAD,EAAK,WAAU,oCAAqC;MACpD,oBAAC,OAAD,EAAK,WAAU,mCAAoC;KAChD;QACL,oBAAC,OAAD;KAAK,WAAU;eACb,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD,YAAO,MAAY,IAClB,WACC,oBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,cAAY,QAAQ,SAAS;iBAC9B;MAEO,EAEP;;IACF,EACF;;GAGL,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,QAAD,YACG,MAAM,KAAK,MAAM,cAAc;KAC9B,MAAM,WAAW,eAAe,MAAM,UAAU;KAEhD,OACE,oBAAC,QAAD;MAAM,WAAU;gBACb,SAAS,WAAW,IACjB,OACA,SAAS,KAAK,SAAS,aACrB,oBAAC,QAAD;OAEE,WAAW,GAAG,QAAQ,OAAO,EAC3B,aAAa,QAAQ,OACvB,CAAC;iBAEA,QAAQ;MACL,GANC,OAAO,UAAU,GAAG,UAMrB,CACP;KACD,GAbmC,QAAQ,WAa3C;IAEV,CAAC,EACG;GACH;GAGL,qBAAC,WAAD;IAAW,WAAU;cAArB,CACE,oBAAC,QAAD;KAAM,WAAU;eAAe;IAAqB,IACpD,oBAAC,OAAD;KACE,WAAU;KACV,SAAQ;KACR,OAAO;KACP,cAAY,QAAQ,cAAc;KAClC,WAAW,MAAM,cAAc,EAAE,OAAO,KAAK;KAC7C,WAAW;IACZ,EACQ;;EACF;;AAEf"}
@@ -28,7 +28,8 @@ import { jsx } from "react/jsx-runtime";
28
28
  * ```
29
29
  */
30
30
  const AutoCompleteTextarea = ({ isActive = true, suggestion: suggestionProp, ...props }) => {
31
- const [text, setText] = useState(String(props.value ?? props.defaultValue ?? ""));
31
+ const defaultValue = String(props.value ?? props.defaultValue ?? "");
32
+ const [text, setText] = useState(defaultValue);
32
33
  const [suggestion, setSuggestion] = useState("");
33
34
  const editorRef = useRef(null);
34
35
  useEffect(() => {
@@ -49,6 +50,8 @@ const AutoCompleteTextarea = ({ isActive = true, suggestion: suggestionProp, ...
49
50
  };
50
51
  const activeGhost = isActive ? suggestionProp ?? (suggestion || void 0) : void 0;
51
52
  const textLines = text.split("\n");
53
+ const activeLine = suggestionProp ? textLines.length - 1 : void 0;
54
+ const activeOffset = suggestionProp ? textLines[textLines.length - 1]?.length ?? 0 : void 0;
52
55
  return /* @__PURE__ */ jsx(ContentEditableTextArea, {
53
56
  ref: editorRef,
54
57
  value: text,
@@ -71,8 +74,8 @@ const AutoCompleteTextarea = ({ isActive = true, suggestion: suggestionProp, ...
71
74
  props.onKeyDown?.(e);
72
75
  },
73
76
  ghostText: activeGhost,
74
- ghostLine: suggestionProp ? textLines.length - 1 : void 0,
75
- ghostOffset: suggestionProp ? textLines[textLines.length - 1]?.length ?? 0 : void 0,
77
+ ghostLine: activeLine,
78
+ ghostOffset: activeOffset,
76
79
  placeholder: props.placeholder,
77
80
  disabled: props.disabled,
78
81
  autoSize: props.autoSize,
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompleteTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/AutocompleteTextArea.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ChangeEvent,\n type FC,\n type KeyboardEvent,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type { AutoSizedTextAreaProps } from './AutoSizeTextArea';\nimport {\n ContentEditableTextArea,\n type ContentEditableTextAreaHandle,\n} from './ContentEditableTextArea';\n\n/**\n * Props for the AutocompleteTextArea component.\n *\n * Extends AutoSizedTextAreaProps with inline autocomplete functionality\n * using a contentEditable-based textarea.\n *\n * @example\n * ```tsx\n * <AutoCompleteTextarea\n * placeholder=\"Start typing...\"\n * isActive={true}\n * autoSize={true}\n * maxRows={10}\n * />\n * ```\n */\nexport type AutocompleteTextAreaProps = AutoSizedTextAreaProps & {\n /** Whether inline autocomplete ghost text is active */\n isActive?: boolean;\n /** Manual suggestion text to display as ghost text after the cursor */\n suggestion?: string;\n};\n\n/**\n * AutoCompleteTextarea Component\n *\n * A textarea with inline autocomplete ghost text, built on a contentEditable div\n * instead of a native `<textarea>`. Ghost text (suggestions) is rendered inline\n * at the cursor position and can be accepted with the Tab key.\n *\n * The component wraps `ContentEditableTextArea` and manages suggestion state.\n * When `suggestion` prop is provided it is shown as ghost text at the end of the\n * current text. When `isActive` is false, ghost text is hidden.\n *\n * @example\n * ```tsx\n * <AutoCompleteTextarea\n * value={content}\n * onChange={handleChange}\n * suggestion=\"suggested completion...\"\n * isActive={true}\n * autoSize={true}\n * />\n * ```\n */\nexport const AutoCompleteTextarea: FC<AutocompleteTextAreaProps> = ({\n isActive = true,\n suggestion: suggestionProp,\n ...props\n}) => {\n const defaultValue = String(props.value ?? props.defaultValue ?? '');\n const [text, setText] = useState(defaultValue);\n const [suggestion, setSuggestion] = useState('');\n const editorRef = useRef<ContentEditableTextAreaHandle>(null);\n\n useEffect(() => {\n if (typeof props.value === 'undefined') return;\n setText(String(props.value ?? props.defaultValue ?? ''));\n }, [props.value, props.defaultValue]);\n\n const acceptSuggestion = () => {\n const active = suggestionProp ?? suggestion;\n if (!active) return;\n\n const cursor = editorRef.current?.getCursorOffset() ?? text.length;\n const next = text.slice(0, cursor) + active + text.slice(cursor);\n setText(next);\n setSuggestion('');\n\n setTimeout(() => {\n editorRef.current?.focus();\n editorRef.current?.setCursorAtOffset(cursor + active.length);\n }, 0);\n };\n\n const activeGhost = isActive\n ? (suggestionProp ?? (suggestion || undefined))\n : undefined;\n const textLines = text.split('\\n');\n const activeLine = suggestionProp ? textLines.length - 1 : undefined;\n const activeOffset = suggestionProp\n ? (textLines[textLines.length - 1]?.length ?? 0)\n : undefined;\n\n return (\n <ContentEditableTextArea\n ref={editorRef}\n value={text}\n onChange={(val) => {\n setText(val);\n setSuggestion('');\n\n if (props.onChange) {\n const evt = {\n target: { value: val },\n currentTarget: { value: val },\n } as ChangeEvent<HTMLTextAreaElement>;\n props.onChange(evt);\n }\n }}\n onKeyDown={(e) => {\n if (e.key === 'Tab' && (suggestionProp ?? suggestion)) {\n e.preventDefault();\n acceptSuggestion();\n }\n props.onKeyDown?.(e as unknown as KeyboardEvent<HTMLTextAreaElement>);\n }}\n ghostText={activeGhost}\n ghostLine={activeLine}\n ghostOffset={activeOffset}\n placeholder={props.placeholder}\n disabled={props.disabled}\n autoSize={props.autoSize}\n maxRows={props.maxRows}\n minRows={props.rows}\n variant={props.variant}\n validationStyleEnabled={props.validationStyleEnabled}\n className={props.className}\n dir={props.dir as 'ltr' | 'rtl' | 'auto'}\n aria-label={props['aria-label']}\n aria-invalid={props['aria-invalid']}\n aria-describedby={props['aria-describedby']}\n data-testid={(props as Record<string, unknown>)['data-testid'] as string}\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAa,wBAAuD,EAClE,WAAW,MACX,YAAY,gBACZ,GAAG,YACC;CAEJ,MAAM,CAAC,MAAM,WAAW,SADH,OAAO,MAAM,SAAS,MAAM,gBAAgB,EACrB,CAAC;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,YAAY,OAAsC,IAAI;CAE5D,gBAAgB;EACd,IAAI,OAAO,MAAM,UAAU,aAAa;EACxC,QAAQ,OAAO,MAAM,SAAS,MAAM,gBAAgB,EAAE,CAAC;CACzD,GAAG,CAAC,MAAM,OAAO,MAAM,YAAY,CAAC;CAEpC,MAAM,yBAAyB;EAC7B,MAAM,SAAS,kBAAkB;EACjC,IAAI,CAAC,QAAQ;EAEb,MAAM,SAAS,UAAU,SAAS,gBAAgB,KAAK,KAAK;EAC5D,MAAM,OAAO,KAAK,MAAM,GAAG,MAAM,IAAI,SAAS,KAAK,MAAM,MAAM;EAC/D,QAAQ,IAAI;EACZ,cAAc,EAAE;EAEhB,iBAAiB;GACf,UAAU,SAAS,MAAM;GACzB,UAAU,SAAS,kBAAkB,SAAS,OAAO,MAAM;EAC7D,GAAG,CAAC;CACN;CAEA,MAAM,cAAc,WACf,mBAAmB,cAAc,UAClC;CACJ,MAAM,YAAY,KAAK,MAAM,IAAI;CAMjC,OACE,oBAAC,yBAAD;EACE,KAAK;EACL,OAAO;EACP,WAAW,QAAQ;GACjB,QAAQ,GAAG;GACX,cAAc,EAAE;GAEhB,IAAI,MAAM,UAAU;IAClB,MAAM,MAAM;KACV,QAAQ,EAAE,OAAO,IAAI;KACrB,eAAe,EAAE,OAAO,IAAI;IAC9B;IACA,MAAM,SAAS,GAAG;GACpB;EACF;EACA,YAAY,MAAM;GAChB,IAAI,EAAE,QAAQ,UAAU,kBAAkB,aAAa;IACrD,EAAE,eAAe;IACjB,iBAAiB;GACnB;GACA,MAAM,YAAY,CAAkD;EACtE;EACA,WAAW;EACX,WA7Be,iBAAiB,UAAU,SAAS,IAAI;EA8BvD,aA7BiB,iBAChB,UAAU,UAAU,SAAS,EAAE,EAAE,UAAU,IAC5C;EA4BA,aAAa,MAAM;EACnB,UAAU,MAAM;EAChB,UAAU,MAAM;EAChB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,SAAS,MAAM;EACf,wBAAwB,MAAM;EAC9B,WAAW,MAAM;EACjB,KAAK,MAAM;EACX,cAAY,MAAM;EAClB,gBAAc,MAAM;EACpB,oBAAkB,MAAM;EACxB,eAAc,MAAkC;CACjD;AAEL"}
1
+ {"version":3,"file":"AutocompleteTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/AutocompleteTextArea.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ChangeEvent,\n type FC,\n type KeyboardEvent,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type { AutoSizedTextAreaProps } from './AutoSizeTextArea';\nimport {\n ContentEditableTextArea,\n type ContentEditableTextAreaHandle,\n} from './ContentEditableTextArea';\n\n/**\n * Props for the AutocompleteTextArea component.\n *\n * Extends AutoSizedTextAreaProps with inline autocomplete functionality\n * using a contentEditable-based textarea.\n *\n * @example\n * ```tsx\n * <AutoCompleteTextarea\n * placeholder=\"Start typing...\"\n * isActive={true}\n * autoSize={true}\n * maxRows={10}\n * />\n * ```\n */\nexport type AutocompleteTextAreaProps = AutoSizedTextAreaProps & {\n /** Whether inline autocomplete ghost text is active */\n isActive?: boolean;\n /** Manual suggestion text to display as ghost text after the cursor */\n suggestion?: string;\n};\n\n/**\n * AutoCompleteTextarea Component\n *\n * A textarea with inline autocomplete ghost text, built on a contentEditable div\n * instead of a native `<textarea>`. Ghost text (suggestions) is rendered inline\n * at the cursor position and can be accepted with the Tab key.\n *\n * The component wraps `ContentEditableTextArea` and manages suggestion state.\n * When `suggestion` prop is provided it is shown as ghost text at the end of the\n * current text. When `isActive` is false, ghost text is hidden.\n *\n * @example\n * ```tsx\n * <AutoCompleteTextarea\n * value={content}\n * onChange={handleChange}\n * suggestion=\"suggested completion...\"\n * isActive={true}\n * autoSize={true}\n * />\n * ```\n */\nexport const AutoCompleteTextarea: FC<AutocompleteTextAreaProps> = ({\n isActive = true,\n suggestion: suggestionProp,\n ...props\n}) => {\n const defaultValue = String(props.value ?? props.defaultValue ?? '');\n const [text, setText] = useState(defaultValue);\n const [suggestion, setSuggestion] = useState('');\n const editorRef = useRef<ContentEditableTextAreaHandle>(null);\n\n useEffect(() => {\n if (typeof props.value === 'undefined') return;\n setText(String(props.value ?? props.defaultValue ?? ''));\n }, [props.value, props.defaultValue]);\n\n const acceptSuggestion = () => {\n const active = suggestionProp ?? suggestion;\n if (!active) return;\n\n const cursor = editorRef.current?.getCursorOffset() ?? text.length;\n const next = text.slice(0, cursor) + active + text.slice(cursor);\n setText(next);\n setSuggestion('');\n\n setTimeout(() => {\n editorRef.current?.focus();\n editorRef.current?.setCursorAtOffset(cursor + active.length);\n }, 0);\n };\n\n const activeGhost = isActive\n ? (suggestionProp ?? (suggestion || undefined))\n : undefined;\n const textLines = text.split('\\n');\n const activeLine = suggestionProp ? textLines.length - 1 : undefined;\n const activeOffset = suggestionProp\n ? (textLines[textLines.length - 1]?.length ?? 0)\n : undefined;\n\n return (\n <ContentEditableTextArea\n ref={editorRef}\n value={text}\n onChange={(val) => {\n setText(val);\n setSuggestion('');\n\n if (props.onChange) {\n const evt = {\n target: { value: val },\n currentTarget: { value: val },\n } as ChangeEvent<HTMLTextAreaElement>;\n props.onChange(evt);\n }\n }}\n onKeyDown={(e) => {\n if (e.key === 'Tab' && (suggestionProp ?? suggestion)) {\n e.preventDefault();\n acceptSuggestion();\n }\n props.onKeyDown?.(e as unknown as KeyboardEvent<HTMLTextAreaElement>);\n }}\n ghostText={activeGhost}\n ghostLine={activeLine}\n ghostOffset={activeOffset}\n placeholder={props.placeholder}\n disabled={props.disabled}\n autoSize={props.autoSize}\n maxRows={props.maxRows}\n minRows={props.rows}\n variant={props.variant}\n validationStyleEnabled={props.validationStyleEnabled}\n className={props.className}\n dir={props.dir as 'ltr' | 'rtl' | 'auto'}\n aria-label={props['aria-label']}\n aria-invalid={props['aria-invalid']}\n aria-describedby={props['aria-describedby']}\n data-testid={(props as Record<string, unknown>)['data-testid'] as string}\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAa,wBAAuD,EAClE,WAAW,MACX,YAAY,gBACZ,GAAG,YACC;CACJ,MAAM,eAAe,OAAO,MAAM,SAAS,MAAM,gBAAgB,EAAE;CACnE,MAAM,CAAC,MAAM,WAAW,SAAS,YAAY;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,YAAY,OAAsC,IAAI;CAE5D,gBAAgB;EACd,IAAI,OAAO,MAAM,UAAU,aAAa;EACxC,QAAQ,OAAO,MAAM,SAAS,MAAM,gBAAgB,EAAE,CAAC;CACzD,GAAG,CAAC,MAAM,OAAO,MAAM,YAAY,CAAC;CAEpC,MAAM,yBAAyB;EAC7B,MAAM,SAAS,kBAAkB;EACjC,IAAI,CAAC,QAAQ;EAEb,MAAM,SAAS,UAAU,SAAS,gBAAgB,KAAK,KAAK;EAC5D,MAAM,OAAO,KAAK,MAAM,GAAG,MAAM,IAAI,SAAS,KAAK,MAAM,MAAM;EAC/D,QAAQ,IAAI;EACZ,cAAc,EAAE;EAEhB,iBAAiB;GACf,UAAU,SAAS,MAAM;GACzB,UAAU,SAAS,kBAAkB,SAAS,OAAO,MAAM;EAC7D,GAAG,CAAC;CACN;CAEA,MAAM,cAAc,WACf,mBAAmB,cAAc,UAClC;CACJ,MAAM,YAAY,KAAK,MAAM,IAAI;CACjC,MAAM,aAAa,iBAAiB,UAAU,SAAS,IAAI;CAC3D,MAAM,eAAe,iBAChB,UAAU,UAAU,SAAS,EAAE,EAAE,UAAU,IAC5C;CAEJ,OACE,oBAAC,yBAAD;EACE,KAAK;EACL,OAAO;EACP,WAAW,QAAQ;GACjB,QAAQ,GAAG;GACX,cAAc,EAAE;GAEhB,IAAI,MAAM,UAAU;IAClB,MAAM,MAAM;KACV,QAAQ,EAAE,OAAO,IAAI;KACrB,eAAe,EAAE,OAAO,IAAI;IAC9B;IACA,MAAM,SAAS,GAAG;GACpB;EACF;EACA,YAAY,MAAM;GAChB,IAAI,EAAE,QAAQ,UAAU,kBAAkB,aAAa;IACrD,EAAE,eAAe;IACjB,iBAAiB;GACnB;GACA,MAAM,YAAY,CAAkD;EACtE;EACA,WAAW;EACX,WAAW;EACX,aAAa;EACb,aAAa,MAAM;EACnB,UAAU,MAAM;EAChB,UAAU,MAAM;EAChB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,SAAS,MAAM;EACf,wBAAwB,MAAM;EAC9B,WAAW,MAAM;EACjB,KAAK,MAAM;EACX,cAAY,MAAM;EAClB,gBAAc,MAAM;EACpB,oBAAkB,MAAM;EACxB,eAAc,MAAkC;CACjD;AAEL"}
@@ -4,8 +4,8 @@ import { Button } from "../Button/Button.mjs";
4
4
  import { Container } from "../Container/index.mjs";
5
5
  import { DropDown } from "../DropDown/index.mjs";
6
6
  import { useState } from "react";
7
- import { CircleDashed, Moon, Sun } from "lucide-react";
8
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { CircleDashed, Moon, Sun } from "lucide-react";
9
9
 
10
10
  //#region src/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.tsx
11
11
  const ButtonItem = ({ Icon, children, ...props }) => /* @__PURE__ */ jsx("div", {
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
4
  import { useState } from "react";
5
- import { CircleDashed, Moon, Sun } from "lucide-react";
6
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { CircleDashed, Moon, Sun } from "lucide-react";
7
7
 
8
8
  //#region src/components/ThemeSwitcherDropDown/MobileThemeSwitcher.tsx
9
9
  const getIconStyle = ({ isCurrentMode, isNextMode }) => cn(`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2`, `opacity-0 transition-opacity duration-300 ease-in-out`, isCurrentMode && `opacity-100 group-hover:opacity-0`, isNextMode && `group-hover:opacity-100`);
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
+ import { jsx } from "react/jsx-runtime";
4
5
  import * as ToastPrimitives from "@radix-ui/react-toast";
5
6
  import { cva } from "class-variance-authority";
6
7
  import { X } from "lucide-react";
7
- import { jsx } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/components/Toaster/Toast.tsx
10
10
  const ToastProvider = ToastPrimitives;
@@ -12,7 +12,7 @@ const TOAST_LIMIT = 1;
12
12
  * Delay before automatically removing dismissed toasts from memory.
13
13
  * Set to 15 minutes (900,000ms) to allow for potential undo actions.
14
14
  */
15
- const TOAST_REMOVE_DELAY = 900 * 1e3;
15
+ const TOAST_REMOVE_DELAY = 9e5;
16
16
  let count = 0;
17
17
  const genId = () => {
18
18
  count = (count + 1) % Number.MAX_SAFE_INTEGER;
@@ -1 +1 @@
1
- {"version":3,"file":"useToast.mjs","names":[],"sources":["../../../../src/components/Toaster/useToast.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode, useEffect, useState } from 'react';\nimport type { ToastActionElement, ToastProps } from './Toast';\n\n/**\n * Maximum number of toasts that can be displayed simultaneously.\n * Prevents UI overflow and maintains clean notification experience.\n */\nconst TOAST_LIMIT = 1;\n\n/**\n * Delay before automatically removing dismissed toasts from memory.\n * Set to 15 minutes (900,000ms) to allow for potential undo actions.\n */\nconst TOAST_REMOVE_DELAY = 15 * 60 * 1000; // 15 seconds\n\n/**\n * Extended toast configuration with additional properties for the toast system.\n *\n * Combines base ToastProps with specific fields needed for toast management\n * including unique identification and content elements.\n *\n * @example\n * ```tsx\n * const toast: ToasterToast = {\n * id: '1',\n * variant: 'success',\n * title: 'Success!',\n * description: 'Your action completed successfully.',\n * action: <ToastAction altText=\"View details\">View</ToastAction>\n * };\n * ```\n */\ntype ToasterToast = ToastProps & {\n /** Unique identifier for the toast instance */\n id: string;\n /** Optional title text or React element */\n title?: ReactNode;\n /** Optional description text or React element */\n description?: ReactNode;\n /** Optional action button element */\n action?: ToastActionElement;\n};\n\n/**\n * Action types for toast state management using reducer pattern.\n *\n * Defines all possible actions that can be performed on the toast state,\n * enabling predictable state updates and better debugging.\n */\nenum ActionTypes {\n /** Add a new toast to the display queue */\n ADD_TOAST = 'ADD_TOAST',\n /** Update properties of an existing toast */\n UPDATE_TOAST = 'UPDATE_TOAST',\n /** Mark a toast as dismissed (triggers exit animation) */\n DISMISS_TOAST = 'DISMISS_TOAST',\n /** Completely remove a toast from memory */\n REMOVE_TOAST = 'REMOVE_TOAST',\n}\n\nlet count = 0;\n\nconst genId = () => {\n count = (count + 1) % Number.MAX_SAFE_INTEGER;\n return count.toString();\n};\n\ntype Action =\n | {\n type: ActionTypes.ADD_TOAST;\n toast: ToasterToast;\n }\n | {\n type: ActionTypes.UPDATE_TOAST;\n toast: Partial<ToasterToast>;\n }\n | {\n type: ActionTypes.DISMISS_TOAST;\n toastId?: ToasterToast['id'];\n }\n | {\n type: ActionTypes.REMOVE_TOAST;\n toastId?: ToasterToast['id'];\n };\n\ntype State = {\n toasts: ToasterToast[];\n};\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();\n\nconst addToRemoveQueue = (toastId: string) => {\n if (toastTimeouts.has(toastId)) {\n return;\n }\n\n const timeout = setTimeout(() => {\n toastTimeouts.delete(toastId);\n dispatch({\n type: ActionTypes.REMOVE_TOAST,\n toastId: toastId,\n });\n }, TOAST_REMOVE_DELAY);\n\n toastTimeouts.set(toastId, timeout);\n};\n\n/**\n * Toast state reducer function that handles all toast state transitions.\n *\n * Implements predictable state updates using the reducer pattern, ensuring\n * consistent behavior across all toast operations.\n *\n * ## State Management\n * - **ADD_TOAST**: Adds new toast and enforces limit by removing excess toasts\n * - **UPDATE_TOAST**: Updates existing toast properties while preserving identity\n * - **DISMISS_TOAST**: Marks toasts as closed and schedules removal\n * - **REMOVE_TOAST**: Permanently removes toasts from state\n *\n * ## Side Effects\n * The DISMISS_TOAST action includes side effects for scheduling toast removal,\n * which could be extracted but is kept here for simplicity.\n *\n * @param state - Current toast state\n * @param action - Action to perform on the state\n * @returns Updated toast state\n *\n * @example\n * ```tsx\n * // Add a new toast\n * const newState = reducer(state, {\n * type: ActionTypes.ADD_TOAST,\n * toast: { id: '1', title: 'Hello', variant: 'default' }\n * });\n *\n * // Dismiss a specific toast\n * const dismissedState = reducer(state, {\n * type: ActionTypes.DISMISS_TOAST,\n * toastId: '1'\n * });\n * ```\n */\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case ActionTypes.ADD_TOAST:\n return {\n ...state,\n toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),\n };\n\n case ActionTypes.UPDATE_TOAST:\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === action.toast.id ? { ...t, ...action.toast } : t\n ),\n };\n\n case ActionTypes.DISMISS_TOAST: {\n const { toastId } = action;\n\n // ! Side effects ! - This could be extracted into a dismissToast() action,\n // but I'll keep it here for simplicity\n if (toastId) {\n addToRemoveQueue(toastId);\n } else {\n state.toasts.forEach((toast) => {\n addToRemoveQueue(toast.id);\n });\n }\n\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === toastId || toastId === undefined\n ? {\n ...t,\n open: false,\n }\n : t\n ),\n };\n }\n case ActionTypes.REMOVE_TOAST:\n if (action.toastId === undefined) {\n return {\n ...state,\n toasts: [],\n };\n }\n return {\n ...state,\n toasts: state.toasts.filter((toast) => toast.id !== action.toastId),\n };\n }\n};\n\nconst listeners: ((state: State) => void)[] = [];\n\nlet memoryState: State = { toasts: [] };\n\nconst dispatch = (action: Action) => {\n memoryState = reducer(memoryState, action);\n listeners.forEach((listener) => {\n listener(memoryState);\n });\n};\n\n/**\n * Toast configuration type for creating new toasts.\n * Omits the 'id' field as it's automatically generated.\n */\ntype Toast = Omit<ToasterToast, 'id'>;\n\n/**\n * Creates and displays a new toast notification.\n *\n * This is the primary function for showing toast notifications to users.\n * It automatically generates unique IDs, handles state updates, and provides\n * control functions for managing the toast lifecycle.\n *\n * ## Features\n * - **Automatic ID Generation**: Each toast gets a unique identifier\n * - **State Management**: Integrates with global toast state\n * - **Lifecycle Control**: Returns functions to update or dismiss the toast\n * - **Auto-dismiss**: Automatically closes when user dismisses\n *\n * ## Return Value\n * Returns an object with control functions:\n * - `id`: Unique identifier for the toast\n * - `dismiss()`: Function to manually dismiss the toast\n * - `update()`: Function to update toast properties\n *\n * @param props - Toast configuration (title, description, variant, etc.)\n * @returns Object with toast ID and control functions\n *\n * @example\n * ```tsx\n * // Basic success toast\n * const { dismiss } = toast({\n * title: 'Success!',\n * description: 'Your file was uploaded successfully.',\n * variant: 'success'\n * });\n *\n * // Error toast with retry action\n * const errorToast = toast({\n * title: 'Upload Failed',\n * description: 'Could not upload file. Please try again.',\n * variant: 'error',\n * action: <ToastAction altText=\"Retry upload\">Retry</ToastAction>\n * });\n *\n * // Update toast content dynamically\n * errorToast.update({\n * title: 'Retrying...',\n * description: 'Please wait while we retry the upload.',\n * variant: 'default'\n * });\n *\n * // Manually dismiss toast\n * setTimeout(() => errorToast.dismiss(), 5000);\n * ```\n */\nconst toast = ({ ...props }: Toast) => {\n const id = genId();\n\n const update = (props: ToasterToast) =>\n dispatch({\n type: ActionTypes.UPDATE_TOAST,\n toast: { ...props, id },\n });\n const dismiss = () =>\n dispatch({ type: ActionTypes.DISMISS_TOAST, toastId: id });\n\n dispatch({\n type: ActionTypes.ADD_TOAST,\n toast: {\n ...props,\n id,\n open: true,\n onOpenChange: (open) => {\n if (!open) dismiss();\n },\n },\n });\n\n return {\n id: id,\n dismiss,\n update,\n };\n};\n\n/**\n * React hook for managing toast notifications.\n *\n * Provides access to the global toast state and functions for creating and\n * managing toast notifications. This hook connects components to the toast\n * system and ensures reactive updates when toasts change.\n *\n * ## Features\n * - **State Synchronization**: Automatically updates when toast state changes\n * - **Toast Creation**: Provides the `toast()` function for creating notifications\n * - **Batch Dismissal**: Can dismiss all toasts or specific toasts by ID\n * - **Memory Management**: Handles proper cleanup of listeners\n *\n * ## Return Value\n * - `toasts`: Array of current toast objects\n * - `toast()`: Function to create new toast notifications\n * - `dismiss()`: Function to dismiss toasts (all or by ID)\n *\n * @returns Toast state and control functions\n *\n * @example\n * ```tsx\n * function NotificationButton() {\n * const { toast, toasts, dismiss } = useToast();\n *\n * const showSuccess = () => {\n * toast({\n * title: 'Success!',\n * description: 'Operation completed successfully.',\n * variant: 'success'\n * });\n * };\n *\n * const clearAll = () => {\n * dismiss(); // Dismisses all toasts\n * };\n *\n * return (\n * <div>\n * <button onClick={showSuccess}>Show Success</button>\n * <button onClick={clearAll}>Clear All ({toasts.length})</button>\n * </div>\n * );\n * }\n *\n * // Usage in a form component\n * function ContactForm() {\n * const { toast } = useToast();\n *\n * const handleSubmit = async (data) => {\n * try {\n * await submitForm(data);\n * toast({\n * title: 'Form Submitted',\n * description: 'We\\'ll get back to you soon!',\n * variant: 'success'\n * });\n * } catch (error) {\n * toast({\n * title: 'Submission Failed',\n * description: 'Please check your connection and try again.',\n * variant: 'error',\n * action: <ToastAction altText=\"Retry\">Retry</ToastAction>\n * });\n * }\n * };\n *\n * // ... form JSX\n * }\n * ```\n *\n * ## Usage Notes\n * - Must be used within a component tree that includes `<Toaster />`\n * - Toast state is global - changes affect all components using this hook\n * - Toasts are automatically cleaned up after the configured delay\n * - Consider UX best practices: don't overwhelm users with too many toasts\n */\nconst useToast = () => {\n const [state, setState] = useState<State>(memoryState);\n\n useEffect(() => {\n listeners.push(setState);\n return () => {\n const index = listeners.indexOf(setState);\n if (index > -1) {\n listeners.splice(index, 1);\n }\n };\n }, [state]);\n\n return {\n ...state,\n toast,\n dismiss: (toastId?: string) =>\n dispatch({ type: ActionTypes.DISMISS_TOAST, toastId }),\n };\n};\n\nexport { toast, useToast };\n"],"mappings":";;;;;;;;;AASA,MAAM,cAAc;;;;;AAMpB,MAAM,qBAAqB,MAAU;AA+CrC,IAAI,QAAQ;AAEZ,MAAM,cAAc;CAClB,SAAS,QAAQ,KAAK,OAAO;CAC7B,OAAO,MAAM,SAAS;AACxB;AAwBA,MAAM,gCAAgB,IAAI,IAA2C;AAErE,MAAM,oBAAoB,YAAoB;CAC5C,IAAI,cAAc,IAAI,OAAO,GAC3B;CAGF,MAAM,UAAU,iBAAiB;EAC/B,cAAc,OAAO,OAAO;EAC5B,SAAS;GACP;GACS;EACX,CAAC;CACH,GAAG,kBAAkB;CAErB,cAAc,IAAI,SAAS,OAAO;AACpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAa,WAAW,OAAc,WAA0B;CAC9D,QAAQ,OAAO,MAAf;EACE,kBACE,OAAO;GACL,GAAG;GACH,QAAQ,CAAC,OAAO,OAAO,GAAG,MAAM,MAAM,CAAC,CAAC,MAAM,GAAG,WAAW;EAC9D;EAEF,qBACE,OAAO;GACL,GAAG;GACH,QAAQ,MAAM,OAAO,KAAK,MACxB,EAAE,OAAO,OAAO,MAAM,KAAK;IAAE,GAAG;IAAG,GAAG,OAAO;GAAM,IAAI,CACzD;EACF;EAEF,sBAAgC;GAC9B,MAAM,EAAE,YAAY;GAIpB,IAAI,SACF,iBAAiB,OAAO;QAExB,MAAM,OAAO,SAAS,UAAU;IAC9B,iBAAiB,MAAM,EAAE;GAC3B,CAAC;GAGH,OAAO;IACL,GAAG;IACH,QAAQ,MAAM,OAAO,KAAK,MACxB,EAAE,OAAO,WAAW,YAAY,SAC5B;KACE,GAAG;KACH,MAAM;IACR,IACA,CACN;GACF;EACF;EACA;GACE,IAAI,OAAO,YAAY,QACrB,OAAO;IACL,GAAG;IACH,QAAQ,CAAC;GACX;GAEF,OAAO;IACL,GAAG;IACH,QAAQ,MAAM,OAAO,QAAQ,UAAU,MAAM,OAAO,OAAO,OAAO;GACpE;CACJ;AACF;AAEA,MAAM,YAAwC,CAAC;AAE/C,IAAI,cAAqB,EAAE,QAAQ,CAAC,EAAE;AAEtC,MAAM,YAAY,WAAmB;CACnC,cAAc,QAAQ,aAAa,MAAM;CACzC,UAAU,SAAS,aAAa;EAC9B,SAAS,WAAW;CACtB,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,MAAM,SAAS,EAAE,GAAG,YAAmB;CACrC,MAAM,KAAK,MAAM;CAEjB,MAAM,UAAU,UACd,SAAS;EACP;EACA,OAAO;GAAE,GAAG;GAAO;EAAG;CACxB,CAAC;CACH,MAAM,gBACJ,SAAS;EAAE;EAAiC,SAAS;CAAG,CAAC;CAE3D,SAAS;EACP;EACA,OAAO;GACL,GAAG;GACH;GACA,MAAM;GACN,eAAe,SAAS;IACtB,IAAI,CAAC,MAAM,QAAQ;GACrB;EACF;CACF,CAAC;CAED,OAAO;EACD;EACJ;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+EA,MAAM,iBAAiB;CACrB,MAAM,CAAC,OAAO,YAAY,SAAgB,WAAW;CAErD,gBAAgB;EACd,UAAU,KAAK,QAAQ;EACvB,aAAa;GACX,MAAM,QAAQ,UAAU,QAAQ,QAAQ;GACxC,IAAI,QAAQ,IACV,UAAU,OAAO,OAAO,CAAC;EAE7B;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,OAAO;EACL,GAAG;EACH;EACA,UAAU,YACR,SAAS;GAAE;GAAiC;EAAQ,CAAC;CACzD;AACF"}
1
+ {"version":3,"file":"useToast.mjs","names":[],"sources":["../../../../src/components/Toaster/useToast.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode, useEffect, useState } from 'react';\nimport type { ToastActionElement, ToastProps } from './Toast';\n\n/**\n * Maximum number of toasts that can be displayed simultaneously.\n * Prevents UI overflow and maintains clean notification experience.\n */\nconst TOAST_LIMIT = 1;\n\n/**\n * Delay before automatically removing dismissed toasts from memory.\n * Set to 15 minutes (900,000ms) to allow for potential undo actions.\n */\nconst TOAST_REMOVE_DELAY = 15 * 60 * 1000; // 15 seconds\n\n/**\n * Extended toast configuration with additional properties for the toast system.\n *\n * Combines base ToastProps with specific fields needed for toast management\n * including unique identification and content elements.\n *\n * @example\n * ```tsx\n * const toast: ToasterToast = {\n * id: '1',\n * variant: 'success',\n * title: 'Success!',\n * description: 'Your action completed successfully.',\n * action: <ToastAction altText=\"View details\">View</ToastAction>\n * };\n * ```\n */\ntype ToasterToast = ToastProps & {\n /** Unique identifier for the toast instance */\n id: string;\n /** Optional title text or React element */\n title?: ReactNode;\n /** Optional description text or React element */\n description?: ReactNode;\n /** Optional action button element */\n action?: ToastActionElement;\n};\n\n/**\n * Action types for toast state management using reducer pattern.\n *\n * Defines all possible actions that can be performed on the toast state,\n * enabling predictable state updates and better debugging.\n */\nenum ActionTypes {\n /** Add a new toast to the display queue */\n ADD_TOAST = 'ADD_TOAST',\n /** Update properties of an existing toast */\n UPDATE_TOAST = 'UPDATE_TOAST',\n /** Mark a toast as dismissed (triggers exit animation) */\n DISMISS_TOAST = 'DISMISS_TOAST',\n /** Completely remove a toast from memory */\n REMOVE_TOAST = 'REMOVE_TOAST',\n}\n\nlet count = 0;\n\nconst genId = () => {\n count = (count + 1) % Number.MAX_SAFE_INTEGER;\n return count.toString();\n};\n\ntype Action =\n | {\n type: ActionTypes.ADD_TOAST;\n toast: ToasterToast;\n }\n | {\n type: ActionTypes.UPDATE_TOAST;\n toast: Partial<ToasterToast>;\n }\n | {\n type: ActionTypes.DISMISS_TOAST;\n toastId?: ToasterToast['id'];\n }\n | {\n type: ActionTypes.REMOVE_TOAST;\n toastId?: ToasterToast['id'];\n };\n\ntype State = {\n toasts: ToasterToast[];\n};\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();\n\nconst addToRemoveQueue = (toastId: string) => {\n if (toastTimeouts.has(toastId)) {\n return;\n }\n\n const timeout = setTimeout(() => {\n toastTimeouts.delete(toastId);\n dispatch({\n type: ActionTypes.REMOVE_TOAST,\n toastId: toastId,\n });\n }, TOAST_REMOVE_DELAY);\n\n toastTimeouts.set(toastId, timeout);\n};\n\n/**\n * Toast state reducer function that handles all toast state transitions.\n *\n * Implements predictable state updates using the reducer pattern, ensuring\n * consistent behavior across all toast operations.\n *\n * ## State Management\n * - **ADD_TOAST**: Adds new toast and enforces limit by removing excess toasts\n * - **UPDATE_TOAST**: Updates existing toast properties while preserving identity\n * - **DISMISS_TOAST**: Marks toasts as closed and schedules removal\n * - **REMOVE_TOAST**: Permanently removes toasts from state\n *\n * ## Side Effects\n * The DISMISS_TOAST action includes side effects for scheduling toast removal,\n * which could be extracted but is kept here for simplicity.\n *\n * @param state - Current toast state\n * @param action - Action to perform on the state\n * @returns Updated toast state\n *\n * @example\n * ```tsx\n * // Add a new toast\n * const newState = reducer(state, {\n * type: ActionTypes.ADD_TOAST,\n * toast: { id: '1', title: 'Hello', variant: 'default' }\n * });\n *\n * // Dismiss a specific toast\n * const dismissedState = reducer(state, {\n * type: ActionTypes.DISMISS_TOAST,\n * toastId: '1'\n * });\n * ```\n */\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case ActionTypes.ADD_TOAST:\n return {\n ...state,\n toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),\n };\n\n case ActionTypes.UPDATE_TOAST:\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === action.toast.id ? { ...t, ...action.toast } : t\n ),\n };\n\n case ActionTypes.DISMISS_TOAST: {\n const { toastId } = action;\n\n // ! Side effects ! - This could be extracted into a dismissToast() action,\n // but I'll keep it here for simplicity\n if (toastId) {\n addToRemoveQueue(toastId);\n } else {\n state.toasts.forEach((toast) => {\n addToRemoveQueue(toast.id);\n });\n }\n\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === toastId || toastId === undefined\n ? {\n ...t,\n open: false,\n }\n : t\n ),\n };\n }\n case ActionTypes.REMOVE_TOAST:\n if (action.toastId === undefined) {\n return {\n ...state,\n toasts: [],\n };\n }\n return {\n ...state,\n toasts: state.toasts.filter((toast) => toast.id !== action.toastId),\n };\n }\n};\n\nconst listeners: ((state: State) => void)[] = [];\n\nlet memoryState: State = { toasts: [] };\n\nconst dispatch = (action: Action) => {\n memoryState = reducer(memoryState, action);\n listeners.forEach((listener) => {\n listener(memoryState);\n });\n};\n\n/**\n * Toast configuration type for creating new toasts.\n * Omits the 'id' field as it's automatically generated.\n */\ntype Toast = Omit<ToasterToast, 'id'>;\n\n/**\n * Creates and displays a new toast notification.\n *\n * This is the primary function for showing toast notifications to users.\n * It automatically generates unique IDs, handles state updates, and provides\n * control functions for managing the toast lifecycle.\n *\n * ## Features\n * - **Automatic ID Generation**: Each toast gets a unique identifier\n * - **State Management**: Integrates with global toast state\n * - **Lifecycle Control**: Returns functions to update or dismiss the toast\n * - **Auto-dismiss**: Automatically closes when user dismisses\n *\n * ## Return Value\n * Returns an object with control functions:\n * - `id`: Unique identifier for the toast\n * - `dismiss()`: Function to manually dismiss the toast\n * - `update()`: Function to update toast properties\n *\n * @param props - Toast configuration (title, description, variant, etc.)\n * @returns Object with toast ID and control functions\n *\n * @example\n * ```tsx\n * // Basic success toast\n * const { dismiss } = toast({\n * title: 'Success!',\n * description: 'Your file was uploaded successfully.',\n * variant: 'success'\n * });\n *\n * // Error toast with retry action\n * const errorToast = toast({\n * title: 'Upload Failed',\n * description: 'Could not upload file. Please try again.',\n * variant: 'error',\n * action: <ToastAction altText=\"Retry upload\">Retry</ToastAction>\n * });\n *\n * // Update toast content dynamically\n * errorToast.update({\n * title: 'Retrying...',\n * description: 'Please wait while we retry the upload.',\n * variant: 'default'\n * });\n *\n * // Manually dismiss toast\n * setTimeout(() => errorToast.dismiss(), 5000);\n * ```\n */\nconst toast = ({ ...props }: Toast) => {\n const id = genId();\n\n const update = (props: ToasterToast) =>\n dispatch({\n type: ActionTypes.UPDATE_TOAST,\n toast: { ...props, id },\n });\n const dismiss = () =>\n dispatch({ type: ActionTypes.DISMISS_TOAST, toastId: id });\n\n dispatch({\n type: ActionTypes.ADD_TOAST,\n toast: {\n ...props,\n id,\n open: true,\n onOpenChange: (open) => {\n if (!open) dismiss();\n },\n },\n });\n\n return {\n id: id,\n dismiss,\n update,\n };\n};\n\n/**\n * React hook for managing toast notifications.\n *\n * Provides access to the global toast state and functions for creating and\n * managing toast notifications. This hook connects components to the toast\n * system and ensures reactive updates when toasts change.\n *\n * ## Features\n * - **State Synchronization**: Automatically updates when toast state changes\n * - **Toast Creation**: Provides the `toast()` function for creating notifications\n * - **Batch Dismissal**: Can dismiss all toasts or specific toasts by ID\n * - **Memory Management**: Handles proper cleanup of listeners\n *\n * ## Return Value\n * - `toasts`: Array of current toast objects\n * - `toast()`: Function to create new toast notifications\n * - `dismiss()`: Function to dismiss toasts (all or by ID)\n *\n * @returns Toast state and control functions\n *\n * @example\n * ```tsx\n * function NotificationButton() {\n * const { toast, toasts, dismiss } = useToast();\n *\n * const showSuccess = () => {\n * toast({\n * title: 'Success!',\n * description: 'Operation completed successfully.',\n * variant: 'success'\n * });\n * };\n *\n * const clearAll = () => {\n * dismiss(); // Dismisses all toasts\n * };\n *\n * return (\n * <div>\n * <button onClick={showSuccess}>Show Success</button>\n * <button onClick={clearAll}>Clear All ({toasts.length})</button>\n * </div>\n * );\n * }\n *\n * // Usage in a form component\n * function ContactForm() {\n * const { toast } = useToast();\n *\n * const handleSubmit = async (data) => {\n * try {\n * await submitForm(data);\n * toast({\n * title: 'Form Submitted',\n * description: 'We\\'ll get back to you soon!',\n * variant: 'success'\n * });\n * } catch (error) {\n * toast({\n * title: 'Submission Failed',\n * description: 'Please check your connection and try again.',\n * variant: 'error',\n * action: <ToastAction altText=\"Retry\">Retry</ToastAction>\n * });\n * }\n * };\n *\n * // ... form JSX\n * }\n * ```\n *\n * ## Usage Notes\n * - Must be used within a component tree that includes `<Toaster />`\n * - Toast state is global - changes affect all components using this hook\n * - Toasts are automatically cleaned up after the configured delay\n * - Consider UX best practices: don't overwhelm users with too many toasts\n */\nconst useToast = () => {\n const [state, setState] = useState<State>(memoryState);\n\n useEffect(() => {\n listeners.push(setState);\n return () => {\n const index = listeners.indexOf(setState);\n if (index > -1) {\n listeners.splice(index, 1);\n }\n };\n }, [state]);\n\n return {\n ...state,\n toast,\n dismiss: (toastId?: string) =>\n dispatch({ type: ActionTypes.DISMISS_TOAST, toastId }),\n };\n};\n\nexport { toast, useToast };\n"],"mappings":";;;;;;;;;AASA,MAAM,cAAc;;;;;AAMpB,MAAM,qBAAqB;AA+C3B,IAAI,QAAQ;AAEZ,MAAM,cAAc;CAClB,SAAS,QAAQ,KAAK,OAAO;CAC7B,OAAO,MAAM,SAAS;AACxB;AAwBA,MAAM,gCAAgB,IAAI,IAA2C;AAErE,MAAM,oBAAoB,YAAoB;CAC5C,IAAI,cAAc,IAAI,OAAO,GAC3B;CAGF,MAAM,UAAU,iBAAiB;EAC/B,cAAc,OAAO,OAAO;EAC5B,SAAS;GACP;GACS;EACX,CAAC;CACH,GAAG,kBAAkB;CAErB,cAAc,IAAI,SAAS,OAAO;AACpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAa,WAAW,OAAc,WAA0B;CAC9D,QAAQ,OAAO,MAAf;EACE,kBACE,OAAO;GACL,GAAG;GACH,QAAQ,CAAC,OAAO,OAAO,GAAG,MAAM,MAAM,CAAC,CAAC,MAAM,GAAG,WAAW;EAC9D;EAEF,qBACE,OAAO;GACL,GAAG;GACH,QAAQ,MAAM,OAAO,KAAK,MACxB,EAAE,OAAO,OAAO,MAAM,KAAK;IAAE,GAAG;IAAG,GAAG,OAAO;GAAM,IAAI,CACzD;EACF;EAEF,sBAAgC;GAC9B,MAAM,EAAE,YAAY;GAIpB,IAAI,SACF,iBAAiB,OAAO;QAExB,MAAM,OAAO,SAAS,UAAU;IAC9B,iBAAiB,MAAM,EAAE;GAC3B,CAAC;GAGH,OAAO;IACL,GAAG;IACH,QAAQ,MAAM,OAAO,KAAK,MACxB,EAAE,OAAO,WAAW,YAAY,SAC5B;KACE,GAAG;KACH,MAAM;IACR,IACA,CACN;GACF;EACF;EACA;GACE,IAAI,OAAO,YAAY,QACrB,OAAO;IACL,GAAG;IACH,QAAQ,CAAC;GACX;GAEF,OAAO;IACL,GAAG;IACH,QAAQ,MAAM,OAAO,QAAQ,UAAU,MAAM,OAAO,OAAO,OAAO;GACpE;CACJ;AACF;AAEA,MAAM,YAAwC,CAAC;AAE/C,IAAI,cAAqB,EAAE,QAAQ,CAAC,EAAE;AAEtC,MAAM,YAAY,WAAmB;CACnC,cAAc,QAAQ,aAAa,MAAM;CACzC,UAAU,SAAS,aAAa;EAC9B,SAAS,WAAW;CACtB,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,MAAM,SAAS,EAAE,GAAG,YAAmB;CACrC,MAAM,KAAK,MAAM;CAEjB,MAAM,UAAU,UACd,SAAS;EACP;EACA,OAAO;GAAE,GAAG;GAAO;EAAG;CACxB,CAAC;CACH,MAAM,gBACJ,SAAS;EAAE;EAAiC,SAAS;CAAG,CAAC;CAE3D,SAAS;EACP;EACA,OAAO;GACL,GAAG;GACH;GACA,MAAM;GACN,eAAe,SAAS;IACtB,IAAI,CAAC,MAAM,QAAQ;GACrB;EACF;CACF,CAAC;CAED,OAAO;EACD;EACJ;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+EA,MAAM,iBAAiB;CACrB,MAAM,CAAC,OAAO,YAAY,SAAgB,WAAW;CAErD,gBAAgB;EACd,UAAU,KAAK,QAAQ;EACvB,aAAa;GACX,MAAM,QAAQ,UAAU,QAAQ,QAAQ;GACxC,IAAI,QAAQ,IACV,UAAU,OAAO,OAAO,CAAC;EAE7B;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,OAAO;EACL,GAAG;EACH;EACA,UAAU,YACR,SAAS;GAAE;GAAiC;EAAQ,CAAC;CACzD;AACF"}