@kkkarsss/ui 1.5.5 → 1.5.6

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.
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Temporal } from '@js-temporal/polyfill';
2
3
  import { Calendar } from 'lucide-react';
3
4
  import { useRef, useState } from 'react';
4
5
  import { Dropdown } from '../../layout';
@@ -9,10 +10,12 @@ export const DateInput = ({ value, onChange, label, placeholder, size = 'm', dis
9
10
  const containerRef = useRef(null);
10
11
  const handleDateSelect = (date) => {
11
12
  if (date) {
12
- const year = date.getFullYear();
13
- const month = String(date.getMonth() + 1).padStart(2, '0');
14
- const day = String(date.getDate()).padStart(2, '0');
15
- onChange(`${year}-${month}-${day}`);
13
+ const plainDate = Temporal.PlainDate.from({
14
+ year: date.getFullYear(),
15
+ month: date.getMonth() + 1,
16
+ day: date.getDate(),
17
+ });
18
+ onChange(plainDate.toString());
16
19
  setIsOpen(false);
17
20
  }
18
21
  };
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Temporal } from '@js-temporal/polyfill';
2
3
  import { useState } from 'react';
3
4
  import { jc } from '../../../utils';
4
5
  export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, onResize, renderTask, }) => {
@@ -8,12 +9,36 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, on
8
9
  e.preventDefault();
9
10
  e.stopPropagation();
10
11
  const startY = e.clientY;
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);
12
+ const toISO = (d) => {
13
+ if (d instanceof Temporal.PlainDate || d instanceof Temporal.PlainDateTime || d instanceof Temporal.Instant)
14
+ return d.toString().split('T')[0];
15
+ if (d instanceof Date)
16
+ return d.toISOString().split('T')[0];
17
+ if (typeof d === 'string' && d.includes(' ')) {
18
+ const parsed = new Date(d);
19
+ if (!isNaN(parsed.getTime()))
20
+ return parsed.toISOString().split('T')[0];
21
+ }
22
+ return String(d);
23
+ };
24
+ const dStart = task.dueDateStart instanceof Temporal.PlainDate
25
+ ? task.dueDateStart
26
+ : Temporal.PlainDate.from(toISO(task.dueDateStart));
27
+ const dEnd = task.dueDateEnd instanceof Temporal.PlainDate
28
+ ? task.dueDateEnd
29
+ : Temporal.PlainDate.from(toISO(task.dueDateEnd));
30
+ const tStart = task.dueTimeStart instanceof Temporal.PlainTime
31
+ ? task.dueTimeStart
32
+ : Temporal.PlainTime.from(String(task.dueTimeStart || '00:00'));
33
+ const tEnd = task.dueTimeEnd instanceof Temporal.PlainTime
34
+ ? task.dueTimeEnd
35
+ : Temporal.PlainTime.from(String(task.dueTimeEnd || '23:59'));
36
+ const dtStart = dStart.toPlainDateTime(tStart);
37
+ const dtEnd = dEnd.toPlainDateTime(tEnd);
38
+ const estimatedTime = Number(dtEnd.since(dtStart).total('minute'));
14
39
  const startHeight = (estimatedTime / 15) * 20;
