This commit is contained in:
syuilo 2025-05-10 15:28:06 +09:00
parent f25bc9bd95
commit c98d72e6e1
2 changed files with 74 additions and 50 deletions

View File

@ -69,25 +69,28 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-if="foldersPaginator.canFetchOlder" ref="moreFolders" @click="foldersPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
</div>
<div v-show="filesPaginator.items.value.length > 0" ref="filesContainer" :class="$style.files">
<div v-for="(file, i) in filesPaginator.items.value" :key="file.id">
<div v-if="i > 0 && isSeparatorNeeded(filesPaginator.items.value[i -1].createdAt, file.createdAt)" :class="$style.date">
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(filesPaginator.items.value[i -1].createdAt, file.createdAt).prevText }}</span>
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
<span>{{ getSeparatorInfo(filesPaginator.items.value[i -1].createdAt, file.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
<div v-show="filesPaginator.items.value.length > 0" ref="filesContainer">
<MkStickyContainer v-for="(item, i) in filesTimeline" :key="item.date.toISOString()">
<template #header>
<div :class="$style.date">
<span><i class="ti ti-chevron-down"></i> {{ item.date.getFullYear() }}/{{ item.date.getMonth() + 1 }}/{{ item.date.getDate() }}</span>
</div>
</template>
<div :class="$style.files">
<XFile
v-for="file in item.items" :key="file.id"
:class="$style.file"
:file="file"
:folder="folder"
:selectMode="select === 'file'"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@chosen="chooseFile"
@dragstart="isDragSource = true"
@dragend="isDragSource = false"
/>
</div>
<XFile
v-else
:class="$style.file"
:file="file"
:folder="folder"
:selectMode="select === 'file'"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@chosen="chooseFile"
@dragstart="isDragSource = true"
@dragend="isDragSource = false"
/>
</div>
</MkStickyContainer>
<MkButton v-show="filesPaginator.canFetchOlder" ref="loadMoreFiles" @click="filesPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
</div>
@ -121,7 +124,7 @@ import { claimAchievement } from '@/utility/achievements.js';
import { prefer } from '@/preferences.js';
import { chooseFileFromPc } from '@/utility/select-file.js';
import { store } from '@/store.js';
import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-separate.js';
import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
import { usePagination } from '@/composables/use-pagination.js';
const props = withDefaults(defineProps<{
@ -183,6 +186,8 @@ const foldersPaginator = usePagination({
autoInit: false,
});
const filesTimeline = makeDateGroupedTimelineComputedRef(filesPaginator.items, 'month');
watch(folder, () => emit('cd', folder.value));
watch(sortModeSelect, () => {
initialize();
@ -725,22 +730,9 @@ onBeforeUnmount(() => {
.folders,
.files {
display: flex;
flex-wrap: wrap;
}
.folder,
.file {
flex-grow: 1;
width: 128px;
margin: 4px;
box-sizing: border-box;
}
.padding {
flex-grow: 1;
pointer-events: none;
width: 128px + 8px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
grid-gap: 12px;
}
.empty {

View File

@ -4,7 +4,7 @@
*/
import { computed } from 'vue';
import type { Ref } from 'vue';
import type { Ref, ShallowRef } from 'vue';
export function getDateText(dateInstance: Date) {
const date = dateInstance.getDate();
@ -12,19 +12,6 @@ export function getDateText(dateInstance: Date) {
return `${month.toString()}/${date.toString()}`;
}
export type DateSeparetedTimelineItem<T> = {
id: string;
type: 'item';
data: T;
} | {
id: string;
type: 'date';
prev: Date;
prevText: string;
next: Date;
nextText: string;
};
// TODO: いちいちDateインスタンス作成するのは無駄感あるから文字列のまま解析したい
export function isSeparatorNeeded(
prev: string | null,
@ -56,7 +43,20 @@ export function getSeparatorInfo(
};
}
export function makeDateSeparatedTimelineComputedRef<T extends { id: string; createdAt: string; }>(items: Ref<T[]>) {
export type DateSeparetedTimelineItem<T> = {
id: string;
type: 'item';
data: T;
} | {
id: string;
type: 'date';
prev: Date;
prevText: string;
next: Date;
nextText: string;
};
export function makeDateSeparatedTimelineComputedRef<T extends { id: string; createdAt: string; }>(items: Ref<T[]> | ShallowRef<T[]>) {
return computed<DateSeparetedTimelineItem<T>[]>(() => {
const tl: DateSeparetedTimelineItem<T>[] = [];
for (let i = 0; i < items.value.length; i++) {
@ -92,3 +92,35 @@ export function makeDateSeparatedTimelineComputedRef<T extends { id: string; cre
return tl;
});
}
export type DateGroupedTimelineItem<T> = {
date: Date;
items: T[];
};
export function makeDateGroupedTimelineComputedRef<T extends { id: string; createdAt: string; }>(items: Ref<T[]> | ShallowRef<T[]>, span: 'day' | 'month' = 'day') {
return computed<DateGroupedTimelineItem<T>[]>(() => {
const tl: DateGroupedTimelineItem<T>[] = [];
for (let i = 0; i < items.value.length; i++) {
const item = items.value[i];
const date = new Date(item.createdAt);
const nextDate = items.value[i + 1] ? new Date(items.value[i + 1].createdAt) : null;
if (tl.length === 0 || (
span === 'day' && tl[tl.length - 1].date.getTime() !== date.getTime()
) || (
span === 'month' && (
tl[tl.length - 1].date.getFullYear() !== date.getFullYear() ||
tl[tl.length - 1].date.getMonth() !== date.getMonth()
)
)) {
tl.push({
date,
items: [],
});
}
tl[tl.length - 1].items.push(item);
}
return tl;
});
}