Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/assets/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"from": "Od",
"to": "Do",
"entireDay": "Celý den",
"more": "další…",
"repeat": {
"repeat": "Opakování",
"end": "Konec",
Expand Down
1 change: 1 addition & 0 deletions src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"from": "From",
"to": "To",
"entireDay": "Entire day",
"more": "more…",
"repeat": {
"repeat": "Repeat",
"end": "End",
Expand Down
3 changes: 2 additions & 1 deletion src/assets/locales/git.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"new": "New Commit",
"title": "Message",
"description": "Message \\n",
"location": "Origin"
"location": "Origin",
"more": "more…"
},
"saveBtn": "Commit",
"createBtn": "Init / Clone",
Expand Down
58 changes: 9 additions & 49 deletions src/components/AllDayBar.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { useEventModal } from '@/composables/modals/useEventModal';
import type { CalendarEvent } from '@/types/core';
import { getAllDayEndDate, getCurrentViewDatetime, isWholeDay } from '@/utils';
import { getCurrentViewDatetime } from '@/utils';
import { getVisibleDayRange, layoutEventsByDay } from '@/utils/allDayEventLayout';
import { DateTime } from 'luxon';
import { computed, useTemplateRef } from 'vue';
import { useRoute } from 'vue-router';
Expand Down Expand Up @@ -36,23 +37,6 @@ const todayGridColumn = computed(() => {
return '';
});

function visibleRange(event: CalendarEvent) {
const end = isWholeDay(event) ? getAllDayEndDate(event) : event.to;
const startIndex = event.from.startOf('day').diff(currentDate.value, 'days').days;
const endIndex = end.startOf('day').diff(currentDate.value, 'days').days;

if (endIndex < 0 || startIndex >= props.numOfDays) return null;

const firstDay = Math.max(0, startIndex);
const lastDay = Math.min(props.numOfDays - 1, endIndex);

return {
startIndex: firstDay,
endIndex: lastDay,
span: Math.max(1, lastDay - firstDay + 1),
};
}

function gridStyle(startIndex: number, span: number, row: number) {
return {
gridColumn: `${startIndex + 1} / span ${span}`,
Expand All @@ -69,42 +53,18 @@ function startCreate(event: PointerEvent) {
props.drag.startCreateAllDay(event, element);
}

const laidOutEvents = computed(() => {
const visibleEvents = props.events.flatMap((event) => {
const range = visibleRange(event);
return range ? [{ event, ...range }] : [];
});

visibleEvents.sort((a, b) => (a.startIndex !== b.startIndex ? a.startIndex - b.startIndex : b.span - a.span));

const rowEnds: number[] = [];
return visibleEvents.map((item) => {
let rowIndex = rowEnds.findIndex((endIndex) => endIndex < item.startIndex);

if (rowIndex === -1) {
rowIndex = rowEnds.length;
rowEnds.push(item.endIndex);
} else {
rowEnds[rowIndex] = item.endIndex;
}

const row = rowIndex + 1;

return {
event: item.event,
startIndex: item.startIndex,
endIndex: item.endIndex,
row,
gridStyle: gridStyle(item.startIndex, item.span, row),
};
});
});
const laidOutEvents = computed(() =>
layoutEventsByDay(props.events, currentDate.value, props.numOfDays).map((item) => ({
...item,
gridStyle: gridStyle(item.startIndex, item.span, item.row),
})),
);

const previewEvent = computed(() => {
const event = props.drag.active.value;
if (props.drag.mode.value !== 'create-all-day' || !event) return null;

const range = visibleRange(event);
const range = getVisibleDayRange(event, currentDate.value, props.numOfDays);
if (!range) return null;

const occupiedRows = new Set(
Expand Down
37 changes: 32 additions & 5 deletions src/components/MonthSideMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { monthNameLong } = useTranslation();
const route = useRoute();

const currentDatetime = ref<DateTime>(DateTime.now());
const isMonthView = computed(() => route.params.view === 'm');
const monthTracker = ref(currentDatetime.value.month);
const yearTracker = ref(currentDatetime.value.year);

Expand Down Expand Up @@ -60,6 +61,12 @@ watch(
const viewStart = getCurrentViewDatetime(route.params);
currentDatetime.value = viewStart;

if (isMonthView.value) {
monthTracker.value = viewStart.month;
yearTracker.value = viewStart.year;
return;
}

const viewEnd = viewStart.plus({
days: getViewLengthInDays(route.params),
});
Expand Down Expand Up @@ -102,6 +109,11 @@ function changeMonthNum(up: boolean) {
function jumpToInterval(clickedDay: DateTime) {
if (route.params.view === 'w') {
router.replace(getWeekAlignedRedirect(clickedDay));
} else if (isMonthView.value) {
router.replace({
name: 'calendar',
params: { view: 'm', year: clickedDay.year, month: clickedDay.month, day: clickedDay.day },
});
} else {
router.replace({ params: { year: clickedDay.year, month: clickedDay.month, day: clickedDay.day } });
}
Expand Down Expand Up @@ -131,11 +143,15 @@ function getIntervalFromDay(day: DateTime) {
const hoverInterval = computed(() => {
if (!hoveredDay.value) return null;

if (isMonthView.value) {
return { start: hoveredDay.value, end: hoveredDay.value };
}

return getIntervalFromDay(hoveredDay.value);
});

const pressedInterval = computed(() => {
if (!pressedDay.value) return null;
if (isMonthView.value || !pressedDay.value) return null;

return getIntervalFromDay(pressedDay.value);
});
Expand Down Expand Up @@ -172,10 +188,11 @@ const pressedInterval = computed(() => {
class="day"
:class="{
today: d.hasSame(DateTime.now(), 'day'),
'selected-day': isMonthView && d.hasSame(currentDatetime, 'day'),
'not-this-month': d.month !== monthTracker,
'in-range': d >= viewInterval.start && d <= viewInterval.end,
'range-start': d.hasSame(viewInterval.start, 'day'),
'range-end': d.hasSame(viewInterval.end, 'day'),
'in-range': !isMonthView && d >= viewInterval.start && d <= viewInterval.end,
'range-start': !isMonthView && d.hasSame(viewInterval.start, 'day'),
'range-end': !isMonthView && d.hasSame(viewInterval.end, 'day'),
'pressed-range': pressedInterval && d >= pressedInterval.start && d <= pressedInterval.end,
}"
@click="jumpToInterval(d)"
Expand Down Expand Up @@ -270,7 +287,7 @@ const pressedInterval = computed(() => {
text-decoration-thickness: 0.15rem;
text-underline-offset: 0.2rem;*/

&:not(.in-range) {
&:not(.in-range):not(.selected-day) {
color: var(--git-color);
}

Expand Down Expand Up @@ -306,6 +323,16 @@ const pressedInterval = computed(() => {
}
}

&.today.not-this-month:not(.selected-day) {
color: color-mix(in srgb, var(--git-color) 45%, transparent);
}

&.selected-day {
border-radius: var(--small-border-radius);
background-color: var(--git-bg-color);
color: var(--text-color-hard);
}

&.range-start {
border-top-left-radius: var(--small-border-radius);
border-bottom-left-radius: var(--small-border-radius);
Expand Down
23 changes: 18 additions & 5 deletions src/components/TopBar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { DateTime } from 'luxon';
import { getCurrentViewDatetime, getWeekAlignedRedirect, moveView } from '@/utils';

import { FiChevronLeft, FiChevronRight } from 'vue-icons-plus/fi';
Expand Down Expand Up @@ -28,19 +29,33 @@ watch(
{ immediate: true },
);
watch(view, (newView) => {
const current = getCurrentViewDatetime(router.currentRoute.value.params);

if (newView === 'w') {
// week view should be aligned
const current = getCurrentViewDatetime(router.currentRoute.value.params);
router.push(getWeekAlignedRedirect(current));
} else if (newView === 'm') {
router.push({
name: 'calendar',
params: { view: 'm', year: current.year, month: current.month, day: current.day },
});
} else {
router.push({
name: 'calendar',
params: { ...router.currentRoute.value.params, view: newView },
params: { view: newView, year: current.year, month: current.month, day: current.day },
});
}
});

function jumpToToday() {
if (view.value === 'm') {
const today = DateTime.now();
router.push({
name: 'calendar',
params: { view: 'm', year: today.year, month: today.month, day: today.day },
});
return;
}

router.push({ name: 'calendar', params: { view: view.value } });
}
</script>
Expand All @@ -51,12 +66,10 @@ function jumpToToday() {
<NewEventBtn v-if="!sidebar.isOpen.value" :large="!isMobile" />

<SyncStatus />
<!-- TODO: remove disabled -->
<MultiToggle
v-model="view"
:options="CALENDAR_VIEWS"
:labels="CALENDAR_VIEWS.map((view) => $t(`views.${view}.${isMobile ? 'short' : 'long'}`))"
:disabled="['m']"
name="view-selector"
/>

Expand Down
35 changes: 35 additions & 0 deletions src/components/monthview/MonthEvent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import BaseEvent from '@/components/timeline/BaseEvent.vue';
import { getTag } from '@/services/calendarCache';
import type { CalendarEvent } from '@/types/core';
import { isWholeDay, timeRangeFormat } from '@/utils';

defineOptions({ inheritAttrs: false });

const props = defineProps<{
event: CalendarEvent;
compact: boolean;
interactive: boolean;
temporary: boolean;
}>();

const attrs = useAttrs();
const showTime = computed(() => !isWholeDay(props.event));
const color = computed(() => getTag(props.event.calendar, props.event.tagId ?? '')?.color);
</script>

<template>
<BaseEvent
v-bind="attrs"
variant="month"
:title="event.title"
:subtitle="showTime ? timeRangeFormat(event.from, event.to) : ''"
:color="color"
:temporary="temporary"
:interactive="interactive"
:compact="compact"
:without-time="!showTime"
:show-subtitle="showTime"
/>
</template>
Loading