@nationaldesignstudio/react 0.0.14 → 0.0.16
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/dist/tailwind.css +15 -1
- package/dist/tokens.css +45 -60
- package/package.json +5 -10
- package/src/App.css +0 -0
- package/src/App.tsx +7 -0
- package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
- package/src/assets/react.svg +1 -0
- package/src/components/atoms/accordion/accordion.stories.tsx +228 -0
- package/src/components/atoms/accordion/accordion.tsx +219 -0
- package/src/components/atoms/accordion/index.ts +6 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
- package/src/components/atoms/button/button.stories.tsx +102 -0
- package/src/components/atoms/button/button.test.tsx +135 -0
- package/src/components/atoms/button/button.tsx +139 -0
- package/src/components/atoms/button/button.visual.test.tsx +102 -0
- package/src/components/atoms/button/icon-button.stories.tsx +166 -0
- package/src/components/atoms/button/icon-button.tsx +120 -0
- package/src/components/atoms/button/index.ts +6 -0
- package/src/components/atoms/ndstudio-footer/index.ts +1 -0
- package/src/components/atoms/ndstudio-footer/ndstudio-footer.tsx +55 -0
- package/src/components/atoms/pager-control/index.ts +5 -0
- package/src/components/atoms/pager-control/pager-control.stories.tsx +209 -0
- package/src/components/atoms/pager-control/pager-control.test.tsx +130 -0
- package/src/components/atoms/pager-control/pager-control.tsx +329 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +82 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +196 -0
- package/src/components/dev-tools/dev-toolbar/index.ts +1 -0
- package/src/components/dev-tools/grid-overlay/grid-overlay.tsx +41 -0
- package/src/components/dev-tools/grid-overlay/index.ts +1 -0
- package/src/components/dev-tools/index.ts +2 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
- package/src/components/organisms/card/card.stories.tsx +293 -0
- package/src/components/organisms/card/card.test.tsx +245 -0
- package/src/components/organisms/card/card.tsx +225 -0
- package/src/components/organisms/card/card.visual.test.tsx +197 -0
- package/src/components/organisms/card/index.ts +19 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/index.ts +18 -0
- package/src/components/organisms/navbar/navbar.stories.tsx +313 -0
- package/src/components/organisms/navbar/navbar.test.tsx +190 -0
- package/src/components/organisms/navbar/navbar.tsx +323 -0
- package/src/components/organisms/navbar/navbar.visual.test.tsx +85 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/index.ts +1 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +35 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +107 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +73 -0
- package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +46 -0
- package/src/components/sections/banner/banner.stories.tsx +150 -0
- package/src/components/sections/banner/banner.test.tsx +185 -0
- package/src/components/sections/banner/banner.tsx +130 -0
- package/src/components/sections/banner/index.ts +2 -0
- package/src/components/sections/card-grid/card-grid.stories.tsx +351 -0
- package/src/components/sections/card-grid/card-grid.tsx +116 -0
- package/src/components/sections/card-grid/index.ts +1 -0
- package/src/components/sections/faq-section/faq-section.stories.tsx +453 -0
- package/src/components/sections/faq-section/faq-section.tsx +84 -0
- package/src/components/sections/faq-section/index.ts +2 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
- package/src/components/sections/hero/hero.stories.tsx +274 -0
- package/src/components/sections/hero/hero.test.tsx +135 -0
- package/src/components/sections/hero/hero.tsx +453 -0
- package/src/components/sections/hero/hero.visual.test.tsx +140 -0
- package/src/components/sections/hero/index.ts +10 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
- package/src/components/sections/prose/index.ts +6 -0
- package/src/components/sections/prose/prose.stories.tsx +144 -0
- package/src/components/sections/prose/prose.test.tsx +178 -0
- package/src/components/sections/prose/prose.tsx +88 -0
- package/src/components/sections/prose/prose.visual.test.tsx +105 -0
- package/src/components/sections/river/index.ts +1 -0
- package/src/components/sections/river/river.stories.tsx +237 -0
- package/src/components/sections/river/river.test.tsx +268 -0
- package/src/components/sections/river/river.tsx +173 -0
- package/src/components/sections/tout/index.ts +1 -0
- package/src/components/sections/tout/tout.stories.tsx +171 -0
- package/src/components/sections/tout/tout.test.tsx +242 -0
- package/src/components/sections/tout/tout.tsx +270 -0
- package/src/components/sections/two-column-section/index.ts +5 -0
- package/src/components/sections/two-column-section/two-column-section.stories.tsx +285 -0
- package/src/components/sections/two-column-section/two-column-section.tsx +162 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-event-listener.ts +73 -0
- package/src/index.ts +155 -0
- package/src/lib/theme.ts +1000 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +13 -0
- package/src/stories/GridSystem.stories.tsx +84 -0
- package/src/stories/Introduction.mdx +114 -0
- package/src/stories/ThemeProvider.stories.tsx +357 -0
- package/src/stories/TokenShowcase.stories.tsx +92 -0
- package/src/stories/TokenShowcase.tsx +1429 -0
- package/src/styles.css +11 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/hooks.ts +40 -0
- package/src/theme/index.ts +43 -0
- package/src/theme/utils.ts +104 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { GridOverlay } from "../grid-overlay";
|
|
3
|
+
|
|
4
|
+
function GridIcon({ active }: { active?: boolean }) {
|
|
5
|
+
return (
|
|
6
|
+
<svg
|
|
7
|
+
width="20"
|
|
8
|
+
height="20"
|
|
9
|
+
viewBox="0 0 20 20"
|
|
10
|
+
fill="none"
|
|
11
|
+
stroke="currentColor"
|
|
12
|
+
strokeWidth={active ? "2" : "1.5"}
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
>
|
|
15
|
+
<rect x="2" y="2" width="16" height="16" rx="2" />
|
|
16
|
+
<line x1="7" y1="2" x2="7" y2="18" />
|
|
17
|
+
<line x1="13" y1="2" x2="13" y2="18" />
|
|
18
|
+
</svg>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DevToolbarProps {
|
|
23
|
+
defaultExpanded?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DRAG_THRESHOLD = 3;
|
|
27
|
+
|
|
28
|
+
export function DevToolbar({ defaultExpanded = false }: DevToolbarProps) {
|
|
29
|
+
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
30
|
+
const [showGrid, setShowGrid] = useState(false);
|
|
31
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
32
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
33
|
+
const hasDraggedRef = useRef(false);
|
|
34
|
+
const dragRef = useRef<{
|
|
35
|
+
startX: number;
|
|
36
|
+
startY: number;
|
|
37
|
+
startPosX: number;
|
|
38
|
+
startPosY: number;
|
|
39
|
+
} | null>(null);
|
|
40
|
+
const toolbarRef = useRef<HTMLDivElement>(null);
|
|
41
|
+
|
|
42
|
+
const toggleGrid = useCallback(() => setShowGrid((prev) => !prev), []);
|
|
43
|
+
const toggleExpanded = useCallback(() => setIsExpanded((prev) => !prev), []);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
47
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "g") {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
toggleGrid();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
54
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
55
|
+
}, [toggleGrid]);
|
|
56
|
+
|
|
57
|
+
const handleDragStart = useCallback(
|
|
58
|
+
(clientX: number, clientY: number) => {
|
|
59
|
+
setIsDragging(true);
|
|
60
|
+
hasDraggedRef.current = false;
|
|
61
|
+
dragRef.current = {
|
|
62
|
+
startX: clientX,
|
|
63
|
+
startY: clientY,
|
|
64
|
+
startPosX: position.x,
|
|
65
|
+
startPosY: position.y,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
[position],
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const handleDragMove = useCallback(
|
|
72
|
+
(clientX: number, clientY: number) => {
|
|
73
|
+
if (!isDragging || !dragRef.current) return;
|
|
74
|
+
|
|
75
|
+
const deltaX = clientX - dragRef.current.startX;
|
|
76
|
+
const deltaY = clientY - dragRef.current.startY;
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
Math.abs(deltaX) > DRAG_THRESHOLD ||
|
|
80
|
+
Math.abs(deltaY) > DRAG_THRESHOLD
|
|
81
|
+
) {
|
|
82
|
+
hasDraggedRef.current = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setPosition({
|
|
86
|
+
x: dragRef.current.startPosX + deltaX,
|
|
87
|
+
y: dragRef.current.startPosY - deltaY,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
[isDragging],
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const handleDragEnd = useCallback(() => {
|
|
94
|
+
setIsDragging(false);
|
|
95
|
+
dragRef.current = null;
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!isDragging) return;
|
|
100
|
+
|
|
101
|
+
const handleMouseMove = (e: MouseEvent) =>
|
|
102
|
+
handleDragMove(e.clientX, e.clientY);
|
|
103
|
+
const handleTouchMove = (e: TouchEvent) => {
|
|
104
|
+
if (e.touches[0])
|
|
105
|
+
handleDragMove(e.touches[0].clientX, e.touches[0].clientY);
|
|
106
|
+
};
|
|
107
|
+
const handleEnd = () => handleDragEnd();
|
|
108
|
+
|
|
109
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
110
|
+
window.addEventListener("mouseup", handleEnd);
|
|
111
|
+
window.addEventListener("touchmove", handleTouchMove);
|
|
112
|
+
window.addEventListener("touchend", handleEnd);
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
116
|
+
window.removeEventListener("mouseup", handleEnd);
|
|
117
|
+
window.removeEventListener("touchmove", handleTouchMove);
|
|
118
|
+
window.removeEventListener("touchend", handleEnd);
|
|
119
|
+
};
|
|
120
|
+
}, [isDragging, handleDragMove, handleDragEnd]);
|
|
121
|
+
|
|
122
|
+
const handleBarMouseDown = (e: React.MouseEvent) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
handleDragStart(e.clientX, e.clientY);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handleBarTouchStart = (e: React.TouchEvent) => {
|
|
128
|
+
if (e.touches[0]) {
|
|
129
|
+
handleDragStart(e.touches[0].clientX, e.touches[0].clientY);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleBarClick = () => {
|
|
134
|
+
if (!hasDraggedRef.current) {
|
|
135
|
+
toggleExpanded();
|
|
136
|
+
}
|
|
137
|
+
hasDraggedRef.current = false;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<>
|
|
142
|
+
{showGrid && <GridOverlay />}
|
|
143
|
+
|
|
144
|
+
<div
|
|
145
|
+
ref={toolbarRef}
|
|
146
|
+
className="fixed bottom-4 left-1/2 z-[9999]"
|
|
147
|
+
style={{
|
|
148
|
+
transform: `translate(calc(-50% + ${position.x}px), ${-position.y}px)`,
|
|
149
|
+
}}
|
|
150
|
+
data-testid="dev-toolbar"
|
|
151
|
+
>
|
|
152
|
+
<div
|
|
153
|
+
className={`bg-gray-1100 rounded-radius-16 shadow-lg flex flex-col items-center overflow-hidden px-spacing-12 py-spacing-8 ${isExpanded ? "gap-spacing-4" : ""}`}
|
|
154
|
+
>
|
|
155
|
+
<div
|
|
156
|
+
className={`
|
|
157
|
+
grid transition-all duration-300 ease-out
|
|
158
|
+
${isExpanded ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}
|
|
159
|
+
`}
|
|
160
|
+
>
|
|
161
|
+
<div className="overflow-hidden">
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
onClick={toggleGrid}
|
|
165
|
+
className={`
|
|
166
|
+
size-spacing-40 rounded-full flex items-center justify-center transition-colors
|
|
167
|
+
${
|
|
168
|
+
showGrid
|
|
169
|
+
? "text-gray-50"
|
|
170
|
+
: "text-gray-400 hover:text-gray-50 hover:bg-alpha-white-10"
|
|
171
|
+
}
|
|
172
|
+
`}
|
|
173
|
+
title="Toggle Grid (⌘G)"
|
|
174
|
+
aria-label="Toggle grid overlay"
|
|
175
|
+
>
|
|
176
|
+
<GridIcon active={showGrid} />
|
|
177
|
+
</button>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<button
|
|
182
|
+
type="button"
|
|
183
|
+
onMouseDown={handleBarMouseDown}
|
|
184
|
+
onTouchStart={handleBarTouchStart}
|
|
185
|
+
onClick={handleBarClick}
|
|
186
|
+
className={`
|
|
187
|
+
w-spacing-32 h-spacing-4 bg-gray-50 rounded-full transition-opacity
|
|
188
|
+
${isDragging ? "opacity-100 cursor-grabbing" : "opacity-60 hover:opacity-100 cursor-grab"}
|
|
189
|
+
`}
|
|
190
|
+
aria-label={isExpanded ? "Close dev tools" : "Open dev tools"}
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DevToolbar, type DevToolbarProps } from "./dev-toolbar";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface GridOverlayProps {
|
|
2
|
+
columnOpacity?: number;
|
|
3
|
+
borderOpacity?: number;
|
|
4
|
+
visible?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function GridOverlay({
|
|
8
|
+
columnOpacity = 0.15,
|
|
9
|
+
borderOpacity = 0.3,
|
|
10
|
+
visible = true,
|
|
11
|
+
}: GridOverlayProps) {
|
|
12
|
+
const columns = Array.from({ length: 24 }, (_, i) => i);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={`
|
|
17
|
+
fixed inset-0 z-[9998] pointer-events-none overflow-hidden
|
|
18
|
+
transition-opacity duration-300 ease-out
|
|
19
|
+
${visible ? "opacity-100" : "opacity-0"}
|
|
20
|
+
`}
|
|
21
|
+
aria-hidden="true"
|
|
22
|
+
data-testid="grid-overlay"
|
|
23
|
+
>
|
|
24
|
+
<div className="h-full w-full max-w-[90rem] mx-auto px-[var(--spatial-grid-small-margin)] md:px-[var(--spatial-grid-medium-margin)] lg:px-[var(--spatial-grid-large-margin)]">
|
|
25
|
+
<div className="h-full grid grid-cols-4 md:grid-cols-12 lg:grid-cols-24 gap-[var(--spatial-grid-small-gutter)] md:gap-[var(--spatial-grid-medium-gutter)] lg:gap-[var(--spatial-grid-large-gutter)]">
|
|
26
|
+
{columns.map((index) => (
|
|
27
|
+
<div
|
|
28
|
+
key={index}
|
|
29
|
+
className="h-full border border-red-500"
|
|
30
|
+
style={{
|
|
31
|
+
backgroundColor: `rgb(239 68 68 / ${columnOpacity})`,
|
|
32
|
+
borderColor: `rgb(239 68 68 / ${borderOpacity})`,
|
|
33
|
+
}}
|
|
34
|
+
data-column={index + 1}
|
|
35
|
+
/>
|
|
36
|
+
))}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GridOverlay, type GridOverlayProps } from "./grid-overlay";
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Button } from "../../atoms/button";
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardActions,
|
|
6
|
+
CardBody,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardEyebrow,
|
|
10
|
+
CardImage,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from ".";
|
|
13
|
+
|
|
14
|
+
const meta: Meta<typeof Card> = {
|
|
15
|
+
title: "Organisms/Card",
|
|
16
|
+
component: Card,
|
|
17
|
+
parameters: {
|
|
18
|
+
layout: "centered",
|
|
19
|
+
},
|
|
20
|
+
} as Meta<typeof Card>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof Card>;
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Default
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
render: () => (
|
|
31
|
+
<div className="w-[418px]">
|
|
32
|
+
<Card>
|
|
33
|
+
<CardImage />
|
|
34
|
+
<CardContent>
|
|
35
|
+
<CardBody>
|
|
36
|
+
<CardEyebrow>Optional Eyebrow</CardEyebrow>
|
|
37
|
+
<div className="flex flex-col gap-[6px]">
|
|
38
|
+
<CardTitle>Card Title</CardTitle>
|
|
39
|
+
<CardDescription>
|
|
40
|
+
Use cards when citizens need to scan items at a glance, either
|
|
41
|
+
to jump to a more detailed page or absorb short information.
|
|
42
|
+
</CardDescription>
|
|
43
|
+
</div>
|
|
44
|
+
</CardBody>
|
|
45
|
+
<CardActions>
|
|
46
|
+
<Button size="default" variant="charcoal">
|
|
47
|
+
Primary
|
|
48
|
+
</Button>
|
|
49
|
+
<Button size="default" variant="charcoalOutline">
|
|
50
|
+
Secondary
|
|
51
|
+
</Button>
|
|
52
|
+
</CardActions>
|
|
53
|
+
</CardContent>
|
|
54
|
+
</Card>
|
|
55
|
+
</div>
|
|
56
|
+
),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Layouts
|
|
61
|
+
// =============================================================================
|
|
62
|
+
|
|
63
|
+
export const Vertical: Story = {
|
|
64
|
+
render: () => (
|
|
65
|
+
<div className="w-[418px]">
|
|
66
|
+
<Card layout="vertical">
|
|
67
|
+
<CardImage />
|
|
68
|
+
<CardContent>
|
|
69
|
+
<CardBody>
|
|
70
|
+
<CardEyebrow>Optional Eyebrow</CardEyebrow>
|
|
71
|
+
<div className="flex flex-col gap-[6px]">
|
|
72
|
+
<CardTitle>Card Title</CardTitle>
|
|
73
|
+
<CardDescription>
|
|
74
|
+
Use cards when citizens need to scan items at a glance, either
|
|
75
|
+
to jump to a more detailed page or absorb short information.
|
|
76
|
+
</CardDescription>
|
|
77
|
+
</div>
|
|
78
|
+
</CardBody>
|
|
79
|
+
<CardActions>
|
|
80
|
+
<Button size="default" variant="charcoal">
|
|
81
|
+
Primary
|
|
82
|
+
</Button>
|
|
83
|
+
<Button size="default" variant="charcoalOutline">
|
|
84
|
+
Secondary
|
|
85
|
+
</Button>
|
|
86
|
+
</CardActions>
|
|
87
|
+
</CardContent>
|
|
88
|
+
</Card>
|
|
89
|
+
</div>
|
|
90
|
+
),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const Horizontal: Story = {
|
|
94
|
+
render: () => (
|
|
95
|
+
<div className="w-[700px]">
|
|
96
|
+
<Card layout="horizontal">
|
|
97
|
+
<CardImage />
|
|
98
|
+
<CardContent>
|
|
99
|
+
<CardBody>
|
|
100
|
+
<CardEyebrow>Eyebrow</CardEyebrow>
|
|
101
|
+
<div className="flex flex-col gap-[9px]">
|
|
102
|
+
<CardTitle>
|
|
103
|
+
Cards can support multi line headings easily.
|
|
104
|
+
</CardTitle>
|
|
105
|
+
<CardDescription>
|
|
106
|
+
Use cards when citizens need to scan items at a glance, either
|
|
107
|
+
to jump to a more detailed page or absorb short information.
|
|
108
|
+
</CardDescription>
|
|
109
|
+
</div>
|
|
110
|
+
</CardBody>
|
|
111
|
+
<CardActions>
|
|
112
|
+
<Button size="sm" variant="charcoal">
|
|
113
|
+
Primary
|
|
114
|
+
</Button>
|
|
115
|
+
<Button size="sm" variant="charcoalOutline">
|
|
116
|
+
Secondary
|
|
117
|
+
</Button>
|
|
118
|
+
</CardActions>
|
|
119
|
+
</CardContent>
|
|
120
|
+
</Card>
|
|
121
|
+
</div>
|
|
122
|
+
),
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Variations
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
export const WithoutImage: Story = {
|
|
130
|
+
render: () => (
|
|
131
|
+
<div className="w-[418px]">
|
|
132
|
+
<Card>
|
|
133
|
+
<CardContent>
|
|
134
|
+
<CardBody>
|
|
135
|
+
<CardEyebrow>Category</CardEyebrow>
|
|
136
|
+
<div className="flex flex-col gap-[6px]">
|
|
137
|
+
<CardTitle>Card Without Image</CardTitle>
|
|
138
|
+
<CardDescription>
|
|
139
|
+
Cards can be used without images for text-focused content.
|
|
140
|
+
</CardDescription>
|
|
141
|
+
</div>
|
|
142
|
+
</CardBody>
|
|
143
|
+
<CardActions>
|
|
144
|
+
<Button size="default" variant="charcoal">
|
|
145
|
+
Learn More
|
|
146
|
+
</Button>
|
|
147
|
+
</CardActions>
|
|
148
|
+
</CardContent>
|
|
149
|
+
</Card>
|
|
150
|
+
</div>
|
|
151
|
+
),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const WithoutEyebrow: Story = {
|
|
155
|
+
render: () => (
|
|
156
|
+
<div className="w-[418px]">
|
|
157
|
+
<Card>
|
|
158
|
+
<CardImage />
|
|
159
|
+
<CardContent>
|
|
160
|
+
<CardBody>
|
|
161
|
+
<div className="flex flex-col gap-[6px]">
|
|
162
|
+
<CardTitle>Card Title</CardTitle>
|
|
163
|
+
<CardDescription>
|
|
164
|
+
The eyebrow is optional and can be omitted when not needed.
|
|
165
|
+
</CardDescription>
|
|
166
|
+
</div>
|
|
167
|
+
</CardBody>
|
|
168
|
+
<CardActions>
|
|
169
|
+
<Button size="default" variant="charcoal">
|
|
170
|
+
Primary
|
|
171
|
+
</Button>
|
|
172
|
+
</CardActions>
|
|
173
|
+
</CardContent>
|
|
174
|
+
</Card>
|
|
175
|
+
</div>
|
|
176
|
+
),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const WithoutActions: Story = {
|
|
180
|
+
render: () => (
|
|
181
|
+
<div className="w-[418px]">
|
|
182
|
+
<Card>
|
|
183
|
+
<CardImage />
|
|
184
|
+
<CardContent>
|
|
185
|
+
<CardBody>
|
|
186
|
+
<CardEyebrow>Information</CardEyebrow>
|
|
187
|
+
<div className="flex flex-col gap-[6px]">
|
|
188
|
+
<CardTitle>Informational Card</CardTitle>
|
|
189
|
+
<CardDescription>
|
|
190
|
+
Cards without actions can be used for purely informational
|
|
191
|
+
content that doesn't require user interaction.
|
|
192
|
+
</CardDescription>
|
|
193
|
+
</div>
|
|
194
|
+
</CardBody>
|
|
195
|
+
</CardContent>
|
|
196
|
+
</Card>
|
|
197
|
+
</div>
|
|
198
|
+
),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const Minimal: Story = {
|
|
202
|
+
render: () => (
|
|
203
|
+
<div className="w-[418px]">
|
|
204
|
+
<Card>
|
|
205
|
+
<CardContent>
|
|
206
|
+
<CardBody>
|
|
207
|
+
<CardTitle>Minimal Card</CardTitle>
|
|
208
|
+
<CardDescription>
|
|
209
|
+
A minimal card with just title and description.
|
|
210
|
+
</CardDescription>
|
|
211
|
+
</CardBody>
|
|
212
|
+
</CardContent>
|
|
213
|
+
</Card>
|
|
214
|
+
</div>
|
|
215
|
+
),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// =============================================================================
|
|
219
|
+
// Grid Example
|
|
220
|
+
// =============================================================================
|
|
221
|
+
|
|
222
|
+
export const CardGrid: Story = {
|
|
223
|
+
render: () => (
|
|
224
|
+
<div className="grid max-w-[900px] grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
225
|
+
{[1, 2, 3].map((i) => (
|
|
226
|
+
<Card key={i}>
|
|
227
|
+
<CardImage />
|
|
228
|
+
<CardContent>
|
|
229
|
+
<CardBody>
|
|
230
|
+
<CardEyebrow>Category {i}</CardEyebrow>
|
|
231
|
+
<div className="flex flex-col gap-[6px]">
|
|
232
|
+
<CardTitle>Card Title {i}</CardTitle>
|
|
233
|
+
<CardDescription>
|
|
234
|
+
Brief description of the card content goes here.
|
|
235
|
+
</CardDescription>
|
|
236
|
+
</div>
|
|
237
|
+
</CardBody>
|
|
238
|
+
<CardActions>
|
|
239
|
+
<Button size="sm" variant="charcoal">
|
|
240
|
+
Action
|
|
241
|
+
</Button>
|
|
242
|
+
</CardActions>
|
|
243
|
+
</CardContent>
|
|
244
|
+
</Card>
|
|
245
|
+
))}
|
|
246
|
+
</div>
|
|
247
|
+
),
|
|
248
|
+
parameters: {
|
|
249
|
+
layout: "padded",
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// =============================================================================
|
|
254
|
+
// Playground
|
|
255
|
+
// =============================================================================
|
|
256
|
+
|
|
257
|
+
export const Playground: Story = {
|
|
258
|
+
render: (args) => (
|
|
259
|
+
<div className="w-[418px]">
|
|
260
|
+
<Card {...args}>
|
|
261
|
+
<CardImage />
|
|
262
|
+
<CardContent>
|
|
263
|
+
<CardBody>
|
|
264
|
+
<CardEyebrow>Eyebrow</CardEyebrow>
|
|
265
|
+
<div className="flex flex-col gap-[6px]">
|
|
266
|
+
<CardTitle>Card Title</CardTitle>
|
|
267
|
+
<CardDescription>
|
|
268
|
+
Use cards when citizens need to scan items at a glance.
|
|
269
|
+
</CardDescription>
|
|
270
|
+
</div>
|
|
271
|
+
</CardBody>
|
|
272
|
+
<CardActions>
|
|
273
|
+
<Button size="default" variant="charcoal">
|
|
274
|
+
Primary
|
|
275
|
+
</Button>
|
|
276
|
+
<Button size="default" variant="charcoalOutline">
|
|
277
|
+
Secondary
|
|
278
|
+
</Button>
|
|
279
|
+
</CardActions>
|
|
280
|
+
</CardContent>
|
|
281
|
+
</Card>
|
|
282
|
+
</div>
|
|
283
|
+
),
|
|
284
|
+
};
|
|
285
|
+
Playground.argTypes = {
|
|
286
|
+
layout: {
|
|
287
|
+
control: { type: "radio" },
|
|
288
|
+
options: ["vertical", "horizontal"],
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
Playground.args = {
|
|
292
|
+
layout: "vertical",
|
|
293
|
+
};
|