Refactored MkDateSeparatedList

This commit is contained in:
Natty 2024-04-07 18:41:44 +02:00
parent 13e5d447e8
commit 156ad5c499
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
5 changed files with 221 additions and 217 deletions

View File

@ -0,0 +1,203 @@
<template>
<component
:is="parentType"
:tag="tag"
:name="name"
class="sqadhkmv"
:class="{ noGap: noGap }"
:data-direction="direction"
:data-reversed="reversed ? 'true' : 'false'"
>
<template v-for="item in effectiveItems" :key="item.key">
<slot v-if="item.type === 'item'" :item="item.item"></slot>
<div v-else class="separator">
<p class="date">
<span>
<i class="ph-caret-up ph-bold ph-lg icon"></i>
{{ item.dateAfter }}
</span>
<span>
{{ item.dateBefore }}
<i class="ph-caret-down ph-bold ph-lg icon"></i>
</span>
</p>
</div>
</template>
</component>
</template>
<script lang="ts" setup generic="T">
import { computed, ref, shallowRef, TransitionGroup, watch } from "vue";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import { magTransProperty } from "@/scripts-mag/mag-util";
type Item = { id: string } & ({ createdAt: string } | { created_at: string });
const props = withDefaults(
defineProps<{
items: (T & Item)[];
direction?: "up" | "down";
reversed?: boolean;
noGap?: boolean;
}>(),
{
direction: "down",
reversed: false,
noGap: false,
}
);
type ItemType =
| { type: "separator"; key: string; dateBefore: string; dateAfter: string }
| { type: "item"; key: string; item: T & Item };
const effectiveItems = shallowRef<ItemType[]>([]);
watch(
() => props.items,
(itemsNew) => {
const listItems: ItemType[] = [];
for (let i = 0; i < itemsNew.length; i++) {
listItems.push({
type: "item",
key: itemsNew[i].id,
item: itemsNew[i],
});
if (itemsNew.length > i + 1) {
const dateAfter = getDateText(itemsNew[i]);
const dateBefore = getDateText(itemsNew[i + 1]);
if (dateBefore === dateAfter) {
continue;
}
listItems.push({
type: "separator",
key: `${itemsNew[i].id}:separator`,
dateBefore,
dateAfter,
});
}
}
effectiveItems.value = listItems;
},
{ deep: true, immediate: true }
);
function getDateText(item: { createdAt: string } | { created_at: string }) {
const dateTime = new Date(
magTransProperty(item, "createdAt", "created_at")
);
const date = dateTime.getDate();
const month = dateTime.getMonth() + 1;
return i18n.t("monthAndDay", {
month: month.toString(),
day: date.toString(),
});
}
const animationEnabled = ref(defaultStore.state.animation);
watch(
() => defaultStore.state.animation,
(val) => (animationEnabled.value = val)
);
const parentType = computed(() =>
animationEnabled ? TransitionGroup : HTMLDivElement
);
const tag = computed(() => (animationEnabled ? "div" : null));
const name = computed(() => (animationEnabled ? "list" : null));
defineSlots<{
default(props: { item: T & Item }): any;
}>();
</script>
<style lang="scss">
.sqadhkmv {
> *:empty {
display: none;
}
> *:not(:last-child) {
margin-bottom: var(--margin);
}
> .list-move {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
> .list-enter-active {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1),
opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
&[data-direction="up"] {
> .list-enter-from {
opacity: 0;
transform: translateY(64px);
}
}
&[data-direction="down"] {
> .list-enter-from {
opacity: 0;
transform: translateY(-64px);
}
}
> .separator {
text-align: center;
> .date {
display: inline-block;
position: relative;
margin: 0;
padding: 0 16px;
line-height: 32px;
text-align: center;
font-size: 12px;
color: var(--dateLabelFg);
> span {
&:first-child {
margin-right: 8px;
> .icon {
margin-right: 8px;
}
}
&:last-child {
margin-left: 8px;
> .icon {
margin-left: 8px;
}
}
}
}
}
&.noGap {
> * {
margin: 0 !important;
border: none;
border-radius: 0;
box-shadow: none;
&:first-child {
border-radius: var(--radius) var(--radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--radius) var(--radius);
}
&:not(:last-child) {
border-bottom: solid 0.5px var(--divider);
}
}
}
}
</style>

View File

@ -23,7 +23,11 @@
<MagStreamStatus @reconnect="reload()" />
<XList
v-slot="{ item: notificationGroup }"
v-slot="{
item: notificationGroup,
}: {
item: NotificationGroup,
}"
class="elsfgstc"
:items="items"
:no-gap="true"
@ -91,7 +95,7 @@ import {
} from "vue";
import XNotification from "@/components/MagNotification.vue";
import XNotificationGroup from "@/components/MagNotificationGroup.vue";
import XList from "@/components/MkDateSeparatedList.vue";
import XList from "@/components/MagDateSeparatedList.vue";
import XNote from "@/components/MagNote.vue";
import MagStreamStatus from "@/components/MagStreamStatus.vue";
import { magStream, stream } from "@/stream";

View File

