@flikk/ui 1.0.0-beta.26 → 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 (131) hide show
  1. package/dist/components/ai/PromptInput/PromptInput.js +3 -0
  2. package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -0
  3. package/dist/components/core/Button/Button.theme.js +1 -1
  4. package/dist/components/core/Calendar/Calendar.theme.js +1 -1
  5. package/dist/components/core/CommandPalette/CommandItem.js +4 -5
  6. package/dist/components/core/CommandPalette/CommandPalette.js +1 -1
  7. package/dist/components/core/CommandPalette/CommandPalette.theme.js +11 -13
  8. package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +0 -2
  9. package/dist/components/core/ContextMenu/ContextMenu.theme.js +13 -14
  10. package/dist/components/core/DotSeparator/DotSeparator.d.ts +3 -0
  11. package/dist/components/core/DotSeparator/DotSeparator.js +19 -0
  12. package/dist/components/core/DotSeparator/DotSeparator.theme.d.ts +2 -0
  13. package/dist/components/core/DotSeparator/DotSeparator.theme.js +10 -0
  14. package/dist/components/core/DotSeparator/DotSeparator.types.d.ts +25 -0
  15. package/dist/components/core/DotSeparator/index.d.ts +3 -0
  16. package/dist/components/core/Dropdown/Dropdown.theme.js +2 -3
  17. package/dist/components/core/Dropdown/DropdownItem.js +1 -1
  18. package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
  19. package/dist/components/core/MenuItem/MenuItem.js +5 -2
  20. package/dist/components/core/MenuItem/MenuItem.theme.d.ts +6 -0
  21. package/dist/components/core/MenuItem/MenuItem.theme.js +19 -16
  22. package/dist/components/core/MenuItem/MenuItem.types.d.ts +2 -0
  23. package/dist/components/core/NavItem/NavItem.js +20 -4
  24. package/dist/components/core/NavItem/NavItem.theme.js +13 -2
  25. package/dist/components/core/Popover/Popover.types.d.ts +6 -2
  26. package/dist/components/core/Popover/PopoverBody.js +10 -3
  27. package/dist/components/core/Segmented/Segmented.theme.js +1 -1
  28. package/dist/components/core/Sidebar/Sidebar.d.ts +2 -0
  29. package/dist/components/core/Sidebar/Sidebar.js +15 -7
  30. package/dist/components/core/Sidebar/Sidebar.types.d.ts +92 -4
  31. package/dist/components/core/Sidebar/SidebarCard.d.ts +25 -0
  32. package/dist/components/core/Sidebar/SidebarCard.js +68 -0
  33. package/dist/components/core/Sidebar/SidebarCard.theme.d.ts +8 -0
  34. package/dist/components/core/Sidebar/SidebarCard.theme.js +18 -0
  35. package/dist/components/core/Sidebar/SidebarContent.js +5 -1
  36. package/dist/components/core/Sidebar/SidebarContext.js +1 -1
  37. package/dist/components/core/Sidebar/SidebarHeader.js +10 -2
  38. package/dist/components/core/Sidebar/SidebarLogo.js +38 -3
  39. package/dist/components/core/Sidebar/SidebarSearch.d.ts +22 -0
  40. package/dist/components/core/Sidebar/SidebarSearch.js +135 -0
  41. package/dist/components/core/Sidebar/SidebarToggle.js +1 -1
  42. package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
  43. package/dist/components/core/Sidebar/index.d.ts +3 -1
  44. package/dist/components/core/Tag/Tag.js +29 -5
  45. package/dist/components/core/Tag/Tag.theme.js +13 -8
  46. package/dist/components/core/Tag/Tag.types.d.ts +7 -0
  47. package/dist/components/core/Tooltip/Tooltip.js +1 -1
  48. package/dist/components/core/Tooltip/Tooltip.theme.js +1 -2
  49. package/dist/components/core/index.d.ts +1 -0
  50. package/dist/components/core/index.js +4 -0
  51. package/dist/components/data-display/GanttChart/GanttChart.d.ts +1 -1
  52. package/dist/components/data-display/GanttChart/GanttChart.js +31 -4
  53. package/dist/components/data-display/GanttChart/GanttChart.theme.js +19 -17
  54. package/dist/components/data-display/GanttChart/GanttChart.types.d.ts +19 -0
  55. package/dist/components/data-display/GanttChart/GanttChart.utils.js +1 -1
  56. package/dist/components/data-display/GanttChart/GanttChartPanel.d.ts +1 -1
  57. package/dist/components/data-display/GanttChart/GanttChartPanel.js +3 -3
  58. package/dist/components/data-display/GanttChart/GanttDependencyArrows.js +7 -4
  59. package/dist/components/data-display/GanttChart/GanttMilestone.js +5 -5
  60. package/dist/components/data-display/GanttChart/GanttTablePanel.js +1 -1
  61. package/dist/components/data-display/GanttChart/GanttTableTaskRow.js +2 -2
  62. package/dist/components/data-display/GanttChart/GanttTaskBar.js +37 -16
  63. package/dist/components/data-display/GanttChart/GanttTimelineGrid.js +2 -1
  64. package/dist/components/data-display/GanttChart/GanttTimelineHeader.js +35 -11
  65. package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
  66. package/dist/components/data-display/GanttChart/hooks/useGanttDrag.js +3 -2
  67. package/dist/components/data-display/GanttChart/index.d.ts +1 -1
  68. package/dist/components/data-display/Table/Table.theme.js +3 -5
  69. package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
  70. package/dist/components/effects/Aurora/Aurora.js +297 -131
  71. package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
  72. package/dist/components/forms/Combobox/Combobox.js +1 -1
  73. package/dist/components/forms/Combobox/Combobox.theme.js +2 -4
  74. package/dist/components/forms/CronInput/CronInput.js +1 -1
  75. package/dist/components/forms/CronInput/CronInput.theme.js +14 -23
  76. package/dist/components/forms/DatePicker/DatePicker.theme.js +11 -18
  77. package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
  78. package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +6 -13
  79. package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
  80. package/dist/components/forms/FileUpload/FileUpload.animations.d.ts +29 -0
  81. package/dist/components/forms/FileUpload/FileUpload.animations.js +19 -0
  82. package/dist/components/forms/FileUpload/FileUpload.js +87 -48
  83. package/dist/components/forms/FileUpload/FileUpload.theme.d.ts +2 -10
  84. package/dist/components/forms/FileUpload/FileUpload.theme.js +16 -30
  85. package/dist/components/forms/FileUpload/FileUpload.types.d.ts +26 -10
  86. package/dist/components/forms/Input/Input.theme.js +13 -10
  87. package/dist/components/forms/InputCounter/InputCounter.theme.js +1 -1
  88. package/dist/components/forms/Mention/Mention.js +152 -57
  89. package/dist/components/forms/Mention/Mention.theme.js +16 -23
  90. package/dist/components/forms/Mention/Mention.types.d.ts +12 -12
  91. package/dist/components/forms/Mention/Mention.utils.js +32 -4
  92. package/dist/components/forms/RichTextEditor/RichTextEditor.js +3 -3
  93. package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +5 -5
  94. package/dist/components/forms/Select/Select.js +2 -2
  95. package/dist/components/forms/Select/Select.theme.js +3 -3
  96. package/dist/components/forms/TimePicker/TimePickerContent.js +3 -0
  97. package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
  98. package/dist/components/forms/forms.theme.js +21 -16
  99. package/dist/components/forms/index.js +3 -0
  100. package/dist/components/layout/AppShell/AppShell.d.ts +10 -0
  101. package/dist/components/layout/AppShell/AppShell.js +84 -0
  102. package/dist/components/layout/AppShell/AppShell.theme.d.ts +3 -0
  103. package/dist/components/layout/AppShell/AppShell.theme.js +42 -0
  104. package/dist/components/layout/AppShell/AppShell.types.d.ts +127 -0
  105. package/dist/components/layout/AppShell/AppShellContext.d.ts +14 -0
  106. package/dist/components/layout/AppShell/AppShellContext.js +19 -0
  107. package/dist/components/layout/AppShell/AppShellFooter.d.ts +3 -0
  108. package/dist/components/layout/AppShell/AppShellFooter.js +12 -0
  109. package/dist/components/layout/AppShell/AppShellHeader.d.ts +3 -0
  110. package/dist/components/layout/AppShell/AppShellHeader.js +12 -0
  111. package/dist/components/layout/AppShell/AppShellMain.d.ts +3 -0
  112. package/dist/components/layout/AppShell/AppShellMain.js +19 -0
  113. package/dist/components/layout/AppShell/AppShellRightPanel.d.ts +3 -0
  114. package/dist/components/layout/AppShell/AppShellRightPanel.js +16 -0
  115. package/dist/components/layout/AppShell/AppShellSidebar.d.ts +3 -0
  116. package/dist/components/layout/AppShell/AppShellSidebar.js +16 -0
  117. package/dist/components/layout/AppShell/AppShellTopbar.d.ts +3 -0
  118. package/dist/components/layout/AppShell/AppShellTopbar.js +12 -0
  119. package/dist/components/layout/AppShell/index.d.ts +3 -0
  120. package/dist/components/layout/FormLayout/FormLayout.js +3 -0
  121. package/dist/components/layout/index.d.ts +1 -0
  122. package/dist/components/layout/index.js +2 -0
  123. package/dist/hooks/useSelectPortal.d.ts +10 -2
  124. package/dist/hooks/useSelectPortal.js +45 -24
  125. package/dist/index.js +6 -0
  126. package/dist/registry.json +10021 -0
  127. package/dist/styles.css +1 -1
  128. package/dist/utils/dateUtils.js +11 -1
  129. package/package.json +4 -2
  130. package/src/global.scss +29 -8
  131. package/src/styles/theme.css +51 -12
