@xsolla/xui-b2b-sidebar 0.150.0 → 0.152.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,43 @@
1
- # B2B Sidebar
1
+ # Sidebar
2
2
 
3
- A composable navigation sidebar for B2B admin surfaces. Renders an expanded panel with grouped menu items and a parallel collapsed icon strip with hover-popovers, both driven by the same `SidebarProvider` so the active route, link component, and collapsed state stay in sync.
3
+ A composable navigation sidebar for B2B admin surfaces. Renders an expanded panel with grouped menu items and a parallel collapsed icon strip with hover popovers, both driven by the same `SidebarProvider` so the active route, link component, and collapsed state stay in sync.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
8
  npm install @xsolla/xui-b2b-sidebar
9
- # or
10
- yarn add @xsolla/xui-b2b-sidebar
11
9
  ```
12
10
 
13
- ## Demo
11
+ ## Imports
14
12
 
15
- ### Basic usage
13
+ ```tsx
14
+ import {
15
+ Sidebar,
16
+ SidebarProvider,
17
+ useSidebar,
18
+ SidebarContent,
19
+ SidebarFooter,
20
+ SidebarGroup,
21
+ SidebarMenu,
22
+ SidebarMenuItem,
23
+ SidebarMenuCollapsible,
24
+ SidebarMenuSub,
25
+ SidebarTrigger,
26
+ SidebarChatButton,
27
+ SidebarCollapsed,
28
+ type SidebarProps,
29
+ type SidebarProviderProps,
30
+ type SidebarMenuItemProps,
31
+ type SidebarMenuCollapsibleProps,
32
+ type SidebarCollapsedProps,
33
+ type SidebarItemType,
34
+ type SidebarLinkProps,
35
+ type SidebarLinkActiveCheck,
36
+ type SidebarLinkClickHandler,
37
+ } from '@xsolla/xui-b2b-sidebar';
38
+ ```
39
+
40
+ ## Quick start
16
41
 
17
42
  Wrap the sidebar in `SidebarProvider`, supplying the current `pathname`, the controlled `collapsed` state, and your app's link component (React Router, Next.js `Link`, etc.). The default link is a plain `<a>` if you omit `linkComponent`.
18
43
 
@@ -32,7 +57,7 @@ import {
32
57
  SidebarTrigger,
33
58
  } from '@xsolla/xui-b2b-sidebar';
34
59
 
35
- export default function AppShell({ pathname }: { pathname: string }) {
60
+ function AppShell({ pathname }: { pathname: string }) {
36
61
  const [collapsed, setCollapsed] = useState(false);
37
62
 
38
63
  return (
@@ -40,22 +65,18 @@ export default function AppShell({ pathname }: { pathname: string }) {
40
65
  collapsed={collapsed}
41
66
  onCollapsedChange={setCollapsed}
42
67
  pathname={pathname}
43
- linkComponent={MyRouterLink}
44
68
  >
45
- <Sidebar
46
- collapsedItems={mainNavItems}
47
- collapsedBottomItems={workspaceNavItems}
48
- >
69
+ <Sidebar>
49
70
  <SidebarContent>
50
71
  <SidebarGroup label="Main">
51
72
  <SidebarMenu>
52
73
  <SidebarMenuItem
53
74
  to="/dashboard"
54
- icon={<Home size={18} variant="line" />}
75
+ icon={<Home size={18} variant="line" aria-hidden />}
55
76
  label="Dashboard"
56
77
  />
57
78
  <SidebarMenuCollapsible
58
- icon={<Wallet size={18} variant="line" />}
79
+ icon={<Wallet size={18} variant="line" aria-hidden />}
59
80
  label="Finance"
60
81
  matchPaths={['/finance']}
61
82
  >
@@ -70,7 +91,7 @@ export default function AppShell({ pathname }: { pathname: string }) {
70
91
  <SidebarMenu>
71
92
  <SidebarMenuItem
72
93
  to="/settings"
73
- icon={<Settings size={18} variant="line" />}
94
+ icon={<Settings size={18} variant="line" aria-hidden />}
74
95
  label="Settings"
75
96
  />
76
97
  </SidebarMenu>
@@ -85,9 +106,176 @@ export default function AppShell({ pathname }: { pathname: string }) {
85
106
  }
86
107
  ```
87
108
 
