@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,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScSidebarIcons
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: sidebar
|
|
5
|
+
status: legacy
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [sidebar, icon, icon-button, hover, footer, collapse-toggle, legacy, wrapper]
|
|
8
|
+
related: [ScButton, ScVersion, StreamoidSidebar, ScSidebarMenu, ScOnlyIcon]
|
|
9
|
+
do_not_confuse_with: [ScOnlyIcon, ScSidebarMenu, ScAppcardLogos, ScButton]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScSidebarIcons
|
|
13
|
+
|
|
14
|
+
**Legacy. A 8 px-padded, `radius-3xl` box around one icon, with a hover background —
|
|
15
|
+
and nothing else.** It drops every prop except `instance`, `state` and `className`, so
|
|
16
|
+
it cannot be clicked, focused or labelled. Superseded by
|
|
17
|
+
`ScButton styleVariant="icon-only"` for a real icon affordance, and by
|
|
18
|
+
`StreamoidSidebar`'s built-in collapse toggle for its original job.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** never. It has no click path.
|
|
23
|
+
- **Reach for this instead:** `ScButton styleVariant="icon-only" icon={…}` (a real,
|
|
24
|
+
keyboard-accessible icon affordance), or `StreamoidSidebar`'s `toggleIcon` /
|
|
25
|
+
`toggleInProfile` for the rail's collapse control. Note `ScOnlyIcon` is **not** a
|
|
26
|
+
general-purpose alternative — it hardcodes `SiconSettings` and has no `icon` prop.
|
|
27
|
+
- **Three things that will bite you:**
|
|
28
|
+
1. ⚠️ **`...props` is destructured and never spread.** `onClick`, `aria-label`,
|
|
29
|
+
`data-*` are silently dropped. The interface doesn't even extend
|
|
30
|
+
`HTMLAttributes`, so TypeScript rejects `onClick` before you get that far.
|
|
31
|
+
2. `instance` defaults to `<SiconSupport />`. Forget it and you ship a headset icon.
|
|
32
|
+
3. It has a hover background but no `cursor: pointer`, no `role`, no `tabIndex`. It
|
|
33
|
+
looks like a button and is inert.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. How to use it
|
|
38
|
+
|
|
39
|
+
### Import
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { ScSidebarIcons, ScButton, StreamoidSidebar } from "@streamoid/ui";
|
|
43
|
+
import { SiconCollapse, SiconSupport } from "@streamoid/icons";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Minimal usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// The entire useful API. Note: no onClick — see Gotcha 1.
|
|
51
|
+
<ScSidebarIcons instance={<SiconCollapse />} />
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Props
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Notes |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `instance` | `JSX.Element` | `<SiconSupport />` | ⚠️ Has a real default. The single child; rendered verbatim (no `cloneElement`, no size or colour normalisation). |
|
|
59
|
+
| `state` | `"default"` \| `"hover"` | `"default"` | `"hover"` force-renders the `fill-neutral-neutral` background. Real `:hover` already does the same. |
|
|
60
|
+
| `className` | `string` | – | Concatenated onto the root. ⚠️ Unguarded — omitting it puts a literal `undefined` in the class list. |
|
|
61
|
+
| `...props` | – | – | ⚠️ **Destructured and discarded.** `IScSidebarIconsProps` does **not** extend `React.HTMLAttributes`, so there is no supported way to attach a handler or an aria attribute. |
|
|
62
|
+
|
|
63
|
+
The whole component is: `<div className="…">{instance}</div>` plus one hover rule.
|
|
64
|
+
|
|
65
|
+
### Recipes
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// You want a clickable icon → use ScButton in icon-only mode
|
|
69
|
+
<ScButton
|
|
70
|
+
styleVariant="icon-only"
|
|
71
|
+
icon={<SiconCollapse size={20} />}
|
|
72
|
+
variant="mono"
|
|
73
|
+
type="tertiary"
|
|
74
|
+
size="md"
|
|
75
|
+
onClick={toggleSidebar}
|
|
76
|
+
aria-label="Collapse sidebar"
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
// You want the rail's collapse control → StreamoidSidebar already renders it
|
|
80
|
+
<StreamoidSidebar
|
|
81
|
+
expanded={expanded}
|
|
82
|
+
onToggle={() => setExpanded((v) => !v)}
|
|
83
|
+
config={config}
|
|
84
|
+
iconMap={iconMap}
|
|
85
|
+
toggleIcon={(isExpanded) => <SiconCollapse style={{ transform: isExpanded ? undefined : "rotate(180deg)" }} />}
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
// You genuinely only want a padded, hover-tinted icon box (decorative)
|
|
89
|
+
<ScSidebarIcons instance={<SiconSupport />} />
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 2. Where to use it
|
|
95
|
+
|
|
96
|
+
Nowhere. It has exactly two composition sites, both inside the design system and both
|
|
97
|
+
in the legacy family:
|
|
98
|
+
|
|
99
|
+
- `src/SC-version/ScVersion.tsx:34` and `:49` — the collapse chevron in the old rail's
|
|
100
|
+
version footer.
|
|
101
|
+
- `src/SC-Sidebar/ScSidebar.tsx:250` — passed into `ScVersion`'s `component` slot.
|
|
102
|
+
|
|
103
|
+
The current rail's collapse control lives inside `StreamoidSidebar` (driven by
|
|
104
|
+
`onToggle` / `toggleIcon` / `toggleInProfile`) and is a real interactive element.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 3. When to use it
|
|
109
|
+
|
|
110
|
+
### Use it when
|
|
111
|
+
|
|
112
|
+
- Never in new code. If you catch yourself wanting it, you want a clickable icon.
|
|
113
|
+
|
|
114
|
+
### Don't use it — reach for this instead
|
|
115
|
+
|
|
116
|
+
| Situation | Use instead |
|
|
117
|
+
|---|---|
|
|
118
|
+
| A clickable bare icon affordance, any icon | `ScButton styleVariant="icon-only" icon={…}` |
|
|
119
|
+
| Specifically a settings-cog affordance | `ScOnlyIcon` — but be aware its icon is hardcoded to `SiconSettings` |
|
|
120
|
+
| The sidebar collapse/expand toggle | `StreamoidSidebar`'s `onToggle` + `toggleIcon` (or `toggleInProfile`) |
|
|
121
|
+
| An icon + label nav row in a rail | `ScSidebarMenu` |
|
|
122
|
+
| A trailing kebab on a nav row | `ScSidebarMenu`'s `moreIcon` + `onMoreClick` |
|
|
123
|
+
| A product/app logo mark | `ScAppcardLogos` / `ScSideBarLogoUnit` / `ScStreamoidMascot` |
|
|
124
|
+
|
|
125
|
+
### Don't confuse with
|
|
126
|
+
|
|
127
|
+
| You may actually want | Not this |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `ScOnlyIcon` — spreads its props (so `onClick` works) but hardcodes `SiconSettings` and exposes no `icon` prop | `ScSidebarIcons` takes any icon but is completely inert. Neither is a good general choice — use `ScButton styleVariant="icon-only"`. |
|
|
130
|
+
| `ScSidebarMenu` — the nav **row** (icon + label, five states) | this is just the icon chrome |
|
|
131
|
+
| `ScAppcardLogos` — branded product marks | this holds any icon, unstyled |
|
|
132
|
+
|
|
133
|
+
Note the plural: the folder is `SC-sidebar icons` and the export is `ScSidebarIcons`,
|
|
134
|
+
but it renders **one** icon. There is no multi-icon component behind that name.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 4. Why to use it
|
|
139
|
+
|
|
140
|
+
You wouldn't. What it still encodes correctly:
|
|
141
|
+
|
|
142
|
+
- The rail's icon-slot geometry: `spacing-md` (8 px) padding, `radius-3xl` corners, so
|
|
143
|
+
a footer icon lines up with the 40 px collapsed nav squares above it.
|
|
144
|
+
- The hover fill token is `--alias-fill-neutral-neutral` — the same one
|
|
145
|
+
`ScSidebarMenu`, `ScSidebarSwitchMenu` and `ScSidebarProfile` use, so hovers across
|
|
146
|
+
the rail match.
|
|
147
|
+
|
|
148
|
+
`ScButton styleVariant="icon-only"` gives you comparable geometry *plus* a click path,
|
|
149
|
+
keyboard handling and `aria-disabled` wiring.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Gotchas
|
|
154
|
+
|
|
155
|
+
**1. Props are silently dropped.** The component destructures `...props` and then never
|
|
156
|
+
uses it. The root div receives only `className`.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// WRONG — will not compile (no onClick on IScSidebarIconsProps) and would be
|
|
160
|
+
// dropped even if it did
|
|
161
|
+
<ScSidebarIcons instance={<SiconCollapse />} onClick={toggleSidebar} />
|
|
162
|
+
|
|
163
|
+
// RIGHT
|
|
164
|
+
<ScButton
|
|
165
|
+
styleVariant="icon-only"
|
|
166
|
+
icon={<SiconCollapse size={20} />}
|
|
167
|
+
variant="mono"
|
|
168
|
+
type="tertiary"
|
|
169
|
+
size="md"
|
|
170
|
+
onClick={toggleSidebar}
|
|
171
|
+
aria-label="Collapse sidebar"
|
|
172
|
+
/>
|
|
173
|
+
|
|
174
|
+
// WORKAROUND if you must keep this component: wrap it
|
|
175
|
+
<div role="button" tabIndex={0} onClick={toggleSidebar} aria-label="Collapse sidebar">
|
|
176
|
+
<ScSidebarIcons instance={<SiconCollapse />} />
|
|
177
|
+
</div>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**2. `instance` defaults to `<SiconSupport />`.** A live-support headset appears if you
|
|
181
|
+
forget it, or typo the prop name (it's `instance`, not `icon`).
|
|
182
|
+
|
|
183
|
+
**3. Interactive-looking, non-interactive.** There is a `:hover` background rule but no
|
|
184
|
+
`cursor: pointer`, no `role`, no `tabIndex`. Users will try to click it.
|
|
185
|
+
|
|
186
|
+
**4. No icon normalisation.** Unlike `ScButton`, your icon is rendered as-is — no
|
|
187
|
+
`cloneElement`, no `currentColor`, no `strokeWidth`. Set the icon's own `size` and
|
|
188
|
+
`color`, and size the box by wrapping or overriding via `className`.
|
|
189
|
+
|
|
190
|
+
**5. `className` produces a literal `undefined` class when omitted.**
|
|
191
|
+
`styles.scSidebarIcons + " " + className + " " + variantsClassName`.
|
|
192
|
+
|
|
193
|
+
**6. `state="hover"` is design parity only** — and `ScVersion`'s expanded branch passes
|
|
194
|
+
`state="hover"` unconditionally, so the old rail's collapse chevron is permanently
|
|
195
|
+
hover-skinned. Don't copy that.
|
|
196
|
+
|
|
197
|
+
**7. No intrinsic size.** The box is content-sized: `display: flex` + 8 px padding
|
|
198
|
+
around whatever `instance` measures. Two `ScSidebarIcons` with differently-sized icons
|
|
199
|
+
will be different sizes.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## In the wild
|
|
204
|
+
|
|
205
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
206
|
+
|
|
207
|
+
Composed only inside the legacy family:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
// npm-components packages/ui/src/SC-version/ScVersion.tsx:49
|
|
211
|
+
<ScSidebarIcons
|
|
212
|
+
instance={<SiconCollapse className={styles.siconCollapseInstance} />}
|
|
213
|
+
state="hover"
|
|
214
|
+
className={styles.scSidebarIconsInstance}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Where it *would* belong — an icon affordance in a sidebar footer — is now
|
|
219
|
+
`ScButton styleVariant="icon-only"`, or the collapse button `StreamoidSidebar` renders
|
|
220
|
+
itself.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Related
|
|
225
|
+
|
|
226
|
+
- `ScButton` (`styleVariant="icon-only"`) — the clickable replacement. Use this.
|
|
227
|
+
- `ScOnlyIcon` — spreads props but hardcodes `SiconSettings`; only for a settings cog.
|
|
228
|
+
- `StreamoidSidebar` — owns the collapse toggle via `onToggle` / `toggleIcon`.
|
|
229
|
+
- `ScVersion` — the legacy footer that composes this; also legacy.
|
|
230
|
+
- `ScSidebarMenu` — the current, stable rail row.
|
|
231
|
+
- `@streamoid/icons` — every `Sicon*`; check `packages/icons/ICONS.md` before drawing
|
|
232
|
+
a new one.
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScSidebarMenu
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: sidebar
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [sidebar, nav, menu-item, nav-row, rail, collapsed, active, more-icon, kebab]
|
|
8
|
+
related: [StreamoidSidebar, ScSidebarSwitchMenu, ScSettingsNav, ScMenuOptions, ScSideBarLogoUnit]
|
|
9
|
+
do_not_confuse_with: [ScSidebarSwitchMenu, ScSidebar, ScMenuOptions, ScSettingsTabComp]
|
|
10
|
+
used_by: [cxo, photogenix, catalogix, artifax]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScSidebarMenu
|
|
14
|
+
|
|
15
|
+
**One nav row in a sidebar rail.** Icon + label + an optional hover-only kebab, as a
|
|
16
|
+
40 px-high pill that has an `active` skin, two "sort of active" skins, and a
|
|
17
|
+
40 × 40 collapsed square. This is the only piece of the old à-la-carte sidebar family
|
|
18
|
+
that is still current — every desktop sidebar in the product is built out of it.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you are laying out sidebar nav rows yourself, or building a
|
|
23
|
+
vertical nav that isn't the app rail (e.g. a settings nav, a panel's section list).
|
|
24
|
+
- **Don't reach for it when:** you want a whole app sidebar — use `StreamoidSidebar`
|
|
25
|
+
(it renders these rows for you from `config` + `iconMap`), or the branded
|
|
26
|
+
`ScCatalogixSidebar` / `ScArtifaxSidebar`. Don't reach for `ScSidebar` (legacy,
|
|
27
|
+
hardcoded).
|
|
28
|
+
- **Four things that will bite you:**
|
|
29
|
+
1. It's a plain `<div>` with `cursor: pointer` — **no `role`, no `tabIndex`, no key
|
|
30
|
+
handling**. Keyboard and screen-reader users get nothing unless you wrap it.
|
|
31
|
+
2. `variant="collapsed"` **drops the label entirely** and adds no `title`. There is
|
|
32
|
+
no built-in tooltip — hosts add their own.
|
|
33
|
+
3. `icon` defaults to `<SiconHome />`. Forget it and you ship a house.
|
|
34
|
+
4. `moreIcon` is `display: none` until `:hover`. It is unreachable on touch.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. How to use it
|
|
39
|
+
|
|
40
|
+
### Import
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { ScSidebarMenu } from "@streamoid/ui";
|
|
44
|
+
import { SiconDot } from "@streamoid/icons";
|
|
45
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Minimal usage
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<ScSidebarMenu
|
|
52
|
+
icon={<SiconDot size={24} />}
|
|
53
|
+
text="Insights"
|
|
54
|
+
state={isActive ? "active" : "default"}
|
|
55
|
+
variant={expanded ? "expanded" : "collapsed"}
|
|
56
|
+
onClick={() => go("insights")}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Props
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Notes |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `icon` | `JSX.Element` | `<SiconHome />` | ⚠️ Has a real default. Always rendered, in both variants. Clamped to 20 × 20 by CSS (`.scSidebarMenu > :first-child`) whatever size you pass the icon. |
|
|
65
|
+
| `text` | `string` | `"Menu Item"` | ⚠️ Has a real default. **Only rendered when `variant="expanded"`.** Single line, `text-overflow: ellipsis`. |
|
|
66
|
+
| `state` | `"default"` \| `"active"` \| `"hover"` \| `"default-highlight"` \| `"default-active"` | `"default"` | See the state table below. |
|
|
67
|
+
| `variant` | `"expanded"` \| `"collapsed"` | `"expanded"` | `collapsed` = fixed 40 × 40 square, icon centred, label and kebab omitted. |
|
|
68
|
+
| `moreIcon` | `ReactNode` | – | Trailing kebab/overflow affordance. **`expanded` only**, and hidden until the row is hovered. |
|
|
69
|
+
| `onMoreClick` | `(e: React.MouseEvent) => void` | – | Fires from the `moreIcon` wrapper; the wrapper calls `stopPropagation()` first, so the row's `onClick` does **not** also fire. |
|
|
70
|
+
| `className` | `string` | – | Appended after the internal class. |
|
|
71
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root div — this is how `onClick`, `style`, `aria-*`, `data-*`, `key` reach the DOM. |
|
|
72
|
+
|
|
73
|
+
### What each `state` renders
|
|
74
|
+
|
|
75
|
+
| `state` | Background | Label colour | Used for |
|
|
76
|
+
|---|---|---|---|
|
|
77
|
+
| `default` | none (hover → `fill-neutral-neutral`) | tertiary | an ordinary row |
|
|
78
|
+
| `active` | `fill-neutral-neutralselected` | primary | the currently open item |
|
|
79
|
+
| `hover` | `fill-neutral-neutral` (forced) | tertiary | Figma parity / screenshots. Also force-reveals `moreIcon`. |
|
|
80
|
+
| `default-highlight` | none | primary | "has unread / needs attention" — brighter text, no fill |
|
|
81
|
+
| `default-active` | `surface-canvas` + a 0.5 px inset ring | primary | this section is the active one but the real selection is a child item |
|
|
82
|
+
|
|
83
|
+
### Recipes
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// The standard host loop — expanded/collapsed off one boolean
|
|
87
|
+
{items.map((item) => (
|
|
88
|
+
<ScSidebarMenu
|
|
89
|
+
key={item.id}
|
|
90
|
+
icon={iconMap[item.iconKey]}
|
|
91
|
+
text={expanded ? item.label : undefined} // collapsed hides it anyway
|
|
92
|
+
state={item.id === activeId ? "active" : "default"}
|
|
93
|
+
variant={expanded ? "expanded" : "collapsed"}
|
|
94
|
+
onClick={() => onItemSelect(item)}
|
|
95
|
+
/>
|
|
96
|
+
))}
|
|
97
|
+
|
|
98
|
+
// A row with an overflow menu (rename / delete on a chat thread)
|
|
99
|
+
<ScSidebarMenu
|
|
100
|
+
icon={<SiconDot size={24} />}
|
|
101
|
+
text={conversation.title}
|
|
102
|
+
state={unread ? "default-highlight" : "default"}
|
|
103
|
+
variant="expanded"
|
|
104
|
+
moreIcon={<SiconMore size={20} color="var(--alias-text---icons-tertiary)" />}
|
|
105
|
+
onMoreClick={() => setMenuOpenFor(conversation.id)}
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
// Collapsed rail + your own tooltip (the component supplies none)
|
|
109
|
+
<Tooltip.Root delayDuration={120}>
|
|
110
|
+
<Tooltip.Trigger asChild>
|
|
111
|
+
<div style={{ display: "inline-flex" }}>
|
|
112
|
+
<ScSidebarMenu icon={<SiconBilling />} variant="collapsed" onClick={goBilling} />
|
|
113
|
+
</div>
|
|
114
|
+
</Tooltip.Trigger>
|
|
115
|
+
<Tooltip.Content side="right">Billing</Tooltip.Content>
|
|
116
|
+
</Tooltip.Root>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 2. Where to use it
|
|
122
|
+
|
|
123
|
+
- **Inside `StreamoidSidebar`** — it already renders one per `config` item and per
|
|
124
|
+
`topItems` entry; you normally never call it directly there. `ScCatalogixSidebar`
|
|
125
|
+
and `ScArtifaxSidebar` do the same.
|
|
126
|
+
- **Ad-hoc nav lists a host owns** — CXO's recents/chat list and Catalogix's
|
|
127
|
+
workspace-service list are rendered by the host as `bodyContent` and pass
|
|
128
|
+
`ScSidebarMenu` rows through.
|
|
129
|
+
- **`ScSettingsNav`** composes three of them (Profile / Workspace / Referral).
|
|
130
|
+
- **Any vertical nav that should match the rail** — a panel's section list, a drawer's
|
|
131
|
+
navigation column.
|
|
132
|
+
|
|
133
|
+
Direct host call sites exist in CXO, Catalogix and Photogenix. Artifax gets it
|
|
134
|
+
indirectly through `ScArtifaxSidebar`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 3. When to use it
|
|
139
|
+
|
|
140
|
+
### Use it when
|
|
141
|
+
|
|
142
|
+
- You need a **single nav row** and you are composing the rail yourself.
|
|
143
|
+
- You want the expanded/collapsed geometry, the five state skins and the hover-kebab
|
|
144
|
+
behaviour to match the shared sidebar exactly.
|
|
145
|
+
|
|
146
|
+
### Don't use it — reach for this instead
|
|
147
|
+
|
|
148
|
+
| Situation | Use instead |
|
|
149
|
+
|---|---|
|
|
150
|
+
| You want the whole sidebar (logo, sections, profile, collapse toggle, app switcher) | `StreamoidSidebar` |
|
|
151
|
+
| The whole sidebar, pre-branded for Catalogix / Artifax | `ScCatalogixSidebar` / `ScArtifaxSidebar` |
|
|
152
|
+
| A row that also needs a **secondary description line** (app-switch list) | `ScSidebarSwitchMenu` |
|
|
153
|
+
| A row inside a floating popup/dropdown menu | `ScMenuOptions` |
|
|
154
|
+
| A horizontal tab strip | `ScTabs` / `ScTabSwitcher` / `ScSettingsTabComp` |
|
|
155
|
+
| The sidebar's logo/product-switch header row | `ScSideBarLogoUnit` |
|
|
156
|
+
| A mobile navigation surface | `ScMobileTopNav` / `ScMobileBottomAction` |
|
|
157
|
+
|
|
158
|
+
### Don't confuse with
|
|
159
|
+
|
|
160
|
+
| You may actually want | Not this |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `ScSidebarSwitchMenu` — same row shape but with `text` **and** `description`, square hover fill, used for the app-switch list | `ScSidebarMenu` is one line only |
|
|
163
|
+
| `ScSidebar` — the **legacy** whole-sidebar mock with hardcoded nav | This is the row primitive, and it is current |
|
|
164
|
+
| `ScMenuOptions` — a popup menu row (has `version` and `error` variants) | Different surface entirely |
|
|
165
|
+
| `StreamoidSidebar` — the current shell that renders these rows | Reach for the shell first |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 4. Why to use it
|
|
170
|
+
|
|
171
|
+
- **The five states are the product's whole nav vocabulary.** `active`,
|
|
172
|
+
`default-highlight` (unread) and `default-active` (section active, child selected)
|
|
173
|
+
are three genuinely different visual answers that hosts got wrong by hand; they are
|
|
174
|
+
one prop here.
|
|
175
|
+
- **Expanded ↔ collapsed geometry is already right.** Collapsed is a fixed 40 × 40
|
|
176
|
+
square with the icon optically centred, and the icon is clamped to 20 px regardless
|
|
177
|
+
of the `size` you pass — so a rail of mixed icons never jitters.
|
|
178
|
+
- **Token-driven.** Fills come from `--alias-fill-neutral-*`, text from
|
|
179
|
+
`--alias-text-and-icons-*`, so light mode works with no conditionals.
|
|
180
|
+
- **One place to change.** `StreamoidSidebar`, `ScCatalogixSidebar`,
|
|
181
|
+
`ScArtifaxSidebar` and `ScSettingsNav` all render this row, so a shape change lands
|
|
182
|
+
in every app's nav at once.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Gotchas
|
|
187
|
+
|
|
188
|
+
**1. No a11y wiring at all.** The root is `<div className=… onClick=…>` — no
|
|
189
|
+
`role="button"`, no `tabIndex`, no `onKeyDown`, no `aria-current`. It is not
|
|
190
|
+
reachable by keyboard.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// WRONG — mouse-only nav
|
|
194
|
+
<ScSidebarMenu text="Billing" onClick={goBilling} />
|
|
195
|
+
|
|
196
|
+
// RIGHT — supply the semantics yourself until the DS does
|
|
197
|
+
<ScSidebarMenu
|
|
198
|
+
text="Billing"
|
|
199
|
+
onClick={goBilling}
|
|
200
|
+
role="link"
|
|
201
|
+
tabIndex={0}
|
|
202
|
+
aria-current={isActive ? "page" : undefined}
|
|
203
|
+
onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && goBilling()}
|
|
204
|
+
/>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**2. Collapsed rows have no label and no tooltip.** `variant="collapsed"` renders the
|
|
208
|
+
icon only; there is no `title` attribute. Every host wraps it in its own tooltip
|
|
209
|
+
(CXO uses Radix). Do the same or your collapsed rail is unlabelled.
|
|
210
|
+
|
|
211
|
+
**3. `icon` defaults to `<SiconHome />` and `text` to `"Menu Item"`.** Both are real
|
|
212
|
+
defaults — a mistyped prop name gives you a house labelled "Menu Item" rather than an
|
|
213
|
+
error. Note that hosts idiomatically pass `text={expanded ? label : undefined}`, which
|
|
214
|
+
*does* fall back to `"Menu Item"` — harmless only because collapsed never renders it.
|
|
215
|
+
|
|
216
|
+
**4. There is no "no icon" row.** `icon` is typed `JSX.Element`, and omitting it (or
|
|
217
|
+
passing `undefined`) falls back to `<SiconHome />` — there is no supported way to
|
|
218
|
+
render a label-only row. If you force a falsy icon past the type, the label becomes the
|
|
219
|
+
first child and the clamp rule `.scSidebarMenu > :first-child { width: 1.25rem;
|
|
220
|
+
height: 1.25rem }` applies to your text instead.
|
|
221
|
+
|
|
222
|
+
**5. `moreIcon` is hover-only.** `.moreIconWrapper` is `display: none` and only
|
|
223
|
+
becomes `flex` on `:hover` (or `state="hover"`). There is no focus-visible reveal, so
|
|
224
|
+
it cannot be reached by keyboard or on a touch device.
|
|
225
|
+
|
|
226
|
+
**6. `onMoreClick` swallows the row click.** The wrapper calls `stopPropagation()`, so
|
|
227
|
+
clicking the kebab does not also fire the row's `onClick`. That's usually what you
|
|
228
|
+
want — just don't wire the same handler to both.
|
|
229
|
+
|
|
230
|
+
**7. `moreIcon` is ignored when collapsed.** It lives inside the
|
|
231
|
+
`variant === "expanded"` branch. So does `text`.
|
|
232
|
+
|
|
233
|
+
**8. `className` is concatenated unguarded.** When you omit `className` the rendered
|
|
234
|
+
class list literally contains `undefined`
|
|
235
|
+
(`ScSidebarMenu_scSidebarMenu undefined ScSidebarMenu_state-default …`). Harmless in
|
|
236
|
+
the browser, but it will show up in DOM snapshots and class-name assertions.
|
|
237
|
+
|
|
238
|
+
**9. Your icon's colour is *not* normalised.** Unlike `ScButton`, this component does
|
|
239
|
+
not `cloneElement` your icon — it renders it as-is. Only the label colour tracks
|
|
240
|
+
`state`; you must colour the icon yourself (every host does:
|
|
241
|
+
`color={active ? "var(--alias-text---icons-primary)" : "var(--alias-text---icons-muted)"}`).
|
|
242
|
+
|
|
243
|
+
**10. `state="hover"` is for design parity.** Real `:hover` already works. Don't wire
|
|
244
|
+
it to mouse handlers.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## In the wild
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
// cxo-dashboard src/app/components/app-sidebar.tsx:773
|
|
252
|
+
<ScSidebarMenu
|
|
253
|
+
icon={<SiconDot size={24} color={iconColor} />}
|
|
254
|
+
text={conversation.title}
|
|
255
|
+
state={menuState}
|
|
256
|
+
variant="expanded"
|
|
257
|
+
onClick={() => handleOpenConversation(conversation.id)}
|
|
258
|
+
moreIcon={
|
|
259
|
+
<SiconMore size={20} color="var(--alias-text---icons-tertiary)" />
|
|
260
|
+
}
|
|
261
|
+
onMoreClick={() =>
|
|
262
|
+
setMenuOpenFor(menuOpenFor === conversation.id ? null : conversation.id)
|
|
263
|
+
}
|
|
264
|
+
/>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Also: `catalogix/dashboard app/containers/LeftMenu/index.jsx:815`,
|
|
268
|
+
`photogenix_v2/dashboard/client/src/components/layout/Sidebar.tsx:840`,
|
|
269
|
+
and internally at
|
|
270
|
+
`packages/ui/src/SC-Sidebar-new/streamoid-sidebar.tsx:173`.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Related
|
|
275
|
+
|
|
276
|
+
- `StreamoidSidebar` (`SC-Sidebar-new`) — the current shell; renders these rows from
|
|
277
|
+
`config` + `iconMap`. Start here.
|
|
278
|
+
- `ScSidebarSwitchMenu` — two-line sibling for the app-switch list.
|
|
279
|
+
- `ScSideBarLogoUnit` — the rail's logo + product-switch header row.
|
|
280
|
+
- `ScSettingsNav` — a small stack of three of these rows.
|
|
281
|
+
- `ScSidebar` / `ScLogoUnit` / `ScSidebarProfile` / `ScSidebarIcons` / `ScVersion` —
|
|
282
|
+
the **legacy** parts of this family; all superseded by `StreamoidSidebar`.
|
|
283
|
+
- `ScMenuOptions` — the popup-menu row, not a rail row.
|