@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.
- package/dist/components/ai/StreamingResponse/StreamingResponse.types.d.ts +7 -7
- package/dist/components/core/Accordion/Accordion.types.d.ts +1 -7
- package/dist/components/core/Accordion/AccordionTrigger.js +4 -6
- package/dist/components/core/Alert/Alert.types.d.ts +1 -1
- package/dist/components/core/Avatar/Avatar.js +22 -18
- package/dist/components/core/Avatar/Avatar.theme.js +1 -1
- package/dist/components/core/Avatar/Avatar.types.d.ts +9 -8
- package/dist/components/core/AvatarGroup/AvatarGroup.js +2 -2
- package/dist/components/core/Badge/Badge.js +7 -5
- package/dist/components/core/Badge/Badge.theme.js +11 -4
- package/dist/components/core/Badge/Badge.types.d.ts +2 -2
- package/dist/components/core/Button/Button.theme.js +2 -2
- package/dist/components/core/Calendar/CalendarEvent/CalendarEvent.types.d.ts +5 -6
- package/dist/components/core/FileTypeIcon/FileTypeIcon.d.ts +3 -0
- package/dist/components/core/FileTypeIcon/FileTypeIcon.js +33 -0
- package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.d.ts +2 -0
- package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.js +12 -0
- package/dist/components/core/FileTypeIcon/FileTypeIcon.types.d.ts +19 -0
- package/dist/components/core/FileTypeIcon/fileTypes.d.ts +7 -0
- package/dist/components/core/FileTypeIcon/fileTypes.js +30 -0
- package/dist/components/core/FileTypeIcon/index.d.ts +2 -0
- package/dist/components/core/MenuItem/MenuItem.theme.js +1 -1
- package/dist/components/core/Message/Message.js +1 -1
- package/dist/components/core/Message/Message.theme.js +2 -2
- package/dist/components/core/Message/Message.types.d.ts +6 -6
- package/dist/components/core/Message/MessageAvatar.d.ts +2 -2
- package/dist/components/core/Message/MessageAvatar.js +3 -3
- package/dist/components/core/Popover/PopoverBody.js +19 -8
- package/dist/components/core/Tag/Tag.d.ts +1 -1
- package/dist/components/core/Tag/Tag.js +2 -2
- package/dist/components/core/Tag/Tag.types.d.ts +1 -1
- package/dist/components/core/index.d.ts +1 -1
- package/dist/components/core/index.js +1 -1
- package/dist/components/data-display/Feed/Feed.animations.d.ts +40 -0
- package/dist/components/data-display/Feed/Feed.animations.js +26 -0
- package/dist/components/data-display/Feed/Feed.d.ts +5 -1
- package/dist/components/data-display/Feed/Feed.js +87 -60
- package/dist/components/data-display/Feed/Feed.theme.js +11 -13
- package/dist/components/data-display/Feed/Feed.types.d.ts +35 -34
- package/dist/components/data-display/Feed/index.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttTaskBar.js +11 -9
- package/dist/components/data-display/Timeline/TimelineMarker.js +2 -2
- package/dist/components/effects/3d/index.d.ts +1 -1
- package/dist/components/effects/AIOrb/AIOrb.d.ts +1 -0
- package/dist/components/effects/AIOrb/AIOrb.js +3 -2
- package/dist/components/effects/AIOrb/AIOrb.types.d.ts +1 -0
- package/dist/components/effects/AIOrb/hooks/index.d.ts +2 -0
- package/dist/components/effects/AIOrb/hooks/useOrbState.d.ts +20 -0
- package/dist/components/effects/AIOrb/hooks/useOrbState.js +27 -0
- package/dist/components/effects/AIOrb/index.d.ts +2 -2
- package/dist/components/effects/AIOrb/variants/Aura/Aura.d.ts +3 -0
- package/dist/components/effects/AIOrb/variants/Aura/Aura.js +75 -0
- package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.d.ts +28 -0
- package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.js +191 -0
- package/dist/components/effects/AIOrb/variants/Aura/Aura.types.d.ts +53 -0
- package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.d.ts +17 -0
- package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.js +149 -0
- package/dist/components/effects/AIOrb/variants/Aura/index.d.ts +2 -0
- package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.js +38 -7
- package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.types.d.ts +6 -0
- package/dist/components/effects/AIOrb/variants/Liquid/Liquid.js +34 -8
- package/dist/components/effects/AIOrb/variants/Liquid/Liquid.types.d.ts +6 -0
- package/dist/components/effects/AIOrb/variants/index.d.ts +2 -0
- package/dist/components/effects/CustomCursor/CustomCursor.d.ts +2 -2
- package/dist/components/effects/CustomCursor/CustomCursor.js +50 -47
- package/dist/components/effects/GlassSurface/GlassSurface.js +24 -12
- package/dist/components/effects/GlassSurface/GlassSurface.types.d.ts +8 -0
- package/dist/components/forms/Mention/Mention.js +2 -2
- package/dist/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/paletteGenerator.d.ts +46 -0
- package/package.json +1 -1
- package/src/styles/theme.css +2 -6
- package/dist/components/core/Loader/Loader.d.ts +0 -24
- package/dist/components/core/Loader/Loader.js +0 -57
- package/dist/components/core/Loader/Loader.theme.d.ts +0 -27
- package/dist/components/core/Loader/Loader.theme.js +0 -60
- package/dist/components/core/Loader/Loader.types.d.ts +0 -71
- 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 ===
|
|
9
|
+
if (variant === "system") {
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
12
|
-
return (jsx("div", { ref: ref, className: className, ...props, children: jsx(Avatar, { src: src,
|
|
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 =
|
|
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: () => {
|
|
65
|
-
|
|
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 ||
|
|
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 &&
|
|
107
|
-
portalContentRef
|
|
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
|
-
? {
|
|
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
|
|
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",
|
|
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",
|
|
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,
|
|
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,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 {
|
|
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
|
|
31
|
+
const FeedItemInner = ({ icon, indicatorClassName, timestamp, children }) => {
|
|
33
32
|
const { theme } = useFeedContext();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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-
|
|
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-
|
|
7
|
-
connectorStyle: "feed-connector absolute top-5 left-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
37
|
-
|
|
26
|
+
collapseStyle?: string;
|
|
27
|
+
collapseButtonStyle?: string;
|
|
28
|
+
collapseSvgStyle?: string;
|
|
38
29
|
}
|
|
39
|
-
export interface FeedProps extends
|
|
30
|
+
export interface FeedProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
40
31
|
children: ReactNode;
|
|
41
32
|
theme?: FeedThemeOverrides;
|
|
42
33
|
className?: string;
|
|
43
34
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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%)",
|