109
+ ## API Reference
110
+
111
+ None of the sidebar components extend `ThemeOverrideProps` — they read theme via `useResolvedTheme()` from the surrounding provider. Wrap the app in a theme provider if you need to override mode or product context.
112
+
113
+ ### `<SidebarProvider>`
114
+
115
+ | Prop | Type | Default | Description |
116
+ | --- | --- | --- | --- |
117
+ | `collapsed` | `boolean` | uncontrolled | Controlled collapsed state. Omit to use the internal state. |
118
+ | `onCollapsedChange` | `(collapsed: boolean) => void` | — | Called when the user toggles via `SidebarTrigger` or the collapsed-mode toggle. |
119
+ | `pathname` | `string` | `""` | Current route, used for active-state matching and `matchPaths` auto-expand. |
120
+ | `linkComponent` | `ComponentType<SidebarLinkProps>` | plain `<a>` | Router link component. |
121
+ | `children` | `ReactNode` | — | Sidebar tree. |
122
+
123
+ ### `useSidebar()`
124
+
125
+ Read the provider's state from anywhere inside the tree.
126
+
127
+ ```ts
128
+ function useSidebar(): {
129
+ collapsed: boolean;
130
+ onCollapsedChange: (collapsed: boolean) => void;
131
+ pathname: string;
132
+ LinkComponent: React.ComponentType<SidebarLinkProps>;
133
+ expandedId: string | null;
134
+ onExpandedIdChange: (id: string | null) => void;
135
+ };
136
+ ```
137
+
138
+ `expandedId` / `onExpandedIdChange` coordinate which `SidebarMenuCollapsible` is open — only one at a time.
139
+
140
+ ### `<Sidebar>`
141
+
142
+ | Prop | Type | Default | Description |
143
+ | --- | --- | --- | --- |
144
+ | `collapsedItems` | `SidebarItemType[]` | `[]` | Top icons in the collapsed strip. |
145
+ | `collapsedToolItems` | `SidebarItemType[]` | `[]` | Tool icons rendered after a spacer. |
146
+ | `collapsedBottomItems` | `SidebarItemType[]` | `[]` | Bottom icons (e.g. Settings, Billing). |
147
+ | `showChat` | `boolean` | `true` | Render the chat button in the collapsed footer. |
148
+ | `onChatClick` | `() => void` | — | Click handler for the chat button. |
149
+ | `chatBadge` | `boolean` | `false` | Show an unread-indicator dot on the chat button. |
150
+ | `children` | `ReactNode` | — | The expanded tree (`SidebarContent`, `SidebarFooter`). |
151
+
152
+ ### `<SidebarMenuItem>`
153
+
154
+ | Prop | Type | Default | Description |
155
+ | --- | --- | --- | --- |
156
+ | `to` | `string` | — | Route URL passed through to `linkComponent`. |
157
+ | `label` | `ReactNode` | — | Text label. |
158
+ | `icon` | `ReactNode` | — | Leading icon (hidden when `isNested`). |
159
+ | `exact` | `boolean` | — | Exact-match flag forwarded to the link component. |
160
+ | `external` | `boolean` | — | Marks the link as external (sets `target`/`rel`). |
161
+ | `hasExternalIcon` | `boolean` | — | Shows the external-link icon at the trailing edge. |
162
+ | `target` | `string \| null` | — | Anchor target. |
163
+ | `onClick` | `SidebarLinkClickHandler` | — | Click handler. |
164
+ | `dataId` | `string` | — | `data-id` attribute for tests/analytics. |
165
+ | `isActive` | `SidebarLinkActiveCheck` | — | Custom active-check forwarded to the link. |
166
+ | `isPinned` | `boolean` | — | Renders the icon at 12px to indicate a pinned item. |
167
+ | `showBadge` | `boolean` | — | Shows a small alert dot next to the label. |
168
+ | `hasTooltip` | `boolean` | — | Truncates labels longer than 20 chars and shows the full text on hover. |
169
+ | `beta` | `boolean` | — | Renders a "Beta" tag at the trailing edge. |
170
+ | `multiLine` | `boolean` | — | Allows the label to wrap to multiple lines. |
171
+ | `extra` | `ReactNode` | — | Slot rendered between the icon and the label. |
172
+ | `isNested` | `boolean` | `false` | Set when rendered inside `SidebarMenuSub` (drops the icon, indents under the rail). |
173
+
174
+ ### `<SidebarMenuCollapsible>`
175
+
176
+ | Prop | Type | Default | Description |
177
+ | --- | --- | --- | --- |
178
+ | `icon` | `ReactNode` | — | Leading icon. |
179
+ | `label` | `ReactNode` | — | Section label. |
180
+ | `dataId` | `string` | — | `data-id` attribute. Also seeds the stable id used by `aria-controls`. |
181
+ | `matchPaths` | `string[]` | `[]` | Route prefixes that auto-expand this section. |
182
+ | `children` | `ReactNode` | — | Nested items, typically a `SidebarMenuSub` of `SidebarMenuItem isNested`. |
183
+
184
+ ### `<SidebarCollapsed>`
185
+
186
+ Lower-level collapsed-strip renderer. The `<Sidebar>` component composes this for you; reach for it when you need to render the collapsed strip outside the standard layout.
187
+
188
+ | Prop | Type | Default | Description |
189
+ | --- | --- | --- | --- |
190
+ | `items` | `SidebarItemType[]` | — | Top icon items. |
191
+ | `toolItems` | `SidebarItemType[]` | `[]` | Tool icons after a spacer. |
192
+ | `bottomItems` | `SidebarItemType[]` | `[]` | Bottom icons. |
193
+ | `onToggleCollapse` | `() => void` | — | Click handler for the toggle button in the footer. |
194
+ | `onChatClick` | `() => void` | — | Click handler for the chat button. |
195
+ | `showChat` | `boolean` | `true` | Render the chat button. |
196
+ | `chatBadge` | `boolean` | `false` | Show the unread dot. |
197
+
198
+ ### `<SidebarContent>`
199
+
200
+ Scrollable body container rendered inside `<Sidebar>`. Adds the configured padding and a hover-visible scrollbar.
201
+
202
+ ### `<SidebarFooter>`
203
+
204
+ Bordered footer row for the bottom of the sidebar; typically wraps the chat button and trigger.
205
+
206
+ ### `<SidebarTrigger>`
207
+
208
+ Collapse/expand toggle button. Reads/writes state from the surrounding `SidebarProvider`.
209
+
210
+ ### `<SidebarChatButton>`
211
+
212
+ Branded chat button for the footer. Props: `onClick: () => void`, `badge?: boolean`.
213
+
214
+ ### `<SidebarGroup>`
215
+
216
+ Section container with an optional uppercase label. Props: `label?: ReactNode`, `children: ReactNode`.
217
+
218
+ ### `<SidebarMenu>`
219
+
220
+ Flex-column wrapper around `SidebarMenuItem` / `SidebarMenuCollapsible` children.
221
+
222
+ ### `<SidebarMenuSub>`
223
+
224
+ Fragment used as a structural marker inside collapsibles to group nested menu items.
225
+
226
+ ### Types
227
+
228
+ ```ts
229
+ type SidebarItemType = {
230
+ to?: string;
231
+ label: ReactNode;
232
+ icon?: ReactNode;
233
+ dataId?: string;
234
+ exact?: boolean;
235
+ external?: boolean;
236
+ hasExternalIcon?: boolean;
237
+ target?: string | null;
238
+ onClick?: SidebarLinkClickHandler;
239
+ hasTooltip?: boolean;
240
+ beta?: boolean;
241
+ multiLine?: boolean;
242
+ isPinned?: boolean;
243
+ showBadge?: boolean;
244
+ reverse?: boolean; // reserved for legacy reversed-layout items
245
+ isActive?: SidebarLinkActiveCheck;
246
+ children?: SidebarItemType[];
247
+ privileges?: string[];
248
+ visibility?: string;
249
+ disallowedIntegrationTypes?: string[];
250
+ extra?: ReactNode;
251
+ };
252
+
253
+ interface SidebarLinkProps {
254
+ to?: string;
255
+ exact?: boolean;
256
+ external?: boolean;
257
+ target?: string | null;
258
+ className?: string;
259
+ activeClassName?: string;
260
+ isActive?: SidebarLinkActiveCheck;
261
+ onClick?: SidebarLinkClickHandler;
262
+ dataId?: string;
263
+ children: ReactNode;
264
+ }
265
+
266
+ type SidebarLinkActiveCheck = (
267
+ match: unknown,
268
+ location: { pathname: string; search?: string; hash?: string }
269
+ ) => boolean;
270
+
271
+ type SidebarLinkClickHandler = (event?: React.MouseEvent<HTMLElement>) => void;
272
+ ```
273
+
274
+ ## Examples
275
+
88
276
  ### Custom link component
