@flikk/ui 1.0.0-beta.6 → 1.0.0-beta.8

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 (57) hide show
  1. package/dist/components/ai/PromptInput/PromptInput.js +5 -124
  2. package/dist/components/core/Alert/Alert.js +1 -0
  3. package/dist/components/core/Alert/Alert.theme.js +3 -7
  4. package/dist/components/core/Alert/Alert.types.d.ts +2 -1
  5. package/dist/components/core/Badge/Badge.theme.js +13 -8
  6. package/dist/components/core/Badge/Badge.types.d.ts +2 -1
  7. package/dist/components/core/Button/Button.theme.js +36 -30
  8. package/dist/components/core/Button/Button.types.d.ts +2 -1
  9. package/dist/components/core/Calendar/Calendar.theme.js +3 -3
  10. package/dist/components/core/CommandPalette/CommandPalette.theme.js +1 -1
  11. package/dist/components/core/MenuItem/MenuItem.theme.js +2 -2
  12. package/dist/components/core/OfflineIndicator/OfflineIndicator.theme.js +1 -1
  13. package/dist/components/core/Popover/PopoverContent.js +2 -0
  14. package/dist/components/core/Progress/Progress.theme.js +9 -6
  15. package/dist/components/core/Progress/Progress.types.d.ts +2 -1
  16. package/dist/components/core/Spinner/Spinner.theme.js +3 -5
  17. package/dist/components/core/Spinner/Spinner.types.d.ts +2 -1
  18. package/dist/components/data-display/KPI/KPI.theme.js +3 -4
  19. package/dist/components/data-display/KPI/KPI.types.d.ts +2 -1
  20. package/dist/components/data-display/Metric/Metric.theme.js +4 -0
  21. package/dist/components/data-display/Metric/Metric.types.d.ts +2 -1
  22. package/dist/components/data-display/Table/TableFilter.js +1 -1
  23. package/dist/components/data-display/Timeline/Timeline.theme.js +2 -2
  24. package/dist/components/effects/CustomCursor/CustomCursor.theme.js +2 -0
  25. package/dist/components/effects/CustomCursor/CustomCursor.types.d.ts +2 -1
  26. package/dist/components/forms/Checkbox/Checkbox.d.ts +3 -4
  27. package/dist/components/forms/Checkbox/Checkbox.js +33 -9
  28. package/dist/components/forms/Checkbox/Checkbox.theme.js +6 -0
  29. package/dist/components/forms/Checkbox/Checkbox.types.d.ts +28 -2
  30. package/dist/components/forms/Checkbox/CheckboxContext.d.ts +14 -0
  31. package/dist/components/forms/Checkbox/CheckboxContext.js +12 -0
  32. package/dist/components/forms/Checkbox/CheckboxGroup.d.ts +3 -0
  33. package/dist/components/forms/Checkbox/CheckboxGroup.js +40 -0
  34. package/dist/components/forms/Checkbox/index.d.ts +1 -1
  35. package/dist/components/forms/FileUpload/FileUpload.js +0 -78
  36. package/dist/components/forms/Radio/Radio.d.ts +3 -4
  37. package/dist/components/forms/Radio/Radio.js +21 -4
  38. package/dist/components/forms/Radio/Radio.theme.js +6 -0
  39. package/dist/components/forms/Radio/Radio.types.d.ts +30 -4
  40. package/dist/components/forms/Radio/RadioContext.d.ts +14 -0
  41. package/dist/components/forms/Radio/RadioContext.js +12 -0
  42. package/dist/components/forms/Radio/RadioGroup.d.ts +3 -0
  43. package/dist/components/forms/Radio/RadioGroup.js +36 -0
  44. package/dist/components/forms/Radio/index.d.ts +1 -1
  45. package/dist/components/forms/TimePicker/TimePickerContent.js +1 -78
  46. package/dist/components/forms/index.d.ts +2 -2
  47. package/dist/components/forms/index.js +0 -78
  48. package/dist/components/layout/FormLayout/FormLayoutBody.js +1 -79
  49. package/dist/hooks/useSelectPortal.d.ts +3 -2
  50. package/dist/hooks/useSelectPortal.js +44 -19
  51. package/dist/index.js +191 -197
  52. package/dist/styles.css +1 -1
  53. package/dist/utils/colorUtils.d.ts +23 -0
  54. package/dist/utils/colorUtils.js +45 -0
  55. package/dist/utils/index.d.ts +1 -0
  56. package/package.json +1 -1
  57. package/src/styles/theme.css +29 -0
