@flikk/ui 1.0.0-beta.25 → 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/CodeBlock/CodeBlock.theme.js +2 -2
- package/dist/components/ai/PromptInput/PromptInput.js +4 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +4 -0
- package/dist/components/ai/StreamingResponse/MarkdownRenderer.js +1 -1
- package/dist/components/ai/TokenCounter/TokenCounter.theme.js +1 -1
- package/dist/components/charts/BarChart/BarChart.js +2 -2
- package/dist/components/charts/StackedBarChart/StackedBarChart.js +1 -1
- package/dist/components/charts/shared/BarRenderer/BarRenderer.js +2 -2
- package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +1 -1
- package/dist/components/core/Button/Button.js +1 -1
- package/dist/components/core/Button/Button.theme.js +5 -5
- package/dist/components/core/Calendar/Calendar.theme.js +1 -1
- package/dist/components/core/Calendar/CalendarMini/CalendarMini.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/Drawer/Drawer.theme.js +2 -2
- package/dist/components/core/Dropdown/Dropdown.theme.js +4 -5
- package/dist/components/core/Dropdown/DropdownItem.js +1 -1
- package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
- package/dist/components/core/Empty/Empty.js +1 -1
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.d.ts +14 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.js +28 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.d.ts +6 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.js +8 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.types.d.ts +32 -0
- package/dist/components/core/ImagePlaceholder/index.d.ts +2 -0
- 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/Message/Message.theme.js +1 -1
- package/dist/components/core/Modal/Modal.theme.js +1 -1
- package/dist/components/core/NavItem/NavItem.d.ts +1 -1
- package/dist/components/core/NavItem/NavItem.js +187 -68
- package/dist/components/core/NavItem/NavItem.theme.js +31 -12
- package/dist/components/core/NavItem/NavItem.types.d.ts +17 -39
- package/dist/components/core/Pagination/Pagination.theme.js +3 -3
- package/dist/components/core/Popover/Popover.theme.js +1 -1
- package/dist/components/core/Popover/Popover.types.d.ts +6 -2
- package/dist/components/core/Popover/PopoverBody.js +10 -3
- package/dist/components/core/Progress/Progress.theme.js +1 -1
- package/dist/components/core/ScrollArea/ScrollArea.js +2 -2
- package/dist/components/core/ScrollArea/ScrollArea.theme.js +1 -1
- package/dist/components/core/Segmented/Segmented.js +8 -6
- package/dist/components/core/Segmented/Segmented.theme.js +3 -3
- package/dist/components/core/Sidebar/Sidebar.d.ts +3 -1
- package/dist/components/core/Sidebar/Sidebar.js +53 -16
- package/dist/components/core/Sidebar/Sidebar.theme.js +5 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +125 -8
- 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 +10 -6
- package/dist/components/core/Sidebar/SidebarContext.d.ts +17 -16
- package/dist/components/core/Sidebar/SidebarContext.js +43 -20
- package/dist/components/core/Sidebar/SidebarFooter.js +4 -6
- package/dist/components/core/Sidebar/SidebarHeader.js +12 -9
- package/dist/components/core/Sidebar/SidebarLogo.js +39 -8
- package/dist/components/core/Sidebar/SidebarNav.js +4 -3
- package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -7
- 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 +6 -1
- package/dist/components/core/Sidebar/SidebarUserProfile.js +33 -17
- package/dist/components/core/Sidebar/index.d.ts +4 -3
- package/dist/components/core/Skeleton/Skeleton.theme.js +1 -1
- package/dist/components/core/Spinner/Spinner.theme.js +1 -1
- package/dist/components/core/Tabs/Tabs.js +6 -1
- package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
- package/dist/components/core/Tabs/TabsBody.js +5 -3
- package/dist/components/core/Tag/Tag.js +29 -5
- package/dist/components/core/Tag/Tag.theme.js +14 -9
- 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 +2 -0
- package/dist/components/core/index.js +6 -2
- 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/GanttTableGroupRow.js +1 -1
- 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/KPI/KPI.js +13 -3
- package/dist/components/data-display/KPI/KPI.theme.js +3 -3
- package/dist/components/data-display/Metric/Metric.theme.js +3 -3
- package/dist/components/data-display/Table/Table.js +4 -3
- package/dist/components/data-display/Table/Table.theme.js +14 -6
- package/dist/components/data-display/Table/Table.types.d.ts +15 -0
- package/dist/components/data-display/Table/TableColumnManager.js +1 -1
- package/dist/components/data-display/Table/index.d.ts +2 -2
- package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
- package/dist/components/effects/Aurora/Aurora.js +310 -105
- package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
- package/dist/components/effects/InteractiveCharacters/InteractiveCharacters.js +3 -3
- package/dist/components/effects/InteractiveCharacters/characters/BlobCharacter.js +13 -4
- package/dist/components/effects/InteractiveCharacters/characters/SmallCharacter.js +13 -4
- package/dist/components/effects/InteractiveCharacters/characters/TallCharacter.js +14 -4
- package/dist/components/effects/MorphingText/MorphingText.js +40 -7
- package/dist/components/effects/StripePattern/StripePattern.d.ts +18 -0
- package/dist/components/effects/StripePattern/StripePattern.js +54 -0
- package/dist/components/effects/StripePattern/StripePattern.types.d.ts +53 -0
- package/dist/components/effects/StripePattern/index.d.ts +2 -0
- package/dist/components/effects/index.d.ts +2 -0
- package/dist/components/effects/index.js +1 -0
- package/dist/components/forms/Checkbox/Checkbox.theme.js +1 -1
- 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 +22 -31
- 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 +7 -14
- 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 +88 -49
- 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/FileUpload/FileUploadProgress.js +27 -20
- package/dist/components/forms/FileUpload/FileUploadProgress.theme.js +2 -2
- package/dist/components/forms/Input/Input.theme.js +16 -15
- 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/Radio/Radio.theme.js +1 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.d.ts +18 -0
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +1231 -236
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.d.ts +53 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +99 -25
- package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +42 -3
- package/dist/components/forms/RichTextEditor/index.d.ts +2 -2
- package/dist/components/forms/Select/Select.js +2 -2
- package/dist/components/forms/Select/Select.theme.js +3 -3
- package/dist/components/forms/Slider/Slider.theme.js +1 -1
- package/dist/components/forms/Switch/Switch.theme.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.theme.js +2 -2
- package/dist/components/forms/TimePicker/TimePickerContent.js +4 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +24 -19
- package/dist/components/forms/index.js +4 -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 +9 -4
- package/dist/components/layout/FormLayout/FormLayout.types.d.ts +3 -3
- package/dist/components/layout/FormLayout/FormLayoutSection.js +6 -1
- package/dist/components/layout/PageLayout/PageLayout.d.ts +2 -2
- package/dist/components/layout/PageLayout/PageLayout.js +23 -5
- package/dist/components/layout/PageLayout/PageLayout.theme.d.ts +2 -1
- package/dist/components/layout/PageLayout/PageLayout.theme.js +35 -6
- package/dist/components/layout/PageLayout/PageLayout.types.d.ts +45 -0
- package/dist/components/layout/PageLayout/PageLayoutContent.js +6 -4
- package/dist/components/layout/PageLayout/PageLayoutHeader.js +3 -3
- package/dist/components/layout/PageLayout/PageLayoutSidebar.js +5 -3
- package/dist/components/layout/PageLayout/index.d.ts +1 -1
- 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 +8 -2
- 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 +174 -100
- package/dist/components/core/Sidebar/SidebarSubmenu.d.ts +0 -7
- package/dist/components/core/Sidebar/SidebarSubmenu.js +0 -12
|
@@ -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 };
|
|
@@ -14,7 +14,7 @@ const GanttTableGroupRow = React__default.memo(({ row, }) => {
|
|
|
14
14
|
e.preventDefault();
|
|
15
15
|
toggleGroup(row.group.id);
|
|
16
16
|
}
|
|
17
|
-
}, role: "button", tabIndex: 0, "aria-expanded": !isCollapsed, "aria-label": `${row.group.title} — ${row.completedCount} of ${row.taskCount} completed`, children: [jsxs("div", { className: "flex items-center gap-2 px-3 flex-1 min-w-0", children: [jsx(motion.div, { variants: chevronVariants, animate: isCollapsed ? "collapsed" : "open", children: jsx(ChevronRightIcon, { className: "size-4 text-[var(--color-text-muted)]" }) }), jsx("div", { className: "size-2.5 rounded-full shrink-0", style: { backgroundColor: row.group.color } }), jsx("span", { className: "font-medium text-[var(--color-text-primary)] truncate", children: row.group.title }), jsxs("span", { className: "text-sm text-[var(--color-text-muted)] shrink-0", children: [row.completedCount, "/", row.taskCount] })] }), onTaskAdd && !readonly && (jsx("button", { className: "p-1 mr-2 rounded hover:bg-[var(--color-
|
|
17
|
+
}, role: "button", tabIndex: 0, "aria-expanded": !isCollapsed, "aria-label": `${row.group.title} — ${row.completedCount} of ${row.taskCount} completed`, children: [jsxs("div", { className: "flex items-center gap-2 px-3 flex-1 min-w-0", children: [jsx(motion.div, { variants: chevronVariants, animate: isCollapsed ? "collapsed" : "open", children: jsx(ChevronRightIcon, { className: "size-4 text-[var(--color-text-muted)]" }) }), jsx("div", { className: "size-2.5 rounded-full shrink-0", style: { backgroundColor: row.group.color } }), jsx("span", { className: "font-medium text-[var(--color-text-primary)] truncate", children: row.group.title }), jsxs("span", { className: "text-sm text-[var(--color-text-muted)] shrink-0", children: [row.completedCount, "/", row.taskCount] })] }), onTaskAdd && !readonly && (jsx("button", { className: "p-1 mr-2 rounded hover:bg-[var(--color-surface-hover)] text-[var(--color-text-muted)] opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity", "aria-label": `Add task to ${row.group.title}`, onClick: (e) => {
|
|
18
18
|
e.stopPropagation();
|
|
19
19
|
onTaskAdd(row.group.id);
|
|
20
20
|
}, children: jsx(PlusIcon, { className: "size-4" }) }))] }));
|
|
@@ -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 &&
|
|
@@ -2,38 +2,62 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { cn } from '../../../utils/cn.js';
|
|
3
3
|
import { useGanttContext } from './GanttChart.js';
|
|
4
4
|
import { formatDayLabel } from './GanttChart.utils.js';
|
|
5
|
-
import { isSameDay, isWeekend } from '../../../utils/dateUtils.js';
|
|
5
|
+
import { isSameDay, isWeekend, getWeekNumber } from '../../../utils/dateUtils.js';
|
|
6
6
|
|
|
7
7
|
function getDayLabel(day, zoom) {
|
|
8
8
|
switch (zoom) {
|
|
9
9
|
case "day":
|
|
10
10
|
return formatDayLabel(day, zoom);
|
|
11
11
|
case "week":
|
|
12
|
-
|
|
13
|
-
return day.getDay() === 1 ? `${day.getDate()}` : "";
|
|
12
|
+
return day.getDay() === 1 ? `W${getWeekNumber(day)}` : "";
|
|
14
13
|
case "quarter":
|
|
15
|
-
// Show day number on 1st, 8th, 15th, 22nd
|
|
16
14
|
return [1, 8, 15, 22].includes(day.getDate())
|
|
17
15
|
? `${day.getDate()}`
|
|
18
16
|
: "";
|
|
19
17
|
case "month":
|
|
20
|
-
// Show day number on 1st and 15th
|
|
21
|
-
return day.getDate() === 1 || day.getDate() === 15
|
|
22
|
-
? `${day.getDate()}`
|
|
23
|
-
: "";
|
|
24
|
-
case "year":
|
|
25
|
-
// Show only on 1st of each month
|
|
26
18
|
return day.getDate() === 1
|
|
27
19
|
? day.toLocaleDateString("en-US", { month: "short" })
|
|
28
20
|
: "";
|
|
21
|
+
case "year":
|
|
22
|
+
return day.getDate() === 1 && day.getMonth() === 0
|
|
23
|
+
? `${day.getFullYear()}`
|
|
24
|
+
: "";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getHeaderSpans(days, monthSpans, zoom) {
|
|
28
|
+
if (zoom !== "month" && zoom !== "year") {
|
|
29
|
+
return monthSpans;
|
|
30
|
+
}
|
|
31
|
+
const spans = [];
|
|
32
|
+
let currentLabel = "";
|
|
33
|
+
let startIndex = 0;
|
|
34
|
+
days.forEach((day, index) => {
|
|
35
|
+
const label = zoom === "year"
|
|
36
|
+
? `${day.getFullYear()}`
|
|
37
|
+
: day.toLocaleDateString("en-US", { year: "numeric" });
|
|
38
|
+
if (index === 0) {
|
|
39
|
+
currentLabel = label;
|
|
40
|
+
startIndex = 0;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (label !== currentLabel) {
|
|
44
|
+
spans.push({ label: currentLabel, startIndex, span: index - startIndex });
|
|
45
|
+
currentLabel = label;
|
|
46
|
+
startIndex = index;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (days.length > 0) {
|
|
50
|
+
spans.push({ label: currentLabel, startIndex, span: days.length - startIndex });
|
|
29
51
|
}
|
|
52
|
+
return spans;
|
|
30
53
|
}
|
|
31
54
|
const GanttTimelineHeader = ({ days, monthSpans, }) => {
|
|
32
55
|
const { theme, dayWidth, zoom } = useGanttContext();
|
|
33
56
|
const totalWidth = days.length * dayWidth;
|
|
57
|
+
const headerSpans = getHeaderSpans(days, monthSpans, zoom);
|
|
34
58
|
const today = new Date();
|
|
35
59
|
today.setHours(0, 0, 0, 0);
|
|
36
|
-
return (jsxs("div", { className: cn(theme.timelineHeaderStyle), style: { width: totalWidth }, children: [jsx("div", { className: "flex", style: { height: 28 }, children:
|
|
60
|
+
return (jsxs("div", { className: cn(theme.timelineHeaderStyle), style: { width: totalWidth }, children: [jsx("div", { className: "flex", style: { height: 28 }, children: headerSpans.map((span) => (jsx("div", { className: cn(theme.timelineMonthStyle), style: { width: span.span * dayWidth }, children: span.label }, `${span.label}-${span.startIndex}`))) }), jsx("div", { className: "flex", style: { height: 28 }, children: days.map((day, i) => {
|
|
37
61
|
const isToday = isSameDay(day, today);
|
|
38
62
|
const weekend = isWeekend(day);
|
|
39
63
|
return (jsx("div", { className: cn(theme.timelineDayStyle, weekend && "bg-[var(--color-background-secondary)]/50", isToday && "font-semibold text-[var(--color-primary)]"), style: { width: dayWidth }, children: getDayLabel(day, zoom) }, i));
|
|
@@ -15,7 +15,7 @@ const ZOOM_OPTIONS = [
|
|
|
15
15
|
{ value: "year", label: "Year" },
|
|
16
16
|
];
|
|
17
17
|
const GanttToolbar = () => {
|
|
18
|
-
const { theme, zoom, setZoom, hideCompleted, setHideCompleted, onGroupAdd, todayOffset, dayWidth, readonly, } = useGanttContext();
|
|
18
|
+
const { theme, zoom, setZoom, hideCompleted, setHideCompleted, onGroupAdd, todayOffset, dayWidth, readonly, toolbar, } = useGanttContext();
|
|
19
19
|
const handleScrollToToday = () => {
|
|
20
20
|
const container = document.querySelector("[data-gantt-chart-scroll]");
|
|
21
21
|
if (!container || todayOffset === null)
|
|
@@ -23,7 +23,7 @@ const GanttToolbar = () => {
|
|
|
23
23
|
const targetScroll = todayOffset * dayWidth - container.clientWidth / 2;
|
|
24
24
|
container.scrollTo({ left: targetScroll, behavior: "smooth" });
|
|
25
25
|
};
|
|
26
|
-
return (jsxs("div", { className: cn(theme.toolbarStyle), children: [jsxs("div", { className: "flex items-center gap-
|
|
26
|
+
return (jsxs("div", { className: cn(theme.toolbarStyle), children: [jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [toolbar.today && todayOffset !== null && (jsxs(Button, { variant: "outline", color: "neutral", size: "sm", onClick: handleScrollToToday, children: [jsx(CalendarIcon, { className: "size-4" }), "Today"] })), toolbar.zoom && (jsx(Segmented, { value: zoom, onChange: (val) => setZoom(val), size: "sm", "aria-label": "Gantt zoom", className: "max-w-full overflow-x-auto", children: ZOOM_OPTIONS.map((opt) => (jsx(Segmented.Item, { value: opt.value, children: opt.label }, opt.value))) })), toolbar.hideCompleted && (jsx(Checkbox, { id: "gantt-hide-completed", name: "hideCompleted", value: "hideCompleted", checked: hideCompleted, onChange: (checked) => setHideCompleted(checked), label: "Hide completed", size: "sm" }))] }), jsx("div", { className: "flex items-center gap-2", children: toolbar.addGroup && onGroupAdd && !readonly && (jsxs(Button, { variant: "soft", size: "sm", onClick: onGroupAdd, children: [jsx(PlusIcon, { className: "size-3.5" }), "Add group"] })) })] }));
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
export { GanttToolbar };
|
|
@@ -34,7 +34,7 @@ function useGanttDrag({ dayWidth, mode, barWidthPx, originalProgress, disabled =
|
|
|
34
34
|
cancelledRef.current = false;
|
|
35
35
|
didDragRef.current = false;
|
|
36
36
|
const onPointerMove = (ev) => {
|
|
37
|
-
var _a;
|
|
37
|
+
var _a, _b;
|
|
38
38
|
if (cancelledRef.current)
|
|
39
39
|
return;
|
|
40
40
|
const rawDelta = ev.clientX - startXRef.current;
|
|
@@ -52,12 +52,13 @@ function useGanttDrag({ dayWidth, mode, barWidthPx, originalProgress, disabled =
|
|
|
52
52
|
const newProgress = Math.round(Math.max(0, Math.min(100, originalProgress + progressDelta)));
|
|
53
53
|
setDragProgress(newProgress);
|
|
54
54
|
setDragDeltaX(rawDelta);
|
|
55
|
+
(_a = onDragStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onDragStateChangeRef, true, rawDelta);
|
|
55
56
|
}
|
|
56
57
|
else {
|
|
57
58
|
// Move/resize: snap to day grid
|
|
58
59
|
const snappedDelta = Math.round(rawDelta / dayWidth) * dayWidth;
|
|
59
60
|
setDragDeltaX(snappedDelta);
|
|
60
|
-
(
|
|
61
|
+
(_b = onDragStateChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onDragStateChangeRef, true, snappedDelta);
|
|
61
62
|
}
|
|
62
63
|
};
|
|
63
64
|
const onPointerUp = () => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { GanttChart } from "./GanttChart";
|
|
2
|
-
export type { GanttChartProps, GanttTask, GanttGroup, GanttColumn, GanttZoom, GanttHoliday, GanttChartThemeOverrides, } from "./GanttChart.types";
|
|
2
|
+
export type { GanttChartProps, GanttTask, GanttGroup, GanttColumn, GanttZoom, GanttTaskLabelMode, GanttToolbarOptions, GanttHoliday, GanttChartThemeOverrides, } from "./GanttChart.types";
|
|
3
3
|
export { ganttChartTheme } from "./GanttChart.theme";
|