@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.
- package/LICENSE +21 -0
- package/README.md +33 -2
- package/dist/styles.css +79 -0
- package/dist/types/SqCalendar.vue.d.ts +605 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/layout.d.ts +28 -0
- package/dist/vue2/index.cjs +312 -0
- package/dist/vue2/index.cjs.map +1 -0
- package/dist/vue2/index.mjs +312 -0
- package/dist/vue2/index.mjs.map +1 -0
- package/dist/vue3/index.cjs +651 -0
- package/dist/vue3/index.cjs.map +1 -0
- package/dist/vue3/index.mjs +651 -0
- package/dist/vue3/index.mjs.map +1 -0
- package/dist/vue3/styles.css +79 -0
- package/package.json +60 -3
|
@@ -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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"}
|