@luckydye/calendar 1.2.1 → 1.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckydye/calendar",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "author": "Tim Havlicek",
5
5
  "contributors": [],
6
6
  "description": "",
@@ -39,6 +39,7 @@ interface InhouseSource extends CalendarSource {
39
39
  sessionCookie: string;
40
40
  employeeId: string;
41
41
  unitId?: string;
42
+ startHour?: 9 | 10;
42
43
  };
43
44
  locked?: boolean;
44
45
  }
@@ -562,6 +563,7 @@ export class CalDAVConfigElement extends LitElement {
562
563
  sessionCookie: this.formData.credentials.sessionCookie,
563
564
  employeeId: this.formData.credentials.employeeId,
564
565
  unitId: this.formData.credentials.unitId,
566
+ startHour: this.formData.credentials.startHour,
565
567
  },
566
568
  color: this.formData.color || "#FF6E68",
567
569
  enabled: this.formData.enabled ?? true,
@@ -597,10 +599,10 @@ export class CalDAVConfigElement extends LitElement {
597
599
  this.requestUpdate();
598
600
  }
599
601
 
600
- updateForm(field: string, value: string | boolean) {
602
+ updateForm(field: string, value: string | boolean | number) {
601
603
  if (field === "serverUrl" || field === "username" || field === "password" || field === "url" ||
602
604
  field === "accessToken" || field === "refreshToken" || field === "tokenExpiry" || field === "calendarId" ||
603
- field === "sessionCookie" || field === "employeeId" || field === "unitId") {
605
+ field === "sessionCookie" || field === "employeeId" || field === "unitId" || field === "startHour") {
604
606
  this.formData = {
605
607
  ...this.formData,
606
608
  credentials: {
@@ -985,6 +987,19 @@ export class CalDAVConfigElement extends LitElement {
985
987
  this.updateForm("unitId", (e.target as HTMLInputElement).value)}
986
988
  />
987
989
  </div>
990
+
991
+ <div class="form-group">
992
+ <label class="form-label">Bookings Start At</label>
993
+ <select
994
+ class="form-input"
995
+ .value=${String(this.formData.credentials?.startHour ?? 9)}
996
+ @change=${(e: Event) =>
997
+ this.updateForm("startHour", Number((e.target as HTMLSelectElement).value))}
998
+ >
999
+ <option value="9" ?selected=${(this.formData.credentials?.startHour ?? 9) === 9}>9:00 AM</option>
1000
+ <option value="10" ?selected=${this.formData.credentials?.startHour === 10}>10:00 AM</option>
1001
+ </select>
1002
+ </div>
988
1003
  `
989
1004
  : null}
990
1005
 
@@ -395,6 +395,7 @@ export class CalendarViewElement extends LitElement {
395
395
  display: flex;
396
396
  flex-direction: column;
397
397
  gap: 16px;
398
+ background: var(--bg-secondary, rgba(36, 36, 38, 0.5));
398
399
  }
399
400
 
400
401
  .event-detail-section {
@@ -3367,7 +3368,7 @@ export class CalendarViewElement extends LitElement {
3367
3368
  const deltaMs = dropDate.getTime() - originDate.getTime();
3368
3369
  const snap15 = (d: Date) => {
3369
3370
  const m = d.getMinutes();
3370
- d.setMinutes(Math.floor(m / 15) * 15, 0, 0);
3371
+ d.setMinutes(Math.round(m / 15) * 15, 0, 0);
3371
3372
  return d;
3372
3373
  };
3373
3374
  const newStart = snap15(
@@ -4183,8 +4184,12 @@ export class CalendarViewElement extends LitElement {
4183
4184
  );
4184
4185
  const newEnd = snap15(new Date(this.movingEvent.end.getTime() + deltaMs));
4185
4186
 
4187
+ // The grab point (originDate) relative to the event's midpoint tells us
4188
+ // which half the user grabbed — this is stable for the whole drag.
4189
+ const eventMidMs = (this.movingEvent.start.getTime() + this.movingEvent.end.getTime()) / 2;
4190
+ const useStartEdge = originDate.getTime() <= eventMidMs;
4191
+
4186
4192
  if (this.movingEventDuplicateMode) {
4187
- // Duplicate mode: use accent color to indicate new event creation
4188
4193
  const accent =
4189
4194
  getComputedStyle(this).getPropertyValue("--accent-primary").trim() ||
4190
4195
  "rgb(100, 150, 255)";
@@ -4196,10 +4201,9 @@ export class CalendarViewElement extends LitElement {
4196
4201
  stroke: accent.replace("rgb", "rgba").replace(")", ", 0.8)"),
4197
4202
  text: "white",
4198
4203
  },
4199
- this.movingEventEnd.y,
4204
+ useStartEdge,
4200
4205
  );
4201
4206
  } else {
4202
- // Normal move: use event color
4203
4207
  const rgb = hexToRgb(this.movingEvent.color || "#888888");
4204
4208
  this.renderVirtualEvent(
4205
4209
  newStart,
@@ -4209,7 +4213,7 @@ export class CalendarViewElement extends LitElement {
4209
4213
  stroke: `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, 0.5)`,
4210
4214
  text: "rgba(255, 255, 255, 0.5)",
4211
4215
  },
4212
- this.movingEventEnd.y,
4216
+ useStartEdge,
4213
4217
  );
4214
4218
  }
4215
4219
  }
@@ -4269,23 +4273,20 @@ export class CalendarViewElement extends LitElement {
4269
4273
  fill = accent.replace("rgb", "rgba").replace(")", ", 0.3)");
4270
4274
  stroke = accent.replace("rgb", "rgba").replace(")", ", 0.8)");
4271
4275
  }
4272
- this.renderVirtualEvent(
4273
- earlier,
4274
- later,
4275
- {
4276
- fill,
4277
- stroke,
4278
- text: "white",
4279
- },
4280
- this.eventCreationEnd.y,
4281
- );
4276
+ // In shift mode the cursor tracks the end; otherwise it tracks whichever
4277
+ // edge is away from the fixed anchor (eventCreationStart).
4278
+ const useStartEdge = this.eventCreationShiftPressed
4279
+ ? false
4280
+ : this.eventCreationEnd.y <= this.eventCreationStart.y;
4281
+
4282
+ this.renderVirtualEvent(earlier, later, { fill, stroke, text: "white" }, useStartEdge);
4282
4283
  }
4283
4284
 
4284
4285
  renderVirtualEvent(
4285
4286
  start: Date,
4286
4287
  end: Date,
4287
4288
  color: { fill: string; stroke: string; text: string },
4288
- cursorY?: number,
4289
+ useStartEdge = true,
4289
4290
  ): void {
4290
4291
  if (!this.overlayCanvas || !this.overlayCtx || !this.scrollContainer)
4291
4292
  return;
@@ -4405,15 +4406,6 @@ export class CalendarViewElement extends LitElement {
4405
4406
  ctx.fillStyle = color.text;
4406
4407
  ctx.textAlign = "left";
4407
4408
 
4408
- // Calculate which edge is closer to cursor
4409
- let useStartEdge = true;
4410
- if (cursorY !== undefined) {
4411
- const cursorYViewport = cursorY - this.scrollTop;
4412
- const distanceToStart = Math.abs(cursorYViewport - startY);
4413
- const distanceToEnd = Math.abs(cursorYViewport - endY);
4414
- useStartEdge = distanceToStart < distanceToEnd;
4415
- }
4416
-
4417
4409
  // Use appropriate column based on which edge is closer
4418
4410
  const labelColX = useStartEdge ? firstColX : lastColX;
4419
4411
 
@@ -17,6 +17,10 @@ export interface InhouseCredentials extends CalendarCredentials {
17
17
  * Unit ID (optional, for filtering by unit/location)
18
18
  */
19
19
  unitId?: string;
20
+ /**
21
+ * Hour at which bookings start (9 or 10, defaults to 9)
22
+ */
23
+ startHour?: 9 | 10;
20
24
  }
21
25
 
22
26
  /**
@@ -191,7 +195,7 @@ export class InhouseBookingSource implements CalendarSource {
191
195
  }
192
196
 
193
197
  const LUNCH_MS = 60 * 60 * 1000;
194
- let currentTime = new Date(year, month - 1, day, 9, 0);
198
+ let currentTime = new Date(year, month - 1, day, this.credentials.startHour ?? 9, 0);
195
199
 
196
200
  for (let i = 0; i < dateBookings.length; i++) {
197
201
  // Insert 1-hour lunch break between morning and afternoon sessions
package/src/app.ts CHANGED
@@ -364,6 +364,7 @@ function createInhouseCalendar(source: CalendarSource): Calendar {
364
364
  sessionCookie: source.credentials.sessionCookie,
365
365
  employeeId: source.credentials.employeeId,
366
366
  unitId: source.credentials.unitId,
367
+ startHour: source.credentials.startHour,
367
368
  },
368
369
  source.enabled
369
370
  );