Skip to content

Front-end for timeline page #3252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<template #content="slotProps">
<div
data-type="timelineBlock"
:data-date="slotProps.item.data[0].timeline.timeDate"
:data-date="(slotProps.item.data[0] as App.Http.Resources.Models.PhotoResource).timeline?.time_date"
class="flex flex-wrap flex-row shrink w-full justify-start gap-1 sm:gap-2 md:gap-4 pb-8"
>
<div class="w-full text-left font-semibold text-muted-color-emphasis text-lg">{{ slotProps.item.header }}</div>
Expand Down
6 changes: 6 additions & 0 deletions resources/js/components/gallery/photoModule/Dock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
v-on:click="emits('toggleStar')"
/>
<DockButton
v-if="!isTimeline"
pi="image"
class="lg:hover:text-primary-500 text-white"
v-tooltip.bottom="$t('gallery.photo.actions.set_album_header')"
Expand Down Expand Up @@ -47,6 +48,8 @@ import DockButton from "./DockButton.vue";
import { useLycheeStateStore } from "@/stores/LycheeState";
import { useTogglablesStateStore } from "@/stores/ModalsState";
import { storeToRefs } from "pinia";
import { computed } from "vue";
import { useRoute } from "vue-router";

const lycheeStore = useLycheeStateStore();
const togglableStore = useTogglablesStateStore();
Expand All @@ -65,4 +68,7 @@ const emits = defineEmits<{
toggleMove: [];
toggleDelete: [];
}>();

const route = useRoute();
const isTimeline = computed(() => (route.name as string).includes("timeline"));
</script>
109 changes: 109 additions & 0 deletions resources/js/components/gallery/timelineModule/TimelineDates.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<template>
<div
:class="{
'absolute flex flex-col text-muted-color text-right top-0 h-full overflow-y-scroll no-scrollbar': true,
'right-6': !isTouch,
'right-2': isTouch,
'bg-gradient-to-l from-(--p-surface-0) pt-14 dark:from-(--p-surface-900) text-shadow-sm group pb-24': true,
}"
@mouseleave="scrollToView"
>
<div v-for="yearChunk in dates" :key="yearChunk.header" class="">
<span
:class="{
'sticky inline-block top-0 font-semibold z-10 shadow-surface-950 drop-shadow-md text-3xl scale-75 text-muted-color-emphasis': true,
'group-hover:scale-100 transition-all duration-150 origin-right': true,
'scale-100': currentYear === parseInt(yearChunk.header, 10),
}"
>
{{ yearChunk.header }}
</span>
<!-- We only apply the hover property for items bellow the current scrolling year,
this avoids the upper part of the element to scroll up and down and some jerky behaviours. -->
<div :class="{ 'date-wrapper group-hover:grid-rows-[1]': currentYear > parseInt(yearChunk.header, 10) }">
<div class="overflow-hidden flex flex-col">
<span
v-for="monthChunk in yearChunk.data"
:key="monthChunk.time_date"
:data-date-pointer="monthChunk.time_date"
:class="{
'cursor-pointer transition-all duration-150 scale-75 ease-in-out origin-right': true,
'hover:text-primary-emphasis hover:font-bold hover:scale-100': !isTouch,
'scale-110 text-primary-emphasis font-bold': currentDate === monthChunk.time_date,
}"
@click="emits('load', monthChunk.time_date)"
>{{ monthChunk.format }}
</span>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useSplitter } from "@/composables/album/splitter";
import { isTouchDevice } from "@/utils/keybindings-utils";
import { useDebounceFn } from "@vueuse/core";
import { ref } from "vue";
import { onMounted } from "vue";
import { watch } from "vue";
import { computed } from "vue";
import { useRoute } from "vue-router";

const route = useRoute();
const props = defineProps<{
dates: App.Http.Resources.Models.Utils.TimelineData[];
}>();

const { spliter } = useSplitter();

const dates = computed(() => {
return spliter(
props.dates,
(d) => d.time_date.split("-")[0],
(d) => d.time_date.split("-")[0],
);
});

const currentDate = computed(() => (route.params.date as string | undefined) ?? "");
const currentYear = computed(() => parseInt(currentDate.value.split("-")[0], 10));

const isTouch = ref(isTouchDevice());

const emits = defineEmits<{
load: [date: string];
}>();

// Scroll magic side
// Select the current date and center it in the view
const scrollToView = useDebounceFn(() => {
if (!currentDate.value) {
return;
}

const el = document.querySelector(`[data-date-pointer="${currentDate.value}"]`);
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "center" });
}
}, 100);

// If the date change, we update!
watch(() => route.params.date, scrollToView, { immediate: true });

// Also do that at when loading the component
onMounted(() =>
// But wait! if we do it too early the dom is not rendered and this does not work.
// Let's wait 500ms for the rendering, then select the element.
setTimeout(scrollToView, 500),
);
</script>
<style>
.date-wrapper {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.25s ease-out;

&:is(:where(.group):hover *) {
grid-template-rows: 1fr;
}
}
</style>
3 changes: 2 additions & 1 deletion resources/js/components/headers/AlbumsHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ const lycheeStore = useLycheeStateStore();
const togglableStore = useTogglablesStateStore();
const favourites = useFavouriteStore();

const { dropbox_api_key, is_favourite_enabled, is_se_preview_enabled, is_live_metrics_enabled, is_registration_enabled } = storeToRefs(lycheeStore);
const { dropbox_api_key, is_favourite_enabled, is_timeline_page_enabled, is_se_preview_enabled, is_live_metrics_enabled, is_registration_enabled } =
storeToRefs(lycheeStore);
const { is_login_open, is_upload_visible, is_create_album_visible, is_create_tag_album_visible, is_metrics_open } = storeToRefs(togglableStore);

const router = useRouter();
Expand Down
Loading
Loading