@squirkle-ui/calendar 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const vue = require("vue");
4
+ const core = require("@squirkle-ui/core");
5
+ const ui = require("@squirkle-ui/ui/vue2");
6
+ function normalizeEvent(ev) {
7
+ const start = new Date(ev.start);
8
+ const end = ev.end ? new Date(ev.end) : new Date(start.getTime() + (ev.allDay ? 864e5 : 36e5));
9
+ return { ev, start, end: end > start ? end : new Date(start.getTime() + 15 * 6e4) };
10
+ }
11
+ function layoutDay(events, day) {
12
+ const d0 = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();
13
+ const d1 = d0 + 864e5;
14
+ const items = events.filter((e) => !e.ev.allDay && e.start.getTime() < d1 && e.end.getTime() > d0).map((e) => ({ ...e, s: Math.max(e.start.getTime(), d0), f: Math.min(e.end.getTime(), d1) })).sort((a, b) => a.s - b.s || b.f - a.f);
15
+ const out = [];
16
+ let cluster = [];
17
+ let clusterEnd = -1;
18
+ const flush = () => {
19
+ const cols = Math.max(1, ...cluster.map((c) => c.col + 1));
20
+ for (const c of cluster) out.push({ ev: c.ev, start: c.start, end: c.end, col: c.col, cols, top: (c.s - d0) / 6e4, height: Math.max(15, (c.f - c.s) / 6e4) });
21
+ cluster = [];
22
+ };
23
+ for (const it of items) {
24
+ if (it.s >= clusterEnd && cluster.length) flush();
25
+ const used = new Set(cluster.filter((c) => c.f > it.s).map((c) => c.col));
26
+ let col = 0;
27
+ while (used.has(col)) col++;
28
+ cluster.push({ ...it, col });
29
+ clusterEnd = Math.max(clusterEnd, it.f);
30
+ }
31
+ if (cluster.length) flush();
32
+ return out;
33
+ }
34
+ const eventsOnDay = (events, day) => {
35
+ const d0 = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();
36
+ const d1 = d0 + 864e5;
37
+ return events.filter((e) => e.start.getTime() < d1 && e.end.getTime() > d0).sort((a, b) => Number(!!b.ev.allDay) - Number(!!a.ev.allDay) || a.start.getTime() - b.start.getTime());
38
+ };
39
+ const _sfc_main = vue.defineComponent({
40
+ name: "SqCalendar",
41
+ components: { SqBtn: ui.SqBtn, SqBtnToggle: ui.SqBtnToggle, SqShape: ui.SqShape },
42
+ model: ui.vModel,
43
+ props: {
44
+ /** Focused date (ISO `YYYY-MM-DD`). */
45
+ modelValue: { type: String, default: void 0 },
46
+ view: { type: String, default: "month" },
47
+ views: { type: Array, default: () => ["month", "week", "day"] },
48
+ events: { type: Array, default: () => [] },
49
+ firstDayOfWeek: { type: Number, default: 0 },
50
+ editable: Boolean,
51
+ /** Minutes the drag snaps to. */
52
+ snap: { type: Number, default: 15 },
53
+ hourHeight: { type: Number, default: 48 },
54
+ height: { type: [Number, String], default: 560 },
55
+ maxEvents: { type: Number, default: 3 },
56
+ locale: { type: String, default: void 0 },
57
+ hour12: { type: Boolean, default: void 0 }
58
+ },
59
+ emits: ["update:modelValue", "update:view", "click:event", "click:date", "click:time", "event-change"],
60
+ setup(props, { emit }) {
61
+ const dateModel = ui.useProxiedModel(props, emit, "modelValue", () => core.toISODate(/* @__PURE__ */ new Date()));
62
+ const viewModel = ui.useProxiedModel(props, emit, "view");
63
+ const loc = vue.computed(() => {
64
+ var _a;
65
+ return (_a = props.locale) != null ? _a : ui.config.locale.code;
66
+ });
67
+ const focus = vue.computed(() => {
68
+ var _a;
69
+ const [y, m, d] = ((_a = dateModel.value) != null ? _a : core.toISODate(/* @__PURE__ */ new Date())).split("-").map(Number);
70
+ return new Date(y, m - 1, d);
71
+ });
72
+ const norm = vue.computed(() => props.events.map(normalizeEvent));
73
+ const weekdays = vue.computed(() => core.weekdayNames(loc.value, props.firstDayOfWeek, "short"));
74
+ const viewLabels = { month: "Month", week: "Week", day: "Day" };
75
+ const now = vue.ref(/* @__PURE__ */ new Date());
76
+ let timer;
77
+ vue.onMounted(() => timer = setInterval(() => now.value = /* @__PURE__ */ new Date(), 6e4));
78
+ vue.onBeforeUnmount(() => clearInterval(timer));
79
+ const title = vue.computed(() => {
80
+ const f = focus.value;
81
+ if (viewModel.value === "day") return new Intl.DateTimeFormat(loc.value, { dateStyle: "full" }).format(f);
82
+ if (viewModel.value === "week") {
83
+ const a = core.startOfWeek(f, props.firstDayOfWeek);
84
+ const b = core.addDays(a, 6);
85
+ const fmt = new Intl.DateTimeFormat(loc.value, { month: "short", day: "numeric" });
86
+ return `${fmt.format(a)} – ${fmt.format(b)}, ${b.getFullYear()}`;
87
+ }
88
+ return new Intl.DateTimeFormat(loc.value, { month: "long", year: "numeric" }).format(f);
89
+ });
90
+ const monthDays = vue.computed(
91
+ () => core.monthGrid(focus.value, props.firstDayOfWeek).map((date) => ({ date, key: core.toISODate(date), inMonth: core.isSameMonth(date, focus.value), today: core.isSameDay(date, now.value), events: eventsOnDay(norm.value, date) }))
92
+ );
93
+ const columns = vue.computed(() => {
94
+ const start = viewModel.value === "day" ? focus.value : core.startOfWeek(focus.value, props.firstDayOfWeek);
95
+ const count = viewModel.value === "day" ? 1 : 7;
96
+ const wfmt = new Intl.DateTimeFormat(loc.value, { weekday: "short" });
97
+ return Array.from({ length: count }, (_, i) => {
98
+ const date = core.addDays(start, i);
99
+ return { date, key: core.toISODate(date), weekday: wfmt.format(date), today: core.isSameDay(date, now.value), placed: layoutDay(norm.value, date), allDay: eventsOnDay(norm.value, date).filter((e) => e.ev.allDay) };
100
+ });
101
+ });
102
+ const shift = (d) => {
103
+ const f = focus.value;
104
+ dateModel.value = core.toISODate(viewModel.value === "month" ? core.addMonths(f, d) : core.addDays(f, d * (viewModel.value === "week" ? 7 : 1)));
105
+ };
106
+ const today = () => dateModel.value = core.toISODate(/* @__PURE__ */ new Date());
107
+ const goDay = (d) => {
108
+ dateModel.value = core.toISODate(d);
109
+ viewModel.value = "day";
110
+ };
111
+ const colorOf = (ev) => {
112
+ var _a;
113
+ return ui.colorValue((_a = ev.color) != null ? _a : "primary");
114
+ };
115
+ const time = (d) => new Intl.DateTimeFormat(loc.value, { hour: "numeric", minute: "2-digit", hour12: props.hour12 }).format(d);
116
+ const hourLabel = (h) => new Intl.DateTimeFormat(loc.value, { hour: "numeric", hour12: props.hour12 }).format(new Date(2024, 0, 1, h));
117
+ const nowTop = vue.computed(() => (now.value.getHours() * 60 + now.value.getMinutes()) / 60 * props.hourHeight);
118
+ const dragId = vue.ref(null);
119
+ const dropKey = vue.ref("");
120
+ const onMonthDrop = (date) => {
121
+ dropKey.value = "";
122
+ const e = norm.value.find((x) => x.ev.id === dragId.value);
123
+ dragId.value = null;
124
+ if (!e || !props.editable) return;
125
+ const delta = Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(e.start.getFullYear(), e.start.getMonth(), e.start.getDate()).getTime()) / 864e5);
126
+ if (delta) emit("event-change", { event: e.ev, start: core.addDays(e.start, delta), end: core.addDays(e.end, delta) });
127
+ };
128
+ const scroller = vue.ref(null);
129
+ const dragState = vue.ref(null);
130
+ const moved = vue.ref(false);
131
+ let origin = { x: 0, y: 0, colWidth: 1, placed: null };
132
+ const snapMin = (m) => Math.round(m / props.snap) * props.snap;
133
+ const onDragMove = (e) => {
134
+ if (!dragState.value) return;
135
+ const dy = e.clientY - origin.y;
136
+ const dx = e.clientX - origin.x;
137
+ if (Math.abs(dy) + Math.abs(dx) > 4) moved.value = true;
138
+ dragState.value = { ...dragState.value, dMin: snapMin(dy / props.hourHeight * 60), dDay: dragState.value.mode === "move" ? Math.round(dx / origin.colWidth) : 0 };
139
+ };
140
+ const onDragEnd = () => {
141
+ window.removeEventListener("pointermove", onDragMove);
142
+ window.removeEventListener("pointerup", onDragEnd);
143
+ const s = dragState.value;
144
+ const p = origin.placed;
145
+ dragState.value = null;
146
+ if (!s || !p || !moved.value) return;
147
+ const start = new Date(p.start.getTime() + (s.mode === "move" ? s.dMin * 6e4 : 0) + s.dDay * 864e5);
148
+ const end = new Date(p.end.getTime() + s.dMin * 6e4 + s.dDay * 864e5);
149
+ if (end > start) emit("event-change", { event: p.ev, start, end });
150
+ setTimeout(() => moved.value = false, 0);
151
+ };
152
+ const startDrag = (e, p, mode) => {
153
+ if (!props.editable || e.button !== 0) return;
154
+ const col = e.currentTarget.closest(".sq-calendar__col");
155
+ origin = { x: e.clientX, y: e.clientY, colWidth: (col == null ? void 0 : col.offsetWidth) || 1, placed: p };
156
+ moved.value = false;
157
+ dragState.value = { id: p.ev.id, mode, dMin: 0, dDay: 0 };
158
+ window.addEventListener("pointermove", onDragMove);
159
+ window.addEventListener("pointerup", onDragEnd);
160
+ };
161
+ vue.onBeforeUnmount(onDragEnd);
162
+ const previewStart = (p) => {
163
+ var _a;
164
+ return ((_a = dragState.value) == null ? void 0 : _a.id) === p.ev.id && dragState.value.mode === "move" ? new Date(p.start.getTime() + dragState.value.dMin * 6e4) : p.start;
165
+ };
166
+ const previewEnd = (p) => {
167
+ var _a;
168
+ return ((_a = dragState.value) == null ? void 0 : _a.id) === p.ev.id ? new Date(p.end.getTime() + dragState.value.dMin * 6e4) : p.end;
169
+ };
170
+ const eventStyle = (p) => {
171
+ var _a;
172
+ const ds = ((_a = dragState.value) == null ? void 0 : _a.id) === p.ev.id ? dragState.value : null;
173
+ const top = (p.top + (ds && ds.mode === "move" ? ds.dMin : 0)) / 60 * props.hourHeight;
174
+ const height = Math.max(18, (p.height + (ds && ds.mode === "resize" ? ds.dMin : 0)) / 60 * props.hourHeight - 2);
175
+ return {
176
+ top: `${top}px`,
177
+ height: `${height}px`,
178
+ left: `calc(${p.col / p.cols * 100}% + 2px)`,
179
+ width: `calc(${100 / p.cols}% - 4px)`,
180
+ transform: ds && ds.dDay ? `translateX(${ds.dDay * 100}%)` : void 0,
181
+ "--sq-ev": colorOf(p.ev)
182
+ };
183
+ };
184
+ const onSlotClick = (e, date) => {
185
+ const col = e.currentTarget;
186
+ const minutes = snapMin((e.clientY - col.getBoundingClientRect().top) / props.hourHeight * 60);
187
+ emit("click:time", new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, minutes));
188
+ };
189
+ const scrollToNow = () => vue.nextTick(() => scroller.value && (scroller.value.scrollTop = Math.max(0, nowTop.value - 120)));
190
+ vue.onMounted(scrollToNow);
191
+ vue.watch(viewModel, (v) => v !== "month" && scrollToNow());
192
+ return {
193
+ viewModel,
194
+ viewLabels,
195
+ weekdays,
196
+ title,
197
+ monthDays,
198
+ columns,
199
+ shift,
200
+ today,
201
+ goDay,
202
+ colorOf,
203
+ time,
204
+ hourLabel,
205
+ nowTop,
206
+ dragId,
207
+ dropKey,
208
+ onMonthDrop,
209
+ scroller,
210
+ dragState,
211
+ moved,
212
+ startDrag,
213
+ previewStart,
214
+ previewEnd,
215
+ eventStyle,
216
+ onSlotClick,
217
+ toUnit: core.toUnit,
218
+ messages: vue.computed(() => ui.config.locale.messages)
219
+ };
220
+ }
221
+ });
222
+ function normalizeComponent(scriptExports, render2, staticRenderFns, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) {
223
+ var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports;
224
+ if (render2) {
225
+ options.render = render2;
226
+ options.staticRenderFns = staticRenderFns;
227
+ options._compiled = true;
228
+ }
229
+ return {
230
+ exports: scriptExports,
231
+ options
232
+ };
233
+ }
234
+ var _sfc_render = function render() {
235
+ var _vm = this, _c = _vm._self._c;
236
+ _vm._self._setupProxy;
237
+ return _c("div", { staticClass: "sq-calendar sq-surface", style: { "--sq-r": "var(--sq-radius-xl)", "--sq-bg": "var(--sq-surface)", "--sq-bd": "var(--sq-border)", "--sq-bw": "1px" } }, [_c("SqShape", { attrs: { "clip": "" } }), _c("div", { staticClass: "sq-calendar__toolbar" }, [_c("SqBtn", { attrs: { "variant": "tonal", "size": "small" }, on: { "click": _vm.today } }, [_vm._v(_vm._s(_vm.messages.today))]), _c("SqBtn", { attrs: { "icon": "chevronLeft", "variant": "text", "color": "surface", "size": "small", "aria-label": _vm.messages.previous }, on: { "click": function($event) {
238
+ return _vm.shift(-1);
239
+ } } }), _c("SqBtn", { attrs: { "icon": "chevronRight", "variant": "text", "color": "surface", "size": "small", "aria-label": _vm.messages.next }, on: { "click": function($event) {
240
+ return _vm.shift(1);
241
+ } } }), _c("div", { staticClass: "sq-calendar__title" }, [_vm._v(_vm._s(_vm.title))]), _c("span", { staticClass: "sq-spacer" }), _c("SqBtnToggle", { attrs: { "mandatory": "" }, model: { value: _vm.viewModel, callback: function($$v) {
242
+ _vm.viewModel = $$v;
243
+ }, expression: "viewModel" } }, _vm._l(_vm.views, function(v) {
244
+ return _c("SqBtn", { key: v, attrs: { "value": v, "size": "small", "variant": "text", "color": "surface" } }, [_vm._v(_vm._s(_vm.viewLabels[v]))]);
245
+ }), 1)], 1), _vm.viewModel === "month" ? _c("div", { staticClass: "sq-calendar__month" }, [_vm._l(_vm.weekdays, function(w, wi) {
246
+ return _c("div", { key: wi, staticClass: "sq-calendar__weekday" }, [_vm._v(_vm._s(w))]);
247
+ }), _vm._l(_vm.monthDays, function(d) {
248
+ return _c("div", { key: d.key, staticClass: "sq-calendar__day", class: { "sq-calendar__day--outside": !d.inMonth, "sq-calendar__day--today": d.today, "sq-calendar__day--drop": _vm.dropKey === d.key }, on: { "click": function($event) {
249
+ return _vm.$emit("click:date", d.key);
250
+ }, "dragover": function($event) {
251
+ $event.preventDefault();
252
+ _vm.dropKey = d.key;
253
+ }, "dragleave": function($event) {
254
+ _vm.dropKey === d.key && (_vm.dropKey = "");
255
+ }, "drop": function($event) {
256
+ $event.preventDefault();
257
+ return _vm.onMonthDrop(d.date);
258
+ } } }, [_c("span", { staticClass: "sq-calendar__date sq-squircle" }, [_vm._v(_vm._s(d.date.getDate()))]), _vm._l(d.events.slice(0, _vm.maxEvents), function(e) {
259
+ return _c("div", { key: String(e.ev.id), staticClass: "sq-calendar__chip sq-squircle", class: { "sq-calendar__chip--allday": e.ev.allDay }, style: { "--sq-ev": _vm.colorOf(e.ev) }, attrs: { "draggable": _vm.editable, "title": e.ev.title }, on: { "dragstart": function($event) {
260
+ _vm.dragId = e.ev.id;
261
+ }, "click": function($event) {
262
+ $event.stopPropagation();
263
+ return _vm.$emit("click:event", e.ev);
264
+ } } }, [!e.ev.allDay ? _c("span", { staticClass: "sq-calendar__chip-time" }, [_vm._v(_vm._s(_vm.time(e.start)))]) : _vm._e(), _vm._v(_vm._s(e.ev.title) + " ")]);
265
+ }), d.events.length > _vm.maxEvents ? _c("button", { staticClass: "sq-calendar__more", attrs: { "type": "button" }, on: { "click": function($event) {
266
+ $event.stopPropagation();
267
+ return _vm.goDay(d.date);
268
+ } } }, [_vm._v("+" + _vm._s(d.events.length - _vm.maxEvents) + " more")]) : _vm._e()], 2);
269
+ })], 2) : _c("div", { staticClass: "sq-calendar__time", style: { "--sq-cal-cols": _vm.columns.length } }, [_c("div", { staticClass: "sq-calendar__time-head" }, [_c("div", { staticClass: "sq-calendar__gutter" }), _vm._l(_vm.columns, function(c) {
270
+ return _c("div", { key: c.key, staticClass: "sq-calendar__col-head", class: { "sq-calendar__col-head--today": c.today } }, [_c("span", [_vm._v(_vm._s(c.weekday))]), _c("b", { staticClass: "sq-squircle" }, [_vm._v(_vm._s(c.date.getDate()))])]);
271
+ })], 2), _c("div", { staticClass: "sq-calendar__allday" }, [_c("div", { staticClass: "sq-calendar__gutter" }, [_vm._v("all-day")]), _vm._l(_vm.columns, function(c) {
272
+ return _c("div", { key: c.key, staticClass: "sq-calendar__allday-col" }, _vm._l(c.allDay, function(e) {
273
+ return _c("div", { key: String(e.ev.id), staticClass: "sq-calendar__chip sq-calendar__chip--allday sq-squircle", style: { "--sq-ev": _vm.colorOf(e.ev) }, on: { "click": function($event) {
274
+ return _vm.$emit("click:event", e.ev);
275
+ } } }, [_vm._v(_vm._s(e.ev.title))]);
276
+ }), 0);
277
+ })], 2), _c("div", { ref: "scroller", staticClass: "sq-calendar__scroll", style: { height: _vm.toUnit(_vm.height) } }, [_c("div", { staticClass: "sq-calendar__grid", style: { height: 24 * _vm.hourHeight + "px" } }, [_c("div", { staticClass: "sq-calendar__gutter sq-calendar__hours" }, _vm._l(24, function(h) {
278
+ return _c("div", { key: h, staticClass: "sq-calendar__hour", style: { height: _vm.hourHeight + "px" } }, [h > 1 ? _c("span", [_vm._v(_vm._s(_vm.hourLabel(h - 1)))]) : _vm._e()]);
279
+ }), 0), _vm._l(_vm.columns, function(c) {
280
+ return _c("div", { key: c.key, staticClass: "sq-calendar__col", on: { "click": function($event) {
281
+ return _vm.onSlotClick($event, c.date);
282
+ } } }, [_vm._l(24, function(h) {
283
+ return _c("div", { key: h, staticClass: "sq-calendar__slot", style: { height: _vm.hourHeight + "px" } });
284
+ }), c.today ? _c("div", { staticClass: "sq-calendar__now", style: { top: _vm.nowTop + "px" } }) : _vm._e(), _vm._l(c.placed, function(p) {
285
+ return _c("div", { key: String(p.ev.id), staticClass: "sq-calendar__event sq-squircle", class: { "sq-calendar__event--dragging": _vm.dragState && _vm.dragState.id === p.ev.id }, style: _vm.eventStyle(p), on: { "pointerdown": function($event) {
286
+ $event.stopPropagation();
287
+ return _vm.startDrag($event, p, "move");
288
+ }, "click": function($event) {
289
+ $event.stopPropagation();
290
+ !_vm.moved && _vm.$emit("click:event", p.ev);
291
+ } } }, [_c("div", { staticClass: "sq-calendar__event-title" }, [_vm._v(_vm._s(p.ev.title))]), _c("div", { staticClass: "sq-calendar__event-time" }, [_vm._v(_vm._s(_vm.time(_vm.previewStart(p))) + " – " + _vm._s(_vm.time(_vm.previewEnd(p))))]), _vm.editable ? _c("div", { staticClass: "sq-calendar__resize", on: { "pointerdown": function($event) {
292
+ $event.stopPropagation();
293
+ return _vm.startDrag($event, p, "resize");
294
+ } } }) : _vm._e()]);
295
+ })], 2);
296
+ })], 2)])])], 1);
297
+ };
298
+ var _sfc_staticRenderFns = [];
299
+ var __component__ = /* @__PURE__ */ normalizeComponent(
300
+ _sfc_main,
301
+ _sfc_render,
302
+ _sfc_staticRenderFns
303
+ );
304
+ const SqCalendar = __component__.exports;
305
+ const componentNames = ["SqCalendar"];
306
+ exports.SqCalendar = SqCalendar;
307
+ exports.componentNames = componentNames;
308
+ exports.default = SqCalendar;
309
+ exports.eventsOnDay = eventsOnDay;
310
+ exports.layoutDay = layoutDay;
311
+ exports.normalizeEvent = normalizeEvent;
312
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../src/layout.ts","../../src/SqCalendar.vue","../../src/index.ts"],"sourcesContent":["/** Pure layout helpers for SqCalendar (unit-tested). */\nexport interface CalendarEvent {\n id: string | number\n title: string\n start: Date | string\n end?: Date | string\n color?: string\n allDay?: boolean\n [key: string]: unknown\n}\n\nexport interface NormEvent { ev: CalendarEvent; start: Date; end: Date }\n\nexport function normalizeEvent(ev: CalendarEvent): NormEvent {\n const start = new Date(ev.start)\n const end = ev.end ? new Date(ev.end) : new Date(start.getTime() + (ev.allDay ? 864e5 : 36e5))\n return { ev, start, end: end > start ? end : new Date(start.getTime() + 15 * 6e4) }\n}\n\nexport interface Placed extends NormEvent { col: number; cols: number; top: number; height: number }\n\n/**\n * Side-by-side layout for timed events of one day: overlapping events share the width in columns.\n * top/height are in minutes from midnight.\n */\nexport function layoutDay(events: NormEvent[], day: Date): Placed[] {\n const d0 = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime()\n const d1 = d0 + 864e5\n const items = events\n .filter((e) => !e.ev.allDay && e.start.getTime() < d1 && e.end.getTime() > d0)\n .map((e) => ({ ...e, s: Math.max(e.start.getTime(), d0), f: Math.min(e.end.getTime(), d1) }))\n .sort((a, b) => a.s - b.s || b.f - a.f)\n const out: Placed[] = []\n let cluster: (typeof items[number] & { col: number })[] = []\n let clusterEnd = -1\n const flush = () => {\n const cols = Math.max(1, ...cluster.map((c) => c.col + 1))\n for (const c of cluster) out.push({ ev: c.ev, start: c.start, end: c.end, col: c.col, cols, top: (c.s - d0) / 6e4, height: Math.max(15, (c.f - c.s) / 6e4) })\n cluster = []\n }\n for (const it of items) {\n if (it.s >= clusterEnd && cluster.length) flush()\n const used = new Set(cluster.filter((c) => c.f > it.s).map((c) => c.col))\n let col = 0\n while (used.has(col)) col++\n cluster.push({ ...it, col })\n clusterEnd = Math.max(clusterEnd, it.f)\n }\n if (cluster.length) flush()\n return out\n}\n\nexport const eventsOnDay = (events: NormEvent[], day: Date) => {\n const d0 = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime()\n const d1 = d0 + 864e5\n return events.filter((e) => e.start.getTime() < d1 && e.end.getTime() > d0).sort((a, b) => Number(!!b.ev.allDay) - Number(!!a.ev.allDay) || a.start.getTime() - b.start.getTime())\n}\n","<template>\n <div class=\"sq-calendar sq-surface\" :style=\"{ '--sq-r': 'var(--sq-radius-xl)', '--sq-bg': 'var(--sq-surface)', '--sq-bd': 'var(--sq-border)', '--sq-bw': '1px' }\">\n <SqShape clip />\n <div class=\"sq-calendar__toolbar\">\n <SqBtn variant=\"tonal\" size=\"small\" @click=\"today\">{{ messages.today }}</SqBtn>\n <SqBtn icon=\"chevronLeft\" variant=\"text\" color=\"surface\" size=\"small\" :aria-label=\"messages.previous\" @click=\"shift(-1)\" />\n <SqBtn icon=\"chevronRight\" variant=\"text\" color=\"surface\" size=\"small\" :aria-label=\"messages.next\" @click=\"shift(1)\" />\n <div class=\"sq-calendar__title\">{{ title }}</div>\n <span class=\"sq-spacer\"></span>\n <SqBtnToggle v-model=\"viewModel\" mandatory>\n <SqBtn v-for=\"v in views\" :key=\"v\" :value=\"v\" size=\"small\" variant=\"text\" color=\"surface\">{{ viewLabels[v] }}</SqBtn>\n </SqBtnToggle>\n </div>\n\n <!-- month -->\n <div v-if=\"viewModel === 'month'\" class=\"sq-calendar__month\">\n <div v-for=\"(w, wi) in weekdays\" :key=\"wi\" class=\"sq-calendar__weekday\">{{ w }}</div>\n <div\n v-for=\"d in monthDays\"\n :key=\"d.key\"\n class=\"sq-calendar__day\"\n :class=\"{ 'sq-calendar__day--outside': !d.inMonth, 'sq-calendar__day--today': d.today, 'sq-calendar__day--drop': dropKey === d.key }\"\n @click=\"$emit('click:date', d.key)\"\n @dragover.prevent=\"dropKey = d.key\"\n @dragleave=\"dropKey === d.key && (dropKey = '')\"\n @drop.prevent=\"onMonthDrop(d.date)\"\n >\n <span class=\"sq-calendar__date sq-squircle\">{{ d.date.getDate() }}</span>\n <div\n v-for=\"e in d.events.slice(0, maxEvents)\"\n :key=\"String(e.ev.id)\"\n class=\"sq-calendar__chip sq-squircle\"\n :class=\"{ 'sq-calendar__chip--allday': e.ev.allDay }\"\n :style=\"{ '--sq-ev': colorOf(e.ev) }\"\n :draggable=\"editable\"\n :title=\"e.ev.title\"\n @dragstart=\"dragId = e.ev.id\"\n @click.stop=\"$emit('click:event', e.ev)\"\n >\n <span v-if=\"!e.ev.allDay\" class=\"sq-calendar__chip-time\">{{ time(e.start) }}</span>{{ e.ev.title }}\n </div>\n <button v-if=\"d.events.length > maxEvents\" type=\"button\" class=\"sq-calendar__more\" @click.stop=\"goDay(d.date)\">+{{ d.events.length - maxEvents }} more</button>\n </div>\n </div>\n\n <!-- week / day -->\n <div v-else class=\"sq-calendar__time\" :style=\"{ '--sq-cal-cols': columns.length }\">\n <div class=\"sq-calendar__time-head\">\n <div class=\"sq-calendar__gutter\"></div>\n <div v-for=\"c in columns\" :key=\"c.key\" class=\"sq-calendar__col-head\" :class=\"{ 'sq-calendar__col-head--today': c.today }\">\n <span>{{ c.weekday }}</span><b class=\"sq-squircle\">{{ c.date.getDate() }}</b>\n </div>\n </div>\n <div class=\"sq-calendar__allday\">\n <div class=\"sq-calendar__gutter\">all-day</div>\n <div v-for=\"c in columns\" :key=\"c.key\" class=\"sq-calendar__allday-col\">\n <div v-for=\"e in c.allDay\" :key=\"String(e.ev.id)\" class=\"sq-calendar__chip sq-calendar__chip--allday sq-squircle\" :style=\"{ '--sq-ev': colorOf(e.ev) }\" @click=\"$emit('click:event', e.ev)\">{{ e.ev.title }}</div>\n </div>\n </div>\n <div ref=\"scroller\" class=\"sq-calendar__scroll\" :style=\"{ height: toUnit(height) }\">\n <div class=\"sq-calendar__grid\" :style=\"{ height: 24 * hourHeight + 'px' }\">\n <div class=\"sq-calendar__gutter sq-calendar__hours\">\n <div v-for=\"h in 24\" :key=\"h\" class=\"sq-calendar__hour\" :style=\"{ height: hourHeight + 'px' }\"><span v-if=\"h > 1\">{{ hourLabel(h - 1) }}</span></div>\n </div>\n <div v-for=\"c in columns\" :key=\"c.key\" class=\"sq-calendar__col\" @click=\"onSlotClick($event, c.date)\">\n <div v-for=\"h in 24\" :key=\"h\" class=\"sq-calendar__slot\" :style=\"{ height: hourHeight + 'px' }\"></div>\n <div v-if=\"c.today\" class=\"sq-calendar__now\" :style=\"{ top: nowTop + 'px' }\"></div>\n <div\n v-for=\"p in c.placed\"\n :key=\"String(p.ev.id)\"\n class=\"sq-calendar__event sq-squircle\"\n :class=\"{ 'sq-calendar__event--dragging': dragState && dragState.id === p.ev.id }\"\n :style=\"eventStyle(p)\"\n @pointerdown.stop=\"startDrag($event, p, 'move')\"\n @click.stop=\"!moved && $emit('click:event', p.ev)\"\n >\n <div class=\"sq-calendar__event-title\">{{ p.ev.title }}</div>\n <div class=\"sq-calendar__event-time\">{{ time(previewStart(p)) }} – {{ time(previewEnd(p)) }}</div>\n <div v-if=\"editable\" class=\"sq-calendar__resize\" @pointerdown.stop=\"startDrag($event, p, 'resize')\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, nextTick, onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue'\nimport { addDays, addMonths, isSameDay, isSameMonth, monthGrid, startOfWeek, toISODate, toUnit, weekdayNames } from '@squirkle-ui/core'\nimport { SqBtn, SqBtnToggle, SqShape, colorValue, config, useProxiedModel, vModel } from '@squirkle-ui/ui'\nimport { eventsOnDay, layoutDay, normalizeEvent, type CalendarEvent, type Placed } from './layout'\n\ntype View = 'month' | 'week' | 'day'\n\n/** Event calendar: month / week / day views, overlapping-event layout, drag to move & resize (editable). */\nexport default defineComponent({\n name: 'SqCalendar',\n components: { SqBtn, SqBtnToggle, SqShape },\n model: vModel,\n props: {\n /** Focused date (ISO `YYYY-MM-DD`). */\n modelValue: { type: String, default: undefined },\n view: { type: String as PropType<View>, default: 'month' },\n views: { type: Array as PropType<View[]>, default: () => ['month', 'week', 'day'] },\n events: { type: Array as PropType<CalendarEvent[]>, default: () => [] },\n firstDayOfWeek: { type: Number, default: 0 },\n editable: Boolean,\n /** Minutes the drag snaps to. */\n snap: { type: Number, default: 15 },\n hourHeight: { type: Number, default: 48 },\n height: { type: [Number, String], default: 560 },\n maxEvents: { type: Number, default: 3 },\n locale: { type: String, default: undefined },\n hour12: { type: Boolean, default: undefined },\n },\n emits: ['update:modelValue', 'update:view', 'click:event', 'click:date', 'click:time', 'event-change'],\n setup(props, { emit }) {\n const dateModel = useProxiedModel<string | undefined>(props, emit, 'modelValue', () => toISODate(new Date()))\n const viewModel = useProxiedModel<View>(props, emit, 'view')\n const loc = computed(() => props.locale ?? config.locale.code)\n const focus = computed(() => {\n const [y, m, d] = (dateModel.value ?? toISODate(new Date())).split('-').map(Number)\n return new Date(y, m - 1, d)\n })\n const norm = computed(() => props.events.map(normalizeEvent))\n const weekdays = computed(() => weekdayNames(loc.value, props.firstDayOfWeek, 'short'))\n const viewLabels: Record<View, string> = { month: 'Month', week: 'Week', day: 'Day' }\n const now = ref(new Date())\n // Started on mount so server rendering never creates a timer.\n let timer: ReturnType<typeof setInterval> | undefined\n onMounted(() => (timer = setInterval(() => (now.value = new Date()), 60_000)))\n onBeforeUnmount(() => clearInterval(timer))\n\n const title = computed(() => {\n const f = focus.value\n if (viewModel.value === 'day') return new Intl.DateTimeFormat(loc.value, { dateStyle: 'full' }).format(f)\n if (viewModel.value === 'week') {\n const a = startOfWeek(f, props.firstDayOfWeek)\n const b = addDays(a, 6)\n const fmt = new Intl.DateTimeFormat(loc.value, { month: 'short', day: 'numeric' })\n return `${fmt.format(a)} – ${fmt.format(b)}, ${b.getFullYear()}`\n }\n return new Intl.DateTimeFormat(loc.value, { month: 'long', year: 'numeric' }).format(f)\n })\n const monthDays = computed(() =>\n monthGrid(focus.value, props.firstDayOfWeek).map((date) => ({ date, key: toISODate(date), inMonth: isSameMonth(date, focus.value), today: isSameDay(date, now.value), events: eventsOnDay(norm.value, date) })),\n )\n const columns = computed(() => {\n const start = viewModel.value === 'day' ? focus.value : startOfWeek(focus.value, props.firstDayOfWeek)\n const count = viewModel.value === 'day' ? 1 : 7\n const wfmt = new Intl.DateTimeFormat(loc.value, { weekday: 'short' })\n return Array.from({ length: count }, (_, i) => {\n const date = addDays(start, i)\n return { date, key: toISODate(date), weekday: wfmt.format(date), today: isSameDay(date, now.value), placed: layoutDay(norm.value, date), allDay: eventsOnDay(norm.value, date).filter((e) => e.ev.allDay) }\n })\n })\n\n const shift = (d: number) => {\n const f = focus.value\n dateModel.value = toISODate(viewModel.value === 'month' ? addMonths(f, d) : addDays(f, d * (viewModel.value === 'week' ? 7 : 1)))\n }\n const today = () => (dateModel.value = toISODate(new Date()))\n const goDay = (d: Date) => { dateModel.value = toISODate(d); viewModel.value = 'day' }\n const colorOf = (ev: CalendarEvent) => colorValue(ev.color ?? 'primary')\n const time = (d: Date) => new Intl.DateTimeFormat(loc.value, { hour: 'numeric', minute: '2-digit', hour12: props.hour12 }).format(d)\n const hourLabel = (h: number) => new Intl.DateTimeFormat(loc.value, { hour: 'numeric', hour12: props.hour12 }).format(new Date(2024, 0, 1, h))\n const nowTop = computed(() => ((now.value.getHours() * 60 + now.value.getMinutes()) / 60) * props.hourHeight)\n\n // month drag (HTML5)\n const dragId = ref<string | number | null>(null)\n const dropKey = ref('')\n const onMonthDrop = (date: Date) => {\n dropKey.value = ''\n const e = norm.value.find((x) => x.ev.id === dragId.value)\n dragId.value = null\n if (!e || !props.editable) return\n const delta = Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(e.start.getFullYear(), e.start.getMonth(), e.start.getDate()).getTime()) / 864e5)\n if (delta) emit('event-change', { event: e.ev, start: addDays(e.start, delta), end: addDays(e.end, delta) })\n }\n\n // week/day pointer drag\n const scroller = ref<HTMLElement | null>(null)\n const dragState = ref<{ id: string | number; mode: 'move' | 'resize'; dMin: number; dDay: number } | null>(null)\n const moved = ref(false)\n let origin = { x: 0, y: 0, colWidth: 1, placed: null as Placed | null }\n const snapMin = (m: number) => Math.round(m / props.snap) * props.snap\n const onDragMove = (e: PointerEvent) => {\n if (!dragState.value) return\n const dy = e.clientY - origin.y\n const dx = e.clientX - origin.x\n if (Math.abs(dy) + Math.abs(dx) > 4) moved.value = true\n dragState.value = { ...dragState.value, dMin: snapMin((dy / props.hourHeight) * 60), dDay: dragState.value.mode === 'move' ? Math.round(dx / origin.colWidth) : 0 }\n }\n const onDragEnd = () => {\n window.removeEventListener('pointermove', onDragMove)\n window.removeEventListener('pointerup', onDragEnd)\n const s = dragState.value\n const p = origin.placed\n dragState.value = null\n if (!s || !p || !moved.value) return\n const start = new Date(p.start.getTime() + (s.mode === 'move' ? s.dMin * 6e4 : 0) + s.dDay * 864e5)\n const end = new Date(p.end.getTime() + s.dMin * 6e4 + s.dDay * 864e5)\n if (end > start) emit('event-change', { event: p.ev, start, end })\n setTimeout(() => (moved.value = false), 0)\n }\n const startDrag = (e: PointerEvent, p: Placed, mode: 'move' | 'resize') => {\n if (!props.editable || e.button !== 0) return\n const col = (e.currentTarget as HTMLElement).closest('.sq-calendar__col') as HTMLElement\n origin = { x: e.clientX, y: e.clientY, colWidth: col?.offsetWidth || 1, placed: p }\n moved.value = false\n dragState.value = { id: p.ev.id, mode, dMin: 0, dDay: 0 }\n window.addEventListener('pointermove', onDragMove)\n window.addEventListener('pointerup', onDragEnd)\n }\n onBeforeUnmount(onDragEnd)\n const previewStart = (p: Placed) => (dragState.value?.id === p.ev.id && dragState.value.mode === 'move' ? new Date(p.start.getTime() + dragState.value.dMin * 6e4) : p.start)\n const previewEnd = (p: Placed) => (dragState.value?.id === p.ev.id ? new Date(p.end.getTime() + dragState.value.dMin * 6e4) : p.end)\n const eventStyle = (p: Placed) => {\n const ds = dragState.value?.id === p.ev.id ? dragState.value : null\n const top = (p.top + (ds && ds.mode === 'move' ? ds.dMin : 0)) / 60 * props.hourHeight\n const height = Math.max(18, ((p.height + (ds && ds.mode === 'resize' ? ds.dMin : 0)) / 60) * props.hourHeight - 2)\n return {\n top: `${top}px`, height: `${height}px`,\n left: `calc(${(p.col / p.cols) * 100}% + 2px)`, width: `calc(${100 / p.cols}% - 4px)`,\n transform: ds && ds.dDay ? `translateX(${ds.dDay * 100}%)` : undefined,\n '--sq-ev': colorOf(p.ev),\n }\n }\n const onSlotClick = (e: MouseEvent, date: Date) => {\n const col = e.currentTarget as HTMLElement\n const minutes = snapMin(((e.clientY - col.getBoundingClientRect().top) / props.hourHeight) * 60)\n emit('click:time', new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, minutes))\n }\n const scrollToNow = () => nextTick(() => scroller.value && (scroller.value.scrollTop = Math.max(0, nowTop.value - 120)))\n onMounted(scrollToNow)\n watch(viewModel, (v) => v !== 'month' && scrollToNow())\n\n return {\n viewModel, viewLabels, weekdays, title, monthDays, columns, shift, today, goDay, colorOf, time, hourLabel, nowTop, dragId, dropKey, onMonthDrop,\n scroller, dragState, moved, startDrag, previewStart, previewEnd, eventStyle, onSlotClick, toUnit, messages: computed(() => config.locale.messages),\n }\n },\n})\n</script>\n\n<style>\n.sq-calendar { display: flex; flex-direction: column; overflow: hidden; color: var(--sq-on-surface); font-size: var(--sq-text-sm); min-width: 0; }\n.sq-calendar__toolbar { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sq-space-2); padding: var(--sq-space-3); border-bottom: 1px solid var(--sq-border); }\n.sq-calendar__title { font-size: var(--sq-text-lg); font-weight: var(--sq-weight-semibold); margin-inline-start: var(--sq-space-2); text-transform: capitalize; }\n.sq-calendar__month { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); grid-template-rows: auto; grid-auto-rows: minmax(104px, 1fr); }\n.sq-calendar__weekday { padding: 8px; text-align: center; font-size: var(--sq-text-2xs); font-weight: var(--sq-weight-bold); text-transform: uppercase; letter-spacing: 0.05em; color: var(--sq-muted); border-bottom: 1px solid var(--sq-border); min-height: 0; }\n.sq-calendar__month > .sq-calendar__weekday { grid-row: 1; }\n.sq-calendar__day { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 4px; border-inline-end: 1px solid var(--sq-border); border-bottom: 1px solid var(--sq-border); cursor: pointer; transition: background var(--sq-duration-fast); }\n.sq-calendar__day:nth-child(7n) { border-inline-end: 0; }\n.sq-calendar__day:hover { background: color-mix(in srgb, var(--sq-primary) 3%, transparent); }\n.sq-calendar__day--outside { background: color-mix(in srgb, var(--sq-surface-variant) 40%, transparent); color: var(--sq-muted); }\n.sq-calendar__day--drop { background: color-mix(in srgb, var(--sq-primary) 10%, transparent) !important; }\n.sq-calendar__date { --sq-r: var(--sq-radius-sm); align-self: flex-end; display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 26px; font-weight: var(--sq-weight-medium); font-variant-numeric: tabular-nums; }\n.sq-calendar__day--today .sq-calendar__date { background: var(--sq-primary); color: var(--sq-on-primary); }\n.sq-calendar__chip { --sq-r: var(--sq-radius-xs); display: block; padding: 2px 6px; font-size: var(--sq-text-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--sq-on-surface); background: color-mix(in srgb, var(--sq-ev) 14%, transparent); border-inline-start: 3px solid var(--sq-ev); cursor: pointer; }\n.sq-calendar__chip--allday { background: var(--sq-ev); color: #fff; border: 0; }\n.sq-calendar__chip-time { margin-inline-end: 4px; color: var(--sq-muted); font-variant-numeric: tabular-nums; }\n.sq-calendar__more { align-self: flex-start; padding: 0 6px; border: 0; background: none; color: var(--sq-primary); font: inherit; font-size: var(--sq-text-xs); font-weight: var(--sq-weight-semibold); cursor: pointer; }\n.sq-calendar__time-head, .sq-calendar__allday { display: grid; grid-template-columns: 56px repeat(var(--sq-cal-cols), minmax(0, 1fr)); border-bottom: 1px solid var(--sq-border); }\n.sq-calendar__col-head { display: flex; flex-direction: column; align-items: center; padding: 6px 0; font-size: var(--sq-text-2xs); color: var(--sq-muted); text-transform: uppercase; }\n.sq-calendar__col-head b { --sq-r: var(--sq-radius-sm); display: inline-flex; align-items: center; justify-content: center; min-width: 32px; height: 32px; font-size: var(--sq-text-lg); color: var(--sq-on-surface); }\n.sq-calendar__col-head--today b { background: var(--sq-primary); color: var(--sq-on-primary); }\n.sq-calendar__gutter { font-size: var(--sq-text-2xs); color: var(--sq-muted); text-align: end; padding-inline-end: 8px; }\n.sq-calendar__allday .sq-calendar__gutter { padding-top: 6px; }\n.sq-calendar__allday-col { display: flex; flex-direction: column; gap: 2px; padding: 3px; min-height: 28px; border-inline-start: 1px solid var(--sq-border); }\n.sq-calendar__scroll { overflow-y: auto; }\n.sq-calendar__grid { display: grid; grid-template-columns: 56px repeat(var(--sq-cal-cols), minmax(0, 1fr)); position: relative; }\n.sq-calendar__hour { position: relative; }\n.sq-calendar__hour span { position: absolute; top: -7px; right: 8px; }\n.sq-calendar__col { position: relative; border-inline-start: 1px solid var(--sq-border); }\n.sq-calendar__slot { border-bottom: 1px solid var(--sq-border); }\n.sq-calendar__slot:nth-child(odd) { border-bottom-style: solid; }\n.sq-calendar__now { position: absolute; left: 0; right: 0; height: 2px; background: var(--sq-error); z-index: 3; }\n.sq-calendar__now::before { content: ''; position: absolute; left: -5px; top: -4px; width: 10px; height: 10px; border-radius: 50%; background: var(--sq-error); }\n.sq-calendar__event { --sq-r: var(--sq-radius-sm); position: absolute; z-index: 2; overflow: hidden; padding: 4px 6px; background: color-mix(in srgb, var(--sq-ev) 18%, var(--sq-surface)); border-inline-start: 3px solid var(--sq-ev); color: var(--sq-on-surface); font-size: var(--sq-text-xs); cursor: pointer; touch-action: none; transition: box-shadow var(--sq-duration-fast); }\n.sq-calendar__event:hover { box-shadow: var(--sq-elevation-2); z-index: 4; }\n.sq-calendar__event--dragging { opacity: 0.85; box-shadow: var(--sq-elevation-3); z-index: 5; cursor: grabbing; }\n.sq-calendar__event-title { font-weight: var(--sq-weight-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.sq-calendar__event-time { color: var(--sq-on-surface-variant); font-variant-numeric: tabular-nums; }\n.sq-calendar__resize { position: absolute; left: 0; right: 0; bottom: 0; height: 6px; cursor: ns-resize; }\n</style>\n","import SqCalendar from './SqCalendar.vue'\n\nexport { SqCalendar }\nexport * from './layout'\nexport const componentNames = ['SqCalendar'] as const\nexport default SqCalendar\n"],"names":["defineComponent","SqBtn","SqBtnToggle","SqShape","vModel","useProxiedModel","toISODate","computed","config","weekdayNames","ref","onMounted","onBeforeUnmount","startOfWeek","addDays","monthGrid","isSameMonth","isSameDay","addMonths","colorValue","nextTick","watch","toUnit"],"mappings":"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"}