From d20a391d91a5eb0d755ded28819c877415fd8db1 Mon Sep 17 00:00:00 2001 From: Freeplay <freeplay@duck.com> Date: Wed, 14 Jun 2023 23:25:31 -0400 Subject: [PATCH] Home slide, apply kanban CSS --- .../client/src/pages/welcome.entrance.a.vue | 24 ++- packages/client/src/ui/universal.vue | 16 +- packages/client/src/ui/visitor/kanban.vue | 167 ++++++++++++++++-- 3 files changed, 177 insertions(+), 30 deletions(-) diff --git a/packages/client/src/pages/welcome.entrance.a.vue b/packages/client/src/pages/welcome.entrance.a.vue index 652d180771..a1d049166b 100644 --- a/packages/client/src/pages/welcome.entrance.a.vue +++ b/packages/client/src/pages/welcome.entrance.a.vue @@ -24,6 +24,9 @@ @swiper="setSwiperRef" @slide-change="onSlideChange" > + <swiper-slide v-slot="{ isActive }"> + <XKanban v-if="isActive"/> + </swiper-slide> <swiper-slide v-slot="{ isActive }"> <MkSpacer :content-max="800" v-if="isActive"> <XNotes :pagination="paginationForLocal" /> @@ -110,9 +113,10 @@ </template> <script lang="ts" setup> -import { computed, watch, onMounted } from "vue"; +import { computed, watch, onMounted, provide } from "vue"; import * as os from "@/os"; import { Swiper, SwiperSlide } from "swiper/vue"; +import XKanban from "@/ui/visitor/kanban.vue"; import XNotes from "@/components/MkNotes.vue"; import XUsers from "./explore.users.vue"; import XChannelList from "@/components/MkChannelList.vue"; @@ -127,7 +131,17 @@ import { DetailedInstanceMetadata } from "calckey-js/built/entities"; import "swiper/scss"; import "swiper/scss/virtual"; +const DESKTOP_THRESHOLD = 1100; +let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD); +matchMedia(`(min-width: ${DESKTOP_THRESHOLD - 1}px)`).onchange = (mql) => { + isDesktop = mql.matches; + syncSlide(isDesktop ? 1 : 0); +}; + +provide("shouldOmitHeaderTitle", true); + const tabs = [ + "home", "local", "remote", "channels", @@ -141,6 +155,11 @@ watch($$(tab), () => syncSlide(tabs.indexOf(tab))); const headerActions = $computed(() => []); const headerTabs = $computed(() => [ + !isDesktop ? { + key: "home", + icon: "ph-house ph-bold ph-lg", + title: i18n.ts.home, + } : [], { key: "local", icon: "ph-lightning ph-bold ph-lg", @@ -196,7 +215,7 @@ function syncSlide(index) { } onMounted(() => { - syncSlide(tabs.indexOf(swiperRef.activeIndex)); + syncSlide(isDesktop ? 1 : 0); }); let meta = $ref<DetailedInstanceMetadata>(); @@ -215,7 +234,6 @@ const paginationForLocal = { days: 14, }, }; - const paginationForRemote = { endpoint: "notes/featured" as const, limit: 20, diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue index 7f5d0583c6..23bc0022f3 100644 --- a/packages/client/src/ui/universal.vue +++ b/packages/client/src/ui/universal.vue @@ -4,7 +4,7 @@ :class="{ wallpaper, isMobile, centered: ui === 'classic' }" > <XSidebar v-if="!isMobile && $i" /> - <XKanban class="kanban" full v-if="!$i" /> + <XKanban sticky v-if="isDesktop" /> <MkStickyContainer class="contents"> <template #header @@ -196,7 +196,7 @@ const DESKTOP_THRESHOLD = 1100; const MOBILE_THRESHOLD = 500; // デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない -const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); +let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD); const isMobile = ref( deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD ); @@ -334,15 +334,9 @@ async function startGroup(): void { mainRouter.push(`/my/messaging/group/${group.id}`); } -onMounted(() => { - if (!isDesktop.value) { - matchMedia(`(min-width: ${DESKTOP_THRESHOLD - 1}px)`).onchange = ( - mql - ) => { - if (mql.matches) isDesktop.value = true; - }; - } -}); +matchMedia(`(min-width: ${DESKTOP_THRESHOLD - 1}px)`).onchange = (mql) => { + isDesktop = mql.matches; +}; const onContextmenu = (ev: MouseEvent) => { const isLink = (el: HTMLElement) => { diff --git a/packages/client/src/ui/visitor/kanban.vue b/packages/client/src/ui/visitor/kanban.vue index f7460bbfd7..a515a5a63c 100644 --- a/packages/client/src/ui/visitor/kanban.vue +++ b/packages/client/src/ui/visitor/kanban.vue @@ -1,21 +1,32 @@ <template> - <div class="instance-info-container" :class="expanded"> + <div class="instance-info-container" + :class="{ sticky }" + > <header id="instance-info" + v-on:scroll.passive="onScroll" > - <div class="ticker" v-if="!expanded"> + <!-- <div class="ticker" v-if="!expanded"> <img class="logo" :src="meta.logoImageUrl" /> - <h1> - <MkA - to="/" class="link" - >{{ instanceName }}</MkA> - </h1> - </div> + <div> + <h1> + <MkA + to="/" class="link" + >{{ instanceName }}</MkA> + </h1> + <p>{{ meta.description || i18n.ts.introMisskey }}</p> + </div> + <MkButton + primary + rounded + @click.stop="expanded = true" + >{{ i18n.ts.instanceInfo }}</MkButton> + </div> --> <img class="banner" :src="meta.backgroundImageUrl" /> - <div class="content" v-if="expanded"> + <div class="content"> <div class="header"> <img class="logo" @@ -183,14 +194,15 @@ import XSignupDialog from "@/components/MkSignupDialog.vue"; import MkKeyValue from "@/components/MkKeyValue.vue"; import MkMention from "@/components/MkMention.vue"; -let expanded = $ref(false); +// let expanded = $ref(window.innerWidth >= 1000); -matchMedia("(max-width: 1000px)").onchange = (mql) => { - console.log("ran"); - expanded = !mql.matches; -}; +// matchMedia("(max-width: 1000px)").onchange = (mql) => { +// expanded = !mql.matches; +// }; -expanded = true; +defineProps<{ + sticky?: boolean; +}>(); let meta = $ref<DetailedInstanceMetadata>(); @@ -234,8 +246,131 @@ function signup() { function showMenu(ev) { openHelpMenu_(ev); } +function onScroll(ev) { + if (ev.target.scrollTop == 0) { + expanded = false; + } +} </script> <style lang="scss" scoped> - +.instance-info-container { + &.sticky { + position: sticky; + top: 0; + max-height: 100vh; + overflow: hidden auto; + min-width: 400px; + width: 450px; + } + margin-left: -1px; + border-right: 1px solid var(--divider); + box-shadow: 0 0 48px -24px rgba(0,0,0,0.1); + z-index: 9000; +} +#instance-info { + display: flex; + flex-direction: column; + height: 100%; + background: var(--accent); + transition: transform .4s cubic-bezier(.5,0,0,1); + .banner { + position: sticky; + top: 0; + width: calc(100% + 2px); + margin-inline: -1px; + min-height: 250px; + max-height: 250px; + object-fit: cover; + object-position: center; + mask: linear-gradient(to bottom, black, calc(100% - 50px), transparent); + transition: min-height .4s, max-height .4s, filter .7s; + } + > .content { + --margin: 32px; + --radius: 16px; + display: flow-root; + position: relative; + z-index: 2; + background: var(--bg); + border-radius: var(--radius) var(--radius) 0 0; + margin-top: calc(0px - var(--radius)); + padding: 0 var(--margin); + text-align: center; + flex-grow: 1; + max-width: 600px; + margin-inline: auto; + width: 100%; + box-sizing: border-box; + + > .header { + position: relative; + margin-top: -50px; + &::before { + content: ""; + position: absolute; + inset: -5px calc(0px - var(--margin)); + bottom: -100px; + backdrop-filter: blur(60px); + filter: brightness(1.2); + pointer-events: none; + z-index: -1; + clip-path: inset(55px 0 0 0 round var(--radius)); + mask: linear-gradient(transparent 55px, #000 50px, transparent); + -webkit-mask: linear-gradient(transparent 55px, #000 50px, transparent); + } + > .logo { + height: 90px; + min-width: 90px; + border-radius: var(--radius); + margin-top: -5px; + transition: transform .4s cubic-bezier(0.5,0,0,1); + } + > h1 { + margin-block: .7em; + font-size: 1.5em; + color: var(--fgHighlighted) + } + } + .menu { + position: absolute; + top: 10px; + right: 10px; + width: 42px; + height: 42px; + border-radius: 100px; + background: var(--buttonBg) + } + .about { + position: relative; + font-size: 1.05em; + .desc { + display: block; + } + .collapsed { + position: relative; + max-height: calc(9em + 50px); + mask: linear-gradient(black calc(100% - 64px), transparent); + -webkit-mask: linear-gradient( + black calc(100% - 64px), + transparent + ); + } + } + .announcements { + text-align: initial; + } + > :not(.header) { + max-width: 600px; + margin-inline: auto; + } + } +} +section { + margin-top: 2em; +} +._formLinksGrid { + grid-template-columns: repeat(2,minmax(150px,1fr)); + text-align: initial; +} </style>