diff --git a/example/helper.tsx b/example/helper.tsx new file mode 100644 index 000000000..8c3e5c69a --- /dev/null +++ b/example/helper.tsx @@ -0,0 +1,271 @@ +import format from "date-fns/format"; +import isValid from "date-fns/isValid"; +import parse from "date-fns/parse"; +import startOfMinute from "date-fns/startOfMinute"; +import startOfDay from "date-fns/startOfDay"; +import endOfDay from "date-fns/endOfDay"; + +import { EditableTaskInfo, EmptyTask, Task, TaskOrEmpty } from "../src"; + +const dateFormat = "dd/MM/yyyy HH:mm"; + +export function initTasks(): TaskOrEmpty[] { + const currentDate = new Date(); + const tasks: TaskOrEmpty[] = [ + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1), + end: new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + 2, + 12, + 28 + ), + name: "Idea", + id: "Idea", + progress: 45, + type: "task", + }, + { + id: "taskWithoutDateId", + type: "empty", + name: "TaskWithoutDate", + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 2), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4, 0, 0), + name: "Research", + id: "Research", + progress: 25, + dependencies: [ + { + sourceId: "Idea", + sourceTarget: "endOfTask", + ownTarget: "startOfTask" + } + ], + type: "task", + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 4), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8, 0, 0), + name: "Discussion with team", + id: "Discussion", + progress: 10, + dependencies: [ + { + sourceId: "Research", + sourceTarget: "endOfTask", + ownTarget: "startOfTask" + } + ], + type: "task", + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8), + end: new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + 10, + 0, + 0 + ), + name: "Developing", + id: "developing", + progress: 50, + dependencies: [ + { + sourceId: "Discussion", + sourceTarget: "endOfTask", + ownTarget: "startOfTask" + } + ], + type: "project", + isDisabled: true, + hideChildren: true + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9), + name: "Code", + id: "code", + type: "task", + progress: 40, + parent: "developing" + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9), + name: "Frontend", + id: "frontend", + type: "task", + progress: 40, + parent: "code", + assignees: ["Bob", "Peter"] + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 9), + name: "Backend", + id: "backend", + type: "task", + progress: 40, + parent: "code", + assignees: ["Marc"] + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 10), + name: "Review", + id: "review", + type: "task", + progress: 70, + parent: "developing" + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15), + name: "Release", + id: "release", + progress: currentDate.getMonth(), + type: "milestone", + dependencies: [ + { + sourceId: "review", + sourceTarget: "endOfTask", + ownTarget: "startOfTask" + } + ], + }, + { + start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 18), + end: new Date(currentDate.getFullYear(), currentDate.getMonth(), 19), + name: "Party Time", + id: "party", + progress: 0, + isDisabled: true, + isRelationDisabled: true, + type: "task" + } + ]; + + return tasks.map(taskOrEmpty => { + if ("start" in taskOrEmpty && "end" in taskOrEmpty) { + const task = taskOrEmpty as Task; + return { + ...task, + start: startOfDay(task.start), + end: endOfDay(task.end) + }; + } + // Return the task as-is if it doesn't have 'start' and 'end' + return taskOrEmpty; + }); +} + +export const getTaskFields = (initialValues: { + name?: string; + start?: Date | null; + end?: Date | null; +}) => { + const name = prompt("Name", initialValues.name); + + const startDateStr = + prompt( + "Start date", + initialValues.start ? format(initialValues.start, dateFormat) : "" + ) || ""; + + const startDate = startOfMinute(parse(startDateStr, dateFormat, new Date())); + + const endDateStr = + prompt( + "End date", + initialValues.end ? format(initialValues.end, dateFormat) : "" + ) || ""; + + const endDate = startOfMinute(parse(endDateStr, dateFormat, new Date())); + + return { + name, + start: isValid(startDate) ? startDate : null, + end: isValid(endDate) ? endDate : null + }; +}; + +export const onAddTask = (parentTask: Task) => { + const taskFields = getTaskFields({ + start: parentTask.start, + end: parentTask.end + }); + + const nextTask: TaskOrEmpty = + taskFields.start && taskFields.end + ? { + type: "task", + start: taskFields.start, + end: taskFields.end, + comparisonLevel: parentTask.comparisonLevel, + id: String(Date.now()), + name: taskFields.name || "", + progress: 0, + parent: parentTask.id, + styles: parentTask.styles + } + : { + type: "empty", + comparisonLevel: parentTask.comparisonLevel, + id: String(Date.now()), + name: taskFields.name || "", + parent: parentTask.id, + styles: parentTask.styles + }; + + return Promise.resolve(nextTask); +}; + +export const onEditTask = (editableTaskInfo: EditableTaskInfo) => { + console.log(editableTaskInfo) + const { task, start, end } = editableTaskInfo; + const taskFields = getTaskFields({ + name: task.name, + start: task.type === "empty" && !task.name ? null : start, + end: task.type === "empty" && !task.name ? null : end + }); + + let nextTask: TaskOrEmpty; + if (task.type === "task" || task.type === "empty") { + nextTask = taskFields.start && taskFields.end + ? { + type: "task", + start: taskFields.start, + end: taskFields.end, + comparisonLevel: task.comparisonLevel, + id: task.id, + name: taskFields.name || task.name, + progress: task.type === "empty" ? 0 : (task as Task).progress, + dependencies: task.type === "empty" ? undefined : (task as Task).dependencies, + parent: task.parent, + styles: task.styles, + isDisabled: task.isDisabled + } + : { + type: "empty", + comparisonLevel: task.comparisonLevel, + id: task.id, + name: taskFields.name || task.name, + parent: task.parent, + styles: task.styles, + isDisabled: task.isDisabled + } as EmptyTask; + } else { + nextTask = { + ...task, + name: taskFields.name || task.name, + start: taskFields.start || (task as Task).start, + end: taskFields.end || (task as Task).end + } as Task; + } + + return Promise.resolve(nextTask); +}; diff --git a/example/simple.tsx b/example/simple.tsx new file mode 100644 index 000000000..78b7139d7 --- /dev/null +++ b/example/simple.tsx @@ -0,0 +1,133 @@ +import React, { useCallback, useState } from 'react'; + +import addDays from 'date-fns/addDays'; + +import { Gantt, OnChangeTasks, Task, TaskOrEmpty, TitleColumn, ViewMode } from '../src'; + +import { onAddTask, onEditTask } from './helper'; + +import '../dist/style.css'; + +const NUMBER_OF_SUBTASKS = 6; + +const initTasks = () => { + const res: TaskOrEmpty[] = []; + + const projectStartDate = new Date(); + const projectEndDate = addDays(projectStartDate, NUMBER_OF_SUBTASKS); + + const projectId = "project"; + const projectName = "Project"; + + const project: Task = { + start: projectStartDate, + end: projectEndDate, + name: projectName, + id: projectId, + progress: 25, + type: "project", + }; + + res.push(project); + + for (let j = 0; j < NUMBER_OF_SUBTASKS; ++j) { + const taskId = `${projectId}/not_connected_task_${j + 1}`; + const taskName = `Not connected task ${j + 1}`; + + const task: Task = { + start: addDays(projectStartDate, j), + end: addDays(projectStartDate, j + 1), + name: taskName, + id: taskId, + progress: 45, + type: "task", + parent: projectId, + }; + + res.push(task); + } + + for (let j = 0; j < NUMBER_OF_SUBTASKS; ++j) { + const taskId = `${projectId}/task_${j + 1}`; + const taskName = `Task ${j + 1}`; + + const task: TaskOrEmpty = { + start: addDays(projectStartDate, j), + end: addDays(projectStartDate, j + 1), + name: taskName, + id: taskId, + progress: 45, + type: "task", + parent: projectId, + dependencies: undefined, + }; + + res.push(task); + } + + res.push({ + type: 'empty', + name: 'An empty task', + id: `${projectId}/task_${NUMBER_OF_SUBTASKS + 1}` + }) + + return res; +}; + +export const Simple: React.FC = props => { + const [tasks, setTasks] = useState(initTasks); + + const onChangeTasks = useCallback((nextTasks, action) => { + switch (action.type) { + case "delete_relation": + if ( + window.confirm( + `Do yo want to remove relation between ${action.payload.taskFrom.name} and ${action.payload.taskTo.name}?` + ) + ) { + setTasks(nextTasks); + } + break; + + case "delete_task": + if (window.confirm("Are you sure?")) { + setTasks(nextTasks); + } + break; + + default: + setTasks(nextTasks); + break; + } + }, []); + + const handleDblClick = useCallback((task: Task) => { + alert("On Double Click event Id:" + task.id); + }, []); + + const handleClick = useCallback((task: TaskOrEmpty) => { + console.log("On Click event Id:" + task.id); + }, []); + + const columns = [{ + Cell: TitleColumn, + width: 200, + title: "Task", + id: 'Task' + }] + + return ( + false} + /> + ); +}; diff --git a/example/src/components/view-switcher.tsx b/example/src/components/view-switcher.tsx deleted file mode 100644 index 42f736016..000000000 --- a/example/src/components/view-switcher.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -import "gantt-task-react/dist/index.css"; -import { ViewMode } from "gantt-task-react"; -type ViewSwitcherProps = { - isChecked: boolean; - onViewListChange: (isChecked: boolean) => void; - onViewModeChange: (viewMode: ViewMode) => void; -}; -export const ViewSwitcher: React.FC = ({ - onViewModeChange, - onViewListChange, - isChecked, -}) => { - return ( -
- - - - - - - - -
- - Show Task List -
-
- ); -}; diff --git a/index.html b/index.html new file mode 100644 index 000000000..c1171de76 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Vite React Project + + + +
+ + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index acd670a4b..33392b6aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "@types/node": "~15.0.3", "@types/react": "~18.3.12", "@types/react-dom": "~18.3.1", - "@vitejs/plugin-react": "4.0.4", + "@vitejs/plugin-react": "^4.0.4", "cross-env": "~7.0.3", "doctrine": "~3.0.0", "eslint": "^8.50.0", @@ -9543,7 +9543,7 @@ }, "node_modules/@vitejs/plugin-react": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz", + "resolved": "https://mirrors.tencent.com/npm/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz", "integrity": "sha512-7wU921ABnNYkETiMaZy7XqpueMnpu5VxvVps13MjmCo+utBdD79sZzrApHawHtVX66cCJQQTXFcjH0y9dSUK8g==", "dev": true, "license": "MIT", diff --git a/package.json b/package.json index 67712f402..46dc6a561 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "@types/node": "~15.0.3", "@types/react": "~18.3.12", "@types/react-dom": "~18.3.1", - "@vitejs/plugin-react": "4.0.4", + "@vitejs/plugin-react": "^4.0.4", "cross-env": "~7.0.3", "doctrine": "~3.0.0", "eslint": "^8.50.0", @@ -125,5 +125,6 @@ "moduleNameMapper": { "/~.+\\.module\\.(css|sass|scss)$/": "identity-obj-proxy" } - } + }, + "packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a" } diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index 4e7e03e6d..bca173306 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -13,6 +13,7 @@ import { DateSetup, Dependency, Distances, + EditableTaskInfo, FixPosition, GanttDateRoundingTimeUnit, GanttProps, @@ -790,11 +791,13 @@ export const Gantt: React.FC = ({ ); const handleEditTask = useCallback( - (task: TaskOrEmpty) => { + (editableTaskInfo: EditableTaskInfo) => { if (!onEditTaskClick && (!onEditTask || !onChangeTasks)) { return; } + const { task } = editableTaskInfo; + const { id, comparisonLevel = 1 } = task; const indexesOnLevel = mapTaskToGlobalIndex.get(comparisonLevel); @@ -810,14 +813,14 @@ export const Gantt: React.FC = ({ } if (onEditTaskClick) { - onEditTaskClick(task, taskIndex, (changedTask: TaskOrEmpty) => + onEditTaskClick((editableTaskInfo), taskIndex, (changedTask: TaskOrEmpty) => getMetadata({ type: "change", task: changedTask }) ); } else if (onEditTask && onChangeTasks) { - onEditTask(task).then(nextTask => { + onEditTask(editableTaskInfo).then(nextTask => { if (!nextTask) { return; } @@ -1954,6 +1957,7 @@ export const Gantt: React.FC = ({ ganttTaskRootRef={ganttTaskRootRef} onScrollGanttContentVertically={onScrollVertically} colors={colors} + handleEditTask={handleEditTask} /> {tooltipTaskFromMap && ( diff --git a/src/components/gantt/task-gantt.tsx b/src/components/gantt/task-gantt.tsx index 42bc28f26..8c80bc455 100644 --- a/src/components/gantt/task-gantt.tsx +++ b/src/components/gantt/task-gantt.tsx @@ -12,9 +12,10 @@ import { Task, Distances, DateExtremity, - TaskDependencyContextualPaletteProps, ColorStyles + TaskDependencyContextualPaletteProps, ColorStyles, EditableTaskInfo } from "../../types/public-types"; import ClickAwayListener from "@mui/material/ClickAwayListener"; +import { HoverableCell } from "../other/hoverable-cell"; export type TaskGanttProps = { barProps: TaskGanttContentProps; @@ -32,6 +33,7 @@ export type TaskGanttProps = { event: SyntheticEvent ) => void; colors: Partial + handleEditTask: (editableTaskInfo: EditableTaskInfo) => void; }; const TaskGanttInner: React.FC = (props) => { @@ -49,7 +51,8 @@ const TaskGanttInner: React.FC = (props) => { onVerticalScrollbarScrollX, ganttTaskRootRef, onScrollGanttContentVertically: onScrollVertically, - colors + colors, + handleEditTask, } = props; const containerStyle: CSSProperties = { // In order to see the vertical scrollbar of the gantt content, @@ -102,6 +105,8 @@ const TaskGanttInner: React.FC = (props) => { setSelectedDependency({ taskFrom, extremityFrom, taskTo, extremityTo }); }; + const [selectedCellPos, setSelectedCellPos] = React.useState<[number, number]>([0, 0]); + const onCloseArrowContextualPalette = () => { setArrowAnchorEl(null); }; @@ -180,6 +185,30 @@ const TaskGanttInner: React.FC = (props) => { } } }; + + const lastPosRef = React.useRef<[number, number]>([-999, -999]); + const onMouseMove = (e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + const xIndex = Math.floor(x / columnWidth); + const yIndex = Math.floor(y / rowHeight); + + if (lastPosRef.current[0] === xIndex && lastPosRef.current[1] === yIndex) { + return; + } + + lastPosRef.current = [xIndex, yIndex]; + + const { mapGlobalRowIndexToTask } = barProps; + const task = mapGlobalRowIndexToTask.get(yIndex); + if (task.type === 'empty') { + setSelectedCellPos([xIndex, yIndex]); + } else { + setSelectedCellPos([-999, -999]); + } + } + return (
= (props) => { style={{ background: colors.oddTaskBackgroundColor }} + onMouseMove={onMouseMove} > = (props) => { onClick={onClickTask} onArrowClick={onClickArrow} /> + { + handleEditTask(editableTaskInfo); + }} + />
{barProps.ContextualPalette && open && ( diff --git a/src/components/other/hoverable-cell.tsx b/src/components/other/hoverable-cell.tsx new file mode 100644 index 000000000..e11e6d05a --- /dev/null +++ b/src/components/other/hoverable-cell.tsx @@ -0,0 +1,44 @@ +import React, { memo, useCallback } from "react"; +import { TaskGanttContentProps } from "../gantt/task-gantt-content"; +import { EditableTaskInfo } from "../../types/public-types"; +import { GridProps } from "../grid/grid"; + +type HoverableCellProps = { + x: number; + y: number; + width: number; + height: number; + selectedCellPos: [number, number]; + gridProps: GridProps; + barProps: TaskGanttContentProps; + onClick: (editableTaskInfo: EditableTaskInfo) => void; +} + +const HoverableCellInner: React.FC = ({ + x, + y, + width, + height, + selectedCellPos: [selectedCellX, selectedCellY], + gridProps: { getDate }, + barProps: { mapGlobalRowIndexToTask }, + onClick, +}) => { + const onCellClick = useCallback(() => { + const task = mapGlobalRowIndexToTask.get(selectedCellY); + const start = getDate(selectedCellX); + const end = getDate(selectedCellX + 1); + onClick({ task, start, end }); + }, [mapGlobalRowIndexToTask, selectedCellY, getDate, selectedCellX, onClick]); + + return ( + + + + + + + ) +} + +export const HoverableCell = memo(HoverableCellInner); diff --git a/src/components/task-list/task-list.tsx b/src/components/task-list/task-list.tsx index 5a3df008c..6fded2151 100644 --- a/src/components/task-list/task-list.tsx +++ b/src/components/task-list/task-list.tsx @@ -8,6 +8,7 @@ import { DateSetup, DependencyMap, Distances, + EditableTaskInfo, Icons, MapTaskToNestedIndex, OnResizeColumn, @@ -42,7 +43,7 @@ export type TaskListProps = { getTaskCurrentState: (task: Task) => Task; handleAddTask: (task: Task) => void; handleDeleteTasks: (task: TaskOrEmpty[]) => void; - handleEditTask: (task: TaskOrEmpty) => void; + handleEditTask: (editableTaskInfo: EditableTaskInfo) => void; handleMoveTaskBefore: (target: TaskOrEmpty, taskForMove: TaskOrEmpty) => void; handleMoveTaskAfter: (target: TaskOrEmpty, taskForMove: TaskOrEmpty) => void; handleMoveTasksInside: (parent: Task, childs: readonly TaskOrEmpty[]) => void; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 000000000..b0964efcc --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { Simple } from '../example/simple' + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); +root.render( + + + +); \ No newline at end of file diff --git a/src/types/public-types.ts b/src/types/public-types.ts index bf7203e01..7de1d923b 100644 --- a/src/types/public-types.ts +++ b/src/types/public-types.ts @@ -156,7 +156,7 @@ export interface Distances { titleCellWidth: number; } -export type TaskType = "task" | "milestone" | "project"; +export type TaskType = "task" | "milestone" | "project" | "empty" ; export interface Task { id: string; @@ -250,8 +250,14 @@ export type OnProgressChange = ( index: number ) => void; +export type EditableTaskInfo = { + task: TaskOrEmpty; + start: Date; + end: Date; +} + export type OnEditTask = ( - task: TaskOrEmpty, + editableTaskInfo: EditableTaskInfo, index: number, getMetadata: GetMetadata ) => void; @@ -425,7 +431,7 @@ export interface EventOption { /** * Callback for getting new data of the edited task */ - onEditTask?: (task: TaskOrEmpty) => Promise; + onEditTask?: (editableTaskInfo: EditableTaskInfo) => Promise; /** * Invokes on edit button click */ @@ -628,7 +634,7 @@ export interface TaskListTableProps { getTaskCurrentState: (task: Task) => Task; handleAddTask: (task: Task) => void; handleDeleteTasks: (task: TaskOrEmpty[]) => void; - handleEditTask: (task: TaskOrEmpty) => void; + handleEditTask: (editableTaskInfo: EditableTaskInfo) => void; handleMoveTaskBefore: (target: TaskOrEmpty, taskForMove: TaskOrEmpty) => void; handleMoveTaskAfter: (target: TaskOrEmpty, taskForMove: TaskOrEmpty) => void; handleMoveTasksInside: (parent: Task, childs: readonly TaskOrEmpty[]) => void; @@ -811,7 +817,7 @@ export type ColumnData = { distances: Distances; handleAddTask: (task: Task) => void; handleDeleteTasks: (task: TaskOrEmpty[]) => void; - handleEditTask: (task: TaskOrEmpty) => void; + handleEditTask: (editableTaskInfo: EditableTaskInfo) => void; hasChildren: boolean; icons?: Partial; indexStr: string; diff --git a/stories/helper.tsx b/stories/helper.tsx index dbe0be89d..a177f4773 100644 --- a/stories/helper.tsx +++ b/stories/helper.tsx @@ -5,7 +5,7 @@ import startOfMinute from "date-fns/startOfMinute"; import startOfDay from "date-fns/startOfDay"; import endOfDay from "date-fns/endOfDay"; -import { EmptyTask, Task, TaskOrEmpty } from "../src"; +import { EditableTaskInfo, EmptyTask, Task, TaskOrEmpty } from "../src"; const dateFormat = "dd/MM/yyyy HH:mm"; @@ -239,11 +239,12 @@ export const onAddTask = (parentTask: Task) => { return Promise.resolve(nextTask); }; -export const onEditTask = (task: TaskOrEmpty) => { +export const onEditTask = (editableTaskInfo: EditableTaskInfo) => { + const { task, start, end } = editableTaskInfo; const taskFields = getTaskFields({ name: task.name, - start: task.type === "empty" ? null : (task as Task).start, - end: task.type === "empty" ? null : (task as Task).end + start: task.type === "empty" && !task.name ? null : start, + end: task.type === "empty" && !task.name ? null : end }); let nextTask: TaskOrEmpty; diff --git a/yarn.lock b/yarn.lock index 3ed43dd1a..bfb43e6e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1681,10 +1681,10 @@ resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@esbuild/darwin-x64@0.18.20": +"@esbuild/darwin-arm64@0.18.20": version "0.18.20" - resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz" - integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz" + integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== "@eslint-community/eslint-utils@^4.2.0": version "4.4.1" @@ -3991,9 +3991,9 @@ magic-string "^0.27.0" react-refresh "^0.14.0" -"@vitejs/plugin-react@4.0.4": +"@vitejs/plugin-react@^4.0.4": version "4.0.4" - resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz" + resolved "https://mirrors.tencent.com/npm/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz" integrity sha512-7wU921ABnNYkETiMaZy7XqpueMnpu5VxvVps13MjmCo+utBdD79sZzrApHawHtVX66cCJQQTXFcjH0y9dSUK8g== dependencies: "@babel/core" "^7.22.9" @@ -7266,6 +7266,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"