15
- const startHour = dStart.getHours();
16
- const startMinutes = dStart.getMinutes();
40
+ const startHour = tStart.hour;
41
+ const startMinutes = tStart.minute;
17
42
  const onMouseMove = (moveEvent) => {
18
43
  const deltaY = moveEvent.clientY - startY;
19
44
  let newHeight;
@@ -74,8 +99,10 @@ export const CalendarItemWrapper = ({ task, position, onDragStart, onDragEnd, on
74
99
  const displayHeight = preview ? `${(preview.estimatedTime / 15) * 20}px` : position.height;
75
100
  let displayTop = position.top;
76
101
  if (preview && (preview.startHour !== undefined || preview.startMinutes !== undefined)) {
77
- const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
78
- const originalStartTotalMinutes = dStart.getHours() * 60 + dStart.getMinutes();
102
+ const tStart = task.dueTimeStart instanceof Temporal.PlainTime
103
+ ? task.dueTimeStart
104
+ : Temporal.PlainTime.from(String(task.dueTimeStart || '00:00'));
105
+ const originalStartTotalMinutes = tStart.hour * 60 + tStart.minute;
79
106
  const newStartTotalMinutes = preview.startHour * 60 + preview.startMinutes;
80
107
  const diffMinutes = newStartTotalMinutes - originalStartTotalMinutes;
81
108
  const diffPixels = (diffMinutes / 15) * 20;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Temporal } from '@js-temporal/polyfill';
2
3
  import { useMemo, useState, useRef } from 'react';
3
4
  import { CalendarItemWrapper } from './calendar-item-wrapper';
4
5
  import { CalendarSlot } from './calendar-slot';
@@ -25,9 +26,31 @@ export const CalendarLike = ({ tasks, slots = DEFAULT_SLOTS, showCurrentTime = t
25
26
  setDraggingTask(task);
26
27
  e.dataTransfer.setData('taskId', task.id);
27
28
  e.dataTransfer.setData('taskTitle', task.title || '');
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);
29
+ const toISO = (d) => {
30
+ if (d instanceof Temporal.PlainDate || d instanceof Temporal.PlainDateTime || d instanceof Temporal.Instant)
31
+ return d.toString().split('T')[0];
32
+ if (d instanceof Date)
33
+ return d.toISOString().split('T')[0];
34
+ if (typeof d === 'string' && d.includes(' ')) {
35
+ const parsed = new Date(d);
36
+ if (!isNaN(parsed.getTime()))
37
+ return parsed.toISOString().split('T')[0];
38
+ }
39
+ return String(d);
40
+ };
41
+ const dStart = task.dueDateStart instanceof Temporal.PlainDate
42
+ ? task.dueDateStart
43
+ : Temporal.PlainDate.from(toISO(task.dueDateStart));
44
+ const dEnd = task.dueDateEnd instanceof Temporal.PlainDate ? task.dueDateEnd : Temporal.PlainDate.from(toISO(task.dueDateEnd));
45
+ const tStart = task.dueTimeStart instanceof Temporal.PlainTime
46
+ ? task.dueTimeStart
47
+ : Temporal.PlainTime.from(String(task.dueTimeStart || '00:00'));
48
+ const tEnd = task.dueTimeEnd instanceof Temporal.PlainTime
49
+ ? task.dueTimeEnd
50
+ : Temporal.PlainTime.from(String(task.dueTimeEnd || '23:59'));
51
+ const dtStart = dStart.toPlainDateTime(tStart);
52
+ const dtEnd = dEnd.toPlainDateTime(tEnd);
53
+ const estimatedTime = Number(dtEnd.since(dtStart).total('minute'));
31
54
  e.dataTransfer.setData('taskEstimatedTime', String(estimatedTime));
32
55
  if (task.color) {
33
56
  e.dataTransfer.setData('taskColor', task.color);
@@ -51,10 +74,14 @@ export const CalendarLike = ({ tasks, slots = DEFAULT_SLOTS, showCurrentTime = t
51
74
  const overlappingTasksCount = tasks.filter((task) => {
52
75
  if (draggingTask && task.id === draggingTask.id)
53
76
  return false;
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();
77
+ const tStart = task.dueTimeStart instanceof Temporal.PlainTime
78
+ ? task.dueTimeStart
79
+ : Temporal.PlainTime.from(String(task.dueTimeStart || '00:00'));
80
+ const tEnd = task.dueTimeEnd instanceof Temporal.PlainTime
81
+ ? task.dueTimeEnd
82
+ : Temporal.PlainTime.from(String(task.dueTimeEnd || '23:59'));
83
+ const taskStart = tStart.hour * 60 + tStart.minute;
84
+ const taskEnd = tEnd.hour * 60 + tEnd.minute;
58
85
  return currentSlotTime >= taskStart && currentSlotTime < taskEnd;
59
86
  }).length;
60
87
  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));
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Temporal } from '@js-temporal/polyfill';
2
3
  import { Circle, CircleCheckBig } from 'lucide-react';
3
4
  import { useState } from 'react';
4
5
  import { CalendarLike, CalendarItem } from './calendar-like';
@@ -10,10 +11,11 @@ const meta = {
10
11
  tags: ['autodocs'],
11
12
  };
12
13
  export default meta;
13
- const getTodayAtHour = (hour, minutes = 0) => {
14
- const date = new Date();
15
- date.setHours(hour, minutes, 0, 0);
16
- return date;
14
+ const getTodayAtHour = (_hour, _minutes = 0) => {
15
+ return Temporal.Now.plainDateISO();
16
+ };
17
+ const getTodayTimeAtHour = (hour, minutes = 0) => {
18
+ return Temporal.PlainTime.from({ hour, minute: minutes });
17
19
  };
18
20
  const INITIAL_TASKS = [
19
21
  {
@@ -21,14 +23,18 @@ const INITIAL_TASKS = [
21
23
  title: 'Утренняя почта',
22
24
  description: 'Проверить входящие и ответить на важные письма',
23
25
  dueDateStart: getTodayAtHour(9),
24
- dueDateEnd: getTodayAtHour(9, 45),
26
+ dueDateEnd: getTodayAtHour(9),
27
+ dueTimeStart: getTodayTimeAtHour(9),
28
+ dueTimeEnd: getTodayTimeAtHour(9, 45),
25
29
  color: '#3b82f6',
26
30
  },
27
31
  {
28
32
  id: '2',
29
33
  title: 'Стендап',
30
34
  dueDateStart: getTodayAtHour(10),
31
- dueDateEnd: getTodayAtHour(10, 15),
35
+ dueDateEnd: getTodayAtHour(10),
36
+ dueTimeStart: getTodayTimeAtHour(10),
37
+ dueTimeEnd: getTodayTimeAtHour(10, 15),
32
38
  color: '#10b981',
33
39
  isInWork: true,
34
40
  },
@@ -36,7 +42,9 @@ const INITIAL_TASKS = [
36
42
  id: '3',
37
43
  title: 'Обед',
38
44
  dueDateStart: getTodayAtHour(13),
39
- dueDateEnd: getTodayAtHour(14),
45
+ dueDateEnd: getTodayAtHour(13),
46
+ dueTimeStart: getTodayTimeAtHour(13),
47
+ dueTimeEnd: getTodayTimeAtHour(14),
40
48
  color: '#f59e0b',
41
49
  },
42
50
  {
@@ -44,14 +52,18 @@ const INITIAL_TASKS = [
44
52
  title: 'Разработка фичи',
45
53
  description: 'Реализация логики календаря',
46
54
  dueDateStart: getTodayAtHour(14),
47
- dueDateEnd: getTodayAtHour(16),
55
+ dueDateEnd: getTodayAtHour(14),
56
+ dueTimeStart: getTodayTimeAtHour(14),
57
+ dueTimeEnd: getTodayTimeAtHour(16),
48
58
  color: '#8b5cf6',
49
59
  },
50
60
  {
51
61
  id: '5',
52
62
  title: 'Завершенная задача',
53
63
  dueDateStart: getTodayAtHour(17),
54
- dueDateEnd: getTodayAtHour(17, 30),
64
+ dueDateEnd: getTodayAtHour(17),
65
+ dueTimeStart: getTodayTimeAtHour(17),
66
+ dueTimeEnd: getTodayTimeAtHour(17, 30),
55
67
  isCompleted: true,
56
68
  color: '#8b5cf6',
57
69
  },
@@ -65,11 +77,18 @@ export const Interactive = {
65
77
  console.log(`Task ${taskId} dropped at ${hour}:${minutes}`);
66
78
  setTasks((prev) => prev.map((t) => {
67
79
  if (t.id === taskId) {
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 };
80
+ const dtStart = t.dueDateStart.toPlainDateTime(t.dueTimeStart || Temporal.PlainTime.from('00:00'));
81
+ const dtEnd = (t.dueDateEnd || t.dueDateStart).toPlainDateTime(t.dueTimeEnd || Temporal.PlainTime.from('23:59'));
82
+ const duration = dtEnd.since(dtStart);
83
+ const newDtStart = t.dueDateStart.toPlainDateTime(Temporal.PlainTime.from({ hour, minute: minutes }));
84
+ const newDtEnd = newDtStart.add(duration);
85
+ return {
86
+ ...t,
87
+ dueDateStart: newDtStart.toPlainDate(),
88
+ dueDateEnd: newDtEnd.toPlainDate(),
89
+ dueTimeStart: newDtStart.toPlainTime(),
90
+ dueTimeEnd: newDtEnd.toPlainTime(),
91
+ };
73
92
  }
74
93
  return t;
75
94
  }));
@@ -78,25 +97,36 @@ export const Interactive = {
78
97
  console.log(`Task ${taskId} resized to ${newEstimatedTime} minutes. New start: ${newStartHour}:${newStartMinutes}`);
79
98
  setTasks((prev) => prev.map((t) => {
80
99
  if (t.id === taskId) {
81
- const newStartDate = new Date(t.dueDateStart);
100
+ let newDtStart;
82
101
  if (newStartHour !== undefined && newStartMinutes !== undefined) {
83
- newStartDate.setHours(newStartHour, newStartMinutes, 0, 0);
102
+ newDtStart = t.dueDateStart.toPlainDateTime(Temporal.PlainTime.from({ hour: newStartHour, minute: newStartMinutes }));
103
+ }
104
+ else {
105
+ newDtStart = t.dueDateStart.toPlainDateTime(t.dueTimeStart || Temporal.PlainTime.from('00:00'));
84
106
  }
85
- const newEndDate = new Date(newStartDate.getTime() + newEstimatedTime * 60 * 1000);
86
- return { ...t, dueDateStart: newStartDate, dueDateEnd: newEndDate };
107
+ const newDtEnd = newDtStart.add({ minutes: newEstimatedTime });
108
+ return {
109
+ ...t,
110
+ dueDateStart: newDtStart.toPlainDate(),
111
+ dueDateEnd: newDtEnd.toPlainDate(),
112
+ dueTimeStart: newDtStart.toPlainTime(),
113
+ dueTimeEnd: newDtEnd.toPlainTime(),
114
+ };
87
115
  }
88
116
  return t;
89
117
  }));
90
118
  };
91
119
  const handleCreateTask = (hour, minutes, estimatedTime) => {
92
120
  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);
121
+ const dtStart = Temporal.Now.plainDateISO().toPlainDateTime(Temporal.PlainTime.from({ hour, minute: minutes }));
122
+ const dtEnd = dtStart.add({ minutes: estimatedTime });
95
123
  const newTask = {
96
124
  id: Math.random().toString(36).substr(2, 9),
97
125
  title: 'Новая задача',
98
- dueDateStart,
99
- dueDateEnd,
126
+ dueDateStart: dtStart.toPlainDate(),
127
+ dueDateEnd: dtEnd.toPlainDate(),
128
+ dueTimeStart: dtStart.toPlainTime(),
129
+ dueTimeEnd: dtEnd.toPlainTime(),
100
130
  color: '#8b5cf6',
101
131
  };
102
132
  setTasks((prev) => [...prev, newTask]);
@@ -1,4 +1,5 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Temporal } from '@js-temporal/polyfill';
2
3
  import { animated, config, useSpring } from '@react-spring/web';
3
4
  import { useState } from 'react';
4
5
  import { jc } from '../../../utils';
@@ -13,7 +14,39 @@ export const CalendarSlot = ({ slotIndex, onDrop, onCreateTask, renderTask, chil
13
14
  to: {
14
15
  opacity: dragOverInfo ? 1 : 0.6,
15
16
  height: dragOverInfo
16
- ? `${((new Date(dragOverInfo.task.dueDateEnd).getTime() - new Date(dragOverInfo.task.dueDateStart).getTime()) / (60 * 1000) / 15) * 20}px`
17
+ ? (() => {
18
+ const toISO = (d) => {
19
+ if (d instanceof Temporal.PlainDate ||
20
+ d instanceof Temporal.PlainDateTime ||
21
+ d instanceof Temporal.Instant)
22
+ return d.toString().split('T')[0];
23
+ if (d instanceof Date)
24
+ return d.toISOString().split('T')[0];
25
+ if (typeof d === 'string' && d.includes(' ')) {
26
+ const parsed = new Date(d);
27
+ if (!isNaN(parsed.getTime()))
28
+ return parsed.toISOString().split('T')[0];
29
+ }
30
+ return String(d);
31
+ };
32
+ const task = dragOverInfo.task;
33
+ const dStart = task.dueDateStart instanceof Temporal.PlainDate
34
+ ? task.dueDateStart
35
+ : Temporal.PlainDate.from(toISO(task.dueDateStart));
36
+ const dEnd = task.dueDateEnd instanceof Temporal.PlainDate
37
+ ? task.dueDateEnd
38
+ : Temporal.PlainDate.from(toISO(task.dueDateEnd));
39
+ const tStart = task.dueTimeStart instanceof Temporal.PlainTime
40
+ ? task.dueTimeStart
41
+ : Temporal.PlainTime.from(String(task.dueTimeStart || '00:00'));
42
+ const tEnd = task.dueTimeEnd instanceof Temporal.PlainTime
43
+ ? task.dueTimeEnd
44
+ : Temporal.PlainTime.from(String(task.dueTimeEnd || '23:59'));
45
+ const dtStart = dStart.toPlainDateTime(tStart);
46
+ const dtEnd = dEnd.toPlainDateTime(tEnd);
47
+ const minutes = Number(dtEnd.since(dtStart).total('minute'));
48
+ return `${(minutes / 15) * 20}px`;
49
+ })()
17
50
  : '40px',
18
51
  },
19
52
  config: { ...config.stiff, precision: 0.001 },
@@ -83,8 +116,12 @@ export const CalendarSlot = ({ slotIndex, onDrop, onCreateTask, renderTask, chil
83
116
  }, children: renderTask ? (renderTask({
84
117
  id: 'new-task-preview',
85
118
  title: 'Новая задача',
86
- dueDateStart: new Date(new Date().setHours(hour, minutes, 0, 0)),
87
- dueDateEnd: new Date(new Date().setHours(hour, minutes + selection.currentEstimatedTime, 0, 0)),
119
+ dueDateStart: Temporal.Now.plainDateISO(),
120
+ dueTimeStart: Temporal.PlainTime.from({ hour, minute: minutes }),
121
+ dueDateEnd: Temporal.Now.plainDateISO(),
122
+ dueTimeEnd: Temporal.PlainTime.from({ hour, minute: minutes }).add({
123
+ minutes: selection.currentEstimatedTime,
124
+ }),
88
125
  color: 'white',
89
126
  })) : (_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"] })] })) }))] })] }));
90
127
  };
@@ -1,8 +1,9 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
1
2
  import { ReactNode } from 'react';
2
3
  export type TCalendarTask = {
3
4
  id: string;
4
- dueDateStart: Date;
5
- dueDateEnd: Date;
5
+ dueDateStart: Temporal.PlainDate;
6
+ dueDateEnd: Temporal.PlainDate;
6
7
  [key: string]: any;
7
8
  };
8
9
  export type TCalendarTaskWithPosition = TCalendarTask & {
@@ -1,3 +1,4 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
1
2
  import { useEffect } from 'react';
2
3
  export const useAutoScroll = (containerRef, enabled, topOffset) => {
3
4
  useEffect(() => {
@@ -5,9 +6,9 @@ export const useAutoScroll = (containerRef, enabled, topOffset) => {
5
6
  const scroll = (retryCount = 0) => {
6
7
  if (!containerRef.current)
7
8
  return;
8
- const now = new Date();
9
- const hours = now.getHours();
10
- const minutes = now.getMinutes();
9
+ const now = Temporal.Now.plainTimeISO();
10
+ const hours = now.hour;
11
+ const minutes = now.minute;
11
12
  const totalMinutes = hours * 60 + minutes;
12
13
  // Находим ближайший скроллируемый родитель или используем окно
13
14
  const scrollParent = (node) => {
@@ -1,4 +1,5 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
1
2
  export declare const useCurrentTime: (updateInterval?: number) => {
2
- now: Date;
3
+ now: Temporal.PlainTime;
3
4
  topOffset: number;
4
5
  };
@@ -1,15 +1,16 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
1
2
  import { useState, useEffect, useMemo } from 'react';
2
3
  export const useCurrentTime = (updateInterval = 60000) => {
3
- const [now, setNow] = useState(new Date());
4
+ const [now, setNow] = useState(Temporal.Now.plainTimeISO());
4
5
  useEffect(() => {
5
6
  const interval = setInterval(() => {
6
- setNow(new Date());
7
+ setNow(Temporal.Now.plainTimeISO());
7
8
  }, updateInterval);
8
9
  return () => clearInterval(interval);
9
10
  }, [updateInterval]);
10
11
  const topOffset = useMemo(() => {
11
- const hours = now.getHours();
12
- const minutes = now.getMinutes();
12
+ const hours = now.hour;
13
+ const minutes = now.minute;
13
14
  const totalMinutes = hours * 60 + minutes;
14
15
  // 15 минут = 20 пикселей (высота слота)
15
16
  return totalMinutes * (20 / 15);
@@ -1,24 +1,39 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
2
+ const toDateTime = (date, time) => {
3
+ const toISO = (d) => {
4
+ if (d instanceof Temporal.PlainDate || d instanceof Temporal.PlainDateTime || d instanceof Temporal.Instant)
5
+ return d.toString().split('T')[0];
6
+ if (d instanceof Date)
7
+ return d.toISOString().split('T')[0];
8
+ if (typeof d === 'string' && d.includes(' ')) {
9
+ const parsed = new Date(d);
10
+ if (!isNaN(parsed.getTime()))
11
+ return parsed.toISOString().split('T')[0];
12
+ }
13
+ return String(d);
14
+ };
15
+ const d = date instanceof Temporal.PlainDate ? date : Temporal.PlainDate.from(toISO(date));
16
+ const t = time instanceof Temporal.PlainTime ? time : Temporal.PlainTime.from(String(time || '00:00'));
17
+ return d.toPlainDateTime(t);
18
+ };
1
19
  export const calculateTaskPositions = (tasks) => {
2
20
  const tasksToRender = [...tasks]
3
21
  .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);
22
+ const dtStart = toDateTime(t.dueDateStart, t.dueTimeStart);
23
+ const dtEnd = toDateTime(t.dueDateEnd, t.dueTimeEnd);
6
24
  return {
7
25
  ...t,
8
- dueDateStart: dStart,
9
- dueDateEnd: dEnd,
26
+ dtStart,
27
+ dtEnd,
10
28
  };
11
29
  })
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());
30
+ .sort((a, b) => Temporal.PlainDateTime.compare(a.dtStart, b.dtStart));
14
31
  const columns = [];
15
32
  tasksToRender.forEach((task) => {
16
- const startTime = task.dueDateStart.getTime();
17
33
  let placed = false;
18
34
  for (const column of columns) {
19
35
  const lastTask = column[column.length - 1];
20
- const lastEndTime = lastTask.dueDateEnd.getTime();
21
- if (startTime >= lastEndTime) {
36
+ if (Temporal.PlainDateTime.compare(task.dtStart, lastTask.dtEnd) >= 0) {
22
37
  column.push(task);
23
38
  placed = true;
24
39
  break;
@@ -30,24 +45,27 @@ export const calculateTaskPositions = (tasks) => {
30
45
  });
31
46
  const clusters = [];
32
47
  let currentCluster = [];
33
- let clusterMaxEndTime = 0;
48
+ let clusterMaxEndTime = null;
34
49
  columns.forEach((column) => {
35
- let columnMinStartTime = Infinity;
50
+ let columnMinStartTime = null;
36
51
  column.forEach((t) => {
37
- const start = t.dueDateStart.getTime();
38
- if (start < columnMinStartTime)
39
- columnMinStartTime = start;
52
+ if (!columnMinStartTime || Temporal.PlainDateTime.compare(t.dtStart, columnMinStartTime) < 0) {
53
+ columnMinStartTime = t.dtStart;
54
+ }
40
55
  });
41
- if (columnMinStartTime >= clusterMaxEndTime && currentCluster.length > 0) {
56
+ if (clusterMaxEndTime &&
57
+ columnMinStartTime &&
58
+ Temporal.PlainDateTime.compare(columnMinStartTime, clusterMaxEndTime) >= 0 &&
59
+ currentCluster.length > 0) {
42
60
  clusters.push(currentCluster);
43
61
  currentCluster = [];
44
- clusterMaxEndTime = 0;
62
+ clusterMaxEndTime = null;
45
63
  }
46
64
  currentCluster.push(column);
47
65
  column.forEach((t) => {
48
- const end = t.dueDateEnd.getTime();
49
- if (end > clusterMaxEndTime)
50
- clusterMaxEndTime = end;
66
+ if (!clusterMaxEndTime || Temporal.PlainDateTime.compare(t.dtEnd, clusterMaxEndTime) > 0) {
67
+ clusterMaxEndTime = t.dtEnd;
68
+ }
51
69
  });
52
70
  });
53
71
  if (currentCluster.length > 0)
@@ -60,11 +78,11 @@ export const calculateTaskPositions = (tasks) => {
60
78
  const width = clusterColumnsCount > 1 ? `${100 / clusterColumnsCount}%` : '100%';
61
79
  const left = clusterColumnsCount > 1 ? `${(colIndex * 100) / clusterColumnsCount}%` : '0%';
62
80
  // Расчитываем смещение внутри 15-минутного слота
63
- const minutes = task.dueDateStart.getMinutes();
81
+ const minutes = task.dtStart.minute;
64
82
  const minutesInSlot = minutes % 15;
65
83
  const topOffset = (minutesInSlot / 15) * 20;
66
84
  const top = `${Math.round(topOffset)}px`;
67
- const diffMinutes = Math.round((task.dueDateEnd.getTime() - task.dueDateStart.getTime()) / (60 * 1000));
85
+ const diffMinutes = Math.round(Number(task.dtEnd.since(task.dtStart).total('minute')));
68
86
  const height = `${Math.round((Math.max(15, diffMinutes || 15) / 15) * 20)}px`;
69
87
  result.push({
70
88
  ...task,
@@ -78,9 +96,9 @@ export const calculateTaskPositions = (tasks) => {
78
96
  export const groupTasksBySlot = (tasksWithPosition) => {
79
97
  const map = {};
80
98
  tasksWithPosition.forEach((task) => {
81
- const dStart = task.dueDateStart instanceof Date ? task.dueDateStart : new Date(task.dueDateStart);
82
- const hour = dStart.getHours();
83
- const minutes = dStart.getMinutes();
99
+ const dtStart = toDateTime(task.dueDateStart, task.dueTimeStart);
100
+ const hour = dtStart.hour;
101
+ const minutes = dtStart.minute;
84
102
  const slotIndex = hour * 4 + Math.floor(minutes / 15);
85
103
  if (!map[slotIndex])
86
104
  map[slotIndex] = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kkkarsss/ui",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "description": "UI Kit for kkkarsss projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -33,7 +33,6 @@
33
33
  "@storybook/react": "^10.2.7",
34
34
  "@storybook/react-vite": "^10.2.7",
35
35
  "@tailwindcss/container-queries": "^0.1.1",
36
- "tailwindcss": "^3.4.19",
37
36
  "@types/node": "^25.0.10",
38
37
  "@types/react": "^19.2.9",
39
38
  "@types/react-dom": "^19.2.3",
@@ -55,12 +54,14 @@
55
54
  "react": "^19.2.3",
56
55
  "react-dom": "^19.2.3",
57
56
  "storybook": "^10.2.7",
57
+ "tailwindcss": "^3.4.19",
58
58
  "typescript": "5.9.3",
59
59
  "typescript-eslint": "^8.53.1",
60
60
  "vite": "^7.3.1",
61
61
  "vitest": "^4.0.18"
62
62
  },
63
63
  "dependencies": {
64
+ "@js-temporal/polyfill": "^0.5.1",
64
65
  "@react-spring/web": "^10.0.3",
65
66
  "date-fns": "^4.1.0",
66
67
  "react-datepicker": "^9.1.0",