@@ -6,85 +6,8 @@ import { cn } from '../../../utils/cn.js';
6
6
  import { TimePickerContext } from './TimePicker.js';
7
7
  import { useSelectPortal } from '../../../hooks/useSelectPortal.js';
8
8
  import { createDropdownAnimations } from '../Select/Select.animations.js';
9
- import '../../core/Accordion/Accordion.js';
10
- import '../../core/AlertDialog/AlertDialog.js';
11
- import '../../core/AspectRatio/AspectRatio.js';
12
- import '../../core/Avatar/Avatar.js';
13
- import '../../core/AvatarGroup/AvatarGroup.js';
14
- import '../../core/Badge/Badge.js';
15
- import '../../core/Breadcrumbs/Breadcrumbs.js';
16
- import '../../core/Breadcrumbs/Breadcrumbs.theme.js';
17
- import { Button } from '../../core/Button/Button.js';
18
- import '../../core/ButtonGroup/ButtonGroup.js';
19
- import '../../core/ButtonGroup/ButtonGroupSeparator.js';
20
- import '../../core/ButtonGroup/ButtonGroupText.js';
21
- import '../../core/Calendar/Calendar.js';
22
- import '../../core/Calendar/CalendarMini/CalendarMini.js';
23
- import '../../core/Card/Card.js';
24
- import '../../core/CardStack/CardStack.js';
25
- import '../../core/Carousel/Carousel.js';
26
- import '../../core/CommandPalette/CommandPalette.js';
27
- import '../../core/Separator/Separator.js';
28
- import '../../core/Drawer/Drawer.js';
29
- import '../../core/Dropdown/Dropdown.js';
30
- import '../../core/Dropdown/DropdownTrigger.js';
31
- import '../../core/Dropdown/DropdownMenu.js';
32
- import '../../core/Dropdown/DropdownItem.js';
33
- import '../../core/Dropdown/DropdownSection.js';
34
- import '../../core/Dropdown/DropdownSeparator.js';
35
- import '../../core/Dropdown/Dropdown.theme.js';
36
- import '../../core/Kbd/Kbd.js';
37
- import '../../core/MenuItem/MenuItem.js';
38
- import '../../core/Link/Link.js';
39
- import '../../core/Loader/Loader.js';
40
- import '../../core/Modal/Modal.js';
41
- import '../../core/ModalStack/ModalStack.js';
42
- import '../../core/PageHeading/PageHeading.js';
43
- import '../../core/Pagination/Pagination.js';
44
- import '../../core/Popover/Popover.js';
45
- import '../../core/Popover/PopoverContext.js';
46
- import '../../core/Progress/Progress.js';
47
- import '../../core/Rating/Rating.js';
48
- import '../../core/ScrollArea/ScrollArea.js';
49
- import '../../core/ScrollArea/smooth/SmoothScrollEngine.js';
50
- import '../../core/Segmented/Segmented.js';
51
- import '../../core/Skeleton/Skeleton.js';
52
- import '../../core/SlidingNumber/SlidingNumber.js';
53
- import '../../core/Tabs/Tabs.js';
54
- import '../../core/Tabs/TabsList.js';
55
- import '../../core/Tabs/TabsTrigger.js';
56
- import '../../core/Tabs/TabsContent.js';
57
- import '../../core/Tabs/TabsContext.js';
58
- import '../../core/Tooltip/Tooltip.js';
59
- import '../../core/Tooltip/Tooltip.animations.js';
60
- import '../../core/Tree/Tree.js';
61
- import '../../core/Tag/Tag.js';
62
- import '../../core/Alert/Alert.js';
63
- import '../../core/Toast/Toast.js';
64
- import '../../core/Toast/ToastProvider.js';
65
- import '@heroicons/react/24/solid';
66
- import '../../core/Spinner/Spinner.js';
67
- import '../../core/Message/Message.js';
68
- import '../../core/Message/TypeWriter.js';
69
- import '../../core/Empty/Empty.js';
70
- import '../../core/Masonry/Masonry.js';
71
- import '../../core/DragDrop/DragDrop.js';
72
- import '../../core/Sortable/Sortable.js';
73
- import '../../core/NavItem/NavItem.js';
74
- import '../../core/Sidebar/Sidebar.js';
75
- import '../../core/Sidebar/SidebarHeader.js';
76
- import '../../core/Sidebar/SidebarContent.js';
77
- import '../../core/Sidebar/SidebarFooter.js';
78
- import '../../core/Sidebar/SidebarNav.js';
79
- import '../../core/Sidebar/SidebarNavGroup.js';
80
- import '../../core/Sidebar/SidebarToggle.js';
81
- import '../../core/Sidebar/SidebarContext.js';
82
- import '../../core/OfflineIndicator/OfflineIndicator.js';
83
- import '../../core/ContextMenu/ContextMenu.js';
84
- import '../../core/ContextMenu/ContextMenu.theme.js';
85
- import '../../core/Pill/Pill.js';
86
- import '../../core/SocialIcon/SocialIcon.js';
87
9
  import { useIsClient } from '../../../hooks/useIsClient.js';
