@flikk/ui 1.0.0-beta.25 → 1.0.0-beta.27

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 (211) hide show
  1. package/dist/components/ai/CodeBlock/CodeBlock.theme.js +2 -2
  2. package/dist/components/ai/PromptInput/PromptInput.js +4 -0
  3. package/dist/components/ai/PromptInput/VoiceRecorder.js +4 -0
  4. package/dist/components/ai/StreamingResponse/MarkdownRenderer.js +1 -1
  5. package/dist/components/ai/TokenCounter/TokenCounter.theme.js +1 -1
  6. package/dist/components/charts/BarChart/BarChart.js +2 -2
  7. package/dist/components/charts/StackedBarChart/StackedBarChart.js +1 -1
  8. package/dist/components/charts/shared/BarRenderer/BarRenderer.js +2 -2
  9. package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +1 -1
  10. package/dist/components/core/Button/Button.js +1 -1
  11. package/dist/components/core/Button/Button.theme.js +5 -5
  12. package/dist/components/core/Calendar/Calendar.theme.js +1 -1
  13. package/dist/components/core/Calendar/CalendarMini/CalendarMini.theme.js +1 -1
  14. package/dist/components/core/CommandPalette/CommandItem.js +4 -5
  15. package/dist/components/core/CommandPalette/CommandPalette.js +1 -1
  16. package/dist/components/core/CommandPalette/CommandPalette.theme.js +11 -13
  17. package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +0 -2
  18. package/dist/components/core/ContextMenu/ContextMenu.theme.js +13 -14
  19. package/dist/components/core/DotSeparator/DotSeparator.d.ts +3 -0
  20. package/dist/components/core/DotSeparator/DotSeparator.js +19 -0
  21. package/dist/components/core/DotSeparator/DotSeparator.theme.d.ts +2 -0
  22. package/dist/components/core/DotSeparator/DotSeparator.theme.js +10 -0
  23. package/dist/components/core/DotSeparator/DotSeparator.types.d.ts +25 -0
  24. package/dist/components/core/DotSeparator/index.d.ts +3 -0
  25. package/dist/components/core/Drawer/Drawer.theme.js +2 -2
  26. package/dist/components/core/Dropdown/Dropdown.theme.js +4 -5
  27. package/dist/components/core/Dropdown/DropdownItem.js +1 -1
  28. package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
  29. package/dist/components/core/Empty/Empty.js +1 -1
  30. package/dist/components/core/ImagePlaceholder/ImagePlaceholder.d.ts +14 -0
  31. package/dist/components/core/ImagePlaceholder/ImagePlaceholder.js +28 -0
  32. package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.d.ts +6 -0
  33. package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.js +8 -0
  34. package/dist/components/core/ImagePlaceholder/ImagePlaceholder.types.d.ts +32 -0
  35. package/dist/components/core/ImagePlaceholder/index.d.ts +2 -0
  36. package/dist/components/core/MenuItem/MenuItem.js +5 -2
  37. package/dist/components/core/MenuItem/MenuItem.theme.d.ts +6 -0
  38. package/dist/components/core/MenuItem/MenuItem.theme.js +19 -16
  39. package/dist/components/core/MenuItem/MenuItem.types.d.ts +2 -0
  40. package/dist/components/core/Message/Message.theme.js +1 -1
  41. package/dist/components/core/Modal/Modal.theme.js +1 -1
  42. package/dist/components/core/NavItem/NavItem.d.ts +1 -1
  43. package/dist/components/core/NavItem/NavItem.js +187 -68
  44. package/dist/components/core/NavItem/NavItem.theme.js +31 -12
  45. package/dist/components/core/NavItem/NavItem.types.d.ts +17 -39
  46. package/dist/components/core/Pagination/Pagination.theme.js +3 -3
  47. package/dist/components/core/Popover/Popover.theme.js +1 -1
  48. package/dist/components/core/Popover/Popover.types.d.ts +6 -2
  49. package/dist/components/core/Popover/PopoverBody.js +10 -3
  50. package/dist/components/core/Progress/Progress.theme.js +1 -1
  51. package/dist/components/core/ScrollArea/ScrollArea.js +2 -2
  52. package/dist/components/core/ScrollArea/ScrollArea.theme.js +1 -1
  53. package/dist/components/core/Segmented/Segmented.js +8 -6
  54. package/dist/components/core/Segmented/Segmented.theme.js +3 -3
  55. package/dist/components/core/Sidebar/Sidebar.d.ts +3 -1
  56. package/dist/components/core/Sidebar/Sidebar.js +53 -16
  57. package/dist/components/core/Sidebar/Sidebar.theme.js +5 -7
  58. package/dist/components/core/Sidebar/Sidebar.types.d.ts +125 -8
  59. package/dist/components/core/Sidebar/SidebarCard.d.ts +25 -0
  60. package/dist/components/core/Sidebar/SidebarCard.js +68 -0
  61. package/dist/components/core/Sidebar/SidebarCard.theme.d.ts +8 -0
  62. package/dist/components/core/Sidebar/SidebarCard.theme.js +18 -0
  63. package/dist/components/core/Sidebar/SidebarContent.js +10 -6
  64. package/dist/components/core/Sidebar/SidebarContext.d.ts +17 -16
  65. package/dist/components/core/Sidebar/SidebarContext.js +43 -20
  66. package/dist/components/core/Sidebar/SidebarFooter.js +4 -6
  67. package/dist/components/core/Sidebar/SidebarHeader.js +12 -9
  68. package/dist/components/core/Sidebar/SidebarLogo.js +39 -8
  69. package/dist/components/core/Sidebar/SidebarNav.js +4 -3
  70. package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -7
  71. package/dist/components/core/Sidebar/SidebarSearch.d.ts +22 -0
  72. package/dist/components/core/Sidebar/SidebarSearch.js +135 -0
  73. package/dist/components/core/Sidebar/SidebarToggle.js +6 -1
  74. package/dist/components/core/Sidebar/SidebarUserProfile.js +33 -17
  75. package/dist/components/core/Sidebar/index.d.ts +4 -3
  76. package/dist/components/core/Skeleton/Skeleton.theme.js +1 -1
  77. package/dist/components/core/Spinner/Spinner.theme.js +1 -1
  78. package/dist/components/core/Tabs/Tabs.js +6 -1
  79. package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
  80. package/dist/components/core/Tabs/TabsBody.js +5 -3
  81. package/dist/components/core/Tag/Tag.js +29 -5
  82. package/dist/components/core/Tag/Tag.theme.js +14 -9
  83. package/dist/components/core/Tag/Tag.types.d.ts +7 -0
  84. package/dist/components/core/Tooltip/Tooltip.js +1 -1
  85. package/dist/components/core/Tooltip/Tooltip.theme.js +1 -2
  86. package/dist/components/core/index.d.ts +2 -0
  87. package/dist/components/core/index.js +6 -2
  88. package/dist/components/data-display/GanttChart/GanttChart.d.ts +1 -1
  89. package/dist/components/data-display/GanttChart/GanttChart.js +31 -4
  90. package/dist/components/data-display/GanttChart/GanttChart.theme.js +19 -17
  91. package/dist/components/data-display/GanttChart/GanttChart.types.d.ts +19 -0
  92. package/dist/components/data-display/GanttChart/GanttChart.utils.js +1 -1
  93. package/dist/components/data-display/GanttChart/GanttChartPanel.d.ts +1 -1
  94. package/dist/components/data-display/GanttChart/GanttChartPanel.js +3 -3
  95. package/dist/components/data-display/GanttChart/GanttDependencyArrows.js +7 -4
  96. package/dist/components/data-display/GanttChart/GanttMilestone.js +5 -5
  97. package/dist/components/data-display/GanttChart/GanttTableGroupRow.js +1 -1
  98. package/dist/components/data-display/GanttChart/GanttTablePanel.js +1 -1
  99. package/dist/components/data-display/GanttChart/GanttTableTaskRow.js +2 -2
  100. package/dist/components/data-display/GanttChart/GanttTaskBar.js +37 -16
  101. package/dist/components/data-display/GanttChart/GanttTimelineGrid.js +2 -1
  102. package/dist/components/data-display/GanttChart/GanttTimelineHeader.js +35 -11
  103. package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
  104. package/dist/components/data-display/GanttChart/hooks/useGanttDrag.js +3 -2
  105. package/dist/components/data-display/GanttChart/index.d.ts +1 -1
  106. package/dist/components/data-display/KPI/KPI.js +13 -3
  107. package/dist/components/data-display/KPI/KPI.theme.js +3 -3
  108. package/dist/components/data-display/Metric/Metric.theme.js +3 -3
  109. package/dist/components/data-display/Table/Table.js +4 -3
  110. package/dist/components/data-display/Table/Table.theme.js +14 -6
  111. package/dist/components/data-display/Table/Table.types.d.ts +15 -0
  112. package/dist/components/data-display/Table/TableColumnManager.js +1 -1
  113. package/dist/components/data-display/Table/index.d.ts +2 -2
  114. package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
  115. package/dist/components/effects/Aurora/Aurora.js +310 -105
  116. package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
  117. package/dist/components/effects/InteractiveCharacters/InteractiveCharacters.js +3 -3
  118. package/dist/components/effects/InteractiveCharacters/characters/BlobCharacter.js +13 -4
  119. package/dist/components/effects/InteractiveCharacters/characters/SmallCharacter.js +13 -4
  120. package/dist/components/effects/InteractiveCharacters/characters/TallCharacter.js +14 -4
  121. package/dist/components/effects/MorphingText/MorphingText.js +40 -7
  122. package/dist/components/effects/StripePattern/StripePattern.d.ts +18 -0
  123. package/dist/components/effects/StripePattern/StripePattern.js +54 -0
  124. package/dist/components/effects/StripePattern/StripePattern.types.d.ts +53 -0
  125. package/dist/components/effects/StripePattern/index.d.ts +2 -0
  126. package/dist/components/effects/index.d.ts +2 -0
  127. package/dist/components/effects/index.js +1 -0
  128. package/dist/components/forms/Checkbox/Checkbox.theme.js +1 -1
  129. package/dist/components/forms/Combobox/Combobox.js +1 -1
  130. package/dist/components/forms/Combobox/Combobox.theme.js +2 -4
  131. package/dist/components/forms/CronInput/CronInput.js +1 -1
  132. package/dist/components/forms/CronInput/CronInput.theme.js +22 -31
  133. package/dist/components/forms/DatePicker/DatePicker.theme.js +11 -18
  134. package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
  135. package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +7 -14
  136. package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
  137. package/dist/components/forms/FileUpload/FileUpload.animations.d.ts +29 -0
  138. package/dist/components/forms/FileUpload/FileUpload.animations.js +19 -0
  139. package/dist/components/forms/FileUpload/FileUpload.js +88 -49
  140. package/dist/components/forms/FileUpload/FileUpload.theme.d.ts +2 -10
  141. package/dist/components/forms/FileUpload/FileUpload.theme.js +16 -30
  142. package/dist/components/forms/FileUpload/FileUpload.types.d.ts +26 -10
  143. package/dist/components/forms/FileUpload/FileUploadProgress.js +27 -20
  144. package/dist/components/forms/FileUpload/FileUploadProgress.theme.js +2 -2
  145. package/dist/components/forms/Input/Input.theme.js +16 -15
  146. package/dist/components/forms/InputCounter/InputCounter.theme.js +1 -1
  147. package/dist/components/forms/Mention/Mention.js +152 -57
  148. package/dist/components/forms/Mention/Mention.theme.js +16 -23
  149. package/dist/components/forms/Mention/Mention.types.d.ts +12 -12
  150. package/dist/components/forms/Mention/Mention.utils.js +32 -4
  151. package/dist/components/forms/Radio/Radio.theme.js +1 -1
  152. package/dist/components/forms/RichTextEditor/RichTextEditor.d.ts +18 -0
  153. package/dist/components/forms/RichTextEditor/RichTextEditor.js +1231 -236
  154. package/dist/components/forms/RichTextEditor/RichTextEditor.theme.d.ts +53 -1
  155. package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +99 -25
  156. package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +42 -3
  157. package/dist/components/forms/RichTextEditor/index.d.ts +2 -2
  158. package/dist/components/forms/Select/Select.js +2 -2
  159. package/dist/components/forms/Select/Select.theme.js +3 -3
  160. package/dist/components/forms/Slider/Slider.theme.js +1 -1
  161. package/dist/components/forms/Switch/Switch.theme.js +1 -1
  162. package/dist/components/forms/TimePicker/TimePicker.theme.js +2 -2
  163. package/dist/components/forms/TimePicker/TimePickerContent.js +4 -0
  164. package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
  165. package/dist/components/forms/forms.theme.js +24 -19
  166. package/dist/components/forms/index.js +4 -0
  167. package/dist/components/layout/AppShell/AppShell.d.ts +10 -0
  168. package/dist/components/layout/AppShell/AppShell.js +84 -0
  169. package/dist/components/layout/AppShell/AppShell.theme.d.ts +3 -0
  170. package/dist/components/layout/AppShell/AppShell.theme.js +42 -0
  171. package/dist/components/layout/AppShell/AppShell.types.d.ts +127 -0
  172. package/dist/components/layout/AppShell/AppShellContext.d.ts +14 -0
  173. package/dist/components/layout/AppShell/AppShellContext.js +19 -0
  174. package/dist/components/layout/AppShell/AppShellFooter.d.ts +3 -0
  175. package/dist/components/layout/AppShell/AppShellFooter.js +12 -0
  176. package/dist/components/layout/AppShell/AppShellHeader.d.ts +3 -0
  177. package/dist/components/layout/AppShell/AppShellHeader.js +12 -0
  178. package/dist/components/layout/AppShell/AppShellMain.d.ts +3 -0
  179. package/dist/components/layout/AppShell/AppShellMain.js +19 -0
  180. package/dist/components/layout/AppShell/AppShellRightPanel.d.ts +3 -0
  181. package/dist/components/layout/AppShell/AppShellRightPanel.js +16 -0
  182. package/dist/components/layout/AppShell/AppShellSidebar.d.ts +3 -0
  183. package/dist/components/layout/AppShell/AppShellSidebar.js +16 -0
  184. package/dist/components/layout/AppShell/AppShellTopbar.d.ts +3 -0
  185. package/dist/components/layout/AppShell/AppShellTopbar.js +12 -0
  186. package/dist/components/layout/AppShell/index.d.ts +3 -0
  187. package/dist/components/layout/FormLayout/FormLayout.js +9 -4
  188. package/dist/components/layout/FormLayout/FormLayout.types.d.ts +3 -3
  189. package/dist/components/layout/FormLayout/FormLayoutSection.js +6 -1
  190. package/dist/components/layout/PageLayout/PageLayout.d.ts +2 -2
  191. package/dist/components/layout/PageLayout/PageLayout.js +23 -5
  192. package/dist/components/layout/PageLayout/PageLayout.theme.d.ts +2 -1
  193. package/dist/components/layout/PageLayout/PageLayout.theme.js +35 -6
  194. package/dist/components/layout/PageLayout/PageLayout.types.d.ts +45 -0
  195. package/dist/components/layout/PageLayout/PageLayoutContent.js +6 -4
  196. package/dist/components/layout/PageLayout/PageLayoutHeader.js +3 -3
  197. package/dist/components/layout/PageLayout/PageLayoutSidebar.js +5 -3
  198. package/dist/components/layout/PageLayout/index.d.ts +1 -1
  199. package/dist/components/layout/index.d.ts +1 -0
  200. package/dist/components/layout/index.js +2 -0
  201. package/dist/hooks/useSelectPortal.d.ts +10 -2
  202. package/dist/hooks/useSelectPortal.js +45 -24
  203. package/dist/index.js +8 -2
  204. package/dist/registry.json +10021 -0
  205. package/dist/styles.css +1 -1
  206. package/dist/utils/dateUtils.js +11 -1
  207. package/package.json +4 -2
  208. package/src/global.scss +29 -8
  209. package/src/styles/theme.css +174 -100
  210. package/dist/components/core/Sidebar/SidebarSubmenu.d.ts +0 -7
  211. package/dist/components/core/Sidebar/SidebarSubmenu.js +0 -12
