@adres/mgda-ui 0.0.0-stage → 0.0.2
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/brand-knowledge.json +451 -0
- package/knowledge/accordion.md +135 -0
- package/knowledge/activity-rings.md +123 -0
- package/knowledge/alert.md +184 -0
- package/knowledge/answer-actions.md +217 -0
- package/knowledge/aspect-ratio.md +82 -0
- package/knowledge/avatar.md +158 -0
- package/knowledge/badge.md +172 -0
- package/knowledge/bar-graph.md +167 -0
- package/knowledge/bento-grid.md +115 -0
- package/knowledge/blockquote.md +153 -0
- package/knowledge/box-list.md +177 -0
- package/knowledge/bullet-list.md +128 -0
- package/knowledge/button-group.md +179 -0
- package/knowledge/button.md +231 -0
- package/knowledge/calendar.md +168 -0
- package/knowledge/card.md +321 -0
- package/knowledge/cards-nav.md +170 -0
- package/knowledge/chain-of-thought.md +164 -0
- package/knowledge/chat-container.md +135 -0
- package/knowledge/chat-message.md +224 -0
- package/knowledge/checkbox-card.md +172 -0
- package/knowledge/checkbox.md +111 -0
- package/knowledge/citations.md +241 -0
- package/knowledge/combobox.md +265 -0
- package/knowledge/countdown-pill.md +153 -0
- package/knowledge/cta.md +118 -0
- package/knowledge/data-list.md +173 -0
- package/knowledge/data-table.md +220 -0
- package/knowledge/dialog.md +249 -0
- package/knowledge/drawer.md +236 -0
- package/knowledge/dropdown-menu.md +238 -0
- package/knowledge/dropzone.md +153 -0
- package/knowledge/email-chips-input.md +161 -0
- package/knowledge/empty-state.md +106 -0
- package/knowledge/entity-header.md +166 -0
- package/knowledge/feature-grid.md +120 -0
- package/knowledge/feedback-bar.md +127 -0
- package/knowledge/footer.md +116 -0
- package/knowledge/heading.md +189 -0
- package/knowledge/hero.md +361 -0
- package/knowledge/input-button.md +187 -0
- package/knowledge/input-group.md +185 -0
- package/knowledge/input.md +256 -0
- package/knowledge/inset.md +177 -0
- package/knowledge/kpi-card.md +206 -0
- package/knowledge/list-nav.md +181 -0
- package/knowledge/logo-cloud.md +102 -0
- package/knowledge/media-gallery.md +158 -0
- package/knowledge/message-actions.md +175 -0
- package/knowledge/nav-sidebar.md +225 -0
- package/knowledge/number-stepper.md +159 -0
- package/knowledge/page-header.md +109 -0
- package/knowledge/pagination.md +225 -0
- package/knowledge/popover.md +159 -0
- package/knowledge/pricing.md +132 -0
- package/knowledge/progress.md +156 -0
- package/knowledge/prompt-input.md +337 -0
- package/knowledge/prompt-suggestions.md +168 -0
- package/knowledge/quota-bar.md +160 -0
- package/knowledge/quote.md +116 -0
- package/knowledge/radio-card.md +172 -0
- package/knowledge/radio-group.md +135 -0
- package/knowledge/reasoning.md +142 -0
- package/knowledge/search.md +131 -0
- package/knowledge/section-nav.md +162 -0
- package/knowledge/segmented-control.md +181 -0
- package/knowledge/select-button.md +205 -0
- package/knowledge/select.md +210 -0
- package/knowledge/separator.md +75 -0
- package/knowledge/shimmer-text.md +140 -0
- package/knowledge/sidebar.md +227 -0
- package/knowledge/skeleton.md +142 -0
- package/knowledge/slider.md +133 -0
- package/knowledge/slideshow.md +159 -0
- package/knowledge/source.md +156 -0
- package/knowledge/spinner.md +120 -0
- package/knowledge/stat-strip.md +128 -0
- package/knowledge/stats.md +117 -0
- package/knowledge/step-indicator.md +157 -0
- package/knowledge/switch.md +88 -0
- package/knowledge/table.md +190 -0
- package/knowledge/tabs.md +169 -0
- package/knowledge/testimonials.md +115 -0
- package/knowledge/text-link.md +192 -0
- package/knowledge/text.md +194 -0
- package/knowledge/thinking-bar.md +115 -0
- package/knowledge/thinking-steps.md +140 -0
- package/knowledge/toaster.md +127 -0
- package/knowledge/toggle.md +113 -0
- package/knowledge/tool-call-card.md +241 -0
- package/knowledge/tooltip.md +120 -0
- package/knowledge/top-nav.md +138 -0
- package/knowledge.json +19854 -0
- package/package.json +94 -4
- package/src/components/accordion.stories.tsx +46 -0
- package/src/components/accordion.tsx +74 -0
- package/src/components/activity-rings.stories.tsx +73 -0
- package/src/components/activity-rings.tsx +121 -0
- package/src/components/alert.stories.tsx +43 -0
- package/src/components/alert.tsx +132 -0
- package/src/components/answer-actions.stories.tsx +54 -0
- package/src/components/answer-actions.test.tsx +37 -0
- package/src/components/answer-actions.tsx +313 -0
- package/src/components/aspect-ratio.stories.tsx +32 -0
- package/src/components/aspect-ratio.tsx +5 -0
- package/src/components/avatar.stories.tsx +40 -0
- package/src/components/avatar.tsx +154 -0
- package/src/components/badge.stories.tsx +27 -0
- package/src/components/badge.tsx +63 -0
- package/src/components/bar-graph.stories.tsx +28 -0
- package/src/components/bar-graph.tsx +204 -0
- package/src/components/bento-grid.stories.tsx +23 -0
- package/src/components/bento-grid.tsx +67 -0
- package/src/components/blockquote.stories.tsx +28 -0
- package/src/components/blockquote.tsx +88 -0
- package/src/components/box-list.stories.tsx +25 -0
- package/src/components/box-list.tsx +108 -0
- package/src/components/bullet-list.stories.tsx +22 -0
- package/src/components/bullet-list.tsx +101 -0
- package/src/components/button-group.stories.tsx +31 -0
- package/src/components/button-group.tsx +156 -0
- package/src/components/button.stories.tsx +66 -0
- package/src/components/button.test.ts +17 -0
- package/src/components/button.tsx +139 -0
- package/src/components/calendar.stories.tsx +25 -0
- package/src/components/calendar.tsx +367 -0
- package/src/components/card.stories.tsx +44 -0
- package/src/components/card.tsx +392 -0
- package/src/components/cards-nav.stories.tsx +35 -0
- package/src/components/cards-nav.tsx +115 -0
- package/src/components/chain-of-thought.stories.tsx +67 -0
- package/src/components/chain-of-thought.test.tsx +50 -0
- package/src/components/chain-of-thought.tsx +170 -0
- package/src/components/chat-container.stories.tsx +47 -0
- package/src/components/chat-container.test.tsx +49 -0
- package/src/components/chat-container.tsx +222 -0
- package/src/components/chat-message.stories.tsx +52 -0
- package/src/components/chat-message.test.tsx +36 -0
- package/src/components/chat-message.tsx +163 -0
- package/src/components/checkbox-card.stories.tsx +27 -0
- package/src/components/checkbox-card.tsx +200 -0
- package/src/components/checkbox.stories.tsx +26 -0
- package/src/components/checkbox.tsx +61 -0
- package/src/components/citations.stories.tsx +64 -0
- package/src/components/citations.test.tsx +61 -0
- package/src/components/citations.tsx +220 -0
- package/src/components/combobox.stories.tsx +184 -0
- package/src/components/combobox.test.tsx +82 -0
- package/src/components/combobox.tsx +407 -0
- package/src/components/countdown-pill.stories.tsx +24 -0
- package/src/components/countdown-pill.tsx +129 -0
- package/src/components/cta.stories.tsx +29 -0
- package/src/components/cta.tsx +52 -0
- package/src/components/data-list.stories.tsx +50 -0
- package/src/components/data-list.test.tsx +35 -0
- package/src/components/data-list.tsx +181 -0
- package/src/components/data-table.stories.tsx +29 -0
- package/src/components/data-table.tsx +200 -0
- package/src/components/dialog.stories.tsx +42 -0
- package/src/components/dialog.tsx +200 -0
- package/src/components/drawer.stories.tsx +48 -0
- package/src/components/drawer.tsx +385 -0
- package/src/components/dropdown-menu.stories.tsx +41 -0
- package/src/components/dropdown-menu.tsx +252 -0
- package/src/components/dropzone.stories.tsx +19 -0
- package/src/components/dropzone.tsx +250 -0
- package/src/components/email-chips-input.stories.tsx +135 -0
- package/src/components/email-chips-input.tsx +210 -0
- package/src/components/empty-state.stories.tsx +22 -0
- package/src/components/empty-state.tsx +35 -0
- package/src/components/entity-header.stories.tsx +43 -0
- package/src/components/entity-header.test.tsx +35 -0
- package/src/components/entity-header.tsx +92 -0
- package/src/components/feature-grid.stories.tsx +25 -0
- package/src/components/feature-grid.tsx +87 -0
- package/src/components/feedback-bar.stories.tsx +25 -0
- package/src/components/feedback-bar.test.tsx +24 -0
- package/src/components/feedback-bar.tsx +62 -0
- package/src/components/footer.stories.tsx +23 -0
- package/src/components/footer.tsx +78 -0
- package/src/components/heading.stories.tsx +45 -0
- package/src/components/heading.test.tsx +22 -0
- package/src/components/heading.tsx +77 -0
- package/src/components/hero.stories.tsx +38 -0
- package/src/components/hero.tsx +498 -0
- package/src/components/input-button.stories.tsx +19 -0
- package/src/components/input-button.tsx +112 -0
- package/src/components/input-group.stories.tsx +38 -0
- package/src/components/input-group.tsx +157 -0
- package/src/components/input.stories.tsx +31 -0
- package/src/components/input.tsx +284 -0
- package/src/components/inset.stories.tsx +29 -0
- package/src/components/inset.tsx +70 -0
- package/src/components/kpi-card.stories.tsx +29 -0
- package/src/components/kpi-card.tsx +284 -0
- package/src/components/list-nav.stories.tsx +22 -0
- package/src/components/list-nav.tsx +145 -0
- package/src/components/logo-cloud.stories.tsx +23 -0
- package/src/components/logo-cloud.tsx +42 -0
- package/src/components/media-gallery.stories.tsx +25 -0
- package/src/components/media-gallery.tsx +347 -0
- package/src/components/message-actions.stories.tsx +45 -0
- package/src/components/message-actions.test.tsx +32 -0
- package/src/components/message-actions.tsx +119 -0
- package/src/components/nav-sidebar.stories.tsx +58 -0
- package/src/components/nav-sidebar.test.tsx +54 -0
- package/src/components/nav-sidebar.tsx +462 -0
- package/src/components/number-stepper.stories.tsx +19 -0
- package/src/components/number-stepper.tsx +158 -0
- package/src/components/page-header.stories.tsx +25 -0
- package/src/components/page-header.tsx +64 -0
- package/src/components/pagination.stories.tsx +110 -0
- package/src/components/pagination.test.tsx +46 -0
- package/src/components/pagination.tsx +144 -0
- package/src/components/popover.stories.tsx +26 -0
- package/src/components/popover.tsx +62 -0
- package/src/components/pricing.stories.tsx +42 -0
- package/src/components/pricing.tsx +130 -0
- package/src/components/progress.stories.tsx +22 -0
- package/src/components/progress.tsx +189 -0
- package/src/components/prompt-input.stories.tsx +62 -0
- package/src/components/prompt-input.test.tsx +106 -0
- package/src/components/prompt-input.tsx +343 -0
- package/src/components/prompt-suggestions.stories.tsx +53 -0
- package/src/components/prompt-suggestions.test.tsx +37 -0
- package/src/components/prompt-suggestions.tsx +122 -0
- package/src/components/quota-bar.stories.tsx +19 -0
- package/src/components/quota-bar.tsx +191 -0
- package/src/components/quote.stories.tsx +18 -0
- package/src/components/quote.tsx +43 -0
- package/src/components/radio-card.stories.tsx +27 -0
- package/src/components/radio-card.tsx +225 -0
- package/src/components/radio-group.stories.tsx +32 -0
- package/src/components/radio-group.tsx +68 -0
- package/src/components/reasoning.stories.tsx +50 -0
- package/src/components/reasoning.test.tsx +48 -0
- package/src/components/reasoning.tsx +143 -0
- package/src/components/search.stories.tsx +18 -0
- package/src/components/search.tsx +133 -0
- package/src/components/section-nav.stories.tsx +59 -0
- package/src/components/section-nav.test.tsx +36 -0
- package/src/components/section-nav.tsx +146 -0
- package/src/components/segmented-control.stories.tsx +22 -0
- package/src/components/segmented-control.tsx +301 -0
- package/src/components/select-button.stories.tsx +27 -0
- package/src/components/select-button.tsx +174 -0
- package/src/components/select.stories.tsx +27 -0
- package/src/components/select.tsx +198 -0
- package/src/components/separator.stories.tsx +22 -0
- package/src/components/separator.tsx +23 -0
- package/src/components/shimmer-text.stories.tsx +23 -0
- package/src/components/shimmer-text.test.tsx +35 -0
- package/src/components/shimmer-text.tsx +43 -0
- package/src/components/sidebar.stories.tsx +25 -0
- package/src/components/sidebar.tsx +234 -0
- package/src/components/skeleton.stories.tsx +22 -0
- package/src/components/skeleton.tsx +96 -0
- package/src/components/slider.stories.tsx +20 -0
- package/src/components/slider.tsx +176 -0
- package/src/components/slideshow.stories.tsx +30 -0
- package/src/components/slideshow.tsx +291 -0
- package/src/components/source.stories.tsx +32 -0
- package/src/components/source.test.tsx +40 -0
- package/src/components/source.tsx +161 -0
- package/src/components/spinner.stories.tsx +19 -0
- package/src/components/spinner.tsx +76 -0
- package/src/components/stat-strip.stories.tsx +36 -0
- package/src/components/stat-strip.test.tsx +25 -0
- package/src/components/stat-strip.tsx +85 -0
- package/src/components/stats.stories.tsx +24 -0
- package/src/components/stats.tsx +83 -0
- package/src/components/step-indicator.stories.tsx +18 -0
- package/src/components/step-indicator.test.tsx +18 -0
- package/src/components/step-indicator.tsx +286 -0
- package/src/components/switch.stories.tsx +26 -0
- package/src/components/switch.tsx +32 -0
- package/src/components/table.stories.tsx +38 -0
- package/src/components/table.tsx +220 -0
- package/src/components/tabs.stories.tsx +25 -0
- package/src/components/tabs.tsx +276 -0
- package/src/components/testimonials.stories.tsx +22 -0
- package/src/components/testimonials.tsx +109 -0
- package/src/components/text-link.stories.tsx +47 -0
- package/src/components/text-link.test.tsx +18 -0
- package/src/components/text-link.tsx +88 -0
- package/src/components/text.stories.tsx +49 -0
- package/src/components/text.tsx +105 -0
- package/src/components/thinking-bar.stories.tsx +19 -0
- package/src/components/thinking-bar.test.tsx +27 -0
- package/src/components/thinking-bar.tsx +64 -0
- package/src/components/thinking-steps.stories.tsx +44 -0
- package/src/components/thinking-steps.test.tsx +42 -0
- package/src/components/thinking-steps.tsx +92 -0
- package/src/components/toaster.stories.tsx +80 -0
- package/src/components/toaster.tsx +36 -0
- package/src/components/toggle.stories.tsx +21 -0
- package/src/components/toggle.tsx +103 -0
- package/src/components/tool-call-card.stories.tsx +97 -0
- package/src/components/tool-call-card.test.tsx +88 -0
- package/src/components/tool-call-card.tsx +231 -0
- package/src/components/tooltip.stories.tsx +25 -0
- package/src/components/tooltip.tsx +43 -0
- package/src/components/top-nav.stories.tsx +20 -0
- package/src/components/top-nav.tsx +76 -0
- package/src/docs/Colors.stories.tsx +192 -0
- package/src/docs/GettingStarted.mdx +72 -0
- package/src/docs/Mcp.mdx +52 -0
- package/src/docs/Typography.stories.tsx +114 -0
- package/src/hooks/use-in-view.ts +32 -0
- package/src/hooks/use-reduced-motion.ts +26 -0
- package/src/index.ts +343 -0
- package/src/lib/cn.test.ts +67 -0
- package/src/lib/cn.ts +81 -0
- package/src/lib/motion.ts +109 -0
- package/src/lib/prefix-utilities.test.ts +56 -0
- package/src/lib/prefix-utilities.ts +50 -0
- package/src/styles/base.css +489 -0
- package/src/styles/motion.css +69 -0
- package/src/styles/overrides.css +44 -0
- package/src/styles/typography.css +173 -0
- package/src/theme/theme.css +391 -0
- package/src/theme/tokens.ts +29 -0
- package/src/theme/types.ts +26 -0
- package/README.md +0 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva } from "class-variance-authority"
|
|
5
|
+
import { cn } from "../lib/cn"
|
|
6
|
+
|
|
7
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export type ProgressSize = "sm" | "md" | "lg"
|
|
10
|
+
export type ProgressVariant = "default" | "success" | "warning" | "error"
|
|
11
|
+
export type ProgressShape = "bar" | "ring"
|
|
12
|
+
|
|
13
|
+
// ─── Bar-shape variants ───────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
const barTrackVariants = cva(
|
|
16
|
+
"ax:relative ax:w-full ax:overflow-hidden ax:rounded-full ax:bg-muted",
|
|
17
|
+
{
|
|
18
|
+
variants: {
|
|
19
|
+
size: {
|
|
20
|
+
sm: "ax:h-1",
|
|
21
|
+
md: "ax:h-2",
|
|
22
|
+
lg: "ax:h-4",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: { size: "md" },
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const barIndicatorVariants = cva(
|
|
30
|
+
"ax:h-full ax:w-full ax:rounded-full ax:[transition:transform_var(--motion-slow)_var(--motion-ease-out)]",
|
|
31
|
+
{
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
default: "ax:bg-primary",
|
|
35
|
+
success: "ax:bg-success",
|
|
36
|
+
warning: "ax:bg-warning",
|
|
37
|
+
error: "ax:bg-destructive",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultVariants: { variant: "default" },
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// ─── Ring-shape mappings ──────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
// `size` for the ring shape maps to a default pixel diameter. `ringSize`
|
|
47
|
+
// overrides this when callers need a specific dimension.
|
|
48
|
+
const RING_DEFAULT_PX: Record<ProgressSize, number> = {
|
|
49
|
+
sm: 32,
|
|
50
|
+
md: 48,
|
|
51
|
+
lg: 64,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Default stroke widths scale with size so a small ring doesn't look hollow
|
|
55
|
+
// and a large ring doesn't look anaemic. `strokeWidth` overrides.
|
|
56
|
+
const RING_DEFAULT_STROKE: Record<ProgressSize, number> = {
|
|
57
|
+
sm: 4,
|
|
58
|
+
md: 5,
|
|
59
|
+
lg: 6,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const RING_STROKE_CLASS: Record<ProgressVariant, string> = {
|
|
63
|
+
default: "ax:stroke-primary",
|
|
64
|
+
success: "ax:stroke-success",
|
|
65
|
+
warning: "ax:stroke-warning",
|
|
66
|
+
error: "ax:stroke-destructive",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Props ────────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
72
|
+
/** Current value, 0–max. */
|
|
73
|
+
value?: number
|
|
74
|
+
/** Maximum value. Defaults to 100. */
|
|
75
|
+
max?: number
|
|
76
|
+
/** Visual shape - horizontal `bar` (default) or circular `ring`. */
|
|
77
|
+
shape?: ProgressShape
|
|
78
|
+
/** Size - applies to both shapes:
|
|
79
|
+
* bar → height (h-1 / h-2 / h-4)
|
|
80
|
+
* ring → pixel diameter (32 / 48 / 64) */
|
|
81
|
+
size?: ProgressSize
|
|
82
|
+
/** Colour treatment. The track stays neutral; this drives the indicator. */
|
|
83
|
+
variant?: ProgressVariant
|
|
84
|
+
/** Ring-only: override the pixel diameter (takes precedence over `size`). */
|
|
85
|
+
ringSize?: number
|
|
86
|
+
/** Ring-only: override the stroke width in pixels. */
|
|
87
|
+
strokeWidth?: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ─── Component ────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
93
|
+
(
|
|
94
|
+
{
|
|
95
|
+
className,
|
|
96
|
+
value = 0,
|
|
97
|
+
max = 100,
|
|
98
|
+
shape = "bar",
|
|
99
|
+
size = "md",
|
|
100
|
+
variant = "default",
|
|
101
|
+
ringSize,
|
|
102
|
+
strokeWidth,
|
|
103
|
+
...props
|
|
104
|
+
},
|
|
105
|
+
ref
|
|
106
|
+
) => {
|
|
107
|
+
const pct = Math.min(100, Math.max(0, (value / max) * 100))
|
|
108
|
+
|
|
109
|
+
// Common ARIA + data attributes work for both shapes.
|
|
110
|
+
const ariaProps = {
|
|
111
|
+
role: "progressbar" as const,
|
|
112
|
+
"aria-valuemin": 0,
|
|
113
|
+
"aria-valuemax": max,
|
|
114
|
+
"aria-valuenow": value,
|
|
115
|
+
"data-state": pct >= 100 ? "complete" : "loading",
|
|
116
|
+
"data-value": value,
|
|
117
|
+
"data-max": max,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (shape === "ring") {
|
|
121
|
+
const diameter = ringSize ?? RING_DEFAULT_PX[size]
|
|
122
|
+
const stroke = strokeWidth ?? RING_DEFAULT_STROKE[size]
|
|
123
|
+
const r = (diameter - stroke) / 2
|
|
124
|
+
const c = diameter / 2
|
|
125
|
+
const circ = 2 * Math.PI * r
|
|
126
|
+
const dashOffset = circ * (1 - pct / 100)
|
|
127
|
+
const strokeClass = RING_STROKE_CLASS[variant]
|
|
128
|
+
return (
|
|
129
|
+
<div
|
|
130
|
+
ref={ref}
|
|
131
|
+
className={cn("ax:inline-flex ax:shrink-0", className)}
|
|
132
|
+
{...ariaProps}
|
|
133
|
+
{...props}
|
|
134
|
+
>
|
|
135
|
+
<svg
|
|
136
|
+
width={diameter}
|
|
137
|
+
height={diameter}
|
|
138
|
+
viewBox={`0 0 ${diameter} ${diameter}`}
|
|
139
|
+
aria-hidden="true"
|
|
140
|
+
>
|
|
141
|
+
{/* Track - same hue at 15% opacity sits behind the indicator arc */}
|
|
142
|
+
<circle
|
|
143
|
+
cx={c}
|
|
144
|
+
cy={c}
|
|
145
|
+
r={r}
|
|
146
|
+
fill="none"
|
|
147
|
+
strokeWidth={stroke}
|
|
148
|
+
className={`${strokeClass} ax:opacity-15`}
|
|
149
|
+
/>
|
|
150
|
+
{/* Indicator arc - animated stroke-dashoffset for smooth value changes */}
|
|
151
|
+
{pct > 0 && (
|
|
152
|
+
<circle
|
|
153
|
+
cx={c}
|
|
154
|
+
cy={c}
|
|
155
|
+
r={r}
|
|
156
|
+
fill="none"
|
|
157
|
+
strokeWidth={stroke}
|
|
158
|
+
strokeLinecap="round"
|
|
159
|
+
strokeDasharray={circ}
|
|
160
|
+
strokeDashoffset={dashOffset}
|
|
161
|
+
transform={`rotate(-90 ${c} ${c})`}
|
|
162
|
+
className={cn(
|
|
163
|
+
strokeClass,
|
|
164
|
+
"ax:[transition:stroke-dashoffset_var(--motion-slow)_var(--motion-ease-out)]"
|
|
165
|
+
)}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
</svg>
|
|
169
|
+
</div>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Bar shape (default)
|
|
174
|
+
return (
|
|
175
|
+
<div
|
|
176
|
+
ref={ref}
|
|
177
|
+
className={cn(barTrackVariants({ size }), className)}
|
|
178
|
+
{...ariaProps}
|
|
179
|
+
{...props}
|
|
180
|
+
>
|
|
181
|
+
<div
|
|
182
|
+
className={barIndicatorVariants({ variant })}
|
|
183
|
+
style={{ transform: `translateX(-${100 - pct}%)` }}
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
Progress.displayName = "Progress"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Paperclip } from "lucide-react"
|
|
4
|
+
import { AttachmentChip, PromptInput, PromptInputAction } from "./prompt-input"
|
|
5
|
+
import { TooltipProvider } from "./tooltip"
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof PromptInput> = {
|
|
8
|
+
title: "Components/PromptInput",
|
|
9
|
+
component: PromptInput,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
decorators: [(Story) => <TooltipProvider><div style={{ maxWidth: 640 }}><Story /></div></TooltipProvider>],
|
|
12
|
+
argTypes: { onSubmit: { control: false }, onStop: { control: false }, onValueChange: { control: false } },
|
|
13
|
+
args: { onSubmit: () => undefined, placeholder: "Ask about properties, prices or communities" },
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default meta
|
|
17
|
+
type Story = StoryObj<typeof PromptInput>
|
|
18
|
+
|
|
19
|
+
export const Default: Story = {}
|
|
20
|
+
|
|
21
|
+
export const Busy: Story = { args: { busy: true, defaultValue: "", onStop: () => undefined } }
|
|
22
|
+
|
|
23
|
+
export const NearTheLimit: Story = { args: { maxLength: 120, defaultValue: "Three-bedroom apartments on Saadiyat Island for a family of five, near a school, with sea views please" } }
|
|
24
|
+
|
|
25
|
+
export const OverTheLimit: Story = { args: { maxLength: 60, defaultValue: "Three-bedroom apartments on Saadiyat Island for a family of five" } }
|
|
26
|
+
|
|
27
|
+
export const Disabled: Story = {
|
|
28
|
+
args: { disabled: true, disabledReason: "The assistant is offline for maintenance until 14:00. Your agent can still help." },
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const WithAttachments: Story = {
|
|
32
|
+
args: {
|
|
33
|
+
hasAttachments: true,
|
|
34
|
+
canSubmit: false,
|
|
35
|
+
hint: "Waiting for uploads to finish.",
|
|
36
|
+
toolbar: <PromptInputAction label="Attach files"><Paperclip /></PromptInputAction>,
|
|
37
|
+
attachments: (
|
|
38
|
+
<div style={{ display: "flex", gap: "0.5rem", overflowX: "auto" }} role="list" aria-label="Attachments">
|
|
39
|
+
<div role="listitem"><AttachmentChip name="floor-plan.pdf" size={1_800_000} onRemove={() => undefined} /></div>
|
|
40
|
+
<div role="listitem"><AttachmentChip name="living-room.jpg" size={420_000} kind="image" progress={45} onRemove={() => undefined} /></div>
|
|
41
|
+
<div role="listitem"><AttachmentChip name="video.mov" size={52_000_000} error="File type not supported" onRemove={() => undefined} /></div>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Small: Story = {
|
|
48
|
+
decorators: [(Story) => <div style={{ maxWidth: 360 }}><Story /></div>],
|
|
49
|
+
args: {
|
|
50
|
+
size: "sm",
|
|
51
|
+
placeholder: "Message Adres Assistant",
|
|
52
|
+
toolbar: <PromptInputAction label="Attach files"><Paperclip /></PromptInputAction>,
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const FileDrop: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
onFilesDrop: () => undefined,
|
|
59
|
+
dropHint: "PDF, PNG or JPG, up to 10 MB each",
|
|
60
|
+
placeholder: "Drag a file onto the composer",
|
|
61
|
+
},
|
|
62
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import { renderToStaticMarkup } from "react-dom/server"
|
|
4
|
+
import { AttachmentChip, PromptInput, PromptInputAction, characterCountTone, formatFileSize, isFileDrag } from "./prompt-input"
|
|
5
|
+
import { TooltipProvider } from "./tooltip"
|
|
6
|
+
|
|
7
|
+
const submit = () => undefined
|
|
8
|
+
|
|
9
|
+
describe("characterCountTone", () => {
|
|
10
|
+
it("stays hidden below 80% of the limit", () => {
|
|
11
|
+
expect(characterCountTone(1599, 2000)).toBeNull()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("is muted from 80%, warning from 90% and destructive once over", () => {
|
|
15
|
+
expect(characterCountTone(1600, 2000)).toBe("muted")
|
|
16
|
+
expect(characterCountTone(1800, 2000)).toBe("warning")
|
|
17
|
+
expect(characterCountTone(2000, 2000)).toBe("warning")
|
|
18
|
+
expect(characterCountTone(2001, 2000)).toBe("destructive")
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe("formatFileSize", () => {
|
|
23
|
+
it("uses KB below a megabyte, never showing 0 KB", () => {
|
|
24
|
+
expect(formatFileSize(10)).toBe("1 KB")
|
|
25
|
+
expect(formatFileSize(420_000)).toBe("410 KB")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("uses MB with one decimal from a megabyte", () => {
|
|
29
|
+
expect(formatFileSize(1024 * 1024 * 1.8)).toBe("1.8 MB")
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe("PromptInput", () => {
|
|
34
|
+
it("disables Send while the field is empty", () => {
|
|
35
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={submit} />)
|
|
36
|
+
expect(html).toMatch(/aria-label="Send message"[^>]*disabled=""|disabled=""[^>]*aria-label="Send message"/)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("enables Send for attachments alone", () => {
|
|
40
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={submit} hasAttachments />)
|
|
41
|
+
expect(html).not.toMatch(/aria-label="Send message"[^>]*disabled=""|disabled=""[^>]*aria-label="Send message"/)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("marks an over-limit field invalid and says how much to cut", () => {
|
|
45
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={submit} maxLength={10} defaultValue="Twelve chars" />)
|
|
46
|
+
expect(html).toContain('aria-invalid="true"')
|
|
47
|
+
expect(html).toContain("Shorten your message by 2 characters to send it.")
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("swaps Send for Stop while busy", () => {
|
|
51
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={submit} busy />)
|
|
52
|
+
expect(html).toContain('aria-label="Stop generating"')
|
|
53
|
+
expect(html).not.toContain('aria-label="Send message"')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("explains why it is disabled", () => {
|
|
57
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={submit} disabled disabledReason="Offline until 14:00" />)
|
|
58
|
+
expect(html).toContain("Offline until 14:00")
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe("AttachmentChip", () => {
|
|
63
|
+
it("shows progress while uploading, then the size", () => {
|
|
64
|
+
expect(renderToStaticMarkup(<AttachmentChip name="a.pdf" size={2048} progress={40} onRemove={submit} />)).toContain('aria-label="Uploading a.pdf"')
|
|
65
|
+
expect(renderToStaticMarkup(<AttachmentChip name="a.pdf" size={2048} onRemove={submit} />)).toContain("2 KB")
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it("shows the error in place of size", () => {
|
|
69
|
+
const html = renderToStaticMarkup(<AttachmentChip name="a.mov" size={2048} error="File type not supported" onRemove={submit} />)
|
|
70
|
+
expect(html).toContain("File type not supported")
|
|
71
|
+
expect(html).not.toContain("2 KB")
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe("PromptInput size", () => {
|
|
76
|
+
it("defaults to md with 36px buttons", () => {
|
|
77
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={() => undefined} />)
|
|
78
|
+
expect(html).toContain('data-size="md"')
|
|
79
|
+
expect(html).toContain("ax:size-9")
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("tightens to sm and sizes toolbar actions to match", () => {
|
|
83
|
+
const html = renderToStaticMarkup(
|
|
84
|
+
<TooltipProvider>
|
|
85
|
+
<PromptInput size="sm" onSubmit={() => undefined} toolbar={<PromptInputAction label="Attach files"><svg /></PromptInputAction>} />
|
|
86
|
+
</TooltipProvider>,
|
|
87
|
+
)
|
|
88
|
+
expect(html).toContain('data-size="sm"')
|
|
89
|
+
expect(html).toContain("ax:rounded-lg")
|
|
90
|
+
expect(html).not.toContain("ax:size-9")
|
|
91
|
+
expect(html.match(/ax:size-8/g)?.length).toBe(2)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe("PromptInput file drop", () => {
|
|
96
|
+
it("only treats drags that carry files as drops", () => {
|
|
97
|
+
expect(isFileDrag(["Files", "application/x-moz-file"])).toBe(true)
|
|
98
|
+
expect(isFileDrag(["text/plain", "text/uri-list"])).toBe(false)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("shows no drop overlay at rest", () => {
|
|
102
|
+
const html = renderToStaticMarkup(<PromptInput onSubmit={submit} onFilesDrop={submit} dropHint="PDF or images" />)
|
|
103
|
+
expect(html).not.toContain("Drop files to attach")
|
|
104
|
+
expect(html).not.toContain("PDF or images")
|
|
105
|
+
})
|
|
106
|
+
})
|