Skip to content

Commit 8a280e3

Browse files
authored
feat: Show midnight-spanning events in the following day column (#271)
1 parent 0dd2259 commit 8a280e3

File tree

9 files changed

+7193
-5823
lines changed

9 files changed

+7193
-5823
lines changed

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
contents: write
3939

4040
steps:
41-
- uses: fastify/github-action-merge-dependabot@v3.10.1
41+
- uses: fastify/github-action-merge-dependabot@v3.11.0
4242

4343
dry-release:
4444
if: github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' && (github.event_name == 'pull_request' || github.event_name == 'merge_group')

dev/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ const events = ref<CalendarEvent[]>(
110110
endDate,
111111
description: randomText(),
112112
color: randomColor(),
113-
readonly: true,
113+
readonly: false,
114114
};
115115
})
116116
);

package-lock.json

Lines changed: 6991 additions & 5666 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
],
88
"main": "./dist/time-blocks-vue.umd.cjs",
99
"module": "./dist/time-blocks-vue.js",
10+
"styles": "./dist/time-blocks-vue.css",
1011
"exports": {
11-
".": {
12-
"import": "./dist/time-blocks-vue.js",
13-
"require": "./dist/time-blocks-vue.umd.cjs"
14-
},
15-
"./style.css": "./dist/style.css"
12+
".": "./dist/time-blocks-vue.js",
13+
"./*": "./dist/*",
14+
"./styles": "./dist/time-blocks-vue.css",
15+
"./styles.css": "./dist/time-blocks-vue.css"
1616
},
1717
"types": "./dist/src/index.d.ts",
1818
"scripts": {
@@ -29,17 +29,17 @@
2929
"vue": "^3.3.12"
3030
},
3131
"devDependencies": {
32-
"@types/node": "^22.5.0",
33-
"@vitejs/plugin-vue": "5.1.2",
32+
"@types/node": "^22.10.3",
33+
"@vitejs/plugin-vue": "5.2.1",
3434
"@vue/eslint-config-typescript": "13.0.0",
35-
"eslint": "8.57.0",
36-
"sass": "^1.77.8",
37-
"eslint-plugin-vue": "9.27.0",
38-
"semantic-release": "^24.1.0",
35+
"eslint": "8.57.1",
36+
"sass": "^1.83.0",
37+
"eslint-plugin-vue": "9.32.0",
38+
"semantic-release": "^24.2.0",
3939
"typescript": "^5.4.5",
40-
"unplugin-vue-components": "^0.27.4",
41-
"vite": "^5.4.2",
42-
"vitepress": "^1.3.4",
43-
"vue-tsc": "^2.0.29"
40+
"unplugin-vue-components": "^0.27.5",
41+
"vite": "^6.0.6",
42+
"vitepress": "^1.5.0",
43+
"vue-tsc": "^2.1.10"
4444
}
4545
}

