@syscore/ui-library 1.1.12 → 1.1.13
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/client/components/icons/AchievementBadges.tsx +33 -0
- package/client/components/icons/ConceptIcons.tsx +169 -22
- package/client/components/icons/NavLogo.tsx +4 -4
- package/client/components/icons/ProviderBadges.tsx +28 -28
- package/client/components/icons/ProviderSeals.tsx +35 -35
- package/client/components/icons/StandardLogo.tsx +47 -0
- package/client/components/icons/UtilityChevronDown.tsx +1 -1
- package/client/components/icons/UtilityClearRegular.tsx +43 -0
- package/client/components/icons/UtilityCompare.tsx +71 -0
- package/client/components/icons/UtilityHome.tsx +26 -0
- package/client/components/icons/UtilityReset.tsx +7 -7
- package/client/components/icons/UtilitySave.tsx +35 -0
- package/client/components/icons/UtilityScopeLarge.tsx +86 -0
- package/client/components/icons/UtilityShow.tsx +41 -0
- package/client/components/icons/UtilityTarget.tsx +21 -0
- package/client/components/icons/UtilityTargetActive.tsx +34 -0
- package/client/components/icons/UtilityText.tsx +8 -8
- package/client/components/ui/breadcrumb.tsx +26 -4
- package/client/components/ui/button.tsx +30 -18
- package/client/components/ui/card.tsx +2 -2
- package/client/components/ui/code-badge.tsx +25 -0
- package/client/components/ui/dialog.tsx +4 -4
- package/client/components/ui/input.tsx +53 -9
- package/client/components/ui/label.tsx +2 -2
- package/client/components/ui/{Navigation.tsx → navigation.tsx} +291 -250
- package/client/components/ui/select.tsx +20 -20
- package/client/components/ui/tabs.tsx +27 -178
- package/client/components/ui/{Tag.tsx → tag.tsx} +11 -10
- package/client/components/ui/textarea.tsx +1 -1
- package/client/components/ui/toggle-group.tsx +19 -2
- package/client/components/ui/toggle.tsx +2 -2
- package/client/components/ui/tooltip.tsx +148 -8
- package/client/global.css +18 -11
- package/client/ui/AspectRatio.stories.tsx +1 -1
- package/client/ui/Button.stories.tsx +5 -5
- package/client/ui/Card.stories.tsx +223 -2
- package/client/ui/CodeBadge.stories.tsx +76 -0
- package/client/ui/Dialog.stories.tsx +52 -5
- package/client/ui/Icons.stories.tsx +31 -31
- package/client/ui/Input.stories.tsx +125 -0
- package/client/ui/Label.stories.tsx +8 -11
- package/client/ui/Navigation.stories.tsx +1 -1
- package/client/ui/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/client/ui/SearchField.stories.tsx +1 -1
- package/client/ui/{Select/Select.stories.tsx → Select.stories.tsx} +2 -2
- package/client/ui/{Switch/Switch.stories.tsx → Switch.stories.tsx} +3 -3
- package/client/ui/Tabs.stories.tsx +174 -10
- package/client/ui/Tag.stories.tsx +48 -1
- package/client/ui/{Textarea/Textarea.stories.tsx → Textarea.stories.tsx} +9 -10
- package/client/ui/Toggle.stories.tsx +3 -3
- package/client/ui/Tooltip.stories.tsx +28 -4
- package/client/ui/WELLDashboard/WELLDashboard.stories.tsx +1 -1
- package/dist/ui/index.cjs.js +1 -1
- package/dist/ui/index.d.ts +4 -5
- package/dist/ui/index.es.js +460 -518
- package/package.json +2 -2
- package/client/components/ui/StrategyTable.tsx +0 -303
- package/client/ui/Input/Input.stories.tsx +0 -69
- package/client/ui/StrategyTable.stories.tsx +0 -138
- /package/client/components/ui/{SearchField.tsx → search.tsx} +0 -0
- /package/client/hooks/{UseTabs.tsx → use-tabs.tsx} +0 -0
|
@@ -5,6 +5,21 @@ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
|
5
5
|
|
|
6
6
|
import { cn } from "@/lib/utils";
|
|
7
7
|
|
|
8
|
+
type TriggerMode = "hover" | "click";
|
|
9
|
+
|
|
10
|
+
// Context for passing trigger mode and close function to children
|
|
11
|
+
const TooltipContext = React.createContext<{
|
|
12
|
+
trigger: TriggerMode;
|
|
13
|
+
toggle: () => void;
|
|
14
|
+
close: () => void;
|
|
15
|
+
triggerRef: React.RefObject<HTMLButtonElement | null>;
|
|
16
|
+
}>({
|
|
17
|
+
trigger: "hover",
|
|
18
|
+
toggle: () => { },
|
|
19
|
+
close: () => { },
|
|
20
|
+
triggerRef: { current: null },
|
|
21
|
+
});
|
|
22
|
+
|
|
8
23
|
function TooltipProvider({
|
|
9
24
|
delayDuration = 0,
|
|
10
25
|
...props
|
|
@@ -18,20 +33,114 @@ function TooltipProvider({
|
|
|
18
33
|
);
|
|
19
34
|
}
|
|
20
35
|
|
|
36
|
+
interface TooltipProps
|
|
37
|
+
extends Omit<
|
|
38
|
+
React.ComponentProps<typeof TooltipPrimitive.Root>,
|
|
39
|
+
"open" | "onOpenChange"
|
|
40
|
+
> {
|
|
41
|
+
/**
|
|
42
|
+
* Trigger mode: "hover" (default) or "click"
|
|
43
|
+
* - hover: Opens on mouse hover, closes on hover off (default tooltip behavior)
|
|
44
|
+
* - click: Opens on click, closes on trigger click or click outside
|
|
45
|
+
*/
|
|
46
|
+
trigger?: TriggerMode;
|
|
47
|
+
/** Controlled open state (optional) */
|
|
48
|
+
open?: boolean;
|
|
49
|
+
/** Callback when open state changes (optional) */
|
|
50
|
+
onOpenChange?: (open: boolean) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
21
53
|
function Tooltip({
|
|
54
|
+
trigger = "hover",
|
|
55
|
+
open: controlledOpen,
|
|
56
|
+
onOpenChange,
|
|
57
|
+
children,
|
|
22
58
|
...props
|
|
23
|
-
}:
|
|
59
|
+
}: TooltipProps) {
|
|
60
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
61
|
+
const triggerRef = React.useRef<HTMLButtonElement>(null);
|
|
62
|
+
|
|
63
|
+
// Use controlled or uncontrolled state
|
|
64
|
+
const isControlled = controlledOpen !== undefined;
|
|
65
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
66
|
+
|
|
67
|
+
const handleOpenChange = React.useCallback(
|
|
68
|
+
(newOpen: boolean) => {
|
|
69
|
+
// For click mode, ignore hover-triggered changes
|
|
70
|
+
if (trigger === "click") return;
|
|
71
|
+
|
|
72
|
+
if (!isControlled) {
|
|
73
|
+
setUncontrolledOpen(newOpen);
|
|
74
|
+
}
|
|
75
|
+
onOpenChange?.(newOpen);
|
|
76
|
+
},
|
|
77
|
+
[isControlled, onOpenChange, trigger]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const close = React.useCallback(() => {
|
|
81
|
+
if (!isControlled) {
|
|
82
|
+
setUncontrolledOpen(false);
|
|
83
|
+
}
|
|
84
|
+
onOpenChange?.(false);
|
|
85
|
+
}, [isControlled, onOpenChange]);
|
|
86
|
+
|
|
87
|
+
const toggle = React.useCallback(() => {
|
|
88
|
+
const newOpen = !isOpen;
|
|
89
|
+
if (!isControlled) {
|
|
90
|
+
setUncontrolledOpen(newOpen);
|
|
91
|
+
}
|
|
92
|
+
onOpenChange?.(newOpen);
|
|
93
|
+
}, [isOpen, isControlled, onOpenChange]);
|
|
94
|
+
|
|
95
|
+
// For click mode, we fully control open state
|
|
96
|
+
const tooltipProps =
|
|
97
|
+
trigger === "click"
|
|
98
|
+
? {
|
|
99
|
+
open: isOpen,
|
|
100
|
+
onOpenChange: handleOpenChange,
|
|
101
|
+
}
|
|
102
|
+
: {
|
|
103
|
+
open: isControlled ? controlledOpen : undefined,
|
|
104
|
+
onOpenChange,
|
|
105
|
+
};
|
|
106
|
+
|
|
24
107
|
return (
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
108
|
+
<TooltipContext.Provider value={{ trigger, toggle, close, triggerRef }}>
|
|
109
|
+
<TooltipProvider delayDuration={trigger === "click" ? 100000 : 0}>
|
|
110
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...tooltipProps} {...props}>
|
|
111
|
+
{children}
|
|
112
|
+
</TooltipPrimitive.Root>
|
|
113
|
+
</TooltipProvider>
|
|
114
|
+
</TooltipContext.Provider>
|
|
28
115
|
);
|
|
29
116
|
}
|
|
30
117
|
|
|
31
118
|
function TooltipTrigger({
|
|
119
|
+
onClick,
|
|
32
120
|
...props
|
|
33
121
|
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
34
|
-
|
|
122
|
+
const { trigger, toggle, triggerRef } = React.useContext(TooltipContext);
|
|
123
|
+
|
|
124
|
+
const handleClick = React.useCallback(
|
|
125
|
+
(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
126
|
+
if (trigger === "click") {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
e.stopPropagation();
|
|
129
|
+
toggle();
|
|
130
|
+
}
|
|
131
|
+
onClick?.(e);
|
|
132
|
+
},
|
|
133
|
+
[trigger, toggle, onClick]
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<TooltipPrimitive.Trigger
|
|
138
|
+
ref={triggerRef}
|
|
139
|
+
data-slot="tooltip-trigger"
|
|
140
|
+
onClick={handleClick}
|
|
141
|
+
{...props}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
35
144
|
}
|
|
36
145
|
|
|
37
146
|
function TooltipContent({
|
|
@@ -42,21 +151,52 @@ function TooltipContent({
|
|
|
42
151
|
alignOffset = 0,
|
|
43
152
|
...props
|
|
44
153
|
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
154
|
+
const { trigger, close, triggerRef } = React.useContext(TooltipContext);
|
|
155
|
+
const contentRef = React.useRef<HTMLDivElement>(null);
|
|
156
|
+
|
|
157
|
+
// Click outside detection for click mode
|
|
158
|
+
React.useEffect(() => {
|
|
159
|
+
if (trigger !== "click") return;
|
|
160
|
+
|
|
161
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
162
|
+
const target = event.target as Node;
|
|
163
|
+
const isOutsideContent =
|
|
164
|
+
contentRef.current && !contentRef.current.contains(target);
|
|
165
|
+
const isOutsideTrigger =
|
|
166
|
+
triggerRef.current && !triggerRef.current.contains(target);
|
|
167
|
+
|
|
168
|
+
if (isOutsideContent && isOutsideTrigger) {
|
|
169
|
+
close();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Small delay to avoid closing immediately on the same click that opened it
|
|
174
|
+
const timeoutId = setTimeout(() => {
|
|
175
|
+
document.addEventListener("click", handleClickOutside);
|
|
176
|
+
}, 0);
|
|
177
|
+
|
|
178
|
+
return () => {
|
|
179
|
+
clearTimeout(timeoutId);
|
|
180
|
+
document.removeEventListener("click", handleClickOutside);
|
|
181
|
+
};
|
|
182
|
+
}, [trigger, close, triggerRef]);
|
|
183
|
+
|
|
45
184
|
return (
|
|
46
185
|
<TooltipPrimitive.Portal>
|
|
47
186
|
<TooltipPrimitive.Content
|
|
187
|
+
ref={contentRef}
|
|
48
188
|
data-slot="tooltip-content"
|
|
49
189
|
sideOffset={sideOffset}
|
|
50
190
|
alignOffset={alignOffset}
|
|
51
191
|
side={side}
|
|
52
192
|
className={cn(
|
|
53
|
-
"group relative bg-gray-700 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-lg
|
|
54
|
-
className
|
|
193
|
+
"group relative bg-gray-700 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-lg p-6 pt-8 shadow-sm mt-3",
|
|
194
|
+
className
|
|
55
195
|
)}
|
|
56
196
|
{...props}
|
|
57
197
|
>
|
|
58
198
|
{/* Marker/Arrow */}
|
|
59
|
-
<div className="absolute left-1/2 -translate-x-1/2 flex justify-center group-data-[side=top]:bottom-0 group-data-[side=top]:rotate-180 group-data-[side=bottom]:top-0 group-data-[side=left]:right-0 group-data-[side=right]:left-0">
|
|
199
|
+
<div className="absolute left-1/2 -translate-x-1/2 flex justify-center group-data-[side=top]:bottom-0 group-data-[side=top]:rotate-180 group-data-[side=bottom]:top-0 group-data-[side=left]:right-0 group-data-[side=right]:left-0 ">
|
|
60
200
|
<svg
|
|
61
201
|
xmlns="http://www.w3.org/2000/svg"
|
|
62
202
|
width="10"
|
package/client/global.css
CHANGED
|
@@ -527,9 +527,10 @@ layer(base);
|
|
|
527
527
|
--color-sidebar-border: hsl(var(--sidebar-border));
|
|
528
528
|
--color-sidebar-ring: hsl(var(--sidebar-ring));
|
|
529
529
|
|
|
530
|
-
--radius-lg:
|
|
531
|
-
--radius-md:
|
|
532
|
-
--radius-sm:
|
|
530
|
+
--radius-lg: 12px;
|
|
531
|
+
--radius-md: 8px;
|
|
532
|
+
--radius-sm: 6px;
|
|
533
|
+
|
|
533
534
|
|
|
534
535
|
/* Figma Design System Colors */
|
|
535
536
|
--color-white: #fff;
|
|
@@ -860,18 +861,16 @@ layer(base);
|
|
|
860
861
|
"cv01" on;
|
|
861
862
|
}
|
|
862
863
|
|
|
864
|
+
.container-xl {
|
|
865
|
+
@apply mx-auto max-w-[calc(1728px+64px)] w-full px-8;
|
|
866
|
+
}
|
|
867
|
+
|
|
863
868
|
.container-lg {
|
|
864
|
-
max-
|
|
865
|
-
width: 100%;
|
|
866
|
-
padding-inline: 32px;
|
|
867
|
-
margin-inline: auto;
|
|
869
|
+
@apply mx-auto max-w-[calc(1536px+64px)] w-full px-8;
|
|
868
870
|
}
|
|
869
871
|
|
|
870
872
|
.container-sm {
|
|
871
|
-
max-
|
|
872
|
-
width: 100%;
|
|
873
|
-
padding-inline: 32px;
|
|
874
|
-
margin-inline: auto;
|
|
873
|
+
@apply mx-auto max-w-[calc(1072px+64px)] w-full px-8;
|
|
875
874
|
}
|
|
876
875
|
}
|
|
877
876
|
|
|
@@ -1156,3 +1155,11 @@ layer(base);
|
|
|
1156
1155
|
display: table;
|
|
1157
1156
|
}
|
|
1158
1157
|
}
|
|
1158
|
+
|
|
1159
|
+
@utility underline-dotted {
|
|
1160
|
+
text-decoration-line: underline;
|
|
1161
|
+
text-decoration-style: dotted;
|
|
1162
|
+
text-decoration-skip-ink: none;
|
|
1163
|
+
text-decoration-thickness: 0.75px;
|
|
1164
|
+
text-underline-offset: 2px;
|
|
1165
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Button } from "
|
|
3
|
-
import { UtilityReset } from "
|
|
4
|
-
import { UtilityClose } from "
|
|
5
|
-
import { UtilityChevronDown } from "
|
|
6
|
-
import { NavAccount } from "
|
|
2
|
+
import { Button } from "../components/ui/button";
|
|
3
|
+
import { UtilityReset } from "../components/icons/UtilityReset";
|
|
4
|
+
import { UtilityClose } from "../components/icons/UtilityClose";
|
|
5
|
+
import { UtilityChevronDown } from "../components/icons/UtilityChevronDown";
|
|
6
|
+
import { NavAccount } from "../components/icons/NavAccount";
|
|
7
7
|
|
|
8
8
|
const meta = {
|
|
9
9
|
title: "Review/Button",
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
2
4
|
import {
|
|
3
5
|
Card,
|
|
4
6
|
CardHeader,
|
|
@@ -6,8 +8,19 @@ import {
|
|
|
6
8
|
CardTitle,
|
|
7
9
|
CardDescription,
|
|
8
10
|
CardContent,
|
|
9
|
-
} from "../components/ui/
|
|
10
|
-
import { Button } from "
|
|
11
|
+
} from "../components/ui/card";
|
|
12
|
+
import { Button } from "../components/ui/button";
|
|
13
|
+
import {
|
|
14
|
+
Select,
|
|
15
|
+
SelectContent,
|
|
16
|
+
SelectItem,
|
|
17
|
+
SelectTrigger,
|
|
18
|
+
SelectValue,
|
|
19
|
+
} from "../components/ui/select";
|
|
20
|
+
import { Label } from "../components/ui/label";
|
|
21
|
+
import { Toggle } from "../components/ui/toggle";
|
|
22
|
+
|
|
23
|
+
import { UtilityReset } from "../components/icons/UtilityReset";
|
|
11
24
|
|
|
12
25
|
const meta = {
|
|
13
26
|
title: "Review/Card",
|
|
@@ -82,3 +95,211 @@ export const Multiple: Story = {
|
|
|
82
95
|
</div>
|
|
83
96
|
),
|
|
84
97
|
};
|
|
98
|
+
|
|
99
|
+
export const FiltersPanel: Story = {
|
|
100
|
+
render: () => (
|
|
101
|
+
<Card className="w-[344px] bg-cyan-50 border-gray-100 rounded-xl p-6 shadow-none flex flex-col gap-6">
|
|
102
|
+
<div className="flex items-center justify-between w-full h-8">
|
|
103
|
+
<Label className="text-gray-800 overline-large">Filters</Label>
|
|
104
|
+
<Button
|
|
105
|
+
variant="secondary-light"
|
|
106
|
+
size="icon"
|
|
107
|
+
className="size-8 rounded-full border-gray-100 bg-white p-0"
|
|
108
|
+
>
|
|
109
|
+
<UtilityReset className="size-4" />
|
|
110
|
+
</Button>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div className="flex flex-col gap-6 w-full">
|
|
114
|
+
{/* Segmented Control */}
|
|
115
|
+
<div className="flex flex-col gap-3">
|
|
116
|
+
<Label className="text-gray-600">Label</Label>
|
|
117
|
+
<Toggle
|
|
118
|
+
options={[
|
|
119
|
+
{ label: "Active", value: "active" },
|
|
120
|
+
{ label: "Inactive", value: "inactive" },
|
|
121
|
+
]}
|
|
122
|
+
defaultValue="active"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{/* Search Selects */}
|
|
127
|
+
{[1, 2, 3].map((i) => (
|
|
128
|
+
<div key={i} className="flex flex-col gap-3">
|
|
129
|
+
<Label className="text-gray-600">Label</Label>
|
|
130
|
+
<Select>
|
|
131
|
+
<SelectTrigger className="h-12 border-gray-200 rounded-[6px] bg-white px-4">
|
|
132
|
+
<SelectValue placeholder="Placeholder" />
|
|
133
|
+
</SelectTrigger>
|
|
134
|
+
<SelectContent>
|
|
135
|
+
<SelectItem value="option1">Option 1</SelectItem>
|
|
136
|
+
<SelectItem value="option2">Option 2</SelectItem>
|
|
137
|
+
</SelectContent>
|
|
138
|
+
</Select>
|
|
139
|
+
</div>
|
|
140
|
+
))}
|
|
141
|
+
</div>
|
|
142
|
+
</Card>
|
|
143
|
+
),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// export const AttributesPanel: Story = {
|
|
147
|
+
// render: () => {
|
|
148
|
+
// const [activeTag, setActiveTag] = useState<string>("tag1");
|
|
149
|
+
// const tags = [
|
|
150
|
+
// { id: "tag1", label: "Tag" },
|
|
151
|
+
// { id: "tag2", label: "Tag" },
|
|
152
|
+
// { id: "tag3", label: "Tag" },
|
|
153
|
+
// ];
|
|
154
|
+
|
|
155
|
+
// return (
|
|
156
|
+
// <Card className="w-[344px] bg-cyan-50 border-gray-100 rounded-xl p-6 shadow-none flex flex-col gap-6">
|
|
157
|
+
// <div className="flex items-center justify-between w-full h-8">
|
|
158
|
+
// <Label className="text-gray-800 overline-large">Attributes</Label>
|
|
159
|
+
// </div>
|
|
160
|
+
|
|
161
|
+
// <div className="flex flex-col gap-6 w-full">
|
|
162
|
+
// {/* Segmented Control */}
|
|
163
|
+
// <div className="flex flex-col gap-3">
|
|
164
|
+
// <Label className="text-gray-600">Label</Label>
|
|
165
|
+
// <Toggle
|
|
166
|
+
// options={[
|
|
167
|
+
// { label: "Active", value: "active" },
|
|
168
|
+
// { label: "Inactive", value: "inactive" },
|
|
169
|
+
// ]}
|
|
170
|
+
// defaultValue="active"
|
|
171
|
+
// />
|
|
172
|
+
// </div>
|
|
173
|
+
|
|
174
|
+
// {/* Tags */}
|
|
175
|
+
// <div className="flex flex-col gap-3">
|
|
176
|
+
// <Label className="text-gray-600">Field name</Label>
|
|
177
|
+
// <div className="flex flex-wrap gap-3 items-start">
|
|
178
|
+
// {tags.map((tag) => (
|
|
179
|
+
// <Tag
|
|
180
|
+
// key={tag.id}
|
|
181
|
+
// active={activeTag === tag.id}
|
|
182
|
+
// onClick={() => setActiveTag(tag.id)}
|
|
183
|
+
// >
|
|
184
|
+
// {tag.label}
|
|
185
|
+
// </Tag>
|
|
186
|
+
// ))}
|
|
187
|
+
// </div>
|
|
188
|
+
// </div>
|
|
189
|
+
// </div>
|
|
190
|
+
// </Card>
|
|
191
|
+
// );
|
|
192
|
+
// },
|
|
193
|
+
// };
|
|
194
|
+
|
|
195
|
+
import {
|
|
196
|
+
IconConceptAir,
|
|
197
|
+
IconConceptCommunity,
|
|
198
|
+
IconConceptLight,
|
|
199
|
+
IconConceptMaterials,
|
|
200
|
+
IconConceptMind,
|
|
201
|
+
IconConceptMovement,
|
|
202
|
+
IconConceptNourishment,
|
|
203
|
+
IconConceptSound,
|
|
204
|
+
IconConceptThermalComfort,
|
|
205
|
+
IconConceptWater,
|
|
206
|
+
} from "../components/icons/ConceptIcons";
|
|
207
|
+
|
|
208
|
+
// ... existing imports
|
|
209
|
+
|
|
210
|
+
export const NavigatorPanel: Story = {
|
|
211
|
+
render: () => {
|
|
212
|
+
const [activeConcept, setActiveConcept] = useState<string>("community");
|
|
213
|
+
const [activeTheme, setActiveTheme] = useState<string>("C7");
|
|
214
|
+
const [activeStrategy, setActiveStrategy] = useState<string>("C7.4");
|
|
215
|
+
|
|
216
|
+
const concepts = [
|
|
217
|
+
{ id: "mind", Icon: IconConceptMind },
|
|
218
|
+
{ id: "community", Icon: IconConceptCommunity },
|
|
219
|
+
{ id: "movement", Icon: IconConceptMovement },
|
|
220
|
+
{ id: "water", Icon: IconConceptWater },
|
|
221
|
+
{ id: "air", Icon: IconConceptAir },
|
|
222
|
+
{ id: "light", Icon: IconConceptLight },
|
|
223
|
+
{ id: "thermal", Icon: IconConceptThermalComfort },
|
|
224
|
+
{ id: "nourishment", Icon: IconConceptNourishment },
|
|
225
|
+
{ id: "sound", Icon: IconConceptSound },
|
|
226
|
+
{ id: "materials", Icon: IconConceptMaterials },
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
const themes = Array.from({ length: 9 }, (_, i) => `C${9 - i}`).reverse();
|
|
230
|
+
const strategies = ["C7.1", "C7.2", "C7.3", "C7.4"];
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<Card className="w-[344px] bg-cyan-50 border-gray-100 rounded-xl p-6 shadow-none flex flex-col gap-6">
|
|
234
|
+
{/* Concepts */}
|
|
235
|
+
<div className="flex flex-col gap-3">
|
|
236
|
+
<Label className="text-gray-600">Concept</Label>
|
|
237
|
+
<div className="flex flex-wrap gap-2">
|
|
238
|
+
{concepts.map(({ id, Icon }) => (
|
|
239
|
+
<button
|
|
240
|
+
key={id}
|
|
241
|
+
onClick={() => setActiveConcept(id)}
|
|
242
|
+
className="rounded-full transition-transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2"
|
|
243
|
+
>
|
|
244
|
+
<Icon active={activeConcept === id} className="size-12" />
|
|
245
|
+
</button>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{/* Theme */}
|
|
251
|
+
<div className="flex flex-col gap-3">
|
|
252
|
+
<Label className="text-gray-600">Theme</Label>
|
|
253
|
+
<div className="flex flex-wrap gap-2">
|
|
254
|
+
{themes.map((theme) => (
|
|
255
|
+
<button
|
|
256
|
+
key={theme}
|
|
257
|
+
onClick={() => setActiveTheme(theme)}
|
|
258
|
+
className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${
|
|
259
|
+
activeTheme === theme
|
|
260
|
+
? "bg-cyan-800 text-white"
|
|
261
|
+
: "bg-blue-100 text-blue-600 hover:bg-blue-200"
|
|
262
|
+
}`}
|
|
263
|
+
>
|
|
264
|
+
<span className="body-small font-semibold">{theme}</span>
|
|
265
|
+
</button>
|
|
266
|
+
))}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
{/* Strategy */}
|
|
271
|
+
<div className="flex flex-col gap-3">
|
|
272
|
+
<Label className="text-gray-600">Strategy</Label>
|
|
273
|
+
<div className="flex flex-wrap gap-2">
|
|
274
|
+
{strategies.map((strategy) => (
|
|
275
|
+
<button
|
|
276
|
+
key={strategy}
|
|
277
|
+
onClick={() => setActiveStrategy(strategy)}
|
|
278
|
+
className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${
|
|
279
|
+
activeStrategy === strategy
|
|
280
|
+
? "bg-[#0F748A]/10 border border-[#0F748A]/20 text-cyan-900"
|
|
281
|
+
: "bg-blue-100 text-blue-600 hover:bg-blue-200"
|
|
282
|
+
}`}
|
|
283
|
+
>
|
|
284
|
+
<span className="body-small font-semibold">{strategy}</span>
|
|
285
|
+
</button>
|
|
286
|
+
))}
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
{/* Scope */}
|
|
291
|
+
<div className="flex flex-col gap-3">
|
|
292
|
+
<Label className="text-gray-600">Scope</Label>
|
|
293
|
+
<Toggle
|
|
294
|
+
className="w-full h-8 border border-gray-100 bg-white rounded-full p-0 [&>button]:flex-1"
|
|
295
|
+
options={[
|
|
296
|
+
{ label: "Non-core", value: "non-core" },
|
|
297
|
+
{ label: "Core", value: "core" },
|
|
298
|
+
]}
|
|
299
|
+
defaultValue="non-core"
|
|
300
|
+
/>
|
|
301
|
+
</div>
|
|
302
|
+
</Card>
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { CodeBadge } from "../components/ui/code-badge";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Review/CodeBadge",
|
|
6
|
+
component: CodeBadge,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "centered",
|
|
10
|
+
},
|
|
11
|
+
} satisfies Meta<typeof CodeBadge>;
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
args: {
|
|
19
|
+
code: "A01",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const WithClassName: Story = {
|
|
24
|
+
render: () => (
|
|
25
|
+
<div className="flex gap-4">
|
|
26
|
+
<CodeBadge
|
|
27
|
+
code="C01"
|
|
28
|
+
className="bg-plum-50 border-plum-200 text-plum-800"
|
|
29
|
+
/>
|
|
30
|
+
<CodeBadge
|
|
31
|
+
code="A02"
|
|
32
|
+
className="bg-cyan-50 border-cyan-200 text-cyan-800"
|
|
33
|
+
/>
|
|
34
|
+
<CodeBadge
|
|
35
|
+
code="W03"
|
|
36
|
+
className="bg-blue-50 border-blue-200 text-blue-800"
|
|
37
|
+
/>
|
|
38
|
+
<CodeBadge
|
|
39
|
+
code="N04"
|
|
40
|
+
className="bg-green-50 border-green-200 text-green-800"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithStyle: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<div className="flex gap-4">
|
|
49
|
+
<CodeBadge
|
|
50
|
+
code="R01"
|
|
51
|
+
className="border-none"
|
|
52
|
+
style={{
|
|
53
|
+
backgroundColor: "#EFF5FB",
|
|
54
|
+
color: "#2E74AD",
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
<CodeBadge
|
|
58
|
+
code="R02"
|
|
59
|
+
className="border-none"
|
|
60
|
+
style={{
|
|
61
|
+
backgroundColor: "#7B4F9D",
|
|
62
|
+
color: "white",
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
<CodeBadge
|
|
66
|
+
code="R03"
|
|
67
|
+
className="border-none"
|
|
68
|
+
style={{
|
|
69
|
+
backgroundColor: "#2E74AD",
|
|
70
|
+
color: "white",
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Button } from "
|
|
2
|
+
import { Button } from "../components/ui/button";
|
|
3
|
+
import { Input } from "../components/ui/input";
|
|
4
|
+
import { Label } from "../components/ui/label";
|
|
3
5
|
import { useState } from "react";
|
|
4
6
|
import {
|
|
5
7
|
Dialog,
|
|
6
8
|
DialogContent,
|
|
7
9
|
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
8
11
|
DialogHeader,
|
|
9
12
|
DialogTitle,
|
|
10
13
|
DialogTrigger,
|
|
11
|
-
} from "
|
|
12
|
-
import { cn } from "@/lib/utils";
|
|
14
|
+
} from "../components/ui/dialog";
|
|
13
15
|
|
|
14
16
|
const meta = {
|
|
15
17
|
title: "Review/Dialog",
|
|
@@ -29,7 +31,7 @@ export const Default: Story = {
|
|
|
29
31
|
const [open, setOpen] = useState(false);
|
|
30
32
|
return (
|
|
31
33
|
<>
|
|
32
|
-
<Dialog open={open}>
|
|
34
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
33
35
|
<DialogTrigger asChild>
|
|
34
36
|
<Button
|
|
35
37
|
size="large"
|
|
@@ -58,7 +60,7 @@ export const Default: Story = {
|
|
|
58
60
|
variant="general-primary"
|
|
59
61
|
onClick={() => setOpen(false)}
|
|
60
62
|
>
|
|
61
|
-
Yes
|
|
63
|
+
Yes, delete user
|
|
62
64
|
</Button>
|
|
63
65
|
</div>
|
|
64
66
|
</DialogContent>
|
|
@@ -67,3 +69,48 @@ export const Default: Story = {
|
|
|
67
69
|
);
|
|
68
70
|
},
|
|
69
71
|
};
|
|
72
|
+
|
|
73
|
+
export const WithForm: Story = {
|
|
74
|
+
render: () => {
|
|
75
|
+
const [open, setOpen] = useState(false);
|
|
76
|
+
return (
|
|
77
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
78
|
+
<DialogTrigger asChild>
|
|
79
|
+
<Button variant="general-secondary">Edit Profile</Button>
|
|
80
|
+
</DialogTrigger>
|
|
81
|
+
<DialogContent className="sm:max-w-[425px]">
|
|
82
|
+
<DialogHeader>
|
|
83
|
+
<DialogTitle>Edit profile</DialogTitle>
|
|
84
|
+
<DialogDescription>
|
|
85
|
+
Make changes to your profile here. Click save when you're done.
|
|
86
|
+
</DialogDescription>
|
|
87
|
+
</DialogHeader>
|
|
88
|
+
<div className="grid gap-4 py-4">
|
|
89
|
+
<div className=" flex flex-col gap-y-3">
|
|
90
|
+
<Label htmlFor="name">Name</Label>
|
|
91
|
+
<Input id="name" defaultValue="Pedro Duarte" />
|
|
92
|
+
</div>
|
|
93
|
+
<div className=" flex flex-col gap-y-3">
|
|
94
|
+
<Label htmlFor="username">Username</Label>
|
|
95
|
+
<Input
|
|
96
|
+
id="username"
|
|
97
|
+
defaultValue="@peduarte"
|
|
98
|
+
className="col-span-3"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<DialogFooter>
|
|
103
|
+
<Button
|
|
104
|
+
size="utility"
|
|
105
|
+
variant="general-primary"
|
|
106
|
+
type="submit"
|
|
107
|
+
onClick={() => setOpen(false)}
|
|
108
|
+
>
|
|
109
|
+
Save changes
|
|
110
|
+
</Button>
|
|
111
|
+
</DialogFooter>
|
|
112
|
+
</DialogContent>
|
|
113
|
+
</Dialog>
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
};
|