@streamoid/ui 0.6.17 → 0.6.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -18
- package/dist/docs/AGENTS.md +321 -0
- package/dist/docs/CreditWarningBanner.md +305 -0
- package/dist/docs/InvoiceHistoryMobile.md +222 -0
- package/dist/docs/ScAccess.md +259 -0
- package/dist/docs/ScAppCard.md +244 -0
- package/dist/docs/ScAppCardForCopilot.md +230 -0
- package/dist/docs/ScAppCardV3.md +273 -0
- package/dist/docs/ScAppField.md +308 -0
- package/dist/docs/ScAppListingCard.md +271 -0
- package/dist/docs/ScAppSwitchPanel.md +286 -0
- package/dist/docs/ScAppcardLogos.md +226 -0
- package/dist/docs/ScArtifaxInvite.md +262 -0
- package/dist/docs/ScArtifaxSidebar.md +330 -0
- package/dist/docs/ScAskAgentButton.md +307 -0
- package/dist/docs/ScBadges.md +261 -0
- package/dist/docs/ScBeacon.md +244 -0
- package/dist/docs/ScBillingHistoryHeader.md +210 -0
- package/dist/docs/ScBillingHistoryTableList.md +243 -0
- package/dist/docs/ScBillingLogsTableHeader.md +212 -0
- package/dist/docs/ScBillingLogsTableList.md +251 -0
- package/dist/docs/ScBriefCard.md +255 -0
- package/dist/docs/ScButton.md +251 -0
- package/dist/docs/ScCalendar.md +268 -0
- package/dist/docs/ScCalendarDateComps.md +264 -0
- package/dist/docs/ScCatalogixInvite.md +345 -0
- package/dist/docs/ScCatalogixSidebar.md +337 -0
- package/dist/docs/ScCatalogixStoreHeader.md +246 -0
- package/dist/docs/ScCatalogixStoreTableList.md +316 -0
- package/dist/docs/ScCheckField.md +233 -0
- package/dist/docs/ScCheckbox.md +272 -0
- package/dist/docs/ScCounter.md +235 -0
- package/dist/docs/ScCreditsUsageCard.md +247 -0
- package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
- package/dist/docs/ScDefaultCard.md +269 -0
- package/dist/docs/ScDp.md +245 -0
- package/dist/docs/ScDrawer.md +318 -0
- package/dist/docs/ScFieldButton.md +255 -0
- package/dist/docs/ScFileField.md +268 -0
- package/dist/docs/ScGoogleSignIn.md +250 -0
- package/dist/docs/ScGuide.md +278 -0
- package/dist/docs/ScHDivider.md +213 -0
- package/dist/docs/ScHeader.md +222 -0
- package/dist/docs/ScImageField.md +253 -0
- package/dist/docs/ScInChatList.md +277 -0
- package/dist/docs/ScInChatMessage.md +205 -0
- package/dist/docs/ScInfoPopup.md +248 -0
- package/dist/docs/ScIntialProfileCover.md +233 -0
- package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
- package/dist/docs/ScLogoUnit.md +232 -0
- package/dist/docs/ScMappingCard.md +241 -0
- package/dist/docs/ScMediaApproval.md +301 -0
- package/dist/docs/ScMediaSelect.md +310 -0
- package/dist/docs/ScMenuOptions.md +308 -0
- package/dist/docs/ScMobileBottomAction.md +252 -0
- package/dist/docs/ScMobileTopNav.md +279 -0
- package/dist/docs/ScModal.md +291 -0
- package/dist/docs/ScOnlyField.md +302 -0
- package/dist/docs/ScOnlyIcon.md +213 -0
- package/dist/docs/ScPagination.md +284 -0
- package/dist/docs/ScPairtext.md +287 -0
- package/dist/docs/ScPendingAction.md +238 -0
- package/dist/docs/ScPhtogenixInvite.md +275 -0
- package/dist/docs/ScPlanCard.md +302 -0
- package/dist/docs/ScPlanComparison.md +264 -0
- package/dist/docs/ScPlanDetailsCard.md +246 -0
- package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
- package/dist/docs/ScPopUpMenu.md +224 -0
- package/dist/docs/ScProfile.md +234 -0
- package/dist/docs/ScProfileImageUpdate.md +261 -0
- package/dist/docs/ScProfileOptions.md +245 -0
- package/dist/docs/ScProfilePopup.md +396 -0
- package/dist/docs/ScProfileSettingsComp.md +250 -0
- package/dist/docs/ScProfileV2Mobile.md +216 -0
- package/dist/docs/ScProgressBar.md +267 -0
- package/dist/docs/ScQuickPrompt.md +277 -0
- package/dist/docs/ScRadio.md +228 -0
- package/dist/docs/ScReferralCardMobile.md +226 -0
- package/dist/docs/ScReferralTableHeader.md +260 -0
- package/dist/docs/ScReferralTableList.md +293 -0
- package/dist/docs/ScRole.md +226 -0
- package/dist/docs/ScRoleMobile.md +199 -0
- package/dist/docs/ScSelect.md +270 -0
- package/dist/docs/ScSelection.md +256 -0
- package/dist/docs/ScSelectionList.md +272 -0
- package/dist/docs/ScSelectionPill.md +240 -0
- package/dist/docs/ScSelectionPillGroup.md +302 -0
- package/dist/docs/ScSettingsNav.md +212 -0
- package/dist/docs/ScSettingsTabComp.md +260 -0
- package/dist/docs/ScSideBarLogoUnit.md +340 -0
- package/dist/docs/ScSidebar.md +243 -0
- package/dist/docs/ScSidebarIcons.md +232 -0
- package/dist/docs/ScSidebarMenu.md +283 -0
- package/dist/docs/ScSidebarProfile.md +231 -0
- package/dist/docs/ScSidebarSwitchMenu.md +258 -0
- package/dist/docs/ScSlider.md +194 -0
- package/dist/docs/ScStoreCard.md +252 -0
- package/dist/docs/ScStrLogo.md +253 -0
- package/dist/docs/ScStreamoidWordmark.md +302 -0
- package/dist/docs/ScSubAgent.md +226 -0
- package/dist/docs/ScTabComp.md +308 -0
- package/dist/docs/ScTabField.md +258 -0
- package/dist/docs/ScTabSwitcher.md +307 -0
- package/dist/docs/ScTableHeader.md +261 -0
- package/dist/docs/ScTableList.md +301 -0
- package/dist/docs/ScTableListMobile.md +282 -0
- package/dist/docs/ScTabs.md +268 -0
- package/dist/docs/ScTaxonomyPill.md +263 -0
- package/dist/docs/ScTextArea.md +259 -0
- package/dist/docs/ScTextField.md +324 -0
- package/dist/docs/ScThinkingStepIcon.md +249 -0
- package/dist/docs/ScTodoList.md +288 -0
- package/dist/docs/ScToggleSwitch.md +229 -0
- package/dist/docs/ScUsageHistoryMobile.md +194 -0
- package/dist/docs/ScVDivider.md +215 -0
- package/dist/docs/ScValueMappingL1.md +256 -0
- package/dist/docs/ScVersion.md +251 -0
- package/dist/docs/ScWorkspace.md +233 -0
- package/dist/docs/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +403 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4849 -0
- package/dist/index.css +36 -36
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScProfileV2Mobile
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: mobile
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [mobile, profile, avatar, initials, name, email, user, identity]
|
|
8
|
+
related: [ScProfile, ScDp, ScTableListMobile, ScReferralCardMobile, ScProfilePopup]
|
|
9
|
+
do_not_confuse_with: [ScProfile, ScProfileOptions, ScProfilePopup, ScProfileSettingsComp, ScDp, ScTableListMobile]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ScProfileV2Mobile
|
|
13
|
+
|
|
14
|
+
**A 40px avatar + name + email, as one mobile identity row.** Purely presentational:
|
|
15
|
+
a circular image (or two-letter initials derived from `name`) on the left or right,
|
|
16
|
+
with a two-line text block beside it. No menu, no chevron, no click behaviour.
|
|
17
|
+
|
|
18
|
+
## TL;DR for agents
|
|
19
|
+
|
|
20
|
+
- **Reach for it when:** a mobile sheet or header needs "who is signed in" as an
|
|
21
|
+
avatar plus name/email, and nothing else.
|
|
22
|
+
- **Don't reach for it when:** you're in the desktop sidebar footer (→ `ScProfile`,
|
|
23
|
+
which has a `collapsed` mode), you need the profile *menu* (→ `ScProfileOptions` /
|
|
24
|
+
`ScProfilePopup`), or the row is a team-member list item with role and permission
|
|
25
|
+
icons (→ `ScTableListMobile`).
|
|
26
|
+
- **Three things that will bite you:**
|
|
27
|
+
1. **It is `width: 288px`, fixed.** Not `100%`. It will not fill your container
|
|
28
|
+
and it will overflow anything narrower than 288px.
|
|
29
|
+
2. **`name` and `email` default to `"Chris Hemsworth"` / `"chris@kepler.com"`.**
|
|
30
|
+
3. `className` is concatenated unguarded, so omitting it puts the literal class
|
|
31
|
+
`undefined` on the root element.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. How to use it
|
|
36
|
+
|
|
37
|
+
### Import
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ScProfileV2Mobile } from "@streamoid/ui";
|
|
41
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Minimal usage
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<ScProfileV2Mobile name={user.name} email={user.email} imageUrl={user.avatarUrl} />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Props
|
|
51
|
+
|
|
52
|
+
| Prop | Type | Default | Notes |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `name` | `string` | `"Chris Hemsworth"` | ⚠️ Demo default. Also the source of the initials **and** the `<img alt>`. |
|
|
55
|
+
| `email` | `string` | `"chris@kepler.com"` | ⚠️ Demo default. Rendered as the grey 12px sub-line — it is really "subtitle", any string works. |
|
|
56
|
+
| `imageUrl` | `string` | – | When set, renders `<img src>` (`object-fit: cover`). When unset, renders initials. No error fallback — see Gotcha 4. |
|
|
57
|
+
| `dpPosition` | `"left"` \| `"right"` | `"left"` | Which side the avatar sits on. The text block always stays in the middle/flex-grow slot. |
|
|
58
|
+
| `className` | `string` | – | Appended after internal classes. Unguarded concat — see Gotcha 3. |
|
|
59
|
+
| `...props` | `React.HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div`. This is the only way to attach `onClick`, `role`, `style`. |
|
|
60
|
+
|
|
61
|
+
### How the initials are derived
|
|
62
|
+
|
|
63
|
+
`name.split(" ").map(n => n[0]).join("").toUpperCase().slice(0, 2)`
|
|
64
|
+
|
|
65
|
+
| `name` | Initials |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `"Chris Hemsworth"` | `CH` |
|
|
68
|
+
| `"chris"` | `C` |
|
|
69
|
+
| `"Ana Maria De Souza"` | `AM` (sliced to 2) |
|
|
70
|
+
| `""` | *(empty circle)* |
|
|
71
|
+
| `" Chris"` | `C` — empty segments contribute nothing |
|
|
72
|
+
|
|
73
|
+
There is no `initials` prop. If you need to control the initials (workspace codes,
|
|
74
|
+
single-letter avatars), use `ScDp` with `initial=` instead.
|
|
75
|
+
|
|
76
|
+
### Recipes
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Avatar on the right — e.g. a right-aligned mobile header slot
|
|
80
|
+
<ScProfileV2Mobile
|
|
81
|
+
name={user.name}
|
|
82
|
+
email={user.email}
|
|
83
|
+
imageUrl={user.avatarUrl}
|
|
84
|
+
dpPosition="right"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
// Make it fill its container (the component is 288px by default)
|
|
88
|
+
<ScProfileV2Mobile name={user.name} email={user.email} className="w-full" />
|
|
89
|
+
|
|
90
|
+
// Make it tappable — the component gives you no semantics, so add them
|
|
91
|
+
<ScProfileV2Mobile
|
|
92
|
+
name={user.name}
|
|
93
|
+
email={user.email}
|
|
94
|
+
role="button"
|
|
95
|
+
tabIndex={0}
|
|
96
|
+
onClick={openProfileSheet}
|
|
97
|
+
onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && openProfileSheet()}
|
|
98
|
+
className="w-full cursor-pointer"
|
|
99
|
+
/>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 2. Where to use it
|
|
105
|
+
|
|
106
|
+
Intended for CXO's mobile surface: the header of a profile bottom sheet, the top of
|
|
107
|
+
a mobile settings screen, or the identity line in a mobile menu — anywhere the
|
|
108
|
+
desktop would use `ScProfile` in the sidebar footer.
|
|
109
|
+
|
|
110
|
+
It composes nothing from the DS (its avatar is hand-rolled, not `ScDp`).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 3. When to use it
|
|
115
|
+
|
|
116
|
+
### Use it when
|
|
117
|
+
|
|
118
|
+
- You need avatar + two text lines, on mobile, with no interaction.
|
|
119
|
+
- The initials can be mechanically derived from the display name.
|
|
120
|
+
|
|
121
|
+
### Don't use it — reach for this instead
|
|
122
|
+
|
|
123
|
+
| Situation | Use instead |
|
|
124
|
+
|---|---|
|
|
125
|
+
| Desktop sidebar footer profile (needs an expanded/collapsed mode) | `ScProfile` — has `collapsed`, `subText`, `profileImage` |
|
|
126
|
+
| The profile **menu** (theme toggle, settings, log out) | `ScProfilePopup` / `ScProfileOptions` |
|
|
127
|
+
| A whole profile settings screen | `ScProfileSettingsComp` |
|
|
128
|
+
| Just an avatar, with controllable initials and a workspace/profile shape | `ScDp` (`variant="workspace" \| "profile"`, `size`, `initial`) |
|
|
129
|
+
| Avatar upload / crop | `ScProfileImageUpdate` |
|
|
130
|
+
| A team-member row with role badge + access icons | `ScTableListMobile` |
|
|
131
|
+
| A referral row with a status badge and credits | `ScReferralCardMobile` |
|
|
132
|
+
| A workspace (not a person) identity row | `ScWorkspaceSwitchMobileV2` |
|
|
133
|
+
|
|
134
|
+
### Don't confuse with
|
|
135
|
+
|
|
136
|
+
| You may actually want | Not this |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `ScProfile` — the desktop profile block; `subText` not `email`, `profileImage` not `imageUrl` | `ScProfileV2Mobile` uses different prop names for the same data |
|
|
139
|
+
| `ScProfileOptions` — a single menu row inside a profile menu | Not an identity display |
|
|
140
|
+
| `ScProfilePopup` — the whole floating profile menu | Not an identity display |
|
|
141
|
+
| `ScDp` — the avatar primitive | `ScProfileV2Mobile` re-implements a 40px circle rather than composing `ScDp` |
|
|
142
|
+
| `ScTableListMobile` — `name` + `email` + `role` + icon groups + `onRowClick` | The superset row; use it for lists |
|
|
143
|
+
|
|
144
|
+
There is **no** `ScProfileV1Mobile`. The "V2" is a design-iteration marker, not a
|
|
145
|
+
version you can choose.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 4. Why to use it
|
|
150
|
+
|
|
151
|
+
- **The one job it does well:** name/email truncation. Both lines get
|
|
152
|
+
`overflow: hidden; text-overflow: ellipsis; white-space: nowrap` inside a
|
|
153
|
+
`flex: 1 0 0; min-width: 0` column, which is the exact combination hand-rolled
|
|
154
|
+
identity rows get wrong (long emails blowing out the row).
|
|
155
|
+
- **`dpPosition` handles mirrored layouts** without you re-ordering DOM.
|
|
156
|
+
- **Consistent 40px avatar and 14/12px type pair** with the rest of the mobile
|
|
157
|
+
family (`ScTableListMobile`, `ScReferralCardMobile` use the same metrics).
|
|
158
|
+
|
|
159
|
+
Given the fixed width, treat this as a thin convenience —
|
|
160
|
+
`ScDp` + two `<p>`s is a legitimate alternative if you need control.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Gotchas
|
|
165
|
+
|
|
166
|
+
**1. Fixed `width: 288px`.** The root is not fluid.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
// WRONG — stays 288px inside a full-width sheet, and overflows a 320px phone
|
|
170
|
+
// with side padding
|
|
171
|
+
<ScProfileV2Mobile name={n} email={e} />
|
|
172
|
+
|
|
173
|
+
// RIGHT
|
|
174
|
+
<ScProfileV2Mobile name={n} email={e} className="w-full" />
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**2. Demo defaults.** `name="Chris Hemsworth"`, `email="chris@kepler.com"`. Rendering
|
|
178
|
+
it bare in a placeholder screen ships a fake person.
|
|
179
|
+
|
|
180
|
+
**3. `className` is concatenated with `+ " " + className`, unguarded.** Omit it and
|
|
181
|
+
the root renders `class="ScProfileV2Mobile_scProfileV2Mobile undefined"`. Harmless
|
|
182
|
+
visually, but it breaks exact-`class` snapshot assertions and
|
|
183
|
+
`[class="…"]` selectors.
|
|
184
|
+
|
|
185
|
+
**4. A broken `imageUrl` shows a broken image, not initials.** There is no `onError`
|
|
186
|
+
fallback — the initials branch is chosen purely on `imageUrl` being truthy. Validate
|
|
187
|
+
the URL, or use `ScDp` (which falls back to `initial`).
|
|
188
|
+
|
|
189
|
+
**5. No interaction semantics.** It's a plain `div`: no `role`, no `tabIndex`, no
|
|
190
|
+
`cursor: pointer`. Attaching `onClick` via `...props` works but is invisible and
|
|
191
|
+
keyboard-inaccessible until you add the rest yourself.
|
|
192
|
+
|
|
193
|
+
**6. `email` is unvalidated free text** and is not rendered as a `mailto:` link.
|
|
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: it is exported from `@streamoid/ui` but **no** host app renders it,
|
|
202
|
+
and it is not agent-runtime either — it is currently unused. Where it belongs is
|
|
203
|
+
CXO's mobile settings/profile surface
|
|
204
|
+
(`cxo-dashboard/src/app/components/mobile-settings-content.tsx`), which today shows
|
|
205
|
+
identity with hand-rolled markup, and `mobile-teams-content.tsx`, which uses the
|
|
206
|
+
richer `ScTableListMobile` for member rows.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Related
|
|
211
|
+
|
|
212
|
+
- `ScProfile` — the desktop sibling with a collapsed mode.
|
|
213
|
+
- `ScDp` — the avatar primitive; use it when you need to control the initials.
|
|
214
|
+
- `ScTableListMobile` — the mobile list-row superset (role + access icons + click).
|
|
215
|
+
- `ScProfilePopup` / `ScProfileOptions` — the profile menu, not the identity block.
|
|
216
|
+
- `ScWorkspaceSwitchMobileV2` — the workspace equivalent of this row.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ScProgressBar
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: overlays
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div (track) > div (fill)
|
|
7
|
+
tags: [progress, progressbar, meter, percent, loading, upload, credits, determinate]
|
|
8
|
+
related: [ScCreditsUsageCard, ScBadges, ScBeacon, ScSlider, ScButton]
|
|
9
|
+
do_not_confuse_with: [ScSlider, ScBadges, ScCounter]
|
|
10
|
+
used_by: [photogenix]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ScProgressBar
|
|
14
|
+
|
|
15
|
+
**The determinate progress track.** A pill-shaped 310px track with 8px of inner
|
|
16
|
+
padding and a 6px amber fill whose width is set inline from `progress` (a
|
|
17
|
+
percentage, clamped 0–100). That's the whole component — no label, no percentage
|
|
18
|
+
text, no tone, no indeterminate mode.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you have a known-percentage operation to show — an upload,
|
|
23
|
+
a batch render, credits consumed.
|
|
24
|
+
- **Don't reach for it when:** the value is user-adjustable (→ `ScSlider`), you
|
|
25
|
+
don't know the percentage (there is no indeterminate mode — use a spinner, e.g.
|
|
26
|
+
`ScButton loading`), or you want the credits meter with its labels and CTA
|
|
27
|
+
(→ `ScCreditsUsageCard`, which already renders one).
|
|
28
|
+
- **Four things that will bite you:**
|
|
29
|
+
1. `progress` defaults to **`50`** ⚠️ — forget it and the bar looks half-done.
|
|
30
|
+
2. Fixed `width: 19.375rem` (310px). It will **not** fill its parent until you
|
|
31
|
+
override the width.
|
|
32
|
+
3. The fill is **always amber** (`--alias-fill-warning-solid`). There is no
|
|
33
|
+
`tone` / `variant` — no green "complete", no red "failed".
|
|
34
|
+
4. No `role="progressbar"` and no `aria-value*`. Add them yourself via the spread.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 1. How to use it
|
|
39
|
+
|
|
40
|
+
### Import
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { ScProgressBar } from "@streamoid/ui";
|
|
44
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Minimal usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<ScProgressBar progress={72} />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Filling its container (what you almost always want):
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<ScProgressBar progress={72} style={{ width: "100%" }} />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Props
|
|
60
|
+
|
|
61
|
+
| Prop | Type | Default | Notes |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `progress` | `number` | `50` | ⚠️ Real default. A **percentage 0–100**, clamped internally with `Math.min(100, Math.max(0, progress))`. `NaN` produces `width: NaN%` (nothing painted). |
|
|
64
|
+
| `className` | `string` | – | Concatenated unconditionally — see Gotcha 8. |
|
|
65
|
+
| `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the **track** div: `style`, `role`, `aria-*`, `title`, `data-*`, `onClick`. |
|
|
66
|
+
|
|
67
|
+
### What it actually renders
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div class="scProgressBar {className}" {...props}>
|
|
71
|
+
<!-- background --alias-fill-neutral-neutral · radius full · padding 8px
|
|
72
|
+
width 19.375rem (310px) · overflow:hidden -->
|
|
73
|
+
<div class="progressFill" style="width: {clamped}%">
|
|
74
|
+
<!-- background --alias-fill-warning-solid · radius full · height 0.375rem (6px) -->
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Total height is 22px (6px fill + 8px padding top and bottom).
|
|
80
|
+
|
|
81
|
+
### Recipes
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
// Percentage text beside the bar, bar flexes (the Photogenix idiom)
|
|
85
|
+
<div className="flex items-center gap-2 w-[160px]">
|
|
86
|
+
<div className="flex-1 min-w-0">
|
|
87
|
+
<ScProgressBar progress={pct} style={{ width: "100%" }} />
|
|
88
|
+
</div>
|
|
89
|
+
<span className="w-10 text-right">{Math.round(pct)}%</span>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
// Derive the percentage — pass 0–100, never a fraction
|
|
93
|
+
<ScProgressBar progress={total > 0 ? Math.round((done / total) * 100) : 0} />
|
|
94
|
+
|
|
95
|
+
// Accessible progress (the component gives you none of this)
|
|
96
|
+
<ScProgressBar
|
|
97
|
+
progress={pct}
|
|
98
|
+
role="progressbar"
|
|
99
|
+
aria-valuenow={Math.round(pct)}
|
|
100
|
+
aria-valuemin={0}
|
|
101
|
+
aria-valuemax={100}
|
|
102
|
+
aria-label="Importing images"
|
|
103
|
+
style={{ width: "100%" }}
|
|
104
|
+
/>
|
|
105
|
+
|
|
106
|
+
// Recolour on completion — there is no `tone` prop, so do it in CSS
|
|
107
|
+
<ScProgressBar progress={pct} className={pct >= 100 ? "done" : undefined} style={{ width: "100%" }} />
|
|
108
|
+
// .done > div { background: var(--alias-fill-success-solid) !important; }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 2. Where to use it
|
|
114
|
+
|
|
115
|
+
- **Photogenix batch + upload flows** — `studio/UploadZipModal` (import progress),
|
|
116
|
+
`studio/batchDetail/BatchExportDrawer` (export), `SPhComps/SPHCXBatchList`
|
|
117
|
+
(per-row render progress in the Shopify app). Photogenix is the only host consumer.
|
|
118
|
+
- **`ScCreditsUsageCard`** composes one internally for the credits meter — that card
|
|
119
|
+
is the right entry point for anything credits-shaped, and it neutralises the fixed
|
|
120
|
+
width with `flex: 1 !important; width: unset !important`.
|
|
121
|
+
- **Any long-running determinate job row** — feed imports, exports, bulk edits.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 3. When to use it
|
|
126
|
+
|
|
127
|
+
### Use it when
|
|
128
|
+
|
|
129
|
+
- You know the **percentage complete** and it will change over time.
|
|
130
|
+
- The operation is long enough that the user needs reassurance (uploads, renders,
|
|
131
|
+
imports).
|
|
132
|
+
- You want the track/fill radius, height and neutral track colour to match the
|
|
133
|
+
credits meter.
|
|
134
|
+
|
|
135
|
+
### Don't use it — reach for this instead
|
|
136
|
+
|
|
137
|
+
| Situation | Use instead |
|
|
138
|
+
|---|---|
|
|
139
|
+
| The value is a control the user drags | `ScSlider` |
|
|
140
|
+
| A bounded numeric input with −/+ | `ScCounter` |
|
|
141
|
+
| You don't know the percentage (spinner / indeterminate) | `ScButton loading`, or your app's spinner — this bar has no indeterminate mode |
|
|
142
|
+
| Credits remaining with labels + a "Buy credits" CTA | `ScCreditsUsageCard` / `ScCreditsUsageCardMobile` |
|
|
143
|
+
| A pass/fail or state marker | `ScBadges` / `ScBeacon` |
|
|
144
|
+
| A multi-step wizard indicator | `ScGuide`'s `stepLabel`, or `ScTabs` |
|
|
145
|
+
| A count that isn't out of a total | `ScBadges` |
|
|
146
|
+
|
|
147
|
+
### Don't confuse with
|
|
148
|
+
|
|
149
|
+
| You may actually want | Not this |
|
|
150
|
+
|---|---|
|
|
151
|
+
| `ScSlider` — interactive range input | `ScProgressBar` is read-only output |
|
|
152
|
+
| `ScCreditsUsageCard` — the whole credits meter (label + bar + remaining text + CTA) | `ScProgressBar` is just the bar |
|
|
153
|
+
| A styled `<progress>` element | This is two divs with an inline width; no native semantics |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 4. Why to use it
|
|
158
|
+
|
|
159
|
+
- **Clamping is already done.** `Math.min(100, Math.max(0, progress))` means a
|
|
160
|
+
server sending `104` or `-3` cannot overflow the track or produce a negative
|
|
161
|
+
width — the classic hand-rolled-bar bug.
|
|
162
|
+
- **Tokenised track and fill.** `--alias-fill-neutral-neutral` behind
|
|
163
|
+
`--alias-fill-warning-solid` stay distinguishable in both themes; a hardcoded
|
|
164
|
+
`#eee` track vanishes in dark mode and a hardcoded dark track vanishes in light.
|
|
165
|
+
- **`overflow: hidden` + `border-radius: full` on the track** means the fill's own
|
|
166
|
+
rounded ends are clipped correctly at both extremes, instead of the fill's corners
|
|
167
|
+
poking outside the track.
|
|
168
|
+
- **It matches the credits meter for free** — the same component is inside
|
|
169
|
+
`ScCreditsUsageCard`, so an upload bar and a credits bar look like siblings.
|
|
170
|
+
- **One line of change surface.** Height, radius and colour live in one CSS module,
|
|
171
|
+
not scattered across every progress UI in three apps.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Gotchas
|
|
176
|
+
|
|
177
|
+
**1. `progress` defaults to 50.** A bar rendered with no props reads "half done".
|
|
178
|
+
|
|
179
|
+
**2. It's a percentage, not a fraction.** `progress={0.75}` renders a 0.75% sliver.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
// WRONG
|
|
183
|
+
<ScProgressBar progress={done / total} />
|
|
184
|
+
|
|
185
|
+
// RIGHT
|
|
186
|
+
<ScProgressBar progress={total > 0 ? (done / total) * 100 : 0} />
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**3. Fixed `width: 19.375rem` — it does not fill its parent.** This is the most
|
|
190
|
+
common complaint. Two fixes, both in use:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// via the spread
|
|
194
|
+
<ScProgressBar progress={pct} style={{ width: "100%" }} />
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```css
|
|
198
|
+
/* via className, when the bar must flex inside a row (what ScCreditsUsageCard does) */
|
|
199
|
+
.barInstance { flex: 1 !important; width: unset !important; }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The `!important` is needed because the module's `width` is on the same element.
|
|
203
|
+
|
|
204
|
+
**4. `progress={100}` never looks completely full.** The inline width is a
|
|
205
|
+
percentage of the **padded content box**, and the track carries `padding: 0.5rem`,
|
|
206
|
+
so 8px of track always remains visible at each end. Don't use "the bar is flush" as
|
|
207
|
+
your completion cue — show a label or swap in a `ScBadges` / `ScBeacon`.
|
|
208
|
+
|
|
209
|
+
**5. The fill is amber for every value.** `--alias-fill-warning-solid`, hardcoded.
|
|
210
|
+
There is no `tone`, `variant` or `color` prop, so a green "complete" or a red
|
|
211
|
+
"failed" bar requires your own CSS override on the child div.
|
|
212
|
+
|
|
213
|
+
**6. No a11y whatsoever.** Plain divs — no `role="progressbar"`, no `aria-valuenow`,
|
|
214
|
+
no accessible name. Screen readers announce nothing. Pass the aria via the spread
|
|
215
|
+
(see Recipes); it lands on the track, which is the right element.
|
|
216
|
+
|
|
217
|
+
**7. No transition on `width`.** Values jump. If you poll progress at long
|
|
218
|
+
intervals, add `transition: width .3s ease` to the fill via a `className`
|
|
219
|
+
descendant selector.
|
|
220
|
+
|
|
221
|
+
**8. `className` is concatenated unconditionally.** Omit it and the class attribute
|
|
222
|
+
contains the literal string `undefined`. Cosmetic; breaks exact-match snapshots.
|
|
223
|
+
|
|
224
|
+
**9. Fixed 22px total height, no `size` prop.** A thin 2px inline bar is not
|
|
225
|
+
available; override `padding` and the child's `height` if you need one.
|
|
226
|
+
|
|
227
|
+
**10. `NaN` renders nothing, silently.** `total = 0` in a naive
|
|
228
|
+
`done / total * 100` gives `NaN`, `Math.min/max` propagate it, and the fill gets
|
|
229
|
+
`width: NaN%`. Guard the divisor.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## In the wild
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
// photogenix_v2 shopify/client/src/components/SPhComps/SPHCXBatchList.tsx:181
|
|
237
|
+
<div className="shrink-0 w-[160px] flex items-center gap-2">
|
|
238
|
+
<div className="flex-1 min-w-0">
|
|
239
|
+
<ScProgressBar
|
|
240
|
+
progress={progressPercent}
|
|
241
|
+
style={{ width: '100%' }}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
<span className="w-10 shrink-0 text-right text-[14px] leading-[20px]">
|
|
245
|
+
{Math.round(progressPercent)}%
|
|
246
|
+
</span>
|
|
247
|
+
</div>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
// photogenix_v2 dashboard/client/src/components/studio/UploadZipModal.tsx:513
|
|
252
|
+
<ScProgressBar
|
|
253
|
+
progress={importProgress.total > 0
|
|
254
|
+
? Math.round((importProgress.completed / importProgress.total) * 100)
|
|
255
|
+
: 0}
|
|
256
|
+
/>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Related
|
|
262
|
+
|
|
263
|
+
- `ScCreditsUsageCard` / `ScCreditsUsageCardMobile` — the credits meter; embeds this bar and shows how to unpin its width.
|
|
264
|
+
- `ScSlider` — the interactive range sibling.
|
|
265
|
+
- `ScCounter` — bounded numeric stepper.
|
|
266
|
+
- `ScBadges` / `ScBeacon` — for the terminal state once progress hits 100.
|
|
267
|
+
- `ScButton` (`loading`) — the indeterminate case this component doesn't cover.
|