@@ -2,6 +2,7 @@ import { ReactNode, HTMLAttributes } from 'react';
2
2
  export type SidebarSide = 'left' | 'right';
3
3
  export type SidebarVariant = 'sidebar' | 'floating' | 'inset';
4
4
  export type SidebarUserStatus = 'online' | 'offline' | 'away' | 'busy';
5
+ export type SidebarDensity = 'comfortable' | 'compact';
5
6
  export interface SidebarContextValue {
6
7
  collapsed: boolean;
7
8
  setCollapsed: (collapsed: boolean) => void;
@@ -12,6 +13,7 @@ export interface SidebarContextValue {
12
13
  };
13
14
  side: SidebarSide;
14
15
  variant: SidebarVariant;
16
+ density: SidebarDensity;
15
17
  /** Whether the sidebar is currently showing as mobile overlay */
16
18
  isMobileOpen: boolean;
17
19
  setMobileOpen: (open: boolean) => void;
@@ -29,6 +31,11 @@ export interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
29
31
  * @default "sidebar"
30
32
  */
31
33
  variant?: SidebarVariant;
34
+ /**
35
+ * Navigation density
36
+ * @default "comfortable"
37
+ */
38
+ density?: SidebarDensity;
32
39
  /**
33
40
  * Whether the sidebar is collapsed by default
34
41
  * @default false
@@ -65,7 +72,7 @@ export interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
65
72
  /**
66
73
  * Children components
67
74
  */
68
- children: ReactNode;
75
+ children?: ReactNode;
69
76
  /**
70
77
  * Custom theme overrides
71
78
  */
@@ -103,6 +110,10 @@ export interface SidebarNavProps extends React.HTMLAttributes<HTMLDivElement> {
103
110
  * @default false
104
111
  */
105
112
  accordion?: boolean;
113
+ /**
114
+ * Navigation density. Inherits from Sidebar when omitted.
115
+ */
116
+ density?: SidebarDensity;
106
117
  children: ReactNode;
107
118
  }
108
119
  export interface SidebarNavGroupProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -157,13 +168,123 @@ export interface SidebarUserProfileProps extends React.HTMLAttributes<HTMLDivEle
157
168
  * Content to render when the profile is clicked (e.g. dropdown menu)
158
169
  */
159
170
  dropdownContent?: ReactNode;
171
+ /**
172
+ * Custom content for the expandable region, replacing the default name/email block.
173
+ * The avatar and all collapse choreography (reveal animation, tooltip, dropdown
174
+ * placement) are preserved — only the text area is overridden. Copy-paste friendly.
175
+ */
176
+ children?: ReactNode;
160
177
  }
