@streamoid/ui 0.6.17 → 0.6.19
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/README.md +35 -18
- package/dist/docs/AGENTS.md +325 -0
- package/dist/docs/CreditWarningBanner.md +305 -0
- package/dist/docs/InvoiceHistoryMobile.md +222 -0
- package/dist/docs/ScAccess.md +259 -0
- package/dist/docs/ScAppCard.md +244 -0
- package/dist/docs/ScAppCardForCopilot.md +230 -0
- package/dist/docs/ScAppCardV3.md +273 -0
- package/dist/docs/ScAppField.md +308 -0
- package/dist/docs/ScAppListingCard.md +271 -0
- package/dist/docs/ScAppSwitchPanel.md +286 -0
- package/dist/docs/ScAppcardLogos.md +226 -0
- package/dist/docs/ScArtifaxInvite.md +262 -0
- package/dist/docs/ScArtifaxSidebar.md +330 -0
- package/dist/docs/ScAskAgentButton.md +307 -0
- package/dist/docs/ScBadges.md +261 -0
- package/dist/docs/ScBeacon.md +244 -0
- package/dist/docs/ScBillingHistoryHeader.md +210 -0
- package/dist/docs/ScBillingHistoryTableList.md +243 -0
- package/dist/docs/ScBillingLogsTableHeader.md +212 -0
- package/dist/docs/ScBillingLogsTableList.md +251 -0
- package/dist/docs/ScBriefCard.md +255 -0
- package/dist/docs/ScButton.md +251 -0
- package/dist/docs/ScCalendar.md +268 -0
- package/dist/docs/ScCalendarDateComps.md +264 -0
- package/dist/docs/ScCatalogixInvite.md +345 -0
- package/dist/docs/ScCatalogixSidebar.md +337 -0
- package/dist/docs/ScCatalogixStoreHeader.md +246 -0
- package/dist/docs/ScCatalogixStoreTableList.md +316 -0
- package/dist/docs/ScCheckField.md +233 -0
- package/dist/docs/ScCheckbox.md +272 -0
- package/dist/docs/ScCounter.md +235 -0
- package/dist/docs/ScCreditsUsageCard.md +247 -0
- package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
- package/dist/docs/ScDefaultCard.md +269 -0
- package/dist/docs/ScDp.md +245 -0
- package/dist/docs/ScDrawer.md +318 -0
- package/dist/docs/ScFieldButton.md +255 -0
- package/dist/docs/ScFileField.md +268 -0
- package/dist/docs/ScGoogleSignIn.md +250 -0
- package/dist/docs/ScGuide.md +278 -0
- package/dist/docs/ScHDivider.md +210 -0
- package/dist/docs/ScHeader.md +222 -0
- package/dist/docs/ScImageField.md +253 -0
- package/dist/docs/ScInChatList.md +277 -0
- package/dist/docs/ScInChatMessage.md +205 -0
- package/dist/docs/ScInfoPopup.md +248 -0
- package/dist/docs/ScIntialProfileCover.md +233 -0
- package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
- package/dist/docs/ScLogoUnit.md +232 -0
- package/dist/docs/ScMappingCard.md +241 -0
- package/dist/docs/ScMediaApproval.md +301 -0
- package/dist/docs/ScMediaSelect.md +310 -0
- package/dist/docs/ScMenuOptions.md +308 -0
- package/dist/docs/ScMobileBottomAction.md +252 -0
- package/dist/docs/ScMobileTopNav.md +279 -0
- package/dist/docs/ScModal.md +291 -0
- package/dist/docs/ScOnlyField.md +302 -0
- package/dist/docs/ScOnlyIcon.md +213 -0
- package/dist/docs/ScPagination.md +284 -0
- package/dist/docs/ScPairtext.md +287 -0
- package/dist/docs/ScPendingAction.md +238 -0
- package/dist/docs/ScPhtogenixInvite.md +275 -0
- package/dist/docs/ScPlanCard.md +302 -0
- package/dist/docs/ScPlanComparison.md +264 -0
- package/dist/docs/ScPlanDetailsCard.md +246 -0
- package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
- package/dist/docs/ScPopUpMenu.md +224 -0
- package/dist/docs/ScProfile.md +234 -0
- package/dist/docs/ScProfileImageUpdate.md +261 -0
- package/dist/docs/ScProfileOptions.md +245 -0
- package/dist/docs/ScProfilePopup.md +396 -0
- package/dist/docs/ScProfileSettingsComp.md +250 -0
- package/dist/docs/ScProfileV2Mobile.md +216 -0
- package/dist/docs/ScProgressBar.md +267 -0
- package/dist/docs/ScQuickPrompt.md +277 -0
- package/dist/docs/ScRadio.md +228 -0
- package/dist/docs/ScReferralCardMobile.md +226 -0
- package/dist/docs/ScReferralTableHeader.md +260 -0
- package/dist/docs/ScReferralTableList.md +293 -0
- package/dist/docs/ScRole.md +226 -0
- package/dist/docs/ScRoleMobile.md +199 -0
- package/dist/docs/ScSelect.md +270 -0
- package/dist/docs/ScSelection.md +256 -0
- package/dist/docs/ScSelectionList.md +272 -0
- package/dist/docs/ScSelectionPill.md +240 -0
- package/dist/docs/ScSelectionPillGroup.md +302 -0
- package/dist/docs/ScSettingsNav.md +212 -0
- package/dist/docs/ScSettingsTabComp.md +260 -0
- package/dist/docs/ScSideBarLogoUnit.md +340 -0
- package/dist/docs/ScSidebar.md +243 -0
- package/dist/docs/ScSidebarIcons.md +232 -0
- package/dist/docs/ScSidebarMenu.md +283 -0
- package/dist/docs/ScSidebarProfile.md +231 -0
- package/dist/docs/ScSidebarSwitchMenu.md +258 -0
- package/dist/docs/ScSlider.md +194 -0
- package/dist/docs/ScStoreCard.md +252 -0
- package/dist/docs/ScStrLogo.md +253 -0
- package/dist/docs/ScStreamoidWordmark.md +302 -0
- package/dist/docs/ScSubAgent.md +226 -0
- package/dist/docs/ScTabComp.md +308 -0
- package/dist/docs/ScTabField.md +258 -0
- package/dist/docs/ScTabSwitcher.md +307 -0
- package/dist/docs/ScTableHeader.md +261 -0
- package/dist/docs/ScTableList.md +301 -0
- package/dist/docs/ScTableListMobile.md +282 -0
- package/dist/docs/ScTabs.md +268 -0
- package/dist/docs/ScTaxonomyPill.md +263 -0
- package/dist/docs/ScTextArea.md +259 -0
- package/dist/docs/ScTextField.md +324 -0
- package/dist/docs/ScThinkingStepIcon.md +249 -0
- package/dist/docs/ScTodoList.md +288 -0
- package/dist/docs/ScToggleSwitch.md +229 -0
- package/dist/docs/ScUsageHistoryMobile.md +194 -0
- package/dist/docs/ScVDivider.md +215 -0
- package/dist/docs/ScValueMappingL1.md +256 -0
- package/dist/docs/ScVersion.md +251 -0
- package/dist/docs/ScWorkspace.md +233 -0
- package/dist/docs/ScWorkspaceAccountMenu.md +115 -0
- package/dist/docs/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +312 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +413 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4931 -0
- package/dist/index.css +361 -36
- package/dist/index.d.mts +213 -88
- package/dist/index.d.ts +213 -88
- package/dist/index.js +2486 -1629
- package/dist/index.mjs +2487 -1620
- package/package.json +5 -3
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScQuickPrompt
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: chat-agent
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [chat, prompt, suggestion, starter, empty-state, copilot, card, bolt]
|
|
8
|
+
related: [ScAppCardForCopilot, ScBriefCard, ScDefaultCard, ScAskAgentButton, ScInChatMessage]
|
|
9
|
+
do_not_confuse_with: [ScBriefCard, ScDefaultCard, ScAppCardForCopilot, ScAppListingCard]
|
|
10
|
+
used_by: [cxo, photogenix]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScQuickPrompt
|
|
14
|
+
|
|
15
|
+
**A suggested-prompt card for an empty chat.** A bolt tile and a pill with the target
|
|
16
|
+
app's name across the top, then a medium heading and a muted description — the thing
|
|
17
|
+
a user clicks to seed the conversation. Bordered card on the canvas surface.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** you're building the chat/copilot empty state and want a row
|
|
22
|
+
or grid of 2–4 clickable starter prompts.
|
|
23
|
+
- **Don't reach for it when:** you want an app tile in the copilot app switcher
|
|
24
|
+
(→ `ScAppCardForCopilot`), a saved-brief card (→ `ScBriefCard`), a generic titled
|
|
25
|
+
option card (→ `ScDefaultCard`), or the gradient "Ask CXO" CTA
|
|
26
|
+
(→ `ScAskAgentButton`).
|
|
27
|
+
- **Three things that will bite you:**
|
|
28
|
+
1. It has a **hardcoded `width: 15.333125rem`** (245px). In a flex row or grid you
|
|
29
|
+
must override it (`!w-auto min-w-0 flex-1`) or every card stays 245px wide.
|
|
30
|
+
2. Every prop has a **real Photogenix default** — omit them and you ship
|
|
31
|
+
"Photogenix / Swap Model and Background / Replace the current model and…".
|
|
32
|
+
3. It is **not a button**: a plain `div`, no `cursor: pointer`, no `role`, no
|
|
33
|
+
`tabIndex`, no keyboard. `onClick` only works because `HTMLAttributes` are spread.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. How to use it
|
|
38
|
+
|
|
39
|
+
### Import
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { ScQuickPrompt } from "@streamoid/ui";
|
|
43
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Minimal usage
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<ScQuickPrompt
|
|
50
|
+
appName="Photogenix"
|
|
51
|
+
heading="Swap Background"
|
|
52
|
+
description="Change the background of this image to a minimalist studio"
|
|
53
|
+
onClick={() => sendPrompt("Change the background of this image to a minimalist studio")}
|
|
54
|
+
style={{ cursor: "pointer" }}
|
|
55
|
+
/>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Notes |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `appName` | `string` | `"Photogenix"` | ⚠️ Real default. Rendered in the top-right outlined pill, `text-tertiary` 12/18. A trailing space is appended by the component. |
|
|
63
|
+
| `heading` | `string` | `"Swap Model and Background"` | ⚠️ Real default. `text-secondary`, 16/24 medium. Wraps freely — no truncation. |
|
|
64
|
+
| `description` | `string` | `"Replace the current model and background with new ones that match my brand's aesthetic and tone."` | ⚠️ Real default. `text-muted`, 14/20. No line clamp — long strings make the card taller. |
|
|
65
|
+
| `className` | `string` | – | Appended after the internal class. **Always concatenated**, so omitting it puts a literal `undefined` class on the root (harmless, but visible in the DOM). |
|
|
66
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | `onClick`, `style`, `role`, `tabIndex`, `onKeyDown`, `data-*`, `aria-*` spread onto the root. **This is the only way to make it interactive.** |
|
|
67
|
+
|
|
68
|
+
There is **no `icon` prop** — the tile always renders `SiconBolt`. There is no
|
|
69
|
+
`prompt`/`value` prop either: the text you send is your own concern, and is usually
|
|
70
|
+
not the same string as `description`.
|
|
71
|
+
|
|
72
|
+
### Recipes
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
// A responsive grid of starters (Photogenix's copilot empty state)
|
|
76
|
+
const suggestions = [
|
|
77
|
+
{ heading: "Swap Background", description: "Change the background of this image to a minimalist studio", prompt: "Change the background of this image to a minimalist studio" },
|
|
78
|
+
{ heading: "Generate Video", description: "Create a cinematic product video with pan-up motion", prompt: "Create a cinematic product video with pan-up motion" },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 w-full">
|
|
82
|
+
{suggestions.map((item, i) => (
|
|
83
|
+
<ScQuickPrompt
|
|
84
|
+
key={i}
|
|
85
|
+
appName="Photogenix"
|
|
86
|
+
heading={item.heading}
|
|
87
|
+
description={item.description}
|
|
88
|
+
onClick={() => onSelect(item.prompt)}
|
|
89
|
+
style={{ cursor: "pointer" }}
|
|
90
|
+
/>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
// An equal-width flex row — the fixed 245px width MUST be overridden
|
|
95
|
+
<div style={{ display: "flex", gap: "var(--spacing-8xl)", width: "100%" }}>
|
|
96
|
+
{prompts.map((p) => (
|
|
97
|
+
<ScQuickPrompt key={p.id} {...p} className="flex-1 !w-auto min-w-0" />
|
|
98
|
+
))}
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
// Keyboard-operable (the component gives you none of this)
|
|
102
|
+
<ScQuickPrompt
|
|
103
|
+
appName="Catalogix"
|
|
104
|
+
heading="Map my feed"
|
|
105
|
+
description="Map the columns of my latest feed to the taxonomy"
|
|
106
|
+
role="button"
|
|
107
|
+
tabIndex={0}
|
|
108
|
+
aria-label="Quick prompt: Map my feed"
|
|
109
|
+
style={{ cursor: "pointer" }}
|
|
110
|
+
onClick={send}
|
|
111
|
+
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); send(); } }}
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 2. Where to use it
|
|
118
|
+
|
|
119
|
+
- **The chat/copilot empty state** — a row or 2×2 grid of starters shown before the
|
|
120
|
+
first turn. Photogenix renders exactly this in
|
|
121
|
+
`dashboard/client/src/components/copilot/CopilotSuggestions.tsx`.
|
|
122
|
+
- **The CXO landing/home hero**, as a row of three cards under the composer — the one
|
|
123
|
+
member of the chat family that genuinely lives in a host dashboard.
|
|
124
|
+
- Any **"what can this app do?"** affordance where the `appName` pill is meaningful
|
|
125
|
+
because the prompt is routed to a specific product.
|
|
126
|
+
|
|
127
|
+
Unlike the rest of the `chat-agent` family, this one is host-facing: CXO and
|
|
128
|
+
Photogenix both render it directly.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 3. When to use it
|
|
133
|
+
|
|
134
|
+
### Use it when
|
|
135
|
+
|
|
136
|
+
- The card's job is to **send a prompt**, and naming the destination app adds
|
|
137
|
+
information.
|
|
138
|
+
- You want the heading/description type scale and the bordered canvas card that
|
|
139
|
+
matches the composer above it.
|
|
140
|
+
|
|
141
|
+
### Don't use it — reach for this instead
|
|
142
|
+
|
|
143
|
+
| Situation | Use instead |
|
|
144
|
+
|---|---|
|
|
145
|
+
| A tile that **launches an app** from the copilot switcher | `ScAppCardForCopilot` |
|
|
146
|
+
| A tile in the app listing / marketing grid | `ScAppListingCard` / `ScAppCard` / `ScAppCardV3` |
|
|
147
|
+
| A saved brief or previous conversation | `ScBriefCard` |
|
|
148
|
+
| A generic pick-one option card (no app attribution) | `ScDefaultCard` |
|
|
149
|
+
| The gradient "Ask CXO" assistant CTA in a sidebar | `ScAskAgentButton` / `ScAskAgentSlot` |
|
|
150
|
+
| A short filter/segment choice | `ScSelectionPill` |
|
|
151
|
+
| An actual message in the transcript | `ScInChatMessage` |
|
|
152
|
+
|
|
153
|
+
### Don't confuse with
|
|
154
|
+
|
|
155
|
+
| You may actually want | Not this |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `ScBriefCard` — a *saved* brief with its own metadata | This is a *suggested* prompt with no persistence |
|
|
158
|
+
| `ScAppCardForCopilot` — app identity + logo, launches the app | This shows a bolt + app *name pill*, and sends text |
|
|
159
|
+
| `ScDefaultCard` — title/description card with no app pill and no fixed width | This one is 245px wide and app-attributed |
|
|
160
|
+
| A component that owns the prompt string | It renders three strings; the prompt you send is separate |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 4. Why to use it
|
|
165
|
+
|
|
166
|
+
- **The three-level type ramp is pre-set** (12/18 tertiary app pill → 16/24 medium
|
|
167
|
+
secondary heading → 14/20 muted description). Getting that hierarchy right by hand
|
|
168
|
+
is the difference between a starter card that scans and one that reads as a wall.
|
|
169
|
+
- **Canvas-on-canvas correctness.** The card sits on `--alias-surface-canvas` with a
|
|
170
|
+
`--alias-border-divider` hairline, so it stays distinguishable from the page in
|
|
171
|
+
light mode instead of turning into a white box on white.
|
|
172
|
+
- **Consistent across two products.** CXO home and the Photogenix copilot render the
|
|
173
|
+
same component, so a change to the starter-card shape lands in both.
|
|
174
|
+
- **The bolt tile is the product's "quick action" mark**, already sized and boxed at
|
|
175
|
+
8px padding on `--alias-fill-neutral-neutral`.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Gotchas
|
|
180
|
+
|
|
181
|
+
**1. The width is hardcoded to 245px.** `width: 15.333125rem` on the root.
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
// WRONG — three 245px cards with a gap, not a full-width row
|
|
185
|
+
<div className="flex gap-4 w-full">
|
|
186
|
+
{prompts.map((p) => <ScQuickPrompt key={p.id} {...p} />)}
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
// RIGHT — override the width and allow shrinking
|
|
190
|
+
<div className="flex gap-4 w-full">
|
|
191
|
+
{prompts.map((p) => <ScQuickPrompt key={p.id} {...p} className="flex-1 !w-auto min-w-0" />)}
|
|
192
|
+
</div>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Note the `!` — `className` is appended to the component's own class, but both are single
|
|
196
|
+
class selectors, so specificity ties and the winner is whichever rule comes later in the
|
|
197
|
+
stylesheet. `@streamoid/ui/dist/index.css` is typically imported before your utility CSS,
|
|
198
|
+
but don't rely on that: use `!w-auto` (or `style={{ width: "auto" }}`) so the override is
|
|
199
|
+
unambiguous.
|
|
200
|
+
|
|
201
|
+
**2. It is not interactive.** No `cursor: pointer`, no `role="button"`, no `tabIndex`,
|
|
202
|
+
no key handling. A card that looks clickable but isn't is the most common bug here.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
// WRONG — clickable to a mouse, invisible to a keyboard, no affordance
|
|
206
|
+
<ScQuickPrompt heading="Map my feed" description="…" onClick={send} />
|
|
207
|
+
|
|
208
|
+
// RIGHT
|
|
209
|
+
<ScQuickPrompt
|
|
210
|
+
heading="Map my feed" description="…"
|
|
211
|
+
role="button" tabIndex={0} style={{ cursor: "pointer" }}
|
|
212
|
+
onClick={send}
|
|
213
|
+
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); send(); } }}
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**3. All three text props default to Photogenix demo copy.** Omitting `appName` on a
|
|
218
|
+
Catalogix surface ships the word "Photogenix".
|
|
219
|
+
|
|
220
|
+
**4. `description` has no clamp.** Cards in a row will have different heights if the
|
|
221
|
+
descriptions differ in length; the row does not equalise them. Cap the string, or set
|
|
222
|
+
`-webkit-line-clamp` via `className`.
|
|
223
|
+
|
|
224
|
+
**5. There is no icon prop.** `SiconBolt` is hardcoded. If a spec asks for a
|
|
225
|
+
per-prompt icon, this component cannot do it — extend the DS rather than overlaying
|
|
226
|
+
one.
|
|
227
|
+
|
|
228
|
+
**6. Omitting `className` puts a literal `undefined` class on the root.** The class
|
|
229
|
+
list is built with plain string concatenation (`styles.scQuickPrompt + " " + className`),
|
|
230
|
+
unlike most components which filter falsy values. Cosmetic, but it will show up in
|
|
231
|
+
snapshot tests — pass `className=""` if a test diff matters.
|
|
232
|
+
|
|
233
|
+
**7. `appName`, `heading` and `description` each render with a trailing space** in the
|
|
234
|
+
JSX (`{appName} `). Harmless for layout, but `toHaveTextContent("Photogenix")` style
|
|
235
|
+
assertions on exact strings need trimming.
|
|
236
|
+
|
|
237
|
+
**8. The card has no selected/active state.** If a starter should stay highlighted
|
|
238
|
+
after being clicked, you need your own `className` skin.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## In the wild
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
// cxo-dashboard src/app/components/landing-content.tsx:380
|
|
246
|
+
<ScQuickPrompt
|
|
247
|
+
appName="Photogenix"
|
|
248
|
+
heading="Swap Model and Background"
|
|
249
|
+
description="Replace the current model and background with new ones that match my brand's aesthetic and tone."
|
|
250
|
+
className="flex-1 !w-auto min-w-0"
|
|
251
|
+
style={{
|
|
252
|
+
color: "var(--alias-text---icons-primary)",
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
// photogenix_v2 dashboard/client/src/components/copilot/CopilotSuggestions.tsx:24
|
|
259
|
+
<ScQuickPrompt
|
|
260
|
+
key={i}
|
|
261
|
+
appName="Photogenix"
|
|
262
|
+
heading={item.heading}
|
|
263
|
+
description={item.description}
|
|
264
|
+
onClick={() => onSelect(item.prompt)}
|
|
265
|
+
style={{ cursor: 'pointer' }}
|
|
266
|
+
/>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Related
|
|
272
|
+
|
|
273
|
+
- `ScAppCardForCopilot` — the app-launcher tile in the copilot switcher.
|
|
274
|
+
- `ScBriefCard` — a saved brief/conversation card in the chat surface.
|
|
275
|
+
- `ScDefaultCard` — generic titled option card, no app attribution, no fixed width.
|
|
276
|
+
- `ScAskAgentButton` — the gradient assistant CTA that opens the chat in the first place.
|
|
277
|
+
- `ScInChatMessage` — what the transcript renders once a starter has been sent.
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScRadio
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: forms
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [radio, radio-button, single-select, option, choice, presentational]
|
|
8
|
+
related: [ScPairtext, ScCheckbox, ScTabField, ScSelectionPillGroup, ScSelection]
|
|
9
|
+
do_not_confuse_with: [ScCheckbox, ScPairtext, ScSelection, ScSelectionPill, ScTabField]
|
|
10
|
+
used_by: [catalogix]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScRadio
|
|
14
|
+
|
|
15
|
+
**The bare radio glyph — a 24×24 ring, and a dot when selected.** It is
|
|
16
|
+
**purely presentational**: there is no `checked`, no `onChange`, no `value`, no
|
|
17
|
+
`name`, and no group component. You pass `state` and wire the click yourself.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** you are building a single-choice option list and laying out
|
|
22
|
+
the rows (label, description, disabled styling) yourself.
|
|
23
|
+
- **Don't reach for it when:** you want a labelled row for free (→ `ScPairtext`
|
|
24
|
+
with `type="radio"`), a segmented single-choice *field* (→ `ScTabField`), pills
|
|
25
|
+
(→ `ScSelectionPillGroup`), or the in-chat option list (→ `ScSelection`, agent
|
|
26
|
+
runtime).
|
|
27
|
+
- **Three things that will bite you:**
|
|
28
|
+
1. **There is no `onChange` and no `checked`.** Only `state`. Put your `onClick`
|
|
29
|
+
on the radio or (better) on the whole row.
|
|
30
|
+
2. **No group.** There is no `ScRadioGroup` — mutual exclusion is entirely yours.
|
|
31
|
+
3. **No `disabled` prop.** Gate your own handler and dim it with your own class.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
### Import
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ScRadio } from "@streamoid/ui";
|
|
41
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Minimal usage
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<ScRadio
|
|
48
|
+
state={mode === "one-time" ? "selected" : "default"}
|
|
49
|
+
onClick={() => setMode("one-time")}
|
|
50
|
+
/>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Props
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Notes |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `state` | `"default"` \| `"selected"` | `"default"` | The **only** state axis. `selected` adds the centre dot and brightens the ring. |
|
|
58
|
+
| `className` | `string` | – | Concatenated between the base and state classes. Also how you resize it (see Gotcha 6). |
|
|
59
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | `onClick`, `style`, `id`, `role`, `aria-*`, `data-*` spread onto the root div. **`onClick` comes from here — it is not a declared prop.** |
|
|
60
|
+
|
|
61
|
+
### What renders in each state
|
|
62
|
+
|
|
63
|
+
| `state` | Ring | Dot | Tokens |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| `default` | muted 1.5px ring | — | `--alias-text-and-icons-muted` |
|
|
66
|
+
| `selected` | bright ring | 6px radius filled circle | `--alias-text-and-icons-primary` (both) |
|
|
67
|
+
|
|
68
|
+
### Recipes
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
// The Catalogix idiom: click target is the whole row, radio is decoration
|
|
72
|
+
{OPTIONS.map((o) => (
|
|
73
|
+
<div
|
|
74
|
+
key={o.value}
|
|
75
|
+
style={{ display: "flex", gap: 8, alignItems: "center", cursor: "pointer" }}
|
|
76
|
+
onClick={() => setValue(o.value)}
|
|
77
|
+
>
|
|
78
|
+
<ScRadio state={value === o.value ? "selected" : "default"} />
|
|
79
|
+
<span>{o.label}</span>
|
|
80
|
+
</div>
|
|
81
|
+
))}
|
|
82
|
+
|
|
83
|
+
// "Disabled" option — gate the handler, dim it yourself
|
|
84
|
+
<div className={cx(styles.option, isLocked && styles.disabledOption)}>
|
|
85
|
+
<ScRadio
|
|
86
|
+
state={value === "one-time" ? "selected" : "default"}
|
|
87
|
+
onClick={() => !isLocked && setValue("one-time")}
|
|
88
|
+
/>
|
|
89
|
+
<span>One Time</span>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
// Want the label for free instead? Go one level up.
|
|
93
|
+
<ScPairtext type="radio" content="One Time" checked={value === "one-time"}
|
|
94
|
+
onChange={() => setValue("one-time")} />
|
|
95
|
+
|
|
96
|
+
// Dense contexts: size it from the outside — the SVG fills the box
|
|
97
|
+
<ScRadio state="selected" style={{ width: 16, height: 16 }} />
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 2. Where to use it
|
|
103
|
+
|
|
104
|
+
- **Catalogix option lists** — `AddNewSyncRule` (One Time / Recurring),
|
|
105
|
+
`Assets/AddAssets` (upload-mode options), `FeedMapping/SelectHeaderRow` (pick which
|
|
106
|
+
preview row is the header). Seven call sites, all the same pattern: a row div owns
|
|
107
|
+
the click, the radio just reflects state.
|
|
108
|
+
- **Inside the DS** — `ScPairtext` (`type="radio"`) and `ScSelection` (the in-chat
|
|
109
|
+
option row, **agent runtime**) both render one.
|
|
110
|
+
- **Table cells** where one row must be picked (`SelectHeaderRow`).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 3. When to use it
|
|
115
|
+
|
|
116
|
+
### Use it when
|
|
117
|
+
|
|
118
|
+
- The choices are **mutually exclusive** and shown as a vertical list with labels or
|
|
119
|
+
descriptions you control.
|
|
120
|
+
- You already own the selection state and just need the correct glyph.
|
|
121
|
+
|
|
122
|
+
### Don't use it — reach for this instead
|
|
123
|
+
|
|
124
|
+
| Situation | Use instead |
|
|
125
|
+
|---|---|
|
|
126
|
+
| Ring **plus** a text label as one click target | `ScPairtext` (`type="radio"`) |
|
|
127
|
+
| A labelled single-choice **field** in a form stack | `ScTabField` (caption + segmented switcher) |
|
|
128
|
+
| 2–5 mutually exclusive views shown as pills | `ScSelectionPillGroup` |
|
|
129
|
+
| A page/panel tab bar | `ScTabs` / `ScTabSwitcher` + `ScTabComp` |
|
|
130
|
+
| Multi-select (more than one active) | `ScCheckbox` / `ScCheckField` |
|
|
131
|
+
| An option row in the **agent chat transcript** | `ScSelection` / `ScSelectionList` (chat runtime) |
|
|
132
|
+
| A real form radio that posts with a `<form>` | native `<input type="radio" name=…>` — this div has no name/value |
|
|
133
|
+
|
|
134
|
+
### Don't confuse with
|
|
135
|
+
|
|
136
|
+
| You may actually want | Not this |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `ScCheckbox` — has `checked` + `onChange` + a `partial` state | `ScRadio` has **none** of those; it is `state`-only |
|
|
139
|
+
| `ScSelection` — the in-chat radio *row* with title + description (**agent runtime**) | `ScRadio` is a host-app glyph |
|
|
140
|
+
| `ScSelectionPill` — segmented tab pill, single-select but a completely different shape | Not a radio |
|
|
141
|
+
| `ScPairtext` — the labelled row that wraps this | This is only the ring |
|
|
142
|
+
|
|
143
|
+
Note the asymmetry with its sibling: `ScCheckbox` grew `checked`/`onChange`/`disabled`
|
|
144
|
+
props, `ScRadio` did not. Don't assume they share an API.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 4. Why to use it
|
|
149
|
+
|
|
150
|
+
- **Theme-correct ring and dot.** The two states are one SVG path plus a circle,
|
|
151
|
+
filled from `--alias-text-and-icons-muted` / `-primary`, so the unselected ring
|
|
152
|
+
stays visible against both the dark and light surfaces. Hand-drawn `border-radius`
|
|
153
|
+
radios routinely disappear in light mode.
|
|
154
|
+
- **It scales cleanly.** The CSS forces the SVG to `100%/100%` with `aspect-ratio: 1`,
|
|
155
|
+
so overriding width/height shrinks the ring *and* the dot proportionally — the
|
|
156
|
+
literal `width="24"` attributes on the SVG would otherwise spill.
|
|
157
|
+
- **Zero API surface to get wrong.** One prop. The selection logic lives in your
|
|
158
|
+
reducer where it belongs, and no two call sites can disagree about the glyph.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Gotchas
|
|
163
|
+
|
|
164
|
+
**1. No `onChange`, no `checked`.** They exist on `ScCheckbox`, not here. `checked`
|
|
165
|
+
is a type error — but **`onChange` compiles and silently never fires**, because
|
|
166
|
+
`HTMLAttributes` declares `onChange` for every div and nothing in `ScRadio` ever
|
|
167
|
+
calls it. Use `state` + `onClick`.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// WRONG — compiles, renders, and does nothing on click
|
|
171
|
+
<ScRadio state="default" onChange={() => setValue("a")} />
|
|
172
|
+
|
|
173
|
+
// RIGHT
|
|
174
|
+
<ScRadio state={value === "a" ? "selected" : "default"} onClick={() => setValue("a")} />
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**2. It is not an `<input type="radio">`.** No `name`, no `value`, no form
|
|
178
|
+
participation, no `role="radio"`, no `aria-checked`, no `tabIndex`, no arrow-key
|
|
179
|
+
roving between options. If the surface needs a11y, add it:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<div role="radiogroup" aria-label="Schedule">
|
|
183
|
+
<ScRadio role="radio" aria-checked={v === "one-time"} tabIndex={0}
|
|
184
|
+
state={v === "one-time" ? "selected" : "default"}
|
|
185
|
+
onClick={() => setV("one-time")} />
|
|
186
|
+
</div>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**3. `cursor: pointer` is always on** — even with no `onClick` and even when your row
|
|
190
|
+
treats the option as locked. The CSS sets it unconditionally, so a "disabled" option
|
|
191
|
+
still looks clickable unless you override the cursor yourself.
|
|
192
|
+
|
|
193
|
+
**4. No `disabled`.** Guard inside your handler *and* dim with your own class; that's
|
|
194
|
+
what `AddNewSyncRule` does (`!isOneTimeDisabled && …` plus a `disabled-option` class).
|
|
195
|
+
|
|
196
|
+
**5. Click the label? Nothing happens.** Same trap as `ScCheckbox`: no inner input,
|
|
197
|
+
so `<label>` gives you no free hit area. Put `onClick` on the row wrapper.
|
|
198
|
+
|
|
199
|
+
**6. Resize from the outside only.** There is no `size` prop (unlike `ScCheckbox`).
|
|
200
|
+
Use `style={{ width, height }}` or a `className` — the SVG will follow.
|
|
201
|
+
|
|
202
|
+
**7. `className` is string-concatenated.** With `className` unset the DOM class list
|
|
203
|
+
contains a literal `undefined`. Don't assert on the class string in snapshots.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## In the wild
|
|
208
|
+
|
|
209
|
+
```jsx
|
|
210
|
+
// catalogix/dashboard app/components/AddNewSyncRule/index.jsx:403
|
|
211
|
+
<ScRadio
|
|
212
|
+
state={syncData.scheduleType === "one-time" ? "selected" : "default"}
|
|
213
|
+
onClick={() =>
|
|
214
|
+
!isOneTimeDisabled &&
|
|
215
|
+
setSyncData({ ...syncData, scheduleType: "one-time" })
|
|
216
|
+
}
|
|
217
|
+
/>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Related
|
|
223
|
+
|
|
224
|
+
- `ScPairtext` — label + radio (or checkbox, or icon) as one clickable row.
|
|
225
|
+
- `ScCheckbox` — the multi-select sibling; note its richer API (`checked`, `onChange`, `partial`).
|
|
226
|
+
- `ScTabField` — labelled single-choice field for form stacks.
|
|
227
|
+
- `ScSelectionPillGroup` — segmented single-choice for filters/views.
|
|
228
|
+
- `ScSelection` / `ScSelectionList` — the in-chat option family (**agent runtime**, not dashboards).
|