From ca76a7556270e3d8d2a9da14a765e9c19076f030 Mon Sep 17 00:00:00 2001 From: quirinecker Date: Mon, 12 May 2025 11:31:54 +0200 Subject: [PATCH] ported to luxon library --- "web/\\" | 143 ++++++++++++++++++ web/bun.lock | 4 + web/components/ui/MainContent.vue | 3 +- web/components/ui/Sidebar.vue | 9 +- web/components/ui/calendar/Calendar.vue | 38 +++-- .../ui/calendar/CalendarCollumn.vue | 19 ++- web/components/ui/calendar/CalendarEvent.vue | 2 +- .../ui/calendar/CalendarSeperator.vue | 6 +- web/package.json | 2 + web/pages/index.vue | 4 +- web/utils/event.ts | 35 +++-- web/utils/lib.ts | 3 +- 12 files changed, 211 insertions(+), 57 deletions(-) create mode 100644 "web/\\" diff --git "a/web/\\" "b/web/\\" new file mode 100644 index 0000000..7784bf7 --- /dev/null +++ "b/web/\\" @@ -0,0 +1,143 @@ +import { DateTime } from "luxon" +import type { Moment } from "moment" +import moment from "moment" + +export class Event { + private static readonly MINUTES_IN_DAY = 24 * 60 + + constructor( + public readonly title: string, + public from: DateTime, + public to: DateTime + ) { } + + + getPercentDimensions(): EventDimensions { + const start_of_day = this.from.startOf('day') + const from_percentage = this.from.diff(start_of_day, 'minutes').minutes / Event.MINUTES_IN_DAY + const to_percentage = this.to.diff(start_of_day, 'minutes').minutes / Event.MINUTES_IN_DAY + return { + from: from_percentage * 100, + to: to_percentage * 100 + } + } + + getPixelDimensions(maxHeight: number): EventDimensions { + const percentDimensions = this.getPercentDimensions() + return { + from: (percentDimensions.from / 100) * maxHeight, + to: (percentDimensions.to / 100) * maxHeight + } + } + + static fromSimple(event: SimpleEvent): Event { + return new Event(event.title, event.from, event.to) + } + + static fromSerializable(event: SerializableEvent) { + return new Event(event.title, DateTime.fromISO(event.from), DateTime.fromISO(event.to)) + } + + static fromPercentDimensions(title: string, dimensions: EventDimensions, date: DateTime): Event { + return new Event( + title, + date.startOf('day').plus({ minutes: (dimensions.from / 100) * Event.MINUTES_IN_DAY }), + date.startOf('day').plus({ minutes: (dimensions.to / 100) * Event.MINUTES_IN_DAY }) + ) + } + + static fromPixelDimensions(title: string, dimensions: EventDimensions, height: number, date: DateTime): Event { + const percentDimensions: EventDimensions = { + from: dimensions.from * 100 / height, + to: dimensions.to * 100 / height + } + + return Event.fromPercentDimensions(title, percentDimensions, date) + } + + static fromDraggedEvent(draggedEvent: DraggedEvent, height: number): Event { + const pixelDimensions: EventDimensions = { + from: draggedEvent.top, + to: draggedEvent.top + draggedEvent.height + } + + return Event.fromPixelDimensions(draggedEvent.target.title, pixelDimensions, height, draggedEvent.date) + } + + updateWithDraggedEvent(draggedEvent: DraggedEvent, height: number): Event { + const newEventData = Event.fromDraggedEvent(draggedEvent, height) + + this.from = newEventData.from + this.to = newEventData.to + + return this + } + + + toSimple(): SimpleEvent { + return { + title: this.title, + from: this.from, + to: this.to + } + } + + toSerializable(): SerializableEvent { + return { + title: this.title, + from: this.from.toISO() ?? '', + to: this.to.toISO() ?? '' + } + } + + withCollisions(collisions: number = 0): CollissionWrapper { + return { + event: this, + collisions + } + } + + toDraggedEvent(height: number, mouseY: number): DraggedEvent { + const pixelDimensions = this.getPixelDimensions(height) + const offset = mouseY - pixelDimensions.from + + return { + date: moment(this.from).startOf('day'), + top: pixelDimensions.from, + height: pixelDimensions.to - pixelDimensions.from, + target: this, + offset: offset + } + } + +} + +export type EventDimensions = { + from: number, + to: number +} + +export type SimpleEvent = { + title: string, + from: DateTime, + to: DateTime +} + +export type SerializableEvent = { + title: string, + from: string, + to: string +} + +export type CollissionWrapper = { + event: Event, + collisions: number +} + +export type DraggedEvent = { + date: DateTime, + top: number, + height: number, + target: Event, + offset: number +} diff --git a/web/bun.lock b/web/bun.lock index f92b2ab..11fa3fc 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -10,8 +10,10 @@ "@nuxt/test-utils": "3.18.0", "@nuxt/ui": "3.1.1", "@nuxtjs/color-mode": "3.5.2", + "@types/luxon": "^3.6.2", "@types/moment": "^2.13.0", "eslint": "^9.0.0", + "luxon": "^3.6.1", "moment": "^2.30.1", "nuxt": "^3.17.2", "nuxt-app": "file:", @@ -474,6 +476,8 @@ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="], + "@types/moment": ["@types/moment@2.13.0", "", { "dependencies": { "moment": "*" } }, "sha512-DyuyYGpV6r+4Z1bUznLi/Y7HpGn4iQ4IVcGn8zrr1P4KotKLdH0sbK1TFR6RGyX6B+G8u83wCzL+bpawKU/hdQ=="], "@types/node": ["@types/node@22.15.12", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-K0fpC/ZVeb8G9rm7bH7vI0KAec4XHEhBam616nVJCV51bKzJ6oA3luG4WdKoaztxe70QaNjS/xBmcDLmr4PiGw=="], diff --git a/web/components/ui/MainContent.vue b/web/components/ui/MainContent.vue index d7da085..8eeb211 100644 --- a/web/components/ui/MainContent.vue +++ b/web/components/ui/MainContent.vue @@ -3,9 +3,10 @@ import type { Moment } from 'moment'; import Calendar from './calendar/Calendar.vue' import { Event } from '~/utils/event'; import { UCard } from '#components'; +import type { DateTime } from 'luxon'; const events = defineModel('events', { required: true }) -const date = defineModel('date', { required: true }) +const date = defineModel('date', { required: true }) diff --git a/web/components/ui/Sidebar.vue b/web/components/ui/Sidebar.vue index 30435b1..6528c42 100644 --- a/web/components/ui/Sidebar.vue +++ b/web/components/ui/Sidebar.vue @@ -3,8 +3,7 @@ import { CalendarDate } from '@internationalized/date'; import ListItem from './ListItem.vue'; import Title1 from './Title1.vue'; import type { DropdownMenuItem } from '@nuxt/ui'; -import type { Moment } from 'moment'; -import moment from 'moment'; +import { DateTime } from 'luxon'; const colorMode = useColorMode(); const currentTheme = ref<'dark' | 'system' | 'light'>(colorMode.preference as 'dark' | 'system' | 'light'); @@ -56,17 +55,17 @@ const dropDownItems = computed(() => [ ] ]) -const date = defineModel('date', { required: true }) +const date = defineModel('date', { required: true }) const selectedDate = computed({ get() { - return new CalendarDate(date.value.year(), date.value.month() + 1, date.value.date()) + return new CalendarDate(date.value.year, date.value.month, date.value.day) }, set(value) { if (value === undefined) { return } - date.value = moment(value.toString()); + date.value = DateTime.fromISO(value.toString()); } }) diff --git a/web/components/ui/calendar/Calendar.vue b/web/components/ui/calendar/Calendar.vue index a3b8b62..7117753 100644 --- a/web/components/ui/calendar/Calendar.vue +++ b/web/components/ui/calendar/Calendar.vue @@ -2,20 +2,20 @@ import { computed, ref } from 'vue'; import CalendarHeader from './CalendarHeader.vue'; import CalendarCollumn from './CalendarCollumn.vue'; -import moment, { type Moment } from 'moment'; import { Event } from '~/utils/event'; +import { DateTime } from 'luxon'; const events = defineModel('events', { required: true }) -const date = defineModel('date', { required: true }) +const date = defineModel('date', { required: true }) const draggedEvent = ref() type Day = { - date: Moment + date: DateTime events: CollissionWrapper[][] } const week = computed(() => { - return moment(date.value).startOf('isoWeek') + return date.value.startOf('week') }) function pushEventWithCollisionUpdate(array: CollissionWrapper[], event: Event, collisions: CollissionWrapper[], collisionCount: number) { @@ -28,8 +28,9 @@ function pushEventWithCollisionUpdate(array: CollissionWrapper[], event: Event, const days = computed(() => { return [1, 2, 3, 4, 5, 6, 7].map((i) => { + const currentDate = date.value.startOf('week').plus({ day: i - 1 }) const filteredEvents = events.value.filter( - (event) => event.from >= moment(week.value).weekday(i).startOf('day') && event.to <= moment(week.value).weekday(i).endOf('day') + (event) => event.from >= currentDate.startOf('day') && event.to <= currentDate.endOf('day') ) const sortedEvents = filteredEvents.sort((a, b) => a.from.valueOf() - b.from.valueOf()) @@ -62,7 +63,7 @@ const days = computed(() => { } return { - date: moment(week.value).weekday(i), + date: currentDate, events: columns } }) @@ -72,17 +73,22 @@ const emits = defineEmits<{ (e: 'create', timespan: Event): void }>() +const hour = (num: number) => { + return DateTime.now().startOf('day').plus({ hours: num }) +} + const seperators = ref([ - { text: '3 AM', time: moment().hour(3) }, - { text: '6 AM', time: moment().hour(6) }, - { text: '9 AM', time: moment().hour(9) }, - { text: '12 PM', time: moment().hour(12) }, - { text: '3 PM', time: moment().hour(15) }, - { text: '6 PM', time: moment().hour(18) }, - { text: '9 PM', time: moment().hour(21) }, + { text: '3 AM', time: hour(3) }, + { text: '6 AM', time: hour(6) }, + { text: '9 AM', time: hour(9) }, + { text: '12 PM', time: hour(12) }, + { text: '3 PM', time: hour(15) }, + { text: '6 PM', time: hour(18) }, + { text: '9 PM', time: hour(21) }, ]) -function quickCreate(date: Moment, timespan: Timespan) { + +function quickCreate(date: DateTime, timespan: Timespan) { const eventTitle = prompt("Event title") if (eventTitle === null) { @@ -91,8 +97,8 @@ function quickCreate(date: Moment, timespan: Timespan) { const newEvent: Event = new Event( eventTitle, - moment(date).startOf('day').minutes(timespan.from * 24 * 60), - moment(date).startOf('day').minutes(timespan.to * 24 * 60) + date.startOf('day').plus({ minutes: timespan.from * 24 * 60 }), + date.startOf('day').plus({ minutes: timespan.to * 24 * 60 }) ) emits('create', newEvent) diff --git a/web/components/ui/calendar/CalendarCollumn.vue b/web/components/ui/calendar/CalendarCollumn.vue index 740fc26..558c7bf 100644 --- a/web/components/ui/calendar/CalendarCollumn.vue +++ b/web/components/ui/calendar/CalendarCollumn.vue @@ -1,19 +1,18 @@ diff --git a/web/package.json b/web/package.json index 75f330b..70aeae8 100644 --- a/web/package.json +++ b/web/package.json @@ -16,8 +16,10 @@ "@nuxt/test-utils": "3.18.0", "@nuxt/ui": "3.1.1", "@nuxtjs/color-mode": "3.5.2", + "@types/luxon": "^3.6.2", "@types/moment": "^2.13.0", "eslint": "^9.0.0", + "luxon": "^3.6.1", "moment": "^2.30.1", "nuxt": "^3.17.2", "nuxt-app": "file:", diff --git a/web/pages/index.vue b/web/pages/index.vue index 9ef4719..f6bd1a0 100644 --- a/web/pages/index.vue +++ b/web/pages/index.vue @@ -1,11 +1,11 @@ diff --git a/web/utils/event.ts b/web/utils/event.ts index 4d4b1a8..d0ac1f7 100644 --- a/web/utils/event.ts +++ b/web/utils/event.ts @@ -1,20 +1,19 @@ -import type { Moment } from "moment" -import moment from "moment" +import { DateTime } from "luxon" export class Event { private static readonly MINUTES_IN_DAY = 24 * 60 constructor( public readonly title: string, - public from: Moment, - public to: Moment + public from: DateTime, + public to: DateTime ) { } getPercentDimensions(): EventDimensions { - const start_of_day = moment(this.from).startOf('day') - const from_percentage = this.from.diff(start_of_day, 'minutes') / Event.MINUTES_IN_DAY - const to_percentage = this.to.diff(start_of_day, 'minutes') / Event.MINUTES_IN_DAY + const start_of_day = this.from.startOf('day') + const from_percentage = (this.from.diff(start_of_day, 'minutes').minutes) / Event.MINUTES_IN_DAY + const to_percentage = (this.to.diff(start_of_day, 'minutes').minutes) / Event.MINUTES_IN_DAY return { from: from_percentage * 100, to: to_percentage * 100 @@ -34,18 +33,18 @@ export class Event { } static fromSerializable(event: SerializableEvent) { - return new Event(event.title, moment(event.from), moment(event.to)) + return new Event(event.title, DateTime.fromISO(event.from), DateTime.fromISO(event.to)) } - static fromPercentDimensions(title: string, dimensions: EventDimensions, date: Moment): Event { + static fromPercentDimensions(title: string, dimensions: EventDimensions, date: DateTime): Event { return new Event( title, - moment(date).startOf('day').minutes((dimensions.from / 100) * Event.MINUTES_IN_DAY), - moment(date).startOf('day').minutes((dimensions.to / 100) * Event.MINUTES_IN_DAY) + date.startOf('day').plus({ minutes: (dimensions.from / 100) * Event.MINUTES_IN_DAY }), + date.startOf('day').plus({ minutes: (dimensions.to / 100) * Event.MINUTES_IN_DAY }) ) } - static fromPixelDimensions(title: string, dimensions: EventDimensions, height: number, date: Moment): Event { + static fromPixelDimensions(title: string, dimensions: EventDimensions, height: number, date: DateTime): Event { const percentDimensions: EventDimensions = { from: dimensions.from * 100 / height, to: dimensions.to * 100 / height @@ -84,8 +83,8 @@ export class Event { toSerializable(): SerializableEvent { return { title: this.title, - from: this.from.toISOString(), - to: this.to.toISOString() + from: this.from.toISO() ?? '', + to: this.to.toISO() ?? '' } } @@ -101,7 +100,7 @@ export class Event { const offset = mouseY - pixelDimensions.from return { - date: moment(this.from).startOf('day'), + date: this.from.startOf('day'), top: pixelDimensions.from, height: pixelDimensions.to - pixelDimensions.from, target: this, @@ -118,8 +117,8 @@ export type EventDimensions = { export type SimpleEvent = { title: string, - from: Moment, - to: Moment + from: DateTime, + to: DateTime } export type SerializableEvent = { @@ -134,7 +133,7 @@ export type CollissionWrapper = { } export type DraggedEvent = { - date: Moment, + date: DateTime, top: number, height: number, target: Event, diff --git a/web/utils/lib.ts b/web/utils/lib.ts index ab530bb..44aa5fc 100644 --- a/web/utils/lib.ts +++ b/web/utils/lib.ts @@ -1,8 +1,9 @@ +import type { DateTime } from "luxon" import type { Moment } from "moment" export type Seperator = { text: string, - time: Moment + time: DateTime } export type Timespan = {