@@ -50,6 +50,7 @@ import '../../core/Dropdown/DropdownItem.js';
50
50
  import '../../core/Dropdown/DropdownSection.js';
51
51
  import '../../core/Dropdown/DropdownSeparator.js';
52
52
  import '../../core/Dropdown/Dropdown.theme.js';
53
+ import '../../core/DotSeparator/DotSeparator.js';
53
54
  import '../../core/Kbd/Kbd.js';
54
55
  import '../../core/MenuItem/MenuItem.js';
55
56
  import '../../core/Link/Link.js';
@@ -100,6 +101,8 @@ import '../../core/Sidebar/SidebarToggle.js';
100
101
  import '../../core/Sidebar/SidebarMobileTrigger.js';
101
102
  import '../../core/Sidebar/SidebarUserProfile.js';
102
103
  import '../../core/Sidebar/SidebarLogo.js';
104
+ import '../../core/Sidebar/SidebarSearch.js';
105
+ import '../../core/Sidebar/SidebarCard.js';
103
106
  import '../../core/Sidebar/SidebarContext.js';
104
107
  import '../../core/OfflineIndicator/OfflineIndicator.js';
105
108
  import '../../core/ContextMenu/ContextMenu.js';
@@ -32,6 +32,7 @@ import '../../core/Dropdown/DropdownItem.js';
32
32
  import '../../core/Dropdown/DropdownSection.js';
33
33
  import '../../core/Dropdown/DropdownSeparator.js';
34
34
  import '../../core/Dropdown/Dropdown.theme.js';
35
+ import '../../core/DotSeparator/DotSeparator.js';
35
36
  import '../../core/Kbd/Kbd.js';
36
37
  import '../../core/MenuItem/MenuItem.js';
37
38
  import '../../core/Link/Link.js';
@@ -82,6 +83,8 @@ import '../../core/Sidebar/SidebarToggle.js';
82
83
  import '../../core/Sidebar/SidebarMobileTrigger.js';
83
84
  import '../../core/Sidebar/SidebarUserProfile.js';
84
85
  import '../../core/Sidebar/SidebarLogo.js';
86
+ import '../../core/Sidebar/SidebarSearch.js';
87
+ import '../../core/Sidebar/SidebarCard.js';
85
88
  import '../../core/Sidebar/SidebarContext.js';
86
89
  import '../../core/OfflineIndicator/OfflineIndicator.js';
87
90
  import '../../core/ContextMenu/ContextMenu.js';
@@ -3,7 +3,7 @@
3
3
  */
