Skip to content

Commit

Permalink
Merge pull request #114 from melfore/113-task-flags-for-actions
Browse files Browse the repository at this point in the history
[Task] Flags for actions
  • Loading branch information
luciob committed Oct 16, 2023
2 parents 57adb97 + 241d94f commit 8e467ac
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 76 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# [1.10.0](https://github.com/melfore/konva-timeline/compare/v1.9.0...v1.10.0) (2023-10-16)


### Features

* 🎸 [Task] Added y coordinate getter ([b8d3b24](https://github.com/melfore/konva-timeline/commit/b8d3b24492b603185ef24f668eeb2c97076db9d6))
- 🎸 [Task] Added y coordinate getter ([b8d3b24](https://github.com/melfore/konva-timeline/commit/b8d3b24492b603185ef24f668eeb2c97076db9d6))

# [1.9.0](https://github.com/melfore/konva-timeline/compare/v1.8.0...v1.9.0) (2023-09-29)

Expand Down
2 changes: 1 addition & 1 deletion src/KonvaTimeline/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const meta = {
onTaskClick: {
type: "function",
},
onTaskDrag: {
onTaskChange: {
type: "function",
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/KonvaTimeline/monthly-scenario.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const meta = {
component: KonvaTimeline,
tags: ["autodocs"],
argTypes: {
onTaskDrag: {
onTaskChange: {
type: "function",
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/KonvaTimeline/yearly-scenario.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const meta = {
component: KonvaTimeline,
tags: ["autodocs"],
argTypes: {
onTaskDrag: {
onTaskChange: {
type: "function",
},
},
Expand Down
140 changes: 76 additions & 64 deletions src/tasks/components/Task/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { memo, useCallback, useMemo, useState } from "react";
import { Group, Rect, useStrictMode as enableStrictMode } from "react-konva";
import { KonvaEventObject } from "konva/lib/Node";
import { DateTime, Duration } from "luxon";
import { Duration } from "luxon";

import { TimeRange } from "../../..";
import { KonvaText } from "../../../@konva";
import { findResourceByCoordinate, findResourceIndexByCoordinate } from "../../../resources/utils/resources";
import { useTimelineContext } from "../../../timeline/TimelineContext";
import { KonvaDrawable, KonvaPoint } from "../../../utils/konva";
import { logDebug } from "../../../utils/logger";
import { getContrastColor } from "../../../utils/theme";
import { getTaskYCoordinate, TaskData } from "../../utils/tasks";
import TaskResizeHandler from "../TaskResizeHandler";
Expand Down Expand Up @@ -65,9 +65,11 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
columnWidth,
displayTasksLabel,
dragResolution: { sizeInUnits: dragSizeInUnits, unit: dragUnit },
enableDrag,
enableResize,
interval,
onTaskClick,
onTaskDrag,
onTaskChange,
resolution: { sizeInUnits, unit },
resources,
rowHeight,
Expand All @@ -85,6 +87,19 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:

const [taskDimensions, setTaskDimensions] = useState(initialTaskDimensions);

const fromPxToTime = useCallback(
(sizePx: number): number => (sizePx * sizeInUnits) / columnWidth,
[columnWidth, sizeInUnits]
);

const onEndTimeRange = useCallback((): TimeRange => {
const { x, width } = taskDimensions;
const timeOffset = fromPxToTime(x);
const start = interval.start!.plus({ [unit]: timeOffset }).toMillis();
const end = start + Duration.fromObject({ [unit]: fromPxToTime(width) }).toMillis();
return { start, end };
}, [fromPxToTime, interval.start, taskDimensions, unit]);

const dragSnapInPX = useMemo(() => {
const resolutionInSnapUnit = Duration.fromObject({ [unit]: sizeInUnits }).as(dragUnit);
const dragSnapInResUnit = dragSizeInUnits / resolutionInSnapUnit;
Expand Down Expand Up @@ -138,10 +153,13 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
return;
}

stage.container().style.cursor = "default";
if (enableDrag) {
stage.container().style.cursor = "default";
}

onTaskMouseEvent(e, onLeave);
},
[onLeave, onTaskMouseEvent, resizing]
[enableDrag, onLeave, onTaskMouseEvent, resizing]
);

const onTaskOver = useCallback(
Expand All @@ -156,21 +174,19 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
return;
}

stage.container().style.cursor = "move";
if (enableDrag) {
stage.container().style.cursor = "move";
}

onTaskMouseEvent(e, onOver);
},
[onOver, onTaskMouseEvent, resizing]
[enableDrag, onOver, onTaskMouseEvent, resizing]
);

const onDragStart = useCallback((e: KonvaEventObject<DragEvent>) => {
console.log("=> onDragStart", e.target);
setDragging(true);
}, []);
const onDragStart = useCallback((e: KonvaEventObject<DragEvent>) => setDragging(true), []);

const onDragMove = useCallback(
(e: KonvaEventObject<DragEvent>) => {
// e.cancelBubble = true;
console.log("=> onDragMove", e.target);
const { x, y } = getDragPoint(e);
const dragFinalX = Math.ceil(x / dragSnapInPX) * dragSnapInPX;
const xCoordinate = dragFinalX < 0 ? 0 : dragFinalX;
Expand All @@ -186,29 +202,17 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:

const onDragEnd = useCallback(
(e: KonvaEventObject<DragEvent>) => {
// e.cancelBubble = true;
console.log("=> onDragEnd", e.target);
const { x, y } = getDragPoint(e);
const timeOffset = (x * sizeInUnits) / columnWidth;
const newStartInMillis = interval.start!.plus({ [unit]: timeOffset }).toMillis();
const newEndInMillis =
newStartInMillis + Duration.fromObject({ [unit]: (width * sizeInUnits) / columnWidth }).toMillis();
const resource = findResourceByCoordinate(y, rowHeight, resources);
logDebug("Task", `New Start: ${x} / ${x} / ${timeOffset} / ${newStartInMillis}`);
logDebug("Task", `StartTime: ${DateTime.fromMillis(newStartInMillis).toISO()}`);
logDebug("Task", `End: ${DateTime.fromMillis(newEndInMillis).toISO()}`);
setDragging(false);
onTaskDrag &&
onTaskDrag({
...data,
resourceId: resource.id,
time: {
end: newEndInMillis,
start: newStartInMillis,
},
});
if (!onTaskChange) {
return;
}

const { y } = getDragPoint(e);
const { id: resourceId } = findResourceByCoordinate(y, rowHeight, resources);
const time = onEndTimeRange();
onTaskChange({ ...data, resourceId, time });
},
[columnWidth, data, interval.start, onTaskDrag, getDragPoint, resources, rowHeight, sizeInUnits, unit, width]
[getDragPoint, onEndTimeRange, rowHeight, resources, onTaskChange, data]
);

const opacity = useMemo(() => (dragging || resizing ? 0.5 : 1), [dragging, resizing]);
Expand All @@ -225,7 +229,6 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:

const onResizeStart = useCallback((e: KonvaEventObject<DragEvent>) => {
e.cancelBubble = true;
console.log("=> onResizeStart", e.target);
setResizing(true);
}, []);

Expand All @@ -234,7 +237,6 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
e.cancelBubble = true;

const { x: dragX } = getDragPoint(e);

setTaskDimensions((taskDimensions) => {
const { x: taskX, width: taskWidth } = taskDimensions;
const handlerX = taskX + dragX;
Expand All @@ -243,14 +245,12 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
switch (handler) {
case "rx":
if (handlerX <= taskX + TASK_BORDER_RADIUS) {
console.log("=> onResizeMove: abort x lower than task start");
return { ...taskDimensions };
}

return { ...taskDimensions, width: handlerX - taskX };
case "lx":
if (handlerX >= taskEndX - TASK_BORDER_RADIUS) {
console.log("=> onResizeMove: abort x higher than task end");
return { ...taskDimensions };
}

Expand All @@ -261,17 +261,25 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
[getDragPoint]
);

const onResizeEnd = useCallback((e: KonvaEventObject<DragEvent>) => {
e.cancelBubble = true;
console.log("=> onResizeEnd", e.target);
setResizing(false);
}, []);
const onResizeEnd = useCallback(
(e: KonvaEventObject<DragEvent>) => {
e.cancelBubble = true;
setResizing(false);
if (!onTaskChange) {
return;
}

const time = onEndTimeRange();
onTaskChange({ ...data, time });
},
[onTaskChange, data, onEndTimeRange]
);

return (
<Group
x={taskDimensions.x}
y={taskDimensions.y}
draggable={!!onTaskDrag}
draggable={enableDrag}
onClick={onClick}
onDragEnd={onDragEnd}
onDragMove={onDragMove}
Expand All @@ -290,26 +298,30 @@ const Task = ({ data, fill = TASK_DEFAULT_FILL, onLeave, onOver, x, y, width }:
strokeWidth={TASK_DEFAULT_STROKE_WIDTH}
width={taskDimensions.width}
/>
<TaskResizeHandler
height={taskHeight}
onResizeStart={onResizeStart}
onResizeMove={onResizeMove}
onResizeEnd={onResizeEnd}
opacity={opacity}
position="lx"
taskId={taskId}
xCoordinate={-1}
/>
<TaskResizeHandler
height={taskHeight}
onResizeStart={onResizeStart}
onResizeMove={onResizeMove}
onResizeEnd={onResizeEnd}
opacity={opacity}
position="rx"
taskId={taskId}
xCoordinate={taskDimensions.width}
/>
{enableResize && (
<TaskResizeHandler
height={taskHeight}
onResizeStart={onResizeStart}
onResizeMove={onResizeMove}
onResizeEnd={onResizeEnd}
opacity={opacity}
position="lx"
taskId={taskId}
xCoordinate={-1}
/>
)}
{enableResize && (
<TaskResizeHandler
height={taskHeight}
onResizeStart={onResizeStart}
onResizeMove={onResizeMove}
onResizeEnd={onResizeEnd}
opacity={opacity}
position="rx"
taskId={taskId}
xCoordinate={taskDimensions.width}
/>
)}
{displayTasksLabel && (
<KonvaText
fill={textStroke}
Expand Down
24 changes: 19 additions & 5 deletions src/timeline/TimelineContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export type TimelineProviderProps = PropsWithChildren<TimelineInput> & {
* Enables debug logging in browser console
*/
debug?: boolean;
/**
* Enables drag&drop operation on tasks
*/
enableDrag?: boolean;
/**
* Enables resize operation on tasks
*/
enableResize?: boolean;
/**
* Callback invoked when errors are thrown
*/
Expand All @@ -34,9 +42,9 @@ export type TimelineProviderProps = PropsWithChildren<TimelineInput> & {
*/
onTaskClick?: (task: TaskData) => void;
/**
* Event handler for task click
* Event handler for task change event (drag and resize)
*/
onTaskDrag?: (task: TaskData) => void;
onTaskChange?: (task: TaskData) => void;
/**
* Theme color in use
*/
Expand All @@ -53,10 +61,12 @@ type TimelineContextType = Required<
blocksOffset: number;
dragResolution: ResolutionData;
drawRange: InternalTimeRange;
enableDrag: boolean;
enableResize: boolean;
interval: Interval;
onErrors?: (errors: KonvaTimelineError[]) => void;
onTaskClick?: (task: TaskData) => void;
onTaskDrag?: (task: TaskData) => void;
onTaskChange?: (task: TaskData) => void;
resolution: ResolutionData;
resolutionKey: Resolution;
resourcesContentHeight: number;
Expand All @@ -80,10 +90,12 @@ export const TimelineProvider = ({
debug = false,
displayTasksLabel = false,
dragResolution: externalDragResolution,
enableDrag = true,
enableResize = true,
hideResources = false,
onErrors,
onTaskClick,
onTaskDrag,
onTaskChange,
tasks: externalTasks,
range: externalRange,
resolution: externalResolution,
Expand Down Expand Up @@ -220,11 +232,13 @@ export const TimelineProvider = ({
displayTasksLabel,
dragResolution,
drawRange,
enableDrag,
enableResize,
hideResources,
interval,
onErrors,
onTaskClick,
onTaskDrag,
onTaskChange,
resolution,
resolutionKey: externalResolution,
resources,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const TaskDecorator = (storyFn: any) => {
<TimelineProvider
{...STORY_DATA}
onTaskClick={(task) => alert(`OnTaskClick event handler - TaskId: ${task.id}`)}
onTaskDrag={(task) => alert(`OnTaskDrag event handler - TaskId: ${task.id}`)}
onTaskChange={(task) => alert(`OnTaskChange event handler - TaskId: ${task.id}`)}
resolution="1hrs"
>
<div ref={wrapperRef}>
Expand Down Expand Up @@ -74,7 +74,7 @@ export const TasksLayerDecorator = (storyFn: any) => {
<TimelineProvider
{...STORY_DATA}
onTaskClick={(task) => alert(`OnTaskClick event handler - TaskId: ${task.id}`)}
onTaskDrag={(task) => alert(`OnTaskDrag event handler - TaskId: ${task.id}`)}
onTaskChange={(task) => alert(`OnTaskChange event handler - TaskId: ${task.id}`)}
resolution="1hrs"
>
<TasksLayerInternalDecorator storyFn={storyFn} />
Expand Down

0 comments on commit 8e467ac

Please sign in to comment.