@snowcone-app/ui 0.1.42 → 0.2.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/CHANGELOG.md +33 -0
- package/README.md +18 -4
- package/package.json +9 -5
- package/src/components/CanvasIsolationBoundary.tsx +202 -0
- package/src/components/LoadingOverlayPrism.tsx +251 -0
- package/src/composed/AddToCart.tsx +229 -0
- package/src/composed/ArtAlignment.tsx +703 -0
- package/src/composed/ArtSelector.tsx +290 -0
- package/src/composed/ArtworkCustomizer.tsx +212 -0
- package/src/composed/CanvasEditor.tsx +79 -0
- package/src/composed/ColorPicker.tsx +111 -0
- package/src/composed/CurrentSelectionDisplay.tsx +86 -0
- package/src/composed/HeroProductImage.tsx +1071 -0
- package/src/composed/Lightbox.index.ts +2 -0
- package/src/composed/Lightbox.tsx +230 -0
- package/src/composed/PlacementClipShapeSelector.tsx +88 -0
- package/src/composed/PlacementTabs.tsx +179 -0
- package/src/composed/ProductCard.tsx +298 -0
- package/src/composed/ProductGallery.tsx +54 -0
- package/src/composed/ProductImage.tsx +129 -0
- package/src/composed/ProductList.tsx +147 -0
- package/src/composed/ProductOptions.tsx +305 -0
- package/src/composed/RealtimeMockup.tsx +121 -0
- package/src/composed/TileCount.tsx +348 -0
- package/src/composed/carousels/HeroCarousel.tsx +240 -0
- package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
- package/src/composed/carousels/index.ts +11 -0
- package/src/composed/carousels/types.ts +58 -0
- package/src/composed/grids/MasonryGrid.tsx +238 -0
- package/src/composed/grids/index.ts +9 -0
- package/src/composed/search/CurrentRefinements.tsx +80 -0
- package/src/composed/search/Filters.tsx +49 -0
- package/src/composed/search/FiltersButton.tsx +57 -0
- package/src/composed/search/FiltersDrawer.tsx +375 -0
- package/src/composed/search/ProductGrid.tsx +118 -0
- package/src/composed/search/ProductHit.tsx +56 -0
- package/src/composed/search/SearchBox.tsx +109 -0
- package/src/composed/search/SearchProvider.tsx +136 -0
- package/src/composed/search/facetConfig.ts +16 -0
- package/src/composed/search/index.ts +22 -0
- package/src/composed/search/meilisearchAdapter.ts +20 -0
- package/src/composed/search/types.ts +22 -0
- package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
- package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
- package/src/composed/zoom/ZoomOverlay.tsx +194 -0
- package/src/composed/zoom/index.ts +12 -0
- package/src/composed/zoom/types.ts +12 -0
- package/src/design-system/ColorPalette.tsx +126 -0
- package/src/design-system/ColorSwatch.tsx +49 -0
- package/src/design-system/DesignSystemPage.tsx +130 -0
- package/src/design-system/ThemeSwitcher.tsx +181 -0
- package/src/design-system/TypographyScale.tsx +106 -0
- package/src/design-system/index.ts +5 -0
- package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
- package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
- package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
- package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
- package/src/hooks/useBrand.ts +41 -0
- package/src/hooks/useCanvasContext.ts +127 -0
- package/src/hooks/useDeviceDetection.ts +64 -0
- package/src/hooks/useFocusTrap.ts +70 -0
- package/src/hooks/useImagePreloader.ts +268 -0
- package/src/hooks/useImageTransition.ts +608 -0
- package/src/hooks/usePlacementsProcessor.ts +74 -0
- package/src/hooks/useProductGallery.ts +193 -0
- package/src/hooks/useProductPage.ts +467 -0
- package/src/hooks/useRenderGuard.ts +96 -0
- package/src/hooks/useScrollDirection.ts +196 -0
- package/src/hooks/viewport/index.ts +25 -0
- package/src/hooks/viewport/useContainerWidth.ts +59 -0
- package/src/hooks/viewport/useMediaQuery.ts +52 -0
- package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
- package/src/hooks/viewport/useViewportDimensions.ts +135 -0
- package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
- package/src/hooks/visibility/index.ts +15 -0
- package/src/hooks/visibility/observerPool.ts +150 -0
- package/src/index.ts +240 -0
- package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
- package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
- package/src/layouts/hero-zoom/index.ts +30 -0
- package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
- package/src/layouts/hero-zoom/types.ts +113 -0
- package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
- package/src/layouts/index.ts +9 -0
- package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
- package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
- package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
- package/src/layouts/pdp/PDPLayout.tsx +246 -0
- package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
- package/src/layouts/pdp/index.ts +40 -0
- package/src/lib/env.ts +15 -0
- package/src/lib/locale.ts +167 -0
- package/src/lib/router.tsx +46 -0
- package/src/lib/utils.ts +6 -0
- package/src/lightbox/README.md +77 -0
- package/src/next/index.tsx +26 -0
- package/src/patterns/MockupPriorityProvider.tsx +1014 -0
- package/src/patterns/Product.tsx +850 -0
- package/src/patterns/ProductPageProvider.tsx +224 -0
- package/src/patterns/RealtimeProvider.tsx +1162 -0
- package/src/patterns/ShopProvider.tsx +603 -0
- package/src/personalization/PersonalizationBridge.tsx +235 -0
- package/src/personalization/PersonalizationContext.ts +29 -0
- package/src/personalization/PersonalizationInputs.tsx +110 -0
- package/src/personalization/PersonalizationProvider.tsx +407 -0
- package/src/personalization/canvas-stub.d.ts +22 -0
- package/src/personalization/index.ts +43 -0
- package/src/personalization/types.ts +48 -0
- package/src/personalization/usePersonalization.ts +32 -0
- package/src/personalization/usePersonalizationShimmer.ts +159 -0
- package/src/personalization/utils.ts +59 -0
- package/src/primitives/BrandLogo.tsx +65 -0
- package/src/primitives/BrandName.tsx +51 -0
- package/src/primitives/Button.tsx +123 -0
- package/src/primitives/ColorSwatch.tsx +221 -0
- package/src/primitives/DragHintAnimation.tsx +190 -0
- package/src/primitives/EdgeSwipeGuards.tsx +60 -0
- package/src/primitives/FloatingActionGroup.tsx +176 -0
- package/src/primitives/ProductPrice.tsx +171 -0
- package/src/primitives/ProgressiveBlur.tsx +295 -0
- package/src/primitives/ThemeToggle.tsx +125 -0
- package/src/primitives/__tests__/story-coverage.test.ts +98 -0
- package/src/primitives/accordion.tsx +280 -0
- package/src/primitives/badge.tsx +137 -0
- package/src/primitives/card.tsx +61 -0
- package/src/primitives/checkbox.tsx +56 -0
- package/src/primitives/collapsible.tsx +51 -0
- package/src/primitives/drawer.tsx +828 -0
- package/src/primitives/dropdown-menu.tsx +197 -0
- package/src/primitives/fieldset.tsx +73 -0
- package/src/primitives/index.ts +138 -0
- package/src/primitives/input.tsx +91 -0
- package/src/primitives/kbd.tsx +130 -0
- package/src/primitives/label.tsx +20 -0
- package/src/primitives/link.tsx +182 -0
- package/src/primitives/popover.tsx +80 -0
- package/src/primitives/radio-group.tsx +79 -0
- package/src/primitives/scroll-fade.tsx +159 -0
- package/src/primitives/select.tsx +170 -0
- package/src/primitives/separator.tsx +25 -0
- package/src/primitives/slider.tsx +221 -0
- package/src/primitives/spinner.tsx +72 -0
- package/src/primitives/stories/Accordion.stories.tsx +121 -0
- package/src/primitives/stories/Badge.stories.tsx +221 -0
- package/src/primitives/stories/Button.stories.tsx +185 -0
- package/src/primitives/stories/Card.stories.tsx +171 -0
- package/src/primitives/stories/Checkbox.stories.tsx +214 -0
- package/src/primitives/stories/Collapsible.stories.tsx +230 -0
- package/src/primitives/stories/Drawer.stories.tsx +378 -0
- package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
- package/src/primitives/stories/Fieldset.stories.tsx +212 -0
- package/src/primitives/stories/Input.stories.tsx +172 -0
- package/src/primitives/stories/Kbd.stories.tsx +183 -0
- package/src/primitives/stories/Label.stories.tsx +98 -0
- package/src/primitives/stories/Link.stories.tsx +260 -0
- package/src/primitives/stories/Popover.stories.tsx +178 -0
- package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
- package/src/primitives/stories/Select.stories.tsx +222 -0
- package/src/primitives/stories/Separator.stories.tsx +134 -0
- package/src/primitives/stories/Slider.stories.tsx +203 -0
- package/src/primitives/stories/Spinner.stories.tsx +142 -0
- package/src/primitives/stories/Surface.stories.tsx +257 -0
- package/src/primitives/stories/Switch.stories.tsx +131 -0
- package/src/primitives/stories/Tabs.stories.tsx +275 -0
- package/src/primitives/stories/TextField.stories.tsx +139 -0
- package/src/primitives/stories/Textarea.stories.tsx +148 -0
- package/src/primitives/stories/Tooltip.stories.tsx +119 -0
- package/src/primitives/surface.tsx +86 -0
- package/src/primitives/switch.tsx +35 -0
- package/src/primitives/tabs.tsx +206 -0
- package/src/primitives/text-field.tsx +84 -0
- package/src/primitives/textarea.tsx +50 -0
- package/src/primitives/tooltip.tsx +58 -0
- package/src/services/CanvasExportService.ts +518 -0
- package/src/styles/base.css +380 -0
- package/src/styles/defaults.css +280 -0
- package/src/styles/globals.css +1242 -0
- package/src/styles/index.css +17 -0
- package/src/styles/ne-themes.css +4740 -0
- package/src/styles/tailwind.css +11 -0
- package/src/styles/tokens.css +117 -0
- package/src/styles/utilities.css +188 -0
- package/src/themes/apply-theme.ts +449 -0
- package/src/themes/getThemeStyles.ts +454 -0
- package/src/themes/index.ts +48 -0
- package/src/themes/oklch-theme.ts +283 -0
- package/src/themes/presets.ts +989 -0
- package/src/themes/types.ts +386 -0
- package/src/themes/useTheme.tsx +450 -0
- package/src/utils/dev-warnings.ts +161 -0
- package/src/utils/devWarnings.ts +153 -0
- package/dist/styles.css +0 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
5
|
+
import { Icon } from '@iconify/react';
|
|
6
|
+
import { cn } from '../lib/utils';
|
|
7
|
+
|
|
8
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
9
|
+
|
|
10
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
11
|
+
|
|
12
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
13
|
+
|
|
14
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
15
|
+
|
|
16
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
17
|
+
|
|
18
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
19
|
+
|
|
20
|
+
const DropdownMenuSubTrigger = React.forwardRef<
|
|
21
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
22
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
23
|
+
inset?: boolean;
|
|
24
|
+
}
|
|
25
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
26
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
27
|
+
ref={ref}
|
|
28
|
+
className={cn(
|
|
29
|
+
'focus:bg-default/50 dark:focus:bg-white/5 data-[state=open]:bg-default/50 dark:data-[state=open]:bg-white/5 flex cursor-default items-center gap-3 rounded-lg px-3 py-3 text-base font-medium text-foreground outline-none select-none',
|
|
30
|
+
'[&_svg]:size-5 [&_svg]:shrink-0 [&_svg]:text-foreground/60',
|
|
31
|
+
inset && 'pl-8',
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
<Icon icon="gravity-ui:chevron-right" className="ml-auto size-5" />
|
|
38
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
39
|
+
));
|
|
40
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
41
|
+
|
|
42
|
+
const DropdownMenuSubContent = React.forwardRef<
|
|
43
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
44
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
|
45
|
+
>(({ className, ...props }, ref) => (
|
|
46
|
+
<DropdownMenuPrimitive.SubContent
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn(
|
|
49
|
+
'z-[200] bg-popover text-popover-foreground min-w-[8rem] overflow-hidden rounded-2xl p-2',
|
|
50
|
+
'shadow-[0_4px_24px_-4px_rgba(0,0,0,0.15),0_0_0_1px_rgba(0,0,0,0.05)]',
|
|
51
|
+
'dark:shadow-[0_4px_24px_-4px_rgba(0,0,0,0.4),0_0_0_1px_rgba(255,255,255,0.1)]',
|
|
52
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
|
53
|
+
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
|
54
|
+
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
|
55
|
+
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
));
|
|
61
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
62
|
+
|
|
63
|
+
const DropdownMenuContent = React.forwardRef<
|
|
64
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
65
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
|
66
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
67
|
+
<DropdownMenuPrimitive.Portal>
|
|
68
|
+
<DropdownMenuPrimitive.Content
|
|
69
|
+
ref={ref}
|
|
70
|
+
sideOffset={sideOffset}
|
|
71
|
+
className={cn(
|
|
72
|
+
'z-[200] bg-popover text-popover-foreground min-w-[8rem] overflow-hidden rounded-2xl p-2',
|
|
73
|
+
'shadow-[0_4px_24px_-4px_rgba(0,0,0,0.15),0_0_0_1px_rgba(0,0,0,0.05)]',
|
|
74
|
+
'dark:shadow-[0_4px_24px_-4px_rgba(0,0,0,0.4),0_0_0_1px_rgba(255,255,255,0.1)]',
|
|
75
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
|
76
|
+
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
|
77
|
+
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
|
78
|
+
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
79
|
+
className
|
|
80
|
+
)}
|
|
81
|
+
{...props}
|
|
82
|
+
/>
|
|
83
|
+
</DropdownMenuPrimitive.Portal>
|
|
84
|
+
));
|
|
85
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
86
|
+
|
|
87
|
+
const DropdownMenuItem = React.forwardRef<
|
|
88
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
89
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
90
|
+
inset?: boolean;
|
|
91
|
+
}
|
|
92
|
+
>(({ className, inset, ...props }, ref) => (
|
|
93
|
+
<DropdownMenuPrimitive.Item
|
|
94
|
+
ref={ref}
|
|
95
|
+
className={cn(
|
|
96
|
+
'relative flex cursor-default items-center gap-3 rounded-lg px-3 py-3 text-base font-medium text-foreground transition-colors outline-none select-none',
|
|
97
|
+
'[&_svg]:size-5 [&_svg]:shrink-0 [&_svg]:text-foreground/60',
|
|
98
|
+
'hover:bg-default/50 focus:bg-default/50 dark:hover:bg-white/5 dark:focus:bg-white/5 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
99
|
+
inset && 'pl-8',
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
));
|
|
105
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
106
|
+
|
|
107
|
+
const DropdownMenuCheckboxItem = React.forwardRef<
|
|
108
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
109
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
|
110
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
111
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
112
|
+
ref={ref}
|
|
113
|
+
className={cn(
|
|
114
|
+
'relative flex cursor-default items-center rounded-full py-1 pr-2 pl-7 text-sm transition-colors outline-none select-none',
|
|
115
|
+
'hover:bg-default/50 focus:bg-default/50 dark:hover:bg-white/5 dark:focus:bg-white/5 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
116
|
+
className
|
|
117
|
+
)}
|
|
118
|
+
checked={checked}
|
|
119
|
+
{...props}
|
|
120
|
+
>
|
|
121
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
122
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
123
|
+
<Icon icon="gravity-ui:check" className="size-4" />
|
|
124
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
125
|
+
</span>
|
|
126
|
+
{children}
|
|
127
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
128
|
+
));
|
|
129
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
130
|
+
|
|
131
|
+
const DropdownMenuRadioItem = React.forwardRef<
|
|
132
|
+
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
133
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
|
134
|
+
>(({ className, children, ...props }, ref) => (
|
|
135
|
+
<DropdownMenuPrimitive.RadioItem
|
|
136
|
+
ref={ref}
|
|
137
|
+
className={cn(
|
|
138
|
+
'relative flex cursor-default items-center rounded-full py-1 pr-2 pl-7 text-sm transition-colors outline-none select-none',
|
|
139
|
+
'hover:bg-default/50 focus:bg-default/50 dark:hover:bg-white/5 dark:focus:bg-white/5 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
140
|
+
className
|
|
141
|
+
)}
|
|
142
|
+
{...props}
|
|
143
|
+
>
|
|
144
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
145
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
146
|
+
<Icon icon="gravity-ui:circle-fill" className="size-2" />
|
|
147
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
148
|
+
</span>
|
|
149
|
+
{children}
|
|
150
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
151
|
+
));
|
|
152
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
153
|
+
|
|
154
|
+
const DropdownMenuLabel = React.forwardRef<
|
|
155
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
156
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
157
|
+
inset?: boolean;
|
|
158
|
+
}
|
|
159
|
+
>(({ className, inset, ...props }, ref) => (
|
|
160
|
+
<DropdownMenuPrimitive.Label
|
|
161
|
+
ref={ref}
|
|
162
|
+
className={cn('px-2 py-1.5 text-xs font-semibold text-muted-foreground', inset && 'pl-8', className)}
|
|
163
|
+
{...props}
|
|
164
|
+
/>
|
|
165
|
+
));
|
|
166
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
167
|
+
|
|
168
|
+
const DropdownMenuSeparator = React.forwardRef<
|
|
169
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
170
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
|
171
|
+
>(({ className, ...props }, ref) => (
|
|
172
|
+
<DropdownMenuPrimitive.Separator ref={ref} className={cn('bg-divider mx-2 my-1 h-px', className)} {...props} />
|
|
173
|
+
));
|
|
174
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
175
|
+
|
|
176
|
+
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
177
|
+
return <span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} />;
|
|
178
|
+
};
|
|
179
|
+
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
|
180
|
+
|
|
181
|
+
export {
|
|
182
|
+
DropdownMenu,
|
|
183
|
+
DropdownMenuTrigger,
|
|
184
|
+
DropdownMenuContent,
|
|
185
|
+
DropdownMenuItem,
|
|
186
|
+
DropdownMenuCheckboxItem,
|
|
187
|
+
DropdownMenuRadioItem,
|
|
188
|
+
DropdownMenuLabel,
|
|
189
|
+
DropdownMenuSeparator,
|
|
190
|
+
DropdownMenuShortcut,
|
|
191
|
+
DropdownMenuGroup,
|
|
192
|
+
DropdownMenuPortal,
|
|
193
|
+
DropdownMenuSub,
|
|
194
|
+
DropdownMenuSubContent,
|
|
195
|
+
DropdownMenuSubTrigger,
|
|
196
|
+
DropdownMenuRadioGroup,
|
|
197
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
export interface FieldsetProps extends React.FieldsetHTMLAttributes<HTMLFieldSetElement> {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fieldset - A semantic grouping element for form controls.
|
|
10
|
+
*
|
|
11
|
+
* Use this to group related form elements together with an optional legend.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <Fieldset>
|
|
16
|
+
* <legend className="text-sm font-medium">Personal Info</legend>
|
|
17
|
+
* <TextField>
|
|
18
|
+
* <Label>Name</Label>
|
|
19
|
+
* <Input placeholder="Enter name..." />
|
|
20
|
+
* </TextField>
|
|
21
|
+
* </Fieldset>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
const Fieldset = React.forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
25
|
+
({ className, disabled, children, ...props }, ref) => {
|
|
26
|
+
return (
|
|
27
|
+
<fieldset
|
|
28
|
+
ref={ref}
|
|
29
|
+
disabled={disabled}
|
|
30
|
+
className={cn(
|
|
31
|
+
'flex flex-col gap-3',
|
|
32
|
+
// Disabled styles
|
|
33
|
+
disabled && 'opacity-60 cursor-not-allowed',
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</fieldset>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
Fieldset.displayName = 'Fieldset';
|
|
44
|
+
|
|
45
|
+
export interface LegendProps extends React.HTMLAttributes<HTMLLegendElement> {}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Legend - A caption for a fieldset.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* <Fieldset>
|
|
53
|
+
* <Legend>Account Settings</Legend>
|
|
54
|
+
* ...
|
|
55
|
+
* </Fieldset>
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
const Legend = React.forwardRef<HTMLLegendElement, LegendProps>(
|
|
59
|
+
({ className, children, ...props }, ref) => {
|
|
60
|
+
return (
|
|
61
|
+
<legend
|
|
62
|
+
ref={ref}
|
|
63
|
+
className={cn('text-base text-foreground mb-3 font-heading', className)}
|
|
64
|
+
{...props}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</legend>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
Legend.displayName = 'Legend';
|
|
72
|
+
|
|
73
|
+
export { Fieldset, Legend };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Button
|
|
2
|
+
export { Button, buttonVariants } from './Button';
|
|
3
|
+
export type { ButtonProps } from './Button';
|
|
4
|
+
|
|
5
|
+
// Label
|
|
6
|
+
export { Label } from './label';
|
|
7
|
+
|
|
8
|
+
// Tooltip
|
|
9
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipRoot } from './tooltip';
|
|
10
|
+
|
|
11
|
+
// Slider
|
|
12
|
+
export { Slider } from './slider';
|
|
13
|
+
|
|
14
|
+
// Switch
|
|
15
|
+
export { Switch } from './switch';
|
|
16
|
+
|
|
17
|
+
// Popover
|
|
18
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverRoot } from './popover';
|
|
19
|
+
|
|
20
|
+
// Tabs
|
|
21
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, TabsRoot } from './tabs';
|
|
22
|
+
|
|
23
|
+
// RadioGroup
|
|
24
|
+
export { RadioGroup, RadioGroupItem, Radio } from './radio-group';
|
|
25
|
+
|
|
26
|
+
// Collapsible (and Disclosure aliases)
|
|
27
|
+
export {
|
|
28
|
+
Collapsible,
|
|
29
|
+
CollapsibleTrigger,
|
|
30
|
+
CollapsibleContent,
|
|
31
|
+
CollapsibleRoot,
|
|
32
|
+
Disclosure,
|
|
33
|
+
DisclosureTrigger,
|
|
34
|
+
DisclosureContent,
|
|
35
|
+
} from './collapsible';
|
|
36
|
+
|
|
37
|
+
// Accordion
|
|
38
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './accordion';
|
|
39
|
+
|
|
40
|
+
// DropdownMenu
|
|
41
|
+
export {
|
|
42
|
+
DropdownMenu,
|
|
43
|
+
DropdownMenuTrigger,
|
|
44
|
+
DropdownMenuContent,
|
|
45
|
+
DropdownMenuItem,
|
|
46
|
+
DropdownMenuCheckboxItem,
|
|
47
|
+
DropdownMenuRadioItem,
|
|
48
|
+
DropdownMenuLabel,
|
|
49
|
+
DropdownMenuSeparator,
|
|
50
|
+
DropdownMenuShortcut,
|
|
51
|
+
DropdownMenuGroup,
|
|
52
|
+
DropdownMenuPortal,
|
|
53
|
+
DropdownMenuSub,
|
|
54
|
+
DropdownMenuSubContent,
|
|
55
|
+
DropdownMenuSubTrigger,
|
|
56
|
+
DropdownMenuRadioGroup,
|
|
57
|
+
} from './dropdown-menu';
|
|
58
|
+
|
|
59
|
+
// Select
|
|
60
|
+
export {
|
|
61
|
+
Select,
|
|
62
|
+
SelectGroup,
|
|
63
|
+
SelectValue,
|
|
64
|
+
SelectTrigger,
|
|
65
|
+
SelectContent,
|
|
66
|
+
SelectLabel,
|
|
67
|
+
SelectItem,
|
|
68
|
+
SelectSeparator,
|
|
69
|
+
SelectScrollUpButton,
|
|
70
|
+
SelectScrollDownButton,
|
|
71
|
+
} from './select';
|
|
72
|
+
|
|
73
|
+
// Drawer - Mobile-optimized drawer component (works on all platforms including iOS Safari)
|
|
74
|
+
export {
|
|
75
|
+
// Primary exports
|
|
76
|
+
Drawer,
|
|
77
|
+
DrawerTrigger,
|
|
78
|
+
DrawerClose,
|
|
79
|
+
DrawerContent,
|
|
80
|
+
DrawerHeader,
|
|
81
|
+
DrawerFooter,
|
|
82
|
+
DrawerTitle,
|
|
83
|
+
DrawerDescription,
|
|
84
|
+
// Legacy IOS-prefixed names (for backwards compatibility)
|
|
85
|
+
IOSDrawer,
|
|
86
|
+
IOSDrawerTrigger,
|
|
87
|
+
IOSDrawerClose,
|
|
88
|
+
IOSDrawerContent,
|
|
89
|
+
IOSDrawerHeader,
|
|
90
|
+
IOSDrawerFooter,
|
|
91
|
+
IOSDrawerTitle,
|
|
92
|
+
IOSDrawerDescription,
|
|
93
|
+
} from './drawer';
|
|
94
|
+
|
|
95
|
+
// Input
|
|
96
|
+
export { Input } from './input';
|
|
97
|
+
export type { InputProps } from './input';
|
|
98
|
+
|
|
99
|
+
// Textarea (with HeroUI TextArea alias)
|
|
100
|
+
export { Textarea, TextArea } from './textarea';
|
|
101
|
+
export type { TextareaProps } from './textarea';
|
|
102
|
+
|
|
103
|
+
// Checkbox
|
|
104
|
+
export { Checkbox } from './checkbox';
|
|
105
|
+
|
|
106
|
+
// Card
|
|
107
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } from './card';
|
|
108
|
+
|
|
109
|
+
// Separator
|
|
110
|
+
export { Separator } from './separator';
|
|
111
|
+
|
|
112
|
+
// Badge (HeroUI Chip replacement)
|
|
113
|
+
export { Badge, Chip, badgeVariants } from './badge';
|
|
114
|
+
export type { BadgeProps } from './badge';
|
|
115
|
+
|
|
116
|
+
// Spinner
|
|
117
|
+
export { Spinner, spinnerVariants } from './spinner';
|
|
118
|
+
export type { SpinnerProps } from './spinner';
|
|
119
|
+
|
|
120
|
+
// Surface
|
|
121
|
+
export { Surface, surfaceVariants, useSurface } from './surface';
|
|
122
|
+
export type { SurfaceProps } from './surface';
|
|
123
|
+
|
|
124
|
+
// TextField (Input + Label wrapper for HeroUI compatibility)
|
|
125
|
+
export { TextField } from './text-field';
|
|
126
|
+
export type { TextFieldProps } from './text-field';
|
|
127
|
+
|
|
128
|
+
// Kbd (keyboard shortcut display)
|
|
129
|
+
export { Kbd, kbdVariants } from './kbd';
|
|
130
|
+
export type { KbdProps } from './kbd';
|
|
131
|
+
|
|
132
|
+
// Fieldset
|
|
133
|
+
export { Fieldset, Legend } from './fieldset';
|
|
134
|
+
export type { FieldsetProps, LegendProps } from './fieldset';
|
|
135
|
+
|
|
136
|
+
// Link
|
|
137
|
+
export { Link, LinkIcon, linkVariants } from './link';
|
|
138
|
+
export type { LinkProps } from './link';
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
import { useSurface } from './surface';
|
|
6
|
+
|
|
7
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
8
|
+
/** Whether the input is in an invalid state */
|
|
9
|
+
isInvalid?: boolean;
|
|
10
|
+
/** Content to display at the start of the input */
|
|
11
|
+
startContent?: React.ReactNode;
|
|
12
|
+
/** Content to display at the end of the input */
|
|
13
|
+
endContent?: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Visual variant - auto-detected from Surface context, or can be set explicitly
|
|
16
|
+
* - `default`: Standard field styling for forms (bg-field)
|
|
17
|
+
* - `filled`: Field on elevated surface (bg-field-on-surface)
|
|
18
|
+
* - `subtle`: Blends with buttons/tabs in toolbars (bg-default)
|
|
19
|
+
*/
|
|
20
|
+
variant?: 'default' | 'filled' | 'subtle';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
24
|
+
(
|
|
25
|
+
{
|
|
26
|
+
className,
|
|
27
|
+
type,
|
|
28
|
+
isInvalid,
|
|
29
|
+
startContent,
|
|
30
|
+
endContent,
|
|
31
|
+
disabled,
|
|
32
|
+
readOnly,
|
|
33
|
+
required,
|
|
34
|
+
variant,
|
|
35
|
+
...props
|
|
36
|
+
},
|
|
37
|
+
ref
|
|
38
|
+
) => {
|
|
39
|
+
// Auto-detect variant from Surface context
|
|
40
|
+
const surface = useSurface();
|
|
41
|
+
const isOnSurface = surface.variant === 'default' || surface.variant === 'secondary';
|
|
42
|
+
const effectiveVariant = variant ?? (isOnSurface ? 'filled' : 'default');
|
|
43
|
+
|
|
44
|
+
const inputElement = (
|
|
45
|
+
<input
|
|
46
|
+
type={type}
|
|
47
|
+
className={cn(
|
|
48
|
+
'flex h-10 w-full rounded-input px-2.5 py-1.5 text-sm appearance-none',
|
|
49
|
+
'text-foreground border-none',
|
|
50
|
+
'file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground',
|
|
51
|
+
'placeholder:text-foreground/50',
|
|
52
|
+
'transition-shadow duration-200 ease-out focus-visible:outline-none focus:ring-2 focus:ring-focus focus:ring-offset-1 focus:ring-offset-background',
|
|
53
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
54
|
+
// Variant styles - semantic: field on page vs field on surface vs toolbar
|
|
55
|
+
effectiveVariant === 'default' && 'bg-field shadow-soft dark:shadow-none',
|
|
56
|
+
effectiveVariant === 'filled' && 'bg-[var(--color-field-on-surface)]',
|
|
57
|
+
effectiveVariant === 'subtle' && 'bg-default dark:bg-[var(--color-default-on-surface)]',
|
|
58
|
+
isInvalid && 'ring-2 ring-danger focus-visible:ring-danger',
|
|
59
|
+
startContent && 'pl-10',
|
|
60
|
+
endContent && 'pr-10',
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
ref={ref}
|
|
64
|
+
disabled={disabled}
|
|
65
|
+
readOnly={readOnly}
|
|
66
|
+
required={required}
|
|
67
|
+
aria-invalid={isInvalid}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (startContent || endContent) {
|
|
73
|
+
return (
|
|
74
|
+
<div className="relative">
|
|
75
|
+
{startContent && (
|
|
76
|
+
<div className="absolute left-3 top-1/2 -translate-y-1/2 text-foreground opacity-40">{startContent}</div>
|
|
77
|
+
)}
|
|
78
|
+
{inputElement}
|
|
79
|
+
{endContent && (
|
|
80
|
+
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-foreground opacity-40">{endContent}</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return inputElement;
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
Input.displayName = 'Input';
|
|
90
|
+
|
|
91
|
+
export { Input };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
import { cn } from '../lib/utils';
|
|
6
|
+
|
|
7
|
+
const kbdVariants = cva(
|
|
8
|
+
'inline-flex items-center justify-center gap-1 rounded font-mono text-xs font-medium',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
'border border-divider bg-default/50 px-1.5 py-0.5 text-foreground/80 shadow-[0_1px_0_1px] shadow-black/10 dark:shadow-black/30',
|
|
14
|
+
minimal: 'bg-default/30 px-1 py-0.5 text-foreground/60',
|
|
15
|
+
outline: 'border border-divider px-1.5 py-0.5 text-foreground/70',
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
sm: 'h-4 min-w-[1rem] text-[10px]',
|
|
19
|
+
md: 'h-5 min-w-[1.25rem] text-xs',
|
|
20
|
+
lg: 'h-6 min-w-[1.5rem] text-sm',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: 'default',
|
|
25
|
+
size: 'md',
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export interface KbdProps
|
|
31
|
+
extends React.HTMLAttributes<HTMLElement>,
|
|
32
|
+
VariantProps<typeof kbdVariants> {
|
|
33
|
+
/**
|
|
34
|
+
* The keyboard key(s) to display.
|
|
35
|
+
* Can use special values that will be transformed:
|
|
36
|
+
* - "cmd" or "command" -> displays platform-specific modifier (⌘ on Mac, Ctrl on Windows)
|
|
37
|
+
* - "ctrl" -> ⌃
|
|
38
|
+
* - "alt" or "option" -> ⌥
|
|
39
|
+
* - "shift" -> ⇧
|
|
40
|
+
* - "enter" or "return" -> ↵
|
|
41
|
+
* - "backspace" or "delete" -> ⌫
|
|
42
|
+
* - "escape" or "esc" -> Esc
|
|
43
|
+
* - "tab" -> ⇥
|
|
44
|
+
* - "space" -> Space
|
|
45
|
+
* - "up", "down", "left", "right" -> arrows
|
|
46
|
+
*/
|
|
47
|
+
keys?: string | string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Map of special key names to their display symbols
|
|
52
|
+
*/
|
|
53
|
+
const keySymbols: Record<string, string> = {
|
|
54
|
+
cmd: '⌘',
|
|
55
|
+
command: '⌘',
|
|
56
|
+
ctrl: '⌃',
|
|
57
|
+
control: '⌃',
|
|
58
|
+
alt: '⌥',
|
|
59
|
+
option: '⌥',
|
|
60
|
+
shift: '⇧',
|
|
61
|
+
enter: '↵',
|
|
62
|
+
return: '↵',
|
|
63
|
+
backspace: '⌫',
|
|
64
|
+
delete: '⌫',
|
|
65
|
+
escape: 'Esc',
|
|
66
|
+
esc: 'Esc',
|
|
67
|
+
tab: '⇥',
|
|
68
|
+
space: '␣',
|
|
69
|
+
up: '↑',
|
|
70
|
+
down: '↓',
|
|
71
|
+
left: '←',
|
|
72
|
+
right: '→',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Transforms a key name to its display symbol
|
|
77
|
+
*/
|
|
78
|
+
function formatKey(key: string): string {
|
|
79
|
+
const lowerKey = key.toLowerCase();
|
|
80
|
+
if (keySymbols[lowerKey]) {
|
|
81
|
+
return keySymbols[lowerKey];
|
|
82
|
+
}
|
|
83
|
+
// Single letters should be uppercase
|
|
84
|
+
if (key.length === 1) {
|
|
85
|
+
return key.toUpperCase();
|
|
86
|
+
}
|
|
87
|
+
return key;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Kbd - Keyboard key display component.
|
|
92
|
+
*
|
|
93
|
+
* Displays keyboard shortcuts in a visually consistent way.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```tsx
|
|
97
|
+
* <Kbd>K</Kbd>
|
|
98
|
+
* <Kbd keys={['cmd', 'shift', 'P']} />
|
|
99
|
+
* <Kbd keys="cmd+K" />
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
const Kbd = React.forwardRef<HTMLElement, KbdProps>(
|
|
103
|
+
({ className, variant, size, keys, children, ...props }, ref) => {
|
|
104
|
+
// Parse keys if provided as a string with "+" separator
|
|
105
|
+
let parsedKeys: string[] = [];
|
|
106
|
+
if (keys) {
|
|
107
|
+
if (Array.isArray(keys)) {
|
|
108
|
+
parsedKeys = keys;
|
|
109
|
+
} else if (typeof keys === 'string') {
|
|
110
|
+
parsedKeys = keys.split('+').map((k) => k.trim());
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<kbd ref={ref} className={cn(kbdVariants({ variant, size }), className)} {...props}>
|
|
116
|
+
{parsedKeys.length > 0
|
|
117
|
+
? parsedKeys.map((key, index) => (
|
|
118
|
+
<React.Fragment key={index}>
|
|
119
|
+
{index > 0 && <span className="text-foreground/40">+</span>}
|
|
120
|
+
<span>{formatKey(key)}</span>
|
|
121
|
+
</React.Fragment>
|
|
122
|
+
))
|
|
123
|
+
: children}
|
|
124
|
+
</kbd>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
Kbd.displayName = 'Kbd';
|
|
129
|
+
|
|
130
|
+
export { Kbd, kbdVariants };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
5
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
6
|
+
import { cn } from '../lib/utils';
|
|
7
|
+
|
|
8
|
+
const labelVariants = cva(
|
|
9
|
+
'block text-sm leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70 font-label'
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const Label = React.forwardRef<
|
|
13
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
14
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
|
15
|
+
>(({ className, ...props }, ref) => (
|
|
16
|
+
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
17
|
+
));
|
|
18
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
19
|
+
|
|
20
|
+
export { Label };
|