@ponti-studios/ui 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +7 -7
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/dist/index.js +1028 -638
- package/dist/index.js.map +1 -1
- package/package.json +30 -32
- package/src/animations.css +16 -434
- package/src/components/data-display/accordion.tsx +10 -7
- package/src/components/data-display/filter-chip.tsx +2 -2
- package/src/components/data-display/index.ts +5 -0
- package/src/components/data-display/metric-card.stories.tsx +48 -0
- package/src/components/data-display/metric-card.tsx +20 -0
- package/src/components/data-display/sort-controls.stories.tsx +45 -0
- package/src/components/data-display/sort-controls.tsx +99 -0
- package/src/components/data-display/sort-row.tsx +74 -0
- package/src/components/data-display/table.tsx +5 -5
- package/src/components/feedback/alert.tsx +4 -4
- package/src/components/feedback/empty-state.tsx +6 -6
- package/src/components/feedback/file-upload-status-badge.stories.tsx +28 -0
- package/src/components/feedback/file-upload-status-badge.tsx +33 -0
- package/src/components/feedback/file-upload-status.stories.tsx +57 -0
- package/src/components/feedback/file-upload-status.tsx +152 -0
- package/src/components/feedback/index.ts +9 -0
- package/src/components/feedback/progress-bar.stories.tsx +39 -0
- package/src/components/feedback/progress-bar.tsx +21 -0
- package/src/components/feedback/progress.stories.tsx +8 -6
- package/src/components/feedback/progress.tsx +1 -1
- package/src/components/feedback/skeleton.stories.tsx +36 -0
- package/src/components/feedback/skeleton.tsx +7 -0
- package/src/components/feedback/spinner.tsx +2 -2
- package/src/components/feedback/update-guard.stories.tsx +32 -0
- package/src/components/feedback/update-guard.tsx +280 -0
- package/src/components/forms/calendar.tsx +11 -11
- package/src/components/forms/date-month-select.stories.tsx +26 -0
- package/src/components/forms/date-month-select.tsx +70 -0
- package/src/components/forms/date-picker.tsx +3 -3
- package/src/components/forms/drop-zone.stories.tsx +56 -0
- package/src/components/forms/drop-zone.tsx +146 -0
- package/src/components/forms/entity-select.stories.tsx +65 -0
- package/src/components/forms/entity-select.tsx +79 -0
- package/src/components/forms/field.tsx +3 -3
- package/src/components/forms/group-by-select.stories.tsx +26 -0
- package/src/components/forms/group-by-select.tsx +47 -0
- package/src/components/forms/index.ts +10 -0
- package/src/components/forms/input.stories.tsx +4 -4
- package/src/components/forms/input.tsx +1 -1
- package/src/components/forms/passkey-management.stories.tsx +81 -0
- package/src/components/forms/passkey-management.tsx +138 -0
- package/src/components/forms/radio-group.tsx +14 -13
- package/src/components/forms/select.stories.tsx +4 -4
- package/src/components/forms/select.tsx +45 -53
- package/src/components/forms/slider.stories.tsx +5 -2
- package/src/components/forms/slider.tsx +27 -17
- package/src/components/forms/stepper.tsx +4 -4
- package/src/components/forms/switch.stories.tsx +1 -1
- package/src/components/forms/switch.tsx +2 -2
- package/src/components/forms/textarea.stories.tsx +2 -2
- package/src/components/forms/textarea.tsx +1 -1
- package/src/components/layout/index.ts +3 -0
- package/src/components/layout/scroll-area.stories.tsx +4 -4
- package/src/components/layout/scroll-area.tsx +23 -21
- package/src/components/layout/section-intro.stories.tsx +29 -0
- package/src/components/layout/section-intro.tsx +31 -0
- package/src/components/layout/surface-panel.stories.tsx +32 -0
- package/src/components/layout/surface-panel.tsx +15 -0
- package/src/components/navigation/app-navigation.stories.tsx +2 -2
- package/src/components/navigation/app-navigation.tsx +13 -13
- package/src/components/navigation/index.ts +4 -0
- package/src/components/navigation/pagination-controls.stories.tsx +41 -0
- package/src/components/navigation/pagination-controls.tsx +50 -0
- package/src/components/navigation/route-link.stories.tsx +30 -0
- package/src/components/navigation/route-link.tsx +42 -0
- package/src/components/navigation/tabs.stories.tsx +5 -5
- package/src/components/navigation/tabs.tsx +7 -7
- package/src/components/overlays/alert-dialog.tsx +21 -46
- package/src/components/overlays/command.tsx +51 -82
- package/src/components/overlays/dialog.tsx +5 -5
- package/src/components/overlays/dropdown-menu.stories.tsx +0 -5
- package/src/components/overlays/dropdown-menu.tsx +44 -142
- package/src/components/overlays/popover.stories.tsx +2 -2
- package/src/components/overlays/popover.tsx +34 -16
- package/src/components/overlays/sheet.stories.tsx +2 -2
- package/src/components/overlays/sheet.tsx +35 -17
- package/src/components/primitives/avatar.tsx +3 -3
- package/src/components/primitives/badge.tsx +7 -11
- package/src/components/primitives/button.tsx +41 -13
- package/src/components/primitives/card.stories.tsx +2 -2
- package/src/components/primitives/card.tsx +2 -2
- package/src/components/primitives/index.ts +2 -0
- package/src/components/primitives/label.stories.tsx +1 -1
- package/src/components/primitives/label.tsx +5 -6
- package/src/components/primitives/status-badge.stories.tsx +52 -0
- package/src/components/primitives/status-badge.tsx +33 -0
- package/src/constants/chart-colors.ts +5 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-sort.ts +20 -0
- package/src/index.ts +30 -11
- package/src/styles.css +76 -1040
- package/src/tokens/color-systems.stories.tsx +9 -119
- package/src/tokens/foundations.stories.tsx +21 -113
- package/src/tokens/generated/dark.colors.generated.ts +56 -0
- package/src/tokens/generated/dark.css +54 -0
- package/src/tokens/generated/foundations.css +148 -0
- package/src/tokens/generated/foundations.generated.ts +443 -0
- package/src/tokens/generated/light.colors.generated.ts +56 -0
- package/src/tokens/generated/light.css +54 -0
- package/src/tokens/index.ts +74 -16
- package/src/tokens/layout.stories.tsx +3 -108
- package/src/tokens/colors.ts +0 -156
- package/src/tokens/motion.ts +0 -51
- package/src/tokens/radii.ts +0 -10
- package/src/tokens/shadows.ts +0 -34
- package/src/tokens/spacing.ts +0 -30
- package/src/tokens/typography.ts +0 -42
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ponti-studios/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared UI component library for ponti-studios labs",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/ponti-studios/labs.git",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
-
"src"
|
|
18
|
+
"src",
|
|
19
|
+
"THIRD_PARTY_NOTICES.md"
|
|
19
20
|
],
|
|
20
21
|
"type": "module",
|
|
21
22
|
"sideEffects": [
|
|
@@ -72,48 +73,41 @@
|
|
|
72
73
|
},
|
|
73
74
|
"dependencies": {
|
|
74
75
|
"@base-ui/react": "^1.6.0",
|
|
75
|
-
"@radix-ui/react-accordion": "^1.2.14",
|
|
76
|
-
"@radix-ui/react-alert-dialog": "^1.1.17",
|
|
77
|
-
"@radix-ui/react-avatar": "^1.2.2",
|
|
78
|
-
"@radix-ui/react-dialog": "^1.1.17",
|
|
79
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
80
|
-
"@radix-ui/react-label": "^2.1.10",
|
|
81
|
-
"@radix-ui/react-popover": "^1.1.19",
|
|
82
|
-
"@radix-ui/react-progress": "^1.1.10",
|
|
83
|
-
"@radix-ui/react-radio-group": "^1.4.1",
|
|
84
|
-
"@radix-ui/react-select": "^2.3.1",
|
|
85
|
-
"@radix-ui/react-slider": "^1.4.1",
|
|
86
|
-
"@radix-ui/react-slot": "^1.3.0",
|
|
87
|
-
"@radix-ui/react-switch": "^1.2.6",
|
|
88
|
-
"@radix-ui/react-tabs": "^1.1.15",
|
|
89
76
|
"class-variance-authority": "^0.7.1",
|
|
90
77
|
"clsx": "^2.1.1",
|
|
91
|
-
"cmdk": "^1.1.1",
|
|
92
78
|
"date-fns": "^4.1.0",
|
|
93
|
-
"framer-motion": "^12.42.
|
|
94
|
-
"lucide-react": "^1.
|
|
95
|
-
"react-day-picker": "^
|
|
79
|
+
"framer-motion": "^12.42.2",
|
|
80
|
+
"lucide-react": "^1.24.0",
|
|
81
|
+
"react-day-picker": "^10.0.1",
|
|
96
82
|
"tailwind-merge": "^3.6.0"
|
|
97
83
|
},
|
|
98
84
|
"devDependencies": {
|
|
99
85
|
"@chromatic-com/storybook": "^5.2.1",
|
|
100
|
-
"@storybook/addon-
|
|
101
|
-
"@storybook/addon-
|
|
102
|
-
"@storybook/addon-
|
|
103
|
-
"@storybook/
|
|
104
|
-
"@
|
|
105
|
-
"@
|
|
86
|
+
"@storybook/addon-a11y": "10.5.3",
|
|
87
|
+
"@storybook/addon-docs": "^10.5.3",
|
|
88
|
+
"@storybook/addon-links": "^10.5.3",
|
|
89
|
+
"@storybook/addon-onboarding": "^10.5.3",
|
|
90
|
+
"@storybook/addon-themes": "10.5.3",
|
|
91
|
+
"@storybook/addon-vitest": "10.5.3",
|
|
92
|
+
"@storybook/react-vite": "^10.5.3",
|
|
93
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
94
|
+
"@types/node": "^26.1.1",
|
|
106
95
|
"@types/react": "^19.2.17",
|
|
107
96
|
"@types/react-dom": "^19.2.3",
|
|
108
97
|
"@vitejs/plugin-react": "^6.0.3",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
98
|
+
"@vitest/browser": "4.1.10",
|
|
99
|
+
"@vitest/browser-playwright": "4.1.10",
|
|
100
|
+
"oxfmt": "0.59.0",
|
|
101
|
+
"oxlint": "^1.74.0",
|
|
102
|
+
"playwright": "^1.61.1",
|
|
111
103
|
"react": "^19.2.7",
|
|
112
104
|
"react-dom": "^19.2.7",
|
|
113
|
-
"storybook": "^10.
|
|
114
|
-
"
|
|
105
|
+
"storybook": "^10.5.3",
|
|
106
|
+
"style-dictionary": "5.5.0",
|
|
107
|
+
"tailwindcss": "^4.3.3",
|
|
115
108
|
"typescript": "6.0.3",
|
|
116
|
-
"vite": "^8.1.
|
|
109
|
+
"vite": "^8.1.5",
|
|
110
|
+
"vitest": "^4.1.10"
|
|
117
111
|
},
|
|
118
112
|
"peerDependencies": {
|
|
119
113
|
"react": "^18.2.0 || ^19.0.0",
|
|
@@ -123,6 +117,10 @@
|
|
|
123
117
|
"build": "vite build",
|
|
124
118
|
"dev": "vite",
|
|
125
119
|
"storybook": "storybook dev -p 6006",
|
|
120
|
+
"check:source": "node tokens/check-source.mjs",
|
|
121
|
+
"test:a11y": "vitest --run --config vitest.config.ts",
|
|
122
|
+
"tokens:build": "style-dictionary build --config style-dictionary.foundations.json && style-dictionary build --config style-dictionary.light.json && style-dictionary build --config style-dictionary.dark.json",
|
|
123
|
+
"tokens:check": "pnpm run tokens:build && node tokens/check-foundations.mjs && git diff --exit-code -- src/tokens/generated",
|
|
126
124
|
"typecheck": "tsc --noEmit",
|
|
127
125
|
"lint": "oxlint src --fix",
|
|
128
126
|
"format": "oxfmt src --write"
|
package/src/animations.css
CHANGED
|
@@ -1,437 +1,19 @@
|
|
|
1
|
-
@
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
@keyframes motion-enter {
|
|
19
|
-
from {
|
|
20
|
-
opacity: 0;
|
|
21
|
-
transform: translateY(6px);
|
|
22
|
-
}
|
|
23
|
-
to {
|
|
24
|
-
opacity: 1;
|
|
25
|
-
transform: translateY(0);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@keyframes motion-exit {
|
|
30
|
-
from {
|
|
31
|
-
opacity: 1;
|
|
32
|
-
transform: translateY(0);
|
|
33
|
-
}
|
|
34
|
-
to {
|
|
35
|
-
opacity: 0;
|
|
36
|
-
transform: translateY(4px);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@keyframes motion-enter-top {
|
|
41
|
-
from {
|
|
42
|
-
opacity: 0;
|
|
43
|
-
transform: translateY(-6px);
|
|
44
|
-
}
|
|
45
|
-
to {
|
|
46
|
-
opacity: 1;
|
|
47
|
-
transform: translateY(0);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@keyframes motion-exit-top {
|
|
52
|
-
from {
|
|
53
|
-
opacity: 1;
|
|
54
|
-
transform: translateY(0);
|
|
55
|
-
}
|
|
56
|
-
to {
|
|
57
|
-
opacity: 0;
|
|
58
|
-
transform: translateY(-4px);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@keyframes motion-enter-bottom {
|
|
63
|
-
from {
|
|
64
|
-
opacity: 0;
|
|
65
|
-
transform: translateY(6px);
|
|
66
|
-
}
|
|
67
|
-
to {
|
|
68
|
-
opacity: 1;
|
|
69
|
-
transform: translateY(0);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@keyframes motion-exit-bottom {
|
|
74
|
-
from {
|
|
75
|
-
opacity: 1;
|
|
76
|
-
transform: translateY(0);
|
|
77
|
-
}
|
|
78
|
-
to {
|
|
79
|
-
opacity: 0;
|
|
80
|
-
transform: translateY(6px);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@keyframes motion-enter-left {
|
|
85
|
-
from {
|
|
86
|
-
opacity: 0;
|
|
87
|
-
transform: translateX(-6px);
|
|
88
|
-
}
|
|
89
|
-
to {
|
|
90
|
-
opacity: 1;
|
|
91
|
-
transform: translateX(0);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
@keyframes motion-exit-left {
|
|
96
|
-
from {
|
|
97
|
-
opacity: 1;
|
|
98
|
-
transform: translateX(0);
|
|
99
|
-
}
|
|
100
|
-
to {
|
|
101
|
-
opacity: 0;
|
|
102
|
-
transform: translateX(-4px);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@keyframes motion-enter-right {
|
|
107
|
-
from {
|
|
108
|
-
opacity: 0;
|
|
109
|
-
transform: translateX(6px);
|
|
110
|
-
}
|
|
111
|
-
to {
|
|
112
|
-
opacity: 1;
|
|
113
|
-
transform: translateX(0);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
@keyframes motion-exit-right {
|
|
118
|
-
from {
|
|
119
|
-
opacity: 1;
|
|
120
|
-
transform: translateX(0);
|
|
121
|
-
}
|
|
122
|
-
to {
|
|
123
|
-
opacity: 0;
|
|
124
|
-
transform: translateX(4px);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/*
|
|
129
|
-
* Sheet/drawer slide — full off-canvas travel (not the 4-6px "nudge" used by
|
|
130
|
-
* the motion-enter/exit-* keyframes above, which are meant for small popovers).
|
|
131
|
-
* Gated on the real [data-state] attribute so open/closed never race — see
|
|
132
|
-
* the note above the data-state fade/zoom rules for why that matters here.
|
|
133
|
-
*/
|
|
134
|
-
@keyframes sheet-slide-in-from-right {
|
|
135
|
-
from {
|
|
136
|
-
transform: translateX(100%);
|
|
137
|
-
}
|
|
138
|
-
to {
|
|
139
|
-
transform: translateX(0);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
@keyframes sheet-slide-out-to-right {
|
|
143
|
-
from {
|
|
144
|
-
transform: translateX(0);
|
|
145
|
-
}
|
|
146
|
-
to {
|
|
147
|
-
transform: translateX(100%);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
@keyframes sheet-slide-in-from-bottom {
|
|
151
|
-
from {
|
|
152
|
-
transform: translateY(100%);
|
|
153
|
-
}
|
|
154
|
-
to {
|
|
155
|
-
transform: translateY(0);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
@keyframes sheet-slide-out-to-bottom {
|
|
159
|
-
from {
|
|
160
|
-
transform: translateY(0);
|
|
161
|
-
}
|
|
162
|
-
to {
|
|
163
|
-
transform: translateY(100%);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.sheet-responsive[data-state="open"] {
|
|
168
|
-
animation: sheet-slide-in-from-bottom 480ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
169
|
-
}
|
|
170
|
-
.sheet-responsive[data-state="closed"] {
|
|
171
|
-
animation: sheet-slide-out-to-bottom 280ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
@media (min-width: 768px) {
|
|
175
|
-
.sheet-responsive[data-state="open"] {
|
|
176
|
-
animation-name: sheet-slide-in-from-right;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.sheet-responsive[data-state="closed"] {
|
|
180
|
-
animation-name: sheet-slide-out-to-right;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@media (prefers-reduced-motion: reduce) {
|
|
185
|
-
.sheet-responsive {
|
|
186
|
-
animation: none !important;
|
|
187
|
-
transform: none !important;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
@keyframes motion-breezy-wave {
|
|
192
|
-
0% {
|
|
193
|
-
transform: translateY(0);
|
|
194
|
-
opacity: 0.9;
|
|
195
|
-
}
|
|
196
|
-
40% {
|
|
197
|
-
transform: translateY(-4px);
|
|
198
|
-
opacity: 1;
|
|
199
|
-
}
|
|
200
|
-
70% {
|
|
201
|
-
transform: translateY(2px);
|
|
202
|
-
opacity: 0.92;
|
|
203
|
-
}
|
|
204
|
-
100% {
|
|
205
|
-
transform: translateY(0);
|
|
206
|
-
opacity: 0.9;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
@keyframes motion-breezy-stream {
|
|
211
|
-
0% {
|
|
212
|
-
transform: translateX(-100%);
|
|
213
|
-
}
|
|
214
|
-
100% {
|
|
215
|
-
transform: translateX(100%);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.motion-anim-enter {
|
|
220
|
-
animation: motion-enter 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
221
|
-
}
|
|
222
|
-
.motion-anim-exit {
|
|
223
|
-
animation: motion-exit 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
224
|
-
}
|
|
225
|
-
.motion-anim-enter-top {
|
|
226
|
-
animation: motion-enter-top 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
227
|
-
}
|
|
228
|
-
.motion-anim-exit-top {
|
|
229
|
-
animation: motion-exit-top 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
230
|
-
}
|
|
231
|
-
.motion-anim-enter-bottom {
|
|
232
|
-
animation: motion-enter-bottom 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
233
|
-
}
|
|
234
|
-
.motion-anim-exit-bottom {
|
|
235
|
-
animation: motion-exit-bottom 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
236
|
-
}
|
|
237
|
-
.motion-anim-enter-left {
|
|
238
|
-
animation: motion-enter-left 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
239
|
-
}
|
|
240
|
-
.motion-anim-exit-left {
|
|
241
|
-
animation: motion-exit-left 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
242
|
-
}
|
|
243
|
-
.motion-anim-enter-right {
|
|
244
|
-
animation: motion-enter-right 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
245
|
-
}
|
|
246
|
-
.motion-anim-exit-right {
|
|
247
|
-
animation: motion-exit-right 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
.motion-anim-breezy {
|
|
251
|
-
animation: motion-breezy-wave 320ms cubic-bezier(0.32, 0.72, 0.28, 1) forwards;
|
|
252
|
-
}
|
|
253
|
-
.motion-anim-breezy-loop {
|
|
254
|
-
animation: motion-breezy-wave 1.8s cubic-bezier(0.32, 0.72, 0.28, 1) infinite;
|
|
255
|
-
}
|
|
256
|
-
.motion-anim-breezy-progress {
|
|
257
|
-
animation: motion-breezy-stream 1.8s linear infinite;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.motion-anim-breezy-stagger > * {
|
|
261
|
-
animation: motion-breezy-wave 1.8s cubic-bezier(0.32, 0.72, 0.28, 1) infinite;
|
|
262
|
-
}
|
|
263
|
-
.motion-anim-breezy-stagger > *:nth-child(1) {
|
|
264
|
-
animation-delay: 0ms;
|
|
265
|
-
}
|
|
266
|
-
.motion-anim-breezy-stagger > *:nth-child(2) {
|
|
267
|
-
animation-delay: 120ms;
|
|
268
|
-
}
|
|
269
|
-
.motion-anim-breezy-stagger > *:nth-child(3) {
|
|
270
|
-
animation-delay: 240ms;
|
|
271
|
-
}
|
|
272
|
-
.motion-anim-breezy-stagger > *:nth-child(4) {
|
|
273
|
-
animation-delay: 360ms;
|
|
274
|
-
}
|
|
275
|
-
.motion-anim-breezy-stagger > *:nth-child(5) {
|
|
276
|
-
animation-delay: 480ms;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
@keyframes accordion-down {
|
|
280
|
-
from {
|
|
281
|
-
opacity: 0;
|
|
282
|
-
transform: scaleY(0.8);
|
|
283
|
-
transform-origin: top;
|
|
284
|
-
}
|
|
285
|
-
to {
|
|
286
|
-
opacity: 1;
|
|
287
|
-
transform: scaleY(1);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
@keyframes accordion-up {
|
|
292
|
-
from {
|
|
293
|
-
opacity: 1;
|
|
294
|
-
transform: scaleY(1);
|
|
295
|
-
transform-origin: top;
|
|
296
|
-
}
|
|
297
|
-
to {
|
|
298
|
-
opacity: 0;
|
|
299
|
-
transform: scaleY(0.8);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.animate-accordion-down,
|
|
304
|
-
[data-state="open"].animate-accordion-down {
|
|
305
|
-
animation: accordion-down 400ms cubic-bezier(0.32, 0.72, 0.28, 1) both;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
.animate-accordion-up,
|
|
309
|
-
[data-state="closed"].animate-accordion-up {
|
|
310
|
-
animation: accordion-up 300ms cubic-bezier(0.32, 0.72, 0.28, 1) both;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
.animate-in {
|
|
314
|
-
animation: motion-enter 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
315
|
-
}
|
|
316
|
-
.animate-out {
|
|
317
|
-
animation: motion-exit 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
318
|
-
}
|
|
319
|
-
.fade-in {
|
|
320
|
-
animation: motion-enter 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
321
|
-
}
|
|
322
|
-
.fade-out {
|
|
323
|
-
animation: motion-exit 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/*
|
|
327
|
-
* These utility classes exist alongside their opposite (open + closed, or
|
|
328
|
-
* visible + hidden) on the same element at all times — only the `data-state`
|
|
329
|
-
* attribute value actually changes between renders. Each selector below must
|
|
330
|
-
* be qualified with the matching `[data-state="..."]` attribute so only the
|
|
331
|
-
* currently-active variant applies; without it, cascade order alone decides
|
|
332
|
-
* the winner (whichever rule is declared last), regardless of real state.
|
|
333
|
-
*/
|
|
334
|
-
[data-state="open"].data-\[state\=open\]\:animate-in {
|
|
335
|
-
animation: motion-enter 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
336
|
-
}
|
|
337
|
-
[data-state="closed"].data-\[state\=closed\]\:animate-out {
|
|
338
|
-
animation: motion-exit 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
[data-state="open"].data-\[state\=open\]\:fade-in-0,
|
|
342
|
-
[data-state="open"].data-\[state\=open\]\:fade-in-90,
|
|
343
|
-
[data-state="visible"].data-\[state\=visible\]\:fade-in {
|
|
344
|
-
animation: motion-enter 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
[data-state="closed"].data-\[state\=closed\]\:fade-out-0,
|
|
348
|
-
[data-state="hidden"].data-\[state\=hidden\]\:fade-out {
|
|
349
|
-
animation: motion-exit 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
[data-state="open"].data-\[state\=open\]\:zoom-in-90,
|
|
353
|
-
[data-state="open"].data-\[state\=open\]\:zoom-in-95 {
|
|
354
|
-
animation: motion-enter 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
355
|
-
}
|
|
356
|
-
[data-state="closed"].data-\[state\=closed\]\:zoom-out-95 {
|
|
357
|
-
animation: motion-exit 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/*
|
|
361
|
-
* Same issue as the data-state pairs above: Popover/Tooltip/DropdownMenu
|
|
362
|
-
* content applies all four data-[side=...] classes simultaneously (only the
|
|
363
|
-
* real `data-side` attribute changes at runtime), so each selector must be
|
|
364
|
-
* qualified with the matching [data-side="..."] attribute — otherwise
|
|
365
|
-
* cascade order alone picks a winner regardless of actual placement.
|
|
366
|
-
*/
|
|
367
|
-
[data-side="bottom"].data-\[side\=bottom\]\:slide-in-from-top-2 {
|
|
368
|
-
animation: motion-enter-top 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
369
|
-
}
|
|
370
|
-
[data-side="top"].data-\[side\=top\]\:slide-in-from-bottom-2 {
|
|
371
|
-
animation: motion-enter-bottom 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
372
|
-
}
|
|
373
|
-
[data-side="left"].data-\[side\=left\]\:slide-in-from-right-2 {
|
|
374
|
-
animation: motion-enter-right 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
375
|
-
}
|
|
376
|
-
[data-side="right"].data-\[side\=right\]\:slide-in-from-left-2 {
|
|
377
|
-
animation: motion-enter-left 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.slide-in-from-bottom-10,
|
|
381
|
-
.slide-in-from-bottom-0 {
|
|
382
|
-
animation: motion-enter-bottom 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
383
|
-
}
|
|
384
|
-
.slide-in-from-left-52 {
|
|
385
|
-
animation: motion-enter-left 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
386
|
-
}
|
|
387
|
-
.slide-in-from-right-52 {
|
|
388
|
-
animation: motion-enter-right 150ms cubic-bezier(0, 0, 0.2, 1) both;
|
|
389
|
-
}
|
|
390
|
-
.slide-out-to-left-52 {
|
|
391
|
-
animation: motion-exit-left 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
392
|
-
}
|
|
393
|
-
.slide-out-to-right-52 {
|
|
394
|
-
animation: motion-exit-right 120ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
.note-picker-panel {
|
|
398
|
-
transition:
|
|
399
|
-
opacity 150ms cubic-bezier(0, 0, 0.2, 1),
|
|
400
|
-
transform 150ms cubic-bezier(0, 0, 0.2, 1),
|
|
401
|
-
overlay 150ms allow-discrete,
|
|
402
|
-
display 150ms allow-discrete;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
.note-picker-panel[open] {
|
|
406
|
-
opacity: 1;
|
|
407
|
-
transform: translateY(0) scale(1);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
@starting-style {
|
|
411
|
-
.note-picker-panel[open] {
|
|
412
|
-
opacity: 0;
|
|
413
|
-
transform: translateY(8px) scale(0.98);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/*
|
|
418
|
-
* View Transitions: opt-in only.
|
|
419
|
-
* The browser's default root crossfade animates every diffed pixel of the
|
|
420
|
-
* whole page using mix-blend-mode: plus-lighter, which visibly flashes white
|
|
421
|
-
* on any element whose color changes during the transition (e.g. a nav item
|
|
422
|
-
* going from inactive to active). Disabling the root group's animation makes
|
|
423
|
-
* transitions opt-in — only elements with an explicit view-transition-name
|
|
424
|
-
* (set inline via style.viewTransitionName) animate; everything else swaps
|
|
425
|
-
* instantly with no crossfade artifact.
|
|
426
|
-
*/
|
|
427
|
-
::view-transition-group(root) {
|
|
428
|
-
animation-duration: 0s;
|
|
429
|
-
}
|
|
1
|
+
@keyframes accordion-down { from { height: 0; } to { height: var(--accordion-content-height); } }
|
|
2
|
+
@keyframes accordion-up { from { height: var(--accordion-content-height); } to { height: 0; } }
|
|
3
|
+
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
4
|
+
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
|
|
5
|
+
@keyframes zoom-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
|
|
6
|
+
@keyframes zoom-out { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }
|
|
7
|
+
|
|
8
|
+
.animate-in { animation: fade-in 150ms ease-out, zoom-in 150ms ease-out; }
|
|
9
|
+
.animate-out { animation: fade-out 120ms ease-in, zoom-out 120ms ease-in; }
|
|
10
|
+
.fade-in-0 { animation-name: fade-in; }
|
|
11
|
+
.fade-out-0 { animation-name: fade-out; }
|
|
12
|
+
.zoom-in-95 { animation-name: zoom-in; }
|
|
13
|
+
.zoom-out-95 { animation-name: zoom-out; }
|
|
14
|
+
.data-open\:animate-in[data-open], .data-open\:animate-in[data-state="open"] { animation: fade-in 150ms ease-out, zoom-in 150ms ease-out; }
|
|
15
|
+
.data-closed\:animate-out[data-closed], .data-closed\:animate-out[data-state="closed"] { animation: fade-out 120ms ease-in, zoom-out 120ms ease-in; }
|
|
430
16
|
|
|
431
17
|
@media (prefers-reduced-motion: reduce) {
|
|
432
|
-
|
|
433
|
-
::view-transition-old(*),
|
|
434
|
-
::view-transition-new(*) {
|
|
435
|
-
animation: none !important;
|
|
436
|
-
}
|
|
18
|
+
.animate-in, .animate-out, [data-open], [data-closed] { animation: none !important; }
|
|
437
19
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
|
|
2
2
|
import { ChevronDown } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
5
|
import { cn } from "../../lib/utils";
|
|
6
6
|
|
|
7
|
-
function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>
|
|
8
|
-
|
|
7
|
+
function Accordion({ type, collapsible: _collapsible, ...props }: React.ComponentProps<typeof AccordionPrimitive.Root> & {
|
|
8
|
+
type?: "single" | "multiple";
|
|
9
|
+
collapsible?: boolean;
|
|
10
|
+
}) {
|
|
11
|
+
return <AccordionPrimitive.Root data-slot="accordion" multiple={type === "multiple"} {...props} className="w-full" />;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
function AccordionItem({
|
|
@@ -16,7 +19,7 @@ function AccordionItem({
|
|
|
16
19
|
<AccordionPrimitive.Item
|
|
17
20
|
data-slot="accordion-item"
|
|
18
21
|
className={cn(
|
|
19
|
-
"
|
|
22
|
+
"mb-2 overflow-hidden rounded-md border last:mb-0",
|
|
20
23
|
className,
|
|
21
24
|
)}
|
|
22
25
|
{...props}
|
|
@@ -50,15 +53,15 @@ function AccordionContent({
|
|
|
50
53
|
className,
|
|
51
54
|
children,
|
|
52
55
|
...props
|
|
53
|
-
}: React.ComponentProps<typeof AccordionPrimitive.
|
|
56
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Panel>) {
|
|
54
57
|
return (
|
|
55
|
-
<AccordionPrimitive.
|
|
58
|
+
<AccordionPrimitive.Panel
|
|
56
59
|
data-slot="accordion-content"
|
|
57
60
|
className="animate-accordion-down overflow-hidden text-sm"
|
|
58
61
|
{...props}
|
|
59
62
|
>
|
|
60
63
|
<div className={cn("px-4 pt-2 pb-3", className)}>{children}</div>
|
|
61
|
-
</AccordionPrimitive.
|
|
64
|
+
</AccordionPrimitive.Panel>
|
|
62
65
|
);
|
|
63
66
|
}
|
|
64
67
|
|
|
@@ -23,14 +23,14 @@ export function FilterChip({ label, onRemove, onClick, className }: FilterChipPr
|
|
|
23
23
|
return (
|
|
24
24
|
<span
|
|
25
25
|
className={cn(
|
|
26
|
-
"border-
|
|
26
|
+
"border-border bg-secondary text-secondary-foreground inline-flex items-center rounded-full border text-sm font-medium",
|
|
27
27
|
className,
|
|
28
28
|
)}
|
|
29
29
|
>
|
|
30
30
|
{labelContent}
|
|
31
31
|
<button
|
|
32
32
|
type="button"
|
|
33
|
-
className="text-
|
|
33
|
+
className="text-muted-foreground hover:text-foreground mr-1 flex size-10 items-center justify-center rounded-full"
|
|
34
34
|
onClick={onRemove}
|
|
35
35
|
aria-label={`Remove filter: ${typeof label === "string" ? label : "selected filter"}`}
|
|
36
36
|
>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion";
|
|
2
2
|
export { FilterChip } from "./filter-chip";
|
|
3
3
|
export type { FilterChipProps } from "./filter-chip";
|
|
4
|
+
export { MetricCard } from "./metric-card";
|
|
5
|
+
export { SortControls } from "./sort-controls";
|
|
6
|
+
export type { SortControlsProps } from "./sort-controls";
|
|
7
|
+
export { SortRow } from "./sort-row";
|
|
8
|
+
export type { SortRowProps } from "./sort-row";
|
|
4
9
|
export {
|
|
5
10
|
Table,
|
|
6
11
|
TableBody,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
|
|
3
|
+
import { MetricCard } from "./metric-card";
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "DataDisplay/MetricCard",
|
|
7
|
+
component: MetricCard,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
} satisfies Meta<typeof MetricCard>;
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
label: "Net worth",
|
|
17
|
+
value: "$48,210",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const WithChange: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
label: "Monthly spend",
|
|
24
|
+
value: "$3,120",
|
|
25
|
+
change: "+4.2% vs last month",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const Empty: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
label: "Runway",
|
|
32
|
+
value: undefined,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Grid: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
label: "Net worth",
|
|
39
|
+
value: "$48,210",
|
|
40
|
+
},
|
|
41
|
+
render: () => (
|
|
42
|
+
<div className="grid w-[520px] grid-cols-3 gap-3">
|
|
43
|
+
<MetricCard label="Net worth" value="$48,210" change="+2.1%" />
|
|
44
|
+
<MetricCard label="Income" value="$6,400" />
|
|
45
|
+
<MetricCard label="Expenses" value="$3,120" change="-1.4%" />
|
|
46
|
+
</div>
|
|
47
|
+
),
|
|
48
|
+
};
|