@ -1,209 +0,0 @@
<script lang="ts">
import { defineComponent, h, PropType, TransitionGroup } from "vue";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
export default defineComponent({
props: {
items: {
type: Array as PropType<
{
id: string;
createdAt?: string;
created_at?: string;
}[]
>,
required: true,
},
direction: {
type: String,
required: false,
default: "down",
},
reversed: {
type: Boolean,
required: false,
default: false,
},
noGap: {
type: Boolean,
required: false,
default: false,
},
},
setup(props, { slots, expose }) {
function getDateText(time: string) {
const date = new Date(time).getDate();
const month = new Date(time).getMonth() + 1;
return i18n.t("monthAndDay", {
month: month.toString(),
day: date.toString(),
});
}
if (props.items.length === 0) return;
const renderChildren = () =>
props.items.map((item, i) => {
if (!slots || !slots.default) return;
const el = slots.default({
item: item,
})[0];
if (el.key == null && item.id) el.key = item.id;
if (
i !== props.items.length - 1 &&
new Date(item.createdAt || item.created_at!).getDate() !==
new Date(
props.items[i + 1].createdAt ||
props.items[i + 1].created_at!
).getDate()
) {
const separator = h(
"div",
{
class: "separator",
key: item.id + ":separator",
},
h(
"p",
{
class: "date",
},
[
h("span", [
h("i", {
class: "ph-caret-up ph-bold ph-lg icon",
}),
getDateText(
item.createdAt || item.created_at!
),
]),
h("span", [
getDateText(
props.items[i + 1].createdAt ||
props.items[i + 1].created_at!
),
h("i", {
class: "ph-caret-down ph-bold ph-lg icon",
}),
]),
]
)
);
return [el, separator];
}
return el;
});
return () =>
h(
defaultStore.state.animation ? TransitionGroup : "div",
defaultStore.state.animation
? {
class: "sqadhkmv" + (props.noGap ? " noGap" : ""),
name: "list",
tag: "div",
"data-direction": props.direction,
"data-reversed": props.reversed ? "true" : "false",
}
: {
class: "sqadhkmv" + (props.noGap ? " noGap" : ""),
},
{ default: renderChildren }
);
},
});
</script>
<style lang="scss">
.sqadhkmv {
> *:empty {
display: none;
}
> *:not(:last-child) {
margin-bottom: var(--margin);
}
> .list-move {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
> .list-enter-active {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1),
opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
&[data-direction="up"] {
> .list-enter-from {
opacity: 0;
transform: translateY(64px);
}
}
&[data-direction="down"] {
> .list-enter-from {
opacity: 0;
transform: translateY(-64px);
}
}
> .separator {
text-align: center;
> .date {
display: inline-block;
position: relative;
margin: 0;
padding: 0 16px;
line-height: 32px;
text-align: center;
font-size: 12px;
color: var(--dateLabelFg);
> span {
&:first-child {
margin-right: 8px;
> .icon {
margin-right: 8px;
}
}
&:last-child {
margin-left: 8px;
> .icon {
margin-left: 8px;
}
}
}
}
}
&.noGap {
> * {
margin: 0 !important;
border: none;
border-radius: 0;
box-shadow: none;
&:first-child {
border-radius: var(--radius) var(--radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--radius) var(--radius);
}
&:not(:last-child) {
border-bottom: solid 0.5px var(--divider);
}
}
}
}
</style>

View File

@ -15,7 +15,7 @@
<div class="giivymft" :class="{ noGap }">
<XList
ref="notes"
v-slot="{ item: note }"
v-slot="{ item: note }: { item: Misskey.entities.Note }"
:items="notes"
:direction="pagination.reversed ? 'up' : 'down'"
:reversed="pagination.reversed"
@ -38,11 +38,12 @@ import { ref } from "vue";
import type { Paging } from "@/components/MkPagination.vue";
import MkPagination from "@/components/MkPagination.vue";
import XNoteResolvingProxy from "@/components/MagNoteResolvingProxy.vue";
import XList from "@/components/MkDateSeparatedList.vue";
import XList from "@/components/MagDateSeparatedList.vue";
import { i18n } from "@/i18n";
import { noteIsMag } from "@/scripts-mag/mag-util";
import type * as Misskey from "calckey-js";
const props = defineProps<{
defineProps<{
pagination: Paging;
noGap?: boolean;
}>();

View File

@ -16,14 +16,18 @@
<template #default="{ items }">
<XList
v-slot="{ item }"
v-slot="{
item,
}: {
item: Misskey.entities.NoteFavorite,
}"
:items="items"
:direction="'down'"
:no-gap="false"
>
<XNoteResolvingProxy
:key="item.id"
:note="item.note"
:note="item.noteId"
:class="$style.note"
/>
</XList>
@ -37,9 +41,10 @@
import { ref } from "vue";
import MkPagination from "@/components/MkPagination.vue";
import XNoteResolvingProxy from "@/components/MagNoteResolvingProxy.vue";
import XList from "@/components/MkDateSeparatedList.vue";
import XList from "@/components/MagDateSeparatedList.vue";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import type * as Misskey from "calckey-js";
const pagination = {
endpoint: "i/favorites" as const,