161
178
  export interface SidebarLogoProps extends HTMLAttributes<HTMLDivElement> {
162
- /** Always-visible icon/mark (shown in both expanded and collapsed states) */
163
- icon: ReactNode;
164
- /** Text shown only when expanded animates in/out automatically */
179
+ /**
180
+ * Logo shown when the sidebar is **expanded**. Accepts any node — an `<img>`,
181
+ * an inline `<svg>`, or composed JSX (e.g. a mark + wordmark). Preferred
182
+ * over the legacy `icon` + `text` API.
183
+ */
184
+ expanded?: ReactNode;
185
+ /**
186
+ * Logo shown when the sidebar is **collapsed**. Typically a square mark
187
+ * (image, SVG, or JSX). If omitted, falls back to `expanded`, then to the
188
+ * legacy `icon` prop.
189
+ */
190
+ collapsed?: ReactNode;
191
+ /**
192
+ * Convenience: image URL for the expanded logo. Rendered as an `<img>` with
193
+ * sensible defaults (`h-8 w-auto object-contain`). Use `expandedImageClassName`
194
+ * to override sizing / object-fit. Ignored when `expanded` (ReactNode) is set.
195
+ */
196
+ expandedSrc?: string;
197
+ /**
198
+ * Convenience: image URL for the collapsed logo. Rendered as an `<img>` in a
199
+ * 36×36 (`size-9`) slot — matching the nav icon track — with `object-cover`
200
+ * by default. Use `collapsedImageClassName` to switch to `object-contain`,
201
+ * adjust border-radius, etc. Ignored when `collapsed` (ReactNode) is set.
202
+ */
203
+ collapsedSrc?: string;
204
+ /** ClassName forwarded to the auto-rendered `<img>` for `expandedSrc`. */
205
+ expandedImageClassName?: string;
206
+ /** ClassName forwarded to the auto-rendered `<img>` for `collapsedSrc`. */
207
+ collapsedImageClassName?: string;
208
+ /** Alt text for any auto-rendered `<img>` (both `expandedSrc` and `collapsedSrc`). */
209
+ alt?: string;
210
+ /**
211
+ * Legacy: always-visible icon/mark. Shown in both states; the `text` prop
212
+ * fades in alongside it when expanded. Use `expanded` / `collapsed` instead
213
+ * for full control (especially for image/SVG logos that differ per state).
214
+ */
215
+ icon?: ReactNode;
216
+ /**
217
+ * Legacy: text rendered to the right of `icon` when expanded — animates
218
+ * in/out on collapse. Use `expanded` instead for full control.
219
+ */
165
220
  text?: ReactNode;
166
221
  }
