@xsolla/xui-b2b-sidebar 0.150.0 → 0.151.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 +265 -140
- package/native/index.d.mts +46 -1
- package/native/index.d.ts +46 -1
- package/native/index.js +197 -5
- package/native/index.js.map +1 -1
- package/native/index.mjs +196 -6
- package/native/index.mjs.map +1 -1
- package/package.json +6 -6
- package/web/index.d.mts +46 -1
- package/web/index.d.ts +46 -1
- package/web/index.js +197 -5
- package/web/index.js.map +1 -1
- package/web/index.mjs +196 -6
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,43 @@
|
|
|
1
|
-
#
|
|
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
|
|
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
|
-
##
|
|
11
|
+
## Imports
|
|
14
12
|
|
|
15
|
-
|
|
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
|
-
|
|
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`, `
|
|
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
|
|
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>
|
|
131
|
-
|
|
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
|
|
329
|
+
### Item flags
|
|
135
330
|
|
|
136
331
|
```tsx
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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={[
|
|
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
|
-
##
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
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.
|
package/native/index.d.mts
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
|
-
|
|
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
|
-
|
|
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 };
|