@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.
Files changed (131) hide show
  1. package/dist/components/ai/PromptInput/PromptInput.js +3 -0
  2. package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -0
  3. package/dist/components/core/Button/Button.theme.js +1 -1
  4. package/dist/components/core/Calendar/Calendar.theme.js +1 -1
  5. package/dist/components/core/CommandPalette/CommandItem.js +4 -5
  6. package/dist/components/core/CommandPalette/CommandPalette.js +1 -1
  7. package/dist/components/core/CommandPalette/CommandPalette.theme.js +11 -13
  8. package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +0 -2
  9. package/dist/components/core/ContextMenu/ContextMenu.theme.js +13 -14
  10. package/dist/components/core/DotSeparator/DotSeparator.d.ts +3 -0
  11. package/dist/components/core/DotSeparator/DotSeparator.js +19 -0
  12. package/dist/components/core/DotSeparator/DotSeparator.theme.d.ts +2 -0
  13. package/dist/components/core/DotSeparator/DotSeparator.theme.js +10 -0
  14. package/dist/components/core/DotSeparator/DotSeparator.types.d.ts +25 -0
  15. package/dist/components/core/DotSeparator/index.d.ts +3 -0
  16. package/dist/components/core/Dropdown/Dropdown.theme.js +2 -3
  17. package/dist/components/core/Dropdown/DropdownItem.js +1 -1
  18. package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
  19. package/dist/components/core/MenuItem/MenuItem.js +5 -2
  20. package/dist/components/core/MenuItem/MenuItem.theme.d.ts +6 -0
  21. package/dist/components/core/MenuItem/MenuItem.theme.js +19 -16
  22. package/dist/components/core/MenuItem/MenuItem.types.d.ts +2 -0
  23. package/dist/components/core/NavItem/NavItem.js +20 -4
  24. package/dist/components/core/NavItem/NavItem.theme.js +13 -2
  25. package/dist/components/core/Popover/Popover.types.d.ts +6 -2
  26. package/dist/components/core/Popover/PopoverBody.js +10 -3
  27. package/dist/components/core/Segmented/Segmented.theme.js +1 -1
  28. package/dist/components/core/Sidebar/Sidebar.d.ts +2 -0
  29. package/dist/components/core/Sidebar/Sidebar.js +15 -7
  30. package/dist/components/core/Sidebar/Sidebar.types.d.ts +92 -4
  31. package/dist/components/core/Sidebar/SidebarCard.d.ts +25 -0
  32. package/dist/components/core/Sidebar/SidebarCard.js +68 -0
  33. package/dist/components/core/Sidebar/SidebarCard.theme.d.ts +8 -0
  34. package/dist/components/core/Sidebar/SidebarCard.theme.js +18 -0
  35. package/dist/components/core/Sidebar/SidebarContent.js +5 -1
  36. package/dist/components/core/Sidebar/SidebarContext.js +1 -1
  37. package/dist/components/core/Sidebar/SidebarHeader.js +10 -2
  38. package/dist/components/core/Sidebar/SidebarLogo.js +38 -3
  39. package/dist/components/core/Sidebar/SidebarSearch.d.ts +22 -0
  40. package/dist/components/core/Sidebar/SidebarSearch.js +135 -0
  41. package/dist/components/core/Sidebar/SidebarToggle.js +1 -1
  42. package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
  43. package/dist/components/core/Sidebar/index.d.ts +3 -1
  44. package/dist/components/core/Tag/Tag.js +29 -5
  45. package/dist/components/core/Tag/Tag.theme.js +13 -8
  46. package/dist/components/core/Tag/Tag.types.d.ts +7 -0
  47. package/dist/components/core/Tooltip/Tooltip.js +1 -1
  48. package/dist/components/core/Tooltip/Tooltip.theme.js +1 -2
  49. package/dist/components/core/index.d.ts +1 -0
  50. package/dist/components/core/index.js +4 -0
  51. package/dist/components/data-display/GanttChart/GanttChart.d.ts +1 -1
  52. package/dist/components/data-display/GanttChart/GanttChart.js +31 -4
  53. package/dist/components/data-display/GanttChart/GanttChart.theme.js +19 -17
  54. package/dist/components/data-display/GanttChart/GanttChart.types.d.ts +19 -0
  55. package/dist/components/data-display/GanttChart/GanttChart.utils.js +1 -1
  56. package/dist/components/data-display/GanttChart/GanttChartPanel.d.ts +1 -1
  57. package/dist/components/data-display/GanttChart/GanttChartPanel.js +3 -3
  58. package/dist/components/data-display/GanttChart/GanttDependencyArrows.js +7 -4
  59. package/dist/components/data-display/GanttChart/GanttMilestone.js +5 -5
  60. package/dist/components/data-display/GanttChart/GanttTablePanel.js +1 -1
  61. package/dist/components/data-display/GanttChart/GanttTableTaskRow.js +2 -2
  62. package/dist/components/data-display/GanttChart/GanttTaskBar.js +37 -16
  63. package/dist/components/data-display/GanttChart/GanttTimelineGrid.js +2 -1
  64. package/dist/components/data-display/GanttChart/GanttTimelineHeader.js +35 -11
  65. package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
  66. package/dist/components/data-display/GanttChart/hooks/useGanttDrag.js +3 -2
  67. package/dist/components/data-display/GanttChart/index.d.ts +1 -1
  68. package/dist/components/data-display/Table/Table.theme.js +3 -5
  69. package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
  70. package/dist/components/effects/Aurora/Aurora.js +297 -131
  71. package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
  72. package/dist/components/forms/Combobox/Combobox.js +1 -1
  73. package/dist/components/forms/Combobox/Combobox.theme.js +2 -4
  74. package/dist/components/forms/CronInput/CronInput.js +1 -1
  75. package/dist/components/forms/CronInput/CronInput.theme.js +14 -23
  76. package/dist/components/forms/DatePicker/DatePicker.theme.js +11 -18
  77. package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
  78. package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +6 -13
  79. package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
  80. package/dist/components/forms/FileUpload/FileUpload.animations.d.ts +29 -0
  81. package/dist/components/forms/FileUpload/FileUpload.animations.js +19 -0
  82. package/dist/components/forms/FileUpload/FileUpload.js +87 -48
  83. package/dist/components/forms/FileUpload/FileUpload.theme.d.ts +2 -10
  84. package/dist/components/forms/FileUpload/FileUpload.theme.js +16 -30
  85. package/dist/components/forms/FileUpload/FileUpload.types.d.ts +26 -10
  86. package/dist/components/forms/Input/Input.theme.js +13 -10
  87. package/dist/components/forms/InputCounter/InputCounter.theme.js +1 -1
  88. package/dist/components/forms/Mention/Mention.js +152 -57
  89. package/dist/components/forms/Mention/Mention.theme.js +16 -23
  90. package/dist/components/forms/Mention/Mention.types.d.ts +12 -12
  91. package/dist/components/forms/Mention/Mention.utils.js +32 -4
  92. package/dist/components/forms/RichTextEditor/RichTextEditor.js +3 -3
  93. package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +5 -5
  94. package/dist/components/forms/Select/Select.js +2 -2
  95. package/dist/components/forms/Select/Select.theme.js +3 -3
  96. package/dist/components/forms/TimePicker/TimePickerContent.js +3 -0
  97. package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
  98. package/dist/components/forms/forms.theme.js +21 -16
  99. package/dist/components/forms/index.js +3 -0
  100. package/dist/components/layout/AppShell/AppShell.d.ts +10 -0
  101. package/dist/components/layout/AppShell/AppShell.js +84 -0
  102. package/dist/components/layout/AppShell/AppShell.theme.d.ts +3 -0
  103. package/dist/components/layout/AppShell/AppShell.theme.js +42 -0
  104. package/dist/components/layout/AppShell/AppShell.types.d.ts +127 -0
  105. package/dist/components/layout/AppShell/AppShellContext.d.ts +14 -0
  106. package/dist/components/layout/AppShell/AppShellContext.js +19 -0
  107. package/dist/components/layout/AppShell/AppShellFooter.d.ts +3 -0
  108. package/dist/components/layout/AppShell/AppShellFooter.js +12 -0
  109. package/dist/components/layout/AppShell/AppShellHeader.d.ts +3 -0
  110. package/dist/components/layout/AppShell/AppShellHeader.js +12 -0
  111. package/dist/components/layout/AppShell/AppShellMain.d.ts +3 -0
  112. package/dist/components/layout/AppShell/AppShellMain.js +19 -0
  113. package/dist/components/layout/AppShell/AppShellRightPanel.d.ts +3 -0
  114. package/dist/components/layout/AppShell/AppShellRightPanel.js +16 -0
  115. package/dist/components/layout/AppShell/AppShellSidebar.d.ts +3 -0
  116. package/dist/components/layout/AppShell/AppShellSidebar.js +16 -0
  117. package/dist/components/layout/AppShell/AppShellTopbar.d.ts +3 -0
  118. package/dist/components/layout/AppShell/AppShellTopbar.js +12 -0
  119. package/dist/components/layout/AppShell/index.d.ts +3 -0
  120. package/dist/components/layout/FormLayout/FormLayout.js +3 -0
  121. package/dist/components/layout/index.d.ts +1 -0
  122. package/dist/components/layout/index.js +2 -0
  123. package/dist/hooks/useSelectPortal.d.ts +10 -2
  124. package/dist/hooks/useSelectPortal.js +45 -24
  125. package/dist/index.js +6 -0
  126. package/dist/registry.json +10021 -0
  127. package/dist/styles.css +1 -1
  128. package/dist/utils/dateUtils.js +11 -1
  129. package/package.json +4 -2
  130. package/src/global.scss +29 -8
  131. 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-white/90 backdrop-blur-md outline outline-[var(--color-border)] shadow-lg " +
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 };
@@ -15,6 +15,7 @@ export * from "./CommandPalette";
15
15
  export * from "./Separator";
