@intlayer/design-system 7.3.15 → 7.5.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 (111) hide show
  1. package/dist/esm/components/Avatar/index.mjs +17 -11
  2. package/dist/esm/components/Avatar/index.mjs.map +1 -1
  3. package/dist/esm/components/Button/Button.mjs +9 -8
  4. package/dist/esm/components/Button/Button.mjs.map +1 -1
  5. package/dist/esm/components/IDE/CodeBlockClient.mjs +1 -1
  6. package/dist/esm/components/IDE/CodeBlockClient.mjs.map +1 -1
  7. package/dist/esm/components/IDE/CodeBlockShiki.mjs +1 -0
  8. package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
  9. package/dist/esm/components/Link/Link.mjs +130 -95
  10. package/dist/esm/components/Link/Link.mjs.map +1 -1
  11. package/dist/esm/components/Link/index.mjs +2 -2
  12. package/dist/esm/components/index.mjs +2 -2
  13. package/dist/types/components/Avatar/index.d.ts.map +1 -1
  14. package/dist/types/components/Badge/index.d.ts +2 -2
  15. package/dist/types/components/Breadcrumb/index.d.ts +2 -2
  16. package/dist/types/components/Breadcrumb/index.d.ts.map +1 -1
  17. package/dist/types/components/Browser/Browser.content.d.ts +11 -11
  18. package/dist/types/components/Browser/Browser.content.d.ts.map +1 -1
  19. package/dist/types/components/Button/Button.d.ts +5 -5
  20. package/dist/types/components/Button/Button.d.ts.map +1 -1
  21. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +3 -3
  22. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts.map +1 -1
  23. package/dist/types/components/Command/index.d.ts +20 -20
  24. package/dist/types/components/Command/index.d.ts.map +1 -1
  25. package/dist/types/components/Container/index.d.ts +4 -4
  26. package/dist/types/components/Container/index.d.ts.map +1 -1
  27. package/dist/types/components/CopyButton/CopyButton.content.d.ts +3 -3
  28. package/dist/types/components/CopyButton/CopyButton.content.d.ts.map +1 -1
  29. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.d.ts +25 -25
  30. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts +9 -9
  31. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts.map +1 -1
  32. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.d.ts +33 -33
  33. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.content.d.ts +25 -25
  34. package/dist/types/components/DictionaryFieldEditor/NavigationView/navigationViewNode.content.d.ts +25 -25
  35. package/dist/types/components/DictionaryFieldEditor/SaveForm/saveForm.content.d.ts +33 -33
  36. package/dist/types/components/DictionaryFieldEditor/StructureView/structureView.content.d.ts +9 -9
  37. package/dist/types/components/DictionaryFieldEditor/VersionSwitcherDropDown/versionSwitcherDropDown.content.d.ts +7 -7
  38. package/dist/types/components/DictionaryFieldEditor/dictionaryFieldEditor.content.d.ts +5 -5
  39. package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts +31 -31
  40. package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts.map +1 -1
  41. package/dist/types/components/ExpandCollapse/expandCollapse.content.d.ts +3 -3
  42. package/dist/types/components/Form/FormBase.d.ts +2 -2
  43. package/dist/types/components/Form/FormBase.d.ts.map +1 -1
  44. package/dist/types/components/Form/FormField.d.ts +2 -2
  45. package/dist/types/components/Form/FormField.d.ts.map +1 -1
  46. package/dist/types/components/Form/FormItem.d.ts +2 -2
  47. package/dist/types/components/Form/FormItem.d.ts.map +1 -1
  48. package/dist/types/components/Form/elements/EditableFieldInputElement.d.ts +2 -2
  49. package/dist/types/components/Form/elements/EditableFieldTextAreaElement.d.ts +2 -2
  50. package/dist/types/components/Form/elements/FormElement.d.ts +2 -2
  51. package/dist/types/components/Form/elements/MultiselectElement.d.ts +2 -2
  52. package/dist/types/components/Form/elements/OTPElement.d.ts +2 -2
  53. package/dist/types/components/Form/elements/SelectElement.d.ts +2 -2
  54. package/dist/types/components/Form/elements/SwitchSelectorElement.d.ts +2 -2
  55. package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
  56. package/dist/types/components/IDE/CodeContext.d.ts +2 -2
  57. package/dist/types/components/IDE/CodeContext.d.ts.map +1 -1
  58. package/dist/types/components/IDE/FileTree.d.ts.map +1 -1
  59. package/dist/types/components/IDE/code.content.d.ts +5 -5
  60. package/dist/types/components/IDE/copyCode.content.d.ts +5 -5
  61. package/dist/types/components/Input/Checkbox.d.ts +3 -3
  62. package/dist/types/components/Input/Checkbox.d.ts.map +1 -1
  63. package/dist/types/components/Input/Input.d.ts +2 -2
  64. package/dist/types/components/Input/OTPInput.d.ts +6 -6
  65. package/dist/types/components/Input/OTPInput.d.ts.map +1 -1
  66. package/dist/types/components/Input/SearchInput.d.ts +2 -2
  67. package/dist/types/components/Input/SearchInput.d.ts.map +1 -1
  68. package/dist/types/components/Link/Link.d.ts +26 -89
  69. package/dist/types/components/Link/Link.d.ts.map +1 -1
  70. package/dist/types/components/Link/index.d.ts +2 -2
  71. package/dist/types/components/Loader/index.content.d.ts +3 -3
  72. package/dist/types/components/Loader/index.content.d.ts.map +1 -1
  73. package/dist/types/components/Loader/spinner.d.ts +2 -2
  74. package/dist/types/components/Loader/spinner.d.ts.map +1 -1
  75. package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts +17 -17
  76. package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts.map +1 -1
  77. package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts +13 -13
  78. package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts.map +1 -1
  79. package/dist/types/components/MaxWidthSmoother/index.d.ts +2 -2
  80. package/dist/types/components/Navbar/Burger.d.ts +2 -2
  81. package/dist/types/components/Navbar/DesktopNavbar.d.ts +2 -2
  82. package/dist/types/components/Navbar/MobileNavbar.d.ts +2 -2
  83. package/dist/types/components/Navbar/MobileNavbar.d.ts.map +1 -1
  84. package/dist/types/components/Navbar/index.d.ts +2 -2
  85. package/dist/types/components/Pagination/Pagination.d.ts +4 -4
  86. package/dist/types/components/Pagination/Pagination.d.ts.map +1 -1
  87. package/dist/types/components/Pagination/pagination.content.d.ts +11 -11
  88. package/dist/types/components/Popover/static.d.ts +3 -3
  89. package/dist/types/components/Popover/static.d.ts.map +1 -1
  90. package/dist/types/components/Select/Select.d.ts +3 -3
  91. package/dist/types/components/Select/Select.d.ts.map +1 -1
  92. package/dist/types/components/SocialNetworks/index.d.ts +2 -2
  93. package/dist/types/components/SocialNetworks/index.d.ts.map +1 -1
  94. package/dist/types/components/SwitchSelector/index.d.ts +5 -5
  95. package/dist/types/components/Tab/Tab.d.ts +6 -6
  96. package/dist/types/components/Tab/Tab.d.ts.map +1 -1
  97. package/dist/types/components/Tab/TabContext.d.ts +2 -2
  98. package/dist/types/components/Tab/TabContext.d.ts.map +1 -1
  99. package/dist/types/components/TabSelector/TabSelector.d.ts +5 -5
  100. package/dist/types/components/TabSelector/TabSelector.d.ts.map +1 -1
  101. package/dist/types/components/Table/table.content.d.ts +3 -3
  102. package/dist/types/components/Table/table.content.d.ts.map +1 -1
  103. package/dist/types/components/Tag/index.d.ts +4 -4
  104. package/dist/types/components/Tag/index.d.ts.map +1 -1
  105. package/dist/types/components/Toaster/Toast.d.ts +3 -3
  106. package/dist/types/components/Toaster/Toast.d.ts.map +1 -1
  107. package/dist/types/components/Toaster/Toaster.d.ts +2 -2
  108. package/dist/types/components/Toaster/Toaster.d.ts.map +1 -1
  109. package/dist/types/components/index.d.ts +2 -2
  110. package/dist/types/hooks/useDevice.d.ts.map +1 -1
  111. package/package.json +14 -14
@@ -15,6 +15,16 @@ const sizeVariants = {
15
15
  xl: "size-16"
16
16
  };
