@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,238 @@
1
+ ---
2
+ component: ScPendingAction
3
+ package: "@streamoid/ui"
4
+ category: actions
5
+ status: stable
6
+ renders: div
7
+ tags: [pending, invite, cancel, resend, teams, members, action-pair, divider]
8
+ related: [ScButton, ScVDivider, ScTableList, ScRole, ScArtifaxInvite]
9
+ do_not_confuse_with: [ScButton, ScMobileBottomAction, ScSelectionList, ScMenuOptions]
10
+ used_by: [cxo]
11
+ ---
12
+
13
+ # ScPendingAction
14
+
15
+ **A fixed 160px "Cancel | Resend" button pair.** Two hardcoded `ScButton`s — `Cancel`
16
+ in `variant="error" type="tertiary"` and `Resend` in `variant="tertiary"`, both
17
+ `size="sm"` — separated by a `ScVDivider`. It is the trailing cell of a pending-invite
18
+ row in the CXO Teams table.
19
+
20
+ ## TL;DR for agents
21
+
22
+ - **Reach for it when:** you are rendering a **pending invite** row and want the exact
23
+ Cancel/Resend pair used in CXO Teams.
24
+ - **Don't reach for it when:** you need *any* other labels, *any* callbacks per button,
25
+ or a general action pair — compose two `ScButton`s with a `ScVDivider` yourself.
26
+ - **Three things that will bite you:**
27
+ 1. It takes **no callbacks and no labels**. `className` plus `HTMLAttributes` is the
28
+ entire API. The only way to know which button was clicked is to inspect
29
+ `event.target` on a root-level `onClick`.
30
+ 2. Because of (1), it is **keyboard-inaccessible**: the inner `ScButton`s call their
31
+ own (undefined) `onClick` on Enter/Space, and no click event bubbles to your root
32
+ handler.
33
+ 3. Fixed `width: 10rem` (160px). It will not adapt to its cell.
34
+
35
+ > **Category note:** `docs/COMPONENT-CATALOG.md` lists this under the Chat/InChat
36
+ > family. That is wrong — it has nothing to do with the agent runtime. Its one real
37
+ > call site is the CXO Teams pending-invite row, so it is documented here as an
38
+ > **actions** component.
39
+
40
+ ---
41
+
42
+ ## 1. How to use it
43
+
44
+ ### Import
45
+
46
+ ```tsx
47
+ import { ScPendingAction } from "@streamoid/ui";
48
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
49
+ ```
50
+
51
+ ### Minimal usage
52
+
53
+ ```tsx
54
+ <ScPendingAction />
55
+ ```
56
+
57
+ That already renders "Cancel" and "Resend". Wiring them is the hard part — see the
58
+ recipe.
59
+
60
+ ### Props
61
+
62
+ | Prop | Type | Default | Notes |
63
+ |---|---|---|---|
64
+ | `className` | `string` | – | **Always concatenated**, so omitting it puts a literal `undefined` class on the root. |
65
+ | `...props` | `HTMLAttributes<HTMLDivElement>` | – | `onClick`, `style`, `data-*`, `aria-*` on the root. **This is the only wiring point.** |
66
+
67
+ There is no `onCancel`, no `onResend`, no `cancelText`, no `resendText`, no `disabled`,
68
+ no `loading`. Everything below is baked into the render body:
69
+
70
+ | Rendered child | Fixed configuration |
71
+ |---|---|
72
+ | Button 1 | `text="Cancel"`, `variant="error"`, `type="tertiary"`, `size="sm"` |
73
+ | Divider | `ScVDivider` |
74
+ | Button 2 | `text="Resend"`, `variant="tertiary"`, `size="sm"` |
75
+
76
+ Both buttons are also passed `icon={<SiconHome />}` — but `styleVariant` is left at its
77
+ default `"default"`, and `ScButton` only renders `icon` for `icon-left`/`icon-right`/
78
+ `icon-only`. **The icons are dead code and never appear.**
79
+
80
+ ### Recipes
81
+
82
+ ```tsx
83
+ // The only way to route the two actions: one root onClick + child hit-testing.
84
+ // This is what CXO does. Child 0 is Cancel, the last child is Resend.
85
+ <ScPendingAction
86
+ className="shrink-0"
87
+ onClick={(event) => {
88
+ event.stopPropagation(); // the row itself is clickable
89
+ const target = event.target as HTMLElement | null;
90
+ if (!target) return;
91
+ const root = event.currentTarget as HTMLElement;
92
+ const cancelAction = root.children.item(0) as HTMLElement | null;
93
+ const resendAction = root.children.item(root.children.length - 1) as HTMLElement | null;
94
+ if (cancelAction?.contains(target)) return onCancelInvite?.();
95
+ if (resendAction?.contains(target)) return onResendInvite?.();
96
+ }}
97
+ />
98
+
99
+ // PREFER THIS for anything that isn't literally the CXO Teams row:
100
+ // compose the pair yourself and get real callbacks, labels and keyboard support.
101
+ <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
102
+ <ScButton text="Cancel" variant="error" type="tertiary" size="sm" onClick={onCancel} />
103
+ <ScVDivider />
104
+ <ScButton text="Resend" variant="tertiary" size="sm" onClick={onResend} />
105
+ </div>
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 2. Where to use it
111
+
112
+ - **The CXO Teams / members table**, in the "signed on" column of a row whose invite is
113
+ still pending: the date is replaced by this pair. See
114
+ `cxo-dashboard src/app/components/teams-content.tsx`.
115
+ - Nowhere else currently. The three invite components (`ScArtifaxInvite`,
116
+ `ScCatalogixInvite`, `ScPhtogenixInvite`) are the surrounding invite vocabulary, and
117
+ `ScRole` / `ScTableList` are the row primitives it sits beside.
118
+
119
+ ---
120
+
121
+ ## 3. When to use it
122
+
123
+ ### Use it when
124
+
125
+ - You are rebuilding **the CXO pending-invite row** and want pixel parity with it,
126
+ including the 160px width that lines the column up with the date text it replaces.
127
+
128
+ ### Don't use it — reach for this instead
129
+
130
+ | Situation | Use instead |
131
+ |---|---|
132
+ | Any other label pair ("Approve/Deny", "Retry/Dismiss") | two `ScButton`s + `ScVDivider` |
133
+ | You need per-button callbacks, disabled or loading states | two `ScButton`s — this component exposes none |
134
+ | A single action | `ScButton`, or `ScOnlyIcon` for a bare icon |
135
+ | A confirm/cancel pair in a modal or drawer footer | two `ScButton`s (`variant="mono"` / `variant="outline"`) |
136
+ | A mobile sticky footer with 1–2 actions | `ScMobileBottomAction` |
137
+ | A menu of row actions behind a `⋯` | `ScPopUpMenu` + `ScMenuOptions` |
138
+ | Accepting/declining an agent suggestion in chat | `ScSelectionList` |
139
+
140
+ ### Don't confuse with
141
+
142
+ | You may actually want | Not this |
143
+ |---|---|
144
+ | `ScButton` — the actual primitive, with `onClick`, labels and states | This is a frozen pair of two of them |
145
+ | `ScMobileBottomAction` — the mobile sticky action bar | Different surface, real props |
146
+ | `ScSelectionList` — approve/reject **toggles** in the chat transcript | Different family entirely; it *does* have callbacks |
147
+ | Something in the chat/agent family, from the name and the catalog listing | It's a Teams-table cell |
148
+
149
+ ---
150
+
151
+ ## 4. Why to use it
152
+
153
+ - **Exactly one reason: parity with the CXO Teams row.** The 160px width, the 4px gap,
154
+ the `error/tertiary` + `tertiary` emphasis pairing and the divider between them are a
155
+ Figma spec, and this component is that spec frozen.
156
+ - Everything else you would want from it — labels, callbacks, disabled, keyboard — is
157
+ better served by composing `ScButton` + `ScVDivider` directly, which costs three lines
158
+ and gives you a correct component.
159
+
160
+ If you are choosing between this and hand-composing, **hand-compose** unless you are
161
+ editing the CXO Teams table.
162
+
163
+ ---
164
+
165
+ ## Gotchas
166
+
167
+ **1. There are no callbacks.** Not "they're optional" — they don't exist. The sanctioned
168
+ workaround is a root `onClick` plus DOM hit-testing against `root.children` (see the
169
+ recipe). That couples your call site to the component's child order: if the DS ever adds
170
+ a wrapper or reorders the buttons, your handlers silently swap or stop firing.
171
+
172
+ **2. It is keyboard-inaccessible.** Each `ScButton` is a `div[role="button"]
173
+ tabIndex={0}` whose Enter/Space handler invokes **its own** `props.onClick` — which is
174
+ `undefined` here — and does not dispatch a DOM click. So focusing "Cancel" and pressing
175
+ Enter does nothing, and your root `onClick` never runs. Keyboard and screen-reader users
176
+ cannot cancel or resend an invite through this component. Treat that as a blocking bug
177
+ for any accessible surface.
178
+
179
+ **3. Fixed `width: 10rem`.** The root is 160px regardless of content or container. In a
180
+ narrower cell it overflows; in a wider one the two buttons stretch (each is
181
+ `flex: 1 !important`) rather than hugging their labels.
182
+
183
+ **4. The labels are hardcoded English.** "Cancel" and "Resend" cannot be changed. Not
184
+ localisable.
185
+
186
+ **5. The `SiconHome` icons never render.** Both buttons get `icon={<SiconHome/>}` but
187
+ `styleVariant` stays `"default"`, and `ScButton` only draws `icon` for the `icon-*`
188
+ variants. Harmless, but don't read the source and assume there are icons.
189
+
190
+ **6. No pending/disabled state.** While a resend request is in flight you cannot dim or
191
+ block the pair; the user can fire it repeatedly. Guard in your handler.
192
+
193
+ **7. Omitting `className` puts a literal `undefined` class on the root** — the class list
194
+ is plain string concatenation. Cosmetic, but visible in snapshot tests.
195
+
196
+ **8. `event.stopPropagation()` is usually required.** The Teams row itself is clickable,
197
+ so without it a Cancel click also opens the member drawer.
198
+
199
+ ---
200
+
201
+ ## In the wild
202
+
203
+ ```tsx
204
+ // cxo-dashboard src/app/components/teams-content.tsx:644
205
+ <ScPendingAction
206
+ className="shrink-0"
207
+ onClick={(event) => {
208
+ event.stopPropagation();
209
+ const target = event.target as HTMLElement | null;
210
+ if (!target) return;
211
+ const root = event.currentTarget as HTMLElement;
212
+ const cancelAction = root.children.item(0) as HTMLElement | null;
213
+ const resendAction = root.children.item(
214
+ root.children.length - 1,
215
+ ) as HTMLElement | null;
216
+ if (cancelAction && cancelAction.contains(target)) {
217
+ onCancelInvite?.();
218
+ return;
219
+ }
220
+ if (resendAction && resendAction.contains(target)) {
221
+ onResendInvite?.();
222
+ }
223
+ }}
224
+ />
225
+ ```
226
+
227
+ That handler is the tell: the DS gap (no `onCancel`/`onResend`) is being paid for at the
228
+ call site.
229
+
230
+ ---
231
+
232
+ ## Related
233
+
234
+ - `ScButton` — the primitive this freezes two of; use it directly for any variation.
235
+ - `ScVDivider` — the separator between the pair.
236
+ - `ScTableList` / `ScRole` — the Teams row primitives it sits inside.
237
+ - `ScArtifaxInvite` / `ScCatalogixInvite` / `ScPhtogenixInvite` — the invite-flow cards.
238
+ - `ScMobileBottomAction` — the mobile action-pair equivalent, with real props.
@@ -0,0 +1,275 @@
1
+ ---
2
+ component: ScPhtogenixInvite
3
+ package: "@streamoid/ui"
4
+ category: cards
5
+ status: stable
6
+ renders: div
7
+ tags: [invite, app-access, photogenix, toggle, permissions, user-management, settings, onboarding, row]
8
+ related: [ScArtifaxInvite, ScCatalogixInvite, ScAppCard, ScToggleSwitch, ScAccess]
9
+ do_not_confuse_with: [ScArtifaxInvite, ScCatalogixInvite, ScAppCard, ScAppListingCard, ScAppCardV3, ScAppCardForCopilot, ScAccess]
10
+ ---
11
+
12
+ # ScPhtogenixInvite
13
+
14
+ **The Photogenix "does this user get the app?" row for the invite / edit-user
15
+ modal.** Markup-identical to `ScArtifaxInvite` — `ScAppCard` on the left,
16
+ `ScToggleSwitch` on the right, hairline divider underneath — but with a different
17
+ (worse) props API and no DOM prop forwarding.
18
+
19
+ > **Note the spelling.** The export and the folder are `ScPhtogenixInvite` /
20
+ > `SC-phtogenixInvite` — missing the **o** in "Photogenix". `ScPhotogenixInvite`
21
+ > does not exist; importing it fails.
22
+
23
+ ## TL;DR for agents
24
+
25
+ - **Reach for it when:** you need the **Photogenix** row in a per-app access list
26
+ and want it to match the neighbouring rows exactly.
27
+ - **Don't reach for it when:** the app needs a nested resource picker
28
+ (→ `ScCatalogixInvite`), or you're writing a new row and can pick the cleaner
29
+ API (→ `ScArtifaxInvite`, which takes `appName`/`appDescription`/`appIcon` and
30
+ forwards DOM props).
31
+ - **Five things that will bite you:**
32
+ 1. The content props are **prefixed**: `scAppCardappName`,
33
+ `scAppCardappDescription`, `scAppCardappIcon`. `appName` is not a prop here.
34
+ 2. ⚠️ It defaults to **ON** (`active = "true"`). Pass `enabled` explicitly.
35
+ 3. ⚠️ The default icon is `SiconArtifacts` — the **Artifax** icon on a
36
+ Photogenix row. Pass `scAppCardappIcon={<SiconPhotogenix />}`.
37
+ 4. `...props` is destructured and **never spread**. `onClick`, `style`,
38
+ `data-*`, `id` reach nothing.
39
+ 5. Default description is `"Info about photogenix"` — Figma filler copy.
40
+
41
+ ---
42
+
43
+ ## 1. How to use it
44
+
45
+ ### Import
46
+
47
+ ```tsx
48
+ import { ScPhtogenixInvite } from "@streamoid/ui";
49
+ import { SiconPhotogenix } from "@streamoid/icons";
50
+ import "@streamoid/ui/dist/index.css"; // once, at your app root
51
+ ```
52
+
53
+ ### Minimal usage
54
+
55
+ ```tsx
56
+ <ScPhtogenixInvite
57
+ enabled={photogenixOn}
58
+ onToggle={setPhotogenixOn}
59
+ scAppCardappDescription="AI product photography"
60
+ scAppCardappIcon={<SiconPhotogenix size={24} />}
61
+ />
62
+ ```
63
+
64
+ ### Props
65
+
66
+ | Prop | Type | Default | Notes |
67
+ |---|---|---|---|
68
+ | `enabled` | `boolean` | – | The real on/off. Overrides `active` whenever defined. **Use this one.** |
69
+ | `active` | `"true"` \| `"false"` | `"true"` | ⚠️ Figma-variant leftover, a *string*. Consulted only when `enabled` is `undefined` — so the row renders **ON** by default. |
70
+ | `onToggle` | `(active: boolean) => void` | – | Fires with the **next** value. Omit it and the switch is inert. |
71
+ | `scAppCardappName` | `string` | `"Photogenix"` | ⚠️ Note the prefix and the lowercase `app`. Passed to `ScAppCard`'s `appName`. |
72
+ | `scAppCardappDescription` | `string` | `"Info about photogenix"` | ⚠️ Real default, and it is placeholder copy. |
73
+ | `scAppCardappIcon` | `JSX.Element` | `<SiconArtifacts />` | ⚠️ The **wrong brand's** icon by default (see Gotcha 3). |
74
+ | `className` | `string` | – | Appended after internal classes. |
75
+
76
+ There is **no** `...props` pass-through: the interface does not extend
77
+ `React.HTMLAttributes`, and the rest parameter the component collects is never
78
+ applied to the DOM.
79
+
80
+ ### Recipes
81
+
82
+ ```tsx
83
+ // The Photogenix row done properly: explicit state, real copy, right icon
84
+ <ScPhtogenixInvite
85
+ enabled={access.photogenix}
86
+ onToggle={(next) => setAccess((a) => ({ ...a, photogenix: next }))}
87
+ scAppCardappName="Photogenix"
88
+ scAppCardappDescription="AI product photography"
89
+ scAppCardappIcon={<SiconPhotogenix size={24} />}
90
+ />
91
+
92
+ // Need a click target, a data-attribute or a style? Wrap it — nothing is forwarded.
93
+ <div data-testid="access-photogenix" onClick={onRowClick}>
94
+ <ScPhtogenixInvite enabled={photogenixOn} onToggle={setPhotogenixOn} />
95
+ </div>
96
+
97
+ // Drop the divider on the last row of the stack
98
+ <ScPhtogenixInvite
99
+ enabled={photogenixOn}
100
+ onToggle={setPhotogenixOn}
101
+ className={styles.lastRow} // .lastRow { border-bottom-width: 0 }
102
+ />
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 2. Where to use it
108
+
109
+ - **The invite-user modal's "App access" section**, between the Artifax row and the
110
+ Catalogix row.
111
+ - **The edit-user / manage-access drawer.**
112
+ - **First-run onboarding** product picker.
113
+
114
+ It composes `ScAppCard` (`flex: 1`) + `ScToggleSwitch` (`flex-shrink: 0`) and draws
115
+ its own bottom hairline, so stacked rows need no `ScHDivider`.
116
+
117
+ ---
118
+
119
+ ## 3. When to use it
120
+
121
+ ### Use it when
122
+
123
+ - You want the Photogenix row and you want it visually identical to the rest of the
124
+ access list (same 16px padding, same divider, same switch).
125
+
126
+ ### Don't use it — reach for this instead
127
+
128
+ | Situation | Use instead |
129
+ |---|---|
130
+ | You need `onClick` / `style` / `data-*` on the row itself | `ScArtifaxInvite` — same markup, spreads `...props` |
131
+ | The app needs a nested resource picker (stores, projects) | `ScCatalogixInvite` |
132
+ | App icon + name + description with **no** toggle | `ScAppCard` |
133
+ | A read-only "which apps does this user have" indicator | `ScAccess` |
134
+ | A marketing/launcher card for an app | `ScAppListingCard` / `ScAppCardV3` |
135
+ | The app card on the copilot/agent surface | `ScAppCardForCopilot` |
136
+ | Just the switch, in your own row | `ScToggleSwitch` |
137
+ | A labelled boolean with real form semantics | `ScCheckField` / `ScCheckbox` |
138
+
139
+ ### Don't confuse with
140
+
141
+ | You may actually want | Not this |
142
+ |---|---|
143
+ | `ScArtifaxInvite` — the same row with **clean** prop names (`appName`, `appDescription`, `appIcon`) and DOM prop forwarding | `ScPhtogenixInvite`'s props are `scAppCardapp*` and nothing is forwarded |
144
+ | `ScCatalogixInvite` — header row **plus** a store-access panel when enabled; defaults to **OFF** | This row is header-only and defaults to **ON** |
145
+ | `ScAppCard` — the inner icon/name/description block | This row wraps one |
146
+ | `ScPhotogenixInvite` (correct spelling) | Doesn't exist — the export is `ScPhtogenixInvite` |
147
+
148
+ Nothing but the default strings ties this component to Photogenix. If you're
149
+ adding a *new* app row, prefer `ScArtifaxInvite` with overridden copy: same
150
+ result, better API.
151
+
152
+ ---
153
+
154
+ ## 4. Why to use it
155
+
156
+ - **Pixel parity with the rest of the access list** — the flex/divider/padding
157
+ combination is the actual spec, and it's what keeps a stack of rows from going
158
+ ragged when one app has a longer description.
159
+ - **The shared toggle.** `ScToggleSwitch` draws the exact Figma on/off SVGs from
160
+ alias tokens, so it flips with the theme and matches every other switch.
161
+ - **Controlled boolean contract** (`enabled` + `onToggle(next)`), which is what an
162
+ invite form submits.
163
+ - **It deletes duplicated markup.** `@streamoid/settings` hand-writes this row —
164
+ including the string `"Info about photogenix"` — with its own local
165
+ `ToggleSwitch`.
166
+
167
+ If you're choosing between the trio for a new row, note plainly: this one is the
168
+ weakest of the three (prefixed props, dropped DOM props, wrong default icon). It
169
+ exists because it was generated per-Figma-frame, not because Photogenix needs
170
+ different behaviour.
171
+
172
+ ---
173
+
174
+ ## Gotchas
175
+
176
+ **1. The content props are prefixed and oddly cased.** `scAppCardappName`, not
177
+ `appName`; `scAppCardappDescription`, not `appDescription`. Written as JSX
178
+ attributes the unprefixed names are a TS error (the interface does not extend
179
+ `HTMLAttributes`, so there is nothing for them to fall into) — but spread from a
180
+ variable they slip past excess-property checking and vanish silently, leaving the
181
+ Photogenix defaults on screen (same mechanism as Gotcha 4).
182
+
183
+ ```tsx
184
+ // WRONG — type error as written; silent no-op if spread from an object
185
+ <ScPhtogenixInvite appName="Photogenix Studio" appDescription="AI photography" />
186
+
187
+ // RIGHT
188
+ <ScPhtogenixInvite
189
+ scAppCardappName="Photogenix Studio"
190
+ scAppCardappDescription="AI photography"
191
+ />
192
+ ```
193
+
194
+ **2. It renders ON unless you pass `enabled`.** `active` defaults to `"true"`.
195
+ (`ScCatalogixInvite` defaults to `"false"` — the trio is inconsistent.)
196
+
197
+ ```tsx
198
+ // WRONG — invite form starts with Photogenix already granted
199
+ <ScPhtogenixInvite onToggle={setPhotogenixOn} />
200
+
201
+ // RIGHT
202
+ <ScPhtogenixInvite enabled={photogenixOn} onToggle={setPhotogenixOn} />
203
+ ```
204
+
205
+ **3. The default icon is the Artifax icon.** `scAppCardappIcon` defaults to
206
+ `<SiconArtifacts />`. Ship it and the Photogenix row wears Artifax's mark. There
207
+ is a correct icon: `SiconPhotogenix` (see `packages/icons/ICONS.md`).
208
+
209
+ **4. `...props` goes nowhere.** The root `<div>` receives only `className`.
210
+ Spreading extra props from a variable type-checks (no excess-property check on
211
+ spreads) and then vanishes at runtime.
212
+
213
+ ```tsx
214
+ // WRONG — the handler and the test id are dropped on the floor
215
+ <ScPhtogenixInvite {...{ onClick: onRowClick, "data-testid": "photogenix-row" }} />
216
+
217
+ // RIGHT — wrap it
218
+ <div onClick={onRowClick} data-testid="photogenix-row">
219
+ <ScPhtogenixInvite enabled={photogenixOn} onToggle={setPhotogenixOn} />
220
+ </div>
221
+ ```
222
+
223
+ **5. `active` is a string, not a boolean,** and `enabled` silently wins when both
224
+ are present. Never pass both.
225
+
226
+ **6. It is stateless.** No `onToggle` → clicking the switch does nothing, because
227
+ `checked` is derived from your props on every render.
228
+
229
+ **7. You cannot disable the toggle.** `ScToggleSwitch` has a `disabled` prop; this
230
+ row never forwards it and exposes none. For read-only access, render `ScAppCard`
231
+ plus your own indicator.
232
+
233
+ **8. No keyboard or screen-reader support.** The switch is a `<div onClick>`, not
234
+ an `<input type="checkbox">`; the row has no `role` and no focusable element. Use
235
+ `ScCheckField` if the control must be operable without a mouse.
236
+
237
+ **9. `styles["active-true"]` doesn't exist.** Only `.active-false` is defined (it
238
+ thickens the divider from 0.5px to 1px), so in the ON state the variant class
239
+ resolves to `undefined` and appears in the class attribute as the literal
240
+ `"undefined"`, as does an omitted `className`. Don't assert on the class list.
241
+
242
+ **10. Every row draws its own bottom divider,** including the last one. Strip it
243
+ via `className`.
244
+
245
+ ---
246
+
247
+ ## In the wild
248
+
249
+ _No host render site found — used by the agent runtime / composed internally._
250
+
251
+ The surface exists but is hand-rolled in the shared settings package, which
252
+ duplicates this row rather than importing it:
253
+
254
+ ```tsx
255
+ // @streamoid/settings packages/settings/src/invite-update-modal.tsx:689
256
+ <ToggleSwitch // local component, not ScToggleSwitch
257
+ active={photogenixOn}
258
+ onClick={() => { setPhotogenixOn(!photogenixOn); markChanged(); }}
259
+ />
260
+ // …preceded by hand-written markup duplicating ScAppCard, with the literal
261
+ // string "Info about photogenix" (line 685). Local ToggleSwitch: line 84.
262
+ ```
263
+
264
+ That modal (CXO settings → Teams → invite/edit user) is where this component
265
+ belongs.
266
+
267
+ ---
268
+
269
+ ## Related
270
+
271
+ - `ScArtifaxInvite` — the same row with the cleaner API; prefer it for new rows.
272
+ - `ScCatalogixInvite` — the same row plus a store-access panel when enabled.
273
+ - `ScAppCard` — the icon/name/description block inside it.
274
+ - `ScToggleSwitch` — the switch on the right, standalone.
275
+ - `ScAccess` — read-only app-access icon row.