@streamoid/ui 0.6.16 → 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.
Files changed (131) hide show
  1. package/README.md +35 -18
  2. package/dist/docs/AGENTS.md +321 -0
  3. package/dist/docs/CreditWarningBanner.md +305 -0
  4. package/dist/docs/InvoiceHistoryMobile.md +222 -0
  5. package/dist/docs/ScAccess.md +259 -0
  6. package/dist/docs/ScAppCard.md +244 -0
  7. package/dist/docs/ScAppCardForCopilot.md +230 -0
  8. package/dist/docs/ScAppCardV3.md +273 -0
  9. package/dist/docs/ScAppField.md +308 -0
  10. package/dist/docs/ScAppListingCard.md +271 -0
  11. package/dist/docs/ScAppSwitchPanel.md +286 -0
  12. package/dist/docs/ScAppcardLogos.md +226 -0
  13. package/dist/docs/ScArtifaxInvite.md +262 -0
  14. package/dist/docs/ScArtifaxSidebar.md +330 -0
  15. package/dist/docs/ScAskAgentButton.md +307 -0
  16. package/dist/docs/ScBadges.md +261 -0
  17. package/dist/docs/ScBeacon.md +244 -0
  18. package/dist/docs/ScBillingHistoryHeader.md +210 -0
  19. package/dist/docs/ScBillingHistoryTableList.md +243 -0
  20. package/dist/docs/ScBillingLogsTableHeader.md +212 -0
  21. package/dist/docs/ScBillingLogsTableList.md +251 -0
  22. package/dist/docs/ScBriefCard.md +255 -0
  23. package/dist/docs/ScButton.md +251 -0
  24. package/dist/docs/ScCalendar.md +268 -0
  25. package/dist/docs/ScCalendarDateComps.md +264 -0
  26. package/dist/docs/ScCatalogixInvite.md +345 -0
  27. package/dist/docs/ScCatalogixSidebar.md +337 -0
  28. package/dist/docs/ScCatalogixStoreHeader.md +246 -0
  29. package/dist/docs/ScCatalogixStoreTableList.md +316 -0
  30. package/dist/docs/ScCheckField.md +233 -0
  31. package/dist/docs/ScCheckbox.md +272 -0
  32. package/dist/docs/ScCounter.md +235 -0
  33. package/dist/docs/ScCreditsUsageCard.md +247 -0
  34. package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
  35. package/dist/docs/ScDefaultCard.md +269 -0
  36. package/dist/docs/ScDp.md +245 -0
  37. package/dist/docs/ScDrawer.md +318 -0
  38. package/dist/docs/ScFieldButton.md +255 -0
  39. package/dist/docs/ScFileField.md +268 -0
  40. package/dist/docs/ScGoogleSignIn.md +250 -0
  41. package/dist/docs/ScGuide.md +278 -0
  42. package/dist/docs/ScHDivider.md +213 -0
  43. package/dist/docs/ScHeader.md +222 -0
  44. package/dist/docs/ScImageField.md +253 -0
  45. package/dist/docs/ScInChatList.md +277 -0
  46. package/dist/docs/ScInChatMessage.md +205 -0
  47. package/dist/docs/ScInfoPopup.md +248 -0
  48. package/dist/docs/ScIntialProfileCover.md +233 -0
  49. package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
  50. package/dist/docs/ScLogoUnit.md +232 -0
  51. package/dist/docs/ScMappingCard.md +241 -0
  52. package/dist/docs/ScMediaApproval.md +301 -0
  53. package/dist/docs/ScMediaSelect.md +310 -0
  54. package/dist/docs/ScMenuOptions.md +308 -0
  55. package/dist/docs/ScMobileBottomAction.md +252 -0
  56. package/dist/docs/ScMobileTopNav.md +279 -0
  57. package/dist/docs/ScModal.md +291 -0
  58. package/dist/docs/ScOnlyField.md +302 -0
  59. package/dist/docs/ScOnlyIcon.md +213 -0
  60. package/dist/docs/ScPagination.md +284 -0
  61. package/dist/docs/ScPairtext.md +287 -0
  62. package/dist/docs/ScPendingAction.md +238 -0
  63. package/dist/docs/ScPhtogenixInvite.md +275 -0
  64. package/dist/docs/ScPlanCard.md +302 -0
  65. package/dist/docs/ScPlanComparison.md +264 -0
  66. package/dist/docs/ScPlanDetailsCard.md +246 -0
  67. package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
  68. package/dist/docs/ScPopUpMenu.md +224 -0
  69. package/dist/docs/ScProfile.md +234 -0
  70. package/dist/docs/ScProfileImageUpdate.md +261 -0
  71. package/dist/docs/ScProfileOptions.md +245 -0
  72. package/dist/docs/ScProfilePopup.md +396 -0
  73. package/dist/docs/ScProfileSettingsComp.md +250 -0
  74. package/dist/docs/ScProfileV2Mobile.md +216 -0
  75. package/dist/docs/ScProgressBar.md +267 -0
  76. package/dist/docs/ScQuickPrompt.md +277 -0
  77. package/dist/docs/ScRadio.md +228 -0
  78. package/dist/docs/ScReferralCardMobile.md +226 -0
  79. package/dist/docs/ScReferralTableHeader.md +260 -0
  80. package/dist/docs/ScReferralTableList.md +293 -0
  81. package/dist/docs/ScRole.md +226 -0
  82. package/dist/docs/ScRoleMobile.md +199 -0
  83. package/dist/docs/ScSelect.md +270 -0
  84. package/dist/docs/ScSelection.md +256 -0
  85. package/dist/docs/ScSelectionList.md +272 -0
  86. package/dist/docs/ScSelectionPill.md +240 -0
  87. package/dist/docs/ScSelectionPillGroup.md +302 -0
  88. package/dist/docs/ScSettingsNav.md +212 -0
  89. package/dist/docs/ScSettingsTabComp.md +260 -0
  90. package/dist/docs/ScSideBarLogoUnit.md +340 -0
  91. package/dist/docs/ScSidebar.md +243 -0
  92. package/dist/docs/ScSidebarIcons.md +232 -0
  93. package/dist/docs/ScSidebarMenu.md +283 -0
  94. package/dist/docs/ScSidebarProfile.md +231 -0
  95. package/dist/docs/ScSidebarSwitchMenu.md +258 -0
  96. package/dist/docs/ScSlider.md +194 -0
  97. package/dist/docs/ScStoreCard.md +252 -0
  98. package/dist/docs/ScStrLogo.md +253 -0
  99. package/dist/docs/ScStreamoidWordmark.md +302 -0
  100. package/dist/docs/ScSubAgent.md +226 -0
  101. package/dist/docs/ScTabComp.md +308 -0
  102. package/dist/docs/ScTabField.md +258 -0
  103. package/dist/docs/ScTabSwitcher.md +307 -0
  104. package/dist/docs/ScTableHeader.md +261 -0
  105. package/dist/docs/ScTableList.md +301 -0
  106. package/dist/docs/ScTableListMobile.md +282 -0
  107. package/dist/docs/ScTabs.md +268 -0
  108. package/dist/docs/ScTaxonomyPill.md +263 -0
  109. package/dist/docs/ScTextArea.md +259 -0
  110. package/dist/docs/ScTextField.md +324 -0
  111. package/dist/docs/ScThinkingStepIcon.md +249 -0
  112. package/dist/docs/ScTodoList.md +288 -0
  113. package/dist/docs/ScToggleSwitch.md +229 -0
  114. package/dist/docs/ScUsageHistoryMobile.md +194 -0
  115. package/dist/docs/ScVDivider.md +215 -0
  116. package/dist/docs/ScValueMappingL1.md +256 -0
  117. package/dist/docs/ScVersion.md +251 -0
  118. package/dist/docs/ScWorkspace.md +233 -0
  119. package/dist/docs/ScWorkspaceCard.md +234 -0
  120. package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
  121. package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
  122. package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
  123. package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
  124. package/dist/docs/StreamoidSidebar.md +403 -0
  125. package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
  126. package/dist/docs/UsageHistoryMobile.md +235 -0
  127. package/dist/docs/components.json +4849 -0
  128. package/dist/index.css +43 -37
  129. package/dist/index.d.mts +10 -0
  130. package/dist/index.d.ts +10 -0
  131. package/package.json +3 -2
