@intlayer/design-system 8.10.0 → 8.11.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/esm/components/Browser/Browser.mjs +12 -4
  2. package/dist/esm/components/Browser/Browser.mjs.map +1 -1
  3. package/dist/esm/components/Button/Button.mjs +1 -1
  4. package/dist/esm/components/Button/Button.mjs.map +1 -1
  5. package/dist/esm/components/Carousel/index.mjs +3 -3
  6. package/dist/esm/components/Carousel/index.mjs.map +1 -1
  7. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
  8. package/dist/esm/components/DictionaryEditor/NodeWrapper/ConditionWrapper.mjs +2 -2
  9. package/dist/esm/components/DictionaryEditor/NodeWrapper/ConditionWrapper.mjs.map +1 -1
  10. package/dist/esm/components/DictionaryEditor/NodeWrapper/EnumerationWrapper.mjs +2 -2
  11. package/dist/esm/components/DictionaryEditor/NodeWrapper/EnumerationWrapper.mjs.map +1 -1
  12. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +2 -2
  13. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
  14. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
  15. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +3 -3
  16. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +1 -1
  17. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +1 -1
  18. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +2 -2
  19. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +1 -1
  20. package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs +2 -2
  21. package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs.map +1 -1
  22. package/dist/esm/components/Form/elements/OTPElement.mjs +1 -1
  23. package/dist/esm/components/Form/layout/FormItemLayout.mjs +1 -1
  24. package/dist/esm/components/Form/layout/FormItemLayout.mjs.map +1 -1
  25. package/dist/esm/components/IDE/FileList.mjs +1 -1
  26. package/dist/esm/components/IDE/FileList.mjs.map +1 -1
  27. package/dist/esm/components/IDE/IDE.mjs +1 -1
  28. package/dist/esm/components/IDE/IDE.mjs.map +1 -1
  29. package/dist/esm/components/Input/Checkbox.mjs +1 -1
  30. package/dist/esm/components/Input/Checkbox.mjs.map +1 -1
  31. package/dist/esm/components/LanguageBackground/LanguageSection.mjs +85 -0
  32. package/dist/esm/components/LanguageBackground/LanguageSection.mjs.map +1 -0
  33. package/dist/esm/components/LanguageBackground/index.mjs +10 -77
  34. package/dist/esm/components/LanguageBackground/index.mjs.map +1 -1
  35. package/dist/esm/components/Loader/spinner.mjs +1 -1
  36. package/dist/esm/components/Loader/spinner.mjs.map +1 -1
  37. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +1 -1
  38. package/dist/esm/components/Modal/Modal.mjs +2 -2
  39. package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
  40. package/dist/esm/components/Pagination/Pagination.mjs +1 -1
  41. package/dist/esm/components/Popover/dynamic.mjs +2 -2
  42. package/dist/esm/components/Popover/dynamic.mjs.map +1 -1
  43. package/dist/esm/components/RightDrawer/RightDrawer.mjs +3 -3
  44. package/dist/esm/components/Tab/Tab.mjs +1 -1
  45. package/dist/esm/components/Tag/index.mjs +1 -1
  46. package/dist/esm/components/Tag/index.mjs.map +1 -1
  47. package/dist/esm/components/Terminal/Terminal.mjs +1 -1
  48. package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
  49. package/dist/esm/components/TextArea/AutoSizeTextArea.mjs +2 -2
  50. package/dist/esm/components/TextArea/AutoSizeTextArea.mjs.map +1 -1
  51. package/dist/esm/components/TextArea/ContentEditableTextArea.mjs +3 -3
  52. package/dist/esm/components/TextArea/ContentEditableTextArea.mjs.map +1 -1
  53. package/dist/esm/components/Toaster/Toast.mjs +1 -1
  54. package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
  55. package/dist/esm/components/WithResizer/index.mjs +7 -2
  56. package/dist/esm/components/WithResizer/index.mjs.map +1 -1
  57. package/dist/esm/components/index.mjs +2 -1
  58. package/dist/esm/hooks/index.mjs +11 -11
  59. package/dist/esm/hooks/reactQuery.mjs +400 -1
  60. package/dist/esm/hooks/reactQuery.mjs.map +1 -1
  61. package/dist/esm/routes.mjs +20 -1
  62. package/dist/esm/routes.mjs.map +1 -1
  63. package/dist/types/components/Badge/index.d.ts +1 -1
  64. package/dist/types/components/Browser/Browser.d.ts.map +1 -1
  65. package/dist/types/components/Button/Button.d.ts +3 -3
  66. package/dist/types/components/Carousel/index.d.ts.map +1 -1
  67. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +2 -2
  68. package/dist/types/components/Command/index.d.ts +1 -1
  69. package/dist/types/components/Container/index.d.ts +6 -6
  70. package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
  71. package/dist/types/components/ExpandCollapse/ExpandCollapse.d.ts.map +1 -1
  72. package/dist/types/components/Input/Checkbox.d.ts +1 -1
  73. package/dist/types/components/LanguageBackground/LanguageSection.d.ts +7 -0
  74. package/dist/types/components/LanguageBackground/LanguageSection.d.ts.map +1 -0
  75. package/dist/types/components/LanguageBackground/index.d.ts +2 -2
  76. package/dist/types/components/LanguageBackground/index.d.ts.map +1 -1
  77. package/dist/types/components/Link/Link.d.ts +3 -3
  78. package/dist/types/components/Loader/spinner.d.ts +1 -1
  79. package/dist/types/components/Pagination/Pagination.d.ts +1 -1
  80. package/dist/types/components/TabSelector/TabSelector.d.ts +1 -1
  81. package/dist/types/components/Tag/index.d.ts +2 -2
  82. package/dist/types/components/TextArea/ContentEditableTextArea.d.ts.map +1 -1
  83. package/dist/types/components/index.d.ts +2 -1
  84. package/dist/types/hooks/index.d.ts +2 -2
  85. package/dist/types/hooks/reactQuery.d.ts +50 -2
  86. package/dist/types/hooks/reactQuery.d.ts.map +1 -1
  87. package/dist/types/routes.d.ts +20 -1
  88. package/dist/types/routes.d.ts.map +1 -1
  89. package/package.json +23 -23
@@ -12,6 +12,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
12
12
  import { useIntlayer } from "react-intlayer";
13
13
 
14
14
  //#region src/components/Browser/Browser.tsx
