@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,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
# >>> DERIVED - regenerated by `npm run knowledge:sync`. Do not edit. <<<
|
|
3
|
+
derived:
|
|
4
|
+
schemaVersion: 1
|
|
5
|
+
id: number-stepper
|
|
6
|
+
sourceFile: packages/design-system/src/components/number-stepper.tsx
|
|
7
|
+
client: true
|
|
8
|
+
category: web-app-patterns
|
|
9
|
+
categoryFromBarrel: web-app-patterns
|
|
10
|
+
barrelExported: true
|
|
11
|
+
importPath: "@adres/mgda-ui"
|
|
12
|
+
exports:
|
|
13
|
+
- name: NumberStepper
|
|
14
|
+
kind: component
|
|
15
|
+
typeText: "ForwardRefExoticComponent<NumberStepperProps & RefAttributes<HTMLDivElement>>"
|
|
16
|
+
- name: NumberStepperProps
|
|
17
|
+
kind: type
|
|
18
|
+
- name: NumberStepperSize
|
|
19
|
+
kind: type
|
|
20
|
+
primaryExport: NumberStepper
|
|
21
|
+
subcomponents: []
|
|
22
|
+
props:
|
|
23
|
+
NumberStepper:
|
|
24
|
+
- name: ariaLabel
|
|
25
|
+
type: string
|
|
26
|
+
required: false
|
|
27
|
+
declaredIn: self
|
|
28
|
+
doc: "Accessible label for the wrapper. Set this when context isn't obvious from the surrounding label element."
|
|
29
|
+
- name: disabled
|
|
30
|
+
type: boolean
|
|
31
|
+
required: false
|
|
32
|
+
declaredIn: self
|
|
33
|
+
doc: "Disables the whole control (both buttons + input). Per-bound disabling of the +/- buttons still happens automatically at min/max."
|
|
34
|
+
- name: id
|
|
35
|
+
type: string
|
|
36
|
+
required: false
|
|
37
|
+
declaredIn: self
|
|
38
|
+
doc: "Pass-through id for the central <input>, so a sibling <label htmlFor> binds correctly."
|
|
39
|
+
- name: max
|
|
40
|
+
type: number
|
|
41
|
+
required: false
|
|
42
|
+
declaredIn: self
|
|
43
|
+
doc: "Largest value the stepper allows. Defaults to 999."
|
|
44
|
+
- name: min
|
|
45
|
+
type: number
|
|
46
|
+
required: false
|
|
47
|
+
declaredIn: self
|
|
48
|
+
doc: "Smallest value the stepper allows. Defaults to 1."
|
|
49
|
+
- name: onChange
|
|
50
|
+
type: "(next: number) => void"
|
|
51
|
+
required: true
|
|
52
|
+
declaredIn: self
|
|
53
|
+
- name: size
|
|
54
|
+
type: NumberStepperSize
|
|
55
|
+
required: false
|
|
56
|
+
declaredIn: self
|
|
57
|
+
doc: "Visual density. Matches Button / Input size scale."
|
|
58
|
+
- name: step
|
|
59
|
+
type: number
|
|
60
|
+
required: false
|
|
61
|
+
declaredIn: self
|
|
62
|
+
doc: "Increment / decrement amount. Defaults to 1."
|
|
63
|
+
- name: value
|
|
64
|
+
type: number
|
|
65
|
+
required: true
|
|
66
|
+
declaredIn: self
|
|
67
|
+
extendsProps:
|
|
68
|
+
- "Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\">"
|
|
69
|
+
variants:
|
|
70
|
+
size:
|
|
71
|
+
kind: enum
|
|
72
|
+
options:
|
|
73
|
+
- sm
|
|
74
|
+
- md
|
|
75
|
+
- lg
|
|
76
|
+
default: md
|
|
77
|
+
on: NumberStepper
|
|
78
|
+
variantSource: prop-union
|
|
79
|
+
compoundVariants: []
|
|
80
|
+
sizeMaps:
|
|
81
|
+
- HEIGHT
|
|
82
|
+
- ICON_SIZE
|
|
83
|
+
dataSlots: []
|
|
84
|
+
dependsOn:
|
|
85
|
+
internal:
|
|
86
|
+
- button
|
|
87
|
+
- input
|
|
88
|
+
lib:
|
|
89
|
+
- cn
|
|
90
|
+
hooks: []
|
|
91
|
+
external:
|
|
92
|
+
- lucide-react
|
|
93
|
+
- react
|
|
94
|
+
contexts: []
|
|
95
|
+
stories:
|
|
96
|
+
- exportName: Default
|
|
97
|
+
id: components-numberstepper--default
|
|
98
|
+
storybookTitle: Components/NumberStepper
|
|
99
|
+
docsId: components-numberstepper--documentation
|
|
100
|
+
argTypeOptions: {}
|
|
101
|
+
resolvedCleanly: true
|
|
102
|
+
contractHash: "7fa157843630"
|
|
103
|
+
factsHash: e16e9641c8c8
|
|
104
|
+
# >>> AUTHORED - humans own everything below this line. <<<
|
|
105
|
+
status: stable
|
|
106
|
+
summary: "A joined +/-/input control for a bounded integer value, combining Button and Input into one visual unit."
|
|
107
|
+
useWhen:
|
|
108
|
+
- "a bounded integer input where direct typing should stay possible alongside +/- buttons - seat counts, quantity selectors"
|
|
109
|
+
useInstead:
|
|
110
|
+
- id: input
|
|
111
|
+
when: "a plain number input is enough and the +/- buttons are not needed"
|
|
112
|
+
- id: slider
|
|
113
|
+
when: "picking a value by dragging is more natural than typing or clicking, especially for a wide range"
|
|
114
|
+
related:
|
|
115
|
+
- input
|
|
116
|
+
- slider
|
|
117
|
+
a11y:
|
|
118
|
+
notes:
|
|
119
|
+
- "Sets role=\"group\" with ariaLabel (default \"Number\") on the wrapper - override ariaLabel with something specific, e.g. \"Number of seats\", whenever a sibling label does not already make the control's purpose obvious."
|
|
120
|
+
- "The +/- buttons disable automatically at min/max and stay in sync as the user types - there is no need to compute or pass that disabled state separately."
|
|
121
|
+
keyboard:
|
|
122
|
+
- "The central Input is a real number input, so native increment/decrement via arrow keys works there in addition to clicking the +/- buttons; Tab moves through the decrement button, the input, and the increment button as three separate stops."
|
|
123
|
+
radixHandles: false
|
|
124
|
+
pitfalls:
|
|
125
|
+
- "value must round-trip through onChange like any controlled input - the component maintains an internal typing draft so the field does not fight the user mid-edit, but value/onChange still need to update normally between renders."
|
|
126
|
+
- "Typed input only commits live when it parses to a clean, in-bounds integer - an out-of-range or partial value stays as an uncommitted draft until blur, when it is clamped to [min, max] and always committed, even replacing an unparseable string with min."
|
|
127
|
+
- "The wrapper is w-full by default and the three segments split evenly - constrain the parent (e.g. max-w-sm) for a traditional compact stepper, or it stretches to fill its row."
|
|
128
|
+
- "disabled disables the whole control, but the individual +/- buttons already disable themselves independently at min/max regardless of disabled - the two disabling mechanisms are separate and both apply."
|
|
129
|
+
canonicalSnippet: |
|
|
130
|
+
import { NumberStepper } from "@adres/mgda-ui"
|
|
131
|
+
|
|
132
|
+
const seatsLabel = "Number of seats"
|
|
133
|
+
|
|
134
|
+
<NumberStepper value={count} onChange={setCount} min={1} max={10} ariaLabel={seatsLabel} />
|
|
135
|
+
reviewedAt: 2026-09-01
|
|
136
|
+
reviewedAgainstContractHash: "7fa157843630"
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Usage
|
|
140
|
+
|
|
141
|
+
`NumberStepper` is a bounded integer input that joins a decrement button, a
|
|
142
|
+
central number field, and an increment button into one visual unit - seat
|
|
143
|
+
counts, quantity selectors, anywhere direct typing should stay possible
|
|
144
|
+
alongside +/- clicks. Reach for `Input` alone when the buttons are not
|
|
145
|
+
needed, and `Slider` when dragging suits the range better than typing or
|
|
146
|
+
clicking.
|
|
147
|
+
|
|
148
|
+
The wrapper is `w-full` and its three segments split evenly - constrain the
|
|
149
|
+
parent (e.g. `max-w-sm`) for a compact stepper rather than one that stretches
|
|
150
|
+
to fill its row.
|
|
151
|
+
|
|
152
|
+
Typed input commits live only when it parses to a clean, in-bounds integer;
|
|
153
|
+
an out-of-range or mid-edit value stays as a draft until blur, when it is
|
|
154
|
+
always clamped to `[min, max]` and committed - an unparseable string resolves
|
|
155
|
+
to `min`.
|
|
156
|
+
|
|
157
|
+
`disabled` turns off the whole control, but the +/- buttons already disable
|
|
158
|
+
themselves independently at `min`/`max` regardless of `disabled` - both
|
|
159
|
+
mechanisms apply at once, not one or the other.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
# >>> DERIVED - regenerated by `npm run knowledge:sync`. Do not edit. <<<
|
|
3
|
+
derived:
|
|
4
|
+
schemaVersion: 1
|
|
5
|
+
id: page-header
|
|
6
|
+
sourceFile: packages/design-system/src/components/page-header.tsx
|
|
7
|
+
client: false
|
|
8
|
+
category: web-app-layout-navigation
|
|
9
|
+
categoryFromBarrel: web-app-layout-navigation
|
|
10
|
+
barrelExported: true
|
|
11
|
+
importPath: "@adres/mgda-ui"
|
|
12
|
+
exports:
|
|
13
|
+
- name: PageHeader
|
|
14
|
+
kind: component
|
|
15
|
+
typeText: "ForwardRefExoticComponent<PageHeaderProps & RefAttributes<HTMLDivElement>>"
|
|
16
|
+
- name: PageHeaderProps
|
|
17
|
+
kind: type
|
|
18
|
+
primaryExport: PageHeader
|
|
19
|
+
subcomponents: []
|
|
20
|
+
props:
|
|
21
|
+
PageHeader:
|
|
22
|
+
- name: children
|
|
23
|
+
type: ReactNode
|
|
24
|
+
required: false
|
|
25
|
+
declaredIn: self
|
|
26
|
+
doc: "Right-aligned slot - typically `<Button>`s, a search input, a `Select`, or any other action(s) the page exposes at the top. Wraps to a new line on narrow viewports rather than truncating."
|
|
27
|
+
- name: subtitle
|
|
28
|
+
type: ReactNode
|
|
29
|
+
required: false
|
|
30
|
+
declaredIn: self
|
|
31
|
+
doc: "Optional subtitle / context line shown directly under the title."
|
|
32
|
+
- name: title
|
|
33
|
+
type: ReactNode
|
|
34
|
+
required: true
|
|
35
|
+
declaredIn: self
|
|
36
|
+
doc: "Page or section title. Rendered as an `<h1>` with `t-h1` typography."
|
|
37
|
+
extendsProps:
|
|
38
|
+
- "Omit<React.HTMLAttributes<HTMLDivElement>, \"title\">"
|
|
39
|
+
variants: {}
|
|
40
|
+
variantSource: none
|
|
41
|
+
compoundVariants: []
|
|
42
|
+
sizeMaps: []
|
|
43
|
+
dataSlots: []
|
|
44
|
+
dependsOn:
|
|
45
|
+
internal: []
|
|
46
|
+
lib:
|
|
47
|
+
- cn
|
|
48
|
+
hooks: []
|
|
49
|
+
external:
|
|
50
|
+
- react
|
|
51
|
+
contexts: []
|
|
52
|
+
stories:
|
|
53
|
+
- exportName: Default
|
|
54
|
+
id: components-pageheader--default
|
|
55
|
+
storybookTitle: Components/PageHeader
|
|
56
|
+
docsId: components-pageheader--documentation
|
|
57
|
+
argTypeOptions: {}
|
|
58
|
+
resolvedCleanly: true
|
|
59
|
+
contractHash: cde2a64fb03f
|
|
60
|
+
factsHash: "8e2d8b1c8eb3"
|
|
61
|
+
# >>> AUTHORED - humans own everything below this line. <<<
|
|
62
|
+
status: stable
|
|
63
|
+
summary: "The top-of-content heading row: an h1 title, optional subtitle, and a right-aligned actions slot."
|
|
64
|
+
useWhen:
|
|
65
|
+
- "the top of a page or major section needs a title plus page-level actions (e.g. \"Import CSV\" / \"Invite students\")"
|
|
66
|
+
- "you want a consistent title+subtitle+actions layout instead of hand-rolling one per page"
|
|
67
|
+
useInstead:
|
|
68
|
+
- id: top-nav
|
|
69
|
+
when: "you need a sticky app-shell bar (breadcrumbs, search, account menu), not an in-content page title"
|
|
70
|
+
related:
|
|
71
|
+
- top-nav
|
|
72
|
+
composesWith:
|
|
73
|
+
- button
|
|
74
|
+
tokens:
|
|
75
|
+
- "--muted-foreground"
|
|
76
|
+
a11y:
|
|
77
|
+
notes:
|
|
78
|
+
- "title renders as a real <h1> - a page should have exactly one PageHeader (or otherwise exactly one <h1>), so don't use it more than once per page."
|
|
79
|
+
- "PageHeader is a server component (no \"use client\") with no interactivity of its own; any focus/keyboard behavior comes entirely from what you put in children."
|
|
80
|
+
requiredProps:
|
|
81
|
+
- "title is required; subtitle and children (actions) are optional"
|
|
82
|
+
keyboard: []
|
|
83
|
+
radixHandles: false
|
|
84
|
+
pitfalls:
|
|
85
|
+
- "title's prop type intentionally shadows and omits the native HTML title attribute (via Omit<..., \"title\">) - you cannot pass a native tooltip title string through props; wrap the component if you need one."
|
|
86
|
+
- "children is the actions slot, not the body content - PageHeader is meant to sit above a page's main content, not wrap it."
|
|
87
|
+
canonicalSnippet: |
|
|
88
|
+
import { PageHeader, Button } from "@adres/mgda-ui"
|
|
89
|
+
|
|
90
|
+
const subtitle = "38 active learners"
|
|
91
|
+
|
|
92
|
+
<PageHeader title="Students" subtitle={subtitle}>
|
|
93
|
+
<Button variant="outline">Import CSV</Button>
|
|
94
|
+
<Button>Invite students</Button>
|
|
95
|
+
</PageHeader>
|
|
96
|
+
reviewedAt: 2026-08-31
|
|
97
|
+
reviewedAgainstContractHash: cde2a64fb03f
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Usage
|
|
101
|
+
|
|
102
|
+
PageHeader pairs an `<h1>` title with an optional subtitle on the start side
|
|
103
|
+
and a free-form actions slot (`children`) on the end side, baseline-aligned
|
|
104
|
+
so a wrapping multi-line title keeps its actions anchored to the last line.
|
|
105
|
+
Actions wrap onto a new row on narrow viewports rather than truncating or
|
|
106
|
+
overflowing.
|
|
107
|
+
|
|
108
|
+
Use it once per page or major section - it renders the page's single `<h1>`,
|
|
109
|
+
and stacking more than one breaks that heading hierarchy.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
# >>> DERIVED - regenerated by `npm run knowledge:sync`. Do not edit. <<<
|
|
3
|
+
derived:
|
|
4
|
+
schemaVersion: 1
|
|
5
|
+
id: pagination
|
|
6
|
+
sourceFile: packages/design-system/src/components/pagination.tsx
|
|
7
|
+
client: true
|
|
8
|
+
category: web-app-patterns
|
|
9
|
+
categoryFromBarrel: web-app-patterns
|
|
10
|
+
barrelExported: true
|
|
11
|
+
importPath: "@adres/mgda-ui"
|
|
12
|
+
exports:
|
|
13
|
+
- name: Pagination
|
|
14
|
+
kind: component
|
|
15
|
+
typeText: "ForwardRefExoticComponent<PaginationProps & RefAttributes<HTMLElement>>"
|
|
16
|
+
- name: PaginationContent
|
|
17
|
+
kind: component
|
|
18
|
+
typeText: "ForwardRefExoticComponent<Omit<DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement>, \"ref\"> & RefAttributes<...>>"
|
|
19
|
+
- name: PaginationEllipsis
|
|
20
|
+
kind: component
|
|
21
|
+
typeText: "ForwardRefExoticComponent<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, \"ref\"> & RefAttributes<...>>"
|
|
22
|
+
- name: PaginationItem
|
|
23
|
+
kind: component
|
|
24
|
+
typeText: "ForwardRefExoticComponent<Omit<DetailedHTMLProps<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, \"ref\"> & RefAttributes<HTMLLIElement>>"
|
|
25
|
+
- name: PaginationLink
|
|
26
|
+
kind: component
|
|
27
|
+
typeText: "ForwardRefExoticComponent<PaginationLinkProps & RefAttributes<HTMLButtonElement>>"
|
|
28
|
+
- name: PaginationLinkProps
|
|
29
|
+
kind: type
|
|
30
|
+
- name: paginationLinkVariants
|
|
31
|
+
kind: variants
|
|
32
|
+
typeText: "TVReturnType<{ isActive: { true: \"ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-strong ax:dark:text-primary-weaker\"; false: \"ax:bg-transparent ax:text-foreground ax:hover:bg-muted\"; }; size: { ...; }...[truncated]"
|
|
33
|
+
- name: PaginationNavLinkProps
|
|
34
|
+
kind: type
|
|
35
|
+
- name: PaginationNext
|
|
36
|
+
kind: component
|
|
37
|
+
typeText: "ForwardRefExoticComponent<PaginationNavLinkProps & RefAttributes<HTMLButtonElement>>"
|
|
38
|
+
- name: PaginationPrevious
|
|
39
|
+
kind: component
|
|
40
|
+
typeText: "ForwardRefExoticComponent<PaginationNavLinkProps & RefAttributes<HTMLButtonElement>>"
|
|
41
|
+
- name: PaginationProps
|
|
42
|
+
kind: type
|
|
43
|
+
primaryExport: Pagination
|
|
44
|
+
subcomponents:
|
|
45
|
+
- PaginationContent
|
|
46
|
+
- PaginationEllipsis
|
|
47
|
+
- PaginationItem
|
|
48
|
+
- PaginationLink
|
|
49
|
+
- PaginationNext
|
|
50
|
+
- PaginationPrevious
|
|
51
|
+
props:
|
|
52
|
+
PaginationLink:
|
|
53
|
+
- name: asChild
|
|
54
|
+
type: boolean
|
|
55
|
+
required: false
|
|
56
|
+
declaredIn: self
|
|
57
|
+
doc: "Render the child element instead of a button (e.g. a router `<Link>`)."
|
|
58
|
+
- name: isActive
|
|
59
|
+
type: boolean
|
|
60
|
+
required: false
|
|
61
|
+
declaredIn: self
|
|
62
|
+
doc: "Marks the current page: sets `aria-current=\"page\"` and the active fill."
|
|
63
|
+
variantKey: true
|
|
64
|
+
- name: size
|
|
65
|
+
type: "\"md\" | \"sm\""
|
|
66
|
+
required: false
|
|
67
|
+
declaredIn: self
|
|
68
|
+
variantKey: true
|
|
69
|
+
PaginationNext:
|
|
70
|
+
- name: asChild
|
|
71
|
+
type: boolean
|
|
72
|
+
required: false
|
|
73
|
+
declaredIn: self
|
|
74
|
+
doc: "Render the child element instead of a button (e.g. a router `<Link>`)."
|
|
75
|
+
- name: size
|
|
76
|
+
type: "\"md\" | \"sm\""
|
|
77
|
+
required: false
|
|
78
|
+
declaredIn: self
|
|
79
|
+
variantKey: true
|
|
80
|
+
PaginationPrevious:
|
|
81
|
+
- name: asChild
|
|
82
|
+
type: boolean
|
|
83
|
+
required: false
|
|
84
|
+
declaredIn: self
|
|
85
|
+
doc: "Render the child element instead of a button (e.g. a router `<Link>`)."
|
|
86
|
+
- name: size
|
|
87
|
+
type: "\"md\" | \"sm\""
|
|
88
|
+
required: false
|
|
89
|
+
declaredIn: self
|
|
90
|
+
variantKey: true
|
|
91
|
+
extendsProps:
|
|
92
|
+
- "React.ComponentPropsWithoutRef<\"nav\">"
|
|
93
|
+
variants:
|
|
94
|
+
isActive:
|
|
95
|
+
kind: boolean
|
|
96
|
+
default: false
|
|
97
|
+
on: Pagination
|
|
98
|
+
size:
|
|
99
|
+
kind: enum
|
|
100
|
+
options:
|
|
101
|
+
- sm
|
|
102
|
+
- md
|
|
103
|
+
default: md
|
|
104
|
+
on: Pagination
|
|
105
|
+
variantSource: tv
|
|
106
|
+
compoundVariants: []
|
|
107
|
+
sizeMaps: []
|
|
108
|
+
dataSlots: []
|
|
109
|
+
dependsOn:
|
|
110
|
+
internal: []
|
|
111
|
+
lib:
|
|
112
|
+
- cn
|
|
113
|
+
hooks: []
|
|
114
|
+
external:
|
|
115
|
+
- "@radix-ui/react-slot"
|
|
116
|
+
- lucide-react
|
|
117
|
+
- react
|
|
118
|
+
- tailwind-variants
|
|
119
|
+
contexts: []
|
|
120
|
+
stories:
|
|
121
|
+
- exportName: Default
|
|
122
|
+
id: components-pagination--default
|
|
123
|
+
- exportName: WithEllipsis
|
|
124
|
+
id: components-pagination--with-ellipsis
|
|
125
|
+
- exportName: Small
|
|
126
|
+
id: components-pagination--small
|
|
127
|
+
- exportName: DisabledEdges
|
|
128
|
+
id: components-pagination--disabled-edges
|
|
129
|
+
storybookTitle: Components/Pagination
|
|
130
|
+
docsId: components-pagination--documentation
|
|
131
|
+
argTypeOptions: {}
|
|
132
|
+
resolvedCleanly: true
|
|
133
|
+
contractHash: "85bf65cd1ffe"
|
|
134
|
+
factsHash: fe8f4aa628e9
|
|
135
|
+
# >>> AUTHORED - humans own everything below this line. <<<
|
|
136
|
+
status: stable
|
|
137
|
+
summary: "Page-range navigation - numbered links, Previous/Next, and an ellipsis for skipped ranges."
|
|
138
|
+
useWhen:
|
|
139
|
+
- "navigating a long, numbered result set (search results, a table, a product grid) where jumping to an arbitrary page is useful"
|
|
140
|
+
- "the list is paged server-side or client-side and the total page count is known up front"
|
|
141
|
+
useInstead:
|
|
142
|
+
- id: data-table
|
|
143
|
+
when: "the paged content already lives inside a DataTable - wire its own pagination footer instead of composing a separate Pagination beside it"
|
|
144
|
+
- id: button
|
|
145
|
+
when: "the affordance is a single 'load more', not numbered pages"
|
|
146
|
+
related:
|
|
147
|
+
- button
|
|
148
|
+
- data-table
|
|
149
|
+
a11y:
|
|
150
|
+
notes:
|
|
151
|
+
- "Pagination sets role=\"navigation\" and aria-label=\"pagination\" on the root nav landmark."
|
|
152
|
+
- "PaginationLink sets aria-current=\"page\" on the active page - that's the only signal a screen reader gets about which page is current, so isActive must always be passed accurately."
|
|
153
|
+
- "PaginationEllipsis is aria-hidden - it's decorative, not a real, focusable page."
|
|
154
|
+
requiredProps:
|
|
155
|
+
- "Every PaginationLink, PaginationPrevious and PaginationNext needs an accessible name - number links get it from their visible text; an icon-only override needs an explicit aria-label."
|
|
156
|
+
keyboard:
|
|
157
|
+
- "Every item is a native button, so Tab moves between them individually - there is no roving tabindex or arrow-key paging like a tablist would provide."
|
|
158
|
+
radixHandles: false
|
|
159
|
+
tokens:
|
|
160
|
+
- "--primary-weaker"
|
|
161
|
+
- "--primary-strong"
|
|
162
|
+
- "--primary-foreground"
|
|
163
|
+
- "--muted"
|
|
164
|
+
- "--foreground"
|
|
165
|
+
- "--button-radius"
|
|
166
|
+
- "--ring"
|
|
167
|
+
pitfalls:
|
|
168
|
+
- "Pagination ships the primitives only, not a page-range calculator - compute which numbers and ellipses to render yourself (see the WithEllipsis story); there is no pageCount prop that does it for you."
|
|
169
|
+
- "PaginationPrevious/PaginationNext don't disable themselves at the edges - pass disabled explicitly when page is 1 or pageCount."
|
|
170
|
+
- "size lives on each PaginationLink/PaginationPrevious/PaginationNext individually, not on the Pagination root - unlike ButtonGroup there is no shared context, so a link left without a matching size silently defaults to md next to sm siblings."
|
|
171
|
+
canonicalSnippet: |
|
|
172
|
+
import {
|
|
173
|
+
Pagination, PaginationContent, PaginationItem, PaginationLink,
|
|
174
|
+
PaginationPrevious, PaginationNext,
|
|
175
|
+
} from "@adres/mgda-ui"
|
|
176
|
+
|
|
177
|
+
<Pagination>
|
|
178
|
+
<PaginationContent>
|
|
179
|
+
<PaginationItem>
|
|
180
|
+
<PaginationPrevious disabled={page === 1} onClick={() => setPage(page - 1)} />
|
|
181
|
+
</PaginationItem>
|
|
182
|
+
{[1, 2, 3].map((n) => (
|
|
183
|
+
<PaginationItem key={n}>
|
|
184
|
+
<PaginationLink isActive={n === page} onClick={() => setPage(n)}>{n}</PaginationLink>
|
|
185
|
+
</PaginationItem>
|
|
186
|
+
))}
|
|
187
|
+
<PaginationItem>
|
|
188
|
+
<PaginationNext disabled={page === 3} onClick={() => setPage(page + 1)} />
|
|
189
|
+
</PaginationItem>
|
|
190
|
+
</PaginationContent>
|
|
191
|
+
</Pagination>
|
|
192
|
+
reviewedAt: 2026-10-16
|
|
193
|
+
reviewedAgainstContractHash: "85bf65cd1ffe"
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Usage
|
|
197
|
+
|
|
198
|
+
`Pagination` is a set of composable parts, not a single opinionated widget:
|
|
199
|
+
`Pagination` (the `nav` landmark), `PaginationContent` (the `ul`), `PaginationItem`
|
|
200
|
+
(each `li`), `PaginationLink` (a page number), `PaginationPrevious`/`PaginationNext`
|
|
201
|
+
(the edge controls), and `PaginationEllipsis` (a decorative truncation marker).
|
|
202
|
+
It follows the shadcn/ui pattern - plain semantic elements styled with design-system
|
|
203
|
+
tokens, no Radix primitive underneath, because page navigation doesn't need
|
|
204
|
+
focus-trapping or portal behaviour, just a nav landmark and native buttons.
|
|
205
|
+
|
|
206
|
+
The component does not compute a page range for you. For a short list, render every
|
|
207
|
+
page number directly. For a long one, decide which numbers and which
|
|
208
|
+
`PaginationEllipsis` markers to show around the current page yourself (the
|
|
209
|
+
`WithEllipsis` story shows a simple "first, current neighbourhood, last" pattern).
|
|
210
|
+
|
|
211
|
+
`size` ("sm" | "md", default "md") is set per `PaginationLink`/`PaginationPrevious`/
|
|
212
|
+
`PaginationNext` - keep it consistent across one `Pagination` instance by passing
|
|
213
|
+
the same value to every item, typically from a variable rather than a shared root
|
|
214
|
+
prop.
|
|
215
|
+
|
|
216
|
+
## Anatomy
|
|
217
|
+
|
|
218
|
+
| Part | Role |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| `Pagination` | The `nav` root. Sets `role="navigation"` and `aria-label="pagination"`. |
|
|
221
|
+
| `PaginationContent` | The `ul` that lays items out in a row. |
|
|
222
|
+
| `PaginationItem` | A plain `li` wrapper around one link, control, or ellipsis. |
|
|
223
|
+
| `PaginationLink` | A page-number button. `isActive` sets `aria-current="page"` and the active fill; supports `asChild` to render a router `Link` instead of a `button`. |
|
|
224
|
+
| `PaginationPrevious` / `PaginationNext` | `PaginationLink` preconfigured with a chevron icon, a default "Previous"/"Next" label (hidden below `sm`, overridable via `children`), and a default `aria-label`. |
|
|
225
|
+
| `PaginationEllipsis` | A decorative, `aria-hidden` "more pages" marker - not a real page. |
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
# >>> DERIVED - regenerated by `npm run knowledge:sync`. Do not edit. <<<
|
|
3
|
+
derived:
|
|
4
|
+
schemaVersion: 1
|
|
5
|
+
id: popover
|
|
6
|
+
sourceFile: packages/design-system/src/components/popover.tsx
|
|
7
|
+
client: true
|
|
8
|
+
category: web-app-overlays
|
|
9
|
+
categoryFromBarrel: web-app-overlays
|
|
10
|
+
barrelExported: true
|
|
11
|
+
importPath: "@adres/mgda-ui"
|
|
12
|
+
exports:
|
|
13
|
+
- name: Popover
|
|
14
|
+
kind: component
|
|
15
|
+
radixSource: "@radix-ui/react-popover#Root"
|
|
16
|
+
typeText: "FC<PopoverProps>"
|
|
17
|
+
- name: PopoverAnchor
|
|
18
|
+
kind: component
|
|
19
|
+
radixSource: "@radix-ui/react-popover#Anchor"
|
|
20
|
+
typeText: "ForwardRefExoticComponent<PopoverAnchorProps & RefAttributes<HTMLDivElement>>"
|
|
21
|
+
- name: PopoverClose
|
|
22
|
+
kind: component
|
|
23
|
+
radixSource: "@radix-ui/react-popover#Close"
|
|
24
|
+
typeText: "ForwardRefExoticComponent<PopoverCloseProps & RefAttributes<HTMLButtonElement>>"
|
|
25
|
+
- name: PopoverContent
|
|
26
|
+
kind: component
|
|
27
|
+
typeText: "ForwardRefExoticComponent<PopoverContentProps & RefAttributes<HTMLDivElement>>"
|
|
28
|
+
- name: PopoverContentProps
|
|
29
|
+
kind: type
|
|
30
|
+
- name: PopoverSize
|
|
31
|
+
kind: type
|
|
32
|
+
- name: PopoverTrigger
|
|
33
|
+
kind: component
|
|
34
|
+
radixSource: "@radix-ui/react-popover#Trigger"
|
|
35
|
+
typeText: "ForwardRefExoticComponent<PopoverTriggerProps & RefAttributes<HTMLButtonElement>>"
|
|
36
|
+
primaryExport: Popover
|
|
37
|
+
subcomponents:
|
|
38
|
+
- PopoverAnchor
|
|
39
|
+
- PopoverClose
|
|
40
|
+
- PopoverContent
|
|
41
|
+
- PopoverTrigger
|
|
42
|
+
props:
|
|
43
|
+
PopoverContent:
|
|
44
|
+
- name: size
|
|
45
|
+
type: "\"lg\" | \"md\" | \"sm\""
|
|
46
|
+
required: false
|
|
47
|
+
declaredIn: self
|
|
48
|
+
variantKey: true
|
|
49
|
+
extendsProps: []
|
|
50
|
+
variants:
|
|
51
|
+
size:
|
|
52
|
+
kind: enum
|
|
53
|
+
options:
|
|
54
|
+
- sm
|
|
55
|
+
- md
|
|
56
|
+
- lg
|
|
57
|
+
default: md
|
|
58
|
+
on: Popover
|
|
59
|
+
variantSource: cva
|
|
60
|
+
compoundVariants: []
|
|
61
|
+
sizeMaps: []
|
|
62
|
+
dataSlots: []
|
|
63
|
+
dependsOn:
|
|
64
|
+
internal: []
|
|
65
|
+
lib:
|
|
66
|
+
- cn
|
|
67
|
+
hooks: []
|
|
68
|
+
external:
|
|
69
|
+
- "@radix-ui/react-popover"
|
|
70
|
+
- class-variance-authority
|
|
71
|
+
- react
|
|
72
|
+
contexts: []
|
|
73
|
+
stories:
|
|
74
|
+
- exportName: Default
|
|
75
|
+
id: components-popover--default
|
|
76
|
+
storybookTitle: Components/Popover
|
|
77
|
+
docsId: components-popover--documentation
|
|
78
|
+
argTypeOptions: {}
|
|
79
|
+
resolvedCleanly: true
|
|
80
|
+
contractHash: "77fe5ab62468"
|
|
81
|
+
factsHash: "56a35d049707"
|
|
82
|
+
# >>> AUTHORED - humans own everything below this line. <<<
|
|
83
|
+
status: stable
|
|
84
|
+
summary: "A click-triggered floating panel that can hold interactive content - forms, buttons, richer context - anchored to a trigger element."
|
|
85
|
+
useWhen:
|
|
86
|
+
- "the trigger needs to reveal interactive content (a form, buttons, custom content) rather than a flat list of actions"
|
|
87
|
+
- "supplementary content should appear next to a specific element, dismissed by clicking outside or pressing Escape"
|
|
88
|
+
useInstead:
|
|
89
|
+
- id: dropdown-menu
|
|
90
|
+
when: "the content is just a list of selectable actions or options"
|
|
91
|
+
- id: tooltip
|
|
92
|
+
when: "the content is a short, non-interactive label shown on hover or focus, not click"
|
|
93
|
+
- id: drawer
|
|
94
|
+
when: "the content needs a full edge-to-edge panel rather than a small anchored one"
|
|
95
|
+
related:
|
|
96
|
+
- dropdown-menu
|
|
97
|
+
- tooltip
|
|
98
|
+
- drawer
|
|
99
|
+
tokens:
|
|
100
|
+
- "--popover"
|
|
101
|
+
- "--popover-foreground"
|
|
102
|
+
- "--border"
|
|
103
|
+
a11y:
|
|
104
|
+
notes:
|
|
105
|
+
- "Wrap the real trigger element with PopoverTrigger asChild so Radix attaches its keyboard and ARIA handling directly to it."
|
|
106
|
+
- "Unlike DropdownMenu, PopoverContent can hold any interactive content (inputs, buttons), and Radix manages focus trapping and Escape-to-close the same way."
|
|
107
|
+
keyboard:
|
|
108
|
+
- "Escape closes the popover and returns focus to the trigger. Tab moves through interactive content inside PopoverContent normally - it is not restricted to a roving-menu pattern the way DropdownMenu is."
|
|
109
|
+
radixHandles: true
|
|
110
|
+
pitfalls:
|
|
111
|
+
- "size only sets width and padding - it does not scale nested typography the way Dialog/Drawer's size drives a type ramp, so headings or body text inside PopoverContent still need their own sizing."
|
|
112
|
+
- "className on PopoverTrigger/PopoverAnchor/PopoverClose cannot be demonstrated in this file's machine-checked canonicalSnippet, because the extractor did not resolve their props from the inlined Radix generics - the same gap as dropdown-menu/drawer. See the fuller example in ## Usage."
|
|
113
|
+
- "PopoverAnchor lets the popover open away from its trigger (e.g. anchor to a table cell while the trigger is a button elsewhere) - most usages do not need it, since PopoverTrigger already anchors by default."
|
|
114
|
+
canonicalSnippet: |
|
|
115
|
+
import { Button, Popover, PopoverContent, PopoverTrigger } from "@adres/mgda-ui"
|
|
116
|
+
|
|
117
|
+
<Popover>
|
|
118
|
+
<PopoverTrigger asChild>
|
|
119
|
+
<Button variant="outline">Open popover</Button>
|
|
120
|
+
</PopoverTrigger>
|
|
121
|
+
<PopoverContent size="md">
|
|
122
|
+
<p>Popover content with helpful context.</p>
|
|
123
|
+
</PopoverContent>
|
|
124
|
+
</Popover>
|
|
125
|
+
reviewedAt: 2026-08-31
|
|
126
|
+
reviewedAgainstContractHash: "77fe5ab62468"
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Usage
|
|
130
|
+
|
|
131
|
+
`Popover` is for interactive or richer content anchored to a trigger element
|
|
132
|
+
- a form, a set of buttons, or custom context - dismissed by clicking outside
|
|
133
|
+
or pressing Escape. Reach for `DropdownMenu` instead when the content is just
|
|
134
|
+
a flat list of selectable actions, and `Tooltip` when it is a short label
|
|
135
|
+
shown on hover rather than click.
|
|
136
|
+
|
|
137
|
+
`size` (`sm`/`md`/`lg`) sets the panel's width and padding only; it does not
|
|
138
|
+
scale nested typography, unlike `Dialog`/`Drawer`. A common alternative is
|
|
139
|
+
overriding width directly via `className`:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<PopoverContent className="ax:w-64">
|
|
143
|
+
<p className="ax:t-body-sm">Popover content with helpful context.</p>
|
|
144
|
+
</PopoverContent>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`className` is not in this file's machine-checked `canonicalSnippet` for the
|
|
148
|
+
same extractor-gap reason noted in `dropdown-menu.md`/`drawer.md` - use the
|
|
149
|
+
example above as the real usage reference.
|
|
150
|
+
|
|
151
|
+
## Anatomy
|
|
152
|
+
|
|
153
|
+
| Part | Role |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `Popover` | Root. Holds open state. A direct Radix `Root` re-export. |
|
|
156
|
+
| `PopoverTrigger` | Opens the popover. Use `asChild` to wrap a real `Button`. |
|
|
157
|
+
| `PopoverAnchor` | Optional - anchors the popover to a different element than its trigger. Rarely needed. |
|
|
158
|
+
| `PopoverContent` | The floating panel. Sets `size`, and renders its own portal and positioning. Can hold any interactive content. |
|
|
159
|
+
| `PopoverClose` | Radix `Close`. Wrap a `Button` with `asChild` to dismiss from inside the content. |
|