@streamoid/ui 0.6.17 → 0.6.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -18
- package/dist/docs/AGENTS.md +325 -0
- package/dist/docs/CreditWarningBanner.md +305 -0
- package/dist/docs/InvoiceHistoryMobile.md +222 -0
- package/dist/docs/ScAccess.md +259 -0
- package/dist/docs/ScAppCard.md +244 -0
- package/dist/docs/ScAppCardForCopilot.md +230 -0
- package/dist/docs/ScAppCardV3.md +273 -0
- package/dist/docs/ScAppField.md +308 -0
- package/dist/docs/ScAppListingCard.md +271 -0
- package/dist/docs/ScAppSwitchPanel.md +286 -0
- package/dist/docs/ScAppcardLogos.md +226 -0
- package/dist/docs/ScArtifaxInvite.md +262 -0
- package/dist/docs/ScArtifaxSidebar.md +330 -0
- package/dist/docs/ScAskAgentButton.md +307 -0
- package/dist/docs/ScBadges.md +261 -0
- package/dist/docs/ScBeacon.md +244 -0
- package/dist/docs/ScBillingHistoryHeader.md +210 -0
- package/dist/docs/ScBillingHistoryTableList.md +243 -0
- package/dist/docs/ScBillingLogsTableHeader.md +212 -0
- package/dist/docs/ScBillingLogsTableList.md +251 -0
- package/dist/docs/ScBriefCard.md +255 -0
- package/dist/docs/ScButton.md +251 -0
- package/dist/docs/ScCalendar.md +268 -0
- package/dist/docs/ScCalendarDateComps.md +264 -0
- package/dist/docs/ScCatalogixInvite.md +345 -0
- package/dist/docs/ScCatalogixSidebar.md +337 -0
- package/dist/docs/ScCatalogixStoreHeader.md +246 -0
- package/dist/docs/ScCatalogixStoreTableList.md +316 -0
- package/dist/docs/ScCheckField.md +233 -0
- package/dist/docs/ScCheckbox.md +272 -0
- package/dist/docs/ScCounter.md +235 -0
- package/dist/docs/ScCreditsUsageCard.md +247 -0
- package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
- package/dist/docs/ScDefaultCard.md +269 -0
- package/dist/docs/ScDp.md +245 -0
- package/dist/docs/ScDrawer.md +318 -0
- package/dist/docs/ScFieldButton.md +255 -0
- package/dist/docs/ScFileField.md +268 -0
- package/dist/docs/ScGoogleSignIn.md +250 -0
- package/dist/docs/ScGuide.md +278 -0
- package/dist/docs/ScHDivider.md +210 -0
- package/dist/docs/ScHeader.md +222 -0
- package/dist/docs/ScImageField.md +253 -0
- package/dist/docs/ScInChatList.md +277 -0
- package/dist/docs/ScInChatMessage.md +205 -0
- package/dist/docs/ScInfoPopup.md +248 -0
- package/dist/docs/ScIntialProfileCover.md +233 -0
- package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
- package/dist/docs/ScLogoUnit.md +232 -0
- package/dist/docs/ScMappingCard.md +241 -0
- package/dist/docs/ScMediaApproval.md +301 -0
- package/dist/docs/ScMediaSelect.md +310 -0
- package/dist/docs/ScMenuOptions.md +308 -0
- package/dist/docs/ScMobileBottomAction.md +252 -0
- package/dist/docs/ScMobileTopNav.md +279 -0
- package/dist/docs/ScModal.md +291 -0
- package/dist/docs/ScOnlyField.md +302 -0
- package/dist/docs/ScOnlyIcon.md +213 -0
- package/dist/docs/ScPagination.md +284 -0
- package/dist/docs/ScPairtext.md +287 -0
- package/dist/docs/ScPendingAction.md +238 -0
- package/dist/docs/ScPhtogenixInvite.md +275 -0
- package/dist/docs/ScPlanCard.md +302 -0
- package/dist/docs/ScPlanComparison.md +264 -0
- package/dist/docs/ScPlanDetailsCard.md +246 -0
- package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
- package/dist/docs/ScPopUpMenu.md +224 -0
- package/dist/docs/ScProfile.md +234 -0
- package/dist/docs/ScProfileImageUpdate.md +261 -0
- package/dist/docs/ScProfileOptions.md +245 -0
- package/dist/docs/ScProfilePopup.md +396 -0
- package/dist/docs/ScProfileSettingsComp.md +250 -0
- package/dist/docs/ScProfileV2Mobile.md +216 -0
- package/dist/docs/ScProgressBar.md +267 -0
- package/dist/docs/ScQuickPrompt.md +277 -0
- package/dist/docs/ScRadio.md +228 -0
- package/dist/docs/ScReferralCardMobile.md +226 -0
- package/dist/docs/ScReferralTableHeader.md +260 -0
- package/dist/docs/ScReferralTableList.md +293 -0
- package/dist/docs/ScRole.md +226 -0
- package/dist/docs/ScRoleMobile.md +199 -0
- package/dist/docs/ScSelect.md +270 -0
- package/dist/docs/ScSelection.md +256 -0
- package/dist/docs/ScSelectionList.md +272 -0
- package/dist/docs/ScSelectionPill.md +240 -0
- package/dist/docs/ScSelectionPillGroup.md +302 -0
- package/dist/docs/ScSettingsNav.md +212 -0
- package/dist/docs/ScSettingsTabComp.md +260 -0
- package/dist/docs/ScSideBarLogoUnit.md +340 -0
- package/dist/docs/ScSidebar.md +243 -0
- package/dist/docs/ScSidebarIcons.md +232 -0
- package/dist/docs/ScSidebarMenu.md +283 -0
- package/dist/docs/ScSidebarProfile.md +231 -0
- package/dist/docs/ScSidebarSwitchMenu.md +258 -0
- package/dist/docs/ScSlider.md +194 -0
- package/dist/docs/ScStoreCard.md +252 -0
- package/dist/docs/ScStrLogo.md +253 -0
- package/dist/docs/ScStreamoidWordmark.md +302 -0
- package/dist/docs/ScSubAgent.md +226 -0
- package/dist/docs/ScTabComp.md +308 -0
- package/dist/docs/ScTabField.md +258 -0
- package/dist/docs/ScTabSwitcher.md +307 -0
- package/dist/docs/ScTableHeader.md +261 -0
- package/dist/docs/ScTableList.md +301 -0
- package/dist/docs/ScTableListMobile.md +282 -0
- package/dist/docs/ScTabs.md +268 -0
- package/dist/docs/ScTaxonomyPill.md +263 -0
- package/dist/docs/ScTextArea.md +259 -0
- package/dist/docs/ScTextField.md +324 -0
- package/dist/docs/ScThinkingStepIcon.md +249 -0
- package/dist/docs/ScTodoList.md +288 -0
- package/dist/docs/ScToggleSwitch.md +229 -0
- package/dist/docs/ScUsageHistoryMobile.md +194 -0
- package/dist/docs/ScVDivider.md +215 -0
- package/dist/docs/ScValueMappingL1.md +256 -0
- package/dist/docs/ScVersion.md +251 -0
- package/dist/docs/ScWorkspace.md +233 -0
- package/dist/docs/ScWorkspaceAccountMenu.md +115 -0
- package/dist/docs/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +312 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +413 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4931 -0
- package/dist/index.css +361 -36
- package/dist/index.d.mts +213 -88
- package/dist/index.d.ts +213 -88
- package/dist/index.js +2486 -1629
- package/dist/index.mjs +2487 -1620
- package/package.json +5 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScOnlyField
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: forms
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div > div > input
|
|
7
|
+
tags: [input, search, searchbox, bare-input, unlabelled, compact, size, sidebar-search, filter]
|
|
8
|
+
related: [ScTextField, ScTextArea, ScSelect, ScOnlyIcon, ScFieldButton]
|
|
9
|
+
do_not_confuse_with: [ScOnlyIcon, ScTextField, ScAppField]
|
|
10
|
+
used_by: [cxo, catalogix, photogenix, artifax]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScOnlyField
|
|
14
|
+
|
|
15
|
+
**A bare input in a pill — no label, ever.** One rounded container, an optional
|
|
16
|
+
icon on either side, and a borderless `<input>` inside. Unlike `ScTextField` it
|
|
17
|
+
has three heights (`48 / 40 / 32 px`) and reports changes as a plain string via
|
|
18
|
+
`onInputChange`. This is the component behind almost every search box in the
|
|
19
|
+
product.
|
|
20
|
+
|
|
21
|
+
## TL;DR for agents
|
|
22
|
+
|
|
23
|
+
- **Reach for it when:** you need a search box, a filter input, or a compact
|
|
24
|
+
input inside a toolbar, sidebar or modal row — with no label above it.
|
|
25
|
+
- **Don't reach for it when:** the field needs a label, a `*`, a helper line or a
|
|
26
|
+
ref (→ `ScTextField`), or it is multi-line (→ `ScTextArea`).
|
|
27
|
+
- **Five things that will bite you:**
|
|
28
|
+
1. `tfGroup` defaults to `"icon-right"` and `tfRightIcon` defaults to a
|
|
29
|
+
**lightning bolt**. Pass `tfGroup="none"` or supply a real icon.
|
|
30
|
+
2. `labelGroup` is **vestigial** — this component renders no label element at
|
|
31
|
+
all. There is no `label` prop either.
|
|
32
|
+
3. `state="active"`, `state="error"` and `state="disabled"` paint **nothing**.
|
|
33
|
+
Only `filled` (and `:hover`) have styles; the focus rule in the stylesheet
|
|
34
|
+
is misspelled `state-ative-focused` and is unreachable.
|
|
35
|
+
4. It's fully controlled but has **no `onChange`** — use `onInputChange(value)`.
|
|
36
|
+
Passing `inputProps.onChange` silently replaces it.
|
|
37
|
+
5. `inputProps.style` **replaces** the component's inline input styles, which
|
|
38
|
+
brings back the browser's default input border and background.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 1. How to use it
|
|
43
|
+
|
|
44
|
+
### Import
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { ScOnlyField } from "@streamoid/ui";
|
|
48
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Minimal usage
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<ScOnlyField
|
|
55
|
+
tfGroup="icon-right"
|
|
56
|
+
tfRightIcon={<SiconSearch />}
|
|
57
|
+
placeholderFilled="Search…"
|
|
58
|
+
value={query}
|
|
59
|
+
onInputChange={setQuery}
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Props
|
|
64
|
+
|
|
65
|
+
Extends `React.HTMLAttributes<HTMLDivElement>`; unlisted props (`style`,
|
|
66
|
+
`onClick`, `data-*`, `id`) are spread onto the **root div**.
|
|
67
|
+
|
|
68
|
+
| Prop | Type | Default | Notes |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| `value` | `string` | – | Controlled value. No internal state — without `onInputChange` typing does nothing. |
|
|
71
|
+
| `onInputChange` | `(value: string) => void` | – | Receives the **string**, not an event. The only change hook. |
|
|
72
|
+
| `size` | `"default"` \| `"medium"` \| `"small"` | `"default"` | 48 px / 40 px / 32 px tall; radius `3xl`/`xl`/`lg`; icon box 24/20/16 px. |
|
|
73
|
+
| `tfGroup` | `"icon-right"` \| `"icon-left"` \| `"icon-left-right"` \| `"none"` | `"icon-right"` | ⚠️ Which icons render. `"none"` for a plain input. |
|
|
74
|
+
| `tfRightIcon` | `JSX.Element` | `<SiconBolt />` | ⚠️ Real default. Force-sized by CSS to the `size` icon box. |
|
|
75
|
+
| `tfLeftIcon` | `JSX.Element` | `<SiconBolt />` | ⚠️ Real default. Same sizing. |
|
|
76
|
+
| `placeholderFilled` | `string` | `"Placeholder"` | ⚠️ Real default. Overridable via `inputProps.placeholder`. |
|
|
77
|
+
| `state` | `"default"` \| `"active"` \| `"error"` \| `"disabled"` \| `"filled"` | `"default"` | Only `"filled"` changes anything (raised fill). See Gotcha 3. |
|
|
78
|
+
| `labelGroup` | `"none"` \| `"label"` \| `"text"` \| `"icon"` | `"none"` | ⚠️ **Dead prop** — no label is ever rendered. |
|
|
79
|
+
| `inputProps` | `React.InputHTMLAttributes<HTMLInputElement>` | – | Spread onto the `<input>` **after** the component's own attributes, so it can override `onChange`, `placeholder` and `style`. Use it for `autoFocus`, `maxLength`, `onKeyDown`, `readOnly`. |
|
|
80
|
+
| `className` | `string` | – | Appended to the root class list. |
|
|
81
|
+
|
|
82
|
+
#### What each `state` actually paints
|
|
83
|
+
|
|
84
|
+
| `state` | Effect |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `default` | base fill (`fill-neutral-neutralplus`); in light mode a 0.5 px subtle border |
|
|
87
|
+
| `filled` | raised fill (`fill-neutral-neutral`); light mode border → `border-default` |
|
|
88
|
+
| `active` | **nothing** — the stylesheet rule is typo'd `state-ative-focused` |
|
|
89
|
+
| `error` | **nothing** — no rule exists |
|
|
90
|
+
| `disabled` | **nothing** — no rule exists, and the input stays editable |
|
|
91
|
+
| `:hover` | same as `filled` (real CSS hover, always live) |
|
|
92
|
+
|
|
93
|
+
### Recipes
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// Toolbar / panel search — the CXO app-switcher idiom
|
|
97
|
+
<ScOnlyField
|
|
98
|
+
tfGroup="icon-right"
|
|
99
|
+
size="medium"
|
|
100
|
+
tfRightIcon={<SiconSearch className="w-6 h-6" color="var(--alias-text-and-icons-primary)" />}
|
|
101
|
+
placeholderFilled="Search..."
|
|
102
|
+
value={searchQuery}
|
|
103
|
+
onInputChange={setSearchQuery}
|
|
104
|
+
style={{ width: 256 }} // style lands on the ROOT (root is width:100%)
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
// Repeated option rows with a trailing delete — the Catalogix taxonomy idiom
|
|
108
|
+
<ScOnlyField
|
|
109
|
+
value={option.value}
|
|
110
|
+
placeholderFilled="Option 1"
|
|
111
|
+
state={hasValue ? "filled" : "default"}
|
|
112
|
+
tfGroup={hasValue ? "icon-right" : "none"}
|
|
113
|
+
tfRightIcon={
|
|
114
|
+
<SiconDelete
|
|
115
|
+
size={24}
|
|
116
|
+
color="var(--alias-text-and-icons-error)"
|
|
117
|
+
style={{ cursor: "pointer" }}
|
|
118
|
+
onClick={removeOption}
|
|
119
|
+
/>
|
|
120
|
+
}
|
|
121
|
+
onInputChange={(val) => updateOption(idx, val)}
|
|
122
|
+
inputProps={{ autoFocus: shouldFocus, onKeyDown: onEnterAddRow }}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
// Dense 32 px input inside a table row
|
|
126
|
+
<ScOnlyField size="small" tfGroup="none" value={v} onInputChange={setV} />
|
|
127
|
+
|
|
128
|
+
// Truly disabled — the `state` prop won't do it
|
|
129
|
+
<ScOnlyField tfGroup="none" value={v} inputProps={{ disabled: true }} />
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 2. Where to use it
|
|
135
|
+
|
|
136
|
+
- **Sidebar / workspace-listing search** — Photogenix `Sidebar.tsx`, Artifax
|
|
137
|
+
`DashboardSidebar.tsx`, Catalogix `LeftMenu/WorkspaceListing`.
|
|
138
|
+
- **App-switcher and panel headers** — CXO `app-switcher.tsx` (256 px, medium).
|
|
139
|
+
- **Store / member filters** — CXO `teams-content.tsx`, the
|
|
140
|
+
`@streamoid/settings` teams and organization screens.
|
|
141
|
+
- **Repeatable value rows in modals** — Catalogix taxonomy "Options" lists,
|
|
142
|
+
where each row is an `ScOnlyField` with a trailing red delete icon.
|
|
143
|
+
- **Composed inside other DS components** — `ScCatalogixInvite` renders one as
|
|
144
|
+
its store-search box.
|
|
145
|
+
|
|
146
|
+
All four host apps use it; it is part of the universal core.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 3. When to use it
|
|
151
|
+
|
|
152
|
+
### Use it when
|
|
153
|
+
|
|
154
|
+
- There is **no label** — the surrounding heading or icon already says what the
|
|
155
|
+
input is for.
|
|
156
|
+
- You need a **40 px or 32 px** field; `ScTextField` is fixed at 48 px.
|
|
157
|
+
- The value is a plain string in local state and you want the terser
|
|
158
|
+
`onInputChange` signature.
|
|
159
|
+
|
|
160
|
+
### Don't use it — reach for this instead
|
|
161
|
+
|
|
162
|
+
| Situation | Use instead |
|
|
163
|
+
|---|---|
|
|
164
|
+
| The field needs a label, a `*`, an info popover or helper text | `ScTextField` |
|
|
165
|
+
| You need a `ref` on the input, or full native `onChange`/form props | `ScTextField` (it forwards a ref) |
|
|
166
|
+
| Multi-line input | `ScTextArea` |
|
|
167
|
+
| Pick from a list | `ScSelect` |
|
|
168
|
+
| A visible error state on the field | `ScTextField` + `desc`, or `ScTextArea` (which has a real `state="error"`) |
|
|
169
|
+
| An icon-only button (no text entry) | `ScButton` with `styleVariant="icon-only"` |
|
|
170
|
+
| A non-editable icon/control + label row | `ScPairtext` (one text line, no input — **not** a label:value pair) |
|
|
171
|
+
|
|
172
|
+
### Don't confuse with
|
|
173
|
+
|
|
174
|
+
| You may actually want | Not this |
|
|
175
|
+
|---|---|
|
|
176
|
+
| `ScOnlyIcon` — a 40 px icon square with a hover background, **not an input** | `ScOnlyField` is a text input. The names are one character apart. |
|
|
177
|
+
| `ScTextField` — labelled, ref-forwarding, native `onChange`, 48 px only | `ScOnlyField` is unlabelled, no ref, `onInputChange`, three sizes |
|
|
178
|
+
| `ScAppField` — a hardcoded three-app permission block, not a generic field | neither of these |
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 4. Why to use it
|
|
183
|
+
|
|
184
|
+
- **Three sizes that match the spec.** Height, padding, radius *and* icon box all
|
|
185
|
+
step together (48/40/32 → 24/20/16 icons). Hand-rolled search boxes get the
|
|
186
|
+
icon size wrong almost every time.
|
|
187
|
+
- **Icons are force-sized.** `.iconWrapper > *` sets `width/height: 100%`, so any
|
|
188
|
+
`Sicon*` you pass lands at exactly the right box size regardless of its own
|
|
189
|
+
`size` prop.
|
|
190
|
+
- **Light mode is explicitly handled.** The neutral fills are near-white in the
|
|
191
|
+
light theme, so `[data-theme="Light"]` adds a persistent 0.5 px border across
|
|
192
|
+
all states — which also removes the 1 px layout shift on focus.
|
|
193
|
+
- **The placeholder uses the muted token** via `::placeholder`, so it can't drift
|
|
194
|
+
from the rest of the product.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Gotchas
|
|
199
|
+
|
|
200
|
+
**1. The default field contains a lightning bolt.**
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
// WRONG — renders a ⚡ on the right
|
|
204
|
+
<ScOnlyField value={q} onInputChange={setQ} />
|
|
205
|
+
|
|
206
|
+
// RIGHT
|
|
207
|
+
<ScOnlyField tfGroup="none" value={q} onInputChange={setQ} />
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**2. There is no label and no `label` prop.** `labelGroup` is accepted and
|
|
211
|
+
ignored — the component's JSX has no label element. If you need one, either use
|
|
212
|
+
`ScTextField` or render your own text above it.
|
|
213
|
+
|
|
214
|
+
**3. Only `filled` is a real state.** `active` is misspelled in the stylesheet
|
|
215
|
+
(`state-ative-focused`), and `error`/`disabled` have no rules at all. So:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
// WRONG — looks identical to default, and is still editable
|
|
219
|
+
<ScOnlyField state="disabled" value={v} />
|
|
220
|
+
|
|
221
|
+
// RIGHT
|
|
222
|
+
<ScOnlyField tfGroup="none" value={v} inputProps={{ disabled: true }} />
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
For an error, put the red on the icon or draw the border yourself via a
|
|
226
|
+
`className` on the root — the component gives you nothing.
|
|
227
|
+
|
|
228
|
+
**4. `inputProps` overrides the component's own input wiring.** It is spread
|
|
229
|
+
last, so `inputProps.onChange` replaces the internal handler and `onInputChange`
|
|
230
|
+
stops firing.
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// WRONG — onInputChange never fires
|
|
234
|
+
<ScOnlyField value={v} onInputChange={setV} inputProps={{ onChange: log }} />
|
|
235
|
+
|
|
236
|
+
// RIGHT — keep one owner of the change
|
|
237
|
+
<ScOnlyField value={v} inputProps={{ onChange: (e) => { setV(e.target.value); log(e); } }} />
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**5. `inputProps.style` wipes the transparent-input styling.** The `<input>`
|
|
241
|
+
carries inline `background: transparent; border: none; outline: none; font:
|
|
242
|
+
inherit; color: primary; width: 100%; padding: 0`. Passing `style` replaces the
|
|
243
|
+
whole object, so the native grey box comes back. Pass only the properties you
|
|
244
|
+
need *plus* those, or style via `className` on the root instead.
|
|
245
|
+
|
|
246
|
+
**6. No ref is forwarded.** For autofocus use `inputProps={{ autoFocus: true }}`
|
|
247
|
+
(Catalogix does). If you genuinely need a DOM handle, use `ScTextField`.
|
|
248
|
+
|
|
249
|
+
**7. `style` lands on the root, not the input.** The root is `width: 100%`, so
|
|
250
|
+
`style={{ width: 256 }}` is the right way to make it narrower.
|
|
251
|
+
|
|
252
|
+
**8. Typing does nothing without `onInputChange`.** `value` is passed straight to
|
|
253
|
+
a controlled input. This looks like a broken field, not a read-only one — if you
|
|
254
|
+
want read-only, say `inputProps={{ readOnly: true }}`.
|
|
255
|
+
|
|
256
|
+
**9. The class attribute contains the literal `undefined`** when `className` is
|
|
257
|
+
omitted (raw string concatenation) and for state/group names with no matching
|
|
258
|
+
rule. Harmless, but don't write CSS or tests that depend on the class list.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## In the wild
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
// cxo-dashboard src/app/components/app-switcher.tsx:246
|
|
266
|
+
<ScOnlyField
|
|
267
|
+
tfGroup="icon-right"
|
|
268
|
+
size="medium"
|
|
269
|
+
tfRightIcon={<SiconSearch className="w-6 h-6" color={PRIMARY_ICON} />}
|
|
270
|
+
placeholderFilled="Search..."
|
|
271
|
+
value={searchQuery}
|
|
272
|
+
onInputChange={setSearchQuery}
|
|
273
|
+
style={{ width: 256 }}
|
|
274
|
+
/>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
```jsx
|
|
278
|
+
// catalogix/dashboard app/components/TaxonomyComponent/AddHierarchyModal/AddHierarchyContent.jsx:379
|
|
279
|
+
<ScOnlyField
|
|
280
|
+
key={indx}
|
|
281
|
+
value={option.value}
|
|
282
|
+
placeholderFilled="Option 1"
|
|
283
|
+
state={option.error ? "error" : hasValue ? "filled" : "default"}
|
|
284
|
+
tfGroup={hasValue ? "icon-right" : "none"}
|
|
285
|
+
tfRightIcon={<SiconDelete size={24} color="var(--alias-text-and-icons-error)" onClick={removeOption} />}
|
|
286
|
+
onInputChange={(val) => { /* writes optionValues[indx].value, clears the error */ }}
|
|
287
|
+
inputProps={{ autoFocus: changeFocus, /* … */ }}
|
|
288
|
+
/>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
*(Note: that call site's `state="error"` renders no error styling — see Gotcha 3.)*
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Related
|
|
296
|
+
|
|
297
|
+
- `ScTextField` — use it the moment you need a label, a ref, or helper text.
|
|
298
|
+
- `ScTextArea` — multi-line sibling with real `error`/`disabled` skins.
|
|
299
|
+
- `ScSelect` — dropdown instead of free text.
|
|
300
|
+
- `ScOnlyIcon` — the confusable namesake; an icon square, not an input.
|
|
301
|
+
- `ScFieldButton` — the compact action that sits beside a field.
|
|
302
|
+
- `@streamoid/icons` — `SiconSearch`, `SiconDelete`, `SiconClose`; see `packages/icons/ICONS.md`.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScOnlyIcon
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: actions
|
|
5
|
+
status: legacy
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [icon-button, icon-only, settings-icon, square, hover, affordance, orphan]
|
|
8
|
+
related: [ScButton, ScFieldButton, ScSidebarIcons, ScMenuOptions]
|
|
9
|
+
do_not_confuse_with: [ScOnlyField, ScButton, ScFieldButton, ScSidebarIcons]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScOnlyIcon
|
|
13
|
+
|
|
14
|
+
**A 40 × 40 hover square that always renders the settings gear.** That is the
|
|
15
|
+
whole component: a `div` with 8 px padding, a 16 px radius, a hover background,
|
|
16
|
+
and a hardcoded `<SiconSettings />` inside. **There is no `icon` prop.**
|
|
17
|
+
|
|
18
|
+
> **Use `ScButton` with `styleVariant="icon-only"` instead.** `ScOnlyIcon` is a
|
|
19
|
+
> Figma-generated stub with no consumers anywhere — not in the four host
|
|
20
|
+
> dashboards, and not inside `@streamoid/ui` itself. It cannot render any icon
|
|
21
|
+
> other than the gear, has no keyboard or `aria` semantics, and has no disabled
|
|
22
|
+
> state.
|
|
23
|
+
|
|
24
|
+
## TL;DR for agents
|
|
25
|
+
|
|
26
|
+
- **Reach for it when:** essentially never. The only honest use is a bare
|
|
27
|
+
settings-gear affordance where you also don't need keyboard access.
|
|
28
|
+
- **Don't reach for it when:** you need *any* other icon, a11y, a disabled state,
|
|
29
|
+
variants or sizes → `ScButton` (`styleVariant="icon-only"`).
|
|
30
|
+
- **Three things that will bite you:**
|
|
31
|
+
1. `icon` **does not exist**. Passing one is a type error and would be ignored.
|
|
32
|
+
2. `variant` accepts only `"default"` and maps to a class that isn't in the
|
|
33
|
+
stylesheet, so it does nothing.
|
|
34
|
+
3. It is a `<div>` — no `role`, no `tabIndex`, no Enter/Space, no focus ring.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. How to use it
|
|
39
|
+
|
|
40
|
+
### Import
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { ScOnlyIcon } from "@streamoid/ui";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Minimal usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<ScOnlyIcon onClick={openSettings} />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Props
|
|
54
|
+
|
|
55
|
+
Extends `React.HTMLAttributes<HTMLDivElement>`; unlisted props (`onClick`,
|
|
56
|
+
`style`, `title`, `aria-*`, `data-*`) are spread onto the root div.
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Default | Notes |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `variant` | `"default"` | `"default"` | Dead prop — resolves to a class (`variant-default`) that the stylesheet does not define. |
|
|
61
|
+
| `className` | `string` | – | Appended to the root class list. The only real styling hook. |
|
|
62
|
+
|
|
63
|
+
There is **no** `icon`, `size`, `state`, `disabled`, `active` or `label` prop.
|
|
64
|
+
|
|
65
|
+
#### What actually renders
|
|
66
|
+
|
|
67
|
+
| Part | Value |
|
|
68
|
+
|---|---|
|
|
69
|
+
| Root | `div`, `padding: 0.5rem`, `border-radius: var(--radius-3xl, 1rem)`, flex row |
|
|
70
|
+
| Icon | `<SiconSettings />` at its default `size={24}`, `stroke: currentColor` |
|
|
71
|
+
| Total box | **40 × 40 px** — derived (24 px icon + 2 × 8 px padding); the stylesheet sets no `width`/`height` |
|
|
72
|
+
| Hover | background `--alias-fill-neutral-neutralhover` |
|
|
73
|
+
| Icon colour | inherited `currentColor` from the parent — set `color` on an ancestor to change it |
|
|
74
|
+
|
|
75
|
+
### Recipes
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// The only sane call: a gear that opens settings, with semantics bolted on
|
|
79
|
+
<ScOnlyIcon
|
|
80
|
+
role="button"
|
|
81
|
+
tabIndex={0}
|
|
82
|
+
aria-label="Settings"
|
|
83
|
+
onClick={openSettings}
|
|
84
|
+
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") openSettings(); }}
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
// Recolour the gear (it inherits currentColor)
|
|
88
|
+
<span style={{ color: "var(--alias-text-and-icons-tertiary)" }}>
|
|
89
|
+
<ScOnlyIcon onClick={openSettings} />
|
|
90
|
+
</span>
|
|
91
|
+
|
|
92
|
+
// What you almost certainly want instead
|
|
93
|
+
<ScButton
|
|
94
|
+
styleVariant="icon-only"
|
|
95
|
+
icon={<SiconSettings size={24} />}
|
|
96
|
+
variant="mono"
|
|
97
|
+
type="tertiary"
|
|
98
|
+
size="md"
|
|
99
|
+
aria-label="Settings"
|
|
100
|
+
onClick={openSettings}
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 2. Where to use it
|
|
107
|
+
|
|
108
|
+
Nowhere, currently. Grepping CXO, Catalogix, Photogenix and Artifax — and
|
|
109
|
+
`packages/ui/src` itself — finds no render site. Its intended home was clearly a
|
|
110
|
+
toolbar or panel-header gear.
|
|
111
|
+
|
|
112
|
+
The surfaces that *look* like its job are already served by other components:
|
|
113
|
+
|
|
114
|
+
| Surface | What actually renders there |
|
|
115
|
+
|---|---|
|
|
116
|
+
| Sidebar rail icons | `ScSidebarIcons` / `StreamoidSidebar` |
|
|
117
|
+
| Header / toolbar icon actions | `ScButton styleVariant="icon-only"` |
|
|
118
|
+
| Composer toolbar actions | `ScFieldButton type="only-icon"` |
|
|
119
|
+
| Row overflow menus | `ScPopUpMenu` + `ScMenuOptions` |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 3. When to use it
|
|
124
|
+
|
|
125
|
+
### Use it when
|
|
126
|
+
|
|
127
|
+
- You want a bare gear icon with the DS hover square and you are fine adding
|
|
128
|
+
`role`/`tabIndex`/`aria-label` yourself.
|
|
129
|
+
|
|
130
|
+
That is the complete list.
|
|
131
|
+
|
|
132
|
+
### Don't use it — reach for this instead
|
|
133
|
+
|
|
134
|
+
| Situation | Use instead |
|
|
135
|
+
|---|---|
|
|
136
|
+
| Any icon other than the settings gear | `ScButton styleVariant="icon-only"` with `icon={…}` |
|
|
137
|
+
| The icon button needs keyboard access, a focus ring, `aria-disabled` | `ScButton` |
|
|
138
|
+
| A disabled/loading state | `ScButton` (`state="disabled"`, `loading`) |
|
|
139
|
+
| A compact icon affordance inside a composer or beside a field | `ScFieldButton type="only-icon"` (which *does* take an `icon`) |
|
|
140
|
+
| A sidebar rail icon with active state | `ScSidebarIcons` / `StreamoidSidebar` |
|
|
141
|
+
| An icon that opens a menu | `ScPopUpMenu` |
|
|
142
|
+
|
|
143
|
+
### Don't confuse with
|
|
144
|
+
|
|
145
|
+
| You may actually want | Not this |
|
|
146
|
+
|---|---|
|
|
147
|
+
| **`ScOnlyField`** — a text **input** in a pill. One character apart from this name and a completely different component. | `ScOnlyIcon` renders no input |
|
|
148
|
+
| `ScFieldButton` with `type="only-icon"` — a 36 px chip that accepts **your** icon | `ScOnlyIcon`'s icon is hardcoded |
|
|
149
|
+
| `ScButton` with `styleVariant="icon-only"` — the supported icon button | `ScOnlyIcon` has no variants, states or semantics |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 4. Why to use it
|
|
154
|
+
|
|
155
|
+
Honestly: there is little reason. What it does give you is the 8 px / 16 px-radius
|
|
156
|
+
hover square that matches other icon affordances, in ~20 lines with no variant
|
|
157
|
+
logic. Everything else — icon choice, colour normalisation, keyboard access,
|
|
158
|
+
disabled handling, Figma variant parity — you get from `ScButton` and not from
|
|
159
|
+
here.
|
|
160
|
+
|
|
161
|
+
If you keep it, keep it for the gear only, and add the semantics yourself.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Gotchas
|
|
166
|
+
|
|
167
|
+
**1. You cannot change the icon.**
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// WRONG — type error, and there is no code path that would render it
|
|
171
|
+
<ScOnlyIcon icon={<SiconClose />} />
|
|
172
|
+
|
|
173
|
+
// RIGHT
|
|
174
|
+
<ScButton styleVariant="icon-only" icon={<SiconClose size={24} />} aria-label="Close" />
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**2. `variant` does nothing.** The component computes `styles["variant-default"]`,
|
|
178
|
+
which is not defined in `ScOnlyIcon.module.css`, so the literal string `undefined`
|
|
179
|
+
lands in the class attribute (raw concatenation also adds `undefined` when
|
|
180
|
+
`className` is omitted). Don't write CSS or tests against this class list.
|
|
181
|
+
|
|
182
|
+
**3. `.hover-true` is unreachable.** The stylesheet has a forced-hover class for
|
|
183
|
+
Figma parity, but no prop sets it. Only real `:hover` works — you cannot
|
|
184
|
+
screenshot the hover state via props the way `ScButton state="hover"` allows.
|
|
185
|
+
|
|
186
|
+
**4. No semantics at all.** `div` with a spread `onClick`. Add
|
|
187
|
+
`role="button" tabIndex={0} aria-label="…"` and an `onKeyDown`, or use `ScButton`.
|
|
188
|
+
|
|
189
|
+
**5. No size control.** There is no `size` prop: the box is whatever the icon plus
|
|
190
|
+
`0.5rem` of padding comes to — 40 × 40 today, because the stylesheet gives
|
|
191
|
+
`.siconSettingsInstance` no sizing rule and `Sicon*` defaults to `size={24}`. The
|
|
192
|
+
CSS itself declares no `width`/`height`, so the only lever is a `className` on the
|
|
193
|
+
root plus a descendant rule.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## In the wild
|
|
198
|
+
|
|
199
|
+
_No host render site found — used by the agent runtime / composed internally._
|
|
200
|
+
|
|
201
|
+
To be precise: not used by the agent runtime either — this one is a genuine
|
|
202
|
+
orphan. If a gear affordance is needed, it belongs in a header/toolbar row beside
|
|
203
|
+
an `ScHeader`, and should be built with `ScButton styleVariant="icon-only"`.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Related
|
|
208
|
+
|
|
209
|
+
- `ScButton` — `styleVariant="icon-only"`; the supported way to render an icon button.
|
|
210
|
+
- `ScFieldButton` — `type="only-icon"`; compact 36 px chip that accepts your icon.
|
|
211
|
+
- `ScOnlyField` — the near-identical *name* that is actually a text input.
|
|
212
|
+
- `ScSidebarIcons` / `StreamoidSidebar` — sidebar rail icons with active states.
|
|
213
|
+
- `@streamoid/icons` — `SiconSettings` and every other icon; see `packages/icons/ICONS.md`.
|