@kkkarsss/ui 1.1.6 → 1.2.0

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.
@@ -9,6 +9,7 @@ export type TCalendarItemWrapperProps = {
9
9
  height: string;
10
10
  };
11
11
  onDragStart?: (e: DragEvent<HTMLDivElement>, id: string) => void;
12
+ onDragEnd?: (e: DragEvent<HTMLDivElement>, id: string) => void;
12
13
  onResize?: (id: string, newEstimatedTime: number) => void;
13
14
  renderTask: (task: TCalendarTask) => ReactNode;
14
15
  };
@@ -1,24 +1,25 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { jc } from '../../../utils';
4
- export const CalendarItemWrapper = ({ task, position, onDragStart, onResize, renderTask, }) => {
4
+ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, onResize, renderTask, }) => {
5
5
  const [previewEstimatedTime, setPreviewEstimatedTime] = useState(null);
6
6
  const handleMouseDown = (e) => {
7
7
  if (onResize) {
8
8
  e.preventDefault();
9
9
  e.stopPropagation();
10
10
  const startY = e.clientY;
11
- const startHeight = (task.estimatedTime / 60) * 80;
11
+ const startHeight = (task.estimatedTime / 15) * 20;
12
12
  const onMouseMove = (moveEvent) => {
13
13
  const deltaY = moveEvent.clientY - startY;
14
14
  const newHeight = Math.max(20, startHeight + deltaY);
15
- const newEstimatedTime = Math.max(15, Math.round(((newHeight / 80) * 60) / 15) * 15);
15
+ // Округляем до 15 минут (20px)
16
+ const newEstimatedTime = Math.max(15, Math.round(newHeight / 20) * 15);
16
17
  setPreviewEstimatedTime(newEstimatedTime);
17
18
  };
18
19
  const onMouseUp = (upEvent) => {
19
20
  const deltaY = upEvent.clientY - startY;
20
21
  const newHeight = Math.max(20, startHeight + deltaY);
21
- const newEstimatedTime = Math.max(15, Math.round(((newHeight / 80) * 60) / 15) * 15);
22
+ const newEstimatedTime = Math.max(15, Math.round(newHeight / 20) * 15);
22
23
  if (newEstimatedTime !== task.estimatedTime) {
23
24
  onResize(task.id, newEstimatedTime);
24
25
  }
@@ -35,22 +36,32 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onResize, ren
35
36
  document.addEventListener('mouseup', onMouseUp);
36
37
  }
37
38
  };
38
- const displayHeight = previewEstimatedTime ? `${(previewEstimatedTime / 60) * 80}px` : position.height;
39
+ const displayHeight = previewEstimatedTime ? `${(previewEstimatedTime / 15) * 20}px` : position.height;
39
40
  return (_jsx("div", { draggable: !!onDragStart, onDragStart: (e) => {
40
41
  onDragStart?.(e, task.id);
41
42
  const el = e.currentTarget;
43
+ // Создаем пустой элемент для скрытия стандартного ghost image браузера
44
+ const img = new Image();
45
+ img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
46
+ e.dataTransfer.setDragImage(img, 0, 0);
42
47
  if (el) {
43
- el.style.opacity = '0.4';
48
+ // Используем setTimeout, чтобы прозрачность и pointer-events применились после того, как браузер создаст ghost image
49
+ setTimeout(() => {
50
+ el.style.opacity = '0';
51
+ el.style.pointerEvents = 'none';
52
+ }, 0);
44
53
  }
45
54
  }, onDragEnd: (e) => {
55
+ onDragEnd?.(e, task.id);
46
56
  const el = e.currentTarget;
47
57
  if (el) {
48
58
  el.style.opacity = '';
59
+ el.style.pointerEvents = '';
49
60
  }
50
61
  }, style: {
51
62
  height: displayHeight,
52
63
  width: position.width,
53
64
  left: position.left,
54
65
  top: position.top,
55
- }, className: jc('calendar-item absolute p-[2px] transition-all flex flex-col z-50 select-none', onDragStart ? 'cursor-move' : '', 'hover:z-[60]', previewEstimatedTime !== null ? 'transition-none z-[60]' : ''), children: _jsxs("div", { className: "w-full h-full relative group/item", children: [renderTask(task), onResize && (_jsx("div", { className: "absolute bottom-0 left-0 right-1 h-2 cursor-ns-resize group/resize flex items-center justify-center z-[70]", onMouseDown: handleMouseDown, children: _jsx("div", { className: "w-8 h-1 bg-accent/30 rounded-full opacity-0 group-hover/resize:opacity-100 transition-opacity" }) }))] }) }));
66
+ }, className: jc('calendar-item absolute p-[2px] transition-[height,width,left,opacity] flex flex-col z-50 select-none', onDragStart ? 'cursor-move' : '', 'hover:z-[60]', previewEstimatedTime !== null ? 'transition-none z-[60]' : ''), children: _jsxs("div", { className: "w-full h-full relative group/item", children: [renderTask(task), onResize && (_jsx("div", { className: "absolute bottom-0 left-0 right-1 h-2 cursor-ns-resize group/resize flex items-center justify-center z-[70]", onMouseDown: handleMouseDown, children: _jsx("div", { className: "w-8 h-1 bg-accent/30 rounded-full opacity-0 group-hover/resize:opacity-100 transition-opacity" }) }))] }) }));
56
67
  };
