astryx-dracula 0.5.0 → 0.6.2

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.
Files changed (129) hide show
  1. package/BRAND.md +3 -3
  2. package/README.md +7 -4
  3. package/USAGE.md +49 -49
  4. package/astryx-dracula.d.ts +2 -2
  5. package/astryx-dracula.js +25 -9
  6. package/astryx-dracula.variants.d.ts +2 -2
  7. package/astryx.integration.mjs +1 -1
  8. package/icons.mjs +21 -17
  9. package/icons.tsx +69 -62
  10. package/package.json +125 -120
  11. package/shared/auth-chrome-config.ts +6 -6
  12. package/shared/auth-chrome.tsx +143 -144
  13. package/shared/auth-copy.ts +14 -14
  14. package/shared/chaptered-doc-config.ts +48 -51
  15. package/shared/chaptered-doc.tsx +267 -289
  16. package/shared/chart-hues.ts +25 -10
  17. package/shared/chart-labels.tsx +31 -30
  18. package/shared/chart-legend.tsx +55 -56
  19. package/shared/chart-panel-style.ts +3 -3
  20. package/shared/data-bar.tsx +97 -102
  21. package/shared/format-money.ts +1 -1
  22. package/shared/gallery-image.ts +6 -6
  23. package/shared/login-demo.ts +1 -1
  24. package/shared/metric-card.tsx +101 -105
  25. package/shared/metric-delta.tsx +28 -27
  26. package/shared/order-desk.tsx +205 -216
  27. package/shared/revenue-chart.tsx +145 -165
  28. package/shared/scene-castle.tsx +206 -291
  29. package/shared/scene-frame.tsx +33 -32
  30. package/shared/scene-hues.ts +17 -17
  31. package/shared/scene-tile.tsx +88 -92
  32. package/shared/settings-data.ts +119 -125
  33. package/shared/settings-rows.tsx +122 -159
  34. package/shared/sparkline.tsx +75 -88
  35. package/shared/sso-icons.tsx +10 -22
  36. package/shared/stars.tsx +16 -25
  37. package/templates/ai-chat-landing.template.mjs +5 -4
  38. package/templates/ai-chat-landing.tsx +473 -460
  39. package/templates/ai-chat.template.mjs +5 -4
  40. package/templates/ai-chat.tsx +543 -612
  41. package/templates/blank.template.mjs +5 -4
  42. package/templates/blank.tsx +18 -14
  43. package/templates/centered-hero.template.mjs +5 -4
  44. package/templates/centered-hero.tsx +55 -68
  45. package/templates/classic-gallery.template.mjs +5 -4
  46. package/templates/classic-gallery.tsx +97 -104
  47. package/templates/contact-form.template.mjs +5 -4
  48. package/templates/contact-form.tsx +247 -282
  49. package/templates/dashboard-portfolio.template.mjs +5 -4
  50. package/templates/dashboard-portfolio.tsx +717 -548
  51. package/templates/dashboard.template.mjs +5 -4
  52. package/templates/dashboard.tsx +596 -567
  53. package/templates/detail-page.template.mjs +5 -4
  54. package/templates/detail-page.tsx +546 -603
  55. package/templates/documentation-design.template.mjs +5 -4
  56. package/templates/documentation-design.tsx +231 -243
  57. package/templates/documentation-technical.template.mjs +5 -4
  58. package/templates/documentation-technical.tsx +236 -239
  59. package/templates/documentation.template.mjs +5 -4
  60. package/templates/documentation.tsx +267 -279
  61. package/templates/editor.template.mjs +5 -4
  62. package/templates/editor.tsx +844 -877
  63. package/templates/file-explorer.template.mjs +5 -4
  64. package/templates/file-explorer.tsx +566 -590
  65. package/templates/form-two-column.template.mjs +5 -4
  66. package/templates/form-two-column.tsx +195 -210
  67. package/templates/gallery-hero.template.mjs +5 -4
  68. package/templates/gallery-hero.tsx +93 -114
  69. package/templates/ide.template.mjs +5 -4
  70. package/templates/ide.tsx +407 -453
  71. package/templates/incident-console.template.mjs +5 -4
  72. package/templates/incident-console.tsx +593 -575
  73. package/templates/kanban-board.template.mjs +5 -4
  74. package/templates/kanban-board.tsx +665 -683
  75. package/templates/library.template.mjs +5 -4
  76. package/templates/library.tsx +409 -430
  77. package/templates/login-card.template.mjs +5 -4
  78. package/templates/login-card.tsx +63 -72
  79. package/templates/login-split.template.mjs +5 -4
  80. package/templates/login-split.tsx +192 -193
  81. package/templates/login-sso.template.mjs +5 -4
  82. package/templates/login-sso.tsx +257 -258
  83. package/templates/login.template.mjs +5 -4
  84. package/templates/login.tsx +53 -49
  85. package/templates/messaging-shell.template.mjs +5 -4
  86. package/templates/messaging-shell.tsx +556 -574
  87. package/templates/mixed-gallery.template.mjs +5 -4
  88. package/templates/mixed-gallery.tsx +60 -58
  89. package/templates/payment-form.template.mjs +5 -4
  90. package/templates/payment-form.tsx +882 -991
  91. package/templates/product-detail.template.mjs +5 -4
  92. package/templates/product-detail.tsx +261 -272
  93. package/templates/product-gallery.template.mjs +5 -4
  94. package/templates/product-gallery.tsx +126 -137
  95. package/templates/product-tour.template.mjs +5 -4
  96. package/templates/product-tour.tsx +741 -773
  97. package/templates/settings-dialog.template.mjs +5 -4
  98. package/templates/settings-dialog.tsx +688 -717
  99. package/templates/settings-sidebar.template.mjs +5 -4
  100. package/templates/settings-sidebar.tsx +762 -807
  101. package/templates/settings.template.mjs +5 -4
  102. package/templates/settings.tsx +289 -304
  103. package/templates/shell-nav.template.mjs +5 -4
  104. package/templates/shell-nav.tsx +212 -219
  105. package/templates/shell-side-nav.template.mjs +5 -4
  106. package/templates/shell-side-nav.tsx +135 -157
  107. package/templates/shell-top-nav.template.mjs +5 -4
  108. package/templates/shell-top-nav.tsx +182 -203
  109. package/templates/side-gallery.template.mjs +5 -4
  110. package/templates/side-gallery.tsx +85 -90
  111. package/templates/table-grouped.template.mjs +5 -4
  112. package/templates/table-grouped.tsx +1000 -1053
  113. package/templates/table-page-chart.template.mjs +5 -4
  114. package/templates/table-page-chart.tsx +400 -398
  115. package/templates/table-page-heatmap-status.template.mjs +5 -4
  116. package/templates/table-page-heatmap-status.tsx +483 -512
  117. package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
  118. package/templates/table-page-shoe-store-heatmap.tsx +748 -746
  119. package/templates/table-page.template.mjs +5 -4
  120. package/templates/table-page.tsx +396 -405
  121. package/templates/table.template.mjs +5 -4
  122. package/templates/table.tsx +92 -93
  123. package/templates/tech-report.template.mjs +5 -4
  124. package/templates/tech-report.tsx +1316 -1304
  125. package/templates/theme-showcase.template.mjs +5 -4
  126. package/templates/theme-showcase.tsx +871 -951
  127. package/theme.css +35 -77
  128. package/theme.css.d.ts +9 -0
  129. package/tokens.css +373 -380
