@kkkarsss/ui 1.5.3 → 1.5.5
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/dist/ui/information/calendar-like/calendar-item-wrapper.js +13 -9
- package/dist/ui/information/calendar-like/calendar-like.js +8 -3
- package/dist/ui/information/calendar-like/calendar-like.stories.js +23 -21
- package/dist/ui/information/calendar-like/calendar-slot.js +5 -3
- package/dist/ui/information/calendar-like/types.d.ts +2 -2
- package/dist/ui/information/calendar-like/utils.js +22 -15
- package/dist/ui/layout/main-page-layout/main-page-layout.js +1 -1
- package/package.json +1 -1
|
@@ -8,9 +8,12 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, on
|
|
|
8
8
|
e.preventDefault();
|
|
9
9
|
e.stopPropagation();
|
|
10
10
|
const startY = e.clientY;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
11
|
+
const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
|
|
12
|
+
const dEnd = task.dueDateEnd instanceof Date ? task.dueDateEnd : new Date(task.dueDateEnd);
|
|
13
|
+
const estimatedTime = (dEnd.getTime() - dStart.getTime()) / (60 * 1000);
|
|
14
|
+
const startHeight = (estimatedTime / 15) * 20;
|
|
15
|
+
const startHour = dStart.getHours();
|
|
16
|
+
const startMinutes = dStart.getMinutes();
|
|
14
17
|
const onMouseMove = (moveEvent) => {
|
|
15
18
|
const deltaY = moveEvent.clientY - startY;
|
|
16
19
|
let newHeight;
|
|
@@ -24,9 +27,9 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, on
|
|
|
24
27
|
else {
|
|
25
28
|
// Resizing top
|
|
26
29
|
const deltaMinutes = Math.round(deltaY / 20) * 15;
|
|
27
|
-
newEstimatedTime = Math.max(15,
|
|
30
|
+
newEstimatedTime = Math.max(15, estimatedTime - deltaMinutes);
|
|
28
31
|
// If we hit min height, don't move the top further down
|
|
29
|
-
const actualDeltaMinutes =
|
|
32
|
+
const actualDeltaMinutes = estimatedTime - newEstimatedTime;
|
|
30
33
|
const totalStartMinutes = startHour * 60 + startMinutes + actualDeltaMinutes;
|
|
31
34
|
newStartHour = Math.floor(totalStartMinutes / 60);
|
|
32
35
|
newStartMinutes = totalStartMinutes % 60;
|
|
@@ -44,13 +47,13 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, on
|
|
|
44
47
|
}
|
|
45
48
|
else {
|
|
46
49
|
const deltaMinutes = Math.round(deltaY / 20) * 15;
|
|
47
|
-
finalEstimatedTime = Math.max(15,
|
|
48
|
-
const actualDeltaMinutes =
|
|
50
|
+
finalEstimatedTime = Math.max(15, estimatedTime - deltaMinutes);
|
|
51
|
+
const actualDeltaMinutes = estimatedTime - finalEstimatedTime;
|
|
49
52
|
const totalStartMinutes = startHour * 60 + startMinutes + actualDeltaMinutes;
|
|
50
53
|
finalStartHour = Math.floor(totalStartMinutes / 60);
|
|
51
54
|
finalStartMinutes = totalStartMinutes % 60;
|
|
52
55
|
}
|
|
53
|
-
if (finalEstimatedTime !==
|
|
56
|
+
if (finalEstimatedTime !== estimatedTime ||
|
|
54
57
|
finalStartHour !== startHour ||
|
|
55
58
|
finalStartMinutes !== startMinutes) {
|
|
56
59
|
onResize(task.id, finalEstimatedTime, finalStartHour, finalStartMinutes);
|
|
@@ -71,7 +74,8 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, on
|
|
|
71
74
|
const displayHeight = preview ? `${(preview.estimatedTime / 15) * 20}px` : position.height;
|
|
72
75
|
let displayTop = position.top;
|
|
73
76
|
if (preview && (preview.startHour !== undefined || preview.startMinutes !== undefined)) {
|
|
74
|
-
const
|
|
77
|
+
const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
|
|
78
|
+
const originalStartTotalMinutes = dStart.getHours() * 60 + dStart.getMinutes();
|
|
75
79
|
const newStartTotalMinutes = preview.startHour * 60 + preview.startMinutes;
|
|
76
80
|
const diffMinutes = newStartTotalMinutes - originalStartTotalMinutes;
|
|
77
81
|
const diffPixels = (diffMinutes / 15) * 20;
|
|
@@ -25,7 +25,10 @@ export const CalendarLike = ({ tasks, slots = DEFAULT_SLOTS, showCurrentTime = t
|
|
|
25
25
|
setDraggingTask(task);
|
|
26
26
|
e.dataTransfer.setData('taskId', task.id);
|
|
27
27
|
e.dataTransfer.setData('taskTitle', task.title || '');
|
|
28
|
-
|
|
28
|
+
const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
|
|
29
|
+
const dEnd = task.dueDateEnd instanceof Date ? task.dueDateEnd : new Date(task.dueDateEnd);
|
|
30
|
+
const estimatedTime = (dEnd.getTime() - dStart.getTime()) / (60 * 1000);
|
|
31
|
+
e.dataTransfer.setData('taskEstimatedTime', String(estimatedTime));
|
|
29
32
|
if (task.color) {
|
|
30
33
|
e.dataTransfer.setData('taskColor', task.color);
|
|
31
34
|
}
|
|
@@ -48,8 +51,10 @@ export const CalendarLike = ({ tasks, slots = DEFAULT_SLOTS, showCurrentTime = t
|
|
|
48
51
|
const overlappingTasksCount = tasks.filter((task) => {
|
|
49
52
|
if (draggingTask && task.id === draggingTask.id)
|
|
50
53
|
return false;
|
|
51
|
-
const
|
|
52
|
-
const
|
|
54
|
+
const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
|
|
55
|
+
const dEnd = task.dueDateEnd instanceof Date ? task.dueDateEnd : new Date(task.dueDateEnd);
|
|
56
|
+
const taskStart = dStart.getHours() * 60 + dStart.getMinutes();
|
|
57
|
+
const taskEnd = dEnd.getHours() * 60 + dEnd.getMinutes();
|
|
53
58
|
return currentSlotTime >= taskStart && currentSlotTime < taskEnd;
|
|
54
59
|
}).length;
|
|
55
60
|
return (_jsx(CalendarSlot, { slotIndex: slotIndex, onDrop: handleDrop, onCreateTask: onCreateTask, renderTask: renderTask, overlappingTasksCount: overlappingTasksCount, draggingTask: draggingTask || undefined, children: tasksForSlot?.map((task) => (_jsx(CalendarItemWrapper, { task: task, position: task.position, onDragStart: (e) => handleDragStart(e, task), onDragEnd: handleDragEnd, onResize: onTaskResize, renderTask: renderTask }, task.id))) }, slotIndex));
|
|
@@ -20,38 +20,38 @@ const INITIAL_TASKS = [
|
|
|
20
20
|
id: '1',
|
|
21
21
|
title: 'Утренняя почта',
|
|
22
22
|
description: 'Проверить входящие и ответить на важные письма',
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
dueDateStart: getTodayAtHour(9),
|
|
24
|
+
dueDateEnd: getTodayAtHour(9, 45),
|
|
25
25
|
color: '#3b82f6',
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
id: '2',
|
|
29
29
|
title: 'Стендап',
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
dueDateStart: getTodayAtHour(10),
|
|
31
|
+
dueDateEnd: getTodayAtHour(10, 15),
|
|
32
32
|
color: '#10b981',
|
|
33
33
|
isInWork: true,
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
id: '3',
|
|
37
37
|
title: 'Обед',
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
dueDateStart: getTodayAtHour(13),
|
|
39
|
+
dueDateEnd: getTodayAtHour(14),
|
|
40
40
|
color: '#f59e0b',
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
id: '4',
|
|
44
44
|
title: 'Разработка фичи',
|
|
45
45
|
description: 'Реализация логики календаря',
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
dueDateStart: getTodayAtHour(14),
|
|
47
|
+
dueDateEnd: getTodayAtHour(16),
|
|
48
48
|
color: '#8b5cf6',
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
id: '5',
|
|
52
52
|
title: 'Завершенная задача',
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
dueDateStart: getTodayAtHour(17),
|
|
54
|
+
dueDateEnd: getTodayAtHour(17, 30),
|
|
55
55
|
isCompleted: true,
|
|
56
56
|
color: '#8b5cf6',
|
|
57
57
|
},
|
|
@@ -65,10 +65,11 @@ export const Interactive = {
|
|
|
65
65
|
console.log(`Task ${taskId} dropped at ${hour}:${minutes}`);
|
|
66
66
|
setTasks((prev) => prev.map((t) => {
|
|
67
67
|
if (t.id === taskId) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
const durationMs = t.dueDateEnd.getTime() - t.dueDateStart.getTime();
|
|
69
|
+
const newStartDate = new Date(t.dueDateStart);
|
|
70
|
+
newStartDate.setHours(hour, minutes, 0, 0);
|
|
71
|
+
const newEndDate = new Date(newStartDate.getTime() + durationMs);
|
|
72
|
+
return { ...t, dueDateStart: newStartDate, dueDateEnd: newEndDate };
|
|
72
73
|
}
|
|
73
74
|
return t;
|
|
74
75
|
}));
|
|
@@ -77,24 +78,25 @@ export const Interactive = {
|
|
|
77
78
|
console.log(`Task ${taskId} resized to ${newEstimatedTime} minutes. New start: ${newStartHour}:${newStartMinutes}`);
|
|
78
79
|
setTasks((prev) => prev.map((t) => {
|
|
79
80
|
if (t.id === taskId) {
|
|
80
|
-
const
|
|
81
|
+
const newStartDate = new Date(t.dueDateStart);
|
|
81
82
|
if (newStartHour !== undefined && newStartMinutes !== undefined) {
|
|
82
|
-
|
|
83
|
-
newDate.setHours(newStartHour, newStartMinutes, 0, 0);
|
|
84
|
-
newTask.dueDate = newDate;
|
|
83
|
+
newStartDate.setHours(newStartHour, newStartMinutes, 0, 0);
|
|
85
84
|
}
|
|
86
|
-
|
|
85
|
+
const newEndDate = new Date(newStartDate.getTime() + newEstimatedTime * 60 * 1000);
|
|
86
|
+
return { ...t, dueDateStart: newStartDate, dueDateEnd: newEndDate };
|
|
87
87
|
}
|
|
88
88
|
return t;
|
|
89
89
|
}));
|
|
90
90
|
};
|
|
91
91
|
const handleCreateTask = (hour, minutes, estimatedTime) => {
|
|
92
92
|
console.log(`Create task at ${hour}:${minutes} with duration ${estimatedTime}`);
|
|
93
|
+
const dueDateStart = getTodayAtHour(hour, minutes);
|
|
94
|
+
const dueDateEnd = new Date(dueDateStart.getTime() + estimatedTime * 60 * 1000);
|
|
93
95
|
const newTask = {
|
|
94
96
|
id: Math.random().toString(36).substr(2, 9),
|
|
95
97
|
title: 'Новая задача',
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
dueDateStart,
|
|
99
|
+
dueDateEnd,
|
|
98
100
|
color: '#8b5cf6',
|
|
99
101
|
};
|
|
100
102
|
setTasks((prev) => [...prev, newTask]);
|
|
@@ -12,7 +12,9 @@ export const CalendarSlot = ({ slotIndex, onDrop, onCreateTask, renderTask, chil
|
|
|
12
12
|
const springs = useSpring({
|
|
13
13
|
to: {
|
|
14
14
|
opacity: dragOverInfo ? 1 : 0.6,
|
|
15
|
-
height: dragOverInfo
|
|
15
|
+
height: dragOverInfo
|
|
16
|
+
? `${((new Date(dragOverInfo.task.dueDateEnd).getTime() - new Date(dragOverInfo.task.dueDateStart).getTime()) / (60 * 1000) / 15) * 20}px`
|
|
17
|
+
: '40px',
|
|
16
18
|
},
|
|
17
19
|
config: { ...config.stiff, precision: 0.001 },
|
|
18
20
|
});
|
|
@@ -81,8 +83,8 @@ export const CalendarSlot = ({ slotIndex, onDrop, onCreateTask, renderTask, chil
|
|
|
81
83
|
}, children: renderTask ? (renderTask({
|
|
82
84
|
id: 'new-task-preview',
|
|
83
85
|
title: 'Новая задача',
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
dueDateStart: new Date(new Date().setHours(hour, minutes, 0, 0)),
|
|
87
|
+
dueDateEnd: new Date(new Date().setHours(hour, minutes + selection.currentEstimatedTime, 0, 0)),
|
|
86
88
|
color: 'white',
|
|
87
89
|
})) : (_jsxs("div", { className: "border rounded-sm h-full p-1", children: [_jsx(Typo, { size: "xs", weight: "500", children: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430" }), _jsxs(Typo, { size: "xs", color: "secondary", children: [selection.currentEstimatedTime, " \u043C\u0438\u043D"] })] })) }))] })] }));
|
|
88
90
|
};
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
export const calculateTaskPositions = (tasks) => {
|
|
2
2
|
const tasksToRender = [...tasks]
|
|
3
|
-
.map((t) =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
.map((t) => {
|
|
4
|
+
const dStart = t.dueDateStart instanceof Date ? t.dueDateStart : new Date(t.dueDateStart);
|
|
5
|
+
const dEnd = t.dueDateEnd instanceof Date ? t.dueDateEnd : new Date(t.dueDateEnd);
|
|
6
|
+
return {
|
|
7
|
+
...t,
|
|
8
|
+
dueDateStart: dStart,
|
|
9
|
+
dueDateEnd: dEnd,
|
|
10
|
+
};
|
|
11
|
+
})
|
|
12
|
+
.filter((t) => t.dueDateStart && !isNaN(t.dueDateStart.getTime()) && t.dueDateEnd && !isNaN(t.dueDateEnd.getTime()))
|
|
13
|
+
.sort((a, b) => a.dueDateStart.getTime() - b.dueDateStart.getTime());
|
|
9
14
|
const columns = [];
|
|
10
15
|
tasksToRender.forEach((task) => {
|
|
11
|
-
const startTime = task.
|
|
16
|
+
const startTime = task.dueDateStart.getTime();
|
|
12
17
|
let placed = false;
|
|
13
18
|
for (const column of columns) {
|
|
14
19
|
const lastTask = column[column.length - 1];
|
|
15
|
-
const lastEndTime = lastTask.
|
|
20
|
+
const lastEndTime = lastTask.dueDateEnd.getTime();
|
|
16
21
|
if (startTime >= lastEndTime) {
|
|
17
22
|
column.push(task);
|
|
18
23
|
placed = true;
|
|
@@ -29,7 +34,7 @@ export const calculateTaskPositions = (tasks) => {
|
|
|
29
34
|
columns.forEach((column) => {
|
|
30
35
|
let columnMinStartTime = Infinity;
|
|
31
36
|
column.forEach((t) => {
|
|
32
|
-
const start = t.
|
|
37
|
+
const start = t.dueDateStart.getTime();
|
|
33
38
|
if (start < columnMinStartTime)
|
|
34
39
|
columnMinStartTime = start;
|
|
35
40
|
});
|
|
@@ -40,7 +45,7 @@ export const calculateTaskPositions = (tasks) => {
|
|
|
40
45
|
}
|
|
41
46
|
currentCluster.push(column);
|
|
42
47
|
column.forEach((t) => {
|
|
43
|
-
const end = t.
|
|
48
|
+
const end = t.dueDateEnd.getTime();
|
|
44
49
|
if (end > clusterMaxEndTime)
|
|
45
50
|
clusterMaxEndTime = end;
|
|
46
51
|
});
|
|
@@ -55,11 +60,12 @@ export const calculateTaskPositions = (tasks) => {
|
|
|
55
60
|
const width = clusterColumnsCount > 1 ? `${100 / clusterColumnsCount}%` : '100%';
|
|
56
61
|
const left = clusterColumnsCount > 1 ? `${(colIndex * 100) / clusterColumnsCount}%` : '0%';
|
|
57
62
|
// Расчитываем смещение внутри 15-минутного слота
|
|
58
|
-
const minutes = task.
|
|
63
|
+
const minutes = task.dueDateStart.getMinutes();
|
|
59
64
|
const minutesInSlot = minutes % 15;
|
|
60
65
|
const topOffset = (minutesInSlot / 15) * 20;
|
|
61
|
-
const top = `${Math.round(topOffset)}px`;
|
|
62
|
-
const
|
|
66
|
+
const top = `${Math.round(topOffset)}px`;
|
|
67
|
+
const diffMinutes = Math.round((task.dueDateEnd.getTime() - task.dueDateStart.getTime()) / (60 * 1000));
|
|
68
|
+
const height = `${Math.round((Math.max(15, diffMinutes || 15) / 15) * 20)}px`;
|
|
63
69
|
result.push({
|
|
64
70
|
...task,
|
|
65
71
|
position: { width, left, top, height },
|
|
@@ -72,8 +78,9 @@ export const calculateTaskPositions = (tasks) => {
|
|
|
72
78
|
export const groupTasksBySlot = (tasksWithPosition) => {
|
|
73
79
|
const map = {};
|
|
74
80
|
tasksWithPosition.forEach((task) => {
|
|
75
|
-
const
|
|
76
|
-
const
|
|
81
|
+
const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
|
|
82
|
+
const hour = dStart.getHours();
|
|
83
|
+
const minutes = dStart.getMinutes();
|
|
77
84
|
const slotIndex = hour * 4 + Math.floor(minutes / 15);
|
|
78
85
|
if (!map[slotIndex])
|
|
79
86
|
map[slotIndex] = [];
|
|
@@ -8,5 +8,5 @@ import { IconAction } from '../icon-action/icon-action';
|
|
|
8
8
|
export const MainPageLayout = ({ children, leftPanel, rightPanel, leftIcon, rightIcon, }) => {
|
|
9
9
|
const { widget } = useWidget();
|
|
10
10
|
const { expandedPanel, toggleLeftPanel, toggleRightPanel } = useLayout();
|
|
11
|
-
return (_jsx("div", { className: "@container w-full h-[100dvh]", children: _jsxs("main", { className: "grid h-full w-full grid-cols-1 @s:grid-cols-[300px_1fr_300px] @s:py-xl @s:px-l @s:gap-l py-m px-s gap-s relative overflow-hidden", children: [_jsx("aside", { className: "hidden @s:block h-full w-full", children: leftPanel }), _jsx("div", { className: "@s:hidden absolute left-4 top-4 z-50", children: _jsx(IconAction, { icon: expandedPanel === 'left' ? _jsx(X, { size: 20 }) : leftIcon || _jsx(Menu, { size: 20 }), onClick: toggleLeftPanel }) }), _jsxs(Flex, { direction: 'column', justify: 'center', gap: '16px', type: 'fill', className: jc(expandedPanel ? 'hidden @s:flex' : 'flex', 'h-full mt-[50px] @s:m-0', '@s:flex-row @s:items-stretch'), children: [!widget?.isFullWidth && (_jsx("div", { className: jc('h-full scrollbar-none overflow-auto', '@s:flex-1', widget ? 'hidden @s:block' : 'block'), children: children })), widget && (_jsx("div", { className: jc('flex-1 min-h-0 h-full w-full', !widget.isFullWidth && '@s:flex-initial @s:w-[360px]'), children: _jsx(Fragment, { children: widget.ui }, widget.id) }))] }), _jsx("aside", { className: "hidden @s:block h-full w-full", children: rightPanel }), _jsx("div", { className: "@s:hidden absolute right-4 top-4 z-50", children: _jsx(IconAction, { icon: expandedPanel === 'right' ? _jsx(X, { size: 20 }) : rightIcon || _jsx(Menu, { size: 20 }), onClick: toggleRightPanel }) }), expandedPanel && (_jsx("div", { className: "@s:hidden absolute inset-0 z-40 bg-background pt-16 px-4 pb-4 overflow-auto", children: expandedPanel === 'left' ? leftPanel : rightPanel }))] }) }));
|
|
11
|
+
return (_jsx("div", { className: "@container w-full h-[100dvh]", children: _jsxs("main", { className: "grid h-full w-full grid-cols-1 @s:grid-cols-[300px_1fr_300px] @s:py-xl @s:px-l @s:gap-l py-m px-s gap-s relative overflow-hidden", children: [_jsx("aside", { className: "hidden @s:block h-full w-full", children: leftPanel }), _jsx("div", { className: "@s:hidden absolute left-4 top-4 z-50", children: _jsx(IconAction, { icon: expandedPanel === 'left' ? _jsx(X, { size: 20 }) : leftIcon || _jsx(Menu, { size: 20 }), onClick: toggleLeftPanel }) }), _jsxs(Flex, { direction: 'column', justify: 'center', gap: '16px', type: 'fill', className: jc(expandedPanel ? 'hidden @s:flex' : 'flex', 'h-full mt-[50px] @s:m-0', '@s:flex-row @s:items-stretch'), children: [!widget?.isFullWidth && (_jsx("div", { className: jc('min-h-0 h-full scrollbar-none overflow-auto', '@s:flex-1', widget ? 'hidden @s:block' : 'block'), children: children })), widget && (_jsx("div", { className: jc('flex-1 min-h-0 h-full w-full', !widget.isFullWidth && '@s:flex-initial @s:w-[360px]'), children: _jsx(Fragment, { children: widget.ui }, widget.id) }))] }), _jsx("aside", { className: "hidden @s:block h-full w-full", children: rightPanel }), _jsx("div", { className: "@s:hidden absolute right-4 top-4 z-50", children: _jsx(IconAction, { icon: expandedPanel === 'right' ? _jsx(X, { size: 20 }) : rightIcon || _jsx(Menu, { size: 20 }), onClick: toggleRightPanel }) }), expandedPanel && (_jsx("div", { className: "@s:hidden absolute inset-0 z-40 bg-background pt-16 px-4 pb-4 overflow-auto", children: expandedPanel === 'left' ? leftPanel : rightPanel }))] }) }));
|
|
12
12
|
};
|