@flikk/ui 1.0.0-beta.26 → 1.0.0-beta.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ai/PromptInput/PromptInput.js +3 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -0
- package/dist/components/core/Button/Button.theme.js +1 -1
- package/dist/components/core/Calendar/Calendar.theme.js +1 -1
- package/dist/components/core/CommandPalette/CommandItem.js +4 -5
- package/dist/components/core/CommandPalette/CommandPalette.js +1 -1
- package/dist/components/core/CommandPalette/CommandPalette.theme.js +11 -13
- package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +0 -2
- package/dist/components/core/ContextMenu/ContextMenu.theme.js +13 -14
- package/dist/components/core/DotSeparator/DotSeparator.d.ts +3 -0
- package/dist/components/core/DotSeparator/DotSeparator.js +19 -0
- package/dist/components/core/DotSeparator/DotSeparator.theme.d.ts +2 -0
- package/dist/components/core/DotSeparator/DotSeparator.theme.js +10 -0
- package/dist/components/core/DotSeparator/DotSeparator.types.d.ts +25 -0
- package/dist/components/core/DotSeparator/index.d.ts +3 -0
- package/dist/components/core/Dropdown/Dropdown.theme.js +2 -3
- package/dist/components/core/Dropdown/DropdownItem.js +1 -1
- package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
- package/dist/components/core/MenuItem/MenuItem.js +5 -2
- package/dist/components/core/MenuItem/MenuItem.theme.d.ts +6 -0
- package/dist/components/core/MenuItem/MenuItem.theme.js +19 -16
- package/dist/components/core/MenuItem/MenuItem.types.d.ts +2 -0
- package/dist/components/core/NavItem/NavItem.js +20 -4
- package/dist/components/core/NavItem/NavItem.theme.js +13 -2
- package/dist/components/core/Popover/Popover.types.d.ts +6 -2
- package/dist/components/core/Popover/PopoverBody.js +10 -3
- package/dist/components/core/Segmented/Segmented.theme.js +1 -1
- package/dist/components/core/Sidebar/Sidebar.d.ts +2 -0
- package/dist/components/core/Sidebar/Sidebar.js +15 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +92 -4
- package/dist/components/core/Sidebar/SidebarCard.d.ts +25 -0
- package/dist/components/core/Sidebar/SidebarCard.js +68 -0
- package/dist/components/core/Sidebar/SidebarCard.theme.d.ts +8 -0
- package/dist/components/core/Sidebar/SidebarCard.theme.js +18 -0
- package/dist/components/core/Sidebar/SidebarContent.js +5 -1
- package/dist/components/core/Sidebar/SidebarContext.js +1 -1
- package/dist/components/core/Sidebar/SidebarHeader.js +10 -2
- package/dist/components/core/Sidebar/SidebarLogo.js +38 -3
- package/dist/components/core/Sidebar/SidebarSearch.d.ts +22 -0
- package/dist/components/core/Sidebar/SidebarSearch.js +135 -0
- package/dist/components/core/Sidebar/SidebarToggle.js +1 -1
- package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
- package/dist/components/core/Sidebar/index.d.ts +3 -1
- package/dist/components/core/Tag/Tag.js +29 -5
- package/dist/components/core/Tag/Tag.theme.js +13 -8
- package/dist/components/core/Tag/Tag.types.d.ts +7 -0
- package/dist/components/core/Tooltip/Tooltip.js +1 -1
- package/dist/components/core/Tooltip/Tooltip.theme.js +1 -2
- package/dist/components/core/index.d.ts +1 -0
- package/dist/components/core/index.js +4 -0
- package/dist/components/data-display/GanttChart/GanttChart.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttChart.js +31 -4
- package/dist/components/data-display/GanttChart/GanttChart.theme.js +19 -17
- package/dist/components/data-display/GanttChart/GanttChart.types.d.ts +19 -0
- package/dist/components/data-display/GanttChart/GanttChart.utils.js +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.js +3 -3
- package/dist/components/data-display/GanttChart/GanttDependencyArrows.js +7 -4
- package/dist/components/data-display/GanttChart/GanttMilestone.js +5 -5
- package/dist/components/data-display/GanttChart/GanttTablePanel.js +1 -1
- package/dist/components/data-display/GanttChart/GanttTableTaskRow.js +2 -2
- package/dist/components/data-display/GanttChart/GanttTaskBar.js +37 -16
- package/dist/components/data-display/GanttChart/GanttTimelineGrid.js +2 -1
- package/dist/components/data-display/GanttChart/GanttTimelineHeader.js +35 -11
- package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
- package/dist/components/data-display/GanttChart/hooks/useGanttDrag.js +3 -2
- package/dist/components/data-display/GanttChart/index.d.ts +1 -1
- package/dist/components/data-display/Table/Table.theme.js +3 -5
- package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
- package/dist/components/effects/Aurora/Aurora.js +297 -131
- package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
- package/dist/components/forms/Combobox/Combobox.js +1 -1
- package/dist/components/forms/Combobox/Combobox.theme.js +2 -4
- package/dist/components/forms/CronInput/CronInput.js +1 -1
- package/dist/components/forms/CronInput/CronInput.theme.js +14 -23
- package/dist/components/forms/DatePicker/DatePicker.theme.js +11 -18
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +6 -13
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
- package/dist/components/forms/FileUpload/FileUpload.animations.d.ts +29 -0
- package/dist/components/forms/FileUpload/FileUpload.animations.js +19 -0
- package/dist/components/forms/FileUpload/FileUpload.js +87 -48
- package/dist/components/forms/FileUpload/FileUpload.theme.d.ts +2 -10
- package/dist/components/forms/FileUpload/FileUpload.theme.js +16 -30
- package/dist/components/forms/FileUpload/FileUpload.types.d.ts +26 -10
- package/dist/components/forms/Input/Input.theme.js +13 -10
- package/dist/components/forms/InputCounter/InputCounter.theme.js +1 -1
- package/dist/components/forms/Mention/Mention.js +152 -57
- package/dist/components/forms/Mention/Mention.theme.js +16 -23
- package/dist/components/forms/Mention/Mention.types.d.ts +12 -12
- package/dist/components/forms/Mention/Mention.utils.js +32 -4
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +3 -3
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +5 -5
- package/dist/components/forms/Select/Select.js +2 -2
- package/dist/components/forms/Select/Select.theme.js +3 -3
- package/dist/components/forms/TimePicker/TimePickerContent.js +3 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +21 -16
- package/dist/components/forms/index.js +3 -0
- package/dist/components/layout/AppShell/AppShell.d.ts +10 -0
- package/dist/components/layout/AppShell/AppShell.js +84 -0
- package/dist/components/layout/AppShell/AppShell.theme.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShell.theme.js +42 -0
- package/dist/components/layout/AppShell/AppShell.types.d.ts +127 -0
- package/dist/components/layout/AppShell/AppShellContext.d.ts +14 -0
- package/dist/components/layout/AppShell/AppShellContext.js +19 -0
- package/dist/components/layout/AppShell/AppShellFooter.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellFooter.js +12 -0
- package/dist/components/layout/AppShell/AppShellHeader.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellHeader.js +12 -0
- package/dist/components/layout/AppShell/AppShellMain.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellMain.js +19 -0
- package/dist/components/layout/AppShell/AppShellRightPanel.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellRightPanel.js +16 -0
- package/dist/components/layout/AppShell/AppShellSidebar.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellSidebar.js +16 -0
- package/dist/components/layout/AppShell/AppShellTopbar.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellTopbar.js +12 -0
- package/dist/components/layout/AppShell/index.d.ts +3 -0
- package/dist/components/layout/FormLayout/FormLayout.js +3 -0
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.js +2 -0
- package/dist/hooks/useSelectPortal.d.ts +10 -2
- package/dist/hooks/useSelectPortal.js +45 -24
- package/dist/index.js +6 -0
- package/dist/registry.json +10021 -0
- package/dist/styles.css +1 -1
- package/dist/utils/dateUtils.js +11 -1
- package/package.json +4 -2
- package/src/global.scss +29 -8
- package/src/styles/theme.css +51 -12
|
@@ -30,6 +30,13 @@ export interface TagProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "o
|
|
|
30
30
|
startContent?: ReactNode;
|
|
31
31
|
/** Whether the tag is disabled */
|
|
32
32
|
disabled?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to render with motion (layout + enter/exit animation). Default true.
|
|
35
|
+
* Set `false` to render a plain `<span>` — required when a Tag lives inside a
|
|
36
|
+
* contentEditable host (e.g. Mention chips), where Framer-Motion's `layout`
|
|
37
|
+
* FLIP re-measurement fights the browser's caret/selection on every keystroke.
|
|
38
|
+
*/
|
|
39
|
+
animated?: boolean;
|
|
33
40
|
/** Additional class name for the tag */
|
|
34
41
|
className?: string;
|
|
35
42
|
/** Theme overrides */
|
|
@@ -115,7 +115,7 @@ const Tooltip = ({ content, children, placement = "top", delay = 300, disabled =
|
|
|
115
115
|
})
|
|
116
116
|
: children;
|
|
117
117
|
return (jsxs("div", { className: cn("relative inline-block", triggerClassName), ref: triggerRef, ...triggerHandlers, "aria-describedby": isVisible && id ? id : undefined, children: [childWithFocusHandlers, isClient &&
|
|
118
|
-
createPortal(jsx("div", { className: darkMode || isDarkMode ? "dark" : undefined, children: tooltipContent }), document.body)] }));
|
|
118
|
+
createPortal(jsx("div", { "data-flikkui-portal": "", className: darkMode || isDarkMode ? "dark" : undefined, children: tooltipContent }), document.body)] }));
|
|
119
119
|
};
|
|
120
120
|
// Set display name for dev tools and Storybook
|
|
121
121
|
Tooltip.displayName = "Tooltip";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const tooltipTheme = {
|
|
2
2
|
// Base style for the tooltip - using fixed positioning for portal rendering
|
|
3
|
-
baseStyle: "fixed z-[99] rounded-[var(--tooltip-radius)] py-1 px-2 text-xs text-[var(--color-text-primary)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 pointer-events-none bg-
|
|
4
|
-
"dark:bg-[var(--color-neutral-800)]/90 dark:outline-[var(--color-border)]/80 dark:border-[var(--color-border)]",
|
|
3
|
+
baseStyle: "fixed z-[99] rounded-[var(--tooltip-radius)] py-1 px-2 text-xs text-[var(--color-text-primary)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 pointer-events-none bg-[var(--color-surface-overlay)]/90 backdrop-blur-md outline outline-[var(--color-border)] shadow-lg",
|
|
5
4
|
};
|
|
6
5
|
|
|
7
6
|
export { tooltipTheme };
|
|
@@ -32,6 +32,8 @@ export { DropdownItem } from './Dropdown/DropdownItem.js';
|
|
|
32
32
|
export { DropdownSection } from './Dropdown/DropdownSection.js';
|
|
33
33
|
export { DropdownSeparator } from './Dropdown/DropdownSeparator.js';
|
|
34
34
|
export { dropdownTheme } from './Dropdown/Dropdown.theme.js';
|
|
35
|
+
export { DotSeparator } from './DotSeparator/DotSeparator.js';
|
|
36
|
+
export { dotSeparatorTheme } from './DotSeparator/DotSeparator.theme.js';
|
|
35
37
|
export { Kbd } from './Kbd/Kbd.js';
|
|
36
38
|
export { MenuItem } from './MenuItem/MenuItem.js';
|
|
37
39
|
export { menuItemTheme } from './MenuItem/MenuItem.theme.js';
|
|
@@ -100,6 +102,8 @@ export { SidebarToggle } from './Sidebar/SidebarToggle.js';
|
|
|
100
102
|
export { SidebarMobileTrigger } from './Sidebar/SidebarMobileTrigger.js';
|
|
101
103
|
export { SidebarUserProfile } from './Sidebar/SidebarUserProfile.js';
|
|
102
104
|
export { SidebarLogo } from './Sidebar/SidebarLogo.js';
|
|
105
|
+
export { SidebarSearch } from './Sidebar/SidebarSearch.js';
|
|
106
|
+
export { SidebarCard } from './Sidebar/SidebarCard.js';
|
|
103
107
|
export { SidebarProvider, useSidebarContext } from './Sidebar/SidebarContext.js';
|
|
104
108
|
export { OfflineIndicator } from './OfflineIndicator/OfflineIndicator.js';
|
|
105
109
|
export { offlineIndicatorTheme } from './OfflineIndicator/OfflineIndicator.theme.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GanttChartProps, GanttContextValue, GanttInteractionValue } from "./GanttChart.types";
|
|
2
2
|
export declare function useGanttContext(): GanttContextValue;
|
|
3
3
|
export declare function useGanttInteraction(): GanttInteractionValue;
|
|
4
|
-
declare function GanttChartComponent({ data, groups, columns, startDate, endDate, todayMarker, scrollToToday, readonly: readonlyProp, readonlyDates: readonlyDatesProp, readonlyProgress: readonlyProgressProp, zoom: zoomProp, defaultZoom, onZoomChange, collapsedGroups: collapsedProp, defaultCollapsedGroups, onCollapsedGroupsChange, hideCompleted: hideCompletedProp, defaultHideCompleted, onHideCompletedChange, tablePanelWidth: tablePanelWidthProp, defaultTablePanelWidth, onTablePanelWidthChange, onTaskUpdate, onTaskDelete, onTaskDuplicate, onTaskAdd, onGroupAdd, onTaskClick, holidays: holidaysProp, gridLines, className, theme: themeOverrides, rowHeight, }: GanttChartProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function GanttChartComponent({ data, groups, columns, startDate, endDate, todayMarker, scrollToToday, toolbar, showTable, showDependencies, showTaskLabels, emptyState, readonly: readonlyProp, readonlyDates: readonlyDatesProp, readonlyProgress: readonlyProgressProp, zoom: zoomProp, defaultZoom, onZoomChange, collapsedGroups: collapsedProp, defaultCollapsedGroups, onCollapsedGroupsChange, hideCompleted: hideCompletedProp, defaultHideCompleted, onHideCompletedChange, tablePanelWidth: tablePanelWidthProp, defaultTablePanelWidth, onTablePanelWidthChange, onTaskUpdate, onTaskDelete, onTaskDuplicate, onTaskAdd, onGroupAdd, onTaskClick, holidays: holidaysProp, gridLines, className, theme: themeOverrides, rowHeight, }: GanttChartProps): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
declare namespace GanttChartComponent {
|
|
6
6
|
var displayName: string;
|
|
7
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { useContext, createContext, useState, useCallback, useMemo } from 'react';
|
|
3
3
|
import { cn } from '../../../utils/cn.js';
|
|
4
4
|
import { ganttChartTheme } from './GanttChart.theme.js';
|
|
@@ -60,10 +60,30 @@ const defaultColumns = [
|
|
|
60
60
|
align: "center",
|
|
61
61
|
},
|
|
62
62
|
];
|
|
63
|
+
const defaultToolbarOptions = {
|
|
64
|
+
today: true,
|
|
65
|
+
zoom: true,
|
|
66
|
+
hideCompleted: true,
|
|
67
|
+
addGroup: true,
|
|
68
|
+
};
|
|
69
|
+
function resolveToolbarOptions(toolbar) {
|
|
70
|
+
if (toolbar === false) {
|
|
71
|
+
return {
|
|
72
|
+
today: false,
|
|
73
|
+
zoom: false,
|
|
74
|
+
hideCompleted: false,
|
|
75
|
+
addGroup: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (toolbar === true || toolbar === undefined) {
|
|
79
|
+
return defaultToolbarOptions;
|
|
80
|
+
}
|
|
81
|
+
return { ...defaultToolbarOptions, ...toolbar };
|
|
82
|
+
}
|
|
63
83
|
// ---------------------------------------------------------------------------
|
|
64
84
|
// Component
|
|
65
85
|
// ---------------------------------------------------------------------------
|
|
66
|
-
function GanttChartComponent({ data, groups, columns, startDate, endDate, todayMarker = true, scrollToToday = true, readonly: readonlyProp = false, readonlyDates: readonlyDatesProp = false, readonlyProgress: readonlyProgressProp = false, zoom: zoomProp, defaultZoom = "day", onZoomChange, collapsedGroups: collapsedProp, defaultCollapsedGroups = [], onCollapsedGroupsChange, hideCompleted: hideCompletedProp, defaultHideCompleted = false, onHideCompletedChange, tablePanelWidth: tablePanelWidthProp, defaultTablePanelWidth = 400, onTablePanelWidthChange, onTaskUpdate, onTaskDelete, onTaskDuplicate, onTaskAdd, onGroupAdd, onTaskClick, holidays: holidaysProp = [], gridLines = "both", className, theme: themeOverrides, rowHeight = 40, }) {
|
|
86
|
+
function GanttChartComponent({ data, groups, columns, startDate, endDate, todayMarker = true, scrollToToday = true, toolbar, showTable = true, showDependencies = true, showTaskLabels = "auto", emptyState, readonly: readonlyProp = false, readonlyDates: readonlyDatesProp = false, readonlyProgress: readonlyProgressProp = false, zoom: zoomProp, defaultZoom = "day", onZoomChange, collapsedGroups: collapsedProp, defaultCollapsedGroups = [], onCollapsedGroupsChange, hideCompleted: hideCompletedProp, defaultHideCompleted = false, onHideCompletedChange, tablePanelWidth: tablePanelWidthProp, defaultTablePanelWidth = 400, onTablePanelWidthChange, onTaskUpdate, onTaskDelete, onTaskDuplicate, onTaskAdd, onGroupAdd, onTaskClick, holidays: holidaysProp = [], gridLines = "both", className, theme: themeOverrides, rowHeight = 40, }) {
|
|
67
87
|
// ----- Controlled / uncontrolled state -----
|
|
68
88
|
const [uncontrolledZoom, setUncontrolledZoom] = useState(defaultZoom);
|
|
69
89
|
const isZoomControlled = zoomProp !== undefined;
|
|
@@ -111,6 +131,7 @@ function GanttChartComponent({ data, groups, columns, startDate, endDate, todayM
|
|
|
111
131
|
const rows = useGanttLayout(groups, data, collapsedGroups, hideCompleted);
|
|
112
132
|
const { tableRef, chartRef, handleTableScroll, handleChartScroll } = useGanttScrollSync();
|
|
113
133
|
const theme = useMemo(() => ({ ...ganttChartTheme, ...(themeOverrides || {}) }), [themeOverrides]);
|
|
134
|
+
const toolbarOptions = useMemo(() => resolveToolbarOptions(toolbar), [toolbar]);
|
|
114
135
|
// ----- Context -----
|
|
115
136
|
const contextValue = useMemo(() => ({
|
|
116
137
|
groups,
|
|
@@ -125,6 +146,11 @@ function GanttChartComponent({ data, groups, columns, startDate, endDate, todayM
|
|
|
125
146
|
collapsedGroups,
|
|
126
147
|
hideCompleted,
|
|
127
148
|
tablePanelWidth,
|
|
149
|
+
toolbar: toolbarOptions,
|
|
150
|
+
showTable,
|
|
151
|
+
showDependencies,
|
|
152
|
+
showTaskLabels,
|
|
153
|
+
emptyState,
|
|
128
154
|
readonly: readonlyProp,
|
|
129
155
|
readonlyDates: readonlyDatesProp,
|
|
130
156
|
readonlyProgress: readonlyProgressProp,
|
|
@@ -147,7 +173,8 @@ function GanttChartComponent({ data, groups, columns, startDate, endDate, todayM
|
|
|
147
173
|
rowHeight,
|
|
148
174
|
}), [
|
|
149
175
|
groups, data, resolvedColumns, rows, dates, zoom, collapsedGroups,
|
|
150
|
-
hideCompleted, tablePanelWidth,
|
|
176
|
+
hideCompleted, tablePanelWidth, toolbarOptions, showTable,
|
|
177
|
+
showDependencies, showTaskLabels, emptyState, readonlyProp, readonlyDatesProp,
|
|
151
178
|
readonlyProgressProp, onTaskUpdate, onTaskDelete, onTaskDuplicate,
|
|
152
179
|
onTaskAdd, onGroupAdd, onTaskClick, toggleGroup, setZoom,
|
|
153
180
|
setHideCompleted, setTablePanelWidth,
|
|
@@ -163,7 +190,7 @@ function GanttChartComponent({ data, groups, columns, startDate, endDate, todayM
|
|
|
163
190
|
activeDrag,
|
|
164
191
|
setActiveDrag,
|
|
165
192
|
}), [hoveredTaskId, focusedTaskId, activeDrag]);
|
|
166
|
-
return (jsx(GanttContext.Provider, { value: contextValue, children: jsx(GanttInteractionContext.Provider, { value: interactionValue, children: jsxs("div", { className: cn(theme.baseStyle, className), children: [jsx(GanttToolbar, {}), jsxs("div", { className: "flex flex-1 min-h-0 overflow-x-auto", children: [jsx(GanttTablePanel, { ref: tableRef, onScroll: handleTableScroll }), jsx(GanttSplitter, {}), jsx(GanttChartPanel, { ref: chartRef, onScroll: handleChartScroll, days: dates.days, monthSpans: dates.monthSpans })] })] }) }) }));
|
|
193
|
+
return (jsx(GanttContext.Provider, { value: contextValue, children: jsx(GanttInteractionContext.Provider, { value: interactionValue, children: jsxs("div", { className: cn(theme.baseStyle, className), children: [Object.values(toolbarOptions).some(Boolean) && jsx(GanttToolbar, {}), jsxs("div", { className: "flex flex-1 min-h-0 overflow-x-auto", children: [showTable && (jsxs(Fragment, { children: [jsx(GanttTablePanel, { ref: tableRef, onScroll: handleTableScroll }), jsx(GanttSplitter, {})] })), jsx(GanttChartPanel, { ref: chartRef, onScroll: showTable ? handleChartScroll : undefined, days: dates.days, monthSpans: dates.monthSpans })] })] }) }) }));
|
|
167
194
|
}
|
|
168
195
|
GanttChartComponent.displayName = "GanttChart";
|
|
169
196
|
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
const ganttChartTheme = {
|
|
2
|
-
baseStyle: "flex flex-col w-full overflow-hidden text-sm",
|
|
3
|
-
toolbarStyle: "flex flex-wrap items-center justify-between gap-
|
|
4
|
-
tablePanelStyle: "
|
|
2
|
+
baseStyle: "flex flex-col w-full overflow-hidden rounded-lg border border-[var(--color-border)] bg-[var(--color-background)] text-sm shadow-sm",
|
|
3
|
+
toolbarStyle: "flex flex-wrap items-center justify-between gap-2 px-3 py-2 border-b border-[var(--color-border)] bg-[var(--color-background-secondary)]/80",
|
|
4
|
+
tablePanelStyle: "flex flex-col shrink-0 overflow-hidden border-r border-[var(--color-border)] bg-[var(--color-background)]",
|
|
5
5
|
chartPanelStyle: "flex flex-col flex-1 min-w-0 overflow-hidden",
|
|
6
|
-
tableHeaderStyle: "flex items-center h-[56px] border-b border-[var(--color-border)] bg-[var(--color-background-secondary)]",
|
|
7
|
-
tableHeaderCellStyle: "px-3 font-
|
|
8
|
-
tableGroupRowStyle: "flex items-center cursor-pointer select-none hover:bg-[var(--color-background-secondary)] transition-colors",
|
|
9
|
-
tableTaskRowStyle: "flex items-center hover:bg-[var(--color-background-secondary)] transition-colors",
|
|
6
|
+
tableHeaderStyle: "flex items-center h-[56px] border-b border-[var(--color-border)] bg-[var(--color-background-secondary)]/80",
|
|
7
|
+
tableHeaderCellStyle: "px-3 text-[11px] font-semibold uppercase tracking-wide text-[var(--color-text-muted)] truncate",
|
|
8
|
+
tableGroupRowStyle: "flex items-center cursor-pointer select-none border-b border-[var(--color-border)]/30 bg-[var(--color-background-secondary)]/35 hover:bg-[var(--color-background-secondary)] transition-colors",
|
|
9
|
+
tableTaskRowStyle: "flex items-center border-b border-[var(--color-border)]/20 hover:bg-[var(--color-background-secondary)]/70 transition-colors",
|
|
10
10
|
tableCellStyle: "px-3 truncate text-[var(--color-text-primary)]",
|
|
11
|
-
timelineHeaderStyle: "bg-[var(--color-background)] border-b border-[var(--color-border)]",
|
|
12
|
-
timelineMonthStyle: "text-
|
|
13
|
-
timelineDayStyle: "text-[10px] text-[var(--color-text-muted)] flex items-center justify-center",
|
|
14
|
-
gridLineStyle: "border-r border-[var(--color-border)]/
|
|
15
|
-
weekendStyle: "bg-[var(--color-background-secondary)]/
|
|
16
|
-
todayMarkerStyle: "bg-[var(--color-primary)] w-
|
|
17
|
-
todayColumnStyle: "bg-[var(--color-primary)]/
|
|
18
|
-
taskBarStyle: "absolute
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
timelineHeaderStyle: "bg-[var(--color-background)] border-b border-[var(--color-border)] shadow-[0_1px_0_var(--color-border)]",
|
|
12
|
+
timelineMonthStyle: "text-[11px] font-semibold uppercase tracking-wide text-[var(--color-text-secondary)] px-2 border-b border-[var(--color-border)] flex items-center bg-[var(--color-background-secondary)]/45",
|
|
13
|
+
timelineDayStyle: "text-[10px] text-[var(--color-text-muted)] flex items-center justify-center border-r border-[var(--color-border)]/20",
|
|
14
|
+
gridLineStyle: "border-r border-[var(--color-border)]/20",
|
|
15
|
+
weekendStyle: "bg-[var(--color-background-secondary)]/35",
|
|
16
|
+
todayMarkerStyle: "bg-[var(--color-primary)] w-0.5",
|
|
17
|
+
todayColumnStyle: "bg-[var(--color-primary)]/10",
|
|
18
|
+
taskBarStyle: "absolute cursor-pointer flex items-center overflow-hidden shadow-sm ring-1 ring-black/10",
|
|
19
|
+
taskLabelOutsideStyle: "absolute text-xs font-medium whitespace-nowrap pointer-events-none drop-shadow-sm",
|
|
20
|
+
emptyStateStyle: "flex flex-col items-center gap-2 px-6 py-8 text-center text-[var(--color-text-muted)]",
|
|
21
|
+
groupBarStyle: "absolute rounded-full opacity-40",
|
|
22
|
+
splitterStyle: "w-1 cursor-col-resize bg-[var(--color-border)]/70 hover:bg-[var(--color-primary)] transition-colors shrink-0",
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
export { ganttChartTheme };
|
|
@@ -41,6 +41,13 @@ export interface GanttDragState {
|
|
|
41
41
|
deltaX: number;
|
|
42
42
|
}
|
|
43
43
|
export type GanttZoom = "day" | "week" | "quarter" | "month" | "year";
|
|
44
|
+
export type GanttTaskLabelMode = "auto" | "inside" | "outside" | "none";
|
|
45
|
+
export interface GanttToolbarOptions {
|
|
46
|
+
today?: boolean;
|
|
47
|
+
zoom?: boolean;
|
|
48
|
+
hideCompleted?: boolean;
|
|
49
|
+
addGroup?: boolean;
|
|
50
|
+
}
|
|
44
51
|
export declare const DAY_WIDTHS: Record<GanttZoom, number>;
|
|
45
52
|
export type GanttRowType = "group" | "task";
|
|
46
53
|
export interface GanttGroupRow {
|
|
@@ -75,6 +82,8 @@ export interface GanttChartThemeOverrides {
|
|
|
75
82
|
todayMarkerStyle?: string;
|
|
76
83
|
todayColumnStyle?: string;
|
|
77
84
|
taskBarStyle?: string;
|
|
85
|
+
taskLabelOutsideStyle?: string;
|
|
86
|
+
emptyStateStyle?: string;
|
|
78
87
|
groupBarStyle?: string;
|
|
79
88
|
splitterStyle?: string;
|
|
80
89
|
}
|
|
@@ -91,6 +100,11 @@ export interface GanttContextValue {
|
|
|
91
100
|
collapsedGroups: string[];
|
|
92
101
|
hideCompleted: boolean;
|
|
93
102
|
tablePanelWidth: number;
|
|
103
|
+
toolbar: GanttToolbarOptions;
|
|
104
|
+
showTable: boolean;
|
|
105
|
+
showDependencies: boolean;
|
|
106
|
+
showTaskLabels: GanttTaskLabelMode;
|
|
107
|
+
emptyState?: ReactNode;
|
|
94
108
|
readonly: boolean;
|
|
95
109
|
readonlyDates: boolean;
|
|
96
110
|
readonlyProgress: boolean;
|
|
@@ -133,6 +147,11 @@ export interface GanttChartProps {
|
|
|
133
147
|
endDate?: Date | string;
|
|
134
148
|
todayMarker?: boolean;
|
|
135
149
|
scrollToToday?: boolean;
|
|
150
|
+
toolbar?: boolean | GanttToolbarOptions;
|
|
151
|
+
showTable?: boolean;
|
|
152
|
+
showDependencies?: boolean;
|
|
153
|
+
showTaskLabels?: GanttTaskLabelMode;
|
|
154
|
+
emptyState?: ReactNode;
|
|
136
155
|
readonly?: boolean;
|
|
137
156
|
readonlyDates?: boolean;
|
|
138
157
|
readonlyProgress?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toDate, getDaysBetween } from '../../../utils/dateUtils.js';
|
|
2
|
-
export { addDays, isSameDay, isWeekend } from '../../../utils/dateUtils.js';
|
|
2
|
+
export { addDays, getWeekNumber, isSameDay, isWeekend } from '../../../utils/dateUtils.js';
|
|
3
3
|
|
|
4
4
|
// Re-export generic date utilities from shared utils so existing consumers
|
|
5
5
|
// that import from "GanttChart.utils" continue to work.
|
|
@@ -11,7 +11,7 @@ import { GanttGroupBar } from './GanttGroupBar.js';
|
|
|
11
11
|
import { GanttMilestone } from './GanttMilestone.js';
|
|
12
12
|
|
|
13
13
|
const GanttChartPanel = forwardRef(({ onScroll, days, monthSpans }, ref) => {
|
|
14
|
-
const { theme, rows, tasks, dayWidth, rowHeight, todayOffset, scrollToToday, onTaskClick, } = useGanttContext();
|
|
14
|
+
const { theme, rows, tasks, dayWidth, rowHeight, todayOffset, scrollToToday, onTaskClick, showDependencies, emptyState, } = useGanttContext();
|
|
15
15
|
const { focusedTaskId, setFocusedTaskId } = useGanttInteraction();
|
|
16
16
|
const totalWidth = days.length * dayWidth;
|
|
17
17
|
const bodyHeight = rows.length * rowHeight;
|
|
@@ -64,9 +64,9 @@ const GanttChartPanel = forwardRef(({ onScroll, days, monthSpans }, ref) => {
|
|
|
64
64
|
}
|
|
65
65
|
}, [rows, focusedTaskId, setFocusedTaskId, onTaskClick]);
|
|
66
66
|
if (tasks.length === 0) {
|
|
67
|
-
return (jsx("div", { className: cn(theme.chartPanelStyle, "items-center justify-center"), children: jsxs("div", { className:
|
|
67
|
+
return (jsx("div", { className: cn(theme.chartPanelStyle, "items-center justify-center"), children: emptyState !== null && emptyState !== void 0 ? emptyState : (jsxs("div", { className: cn(theme.emptyStateStyle), children: [jsx(CalendarDaysIcon, { className: "size-10 opacity-40" }), jsx("div", { className: "text-base font-medium text-[var(--color-text-secondary)]", children: "No tasks to display" }), jsx("div", { className: "text-sm opacity-70", children: "Add tasks to see them on the timeline" })] })) }));
|
|
68
68
|
}
|
|
69
|
-
return (jsx("div", { className: cn(theme.chartPanelStyle), children: jsx("div", { ref: ref, className: "overflow-auto flex-1 min-h-0 focus:outline-none", onScroll: onScroll, onKeyDown: handleKeyDown, tabIndex: 0, role: "grid", "aria-label": "Gantt chart timeline", "data-gantt-chart-scroll": true, children: jsxs("div", { style: { width: totalWidth }, children: [jsx("div", { className: "sticky top-0 z-10", children: jsx(GanttTimelineHeader, { days: days, monthSpans: monthSpans }) }), jsxs("div", { className: "relative", style: { height: bodyHeight }, children: [jsx(GanttTimelineGrid, { days: days, height: bodyHeight }), jsx(GanttDependencyArrows, {}), rows.map((row, i) => {
|
|
69
|
+
return (jsx("div", { className: cn(theme.chartPanelStyle), children: jsx("div", { ref: ref, className: "overflow-auto flex-1 min-h-0 focus:outline-none", onScroll: onScroll, onKeyDown: handleKeyDown, tabIndex: 0, role: "grid", "aria-label": "Gantt chart timeline", "data-gantt-chart-scroll": true, children: jsxs("div", { style: { width: totalWidth }, children: [jsx("div", { className: "sticky top-0 z-10", children: jsx(GanttTimelineHeader, { days: days, monthSpans: monthSpans }) }), jsxs("div", { className: "relative", style: { height: bodyHeight }, children: [jsx(GanttTimelineGrid, { days: days, height: bodyHeight }), showDependencies && jsx(GanttDependencyArrows, {}), rows.map((row, i) => {
|
|
70
70
|
const top = i * rowHeight;
|
|
71
71
|
if (row.type === "group") {
|
|
72
72
|
return (jsx(GanttGroupBar, { row: row, top: top }, `group-${row.group.id}`));
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useId, useMemo } from 'react';
|
|
3
3
|
import { useGanttContext, useGanttInteraction } from './GanttChart.js';
|
|
4
4
|
import { buildDependencyArrows } from './GanttChart.utils.js';
|
|
5
5
|
|
|
6
6
|
const GanttDependencyArrows = () => {
|
|
7
7
|
const { tasks, rows, dayWidth, rowHeight, rangeStart } = useGanttContext();
|
|
8
8
|
const { hoveredTaskId, activeDrag } = useGanttInteraction();
|
|
9
|
+
const markerId = useId();
|
|
10
|
+
const arrowheadId = `${markerId}-gantt-arrowhead`;
|
|
11
|
+
const highlightedArrowheadId = `${markerId}-gantt-arrowhead-highlight`;
|
|
9
12
|
const arrows = useMemo(() => buildDependencyArrows(tasks, rows, dayWidth, rowHeight, rangeStart, activeDrag), [tasks, rows, dayWidth, rowHeight, rangeStart, activeDrag]);
|
|
10
13
|
if (arrows.length === 0)
|
|
11
14
|
return null;
|
|
@@ -17,14 +20,14 @@ const GanttDependencyArrows = () => {
|
|
|
17
20
|
const [sourceId, targetId] = arrowId.split("->");
|
|
18
21
|
return sourceId === hoveredTaskId || targetId === hoveredTaskId;
|
|
19
22
|
};
|
|
20
|
-
return (jsxs("svg", { className: "absolute inset-0 pointer-events-none", style: { overflow: "visible" }, children: [jsxs("defs", { children: [jsx("marker", { id:
|
|
23
|
+
return (jsxs("svg", { className: "absolute inset-0 pointer-events-none", style: { overflow: "visible" }, children: [jsxs("defs", { children: [jsx("marker", { id: arrowheadId, markerWidth: "8", markerHeight: "6", refX: "8", refY: "3", orient: "auto", children: jsx("path", { d: "M 0 0 L 8 3 L 0 6 Z", fill: "var(--color-text-secondary, #888)" }) }), jsx("marker", { id: highlightedArrowheadId, markerWidth: "8", markerHeight: "6", refX: "8", refY: "3", orient: "auto", children: jsx("path", { d: "M 0 0 L 8 3 L 0 6 Z", fill: "var(--color-primary, #3B82F6)" }) })] }), arrows.map((arrow) => {
|
|
21
24
|
const connected = isArrowConnected(arrow.id);
|
|
22
25
|
const faded = hoveredTaskId !== null && !connected;
|
|
23
26
|
return (jsx("path", { d: arrow.d, fill: "none", stroke: connected
|
|
24
27
|
? "var(--color-primary, #3B82F6)"
|
|
25
28
|
: "var(--color-text-secondary, #888)", strokeWidth: connected ? 2.5 : 1.5, strokeDasharray: arrow.isBackward ? "4 3" : undefined, markerEnd: connected
|
|
26
|
-
?
|
|
27
|
-
:
|
|
29
|
+
? `url(#${highlightedArrowheadId})`
|
|
30
|
+
: `url(#${arrowheadId})`, opacity: faded ? 0.15 : connected ? 1 : 0.6, style: {
|
|
28
31
|
transition: activeDrag
|
|
29
32
|
? "none"
|
|
30
33
|
: "opacity 0.15s, stroke-width 0.15s",
|
|
@@ -8,7 +8,7 @@ import { GanttTaskContextMenu } from './GanttTaskContextMenu.js';
|
|
|
8
8
|
|
|
9
9
|
const GanttMilestone = React__default.memo(({ task, group, top, }) => {
|
|
10
10
|
var _a;
|
|
11
|
-
const { dayWidth, rangeStart, rowHeight, onTaskClick, readonly } = useGanttContext();
|
|
11
|
+
const { dayWidth, rangeStart, rowHeight, onTaskClick, readonly, showTaskLabels, theme, } = useGanttContext();
|
|
12
12
|
const offset = getDayOffset(task.endDate, rangeStart);
|
|
13
13
|
const left = offset * dayWidth + dayWidth / 2;
|
|
14
14
|
const size = 14;
|
|
@@ -26,19 +26,19 @@ const GanttMilestone = React__default.memo(({ task, group, top, }) => {
|
|
|
26
26
|
return (jsx(GanttTaskContextMenu, { task: task, children: jsx(Tooltip, { content: tooltipContent, placement: "top", children: jsxs("div", { className: "absolute", style: {
|
|
27
27
|
left: left - size / 2,
|
|
28
28
|
top: top + rowHeight / 2 - size / 2,
|
|
29
|
-
}, onClick: onTaskClick ? handleClick : undefined, onKeyDown: handleKeyDown, role: "button", tabIndex: onTaskClick ? 0 : undefined, "aria-label": `Milestone: ${task.title}`, children: [jsx("div", { className: `hover:scale-110 transition-transform ${readonly ? "cursor-default" : "cursor-pointer"}`, style: {
|
|
29
|
+
}, onClick: onTaskClick ? handleClick : undefined, onKeyDown: handleKeyDown, role: onTaskClick ? "button" : "img", tabIndex: onTaskClick ? 0 : undefined, "aria-label": `Milestone: ${task.title}`, children: [jsx("div", { className: `hover:scale-110 transition-transform ${readonly ? "cursor-default" : "cursor-pointer"}`, style: {
|
|
30
30
|
width: size,
|
|
31
31
|
height: size,
|
|
32
32
|
backgroundColor: barColor,
|
|
33
33
|
border: "2px solid var(--color-background, white)",
|
|
34
34
|
transform: "rotate(45deg)",
|
|
35
35
|
boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
|
|
36
|
-
} }), jsx("span", { className:
|
|
37
|
-
left: size +
|
|
36
|
+
} }), showTaskLabels !== "none" && showTaskLabels !== "inside" && (jsx("span", { className: theme.taskLabelOutsideStyle, style: {
|
|
37
|
+
left: size + 6,
|
|
38
38
|
top: size / 2,
|
|
39
39
|
transform: "translateY(-50%)",
|
|
40
40
|
color: barColor,
|
|
41
|
-
}, children: task.title })] }) }) }));
|
|
41
|
+
}, children: task.title }))] }) }) }));
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
export { GanttMilestone };
|
|
@@ -8,7 +8,7 @@ import { GanttTableTaskRow } from './GanttTableTaskRow.js';
|
|
|
8
8
|
|
|
9
9
|
const GanttTablePanel = forwardRef(({ onScroll }, ref) => {
|
|
10
10
|
const { theme, rows, tablePanelWidth } = useGanttContext();
|
|
11
|
-
return (jsxs("div", { className: cn(theme.tablePanelStyle), style: { width: tablePanelWidth }, children: [jsx(GanttTableHeader, {}), jsx("div", { ref: ref, className: "overflow-y-auto overflow-x-hidden flex-1 min-h-0", onScroll: onScroll, children: rows.map((row, i) => {
|
|
11
|
+
return (jsxs("div", { className: cn(theme.tablePanelStyle), style: { width: tablePanelWidth, minWidth: Math.min(tablePanelWidth, 280) }, children: [jsx(GanttTableHeader, {}), jsx("div", { ref: ref, className: "overflow-y-auto overflow-x-hidden flex-1 min-h-0", onScroll: onScroll, children: rows.map((row, i) => {
|
|
12
12
|
if (row.type === "group") {
|
|
13
13
|
return (jsx(GanttTableGroupRow, { row: row }, `group-${row.group.id}`));
|
|
14
14
|
}
|
|
@@ -15,8 +15,8 @@ function getCellValue(task, group, column) {
|
|
|
15
15
|
}
|
|
16
16
|
const GanttTableTaskRow = React__default.memo(({ task, group, }) => {
|
|
17
17
|
const { theme, columns, rowHeight, onTaskClick } = useGanttContext();
|
|
18
|
-
const { focusedTaskId } = useGanttInteraction();
|
|
19
|
-
return (jsx(GanttTaskContextMenu, { task: task, children: jsx("div", { className: cn(theme.tableTaskRowStyle, "group", onTaskClick && "cursor-pointer", focusedTaskId === task.id && "bg-[var(--color-primary)]/5"), style: { height: rowHeight }, onClick: onTaskClick ? (e) => onTaskClick(task, e) : undefined, onKeyDown: onTaskClick ? (e) => {
|
|
18
|
+
const { focusedTaskId, setFocusedTaskId, setHoveredTaskId } = useGanttInteraction();
|
|
19
|
+
return (jsx(GanttTaskContextMenu, { task: task, children: jsx("div", { className: cn(theme.tableTaskRowStyle, "group", onTaskClick && "cursor-pointer", focusedTaskId === task.id && "bg-[var(--color-primary)]/5"), style: { height: rowHeight }, onMouseEnter: () => setHoveredTaskId(task.id), onMouseLeave: () => setHoveredTaskId(null), onClick: onTaskClick ? (e) => onTaskClick(task, e) : undefined, onFocus: () => setFocusedTaskId(task.id), onBlur: () => setFocusedTaskId(null), onKeyDown: onTaskClick ? (e) => {
|
|
20
20
|
if (e.key === "Enter" || e.key === " ") {
|
|
21
21
|
e.preventDefault();
|
|
22
22
|
onTaskClick(task, e);
|
|
@@ -13,8 +13,8 @@ import { getDaysBetween, addDays, toDate } from '../../../utils/dateUtils.js';
|
|
|
13
13
|
|
|
14
14
|
const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
15
15
|
var _a;
|
|
16
|
-
const { theme, dayWidth, rangeStart, rowHeight, readonly, readonlyDates, readonlyProgress, onTaskUpdate, onTaskClick, tasks, } = useGanttContext();
|
|
17
|
-
const { hoveredTaskId, setHoveredTaskId, focusedTaskId, setActiveDrag } = useGanttInteraction();
|
|
16
|
+
const { theme, dayWidth, rangeStart, rowHeight, readonly, readonlyDates, readonlyProgress, onTaskUpdate, onTaskClick, tasks, showTaskLabels, } = useGanttContext();
|
|
17
|
+
const { hoveredTaskId, setHoveredTaskId, focusedTaskId, setFocusedTaskId, 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);
|
|
@@ -26,6 +26,13 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
26
26
|
const canDragProgress = !readonly && !readonlyProgress && !!onTaskUpdate;
|
|
27
27
|
const expectedProgress = calculateExpectedProgress(task.startDate, task.endDate);
|
|
28
28
|
const isBehindSchedule = !task.completed && task.progress < expectedProgress;
|
|
29
|
+
const updateActiveDrag = useCallback((isDragging, deltaX, mode) => {
|
|
30
|
+
if (isDragging) {
|
|
31
|
+
setTooltipPos(null);
|
|
32
|
+
setHoveredTaskId(null);
|
|
33
|
+
}
|
|
34
|
+
setActiveDrag(isDragging ? { taskId: task.id, mode, deltaX } : null);
|
|
35
|
+
}, [setActiveDrag, setHoveredTaskId, task.id]);
|
|
29
36
|
// ----- Drag: Move -----
|
|
30
37
|
const handleMoveDragEnd = useCallback((result) => {
|
|
31
38
|
if (!onTaskUpdate || result.daysDelta === 0)
|
|
@@ -45,8 +52,8 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
45
52
|
disabled: !canDragDates,
|
|
46
53
|
onDragEnd: handleMoveDragEnd,
|
|
47
54
|
onDragStateChange: useCallback((isDragging, deltaX) => {
|
|
48
|
-
|
|
49
|
-
}, [
|
|
55
|
+
updateActiveDrag(isDragging, deltaX, "move");
|
|
56
|
+
}, [updateActiveDrag]),
|
|
50
57
|
});
|
|
51
58
|
// ----- Drag: Resize left -----
|
|
52
59
|
const handleResizeLeftEnd = useCallback((result) => {
|
|
@@ -66,10 +73,8 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
66
73
|
disabled: !canDragDates,
|
|
67
74
|
onDragEnd: handleResizeLeftEnd,
|
|
68
75
|
onDragStateChange: useCallback((isDragging, deltaX) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
: null);
|
|
72
|
-
}, [setActiveDrag, task.id]),
|
|
76
|
+
updateActiveDrag(isDragging, deltaX, "resize-left");
|
|
77
|
+
}, [updateActiveDrag]),
|
|
73
78
|
});
|
|
74
79
|
// ----- Drag: Resize right -----
|
|
75
80
|
const handleResizeRightEnd = useCallback((result) => {
|
|
@@ -89,20 +94,20 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
89
94
|
disabled: !canDragDates,
|
|
90
95
|
onDragEnd: handleResizeRightEnd,
|
|
91
96
|
onDragStateChange: useCallback((isDragging, deltaX) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: null);
|
|
95
|
-
}, [setActiveDrag, task.id]),
|
|
97
|
+
updateActiveDrag(isDragging, deltaX, "resize-right");
|
|
98
|
+
}, [updateActiveDrag]),
|
|
96
99
|
});
|
|
97
100
|
// ----- Drag: Progress -----
|
|
98
101
|
const handleProgressDragEnd = useCallback((result) => {
|
|
99
102
|
if (!onTaskUpdate)
|
|
100
103
|
return;
|
|
104
|
+
if (result.newProgress === task.progress)
|
|
105
|
+
return;
|
|
101
106
|
onTaskUpdate(task.id, {
|
|
102
107
|
progress: result.newProgress,
|
|
103
108
|
completed: result.newProgress === 100,
|
|
104
109
|
});
|
|
105
|
-
}, [onTaskUpdate, task.id]);
|
|
110
|
+
}, [onTaskUpdate, task.id, task.progress]);
|
|
106
111
|
const progressDrag = useGanttDrag({
|
|
107
112
|
dayWidth,
|
|
108
113
|
mode: "progress",
|
|
@@ -110,6 +115,12 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
110
115
|
originalProgress: task.progress,
|
|
111
116
|
disabled: !canDragProgress,
|
|
112
117
|
onDragEnd: handleProgressDragEnd,
|
|
118
|
+
onDragStateChange: useCallback((isDragging) => {
|
|
119
|
+
if (isDragging) {
|
|
120
|
+
setTooltipPos(null);
|
|
121
|
+
setHoveredTaskId(null);
|
|
122
|
+
}
|
|
123
|
+
}, [setHoveredTaskId]),
|
|
113
124
|
});
|
|
114
125
|
const anyDragging = moveDrag.isDragging ||
|
|
115
126
|
resizeLeftDrag.isDragging ||
|
|
@@ -143,6 +154,10 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
143
154
|
displayProgress = progressDrag.dragProgress;
|
|
144
155
|
}
|
|
145
156
|
const isNarrow = visualWidth <= 80;
|
|
157
|
+
const showInsideLabel = showTaskLabels === "inside" ||
|
|
158
|
+
(showTaskLabels === "auto" && !isNarrow);
|
|
159
|
+
const showOutsideLabel = showTaskLabels === "outside" ||
|
|
160
|
+
(showTaskLabels === "auto" && isNarrow);
|
|
146
161
|
// ----- Hover highlighting (dependency connections) -----
|
|
147
162
|
const isHighlighted = useMemo(() => {
|
|
148
163
|
var _a, _b, _c;
|
|
@@ -163,6 +178,12 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
163
178
|
return;
|
|
164
179
|
onTaskClick === null || onTaskClick === void 0 ? void 0 : onTaskClick(task, e);
|
|
165
180
|
};
|
|
181
|
+
const handleKeyDown = (e) => {
|
|
182
|
+
if ((e.key === "Enter" || e.key === " ") && onTaskClick) {
|
|
183
|
+
e.preventDefault();
|
|
184
|
+
onTaskClick(task, e);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
166
187
|
// ----- Tooltip -----
|
|
167
188
|
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: {
|
|
168
189
|
width: `${task.progress}%`,
|
|
@@ -183,12 +204,12 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
183
204
|
}, onMouseLeave: () => {
|
|
184
205
|
setTooltipPos(null);
|
|
185
206
|
setHoveredTaskId(null);
|
|
186
|
-
}, onClick: onTaskClick ? handleClick : undefined, onPointerDown: canDragDates ? moveDrag.handlePointerDown : undefined, role: "button", "aria-label": task.title, children: [jsxs(motion.div, { className: cn(theme.taskBarStyle, "group", task.completed && "opacity-60", canDragDates && !anyDragging && "cursor-grab", anyDragging && "cursor-grabbing", !canDragDates && "cursor-default", isHighlighted && "ring-2 ring-white/60", isFaded && "opacity-40", isFocused && "ring-2 ring-[var(--color-primary)] ring-offset-
|
|
207
|
+
}, onClick: onTaskClick ? handleClick : undefined, onPointerDown: canDragDates ? moveDrag.handlePointerDown : undefined, onFocus: () => setFocusedTaskId(task.id), onBlur: () => setFocusedTaskId(null), onKeyDown: handleKeyDown, role: onTaskClick ? "button" : "img", tabIndex: onTaskClick ? 0 : undefined, "aria-label": task.title, children: [jsxs(motion.div, { className: cn(theme.taskBarStyle, "group", task.completed && "opacity-60", canDragDates && !anyDragging && "cursor-grab", anyDragging && "cursor-grabbing", !canDragDates && "cursor-default", isHighlighted && "ring-2 ring-white/60", isFaded && "opacity-40", isFocused && "ring-2 ring-[var(--color-primary)] ring-offset-2 ring-offset-[var(--color-background)]"), style: {
|
|
187
208
|
width: visualWidth,
|
|
188
209
|
height: barHeight,
|
|
189
210
|
backgroundColor: barColor,
|
|
190
211
|
border: "1px solid rgba(0,0,0,0.12)",
|
|
191
|
-
borderRadius:
|
|
212
|
+
borderRadius: 999,
|
|
192
213
|
transformOrigin: "left center",
|
|
193
214
|
transition: anyDragging ? "none" : undefined,
|
|
194
215
|
}, variants: taskBarVariants, initial: "hidden", animate: "visible", whileHover: anyDragging ? undefined : { filter: "brightness(0.96)" }, children: [jsx("div", { className: "absolute inset-0 rounded-full", style: {
|
|
@@ -197,7 +218,7 @@ const GanttTaskBar = React__default.memo(({ task, group, top }) => {
|
|
|
197
218
|
} }), isBehindSchedule && (jsx("div", { className: "absolute top-0 bottom-0 w-px", style: {
|
|
198
219
|
left: `${expectedProgress}%`,
|
|
199
220
|
borderLeft: "2px dashed rgba(255,255,255,0.5)",
|
|
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-
|
|
221
|
+
} })), 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 z-20 w-3 cursor-ew-resize rounded-l-full opacity-0 transition-opacity hover:bg-black/15 group-hover:opacity-100", onPointerDown: resizeLeftDrag.handlePointerDown, "aria-hidden": "true" })), canDragDates && (jsx("div", { className: "absolute right-0 top-0 bottom-0 z-20 w-3 cursor-ew-resize rounded-r-full opacity-0 transition-opacity hover:bg-black/15 group-hover:opacity-100", onPointerDown: resizeRightDrag.handlePointerDown, "aria-hidden": "true" })), canDragProgress && !isNarrow && (jsx("div", { className: "absolute top-0 bottom-0 z-20 flex w-4 cursor-ew-resize items-center justify-center opacity-0 transition-opacity group-hover:opacity-100", style: { left: `calc(${displayProgress}% - 6px)` }, onPointerDown: progressDrag.handlePointerDown, "aria-hidden": "true", children: jsx("div", { className: "h-3.5 w-0.5 rounded-full bg-white/90 shadow" }) })), showInsideLabel && (jsxs("div", { className: "relative z-10 flex min-w-0 items-center gap-1.5 px-1.5 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 })] }))] }), showOutsideLabel && (jsx("span", { className: cn(theme.taskLabelOutsideStyle), style: {
|
|
201
222
|
left: visualWidth + 6,
|
|
202
223
|
top: barHeight / 2,
|
|
203
224
|
transform: "translateY(-50%)",
|
|
@@ -9,6 +9,7 @@ const GanttTimelineGrid = ({ days, height, }) => {
|
|
|
9
9
|
const totalWidth = days.length * dayWidth;
|
|
10
10
|
const showVertical = gridLines === "both" || gridLines === "vertical";
|
|
11
11
|
const showHorizontal = gridLines === "both" || gridLines === "horizontal";
|
|
12
|
+
const shouldDrawVerticalLine = (day) => dayWidth >= 12 || day.getDate() === 1 || day.getDay() === 1;
|
|
12
13
|
// Build a dateKey -> holiday lookup
|
|
13
14
|
const holidayMap = useMemo(() => {
|
|
14
15
|
const map = new Map();
|
|
@@ -23,7 +24,7 @@ const GanttTimelineGrid = ({ days, height, }) => {
|
|
|
23
24
|
var _a, _b;
|
|
24
25
|
const key = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`;
|
|
25
26
|
const holiday = holidayMap.get(key);
|
|
26
|
-
return (jsx("div", { className: cn("absolute top-0 bottom-0", showVertical && theme.gridLineStyle, isWeekend(day) && theme.weekendStyle), style: { left: i * dayWidth, width: dayWidth }, children: holiday && (jsxs(Fragment, { children: [jsx("div", { className: "absolute inset-0", style: {
|
|
27
|
+
return (jsx("div", { className: cn("absolute top-0 bottom-0", showVertical && shouldDrawVerticalLine(day) && theme.gridLineStyle, isWeekend(day) && theme.weekendStyle), style: { left: i * dayWidth, width: dayWidth }, children: holiday && (jsxs(Fragment, { children: [jsx("div", { className: "absolute inset-0", style: {
|
|
27
28
|
backgroundColor: (_a = holiday.color) !== null && _a !== void 0 ? _a : "var(--color-warning-100, rgba(245, 158, 11, 0.08))",
|
|
28
29
|
} }), jsx("span", { className: "absolute bottom-1 left-1/2 -translate-x-1/2 text-[8px] whitespace-nowrap opacity-60", style: { color: (_b = holiday.color) !== null && _b !== void 0 ? _b : "var(--color-warning, #F59E0B)" }, children: holiday.label })] })) }, i));
|
|
29
30
|
}), showHorizontal &&
|