@@ -1,315 +1,280 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > V[a=center] > S.transparent[p=6] > V[g=6] > (V[g=2 a=center] > Hd"Let's brew after dark"[level=1] + Tx"Tell us a bit"[t=body]) + (V[g=4] > Hd"Why work with us"[level=2] + (G[c={min:200} g=4] > (C > V[g=3] > Ic + (V[g=1] > Hd"Title"[level=3] + Tx"Desc"[t=body]))*3)) + (V[g=4] > Hd"Your details"[level=2] + (G[c={min:260} g=4] > TI"Full name" + TI"Email") + (G[c={min:260} g=4] > TI"Company" + TI"Phone")) + D + (V[g=4] > Hd"Your project"[level=2] + (Fd"Goals"[req] > (H[g=2] > Tk"Goal"*10)) + SE"Timeline" + SE"Budget" + (RL"Source" > RLI*5) + TA"Notes" + CB"Decision maker") + (V[g=3] > B.primary"Send it into the night" + (Tx[t=supporting] > Lk"Privacy Policy"))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // Ctr > V[a=center] > S.transparent[p=6] > V[g=6] > (V[g=2 a=center] > Hd"Let's brew after dark"[level=1 t=display-2] + Tx"Tell us a bit"[t=body]) + (V[g=4] > Hd"Why work with us"[level=2] + (G[c={min:200} g=4] > (C > V[g=3] > Ic + (V[g=1] > Hd"Title"[level=3] + Tx"Desc"[t=body]))*3)) + (V[g=4] > Hd"Your details"[level=2] + (G[c={min:260} g=4] > TI"Full name"[req] + TI"Email"[req]) + (G[c={min:260} g=4] > TI"Company"[req] + TI"Phone"[req])) + D + (V[g=4] > Hd"Your project"[level=2] + (Fd"What are you going for?"[req] > (H[g=2] > Tk"Goal"*10)) + SE{Selector}"When are you thinking?" + SE{Selector}"Ballpark budget?" + (RL"How did you hear about us?" > RLI*5) + TA"Anything else?" + CB"I'm a budget decision-maker") + (V[g=3] > B.primary"Send it into the night" + (Tx[t=supporting] > Lk"Privacy Policy"))
4
6
 