src/components/Day.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
:intervalHeight="intervalHeight"
3030
:intervalMinutes="intervalMinutes"
3131
:concurrencyMode="concurrencyMode"
32+
:column-date="date"
3233
@event-mousedown="(h: 'top' | 'bottom' | 'body') => emits('event-mousedown', event, h)"
3334
@event-mouseup="emits('event-mouseup')"
3435
@event-clicked="emits('event-clicked', event)"
@@ -51,7 +52,7 @@
5152
import DayEvent from "./DayEvent.vue";
5253
import { $CalendarEvent } from "../types/interfaces";
5354
import { computed, onMounted, onUnmounted, ref } from "vue";
54-
import { isSameDay, isToday } from "date-fns";
55+
import { isSameDay, isAfter } from "date-fns";
5556
import {
5657
calculatePositions,
5758
processConcurrency,
@@ -89,7 +90,11 @@ onUnmounted(() => {
8990
const isDateToday = computed(() => isSameDay(now.value, props.date));
9091
9192
const filteredEvents = computed(() => {
92-
let filtered = props.events.filter((e) => isSameDay(e.startDate, props.date));
93+
let filtered = props.events.filter(
94+
(e) =>
95+
isSameDay(e.startDate, props.date) ||
96+
(isSameDay(e.endDate, props.date) && isAfter(e.endDate, props.date))
97+
);
9398
return props.concurrencyMode == "stack"
9499
? processConcurrency(filtered)
95100
: calculatePositions(filtered);

src/components/DayEvent.vue

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
</div>
5555

5656
<div
57-
v-if="!event.readonly"
57+
v-if="!event.readonly && !restrictStart"
5858
style="
5959
top: 0px;
6060
position: absolute;
@@ -67,7 +67,7 @@
6767
@mousedown.stop="onMouseDown('top')"
6868
/>
6969
<div
70-
v-if="!event.readonly"
70+
v-if="!event.readonly && !restrictEnd"
7171
style="
7272
bottom: 0px;
7373
position: absolute;
@@ -118,7 +118,14 @@
118118
</template>
119119

120120
<script setup lang="ts">
121-
import { format, differenceInMinutes } from "date-fns";
121+
import {
122+
format,
123+
differenceInMinutes,
124+
isSameDay,
125+
endOfDay,
126+
startOfDay,
127+
addMinutes,
128+
} from "date-fns";
122129
import { $CalendarEvent } from "../types/interfaces";
123130
import {
124131
computed,
@@ -154,6 +161,7 @@ const emits = defineEmits<{
154161
155162
const props = defineProps<{
156163
event: $CalendarEvent;
164+
columnDate: Date;
157165
}>();
158166
159167
watch(hovering, (v) => {
@@ -176,19 +184,47 @@ onUnmounted(() => {
176184
removeEventListener("wheel", onMouseWheel);
177185
});
178186
187+
const minutesPastMidnight = computed(() =>
188+
differenceInMinutes(props.event.endDate, endOfDay(props.columnDate))
189+
);
190+
191+
const spansMidnight = computed(
192+
() => !isSameDay(props.event.startDate, props.event.endDate)
193+
);
194+
195+
const restrictStart = computed(
196+
() => spansMidnight.value && isSameDay(props.event.endDate, props.columnDate)
197+
);
198+
199+
const restrictEnd = computed(
200+
() => minutesPastMidnight.value > config.value.hoursPastMidnight * 60
201+
);
202+
179203
const zIndex = computed(() => (bringToFront.value ? 99 : props.event.zIndex));
180204
181205
const top = computed(() => {
182206
return Math.round(
183-
(props.event.startDate.getHours() * 60 +
184-
props.event.startDate.getMinutes()) *
185-
(config.value.intervalHeight / config.value.intervalMinutes)
207+
restrictStart.value
208+
? 0
209+
: (props.event.startDate.getHours() * 60 +
210+
props.event.startDate.getMinutes()) *
211+
(config.value.intervalHeight / config.value.intervalMinutes)
186212
);
187213
});
188214
189215
const height = computed(() => {
190-
let h =
191-
differenceInMinutes(props.event.endDate, props.event.startDate) *
216+
const start = restrictStart.value
217+
? startOfDay(props.event.endDate)
218+
: props.event.startDate;
219+
const end = restrictEnd.value
220+
? addMinutes(
221+
endOfDay(props.event.startDate),
222+
Math.min(minutesPastMidnight.value, config.value.hoursPastMidnight * 60)
223+
)
224+
: props.event.endDate;
225+
226+
const h =
227+
differenceInMinutes(end, start) *
192228
(config.value.intervalHeight / config.value.intervalMinutes);
193229
return Math.max(h, config.value.intervalHeight * 0.5);
194230
});
@@ -234,9 +270,6 @@ function onMouseMove(e: MouseEvent) {
234270
235271
function onMouseDown(handle: "top" | "bottom" | "body") {
236272
showTooltip.value = false;
237-
if (props.event.readonly === true) {
238-
return;
239-
}
240273
document.addEventListener("mouseup", onMouseUp);
241274
emits("event-mousedown", handle);
242275
}
@@ -303,6 +336,7 @@ const tooltipOffset = ref(0);
303336
.event-card:hover {
304337
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15), 0 8px 16px rgba(0, 0, 0, 0.15);
305338
}
339+
306340
.event-card-root > * {
307341
box-sizing: border-box;
308342
width: 100%;

0 commit comments

Comments
 (0)