@ponti-studios/ui 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/index.js +2118 -0
- package/dist/index.js.map +1 -0
- package/package.json +130 -0
- package/src/animations.css +437 -0
- package/src/components/data-display/accordion.stories.tsx +57 -0
- package/src/components/data-display/accordion.tsx +65 -0
- package/src/components/data-display/filter-chip.tsx +41 -0
- package/src/components/data-display/index.ts +13 -0
- package/src/components/data-display/table.stories.tsx +75 -0
- package/src/components/data-display/table.tsx +84 -0
- package/src/components/feedback/alert.stories.tsx +51 -0
- package/src/components/feedback/alert.tsx +60 -0
- package/src/components/feedback/empty-state.tsx +66 -0
- package/src/components/feedback/index.ts +6 -0
- package/src/components/feedback/progress.stories.tsx +37 -0
- package/src/components/feedback/progress.tsx +30 -0
- package/src/components/feedback/spinner.stories.tsx +52 -0
- package/src/components/feedback/spinner.tsx +48 -0
- package/src/components/forms/calendar.stories.tsx +30 -0
- package/src/components/forms/calendar.tsx +67 -0
- package/src/components/forms/date-picker.stories.tsx +48 -0
- package/src/components/forms/date-picker.tsx +60 -0
- package/src/components/forms/field.stories.tsx +62 -0
- package/src/components/forms/field.tsx +65 -0
- package/src/components/forms/field.types.ts +7 -0
- package/src/components/forms/index.ts +24 -0
- package/src/components/forms/input.stories.tsx +47 -0
- package/src/components/forms/input.tsx +20 -0
- package/src/components/forms/radio-group.stories.tsx +48 -0
- package/src/components/forms/radio-group.tsx +38 -0
- package/src/components/forms/select.stories.tsx +88 -0
- package/src/components/forms/select.tsx +147 -0
- package/src/components/forms/slider.stories.tsx +67 -0
- package/src/components/forms/slider.tsx +25 -0
- package/src/components/forms/stepper.stories.tsx +111 -0
- package/src/components/forms/stepper.tsx +66 -0
- package/src/components/forms/switch.stories.tsx +144 -0
- package/src/components/forms/switch.tsx +36 -0
- package/src/components/forms/text-field.stories.tsx +87 -0
- package/src/components/forms/text-field.tsx +40 -0
- package/src/components/forms/text-field.types.ts +9 -0
- package/src/components/forms/textarea.stories.tsx +150 -0
- package/src/components/forms/textarea.tsx +18 -0
- package/src/components/layout/index.ts +2 -0
- package/src/components/layout/scroll-area.stories.tsx +83 -0
- package/src/components/layout/scroll-area.tsx +40 -0
- package/src/components/navigation/app-navigation.stories.tsx +81 -0
- package/src/components/navigation/app-navigation.tsx +155 -0
- package/src/components/navigation/index.ts +3 -0
- package/src/components/navigation/tabs.stories.tsx +69 -0
- package/src/components/navigation/tabs.tsx +49 -0
- package/src/components/overlays/alert-dialog.stories.tsx +45 -0
- package/src/components/overlays/alert-dialog.tsx +119 -0
- package/src/components/overlays/command.stories.tsx +65 -0
- package/src/components/overlays/command.tsx +130 -0
- package/src/components/overlays/dialog.stories.tsx +111 -0
- package/src/components/overlays/dialog.tsx +162 -0
- package/src/components/overlays/dropdown-menu.stories.tsx +79 -0
- package/src/components/overlays/dropdown-menu.tsx +214 -0
- package/src/components/overlays/index.ts +65 -0
- package/src/components/overlays/popover.stories.tsx +39 -0
- package/src/components/overlays/popover.tsx +40 -0
- package/src/components/overlays/sheet.stories.tsx +68 -0
- package/src/components/overlays/sheet.tsx +98 -0
- package/src/components/primitives/avatar.stories.tsx +71 -0
- package/src/components/primitives/avatar.tsx +132 -0
- package/src/components/primitives/badge.stories.tsx +33 -0
- package/src/components/primitives/badge.tsx +44 -0
- package/src/components/primitives/button.stories.tsx +113 -0
- package/src/components/primitives/button.tsx +95 -0
- package/src/components/primitives/button.types.ts +10 -0
- package/src/components/primitives/card.stories.tsx +59 -0
- package/src/components/primitives/card.tsx +69 -0
- package/src/components/primitives/index.ts +13 -0
- package/src/components/primitives/label.stories.tsx +47 -0
- package/src/components/primitives/label.tsx +21 -0
- package/src/constants/chart-colors.ts +39 -0
- package/src/env.d.ts +9 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/sort.types.ts +12 -0
- package/src/hooks/use-api-client.ts +122 -0
- package/src/hooks/use-debounced-value.ts +12 -0
- package/src/hooks/use-filter-state.ts +68 -0
- package/src/hooks/use-media-query.ts +65 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/index.ts +25 -0
- package/src/lib/auth.ts +5 -0
- package/src/lib/clipboard.ts +8 -0
- package/src/lib/create-memory-storage.ts +24 -0
- package/src/lib/index.ts +4 -0
- package/src/lib/utils.ts +6 -0
- package/src/storybook/controls.ts +117 -0
- package/src/storybook/options.ts +52 -0
- package/src/styles.css +1052 -0
- package/src/tokens/color-systems.stories.tsx +133 -0
- package/src/tokens/colors.ts +156 -0
- package/src/tokens/foundations.stories.tsx +151 -0
- package/src/tokens/index.ts +16 -0
- package/src/tokens/layout.stories.tsx +118 -0
- package/src/tokens/motion.ts +51 -0
- package/src/tokens/radii.ts +10 -0
- package/src/tokens/shadows.ts +34 -0
- package/src/tokens/spacing.ts +30 -0
- package/src/tokens/typography.ts +42 -0
- package/src/translations/en.ts +34 -0
- package/src/translations/index.ts +32 -0
- package/src/types/upload.ts +42 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2
|
+
.motion-anim-enter,
|
|
3
|
+
.motion-anim-exit,
|
|
4
|
+
.motion-anim-enter-top,
|
|
5
|
+
.motion-anim-enter-bottom,
|
|
6
|
+
.motion-anim-enter-left,
|
|
7
|
+
.motion-anim-enter-right,
|
|
8
|
+
.motion-anim-breezy,
|
|
9
|
+
.motion-anim-breezy-loop,
|
|
10
|
+
.motion-anim-breezy-progress,
|
|
11
|
+
.motion-anim-breezy-stagger > * {
|
|
12
|
+
animation: none !important;
|
|
13
|
+
opacity: 1 !important;
|
|
14
|
+
transform: none !important;
|
|
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
|
+
}
|
|
430
|
+
|
|
431
|
+
@media (prefers-reduced-motion: reduce) {
|
|
432
|
+
::view-transition-group(*),
|
|
433
|
+
::view-transition-old(*),
|
|
434
|
+
::view-transition-new(*) {
|
|
435
|
+
animation: none !important;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
|
|
3
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion";
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "DataDisplay/Accordion",
|
|
7
|
+
component: Accordion,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
args: {
|
|
10
|
+
type: "single",
|
|
11
|
+
},
|
|
12
|
+
} satisfies Meta<typeof Accordion>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
render: () => (
|
|
19
|
+
<Accordion type="single" collapsible className="w-full">
|
|
20
|
+
<AccordionItem value="item-1">
|
|
21
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
22
|
+
<AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
|
|
23
|
+
</AccordionItem>
|
|
24
|
+
<AccordionItem value="item-2">
|
|
25
|
+
<AccordionTrigger>Is it styled?</AccordionTrigger>
|
|
26
|
+
<AccordionContent>
|
|
27
|
+
Yes. It comes with default styles that match the other components.
|
|
28
|
+
</AccordionContent>
|
|
29
|
+
</AccordionItem>
|
|
30
|
+
<AccordionItem value="item-3">
|
|
31
|
+
<AccordionTrigger>Is it animated?</AccordionTrigger>
|
|
32
|
+
<AccordionContent>
|
|
33
|
+
Yes. It's animated by default, but you can disable it if you prefer.
|
|
34
|
+
</AccordionContent>
|
|
35
|
+
</AccordionItem>
|
|
36
|
+
</Accordion>
|
|
37
|
+
),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const Multiple: Story = {
|
|
41
|
+
render: () => (
|
|
42
|
+
<Accordion type="multiple" className="w-full max-w-md">
|
|
43
|
+
<AccordionItem value="item-1">
|
|
44
|
+
<AccordionTrigger>First item</AccordionTrigger>
|
|
45
|
+
<AccordionContent>Content for the first item.</AccordionContent>
|
|
46
|
+
</AccordionItem>
|
|
47
|
+
<AccordionItem value="item-2">
|
|
48
|
+
<AccordionTrigger>Second item</AccordionTrigger>
|
|
49
|
+
<AccordionContent>Content for the second item.</AccordionContent>
|
|
50
|
+
</AccordionItem>
|
|
51
|
+
<AccordionItem value="item-3">
|
|
52
|
+
<AccordionTrigger>Third item</AccordionTrigger>
|
|
53
|
+
<AccordionContent>Content for the third item.</AccordionContent>
|
|
54
|
+
</AccordionItem>
|
|
55
|
+
</Accordion>
|
|
56
|
+
),
|
|
57
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
2
|
+
import { ChevronDown } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
|
|
8
|
+
return <AccordionPrimitive.Root data-slot="accordion" {...props} className="w-full" />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function AccordionItem({
|
|
12
|
+
className,
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
|
|
15
|
+
return (
|
|
16
|
+
<AccordionPrimitive.Item
|
|
17
|
+
data-slot="accordion-item"
|
|
18
|
+
className={cn(
|
|
19
|
+
"border-default bg-canvas mb-2 overflow-hidden rounded-md border last:mb-0",
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function AccordionTrigger({
|
|
28
|
+
className,
|
|
29
|
+
children,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
|
32
|
+
return (
|
|
33
|
+
<AccordionPrimitive.Header className="flex">
|
|
34
|
+
<AccordionPrimitive.Trigger
|
|
35
|
+
data-slot="accordion-trigger"
|
|
36
|
+
className={cn(
|
|
37
|
+
"flex w-full flex-1 items-center justify-between gap-4 px-4 py-3 text-left text-sm font-semibold disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
<ChevronDown className="pointer-events-none size-4 shrink-0 transition-transform duration-200" />
|
|
44
|
+
</AccordionPrimitive.Trigger>
|
|
45
|
+
</AccordionPrimitive.Header>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function AccordionContent({
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
...props
|
|
53
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
|
|
54
|
+
return (
|
|
55
|
+
<AccordionPrimitive.Content
|
|
56
|
+
data-slot="accordion-content"
|
|
57
|
+
className="animate-accordion-down overflow-hidden text-sm"
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
<div className={cn("px-4 pt-2 pb-3", className)}>{children}</div>
|
|
61
|
+
</AccordionPrimitive.Content>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { X } from "lucide-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
|
|
6
|
+
export interface FilterChipProps {
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
onRemove: () => void;
|
|
9
|
+
onClick?: () => void;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** An applied filter with an optional edit action and an explicit remove action. */
|
|
14
|
+
export function FilterChip({ label, onRemove, onClick, className }: FilterChipProps) {
|
|
15
|
+
const labelContent = onClick ? (
|
|
16
|
+
<button type="button" className="min-h-11 px-3 text-left" onClick={onClick}>
|
|
17
|
+
{label}
|
|
18
|
+
</button>
|
|
19
|
+
) : (
|
|
20
|
+
<span className="flex min-h-11 items-center px-3">{label}</span>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<span
|
|
25
|
+
className={cn(
|
|
26
|
+
"border-subtle bg-panel text-primary inline-flex items-center rounded-full border text-sm font-medium",
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
>
|
|
30
|
+
{labelContent}
|
|
31
|
+
<button
|
|
32
|
+
type="button"
|
|
33
|
+
className="text-secondary hover:text-primary mr-1 flex size-10 items-center justify-center rounded-full"
|
|
34
|
+
onClick={onRemove}
|
|
35
|
+
aria-label={`Remove filter: ${typeof label === "string" ? label : "selected filter"}`}
|
|
36
|
+
>
|
|
37
|
+
<X className="size-4" aria-hidden />
|
|
38
|
+
</button>
|
|
39
|
+
</span>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion";
|
|
2
|
+
export { FilterChip } from "./filter-chip";
|
|
3
|
+
export type { FilterChipProps } from "./filter-chip";
|
|
4
|
+
export {
|
|
5
|
+
Table,
|
|
6
|
+
TableBody,
|
|
7
|
+
TableCaption,
|
|
8
|
+
TableCell,
|
|
9
|
+
TableFooter,
|
|
10
|
+
TableHead,
|
|
11
|
+
TableHeader,
|
|
12
|
+
TableRow,
|
|
13
|
+
} from "./table";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Table,
|
|
5
|
+
TableBody,
|
|
6
|
+
TableCaption,
|
|
7
|
+
TableCell,
|
|
8
|
+
TableFooter,
|
|
9
|
+
TableHead,
|
|
10
|
+
TableHeader,
|
|
11
|
+
TableRow,
|
|
12
|
+
} from "./table";
|
|
13
|
+
|
|
14
|
+
const invoices = [
|
|
15
|
+
{
|
|
16
|
+
invoice: "INV001",
|
|
17
|
+
paymentStatus: "Paid",
|
|
18
|
+
totalAmount: "$250.00",
|
|
19
|
+
paymentMethod: "Credit Card",
|
|
20
|
+
},
|
|
21
|
+
{ invoice: "INV002", paymentStatus: "Pending", totalAmount: "$150.00", paymentMethod: "PayPal" },
|
|
22
|
+
{
|
|
23
|
+
invoice: "INV003",
|
|
24
|
+
paymentStatus: "Unpaid",
|
|
25
|
+
totalAmount: "$350.00",
|
|
26
|
+
paymentMethod: "Bank Transfer",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
invoice: "INV004",
|
|
30
|
+
paymentStatus: "Paid",
|
|
31
|
+
totalAmount: "$450.00",
|
|
32
|
+
paymentMethod: "Credit Card",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const meta = {
|
|
37
|
+
title: "DataDisplay/Table",
|
|
38
|
+
component: Table,
|
|
39
|
+
tags: ["autodocs"],
|
|
40
|
+
} satisfies Meta<typeof Table>;
|
|
41
|
+
|
|
42
|
+
export default meta;
|
|
43
|
+
type Story = StoryObj<typeof meta>;
|
|
44
|
+
|
|
45
|
+
export const Default: Story = {
|
|
46
|
+
render: () => (
|
|
47
|
+
<Table>
|
|
48
|
+
<TableCaption>A list of your recent invoices.</TableCaption>
|
|
49
|
+
<TableHeader>
|
|
50
|
+
<TableRow>
|
|
51
|
+
<TableHead className="w-[100px]">Invoice</TableHead>
|
|
52
|
+
<TableHead>Status</TableHead>
|
|
53
|
+
<TableHead>Method</TableHead>
|
|
54
|
+
<TableHead className="text-right">Amount</TableHead>
|
|
55
|
+
</TableRow>
|
|
56
|
+
</TableHeader>
|
|
57
|
+
<TableBody>
|
|
58
|
+
{invoices.map((invoice) => (
|
|
59
|
+
<TableRow key={invoice.invoice}>
|
|
60
|
+
<TableCell className="font-medium">{invoice.invoice}</TableCell>
|
|
61
|
+
<TableCell>{invoice.paymentStatus}</TableCell>
|
|
62
|
+
<TableCell>{invoice.paymentMethod}</TableCell>
|
|
63
|
+
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
|
|
64
|
+
</TableRow>
|
|
65
|
+
))}
|
|
66
|
+
</TableBody>
|
|
67
|
+
<TableFooter>
|
|
68
|
+
<TableRow>
|
|
69
|
+
<TableCell colSpan={3}>Total</TableCell>
|
|
70
|
+
<TableCell className="text-right">$1,200.00</TableCell>
|
|
71
|
+
</TableRow>
|
|
72
|
+
</TableFooter>
|
|
73
|
+
</Table>
|
|
74
|
+
),
|
|
75
|
+
};
|