@a4ui/core 0.37.0 → 0.38.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/README.md +8 -7
- package/dist/Collapse-Cr5cDqAl.js +375 -0
- package/dist/charts/AreaChart.d.ts +38 -0
- package/dist/charts/BarChart.d.ts +17 -1
- package/dist/charts/PieChart.d.ts +29 -0
- package/dist/charts/RadialBarChart.d.ts +33 -0
- package/dist/charts/index.d.ts +5 -0
- package/dist/charts/shared.d.ts +111 -0
- package/dist/charts.js +1493 -780
- package/dist/commerce.js +625 -634
- package/dist/elements.css +1076 -8
- package/dist/elements.iife.js +1 -6
- package/dist/elements.js +7774 -9133
- package/dist/full.css +1076 -8
- package/dist/index.d.ts +30 -3
- package/dist/index.js +17502 -15098
- package/dist/layout/Aurora.d.ts +16 -0
- package/dist/layout/EffectsToggle.d.ts +1 -1
- package/dist/layout/SpaceBackground.d.ts +1 -1
- package/dist/lib/anchored.d.ts +16 -0
- package/dist/motion-CTIBwMCA.js +104 -0
- package/dist/styles.css +110 -3
- package/dist/ui/AspectRatio.d.ts +19 -0
- package/dist/ui/Attachment.d.ts +70 -0
- package/dist/ui/ButtonGroup.d.ts +37 -0
- package/dist/ui/ChatMarker.d.ts +20 -0
- package/dist/ui/ChatThread.d.ts +32 -8
- package/dist/ui/ContactSection.d.ts +49 -0
- package/dist/ui/CtaBanner.d.ts +28 -0
- package/dist/ui/DateField.d.ts +2 -2
- package/dist/ui/Drawer.d.ts +5 -2
- package/dist/ui/Faq.d.ts +29 -0
- package/dist/ui/FeatureGrid.d.ts +35 -0
- package/dist/ui/Field.d.ts +148 -0
- package/dist/ui/Hero.d.ts +48 -0
- package/dist/ui/InputGroup.d.ts +93 -0
- package/dist/ui/Item.d.ts +63 -0
- package/dist/ui/Label.d.ts +23 -0
- package/dist/ui/Menubar.d.ts +83 -0
- package/dist/ui/Message.d.ts +51 -5
- package/dist/ui/Modal.d.ts +8 -2
- package/dist/ui/NativeSelect.d.ts +26 -0
- package/dist/ui/NavigationMenu.d.ts +85 -0
- package/dist/ui/PromptComposer.d.ts +5 -0
- package/dist/ui/Prose.d.ts +21 -0
- package/dist/ui/Questionnaire.d.ts +71 -0
- package/dist/ui/ScrollArea.d.ts +30 -0
- package/dist/ui/Sidebar.d.ts +229 -0
- package/dist/ui/SiteFooter.d.ts +39 -0
- package/dist/ui/SiteNav.d.ts +35 -0
- package/dist/ui/TeamGrid.d.ts +33 -0
- package/dist/ui/Testimonials.d.ts +37 -0
- package/dist/ui/internal/sectionKit.d.ts +22 -0
- package/dist/ui/internal/socialIcons.d.ts +9 -0
- package/package.json +38 -26
- package/preset.d.ts +23 -0
- package/src/charts/AreaChart.tsx +206 -0
- package/src/charts/BarChart.tsx +241 -5
- package/src/charts/DonutChart.tsx +8 -11
- package/src/charts/LineChart.tsx +2 -3
- package/src/charts/PieChart.tsx +145 -0
- package/src/charts/RadialBarChart.tsx +141 -0
- package/src/charts/index.ts +14 -0
- package/src/charts/shared.tsx +304 -0
- package/src/index.ts +192 -3
- package/src/layout/Aurora.tsx +36 -7
- package/src/layout/sceneEffects.ts +22 -3
- package/src/lib/anchored.ts +57 -0
- package/src/ui/AspectRatio.tsx +35 -0
- package/src/ui/Attachment.tsx +189 -0
- package/src/ui/ButtonGroup.tsx +89 -0
- package/src/ui/ChatMarker.tsx +58 -0
- package/src/ui/ChatThread.tsx +115 -23
- package/src/ui/ContactSection.tsx +199 -0
- package/src/ui/CtaBanner.tsx +62 -0
- package/src/ui/DateField.tsx +17 -7
- package/src/ui/DateRangePicker.tsx +15 -5
- package/src/ui/Drawer.tsx +37 -4
- package/src/ui/Faq.tsx +57 -0
- package/src/ui/FeatureGrid.tsx +77 -0
- package/src/ui/Field.tsx +374 -0
- package/src/ui/Hero.tsx +146 -0
- package/src/ui/InputGroup.tsx +219 -0
- package/src/ui/Item.tsx +185 -0
- package/src/ui/Label.tsx +50 -0
- package/src/ui/Menubar.tsx +212 -0
- package/src/ui/Message.tsx +139 -13
- package/src/ui/Modal.tsx +41 -5
- package/src/ui/MultiSelect.tsx +15 -5
- package/src/ui/NativeSelect.tsx +66 -0
- package/src/ui/NavigationMenu.tsx +262 -0
- package/src/ui/PromptComposer.tsx +11 -0
- package/src/ui/Prose.tsx +63 -0
- package/src/ui/Questionnaire.tsx +331 -0
- package/src/ui/ScrollArea.tsx +70 -0
- package/src/ui/Sidebar.tsx +753 -0
- package/src/ui/SiteFooter.tsx +112 -0
- package/src/ui/SiteNav.tsx +146 -0
- package/src/ui/TeamGrid.tsx +81 -0
- package/src/ui/Testimonials.tsx +103 -0
- package/src/ui/TimeField.tsx +15 -5
- package/src/ui/internal/sectionKit.tsx +72 -0
- package/src/ui/internal/socialIcons.tsx +41 -0
- package/dist/NumberInput-Bnh2u7pp.js +0 -381
- package/dist/motion-jYMWmZqB.js +0 -119
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// import '@a4ui/core/styles.css'
|
|
9
9
|
// import { Button, Card, Modal } from '@a4ui/core'
|
|
10
10
|
|
|
11
|
-
export const A4UI_VERSION = '0.
|
|
11
|
+
export const A4UI_VERSION = '0.38.0'
|
|
12
12
|
|
|
13
13
|
// Helpers (src/lib) — generic, framework-level utilities.
|
|
14
14
|
export { cn } from './lib/cn'
|
|
@@ -42,6 +42,19 @@ export { createLinkedSelection, type LinkedSelection } from './lib/createLinkedS
|
|
|
42
42
|
export { Accordion, type AccordionItem } from './ui/Accordion'
|
|
43
43
|
export { Badge, type BadgeTone } from './ui/Badge'
|
|
44
44
|
export { Button, type ButtonVariant } from './ui/Button'
|
|
45
|
+
export {
|
|
46
|
+
ButtonGroup,
|
|
47
|
+
ButtonGroupText,
|
|
48
|
+
ButtonGroupSeparator,
|
|
49
|
+
type ButtonGroupProps,
|
|
50
|
+
type ButtonGroupTextProps,
|
|
51
|
+
type ButtonGroupSeparatorProps,
|
|
52
|
+
type ButtonGroupOrientation,
|
|
53
|
+
} from './ui/ButtonGroup'
|
|
54
|
+
export { AspectRatio, type AspectRatioProps } from './ui/AspectRatio'
|
|
55
|
+
export { Label, type LabelProps } from './ui/Label'
|
|
56
|
+
export { NativeSelect, type NativeSelectProps, type NativeSelectSize } from './ui/NativeSelect'
|
|
57
|
+
export { Prose, type ProseProps, type ProseSize } from './ui/Prose'
|
|
45
58
|
export { Card, CardHeader, CardTitle, CardContent } from './ui/Card'
|
|
46
59
|
export { Checkbox } from './ui/Checkbox'
|
|
47
60
|
export { DateField } from './ui/DateField'
|
|
@@ -49,12 +62,55 @@ export { TimeField, type TimeFieldProps } from './ui/TimeField'
|
|
|
49
62
|
export { DateTimeField, type DateTimeFieldProps } from './ui/DateTimeField'
|
|
50
63
|
export { DateRangePicker, type DateRange, type DateRangePickerProps } from './ui/DateRangePicker'
|
|
51
64
|
export { FormField, FormLabel, FormControl, FormDescription, FormError, type FormFieldProps } from './ui/Form'
|
|
65
|
+
export {
|
|
66
|
+
Field,
|
|
67
|
+
FieldSet,
|
|
68
|
+
FieldLegend,
|
|
69
|
+
FieldGroup,
|
|
70
|
+
FieldContent,
|
|
71
|
+
FieldLabel,
|
|
72
|
+
FieldTitle,
|
|
73
|
+
FieldDescription,
|
|
74
|
+
FieldSeparator,
|
|
75
|
+
FieldError,
|
|
76
|
+
type FieldProps,
|
|
77
|
+
type FieldSetProps,
|
|
78
|
+
type FieldLegendProps,
|
|
79
|
+
type FieldGroupProps,
|
|
80
|
+
type FieldLabelProps,
|
|
81
|
+
type FieldSeparatorProps,
|
|
82
|
+
type FieldErrorProps,
|
|
83
|
+
type FieldOrientation,
|
|
84
|
+
} from './ui/Field'
|
|
85
|
+
export {
|
|
86
|
+
InputGroup,
|
|
87
|
+
InputGroupAddon,
|
|
88
|
+
InputGroupText,
|
|
89
|
+
InputGroupButton,
|
|
90
|
+
InputGroupInput,
|
|
91
|
+
InputGroupTextarea,
|
|
92
|
+
type InputGroupProps,
|
|
93
|
+
type InputGroupAddonProps,
|
|
94
|
+
type InputGroupAddonAlign,
|
|
95
|
+
type InputGroupButtonProps,
|
|
96
|
+
type InputGroupButtonSize,
|
|
97
|
+
type InputGroupInputProps,
|
|
98
|
+
type InputGroupTextareaProps,
|
|
99
|
+
} from './ui/InputGroup'
|
|
52
100
|
export { FileUpload, type UploadFile, type FileUploadProps } from './ui/FileUpload'
|
|
53
101
|
export { MultiSelect, type MultiSelectOption, type MultiSelectProps } from './ui/MultiSelect'
|
|
54
|
-
export { Drawer } from './ui/Drawer'
|
|
102
|
+
export { Drawer, type DrawerProps, type DrawerSide } from './ui/Drawer'
|
|
55
103
|
export { Dropdown, type DropdownItem } from './ui/Dropdown'
|
|
56
104
|
export { Dropzone } from './ui/Dropzone'
|
|
57
105
|
export { Input } from './ui/Input'
|
|
106
|
+
export {
|
|
107
|
+
Questionnaire,
|
|
108
|
+
type QuestionnaireProps,
|
|
109
|
+
type QuestionnaireQuestion,
|
|
110
|
+
type QuestionnaireOption,
|
|
111
|
+
type QuestionnaireAnswers,
|
|
112
|
+
type QuestionnaireLabels,
|
|
113
|
+
} from './ui/Questionnaire'
|
|
58
114
|
export { Modal } from './ui/Modal'
|
|
59
115
|
export { PageHeader } from './ui/PageHeader'
|
|
60
116
|
export { Pagination } from './ui/Pagination'
|
|
@@ -84,6 +140,37 @@ export { Toggle } from './ui/Toggle'
|
|
|
84
140
|
export { ToggleGroup, type ToggleGroupOption } from './ui/ToggleGroup'
|
|
85
141
|
export { SegmentedControl, type SegmentedOption } from './ui/SegmentedControl'
|
|
86
142
|
export { Breadcrumb, type BreadcrumbItem } from './ui/Breadcrumb'
|
|
143
|
+
export {
|
|
144
|
+
Menubar,
|
|
145
|
+
MenubarCheckboxItem,
|
|
146
|
+
MenubarItem,
|
|
147
|
+
MenubarLabel,
|
|
148
|
+
MenubarMenu,
|
|
149
|
+
MenubarRadioGroup,
|
|
150
|
+
MenubarRadioItem,
|
|
151
|
+
MenubarSeparator,
|
|
152
|
+
MenubarSub,
|
|
153
|
+
type MenubarCheckboxItemProps,
|
|
154
|
+
type MenubarItemProps,
|
|
155
|
+
type MenubarMenuProps,
|
|
156
|
+
type MenubarProps,
|
|
157
|
+
type MenubarRadioGroupProps,
|
|
158
|
+
type MenubarRadioItemProps,
|
|
159
|
+
type MenubarSubProps,
|
|
160
|
+
} from './ui/Menubar'
|
|
161
|
+
export {
|
|
162
|
+
NavigationMenu,
|
|
163
|
+
NavigationMenuContent,
|
|
164
|
+
NavigationMenuItem,
|
|
165
|
+
NavigationMenuLink,
|
|
166
|
+
NavigationMenuPanelLink,
|
|
167
|
+
NavigationMenuTrigger,
|
|
168
|
+
type NavigationMenuContentProps,
|
|
169
|
+
type NavigationMenuLinkProps,
|
|
170
|
+
type NavigationMenuMobileSection,
|
|
171
|
+
type NavigationMenuPanelLinkProps,
|
|
172
|
+
type NavigationMenuProps,
|
|
173
|
+
} from './ui/NavigationMenu'
|
|
87
174
|
export { AlertDialog } from './ui/AlertDialog'
|
|
88
175
|
export { ContextMenu, type ContextMenuItem } from './ui/ContextMenu'
|
|
89
176
|
export { Combobox } from './ui/Combobox'
|
|
@@ -92,6 +179,33 @@ export { Stepper, type StepItem, type StepperProps } from './ui/Stepper'
|
|
|
92
179
|
export { Timeline, type TimelineItem, type TimelineTone, type TimelineProps } from './ui/Timeline'
|
|
93
180
|
export { Rating, type RatingProps } from './ui/Rating'
|
|
94
181
|
export { PricingTable, type PricingTier, type PricingPeriod } from './ui/PricingTable'
|
|
182
|
+
// Page sections — data-driven marketing blocks.
|
|
183
|
+
export type { SectionLink } from './ui/internal/sectionKit'
|
|
184
|
+
export { SiteNav, type SiteNavProps } from './ui/SiteNav'
|
|
185
|
+
export { Hero, type HeroProps, type HeroLayout, type HeroBackground } from './ui/Hero'
|
|
186
|
+
export { FeatureGrid, type FeatureGridProps, type FeatureItem } from './ui/FeatureGrid'
|
|
187
|
+
export {
|
|
188
|
+
Testimonials,
|
|
189
|
+
type TestimonialsProps,
|
|
190
|
+
type TestimonialItem,
|
|
191
|
+
type TestimonialsLayout,
|
|
192
|
+
} from './ui/Testimonials'
|
|
193
|
+
export { Faq, type FaqProps, type FaqItem } from './ui/Faq'
|
|
194
|
+
export { CtaBanner, type CtaBannerProps } from './ui/CtaBanner'
|
|
195
|
+
export {
|
|
196
|
+
ContactSection,
|
|
197
|
+
type ContactSectionProps,
|
|
198
|
+
type ContactField,
|
|
199
|
+
type ContactValues,
|
|
200
|
+
} from './ui/ContactSection'
|
|
201
|
+
export {
|
|
202
|
+
SiteFooter,
|
|
203
|
+
type SiteFooterProps,
|
|
204
|
+
type FooterColumn,
|
|
205
|
+
type SocialLink,
|
|
206
|
+
type SocialNetwork,
|
|
207
|
+
} from './ui/SiteFooter'
|
|
208
|
+
export { TeamGrid, type TeamGridProps, type TeamMember } from './ui/TeamGrid'
|
|
95
209
|
export { BeforeAfter, type BeforeAfterProps } from './ui/BeforeAfter'
|
|
96
210
|
export {
|
|
97
211
|
Configurator,
|
|
@@ -124,6 +238,23 @@ export {
|
|
|
124
238
|
export { Anchor, type AnchorItem, type AnchorProps } from './ui/Anchor'
|
|
125
239
|
export { Highlight, type HighlightProps } from './ui/Highlight'
|
|
126
240
|
export { List, type ListItem } from './ui/List'
|
|
241
|
+
export {
|
|
242
|
+
Item,
|
|
243
|
+
ItemActions,
|
|
244
|
+
ItemContent,
|
|
245
|
+
ItemDescription,
|
|
246
|
+
ItemFooter,
|
|
247
|
+
ItemGroup,
|
|
248
|
+
ItemHeader,
|
|
249
|
+
ItemMedia,
|
|
250
|
+
ItemSeparator,
|
|
251
|
+
ItemTitle,
|
|
252
|
+
type ItemMediaVariant,
|
|
253
|
+
type ItemProps,
|
|
254
|
+
type ItemSize,
|
|
255
|
+
type ItemVariant,
|
|
256
|
+
} from './ui/Item'
|
|
257
|
+
export { ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps } from './ui/ScrollArea'
|
|
127
258
|
export { Countdown, type CountdownProps } from './ui/Countdown'
|
|
128
259
|
export { Affix } from './ui/Affix'
|
|
129
260
|
export { Transfer, type TransferItem, type TransferProps } from './ui/Transfer'
|
|
@@ -151,7 +282,31 @@ export { Clock, type ClockProps } from './ui/Clock'
|
|
|
151
282
|
|
|
152
283
|
// Conversation / AI, transitions, and depth surfaces (roadmap phase 1).
|
|
153
284
|
export { ChatThread, type ChatThreadProps } from './ui/ChatThread'
|
|
154
|
-
export {
|
|
285
|
+
export {
|
|
286
|
+
Message,
|
|
287
|
+
MessageGroup,
|
|
288
|
+
type MessageProps,
|
|
289
|
+
type MessageGroupProps,
|
|
290
|
+
type MessageVariant,
|
|
291
|
+
type MessageAlign,
|
|
292
|
+
type ChatRole,
|
|
293
|
+
} from './ui/Message'
|
|
294
|
+
export { type ChatThreadApi } from './ui/ChatThread'
|
|
295
|
+
export { ChatMarker, type ChatMarkerProps, type ChatMarkerVariant } from './ui/ChatMarker'
|
|
296
|
+
export {
|
|
297
|
+
Attachment,
|
|
298
|
+
AttachmentMedia,
|
|
299
|
+
AttachmentContent,
|
|
300
|
+
AttachmentTitle,
|
|
301
|
+
AttachmentDescription,
|
|
302
|
+
AttachmentActions,
|
|
303
|
+
AttachmentAction,
|
|
304
|
+
AttachmentGroup,
|
|
305
|
+
type AttachmentProps,
|
|
306
|
+
type AttachmentActionProps,
|
|
307
|
+
type AttachmentState,
|
|
308
|
+
type AttachmentOrientation,
|
|
309
|
+
} from './ui/Attachment'
|
|
155
310
|
export { StreamingText, type StreamingTextProps } from './ui/StreamingText'
|
|
156
311
|
export { Citation, type CitationProps, SourceList, type SourceListProps } from './ui/Citation'
|
|
157
312
|
export { PromptComposer, type PromptComposerProps } from './ui/PromptComposer'
|
|
@@ -330,8 +485,42 @@ export { flyToCart, type FlyToCartOptions } from './lib/flyToCart'
|
|
|
330
485
|
// Layout (src/layout) — generic shell + backdrop + toggles. App-coupled pieces
|
|
331
486
|
// (Sidebar, Topbar, CompanySwitcher, DemoBanner, CommandPalette) stay in the app.
|
|
332
487
|
export { AppShell } from './layout/AppShell'
|
|
488
|
+
export {
|
|
489
|
+
Sidebar,
|
|
490
|
+
SidebarProvider,
|
|
491
|
+
SidebarTrigger,
|
|
492
|
+
SidebarRail,
|
|
493
|
+
SidebarInset,
|
|
494
|
+
SidebarHeader,
|
|
495
|
+
SidebarFooter,
|
|
496
|
+
SidebarContent,
|
|
497
|
+
SidebarGroup,
|
|
498
|
+
SidebarGroupLabel,
|
|
499
|
+
SidebarGroupAction,
|
|
500
|
+
SidebarGroupContent,
|
|
501
|
+
SidebarMenu,
|
|
502
|
+
SidebarMenuItem,
|
|
503
|
+
SidebarMenuButton,
|
|
504
|
+
SidebarMenuAction,
|
|
505
|
+
SidebarMenuBadge,
|
|
506
|
+
SidebarMenuSub,
|
|
507
|
+
SidebarMenuSubItem,
|
|
508
|
+
SidebarMenuSubButton,
|
|
509
|
+
SidebarMenuSkeleton,
|
|
510
|
+
useSidebar,
|
|
511
|
+
type SidebarProps,
|
|
512
|
+
type SidebarProviderProps,
|
|
513
|
+
type SidebarTriggerProps,
|
|
514
|
+
type SidebarGroupActionProps,
|
|
515
|
+
type SidebarMenuButtonProps,
|
|
516
|
+
type SidebarMenuSubProps,
|
|
517
|
+
type SidebarSide,
|
|
518
|
+
type SidebarVariant,
|
|
519
|
+
type SidebarCollapsible,
|
|
520
|
+
} from './ui/Sidebar'
|
|
333
521
|
export { SpaceBackground } from './layout/SpaceBackground'
|
|
334
522
|
export { Aurora, type AuroraProps } from './layout/Aurora'
|
|
523
|
+
export type { ModalProps } from './ui/Modal'
|
|
335
524
|
export { ThemedScenery, type ThemedSceneryProps } from './layout/ThemedScenery'
|
|
336
525
|
export { SnowScenery } from './layout/SnowScenery'
|
|
337
526
|
export { ChristmasBackground } from './layout/ChristmasBackground'
|
package/src/layout/Aurora.tsx
CHANGED
|
@@ -30,9 +30,32 @@ export interface AuroraProps {
|
|
|
30
30
|
* @default 'blobs'
|
|
31
31
|
*/
|
|
32
32
|
variant?: 'blobs' | 'mesh'
|
|
33
|
+
/**
|
|
34
|
+
* Blob palette. Each entry is a CSS color (`'#ff6a3d'`, `'oklch(0.7 0.2 40)'`,
|
|
35
|
+
* `'hsl(var(--brand))'`) or a theme token written as a custom property name
|
|
36
|
+
* (`'--primary'`, `'--accent'`, `'--success'` ...). Entries are assigned to the
|
|
37
|
+
* blobs / mesh points in order and repeat when there are fewer colors than
|
|
38
|
+
* blobs. Defaults to the theme tokens `--primary` / `--accent`, so every theme
|
|
39
|
+
* still tints it for free.
|
|
40
|
+
*/
|
|
41
|
+
colors?: string[]
|
|
42
|
+
/**
|
|
43
|
+
* Color of the cursor-following glow, same format as a `colors` entry.
|
|
44
|
+
* @default '--primary'
|
|
45
|
+
*/
|
|
46
|
+
glowColor?: string
|
|
47
|
+
/** Peak opacity of the cursor glow, 0..1. @default 0.1 */
|
|
48
|
+
glowOpacity?: number
|
|
33
49
|
class?: string
|
|
34
50
|
}
|
|
35
51
|
|
|
52
|
+
/** Resolve a palette entry to a CSS color at the given alpha. `--token` => theme token. */
|
|
53
|
+
function colorAt(color: string, alpha: number): string {
|
|
54
|
+
const a = Math.min(1, Math.max(0, alpha))
|
|
55
|
+
if (color.startsWith('--')) return `hsl(var(${color}) / ${a.toFixed(2)})`
|
|
56
|
+
return `color-mix(in srgb, ${color} ${(a * 100).toFixed(0)}%, transparent)`
|
|
57
|
+
}
|
|
58
|
+
|
|
36
59
|
// Mesh variant — several token-tinted radial gradients composited into one
|
|
37
60
|
// backdrop (positions chosen to fill the corners without a hard seam).
|
|
38
61
|
const MESH_STOPS = [
|
|
@@ -68,10 +91,16 @@ const BLOBS = [
|
|
|
68
91
|
*/
|
|
69
92
|
export function Aurora(props: AuroraProps): JSX.Element {
|
|
70
93
|
const intensity = (): number => props.intensity ?? 0.45
|
|
94
|
+
// Palette entry for blob/mesh index `i`: custom colors cycle; otherwise the
|
|
95
|
+
// built-in token for that slot.
|
|
96
|
+
const colorFor = (i: number, token: string): string => {
|
|
97
|
+
const list = props.colors
|
|
98
|
+
return list && list.length > 0 ? list[i % list.length] : token
|
|
99
|
+
}
|
|
71
100
|
const meshBg = (): string =>
|
|
72
101
|
MESH_STOPS.map(
|
|
73
|
-
(s) =>
|
|
74
|
-
`radial-gradient(45% 45% at ${s.at},
|
|
102
|
+
(s, i) =>
|
|
103
|
+
`radial-gradient(45% 45% at ${s.at}, ${colorAt(colorFor(i, s.token), intensity() * s.a)}, transparent 70%)`,
|
|
75
104
|
).join(', ')
|
|
76
105
|
let root: HTMLDivElement | undefined
|
|
77
106
|
|
|
@@ -93,11 +122,11 @@ export function Aurora(props: AuroraProps): JSX.Element {
|
|
|
93
122
|
when={props.variant === 'mesh'}
|
|
94
123
|
fallback={
|
|
95
124
|
<For each={BLOBS}>
|
|
96
|
-
{(b) => (
|
|
125
|
+
{(b, i) => (
|
|
97
126
|
<div
|
|
98
127
|
class={cn('absolute rounded-full blur-3xl', b.pos, b.size, props.animated && 'aurora-drift')}
|
|
99
128
|
style={{
|
|
100
|
-
background: `radial-gradient(circle,
|
|
129
|
+
background: `radial-gradient(circle, ${colorAt(colorFor(i(), b.token), intensity() * b.a)}, transparent 70%)`,
|
|
101
130
|
}}
|
|
102
131
|
/>
|
|
103
132
|
)}
|
|
@@ -110,12 +139,12 @@ export function Aurora(props: AuroraProps): JSX.Element {
|
|
|
110
139
|
/>
|
|
111
140
|
</Show>
|
|
112
141
|
{/* Soft glow that follows the cursor across the backdrop (positioned by
|
|
113
|
-
bindPointerFx via
|
|
142
|
+
bindPointerFx via transform, coalesced per frame). */}
|
|
114
143
|
<div
|
|
115
144
|
id="cursorGlow"
|
|
116
|
-
class="absolute h-[520px] w-[520px] rounded-full opacity-0 transition-opacity duration-300"
|
|
145
|
+
class="absolute h-[520px] w-[520px] rounded-full opacity-0 transition-opacity duration-300 will-change-transform"
|
|
117
146
|
style={{
|
|
118
|
-
background:
|
|
147
|
+
background: `radial-gradient(circle, ${colorAt(props.glowColor ?? '--primary', props.glowOpacity ?? 0.1)}, transparent 70%)`,
|
|
119
148
|
transform: 'translate(-50%,-50%)',
|
|
120
149
|
}}
|
|
121
150
|
/>
|
|
@@ -8,11 +8,29 @@
|
|
|
8
8
|
export function bindPointerFx(root: HTMLElement): () => void {
|
|
9
9
|
const glow = root.querySelector<HTMLElement>('#cursorGlow')
|
|
10
10
|
|
|
11
|
+
// The glow is moved with `transform` only (compositor-friendly: no layout, and
|
|
12
|
+
// no repaint of backdrop-filter surfaces in front of it). It is pinned at 0,0
|
|
13
|
+
// once; pointermoves are coalesced to one write per animation frame.
|
|
14
|
+
let frame = 0
|
|
15
|
+
let gx = 0
|
|
16
|
+
let gy = 0
|
|
17
|
+
if (glow) {
|
|
18
|
+
glow.style.left = '0px'
|
|
19
|
+
glow.style.top = '0px'
|
|
20
|
+
glow.style.willChange = 'transform'
|
|
21
|
+
}
|
|
22
|
+
const paintGlow = (): void => {
|
|
23
|
+
frame = 0
|
|
24
|
+
if (!glow) return
|
|
25
|
+
glow.style.transform = `translate3d(${gx}px, ${gy}px, 0) translate(-50%, -50%)`
|
|
26
|
+
}
|
|
27
|
+
|
|
11
28
|
const onMove = (e: PointerEvent): void => {
|
|
12
29
|
if (glow) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
glow.style.
|
|
30
|
+
gx = e.clientX
|
|
31
|
+
gy = e.clientY
|
|
32
|
+
if (glow.style.opacity !== '1') glow.style.opacity = '1'
|
|
33
|
+
if (!frame) frame = requestAnimationFrame(paintGlow)
|
|
16
34
|
}
|
|
17
35
|
const el = e.target as Element | null
|
|
18
36
|
const magnetic = el?.closest?.('.magnetic') as HTMLElement | null
|
|
@@ -42,6 +60,7 @@ export function bindPointerFx(root: HTMLElement): () => void {
|
|
|
42
60
|
document.addEventListener('pointerout', onOut as EventListener)
|
|
43
61
|
|
|
44
62
|
return () => {
|
|
63
|
+
if (frame) cancelAnimationFrame(frame)
|
|
45
64
|
document.removeEventListener('pointermove', onMove as EventListener)
|
|
46
65
|
window.removeEventListener('pointerleave', onLeave)
|
|
47
66
|
document.removeEventListener('pointerout', onOut as EventListener)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Keeping a portaled popover glued to the trigger it belongs to.
|
|
2
|
+
//
|
|
3
|
+
// DateField, TimeField, DateRangePicker and MultiSelect all render their panel
|
|
4
|
+
// in a <Portal> so it escapes the stacking context of whatever card it sits in.
|
|
5
|
+
// At <body> it cannot be positioned relative to the trigger, so each one places
|
|
6
|
+
// it with `position: fixed` at the trigger's viewport rect — which is correct
|
|
7
|
+
// until the page scrolls and the rect goes stale.
|
|
8
|
+
//
|
|
9
|
+
// All four handled that by CLOSING on any captured scroll, and that made them
|
|
10
|
+
// unusable on a scrolling page: clicking a trigger below the fold scrolls it
|
|
11
|
+
// into view, the scroll event arrives right after the panel opens, and the
|
|
12
|
+
// panel shuts in the same gesture. Reported as a date picker that simply did
|
|
13
|
+
// not respond to clicks; measured in a browser as open-then-closed within
|
|
14
|
+
// 300 ms, with `mousedown` followed by `scroll:document`.
|
|
15
|
+
//
|
|
16
|
+
// A fixed panel does not need to disappear when the page moves — it needs to
|
|
17
|
+
// move with it. This re-places it, and closes only when the trigger has
|
|
18
|
+
// actually left the viewport, where following it would leave a panel hovering
|
|
19
|
+
// over unrelated content.
|
|
20
|
+
|
|
21
|
+
/** Vertical slack, in px, before a trigger counts as gone. Half a row: enough
|
|
22
|
+
* not to close while a sticky header slides over the trigger's top edge. */
|
|
23
|
+
const OFF_SCREEN_SLACK = 16
|
|
24
|
+
|
|
25
|
+
export interface AnchorReflowOptions {
|
|
26
|
+
/** The trigger the panel is anchored to. */
|
|
27
|
+
anchor: () => HTMLElement | undefined
|
|
28
|
+
/** Re-reads the anchor's rect and repositions the panel. */
|
|
29
|
+
place: () => void
|
|
30
|
+
/** Called only when the anchor is no longer on screen. */
|
|
31
|
+
close: () => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The scroll/resize handler an anchored popover should register, instead of
|
|
36
|
+
* `close`.
|
|
37
|
+
*
|
|
38
|
+
* Registering it for `scroll` still needs capture (`true`): the scroll may come
|
|
39
|
+
* from any ancestor container, and scroll events do not bubble to `window`.
|
|
40
|
+
*/
|
|
41
|
+
export function createAnchorReflow(options: AnchorReflowOptions): () => void {
|
|
42
|
+
return () => {
|
|
43
|
+
const el = options.anchor()
|
|
44
|
+
if (!el) {
|
|
45
|
+
options.close()
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
const rect = el.getBoundingClientRect()
|
|
49
|
+
const gone =
|
|
50
|
+
rect.bottom < -OFF_SCREEN_SLACK ||
|
|
51
|
+
rect.top > window.innerHeight + OFF_SCREEN_SLACK ||
|
|
52
|
+
rect.right < 0 ||
|
|
53
|
+
rect.left > window.innerWidth
|
|
54
|
+
if (gone) options.close()
|
|
55
|
+
else options.place()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { splitProps, type JSX, type ParentProps } from 'solid-js'
|
|
2
|
+
|
|
3
|
+
import { cn } from '../lib/cn'
|
|
4
|
+
|
|
5
|
+
interface AspectRatioProps extends ParentProps {
|
|
6
|
+
/** Width divided by height, e.g. `16 / 9`. @default 1 */
|
|
7
|
+
ratio?: number
|
|
8
|
+
class?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type { AspectRatioProps }
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Box that keeps a fixed width:height ratio (CSS `aspect-ratio`). Children fill
|
|
15
|
+
* it; give images/video `object-cover`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <AspectRatio ratio={16 / 9} class="w-full overflow-hidden rounded-lg">
|
|
20
|
+
* <img src="/cover.jpg" alt="" class="object-cover" />
|
|
21
|
+
* </AspectRatio>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function AspectRatio(props: AspectRatioProps): JSX.Element {
|
|
25
|
+
const [local, rest] = splitProps(props, ['ratio', 'class', 'children'])
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
class={cn('relative w-full [&>*]:h-full [&>*]:w-full', local.class)}
|
|
29
|
+
style={{ 'aspect-ratio': String(local.ratio ?? 1) }}
|
|
30
|
+
{...rest}
|
|
31
|
+
>
|
|
32
|
+
{local.children}
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Attachment — a file/image chip for chat UIs, built on the `Item` row primitive.
|
|
2
|
+
// Adds an upload lifecycle (idle / uploading / processing / error / done), a
|
|
3
|
+
// progress bar and a horizontally scrolling group.
|
|
4
|
+
import { splitProps, type JSX, type ParentProps, Show } from 'solid-js'
|
|
5
|
+
|
|
6
|
+
import { cn } from '../lib/cn'
|
|
7
|
+
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle, type ItemSize } from './Item'
|
|
8
|
+
import { ScrollArea } from './ScrollArea'
|
|
9
|
+
|
|
10
|
+
export type AttachmentState = 'idle' | 'uploading' | 'processing' | 'error' | 'done'
|
|
11
|
+
export type AttachmentOrientation = 'horizontal' | 'vertical'
|
|
12
|
+
|
|
13
|
+
interface AttachmentProps extends ParentProps {
|
|
14
|
+
/** Lifecycle state. `uploading`/`processing` set `aria-busy`; `error` uses destructive tokens. @default 'idle' */
|
|
15
|
+
state?: AttachmentState
|
|
16
|
+
/** Density, forwarded to `Item`. @default 'default' */
|
|
17
|
+
size?: ItemSize
|
|
18
|
+
/** Lay media and text side by side, or stacked. @default 'horizontal' */
|
|
19
|
+
orientation?: AttachmentOrientation
|
|
20
|
+
/**
|
|
21
|
+
* Upload progress, 0–100. Only used while `state="uploading"`; when omitted
|
|
22
|
+
* the bar is indeterminate (no `aria-valuenow`).
|
|
23
|
+
*/
|
|
24
|
+
progress?: number
|
|
25
|
+
/** Accessible name of the progress bar. @default 'Uploading' / 'Processing' */
|
|
26
|
+
progressLabel?: string
|
|
27
|
+
class?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type { AttachmentProps }
|
|
31
|
+
|
|
32
|
+
const clamp = (n: number): number => Math.min(100, Math.max(0, n))
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A file or image attachment: `AttachmentMedia`, an `AttachmentContent` with
|
|
36
|
+
* title/description and trailing `AttachmentActions`. Group several in an
|
|
37
|
+
* `AttachmentGroup` (horizontal scroll-snap row).
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <Attachment state="uploading" progress={42}>
|
|
42
|
+
* <AttachmentMedia><FileText /></AttachmentMedia>
|
|
43
|
+
* <AttachmentContent>
|
|
44
|
+
* <AttachmentTitle>roadmap.pdf</AttachmentTitle>
|
|
45
|
+
* <AttachmentDescription>42% of 2.4 MB</AttachmentDescription>
|
|
46
|
+
* </AttachmentContent>
|
|
47
|
+
* <AttachmentActions>
|
|
48
|
+
* <AttachmentAction label="Cancel upload"><X /></AttachmentAction>
|
|
49
|
+
* </AttachmentActions>
|
|
50
|
+
* </Attachment>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function Attachment(props: AttachmentProps): JSX.Element {
|
|
54
|
+
const [local] = splitProps(props, [
|
|
55
|
+
'state',
|
|
56
|
+
'size',
|
|
57
|
+
'orientation',
|
|
58
|
+
'progress',
|
|
59
|
+
'progressLabel',
|
|
60
|
+
'class',
|
|
61
|
+
'children',
|
|
62
|
+
])
|
|
63
|
+
const state = (): AttachmentState => local.state ?? 'idle'
|
|
64
|
+
const busy = (): boolean => state() === 'uploading' || state() === 'processing'
|
|
65
|
+
const determinate = (): boolean => state() === 'uploading' && local.progress !== undefined
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
data-slot="attachment"
|
|
69
|
+
data-state={state()}
|
|
70
|
+
data-orientation={local.orientation ?? 'horizontal'}
|
|
71
|
+
aria-busy={busy() ? 'true' : undefined}
|
|
72
|
+
class={cn(
|
|
73
|
+
'relative w-full overflow-hidden rounded-lg border bg-card/60 text-card-foreground',
|
|
74
|
+
state() === 'error'
|
|
75
|
+
? 'border-destructive/50 bg-destructive/10 text-destructive [&_[data-slot=item-description]]:text-destructive/80'
|
|
76
|
+
: 'border-border',
|
|
77
|
+
local.class,
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<Item
|
|
81
|
+
size={local.size}
|
|
82
|
+
class={cn(
|
|
83
|
+
'flex-nowrap border-0',
|
|
84
|
+
local.orientation === 'vertical' && 'flex-col items-start',
|
|
85
|
+
state() === 'processing' && 'animate-pulse',
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
{local.children}
|
|
89
|
+
</Item>
|
|
90
|
+
<Show when={busy()}>
|
|
91
|
+
<div
|
|
92
|
+
role="progressbar"
|
|
93
|
+
aria-label={local.progressLabel ?? (state() === 'uploading' ? 'Uploading' : 'Processing')}
|
|
94
|
+
aria-valuemin={0}
|
|
95
|
+
aria-valuemax={100}
|
|
96
|
+
aria-valuenow={determinate() ? Math.round(clamp(local.progress ?? 0)) : undefined}
|
|
97
|
+
class="absolute inset-x-0 bottom-0 h-1 bg-muted"
|
|
98
|
+
>
|
|
99
|
+
<div
|
|
100
|
+
class={cn(
|
|
101
|
+
'h-full bg-primary transition-[width] duration-200',
|
|
102
|
+
!determinate() && 'w-1/3 animate-pulse',
|
|
103
|
+
)}
|
|
104
|
+
style={determinate() ? { width: `${clamp(local.progress ?? 0)}%` } : undefined}
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
</Show>
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface SlotProps extends ParentProps {
|
|
113
|
+
class?: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Leading visual: an icon tile (`icon`, default) or an image thumbnail (`image`). */
|
|
117
|
+
export function AttachmentMedia(props: SlotProps & { variant?: 'icon' | 'image' }): JSX.Element {
|
|
118
|
+
return (
|
|
119
|
+
<ItemMedia variant={props.variant ?? 'icon'} class={props.class}>
|
|
120
|
+
{props.children}
|
|
121
|
+
</ItemMedia>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The text column. */
|
|
126
|
+
export function AttachmentContent(props: SlotProps): JSX.Element {
|
|
127
|
+
return <ItemContent class={props.class}>{props.children}</ItemContent>
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** File name; truncates on one line. */
|
|
131
|
+
export function AttachmentTitle(props: SlotProps): JSX.Element {
|
|
132
|
+
return <ItemTitle class={cn('block truncate', props.class)}>{props.children}</ItemTitle>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Secondary line: size, status or error message. */
|
|
136
|
+
export function AttachmentDescription(props: SlotProps): JSX.Element {
|
|
137
|
+
return <ItemDescription class={cn('line-clamp-1 text-xs', props.class)}>{props.children}</ItemDescription>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Trailing controls. */
|
|
141
|
+
export function AttachmentActions(props: SlotProps): JSX.Element {
|
|
142
|
+
return <ItemActions class={cn('gap-1', props.class)}>{props.children}</ItemActions>
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface AttachmentActionProps extends ParentProps {
|
|
146
|
+
/** Accessible name (required: the button only holds an icon). */
|
|
147
|
+
label: string
|
|
148
|
+
onClick?: (event: MouseEvent) => void
|
|
149
|
+
disabled?: boolean
|
|
150
|
+
class?: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type { AttachmentActionProps }
|
|
154
|
+
|
|
155
|
+
/** Icon button inside `AttachmentActions`. */
|
|
156
|
+
export function AttachmentAction(props: AttachmentActionProps): JSX.Element {
|
|
157
|
+
return (
|
|
158
|
+
<button
|
|
159
|
+
type="button"
|
|
160
|
+
aria-label={props.label}
|
|
161
|
+
disabled={props.disabled}
|
|
162
|
+
onClick={(e) => props.onClick?.(e)}
|
|
163
|
+
class={cn(
|
|
164
|
+
'grid h-7 w-7 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&>svg]:h-4 [&>svg]:w-4',
|
|
165
|
+
props.class,
|
|
166
|
+
)}
|
|
167
|
+
>
|
|
168
|
+
{props.children}
|
|
169
|
+
</button>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Horizontal scroll-snap row of attachments. */
|
|
174
|
+
export function AttachmentGroup(props: SlotProps & { label?: string }): JSX.Element {
|
|
175
|
+
return (
|
|
176
|
+
<ScrollArea
|
|
177
|
+
orientation="horizontal"
|
|
178
|
+
label={props.label ?? 'Attachments'}
|
|
179
|
+
class={cn('w-full snap-x snap-mandatory rounded-lg', props.class)}
|
|
180
|
+
>
|
|
181
|
+
<div
|
|
182
|
+
data-slot="attachment-group"
|
|
183
|
+
class="flex w-max gap-2 p-1 [&>[data-slot=attachment]]:w-64 [&>[data-slot=attachment]]:shrink-0 [&>[data-slot=attachment]]:snap-start"
|
|
184
|
+
>
|
|
185
|
+
{props.children}
|
|
186
|
+
</div>
|
|
187
|
+
</ScrollArea>
|
|
188
|
+
)
|
|
189
|
+
}
|