refactor(client): use css modules

This commit is contained in:
syuilo 2023-01-10 05:17:54 +09:00
parent 4d39d1caf6
commit 06f55ffb37
5 changed files with 425 additions and 480 deletions

View file

@ -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,11 +386,12 @@ function readPromo() {
}
}
&:hover > .article > .main > .footer > .button {
&:hover > .article > .main > .footer > .footerButton {
opacity: 1;
}
}
> .info {
.tip {
display: flex;
align-items: center;
padding: 16px 32px 8px 32px;
@ -399,82 +399,70 @@ function readPromo() {
font-size: 90%;
white-space: pre;
color: #d28a3f;
}
> i {
margin-right: 4px;
}
> .hide {
margin-left: auto;
color: inherit;
}
}
> .info + .article {
.tip + .article {
padding-top: 8px;
}
}
> .reply-to {
.replyTo {
opacity: 0.7;
padding-bottom: 0;
}
}
> .renote {
.renote {
display: flex;
align-items: center;
padding: 16px 32px 8px 32px;
line-height: 28px;
white-space: pre;
color: var(--renote);
}
> .avatar {
.renoteAvatar {
flex-shrink: 0;
display: inline-block;
width: 28px;
height: 28px;
margin: 0 8px 0 0;
border-radius: 6px;
}
}
> i {
margin-right: 4px;
}
> span {
.renoteText {
overflow: hidden;
flex-shrink: 1;
text-overflow: ellipsis;
white-space: nowrap;
}
> .name {
.renoteUserName {
font-weight: bold;
}
}
}
> .info {
.renoteInfo {
margin-left: auto;
font-size: 0.9em;
}
> .time {
.renoteTime {
flex-shrink: 0;
color: inherit;
}
> .dropdownIcon {
.renoteMenu {
margin-right: 4px;
}
}
}
}
}
> .renote + .article {
.renoteInfo + .article {
padding-top: 8px;
}
}
> .article {
.article {
display: flex;
padding: 28px 32px 18px;
}
> .avatar {
.avatar {
flex-shrink: 0;
display: block;
margin: 0 14px 8px 0;
@ -483,52 +471,58 @@ function readPromo() {
position: sticky;
top: calc(22px + var(--stickyTop, 0px));
left: 0;
}
}
> .main {
.main {
flex: 1;
min-width: 0;
}
> .body {
.body {
container-type: inline-size;
}
> .cw {
.cw {
cursor: default;
display: block;
margin: 0;
padding: 0;
overflow-wrap: break-word;
}
> .text {
.cwText {
margin-right: 8px;
}
}
}
> .content {
&.isLong {
> .showLess {
.content {
}
.contentIsLong {
}
.showLess {
width: 100%;
margin-top: 1em;
position: sticky;
bottom: 1em;
}
> span {
.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%);
}
}
}
}
&.collapsed {
.contentCollapsed {
position: relative;
max-height: 9em;
overflow: clip;
}
> .fade {
.collapsed {
display: block;
position: absolute;
bottom: 0;
@ -537,72 +531,60 @@ function readPromo() {
height: 64px;
background: linear-gradient(0deg, var(--panel), var(--X15));
> span {
&:hover > .collapsedLabel {
background: var(--panelHighlight);
}
}
.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%);
}
}
&:hover {
> span {
background: var(--panelHighlight);
}
}
}
}
> .text {
.text {
overflow-wrap: break-word;
}
> .reply {
.replyIcon {
color: var(--accent);
margin-right: 0.5em;
}
}
> .rp {
margin-left: 4px;
font-style: oblique;
color: var(--renote);
}
> .translation {
.translation {
border: solid 0.5px var(--divider);
border-radius: var(--radius);
padding: 12px;
margin-top: 8px;
}
}
}
> .url-preview {
.urlPreview {
margin-top: 8px;
}
}
> .poll {
.poll {
font-size: 80%;
}
}
> .renote {
.quote {
padding: 8px 0;
}
> .note {
.quoteNote {
padding: 16px;
border: dashed 1px var(--renote);
border-radius: 8px;
}
}
}
}
> .channel {
.channel {
opacity: 0.7;
font-size: 80%;
}
}
}
> .footer {
> .button {
.footerButton {
margin: 0;
padding: 8px;
opacity: 0.7;
@ -614,97 +596,65 @@ function readPromo() {
&:hover {
color: var(--fgHighlighted);
}
}
> .count {
.footerButtonCount {
display: inline;
margin: 0 0 0 8px;
opacity: 0.7;
}
&.reacted {
color: var(--accent);
}
}
}
}
}
> .reply {
border-top: solid 0.5px var(--divider);
}
}
@container (max-width: 500px) {
.tkcbzcuz {
.root {
font-size: 0.9em;
}
> .article {
> .avatar {
.avatar {
width: 50px;
height: 50px;
}
}
}
}
@container (max-width: 450px) {
.tkcbzcuz {
> .renote {
.renote {
padding: 8px 16px 0 16px;
}
> .info {
.tip {
padding: 8px 16px 0 16px;
}
> .article {
.article {
padding: 14px 16px 9px;
}
> .avatar {
.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 {
.footerButton {
&:not(:last-child) {
margin-right: 18px;
}
}
}
}
}
}
}
@container (max-width: 300px) {
.tkcbzcuz {
> .article {
> .avatar {
.avatar {
width: 44px;
height: 44px;
}
> .main {
> .footer {
> .button {
.footerButton {
&:not(:last-child) {
margin-right: 12px;
}
}
}
}
}
}
}
.muted {

View file

@ -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,13 +32,14 @@ defineProps<{
}>();
</script>
<style lang="scss" scoped>
.kkwtjztg {
<style lang="scss" module>
.root {
display: flex;
align-items: baseline;
white-space: nowrap;
}
> .name {
.name {
flex-shrink: 1;
display: block;
margin: 0 .5em 0 0;
@ -52,9 +53,9 @@ defineProps<{
&:hover {
text-decoration: underline;
}
}
}
> .is-bot {
.isBot {
flex-shrink: 0;
align-self: center;
margin: 0 .5em 0 0;
@ -62,19 +63,18 @@ defineProps<{
font-size: 80%;
border: solid 0.5px var(--divider);
border-radius: 3px;
}
}
> .username {
.username {
flex-shrink: 9999999;
margin: 0 .5em 0 0;
overflow: hidden;
text-overflow: ellipsis;
}
}
> .info {
.info {
flex-shrink: 0;
margin-left: auto;
font-size: 0.9em;
}
}
</style>

View file

@ -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>

View file

@ -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,16 +68,19 @@ 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 {
.inner {
position: absolute;
bottom: 0;
left: 0;
@ -89,26 +92,26 @@ watch(() => props.user.avatarBlurhash, () => {
object-fit: cover;
width: 100%;
height: 100%;
}
}
> .indicator {
.indicator {
position: absolute;
z-index: 1;
bottom: 0;
left: 0;
width: 20%;
height: 20%;
}
}
&.square {
.square {
border-radius: 20%;
> .inner {
border-radius: 20%;
}
}
}
&.cat {
.cat {
&:before, &:after {
background: #df548f;
border: solid 4px currentColor;
@ -138,6 +141,5 @@ watch(() => props.user.avatarBlurhash, () => {
animation: earwiggleright 1s infinite;
}
}
}
}
</style>

View file

@ -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,12 +47,13 @@ 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 {
.custom {
height: 2.5em;
vertical-align: middle;
transition: transform 0.2s ease;
@ -60,19 +61,18 @@ function computeTitle(event: PointerEvent): void {
&:hover {
transform: scale(1.2);
}
}
&.normal {
.normal {
height: 1.25em;
vertical-align: -0.25em;
&:hover {
transform: none;
}
}
}
}
&.noStyle {
.noStyle {
height: auto !important;
}
}
</style>