@urbicon-ui/blocks 6.21.3 → 6.22.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.
@@ -441,6 +441,14 @@
441
441
  onMonthChange?.(clamped.month, clamped.year);
442
442
  }
443
443
 
444
+ // Jump the reference date to a concrete day (mini-calendar drill-down into
445
+ // week/day view). Delegates to the controller, which clamps to
446
+ // [minDate, maxDate] and reports back through handleNavigate like every
447
+ // other controller-driven navigation path.
448
+ function goToDate(date: Date) {
449
+ controller.goTo(date);
450
+ }
451
+
444
452
  function setView(v: CalendarViewMode) {
445
453
  view = v;
446
454
  onViewChange?.(v);
@@ -572,6 +580,7 @@
572
580
  navigateYear,
573
581
  goToToday,
574
582
  goToMonth,
583
+ goToDate,
575
584
  selectDate,
576
585
  setFocusedDate,
577
586
  moveFocus,
@@ -52,13 +52,14 @@
52
52
  function handleDayClick(date: Date) {
53
53
  // Navigate main calendar to this date and select it
54
54
  ctx.selectDate(date);
55
- // Also sync view to the clicked day's month
56
- if (date.getMonth() !== ctx.displayedMonth || date.getFullYear() !== ctx.displayedYear) {
57
- ctx.goToMonth(date.getMonth(), date.getFullYear());
58
- }
59
- // If in week or day view, update displayed date
60
55
  if (ctx.view === 'week' || ctx.view === 'day') {
61
- ctx.navigateDay(0); // triggers a re-render with the newly selected date
56
+ // selectDate only updates the selection it never moves the reference
57
+ // date, so jump the week/day grid to the clicked day explicitly.
58
+ ctx.goToDate(date);
59
+ } else if (date.getMonth() !== ctx.displayedMonth || date.getFullYear() !== ctx.displayedYear) {
60
+ // Month-based views (month handled its own spill-jump in selectDate;
61
+ // agenda lands here) only need to sync to the clicked day's month.
62
+ ctx.goToMonth(date.getMonth(), date.getFullYear());
62
63
  }
63
64
  }
64
65
 
@@ -140,9 +141,10 @@
140
141
  </Button>
141
142
  </div>
142
143
 
143
- <!-- Weekday headers -->
144
+ <!-- Weekday headers — narrow names duplicate in many locales (de-DE: M, D,
145
+ M, D, F, S, S), so the key needs the column position to stay unique. -->
144
146
  <div class="grid grid-cols-7">
145
- {#each weekdayNames as name (name)}
147
+ {#each weekdayNames as name, i (`${i}-${name}`)}
146
148
  <span class={slot('miniCalendarWeekday')}>{name}</span>
147
149
  {/each}
148
150
  </div>
@@ -51,6 +51,9 @@ export interface CalendarContext {
51
51
  setView: (view: CalendarViewMode) => void;
52
52
  /** Navigate to a specific month (used by year grid drill-down). */
53
53
  goToMonth: (month: number, year: number) => void;
54
+ /** Jump the reference date to a specific day, clamped to [minDate, maxDate]
55
+ * (mini-calendar drill-down into week/day view). */
56
+ goToDate: (date: Date) => void;
54
57
  isDateDisabled: (date: Date) => boolean;
55
58
  isDateSelected: (date: Date) => boolean;
56
59
  isDateToday: (date: Date) => boolean;
@@ -194,11 +194,8 @@ export function resizableEvent(opts) {
194
194
  return;
195
195
  isResizing = false;
196
196
  handle.releasePointerCapture(e.pointerId);
197
- handle.removeEventListener('pointermove', handlePointerMove);
198
- handle.removeEventListener('pointerup', handlePointerUp);
199
- handle.removeEventListener('pointercancel', handlePointerUp);
200
- document.body.style.cursor = '';
201
- // Calculate new end time based on final position
197
+ // Calculate new end time based on final position — measured BEFORE
198
+ // cleanup(), which resets the inline height the calculation reads.
202
199
  const gridRect = opts.gridEl.getBoundingClientRect();
203
200
  const eventRect = opts.eventEl.getBoundingClientRect();
204
201
  const gridTotalMinutes = (opts.endHour - opts.startHour) * 60;
@@ -219,14 +216,29 @@ export function resizableEvent(opts) {
219
216
  // Compute new end date
220
217
  const newEnd = new Date(opts.event.start);
221
218
  newEnd.setHours(Math.floor(clampedMinutes / 60), clampedMinutes % 60, 0, 0);
222
- // Reset inline height — the parent will re-render with new props
223
- opts.eventEl.style.height = '';
219
+ cleanup();
224
220
  opts.onResizeEnd?.(opts.event, newEnd);
225
221
  }
222
+ // Teardown of everything a live resize holds: document-level cursor, the
223
+ // move/up/cancel listeners, and the event block's inline height (the parent
224
+ // re-renders the committed height from props).
225
+ function cleanup() {
226
+ handle.removeEventListener('pointermove', handlePointerMove);
227
+ handle.removeEventListener('pointerup', handlePointerUp);
228
+ handle.removeEventListener('pointercancel', handlePointerUp);
229
+ document.body.style.cursor = '';
230
+ opts.eventEl.style.height = '';
231
+ }
226
232
  handle.addEventListener('pointerdown', handlePointerDown);
227
233
  handle.style.touchAction = 'none';
228
234
  return () => {
229
235
  handle.removeEventListener('pointerdown', handlePointerDown);
236
+ // Clean up any in-progress resize on unmount (mirrors draggableEvent) —
237
+ // otherwise the row-resize cursor and inline height would leak.
238
+ if (isResizing) {
239
+ cleanup();
240
+ isResizing = false;
241
+ }
230
242
  };
231
243
  };
232
244
  }