@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,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScAppSwitchPanel
|
|
3
|
+
also_exports: [ScAppSwitchRow]
|
|
4
|
+
package: "@streamoid/ui"
|
|
5
|
+
category: sidebar
|
|
6
|
+
status: stable
|
|
7
|
+
renders: div
|
|
8
|
+
tags: [app-switcher, switch-app, product-switcher, cross-product, tactix, artifax, photogenix, catalogix, streamoid, wordmark]
|
|
9
|
+
related: [ScSideBarLogoUnit, StreamoidSidebar, ScSidebarSwitchMenu, ScWorkspaceSwitchCard]
|
|
10
|
+
do_not_confuse_with: [ScSidebarSwitchMenu, StreamoidWorkspaceSwitcher, ScWorkspaceSwitchCard, ScAppListingCard, ScAppCard]
|
|
11
|
+
used_by: [cxo, photogenix, catalogix, artifax]
|
|
12
|
+
required_props: [apps]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# ScAppSwitchPanel
|
|
16
|
+
|
|
17
|
+
**The cross-product "switch app" list — one identical implementation for all four
|
|
18
|
+
apps.** A bare column of 60px rows, each showing the target product's wordmark
|
|
19
|
+
(gray, brightening to white on hover) and a `>` chevron that fades in. It has no
|
|
20
|
+
card, no border and no background: the surrounding sidebar or flyout supplies those.
|
|
21
|
+
|
|
22
|
+
## TL;DR for agents
|
|
23
|
+
|
|
24
|
+
- **Reach for it when:** you need the "go to another Streamoid product" list —
|
|
25
|
+
in `StreamoidSidebar`'s `switchPanel`, or in your own portalled flyout.
|
|
26
|
+
- **Don't reach for it when:** you're switching **workspaces** rather than products
|
|
27
|
+
(→ `ScWorkspaceSwitchCard` / `StreamoidWorkspaceSwitcher`), you want one row with
|
|
28
|
+
an icon + description (→ `ScSidebarSwitchMenu`), or you want a product tile in a
|
|
29
|
+
launcher grid (→ `ScAppListingCard`).
|
|
30
|
+
- **Four things that will bite you:**
|
|
31
|
+
1. It **re-sorts your array** into a canonical product order. Your ordering is
|
|
32
|
+
discarded.
|
|
33
|
+
2. If a product matches an embedded wordmark, `name` **and** `description` are
|
|
34
|
+
**not rendered at all** — the wordmark replaces them.
|
|
35
|
+
3. Matching is a **substring** test over `` `${key} ${name}`.toLowerCase() ``. A key
|
|
36
|
+
containing `"cxo"` or `"streamoid"` gets the Streamoid wordmark.
|
|
37
|
+
4. It does **not** exclude the current app. That's the host's job.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. How to use it
|
|
42
|
+
|
|
43
|
+
### Import
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import {
|
|
47
|
+
ScAppSwitchPanel,
|
|
48
|
+
ScAppSwitchRow,
|
|
49
|
+
type ScAppSwitchItem,
|
|
50
|
+
} from "@streamoid/ui";
|
|
51
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Minimal usage
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<ScAppSwitchPanel
|
|
58
|
+
apps={[
|
|
59
|
+
{ key: "artifax", name: "Artifax", onSelect: () => open(artifaxUrl) },
|
|
60
|
+
{ key: "photogenix", name: "Photogenix", onSelect: () => open(photogenixUrl) },
|
|
61
|
+
]}
|
|
62
|
+
/>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Props — `ScAppSwitchPanel`
|
|
66
|
+
|
|
67
|
+
| Prop | Type | Default | Notes |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| `apps` | `ScAppSwitchItem[]` | — | **Required.** ⚠️ **Re-sorted internally** into canonical order; unknown products keep their relative order and go last. |
|
|
70
|
+
| `className` | `string` | – | On the root column. |
|
|
71
|
+
| `style` | `CSSProperties` | – | Merged **after** the base inline styles, so it can override `paddingBottom`. |
|
|
72
|
+
|
|
73
|
+
### Props — `ScAppSwitchRow`
|
|
74
|
+
|
|
75
|
+
| Prop | Type | Default | Notes |
|
|
76
|
+
|---|---|---|---|
|
|
77
|
+
| `item` | `ScAppSwitchItem` | — | **Required.** The only prop — no `className`, no `style`. Rarely used directly; the panel renders one per app. |
|
|
78
|
+
|
|
79
|
+
### `ScAppSwitchItem`
|
|
80
|
+
|
|
81
|
+
| Field | Type | Notes |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `key` | `string` | React key **and** half of the wordmark-matching string. |
|
|
84
|
+
| `name` | `string` | Matching input, and the fallback label. ⚠️ **Not rendered** when a wordmark matches. Always used for the row's `aria-label` (`"Switch to {name}"`). |
|
|
85
|
+
| `description` | `string` | Optional tagline. ⚠️ **Only rendered for products with no embedded wordmark.** |
|
|
86
|
+
| `onSelect` | `() => void` | Row click. |
|
|
87
|
+
|
|
88
|
+
### Wordmark matching + canonical order
|
|
89
|
+
|
|
90
|
+
| Order | Matches (substring, case-insensitive, on `key + " " + name`) | Renders |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| 1 | `tactix` | Tactix wordmark (100×34) |
|
|
93
|
+
| 2 | `artifax`, `design studio` | Artifax wordmark (116×32) |
|
|
94
|
+
| 3 | `photogenix`, `photo studio` | Photogenix wordmark (142×36) |
|
|
95
|
+
| 4 | `catalogix`, `catalog studio` | Catalogix wordmark (94×36) |
|
|
96
|
+
| 5 | `streamoid`, `cxo` | Streamoid wordmark (110×34) |
|
|
97
|
+
| last | anything else | `name` (16px semibold) + `description` (14px tertiary) |
|
|
98
|
+
|
|
99
|
+
### Recipes
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// Inside StreamoidSidebar — the sidebar positions it for both rails
|
|
103
|
+
<StreamoidSidebar
|
|
104
|
+
switchPanel={apps.length ? <ScAppSwitchPanel apps={apps} /> : undefined}
|
|
105
|
+
switchPanelOpen={appListOpen}
|
|
106
|
+
onSwitchPanelClose={() => setAppListOpen(false)}
|
|
107
|
+
/* … */
|
|
108
|
+
/>
|
|
109
|
+
|
|
110
|
+
// Mapping a host service catalog, excluding the current app yourself
|
|
111
|
+
const apps: ScAppSwitchItem[] = serviceCatalog
|
|
112
|
+
.filter((s) => s.key !== CURRENT_APP_KEY) // the panel will NOT do this
|
|
113
|
+
.map((s) => ({
|
|
114
|
+
key: s.key,
|
|
115
|
+
name: s.name,
|
|
116
|
+
description: s.desc, // only shows for unknown products
|
|
117
|
+
onSelect: () => window.open(s.pageUrl, "_blank"),
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
<ScAppSwitchPanel apps={apps} />
|
|
121
|
+
|
|
122
|
+
// In your own portalled flyout (ScArtifaxSidebar has no switchPanel slot).
|
|
123
|
+
// The panel brings its own bottom padding — don't add a second one.
|
|
124
|
+
createPortal(
|
|
125
|
+
<div style={{ width: 240, background: "var(--alias-surface-base)", border: "1px solid var(--alias-border-default)", borderRadius: "var(--radius-3xl)", overflow: "hidden" }}>
|
|
126
|
+
<ScAppSwitchPanel apps={apps} />
|
|
127
|
+
</div>,
|
|
128
|
+
document.body,
|
|
129
|
+
)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 2. Where to use it
|
|
135
|
+
|
|
136
|
+
- **`StreamoidSidebar`'s `switchPanel` slot** — CXO, Photogenix and Catalogix all do
|
|
137
|
+
this. The sidebar renders it as an animated drop-down card when expanded and as a
|
|
138
|
+
240px right-side flyout when collapsed.
|
|
139
|
+
- **A host-owned portalled flyout** — Artifax, because `ScArtifaxSidebar` has no
|
|
140
|
+
switch slot and the sidebar body would clip an inline panel.
|
|
141
|
+
- Triggered by `ScSideBarLogoUnit`; it never renders its own trigger.
|
|
142
|
+
- All four apps use it, which is the point: the switcher looks and orders identically
|
|
143
|
+
everywhere regardless of the order each backend returns.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 3. When to use it
|
|
148
|
+
|
|
149
|
+
### Use it when
|
|
150
|
+
|
|
151
|
+
- The user is moving between **Streamoid products** (Tactix / Artifax / Photogenix /
|
|
152
|
+
Catalogix / Streamoid CXO).
|
|
153
|
+
- You want the canonical order and the embedded brand wordmarks for free.
|
|
154
|
+
- The list lives inside chrome you already own (a sidebar card, a flyout, a popover).
|
|
155
|
+
|
|
156
|
+
### Don't use it — reach for this instead
|
|
157
|
+
|
|
158
|
+
| Situation | Use instead |
|
|
159
|
+
|---|---|
|
|
160
|
+
| Switching **workspace/tenant**, not product | `ScWorkspaceSwitchCard`, `StreamoidWorkspaceSwitcher`, `ScWorkspaceSwitchMobile` |
|
|
161
|
+
| A single product row with an icon, name **and** description you control | `ScSidebarSwitchMenu` |
|
|
162
|
+
| A product tile in a launcher grid / marketing surface | `ScAppListingCard`, `ScAppCardV3` |
|
|
163
|
+
| Products shown as nav items in the sidebar body | `ScSidebarMenu` via `config.sections` |
|
|
164
|
+
| An arbitrary popover menu of actions | `ScPopUpMenu` + `ScMenuOptions` |
|
|
165
|
+
| You need to render the current app as "selected" | Nothing here supports it — this list is "other apps" only |
|
|
166
|
+
|
|
167
|
+
### Don't confuse with
|
|
168
|
+
|
|
169
|
+
| You may actually want | Not this |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `ScSidebarSwitchMenu` — one row, `icon` + `text` + `description`, five `state`s, `variant="collapsed"` | `ScAppSwitchPanel` renders whole rows with **embedded** wordmarks and no state |
|
|
172
|
+
| `StreamoidWorkspaceSwitcher` / `ScWorkspaceSwitchCard` — workspaces (note: there is no `ScWorkspaceSwitcher` export) | This is products |
|
|
173
|
+
| `ScAppCard` / `ScAppCardV3` / `ScAppListingCard` — product **cards** | This is a compact switch list |
|
|
174
|
+
| `ScSideBarLogoUnit` — the trigger row | This is the panel it opens |
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 4. Why to use it
|
|
179
|
+
|
|
180
|
+
- **One canonical order, enforced in the component.** Every backend returns the
|
|
181
|
+
service catalog in a different order; the panel sorts by a fixed product list so
|
|
182
|
+
the switcher never reshuffles between apps. The Artifax test suite asserts exactly
|
|
183
|
+
this ("imposes its own canonical order regardless of the order supplied").
|
|
184
|
+
- **Brand wordmarks that respect the theme.** Each wordmark is drawn as a **CSS mask**
|
|
185
|
+
(`mask-image` data URI + `background-color`), so its colour is a token —
|
|
186
|
+
`--alias-text---icons-tertiary` at rest, `-primary` on hover. A plain `<img>` logo
|
|
187
|
+
would be stuck at one colour and invisible in light mode.
|
|
188
|
+
- **Real `<button type="button">` per row** with `aria-label="Switch to {name}"`, and
|
|
189
|
+
the wordmark span marked `aria-hidden` so the announcement isn't duplicated.
|
|
190
|
+
- **No chrome, deliberately.** Because it ships no card or border, the identical panel
|
|
191
|
+
drops into a sidebar drop-down, a 240px collapsed flyout and a portalled overlay
|
|
192
|
+
without fighting the host's surface.
|
|
193
|
+
- **One place to add a product.** New product ⇒ one entry in the `WORDMARKS` table,
|
|
194
|
+
and it appears, in the right position, in all four apps.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Gotchas
|
|
199
|
+
|
|
200
|
+
**1. Your order is discarded.** `ordered = [...apps].sort((a, b) => orderIndex(a) - orderIndex(b))`.
|
|
201
|
+
Sorting the array before passing it is wasted work; you cannot pin a product to the top.
|
|
202
|
+
|
|
203
|
+
**2. A matched wordmark hides `name` and `description`.** The row renders either the
|
|
204
|
+
mask wordmark **or** the name/description block, never both.
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
// POINTLESS — "Design generation" is never shown; "artifax" matches the wordmark
|
|
208
|
+
<ScAppSwitchPanel apps={[{ key: "artifax", name: "Artifax", description: "Design generation", onSelect }]} />
|
|
209
|
+
|
|
210
|
+
// description only surfaces for products with no embedded mark
|
|
211
|
+
<ScAppSwitchPanel apps={[{ key: "insights", name: "Insights", description: "Reporting", onSelect }]} />
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**3. Matching is a loose substring test on `key + " " + name`.** `"cxo"` and
|
|
215
|
+
`"streamoid"` both map to the Streamoid wordmark, `"design studio"` maps to Artifax,
|
|
216
|
+
`"photo studio"` to Photogenix, `"catalog studio"` to Catalogix. An internal key like
|
|
217
|
+
`"cxo-insights"` will render the **Streamoid** wordmark, not a text row.
|
|
218
|
+
|
|
219
|
+
**4. The current app is not excluded.** The source comment is explicit — "The current
|
|
220
|
+
app is expected to be excluded by the host." Forget the filter and the sidebar offers
|
|
221
|
+
to switch to the app you're already in.
|
|
222
|
+
|
|
223
|
+
**5. Hover is React state, not CSS.** Each row keeps `useState(hover)` and colours
|
|
224
|
+
itself inline. So (a) there is no `:hover` rule to override from the host, and (b)
|
|
225
|
+
the chevron has `opacity: 0` until a real `mouseenter` — it will be missing from
|
|
226
|
+
snapshots and from any screenshot harness that fakes `:hover`.
|
|
227
|
+
|
|
228
|
+
**6. There's no active/selected/disabled state.** No `selected`, no `disabled`, no
|
|
229
|
+
loading row. Hosts that need a loading state render their own skeleton **instead of**
|
|
230
|
+
the panel (CXO does exactly that).
|
|
231
|
+
|
|
232
|
+
**7. It brings its own `paddingBottom: var(--spacing-md)`.** Host flyouts note this
|
|
233
|
+
("applies its own bottom padding; don't double it"). Override via `style` if needed.
|
|
234
|
+
|
|
235
|
+
**8. Row height is a hardcoded 60px** with design-literal padding
|
|
236
|
+
(`15px 12px 12px 16px`). Long text rows do not grow — a two-line
|
|
237
|
+
`name` + `description` at 16px/14px only just fits.
|
|
238
|
+
|
|
239
|
+
**9. Everything is inline styles; there is no CSS module.** `className` gets you a
|
|
240
|
+
hook on the root only, and inline styles beat your stylesheet — restyle by wrapping,
|
|
241
|
+
not by targeting.
|
|
242
|
+
|
|
243
|
+
**10. `aria-label` is hardcoded English** (`Switch to {name}`), as is the entire
|
|
244
|
+
wordmark set. Not localisable without changing the DS.
|
|
245
|
+
|
|
246
|
+
**11. The wordmark assets are not public API.** `STREAMOID`, `ARTIFAX`,
|
|
247
|
+
`PHOTOGENIX`, `CATALOGIX`, `TACTIX` and `WordmarkAsset` live in
|
|
248
|
+
`SC-AppSwitch/wordmarks.ts` but are **not** re-exported from `@streamoid/ui` (only
|
|
249
|
+
`ScAppSwitchPanel`, `ScAppSwitchRow`, `ScAppSwitchItem`,
|
|
250
|
+
`IScAppSwitchPanelProps` are). For a wordmark you can place yourself, use
|
|
251
|
+
`ProductWordmark` from `SC-SideBarLogoUnit` — a different, path-based set.
|
|
252
|
+
|
|
253
|
+
**12. `mask-image: url("data:image/svg+xml,…")`.** If your app ships a strict CSP,
|
|
254
|
+
`img-src`/`default-src` must allow `data:` or the wordmarks render as blank boxes
|
|
255
|
+
(the coloured background shows through nothing).
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## In the wild
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
// cxo-dashboard src/app/components/app-sidebar.tsx:901
|
|
263
|
+
<ScAppSwitchPanel
|
|
264
|
+
apps={switchApps.map((app) => ({
|
|
265
|
+
key: app.key,
|
|
266
|
+
name: app.name,
|
|
267
|
+
description: app.desc,
|
|
268
|
+
onSelect: () => openSwitchApp(app.pageUrl),
|
|
269
|
+
}))}
|
|
270
|
+
/>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Also: `catalogix/dashboard app/containers/LeftMenu/index.jsx:1002` (straight into
|
|
274
|
+
`switchPanel`) and `artifax packages/shared/src/components/DashboardSidebar.tsx:1474`
|
|
275
|
+
(inside the host's portalled `SwitchFlyout`).
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Related
|
|
280
|
+
|
|
281
|
+
- `ScSideBarLogoUnit` — the row that opens this panel.
|
|
282
|
+
- `StreamoidSidebar` — pass this as `switchPanel`; it handles both rails and the backdrop.
|
|
283
|
+
- `ScArtifaxSidebar` — has **no** switch slot; portal the panel yourself.
|
|
284
|
+
- `ScSidebarSwitchMenu` — the older per-row switcher, still used by `ScCatalogixSidebar`.
|
|
285
|
+
- `ScWorkspaceSwitchCard` / `StreamoidWorkspaceSwitcher` — the workspace equivalent.
|
|
286
|
+
- `ProductWordmark` (SC-SideBarLogoUnit) — the other, exported wordmark set.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScAppcardLogos
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: brand
|
|
5
|
+
status: stable
|
|
6
|
+
renders: span[role="img"]
|
|
7
|
+
tags: [wordmark, logo, brand, product, tactix, artifax, photogenix, catalogix, svg, currentColor]
|
|
8
|
+
related: [ScAppCardForCopilot, ProductWordmark, ProductCollapsedMark, ScStreamoidWordmark, ScStrLogo]
|
|
9
|
+
do_not_confuse_with: [ProductWordmark, ScStreamoidWordmark, ScStreamoidMascot, ScCxoCopilotLogo, ScAppCard]
|
|
10
|
+
used_by: [agent]
|
|
11
|
+
required_props: [product]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# ScAppcardLogos
|
|
15
|
+
|
|
16
|
+
**One single-colour product wordmark, 14px tall.** Inline SVG for Tactix, Artifax,
|
|
17
|
+
Photogenix or Catalogix, filled with `currentColor` so the parent's text colour
|
|
18
|
+
paints it. Ships the type `AppcardProduct` used by `ScAppCardForCopilot`.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you need a product's *name as artwork* — inside a card,
|
|
23
|
+
chip or pill where a hover/theme colour change should recolour the mark for free.
|
|
24
|
+
- **Don't reach for it when:** you want the sidebar-header wordmark
|
|
25
|
+
(→ `ProductWordmark` / `ProductCollapsedMark`), the Streamoid brand
|
|
26
|
+
(→ `ScStreamoidWordmark` / `ScStreamoidMascot`), the CXO copilot mark
|
|
27
|
+
(→ `ScCxoCopilotLogo`), or an app icon plus a typed name (→ `ScAppCard`).
|
|
28
|
+
- **Four things that will bite you:**
|
|
29
|
+
1. `product` is **required** and the set is exactly four keys — `tactix`,
|
|
30
|
+
`artifax`, `photogenix`, `catalogix`. **There is no Streamoid or CXO wordmark.**
|
|
31
|
+
2. Widths differ per product (56 / 54 / 88 / 72 px at a fixed 14px height), so a
|
|
32
|
+
row of them is **not** visually uniform.
|
|
33
|
+
3. It sets `role="img"` with **`aria-label={product}` — the raw lowercase key**
|
|
34
|
+
("photogenix"), not a display name.
|
|
35
|
+
4. The SVG is injected with `dangerouslySetInnerHTML`. Safe here (the markup is a
|
|
36
|
+
compile-time constant), but it means you cannot target inner paths from a
|
|
37
|
+
parent stylesheet by anything other than element selectors.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. How to use it
|
|
42
|
+
|
|
43
|
+
### Import
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { ScAppcardLogos, type AppcardProduct } from "@streamoid/ui";
|
|
47
|
+
// No CSS import needed for this component — it styles itself inline.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Minimal usage
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<ScAppcardLogos product="catalogix" />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Props
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Default | Notes |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `product` | `AppcardProduct` = `"tactix" \| "artifax" \| "photogenix" \| "catalogix"` | — | **Required.** Selects the wordmark. Also becomes `aria-label` verbatim. |
|
|
61
|
+
| `color` | `string` | – | Sets `color` on the wrapper `<span>`; the SVG paths are `fill="currentColor"`, so this is how you tint the mark. Omit it and the mark inherits the surrounding text colour. |
|
|
62
|
+
| `className` | `string` | – | Applied to the wrapper `<span>` (the usual place to set `height`, `flex-shrink`, or a hover colour). |
|
|
63
|
+
| `style` | `CSSProperties` | – | Merged **after** the built-in `display: inline-flex; align-items: center; color` — so `style` can override `color`, and `style={{ display: "block" }}` works. |
|
|
64
|
+
|
|
65
|
+
There is no props spread: `onClick`, `data-*`, `aria-hidden` etc. are not accepted.
|
|
66
|
+
|
|
67
|
+
### Wordmark geometry
|
|
68
|
+
|
|
69
|
+
| `product` | SVG width | SVG height |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `tactix` | 56 | 14 |
|
|
72
|
+
| `artifax` | 54 | 14 |
|
|
73
|
+
| `photogenix` | 88 | 14 |
|
|
74
|
+
| `catalogix` | 72 | 14 |
|
|
75
|
+
|
|
76
|
+
### Recipes
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Inherit the parent's colour — the idiomatic use. The mark follows a hover
|
|
80
|
+
// transition on the parent for free, because it's currentColor all the way down.
|
|
81
|
+
<button className="text-[var(--alias-text---icons-tertiary)] hover:text-[var(--alias-text---icons-primary)]">
|
|
82
|
+
<ScAppcardLogos product="photogenix" />
|
|
83
|
+
</button>
|
|
84
|
+
|
|
85
|
+
// Pin it white on a dark/glassy card (what ScAppCardForCopilot does)
|
|
86
|
+
<ScAppcardLogos product="artifax" color="var(--alias-text---icons-fullwhite, #ffffff)" />
|
|
87
|
+
|
|
88
|
+
// Scale it: the SVG has width/height attributes, so constrain via CSS, not props
|
|
89
|
+
<ScAppcardLogos product="catalogix" style={{ height: 18 }} className="[&>svg]:h-full [&>svg]:w-auto" />
|
|
90
|
+
|
|
91
|
+
// Iterate the whole set type-safely
|
|
92
|
+
const PRODUCTS: AppcardProduct[] = ["tactix", "artifax", "photogenix", "catalogix"];
|
|
93
|
+
{PRODUCTS.map((p) => <ScAppcardLogos key={p} product={p} />)}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 2. Where to use it
|
|
99
|
+
|
|
100
|
+
- **`ScAppCardForCopilot`** renders it as the card's identity, pinned white with a
|
|
101
|
+
14px height.
|
|
102
|
+
- **The agent's empty chat state** (`stream-agent`) renders it directly inside
|
|
103
|
+
hand-rolled dashed-border product pills — the wordmark is tertiary by default and
|
|
104
|
+
goes primary on hover purely via `currentColor`.
|
|
105
|
+
- Any **compact "which product" affordance** on a dark or coloured surface where a
|
|
106
|
+
wordmark reads better than an icon + label.
|
|
107
|
+
|
|
108
|
+
Generated from Figma `AppcardLogos` (node `6383:9997`); the SVG strings live in
|
|
109
|
+
`SC-AppcardLogos/sources.ts` and are auto-generated — **do not hand-edit them**.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 3. When to use it
|
|
114
|
+
|
|
115
|
+
### Use it when
|
|
116
|
+
|
|
117
|
+
- The mark must **recolour with its context** (hover, theme, inverse surface) and
|
|
118
|
+
you do not want to ship two PNGs.
|
|
119
|
+
- Space is tight and 14px of wordmark is the whole identity.
|
|
120
|
+
- You need the `AppcardProduct` union to key your own product data.
|
|
121
|
+
|
|
122
|
+
### Don't use it — reach for this instead
|
|
123
|
+
|
|
124
|
+
| Situation | Use instead |
|
|
125
|
+
|---|---|
|
|
126
|
+
| A sidebar header's product wordmark (expanded state) | `ProductWordmark` |
|
|
127
|
+
| A collapsed sidebar's square product mark | `ProductCollapsedMark` (check `hasCollapsedMark`) |
|
|
128
|
+
| The Streamoid wordmark (98:32) or mascot | `ScStreamoidWordmark` / `ScStreamoidMascot` (SC-Brand) |
|
|
129
|
+
| The plain header Streamoid lockup vs the app-switcher lockup | `ScStreamoidWordmark` (SC-Brand) — those two sizes are distinct, don't conflate |
|
|
130
|
+
| The CXO Copilot logo | `ScCxoCopilotLogo` |
|
|
131
|
+
| The generic Streamoid `S` logo | `ScStrLogo` |
|
|
132
|
+
| An app *icon* (not the name) plus a typed label | `ScAppCard` / `ScAppListingCard` with an `Sicon*` |
|
|
133
|
+
| A whole clickable product card | `ScAppCardForCopilot` |
|
|
134
|
+
|
|
135
|
+
### Don't confuse with
|
|
136
|
+
|
|
137
|
+
| You may actually want | Not this |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `ProductWordmark` — the sidebar wordmark set, sized for the shell header | `ScAppcardLogos` is the 14px *appcard* set; different sizes, different Figma source |
|
|
140
|
+
| `ScStreamoidWordmark` — the company mark | This set has **no** Streamoid entry |
|
|
141
|
+
| `ScAppCard` — icon + name + subtitle row | This is only the name, as vector art |
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 4. Why to use it
|
|
146
|
+
|
|
147
|
+
- **One asset, every colour.** `fill="currentColor"` inline SVG means the mark
|
|
148
|
+
inherits hover, focus, theme and inverse-surface colours with no second asset and
|
|
149
|
+
no `filter: invert()` hack.
|
|
150
|
+
- **Inline, not `<img>`** — deliberately, per the source comment: an `<img>` cannot
|
|
151
|
+
pick up `currentColor`, so the wordmark would need a per-theme file.
|
|
152
|
+
- **No network request, no flash.** The SVG is compiled into the bundle, so the
|
|
153
|
+
mark is painted on first render (important in the agent's empty state, which is
|
|
154
|
+
the first thing a user sees).
|
|
155
|
+
- **The `AppcardProduct` union is the single source of truth** for "which products
|
|
156
|
+
have an appcard wordmark", and it is derived from the wordmark map itself — add a
|
|
157
|
+
product to `sources.ts` and every consumer's types widen automatically.
|
|
158
|
+
- **`role="img"` + a label** means assistive tech announces a product rather than
|
|
159
|
+
skipping an unlabelled decorative span.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Gotchas
|
|
164
|
+
|
|
165
|
+
**1. Only four products exist.** `product="streamoid"` is a type error, and there is
|
|
166
|
+
no CXO mark here.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
// WRONG — not in the union
|
|
170
|
+
<ScAppcardLogos product="streamoid" />
|
|
171
|
+
|
|
172
|
+
// RIGHT
|
|
173
|
+
<ScStreamoidWordmark />
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**2. `aria-label` is the raw key.** Screen readers say "photogenix", lowercase. If
|
|
177
|
+
the surrounding control needs a nicer name, label the control itself — you cannot
|
|
178
|
+
override the span's label (no props spread).
|
|
179
|
+
|
|
180
|
+
**3. Uneven widths.** Photogenix is 88px wide, Artifax 54px. A flex row of the four
|
|
181
|
+
marks will look ragged; add `justify-content` / fixed-width slots if the design
|
|
182
|
+
expects a grid. `ScAppCardForCopilot` inherits this — its `width: fit-content` cards
|
|
183
|
+
come out different widths per product.
|
|
184
|
+
|
|
185
|
+
**4. `size` does not exist.** Unlike `Sicon*`, there is no size prop. The inner SVG
|
|
186
|
+
carries hardcoded `width`/`height` attributes, so scaling requires CSS on the SVG
|
|
187
|
+
itself (`[&>svg]{height:100%;width:auto}` or similar), not just a height on the span.
|
|
188
|
+
|
|
189
|
+
**5. `style` beats `color`.** The wrapper's inline style is
|
|
190
|
+
`{ display: "inline-flex", alignItems: "center", color, ...style }` — a `color` inside
|
|
191
|
+
`style` silently wins over the `color` prop. Pick one.
|
|
192
|
+
|
|
193
|
+
**6. `APPCARD_WORDMARKS` is not exported.** Only `ScAppcardLogos` and the
|
|
194
|
+
`AppcardProduct` type are public. Do not try to import the raw SVG map from
|
|
195
|
+
`@streamoid/ui`; if you need the list of products, iterate a literal array typed as
|
|
196
|
+
`AppcardProduct[]`.
|
|
197
|
+
|
|
198
|
+
**7. Don't hand-edit `sources.ts`.** It is auto-generated from Figma and its four
|
|
199
|
+
wordmark lines each contain a very large inline SVG string — grep it, never open it
|
|
200
|
+
whole, and regenerate rather than patch.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## In the wild
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
// stream-agent frontend/src/components/chat/EmptyChat.tsx:228
|
|
208
|
+
<ScAppcardLogos product={p.product} className="shrink-0" />
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
No host render site found in cxo / photogenix / catalogix / artifax — the call site
|
|
212
|
+
above is the **agent runtime** (`used_by: [agent]`).
|
|
213
|
+
Inside the DS it is composed by `ScAppCardForCopilot`
|
|
214
|
+
(`packages/ui/src/SC-AppCardForCopilot/ScAppCardForCopilot.tsx:36`). If a host
|
|
215
|
+
dashboard ever needs it, the natural home is a product-switcher chip or an
|
|
216
|
+
empty-state "jump to product" row.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Related
|
|
221
|
+
|
|
222
|
+
- `ScAppCardForCopilot` — the dark glassy card built around this wordmark.
|
|
223
|
+
- `ProductWordmark` / `ProductCollapsedMark` — the sidebar-shell wordmark set.
|
|
224
|
+
- `ScStreamoidWordmark` / `ScStreamoidMascot` — company marks (both live in SC-Brand).
|
|
225
|
+
- `ScCxoCopilotLogo` / `ScStrLogo` — CXO and Streamoid logo marks.
|
|
226
|
+
- `@streamoid/icons` — `Sicon*` product/feature icons, when you want a glyph not a wordmark.
|