@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.
- package/dist/esm/components/Avatar/index.mjs +17 -11
- package/dist/esm/components/Avatar/index.mjs.map +1 -1
- package/dist/esm/components/Button/Button.mjs +9 -8
- package/dist/esm/components/Button/Button.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockClient.mjs +1 -1
- package/dist/esm/components/IDE/CodeBlockClient.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockShiki.mjs +1 -0
- package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
- package/dist/esm/components/Link/Link.mjs +130 -95
- package/dist/esm/components/Link/Link.mjs.map +1 -1
- package/dist/esm/components/Link/index.mjs +2 -2
- package/dist/esm/components/index.mjs +2 -2
- package/dist/types/components/Avatar/index.d.ts.map +1 -1
- package/dist/types/components/Badge/index.d.ts +2 -2
- package/dist/types/components/Breadcrumb/index.d.ts +2 -2
- package/dist/types/components/Breadcrumb/index.d.ts.map +1 -1
- package/dist/types/components/Browser/Browser.content.d.ts +11 -11
- package/dist/types/components/Browser/Browser.content.d.ts.map +1 -1
- package/dist/types/components/Button/Button.d.ts +5 -5
- package/dist/types/components/Button/Button.d.ts.map +1 -1
- package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +3 -3
- package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts.map +1 -1
- package/dist/types/components/Command/index.d.ts +20 -20
- package/dist/types/components/Command/index.d.ts.map +1 -1
- package/dist/types/components/Container/index.d.ts +4 -4
- package/dist/types/components/Container/index.d.ts.map +1 -1
- package/dist/types/components/CopyButton/CopyButton.content.d.ts +3 -3
- package/dist/types/components/CopyButton/CopyButton.content.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.d.ts +25 -25
- package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts +9 -9
- package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.d.ts +33 -33
- package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.content.d.ts +25 -25
- package/dist/types/components/DictionaryFieldEditor/NavigationView/navigationViewNode.content.d.ts +25 -25
- package/dist/types/components/DictionaryFieldEditor/SaveForm/saveForm.content.d.ts +33 -33
- package/dist/types/components/DictionaryFieldEditor/StructureView/structureView.content.d.ts +9 -9
- package/dist/types/components/DictionaryFieldEditor/VersionSwitcherDropDown/versionSwitcherDropDown.content.d.ts +7 -7
- package/dist/types/components/DictionaryFieldEditor/dictionaryFieldEditor.content.d.ts +5 -5
- package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts +31 -31
- package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts.map +1 -1
- package/dist/types/components/ExpandCollapse/expandCollapse.content.d.ts +3 -3
- package/dist/types/components/Form/FormBase.d.ts +2 -2
- package/dist/types/components/Form/FormBase.d.ts.map +1 -1
- package/dist/types/components/Form/FormField.d.ts +2 -2
- package/dist/types/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/components/Form/FormItem.d.ts +2 -2
- package/dist/types/components/Form/FormItem.d.ts.map +1 -1
- package/dist/types/components/Form/elements/EditableFieldInputElement.d.ts +2 -2
- package/dist/types/components/Form/elements/EditableFieldTextAreaElement.d.ts +2 -2
- package/dist/types/components/Form/elements/FormElement.d.ts +2 -2
- package/dist/types/components/Form/elements/MultiselectElement.d.ts +2 -2
- package/dist/types/components/Form/elements/OTPElement.d.ts +2 -2
- package/dist/types/components/Form/elements/SelectElement.d.ts +2 -2
- package/dist/types/components/Form/elements/SwitchSelectorElement.d.ts +2 -2
- package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeContext.d.ts +2 -2
- package/dist/types/components/IDE/CodeContext.d.ts.map +1 -1
- package/dist/types/components/IDE/FileTree.d.ts.map +1 -1
- package/dist/types/components/IDE/code.content.d.ts +5 -5
- package/dist/types/components/IDE/copyCode.content.d.ts +5 -5
- package/dist/types/components/Input/Checkbox.d.ts +3 -3
- package/dist/types/components/Input/Checkbox.d.ts.map +1 -1
- package/dist/types/components/Input/Input.d.ts +2 -2
- package/dist/types/components/Input/OTPInput.d.ts +6 -6
- package/dist/types/components/Input/OTPInput.d.ts.map +1 -1
- package/dist/types/components/Input/SearchInput.d.ts +2 -2
- package/dist/types/components/Input/SearchInput.d.ts.map +1 -1
- package/dist/types/components/Link/Link.d.ts +26 -89
- package/dist/types/components/Link/Link.d.ts.map +1 -1
- package/dist/types/components/Link/index.d.ts +2 -2
- package/dist/types/components/Loader/index.content.d.ts +3 -3
- package/dist/types/components/Loader/index.content.d.ts.map +1 -1
- package/dist/types/components/Loader/spinner.d.ts +2 -2
- package/dist/types/components/Loader/spinner.d.ts.map +1 -1
- package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts +17 -17
- package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts.map +1 -1
- package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts +13 -13
- package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts.map +1 -1
- package/dist/types/components/MaxWidthSmoother/index.d.ts +2 -2
- package/dist/types/components/Navbar/Burger.d.ts +2 -2
- package/dist/types/components/Navbar/DesktopNavbar.d.ts +2 -2
- package/dist/types/components/Navbar/MobileNavbar.d.ts +2 -2
- package/dist/types/components/Navbar/MobileNavbar.d.ts.map +1 -1
- package/dist/types/components/Navbar/index.d.ts +2 -2
- package/dist/types/components/Pagination/Pagination.d.ts +4 -4
- package/dist/types/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/types/components/Pagination/pagination.content.d.ts +11 -11
- package/dist/types/components/Popover/static.d.ts +3 -3
- package/dist/types/components/Popover/static.d.ts.map +1 -1
- package/dist/types/components/Select/Select.d.ts +3 -3
- package/dist/types/components/Select/Select.d.ts.map +1 -1
- package/dist/types/components/SocialNetworks/index.d.ts +2 -2
- package/dist/types/components/SocialNetworks/index.d.ts.map +1 -1
- package/dist/types/components/SwitchSelector/index.d.ts +5 -5
- package/dist/types/components/Tab/Tab.d.ts +6 -6
- package/dist/types/components/Tab/Tab.d.ts.map +1 -1
- package/dist/types/components/Tab/TabContext.d.ts +2 -2
- package/dist/types/components/Tab/TabContext.d.ts.map +1 -1
- package/dist/types/components/TabSelector/TabSelector.d.ts +5 -5
- package/dist/types/components/TabSelector/TabSelector.d.ts.map +1 -1
- package/dist/types/components/Table/table.content.d.ts +3 -3
- package/dist/types/components/Table/table.content.d.ts.map +1 -1
- package/dist/types/components/Tag/index.d.ts +4 -4
- package/dist/types/components/Tag/index.d.ts.map +1 -1
- package/dist/types/components/Toaster/Toast.d.ts +3 -3
- package/dist/types/components/Toaster/Toast.d.ts.map +1 -1
- package/dist/types/components/Toaster/Toaster.d.ts +2 -2
- package/dist/types/components/Toaster/Toaster.d.ts.map +1 -1
- package/dist/types/components/index.d.ts +2 -2
- package/dist/types/hooks/useDevice.d.ts.map +1 -1
- 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__ */
|
|
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:
|
|
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
|
|
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
|
|
139
|
+
size: getIconSize(size),
|
|
130
140
|
"aria-label": "Default user icon"
|
|
131
141
|
})
|
|
132
142
|
]
|
|
133
143
|
})
|
|
134
|
-
})
|
|
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
|
|
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}`]: "
|
|
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}`]: "
|
|
100
|
-
[`${ButtonColor.TEXT}`]: "
|
|
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}`]: "
|
|
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/
|
|
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/
|
|
131
|
-
[`${ButtonVariant.FADE}`]: "rounded-lg border-none bg-current/
|
|
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-
|
|
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
|
|
80
|
-
[`${LinkVariant.BUTTON_OUTLINED}`]: "flex
|
|
81
|
-
[`${LinkVariant.HOVERABLE}`]: "block rounded-lg border-none bg-current/0
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
263
|
+
size,
|
|
264
|
+
className: isButton ? void 0 : className
|
|
233
265
|
})),
|
|
234
266
|
...otherProps,
|
|
235
267
|
children: [
|
|
236
|
-
|
|
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
|