refactor: gant bar suport click

This commit is contained in:
katherinehhh 2023-02-07 10:40:38 +08:00
parent 54aa2d7f82
commit ad886ac304
5 changed files with 150 additions and 74 deletions

View File

@ -0,0 +1,6 @@
import { observer } from '@formily/react';
import React from 'react';
export const Event = observer((props) => {
return <>{props.children}</>;
});

View File

@ -1,6 +1,7 @@
import React, { useState, SyntheticEvent, useRef, useEffect, useMemo } from 'react';
import React, { useState, SyntheticEvent, useRef, useEffect, useMemo, useCallback } from 'react';
import { useFieldSchema, Schema, RecursionField } from '@formily/react';
import { cx } from '@emotion/css';
import { createForm } from '@formily/core';
import { Task } from '../../types/public-types';
import { GridProps } from '../grid/grid';
import { ganttDateRange, seedDates } from '../../helpers/date-helper';
@ -16,15 +17,39 @@ import { DateSetup } from '../../types/date-setup';
import { HorizontalScroll } from '../other/horizontal-scroll';
import { removeHiddenTasks, sortTasks } from '../../helpers/other-helper';
import { wrapper } from './style';
import { ActionContext } from '../../../action';
import { useDesignable } from '../../../../../schema-component';
import { useGanttBlockContext, useBlockRequestContext } from '../../../../../block-provider';
import { useBlockRequestContext } from '../../../../../block-provider';
import { RecordProvider } from '../../../../../record-provider';
const getColumnWidth = (dataSetLength: any, clientWidth: any) => {
const columnWidth = clientWidth / dataSetLength > 50 ? Math.floor(clientWidth / dataSetLength) + 20 : 50;
return columnWidth;
};
export const DeleteEventContext = React.createContext({
close: () => {},
});
const GanttRecordViewer = (props) => {
const { visible, setVisible, record } = props;
const form = useMemo(() => createForm(), [record]);
const fieldSchema = useFieldSchema();
const eventSchema: Schema = fieldSchema.properties.detail;
const close = useCallback(() => {
setVisible(false);
}, []);
return (
eventSchema && (
<DeleteEventContext.Provider value={{ close }}>
<ActionContext.Provider value={{ visible, setVisible }}>
<RecordProvider record={record}>
<RecursionField schema={eventSchema} name={eventSchema.name} />
</RecordProvider>
</ActionContext.Provider>
</DeleteEventContext.Provider>
)
);
};
export const Gantt: any = (props: any) => {
const { designable } = useDesignable();
const {
@ -63,9 +88,7 @@ export const Gantt: any = (props: any) => {
useProps,
} = props;
const { onExpanderClick, tasks } = useProps();
// const ctx = useGanttBlockContext();
const { resource, service } = useBlockRequestContext();
// console.log(ctx,useBlockRequestContext())
const fieldSchema = useFieldSchema();
const { fieldNames } = useProps(props);
const viewMode = fieldNames.range || 'day';
@ -76,6 +99,8 @@ export const Gantt: any = (props: any) => {
const [startDate, endDate] = ganttDateRange(tasks, viewMode, preStepsCount);
return { viewMode, dates: seedDates(startDate, endDate, viewMode) };
});
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState<any>({});
const [currentViewDate, setCurrentViewDate] = useState<Date | undefined>(undefined);
const [taskListWidth, setTaskListWidth] = useState(0);
const [svgContainerWidth, setSvgContainerWidth] = useState(0);
@ -359,7 +384,7 @@ export const Gantt: any = (props: any) => {
[fieldNames.progress]: task.progress / 100,
},
});
await service?.refresh()
await service?.refresh();
};
const handleTaskChange = async (task: Task) => {
await resource.update({
@ -370,7 +395,15 @@ export const Gantt: any = (props: any) => {
[fieldNames.end]: task.end,
},
});
await service?.refresh()
await service?.refresh();
};
const handleBarClick = (data) => {
const recordData = service.data?.data?.find((item) => item.id === +data.id);
if (!recordData) {
return;
}
setRecord(recordData);
setVisible(true);
};
const gridProps: GridProps = {
columnWidth,
@ -412,13 +445,13 @@ export const Gantt: any = (props: any) => {
onDateChange: handleTaskChange,
onProgressChange: fieldNames.progress && handleProgressChange,
onDoubleClick,
onClick,
onClick: handleBarClick,
onDelete,
};
return (
<div>
<div>
<GanttRecordViewer visible={visible} setVisible={setVisible} record={record} />
<RecursionField name={'anctionBar'} schema={fieldSchema.properties.toolBar} />
<RecursionField name={'table'} schema={fieldSchema.properties.table} />
<div className={cx(wrapper)} onKeyDown={handleKeyDown} tabIndex={0} ref={wrapperRef}>

View File

@ -1,16 +1,14 @@
import React, { useEffect, useState } from "react";
import { EventOption } from "../../types/public-types";
import { BarTask } from "../../types/bar-task";
import { Arrow } from "../other/arrow";
import { handleTaskBySVGMouseEvent } from "../../helpers/bar-helper";
import { isKeyboardEvent } from "../../helpers/other-helper";
import { TaskItem } from "../task-item/task-item";
import {
BarMoveAction,
GanttContentMoveAction,
GanttEvent,
} from "../../types/gantt-task-actions";
import React, { useEffect, useState } from 'react';
import { EventOption } from '../../types/public-types';
import { BarTask } from '../../types/bar-task';
import { Arrow } from '../other/arrow';
import { handleTaskBySVGMouseEvent } from '../../helpers/bar-helper';
import { isKeyboardEvent } from '../../helpers/other-helper';
import { TaskItem } from '../task-item/task-item';
import { BarMoveAction, GanttContentMoveAction, GanttEvent } from '../../types/gantt-task-actions';
let lastAction = null;
let lastStart = null;
export type TaskGanttContentProps = {
tasks: BarTask[];
dates: Date[];
@ -78,9 +76,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
event.preventDefault();
point.x = event.clientX;
const cursor = point.matrixTransform(
svg?.current.getScreenCTM()?.inverse()
);
const cursor = point.matrixTransform(svg?.current.getScreenCTM()?.inverse());
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
cursor.x,
@ -89,7 +85,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
xStep,
timeStep,
initEventX1Delta,
rtl
rtl,
);
if (isChanged) {
setGanttEvent({ action: ganttEvent.action, changedTask });
@ -98,14 +94,11 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
const handleMouseUp = async (event: MouseEvent) => {
const { action, originalSelectedTask, changedTask } = ganttEvent;
if (!changedTask || !point || !svg?.current || !originalSelectedTask)
return;
if (!changedTask || !point || !svg?.current || !originalSelectedTask) return;
event.preventDefault();
point.x = event.clientX;
const cursor = point.matrixTransform(
svg?.current.getScreenCTM()?.inverse()
);
const cursor = point.matrixTransform(svg?.current.getScreenCTM()?.inverse());
const { changedTask: newChangedTask } = handleTaskBySVGMouseEvent(
cursor.x,
action as BarMoveAction,
@ -113,7 +106,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
xStep,
timeStep,
initEventX1Delta,
rtl
rtl,
);
const isNotLikeOriginal =
@ -122,23 +115,16 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
originalSelectedTask.progress !== newChangedTask.progress;
// remove listeners
svg.current.removeEventListener("mousemove", handleMouseMove);
svg.current.removeEventListener("mouseup", handleMouseUp);
setGanttEvent({ action: "" });
svg.current.removeEventListener('mousemove', handleMouseMove);
svg.current.removeEventListener('mouseup', handleMouseUp);
setGanttEvent({ action: '' });
setIsMoving(false);
// custom operation start
let operationSuccess: any = true;
if (
(action === "move" || action === "end" || action === "start") &&
onDateChange &&
isNotLikeOriginal
) {
if ((action === 'move' || action === 'end' || action === 'start') && onDateChange && isNotLikeOriginal) {
try {
const result = await onDateChange(
newChangedTask,
newChangedTask.barChildren
);
const result = await onDateChange(newChangedTask, newChangedTask.barChildren);
if (result !== undefined) {
operationSuccess = result;
}
@ -147,10 +133,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
}
} else if (onProgressChange && isNotLikeOriginal) {
try {
const result = await onProgressChange(
newChangedTask,
newChangedTask.barChildren
);
const result = await onProgressChange(newChangedTask, newChangedTask.barChildren);
if (result !== undefined) {
operationSuccess = result;
}
@ -167,14 +150,14 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
if (
!isMoving &&
(ganttEvent.action === "move" ||
ganttEvent.action === "end" ||
ganttEvent.action === "start" ||
ganttEvent.action === "progress") &&
(ganttEvent.action === 'move' ||
ganttEvent.action === 'end' ||
ganttEvent.action === 'start' ||
ganttEvent.action === 'progress') &&
svg?.current
) {
svg.current.addEventListener("mousemove", handleMouseMove);
svg.current.addEventListener("mouseup", handleMouseUp);
svg.current.addEventListener('mousemove', handleMouseMove);
svg.current.addEventListener('mouseup', handleMouseUp);
setIsMoving(true);
}
}, [
@ -198,16 +181,16 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
const handleBarEventStart = async (
action: GanttContentMoveAction,
task: BarTask,
event?: React.MouseEvent | React.KeyboardEvent
event?: React.MouseEvent | React.KeyboardEvent,
) => {
if (!event) {
if (action === "select") {
if (action === 'select') {
setSelectedTask(task.id);
}
}
// Keyboard events
else if (isKeyboardEvent(event)) {
if (action === "delete") {
if (action === 'delete') {
if (onDelete) {
try {
const result = await onDelete(task);
@ -215,13 +198,13 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
setGanttEvent({ action, changedTask: task });
}
} catch (error) {
console.error("Error on Delete. " + error);
console.error('Error on Delete. ' + error);
}
}
}
}
// Mouse Events
else if (action === "mouseenter") {
else if (action === 'mouseenter') {
if (!ganttEvent.action) {
setGanttEvent({
action,
@ -229,22 +212,20 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
originalSelectedTask: task,
});
}
} else if (action === "mouseleave") {
if (ganttEvent.action === "mouseenter") {
setGanttEvent({ action: "" });
} else if (action === 'mouseleave') {
if (ganttEvent.action === 'mouseenter') {
setGanttEvent({ action: '' });
}
} else if (action === "dblclick") {
} else if (action === 'dblclick') {
!!onDoubleClick && onDoubleClick(task);
} else if (action === "click") {
} else if (action === 'click') {
!!onClick && onClick(task);
}
// Change task event start
else if (action === "move") {
else if (action === 'move') {
if (!svg?.current || !point) return;
point.x = event.clientX;
const cursor = point.matrixTransform(
svg.current.getScreenCTM()?.inverse()
);
const cursor = point.matrixTransform(svg.current.getScreenCTM()?.inverse());
setInitEventX1Delta(cursor.x - task.x1);
setGanttEvent({
action,
@ -260,11 +241,27 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
}
};
const handleBarEvent = (action, task, event) => {
if (['click'].includes(action)) {
if (!['start', 'end', 'progress'].includes(lastAction) && (!lastStart || lastStart === task.start)) {
handleBarEventStart(action, task, event);
}
lastAction = null;
lastStart = null;
} else if (['move', 'select'].includes(action)) {
lastStart = task.start;
handleBarEventStart(action, task, event);
} else {
lastStart = task.start;
lastAction = action;
handleBarEventStart(action, task, event);
}
};
return (
<g className="content">
<g className="arrows" fill={arrowColor} stroke={arrowColor}>
{tasks.map(task => {
return task.barChildren.map(child => {
{tasks.map((task) => {
return task.barChildren.map((child) => {
return (
<Arrow
key={`Arrow from ${task.id} to ${tasks[child.index].id}`}
@ -280,7 +277,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
})}
</g>
<g className="bar" fontFamily={fontFamily} fontSize={fontSize}>
{tasks.map(task => {
{tasks.map((task) => {
return (
<TaskItem
task={task}
@ -289,7 +286,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
isProgressChangeable={!!onProgressChange && !task.isDisabled}
isDateChangeable={!!onDateChange && !task.isDisabled}
isDelete={!task.isDisabled}
onEventStart={handleBarEventStart}
onEventStart={handleBarEvent}
key={task.id}
isSelected={!!selectedTask && task.id === selectedTask.id}
rtl={rtl}

View File

@ -1,13 +1,14 @@
import { ActionBar } from '../action';
import { Gantt } from './components/gantt/gantt';
import { GanttDesigner } from './Gantt.Designer';
import { ViewMode } from './types/public-types';
import {Event} from './components/gantt/Event';
Gantt.ActionBar = ActionBar;
Gantt.ViewMode = ViewMode;
Gantt.Designer = GanttDesigner;
Gantt.Event = Event;
// const GanttV2 = Gantt;

View File

@ -1100,6 +1100,45 @@ export const createGanttBlockSchema = (options) => {
},
},
},
detail: {
type: 'void',
'x-component': 'Gantt.Event',
properties: {
drawer: {
type: 'void',
'x-component': 'Action.Drawer',
'x-component-props': {
className: 'nb-action-popup',
},
title: '{{ t("View record") }}',
properties: {
tabs: {
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
properties: {
tab1: {
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
properties: {},
},
},
},
},
},
},
},
},
},
},
},
},