diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 45aba1d24ba81f57673ef31f6c0b6102c8706618..54afae952c548ee245777c0afb7b19704b6875e1 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -4,27 +4,26 @@ v-show="!isDeleted" ref="el" v-hotkey="keymap" - class="tkcbzcuz" + :class="[$style.root, { [$style.isRenote]: isRenote }]" :tabindex="!isDeleted ? '-1' : null" - :class="{ renote: isRenote }" > - <MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" class="reply-to"/> - <div v-if="pinned" class="info"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> - <div v-if="appearNote._prId_" class="info"><i class="fas fa-bullhorn"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div> - <div v-if="appearNote._featuredId_" class="info"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div> - <div v-if="isRenote" class="renote"> - <MkAvatar v-once class="avatar" :user="note.user"/> - <i class="ti ti-repeat"></i> - <I18n :src="i18n.ts.renotedBy" tag="span"> + <MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo"/> + <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> + <!--<div v-if="appearNote._prId_" class="tip"><i class="fas fa-bullhorn"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> + <!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>--> + <div v-if="isRenote" :class="$style.renote"> + <MkAvatar v-once :class="$style.renoteAvatar" :user="note.user"/> + <i class="ti ti-repeat" style="margin-right: 4px;"></i> + <I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText"> <template #user> - <MkA v-user-preview="note.userId" class="name" :to="userPage(note.user)"> + <MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)"> <MkUserName :user="note.user"/> </MkA> </template> </I18n> - <div class="info"> - <button ref="renoteTime" class="_button time" @click="showRenoteMenu()"> - <i v-if="isMyRenote" class="ti ti-dots dropdownIcon"></i> + <div :class="$style.renoteInfo"> + <button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()"> + <i v-if="isMyRenote" class="ti ti-dots" :class="$style.renoteMenu"></i> <MkTime :time="note.createdAt"/> </button> <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> @@ -35,80 +34,80 @@ <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['localOnly']"><i class="ti ti-world-off"></i></span> </div> </div> - <article class="article" @contextmenu.stop="onContextmenu"> - <MkAvatar v-once class="avatar" :user="appearNote.user"/> - <div class="main"> - <MkNoteHeader class="header" :note="appearNote" :mini="true"/> - <MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/> - <div class="body"> - <p v-if="appearNote.cw != null" class="cw"> - <Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i"/> + <article :class="$style.article" @contextmenu.stop="onContextmenu"> + <MkAvatar v-once :class="$style.avatar" :user="appearNote.user"/> + <div :class="$style.main"> + <MkNoteHeader :class="$style.header" :note="appearNote" :mini="true"/> + <MkInstanceTicker v-if="showTicker" :class="$style.ticker" :instance="appearNote.user.instance"/> + <div :class="$style.body"> + <p v-if="appearNote.cw != null" :class="$style.cw"> + <Mfm v-if="appearNote.cw != ''" :class="$style.cwText" :text="appearNote.cw" :author="appearNote.user" :i="$i"/> <MkCwButton v-model="showContent" :note="appearNote"/> </p> - <div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed, isLong }"> - <div class="text"> + <div v-show="appearNote.cw == null || showContent" :class="[$style.content, { [$style.contentCollapsed]: collapsed, [$style.contentIsLong]: isLong }]"> + <div :class="$style.text"> <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> - <MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> + <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> <Mfm v-if="appearNote.text" v-once :text="appearNote.text" :author="appearNote.user" :i="$i"/> - <a v-if="appearNote.renote != null" class="rp">RN:</a> - <div v-if="translating || translation" class="translation"> + <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> - <div v-else class="translated"> + <div v-else :class="$style.translated"> <b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :i="$i"/> </div> </div> </div> - <div v-if="appearNote.files.length > 0" class="files"> + <div v-if="appearNote.files.length > 0" :class="$style.files"> <MkMediaList :media-list="appearNote.files"/> </div> - <MkPoll v-if="appearNote.poll" ref="pollViewer" :note="appearNote" class="poll"/> - <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" class="url-preview"/> - <div v-if="appearNote.renote" class="renote"><MkNoteSimple :note="appearNote.renote" class="note"/></div> - <button v-if="isLong && collapsed" class="fade _button" @click="collapsed = false"> - <span>{{ i18n.ts.showMore }}</span> + <MkPoll v-if="appearNote.poll" ref="pollViewer" :note="appearNote" :class="$style.poll"/> + <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/> + <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> + <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> + <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> </button> - <button v-else-if="isLong && !collapsed" class="showLess _button" @click="collapsed = true"> - <span>{{ i18n.ts.showLess }}</span> + <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> + <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> </button> </div> - <MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> + <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> </div> - <footer class="footer"> + <footer :class="$style.footer"> <MkReactionsViewer ref="reactionsViewer" :note="appearNote"/> - <button class="button _button" @click="reply()"> + <button :class="$style.footerButton" class="_button" @click="reply()"> <i class="ti ti-arrow-back-up"></i> - <p v-if="appearNote.repliesCount > 0" class="count">{{ appearNote.repliesCount }}</p> + <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p> </button> <button v-if="canRenote" ref="renoteButton" - class="button _button" + :class="$style.footerButton" + class="_button" @mousedown="renote()" > <i class="ti ti-repeat"></i> - <p v-if="appearNote.renoteCount > 0" class="count">{{ appearNote.renoteCount }}</p> + <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p> </button> - <button v-else class="button _button" disabled> + <button v-else :class="$style.footerButton" class="_button" disabled> <i class="ti ti-ban"></i> </button> - <button v-if="appearNote.myReaction == null" ref="reactButton" class="button _button" @mousedown="react()"> + <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()"> <i class="ti ti-plus"></i> </button> - <button v-if="appearNote.myReaction != null" ref="reactButton" class="button _button reacted" @click="undoReact(appearNote)"> + <button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)"> <i class="ti ti-minus"></i> </button> - <button ref="menuButton" class="button _button" @mousedown="menu()"> + <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> <i class="ti ti-dots"></i> </button> </footer> </div> </article> </div> -<div v-else class="muted" @click="muted = false"> +<div v-else :class="$style.muted" @click="muted = false"> <I18n :src="i18n.ts.userSaysSomething" tag="small"> <template #name> - <MkA v-user-preview="appearNote.userId" class="name" :to="userPage(appearNote.user)"> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> <MkUserName :user="appearNote.user"/> </MkA> </template> @@ -349,8 +348,8 @@ function readPromo() { } </script> -<style lang="scss" scoped> -.tkcbzcuz { +<style lang="scss" module> +.root { position: relative; transition: box-shadow 0.1s ease; font-size: 1.05em; @@ -387,322 +386,273 @@ function readPromo() { } } - &:hover > .article > .main > .footer > .button { + &:hover > .article > .main > .footer > .footerButton { opacity: 1; } +} - > .info { - display: flex; - align-items: center; - padding: 16px 32px 8px 32px; - line-height: 24px; - font-size: 90%; - white-space: pre; - color: #d28a3f; - - > i { - margin-right: 4px; - } +.tip { + display: flex; + align-items: center; + padding: 16px 32px 8px 32px; + line-height: 24px; + font-size: 90%; + white-space: pre; + color: #d28a3f; +} - > .hide { - margin-left: auto; - color: inherit; - } - } +.tip + .article { + padding-top: 8px; +} - > .info + .article { - padding-top: 8px; - } +.replyTo { + opacity: 0.7; + padding-bottom: 0; +} - > .reply-to { - opacity: 0.7; - padding-bottom: 0; - } +.renote { + display: flex; + align-items: center; + padding: 16px 32px 8px 32px; + line-height: 28px; + white-space: pre; + color: var(--renote); +} - > .renote { - display: flex; - align-items: center; - padding: 16px 32px 8px 32px; - line-height: 28px; - white-space: pre; - color: var(--renote); - - > .avatar { - flex-shrink: 0; - display: inline-block; - width: 28px; - height: 28px; - margin: 0 8px 0 0; - border-radius: 6px; - } +.renoteAvatar { + flex-shrink: 0; + display: inline-block; + width: 28px; + height: 28px; + margin: 0 8px 0 0; + border-radius: 6px; +} - > i { - margin-right: 4px; - } +.renoteText { + overflow: hidden; + flex-shrink: 1; + text-overflow: ellipsis; + white-space: nowrap; +} - > span { - overflow: hidden; - flex-shrink: 1; - text-overflow: ellipsis; - white-space: nowrap; +.renoteUserName { + font-weight: bold; +} - > .name { - font-weight: bold; - } - } +.renoteInfo { + margin-left: auto; + font-size: 0.9em; +} - > .info { - margin-left: auto; - font-size: 0.9em; +.renoteTime { + flex-shrink: 0; + color: inherit; +} - > .time { - flex-shrink: 0; - color: inherit; +.renoteMenu { + margin-right: 4px; +} - > .dropdownIcon { - margin-right: 4px; - } - } - } - } +.renoteInfo + .article { + padding-top: 8px; +} + +.article { + display: flex; + padding: 28px 32px 18px; +} + +.avatar { + flex-shrink: 0; + display: block; + margin: 0 14px 8px 0; + width: 58px; + height: 58px; + position: sticky; + top: calc(22px + var(--stickyTop, 0px)); + left: 0; +} + +.main { + flex: 1; + min-width: 0; +} + +.body { + container-type: inline-size; +} + +.cw { + cursor: default; + display: block; + margin: 0; + padding: 0; + overflow-wrap: break-word; +} + +.cwText { + margin-right: 8px; +} + +.content { +} + +.contentIsLong { +} + +.showLess { + width: 100%; + margin-top: 1em; + position: sticky; + bottom: 1em; +} + +.howLessLabel { + display: inline-block; + background: var(--popup); + padding: 6px 10px; + font-size: 0.8em; + border-radius: 999px; + box-shadow: 0 2px 6px rgb(0 0 0 / 20%); +} - > .renote + .article { - padding-top: 8px; +.contentCollapsed { + position: relative; + max-height: 9em; + overflow: clip; +} + +.collapsed { + display: block; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 64px; + background: linear-gradient(0deg, var(--panel), var(--X15)); + + &:hover > .collapsedLabel { + background: var(--panelHighlight); } +} - > .article { - display: flex; - padding: 28px 32px 18px; +.collapsedLabel { + display: inline-block; + background: var(--panel); + padding: 6px 10px; + font-size: 0.8em; + border-radius: 999px; + box-shadow: 0 2px 6px rgb(0 0 0 / 20%); +} - > .avatar { - flex-shrink: 0; - display: block; - margin: 0 14px 8px 0; - width: 58px; - height: 58px; - position: sticky; - top: calc(22px + var(--stickyTop, 0px)); - left: 0; - } +.text { + overflow-wrap: break-word; +} - > .main { - flex: 1; - min-width: 0; - - > .body { - container-type: inline-size; - - > .cw { - cursor: default; - display: block; - margin: 0; - padding: 0; - overflow-wrap: break-word; - - > .text { - margin-right: 8px; - } - } - - > .content { - &.isLong { - > .showLess { - width: 100%; - margin-top: 1em; - position: sticky; - bottom: 1em; - - > span { - display: inline-block; - background: var(--popup); - padding: 6px 10px; - font-size: 0.8em; - border-radius: 999px; - box-shadow: 0 2px 6px rgb(0 0 0 / 20%); - } - } - } - - &.collapsed { - position: relative; - max-height: 9em; - overflow: clip; - - > .fade { - display: block; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 64px; - background: linear-gradient(0deg, var(--panel), var(--X15)); - - > span { - display: inline-block; - background: var(--panel); - padding: 6px 10px; - font-size: 0.8em; - border-radius: 999px; - box-shadow: 0 2px 6px rgb(0 0 0 / 20%); - } - - &:hover { - > span { - background: var(--panelHighlight); - } - } - } - } - - > .text { - overflow-wrap: break-word; - - > .reply { - color: var(--accent); - margin-right: 0.5em; - } - - > .rp { - margin-left: 4px; - font-style: oblique; - color: var(--renote); - } - - > .translation { - border: solid 0.5px var(--divider); - border-radius: var(--radius); - padding: 12px; - margin-top: 8px; - } - } - - > .url-preview { - margin-top: 8px; - } - - > .poll { - font-size: 80%; - } - - > .renote { - padding: 8px 0; - - > .note { - padding: 16px; - border: dashed 1px var(--renote); - border-radius: 8px; - } - } - } - - > .channel { - opacity: 0.7; - font-size: 80%; - } - } - - > .footer { - > .button { - margin: 0; - padding: 8px; - opacity: 0.7; - - &:not(:last-child) { - margin-right: 28px; - } - - &:hover { - color: var(--fgHighlighted); - } - - > .count { - display: inline; - margin: 0 0 0 8px; - opacity: 0.7; - } - - &.reacted { - color: var(--accent); - } - } - } - } +.replyIcon { + color: var(--accent); + margin-right: 0.5em; +} + +.translation { + border: solid 0.5px var(--divider); + border-radius: var(--radius); + padding: 12px; + margin-top: 8px; +} + +.urlPreview { + margin-top: 8px; +} + +.poll { + font-size: 80%; +} + +.quote { + padding: 8px 0; +} + +.quoteNote { + padding: 16px; + border: dashed 1px var(--renote); + border-radius: 8px; +} + +.channel { + opacity: 0.7; + font-size: 80%; +} + +.footerButton { + margin: 0; + padding: 8px; + opacity: 0.7; + + &:not(:last-child) { + margin-right: 28px; } - > .reply { - border-top: solid 0.5px var(--divider); + &:hover { + color: var(--fgHighlighted); } } +.footerButtonCount { + display: inline; + margin: 0 0 0 8px; + opacity: 0.7; +} + @container (max-width: 500px) { - .tkcbzcuz { + .root { font-size: 0.9em; + } - > .article { - > .avatar { - width: 50px; - height: 50px; - } - } + .avatar { + width: 50px; + height: 50px; } } @container (max-width: 450px) { - .tkcbzcuz { - > .renote { - padding: 8px 16px 0 16px; - } + .renote { + padding: 8px 16px 0 16px; + } - > .info { - padding: 8px 16px 0 16px; - } + .tip { + padding: 8px 16px 0 16px; + } - > .article { - padding: 14px 16px 9px; + .article { + padding: 14px 16px 9px; + } - > .avatar { - margin: 0 10px 8px 0; - width: 46px; - height: 46px; - top: calc(14px + var(--stickyTop, 0px)); - } - } + .avatar { + margin: 0 10px 8px 0; + width: 46px; + height: 46px; + top: calc(14px + var(--stickyTop, 0px)); } } @container (max-width: 350px) { - .tkcbzcuz { - > .article { - > .main { - > .footer { - > .button { - &:not(:last-child) { - margin-right: 18px; - } - } - } - } + .footerButton { + &:not(:last-child) { + margin-right: 18px; } } } @container (max-width: 300px) { - .tkcbzcuz { - > .article { - > .avatar { - width: 44px; - height: 44px; - } - - > .main { - > .footer { - > .button { - &:not(:last-child) { - margin-right: 12px; - } - } - } - } + .avatar { + width: 44px; + height: 44px; + } + + .footerButton { + &:not(:last-child) { + margin-right: 12px; } } } diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index 38bd33ff7480bccf0547b7ff6e2b22399f5f6f3e..8771168a423ac358fb95fb8c3ab1e14703820776 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -1,12 +1,12 @@ <template> -<header class="kkwtjztg"> - <MkA v-once v-user-preview="note.user.id" class="name" :to="userPage(note.user)"> +<header :class="$style.root"> + <MkA v-once v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)"> <MkUserName :user="note.user"/> </MkA> - <div v-if="note.user.isBot" class="is-bot">bot</div> - <div class="username"><MkAcct :user="note.user"/></div> - <div class="info"> - <MkA class="created-at" :to="notePage(note)"> + <div v-if="note.user.isBot" :class="$style.isBot">bot</div> + <div :class="$style.username"><MkAcct :user="note.user"/></div> + <div :class="$style.info"> + <MkA :to="notePage(note)"> <MkTime :time="note.createdAt"/> </MkA> <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> @@ -32,49 +32,49 @@ defineProps<{ }>(); </script> -<style lang="scss" scoped> -.kkwtjztg { +<style lang="scss" module> +.root { display: flex; align-items: baseline; white-space: nowrap; +} - > .name { - flex-shrink: 1; - display: block; - margin: 0 .5em 0 0; - padding: 0; - overflow: hidden; - font-size: 1em; - font-weight: bold; - text-decoration: none; - text-overflow: ellipsis; +.name { + flex-shrink: 1; + display: block; + margin: 0 .5em 0 0; + padding: 0; + overflow: hidden; + font-size: 1em; + font-weight: bold; + text-decoration: none; + text-overflow: ellipsis; - &:hover { - text-decoration: underline; - } + &:hover { + text-decoration: underline; } +} - > .is-bot { - flex-shrink: 0; - align-self: center; - margin: 0 .5em 0 0; - padding: 1px 6px; - font-size: 80%; - border: solid 0.5px var(--divider); - border-radius: 3px; - } +.isBot { + flex-shrink: 0; + align-self: center; + margin: 0 .5em 0 0; + padding: 1px 6px; + font-size: 80%; + border: solid 0.5px var(--divider); + border-radius: 3px; +} - > .username { - flex-shrink: 9999999; - margin: 0 .5em 0 0; - overflow: hidden; - text-overflow: ellipsis; - } +.username { + flex-shrink: 9999999; + margin: 0 .5em 0 0; + overflow: hidden; + text-overflow: ellipsis; +} - > .info { - flex-shrink: 0; - margin-left: auto; - font-size: 0.9em; - } +.info { + flex-shrink: 0; + margin-left: auto; + font-size: 0.9em; } </style> diff --git a/packages/frontend/src/components/global/MkAcct.vue b/packages/frontend/src/components/global/MkAcct.vue index c3e806b5fb603c5a13827e7518fb3d544a53d5b7..2a43ded9e1ee3d70fedc43ffde62ccb0c4d7cac3 100644 --- a/packages/frontend/src/components/global/MkAcct.vue +++ b/packages/frontend/src/components/global/MkAcct.vue @@ -1,7 +1,7 @@ <template> -<span class="mk-acct"> - <span class="name">@{{ user.username }}</span> - <span v-if="user.host || detail || $store.state.showFullAcct" class="host">@{{ user.host || host }}</span> +<span> + <span>@{{ user.username }}</span> + <span v-if="user.host || detail || $store.state.showFullAcct" style="opacity: 0.5;">@{{ user.host || host }}</span> </span> </template> @@ -18,10 +18,3 @@ defineProps<{ const host = toUnicode(hostRaw); </script> -<style lang="scss" scoped> -.mk-acct { - > .host { - opacity: 0.5; - } -} -</style> diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 2f8e7945fc993d0d25ac47ef7562e2072c4b81f7..0a42a29323f7d5667665caeb3e9acd1d376c87c7 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -1,11 +1,11 @@ <template> -<span v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" class="eiwwqkts _noSelect" :class="{ cat: user.isCat, square: $store.state.squareAvatars }" :style="{ color }" :title="acct(user)" @click="onClick"> - <img class="inner" :src="url" decoding="async"/> - <MkUserOnlineIndicator v-if="showIndicator" class="indicator" :user="user"/> +<span v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" class="_noSelect" :style="{ color }" :title="acct(user)" @click="onClick"> + <img :class="$style.inner" :src="url" decoding="async"/> + <MkUserOnlineIndicator v-if="showIndicator" :class="$style.indicator" :user="user"/> </span> -<MkA v-else v-user-preview="disablePreview ? undefined : user.id" class="eiwwqkts _noSelect" :class="{ cat: user.isCat, square: $store.state.squareAvatars }" :style="{ color }" :to="userPage(user)" :title="acct(user)" :target="target"> - <img class="inner" :src="url" decoding="async"/> - <MkUserOnlineIndicator v-if="showIndicator" class="indicator" :user="user"/> +<MkA v-else v-user-preview="disablePreview ? undefined : user.id" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :to="userPage(user)" :title="acct(user)" :target="target"> + <img :class="$style.inner" :src="url" decoding="async"/> + <MkUserOnlineIndicator v-if="showIndicator" :class="$style.indicator" :user="user"/> </MkA> </template> @@ -68,75 +68,77 @@ watch(() => props.user.avatarBlurhash, () => { 75% { transform: rotate(0deg) skew(-30deg); } to { transform: rotate(-37.6deg) skew(-30deg); } } +</style> -.eiwwqkts { +<style lang="scss" module> +.root { position: relative; display: inline-block; vertical-align: bottom; flex-shrink: 0; border-radius: 100%; line-height: 16px; +} + +.inner { + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + border-radius: 100%; + z-index: 1; + overflow: clip; + object-fit: cover; + width: 100%; + height: 100%; +} + +.indicator { + position: absolute; + z-index: 1; + bottom: 0; + left: 0; + width: 20%; + height: 20%; +} + +.square { + border-radius: 20%; > .inner { - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; - border-radius: 100%; - z-index: 1; - overflow: clip; - object-fit: cover; - width: 100%; - height: 100%; + border-radius: 20%; } +} - > .indicator { - position: absolute; - z-index: 1; - bottom: 0; - left: 0; - width: 20%; - height: 20%; +.cat { + &:before, &:after { + background: #df548f; + border: solid 4px currentColor; + box-sizing: border-box; + content: ''; + display: inline-block; + height: 50%; + width: 50%; } - &.square { - border-radius: 20%; - - > .inner { - border-radius: 20%; - } + &:before { + border-radius: 0 75% 75%; + transform: rotate(37.5deg) skew(30deg); } - &.cat { - &:before, &:after { - background: #df548f; - border: solid 4px currentColor; - box-sizing: border-box; - content: ''; - display: inline-block; - height: 50%; - width: 50%; - } + &:after { + border-radius: 75% 0 75% 75%; + transform: rotate(-37.5deg) skew(-30deg); + } + &:hover { &:before { - border-radius: 0 75% 75%; - transform: rotate(37.5deg) skew(30deg); + animation: earwiggleleft 1s infinite; } &:after { - border-radius: 75% 0 75% 75%; - transform: rotate(-37.5deg) skew(-30deg); - } - - &:hover { - &:before { - animation: earwiggleleft 1s infinite; - } - - &:after { - animation: earwiggleright 1s infinite; - } + animation: earwiggleright 1s infinite; } } } diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index 67e9ef428a9fe7dc5704bddbe41a6ed81fdd20f1..bc88cf3be4ebdec696ceeed68d203b1a1c4cca84 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -1,6 +1,6 @@ <template> -<img v-if="isCustom" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" decoding="async"/> -<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" decoding="async" @pointerenter="computeTitle"/> +<img v-if="isCustom" :class="[$style.root, $style.custom, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" :src="url" :alt="alt" :title="alt" decoding="async"/> +<img v-else-if="char && !useOsNativeEmojis" :class="$style.root" :src="url" :alt="alt" decoding="async" @pointerenter="computeTitle"/> <span v-else-if="char && useOsNativeEmojis" :alt="alt" @pointerenter="computeTitle">{{ char }}</span> <span v-else>{{ emoji }}</span> </template> @@ -47,32 +47,32 @@ function computeTitle(event: PointerEvent): void { } </script> -<style lang="scss" scoped> -.mk-emoji { +<style lang="scss" module> +.root { height: 1.25em; vertical-align: -0.25em; +} - &.custom { - height: 2.5em; - vertical-align: middle; - transition: transform 0.2s ease; +.custom { + height: 2.5em; + vertical-align: middle; + transition: transform 0.2s ease; - &:hover { - transform: scale(1.2); - } + &:hover { + transform: scale(1.2); + } +} - &.normal { - height: 1.25em; - vertical-align: -0.25em; +.normal { + height: 1.25em; + vertical-align: -0.25em; - &:hover { - transform: none; - } - } + &:hover { + transform: none; } +} - &.noStyle { - height: auto !important; - } +.noStyle { + height: auto !important; } </style>