diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index b9bce4d74d..e9f0624a2e 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -1,51 +1,50 @@ <template> -<div class="mvcprjjd" :class="{ iconOnly }"> - <div class="body"> - <div class="top"> - <div class="banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> - <button v-click-anime v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="item _button instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/> +<div :class="[$style.root, { [$style.iconOnly]: iconOnly }]"> + <div :class="$style.body"> + <div :class="$style.top"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> + <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> + <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> </button> </div> - <div class="middle"> - <MkA v-click-anime v-tooltip.noDelay.right="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact> - <i class="icon ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span> + <div :class="$style.middle"> + <MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact> + <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span> </MkA> <template v-for="item in menu"> - <div v-if="item === '-'" class="divider"></div> + <div v-if="item === '-'" :class="$style.divider"></div> <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" - v-click-anime v-tooltip.noDelay.right="navbarItemDef[item].title" - class="item _button" - :class="[item, { active: navbarItemDef[item].active }]" - activeClass="active" + class="_button" + :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" + :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" > - <i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span> - <span v-if="navbarItemDef[item].indicated" class="indicator"><i class="icon _indicatorCircle"></i></span> + <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> + <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> </component> </template> - <div class="divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip.noDelay.right="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin"> - <i class="icon ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span> + <div :class="$style.divider"></div> + <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="$style.item" :activeClass="$style.active" to="/admin"> + <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> </MkA> - <button v-click-anime class="item _button" @click="more"> - <i class="icon ti ti-grid-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span> - <span v-if="otherMenuItemIndicated" class="indicator"><i class="icon _indicatorCircle"></i></span> + <button class="_button" :class="$style.item" @click="more"> + <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> + <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> </button> - <MkA v-click-anime v-tooltip.noDelay.right="i18n.ts.settings" class="item" activeClass="active" to="/settings"> - <i class="icon ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span> + <MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" to="/settings"> + <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span> </MkA> </div> - <div class="bottom"> - <button v-tooltip.noDelay.right="i18n.ts.note" class="item _button post" data-cy-open-post-form @click="os.post"> - <i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span> + <div :class="$style.bottom"> + <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form @click="os.post"> + <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span> </button> - <button v-click-anime v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="item _button account" @click="openAccountMenu"> - <MkAvatar :user="$i" class="avatar"/><MkAcct class="text _nowrap" :user="$i"/> + <button v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" :class="[$style.account]" @click="openAccountMenu"> + <MkAvatar :user="$i" :class="$style.avatar"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/> </button> </div> </div> @@ -99,375 +98,376 @@ function more(ev: MouseEvent) { } </script> -<style lang="scss" scoped> -.mvcprjjd { - $nav-width: 250px; - $nav-icon-only-width: 80px; +<style lang="scss" module> +.root { + --nav-width: 250px; + --nav-icon-only-width: 80px; - flex: 0 0 $nav-width; - width: $nav-width; + flex: 0 0 var(--nav-width); + width: var(--nav-width); box-sizing: border-box; +} - > .body { - position: fixed; +.body { + position: fixed; + top: 0; + left: 0; + z-index: 1001; + width: var(--nav-icon-only-width); + height: 100dvh; + box-sizing: border-box; + overflow: auto; + overflow-x: clip; + overscroll-behavior: contain; + background: var(--navBg); + contain: strict; + display: flex; + flex-direction: column; +} + +.root:not(.iconOnly) { + .body { + width: var(--nav-width); + } + + .top { + position: sticky; + top: 0; + z-index: 1; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } + + .banner { + position: absolute; top: 0; left: 0; - z-index: 1001; - width: $nav-icon-only-width; - height: 100dvh; - box-sizing: border-box; - overflow: auto; - overflow-x: clip; - overscroll-behavior: contain; - background: var(--navBg); - contain: strict; + width: 100%; + height: 100%; + background-size: cover; + background-position: center center; + -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); + mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); + } + + .instance { + position: relative; + display: block; + text-align: center; + width: 100%; + } + + .instanceIcon { + display: inline-block; + width: 38px; + aspect-ratio: 1; + } + + .bottom { + position: sticky; + bottom: 0; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } + + .post { + position: relative; + display: block; + width: 100%; + height: 40px; + color: var(--fgOnAccent); + font-weight: bold; + text-align: left; + + &:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + } + + &:hover, &.active { + &:before { + background: var(--accentLighten); + } + } + } + + .postIcon { + position: relative; + margin-left: 30px; + margin-right: 8px; + width: 32px; + } + + .postText { + position: relative; + } + + .account { + position: relative; display: flex; - flex-direction: column; + align-items: center; + padding-left: 30px; + width: 100%; + text-align: left; + box-sizing: border-box; + margin-top: 16px; } - &:not(.iconOnly) { - > .body { - width: $nav-width; + .avatar { + display: block; + flex-shrink: 0; + position: relative; + width: 32px; + aspect-ratio: 1; + margin-right: 8px; + } - > .top { - position: sticky; + .acct { + display: block; + flex-shrink: 1; + padding-right: 8px; + } + + .middle { + flex: 1; + } + + .divider { + margin: 16px 16px; + border-top: solid 0.5px var(--divider); + } + + .item { + position: relative; + display: block; + padding-left: 30px; + line-height: 2.85rem; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + text-align: left; + box-sizing: border-box; + color: var(--navFg); + + &:hover { + text-decoration: none; + color: var(--navHoverFg); + } + + &.active { + color: var(--navActive); + } + + &:hover, &.active { + color: var(--accent); + + &:before { + content: ""; + display: block; + width: calc(100% - 34px); + height: 100%; + margin: auto; + position: absolute; top: 0; - z-index: 1; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - - > .banner { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-size: cover; - background-position: center center; - -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - } - - > .instance { - position: relative; - display: block; - text-align: center; - width: 100%; - - > .icon { - display: inline-block; - width: 38px; - aspect-ratio: 1; - } - } - } - - > .bottom { - position: sticky; + left: 0; + right: 0; bottom: 0; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - - > .post { - position: relative; - display: block; - width: 100%; - height: 40px; - color: var(--fgOnAccent); - font-weight: bold; - text-align: left; - - &:before { - content: ""; - display: block; - width: calc(100% - 38px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - } - - &:hover, &.active { - &:before { - background: var(--accentLighten); - } - } - - > .icon { - position: relative; - margin-left: 30px; - margin-right: 8px; - width: 32px; - } - - > .text { - position: relative; - } - } - - > .account { - position: relative; - display: flex; - align-items: center; - padding-left: 30px; - width: 100%; - text-align: left; - box-sizing: border-box; - margin-top: 16px; - - > .avatar { - display: block; - flex-shrink: 0; - position: relative; - width: 32px; - aspect-ratio: 1; - margin-right: 8px; - } - - > .text { - display: block; - flex-shrink: 1; - padding-right: 8px; - } - } - } - - > .middle { - flex: 1; - - > .divider { - margin: 16px 16px; - border-top: solid 0.5px var(--divider); - } - - > .item { - position: relative; - display: block; - padding-left: 30px; - line-height: 2.85rem; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - width: 100%; - text-align: left; - box-sizing: border-box; - color: var(--navFg); - - > .icon { - position: relative; - width: 32px; - margin-right: 8px; - } - - > .indicator { - position: absolute; - top: 0; - left: 20px; - color: var(--navIndicator); - font-size: 8px; - animation: blink 1s infinite; - } - - > .text { - position: relative; - font-size: 0.9em; - } - - &:hover { - text-decoration: none; - color: var(--navHoverFg); - } - - &.active { - color: var(--navActive); - } - - &:hover, &.active { - color: var(--accent); - - &:before { - content: ""; - display: block; - width: calc(100% - 34px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: var(--accentedBg); - } - } - } + border-radius: 999px; + background: var(--accentedBg); } } } - &.iconOnly { - flex: 0 0 $nav-icon-only-width; - width: $nav-icon-only-width; + .itemIcon { + position: relative; + width: 32px; + margin-right: 8px; + } - > .body { - width: $nav-icon-only-width; + .itemIndicator { + position: absolute; + top: 0; + left: 20px; + color: var(--navIndicator); + font-size: 8px; + animation: blink 1s infinite; + } - > .top { - position: sticky; - top: 0; - z-index: 1; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); + .itemText { + position: relative; + font-size: 0.9em; + } +} - > .instance { - display: block; - text-align: center; - width: 100%; +.root.iconOnly { + flex: 0 0 var(--nav-icon-only-width); + width: var(--nav-icon-only-width); - > .icon { - display: inline-block; - width: 30px; - aspect-ratio: 1; - } - } - } + .body { + width: var(--nav-icon-only-width); + } - > .bottom { - position: sticky; - bottom: 0; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); + .top { + position: sticky; + top: 0; + z-index: 1; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } - > .post { - display: block; - position: relative; - width: 100%; - height: 52px; - margin-bottom: 16px; - text-align: center; + .instance { + display: block; + text-align: center; + width: 100%; + } - &:before { - content: ""; - display: block; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - margin: auto; - width: 52px; - aspect-ratio: 1/1; - border-radius: 100%; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - } + .instanceIcon { + display: inline-block; + width: 30px; + aspect-ratio: 1; + } - &:hover, &.active { - &:before { - background: var(--accentLighten); - } - } + .bottom { + position: sticky; + bottom: 0; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } - > .icon { - position: relative; - color: var(--fgOnAccent); - } + .post { + display: block; + position: relative; + width: 100%; + height: 52px; + margin-bottom: 16px; + text-align: center; - > .text { - display: none; - } - } + &:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 52px; + aspect-ratio: 1/1; + border-radius: 100%; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + } - > .account { - display: block; - text-align: center; - width: 100%; - - > .avatar { - display: inline-block; - width: 38px; - aspect-ratio: 1; - } - - > .text { - display: none; - } - } - } - - > .middle { - flex: 1; - - > .divider { - margin: 8px auto; - width: calc(100% - 32px); - border-top: solid 0.5px var(--divider); - } - - > .item { - display: block; - position: relative; - padding: 18px 0; - width: 100%; - text-align: center; - - > .icon { - display: block; - margin: 0 auto; - opacity: 0.7; - } - - > .text { - display: none; - } - - > .indicator { - position: absolute; - top: 6px; - left: 24px; - color: var(--navIndicator); - font-size: 8px; - animation: blink 1s infinite; - } - - &:hover, &.active { - text-decoration: none; - color: var(--accent); - - &:before { - content: ""; - display: block; - height: 100%; - aspect-ratio: 1; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: var(--accentedBg); - } - - > .icon, > .text { - opacity: 1; - } - } - } + &:hover, &.active { + &:before { + background: var(--accentLighten); } } } + + .postIcon { + position: relative; + color: var(--fgOnAccent); + } + + .postText { + display: none; + } + + .account { + display: block; + text-align: center; + width: 100%; + } + + .avatar { + display: inline-block; + width: 38px; + aspect-ratio: 1; + } + + .acct { + display: none; + } + + .middle { + flex: 1; + } + + .divider { + margin: 8px auto; + width: calc(100% - 32px); + border-top: solid 0.5px var(--divider); + } + + .item { + display: block; + position: relative; + padding: 18px 0; + width: 100%; + text-align: center; + + &:hover, &.active { + text-decoration: none; + color: var(--accent); + + &:before { + content: ""; + display: block; + height: 100%; + aspect-ratio: 1; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: var(--accentedBg); + } + + > .icon, + > .text { + opacity: 1; + } + } + } + + .itemIcon { + display: block; + margin: 0 auto; + opacity: 0.7; + } + + .itemText { + display: none; + } + + .itemIndicator { + position: absolute; + top: 6px; + left: 24px; + color: var(--navIndicator); + font-size: 8px; + animation: blink 1s infinite; + } } </style>