5
- import {useState} from 'react';
6
- import {VStack, HStack} from '@astryxdesign/core/Layout';
7
- import {Center} from '@astryxdesign/core/Center';
8
- import {Section} from '@astryxdesign/core/Section';
9
- import {Grid} from '@astryxdesign/core/Grid';
10
- import {Card} from '@astryxdesign/core/Card';
11
- import {Button} from '@astryxdesign/core/Button';
12
- import {Text, Heading} from '@astryxdesign/core/Text';
13
- import {Icon} from '@astryxdesign/core/Icon';
14
- import {TextInput} from '@astryxdesign/core/TextInput';
15
- import {Selector} from '@astryxdesign/core/Selector';
16
- import {CheckboxInput} from '@astryxdesign/core/CheckboxInput';
17
- import {Link} from '@astryxdesign/core/Link';
18
- import {Token} from '@astryxdesign/core/Token';
19
- import {RadioList, RadioListItem} from '@astryxdesign/core/RadioList';
20
- import {TextArea} from '@astryxdesign/core/TextArea';
21
- import {Divider} from '@astryxdesign/core/Divider';
22
- import {Field} from '@astryxdesign/core/Field';
23
- import {Rocket, SlidersHorizontal, Hand, Check} from 'lucide-react';
7
+ import { Button } from "@astryxdesign/core/Button";
8
+ import { Card } from "@astryxdesign/core/Card";
9
+ import { Center } from "@astryxdesign/core/Center";
10
+ import { CheckboxInput } from "@astryxdesign/core/CheckboxInput";
11
+ import { Divider } from "@astryxdesign/core/Divider";
12
+ import { Field } from "@astryxdesign/core/Field";
13
+ import { Grid } from "@astryxdesign/core/Grid";
14
+ import { Icon } from "@astryxdesign/core/Icon";
15
+ import { HStack, VStack } from "@astryxdesign/core/Layout";
16
+ import { Link } from "@astryxdesign/core/Link";
17
+ import { RadioList, RadioListItem } from "@astryxdesign/core/RadioList";
18
+ import { Section } from "@astryxdesign/core/Section";
19
+ import { Selector } from "@astryxdesign/core/Selector";
20
+ import { Heading, Text } from "@astryxdesign/core/Text";
21
+ import { TextArea } from "@astryxdesign/core/TextArea";
22
+ import { TextInput } from "@astryxdesign/core/TextInput";
23
+ import { Token } from "@astryxdesign/core/Token";
24
+ import { Check, Hand, Rocket, SlidersHorizontal } from "lucide-react";
25
+ import { useState } from "react";
24
26
 
