@stasho/ds 0.11.0 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stasho/ds",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "stasho design system",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,6 +48,7 @@
48
48
  "./pagination": "./src/components/pagination/pagination.tsx",
49
49
  "./copyable-text": "./src/components/copyable-text/copyable-text.tsx",
50
50
  "./dialog": "./src/components/dialog/dialog.tsx",
51
+ "./drawer": "./src/components/drawer/drawer.tsx",
51
52
  "./logo": "./src/components/logo/logo.tsx",
52
53
  "./progress-bar": "./src/components/progress-bar/progress-bar.tsx",
53
54
  "./stepper": "./src/components/stepper/stepper.tsx",
@@ -0,0 +1,127 @@
1
+ "use client";
2
+
3
+ import {
4
+ forwardRef,
5
+ type ComponentPropsWithoutRef,
6
+ } from "react";
7
+ import { Dialog as DialogPrimitive } from "radix-ui";
8
+ import { X } from "@phosphor-icons/react";
9
+ import { cn } from "@ac/lib/cn";
10
+
11
+ /* ── Direct re-exports ─────────────────────────── */
12
+
13
+ const Drawer = DialogPrimitive.Root;
14
+ const DrawerTrigger = DialogPrimitive.Trigger;
15
+ const DrawerClose = DialogPrimitive.Close;
16
+
17
+ /* ── DrawerContent (Portal + Overlay + Content) ── */
18
+
19
+ type DrawerSide = "bottom" | "left" | "right";
20
+
21
+ const sideClasses: Record<DrawerSide, string> = {
22
+ bottom: cn(
23
+ "inset-x-0 bottom-0 max-h-[85dvh] w-full rounded-t-xl border-t",
24
+ "motion-safe:data-[state=open]:animate-drawer-in-bottom",
25
+ "motion-safe:data-[state=closed]:animate-drawer-out-bottom",
26
+ ),
27
+ left: cn(
28
+ "inset-y-0 left-0 h-dvh w-80 max-w-[85vw] border-r",
29
+ "motion-safe:data-[state=open]:animate-drawer-in-left",
30
+ "motion-safe:data-[state=closed]:animate-drawer-out-left",
31
+ ),
32
+ right: cn(
33
+ "inset-y-0 right-0 h-dvh w-80 max-w-[85vw] border-l",
34
+ "motion-safe:data-[state=open]:animate-drawer-in-right",
35
+ "motion-safe:data-[state=closed]:animate-drawer-out-right",
36
+ ),
37
+ };
38
+
39
+ type DrawerContentProps = ComponentPropsWithoutRef<
40
+ typeof DialogPrimitive.Content
41
+ > & {
42
+ side?: DrawerSide;
43
+ };
44
+
45
+ const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
46
+ ({ className, children, side = "bottom", ...rest }, ref) => (
47
+ <DialogPrimitive.Portal>
48
+ <DialogPrimitive.Overlay
49
+ className={cn(
50
+ "fixed inset-0 z-50 bg-black/60 backdrop-blur-sm",
51
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0",
52
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
53
+ "motion-reduce:animate-none",
54
+ )}
55
+ />
56
+ <DialogPrimitive.Content
57
+ ref={ref}
58
+ className={cn(
59
+ "fixed z-50 border-edge bg-surface p-6",
60
+ "overflow-y-auto overscroll-contain",
61
+ sideClasses[side],
62
+ className,
63
+ )}
64
+ {...rest}
65
+ >
66
+ {children}
67
+ <DialogPrimitive.Close
68
+ className={cn(
69
+ "absolute top-4 right-4 rounded-sm",
70
+ "text-muted-foreground transition-colors hover:text-foreground",
71
+ "focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2",
72
+ )}
73
+ aria-label="Close"
74
+ >
75
+ <X weight="bold" className="size-4" />
76
+ </DialogPrimitive.Close>
77
+ </DialogPrimitive.Content>
78
+ </DialogPrimitive.Portal>
79
+ ),
80
+ );
81
+
82
+ DrawerContent.displayName = "DrawerContent";
83
+
84
+ /* ── DrawerTitle ───────────────────────────────── */
85
+
86
+ const DrawerTitle = forwardRef<
87
+ HTMLHeadingElement,
88
+ ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
89
+ >(({ className, ...rest }, ref) => (
90
+ <DialogPrimitive.Title
91
+ ref={ref}
92
+ className={cn(
93
+ "font-heading font-bold text-lg text-foreground",
94
+ className,
95
+ )}
96
+ {...rest}
97
+ />
98
+ ));
99
+
100
+ DrawerTitle.displayName = "DrawerTitle";
101
+
102
+ /* ── DrawerDescription ─────────────────────────── */
103
+
104
+ const DrawerDescription = forwardRef<
105
+ HTMLParagraphElement,
106
+ ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
107
+ >(({ className, ...rest }, ref) => (
108
+ <DialogPrimitive.Description
109
+ ref={ref}
110
+ className={cn("text-sm text-muted-foreground", className)}
111
+ {...rest}
112
+ />
113
+ ));
114
+
115
+ DrawerDescription.displayName = "DrawerDescription";
116
+
117
+ /* ── Exports ───────────────────────────────────── */
118
+
119
+ export {
120
+ Drawer,
121
+ DrawerClose,
122
+ DrawerContent,
123
+ DrawerDescription,
124
+ DrawerTitle,
125
+ DrawerTrigger,
126
+ type DrawerContentProps,
127
+ };
@@ -64,5 +64,71 @@ const LogoFull = forwardRef<SVGSVGElement, LogoProps>(
64
64
 
65
65
  LogoFull.displayName = "LogoFull";
66
66
 
67
- export { Logo, LogoFull };
67
+ /**
68
+ * stasho wordmark only, no icon (placeholder logotype, rebrand still deferred).
69
+ * Set in Anybody 800 italic to match the brand headings; the consuming app must
70
+ * load Anybody or it falls back to sans-serif. viewBox is fitted to "stasho" at
71
+ * that cut. Inherits color from parent via `currentColor`.
72
+ */
73
+ const LogoWordmark = forwardRef<SVGSVGElement, LogoProps>(
74
+ ({ className, ...rest }, ref) => (
75
+ <svg
76
+ ref={ref}
77
+ viewBox="0 0 654 229"
78
+ xmlns="http://www.w3.org/2000/svg"
79
+ fill="currentColor"
80
+ className={cn("shrink-0", className)}
81
+ {...rest}
82
+ >
83
+ <text
84
+ x="0"
85
+ y="180"
86
+ fill="currentColor"
87
+ fontFamily="Anybody, sans-serif"
88
+ fontSize="200"
89
+ fontWeight="800"
90
+ fontStyle="italic"
91
+ >
92
+ stasho
93
+ </text>
94
+ </svg>
95
+ ),
96
+ );
97
+
98
+ LogoWordmark.displayName = "LogoWordmark";
99
+
100
+ /**
101
+ * stasho letter mark "s", for collapsed / compact placements (placeholder
102
+ * logotype, rebrand still deferred). Set in Anybody 800 italic to match the
103
+ * brand headings; the consuming app must load Anybody or it falls back to
104
+ * sans-serif. Inherits color from parent via `currentColor`.
105
+ */
106
+ const LogoLetter = forwardRef<SVGSVGElement, LogoProps>(
107
+ ({ className, ...rest }, ref) => (
108
+ <svg
109
+ ref={ref}
110
+ viewBox="0 0 119 229"
111
+ xmlns="http://www.w3.org/2000/svg"
112
+ fill="currentColor"
113
+ className={cn("shrink-0", className)}
114
+ {...rest}
115
+ >
116
+ <text
117
+ x="0"
118
+ y="180"
119
+ fill="currentColor"
120
+ fontFamily="Anybody, sans-serif"
121
+ fontSize="200"
122
+ fontWeight="800"
123
+ fontStyle="italic"
124
+ >
125
+ s
126
+ </text>
127
+ </svg>
128
+ ),
129
+ );
130
+
131
+ LogoLetter.displayName = "LogoLetter";
132
+
133
+ export { Logo, LogoFull, LogoWordmark, LogoLetter };
68
134
  export type { LogoProps };
@@ -398,4 +398,49 @@
398
398
  @keyframes accordion-up {
399
399
  from { height: var(--radix-accordion-content-height); }
400
400
  to { height: 0; }
401
+ }
402
+
403
+ /* ── Drawer slide in/out (per side) ────────────── */
404
+
405
+ /* Same mechanism as the Accordion utilities above: registered in @theme so
406
+ the `data-[state=...]:` variant composes, and applied in the component as
407
+ `motion-safe:data-[state=…]:animate-drawer-*` so reduced motion is honored
408
+ by gating the animation INTO motion-safe. */
409
+ @theme {
410
+ --animate-drawer-in-bottom: drawer-in-bottom 300ms ease-out;
411
+ --animate-drawer-out-bottom: drawer-out-bottom 300ms ease-in;
412
+ --animate-drawer-in-left: drawer-in-left 300ms ease-out;
413
+ --animate-drawer-out-left: drawer-out-left 300ms ease-in;
414
+ --animate-drawer-in-right: drawer-in-right 300ms ease-out;
415
+ --animate-drawer-out-right: drawer-out-right 300ms ease-in;
416
+ }
417
+
418
+ @keyframes drawer-in-bottom {
419
+ from { transform: translateY(100%); }
420
+ to { transform: translateY(0); }
421
+ }
422
+
423
+ @keyframes drawer-out-bottom {
424
+ from { transform: translateY(0); }
425
+ to { transform: translateY(100%); }
426
+ }
427
+
428
+ @keyframes drawer-in-left {
429
+ from { transform: translateX(-100%); }
430
+ to { transform: translateX(0); }
431
+ }
432
+
433
+ @keyframes drawer-out-left {
434
+ from { transform: translateX(0); }
435
+ to { transform: translateX(-100%); }
436
+ }
437
+
438
+ @keyframes drawer-in-right {
439
+ from { transform: translateX(100%); }
440
+ to { transform: translateX(0); }
441
+ }
442
+
443
+ @keyframes drawer-out-right {
444
+ from { transform: translateX(0); }
445
+ to { transform: translateX(100%); }
401
446
  }