16
16
  export * from "./Drawer";
17
17
  export * from "./Dropdown";
18
+ export * from "./DotSeparator";
18
19
  export * from "./Kbd";
19
20
  export * from "./MenuItem";
20
21
  export * from "./Link";
@@ -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, readonlyProp, readonlyDatesProp,
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-3 px-3 py-2 border-b border-[var(--color-border)] bg-[var(--color-background-secondary)]",
4
- tablePanelStyle: "hidden sm:flex flex-col shrink-0 overflow-hidden",
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-medium text-[var(--color-text-secondary)] truncate",
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-xs font-medium text-[var(--color-text-secondary)] px-2 border-b border-[var(--color-border)] flex items-center",
13
- timelineDayStyle: "text-[10px] text-[var(--color-text-muted)] flex items-center justify-center",
14
- gridLineStyle: "border-r border-[var(--color-border)]/30",
15
- weekendStyle: "bg-[var(--color-background-secondary)]/50",
16
- todayMarkerStyle: "bg-[var(--color-primary)] w-px",
17
- todayColumnStyle: "bg-[var(--color-primary)]/5",
18
- taskBarStyle: "absolute rounded-md cursor-pointer flex items-center",
19
- groupBarStyle: "absolute rounded-md opacity-25",
20
- splitterStyle: "hidden sm:block w-1 cursor-col-resize bg-[var(--color-border)] hover:bg-[var(--color-primary)] transition-colors shrink-0",
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.
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import type { MonthSpan } from "./GanttChart.utils";
3
3
  interface GanttChartPanelProps {
4
- onScroll: () => void;
4
+ onScroll?: () => void;
5
5
  days: Date[];
6
6
  monthSpans: MonthSpan[];
7
7
  }
