Skip to content

Commit 63f758c

Browse files
committed
fix(calendar): correct extraneous placeholder at the end of the range
1 parent e0c46b7 commit 63f758c

File tree

5 files changed

+144
-22
lines changed

5 files changed

+144
-22
lines changed

src/components/views/calendar/CalendarComponent.vue

+24-7
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,30 @@ import { watchUserChange } from '~/utils/store.utils';
1818
1919
const i18n = useI18n('calendar');
2020
21-
const { calendar, loading } = useCalendarStoreRefs();
21+
const { calendar, loading, center } = useCalendarStoreRefs();
2222
const { fetchCalendar, clearState } = useCalendarStore();
2323
2424
const list = useListScroll(calendar, 'date');
2525
26-
const today = computed(() => {
26+
const centerItem = computed(() => {
2727
return list.value.find(
28-
item => item.date?.current.toLocaleDateString() === new Date().toLocaleDateString(),
28+
item => item.date?.current.toLocaleDateString() === center.value.toLocaleDateString(),
29+
);
30+
});
31+
32+
const centerIsToday = computed(() => {
33+
return (
34+
centerItem.value?.date?.current.toLocaleDateString() ===
35+
new Date().toLocaleDateString()
2936
);
3037
});
3138
3239
const listRef = ref<{ list: VirtualListRef }>();
3340
34-
const scrollTo = (options?: VirtualListScrollToOptions, index = today.value?.index) => {
41+
const scrollTo = (
42+
options?: VirtualListScrollToOptions,
43+
index = centerItem.value?.index,
44+
) => {
3545
if (index === undefined) return;
3646
if (!listRef.value?.list) return;
3747
@@ -42,6 +52,7 @@ const scrollTo = (options?: VirtualListScrollToOptions, index = today.value?.ind
4252
};
4353
4454
const reload = async () => {
55+
console.info('reload', center.value);
4556
const promise = fetchCalendar();
4657
// watch for loading changes and recenter
4758
const unsub = watch(list, async () => scrollTo());
@@ -50,8 +61,14 @@ const reload = async () => {
5061
unsub();
5162
};
5263
64+
watch(center, () => reload());
65+
5366
watchUserChange({
54-
fetch: reload,
67+
mounted: reload,
68+
activated: async changed => {
69+
console.info('activated', changed);
70+
if (changed) await reload();
71+
},
5572
userChange: async active => {
5673
clearState();
5774
if (active) await reload();
@@ -91,7 +108,7 @@ const onScrollBottom = async () => {
91108
:loading="loading"
92109
:scroll-threshold="300"
93110
episode
94-
:scroll-into-view="today?.id ? [today?.id] : []"
111+
:scroll-into-view="centerItem?.id ? [centerItem?.id] : []"
95112
@on-scroll-into-view="e => onScrollIntoOutOfView(false, e.ref)"
96113
@on-scroll-out-of-view="e => onScrollIntoOutOfView(true, e.ref)"
97114
@on-scroll-top="onScrollTop"
@@ -107,7 +124,7 @@ const onScrollBottom = async () => {
107124
:icon="recenterIcon"
108125
@on-click="onClick"
109126
>
110-
{{ i18n('recenter', 'common', 'button') }}
127+
{{ i18n(centerIsToday ? 'today' : 'recenter', 'common', 'button') }}
111128
</FloatingButton>
112129
</div>
113130
</template>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,99 @@
1-
<script setup lang="ts"></script>
1+
<script setup lang="ts">
2+
import { NDatePicker, NFlex, NIcon, NInput } from 'naive-ui';
3+
4+
import { computed, defineProps, ref } from 'vue';
5+
6+
import IconCalendar from '~/components/icons/IconCalendar.vue';
7+
import IconChevron from '~/components/icons/IconChevronDownSmall.vue';
8+
9+
import IconLoop from '~/components/icons/IconLoop.vue';
10+
11+
import { useCalendarStore, useCalendarStoreRefs } from '~/stores/data/calendar.store';
12+
import { useI18n } from '~/utils';
13+
import { useDebouncedSearch } from '~/utils/store.utils';
14+
15+
defineProps({
16+
parentElement: {
17+
type: HTMLElement,
18+
required: false,
19+
},
20+
});
21+
22+
const i18n = useI18n('navbar');
23+
24+
const { filter, center } = useCalendarStoreRefs();
25+
const { clearState, fetchCalendar } = useCalendarStore();
26+
27+
const debouncedSearch = useDebouncedSearch(filter);
28+
29+
const pickerValue = computed({
30+
get: () => center.value.getTime(),
31+
set: (value: number) => {
32+
const newDate = value ? new Date(value) : new Date();
33+
if (newDate.toLocaleDateString() === center.value.toLocaleDateString()) return;
34+
console.info('pickerValue', new Date(value));
35+
clearState(value ? new Date(value) : undefined);
36+
},
37+
});
38+
39+
const open = ref(false);
40+
41+
const onDateChange = (value?: number) => {
42+
console.info('onDateChange', value);
43+
if (!value) return;
44+
console.info('onDateChange', value);
45+
};
46+
</script>
247

348
<template>
4-
<div>Calendar Navbar</div>
49+
<NFlex class="row" align="center" justify="space-evenly" :vertical="false">
50+
<NDatePicker
51+
v-model:show="open"
52+
v-model:value="pickerValue"
53+
class="date-picker"
54+
type="date"
55+
:to="parentElement"
56+
placement="bottom"
57+
update-value-on-close
58+
close-on-select
59+
clearable
60+
>
61+
<template #date-icon>
62+
<NIcon :component="open ? IconChevron : IconCalendar" />
63+
</template>
64+
</NDatePicker>
65+
<NInput
66+
v-model:value="debouncedSearch"
67+
class="search-input"
68+
:placeholder="i18n('search')"
69+
autosize
70+
clearable
71+
>
72+
<template #prefix>
73+
<NIcon :component="IconLoop" />
74+
</template>
75+
</NInput>
76+
</NFlex>
577
</template>
78+
79+
<style lang="scss" scoped>
80+
.row {
81+
width: 100%;
82+
padding: 0 0.5rem;
83+
84+
.date-picker {
85+
flex: 0 1 48%;
86+
}
87+
88+
.search-input {
89+
flex: 1 1 50%;
90+
}
91+
}
92+
</style>
93+
94+
<style lang="scss">
95+
.n-date-panel {
96+
margin-top: 12px;
97+
margin-left: -16px;
98+
}
99+
</style>

src/i18n/en/common/button.json

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"description": "Button to scroll to the top of the history."
55
},
66
"common__button__recenter": {
7+
"message": "Recenter",
8+
"description": "Button to recenter the timeline on the initial center."
9+
},
10+
"common__button__today": {
711
"message": "Today",
812
"description": "Button to recenter the timeline on today."
913
}

src/stores/data/calendar.store.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const getPlaceholder = (date: Date) => ({ ...CalendarPlaceholder, id: `empty-${d
2828
const getLoadingPlaceholder = (date: Date) =>
2929
({ ...getPlaceholder(date), id: `loading-${date.getTime()}`, type: ListScrollItemType.loading }) as CalendarItem;
3030

31-
export const getEmptyWeeks = (fromDate = DateUtils.weeks.previous(1), loading?: boolean) => {
31+
export const getEmptyWeeks = (fromDate: Date, loading?: boolean) => {
3232
return Array(14)
3333
.fill(CalendarPlaceholder)
3434
.map((_, index) => {
@@ -42,16 +42,20 @@ export const useCalendarStore = defineStore('data.calendar', () => {
4242
const loading = ref(true);
4343
const calendar = ref<CalendarItem[]>([]);
4444

45-
const startCalendar = ref<Date>(DateUtils.weeks.previous(1));
46-
const endCalendar = ref<Date>(DateUtils.weeks.next(1));
45+
const center = ref(new Date());
46+
const startCalendar = ref<Date>(DateUtils.weeks.previous(1, center.value));
47+
const endCalendar = ref<Date>(DateUtils.weeks.next(1, center.value));
4748

4849
const weeks = ref(1);
4950
const days = computed(() => weeks.value * 7 * 2);
5051

51-
const clearState = () => {
52+
const filter = ref('');
53+
54+
const clearState = (date: Date = new Date()) => {
5255
calendar.value = [];
53-
startCalendar.value = DateUtils.weeks.previous(1);
54-
endCalendar.value = DateUtils.weeks.next(1);
56+
center.value = date;
57+
startCalendar.value = DateUtils.weeks.previous(1, center.value);
58+
endCalendar.value = DateUtils.weeks.next(1, center.value);
5559
};
5660

5761
const saveState = async () =>
@@ -85,7 +89,7 @@ export const useCalendarStore = defineStore('data.calendar', () => {
8589
if (mode === 'start') startCalendar.value = DateUtils.previous(days.value, startCalendar.value);
8690

8791
const startDate = ['start', 'reload'].includes(mode) ? startCalendar.value : endCalendar.value;
88-
const endDate = DateUtils.next(days.value, startDate);
92+
const endDate = DateUtils.next(days.value - 1, startDate);
8993
const start_date = startDate.toISOString().split('T')[0];
9094

9195
if (mode === 'end') endCalendar.value = DateUtils.next(days.value, endCalendar.value);
@@ -140,7 +144,7 @@ export const useCalendarStore = defineStore('data.calendar', () => {
140144
newData.forEach((item, index) => {
141145
if (index === 0) {
142146
// if the first item isn't the start date, add placeholders
143-
if (item.date.toLocaleDateString() !== startDate.toLocaleDateString()) {
147+
if (item.date.getTime() > startDate.getTime() && item.date.toLocaleDateString() !== startDate.toLocaleDateString()) {
144148
let previousDate: Date = item.date;
145149
while (previousDate.toLocaleDateString() !== startDate.toLocaleDateString()) {
146150
previousDate = DateUtils.previous(1, previousDate);
@@ -155,12 +159,11 @@ export const useCalendarStore = defineStore('data.calendar', () => {
155159

156160
// if the last item isn't one day before the end date, add placeholders
157161
const dayBeforeEnd = DateUtils.previous(1, endDate);
158-
if (item.date.toLocaleDateString() !== dayBeforeEnd.toLocaleDateString()) {
162+
if (item.date.getTime() < dayBeforeEnd.getTime() && item.date.toLocaleDateString() !== dayBeforeEnd.toLocaleDateString()) {
159163
let nextDate: Date = item.date;
160164
while (nextDate.toLocaleDateString() !== dayBeforeEnd.toLocaleDateString()) {
161165
nextDate = DateUtils.next(1, nextDate);
162166
spacedData.push(getPlaceholder(nextDate));
163-
console.info('Adding placeholder', { nextDate: nextDate.toLocaleDateString() });
164167
}
165168
}
166169
return;
@@ -200,7 +203,7 @@ export const useCalendarStore = defineStore('data.calendar', () => {
200203
}
201204
};
202205

203-
return { clearState, saveState, restoreState, loading, pageSize: weeks, calendar, startCalendar, endCalendar, fetchCalendar };
206+
return { clearState, saveState, restoreState, loading, pageSize: weeks, calendar, startCalendar, endCalendar, fetchCalendar, filter, center };
204207
});
205208

206209
export const useCalendarStoreRefs = () => storeToRefs(useCalendarStore());

src/utils/store.utils.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,24 @@ export const watchUserChange = ({
6767
fetch?: () => Promise<void>;
6868
clear?: () => void | Promise<void>;
6969
mounted?: () => void | Promise<void>;
70-
activated?: () => void | Promise<void>;
70+
activated?: (changed?: boolean) => void | Promise<void>;
7171
deactivated?: () => void | Promise<void>;
7272
userChange?: (active: boolean) => void | Promise<void>;
7373
}) => {
7474
const { user } = useUserSettingsStoreRefs();
7575

7676
const active = ref(false);
77+
const changed = ref(false);
7778

7879
onActivated(async () => {
7980
active.value = true;
80-
if (activated) return activated();
81+
if (activated) return activated(changed.value);
8182
await fetch?.();
8283
});
8384

8485
onDeactivated(() => {
8586
active.value = false;
87+
changed.value = false;
8688
deactivated?.();
8789
});
8890

@@ -92,6 +94,8 @@ export const watchUserChange = ({
9294
user,
9395

9496
async () => {
97+
if (!active.value) changed.value = true;
98+
9599
if (userChange) return userChange(active.value);
96100
if (active.value) await fetch?.();
97101
else clear?.();

0 commit comments

Comments
 (0)