@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.
Files changed (134) hide show
  1. package/README.md +35 -18
  2. package/dist/docs/AGENTS.md +325 -0
  3. package/dist/docs/CreditWarningBanner.md +305 -0
  4. package/dist/docs/InvoiceHistoryMobile.md +222 -0
  5. package/dist/docs/ScAccess.md +259 -0
  6. package/dist/docs/ScAppCard.md +244 -0
  7. package/dist/docs/ScAppCardForCopilot.md +230 -0
  8. package/dist/docs/ScAppCardV3.md +273 -0
  9. package/dist/docs/ScAppField.md +308 -0
  10. package/dist/docs/ScAppListingCard.md +271 -0
  11. package/dist/docs/ScAppSwitchPanel.md +286 -0
  12. package/dist/docs/ScAppcardLogos.md +226 -0
  13. package/dist/docs/ScArtifaxInvite.md +262 -0
  14. package/dist/docs/ScArtifaxSidebar.md +330 -0
  15. package/dist/docs/ScAskAgentButton.md +307 -0
  16. package/dist/docs/ScBadges.md +261 -0
  17. package/dist/docs/ScBeacon.md +244 -0
  18. package/dist/docs/ScBillingHistoryHeader.md +210 -0
  19. package/dist/docs/ScBillingHistoryTableList.md +243 -0
  20. package/dist/docs/ScBillingLogsTableHeader.md +212 -0
  21. package/dist/docs/ScBillingLogsTableList.md +251 -0
  22. package/dist/docs/ScBriefCard.md +255 -0
  23. package/dist/docs/ScButton.md +251 -0
  24. package/dist/docs/ScCalendar.md +268 -0
  25. package/dist/docs/ScCalendarDateComps.md +264 -0
  26. package/dist/docs/ScCatalogixInvite.md +345 -0
  27. package/dist/docs/ScCatalogixSidebar.md +337 -0
  28. package/dist/docs/ScCatalogixStoreHeader.md +246 -0
  29. package/dist/docs/ScCatalogixStoreTableList.md +316 -0
  30. package/dist/docs/ScCheckField.md +233 -0
  31. package/dist/docs/ScCheckbox.md +272 -0
  32. package/dist/docs/ScCounter.md +235 -0
  33. package/dist/docs/ScCreditsUsageCard.md +247 -0
  34. package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
  35. package/dist/docs/ScDefaultCard.md +269 -0
  36. package/dist/docs/ScDp.md +245 -0
  37. package/dist/docs/ScDrawer.md +318 -0
  38. package/dist/docs/ScFieldButton.md +255 -0
  39. package/dist/docs/ScFileField.md +268 -0
  40. package/dist/docs/ScGoogleSignIn.md +250 -0
  41. package/dist/docs/ScGuide.md +278 -0
  42. package/dist/docs/ScHDivider.md +210 -0
  43. package/dist/docs/ScHeader.md +222 -0
  44. package/dist/docs/ScImageField.md +253 -0
  45. package/dist/docs/ScInChatList.md +277 -0
  46. package/dist/docs/ScInChatMessage.md +205 -0
  47. package/dist/docs/ScInfoPopup.md +248 -0
  48. package/dist/docs/ScIntialProfileCover.md +233 -0
  49. package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
  50. package/dist/docs/ScLogoUnit.md +232 -0
  51. package/dist/docs/ScMappingCard.md +241 -0
  52. package/dist/docs/ScMediaApproval.md +301 -0
  53. package/dist/docs/ScMediaSelect.md +310 -0
  54. package/dist/docs/ScMenuOptions.md +308 -0
  55. package/dist/docs/ScMobileBottomAction.md +252 -0
  56. package/dist/docs/ScMobileTopNav.md +279 -0
  57. package/dist/docs/ScModal.md +291 -0
  58. package/dist/docs/ScOnlyField.md +302 -0
  59. package/dist/docs/ScOnlyIcon.md +213 -0
  60. package/dist/docs/ScPagination.md +284 -0
  61. package/dist/docs/ScPairtext.md +287 -0
  62. package/dist/docs/ScPendingAction.md +238 -0
  63. package/dist/docs/ScPhtogenixInvite.md +275 -0
  64. package/dist/docs/ScPlanCard.md +302 -0
  65. package/dist/docs/ScPlanComparison.md +264 -0
  66. package/dist/docs/ScPlanDetailsCard.md +246 -0
  67. package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
  68. package/dist/docs/ScPopUpMenu.md +224 -0
  69. package/dist/docs/ScProfile.md +234 -0
  70. package/dist/docs/ScProfileImageUpdate.md +261 -0
  71. package/dist/docs/ScProfileOptions.md +245 -0
  72. package/dist/docs/ScProfilePopup.md +396 -0
  73. package/dist/docs/ScProfileSettingsComp.md +250 -0
  74. package/dist/docs/ScProfileV2Mobile.md +216 -0
  75. package/dist/docs/ScProgressBar.md +267 -0
  76. package/dist/docs/ScQuickPrompt.md +277 -0
  77. package/dist/docs/ScRadio.md +228 -0
  78. package/dist/docs/ScReferralCardMobile.md +226 -0
  79. package/dist/docs/ScReferralTableHeader.md +260 -0
  80. package/dist/docs/ScReferralTableList.md +293 -0
  81. package/dist/docs/ScRole.md +226 -0
  82. package/dist/docs/ScRoleMobile.md +199 -0
  83. package/dist/docs/ScSelect.md +270 -0
  84. package/dist/docs/ScSelection.md +256 -0
  85. package/dist/docs/ScSelectionList.md +272 -0
  86. package/dist/docs/ScSelectionPill.md +240 -0
  87. package/dist/docs/ScSelectionPillGroup.md +302 -0
  88. package/dist/docs/ScSettingsNav.md +212 -0
  89. package/dist/docs/ScSettingsTabComp.md +260 -0
  90. package/dist/docs/ScSideBarLogoUnit.md +340 -0
  91. package/dist/docs/ScSidebar.md +243 -0
  92. package/dist/docs/ScSidebarIcons.md +232 -0
  93. package/dist/docs/ScSidebarMenu.md +283 -0
  94. package/dist/docs/ScSidebarProfile.md +231 -0
  95. package/dist/docs/ScSidebarSwitchMenu.md +258 -0
  96. package/dist/docs/ScSlider.md +194 -0
  97. package/dist/docs/ScStoreCard.md +252 -0
  98. package/dist/docs/ScStrLogo.md +253 -0
  99. package/dist/docs/ScStreamoidWordmark.md +302 -0
  100. package/dist/docs/ScSubAgent.md +226 -0
  101. package/dist/docs/ScTabComp.md +308 -0
  102. package/dist/docs/ScTabField.md +258 -0
  103. package/dist/docs/ScTabSwitcher.md +307 -0
  104. package/dist/docs/ScTableHeader.md +261 -0
  105. package/dist/docs/ScTableList.md +301 -0
  106. package/dist/docs/ScTableListMobile.md +282 -0
  107. package/dist/docs/ScTabs.md +268 -0
  108. package/dist/docs/ScTaxonomyPill.md +263 -0
  109. package/dist/docs/ScTextArea.md +259 -0
  110. package/dist/docs/ScTextField.md +324 -0
  111. package/dist/docs/ScThinkingStepIcon.md +249 -0
  112. package/dist/docs/ScTodoList.md +288 -0
  113. package/dist/docs/ScToggleSwitch.md +229 -0
  114. package/dist/docs/ScUsageHistoryMobile.md +194 -0
  115. package/dist/docs/ScVDivider.md +215 -0
  116. package/dist/docs/ScValueMappingL1.md +256 -0
  117. package/dist/docs/ScVersion.md +251 -0
  118. package/dist/docs/ScWorkspace.md +233 -0
  119. package/dist/docs/ScWorkspaceAccountMenu.md +115 -0
  120. package/dist/docs/ScWorkspaceCard.md +234 -0
  121. package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
  122. package/dist/docs/ScWorkspaceSwitchCard.md +312 -0
  123. package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
  124. package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
  125. package/dist/docs/StreamoidSidebar.md +413 -0
  126. package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
  127. package/dist/docs/UsageHistoryMobile.md +235 -0
  128. package/dist/docs/components.json +4931 -0
  129. package/dist/index.css +361 -36
  130. package/dist/index.d.mts +213 -88
  131. package/dist/index.d.ts +213 -88
  132. package/dist/index.js +2486 -1629
  133. package/dist/index.mjs +2487 -1620
  134. package/package.json +5 -3