89
277
 
90
- Supply your router's link via the `linkComponent` prop on `SidebarProvider`. The component receives `to`, `className`, `activeClassName`, `onClick`, `external`, `target`, `dataId`, and renders children.
278
+ Supply your router's link via the `linkComponent` prop on `SidebarProvider`. The component receives `to`, `exact`, `external`, `target`, `className`, `activeClassName`, `isActive`, `onClick`, `dataId`, and renders children.
91
279
 
92
280
  ```tsx
93
281
  import { NavLink } from 'react-router-dom';
@@ -105,8 +293,7 @@ const RouterLink: React.FC<SidebarLinkProps> = ({
105
293
  <NavLink
106
294
  to={to ?? '#'}
107
295
  end={exact}
108
- className={className}
109
- activeClassName={activeClassName}
296
+ className={({ isActive }) => [className, isActive && activeClassName].filter(Boolean).join(' ')}
110
297
  data-id={dataId}
111
298
  onClick={onClick}
112
299
  >
@@ -114,48 +301,75 @@ const RouterLink: React.FC<SidebarLinkProps> = ({
114
301
  </NavLink>
115
302
  );
116
303
 
117
- <SidebarProvider linkComponent={RouterLink} pathname={location.pathname} ...>
304
+ <SidebarProvider linkComponent={RouterLink} pathname={location.pathname}>
305
+ {/* ... */}
306
+ </SidebarProvider>;
118
307
  ```
