@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.
@@ -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 startHeight = (task.estimatedTime / 15) * 20;
12
- const startHour = task.dueDate.getHours();
13
- const startMinutes = task.dueDate.getMinutes();
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, task.estimatedTime - deltaMinutes);
30
+ newEstimatedTime = Math.max(15, estimatedTime - deltaMinutes);
28
31
  // If we hit min height, don't move the top further down
29
- const actualDeltaMinutes = task.estimatedTime - newEstimatedTime;
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, task.estimatedTime - deltaMinutes);
48
- const actualDeltaMinutes = task.estimatedTime - finalEstimatedTime;
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 !== task.estimatedTime ||
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 originalStartTotalMinutes = task.dueDate.getHours() * 60 + task.dueDate.getMinutes();
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
- e.dataTransfer.setData('taskEstimatedTime', String(task.estimatedTime));
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 taskStart = task.dueDate.getHours() * 60 + task.dueDate.getMinutes();
52
- const taskEnd = taskStart + task.estimatedTime;
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
- dueDate: getTodayAtHour(9),
24
- estimatedTime: 45,
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
- dueDate: getTodayAtHour(10),
31
- estimatedTime: 15,
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
- dueDate: getTodayAtHour(13),
39
- estimatedTime: 60,
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
- dueDate: getTodayAtHour(14),
47
- estimatedTime: 120,
46
+ dueDateStart: getTodayAtHour(14),
47
+ dueDateEnd: getTodayAtHour(16),
48
48
  color: '#8b5cf6',
49
49
  },
50
50
  {
51
51
  id: '5',
52
52
  title: 'Завершенная задача',
53
- dueDate: getTodayAtHour(17),
54
- estimatedTime: 30,
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 newDate = new Date(t.dueDate);
69
- // Storybook handles the date update
70
- newDate.setHours(hour, minutes, 0, 0);
71
- return { ...t, dueDate: newDate };
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 newTask = { ...t, estimatedTime: newEstimatedTime };
81
+ const newStartDate = new Date(t.dueDateStart);
81
82
  if (newStartHour !== undefined && newStartMinutes !== undefined) {
82
- const newDate = new Date(t.dueDate);
83
- newDate.setHours(newStartHour, newStartMinutes, 0, 0);
84
- newTask.dueDate = newDate;
83
+ newStartDate.setHours(newStartHour, newStartMinutes, 0, 0);
85
84
  }
86
- return newTask;
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
- dueDate: getTodayAtHour(hour, minutes),
97
- estimatedTime,
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 ? `${(dragOverInfo.task.estimatedTime / 15) * 20}px` : '40px',
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
- dueDate: new Date(new Date().setHours(hour, minutes, 0, 0)),
85
- estimatedTime: selection.currentEstimatedTime,
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,8 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
2
  export type TCalendarTask = {
3
3
  id: string;
4
- dueDate: Date;
5
- estimatedTime: number;
4
+ dueDateStart: Date;
5
+ dueDateEnd: Date;
6
6
  [key: string]: any;
7
7
  };
8
8
  export type TCalendarTaskWithPosition = TCalendarTask & {
@@ -1,18 +1,23 @@
1
1
  export const calculateTaskPositions = (tasks) => {
2
2
  const tasksToRender = [...tasks]
3
- .map((t) => ({
4
- ...t,
5
- dueDate: t.dueDate instanceof Date ? t.dueDate : new Date(t.dueDate),
6
- }))
7
- .filter((t) => t.dueDate && !isNaN(t.dueDate.getTime()))
8
- .sort((a, b) => a.dueDate.getTime() - b.dueDate.getTime());
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.dueDate.getTime();
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.dueDate.getTime() + lastTask.estimatedTime * 60 * 1000;
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.dueDate.getTime();
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.dueDate.getTime() + t.estimatedTime * 60 * 1000;
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.dueDate.getMinutes();
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`; // Округляем для надежности, но по идее minutesInSlot должен быть 0 если все по сетке
62
- const height = `${Math.round((Math.max(15, task.estimatedTime || 15) / 15) * 20)}px`;
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 hour = task.dueDate.getHours();
76
- const minutes = task.dueDate.getMinutes();
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kkkarsss/ui",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "UI Kit for kkkarsss projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",