222
+ export interface SidebarSearchProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "defaultValue"> {
223
+ /** Controlled value */
224
+ value?: string;
225
+ /** Uncontrolled initial value */
226
+ defaultValue?: string;
227
+ /**
228
+ * Fires on every keystroke. Consumers wire this to render results wherever
229
+ * they like (popover, command palette, side panel). The Sidebar tree itself
230
+ * is NOT filtered.
231
+ */
232
+ onChange?: (value: string) => void;
233
+ /** @default "Search…" */
234
+ placeholder?: string;
235
+ /** @default 'sm' */
236
+ size?: "sm" | "md";
237
+ /** Override the leading icon (defaults to MagnifyingGlassIcon). */
238
+ iconStart?: ReactNode;
239
+ /** Tooltip + aria-label for the collapsed-mode icon button. @default "Search" */
240
+ collapsedLabel?: string;
241
+ /** Hide the collapsed-mode icon button entirely. @default false */
242
+ hideWhenCollapsed?: boolean;
243
+ /** Forwarded to the underlying Input's group wrapper — use for background
244
+ * overrides on custom sidebar surfaces (e.g. `bg-transparent`). */
245
+ inputGroupClassName?: string;
246
+ }
247
+ export interface SidebarCardTheme {
248
+ /** Outer wrapper applied in both styled and unstyled modes — must carry
249
+ * `overflow-hidden` so the height collapse animation clips children. */
250
+ wrapperStyle: string;
251
+ /** Default container chrome (rounded / surface bg / border / padding).
252
+ * Skipped when the consumer sets `unstyled`. */
253
+ baseStyle: string;
254
+ }
255
+ export interface SidebarCardThemeOverrides {
256
+ wrapperStyle?: string;
257
+ baseStyle?: string;
258
+ }
259
+ export interface SidebarCardProps extends HTMLAttributes<HTMLDivElement> {
260
+ children: ReactNode;
261
+ className?: string;
262
+ /**
263
+ * Drop the default container styling (rounded / bg / border / padding)
264
+ * so consumers can put an edge-to-edge image, a custom panel, or any
265
+ * non-card-shaped content inside without fighting the default chrome.
266
+ * The disappear-on-collapse animation is unchanged.
267
+ * @default false
268
+ */
269
+ unstyled?: boolean;
270
+ theme?: SidebarCardThemeOverrides;
271
+ }
272
+ export interface SidebarProviderProps {
273
+ children: React.ReactNode;
274
+ defaultCollapsed?: boolean;
275
+ collapsed?: boolean;
276
+ onCollapsedChange?: (collapsed: boolean) => void;
277
+ mobileBreakpoint?: number;
278
+ mobileOpen?: boolean;
279
+ onMobileOpenChange?: (open: boolean) => void;
280
+ width?: {
281
+ expanded?: number;
282
+ collapsed?: number;
283
+ };
284
+ side?: SidebarSide;
285
+ variant?: SidebarVariant;
286
+ density?: SidebarDensity;
287
+ }
167
288
  export interface SidebarTheme {
168
289
  baseStyle: string;
169
290
  variants: {
@@ -172,8 +293,6 @@ export interface SidebarTheme {
172
293
  sides: {
173
294
  [K in SidebarSide]: string;
174
295
  };
175
- collapsed: string;
176
- expanded: string;
177
296
  mobileOverlay: string;
178
297
  mobileBackdrop: string;
179
298
  }
@@ -185,8 +304,6 @@ export interface SidebarThemeOverrides {
185
304
  sides?: Partial<{
186
305
  [K in SidebarSide]: string;
187
306
  }>;
188
- collapsed?: string;
189
- expanded?: string;
190
307
  mobileOverlay?: string;
191
308
  mobileBackdrop?: string;
192
309
  }
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ import { SidebarCardProps } from "./Sidebar.types";
3
+ /**
4
+ * A collapse-aware container for arbitrary sidebar content — promo CTAs,
5
+ * storage meters, tip-of-the-day, banner images, anything. Renders its
6
+ * children when the sidebar is expanded and **disappears entirely** when
7
+ * the sidebar collapses (smooth height + opacity exit via AnimatePresence).
8
+ *
9
+ * Composition is open: drop in raw text, an `<img>`, a `<Button>`,
10
+ * `<Progress>`, or any custom JSX. There are intentionally no sub-parts
11
+ * (`Sidebar.Card.Header`/`.Body`/…) because the whole card vanishes
12
+ * together — granular slots add ceremony with no payoff.
13
+ *
14
+ * @example
15
+ * <Sidebar.Card>
16
+ * <p className="text-sm">Upgrade for unlimited projects.</p>
17
+ * <Button size="sm" className="mt-2 w-full">Upgrade</Button>
18
+ * </Sidebar.Card>
19
+ *
20
+ * @example Edge-to-edge image (no default container chrome):
21
+ * <Sidebar.Card unstyled>
22
+ * <img src="/promo.jpg" alt="" className="w-full rounded-[var(--card-radius)]" />
23
+ * </Sidebar.Card>
24
+ */
25
+ export declare const SidebarCard: React.ForwardRefExoticComponent<SidebarCardProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,68 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import React__default, { useMemo, useState, useEffect } from 'react';
3
+ import { AnimatePresence, motion } from 'motion/react';
4
+ import { cn } from '../../../utils/cn.js';
5
+ import { sidebarCardTheme } from './SidebarCard.theme.js';
6
+ import { useSidebarCollapsed } from './SidebarContext.js';
7
+
8
+ // Sidebar.Card mount is sequenced relative to the sidebar's collapse/expand
9
+ // animation. On collapse → the card exits immediately (popLayout + slide/fade
10
+ // in parallel with the rail narrowing). On expand → the card mount is delayed
11
+ // until the sidebar's spring has settled, so the card lays out at its FINAL
12
+ // expanded width on first render and the text never reflows mid-animation.
13
+ // 260ms ≈ the spring's 99% settle time (stiffness 320, damping 34) — see
14
+ // Sidebar.tsx's SIDEBAR_TRANSITION comment for the underlying math.
15
+ const MOUNT_DELAY_MS = 260;
16
+ /**
17
+ * A collapse-aware container for arbitrary sidebar content — promo CTAs,
18
+ * storage meters, tip-of-the-day, banner images, anything. Renders its
19
+ * children when the sidebar is expanded and **disappears entirely** when
20
+ * the sidebar collapses (smooth height + opacity exit via AnimatePresence).
21
+ *
22
+ * Composition is open: drop in raw text, an `<img>`, a `<Button>`,
23
+ * `<Progress>`, or any custom JSX. There are intentionally no sub-parts
24
+ * (`Sidebar.Card.Header`/`.Body`/…) because the whole card vanishes
25
+ * together — granular slots add ceremony with no payoff.
26
+ *
27
+ * @example
28
+ * <Sidebar.Card>
29
+ * <p className="text-sm">Upgrade for unlimited projects.</p>
30
+ * <Button size="sm" className="mt-2 w-full">Upgrade</Button>
31
+ * </Sidebar.Card>
32
+ *
33
+ * @example Edge-to-edge image (no default container chrome):
34
+ * <Sidebar.Card unstyled>
35
+ * <img src="/promo.jpg" alt="" className="w-full rounded-[var(--card-radius)]" />
36
+ * </Sidebar.Card>
37
+ */
38
+ const SidebarCard = React__default.forwardRef(({ children, className, unstyled = false, theme: customTheme, ...props }, ref) => {
39
+ const { isCollapsedView } = useSidebarCollapsed();
40
+ const theme = useMemo(() => ({ ...sidebarCardTheme, ...customTheme }), [customTheme]);
41
+ // Card is visible when expanded. Hide immediately on collapse (so the
42
+ // AnimatePresence exit + popLayout reflow happens in parallel with the
43
+ // rail narrowing). On expand, wait for the sidebar spring to settle
44
+ // before mounting — see `MOUNT_DELAY_MS` rationale.
45
+ const [shouldShow, setShouldShow] = useState(!isCollapsedView);
46
+ useEffect(() => {
47
+ if (isCollapsedView) {
48
+ setShouldShow(false);
49
+ return;
50
+ }
51
+ const id = setTimeout(() => setShouldShow(true), MOUNT_DELAY_MS);
52
+ return () => clearTimeout(id);
53
+ }, [isCollapsedView]);
54
+ // Strip drag/animation DOM handlers that collide with motion's prop types
55
+ // (same pattern the other Sidebar pieces use, e.g. Sidebar.tsx:54-61).
56
+ const { onAnimationStart, onAnimationEnd, onDrag, onDragStart, onDragEnd, ...motionSafeProps } = props;
57
+ return (
58
+ // `mode="popLayout"` removes the exiting card from the layout flow
59
+ // immediately so siblings (footer, nav rows below) snap into their new
60
+ // positions without waiting on the card's exit animation.
61
+ jsx(AnimatePresence, { initial: false, mode: "popLayout", children: shouldShow && (jsx(motion.div, { ref: ref, initial: { opacity: 0, x: -16 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -16 }, transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] }, className: cn(theme.wrapperStyle,
62
+ // Default card chrome (rounded / bg / border / p-3) unless the
63
+ // consumer opts out. className wins per shadcn rules.
64
+ !unstyled && theme.baseStyle, className), ...motionSafeProps, children: children }, "sidebar-card")) }));
65
+ });
66
+ SidebarCard.displayName = "SidebarCard";
67
+
68
+ export { SidebarCard };
@@ -0,0 +1,8 @@
1
+ import { SidebarCardTheme } from "./Sidebar.types";
2
+ /**
3
+ * Default theme for `<Sidebar.Card>`. Small surface — the component itself
4
+ * only renders a single styled wrapper that disappears on collapse, so the
5
+ * theme just holds the default outer chrome. Set `unstyled` on the card to
6
+ * skip it entirely (e.g. for full-bleed images).
7
+ */
8
+ export declare const sidebarCardTheme: SidebarCardTheme;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Default theme for `<Sidebar.Card>`. Small surface — the component itself
3
+ * only renders a single styled wrapper that disappears on collapse, so the
4
+ * theme just holds the default outer chrome. Set `unstyled` on the card to
5
+ * skip it entirely (e.g. for full-bleed images).
6
+ */
7
+ const sidebarCardTheme = {
8
+ // Card chrome — surface bg, hairline border, sidebar-tuned padding (p-3 is
9
+ // tighter than core/Card's p-6 because the rail is narrow). `overflow-hidden`
10
+ // keeps any children clipped to the rounded corners.
11
+ baseStyle: "rounded-[var(--card-radius)] bg-[var(--color-surface)] border border-[var(--color-border)] p-3 overflow-hidden",
12
+ // Wrapper applied in both styled + unstyled modes. Empty by default —
13
+ // the slide/fade animation lives on motion.div itself, not on this layer,
14
+ // so no overflow trickery needed here.
15
+ wrapperStyle: "",
16
+ };
17
+
18
+ export { sidebarCardTheme };
@@ -1,14 +1,18 @@
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 { useSidebarContext } from './SidebarContext.js';
4
+ import { useSidebarCollapsed, useSidebarDensity } from './SidebarContext.js';
5
+ import { ScrollArea } from '../ScrollArea/ScrollArea.js';
6
+ import '../ScrollArea/smooth/SmoothScrollEngine.js';
5
7
 