15
+ const UrlPath = ({ url }) => {
16
+ const parts = getUrlPath(url).split("/").filter(Boolean);
17
+ if (parts.length === 0) return /* @__PURE__ */ jsx("span", { children: "/" });
18
+ return parts.flatMap((part, index, array) => [/* @__PURE__ */ jsx("span", { children: part }, `part-${index}`), index < array.length - 1 && /* @__PURE__ */ jsx("span", {
19
+ className: "mx-2 text-neutral",
20
+ children: "/"
21
+ }, `sep-${index}`)]);
22
+ };
15
23
  const getUrlPath = (url) => {
16
24
  try {
17
25
  const { pathname, search, hash } = new URL(url);
@@ -232,7 +240,7 @@ const Browser = ({ initialUrl = "https://example.com", path, className, style, s
232
240
  /* @__PURE__ */ jsx("label", {
233
241
  htmlFor: "browser-url",
234
242
  className: "sr-only",
235
- children: content.urlLabel.value
243
+ children: content.urlLabel
236
244
  }),
237
245
  /* @__PURE__ */ jsx(Input, {
238
246
  id: "browser-url",
@@ -312,10 +320,10 @@ const Browser = ({ initialUrl = "https://example.com", path, className, style, s
312
320
  "aria-label": content.sitemapButtonLabel.value,
313
321
  children: sitemapLoading ? /* @__PURE__ */ jsx("li", {
314
322
  className: "px-3 py-4 text-center text-neutral text-xs",
315
- children: content.sitemapLoading.value
323
+ children: content.sitemapLoading
316
324
  }) : sitemapError || !sitemapLoading && filteredSitemapUrls.length === 0 ? /* @__PURE__ */ jsx("li", {
317
325
  className: "px-3 py-4 text-center text-neutral text-xs",
318
- children: sitemapError ? content.sitemapError.value : content.sitemapEmpty.value
326
+ children: sitemapError ? content.sitemapError : content.sitemapEmpty
319
327
  }) : filteredSitemapUrls.map((url) => /* @__PURE__ */ jsx("li", {
320
328
  className: "py-0.5",
321
329
  children: /* @__PURE__ */ jsx(Button, {
@@ -330,7 +338,7 @@ const Browser = ({ initialUrl = "https://example.com", path, className, style, s
330
338
  },
331
339
  children: /* @__PURE__ */ jsx("span", {
332
340
  className: "max-w-64 truncate text-left text-base",
333
- children: getUrlPath(url)
341
+ children: /* @__PURE__ */ jsx(UrlPath, { url })
334
342
  })
335
343
  })
336
344
  }, url))
@@ -1 +1 @@
1
- {"version":3,"file":"Browser.mjs","names":[],"sources":["../../../../src/components/Browser/Browser.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { cn } from '@utils/cn';\nimport {\n ArrowLeft,\n ArrowRight,\n Globe,\n List,\n RotateCw,\n ScanSearch,\n} from 'lucide-react';\nimport {\n type CSSProperties,\n type HTMLAttributes,\n type RefObject,\n type SubmitEvent,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { ClickOutsideDiv } from '../ClickOutsideDiv';\nimport { DropDown } from '../DropDown';\nimport { Input, inputVariants } from '../Input';\n\nexport type BrowserProps = {\n initialUrl?: string;\n path?: string;\n className?: string;\n style?: CSSProperties;\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n 'aria-label'?: string;\n sandbox?: string;\n ref?: RefObject<HTMLIFrameElement | null>;\n domainRestriction?: string;\n} & HTMLAttributes<HTMLIFrameElement>;\n\nconst getUrlPath = (url: string) => {\n try {\n const { pathname, search, hash } = new URL(url);\n return `${pathname}${search}${hash}` || '/';\n } catch {\n return url;\n }\n};\n\nexport const Browser = ({\n initialUrl = 'https://example.com',\n path,\n className,\n style,\n size = 'md',\n 'aria-label': ariaLabel,\n sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads',\n ref,\n domainRestriction,\n ...props\n}: BrowserProps) => {\n // --- State -----------------------------------------------------------------\n const [inputUrl, setInputUrl] = useState(initialUrl);\n const [currentUrl, setCurrentUrl] = useState(initialUrl);\n\n // History Management\n const [history, setHistory] = useState<string[]>([initialUrl]);\n const [currentIndex, setCurrentIndex] = useState(0);\n\n const [error, setError] = useState<string | null>(null);\n const [submitted, setSubmitted] = useState(false);\n const internalIframeRef = useRef<HTMLIFrameElement>(null);\n\n // Sitemap explorer state\n const [sitemapOpen, setSitemapOpen] = useState(false);\n const [sitemapUrls, setSitemapUrls] = useState<string[]>([]);\n const [sitemapSearch, setSitemapSearch] = useState('');\n const [sitemapLoading, setSitemapLoading] = useState(false);\n const [sitemapFetched, setSitemapFetched] = useState(false);\n const [sitemapError, setSitemapError] = useState(false);\n const sitemapInputRef = useRef<HTMLInputElement>(null);\n\n useImperativeHandle(ref, () => internalIframeRef.current!, []);\n const content = useIntlayer('browser');\n\n // --- Effects ---------------------------------------------------------------\n\n // Reset everything if initialUrl changes completely\n useEffect(() => {\n setInputUrl(initialUrl);\n setCurrentUrl(initialUrl);\n setHistory([initialUrl]);\n setCurrentIndex(0);\n setError(null);\n setSubmitted(false);\n setSitemapOpen(false);\n setSitemapUrls([]);\n setSitemapSearch('');\n setSitemapFetched(false);\n setSitemapError(false);\n }, [initialUrl]);\n\n // Sync external path changes with the URL bar and History\n useEffect(() => {\n if (!path) return;\n\n try {\n const baseOrigin = domainRestriction ?? initialUrl;\n const origin = new URL(baseOrigin).origin;\n const fullUrl = `${origin}${path}`;\n\n // Update Input (Always update the visual bar)\n setInputUrl(fullUrl);\n\n // Check internal iframe state to avoid reload if already there\n let isAlreadyAtUrl = false;\n if (internalIframeRef.current?.contentWindow) {\n try {\n const currentIframeHref =\n internalIframeRef.current.contentWindow.location.href;\n if (new URL(currentIframeHref).href === new URL(fullUrl).href) {\n isAlreadyAtUrl = true;\n }\n } catch {\n // Cross-origin access ignored\n }\n }\n\n // Update History Stack regardless of isAlreadyAtUrl so arrow navigation is always correct\n if (history[currentIndex] !== fullUrl) {\n setHistory((prev) => {\n const newHistory = prev.slice(0, currentIndex + 1);\n newHistory.push(fullUrl);\n return newHistory;\n });\n setCurrentIndex((prev) => prev + 1);\n }\n\n // Navigate only if NOT already there to avoid refreshing on internal iframe navigation\n if (!isAlreadyAtUrl) {\n setCurrentUrl(fullUrl);\n }\n\n setError(null);\n } catch {\n // Ignore invalid paths\n }\n }, [path, domainRestriction, initialUrl]); // Removed currentIndex dependency to prevent loops\n\n // Imperatively keep the iframe src in sync with currentUrl.\n // React's attribute reconciliation can be unreliable for cross-origin iframes after\n // internal navigation, so this effect is the source of truth for navigation.\n useEffect(() => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n if (iframe.src !== currentUrl) {\n iframe.src = currentUrl;\n }\n }, [currentUrl]);\n\n // --- Navigation Logic ------------------------------------------------------\n\n const handleNavigateTo = (url: string) => {\n try {\n const validated = normalizeUrl(url);\n\n // If we are navigating to the exact same URL, just reload\n if (validated === currentUrl) {\n handleReload();\n return;\n }\n\n setCurrentUrl(validated);\n setInputUrl(validated);\n setError(null);\n\n // Update History: Slice future if we went back, then push new\n const newHistory = history.slice(0, currentIndex + 1);\n newHistory.push(validated);\n setHistory(newHistory);\n setCurrentIndex(newHistory.length - 1);\n } catch (e) {\n if (\n e instanceof Error &&\n e.message === 'URL does not match allowed domain' &&\n domainRestriction\n ) {\n setError(\n content.domainRestrictionError?.value ??\n `Only URLs from ${domainRestriction} are allowed.`\n );\n } else {\n setError(content.errorMessage.value);\n }\n }\n };\n\n const handleBack = () => {\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1;\n const prevUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(prevUrl);\n setInputUrl(prevUrl);\n setError(null);\n }\n };\n\n const handleForward = () => {\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1;\n const nextUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(nextUrl);\n setInputUrl(nextUrl);\n setError(null);\n }\n };\n\n const handleSubmit = (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault();\n setSubmitted(true);\n handleNavigateTo(inputUrl);\n };\n\n const handleReload = () => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n const src = iframe.src;\n iframe.src = '';\n setTimeout(() => {\n if (internalIframeRef.current) internalIframeRef.current.src = src;\n }, 50);\n };\n\n // --- Validation Helpers ----------------------------------------------------\n const isValidHostname = (host: string) => {\n if (host === 'localhost') return true;\n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(host)) return true;\n if (/^[a-f0-9:]+$/i.test(host)) return true;\n if (!/^[a-z0-9.-]+$/i.test(host)) return false;\n if (/^[-.]/.test(host) || /[-.]$/.test(host)) return false;\n if (host.includes('..')) return false;\n if (!host.includes('.')) return false;\n return true;\n };\n\n const getRestrictionOrigin = (): URL | null => {\n if (!domainRestriction) return null;\n try {\n return new URL(domainRestriction);\n } catch {\n return null;\n }\n };\n\n const normalizeUrl = (raw: string) => {\n const trimmed = raw.trim();\n if (!trimmed || /\\s/.test(trimmed)) throw new Error('Invalid');\n\n const restrictionOrigin = getRestrictionOrigin();\n const isRelativePath = trimmed.startsWith('/') && !trimmed.startsWith('//');\n\n if (isRelativePath) {\n if (restrictionOrigin) {\n return new URL(`${restrictionOrigin.origin}${trimmed}`).toString();\n }\n return new URL(`${new URL(currentUrl).origin}${trimmed}`).toString();\n }\n\n const hasProtocol = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed);\n const candidate = hasProtocol ? trimmed : `https://${trimmed}`;\n const url = new URL(candidate);\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('Only http(s) is allowed');\n }\n\n if (!isValidHostname(url.hostname)) throw new Error('Invalid host');\n\n if (restrictionOrigin) {\n const urlMatches =\n url.hostname === restrictionOrigin.hostname &&\n url.protocol === restrictionOrigin.protocol &&\n (restrictionOrigin.port === '' ||\n url.port === restrictionOrigin.port ||\n url.host === restrictionOrigin.host);\n\n if (!urlMatches) throw new Error('URL does not match allowed domain');\n }\n\n return url.toString();\n };\n\n const handleSitemapToggle = async () => {\n const nextOpen = !sitemapOpen;\n setSitemapOpen(nextOpen);\n\n if (nextOpen && !sitemapFetched) {\n setSitemapLoading(true);\n setSitemapError(false);\n setSitemapFetched(true);\n try {\n const { extractUrlFromSitemap } = await import(\n './extractUrlFromSitemap'\n );\n const urls = await extractUrlFromSitemap(currentUrl);\n setSitemapUrls(urls);\n if (urls.length === 0) setSitemapError(false);\n } catch {\n setSitemapError(true);\n } finally {\n setSitemapLoading(false);\n }\n setTimeout(() => sitemapInputRef.current?.focus(), 50);\n }\n };\n\n const filteredSitemapUrls = useMemo(() => {\n const query = sitemapSearch.trim().toLowerCase();\n if (!query) return sitemapUrls;\n return sitemapUrls.filter((url) => url.toLowerCase().includes(query));\n }, [sitemapUrls, sitemapSearch]);\n\n const showError = submitted && !!error;\n const canGoBack = currentIndex > 0;\n const canGoForward = currentIndex < history.length - 1;\n\n return (\n <section\n className={cn(\n 'flex w-full flex-col overflow-hidden rounded-xl bg-background shadow-[0_4px_12px_rgba(0,0,0,0.4),0_0_1px_rgba(0,0,0,0.2)]',\n className\n )}\n style={style}\n aria-label={ariaLabel ?? content.ariaLabel.value}\n >\n {/* Top bar */}\n <div className=\"relative z-10 flex shrink-0 items-center gap-3 rounded-t-xl bg-text/15 px-4 py-2\">\n {/* Navigation Controls */}\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n onClick={handleBack}\n disabled={!canGoBack}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.backButtonLabel.value}\n Icon={ArrowLeft}\n />\n <Button\n type=\"button\"\n onClick={handleForward}\n disabled={!canGoForward}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.forwardButtonLabel.value}\n Icon={ArrowRight}\n />\n </div>\n\n {/* URL Bar */}\n <form\n onSubmit={handleSubmit}\n noValidate\n className={cn(\n inputVariants(),\n 'flex w-full gap-2 rounded-xl p-0.5! supports-[corner-shape:squircle]:rounded-2xl',\n 'bg-neutral/10 text-text/50 placeholder:text-neutral/80'\n )}\n >\n <label htmlFor=\"browser-url\" className=\"sr-only\">\n {content.urlLabel.value}\n </label>\n <Input\n id=\"browser-url\"\n type=\"text\"\n inputMode=\"url\"\n spellCheck={false}\n autoCapitalize=\"off\"\n variant=\"invisible\"\n className=\"ml-3 p-0!\"\n size=\"sm\"\n autoCorrect=\"off\"\n value={inputUrl}\n onChange={(e) => {\n setInputUrl(e.target.value);\n if (showError) setError(null);\n }}\n placeholder={content.urlPlaceholder.value}\n aria-label={content.urlLabel.value}\n aria-invalid={showError}\n aria-describedby={showError ? 'browser-url-error' : undefined}\n />\n\n <Button\n type=\"button\"\n onClick={handleReload}\n variant=\"hoverable\"\n size=\"icon-md\"\n className=\"p-1!\"\n label={'content.reloadButtonTitle.value'}\n Icon={RotateCw}\n />\n\n {/* invisible submit */}\n <button type=\"submit\" className=\"sr-only absolute\" tabIndex={-1} />\n </form>\n\n {/* Sitemap Explorer */}\n <ClickOutsideDiv\n onClickOutSide={() => setSitemapOpen(false)}\n disabled={!sitemapOpen}\n role=\"none\"\n >\n <DropDown identifier=\"sitemap-explorer\">\n <DropDown.Trigger\n identifier=\"sitemap-explorer-trigger\"\n type=\"button\"\n color=\"text\"\n onClick={handleSitemapToggle}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.sitemapButtonLabel.value}\n Icon={ScanSearch}\n />\n\n <DropDown.Panel\n identifier=\"sitemap-explorer\"\n isHidden={!sitemapOpen}\n align=\"end\"\n isFocusable\n isOverable\n >\n <Container\n className=\"min-w-28 rounded-md!\"\n roundedSize=\"sm\"\n border\n borderColor=\"neutral\"\n >\n <div className=\"p-2\">\n <Input\n type=\"search\"\n ref={sitemapInputRef}\n aria-label={content.sitemapSearchAriaLabel.value}\n placeholder={content.sitemapSearchPlaceholder.value}\n onChange={(e) => setSitemapSearch(e.target.value)}\n value={sitemapSearch}\n size=\"sm\"\n />\n </div>\n <ul\n className=\"max-h-64 divide-y divide-dotted divide-neutral/30 overflow-y-auto p-1 text-center\"\n aria-label={content.sitemapButtonLabel.value}\n >\n {sitemapLoading ? (\n <li className=\"px-3 py-4 text-center text-neutral text-xs\">\n {content.sitemapLoading.value}\n </li>\n ) : sitemapError ||\n (!sitemapLoading && filteredSitemapUrls.length === 0) ? (\n <li className=\"px-3 py-4 text-center text-neutral text-xs\">\n {sitemapError\n ? content.sitemapError.value\n : content.sitemapEmpty.value}\n </li>\n ) : (\n filteredSitemapUrls.map((url) => (\n <li key={url} className=\"py-0.5\">\n <Button\n variant=\"hoverable\"\n color=\"text\"\n size=\"sm\"\n className=\"w-full text-left\"\n label={url}\n onClick={() => {\n handleNavigateTo(url);\n setSitemapOpen(false);\n }}\n >\n <span className=\"max-w-64 truncate text-left text-base\">\n {getUrlPath(url)}\n </span>\n </Button>\n </li>\n ))\n )}\n </ul>\n </Container>\n </DropDown.Panel>\n </DropDown>\n </ClickOutsideDiv>\n\n {/* Error Message Tooltip */}\n {showError && (\n <div className=\"absolute top-full left-4 z-20 mt-1\">\n <p\n id=\"browser-url-error\"\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"rounded-md bg-red-900/90 px-3 py-1.5 text-red-100 text-xs shadow-md backdrop-blur-sm\"\n >\n {error}\n </p>\n </div>\n )}\n </div>\n\n {/* Iframe */}\n <div className=\"relative z-0 flex min-h-0 w-full flex-1 flex-col overflow-hidden rounded-b-xl bg-background\">\n <iframe\n ref={internalIframeRef}\n src={currentUrl}\n title={content.iframeTitle.value}\n className=\"size-full flex-1\"\n sandbox={sandbox}\n loading=\"lazy\"\n aria-live=\"polite\"\n {...props}\n />\n </div>\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Browser.mjs","names":[],"sources":["../../../../src/components/Browser/Browser.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { cn } from '@utils/cn';\nimport { ArrowLeft, ArrowRight, RotateCw, ScanSearch } from 'lucide-react';\nimport {\n type CSSProperties,\n type HTMLAttributes,\n type RefObject,\n type SubmitEvent,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { ClickOutsideDiv } from '../ClickOutsideDiv';\nimport { DropDown } from '../DropDown';\nimport { Input, inputVariants } from '../Input';\n\nexport type BrowserProps = {\n initialUrl?: string;\n path?: string;\n className?: string;\n style?: CSSProperties;\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n 'aria-label'?: string;\n sandbox?: string;\n ref?: RefObject<HTMLIFrameElement | null>;\n domainRestriction?: string;\n} & HTMLAttributes<HTMLIFrameElement>;\n\nconst UrlPath = ({ url }: { url: string }) => {\n const parts = getUrlPath(url).split('/').filter(Boolean);\n\n if (parts.length === 0) return <span>/</span>;\n\n return parts.flatMap((part, index, array) => [\n <span key={`part-${index}`}>{part}</span>,\n index < array.length - 1 && (\n <span key={`sep-${index}`} className=\"mx-2 text-neutral\">\n /\n </span>\n ),\n ]);\n};\n\nconst getUrlPath = (url: string) => {\n try {\n const { pathname, search, hash } = new URL(url);\n\n return `${pathname}${search}${hash}` || '/';\n } catch {\n return url;\n }\n};\n\nexport const Browser = ({\n initialUrl = 'https://example.com',\n path,\n className,\n style,\n size = 'md',\n 'aria-label': ariaLabel,\n sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads',\n ref,\n domainRestriction,\n ...props\n}: BrowserProps) => {\n // --- State -----------------------------------------------------------------\n const [inputUrl, setInputUrl] = useState(initialUrl);\n const [currentUrl, setCurrentUrl] = useState(initialUrl);\n\n // History Management\n const [history, setHistory] = useState<string[]>([initialUrl]);\n const [currentIndex, setCurrentIndex] = useState(0);\n\n const [error, setError] = useState<string | null>(null);\n const [submitted, setSubmitted] = useState(false);\n const internalIframeRef = useRef<HTMLIFrameElement>(null);\n\n // Sitemap explorer state\n const [sitemapOpen, setSitemapOpen] = useState(false);\n const [sitemapUrls, setSitemapUrls] = useState<string[]>([]);\n const [sitemapSearch, setSitemapSearch] = useState('');\n const [sitemapLoading, setSitemapLoading] = useState(false);\n const [sitemapFetched, setSitemapFetched] = useState(false);\n const [sitemapError, setSitemapError] = useState(false);\n const sitemapInputRef = useRef<HTMLInputElement>(null);\n\n useImperativeHandle(ref, () => internalIframeRef.current!, []);\n const content = useIntlayer('browser');\n\n // --- Effects ---------------------------------------------------------------\n\n // Reset everything if initialUrl changes completely\n useEffect(() => {\n setInputUrl(initialUrl);\n setCurrentUrl(initialUrl);\n setHistory([initialUrl]);\n setCurrentIndex(0);\n setError(null);\n setSubmitted(false);\n setSitemapOpen(false);\n setSitemapUrls([]);\n setSitemapSearch('');\n setSitemapFetched(false);\n setSitemapError(false);\n }, [initialUrl]);\n\n // Sync external path changes with the URL bar and History\n useEffect(() => {\n if (!path) return;\n\n try {\n const baseOrigin = domainRestriction ?? initialUrl;\n const origin = new URL(baseOrigin).origin;\n const fullUrl = `${origin}${path}`;\n\n // Update Input (Always update the visual bar)\n setInputUrl(fullUrl);\n\n // Check internal iframe state to avoid reload if already there\n let isAlreadyAtUrl = false;\n if (internalIframeRef.current?.contentWindow) {\n try {\n const currentIframeHref =\n internalIframeRef.current.contentWindow.location.href;\n if (new URL(currentIframeHref).href === new URL(fullUrl).href) {\n isAlreadyAtUrl = true;\n }\n } catch {\n // Cross-origin access ignored\n }\n }\n\n // Update History Stack regardless of isAlreadyAtUrl so arrow navigation is always correct\n if (history[currentIndex] !== fullUrl) {\n setHistory((prev) => {\n const newHistory = prev.slice(0, currentIndex + 1);\n newHistory.push(fullUrl);\n return newHistory;\n });\n setCurrentIndex((prev) => prev + 1);\n }\n\n // Navigate only if NOT already there to avoid refreshing on internal iframe navigation\n if (!isAlreadyAtUrl) {\n setCurrentUrl(fullUrl);\n }\n\n setError(null);\n } catch {\n // Ignore invalid paths\n }\n }, [path, domainRestriction, initialUrl]); // Removed currentIndex dependency to prevent loops\n\n // Imperatively keep the iframe src in sync with currentUrl.\n // React's attribute reconciliation can be unreliable for cross-origin iframes after\n // internal navigation, so this effect is the source of truth for navigation.\n useEffect(() => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n if (iframe.src !== currentUrl) {\n iframe.src = currentUrl;\n }\n }, [currentUrl]);\n\n // --- Navigation Logic ------------------------------------------------------\n\n const handleNavigateTo = (url: string) => {\n try {\n const validated = normalizeUrl(url);\n\n // If we are navigating to the exact same URL, just reload\n if (validated === currentUrl) {\n handleReload();\n return;\n }\n\n setCurrentUrl(validated);\n setInputUrl(validated);\n setError(null);\n\n // Update History: Slice future if we went back, then push new\n const newHistory = history.slice(0, currentIndex + 1);\n newHistory.push(validated);\n setHistory(newHistory);\n setCurrentIndex(newHistory.length - 1);\n } catch (e) {\n if (\n e instanceof Error &&\n e.message === 'URL does not match allowed domain' &&\n domainRestriction\n ) {\n setError(\n content.domainRestrictionError?.value ??\n `Only URLs from ${domainRestriction} are allowed.`\n );\n } else {\n setError(content.errorMessage.value);\n }\n }\n };\n\n const handleBack = () => {\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1;\n const prevUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(prevUrl);\n setInputUrl(prevUrl);\n setError(null);\n }\n };\n\n const handleForward = () => {\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1;\n const nextUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(nextUrl);\n setInputUrl(nextUrl);\n setError(null);\n }\n };\n\n const handleSubmit = (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault();\n setSubmitted(true);\n handleNavigateTo(inputUrl);\n };\n\n const handleReload = () => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n const src = iframe.src;\n iframe.src = '';\n setTimeout(() => {\n if (internalIframeRef.current) internalIframeRef.current.src = src;\n }, 50);\n };\n\n // --- Validation Helpers ----------------------------------------------------\n const isValidHostname = (host: string) => {\n if (host === 'localhost') return true;\n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(host)) return true;\n if (/^[a-f0-9:]+$/i.test(host)) return true;\n if (!/^[a-z0-9.-]+$/i.test(host)) return false;\n if (/^[-.]/.test(host) || /[-.]$/.test(host)) return false;\n if (host.includes('..')) return false;\n if (!host.includes('.')) return false;\n return true;\n };\n\n const getRestrictionOrigin = (): URL | null => {\n if (!domainRestriction) return null;\n try {\n return new URL(domainRestriction);\n } catch {\n return null;\n }\n };\n\n const normalizeUrl = (raw: string) => {\n const trimmed = raw.trim();\n if (!trimmed || /\\s/.test(trimmed)) throw new Error('Invalid');\n\n const restrictionOrigin = getRestrictionOrigin();\n const isRelativePath = trimmed.startsWith('/') && !trimmed.startsWith('//');\n\n if (isRelativePath) {\n if (restrictionOrigin) {\n return new URL(`${restrictionOrigin.origin}${trimmed}`).toString();\n }\n return new URL(`${new URL(currentUrl).origin}${trimmed}`).toString();\n }\n\n const hasProtocol = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed);\n const candidate = hasProtocol ? trimmed : `https://${trimmed}`;\n const url = new URL(candidate);\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('Only http(s) is allowed');\n }\n\n if (!isValidHostname(url.hostname)) throw new Error('Invalid host');\n\n if (restrictionOrigin) {\n const urlMatches =\n url.hostname === restrictionOrigin.hostname &&\n url.protocol === restrictionOrigin.protocol &&\n (restrictionOrigin.port === '' ||\n url.port === restrictionOrigin.port ||\n url.host === restrictionOrigin.host);\n\n if (!urlMatches) throw new Error('URL does not match allowed domain');\n }\n\n return url.toString();\n };\n\n const handleSitemapToggle = async () => {\n const nextOpen = !sitemapOpen;\n setSitemapOpen(nextOpen);\n\n if (nextOpen && !sitemapFetched) {\n setSitemapLoading(true);\n setSitemapError(false);\n setSitemapFetched(true);\n try {\n const { extractUrlFromSitemap } = await import(\n './extractUrlFromSitemap'\n );\n const urls = await extractUrlFromSitemap(currentUrl);\n setSitemapUrls(urls);\n if (urls.length === 0) setSitemapError(false);\n } catch {\n setSitemapError(true);\n } finally {\n setSitemapLoading(false);\n }\n setTimeout(() => sitemapInputRef.current?.focus(), 50);\n }\n };\n\n const filteredSitemapUrls = useMemo(() => {\n const query = sitemapSearch.trim().toLowerCase();\n if (!query) return sitemapUrls;\n return sitemapUrls.filter((url) => url.toLowerCase().includes(query));\n }, [sitemapUrls, sitemapSearch]);\n\n const showError = submitted && !!error;\n const canGoBack = currentIndex > 0;\n const canGoForward = currentIndex < history.length - 1;\n\n return (\n <section\n className={cn(\n 'flex w-full flex-col overflow-hidden rounded-xl bg-background shadow-[0_4px_12px_rgba(0,0,0,0.4),0_0_1px_rgba(0,0,0,0.2)]',\n className\n )}\n style={style}\n aria-label={ariaLabel ?? content.ariaLabel.value}\n >\n {/* Top bar */}\n <div className=\"relative z-10 flex shrink-0 items-center gap-3 rounded-t-xl bg-text/15 px-4 py-2\">\n {/* Navigation Controls */}\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n onClick={handleBack}\n disabled={!canGoBack}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.backButtonLabel.value}\n Icon={ArrowLeft}\n />\n <Button\n type=\"button\"\n onClick={handleForward}\n disabled={!canGoForward}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.forwardButtonLabel.value}\n Icon={ArrowRight}\n />\n </div>\n\n {/* URL Bar */}\n <form\n onSubmit={handleSubmit}\n noValidate\n className={cn(\n inputVariants(),\n 'flex w-full gap-2 rounded-xl p-0.5! supports-[corner-shape:squircle]:rounded-2xl',\n 'bg-neutral/10 text-text/50 placeholder:text-neutral/80'\n )}\n >\n <label htmlFor=\"browser-url\" className=\"sr-only\">\n {content.urlLabel}\n </label>\n <Input\n id=\"browser-url\"\n type=\"text\"\n inputMode=\"url\"\n spellCheck={false}\n autoCapitalize=\"off\"\n variant=\"invisible\"\n className=\"ml-3 p-0!\"\n size=\"sm\"\n autoCorrect=\"off\"\n value={inputUrl}\n onChange={(e) => {\n setInputUrl(e.target.value);\n if (showError) setError(null);\n }}\n placeholder={content.urlPlaceholder.value}\n aria-label={content.urlLabel.value}\n aria-invalid={showError}\n aria-describedby={showError ? 'browser-url-error' : undefined}\n />\n\n <Button\n type=\"button\"\n onClick={handleReload}\n variant=\"hoverable\"\n size=\"icon-md\"\n className=\"p-1!\"\n label={'content.reloadButtonTitle.value'}\n Icon={RotateCw}\n />\n\n {/* invisible submit */}\n <button type=\"submit\" className=\"sr-only absolute\" tabIndex={-1} />\n </form>\n\n {/* Sitemap Explorer */}\n <ClickOutsideDiv\n onClickOutSide={() => setSitemapOpen(false)}\n disabled={!sitemapOpen}\n role=\"none\"\n >\n <DropDown identifier=\"sitemap-explorer\">\n <DropDown.Trigger\n identifier=\"sitemap-explorer-trigger\"\n type=\"button\"\n color=\"text\"\n onClick={handleSitemapToggle}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.sitemapButtonLabel.value}\n Icon={ScanSearch}\n />\n\n <DropDown.Panel\n identifier=\"sitemap-explorer\"\n isHidden={!sitemapOpen}\n align=\"end\"\n isFocusable\n isOverable\n >\n <Container\n className=\"min-w-28 rounded-md!\"\n roundedSize=\"sm\"\n border\n borderColor=\"neutral\"\n >\n <div className=\"p-2\">\n <Input\n type=\"search\"\n ref={sitemapInputRef}\n aria-label={content.sitemapSearchAriaLabel.value}\n placeholder={content.sitemapSearchPlaceholder.value}\n onChange={(e) => setSitemapSearch(e.target.value)}\n value={sitemapSearch}\n size=\"sm\"\n />\n </div>\n <ul\n className=\"max-h-64 divide-y divide-dotted divide-neutral/30 overflow-y-auto p-1 text-center\"\n aria-label={content.sitemapButtonLabel.value}\n >\n {sitemapLoading ? (\n <li className=\"px-3 py-4 text-center text-neutral text-xs\">\n {content.sitemapLoading}\n </li>\n ) : sitemapError ||\n (!sitemapLoading && filteredSitemapUrls.length === 0) ? (\n <li className=\"px-3 py-4 text-center text-neutral text-xs\">\n {sitemapError\n ? content.sitemapError\n : content.sitemapEmpty}\n </li>\n ) : (\n filteredSitemapUrls.map((url) => (\n <li key={url} className=\"py-0.5\">\n <Button\n variant=\"hoverable\"\n color=\"text\"\n size=\"sm\"\n className=\"w-full text-left\"\n label={url}\n onClick={() => {\n handleNavigateTo(url);\n setSitemapOpen(false);\n }}\n >\n <span className=\"max-w-64 truncate text-left text-base\">\n <UrlPath url={url} />\n </span>\n </Button>\n </li>\n ))\n )}\n </ul>\n </Container>\n </DropDown.Panel>\n </DropDown>\n </ClickOutsideDiv>\n\n {/* Error Message Tooltip */}\n {showError && (\n <div className=\"absolute top-full left-4 z-20 mt-1\">\n <p\n id=\"browser-url-error\"\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"rounded-md bg-red-900/90 px-3 py-1.5 text-red-100 text-xs shadow-md backdrop-blur-sm\"\n >\n {error}\n </p>\n </div>\n )}\n </div>\n\n {/* Iframe */}\n <div className=\"relative z-0 flex min-h-0 w-full flex-1 flex-col overflow-hidden rounded-b-xl bg-background\">\n <iframe\n ref={internalIframeRef}\n src={currentUrl}\n title={content.iframeTitle.value}\n className=\"size-full flex-1\"\n sandbox={sandbox}\n loading=\"lazy\"\n aria-live=\"polite\"\n {...props}\n />\n </div>\n </section>\n );\n};\n"],"mappings":"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"}
@@ -172,7 +172,7 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
172
172
  defaultVariants: {
173
173
  variant: `default`,
174
174
  size: `md`,
175
- color: `custom`,
175
+ color: `text`,
176
176
  roundedSize: `${"md"}`,
177
177
  textAlign: `center`,
178
178
  isFullWidth: false
@@ -1 +1 @@
1
- {"version":3,"file":"Button.mjs","names":[],"sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { LucideIcon } from 'lucide-react';\nimport type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { ContainerRoundedSize as ButtonRoundedSize } from '../Container';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport enum ButtonSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n ICON_SM = 'icon-sm',\n ICON_MD = 'icon-md',\n ICON_LG = 'icon-lg',\n ICON_XL = 'icon-xl',\n}\n\nconst buttonIconVariants = cva('flex-none shrink-0', {\n variants: {\n size: {\n [`${ButtonSize.XS}`]: 'size-2',\n [`${ButtonSize.SM}`]: 'size-3',\n [`${ButtonSize.MD}`]: 'size-4',\n [`${ButtonSize.LG}`]: 'size-5',\n [`${ButtonSize.XL}`]: 'size-6',\n [`${ButtonSize.ICON_SM}`]: 'size-3',\n [`${ButtonSize.ICON_MD}`]: 'size-4',\n [`${ButtonSize.ICON_LG}`]: 'size-5',\n [`${ButtonSize.ICON_XL}`]: 'size-6',\n },\n },\n defaultVariants: {\n size: ButtonSize.MD,\n },\n});\n\n/**\n * Button visual style variants\n */\nexport enum ButtonVariant {\n DEFAULT = 'default',\n NONE = 'none',\n OUTLINE = 'outline',\n LINK = 'link',\n INVISIBLE_LINK = 'invisible-link',\n HOVERABLE = 'hoverable',\n FADE = 'fade',\n INPUT = 'input',\n}\n\n/**\n * Button color themes that work with the design system\n */\nexport enum ButtonColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n DESTRUCTIVE = 'destructive',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n CARD = 'card',\n TEXT_INVERSE = 'text-inverse',\n CURRENT = 'current',\n ERROR = 'error',\n SUCCESS = 'success',\n CUSTOM = 'custom',\n}\n\n/**\n * Text alignment options for button content\n */\nexport enum ButtonTextAlign {\n LEFT = 'left',\n CENTER = 'center',\n RIGHT = 'right',\n}\n\n/**\n * Enhanced button variants with improved accessibility and focus states\n */\nexport const buttonVariants = cva(\n 'relative inline-flex cursor-pointer items-center justify-center font-medium ring-0 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n size: {\n [`${ButtonSize.XS}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.SM}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.MD}`]: 'min-h-8 px-6 text-sm max-md:py-2',\n [`${ButtonSize.LG}`]: 'min-h-10 px-8 text-lg max-md:py-3',\n [`${ButtonSize.XL}`]: 'min-h-11 px-10 text-xl max-md:py-4',\n [`${ButtonSize.ICON_SM}`]: 'p-1.5',\n [`${ButtonSize.ICON_MD}`]: 'p-1.5',\n [`${ButtonSize.ICON_LG}`]: 'p-1.5',\n [`${ButtonSize.ICON_XL}`]: 'p-3',\n },\n color: {\n [`${ButtonColor.PRIMARY}`]:\n 'hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light',\n [`${ButtonColor.SECONDARY}`]:\n 'hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light',\n [`${ButtonColor.DESTRUCTIVE}`]:\n 'hover-destructive-500/20 text-destructive ring-destructive-500/20 *:text-text-light',\n [`${ButtonColor.NEUTRAL}`]:\n 'text-neutral ring-neutral-500/5 *:text-text-light',\n [`${ButtonColor.CARD}`]:\n 'hover-card-500/20 text-card ring-card-500/20 *:text-text-light',\n [`${ButtonColor.LIGHT}`]:\n 'hover-white-500/20 text-white ring-white/20 *:text-text-light',\n [`${ButtonColor.DARK}`]:\n 'text-neutral-800 ring-text-light/50 *:text-text-light',\n [`${ButtonColor.TEXT}`]: 'text-text ring-text/20 *:text-text-opposite',\n [`${ButtonColor.CURRENT}`]:\n 'hover-current-500/10 text-current ring-current/10 *:text-text-light',\n [`${ButtonColor.TEXT_INVERSE}`]:\n 'text-text-opposite ring-text-opposite/20 *:text-text',\n [`${ButtonColor.ERROR}`]:\n 'hover-error-500/20 text-error ring-error/20 *:text-text-light',\n [`${ButtonColor.SUCCESS}`]:\n 'hover-success-500/20 text-success ring-success/20 *:text-text-light',\n [`${ButtonColor.CUSTOM}`]: '',\n },\n roundedSize: {\n [`${ButtonRoundedSize.NONE}`]: 'rounded-none',\n [`${ButtonRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${ButtonRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${ButtonRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${ButtonRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n [`${ButtonRoundedSize['2xl']}`]:\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n [`${ButtonRoundedSize['3xl']}`]:\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n [`${ButtonRoundedSize['4xl']}`]:\n 'rounded-[3rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[4rem]',\n [`${ButtonRoundedSize['5xl']}`]:\n 'rounded-[4rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[5rem]',\n [`${ButtonRoundedSize.FULL}`]: 'rounded-full',\n },\n variant: {\n [`${ButtonVariant.DEFAULT}`]: [\n 'bg-current',\n 'hover:bg-current/90',\n 'hover:ring-5',\n 'aria-selected:ring-5',\n ],\n\n [`${ButtonVariant.OUTLINE}`]: [\n 'rounded-2xl border-[1.3px] border-current bg-current/0 *:text-current!',\n 'hover:bg-current/20 focus-visible:bg-current/20',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n\n [`${ButtonVariant.NONE}`]:\n 'border-none bg-current/0 text-inherit hover:bg-current/0',\n\n [`${ButtonVariant.LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline',\n\n [`${ButtonVariant.INVISIBLE_LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent',\n\n [`${ButtonVariant.HOVERABLE}`]:\n 'rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n\n [`${ButtonVariant.FADE}`]: [\n 'rounded-lg border-none bg-current/10 ring-current/5 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n [`${ButtonVariant.INPUT}`]: [\n // base styles\n 'text-text',\n 'w-full select-text resize-none rounded-2xl text-base shadow-none outline-none supports-[corner-shape:squircle]:rounded-4xl',\n 'transition-shadow duration-100 md:text-sm',\n 'ring-0', // base ring\n 'disabled:opacity-50',\n\n 'text-text',\n 'bg-neutral-50 dark:bg-neutral-950',\n 'ring-neutral-100 dark:ring-neutral-700',\n\n // Hover ring (similar spirit to your input)\n 'hover:ring-3', // width\n 'aria-selected:ring-4',\n 'focus-visible:ring-3',\n 'disabled:ring-0',\n\n // Focus ring + animation\n 'focus-visible:outline-none',\n\n // Remove any weird box-shadow\n '[box-shadow:none] focus:[box-shadow:none]',\n\n // aria-invalid border color\n 'aria-invalid:border-error',\n ],\n },\n\n textAlign: {\n [`${ButtonTextAlign.LEFT}`]: 'justify-start text-left',\n [`${ButtonTextAlign.CENTER}`]: 'justify-center text-center',\n [`${ButtonTextAlign.RIGHT}`]: 'justify-end text-right',\n },\n\n isFullWidth: {\n true: 'w-full',\n false: '',\n },\n },\n defaultVariants: {\n variant: `${ButtonVariant.DEFAULT}`,\n size: `${ButtonSize.MD}`,\n color: `${ButtonColor.CUSTOM}`,\n roundedSize: `${ButtonRoundedSize.MD}`,\n textAlign: `${ButtonTextAlign.CENTER}`,\n isFullWidth: false,\n },\n }\n);\n\n/**\n * Enhanced Button component props with comprehensive type safety and accessibility features\n */\nexport type ButtonProps = DetailedHTMLProps<\n ButtonHTMLAttributes<HTMLButtonElement>,\n HTMLButtonElement\n> &\n VariantProps<typeof buttonVariants> & {\n /**\n * Accessible label for screen readers and assistive technologies.\n * This is required for accessibility compliance.\n */\n label: string | null;\n\n /**\n * Optional icon to display on the left side of the button\n */\n Icon?: FC | LucideIcon;\n\n /**\n * Optional icon to display on the right side of the button\n */\n IconRight?: FC | LucideIcon;\n\n /**\n * Additional CSS classes for icon styling\n */\n iconClassName?: string;\n\n /**\n * Shows loading spinner and disables button interaction when true\n */\n isLoading?: boolean;\n\n /**\n * Marks the button as active (useful for navigation or toggle states)\n */\n isActive?: boolean;\n\n /**\n * Marks the button as selected\n */\n isSelected?: boolean;\n\n /**\n * Makes the button span the full width of its container\n */\n isFullWidth?: boolean;\n\n /**\n * Additional description for complex buttons (optional)\n */\n 'aria-describedby'?: string;\n\n /**\n * Expanded state for collapsible sections (optional)\n */\n 'aria-expanded'?: boolean;\n\n /**\n * Controls whether the button has popup/menu (optional)\n */\n 'aria-haspopup'?:\n | boolean\n | 'true'\n | 'false'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog';\n\n /**\n * Indicates if button controls are currently pressed (for toggle buttons)\n */\n 'aria-pressed'?: boolean;\n };\n\n/**\n * Button Component - A comprehensive, accessible button component\n *\n * Features:\n * - Full accessibility compliance with ARIA attributes\n * - Multiple variants and sizes for different use cases\n * - Icon support (left and right positioning)\n * - Loading states with spinner\n * - Keyboard navigation support\n * - Focus management with visible indicators\n * - Responsive design adaptations\n *\n * @example\n * ```tsx\n * // Basic button\n * <Button label=\"Click me\">Click me</Button>\n *\n * // Button with icon and loading state\n * <Button\n * label=\"Save document\"\n * Icon={SaveIcon}\n * isLoading={saving}\n * disabled={!hasChanges}\n * >\n * Save\n * </Button>\n *\n * // Destructive action button\n * <Button\n * variant={`${ButtonVariant.OUTLINE}`}\n * color={ButtonColor.DESTRUCTIVE}\n * label=\"Delete item permanently\"\n * aria-describedby=\"delete-warning\"\n * >\n * Delete\n * </Button>\n * ```\n */\nexport const Button: FC<ButtonProps> = ({\n variant,\n size,\n color,\n children,\n Icon,\n IconRight,\n iconClassName,\n isLoading = false,\n isActive,\n isSelected,\n isFullWidth,\n roundedSize,\n textAlign,\n disabled,\n label,\n className,\n type = 'button',\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': ariaPressed,\n ...props\n}) => {\n const isLink =\n variant === `${ButtonVariant.LINK}` ||\n variant === `${ButtonVariant.INVISIBLE_LINK}`;\n const isIconOnly = !children && (Icon || IconRight);\n\n const accessibilityProps = {\n 'aria-label': isIconOnly ? (label ?? undefined) : undefined,\n 'aria-labelledby': !isIconOnly ? undefined : undefined,\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': isActive !== undefined ? isActive : ariaPressed,\n 'aria-busy': isLoading,\n 'aria-current': (isActive ? 'page' : undefined) as 'page' | undefined,\n 'aria-disabled': disabled || isLoading,\n 'aria-selected': isSelected,\n };\n\n const isSquareButton =\n size === ButtonSize.ICON_SM ||\n size === ButtonSize.ICON_MD ||\n size === ButtonSize.ICON_LG ||\n size === ButtonSize.ICON_XL;\n\n return (\n <button\n disabled={isLoading || disabled}\n role={isLink ? 'link' : 'button'}\n type={type}\n className={buttonVariants({\n variant,\n size,\n color,\n isFullWidth,\n roundedSize,\n textAlign:\n textAlign ??\n (IconRight ? ButtonTextAlign.LEFT : ButtonTextAlign.CENTER),\n className,\n })}\n {...accessibilityProps}\n {...props}\n >\n {Icon && !isLoading && (\n <Icon\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n\n <div\n className={cn(\n 'flex items-center justify-center transition-[width] duration-300',\n isLoading && size === ButtonSize.SM && 'w-3',\n isLoading && size === ButtonSize.MD && 'w-4',\n isLoading && size === ButtonSize.LG && 'w-6',\n isLoading && size === ButtonSize.XL && 'w-8'\n )}\n >\n <Loader\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n isLoading={isLoading}\n aria-hidden=\"true\"\n data-testid=\"loader\"\n />\n </div>\n\n {children && (\n <span className=\"flex-1 truncate whitespace-nowrap\">{children}</span>\n )}\n\n {!children && isIconOnly && <span className=\"sr-only\">{label}</span>}\n\n {IconRight && (\n <IconRight\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'ml-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n};\n"],"mappings":";;;;;;;;;;AAUA,IAAY,aAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,MAAM,qBAAqB,IAAI,sBAAsB;CACnD,UAAU,EACR,MAAM;GACH,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;CAC7B,EACF;CACA,iBAAiB,EACf,WACF;AACF,CAAC;;;;AAKD,IAAY,gBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,cAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,kBAAL;CACL;CACA;CACA;;AACF;;;;AAKA,MAAa,iBAAiB,IAC5B,8LACA;CACE,UAAU;EACR,MAAM;IACH,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;EAC7B;EACA,OAAO;IACJ,YACC;IACD,cACC;IACD,gBACC;IACD,YACC;IACD,SACC;IACD,UACC;IACD,SACC;IACD,SAAwB;IACxB,YACC;IACD,iBACC;IACD,UACC;IACD,YACC;IACD,WAA0B;EAC7B;EACA,aAAa;IACV,cAA8B;IAC9B,YACC;IACD,YACC;IACD,YACC;IACD,YACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,cAA8B;EACjC;EACA,SAAS;IACN,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,SACC;IAED,SACC;IAED,mBACC;IAED,cACC;IAED,SAA0B;IACzB;IACA;IACA;GACF;IACC,UAA2B;IAE1B;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IAGA;IAGA;GACF;EACF;EAEA,WAAW;IACR,SAA4B;IAC5B,WAA8B;IAC9B,UAA6B;EAChC;EAEA,aAAa;GACX,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;EACP,aAAa;EACb,WAAW;EACX,aAAa;CACf;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,MAAa,UAA2B,EACtC,SACA,MACA,OACA,UACA,MACA,WACA,eACA,YAAY,OACZ,UACA,YACA,aACA,aACA,WACA,UACA,OACA,WACA,OAAO,UACP,oBAAoB,iBACpB,iBAAiB,cACjB,iBAAiB,cACjB,gBAAgB,aAChB,GAAG,YACC;CACJ,MAAM,SACJ,YAAY,UACZ,YAAY;CACd,MAAM,aAAa,CAAC,aAAa,QAAQ;CAEzC,MAAM,qBAAqB;EACzB,cAAc,aAAc,SAAS,SAAa;EAClD,mBAAmB,CAAC,aAAa,SAAY;EAC7C,oBAAoB;EACpB,iBAAiB;EACjB,iBAAiB;EACjB,gBAAgB,aAAa,SAAY,WAAW;EACpD,aAAa;EACb,gBAAiB,WAAW,SAAS;EACrC,iBAAiB,YAAY;EAC7B,iBAAiB;CACnB;CAEA,MAAM,iBACJ,sBACA,sBACA,sBACA;CAEF,OACE,qBAAC,UAAD;EACE,UAAU,aAAa;EACvB,MAAM,SAAS,SAAS;EAClB;EACN,WAAW,eAAe;GACxB;GACA;GACA;GACA;GACA;GACA,WACE,cACC;GACH;EACF,CAAC;EACD,GAAI;EACJ,GAAI;YAhBN;GAkBG,QAAQ,CAAC,aACR,oBAAC,MAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;GAGH,oBAAC,OAAD;IACE,WAAW,GACT,oEACA,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,KACzC;cAEA,oBAAC,QAAD;KACE,WAAW,mBAAmB;MAC5B;MACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;KACxD,CAAC;KACU;KACX,eAAY;KACZ,eAAY;IACb;GACE;GAEJ,YACC,oBAAC,QAAD;IAAM,WAAU;IAAqC;GAAe;GAGrE,CAAC,YAAY,cAAc,oBAAC,QAAD;IAAM,WAAU;cAAW;GAAY;GAElE,aACC,oBAAC,WAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;EAEG;;AAEZ"}
1
+ {"version":3,"file":"Button.mjs","names":[],"sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { LucideIcon } from 'lucide-react';\nimport type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { ContainerRoundedSize as ButtonRoundedSize } from '../Container';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport enum ButtonSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n ICON_SM = 'icon-sm',\n ICON_MD = 'icon-md',\n ICON_LG = 'icon-lg',\n ICON_XL = 'icon-xl',\n}\n\nconst buttonIconVariants = cva('flex-none shrink-0', {\n variants: {\n size: {\n [`${ButtonSize.XS}`]: 'size-2',\n [`${ButtonSize.SM}`]: 'size-3',\n [`${ButtonSize.MD}`]: 'size-4',\n [`${ButtonSize.LG}`]: 'size-5',\n [`${ButtonSize.XL}`]: 'size-6',\n [`${ButtonSize.ICON_SM}`]: 'size-3',\n [`${ButtonSize.ICON_MD}`]: 'size-4',\n [`${ButtonSize.ICON_LG}`]: 'size-5',\n [`${ButtonSize.ICON_XL}`]: 'size-6',\n },\n },\n defaultVariants: {\n size: ButtonSize.MD,\n },\n});\n\n/**\n * Button visual style variants\n */\nexport enum ButtonVariant {\n DEFAULT = 'default',\n NONE = 'none',\n OUTLINE = 'outline',\n LINK = 'link',\n INVISIBLE_LINK = 'invisible-link',\n HOVERABLE = 'hoverable',\n FADE = 'fade',\n INPUT = 'input',\n}\n\n/**\n * Button color themes that work with the design system\n */\nexport enum ButtonColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n DESTRUCTIVE = 'destructive',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n CARD = 'card',\n TEXT_INVERSE = 'text-inverse',\n CURRENT = 'current',\n ERROR = 'error',\n SUCCESS = 'success',\n CUSTOM = 'custom',\n}\n\n/**\n * Text alignment options for button content\n */\nexport enum ButtonTextAlign {\n LEFT = 'left',\n CENTER = 'center',\n RIGHT = 'right',\n}\n\n/**\n * Enhanced button variants with improved accessibility and focus states\n */\nexport const buttonVariants = cva(\n 'relative inline-flex cursor-pointer items-center justify-center font-medium ring-0 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n size: {\n [`${ButtonSize.XS}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.SM}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.MD}`]: 'min-h-8 px-6 text-sm max-md:py-2',\n [`${ButtonSize.LG}`]: 'min-h-10 px-8 text-lg max-md:py-3',\n [`${ButtonSize.XL}`]: 'min-h-11 px-10 text-xl max-md:py-4',\n [`${ButtonSize.ICON_SM}`]: 'p-1.5',\n [`${ButtonSize.ICON_MD}`]: 'p-1.5',\n [`${ButtonSize.ICON_LG}`]: 'p-1.5',\n [`${ButtonSize.ICON_XL}`]: 'p-3',\n },\n color: {\n [`${ButtonColor.PRIMARY}`]:\n 'hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light',\n [`${ButtonColor.SECONDARY}`]:\n 'hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light',\n [`${ButtonColor.DESTRUCTIVE}`]:\n 'hover-destructive-500/20 text-destructive ring-destructive-500/20 *:text-text-light',\n [`${ButtonColor.NEUTRAL}`]:\n 'text-neutral ring-neutral-500/5 *:text-text-light',\n [`${ButtonColor.CARD}`]:\n 'hover-card-500/20 text-card ring-card-500/20 *:text-text-light',\n [`${ButtonColor.LIGHT}`]:\n 'hover-white-500/20 text-white ring-white/20 *:text-text-light',\n [`${ButtonColor.DARK}`]:\n 'text-neutral-800 ring-text-light/50 *:text-text-light',\n [`${ButtonColor.TEXT}`]: 'text-text ring-text/20 *:text-text-opposite',\n [`${ButtonColor.CURRENT}`]:\n 'hover-current-500/10 text-current ring-current/10 *:text-text-light',\n [`${ButtonColor.TEXT_INVERSE}`]:\n 'text-text-opposite ring-text-opposite/20 *:text-text',\n [`${ButtonColor.ERROR}`]:\n 'hover-error-500/20 text-error ring-error/20 *:text-text-light',\n [`${ButtonColor.SUCCESS}`]:\n 'hover-success-500/20 text-success ring-success/20 *:text-text-light',\n [`${ButtonColor.CUSTOM}`]: '',\n },\n roundedSize: {\n [`${ButtonRoundedSize.NONE}`]: 'rounded-none',\n [`${ButtonRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${ButtonRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${ButtonRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${ButtonRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n [`${ButtonRoundedSize['2xl']}`]:\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n [`${ButtonRoundedSize['3xl']}`]:\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n [`${ButtonRoundedSize['4xl']}`]:\n 'rounded-[3rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[4rem]',\n [`${ButtonRoundedSize['5xl']}`]:\n 'rounded-[4rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[5rem]',\n [`${ButtonRoundedSize.FULL}`]: 'rounded-full',\n },\n variant: {\n [`${ButtonVariant.DEFAULT}`]: [\n 'bg-current',\n 'hover:bg-current/90',\n 'hover:ring-5',\n 'aria-selected:ring-5',\n ],\n\n [`${ButtonVariant.OUTLINE}`]: [\n 'rounded-2xl border-[1.3px] border-current bg-current/0 *:text-current!',\n 'hover:bg-current/20 focus-visible:bg-current/20',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n\n [`${ButtonVariant.NONE}`]:\n 'border-none bg-current/0 text-inherit hover:bg-current/0',\n\n [`${ButtonVariant.LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline',\n\n [`${ButtonVariant.INVISIBLE_LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent',\n\n [`${ButtonVariant.HOVERABLE}`]:\n 'rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n\n [`${ButtonVariant.FADE}`]: [\n 'rounded-lg border-none bg-current/10 ring-current/5 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n [`${ButtonVariant.INPUT}`]: [\n // base styles\n 'text-text',\n 'w-full select-text resize-none rounded-2xl text-base shadow-none outline-none supports-[corner-shape:squircle]:rounded-4xl',\n 'transition-shadow duration-100 md:text-sm',\n 'ring-0', // base ring\n 'disabled:opacity-50',\n\n 'text-text',\n 'bg-neutral-50 dark:bg-neutral-950',\n 'ring-neutral-100 dark:ring-neutral-700',\n\n // Hover ring (similar spirit to your input)\n 'hover:ring-3', // width\n 'aria-selected:ring-4',\n 'focus-visible:ring-3',\n 'disabled:ring-0',\n\n // Focus ring + animation\n 'focus-visible:outline-none',\n\n // Remove any weird box-shadow\n '[box-shadow:none] focus:[box-shadow:none]',\n\n // aria-invalid border color\n 'aria-invalid:border-error',\n ],\n },\n\n textAlign: {\n [`${ButtonTextAlign.LEFT}`]: 'justify-start text-left',\n [`${ButtonTextAlign.CENTER}`]: 'justify-center text-center',\n [`${ButtonTextAlign.RIGHT}`]: 'justify-end text-right',\n },\n\n isFullWidth: {\n true: 'w-full',\n false: '',\n },\n },\n defaultVariants: {\n variant: `${ButtonVariant.DEFAULT}`,\n size: `${ButtonSize.MD}`,\n color: `${ButtonColor.TEXT}`,\n roundedSize: `${ButtonRoundedSize.MD}`,\n textAlign: `${ButtonTextAlign.CENTER}`,\n isFullWidth: false,\n },\n }\n);\n\n/**\n * Enhanced Button component props with comprehensive type safety and accessibility features\n */\nexport type ButtonProps = DetailedHTMLProps<\n ButtonHTMLAttributes<HTMLButtonElement>,\n HTMLButtonElement\n> &\n VariantProps<typeof buttonVariants> & {\n /**\n * Accessible label for screen readers and assistive technologies.\n * This is required for accessibility compliance.\n */\n label: string | null;\n\n /**\n * Optional icon to display on the left side of the button\n */\n Icon?: FC | LucideIcon;\n\n /**\n * Optional icon to display on the right side of the button\n */\n IconRight?: FC | LucideIcon;\n\n /**\n * Additional CSS classes for icon styling\n */\n iconClassName?: string;\n\n /**\n * Shows loading spinner and disables button interaction when true\n */\n isLoading?: boolean;\n\n /**\n * Marks the button as active (useful for navigation or toggle states)\n */\n isActive?: boolean;\n\n /**\n * Marks the button as selected\n */\n isSelected?: boolean;\n\n /**\n * Makes the button span the full width of its container\n */\n isFullWidth?: boolean;\n\n /**\n * Additional description for complex buttons (optional)\n */\n 'aria-describedby'?: string;\n\n /**\n * Expanded state for collapsible sections (optional)\n */\n 'aria-expanded'?: boolean;\n\n /**\n * Controls whether the button has popup/menu (optional)\n */\n 'aria-haspopup'?:\n | boolean\n | 'true'\n | 'false'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog';\n\n /**\n * Indicates if button controls are currently pressed (for toggle buttons)\n */\n 'aria-pressed'?: boolean;\n };\n\n/**\n * Button Component - A comprehensive, accessible button component\n *\n * Features:\n * - Full accessibility compliance with ARIA attributes\n * - Multiple variants and sizes for different use cases\n * - Icon support (left and right positioning)\n * - Loading states with spinner\n * - Keyboard navigation support\n * - Focus management with visible indicators\n * - Responsive design adaptations\n *\n * @example\n * ```tsx\n * // Basic button\n * <Button label=\"Click me\">Click me</Button>\n *\n * // Button with icon and loading state\n * <Button\n * label=\"Save document\"\n * Icon={SaveIcon}\n * isLoading={saving}\n * disabled={!hasChanges}\n * >\n * Save\n * </Button>\n *\n * // Destructive action button\n * <Button\n * variant={`${ButtonVariant.OUTLINE}`}\n * color={ButtonColor.DESTRUCTIVE}\n * label=\"Delete item permanently\"\n * aria-describedby=\"delete-warning\"\n * >\n * Delete\n * </Button>\n * ```\n */\nexport const Button: FC<ButtonProps> = ({\n variant,\n size,\n color,\n children,\n Icon,\n IconRight,\n iconClassName,\n isLoading = false,\n isActive,\n isSelected,\n isFullWidth,\n roundedSize,\n textAlign,\n disabled,\n label,\n className,\n type = 'button',\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': ariaPressed,\n ...props\n}) => {\n const isLink =\n variant === `${ButtonVariant.LINK}` ||\n variant === `${ButtonVariant.INVISIBLE_LINK}`;\n const isIconOnly = !children && (Icon || IconRight);\n\n const accessibilityProps = {\n 'aria-label': isIconOnly ? (label ?? undefined) : undefined,\n 'aria-labelledby': !isIconOnly ? undefined : undefined,\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': isActive !== undefined ? isActive : ariaPressed,\n 'aria-busy': isLoading,\n 'aria-current': (isActive ? 'page' : undefined) as 'page' | undefined,\n 'aria-disabled': disabled || isLoading,\n 'aria-selected': isSelected,\n };\n\n const isSquareButton =\n size === ButtonSize.ICON_SM ||\n size === ButtonSize.ICON_MD ||\n size === ButtonSize.ICON_LG ||\n size === ButtonSize.ICON_XL;\n\n return (\n <button\n disabled={isLoading || disabled}\n role={isLink ? 'link' : 'button'}\n type={type}\n className={buttonVariants({\n variant,\n size,\n color,\n isFullWidth,\n roundedSize,\n textAlign:\n textAlign ??\n (IconRight ? ButtonTextAlign.LEFT : ButtonTextAlign.CENTER),\n className,\n })}\n {...accessibilityProps}\n {...props}\n >\n {Icon && !isLoading && (\n <Icon\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n\n <div\n className={cn(\n 'flex items-center justify-center transition-[width] duration-300',\n isLoading && size === ButtonSize.SM && 'w-3',\n isLoading && size === ButtonSize.MD && 'w-4',\n isLoading && size === ButtonSize.LG && 'w-6',\n isLoading && size === ButtonSize.XL && 'w-8'\n )}\n >\n <Loader\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n isLoading={isLoading}\n aria-hidden=\"true\"\n data-testid=\"loader\"\n />\n </div>\n\n {children && (\n <span className=\"flex-1 truncate whitespace-nowrap\">{children}</span>\n )}\n\n {!children && isIconOnly && <span className=\"sr-only\">{label}</span>}\n\n {IconRight && (\n <IconRight\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'ml-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n};\n"],"mappings":";;;;;;;;;;AAUA,IAAY,aAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,MAAM,qBAAqB,IAAI,sBAAsB;CACnD,UAAU,EACR,MAAM;GACH,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;CAC7B,EACF;CACA,iBAAiB,EACf,WACF;AACF,CAAC;;;;AAKD,IAAY,gBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,cAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,kBAAL;CACL;CACA;CACA;;AACF;;;;AAKA,MAAa,iBAAiB,IAC5B,8LACA;CACE,UAAU;EACR,MAAM;IACH,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;EAC7B;EACA,OAAO;IACJ,YACC;IACD,cACC;IACD,gBACC;IACD,YACC;IACD,SACC;IACD,UACC;IACD,SACC;IACD,SAAwB;IACxB,YACC;IACD,iBACC;IACD,UACC;IACD,YACC;IACD,WAA0B;EAC7B;EACA,aAAa;IACV,cAA8B;IAC9B,YACC;IACD,YACC;IACD,YACC;IACD,YACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,cAA8B;EACjC;EACA,SAAS;IACN,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,SACC;IAED,SACC;IAED,mBACC;IAED,cACC;IAED,SAA0B;IACzB;IACA;IACA;GACF;IACC,UAA2B;IAE1B;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IAGA;IAGA;GACF;EACF;EAEA,WAAW;IACR,SAA4B;IAC5B,WAA8B;IAC9B,UAA6B;EAChC;EAEA,aAAa;GACX,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;EACP,aAAa;EACb,WAAW;EACX,aAAa;CACf;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,MAAa,UAA2B,EACtC,SACA,MACA,OACA,UACA,MACA,WACA,eACA,YAAY,OACZ,UACA,YACA,aACA,aACA,WACA,UACA,OACA,WACA,OAAO,UACP,oBAAoB,iBACpB,iBAAiB,cACjB,iBAAiB,cACjB,gBAAgB,aAChB,GAAG,YACC;CACJ,MAAM,SACJ,YAAY,UACZ,YAAY;CACd,MAAM,aAAa,CAAC,aAAa,QAAQ;CAEzC,MAAM,qBAAqB;EACzB,cAAc,aAAc,SAAS,SAAa;EAClD,mBAAmB,CAAC,aAAa,SAAY;EAC7C,oBAAoB;EACpB,iBAAiB;EACjB,iBAAiB;EACjB,gBAAgB,aAAa,SAAY,WAAW;EACpD,aAAa;EACb,gBAAiB,WAAW,SAAS;EACrC,iBAAiB,YAAY;EAC7B,iBAAiB;CACnB;CAEA,MAAM,iBACJ,sBACA,sBACA,sBACA;CAEF,OACE,qBAAC,UAAD;EACE,UAAU,aAAa;EACvB,MAAM,SAAS,SAAS;EAClB;EACN,WAAW,eAAe;GACxB;GACA;GACA;GACA;GACA;GACA,WACE,cACC;GACH;EACF,CAAC;EACD,GAAI;EACJ,GAAI;YAhBN;GAkBG,QAAQ,CAAC,aACR,oBAAC,MAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;GAGH,oBAAC,OAAD;IACE,WAAW,GACT,oEACA,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,KACzC;cAEA,oBAAC,QAAD;KACE,WAAW,mBAAmB;MAC5B;MACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;KACxD,CAAC;KACU;KACX,eAAY;KACZ,eAAY;IACb;GACE;GAEJ,YACC,oBAAC,QAAD;IAAM,WAAU;IAAqC;GAAe;GAGrE,CAAC,YAAY,cAAc,oBAAC,QAAD;IAAM,WAAU;cAAW;GAAY;GAElE,aACC,oBAAC,WAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;EAEG;;AAEZ"}
@@ -4,7 +4,7 @@ import { cn } from "../../utils/cn.mjs";
4
4
  import { Button, ButtonColor, ButtonSize, ButtonVariant } from "../Button/Button.mjs";
5
5
  import { KeyboardShortcut } from "../KeyboardShortcut/KeyboardShortcut.mjs";
6
6
  import { Popover } from "../Popover/dynamic.mjs";
7
- import { Children, createContext, isValidElement, useContext, useEffect, useRef, useState } from "react";
7
+ import { Children, createContext, isValidElement, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
8
8
  import { ChevronLeft, ChevronRight } from "lucide-react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { useIntlayer } from "react-intlayer";
@@ -143,7 +143,7 @@ const CarouselRoot = ({ children, className, initialIndex = 0, onIndexChange, ..
143
143
  const [displayedIndex, setDisplayedIndex] = useState(initialIndex);
144
144
  const [containerHeight, setContainerHeight] = useState(0);
145
145
  const [containerWidth, setContainerWidth] = useState(0);
146
- useEffect(() => {
146
+ useLayoutEffect(() => {
147
147
  const calculateDimensions = () => {
148
148
  if (!containerRef.current) return;
149
149
  const width = containerRef.current.clientWidth;
@@ -216,7 +216,7 @@ const CarouselRoot = ({ children, className, initialIndex = 0, onIndexChange, ..
216
216
  }, TRANSITION_DELAY_MS);
217
217
  return () => clearInterval(interval);
218
218
  }, [selectedIndex, displayedIndex]);
219
- useEffect(() => {
219
+ useLayoutEffect(() => {
220
220
  const calculateMaxHeight = () => {
221
221
  const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);
222
222
  const maxHeight = Math.max(0, ...heights);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Carousel/index.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n ButtonColor,\n ButtonSize,\n ButtonVariant,\n} from '@components/Button';\nimport { KeyboardShortcut } from '@components/KeyboardShortcut';\nimport { Popover } from '@components/Popover';\nimport { cn } from '@utils/cn';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport {\n Children,\n createContext,\n type FC,\n type HTMLAttributes,\n isValidElement,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type TouchEventHandler,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\n\n// ------------------------------------------------------------------\n// Configuration\n// ------------------------------------------------------------------\nconst SWIPE_THRESHOLD_DIVISOR = 5;\nconst TRANSITION_DELAY_MS = 50;\n\n// ------------------------------------------------------------------\n// Context Definition\n// ------------------------------------------------------------------\ntype CarouselContextValue = {\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n totalItems: number;\n handlePrev: () => void;\n handleNext: () => void;\n};\n\nconst CarouselContext = createContext<CarouselContextValue | null>(null);\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext);\n if (!context) {\n throw new Error('useCarousel must be used within a Carousel');\n }\n return context;\n};\n\n// ------------------------------------------------------------------\n// Helper Functions\n// ------------------------------------------------------------------\nconst getCardStyle = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 'opacity-100 z-40';\n case 1:\n return 'opacity-75 z-30 cursor-pointer';\n case 2:\n return 'opacity-50 z-20 pointer-events-none';\n default:\n return 'opacity-0 z-10 pointer-events-none';\n }\n};\n\nconst getCardScale = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 1;\n case 1:\n return 0.9;\n case 2:\n return 0.8;\n default:\n return 0.7;\n }\n};\n\n// This allows the calculation to work on SSR without hydration mismatch.\n// Your original logic: (3 * screenWidth) / 10 === 30% of viewport width\nconst getCardPositionX = (\n index: number,\n displayedIndex: number,\n containerWidth: number\n) => {\n const diff = index - displayedIndex;\n const gapPercentage = containerWidth < 600 ? 0.15 : 0.3; // Dropped to 15% for a tighter cluster\n const step = Math.min(containerWidth * gapPercentage, 300);\n\n // The 'px' here is mandatory\n return `${diff * step}px`;\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Item\n// ------------------------------------------------------------------\ntype CarouselItemProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n};\n\nconst CarouselItem: FC<CarouselItemProps> = ({\n children,\n className,\n ...props\n}) => {\n return (\n <div className={cn('h-full w-full', className)} {...props}>\n {children}\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Indicators (Controller)\n// ------------------------------------------------------------------\ntype CarouselIndicatorsProps = HTMLAttributes<HTMLDivElement> & {\n disableKeyboardShortcuts?: boolean;\n};\n\nconst CarouselIndicators: FC<CarouselIndicatorsProps> = ({\n className,\n disableKeyboardShortcuts = false,\n ...props\n}) => {\n const {\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n } = useCarousel();\n const { goToSlide, previousSlide, nextSlide } = useIntlayer('carousel');\n\n if (totalItems <= 1) return null;\n\n return (\n <div\n className={cn(\n 'absolute bottom-4 left-1/2 z-50 flex -translate-x-1/2 flex-row items-center gap-2',\n className\n )}\n {...props}\n >\n <Popover identifier=\"carousel-prev\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n label={previousSlide.value}\n roundedSize=\"full\"\n onClick={(e) => {\n e.stopPropagation();\n handlePrev();\n }}\n Icon={ChevronLeft}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === 0}\n />\n\n <Popover.Detail identifier=\"carousel-prev\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {previousSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowLeft\"\n disabled={disableKeyboardShortcuts}\n size=\"sm\"\n onTriggered={handlePrev}\n />\n </div>\n </Popover.Detail>\n </Popover>\n\n {Array.from({ length: totalItems }).map((_, index) => {\n const isActive = index === selectedIndex;\n return (\n <button\n key={index}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n aria-label={goToSlide({ index: index + 1 }).value}\n className={cn(\n 'h-2.5 w-2.5 rounded-full transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2',\n isActive ? 'scale-110 bg-text' : 'bg-text/20 hover:bg-text/40'\n )}\n />\n );\n })}\n\n <Popover identifier=\"carousel-next\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n roundedSize=\"full\"\n label={nextSlide.value}\n onClick={(e) => {\n e.stopPropagation();\n handleNext();\n }}\n Icon={ChevronRight}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === totalItems - 1}\n />\n\n <Popover.Detail identifier=\"carousel-next\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {nextSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowRight\"\n size=\"sm\"\n onTriggered={handleNext}\n disabled={disableKeyboardShortcuts}\n />\n </div>\n </Popover.Detail>\n </Popover>\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Main Component: Carousel Root\n// ------------------------------------------------------------------\ntype CarouselProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n initialIndex?: number;\n onIndexChange?: (index: number) => void;\n};\n\nconst partitionCarouselChildren = (\n children: ReactNode[]\n): [ReactElement[], ReactNode[]] => {\n const slides: ReactElement[] = [];\n const others: ReactNode[] = [];\n\n children.forEach((child) => {\n if (isValidElement(child) && child.type === CarouselItem) {\n slides.push(child);\n } else {\n others.push(child);\n }\n });\n\n return [slides, others];\n};\n\nconst CarouselRoot: FC<CarouselProps> = ({\n children,\n className,\n initialIndex = 0,\n onIndexChange,\n ...props\n}) => {\n const allChildren = Children.toArray(children);\n const [slides, others] = partitionCarouselChildren(allChildren);\n const totalItems = slides.length;\n\n // State Management\n const [selectedIndex, setSelectedIndex] = useState<number>(initialIndex);\n const [displayedIndex, setDisplayedIndex] = useState<number>(initialIndex);\n const [containerHeight, setContainerHeight] = useState<number>(0);\n const [containerWidth, setContainerWidth] = useState<number>(0);\n\n useEffect(() => {\n const calculateDimensions = () => {\n if (!containerRef.current) return;\n\n // Track Width\n const width = containerRef.current.clientWidth;\n setContainerWidth(width);\n\n // Track Height (existing logic)\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateDimensions();\n\n const observer = new ResizeObserver(() => {\n calculateDimensions();\n });\n\n if (containerRef.current) observer.observe(containerRef.current);\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]);\n\n // Drag State\n const [startX, setStartX] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n // Refs\n const containerRef = useRef<HTMLDivElement>(null);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n\n // Navigation Logic\n const handleSwitchItem = (diff: number) => {\n if (containerWidth === 0) return;\n\n // Use container width for the threshold\n const swipeStep = containerWidth / SWIPE_THRESHOLD_DIVISOR;\n const numSwipe = Math.round(diff / swipeStep);\n\n if (Math.abs(numSwipe) >= 1) {\n const newIndex = displayedIndex - numSwipe;\n const clampedIndex = Math.max(0, Math.min(newIndex, totalItems - 1));\n\n if (clampedIndex !== selectedIndex) {\n setSelectedIndex(clampedIndex);\n setStartX((prev) => prev + diff);\n }\n }\n };\n\n const handleNext = () => {\n setSelectedIndex((prev) => Math.min(prev + 1, totalItems - 1));\n };\n\n const handlePrev = () => {\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\n };\n\n // Input Handlers\n const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.clientX);\n };\n const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.clientX - startX);\n };\n const handleDragEnd = () => setIsDragging(false);\n const handleTouchStart: TouchEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.touches[0].clientX);\n };\n const handleTouchMove: TouchEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.touches[0].clientX - startX);\n };\n\n // Effects\n useEffect(() => {\n if (selectedIndex) onIndexChange?.(selectedIndex);\n }, [selectedIndex, onIndexChange]);\n\n useEffect(() => {\n let interval: NodeJS.Timeout;\n\n if (selectedIndex !== displayedIndex) {\n interval = setInterval(() => {\n setDisplayedIndex((prev) => {\n if (prev === selectedIndex) {\n clearInterval(interval);\n return prev;\n }\n return prev < selectedIndex ? prev + 1 : prev - 1;\n });\n }, TRANSITION_DELAY_MS);\n }\n return () => clearInterval(interval);\n }, [selectedIndex, displayedIndex]);\n\n // Calculate height based on the MAX height of ALL items\n useEffect(() => {\n const calculateMaxHeight = () => {\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateMaxHeight();\n\n const observer = new ResizeObserver(() => {\n calculateMaxHeight();\n });\n\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]); // Removed isMounted dependency\n\n return (\n <CarouselContext.Provider\n value={{\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n }}\n >\n <div\n ref={containerRef}\n className={cn(\n 'relative flex w-full cursor-grab select-none items-center overflow-hidden outline-none transition-[height] duration-300 ease-in-out focus:outline-none focus:outline-none focus:ring-0 active:cursor-grabbing',\n 'max-w-[1400px]',\n className\n )}\n style={{\n height: containerHeight > 0 ? containerHeight : 'auto',\n minHeight: '400px',\n }}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleDragEnd}\n onMouseLeave={handleDragEnd}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleDragEnd}\n role=\"region\"\n aria-label=\"Carousel\"\n {...props}\n >\n {slides.map((child, index) => {\n return (\n <div\n key={index}\n role=\"button\"\n tabIndex={0}\n ref={(el) => {\n itemsRef.current[index] = el;\n }}\n // FIX 2: Removed isMounted checks and invisible classes.\n // CSS units allow correct SSR rendering.\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 transition-all duration-300 ease-in-out',\n 'outline-none ring-0 focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0',\n getCardStyle(index, displayedIndex)\n )}\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') setSelectedIndex(index);\n }}\n style={{\n transform: `\n translateX(${getCardPositionX(\n index,\n displayedIndex,\n containerWidth\n )})\n scale(${getCardScale(index, displayedIndex)})\n `,\n }}\n >\n {child}\n </div>\n );\n })}\n\n {others}\n </div>\n </CarouselContext.Provider>\n );\n};\n\nexport const Carousel = Object.assign(CarouselRoot, {\n Item: CarouselItem,\n Indicators: CarouselIndicators,\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Carousel/index.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n ButtonColor,\n ButtonSize,\n ButtonVariant,\n} from '@components/Button';\nimport { KeyboardShortcut } from '@components/KeyboardShortcut';\nimport { Popover } from '@components/Popover';\nimport { cn } from '@utils/cn';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport {\n Children,\n createContext,\n type FC,\n type HTMLAttributes,\n isValidElement,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type TouchEventHandler,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\n\n// ------------------------------------------------------------------\n// Configuration\n// ------------------------------------------------------------------\nconst SWIPE_THRESHOLD_DIVISOR = 5;\nconst TRANSITION_DELAY_MS = 50;\n\n// ------------------------------------------------------------------\n// Context Definition\n// ------------------------------------------------------------------\ntype CarouselContextValue = {\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n totalItems: number;\n handlePrev: () => void;\n handleNext: () => void;\n};\n\nconst CarouselContext = createContext<CarouselContextValue | null>(null);\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext);\n if (!context) {\n throw new Error('useCarousel must be used within a Carousel');\n }\n return context;\n};\n\n// ------------------------------------------------------------------\n// Helper Functions\n// ------------------------------------------------------------------\nconst getCardStyle = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 'opacity-100 z-40';\n case 1:\n return 'opacity-75 z-30 cursor-pointer';\n case 2:\n return 'opacity-50 z-20 pointer-events-none';\n default:\n return 'opacity-0 z-10 pointer-events-none';\n }\n};\n\nconst getCardScale = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 1;\n case 1:\n return 0.9;\n case 2:\n return 0.8;\n default:\n return 0.7;\n }\n};\n\n// This allows the calculation to work on SSR without hydration mismatch.\n// Your original logic: (3 * screenWidth) / 10 === 30% of viewport width\nconst getCardPositionX = (\n index: number,\n displayedIndex: number,\n containerWidth: number\n) => {\n const diff = index - displayedIndex;\n const gapPercentage = containerWidth < 600 ? 0.15 : 0.3; // Dropped to 15% for a tighter cluster\n const step = Math.min(containerWidth * gapPercentage, 300);\n\n // The 'px' here is mandatory\n return `${diff * step}px`;\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Item\n// ------------------------------------------------------------------\ntype CarouselItemProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n};\n\nconst CarouselItem: FC<CarouselItemProps> = ({\n children,\n className,\n ...props\n}) => {\n return (\n <div className={cn('h-full w-full', className)} {...props}>\n {children}\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Indicators (Controller)\n// ------------------------------------------------------------------\ntype CarouselIndicatorsProps = HTMLAttributes<HTMLDivElement> & {\n disableKeyboardShortcuts?: boolean;\n};\n\nconst CarouselIndicators: FC<CarouselIndicatorsProps> = ({\n className,\n disableKeyboardShortcuts = false,\n ...props\n}) => {\n const {\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n } = useCarousel();\n const { goToSlide, previousSlide, nextSlide } = useIntlayer('carousel');\n\n if (totalItems <= 1) return null;\n\n return (\n <div\n className={cn(\n 'absolute bottom-4 left-1/2 z-50 flex -translate-x-1/2 flex-row items-center gap-2',\n className\n )}\n {...props}\n >\n <Popover identifier=\"carousel-prev\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n label={previousSlide.value}\n roundedSize=\"full\"\n onClick={(e) => {\n e.stopPropagation();\n handlePrev();\n }}\n Icon={ChevronLeft}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === 0}\n />\n\n <Popover.Detail identifier=\"carousel-prev\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {previousSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowLeft\"\n disabled={disableKeyboardShortcuts}\n size=\"sm\"\n onTriggered={handlePrev}\n />\n </div>\n </Popover.Detail>\n </Popover>\n\n {Array.from({ length: totalItems }).map((_, index) => {\n const isActive = index === selectedIndex;\n return (\n <button\n key={index}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n aria-label={goToSlide({ index: index + 1 }).value}\n className={cn(\n 'h-2.5 w-2.5 rounded-full transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2',\n isActive ? 'scale-110 bg-text' : 'bg-text/20 hover:bg-text/40'\n )}\n />\n );\n })}\n\n <Popover identifier=\"carousel-next\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n roundedSize=\"full\"\n label={nextSlide.value}\n onClick={(e) => {\n e.stopPropagation();\n handleNext();\n }}\n Icon={ChevronRight}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === totalItems - 1}\n />\n\n <Popover.Detail identifier=\"carousel-next\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {nextSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowRight\"\n size=\"sm\"\n onTriggered={handleNext}\n disabled={disableKeyboardShortcuts}\n />\n </div>\n </Popover.Detail>\n </Popover>\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Main Component: Carousel Root\n// ------------------------------------------------------------------\ntype CarouselProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n initialIndex?: number;\n onIndexChange?: (index: number) => void;\n};\n\nconst partitionCarouselChildren = (\n children: ReactNode[]\n): [ReactElement[], ReactNode[]] => {\n const slides: ReactElement[] = [];\n const others: ReactNode[] = [];\n\n children.forEach((child) => {\n if (isValidElement(child) && child.type === CarouselItem) {\n slides.push(child);\n } else {\n others.push(child);\n }\n });\n\n return [slides, others];\n};\n\nconst CarouselRoot: FC<CarouselProps> = ({\n children,\n className,\n initialIndex = 0,\n onIndexChange,\n ...props\n}) => {\n const allChildren = Children.toArray(children);\n const [slides, others] = partitionCarouselChildren(allChildren);\n const totalItems = slides.length;\n\n // State Management\n const [selectedIndex, setSelectedIndex] = useState<number>(initialIndex);\n const [displayedIndex, setDisplayedIndex] = useState<number>(initialIndex);\n const [containerHeight, setContainerHeight] = useState<number>(0);\n const [containerWidth, setContainerWidth] = useState<number>(0);\n\n useLayoutEffect(() => {\n const calculateDimensions = () => {\n if (!containerRef.current) return;\n\n // Track Width\n const width = containerRef.current.clientWidth;\n setContainerWidth(width);\n\n // Track Height (existing logic)\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateDimensions();\n\n const observer = new ResizeObserver(() => {\n calculateDimensions();\n });\n\n if (containerRef.current) observer.observe(containerRef.current);\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]);\n\n // Drag State\n const [startX, setStartX] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n // Refs\n const containerRef = useRef<HTMLDivElement>(null);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n\n // Navigation Logic\n const handleSwitchItem = (diff: number) => {\n if (containerWidth === 0) return;\n\n // Use container width for the threshold\n const swipeStep = containerWidth / SWIPE_THRESHOLD_DIVISOR;\n const numSwipe = Math.round(diff / swipeStep);\n\n if (Math.abs(numSwipe) >= 1) {\n const newIndex = displayedIndex - numSwipe;\n const clampedIndex = Math.max(0, Math.min(newIndex, totalItems - 1));\n\n if (clampedIndex !== selectedIndex) {\n setSelectedIndex(clampedIndex);\n setStartX((prev) => prev + diff);\n }\n }\n };\n\n const handleNext = () => {\n setSelectedIndex((prev) => Math.min(prev + 1, totalItems - 1));\n };\n\n const handlePrev = () => {\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\n };\n\n // Input Handlers\n const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.clientX);\n };\n const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.clientX - startX);\n };\n const handleDragEnd = () => setIsDragging(false);\n const handleTouchStart: TouchEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.touches[0].clientX);\n };\n const handleTouchMove: TouchEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.touches[0].clientX - startX);\n };\n\n // Effects\n useEffect(() => {\n if (selectedIndex) onIndexChange?.(selectedIndex);\n }, [selectedIndex, onIndexChange]);\n\n useEffect(() => {\n let interval: NodeJS.Timeout;\n\n if (selectedIndex !== displayedIndex) {\n interval = setInterval(() => {\n setDisplayedIndex((prev) => {\n if (prev === selectedIndex) {\n clearInterval(interval);\n return prev;\n }\n return prev < selectedIndex ? prev + 1 : prev - 1;\n });\n }, TRANSITION_DELAY_MS);\n }\n return () => clearInterval(interval);\n }, [selectedIndex, displayedIndex]);\n\n // Calculate height based on the MAX height of ALL items\n useLayoutEffect(() => {\n const calculateMaxHeight = () => {\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateMaxHeight();\n\n const observer = new ResizeObserver(() => {\n calculateMaxHeight();\n });\n\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]); // Removed isMounted dependency\n\n return (\n <CarouselContext.Provider\n value={{\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n }}\n >\n <div\n ref={containerRef}\n className={cn(\n 'relative flex w-full cursor-grab select-none items-center overflow-hidden outline-none transition-[height] duration-300 ease-in-out focus:outline-none focus:outline-none focus:ring-0 active:cursor-grabbing',\n 'max-w-[1400px]',\n className\n )}\n style={{\n height: containerHeight > 0 ? containerHeight : 'auto',\n minHeight: '400px',\n }}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleDragEnd}\n onMouseLeave={handleDragEnd}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleDragEnd}\n role=\"region\"\n aria-label=\"Carousel\"\n {...props}\n >\n {slides.map((child, index) => {\n return (\n <div\n key={index}\n role=\"button\"\n tabIndex={0}\n ref={(el) => {\n itemsRef.current[index] = el;\n }}\n // FIX 2: Removed isMounted checks and invisible classes.\n // CSS units allow correct SSR rendering.\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 transition-all duration-300 ease-in-out',\n 'outline-none ring-0 focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0',\n getCardStyle(index, displayedIndex)\n )}\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') setSelectedIndex(index);\n }}\n style={{\n transform: `\n translateX(${getCardPositionX(\n index,\n displayedIndex,\n containerWidth\n )})\n scale(${getCardScale(index, displayedIndex)})\n `,\n }}\n >\n {child}\n </div>\n );\n })}\n\n {others}\n </div>\n </CarouselContext.Provider>\n );\n};\n\nexport const Carousel = Object.assign(CarouselRoot, {\n Item: CarouselItem,\n Indicators: CarouselIndicators,\n});\n"],"mappings":";;;;;;;;;;;;AAiCA,MAAM,0BAA0B;AAChC,MAAM,sBAAsB;AAa5B,MAAM,kBAAkB,cAA2C,IAAI;AAEvE,MAAM,oBAAoB;CACxB,MAAM,UAAU,WAAW,eAAe;CAC1C,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,4CAA4C;CAE9D,OAAO;AACT;AAKA,MAAM,gBAAgB,OAAe,mBAA2B;CAE9D,QADa,KAAK,IAAI,QAAQ,cACnB,GAAX;EACE,KAAK,GACH,OAAO;EACT,KAAK,GACH,OAAO;EACT,KAAK,GACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAM,gBAAgB,OAAe,mBAA2B;CAE9D,QADa,KAAK,IAAI,QAAQ,cACnB,GAAX;EACE,KAAK,GACH,OAAO;EACT,KAAK,GACH,OAAO;EACT,KAAK,GACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAIA,MAAM,oBACJ,OACA,gBACA,mBACG;CAMH,OAAO,IALM,QAAQ,kBAER,KAAK,IAAI,kBADA,iBAAiB,MAAM,MAAO,KACE,GAGlC,EAAE;AACxB;AASA,MAAM,gBAAuC,EAC3C,UACA,WACA,GAAG,YACC;CACJ,OACE,oBAAC,OAAD;EAAK,WAAW,GAAG,iBAAiB,SAAS;EAAG,GAAI;EACjD;CACE;AAET;AASA,MAAM,sBAAmD,EACvD,WACA,2BAA2B,OAC3B,GAAG,YACC;CACJ,MAAM,EACJ,eACA,kBACA,YACA,YACA,eACE,YAAY;CAChB,MAAM,EAAE,WAAW,eAAe,cAAc,YAAY,UAAU;CAEtE,IAAI,cAAc,GAAG,OAAO;CAE5B,OACE,qBAAC,OAAD;EACE,WAAW,GACT,qFACA,SACF;EACA,GAAI;YALN;GAOE,qBAAC,SAAD;IAAS,YAAW;cAApB,CACE,oBAAC,QAAD;KACE;KACA;KACA,OAAO,cAAc;KACrB,aAAY;KACZ,UAAU,MAAM;MACd,EAAE,gBAAgB;MAClB,WAAW;KACb;KACA,MAAM;KACN;KACA,UAAU,kBAAkB;IAC7B,IAED,oBAAC,QAAQ,QAAT;KAAgB,YAAW;eACzB,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBACb,cAAc;MACX,IACN,oBAAC,kBAAD;OACE,UAAS;OACT,UAAU;OACV,MAAK;OACL,aAAa;MACd,EACE;;IACS,EACT;;GAER,MAAM,KAAK,EAAE,QAAQ,WAAW,CAAC,EAAE,KAAK,GAAG,UAAU;IACpD,MAAM,WAAW,UAAU;IAC3B,OACE,oBAAC,UAAD;KAEE,MAAK;KACL,UAAU,MAAM;MACd,EAAE,gBAAgB;MAClB,iBAAiB,KAAK;KACxB;KACA,cAAY,UAAU,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE;KAC5C,WAAW,GACT,mIACA,WAAW,sBAAsB,6BACnC;IACD,GAXM,KAWN;GAEL,CAAC;GAED,qBAAC,SAAD;IAAS,YAAW;cAApB,CACE,oBAAC,QAAD;KACE;KACA;KACA,aAAY;KACZ,OAAO,UAAU;KACjB,UAAU,MAAM;MACd,EAAE,gBAAgB;MAClB,WAAW;KACb;KACA,MAAM;KACN;KACA,UAAU,kBAAkB,aAAa;IAC1C,IAED,oBAAC,QAAQ,QAAT;KAAgB,YAAW;eACzB,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBACb,UAAU;MACP,IACN,oBAAC,kBAAD;OACE,UAAS;OACT,MAAK;OACL,aAAa;OACb,UAAU;MACX,EACE;;IACS,EACT;;EACN;;AAET;AAWA,MAAM,6BACJ,aACkC;CAClC,MAAM,SAAyB,CAAC;CAChC,MAAM,SAAsB,CAAC;CAE7B,SAAS,SAAS,UAAU;EAC1B,IAAI,eAAe,KAAK,KAAK,MAAM,SAAS,cAC1C,OAAO,KAAK,KAAK;OAEjB,OAAO,KAAK,KAAK;CAErB,CAAC;CAED,OAAO,CAAC,QAAQ,MAAM;AACxB;AAEA,MAAM,gBAAmC,EACvC,UACA,WACA,eAAe,GACf,eACA,GAAG,YACC;CAEJ,MAAM,CAAC,QAAQ,UAAU,0BADL,SAAS,QAAQ,QACwB,CAAC;CAC9D,MAAM,aAAa,OAAO;CAG1B,MAAM,CAAC,eAAe,oBAAoB,SAAiB,YAAY;CACvE,MAAM,CAAC,gBAAgB,qBAAqB,SAAiB,YAAY;CACzE,MAAM,CAAC,iBAAiB,sBAAsB,SAAiB,CAAC;CAChE,MAAM,CAAC,gBAAgB,qBAAqB,SAAiB,CAAC;CAE9D,sBAAsB;EACpB,MAAM,4BAA4B;GAChC,IAAI,CAAC,aAAa,SAAS;GAG3B,MAAM,QAAQ,aAAa,QAAQ;GACnC,kBAAkB,KAAK;GAGvB,MAAM,UAAU,SAAS,QAAQ,KAAK,SAAS,MAAM,gBAAgB,CAAC;GACtE,MAAM,YAAY,KAAK,IAAI,GAAG,GAAG,OAAO;GACxC,IAAI,YAAY,GACd,mBAAmB,YAAY,EAAE;EAErC;EAEA,oBAAoB;EAEpB,MAAM,WAAW,IAAI,qBAAqB;GACxC,oBAAoB;EACtB,CAAC;EAED,IAAI,aAAa,SAAS,SAAS,QAAQ,aAAa,OAAO;EAC/D,SAAS,QAAQ,SAAS,SAAS;GACjC,IAAI,MAAM,SAAS,QAAQ,IAAI;EACjC,CAAC;EAED,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,UAAU,CAAC;CAGf,MAAM,CAAC,QAAQ,aAAa,SAAS,CAAC;CACtC,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAGlD,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,WAAW,OAAkC,CAAC,CAAC;CAGrD,MAAM,oBAAoB,SAAiB;EACzC,IAAI,mBAAmB,GAAG;EAG1B,MAAM,YAAY,iBAAiB;EACnC,MAAM,WAAW,KAAK,MAAM,OAAO,SAAS;EAE5C,IAAI,KAAK,IAAI,QAAQ,KAAK,GAAG;GAC3B,MAAM,WAAW,iBAAiB;GAClC,MAAM,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,UAAU,aAAa,CAAC,CAAC;GAEnE,IAAI,iBAAiB,eAAe;IAClC,iBAAiB,YAAY;IAC7B,WAAW,SAAS,OAAO,IAAI;GACjC;EACF;CACF;CAEA,MAAM,mBAAmB;EACvB,kBAAkB,SAAS,KAAK,IAAI,OAAO,GAAG,aAAa,CAAC,CAAC;CAC/D;CAEA,MAAM,mBAAmB;EACvB,kBAAkB,SAAS,KAAK,IAAI,OAAO,GAAG,CAAC,CAAC;CAClD;CAGA,MAAM,mBAAsD,MAAM;EAChE,cAAc,IAAI;EAClB,UAAU,EAAE,OAAO;CACrB;CACA,MAAM,mBAAsD,MAAM;EAChE,IAAI,YAAY,iBAAiB,EAAE,UAAU,MAAM;CACrD;CACA,MAAM,sBAAsB,cAAc,KAAK;CAC/C,MAAM,oBAAuD,MAAM;EACjE,cAAc,IAAI;EAClB,UAAU,EAAE,QAAQ,GAAG,OAAO;CAChC;CACA,MAAM,mBAAsD,MAAM;EAChE,IAAI,YAAY,iBAAiB,EAAE,QAAQ,GAAG,UAAU,MAAM;CAChE;CAGA,gBAAgB;EACd,IAAI,eAAe,gBAAgB,aAAa;CAClD,GAAG,CAAC,eAAe,aAAa,CAAC;CAEjC,gBAAgB;EACd,IAAI;EAEJ,IAAI,kBAAkB,gBACpB,WAAW,kBAAkB;GAC3B,mBAAmB,SAAS;IAC1B,IAAI,SAAS,eAAe;KAC1B,cAAc,QAAQ;KACtB,OAAO;IACT;IACA,OAAO,OAAO,gBAAgB,OAAO,IAAI,OAAO;GAClD,CAAC;EACH,GAAG,mBAAmB;EAExB,aAAa,cAAc,QAAQ;CACrC,GAAG,CAAC,eAAe,cAAc,CAAC;CAGlC,sBAAsB;EACpB,MAAM,2BAA2B;GAC/B,MAAM,UAAU,SAAS,QAAQ,KAAK,SAAS,MAAM,gBAAgB,CAAC;GACtE,MAAM,YAAY,KAAK,IAAI,GAAG,GAAG,OAAO;GAExC,IAAI,YAAY,GACd,mBAAmB,YAAY,EAAE;EAErC;EAEA,mBAAmB;EAEnB,MAAM,WAAW,IAAI,qBAAqB;GACxC,mBAAmB;EACrB,CAAC;EAED,SAAS,QAAQ,SAAS,SAAS;GACjC,IAAI,MAAM,SAAS,QAAQ,IAAI;EACjC,CAAC;EAED,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,UAAU,CAAC;CAEf,OACE,oBAAC,gBAAgB,UAAjB;EACE,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;YAEA,qBAAC,OAAD;GACE,KAAK;GACL,WAAW,GACT,iNACA,kBACA,SACF;GACA,OAAO;IACL,QAAQ,kBAAkB,IAAI,kBAAkB;IAChD,WAAW;GACb;GACA,aAAa;GACb,aAAa;GACb,WAAW;GACX,cAAc;GACd,cAAc;GACd,aAAa;GACb,YAAY;GACZ,MAAK;GACL,cAAW;GACX,GAAI;aApBN,CAsBG,OAAO,KAAK,OAAO,UAAU;IAC5B,OACE,oBAAC,OAAD;KAEE,MAAK;KACL,UAAU;KACV,MAAM,OAAO;MACX,SAAS,QAAQ,SAAS;KAC5B;KAGA,WAAW,GACT,8EACA,uJACA,aAAa,OAAO,cAAc,CACpC;KACA,UAAU,MAAM;MACd,EAAE,gBAAgB;MAClB,iBAAiB,KAAK;KACxB;KACA,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK,iBAAiB,KAAK;KAChE;KACA,OAAO,EACL,WAAW;+BACI,iBACX,OACA,gBACA,cACF,EAAE;0BACM,aAAa,OAAO,cAAc,EAAE;kBAEhD;eAEC;IACE,GAhCE,KAgCF;GAET,CAAC,GAEA,MACE;;CACmB;AAE9B;AAEA,MAAa,WAAW,OAAO,OAAO,cAAc;CAClD,MAAM;CACN,YAAY;AACd,CAAC"}
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
+ import { useUser } from "../../hooks/useUser/index.mjs";
3
4
  import { Button, ButtonColor, ButtonSize, ButtonVariant } from "../Button/Button.mjs";
4
5
  import { AutoCompleteTextarea } from "../TextArea/AutocompleteTextArea.mjs";
5
- import { useUser } from "../../hooks/useUser/index.mjs";
6
6
  import { useEffect, useState } from "react";
7
7
  import { Check, X } from "lucide-react";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,4 +1,4 @@
1
- import { NodeWrapper, traceKeys } from "./index.mjs";
1
+ import { NodeWrapper } from "./index.mjs";
2
2
  import { createElement } from "react";
3
3
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
4
4
  import * as NodeTypes from "@intlayer/types/nodeType";
@@ -8,7 +8,7 @@ const ConditionWrapper = (props) => {
8
8
  const { keyPath, section } = props;
9
9
  return /* @__PURE__ */ jsx("div", {
10
10
  className: "ml-2 grid grid-cols-[auto,1fr] gap-2",
11
- children: Object.keys(section).filter((key) => !traceKeys.includes(key)).map((key) => {
11
+ children: Object.keys(section[NodeTypes.CONDITION]).map((key) => {
12
12
  const newKeyPathEl = {
13
13
  type: NodeTypes.CONDITION,
14
14
  key
@@ -1 +1 @@
1
- {"version":3,"file":"ConditionWrapper.mjs","names":[],"sources":["../../../../../src/components/DictionaryEditor/NodeWrapper/ConditionWrapper.tsx"],"sourcesContent":["import type { ConditionContent } from '@intlayer/core/transpiler';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type { FC } from 'react';\nimport { NodeWrapper, type NodeWrapperProps, traceKeys } from './index';\n\ntype ConditionWrapperProps = Omit<NodeWrapperProps, 'section'> & {\n section: ConditionContent<ContentNode>;\n};\n\nexport const ConditionWrapper: FC<ConditionWrapperProps> = (props) => {\n const { keyPath, section } = props;\n\n return (\n <div className=\"ml-2 grid grid-cols-[auto,1fr] gap-2\">\n {Object.keys(section)\n .filter((key) => !traceKeys.includes(key))\n .map((key) => {\n const newKeyPathEl: KeyPath = {\n type: NodeTypes.CONDITION,\n key,\n };\n const newKeyPath: KeyPath[] = [...keyPath, newKeyPathEl];\n\n const subSection =\n section[NodeTypes.CONDITION][\n key as keyof (typeof section)[typeof NodeTypes.CONDITION]\n ]!;\n\n return (\n <>\n <span className=\"flex items-center font-bold\">{key}</span>\n <NodeWrapper\n {...props}\n key={key}\n keyPath={newKeyPath}\n section={subSection}\n />\n </>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;AAWA,MAAa,oBAA+C,UAAU;CACpE,MAAM,EAAE,SAAS,YAAY;CAE7B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAAO,KAAK,OAAO,EACjB,QAAQ,QAAQ,CAAC,UAAU,SAAS,GAAG,CAAC,EACxC,KAAK,QAAQ;GACZ,MAAM,eAAwB;IAC5B,MAAM,UAAU;IAChB;GACF;GACA,MAAM,aAAwB,CAAC,GAAG,SAAS,YAAY;GAEvD,MAAM,aACJ,QAAQ,UAAU,WAChB;GAGJ,OACE,8CACE,oBAAC,QAAD;IAAM,WAAU;cAA+B;GAAU,IACzD,8BAAC,aAAD;IACE,GAAI;IACC;IACL,SAAS;IACT,SAAS;GACV,EACD;EAEN,CAAC;CACA;AAET"}
1
+ {"version":3,"file":"ConditionWrapper.mjs","names":[],"sources":["../../../../../src/components/DictionaryEditor/NodeWrapper/ConditionWrapper.tsx"],"sourcesContent":["import type { ConditionContent } from '@intlayer/core/transpiler';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type { FC } from 'react';\nimport { NodeWrapper, type NodeWrapperProps } from './index';\n\ntype ConditionWrapperProps = Omit<NodeWrapperProps, 'section'> & {\n section: ConditionContent<ContentNode>;\n};\n\nexport const ConditionWrapper: FC<ConditionWrapperProps> = (props) => {\n const { keyPath, section } = props;\n\n return (\n <div className=\"ml-2 grid grid-cols-[auto,1fr] gap-2\">\n {Object.keys(section[NodeTypes.CONDITION]).map((key) => {\n const newKeyPathEl: KeyPath = {\n type: NodeTypes.CONDITION,\n key,\n };\n const newKeyPath: KeyPath[] = [...keyPath, newKeyPathEl];\n\n const subSection =\n section[NodeTypes.CONDITION][\n key as keyof (typeof section)[typeof NodeTypes.CONDITION]\n ]!;\n\n return (\n <>\n <span className=\"flex items-center font-bold\">{key}</span>\n <NodeWrapper\n {...props}\n key={key}\n keyPath={newKeyPath}\n section={subSection}\n />\n </>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;AAWA,MAAa,oBAA+C,UAAU;CACpE,MAAM,EAAE,SAAS,YAAY;CAE7B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAAO,KAAK,QAAQ,UAAU,UAAU,EAAE,KAAK,QAAQ;GACtD,MAAM,eAAwB;IAC5B,MAAM,UAAU;IAChB;GACF;GACA,MAAM,aAAwB,CAAC,GAAG,SAAS,YAAY;GAEvD,MAAM,aACJ,QAAQ,UAAU,WAChB;GAGJ,OACE,8CACE,oBAAC,QAAD;IAAM,WAAU;cAA+B;GAAU,IACzD,8BAAC,aAAD;IACE,GAAI;IACC;IACL,SAAS;IACT,SAAS;GACV,EACD;EAEN,CAAC;CACE;AAET"}
@@ -1,4 +1,4 @@
1
- import { NodeWrapper, traceKeys } from "./index.mjs";
1
+ import { NodeWrapper } from "./index.mjs";
2
2
  import { createElement } from "react";
3
3
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
4
4
  import * as NodeTypes from "@intlayer/types/nodeType";
@@ -8,7 +8,7 @@ const EnumerationWrapper = (props) => {
8
8
  const { keyPath, section } = props;
9
9
  return /* @__PURE__ */ jsx("div", {
10
10
  className: "ml-2 grid grid-cols-[auto,1fr] gap-2",
11
- children: Object.keys(section).filter((key) => !traceKeys.includes(key)).map((key) => {
11
+ children: Object.keys(section[NodeTypes.ENUMERATION]).map((key) => {
12
12
  const newKeyPathEl = {
13
13
  type: NodeTypes.ENUMERATION,
14
14
  key
@@ -1 +1 @@
1
- {"version":3,"file":"EnumerationWrapper.mjs","names":[],"sources":["../../../../../src/components/DictionaryEditor/NodeWrapper/EnumerationWrapper.tsx"],"sourcesContent":["import type { EnumerationContent } from '@intlayer/core/transpiler';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type { FC } from 'react';\nimport { NodeWrapper, type NodeWrapperProps, traceKeys } from './index';\n\ntype EnumerationWrapperProps = Omit<NodeWrapperProps, 'section'> & {\n section: EnumerationContent<ContentNode>;\n};\n\nexport const EnumerationWrapper: FC<EnumerationWrapperProps> = (props) => {\n const { keyPath, section } = props;\n\n return (\n <div className=\"ml-2 grid grid-cols-[auto,1fr] gap-2\">\n {Object.keys(section)\n .filter((key) => !traceKeys.includes(key))\n .map((key) => {\n const newKeyPathEl: KeyPath = {\n type: NodeTypes.ENUMERATION,\n key,\n };\n const newKeyPath: KeyPath[] = [...keyPath, newKeyPathEl];\n\n const subSection =\n section[NodeTypes.ENUMERATION][\n key as keyof (typeof section)[typeof NodeTypes.ENUMERATION]\n ]!;\n\n return (\n <>\n <span className=\"flex items-center font-bold\">{key}</span>\n <NodeWrapper\n {...props}\n key={key}\n keyPath={newKeyPath}\n section={subSection}\n />\n </>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;AAWA,MAAa,sBAAmD,UAAU;CACxE,MAAM,EAAE,SAAS,YAAY;CAE7B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAAO,KAAK,OAAO,EACjB,QAAQ,QAAQ,CAAC,UAAU,SAAS,GAAG,CAAC,EACxC,KAAK,QAAQ;GACZ,MAAM,eAAwB;IAC5B,MAAM,UAAU;IAChB;GACF;GACA,MAAM,aAAwB,CAAC,GAAG,SAAS,YAAY;GAEvD,MAAM,aACJ,QAAQ,UAAU,aAChB;GAGJ,OACE,8CACE,oBAAC,QAAD;IAAM,WAAU;cAA+B;GAAU,IACzD,8BAAC,aAAD;IACE,GAAI;IACC;IACL,SAAS;IACT,SAAS;GACV,EACD;EAEN,CAAC;CACA;AAET"}
1
+ {"version":3,"file":"EnumerationWrapper.mjs","names":[],"sources":["../../../../../src/components/DictionaryEditor/NodeWrapper/EnumerationWrapper.tsx"],"sourcesContent":["import type { EnumerationContent } from '@intlayer/core/transpiler';\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type { FC } from 'react';\nimport { NodeWrapper, type NodeWrapperProps } from './index';\n\ntype EnumerationWrapperProps = Omit<NodeWrapperProps, 'section'> & {\n section: EnumerationContent<ContentNode>;\n};\n\nexport const EnumerationWrapper: FC<EnumerationWrapperProps> = (props) => {\n const { keyPath, section } = props;\n\n return (\n <div className=\"ml-2 grid grid-cols-[auto,1fr] gap-2\">\n {Object.keys(section[NodeTypes.ENUMERATION]).map((key) => {\n const newKeyPathEl: KeyPath = {\n type: NodeTypes.ENUMERATION,\n key,\n };\n const newKeyPath: KeyPath[] = [...keyPath, newKeyPathEl];\n\n const subSection =\n section[NodeTypes.ENUMERATION][\n key as keyof (typeof section)[typeof NodeTypes.ENUMERATION]\n ]!;\n\n return (\n <>\n <span className=\"flex items-center font-bold\">{key}</span>\n <NodeWrapper\n {...props}\n key={key}\n keyPath={newKeyPath}\n section={subSection}\n />\n </>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;AAWA,MAAa,sBAAmD,UAAU;CACxE,MAAM,EAAE,SAAS,YAAY;CAE7B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAAO,KAAK,QAAQ,UAAU,YAAY,EAAE,KAAK,QAAQ;GACxD,MAAM,eAAwB;IAC5B,MAAM,UAAU;IAChB;GACF;GACA,MAAM,aAAwB,CAAC,GAAG,SAAS,YAAY;GAEvD,MAAM,aACJ,QAAQ,UAAU,aAChB;GAGJ,OACE,8CACE,oBAAC,QAAD;IAAM,WAAU;cAA+B;GAAU,IACzD,8BAAC,aAAD;IACE,GAAI;IACC;IACL,SAAS;IACT,SAAS;GACV,EACD;EAEN,CAAC;CACE;AAET"}
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { useAuditContentDeclarationField } from "../../../hooks/reactQuery.mjs";
3
4
  import { Container } from "../../Container/index.mjs";
4
5
  import { Loader } from "../../Loader/index.mjs";
5
6
  import { Button, ButtonColor, ButtonSize, ButtonTextAlign, ButtonVariant } from "../../Button/Button.mjs";
@@ -8,7 +9,6 @@ import { InputVariant } from "../../Input/Input.mjs";
8
9
  import { SwitchSelector, SwitchSelectorColor, SwitchSelectorSize } from "../../SwitchSelector/SwitchSelector.mjs";
9
10
  import { useLocaleSwitcherContent } from "../../LocaleSwitcherContentDropDown/LocaleSwitcherContentContext.mjs";
10
11
  import { ContentEditorInput as ContentEditorInput$1 } from "../../ContentEditor/ContentEditorInput.mjs";
11
- import { useAuditContentDeclarationField } from "../../../hooks/reactQuery.mjs";
12
12
  import { ContentEditorTextArea as ContentEditorTextArea$1 } from "../../ContentEditor/ContentEditorTextArea.mjs";
13
13
  import { renameKey } from "./object.mjs";
14
14
  import { Label } from "../../Label/index.mjs";
@@ -17,9 +17,9 @@ import { SafeHtmlRenderer } from "./SafeHtmlRenderer.mjs";
17
17
  import { Fragment, Suspense, lazy, memo, useState } from "react";
18
18
  import { Plus, Trash, WandSparkles } from "lucide-react";
19
19
  import { jsx, jsxs } from "react/jsx-runtime";
20
+ import { useConfiguration, useEditedContent } from "@intlayer/editor-react";
20
21
  import { useIntlayer, useLocale } from "react-intlayer";
21
22
  import { getLocaleName } from "@intlayer/core/localization";
22
- import { useConfiguration, useEditedContent } from "@intlayer/editor-react";
23
23
  import { getEmptyNode, getNodeType } from "@intlayer/core/dictionaryManipulator";
24
24
  import * as NodeTypes from "@intlayer/types/nodeType";
25
25
  import { camelCaseToSentence } from "@intlayer/config/client";