@syscore/ui-library 1.25.0 → 2.0.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/ui/app-bar.tsx +299 -0
- package/client/components/ui/banner.tsx +108 -0
- package/client/components/ui/bottom-navigation.tsx +223 -0
- package/client/components/ui/card.tsx +108 -26
- package/client/components/ui/date-picker.tsx +188 -0
- package/client/components/ui/empty-state.tsx +197 -0
- package/client/components/ui/file-upload.tsx +214 -0
- package/client/components/ui/footer.tsx +179 -0
- package/client/components/ui/layout.tsx +381 -0
- package/client/components/ui/sidebar.tsx +11 -5
- package/client/components/ui/stepper.tsx +148 -0
- package/client/components/ui/system-bar.tsx +119 -0
- package/client/components/ui/timeline.tsx +181 -0
- package/client/global.css +2304 -41
- package/client/ui/AppBar/app-bar.stories.tsx +280 -0
- package/client/ui/Banner/banner.stories.tsx +238 -0
- package/client/ui/BottomNavigation/bottom-navigation.stories.tsx +285 -0
- package/client/ui/Card.stories.tsx +285 -168
- package/client/ui/DatePicker/DatePicker.stories.tsx +293 -0
- package/client/ui/EmptyState/empty-state.stories.tsx +251 -0
- package/client/ui/FileUpload/FileUpload.stories.tsx +218 -0
- package/client/ui/Footer/footer.stories.tsx +194 -0
- package/client/ui/Layout.stories.tsx +1481 -0
- package/client/ui/Stepper/Stepper.stories.tsx +344 -0
- package/client/ui/SystemBar/system-bar.stories.tsx +179 -0
- package/client/ui/Timeline/timeline.stories.tsx +404 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +219 -0
- package/dist/index.es.js +1504 -99
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { useState } from "react";
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
4
|
Card,
|
|
@@ -8,6 +7,10 @@ import {
|
|
|
8
7
|
CardTitle,
|
|
9
8
|
CardDescription,
|
|
10
9
|
CardContent,
|
|
10
|
+
CardMedia,
|
|
11
|
+
CardBadge,
|
|
12
|
+
CardActions,
|
|
13
|
+
CardMetric,
|
|
11
14
|
CardWithIcon,
|
|
12
15
|
} from "../components/ui/card";
|
|
13
16
|
import { Button } from "../components/ui/button";
|
|
@@ -36,29 +39,63 @@ import {
|
|
|
36
39
|
import { UtilityReset } from "../components/icons/UtilityReset";
|
|
37
40
|
|
|
38
41
|
const meta = {
|
|
39
|
-
title: "
|
|
42
|
+
title: "UI/Card",
|
|
40
43
|
component: Card,
|
|
41
44
|
tags: ["autodocs"],
|
|
42
45
|
parameters: {
|
|
43
46
|
layout: "padded",
|
|
44
47
|
docs: {
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
description: {
|
|
49
|
+
component: [
|
|
50
|
+
"Flexible card component with variants and composable sub-parts.",
|
|
51
|
+
"",
|
|
52
|
+
"**Import:**",
|
|
53
|
+
"```tsx",
|
|
54
|
+
`import {`,
|
|
55
|
+
` Card, CardHeader, CardTitle, CardDescription,`,
|
|
56
|
+
` CardContent, CardFooter,`,
|
|
57
|
+
` CardMedia, CardBadge, CardActions, CardMetric,`,
|
|
58
|
+
` CardWithIcon,`,
|
|
59
|
+
`} from "@syscore/ui-library"`,
|
|
60
|
+
"```",
|
|
61
|
+
"",
|
|
62
|
+
"**Variants:** `default` · `outlined` · `elevated` · `filled` · `interactive`",
|
|
63
|
+
].join("\n"),
|
|
47
64
|
},
|
|
65
|
+
source: { type: "code" },
|
|
48
66
|
},
|
|
49
67
|
},
|
|
50
68
|
} satisfies Meta<typeof Card>;
|
|
51
69
|
|
|
52
70
|
export default meta;
|
|
53
|
-
|
|
54
71
|
type Story = StoryObj<typeof meta>;
|
|
55
72
|
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Variants
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
56
77
|
export const Default: Story = {
|
|
78
|
+
parameters: {
|
|
79
|
+
docs: {
|
|
80
|
+
source: {
|
|
81
|
+
code: `import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@syscore/ui-library"
|
|
82
|
+
|
|
83
|
+
<Card variant="default">
|
|
84
|
+
<CardHeader>
|
|
85
|
+
<CardTitle>Default Card</CardTitle>
|
|
86
|
+
<CardDescription>Cyan-50 background with a subtle border.</CardDescription>
|
|
87
|
+
</CardHeader>
|
|
88
|
+
<CardContent>Main content area.</CardContent>
|
|
89
|
+
<CardFooter>Footer information</CardFooter>
|
|
90
|
+
</Card>`,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
57
94
|
render: () => (
|
|
58
|
-
<Card className="w-full max-w-sm">
|
|
95
|
+
<Card variant="default" className="w-full max-w-sm">
|
|
59
96
|
<CardHeader>
|
|
60
|
-
<CardTitle>Card
|
|
61
|
-
<CardDescription>
|
|
97
|
+
<CardTitle>Default Card</CardTitle>
|
|
98
|
+
<CardDescription>Cyan-50 background with a subtle border.</CardDescription>
|
|
62
99
|
</CardHeader>
|
|
63
100
|
<CardContent>This is the main content area of the card.</CardContent>
|
|
64
101
|
<CardFooter>
|
|
@@ -68,6 +105,223 @@ export const Default: Story = {
|
|
|
68
105
|
),
|
|
69
106
|
};
|
|
70
107
|
|
|
108
|
+
export const Outlined: Story = {
|
|
109
|
+
render: () => (
|
|
110
|
+
<Card variant="outlined" className="w-full max-w-sm">
|
|
111
|
+
<CardHeader>
|
|
112
|
+
<CardTitle>Outlined Card</CardTitle>
|
|
113
|
+
<CardDescription>White background with a visible border, no fill.</CardDescription>
|
|
114
|
+
</CardHeader>
|
|
115
|
+
<CardContent>Clean and minimal — great for secondary content areas.</CardContent>
|
|
116
|
+
<CardFooter>
|
|
117
|
+
<Button size="large" variant="secondary-light">Action</Button>
|
|
118
|
+
</CardFooter>
|
|
119
|
+
</Card>
|
|
120
|
+
),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const Elevated: Story = {
|
|
124
|
+
render: () => (
|
|
125
|
+
<Card variant="elevated" className="w-full max-w-sm">
|
|
126
|
+
<CardHeader>
|
|
127
|
+
<CardTitle>Elevated Card</CardTitle>
|
|
128
|
+
<CardDescription>White background with a drop shadow, no border.</CardDescription>
|
|
129
|
+
</CardHeader>
|
|
130
|
+
<CardContent>Use this to make content float above the page surface.</CardContent>
|
|
131
|
+
<CardFooter>
|
|
132
|
+
<Button size="large" variant="primary-dark">Get started</Button>
|
|
133
|
+
</CardFooter>
|
|
134
|
+
</Card>
|
|
135
|
+
),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const Filled: Story = {
|
|
139
|
+
render: () => (
|
|
140
|
+
<Card variant="filled" className="w-full max-w-sm">
|
|
141
|
+
<CardHeader>
|
|
142
|
+
<CardTitle>Filled Card</CardTitle>
|
|
143
|
+
<CardDescription>Dark background for high-contrast sections.</CardDescription>
|
|
144
|
+
</CardHeader>
|
|
145
|
+
<CardContent>Use for featured content, CTAs, or dark-themed sections.</CardContent>
|
|
146
|
+
<CardFooter>
|
|
147
|
+
<Button size="large" variant="tertiary-light">Learn more</Button>
|
|
148
|
+
</CardFooter>
|
|
149
|
+
</Card>
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const Interactive: Story = {
|
|
154
|
+
render: () => (
|
|
155
|
+
<div className="grid grid-cols-1 gap-4 max-w-2xl">
|
|
156
|
+
{["Card One", "Card Two", "Card Three"].map((title) => (
|
|
157
|
+
<Card
|
|
158
|
+
key={title}
|
|
159
|
+
variant="interactive"
|
|
160
|
+
className="w-full"
|
|
161
|
+
onClick={() => alert(`Clicked: ${title}`)}
|
|
162
|
+
>
|
|
163
|
+
<CardHeader>
|
|
164
|
+
<CardTitle>{title}</CardTitle>
|
|
165
|
+
<CardDescription>Click me — I'm interactive.</CardDescription>
|
|
166
|
+
</CardHeader>
|
|
167
|
+
<CardContent>Hover to see the scale effect, click to interact.</CardContent>
|
|
168
|
+
</Card>
|
|
169
|
+
))}
|
|
170
|
+
</div>
|
|
171
|
+
),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const AllVariants: Story = {
|
|
175
|
+
render: () => (
|
|
176
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 max-w-5xl">
|
|
177
|
+
{(["default", "outlined", "elevated", "filled", "interactive"] as const).map((variant) => (
|
|
178
|
+
<Card key={variant} variant={variant}>
|
|
179
|
+
<CardHeader>
|
|
180
|
+
<CardTitle>{variant.charAt(0).toUpperCase() + variant.slice(1)}</CardTitle>
|
|
181
|
+
<CardDescription>variant="{variant}"</CardDescription>
|
|
182
|
+
</CardHeader>
|
|
183
|
+
<CardContent>Card content goes here.</CardContent>
|
|
184
|
+
</Card>
|
|
185
|
+
))}
|
|
186
|
+
</div>
|
|
187
|
+
),
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Sub-components
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
export const WithMedia: Story = {
|
|
195
|
+
parameters: {
|
|
196
|
+
docs: {
|
|
197
|
+
source: {
|
|
198
|
+
code: `import { Card, CardMedia, CardHeader, CardTitle, CardDescription, CardActions } from "@syscore/ui-library"
|
|
199
|
+
import { Button } from "@syscore/ui-library"
|
|
200
|
+
|
|
201
|
+
<Card variant="outlined" className="p-0">
|
|
202
|
+
<CardMedia src="/your-image.jpg" alt="Description" height={200} />
|
|
203
|
+
<div className="p-6 flex flex-col gap-6">
|
|
204
|
+
<CardHeader>
|
|
205
|
+
<CardTitle>Media Card</CardTitle>
|
|
206
|
+
<CardDescription>Full-bleed image above the content.</CardDescription>
|
|
207
|
+
</CardHeader>
|
|
208
|
+
<CardActions>
|
|
209
|
+
<Button variant="primary-dark" size="large">View details</Button>
|
|
210
|
+
<Button variant="secondary-light" size="large">Save</Button>
|
|
211
|
+
</CardActions>
|
|
212
|
+
</div>
|
|
213
|
+
</Card>`,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
render: () => (
|
|
218
|
+
<Card variant="outlined" className="w-full max-w-sm p-0">
|
|
219
|
+
<CardMedia
|
|
220
|
+
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&q=80"
|
|
221
|
+
alt="Mountain landscape"
|
|
222
|
+
height={200}
|
|
223
|
+
/>
|
|
224
|
+
<div className="p-6 flex flex-col gap-6">
|
|
225
|
+
<CardHeader>
|
|
226
|
+
<CardTitle>Media Card</CardTitle>
|
|
227
|
+
<CardDescription>A full-bleed image sits above the card content.</CardDescription>
|
|
228
|
+
</CardHeader>
|
|
229
|
+
<CardActions>
|
|
230
|
+
<Button size="large" variant="primary-dark">View details</Button>
|
|
231
|
+
<Button size="large" variant="secondary-light">Save</Button>
|
|
232
|
+
</CardActions>
|
|
233
|
+
</div>
|
|
234
|
+
</Card>
|
|
235
|
+
),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export const WithBadge: Story = {
|
|
239
|
+
render: () => (
|
|
240
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 max-w-2xl">
|
|
241
|
+
{(["default", "success", "warning", "error", "info"] as const).map((color) => (
|
|
242
|
+
<Card key={color} variant="outlined" className="w-full">
|
|
243
|
+
<CardBadge color={color}>
|
|
244
|
+
{color.charAt(0).toUpperCase() + color.slice(1)}
|
|
245
|
+
</CardBadge>
|
|
246
|
+
<CardHeader>
|
|
247
|
+
<CardTitle>Badge Card</CardTitle>
|
|
248
|
+
<CardDescription>Status shown via top-right badge.</CardDescription>
|
|
249
|
+
</CardHeader>
|
|
250
|
+
<CardContent>Badge color: {color}</CardContent>
|
|
251
|
+
</Card>
|
|
252
|
+
))}
|
|
253
|
+
</div>
|
|
254
|
+
),
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const WithActions: Story = {
|
|
258
|
+
render: () => (
|
|
259
|
+
<Card variant="default" className="w-full max-w-sm">
|
|
260
|
+
<CardHeader>
|
|
261
|
+
<CardTitle>Card with Actions</CardTitle>
|
|
262
|
+
<CardDescription>Action buttons sit in a dedicated row with a separator.</CardDescription>
|
|
263
|
+
</CardHeader>
|
|
264
|
+
<CardContent>Review the details below before confirming.</CardContent>
|
|
265
|
+
<CardActions>
|
|
266
|
+
<Button size="large" variant="primary-dark">Confirm</Button>
|
|
267
|
+
<Button size="large" variant="secondary-light">Cancel</Button>
|
|
268
|
+
</CardActions>
|
|
269
|
+
</Card>
|
|
270
|
+
),
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export const MetricCard: Story = {
|
|
274
|
+
parameters: {
|
|
275
|
+
docs: {
|
|
276
|
+
source: {
|
|
277
|
+
code: `import { Card, CardMetric } from "@syscore/ui-library"
|
|
278
|
+
|
|
279
|
+
<Card variant="outlined">
|
|
280
|
+
<CardMetric
|
|
281
|
+
value="2,847"
|
|
282
|
+
label="Total projects"
|
|
283
|
+
trend="up"
|
|
284
|
+
trendValue="12% this month"
|
|
285
|
+
/>
|
|
286
|
+
</Card>`,
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
render: () => (
|
|
291
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-3xl">
|
|
292
|
+
<Card variant="outlined">
|
|
293
|
+
<CardMetric value="2,847" label="Total projects" trend="up" trendValue="12% this month" />
|
|
294
|
+
</Card>
|
|
295
|
+
<Card variant="outlined">
|
|
296
|
+
<CardMetric value="94%" label="Compliance score" trend="up" trendValue="3% vs last quarter" />
|
|
297
|
+
</Card>
|
|
298
|
+
<Card variant="outlined">
|
|
299
|
+
<CardMetric value="18" label="Open issues" trend="down" trendValue="5 resolved today" />
|
|
300
|
+
</Card>
|
|
301
|
+
</div>
|
|
302
|
+
),
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export const MetricCardFilled: Story = {
|
|
306
|
+
render: () => (
|
|
307
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-3xl">
|
|
308
|
+
<Card variant="filled">
|
|
309
|
+
<CardMetric value="2,847" label="Total projects" trend="up" trendValue="12% this month" />
|
|
310
|
+
</Card>
|
|
311
|
+
<Card variant="filled">
|
|
312
|
+
<CardMetric value="94%" label="Compliance score" trend="neutral" trendValue="No change" />
|
|
313
|
+
</Card>
|
|
314
|
+
<Card variant="filled">
|
|
315
|
+
<CardMetric value="18" label="Open issues" trend="down" trendValue="5 resolved today" />
|
|
316
|
+
</Card>
|
|
317
|
+
</div>
|
|
318
|
+
),
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
// Existing stories preserved
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
|
|
71
325
|
export const WithButton: Story = {
|
|
72
326
|
render: () => (
|
|
73
327
|
<Card className="w-full max-w-sm">
|
|
@@ -92,24 +346,14 @@ export const WithButton: Story = {
|
|
|
92
346
|
export const Multiple: Story = {
|
|
93
347
|
render: () => (
|
|
94
348
|
<div className="grid grid-cols-1 gap-4 max-w-2xl">
|
|
95
|
-
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<CardTitle className="heading-xsmall">Card Two</CardTitle>
|
|
104
|
-
</CardHeader>
|
|
105
|
-
<CardContent>Content for the second card</CardContent>
|
|
106
|
-
</Card>
|
|
107
|
-
<Card>
|
|
108
|
-
<CardHeader>
|
|
109
|
-
<CardTitle className="heading-xsmall">Card Three</CardTitle>
|
|
110
|
-
</CardHeader>
|
|
111
|
-
<CardContent>Content for the third card</CardContent>
|
|
112
|
-
</Card>
|
|
349
|
+
{["Card One", "Card Two", "Card Three"].map((title) => (
|
|
350
|
+
<Card key={title}>
|
|
351
|
+
<CardHeader>
|
|
352
|
+
<CardTitle className="heading-xsmall">{title}</CardTitle>
|
|
353
|
+
</CardHeader>
|
|
354
|
+
<CardContent>Content for {title.toLowerCase()}</CardContent>
|
|
355
|
+
</Card>
|
|
356
|
+
))}
|
|
113
357
|
</div>
|
|
114
358
|
),
|
|
115
359
|
};
|
|
@@ -129,7 +373,6 @@ export const FiltersPanel: Story = {
|
|
|
129
373
|
</div>
|
|
130
374
|
|
|
131
375
|
<div className="flex flex-col gap-6 w-full">
|
|
132
|
-
{/* Segmented Control */}
|
|
133
376
|
<div className="flex flex-col gap-3">
|
|
134
377
|
<Label className="text-gray-600">Label</Label>
|
|
135
378
|
<Toggle
|
|
@@ -141,7 +384,6 @@ export const FiltersPanel: Story = {
|
|
|
141
384
|
/>
|
|
142
385
|
</div>
|
|
143
386
|
|
|
144
|
-
{/* Search Selects */}
|
|
145
387
|
{[1, 2, 3].map((i) => (
|
|
146
388
|
<div key={i} className="flex flex-col gap-3">
|
|
147
389
|
<Label className="text-gray-600">Label</Label>
|
|
@@ -161,150 +403,26 @@ export const FiltersPanel: Story = {
|
|
|
161
403
|
),
|
|
162
404
|
};
|
|
163
405
|
|
|
164
|
-
// export const AttributesPanel: Story = {
|
|
165
|
-
// render: () => {
|
|
166
|
-
// const [activeTag, setActiveTag] = useState<string>("tag1");
|
|
167
|
-
// const tags = [
|
|
168
|
-
// { id: "tag1", label: "Tag" },
|
|
169
|
-
// { id: "tag2", label: "Tag" },
|
|
170
|
-
// { id: "tag3", label: "Tag" },
|
|
171
|
-
// ];
|
|
172
|
-
|
|
173
|
-
// return (
|
|
174
|
-
// <Card className="w-[344px] bg-cyan-50 border-gray-100 rounded-xl p-6 shadow-none flex flex-col gap-6">
|
|
175
|
-
// <div className="flex items-center justify-between w-full h-8">
|
|
176
|
-
// <Label className="text-gray-800 overline-large">Attributes</Label>
|
|
177
|
-
// </div>
|
|
178
|
-
|
|
179
|
-
// <div className="flex flex-col gap-6 w-full">
|
|
180
|
-
// {/* Segmented Control */}
|
|
181
|
-
// <div className="flex flex-col gap-3">
|
|
182
|
-
// <Label className="text-gray-600">Label</Label>
|
|
183
|
-
// <Toggle
|
|
184
|
-
// options={[
|
|
185
|
-
// { label: "Active", value: "active" },
|
|
186
|
-
// { label: "Inactive", value: "inactive" },
|
|
187
|
-
// ]}
|
|
188
|
-
// defaultValue="active"
|
|
189
|
-
// />
|
|
190
|
-
// </div>
|
|
191
|
-
|
|
192
|
-
// {/* Tags */}
|
|
193
|
-
// <div className="flex flex-col gap-3">
|
|
194
|
-
// <Label className="text-gray-600">Field name</Label>
|
|
195
|
-
// <div className="flex flex-wrap gap-3 items-start">
|
|
196
|
-
// {tags.map((tag) => (
|
|
197
|
-
// <Tag
|
|
198
|
-
// key={tag.id}
|
|
199
|
-
// active={activeTag === tag.id}
|
|
200
|
-
// onClick={() => setActiveTag(tag.id)}
|
|
201
|
-
// >
|
|
202
|
-
// {tag.label}
|
|
203
|
-
// </Tag>
|
|
204
|
-
// ))}
|
|
205
|
-
// </div>
|
|
206
|
-
// </div>
|
|
207
|
-
// </div>
|
|
208
|
-
// </Card>
|
|
209
|
-
// );
|
|
210
|
-
// },
|
|
211
|
-
// };
|
|
212
|
-
|
|
213
406
|
export const ConceptCard: Story = {
|
|
214
407
|
parameters: {
|
|
215
|
-
docs: {
|
|
216
|
-
source: {
|
|
217
|
-
type: "code",
|
|
218
|
-
},
|
|
219
|
-
},
|
|
408
|
+
docs: { source: { type: "code" } },
|
|
220
409
|
},
|
|
221
410
|
render: () => {
|
|
222
411
|
const CONCEPTS_MOCK = [
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
title: "WATER",
|
|
235
|
-
description: "Increase hydration and access to high quality water",
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
id: "nourishment",
|
|
239
|
-
slug: "nourishment",
|
|
240
|
-
name: "Nourishment",
|
|
241
|
-
title: "NOURISHMENT",
|
|
242
|
-
description:
|
|
243
|
-
"Make healthy foods the easy decision and encourage better food choices",
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
id: "light",
|
|
247
|
-
slug: "light",
|
|
248
|
-
name: "Light",
|
|
249
|
-
title: "LIGHT",
|
|
250
|
-
description:
|
|
251
|
-
"Design lighting to increase alertness, enhance occupant experience, and promote optimal sleep patterns",
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
id: "movement",
|
|
255
|
-
slug: "movement",
|
|
256
|
-
name: "Movement",
|
|
257
|
-
title: "MOVEMENT",
|
|
258
|
-
description: "Integrate physical activity and fitness into everyday life",
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
id: "thermal-comfort",
|
|
262
|
-
slug: "thermal-comfort",
|
|
263
|
-
name: "Thermal Comfort",
|
|
264
|
-
title: "THERMAL COMFORT",
|
|
265
|
-
description: "Provide productive and comfortable indoor environments",
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
id: "sound",
|
|
269
|
-
slug: "sound",
|
|
270
|
-
name: "Sound",
|
|
271
|
-
title: "SOUND",
|
|
272
|
-
description:
|
|
273
|
-
"Support optimal acoustical comfort to reduce distractions and promote focus",
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
id: "materials",
|
|
277
|
-
slug: "materials",
|
|
278
|
-
name: "Materials",
|
|
279
|
-
title: "MATERIALS",
|
|
280
|
-
description:
|
|
281
|
-
"Improve respiratory health through use of safe materials and finishes",
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
id: "community",
|
|
285
|
-
slug: "community",
|
|
286
|
-
name: "Community",
|
|
287
|
-
title: "COMMUNITY",
|
|
288
|
-
description: "Foster community engagement and social support",
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
id: "mind",
|
|
292
|
-
slug: "mind",
|
|
293
|
-
name: "Mind",
|
|
294
|
-
title: "MIND",
|
|
295
|
-
description:
|
|
296
|
-
"Support cognitive and emotional health through design, technology, and treatment strategies",
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
id: "innovation",
|
|
300
|
-
slug: "innovation",
|
|
301
|
-
name: "Innovation",
|
|
302
|
-
title: "INNOVATION",
|
|
303
|
-
description: "Develop unique strategies for creating healthy environments",
|
|
304
|
-
},
|
|
412
|
+
{ id: "air", slug: "air", title: "AIR", description: "Reduce or minimize sources of indoor air pollution" },
|
|
413
|
+
{ id: "water", slug: "water", title: "WATER", description: "Increase hydration and access to high quality water" },
|
|
414
|
+
{ id: "nourishment", slug: "nourishment", title: "NOURISHMENT", description: "Make healthy foods the easy decision and encourage better food choices" },
|
|
415
|
+
{ id: "light", slug: "light", title: "LIGHT", description: "Design lighting to increase alertness, enhance occupant experience, and promote optimal sleep patterns" },
|
|
416
|
+
{ id: "movement", slug: "movement", title: "MOVEMENT", description: "Integrate physical activity and fitness into everyday life" },
|
|
417
|
+
{ id: "thermal-comfort", slug: "thermal-comfort", title: "THERMAL COMFORT", description: "Provide productive and comfortable indoor environments" },
|
|
418
|
+
{ id: "sound", slug: "sound", title: "SOUND", description: "Support optimal acoustical comfort to reduce distractions and promote focus" },
|
|
419
|
+
{ id: "materials", slug: "materials", title: "MATERIALS", description: "Improve respiratory health through use of safe materials and finishes" },
|
|
420
|
+
{ id: "community", slug: "community", title: "COMMUNITY", description: "Foster community engagement and social support" },
|
|
421
|
+
{ id: "mind", slug: "mind", title: "MIND", description: "Support cognitive and emotional health through design, technology, and treatment strategies" },
|
|
422
|
+
{ id: "innovation", slug: "innovation", title: "INNOVATION", description: "Develop unique strategies for creating healthy environments" },
|
|
305
423
|
];
|
|
306
424
|
|
|
307
|
-
const CONCEPT_ICONS = {
|
|
425
|
+
const CONCEPT_ICONS: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
|
|
308
426
|
air: IconConceptAir,
|
|
309
427
|
water: IconConceptWater,
|
|
310
428
|
nourishment: IconConceptNourishment,
|
|
@@ -319,9 +437,9 @@ export const ConceptCard: Story = {
|
|
|
319
437
|
};
|
|
320
438
|
|
|
321
439
|
return (
|
|
322
|
-
<div className="grid grid-cols-1 md:grid-cols-3
|
|
440
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
323
441
|
{CONCEPTS_MOCK.map((concept) => {
|
|
324
|
-
const Icon = CONCEPT_ICONS[concept.slug
|
|
442
|
+
const Icon = CONCEPT_ICONS[concept.slug];
|
|
325
443
|
return (
|
|
326
444
|
<CardWithIcon
|
|
327
445
|
key={concept.id}
|
|
@@ -335,4 +453,3 @@ export const ConceptCard: Story = {
|
|
|
335
453
|
);
|
|
336
454
|
},
|
|
337
455
|
};
|
|
338
|
-
|