@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,345 @@
1
+ ---
2
+ component: ScCatalogixInvite
3
+ package: "@streamoid/ui"
4
+ category: cards
5
+ status: stable
6
+ renders: div
7
+ tags: [invite, app-access, catalogix, toggle, store-access, permissions, user-management, settings, search, checkbox-list]
8
+ related: [ScArtifaxInvite, ScPhtogenixInvite, ScAppCard, ScToggleSwitch, ScPairtext, ScOnlyField]
9
+ do_not_confuse_with: [ScArtifaxInvite, ScPhtogenixInvite, ScAppCard, ScAppListingCard, ScAccess, ScCatalogixStoreTableList, ScCheckField]
10
+ ---
11
+
12
+ # ScCatalogixInvite
13
+
14
+ **The Catalogix access row *plus* its store-scoping panel.** Same header as the
15
+ other invite rows — `ScAppCard` + `ScToggleSwitch` — and when the toggle is on it
16
+ reveals a nested panel: "Store access", an *N* Selected count, a search field, and
17
+ a checkbox list of stores. The only member of the invite trio with a sub-scope.
18
+
19
+ ## TL;DR for agents
20
+
21
+ - **Reach for it when:** you're building the per-app access list in an invite /
22
+ edit-user flow and the Catalogix row has to pick **which stores** the user can see.
23
+ - **Don't reach for it when:** access is a plain on/off (→ `ScArtifaxInvite`), it's
24
+ the Photogenix row (→ `ScPhtogenixInvite`), or you want a standalone
25
+ searchable multi-select that isn't bolted to an app toggle (→ `ScOnlyField` +
26
+ `ScPairtext`, or `ScCheckField`).
27
+ - **Five things that will bite you:**
28
+ 1. ⚠️ Omit `stores` and you ship **five dummy rows labelled "Pairtext"** — the
29
+ Figma placeholder. Pass `stores` (even `[]`) always.
30
+ 2. It does **not** filter. `searchValue`/`onSearchChange` are a plain controlled
31
+ input; *you* filter `stores` before passing them.
32
+ 3. Pass `searchValue` without `onSearchChange` and the search box is read-only.
33
+ 4. The store list has **no max-height and no overflow** — 300 stores means a
34
+ 300-row-tall modal. Cap it yourself.
35
+ 5. ⚠️ Unlike its two siblings it defaults to **OFF** (`active = "false"`) and it
36
+ draws **no bottom divider**.
37
+
38
+ ---
39
+
40
+ ## 1. How to use it
41
+
42
+ ### Import
43
+
44
+ ```tsx
45
+ import { ScCatalogixInvite } from "@streamoid/ui";
46
+ import { SiconCatalogix } from "@streamoid/icons";
47
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
48
+ ```
49
+
50
+ ### Minimal usage
51
+
52
+ ```tsx
53
+ <ScCatalogixInvite
54
+ enabled={catalogixOn}
55
+ onToggle={setCatalogixOn}
56
+ appIcon={<SiconCatalogix size={24} />}
57
+ appDescription="Catalog enrichment and feeds"
58
+ stores={visibleStores} // already filtered by your search
59
+ selectedStores={selectedStoreIds}
60
+ onStoreToggle={toggleStore}
61
+ searchValue={storeSearch}
62
+ onSearchChange={setStoreSearch}
63
+ />
64
+ ```
65
+
66
+ ### Props
67
+
68
+ | Prop | Type | Default | Notes |
69
+ |---|---|---|---|
70
+ | `enabled` | `boolean` | – | The real on/off. Overrides `active` whenever defined. **Use this one.** Also the switch that reveals the store panel. |
71
+ | `active` | `"true"` \| `"false"` | `"false"` | ⚠️ Figma-variant leftover, a *string*. Consulted only when `enabled` is `undefined`. Defaults **off** — the opposite of `ScArtifaxInvite`/`ScPhtogenixInvite`. |
72
+ | `onToggle` | `(active: boolean) => void` | – | Fires with the **next** value. Omit it and the row can never be expanded. |
73
+ | `appName` | `string` | `"Catalogix"` | ⚠️ Real default. Passed to `ScAppCard`. |
74
+ | `appDescription` | `string` | `"Info about catalogix"` | ⚠️ Real default, and it is placeholder copy. |
75
+ | `appIcon` | `JSX.Element` | `<SiconArtifacts />` | ⚠️ The **wrong brand's** icon by default — pass `<SiconCatalogix />`. |
76
+ | `stores` | `{ id: string; name: string }[]` | – | ⚠️ Undefined ≠ empty. Undefined renders 5 placeholder rows; `[]` renders "No stores found". |
77
+ | `selectedStores` | `string[]` | – | Ids that render checked. Also **replaces** `selectedCount` with `` `${selectedStores.length} Selected` ``. |
78
+ | `onStoreToggle` | `(storeId: string) => void` | – | Fires on a store row click. You compute the next selection. |
79
+ | `searchValue` | `string` | – | Value of the search input. Controlled — see Gotcha 3. |
80
+ | `onSearchChange` | `(query: string) => void` | – | Fires on every keystroke. The component does **no** filtering. |
81
+ | `storeAccessTitle` | `string` | `"Store access"` | Panel heading. Truncates with ellipsis (`flex: 1`, `nowrap`). |
82
+ | `selectedCount` | `string` | `"0 Selected"` | Right-hand count. **Ignored whenever `selectedStores` is passed.** |
83
+ | `searchPlaceholder` | `string` | `"Search stores..."` | Placeholder of the search input. |
84
+ | `className` | `string` | – | Appended after internal classes. |
85
+ | `...props` | `Omit<HTMLAttributes<HTMLDivElement>, "onToggle">` | – | Spread onto the root `<div>`. |
86
+
87
+ ### What renders in each state
88
+
89
+ | Region | `enabled` false | `enabled` true |
90
+ |---|---|---|
91
+ | Header | `ScAppCard` + `ScToggleSwitch` | same |
92
+ | Store-access panel | **not rendered at all** | canvas-base panel, 16px padding, radius 16 |
93
+ | Panel header | — | `storeAccessTitle` + count (`selectedStores.length` if given, else `selectedCount`) |
94
+ | Search | — | `ScOnlyField`, search icon on the **right**, `searchPlaceholder` |
95
+ | List: `stores` non-empty | — | one `ScPairtext type="checkbox"` per store, name left / checkbox right |
96
+ | List: `stores === []` | — | hardcoded "No stores found" |
97
+ | List: `stores` undefined | — | ⚠️ five placeholder `ScPairtext` rows reading **"Pairtext"** |
98
+
99
+ `storeAccessTitle`, `selectedCount`, `searchPlaceholder`, `stores`,
100
+ `selectedStores`, `searchValue` and their handlers are all inert while the row is
101
+ off — the whole panel is behind `isEnabled &&`.
102
+
103
+ ### Recipes
104
+
105
+ ```tsx
106
+ // The full job: toggle + search + multi-select, with YOU doing the filtering
107
+ const [catalogixOn, setCatalogixOn] = useState(false);
108
+ const [storeSearch, setStoreSearch] = useState("");
109
+ const [selected, setSelected] = useState<string[]>([]);
110
+
111
+ const visibleStores = useMemo(
112
+ () =>
113
+ allStores
114
+ .map((s) => ({ id: s._id, name: s.name || s._id }))
115
+ .filter((s) => s.name.toLowerCase().includes(storeSearch.toLowerCase())),
116
+ [allStores, storeSearch],
117
+ );
118
+
119
+ <ScCatalogixInvite
120
+ enabled={catalogixOn}
121
+ onToggle={(next) => {
122
+ setCatalogixOn(next);
123
+ if (!next) setSelected([]); // clearing on disable is YOUR job
124
+ }}
125
+ appIcon={<SiconCatalogix size={24} />}
126
+ appDescription="Catalog enrichment and feeds"
127
+ stores={visibleStores}
128
+ selectedStores={selected}
129
+ onStoreToggle={(id) =>
130
+ setSelected((prev) =>
131
+ prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
132
+ )
133
+ }
134
+ searchValue={storeSearch}
135
+ onSearchChange={setStoreSearch}
136
+ />
137
+
138
+ // Cap the list height — the component never scrolls on its own
139
+ <ScCatalogixInvite
140
+ /* …as above… */
141
+ className={styles.invite}
142
+ />
143
+ ```
144
+
145
+ ```css
146
+ /* Target the DS class from your own stylesheet, or wrap and use a descendant
147
+ selector — the list container is the third child of the panel. */
148
+ .invite [class*="storeListContainer"] { max-height: 240px; overflow-y: auto; }
149
+ ```
150
+
151
+ ```tsx
152
+ // Loading state: pass [] (NOT undefined) so you get the empty state, not placeholders
153
+ <ScCatalogixInvite
154
+ enabled={catalogixOn}
155
+ onToggle={setCatalogixOn}
156
+ stores={isLoading ? [] : visibleStores}
157
+ selectedStores={selected}
158
+ onStoreToggle={toggleStore}
159
+ />
160
+ ```
161
+
162
+ ---
163
+
164
+ ## 2. Where to use it
165
+
166
+ - **The invite-user modal's "App access" section**, as the Catalogix row — normally
167
+ last, because expanding it makes the modal grow.
168
+ - **The edit-user / manage-access drawer**, pre-populated with the user's current
169
+ store scope.
170
+ - Any **app-access + resource-scope** pairing with the same shape (app on/off, then
171
+ "which of these N things").
172
+
173
+ It composes `ScAppCard`, `ScToggleSwitch`, `ScOnlyField` (search) and `ScPairtext`
174
+ (checkbox rows), with a two-tone nesting: the panel sits on
175
+ `--alias-surface-canvasbase` and the list on `--alias-fill-neutral-neutralplus`, so
176
+ it reads as inset in both themes.
177
+
178
+ ---
179
+
180
+ ## 3. When to use it
181
+
182
+ ### Use it when
183
+
184
+ - Granting the app is **not** enough — the user also needs a subset of stores.
185
+ - The scope list is short enough to show inline (or you cap the height yourself).
186
+ - You want the row to line up with the other app rows in the same modal.
187
+
188
+ ### Don't use it — reach for this instead
189
+
190
+ | Situation | Use instead |
191
+ |---|---|
192
+ | Access is a plain boolean, no sub-scope | `ScArtifaxInvite` |
193
+ | The Photogenix row | `ScPhtogenixInvite` (prefixed `scAppCardapp*` props) |
194
+ | A searchable multi-select that isn't attached to an app toggle | `ScOnlyField` + `ScPairtext`, or `ScCheckField` |
195
+ | A list of stores as a data table (owner, status, actions) | `ScCatalogixStoreTableList` |
196
+ | Read-only "which apps does this user have" | `ScAccess` |
197
+ | App icon + name + description, no toggle | `ScAppCard` |
198
+ | A launcher/marketing card for the app | `ScAppListingCard` / `ScAppCardV3` |
199
+ | Checkboxes with real form semantics (labels, keyboard) | `ScCheckField` / `ScCheckbox` |
200
+ | Hundreds of options needing virtualisation | build your own list — this one renders every row |
201
+
202
+ ### Don't confuse with
203
+
204
+ | You may actually want | Not this |
205
+ |---|---|
206
+ | `ScArtifaxInvite` — header-only row, defaults **ON**, draws a bottom divider | `ScCatalogixInvite` adds the store panel, defaults **OFF**, draws **no** divider |
207
+ | `ScPhtogenixInvite` — header-only row with prefixed props and no DOM prop forwarding | This one uses clean `appName`/`appDescription`/`appIcon` and spreads `...props` |
208
+ | `ScCatalogixStoreTableList` — the store **table** on Catalogix's own screens | This is a compact checkbox picker inside an invite modal |
209
+ | `ScCheckField` — a labelled group of real checkboxes | The rows here are `ScPairtext` divs: no `<input>`, no keyboard |
210
+ | `ScAccess` — three app icons, read-only | Not an editable control |
211
+
212
+ All three invite components are app-agnostic apart from their default copy and
213
+ icon. Choose by **shape**: no sub-scope → `ScArtifaxInvite`; sub-scope →
214
+ `ScCatalogixInvite`.
215
+
216
+ ---
217
+
218
+ ## 4. Why to use it
219
+
220
+ - **The disclosure logic is already right.** Toggle on → panel appears; toggle off →
221
+ the entire panel (and its search state's visual footprint) is gone, in one prop.
222
+ Hand-rolled versions leak a half-expanded panel or keep a stale search box mounted.
223
+ - **Two-tone inset nesting that survives light mode.** Panel on
224
+ `surface-canvasbase`, list on `fill-neutral-neutralplus` — two distinct tokens
225
+ rather than a grey that collapses to white on a light canvas.
226
+ - **Consistent count/heading typography.** Title is `text-sm-regular` primary,
227
+ count is `text-xs-regular` muted, both ellipsised — the exact spec, and the
228
+ detail most often missed when this row is rebuilt by hand.
229
+ - **It matches its sibling rows for free**: same 16px padding, same `ScAppCard`
230
+ flex, same `ScToggleSwitch`.
231
+ - **One place to change.** `@streamoid/settings` currently reimplements this whole
232
+ panel — local toggle, local search input, local store rows, the same
233
+ "Search stores..." and "No stores found" strings — in a single ~980-line modal.
234
+
235
+ ---
236
+
237
+ ## Gotchas
238
+
239
+ **1. `stores` undefined renders five dummy "Pairtext" rows.** The Figma-placeholder
240
+ branch is still in the component, and it renders unchecked checkbox rows whose
241
+ label is `ScPairtext`'s own default (`"Pairtext"`), wired to nothing.
242
+
243
+ ```tsx
244
+ // WRONG — five rows reading "Pairtext" appear the moment the toggle goes on
245
+ <ScCatalogixInvite enabled onToggle={setOn} selectedStores={selected} />
246
+
247
+ // RIGHT — pass an array, even while loading
248
+ <ScCatalogixInvite enabled onToggle={setOn} stores={stores ?? []} selectedStores={selected} />
249
+ ```
250
+
251
+ **2. It does not filter, sort, or paginate.** `onSearchChange` just reports
252
+ keystrokes; the list renders exactly the `stores` array you pass. Filter upstream.
253
+
254
+ ```tsx
255
+ // WRONG — typing changes nothing on screen
256
+ <ScCatalogixInvite stores={allStores} searchValue={q} onSearchChange={setQ} … />
257
+
258
+ // RIGHT
259
+ <ScCatalogixInvite
260
+ stores={allStores.filter((s) => s.name.toLowerCase().includes(q.toLowerCase()))}
261
+ searchValue={q}
262
+ onSearchChange={setQ}
263
+
264
+ />
265
+ ```
266
+
267
+ **3. `searchValue` without `onSearchChange` is a read-only input.** It's forwarded
268
+ straight to a controlled `<input value>`; with no change handler, keystrokes are
269
+ discarded. Pass both, or neither.
270
+
271
+ **4. The store list never scrolls.** `.storeListContainer` sets no `max-height` and
272
+ no `overflow`, so the panel grows with the data and pushes your modal footer off
273
+ screen. Constrain it from your side (see the Recipes).
274
+
275
+ **5. It defaults to OFF, and its siblings default to ON.** `active = "false"` here
276
+ vs `"true"` in `ScArtifaxInvite`/`ScPhtogenixInvite`. Always pass `enabled` and
277
+ stop thinking about `active`.
278
+
279
+ **6. `selectedCount` is dead whenever `selectedStores` is present.** The component
280
+ computes `` `${selectedStores.length} Selected` ``. The `" Selected"` suffix is
281
+ hardcoded English; the only way to change the wording is to omit `selectedStores`
282
+ and drive `selectedCount` yourself — which also loses the checked state.
283
+
284
+ **7. The default `appIcon` is the Artifax icon.** `SiconArtifacts` is the fallback
285
+ in all three invite components. Pass `<SiconCatalogix />`.
286
+
287
+ **8. `"No stores found"` is not overridable,** and neither is the placeholder
288
+ branch. Only `storeAccessTitle`, `selectedCount` and `searchPlaceholder` are props.
289
+ Don't use this row in a localised surface without changing the DS first.
290
+
291
+ **9. The search icon is on the right, and the left one is discarded.** The
292
+ component passes both `tfLeftIcon` and `tfRightIcon` to `ScOnlyField` but never
293
+ sets `tfGroup`, which defaults to `"icon-right"` — so only the right icon renders.
294
+ You cannot move it or replace it.
295
+
296
+ **10. Store rows are not accessible.** `ScPairtext` renders a `<div onClick>` with
297
+ `cursor: pointer` and an `ScCheckbox` **visual** — no `<input>`, no `role`, no
298
+ `tabIndex`, no label association. Keyboard users cannot select a store. Use
299
+ `ScCheckField` if that matters.
300
+
301
+ **11. No bottom divider.** Unlike the other two invite rows, `.scCatalogixInvite`
302
+ has no border. Stack it after them and the separator sequence breaks — add your own
303
+ `ScHDivider` or a border via `className`.
304
+
305
+ **12. Turning the row off does not clear the selection.** `selectedStores` is your
306
+ state; the panel simply unmounts. Reset it in `onToggle` if a disabled app should
307
+ drop its scope.
308
+
309
+ **13. `styles["active-false"]` doesn't exist.** Only `.active-true` is defined
310
+ (it's what styles the whole panel), so in the OFF state the variant class resolves
311
+ to `undefined` and lands in the class attribute as the literal `"undefined"`, as
312
+ does an omitted `className`. Don't assert on the class list.
313
+
314
+ ---
315
+
316
+ ## In the wild
317
+
318
+ _No host render site found — used by the agent runtime / composed internally._
319
+
320
+ The surface is real but hand-rolled in the shared settings package, which
321
+ reimplements this component end to end:
322
+
323
+ ```tsx
324
+ // @streamoid/settings packages/settings/src/invite-update-modal.tsx:700
325
+ {/* Catalogix row + store access */}
326
+ // …~200 lines duplicating this component: local ToggleSwitch (line 84),
327
+ // a hand-written search input with placeholder "Search stores..." (line 838),
328
+ // filteredStores mapped to hand-rolled checkbox rows (line 867),
329
+ // and the same "No stores found" empty state (line 901).
330
+ ```
331
+
332
+ That modal — CXO settings → Teams → invite/edit user — is exactly where this
333
+ component belongs; adopting it would delete the duplication.
334
+
335
+ ---
336
+
337
+ ## Related
338
+
339
+ - `ScArtifaxInvite` — the plain header-only row; the cleanest API of the trio.
340
+ - `ScPhtogenixInvite` — the Photogenix twin; prefixed `scAppCardapp*` props.
341
+ - `ScAppCard` — the icon/name/description block in the header.
342
+ - `ScToggleSwitch` — the switch, standalone.
343
+ - `ScOnlyField` — the search field it renders; note `tfGroup` controls icon sides.
344
+ - `ScPairtext` — the store rows; `type="checkbox"`, `iconPosition="right"`.
345
+ - `ScCheckField` — reach for this when the multi-select needs real form semantics.
@@ -0,0 +1,337 @@
1
+ ---
2
+ component: ScCatalogixSidebar
3
+ package: "@streamoid/ui"
4
+ category: sidebar
5
+ status: legacy
6
+ renders: div
7
+ tags: [sidebar, catalogix, nav, shell, rail, wrapper, stores, ai-training, insights]
8
+ related: [StreamoidSidebar, ScSideBarLogoUnit, ScSidebarSwitchMenu, ScAppSwitchPanel, CreditWarningBanner]
9
+ do_not_confuse_with: [StreamoidSidebar, ScArtifaxSidebar, ScCatalogixInvite, ScCatalogixStoreHeader]
10
+ required_props: [expanded, onToggle]
11
+ ---
12
+
13
+ # ScCatalogixSidebar
14
+
15
+ **A Catalogix-flavoured preset of `StreamoidSidebar`.** It hardcodes the Catalogix
16
+ wordmark logo unit, a default Stores / AI Training / Insights nav, an inline
17
+ app-switch list built from `ScSidebarSwitchMenu`, and the credit banner — so a caller
18
+ only supplies `expanded` + callbacks.
19
+
20
+ > **`status: legacy`.** Catalogix's own shell no longer uses it: as of the
21
+ > 2026-07 redesign, `catalogix/dashboard app/containers/LeftMenu/index.jsx:991`
22
+ > composes **`StreamoidSidebar` directly** — it needs `assistantCta` (the "Ask CXO"
23
+ > gradient CTA), `activeItemId`-driven nav and `ScAppSwitchPanel`, none of which this
24
+ > wrapper can express. **For new work, use `StreamoidSidebar`.**
25
+
26
+ ## TL;DR for agents
27
+
28
+ - **Reach for it when:** essentially never in new code. It is useful mainly as a
29
+ worked example of how to preset `StreamoidSidebar`.
30
+ - **Don't reach for it when:** you need the assistant CTA, the shared
31
+ `ScAppSwitchPanel` rows, the collapsed right-side switch flyout, `topItems`,
32
+ section labels, or a version footer — all unreachable through this wrapper
33
+ (→ `StreamoidSidebar`).
34
+ - **Three things that will bite you:**
35
+ 1. **There is no `assistantCta` prop.** You cannot render the "Ask CXO" gradient
36
+ CTA through this component, despite the component catalog implying otherwise.
37
+ 2. `switchApps` rows are `ScSidebarSwitchMenu`, **not** `ScAppSwitchPanel` — a
38
+ different visual language from every other app's switcher.
39
+ 3. `switchPanel` is only built when `expanded`, so the collapsed rail gets an
40
+ **inline icon list** instead of `StreamoidSidebar`'s right-side flyout, and no
41
+ backdrop.
42
+
43
+ ---
44
+
45
+ ## 1. How to use it
46
+
47
+ ### Import
48
+
49
+ ```tsx
50
+ import {
51
+ ScCatalogixSidebar,
52
+ type CatalogixNavItem,
53
+ type CatalogixSwitchApp,
54
+ } from "@streamoid/ui";
55
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
56
+ ```
57
+
58
+ ### Minimal usage
59
+
60
+ Everything except the two state props has a default:
61
+
62
+ ```tsx
63
+ <ScCatalogixSidebar
64
+ expanded={expanded}
65
+ onToggle={() => setExpanded((v) => !v)}
66
+ activeItemId="stores"
67
+ onItemSelect={(item) => navigate(`/${item.id}`)}
68
+ />
69
+ ```
70
+
71
+ ### Props
72
+
73
+ | Prop | Type | Default | Notes |
74
+ |---|---|---|---|
75
+ | `expanded` | `boolean` | — | **Required.** Forwarded to `StreamoidSidebar`. |
76
+ | `onToggle` | `() => void` | — | **Required.** Also the fallback for the collapsed credit-banner click. |
77
+ | `items` | `CatalogixNavItem[]` | ⚠️ `[Stores, AI Training, Insights]` | Real default: ids `stores` / `ai-training` / `insights`. |
78
+ | `iconMap` | `CatalogixSidebarIconMap` | ⚠️ `SiconBag` / `SiconBrain` / `SiconInsights` | Real default, keyed `stores` / `aiTraining` / `insights`. Replacing `items` without replacing `iconMap` leaves rows **icon-less**. |
79
+ | `activeItemId` | `string` | – | Matching row → `state="active"`; `active` is passed to the icon renderer. |
80
+ | `onItemSelect` | `(item: CatalogixNavItem) => void` | – | No `sectionId` argument (unlike `StreamoidSidebar`). |
81
+ | `switchApps` | `CatalogixSwitchApp[]` | – | `{ id, name, description?, icon?, onClick? }[]`. Omit to disable app switching. |
82
+ | `onLogoClick` | `() => void` | – | Fires **in addition to** the internal open/close of the app list. |
83
+ | `expandedLogo` | `ReactNode` | ⚠️ built-in `ScSideBarLogoUnit product="Catalogix"` | Overriding it **breaks the switcher** — the default node is what carries `onClick={handleSwitch}`. |
84
+ | `collapsedLogo` | `ReactNode` | ⚠️ built-in collapsed Catalogix mark | Same caveat, plus you lose the `hover` highlight while the list is open. |
85
+ | `profile` | `CatalogixSidebarProfile` | – | `{ name, subtitle?, avatar?, onClick? }`. **Required in practice** — see Gotcha 4. |
86
+ | `creditWarning` | `CatalogixSidebarCreditWarning \| null` | – | `{ availableCredits, remainingPct, level?, onBuyCredits, onCollapsedClick? }`. `level` defaults to `"danger"`; `onCollapsedClick` defaults to `onToggle`. |
87
+ | `toggleIcon` | `ReactNode \| ((expanded: boolean) => ReactNode)` | `SiconCollapse` / `SiconExpand`, muted, `size-6` | Real default, and it correctly swaps glyphs per state. |
88
+ | `className` | `string` | – | Forwarded to `StreamoidSidebar`'s outer wrapper. |
89
+ | `style` | `CSSProperties` | – | Forwarded likewise. |
90
+
91
+ ### Types
92
+
93
+ ```ts
94
+ interface CatalogixNavItem { id: string; label: string; iconKey: string }
95
+ interface CatalogixSwitchApp { id: string; name: string; description?: string; icon?: ReactNode; onClick?: () => void }
96
+ interface CatalogixSidebarProfile { name: string; subtitle?: string; avatar?: ReactNode; onClick?: () => void }
97
+ interface CatalogixSidebarCreditWarning {
98
+ availableCredits: number; remainingPct: number;
99
+ level?: "warning" | "danger"; // default "danger"
100
+ onBuyCredits: () => void;
101
+ onCollapsedClick?: () => void; // defaults to onToggle
102
+ }
103
+ type CatalogixSidebarIconRenderer = (ctx: { active: boolean; expanded: boolean }) => ReactNode;
104
+ ```
105
+
106
+ ### What it hardcodes on `StreamoidSidebar`
107
+
108
+ Anything in this list is **not reachable** through this wrapper:
109
+
110
+ | `StreamoidSidebar` prop | Fixed value here |
111
+ |---|---|
112
+ | `config` | `{ sections: [] }` — no `topItems`, no `bottomItems`, no section labels |
113
+ | `iconMap` | `{}` |
114
+ | `showBody` / `bodyContent` | always on / the internal nav + collapsed switch list |
115
+ | `switchPanel` | the `ScSidebarSwitchMenu` rows, **and only when `expanded`** |
116
+ | `switchPanelOpen` / `onSwitchPanelClose` | internal `appListOpen` state |
117
+ | `toggleInProfile` | `true` (so `profile` is mandatory in practice) |
118
+ | `versionText` | `""` |
119
+ | `preFooterContent` | the `CreditWarningBanner` |
120
+ | `assistantCta` | **never passed — no way to set it** |
121
+ | `isMobile`, `hideFooter`, `topItems`, `highlightedItemIds`, `moreIcon`, `onMoreClick` | never passed |
122
+
123
+ ### Recipes
124
+
125
+ ```tsx
126
+ // The full intended wiring
127
+ <ScCatalogixSidebar
128
+ expanded={expanded}
129
+ onToggle={() => setExpanded((v) => !v)}
130
+ activeItemId={currentSection}
131
+ onItemSelect={(item) => go(item.id)}
132
+ switchApps={otherProducts.map((p) => ({
133
+ id: p.id,
134
+ name: p.name,
135
+ description: p.desc,
136
+ icon: <ProductIcon product={p.id} />,
137
+ onClick: () => open(p.url),
138
+ }))}
139
+ onLogoClick={loadServiceCatalog} // lazy-load the product list
140
+ profile={{ name, subtitle: workspace, avatar: <ScDp type="initial" initial={initial} size={32} />, onClick: openProfilePopup }}
141
+ creditWarning={low ? { availableCredits: credits, remainingPct: Math.round(pct), onBuyCredits: goBilling } : null}
142
+ />
143
+
144
+ // Custom nav — replace items AND iconMap together
145
+ <ScCatalogixSidebar
146
+ expanded={expanded}
147
+ onToggle={toggle}
148
+ items={[{ id: "feeds", label: "Feeds", iconKey: "feeds" }]}
149
+ iconMap={{
150
+ feeds: ({ active }) => (
151
+ <SiconUpload color={active ? "var(--alias-text---icons-primary)" : "var(--alias-text---icons-tertiary)"} />
152
+ ),
153
+ }}
154
+ />
155
+
156
+ // What to write instead for anything non-trivial (this is what Catalogix ships):
157
+ <StreamoidSidebar
158
+ expanded={expanded}
159
+ onToggle={toggle}
160
+ config={navConfig}
161
+ iconMap={iconMap}
162
+ activeItemId={currentSection}
163
+ showBody={!insideStore}
164
+ bodyContent={workspaceServiceBody}
165
+ switchPanel={apps.length ? <ScAppSwitchPanel apps={apps} /> : undefined}
166
+ switchPanelOpen={appListOpen}
167
+ onSwitchPanelClose={() => setAppListOpen(false)}
168
+ assistantCta={{ label: "Ask CXO", icon: <SiconBolt size={20} color="currentColor" />, active: askOpen, onClick: toggleAsk }}
169
+ expandedLogo={<ScSideBarLogoUnit wordmark={<ProductWordmark product="Catalogix" />} onClick={() => setAppListOpen((v) => !v)} />}
170
+ collapsedLogo={<ScSideBarLogoUnit state="collapsed" wordmark={<ProductCollapsedMark product="Catalogix" />} onClick={() => setAppListOpen((v) => !v)} />}
171
+ profile={profile}
172
+ toggleInProfile
173
+ versionText=""
174
+ toggleIcon={(exp) => <SiconCollapse size={20} style={exp ? undefined : { transform: "rotate(180deg)" }} />}
175
+ />
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 2. Where to use it
181
+
182
+ - **Historically:** the Catalogix dashboard shell. **Today: nowhere** — see the
183
+ status note above.
184
+ - It composes `StreamoidSidebar`, `ScSidebarMenu`, `ScSidebarSwitchMenu`,
185
+ `ScHDivider`, `ScSideBarLogoUnit` (with the built-in `Catalogix` product mark) and
186
+ `CreditWarningBanner`.
187
+ - It is **presentational**: it renders chrome and fires callbacks. It fetches
188
+ nothing, holds no auth/workspace/theme state, and renders no popups — profile
189
+ menus, workspace modals and routing stay in the host.
190
+
191
+ ---
192
+
193
+ ## 3. When to use it
194
+
195
+ ### Use it when
196
+
197
+ - You are maintaining an existing call site (there are none in the four host repos).
198
+ - You want a compact reference for how to preset `StreamoidSidebar` for one product.
199
+
200
+ ### Don't use it — reach for this instead
201
+
202
+ | Situation | Use instead |
203
+ |---|---|
204
+ | Any new Catalogix sidebar work | `StreamoidSidebar` (what Catalogix actually ships) |
205
+ | You need the "Ask CXO" gradient CTA | `StreamoidSidebar` + `assistantCta` (or `ScAskAgentSlot`) |
206
+ | You want the shared cross-product switcher rows | `ScAppSwitchPanel` inside `StreamoidSidebar`'s `switchPanel` |
207
+ | You want the collapsed switch **flyout** + backdrop | `StreamoidSidebar` (pass `switchPanel` in both rails) |
208
+ | Section labels, top items, bottom items, a version footer | `StreamoidSidebar` |
209
+ | Artifax's shell / collapsible sections | `ScArtifaxSidebar` |
210
+ | One nav row / one switch row | `ScSidebarMenu` / `ScSidebarSwitchMenu` |
211
+
212
+ ### Don't confuse with
213
+
214
+ | You may actually want | Not this |
215
+ |---|---|
216
+ | `StreamoidSidebar` — the component this wraps, and the one Catalogix ships | This wrapper hides two thirds of its props |
217
+ | `ScArtifaxSidebar` — a **standalone** sibling shell, not a wrapper | Different architecture, different prop names |
218
+ | `ScCatalogixInvite` — the Catalogix invite/onboarding card | Not navigation |
219
+ | `ScCatalogixStoreHeader` / `ScCatalogixStoreTableList` — the stores table | Not navigation |
220
+
221
+ ---
222
+
223
+ ## 4. Why to use it
224
+
225
+ The honest answer: **for the defaults, and nothing else.** What it genuinely gives you:
226
+
227
+ - **The Catalogix brand marks without importing them** — `product="Catalogix"` resolves
228
+ both the expanded wordmark and (unusually) a real collapsed square mark.
229
+ - **A correct collapse/expand glyph pair** — `SiconCollapse` when expanded,
230
+ `SiconExpand` when collapsed, rather than one rotated icon.
231
+ - **A worked example of the `StreamoidSidebar` slot contract** — `showBody` +
232
+ `bodyContent`, `switchPanel` + `switchPanelOpen` + `onSwitchPanelClose`,
233
+ `preFooterContent`, `toggleInProfile`, `versionText=""`. Copy the pattern, not
234
+ the component.
235
+
236
+ Everything else you get from `StreamoidSidebar` itself, with more control.
237
+
238
+ ---
239
+
240
+ ## Gotchas
241
+
242
+ **1. No `assistantCta`.** The component catalog describes this wrapper as having an
243
+ "Ask CXO"-style CTA. It does not: `assistantCta` is never forwarded to
244
+ `StreamoidSidebar`, and there is no prop for it. This alone is why Catalogix stopped
245
+ using the wrapper.
246
+
247
+ **2. Overriding `expandedLogo` / `collapsedLogo` disables the app switcher.** The
248
+ default nodes are the only place `handleSwitch` (which flips the internal
249
+ `appListOpen`) is wired.
250
+
251
+ ```tsx
252
+ // WRONG — switchApps become unreachable
253
+ <ScCatalogixSidebar expandedLogo={<MyLogo />} switchApps={apps} … />
254
+
255
+ // RIGHT — keep the default logo; use onLogoClick for side effects
256
+ <ScCatalogixSidebar switchApps={apps} onLogoClick={loadCatalog} … />
257
+ ```
258
+
259
+ **3. Collapsed switching behaves differently from every other app.**
260
+ `switchPanelContent` is `undefined` unless `expanded`, so `StreamoidSidebar`'s
261
+ `hasSwitchCard` is false in the collapsed rail: no card, no 240px flyout, no
262
+ backdrop, no `onSwitchPanelClose`. You get an inline column of
263
+ `ScSidebarSwitchMenu variant="collapsed"` icons in the body instead.
264
+
265
+ **4. `profile` is effectively required.** The wrapper always passes
266
+ `toggleInProfile`, and `StreamoidSidebar` renders that footer branch only inside
267
+ `profile ? … : null`. **Omit `profile` and there is no collapse toggle at all.**
268
+
269
+ **5. Replacing `items` without `iconMap` yields icon-less rows.** Both props have
270
+ defaults, and they default *independently*; the default `iconMap` only knows
271
+ `stores` / `aiTraining` / `insights`. A missing key resolves to `null`, silently.
272
+
273
+ **6. Switch rows are hardcoded to `state="default-highlight"`** in the expanded list
274
+ and `state="default"` when collapsed — you cannot mark the current product, and the
275
+ `description` you pass only renders in the expanded rail.
276
+
277
+ **7. `onLogoClick` does not intercept the toggle.** It fires *in addition to*
278
+ flipping the internal list state, on every click, in both directions — so it's a
279
+ lazy-load hook, not an override.
280
+
281
+ **8. It inherits every `StreamoidSidebar` gotcha.** Notably: layout depends on
282
+ Tailwind utility classes that `dist/index.css` does **not** ship; the profile row and
283
+ toggle are `div`s with `onClick` (no keyboard access); widths are hardcoded 256/56px;
284
+ the body scrolls, so popovers must be portalled to `document.body`.
285
+
286
+ **9. It inherits `CreditWarningBanner`'s self-dismissal.** The ✕ sets internal state
287
+ and the banner returns `null` forever, with no callback — remount with a new `key` to
288
+ bring it back. `remainingPct` is printed raw, so round it yourself.
289
+
290
+ ---
291
+
292
+ ## In the wild
293
+
294
+ _No host render site found — Catalogix's shell composes `StreamoidSidebar` directly._
295
+
296
+ The nearest thing to a call site is the code that **replaced** it:
297
+
298
+ ```jsx
299
+ // catalogix/dashboard app/containers/LeftMenu/index.jsx:991
300
+ <StreamoidSidebar
301
+ expanded={!collapsed}
302
+ onToggle={handleToggleCollapse}
303
+ config={navConfig}
304
+ iconMap={iconMap}
305
+ activeItemId={currentSection}
306
+ onItemSelect={handleItemSelect}
307
+ showBody={!insideStore}
308
+ bodyContent={workspaceServiceBody}
309
+ switchPanel={appSwitchItems.length ? <ScAppSwitchPanel apps={appSwitchItems} /> : undefined}
310
+ assistantCta={{ label: "Ask CXO", icon: <SiconBolt size={20} color="currentColor" />, active: askCxoOpen, onClick: toggleAskCxo }}
311
+ switchPanelOpen={appListOpen}
312
+ onSwitchPanelClose={() => setAppListOpen(false)}
313
+ expandedLogo={
314
+ <ScSideBarLogoUnit
315
+ state="expanded"
316
+ wordmark={<ProductWordmark product="Catalogix" />}
317
+ onClick={() => setAppListOpen((v) => !v)}
318
+ switchAriaLabel="Switch app"
319
+ />
320
+ }
321
+ /* … profile, toggleInProfile, versionText="" … */
322
+ />
323
+ ```
324
+
325
+ If this wrapper is kept, it belongs behind the same wiring: add an `assistantCta`
326
+ pass-through and pass `switchPanel` in both rails.
327
+
328
+ ---
329
+
330
+ ## Related
331
+
332
+ - `StreamoidSidebar` — the component this presets, and the replacement to use.
333
+ - `ScSideBarLogoUnit` + `ProductWordmark` / `ProductCollapsedMark` — the Catalogix marks it renders by default.
334
+ - `ScAppSwitchPanel` — the shared switcher rows Catalogix now uses instead of `ScSidebarSwitchMenu`.
335
+ - `ScSidebarSwitchMenu` — the row type this wrapper builds its switch list from.
336
+ - `CreditWarningBanner` — what `creditWarning` renders.
337
+ - `ScArtifaxSidebar` — the other app-branded shell (standalone, not a wrapper).