@adres/mgda-ui 0.0.0-stage → 0.0.1
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,31 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { ButtonGroup, ButtonGroupItem } from "./button-group"
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ButtonGroup> = {
|
|
6
|
+
title: "Components/ButtonGroup",
|
|
7
|
+
component: ButtonGroup,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
type Story = StoryObj<typeof ButtonGroup>
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<ButtonGroup>
|
|
17
|
+
<ButtonGroupItem active>List</ButtonGroupItem>
|
|
18
|
+
<ButtonGroupItem>Grid</ButtonGroupItem>
|
|
19
|
+
<ButtonGroupItem>Board</ButtonGroupItem>
|
|
20
|
+
</ButtonGroup>
|
|
21
|
+
),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const Primary: Story = {
|
|
25
|
+
render: () => (
|
|
26
|
+
<ButtonGroup variant="primary">
|
|
27
|
+
<ButtonGroupItem>Save</ButtonGroupItem>
|
|
28
|
+
<ButtonGroupItem compact>▾</ButtonGroupItem>
|
|
29
|
+
</ButtonGroup>
|
|
30
|
+
),
|
|
31
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
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 ButtonGroupSize = "sm" | "md" | "lg"
|
|
10
|
+
export type ButtonGroupVariant = "outline" | "primary"
|
|
11
|
+
export type ButtonGroupSelected = "primary" | "muted"
|
|
12
|
+
|
|
13
|
+
// ─── Context ──────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
interface ButtonGroupContextValue {
|
|
16
|
+
size: ButtonGroupSize
|
|
17
|
+
variant: ButtonGroupVariant
|
|
18
|
+
selected: ButtonGroupSelected
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ButtonGroupContext = React.createContext<ButtonGroupContextValue>({
|
|
22
|
+
size: "md",
|
|
23
|
+
variant: "outline",
|
|
24
|
+
selected: "primary",
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// ─── Root ─────────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const rootVariants = cva(
|
|
30
|
+
"ax:inline-flex ax:items-stretch ax:overflow-hidden ax:rounded-[var(--radius)]",
|
|
31
|
+
{
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
outline: "ax:border ax:border-border",
|
|
35
|
+
primary: "",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: { variant: "outline" },
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export interface ButtonGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
43
|
+
/** Item height + type metrics - mirrors Button at the same size. */
|
|
44
|
+
size?: ButtonGroupSize
|
|
45
|
+
/** Visual mode. `outline` = bordered container with transparent items;
|
|
46
|
+
* `primary` = solid primary surface across all items (split-button look). */
|
|
47
|
+
variant?: ButtonGroupVariant
|
|
48
|
+
/** How an `active` item stands out (only relevant when variant="outline").
|
|
49
|
+
* `primary` - strong, brand-coloured active cell (view toggle).
|
|
50
|
+
* `muted` - quiet, neutral-grey active cell (filter group). */
|
|
51
|
+
selected?: ButtonGroupSelected
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* ButtonGroup - a row of connected buttons sharing one container.
|
|
56
|
+
*
|
|
57
|
+
* Use for: view toggles, filter groups, toolbars, split-action buttons.
|
|
58
|
+
* NOT for: navigating between content panels (use Tabs) or single-selection
|
|
59
|
+
* view-mode switches with a sliding indicator (use SegmentedControl).
|
|
60
|
+
*/
|
|
61
|
+
export const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(
|
|
62
|
+
({ className, size = "md", variant = "outline", selected = "primary", children, ...props }, ref) => (
|
|
63
|
+
<ButtonGroupContext.Provider value={{ size, variant, selected }}>
|
|
64
|
+
<div
|
|
65
|
+
ref={ref}
|
|
66
|
+
role="group"
|
|
67
|
+
className={cn(rootVariants({ variant }), className)}
|
|
68
|
+
{...props}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
</ButtonGroupContext.Provider>
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
ButtonGroup.displayName = "ButtonGroup"
|
|
76
|
+
|
|
77
|
+
// ─── Item ─────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
// Height + type metrics mirror Button so a ButtonGroup reads as a strip of
|
|
80
|
+
// connected Buttons. Padding is applied separately per size + compact flag
|
|
81
|
+
// to avoid Tailwind class collisions.
|
|
82
|
+
const itemBase = cva(
|
|
83
|
+
[
|
|
84
|
+
"ax:inline-flex ax:items-center ax:justify-center ax:gap-xs ax:whitespace-nowrap ax:select-none ax:cursor-pointer",
|
|
85
|
+
"ax:transition-colors ax:transition-snappy",
|
|
86
|
+
"ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:focus-visible:ring-inset",
|
|
87
|
+
"ax:disabled:pointer-events-none ax:disabled:opacity-50",
|
|
88
|
+
"ax:[&_svg]:pointer-events-none ax:[&_svg]:shrink-0",
|
|
89
|
+
],
|
|
90
|
+
{
|
|
91
|
+
variants: {
|
|
92
|
+
size: {
|
|
93
|
+
sm: "ax:h-2xl ax:t-label-xs ax:[&_svg]:size-3",
|
|
94
|
+
md: "ax:h-3xl ax:t-label-sm ax:[&_svg]:size-3.5",
|
|
95
|
+
lg: "ax:h-4xl ax:text-base ax:[&_svg]:size-4",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
const PADDING: Record<ButtonGroupSize, { normal: string; compact: string }> = {
|
|
102
|
+
sm: { normal: "ax:px-sm", compact: "ax:px-2" },
|
|
103
|
+
md: { normal: "ax:px-md", compact: "ax:px-2.5" },
|
|
104
|
+
lg: { normal: "ax:px-xl", compact: "ax:px-3" },
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Cells use emphasis aliases, so the selected/primary fills auto-invert to deep
|
|
108
|
+
// tints in dark (no dark: override). One exception: the idle cell's hover:
|
|
109
|
+
// `neutral-weakest` inverts to ~the dark background, giving no visible lift, so a
|
|
110
|
+
// `dark:hover:bg-neutral-weaker` provides the lift. `outlineSelectedMuted` uses
|
|
111
|
+
// mode-aware `bg-selected`/`text-foreground`.
|
|
112
|
+
const STATE = {
|
|
113
|
+
outlineIdle: "ax:bg-muted ax:text-foreground ax:border-r ax:border-border ax:last:border-r-0 ax:hover:bg-muted/50",
|
|
114
|
+
outlineSelectedPrimary: "ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-strong ax:dark:text-primary-weaker ax:font-medium ax:border-r ax:border-border ax:last:border-r-0 ax:hover:bg-primary-weak/80 ax:dark:hover:bg-primary-strong/80",
|
|
115
|
+
outlineSelectedMuted: "ax:bg-selected ax:text-foreground ax:font-medium ax:border-r ax:border-border ax:last:border-r-0",
|
|
116
|
+
primary: "ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-strong ax:dark:text-primary-weaker ax:font-medium ax:hover:bg-primary-weak/80 ax:border-r ax:border-background ax:dark:hover:bg-primary-strong/80 ax:last:border-r-0",
|
|
117
|
+
} as const
|
|
118
|
+
|
|
119
|
+
export interface ButtonGroupItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
120
|
+
/** Marks this item as the currently-selected cell (sets aria-pressed). */
|
|
121
|
+
active?: boolean
|
|
122
|
+
/** Reduces horizontal padding - use for chevron/icon-only trailing buttons
|
|
123
|
+
* (the dropdown side of a split button). */
|
|
124
|
+
compact?: boolean
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const ButtonGroupItem = React.forwardRef<HTMLButtonElement, ButtonGroupItemProps>(
|
|
128
|
+
({ className, active, compact, type = "button", children, ...props }, ref) => {
|
|
129
|
+
const { size, variant, selected } = React.useContext(ButtonGroupContext)
|
|
130
|
+
const stateClass =
|
|
131
|
+
variant === "primary"
|
|
132
|
+
? STATE.primary
|
|
133
|
+
: active
|
|
134
|
+
? selected === "muted"
|
|
135
|
+
? STATE.outlineSelectedMuted
|
|
136
|
+
: STATE.outlineSelectedPrimary
|
|
137
|
+
: STATE.outlineIdle
|
|
138
|
+
return (
|
|
139
|
+
<button
|
|
140
|
+
ref={ref}
|
|
141
|
+
type={type}
|
|
142
|
+
aria-pressed={active ?? undefined}
|
|
143
|
+
className={cn(
|
|
144
|
+
itemBase({ size }),
|
|
145
|
+
PADDING[size][compact ? "compact" : "normal"],
|
|
146
|
+
stateClass,
|
|
147
|
+
className,
|
|
148
|
+
)}
|
|
149
|
+
{...props}
|
|
150
|
+
>
|
|
151
|
+
{children}
|
|
152
|
+
</button>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
ButtonGroupItem.displayName = "ButtonGroupItem"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Settings } from "lucide-react"
|
|
4
|
+
import { Button } from "./button"
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Button> = {
|
|
7
|
+
title: "Components/Button",
|
|
8
|
+
component: Button,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
argTypes: {
|
|
11
|
+
variant: {
|
|
12
|
+
control: "select",
|
|
13
|
+
options: ["primary", "secondary", "outline", "ghost", "destructive", "accent"],
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
control: "select",
|
|
17
|
+
options: ["sm", "md", "lg", "icon-sm", "icon", "icon-lg"],
|
|
18
|
+
},
|
|
19
|
+
elevated: { control: "boolean" },
|
|
20
|
+
aurora: { control: "boolean" },
|
|
21
|
+
disabled: { control: "boolean" },
|
|
22
|
+
},
|
|
23
|
+
args: {
|
|
24
|
+
children: "Button",
|
|
25
|
+
variant: "primary",
|
|
26
|
+
size: "md",
|
|
27
|
+
elevated: false,
|
|
28
|
+
aurora: false,
|
|
29
|
+
disabled: false,
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default meta
|
|
34
|
+
type Story = StoryObj<typeof Button>
|
|
35
|
+
|
|
36
|
+
export const Primary: Story = {}
|
|
37
|
+
|
|
38
|
+
export const Secondary: Story = {
|
|
39
|
+
args: { variant: "secondary" },
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const Outline: Story = {
|
|
43
|
+
args: { variant: "outline" },
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const Destructive: Story = {
|
|
47
|
+
args: { variant: "destructive" },
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Elevated: Story = {
|
|
51
|
+
args: { elevated: true },
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const Large: Story = {
|
|
55
|
+
args: { size: "lg", children: "Get started" },
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const IconSizes: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
|
61
|
+
<Button variant="outline" size="icon-sm" aria-label="Settings"><Settings /></Button>
|
|
62
|
+
<Button variant="outline" size="icon" aria-label="Settings"><Settings /></Button>
|
|
63
|
+
<Button variant="outline" size="icon-lg" aria-label="Settings"><Settings /></Button>
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import { buttonVariants } from "./button"
|
|
3
|
+
|
|
4
|
+
describe("buttonVariants", () => {
|
|
5
|
+
it("includes the ax prefix on the base classes", () => {
|
|
6
|
+
expect(buttonVariants()).toContain("ax:inline-flex")
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it("matches each icon size to its text-button height", () => {
|
|
10
|
+
expect(buttonVariants({ size: "sm" })).toContain("ax:h-2xl")
|
|
11
|
+
expect(buttonVariants({ size: "icon-sm" })).toContain("ax:size-2xl")
|
|
12
|
+
expect(buttonVariants({ size: "md" })).toContain("ax:h-3xl")
|
|
13
|
+
expect(buttonVariants({ size: "icon" })).toContain("ax:size-3xl")
|
|
14
|
+
expect(buttonVariants({ size: "lg" })).toContain("ax:h-4xl")
|
|
15
|
+
expect(buttonVariants({ size: "icon-lg" })).toContain("ax:size-4xl")
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
5
|
+
import { tv, type VariantProps } from "tailwind-variants"
|
|
6
|
+
import { cn } from "../lib/cn"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Button - the most leveraged primitive in the system.
|
|
10
|
+
*
|
|
11
|
+
* Variants reflect role, not colour: every brand can re-skin them by changing
|
|
12
|
+
* `--primary`, `--secondary`, `--accent`, etc.; the component never knows
|
|
13
|
+
* which brand it's wearing.
|
|
14
|
+
*
|
|
15
|
+
* Sizes follow the type+touch ladder:
|
|
16
|
+
* sm - dense UI rows (32px tall)
|
|
17
|
+
* md - DEFAULT, comfortable body buttons (40px)
|
|
18
|
+
* lg - primary marketing CTAs (48px)
|
|
19
|
+
* icon-sm / icon / icon-lg - square 32 / 40 / 48px icon-only actions, matching
|
|
20
|
+
* sm / md / lg heights (a11y label still required)
|
|
21
|
+
*/
|
|
22
|
+
export const buttonVariants = tv({
|
|
23
|
+
base: [
|
|
24
|
+
"ax:inline-flex ax:items-center ax:justify-center ax:gap-xs ax:whitespace-nowrap ax:select-none ax:cursor-pointer",
|
|
25
|
+
// Font size is set per-size below (not via `t-label-sm`) so an app that
|
|
26
|
+
// re-scales the `t-label-*` utilities can't shrink the button text.
|
|
27
|
+
"ax:font-medium ax:leading-none",
|
|
28
|
+
"ax:transition-[transform,background-color,border-color,color,box-shadow] ax:transition-snappy",
|
|
29
|
+
"ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:focus-visible:ring-offset-2 ax:focus-visible:ring-offset-background",
|
|
30
|
+
"ax:disabled:pointer-events-none ax:disabled:opacity-50",
|
|
31
|
+
"ax:active:scale-[0.98]",
|
|
32
|
+
"ax:[&_svg]:pointer-events-none ax:[&_svg]:size-4 ax:[&_svg]:shrink-0",
|
|
33
|
+
],
|
|
34
|
+
variants: {
|
|
35
|
+
variant: {
|
|
36
|
+
primary: [
|
|
37
|
+
// `weak` fill + `weaker` hover: emphasis aliases auto-invert to deep
|
|
38
|
+
// tints in dark, so `-foreground` stays legible in both modes (no dark:).
|
|
39
|
+
"ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-weakest ax:dark:text-primary-strong",
|
|
40
|
+
"ax:hover:bg-primary-weak/80 ax:dark:hover:bg-primary-weak/80",
|
|
41
|
+
],
|
|
42
|
+
secondary: [
|
|
43
|
+
"ax:bg-secondary-weaker ax:text-secondary-foreground ax:dark:bg-secondary-strong ax:dark:text-secondary-muted",
|
|
44
|
+
"ax:hover:bg-secondary-weaker/80 ax:dark:hover:bg-secondary-strong/80",
|
|
45
|
+
"ax:shadow-xs",
|
|
46
|
+
],
|
|
47
|
+
outline: [
|
|
48
|
+
"ax:bg-transparent ax:text-foreground ax:border ax:border-border",
|
|
49
|
+
"ax:hover:bg-muted",
|
|
50
|
+
],
|
|
51
|
+
ghost: [
|
|
52
|
+
"ax:bg-transparent ax:text-foreground",
|
|
53
|
+
"ax:hover:bg-muted",
|
|
54
|
+
],
|
|
55
|
+
destructive: [
|
|
56
|
+
"ax:bg-destructive-weaker ax:text-destructive-foreground",
|
|
57
|
+
"ax:hover:bg-destructive-weaker/80",
|
|
58
|
+
],
|
|
59
|
+
accent: [
|
|
60
|
+
"ax:bg-accent-weak ax:text-accent-foreground",
|
|
61
|
+
"ax:hover:bg-accent-weak/80",
|
|
62
|
+
"ax:shadow-sm",
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
size: {
|
|
66
|
+
sm: "ax:h-2xl ax:px-sm ax:rounded-(--button-radius) ax:text-sm",
|
|
67
|
+
md: "ax:h-3xl ax:px-md ax:rounded-(--button-radius) ax:text-sm",
|
|
68
|
+
lg: "ax:h-4xl ax:px-xl ax:rounded-(--button-radius) ax:text-base",
|
|
69
|
+
// Square icon-only buttons, one per height above: 32 / 40 / 48px.
|
|
70
|
+
"icon-sm": "ax:size-2xl ax:rounded-(--button-radius)",
|
|
71
|
+
icon: "ax:size-3xl ax:rounded-(--button-radius)",
|
|
72
|
+
"icon-lg": "ax:size-4xl ax:rounded-(--button-radius) ax:[&_svg]:size-5",
|
|
73
|
+
},
|
|
74
|
+
// Opt-in tinted drop shadow. Only variants with a defined tinted shadow
|
|
75
|
+
// (primary, destructive) react, wired in compoundVariants below.
|
|
76
|
+
elevated: {
|
|
77
|
+
true: "",
|
|
78
|
+
false: "",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
compoundVariants: [
|
|
82
|
+
// Tinted button shadow is a light-mode flourish, cleared in dark, where the
|
|
83
|
+
// low-contrast page makes a coloured lift read as muddy.
|
|
84
|
+
{ variant: "primary", elevated: true, class: "ax:shadow-primary-btn ax:dark:shadow-none" },
|
|
85
|
+
{ variant: "destructive", elevated: true, class: "ax:shadow-destructive-btn ax:dark:shadow-none" },
|
|
86
|
+
],
|
|
87
|
+
defaultVariants: {
|
|
88
|
+
variant: "primary",
|
|
89
|
+
size: "md",
|
|
90
|
+
elevated: false,
|
|
91
|
+
},
|
|
92
|
+
}, { twMerge: false })
|
|
93
|
+
|
|
94
|
+
export interface ButtonProps
|
|
95
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
96
|
+
VariantProps<typeof buttonVariants> {
|
|
97
|
+
/** When true, renders the child element instead of a button (e.g. for `<Link>`). */
|
|
98
|
+
asChild?: boolean
|
|
99
|
+
/** Decorative "aurora" - two soft, blurred orbs (primary + accent) drifting
|
|
100
|
+
* behind the label for a modern, glowing look. Designed for the `primary`
|
|
101
|
+
* variant. Ignored with `asChild` (it needs to inject its own children). */
|
|
102
|
+
aurora?: boolean
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
106
|
+
({ className, variant, size, elevated, aurora = false, asChild = false, children, ...props }, ref) => {
|
|
107
|
+
const Comp = asChild ? Slot : "button"
|
|
108
|
+
// Aurora injects decorative children, so it can't pass through Slot.
|
|
109
|
+
const showAurora = aurora && !asChild
|
|
110
|
+
return (
|
|
111
|
+
<Comp
|
|
112
|
+
ref={ref}
|
|
113
|
+
data-slot="button"
|
|
114
|
+
className={cn(
|
|
115
|
+
buttonVariants({ variant, size, elevated }),
|
|
116
|
+
// `isolate` scopes the orbs' negative z-index to the button; `overflow-hidden`
|
|
117
|
+
// clips them to the (rounded) button shape.
|
|
118
|
+
showAurora && "ax:relative ax:isolate ax:overflow-hidden",
|
|
119
|
+
className,
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
>
|
|
123
|
+
{showAurora ? (
|
|
124
|
+
<>
|
|
125
|
+
{/* Blurred orbs sit behind the label (-z-10): a primary glow top-start,
|
|
126
|
+
an accent glow bottom-end. */}
|
|
127
|
+
<span aria-hidden="true" className="ax:pointer-events-none ax:absolute ax:-top-12 ax:-left-2 ax:-z-10 ax:size-14 ax:rounded-full ax:bg-primary/40 ax:dark:bg-primary-weaker ax:blur-xl ax:saturate-250" />
|
|
128
|
+
<span aria-hidden="true" className="ax:pointer-events-none ax:absolute ax:-top-4 ax:-z-10 ax:size-16 ax:rounded-full ax:bg-primary-weaker ax:dark:bg-primary/20 ax:blur-xl ax:saturate-250" />
|
|
129
|
+
<span aria-hidden="true" className="ax:pointer-events-none ax:absolute ax:-bottom-12 ax:-right-6 ax:-z-10 ax:size-16 ax:rounded-full ax:bg-primary-weaker ax:blur-xl ax:saturate-250" />
|
|
130
|
+
{children}
|
|
131
|
+
</>
|
|
132
|
+
) : (
|
|
133
|
+
children
|
|
134
|
+
)}
|
|
135
|
+
</Comp>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
Button.displayName = "Button"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Calendar } from "./calendar"
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Calendar> = {
|
|
6
|
+
title: "Components/Calendar",
|
|
7
|
+
component: Calendar,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
args: {
|
|
10
|
+
mode: "single",
|
|
11
|
+
defaultValue: new Date(2026, 7, 11),
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default meta
|
|
16
|
+
type Story = StoryObj<typeof Calendar>
|
|
17
|
+
|
|
18
|
+
export const Default: Story = {}
|
|
19
|
+
|
|
20
|
+
export const Range: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
mode: "range",
|
|
23
|
+
defaultValue: { from: new Date(2026, 7, 10), to: new Date(2026, 7, 15) },
|
|
24
|
+
},
|
|
25
|
+
}
|