@streamoid/ui 0.6.17 → 0.6.18
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 +321 -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 +213 -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/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +403 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4849 -0
- package/dist/index.css +36 -36
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScAskAgentButton
|
|
3
|
+
also_exports: [ScAskAgentSlot, AssistantCta]
|
|
4
|
+
package: "@streamoid/ui"
|
|
5
|
+
category: sidebar
|
|
6
|
+
status: stable
|
|
7
|
+
renders: button[type="button"]
|
|
8
|
+
tags: [ask-cxo, assistant, agent, cta, gradient, copilot, sidebar-footer, pill]
|
|
9
|
+
related: [StreamoidSidebar, ScArtifaxSidebar, ScButton, ScQuickPrompt]
|
|
10
|
+
do_not_confuse_with: [ScButton, ScCxoCopilotLogo, ScQuickPrompt, ScSidebarMenu]
|
|
11
|
+
used_by: [catalogix, artifax]
|
|
12
|
+
required_props: [cta]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# ScAskAgentButton
|
|
16
|
+
|
|
17
|
+
**The gradient "Ask CXO" call-to-action that sits just above a sidebar's profile
|
|
18
|
+
row.** A red→coral gradient pill with a leading icon and a label when expanded; a
|
|
19
|
+
40×40 gradient square with only the icon when collapsed. `ScAskAgentSlot` is the same
|
|
20
|
+
button plus the rail insets both sidebars use.
|
|
21
|
+
|
|
22
|
+
You will normally never render it yourself: pass `assistantCta` to `StreamoidSidebar`
|
|
23
|
+
or `ScArtifaxSidebar` and they render `ScAskAgentSlot` for you.
|
|
24
|
+
|
|
25
|
+
## TL;DR for agents
|
|
26
|
+
|
|
27
|
+
- **Reach for it when:** you need the assistant-panel CTA **outside** a DS sidebar.
|
|
28
|
+
Inside one, use the sidebar's `assistantCta` prop instead.
|
|
29
|
+
- **Don't reach for it when:** it's an ordinary action (→ `ScButton`), a suggested
|
|
30
|
+
prompt chip in a chat (→ `ScQuickPrompt`), or a nav row (→ `ScSidebarMenu`).
|
|
31
|
+
- **Three things that will bite you:**
|
|
32
|
+
1. `collapsed` drops the label, so with **no `icon` the collapsed square renders
|
|
33
|
+
empty** — a 40×40 patch of gradient with nothing in it.
|
|
34
|
+
2. Colours are **deliberately not tokens**: a hardcoded gradient and `#f5f5f5`
|
|
35
|
+
text. It looks identical in light and dark mode. That's the design, not a bug.
|
|
36
|
+
3. There is **no `disabled`** and no `loading`. Every click fires.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 1. How to use it
|
|
41
|
+
|
|
42
|
+
### Import
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import {
|
|
46
|
+
ScAskAgentButton,
|
|
47
|
+
ScAskAgentSlot,
|
|
48
|
+
type AssistantCta,
|
|
49
|
+
} from "@streamoid/ui";
|
|
50
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Minimal usage
|
|
54
|
+
|
|
55
|
+
Through a sidebar (the normal path — the sidebar picks the right variant per rail):
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<StreamoidSidebar
|
|
59
|
+
assistantCta={{
|
|
60
|
+
label: "Ask CXO",
|
|
61
|
+
icon: <SiconBolt size={20} color="currentColor" />,
|
|
62
|
+
active: askOpen,
|
|
63
|
+
onClick: () => setAskOpen((v) => !v),
|
|
64
|
+
}}
|
|
65
|
+
/* … */
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Standalone:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<ScAskAgentSlot
|
|
73
|
+
cta={{ label: "Ask CXO", icon: <SiconBolt size={20} />, onClick: openAssistant }}
|
|
74
|
+
/>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Props — `ScAskAgentButton` and `ScAskAgentSlot`
|
|
78
|
+
|
|
79
|
+
Both take exactly the same two props (`IScAskAgentButtonProps`).
|
|
80
|
+
|
|
81
|
+
| Prop | Type | Default | Notes |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `cta` | `AssistantCta` | — | **Required.** The whole contract lives in this object. |
|
|
84
|
+
| `collapsed` | `boolean` | `false` | `true` → 40×40 icon-only square, label dropped, `flex-shrink: 0`. `false` → full-width pill. |
|
|
85
|
+
|
|
86
|
+
### `AssistantCta`
|
|
87
|
+
|
|
88
|
+
| Field | Type | Default | Notes |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `label` | `string` | — | **Required.** Visible text when expanded. When collapsed it is **invisible but still the accessible name** — never drop it. |
|
|
91
|
+
| `icon` | `ReactNode` | – | Leading icon; rendered in both states. ⚠️ Optional in the type, **mandatory in practice for `collapsed`** (it is the only content). |
|
|
92
|
+
| `active` | `boolean` | – | Pressed styling (a 1.5px inset white ring) and `aria-pressed`. Wire it to "the panel this opens is visible". |
|
|
93
|
+
| `ariaLabel` | `string` | falls back to `label` | Used for both `aria-label` and the native `title` tooltip. |
|
|
94
|
+
| `onClick` | `() => void` | — | **Required.** |
|
|
95
|
+
|
|
96
|
+
### What renders in each state
|
|
97
|
+
|
|
98
|
+
| | `collapsed={false}` | `collapsed={true}` |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| Button | `width: 100%`, `padding: var(--spacing-xl)` (12px) | `2.5rem × 2.5rem`, `padding: var(--spacing-md)` |
|
|
101
|
+
| Icon | rendered, svg pinned to 20×20 | rendered, the only content |
|
|
102
|
+
| Label | 12px/600, `nowrap`, `#f5f5f5` | **not rendered** |
|
|
103
|
+
| Slot insets | `padding: 0 8px 16px` (full width) | `padding: 8px 0`, centred |
|
|
104
|
+
| `aria-pressed` | `cta.active ?? undefined` | same |
|
|
105
|
+
|
|
106
|
+
### Recipes
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// Both rails, driven by the sidebar (Catalogix / Artifax)
|
|
110
|
+
<ScArtifaxSidebar
|
|
111
|
+
expanded={!collapsed}
|
|
112
|
+
assistantCta={
|
|
113
|
+
onToggleAssistant
|
|
114
|
+
? { label: "Ask CXO", icon: <SiconBolt className="h-5 w-5" />, active: assistantOpen, onClick: onToggleAssistant }
|
|
115
|
+
: undefined // omit/null hides the CTA entirely
|
|
116
|
+
}
|
|
117
|
+
/* … */
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
// Standalone, mirroring the sidebar rails yourself
|
|
121
|
+
{expanded
|
|
122
|
+
? <ScAskAgentSlot cta={cta} />
|
|
123
|
+
: <ScAskAgentSlot cta={cta} collapsed />}
|
|
124
|
+
|
|
125
|
+
// Bare button (no rail insets) — e.g. in a page header or an empty state
|
|
126
|
+
<ScAskAgentButton
|
|
127
|
+
cta={{
|
|
128
|
+
label: "Ask CXO",
|
|
129
|
+
ariaLabel: "Open the CXO assistant", // distinct tooltip / accessible name
|
|
130
|
+
icon: <SiconBolt size={20} />,
|
|
131
|
+
active: panelOpen,
|
|
132
|
+
onClick: togglePanel,
|
|
133
|
+
}}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
// "Disabled" isn't supported — gate at the call site instead
|
|
137
|
+
{canUseAssistant ? <ScAskAgentSlot cta={cta} /> : null}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 2. Where to use it
|
|
143
|
+
|
|
144
|
+
- **The pre-footer slot of a sidebar rail**, immediately above `preFooterContent` and
|
|
145
|
+
the profile row. `StreamoidSidebar` renders `<ScAskAgentSlot cta={assistantCta} />`
|
|
146
|
+
(expanded) / `<ScAskAgentSlot cta={assistantCta} collapsed />` (collapsed);
|
|
147
|
+
`ScArtifaxSidebar` does the same.
|
|
148
|
+
- **Live hosts:** Catalogix (`LeftMenu`, via `StreamoidSidebar`) and Artifax
|
|
149
|
+
(`DashboardSidebar`, via `ScArtifaxSidebar`). CXO instead exposes "Ask CXO" as a
|
|
150
|
+
**top nav item** in its sidebar body, so it does not use this component.
|
|
151
|
+
- Standalone use (bare `ScAskAgentButton`) is fine anywhere the CTA must not be
|
|
152
|
+
inset for a 256px rail.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 3. When to use it
|
|
157
|
+
|
|
158
|
+
### Use it when
|
|
159
|
+
|
|
160
|
+
- The action is **"open the assistant/copilot panel"** and it should read as a
|
|
161
|
+
special, branded affordance rather than a regular button.
|
|
162
|
+
- You need it to survive rail collapse without a second implementation.
|
|
163
|
+
- You want `aria-pressed` to track whether the panel is open.
|
|
164
|
+
|
|
165
|
+
### Don't use it — reach for this instead
|
|
166
|
+
|
|
167
|
+
| Situation | Use instead |
|
|
168
|
+
|---|---|
|
|
169
|
+
| Any ordinary action or CTA | `ScButton` (`variant="mono"` / `"primary"` / `"outline"`) |
|
|
170
|
+
| A suggested-prompt chip inside the chat transcript | `ScQuickPrompt` |
|
|
171
|
+
| A nav row that happens to route to the assistant | `ScSidebarMenu`, or `StreamoidSidebar`'s `topItems` — what CXO does |
|
|
172
|
+
| The CXO Copilot brand lockup / logo | `ScCxoCopilotLogo` |
|
|
173
|
+
| A destructive/primary form submit | `ScButton variant="error"` / `"primary"` |
|
|
174
|
+
| You need `disabled`, `loading` or a spinner | `ScButton` — this component has none |
|
|
175
|
+
|
|
176
|
+
### Don't confuse with
|
|
177
|
+
|
|
178
|
+
| You may actually want | Not this |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `ScButton` — six variants, tokenised, `loading`, `state="disabled"` | This is one fixed brand gradient with no states beyond `active` |
|
|
181
|
+
| `ScQuickPrompt` — chat-runtime prompt chips | Different family, different surface |
|
|
182
|
+
| `ScCxoCopilotLogo` — brand art | Not interactive |
|
|
183
|
+
| `ScSidebarMenu` with an icon | The nav-row treatment; no gradient |
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 4. Why to use it
|
|
188
|
+
|
|
189
|
+
- **One gradient, one place.** The CSS was extracted verbatim from
|
|
190
|
+
`ScArtifaxSidebar.module.css` so Artifax's pill and the CXO/Catalogix pill cannot
|
|
191
|
+
drift. Matches Figma `SC-AskAgent` (node 6141-7469): red→coral 135° gradient, 0.5px
|
|
192
|
+
red-tinted stroke, `var(--radius-2xl)` corners (**14px** with `@streamoid/tokens`
|
|
193
|
+
loaded — the `0.75rem`/12px in the CSS is only the no-token fallback), 12px padding.
|
|
194
|
+
- **Deliberately theme-*in*variant, correctly.** The gradient is always dark, so the
|
|
195
|
+
label and icon are pinned to `#f5f5f5` rather than `--alias-text-*`. Using the token
|
|
196
|
+
would make the text black-on-red in light mode. Hand-rolling this is exactly how
|
|
197
|
+
that bug ships.
|
|
198
|
+
- **Both rails in one prop.** `collapsed` swaps a full-width pill for a 40×40 square
|
|
199
|
+
and drops the label while keeping the accessible name — no duplicate markup, no
|
|
200
|
+
divergent hover.
|
|
201
|
+
- **Correct toggle semantics.** Real `<button type="button">` with `aria-pressed`,
|
|
202
|
+
a `title` tooltip, and a `:focus-visible` ring designed to be visible **on the
|
|
203
|
+
gradient** (white, 2px, 2px offset).
|
|
204
|
+
- **The slot owns the rail geometry.** `ScAskAgentSlot` carries the Figma insets
|
|
205
|
+
(8px horizontal, 16px gap before the divider), so a sidebar only decides *whether*
|
|
206
|
+
to show the CTA.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Gotchas
|
|
211
|
+
|
|
212
|
+
**1. Collapsed with no `icon` renders an empty gradient square.** The label is
|
|
213
|
+
suppressed and `icon` is optional, so nothing is left.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
// WRONG — a blank 40×40 gradient tile
|
|
217
|
+
<ScAskAgentSlot cta={{ label: "Ask CXO", onClick: open }} collapsed />
|
|
218
|
+
|
|
219
|
+
// RIGHT
|
|
220
|
+
<ScAskAgentSlot cta={{ label: "Ask CXO", icon: <SiconBolt size={20} />, onClick: open }} collapsed />
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**2. Don't drop `label` when collapsing.** It is required, and it supplies both
|
|
224
|
+
`aria-label` and `title` when `ariaLabel` is unset — the collapsed square's only
|
|
225
|
+
accessible name.
|
|
226
|
+
|
|
227
|
+
**3. It ignores your theme.** Gradient `rgba(217,21,54) → rgba(238,94,58)`, border
|
|
228
|
+
`rgba(255,0,0,0.32)`, text/icon `#f5f5f5`, hover `filter: brightness(1.08)` — all
|
|
229
|
+
literal, none tokenised. It will not respond to light mode, `data-theme`, or a
|
|
230
|
+
`color` you set on a parent.
|
|
231
|
+
|
|
232
|
+
**4. Your icon's colour is not overridden, but the container's is.**
|
|
233
|
+
`.askAgentIcon` sets `color: #f5f5f5`, so an icon drawn with `currentColor`
|
|
234
|
+
(`<SiconBolt color="currentColor" />`) comes out light — which is what you want. An
|
|
235
|
+
icon with an explicit token colour will keep it and may be unreadable on the gradient.
|
|
236
|
+
|
|
237
|
+
**5. The svg is pinned to 20×20 at class-level specificity.**
|
|
238
|
+
`.askAgentIcon :where(svg) { width: 1.25rem; height: 1.25rem }`. `:where()` adds no
|
|
239
|
+
specificity, so a Tailwind `h-5 w-5` on your icon *ties* and the winner depends on
|
|
240
|
+
stylesheet order. If you need a different size, set it inline:
|
|
241
|
+
`<MyIcon style={{ width: 24, height: 24 }} />`.
|
|
242
|
+
|
|
243
|
+
**6. No `disabled`, no `loading`, no `state`.** Gate rendering at the call site
|
|
244
|
+
(Artifax only builds the `assistantCta` object when the host supplies a toggle).
|
|
245
|
+
|
|
246
|
+
**7. `active` is styling *and* semantics.** It adds an inset white ring **and** sets
|
|
247
|
+
`aria-pressed`. Don't use it as a "highlight" for anything other than "the thing this
|
|
248
|
+
opens is currently open", or screen readers will lie.
|
|
249
|
+
|
|
250
|
+
**8. Don't double it up.** Both sidebars render this from `assistantCta`. Adding your
|
|
251
|
+
own `ScAskAgentSlot` to `preFooterContent` gives you two pills stacked.
|
|
252
|
+
|
|
253
|
+
**9. `ScAskAgentButton` vs `ScAskAgentSlot`.** The button has **no outer margin**; the
|
|
254
|
+
slot adds the rail insets (`0 8px 16px` expanded, `8px 0` collapsed). Inside a sidebar
|
|
255
|
+
rail use the slot; elsewhere use the button and space it yourself.
|
|
256
|
+
|
|
257
|
+
**10. The focus ring is white.** `outline: 2px solid rgba(255,255,255,0.9)` — it's
|
|
258
|
+
designed to sit on the gradient. On a white page background the offset ring is
|
|
259
|
+
effectively invisible.
|
|
260
|
+
|
|
261
|
+
**11. `ArtifaxSidebarAssistantCta` is just an alias of `AssistantCta`.** Kept as a
|
|
262
|
+
named export for back-compat; the same object satisfies both sidebars.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## In the wild
|
|
267
|
+
|
|
268
|
+
The hosts render it through their sidebar's `assistantCta` prop rather than importing
|
|
269
|
+
the component:
|
|
270
|
+
|
|
271
|
+
```jsx
|
|
272
|
+
// catalogix/dashboard app/containers/LeftMenu/index.jsx:1005 (→ StreamoidSidebar renders ScAskAgentSlot)
|
|
273
|
+
assistantCta={{
|
|
274
|
+
label: "Ask CXO",
|
|
275
|
+
icon: <SiconBolt size={20} color="currentColor" />,
|
|
276
|
+
active: askCxoOpen,
|
|
277
|
+
onClick: toggleAskCxo,
|
|
278
|
+
}}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
// artifax packages/shared/src/components/DashboardSidebar.tsx:684 (→ ScArtifaxSidebar renders ScAskAgentSlot)
|
|
283
|
+
assistantCta={
|
|
284
|
+
onToggleAssistant
|
|
285
|
+
? {
|
|
286
|
+
label: ASK_CXO_LABEL,
|
|
287
|
+
icon: <SiconBolt className="h-5 w-5" />,
|
|
288
|
+
active: assistantOpen,
|
|
289
|
+
onClick: onToggleAssistant,
|
|
290
|
+
}
|
|
291
|
+
: undefined
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
No host imports `ScAskAgentButton` / `ScAskAgentSlot` directly — if you need it
|
|
296
|
+
outside a DS sidebar rail, you are the first, and `ScAskAgentButton` (not the slot)
|
|
297
|
+
is the right entry point.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Related
|
|
302
|
+
|
|
303
|
+
- `StreamoidSidebar` — pass `assistantCta`; it renders the slot in both rails.
|
|
304
|
+
- `ScArtifaxSidebar` — same, via `ArtifaxSidebarAssistantCta`.
|
|
305
|
+
- `ScButton` — the tokenised, stateful button for everything that isn't this CTA.
|
|
306
|
+
- `ScQuickPrompt` — suggested-prompt chips inside the chat surface.
|
|
307
|
+
- `@streamoid/icons` — `SiconBolt` is the icon both hosts pass.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScBadges
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: overlays
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [badge, chip, status, tag, label, pill, count, success, warning, error, info]
|
|
8
|
+
related: [ScBeacon, ScRole, ScSelectionPill, ScTaxonomyPill, ScProgressBar]
|
|
9
|
+
do_not_confuse_with: [ScBeacon, ScSelectionPill, ScTaxonomyPill, ScRole, ScButton]
|
|
10
|
+
used_by: [cxo, photogenix, artifax]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScBadges
|
|
14
|
+
|
|
15
|
+
**The status chip.** A small rounded box with one line of 12px text, in five
|
|
16
|
+
semantic colours × two skins (`solid` filled, `opaque` outlined). Display-only:
|
|
17
|
+
it takes no click handler, no style, no aria — just `text`, `variant`,
|
|
18
|
+
`styleVariant`, `className`.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you need to state a status, a count or a tag next to
|
|
23
|
+
something — "Revoked", "Active", "1,240 credits", "Needs attention".
|
|
24
|
+
- **Don't reach for it when:** it should be clickable (→ `ScSelectionPill`,
|
|
25
|
+
`ScTaxonomyPill`, `ScButton`), it's a bare status dot with no text
|
|
26
|
+
(→ `ScBeacon`), or it's specifically an Admin/Member role (→ `ScRole`).
|
|
27
|
+
- **Four things that will bite you:**
|
|
28
|
+
1. `...props` is destructured but **never spread**. `onClick`, `style`,
|
|
29
|
+
`title`, `data-*`, `aria-*` are all dropped *and* are type errors. Wrap it.
|
|
30
|
+
2. The root is `align-self: stretch` ⚠️ — inside a flex row it grows to the
|
|
31
|
+
tallest sibling's height instead of hugging its text.
|
|
32
|
+
3. `text` defaults to `"Badge"`.
|
|
33
|
+
4. `styleVariant="solid"` + `variant="info"` is the one solid combination that
|
|
34
|
+
does **not** force white text — a real contrast inconsistency.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. How to use it
|
|
39
|
+
|
|
40
|
+
### Import
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { ScBadges } from "@streamoid/ui";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Minimal usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<ScBadges text="Revoked" variant="error" />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Props
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Notes |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `text` | `string` | `"Badge"` | ⚠️ Real default. Plain string only — build the label yourself (`` `${n} credits` ``). |
|
|
58
|
+
| `variant` | `"default"` \| `"success"` \| `"warning"` \| `"error"` \| `"info"` | `"default"` | The semantic role. |
|
|
59
|
+
| `styleVariant` | `"solid"` \| `"opaque"` | `"solid"` | `solid` = filled, `opaque` = 1px semantic border + semantic text on a **transparent** background (`background: unset`). |
|
|
60
|
+
| `className` | `string` | – | Concatenated unconditionally — see Gotcha 5. **The only escape hatch you get.** |
|
|
61
|
+
|
|
62
|
+
`IScBadgesProps` does **not** extend `HTMLAttributes`, and the component's
|
|
63
|
+
`...props` rest is collected and thrown away. There is no `onClick`, `style`,
|
|
64
|
+
`title`, `size`, `icon` or `onClose`.
|
|
65
|
+
|
|
66
|
+
### The colour matrix
|
|
67
|
+
|
|
68
|
+
| `variant` | `styleVariant="solid"` | `styleVariant="opaque"` |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `default` | fill `--alias-fill-neutral-neutralactive`, text `…-primary` | border `--alias-border-strong`, text `…-tertiary` |
|
|
71
|
+
| `success` | fill `--alias-fill-success-solidactive`, text **`…-fullwhite`** | border `--alias-border-successplus`, text `…-success` |
|
|
72
|
+
| `warning` | fill `--alias-fill-warning-solidactive`, text **`…-fullwhite`** | border `--alias-border-warningplus`, text `…-warning` |
|
|
73
|
+
| `error` | fill `--alias-fill-error-solidactive`, text **`…-fullwhite`** | border `--alias-border-errorplus`, text `…-error` |
|
|
74
|
+
| `info` | fill `--alias-fill-info-solid`, text `…-primary` ⚠️ | border `--alias-border-infoplus`, text `…-info` |
|
|
75
|
+
|
|
76
|
+
### Recipes
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Outlined status chip — the most common host choice (quieter in dense lists)
|
|
80
|
+
<ScBadges text={label} variant={statusVariant} styleVariant="opaque" />
|
|
81
|
+
|
|
82
|
+
// Make it clickable / hoverable — wrap it, you cannot pass onClick
|
|
83
|
+
<span role="button" tabIndex={0} onClick={filterByStatus} style={{ cursor: "pointer" }}>
|
|
84
|
+
<ScBadges text="Needs attention" variant="warning" styleVariant="opaque" />
|
|
85
|
+
</span>
|
|
86
|
+
|
|
87
|
+
// Stop it stretching to the row height
|
|
88
|
+
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
89
|
+
<span>Feed status</span>
|
|
90
|
+
<ScBadges text="Live" variant="success" styleVariant="opaque" className="hug" />
|
|
91
|
+
</div>
|
|
92
|
+
// .hug { align-self: center; } /* or: flex-shrink:0; align-self:auto */
|
|
93
|
+
|
|
94
|
+
// Map your domain status onto a variant once, at the boundary
|
|
95
|
+
const VARIANT = { active: "success", pending: "warning", failed: "error", draft: "default" } as const;
|
|
96
|
+
<ScBadges text={STATUS_LABEL[s]} variant={VARIANT[s]} styleVariant="opaque" />
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 2. Where to use it
|
|
102
|
+
|
|
103
|
+
- **Table / list rows** — status column. CXO's token list marks revoked API tokens;
|
|
104
|
+
Photogenix wraps it as `StatusBadge` / `ConsistencyStatusPill` for render batches.
|
|
105
|
+
- **Card headers** — `ScPlanDetailsCard` (+ its Mobile twin), `ScWorkspaceCard` and
|
|
106
|
+
`ScProfileOptions` each render one internally; pass their own badge prop rather
|
|
107
|
+
than nesting your own.
|
|
108
|
+
- **Sidebar footer** — Artifax puts the credits figure in an `opaque` badge beside
|
|
109
|
+
the workspace name.
|
|
110
|
+
- **Referral / billing tables** — `ScReferralTableList` renders one per row.
|
|
111
|
+
- **Gallery / media tiles** — Photogenix's `RendersGallery`.
|
|
112
|
+
|
|
113
|
+
Used by CXO, Photogenix and Artifax; Catalogix has app-local status pills. Part of
|
|
114
|
+
the catalog's "universal core".
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 3. When to use it
|
|
119
|
+
|
|
120
|
+
### Use it when
|
|
121
|
+
|
|
122
|
+
- The content is a **short, non-interactive fact**: a state, a count, a tag.
|
|
123
|
+
- You want the semantic colour pair (fill/border + text) to be identical to every
|
|
124
|
+
other status in the product, in both themes.
|
|
125
|
+
- The badge sits **beside** its subject, not on top of it.
|
|
126
|
+
|
|
127
|
+
### Don't use it — reach for this instead
|
|
128
|
+
|
|
129
|
+
| Situation | Use instead |
|
|
130
|
+
|---|---|
|
|
131
|
+
| The chip is a filter/tab the user clicks | `ScSelectionPill` (+ `ScSelectionPillGroup`) |
|
|
132
|
+
| A node in a taxonomy/hierarchy tree with counts and expand/collapse | `ScTaxonomyPill` |
|
|
133
|
+
| Specifically an Admin / Member role marker | `ScRole` / `ScRoleMobile` |
|
|
134
|
+
| A bare coloured status dot, no text | `ScBeacon` |
|
|
135
|
+
| A removable input token with an ✕ | no DS component — build it app-local; this badge has no close affordance |
|
|
136
|
+
| A progress/quantity meter | `ScProgressBar` |
|
|
137
|
+
| A call to action | `ScButton` |
|
|
138
|
+
| A validation message under a field | `ScTextField`'s helper/error text |
|
|
139
|
+
| A dismissible page-level alert | `CreditWarningBanner` (exported without the `Sc` prefix), or your app's banner |
|
|
140
|
+
|
|
141
|
+
### Don't confuse with
|
|
142
|
+
|
|
143
|
+
| You may actually want | Not this |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `ScBeacon` — a 20px two-ring status **dot**, 4 tones (no `info`), `aria-hidden` | `ScBadges` is the text chip; the two are often used together |
|
|
146
|
+
| `ScSelectionPill` — a real `<button aria-pressed>` for segmented selection | `ScBadges` is a non-interactive `<div>` |
|
|
147
|
+
| `ScTaxonomyPill` — tree node with `pill`/`expand`/`collapse` types | Visually similar, structurally different |
|
|
148
|
+
| `ScRole` — dedicated `admin`/`member` badge | Use it instead of hand-mapping roles onto variants |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 4. Why to use it
|
|
153
|
+
|
|
154
|
+
- **The semantic pairs are already correct in both themes.** Each `opaque` variant
|
|
155
|
+
pairs a `--alias-border-*plus` (a very dark tinted border) with the matching
|
|
156
|
+
`--alias-text-and-icons-*`, and each `solid` variant pairs a `*-solidactive` fill
|
|
157
|
+
with `fullwhite` text. Hand-rolled chips are the most common place a status colour
|
|
158
|
+
becomes unreadable when light mode collapses the surfaces.
|
|
159
|
+
- **`fullwhite`, not `primary`, on solid fills.** Solid success/warning/error force
|
|
160
|
+
`--alias-text-and-icons-fullwhite` — deliberately *not* the theme-flipping primary
|
|
161
|
+
token, because the fill stays saturated in both themes. Getting that wrong is the
|
|
162
|
+
classic light-mode "white text on light green" bug.
|
|
163
|
+
- **One vocabulary of five statuses.** Every status in the product resolves to the
|
|
164
|
+
same five colours, so users learn the palette once.
|
|
165
|
+
- **It's tiny and it composes.** `flex-shrink: 0` and centred content mean it sits
|
|
166
|
+
cleanly in a table cell, a card header or a flex row without pushing anything
|
|
167
|
+
around — and five DS cards already embed one.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Gotchas
|
|
172
|
+
|
|
173
|
+
**1. `...props` is collected and never spread.** Look at the source: the rest
|
|
174
|
+
element exists but the returned `<div>` receives only `className`. So there is *no*
|
|
175
|
+
way to attach a handler, an inline style, a tooltip or a test id — and because the
|
|
176
|
+
interface doesn't extend `HTMLAttributes`, TypeScript rejects them anyway.
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// WRONG — type error, and nothing happens at runtime either
|
|
180
|
+
<ScBadges text="Revoked" variant="error" onClick={undo} title="Revoked 2 days ago" />
|
|
181
|
+
|
|
182
|
+
// RIGHT — wrap it
|
|
183
|
+
<span title="Revoked 2 days ago" onClick={undo} role="button" tabIndex={0}>
|
|
184
|
+
<ScBadges text="Revoked" variant="error" />
|
|
185
|
+
</span>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**2. `align-self: stretch` on the root.** This is the surprise that produces
|
|
189
|
+
"why is my badge tall?". In a flex **row** it stretches to the tallest sibling; in a
|
|
190
|
+
flex **column** it stretches to full width.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// WRONG — badge grows to the height of the two-line name block beside it
|
|
194
|
+
<div style={{ display: "flex" }}>
|
|
195
|
+
<div><b>{name}</b><br />{email}</div>
|
|
196
|
+
<ScBadges text="Admin" />
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
// RIGHT — pin it, or give the parent alignItems
|
|
200
|
+
<div style={{ display: "flex", alignItems: "center" }}>…</div>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**3. `text` defaults to `"Badge"`.** Forget the prop and you ship the placeholder.
|
|
204
|
+
|
|
205
|
+
**4. Solid `info` keeps primary text.** Solid success/warning/error force
|
|
206
|
+
`fullwhite`; `info` does not — it renders `--alias-text-and-icons-primary` on a
|
|
207
|
+
`--alias-fill-info-solid` blue, which in light mode is dark-on-blue. Prefer
|
|
208
|
+
`styleVariant="opaque"` for `info`.
|
|
209
|
+
|
|
210
|
+
**5. `className` is concatenated unconditionally.** Omit it and the class attribute
|
|
211
|
+
contains the literal string `undefined`. Harmless in the browser, breaks
|
|
212
|
+
exact-match snapshots.
|
|
213
|
+
|
|
214
|
+
**6. `opaque` has `background: unset`.** The chip is transparent, so it inherits
|
|
215
|
+
whatever is behind it — over a busy or coloured surface the text can lose contrast.
|
|
216
|
+
`solid` is the safer choice on non-flat backgrounds.
|
|
217
|
+
|
|
218
|
+
**7. No `size`, no `icon`, no `onClose`.** Text is always 12px/1.125rem, padding
|
|
219
|
+
always `4px 8px`, radius always `--radius-md`. A leading dot must be a separate
|
|
220
|
+
`ScBeacon` next to it; a removable token is not this component.
|
|
221
|
+
|
|
222
|
+
**8. `text` is `string`, not `ReactNode`.** You cannot put a `<b>` or an icon
|
|
223
|
+
inside. Interpolate the string.
|
|
224
|
+
|
|
225
|
+
**9. Five DS cards already render one.** `ScPlanDetailsCard`,
|
|
226
|
+
`ScPlanDetailsCardMobile`, `ScWorkspaceCard`, `ScProfileOptions` and
|
|
227
|
+
`ScReferralTableList` embed a badge internally. Pass their badge/status prop instead
|
|
228
|
+
of nesting a second one.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## In the wild
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
// photogenix_v2 dashboard/client/src/components/studio/StatusBadge.tsx:95
|
|
236
|
+
<ScBadges
|
|
237
|
+
text={label}
|
|
238
|
+
variant={getStatusVariant(status)}
|
|
239
|
+
styleVariant="opaque"
|
|
240
|
+
className={className}
|
|
241
|
+
/>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
// artifax packages/shared/src/components/DashboardSidebar.tsx:901
|
|
246
|
+
<ScBadges
|
|
247
|
+
text={creditsLabel}
|
|
248
|
+
variant={typeof credits === 'number' && credits > 0 ? 'success' : 'default'}
|
|
249
|
+
styleVariant="opaque"
|
|
250
|
+
/>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Related
|
|
256
|
+
|
|
257
|
+
- `ScBeacon` — the dot-only status marker; pair it with a badge or use it alone in dense tables.
|
|
258
|
+
- `ScRole` / `ScRoleMobile` — the purpose-built Admin/Member badge.
|
|
259
|
+
- `ScSelectionPill` / `ScTaxonomyPill` — the *interactive* chip vocabulary.
|
|
260
|
+
- `ScProgressBar` — when the status is really a quantity.
|
|
261
|
+
- `ScPlanDetailsCard` / `ScWorkspaceCard` / `ScReferralTableList` — DS parents that already render one.
|