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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/components/ai/StreamingResponse/StreamingResponse.types.d.ts +7 -7
  2. package/dist/components/core/Accordion/Accordion.types.d.ts +1 -7
  3. package/dist/components/core/Accordion/AccordionTrigger.js +4 -6
  4. package/dist/components/core/Alert/Alert.types.d.ts +1 -1
  5. package/dist/components/core/Avatar/Avatar.js +22 -18
  6. package/dist/components/core/Avatar/Avatar.theme.js +1 -1
  7. package/dist/components/core/Avatar/Avatar.types.d.ts +9 -8
  8. package/dist/components/core/AvatarGroup/AvatarGroup.js +2 -2
  9. package/dist/components/core/Badge/Badge.js +7 -5
  10. package/dist/components/core/Badge/Badge.theme.js +11 -4
  11. package/dist/components/core/Badge/Badge.types.d.ts +2 -2
  12. package/dist/components/core/Button/Button.theme.js +2 -2
  13. package/dist/components/core/Calendar/CalendarEvent/CalendarEvent.types.d.ts +5 -6
  14. package/dist/components/core/FileTypeIcon/FileTypeIcon.d.ts +3 -0
  15. package/dist/components/core/FileTypeIcon/FileTypeIcon.js +33 -0
  16. package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.d.ts +2 -0
  17. package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.js +12 -0
  18. package/dist/components/core/FileTypeIcon/FileTypeIcon.types.d.ts +19 -0
  19. package/dist/components/core/FileTypeIcon/fileTypes.d.ts +7 -0
  20. package/dist/components/core/FileTypeIcon/fileTypes.js +30 -0
  21. package/dist/components/core/FileTypeIcon/index.d.ts +2 -0
  22. package/dist/components/core/MenuItem/MenuItem.theme.js +1 -1
  23. package/dist/components/core/Message/Message.js +1 -1
  24. package/dist/components/core/Message/Message.theme.js +2 -2
  25. package/dist/components/core/Message/Message.types.d.ts +6 -6
  26. package/dist/components/core/Message/MessageAvatar.d.ts +2 -2
  27. package/dist/components/core/Message/MessageAvatar.js +3 -3
  28. package/dist/components/core/Popover/PopoverBody.js +19 -8
  29. package/dist/components/core/Tag/Tag.d.ts +1 -1
  30. package/dist/components/core/Tag/Tag.js +2 -2
  31. package/dist/components/core/Tag/Tag.types.d.ts +1 -1
  32. package/dist/components/core/index.d.ts +1 -1
  33. package/dist/components/core/index.js +1 -1
  34. package/dist/components/data-display/Feed/Feed.animations.d.ts +40 -0
  35. package/dist/components/data-display/Feed/Feed.animations.js +26 -0
  36. package/dist/components/data-display/Feed/Feed.d.ts +5 -1
  37. package/dist/components/data-display/Feed/Feed.js +87 -60
  38. package/dist/components/data-display/Feed/Feed.theme.js +11 -13
  39. package/dist/components/data-display/Feed/Feed.types.d.ts +35 -34
  40. package/dist/components/data-display/Feed/index.d.ts +1 -1
  41. package/dist/components/data-display/GanttChart/GanttTaskBar.js +11 -9
  42. package/dist/components/data-display/Timeline/TimelineMarker.js +2 -2
  43. package/dist/components/effects/3d/index.d.ts +1 -1
  44. package/dist/components/effects/AIOrb/AIOrb.d.ts +1 -0
  45. package/dist/components/effects/AIOrb/AIOrb.js +3 -2
  46. package/dist/components/effects/AIOrb/AIOrb.types.d.ts +1 -0
  47. package/dist/components/effects/AIOrb/hooks/index.d.ts +2 -0
  48. package/dist/components/effects/AIOrb/hooks/useOrbState.d.ts +20 -0
  49. package/dist/components/effects/AIOrb/hooks/useOrbState.js +27 -0
  50. package/dist/components/effects/AIOrb/index.d.ts +2 -2
  51. package/dist/components/effects/AIOrb/variants/Aura/Aura.d.ts +3 -0
  52. package/dist/components/effects/AIOrb/variants/Aura/Aura.js +75 -0
  53. package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.d.ts +28 -0
  54. package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.js +191 -0
  55. package/dist/components/effects/AIOrb/variants/Aura/Aura.types.d.ts +53 -0
  56. package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.d.ts +17 -0
  57. package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.js +149 -0
  58. package/dist/components/effects/AIOrb/variants/Aura/index.d.ts +2 -0
  59. package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.js +38 -7
  60. package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.types.d.ts +6 -0
  61. package/dist/components/effects/AIOrb/variants/Liquid/Liquid.js +34 -8
  62. package/dist/components/effects/AIOrb/variants/Liquid/Liquid.types.d.ts +6 -0
  63. package/dist/components/effects/AIOrb/variants/index.d.ts +2 -0
  64. package/dist/components/effects/CustomCursor/CustomCursor.d.ts +2 -2
  65. package/dist/components/effects/CustomCursor/CustomCursor.js +50 -47
  66. package/dist/components/effects/GlassSurface/GlassSurface.js +24 -12
  67. package/dist/components/effects/GlassSurface/GlassSurface.types.d.ts +8 -0
  68. package/dist/components/forms/Mention/Mention.js +2 -2
  69. package/dist/index.js +1 -1
  70. package/dist/styles.css +1 -1
  71. package/dist/utils/index.d.ts +1 -0
  72. package/dist/utils/paletteGenerator.d.ts +46 -0
  73. package/package.json +1 -1
  74. package/src/styles/theme.css +2 -6
  75. package/dist/components/core/Loader/Loader.d.ts +0 -24
  76. package/dist/components/core/Loader/Loader.js +0 -57
  77. package/dist/components/core/Loader/Loader.theme.d.ts +0 -27
  78. package/dist/components/core/Loader/Loader.theme.js +0 -60
  79. package/dist/components/core/Loader/Loader.types.d.ts +0 -71
  80. package/dist/components/core/Loader/index.d.ts +0 -2