17
17
  /**
18
+ * Icon size variants for the default user icon
19
+ */
20
+ const iconSizeVariants = {
21
+ sm: 14,
22
+ md: 25,
23
+ lg: 30,
24
+ xl: 40
25
+ };
26
+ const getIconSize = (size) => iconSizeVariants[size ?? "md"];
27
+ /**
18
28
  * @description Gets the capital letters from a name.
19
29
  * @param name - The name to extract capitals from.
20
30
  * @param separator - The separator to split the name (default is an empty string, which splits by each character).
@@ -96,13 +106,13 @@ const Avatar = ({ fullname, className, isLoading = false, isLoggedIn = true, src
96
106
  isClickable,
97
107
  focusable
98
108
  ]);
99
- return /* @__PURE__ */ jsxs(Container, {
109
+ return /* @__PURE__ */ jsx(Container, {
100
110
  isClickable,
101
111
  className: cn(`rounded-full border-[1.5px] border-text p-[1.5px]`, sizeClass, isClickable && `cursor-pointer hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`, !isClickable && focusable && `focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`, displayLoader && "animate-pulse", className),
102
112
  onClick,
103
113
  ...accessibilityProps,
104
114
  ...props,
105
- children: [/* @__PURE__ */ jsx("div", {
115
+ children: /* @__PURE__ */ jsx("div", {
106
116
  className: "relative flex size-full flex-row items-center justify-center",
107
117
  children: /* @__PURE__ */ jsxs("div", {
108
118
  className: "absolute top-0 left-0 flex size-full flex-col items-center justify-center rounded-full bg-text text-text-opposite",
@@ -115,27 +125,23 @@ const Avatar = ({ fullname, className, isLoading = false, isLoggedIn = true, src
115
125
  className: "size-full rounded-full object-cover",
116
126
  src,
117
127
  srcSet: src,
118
- alt: alt || `Avatar of ${fullname}`,
128
+ alt: alt ?? `Avatar of ${fullname}`,
119
129
  width: 59,
120
130
  height: 59,
121
- loading: "lazy"
131
+ loading: "lazy",
132
+ draggable: false
122
133
  }),
123
134
  displayInitials && /* @__PURE__ */ jsx("div", {
124
135
  className: "flex size-full items-center justify-center gap-[0.1rem] font-bold text-sm max-md:py-1",
125
- "aria-label": `Initials for ${fullname}`,
126
136
  children: capitals?.map((capital, index) => /* @__PURE__ */ jsx("span", { children: capital }, `${capital}-${index}`))
127
137
  }),
128
138
  displayUserIcon && /* @__PURE__ */ jsx(User, {
129
- size: size === "sm" ? 14 : size === "md" ? 25 : size === "lg" ? 30 : 40,
139
+ size: getIconSize(size),
130
140
  "aria-label": "Default user icon"
131
141
  })
132
142
  ]
133
143
  })
134
- }), isClickable && /* @__PURE__ */ jsx("span", {
135
- id: "avatar-description",
136
- className: "sr-only",
137
- children: "Click to view profile"
138
- })]
144
+ })
139
145
  });
140
146
  };