10
+ import { Button } from '../../core/Button/Button.js';
88
11
 
89
12
  /**
90
13
  * Hook to detect dark mode state from document
@@ -11,10 +11,10 @@ export { FormLabel } from "./FormLabel";
11
11
  export type { FormLabelProps, FormLabelState, FormLabelTheme, } from "./FormLabel";
12
12
  export { formLabelTheme } from "./FormLabel";
13
13
  export { Radio } from "./Radio";
14
- export type { RadioProps, RadioSize, RadioState } from "./Radio";
14
+ export type { RadioProps, RadioSize, RadioState, RadioGroupProps, RadioGroupOrientation, } from "./Radio";
15
15
  export { radioTheme } from "./Radio";
16
16
  export { Checkbox } from "./Checkbox";
17
- export type { CheckboxProps, CheckboxSize, CheckboxState } from "./Checkbox";
17
+ export type { CheckboxProps, CheckboxSize, CheckboxState, CheckboxGroupProps, CheckboxGroupOrientation, } from "./Checkbox";
18
18
  export { checkboxTheme } from "./Checkbox";
19
19
  export { Switch } from "./Switch";
20
20
  export type { SwitchProps, SwitchThemeOverrides } from "./Switch";
@@ -24,84 +24,6 @@ import 'clsx';
24
24
  import 'tailwind-merge';
25
25
  import 'motion/react';
26
26
  import 'react-dom';
27
- import '../core/Accordion/Accordion.js';
28
- import '../core/AlertDialog/AlertDialog.js';
29
- import '../core/AspectRatio/AspectRatio.js';
30
- import '../core/Avatar/Avatar.js';
31
- import '../core/AvatarGroup/AvatarGroup.js';
32
- import '../core/Badge/Badge.js';
33
- import '../core/Breadcrumbs/Breadcrumbs.js';
34
- import '../core/Breadcrumbs/Breadcrumbs.theme.js';
35
- import '../core/Button/Button.js';
36
- import '../core/ButtonGroup/ButtonGroup.js';
37
- import '../core/ButtonGroup/ButtonGroupSeparator.js';
38
- import '../core/ButtonGroup/ButtonGroupText.js';
39
- import '../core/Calendar/Calendar.js';
40
- import '../core/Calendar/CalendarMini/CalendarMini.js';
41
- import '../core/Card/Card.js';
42
- import '../core/CardStack/CardStack.js';
43
- import '../core/Carousel/Carousel.js';
44
- import '../core/CommandPalette/CommandPalette.js';
45
- import '../core/Separator/Separator.js';
46
- import '../core/Drawer/Drawer.js';
47
- import '../core/Dropdown/Dropdown.js';
48
- import '../core/Dropdown/DropdownTrigger.js';
49
- import '../core/Dropdown/DropdownMenu.js';
50
- import '../core/Dropdown/DropdownItem.js';
51
- import '../core/Dropdown/DropdownSection.js';
52
- import '../core/Dropdown/DropdownSeparator.js';
53
- import '../core/Dropdown/Dropdown.theme.js';
54
- import '../core/Kbd/Kbd.js';
55
- import '../core/MenuItem/MenuItem.js';
56
- import '../core/Link/Link.js';
57
- import '../core/Loader/Loader.js';
58
- import '../core/Modal/Modal.js';
59
- import '../core/ModalStack/ModalStack.js';
60
- import '../core/PageHeading/PageHeading.js';
61
- import '../core/Pagination/Pagination.js';
62
- import '../core/Popover/Popover.js';
63
- import '../core/Popover/PopoverContext.js';
64
- import '../core/Progress/Progress.js';
65
- import '../core/Rating/Rating.js';
66
- import '../core/ScrollArea/ScrollArea.js';
67
- import '../core/ScrollArea/smooth/SmoothScrollEngine.js';
68
- import '../core/Segmented/Segmented.js';
69
- import '../core/Skeleton/Skeleton.js';
70
- import '../core/SlidingNumber/SlidingNumber.js';
71
- import '../core/Tabs/Tabs.js';
72
- import '../core/Tabs/TabsList.js';
73
- import '../core/Tabs/TabsTrigger.js';
74
- import '../core/Tabs/TabsContent.js';
75
- import '../core/Tabs/TabsContext.js';
76
- import '../core/Tooltip/Tooltip.js';
77
- import '../core/Tooltip/Tooltip.animations.js';
78
- import '../core/Tree/Tree.js';
79
- import '../core/Tag/Tag.js';
80
- import '../core/Alert/Alert.js';
81
- import '../core/Toast/Toast.js';
82
- import '../core/Toast/ToastProvider.js';
83
- import '@heroicons/react/24/solid';
84
- import '../core/Spinner/Spinner.js';
85
- import '../core/Message/Message.js';
86
- import '../core/Message/TypeWriter.js';
87
- import '../core/Empty/Empty.js';
88
- import '../core/Masonry/Masonry.js';
89
- import '../core/DragDrop/DragDrop.js';
90
- import '../core/Sortable/Sortable.js';
91
- import '../core/NavItem/NavItem.js';
92
- import '../core/Sidebar/Sidebar.js';
93
- import '../core/Sidebar/SidebarHeader.js';
94
- import '../core/Sidebar/SidebarContent.js';
95
- import '../core/Sidebar/SidebarFooter.js';
96
- import '../core/Sidebar/SidebarNav.js';
97
- import '../core/Sidebar/SidebarNavGroup.js';
98
- import '../core/Sidebar/SidebarToggle.js';
99
- import '../core/Sidebar/SidebarContext.js';
100
- import '../core/OfflineIndicator/OfflineIndicator.js';
101
- import '../core/ContextMenu/ContextMenu.js';
102
- import '../core/ContextMenu/ContextMenu.theme.js';
103
- import '../core/Pill/Pill.js';
104
- import '../core/SocialIcon/SocialIcon.js';
105
27
  import './TimePicker/WheelColumn.js';
106
28
  export { timePickerTheme } from './TimePicker/TimePicker.theme.js';
107
29
  export { Slider } from './Slider/Slider.js';
@@ -1,86 +1,8 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import React__default from 'react';
3
3
  import { cn } from '../../../utils/cn.js';
4
- import '../../core/Accordion/Accordion.js';
5
- import '../../core/AlertDialog/AlertDialog.js';
6
- import '../../core/AspectRatio/AspectRatio.js';
7
- import '../../core/Avatar/Avatar.js';
8
- import '../../core/AvatarGroup/AvatarGroup.js';
9
- import '../../core/Badge/Badge.js';
10
- import '../../core/Breadcrumbs/Breadcrumbs.js';
11
- import '../../core/Breadcrumbs/Breadcrumbs.theme.js';
12
- import '../../core/Button/Button.js';
13
- import '../../core/ButtonGroup/ButtonGroup.js';
14
- import '../../core/ButtonGroup/ButtonGroupSeparator.js';
15
- import '../../core/ButtonGroup/ButtonGroupText.js';
16
- import '../../core/Calendar/Calendar.js';
17
- import '../../core/Calendar/CalendarMini/CalendarMini.js';
18
- import '../../core/Card/Card.js';
19
- import '../../core/CardStack/CardStack.js';
20
- import '../../core/Carousel/Carousel.js';
21
- import '../../core/CommandPalette/CommandPalette.js';
22
- import { Separator } from '../../core/Separator/Separator.js';
23
- import '../../core/Drawer/Drawer.js';
24
- import '../../core/Dropdown/Dropdown.js';
25
- import '../../core/Dropdown/DropdownTrigger.js';
26
- import '../../core/Dropdown/DropdownMenu.js';
27
- import '../../core/Dropdown/DropdownItem.js';
28
- import '../../core/Dropdown/DropdownSection.js';
29
- import '../../core/Dropdown/DropdownSeparator.js';
30
- import '../../core/Dropdown/Dropdown.theme.js';
31
- import '../../core/Kbd/Kbd.js';
32
- import '../../core/MenuItem/MenuItem.js';
33
- import '../../core/Link/Link.js';
34
- import '../../core/Loader/Loader.js';
35
- import '../../core/Modal/Modal.js';
36
- import '../../core/ModalStack/ModalStack.js';
37
- import '../../core/PageHeading/PageHeading.js';
38
- import '../../core/Pagination/Pagination.js';
39
- import '../../core/Popover/Popover.js';
40
- import '../../core/Popover/PopoverContext.js';
41
- import '../../core/Progress/Progress.js';
42
- import '../../core/Rating/Rating.js';
43
- import '../../core/ScrollArea/ScrollArea.js';
44
- import '../../core/ScrollArea/smooth/SmoothScrollEngine.js';
45
- import '../../core/Segmented/Segmented.js';
46
- import '../../core/Skeleton/Skeleton.js';
47
- import '../../core/SlidingNumber/SlidingNumber.js';
48
- import '../../core/Tabs/Tabs.js';
49
- import '../../core/Tabs/TabsList.js';
50
- import '../../core/Tabs/TabsTrigger.js';
51
- import '../../core/Tabs/TabsContent.js';
52
- import '../../core/Tabs/TabsContext.js';
53
- import '../../core/Tooltip/Tooltip.js';
54
- import '../../core/Tooltip/Tooltip.animations.js';
55
- import '../../core/Tree/Tree.js';
56
- import '../../core/Tag/Tag.js';
57
- import '../../core/Alert/Alert.js';
58
- import '../../core/Toast/Toast.js';
59
- import '../../core/Toast/ToastProvider.js';
60
- import '@heroicons/react/24/solid';
61
- import '../../core/Spinner/Spinner.js';
62
- import '../../core/Message/Message.js';
63
- import '../../core/Message/TypeWriter.js';
64
- import '../../core/Empty/Empty.js';
65
- import '../../core/Masonry/Masonry.js';
66
- import '../../core/DragDrop/DragDrop.js';
67
- import '../../core/Sortable/Sortable.js';
68
- import '../../core/NavItem/NavItem.js';
69
- import '../../core/Sidebar/Sidebar.js';
70
- import '../../core/Sidebar/SidebarHeader.js';
71
- import '../../core/Sidebar/SidebarContent.js';
72
- import '../../core/Sidebar/SidebarFooter.js';
73
- import '../../core/Sidebar/SidebarNav.js';
74
- import '../../core/Sidebar/SidebarNavGroup.js';
75
- import 'motion/react';
76
- import '../../core/Sidebar/SidebarToggle.js';
77
- import '../../core/Sidebar/SidebarContext.js';
78
- import '../../core/OfflineIndicator/OfflineIndicator.js';
79
- import '../../core/ContextMenu/ContextMenu.js';
80
- import '../../core/ContextMenu/ContextMenu.theme.js';
81
- import '../../core/Pill/Pill.js';
82
- import '../../core/SocialIcon/SocialIcon.js';
83
4
  import { formLayoutTheme } from './FormLayout.theme.js';
5
+ import { Separator } from '../../core/Separator/Separator.js';
84
6
 
85
7
  const FormLayoutBody = React__default.forwardRef(({ className, children, ...props }, ref) => {
86
8
  const childArray = React__default.Children.toArray(children).filter(Boolean);
@@ -6,6 +6,7 @@ export interface UseSimpleDropdownOptions {
6
6
  offset?: number;
7
7
  estimatedWidth?: number;
8
8
  autoWidth?: boolean;
9
+ onClose?: () => void;
9
10
  }
10
11
  export interface SimpleDropdownReturn {
11
12
  position: {
@@ -24,5 +25,5 @@ export interface SimpleDropdownReturn {
24
25
  * Uses position: fixed with pure viewport coordinates to properly follow scroll
25
26
  * Fixes critical issues: dropdown not following scroll & multiple dropdowns not working
26
27
  */