25
27
  const CAMPAIGN_GOALS = [
26
- 'Brand Awareness',
27
- 'Product Sampling',
28
- 'Product Launch',
29
- 'Event Promotion',
30
- 'Retail / In-Store',
31
- 'Trade Show',
32
- 'Influencer Activation',
33
- 'Community Building',
34
- 'Seasonal Campaign',
35
- 'Other',
28
+ "Brand Awareness",
29
+ "Product Sampling",
30
+ "Product Launch",
31
+ "Event Promotion",
32
+ "Retail / In-Store",
33
+ "Trade Show",
34
+ "Influencer Activation",
35
+ "Community Building",
36
+ "Seasonal Campaign",
37
+ "Other",
36
38
  ];
37
39
 
38
40
  const LAUNCH_OPTIONS = [
39
- 'Within 30 days',
40
- '1\u20133 months',
41
- '3\u20136 months',
42
- '6\u201312 months',
43
- '12+ months',
41
+ "Within 30 days",
42
+ "1\u20133 months",
43
+ "3\u20136 months",
44
+ "6\u201312 months",
45
+ "12+ months",
44
46
  ];
45
47
 
46
48
  const BUDGET_OPTIONS = [
47
- 'Under $5K/mo',
48
- '$5K\u2013$15K/mo',
49
- '$15K\u2013$50K/mo',
50
- '$50K\u2013$100K/mo',
51
- '$100K+/mo',
49
+ "Under $5K/mo",
50
+ "$5K\u2013$15K/mo",
51
+ "$15K\u2013$50K/mo",
52
+ "$50K\u2013$100K/mo",
53
+ "$100K+/mo",
52
54
  ];
53
55
 
54
56
  const WHY_US = [
55
- {
56
- icon: Rocket,
57
- title: 'We ship before sunrise',
58
- description: 'We cut through the fog and land the work before dawn.',
59
- },
60
- {
61
- icon: SlidersHorizontal,
62
- title: 'We tune around you',
63
- description: "Every dial set to what you're trying to summon.",
64
- },
65
- {
66
- icon: Hand,
67
- title: 'We answer after dark',
68
- description: 'A dedicated coven that knows your brand and haunts your roadmap.',
69
- },
57
+ {
58
+ icon: Rocket,
59
+ title: "We ship before sunrise",
60
+ description: "We cut through the fog and land the work before dawn.",
61
+ },
62
+ {
63
+ icon: SlidersHorizontal,
64
+ title: "We tune around you",
65
+ description: "Every dial set to what you're trying to summon.",
66
+ },
67
+ {
68
+ icon: Hand,
69
+ title: "We answer after dark",
70
+ description: "A dedicated coven that knows your brand and haunts your roadmap.",
71
+ },
70
72
  ];
71
73
 
72
74
  /**
73
75
  * Contact Form — lead capture form template
74
76
  */
