react-weekly-planning 1.0.42 → 1.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -3
- package/dist/components/CalendarForWeek.js +6 -1
- package/dist/components/CalendarForday.js +1 -1
- package/dist/components/TaskContainer/TaskVirtual.js +3 -3
- package/dist/components/VirtualGroupCell.js +33 -0
- package/dist/components/VirtualGroupRow.js +8 -8
- package/dist/components/VirtualGroupRowDay.js +7 -7
- package/dist/components/index.js +1 -1
- package/dist/components/style.css +112 -108
- package/dist/contexts/CalendarTaskContext.js +1 -1
- package/dist/hooks/useCalendarDateState.js +3 -4
- package/dist/hooks/useCalendarTask.js +5 -1
- package/dist/hooks/useContainerScroll.js +87 -8
- package/dist/hooks/useGridContainer.js +10 -10
- package/dist/hooks/useIntersectionObserver.js +8 -2
- package/dist/index.js +1 -0
- package/dist/lib/utils.js +5 -5
- package/dist/types/components/TaskContainer/TaskVirtual.d.ts +4 -1
- package/dist/types/components/VirtualGroupCell.d.ts +20 -0
- package/dist/types/definitions/index.d.ts +1 -0
- package/dist/types/hooks/useCalendarDateState.d.ts +13 -3
- package/dist/types/hooks/useCalendarTask.d.ts +1 -0
- package/dist/types/hooks/useContainerScroll.d.ts +6 -2
- package/dist/types/hooks/useIntersectionObserver.d.ts +4 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lib/utils.d.ts +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -215,7 +215,7 @@ Use this hook within any component nested under the provider to access the task
|
|
|
215
215
|
| `getTask` | `(hash: string, taskId: string) => Task \| undefined` | Finds a specific task by ID. |
|
|
216
216
|
| `updateTask` | `(hash: string, taskId: string, updatedTask: Partial<Task>) => void` | Updates an existing task's properties. |
|
|
217
217
|
| `deleteTask` | `(hash: string, taskId: string) => void` | Removes a task from the store. |
|
|
218
|
-
| `isValidTask` | `(task: Task) => boolean` |
|
|
218
|
+
| `isValidTask` | `(task: Task) => boolean` | Verifies if a task has not yet expired. |
|
|
219
219
|
| `cleanExpiredTasks` | `() => void` | Removes all tasks that have passed their `taskExpiryDate`. |
|
|
220
220
|
| `cleanExpiredTasksByHash` | `(hash: string) => void` | Removes expired tasks within a specific hash bucket. |
|
|
221
221
|
| `hashScope` | `"week" \| "group" \| "day"` | The active hashing strategy. |
|
|
@@ -314,7 +314,7 @@ The library uses "hashes" to bucket tasks efficiently. When calling `getTasks(ha
|
|
|
314
314
|
|
|
315
315
|
- **Manual Rendering**: Use `getTasks(hash)` to retrieve only the tasks relevant to the current view.
|
|
316
316
|
- **CRUD Operations**: Use `addTask`, `updateTask`, and `deleteTask` to modify the store. The UI will re-render automatically thanks to the context.
|
|
317
|
-
- **Validation**: Use `isValidTask(task)` to verify if a task
|
|
317
|
+
- **Validation**: Use `isValidTask(task)` to verify if a task has not yet expired before displaying it, or rely on `cleanExpiredTasks()` to prune the store.
|
|
318
318
|
- **Performance**: Accessing tasks by hash is highly optimized. Avoid looping through the entire `tasks.buckets` manually if possible.
|
|
319
319
|
|
|
320
320
|
---
|
|
@@ -425,6 +425,33 @@ to create an organization that truly reflects you.
|
|
|
425
425
|
console.log(hashes.day); // "0/group-1/2"
|
|
426
426
|
```
|
|
427
427
|
|
|
428
|
+
### `calculateWeekDifference`
|
|
429
|
+
|
|
430
|
+
- **Description**: Calculates the week difference in days between a selected date and the current date, normalized to Sundays.
|
|
431
|
+
- **Parameters**:
|
|
432
|
+
- `dateSelectionnee` (Date): The date to compare.
|
|
433
|
+
- `timeZone` (string, optional): The timezone to use for the comparison.
|
|
434
|
+
- **Returns**: The difference in days (e.g., 0, 7, -7, 14, -14...).
|
|
435
|
+
|
|
436
|
+
**Example**:
|
|
437
|
+
```javascript
|
|
438
|
+
import { calculateWeekDifference } from "react-weekly-planning";
|
|
439
|
+
const diff = calculateWeekDifference(new Date());
|
|
440
|
+
console.log(diff); // Logs 0 if it's the current week
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### `getUniqueId`
|
|
444
|
+
|
|
445
|
+
- **Description**: Generates a unique identifier (UUID v4).
|
|
446
|
+
- **Returns**: A unique ID string.
|
|
447
|
+
|
|
448
|
+
**Example**:
|
|
449
|
+
```javascript
|
|
450
|
+
import { getUniqueId } from "react-weekly-planning";
|
|
451
|
+
const id = getUniqueId();
|
|
452
|
+
console.log(id); // Logs a unique UUID
|
|
453
|
+
```
|
|
454
|
+
|
|
428
455
|
### `getNewTaskForDropOrPaste`
|
|
429
456
|
|
|
430
457
|
- **Description**: Utility function to generate a new task correctly positioned when dropping or pasting it in a custom calendar context. It reads the dragged task information from the browser's `sessionStorage` and calculates the new start/end dates based on the drop target.
|
|
@@ -461,5 +488,37 @@ to create an organization that truly reflects you.
|
|
|
461
488
|
};
|
|
462
489
|
```
|
|
463
490
|
|
|
464
|
-
|
|
491
|
+
### `useIntersectionObserver`
|
|
465
492
|
|
|
493
|
+
- **Description**: Utility hook designed to help virtualize scrolling components. It leverages the Intersection Observer API to detect when an element enters or leaves the viewport, allowing you to mount or unmount heavy DOM elements dynamically for better performance in long lists or large grids.
|
|
494
|
+
- **Parameters**:
|
|
495
|
+
- `ref` (React.RefObject): The ref assigned to the DOM element you want to observe.
|
|
496
|
+
- `options` (IntersectionObserverInit, optional): Configuration object (e.g., `rootMargin`, `threshold`).
|
|
497
|
+
- **Returns**: An object containing `{ entry, height }`. `entry` is the `IntersectionObserverEntry` which can be used to check `isIntersecting`. `height` provides the element's cached height when it is unmounted to maintain scroll position consistency.
|
|
498
|
+
|
|
499
|
+
**Example**:
|
|
500
|
+
```tsx
|
|
501
|
+
import React, { useRef } from "react";
|
|
502
|
+
import { useIntersectionObserver } from "react-pweekly-planning";
|
|
503
|
+
|
|
504
|
+
const VirtualItem = ({ children }) => {
|
|
505
|
+
const ref = useRef(null);
|
|
506
|
+
const { entry, height } = useIntersectionObserver(ref, {
|
|
507
|
+
rootMargin: "600px",
|
|
508
|
+
threshold: 0
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const isVisible = !!entry?.isIntersecting;
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
<div
|
|
515
|
+
ref={ref}
|
|
516
|
+
style={{ minHeight: isVisible ? "auto" : `${height}px` }}
|
|
517
|
+
>
|
|
518
|
+
{isVisible ? children : null}
|
|
519
|
+
</div>
|
|
520
|
+
);
|
|
521
|
+
};
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
@@ -13,7 +13,12 @@ const CalendarForWeek = (props) => {
|
|
|
13
13
|
const { dailyHours, weekDays } = useCalendarDateState(props.date, props.weekOffset, props.timeZone);
|
|
14
14
|
const memoizedHeader = useMemo(() => (_jsx("div", { className: "planningCalendarHeader", children: _jsxs("div", { className: `planningCalendarRow ${props.rowsClassName}`, style: Object.assign(Object.assign({}, theadTrStyle), props.rowsStyle), children: [_jsx("div", { className: `dayTh ${props.groupsColsClassName}`, style: Object.assign({}, props.groupsColsStyle), children: _jsx(GroupsHeadContainer, { className: `${props.groupHeadContainerClassName}`, style: props.groupHeadContainerStyle, groupsHeadRender: props.groupsHeadRender }) }), weekDays.map((day, i) => (_jsx("div", { className: `dayCol ${props.daysColsClassName}`, style: Object.assign({}, props.daysColsStyle), children: _jsx(DayContainer, { style: props.dayStyle, className: props.dayClassName, dayIndex: i, dayRender: props.dayRender, day: day.day, dayOfTheMonth: day.dayOfTheMonth, dayMonth: day.dayMonth, dayYear: day.dayYear }) }, i)))] }, "header") })), [weekDays, props.rowsClassName, props.rowsStyle, props.groupsColsClassName, props.groupsColsStyle, props.groupHeadContainerClassName, props.groupHeadContainerStyle, props.groupsHeadRender, props.daysColsClassName, props.daysColsStyle, props.dayStyle, props.dayClassName, props.dayRender]);
|
|
15
15
|
const offset = useMemo(() => updateOffsetWithDateCalendar(props.date), [props.date]);
|
|
16
|
-
return (_jsx("div", { className: "calendarForWeek", style: { position: "relative" }, children: _jsxs("div", { className: `planningCalendar ${props.className}`, style: Object.assign({}, props.style), children: [memoizedHeader, _jsx("div", { className: "planningCalendarBody",
|
|
16
|
+
return (_jsx("div", { className: "calendarForWeek", style: { position: "relative" }, children: _jsxs("div", { className: `planningCalendar ${props.className}`, style: Object.assign({}, props.style), children: [memoizedHeader, _jsx("div", { className: "planningCalendarBody", style: {
|
|
17
|
+
// position: "absolute",
|
|
18
|
+
// width: "100%",
|
|
19
|
+
// // top: `${36 + 5}px`,
|
|
20
|
+
// top: `${36 + 5 + ((65 + 4) * Math.max(rowSliceIndexStart, 0))}px`,
|
|
21
|
+
}, children: (_a = props.groups) === null || _a === void 0 ? void 0 : _a.map((group, i) => {
|
|
17
22
|
var _a;
|
|
18
23
|
const scope = hashScope || "week";
|
|
19
24
|
const groupHash = getHash(offset, group.id);
|
|
@@ -9,7 +9,7 @@ function CalendarForDay(props) {
|
|
|
9
9
|
const { dailyHours, weekDays } = useCalendarDateState(props.date, props.weekOffset, props.timeZone);
|
|
10
10
|
const { getTasks, isValidTask, addTask, deleteTask, updateTask, getTask, hashScope, tasks } = useCalendarTaskContext();
|
|
11
11
|
const currentDay = weekDays[props.dayOffset || 0];
|
|
12
|
-
const memoizedHeader = useMemo(() => (currentDay ? (_jsx(DayContainer, { style: props.dayStyle, className: props.dayClassName, dayIndex: props.dayOffset || 0, dayRender: props.dayRender, day: currentDay.day, dayOfTheMonth: currentDay.dayOfTheMonth, dayMonth: currentDay.dayMonth, dayYear: currentDay.dayYear })) : null), [
|
|
12
|
+
const memoizedHeader = useMemo(() => (currentDay ? (_jsx(DayContainer, { style: props.dayStyle, className: props.dayClassName, dayIndex: props.dayOffset || 0, dayRender: props.dayRender, day: currentDay.day, dayOfTheMonth: currentDay.dayOfTheMonth, dayMonth: currentDay.dayMonth, dayYear: currentDay.dayYear })) : null), [weekDays, props.rowsClassName, props.rowsStyle, props.groupsColsClassName, props.groupsColsStyle, props.groupHeadContainerClassName, props.groupHeadContainerStyle, props.groupsHeadRender, props.daysColsClassName, props.daysColsStyle, props.dayStyle, props.dayClassName, props.dayRender]);
|
|
13
13
|
return (_jsxs("div", { className: `CalendarTableForDay ${props.className}`, style: Object.assign({ position: "relative" }, props.style), children: [memoizedHeader, _jsx("div", { className: `CalendarTableForDayTasksContainer`, children: props.groups.map((group, i) => (_jsx(VirtualGroupRowDay, { group: group, i: i, props: props, getTasks: getTasks, isValidTask: isValidTask, addTask: addTask, deleteTask: deleteTask, updateTask: updateTask, getTask: getTask, dailyHours: dailyHours, dayOffset: props.dayOffset || 0, hashScope: hashScope || "day", tasks: tasks }, `${group.id}-${i}`))) })] }));
|
|
14
14
|
}
|
|
15
15
|
export default memo(CalendarForDay, (prevProps, nextProps) => {
|
|
@@ -4,15 +4,15 @@ import { useIntersectionObserver } from "../../hooks/useIntersectionObserver";
|
|
|
4
4
|
import TaskContainer from ".";
|
|
5
5
|
const TaskVirtual = (props) => {
|
|
6
6
|
const ref = useRef(null);
|
|
7
|
-
const entry = useIntersectionObserver(ref, {
|
|
7
|
+
const { entry, height } = useIntersectionObserver(ref, {
|
|
8
8
|
rootMargin: "200px", // Margin to pre-render tasks before they appear
|
|
9
9
|
threshold: 0,
|
|
10
10
|
});
|
|
11
11
|
const isVisible = !!(entry === null || entry === void 0 ? void 0 : entry.isIntersecting);
|
|
12
12
|
return (_jsx("div", { ref: ref, style: {
|
|
13
|
-
minHeight: isVisible ? "auto" :
|
|
13
|
+
minHeight: isVisible ? "auto" : `${height}px`,
|
|
14
14
|
width: "100%",
|
|
15
15
|
marginBottom: "4px"
|
|
16
|
-
}, children: isVisible ? (_jsx(TaskContainer, Object.assign({}, props))) : (_jsx("div", { style: { height:
|
|
16
|
+
}, "data-index": props.index, children: isVisible ? (_jsx(TaskContainer, Object.assign({}, props))) : (_jsx("div", { style: { height: `${height}px`, backgroundColor: "rgba(200, 200, 200, 0.1)", borderRadius: "4px" } })) }));
|
|
17
17
|
};
|
|
18
18
|
export default React.memo(TaskVirtual);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import TaskVirtual from "./TaskContainer/TaskVirtual";
|
|
4
|
+
import AddTask from "./AddTask";
|
|
5
|
+
import { getNewTaskForDropOrPaste, getUniqueId, } from "../lib/utils";
|
|
6
|
+
import { memo } from "react";
|
|
7
|
+
const VirtualGroupCell = ({ group, i, props, getTasks, isValidTask, addTask, deleteTask, getTask, day, hash, tasks, tasksStore, start, end }) => {
|
|
8
|
+
return (_jsx("div", { onDragOver: (e) => e.preventDefault(), onDrop: (event) => {
|
|
9
|
+
const dropInfo = getNewTaskForDropOrPaste(day.positionDay, group.id, getTask, hash);
|
|
10
|
+
if (!dropInfo)
|
|
11
|
+
return;
|
|
12
|
+
if (props.drop === "copy") {
|
|
13
|
+
addTask(Object.assign(Object.assign({}, dropInfo.newTask), { id: uuidv4() }));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
deleteTask(dropInfo.newTask.draghash, dropInfo.newTask.id);
|
|
17
|
+
addTask(Object.assign(Object.assign({}, dropInfo.newTask), { id: getUniqueId() }));
|
|
18
|
+
}, id: `col-${group.id}day-i`, className: `dayCol ${props.dayColsClassName}`, style: Object.assign(Object.assign({}, props.dayColsStyle), { minHeight: '60px' }), children: _jsxs("div", { style: {
|
|
19
|
+
display: "flex",
|
|
20
|
+
width: "100%",
|
|
21
|
+
height: "100%",
|
|
22
|
+
flexDirection: "column",
|
|
23
|
+
padding: "5px",
|
|
24
|
+
}, children: [tasks.slice(start, end).map((task) => {
|
|
25
|
+
if (task.dayIndex === day.positionDay &&
|
|
26
|
+
task.groupId === group.id && isValidTask(task)) {
|
|
27
|
+
return (_jsx(TaskVirtual, { handleDragTask: props.handleDragTask, taskRender: props.taskRender, handleDragTaskEnd: props.handleDragTaskEnd, style: props.taskContainerStyle, className: `${props.taskContainerClassName}`, currentTask: task, handleClickTask: props.handleClickTask }, `${task.id} task`));
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
return null;
|
|
31
|
+
}), _jsx(AddTask, { addTaskStyle: props.addTaskStyle, addTaskClassName: props.addTaskClassName, currentGroup: group, dayInfo: day, addTaskRender: props.addTaskRender, handleAddTask: props.handleAddTask })] }) }, `col-${group.id}day-i${day.positionDay}`));
|
|
32
|
+
};
|
|
33
|
+
export default memo(VirtualGroupCell);
|
|
@@ -5,11 +5,11 @@ import { useIntersectionObserver } from "../hooks/useIntersectionObserver";
|
|
|
5
5
|
import GroupContainer from "./GroupContainer";
|
|
6
6
|
import TaskVirtual from "./TaskContainer/TaskVirtual";
|
|
7
7
|
import AddTask from "./AddTask";
|
|
8
|
-
import { getHash, getNewTaskForDropOrPaste,
|
|
8
|
+
import { getHash, getNewTaskForDropOrPaste, getUniqueId, updateOffsetWithDateCalendar, } from "../lib/utils";
|
|
9
9
|
import { groupTdStyle } from "../lib/slyles";
|
|
10
|
-
const VirtualGroupRow = ({ group, i, props, getTasks, isValidTask, addTask, deleteTask, getTask, dailyHours, hashScope, tasks, sumHoursByGroupsCount }) => {
|
|
10
|
+
const VirtualGroupRow = ({ group, i, props, getTasks, isValidTask, addTask, deleteTask, getTask, dailyHours, hashScope, tasks, sumHoursByGroupsCount, }) => {
|
|
11
11
|
const ref = useRef(null);
|
|
12
|
-
const entry = useIntersectionObserver(ref, {
|
|
12
|
+
const { entry, height } = useIntersectionObserver(ref, {
|
|
13
13
|
rootMargin: "600px",
|
|
14
14
|
threshold: 0,
|
|
15
15
|
});
|
|
@@ -28,7 +28,7 @@ const VirtualGroupRow = ({ group, i, props, getTasks, isValidTask, addTask, dele
|
|
|
28
28
|
};
|
|
29
29
|
});
|
|
30
30
|
}, [isVisible, offset, group.id, dailyHours, hashScope, getTasks, isValidTask, tasks]);
|
|
31
|
-
return (_jsx("div", { ref: ref, className: `planningCalendarRow ${props.rowsClassName}`, style: Object.assign(Object.assign({}, props.rowsStyle), { minHeight: isVisible ? "auto" :
|
|
31
|
+
return (_jsx("div", { ref: ref, className: `planningCalendarRow ${props.rowsClassName}`, style: Object.assign(Object.assign({}, props.rowsStyle), { minHeight: isVisible ? "auto" : `${height}px` }), "data-index": i, children: isVisible ? (_jsxs(_Fragment, { children: [_jsx("div", { className: `groupCol ${props.groupsColsClassName}`, style: Object.assign(Object.assign({}, groupTdStyle), props.groupsColsStyle), children: _jsx(GroupContainer, { style: props.groupContainerStyle, className: props.groupContainerClassName, groupRender: props.groupRender, currentGroup: group, handleClickGroup: props.handleClickGroup }) }, group.id), cellData.map((cell) => {
|
|
32
32
|
return (_jsx("div", { onDragOver: (e) => e.preventDefault(), onDrop: (event) => {
|
|
33
33
|
const dropInfo = getNewTaskForDropOrPaste(cell.positionDay, group.id, getTask, cell.hash);
|
|
34
34
|
if (!dropInfo)
|
|
@@ -38,22 +38,22 @@ const VirtualGroupRow = ({ group, i, props, getTasks, isValidTask, addTask, dele
|
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
deleteTask(dropInfo.newTask.draghash, dropInfo.newTask.id);
|
|
41
|
-
addTask(Object.assign(Object.assign({}, dropInfo.newTask), { id:
|
|
41
|
+
addTask(Object.assign(Object.assign({}, dropInfo.newTask), { id: getUniqueId() }));
|
|
42
42
|
}, id: `col-${group.id}day-i`, className: `dayCol ${props.dayColsClassName}`, style: props.dayColsStyle, children: _jsxs("div", { style: {
|
|
43
43
|
display: "flex",
|
|
44
44
|
width: "100%",
|
|
45
45
|
height: "100%",
|
|
46
46
|
flexDirection: "column",
|
|
47
47
|
padding: "5px",
|
|
48
|
-
}, children: [cell.tasks.map((task) => {
|
|
48
|
+
}, children: [cell.tasks.map((task, index) => {
|
|
49
49
|
if (task.dayIndex === cell.positionDay &&
|
|
50
50
|
task.groupId === group.id && isValidTask(task)) {
|
|
51
|
-
return (_jsx(TaskVirtual, { handleDragTask: props.handleDragTask, taskRender: props.taskRender, handleDragTaskEnd: props.handleDragTaskEnd, style: props.taskContainerStyle, className: `${props.taskContainerClassName}`, currentTask: task, handleClickTask: props.handleClickTask }, `${task.id} task`));
|
|
51
|
+
return (_jsx(TaskVirtual, { index: index, handleDragTask: props.handleDragTask, taskRender: props.taskRender, handleDragTaskEnd: props.handleDragTaskEnd, style: props.taskContainerStyle, className: `${props.taskContainerClassName}`, currentTask: task, handleClickTask: props.handleClickTask }, `${task.id} task`));
|
|
52
52
|
}
|
|
53
53
|
else
|
|
54
54
|
return null;
|
|
55
55
|
}), _jsx(AddTask, { addTaskStyle: props.addTaskStyle, addTaskClassName: props.addTaskClassName, currentGroup: group, dayInfo: dailyHours[cell.positionDay], addTaskRender: props.addTaskRender, handleAddTask: props.handleAddTask })] }) }, `col-${group.id}day-i${cell.positionDay}`));
|
|
56
|
-
})] })) : (_jsx("div", { style: { height:
|
|
56
|
+
})] })) : (_jsx("div", { style: { height: `${height}px`, width: "100%" } })) }));
|
|
57
57
|
};
|
|
58
58
|
export default memo(VirtualGroupRow, (prev, next) => {
|
|
59
59
|
return (prev.group.id === next.group.id &&
|
|
@@ -4,10 +4,10 @@ import { useIntersectionObserver } from "../hooks/useIntersectionObserver";
|
|
|
4
4
|
import GroupContainer from "./GroupContainer";
|
|
5
5
|
import TaskVirtual from "./TaskContainer/TaskVirtual";
|
|
6
6
|
import AddTask from "./AddTask";
|
|
7
|
-
import { getHash, getNewTaskForDropOrPaste,
|
|
7
|
+
import { getHash, getNewTaskForDropOrPaste, getUniqueId, updateOffsetWithDateCalendar, } from "../lib/utils";
|
|
8
8
|
const VirtualGroupRowDay = ({ group, i, props, getTasks, isValidTask, addTask, deleteTask, updateTask, getTask, dailyHours, dayOffset, hashScope, tasks, }) => {
|
|
9
9
|
const ref = useRef(null);
|
|
10
|
-
const entry = useIntersectionObserver(ref, {
|
|
10
|
+
const { entry, height } = useIntersectionObserver(ref, {
|
|
11
11
|
rootMargin: "600px",
|
|
12
12
|
threshold: 0,
|
|
13
13
|
});
|
|
@@ -19,26 +19,26 @@ const VirtualGroupRowDay = ({ group, i, props, getTasks, isValidTask, addTask, d
|
|
|
19
19
|
const handleDragOver = (event) => {
|
|
20
20
|
event.preventDefault();
|
|
21
21
|
};
|
|
22
|
-
return (_jsx("div", { ref: ref, style: Object.assign({ width: "100%", height: "auto", padding: "5px", borderBottom: "1.5px solid #0f52737e", borderRight: "0.74px solid rgba(198, 219, 225, 0.68)", borderLeft: "0.74px solid rgba(198, 219, 225, 0.68)", minHeight: isVisible ? "auto" :
|
|
22
|
+
return (_jsx("div", { ref: ref, style: Object.assign({ width: "100%", height: "auto", padding: "5px", borderBottom: "1.5px solid #0f52737e", borderRight: "0.74px solid rgba(198, 219, 225, 0.68)", borderLeft: "0.74px solid rgba(198, 219, 225, 0.68)", minHeight: isVisible ? "auto" : `${height}px` }, props.rowsStyle), className: `CalendarTableForDayRow ${props.rowsClassName}`, children: isVisible ? (_jsxs(_Fragment, { children: [_jsx("div", { style: Object.assign({ width: "auto", height: "auto" }, props.groupsColsStyle), className: props.groupsColsClassName, children: _jsx(GroupContainer, { style: props.groupContainerStyle, className: props.groupContainerClassName, groupRender: props.groupRender, currentGroup: group, handleClickGroup: props.handleClickGroup }) }), _jsxs("div", { className: "CalendarTableForDayGroupTasks", onDragOver: handleDragOver, onDrop: (event) => {
|
|
23
23
|
if (!cellTasks)
|
|
24
24
|
return;
|
|
25
25
|
const dropInfo = getNewTaskForDropOrPaste(currentDailyHours.positionDay, group.id, getTask, hash[hashScope]);
|
|
26
26
|
if (!dropInfo)
|
|
27
27
|
return;
|
|
28
28
|
if (props.drop === "copy") {
|
|
29
|
-
addTask(Object.assign(Object.assign({}, dropInfo.newTask), { id:
|
|
29
|
+
addTask(Object.assign(Object.assign({}, dropInfo.newTask), { id: getUniqueId() }));
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
updateTask(hash[hashScope], dropInfo.newTask.id, dropInfo.newTask);
|
|
33
|
-
}, children: [cellTasks.map((task) => {
|
|
33
|
+
}, children: [cellTasks.map((task, index) => {
|
|
34
34
|
if (task.dayIndex === dayOffset &&
|
|
35
35
|
task.groupId === group.id &&
|
|
36
36
|
isValidTask(task)) {
|
|
37
|
-
return (_jsx(TaskVirtual, { handleDragTask: props.handleDragTask, taskRender: props.taskRender, handleDragTaskEnd: props.handleDragTaskEnd, style: props.taskContainerStyle, className: `${props.taskContainerClassName}`, currentTask: task, handleClickTask: props.handleClickTask }, `${task.id} task`));
|
|
37
|
+
return (_jsx(TaskVirtual, { index: index, handleDragTask: props.handleDragTask, taskRender: props.taskRender, handleDragTaskEnd: props.handleDragTaskEnd, style: props.taskContainerStyle, className: `${props.taskContainerClassName}`, currentTask: task, handleClickTask: props.handleClickTask }, `${task.id} task`));
|
|
38
38
|
}
|
|
39
39
|
else
|
|
40
40
|
return null;
|
|
41
|
-
}), _jsx(AddTask, { addTaskStyle: props.addTaskStyle, addTaskClassName: props.addTaskClassName, currentGroup: group, dayInfo: currentDailyHours, addTaskRender: props.addTaskRender, handleAddTask: props.handleAddTask })] })] })) : (_jsx("div", { style: { height:
|
|
41
|
+
}), _jsx(AddTask, { addTaskStyle: props.addTaskStyle, addTaskClassName: props.addTaskClassName, currentGroup: group, dayInfo: currentDailyHours, addTaskRender: props.addTaskRender, handleAddTask: props.handleAddTask })] })] })) : (_jsx("div", { style: { height: `${height}px`, width: "100%" } })) }));
|
|
42
42
|
};
|
|
43
43
|
export default memo(VirtualGroupRowDay, (prev, next) => {
|
|
44
44
|
return (prev.group.id === next.group.id &&
|
package/dist/components/index.js
CHANGED
|
@@ -65,6 +65,6 @@ import CalendarTaskContextProvider from "../contexts/CalendarTaskContext";
|
|
|
65
65
|
* @param {string} [props.hoursColsClassName] - Additional class names for the hours columns.
|
|
66
66
|
*/
|
|
67
67
|
const Calendar = (props) => {
|
|
68
|
-
return (_jsx(CalendarTaskContextProvider, { hashScope: "
|
|
68
|
+
return (_jsx(CalendarTaskContextProvider, { hashScope: "day", children: props.scope === "day" ? _jsx(CalendarForDay, Object.assign({}, props)) : _jsx(CalendarForWeek, Object.assign({}, props)) }));
|
|
69
69
|
};
|
|
70
70
|
export default Calendar;
|
|
@@ -1,167 +1,171 @@
|
|
|
1
1
|
.addPlanStyle {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
cursor: pointer;
|
|
3
|
+
display: flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
flex: 1;
|
|
7
|
+
background-color: #f2f8fb;
|
|
8
|
+
opacity: 0;
|
|
9
|
+
border-radius: 5px;
|
|
10
|
+
color: #0f5173;
|
|
11
|
+
transition: background-color 0.3s, opacity 0.3s;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
.addPlanStyle:hover {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
background-color: #c6dbe159;
|
|
16
|
+
opacity: 1;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.taskContainer {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
box-shadow: 0px 7px 18px 0px rgba(15, 105, 115, 0.03),
|
|
21
|
+
0px 6px 10px 0px rgba(15, 105, 115, 0.04),
|
|
22
|
+
0px 3px 5px 0px rgba(15, 105, 115, 0.05);
|
|
23
|
+
display: flex;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
column-gap: 10px;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
height: auto;
|
|
28
|
+
border-left: 5px solid #457993;
|
|
29
|
+
background-color: #f4ffff;
|
|
30
|
+
color: #457993;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
border-radius: 5px;
|
|
33
|
+
margin-bottom: 5px;
|
|
34
|
+
font-size: 13px;
|
|
35
|
+
overflow: auto;
|
|
36
|
+
padding: 2px;
|
|
37
|
+
z-index: 10;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
.tasklabel {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
color: #55585d;
|
|
42
|
+
font-size: 12px;
|
|
43
|
+
line-height: 16px;
|
|
44
|
+
font-weight: 400;
|
|
45
|
+
margin-left: 5px;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
.taskhour {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
font-size: 12px;
|
|
50
|
+
line-height: 16px;
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
margin-left: 5px;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.planningCalendar {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
width: 100%;
|
|
57
|
+
border-radius: 0.5rem;
|
|
58
|
+
z-index: 10;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
border: 0.74px solid rgba(198, 219, 225, 0.68);
|
|
62
|
+
border-bottom: none;
|
|
63
|
+
background-color: white;
|
|
64
64
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
.planningCalendarHeader {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
display: flex;
|
|
69
|
+
width: 100%;
|
|
70
|
+
z-index: 200;
|
|
71
|
+
border-bottom: 1.5px solid #0f52737e;
|
|
72
|
+
background-color: #fff;
|
|
73
|
+
position: sticky;
|
|
74
|
+
top: 0px;
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
.planningCalendarRow {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
display: flex;
|
|
79
|
+
width: 100%;
|
|
80
|
+
border-bottom: 1.5px solid #0f52737e;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
.planningCalendarRow:last-child {
|
|
82
|
-
|
|
84
|
+
border-bottom: none;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
.planningCalendar .dayTh,
|
|
86
88
|
.planningCalendar .groupCol {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
flex: 0 0 150px;
|
|
90
|
+
color: #0f5173;
|
|
91
|
+
padding-left: 5px;
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
.planningCalendar .dayCol {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
flex: 1;
|
|
98
|
+
border-left: 0.74px solid rgba(198, 219, 225, 0.68);
|
|
99
|
+
min-width: 0;
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: column;
|
|
102
|
+
justify-content: center;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
.planningCalendar .totalTh,
|
|
104
106
|
.planningCalendar .totalCol {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
flex: 0 0 50px;
|
|
108
|
+
color: #0f5173;
|
|
109
|
+
text-align: right;
|
|
110
|
+
padding-right: 5px;
|
|
111
|
+
border-left: 0.74px solid rgba(198, 219, 225, 0.68);
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: flex-end;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
.CalendarTableForDay {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
display: flex;
|
|
119
|
+
gap: 10px;
|
|
120
|
+
width: 100vw;
|
|
121
|
+
height: auto;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
.CalendarTableForDayTasksContainer {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
flex: 1;
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
height: auto;
|
|
129
|
+
padding: 10px;
|
|
130
|
+
position: relative;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
.CalendarTableForDayRow {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
display: flex !important;
|
|
135
|
+
flex-direction: row !important;
|
|
136
|
+
gap: 15px;
|
|
137
|
+
align-items: flex-start;
|
|
138
|
+
padding: 15px !important;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
.CalendarTableForDayGroupTasks {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
flex: 1;
|
|
143
|
+
display: grid;
|
|
144
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
145
|
+
gap: 10px;
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
.CalendarTableForDayGroupTasks .taskContainer,
|
|
147
149
|
.CalendarTableForDayGroupTasks .addPlanStyle {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
margin-bottom: 0px;
|
|
151
|
+
height: 100%;
|
|
152
|
+
min-height: 80px;
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
.calendarForWeek
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
.calendarForWeek,
|
|
156
|
+
.calendarForDay {
|
|
157
|
+
display: flex;
|
|
158
|
+
width: 100%;
|
|
159
|
+
flex: 1;
|
|
160
|
+
overflow: auto;
|
|
161
|
+
position: relative;
|
|
159
162
|
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
.calendarForWeek-container
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
.calendarForWeek-container,
|
|
166
|
+
.calendarForDay-container {
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
width: 100%;
|
|
170
|
+
height: 100%;
|
|
167
171
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { createContext, useContext } from "react";
|
|
3
3
|
import { useCalendarTask } from "../hooks/useCalendarTask";
|
|
4
4
|
const CalendarTaskContext = createContext({
|
|
5
|
-
tasks: { buckets: {}, dataLength: 0, taskCache: {} },
|
|
5
|
+
tasks: { buckets: {}, dataLength: 0, taskCache: {}, maxBucketSize: 0 },
|
|
6
6
|
addTask: () => { },
|
|
7
7
|
getTasks: () => [],
|
|
8
8
|
updateTask: () => { },
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMemo } from "react";
|
|
2
2
|
import { calculateWeekDifference, getDateObjectInTimeZone, getDayHourly, getWeekDays, } from "../lib/utils";
|
|
3
3
|
function useCalendarDateState(date, weekOffset, timeZone) {
|
|
4
|
-
|
|
5
|
-
useEffect(() => {
|
|
4
|
+
let calendarDateState = useMemo(() => {
|
|
6
5
|
const weekOffsetByDate = timeZone
|
|
7
6
|
? calculateWeekDifference(getDateObjectInTimeZone(timeZone), timeZone)
|
|
8
7
|
: calculateWeekDifference(date, timeZone);
|
|
@@ -12,7 +11,7 @@ function useCalendarDateState(date, weekOffset, timeZone) {
|
|
|
12
11
|
dailyHours: dailyHours,
|
|
13
12
|
weekDays,
|
|
14
13
|
};
|
|
15
|
-
|
|
14
|
+
return calData;
|
|
16
15
|
}, [date, weekOffset]);
|
|
17
16
|
return Object.assign({}, calendarDateState);
|
|
18
17
|
}
|
|
@@ -2,7 +2,8 @@ import { useEffect, useRef, useState, useMemo } from "react";
|
|
|
2
2
|
import { getHash, updateOffsetWithDateCalendar } from "../lib/utils";
|
|
3
3
|
const STORAGE_KEY = "calendar_tasks";
|
|
4
4
|
export function useCalendarTask(hashScope, timeZone) {
|
|
5
|
-
const tasksRef = useRef({ buckets: {}, dataLength: 0, taskCache: {} });
|
|
5
|
+
const tasksRef = useRef({ buckets: {}, dataLength: 0, taskCache: {}, maxBucketSize: 0 });
|
|
6
|
+
const bucketSizeCountsRef = useRef({});
|
|
6
7
|
const scheduleCleanRef = useRef(null);
|
|
7
8
|
const [render, forceRender] = useState(0);
|
|
8
9
|
const cleanExpiredTasks = () => {
|
|
@@ -110,6 +111,7 @@ export function useCalendarTask(hashScope, timeZone) {
|
|
|
110
111
|
const bucket = tasksRef.current.buckets[hash];
|
|
111
112
|
const index = bucket.list.length;
|
|
112
113
|
bucket.list.push(Object.assign(Object.assign({}, task), { hash }));
|
|
114
|
+
tasksRef.current.maxBucketSize = Math.max(tasksRef.current.maxBucketSize, bucket.list.length);
|
|
113
115
|
bucket.indexMap[task.id] = index;
|
|
114
116
|
bucket.sumOfTaskDuration += task.taskEnd - task.taskStart;
|
|
115
117
|
tasksRef.current.dataLength++;
|
|
@@ -148,6 +150,7 @@ export function useCalendarTask(hashScope, timeZone) {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
bucket.list[index] = Object.assign(Object.assign({}, bucket.list[index]), updatedTask);
|
|
153
|
+
tasksRef.current.maxBucketSize = Math.max(tasksRef.current.maxBucketSize, bucket.list.length);
|
|
151
154
|
saveToStorage();
|
|
152
155
|
scheduleClean();
|
|
153
156
|
forceRender((x) => x + 1);
|
|
@@ -172,6 +175,7 @@ export function useCalendarTask(hashScope, timeZone) {
|
|
|
172
175
|
delete bucket.indexMap[taskId];
|
|
173
176
|
bucket.sumOfTaskDuration -= oldTAskDuration;
|
|
174
177
|
tasksRef.current.dataLength--;
|
|
178
|
+
tasksRef.current.maxBucketSize = Math.max(tasksRef.current.maxBucketSize, bucket.list.length);
|
|
175
179
|
saveToStorage();
|
|
176
180
|
scheduleClean();
|
|
177
181
|
forceRender((x) => x + 1);
|
|
@@ -1,14 +1,93 @@
|
|
|
1
|
-
import { useState, useEffect } from "react";
|
|
2
|
-
export function useContainerScroll(mainContaierRef, cardHeight,
|
|
3
|
-
const [
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
|
2
|
+
export function useContainerScroll(mainContaierRef, cardHeight, gridContaineRef) {
|
|
3
|
+
const [rowSliceIndexStart, setRowSliceIndexStart] = useState(0);
|
|
4
|
+
const [rowSliceIndexEnd, setRowSliceIndexEnd] = useState(15);
|
|
5
|
+
const [taskSliceIdexStart, setTaskSliceIdexStart] = useState(0);
|
|
6
|
+
const [taskSLiceIdexEnd, setTaskSLiceIdexEnd] = useState(30);
|
|
7
|
+
const [rowHeight, setRowHeight] = useState(0);
|
|
8
|
+
const visibleRows = useRef(new Set());
|
|
4
9
|
useEffect(() => {
|
|
5
10
|
const mainContainer = mainContaierRef.current;
|
|
11
|
+
const gridContainer = gridContaineRef.current;
|
|
12
|
+
if (!mainContainer || !gridContainer)
|
|
13
|
+
return;
|
|
14
|
+
const observer = new IntersectionObserver((entries) => {
|
|
15
|
+
entries.forEach((entry) => {
|
|
16
|
+
const row = entry.target;
|
|
17
|
+
const indexAttr = row.getAttribute('data-index');
|
|
18
|
+
if (!indexAttr)
|
|
19
|
+
return;
|
|
20
|
+
const index = parseInt(indexAttr, 10);
|
|
21
|
+
if (entry.isIntersecting) {
|
|
22
|
+
visibleRows.current.add(index);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
visibleRows.current.delete(index);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}, {
|
|
29
|
+
root: mainContainer,
|
|
30
|
+
threshold: 0,
|
|
31
|
+
});
|
|
32
|
+
const observedElements = new Set();
|
|
33
|
+
const observeChildren = () => {
|
|
34
|
+
if (!gridContainer.children)
|
|
35
|
+
return;
|
|
36
|
+
for (let i = 0; i < gridContainer.children.length; i++) {
|
|
37
|
+
const element = gridContainer.children[i];
|
|
38
|
+
if (!observedElements.has(element)) {
|
|
39
|
+
observer.observe(element);
|
|
40
|
+
observedElements.add(element);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const mutationObserver = new MutationObserver(() => {
|
|
45
|
+
observeChildren();
|
|
46
|
+
});
|
|
47
|
+
observeChildren();
|
|
48
|
+
mutationObserver.observe(gridContainer, { childList: true });
|
|
6
49
|
const handleScroll = () => {
|
|
7
|
-
const
|
|
8
|
-
|
|
50
|
+
const scrollTop = mainContainer.scrollTop;
|
|
51
|
+
const containerHeight = mainContainer.clientHeight || 800;
|
|
52
|
+
// ---- CALCUL POUR LES ROWS ----
|
|
53
|
+
const baseRowHeight = 72;
|
|
54
|
+
let startRowIndex = Math.floor(scrollTop / baseRowHeight);
|
|
55
|
+
if (visibleRows.current.size > 0) {
|
|
56
|
+
const firstVisibleRowIndex = Math.min(...Array.from(visibleRows.current));
|
|
57
|
+
if (startRowIndex > firstVisibleRowIndex) {
|
|
58
|
+
startRowIndex = firstVisibleRowIndex;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Calcul de la fin (avec un buffer de vue de 2 éléments)
|
|
62
|
+
const visibleRowsCount = Math.ceil(containerHeight / baseRowHeight);
|
|
63
|
+
const endRowIndex = startRowIndex + visibleRowsCount + 2;
|
|
64
|
+
// ---- CALCUL POUR LES TASKS ----
|
|
65
|
+
// Base théorique pour une task (environ 40px minimum)
|
|
66
|
+
const baseTaskHeight = 40;
|
|
67
|
+
// Attention : Ceci est une estimation globale. Si chaque ROW scrolle individuellement,
|
|
68
|
+
// cette formule devra être ajustée.
|
|
69
|
+
const startTaskIndex = Math.floor(Math.max(0, scrollTop) / baseTaskHeight);
|
|
70
|
+
const visibleTasksCount = Math.ceil(containerHeight / baseTaskHeight);
|
|
71
|
+
const endTaskIndex = startTaskIndex + visibleTasksCount + 5;
|
|
72
|
+
setRowSliceIndexStart(startRowIndex);
|
|
73
|
+
setRowSliceIndexEnd(endRowIndex);
|
|
74
|
+
setTaskSliceIdexStart(startTaskIndex);
|
|
75
|
+
setTaskSLiceIdexEnd(endTaskIndex);
|
|
9
76
|
};
|
|
10
77
|
mainContainer.addEventListener("scroll", handleScroll);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
78
|
+
// Exécution initiale
|
|
79
|
+
handleScroll();
|
|
80
|
+
return () => {
|
|
81
|
+
mainContainer.removeEventListener("scroll", handleScroll);
|
|
82
|
+
observer.disconnect();
|
|
83
|
+
mutationObserver.disconnect();
|
|
84
|
+
};
|
|
85
|
+
}, [mainContaierRef, gridContaineRef]);
|
|
86
|
+
return {
|
|
87
|
+
rowSliceIndexStart,
|
|
88
|
+
rowSliceIndexEnd,
|
|
89
|
+
taskSliceIdexStart,
|
|
90
|
+
taskSLiceIdexEnd,
|
|
91
|
+
rowHeight
|
|
92
|
+
};
|
|
14
93
|
}
|
|
@@ -8,17 +8,17 @@ export const useGridContainer = (gridContaineRef) => {
|
|
|
8
8
|
useEffect(() => {
|
|
9
9
|
if (typeof window === "undefined")
|
|
10
10
|
return;
|
|
11
|
-
//si le parent n'a aucun enfant on sort
|
|
11
|
+
// //si le parent n'a aucun enfant on sort
|
|
12
12
|
if (gridContaineRef.current.hasChildNodes() === false)
|
|
13
13
|
return;
|
|
14
|
-
//la hauteur du premier enfant de la grid
|
|
15
|
-
const cardH = gridContaineRef.current.firstChild.clientHeight;
|
|
16
|
-
if (cardH !== cardCompH) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
14
|
+
// //la hauteur du premier enfant de la grid
|
|
15
|
+
// const cardH = gridContaineRef.current.firstChild.clientHeight;
|
|
16
|
+
// if (cardH !== cardCompH) {
|
|
17
|
+
// const gridW = gridContaineRef.current.clientWidth;
|
|
18
|
+
// const cardW = gridContaineRef.current.firstChild.clientWidth;
|
|
19
|
+
// setCardCompH(cardH);
|
|
20
|
+
// setItemsByLines(Math.floor(gridW / cardW));
|
|
21
|
+
// }
|
|
22
22
|
}, [width]);
|
|
23
|
-
return { cardCompH, cardRef, itemsByLine };
|
|
23
|
+
return { cardCompH: 65, cardRef, itemsByLine: 1 };
|
|
24
24
|
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
export function useIntersectionObserver(elementRef, { threshold = 0, root = null, rootMargin = "0%", freezeOnceVisible = false, } = {}) {
|
|
3
3
|
const [entry, setEntry] = useState();
|
|
4
|
+
const [height, setHeight] = useState(60);
|
|
4
5
|
const frozen = (entry === null || entry === void 0 ? void 0 : entry.isIntersecting) && freezeOnceVisible;
|
|
5
6
|
const updateEntry = ([entry]) => {
|
|
6
7
|
setEntry(entry);
|
|
8
|
+
if (entry.boundingClientRect) {
|
|
9
|
+
if (entry.boundingClientRect.height > 0) {
|
|
10
|
+
setHeight(entry.boundingClientRect.height);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
7
13
|
};
|
|
8
14
|
useEffect(() => {
|
|
9
|
-
const node = elementRef === null || elementRef === void 0 ? void 0 : elementRef.current;
|
|
15
|
+
const node = elementRef === null || elementRef === void 0 ? void 0 : elementRef.current;
|
|
10
16
|
const hasIOSupport = !!window.IntersectionObserver;
|
|
11
17
|
if (!hasIOSupport || frozen || !node)
|
|
12
18
|
return;
|
|
@@ -15,5 +21,5 @@ export function useIntersectionObserver(elementRef, { threshold = 0, root = null
|
|
|
15
21
|
observer.observe(node);
|
|
16
22
|
return () => observer.disconnect();
|
|
17
23
|
}, [elementRef, JSON.stringify(threshold), root, rootMargin, frozen]);
|
|
18
|
-
return entry;
|
|
24
|
+
return { entry, height };
|
|
19
25
|
}
|
package/dist/index.js
CHANGED
package/dist/lib/utils.js
CHANGED
|
@@ -388,7 +388,7 @@ function recurring(ecartDay, task, timeZone) {
|
|
|
388
388
|
newTask.taskExpiryDate = new Date(newTask.taskExpiryDate.getTime() + ecartDay);
|
|
389
389
|
newTask.taskDate = new Date(newTask.taskStart);
|
|
390
390
|
newTask.dayIndex = getArbitraryDateInTimeZone(newTask.taskDate, timeZone).getDay();
|
|
391
|
-
newTask.id =
|
|
391
|
+
newTask.id = getUniqueId();
|
|
392
392
|
return newTask;
|
|
393
393
|
}
|
|
394
394
|
export function recurringTasks(allTasks, task, recurrenceType, occurrences, timeZone) {
|
|
@@ -539,7 +539,7 @@ function updateTaskStartTimeAnEndTime(start, end, calendarOffset, dayIndex, task
|
|
|
539
539
|
const endTime = end + diffDay * DAY_IN_MILLISECONDS;
|
|
540
540
|
return { startTime, endTime };
|
|
541
541
|
}
|
|
542
|
-
export function
|
|
542
|
+
export function getUniqueId() {
|
|
543
543
|
const uid = uuidv4();
|
|
544
544
|
return uid;
|
|
545
545
|
}
|
|
@@ -552,7 +552,7 @@ export function pastTasks(dayInfo, groupId, tasks, taskExpiryDate, timeZone) {
|
|
|
552
552
|
const newTaskStartAndEnd = updateTaskStartTimeAnEndTime(copiedTasktaskStart, copiedTasktaskEnd, updateOffsetWithDateCalendar(dayInfo.day, timeZone), dayInfo.positionDay, copiedTaskDayIndex, timeZone);
|
|
553
553
|
if (!checkDuplicates(previousTasks, newTaskStartAndEnd.startTime, newTaskStartAndEnd.endTime, groupId)) {
|
|
554
554
|
const newTaskDate = new Date(newTaskStartAndEnd.startTime);
|
|
555
|
-
const newTaskId = `${
|
|
555
|
+
const newTaskId = `${getUniqueId()}`;
|
|
556
556
|
const newTask = Object.assign({ taskStart: newTaskStartAndEnd.startTime, taskEnd: newTaskStartAndEnd.endTime, dayIndex: dayInfo.positionDay, id: newTaskId, taskDate: newTaskDate, groupId: groupId, taskExpiryDate: taskExpiryDate }, rest);
|
|
557
557
|
return [...previousTasks, newTask];
|
|
558
558
|
}
|
|
@@ -591,7 +591,7 @@ export function duplicateTasksForPeriod(periodStart, periodEnd, calendarOffset,
|
|
|
591
591
|
const newTaskEnd = taskEnd + ecartDay * DAY_IN_MILLISECONDS;
|
|
592
592
|
const newTaskDate = new Date(taskDate.getTime() + ecartDay * DAY_IN_MILLISECONDS);
|
|
593
593
|
if (!checkDuplicates(allTasks, newTaskStart, newTaskEnd, rest.groupId)) {
|
|
594
|
-
const newTask = Object.assign(Object.assign({}, rest), { taskDate: newTaskDate, taskStart: newTaskStart, taskEnd: newTaskEnd, id:
|
|
594
|
+
const newTask = Object.assign(Object.assign({}, rest), { taskDate: newTaskDate, taskStart: newTaskStart, taskEnd: newTaskEnd, id: getUniqueId() });
|
|
595
595
|
tasks.push(Object.assign(Object.assign({}, newTask), { offset: newOffset }));
|
|
596
596
|
}
|
|
597
597
|
});
|
|
@@ -613,7 +613,7 @@ export function duplicateTaskForPeriod(periodStart, periodEnd, task, ecartDay, g
|
|
|
613
613
|
const newTaskEnd = taskEnd + ecartDay * DAY_IN_MILLISECONDS;
|
|
614
614
|
const newTaskDate = new Date(taskDate.getTime() + ecartDay * DAY_IN_MILLISECONDS);
|
|
615
615
|
if (!checkDuplicates(allTasks, newTaskStart, newTaskEnd, rest.groupId)) {
|
|
616
|
-
const newTask = Object.assign(Object.assign({}, rest), { taskDate: newTaskDate, taskStart: newTaskStart, taskEnd: newTaskEnd, id:
|
|
616
|
+
const newTask = Object.assign(Object.assign({}, rest), { taskDate: newTaskDate, taskStart: newTaskStart, taskEnd: newTaskEnd, id: getUniqueId(), groupId: groupId ? groupId : currentTaskGroupId, taskExpiryDate: taskExpiryDate });
|
|
617
617
|
allTasks.push(Object.assign(Object.assign({}, newTask), { offset: newOffset }));
|
|
618
618
|
}
|
|
619
619
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { TaskContainerPropsType } from "../../definitions";
|
|
3
|
-
|
|
3
|
+
interface TaskVirtualProps extends TaskContainerPropsType {
|
|
4
|
+
index?: number;
|
|
5
|
+
}
|
|
6
|
+
declare const _default: React.MemoExoticComponent<(props: TaskVirtualProps) => import("react/jsx-runtime").JSX.Element>;
|
|
4
7
|
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CalendarTablePropsType, GroupFeildsType, TaskType, TasksStore, TasksType, dayInfoType } from "../definitions";
|
|
2
|
+
import React from "react";
|
|
3
|
+
interface VirtualGroupCellProps {
|
|
4
|
+
group: GroupFeildsType;
|
|
5
|
+
i: number;
|
|
6
|
+
props: CalendarTablePropsType;
|
|
7
|
+
getTasks: (hash: string) => TasksType;
|
|
8
|
+
isValidTask: (task: TaskType) => boolean;
|
|
9
|
+
addTask: (task: TaskType) => void;
|
|
10
|
+
deleteTask: (hash: string, taskId: string) => void;
|
|
11
|
+
getTask: (hash: string, taskId: string) => TaskType | undefined;
|
|
12
|
+
day: dayInfoType;
|
|
13
|
+
hash: string;
|
|
14
|
+
tasks: TasksType;
|
|
15
|
+
tasksStore: TasksStore;
|
|
16
|
+
start: number;
|
|
17
|
+
end: number;
|
|
18
|
+
}
|
|
19
|
+
declare const _default: React.NamedExoticComponent<VirtualGroupCellProps>;
|
|
20
|
+
export default _default;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TimeZone } from "../definitions";
|
|
2
2
|
declare function useCalendarDateState(date: Date, weekOffset: number | undefined, timeZone: TimeZone | undefined): {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
dailyHours: {
|
|
4
|
+
positionDay: number;
|
|
5
|
+
day: Date;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
}[];
|
|
9
|
+
weekDays: {
|
|
10
|
+
day: string;
|
|
11
|
+
dayMonth: string;
|
|
12
|
+
dayYear: number;
|
|
13
|
+
dayOfTheMonth: number;
|
|
14
|
+
}[];
|
|
5
15
|
};
|
|
6
16
|
export default useCalendarDateState;
|
|
@@ -4,6 +4,7 @@ export declare function useCalendarTask(hashScope: "week" | "group" | "day", tim
|
|
|
4
4
|
buckets: Record<string, import("../definitions").TaskBucket>;
|
|
5
5
|
dataLength: number;
|
|
6
6
|
taskCache: Record<string, Task[]>;
|
|
7
|
+
maxBucketSize: number;
|
|
7
8
|
};
|
|
8
9
|
addTask: (task: Task) => void;
|
|
9
10
|
getTasks: (hash: string) => Task[];
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
export declare function useContainerScroll(mainContaierRef: React.RefObject<any>, cardHeight: number,
|
|
2
|
-
|
|
1
|
+
export declare function useContainerScroll(mainContaierRef: React.RefObject<any>, cardHeight: number, gridContaineRef: React.RefObject<any>): {
|
|
2
|
+
rowSliceIndexStart: number;
|
|
3
|
+
rowSliceIndexEnd: number;
|
|
4
|
+
taskSliceIdexStart: number;
|
|
5
|
+
taskSLiceIdexEnd: number;
|
|
6
|
+
rowHeight: number;
|
|
3
7
|
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
interface UseIntersectionObserverProps extends IntersectionObserverInit {
|
|
2
2
|
freezeOnceVisible?: boolean;
|
|
3
3
|
}
|
|
4
|
-
export declare function useIntersectionObserver(elementRef: React.RefObject<Element | null>, { threshold, root, rootMargin, freezeOnceVisible, }?: UseIntersectionObserverProps):
|
|
4
|
+
export declare function useIntersectionObserver(elementRef: React.RefObject<Element | null>, { threshold, root, rootMargin, freezeOnceVisible, }?: UseIntersectionObserverProps): {
|
|
5
|
+
entry: IntersectionObserverEntry | undefined;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
5
8
|
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export declare function selectTask(task: TaskFeildsType): void;
|
|
|
76
76
|
export declare function deSelectTask(task: TaskFeildsType): void;
|
|
77
77
|
export declare function copyTasks(task: TaskFeildsType): void;
|
|
78
78
|
export declare function cutTasks(task: TaskFeildsType, tasks: TaskFeildsType[]): TaskFeildsType[];
|
|
79
|
-
export declare function
|
|
79
|
+
export declare function getUniqueId(): string;
|
|
80
80
|
export declare function pastTasks(dayInfo: dayInfoType, groupId: string, tasks: TaskFeildsType[], taskExpiryDate?: Date, timeZone?: TimeZone): TaskFeildsType[] | undefined;
|
|
81
81
|
export declare function updateTask(tasks: TaskFeildsType[], taskId: string, newtask: TaskFeildsType): TaskFeildsType[];
|
|
82
82
|
export declare function duplicateTasksForPeriod(periodStart: Date, periodEnd: Date, calendarOffset: number, allTasks: TasksType): TasksType;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-weekly-planning",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.44",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/types/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
"./components/GroupContainer": {
|
|
64
64
|
"types": "./dist/types/components/GroupContainer.d.ts",
|
|
65
65
|
"default": "./dist/components/GroupContainer.js"
|
|
66
|
+
},
|
|
67
|
+
"./hooks/useIntersectionObserver": {
|
|
68
|
+
"types": "./dist/types/hooks/useIntersectionObserver.d.ts",
|
|
69
|
+
"default": "./dist/hooks/useIntersectionObserver.js"
|
|
66
70
|
}
|
|
67
71
|
},
|
|
68
72
|
"files": [
|