27
- export declare const useSimpleDropdown: ({ triggerRef, isOpen, placement, offset, estimatedWidth, autoWidth, }: UseSimpleDropdownOptions) => SimpleDropdownReturn;
28
- export declare const useSelectPortal: ({ triggerRef, isOpen, placement, offset, estimatedWidth, autoWidth, }: UseSimpleDropdownOptions) => SimpleDropdownReturn;
28
+ export declare const useSimpleDropdown: ({ triggerRef, isOpen, placement, offset, estimatedWidth, autoWidth, onClose, }: UseSimpleDropdownOptions) => SimpleDropdownReturn;
29
+ export declare const useSelectPortal: ({ triggerRef, isOpen, placement, offset, estimatedWidth, autoWidth, onClose, }: UseSimpleDropdownOptions) => SimpleDropdownReturn;
@@ -1,19 +1,11 @@
1
- import { useRef, useState, useCallback, useLayoutEffect, useMemo } from 'react';
1
+ import { useRef, useState, useCallback, useLayoutEffect } from 'react';
2
2
 
3
- // Simple debounce utility
4
- const debounce = (func, wait) => {
5
- let timeout;
6
- return () => {
7
- clearTimeout(timeout);
8
- timeout = setTimeout(func, wait);
9
- };
10
- };
11
3
  /**
12
4
  * Fixed dropdown positioning hook
13
5
  * Uses position: fixed with pure viewport coordinates to properly follow scroll
14
6
  * Fixes critical issues: dropdown not following scroll & multiple dropdowns not working
15
7
  */