@@ -0,0 +1,261 @@
1
+ ---
2
+ component: ScProfileImageUpdate
3
+ package: "@streamoid/ui"
4
+ category: profile
5
+ status: stable
6
+ renders: div
7
+ tags: [avatar, profile, picture, upload, delete, initials, settings, image]
8
+ related: [ScDp, ScIntialProfileCover, ScImageField, ScFileField, ScProfileSettingsComp]
9
+ do_not_confuse_with: [ScImageField, ScDp, ScIntialProfileCover, ScProfile, ScProfileSettingsComp]
10
+ used_by: [cxo]
11
+ ---
12
+
13
+ # ScProfileImageUpdate
14
+
15
+ **The avatar editor block at the top of a profile settings screen.** A 100px circle
16
+ (the image, or initials on a grey disc) with an upload / delete icon-button pair
17
+ underneath, split by a vertical divider. Nothing else — no label, no filename, no
18
+ crop.
19
+
20
+ ## TL;DR for agents
21
+
22
+ - **Reach for it when:** a **profile settings** screen needs "change your picture" —
23
+ circle above, two icon buttons below.
24
+ - **Don't reach for it when:** you want a labelled rectangular image field in a form
25
+ (→ `ScImageField`), any non-image file (→ `ScFileField`), or you're only *showing*
26
+ an avatar (→ `ScDp`).
27
+ - **Four things that will bite you:**
28
+ 1. The **delete button always renders**, even with no image. Gate `onDelete`
29
+ yourself or the user "deletes" nothing and you fire a request.
30
+ 2. Both buttons are **unlabelled** `ScButton`s → `div[role="button"]` with no
31
+ `aria-label`. Add your own via a wrapper; you can't pass one through.
32
+ 3. `accept="image/*"` is **hardcoded**; there is no `accept`, no size limit, no
33
+ loading/uploading state.
34
+ 4. **Fixed 7.5rem wide / 6.25rem circle.** No `size` prop (unlike `ScDp`).
35
+
36
+ ---
37
+
38
+ ## 1. How to use it
39
+
40
+ ### Import
41
+
42
+ ```tsx
43
+ import { ScProfileImageUpdate } from "@streamoid/ui";
44
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
45
+ ```
46
+
47
+ ### Minimal usage
48
+
49
+ ```tsx
50
+ <ScProfileImageUpdate
51
+ imageUrl={profileImage}
52
+ initials={profileInitials}
53
+ onUpload={handleProfileImageUpload}
54
+ onDelete={handleProfileImageDelete}
55
+ />
56
+ ```
57
+
58
+ ### Props
59
+
60
+ | Prop | Type | Default | Notes |
61
+ |---|---|---|---|
62
+ | `imageUrl` | `string` | – | Remote URL or blob URL. Truthy → `<img alt="profile">`; falsy → the initials disc. **No `onError` fallback** — a broken URL shows a broken image, it does not fall back to initials (`ScDp` does). |
63
+ | `initials` | `string` | `""` | Shown only when `imageUrl` is falsy. Default is an **empty grey circle** — pass 1–2 characters. |
64
+ | `onUpload` | `(file: File) => void` | – | Fires with the chosen `File`. The input's value is reset afterwards, so re-picking the same file fires again. |
65
+ | `onDelete` | `() => void` | – | The trash button. **Always clickable** — see Gotcha 1. |
66
+ | `className` | `string` | – | Concatenated after the root class. |
67
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div` — `style`, `data-*`, `aria-*`, `onClick` all work. (This one *does* extend `HTMLAttributes`; `ScImageField` and `ScFileField` do not.) |
68
+
69
+ ### What renders in each state
70
+
71
+ | `imageUrl` | Circle | Buttons |
72
+ |---|---|---|
73
+ | truthy | `<img>` cropped `object-fit: cover` to a 6.25rem circle | ⬆ upload · │ · 🗑 delete |
74
+ | falsy | `initials` centred on `--alias-fill-neutral-neutralselected`, 1.75rem / weight 500 | ⬆ upload · │ · 🗑 delete (still) |
75
+
76
+ ### Recipes
77
+
78
+ ```tsx
79
+ // The standard settings pattern: local preview, server persist
80
+ const [preview, setPreview] = useState<string | null>(null);
81
+
82
+ <ScProfileImageUpdate
83
+ imageUrl={preview ?? user.avatarUrl}
84
+ initials={initialsOf(user.fullName)}
85
+ onUpload={async (file) => {
86
+ setPreview(URL.createObjectURL(file)); // instant feedback
87
+ await api.uploadAvatar(file); // then persist
88
+ }}
89
+ onDelete={async () => {
90
+ if (preview) { URL.revokeObjectURL(preview); setPreview(null); }
91
+ await api.deleteAvatar();
92
+ }}
93
+ />
94
+
95
+ // Guard the delete so it can't fire on an empty avatar
96
+ <ScProfileImageUpdate
97
+ imageUrl={avatar}
98
+ initials={initials}
99
+ onUpload={upload}
100
+ onDelete={avatar ? remove : undefined} // button still shows, but does nothing
101
+ />
102
+
103
+ // Validate before you accept the file (there is no accept/size prop)
104
+ <ScProfileImageUpdate
105
+ imageUrl={avatar}
106
+ initials={initials}
107
+ onUpload={(f) => {
108
+ if (f.size > 2_000_000) return toast("Keep it under 2 MB");
109
+ upload(f);
110
+ }}
111
+ />
112
+ ```
113
+
114
+ ---
115
+
116
+ ## 2. Where to use it
117
+
118
+ - **CXO mobile settings → Profile tab**, centred at the top of the field stack, above
119
+ the `ScTextField`s for name and email. That is its only live consumer.
120
+ - Any **"My profile" / "Account" settings screen** in a host app: it is the intended
121
+ partner for `ScProfileSettingsComp`'s name/email rows.
122
+ - Only for a **person's** avatar. A workspace/brand logo is a rectangle in a form —
123
+ that is `ScImageField`.
124
+
125
+ Internally it composes `ScButton` (×2, `styleVariant="icon-only"`,
126
+ `variant="tertiary"`, `size="md"`) and `ScVDivider`.
127
+
128
+ ---
129
+
130
+ ## 3. When to use it
131
+
132
+ ### Use it when
133
+
134
+ - The image is a **person's avatar**, round, and the affordances should be two small
135
+ icon buttons rather than a dropzone.
136
+ - You have both operations: set a new picture **and** remove the current one.
137
+ - The layout is a centred column — it is a fixed 7.5rem-wide block, not a
138
+ full-width form row.
139
+
140
+ ### Don't use it — reach for this instead
141
+
142
+ | Situation | Use instead |
143
+ |---|---|
144
+ | A labelled, full-width, rectangular image field inside a form (workspace logo, brand mark) | `ScImageField` |
145
+ | Any non-image file, or you need progress / an error line | `ScFileField` |
146
+ | Only **displaying** an avatar (header, sidebar, table row, switcher) | `ScDp` — has `size`, `variant="workspace" \| "profile"`, and a real image-error fallback to initials |
147
+ | Only displaying **initials** at an arbitrary size | `ScIntialProfileCover` |
148
+ | The whole profile settings form (name, email rows) | `ScProfileSettingsComp` |
149
+ | The profile *menu* / popover in the header | `ScProfile`, `ScProfileOptions`, `ScProfilePopup` |
150
+ | A different avatar size | `ScDp` — this component has no `size` prop |
151
+
152
+ ### Don't confuse with
153
+
154
+ | You may actually want | Not this |
155
+ |---|---|
156
+ | `ScImageField` — labelled 120px **rectangle**, thumbnail + filename + one red delete icon, `onFileSelect(file)` | `ScProfileImageUpdate` is a 100px **circle** with two icon buttons and no label or filename |
157
+ | `ScDp` — read-only avatar with `size`, `type="initial" \| "image"`, `variant`, and `onError` → initials | This component uploads but cannot resize and has no error fallback |
158
+ | `ScIntialProfileCover` — initials-only disc, takes `intial` (note the spelling) and `size` | Not an editor |
159
+ | `ScProfile` / `ScProfileOptions` / `ScProfilePopup` / `ScProfileSettingsComp` | Menu/row/form components, nothing to do with picking an image |
160
+
161
+ Prop-name trap across the family: this one takes **`imageUrl`**, `ScImageField`
162
+ takes **`imagePreview`**, `ScDp` takes **`imageUrl`** + **`initial`** (singular),
163
+ `ScIntialProfileCover` takes **`intial`** (sic). None are interchangeable.
164
+
165
+ ---
166
+
167
+ ## 4. Why to use it
168
+
169
+ - **The Figma block, exactly.** 7.5rem column, 6.25rem circle, 0.5rem gap, 0.625rem
170
+ between buttons, a 1.5rem-tall divider, 1.25rem icons — all pinned so every
171
+ product's profile screen looks identical.
172
+ - **Initials fallback is already tokenised** (`fill-neutral-neutralselected` disc,
173
+ `text-and-icons-primary` glyphs at 1.75rem/500), so an avatar-less user doesn't
174
+ get a blank hole or a light-mode contrast bug.
175
+ - **The file input is reset after every pick**, so the "user re-selects the same
176
+ file and nothing happens" bug is already handled.
177
+ - **Buttons inherit `ScButton`** — so hover, `tabIndex={0}`, Enter/Space activation
178
+ and the tertiary token set come for free, and a future button restyle reaches this
179
+ component automatically. (There is no focus *ring*: `ScButton.module.css` has no
180
+ `:focus` rule, so the buttons are focusable but invisibly so.)
181
+
182
+ ---
183
+
184
+ ## Gotchas
185
+
186
+ **1. Delete is never disabled.** There is no `canDelete`/`state` prop and no gate on
187
+ `imageUrl`. A user with no picture can still click the trash.
188
+
189
+ ```tsx
190
+ // WRONG — fires a DELETE for an avatar that isn't there
191
+ <ScProfileImageUpdate initials="NS" onDelete={() => api.deleteAvatar()} />
192
+
193
+ // RIGHT — no-op when there's nothing to remove
194
+ <ScProfileImageUpdate initials="NS" imageUrl={avatar} onDelete={avatar ? remove : undefined} />
195
+ ```
196
+
197
+ **2. Neither button has an accessible name.** Both are `ScButton`
198
+ `styleVariant="icon-only"` with no `aria-label`, and `ScButton` renders
199
+ `div[role="button"]` (with `tabIndex={0}` and its own Enter/Space handler, but no
200
+ visible focus style). A screen reader announces two unnamed buttons. You cannot pass
201
+ labels through — the props stop at `onUpload`/`onDelete`. Wrap the component and
202
+ label the region, or fix the DS:
203
+
204
+ ```tsx
205
+ <div role="group" aria-label="Profile picture">
206
+ <ScProfileImageUpdate … />
207
+ </div>
208
+ ```
209
+
210
+ **3. `className` is concatenated with `" " + className` unguarded**, so when you omit
211
+ it the root's class list literally contains `undefined`:
212
+ `class="scProfileImageUpdate undefined"`. Cosmetic, but never assert on the exact
213
+ `className` string, and don't write a global `.undefined` rule (it would hit dozens
214
+ of DS components that do this).
215
+
216
+ **4. `accept="image/*"` is hardcoded.** No `accept`, no `maxSize`, no type filter.
217
+ Validate inside `onUpload` — after the picker has already closed.
218
+
219
+ **5. No loading / uploading state.** Nothing indicates an in-flight upload and
220
+ nothing blocks a second click. Set `imageUrl` to a blob preview immediately and
221
+ disable your own surrounding UI if the request is slow.
222
+
223
+ **6. A broken `imageUrl` does not fall back to initials.** There is no `onError`
224
+ handler (`ScDp` has one). A 404 avatar URL renders the browser's broken-image glyph
225
+ inside the circle. If your URLs can 404, prefer `ScDp` for display and keep this
226
+ component for the edit affordance only, or pre-validate the URL.
227
+
228
+ **7. Fixed size, and the image is cropped.** `width: 7.5rem` on the root,
229
+ `6.25rem` square + `border-radius: full` + `object-fit: cover` on the image. A
230
+ non-square upload is centre-cropped with no crop UI. There is no `size` prop.
231
+
232
+ **8. `initials` defaults to `""`.** Forget it and an avatar-less user gets a blank
233
+ grey circle. Also: only 1–2 characters fit at 1.75rem inside 100px.
234
+
235
+ **9. It reports nothing back about the delete.** `onDelete` takes no argument and the
236
+ component keeps no state, so the circle only clears when *you* clear `imageUrl`.
237
+
238
+ ---
239
+
240
+ ## In the wild
241
+
242
+ ```tsx
243
+ // cxo-dashboard src/app/components/mobile-settings-content.tsx:252
244
+ <ScProfileImageUpdate
245
+ imageUrl={profileImage}
246
+ initials={profileInitials}
247
+ onUpload={handleProfileImageUpload}
248
+ onDelete={handleProfileImageDelete}
249
+ />
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Related
255
+
256
+ - `ScDp` — the read-only avatar with `size`, `variant` and an image-error fallback; use it everywhere you only display.
257
+ - `ScIntialProfileCover` — initials-only disc (`intial`, `size`).
258
+ - `ScImageField` — the labelled rectangular image field for logos inside forms.
259
+ - `ScFileField` — non-image uploads, with progress and error.
260
+ - `ScProfileSettingsComp` — the name/email rows this block sits above.
261
+ - `ScButton` / `ScVDivider` — what it composes internally.
@@ -0,0 +1,245 @@
1
+ ---
2
+ component: ScProfileOptions
3
+ package: "@streamoid/ui"
4
+ category: profile
5
+ status: legacy
6
+ renders: div
7
+ tags: [profile-menu, profile-dropdown, account-menu, logout, theme-switcher, figma-export, static]
8
+ related: [ScProfilePopup, ScProfile, ScWorkspace, ScMenuOptions, ScTabSwitcher]
9
+ do_not_confuse_with: [ScProfilePopup, ScProfileSettingsComp, ScProfile, ScPopUpMenu, ScMenuOptions]
10
+ ---
11
+
12
+ # ScProfileOptions
13
+
14
+ **Superseded — use `ScProfilePopup`.** This is the untouched Figma export of the
15
+ profile dropdown (`SC-Profile options`, node 6666:25516): a 264px panel with a
16
+ profile header, workspace + credits badge, four menu rows, a logout row and a
17
+ three-way theme switcher — **all values hardcoded and nothing wired**. Its only prop
18
+ is `className`. `ScProfilePopup` is the same design node rebuilt with real props,
19
+ slots, keyboard handling and a forwarded ref.
20
+
21
+ ## TL;DR for agents
22
+
23
+ - **Reach for it when:** never, in new code. Keep it only to avoid breaking an
24
+ existing import.
25
+ - **Don't reach for it when:** you need a working profile menu → **`ScProfilePopup`**.
26
+ - **Four things that will bite you:**
27
+ 1. There are **no data props**. Name, email, workspace, credits, labels and version
28
+ are all literal strings inside the component.
29
+ 2. There are **no callbacks**. Logout, Billing, Settings, Switch workspace and the
30
+ theme tabs do nothing at all.
31
+ 3. The first menu row renders `ScMenuOptions`' own default label — literally
32
+ **"Menu Option"** — because it is passed an `icon` but no `text`.
33
+ 4. It shows "Chris Hemsworth", "Kepler workspace" and "1000 credits left" to your
34
+ real users.
35
+
36
+ ---
37
+
38
+ ## 1. How to use it
39
+
40
+ ### Import
41
+
42
+ ```tsx
43
+ import { ScProfilePopup } from "@streamoid/ui"; // ← the replacement
44
+ import { ScProfileOptions } from "@streamoid/ui"; // legacy, className-only
45
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
46
+ ```
47
+
48
+ ### Minimal usage
49
+
50
+ The whole API:
51
+
52
+ ```tsx
53
+ <ScProfileOptions />
54
+ ```
55
+
56
+ The migration — same panel, real data:
57
+
58
+ ```tsx
59
+ <ScProfilePopup
60
+ name={user.name}
61
+ email={user.email}
62
+ avatar={user.avatarUrl}
63
+ workspaceName={ws.name}
64
+ userRole={ws.role}
65
+ credits={credits}
66
+ version={`v${APP_VERSION}`}
67
+ themeMode={themePreference}
68
+ onThemeModeChange={setThemePreference}
69
+ onLogout={logout}
70
+ onSwitchWorkspace={openSwitcher}
71
+ onTeams={() => go("team")}
72
+ onBilling={() => go("billing")}
73
+ onSettings={() => go("account")}
74
+ />
75
+ ```
76
+
77
+ ### Props
78
+
79
+ | Prop | Type | Default | Notes |
80
+ |---|---|---|---|
81
+ | `className` | `string` | – | Appended (concatenated unconditionally — you get a literal `undefined` class when omitted). |
82
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | Spread onto the root div. An `onClick` here fires for the whole panel, which is not useful. |
83
+
84
+ That is the entire public surface.
85
+
86
+ ### What it renders (all hardcoded)
87
+
88
+ | Region | Fixed content |
89
+ |---|---|
90
+ | Header | `ScProfile` with **no props** → "Chris Hemsworth" / "Kepler workspace" / broken `profile-image0.png` |
91
+ | Workspace | `ScWorkspace` with **no props** → "Kepler" / "Admin" / broken image, plus `ScBadges` "1000 credits left" (`success`, `opaque`) |
92
+ | Menu row 1 | `SiconTeam` + **"Menu Option"** (the `ScMenuOptions` default label) |
93
+ | Menu row 2 | `SiconBilling` + "Billing" |
94
+ | Button | `ScButton` "Switch workspace", `variant="secondary"`, `size="md"`, `SiconHome` icon |
95
+ | Menu row 3 | `SiconBolt` + "What's new", `variant="with-v"` → shows `ScMenuOptions`' default version **"v2.0.21"** |
96
+ | Menu row 4 | `SiconSettings` + "All settings" |
97
+ | Logout | `SiconLogout` + "Logout", `variant="error"` — a div, not a button |
98
+ | Theme | `ScTabSwitcher` with 3 icon-only `ScTabComp`s; **System is hardcoded active** |
99
+
100
+ ### Recipes
101
+
102
+ There is nothing to configure. The only useful recipe is the replacement above.
103
+
104
+ ---
105
+
106
+ ## 2. Where to use it
107
+
108
+ Nowhere new. It is the design-time artefact of the profile dropdown; the shipped
109
+ surface is `ScProfilePopup`:
110
+
111
+ - **Catalogix** — `app/components/AppSettingsPopup` renders `ScProfilePopup` and
112
+ notes in its own header comment that it replaced ~430 lines of hand-assembled
113
+ panel.
114
+ - **CXO / Photogenix / Artifax** — profile menus live in their sidebar shells.
115
+
116
+ ---
117
+
118
+ ## 3. When to use it
119
+
120
+ ### Use it when
121
+
122
+ - Never in product code. Legitimate uses are: a Figma-parity screenshot of the
123
+ original node, or keeping an old import compiling until it is migrated.
124
+
125
+ ### Don't use it — reach for this instead
126
+
127
+ | Situation | Use instead |
128
+ |---|---|
129
+ | A working profile dropdown with real identity, callbacks and slots | `ScProfilePopup` |
130
+ | Only the identity row | `ScProfile` |
131
+ | Only one menu row | `ScMenuOptions` |
132
+ | A generic popover of options anchored to a trigger | `ScPopUpMenu` + `ScMenuOptions` |
133
+ | The theme switcher on its own | `ScTabSwitcher` + `ScTabComp` (`type="icon-only"`) |
134
+ | The account settings **form** | compose `ScProfileImageUpdate` + `ScTextField` (see `ScProfileSettingsComp`) |
135
+
136
+ ### Don't confuse with
137
+
138
+ | You may actually want | Not this |
139
+ |---|---|
140
+ | `ScProfilePopup` — the live, propped panel built from the same Figma node | `ScProfileOptions` is the static export of it |
141
+ | `ScProfileSettingsComp` — the account **form** scaffold (first/last/email) | this is the **menu** |
142
+ | `ScProfile` — the identity row alone | this is the whole panel |
143
+ | `ScPopUpMenu` — generic anchored popover | this is the profile-specific panel body |
144
+
145
+ `ScProfileOptions` vs `ScProfilePopup` vs `ScProfileSettingsComp` vs `ScProfile` is
146
+ the highest-traffic mis-pick in this folder: **menu (static) / menu (live) / form /
147
+ row**.
148
+
149
+ ---
150
+
151
+ ## 4. Why to use it
152
+
153
+ Honestly: you shouldn't. What `ScProfilePopup` gives you over it —
154
+
155
+ - **Real props and callbacks** for every row, plus `show*` flags to drop rows.
156
+ - **Four nesting slots** (`headerActions`, `workspaceContent`, `menuContent`,
157
+ `preFooterContent`) with a stretch rule so nested DS components lay out correctly.
158
+ - **Keyboard access** — Enter/Space on the div-based rows, `role="radiogroup"` +
159
+ `role="radio"` + `aria-checked` on the theme tabs, focus-visible rings everywhere.
160
+ - **A forwarded ref** on the panel root, for positioning and outside-click dismissal.
161
+ - **Icon colour fixed** — `ScMenuOptions` colours only its label, so the built-in
162
+ icons inherit whatever the host happened to set; the popup drives them from
163
+ `--alias-text-and-icons-primary`.
164
+
165
+ The one thing this component still documents well is the **panel shell recipe**:
166
+ `surface-subtle` + `radius-4xl` + `0.03125rem border-subtle` + `backdrop-filter:
167
+ blur(2.625rem)`, 264px wide. `ScProfilePopup` reproduces it at 320px using an inset
168
+ box-shadow instead of a border (a real border eats a pixel of the content width).
169
+
170
+ ---
171
+
172
+ ## Gotchas
173
+
174
+ **1. `ScProfile` and `ScWorkspace` are rendered with no props.** Both fall back to
175
+ their placeholder identity **and** to `profileImage="profile-image0.png"`, so the
176
+ panel shows two broken-image glyphs in any host.
177
+
178
+ **2. "Menu Option" ships to production.** The first row passes only an `icon`:
179
+
180
+ ```tsx
181
+ // what the component actually does — text is omitted, so ScMenuOptions' default wins
182
+ <ScMenuOptions icon={<SiconTeam />} /> // renders "Menu Option"
183
+ ```
184
+
185
+ The intended label was "Teams" (`ScProfilePopup` fixes it with `teamsLabel="Teams"`).
186
+
187
+ **3. The version stamp is a lie.** The "What's new" row uses `variant="with-v"`
188
+ without a `version`, so `ScMenuOptions`' default `"v2.0.21"` renders regardless of
189
+ your app version.
190
+
191
+ **4. Nothing is interactive.** Every row is a bare `<div>` with no `onClick`, no
192
+ `role`, no `tabIndex`. Not clickable, not focusable, invisible to screen readers as a
193
+ menu.
194
+
195
+ **5. The theme switcher's active tab is hardcoded.** `active="true"` sits on the
196
+ System tab literally; clicking Light or Dark does nothing.
197
+
198
+ **6. The `scTabComp*` props passed to `ScTabSwitcher` are dead.**
199
+ `scTabCompicon` / `scTabCompactive` / `scTabComptype` are destructured to `_`-prefixed
200
+ names inside `ScTabSwitcher` and never used. Only `component`…`component5` render.
201
+
202
+ **7. Fixed 264px width** (`width: 16.5rem`) with `backdrop-filter: blur(42px)` — the
203
+ blur is invisible over an opaque background, and the panel will not stretch to a
204
+ host container.
205
+
206
+ **8. A real `border-width: 0.03125rem`** (0.5px) is used here, so the panel's outer
207
+ box is 1px wider and taller than the Figma frame. `ScProfilePopup` switched to
208
+ `box-shadow: inset` for exactly this reason.
209
+
210
+ ---
211
+
212
+ ## In the wild
213
+
214
+ _No host render site found — used by the agent runtime / composed internally._
215
+
216
+ No app imports it. Its live replacement is rendered here:
217
+
218
+ ```jsx
219
+ // catalogix/dashboard app/components/AppSettingsPopup/index.jsx:75
220
+ <ScProfilePopup
221
+ data-theme="Mode 1"
222
+ role="menu"
223
+ aria-label="Profile menu"
224
+ name={(userData && (userData.name || userData.email)) || ""}
225
+ email={(userData && userData.email) || ""}
226
+ avatar={userData?.imageUrl || undefined}
227
+ onProfileClick={() => goSettings("account")}
228
+ workspaceName={selectedWorkspace?.name || ""}
229
+ userRole={selectedWorkspace?.role || ""}
230
+ credits={creditsLabel}
231
+ onSwitchWorkspace={() => showWorkspaceList()}
232
+ themeMode={themePreference}
233
+ onThemeModeChange={setThemePreference}
234
+ />
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Related
240
+
241
+ - `ScProfilePopup` — **the replacement.** Same design node, real API.
242
+ - `ScProfile` / `ScWorkspace` — the two identity rows it stacks.
243
+ - `ScMenuOptions` — one menu row (`variant="with-v" | "error" | "default"`).
244
+ - `ScTabSwitcher` / `ScTabComp` — the theme switcher pair.
245
+ - `ScBadges` — the credits chip it uses (the popup inlines its own credits pill instead).