@@ -1,26 +1,47 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
3
- import { CalendarHourSlot } from './calendar-hour-slot';
2
+ import { useMemo, useState } from 'react';
4
3
  import { CalendarItemWrapper } from './calendar-item-wrapper';
5
- import { calculateTaskPositions, groupTasksByHour } from './utils';
4
+ import { CalendarSlot } from './calendar-slot';
5
+ import { calculateTaskPositions, groupTasksBySlot } from './utils';
6
+ import { jc } from '../../../utils';
6
7
  export * from './types';
7
8
  export * from './calendar-item-wrapper';
8
9
  export * from './calendar-item';
9
- const DEFAULT_HOURS = Array.from({ length: 24 }, (_, i) => i);
10
- export const CalendarLike = ({ tasks, hours = DEFAULT_HOURS, renderTask, onTaskDrop, onTaskResize, onCreateTask, }) => {
10
+ const DEFAULT_SLOTS = Array.from({ length: 96 }, (_, i) => i);
11
+ export const CalendarLike = ({ tasks, slots = DEFAULT_SLOTS, renderTask, onTaskDrop, onTaskResize, onCreateTask, }) => {
12
+ const [isDragging, setIsDragging] = useState(false);
11
13
  const tasksWithPosition = useMemo(() => calculateTaskPositions(tasks), [tasks]);
12
- const tasksByHour = useMemo(() => groupTasksByHour(tasksWithPosition), [tasksWithPosition]);
13
- const handleDragStart = (e, taskId) => {
14
- e.dataTransfer.setData('taskId', taskId);
14
+ const tasksBySlot = useMemo(() => groupTasksBySlot(tasksWithPosition), [tasksWithPosition]);
15
+ const handleDragStart = (e, task) => {
16
+ setIsDragging(true);
17
+ e.dataTransfer.setData('taskId', task.id);
18
+ e.dataTransfer.setData('taskTitle', task.title || '');
19
+ e.dataTransfer.setData('taskEstimatedTime', String(task.estimatedTime));
20
+ if (task.color) {
21
+ e.dataTransfer.setData('taskColor', task.color);
22
+ }
23
+ // Сохраняем в глобальную переменную для доступа во время dragOver
24
+ // eslint-disable-next-line react-hooks/immutability
25
+ window._draggingTask = {
26
+ id: task.id,
27
+ title: task.title,
28
+ estimatedTime: task.estimatedTime,
29
+ color: task.color,
30
+ };
31
+ };
32
+ const handleDragEnd = () => {
33
+ setIsDragging(false);
34
+ delete window._draggingTask;
15
35
  };
16
36
  const handleDrop = (e, hour, minutes) => {
37
+ setIsDragging(false);
17
38
  const taskId = e.dataTransfer.getData('taskId');
18
39
  if (taskId && onTaskDrop) {
19
40
  onTaskDrop(taskId, hour, minutes);
20
41
  }
21
42
  };
22
- return (_jsx("div", { className: "relative border-l border-secondary/20 ml-12 select-none", children: hours.map((hour) => {
23
- const tasksForHour = tasksByHour[hour];
24
- return (_jsx(CalendarHourSlot, { hour: hour, onDrop: handleDrop, onCreateTask: onCreateTask, children: tasksForHour?.map((task) => (_jsx(CalendarItemWrapper, { task: task, position: task.position, onDragStart: handleDragStart, onResize: onTaskResize, renderTask: renderTask }, task.id))) }, hour));
43
+ return (_jsx("div", { className: jc('relative border-l border-secondary/20 ml-12 select-none', isDragging ? 'is-dragging' : ''), children: slots.map((slotIndex) => {
44
+ const tasksForSlot = tasksBySlot[slotIndex];
45
+ return (_jsx(CalendarSlot, { slotIndex: slotIndex, onDrop: handleDrop, onCreateTask: onCreateTask, 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));
25
46
  }) }));
26
47
  };
@@ -58,12 +58,13 @@ export const Interactive = {
58
58
  const [tasks, setTasks] = useState(INITIAL_TASKS);
59
59
  const [showCompleted, setShowCompleted] = useState(true);
60
60
  const filteredTasks = tasks.filter((t) => (showCompleted ? true : !t.isCompleted));
61
- const handleTaskDrop = (taskId, hour) => {
62
- console.log(`Task ${taskId} dropped at hour ${hour}`);
61
+ const handleTaskDrop = (taskId, hour, minutes) => {
62
+ console.log(`Task ${taskId} dropped at ${hour}:${minutes}`);
63
63
  setTasks((prev) => prev.map((t) => {
64
64
  if (t.id === taskId) {
65
65
  const newDate = new Date(t.dueDate);
66
- newDate.setHours(hour, 0, 0, 0);
66
+ // Storybook handles the date update
67
+ newDate.setHours(hour, minutes, 0, 0);
67
68
  return { ...t, dueDate: newDate };
68
69
  }
69
70
  return t;
@@ -1,8 +1,8 @@
1
1
  import { type FC, type DragEvent, ReactNode } from 'react';
2
- export type TCalendarHourSlotProps = {
3
- hour: number;
2
+ export type TCalendarSlotProps = {
3
+ slotIndex: number;
4
4
  onDrop?: (e: DragEvent<HTMLDivElement>, hour: number, minutes: number) => void;
5
5
  onCreateTask?: (hour: number, minutes: number, estimatedTime: number) => void;
6
6
  children?: ReactNode;
7
7
  };
8
- export declare const CalendarHourSlot: FC<TCalendarHourSlotProps>;
8
+ export declare const CalendarSlot: FC<TCalendarSlotProps>;
@@ -0,0 +1,70 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Typo } from '../text/typo';
4
+ export const CalendarSlot = ({ slotIndex, onDrop, onCreateTask, children }) => {
5
+ const hour = Math.floor(slotIndex / 4);
6
+ const minutes = (slotIndex % 4) * 15;
7
+ const isTopSlot = slotIndex % 4 === 0;
8
+ const [selection, setSelection] = useState(null);
9
+ const [dragOverInfo, setDragOverInfo] = useState(null);
10
+ const handleDragOver = (e) => {
11
+ e.preventDefault();
12
+ // Пытаемся получить данные из глобальной переменной
13
+ const draggingTask = window._draggingTask;
14
+ const title = draggingTask?.title || e.dataTransfer.getData('taskTitle');
15
+ const estimatedTime = draggingTask?.estimatedTime || Number(e.dataTransfer.getData('taskEstimatedTime')) || 15;
16
+ const color = draggingTask?.color || e.dataTransfer.getData('taskColor');
17
+ setDragOverInfo({
18
+ title,
19
+ estimatedTime,
20
+ color,
21
+ topOffset: 0, // Приклеиваем к началу слота (15-минутка)
22
+ });
23
+ };
24
+ const handleDragLeave = () => {
25
+ setDragOverInfo(null);
26
+ };
27
+ const handleInternalDrop = (e) => {
28
+ setDragOverInfo(null);
29
+ onDrop?.(e, hour, minutes); // Приклеиваем к началу слота (15-минутка)
30
+ };
31
+ const handleMouseDown = (e) => {
32
+ if (e.button !== 0 || !onCreateTask)
33
+ return;
34
+ if (e.target.closest('.calendar-item'))
35
+ return;
36
+ setSelection({
37
+ startMinutes: minutes,
38
+ currentEstimatedTime: 15,
39
+ });
40
+ const startY = e.clientY;
41
+ const onMouseMove = (moveEvent) => {
42
+ const deltaY = moveEvent.clientY - startY;
43
+ // Округляем до 15 минут (20px)
44
+ const newEstimatedTime = Math.max(15, Math.round(deltaY / 20) * 15 + 15);
45
+ setSelection((prev) => (prev ? { ...prev, currentEstimatedTime: newEstimatedTime } : null));
46
+ };
47
+ const onMouseUp = () => {
48
+ document.removeEventListener('mousemove', onMouseMove);
49
+ document.removeEventListener('mouseup', onMouseUp);
50
+ setSelection((finalSelection) => {
51
+ if (finalSelection) {
52
+ onCreateTask(hour, minutes, finalSelection.currentEstimatedTime);
53
+ }
54
+ return null;
55
+ });
56
+ };
57
+ document.addEventListener('mousemove', onMouseMove);
58
+ document.addEventListener('mouseup', onMouseUp);
59
+ };
60
+ return (_jsxs("div", { onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleInternalDrop, onMouseDown: handleMouseDown, className: `relative h-5 group hover:z-10 has-[.calendar-item:hover]:z-30 has-[.calendar-item:active]:z-30 ${isTopSlot ? 'border-t border-t-secondary/20' : ''}`, children: [isTopSlot && (_jsx("div", { className: "absolute -left-12 top-0 -translate-y-1/2 w-10 text-right", children: _jsxs(Typo, { size: "xs", color: "secondary", children: [String(hour).padStart(2, '0'), ":00"] }) })), _jsxs("div", { className: "pl-4 relative h-full", children: [children, dragOverInfo !== null && (_jsx("div", { className: "absolute left-4 right-1 rounded-sm z-[40] pointer-events-none flex flex-col overflow-hidden border border-accent transition-colors", style: {
61
+ top: `${dragOverInfo.topOffset}px`,
62
+ height: `${(dragOverInfo.estimatedTime / 15) * 20}px`,
63
+ minHeight: '20px',
64
+ backgroundColor: dragOverInfo.color ? `${dragOverInfo.color}80` : 'var(--accent)',
65
+ borderColor: dragOverInfo.color ? dragOverInfo.color : 'var(--accent)',
66
+ }, children: _jsx("div", { className: "p-1", children: _jsx(Typo, { size: "s", weight: "500", className: "truncate leading-none", children: dragOverInfo.title || 'Переместить сюда' }) }) })), selection && (_jsx("div", { className: "absolute left-4 right-1 bg-accent/30 border border-accent rounded-sm z-[40] pointer-events-none", style: {
67
+ top: '0px',
68
+ height: `${(selection.currentEstimatedTime / 15) * 20}px`,
69
+ }, children: _jsxs("div", { className: "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"] })] }) }))] })] }));
70
+ };
@@ -2,4 +2,3 @@ export * from './calendar-like';
2
2
  export * from './types';
3
3
  export * from './calendar-item';
4
4
  export * from './calendar-item-wrapper';
5
- export * from './calendar-hour-slot';
@@ -2,4 +2,3 @@ export * from './calendar-like';
2
2
  export * from './types';
3
3
  export * from './calendar-item';
4
4
  export * from './calendar-item-wrapper';
5
- export * from './calendar-hour-slot';
@@ -16,6 +16,7 @@ export type TCalendarTaskWithPosition = TCalendarTask & {
16
16
  export type TCalendarLikeProps = {
17
17
  tasks: TCalendarTask[];
18
18
  hours?: number[];
19
+ slots?: number[];
19
20
  renderTask: (task: TCalendarTask) => ReactNode;
20
21
  onTaskDrop?: (taskId: string, hour: number, minutes: number) => void;
21
22
  onTaskResize?: (taskId: string, newEstimatedTime: number) => void;
@@ -1,3 +1,3 @@
1
1
  import { TCalendarTask, TCalendarTaskWithPosition } from './types';
2
2
  export declare const calculateTaskPositions: (tasks: TCalendarTask[]) => TCalendarTaskWithPosition[];
3
- export declare const groupTasksByHour: (tasksWithPosition: TCalendarTaskWithPosition[]) => Record<number, TCalendarTaskWithPosition[]>;
3
+ export declare const groupTasksBySlot: (tasksWithPosition: TCalendarTaskWithPosition[]) => Record<number, TCalendarTaskWithPosition[]>;
@@ -54,8 +54,12 @@ export const calculateTaskPositions = (tasks) => {
54
54
  column.forEach((task) => {
55
55
  const width = clusterColumnsCount > 1 ? `${100 / clusterColumnsCount}%` : '100%';
56
56
  const left = clusterColumnsCount > 1 ? `${(colIndex * 100) / clusterColumnsCount}%` : '0%';
57
- const top = `${(task.dueDate.getMinutes() / 60) * 80}px`;
58
- const height = `${(Math.max(15, task.estimatedTime || 15) / 60) * 80}px`;
57
+ // Расчитываем смещение внутри 15-минутного слота
58
+ const minutes = task.dueDate.getMinutes();
59
+ const minutesInSlot = minutes % 15;
60
+ 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`;
59
63
  result.push({
60
64
  ...task,
61
65
  position: { width, left, top, height },
@@ -65,13 +69,15 @@ export const calculateTaskPositions = (tasks) => {
65
69
  });
66
70
  return result;
67
71
  };
68
- export const groupTasksByHour = (tasksWithPosition) => {
72
+ export const groupTasksBySlot = (tasksWithPosition) => {
69
73
  const map = {};
70
74
  tasksWithPosition.forEach((task) => {
71
75
  const hour = task.dueDate.getHours();
72
- if (!map[hour])
73
- map[hour] = [];
74
- map[hour].push(task);
76
+ const minutes = task.dueDate.getMinutes();
77
+ const slotIndex = hour * 4 + Math.floor(minutes / 15);
78
+ if (!map[slotIndex])
79
+ map[slotIndex] = [];
80
+ map[slotIndex].push(task);
75
81
  });
76
82
  return map;
77
83
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kkkarsss/ui",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
4
4
  "description": "UI Kit for kkkarsss projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,12 +26,12 @@
26
26
  "devDependencies": {
27
27
  "@chromatic-com/storybook": "^5.0.0",
28
28
  "@eslint/js": "^9.39.2",
29
- "@storybook/addon-a11y": "^10.2.1",
30
- "@storybook/addon-docs": "^10.2.1",
31
- "@storybook/addon-onboarding": "^10.2.1",
32
- "@storybook/addon-vitest": "^10.2.1",
33
- "@storybook/react": "^10.2.1",
34
- "@storybook/react-vite": "^10.2.1",
29
+ "@storybook/addon-a11y": "^10.2.7",
30
+ "@storybook/addon-docs": "^10.2.7",
31
+ "@storybook/addon-onboarding": "^10.2.7",
32
+ "@storybook/addon-vitest": "^10.2.7",
33
+ "@storybook/react": "^10.2.7",
34
+ "@storybook/react-vite": "^10.2.7",
35
35
  "@types/node": "^25.0.10",
36
36
  "@types/react": "^19.2.9",
37
37
  "@types/react-dom": "^19.2.3",
@@ -44,7 +44,7 @@
44
44
  "eslint-plugin-import": "^2.32.0",
45
45
  "eslint-plugin-react-hooks": "^7.0.1",
46
46
  "eslint-plugin-react-refresh": "^0.4.26",
47
- "eslint-plugin-storybook": "^10.2.1",
47
+ "eslint-plugin-storybook": "^10.2.7",
48
48
  "globals": "^16.5.0",
49
49
  "lucide-react": "^0.563.0",
50
50
  "playwright": "^1.58.0",
@@ -52,7 +52,7 @@
52
52
  "prettier": "^3.8.1",
53
53
  "react": "^19.2.3",
54
54
  "react-dom": "^19.2.3",
55
- "storybook": "^10.2.1",
55
+ "storybook": "^10.2.7",
56
56
  "tailwindcss": "^3.4.19",
57
57
  "typescript": "5.9.3",
58
58
  "typescript-eslint": "^8.53.1",
@@ -1,64 +0,0 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Typo } from '../text/typo';
4
- export const CalendarHourSlot = ({ hour, onDrop, onCreateTask, children }) => {
5
- const [selection, setSelection] = useState(null);
6
- const [dragOverMinutes, setDragOverMinutes] = useState(null);
7
- const handleDragOver = (e) => {
8
- e.preventDefault();
9
- const rect = e.currentTarget.getBoundingClientRect();
10
- const offsetY = e.clientY - rect.top;
11
- const minutes = Math.floor(offsetY / 20) * 15;
12
- if (minutes !== dragOverMinutes) {
13
- setDragOverMinutes(minutes);
14
- }
15
- };
16
- const handleDragLeave = () => {
17
- setDragOverMinutes(null);
18
- };
19
- const handleInternalDrop = (e) => {
20
- setDragOverMinutes(null);
21
- const rect = e.currentTarget.getBoundingClientRect();
22
- const offsetY = e.clientY - rect.top;
23
- const minutes = Math.floor(offsetY / 20) * 15;
24
- onDrop?.(e, hour, minutes);
25
- };
26
- const handleMouseDown = (e) => {
27
- if (e.button !== 0 || !onCreateTask)
28
- return;
29
- if (e.target.closest('.calendar-item'))
30
- return;
31
- const rect = e.currentTarget.getBoundingClientRect();
32
- const offsetY = e.clientY - rect.top;
33
- const startMinutes = Math.floor(offsetY / 20) * 15;
34
- setSelection({
35
- startMinutes,
36
- currentEstimatedTime: 15,
37
- });
38
- const startY = e.clientY;
39
- const onMouseMove = (moveEvent) => {
40
- const deltaY = moveEvent.clientY - startY;
41
- const newEstimatedTime = Math.max(15, Math.round(((deltaY / 80) * 60) / 15) * 15);
42
- setSelection((prev) => (prev ? { ...prev, currentEstimatedTime: newEstimatedTime } : null));
43
- };
44
- const onMouseUp = () => {
45
- document.removeEventListener('mousemove', onMouseMove);
46
- document.removeEventListener('mouseup', onMouseUp);
47
- setSelection((finalSelection) => {
48
- if (finalSelection) {
49
- onCreateTask(hour, finalSelection.startMinutes, finalSelection.currentEstimatedTime);
50
- }
51
- return null;
52
- });
53
- };
54
- document.addEventListener('mousemove', onMouseMove);
55
- document.addEventListener('mouseup', onMouseUp);
56
- };
57
- return (_jsxs("div", { onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleInternalDrop, onMouseDown: handleMouseDown, className: "relative h-20 border-b border-secondary-foreground group hover:z-10 has-[.calendar-item:hover]:z-30 has-[.calendar-item:active]:z-30", children: [_jsx("div", { className: "absolute -left-12 top-0 -translate-y-1/2 w-10 text-right", children: _jsxs(Typo, { size: "xs", color: "secondary", children: [String(hour).padStart(2, '0'), ":00"] }) }), _jsxs("div", { className: "pl-4 py-1 relative h-full", children: [children, dragOverMinutes !== null && (_jsx("div", { className: "absolute left-4 right-1 bg-accent/10 border border-dashed border-accent/50 rounded-sm z-[40] pointer-events-none", style: {
58
- top: `${(dragOverMinutes / 60) * 80}px`,
59
- height: '20px',
60
- } })), selection && (_jsx("div", { className: "absolute left-4 right-1 bg-accent/30 border border-accent rounded-sm z-[40] pointer-events-none", style: {
61
- top: `${(selection.startMinutes / 60) * 80}px`,
62
- height: `${(selection.currentEstimatedTime / 60) * 80}px`,
63
- }, children: _jsxs("div", { className: "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"] })] }) }))] })] }));
64
- };