@@ -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: "flex flex-col items-center gap-2 text-[var(--color-text-muted)]", children: [jsx(CalendarDaysIcon, { className: "size-10 opacity-40" }), jsx("div", { className: "text-base font-medium", children: "No tasks to display" }), jsx("div", { className: "text-sm opacity-70", children: "Add tasks to see them on the timeline" })] }) }));
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: "gantt-arrowhead", 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: "gantt-arrowhead-highlight", 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) => {
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
- ? "url(#gantt-arrowhead-highlight)"
27
- : "url(#gantt-arrowhead)", opacity: faded ? 0.15 : connected ? 1 : 0.6, style: {
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: "absolute text-sm font-medium whitespace-nowrap pointer-events-none", style: {
37
- left: size + 4,
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
- setActiveDrag(isDragging ? { taskId: task.id, mode: "move", deltaX } : null);
49
- }, [setActiveDrag, task.id]),
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
- setActiveDrag(isDragging
70
- ? { taskId: task.id, mode: "resize-left", deltaX }
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
- setActiveDrag(isDragging
93
- ? { taskId: task.id, mode: "resize-right", deltaX }
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-1"), style: {
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: "2rem",
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-2 cursor-ew-resize z-20 rounded-l-full opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/10", onPointerDown: resizeLeftDrag.handlePointerDown })), canDragDates && (jsx("div", { className: "absolute right-0 top-0 bottom-0 w-2 cursor-ew-resize z-20 rounded-r-full opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/10", onPointerDown: resizeRightDrag.handlePointerDown })), canDragProgress && !isNarrow && (jsx("div", { className: "absolute top-0 bottom-0 w-3 cursor-ew-resize z-20 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity", style: { left: `calc(${displayProgress}% - 6px)` }, onPointerDown: progressDrag.handlePointerDown, children: jsx("div", { className: "w-0.5 h-3 rounded-full bg-white/80 shadow" }) })), !isNarrow && (jsxs("div", { className: "relative flex items-center gap-1.5 px-1 z-10 min-w-0 pointer-events-none", children: [task.assignee && (jsx(Avatar, { src: task.assignee.avatar, name: task.assignee.name, size: "xs" })), jsx("span", { className: "truncate text-sm text-[var(--color-text-inverse)] font-medium", children: task.title })] }))] }), isNarrow && (jsx("span", { className: "absolute text-sm font-medium whitespace-nowrap pointer-events-none", style: {
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 &&