141
147
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["Avatar: FC<AvatarProps>","baseProps: Record<string, any>"],"sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import { User } from 'lucide-react';\nimport type { ComponentProps, FC, HTMLAttributes } from 'react';\nimport { forwardRef, useMemo } from 'react';\nimport { cn } from '../../utils/cn';\nimport { Loader } from '../Loader';\n\n/**\n * Props for the Avatar component\n */\nexport interface AvatarProps extends Omit<ComponentProps<'button'>, 'onClick'> {\n /** Image source URL for the avatar */\n src?: string;\n /** Full name used to generate initials and alt text */\n fullname?: string;\n /** Displays a loading spinner when true */\n isLoading?: boolean;\n /** Whether the user is authenticated */\n isLoggedIn?: boolean;\n /** Size variant of the avatar */\n size?: 'sm' | 'md' | 'lg' | 'xl';\n /** Click handler - when provided, makes the avatar clickable */\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n /** Alternative text for accessibility */\n alt?: string;\n /** Whether the avatar should be focusable when not clickable */\n focusable?: boolean;\n}\n\n/**\n * Size variants for the avatar\n */\nconst sizeVariants = {\n sm: 'size-6',\n md: 'size-9',\n lg: 'size-12',\n xl: 'size-16',\n} as const;\n\n/**\n * @description Gets the capital letters from a name.\n * @param name - The name to extract capitals from.\n * @param separator - The separator to split the name (default is an empty string, which splits by each character).\n * @returns {string[]} An array of capital letters from the name.\n */\nexport const getCapitals = (name: string, separator = ' '): string[] => {\n if (!name) return [];\n\n const parts =\n separator === ' '\n ? name\n .trim()\n .split(/\\s+/) // handle multiple spaces\n : name.split(separator);\n\n return parts.filter(Boolean).map((word) => word.charAt(0).toUpperCase());\n};\n\n/**\n * Container component that renders either a button or div based on interactivity\n */\nconst Container = forwardRef<\n HTMLElement,\n HTMLAttributes<HTMLElement> & {\n isClickable: boolean;\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n }\n>(({ isClickable, onClick, ...props }, ref) => {\n if (isClickable && onClick) {\n return (\n <button\n {...(props as ComponentProps<'button'>)}\n ref={ref as React.Ref<HTMLButtonElement>}\n onClick={onClick}\n type=\"button\"\n />\n );\n }\n\n return (\n <div\n {...(props as ComponentProps<'div'>)}\n ref={ref as React.Ref<HTMLDivElement>}\n role={\n props.tabIndex !== undefined || props['aria-label'] ? 'img' : undefined\n }\n />\n );\n});\n\nContainer.displayName = 'AvatarContainer';\n\n/**\n * Avatar component that displays user profile images, initials, or a default user icon.\n * Supports loading states, authentication states, and accessibility features.\n *\n * @example\n * ```tsx\n * // With image\n * <Avatar src=\"https://example.com/avatar.jpg\" fullname=\"John Doe\" />\n *\n * // With initials\n * <Avatar fullname=\"John Doe\" />\n *\n * // Clickable avatar\n * <Avatar\n * fullname=\"John Doe\"\n * onClick={(e) => console.log('Avatar clicked')}\n * />\n *\n * // Loading state\n * <Avatar isLoading fullname=\"John Doe\" />\n * ```\n */\nexport const Avatar: FC<AvatarProps> = ({\n fullname,\n className,\n isLoading = false,\n isLoggedIn = true,\n src,\n onClick,\n size = 'md',\n alt,\n focusable = false,\n ...props\n}) => {\n const isImageDefined = Boolean(src);\n const isNameDefined = Boolean((fullname ?? '').length > 0);\n const capitals = fullname ? getCapitals(fullname) : undefined;\n\n // Display logic\n const displayLoader = isLoading;\n const displayAvatar = isLoggedIn && !displayLoader && isImageDefined;\n const displayInitials =\n isLoggedIn && !displayLoader && !displayAvatar && isNameDefined;\n const displayUserIcon =\n isLoggedIn && !displayLoader && !displayAvatar && !displayInitials;\n\n const isClickable = onClick !== undefined;\n const sizeClass = sizeVariants[size];\n\n // Accessibility attributes\n const accessibilityProps = useMemo(() => {\n const baseProps: Record<string, any> = {};\n\n if (displayAvatar && alt) {\n baseProps['aria-label'] = alt;\n } else if (displayAvatar && fullname) {\n baseProps['aria-label'] = `Avatar of ${fullname}`;\n } else if (displayInitials && fullname) {\n baseProps['aria-label'] = `Avatar initials for ${fullname}`;\n } else if (displayUserIcon) {\n baseProps['aria-label'] = 'Default user avatar';\n } else if (displayLoader) {\n baseProps['aria-label'] = 'Loading avatar';\n baseProps['aria-busy'] = true;\n }\n\n if (!isClickable && focusable) {\n baseProps.tabIndex = 0;\n }\n\n if (isClickable) {\n baseProps['aria-describedby'] = 'avatar-description';\n }\n\n return baseProps;\n }, [\n displayAvatar,\n displayInitials,\n displayUserIcon,\n displayLoader,\n alt,\n fullname,\n isClickable,\n focusable,\n ]);\n\n return (\n <Container\n isClickable={isClickable}\n className={cn(\n `rounded-full border-[1.5px] border-text p-[1.5px]`,\n sizeClass,\n isClickable &&\n `cursor-pointer hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`,\n !isClickable &&\n focusable &&\n `focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`,\n displayLoader && 'animate-pulse',\n className\n )}\n onClick={onClick}\n {...accessibilityProps}\n {...props}\n >\n <div className=\"relative flex size-full flex-row items-center justify-center\">\n <div className=\"absolute top-0 left-0 flex size-full flex-col items-center justify-center rounded-full bg-text text-text-opposite\">\n {displayLoader && (\n <Loader className=\"w-3/4\" aria-label=\"Loading user avatar\" />\n )}\n\n {displayAvatar && (\n <img\n className=\"size-full rounded-full object-cover\"\n src={src}\n srcSet={src}\n alt={alt || `Avatar of ${fullname}`}\n width={59}\n height={59}\n loading=\"lazy\"\n />\n )}\n\n {displayInitials && (\n <div\n className=\"flex size-full items-center justify-center gap-[0.1rem] font-bold text-sm max-md:py-1\"\n aria-label={`Initials for ${fullname}`}\n >\n {capitals?.map((capital, index) => (\n <span key={`${capital}-${index}`}>{capital}</span>\n ))}\n </div>\n )}\n\n {displayUserIcon && (\n <User\n size={\n size === 'sm'\n ? 14\n : size === 'md'\n ? 25\n : size === 'lg'\n ? 30\n : 40\n }\n aria-label=\"Default user icon\"\n />\n )}\n </div>\n </div>\n\n {isClickable && (\n <span id=\"avatar-description\" className=\"sr-only\">\n Click to view profile\n </span>\n )}\n </Container>\n );\n};\n"],"mappings":";;;;;;;;;;AA+BA,MAAM,eAAe;CACnB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;;;;;;;AAQD,MAAa,eAAe,MAAc,YAAY,QAAkB;AACtE,KAAI,CAAC,KAAM,QAAO,EAAE;AASpB,SANE,cAAc,MACV,KACG,MAAM,CACN,MAAM,MAAM,GACf,KAAK,MAAM,UAAU,EAEd,OAAO,QAAQ,CAAC,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,CAAC;;;;;AAM1E,MAAM,YAAY,YAMf,EAAE,aAAa,SAAS,GAAG,SAAS,QAAQ;AAC7C,KAAI,eAAe,QACjB,QACE,oBAAC;EACC,GAAK;EACA;EACI;EACT,MAAK;GACL;AAIN,QACE,oBAAC;EACC,GAAK;EACA;EACL,MACE,MAAM,aAAa,UAAa,MAAM,gBAAgB,QAAQ;GAEhE;EAEJ;AAEF,UAAU,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAwBxB,MAAaA,UAA2B,EACtC,UACA,WACA,YAAY,OACZ,aAAa,MACb,KACA,SACA,OAAO,MACP,KACA,YAAY,OACZ,GAAG,YACC;CACJ,MAAM,iBAAiB,QAAQ,IAAI;CACnC,MAAM,gBAAgB,SAAS,YAAY,IAAI,SAAS,EAAE;CAC1D,MAAM,WAAW,WAAW,YAAY,SAAS,GAAG;CAGpD,MAAM,gBAAgB;CACtB,MAAM,gBAAgB,cAAc,CAAC,iBAAiB;CACtD,MAAM,kBACJ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB;CACpD,MAAM,kBACJ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB,CAAC;CAErD,MAAM,cAAc,YAAY;CAChC,MAAM,YAAY,aAAa;CAG/B,MAAM,qBAAqB,cAAc;EACvC,MAAMC,YAAiC,EAAE;AAEzC,MAAI,iBAAiB,IACnB,WAAU,gBAAgB;WACjB,iBAAiB,SAC1B,WAAU,gBAAgB,aAAa;WAC9B,mBAAmB,SAC5B,WAAU,gBAAgB,uBAAuB;WACxC,gBACT,WAAU,gBAAgB;WACjB,eAAe;AACxB,aAAU,gBAAgB;AAC1B,aAAU,eAAe;;AAG3B,MAAI,CAAC,eAAe,UAClB,WAAU,WAAW;AAGvB,MAAI,YACF,WAAU,sBAAsB;AAGlC,SAAO;IACN;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,QACE,qBAAC;EACc;EACb,WAAW,GACT,qDACA,WACA,eACE,2GACF,CAAC,eACC,aACA,2EACF,iBAAiB,iBACjB,UACD;EACQ;EACT,GAAI;EACJ,GAAI;aAEJ,oBAAC;GAAI,WAAU;aACb,qBAAC;IAAI,WAAU;;KACZ,iBACC,oBAAC;MAAO,WAAU;MAAQ,cAAW;OAAwB;KAG9D,iBACC,oBAAC;MACC,WAAU;MACL;MACL,QAAQ;MACR,KAAK,OAAO,aAAa;MACzB,OAAO;MACP,QAAQ;MACR,SAAQ;OACR;KAGH,mBACC,oBAAC;MACC,WAAU;MACV,cAAY,gBAAgB;gBAE3B,UAAU,KAAK,SAAS,UACvB,oBAAC,oBAAkC,WAAxB,GAAG,QAAQ,GAAG,QAAyB,CAClD;OACE;KAGP,mBACC,oBAAC;MACC,MACE,SAAS,OACL,KACA,SAAS,OACP,KACA,SAAS,OACP,KACA;MAEV,cAAW;OACX;;KAEA;IACF,EAEL,eACC,oBAAC;GAAK,IAAG;GAAqB,WAAU;aAAU;IAE3C;GAEC"}
1
+ {"version":3,"file":"index.mjs","names":["iconSizeVariants: Record<AvatarProps['size'] & string, number>","Avatar: FC<AvatarProps>","baseProps: Record<string, any>"],"sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import { User } from 'lucide-react';\nimport type { ComponentProps, FC, HTMLAttributes } from 'react';\nimport { forwardRef, useMemo } from 'react';\nimport { cn } from '../../utils/cn';\nimport { Loader } from '../Loader';\n\n/**\n * Props for the Avatar component\n */\nexport interface AvatarProps extends Omit<ComponentProps<'button'>, 'onClick'> {\n /** Image source URL for the avatar */\n src?: string;\n /** Full name used to generate initials and alt text */\n fullname?: string;\n /** Displays a loading spinner when true */\n isLoading?: boolean;\n /** Whether the user is authenticated */\n isLoggedIn?: boolean;\n /** Size variant of the avatar */\n size?: 'sm' | 'md' | 'lg' | 'xl';\n /** Click handler - when provided, makes the avatar clickable */\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n /** Alternative text for accessibility */\n alt?: string;\n /** Whether the avatar should be focusable when not clickable */\n focusable?: boolean;\n}\n\n/**\n * Size variants for the avatar\n */\nconst sizeVariants = {\n sm: 'size-6',\n md: 'size-9',\n lg: 'size-12',\n xl: 'size-16',\n} as const;\n\n/**\n * Icon size variants for the default user icon\n */\nconst iconSizeVariants: Record<AvatarProps['size'] & string, number> = {\n sm: 14,\n md: 25,\n lg: 30,\n xl: 40,\n};\n\nconst getIconSize = (size: AvatarProps['size']): number =>\n iconSizeVariants[size ?? 'md'];\n\n/**\n * @description Gets the capital letters from a name.\n * @param name - The name to extract capitals from.\n * @param separator - The separator to split the name (default is an empty string, which splits by each character).\n * @returns {string[]} An array of capital letters from the name.\n */\nexport const getCapitals = (name: string, separator = ' '): string[] => {\n if (!name) return [];\n\n const parts =\n separator === ' '\n ? name\n .trim()\n .split(/\\s+/) // handle multiple spaces\n : name.split(separator);\n\n return parts.filter(Boolean).map((word) => word.charAt(0).toUpperCase());\n};\n\n/**\n * Container component that renders either a button or div based on interactivity\n */\nconst Container = forwardRef<\n HTMLElement,\n HTMLAttributes<HTMLElement> & {\n isClickable: boolean;\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n }\n>(({ isClickable, onClick, ...props }, ref) => {\n if (isClickable && onClick) {\n return (\n <button\n {...(props as ComponentProps<'button'>)}\n ref={ref as React.Ref<HTMLButtonElement>}\n onClick={onClick}\n type=\"button\"\n />\n );\n }\n\n return (\n <div\n {...(props as ComponentProps<'div'>)}\n ref={ref as React.Ref<HTMLDivElement>}\n role={\n props.tabIndex !== undefined || props['aria-label'] ? 'img' : undefined\n }\n />\n );\n});\n\nContainer.displayName = 'AvatarContainer';\n\n/**\n * Avatar component that displays user profile images, initials, or a default user icon.\n * Supports loading states, authentication states, and accessibility features.\n *\n * @example\n * ```tsx\n * // With image\n * <Avatar src=\"https://example.com/avatar.jpg\" fullname=\"John Doe\" />\n *\n * // With initials\n * <Avatar fullname=\"John Doe\" />\n *\n * // Clickable avatar\n * <Avatar\n * fullname=\"John Doe\"\n * onClick={(e) => console.log('Avatar clicked')}\n * />\n *\n * // Loading state\n * <Avatar isLoading fullname=\"John Doe\" />\n * ```\n */\nexport const Avatar: FC<AvatarProps> = ({\n fullname,\n className,\n isLoading = false,\n isLoggedIn = true,\n src,\n onClick,\n size = 'md',\n alt,\n focusable = false,\n ...props\n}) => {\n const isImageDefined = Boolean(src);\n const isNameDefined = Boolean((fullname ?? '').length > 0);\n const capitals = fullname ? getCapitals(fullname) : undefined;\n\n // Display logic\n const displayLoader = isLoading;\n const displayAvatar = isLoggedIn && !displayLoader && isImageDefined;\n const displayInitials =\n isLoggedIn && !displayLoader && !displayAvatar && isNameDefined;\n const displayUserIcon =\n isLoggedIn && !displayLoader && !displayAvatar && !displayInitials;\n\n const isClickable = onClick !== undefined;\n const sizeClass = sizeVariants[size];\n\n // Accessibility attributes\n const accessibilityProps = useMemo(() => {\n const baseProps: Record<string, any> = {};\n\n if (displayAvatar && alt) {\n baseProps['aria-label'] = alt;\n } else if (displayAvatar && fullname) {\n baseProps['aria-label'] = `Avatar of ${fullname}`;\n } else if (displayInitials && fullname) {\n baseProps['aria-label'] = `Avatar initials for ${fullname}`;\n } else if (displayUserIcon) {\n baseProps['aria-label'] = 'Default user avatar';\n } else if (displayLoader) {\n baseProps['aria-label'] = 'Loading avatar';\n baseProps['aria-busy'] = true;\n }\n\n if (!isClickable && focusable) {\n baseProps.tabIndex = 0;\n }\n\n if (isClickable) {\n baseProps['aria-describedby'] = 'avatar-description';\n }\n\n return baseProps;\n }, [\n displayAvatar,\n displayInitials,\n displayUserIcon,\n displayLoader,\n alt,\n fullname,\n isClickable,\n focusable,\n ]);\n\n return (\n <Container\n isClickable={isClickable}\n className={cn(\n `rounded-full border-[1.5px] border-text p-[1.5px]`,\n sizeClass,\n isClickable &&\n `cursor-pointer hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`,\n !isClickable &&\n focusable &&\n `focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`,\n displayLoader && 'animate-pulse',\n className\n )}\n onClick={onClick}\n {...accessibilityProps}\n {...props}\n >\n <div className=\"relative flex size-full flex-row items-center justify-center\">\n <div className=\"absolute top-0 left-0 flex size-full flex-col items-center justify-center rounded-full bg-text text-text-opposite\">\n {displayLoader && (\n <Loader className=\"w-3/4\" aria-label=\"Loading user avatar\" />\n )}\n\n {displayAvatar && (\n <img\n className=\"size-full rounded-full object-cover\"\n src={src}\n srcSet={src}\n alt={alt ?? `Avatar of ${fullname}`}\n width={59}\n height={59}\n loading=\"lazy\"\n draggable={false}\n />\n )}\n\n {displayInitials && (\n <div className=\"flex size-full items-center justify-center gap-[0.1rem] font-bold text-sm max-md:py-1\">\n {capitals?.map((capital, index) => (\n <span key={`${capital}-${index}`}>{capital}</span>\n ))}\n </div>\n )}\n\n {displayUserIcon && (\n <User size={getIconSize(size)} aria-label=\"Default user icon\" />\n )}\n </div>\n </div>\n </Container>\n );\n};\n"],"mappings":";;;;;;;;;;AA+BA,MAAM,eAAe;CACnB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;;;;AAKD,MAAMA,mBAAiE;CACrE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAED,MAAM,eAAe,SACnB,iBAAiB,QAAQ;;;;;;;AAQ3B,MAAa,eAAe,MAAc,YAAY,QAAkB;AACtE,KAAI,CAAC,KAAM,QAAO,EAAE;AASpB,SANE,cAAc,MACV,KACG,MAAM,CACN,MAAM,MAAM,GACf,KAAK,MAAM,UAAU,EAEd,OAAO,QAAQ,CAAC,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,CAAC;;;;;AAM1E,MAAM,YAAY,YAMf,EAAE,aAAa,SAAS,GAAG,SAAS,QAAQ;AAC7C,KAAI,eAAe,QACjB,QACE,oBAAC;EACC,GAAK;EACA;EACI;EACT,MAAK;GACL;AAIN,QACE,oBAAC;EACC,GAAK;EACA;EACL,MACE,MAAM,aAAa,UAAa,MAAM,gBAAgB,QAAQ;GAEhE;EAEJ;AAEF,UAAU,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAwBxB,MAAaC,UAA2B,EACtC,UACA,WACA,YAAY,OACZ,aAAa,MACb,KACA,SACA,OAAO,MACP,KACA,YAAY,OACZ,GAAG,YACC;CACJ,MAAM,iBAAiB,QAAQ,IAAI;CACnC,MAAM,gBAAgB,SAAS,YAAY,IAAI,SAAS,EAAE;CAC1D,MAAM,WAAW,WAAW,YAAY,SAAS,GAAG;CAGpD,MAAM,gBAAgB;CACtB,MAAM,gBAAgB,cAAc,CAAC,iBAAiB;CACtD,MAAM,kBACJ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB;CACpD,MAAM,kBACJ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB,CAAC;CAErD,MAAM,cAAc,YAAY;CAChC,MAAM,YAAY,aAAa;CAG/B,MAAM,qBAAqB,cAAc;EACvC,MAAMC,YAAiC,EAAE;AAEzC,MAAI,iBAAiB,IACnB,WAAU,gBAAgB;WACjB,iBAAiB,SAC1B,WAAU,gBAAgB,aAAa;WAC9B,mBAAmB,SAC5B,WAAU,gBAAgB,uBAAuB;WACxC,gBACT,WAAU,gBAAgB;WACjB,eAAe;AACxB,aAAU,gBAAgB;AAC1B,aAAU,eAAe;;AAG3B,MAAI,CAAC,eAAe,UAClB,WAAU,WAAW;AAGvB,MAAI,YACF,WAAU,sBAAsB;AAGlC,SAAO;IACN;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,QACE,oBAAC;EACc;EACb,WAAW,GACT,qDACA,WACA,eACE,2GACF,CAAC,eACC,aACA,2EACF,iBAAiB,iBACjB,UACD;EACQ;EACT,GAAI;EACJ,GAAI;YAEJ,oBAAC;GAAI,WAAU;aACb,qBAAC;IAAI,WAAU;;KACZ,iBACC,oBAAC;MAAO,WAAU;MAAQ,cAAW;OAAwB;KAG9D,iBACC,oBAAC;MACC,WAAU;MACL;MACL,QAAQ;MACR,KAAK,OAAO,aAAa;MACzB,OAAO;MACP,QAAQ;MACR,SAAQ;MACR,WAAW;OACX;KAGH,mBACC,oBAAC;MAAI,WAAU;gBACZ,UAAU,KAAK,SAAS,UACvB,oBAAC,oBAAkC,WAAxB,GAAG,QAAQ,GAAG,QAAyB,CAClD;OACE;KAGP,mBACC,oBAAC;MAAK,MAAM,YAAY,KAAK;MAAE,cAAW;OAAsB;;KAE9D;IACF;GACI"}
@@ -93,13 +93,13 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
93
93
  [`${ButtonColor.PRIMARY}`]: "hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light",
