@shipfox/react-ui 0.2.0 → 0.4.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/.storybook/preview.tsx +1 -1
- package/.turbo/turbo-build.log +2 -2
- package/.turbo/turbo-check.log +3 -3
- package/.turbo/turbo-type.log +1 -1
- package/CHANGELOG.md +17 -0
- package/dist/components/alert/alert.d.ts +18 -0
- package/dist/components/alert/alert.d.ts.map +1 -0
- package/dist/components/alert/alert.js +123 -0
- package/dist/components/alert/alert.js.map +1 -0
- package/dist/components/alert/alert.stories.js +112 -0
- package/dist/components/alert/alert.stories.js.map +1 -0
- package/dist/components/alert/index.d.ts +2 -0
- package/dist/components/alert/index.d.ts.map +1 -0
- package/dist/components/alert/index.js +3 -0
- package/dist/components/alert/index.js.map +1 -0
- package/dist/components/avatar/avatar-group.d.ts +18 -0
- package/dist/components/avatar/avatar-group.d.ts.map +1 -0
- package/dist/components/avatar/avatar-group.js +132 -0
- package/dist/components/avatar/avatar-group.js.map +1 -0
- package/dist/components/avatar/avatar.d.ts +24 -0
- package/dist/components/avatar/avatar.d.ts.map +1 -0
- package/dist/components/avatar/avatar.js +165 -0
- package/dist/components/avatar/avatar.js.map +1 -0
- package/dist/components/avatar/avatar.stories.js +267 -0
- package/dist/components/avatar/avatar.stories.js.map +1 -0
- package/dist/components/avatar/index.d.ts +3 -0
- package/dist/components/avatar/index.d.ts.map +1 -0
- package/dist/components/avatar/index.js +4 -0
- package/dist/components/avatar/index.js.map +1 -0
- package/dist/components/badge/badge.d.ts +48 -0
- package/dist/components/badge/badge.d.ts.map +1 -0
- package/dist/components/badge/badge.js +72 -0
- package/dist/components/badge/badge.js.map +1 -0
- package/dist/components/badge/badge.stories.js +802 -0
- package/dist/components/badge/badge.stories.js.map +1 -0
- package/dist/components/badge/icon-badge.d.ts +9 -0
- package/dist/components/badge/icon-badge.d.ts.map +1 -0
- package/dist/components/badge/icon-badge.js +32 -0
- package/dist/components/badge/icon-badge.js.map +1 -0
- package/dist/components/badge/index.d.ts +5 -0
- package/dist/components/badge/index.d.ts.map +1 -0
- package/dist/components/badge/index.js +6 -0
- package/dist/components/badge/index.js.map +1 -0
- package/dist/components/badge/status-badge.d.ts +9 -0
- package/dist/components/badge/status-badge.d.ts.map +1 -0
- package/dist/components/badge/status-badge.js +29 -0
- package/dist/components/badge/status-badge.js.map +1 -0
- package/dist/components/badge/user-badge.d.ts +8 -0
- package/dist/components/badge/user-badge.d.ts.map +1 -0
- package/dist/components/badge/user-badge.js +24 -0
- package/dist/components/badge/user-badge.js.map +1 -0
- package/dist/components/{button.d.ts → button/button.d.ts} +1 -1
- package/dist/components/button/button.d.ts.map +1 -0
- package/dist/components/{button.js → button/button.js} +2 -2
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/{button.stories.js → button/button.stories.js} +1 -1
- package/dist/components/button/button.stories.js.map +1 -0
- package/dist/components/button/index.d.ts +2 -0
- package/dist/components/button/index.d.ts.map +1 -0
- package/dist/components/button/index.js +3 -0
- package/dist/components/button/index.js.map +1 -0
- package/dist/components/checkbox/checkbox-label.d.ts +14 -0
- package/dist/components/checkbox/checkbox-label.d.ts.map +1 -0
- package/dist/components/checkbox/checkbox-label.js +82 -0
- package/dist/components/checkbox/checkbox-label.js.map +1 -0
- package/dist/components/checkbox/checkbox-links.d.ts +18 -0
- package/dist/components/checkbox/checkbox-links.d.ts.map +1 -0
- package/dist/components/checkbox/checkbox-links.js +58 -0
- package/dist/components/checkbox/checkbox-links.js.map +1 -0
- package/dist/components/checkbox/checkbox.d.ts +9 -0
- package/dist/components/checkbox/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox/checkbox.js +49 -0
- package/dist/components/checkbox/checkbox.js.map +1 -0
- package/dist/components/checkbox/checkbox.stories.js +566 -0
- package/dist/components/checkbox/checkbox.stories.js.map +1 -0
- package/dist/components/checkbox/index.d.ts +4 -0
- package/dist/components/checkbox/index.d.ts.map +1 -0
- package/dist/components/checkbox/index.js +5 -0
- package/dist/components/checkbox/index.js.map +1 -0
- package/dist/components/code-block/code-block-footer.d.ts +26 -0
- package/dist/components/code-block/code-block-footer.d.ts.map +1 -0
- package/dist/components/code-block/code-block-footer.js +86 -0
- package/dist/components/code-block/code-block-footer.js.map +1 -0
- package/dist/components/code-block/code-block.d.ts +50 -0
- package/dist/components/code-block/code-block.d.ts.map +1 -0
- package/dist/components/code-block/code-block.js +142 -0
- package/dist/components/code-block/code-block.js.map +1 -0
- package/dist/components/code-block/code-block.stories.js +341 -0
- package/dist/components/code-block/code-block.stories.js.map +1 -0
- package/dist/components/code-block/code-content.d.ts +11 -0
- package/dist/components/code-block/code-content.d.ts.map +1 -0
- package/dist/components/code-block/code-content.js +29 -0
- package/dist/components/code-block/code-content.js.map +1 -0
- package/dist/components/code-block/code-copy-button.d.ts +11 -0
- package/dist/components/code-block/code-copy-button.d.ts.map +1 -0
- package/dist/components/code-block/code-copy-button.js +49 -0
- package/dist/components/code-block/code-copy-button.js.map +1 -0
- package/dist/components/code-block/code-tabs.d.ts +16 -0
- package/dist/components/code-block/code-tabs.d.ts.map +1 -0
- package/dist/components/code-block/code-tabs.js +98 -0
- package/dist/components/code-block/code-tabs.js.map +1 -0
- package/dist/components/code-block/index.d.ts +4 -0
- package/dist/components/code-block/index.d.ts.map +1 -0
- package/dist/components/code-block/index.js +5 -0
- package/dist/components/code-block/index.js.map +1 -0
- package/dist/components/dynamic-item/dynamic-item.d.ts +13 -0
- package/dist/components/dynamic-item/dynamic-item.d.ts.map +1 -0
- package/dist/components/dynamic-item/dynamic-item.js +43 -0
- package/dist/components/dynamic-item/dynamic-item.js.map +1 -0
- package/dist/components/dynamic-item/dynamic-item.stories.js +375 -0
- package/dist/components/dynamic-item/dynamic-item.stories.js.map +1 -0
- package/dist/components/dynamic-item/index.d.ts +2 -0
- package/dist/components/dynamic-item/index.d.ts.map +1 -0
- package/dist/components/dynamic-item/index.js +3 -0
- package/dist/components/dynamic-item/index.js.map +1 -0
- package/dist/components/icon/custom/index.d.ts +3 -0
- package/dist/components/icon/custom/index.d.ts.map +1 -1
- package/dist/components/icon/custom/index.js +3 -0
- package/dist/components/icon/custom/index.js.map +1 -1
- package/dist/components/icon/custom/shipfox-logo.d.ts +8 -0
- package/dist/components/icon/custom/shipfox-logo.d.ts.map +1 -0
- package/dist/components/icon/custom/shipfox-logo.js +22 -0
- package/dist/components/icon/custom/shipfox-logo.js.map +1 -0
- package/dist/components/icon/custom/slack-logo.d.ts +6 -0
- package/dist/components/icon/custom/slack-logo.d.ts.map +1 -0
- package/dist/components/icon/custom/slack-logo.js +34 -0
- package/dist/components/icon/custom/slack-logo.js.map +1 -0
- package/dist/components/icon/custom/stripe-logo.d.ts +8 -0
- package/dist/components/icon/custom/stripe-logo.d.ts.map +1 -0
- package/dist/components/icon/custom/stripe-logo.js +24 -0
- package/dist/components/icon/custom/stripe-logo.js.map +1 -0
- package/dist/components/icon/icon.d.ts +13 -1
- package/dist/components/icon/icon.d.ts.map +1 -1
- package/dist/components/icon/icon.js +15 -3
- package/dist/components/icon/icon.js.map +1 -1
- package/dist/components/icon/icon.stories.js +6 -3
- package/dist/components/icon/icon.stories.js.map +1 -1
- package/dist/components/index.d.ts +12 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +13 -2
- package/dist/components/index.js.map +1 -1
- package/dist/components/inline-tips/index.d.ts +2 -0
- package/dist/components/inline-tips/index.d.ts.map +1 -0
- package/dist/components/inline-tips/index.js +3 -0
- package/dist/components/inline-tips/index.js.map +1 -0
- package/dist/components/inline-tips/inline-tips.d.ts +19 -0
- package/dist/components/inline-tips/inline-tips.d.ts.map +1 -0
- package/dist/components/inline-tips/inline-tips.js +98 -0
- package/dist/components/inline-tips/inline-tips.js.map +1 -0
- package/dist/components/inline-tips/inline-tips.stories.js +214 -0
- package/dist/components/inline-tips/inline-tips.stories.js.map +1 -0
- package/dist/components/input/index.d.ts +2 -0
- package/dist/components/input/index.d.ts.map +1 -0
- package/dist/components/input/index.js +3 -0
- package/dist/components/input/index.js.map +1 -0
- package/dist/components/input/input.d.ts.map +1 -0
- package/dist/components/{input.js → input/input.js} +2 -2
- package/dist/components/input/input.js.map +1 -0
- package/dist/components/{input.stories.js → input/input.stories.js} +1 -1
- package/dist/components/input/input.stories.js.map +1 -0
- package/dist/components/item/index.d.ts +2 -0
- package/dist/components/item/index.d.ts.map +1 -0
- package/dist/components/item/index.js +3 -0
- package/dist/components/item/index.js.map +1 -0
- package/dist/components/item/item.d.ts +32 -0
- package/dist/components/item/item.d.ts.map +1 -0
- package/dist/components/item/item.js +120 -0
- package/dist/components/item/item.js.map +1 -0
- package/dist/components/item/item.stories.js +232 -0
- package/dist/components/item/item.stories.js.map +1 -0
- package/dist/components/label/index.d.ts +2 -0
- package/dist/components/label/index.d.ts.map +1 -0
- package/dist/components/label/index.js +3 -0
- package/dist/components/label/index.js.map +1 -0
- package/dist/components/label/label.d.ts +7 -0
- package/dist/components/label/label.d.ts.map +1 -0
- package/dist/components/label/label.js +13 -0
- package/dist/components/label/label.js.map +1 -0
- package/dist/components/label/label.stories.js +105 -0
- package/dist/components/label/label.stories.js.map +1 -0
- package/dist/components/moving-border/moving-border.d.ts +9 -0
- package/dist/components/moving-border/moving-border.d.ts.map +1 -0
- package/dist/components/moving-border/moving-border.js +54 -0
- package/dist/components/moving-border/moving-border.js.map +1 -0
- package/dist/components/textarea/textarea.js +1 -1
- package/dist/components/textarea/textarea.js.map +1 -1
- package/dist/components/theme/index.d.ts +2 -0
- package/dist/components/theme/index.d.ts.map +1 -0
- package/dist/components/theme/index.js +3 -0
- package/dist/components/theme/index.js.map +1 -0
- package/dist/components/{theme-provider.d.ts → theme/theme-provider.d.ts} +1 -1
- package/dist/components/theme/theme-provider.d.ts.map +1 -0
- package/dist/components/{theme-provider.js → theme/theme-provider.js} +1 -1
- package/dist/components/theme/theme-provider.js.map +1 -0
- package/dist/components/toast/index.d.ts +3 -0
- package/dist/components/toast/index.d.ts.map +1 -0
- package/dist/components/toast/index.js +4 -0
- package/dist/components/toast/index.js.map +1 -0
- package/dist/components/toast/toast-custom.d.ts +19 -0
- package/dist/components/toast/toast-custom.d.ts.map +1 -0
- package/dist/components/toast/toast-custom.js +134 -0
- package/dist/components/toast/toast-custom.js.map +1 -0
- package/dist/components/toast/toast.d.ts +5 -0
- package/dist/components/toast/toast.d.ts.map +1 -0
- package/dist/components/toast/toast.js +40 -0
- package/dist/components/toast/toast.js.map +1 -0
- package/dist/components/toast/toast.stories.js +326 -0
- package/dist/components/toast/toast.stories.js.map +1 -0
- package/dist/components/tooltip/index.d.ts +2 -0
- package/dist/components/tooltip/index.d.ts.map +1 -0
- package/dist/components/tooltip/index.js +3 -0
- package/dist/components/tooltip/index.js.map +1 -0
- package/dist/components/tooltip/tooltip.d.ts +20 -0
- package/dist/components/tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip/tooltip.js +98 -0
- package/dist/components/tooltip/tooltip.js.map +1 -0
- package/dist/components/tooltip/tooltip.stories.js +560 -0
- package/dist/components/tooltip/tooltip.stories.js.map +1 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useResolvedTheme.d.ts +2 -0
- package/dist/hooks/useResolvedTheme.d.ts.map +1 -0
- package/dist/hooks/useResolvedTheme.js +24 -0
- package/dist/hooks/useResolvedTheme.js.map +1 -0
- package/dist/hooks/useShikiHighlight.d.ts +28 -0
- package/dist/hooks/useShikiHighlight.d.ts.map +1 -0
- package/dist/hooks/useShikiHighlight.js +106 -0
- package/dist/hooks/useShikiHighlight.js.map +1 -0
- package/dist/hooks/useShikiStyleInjection.d.ts +2 -0
- package/dist/hooks/useShikiStyleInjection.d.ts.map +1 -0
- package/dist/hooks/useShikiStyleInjection.js +34 -0
- package/dist/hooks/useShikiStyleInjection.js.map +1 -0
- package/dist/utils/avatar.d.ts +3 -0
- package/dist/utils/avatar.d.ts.map +1 -0
- package/dist/utils/avatar.js +32 -0
- package/dist/utils/avatar.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/index.css +130 -7
- package/package.json +7 -4
- package/src/assets/illustration-1.svg +92 -0
- package/src/assets/illustration-2.svg +14 -0
- package/src/assets/illustration-gradient.svg +7049 -0
- package/src/components/alert/alert.stories.tsx +77 -0
- package/src/components/alert/alert.tsx +144 -0
- package/src/components/alert/index.ts +1 -0
- package/src/components/avatar/avatar-group.tsx +186 -0
- package/src/components/avatar/avatar.stories.tsx +179 -0
- package/src/components/avatar/avatar.tsx +219 -0
- package/src/components/avatar/index.ts +2 -0
- package/src/components/badge/badge.stories.tsx +468 -0
- package/src/components/badge/badge.tsx +147 -0
- package/src/components/badge/icon-badge.tsx +43 -0
- package/src/components/badge/index.ts +4 -0
- package/src/components/badge/status-badge.tsx +43 -0
- package/src/components/badge/user-badge.tsx +34 -0
- package/src/components/{button.tsx → button/button.tsx} +1 -1
- package/src/components/button/index.ts +1 -0
- package/src/components/checkbox/checkbox-label.tsx +125 -0
- package/src/components/checkbox/checkbox-links.tsx +90 -0
- package/src/components/checkbox/checkbox.stories.tsx +375 -0
- package/src/components/checkbox/checkbox.tsx +71 -0
- package/src/components/checkbox/index.ts +3 -0
- package/src/components/code-block/code-block-footer.tsx +173 -0
- package/src/components/code-block/code-block.stories.tsx +323 -0
- package/src/components/code-block/code-block.tsx +283 -0
- package/src/components/code-block/code-content.tsx +60 -0
- package/src/components/code-block/code-copy-button.tsx +73 -0
- package/src/components/code-block/code-tabs.tsx +170 -0
- package/src/components/code-block/index.ts +3 -0
- package/src/components/dynamic-item/dynamic-item.stories.tsx +261 -0
- package/src/components/dynamic-item/dynamic-item.tsx +68 -0
- package/src/components/dynamic-item/index.ts +1 -0
- package/src/components/icon/custom/index.ts +3 -0
- package/src/components/icon/custom/shipfox-logo.tsx +20 -0
- package/src/components/icon/custom/slack-logo.tsx +35 -0
- package/src/components/icon/custom/stripe-logo.tsx +27 -0
- package/src/components/icon/icon.stories.tsx +3 -1
- package/src/components/icon/icon.tsx +29 -1
- package/src/components/index.ts +12 -1
- package/src/components/inline-tips/index.ts +1 -0
- package/src/components/inline-tips/inline-tips.stories.tsx +126 -0
- package/src/components/inline-tips/inline-tips.tsx +132 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/{input.tsx → input/input.tsx} +1 -1
- package/src/components/item/index.ts +1 -0
- package/src/components/item/item.stories.tsx +150 -0
- package/src/components/item/item.tsx +182 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.stories.tsx +67 -0
- package/src/components/label/label.tsx +15 -0
- package/src/components/moving-border/moving-border.tsx +67 -0
- package/src/components/textarea/textarea.tsx +1 -1
- package/src/components/theme/index.ts +1 -0
- package/src/components/toast/index.ts +2 -0
- package/src/components/toast/toast-custom.tsx +154 -0
- package/src/components/toast/toast.stories.tsx +369 -0
- package/src/components/toast/toast.tsx +41 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.stories.tsx +284 -0
- package/src/components/tooltip/tooltip.tsx +121 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useResolvedTheme.ts +34 -0
- package/src/hooks/useShikiHighlight.ts +140 -0
- package/src/hooks/useShikiStyleInjection.ts +34 -0
- package/src/utils/avatar.ts +27 -0
- package/src/utils/index.ts +1 -0
- package/dist/components/button.d.ts.map +0 -1
- package/dist/components/button.js.map +0 -1
- package/dist/components/button.stories.js.map +0 -1
- package/dist/components/input.d.ts.map +0 -1
- package/dist/components/input.js.map +0 -1
- package/dist/components/input.stories.js.map +0 -1
- package/dist/components/theme-provider.d.ts.map +0 -1
- package/dist/components/theme-provider.js.map +0 -1
- /package/dist/components/{input.d.ts → input/input.d.ts} +0 -0
- /package/src/components/{button.stories.tsx → button/button.stories.tsx} +0 -0
- /package/src/components/{input.stories.tsx → input/input.stories.tsx} +0 -0
- /package/src/components/{theme-provider.tsx → theme/theme-provider.tsx} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {useTheme} from 'hooks/useTheme';
|
|
2
|
+
import {AlertTriangle, CheckCircle2, Info, Loader2, XCircle} from 'lucide-react';
|
|
3
|
+
import {
|
|
4
|
+
Toaster as SonnerToaster,
|
|
5
|
+
type ToasterProps as SonnerToasterProps,
|
|
6
|
+
toast as sonnerToast,
|
|
7
|
+
} from 'sonner';
|
|
8
|
+
|
|
9
|
+
type ToasterProps = Omit<SonnerToasterProps, 'theme'>;
|
|
10
|
+
|
|
11
|
+
function Toaster({...props}: ToasterProps) {
|
|
12
|
+
const {theme} = useTheme();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<SonnerToaster
|
|
16
|
+
theme={theme as SonnerToasterProps['theme']}
|
|
17
|
+
className="toaster group"
|
|
18
|
+
toastOptions={{
|
|
19
|
+
classNames: {
|
|
20
|
+
toast:
|
|
21
|
+
'group toast group-[.toaster]:bg-background-components-base group-[.toaster]:text-foreground-neutral-base group-[.toaster]:border group-[.toaster]:border-border-neutral-base group-[.toaster]:shadow-tooltip rounded-8 p-8',
|
|
22
|
+
description: 'group-[.toast]:text-foreground-neutral-muted text-xs leading-20 mt-4',
|
|
23
|
+
actionButton:
|
|
24
|
+
'group-[.toast]:bg-background-button-neutral-default group-[.toast]:text-foreground-neutral-base group-[.toast]:hover:bg-background-button-neutral-hover rounded-6 px-8 py-4 text-xs font-medium',
|
|
25
|
+
cancelButton:
|
|
26
|
+
'group-[.toast]:bg-background-button-transparent-default group-[.toast]:text-foreground-neutral-base group-[.toast]:hover:bg-background-button-transparent-hover rounded-6 px-8 py-4 text-xs font-medium',
|
|
27
|
+
},
|
|
28
|
+
}}
|
|
29
|
+
icons={{
|
|
30
|
+
success: <CheckCircle2 className="size-20 text-tag-success-icon" />,
|
|
31
|
+
info: <Info className="size-20 text-tag-blue-icon" />,
|
|
32
|
+
warning: <AlertTriangle className="size-20 text-tag-warning-icon" />,
|
|
33
|
+
error: <XCircle className="size-20 text-tag-error-icon" />,
|
|
34
|
+
loading: <Loader2 className="size-20 text-tag-neutral-icon animate-spin" />,
|
|
35
|
+
}}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export {Toaster, sonnerToast as toast, type ToasterProps};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tooltip';
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/react';
|
|
2
|
+
import {Code} from 'components/typography';
|
|
3
|
+
import {Button} from '../button';
|
|
4
|
+
import {Tooltip, TooltipContent, type TooltipContentProps, TooltipTrigger} from './tooltip';
|
|
5
|
+
|
|
6
|
+
type TooltipStoryArgs = {
|
|
7
|
+
defaultOpen?: boolean;
|
|
8
|
+
delayDuration?: number;
|
|
9
|
+
variant?: TooltipContentProps['variant'];
|
|
10
|
+
size?: TooltipContentProps['size'];
|
|
11
|
+
side?: TooltipContentProps['side'];
|
|
12
|
+
align?: TooltipContentProps['align'];
|
|
13
|
+
sideOffset?: TooltipContentProps['sideOffset'];
|
|
14
|
+
animated?: TooltipContentProps['animated'];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
title: 'Components/Tooltip',
|
|
19
|
+
component: Tooltip,
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
argTypes: {
|
|
22
|
+
defaultOpen: {control: 'boolean'},
|
|
23
|
+
delayDuration: {control: 'number'},
|
|
24
|
+
variant: {
|
|
25
|
+
control: 'select',
|
|
26
|
+
options: ['default', 'inverted', 'muted'],
|
|
27
|
+
},
|
|
28
|
+
size: {
|
|
29
|
+
control: 'select',
|
|
30
|
+
options: ['sm', 'md', 'lg'],
|
|
31
|
+
},
|
|
32
|
+
side: {
|
|
33
|
+
control: 'select',
|
|
34
|
+
options: ['top', 'bottom', 'left', 'right'],
|
|
35
|
+
},
|
|
36
|
+
align: {
|
|
37
|
+
control: 'select',
|
|
38
|
+
options: ['start', 'center', 'end'],
|
|
39
|
+
},
|
|
40
|
+
sideOffset: {control: 'number'},
|
|
41
|
+
animated: {control: 'boolean'},
|
|
42
|
+
},
|
|
43
|
+
args: {
|
|
44
|
+
defaultOpen: false,
|
|
45
|
+
delayDuration: 0,
|
|
46
|
+
variant: 'default',
|
|
47
|
+
size: 'md',
|
|
48
|
+
side: 'top',
|
|
49
|
+
align: 'center',
|
|
50
|
+
sideOffset: 8,
|
|
51
|
+
animated: true,
|
|
52
|
+
},
|
|
53
|
+
} satisfies Meta<TooltipStoryArgs>;
|
|
54
|
+
|
|
55
|
+
export default meta;
|
|
56
|
+
type Story = StoryObj<typeof meta>;
|
|
57
|
+
|
|
58
|
+
export const Default: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
defaultOpen: false,
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
render: (args: TooltipStoryArgs) => {
|
|
64
|
+
const {defaultOpen, delayDuration, variant, size, side, align, sideOffset, animated} = args;
|
|
65
|
+
return (
|
|
66
|
+
<div className="flex items-center justify-center p-64">
|
|
67
|
+
<Tooltip defaultOpen={defaultOpen} delayDuration={delayDuration}>
|
|
68
|
+
<TooltipTrigger asChild>
|
|
69
|
+
<Button>Hover me</Button>
|
|
70
|
+
</TooltipTrigger>
|
|
71
|
+
<TooltipContent
|
|
72
|
+
variant={variant}
|
|
73
|
+
size={size}
|
|
74
|
+
side={side}
|
|
75
|
+
align={align}
|
|
76
|
+
sideOffset={sideOffset}
|
|
77
|
+
animated={animated}
|
|
78
|
+
>
|
|
79
|
+
Tooltip Text
|
|
80
|
+
</TooltipContent>
|
|
81
|
+
</Tooltip>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const Types: Story = {
|
|
88
|
+
render: () => (
|
|
89
|
+
<div className="flex flex-col gap-64 p-64">
|
|
90
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
91
|
+
TYPES
|
|
92
|
+
</Code>
|
|
93
|
+
<div className="flex flex-col gap-32">
|
|
94
|
+
{/* Text Type */}
|
|
95
|
+
<div className="flex flex-col gap-16">
|
|
96
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
97
|
+
Type=Text
|
|
98
|
+
</Code>
|
|
99
|
+
<div className="flex gap-16">
|
|
100
|
+
<Tooltip>
|
|
101
|
+
<TooltipTrigger asChild>
|
|
102
|
+
<Button variant="primary">Tooltip Text</Button>
|
|
103
|
+
</TooltipTrigger>
|
|
104
|
+
<TooltipContent variant="inverted">Tooltip Text</TooltipContent>
|
|
105
|
+
</Tooltip>
|
|
106
|
+
<Tooltip>
|
|
107
|
+
<TooltipTrigger asChild>
|
|
108
|
+
<Button variant="secondary">Tooltip Text</Button>
|
|
109
|
+
</TooltipTrigger>
|
|
110
|
+
<TooltipContent>Tooltip Text</TooltipContent>
|
|
111
|
+
</Tooltip>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* Shortcut Type */}
|
|
116
|
+
<div className="flex flex-col gap-16">
|
|
117
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
118
|
+
Type=Shortcut
|
|
119
|
+
</Code>
|
|
120
|
+
<div className="flex gap-16">
|
|
121
|
+
<Tooltip>
|
|
122
|
+
<TooltipTrigger asChild>
|
|
123
|
+
<Button variant="primary">Tooltip Text</Button>
|
|
124
|
+
</TooltipTrigger>
|
|
125
|
+
<TooltipContent variant="inverted">
|
|
126
|
+
<div className="flex items-center gap-6">
|
|
127
|
+
<span>Tooltip Text</span>
|
|
128
|
+
<div className="flex items-center gap-4">
|
|
129
|
+
<kbd className="flex h-16 w-16 items-center justify-center rounded-4 border border-border-neutral-base bg-background-field-base text-sm text-foreground-neutral-subtle">
|
|
130
|
+
⌘
|
|
131
|
+
</kbd>
|
|
132
|
+
<kbd className="flex h-16 w-16 items-center justify-center rounded-4 border border-border-neutral-base bg-background-field-base text-sm text-foreground-neutral-subtle">
|
|
133
|
+
/
|
|
134
|
+
</kbd>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</TooltipContent>
|
|
138
|
+
</Tooltip>
|
|
139
|
+
<Tooltip>
|
|
140
|
+
<TooltipTrigger asChild>
|
|
141
|
+
<Button variant="secondary">Tooltip Text</Button>
|
|
142
|
+
</TooltipTrigger>
|
|
143
|
+
<TooltipContent>
|
|
144
|
+
<div className="flex items-center gap-6">
|
|
145
|
+
<span>Tooltip Text</span>
|
|
146
|
+
<div className="flex items-center gap-4">
|
|
147
|
+
<kbd className="flex h-16 w-16 items-center justify-center rounded-4 border border-border-neutral-base bg-background-field-base text-xs text-foreground-neutral-subtle">
|
|
148
|
+
⌘
|
|
149
|
+
</kbd>
|
|
150
|
+
<kbd className="flex h-16 w-16 items-center justify-center rounded-4 border border-border-neutral-base bg-background-field-base text-xs text-foreground-neutral-subtle">
|
|
151
|
+
/
|
|
152
|
+
</kbd>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</TooltipContent>
|
|
156
|
+
</Tooltip>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* Graph Type */}
|
|
161
|
+
<div className="flex flex-col gap-16">
|
|
162
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
163
|
+
Type=Graph
|
|
164
|
+
</Code>
|
|
165
|
+
<div className="flex gap-16">
|
|
166
|
+
<Tooltip>
|
|
167
|
+
<TooltipTrigger asChild>
|
|
168
|
+
<Button variant="primary">Hover for graph data</Button>
|
|
169
|
+
</TooltipTrigger>
|
|
170
|
+
<TooltipContent variant="inverted" className="w-160">
|
|
171
|
+
<div className="flex flex-col gap-4">
|
|
172
|
+
<div className="text-xs text-foreground-neutral-subtle">Jul 22, 2025</div>
|
|
173
|
+
<div className="flex flex-col gap-4">
|
|
174
|
+
<div className="flex items-center justify-between gap-12">
|
|
175
|
+
<div className="flex flex-1 items-center gap-4">
|
|
176
|
+
<div className="size-6 rounded-full bg-purple-500" />
|
|
177
|
+
<span className="text-xs">Data A</span>
|
|
178
|
+
</div>
|
|
179
|
+
<span className="text-xs">$6.14</span>
|
|
180
|
+
</div>
|
|
181
|
+
<div className="flex items-center justify-between gap-12">
|
|
182
|
+
<div className="flex flex-1 items-center gap-4">
|
|
183
|
+
<div className="size-6 rounded-full bg-green-500" />
|
|
184
|
+
<span className="text-xs">Data B</span>
|
|
185
|
+
</div>
|
|
186
|
+
<span className="text-xs">$4.37</span>
|
|
187
|
+
</div>
|
|
188
|
+
<div className="flex items-center justify-between gap-12">
|
|
189
|
+
<div className="flex flex-1 items-center gap-4">
|
|
190
|
+
<div className="size-6 rounded-full bg-orange-500" />
|
|
191
|
+
<span className="text-xs">Data C</span>
|
|
192
|
+
</div>
|
|
193
|
+
<span className="text-xs">$12.88</span>
|
|
194
|
+
</div>
|
|
195
|
+
<div className="flex items-center justify-between gap-12">
|
|
196
|
+
<div className="flex flex-1 items-center gap-4">
|
|
197
|
+
<div className="size-6 rounded-full bg-blue-500" />
|
|
198
|
+
<span className="text-xs">Data D</span>
|
|
199
|
+
</div>
|
|
200
|
+
<span className="text-xs">$2.91</span>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</TooltipContent>
|
|
205
|
+
</Tooltip>
|
|
206
|
+
<Tooltip>
|
|
207
|
+
<TooltipTrigger asChild>
|
|
208
|
+
<Button variant="secondary">Hover for graph data</Button>
|
|
209
|
+
</TooltipTrigger>
|
|
210
|
+
<TooltipContent className="w-160">
|
|
211
|
+
<div className="flex flex-col gap-4">
|
|
212
|
+
<div className="text-xs text-foreground-neutral-subtle">Jul 22, 2025</div>
|
|
213
|
+
<div className="flex flex-col gap-4">
|
|
214
|
+
<div className="flex items-center justify-between gap-12">
|
|
215
|
+
<div className="flex flex-1 items-center gap-4">
|
|
216
|
+
<div className="size-6 rounded-full bg-purple-500" />
|
|
217
|
+
<span className="text-xs">Data A</span>
|
|
218
|
+
</div>
|
|
219
|
+
<span className="text-xs">$6.14</span>
|
|
220
|
+
</div>
|
|
221
|
+
<div className="flex items-center justify-between gap-12">
|
|
222
|
+
<div className="flex flex-1 items-center gap-4">
|
|
223
|
+
<div className="size-6 rounded-full bg-green-500" />
|
|
224
|
+
<span className="text-xs">Data B</span>
|
|
225
|
+
</div>
|
|
226
|
+
<span className="text-xs">$4.37</span>
|
|
227
|
+
</div>
|
|
228
|
+
<div className="flex items-center justify-between gap-12">
|
|
229
|
+
<div className="flex flex-1 items-center gap-4">
|
|
230
|
+
<div className="size-6 rounded-full bg-orange-500" />
|
|
231
|
+
<span className="text-xs">Data C</span>
|
|
232
|
+
</div>
|
|
233
|
+
<span className="text-xs">$12.88</span>
|
|
234
|
+
</div>
|
|
235
|
+
<div className="flex items-center justify-between gap-12">
|
|
236
|
+
<div className="flex flex-1 items-center gap-4">
|
|
237
|
+
<div className="size-6 rounded-full bg-blue-500" />
|
|
238
|
+
<span className="text-xs">Data D</span>
|
|
239
|
+
</div>
|
|
240
|
+
<span className="text-xs">$2.91</span>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</TooltipContent>
|
|
245
|
+
</Tooltip>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
{/* Breadcrumbs Type */}
|
|
250
|
+
<div className="flex flex-col gap-16">
|
|
251
|
+
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
252
|
+
Type=Breadcrumbs
|
|
253
|
+
</Code>
|
|
254
|
+
<div className="flex gap-16">
|
|
255
|
+
<Tooltip>
|
|
256
|
+
<TooltipTrigger asChild>
|
|
257
|
+
<Button variant="primary">Breadcrumb</Button>
|
|
258
|
+
</TooltipTrigger>
|
|
259
|
+
<TooltipContent variant="inverted">
|
|
260
|
+
<div className="flex items-center gap-6">
|
|
261
|
+
<span>Breadcrumb</span>
|
|
262
|
+
<span className="text-foreground-neutral-muted">/</span>
|
|
263
|
+
<span>Breadcrumb</span>
|
|
264
|
+
</div>
|
|
265
|
+
</TooltipContent>
|
|
266
|
+
</Tooltip>
|
|
267
|
+
<Tooltip>
|
|
268
|
+
<TooltipTrigger asChild>
|
|
269
|
+
<Button variant="secondary">Breadcrumb</Button>
|
|
270
|
+
</TooltipTrigger>
|
|
271
|
+
<TooltipContent>
|
|
272
|
+
<div className="flex items-center gap-6">
|
|
273
|
+
<span>Breadcrumb</span>
|
|
274
|
+
<span className="text-foreground-neutral-muted">/</span>
|
|
275
|
+
<span>Breadcrumb</span>
|
|
276
|
+
</div>
|
|
277
|
+
</TooltipContent>
|
|
278
|
+
</Tooltip>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
),
|
|
284
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
2
|
+
import {cva, type VariantProps} from 'class-variance-authority';
|
|
3
|
+
import {motion, type Transition} from 'framer-motion';
|
|
4
|
+
import type {ComponentProps} from 'react';
|
|
5
|
+
import {cn} from 'utils/cn';
|
|
6
|
+
|
|
7
|
+
const tooltipContentVariants = cva(
|
|
8
|
+
'rounded-8 px-8 py-4 text-xs font-medium leading-20 z-50 w-fit text-balance shadow-tooltip',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-background-components-base text-foreground-neutral-base',
|
|
13
|
+
inverted: 'bg-background-button-inverted-default text-foreground-contrast-primary',
|
|
14
|
+
muted: 'bg-background-neutral-subtle text-foreground-neutral-muted',
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
sm: 'px-6 py-2 text-xs',
|
|
18
|
+
md: 'px-8 py-4 text-xs',
|
|
19
|
+
lg: 'px-10 py-6 text-sm',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: 'default',
|
|
24
|
+
size: 'md',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
function TooltipProvider({
|
|
30
|
+
delayDuration = 0,
|
|
31
|
+
...props
|
|
32
|
+
}: ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
33
|
+
return (
|
|
34
|
+
<TooltipPrimitive.Provider
|
|
35
|
+
data-slot="tooltip-provider"
|
|
36
|
+
delayDuration={delayDuration}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function Tooltip({...props}: ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
43
|
+
return (
|
|
44
|
+
<TooltipProvider>
|
|
45
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
46
|
+
</TooltipProvider>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function TooltipTrigger({...props}: ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
51
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const defaultTransition: Transition = {
|
|
55
|
+
type: 'spring',
|
|
56
|
+
stiffness: 300,
|
|
57
|
+
damping: 17,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type TooltipContentProps = ComponentProps<typeof TooltipPrimitive.Content> &
|
|
61
|
+
VariantProps<typeof tooltipContentVariants> & {
|
|
62
|
+
animated?: boolean;
|
|
63
|
+
transition?: Transition;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function TooltipContent({
|
|
67
|
+
className,
|
|
68
|
+
sideOffset = 8,
|
|
69
|
+
children,
|
|
70
|
+
variant,
|
|
71
|
+
size,
|
|
72
|
+
animated = true,
|
|
73
|
+
transition = defaultTransition,
|
|
74
|
+
...props
|
|
75
|
+
}: TooltipContentProps) {
|
|
76
|
+
if (animated) {
|
|
77
|
+
return (
|
|
78
|
+
<TooltipPrimitive.Portal>
|
|
79
|
+
<TooltipPrimitive.Content
|
|
80
|
+
data-slot="tooltip-content"
|
|
81
|
+
sideOffset={sideOffset}
|
|
82
|
+
asChild
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
<motion.div
|
|
86
|
+
className={cn(tooltipContentVariants({variant, size, className}))}
|
|
87
|
+
initial={{opacity: 0, scale: 0.95}}
|
|
88
|
+
animate={{opacity: 1, scale: 1}}
|
|
89
|
+
exit={{opacity: 0, scale: 0.95}}
|
|
90
|
+
transition={transition}
|
|
91
|
+
>
|
|
92
|
+
{children}
|
|
93
|
+
</motion.div>
|
|
94
|
+
</TooltipPrimitive.Content>
|
|
95
|
+
</TooltipPrimitive.Portal>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<TooltipPrimitive.Portal>
|
|
101
|
+
<TooltipPrimitive.Content
|
|
102
|
+
data-slot="tooltip-content"
|
|
103
|
+
sideOffset={sideOffset}
|
|
104
|
+
className={cn(tooltipContentVariants({variant, size, className}))}
|
|
105
|
+
{...props}
|
|
106
|
+
>
|
|
107
|
+
{children}
|
|
108
|
+
</TooltipPrimitive.Content>
|
|
109
|
+
</TooltipPrimitive.Portal>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
Tooltip,
|
|
115
|
+
TooltipTrigger,
|
|
116
|
+
TooltipContent,
|
|
117
|
+
TooltipProvider,
|
|
118
|
+
tooltipContentVariants,
|
|
119
|
+
defaultTransition,
|
|
120
|
+
};
|
|
121
|
+
export type {TooltipContentProps};
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {useSyncExternalStore} from 'react';
|
|
2
|
+
import {useTheme} from './useTheme';
|
|
3
|
+
|
|
4
|
+
export function useResolvedTheme(): 'light' | 'dark' {
|
|
5
|
+
const {theme} = useTheme();
|
|
6
|
+
|
|
7
|
+
const systemTheme = useSyncExternalStore<'light' | 'dark'>(
|
|
8
|
+
(callback) => {
|
|
9
|
+
if (typeof window === 'undefined' || theme !== 'system') {
|
|
10
|
+
return () => {
|
|
11
|
+
// No-op unsubscribe
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
15
|
+
mql.addEventListener('change', callback);
|
|
16
|
+
return () => {
|
|
17
|
+
mql.removeEventListener('change', callback);
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
(): 'light' | 'dark' =>
|
|
21
|
+
typeof window !== 'undefined' && theme === 'system'
|
|
22
|
+
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
23
|
+
? 'dark'
|
|
24
|
+
: 'light'
|
|
25
|
+
: 'light',
|
|
26
|
+
(): 'light' | 'dark' => 'light', // Server snapshot
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (theme === 'system') {
|
|
30
|
+
return systemTheme;
|
|
31
|
+
}
|
|
32
|
+
// TypeScript should narrow theme to 'light' | 'dark' here
|
|
33
|
+
return theme as 'light' | 'dark';
|
|
34
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react';
|
|
2
|
+
|
|
3
|
+
type ShikiThemes = {
|
|
4
|
+
light: string;
|
|
5
|
+
dark: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type UseShikiHighlightOptions = {
|
|
9
|
+
code: string;
|
|
10
|
+
lang: string;
|
|
11
|
+
themes: ShikiThemes;
|
|
12
|
+
resolvedTheme: 'light' | 'dark';
|
|
13
|
+
syntaxHighlighting: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function useShikiHighlight({
|
|
17
|
+
code,
|
|
18
|
+
lang,
|
|
19
|
+
themes,
|
|
20
|
+
resolvedTheme,
|
|
21
|
+
syntaxHighlighting,
|
|
22
|
+
}: UseShikiHighlightOptions): {highlightedCode: string; isLoading: boolean} {
|
|
23
|
+
const [highlightedCode, setHighlightedCode] = useState<string>('');
|
|
24
|
+
const [isLoading, setIsLoading] = useState(syntaxHighlighting);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!syntaxHighlighting) {
|
|
28
|
+
setIsLoading(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setIsLoading(true);
|
|
33
|
+
let cancelled = false;
|
|
34
|
+
|
|
35
|
+
const loadHighlightedCode = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const {codeToHtml} = await import('shiki');
|
|
38
|
+
|
|
39
|
+
const html = await codeToHtml(code, {
|
|
40
|
+
lang,
|
|
41
|
+
themes: {
|
|
42
|
+
light: themes.light,
|
|
43
|
+
dark: themes.dark,
|
|
44
|
+
},
|
|
45
|
+
defaultColor: resolvedTheme === 'dark' ? 'dark' : 'light',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!cancelled) {
|
|
49
|
+
setHighlightedCode(html);
|
|
50
|
+
setIsLoading(false);
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
if (!cancelled) {
|
|
54
|
+
setIsLoading(false);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
loadHighlightedCode();
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
cancelled = true;
|
|
63
|
+
};
|
|
64
|
+
}, [code, lang, themes.light, themes.dark, resolvedTheme, syntaxHighlighting]);
|
|
65
|
+
|
|
66
|
+
return {highlightedCode, isLoading};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type UseShikiHighlightMultipleOptions = {
|
|
70
|
+
codes: Record<string, string>;
|
|
71
|
+
lang: string;
|
|
72
|
+
themes: ShikiThemes;
|
|
73
|
+
resolvedTheme: 'light' | 'dark';
|
|
74
|
+
syntaxHighlighting: boolean;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export function useShikiHighlightMultiple({
|
|
78
|
+
codes,
|
|
79
|
+
lang,
|
|
80
|
+
themes,
|
|
81
|
+
resolvedTheme,
|
|
82
|
+
syntaxHighlighting,
|
|
83
|
+
}: UseShikiHighlightMultipleOptions): {
|
|
84
|
+
highlightedCodes: Record<string, string>;
|
|
85
|
+
isLoading: boolean;
|
|
86
|
+
} {
|
|
87
|
+
const [highlightedCodes, setHighlightedCodes] = useState<Record<string, string>>({});
|
|
88
|
+
const [isLoading, setIsLoading] = useState(syntaxHighlighting);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!syntaxHighlighting) {
|
|
92
|
+
setIsLoading(false);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setIsLoading(true);
|
|
97
|
+
let cancelled = false;
|
|
98
|
+
|
|
99
|
+
const loadHighlightedCode = async () => {
|
|
100
|
+
try {
|
|
101
|
+
const {codeToHtml} = await import('shiki');
|
|
102
|
+
const newHighlightedCodes: Record<string, string> = {};
|
|
103
|
+
|
|
104
|
+
for (const [command, val] of Object.entries(codes)) {
|
|
105
|
+
if (cancelled) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const highlighted = await codeToHtml(val, {
|
|
110
|
+
lang,
|
|
111
|
+
themes: {
|
|
112
|
+
light: themes.light,
|
|
113
|
+
dark: themes.dark,
|
|
114
|
+
},
|
|
115
|
+
defaultColor: resolvedTheme === 'dark' ? 'dark' : 'light',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
newHighlightedCodes[command] = highlighted;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!cancelled) {
|
|
122
|
+
setHighlightedCodes(newHighlightedCodes);
|
|
123
|
+
setIsLoading(false);
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
if (!cancelled) {
|
|
127
|
+
setIsLoading(false);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
loadHighlightedCode();
|
|
133
|
+
|
|
134
|
+
return () => {
|
|
135
|
+
cancelled = true;
|
|
136
|
+
};
|
|
137
|
+
}, [resolvedTheme, lang, themes.light, themes.dark, codes, syntaxHighlighting]);
|
|
138
|
+
|
|
139
|
+
return {highlightedCodes, isLoading};
|
|
140
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {useEffect} from 'react';
|
|
2
|
+
|
|
3
|
+
export function useShikiStyleInjection(syntaxHighlighting: boolean): void {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (!syntaxHighlighting) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const styleId = 'shiki-override-styles';
|
|
10
|
+
if (document.getElementById(styleId)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const style = document.createElement('style');
|
|
15
|
+
style.id = styleId;
|
|
16
|
+
style.textContent = `
|
|
17
|
+
.shiki-override pre,
|
|
18
|
+
.shiki-override code,
|
|
19
|
+
.shiki-override pre *,
|
|
20
|
+
.shiki-override code * {
|
|
21
|
+
background: transparent !important;
|
|
22
|
+
font-family: "Commit Mono", monospace !important;
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
document.head.appendChild(style);
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
const existingStyle = document.getElementById(styleId);
|
|
29
|
+
if (existingStyle) {
|
|
30
|
+
existingStyle.remove();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}, [syntaxHighlighting]);
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const hashString = (str: string): number => {
|
|
2
|
+
let hash = 0;
|
|
3
|
+
for (let i = 0; i < str.length; i++) {
|
|
4
|
+
const char = str.charCodeAt(i);
|
|
5
|
+
hash = (hash << 5) - hash + char;
|
|
6
|
+
hash = hash & hash;
|
|
7
|
+
}
|
|
8
|
+
return Math.abs(hash);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const getPlaceholderImageUrl = (name?: string): string => {
|
|
12
|
+
const backgroundColors = ['BFDFFF', 'BFEAFF', 'CFBFFF', 'FFBFC3', 'FFEABF', 'E3E6EA', 'EAEAEA'];
|
|
13
|
+
|
|
14
|
+
const seed = name?.trim() || 'avatar';
|
|
15
|
+
|
|
16
|
+
const colorIndex = hashString(seed) % backgroundColors.length;
|
|
17
|
+
const backgroundColor = backgroundColors[colorIndex];
|
|
18
|
+
|
|
19
|
+
return `https://api.dicebear.com/9.x/micah/svg?backgroundColor=${backgroundColor}&seed=${encodeURIComponent(seed)}`;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getInitial = (name?: string): string => {
|
|
23
|
+
if (name) {
|
|
24
|
+
return name.trim()[0]?.toUpperCase() ?? 'L';
|
|
25
|
+
}
|
|
26
|
+
return 'L';
|
|
27
|
+
};
|