@flikk/ui 1.0.0-beta.13 → 1.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/components/ai/PromptInput/PromptInput.js +1 -1
  2. package/dist/components/ai/PromptInput/VoiceRecorder.js +1 -1
  3. package/dist/components/ai/PromptSuggestions/PromptSuggestion.theme.js +1 -1
  4. package/dist/components/charts/FunnelChart/FunnelChart.js +2 -2
  5. package/dist/components/core/Badge/Badge.js +3 -3
  6. package/dist/components/core/Badge/Badge.theme.js +5 -5
  7. package/dist/components/core/Badge/Badge.types.d.ts +1 -1
  8. package/dist/components/core/Button/Button.ripple.js +1 -1
  9. package/dist/components/core/Button/Button.theme.d.ts +1 -1
  10. package/dist/components/core/Button/Button.theme.js +3 -3
  11. package/dist/components/core/Button/Button.types.d.ts +1 -1
  12. package/dist/components/core/Calendar/CalendarMini/CalendarMini.js +2 -2
  13. package/dist/components/core/Drawer/DrawerHeader.js +1 -1
  14. package/dist/components/core/Link/Link.js +1 -1
  15. package/dist/components/core/Modal/ModalHeader.js +1 -1
  16. package/dist/components/core/NavItem/NavItem.js +100 -52
  17. package/dist/components/core/NavItem/NavItem.theme.js +6 -3
  18. package/dist/components/core/NavItem/NavItem.types.d.ts +44 -30
  19. package/dist/components/core/NavItem/index.d.ts +1 -1
  20. package/dist/components/core/Pill/Pill.theme.js +1 -1
  21. package/dist/components/core/Sidebar/Sidebar.d.ts +11 -1
  22. package/dist/components/core/Sidebar/Sidebar.js +49 -16
  23. package/dist/components/core/Sidebar/Sidebar.theme.js +9 -7
  24. package/dist/components/core/Sidebar/Sidebar.types.d.ts +57 -0
  25. package/dist/components/core/Sidebar/SidebarContent.js +3 -1
  26. package/dist/components/core/Sidebar/SidebarContext.d.ts +14 -0
  27. package/dist/components/core/Sidebar/SidebarContext.js +63 -11
  28. package/dist/components/core/Sidebar/SidebarFooter.js +3 -1
  29. package/dist/components/core/Sidebar/SidebarHeader.js +6 -2
  30. package/dist/components/core/Sidebar/SidebarMobileTrigger.d.ts +3 -0
  31. package/dist/components/core/Sidebar/SidebarMobileTrigger.js +28 -0
  32. package/dist/components/core/Sidebar/SidebarNav.js +3 -2
  33. package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -2
  34. package/dist/components/core/Sidebar/SidebarToggle.js +1 -1
  35. package/dist/components/core/Sidebar/SidebarUserProfile.d.ts +3 -0
  36. package/dist/components/core/Sidebar/SidebarUserProfile.js +35 -0
  37. package/dist/components/core/Sidebar/index.d.ts +3 -1
  38. package/dist/components/core/Tabs/Tabs.theme.d.ts +1 -1
  39. package/dist/components/core/Tabs/Tabs.theme.js +3 -3
  40. package/dist/components/core/index.js +2 -0
  41. package/dist/components/data-display/GanttChart/GanttToolbar.js +1 -1
  42. package/dist/components/forms/RichTextEditor/RichTextEditor.js +10 -10
  43. package/dist/index.js +2 -0
  44. package/dist/styles.css +1 -1
  45. package/package.json +1 -1
  46. package/src/global.scss +1 -0
  47. package/src/styles/theme.css +48 -48
@@ -1,33 +1,35 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React__default, { useMemo } from 'react';
3
3
  import { cn } from '../../../utils/cn.js';
4
- import { motion } from 'motion/react';
4
+ import { motion, AnimatePresence } from 'motion/react';
5
5
  import { sidebarTheme } from './Sidebar.theme.js';
6
6
  import { useSidebarContext, SidebarProvider } from './SidebarContext.js';
7
+ import { SidebarHeader } from './SidebarHeader.js';
8
+ import { SidebarContent } from './SidebarContent.js';
9
+ import { SidebarFooter } from './SidebarFooter.js';
10
+ import { SidebarNav } from './SidebarNav.js';
11
+ import { SidebarNavGroup } from './SidebarNavGroup.js';
12
+ import { SidebarSubmenu } from './SidebarSubmenu.js';
13
+ import { SidebarToggle } from './SidebarToggle.js';
14
+ import { SidebarMobileTrigger } from './SidebarMobileTrigger.js';
15
+ import { SidebarUserProfile } from './SidebarUserProfile.js';
7
16
 
8
17
  const SIDEBAR_TRANSITION = {
9
18
  type: "spring",
10
19
  stiffness: 300,
11
20
  damping: 30,
12
21
  };