94
94
  [`${ButtonColor.SECONDARY}`]: "hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light",
95
95
  [`${ButtonColor.DESTRUCTIVE}`]: "hover-destructive-500/20 text-destructive ring-destructive-500/20 *:text-text-light",
96
- [`${ButtonColor.NEUTRAL}`]: "hover-neutral-500/20 text-neutral ring-neutral-500/20 *:text-text-light",
96
+ [`${ButtonColor.NEUTRAL}`]: "text-neutral ring-neutral-500/20 *:text-text-light",
97
97
  [`${ButtonColor.CARD}`]: "hover-card-500/20 text-card ring-card-500/20 *:text-text-light",
98
98
  [`${ButtonColor.LIGHT}`]: "hover-white-500/20 text-white ring-white/20 *:text-text-light",
99
- [`${ButtonColor.DARK}`]: "hover-neutral-500/20 text-neutral-800 ring-neutral-800/50 *:text-text-light",
100
- [`${ButtonColor.TEXT}`]: "hover-neutral-500/20 text-text ring-neutral-500/20 *:text-text-opposite",
99
+ [`${ButtonColor.DARK}`]: "text-neutral-800 ring-neutral-800/50 *:text-text-light",
100
+ [`${ButtonColor.TEXT}`]: "text-text ring-neutral-500/20 *:text-text-opposite",
101
101
  [`${ButtonColor.CURRENT}`]: "hover-current-500/20 text-current ring-current/20 *:text-text-light",
102
- [`${ButtonColor.TEXT_INVERSE}`]: "hover-neutral-500/20 text-text-opposite ring-neutral-500/20 *:text-text",
102
+ [`${ButtonColor.TEXT_INVERSE}`]: "text-text-opposite ring-neutral-500/20 *:text-text",
103
103
  [`${ButtonColor.ERROR}`]: "hover-error-500/20 text-error ring-error/20 *:text-text-light",
104
104
  [`${ButtonColor.SUCCESS}`]: "hover-success-500/20 text-success ring-success/20 *:text-text-light",
105
105
  [`${ButtonColor.CUSTOM}`]: ""
@@ -109,7 +109,8 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
109
109
  [`${ContainerRoundedSize.SM}`]: "rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl",
110
110
  [`${ContainerRoundedSize.MD}`]: "rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl",
111
111
  [`${ContainerRoundedSize.LG}`]: "rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl",
112
- [`${ContainerRoundedSize.XL}`]: "rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl"
112
+ [`${ContainerRoundedSize.XL}`]: "rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl",
113
+ [`${ContainerRoundedSize.FULL}`]: "rounded-full"
113
114
  },