119
308
 
120
309
  ### Auto-expanding sections
121
310
 
122
- Pass `matchPaths` to `SidebarMenuCollapsible`. When the current `pathname` starts with any of the listed prefixes, the section auto-expands and stays expanded. Only one collapsible can be open at a time (Radix-style accordion), so navigating between sections collapses the others.
311
+ Pass `matchPaths` to `SidebarMenuCollapsible`. When the current `pathname` starts with any of the listed prefixes, the section auto-expands. Only one collapsible can be open at a time, so navigating between sections collapses the others.
123
312
 
124
313
  ```tsx
314
+ import { Wallet } from '@xsolla/xui-icons-base';
315
+ import { SidebarMenuCollapsible, SidebarMenuSub, SidebarMenuItem } from '@xsolla/xui-b2b-sidebar';
316
+
125
317
  <SidebarMenuCollapsible
126
- icon={<Wallet size={18} variant="line" />}
318
+ icon={<Wallet size={18} variant="line" aria-hidden />}
127
319
  label="Finance"
128
320
  matchPaths={['/finance', '/finance/payouts', '/finance/reports']}
129
321
  >
130
- <SidebarMenuSub>{/* ... */}</SidebarMenuSub>
131
- </SidebarMenuCollapsible>
322
+ <SidebarMenuSub>
323
+ <SidebarMenuItem to="/finance/payouts" label="Payouts" isNested />
324
+ <SidebarMenuItem to="/finance/reports" label="Reports" isNested />
325
+ </SidebarMenuSub>
326
+ </SidebarMenuCollapsible>;
132
327
  ```
133
328
 
134
- ### Item flags (pin / badge / beta / external)
329
+ ### Item flags
135
330
 
136
331
  ```tsx
137
- <SidebarMenuItem to="/pinned" label="Pinned report" icon={<Pin size={18} />} isPinned showBadge />
138
- <SidebarMenuItem to="/labs" label="New analytics" icon={<Graph size={18} />} beta />
139
- <SidebarMenuItem
140
- to="https://docs.example.com"
141
- label="Documentation"
142
- icon={<Layer size={18} />}
143
- external
144
- hasExternalIcon
145
- target="_blank"
146
- />
332
+ import { Pin, Graph, Layer } from '@xsolla/xui-icons-base';
333
+ import { SidebarMenuItem } from '@xsolla/xui-b2b-sidebar';
334
+
335
+ <>
336
+ <SidebarMenuItem
337
+ to="/pinned"
338
+ label="Pinned report"
339
+ icon={<Pin size={18} aria-hidden />}
340
+ isPinned
341
+ showBadge
342
+ />
343
+ <SidebarMenuItem
344
+ to="/labs"
345
+ label="New analytics"
346
+ icon={<Graph size={18} aria-hidden />}
347
+ beta
348
+ />
349
+ <SidebarMenuItem
350
+ to="https://docs.example.com"
351
+ label="Documentation"
352
+ icon={<Layer size={18} aria-hidden />}
353
+ external
354
+ hasExternalIcon
355
+ target="_blank"
356
+ />
357
+ </>;
147
358
  ```
148
359
 
149
360
  ### Collapsed icon strip
150
361
 
151
- Because the collapsed layout is fundamentally different (icons only, hover popovers), pass the icon-strip items separately as `collapsedItems`, `collapsedToolItems` (rendered after a spacer in the main scroll area), and `collapsedBottomItems` (rendered above the chat/toggle footer). Items with `children` open a hover popover listing the children; items without children open a single-link popover.
362
+ Because the collapsed layout is fundamentally different (icons only, hover popovers), pass icon-strip items separately as `collapsedItems`, `collapsedToolItems` (rendered after a spacer in the main scroll area), and `collapsedBottomItems` (rendered above the chat/toggle footer). Items with `children` open a hover popover listing the children; items without children open a single-link popover.
152
363
 