4
4
  const buttonTheme = {
5
5
  // Base styles for the button element
6
- baseStyle: "relative inline-flex items-center justify-center gap-2 focus:outline-none rounded-[var(--button-radius)] overflow-hidden cursor-pointer transition-all duration-300 ease-in-out min-w-fit flex-shrink-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 cursor-pointer",
6
+ baseStyle: "relative inline-flex items-center justify-center gap-2 focus:outline-none rounded-[var(--button-radius)] overflow-hidden cursor-pointer transition-all duration-300 ease-in-out min-w-fit flex-shrink-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 cursor-pointer backdrop-blur-sm",
7
7
  // Visual style variants - STRUCTURE only (no colors)
8
8
  variantStyles: {
9
9
  filled: "border hover:brightness-[0.9] dark:hover:brightness-[1.15]",
@@ -3,7 +3,7 @@
3
3
  * Uses design tokens and data-[state=*] selectors
4
4
  */
5
5
  const calendarTheme = {
6
- baseStyle: "select-none w-full bg-white dark:bg-[var(--color-neutral-900)]",
6
+ baseStyle: "select-none w-full bg-[var(--color-surface)]",
7
7
  headerStyle: "flex items-center justify-between mb-4",
8
8
  titleStyle: "text-sm font-semibold text-[var(--color-text-primary)] " + "",
9
9
  headerDropdownsStyle: "grid grid-cols-2 items-center gap-2 flex-1 justify-center mx-3",
@@ -1,8 +1,7 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx } from 'react/jsx-runtime';
2
2
  import React__default from 'react';
3
- import { cn } from '../../../utils/cn.js';
3
+ import { MenuItem } from '../MenuItem/MenuItem.js';
4
4
  import { Kbd } from '../Kbd/Kbd.js';
5
- import { commandPaletteTheme } from './CommandPalette.theme.js';
6
5
 
7
6
  /**
8
7
  * Map string shortcuts to KbdKey types
@@ -100,7 +99,7 @@ const Shortcut = ({ keys }) => {
100
99
  * />
101
100
  * ```
102
101
  */
103
- const CommandItem = ({ command, index, isHighlighted, contentStart, contentEnd, className, onSelect, onMouseEnter, theme = commandPaletteTheme, }) => {
102
+ const CommandItem = ({ command, index, isHighlighted, contentStart, contentEnd, className, onSelect, onMouseEnter, }) => {
104
103
  const handleClick = () => {
105
104
  if (!command.disabled) {
106
105
  onSelect(command);
@@ -126,7 +125,7 @@ const CommandItem = ({ command, index, isHighlighted, contentStart, contentEnd,
126
125
  }
127
126
  return null;
128
127
  };
129
- return (jsxs("div", { "data-command-index": index, "data-highlighted": isHighlighted, "data-disabled": command.disabled || false, className: cn(theme.commandItem, className), role: "option", "aria-selected": isHighlighted, "aria-disabled": command.disabled || false, onClick: handleClick, onMouseEnter: handleMouseEnter, children: [finalContentStart && (jsx("div", { className: "flex-shrink-0", children: finalContentStart })), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "text-base truncate", children: command.label }), command.subtitle && (jsx("div", { className: "text-base opacity-70 truncate mt-0", children: command.subtitle }))] }), renderContentEnd()] }));
128
+ return (jsx(MenuItem, { role: "option", selected: isHighlighted, disabled: command.disabled, startContent: finalContentStart, subtitle: command.subtitle, endContent: renderContentEnd(), onActivate: handleClick, onMouseEnter: handleMouseEnter, "aria-selected": isHighlighted, "data-command-index": index, "data-highlighted": isHighlighted, className: className, children: command.label }));
130
129
  };
131
130
 
132
131
  export { CommandItem };