6
8
  const SidebarContent = React__default.forwardRef(({ children, className, ...props }, ref) => {
7
- const sidebarContext = useSidebarContext();
8
- const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
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-3 transition-all duration-200", isCollapsedView ? "px-2" : "px-2.5", className), ...props, children: children }));
9
+ const { isCollapsedView } = useSidebarCollapsed();
10
+ const density = useSidebarDensity();
11
+ return (jsx(ScrollArea, { ref: ref, type: "none", size: "thin", className: "flex-1 min-h-0", ...props, children: jsx("div", { className: cn("py-3 transition-[padding] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]", density === "compact" && "py-2",
12
+ // Collapsed px-3 (12) on a 60px rail leaves a 36px interior — every
13
+ // child row (NavItem, Sidebar.Search) is exactly 36px wide, matching
14
+ // the 36×36 hit-area target.
15
+ isCollapsedView ? "px-3" : "px-2.5", className), children: children }) }));
12
16
  });
13
17
  SidebarContent.displayName = "SidebarContent";
14
18
 
@@ -1,6 +1,22 @@
1
1
  import React from 'react';
2
- import { SidebarContextValue } from './Sidebar.types';
2
+ import { SidebarContextValue, SidebarDensity, SidebarProviderProps } from './Sidebar.types';
3
3
  export declare const useSidebarContext: () => SidebarContextValue | null;