153
364
  ```tsx
365
+ import { Home, Wallet, Settings } from '@xsolla/xui-icons-base';
366
+ import { Sidebar, type SidebarItemType } from '@xsolla/xui-b2b-sidebar';
367
+
154
368
  const mainNavItems: SidebarItemType[] = [
155
- { to: '/dashboard', label: 'Dashboard', icon: <Home size={18} variant="line" /> },
369
+ { to: '/dashboard', label: 'Dashboard', icon: <Home size={18} variant="line" aria-hidden /> },
156
370
  {
157
371
  label: 'Finance',
158
- icon: <Wallet size={18} variant="line" />,
372
+ icon: <Wallet size={18} variant="line" aria-hidden />,
159
373
  children: [
160
374
  { to: '/finance/payouts', label: 'Payouts' },
161
375
  { to: '/finance/reports', label: 'Reports' },
@@ -165,116 +379,27 @@ const mainNavItems: SidebarItemType[] = [
165
379
 
166
380
  <Sidebar
167
381
  collapsedItems={mainNavItems}
168
- collapsedBottomItems={[{ to: '/settings', label: 'Settings', icon: <Settings size={18} /> }]}
382
+ collapsedBottomItems={[
383
+ { to: '/settings', label: 'Settings', icon: <Settings size={18} aria-hidden /> },
384
+ ]}
169
385
  onChatClick={() => openSupportChat()}
170
386
  chatBadge={hasUnreadMessages}
171
- />
387
+ >
388
+ {/* expanded tree */}
389
+ </Sidebar>;
172
390
  ```
173
391
 
174
- ## API Reference
175
-
176
- ### SidebarProvider
177
-
178
- | Prop | Type | Default | Description |
179
- | :--- | :--- | :------ | :---------- |
180
- | `collapsed` | `boolean` | `false` | Controlled collapsed state. |
181
- | `onCollapsedChange` | `(collapsed: boolean) => void` | - | Called when the user toggles collapsed via the trigger button. |
182
- | `pathname` | `string` | `""` | Current route, used for active-state matching and auto-expand. |
183
- | `linkComponent` | `ComponentType<SidebarLinkProps>` | plain `<a>` | Router link component. Receives `to`, `className`, `activeClassName`, `onClick`, `external`, `target`, `dataId`, `children`. |
184
- | `children` | `ReactNode` | - | Sidebar tree. |
185
-
186
- ### Sidebar
187
-
188
- | Prop | Type | Default | Description |
189
- | :--- | :--- | :------ | :---------- |
190
- | `collapsedItems` | `SidebarItemType[]` | `[]` | Top icons in the collapsed strip. |
191
- | `collapsedToolItems` | `SidebarItemType[]` | `[]` | Tool icons rendered after a spacer. |
192
- | `collapsedBottomItems` | `SidebarItemType[]` | `[]` | Bottom icons (e.g. Settings, Billing). |
193
- | `showChat` | `boolean` | `true` | Render the chat button in the collapsed footer. |
194
- | `onChatClick` | `() => void` | - | Click handler for the chat button. |
195
- | `chatBadge` | `boolean` | `false` | Show an unread-indicator dot on the chat button. |
196
- | `children` | `ReactNode` | - | The expanded tree (`SidebarContent`, `SidebarFooter`). |
197
-
198
- ### SidebarMenuItem
199
-
200
- | Prop | Type | Default | Description |
201
- | :--- | :--- | :------ | :---------- |
202
- | `to` | `string` | - | Route URL passed through to `linkComponent`. |
203
- | `label` | `ReactNode` | - | Text label. |
204
- | `icon` | `ReactNode` | - | Leading icon (hidden when `isNested`). |
205
- | `exact` | `boolean` | - | Exact-match flag forwarded to the link component. |
206
- | `external` | `boolean` | - | Marks the link as external (sets `target`/`rel`). |
207
- | `hasExternalIcon` | `boolean` | - | Shows the external-link icon at the trailing edge. |
208
- | `target` | `string \| null` | - | Anchor target. |
209
- | `onClick` | `(event?) => void` | - | Click handler. |
210
- | `dataId` | `string` | - | `data-id` attribute for tests/analytics. |
211
- | `isActive` | `(match, location) => boolean` | - | Custom active-check forwarded to the link. |
212
- | `isPinned` | `boolean` | - | Renders the icon at 12px to indicate a pinned item. |
213
- | `showBadge` | `boolean` | - | Shows a small alert dot next to the label. |
214
- | `hasTooltip` | `boolean` | - | Truncates labels longer than 20 chars and shows the full text on hover. |
215
- | `beta` | `boolean` | - | Renders a "Beta" tag at the trailing edge. |
216
- | `multiLine` | `boolean` | - | Allows the label to wrap to multiple lines. |
217
- | `extra` | `ReactNode` | - | Slot rendered between the icon and the label. |
218
- | `isNested` | `boolean` | `false` | Set when rendered inside `SidebarMenuSub` (drops the icon, indents under the rail). |
219
-
220
- ### SidebarMenuCollapsible
221
-
222
- | Prop | Type | Default | Description |
223
- | :--- | :--- | :------ | :---------- |
224
- | `icon` | `ReactNode` | - | Leading icon. |
225
- | `label` | `ReactNode` | - | Section label. |
226
- | `dataId` | `string` | - | `data-id` attribute. |
227
- | `matchPaths` | `string[]` | `[]` | Route prefixes that auto-expand this section. |
228
- | `children` | `ReactNode` | - | Nested items, typically a `SidebarMenuSub` containing `SidebarMenuItem isNested`. |
229
-
230
- ### SidebarTrigger / SidebarChatButton / SidebarFooter / SidebarGroup / SidebarMenu / SidebarMenuSub
231
-
232
- Layout primitives:
233
-
234
- - `<SidebarTrigger />` — collapse/expand toggle button (reads state from the provider).
235
- - `<SidebarChatButton onClick badge />` — branded chat button for the footer.
236
- - `<SidebarFooter>{...}</SidebarFooter>` — bordered footer row, typically holding the chat button and trigger.
237
- - `<SidebarGroup label="...">` — section container with optional uppercase label.
238
- - `<SidebarMenu>` — flex column wrapper around items.
239
- - `<SidebarMenuSub>` — fragment used as a structural marker inside collapsibles.
240
-
241
- ### Types
392
+ ## Accessibility
242
393
 
