@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,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScRoleMobile
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: mobile
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [mobile, role, admin, member, badge, chip, permission, label]
|
|
8
|
+
related: [ScRole, ScBadges, ScTableListMobile, ScWorkspaceSwitchMobileV2, ScAccess]
|
|
9
|
+
do_not_confuse_with: [ScRole, ScBadges, ScTaxonomyPill, ScSelectionPill]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScRoleMobile
|
|
13
|
+
|
|
14
|
+
**A fixed 64×32 "Admin" / "Member" chip for mobile rows.** Two hardcoded looks: admin
|
|
15
|
+
= blue info fill with blue text, member = neutral grey fill with grey text. The label
|
|
16
|
+
text is derived from the `role` prop — there is no way to change the words.
|
|
17
|
+
|
|
18
|
+
## TL;DR for agents
|
|
19
|
+
|
|
20
|
+
- **Reach for it when:** a mobile list row or card needs the user's role as a chip
|
|
21
|
+
and the only two roles are admin and member.
|
|
22
|
+
- **Don't reach for it when:** the desktop needs the same chip (→ `ScRole`, which has
|
|
23
|
+
a `label` prop), the label is anything other than Admin/Member (→ `ScBadges`), or
|
|
24
|
+
the chip is meant to be tappable/selectable (→ `ScSelectionPill`).
|
|
25
|
+
- **Three things that will bite you:**
|
|
26
|
+
1. **The label is hardcoded English**, computed as `role === "admin" ? "Admin" : "Member"`.
|
|
27
|
+
No `label` prop — unlike its desktop twin `ScRole`.
|
|
28
|
+
2. **`role` is the prop name, so you can never set the ARIA `role` attribute.**
|
|
29
|
+
The types intersect down to `"admin" | "member"`.
|
|
30
|
+
3. **Fixed `width: 64px; height: 32px`** — "Member" fits, longer words would not,
|
|
31
|
+
and it never fills its container.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
### Import
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ScRoleMobile } from "@streamoid/ui";
|
|
41
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Minimal usage
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<ScRoleMobile role={member.permissions.role === "admin" ? "admin" : "member"} />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Props
|
|
51
|
+
|
|
52
|
+
| Prop | Type | Default | Notes |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `role` | `"admin"` \| `"member"` | `"admin"` | ⚠️ Defaults to **admin** — the more privileged value. Drives both the skin and the label text. |
|
|
55
|
+
| `className` | `string` | – | Appended after internal classes. Unguarded concat — see Gotcha 4. |
|
|
56
|
+
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div`. Note `role` is consumed by this component, so the ARIA attribute is unavailable. |
|
|
57
|
+
|
|
58
|
+
### What each value renders
|
|
59
|
+
|
|
60
|
+
| `role` | Label | Background | Border | Text |
|
|
61
|
+
|---|---|---|---|---|
|
|
62
|
+
| `"admin"` | `Admin` | `--alias-fill-info-soft` | `--alias-border-infoplus` | `--alias-text-and-icons-infocont` |
|
|
63
|
+
| `"member"` | `Member` | `--alias-fill-neutral-neutral` | `--alias-border-default` | `--alias-text-and-icons-tertiary` |
|
|
64
|
+
|
|
65
|
+
Anything that isn't exactly `"admin"` falls to the member branch for the label, but the
|
|
66
|
+
skin class is `styles["role-" + role]`, so a value outside the union produces an
|
|
67
|
+
**unstyled** chip (no background, a `1px solid` border with no colour).
|
|
68
|
+
|
|
69
|
+
### Recipes
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// Mapping a backend role that has more than two values
|
|
73
|
+
<ScRoleMobile role={m.role === "admin" || m.role === "owner" ? "admin" : "member"} />
|
|
74
|
+
|
|
75
|
+
// Need "Owner" / "Viewer" / "Billing" wording? This chip cannot do it — use ScBadges
|
|
76
|
+
import { ScBadges } from "@streamoid/ui";
|
|
77
|
+
<ScBadges text="Owner" variant="info" styleVariant="opaque" />
|
|
78
|
+
|
|
79
|
+
// Announce it to assistive tech — you cannot pass role="…", so use aria-label on
|
|
80
|
+
// the wrapper, or aria-label directly (the prop name collision only blocks `role`)
|
|
81
|
+
<ScRoleMobile role="member" aria-label="Workspace role: Member" />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 2. Where to use it
|
|
87
|
+
|
|
88
|
+
The role slot of a mobile list row or card — the mobile counterpart of the role column
|
|
89
|
+
in a desktop members table. In practice its job is already done inline by the mobile
|
|
90
|
+
components that need it: `ScTableListMobile`, `ScWorkspaceSettingsMobile` and
|
|
91
|
+
`ScWorkspaceSwitchMobile` each render their **own** role badge rather than composing
|
|
92
|
+
this one, so `ScRoleMobile` is only for rows you assemble yourself.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. When to use it
|
|
97
|
+
|
|
98
|
+
### Use it when
|
|
99
|
+
|
|
100
|
+
- The role vocabulary really is just admin/member.
|
|
101
|
+
- You are hand-assembling a mobile row and want the same chip metrics as the rest of
|
|
102
|
+
the mobile family.
|
|
103
|
+
|
|
104
|
+
### Don't use it — reach for this instead
|
|
105
|
+
|
|
106
|
+
| Situation | Use instead |
|
|
107
|
+
|---|---|
|
|
108
|
+
| Desktop members table / settings row | `ScRole` — same two types, **plus a `label` prop** |
|
|
109
|
+
| Any other word ("Owner", "Viewer", "Pending", "Billing admin") | `ScBadges` (`text` + `variant` + `styleVariant`) |
|
|
110
|
+
| A status chip that must change colour by state | `ScBadges` |
|
|
111
|
+
| A tappable filter/segment chip | `ScSelectionPill` (+ `ScSelectionPillGroup`) |
|
|
112
|
+
| A taxonomy tree node chip | `ScTaxonomyPill` |
|
|
113
|
+
| Per-app access / permission icons | `ScAccess`, or the `accessIcons` / `permissionIcons` props of `ScTableListMobile` |
|
|
114
|
+
| A whole member row (avatar, name, email, role, icons) | `ScTableListMobile` |
|
|
115
|
+
|
|
116
|
+
### Don't confuse with
|
|
117
|
+
|
|
118
|
+
| You may actually want | Not this |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `ScRole` — desktop twin. Prop is **`type`**, not `role`, and it has `label` so you can print any word | `ScRoleMobile` has neither |
|
|
121
|
+
| `ScBadges` — the general status chip; any text, several variants | `ScRoleMobile` is two hardcoded words |
|
|
122
|
+
| `ScSelectionPill` — interactive segmented pill with `aria-pressed` | `ScRoleMobile` is a static `div` |
|
|
123
|
+
| The role badge inside `ScWorkspaceSwitchMobile` / `ScWorkspaceSettingsMobile` | Those are **internal** markup with their own metrics (80px wide, `--radius-xl`, always info-blue). This component is not what they use. |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 4. Why to use it
|
|
128
|
+
|
|
129
|
+
- **The two role skins are already decided** — info-soft/infoplus for admin,
|
|
130
|
+
neutral/default for member — matching how role is coloured everywhere else in the
|
|
131
|
+
product, so an admin looks like an admin on every screen.
|
|
132
|
+
- **Fixed metrics mean lists line up.** 64×32 with `--radius-md` keeps the chip column
|
|
133
|
+
the same width down a list without you setting min-widths.
|
|
134
|
+
- **One prop.** There is genuinely nothing to get wrong except the five gotchas below.
|
|
135
|
+
|
|
136
|
+
If you need any flexibility at all — different words, more roles, colour by state —
|
|
137
|
+
`ScBadges` is the right primitive and this component is a trap.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Gotchas
|
|
142
|
+
|
|
143
|
+
**1. The label is hardcoded English.** `"Admin"` / `"Member"` are literals in the
|
|
144
|
+
render body. Do not use this component on a localised surface.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// IMPOSSIBLE — there is no label prop (this is a type error)
|
|
148
|
+
<ScRoleMobile role="admin" label="Administrateur" />
|
|
149
|
+
|
|
150
|
+
// The desktop twin CAN do it
|
|
151
|
+
<ScRole type="admin" label="Administrateur" />
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**2. You cannot set the ARIA `role` attribute.** `role` is destructured as the
|
|
155
|
+
component's own prop, and because the signature is
|
|
156
|
+
`IScRoleMobileProps & React.HTMLAttributes<HTMLDivElement>`, the type narrows to
|
|
157
|
+
`"admin" | "member"`:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
// TYPE ERROR — "status" is not assignable to "admin" | "member"
|
|
161
|
+
<ScRoleMobile role="status" />
|
|
162
|
+
|
|
163
|
+
// Workaround: aria-* still works, or wrap it
|
|
164
|
+
<span role="status"><ScRoleMobile role="member" /></span>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**3. `role` defaults to `"admin"`.** Forgetting the prop grants everyone the
|
|
168
|
+
privileged-looking chip. Always pass it explicitly.
|
|
169
|
+
|
|
170
|
+
**4. `className` is concatenated unguarded.** Omit it and the root carries a literal
|
|
171
|
+
`undefined` class. Cosmetic, but it breaks exact-class assertions.
|
|
172
|
+
|
|
173
|
+
**5. An out-of-union `role` renders an unstyled chip.** `styles["role-" + role]` is
|
|
174
|
+
`undefined` for anything else, and the base class sets `border: 1px solid` with no
|
|
175
|
+
colour. TypeScript catches this; plain JS hosts (Catalogix, Photogenix) will not.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## In the wild
|
|
180
|
+
|
|
181
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
182
|
+
|
|
183
|
+
To be precise: it is exported from `@streamoid/ui` but no host app renders it, and it
|
|
184
|
+
is not agent-runtime — it is currently unused. Where it belongs is CXO's mobile teams
|
|
185
|
+
screen; that screen instead passes a plain string to the row component
|
|
186
|
+
(`cxo-dashboard/src/app/components/mobile-teams-content.tsx:311`,
|
|
187
|
+
`role={member.permissions.role === "admin" ? "Admin" : "Member"}` on
|
|
188
|
+
`ScTableListMobile`), which renders its own badge.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Related
|
|
193
|
+
|
|
194
|
+
- `ScRole` — the desktop twin; prop is `type`, and it accepts a custom `label`.
|
|
195
|
+
- `ScBadges` — reach for this the moment the wording or colour must vary.
|
|
196
|
+
- `ScTableListMobile` — the mobile member row; renders its own role badge.
|
|
197
|
+
- `ScWorkspaceSettingsMobile` / `ScWorkspaceSwitchMobile` — also render their own
|
|
198
|
+
(always-blue, 80px) role badge rather than this chip.
|
|
199
|
+
- `ScAccess` — per-app access rows, the other half of "what can this user do".
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScSelect
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: forms
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div > button[aria-haspopup="listbox"] + div[role="listbox"]
|
|
7
|
+
tags: [select, dropdown, picker, listbox, options, combobox, choice]
|
|
8
|
+
related: [ScTextField, ScTabField, ScPopUpMenu, ScMenuOptions, ScMappingCard, ScSelectionPillGroup]
|
|
9
|
+
do_not_confuse_with: [ScSelection, ScSelectionList, ScSelectionPill, ScSelectionPillGroup, ScPopUpMenu, ScMenuOptions]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScSelect
|
|
13
|
+
|
|
14
|
+
**The DS-native dropdown.** A 48 px field-shaped `<button>` showing the selected
|
|
15
|
+
label (or a placeholder) plus a chevron that rotates when open, and an
|
|
16
|
+
absolutely-positioned `role="listbox"` panel of options — each option optionally
|
|
17
|
+
carrying a second description line. It owns its own open/closed state and closes
|
|
18
|
+
on an outside `mousedown`.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** the user picks **one** value from a list that is too long
|
|
23
|
+
or too wordy for inline pills, and you want DS chrome without wiring a trigger
|
|
24
|
+
yourself.
|
|
25
|
+
- **Don't reach for it when:** the menu must escape a clipping parent (it is
|
|
26
|
+
**not portalled**), you need multi-select/search/async options, or the "menu" is
|
|
27
|
+
really a list of *actions* (→ `ScPopUpMenu` + `ScMenuOptions`).
|
|
28
|
+
- **Five things that will bite you:**
|
|
29
|
+
1. Options are keyed by **`id`**, not `value` — `{ id, label, description? }`.
|
|
30
|
+
2. `placeholder` defaults to the literal `"Select an option..."`.
|
|
31
|
+
3. `label` renders **only** when you also pass `labelGroup="label"`.
|
|
32
|
+
4. The menu is `position: absolute; z-index: 20` inside the component. Any
|
|
33
|
+
ancestor with `overflow: hidden` clips it — including `ScMappingCard` and
|
|
34
|
+
`ScTextField`'s own field container.
|
|
35
|
+
5. **No keyboard support in the list.** The trigger is a real button, but the
|
|
36
|
+
options are `div`s: no arrow keys, no Enter-to-select, no Escape-to-close,
|
|
37
|
+
no focus management.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. How to use it
|
|
42
|
+
|
|
43
|
+
### Import
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { ScSelect } from "@streamoid/ui";
|
|
47
|
+
import type { IScSelectOption } from "@streamoid/ui";
|
|
48
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Minimal usage
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<ScSelect
|
|
55
|
+
options={[
|
|
56
|
+
{ id: "admin", label: "Admin" },
|
|
57
|
+
{ id: "member", label: "Member" },
|
|
58
|
+
]}
|
|
59
|
+
value={role}
|
|
60
|
+
onChange={setRole}
|
|
61
|
+
/>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Props
|
|
65
|
+
|
|
66
|
+
`ScSelect` does **not** extend `HTMLAttributes` and does **not** spread extra
|
|
67
|
+
props. `className` is the only escape hatch — no `style`, `id`, `data-*`,
|
|
68
|
+
`onBlur`, `name` or `required`.
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Default | Notes |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| `options` | `IScSelectOption[]` | `[]` | ⚠️ Empty default: with no options the panel opens blank. |
|
|
73
|
+
| `value` | `string` | – | Matched against `option.id`. Controlled — no internal value state. |
|
|
74
|
+
| `onChange` | `(id: string) => void` | – | Called with the option **`id`**, then the menu closes. |
|
|
75
|
+
| `placeholder` | `string` | `"Select an option..."` | ⚠️ Real default, hardcoded English. |
|
|
76
|
+
| `label` | `string` | – | Rendered only when `labelGroup === "label"` **and** `label` is truthy. |
|
|
77
|
+
| `labelGroup` | `""` \| `"label"` | `""` | Gates the label row. |
|
|
78
|
+
| `disabled` | `boolean` | `false` | Disables the native button, blocks toggling, greys text + chevron. |
|
|
79
|
+
| `className` | `string` | – | Appended to the root class list. |
|
|
80
|
+
|
|
81
|
+
`IScSelectOption`
|
|
82
|
+
|
|
83
|
+
| Field | Type | Notes |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `id` | `string` | **Required.** The value passed to `onChange`; also the React key. |
|
|
86
|
+
| `label` | `string` | **Required.** Trigger text and option title. |
|
|
87
|
+
| `description` | `string` | Optional 12 px muted second line inside the option row. |
|
|
88
|
+
|
|
89
|
+
#### Derived visual state (you cannot set it)
|
|
90
|
+
|
|
91
|
+
| Condition | State | Trigger skin |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `disabled` | `disabled` | base fill, subtle border, `not-allowed`, disabled text |
|
|
94
|
+
| menu open | `active` | raised fill, **strong** border |
|
|
95
|
+
| a matching option | `filled` | raised fill, default border |
|
|
96
|
+
| otherwise | `default` | base fill, subtle border, muted placeholder text |
|
|
97
|
+
|
|
98
|
+
### Recipes
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
// Labelled, with descriptions
|
|
102
|
+
<ScSelect
|
|
103
|
+
label="Role"
|
|
104
|
+
labelGroup="label"
|
|
105
|
+
options={[
|
|
106
|
+
{ id: "admin", label: "Admin", description: "Full access, can invite users" },
|
|
107
|
+
{ id: "member", label: "Member", description: "Scoped access you configure" },
|
|
108
|
+
]}
|
|
109
|
+
value={role}
|
|
110
|
+
onChange={setRole}
|
|
111
|
+
/>
|
|
112
|
+
|
|
113
|
+
// Mapping API objects — remember `id`, not `value`
|
|
114
|
+
<ScSelect
|
|
115
|
+
options={attributes.map((a) => ({ id: a.code, label: a.displayName }))}
|
|
116
|
+
value={target}
|
|
117
|
+
onChange={setTarget}
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
// Width/margins: className only (there is no `style` prop)
|
|
121
|
+
<ScSelect className="my-select" options={opts} value={v} onChange={setV} />
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 2. Where to use it
|
|
127
|
+
|
|
128
|
+
- **Simple settings and form dropdowns** where the option list is static and
|
|
129
|
+
short (roles, units, sort orders, statuses).
|
|
130
|
+
- **Inside `ScModal` / `ScDrawer`** — but check the container: the menu is not
|
|
131
|
+
portalled, so a scrolling modal body will clip or scroll it away.
|
|
132
|
+
|
|
133
|
+
The hosts currently solve dropdowns two other ways, which is why this component
|
|
134
|
+
has no host render site yet:
|
|
135
|
+
|
|
136
|
+
| Host | What it does instead |
|
|
137
|
+
|---|---|
|
|
138
|
+
| Catalogix | `app/components/DsDropdown` — a read-only `ScTextField` as the trigger with a hand-rolled, host-owned menu; plus `react-select` (portalled) for feed mapping |
|
|
139
|
+
| `@streamoid/settings` | wraps `ScTextField` in a clickable div for the specialization picker |
|
|
140
|
+
|
|
141
|
+
`ScSelect` is the intended replacement for those wrappers whenever the menu does
|
|
142
|
+
**not** need to escape a clipping ancestor.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 3. When to use it
|
|
147
|
+
|
|
148
|
+
### Use it when
|
|
149
|
+
|
|
150
|
+
- One value, from **5–30** static options, where pills/tabs would not fit.
|
|
151
|
+
- Options benefit from a **second description line** (`description`).
|
|
152
|
+
- You want the trigger to look identical to `ScTextField` (same 48 px pill, same
|
|
153
|
+
fill/border tokens, same focus skin) with none of the wrapper work.
|
|
154
|
+
|
|
155
|
+
### Don't use it — reach for this instead
|
|
156
|
+
|
|
157
|
+
| Situation | Use instead |
|
|
158
|
+
|---|---|
|
|
159
|
+
| 2–4 mutually exclusive choices in a form, all visible | `ScTabField` |
|
|
160
|
+
| 2–5 filters/views over a list | `ScSelectionPillGroup` |
|
|
161
|
+
| A list of **actions** (Rename, Duplicate, Delete) | `ScPopUpMenu` + `ScMenuOptions` |
|
|
162
|
+
| Multi-select, type-ahead search, async/paged options, grouped options | not in the DS — use the host's `react-select` (portalled) or build it |
|
|
163
|
+
| The menu must escape `overflow: hidden` (e.g. inside `ScMappingCard`'s `targets` slot) | a portalled select of your own — `ScSelect` will be clipped |
|
|
164
|
+
| Native form participation (`name`, `required`, uncontrolled submit) | a real `<select>`, styled with the tokens |
|
|
165
|
+
| Free text with an optional suggestion list | `ScTextField` + your own list |
|
|
166
|
+
|
|
167
|
+
### Don't confuse with
|
|
168
|
+
|
|
169
|
+
| You may actually want | Not this |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `ScSelection` — an in-chat radio row (title + description) in the **agent runtime** | `ScSelect` is a host-app dropdown |
|
|
172
|
+
| `ScSelectionList` — an in-chat approve/reject list, also agent runtime | unrelated |
|
|
173
|
+
| `ScSelectionPill` / `ScSelectionPillGroup` — a segmented pill row in the dashboards | `ScSelect` is a collapsed menu |
|
|
174
|
+
| `ScPopUpMenu` / `ScMenuOptions` — an action menu hung off an icon | `ScSelect` sets a **value** |
|
|
175
|
+
|
|
176
|
+
The `ScSelect` / `ScSelection*` name collision is one of the top sources of wrong
|
|
177
|
+
picks in this library: **`ScSelect` = dropdown (dashboards)**,
|
|
178
|
+
**`ScSelection*` = chat transcript UI (agent runtime)**.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 4. Why to use it
|
|
183
|
+
|
|
184
|
+
- **The trigger already matches the field vocabulary** — `fill-neutral-neutralplus`
|
|
185
|
+
→ `neutral` on hover/filled, `border-subtle` → `border-strong` when open, the
|
|
186
|
+
same `radius-3xl` and 48 px height as `ScTextField`. A hand-rolled trigger drifts
|
|
187
|
+
immediately.
|
|
188
|
+
- **Correct listbox semantics on the parts that matter**: `aria-haspopup="listbox"`,
|
|
189
|
+
`aria-expanded` on a real `<button type="button">`, `role="listbox"` on the panel
|
|
190
|
+
and `role="option"` + `aria-selected` on each row.
|
|
191
|
+
- **Outside-click closing is already handled** (a `mousedown` listener registered
|
|
192
|
+
only while open, and cleaned up on close) — the single most commonly forgotten
|
|
193
|
+
piece of a hand-rolled dropdown.
|
|
194
|
+
- **Descriptions in options** are tokenised (12 px muted) so a two-line option row
|
|
195
|
+
looks the same everywhere.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Gotchas
|
|
200
|
+
|
|
201
|
+
**1. Options use `id`, not `value`.** Nearly every other list-shaped DS prop in
|
|
202
|
+
this library (`ScTabField.tabs`, `ScSelectionPillGroup.options`) uses
|
|
203
|
+
`{ label, value }`. This one is `{ id, label, description? }`.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
// WRONG — never matches; the trigger shows the placeholder forever
|
|
207
|
+
<ScSelect options={[{ value: "admin", label: "Admin" }]} value="admin" />
|
|
208
|
+
|
|
209
|
+
// RIGHT
|
|
210
|
+
<ScSelect options={[{ id: "admin", label: "Admin" }]} value="admin" onChange={setRole} />
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**2. The menu is not portalled.** It is `position: absolute; top: 100%;
|
|
214
|
+
z-index: 20` inside `.selectArea`. Any ancestor with `overflow: hidden` clips it —
|
|
215
|
+
notably `ScMappingCard` (whose README warns about exactly this) and
|
|
216
|
+
`ScTextField`'s field container. Lift the select out, or use a portalled select.
|
|
217
|
+
|
|
218
|
+
**3. `label` alone renders nothing.**
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
// WRONG — the label is dropped
|
|
222
|
+
<ScSelect label="Role" options={opts} value={v} onChange={setV} />
|
|
223
|
+
|
|
224
|
+
// RIGHT
|
|
225
|
+
<ScSelect label="Role" labelGroup="label" options={opts} value={v} onChange={setV} />
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**4. No keyboard navigation inside the list.** Options are `div`s with `onClick`.
|
|
229
|
+
Keyboard and screen-reader users can open the button but cannot reach the options.
|
|
230
|
+
Don't use it as the only path to a critical setting until this is fixed in the DS.
|
|
231
|
+
|
|
232
|
+
**5. There is no `style` prop and no prop spreading.** `className` is the whole
|
|
233
|
+
API surface for layout. You cannot attach `data-testid`, `id`, `onBlur` or
|
|
234
|
+
`aria-label` to the root.
|
|
235
|
+
|
|
236
|
+
**6. The chevron is a hardcoded inline SVG,** not an `Sicon*`, and there is no
|
|
237
|
+
left-icon or adornment slot on the trigger. If the spec puts an avatar, a colour
|
|
238
|
+
swatch or a custom caret in the closed state, you cannot express it here — use the
|
|
239
|
+
`ScTextField`-as-trigger pattern instead.
|
|
240
|
+
|
|
241
|
+
**7. Fonts are hardcoded `Inter, sans-serif`** in this stylesheet rather than
|
|
242
|
+
`--text-text-sm-regular-font-family` like the other fields. Sizes/colours are
|
|
243
|
+
tokenised; the family is not. Harmless today, a drift risk if the body font
|
|
244
|
+
changes.
|
|
245
|
+
|
|
246
|
+
**8. Open state is internal.** There is no `open`/`onOpenChange` prop, so you
|
|
247
|
+
cannot open it programmatically or coordinate it with another popover.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## In the wild
|
|
252
|
+
|
|
253
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
254
|
+
|
|
255
|
+
Most likely home: the Catalogix feed **Map-Attributes** target rows
|
|
256
|
+
(`ScMappingCard`'s `targets` slot) and simple settings dropdowns — but note
|
|
257
|
+
Gotcha 2, since `ScMappingCard` sets `overflow: hidden`. Today Catalogix uses
|
|
258
|
+
`app/components/DsDropdown` (an `ScTextField` trigger with its own menu) and
|
|
259
|
+
`react-select` for the portalled cases.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Related
|
|
264
|
+
|
|
265
|
+
- `ScTextField` — what the trigger is modelled on; also the current host pattern
|
|
266
|
+
for hand-rolled dropdowns.
|
|
267
|
+
- `ScTabField` — a segmented field when there are only 2–4 options.
|
|
268
|
+
- `ScPopUpMenu` / `ScMenuOptions` — action menus, not value pickers.
|
|
269
|
+
- `ScSelectionPillGroup` — visible segmented filters over a list.
|
|
270
|
+
- `ScSelection` / `ScSelectionList` — the unrelated in-chat family (agent runtime).
|