@ayseaistudio/ui-components 3.9.2 → 3.9.3

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,12 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import PropTypes from "prop-types";
3
3
  import React from "react";
4
- import { IconChevronRight } from "@tabler/icons-react";
5
- import { IconChevronLeft } from "@tabler/icons-react";
6
- import "./style.css";
4
+ import { IconChevronRight, IconChevronLeft } from "@tabler/icons-react";
7
5
  import { Label } from "../Label/Label";
8
6
  import { TertiaryButton } from "../TertiaryButton/TertiaryButton";
9
7
  import { CalendarYearDay } from "../CalendarYearDay/CalendarYearDay";
8
+ import "./style.css";
10
9
  const normalizeVariantInput = (value) => {
11
10
  if (!value)
12
11
  return undefined;
@@ -33,6 +32,8 @@ export const Calendar = ({ property1, className, tertiaryButtonIcon = (_jsx(Icon
33
32
  const [selectedDay, setSelectedDay] = React.useState(null);
34
33
  const [selectedDays, setSelectedDays] = React.useState([]);
35
34
  const [selectedWeekRange, setSelectedWeekRange] = React.useState(null);
35
+ const [rangeStart, setRangeStart] = React.useState(null);
36
+ const [rangeEnd, setRangeEnd] = React.useState(null);
36
37
  const today = new Date();
37
38
  const [currentMonth, setCurrentMonth] = React.useState(month ?? today.getMonth());
38
39
  const [currentYear, setCurrentYear] = React.useState(year ?? today.getFullYear());
@@ -55,38 +56,61 @@ export const Calendar = ({ property1, className, tertiaryButtonIcon = (_jsx(Icon
55
56
  setSelectedDay(value.getDate());
56
57
  }
57
58
  }, [value, effectiveVariant]);
58
- // Controlled multiple values support (when provided)
59
+ // Controlled multiple values support (when provided) – treat as range in multi mode
59
60
  React.useEffect(() => {
60
61
  if (effectiveVariant !== "multi")
61
62
  return;
62
63
  if (Array.isArray(values) && values.length > 0) {
63
- // sync month/year to first value for visibility
64
- const first = values[0];
65
- if (first instanceof Date) {
64
+ const sorted = [...values].sort((a, b) => a.getTime() - b.getTime());
65
+ const first = sorted[0];
66
+ const last = sorted[sorted.length - 1];
67
+ if (first instanceof Date && last instanceof Date) {
68
+ setRangeStart(first);
69
+ setRangeEnd(last);
66
70
  setCurrentMonth(first.getMonth());
67
71
  setCurrentYear(first.getFullYear());
68
72
  }
69
- // take only current month days for highlighting
70
- const currentMonthDays = values
71
- .filter(d => d.getFullYear() === currentYear && d.getMonth() === currentMonth)
72
- .map(d => d.getDate());
73
- setSelectedDays(currentMonthDays);
74
73
  }
75
- }, [values, currentMonth, currentYear, effectiveVariant]);
76
- // Notify parent component when selection changes
74
+ else {
75
+ setRangeStart(null);
76
+ setRangeEnd(null);
77
+ }
78
+ }, [values, effectiveVariant]);
79
+ // Compute selectedDays for the current month from the active range in multi mode
77
80
  React.useEffect(() => {
78
- if (onDaySelection) {
79
- if (selectedDays.length > 0) {
80
- const dates = selectedDays
81
- .sort((a, b) => a - b)
82
- .map(d => new Date(currentYear, currentMonth, d));
83
- onDaySelection(dates);
84
- }
85
- else {
86
- onDaySelection([]);
81
+ if (effectiveVariant !== "multi")
82
+ return;
83
+ if (!rangeStart) {
84
+ setSelectedDays([]);
85
+ return;
86
+ }
87
+ const end = rangeEnd && rangeEnd.getTime() >= rangeStart.getTime() ? rangeEnd : rangeStart;
88
+ const days = [];
89
+ const cursor = new Date(rangeStart);
90
+ while (cursor.getTime() <= end.getTime()) {
91
+ if (cursor.getFullYear() === currentYear && cursor.getMonth() === currentMonth) {
92
+ days.push(cursor.getDate());
87
93
  }
94
+ cursor.setDate(cursor.getDate() + 1);
88
95
  }
89
- }, [selectedDays, currentYear, currentMonth]);
96
+ setSelectedDays(days);
97
+ }, [rangeStart, rangeEnd, currentMonth, currentYear, effectiveVariant]);
98
+ // Notify parent component when selection changes (except multi, which calls manually)
99
+ React.useEffect(() => {
100
+ if (!onDaySelection)
101
+ return;
102
+ if (effectiveVariant === "multi")
103
+ return;
104
+ if (selectedDays.length > 0) {
105
+ const dates = selectedDays
106
+ .sort((a, b) => a - b)
107
+ .map(d => new Date(currentYear, currentMonth, d));
108
+ onDaySelection(dates);
109
+ }
110
+ else {
111
+ onDaySelection([]);
112
+ }
113
+ }, [selectedDays, currentYear, currentMonth, effectiveVariant, onDaySelection]);
90
114
  // Reset selection when variant changes
91
115
  React.useEffect(() => {
92
116
  if (effectiveVariant === "single") {
@@ -169,22 +193,40 @@ export const Calendar = ({ property1, className, tertiaryButtonIcon = (_jsx(Icon
169
193
  onDaySelection?.(resolvedWeek);
170
194
  return;
171
195
  }
172
- // multi
173
- if (!isCurrentMonth) {
196
+ // multi: treat as date range (start + end)
197
+ if (effectiveVariant === "multi") {
198
+ // Always move view to clicked date
174
199
  setCurrentMonth(cellDate.getMonth());
175
200
  setCurrentYear(cellDate.getFullYear());
176
- setSelectedDays([]);
201
+ // Case 1: no start yet OR both start & end already selected -> start new range
202
+ if (!rangeStart || rangeEnd) {
203
+ setRangeStart(cellDate);
204
+ setRangeEnd(null);
205
+ onChangeMultiple?.([cellDate]);
206
+ onDaySelection?.([cellDate]);
207
+ return;
208
+ }
209
+ // Case 2: we have a start but no end -> complete the range
210
+ let start = rangeStart;
211
+ let end = cellDate;
212
+ if (end.getTime() < start.getTime()) {
213
+ const tmp = start;
214
+ start = end;
215
+ end = tmp;
216
+ }
217
+ setRangeStart(start);
218
+ setRangeEnd(end);
219
+ const allDates = [];
220
+ const cursor = new Date(start);
221
+ while (cursor.getTime() <= end.getTime()) {
222
+ allDates.push(new Date(cursor));
223
+ cursor.setDate(cursor.getDate() + 1);
224
+ }
225
+ onChangeMultiple?.(allDates);
226
+ onDaySelection?.(allDates);
177
227
  return;
178
228
  }
179
- setSelectedDays((prev) => {
180
- const dayNum = cellDate.getDate();
181
- const next = prev.includes(dayNum) ? prev.filter((d) => d !== dayNum) : [...prev, dayNum];
182
- onChangeMultiple?.(next
183
- .sort((a, b) => a - b)
184
- .map((d) => new Date(cellDate.getFullYear(), cellDate.getMonth(), d)));
185
- return next;
186
- });
187
- }, [buildWeekFromDate, disableOutsideDays, effectiveVariant, onChange, onChangeMultiple, onDaySelection, maxDate]);
229
+ }, [buildWeekFromDate, disableOutsideDays, effectiveVariant, onChange, onChangeMultiple, onDaySelection, maxDate, rangeStart, rangeEnd]);
188
230
  const getStatusForDay = (day, cellDate) => {
189
231
  if (effectiveVariant === "week" && selectedWeekRange) {
190
232
  const { isStart, isEnd } = getWeekRangeMeta(cellDate);
@@ -315,12 +357,31 @@ export const Calendar = ({ property1, className, tertiaryButtonIcon = (_jsx(Icon
315
357
  const taskCount = getAppointmentCountForDate(cellDate);
316
358
  const weekDates = buildWeekFromDate(cellDate);
317
359
  const weekMeta = effectiveVariant === "week" ? getWeekRangeMeta(cellDate) : { inRange: false, isStart: false, isEnd: false };
318
- const isWeekRangeCell = effectiveVariant === "week" && weekMeta.inRange;
319
- const isDisabled = !isWeekRangeCell && ((disableOutsideDays && !day.currentMonth) || (maxDate && cellDate > maxDate));
320
- const status = effectiveVariant === "week" && (weekMeta.isStart || weekMeta.isEnd)
360
+ // Range meta for multi-mode (date range)
361
+ let multiRangeMeta = { inRange: false, isStart: false, isEnd: false };
362
+ if (effectiveVariant === "multi" && rangeStart) {
363
+ let start = rangeStart;
364
+ let end = rangeEnd && rangeEnd.getTime() >= rangeStart.getTime() ? rangeEnd : rangeStart;
365
+ if (rangeEnd && rangeEnd.getTime() < rangeStart.getTime()) {
366
+ start = rangeEnd;
367
+ end = rangeStart;
368
+ }
369
+ const time = cellDate.getTime();
370
+ multiRangeMeta = {
371
+ inRange: time >= start.getTime() && time <= end.getTime(),
372
+ isStart: time === start.getTime(),
373
+ isEnd: time === end.getTime(),
374
+ };
375
+ }
376
+ const isRangeCell = (effectiveVariant === "week" && weekMeta.inRange) || (effectiveVariant === "multi" && multiRangeMeta.inRange);
377
+ const isStartCell = effectiveVariant === "week" ? weekMeta.isStart : multiRangeMeta.isStart;
378
+ const isEndCell = effectiveVariant === "week" ? weekMeta.isEnd : multiRangeMeta.isEnd;
379
+ const isDisabled = !isRangeCell && ((disableOutsideDays && !day.currentMonth) || (maxDate && cellDate > maxDate));
380
+ const status = ((effectiveVariant === "week" && (weekMeta.isStart || weekMeta.isEnd)) ||
381
+ (effectiveVariant === "multi" && (multiRangeMeta.isStart || multiRangeMeta.isEnd)))
321
382
  ? "selected"
322
383
  : getStatusForDay(day, cellDate);
323
- return day ? (_jsx("div", { className: `calendar-year-day-clickable${isDisabled ? " disabled" : ""}${weekMeta.inRange ? " week-range" : ""}${weekMeta.isStart ? " week-start" : ""}${weekMeta.isEnd ? " week-end" : ""}`, onClick: () => handleSelectDate(cellDate, day.currentMonth, weekDates), children: _jsx(CalendarYearDay, { className: `calendar-year-day-instance ${day.currentMonth ? 'current-month' : 'other-month'}${isToday ? ' today' : ''}`, size: "medium", status: status, text: day.day.toString(), taskCount: taskCount, version: day.currentMonth ? "month-day" : "different-month-day" }) }, cellKey)) : (_jsx("div", { className: "calendar-year-day-empty" }, cellKey));
384
+ return day ? (_jsx("div", { className: `calendar-year-day-clickable${isDisabled ? " disabled" : ""}${isRangeCell ? " week-range" : ""}${isStartCell ? " week-start" : ""}${isEndCell ? " week-end" : ""}`, onClick: () => handleSelectDate(cellDate, day.currentMonth, weekDates), children: _jsx(CalendarYearDay, { className: `calendar-year-day-instance ${day.currentMonth ? 'current-month' : 'other-month'}${isToday ? ' today' : ''}`, size: "medium", status: status, text: day.day.toString(), taskCount: taskCount, version: day.currentMonth ? "month-day" : "different-month-day" }) }, cellKey)) : (_jsx("div", { className: "calendar-year-day-empty" }, cellKey));
324
385
  }) }, `week-${wi}`));
325
386
  }) })] }));
326
387
  };
