@kushagradhawan/kookie-ui 0.1.12 → 0.1.13
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/components.css +135 -0
- package/dist/cjs/components/image.js.map +3 -3
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.d.ts.map +1 -1
- package/dist/cjs/components/index.js +1 -1
- package/dist/cjs/components/index.js.map +3 -3
- package/dist/cjs/components/sidebar.d.ts +71 -0
- package/dist/cjs/components/sidebar.d.ts.map +1 -0
- package/dist/cjs/components/sidebar.js +2 -0
- package/dist/cjs/components/sidebar.js.map +7 -0
- package/dist/cjs/components/sidebar.props.d.ts +48 -0
- package/dist/cjs/components/sidebar.props.d.ts.map +1 -0
- package/dist/cjs/components/sidebar.props.js +2 -0
- package/dist/cjs/components/sidebar.props.js.map +7 -0
- package/dist/esm/components/image.js.map +3 -3
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +1 -1
- package/dist/esm/components/index.js.map +3 -3
- package/dist/esm/components/sidebar.d.ts +71 -0
- package/dist/esm/components/sidebar.d.ts.map +1 -0
- package/dist/esm/components/sidebar.js +2 -0
- package/dist/esm/components/sidebar.js.map +7 -0
- package/dist/esm/components/sidebar.props.d.ts +48 -0
- package/dist/esm/components/sidebar.props.d.ts.map +1 -0
- package/dist/esm/components/sidebar.props.js +2 -0
- package/dist/esm/components/sidebar.props.js.map +7 -0
- package/package.json +2 -2
- package/src/components/image.tsx +1 -1
- package/src/components/index.css +1 -0
- package/src/components/index.tsx +1 -0
- package/src/components/sidebar.css +132 -0
- package/src/components/sidebar.props.tsx +38 -0
- package/src/components/sidebar.tsx +536 -0
- package/styles.css +135 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
|
|
6
|
+
import { sidebarPropDefs } from './sidebar.props.js';
|
|
7
|
+
|
|
8
|
+
import { IconButton } from './icon-button.js';
|
|
9
|
+
import { ScrollArea } from './scroll-area.js';
|
|
10
|
+
import { Separator } from './separator.js';
|
|
11
|
+
import { Theme, useThemeContext } from './theme.js';
|
|
12
|
+
import { ChevronDownIcon } from './icons.js';
|
|
13
|
+
import { extractProps } from '../helpers/extract-props.js';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// Import base menu styling and components
|
|
17
|
+
import { baseMenuItemPropDefs } from './_internal/base-menu.props.js';
|
|
18
|
+
import { Slot } from 'radix-ui';
|
|
19
|
+
|
|
20
|
+
import type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';
|
|
21
|
+
import type { GetPropDefTypes } from '../props/prop-def.js';
|
|
22
|
+
|
|
23
|
+
// Context for sidebar state
|
|
24
|
+
type SidebarContextProps = {
|
|
25
|
+
open: boolean;
|
|
26
|
+
setOpen: (open: boolean) => void;
|
|
27
|
+
collapsible: 'icon' | 'offcanvas' | 'none';
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
|
|
31
|
+
|
|
32
|
+
function useSidebar() {
|
|
33
|
+
const context = React.useContext(SidebarContext);
|
|
34
|
+
if (!context) {
|
|
35
|
+
throw new Error('useSidebar must be used within a Sidebar.Provider');
|
|
36
|
+
}
|
|
37
|
+
return context;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Provider component
|
|
41
|
+
interface SidebarProviderProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
42
|
+
defaultOpen?: boolean;
|
|
43
|
+
open?: boolean;
|
|
44
|
+
onOpenChange?: (open: boolean) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const SidebarProvider = React.forwardRef<HTMLDivElement, SidebarProviderProps>(
|
|
48
|
+
({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, children, ...props }, forwardedRef) => {
|
|
49
|
+
// Internal state for uncontrolled mode
|
|
50
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
|
|
51
|
+
|
|
52
|
+
// Use controlled state if provided, otherwise internal state
|
|
53
|
+
const open = openProp ?? internalOpen;
|
|
54
|
+
|
|
55
|
+
const setOpen = React.useCallback((value: boolean) => {
|
|
56
|
+
if (setOpenProp) {
|
|
57
|
+
setOpenProp(value); // Controlled mode
|
|
58
|
+
} else {
|
|
59
|
+
setInternalOpen(value); // Uncontrolled mode
|
|
60
|
+
}
|
|
61
|
+
}, [setOpenProp]);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div {...props} ref={forwardedRef}>
|
|
65
|
+
<SidebarContext.Provider value={{ open, setOpen, collapsible: 'icon' }}>
|
|
66
|
+
{children}
|
|
67
|
+
</SidebarContext.Provider>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
SidebarProvider.displayName = 'Sidebar.Provider';
|
|
73
|
+
|
|
74
|
+
// Root sidebar container
|
|
75
|
+
type SidebarRootOwnProps = GetPropDefTypes<typeof sidebarPropDefs>;
|
|
76
|
+
type SidebarRootElement = HTMLDivElement;
|
|
77
|
+
interface SidebarRootProps
|
|
78
|
+
extends ComponentPropsWithout<'div', RemovedProps>,
|
|
79
|
+
SidebarRootOwnProps {}
|
|
80
|
+
|
|
81
|
+
const SidebarRoot = React.forwardRef<SidebarRootElement, SidebarRootProps>(
|
|
82
|
+
(props, forwardedRef) => {
|
|
83
|
+
const themeContext = useThemeContext();
|
|
84
|
+
const { open } = useSidebar();
|
|
85
|
+
|
|
86
|
+
const {
|
|
87
|
+
size = sidebarPropDefs.size.default,
|
|
88
|
+
variant = sidebarPropDefs.variant.default,
|
|
89
|
+
side = sidebarPropDefs.side.default,
|
|
90
|
+
collapsible = sidebarPropDefs.collapsible.default,
|
|
91
|
+
floating: _floating = sidebarPropDefs.floating.default,
|
|
92
|
+
color,
|
|
93
|
+
highContrast = sidebarPropDefs.highContrast.default,
|
|
94
|
+
} = props;
|
|
95
|
+
|
|
96
|
+
const { className, children, ...rootProps } = extractProps(props, sidebarPropDefs);
|
|
97
|
+
const resolvedColor = color || themeContext.accentColor;
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
{...rootProps}
|
|
102
|
+
ref={forwardedRef}
|
|
103
|
+
data-accent-color={resolvedColor}
|
|
104
|
+
data-state={open ? 'expanded' : 'collapsed'}
|
|
105
|
+
data-side={side}
|
|
106
|
+
data-collapsible={collapsible}
|
|
107
|
+
className={classNames(
|
|
108
|
+
'rt-SidebarRoot',
|
|
109
|
+
className
|
|
110
|
+
)}
|
|
111
|
+
>
|
|
112
|
+
<Theme asChild>
|
|
113
|
+
<div
|
|
114
|
+
className={classNames('rt-SidebarContainer', `rt-variant-${variant}`, `rt-r-size-${size}`)}
|
|
115
|
+
data-accent-color={resolvedColor}
|
|
116
|
+
data-high-contrast={highContrast || undefined}
|
|
117
|
+
>
|
|
118
|
+
{children}
|
|
119
|
+
</div>
|
|
120
|
+
</Theme>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
SidebarRoot.displayName = 'Sidebar.Root';
|
|
126
|
+
|
|
127
|
+
// Sidebar content area
|
|
128
|
+
type SidebarContentElement = HTMLDivElement;
|
|
129
|
+
interface SidebarContentProps extends ComponentPropsWithout<'div', RemovedProps> {}
|
|
130
|
+
|
|
131
|
+
const SidebarContent = React.forwardRef<SidebarContentElement, SidebarContentProps>(
|
|
132
|
+
({ className, children, ...props }, forwardedRef) => (
|
|
133
|
+
<ScrollArea type="auto">
|
|
134
|
+
<div
|
|
135
|
+
{...props}
|
|
136
|
+
ref={forwardedRef}
|
|
137
|
+
className={classNames('rt-SidebarContent', 'rt-BaseMenuContent', className)}
|
|
138
|
+
>
|
|
139
|
+
{children}
|
|
140
|
+
</div>
|
|
141
|
+
</ScrollArea>
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
SidebarContent.displayName = 'Sidebar.Content';
|
|
145
|
+
|
|
146
|
+
// Sidebar header
|
|
147
|
+
type SidebarHeaderElement = HTMLDivElement;
|
|
148
|
+
interface SidebarHeaderProps extends ComponentPropsWithout<'div', RemovedProps> {}
|
|
149
|
+
|
|
150
|
+
const SidebarHeader = React.forwardRef<SidebarHeaderElement, SidebarHeaderProps>(
|
|
151
|
+
({ className, ...props }, forwardedRef) => (
|
|
152
|
+
<div
|
|
153
|
+
{...props}
|
|
154
|
+
ref={forwardedRef}
|
|
155
|
+
className={classNames('rt-SidebarHeader', 'rt-BaseMenuContent', className)}
|
|
156
|
+
/>
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
SidebarHeader.displayName = 'Sidebar.Header';
|
|
160
|
+
|
|
161
|
+
// Sidebar footer
|
|
162
|
+
type SidebarFooterElement = HTMLDivElement;
|
|
163
|
+
interface SidebarFooterProps extends ComponentPropsWithout<'div', RemovedProps> {}
|
|
164
|
+
|
|
165
|
+
const SidebarFooter = React.forwardRef<SidebarFooterElement, SidebarFooterProps>(
|
|
166
|
+
({ className, ...props }, forwardedRef) => (
|
|
167
|
+
<div
|
|
168
|
+
{...props}
|
|
169
|
+
ref={forwardedRef}
|
|
170
|
+
className={classNames('rt-SidebarFooter', 'rt-BaseMenuContent', className)}
|
|
171
|
+
/>
|
|
172
|
+
)
|
|
173
|
+
);
|
|
174
|
+
SidebarFooter.displayName = 'Sidebar.Footer';
|
|
175
|
+
|
|
176
|
+
// Sidebar trigger button
|
|
177
|
+
type SidebarTriggerElement = React.ElementRef<typeof IconButton>;
|
|
178
|
+
interface SidebarTriggerProps extends ComponentPropsWithout<typeof IconButton, RemovedProps> {}
|
|
179
|
+
|
|
180
|
+
const SidebarTrigger = React.forwardRef<SidebarTriggerElement, SidebarTriggerProps>(
|
|
181
|
+
({ onClick, ...props }, forwardedRef) => {
|
|
182
|
+
const { setOpen, open } = useSidebar();
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<IconButton
|
|
186
|
+
{...props}
|
|
187
|
+
ref={forwardedRef}
|
|
188
|
+
variant="ghost"
|
|
189
|
+
onClick={(event) => {
|
|
190
|
+
onClick?.(event);
|
|
191
|
+
setOpen(!open);
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
<ChevronDownIcon />
|
|
195
|
+
</IconButton>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
SidebarTrigger.displayName = 'Sidebar.Trigger';
|
|
200
|
+
|
|
201
|
+
// Main content area (pushes to make room for sidebar)
|
|
202
|
+
type SidebarInsetElement = HTMLDivElement;
|
|
203
|
+
interface SidebarInsetProps extends ComponentPropsWithout<'main', RemovedProps> {}
|
|
204
|
+
|
|
205
|
+
const SidebarInset = React.forwardRef<SidebarInsetElement, SidebarInsetProps>(
|
|
206
|
+
({ className, ...props }, forwardedRef) => (
|
|
207
|
+
<main
|
|
208
|
+
{...props}
|
|
209
|
+
ref={forwardedRef}
|
|
210
|
+
className={classNames('rt-SidebarInset', className)}
|
|
211
|
+
/>
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
SidebarInset.displayName = 'Sidebar.Inset';
|
|
215
|
+
|
|
216
|
+
// Create sidebar-specific menu components that don't require DropdownMenu context
|
|
217
|
+
interface SidebarLabelProps extends React.ComponentPropsWithoutRef<'div'> {}
|
|
218
|
+
|
|
219
|
+
const SidebarLabel = React.forwardRef<HTMLDivElement, SidebarLabelProps>(
|
|
220
|
+
({ className, ...props }, ref) => (
|
|
221
|
+
<div
|
|
222
|
+
ref={ref}
|
|
223
|
+
className={classNames('rt-BaseMenuLabel', className)}
|
|
224
|
+
{...props}
|
|
225
|
+
/>
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
SidebarLabel.displayName = 'Sidebar.Label';
|
|
229
|
+
|
|
230
|
+
type SidebarItemOwnProps = GetPropDefTypes<typeof baseMenuItemPropDefs>;
|
|
231
|
+
interface SidebarItemProps
|
|
232
|
+
extends ComponentPropsWithout<'div', RemovedProps>,
|
|
233
|
+
SidebarItemOwnProps {}
|
|
234
|
+
|
|
235
|
+
const SidebarItem = React.forwardRef<HTMLDivElement, SidebarItemProps>(
|
|
236
|
+
(props, ref) => {
|
|
237
|
+
const {
|
|
238
|
+
className,
|
|
239
|
+
children,
|
|
240
|
+
color = baseMenuItemPropDefs.color.default,
|
|
241
|
+
shortcut,
|
|
242
|
+
asChild = false,
|
|
243
|
+
onMouseEnter,
|
|
244
|
+
onMouseLeave,
|
|
245
|
+
onFocus,
|
|
246
|
+
onBlur,
|
|
247
|
+
...itemProps
|
|
248
|
+
} = props;
|
|
249
|
+
|
|
250
|
+
const [highlighted, setHighlighted] = React.useState(false);
|
|
251
|
+
|
|
252
|
+
const handleMouseEnter = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
253
|
+
setHighlighted(true);
|
|
254
|
+
onMouseEnter?.(e);
|
|
255
|
+
}, [onMouseEnter]);
|
|
256
|
+
|
|
257
|
+
const handleMouseLeave = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
258
|
+
setHighlighted(false);
|
|
259
|
+
onMouseLeave?.(e);
|
|
260
|
+
}, [onMouseLeave]);
|
|
261
|
+
|
|
262
|
+
const handleFocus = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
263
|
+
setHighlighted(true);
|
|
264
|
+
onFocus?.(e);
|
|
265
|
+
}, [onFocus]);
|
|
266
|
+
|
|
267
|
+
const handleBlur = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
268
|
+
setHighlighted(false);
|
|
269
|
+
onBlur?.(e);
|
|
270
|
+
}, [onBlur]);
|
|
271
|
+
|
|
272
|
+
if (asChild) {
|
|
273
|
+
return (
|
|
274
|
+
<Slot.Root
|
|
275
|
+
ref={ref}
|
|
276
|
+
data-accent-color={color}
|
|
277
|
+
data-highlighted={highlighted || undefined}
|
|
278
|
+
className={classNames('rt-reset', 'rt-BaseMenuItem', className)}
|
|
279
|
+
onMouseEnter={handleMouseEnter}
|
|
280
|
+
onMouseLeave={handleMouseLeave}
|
|
281
|
+
onFocus={handleFocus}
|
|
282
|
+
onBlur={handleBlur}
|
|
283
|
+
{...itemProps}
|
|
284
|
+
>
|
|
285
|
+
{children}
|
|
286
|
+
</Slot.Root>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div
|
|
292
|
+
ref={ref}
|
|
293
|
+
data-accent-color={color}
|
|
294
|
+
data-highlighted={highlighted || undefined}
|
|
295
|
+
className={classNames('rt-reset', 'rt-BaseMenuItem', className)}
|
|
296
|
+
onMouseEnter={handleMouseEnter}
|
|
297
|
+
onMouseLeave={handleMouseLeave}
|
|
298
|
+
onFocus={handleFocus}
|
|
299
|
+
onBlur={handleBlur}
|
|
300
|
+
tabIndex={0}
|
|
301
|
+
role="menuitem"
|
|
302
|
+
{...itemProps}
|
|
303
|
+
>
|
|
304
|
+
<Slot.Slottable>{children}</Slot.Slottable>
|
|
305
|
+
{shortcut && <div className="rt-BaseMenuShortcut">{shortcut}</div>}
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
SidebarItem.displayName = 'Sidebar.Item';
|
|
311
|
+
|
|
312
|
+
interface SidebarGroupProps extends React.ComponentPropsWithoutRef<'div'> {}
|
|
313
|
+
|
|
314
|
+
const SidebarGroup = React.forwardRef<HTMLDivElement, SidebarGroupProps>(
|
|
315
|
+
({ className, ...props }, ref) => (
|
|
316
|
+
<div
|
|
317
|
+
ref={ref}
|
|
318
|
+
className={classNames('rt-BaseMenuGroup', className)}
|
|
319
|
+
{...props}
|
|
320
|
+
/>
|
|
321
|
+
)
|
|
322
|
+
);
|
|
323
|
+
SidebarGroup.displayName = 'Sidebar.Group';
|
|
324
|
+
|
|
325
|
+
interface SidebarSeparatorProps extends React.ComponentPropsWithoutRef<'div'> {}
|
|
326
|
+
|
|
327
|
+
const SidebarSeparator = React.forwardRef<HTMLDivElement, SidebarSeparatorProps>(
|
|
328
|
+
({ className, ..._props }, ref) => (
|
|
329
|
+
<Separator
|
|
330
|
+
ref={ref}
|
|
331
|
+
className={classNames('rt-BaseMenuSeparator', className)}
|
|
332
|
+
/>
|
|
333
|
+
)
|
|
334
|
+
);
|
|
335
|
+
SidebarSeparator.displayName = 'Sidebar.Separator';
|
|
336
|
+
|
|
337
|
+
// Sidebar checkbox item with proper prop filtering
|
|
338
|
+
interface SidebarCheckboxItemProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
339
|
+
checked?: boolean;
|
|
340
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
341
|
+
color?: string;
|
|
342
|
+
shortcut?: string;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const SidebarCheckboxItem = React.forwardRef<HTMLDivElement, SidebarCheckboxItemProps>(
|
|
346
|
+
({
|
|
347
|
+
className,
|
|
348
|
+
checked,
|
|
349
|
+
onCheckedChange,
|
|
350
|
+
children,
|
|
351
|
+
color,
|
|
352
|
+
shortcut,
|
|
353
|
+
onMouseEnter,
|
|
354
|
+
onMouseLeave,
|
|
355
|
+
onFocus,
|
|
356
|
+
onBlur,
|
|
357
|
+
onClick,
|
|
358
|
+
...props
|
|
359
|
+
}, ref) => {
|
|
360
|
+
const [highlighted, setHighlighted] = React.useState(false);
|
|
361
|
+
|
|
362
|
+
const handleMouseEnter = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
363
|
+
setHighlighted(true);
|
|
364
|
+
onMouseEnter?.(e);
|
|
365
|
+
}, [onMouseEnter]);
|
|
366
|
+
|
|
367
|
+
const handleMouseLeave = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
368
|
+
setHighlighted(false);
|
|
369
|
+
onMouseLeave?.(e);
|
|
370
|
+
}, [onMouseLeave]);
|
|
371
|
+
|
|
372
|
+
const handleFocus = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
373
|
+
setHighlighted(true);
|
|
374
|
+
onFocus?.(e);
|
|
375
|
+
}, [onFocus]);
|
|
376
|
+
|
|
377
|
+
const handleBlur = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
378
|
+
setHighlighted(false);
|
|
379
|
+
onBlur?.(e);
|
|
380
|
+
}, [onBlur]);
|
|
381
|
+
|
|
382
|
+
const handleClick = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
383
|
+
onCheckedChange?.(!checked);
|
|
384
|
+
onClick?.(e);
|
|
385
|
+
}, [checked, onCheckedChange, onClick]);
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<div
|
|
389
|
+
ref={ref}
|
|
390
|
+
data-accent-color={color}
|
|
391
|
+
data-highlighted={highlighted || undefined}
|
|
392
|
+
className={classNames(
|
|
393
|
+
'rt-reset',
|
|
394
|
+
'rt-BaseMenuItem',
|
|
395
|
+
'rt-BaseMenuCheckboxItem',
|
|
396
|
+
className
|
|
397
|
+
)}
|
|
398
|
+
onClick={handleClick}
|
|
399
|
+
onKeyDown={(e) => {
|
|
400
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
401
|
+
e.preventDefault();
|
|
402
|
+
handleClick(e as any);
|
|
403
|
+
}
|
|
404
|
+
}}
|
|
405
|
+
onMouseEnter={handleMouseEnter}
|
|
406
|
+
onMouseLeave={handleMouseLeave}
|
|
407
|
+
onFocus={handleFocus}
|
|
408
|
+
onBlur={handleBlur}
|
|
409
|
+
tabIndex={0}
|
|
410
|
+
role="menuitemcheckbox"
|
|
411
|
+
aria-checked={checked}
|
|
412
|
+
{...props}
|
|
413
|
+
>
|
|
414
|
+
<Slot.Slottable>{children}</Slot.Slottable>
|
|
415
|
+
{checked && <div className="rt-BaseMenuItemIndicator">✓</div>}
|
|
416
|
+
{shortcut && <div className="rt-BaseMenuShortcut">{shortcut}</div>}
|
|
417
|
+
</div>
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
SidebarCheckboxItem.displayName = 'Sidebar.CheckboxItem';
|
|
422
|
+
|
|
423
|
+
// Sidebar radio group with proper prop filtering
|
|
424
|
+
interface SidebarRadioGroupProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
425
|
+
value?: string;
|
|
426
|
+
onValueChange?: (value: string) => void;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const SidebarRadioGroup = React.forwardRef<HTMLDivElement, SidebarRadioGroupProps>(
|
|
430
|
+
({ className, value, onValueChange, children, ...props }, ref) => (
|
|
431
|
+
<div
|
|
432
|
+
ref={ref}
|
|
433
|
+
className={classNames('rt-BaseMenuGroup', className)}
|
|
434
|
+
{...props}
|
|
435
|
+
>
|
|
436
|
+
{children}
|
|
437
|
+
</div>
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
SidebarRadioGroup.displayName = 'Sidebar.RadioGroup';
|
|
441
|
+
|
|
442
|
+
// Sidebar radio item with proper prop filtering
|
|
443
|
+
interface SidebarRadioItemProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
444
|
+
value?: string;
|
|
445
|
+
color?: string;
|
|
446
|
+
shortcut?: string;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const SidebarRadioItem = React.forwardRef<HTMLDivElement, SidebarRadioItemProps>(
|
|
450
|
+
({
|
|
451
|
+
className,
|
|
452
|
+
value,
|
|
453
|
+
children,
|
|
454
|
+
color,
|
|
455
|
+
shortcut,
|
|
456
|
+
onMouseEnter,
|
|
457
|
+
onMouseLeave,
|
|
458
|
+
onFocus,
|
|
459
|
+
onBlur,
|
|
460
|
+
...props
|
|
461
|
+
}, ref) => {
|
|
462
|
+
const [highlighted, setHighlighted] = React.useState(false);
|
|
463
|
+
|
|
464
|
+
const handleMouseEnter = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
465
|
+
setHighlighted(true);
|
|
466
|
+
onMouseEnter?.(e);
|
|
467
|
+
}, [onMouseEnter]);
|
|
468
|
+
|
|
469
|
+
const handleMouseLeave = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
470
|
+
setHighlighted(false);
|
|
471
|
+
onMouseLeave?.(e);
|
|
472
|
+
}, [onMouseLeave]);
|
|
473
|
+
|
|
474
|
+
const handleFocus = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
475
|
+
setHighlighted(true);
|
|
476
|
+
onFocus?.(e);
|
|
477
|
+
}, [onFocus]);
|
|
478
|
+
|
|
479
|
+
const handleBlur = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
480
|
+
setHighlighted(false);
|
|
481
|
+
onBlur?.(e);
|
|
482
|
+
}, [onBlur]);
|
|
483
|
+
|
|
484
|
+
return (
|
|
485
|
+
<div
|
|
486
|
+
ref={ref}
|
|
487
|
+
data-accent-color={color}
|
|
488
|
+
data-highlighted={highlighted || undefined}
|
|
489
|
+
className={classNames('rt-reset', 'rt-BaseMenuItem', className)}
|
|
490
|
+
onMouseEnter={handleMouseEnter}
|
|
491
|
+
onMouseLeave={handleMouseLeave}
|
|
492
|
+
onFocus={handleFocus}
|
|
493
|
+
onBlur={handleBlur}
|
|
494
|
+
tabIndex={0}
|
|
495
|
+
role="menuitemradio"
|
|
496
|
+
aria-checked={false}
|
|
497
|
+
{...props}
|
|
498
|
+
>
|
|
499
|
+
<Slot.Slottable>{children}</Slot.Slottable>
|
|
500
|
+
{shortcut && <div className="rt-BaseMenuShortcut">{shortcut}</div>}
|
|
501
|
+
</div>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
SidebarRadioItem.displayName = 'Sidebar.RadioItem';
|
|
506
|
+
|
|
507
|
+
// Export all components
|
|
508
|
+
export {
|
|
509
|
+
SidebarProvider as Provider,
|
|
510
|
+
SidebarRoot as Root,
|
|
511
|
+
SidebarContent as Content,
|
|
512
|
+
SidebarHeader as Header,
|
|
513
|
+
SidebarFooter as Footer,
|
|
514
|
+
SidebarTrigger as Trigger,
|
|
515
|
+
SidebarInset as Inset,
|
|
516
|
+
// Re-export DropdownMenu components as sidebar menu components
|
|
517
|
+
SidebarLabel as Label,
|
|
518
|
+
SidebarItem as Item,
|
|
519
|
+
SidebarGroup as Group,
|
|
520
|
+
SidebarSeparator as Separator,
|
|
521
|
+
SidebarCheckboxItem as CheckboxItem,
|
|
522
|
+
SidebarRadioGroup as RadioGroup,
|
|
523
|
+
SidebarRadioItem as RadioItem,
|
|
524
|
+
// Export hook
|
|
525
|
+
useSidebar,
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
export type {
|
|
529
|
+
SidebarProviderProps as ProviderProps,
|
|
530
|
+
SidebarRootProps as RootProps,
|
|
531
|
+
SidebarContentProps as ContentProps,
|
|
532
|
+
SidebarHeaderProps as HeaderProps,
|
|
533
|
+
SidebarFooterProps as FooterProps,
|
|
534
|
+
SidebarTriggerProps as TriggerProps,
|
|
535
|
+
SidebarInsetProps as InsetProps,
|
|
536
|
+
};
|
package/styles.css
CHANGED
|
@@ -14123,6 +14123,141 @@
|
|
|
14123
14123
|
--separator-size: 100%;
|
|
14124
14124
|
}
|
|
14125
14125
|
}
|
|
14126
|
+
.rt-SidebarRoot {
|
|
14127
|
+
--sidebar-width: 16rem;
|
|
14128
|
+
--sidebar-width-icon: 3rem;
|
|
14129
|
+
position: fixed;
|
|
14130
|
+
top: 0;
|
|
14131
|
+
bottom: 0;
|
|
14132
|
+
left: 0;
|
|
14133
|
+
z-index: 30;
|
|
14134
|
+
width: var(--sidebar-width);
|
|
14135
|
+
transition: transform 0.2s ease, width 0.2s ease;
|
|
14136
|
+
}
|
|
14137
|
+
.rt-SidebarContainer {
|
|
14138
|
+
display: flex;
|
|
14139
|
+
flex-direction: column;
|
|
14140
|
+
height: 100%;
|
|
14141
|
+
width: 100%;
|
|
14142
|
+
background-color: var(--color-panel-solid);
|
|
14143
|
+
border-right: 1px solid var(--gray-a5);
|
|
14144
|
+
}
|
|
14145
|
+
.rt-SidebarHeader {
|
|
14146
|
+
display: flex;
|
|
14147
|
+
align-items: center;
|
|
14148
|
+
padding: var(--space-3);
|
|
14149
|
+
border-bottom: 1px solid var(--gray-a5);
|
|
14150
|
+
}
|
|
14151
|
+
.rt-SidebarContent {
|
|
14152
|
+
flex: 1;
|
|
14153
|
+
padding: var(--space-2);
|
|
14154
|
+
min-height: 0;
|
|
14155
|
+
}
|
|
14156
|
+
.rt-SidebarFooter {
|
|
14157
|
+
padding: var(--space-3);
|
|
14158
|
+
border-top: 1px solid var(--gray-a5);
|
|
14159
|
+
}
|
|
14160
|
+
.rt-SidebarInset {
|
|
14161
|
+
flex: 1;
|
|
14162
|
+
margin-left: var(--sidebar-width);
|
|
14163
|
+
transition: margin-left 0.2s ease;
|
|
14164
|
+
}
|
|
14165
|
+
.rt-SidebarRoot:where(.rt-r-size-1) {
|
|
14166
|
+
--sidebar-width: 14rem;
|
|
14167
|
+
--sidebar-width-icon: 2.5rem;
|
|
14168
|
+
}
|
|
14169
|
+
.rt-SidebarRoot:where(.rt-r-size-3) {
|
|
14170
|
+
--sidebar-width: 18rem;
|
|
14171
|
+
--sidebar-width-icon: 3.5rem;
|
|
14172
|
+
}
|
|
14173
|
+
@media (min-width: 520px) {
|
|
14174
|
+
.rt-SidebarRoot:where(.xs\:rt-r-size-1) {
|
|
14175
|
+
--sidebar-width: 14rem;
|
|
14176
|
+
--sidebar-width-icon: 2.5rem;
|
|
14177
|
+
}
|
|
14178
|
+
.rt-SidebarRoot:where(.xs\:rt-r-size-3) {
|
|
14179
|
+
--sidebar-width: 18rem;
|
|
14180
|
+
--sidebar-width-icon: 3.5rem;
|
|
14181
|
+
}
|
|
14182
|
+
}
|
|
14183
|
+
@media (min-width: 768px) {
|
|
14184
|
+
.rt-SidebarRoot:where(.sm\:rt-r-size-1) {
|
|
14185
|
+
--sidebar-width: 14rem;
|
|
14186
|
+
--sidebar-width-icon: 2.5rem;
|
|
14187
|
+
}
|
|
14188
|
+
.rt-SidebarRoot:where(.sm\:rt-r-size-3) {
|
|
14189
|
+
--sidebar-width: 18rem;
|
|
14190
|
+
--sidebar-width-icon: 3.5rem;
|
|
14191
|
+
}
|
|
14192
|
+
}
|
|
14193
|
+
@media (min-width: 1024px) {
|
|
14194
|
+
.rt-SidebarRoot:where(.md\:rt-r-size-1) {
|
|
14195
|
+
--sidebar-width: 14rem;
|
|
14196
|
+
--sidebar-width-icon: 2.5rem;
|
|
14197
|
+
}
|
|
14198
|
+
.rt-SidebarRoot:where(.md\:rt-r-size-3) {
|
|
14199
|
+
--sidebar-width: 18rem;
|
|
14200
|
+
--sidebar-width-icon: 3.5rem;
|
|
14201
|
+
}
|
|
14202
|
+
}
|
|
14203
|
+
@media (min-width: 1280px) {
|
|
14204
|
+
.rt-SidebarRoot:where(.lg\:rt-r-size-1) {
|
|
14205
|
+
--sidebar-width: 14rem;
|
|
14206
|
+
--sidebar-width-icon: 2.5rem;
|
|
14207
|
+
}
|
|
14208
|
+
.rt-SidebarRoot:where(.lg\:rt-r-size-3) {
|
|
14209
|
+
--sidebar-width: 18rem;
|
|
14210
|
+
--sidebar-width-icon: 3.5rem;
|
|
14211
|
+
}
|
|
14212
|
+
}
|
|
14213
|
+
@media (min-width: 1640px) {
|
|
14214
|
+
.rt-SidebarRoot:where(.xl\:rt-r-size-1) {
|
|
14215
|
+
--sidebar-width: 14rem;
|
|
14216
|
+
--sidebar-width-icon: 2.5rem;
|
|
14217
|
+
}
|
|
14218
|
+
.rt-SidebarRoot:where(.xl\:rt-r-size-3) {
|
|
14219
|
+
--sidebar-width: 18rem;
|
|
14220
|
+
--sidebar-width-icon: 3.5rem;
|
|
14221
|
+
}
|
|
14222
|
+
}
|
|
14223
|
+
.rt-SidebarHeader:where(.rt-r-size-1) {
|
|
14224
|
+
padding: var(--space-2);
|
|
14225
|
+
}
|
|
14226
|
+
.rt-SidebarHeader:where(.rt-r-size-3) {
|
|
14227
|
+
padding: var(--space-4);
|
|
14228
|
+
}
|
|
14229
|
+
.rt-SidebarFooter:where(.rt-r-size-1) {
|
|
14230
|
+
padding: var(--space-2);
|
|
14231
|
+
}
|
|
14232
|
+
.rt-SidebarFooter:where(.rt-r-size-3) {
|
|
14233
|
+
padding: var(--space-4);
|
|
14234
|
+
}
|
|
14235
|
+
.rt-SidebarContent:where(.rt-r-size-1) {
|
|
14236
|
+
padding: var(--space-1);
|
|
14237
|
+
}
|
|
14238
|
+
.rt-SidebarContent:where(.rt-r-size-3) {
|
|
14239
|
+
padding: var(--space-3);
|
|
14240
|
+
}
|
|
14241
|
+
.rt-SidebarContainer:where(.rt-variant-classic) {
|
|
14242
|
+
background-color: var(--color-surface);
|
|
14243
|
+
border: 1px solid var(--gray-a5);
|
|
14244
|
+
}
|
|
14245
|
+
.rt-SidebarContainer:where(.rt-variant-ghost) {
|
|
14246
|
+
background-color: transparent;
|
|
14247
|
+
border: none;
|
|
14248
|
+
}
|
|
14249
|
+
.rt-SidebarContent :where(.rt-BaseMenuItem) {
|
|
14250
|
+
border-radius: var(--radius-2);
|
|
14251
|
+
margin-bottom: var(--space-1);
|
|
14252
|
+
}
|
|
14253
|
+
@media (max-width: 768px) {
|
|
14254
|
+
.rt-SidebarRoot {
|
|
14255
|
+
display: none;
|
|
14256
|
+
}
|
|
14257
|
+
.rt-SidebarInset {
|
|
14258
|
+
margin-left: 0;
|
|
14259
|
+
}
|
|
14260
|
+
}
|
|
14126
14261
|
.rt-SliderRoot {
|
|
14127
14262
|
--slider-thumb-size: calc(var(--slider-track-size) + var(--space-1));
|
|
14128
14263
|
position: relative;
|