@@ -0,0 +1,340 @@
1
+ ---
2
+ component: ScSideBarLogoUnit
3
+ also_exports: [ProductWordmark, ProductCollapsedMark, hasCollapsedMark]
4
+ package: "@streamoid/ui"
5
+ category: sidebar
6
+ status: stable
7
+ renders: div > button[type="button"]
8
+ tags: [logo, wordmark, sidebar-header, app-switcher, switch-product, chevron, brand, product-mark]
9
+ related: [StreamoidSidebar, ScArtifaxSidebar, ScAppSwitchPanel, ScStreamoidWordmark, ScStreamoidMascot]
10
+ do_not_confuse_with: [ScLogoUnit, ScStreamoidWordmark, ScStreamoidMascot, ScStrLogo, ScAppcardLogos, ScCxoCopilotLogo]
11
+ used_by: [cxo, photogenix, catalogix, artifax]
12
+ ---
13
+
14
+ # ScSideBarLogoUnit
15
+
16
+ **The row at the top of every sidebar: the product wordmark plus the
17
+ double-arrow "switch product" pill, as one click target.** Expanded it is a 64px-tall
18
+ full-width row; collapsed it is a square product icon stacked above the same pill.
19
+ Clicking anywhere on it fires your handler — which is always "open the app switcher".
20
+
21
+ The folder also exports the branded marks it uses: `ProductWordmark`,
22
+ `ProductCollapsedMark` and `hasCollapsedMark`.
23
+
24
+ ## TL;DR for agents
25
+
26
+ - **Reach for it when:** you are filling `StreamoidSidebar`/`ScArtifaxSidebar`'s
27
+ `expandedLogo` / `collapsedLogo` slots.
28
+ - **Don't reach for it when:** you want a bare Streamoid wordmark in a page header
29
+ (→ `ScStreamoidWordmark`), the app-switch list itself (→ `ScAppSwitchPanel`), or
30
+ the pre-`StreamoidSidebar` logo part (→ `ScLogoUnit`, legacy).
31
+ - **Four things that will bite you:**
32
+ 1. `onClick` **and** `onSwitchClick` both fire on a single click. Wire exactly one,
33
+ or a functional `setState` toggle cancels itself out.
34
+ 2. `wordmark` overrides `product` in **both** states — pass a *different* node per
35
+ state, not one node for both.
36
+ 3. `product="CXO" | "Artifax" | "Photogenix"` with `state="collapsed"` renders
37
+ **nothing**. Only `Catalogix` and `Tactix` have collapsed marks.
38
+ 4. The wrapper adds `padding-bottom: var(--spacing-md)` in the expanded state;
39
+ hosts kill it with `style={{ paddingBottom: 0 }}`.
40
+
41
+ ---
42
+
43
+ ## 1. How to use it
44
+
45
+ ### Import
46
+
47
+ ```tsx
48
+ import {
49
+ ScSideBarLogoUnit,
50
+ ProductWordmark,
51
+ ProductCollapsedMark,
52
+ hasCollapsedMark,
53
+ type SidebarProduct,
54
+ } from "@streamoid/ui";
55
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
56
+ ```
57
+
58
+ ### Minimal usage
59
+
60
+ ```tsx
61
+ // Built-in branded mark
62
+ <ScSideBarLogoUnit product="Catalogix" onClick={() => setAppListOpen((v) => !v)} />
63
+
64
+ // Your own art (the common case — most products pass their own logo component)
65
+ <ScSideBarLogoUnit
66
+ wordmark={<ScStreamoidWordmark width={110} height={16} />}
67
+ onClick={() => setAppListOpen((v) => !v)}
68
+ />
69
+ ```
70
+
71
+ ### Props — `ScSideBarLogoUnit`
72
+
73
+ | Prop | Type | Default | Notes |
74
+ |---|---|---|---|
75
+ | `state` | `"expanded"` \| `"collapsed"` | `"expanded"` | Two different render branches, not a width change. |
76
+ | `wordmark` | `ReactNode` | – | The mark to render. **Overrides `product` in both states.** Expanded: the stacked wordmark; collapsed: the square icon. |
77
+ | `product` | `SidebarProduct` | – | Built-in branded mark. The five real keys are `"CXO"`, `"Catalogix"`, `"Artifax"`, `"Photogenix"`, `"Tactix"`. ⚠️ **`SidebarProduct` is not a literal union** — it is `keyof typeof WORDMARKS` over a `Record<string, Wordmark>`, so it widens to `string` and a typo compiles. Only used when `wordmark` is unset. |
78
+ | `onClick` | `() => void` | – | Whole-row click. |
79
+ | `onSwitchClick` | `() => void` | – | Back-compat alias. ⚠️ **Fires *alongside* `onClick`**, not instead of it. |
80
+ | `switchAriaLabel` | `string` | ⚠️ `"Switch product"` | The button's `aria-label`, only applied when interactive. Hardcoded English default. |
81
+ | `hideSwitch` | `boolean` | `false` | Drops the double-arrow pill. The design always shows it. |
82
+ | `hover` | `boolean \| undefined` | `undefined` | `undefined` → CSS `:hover` drives it. `true` → force the hover background on (use while the switch panel is open). `false` → suppress the row background via an inline style. |
83
+ | `disabled` | `boolean` | `false` | Sets the native `disabled` on the button and makes it non-interactive. |
84
+ | `className` | `string` | – | Appended on the **outer wrapper**, not the button. |
85
+ | `style` | `CSSProperties` | – | On the outer wrapper. |
86
+
87
+ **Interactivity rule:** `interactive = !disabled && (!!onClick || !!onSwitchClick)`.
88
+ With no handler you get a focusable `<button>` with `aria-disabled="true"`, no
89
+ `aria-label` and no click — still visually hoverable.
90
+
91
+ ### Props — `ProductWordmark` / `ProductCollapsedMark`
92
+
93
+ | Prop | Type | Default | Notes |
94
+ |---|---|---|---|
95
+ | `product` | `SidebarProduct` | — | **Required.** Unknown product → returns `null`. |
96
+ | `style` | `CSSProperties` | – | Merged over the base style (`display: block`, `color: var(--alias-text---icons-primary)`). |
97
+
98
+ Both render an `<svg role="img" aria-label={product}>` whose paths are
99
+ `fill="currentColor"`, so the mark follows the surrounding text colour and is
100
+ theme-aware.
101
+
102
+ | product | `ProductWordmark` (h × w) | `ProductCollapsedMark` |
103
+ |---|---|---|
104
+ | `CXO` | 32 × 56 | — (returns `null`) |
105
+ | `Catalogix` | 32 × 117 | 40 × 40 |
106
+ | `Artifax` | 32 × 90 | — (`null`) |
107
+ | `Photogenix` | 32 × 144 | — (`null`) |
108
+ | `Tactix` | 32 × 80 | 40 × 40 |
109
+
110
+ `hasCollapsedMark(product: SidebarProduct): boolean` — check before relying on
111
+ `ProductCollapsedMark` / `state="collapsed"` + `product`.
112
+
113
+ ### Recipes
114
+
115
+ ```tsx
116
+ // The standard pair, driven from one bit of host state (CXO / Photogenix / Artifax)
117
+ const [appListOpen, setAppListOpen] = useState(false);
118
+ const toggle = () => setAppListOpen((v) => !v);
119
+
120
+ <StreamoidSidebar
121
+ expandedLogo={
122
+ <ScSideBarLogoUnit
123
+ wordmark={<ScStreamoidWordmark width={110} height={16} />}
124
+ onClick={toggle}
125
+ switchAriaLabel="Switch product"
126
+ style={{ paddingBottom: 0 }} // sits flush with the divider below
127
+ />
128
+ }
129
+ collapsedLogo={
130
+ <ScSideBarLogoUnit
131
+ state="collapsed"
132
+ wordmark={<ScStreamoidMascot size={40} />}
133
+ onClick={toggle}
134
+ hover={appListOpen ? true : undefined} // stay lit while the panel is open
135
+ />
136
+ }
137
+ /* … */
138
+ />
139
+
140
+ // Built-in marks, guarding the collapsed one
141
+ <ScSideBarLogoUnit
142
+ state="collapsed"
143
+ {...(hasCollapsedMark(product)
144
+ ? { product }
145
+ : { wordmark: <MyOwnSquareMark /> })}
146
+ onClick={toggle}
147
+ />
148
+
149
+ // Using the marks standalone (e.g. Catalogix passes them in explicitly)
150
+ <ScSideBarLogoUnit state="expanded" wordmark={<ProductWordmark product="Catalogix" />} onClick={toggle} />
151
+ <ScSideBarLogoUnit state="collapsed" wordmark={<ProductCollapsedMark product="Catalogix" />} onClick={toggle} />
152
+
153
+ // Non-interactive brand row (no switcher in this app)
154
+ <ScSideBarLogoUnit product="Photogenix" hideSwitch />
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 2. Where to use it
160
+
161
+ - **`expandedLogo` / `collapsedLogo` of `StreamoidSidebar`** — CXO, Photogenix and
162
+ Catalogix all do exactly this.
163
+ - **`expandedLogo` / `collapsedLogo` of `ScArtifaxSidebar`** — same, wrapped in a
164
+ `ref`'d div so the host's portalled switch flyout can anchor to it.
165
+ - It is the **trigger** for `ScAppSwitchPanel`: this component renders the affordance,
166
+ `ScAppSwitchPanel` renders the list, and the sidebar (or the host) positions it.
167
+ - All four apps use it. It is part of the "universal core".
168
+
169
+ ---
170
+
171
+ ## 3. When to use it
172
+
173
+ ### Use it when
174
+
175
+ - A sidebar needs its product identity **plus** an app-switch affordance in one row.
176
+ - You want the hover choreography (row → `fill-neutral-neutral`, pill →
177
+ `fill-neutral-neutralplus`) to match the nav rows below it.
178
+ - You need the expanded and collapsed brand treatments to stay in sync.
179
+
180
+ ### Don't use it — reach for this instead
181
+
182
+ | Situation | Use instead |
183
+ |---|---|
184
+ | A Streamoid wordmark in a page header / auth screen | `ScStreamoidWordmark` (110×16 plain) or `ScStreamoidBrand` |
185
+ | The mascot / collapsed Streamoid glyph on its own | `ScStreamoidMascot` |
186
+ | Product art inside an app card | `ScAppcardLogos` |
187
+ | The CXO Copilot lockup | `ScCxoCopilotLogo` |
188
+ | The list of other products | `ScAppSwitchPanel` |
189
+ | One switchable product row (icon + name + description) | `ScSidebarSwitchMenu` |
190
+ | The pre-`StreamoidSidebar` logo part | `ScLogoUnit` — legacy, don't start there |
191
+ | A workspace (not product) switcher | `ScWorkspaceSwitchCard` / `StreamoidWorkspaceSwitcher` |
192
+
193
+ ### Don't confuse with
194
+
195
+ | You may actually want | Not this |
196
+ |---|---|
197
+ | `ScLogoUnit` — legacy sidebar logo part, props are only `state` + `className` | `ScSideBarLogoUnit` is the current one (note the capital **B**) |
198
+ | `ScStreamoidWordmark` — the plain 110×16 header wordmark | This is a whole interactive row with a switch pill |
199
+ | `ScStreamoidMascot` — the square glyph you'd *pass in* as `wordmark` | Not a replacement for this row |
200
+ | `ScAppSwitchPanel` — the panel this row opens | Different component, different folder |
201
+ | `ProductWordmark` (this folder, `currentColor` SVG paths, 32px tall) vs the `SC-AppSwitch` wordmark assets (CSS-mask data URIs) | Two separate wordmark sets that are **not** interchangeable |
202
+
203
+ ---
204
+
205
+ ## 4. Why to use it
206
+
207
+ - **One click target, correctly.** The design merged the wordmark and the switch
208
+ chevron into a single row; the pill inside is decorative (`<span>`, not a nested
209
+ button), so there is exactly one focus stop and no nested-interactive a11y bug.
210
+ - **Real `<button type="button">`** with `aria-label`, `disabled` and `aria-disabled`
211
+ wired — unlike `StreamoidSidebar`'s own profile row, which is a `div`.
212
+ - **Two-tone hover that survives light mode.** The row goes to
213
+ `--alias-fill-neutral-neutral` while the pill goes one step darker to
214
+ `--alias-fill-neutral-neutralplus`, so the pill stays visible against the new row
215
+ background in both themes. Getting this wrong is the classic "the chevron
216
+ disappears on hover in light mode" bug.
217
+ - **The `hover` prop exists for a real reason.** While the app-switch panel is open
218
+ the row must *stay* lit even though the pointer has moved into the panel.
219
+ `hover={true}` does that without you touching CSS.
220
+ - **`hover={false}`** lets you keep clicks but suppress the highlight (e.g. a
221
+ screenshot or a walkthrough overlay).
222
+ - **Branded marks are tokenised, not baked.** `ProductWordmark` paths use
223
+ `currentColor` over `--alias-text---icons-primary`, so the wordmark inverts with
224
+ the theme instead of shipping two PNGs.
225
+
226
+ ---
227
+
228
+ ## Gotchas
229
+
230
+ **1. `onClick` and `onSwitchClick` both fire, in that order.** `handleClick` calls
231
+ both. CXO's source carries an explicit comment about this: a double-fire cancels a
232
+ functional `setState` toggle out, so the panel never opens.
233
+
234
+ ```tsx
235
+ // WRONG — toggles twice, net zero
236
+ <ScSideBarLogoUnit onClick={toggle} onSwitchClick={toggle} />
237
+
238
+ // RIGHT — pick one; onClick is the idiomatic choice
239
+ <ScSideBarLogoUnit onClick={toggle} />
240
+ ```
241
+
242
+ **2. `wordmark` wins over `product` in *both* states.** The same node is used for the
243
+ expanded wordmark and the collapsed square, so reusing one node gives you a 117px-wide
244
+ wordmark inside a 56px rail. Always pass state-specific art.
245
+
246
+ ```tsx
247
+ // WRONG — the wide wordmark overflows the collapsed rail
248
+ <ScSideBarLogoUnit state="collapsed" wordmark={<ProductWordmark product="Catalogix" />} />
249
+
250
+ // RIGHT
251
+ <ScSideBarLogoUnit state="collapsed" wordmark={<ProductCollapsedMark product="Catalogix" />} />
252
+ ```
253
+
254
+ **3. Most products have no collapsed mark, and a typo'd `product` is not a type
255
+ error.** `COLLAPSED_MARKS` contains only `Catalogix` and `Tactix`.
256
+ `ProductCollapsedMark` returns `null` for the rest, so
257
+ `<ScSideBarLogoUnit state="collapsed" product="CXO" />` renders an empty (but still
258
+ clickable) button with just the pill. And because `SidebarProduct` resolves to
259
+ `string` (the tables are typed `Record<string, …>`), `product="catalogix"`
260
+ (lowercase) also compiles and renders nothing — the lookup is case-sensitive.
261
+ Guard with `hasCollapsedMark(product)`.
262
+
263
+ **4. The expanded wrapper has bottom padding you probably don't want.**
264
+ `.scSideBarLogoUnit { padding-bottom: var(--spacing-md) }`. CXO and Catalogix both
265
+ pass `style={{ paddingBottom: 0 }}` so the row sits flush against the switch card's
266
+ divider.
267
+
268
+ **5. Fixed geometry.** The expanded row is `height: 4rem` (64px) with asymmetric
269
+ padding (16px left, 12px right), and the wordmark slot is a fixed `2rem` tall
270
+ `inline-flex`. Nothing is `overflow: hidden`, so an over-tall logo visually overlaps
271
+ the neighbouring content rather than being clipped. Keep marks ≤32px tall.
272
+
273
+ **6. `hover={false}` doesn't stop the *pill* from reacting.** The inline
274
+ `backgroundColor: transparent` applies to the row only; the CSS rule
275
+ `.row:hover .switch` still swaps the pill to `neutralplus` on real mouse hover.
276
+
277
+ **7. `className` lands on the wrapper `div`, not the button.** Styling `.row`
278
+ requires a descendant selector (Photogenix does this with `.ph-sidebar-logo`).
279
+
280
+ **8. The accessible name can double up.** `ProductWordmark` renders
281
+ `<svg role="img" aria-label={product}>` *inside* a button that carries
282
+ `aria-label={switchAriaLabel}`. The button's label wins, but if you pass your own
283
+ `wordmark` with its own text, check what a screen reader actually announces.
284
+
285
+ **9. `switchAriaLabel` disappears when non-interactive.** With no `onClick` /
286
+ `onSwitchClick`, `aria-label` is `undefined` and `aria-disabled="true"` — but the
287
+ `<button>` is still in the tab order unless you also pass `disabled`.
288
+
289
+ **10. `hideSwitch` is a design deviation, not a mode.** The comment in the source is
290
+ explicit: "rare; the design always shows it".
291
+
292
+ **11. The two wordmark systems are different.** This folder's `wordmarks.tsx` holds
293
+ inline SVG **path** data (`currentColor`, theme-tinted). `SC-AppSwitch/wordmarks.ts`
294
+ holds base64/URI-encoded SVGs used as **CSS masks**, and those are **not exported**
295
+ from the package. Don't try to import `CATALOGIX` / `ARTIFAX` etc. from
296
+ `@streamoid/ui`.
297
+
298
+ ---
299
+
300
+ ## In the wild
301
+
302
+ ```tsx
303
+ // cxo-dashboard src/app/components/app-sidebar.tsx:75
304
+ <ScSideBarLogoUnit
305
+ wordmark={<ScStreamoidWordmark width={110} height={16} />}
306
+ // ScSideBarLogoUnit fires both onClick and onSwitchClick on a single
307
+ // click, so wire the toggle through exactly one prop to avoid a
308
+ // double-fire that cancels a functional setState toggle out.
309
+ onClick={onClick}
310
+ switchAriaLabel="Switch product"
311
+ // Drop the unit's bottom padding so it sits flush with the switch card's
312
+ // divider / the divider below it.
313
+ style={{ paddingBottom: 0 }}
314
+ />
315
+ ```
316
+
317
+ ```jsx
318
+ // catalogix/dashboard app/containers/LeftMenu/index.jsx:1031 (collapsed, built-in mark)
319
+ <ScSideBarLogoUnit
320
+ state="collapsed"
321
+ wordmark={<ProductCollapsedMark product="Catalogix" />}
322
+ onClick={() => setAppListOpen((v) => !v)}
323
+ switchAriaLabel="Switch app"
324
+ />
325
+ ```
326
+
327
+ Also: `photogenix_v2 dashboard/client/src/components/layout/Sidebar.tsx:745`
328
+ (one helper returning both states, `hover={appListOpen ? true : undefined}`) and
329
+ `artifax packages/shared/src/components/DashboardSidebar.tsx:660`.
330
+
331
+ ---
332
+
333
+ ## Related
334
+
335
+ - `StreamoidSidebar` / `ScArtifaxSidebar` — the two slots this fills.
336
+ - `ScAppSwitchPanel` — the list this row opens.
337
+ - `ScStreamoidWordmark` / `ScStreamoidMascot` (SC-Brand) — the nodes CXO passes as `wordmark`.
338
+ - `ScSidebarSwitchMenu` — a single switchable-product row (the older switcher vocabulary).
339
+ - `ScLogoUnit` — the legacy predecessor; don't start new work there.
340
+ - `@streamoid/icons` — `SiconDoubleArrow` is the pill glyph, rendered at 16px, strokeWidth 1.5.
@@ -0,0 +1,243 @@
1
+ ---
2
+ component: ScSidebar
3
+ package: "@streamoid/ui"
4
+ category: sidebar
5
+ status: legacy
6
+ renders: div
7
+ tags: [sidebar, rail, nav, legacy, deprecated, figma-export, expanded, collapsed, shell]
8
+ related: [StreamoidSidebar, ScCatalogixSidebar, ScArtifaxSidebar, ScSidebarMenu, ScSideBarLogoUnit]
9
+ do_not_confuse_with: [StreamoidSidebar, ScSidebarMenu, ScCatalogixSidebar, ScArtifaxSidebar, ScMobileTopNav]
10
+ ---
11
+
12
+ # ScSidebar
13
+
14
+ **Legacy. Superseded by `StreamoidSidebar` (`src/SC-Sidebar-new`). Do not use it in new
15
+ code.** It is a verbatim Figma export of the original CXO rail: every nav label,
16
+ section header, chat title and icon is **hardcoded in the JSX**. It accepts no items,
17
+ no config, no handlers — the only prop that changes anything is `state`.
18
+
19
+ ## TL;DR for agents
20
+
21
+ - **Reach for it when:** never, in product code. It exists so old imports keep
22
+ resolving, and as a pixel reference for the original design.
23
+ - **Reach for this instead:** `StreamoidSidebar` (data-driven: `config` + `iconMap` +
24
+ `topItems` + `profile` + `switchPanel`), or the branded `ScCatalogixSidebar` /
25
+ `ScArtifaxSidebar`.
26
+ - **Four things that will bite you:**
27
+ 1. The nav is **hardcoded**: "New chat", "Apps / Artifax / Photogenix / Catalogix",
28
+ "Management / Teams / Billing", "Recents / Chat 1 / Chat 2 / Chat 3 / View all",
29
+ "Live support". "Artifax" is always the active row. There is no items prop.
30
+ 2. `onToggle` is typed and accepted but **never called**. Dead prop.
31
+ 3. `...props` is destructured and then **never spread** — `onClick`, `style`,
32
+ `id`, `data-*` are all silently dropped.
33
+ 4. The root has a hardcoded `height: 62.5rem` (1000 px). It does not fill its parent.
34
+
35
+ ---
36
+
37
+ ## 1. How to use it
38
+
39
+ ### Import
40
+
41
+ ```tsx
42
+ import { ScSidebar } from "@streamoid/ui";
43
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
44
+ ```
45
+
46
+ ### Minimal usage
47
+
48
+ ```tsx
49
+ // Everything inside is fixed. This is the entire API surface.
50
+ <ScSidebar state="expanded" />
51
+ ```
52
+
53
+ ### Props
54
+
55
+ | Prop | Type | Default | Notes |
56
+ |---|---|---|---|
57
+ | `state` | `"expanded"` \| `"collapsed"` | `"expanded"` | The only functional prop. Picks between two completely separate hardcoded trees. |
58
+ | `className` | `string` | – | Concatenated onto the root. ⚠️ Unguarded — omitting it puts a literal `undefined` in the class list. |
59
+ | `onToggle` | `(state: "expanded" \| "collapsed") => void` | – | ⚠️ **Never invoked.** Declared, destructured, discarded. Nothing in the component calls it. |
60
+ | `...props` | `Omit<HTMLAttributes<HTMLDivElement>, "onToggle">` | – | ⚠️ **Not spread onto the root.** Typed as accepted, then dropped on the floor. |
61
+
62
+ ### What renders in each state
63
+
64
+ | Region | `expanded` (16 rem card) | `collapsed` (icon rail) |
65
+ |---|---|---|
66
+ | Header | `ScLogoUnit` (full Streamoid wordmark, 240 px wide) | `ScLogoUnit state="collapsed"` (wordmark squeezed to 32 × 12) |
67
+ | Top item | "New chat" + `SiconPlus` | same, icon-only |
68
+ | Section 1 | header "Apps " + Artifax (**active**) / Photogenix / Catalogix | same three icons, no header, `ScHDivider` above |
69
+ | Section 2 | header "Management " + Teams / Billing | same two icons |
70
+ | Section 3 | header "Recents " + Chat 1 / Chat 2 / Chat 3 / "View all" | same four icons |
71
+ | Pre-footer | "Live support" + `SiconSupport` | same, icon-only |
72
+ | Profile | `ScSidebarProfile` → renders `ScProfile` with **no props** → "Chris Hemsworth / Kepler workspace" and a broken `profile-image0.png` | same, in a 3 rem box |
73
+ | Footer | `ScVersion` → "v2.1.2" + a non-clickable collapse chevron | `ScVersion state="collapsed"` |
74
+
75
+ ### Recipes
76
+
77
+ There are none. There is nothing to configure. If you are here because you need a
78
+ sidebar, this is the replacement:
79
+
80
+ ```tsx
81
+ import { StreamoidSidebar, type SidebarConfig, type SidebarIconMap } from "@streamoid/ui";
82
+
83
+ <StreamoidSidebar
84
+ expanded={expanded}
85
+ onToggle={() => setExpanded((v) => !v)}
86
+ config={sidebarConfig} // sections + items, JSON-shaped
87
+ iconMap={iconMap} // iconKey -> node/renderer
88
+ topItems={[{ id: "new-chat", label: "New chat", iconKey: "plus" }]}
89
+ activeItemId={activeId}
90
+ onItemSelect={(item) => go(item.id)}
91
+ expandedLogo={<ScSideBarLogoUnit product="CXO" onClick={openSwitcher} />}
92
+ collapsedLogo={<ScSideBarLogoUnit state="collapsed" product="CXO" onClick={openSwitcher} />}
93
+ profile={{ name, subtitle, avatar: <img src={photoUrl} alt="" />, onClick: openProfilePopup }}
94
+ versionText="v2.1.2"
95
+ switchPanel={<ScAppSwitchPanel apps={apps} />}
96
+ switchPanelOpen={appListOpen}
97
+ onSwitchPanelClose={() => setAppListOpen(false)}
98
+ />
99
+ ```
100
+
101
+ ---
102
+
103
+ ## 2. Where to use it
104
+
105
+ Nowhere in production. Its only remaining value:
106
+
107
+ - **Design reference** — a single file that shows the original rail's spacing, divider
108
+ placement and section rhythm.
109
+ - **Import compatibility** — it is still exported from `src/index.ts`, so old code that
110
+ imports the name still compiles.
111
+
112
+ Every desktop app has converged on `StreamoidSidebar` + `ScAppSwitchPanel`: CXO
113
+ (`src/app/components/app-sidebar.tsx:965`), Photogenix
114
+ (`dashboard/client/src/components/layout/Sidebar.tsx:855`), Catalogix
115
+ (`app/containers/LeftMenu/index.jsx:991` — directly on `StreamoidSidebar`, *not*
116
+ via `ScCatalogixSidebar`), Artifax
117
+ (`packages/shared/src/components/DashboardSidebar.tsx:641`, via `ScArtifaxSidebar`).
118
+
119
+ ---
120
+
121
+ ## 3. When to use it
122
+
123
+ ### Use it when
124
+
125
+ - Never. If you think you have a case, you want `StreamoidSidebar`.
126
+
127
+ ### Don't use it — reach for this instead
128
+
129
+ | Situation | Use instead |
130
+ |---|---|
131
+ | Any real app sidebar | `StreamoidSidebar` (`SC-Sidebar-new`) |
132
+ | A Catalogix-branded sidebar with its default nav + credit card | `ScCatalogixSidebar` |
133
+ | An Artifax-branded sidebar | `ScArtifaxSidebar` |
134
+ | Just one nav row, composing the rail yourself | `ScSidebarMenu` |
135
+ | The logo + product-switch header row | `ScSideBarLogoUnit` |
136
+ | The app-switch list | `ScAppSwitchPanel` (+ `ScSidebarSwitchMenu` rows) |
137
+ | A mobile navigation surface | `ScMobileTopNav` / `ScMobileBottomAction` |
138
+ | A settings-page vertical nav | `ScSettingsNav` |
139
+
140
+ ### Don't confuse with
141
+
142
+ | You may actually want | Not this |
143
+ |---|---|
144
+ | `StreamoidSidebar` — the current, data-driven shell, exported from the same package | `ScSidebar` is the frozen predecessor with the same-ish name |
145
+ | `ScSidebarMenu` — one nav row, **stable and current** | `ScSidebar` is the whole legacy rail |
146
+ | `ScCatalogixSidebar` / `ScArtifaxSidebar` — branded shells built on `StreamoidSidebar` | not built on this |
147
+
148
+ Name collision warning: `ScSidebar` and `StreamoidSidebar` differ by one word and live
149
+ in the same barrel export. Autocomplete will offer you the wrong one.
150
+
151
+ ---
152
+
153
+ ## 4. Why to use it
154
+
155
+ You wouldn't. For completeness, what you *lose* by using it instead of
156
+ `StreamoidSidebar`: any nav data, active-item tracking, unread highlighting,
157
+ collapse/expand behaviour, the app-switch overlay, the assistant CTA, a real profile
158
+ row, the credit-warning slot, `bodyContent`, mobile behaviour, and every callback.
159
+
160
+ What it does still demonstrate correctly (expanded state): the `surface-base` card,
161
+ its `border-subtle` 1 px border, `radius-3xl` corners, `spacing-md` gutters, the
162
+ divider positions, and the 50 %-opacity `text-xs` section headers. (Collapsed draws no
163
+ card — just a 1 px `border-subtle` right edge.)
164
+
165
+ ---
166
+
167
+ ## Gotchas
168
+
169
+ **1. `onToggle` does nothing.** It type-checks, so this looks like it works:
170
+
171
+ ```tsx
172
+ // WRONG — the callback is never called; the sidebar cannot collapse itself
173
+ const [state, setState] = useState<"expanded" | "collapsed">("expanded");
174
+ <ScSidebar state={state} onToggle={setState} />
175
+
176
+ // RIGHT — the shell that actually reports toggles
177
+ <StreamoidSidebar expanded={expanded} onToggle={() => setExpanded((v) => !v)} … />
178
+ ```
179
+
180
+ **2. `...props` never reaches the DOM.** The root `<div>` receives only `className`.
181
+ So `onClick`, `style`, `id`, `ref`-adjacent attributes and `data-*` are dropped. You
182
+ cannot even size it from the outside via `style`.
183
+
184
+ **3. Hardcoded 1000 px height.** `.scSidebar { height: 62.5rem }`. In a real layout it
185
+ either overflows the viewport or leaves a gap; and since `style` is dropped (Gotcha 2)
186
+ you can only fix it with a `className` override.
187
+
188
+ **4. `className` produces a literal `undefined` class when omitted.**
189
+ `styles.scSidebar + " " + className + " " + variantsClassName`.
190
+
191
+ **5. It ships placeholder identity.** `ScSidebarProfile` is rendered with no props, so
192
+ the footer says **"Chris Hemsworth" / "Kepler workspace"** with a broken
193
+ `profile-image0.png`, and `ScVersion` prints **"v2.1.2"**. There is no way to pass real
194
+ values through.
195
+
196
+ **6. Every label is English and baked in.** No `labels`/`items`/`config` prop exists.
197
+ Localising or renaming a single nav item means editing the DS.
198
+
199
+ **7. "Artifax" is always `state="active"`.** Both trees hardcode it. No prop changes
200
+ the selection.
201
+
202
+ **8. The collapsed tree fights its own children.** `.scSidebar.state-collapsed
203
+ .scSidebarMenuInstance` unsets `display`, `padding`, `flex-direction`, `gap`,
204
+ `align-items`, `height` and `position` on the nested `ScSidebarMenu` — a Figma-export
205
+ artifact. Any change to `ScSidebarMenu`'s own CSS can break the collapsed rail here in
206
+ ways it won't break anywhere else.
207
+
208
+ **9. The collapse chevron is decorative.** It goes through `ScVersion` →
209
+ `ScSidebarIcons`, and `ScSidebarIcons` does not spread props, so nothing is clickable.
210
+
211
+ **10. Two different `ScSidebarIcons` shapes.** `expanded` passes `component={<ScSidebarIcons …/>}`
212
+ *and* `scSidebarIconsinstance`, while `collapsed` passes only
213
+ `scSidebarIconsinstance` — and `ScVersion` ignores **both** in its expanded branch.
214
+ Don't take this call site as a usage example for `ScVersion`.
215
+
216
+ ---
217
+
218
+ ## In the wild
219
+
220
+ _No host render site found — used by the agent runtime / composed internally._
221
+
222
+ More precisely: **no render site at all.** Nothing in `@streamoid/ui`, CXO, Catalogix,
223
+ Photogenix or Artifax renders `ScSidebar`; it is exported and unused
224
+ (`packages/ui/src/index.ts:35`). Where it *would* belong — the desktop app rail — is
225
+ now occupied by:
226
+
227
+ ```tsx
228
+ // cxo-dashboard src/app/components/app-sidebar.tsx:965 (the real rail)
229
+ <StreamoidSidebar expanded={sidebarExpanded} onToggle={onToggle} config={…} iconMap={iconMap} … />
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Related
235
+
236
+ - `StreamoidSidebar` (`SC-Sidebar-new`) — **the replacement.** Read
237
+ `src/SC-Sidebar-new/README.md`.
238
+ - `ScCatalogixSidebar` / `ScArtifaxSidebar` — branded shells over `StreamoidSidebar`.
239
+ - `ScSidebarMenu` — the one part of this old family that is still current.
240
+ - `ScSideBarLogoUnit` — replaces `ScLogoUnit` for the header row.
241
+ - `ScAppSwitchPanel` — replaces the inline app list.
242
+ - `ScLogoUnit` / `ScSidebarProfile` / `ScSidebarIcons` / `ScVersion` — the other
243
+ legacy parts this file composes; all superseded.