firefish/packages/client/src/components/MkDateSeparatedList.vue
Sam Smucny 834671839f
feat: federated events
Co-authored-by: Kainoa Kanter <kainoa@t1c.dev>
2023-06-21 23:51:11 -07:00

224 lines
4.2 KiB
Vue

<script lang="ts">
import { defineComponent, h, PropType, TransitionGroup } from "vue";
import MkAd from "@/components/global/MkAd.vue";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
export default defineComponent({
props: {
items: {
type: Array as PropType<
{ id: string; createdAt: string; _shouldInsertAd_: boolean }[]
>,
required: true,
},
direction: {
type: String,
required: false,
default: "down",
},
reversed: {
type: Boolean,
required: false,
default: false,
},
noGap: {
type: Boolean,
required: false,
default: false,
},
ad: {
type: Boolean,
required: false,
default: false,
},
getDate: {
type: Function, // Note => date string
required: false,
default: undefined,
},
},
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 getDateKey = (item): string =>
props.getDate ? props.getDate(item) : item.createdAt;
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(getDateKey(item)).getDate() !==
new Date(getDateKey(props.items[i + 1])).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(getDateKey(item)),
]),
h("span", [
getDateText(getDateKey(props.items[i + 1])),
h("i", {
class: "ph-caret-down ph-bold ph-lg icon",
}),
]),
]
)
);
return [el, separator];
} else {
if (props.ad && item._shouldInsertAd_) {
return [
h(MkAd, {
class: "a", // advertiseの意(ブロッカー対策)
key: item.id + ":ad",
prefer: ["inline", "inline-big"],
}),
el,
];
} else {
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>