@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.
- 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 +43 -37
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScSidebarProfile
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: sidebar
|
|
5
|
+
status: legacy
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [sidebar, profile, footer, avatar, user, legacy, placeholder]
|
|
8
|
+
related: [ScProfile, StreamoidSidebar, ScProfilePopup, ScProfileOptions, ScDp]
|
|
9
|
+
do_not_confuse_with: [ScProfile, ScProfileOptions, ScProfilePopup, ScProfileSettingsComp, ScProfileV2Mobile, ScIntialProfileCover]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScSidebarProfile
|
|
13
|
+
|
|
14
|
+
**Legacy. Superseded by `StreamoidSidebar`'s `profile` prop.** A hover-able padded
|
|
15
|
+
wrapper whose only job was to place a `ScProfile` in the old sidebar footer — and it
|
|
16
|
+
renders that `ScProfile` **with no props at all**, so it always shows
|
|
17
|
+
`"Chris Hemsworth" / "Kepler workspace"` and a broken `profile-image0.png`.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** never. There is no way to pass a real user through it.
|
|
22
|
+
- **Reach for this instead:** `StreamoidSidebar`'s
|
|
23
|
+
`profile={{ name, subtitle, avatar, onClick }}` for a rail footer, or `ScProfile`
|
|
24
|
+
directly if you just want the avatar + two-line identity block.
|
|
25
|
+
- **Three things that will bite you:**
|
|
26
|
+
1. ⚠️ **There is no `name`, `subText` or `profileImage` prop.** The placeholder
|
|
27
|
+
identity is unavoidable.
|
|
28
|
+
2. `variant="collapsed"` does **not** collapse the inner `ScProfile` — the name and
|
|
29
|
+
subtitle still render, inside a 3 rem-wide box.
|
|
30
|
+
3. The root is a `<div>` with a hover background but no `role`/`tabIndex` — it looks
|
|
31
|
+
clickable and isn't, unless you wire it yourself.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
### Import
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ScSidebarProfile } from "@streamoid/ui";
|
|
41
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Minimal usage
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
// This is the whole API. The content is fixed.
|
|
48
|
+
<ScSidebarProfile variant="expanded" onClick={openProfilePopup} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Props
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Default | Notes |
|
|
54
|
+
|---|---|---|---|
|
|
55
|
+
| `state` | `"default"` \| `"hover"` | `"default"` | `"hover"` force-renders the `fill-neutral-neutral` background (Figma parity). Real `:hover` already does this. |
|
|
56
|
+
| `variant` | `"expanded"` \| `"collapsed"` | `"expanded"` | `expanded` = `align-self: stretch`, 8 / 12 px padding. `collapsed` = fixed `width: 3rem`, 8 px padding, and grows to `height: 3rem` on hover. |
|
|
57
|
+
| `className` | `string` | – | Concatenated onto the root. ⚠️ Unguarded — omitting it puts a literal `undefined` in the class list. |
|
|
58
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | **Is** spread onto the root, so `onClick`, `style`, `aria-*`, `data-*` work. |
|
|
59
|
+
|
|
60
|
+
There is deliberately **nothing else**. The child is a hardcoded
|
|
61
|
+
`<ScProfile className={styles.scProfileInstance} />`.
|
|
62
|
+
|
|
63
|
+
### What renders in each variant
|
|
64
|
+
|
|
65
|
+
| | `expanded` | `collapsed` |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| Box | stretches to the rail width, `radius-3xl` | fixed 48 px wide |
|
|
68
|
+
| `ScProfile` | avatar + name + subText | avatar + **name + subText still rendered**, squeezed into 48 px |
|
|
69
|
+
| Hover | `fill-neutral-neutral` | `fill-neutral-neutral` + `height: 3rem` |
|
|
70
|
+
|
|
71
|
+
### Recipes
|
|
72
|
+
|
|
73
|
+
None worth having. Here is what replaces it:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// A rail footer with a real user — StreamoidSidebar owns the profile row
|
|
77
|
+
<StreamoidSidebar
|
|
78
|
+
expanded={expanded}
|
|
79
|
+
onToggle={() => setExpanded((v) => !v)}
|
|
80
|
+
config={config}
|
|
81
|
+
iconMap={iconMap}
|
|
82
|
+
profile={{
|
|
83
|
+
name: user.fullName,
|
|
84
|
+
subtitle: workspace.name,
|
|
85
|
+
avatar: <img src={user.photoUrl} alt="" />,
|
|
86
|
+
onClick: openProfilePopup,
|
|
87
|
+
}}
|
|
88
|
+
toggleInProfile // profile + a separate collapse button, no version footer
|
|
89
|
+
/>
|
|
90
|
+
|
|
91
|
+
// Or, if you only want the identity block itself
|
|
92
|
+
<ScProfile
|
|
93
|
+
name={user.fullName}
|
|
94
|
+
subText={workspace.name}
|
|
95
|
+
profileImage={user.photoUrl}
|
|
96
|
+
collapsed={expanded ? "false" : "true"}
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 2. Where to use it
|
|
103
|
+
|
|
104
|
+
Nowhere. Its only render site in the whole codebase is inside the legacy `ScSidebar`
|
|
105
|
+
(`src/SC-Sidebar/ScSidebar.tsx:134` and `:243`), which is itself unused.
|
|
106
|
+
|
|
107
|
+
The current rail footer is built by `StreamoidSidebar` from its `profile` prop
|
|
108
|
+
(optionally with `toggleInProfile` to put the collapse button beside it), and by
|
|
109
|
+
`ScCatalogixSidebar` / `ScArtifaxSidebar` from their own `profile` configs.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 3. When to use it
|
|
114
|
+
|
|
115
|
+
### Use it when
|
|
116
|
+
|
|
117
|
+
- Never.
|
|
118
|
+
|
|
119
|
+
### Don't use it — reach for this instead
|
|
120
|
+
|
|
121
|
+
| Situation | Use instead |
|
|
122
|
+
|---|---|
|
|
123
|
+
| The profile row in a real sidebar footer | `StreamoidSidebar`'s `profile` prop (or `ScCatalogixSidebar` / `ScArtifaxSidebar`) |
|
|
124
|
+
| Just the avatar + name + subtitle block, with real data | `ScProfile` (`name`, `subText`, `profileImage`, `collapsed`) |
|
|
125
|
+
| Just the avatar | `ScDp` |
|
|
126
|
+
| The popup that opens when you click the footer | `ScProfilePopup` |
|
|
127
|
+
| The menu rows inside that popup | `ScProfileOptions` |
|
|
128
|
+
| The settings-page profile form | `ScProfileSettingsComp` |
|
|
129
|
+
| A mobile profile screen | `ScProfileV2Mobile` |
|
|
130
|
+
| The initials/cover block for a user with no photo | `ScIntialProfileCover` |
|
|
131
|
+
|
|
132
|
+
### Don't confuse with
|
|
133
|
+
|
|
134
|
+
| You may actually want | Not this |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `ScProfile` — the actual identity block, **with** `name` / `subText` / `profileImage` / `collapsed` props | `ScSidebarProfile` is a props-less wrapper around it |
|
|
137
|
+
| `ScProfileOptions` — the list of actions (Settings, Theme, Log out) inside the profile popup | not a footer row |
|
|
138
|
+
| `ScProfilePopup` — the whole floating popup | not a footer row |
|
|
139
|
+
| `ScProfileSettingsComp` — the settings-page profile editor | different surface |
|
|
140
|
+
| `ScProfileV2Mobile` — the mobile twin | different surface |
|
|
141
|
+
| `ScIntialProfileCover` — initials avatar / cover | different job |
|
|
142
|
+
|
|
143
|
+
Six profile-ish exports (`ScProfile`, `ScProfileOptions`, `ScProfilePopup`,
|
|
144
|
+
`ScProfileSettingsComp`, `ScProfileImageUpdate`, `ScProfileV2Mobile`) plus this
|
|
145
|
+
`ScSidebarProfile` — and this one is the only one you should never use. Read the table
|
|
146
|
+
before picking.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 4. Why to use it
|
|
151
|
+
|
|
152
|
+
You wouldn't. What it demonstrates that's still correct:
|
|
153
|
+
|
|
154
|
+
- The rail footer's hover affordance is a **background fill**
|
|
155
|
+
(`--alias-fill-neutral-neutral`), not a border or an opacity change.
|
|
156
|
+
- Collapsed, the row is pinned to `width: 3rem` (48 px) with its height left to the
|
|
157
|
+
content, and `:hover` adds an explicit `height: 3rem` so the hovered box reads as a
|
|
158
|
+
48 × 48 square like the collapsed nav squares.
|
|
159
|
+
- The 8 / 12 px padding + `radius-3xl` matches `ScSidebarMenu`, so the footer sits on
|
|
160
|
+
the same rhythm as the nav rows above it.
|
|
161
|
+
|
|
162
|
+
Everything else — real identity, click target, popup wiring, keyboard access — you
|
|
163
|
+
have to supply, and `StreamoidSidebar` already does.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Gotchas
|
|
168
|
+
|
|
169
|
+
**1. The user is hardcoded, two levels down.** `ScSidebarProfile` renders
|
|
170
|
+
`<ScProfile />` with only a `className`. `ScProfile`'s own defaults then take over:
|
|
171
|
+
`name = "Chris Hemsworth"`, `subText = "Kepler workspace"`,
|
|
172
|
+
`profileImage = "profile-image0.png"`. That last one is a bare relative path, so it
|
|
173
|
+
404s in every host and you get a broken-image glyph.
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
// WRONG — there is no such prop; you still get "Chris Hemsworth"
|
|
177
|
+
<ScSidebarProfile name={user.fullName} profileImage={user.photoUrl} />
|
|
178
|
+
|
|
179
|
+
// RIGHT — use ScProfile directly, or StreamoidSidebar's `profile`
|
|
180
|
+
<ScProfile name={user.fullName} subText={workspace.name} profileImage={user.photoUrl} />
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**2. `variant="collapsed"` doesn't collapse the child.** `ScProfile` has its own
|
|
184
|
+
`collapsed` prop (`"true"` / `"false"`, a *string* union), and `ScSidebarProfile` never
|
|
185
|
+
passes it. So the collapsed variant renders the name and subtitle inside a 48 px box.
|
|
186
|
+
This is a real bug, not a subtlety.
|
|
187
|
+
|
|
188
|
+
**3. It only *looks* interactive.** `cursor` is never set, and there is no `role`,
|
|
189
|
+
`tabIndex` or `onKeyDown` — but there **is** a hover background. `...props` does reach
|
|
190
|
+
the root, so an `onClick` works for mouse users; keyboard users get nothing.
|
|
191
|
+
|
|
192
|
+
**4. `className` produces a literal `undefined` class when omitted.**
|
|
193
|
+
`styles.scSidebarProfile + " " + className + " " + variantsClassName`.
|
|
194
|
+
|
|
195
|
+
**5. `align-self: stretch` in the expanded variant.** It assumes a stretching
|
|
196
|
+
flex-column parent (the rail). In a row-direction container it collapses.
|
|
197
|
+
|
|
198
|
+
**6. `state="hover"` is design parity only.** Never wire it to mouse handlers.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## In the wild
|
|
203
|
+
|
|
204
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
205
|
+
|
|
206
|
+
Its only composition site is the legacy sidebar:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
// npm-components packages/ui/src/SC-Sidebar/ScSidebar.tsx:134
|
|
210
|
+
<ScSidebarProfile
|
|
211
|
+
variant="collapsed"
|
|
212
|
+
className={styles.scSidebarProfileInstance}
|
|
213
|
+
/>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Where it *would* belong — the desktop rail footer — is now owned by
|
|
217
|
+
`StreamoidSidebar`'s `profile` prop, which every host passes (e.g.
|
|
218
|
+
`cxo-dashboard src/app/components/app-sidebar.tsx`).
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Related
|
|
223
|
+
|
|
224
|
+
- `ScProfile` — the real identity block; use this if you need a props-driven avatar +
|
|
225
|
+
name + subtitle.
|
|
226
|
+
- `StreamoidSidebar` — the current rail; owns the footer via `profile` and
|
|
227
|
+
`toggleInProfile`.
|
|
228
|
+
- `ScProfilePopup` / `ScProfileOptions` — what the footer opens.
|
|
229
|
+
- `ScDp` — avatar-only primitive.
|
|
230
|
+
- `ScSidebar` / `ScLogoUnit` / `ScSidebarIcons` / `ScVersion` — the rest of this legacy
|
|
231
|
+
sidebar family.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScSidebarSwitchMenu
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: sidebar
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [sidebar, app-switcher, product-switch, two-line, nav-row, description, switch-menu]
|
|
8
|
+
related: [ScSidebarMenu, ScAppSwitchPanel, ScCatalogixSidebar, StreamoidSidebar, ScSideBarLogoUnit]
|
|
9
|
+
do_not_confuse_with: [ScSidebarMenu, ScAppSwitchPanel, ScWorkspaceSwitchCard, StreamoidWorkspaceSwitcher, ScAppCard]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScSidebarSwitchMenu
|
|
13
|
+
|
|
14
|
+
**The two-line row in a sidebar's app-switch list.** Icon + product name + a
|
|
15
|
+
description line beneath it. Expanded it is content-height (8/12 px padding, two text
|
|
16
|
+
lines) with a **square** hover fill; collapsed it becomes a rounded 40 × 40 icon
|
|
17
|
+
square that matches `ScSidebarMenu`'s collapsed row exactly.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** you're building a "switch to another product" list inside a
|
|
22
|
+
sidebar and each entry needs a name **and** a subtitle.
|
|
23
|
+
- **Don't reach for it when:** the row is ordinary nav with one line
|
|
24
|
+
(→ `ScSidebarMenu`), or you want the whole ready-made switch panel
|
|
25
|
+
(→ `ScAppSwitchPanel`, which is what CXO and Catalogix pass to `StreamoidSidebar`'s
|
|
26
|
+
`switchPanel` slot — note it builds its own masked-wordmark rows and does **not**
|
|
27
|
+
compose this component).
|
|
28
|
+
- **Three things that will bite you:**
|
|
29
|
+
1. `text` defaults to `"Pairtext"` and `description` to `"Description"`. ⚠️ Both are
|
|
30
|
+
real Figma placeholder strings that will ship.
|
|
31
|
+
2. `variant="collapsed"` drops **both** text lines and the kebab; only `icon`
|
|
32
|
+
survives. There is no tooltip.
|
|
33
|
+
3. Like `ScSidebarMenu`, the root is a bare `<div>` — no `role`, no `tabIndex`, no
|
|
34
|
+
key handling.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. How to use it
|
|
39
|
+
|
|
40
|
+
### Import
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { ScSidebarSwitchMenu } from "@streamoid/ui";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Minimal usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<ScSidebarSwitchMenu
|
|
51
|
+
icon={<img src={app.iconUrl} alt="" />}
|
|
52
|
+
text="Photogenix"
|
|
53
|
+
description="AI product photography"
|
|
54
|
+
onClick={() => openApp(app)}
|
|
55
|
+
/>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Notes |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `icon` | `ReactNode` | – | Optional. When omitted the icon wrapper is **not rendered at all** (unlike `ScSidebarMenu`, which has a default icon). The wrapper is 20 × 20, `overflow: hidden`, and clamps its child with `max-width/max-height: 100%`. |
|
|
63
|
+
| `text` | `string` | `"Pairtext"` | ⚠️ Real default. Product name. Single line, ellipsised. **`expanded` only.** |
|
|
64
|
+
| `description` | `string` | `"Description"` | ⚠️ Real default. Subtitle line. Single line, ellipsised. **`expanded` only.** |
|
|
65
|
+
| `state` | `"default"` \| `"active"` \| `"hover"` \| `"default-highlight"` \| `"default-active"` | `"default"` | Same five-state vocabulary as `ScSidebarMenu`. Note: `state` only ever recolours **`text`**, never `description`. |
|
|
66
|
+
| `variant` | `"expanded"` \| `"collapsed"` | `"expanded"` | `collapsed` = rounded 40 × 40 square, icon only. |
|
|
67
|
+
| `moreIcon` | `ReactNode` | – | Trailing affordance. **`expanded` only**, hidden until `:hover`. |
|
|
68
|
+
| `onMoreClick` | `(e: React.MouseEvent) => void` | – | Fires from the kebab wrapper, which `stopPropagation()`s first. |
|
|
69
|
+
| `className` | `string` | – | Appended after the internal class. Guarded — no stray `undefined` here (this component is the fixed one in the family). |
|
|
70
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root div — `onClick`, `style`, `aria-*`, `data-*`. |
|
|
71
|
+
|
|
72
|
+
### What each `state` renders
|
|
73
|
+
|
|
74
|
+
| `state` | Background | `text` colour | `description` colour |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `default` | none (hover → `fill-neutral-neutral`) | tertiary | tertiary |
|
|
77
|
+
| `active` | `fill-neutral-neutralselected` | primary | tertiary |
|
|
78
|
+
| `hover` | `fill-neutral-neutral` (forced) | tertiary | tertiary |
|
|
79
|
+
| `default-highlight` | none | primary | tertiary |
|
|
80
|
+
| `default-active` | `surface-canvas` + 0.5 px inset ring | primary | tertiary |
|
|
81
|
+
|
|
82
|
+
`default-highlight` is the idiomatic state for app-switch rows: bright title, no fill.
|
|
83
|
+
|
|
84
|
+
### Recipes
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// The expanded app-switch list (this is what ScCatalogixSidebar does)
|
|
88
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "var(--spacing-md)" }}>
|
|
89
|
+
{switchApps.map((app) => (
|
|
90
|
+
<ScSidebarSwitchMenu
|
|
91
|
+
key={app.id}
|
|
92
|
+
icon={app.icon}
|
|
93
|
+
text={app.name}
|
|
94
|
+
description={app.description}
|
|
95
|
+
state="default-highlight"
|
|
96
|
+
variant="expanded"
|
|
97
|
+
onClick={app.onClick}
|
|
98
|
+
/>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
// The collapsed rail's inline icon list — no card, no labels
|
|
103
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
|
|
104
|
+
{switchApps.map((app) => (
|
|
105
|
+
<ScSidebarSwitchMenu
|
|
106
|
+
key={app.id}
|
|
107
|
+
icon={app.icon}
|
|
108
|
+
state="default"
|
|
109
|
+
variant="collapsed"
|
|
110
|
+
onClick={app.onClick}
|
|
111
|
+
aria-label={app.name}
|
|
112
|
+
/>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 2. Where to use it
|
|
120
|
+
|
|
121
|
+
- **The app-switch list inside a sidebar.** `ScCatalogixSidebar` renders it twice: as
|
|
122
|
+
the expanded list (inside the switch card) and as the collapsed inline icon column.
|
|
123
|
+
- **Any "pick a destination with a subtitle" column in a rail** — workspaces,
|
|
124
|
+
environments, tenants.
|
|
125
|
+
|
|
126
|
+
It is not exported for a page body: the type ramp, the square hover fill and the
|
|
127
|
+
`align-self: stretch` geometry are all rail-specific.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 3. When to use it
|
|
132
|
+
|
|
133
|
+
### Use it when
|
|
134
|
+
|
|
135
|
+
- The row needs **two lines** (name + description) and lives in a sidebar.
|
|
136
|
+
- You are hand-composing the switch list because your app owns the catalogue data.
|
|
137
|
+
|
|
138
|
+
### Don't use it — reach for this instead
|
|
139
|
+
|
|
140
|
+
| Situation | Use instead |
|
|
141
|
+
|---|---|
|
|
142
|
+
| A ready-made app-switch panel from an `apps[]` array | `ScAppSwitchPanel` — pass it to `StreamoidSidebar`'s `switchPanel` prop |
|
|
143
|
+
| A one-line sidebar nav row | `ScSidebarMenu` |
|
|
144
|
+
| A whole branded sidebar with the switcher already wired | `ScCatalogixSidebar` / `ScArtifaxSidebar` / `StreamoidSidebar` |
|
|
145
|
+
| Switching **workspace**, not product | `StreamoidWorkspaceSwitcher` / `ScWorkspaceSwitchCard` |
|
|
146
|
+
| An app tile on a launcher/home grid | `ScAppCardV3` / `ScAppListingCard` |
|
|
147
|
+
| A row in a floating popup menu | `ScMenuOptions` |
|
|
148
|
+
|
|
149
|
+
### Don't confuse with
|
|
150
|
+
|
|
151
|
+
| You may actually want | Not this |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `ScSidebarMenu` — one line, rounded 16 px pill, has a default icon | `ScSidebarSwitchMenu` is two lines, square when expanded, and renders **no** icon if you omit it |
|
|
154
|
+
| `ScAppSwitchPanel` — the whole panel, driven by `apps[{ key, name, description?, onSelect }]` | It draws its own masked-wordmark rows; this component is **not** one of them |
|
|
155
|
+
| `ScWorkspaceSwitchCard` / `StreamoidWorkspaceSwitcher` (folder `SC-WorkspaceSwitcher`) — workspace, not product | Different domain |
|
|
156
|
+
| `ScAppCard` / `ScAppCardV3` / `ScAppCardForCopilot` — page-body app tiles | Rail row, not a card |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 4. Why to use it
|
|
161
|
+
|
|
162
|
+
- **The square/rounded split is a deliberate design rule.** Expanded rows use
|
|
163
|
+
`border-radius: 0` so the hover fill reads as a list band inside the switch card;
|
|
164
|
+
collapsed squares go back to `radius-3xl` so they read as icon buttons in the rail.
|
|
165
|
+
Hand-rolling this is where the switch panel usually starts looking wrong.
|
|
166
|
+
- **Two-line truncation already works.** The text column (`.content`) is
|
|
167
|
+
`flex: 1 0 0; min-width: 0`, and both lines inside it are `width: 100%` with
|
|
168
|
+
`text-overflow: ellipsis`, so long product names shrink instead of pushing the kebab
|
|
169
|
+
out of the rail.
|
|
170
|
+
- **Icon containment.** `.iconWrapper` is a hard 20 × 20 `overflow: hidden` box with
|
|
171
|
+
`max-width/max-height: 100%` on its child, so a remote product logo of any intrinsic
|
|
172
|
+
size lands at the right optical weight.
|
|
173
|
+
- **Shares the state vocabulary with `ScSidebarMenu`**, so a switch row and a nav row
|
|
174
|
+
in the same rail agree on what "active" looks like.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Gotchas
|
|
179
|
+
|
|
180
|
+
**1. `text` and `description` default to Figma placeholders.** `"Pairtext"` and
|
|
181
|
+
`"Description"` will render if you forget them (or typo the prop name).
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
// WRONG — renders "Pairtext" / "Description"
|
|
185
|
+
<ScSidebarSwitchMenu icon={icon} name="Photogenix" />
|
|
186
|
+
|
|
187
|
+
// RIGHT
|
|
188
|
+
<ScSidebarSwitchMenu icon={icon} text="Photogenix" description="AI product photography" />
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**2. `state` never recolours `description`.** Only `.text` gets the primary colour in
|
|
192
|
+
`active` / `default-highlight` / `default-active`. The subtitle stays tertiary in every
|
|
193
|
+
state — by design, but surprising if you expected the whole row to brighten.
|
|
194
|
+
|
|
195
|
+
**3. Collapsed loses everything but the icon.** `text`, `description`, `moreIcon` all
|
|
196
|
+
live inside the `variant === "expanded"` branch. And there's no `title`/`aria-label` —
|
|
197
|
+
pass `aria-label` yourself (it reaches the DOM via `...props`) or add a tooltip.
|
|
198
|
+
|
|
199
|
+
**4. Omitting `icon` gives you a *left-flush text row*, not a placeholder.** The icon
|
|
200
|
+
wrapper is conditional (`icon ? … : null`). This differs from `ScSidebarMenu`, which
|
|
201
|
+
always renders an icon and defaults it to `<SiconHome />`.
|
|
202
|
+
|
|
203
|
+
**5. No a11y wiring.** Plain `<div>` with `cursor: pointer`; no `role`, `tabIndex` or
|
|
204
|
+
`onKeyDown`. Add them via `...props` if the row is a real navigation target.
|
|
205
|
+
|
|
206
|
+
**6. `moreIcon` is `display: none` until `:hover`.** No focus-visible reveal, so it is
|
|
207
|
+
unreachable by keyboard and on touch.
|
|
208
|
+
|
|
209
|
+
**7. `align-self: stretch` in expanded state.** The row assumes a stretching
|
|
210
|
+
flex-column parent. Drop it into a `flex-direction: row` container and it collapses.
|
|
211
|
+
`collapsed` unsets this and pins 40 × 40.
|
|
212
|
+
|
|
213
|
+
**8. The row is not a card.** It draws no border and no background of its own — the
|
|
214
|
+
bordered "switch card" look comes from the sidebar shell (`StreamoidSidebar`'s
|
|
215
|
+
`switchPanel` renders the logo + panel inside one bordered card and dims the rest).
|
|
216
|
+
Don't try to reproduce that here.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## In the wild
|
|
221
|
+
|
|
222
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
223
|
+
|
|
224
|
+
It is composed inside the design system, by the Catalogix sidebar wrapper:
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
// npm-components packages/ui/src/SC-Catalogix-Sidebar/ScCatalogixSidebar.tsx:180
|
|
228
|
+
<ScSidebarSwitchMenu
|
|
229
|
+
key={app.id}
|
|
230
|
+
icon={app.icon}
|
|
231
|
+
text={app.name}
|
|
232
|
+
description={app.description}
|
|
233
|
+
state="default-highlight"
|
|
234
|
+
variant="expanded"
|
|
235
|
+
onClick={app.onClick}
|
|
236
|
+
/>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
…and again at `ScCatalogixSidebar.tsx:204` for the collapsed icon column. Those are its
|
|
240
|
+
**only** two render sites anywhere. No host app reaches it, even indirectly: the
|
|
241
|
+
Catalogix dashboard builds its rail from `StreamoidSidebar` directly
|
|
242
|
+
(`app/containers/LeftMenu/index.jsx:991`) and hands it a `ScAppSwitchPanel`, and
|
|
243
|
+
`ScAppSwitchPanel` draws its own rows. If you need a direct call site, it belongs in a
|
|
244
|
+
sidebar's app/workspace-switch list — nowhere else.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Related
|
|
249
|
+
|
|
250
|
+
- `ScSidebarMenu` — the one-line sibling; the workhorse rail row.
|
|
251
|
+
- `ScAppSwitchPanel` — the assembled switch panel; prefer it over hand-listing rows.
|
|
252
|
+
- `StreamoidSidebar` — pass the panel via `switchPanel` + `switchPanelOpen`, **not**
|
|
253
|
+
inline in the logo slot (that pushes the nav down).
|
|
254
|
+
- `ScCatalogixSidebar` / `ScArtifaxSidebar` — branded shells that already wire this up.
|
|
255
|
+
- `ScSideBarLogoUnit` — the header row whose chevron opens the switch list.
|
|
256
|
+
- `StreamoidWorkspaceSwitcher` / `ScWorkspaceSwitchCard` — the workspace-level equivalents.
|
|
257
|
+
(Note: the folder is `SC-WorkspaceSwitcher` but the export is `StreamoidWorkspaceSwitcher`;
|
|
258
|
+
there is no `ScWorkspaceSwitcher`.)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScSlider
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: forms
|
|
5
|
+
status: legacy
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [slider, range, steps, segmented-progress, strength, opacity, non-interactive, figma-trace]
|
|
8
|
+
related: [ScProgressBar, ScCounter, ScTextField]
|
|
9
|
+
do_not_confuse_with: [ScProgressBar, ScCounter, ScSelectionPillGroup, ScTabSwitcher]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScSlider
|
|
13
|
+
|
|
14
|
+
**Not a working slider — a static five-segment bar.** It renders a fixed row of
|
|
15
|
+
5 bars + 5 dots with the **first** segment painted warning-orange and the rest grey.
|
|
16
|
+
Nothing is draggable, there is no `value`, and its one callback is never called.
|
|
17
|
+
Treat it as an unfinished Figma trace.
|
|
18
|
+
|
|
19
|
+
## TL;DR for agents
|
|
20
|
+
|
|
21
|
+
- **Reach for it when:** essentially never in its current form. There is no host call
|
|
22
|
+
site, and it cannot report or receive a value.
|
|
23
|
+
- **Don't reach for it when:** you need a real range input (→ native
|
|
24
|
+
`<input type="range">`), a progress indicator (→ `ScProgressBar`), or a bounded
|
|
25
|
+
numeric value (→ `ScCounter`).
|
|
26
|
+
- **Three things that will bite you:**
|
|
27
|
+
1. **`onValueChange` is declared and never invoked.** Dead prop.
|
|
28
|
+
2. There is **no `value` / `min` / `max` / `step`** — the highlighted segment is
|
|
29
|
+
hardcoded to the first one.
|
|
30
|
+
3. The root is `overflow: hidden`, so a thumb or tooltip you try to inject gets
|
|
31
|
+
clipped.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
### Import
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ScSlider } from "@streamoid/ui";
|
|
41
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Minimal usage
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
// Renders the fixed 5-segment bar. This is all it does.
|
|
48
|
+
<ScSlider />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Props
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Default | Notes |
|
|
54
|
+
|---|---|---|---|
|
|
55
|
+
| `onValueChange` | `(value: number) => void` | – | ⚠️ **Never called.** Declared in the interface, destructured, and dropped. |
|
|
56
|
+
| `className` | `string` | – | Concatenated onto the root. |
|
|
57
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root — `onClick`, `onPointerDown`, `style`, `aria-*`. The only way to make it do anything. |
|
|
58
|
+
|
|
59
|
+
### What it actually renders
|
|
60
|
+
|
|
61
|
+
| Child | Count | Fill |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| First bar + first dot | 1 each | `--alias-fill-warning-solid` (orange) |
|
|
64
|
+
| Remaining bars | 4 | `--alias-fill-neutral-neutralactive` |
|
|
65
|
+
| Remaining dots | 4 | `--alias-text-and-icons-primary` (white) |
|
|
66
|
+
|
|
67
|
+
Container: `--alias-surface-base` background, `--radius-full`, `1rem` padding,
|
|
68
|
+
`0.25rem` gaps, `overflow: hidden`. Bars are `flex: 1` (they stretch); dots are
|
|
69
|
+
fixed `0.5rem` squares.
|
|
70
|
+
|
|
71
|
+
### Recipes
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// What you almost certainly want instead — a real range input, styled by you.
|
|
75
|
+
// (Photogenix does exactly this locally: client/src/components/viewer/views/AdjustmentsView.tsx)
|
|
76
|
+
<input
|
|
77
|
+
type="range"
|
|
78
|
+
min={0}
|
|
79
|
+
max={100}
|
|
80
|
+
value={strength}
|
|
81
|
+
onChange={(e) => setStrength(Number(e.target.value))}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
// A determinate progress bar from the DS — `progress` is a PERCENT (0–100, clamped),
|
|
85
|
+
// not a 0–1 fraction. It defaults to 50.
|
|
86
|
+
<ScProgressBar progress={42} />
|
|
87
|
+
|
|
88
|
+
// A bounded numeric value with a stepper
|
|
89
|
+
<ScCounter count={strength} min={0} max={100} onChange={setStrength} label="%" />
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 2. Where to use it
|
|
95
|
+
|
|
96
|
+
Nowhere today. No render site exists in CXO, Catalogix, Photogenix or Artifax, and no
|
|
97
|
+
other DS component composes it. Its visual — a segmented bar with one hot segment —
|
|
98
|
+
reads like a **"step 1 of 5" indicator** rather than a range control, so if it gets
|
|
99
|
+
finished it most plausibly belongs to a **multi-step flow header** (feed import,
|
|
100
|
+
onboarding) or a discrete strength picker in Photogenix's adjustments panel.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 3. When to use it
|
|
105
|
+
|
|
106
|
+
### Use it when
|
|
107
|
+
|
|
108
|
+
- You deliberately want the static five-segment decoration and are fine with it being
|
|
109
|
+
non-interactive, **and** you accept that its API may change when someone finishes it.
|
|
110
|
+
|
|
111
|
+
### Don't use it — reach for this instead
|
|
112
|
+
|
|
113
|
+
| Situation | Use instead |
|
|
114
|
+
|---|---|
|
|
115
|
+
| A draggable numeric range (strength, opacity, threshold) | native `<input type="range">` — Photogenix's `AdjustmentsView` is the reference |
|
|
116
|
+
| Determinate progress / percentage complete | `ScProgressBar` (`progress?: number`, 0–100, default `50`) |
|
|
117
|
+
| A bounded integer the user nudges or types | `ScCounter` (has real `min`/`max`) |
|
|
118
|
+
| Free numeric entry in a form | `ScTextField` / `ScOnlyField` |
|
|
119
|
+
| Discrete choice between 2–5 options | `ScSelectionPillGroup` / `ScTabSwitcher` |
|
|
120
|
+
|
|
121
|
+
### Don't confuse with
|
|
122
|
+
|
|
123
|
+
| You may actually want | Not this |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `ScProgressBar` — one bar driven by a real `progress` number | `ScSlider` has no value input at all |
|
|
126
|
+
| `ScCounter` — the only DS control with working `min`/`max` clamping | Not a slider, but usually the right answer |
|
|
127
|
+
| A "steps" indicator | This looks like one but exposes no current-step prop |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 4. Why to use it
|
|
132
|
+
|
|
133
|
+
Honestly: little. The only things it gives you are the token-driven fills
|
|
134
|
+
(`--alias-fill-warning-solid`, `--alias-fill-neutral-neutralactive`) and the pill
|
|
135
|
+
container geometry. Because it accepts no value and emits no events, **any real
|
|
136
|
+
interaction has to be built on top of the spread `HTMLAttributes`**, at which point
|
|
137
|
+
you are writing the slider yourself and should do so with a native
|
|
138
|
+
`<input type="range">` that gets keyboard support, `aria-valuenow` and touch handling
|
|
139
|
+
for free.
|
|
140
|
+
|
|
141
|
+
If you need this component for real, the honest move is to fix it in the DS
|
|
142
|
+
(add `value` / `min` / `max` / `step`, wire `onValueChange`, drop `overflow: hidden`)
|
|
143
|
+
rather than to work around it at a call site.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Gotchas
|
|
148
|
+
|
|
149
|
+
**1. `onValueChange` never fires.** It is destructured out of props and then unused —
|
|
150
|
+
so it is not even forwarded to the DOM.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
// WRONG — the handler is dead code
|
|
154
|
+
<ScSlider onValueChange={setStrength} />
|
|
155
|
+
|
|
156
|
+
// RIGHT — for a real slider, don't use this component
|
|
157
|
+
<input type="range" min={0} max={100} value={strength}
|
|
158
|
+
onChange={(e) => setStrength(Number(e.target.value))} />
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**2. The highlighted segment is hardcoded.** The first bar/dot pair always uses the
|
|
162
|
+
warning colour; segments 2–5 always use the neutral pair. You cannot express "3 of 5".
|
|
163
|
+
|
|
164
|
+
**3. `overflow: hidden` on the root.** Any absolutely-positioned thumb, value bubble
|
|
165
|
+
or tooltip you add via children/`style` will be clipped by the pill.
|
|
166
|
+
|
|
167
|
+
**4. Bars stretch, dots don't.** Bars are `flex: 1`; dots are fixed 8px. In a narrow
|
|
168
|
+
container the bars collapse toward zero and you get a dotted line.
|
|
169
|
+
|
|
170
|
+
**5. It has no accessible role.** No `role="slider"`, no `aria-valuemin/now/max`, no
|
|
171
|
+
`tabIndex`. Screen readers see an empty group of divs.
|
|
172
|
+
|
|
173
|
+
**6. `className` is string-concatenated**, so the DOM class list contains a literal
|
|
174
|
+
`undefined` when you omit it.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## In the wild
|
|
179
|
+
|
|
180
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
181
|
+
|
|
182
|
+
In this case neither: nothing renders it. It exists only as an export. The nearest
|
|
183
|
+
real implementation of the job it looks like it does is Photogenix's local
|
|
184
|
+
`<input type="range">` in
|
|
185
|
+
`photogenix_v2 dashboard/client/src/components/viewer/views/AdjustmentsView.tsx:60`.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Related
|
|
190
|
+
|
|
191
|
+
- `ScProgressBar` — determinate progress from a real `progress` number.
|
|
192
|
+
- `ScCounter` — bounded numeric stepper with working `min`/`max`.
|
|
193
|
+
- `ScSelectionPillGroup` / `ScTabSwitcher` — if the intent was discrete steps/choices.
|
|
194
|
+
- `ScTextField` — plain numeric entry inside a form.
|