@streamoid/ui 0.6.16 → 0.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +35 -18
  2. package/dist/docs/AGENTS.md +321 -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 +213 -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/ScWorkspaceCard.md +234 -0
  120. package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
  121. package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
  122. package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
  123. package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
  124. package/dist/docs/StreamoidSidebar.md +403 -0
  125. package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
  126. package/dist/docs/UsageHistoryMobile.md +235 -0
  127. package/dist/docs/components.json +4849 -0
  128. package/dist/index.css +43 -37
  129. package/dist/index.d.mts +10 -0
  130. package/dist/index.d.ts +10 -0
  131. package/package.json +3 -2
@@ -0,0 +1,265 @@
1
+ ---
2
+ component: ScWorkspaceSettingsMobile
3
+ package: "@streamoid/ui"
4
+ category: mobile
5
+ status: stable
6
+ renders: div
7
+ tags: [mobile, workspace, settings, team, owner, members, card, role]
8
+ related: [ScWorkspaceSwitchMobile, ScWorkspaceSwitchMobileV2, ScWorkspaceCard, ScTableListMobile]
9
+ do_not_confuse_with: [ScWorkspaceSwitchMobile, ScWorkspaceSwitchMobileV2, ScWorkspaceCard, ScWorkspaceSwitchCard, ScProfileSettingsComp]
10
+ ---
11
+
12
+ # ScWorkspaceSettingsMobile
13
+
14
+ **The mobile workspace card with two info rows.** A fixed 326px rounded card: a 48px
15
+ initials tile beside the workspace name with an always-blue role badge on the right, a
16
+ divider, then **two** icon+text rows — the owner email and the team count. Structurally
17
+ `ScWorkspaceSwitchMobile` plus the second row.
18
+
19
+ ## TL;DR for agents
20
+
21
+ - **Reach for it when:** a mobile workspace-settings screen needs a workspace summary
22
+ that also shows how many people are in it.
23
+ - **Don't reach for it when:** the card's job is *switching* workspaces
24
+ (→ `ScWorkspaceSwitchMobileV2`, which is what CXO actually ships), or you're on desktop
25
+ (→ `ScWorkspaceCard` / `ScWorkspaceSwitchCard`).
26
+ - **Four things that will bite you:**
27
+ 1. **It is `width: 326px`, fixed.** Not `100%`.
28
+ 2. **`ownerIcon` and `teamIcon` have no defaults**, but both 48px icon gutters render
29
+ regardless — omit them and you get two blocks of empty space.
30
+ 3. **The role badge is always info-blue**, whatever `role` says.
31
+ 4. **`currentWs` defaults to `true`**, so in a list every card looks active.
32
+
33
+ ---
34
+
35
+ ## 1. How to use it
36
+
37
+ ### Import
38
+
39
+ ```tsx
40
+ import { ScWorkspaceSettingsMobile } from "@streamoid/ui";
41
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
42
+ ```
43
+
44
+ ### Minimal usage
45
+
46
+ ```tsx
47
+ import { SiconUser, SiconUsers } from "@streamoid/icons";
48
+
49
+ <ScWorkspaceSettingsMobile
50
+ storeName={ws.name}
51
+ initials={ws.initials}
52
+ role={ws.role === "admin" ? "Admin" : "Member"}
53
+ ownerId={ws.ownerEmail}
54
+ ownerIcon={<SiconUser size={24} />}
55
+ teamCount={`${ws.memberCount} Users`}
56
+ teamIcon={<SiconUsers size={24} />}
57
+ currentWs={ws._id === currentId}
58
+ />
59
+ ```
60
+
61
+ ### Props
62
+
63
+ | Prop | Type | Default | Notes |
64
+ |---|---|---|---|
65
+ | `storeName` | `string` | `"Stores"` | ⚠️ Demo default. The workspace name — note `storeName`, not `wsName`. 16px/500. It has `overflow: hidden; text-overflow: ellipsis` but **no `white-space: nowrap`**, so a long name wraps instead of ellipsising — see Gotcha 10. |
66
+ | `initials` | `string` | `"WS"` | ⚠️ Demo default. Rendered into a hand-rolled 48px `--radius-xl` tile. No name-derived fallback, no image support. |
67
+ | `role` | `string` | `"Admin"` | ⚠️ Demo default. Free text in a **fixed 80px always-blue** badge. Colour never varies. |
68
+ | `ownerId` | `string` | `"rohan@streamoid.com"` | ⚠️ Demo default — a real-looking Streamoid address. First info row. |
69
+ | `teamCount` | `string` | `"12 Users"` | ⚠️ Demo default. Second info row. You append the word "Users" — the card does no pluralisation. |
70
+ | `currentWs` | `boolean` | `true` | ⚠️ Defaults to **true**. Picks the `current` vs `other` skin. |
71
+ | `ownerIcon` | `JSX.Element` | – | **Slot** for the first info row's icon. No default; the gutter renders anyway. |
72
+ | `teamIcon` | `JSX.Element` | – | **Slot** for the second info row's icon. Same caveat. |
73
+ | `className` | `string` | – | Appended after internal classes. Unguarded concat — see Gotcha 5. |
74
+ | `onClick` | `(e: React.MouseEvent) => void` | – | Attached to the root. No `role`/`tabIndex`/`cursor` come with it. |
75
+ | `...props` | `React.HTMLAttributes<HTMLDivElement>` | – | Spread onto the root `div`. |
76
+
77
+ ### What each `currentWs` value renders
78
+
79
+ | `currentWs` | Class | Background | Border |
80
+ |---|---|---|---|
81
+ | `true` | `variant-current` | `--alias-surface-canvas` | 0.5px `--alias-border-subtle` |
82
+ | `false` | `variant-other` | `--alias-surface-base` | 0.5px `--alias-border-subtle` |
83
+
84
+ Both variants keep the border (unlike `ScWorkspaceSwitchMobile`, whose current variant
85
+ has none), so the two skins differ only by background.
86
+
87
+ ### Recipes
88
+
89
+ ```tsx
90
+ // Make it fluid (the card is 326px by default)
91
+ <ScWorkspaceSettingsMobile storeName={ws.name} className="w-full" />
92
+
93
+ // Both icon slots — the inner wrapper is 24×24, so size 20–24
94
+ import { SiconUser, SiconUsers } from "@streamoid/icons";
95
+ <ScWorkspaceSettingsMobile
96
+ storeName={ws.name}
97
+ initials={ws.initials}
98
+ role="Admin"
99
+ ownerId={ws.ownerEmail}
100
+ ownerIcon={<SiconUser size={24} />}
101
+ teamCount={`${n} ${n === 1 ? "User" : "Users"}`} // pluralise yourself
102
+ teamIcon={<SiconUsers size={24} />}
103
+ currentWs={false}
104
+ className="w-full"
105
+ />
106
+
107
+ // Tappable — add the semantics yourself
108
+ <ScWorkspaceSettingsMobile
109
+ storeName={ws.name}
110
+ onClick={() => openManageSheet(ws)}
111
+ role="Admin"
112
+ tabIndex={0}
113
+ className="w-full cursor-pointer"
114
+ />
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 2. Where to use it
120
+
121
+ Its intended home is the mobile **workspace settings** screen: the summary card at the
122
+ top of "manage this workspace", or one card per workspace in a settings list where the
123
+ member count matters.
124
+
125
+ CXO's mobile settings screen currently renders `ScWorkspaceSwitchMobileV2` instead
126
+ (`cxo-dashboard/src/app/components/mobile-settings-content.tsx:313`), which shows
127
+ `role · plan · owner` but **not** a team count — so this card's distinguishing feature
128
+ is exactly what that screen is missing.
129
+
130
+ It composes nothing from the DS: the initials tile and the role badge are hand-rolled,
131
+ and both icons are slots you fill.
132
+
133
+ ---
134
+
135
+ ## 3. When to use it
136
+
137
+ ### Use it when
138
+
139
+ - The screen is workspace **settings** (not switching) on mobile.
140
+ - The team size is a fact worth showing on the card.
141
+ - You have icons to put in both info rows.
142
+
143
+ ### Don't use it — reach for this instead
144
+
145
+ | Situation | Use instead |
146
+ |---|---|
147
+ | Switching workspaces on mobile | `ScWorkspaceSwitchMobileV2` (fluid, images, `plan`, `type` triad) |
148
+ | The V1 switch card with one owner row | `ScWorkspaceSwitchMobile` — but it's `status: legacy` |
149
+ | Desktop workspace card / switcher | `ScWorkspaceCard` / `ScWorkspaceSwitchCard` / `StreamoidWorkspaceSwitcher` |
150
+ | A whole profile-settings scaffold | `ScProfileSettingsComp` |
151
+ | Listing the workspace's members individually | `ScTableListMobile`, one per member |
152
+ | A role chip whose colour varies | `ScRoleMobile` (two skins) or `ScBadges` (any colour) |
153
+ | A workspace avatar on its own, with image support | `ScDp` (`variant="workspace"`) |
154
+
155
+ ### Don't confuse with
156
+
157
+ | You may actually want | Not this |
158
+ |---|---|
159
+ | `ScWorkspaceSwitchMobile` — **the same card minus the team row**. Identical props (`storeName`, `initials`, `role`, `ownerId`, `currentWs`, `ownerIcon`) with only `teamCount`/`teamIcon` added here, and a slightly different skin pair | The two are trivially interchangeable at a glance; decide by screen: settings → this, switching → V2 |
160
+ | `ScWorkspaceSwitchMobileV2` — the current mobile workspace row; `wsName`, `type`, `imageUrl`, `plan`, fluid width, composes `ScDp` | Different prop names for the same data |
161
+ | `ScWorkspaceCard` — the desktop workspace tile | Desktop metrics |
162
+ | `ScProfileSettingsComp` — a whole settings screen scaffold, for a person | Not a workspace card |
163
+
164
+ `ScWorkspaceSettingsMobile` / `ScWorkspaceSwitchMobile` / `ScWorkspaceSwitchMobileV2` are
165
+ three near-namesakes for the same object. Ship V2 unless you specifically need the team
166
+ count (this one) or the V1 look.
167
+
168
+ ---
169
+
170
+ ## 4. Why to use it
171
+
172
+ - **The only mobile workspace card that shows team size**, with the icon gutters already
173
+ aligned so both info rows line up under the name.
174
+ - **Truncation is handled** on `ownerId` and `teamCount`
175
+ (`flex: 1 0 0; min-width: 0` + `overflow: hidden; text-overflow: ellipsis;
176
+ white-space: nowrap`), so long emails and big counts never break the card.
177
+ `storeName` gets the same flex treatment but is missing `white-space: nowrap`, so it
178
+ wraps rather than ellipsising (Gotcha 10).
179
+ - **Icon slots keep app-specific iconography out of the DS** — you pass whichever
180
+ `Sicon*` your product uses for "owner" and "team".
181
+ - **Fixed 48px gutters** mean the two rows are optically aligned regardless of icon
182
+ width, which is what hand-rolled versions get wrong.
183
+
184
+ ---
185
+
186
+ ## Gotchas
187
+
188
+ **1. Fixed `width: 326px`.** The root is not fluid; it will not fill a mobile sheet.
189
+
190
+ ```tsx
191
+ // WRONG — 326px card inside a full-width settings screen
192
+ <ScWorkspaceSettingsMobile storeName={ws.name} />
193
+
194
+ // RIGHT
195
+ <ScWorkspaceSettingsMobile storeName={ws.name} className="w-full" />
196
+ ```
197
+
198
+ **2. Both icon slots are empty by default, but their gutters are not.**
199
+ `.iconContainer` is `width: 48px; flex-shrink: 0` and renders unconditionally; the icon
200
+ inside is `{ownerIcon && …}` / `{teamIcon && …}`. Omit them and you get two 48px voids.
201
+
202
+ ```tsx
203
+ // WRONG — two rows of text indented by 48px of nothing
204
+ <ScWorkspaceSettingsMobile ownerId={email} teamCount="12 Users" />
205
+
206
+ // RIGHT
207
+ <ScWorkspaceSettingsMobile
208
+ ownerId={email}
209
+ ownerIcon={<SiconUser size={24} />}
210
+ teamCount="12 Users"
211
+ teamIcon={<SiconUsers size={24} />}
212
+ />
213
+ ```
214
+
215
+ **3. The role badge is always info-blue.** `.roleBadge` hardcodes
216
+ `--alias-fill-info-soft` + `--alias-border-info` + info text, fixed 80px wide. `"Member"`
217
+ looks like `"Admin"`.
218
+
219
+ **4. `currentWs` defaults to `true`.** In a list, forgetting it makes every card look
220
+ like the active workspace.
221
+
222
+ **5. `className` is concatenated unguarded.** Omit it and the root carries a literal
223
+ `undefined` class.
224
+
225
+ **6. `.initialsText` has a wrong fallback.** It uses
226
+ `var(--font-size-font-size-md, 14px)` where `md` is 16px everywhere else, so the initials
227
+ change size depending on whether the tokens stylesheet is loaded.
228
+
229
+ **7. `teamCount` is a string you build.** No number formatting, no pluralisation — pass
230
+ `"1 User"` vs `"12 Users"` yourself.
231
+
232
+ **8. `onClick` brings no semantics.** Plain `div`, no `cursor: pointer` in the CSS. Add
233
+ `role="button"`, `tabIndex={0}` and `onKeyDown` yourself. Note the `role` prop here is
234
+ this component's *badge text*, so if you need the ARIA role, wrap the card.
235
+
236
+ **9. `backdrop-filter: blur(4px)`** on the root creates a containing block, so any
237
+ `position: fixed` descendant anchors to this card rather than the viewport.
238
+
239
+ **10. `storeName` wraps, it does not ellipsise.** `.storeName` sets `overflow: hidden`
240
+ and `text-overflow: ellipsis` but omits `white-space: nowrap`, so a long workspace name
241
+ breaks onto a second line (and then gets clipped) instead of showing "…". The two info
242
+ rows (`ownerId`, `teamCount`) do set `nowrap` and truncate correctly. Cap the name
243
+ yourself if it can be long.
244
+
245
+ ---
246
+
247
+ ## In the wild
248
+
249
+ _No host render site found — used by the agent runtime / composed internally._
250
+
251
+ To be precise: it is exported from `@streamoid/ui` but no host app renders it, and it is
252
+ not agent-runtime — it is currently unused. Where it belongs is CXO's mobile settings
253
+ screen, which today renders `ScWorkspaceSwitchMobileV2` for the same slot
254
+ (`cxo-dashboard/src/app/components/mobile-settings-content.tsx:313`) and therefore shows
255
+ `role · plan · owner` without a member count.
256
+
257
+ ---
258
+
259
+ ## Related
260
+
261
+ - `ScWorkspaceSwitchMobile` — the same card minus the team row (`status: legacy`).
262
+ - `ScWorkspaceSwitchMobileV2` — the current mobile workspace row; what CXO ships.
263
+ - `ScWorkspaceCard` / `ScWorkspaceSwitchCard` / `StreamoidWorkspaceSwitcher` — desktop.
264
+ - `ScTableListMobile` — a member row, if you're listing the team rather than counting it.
265
+ - `ScDp` — the avatar primitive V2 uses; this card hand-rolls a 48px initials tile.
@@ -0,0 +1,314 @@
1
+ ---
2
+ component: ScWorkspaceSwitchCard
3
+ also_exports: [NscWorkspaceSwitch]
4
+ package: "@streamoid/ui"
5
+ category: cards
6
+ status: stable
7
+ renders: div
8
+ tags: [workspace, switch-workspace, tenant, current-workspace, role, plan, owner, hover-actions, leave-workspace]
9
+ related: [ScWorkspaceCard, ScWorkspaceSwitchMobileV2, ScDp, StreamoidWorkspaceSwitcher, ScProfilePopup]
10
+ do_not_confuse_with: [ScWorkspaceCard, ScWorkspace, StreamoidWorkspaceSwitcher, ScWorkspaceSwitchMobile, ScWorkspaceSwitchMobileV2]
11
+ used_by: [cxo, photogenix, catalogix, artifax]
12
+ ---
13
+
14
+ # ScWorkspaceSwitchCard
15
+
16
+ **One row in the "switch workspace" list.** A 90px workspace tile, the workspace name,
17
+ and a Role / Plan / Workspace-Owner detail strip — plus up to two **hover-only**
18
+ trailing actions: "Switch Workspace" (other workspaces), a settings icon (admins), or
19
+ a red leave button (non-admins). All four host apps render it; the modal around it is
20
+ hand-rolled in each.
21
+
22
+ ## TL;DR for agents
23
+
24
+ - **Reach for it when:** you are building a workspace list where each row shows role,
25
+ plan and owner and can be switched into, configured or left.
26
+ - **Don't reach for it when:** you need the card with a user count and a persistent
27
+ CTA (→ `ScWorkspaceCard`), a mobile row (→ `ScWorkspaceSwitchMobileV2`), or a bare
28
+ workspace identity line (→ `ScProfilePopup`'s workspace block).
29
+ - **Five things that will bite you:**
30
+ 1. **Every action is hover-gated.** No hover → no buttons. Touch devices and
31
+ keyboard users can never reach them. That is why the `*Mobile` twins exist.
32
+ 2. `role` is a **free string** and drives everything: `role.toLowerCase() === "admin"`
33
+ picks amber text **and** the settings button; anything else (including
34
+ `"owner"`) gets info-blue and the **red leave** button.
35
+ 3. Its props do **not** extend `HTMLAttributes` — no `onClick`, no `style`, no
36
+ `data-*`, no `aria-*`. Only the 12 listed props.
37
+ 4. ⚠️ `ownerId` defaults to a real person's address, **`"rohan@streamoid.com"`**,
38
+ and `wsName` to `"Workspace name is kepler"`.
39
+ 5. `cardOther` sets `cursor: pointer` on the whole card, but the card has **no
40
+ click handler** — only the buttons do. The cursor lies.
41
+
42
+ ---
43
+
44
+ ## 1. How to use it
45
+
46
+ ### Import
47
+
48
+ ```tsx
49
+ import { ScWorkspaceSwitchCard } from "@streamoid/ui";
50
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
51
+ ```
52
+
53
+ ### Minimal usage
54
+
55
+ ```tsx
56
+ <ScWorkspaceSwitchCard
57
+ wsName={ws.name}
58
+ role={ws.role}
59
+ plan={ws.plan}
60
+ ownerId={ws.ownerEmail}
61
+ type={ws.isCurrent ? "currentWS" : "otherWS"}
62
+ onSwitch={() => void switchTo(ws)}
63
+ />
64
+ ```
65
+
66
+ ### Props
67
+
68
+ | Prop | Type | Default | Notes |
69
+ |---|---|---|---|
70
+ | `wsName` | `string` | `"Workspace name is kepler"` | ⚠️ Real default. Also the source of derived initials. Single line + ellipsis. |
71
+ | `role` | `string` | `"Admin"` | ⚠️ Real default, and a **free string**. Compared case-insensitively to `"admin"`; displayed Title-Cased (`charAt(0).toUpperCase() + rest.toLowerCase()`). |
72
+ | `plan` | `string` | `"Free"` | ⚠️ Real default. Plain secondary text. |
73
+ | `ownerId` | `string` | `"rohan@streamoid.com"` | ⚠️ Real default — a real address. Always pass the actual owner. |
74
+ | `initials` | `string` | derived from `wsName` | First letters of the first two words, uppercased; `"WS"` if that yields nothing. |
75
+ | `imageUrl` | `string` | – | When set, the tile renders the image (`ScDp type="image"`); otherwise initials. |
76
+ | `type` | `"currentWS"` \| `"otherWS"` | `"currentWS"` | ⚠️ Defaults to **current**. Drives surface, border, cursor and whether "Switch Workspace" can appear. |
77
+ | `hover` | `boolean` | – | Controlled override of the internal hover state. `undefined` → the card tracks its own mouse enter/leave. Pass `true` to force the actions visible (Figma parity / tests). |
78
+ | `className` | `string` | – | Appended after the internal classes. |
79
+ | `onSwitch` | `() => void` | – | "Switch Workspace" button. **`otherWS` + hover only.** |
80
+ | `onSettings` | `() => void` | – | Settings icon button. **admin + hover only** (both `type`s). |
81
+ | `onLeave` | `() => void` | – | Red leave button. **non-admin + hover only** (both `type`s). |
82
+
83
+ Nothing else is accepted — `ScWorkspaceSwitchCardProps` is a plain interface, not an
84
+ `HTMLAttributes` extension.
85
+
86
+ ### What renders in each state
87
+
88
+ | Region | `currentWS` | `otherWS` |
89
+ |---|---|---|
90
+ | Surface | `surface-raised` + `border-default`, `cursor: default` | `surface-base` + `border-subtle`, `cursor: pointer`; hover → `surface-basesubtle` |
91
+ | Tile | `ScDp` forced to **90 × 90**, `radius-xl`, bg `neutralactive` | same, bg `neutralselected` |
92
+ | Details | Role · Plan · Workspace Owner, split by two `ScVDivider`s | identical |
93
+ | "Switch Workspace" | **never** | hover only |
94
+ | Settings icon | hover **and** `role` is admin | hover **and** `role` is admin |
95
+ | Leave (red) | hover **and** `role` is not admin | hover **and** `role` is not admin |
96
+
97
+ Role colour: admin → `text-and-icons-warning` (amber); everything else →
98
+ `text-and-icons-info` (blue).
99
+
100
+ ### Recipes
101
+
102
+ ```tsx
103
+ // Current + other sections (the shape all four hosts use)
104
+ <>
105
+ <ScWorkspaceSwitchCard
106
+ wsName={current.name}
107
+ initials={current.initials}
108
+ imageUrl={current.imageUrl}
109
+ role={current.role}
110
+ plan={current.plan}
111
+ ownerId={current.ownerEmail}
112
+ type="currentWS"
113
+ onSettings={() => openSettings(current)}
114
+ onLeave={() => confirmLeave(current)}
115
+ />
116
+ {others.map((ws) => (
117
+ <ScWorkspaceSwitchCard
118
+ key={ws.id}
119
+ wsName={ws.name}
120
+ initials={ws.initials}
121
+ imageUrl={ws.imageUrl}
122
+ role={ws.role}
123
+ plan={ws.plan}
124
+ ownerId={ws.ownerEmail}
125
+ type="otherWS"
126
+ onSwitch={() => void switchTo(ws)}
127
+ onSettings={() => openSettings(ws)}
128
+ onLeave={() => confirmLeave(ws)}
129
+ />
130
+ ))}
131
+ </>
132
+
133
+ // Force the actions visible — screenshots, Figma parity, or a touch fallback
134
+ <ScWorkspaceSwitchCard wsName={ws.name} type="otherWS" hover onSwitch={switchTo} />
135
+
136
+ // Make the whole row keyboard-reachable (the card gives you no semantics)
137
+ <div role="button" tabIndex={0}
138
+ onClick={() => void switchTo(ws)}
139
+ onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); void switchTo(ws); } }}>
140
+ <ScWorkspaceSwitchCard wsName={ws.name} type="otherWS" onSwitch={() => void switchTo(ws)} />
141
+ </div>
142
+ ```
143
+
144
+ ---
145
+
146
+ ## 2. Where to use it
147
+
148
+ Stacked in a scrolling list inside a **host-owned** modal or drawer. Every app builds
149
+ that shell itself:
150
+
151
+ | App | Shell |
152
+ |---|---|
153
+ | CXO | `src/app/components/app-workspace-switcher.tsx` (+ `app-organization-switcher.tsx` for orgs) |
154
+ | Catalogix | `app/containers/LeftMenu/WorkspaceListing/index.jsx` |
155
+ | Photogenix | `dashboard/client/src/components/layout/Sidebar.tsx` |
156
+ | Artifax | `packages/shared/src/components/DashboardSidebar.tsx` |
157
+
158
+ The usual trigger is `ScProfilePopup`'s `onSwitchWorkspace`. `StreamoidWorkspaceSwitcher`
159
+ is the DS's own attempt at that modal, but no app uses it (see its README).
160
+
161
+ ---
162
+
163
+ ## 3. When to use it
164
+
165
+ ### Use it when
166
+
167
+ - The row's job is **role + plan + owner at a glance**, with switch/settings/leave as
168
+ secondary hover affordances.
169
+ - You are rendering a current-vs-others list and want the two surfaces to differ
170
+ without writing the token pairs.
171
+
172
+ ### Don't use it — reach for this instead
173
+
174
+ | Situation | Use instead |
175
+ |---|---|
176
+ | Workspace card with a **user count** and an always-visible CTA button | `ScWorkspaceCard` |
177
+ | Mobile workspace row (tap, no hover) | `ScWorkspaceSwitchMobileV2` (or `ScWorkspaceSwitchMobile`, `ScWorkspaceSettingsMobile`) |
178
+ | A one-line workspace identity (logo + name + role) | `ScProfilePopup`'s workspace block (`ScWorkspace` itself is not exported) |
179
+ | The whole switch **modal**, prebuilt | `StreamoidWorkspaceSwitcher` — but read its Tailwind caveat first |
180
+ | A generic pick-one option card | `ScDefaultCard` |
181
+ | A store row (Catalogix) | `ScStoreCard` / `ScCatalogixStoreTableList` |
182
+ | Just the workspace tile | `ScDp variant="workspace"` / `ScIntialProfileCover` |
183
+
184
+ ### Don't confuse with
185
+
186
+ | You may actually want | Not this |
187
+ |---|---|
188
+ | `ScWorkspaceCard` — `workspaceName`/`role`/`userCount`/`ownerEmail`/`buttonText`, persistent button, no plan, no hover gating | `ScWorkspaceSwitchCard` has `plan`, hover actions, and no user count |
189
+ | `ScWorkspace` — the bare two-line identity row (**not exported**) | this is the full row with actions |
190
+ | `StreamoidWorkspaceSwitcher` — the modal that would contain rows like this | this is one row |
191
+ | `ScWorkspaceSwitchMobileV2` — mobile twin; has a third `type="display"` and a single `onClick` | this is desktop and hover-driven |
192
+ | `NscWorkspaceSwitch` | **`@deprecated` alias of this same component.** Import `ScWorkspaceSwitchCard`. |
193
+
194
+ ### Prop-name trap across the family
195
+
196
+ | Component | Name prop | Role prop | Owner prop |
197
+ |---|---|---|---|
198
+ | `ScWorkspaceSwitchCard` | `wsName` | `role` | `ownerId` |
199
+ | `ScWorkspaceCard` | `workspaceName` | `role` | `ownerEmail` |
200
+ | `ScWorkspaceSwitchMobile` | `storeName` | `role` | `ownerId` |
201
+ | `ScWorkspaceSwitchMobileV2` | `wsName` | `role` | `ownerId` |
202
+ | `ScWorkspace` (internal) | `workspaceName` | `userRole` | — |
203
+
204
+ ---
205
+
206
+ ## 4. Why to use it
207
+
208
+ - **Current vs other is a token pair, not a colour.** `surface-raised`/`border-default`
209
+ against `surface-base`/`border-subtle`, with `surface-basesubtle` on hover — three
210
+ greys that stay distinguishable in light mode, where a hand-picked grey collapses to
211
+ white.
212
+ - **The action matrix is already correct.** Switch only for other workspaces, settings
213
+ only for admins, leave only for non-admins, none of them on the current card's
214
+ switch slot. Getting that wrong ships a "leave" button to the owner of the workspace.
215
+ - **`min-width: 0` + `flex: 1 0 0` in the right three places** so long names and owner
216
+ emails ellipsise instead of pushing the buttons off the card — the most common bug
217
+ when this row is hand-rolled.
218
+ - **Initials derivation for free** (first letters of two words, `"WS"` fallback), so
219
+ you can pass `wsName` alone.
220
+ - **Consistent across four apps** — the same row in CXO, Catalogix, Photogenix and
221
+ Artifax, differing only in the modal around it.
222
+
223
+ ---
224
+
225
+ ## Gotchas
226
+
227
+ **1. All actions are hover-only, and they are plain `<div onClick>`s.** No `role`, no
228
+ `tabIndex`, no keyboard activation. A keyboard-only or touch user cannot switch, open
229
+ settings or leave from this card. Provide your own path (a wrapping `role="button"`,
230
+ or `hover` forced on for touch), or use the `*Mobile` twins.
231
+
232
+ **2. `role` is a free string with real consequences.**
233
+
234
+ ```tsx
235
+ // WRONG — "Owner" is not "admin": info-blue text AND the red leave button
236
+ <ScWorkspaceSwitchCard wsName={ws.name} role="Owner" onLeave={leave} />
237
+
238
+ // RIGHT — normalise, then relabel elsewhere if you need to
239
+ <ScWorkspaceSwitchCard wsName={ws.name} role={isAdmin ? "Admin" : "Member"} onSettings={settings} />
240
+ ```
241
+
242
+ Comparison is `role?.toLowerCase() === "admin"`, so `"ADMIN"` and `"admin"` are fine;
243
+ display is force-Title-Cased, so `"co-admin"` renders as `"Co-admin"` **and** is
244
+ treated as a non-admin.
245
+
246
+ **3. Personal data in the defaults.** `ownerId` defaults to
247
+ `"rohan@streamoid.com"`, `plan` to `"Free"` and `wsName` to
248
+ `"Workspace name is kepler"`. This has already shipped as a real bug: Artifax's
249
+ `packages/shared/src/components/DashboardSidebar.tsx:1032` carries a comment about every row falling back to
250
+ `"Free" + "rohan@streamoid.com"` because the store's `/workspaces` payload has no
251
+ plan. Always pass real values, or `""`.
252
+
253
+ **4. No `onClick` / `style` / `data-*` on the card.** The interface lists 12 props and
254
+ does not extend `HTMLAttributes`. To make the whole row clickable, wrap it (and note
255
+ the inner buttons already `stopPropagation`, so a wrapper handler won't double-fire).
256
+
257
+ **5. The `otherWS` cursor implies a click that does not exist.** `.cardOther` sets
258
+ `cursor: pointer` on the whole card, but only the "Switch Workspace" button is wired.
259
+ Either wrap the card in your own click handler or accept the mismatch.
260
+
261
+ **6. `ScDp`'s `size` is overridden.** `.dp` forces `width/height: 90px !important`, so
262
+ the nested tile is always 90px regardless of anything you would pass. The tile's
263
+ `border-radius` is also overridden inline to `radius-xl`.
264
+
265
+ **7. `hover` is a controlled override, not an event.** Passing `hover={false}` pins
266
+ the actions **hidden** forever, including on real mouse-over. Pass `undefined` (omit
267
+ it) for normal behaviour.
268
+
269
+ **8. Both hover actions can appear at once.** An admin viewing another workspace gets
270
+ "Switch Workspace" **and** the settings icon; a member gets "Switch Workspace" **and**
271
+ the red leave button. Budget the horizontal space — the details strip shrinks to make
272
+ room.
273
+
274
+ **9. Leave is available on the current workspace too.** `isHover && !isAdmin` is not
275
+ gated on `type`, so a member sees the red button on the card they are currently in.
276
+ Confirm destructively (all four hosts open a confirm modal from `onLeave`).
277
+
278
+ **10. `NscWorkspaceSwitch` is a deprecated alias** of this export, kept for old
279
+ imports. Don't use it in new code.
280
+
281
+ ---
282
+
283
+ ## In the wild
284
+
285
+ ```tsx
286
+ // cxo-dashboard src/app/components/app-workspace-switcher.tsx:408
287
+ <ScWorkspaceSwitchCard
288
+ wsName={currentWorkspace.name}
289
+ initials={currentWorkspace.initials}
290
+ imageUrl={currentWorkspace.imageUrl}
291
+ role={currentWorkspace.role}
292
+ plan={currentWorkspace.plan}
293
+ ownerId={currentWorkspace.ownerEmail}
294
+ type="currentWS"
295
+ onSwitch={() => void handleSwitch(currentWorkspace)}
296
+ onSettings={() => { setEditingWs(currentWorkspace); setUpdateModalOpen(true); }}
297
+ onLeave={() => { setLeavingWs(currentWorkspace); setConfirmLeaveOpen(true); }}
298
+ />
299
+ ```
300
+
301
+ Also: `catalogix/dashboard app/containers/LeftMenu/WorkspaceListing/index.jsx:371`,
302
+ `photogenix_v2 dashboard/client/src/components/layout/Sidebar.tsx:1129`, and
303
+ `artifax packages/shared/src/components/DashboardSidebar.tsx:1134`.
304
+
305
+ ---
306
+
307
+ ## Related
308
+
309
+ - `ScWorkspaceCard` — user count + persistent CTA instead of hover actions.
310
+ - `ScWorkspaceSwitchMobileV2` — the mobile twin (tap, no hover, extra `"display"` type).
311
+ - `StreamoidWorkspaceSwitcher` — the DS's prebuilt modal; unused by hosts, needs Tailwind.
312
+ - `ScProfilePopup` — its `onSwitchWorkspace` is what opens the list.
313
+ - `ScDp` — the tile it composes (forced to 90px here).
314
+ - `ScVDivider` — the two separators in the details strip.