@syscore/ui-library 1.3.7 → 1.5.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/client/components/icons/ConceptIcons.tsx +2 -74
- package/client/components/ui/hero-section.tsx +45 -0
- package/client/components/ui/navigation.tsx +237 -76
- package/client/components/ui/page-header.tsx +145 -0
- package/client/components/ui/standard-table.tsx +554 -0
- package/client/global.css +198 -36
- package/client/lib/concept-colors.ts +115 -0
- package/client/lib/concept-icons.ts +45 -0
- package/client/lib/concepts-mock-data.ts +797 -0
- package/client/ui/Card.stories.tsx +2 -98
- package/client/ui/Hero.stories.tsx +72 -0
- package/client/ui/Navigation.stories.tsx +30 -21
- package/client/ui/PageHeader.stories.tsx +150 -0
- package/client/ui/Panel.stories.tsx +480 -0
- package/client/ui/StandardTable.stories.tsx +311 -0
- package/dist/img/half-sphere.png +0 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.es.js +789 -265
- package/package.json +1 -1
|
@@ -210,103 +210,6 @@ export const FiltersPanel: Story = {
|
|
|
210
210
|
// },
|
|
211
211
|
// };
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
export const NavigatorPanel: Story = {
|
|
215
|
-
render: () => {
|
|
216
|
-
const [activeConcept, setActiveConcept] = useState<string>("community");
|
|
217
|
-
const [activeTheme, setActiveTheme] = useState<string>("C7");
|
|
218
|
-
const [activeStrategy, setActiveStrategy] = useState<string>("C7.4");
|
|
219
|
-
|
|
220
|
-
const concepts = [
|
|
221
|
-
{ id: "mind", Icon: IconConceptMind },
|
|
222
|
-
{ id: "community", Icon: IconConceptCommunity },
|
|
223
|
-
{ id: "movement", Icon: IconConceptMovement },
|
|
224
|
-
{ id: "water", Icon: IconConceptWater },
|
|
225
|
-
{ id: "air", Icon: IconConceptAir },
|
|
226
|
-
{ id: "light", Icon: IconConceptLight },
|
|
227
|
-
{ id: "thermal", Icon: IconConceptThermalComfort },
|
|
228
|
-
{ id: "nourishment", Icon: IconConceptNourishment },
|
|
229
|
-
{ id: "sound", Icon: IconConceptSound },
|
|
230
|
-
{ id: "materials", Icon: IconConceptMaterials },
|
|
231
|
-
];
|
|
232
|
-
|
|
233
|
-
const themes = Array.from({ length: 9 }, (_, i) => `C${9 - i}`).reverse();
|
|
234
|
-
const strategies = ["C7.1", "C7.2", "C7.3", "C7.4"];
|
|
235
|
-
|
|
236
|
-
return (
|
|
237
|
-
<Card className="w-[344px] bg-cyan-50 border-gray-100 rounded-xl p-6 shadow-none flex flex-col gap-6">
|
|
238
|
-
{/* Concepts */}
|
|
239
|
-
<div className="flex flex-col gap-3">
|
|
240
|
-
<Label className="text-gray-600">Concept</Label>
|
|
241
|
-
<div className="flex flex-wrap gap-2">
|
|
242
|
-
{concepts.map(({ id, Icon }) => (
|
|
243
|
-
<button
|
|
244
|
-
key={id}
|
|
245
|
-
onClick={() => setActiveConcept(id)}
|
|
246
|
-
className="rounded-full transition-transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2"
|
|
247
|
-
>
|
|
248
|
-
<Icon active={activeConcept === id} className="size-12" />
|
|
249
|
-
</button>
|
|
250
|
-
))}
|
|
251
|
-
</div>
|
|
252
|
-
</div>
|
|
253
|
-
|
|
254
|
-
{/* Theme */}
|
|
255
|
-
<div className="flex flex-col gap-3">
|
|
256
|
-
<Label className="text-gray-600">Theme</Label>
|
|
257
|
-
<div className="flex flex-wrap gap-2">
|
|
258
|
-
{themes.map((theme) => (
|
|
259
|
-
<button
|
|
260
|
-
key={theme}
|
|
261
|
-
onClick={() => setActiveTheme(theme)}
|
|
262
|
-
className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${activeTheme === theme
|
|
263
|
-
? "bg-cyan-800 text-white"
|
|
264
|
-
: "bg-blue-100 text-blue-600 hover:bg-blue-200"
|
|
265
|
-
}`}
|
|
266
|
-
>
|
|
267
|
-
<span className="body-small font-semibold">{theme}</span>
|
|
268
|
-
</button>
|
|
269
|
-
))}
|
|
270
|
-
</div>
|
|
271
|
-
</div>
|
|
272
|
-
|
|
273
|
-
{/* Strategy */}
|
|
274
|
-
<div className="flex flex-col gap-3">
|
|
275
|
-
<Label className="text-gray-600">Strategy</Label>
|
|
276
|
-
<div className="flex flex-wrap gap-2">
|
|
277
|
-
{strategies.map((strategy) => (
|
|
278
|
-
<button
|
|
279
|
-
key={strategy}
|
|
280
|
-
onClick={() => setActiveStrategy(strategy)}
|
|
281
|
-
className={`w-12 h-8 rounded-[6px] flex items-center justify-center transition-colors ${activeStrategy === strategy
|
|
282
|
-
? "bg-[#0F748A]/10 border border-[#0F748A]/20 text-cyan-900"
|
|
283
|
-
: "bg-blue-100 text-blue-600 hover:bg-blue-200"
|
|
284
|
-
}`}
|
|
285
|
-
>
|
|
286
|
-
<span className="body-small font-semibold">{strategy}</span>
|
|
287
|
-
</button>
|
|
288
|
-
))}
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
|
|
292
|
-
{/* Scope */}
|
|
293
|
-
<div className="flex flex-col gap-3">
|
|
294
|
-
<Label className="text-gray-600">Scope</Label>
|
|
295
|
-
<Toggle
|
|
296
|
-
className="w-full h-8 border border-gray-100 bg-white rounded-full p-0 [&>button]:flex-1"
|
|
297
|
-
options={[
|
|
298
|
-
{ label: "Non-core", value: "non-core" },
|
|
299
|
-
{ label: "Core", value: "core" },
|
|
300
|
-
]}
|
|
301
|
-
defaultValue="non-core"
|
|
302
|
-
/>
|
|
303
|
-
</div>
|
|
304
|
-
</Card>
|
|
305
|
-
);
|
|
306
|
-
},
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
|
|
310
213
|
export const ConceptCard: Story = {
|
|
311
214
|
parameters: {
|
|
312
215
|
docs: {
|
|
@@ -431,4 +334,5 @@ export const ConceptCard: Story = {
|
|
|
431
334
|
</div>
|
|
432
335
|
);
|
|
433
336
|
},
|
|
434
|
-
};
|
|
337
|
+
};
|
|
338
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Button } from "../components/ui/button";
|
|
4
|
+
import { HeroSection } from "../components/ui/hero-section";
|
|
5
|
+
import { motion } from "motion/react";
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: "Review/Hero",
|
|
9
|
+
component: HeroSection,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: "fullscreen",
|
|
13
|
+
},
|
|
14
|
+
} satisfies Meta<typeof HeroSection>;
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
export const Default: Story = {
|
|
21
|
+
render: () => (
|
|
22
|
+
<HeroSection backgroundSlot={
|
|
23
|
+
<motion.div
|
|
24
|
+
className="absolute bottom-0 flex items-center justify-center max-w-[1303px] mx-auto hidden sm:block"
|
|
25
|
+
initial={{ opacity: 0, scale: 1 }}
|
|
26
|
+
animate={{ opacity: 1, scale: 1.05 }}
|
|
27
|
+
transition={{ duration: 1, ease: [0.25, 0.46, 0.45, 0.94] }}
|
|
28
|
+
style={{ willChange: "transform, opacity" }}
|
|
29
|
+
>
|
|
30
|
+
<motion.div
|
|
31
|
+
animate={{
|
|
32
|
+
y: [0, -15, 0],
|
|
33
|
+
scale: [1, 1.02, 1],
|
|
34
|
+
}}
|
|
35
|
+
transition={{
|
|
36
|
+
duration: 8,
|
|
37
|
+
ease: "easeInOut",
|
|
38
|
+
repeat: Infinity,
|
|
39
|
+
repeatType: "loop",
|
|
40
|
+
}}
|
|
41
|
+
style={{ willChange: "transform" }}
|
|
42
|
+
>
|
|
43
|
+
<img
|
|
44
|
+
src="/img/half-sphere.png"
|
|
45
|
+
alt="WELL Standard Sphere"
|
|
46
|
+
width={900}
|
|
47
|
+
height={900}
|
|
48
|
+
className="w-auto object-bottom object-contain select-none pointer-events-none "
|
|
49
|
+
/>
|
|
50
|
+
</motion.div>
|
|
51
|
+
</motion.div>
|
|
52
|
+
} contentSlot={
|
|
53
|
+
<motion.div
|
|
54
|
+
className="flex flex-wrap justify-center gap-4"
|
|
55
|
+
initial={{ opacity: 0, y: 20 }}
|
|
56
|
+
animate={{ opacity: 1, y: 0 }}
|
|
57
|
+
transition={{ duration: 0.6, delay: 0.5 }}
|
|
58
|
+
>
|
|
59
|
+
<Button variant="secondary-light" size="xlarge" className="shadow-2xl ">
|
|
60
|
+
Explore the standard
|
|
61
|
+
</Button>
|
|
62
|
+
<Button
|
|
63
|
+
size="xlarge"
|
|
64
|
+
variant="clear"
|
|
65
|
+
className="shadow-2xl bg-none bg-[#282A3133] px-15 text-white hover:bg-[#282a3166] "
|
|
66
|
+
>
|
|
67
|
+
See what's new
|
|
68
|
+
</Button>
|
|
69
|
+
</motion.div>} />
|
|
70
|
+
),
|
|
71
|
+
};
|
|
72
|
+
|
|
@@ -23,35 +23,44 @@ const navItems = [
|
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
export const Default: Story = {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
args: {
|
|
27
|
+
isStrategy: false,
|
|
28
|
+
onLinkClick: (href) => console.log("Clicked:", href),
|
|
29
|
+
pathname: "/",
|
|
30
|
+
},
|
|
31
|
+
render: (args) => (
|
|
32
|
+
<div className="h-screen bg-cyan-900">
|
|
28
33
|
<Navigation
|
|
29
|
-
|
|
30
|
-
isStrategy={false}
|
|
31
|
-
onLinkClick={(href) => console.log("Clicked:", href)}
|
|
34
|
+
{...args}
|
|
32
35
|
/>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
|
|
37
|
+
</div>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const WithNavItems: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
navItems: navItems,
|
|
44
|
+
isStrategy: false,
|
|
45
|
+
onLinkClick: (href) => console.log("Clicked:", href),
|
|
46
|
+
pathname: "/",
|
|
47
|
+
},
|
|
48
|
+
render: (args) => (
|
|
49
|
+
<div className="h-screen bg-cyan-900">
|
|
50
|
+
<Navigation {...args} />
|
|
39
51
|
</div>
|
|
40
52
|
),
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
export const StrategyMode: Story = {
|
|
44
|
-
|
|
56
|
+
args: {
|
|
57
|
+
isStrategy: true,
|
|
58
|
+
onLinkClick: (href) => console.log("Clicked:", href),
|
|
59
|
+
pathname: "/strategy",
|
|
60
|
+
},
|
|
61
|
+
render: (args) => (
|
|
45
62
|
<div className="h-screen bg-white">
|
|
46
|
-
<Navigation
|
|
47
|
-
navItems={navItems}
|
|
48
|
-
isStrategy={true}
|
|
49
|
-
onLinkClick={(href) => console.log("Clicked:", href)}
|
|
50
|
-
/>
|
|
51
|
-
|
|
52
|
-
<div className="pt-20 p-8">
|
|
53
|
-
<p className="text-gray-600">Strategy mode navigation.</p>
|
|
54
|
-
</div>
|
|
63
|
+
<Navigation {...args} />
|
|
55
64
|
</div>
|
|
56
65
|
),
|
|
57
66
|
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
PageHeader,
|
|
4
|
+
PageHeaderTopSection,
|
|
5
|
+
PageHeaderLeftContent,
|
|
6
|
+
PageHeaderBadge,
|
|
7
|
+
PageHeaderTitle,
|
|
8
|
+
PageHeaderDescription,
|
|
9
|
+
} from "../components/ui/page-header";
|
|
10
|
+
import { BadgeCertificationBronze } from "../components/icons/AchievementBadges";
|
|
11
|
+
import { CodeBadge } from "../components/ui/code-badge";
|
|
12
|
+
import {
|
|
13
|
+
Tooltip,
|
|
14
|
+
TooltipTrigger,
|
|
15
|
+
TooltipContent,
|
|
16
|
+
} from "../components/ui/tooltip";
|
|
17
|
+
|
|
18
|
+
const meta = {
|
|
19
|
+
title: "Review/PageHeader",
|
|
20
|
+
component: PageHeader,
|
|
21
|
+
parameters: {
|
|
22
|
+
layout: "padded",
|
|
23
|
+
},
|
|
24
|
+
tags: ["autodocs"],
|
|
25
|
+
} satisfies Meta<typeof PageHeader>;
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
type Story = StoryObj<typeof meta>;
|
|
29
|
+
|
|
30
|
+
// Header variant 1: Explore the standard (simple title + conditional animated badge)
|
|
31
|
+
export const ExploreStandard: Story = {
|
|
32
|
+
render: () => {
|
|
33
|
+
const hasAnyExpanded = true;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<PageHeader>
|
|
37
|
+
<PageHeaderTopSection>
|
|
38
|
+
<PageHeaderTitle className="heading-small">
|
|
39
|
+
Explore the standard
|
|
40
|
+
</PageHeaderTitle>
|
|
41
|
+
<PageHeaderBadge animated show={hasAnyExpanded}>
|
|
42
|
+
<BadgeCertificationBronze />
|
|
43
|
+
</PageHeaderBadge>
|
|
44
|
+
</PageHeaderTopSection>
|
|
45
|
+
|
|
46
|
+
<PageHeaderDescription className="body-large">
|
|
47
|
+
Dive into a unified, harmonized WELL. Browse concepts, themes and
|
|
48
|
+
strategies that drive progress toward healthier, people-first places.
|
|
49
|
+
</PageHeaderDescription>
|
|
50
|
+
</PageHeader>
|
|
51
|
+
);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Header variant 1: Without badge
|
|
56
|
+
export const ExploreStandardNoBadge: Story = {
|
|
57
|
+
render: () => {
|
|
58
|
+
const hasAnyExpanded = false;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<PageHeader>
|
|
62
|
+
<PageHeaderTopSection>
|
|
63
|
+
<PageHeaderTitle className="heading-small">
|
|
64
|
+
Explore the standard
|
|
65
|
+
</PageHeaderTitle>
|
|
66
|
+
<PageHeaderBadge animated show={hasAnyExpanded}>
|
|
67
|
+
<BadgeCertificationBronze />
|
|
68
|
+
</PageHeaderBadge>
|
|
69
|
+
</PageHeaderTopSection>
|
|
70
|
+
|
|
71
|
+
<PageHeaderDescription className="body-large">
|
|
72
|
+
Dive into a unified, harmonized WELL. Browse concepts, themes and
|
|
73
|
+
strategies that drive progress toward healthier, people-first places.
|
|
74
|
+
</PageHeaderDescription>
|
|
75
|
+
</PageHeader>
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Header variant 2: Theme header (CodeBadge + Tooltip + permanent badge)
|
|
81
|
+
export const ThemeHeader: Story = {
|
|
82
|
+
render: () => {
|
|
83
|
+
const themeCode = "C8";
|
|
84
|
+
const conceptColor = {
|
|
85
|
+
solid: "#0f748a",
|
|
86
|
+
contrast: "#0f748a",
|
|
87
|
+
border: "#0f748a",
|
|
88
|
+
};
|
|
89
|
+
const themeData = {
|
|
90
|
+
name: "Occupant experience",
|
|
91
|
+
description:
|
|
92
|
+
"This theme focuses on understanding and improving the overall experience of building occupants through research and feedback mechanisms.",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<PageHeader>
|
|
97
|
+
<PageHeaderTopSection className="mb-4">
|
|
98
|
+
<PageHeaderLeftContent>
|
|
99
|
+
<CodeBadge
|
|
100
|
+
code={themeCode}
|
|
101
|
+
style={{
|
|
102
|
+
backgroundColor: conceptColor.contrast || conceptColor.solid,
|
|
103
|
+
borderColor: conceptColor.border,
|
|
104
|
+
color: "white",
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
107
|
+
<Tooltip trigger="click">
|
|
108
|
+
<TooltipTrigger>
|
|
109
|
+
<span className="body-base text-gray-600 underline-dotted cursor-pointer">
|
|
110
|
+
Theme
|
|
111
|
+
</span>
|
|
112
|
+
</TooltipTrigger>
|
|
113
|
+
<TooltipContent className="max-w-[400px]">
|
|
114
|
+
<p className="body-base text-white">
|
|
115
|
+
Themes are groupings of related strategies that address
|
|
116
|
+
specific aspects of health and well-being within a concept
|
|
117
|
+
area.
|
|
118
|
+
</p>
|
|
119
|
+
</TooltipContent>
|
|
120
|
+
</Tooltip>
|
|
121
|
+
</PageHeaderLeftContent>
|
|
122
|
+
|
|
123
|
+
<PageHeaderBadge>
|
|
124
|
+
<BadgeCertificationBronze />
|
|
125
|
+
</PageHeaderBadge>
|
|
126
|
+
</PageHeaderTopSection>
|
|
127
|
+
|
|
128
|
+
<PageHeaderTitle className="heading-small mb-3">
|
|
129
|
+
{themeData.name}
|
|
130
|
+
</PageHeaderTitle>
|
|
131
|
+
<PageHeaderDescription className="body-large">
|
|
132
|
+
{themeData.description}
|
|
133
|
+
</PageHeaderDescription>
|
|
134
|
+
</PageHeader>
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Title only
|
|
140
|
+
export const TitleOnly: Story = {
|
|
141
|
+
render: () => {
|
|
142
|
+
return (
|
|
143
|
+
<PageHeader>
|
|
144
|
+
<PageHeaderTitle className="heading-small">
|
|
145
|
+
Title only header
|
|
146
|
+
</PageHeaderTitle>
|
|
147
|
+
</PageHeader>
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
};
|