114
115
  variant: {
115
116
  [`${ButtonVariant.DEFAULT}`]: [
@@ -120,15 +121,15 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
120
121
  ],
121
122
  [`${ButtonVariant.OUTLINE}`]: [
122
123
  "rounded-2xl border-[1.5px] border-current bg-current/0 *:text-current!",
123
- "hover:bg-current/10",
124
+ "hover:bg-current/20",
124
125
  "hover:ring-6",
125
126
  "aria-selected:ring-6"
126
127
  ],
127
128
  [`${ButtonVariant.NONE}`]: "border-none bg-current/0 text-inherit hover:bg-current/0",
128
129
  [`${ButtonVariant.LINK}`]: "h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline",
129
130
  [`${ButtonVariant.INVISIBLE_LINK}`]: "h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent",
130
- [`${ButtonVariant.HOVERABLE}`]: "rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/10 aria-[current]:bg-current/5",
131
- [`${ButtonVariant.FADE}`]: "rounded-lg border-none bg-current/10 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5",
131
+ [`${ButtonVariant.HOVERABLE}`]: "rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5",
132
+ [`${ButtonVariant.FADE}`]: "rounded-lg border-none bg-current/20 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5",
132
133
  [`${ButtonVariant.INPUT}`]: [
133
134
  "text-text",
134
135
  "w-full select-text resize-none rounded-2xl text-base shadow-none outline-none supports-[corner-shape:squircle]:rounded-4xl",
@@ -1 +1 @@
1
- {"version":3,"file":"Button.mjs","names":["Button: FC<ButtonProps>"],"sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport type { LucideIcon } from 'lucide-react';\nimport type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { cn } from '../../utils/cn';\nimport { ContainerRoundedSize } from '../Container';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport enum ButtonSize {\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.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-4',\n [`${ButtonSize.ICON_XL}`]: 'size-5',\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.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-2',\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 'hover-neutral-500/20 text-neutral ring-neutral-500/20 *: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 'hover-neutral-500/20 text-neutral-800 ring-neutral-800/50 *:text-text-light',\n [`${ButtonColor.TEXT}`]:\n 'hover-neutral-500/20 text-text ring-neutral-500/20 *:text-text-opposite',\n [`${ButtonColor.CURRENT}`]:\n 'hover-current-500/20 text-current ring-current/20 *:text-text-light',\n [`${ButtonColor.TEXT_INVERSE}`]:\n 'hover-neutral-500/20 text-text-opposite ring-neutral-500/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 [`${ContainerRoundedSize.NONE}`]: 'rounded-none',\n [`${ContainerRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${ContainerRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${ContainerRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${ContainerRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n },\n variant: {\n [`${ButtonVariant.DEFAULT}`]: [\n 'bg-current',\n 'hover:bg-current/90',\n\n // Hover ring (similar spirit to your input)\n 'hover:ring-6', // width\n 'aria-selected:ring-6',\n ],\n\n [`${ButtonVariant.OUTLINE}`]: [\n 'rounded-2xl border-[1.5px] border-current bg-current/0 *:text-current!',\n 'hover:bg-current/10',\n\n // Same hover ring behavior as DEFAULT for coherence\n 'hover:ring-6',\n 'aria-selected:ring-6',\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/10 aria-[current]:bg-current/5',\n\n [`${ButtonVariant.FADE}`]:\n 'rounded-lg border-none bg-current/10 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/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: `${ContainerRoundedSize.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 = false,\n isSelected = false,\n isFullWidth = false,\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 w-0 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-5',\n isLoading && size === ButtonSize.XL && 'w-6'\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":"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"}
1
+ {"version":3,"file":"Button.mjs","names":["Button: FC<ButtonProps>"],"sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport type { LucideIcon } from 'lucide-react';\nimport type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { cn } from '../../utils/cn';\nimport { ContainerRoundedSize } from '../Container';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport enum ButtonSize {\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.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-4',\n [`${ButtonSize.ICON_XL}`]: 'size-5',\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.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-2',\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/20 *: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-neutral-800/50 *:text-text-light',\n [`${ButtonColor.TEXT}`]:\n 'text-text ring-neutral-500/20 *:text-text-opposite',\n [`${ButtonColor.CURRENT}`]:\n 'hover-current-500/20 text-current ring-current/20 *:text-text-light',\n [`${ButtonColor.TEXT_INVERSE}`]:\n 'text-text-opposite ring-neutral-500/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 [`${ContainerRoundedSize.NONE}`]: 'rounded-none',\n [`${ContainerRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${ContainerRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${ContainerRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${ContainerRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n [`${ContainerRoundedSize.FULL}`]: 'rounded-full',\n },\n variant: {\n [`${ButtonVariant.DEFAULT}`]: [\n 'bg-current',\n 'hover:bg-current/90',\n\n // Hover ring (similar spirit to your input)\n 'hover:ring-6', // width\n 'aria-selected:ring-6',\n ],\n\n [`${ButtonVariant.OUTLINE}`]: [\n 'rounded-2xl border-[1.5px] border-current bg-current/0 *:text-current!',\n 'hover:bg-current/20',\n\n // Same hover ring behavior as DEFAULT for coherence\n 'hover:ring-6',\n 'aria-selected:ring-6',\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/20 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/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: `${ContainerRoundedSize.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 = false,\n isSelected = false,\n isFullWidth = false,\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 w-0 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-5',\n isLoading && size === ButtonSize.XL && 'w-6'\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":"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"}
@@ -6,7 +6,7 @@ import { jsx } from "react/jsx-runtime";
6
6
  const CodeDefault = ({ children }) => /* @__PURE__ */ jsx("div", {
7
7
  className: "min-w-0 max-w-full overflow-x-auto",
8
8
  children: /* @__PURE__ */ jsx("pre", {
9
- className: "min-w-0 max-w-full overflow-x-auto",
9
+ className: "min-w-0 max-w-full overflow-x-auto [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
10
10
  children: /* @__PURE__ */ jsx("code", { children: children.split("\n").map((line, index) => /* @__PURE__ */ jsx("span", {
11
11
  className: "line block w-full",
12
12
  children: line
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockClient.mjs","names":["CodeDefault: FC<{ children: string }>","CodeBlock: FC<CodeBlockProps>"],"sources":["../../../../src/components/IDE/CodeBlockClient.tsx"],"sourcesContent":["import { type FC, type HTMLAttributes, lazy, Suspense } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { cn } from '../../utils/cn';\n\nexport const CodeDefault: FC<{ children: string }> = ({ children }) => (\n <div className=\"min-w-0 max-w-full overflow-x-auto\">\n <pre className=\"min-w-0 max-w-full overflow-x-auto\">\n <code>\n {children.split('\\n').map((line, index) => (\n <span\n className=\"line block w-full\"\n key={`line-${index}-${line.slice(0, 10)}`}\n >\n {line}\n </span>\n ))}\n </code>\n </pre>\n </div>\n);\n\n// Lazy load the Shiki component\nconst CodeBlockShiki = lazy(() =>\n import('./CodeBlockShiki').then((mod) => ({\n default: mod.CodeBlockShiki,\n }))\n);\n\nexport type CodeBlockProps = {\n children: string;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n isEditable?: boolean;\n onChange?: (content: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>;\n\nexport const CodeBlock: FC<CodeBlockProps> = ({\n className,\n onChange,\n isEditable,\n children,\n lang,\n isDarkMode,\n ...props\n}) => (\n <div\n className={cn('flex w-full min-w-0 max-w-full overflow-x-auto', className)}\n {...props}\n >\n <Suspense fallback={<CodeDefault>{children}</CodeDefault>}>\n <CodeBlockShiki lang={lang} isDarkMode={isDarkMode}>\n {children}\n </CodeBlockShiki>\n </Suspense>\n </div>\n);\n"],"mappings":";;;;;AAIA,MAAaA,eAAyC,EAAE,eACtD,oBAAC;CAAI,WAAU;WACb,oBAAC;EAAI,WAAU;YACb,oBAAC,oBACE,SAAS,MAAM,KAAK,CAAC,KAAK,MAAM,UAC/B,oBAAC;GACC,WAAU;aAGT;KAFI,QAAQ,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,GAGlC,CACP,GACG;GACH;EACF;AAIR,MAAM,iBAAiB,WACrB,OAAO,wBAAoB,MAAM,SAAS,EACxC,SAAS,IAAI,gBACd,EAAE,CACJ;AAUD,MAAaC,aAAiC,EAC5C,WACA,UACA,YACA,UACA,MACA,YACA,GAAG,YAEH,oBAAC;CACC,WAAW,GAAG,kDAAkD,UAAU;CAC1E,GAAI;WAEJ,oBAAC;EAAS,UAAU,oBAAC,eAAa,WAAuB;YACvD,oBAAC;GAAqB;GAAkB;GACrC;IACc;GACR;EACP"}
1
+ {"version":3,"file":"CodeBlockClient.mjs","names":["CodeDefault: FC<{ children: string }>","CodeBlock: FC<CodeBlockProps>"],"sources":["../../../../src/components/IDE/CodeBlockClient.tsx"],"sourcesContent":["import { type FC, type HTMLAttributes, lazy, Suspense } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { cn } from '../../utils/cn';\n\nexport const CodeDefault: FC<{ children: string }> = ({ children }) => (\n <div className=\"min-w-0 max-w-full overflow-x-auto\">\n <pre className=\"min-w-0 max-w-full overflow-x-auto [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <code>\n {children.split('\\n').map((line, index) => (\n <span\n className=\"line block w-full\"\n key={`line-${index}-${line.slice(0, 10)}`}\n >\n {line}\n </span>\n ))}\n </code>\n </pre>\n </div>\n);\n\n// Lazy load the Shiki component\nconst CodeBlockShiki = lazy(() =>\n import('./CodeBlockShiki').then((mod) => ({\n default: mod.CodeBlockShiki,\n }))\n);\n\nexport type CodeBlockProps = {\n children: string;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n isEditable?: boolean;\n onChange?: (content: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>;\n\nexport const CodeBlock: FC<CodeBlockProps> = ({\n className,\n onChange,\n isEditable,\n children,\n lang,\n isDarkMode,\n ...props\n}) => (\n <div\n className={cn('flex w-full min-w-0 max-w-full overflow-x-auto', className)}\n {...props}\n >\n <Suspense fallback={<CodeDefault>{children}</CodeDefault>}>\n <CodeBlockShiki lang={lang} isDarkMode={isDarkMode}>\n {children}\n </CodeBlockShiki>\n </Suspense>\n </div>\n);\n"],"mappings":";;;;;AAIA,MAAaA,eAAyC,EAAE,eACtD,oBAAC;CAAI,WAAU;WACb,oBAAC;EAAI,WAAU;YACb,oBAAC,oBACE,SAAS,MAAM,KAAK,CAAC,KAAK,MAAM,UAC/B,oBAAC;GACC,WAAU;aAGT;KAFI,QAAQ,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,GAGlC,CACP,GACG;GACH;EACF;AAIR,MAAM,iBAAiB,WACrB,OAAO,wBAAoB,MAAM,SAAS,EACxC,SAAS,IAAI,gBACd,EAAE,CACJ;AAUD,MAAaC,aAAiC,EAC5C,WACA,UACA,YACA,UACA,MACA,YACA,GAAG,YAEH,oBAAC;CACC,WAAW,GAAG,kDAAkD,UAAU;CAC1E,GAAI;WAEJ,oBAAC;EAAS,UAAU,oBAAC,eAAa,WAAuB;YACvD,oBAAC;GAAqB;GAAkB;GACrC;IACc;GACR;EACP"}
@@ -94,6 +94,7 @@ const CodeBlockShiki = ({ children, lang, isDarkMode }) => {
94
94
  isDarkMode
95
95
  ]);
96
96
  return /* @__PURE__ */ jsx("div", {
97
+ className: "min-w-0 max-w-full overflow-auto bg-transparent [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&_pre::-webkit-scrollbar]:hidden [&_pre]:[-ms-overflow-style:none] [&_pre]:[scrollbar-width:none]",
97
98
  style: {
98
99
  backgroundColor: "transparent",
99
100
  minWidth: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockShiki.mjs","names":["languageModule: any","themeModule: any","themeName: BundledTheme","CodeBlockShiki: FC<CodeBlockShikiProps>"],"sources":["../../../../src/components/IDE/CodeBlockShiki.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useEffect, useState } from 'react';\nimport type {\n BundledLanguage,\n BundledTheme,\n CodeToHastOptions,\n} from 'shiki/bundle/web';\nimport { CodeDefault } from './CodeBlockClient';\n\n// Map of loaded modules to avoid re-importing\nconst languageCache = new Map<BundledLanguage, any>();\nconst themeCache = new Map<BundledTheme, any>();\n\n// Lazy load language modules\nconst loadLanguage = async (lang: BundledLanguage): Promise<any> => {\n if (languageCache.has(lang)) {\n return languageCache.get(lang);\n }\n\n let languageModule: any;\n\n switch (lang) {\n case 'typescript':\n case 'ts':\n languageModule = await import('shiki/langs/typescript.mjs');\n break;\n case 'javascript':\n case 'js':\n languageModule = await import('shiki/langs/javascript.mjs');\n break;\n case 'bash':\n case 'sh':\n case 'shell':\n languageModule = await import('shiki/langs/bash.mjs');\n break;\n case 'json':\n languageModule = await import('shiki/langs/json.mjs');\n break;\n case 'tsx':\n languageModule = await import('shiki/langs/tsx.mjs');\n break;\n case 'vue':\n languageModule = await import('shiki/langs/vue.mjs');\n break;\n case 'html':\n languageModule = await import('shiki/langs/html.mjs');\n break;\n default:\n // Fallback to typescript for unknown languages\n languageModule = await import('shiki/langs/typescript.mjs');\n break;\n }\n\n const language = languageModule.default;\n languageCache.set(lang, language);\n return language;\n};\n\n// Lazy load theme modules\nconst loadTheme = async (themeName: BundledTheme): Promise<any> => {\n if (themeCache.has(themeName)) {\n return themeCache.get(themeName);\n }\n\n let themeModule: any;\n\n switch (themeName) {\n case 'github-dark':\n themeModule = await import('shiki/themes/github-dark.mjs');\n break;\n case 'github-light':\n themeModule = await import('shiki/themes/github-light.mjs');\n break;\n default:\n themeModule = await import('shiki/themes/github-light.mjs');\n break;\n }\n\n const theme = themeModule.default;\n themeCache.set(themeName, theme);\n return theme;\n};\n\n// Create a promise for highlighting\nconst highlightCode = async (\n code: string,\n lang: BundledLanguage,\n isDarkMode?: boolean\n): Promise<string> => {\n const themeName: BundledTheme = isDarkMode ? 'github-dark' : 'github-light';\n\n // Lazy load shiki, language, and theme in parallel\n const [{ codeToHtml }, languageModule, themeModule] = await Promise.all([\n import('shiki/bundle/web'),\n loadLanguage(lang),\n loadTheme(themeName),\n ]);\n\n const shikiOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n lang,\n theme: themeModule,\n };\n\n return codeToHtml(code, {\n ...shikiOptions,\n langs: [languageModule],\n } as any);\n};\n\nexport type CodeBlockShikiProps = {\n children: string;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n};\n\nexport const CodeBlockShiki: FC<CodeBlockShikiProps> = ({\n children,\n lang,\n isDarkMode,\n}) => {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let isCancelled = false;\n\n setHtml(null);\n\n highlightCode(children, lang, isDarkMode)\n .then((result) => {\n if (!isCancelled) setHtml(result);\n })\n .catch(() => {\n if (!isCancelled) setHtml('');\n });\n\n return () => {\n isCancelled = true;\n };\n }, [children, lang, isDarkMode]);\n\n return (\n <div\n style={{\n backgroundColor: 'transparent',\n minWidth: 0,\n maxWidth: '100%',\n overflow: 'auto',\n }}\n >\n {html ? (\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki generates safe HTML for code highlighting\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <CodeDefault>{children}</CodeDefault>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAWA,MAAM,gCAAgB,IAAI,KAA2B;AACrD,MAAM,6BAAa,IAAI,KAAwB;AAG/C,MAAM,eAAe,OAAO,SAAwC;AAClE,KAAI,cAAc,IAAI,KAAK,CACzB,QAAO,cAAc,IAAI,KAAK;CAGhC,IAAIA;AAEJ,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;EACL,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;EACL,KAAK;EACL,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF;AAEE,oBAAiB,MAAM,OAAO;AAC9B;;CAGJ,MAAM,WAAW,eAAe;AAChC,eAAc,IAAI,MAAM,SAAS;AACjC,QAAO;;AAIT,MAAM,YAAY,OAAO,cAA0C;AACjE,KAAI,WAAW,IAAI,UAAU,CAC3B,QAAO,WAAW,IAAI,UAAU;CAGlC,IAAIC;AAEJ,SAAQ,WAAR;EACE,KAAK;AACH,iBAAc,MAAM,OAAO;AAC3B;EACF,KAAK;AACH,iBAAc,MAAM,OAAO;AAC3B;EACF;AACE,iBAAc,MAAM,OAAO;AAC3B;;CAGJ,MAAM,QAAQ,YAAY;AAC1B,YAAW,IAAI,WAAW,MAAM;AAChC,QAAO;;AAIT,MAAM,gBAAgB,OACpB,MACA,MACA,eACoB;CACpB,MAAMC,YAA0B,aAAa,gBAAgB;CAG7D,MAAM,CAAC,EAAE,cAAc,gBAAgB,eAAe,MAAM,QAAQ,IAAI;EACtE,OAAO;EACP,aAAa,KAAK;EAClB,UAAU,UAAU;EACrB,CAAC;AAOF,QAAO,WAAW,MAAM;EAJtB;EACA,OAAO;EAKP,OAAO,CAAC,eAAe;EACxB,CAAQ;;AASX,MAAaC,kBAA2C,EACtD,UACA,MACA,iBACI;CACJ,MAAM,CAAC,MAAM,WAAW,SAAwB,KAAK;AAErD,iBAAgB;EACd,IAAI,cAAc;AAElB,UAAQ,KAAK;AAEb,gBAAc,UAAU,MAAM,WAAW,CACtC,MAAM,WAAW;AAChB,OAAI,CAAC,YAAa,SAAQ,OAAO;IACjC,CACD,YAAY;AACX,OAAI,CAAC,YAAa,SAAQ,GAAG;IAC7B;AAEJ,eAAa;AACX,iBAAc;;IAEf;EAAC;EAAU;EAAM;EAAW,CAAC;AAEhC,QACE,oBAAC;EACC,OAAO;GACL,iBAAiB;GACjB,UAAU;GACV,UAAU;GACV,UAAU;GACX;YAEA,OAEC,oBAAC,SAAI,yBAAyB,EAAE,QAAQ,MAAM,GAAI,GAElD,oBAAC,eAAa,WAAuB;GAEnC"}
1
+ {"version":3,"file":"CodeBlockShiki.mjs","names":["languageModule: any","themeModule: any","themeName: BundledTheme","CodeBlockShiki: FC<CodeBlockShikiProps>"],"sources":["../../../../src/components/IDE/CodeBlockShiki.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useEffect, useState } from 'react';\nimport type {\n BundledLanguage,\n BundledTheme,\n CodeToHastOptions,\n} from 'shiki/bundle/web';\nimport { CodeDefault } from './CodeBlockClient';\n\n// Map of loaded modules to avoid re-importing\nconst languageCache = new Map<BundledLanguage, any>();\nconst themeCache = new Map<BundledTheme, any>();\n\n// Lazy load language modules\nconst loadLanguage = async (lang: BundledLanguage): Promise<any> => {\n if (languageCache.has(lang)) {\n return languageCache.get(lang);\n }\n\n let languageModule: any;\n\n switch (lang) {\n case 'typescript':\n case 'ts':\n languageModule = await import('shiki/langs/typescript.mjs');\n break;\n case 'javascript':\n case 'js':\n languageModule = await import('shiki/langs/javascript.mjs');\n break;\n case 'bash':\n case 'sh':\n case 'shell':\n languageModule = await import('shiki/langs/bash.mjs');\n break;\n case 'json':\n languageModule = await import('shiki/langs/json.mjs');\n break;\n case 'tsx':\n languageModule = await import('shiki/langs/tsx.mjs');\n break;\n case 'vue':\n languageModule = await import('shiki/langs/vue.mjs');\n break;\n case 'html':\n languageModule = await import('shiki/langs/html.mjs');\n break;\n default:\n // Fallback to typescript for unknown languages\n languageModule = await import('shiki/langs/typescript.mjs');\n break;\n }\n\n const language = languageModule.default;\n languageCache.set(lang, language);\n return language;\n};\n\n// Lazy load theme modules\nconst loadTheme = async (themeName: BundledTheme): Promise<any> => {\n if (themeCache.has(themeName)) {\n return themeCache.get(themeName);\n }\n\n let themeModule: any;\n\n switch (themeName) {\n case 'github-dark':\n themeModule = await import('shiki/themes/github-dark.mjs');\n break;\n case 'github-light':\n themeModule = await import('shiki/themes/github-light.mjs');\n break;\n default:\n themeModule = await import('shiki/themes/github-light.mjs');\n break;\n }\n\n const theme = themeModule.default;\n themeCache.set(themeName, theme);\n return theme;\n};\n\n// Create a promise for highlighting\nconst highlightCode = async (\n code: string,\n lang: BundledLanguage,\n isDarkMode?: boolean\n): Promise<string> => {\n const themeName: BundledTheme = isDarkMode ? 'github-dark' : 'github-light';\n\n // Lazy load shiki, language, and theme in parallel\n const [{ codeToHtml }, languageModule, themeModule] = await Promise.all([\n import('shiki/bundle/web'),\n loadLanguage(lang),\n loadTheme(themeName),\n ]);\n\n const shikiOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n lang,\n theme: themeModule,\n };\n\n return codeToHtml(code, {\n ...shikiOptions,\n langs: [languageModule],\n } as any);\n};\n\nexport type CodeBlockShikiProps = {\n children: string;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n};\n\nexport const CodeBlockShiki: FC<CodeBlockShikiProps> = ({\n children,\n lang,\n isDarkMode,\n}) => {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let isCancelled = false;\n\n setHtml(null);\n\n highlightCode(children, lang, isDarkMode)\n .then((result) => {\n if (!isCancelled) setHtml(result);\n })\n .catch(() => {\n if (!isCancelled) setHtml('');\n });\n\n return () => {\n isCancelled = true;\n };\n }, [children, lang, isDarkMode]);\n\n return (\n <div\n className=\"min-w-0 max-w-full overflow-auto bg-transparent [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&_pre::-webkit-scrollbar]:hidden [&_pre]:[-ms-overflow-style:none] [&_pre]:[scrollbar-width:none]\"\n style={{\n backgroundColor: 'transparent',\n minWidth: 0,\n maxWidth: '100%',\n overflow: 'auto',\n }}\n >\n {html ? (\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki generates safe HTML for code highlighting\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <CodeDefault>{children}</CodeDefault>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAWA,MAAM,gCAAgB,IAAI,KAA2B;AACrD,MAAM,6BAAa,IAAI,KAAwB;AAG/C,MAAM,eAAe,OAAO,SAAwC;AAClE,KAAI,cAAc,IAAI,KAAK,CACzB,QAAO,cAAc,IAAI,KAAK;CAGhC,IAAIA;AAEJ,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;EACL,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;EACL,KAAK;EACL,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF,KAAK;AACH,oBAAiB,MAAM,OAAO;AAC9B;EACF;AAEE,oBAAiB,MAAM,OAAO;AAC9B;;CAGJ,MAAM,WAAW,eAAe;AAChC,eAAc,IAAI,MAAM,SAAS;AACjC,QAAO;;AAIT,MAAM,YAAY,OAAO,cAA0C;AACjE,KAAI,WAAW,IAAI,UAAU,CAC3B,QAAO,WAAW,IAAI,UAAU;CAGlC,IAAIC;AAEJ,SAAQ,WAAR;EACE,KAAK;AACH,iBAAc,MAAM,OAAO;AAC3B;EACF,KAAK;AACH,iBAAc,MAAM,OAAO;AAC3B;EACF;AACE,iBAAc,MAAM,OAAO;AAC3B;;CAGJ,MAAM,QAAQ,YAAY;AAC1B,YAAW,IAAI,WAAW,MAAM;AAChC,QAAO;;AAIT,MAAM,gBAAgB,OACpB,MACA,MACA,eACoB;CACpB,MAAMC,YAA0B,aAAa,gBAAgB;CAG7D,MAAM,CAAC,EAAE,cAAc,gBAAgB,eAAe,MAAM,QAAQ,IAAI;EACtE,OAAO;EACP,aAAa,KAAK;EAClB,UAAU,UAAU;EACrB,CAAC;AAOF,QAAO,WAAW,MAAM;EAJtB;EACA,OAAO;EAKP,OAAO,CAAC,eAAe;EACxB,CAAQ;;AASX,MAAaC,kBAA2C,EACtD,UACA,MACA,iBACI;CACJ,MAAM,CAAC,MAAM,WAAW,SAAwB,KAAK;AAErD,iBAAgB;EACd,IAAI,cAAc;AAElB,UAAQ,KAAK;AAEb,gBAAc,UAAU,MAAM,WAAW,CACtC,MAAM,WAAW;AAChB,OAAI,CAAC,YAAa,SAAQ,OAAO;IACjC,CACD,YAAY;AACX,OAAI,CAAC,YAAa,SAAQ,GAAG;IAC7B;AAEJ,eAAa;AACX,iBAAc;;IAEf;EAAC;EAAU;EAAM;EAAW,CAAC;AAEhC,QACE,oBAAC;EACC,WAAU;EACV,OAAO;GACL,iBAAiB;GACjB,UAAU;GACV,UAAU;GACV,UAAU;GACX;YAEA,OAEC,oBAAC,SAAI,yBAAyB,EAAE,QAAQ,MAAM,GAAI,GAElD,oBAAC,eAAa,WAAuB;GAEnC"}
@@ -53,6 +53,26 @@ let LinkColor = /* @__PURE__ */ function(LinkColor$1) {
53
53
  LinkColor$1["CUSTOM"] = "custom";
54
54
  return LinkColor$1;
55
55
  }({});
56
+ /** Available rounded corner sizes for the container */
57
+ let LinkRoundedSize = /* @__PURE__ */ function(LinkRoundedSize$1) {
58
+ LinkRoundedSize$1["NONE"] = "none";
59
+ LinkRoundedSize$1["SM"] = "sm";
60
+ LinkRoundedSize$1["MD"] = "md";
61
+ LinkRoundedSize$1["LG"] = "lg";
62
+ LinkRoundedSize$1["XL"] = "xl";
63
+ LinkRoundedSize$1["TWO_XL"] = "2xl";
64
+ LinkRoundedSize$1["THREE_XL"] = "3xl";
65
+ LinkRoundedSize$1["FULL"] = "full";
66
+ return LinkRoundedSize$1;
67
+ }({});
68
+ let LinkSize = /* @__PURE__ */ function(LinkSize$1) {
69
+ LinkSize$1["SM"] = "sm";
70
+ LinkSize$1["MD"] = "md";
71
+ LinkSize$1["LG"] = "lg";
72
+ LinkSize$1["XL"] = "xl";
73
+ LinkSize$1["CUSTOM"] = "custom";
74
+ return LinkSize$1;
75
+ }({});
56
76
  /**
57
77
  * Underline style options for Link component
58
78
  *
@@ -71,14 +91,24 @@ let LinkUnderlined = /* @__PURE__ */ function(LinkUnderlined$1) {
71
91
  * Class variance authority configuration for Link component styling
72
92
  * Defines the visual appearance based on variant, color, and underline options
73
93
  */
74
- const linkVariants = cva("gap-3 transition focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50", {
94
+ const linkVariants = cva("gap-3 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50", {
75
95
  variants: {
76
96
  variant: {
77
97
  [`${LinkVariant.DEFAULT}`]: "h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0 hover:underline",
78
98
  [`${LinkVariant.INVISIBLE_LINK}`]: "h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0",
79
- [`${LinkVariant.BUTTON}`]: "flex min-h-8 items-center justify-center gap-2 whitespace-nowrap rounded-lg bg-current px-6 font-medium text-sm transition *:text-text-opposite focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 max-md:py-2",
80
- [`${LinkVariant.BUTTON_OUTLINED}`]: "flex min-h-8 items-center justify-center gap-2 whitespace-nowrap rounded-lg border-[1.5px] px-6 font-medium text-sm transition hover:bg-current/30 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 max-md:py-2",
81
- [`${LinkVariant.HOVERABLE}`]: "block rounded-lg border-none bg-current/0 transition hover:bg-current/20 aria-[current]:bg-current/5"
99
+ [`${LinkVariant.BUTTON}`]: "relative flex cursor-pointer flex-row items-center justify-center gap-2 rounded-full bg-current text-center font-medium text-text ring-0 *:text-text-opposite hover:bg-current/90 hover:ring-6 aria-selected:ring-6",
100
+ [`${LinkVariant.BUTTON_OUTLINED}`]: "relative flex cursor-pointer flex-row items-center justify-center gap-2 rounded-full border-[1.5px] border-current text-center font-medium text-text ring-0 *:text-text hover:bg-current/20 hover:ring-6 aria-selected:ring-6",
101
+ [`${LinkVariant.HOVERABLE}`]: "block rounded-lg border-none bg-current/0 hover:bg-current/10 aria-[current]:bg-current/5"
102
+ },
103
+ roundedSize: {
104
+ [`${LinkRoundedSize.NONE}`]: "rounded-none",
105
+ [`${LinkRoundedSize.SM}`]: "rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl",
106
+ [`${LinkRoundedSize.MD}`]: "rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl",
107
+ [`${LinkRoundedSize.LG}`]: "rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl",
108
+ [`${LinkRoundedSize.XL}`]: "rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl",
109
+ [`${LinkRoundedSize.TWO_XL}`]: "rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]",
110
+ [`${LinkRoundedSize.THREE_XL}`]: "rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]",
111
+ [`${LinkRoundedSize.FULL}`]: "rounded-full"
82
112
  },
83
113
  color: {
84
114
  [`${LinkColor.PRIMARY}`]: "text-primary",
@@ -93,16 +123,97 @@ const linkVariants = cva("gap-3 transition focus-visible:outline-hidden disabled
93
123
  [`${LinkColor.SUCCESS}`]: "text-success",
94
124
  [`${LinkColor.CUSTOM}`]: ""
95
125
  },
126
+ size: {
127
+ [`${LinkSize.SM}`]: "text-sm",
128
+ [`${LinkSize.MD}`]: "text-base",
129
+ [`${LinkSize.LG}`]: "text-lg",
130
+ [`${LinkSize.XL}`]: "text-xl",
131
+ [`${LinkSize.CUSTOM}`]: ""
132
+ },
96
133
  underlined: {
97
134
  [LinkUnderlined.DEFAULT]: "",
98
135
  [LinkUnderlined.TRUE]: "underline",
99
136
  [LinkUnderlined.FALSE]: "no-underline"
100
137
  }
101
138
  },
139
+ compoundVariants: [
140
+ {
141
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
142
+ size: LinkSize.SM,
143
+ class: "min-h-7 px-3 max-md:py-1"
144
+ },
145
+ {
146
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
147
+ size: LinkSize.MD,
148
+ class: "min-h-8 px-6 max-md:py-2"
149
+ },
150
+ {
151
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
152
+ size: LinkSize.LG,
153
+ class: "min-h-10 px-8 max-md:py-3"
154
+ },
155
+ {
156
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
157
+ size: LinkSize.XL,
158
+ class: "min-h-11 px-10 max-md:py-4"
159
+ },
160
+ {
161
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
162
+ color: LinkColor.PRIMARY,
163
+ class: "ring-primary/20"
164
+ },
165
+ {
166
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
167
+ color: LinkColor.SECONDARY,
168
+ class: "ring-secondary/20"
169
+ },
170
+ {
171
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
172
+ color: LinkColor.DESTRUCTIVE,
173
+ class: "ring-destructive/20"
174
+ },
175
+ {
176
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
177
+ color: LinkColor.NEUTRAL,
178
+ class: "ring-neutral/20"
179
+ },
180
+ {
181
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
182
+ color: LinkColor.LIGHT,
183
+ class: "ring-white/20"
184
+ },
185
+ {
186
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
187
+ color: LinkColor.DARK,
188
+ class: "ring-neutral-800/20"
189
+ },
190
+ {
191
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
192
+ color: LinkColor.TEXT,
193
+ class: "ring-text/20"
194
+ },
195
+ {
196
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
197
+ color: LinkColor.TEXT_INVERSE,
198
+ class: "ring-text-opposite/20"
199
+ },
200
+ {
201
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
202
+ color: LinkColor.ERROR,
203
+ class: "ring-error/20"
204
+ },
205
+ {
206
+ variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
207
+ color: LinkColor.SUCCESS,
208
+ class: "ring-success/20"
209
+ }
210
+ ],
102
211
  defaultVariants: {
103
212
  variant: LinkVariant.DEFAULT,
104
213
  color: LinkColor.PRIMARY,
105
- underlined: LinkUnderlined.DEFAULT
214
+ roundedSize: LinkRoundedSize.MD,
215
+ underlined: LinkUnderlined.DEFAULT,
216
+ size: LinkSize.MD
106
217
  }
107
218
  });
108
219
  /**
@@ -113,7 +224,7 @@ const linkVariants = cva("gap-3 transition focus-visible:outline-hidden disabled
113
224
  *
114
225
  * @example
115
226
  * ```tsx
116
- * checkIsExternalLink({ href: 'https://example.com' }) // true
227
+ * checkIsExternalLink({ href: '[https://example.com](https://example.com)' }) // true
117
228
  * checkIsExternalLink({ href: '/internal-page' }) // false
118
229
  * checkIsExternalLink({ href: '/page', isExternalLink: true }) // true
119
230
  * ```
@@ -127,100 +238,19 @@ const checkIsExternalLink = ({ href, isExternalLink: isExternalLinkProp }) => {
127
238
  *
128
239
  * A versatile link component that handles both internal and external navigation
129
240
  * with comprehensive internationalization support and multiple visual variants.
130
- *
131
- * ## Key Features
132
- * - **Multiple Variants**: Default, invisible, button, outlined button, and hoverable styles
133
- * - **Color Themes**: Comprehensive color palette for different contexts and meanings
134
- * - **External Link Detection**: Automatic detection and handling of external URLs
135
- * - **Internationalization**: Built-in support for localized URLs via Intlayer
136
- * - **Security**: Automatic security attributes for external links (noopener, noreferrer)
137
- * - **Accessibility**: Full ARIA support with proper labels and current page indication
138
- * - **Visual Feedback**: Hover effects, underline options, and active states
139
- *
140
- * ## Use Cases
141
- * - Navigation within applications (internal links)
142
- * - External links to other websites with security measures
143
- * - Button-styled links for call-to-action scenarios
144
- * - Subtle hoverable links for navigation menus
145
- * - Multi-language website navigation with automatic URL localization
146
- *
147
- * ## Security Features
148
- * External links automatically receive security attributes:
149
- * - `rel="noopener noreferrer nofollow"` - Prevents security vulnerabilities
150
- * - `target="_blank"` - Opens in new tab/window
151
- * - External link icon indication for user clarity
152
- *
153
- * ## Internationalization
154
- * When used with Intlayer, the component automatically:
155
- * - Localizes internal URLs based on the current or specified locale
156
- * - Sets appropriate `hrefLang` attributes for SEO
157
- * - Maintains proper URL structure for multi-language sites
158
- *
159
- * @component
160
- * @example
161
- * ```tsx
162
- * // Basic internal link
163
- * <Link href="/about" label="Go to about page">
164
- * About Us
165
- * </Link>
166
- *
167
- * // External link with auto-detection
168
- * <Link href="https://example.com" label="Visit external site">
169
- * External Site
170
- * </Link>
171
- *
172
- * // Button-styled link
173
- * <Link
174
- * href="/signup"
175
- * variant={LinkVariant.BUTTON}
176
- * color={LinkColor.PRIMARY}
177
- * label="Sign up for account"
178
- * >
179
- * Get Started
180
- * </Link>
181
- *
182
- * // Localized link
183
- * <Link
184
- * href="/products"
185
- * locale="fr"
186
- * label="Voir les produits"
187
- * >
188
- * Produits
189
- * </Link>
190
- *
191
- * // Active navigation link
192
- * <Link
193
- * href="/dashboard"
194
- * isActive={true}
195
- * variant={LinkVariant.HOVERABLE}
196
- * label="Current page: Dashboard"
197
- * >
198
- * Dashboard
199
- * </Link>
200
- * ```
201
- *
202
- * @param props - Link component props
203
- * @param props.children - Content to display inside the link
204
- * @param props.href - URL or path to navigate to
205
- * @param props.label - Accessible label describing the link's purpose
206
- * @param props.variant - Visual style variant
207
- * @param props.color - Color theme for the link
208
- * @param props.underlined - Underline visibility option
209
- * @param props.isExternalLink - Override external link detection
210
- * @param props.isActive - Whether this link represents the current page
211
- * @param props.locale - Locale for URL internationalization
212
- * @param props.className - Additional CSS classes
213
- * @returns Accessible and internationalized link component
241
+ * ...
214
242
  */
215
243
  const Link = (props) => {
216
- const { variant = LinkVariant.DEFAULT, color = LinkColor.PRIMARY, children, label, className, isActive, underlined, locale, isExternalLink: isExternalLinkProp, isPageSection: isPageSectionProp, href: hrefProp, ...otherProps } = props;
244
+ const { variant = LinkVariant.DEFAULT, color = LinkColor.PRIMARY, roundedSize, children, label, className, isActive, underlined, locale, size, isExternalLink: isExternalLinkProp, isPageSection: isPageSectionProp, href: hrefProp, ...otherProps } = props;
217
245
  const isExternalLink = isExternalLinkProp ?? checkIsExternalLink(props);
218
246
  const isPageSection = isPageSectionProp ?? hrefProp?.startsWith("#") ?? false;
219
247
  const isChildrenString = typeof children === "string";
220
248
  const rel = isExternalLink ? "noopener noreferrer nofollow" : void 0;
221
249
  const target = isExternalLink ? "_blank" : "_self";
250
+ const href = locale && hrefProp && !isExternalLink && !isPageSection ? getLocalizedUrl(hrefProp, locale) : hrefProp;
251
+ const isButton = variant === "button" || variant === "button-outlined";
222
252
  return /* @__PURE__ */ jsxs("a", {
223
- href: locale && hrefProp && !isExternalLink && !isPageSection ? getLocalizedUrl(hrefProp, locale) : hrefProp,
253
+ href,
224
254
  "aria-label": label,
225
255
  rel,
226
256
  target,
@@ -228,12 +258,17 @@ const Link = (props) => {
228
258
  className: cn(linkVariants({
229
259
  variant,
230
260
  color,
261
+ roundedSize,
231
262
  underlined,
232
- className
263
+ size,
264
+ className: isButton ? void 0 : className
233
265
  })),
234
266
  ...otherProps,
235
267
  children: [
236
- variant === "button" ? /* @__PURE__ */ jsx("span", { children }) : children,
268
+ isButton ? /* @__PURE__ */ jsx("span", {
269
+ className,
270
+ children
271
+ }) : children,
237
272
  isExternalLink && isChildrenString && /* @__PURE__ */ jsx(ExternalLink, { className: "ml-2 inline-block size-4" }),
238
273
  isPageSection && /* @__PURE__ */ jsx(MoveRight, { className: "ml-2 inline-block size-4" })
239
274
  ]
@@ -241,5 +276,5 @@ const Link = (props) => {
241
276
  };
242
277
 
243
278
  //#endregion
244
- export { Link, LinkColor, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants };
279
+ export { Link, LinkColor, LinkRoundedSize, LinkSize, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants };
245
280
  //# sourceMappingURL=Link.mjs.map