@@ -211,7 +211,7 @@ const CommandPalette = React__default.forwardRef(({ isOpen, onClose, commands =
211
211
  // Otherwise render flat list
212
212
  return displayCommands.map((command, index) => (jsx(CommandItem, { command: command, index: index, isHighlighted: index === highlightedIndex, onSelect: handleSelectCommand, onMouseEnter: setHighlightedIndex, theme: theme }, command.id)));
213
213
  };
214
- return (jsxs(Overlay, { ref: ref, isOpen: isOpen, onClose: onClose, closeOnOverlayClick: closeOnOverlayClick, closeOnEsc: closeOnEsc, position: "top", overlayClassName: overlayClassName, contentClassName: cn(theme.container, className), animations: {
214
+ return (jsxs(Overlay, { ref: ref, isOpen: isOpen, onClose: onClose, closeOnOverlayClick: closeOnOverlayClick, closeOnEsc: closeOnEsc, position: "top", overlayClassName: cn(theme.overlay, overlayClassName), contentClassName: cn(theme.container, className), animations: {
215
215
  overlay: overlayAnimations,
216
216
  content: containerAnimations,
217
217
  }, children: [jsx("div", { className: theme.searchWrapper, children: jsxs("div", { className: "flex items-center gap-3", children: [jsx(MagnifyingGlassIcon, { className: "w-5 h-5 text-[var(--color-text-muted)]", strokeWidth: 2 }), jsx("input", { ref: searchInputRef, type: "text", className: theme.searchInput, placeholder: placeholder, value: searchValue, onChange: handleSearchChange, autoComplete: "off", autoCorrect: "off", spellCheck: false, "aria-label": "Search commands" })] }) }), jsx(ScrollArea, { ref: listContainerRef, type: "hover", className: "overscroll-contain px-1", style: {
@@ -1,8 +1,12 @@
1
1
  const commandPaletteTheme = {
2
- overlay: "fixed inset-0 bg-[var(--overlay-bg)] backdrop-blur-[var(--overlay-blur)] z-50 " +
3
- "flex items-start justify-center pt-[min(10vh,4rem)] px-4",
2
+ // Applied to the Overlay backdrop (positioning/z-index come from Overlay).
3
+ // Overrides the shared 65%-black scrim (--overlay-bg) with a lighter dim so
4
+ // real page content sits behind the container — that's what the container's
5
+ // backdrop-blur frosts into the glass effect. A heavy scrim would leave only
6
+ // a flat dark layer to blur, making backdrop-blur look like it does nothing.
7
+ overlay: "bg-black/30",
4
8
  container: "relative w-full max-h-[calc(100vh-2rem)] max-w-lg " +
5
- "bg-[var(--color-background)] rounded-[var(--modal-radius)] shadow-2xl shadow-black/30 " +
9
+ "bg-[var(--color-surface-overlay)] backdrop-blur-md rounded-[var(--modal-radius)] shadow-2xl shadow-black/30 " +
6
10
  "border border-[var(--color-border)] ring-4 ring-[var(--color-background)]/20 " +
7
11
  "flex flex-col my-4 min-h-0 overflow-hidden " +
8
12
  // Dark mode
@@ -11,18 +15,12 @@ const commandPaletteTheme = {
11
15
  searchInput: "w-full px-0 py-0 text-sm bg-transparent border-none outline-none " +
12
16
  "text-[var(--color-text-primary)] placeholder:text-[var(--color-text-placeholder)] " +
13
17
  "focus:outline-none focus:ring-0 " +
14
- // Dark mode
15
- "dark:text-[var(--color-neutral-200)] dark:placeholder:text-[var(--color-neutral-500)]",
18
+ // Dark mode — matches Input's controlBaseStyle text/placeholder tokens
19
+ "dark:text-[var(--color-text-primary)] dark:placeholder:text-[var(--color-neutral-500)]",
16
20
  groupLabel: "px-4 py-1 text-xs font-semibold text-[var(--color-text-muted)]/50 uppercase " +
17
21
  "dark:text-[var(--color-neutral-500)]",
18
- commandItem: "flex items-center gap-3 px-4 py-2 rounded-[var(--radius-base)] cursor-pointer transition-colors " +
19
- "hover:bg-[var(--color-primary)] " +
20
- "data-[highlighted=true]:bg-[var(--color-primary)] data-[highlighted=true]:hover:bg-[var(--color-primary)] " +
21
- "data-[highlighted=true]:text-[var(--color-primary-contrast)] " +
22
- "data-[disabled=true]:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:hover:bg-transparent " +
23
- // Dark mode - adjust hover state
24
- "dark:text-[var(--color-neutral-200)] dark:hover:bg-[var(--color-primary-600)] " +
25
- "dark:data-[highlighted=true]:bg-[var(--color-primary-600)]",
22
+ // Command rows render via the shared MenuItem primitive and use its default
23
+ // styling (subtle highlight + standard padding) no per-row theme here.
26
24
  emptyState: "px-4 py-12 text-center text-sm text-[var(--color-text-muted)] " +
27
25
  "dark:text-[var(--color-neutral-500)]",
28
26
  footer: "border-t border-[var(--color-border)] px-4 py-2 text-xs text-[var(--color-text-muted)]font-medium",
@@ -86,8 +86,6 @@ export interface CommandPaletteThemeOverrides {
86
86
  searchInput?: string;
87
87
  /** Style for group labels */
88
88
  groupLabel?: string;
89
- /** Style for command items (uses data-[highlighted] and data-[disabled] selectors) */
90
- commandItem?: string;
91
89
  /** Style for empty state */
92
90
  emptyState?: string;
93
91
  /** Style for the footer area */
@@ -5,21 +5,20 @@ const contextMenuTheme = {
5
5
  ...dropdownTheme,
6
6
  // Content uses the same styling as Dropdown menu
7
7
  contentStyle: dropdownTheme.menuStyle.replace("flex", "flex-col p-1"),
8
- // Items use exact same styling as Dropdown items (with space for check icon)
9
- itemStyle: `${dropdownTheme.itemBaseStyle} data-[checked=true]:pl-8`,
10
- // Checked state - tick icon positioning (absolute left)
11
- itemCheckedStyle: "absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--color-text-primary)] " +
12
- "dark:text-[var(--color-neutral-200)]",
13
- // Submenu trigger - based on Dropdown item style with chevron space
14
- subTriggerStyle: `${dropdownTheme.itemBaseStyle} pr-8 data-[state=open]:bg-[var(--color-surface-hover)] ` +
15
- "dark:data-[state=open]:bg-[var(--color-neutral-700)]",
16
- // Submenu content - matches main dropdown menu style
8
+ // Items use exact same styling as Dropdown items. The left padding for the
9
+ // check icon is applied per-item in ContextMenuItem (className) when checked.
10
+ itemStyle: dropdownTheme.itemBaseStyle,
11
+ // Checked state - tick icon positioning (absolute left).
12
+ // --color-text-primary flips for dark mode on its own.
13
+ itemCheckedStyle: "absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--color-text-primary)]",
14
+ // Submenu trigger - based on Dropdown item style with chevron space.
15
+ // --color-surface-hover is a translucent tint that lightens in dark on its own.
16
+ subTriggerStyle: `${dropdownTheme.itemBaseStyle} pr-8 data-[state=open]:bg-[var(--color-surface-hover)]`,
17
+ // Submenu content - floating anchored overlay, matches main dropdown menu
17
18
  subContentStyle: "min-w-[180px] max-h-[400px] overflow-y-auto rounded-[var(--dropdown-radius)] border border-[var(--color-border)] " +
18
- "bg-[var(--color-background)] shadow-real-xl flex flex-col p-1 focus:outline-none " +
19
- "dark:bg-[var(--color-background-secondary)]",
20
- // Separator - horizontal divider
21
- separatorStyle: "h-px bg-[var(--color-border)] my-1 -mx-1 " +
22
- "dark:bg-[var(--color-neutral-700)]",
19
+ "bg-[var(--color-surface-overlay)] shadow-real-xl flex flex-col p-1 focus:outline-none",
20
+ // Separator - horizontal divider (--color-border flips for dark on its own)
21
+ separatorStyle: "h-px bg-[var(--color-border)] my-1 -mx-1",
23
22
  // Label - section header (matches Dropdown section title)
24
23
  labelStyle: dropdownTheme.sectionTitleStyle,
25
24
  };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { DotSeparatorProps } from './DotSeparator.types';
3
+ export declare const DotSeparator: React.ForwardRefExoticComponent<DotSeparatorProps & React.RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,19 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import React__default, { useMemo } from 'react';
3
+ import { cn } from '../../../utils/cn.js';
4
+ import { dotSeparatorTheme } from './DotSeparator.theme.js';
5
+
6
+ const DotSeparator = React__default.forwardRef(({ size = 'md', className, theme: customTheme = {}, ...props }, ref) => {
7
+ const theme = useMemo(() => ({
8
+ ...dotSeparatorTheme,
9
+ ...customTheme,
10
+ sizes: {
11
+ ...dotSeparatorTheme.sizes,
12
+ ...customTheme.sizes,
13
+ },
14
+ }), [customTheme]);
15
+ return (jsx("span", { ref: ref, "aria-hidden": "true", className: cn(theme.baseStyle, theme.sizes[size], className), ...props }));
16
+ });
17
+ DotSeparator.displayName = 'DotSeparator';
18
+
19
+ export { DotSeparator };
@@ -0,0 +1,2 @@
1
+ import { DotSeparatorTheme } from './DotSeparator.types';
2
+ export declare const dotSeparatorTheme: DotSeparatorTheme;
@@ -0,0 +1,10 @@
1
+ const dotSeparatorTheme = {
2
+ baseStyle: 'inline-block shrink-0 rounded-full align-middle bg-[var(--color-text-muted)]',
3
+ sizes: {
4
+ sm: 'size-0.5 mx-1', // 2px dot
5
+ md: 'size-1 mx-1.5', // 4px dot
6
+ lg: 'size-1.5 mx-2', // 6px dot
7
+ },
8
+ };
9
+
10
+ export { dotSeparatorTheme };
@@ -0,0 +1,25 @@
1
+ import { HTMLAttributes } from 'react';
2
+ export type DotSeparatorSize = 'sm' | 'md' | 'lg';
3
+ export interface DotSeparatorTheme {
4
+ baseStyle: string;
5
+ sizes: Record<DotSeparatorSize, string>;
6
+ }
7
+ export interface DotSeparatorThemeOverrides {
8
+ baseStyle?: string;
9
+ sizes?: Partial<Record<DotSeparatorSize, string>>;
10
+ }
11
+ export interface DotSeparatorProps extends HTMLAttributes<HTMLSpanElement> {
12
+ /**
13
+ * The size of the dot (also scales its horizontal spacing)
14
+ * @default 'md'
15
+ */
16
+ size?: DotSeparatorSize;
17
+ /**
18
+ * Custom class name for the dot
19
+ */
20
+ className?: string;
21
+ /**
22
+ * Component-level theme overrides
23
+ */
24
+ theme?: DotSeparatorThemeOverrides;
25
+ }
@@ -0,0 +1,3 @@
1
+ export { DotSeparator } from './DotSeparator';
2
+ export type { DotSeparatorProps, DotSeparatorSize, DotSeparatorTheme, DotSeparatorThemeOverrides, } from './DotSeparator.types';
3
+ export { dotSeparatorTheme } from './DotSeparator.theme';
@@ -12,9 +12,8 @@ const dropdownTheme = {
12
12
  sectionTitleStyle: "px-2 py-1.5 text-xs font-semibold text-[var(--color-text-muted)] uppercase tracking-tight",
13
13
  // Section container styling
14
14
  sectionStyle: "py-1",
15
- // Separator between sections
16
- separatorStyle: "my-1 h-px bg-[var(--color-border)] " +
17
- "dark:bg-[var(--color-neutral-700)]",
15
+ // Separator between sections (--color-border flips for dark mode on its own)
16
+ separatorStyle: "my-1 h-px bg-[var(--color-border)]",
18
17
  };
19
18
 
20
19
  export { dropdownTheme };
@@ -63,7 +63,7 @@ const DropdownItem = React__default.forwardRef(({ itemKey, children, className,
63
63
  // Build end content: user's endContent OR selection checkmark
64
64
  const resolvedEndContent = endContent ||
65
65
  (selectionMode !== "none" ? (jsx("div", { className: cn("flex items-center justify-center flex-shrink-0 w-5 h-5"), children: isSelected && (jsx(CheckIcon, { className: "size-4 text-[var(--color-text-secondary)]" })) })) : undefined);
66
- return (jsx(MenuItem, { ref: ref, role: itemRole, disabled: disabled, isDanger: isDanger, startContent: startContent, endContent: resolvedEndContent, subtitle: subtitle, shortcut: shortcut, onActivate: handleActivate, "data-selected": isSelected, ...(selectionMode !== "none" ? { "aria-checked": isSelected } : {}), className: className, theme: {
66
+ return (jsx(MenuItem, { ref: ref, role: itemRole, disabled: disabled, isDanger: isDanger, startContent: startContent, endContent: resolvedEndContent, subtitle: subtitle, shortcut: shortcut, onActivate: handleActivate, selected: isSelected, ...(selectionMode !== "none" ? { "aria-checked": isSelected } : {}), className: className, theme: {
67
67
  baseStyle: theme.itemBaseStyle,
68
68
  dangerStyle: theme.itemDangerStyle,
69
69
  focusStyle: theme.itemFocusStyle,
@@ -189,7 +189,7 @@ const DropdownMenu = React__default.forwardRef(({ children, className, animation
189
189
  }, "data-state": isOpen ? "open" : "closed", "data-placement": placement, ...domProps, children: children })) }));
190
190
  // Use portal if specified - wrap with dark class for dark mode support
191
191
  if (portal && isClient) {
192
- return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: menuContent }), document.body);
192
+ return createPortal(jsx("div", { "data-flikkui-portal": "", className: isDarkMode ? "dark" : undefined, children: menuContent }), document.body);
193
193
  }
194
194
  return menuContent;
195
195
  });
@@ -9,12 +9,15 @@ import { menuItemTheme } from './MenuItem.theme.js';
9
9
  * Handles visual layout, base styling, keyboard activation, and focus tracking.
10
10
  * Context-specific behavior (selection, checked state) is handled by consumers.
11
11
  */
12
- const MenuItem = React__default.forwardRef(({ children, className, startContent, endContent, subtitle, shortcut, disabled = false, isDanger = false, onActivate, theme: themeOverrides, role = "menuitem", ...props }, ref) => {
12
+ const MenuItem = React__default.forwardRef(({ children, className, startContent, endContent, subtitle, shortcut, disabled = false, isDanger = false, selected = false, onActivate, theme: themeOverrides, role = "menuitem", ...props }, ref) => {
13
13
  const [isFocused, setIsFocused] = useState(false);
14
14
  const theme = {
15
15
  ...menuItemTheme,
16
16
  ...themeOverrides,
17
17
  };
18
+ // Resolve state to plain utility classes (not data-[state] variants) so a
19
+ // consumer `className` always wins the cascade. See MenuItem.theme.ts.
20
+ const stateKey = disabled ? "disabled" : selected ? "selected" : "default";
18
21
  const handleClick = useCallback(() => {
19
22
  if (disabled)
20
23
  return;
@@ -26,7 +29,7 @@ const MenuItem = React__default.forwardRef(({ children, className, startContent,
26
29
  handleClick();
27
30
  }
28
31
  }, [handleClick]);
29
- return (jsxs("div", { ref: ref, role: role, tabIndex: disabled ? undefined : -1, "aria-disabled": disabled, "data-focused": isFocused, "data-disabled": disabled, className: cn(theme.baseStyle, theme.focusStyle, isDanger && theme.dangerStyle, className), onClick: handleClick, onKeyDown: handleKeyDown, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), ...props, children: [startContent && (jsx("div", { className: "flex items-center justify-center flex-shrink-0", children: startContent })), jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center justify-between gap-2", children: [jsx("span", { className: "truncate", children: children }), shortcut && (jsx("span", { className: "ml-auto flex-shrink-0", children: shortcut }))] }), subtitle && (typeof subtitle === 'string'
32
+ return (jsxs("div", { ref: ref, role: role, tabIndex: disabled ? undefined : -1, "aria-disabled": disabled, "data-focused": isFocused, "data-disabled": disabled, "data-selected": selected, className: cn(theme.baseStyle, theme.states[stateKey], isFocused && !disabled && !selected && theme.focusedStyle, isDanger && theme.dangerStyle, theme.focusStyle, className), onClick: handleClick, onKeyDown: handleKeyDown, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), ...props, children: [startContent && (jsx("div", { className: "flex items-center justify-center flex-shrink-0", children: startContent })), jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center justify-between gap-2", children: [jsx("span", { className: "truncate", children: children }), shortcut && (jsx("span", { className: "ml-auto flex-shrink-0", children: shortcut }))] }), subtitle && (typeof subtitle === 'string'
30
33
  ? jsx("div", { className: theme.subtitleStyle, children: subtitle })
31
34
  : subtitle)] }), endContent && (jsx("div", { className: "flex items-center justify-center ml-2", children: endContent }))] }));
32
35
  });
@@ -1,5 +1,11 @@
1
1
  export interface MenuItemTheme {
2
2
  baseStyle: string;
3
+ states: {
4
+ default: string;
5
+ selected: string;
6
+ disabled: string;
7
+ };
8
+ focusedStyle: string;
3
9
  dangerStyle: string;
4
10
  focusStyle: string;
5
11
  subtitleStyle: string;
@@ -1,25 +1,28 @@
1
1
  const menuItemTheme = {
2
- // Base item styling with hover, focus, and disabled states
3
- baseStyle: "relative flex items-center gap-2 px-3 py-1.5 text-sm font-[500] text-[var(--color-text-primary)] cursor-pointer outline-none select-none rounded-[var(--radius-base)] transition-colors duration-300 " +
4
- "hover:bg-[var(--color-background-secondary)] focus:bg-[var(--color-background-secondary)] " +
5
- "data-[focused=true]:bg-[var(--color-background-secondary)] " +
6
- "data-[selected=true]:bg-[var(--color-primary)]/5 data-[selected=true]:text-[var(--color-primary)] " +
7
- "mb-px last-of-type:mb-0 " +
8
- "data-[disabled=true]:opacity-60 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:pointer-events-none " +
9
- // Dark mode
10
- " " +
11
- " " +
12
- "dark:data-[focused=true]:bg-[var(--color-background-secondary)] " +
13
- "dark:hover:bg-[var(--color-neutral-700)] " +
14
- "dark:data-[selected=true]:bg-[var(--color-neutral-600)]/50 dark:data-[selected=true]:text-[var(--color-text-primary)]",
2
+ // Structural base only layout, spacing, radius, transition. No state colors:
3
+ // those are resolved in JS (see `states`) so a consumer `className` always wins.
4
+ baseStyle: "relative flex items-center gap-2 px-3 py-1.5 text-sm font-[500] cursor-pointer outline-none select-none rounded-[var(--radius-base)] transition-colors duration-300 mb-px last-of-type:mb-0",
5
+ // State styling resolved in JS to plain utility classes so `className` overrides
6
+ // win (data-[state] variants carry attribute-selector specificity and can't be
7
+ // overridden by a plain className — see Button.theme.ts / NavItem.theme.ts for
8
+ // the same rationale). data-* attributes are still emitted as hooks for
9
+ // consumer CSS + tests, just not used for styling here.
10
+ states: {
11
+ default: "text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)]",
12
+ selected: "bg-[var(--color-primary)]/5 text-[var(--color-primary)] " +
13
+ "dark:bg-white/10 dark:text-[var(--color-text-primary)]",
14
+ disabled: "opacity-60 cursor-not-allowed pointer-events-none",
15
+ },
16
+ // Keyboard-focus highlight (item received roving focus) — same fill as hover.
17
+ focusedStyle: "bg-[var(--color-surface-hover)]",
15
18
  // Danger item styling (e.g., Delete actions)
16
19
  dangerStyle: "text-[var(--color-danger)] hover:bg-[var(--color-danger)] hover:text-[var(--color-danger-contrast)] " +
17
20
  "dark:text-[var(--color-danger-400)] dark:hover:bg-[var(--color-danger-600)] dark:hover:text-[var(--color-danger-contrast)]",
18
21
  // Focus visible styling for keyboard navigation
19
- focusStyle: "focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 " +
20
- "dark:focus-visible:ring-[var(--color-primary-400)] dark:focus-visible:ring-offset-[var(--color-neutral-900)]",
22
+ focusStyle: "focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-background)] " +
23
+ "dark:focus-visible:ring-[var(--color-primary-400)]",
21
24
  // Description text below the label
22
- subtitleStyle: "text-xs text-[var(--color-text-muted)] mt-0.5 truncate",
25
+ subtitleStyle: "text-sm text-[var(--color-text-secondary)]/80 mt-0.5 truncate",
23
26
  };
24
27
 
25
28
  export { menuItemTheme };
@@ -17,6 +17,8 @@ export interface MenuItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>
17
17
  disabled?: boolean;
18
18
  /** Whether the item has danger/destructive styling */
19
19
  isDanger?: boolean;
20
+ /** Whether the item is in a selected state (drives selected styling) */
21
+ selected?: boolean;
20
22
  /** Called on click or Enter/Space keyboard activation */
21
23
  onActivate?: () => void;
22
24
  /** Theme overrides for styling */
@@ -186,8 +186,15 @@ const NavItem = React__default.forwardRef(({ children, startContent, endContent,
186
186
  const textClasses = cn(theme.text);
187
187
  // Chevron for submenu
188
188
  const chevron = hasSubmenu && !isCollapsedView ? (jsx(ChevronDownIcon, { strokeWidth: 2, className: cn("size-4 transition-transform duration-200 text-[var(--color-text-muted)]", isExpanded && "text-[var(--color-text-primary)]", isExpanded && "rotate-180") })) : null;
189
- // Strip drag/animation DOM handlers that collide with motion's prop types
190
- const { onAnimationStart, onAnimationEnd, onDrag, onDragStart, onDragEnd, ...motionSafeProps } = props;
189
+ // Strip drag/animation DOM handlers that collide with motion's prop types.
190
+ // `onSubmit`/`onSubmitCapture` are stripped too: NavItem renders as either
191
+ // `motion.a` or `motion.button`, but `NavItemProps extends HTMLAttributes<HTMLElement>`
192
+ // types these as `SubmitEventHandler<HTMLElement>`. Under react@canary types
193
+ // (e.g. Next 16 consumers building from source), `SubmitEvent` gains a
194
+ // T-dependent field, so the generic-element handler is no longer assignable to
195
+ // motion's element-specific `HTMLMotionProps<"a">` — a build-time type error.
196
+ // A NavItem never submits a form, so dropping these is behaviour-neutral.
197
+ const { onAnimationStart, onAnimationEnd, onDrag, onDragStart, onDragEnd, onSubmit, onSubmitCapture, ...motionSafeProps } = props;
191
198
  const interactionProps = {
192
199
  className: cn("relative overflow-hidden", containerClasses),
193
200
  "aria-disabled": isDisabled || undefined,
@@ -199,15 +206,24 @@ const NavItem = React__default.forwardRef(({ children, startContent, endContent,
199
206
  whileHover: isDisabled ? undefined : (isCollapsedView ? { scale: 1.05 } : undefined),
200
207
  whileTap: isDisabled ? undefined : { scale: 0.98 },
201
208
  transition: { type: "spring", stiffness: 400, damping: 25 },
209
+ // Note: tried Framer Motion's canonical `layout` + `LayoutGroup` +
210
+ // `mode="popLayout"` pattern for the row's height shrink — transforms
211
+ // *do* fire (verified via getComputedStyle), but the parent Sidebar's
212
+ // concurrent `motion.aside` width animation re-triggers measurements
213
+ // every frame, which keeps resetting the layout animation before it
214
+ // settles. The row visibly freezes mid-transform. Animating the inner
215
+ // motion.div's height directly (see AnimatePresence below) sidesteps the
216
+ // concurrent-measurement problem and produces a smooth ~200ms shrink.
202
217
  ...motionSafeProps,
203
218
  };
204
219
  const innerContent = (jsxs(Fragment, { children: [startContent && (jsxs("div", { className: "flex items-center justify-center flex-shrink-0 relative", children: [React__default.isValidElement(startContent)
205
220
  ? React__default.cloneElement(startContent, {
206
221
  className: cn(iconClasses, startContent.props.className),
207
222
  })
208
- : startContent, isCollapsedView && hasNotification && (jsx("span", { className: cn("absolute -top-0.5 -right-0.5 ring-2 ring-[var(--color-surface)]", navItemTheme.notificationDot, customTheme.notificationDot), "aria-hidden": "true" }))] })), jsx(AnimatePresence, { initial: false, children: !isCollapsedView && (jsxs(motion.span, { initial: { opacity: 0, x: -4 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -4 }, transition: {
223
+ : startContent, isCollapsedView && hasNotification && (jsx("span", { className: cn("absolute -top-0.5 -right-0.5 ring-2 ring-[var(--color-surface)]", navItemTheme.notificationDot, customTheme.notificationDot), "aria-hidden": "true" }))] })), jsx(AnimatePresence, { initial: false, children: !isCollapsedView && (jsxs(motion.div, { initial: { opacity: 0, x: -4, height: 0 }, animate: { opacity: 1, x: 0, height: "auto" }, exit: { opacity: 0, x: -4, height: 0 }, transition: {
209
224
  opacity: { duration: 0.15, ease: [0.4, 0, 0.2, 1] },
210
- x: { duration: 0.2, ease: [0.4, 0, 0.2, 1] }
225
+ x: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
226
+ height: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
211
227
  }, className: "flex-1 min-w-0 flex flex-col overflow-hidden whitespace-nowrap", children: [jsxs("span", { className: "flex items-center justify-between gap-2 w-full min-w-0", children: [jsx("span", { className: cn("truncate flex-1 min-w-0", textClasses), children: children }), jsxs("span", { className: "flex items-center gap-1.5 flex-shrink-0 ml-2", children: [shortcut && (jsx("kbd", { className: "hidden group-hover:inline-flex h-[18px] items-center text-[10px] text-[var(--color-text-muted)] font-sans border border-[var(--color-border)] rounded px-1.5 leading-none flex-shrink-0", children: shortcut })), notificationElement, endContent, chevron] })] }), description && (jsx("span", { className: "block text-xs text-[var(--color-text-muted)] mt-0.5 truncate w-full", children: description }))] }, "expanded-content")) }), isCollapsedView && (jsxs("span", { className: "sr-only", children: [children, typeof notification === "number" && notification > 0
212
228
  ? ` (${notification} unread)`
213
229
  : notification === true
@@ -1,5 +1,5 @@
1
1
  const navItemTheme = {
2
- baseStyle: "group relative flex items-center gap-3 px-2.5 py-2 text-sm font-medium cursor-pointer select-none w-full rounded-[var(--nav-item-radius)] outline-none overflow-hidden transition-[padding,gap,background-color,border-color,color,box-shadow] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] text-left",
2
+ baseStyle: "group relative flex items-center gap-3 px-2.5 py-2 text-sm font-medium cursor-pointer select-none w-full rounded-[var(--nav-item-radius)] outline-none overflow-hidden transition-[padding,gap,height,background-color,border-color,color,box-shadow] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] text-left",
3
3
  icon: "size-5",
4
4
  text: "flex-1 min-w-0 truncate",
5
5
  // State styling resolved in JS to plain utility classes so `className` overrides win
@@ -25,8 +25,19 @@ const navItemTheme = {
25
25
  1: "pl-11",
26
26
  2: "pl-10",
27
27
  },
28
+ // Collapsed override gives every nav row a uniform 36×36 square hit-area.
29
+ // CRITICAL: this uses PADDING-based centering, not `justify-center`, so the
30
+ // expanded↔collapsed transition is one continuous animation of the padding
31
+ // + gap properties (which are CSS-transitionable). Toggling `justify-*`
32
+ // would cause a frame-0 flex-layout snap — the label appears at its final
33
+ // position instead of sliding in via AnimatePresence.
34
+ //
35
+ // On a 60px rail (Sidebar.Content px-3 → 36px interior, NavItem w-full):
36
+ // !px-2 (8 each) + size-5 icon (20) = 8 + 20 + 8 = 36 → centered icon.
37
+ // baseStyle's py-2 (8 each) + icon 20 = 36 → square height.
38
+ // !py-2 is repeated explicitly so the compact density's py-1.5 doesn't win.
28
39
  collapsed: {
29
- container: "!gap-0 !px-[14px] !py-2",
40
+ container: "!gap-0 !px-2 !py-2",
30
41
  icon: "size-5",
31
42
  },
32
43
  density: {
@@ -48,8 +48,12 @@ export interface PopoverBodyProps extends React.HTMLAttributes<HTMLDivElement> {
48
48
  children: ReactNode;
49
49
  animation?: boolean;
50
50
  portal?: boolean;
51
- /** Enable scroll support with viewport-aware max-height. Uses available
52
- * space computed by the positioning hook, falling back to 400px. */
51
+ /** Cap the body to the viewport-available height (space below/above the
52
+ * trigger, computed by the positioning hook; falls back to 400px) and add
53
+ * `overflow-y-auto`, so tall content scrolls inside the popover instead of
54
+ * extending past the viewport unreachably. `max-height` + `auto` means short
55
+ * popovers show no scrollbar. Set `false` to opt out (uncapped, may overflow).
56
+ * @default true */
53
57
  scrollable?: boolean;
54
58
  /** Custom max-height when scrollable is true (e.g. "300px", "20rem").
55
59
  * Overrides the default viewport-aware height. */
@@ -26,7 +26,7 @@ const overlayContentAnimations = {
26
26
  /**
27
27
  * PopoverBody component displays the body/content of the popover
28
28
  */
29
- const PopoverBody = React__default.forwardRef(({ children, className, animation = true, portal = true, scrollable = false, maxHeight, ...props }, ref) => {
29
+ const PopoverBody = React__default.forwardRef(({ children, className, animation = true, portal = true, scrollable = true, maxHeight, ...props }, ref) => {
30
30
  const { isOpen, onOpenChange, triggerRef, contentRef, placement, offset, triggerType, closeOnClickOutside, closeOnEsc, preventClose, hasBackdrop, theme, contentId, } = usePopoverContext();
31
31
  const shouldReduceMotion = useReducedMotion();
32
32
  const localRef = useRef(null);
@@ -45,13 +45,20 @@ const PopoverBody = React__default.forwardRef(({ children, className, animation
45
45
  });
46
46
  // Click outside detection (active when no backdrop — with backdrop, Overlay handles it)
47
47
  useClickOutside(localRef, (e) => {
48
- var _a;
48
+ var _a, _b, _c;
49
49
  if (hasBackdrop)
50
50
  return; // Overlay handles click-outside via backdrop
51
51
  if (e.target === triggerRef.current ||
52
52
  ((_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
53
53
  return;
54
54
  }
55
+ // Ignore clicks that land inside any FlikkUI overlay portal (a nested
56
+ // Select/Tooltip/Dropdown/Popover renders to document.body, outside our
57
+ // content ref — without this it would count as an outside click and
58
+ // dismiss the popover). See the `data-flikkui-portal` convention.
59
+ if ((_c = (_b = e.target) === null || _b === void 0 ? void 0 : _b.closest) === null || _c === void 0 ? void 0 : _c.call(_b, "[data-flikkui-portal]")) {
60
+ return;
61
+ }
55
62
  if (closeOnClickOutside && !preventClose) {
56
63
  onOpenChange(false);
57
64
  }
@@ -120,7 +127,7 @@ const PopoverBody = React__default.forwardRef(({ children, className, animation
120
127
  ? { duration: 0 }
121
128
  : popoverTransitions.content, "data-state": isOpen ? "open" : "closed", "data-placement": placement, "data-trigger": triggerType, onClick: (e) => e.stopPropagation(), ...domProps, children: children })) }));
122
129
  if (portal && isClient) {
123
- return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: popoverContent }), document.body);
130
+ return createPortal(jsx("div", { "data-flikkui-portal": "", className: isDarkMode ? "dark" : undefined, children: popoverContent }), document.body);
124
131
  }
125
132
  return popoverContent;
126
133
  });
@@ -3,7 +3,7 @@
3
3
  */
4
4
  const segmentedTheme = {
5
5
  // Base styles for the segmented container
6
- baseStyle: "relative flex rounded-[var(--segmented-radius)] bg-[var(--color-surface-sunken)]/10 border border-[var(--color-border)]/60 p-[var(--segmented-padding)] shadow-inner dark:bg-[var(--color-surface-sunken)]",
6
+ baseStyle: "relative flex rounded-[var(--segmented-radius)] bg-[var(--color-surface)] border border-[var(--color-border)]/60 p-[var(--segmented-padding)] shadow-inner",
7
7
  // Size variants using CSS variables
8
8
  sizes: {
9
9
  sm: "min-h-[var(--segmented-min-h-sm)]",
@@ -11,4 +11,6 @@ export declare const Sidebar: React.ForwardRefExoticComponent<SidebarProps & Rea
11
11
  MobileTrigger: React.ForwardRefExoticComponent<import("./Sidebar.types").SidebarMobileTriggerProps & React.RefAttributes<HTMLButtonElement>>;
12
12
  UserProfile: React.ForwardRefExoticComponent<import("./Sidebar.types").SidebarUserProfileProps & React.RefAttributes<HTMLDivElement>>;
13
13
  Logo: React.ForwardRefExoticComponent<import("./Sidebar.types").SidebarLogoProps & React.RefAttributes<HTMLDivElement>>;
14
+ Search: React.ForwardRefExoticComponent<import("./Sidebar.types").SidebarSearchProps & React.RefAttributes<HTMLInputElement>>;
15
+ Card: React.ForwardRefExoticComponent<import("./Sidebar.types").SidebarCardProps & React.RefAttributes<HTMLDivElement>>;
14
16
  };
@@ -14,21 +14,27 @@ import { SidebarToggle } from './SidebarToggle.js';
14
14
  import { SidebarMobileTrigger } from './SidebarMobileTrigger.js';
15
15
  import { SidebarUserProfile } from './SidebarUserProfile.js';
16
16
  import { SidebarLogo } from './SidebarLogo.js';
17
+ import { SidebarSearch } from './SidebarSearch.js';
18
+ import { SidebarCard } from './SidebarCard.js';
17
19
 
18
- // Matches the 200ms / cubic-bezier(0.4,0,0.2,1) timing used by Tailwind's
19
- // `duration-200` transitions on the content + footer, so width, padding and
20
- // icon animations stay in sync.
20
+ // Subtle spring on the sidebar's width animation. Stiffness/damping picked so
21
+ // the effective duration sits in the same ~200ms window as the internal CSS
22
+ // transitions on padding/gap (Tailwind `duration-200`), but with a soft
23
+ // organic settle instead of a linear cubic-bezier. Damping ratio ≈ 0.9 — a
24
+ // hair under critical so there's a barely-perceptible easing-into-final
25
+ // rather than an overshoot/bounce.
21
26
  const SIDEBAR_TRANSITION = {
22
- type: "tween",
23
- duration: 0.2,
24
- ease: [0.4, 0, 0.2, 1],
27
+ type: "spring",
28
+ stiffness: 320,
29
+ damping: 34,
30
+ mass: 1,
25
31
  };
26
32
  const SidebarDesktop = React__default.forwardRef(({ side = "left", variant = "sidebar", density, width, children, className, theme: customTheme = {}, ...props }, ref) => {
27
33
  var _a, _b, _c, _d;
28
34
  const context = useSidebarContext();
29
35
  const collapsed = (_a = context === null || context === void 0 ? void 0 : context.collapsed) !== null && _a !== void 0 ? _a : false;
30
36
  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;
31
- 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;
37
+ 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) || 60;
32
38
  const theme = useMemo(() => ({
33
39
  ...sidebarTheme,
34
40
  ...customTheme,
@@ -104,6 +110,8 @@ const Sidebar = Object.assign(SidebarRoot, {
104
110
  MobileTrigger: SidebarMobileTrigger,
105
111
  UserProfile: SidebarUserProfile,
106
112
  Logo: SidebarLogo,
113
+ Search: SidebarSearch,
114
+ Card: SidebarCard,
107
115
  });
108
116
 
109
117
  export { Sidebar };