243
- ```ts
244
- type SidebarItemType = {
245
- to?: string;
246
- label: ReactNode;
247
- icon?: ReactNode;
248
- dataId?: string;
249
- exact?: boolean;
250
- external?: boolean;
251
- hasExternalIcon?: boolean;
252
- target?: string | null;
253
- onClick?: (event?: MouseEvent) => void;
254
- hasTooltip?: boolean;
255
- beta?: boolean;
256
- multiLine?: boolean;
257
- isPinned?: boolean;
258
- showBadge?: boolean;
259
- isActive?: (match: unknown, location: { pathname: string }) => boolean;
260
- children?: SidebarItemType[];
261
- privileges?: string[];
262
- visibility?: string;
263
- disallowedIntegrationTypes?: string[];
264
- };
265
- ```
394
+ - The root sidebar is `role="navigation"` with `aria-label="Sidebar navigation"`. The expanded and collapsed panes are toggled via `aria-hidden` so only one is announced at a time.
395
+ - `SidebarTrigger` and the collapsed-mode toggle have `aria-pressed` and a context-aware `aria-label` (`"Expand sidebar"` / `"Collapse sidebar"`).
396
+ - Each `SidebarMenuCollapsible` header is a `<button>` with `aria-expanded` and `aria-controls` linking to its region.
397
+ - Collapsed icon items expose `aria-haspopup="menu"` when they have children; popovers are keyboard-reachable (focus opens, blur closes after 150ms; Escape closes immediately).
398
+ - Truncated labels (`hasTooltip`) fall back to a Tooltip with the full text.
266
399
 
267
400
  ## Behaviour
268
401
 
269
- - Expanded width is 280px, collapsed width is 48px; the wrapper animates `width` over 250ms and cross-fades the two views via opacity.
270
- - Only one `SidebarMenuCollapsible` is open at a time; the section auto-expands when `pathname` matches one of `matchPaths`.
402
+ - Expanded width is sourced from `theme.sizing.sidebar().widthExpanded`, collapsed from `widthCollapsed`; the wrapper animates `width` and cross-fades the two views via opacity.
403
+ - Only one `SidebarMenuCollapsible` is open at a time; the section auto-expands when `pathname` matches one of `matchPaths` (a leading `/<merchantId>` numeric prefix is stripped before matching).
271
404
  - Hover popovers in collapsed mode are rendered into a portal on `document.body` and positioned with viewport-clamped `position: fixed` math.
272
- - Custom scrollbar styling on the expanded scroll area; thumb fades in on sidebar hover.
273
405
  - Active route detection lives in the consumer's `linkComponent` — apply `activeClassName` when the link matches the current route.
274
-
275
- ## Accessibility
276
-
277
- - Collapse/expand button has `aria-pressed` and a context-aware `aria-label` (`"Expand sidebar"` / `"Collapse sidebar"`).
278
- - Each `SidebarMenuCollapsible` header is a `<button>` with `aria-expanded` and `aria-controls` linking to the region.
279
- - Collapsed icon items expose `aria-haspopup="menu"` when they have children; popover is keyboard-reachable and closes on Escape.
280
- - Truncated labels (`hasTooltip`) fall back to a Tooltip with the full text.
@@ -61,6 +61,7 @@ type SidebarItemType = {
61
61
  visibility?: string;
62
62
  disallowedIntegrationTypes?: Array<string>;
63
63
  isPinned?: boolean;
64
+ onPinToggle?: (e: MouseEvent<HTMLButtonElement>) => void;
64
65
  showBadge?: boolean;
65
66
  };