16
- const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", offset = 4, estimatedWidth, autoWidth = false, }) => {
8
+ const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", offset = 4, estimatedWidth, autoWidth = false, onClose, }) => {
17
9
  const contentRef = useRef(null);
18
10
  const [position, setPosition] = useState({ top: -9999, left: -9999, width: 0 });
19
11
  const [cssVariables, setCssVariables] = useState({});
@@ -280,20 +272,53 @@ const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", off
280
272
  return;
281
273
  calculatePosition();
282
274
  }, [isOpen, calculatePosition]);
283
- // Debounced position updates for better performance
284
- const debouncedCalculatePosition = useMemo(() => debounce(calculatePosition, 16), // ~60fps
285
- [calculatePosition]);
286
- // Update position on scroll and resize - debounced for performance
275
+ // rAF-synced scroll handler: skips internal scrolls, detects off-screen trigger
276
+ const scrollRafRef = useRef(null);
277
+ const handleScroll = useCallback((e) => {
278
+ // Skip if scroll originated inside the popover content
279
+ if (contentRef.current && contentRef.current.contains(e.target)) {
280
+ return;
281
+ }
282
+ // Close if trigger has scrolled off-screen
283
+ if (triggerRef.current) {
284
+ const rect = triggerRef.current.getBoundingClientRect();
285
+ const isOffScreen = rect.bottom < 0 || rect.top > window.innerHeight
286
+ || rect.right < 0 || rect.left > window.innerWidth;
287
+ if (isOffScreen && onClose) {
288
+ onClose();
289
+ return;
290
+ }
291
+ }
292
+ if (scrollRafRef.current)
293
+ cancelAnimationFrame(scrollRafRef.current);
294
+ scrollRafRef.current = requestAnimationFrame(() => {
295
+ scrollRafRef.current = null;
296
+ calculatePosition();
297
+ });
298
+ }, [calculatePosition, onClose, triggerRef]);
299
+ const handleResize = useCallback(() => {
300
+ if (scrollRafRef.current)
301
+ cancelAnimationFrame(scrollRafRef.current);
302
+ scrollRafRef.current = requestAnimationFrame(() => {
303
+ scrollRafRef.current = null;
304
+ calculatePosition();
305
+ });
306
+ }, [calculatePosition]);
307
+ // Update position on scroll and resize - rAF-synced for smooth tracking
287
308
  useLayoutEffect(() => {
288
309
  if (!isOpen)
289
310
  return;
290
- window.addEventListener("scroll", debouncedCalculatePosition, { passive: true, capture: true });
291
- window.addEventListener("resize", debouncedCalculatePosition, { passive: true });
311
+ window.addEventListener("scroll", handleScroll, { passive: true, capture: true });
312
+ window.addEventListener("resize", handleResize, { passive: true });
292
313
  return () => {
293
- window.removeEventListener("scroll", debouncedCalculatePosition, true);
294
- window.removeEventListener("resize", debouncedCalculatePosition);
314
+ window.removeEventListener("scroll", handleScroll, true);
315
+ window.removeEventListener("resize", handleResize);
316
+ if (scrollRafRef.current) {
317
+ cancelAnimationFrame(scrollRafRef.current);
318
+ scrollRafRef.current = null;
319
+ }
295
320
  };
296
- }, [isOpen, debouncedCalculatePosition]);
321
+ }, [isOpen, handleScroll, handleResize]);
297
322
  // Recalculate when dropdown content resizes (e.g. search filtering)
298
323
  useLayoutEffect(() => {
299
324
  if (!isOpen)