@urbicon-ui/blocks 6.1.4 → 6.1.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/components/Calendar/Calendar.svelte +29 -9
- package/dist/components/Calendar/index.d.ts +16 -4
- package/dist/components/ChartFrame/index.d.ts +10 -0
- package/dist/date/geometry.d.ts +5 -0
- package/dist/date/geometry.js +7 -0
- package/dist/date/index.d.ts +1 -1
- package/dist/date/index.js +1 -1
- package/dist/internal/date-grid/date-grid.svelte.js +16 -2
- package/package.json +3 -3
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
toIso,
|
|
11
11
|
stripTime,
|
|
12
12
|
addDays,
|
|
13
|
-
clampMonth
|
|
13
|
+
clampMonth,
|
|
14
|
+
daysInMonth
|
|
14
15
|
} from '../../date';
|
|
15
16
|
import { DateGridController } from '../../internal/date-grid';
|
|
16
17
|
import type { DateGridSelection, DateGridView } from '../../internal/date-grid';
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
// Selection
|
|
44
45
|
selectionMode = 'single',
|
|
45
46
|
value = $bindable(undefined),
|
|
47
|
+
defaultDate,
|
|
46
48
|
defaultMonth,
|
|
47
49
|
defaultYear,
|
|
48
50
|
// Locale
|
|
@@ -130,8 +132,12 @@
|
|
|
130
132
|
// --- Reference date: the single anchor the grid is built around ---
|
|
131
133
|
// Priority:
|
|
132
134
|
// 1. the first selected date — a populated picker opens on its selection
|
|
133
|
-
// 2. `
|
|
134
|
-
//
|
|
135
|
+
// 2. `defaultDate` — an explicit day anchor (the only one that anchors a
|
|
136
|
+
// week/day view on a specific week without also selecting that day)
|
|
137
|
+
// 3. `defaultMonth` / `defaultYear` — month/year anchor (resolves to the 1st;
|
|
138
|
+
// fine for month/year views, but a week/day view then opens on the 1st's
|
|
139
|
+
// week, which can fall mostly in the prior month — use `defaultDate` there)
|
|
140
|
+
// 4. today
|
|
135
141
|
// Read at mount time only; the user navigates freely afterwards. Remount (e.g.
|
|
136
142
|
// via DatePicker's popover open/close) to re-anchor on the current selection.
|
|
137
143
|
function firstDateOf(v: CalendarProps['value']): Date | undefined {
|
|
@@ -142,16 +148,19 @@
|
|
|
142
148
|
}
|
|
143
149
|
function resolveInitialReference(
|
|
144
150
|
v: CalendarProps['value'],
|
|
151
|
+
date: Date | undefined,
|
|
145
152
|
month: number | undefined,
|
|
146
153
|
year: number | undefined
|
|
147
154
|
): Date {
|
|
148
|
-
const anchor = firstDateOf(v);
|
|
155
|
+
const anchor = firstDateOf(v) ?? date;
|
|
149
156
|
if (anchor) return stripTime(anchor);
|
|
150
157
|
const today = stripTime(new Date());
|
|
151
158
|
if (month == null && year == null) return today;
|
|
152
159
|
return new Date(year ?? today.getFullYear(), month ?? today.getMonth(), 1);
|
|
153
160
|
}
|
|
154
|
-
let referenceDate = $state(
|
|
161
|
+
let referenceDate = $state(
|
|
162
|
+
resolveInitialReference(value, defaultDate, defaultMonth, defaultYear)
|
|
163
|
+
);
|
|
155
164
|
|
|
156
165
|
// The month/year derived from the single reference date.
|
|
157
166
|
const displayedMonth = $derived(referenceDate.getMonth());
|
|
@@ -373,7 +382,15 @@
|
|
|
373
382
|
const targetMonth = ((total % 12) + 12) % 12;
|
|
374
383
|
const clamped = clampMonth(targetMonth, targetYear, minDate, maxDate);
|
|
375
384
|
controller.navDirection = delta > 0 ? 'forward' : 'backward';
|
|
376
|
-
|
|
385
|
+
// Preserve the day-of-month (clamped) so a later switch to week/day view
|
|
386
|
+
// anchors on a real in-month day, not the 1st's (possibly prior-month) week.
|
|
387
|
+
const day = Math.min(referenceDate.getDate(), daysInMonth(clamped.year, clamped.month));
|
|
388
|
+
let next = new Date(clamped.year, clamped.month, day);
|
|
389
|
+
// clampMonth bounds the month; clamp the day too so it never lands before
|
|
390
|
+
// minDate / after maxDate within that boundary month.
|
|
391
|
+
if (minDate && next < stripTime(minDate)) next = stripTime(minDate);
|
|
392
|
+
if (maxDate && next > stripTime(maxDate)) next = stripTime(maxDate);
|
|
393
|
+
referenceDate = next;
|
|
377
394
|
onMonthChange?.(clamped.month, clamped.year);
|
|
378
395
|
}
|
|
379
396
|
|
|
@@ -391,8 +408,10 @@
|
|
|
391
408
|
|
|
392
409
|
function navigateYear(delta: number) {
|
|
393
410
|
controller.navDirection = delta > 0 ? 'forward' : 'backward';
|
|
394
|
-
|
|
395
|
-
|
|
411
|
+
const targetYear = displayedYear + delta;
|
|
412
|
+
const day = Math.min(referenceDate.getDate(), daysInMonth(targetYear, displayedMonth));
|
|
413
|
+
referenceDate = new Date(targetYear, displayedMonth, day);
|
|
414
|
+
onMonthChange?.(displayedMonth, targetYear);
|
|
396
415
|
}
|
|
397
416
|
|
|
398
417
|
function goToToday() {
|
|
@@ -400,7 +419,8 @@
|
|
|
400
419
|
}
|
|
401
420
|
|
|
402
421
|
function goToMonth(month: number, year: number) {
|
|
403
|
-
|
|
422
|
+
const day = Math.min(referenceDate.getDate(), daysInMonth(year, month));
|
|
423
|
+
referenceDate = new Date(year, month, day);
|
|
404
424
|
}
|
|
405
425
|
|
|
406
426
|
function setView(v: CalendarViewMode) {
|
|
@@ -5,10 +5,13 @@ import type { CalendarEvent, CalendarEventCategory, CalendarSelection, CalendarV
|
|
|
5
5
|
import type { CalendarVariants } from './calendar.variants';
|
|
6
6
|
export type CalendarSlotName = 'base' | 'header' | 'title' | 'nav' | 'navButton' | 'grid' | 'weekdayHeader' | 'weekday' | 'weekRow' | 'weekNumber' | 'day' | 'dayNumber' | 'dotContainer' | 'dot' | 'list' | 'dateHeader' | 'empty' | 'item' | 'colorBar' | 'eventTitle' | 'eventDescription' | 'eventHelper' | 'legend' | 'legendItem' | 'legendDot' | 'legendLabel' | 'yearGrid' | 'yearMonth' | 'yearMonthTitle' | 'yearMiniDay' | 'yearMiniDot' | 'weekGrid' | 'weekColumn' | 'weekColumnHeader' | 'weekColumnDayName' | 'weekColumnDayNumber' | 'weekEventList' | 'weekAllDayEvent' | 'multiDayBar' | 'multiDayBarContainer' | 'agendaView' | 'agendaDayGroup' | 'agendaDayHeader' | 'agendaEventList' | 'dayView' | 'dayViewHeader' | 'dayEventList' | 'timeGrid' | 'timeLabel' | 'timeSlotRow' | 'timeDayColumn' | 'timeEvent' | 'allDayArea' | 'currentTimeLine' | 'weekTimeLayout' | 'eventPopover' | 'eventPopoverItem' | 'miniCalendar' | 'miniCalendarHeader' | 'miniCalendarTitle' | 'miniCalendarNavButton' | 'miniCalendarWeekday' | 'miniCalendarDay';
|
|
7
7
|
/**
|
|
8
|
-
* @description Flexible calendar component with month, year, week, and day views
|
|
9
|
-
*
|
|
8
|
+
* @description Flexible calendar component with month, year, week, and day views.
|
|
9
|
+
* Renders timed appointments, multi-day spans and recurrence on a time grid, with
|
|
10
|
+
* event display, date selection and configurable layout. For a headless grid that
|
|
11
|
+
* buckets your own domain content (meals, shifts, bookings) per day, use `Planner`.
|
|
10
12
|
*
|
|
11
13
|
* @tag display
|
|
14
|
+
* @related Planner
|
|
12
15
|
* @related DatePicker
|
|
13
16
|
* @related DateRangePicker
|
|
14
17
|
*
|
|
@@ -53,8 +56,17 @@ export interface CalendarProps extends Omit<CalendarVariants, 'dayState' | 'hasE
|
|
|
53
56
|
/** Currently selected date(s). Supports bind:value. */
|
|
54
57
|
value?: CalendarSelection;
|
|
55
58
|
/**
|
|
56
|
-
* Initial
|
|
57
|
-
*
|
|
59
|
+
* Initial reference day the grid is anchored on, without selecting it. Use
|
|
60
|
+
* this to open a **week** or **day** view on a specific week — `defaultMonth`/
|
|
61
|
+
* `defaultYear` resolve to the 1st, whose week can fall mostly in the previous
|
|
62
|
+
* month. Ignored when `value` is set (the selection anchors instead); takes
|
|
63
|
+
* priority over `defaultMonth`/`defaultYear`. Read at mount only.
|
|
64
|
+
*/
|
|
65
|
+
defaultDate?: Date;
|
|
66
|
+
/**
|
|
67
|
+
* Initial displayed month (0–11). Used only when `value` and `defaultDate`
|
|
68
|
+
* are unset; when `value` is provided, the calendar opens on the value's
|
|
69
|
+
* month. Best for month/year views — for week/day views prefer `defaultDate`.
|
|
58
70
|
* Defaults to current month.
|
|
59
71
|
*/
|
|
60
72
|
defaultMonth?: number;
|
|
@@ -12,6 +12,16 @@ import type { ChartMargin, ChartPlot, ChartSlotClasses } from '../../internal/ch
|
|
|
12
12
|
* @related BarChart
|
|
13
13
|
* @related Sankey
|
|
14
14
|
* @stability beta
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```svelte
|
|
18
|
+
* <ChartFrame height={200} ariaLabel="Weekly revenue">
|
|
19
|
+
* {#snippet children({ innerWidth, innerHeight })}
|
|
20
|
+
* <!-- map your data onto innerWidth / innerHeight, then draw SVG marks -->
|
|
21
|
+
* <polyline points={pts} fill="none" class="stroke-primary" stroke-width="2" />
|
|
22
|
+
* {/snippet}
|
|
23
|
+
* </ChartFrame>
|
|
24
|
+
* ```
|
|
15
25
|
*/
|
|
16
26
|
export interface ChartFrameProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
17
27
|
/** Fixed SVG height in px. @default 240 */
|
package/dist/date/geometry.d.ts
CHANGED
|
@@ -37,6 +37,11 @@ export declare function getYearMonths(year: number): {
|
|
|
37
37
|
month: number;
|
|
38
38
|
year: number;
|
|
39
39
|
}[];
|
|
40
|
+
/**
|
|
41
|
+
* Number of days in a month, leap years included. `month` is 0-based
|
|
42
|
+
* (0 = January). Day 0 of the next month is the last day of this one.
|
|
43
|
+
*/
|
|
44
|
+
export declare function daysInMonth(year: number, month: number): number;
|
|
40
45
|
/**
|
|
41
46
|
* Clamp a month/year to optional min/max date boundaries.
|
|
42
47
|
* Returns the (possibly adjusted) month/year plus flags for whether navigating
|
package/dist/date/geometry.js
CHANGED
|
@@ -77,6 +77,13 @@ export function getWeekNumber(date) {
|
|
|
77
77
|
export function getYearMonths(year) {
|
|
78
78
|
return Array.from({ length: 12 }, (_, i) => ({ month: i, year }));
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Number of days in a month, leap years included. `month` is 0-based
|
|
82
|
+
* (0 = January). Day 0 of the next month is the last day of this one.
|
|
83
|
+
*/
|
|
84
|
+
export function daysInMonth(year, month) {
|
|
85
|
+
return new Date(year, month + 1, 0).getDate();
|
|
86
|
+
}
|
|
80
87
|
/**
|
|
81
88
|
* Clamp a month/year to optional min/max date boundaries.
|
|
82
89
|
* Returns the (possibly adjusted) month/year plus flags for whether navigating
|
package/dist/date/index.d.ts
CHANGED
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export { daysBetween, isInMonth, isInRange, isSameDay, isWeekend, stripTime } from './compare';
|
|
16
16
|
export { formatDate, formatDateFull, formatDateRange, formatDayTitle, formatMonthShort, formatMonthYear, formatWeekRange, formatWeekTitle, getWeekdayNames } from './format';
|
|
17
|
-
export { clampMonth, getMonthGrid, getWeekDates, getWeekNumber, getYearMonths } from './geometry';
|
|
17
|
+
export { clampMonth, daysInMonth, getMonthGrid, getWeekDates, getWeekNumber, getYearMonths } from './geometry';
|
|
18
18
|
export { addDays, eachDayOfRange, endOfWeek, isoToDate, startOfWeek, toIso } from './range';
|
package/dist/date/index.js
CHANGED
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export { daysBetween, isInMonth, isInRange, isSameDay, isWeekend, stripTime } from './compare';
|
|
16
16
|
export { formatDate, formatDateFull, formatDateRange, formatDayTitle, formatMonthShort, formatMonthYear, formatWeekRange, formatWeekTitle, getWeekdayNames } from './format';
|
|
17
|
-
export { clampMonth, getMonthGrid, getWeekDates, getWeekNumber, getYearMonths } from './geometry';
|
|
17
|
+
export { clampMonth, daysInMonth, getMonthGrid, getWeekDates, getWeekNumber, getYearMonths } from './geometry';
|
|
18
18
|
export { addDays, eachDayOfRange, endOfWeek, isoToDate, startOfWeek, toIso } from './range';
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the source of truth (`bind:value`). Only `focusedDate`, `hoveredDate` and
|
|
14
14
|
* `navDirection` are controller-owned `$state`.
|
|
15
15
|
*/
|
|
16
|
-
import { addDays, clampMonth, daysBetween, endOfWeek, formatDateRange, formatDayTitle, formatMonthYear, formatWeekTitle, getMonthGrid, getWeekDates, getWeekdayNames, getWeekNumber, isInMonth, isInRange, isSameDay, isWeekend, startOfWeek, stripTime, toIso } from '../../date';
|
|
16
|
+
import { addDays, clampMonth, daysBetween, daysInMonth, endOfWeek, formatDateRange, formatDayTitle, formatMonthYear, formatWeekTitle, getMonthGrid, getWeekDates, getWeekdayNames, getWeekNumber, isInMonth, isInRange, isSameDay, isWeekend, startOfWeek, stripTime, toIso } from '../../date';
|
|
17
17
|
export class DateGridController {
|
|
18
18
|
#opts;
|
|
19
19
|
/** The roving keyboard focus target (local midnight). Controller-owned. */
|
|
@@ -228,7 +228,21 @@ export class DateGridController {
|
|
|
228
228
|
const targetYear = Math.floor(total / 12);
|
|
229
229
|
const targetMonth = ((total % 12) + 12) % 12;
|
|
230
230
|
const clamped = clampMonth(targetMonth, targetYear, minDate, maxDate);
|
|
231
|
-
|
|
231
|
+
// Preserve the day-of-month (clamped to the target month's length, e.g.
|
|
232
|
+
// 31 Jan → 28 Feb) rather than snapping to the 1st. Month view ignores the
|
|
233
|
+
// day, but a week/day view sharing this reference then anchors on a real
|
|
234
|
+
// in-month day — anchoring on the 1st can land its week mostly in the prior
|
|
235
|
+
// month (1 Mar 2026 is a Sunday → its Monday-week is 23 Feb–1 Mar). Matches
|
|
236
|
+
// the keyboard PageUp/PageDown month step, which already clamps the day.
|
|
237
|
+
const day = Math.min(referenceDate.getDate(), daysInMonth(clamped.year, clamped.month));
|
|
238
|
+
next = new Date(clamped.year, clamped.month, day);
|
|
239
|
+
// clampMonth bounds the month; clamp the preserved day too so it never lands
|
|
240
|
+
// before minDate / after maxDate within that boundary month (a week view
|
|
241
|
+
// would otherwise open on an all-disabled week just outside the range).
|
|
242
|
+
if (minDate && next < stripTime(minDate))
|
|
243
|
+
next = stripTime(minDate);
|
|
244
|
+
if (maxDate && next > stripTime(maxDate))
|
|
245
|
+
next = stripTime(maxDate);
|
|
232
246
|
break;
|
|
233
247
|
}
|
|
234
248
|
case 'week':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbicon-ui/blocks",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.5",
|
|
4
4
|
"description": "Svelte 5 UI component library with Tailwind CSS 4, OKLCH design tokens and zero runtime dependencies",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -90,8 +90,8 @@
|
|
|
90
90
|
"@sveltejs/package": "^2.5.8",
|
|
91
91
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
92
92
|
"@tailwindcss/vite": "^4.3.1",
|
|
93
|
-
"@urbicon-ui/i18n": "6.1.
|
|
94
|
-
"@urbicon-ui/shared-types": "6.1.
|
|
93
|
+
"@urbicon-ui/i18n": "6.1.5",
|
|
94
|
+
"@urbicon-ui/shared-types": "6.1.5",
|
|
95
95
|
"prettier": "^3.8.4",
|
|
96
96
|
"prettier-plugin-svelte": "^4.1.1",
|
|
97
97
|
"prettier-plugin-tailwindcss": "^0.8.0",
|