66
67
 
@@ -119,6 +120,8 @@ declare const SidebarTrigger: React.FC;
119
120
  interface SidebarGroupProps {
120
121
  label?: string;
121
122
  dataId?: string;
123
+ /** Docks this group to the bottom of the expanded sidebar (e.g. Settings, Billing). */
124
+ pinnedToBottom?: boolean;
122
125
  children: ReactNode;
123
126
  }
124
127
  declare const SidebarGroup: React.FC<SidebarGroupProps>;
@@ -139,6 +142,10 @@ interface SidebarMenuItemProps {
139
142
  dataId?: string;
140
143
  isActive?: SidebarLinkActiveCheck;
141
144
  isPinned?: boolean;
145
+ /** When provided, renders a pin/unpin toggle on hover (or always, when pinned). */
146
+ onPinToggle?: (e: React.MouseEvent<HTMLButtonElement>) => void;
147
+ /** When true on a pinned item, the leading pin glyph swaps to a drag handle on row hover (reorder affordance). */
148
+ dragHandle?: boolean;
142
149
  showBadge?: boolean;
143
150
  hasTooltip?: boolean;
144
151
  beta?: boolean;
@@ -185,4 +192,42 @@ interface SidebarCollapsedProps {
185
192
  }
186
193
  declare const SidebarCollapsed: React.FC<SidebarCollapsedProps>;
187
194
 
188
- export { Sidebar, SidebarChatButton, SidebarCollapsed, type SidebarCollapsedProps, SidebarContent, SidebarFooter, SidebarGroup, type SidebarItemType, type SidebarLinkActiveCheck, type SidebarLinkClickHandler, type SidebarLinkProps, SidebarMenu, SidebarMenuCollapsible, type SidebarMenuCollapsibleProps, SidebarMenuItem, type SidebarMenuItemProps, SidebarMenuSub, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, useSidebar };
195
+ interface SidebarPinnedItem {
196
+ key: string;
197
+ to: string;
198
+ label: ReactNode;
199
+ exact?: boolean;
200
+ }
201
+ interface SidebarPinnedListProps {
202
+ /** Ordered list of currently-pinned items. */
203
+ items: SidebarPinnedItem[];
204
+ /** Called when the user clicks the × on a pinned shortcut. */
205
+ onUnpin: (key: string) => void;
206
+ /**
207
+ * Called as the user drags to reorder. Omit to disable drag-and-drop —
208
+ * the leading icon stays as the static pin glyph.
209
+ */
210
+ onReorder?: (fromKey: string, toKey: string) => void;
211
+ /** Render 8px top/bottom spacers when items are present. Default true. */
212
+ spacers?: boolean;
213
+ }
214
+ /**
215
+ * Renders the pinned shortcuts list with pointer-event-based drag-and-drop
216
+ * reorder. Drag only initiates from the leading drag-handle area on each row.
217
+ *
218
+ * Pinning is fully opt-in: consumers who don't render this component get no
219
+ * pin UI. Consumers without a reorder backend can omit `onReorder`; the list
220
+ * stays static but unpinning still works.
221
+ */
222
+ declare const SidebarPinnedList: React.FC<SidebarPinnedListProps>;
223
+ /**
224
+ * Helper for the collapsed sidebar: returns a single `SidebarItemType` whose
225
+ * popover lists every pinned shortcut. Spread it into `collapsedItems` at the
226
+ * position you want the Pin button to appear. Returns `null` when no items.
227
+ */
228
+ declare const getPinnedCollapsedItem: (items: SidebarPinnedItem[], options?: {
229
+ label?: string;
230
+ dataId?: string;
231
+ }) => SidebarItemType | null;
232
+
233
+ export { Sidebar, SidebarChatButton, SidebarCollapsed, type SidebarCollapsedProps, SidebarContent, SidebarFooter, SidebarGroup, type SidebarItemType, type SidebarLinkActiveCheck, type SidebarLinkClickHandler, type SidebarLinkProps, SidebarMenu, SidebarMenuCollapsible, type SidebarMenuCollapsibleProps, SidebarMenuItem, type SidebarMenuItemProps, SidebarMenuSub, type SidebarPinnedItem, SidebarPinnedList, type SidebarPinnedListProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, getPinnedCollapsedItem, useSidebar };
package/native/index.d.ts CHANGED
@@ -61,6 +61,7 @@ type SidebarItemType = {
61
61
  visibility?: string;
62
62
  disallowedIntegrationTypes?: Array<string>;
63
63
  isPinned?: boolean;
64
+ onPinToggle?: (e: MouseEvent<HTMLButtonElement>) => void;
64
65
  showBadge?: boolean;
65
66
  };
66
67
 
@@ -119,6 +120,8 @@ declare const SidebarTrigger: React.FC;
119
120
  interface SidebarGroupProps {
120
121
  label?: string;
121
122
  dataId?: string;
123
+ /** Docks this group to the bottom of the expanded sidebar (e.g. Settings, Billing). */
124
+ pinnedToBottom?: boolean;
122
125
  children: ReactNode;
123
126
  }
124
127
  declare const SidebarGroup: React.FC<SidebarGroupProps>;
@@ -139,6 +142,10 @@ interface SidebarMenuItemProps {
139
142
  dataId?: string;
140
143
  isActive?: SidebarLinkActiveCheck;
141
144
  isPinned?: boolean;
145
+ /** When provided, renders a pin/unpin toggle on hover (or always, when pinned). */
146
+ onPinToggle?: (e: React.MouseEvent<HTMLButtonElement>) => void;
147
+ /** When true on a pinned item, the leading pin glyph swaps to a drag handle on row hover (reorder affordance). */
148
+ dragHandle?: boolean;
142
149
  showBadge?: boolean;
143
150
  hasTooltip?: boolean;
144
151
  beta?: boolean;
@@ -185,4 +192,42 @@ interface SidebarCollapsedProps {
185
192
  }
186
193
  declare const SidebarCollapsed: React.FC<SidebarCollapsedProps>;
187
194
 
188
- export { Sidebar, SidebarChatButton, SidebarCollapsed, type SidebarCollapsedProps, SidebarContent, SidebarFooter, SidebarGroup, type SidebarItemType, type SidebarLinkActiveCheck, type SidebarLinkClickHandler, type SidebarLinkProps, SidebarMenu, SidebarMenuCollapsible, type SidebarMenuCollapsibleProps, SidebarMenuItem, type SidebarMenuItemProps, SidebarMenuSub, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, useSidebar };
195
+ interface SidebarPinnedItem {
196
+ key: string;
197
+ to: string;
198
+ label: ReactNode;
199
+ exact?: boolean;
200
+ }
201
+ interface SidebarPinnedListProps {
202
+ /** Ordered list of currently-pinned items. */
203
+ items: SidebarPinnedItem[];
204
+ /** Called when the user clicks the × on a pinned shortcut. */
205
+ onUnpin: (key: string) => void;
206
+ /**
207
+ * Called as the user drags to reorder. Omit to disable drag-and-drop —
208
+ * the leading icon stays as the static pin glyph.
209
+ */
210
+ onReorder?: (fromKey: string, toKey: string) => void;
211
+ /** Render 8px top/bottom spacers when items are present. Default true. */
212
+ spacers?: boolean;
213
+ }
214
+ /**
215
+ * Renders the pinned shortcuts list with pointer-event-based drag-and-drop
216
+ * reorder. Drag only initiates from the leading drag-handle area on each row.
217
+ *
218
+ * Pinning is fully opt-in: consumers who don't render this component get no
219
+ * pin UI. Consumers without a reorder backend can omit `onReorder`; the list
220
+ * stays static but unpinning still works.
221
+ */
222
+ declare const SidebarPinnedList: React.FC<SidebarPinnedListProps>;
223
+ /**
224
+ * Helper for the collapsed sidebar: returns a single `SidebarItemType` whose
225
+ * popover lists every pinned shortcut. Spread it into `collapsedItems` at the
226
+ * position you want the Pin button to appear. Returns `null` when no items.
227
+ */
228
+ declare const getPinnedCollapsedItem: (items: SidebarPinnedItem[], options?: {
229
+ label?: string;
230
+ dataId?: string;
231
+ }) => SidebarItemType | null;
232
+
233
+ export { Sidebar, SidebarChatButton, SidebarCollapsed, type SidebarCollapsedProps, SidebarContent, SidebarFooter, SidebarGroup, type SidebarItemType, type SidebarLinkActiveCheck, type SidebarLinkClickHandler, type SidebarLinkProps, SidebarMenu, SidebarMenuCollapsible, type SidebarMenuCollapsibleProps, SidebarMenuItem, type SidebarMenuItemProps, SidebarMenuSub, type SidebarPinnedItem, SidebarPinnedList, type SidebarPinnedListProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, getPinnedCollapsedItem, useSidebar };