75
77
  export default function ContactForm() {
76
- const [fullName, setFullName] = useState('');
77
- const [email, setEmail] = useState('');
78
- const [company, setCompany] = useState('');
79
- const [phone, setPhone] = useState('');
80
- const [goals, setGoals] = useState<string[]>([]);
81
- const [timeline, setTimeline] = useState('');
82
- const [budget, setBudget] = useState('');
83
- const [message, setMessage] = useState('');
84
- const [hearAboutUs, setHearAboutUs] = useState('');
85
- const [isDecider, setIsDecider] = useState(false);
86
- const [submitted, setSubmitted] = useState(false);
78
+ const [fullName, setFullName] = useState("");
79
+ const [email, setEmail] = useState("");
80
+ const [company, setCompany] = useState("");
81
+ const [phone, setPhone] = useState("");
82
+ const [goals, setGoals] = useState<string[]>([]);
83
+ const [timeline, setTimeline] = useState("");
84
+ const [budget, setBudget] = useState("");
85
+ const [message, setMessage] = useState("");
86
+ const [hearAboutUs, setHearAboutUs] = useState("");
87
+ const [isDecider, setIsDecider] = useState(false);
88
+ const [submitted, setSubmitted] = useState(false);
87
89
 
88
- const errors = submitted
89
- ? {
90
- fullName: !fullName.trim() ? 'Required' : undefined,
91
- email: !email.trim() ? 'Required' : undefined,
92
- company: !company.trim() ? 'Required' : undefined,
93
- phone: !phone.trim() ? 'Required' : undefined,
94
- goals: goals.length === 0 ? 'Pick at least one' : undefined,
95
- timeline: !timeline ? 'Required' : undefined,
96
- budget: !budget ? 'Required' : undefined,
97
- }
98
- : {};
90
+ const errors = submitted
91
+ ? {
92
+ fullName: !fullName.trim() ? "Required" : undefined,
93
+ email: !email.trim() ? "Required" : undefined,
94
+ company: !company.trim() ? "Required" : undefined,
95
+ phone: !phone.trim() ? "Required" : undefined,
96
+ goals: goals.length === 0 ? "Pick at least one" : undefined,
97
+ timeline: !timeline ? "Required" : undefined,
98
+ budget: !budget ? "Required" : undefined,
99
+ }
100
+ : {};
99
101
 
100
- const toggleGoal = (goal: string) =>
101
- setGoals(prev =>
102
- prev.includes(goal) ? prev.filter(g => g !== goal) : [...prev, goal],
103
- );
102
+ const toggleGoal = (goal: string) =>
103
+ setGoals((prev) => (prev.includes(goal) ? prev.filter((g) => g !== goal) : [...prev, goal]));
104
104
 
105
- return (
106
- <Center axis="horizontal">
107
- <VStack hAlign="center" width="100%">
108
- <Section maxWidth={800} width="100%" padding={6} variant="transparent">
109
- <VStack gap={6}>
110
- {/* Header */}
111
- <VStack gap={2} hAlign="center" style={{textAlign: 'center'}}>
112
- <Heading level={1} type="display-2">
113
- Let&apos;s brew after dark
114
- </Heading>
115
- <Text type="body" color="secondary">
116
- Tell us a bit about what you&apos;re summoning, and the coven
117
- would love to help it take flight.
118
- </Text>
119
- </VStack>
105
+ return (
106
+ <Center axis="horizontal">
107
+ <VStack hAlign="center" width="100%">
108
+ <Section maxWidth={800} width="100%" padding={6} variant="transparent">
109
+ <VStack gap={6}>
110
+ {/* Header */}
111
+ <VStack gap={2} hAlign="center" style={{ textAlign: "center" }}>
112
+ <Heading level={1} type="display-2">
113
+ Let&apos;s brew after dark
114
+ </Heading>
115
+ <Text type="body" color="secondary">
116
+ Tell us a bit about what you&apos;re summoning, and the coven would love to help it
117
+ take flight.
118
+ </Text>
119
+ </VStack>
120
120
 
121
- {/* Why work with us */}
122
- <VStack gap={4}>
123
- <Heading level={2}>Why work with us</Heading>
124
- <Grid columns={{minWidth: 200}} gap={4}>
125
- {WHY_US.map(item => (
126
- <Card key={item.title}>
127
- <VStack gap={3}>
128
- <Icon icon={item.icon} size="lg" color="secondary" />
129
- <VStack gap={1}>
130
- <Heading level={3}>{item.title}</Heading>
131
- <Text type="body" color="secondary">
132
- {item.description}
133
- </Text>
134
- </VStack>
135
- </VStack>
136
- </Card>
137
- ))}
138
- </Grid>
139
- </VStack>
121
+ {/* Why work with us */}
122
+ <VStack gap={4}>
123
+ <Heading level={2}>Why work with us</Heading>
124
+ <Grid columns={{ minWidth: 200 }} gap={4}>
125
+ {WHY_US.map((item) => (
126
+ <Card key={item.title}>
127
+ <VStack gap={3}>
128
+ <Icon icon={item.icon} size="lg" color="secondary" />
129
+ <VStack gap={1}>
130
+ <Heading level={3}>{item.title}</Heading>
131
+ <Text type="body" color="secondary">
132
+ {item.description}
133
+ </Text>
134
+ </VStack>
135
+ </VStack>
136
+ </Card>
137
+ ))}
138
+ </Grid>
139
+ </VStack>
140
140
 
141
- {/* Your details */}
142
- <VStack gap={4}>
143
- <Heading level={2}>Your details</Heading>
144
- <Grid columns={{minWidth: 260}} gap={4}>
145
- <TextInput
146
- label="Full name"
147
- isRequired
148
- placeholder="Full name"
149
- value={fullName}
150
- onChange={setFullName}
151
- status={
152
- errors.fullName
153
- ? {type: 'error', message: errors.fullName}
154
- : undefined
155
- }
156
- />
157
- <TextInput
158
- label="Email"
159
- isRequired
160
- placeholder="you@company.com"
161
- value={email}
162
- onChange={setEmail}
163
- status={
164
- errors.email
165
- ? {type: 'error', message: errors.email}
166
- : undefined
167
- }
168
- />
169
- </Grid>
170
- <Grid columns={{minWidth: 260}} gap={4}>
171
- <TextInput
172
- label="Company"
173
- isRequired
174
- placeholder="Company"
175
- value={company}
176
- onChange={setCompany}
177
- status={
178
- errors.company
179
- ? {type: 'error', message: errors.company}
180
- : undefined
181
- }
182
- />
183
- <TextInput
184
- label="Phone"
185
- isRequired
186
- placeholder="Phone number"
187
- value={phone}
188
- onChange={setPhone}
189
- status={
190
- errors.phone
191
- ? {type: 'error', message: errors.phone}
192
- : undefined
193
- }
194
- />
195
- </Grid>
196
- </VStack>
141
+ {/* Your details */}
142
+ <VStack gap={4}>
143
+ <Heading level={2}>Your details</Heading>
144
+ <Grid columns={{ minWidth: 260 }} gap={4}>
145
+ <TextInput
146
+ label="Full name"
147
+ isRequired
148
+ placeholder="Full name"
149
+ value={fullName}
150
+ onChange={setFullName}
151
+ status={errors.fullName ? { type: "error", message: errors.fullName } : undefined}
152
+ />
153
+ <TextInput
154
+ label="Email"
155
+ isRequired
156
+ placeholder="you@company.com"
157
+ value={email}
158
+ onChange={setEmail}
159
+ status={errors.email ? { type: "error", message: errors.email } : undefined}
160
+ />
161
+ </Grid>
162
+ <Grid columns={{ minWidth: 260 }} gap={4}>
163
+ <TextInput
164
+ label="Company"
165
+ isRequired
166
+ placeholder="Company"
167
+ value={company}
168
+ onChange={setCompany}
169
+ status={errors.company ? { type: "error", message: errors.company } : undefined}
170
+ />
171
+ <TextInput
172
+ label="Phone"
173
+ isRequired
174
+ placeholder="Phone number"
175
+ value={phone}
176
+ onChange={setPhone}
177
+ status={errors.phone ? { type: "error", message: errors.phone } : undefined}
178
+ />
179
+ </Grid>
180
+ </VStack>
197
181
 
198
- <Divider />
182
+ <Divider />
199
183
 
200
- {/* Your project */}
201
- <VStack gap={4}>
202
- <Heading level={2}>Your project</Heading>
203
- <Field
204
- label="What are you going for?"
205
- inputID="campaign-goals"
206
- labelID="campaign-goals-label"
207
- isGroupLabel
208
- isRequired
209
- status={
210
- errors.goals
211
- ? {type: 'error', message: errors.goals}
212
- : undefined
213
- }>
214
- <HStack
215
- gap={2}
216
- wrap="wrap"
217
- role="group"
218
- aria-labelledby="campaign-goals-label">
219
- {CAMPAIGN_GOALS.map(goal => {
220
- const isSelected = goals.includes(goal);
221
- return (
222
- <Token
223
- key={goal}
224
- label={goal}
225
- color={isSelected ? 'yellow' : 'default'}
226
- icon={
227
- isSelected ? (
228
- <Icon icon={Check} size="xsm" color="inherit" />
229
- ) : undefined
230
- }
231
- description={isSelected ? 'Selected' : undefined}
232
- onClick={() => toggleGoal(goal)}
233
- />
234
- );
235
- })}
236
- </HStack>
237
- </Field>
184
+ {/* Your project */}
185
+ <VStack gap={4}>
186
+ <Heading level={2}>Your project</Heading>
187
+ <Field
188
+ label="What are you going for?"
189
+ inputID="campaign-goals"
190
+ labelID="campaign-goals-label"
191
+ isGroupLabel
192
+ isRequired
193
+ status={errors.goals ? { type: "error", message: errors.goals } : undefined}
194
+ >
195
+ <HStack gap={2} wrap="wrap" role="group" aria-labelledby="campaign-goals-label">
196
+ {CAMPAIGN_GOALS.map((goal) => {
197
+ const isSelected = goals.includes(goal);
198
+ return (
199
+ <Token
200
+ key={goal}
201
+ label={goal}
202
+ color={isSelected ? "yellow" : "default"}
203
+ icon={
204
+ isSelected ? <Icon icon={Check} size="xsm" color="inherit" /> : undefined
205
+ }
206
+ description={isSelected ? "Selected" : undefined}
207
+ onClick={() => toggleGoal(goal)}
208
+ />
209
+ );
210
+ })}
211
+ </HStack>
212
+ </Field>
238
213
 
239
- <Selector
240
- label="When are you thinking?"
241
- isRequired
242
- placeholder="When are you thinking of launching?"
243
- options={LAUNCH_OPTIONS}
244
- value={timeline}
245
- onChange={setTimeline}
246
- status={
247
- errors.timeline
248
- ? {type: 'error', message: errors.timeline}
249
- : undefined
250
- }
251
- />
214
+ <Selector
215
+ label="When are you thinking?"
216
+ isRequired
217
+ placeholder="When are you thinking of launching?"
218
+ options={LAUNCH_OPTIONS}
219
+ value={timeline}
220
+ onChange={setTimeline}
221
+ status={errors.timeline ? { type: "error", message: errors.timeline } : undefined}
222
+ />
252
223
 
253
- <Selector
254
- label="Ballpark budget?"
255
- isRequired
256
- placeholder="What's your rough monthly budget?"
257
- options={BUDGET_OPTIONS}
258
- value={budget}
259
- onChange={setBudget}
260
- status={
261
- errors.budget
262
- ? {type: 'error', message: errors.budget}
263
- : undefined
264
- }
265
- />
224
+ <Selector
225
+ label="Ballpark budget?"
226
+ isRequired
227
+ placeholder="What's your rough monthly budget?"
228
+ options={BUDGET_OPTIONS}
229
+ value={budget}
230
+ onChange={setBudget}
231
+ status={errors.budget ? { type: "error", message: errors.budget } : undefined}
232
+ />
266
233
 
267
- <RadioList
268
- label="How did you hear about us?"
269
- value={hearAboutUs}
270
- onChange={setHearAboutUs}>
271
- <RadioListItem label="Social media" value="social" />
272
- <RadioListItem label="Word of mouth" value="word-of-mouth" />
273
- <RadioListItem label="Search engine" value="search" />
274
- <RadioListItem label="Event or conference" value="event" />
275
- <RadioListItem label="Other" value="other" />
276
- </RadioList>
234
+ <RadioList
235
+ label="How did you hear about us?"
236
+ value={hearAboutUs}
237
+ onChange={setHearAboutUs}
238
+ >
239
+ <RadioListItem label="Social media" value="social" />
240
+ <RadioListItem label="Word of mouth" value="word-of-mouth" />
241
+ <RadioListItem label="Search engine" value="search" />
242
+ <RadioListItem label="Event or conference" value="event" />
243
+ <RadioListItem label="Other" value="other" />
244
+ </RadioList>
277
245
 
278
- <TextArea
279
- label="Anything else?"
280
- placeholder="Tell us whatever else is on your mind…"
281
- value={message}
282
- onChange={setMessage}
283
- />
246
+ <TextArea
247
+ label="Anything else?"
248
+ placeholder="Tell us whatever else is on your mind…"
249
+ value={message}
250
+ onChange={setMessage}
251
+ />
284
252
 
285
- <CheckboxInput
286
- label="I'm a budget decision-maker"
287
- value={isDecider}
288
- onChange={setIsDecider}
289
- />
290
- </VStack>
253
+ <CheckboxInput
254
+ label="I'm a budget decision-maker"
255
+ value={isDecider}
256
+ onChange={setIsDecider}
257
+ />
258
+ </VStack>
291
259
 
292
- {/* Submit */}
293
- <VStack gap={3}>
294
- <Button
295
- label="Send it into the night"
296
- variant="primary"
297
- onClick={() => setSubmitted(true)}
298
- />
299
- <Text type="supporting" color="secondary">
300
- By submitting you agree to our{' '}
301
- <Link
302
- href="#/templates/contact-form"
303
- type="supporting"
304
- hasUnderline>
305
- Privacy Policy
306
- </Link>
307
- .
308
- </Text>
309
- </VStack>
310
- </VStack>
311
- </Section>
312
- </VStack>
313
- </Center>
314
- );
260
+ {/* Submit */}
261
+ <VStack gap={3}>
262
+ <Button
263
+ label="Send it into the night"
264
+ variant="primary"
265
+ onClick={() => setSubmitted(true)}
266
+ />
267
+ <Text type="supporting" color="secondary">
268
+ By submitting you agree to our{" "}
269
+ <Link href="#/templates/contact-form" type="supporting" hasUnderline>
270
+ Privacy Policy
271
+ </Link>
272
+ .
273
+ </Text>
274
+ </VStack>
275
+ </VStack>
276
+ </Section>
277
+ </VStack>
278
+ </Center>
279
+ );
315
280
  }
@@ -1,6 +1,7 @@
1
1
  export default {
2
- type: 'page',
3
- name: 'Portfolio Dashboard',
4
- description: 'Holdings chart, market cards, and trending table',
5
- category: 'dashboard',
2
+ type: "page",
3
+ name: "Portfolio Dashboard",
4
+ description: "Holdings chart, market cards, and trending table",
5
+ keywords: ["portfolio", "holdings", "wealth", "investment", "positions", "dashboard"],
6
+ category: "dashboard",
6
7
  };