13
- const SidebarContainer = React__default.forwardRef(({ side = "left", variant = "sidebar", width, children, className, theme: customTheme = {}, ...props }, ref) => {
22
+ const SidebarDesktop = React__default.forwardRef(({ side = "left", variant = "sidebar", width, children, className, theme: customTheme = {}, ...props }, ref) => {
14
23
  var _a, _b, _c;
15
24
  const context = useSidebarContext();
16
25
  const collapsed = (_a = context === null || context === void 0 ? void 0 : context.collapsed) !== null && _a !== void 0 ? _a : false;
17
- // Default widths
18
26
  const expandedWidth = (width === null || width === void 0 ? void 0 : width.expanded) || ((_b = context === null || context === void 0 ? void 0 : context.width) === null || _b === void 0 ? void 0 : _b.expanded) || 280;
19
- const collapsedWidth = (width === null || width === void 0 ? void 0 : width.collapsed) || ((_c = context === null || context === void 0 ? void 0 : context.width) === null || _c === void 0 ? void 0 : _c.collapsed) || 80;
27
+ const collapsedWidth = (width === null || width === void 0 ? void 0 : width.collapsed) || ((_c = context === null || context === void 0 ? void 0 : context.width) === null || _c === void 0 ? void 0 : _c.collapsed) || 64;
20
28
  const theme = useMemo(() => ({
21
29
  ...sidebarTheme,
22
30
  ...customTheme,
23
- variants: {
24
- ...sidebarTheme.variants,
25
- ...customTheme.variants,
26
- },
27
- sides: {
28
- ...sidebarTheme.sides,
29
- ...customTheme.sides,
30
- },
31
+ variants: { ...sidebarTheme.variants, ...customTheme.variants },
32
+ sides: { ...sidebarTheme.sides, ...customTheme.sides },
31
33
  }), [customTheme]);
32
34
  const sidebarClasses = cn(theme.baseStyle, theme.variants[variant], theme.sides[side], className);
33
35
  const animateValue = useMemo(() => ({
@@ -36,9 +38,40 @@ const SidebarContainer = React__default.forwardRef(({ side = "left", variant = "
36
38
  }), [collapsed, collapsedWidth, expandedWidth]);
37
39
  return (jsx(motion.div, { ref: ref, className: sidebarClasses, "data-side": side, "data-variant": variant, "data-collapsed": collapsed, initial: false, animate: animateValue, transition: SIDEBAR_TRANSITION, ...props, children: children }));
38
40
  });
39
- const Sidebar = React__default.forwardRef(({ side = "left", variant = "sidebar", defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, width, children, className, theme: customTheme = {}, ...props }, ref) => {
40
- return (jsx(SidebarProvider, { defaultCollapsed: defaultCollapsed, collapsed: controlledCollapsed, onCollapsedChange: onCollapsedChange, width: width, side: side, variant: variant, children: jsx(SidebarContainer, { ref: ref, className: className, theme: customTheme, side: side, variant: variant, width: width, ...props, children: children }) }));
41
+ SidebarDesktop.displayName = "SidebarDesktop";
42
+ const SidebarMobile = ({ children, className, theme: customTheme = {} }) => {
43
+ const context = useSidebarContext();
44
+ if (!context)
45
+ return null;
46
+ const { isMobileOpen, setMobileOpen } = context;
47
+ const theme = useMemo(() => ({
48
+ ...sidebarTheme,
49
+ ...customTheme,
50
+ }), [customTheme]);
51
+ return (jsx(AnimatePresence, { children: isMobileOpen && (jsxs(Fragment, { children: [jsx(motion.div, { className: cn(theme.mobileBackdrop), initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, onClick: () => setMobileOpen(false), "aria-hidden": "true" }), jsx(motion.div, { className: cn(theme.mobileOverlay, className), initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 20 }, transition: { type: "spring", stiffness: 400, damping: 35 }, role: "dialog", "aria-modal": "true", "aria-label": "Navigation menu", children: children })] })) }));
52
+ };
53
+ const SidebarContainer = React__default.forwardRef((props, ref) => {
54
+ const context = useSidebarContext();
55
+ if (context === null || context === void 0 ? void 0 : context.isMobile) {
56
+ return (jsx(SidebarMobile, { className: props.className, theme: props.theme, children: props.children }));
57
+ }
58
+ return jsx(SidebarDesktop, { ref: ref, ...props });
59
+ });
60
+ SidebarContainer.displayName = "SidebarContainer";
61
+ const SidebarRoot = React__default.forwardRef(({ side = "left", variant = "sidebar", defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, mobileBreakpoint, mobileOpen, onMobileOpenChange, width, children, className, theme: customTheme = {}, ...props }, ref) => {
62
+ return (jsx(SidebarProvider, { defaultCollapsed: defaultCollapsed, collapsed: controlledCollapsed, onCollapsedChange: onCollapsedChange, mobileBreakpoint: mobileBreakpoint, mobileOpen: mobileOpen, onMobileOpenChange: onMobileOpenChange, width: width, side: side, variant: variant, children: jsx(SidebarContainer, { ref: ref, className: className, theme: customTheme, side: side, variant: variant, width: width, ...props, children: children }) }));
63
+ });
64
+ SidebarRoot.displayName = "Sidebar";
65
+ const Sidebar = Object.assign(SidebarRoot, {
66
+ Header: SidebarHeader,
67
+ Content: SidebarContent,
68
+ Footer: SidebarFooter,
69
+ Nav: SidebarNav,
70
+ NavGroup: SidebarNavGroup,
71
+ Submenu: SidebarSubmenu,
72
+ Toggle: SidebarToggle,
73
+ MobileTrigger: SidebarMobileTrigger,
74
+ UserProfile: SidebarUserProfile,
41
75
  });
42
- Sidebar.displayName = "Sidebar";
43
76
 
44
77
  export { Sidebar };
@@ -1,16 +1,18 @@
1
1
  const sidebarTheme = {
2
- baseStyle: 'flex flex-col h-full bg-white border-r border-gray-200 overflow-hidden transition-all duration-300 ease-in-out',
2
+ baseStyle: 'flex flex-col h-full bg-[var(--color-background)] overflow-hidden',
3
3
  variants: {
4
4
  sidebar: 'relative',
5
- floating: 'rounded-[var(--radius-base)] shadow-lg border border-gray-200',
6
- inset: 'rounded-[var(--radius-base)] bg-gray-50 border border-gray-200',
5
+ floating: 'rounded-[var(--radius-base)] shadow-lg border border-[var(--color-border)]',
6
+ inset: 'rounded-[var(--radius-base)] bg-[var(--color-background-secondary)] border border-[var(--color-border)]',
7
7
  },
8
8
  sides: {
9
- left: 'border-r',
10
- right: 'border-l',
9
+ left: 'border-r border-[var(--color-border)]',
10
+ right: 'border-l border-[var(--color-border)]',
11
11
  },
12
- collapsed: 'w-20',
13
- expanded: 'w-[280px]', // Using arbitrary value since 280px is not a standard Tailwind width
12
+ collapsed: 'w-16',
13
+ expanded: 'w-[280px]',
14
+ mobileOverlay: 'fixed inset-0 z-50 flex flex-col bg-[var(--color-background)]',
15
+ mobileBackdrop: 'fixed inset-0 z-40 bg-black/50 backdrop-blur-sm',
14
16
  };
15
17
 
16
18
  export { sidebarTheme };
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  export type SidebarSide = 'left' | 'right';
3
3
  export type SidebarVariant = 'sidebar' | 'floating' | 'inset';
4
+ export type SidebarUserStatus = 'online' | 'offline' | 'away' | 'busy';
4
5
  export interface SidebarContextValue {
5
6
  collapsed: boolean;
6
7
  setCollapsed: (collapsed: boolean) => void;
@@ -11,6 +12,11 @@ export interface SidebarContextValue {
11
12
  };
12
13
  side: SidebarSide;
13
14
  variant: SidebarVariant;
15
+ /** Whether the sidebar is currently showing as mobile overlay */
16
+ isMobileOpen: boolean;
17
+ setMobileOpen: (open: boolean) => void;
18
+ /** Whether we're below the mobile breakpoint */
19
+ isMobile: boolean;
14
20
  }
15
21
  export interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
16
22
  /**
@@ -43,6 +49,19 @@ export interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
43
49
  expanded?: number;
44
50
  collapsed?: number;
45
51
  };
52
+ /**
53
+ * Mobile breakpoint in pixels. Below this, sidebar becomes a full-screen overlay.
54
+ * @default 1024
55
+ */
56
+ mobileBreakpoint?: number;
57
+ /**
58
+ * Controlled mobile open state
59
+ */
60
+ mobileOpen?: boolean;
61
+ /**
62
+ * Callback when mobile open state changes
63
+ */
64
+ onMobileOpenChange?: (open: boolean) => void;
46
65
  /**
47
66
  * Children components
48
67
  */
@@ -78,6 +97,12 @@ export interface SidebarFooterProps extends React.HTMLAttributes<HTMLDivElement>
78
97
  children: ReactNode;
79
98
  }
80
99
  export interface SidebarNavProps extends React.HTMLAttributes<HTMLDivElement> {
100
+ /**
101
+ * When true, only one submenu can be open at a time within this nav.
102
+ * Opening a new submenu auto-closes the previously open one.
103
+ * @default false
104
+ */
105
+ accordion?: boolean;
81
106
  children: ReactNode;
82
107
  }
83
108
  export interface SidebarNavGroupProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -105,6 +130,34 @@ export interface SidebarToggleProps extends React.HTMLAttributes<HTMLButtonEleme
105
130
  */
106
131
  collapseIcon?: ReactNode;
107
132
  }
133
+ export interface SidebarMobileTriggerProps extends React.HTMLAttributes<HTMLButtonElement> {
134
+ /**
135
+ * Custom icon for the trigger
136
+ */
137
+ icon?: ReactNode;
138
+ }
139
+ export interface SidebarUserProfileProps extends React.HTMLAttributes<HTMLDivElement> {
140
+ /**
141
+ * User display name
142
+ */
143
+ name: string;
144
+ /**
145
+ * User email
146
+ */
147
+ email?: string;
148
+ /**
149
+ * Avatar image URL
150
+ */
151
+ avatar?: string;
152
+ /**
153
+ * Online status indicator
154
+ */
155
+ status?: SidebarUserStatus;
156
+ /**
157
+ * Content to render when the profile is clicked (e.g. dropdown menu)
158
+ */
159
+ dropdownContent?: ReactNode;
160
+ }
108
161
  export interface SidebarTheme {
109
162
  baseStyle: string;
110
163
  variants: {
@@ -115,6 +168,8 @@ export interface SidebarTheme {
115
168
  };
116
169
  collapsed: string;
117
170
  expanded: string;
171
+ mobileOverlay: string;
172
+ mobileBackdrop: string;
118
173
  }
119
174
  export interface SidebarThemeOverrides {
120
175
  baseStyle?: string;
@@ -126,4 +181,6 @@ export interface SidebarThemeOverrides {
126
181
  }>;
127
182
  collapsed?: string;
128
183
  expanded?: string;
184
+ mobileOverlay?: string;
185
+ mobileBackdrop?: string;
129
186
  }
@@ -6,7 +6,9 @@ import { useSidebarContext } from './SidebarContext.js';
6
6
  const SidebarContent = React__default.forwardRef(({ children, className, ...props }, ref) => {
7
7
  const sidebarContext = useSidebarContext();
8
8
  const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
9
- return (jsx("div", { ref: ref, className: cn("flex-1 overflow-y-auto overflow-x-hidden py-2 transition-all duration-200", collapsed ? "px-2" : "px-3", className), ...props, children: children }));
9
+ const isMobile = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.isMobile) || false;
10
+ const isCollapsedView = collapsed && !isMobile;
11
+ return (jsx("div", { ref: ref, className: cn("flex-1 overflow-y-auto overflow-x-hidden py-2 transition-all duration-200", isCollapsedView ? "px-2" : "px-3", className), ...props, children: children }));
10
12
  });
11
13
  SidebarContent.displayName = "SidebarContent";
12
14
 
@@ -1,11 +1,25 @@
1
1
  import React from 'react';
2
2
  import { SidebarContextValue } from './Sidebar.types';
3
3
  export declare const useSidebarContext: () => SidebarContextValue | null;
4
+ /** Accordion nav context: allows SidebarNav to coordinate which submenu is open */
5
+ interface AccordionNavContextValue {
6
+ openId: string | null;
7
+ register: (id: string) => void;
8
+ unregister: (id: string) => void;
9
+ }
10
+ export declare const useAccordionNavContext: () => AccordionNavContextValue | null;
11
+ export declare const AccordionNavProvider: React.FC<{
12
+ children: React.ReactNode;
13
+ accordion?: boolean;
14
+ }>;
4
15
  interface SidebarProviderProps {
5
16
  children: React.ReactNode;
6
17
  defaultCollapsed?: boolean;
7
18
  collapsed?: boolean;
8
19
  onCollapsedChange?: (collapsed: boolean) => void;
20
+ mobileBreakpoint?: number;
21
+ mobileOpen?: boolean;
22
+ onMobileOpenChange?: (open: boolean) => void;
9
23
  width?: {
10
24
  expanded?: number;
11
25
  collapsed?: number;
@@ -1,5 +1,5 @@
1
- import { jsx } from 'react/jsx-runtime';
2
- import { createContext, useContext, useState, useCallback, useMemo } from 'react';
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
3
3
 
4
4
  const SidebarContext = createContext(null);
5
5
  const useSidebarContext = () => {
@@ -7,32 +7,84 @@ const useSidebarContext = () => {
7
7
  // Return null if not in sidebar context (allows NavItem to work elsewhere)
8
8
  return context;
9
9
  };
10
- const SidebarProvider = ({ children, defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, width = {}, side = 'left', variant = 'sidebar', }) => {
10
+ const AccordionNavContext = createContext(null);
11
+ const useAccordionNavContext = () => useContext(AccordionNavContext);
12
+ const AccordionNavProvider = ({ children, accordion = false }) => {
13
+ const [openId, setOpenId] = useState(null);
14
+ const register = useCallback((id) => {
15
+ setOpenId(id);
16
+ }, []);
17
+ const unregister = useCallback((id) => {
18
+ setOpenId(prev => (prev === id ? null : prev));
19
+ }, []);
20
+ const value = useMemo(() => ({ openId, register, unregister }), [openId, register, unregister]);
21
+ if (!accordion)
22
+ return jsx(Fragment, { children: children });
23
+ return (jsx(AccordionNavContext.Provider, { value: value, children: children }));
24
+ };
25
+ const SidebarProvider = ({ children, defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, mobileBreakpoint = 1024, mobileOpen: controlledMobileOpen, onMobileOpenChange, width = {}, side = 'left', variant = 'sidebar', }) => {
11
26
  const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
12
- // Determine if component is controlled
13
- const isControlled = controlledCollapsed !== undefined;
14
- const collapsed = isControlled ? controlledCollapsed : internalCollapsed;
27
+ const [internalMobileOpen, setInternalMobileOpen] = useState(false);
28
+ const [isMobile, setIsMobile] = useState(false);
29
+ // Detect mobile breakpoint
30
+ useEffect(() => {
31
+ if (typeof window === 'undefined')
32
+ return;
33
+ const mql = window.matchMedia(`(max-width: ${mobileBreakpoint - 1}px)`);
34
+ const handler = (e) => {
35
+ setIsMobile(e.matches);
36
+ if (!e.matches) {
37
+ setInternalMobileOpen(false);
38
+ onMobileOpenChange === null || onMobileOpenChange === void 0 ? void 0 : onMobileOpenChange(false);
39
+ }
40
+ };
41
+ handler(mql);
42
+ mql.addEventListener('change', handler);
43
+ return () => mql.removeEventListener('change', handler);
44
+ }, [mobileBreakpoint, onMobileOpenChange]);
45
+ // Collapsed state
46
+ const isCollapsedControlled = controlledCollapsed !== undefined;
47
+ const collapsed = isCollapsedControlled ? controlledCollapsed : internalCollapsed;
15
48
  const setCollapsed = useCallback((newCollapsed) => {
16
- if (!isControlled) {
49
+ if (!isCollapsedControlled) {
17
50
  setInternalCollapsed(newCollapsed);
18
51
  }
19
52
  onCollapsedChange === null || onCollapsedChange === void 0 ? void 0 : onCollapsedChange(newCollapsed);
20
- }, [isControlled, onCollapsedChange]);
53
+ }, [isCollapsedControlled, onCollapsedChange]);
21
54
  const toggleCollapsed = useCallback(() => {
22
55
  setCollapsed(!collapsed);
23
56
  }, [collapsed, setCollapsed]);
57
+ // Mobile open state
58
+ const isMobileOpenControlled = controlledMobileOpen !== undefined;
59
+ const isMobileOpen = isMobileOpenControlled ? controlledMobileOpen : internalMobileOpen;
60
+ const setMobileOpen = useCallback((open) => {
61
+ if (!isMobileOpenControlled) {
62
+ setInternalMobileOpen(open);
63
+ }
64
+ onMobileOpenChange === null || onMobileOpenChange === void 0 ? void 0 : onMobileOpenChange(open);
65
+ }, [isMobileOpenControlled, onMobileOpenChange]);
66
+ // Lock body scroll when mobile overlay is open
67
+ useEffect(() => {
68
+ if (isMobile && isMobileOpen) {
69
+ document.body.style.overflow = 'hidden';
70
+ return () => { document.body.style.overflow = ''; };
71
+ }
72
+ }, [isMobile, isMobileOpen]);
24
73
  const contextValue = useMemo(() => ({
25
74
  collapsed,
26
75
  setCollapsed,
27
76
  toggleCollapsed,
28
77
  width: {
29
78
  expanded: width.expanded || 280,
30
- collapsed: width.collapsed || 80,
79
+ collapsed: width.collapsed || 64,
31
80
  },
32
81
  side,
33
82
  variant,
34
- }), [collapsed, setCollapsed, toggleCollapsed, width.expanded, width.collapsed, side, variant]);
83
+ isMobileOpen,
84
+ setMobileOpen,
85
+ isMobile,
86
+ }), [collapsed, setCollapsed, toggleCollapsed, width.expanded, width.collapsed, side, variant, isMobileOpen, setMobileOpen, isMobile]);
35
87
  return (jsx(SidebarContext.Provider, { value: contextValue, children: children }));
36
88
  };
37
89
 
38
- export { SidebarProvider, useSidebarContext };
90
+ export { AccordionNavProvider, SidebarProvider, useAccordionNavContext, useSidebarContext };
@@ -6,7 +6,9 @@ import { useSidebarContext } from './SidebarContext.js';
6
6
  const SidebarFooter = React__default.forwardRef(({ children, className, ...props }, ref) => {
7
7
  const sidebarContext = useSidebarContext();
8
8
  const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
9
- return (jsx("div", { ref: ref, className: cn("flex-shrink-0 border-t border-[var(--color-border)] bg-[var(--color-background)] transition-all duration-200", collapsed ? "p-2 flex justify-center" : "p-4", className), ...props, children: children }));
9
+ const isMobile = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.isMobile) || false;
10
+ const isCollapsedView = collapsed && !isMobile;
11
+ return (jsx("div", { ref: ref, className: cn("flex-shrink-0 border-t border-[var(--color-border)] transition-all duration-200", isCollapsedView ? "p-2 flex justify-center" : "p-4", className), ...props, children: children }));
10
12
  });
11
13
  SidebarFooter.displayName = "SidebarFooter";
12
14
 
@@ -1,17 +1,21 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import React__default from 'react';
3
+ import { XMarkIcon } from '@heroicons/react/24/outline';
3
4
  import { cn } from '../../../utils/cn.js';
4
5
  import { useSidebarContext } from './SidebarContext.js';
5
6
  import { SidebarToggle } from './SidebarToggle.js';
7
+ import { Button } from '../Button/Button.js';
6
8
 
7
9
  const SidebarHeader = React__default.forwardRef(({ logo, showToggle = true, customToggle, children, className, ...props }, ref) => {
8
10
  const sidebarContext = useSidebarContext();
9
11
  const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
12
+ const isMobile = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.isMobile) || false;
13
+ const isMobileOpen = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.isMobileOpen) || false;
10
14
  // If children are provided, use them instead of default layout
11
15
  if (children) {
12
- return (jsx("div", { ref: ref, className: cn("flex-shrink-0 border-b border-gray-200 bg-white", className), ...props, children: children }));
16
+ return (jsx("div", { ref: ref, className: cn("flex-shrink-0 border-b border-[var(--color-border)]", className), ...props, children: children }));
13
17
  }
14
- return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between flex-shrink-0 h-16 border-b border-[var(--color-border)] bg-[var(--color-background)] transition-all duration-200", collapsed ? "px-3" : "px-4", className), ...props, children: [jsx("div", { className: cn("flex items-center min-w-0 transition-all duration-200", collapsed ? "flex-shrink-0" : "flex-1"), children: logo }), showToggle && (jsx("div", { className: "flex-shrink-0", children: customToggle || jsx(SidebarToggle, {}) }))] }));
18
+ return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between flex-shrink-0 h-16 border-b border-[var(--color-border)] transition-all duration-200", collapsed && !isMobile ? "px-3" : "px-4", className), ...props, children: [jsx("div", { className: cn("flex items-center min-w-0 transition-all duration-200", collapsed && !isMobile ? "flex-shrink-0" : "flex-1"), children: logo }), isMobile && isMobileOpen && (jsx(Button, { variant: "soft", size: "sm", className: "size-8 p-0", onClick: () => sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.setMobileOpen(false), "aria-label": "Close navigation", children: jsx(XMarkIcon, { className: "size-5" }) })), !isMobile && showToggle && (jsx("div", { className: "flex-shrink-0", children: customToggle || jsx(SidebarToggle, {}) }))] }));
15
19
  });
16
20
  SidebarHeader.displayName = "SidebarHeader";
17
21
 
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { SidebarMobileTriggerProps } from "./Sidebar.types";
3
+ export declare const SidebarMobileTrigger: React.ForwardRefExoticComponent<SidebarMobileTriggerProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,28 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import React__default from 'react';
3
+ import { Bars3Icon } from '@heroicons/react/24/outline';
4
+ import { cn } from '../../../utils/cn.js';
5
+ import { useSidebarContext } from './SidebarContext.js';
6
+ import { Button } from '../Button/Button.js';
7
+
8
+ const SidebarMobileTrigger = React__default.forwardRef(({ icon, className, onClick, ...props }, ref) => {
9
+ const sidebarContext = useSidebarContext();
10
+ if (!sidebarContext) {
11
+ if (process.env.NODE_ENV !== "production") {
12
+ console.warn("SidebarMobileTrigger must be used within a Sidebar component");
13
+ }
14
+ return null;
15
+ }
16
+ const { isMobile, setMobileOpen } = sidebarContext;
17
+ // Only render on mobile
18
+ if (!isMobile)
19
+ return null;
20
+ const handleClick = (e) => {
21
+ setMobileOpen(true);
22
+ onClick === null || onClick === void 0 ? void 0 : onClick(e);
23
+ };
24
+ return (jsx(Button, { ref: ref, variant: "soft", size: "sm", className: cn("size-9 p-0", className), onClick: handleClick, "aria-label": "Open navigation menu", ...props, children: icon || jsx(Bars3Icon, { className: "size-5" }) }));
25
+ });
26
+ SidebarMobileTrigger.displayName = "SidebarMobileTrigger";
27
+
28
+ export { SidebarMobileTrigger };
@@ -1,9 +1,10 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import React__default from 'react';
3
3
  import { cn } from '../../../utils/cn.js';
4
+ import { AccordionNavProvider } from './SidebarContext.js';
4
5
 
5
- const SidebarNav = React__default.forwardRef(({ children, className, ...props }, ref) => {
6
- return (jsx("nav", { ref: ref, className: cn('flex-1 space-y-1 p-4', className), role: "navigation", ...props, children: children }));
6
+ const SidebarNav = React__default.forwardRef(({ accordion = false, children, className, ...props }, ref) => {
7
+ return (jsx("nav", { ref: ref, className: cn('flex-1 space-y-1 p-4', className), role: "navigation", ...props, children: jsx(AccordionNavProvider, { accordion: accordion, children: children }) }));
7
8
  });
8
9
  SidebarNav.displayName = 'SidebarNav';
9
10
 
@@ -6,8 +6,10 @@ import { useSidebarContext } from './SidebarContext.js';
6
6
  const SidebarNavGroup = React__default.forwardRef(({ title, showTitleWhenCollapsed = false, children, className, ...props }, ref) => {
7
7
  const sidebarContext = useSidebarContext();
8
8
  const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
9
- const showTitle = title && (!collapsed || showTitleWhenCollapsed);
10
- return (jsxs("div", { ref: ref, className: cn("space-y-1", className), ...props, children: [showTitle && (jsx("div", { className: "px-2 py-2", children: jsx("h3", { className: "text-sm font-semibold text-[var(--color-text-muted)] uppercase tracking-wider", children: title }) })), collapsed && !showTitleWhenCollapsed && (jsx("div", { className: "h-px bg-[var(--color-border)] mx-2 my-2" })), jsx("div", { className: "space-y-1", children: children })] }));
9
+ const isMobile = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.isMobile) || false;
10
+ const isCollapsedView = collapsed && !isMobile;
11
+ const showTitle = title && (!isCollapsedView || showTitleWhenCollapsed);
12
+ return (jsxs("div", { ref: ref, className: cn("space-y-1", className), ...props, children: [showTitle && (jsx("div", { className: "px-2 py-2", children: jsx("h3", { className: "text-sm font-semibold text-[var(--color-text-muted)] uppercase tracking-wider", children: title }) })), isCollapsedView && !showTitleWhenCollapsed && (jsx("div", { className: "h-px bg-[var(--color-border)] mx-2 my-2" })), jsx("div", { className: "space-y-1", children: children })] }));
11
13
  });
12
14
  SidebarNavGroup.displayName = "SidebarNavGroup";
13
15
 
@@ -19,7 +19,7 @@ const SidebarToggle = React__default.forwardRef(({ expandIcon, collapseIcon, cla
19
19
  toggleCollapsed();
20
20
  onClick === null || onClick === void 0 ? void 0 : onClick(e);
21
21
  };
22
- return (jsx(Button, { ref: ref, variant: "ghost", size: "sm", className: cn("size-8 p-0", className), onClick: handleClick, "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar", title: collapsed ? "Expand sidebar" : "Collapse sidebar", ...props, children: jsx(motion.div, { initial: false, animate: { rotate: collapsed ? 180 : 0 }, transition: { duration: 0.2 }, children: collapsed
22
+ return (jsx(Button, { ref: ref, variant: "soft", size: "sm", className: cn("size-8 p-0", className), onClick: handleClick, "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar", title: collapsed ? "Expand sidebar" : "Collapse sidebar", ...props, children: jsx(motion.div, { initial: false, animate: { rotate: collapsed ? 180 : 0 }, transition: { duration: 0.2 }, children: collapsed
23
23
  ? expandIcon || jsx(ChevronRightIcon, { className: "size-4" })
24
24
  : collapseIcon || jsx(ChevronLeftIcon, { className: "size-4" }) }) }));
25
25
  });
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { SidebarUserProfileProps } from "./Sidebar.types";
3
+ export declare const SidebarUserProfile: React.ForwardRefExoticComponent<SidebarUserProfileProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,35 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import React__default from 'react';
3
+ import { cn } from '../../../utils/cn.js';
4
+ import { useSidebarContext } from './SidebarContext.js';
5
+ import { Avatar } from '../Avatar/Avatar.js';
6
+ import { Tooltip } from '../Tooltip/Tooltip.js';
7
+ import '../Tooltip/Tooltip.animations.js';
8
+
9
+ const statusColors = {
10
+ online: "bg-[var(--color-success)]",
11
+ offline: "bg-[var(--color-background-quaternary)]",
12
+ away: "bg-[var(--color-warning)]",
13
+ busy: "bg-[var(--color-danger)]",
14
+ };
15
+ const SidebarUserProfile = React__default.forwardRef(({ name, email, avatar, status, dropdownContent, className, onClick, ...props }, ref) => {
16
+ const sidebarContext = useSidebarContext();
17
+ const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
18
+ const isMobile = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.isMobile) || false;
19
+ const isCollapsedView = collapsed && !isMobile;
20
+ const avatarElement = (jsxs("div", { className: "relative flex-shrink-0", children: [jsx(Avatar, { src: avatar, name: name, size: "sm" }), status && (jsx("span", { className: cn("absolute -bottom-0.5 -right-0.5 size-3 rounded-full ring-2 ring-[var(--color-background)]", statusColors[status]), "aria-label": `Status: ${status}` }))] }));
21
+ const content = (jsxs("div", { ref: ref, className: cn("flex items-center gap-3 rounded-[var(--radius-base)] cursor-pointer transition-colors duration-150 hover:bg-[var(--color-background-secondary)]", isCollapsedView ? "justify-center p-2" : "px-3 py-2", className), role: "button", tabIndex: 0, onClick: onClick, onKeyDown: (e) => {
22
+ if ((e.key === "Enter" || e.key === " ") && onClick) {
23
+ e.preventDefault();
24
+ onClick(e);
25
+ }
26
+ }, ...props, children: [avatarElement, !isCollapsedView && (jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "text-sm font-medium text-[var(--color-text-primary)] truncate", children: name }), email && (jsx("div", { className: "text-xs text-[var(--color-text-muted)] truncate", children: email }))] }))] }));
27
+ // Tooltip when collapsed
28
+ if (isCollapsedView) {
29
+ return (jsx(Tooltip, { content: name, placement: "right", children: content }));
30
+ }
31
+ return content;
32
+ });
33
+ SidebarUserProfile.displayName = "SidebarUserProfile";
34
+
35
+ export { SidebarUserProfile };
@@ -6,5 +6,7 @@ export { SidebarNav } from "./SidebarNav";
6
6
  export { SidebarNavGroup } from "./SidebarNavGroup";
7
7
  export { SidebarSubmenu } from "./SidebarSubmenu";
8
8
  export { SidebarToggle } from "./SidebarToggle";
9
+ export { SidebarMobileTrigger } from "./SidebarMobileTrigger";
10
+ export { SidebarUserProfile } from "./SidebarUserProfile";
9
11
  export { useSidebarContext } from "./SidebarContext";
10
- export type { SidebarProps, SidebarHeaderProps, SidebarContentProps, SidebarFooterProps, SidebarNavProps, SidebarNavGroupProps, SidebarToggleProps, SidebarSide, SidebarVariant, SidebarContextValue, SidebarTheme, SidebarThemeOverrides, } from "./Sidebar.types";
12
+ export type { SidebarProps, SidebarHeaderProps, SidebarContentProps, SidebarFooterProps, SidebarNavProps, SidebarNavGroupProps, SidebarToggleProps, SidebarMobileTriggerProps, SidebarUserProfileProps, SidebarUserStatus, SidebarSide, SidebarVariant, SidebarContextValue, SidebarTheme, SidebarThemeOverrides, } from "./Sidebar.types";
@@ -1,4 +1,4 @@
1
- import { TabsSize } from './Tabs.types';
1
+ import { TabsSize } from "./Tabs.types";
2
2
  /**
3
3
  * Clean theme structure for Tabs components
4
4
  */
@@ -7,11 +7,11 @@ const tabsTheme = {
7
7
  // TabsList styles with proper design tokens
8
8
  listStyle: "relative inline-flex items-center flex-row border-b border-[var(--color-border)] gap-2",
9
9
  // TabsTrigger base styles - no individual borders, underline will be handled separately
10
- triggerStyle: "relative inline-flex items-center justify-center whitespace-nowrap font-medium transition-all duration-300 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] data-[state=active]:text-[var(--color-primary)]",
10
+ triggerStyle: "relative inline-flex items-center justify-center whitespace-nowrap font-semibold transition-all duration-300 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] data-[state=active]:text-[var(--color-primary)]",
11
11
  // TabsTrigger size variants using CSS variables where applicable
12
12
  triggerSizes: {
13
- sm: "px-2 py-1.5 text-base gap-1",
14
- md: "px-2 py-2 text-base gap-1",
13
+ sm: "px-2 py-1.5 text-base",
14
+ md: "px-2 py-2 text-base",
15
15
  },
16
16
  // TabsBody styles with proper CSS variables
17
17
  bodyStyle: "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2",
@@ -89,6 +89,8 @@ export { SidebarNav } from './Sidebar/SidebarNav.js';
89
89
  export { SidebarNavGroup } from './Sidebar/SidebarNavGroup.js';
90
90
  export { SidebarSubmenu } from './Sidebar/SidebarSubmenu.js';
91
91
  export { SidebarToggle } from './Sidebar/SidebarToggle.js';
92
+ export { SidebarMobileTrigger } from './Sidebar/SidebarMobileTrigger.js';
93
+ export { SidebarUserProfile } from './Sidebar/SidebarUserProfile.js';
92
94
  export { useSidebarContext } from './Sidebar/SidebarContext.js';
93
95
  export { OfflineIndicator } from './OfflineIndicator/OfflineIndicator.js';
94
96
  export { offlineIndicatorTheme } from './OfflineIndicator/OfflineIndicator.theme.js';
@@ -23,7 +23,7 @@ const GanttToolbar = () => {
23
23
  const targetScroll = todayOffset * dayWidth - container.clientWidth / 2;
24
24
  container.scrollTo({ left: targetScroll, behavior: "smooth" });
25
25
  };
26
- return (jsxs("div", { className: cn(theme.toolbarStyle), children: [jsxs("div", { className: "flex items-center gap-3", children: [todayOffset !== null && (jsxs(Button, { variant: "outline", color: "neutral", size: "sm", onClick: handleScrollToToday, children: [jsx(CalendarIcon, { className: "size-4" }), "Today"] })), jsx(Segmented, { value: zoom, onChange: (val) => setZoom(val), size: "sm", children: ZOOM_OPTIONS.map((opt) => (jsx(Segmented.Item, { value: opt.value, children: opt.label }, opt.value))) }), jsx(Checkbox, { id: "gantt-hide-completed", name: "hideCompleted", value: "hideCompleted", checked: hideCompleted, onChange: (checked) => setHideCompleted(checked), label: "Hide completed", size: "sm" })] }), jsx("div", { className: "flex items-center gap-2", children: onGroupAdd && !readonly && (jsxs(Button, { variant: "ghost", size: "sm", onClick: onGroupAdd, children: [jsx(PlusIcon, { className: "size-3.5" }), "Add group"] })) })] }));
26
+ return (jsxs("div", { className: cn(theme.toolbarStyle), children: [jsxs("div", { className: "flex items-center gap-3", children: [todayOffset !== null && (jsxs(Button, { variant: "outline", color: "neutral", size: "sm", onClick: handleScrollToToday, children: [jsx(CalendarIcon, { className: "size-4" }), "Today"] })), jsx(Segmented, { value: zoom, onChange: (val) => setZoom(val), size: "sm", children: ZOOM_OPTIONS.map((opt) => (jsx(Segmented.Item, { value: opt.value, children: opt.label }, opt.value))) }), jsx(Checkbox, { id: "gantt-hide-completed", name: "hideCompleted", value: "hideCompleted", checked: hideCompleted, onChange: (checked) => setHideCompleted(checked), label: "Hide completed", size: "sm" })] }), jsx("div", { className: "flex items-center gap-2", children: onGroupAdd && !readonly && (jsxs(Button, { variant: "soft", size: "sm", onClick: onGroupAdd, children: [jsx(PlusIcon, { className: "size-3.5" }), "Add group"] })) })] }));
27
27
  };
28
28
 
29
29
  export { GanttToolbar };