@@ -114,6 +114,24 @@
114
114
  .calendar .calendar-year-day-clickable.week-range {
115
115
  background-color: rgba(113, 163, 13, 0.1);
116
116
  border-radius: 0;
117
+ margin: 0 -2px;
118
+ padding: 0 2px;
119
+ }
120
+
121
+ /* Başlangıç hücresinde sol margin/padding yok */
122
+ .calendar .calendar-year-day-clickable.week-range.week-start {
123
+ border-top-left-radius: 10px;
124
+ border-bottom-left-radius: 10px;
125
+ margin-left: 0;
126
+ padding-left: 0;
127
+ }
128
+
129
+ /* Bitiş hücresinde sağ margin/padding yok */
130
+ .calendar .calendar-year-day-clickable.week-range.week-end {
131
+ border-top-right-radius: 10px;
132
+ border-bottom-right-radius: 10px;
133
+ margin-right: 0;
134
+ padding-right: 0;
117
135
  }
118
136
 
119
137
  .calendar .calendar-year-day-clickable.week-range .calendar-year-day-instance {
@@ -132,16 +150,6 @@
132
150
  color: rgba(61, 61, 61, 1) !important;
133
151
  }
134
152
 
135
- .calendar .calendar-year-day-clickable.week-range.week-start {
136
- border-top-left-radius: 10px;
137
- border-bottom-left-radius: 10px;
138
- }
139
-
140
- .calendar .calendar-year-day-clickable.week-range.week-end {
141
- border-top-right-radius: 10px;
142
- border-bottom-right-radius: 10px;
143
- }
144
-
145
153
  .calendar .calendar-year-day-clickable.week-range.week-start .calendar-year-day-instance.selected,