@@ -6,11 +6,11 @@ import { useMessageContext } from './MessageContext.js';
6
6
  const MessageAvatar = React__default.forwardRef(({ src, name, fallback, className, ...props }, ref) => {
7
7
  const { variant } = useMessageContext();
8
8
  // Don't render avatar for system messages
9
- if (variant === 'system') {
9
+ if (variant === "system") {
10
10
  return null;
11
11
  }
12
- return (jsx("div", { ref: ref, className: className, ...props, children: jsx(Avatar, { src: src, alt: name, fallback: fallback, size: "xs" }) }));
12
+ return (jsx("div", { ref: ref, className: className, ...props, children: jsx(Avatar, { src: src, name: name, fallback: fallback, size: "xs" }) }));
13
13
  });
14
- MessageAvatar.displayName = 'Message.Avatar';
14
+ MessageAvatar.displayName = "Message.Avatar";
15
15
 
16
16
  export { MessageAvatar };
@@ -55,14 +55,16 @@ const PopoverBody = React__default.forwardRef(({ children, className, animation
55
55
  const shouldReduceMotion = useReducedMotion();
56
56
  const localRef = useRef(null);
57
57
  // Use the same positioning hook as Select/Tooltip (now supports all 12 placements)
58
- const { position, cssVariables, contentRef: portalContentRef } = useSelectPortal({
58
+ const { position, cssVariables, contentRef: portalContentRef, } = useSelectPortal({
59
59
  triggerRef,
60
60
  isOpen: isOpen || false,
61
61
  placement: placement,
62
62
  offset: offset || 8,
63
63
  autoWidth: true,
64
- onClose: () => { if (!preventClose)
65
- onOpenChange(false); },
64
+ onClose: () => {
65
+ if (!preventClose)
66
+ onOpenChange(false);
67
+ },
66
68
  stickyTracking: true,
67
69
  });
68
70
  // Click outside detection (active when no backdrop — with backdrop, Overlay handles it)
@@ -70,7 +72,8 @@ const PopoverBody = React__default.forwardRef(({ children, className, animation
70
72
  var _a;
71
73
  if (hasBackdrop)
72
74
  return; // Overlay handles click-outside via backdrop
73
- if (e.target === triggerRef.current || ((_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
75
+ if (e.target === triggerRef.current ||
76
+ ((_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
74
77
  return;
75
78
  }
76
79
  if (closeOnClickOutside && !preventClose) {
@@ -103,8 +106,11 @@ const PopoverBody = React__default.forwardRef(({ children, className, animation
103
106
  if (contentRef) {
104
107
  contentRef.current = el;
105
108
  }
106
- if (portalContentRef && typeof portalContentRef === 'object' && 'current' in portalContentRef) {
107
- portalContentRef.current = el;
109
+ if (portalContentRef &&
110
+ typeof portalContentRef === "object" &&
111
+ "current" in portalContentRef) {
112
+ portalContentRef.current =
113
+ el;
108
114
  }
109
115
  }, [ref, contentRef, portalContentRef]);
110
116
  const isDarkMode = useDarkMode();
@@ -127,11 +133,16 @@ const PopoverBody = React__default.forwardRef(({ children, className, animation
127
133
  if (!preventClose)
128
134
  onOpenChange(false);
129
135
  }, closeOnOverlayClick: closeOnClickOutside && !preventClose, closeOnEsc: closeOnEsc && !preventClose, enableScrollLock: false, enableFocusTrap: false, position: "none", role: "dialog", ariaModal: true, contentClassName: contentClasses, contentStyle: contentPositionStyle, animations: animation && !shouldReduceMotion
130
- ? { overlay: overlayBackdropAnimations, content: overlayContentAnimations }
136
+ ? {
137
+ overlay: overlayBackdropAnimations,
138
+ content: overlayContentAnimations,
139
+ }
131
140
  : undefined, children: jsx("div", { id: contentId, "data-state": isOpen ? "open" : "closed", "data-placement": placement, "data-trigger": triggerType, ...domProps, children: children }) }));
132
141
  }
133
142
  // Without backdrop: manual portal + positioning + escape + click-outside
134
- const popoverContent = (jsx(AnimatePresence, { children: isOpen && (jsx(motion.div, { ref: handleRef, id: contentId, className: cn("fixed z-50", contentClasses), style: contentPositionStyle, role: "dialog", "aria-modal": "true", variants: shouldReduceMotion || !animation ? {} : popoverContentVariants, initial: shouldReduceMotion || !animation ? undefined : "hidden", animate: shouldReduceMotion || !animation ? undefined : "visible", exit: shouldReduceMotion || !animation ? undefined : "exit", transition: shouldReduceMotion || !animation ? { duration: 0 } : popoverTransitions.content, "data-state": isOpen ? "open" : "closed", "data-placement": placement, "data-trigger": triggerType, onClick: (e) => e.stopPropagation(), ...domProps, children: children })) }));
143
+ const popoverContent = (jsx(AnimatePresence, { children: isOpen && (jsx(motion.div, { ref: handleRef, id: contentId, className: cn("fixed z-50", contentClasses), style: contentPositionStyle, role: "dialog", "aria-modal": "true", variants: shouldReduceMotion || !animation ? {} : popoverContentVariants, initial: shouldReduceMotion || !animation ? undefined : "hidden", animate: shouldReduceMotion || !animation ? undefined : "visible", exit: shouldReduceMotion || !animation ? undefined : "exit", transition: shouldReduceMotion || !animation
144
+ ? { duration: 0 }
145
+ : popoverTransitions.content, "data-state": isOpen ? "open" : "closed", "data-placement": placement, "data-trigger": triggerType, onClick: (e) => e.stopPropagation(), ...domProps, children: children })) }));
135
146
  if (portal && isClient) {
136
147
  return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: popoverContent }), document.body);
137
148
  }
@@ -7,7 +7,7 @@ import { TagProps } from "./Tag.types";
7
7
  * @example
8
8
  * ```tsx
9
9
  * <Tag onRemove={() => handleRemove('tag1')}>JavaScript</Tag>
10
- * <Tag avatar={{ src: "/avatar.jpg", alt: "Alice" }} onRemove={() => {}}>Alice</Tag>
10
+ * <Tag avatar={{ src: "/avatar.jpg", name: "Alice" }} onRemove={() => {}}>Alice</Tag>
11
11
  * <Tag startContent={<CodeIcon className="size-4" />} onRemove={() => {}}>React</Tag>
12
12
  * ```
13
13
  */
@@ -14,7 +14,7 @@ import { tagVariants, tagLayoutTransition } from './Tag.animations.js';
14
14
  * @example
15
15
  * ```tsx
16
16
  * <Tag onRemove={() => handleRemove('tag1')}>JavaScript</Tag>
17
- * <Tag avatar={{ src: "/avatar.jpg", alt: "Alice" }} onRemove={() => {}}>Alice</Tag>
17
+ * <Tag avatar={{ src: "/avatar.jpg", name: "Alice" }} onRemove={() => {}}>Alice</Tag>
18
18
  * <Tag startContent={<CodeIcon className="size-4" />} onRemove={() => {}}>React</Tag>
19
19
  * ```
20
20
  */
@@ -47,7 +47,7 @@ const Tag = React__default.forwardRef(({ children, size = "md", removable = true
47
47
  // Avatar takes precedence over startContent
48
48
  const renderLeadingContent = () => {
49
49
  if (avatar) {
50
- return (jsx(Avatar, { src: avatar.src, alt: avatar.alt, fallback: avatar.fallback, initialsCount: avatar.initialsCount, className: cn(theme.avatarSizes[size], "rounded-full border-0") }));
50
+ return (jsx(Avatar, { src: avatar.src, name: avatar.name, fallback: avatar.fallback, initialsCount: avatar.initialsCount, className: cn(theme.avatarSizes[size], "rounded-full border-0") }));
51
51
  }
52
52
  if (startContent) {
53
53
  return (jsx("span", { className: cn("inline-flex items-center", theme.startContentSizes[size]), children: startContent }));
@@ -18,7 +18,7 @@ export interface TagProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "o
18
18
  */
19
19
  avatar?: {
20
20
  src?: string;
21
- alt: string;
21
+ name: string;
22
22
  fallback?: ReactNode;
23
23
  initialsCount?: number;
24
24
  };
@@ -18,7 +18,6 @@ export * from "./Dropdown";
18
18
  export * from "./Kbd";
19
19
  export * from "./MenuItem";
20
20
  export * from "./Link";
21
- export * from "./Loader";
22
21
  export * from "./Modal";
23
22
  export * from "./ModalStack";
24
23
  export * from "./PageHeading";
@@ -48,3 +47,4 @@ export * from "./OfflineIndicator";
48
47
  export * from "./ContextMenu";
49
48
  export * from "./Pill";
50
49
  export * from "./SocialIcon";
50
+ export * from "./FileTypeIcon";
@@ -36,7 +36,6 @@ export { Kbd } from './Kbd/Kbd.js';
36
36
  export { MenuItem } from './MenuItem/MenuItem.js';
37
37
  export { menuItemTheme } from './MenuItem/MenuItem.theme.js';
38
38
  export { Link } from './Link/Link.js';
39
- export { Loader } from './Loader/Loader.js';
40
39
  export { Modal } from './Modal/Modal.js';
41
40
  export { ModalStack } from './ModalStack/ModalStack.js';
42
41
  export { PageHeading } from './PageHeading/PageHeading.js';
@@ -98,6 +97,7 @@ export { contextMenuTheme } from './ContextMenu/ContextMenu.theme.js';
98
97
  export { Pill } from './Pill/Pill.js';
99
98
  export { pillTheme } from './Pill/Pill.theme.js';
100
99
  export { SocialIcon } from './SocialIcon/SocialIcon.js';
100
+ export { FileTypeIcon } from './FileTypeIcon/FileTypeIcon.js';
101
101
  export { AnimatePresence } from 'motion/react';
102
102
  export { CardBody } from './Card/CardBody.js';
103
103
  export { CardFooter } from './Card/CardFooter.js';
@@ -0,0 +1,40 @@
1
+ export declare const EASE: readonly [0.04, 0.62, 0.23, 0.98];
2
+ export declare const STAGGER_IN = 0.05;
3
+ export declare const STAGGER_OUT = 0.04;
4
+ export declare const STAGGER_BASE_DELAY = 0.15;
5
+ export declare const collapseLayerExitTiming: {
6
+ height: {
7
+ duration: number;
8
+ delay: number;
9
+ ease: readonly [0.04, 0.62, 0.23, 0.98];
10
+ };
11
+ opacity: {
12
+ duration: number;
13
+ delay: number;
14
+ };
15
+ };
16
+ export declare const collapseLayerEnterTiming: {
17
+ height: {
18
+ duration: number;
19
+ delay: number;
20
+ ease: readonly [0.04, 0.62, 0.23, 0.98];
21
+ };
22
+ opacity: {
23
+ duration: number;
24
+ delay: number;
25
+ };
26
+ };
27
+ export declare const expandLayerEnterTiming: {
28
+ height: {
29
+ duration: number;
30
+ delay: number;
31
+ ease: readonly [0.04, 0.62, 0.23, 0.98];
32
+ };
33
+ };
34
+ export declare const expandLayerExitTiming: {
35
+ height: {
36
+ duration: number;
37
+ delay: number;
38
+ ease: readonly [0.04, 0.62, 0.23, 0.98];
39
+ };
40
+ };
@@ -0,0 +1,26 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Feed.Collapse — multi-stage animation timing
3
+ // ---------------------------------------------------------------------------
4
+ const EASE = [0.04, 0.62, 0.23, 0.98];
5
+ // Stagger timing
6
+ const STAGGER_IN = 0.05; // 50ms between each item entering
7
+ const STAGGER_OUT = 0.04; // 40ms between each item exiting (slightly faster)
8
+ const STAGGER_BASE_DELAY = 0.15; // items start shortly after height begins expanding
9
+ // Collapsed layer (curve + label) — exit = collapsing away, enter = reappearing
10
+ const collapseLayerExitTiming = {
11
+ height: { duration: 0.3, delay: 0, ease: EASE },
12
+ opacity: { duration: 0.2, delay: 0 },
13
+ };
14
+ const collapseLayerEnterTiming = {
15
+ height: { duration: 0.35, delay: 0.05, ease: EASE },
16
+ opacity: { duration: 0.25, delay: 0.15 },
17
+ };
18
+ // Expanded layer (children container) — enter = growing, exit = shrinking
19
+ const expandLayerEnterTiming = {
20
+ height: { duration: 0.4, delay: 0, ease: EASE },
21
+ };
22
+ const expandLayerExitTiming = {
23
+ height: { duration: 0.35, delay: 0.05, ease: EASE },
24
+ };
25
+
26
+ export { EASE, STAGGER_BASE_DELAY, STAGGER_IN, STAGGER_OUT, collapseLayerEnterTiming, collapseLayerExitTiming, expandLayerEnterTiming, expandLayerExitTiming };
@@ -1,5 +1,9 @@
1
1
  import React from "react";
2
- import type { FeedProps, FeedItemProps } from "./Feed.types";
2
+ import type { FeedProps, FeedItemProps, FeedCollapseProps, FeedTheme } from "./Feed.types";
3
+ export declare const useFeedContext: () => {
4
+ theme: FeedTheme;
5
+ };
3
6
  export declare const Feed: React.ForwardRefExoticComponent<FeedProps & React.RefAttributes<HTMLDivElement>> & {
4
7
  Item: React.ForwardRefExoticComponent<FeedItemProps & React.RefAttributes<HTMLLIElement>>;
8
+ Collapse: React.FC<FeedCollapseProps>;
5
9
  };
@@ -1,11 +1,10 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import React__default, { createContext, useMemo, useState, useContext } from 'react';
2
+ import React__default, { createContext, useMemo, useState, useCallback, useContext } from 'react';
3
+ import { useReducedMotion, motion } from 'motion/react';
4
+ import { ChevronUpDownIcon } from '@heroicons/react/16/solid';
3
5
  import { cn } from '../../../utils/cn.js';
4
6
  import { feedTheme } from './Feed.theme.js';
5
- import { Avatar } from '../../core/Avatar/Avatar.js';
6
- import { Button } from '../../core/Button/Button.js';
7
- import { Textarea } from '../../forms/Textarea/Textarea.js';
8
- import '../../forms/Textarea/Textarea.theme.js';
7
+ import { collapseLayerExitTiming, collapseLayerEnterTiming, EASE, expandLayerEnterTiming, expandLayerExitTiming, STAGGER_BASE_DELAY, STAGGER_IN, STAGGER_OUT } from './Feed.animations.js';
9
8
 
10
9
  // ---------------------------------------------------------------------------
11
10
  // Context
@@ -29,68 +28,96 @@ FeedRoot.displayName = "Feed";
29
28
  // ---------------------------------------------------------------------------
30
29
  // Feed.Item
31
30
  // ---------------------------------------------------------------------------
32
- const FeedItem = React__default.forwardRef(({ variant = "default", title, timestamp, icon, avatar, children, placeholder = "Add your comment...", submitLabel = "Comment", onSubmit, className, ...props }, ref) => {
31
+ const FeedItemInner = ({ icon, indicatorClassName, timestamp, children }) => {
33
32
  const { theme } = useFeedContext();
34
- // Always call useState (rules of hooks) only used for comment-input
35
- const [inputValue, setInputValue] = useState("");
36
- // ------- Indicator rendering -------
37
- const renderIndicator = () => {
38
- var _a;
39
- switch (variant) {
40
- case "icon":
41
- return jsx("div", { className: theme.iconIndicatorStyle, children: icon });
42
- case "avatar":
43
- case "comment-input":
44
- return (jsx("div", { className: theme.avatarIndicatorStyle, children: jsx(Avatar, { src: avatar === null || avatar === void 0 ? void 0 : avatar.src, alt: (_a = avatar === null || avatar === void 0 ? void 0 : avatar.alt) !== null && _a !== void 0 ? _a : "", className: "size-10 ring-8 ring-[var(--color-background)]" }) }));
45
- case "default":
46
- default:
47
- return (jsx("div", { className: theme.dotIndicatorStyle, children: jsx("span", { className: "size-1.5 rounded-full bg-[var(--color-text-muted)]" }) }));
48
- }
49
- };
50
- // ------- Content rendering -------
51
- const renderContent = () => {
52
- switch (variant) {
53
- case "default":
54
- case "icon":
55
- return (jsxs("div", { className: cn(theme.contentStyle, "py-1.5"), children: [jsxs("p", { className: theme.actionTextStyle, children: [title && (typeof title === 'string'
56
- ? jsxs("span", { className: theme.titleStyle, children: [title, " "] })
57
- : title), children] }), timestamp && (typeof timestamp === 'string'
58
- ? jsx("p", { className: theme.timestampStyle, children: timestamp })
59
- : timestamp)] }));
60
- case "avatar":
61
- return (jsxs("div", { className: theme.contentStyle, children: [title && (typeof title === 'string'
62
- ? jsx("p", { className: cn(theme.titleStyle, "text-base"), children: title })
63
- : title), jsx("p", { className: cn(theme.actionTextStyle, "mt-0.5"), children: children }), timestamp && (typeof timestamp === 'string'
64
- ? jsx("p", { className: theme.timestampStyle, children: timestamp })
65
- : timestamp)] }));
66
- case "comment-input": {
67
- const handleSubmit = () => {
68
- const trimmed = inputValue.trim();
69
- if (trimmed && onSubmit) {
70
- onSubmit(trimmed);
71
- setInputValue("");
72
- }
73
- };
74
- const handleKeyDown = (e) => {
75
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
76
- e.preventDefault();
77
- handleSubmit();
78
- }
79
- };
80
- return (jsxs("div", { className: theme.commentInputWrapperStyle, children: [jsx(Textarea, { size: "sm", rows: 3, placeholder: placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyDown }), jsx("div", { className: theme.commentInputFooterStyle, children: jsx(Button, { size: "sm", onClick: handleSubmit, children: submitLabel }) })] }));
81
- }
82
- default:
83
- return null;
84
- }
85
- };
86
- return (jsx("li", { ref: ref, className: className, ...props, children: jsxs("div", { className: theme.itemInnerStyle, children: [jsx("span", { "aria-hidden": "true", className: theme.connectorStyle }), jsxs("div", { className: theme.itemContentStyle, children: [renderIndicator(), renderContent()] })] }) }));
33
+ return (jsxs("div", { className: theme.itemInnerStyle, children: [jsx("span", { "aria-hidden": "true", className: theme.connectorStyle }), jsxs("div", { className: theme.itemContentStyle, children: [icon ? (jsx("div", { className: cn(theme.indicatorStyle, indicatorClassName), children: icon })) : (jsx("div", { className: cn(theme.dotIndicatorStyle, indicatorClassName), children: jsx("span", { className: "size-1.5 rounded-full bg-[var(--color-text-muted)]" }) })), jsxs("div", { className: theme.contentStyle, children: [children, timestamp && jsx("p", { className: theme.timestampStyle, children: timestamp })] })] })] }));
34
+ };
35
+ const FeedItem = React__default.forwardRef(({ icon, indicatorClassName, timestamp, children, className, _as = "li", ...props }, ref) => {
36
+ const inner = (jsx(FeedItemInner, { icon: icon, indicatorClassName: indicatorClassName, timestamp: timestamp, children: children }));
37
+ if (_as === "div") {
38
+ return (jsx("div", { className: className, ...props, children: inner }));
39
+ }
40
+ return (jsx("li", { ref: ref, className: className, ...props, children: inner }));
87
41
  });
88
42
  FeedItem.displayName = "Feed.Item";
89
43
  // ---------------------------------------------------------------------------
44
+ // Feed.Collapse
45
+ // ---------------------------------------------------------------------------
46
+ /**
47
+ * SVG curved connector for the collapsed state.
48
+ * Draws a smooth parenthesis ) shape from (1,0) → (APEX,MID) → (1,H).
49
+ * Cubic beziers with vertical tangents at every junction so the curve
50
+ * connects seamlessly to the straight Feed connector above/below.
51
+ *
52
+ * Both CURVE_PATH and STRAIGHT_PATH use identical command structure
53
+ * (M, C, C) so framer-motion interpolates all numeric values smoothly.
54
+ */
55
+ const COLLAPSED_HEIGHT = 72;
56
+ const CURVE_APEX_X = 24;
57
+ const STROKE_WIDTH = 2;
58
+ const SVG_WIDTH = CURVE_APEX_X + STROKE_WIDTH;
59
+ const MID_Y = COLLAPSED_HEIGHT / 2;
60
+ const CURVE_PATH = `M 1 0 C 1 20, ${CURVE_APEX_X} 16, ${CURVE_APEX_X} ${MID_Y} C ${CURVE_APEX_X} 56, 1 52, 1 ${COLLAPSED_HEIGHT}`;
61
+ const STRAIGHT_PATH = `M 1 0 C 1 12, 1 24, 1 ${MID_Y} C 1 48, 1 60, 1 ${COLLAPSED_HEIGHT}`;
62
+ const FeedCollapse = ({ label = "Show {count} more", icon, defaultExpanded = false, expanded: controlledExpanded, onToggle, children, className, }) => {
63
+ const { theme } = useFeedContext();
64
+ const shouldReduceMotion = useReducedMotion();
65
+ const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
66
+ const isControlled = controlledExpanded !== undefined;
67
+ const expanded = isControlled ? controlledExpanded : internalExpanded;
68
+ const count = React__default.Children.count(children);
69
+ const resolvedLabel = label.replace("{count}", String(count));
70
+ const handleToggle = useCallback(() => {
71
+ const next = !expanded;
72
+ if (!isControlled)
73
+ setInternalExpanded(next);
74
+ onToggle === null || onToggle === void 0 ? void 0 : onToggle(next);
75
+ }, [expanded, isControlled, onToggle]);
76
+ // Clone children with _as="div" to avoid nested <li>
77
+ const clonedChildren = React__default.Children.map(children, (child) => {
78
+ if (React__default.isValidElement(child)) {
79
+ return React__default.cloneElement(child, {
80
+ _as: "div",
81
+ });
82
+ }
83
+ return child;
84
+ });
85
+ const collapseIcon = icon !== null && icon !== void 0 ? icon : (jsx(ChevronUpDownIcon, { className: "size-4 text-[var(--color-primary)]" }));
86
+ /* ---- Reduced motion: instant toggle, no staged animations ---- */
87
+ if (shouldReduceMotion) {
88
+ return (jsxs("li", { className: cn(theme.collapseStyle, className), children: [expanded && (jsx("span", { "aria-hidden": "true", className: "absolute left-5 -translate-x-1/2 top-0 h-full w-0.5 bg-[var(--color-border)]/60" })), !expanded ? (jsxs("div", { className: "relative", style: { height: COLLAPSED_HEIGHT }, children: [jsx("svg", { "aria-hidden": "true", className: theme.collapseSvgStyle, style: { left: "calc(1.25rem - 1px)" }, width: SVG_WIDTH, height: COLLAPSED_HEIGHT, fill: "none", children: jsx("path", { d: CURVE_PATH, stroke: "var(--color-border)", strokeOpacity: "0.6", strokeWidth: STROKE_WIDTH }) }), jsxs("button", { type: "button", className: "absolute top-1/2 -translate-y-1/2 flex items-center gap-2.5 cursor-pointer bg-transparent border-none p-0", style: {
89
+ left: `calc(1.25rem - 1px + ${CURVE_APEX_X + 12}px)`,
90
+ }, onClick: handleToggle, "aria-expanded": false, children: [jsx("div", { className: "flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--color-surface)] ", children: collapseIcon }), jsx("span", { className: theme.collapseButtonStyle, children: resolvedLabel })] })] })) : (jsxs("div", { children: [clonedChildren, jsx("div", { className: "relative pb-8", children: jsxs("div", { className: theme.itemContentStyle, children: [jsx("div", { className: "flex size-8 ml-1 shrink-0 items-center justify-center rounded-full bg-[var(--color-background-secondary)] ring-2 ring-[var(--color-background)]/50", children: collapseIcon }), jsx("div", { className: theme.contentStyle, children: jsx("button", { type: "button", className: theme.collapseButtonStyle, onClick: handleToggle, "aria-expanded": true, children: "Show less" }) })] }) })] }))] }));
91
+ }
92
+ /* ---- Full multi-stage animation ---- */
93
+ return (jsxs("li", { className: cn(theme.collapseStyle, className), children: [jsx(motion.span, { "aria-hidden": "true", className: "absolute left-5 -translate-x-1/2 top-0 h-full w-0.5 bg-[var(--color-border)]/60", animate: { opacity: expanded ? 1 : 0 }, transition: { duration: 0.2 } }), jsx(motion.div, { animate: {
94
+ height: expanded ? 0 : COLLAPSED_HEIGHT,
95
+ opacity: expanded ? 0 : 1,
96
+ }, transition: expanded ? collapseLayerExitTiming : collapseLayerEnterTiming, style: { overflow: "hidden", position: "relative" }, children: jsxs("div", { className: "relative", style: { height: COLLAPSED_HEIGHT }, children: [jsx("svg", { "aria-hidden": "true", className: theme.collapseSvgStyle, style: { left: "calc(1.25rem - 1px)" }, width: SVG_WIDTH, height: COLLAPSED_HEIGHT, fill: "none", children: jsx(motion.path, { animate: { d: expanded ? STRAIGHT_PATH : CURVE_PATH }, transition: { duration: 0.3, ease: EASE }, stroke: "var(--color-border)", strokeOpacity: "0.6", strokeWidth: STROKE_WIDTH }) }), jsxs(motion.button, { type: "button", className: "absolute top-1/2 -translate-y-1/2 flex items-center gap-2.5 cursor-pointer bg-transparent border-none p-0", style: {
97
+ left: `calc(1.25rem - 1px + ${CURVE_APEX_X + 12}px)`,
98
+ }, animate: { opacity: expanded ? 0 : 1 }, transition: { duration: 0.2, delay: expanded ? 0 : 0.2 }, onClick: handleToggle, "aria-expanded": false, children: [jsx("div", { className: "flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--color-surface)] border border-[var(--color-border)] shadow-sm", children: collapseIcon }), jsx("span", { className: theme.collapseButtonStyle, children: resolvedLabel })] })] }) }), jsxs(motion.div, { animate: { height: expanded ? "auto" : 0 }, transition: expanded ? expandLayerEnterTiming : expandLayerExitTiming, style: { overflow: "hidden" }, children: [React__default.Children.map(clonedChildren, (child, i) => (jsx(motion.div, { animate: {
99
+ opacity: expanded ? 1 : 0,
100
+ y: expanded ? 0 : 8,
101
+ }, transition: {
102
+ duration: 0.25,
103
+ delay: expanded
104
+ ? STAGGER_BASE_DELAY + i * STAGGER_IN
105
+ : (count - i) * STAGGER_OUT,
106
+ }, children: child }, i))), jsx(motion.div, { animate: {
107
+ opacity: expanded ? 1 : 0,
108
+ y: expanded ? 0 : 8,
109
+ }, transition: {
110
+ duration: 0.25,
111
+ delay: expanded ? STAGGER_BASE_DELAY + count * STAGGER_IN : 0,
112
+ }, children: jsx("div", { className: "relative pb-8", children: jsxs("div", { className: theme.itemContentStyle, children: [jsx("div", { className: "flex size-8 ml-1 shrink-0 items-center justify-center rounded-full bg-[var(--color-background-secondary)] ring-2 ring-[var(--color-background)]/50", children: collapseIcon }), jsx("div", { className: theme.contentStyle, children: jsx("button", { type: "button", className: theme.collapseButtonStyle, onClick: handleToggle, "aria-expanded": true, children: "Show less" }) })] }) }) })] })] }));
113
+ };
114
+ FeedCollapse.displayName = "Feed.Collapse";
115
+ // ---------------------------------------------------------------------------
90
116
  // Compound export
91
117
  // ---------------------------------------------------------------------------
92
118
  const Feed = Object.assign(FeedRoot, {
93
119
  Item: FeedItem,
120
+ Collapse: FeedCollapse,
94
121
  });
95
122
 
96
- export { Feed };
123
+ export { Feed, useFeedContext };
@@ -1,22 +1,20 @@
1
1
  const feedTheme = {
2
2
  // Structural
3
3
  baseStyle: "flow-root",
4
- listStyle: "-mb-8 [&>li:last-child_.feed-connector]:hidden",
4
+ listStyle: "-mb-8 [&>li:last-child>div>.feed-connector]:hidden",
5
5
  itemInnerStyle: "relative pb-8",
6
- itemContentStyle: "relative flex items-start space-x-3",
7
- connectorStyle: "feed-connector absolute top-5 left-[18px] h-full w-0.5 bg-[var(--color-border)]",
6
+ itemContentStyle: "relative flex items-start space-x-4",
7
+ connectorStyle: "feed-connector absolute top-5 left-5 -translate-x-1/2 h-full w-0.5 bg-[var(--color-border)]",
8
8
  // Indicators
9
- dotIndicatorStyle: "relative flex size-9 items-center justify-center",
10
- iconIndicatorStyle: "flex size-9 items-center justify-center rounded-full bg-[var(--color-background-tertiary)] ring-8 ring-[var(--color-background)]",
11
- avatarIndicatorStyle: "relative flex-shrink-0",
9
+ indicatorStyle: "relative flex size-8 ml-1 shrink-0 items-center justify-center bg-[var(--color-background-secondary)] rounded-full ring-2 ring-[var(--color-background)]/50",
10
+ dotIndicatorStyle: "relative flex size-10 items-center justify-center",
12
11
  // Content
13
- contentStyle: "min-w-0 flex-1",
14
- titleStyle: "font-medium text-[var(--color-text-primary)]",
15
- actionTextStyle: "text-base text-[var(--color-text-secondary)]",
16
- timestampStyle: "mt-1 text-sm text-[var(--color-text-muted)]/80 whitespace-nowrap font-medium",
17
- // Comment input
18
- commentInputWrapperStyle: "min-w-0 flex-1 space-y-2",
19
- commentInputFooterStyle: "flex justify-end",
12
+ contentStyle: "min-w-0 flex-1 pt-1",
13
+ timestampStyle: "mt-1 text-xs text-[var(--color-text-muted)]/80 whitespace-nowrap",
14
+ // Collapse
15
+ collapseStyle: "relative",
16
+ collapseButtonStyle: "flex items-center gap-1.5 text-sm font-medium text-[var(--color-primary)] hover:text-[var(--color-primary-700)] transition-colors cursor-pointer bg-transparent border-none p-0",
17
+ collapseSvgStyle: "absolute top-0",
20
18
  };
21
19
 
22
20
  export { feedTheme };
@@ -1,24 +1,17 @@
1
1
  import { ReactNode } from "react";
2
- export type FeedItemVariant = "default" | "icon" | "avatar" | "comment-input";
3
- export interface FeedAvatarConfig {
4
- src?: string;
5
- alt: string;
6
- }
7
2
  export interface FeedTheme {
8
3
  baseStyle: string;
9
4
  listStyle: string;
10
5
  itemInnerStyle: string;
11
6
  itemContentStyle: string;
12
7
  connectorStyle: string;
8
+ indicatorStyle: string;
13
9
  dotIndicatorStyle: string;
14
- iconIndicatorStyle: string;
15
- avatarIndicatorStyle: string;
16
10
  contentStyle: string;
17
- titleStyle: string;
18
- actionTextStyle: string;
19
11
  timestampStyle: string;
20
- commentInputWrapperStyle: string;
21
- commentInputFooterStyle: string;
12
+ collapseStyle: string;
13
+ collapseButtonStyle: string;
14
+ collapseSvgStyle: string;
22
15
  }
23
16
  export interface FeedThemeOverrides {
24
17
  baseStyle?: string;
@@ -26,39 +19,47 @@ export interface FeedThemeOverrides {
26
19
  itemInnerStyle?: string;
27
20
  itemContentStyle?: string;
28
21
  connectorStyle?: string;
22
+ indicatorStyle?: string;
29
23
  dotIndicatorStyle?: string;
30
- iconIndicatorStyle?: string;
31
- avatarIndicatorStyle?: string;
32
24
  contentStyle?: string;
33
- titleStyle?: string;
34
- actionTextStyle?: string;
35
25
  timestampStyle?: string;
36
- commentInputWrapperStyle?: string;
37
- commentInputFooterStyle?: string;
26
+ collapseStyle?: string;
27
+ collapseButtonStyle?: string;
28
+ collapseSvgStyle?: string;
38
29
  }
39
- export interface FeedProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
30
+ export interface FeedProps extends React.HTMLAttributes<HTMLDivElement> {
40
31
  children: ReactNode;
41
32
  theme?: FeedThemeOverrides;
42
33
  className?: string;
43
34
  }
44
- export interface FeedItemProps extends Omit<React.HTMLAttributes<HTMLLIElement>, "onSubmit" | "title"> {
45
- /** Visual variant of the feed item */
46
- variant?: FeedItemVariant;
47
- /** Name/label of the entity that performed the action */
48
- title?: ReactNode;
35
+ /** @internal Injected by Feed.Collapse via cloneElement */
36
+ export interface FeedItemInternalProps {
37
+ _as?: "li" | "div";
38
+ }
39
+ export interface FeedItemProps extends React.HTMLAttributes<HTMLLIElement>, FeedItemInternalProps {
40
+ /** Icon or avatar element rendered as the indicator */
41
+ icon?: ReactNode;
42
+ /** Custom className for the indicator wrapper (icon background circle) */
43
+ indicatorClassName?: string;
49
44
  /** Timestamp label (e.g. "6d ago") */
50
45
  timestamp?: ReactNode;
51
- /** Icon element for the "icon" variant indicator */
52
- icon?: ReactNode;
53
- /** Avatar configuration for avatar/comment-input variants */
54
- avatar?: FeedAvatarConfig;
55
- /** Body content (action description, comment text, etc.) */
46
+ /** Body content */
56
47
  children?: ReactNode;
57
- /** Placeholder text for the comment-input textarea */
58
- placeholder?: string;
59
- /** Label for the comment-input submit button */
60
- submitLabel?: string;
61
- /** Callback when the comment-input form is submitted */
62
- onSubmit?: (text: string) => void;
48
+ className?: string;
49
+ }
50
+ export interface FeedCollapseProps {
51
+ /** Template string with `{count}` placeholder for the number of hidden items */
52
+ label?: string;
53
+ /** Icon rendered next to the label */
54
+ icon?: ReactNode;
55
+ /** Uncontrolled initial expanded state */
56
+ defaultExpanded?: boolean;
57
+ /** Controlled expanded state */
58
+ expanded?: boolean;
59
+ /** Callback fired when the toggle is clicked */
60
+ onToggle?: (expanded: boolean) => void;
61
+ /** Feed.Item children to hide/reveal */
62
+ children: ReactNode;
63
+ /** Override className on the wrapper <li> */
63
64
  className?: string;
64
65
  }
@@ -1,2 +1,2 @@
1
1
  export { Feed } from "./Feed";
2
- export type { FeedProps, FeedItemProps, FeedItemVariant, FeedAvatarConfig, FeedTheme, FeedThemeOverrides, } from "./Feed.types";
2
+ export type { FeedProps, FeedItemProps, FeedCollapseProps, FeedItemInternalProps, FeedTheme, FeedThemeOverrides, } from "./Feed.types";
@@ -11,10 +11,10 @@ import { taskBarVariants } from './GanttChart.animations.js';
11
11
  import { useGanttDrag } from './hooks/useGanttDrag.js';
12
12
  import { getDaysBetween, addDays, toDate } from '../../../utils/dateUtils.js';
13
13
 
14
- const GanttTaskBar = React__default.memo(({ task, group, top, }) => {
14
+ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
15
15
  var _a;
16
16
  const { theme, dayWidth, rangeStart, rowHeight, readonly, readonlyDates, readonlyProgress, onTaskUpdate, onTaskClick, tasks, } = useGanttContext();
17
- const { hoveredTaskId, setHoveredTaskId, focusedTaskId, setActiveDrag, } = useGanttInteraction();
17
+ const { hoveredTaskId, setHoveredTaskId, focusedTaskId, setActiveDrag } = useGanttInteraction();
18
18
  const [tooltipPos, setTooltipPos] = useState(null);
19
19
  const offset = getDayOffset(task.startDate, rangeStart);
20
20
  const span = getDaysBetween(task.startDate, task.endDate);
@@ -66,7 +66,9 @@ const GanttTaskBar = React__default.memo(({ task, group, top, }) => {
66
66
  disabled: !canDragDates,
67
67
  onDragEnd: handleResizeLeftEnd,
68
68
  onDragStateChange: useCallback((isDragging, deltaX) => {
69
- setActiveDrag(isDragging ? { taskId: task.id, mode: "resize-left", deltaX } : null);
69
+ setActiveDrag(isDragging
70
+ ? { taskId: task.id, mode: "resize-left", deltaX }
71
+ : null);
70
72
  }, [setActiveDrag, task.id]),
71
73
  });
72
74
  // ----- Drag: Resize right -----
@@ -87,7 +89,9 @@ const GanttTaskBar = React__default.memo(({ task, group, top, }) => {
87
89
  disabled: !canDragDates,
88
90
  onDragEnd: handleResizeRightEnd,
89
91
  onDragStateChange: useCallback((isDragging, deltaX) => {
90
- setActiveDrag(isDragging ? { taskId: task.id, mode: "resize-right", deltaX } : null);
92
+ setActiveDrag(isDragging
93
+ ? { taskId: task.id, mode: "resize-right", deltaX }
94
+ : null);
91
95
  }, [setActiveDrag, task.id]),
92
96
  });
93
97
  // ----- Drag: Progress -----
@@ -151,9 +155,7 @@ const GanttTaskBar = React__default.memo(({ task, group, top, }) => {
151
155
  const hoveredTask = tasks.find((t) => t.id === hoveredTaskId);
152
156
  return (_c = (_b = hoveredTask === null || hoveredTask === void 0 ? void 0 : hoveredTask.dependencies) === null || _b === void 0 ? void 0 : _b.includes(task.id)) !== null && _c !== void 0 ? _c : false;
153
157
  }, [hoveredTaskId, task.id, task.dependencies, tasks]);
154
- const isFaded = hoveredTaskId !== null &&
155
- hoveredTaskId !== task.id &&
156
- !isHighlighted;
158
+ const isFaded = hoveredTaskId !== null && hoveredTaskId !== task.id && !isHighlighted;
157
159
  const isFocused = focusedTaskId === task.id;
158
160
  // ----- Click handler -----
159
161
  const handleClick = (e) => {
@@ -165,7 +167,7 @@ const GanttTaskBar = React__default.memo(({ task, group, top, }) => {
165
167
  const tooltipContent = (jsxs("div", { className: "text-sm space-y-2 min-w-[180px]", children: [jsx("div", { className: "font-semibold text-base", children: task.title }), jsxs("div", { className: "flex items-center gap-1.5 opacity-70", children: [jsx("span", { className: "inline-block size-2 rounded-full shrink-0", style: { backgroundColor: group.color } }), group.title] }), jsxs("div", { className: "space-y-0.5", children: [jsxs("div", { children: [formatDateShort(task.startDate), " \u2014 ", formatDateShort(task.endDate)] }), jsx("div", { className: "opacity-70", children: formatDuration(task.startDate, task.endDate) })] }), jsxs("div", { children: [jsxs("div", { className: "flex justify-between mb-1", children: [jsx("span", { children: "Progress" }), jsxs("span", { className: "font-medium", children: [task.progress, "%"] })] }), jsx("div", { className: "h-1.5 rounded-full bg-white/20 overflow-hidden", children: jsx("div", { className: "h-full rounded-full", style: {
166
168
  width: `${task.progress}%`,
167
169
  backgroundColor: barColor,
168
- } }) })] }), task.assignee && (jsxs("div", { className: "flex items-center gap-1.5 pt-0.5", children: [jsx(Avatar, { src: task.assignee.avatar, alt: task.assignee.name, size: "xs" }), jsx("span", { children: task.assignee.name })] })), isBehindSchedule && (jsxs("div", { className: "text-[var(--color-warning)] text-[10px]", children: ["Behind schedule (expected ", expectedProgress, "%)"] }))] }));
170
+ } }) })] }), task.assignee && (jsxs("div", { className: "flex items-center gap-1.5 pt-0.5", children: [jsx(Avatar, { src: task.assignee.avatar, name: task.assignee.name, size: "xs" }), jsx("span", { children: task.assignee.name })] })), isBehindSchedule && (jsxs("div", { className: "text-[var(--color-warning)] text-[10px]", children: ["Behind schedule (expected ", expectedProgress, "%)"] }))] }));
169
171
  return (jsxs(Fragment, { children: [jsx(GanttTaskContextMenu, { task: task, children: jsxs("div", { className: "absolute", style: {
170
172
  left: visualLeft,
171
173
  top: top + (rowHeight - barHeight) / 2,
@@ -195,7 +197,7 @@ const GanttTaskBar = React__default.memo(({ task, group, top, }) => {
195
197
  } }), isBehindSchedule && (jsx("div", { className: "absolute top-0 bottom-0 w-px", style: {
196
198
  left: `${expectedProgress}%`,
197
199
  borderLeft: "2px dashed rgba(255,255,255,0.5)",
198
- } })), task.completed && (jsx("div", { className: "absolute top-1/2 left-2 right-2 h-px bg-white/60", style: { transform: "translateY(-0.5px)" } })), canDragDates && (jsx("div", { className: "absolute left-0 top-0 bottom-0 w-2 cursor-ew-resize z-20 rounded-l-full opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/10", onPointerDown: resizeLeftDrag.handlePointerDown })), canDragDates && (jsx("div", { className: "absolute right-0 top-0 bottom-0 w-2 cursor-ew-resize z-20 rounded-r-full opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/10", onPointerDown: resizeRightDrag.handlePointerDown })), canDragProgress && !isNarrow && (jsx("div", { className: "absolute top-0 bottom-0 w-3 cursor-ew-resize z-20 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity", style: { left: `calc(${displayProgress}% - 6px)` }, onPointerDown: progressDrag.handlePointerDown, children: jsx("div", { className: "w-0.5 h-3 rounded-full bg-white/80 shadow" }) })), !isNarrow && (jsxs("div", { className: "relative flex items-center gap-1.5 px-1 z-10 min-w-0 pointer-events-none", children: [task.assignee && (jsx(Avatar, { src: task.assignee.avatar, alt: task.assignee.name, size: "xs" })), jsx("span", { className: "truncate text-sm text-[var(--color-text-inverse)] font-medium", children: task.title })] }))] }), isNarrow && (jsx("span", { className: "absolute text-sm font-medium whitespace-nowrap pointer-events-none", style: {
200
+ } })), task.completed && (jsx("div", { className: "absolute top-1/2 left-2 right-2 h-px bg-white/60", style: { transform: "translateY(-0.5px)" } })), canDragDates && (jsx("div", { className: "absolute left-0 top-0 bottom-0 w-2 cursor-ew-resize z-20 rounded-l-full opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/10", onPointerDown: resizeLeftDrag.handlePointerDown })), canDragDates && (jsx("div", { className: "absolute right-0 top-0 bottom-0 w-2 cursor-ew-resize z-20 rounded-r-full opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/10", onPointerDown: resizeRightDrag.handlePointerDown })), canDragProgress && !isNarrow && (jsx("div", { className: "absolute top-0 bottom-0 w-3 cursor-ew-resize z-20 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity", style: { left: `calc(${displayProgress}% - 6px)` }, onPointerDown: progressDrag.handlePointerDown, children: jsx("div", { className: "w-0.5 h-3 rounded-full bg-white/80 shadow" }) })), !isNarrow && (jsxs("div", { className: "relative flex items-center gap-1.5 px-1 z-10 min-w-0 pointer-events-none", children: [task.assignee && (jsx(Avatar, { src: task.assignee.avatar, name: task.assignee.name, size: "xs" })), jsx("span", { className: "truncate text-sm text-[var(--color-text-inverse)] font-medium", children: task.title })] }))] }), isNarrow && (jsx("span", { className: "absolute text-sm font-medium whitespace-nowrap pointer-events-none", style: {
199
201
  left: visualWidth + 6,
200
202
  top: barHeight / 2,
201
203
  transform: "translateY(-50%)",