4
+ export declare const useSidebarDensity: () => SidebarDensity;
5
+ export declare const SidebarDensityProvider: React.FC<{
6
+ children: React.ReactNode;
7
+ density?: SidebarDensity;
8
+ }>;
9
+ /**
10
+ * Derived collapsed-view state, shared by all Sidebar sub-components.
11
+ * Returns falsy defaults outside a Sidebar so it can be called unconditionally.
12
+ */
13
+ export declare const useSidebarCollapsed: () => {
14
+ collapsed: boolean;
15
+ isMobile: boolean;
16
+ isMobileOpen: boolean;
17
+ /** Collapsed desktop rail — true only when collapsed AND not the mobile overlay */
18
+ isCollapsedView: boolean;
19
+ };
4
20
  /** Accordion nav context: allows SidebarNav to coordinate which submenu is open */
5
21
  interface AccordionNavContextValue {
6
22
  openId: string | null;
@@ -12,20 +28,5 @@ export declare const AccordionNavProvider: React.FC<{
12
28
  children: React.ReactNode;
13
29
  accordion?: boolean;
14
30
  }>;
15
- interface SidebarProviderProps {
16
- children: React.ReactNode;
17
- defaultCollapsed?: boolean;
18
- collapsed?: boolean;
19
- onCollapsedChange?: (collapsed: boolean) => void;
20
- mobileBreakpoint?: number;
21
- mobileOpen?: boolean;
22
- onMobileOpenChange?: (open: boolean) => void;
23
- width?: {
24
- expanded?: number;
25
- collapsed?: number;
26
- };
27
- side?: 'left' | 'right';
28
- variant?: 'sidebar' | 'floating' | 'inset';
29
- }
30
31
  export declare const SidebarProvider: React.FC<SidebarProviderProps>;
31
32
  export {};
@@ -1,12 +1,42 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
3
+ import { useMediaQuery } from '../../../hooks/useMediaQuery.js';
3
4
 
4
5
  const SidebarContext = createContext(null);
6
+ const SidebarDensityContext = createContext(null);
5
7
  const useSidebarContext = () => {
6
8
  const context = useContext(SidebarContext);
7
9
  // Return null if not in sidebar context (allows NavItem to work elsewhere)
8
10
  return context;
9
11
  };
12
+ const useSidebarDensity = () => {
13
+ var _a;
14
+ const navDensity = useContext(SidebarDensityContext);
15
+ const sidebarContext = useContext(SidebarContext);
16
+ return (_a = navDensity !== null && navDensity !== void 0 ? navDensity : sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.density) !== null && _a !== void 0 ? _a : 'comfortable';
17
+ };
18
+ const SidebarDensityProvider = ({ children, density }) => {
19
+ if (!density)
20
+ return jsx(Fragment, { children: children });
21
+ return (jsx(SidebarDensityContext.Provider, { value: density, children: children }));
22
+ };
23
+ /**
24
+ * Derived collapsed-view state, shared by all Sidebar sub-components.
25
+ * Returns falsy defaults outside a Sidebar so it can be called unconditionally.
26
+ */
27
+ const useSidebarCollapsed = () => {
28
+ var _a, _b, _c;
29
+ const context = useContext(SidebarContext);
30
+ const collapsed = (_a = context === null || context === void 0 ? void 0 : context.collapsed) !== null && _a !== void 0 ? _a : false;
31
+ const isMobile = (_b = context === null || context === void 0 ? void 0 : context.isMobile) !== null && _b !== void 0 ? _b : false;
32
+ return {
33
+ collapsed,
34
+ isMobile,
35
+ isMobileOpen: (_c = context === null || context === void 0 ? void 0 : context.isMobileOpen) !== null && _c !== void 0 ? _c : false,
36
+ /** Collapsed desktop rail — true only when collapsed AND not the mobile overlay */
37
+ isCollapsedView: collapsed && !isMobile,
38
+ };
39
+ };
10
40
  const AccordionNavContext = createContext(null);
11
41
  const useAccordionNavContext = () => useContext(AccordionNavContext);
12
42
  const AccordionNavProvider = ({ children, accordion = false }) => {
@@ -22,26 +52,18 @@ const AccordionNavProvider = ({ children, accordion = false }) => {
22
52
  return jsx(Fragment, { children: children });
23
53
  return (jsx(AccordionNavContext.Provider, { value: value, children: children }));
24
54
  };
25
- const SidebarProvider = ({ children, defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, mobileBreakpoint = 1024, mobileOpen: controlledMobileOpen, onMobileOpenChange, width = {}, side = 'left', variant = 'sidebar', }) => {
55
+ const SidebarProvider = ({ children, defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, mobileBreakpoint = 1024, mobileOpen: controlledMobileOpen, onMobileOpenChange, width = {}, side = 'left', variant = 'sidebar', density = 'comfortable', }) => {
26
56
  const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
27
57
  const [internalMobileOpen, setInternalMobileOpen] = useState(false);
28
- const [isMobile, setIsMobile] = useState(false);
29
- // Detect mobile breakpoint
58
+ // Detect mobile breakpoint via the shared (SSR-safe) media-query hook
59
+ const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint - 1}px)`);
60
+ // Close the mobile drawer when leaving the mobile breakpoint
30
61
  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]);
62
+ if (!isMobile) {
63
+ setInternalMobileOpen(false);
64
+ onMobileOpenChange === null || onMobileOpenChange === void 0 ? void 0 : onMobileOpenChange(false);
65
+ }
66
+ }, [isMobile, onMobileOpenChange]);
45
67
  // Collapsed state
46
68
  const isCollapsedControlled = controlledCollapsed !== undefined;
47
69
  const collapsed = isCollapsedControlled ? controlledCollapsed : internalCollapsed;
@@ -76,15 +98,16 @@ const SidebarProvider = ({ children, defaultCollapsed = false, collapsed: contro
76
98
  toggleCollapsed,
77
99
  width: {
78
100
  expanded: width.expanded || 280,
79
- collapsed: width.collapsed || 64,
101
+ collapsed: width.collapsed || 60,
80
102
  },
81
103
  side,
82
104
  variant,
105
+ density,
83
106
  isMobileOpen,
84
107
  setMobileOpen,
85
108
  isMobile,
86
- }), [collapsed, setCollapsed, toggleCollapsed, width.expanded, width.collapsed, side, variant, isMobileOpen, setMobileOpen, isMobile]);
109
+ }), [collapsed, setCollapsed, toggleCollapsed, width.expanded, width.collapsed, side, variant, density, isMobileOpen, setMobileOpen, isMobile]);
87
110
  return (jsx(SidebarContext.Provider, { value: contextValue, children: children }));
88
111
  };
89
112
 
90
- export { AccordionNavProvider, SidebarProvider, useAccordionNavContext, useSidebarContext };
113
+ export { AccordionNavProvider, SidebarDensityProvider, SidebarProvider, useAccordionNavContext, useSidebarCollapsed, useSidebarContext, useSidebarDensity };
@@ -1,14 +1,12 @@
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 { useSidebarContext } from './SidebarContext.js';
4
+ import { useSidebarCollapsed, useSidebarDensity } from './SidebarContext.js';
5
5
 
6
6
  const SidebarFooter = React__default.forwardRef(({ children, className, ...props }, ref) => {
7
- const sidebarContext = useSidebarContext();
8
- const collapsed = (sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.collapsed) || false;
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-3", className), ...props, children: children }));
7
+ useSidebarCollapsed();
8
+ useSidebarDensity();
9
+ return (jsx("div", { ref: ref, className: cn("flex-shrink-0 border-t border-[var(--color-border)] p-2", className), ...props, children: children }));
12
10
  });
13
11
  SidebarFooter.displayName = "SidebarFooter";
14
12
 
@@ -1,24 +1,27 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import React__default from 'react';
3
3
  import { XMarkIcon } from '@heroicons/react/24/outline';
4
4
  import { cn } from '../../../utils/cn.js';
5
- import { useSidebarContext } from './SidebarContext.js';
5
+ import { useSidebarContext, useSidebarCollapsed } from './SidebarContext.js';
6
6
  import { SidebarToggle } from './SidebarToggle.js';
7
7
  import { Button } from '../Button/Button.js';
8
8
 
9
9
  const SidebarHeader = React__default.forwardRef(({ logo, showToggle = true, customToggle, children, className, ...props }, ref) => {
10
10
  const sidebarContext = useSidebarContext();
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;
11
+ const { isMobile, isMobileOpen } = useSidebarCollapsed();
14
12
  // If children are provided, use them instead of default layout
15
13
  if (children) {
16
14
  return (jsx("div", { ref: ref, className: cn("flex-shrink-0 border-b border-[var(--color-border)]", className), ...props, children: children }));
17
15
  }
18
- const isCollapsedDesktop = collapsed && !isMobile;
19
- return (jsx("div", { ref: ref, className: cn("flex items-center flex-shrink-0 h-14 border-b border-[var(--color-border)] overflow-hidden", isCollapsedDesktop ? "justify-center px-2" : "justify-between px-3", className), ...props, children: isCollapsedDesktop ? (
20
- /* Collapsed: only show the toggle (expand) button centered */
21
- showToggle && (customToggle || jsx(SidebarToggle, {}))) : (jsxs(Fragment, { children: [jsx("div", { className: "flex items-center min-w-0 flex-1", children: logo }), isMobile && isMobileOpen && (jsx(Button, { variant: "ghost", size: "sm", className: "size-7 p-0 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]", onClick: () => sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.setMobileOpen(false), "aria-label": "Close navigation", children: jsx(XMarkIcon, { className: "size-4" }) })), !isMobile && showToggle && (jsx("div", { className: "flex-shrink-0", children: customToggle || jsx(SidebarToggle, {}) }))] })) }));
16
+ return (jsxs("div", { ref: ref, className: cn("flex items-center flex-shrink-0 h-14 border-b border-[var(--color-border)] overflow-visible relative transition-[padding,background-color,border-color] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]",
17
+ // Padding mirrors Sidebar.Content so the header logo sits on the
18
+ // same horizontal track as the NavItem rows below it. px-3 (12px)
19
+ // on the 60px collapsed rail leaves a 36px interior a 32px logo
20
+ // (size-8) lands with 2px slack on each side; a 36px logo (size-9)
21
+ // fills the interior exactly. Left-pinned in both states (no
22
+ // `justify-*` toggle) so nothing jumps mid-animation. Same value
23
+ // for collapsed + expanded → no padding transition needed.
24
+ "px-3", className), ...props, children: [jsx("div", { className: "flex items-center min-w-0 flex-1", children: logo }), isMobile && isMobileOpen && (jsx(Button, { variant: "ghost", size: "sm", className: "size-7 p-0 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]", onClick: () => sidebarContext === null || sidebarContext === void 0 ? void 0 : sidebarContext.setMobileOpen(false), "aria-label": "Close navigation", children: jsx(XMarkIcon, { className: "size-4" }) })), !isMobile && showToggle && (customToggle || jsx(SidebarToggle, {}))] }));
22
25
  });
23
26
  SidebarHeader.displayName = "SidebarHeader";
24
27
 
@@ -1,16 +1,47 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import React__default from 'react';
3
3
  import { cn } from '../../../utils/cn.js';
4
4
  import { AnimatePresence, motion } from 'motion/react';
5
- import { useSidebarContext } from './SidebarContext.js';
5
+ import { useSidebarCollapsed } from './SidebarContext.js';
6
6
 
7
- const SidebarLogo = React__default.forwardRef(({ icon, text, className, ...props }, ref) => {
7
+ const SidebarLogo = React__default.forwardRef(({ expanded, collapsed, expandedSrc, collapsedSrc, expandedImageClassName, collapsedImageClassName, alt = "", icon, text, className, ...props }, ref) => {
8
8
  var _a, _b;
9
- const context = useSidebarContext();
10
- const collapsed = (_a = context === null || context === void 0 ? void 0 : context.collapsed) !== null && _a !== void 0 ? _a : false;
11
- const isMobile = (_b = context === null || context === void 0 ? void 0 : context.isMobile) !== null && _b !== void 0 ? _b : false;
12
- const isCollapsedView = collapsed && !isMobile;
13
- return (jsxs("div", { ref: ref, className: cn("flex items-center gap-2 overflow-hidden", className), ...props, children: [jsx("div", { className: "flex-shrink-0", children: icon }), jsx(AnimatePresence, { initial: false, children: !isCollapsedView && text && (jsx(motion.div, { className: "overflow-hidden whitespace-nowrap", initial: { opacity: 0, x: -8 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -8 }, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] }, children: typeof text === "string" ? (jsx("span", { className: "text-lg font-bold text-[var(--color-text-primary)]", children: text })) : (text) }, "logo-text")) })] }));
9
+ const { isCollapsedView } = useSidebarCollapsed();
10
+ // Auto-render <img> for the src convenience API. ReactNode props still win
11
+ // (full control); src is the easy path for plain image/SVG assets.
12
+ //
13
+ // Defaults:
14
+ // - Collapsed: size-9 (36px) square, object-cover — matches the nav icon
15
+ // track so the collapsed logo aligns visually with NavItem rows below.
16
+ // - Expanded: h-8 w-auto object-contain — typical wordmark sizing.
17
+ // Override via `collapsedImageClassName` / `expandedImageClassName`
18
+ // (tailwind-merge resolves conflicts in favor of the user's classes).
19
+ const expandedNode = expanded !== null && expanded !== void 0 ? expanded : (expandedSrc ? (jsx("img", { src: expandedSrc, alt: alt, className: cn("h-8 w-auto object-contain", expandedImageClassName) })) : undefined);
20
+ const collapsedNode = collapsed !== null && collapsed !== void 0 ? collapsed : (collapsedSrc ? (jsx("img", { src: collapsedSrc, alt: alt, className: cn("size-9 object-cover", collapsedImageClassName) })) : undefined);
21
+ // New API takes precedence: if the user passed `expanded`/`collapsed`
22
+ // (ReactNode) OR their `*Src` shortcuts, render those (cross-faded).
23
+ // Otherwise fall back to the legacy icon-plus-animated-text rendering so
24
+ // existing consumers keep working.
25
+ const hasNewApi = expandedNode !== undefined || collapsedNode !== undefined;
26
+ if (hasNewApi) {
27
+ // Resolve the logo for the current state, falling all the way back to
28
+ // the legacy `icon` so a partial new-API usage still has something to
29
+ // show. Per-state fallback: collapsed → expanded → icon, and vice versa.
30
+ const currentLogo = isCollapsedView
31
+ ? (_a = collapsedNode !== null && collapsedNode !== void 0 ? collapsedNode : expandedNode) !== null && _a !== void 0 ? _a : icon
32
+ : (_b = expandedNode !== null && expandedNode !== void 0 ? expandedNode : collapsedNode) !== null && _b !== void 0 ? _b : icon;
33
+ return (jsx("div", { ref: ref,
34
+ // No `overflow-hidden`: would clip wide logos in collapsed mode.
35
+ // Sidebar itself is `overflow-visible`.
36
+ className: cn("flex items-center", className), ...props, children: jsx(AnimatePresence, { mode: "wait", initial: false, children: jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] }, className: "flex items-center", children: currentLogo }, isCollapsedView ? "collapsed" : "expanded") }) }));
37
+ }
38
+ // Legacy API: icon + animated text.
39
+ return (jsxs("div", { ref: ref,
40
+ // No `overflow-hidden` here — that would clip the icon when the
41
+ // sidebar is collapsed and horizontal space is tight. The text
42
+ // collapse animation owns its own `overflow-hidden` on the motion.div
43
+ // below, which is the only place it's actually needed.
44
+ className: cn("flex items-center gap-2", className), ...props, children: [jsx("div", { className: "flex-shrink-0", children: icon }), jsx(AnimatePresence, { initial: false, children: !isCollapsedView && text && (jsx(motion.div, { className: "overflow-hidden whitespace-nowrap", initial: { opacity: 0, x: -8 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -8 }, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] }, children: typeof text === "string" ? (jsx("span", { className: "text-base font-semibold tracking-tight text-[var(--color-text-primary)]", children: text })) : (text) }, "logo-text")) })] }));
14
45
  });
15
46
  SidebarLogo.displayName = "SidebarLogo";
16
47
 
@@ -1,10 +1,11 @@
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
+ import { useSidebarDensity, SidebarDensityProvider, AccordionNavProvider } from './SidebarContext.js';
5
5
 
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 }) }));
6
+ const SidebarNav = React__default.forwardRef(({ accordion = false, density, children, className, ...props }, ref) => {
7
+ const resolvedDensity = density !== null && density !== void 0 ? density : useSidebarDensity();
8
+ return (jsx("nav", { ref: ref, className: cn('flex-1', resolvedDensity === 'compact' ? 'space-y-0.5' : 'space-y-1', className), "aria-label": "Sidebar", "data-density": resolvedDensity, ...props, children: jsx(SidebarDensityProvider, { density: density, children: jsx(AccordionNavProvider, { accordion: accordion, children: children }) }) }));
8
9
  });
9
10
  SidebarNav.displayName = 'SidebarNav';
10
11