146
154
  .calendar .calendar-year-day-clickable.week-range.week-end .calendar-year-day-instance.selected {
147
155
  background-color: #b1e635 !important;
@@ -12,7 +12,7 @@ export const CalendarYearDay = ({ size, version, status, className, text, taskCo
12
12
  return 0;
13
13
  };
14
14
  const badgeCount = getBadgeCount();
15
- return (_jsxs("button", { type: "button", "aria-pressed": dayStatus === "selected", className: `calendar-year-day ${dayStatus} ${version} size-0-${size} ${className}`, children: [_jsx(Label, { className: "calendar-year-day-label", bold: "off", color: "black", size: "medium", spacing: "off", stroke: "off", text: text, version: "primary" }), badgeCount > 0 && (_jsx("div", { className: "badge-container", children: Array.from({ length: badgeCount }).map((_, idx) => (_jsx(Badge, { size: "XX-small", color: "grey", className: "instance-node" }, idx))) }))] }));
15
+ return (_jsxs("button", { type: "button", "aria-pressed": dayStatus === "selected", className: `calendar-year-day ${dayStatus} ${version} size-0-${size} ${className}`, onMouseDown: (e) => e.preventDefault(), children: [_jsx(Label, { className: "calendar-year-day-label", bold: "off", color: "black", size: "medium", spacing: "off", stroke: "off", text: text, version: "primary" }), badgeCount > 0 && (_jsx("div", { className: "badge-container", children: Array.from({ length: badgeCount }).map((_, idx) => (_jsx(Badge, { size: "XX-small", color: "grey", className: "instance-node" }, idx))) }))] }));
16
16
  };
17
17
  CalendarYearDay.propTypes = {
18
18
  size: PropTypes.oneOf(["large", "medium"]),
@@ -13,6 +13,7 @@
13
13
  position: relative;
14
14
  width: 36px;
15
15
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
16
+ -webkit-tap-highlight-color: transparent;
16
17
  }
17
18
 
18
19
  .calendar-year-day.size-0-large {
@@ -44,6 +45,17 @@
44
45
  border-color: rgba(152, 211, 23, 1);
45
46
  }
46
47
 
48
+ .calendar-year-day:focus {
49
+ outline: none !important;
50
+ box-shadow: none !important;
51
+ }
52
+
53
+ .calendar-year-day:active {
54
+ outline: none !important;
55
+ box-shadow: none !important;
56
+ border-color: transparent !important;
57
+ }
58
+
47
59
  .calendar-year-day:focus-visible {
48
60
  outline: 2px solid rgba(152, 211, 23, 0.6);
49
61
  outline-offset: 2px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ayseaistudio/ui-components",
3
- "version": "3.9.2",
3
+ "version": "3.9.3",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",