mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-30 11:43:08 +02:00
Refine UI (#7806)
* wip * wip * wip * wip * wip * wip * wip * wip * Update default.vue * wip
This commit is contained in:
parent
d252514a39
commit
361069314f
20 changed files with 517 additions and 386 deletions
|
@ -73,6 +73,22 @@ export default defineComponent({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@keyframes earwiggleleft {
|
||||||
|
from { transform: rotate(37.6deg) skew(30deg); }
|
||||||
|
25% { transform: rotate(10deg) skew(30deg); }
|
||||||
|
50% { transform: rotate(20deg) skew(30deg); }
|
||||||
|
75% { transform: rotate(0deg) skew(30deg); }
|
||||||
|
to { transform: rotate(37.6deg) skew(30deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes earwiggleright {
|
||||||
|
from { transform: rotate(-37.6deg) skew(-30deg); }
|
||||||
|
30% { transform: rotate(-10deg) skew(-30deg); }
|
||||||
|
55% { transform: rotate(-20deg) skew(-30deg); }
|
||||||
|
75% { transform: rotate(0deg) skew(-30deg); }
|
||||||
|
to { transform: rotate(-37.6deg) skew(-30deg); }
|
||||||
|
}
|
||||||
|
|
||||||
.eiwwqkts {
|
.eiwwqkts {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -132,6 +148,16 @@ export default defineComponent({
|
||||||
border-radius: 75% 0 75% 75%;
|
border-radius: 75% 0 75% 75%;
|
||||||
transform: rotate(-37.5deg) skew(-30deg);
|
transform: rotate(-37.5deg) skew(-30deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
&:before {
|
||||||
|
animation: earwiggleleft 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
animation: earwiggleright 1s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -99,7 +99,8 @@ export default defineComponent({
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: var(--stickyTop, 0px);
|
top: var(--stickyTop, 0px);
|
||||||
background: var(--panel);
|
padding: var(--x-padding);
|
||||||
|
background: var(--x-header, var(--panel));
|
||||||
/* TODO panelの半透明バージョンをプログラマティックに作りたい
|
/* TODO panelの半透明バージョンをプログラマティックに作りたい
|
||||||
background: var(--X17);
|
background: var(--X17);
|
||||||
-webkit-backdrop-filter: var(--blur, blur(8px));
|
-webkit-backdrop-filter: var(--blur, blur(8px));
|
||||||
|
|
|
@ -245,7 +245,7 @@ export default defineComponent({
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border: solid 1px var(--inputBorder);
|
border: solid 0.5px var(--inputBorder);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
@ -212,7 +212,7 @@ export default defineComponent({
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border: solid 1px var(--inputBorder);
|
border: solid 0.5px var(--inputBorder);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
@ -15,7 +15,7 @@ if (localStorage.getItem('accounts') != null) {
|
||||||
|
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import { Integrations } from '@sentry/tracing';
|
import { Integrations } from '@sentry/tracing';
|
||||||
import { computed, createApp, watch, markRaw } from 'vue';
|
import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue';
|
||||||
import compareVersions from 'compare-versions';
|
import compareVersions from 'compare-versions';
|
||||||
|
|
||||||
import widgets from '@client/widgets';
|
import widgets from '@client/widgets';
|
||||||
|
@ -47,6 +47,8 @@ window.onunhandledrejection = null;
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
console.warn('Development mode!!!');
|
console.warn('Development mode!!!');
|
||||||
|
|
||||||
|
console.info(`vue ${vueVersion}`);
|
||||||
|
|
||||||
(window as any).$i = $i;
|
(window as any).$i = $i;
|
||||||
(window as any).$store = defaultStore;
|
(window as any).$store = defaultStore;
|
||||||
|
|
||||||
|
|
134
src/client/pages/emojis.category.vue
Normal file
134
src/client/pages/emojis.category.vue
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
<template>
|
||||||
|
<div class="driuhtrh">
|
||||||
|
<div class="query">
|
||||||
|
<MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search">
|
||||||
|
<template #prefix><i class="fas fa-search"></i></template>
|
||||||
|
</MkInput>
|
||||||
|
|
||||||
|
<div class="tags">
|
||||||
|
<span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkFolder class="emojis" v-if="searchEmojis">
|
||||||
|
<template #header>{{ $ts.searchResult }}</template>
|
||||||
|
<div class="zuvgdzyt">
|
||||||
|
<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" class="emoji" :emoji="emoji"/>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder class="emojis" v-for="category in customEmojiCategories" :key="category">
|
||||||
|
<template #header>{{ category || $ts.other }}</template>
|
||||||
|
<div class="zuvgdzyt">
|
||||||
|
<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji" :emoji="emoji"/>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed } from 'vue';
|
||||||
|
import MkButton from '@client/components/ui/button.vue';
|
||||||
|
import MkInput from '@client/components/ui/input.vue';
|
||||||
|
import MkSelect from '@client/components/ui/select.vue';
|
||||||
|
import MkFolder from '@client/components/ui/folder.vue';
|
||||||
|
import MkTab from '@client/components/tab.vue';
|
||||||
|
import * as os from '@client/os';
|
||||||
|
import * as symbols from '@client/symbols';
|
||||||
|
import { emojiCategories, emojiTags } from '@client/instance';
|
||||||
|
import XEmoji from './emojis.emoji.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
MkButton,
|
||||||
|
MkInput,
|
||||||
|
MkSelect,
|
||||||
|
MkFolder,
|
||||||
|
MkTab,
|
||||||
|
XEmoji,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
q: '',
|
||||||
|
customEmojiCategories: emojiCategories,
|
||||||
|
customEmojis: this.$instance.emojis,
|
||||||
|
tags: emojiTags,
|
||||||
|
selectedTags: new Set(),
|
||||||
|
searchEmojis: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
q() { this.search(); },
|
||||||
|
selectedTags: {
|
||||||
|
handler() {
|
||||||
|
this.search();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
search() {
|
||||||
|
if ((this.q === '' || this.q == null) && this.selectedTags.size === 0) {
|
||||||
|
this.searchEmojis = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.selectedTags.size === 0) {
|
||||||
|
this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q));
|
||||||
|
} else {
|
||||||
|
this.searchEmojis = this.customEmojis.filter(e => (e.name.includes(this.q) || e.aliases.includes(this.q)) && [...this.selectedTags].every(t => e.aliases.includes(t)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleTag(tag) {
|
||||||
|
if (this.selectedTags.has(tag)) {
|
||||||
|
this.selectedTags.delete(tag);
|
||||||
|
} else {
|
||||||
|
this.selectedTags.add(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.driuhtrh {
|
||||||
|
background: var(--bg);
|
||||||
|
|
||||||
|
> .query {
|
||||||
|
background: var(--bg);
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
> .tags {
|
||||||
|
> .tag {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 8px 8px 0 0;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
background: var(--panel);
|
||||||
|
border: solid 0.5px var(--divider);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .emojis {
|
||||||
|
--x-header: var(--bg);
|
||||||
|
--x-padding: 0 16px;
|
||||||
|
|
||||||
|
.zuvgdzyt {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
margin: 0 var(--margin) var(--margin) var(--margin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
92
src/client/pages/emojis.emoji.vue
Normal file
92
src/client/pages/emojis.emoji.vue
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<button class="zuvgdzyu _button" @click="menu">
|
||||||
|
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||||
|
<div class="body">
|
||||||
|
<div class="name _monospace">{{ emoji.name }}</div>
|
||||||
|
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import * as os from '@client/os';
|
||||||
|
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||||
|
import VanillaTilt from 'vanilla-tilt';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
emoji: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
VanillaTilt.init(this.$el, {
|
||||||
|
reverse: true,
|
||||||
|
gyroscope: false,
|
||||||
|
scale: 1.1,
|
||||||
|
speed: 500,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
menu(ev) {
|
||||||
|
os.popupMenu([{
|
||||||
|
type: 'label',
|
||||||
|
text: ':' + this.emoji.name + ':',
|
||||||
|
}, {
|
||||||
|
text: this.$ts.copy,
|
||||||
|
icon: 'fas fa-copy',
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(`:${this.emoji.name}:`);
|
||||||
|
os.success();
|
||||||
|
}
|
||||||
|
}], ev.currentTarget || ev.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.zuvgdzyu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background: var(--panel);
|
||||||
|
border-radius: 8px;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transform: perspective(1000px);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .img {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
transform: translateZ(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .body {
|
||||||
|
padding: 0 0 0 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateZ(10px);
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 0.9em;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,151 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="driuhtrh">
|
<XCategory v-if="tab === 'category'"/>
|
||||||
<div class="query">
|
|
||||||
<MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search">
|
|
||||||
<template #prefix><i class="fas fa-search"></i></template>
|
|
||||||
</MkInput>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="emojis">
|
|
||||||
<MkFolder v-if="searchEmojis">
|
|
||||||
<template #header>{{ $ts.searchResult }}</template>
|
|
||||||
<div class="zuvgdzyt">
|
|
||||||
<button v-for="emoji in searchEmojis" :key="emoji.name" class="emoji _button" @click="menu(emoji, $event)">
|
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="name _monospace">{{ emoji.name }}</div>
|
|
||||||
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
<MkFolder v-for="category in customEmojiCategories" :key="category">
|
|
||||||
<template #header>{{ category || $ts.other }}</template>
|
|
||||||
<div class="zuvgdzyt">
|
|
||||||
<button v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji _button" @click="menu(emoji, $event)">
|
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="name _monospace">{{ emoji.name }}</div>
|
|
||||||
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, computed } from 'vue';
|
||||||
import MkButton from '@client/components/ui/button.vue';
|
|
||||||
import MkInput from '@client/components/ui/input.vue';
|
|
||||||
import MkSelect from '@client/components/ui/select.vue';
|
|
||||||
import MkFolder from '@client/components/ui/folder.vue';
|
|
||||||
import * as os from '@client/os';
|
import * as os from '@client/os';
|
||||||
import * as symbols from '@client/symbols';
|
import * as symbols from '@client/symbols';
|
||||||
import { emojiCategories } from '@client/instance';
|
import XCategory from './emojis.category.vue';
|
||||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkButton,
|
XCategory,
|
||||||
MkInput,
|
|
||||||
MkSelect,
|
|
||||||
MkFolder,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
[symbols.PAGE_INFO]: {
|
[symbols.PAGE_INFO]: computed(() => ({
|
||||||
title: this.$ts.customEmojis,
|
title: this.$ts.customEmojis,
|
||||||
icon: 'fas fa-laugh'
|
icon: 'fas fa-laugh',
|
||||||
},
|
bg: 'var(--bg)',
|
||||||
q: '',
|
})),
|
||||||
customEmojiCategories: emojiCategories,
|
tab: 'category',
|
||||||
customEmojis: this.$instance.emojis,
|
|
||||||
searchEmojis: null,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
|
||||||
q() {
|
|
||||||
if (this.q === '' || this.q == null) {
|
|
||||||
this.searchEmojis = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
menu(emoji, ev) {
|
|
||||||
os.popupMenu([{
|
|
||||||
type: 'label',
|
|
||||||
text: ':' + emoji.name + ':',
|
|
||||||
}, {
|
|
||||||
text: this.$ts.copy,
|
|
||||||
icon: 'fas fa-copy',
|
|
||||||
action: () => {
|
|
||||||
copyToClipboard(`:${emoji.name}:`);
|
|
||||||
os.success();
|
|
||||||
}
|
|
||||||
}], ev.currentTarget || ev.target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.driuhtrh {
|
|
||||||
> .query {
|
|
||||||
background: var(--bg);
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .emojis {
|
|
||||||
.zuvgdzyt {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
|
||||||
grid-gap: 12px;
|
|
||||||
margin: 0 var(--margin) var(--margin) var(--margin);
|
|
||||||
|
|
||||||
> .emoji {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px;
|
|
||||||
text-align: left;
|
|
||||||
border: solid 1px var(--divider);
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .img {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .body {
|
|
||||||
padding: 0 0 0 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
> .name {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 0.9em;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -22,7 +22,8 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
[symbols.PAGE_INFO]: {
|
[symbols.PAGE_INFO]: {
|
||||||
title: this.$ts.favorites,
|
title: this.$ts.favorites,
|
||||||
icon: 'fas fa-star'
|
icon: 'fas fa-star',
|
||||||
|
bg: 'var(--bg)',
|
||||||
},
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
endpoint: 'i/favorites',
|
endpoint: 'i/favorites',
|
||||||
|
|
|
@ -1,37 +1,39 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fcuexfpr _root">
|
<div class="fcuexfpr">
|
||||||
<transition name="fade" mode="out-in">
|
<div class="_root">
|
||||||
<div v-if="note" class="note">
|
<transition name="fade" mode="out-in">
|
||||||
<div class="_gap" v-if="showNext">
|
<div v-if="note" class="note">
|
||||||
<XNotes class="_content" :pagination="next" :no-gap="true"/>
|
<div class="_gap" v-if="showNext">
|
||||||
</div>
|
<XNotes class="_content" :pagination="next" :no-gap="true"/>
|
||||||
|
|
||||||
<div class="main _gap">
|
|
||||||
<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton>
|
|
||||||
<div class="note _gap">
|
|
||||||
<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/>
|
|
||||||
<XNoteDetailed v-model:note="note" :key="note.id" class="_isolated note"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="_content clips _gap" v-if="clips && clips.length > 0">
|
|
||||||
<div class="title">{{ $ts.clip }}</div>
|
|
||||||
<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
|
|
||||||
<b>{{ item.name }}</b>
|
|
||||||
<div v-if="item.description" class="description">{{ item.description }}</div>
|
|
||||||
<div class="user">
|
|
||||||
<MkAvatar :user="item.user" class="avatar" :show-indicator="true"/> <MkUserName :user="item.user" :nowrap="false"/>
|
|
||||||
</div>
|
|
||||||
</MkA>
|
|
||||||
</div>
|
|
||||||
<MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="_gap" v-if="showPrev">
|
<div class="main _gap">
|
||||||
<XNotes class="_content" :pagination="prev" :no-gap="true"/>
|
<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton>
|
||||||
|
<div class="note _gap">
|
||||||
|
<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/>
|
||||||
|
<XNoteDetailed v-model:note="note" :key="note.id" class="_isolated note"/>
|
||||||
|
</div>
|
||||||
|
<div class="_content clips _gap" v-if="clips && clips.length > 0">
|
||||||
|
<div class="title">{{ $ts.clip }}</div>
|
||||||
|
<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
|
||||||
|
<b>{{ item.name }}</b>
|
||||||
|
<div v-if="item.description" class="description">{{ item.description }}</div>
|
||||||
|
<div class="user">
|
||||||
|
<MkAvatar :user="item.user" class="avatar" :show-indicator="true"/> <MkUserName :user="item.user" :nowrap="false"/>
|
||||||
|
</div>
|
||||||
|
</MkA>
|
||||||
|
</div>
|
||||||
|
<MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="_gap" v-if="showPrev">
|
||||||
|
<XNotes class="_content" :pagination="prev" :no-gap="true"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<MkError v-else-if="error" @retry="fetch()"/>
|
||||||
<MkError v-else-if="error" @retry="fetch()"/>
|
<MkLoading v-else/>
|
||||||
<MkLoading v-else/>
|
</transition>
|
||||||
</transition>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -63,12 +65,14 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
[symbols.PAGE_INFO]: computed(() => this.note ? {
|
[symbols.PAGE_INFO]: computed(() => this.note ? {
|
||||||
title: this.$ts.note,
|
title: this.$ts.note,
|
||||||
|
subtitle: new Date(this.note.createdAt).toLocaleString(),
|
||||||
avatar: this.note.user,
|
avatar: this.note.user,
|
||||||
path: `/notes/${this.note.id}`,
|
path: `/notes/${this.note.id}`,
|
||||||
share: {
|
share: {
|
||||||
title: this.$t('noteOf', { user: this.note.user.name }),
|
title: this.$t('noteOf', { user: this.note.user.name }),
|
||||||
text: this.note.text,
|
text: this.note.text,
|
||||||
},
|
},
|
||||||
|
bg: 'var(--bg)',
|
||||||
} : null),
|
} : null),
|
||||||
note: null,
|
note: null,
|
||||||
clips: null,
|
clips: null,
|
||||||
|
@ -149,52 +153,54 @@ export default defineComponent({
|
||||||
.fcuexfpr {
|
.fcuexfpr {
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
|
|
||||||
> .note {
|
> ._root {
|
||||||
> .main {
|
> .note {
|
||||||
> .load {
|
> .main {
|
||||||
min-width: 0;
|
> .load {
|
||||||
margin: 0 auto;
|
min-width: 0;
|
||||||
border-radius: 999px;
|
margin: 0 auto;
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
&.next {
|
&.next {
|
||||||
margin-bottom: var(--margin);
|
margin-bottom: var(--margin);
|
||||||
}
|
|
||||||
|
|
||||||
&.prev {
|
|
||||||
margin-top: var(--margin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .note {
|
|
||||||
> .note {
|
|
||||||
border-radius: var(--radius);
|
|
||||||
background: var(--panel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .clips {
|
|
||||||
> .title {
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .item {
|
|
||||||
display: block;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
> .description {
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .user {
|
&.prev {
|
||||||
$height: 32px;
|
margin-top: var(--margin);
|
||||||
padding-top: 16px;
|
}
|
||||||
border-top: solid 0.5px var(--divider);
|
}
|
||||||
line-height: $height;
|
|
||||||
|
|
||||||
> .avatar {
|
> .note {
|
||||||
width: $height;
|
> .note {
|
||||||
height: $height;
|
border-radius: var(--radius);
|
||||||
|
background: var(--panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .clips {
|
||||||
|
> .title {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .item {
|
||||||
|
display: block;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
> .description {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .user {
|
||||||
|
$height: 32px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: solid 0.5px var(--divider);
|
||||||
|
line-height: $height;
|
||||||
|
|
||||||
|
> .avatar {
|
||||||
|
width: $height;
|
||||||
|
height: $height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export default defineComponent({
|
||||||
[symbols.PAGE_INFO]: {
|
[symbols.PAGE_INFO]: {
|
||||||
title: this.$ts.notifications,
|
title: this.$ts.notifications,
|
||||||
icon: 'fas fa-bell',
|
icon: 'fas fa-bell',
|
||||||
|
bg: 'var(--bg)',
|
||||||
actions: [{
|
actions: [{
|
||||||
text: this.$ts.markAllAsRead,
|
text: this.$ts.markAllAsRead,
|
||||||
icon: 'fas fa-check',
|
icon: 'fas fa-check',
|
||||||
|
|
|
@ -86,7 +86,8 @@ export default defineComponent({
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const indexInfo = {
|
const indexInfo = {
|
||||||
title: i18n.locale.settings,
|
title: i18n.locale.settings,
|
||||||
icon: 'fas fa-cog'
|
icon: 'fas fa-cog',
|
||||||
|
bg: 'var(--bg)',
|
||||||
};
|
};
|
||||||
const INFO = ref(indexInfo);
|
const INFO = ref(indexInfo);
|
||||||
const page = ref(props.initialPage);
|
const page = ref(props.initialPage);
|
||||||
|
|
|
@ -1,25 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }">
|
<div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }">
|
||||||
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block _isolated"/>
|
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/>
|
||||||
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block _isolated" fixed/>
|
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/>
|
||||||
<div class="tabs">
|
|
||||||
<div class="left">
|
|
||||||
<button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><i class="fas fa-home"></i></button>
|
|
||||||
<button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><i class="fas fa-comments"></i></button>
|
|
||||||
<button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><i class="fas fa-share-alt"></i></button>
|
|
||||||
<button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><i class="fas fa-globe"></i></button>
|
|
||||||
<span class="divider"></span>
|
|
||||||
<button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><i class="fas fa-at"></i><i v-if="$i.hasUnreadMentions" class="fas fa-circle i"></i></button>
|
|
||||||
<button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><i class="fas fa-envelope"></i><i v-if="$i.hasUnreadSpecifiedNotes" class="fas fa-circle i"></i></button>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><i class="fas fa-satellite-dish"></i><i v-if="$i.hasUnreadChannel" class="fas fa-circle i"></i></button>
|
|
||||||
<button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><i class="fas fa-satellite"></i><i v-if="$i.hasUnreadAntenna" class="fas fa-circle i"></i></button>
|
|
||||||
<button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><i class="fas fa-list-ul"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
|
<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
|
||||||
<div class="tl">
|
<div class="tl _block">
|
||||||
<XTimeline ref="tl" class="tl"
|
<XTimeline ref="tl" class="tl"
|
||||||
:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src"
|
:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src"
|
||||||
:src="src"
|
:src="src"
|
||||||
|
@ -63,12 +48,37 @@ export default defineComponent({
|
||||||
queue: 0,
|
queue: 0,
|
||||||
[symbols.PAGE_INFO]: computed(() => ({
|
[symbols.PAGE_INFO]: computed(() => ({
|
||||||
title: this.$ts.timeline,
|
title: this.$ts.timeline,
|
||||||
subtitle: this.src === 'local' ? this.$ts._timelines.local : this.src === 'social' ? this.$ts._timelines.social : this.src === 'global' ? this.$ts._timelines.global : this.$ts._timelines.home,
|
|
||||||
icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home',
|
icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home',
|
||||||
|
bg: 'var(--bg)',
|
||||||
actions: [{
|
actions: [{
|
||||||
icon: 'fas fa-calendar-alt',
|
icon: 'fas fa-calendar-alt',
|
||||||
text: this.$ts.jumpToSpecifiedDate,
|
text: this.$ts.jumpToSpecifiedDate,
|
||||||
handler: this.timetravel
|
handler: this.timetravel
|
||||||
|
}],
|
||||||
|
tabs: [{
|
||||||
|
active: this.src === 'home',
|
||||||
|
title: this.$ts._timelines.home,
|
||||||
|
icon: 'fas fa-home',
|
||||||
|
iconOnly: true,
|
||||||
|
onClick: () => { this.src = 'home'; this.saveSrc(); },
|
||||||
|
}, {
|
||||||
|
active: this.src === 'local',
|
||||||
|
title: this.$ts._timelines.local,
|
||||||
|
icon: 'fas fa-comments',
|
||||||
|
iconOnly: true,
|
||||||
|
onClick: () => { this.src = 'local'; this.saveSrc(); },
|
||||||
|
}, {
|
||||||
|
active: this.src === 'social',
|
||||||
|
title: this.$ts._timelines.social,
|
||||||
|
icon: 'fas fa-share-alt',
|
||||||
|
iconOnly: true,
|
||||||
|
onClick: () => { this.src = 'social'; this.saveSrc(); },
|
||||||
|
}, {
|
||||||
|
active: this.src === 'global',
|
||||||
|
title: this.$ts._timelines.global,
|
||||||
|
icon: 'fas fa-globe',
|
||||||
|
iconOnly: true,
|
||||||
|
onClick: () => { this.src = 'global'; this.saveSrc(); },
|
||||||
}]
|
}]
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
@ -213,6 +223,8 @@ export default defineComponent({
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.cmuxhskf {
|
.cmuxhskf {
|
||||||
|
padding: var(--margin);
|
||||||
|
|
||||||
> .new {
|
> .new {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: calc(var(--stickyTop, 0px) + 16px);
|
top: calc(var(--stickyTop, 0px) + 16px);
|
||||||
|
@ -227,79 +239,15 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .tabs {
|
> .tl {
|
||||||
display: flex;
|
background: var(--bg);
|
||||||
box-sizing: border-box;
|
border-radius: var(--radius);
|
||||||
padding: 0 8px;
|
overflow: clip;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: auto;
|
|
||||||
border-bottom: solid 0.5px var(--divider);
|
|
||||||
|
|
||||||
// 影の都合上
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
> .right {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .left, > .right {
|
|
||||||
> .tab {
|
|
||||||
position: relative;
|
|
||||||
height: 50px;
|
|
||||||
padding: 0 12px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--fgHighlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: var(--fgHighlighted);
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
background: var(--accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .i {
|
|
||||||
position: absolute;
|
|
||||||
top: 16px;
|
|
||||||
right: 8px;
|
|
||||||
color: var(--indicator);
|
|
||||||
font-size: 8px;
|
|
||||||
animation: blink 1s infinite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .divider {
|
|
||||||
display: inline-block;
|
|
||||||
width: 1px;
|
|
||||||
height: 28px;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin: 0 8px;
|
|
||||||
background: var(--divider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.min-width_800px {
|
&.min-width_800px {
|
||||||
> .tl {
|
max-width: 800px;
|
||||||
background: var(--bg);
|
margin: 0 auto;
|
||||||
padding: 32px 0;
|
|
||||||
|
|
||||||
> .tl {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -60,23 +60,9 @@
|
||||||
<XPhotos :user="user" :key="user.id" class="_gap"/>
|
<XPhotos :user="user" :key="user.id" class="_gap"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="nav _gap">
|
<div class="actions">
|
||||||
<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link">
|
<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
|
||||||
<i class="fas fa-comment-alt icon"></i>
|
<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
|
||||||
<span>{{ $ts.notes }}</span>
|
|
||||||
</MkA>
|
|
||||||
<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link">
|
|
||||||
<i class="fas fa-paperclip icon"></i>
|
|
||||||
<span>{{ $ts.clips }}</span>
|
|
||||||
</MkA>
|
|
||||||
<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link">
|
|
||||||
<i class="fas fa-file-alt icon"></i>
|
|
||||||
<span>{{ $ts.pages }}</span>
|
|
||||||
</MkA>
|
|
||||||
<div class="actions">
|
|
||||||
<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
|
|
||||||
<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<template v-if="page === 'index'">
|
<template v-if="page === 'index'">
|
||||||
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
||||||
|
@ -178,25 +164,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="nav _gap">
|
|
||||||
<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link" v-click-anime>
|
|
||||||
<i class="fas fa-comment-alt icon"></i>
|
|
||||||
<span>{{ $ts.notes }}</span>
|
|
||||||
</MkA>
|
|
||||||
<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link" v-click-anime>
|
|
||||||
<i class="fas fa-paperclip icon"></i>
|
|
||||||
<span>{{ $ts.clips }}</span>
|
|
||||||
</MkA>
|
|
||||||
<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link" v-click-anime>
|
|
||||||
<i class="fas fa-file-alt icon"></i>
|
|
||||||
<span>{{ $ts.pages }}</span>
|
|
||||||
</MkA>
|
|
||||||
<MkA :to="userPage(user, 'gallery')" :class="{ active: page === 'gallery' }" class="link" v-click-anime>
|
|
||||||
<i class="fas fa-icons icon"></i>
|
|
||||||
<span>{{ $ts.gallery }}</span>
|
|
||||||
</MkA>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-if="page === 'index'">
|
<template v-if="page === 'index'">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
||||||
|
@ -283,6 +250,27 @@ export default defineComponent({
|
||||||
share: {
|
share: {
|
||||||
title: this.user.name,
|
title: this.user.name,
|
||||||
},
|
},
|
||||||
|
bg: 'var(--bg)',
|
||||||
|
tabs: [{
|
||||||
|
active: this.page === 'index',
|
||||||
|
title: this.$ts.overview,
|
||||||
|
icon: 'fas fa-home',
|
||||||
|
}, {
|
||||||
|
active: this.page === 'clips',
|
||||||
|
title: this.$ts.clips,
|
||||||
|
icon: 'fas fa-paperclip',
|
||||||
|
onClick: () => { this.page = 'clips'; },
|
||||||
|
}, {
|
||||||
|
active: this.page === 'pages',
|
||||||
|
title: this.$ts.pages,
|
||||||
|
icon: 'fas fa-file-alt',
|
||||||
|
onClick: () => { this.page = 'pages'; },
|
||||||
|
}, {
|
||||||
|
active: this.page === 'gallery',
|
||||||
|
title: this.$ts.gallery,
|
||||||
|
icon: 'fas fa-icons',
|
||||||
|
onClick: () => { this.page = 'gallery'; },
|
||||||
|
}]
|
||||||
} : null),
|
} : null),
|
||||||
user: null,
|
user: null,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -314,7 +302,7 @@ export default defineComponent({
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
window.requestAnimationFrame(this.parallaxLoop);
|
window.requestAnimationFrame(this.parallaxLoop);
|
||||||
this.narrow = this.$el.clientWidth < 1000;
|
this.narrow = true//this.$el.clientWidth < 1000;
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
@ -772,37 +760,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
> .contents {
|
> .contents {
|
||||||
> .nav {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 90%;
|
|
||||||
|
|
||||||
> .link {
|
|
||||||
flex: 1;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 16px;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: solid 3px transparent;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: var(--accent);
|
|
||||||
border-bottom-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.active):hover {
|
|
||||||
color: var(--fgHighlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .icon {
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
margin-bottom: var(--margin);
|
margin-bottom: var(--margin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,6 @@ hr {
|
||||||
._panel {
|
._panel {
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
border: var(--panelBorder);
|
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
navFg: '@fg',
|
navFg: '@fg',
|
||||||
navHoverFg: ':lighten<17<@fg',
|
navHoverFg: ':lighten<17<@fg',
|
||||||
navActive: '@accent',
|
navActive: '@accent',
|
||||||
navIndicator: '@accent',
|
navIndicator: '@indicator',
|
||||||
link: '#44a4c1',
|
link: '#44a4c1',
|
||||||
hashtag: '#ff9156',
|
hashtag: '#ff9156',
|
||||||
mention: '@accent',
|
mention: '@accent',
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
navFg: '@fg',
|
navFg: '@fg',
|
||||||
navHoverFg: ':darken<17<@fg',
|
navHoverFg: ':darken<17<@fg',
|
||||||
navActive: '@accent',
|
navActive: '@accent',
|
||||||
navIndicator: '@accent',
|
navIndicator: '@indicator',
|
||||||
link: '#44a4c1',
|
link: '#44a4c1',
|
||||||
hashtag: '#ff9156',
|
hashtag: '#ff9156',
|
||||||
mention: '@accent',
|
mention: '@accent',
|
||||||
|
|
|
@ -1,25 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fdidabkb" :class="{ center }" :style="`--height:${height};`" :key="key">
|
<div class="fdidabkb" :class="{ slim: titleOnly || narrow }" :style="`--height:${height};`" :key="key">
|
||||||
<transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear>
|
<transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear>
|
||||||
<div class="buttons left" v-if="backButton">
|
<div class="buttons left" v-if="backButton">
|
||||||
<button class="_button button back" @click.stop="$emit('back')" @touchstart="preventDrag" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button>
|
<button class="_button button back" @click.stop="$emit('back')" @touchstart="preventDrag" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<template v-if="info">
|
<template v-if="info">
|
||||||
<div class="titleContainer">
|
<div class="titleContainer" @click="showTabsPopup">
|
||||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||||
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
|
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/>
|
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/>
|
||||||
<div v-else-if="info.title" class="title">{{ info.title }}</div>
|
<div v-else-if="info.title" class="title">{{ info.title }}</div>
|
||||||
<div class="subtitle" v-if="info.subtitle">
|
<div class="subtitle" v-if="!narrow && info.subtitle">
|
||||||
{{ info.subtitle }}
|
{{ info.subtitle }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="subtitle activeTab" v-if="narrow && hasTabs">
|
||||||
|
{{ info.tabs.find(tab => tab.active)?.title }}
|
||||||
|
<i class="chevron fas fa-chevron-down"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tabs" v-if="!narrow">
|
||||||
|
<button class="tab _button" v-for="tab in info.tabs" :class="{ active: tab.active }" @click="tab.onClick" v-tooltip="tab.title">
|
||||||
|
<i v-if="tab.icon" class="icon" :class="tab.icon"></i>
|
||||||
|
<span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="buttons right">
|
<div class="buttons right">
|
||||||
<template v-if="info.actions && showActions">
|
<template v-if="info.actions && !narrow">
|
||||||
<button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button>
|
<button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button>
|
||||||
</template>
|
</template>
|
||||||
<button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button>
|
<button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button>
|
||||||
|
@ -52,24 +62,28 @@ export default defineComponent({
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
center: {
|
titleOnly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showActions: false,
|
narrow: false,
|
||||||
height: 0,
|
height: 0,
|
||||||
key: 0,
|
key: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
hasTabs(): boolean {
|
||||||
|
return this.info.tabs && this.info.tabs.length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
shouldShowMenu() {
|
shouldShowMenu() {
|
||||||
if (this.info.actions != null && !this.showActions) return true;
|
if (this.info.actions != null && this.narrow) return true;
|
||||||
if (this.info.menu != null) return true;
|
if (this.info.menu != null) return true;
|
||||||
if (this.info.share != null) return true;
|
if (this.info.share != null) return true;
|
||||||
if (this.menu != null) return true;
|
if (this.menu != null) return true;
|
||||||
|
@ -85,10 +99,10 @@ export default defineComponent({
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.height = this.$el.parentElement.offsetHeight + 'px';
|
this.height = this.$el.parentElement.offsetHeight + 'px';
|
||||||
this.showActions = this.$el.parentElement.offsetWidth >= 500;
|
this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500;
|
||||||
new ResizeObserver((entries, observer) => {
|
new ResizeObserver((entries, observer) => {
|
||||||
this.height = this.$el.parentElement.offsetHeight + 'px';
|
this.height = this.$el.parentElement.offsetHeight + 'px';
|
||||||
this.showActions = this.$el.parentElement.offsetWidth >= 500;
|
this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500;
|
||||||
}).observe(this.$el);
|
}).observe(this.$el);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -102,7 +116,7 @@ export default defineComponent({
|
||||||
|
|
||||||
showMenu(ev) {
|
showMenu(ev) {
|
||||||
let menu = this.info.menu ? this.info.menu() : [];
|
let menu = this.info.menu ? this.info.menu() : [];
|
||||||
if (!this.showActions && this.info.actions) {
|
if (this.narrow && this.info.actions) {
|
||||||
menu = [...this.info.actions.map(x => ({
|
menu = [...this.info.actions.map(x => ({
|
||||||
text: x.text,
|
text: x.text,
|
||||||
icon: x.icon,
|
icon: x.icon,
|
||||||
|
@ -124,6 +138,18 @@ export default defineComponent({
|
||||||
popupMenu(menu, ev.currentTarget || ev.target);
|
popupMenu(menu, ev.currentTarget || ev.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showTabsPopup(ev) {
|
||||||
|
if (!this.hasTabs) return;
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
const menu = this.info.tabs.map(tab => ({
|
||||||
|
text: tab.title,
|
||||||
|
icon: tab.icon,
|
||||||
|
action: tab.onClick,
|
||||||
|
}));
|
||||||
|
popupMenu(menu, ev.currentTarget || ev.target);
|
||||||
|
},
|
||||||
|
|
||||||
preventDrag(ev) {
|
preventDrag(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
}
|
||||||
|
@ -135,7 +161,7 @@ export default defineComponent({
|
||||||
.fdidabkb {
|
.fdidabkb {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&.center {
|
&.slim {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
> .titleContainer {
|
> .titleContainer {
|
||||||
|
@ -190,6 +216,7 @@ export default defineComponent({
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
> .avatar {
|
> .avatar {
|
||||||
$size: 32px;
|
$size: 32px;
|
||||||
|
@ -219,6 +246,54 @@ export default defineComponent({
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&.activeTab {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
> .chevron {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .tabs {
|
||||||
|
margin-left: 16px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
> .tab {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 10px;
|
||||||
|
height: 100%;
|
||||||
|
font-weight: normal;
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .icon + .title {
|
||||||
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<main class="main" @contextmenu.stop="onContextmenu">
|
<main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }">
|
||||||
<header class="header" @click="onHeaderClick">
|
<header class="header" @click="onHeaderClick">
|
||||||
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
||||||
</header>
|
</header>
|
||||||
|
@ -145,6 +145,15 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}, '*');
|
}, '*');
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
|
window.addEventListener('touchmove', ev => {
|
||||||
|
this.$refs.live2d.contentWindow.postMessage({
|
||||||
|
type: 'moveCursor',
|
||||||
|
body: {
|
||||||
|
x: ev.touches[0].clientX - iframeRect.left,
|
||||||
|
y: ev.touches[0].clientY - iframeRect.top,
|
||||||
|
}
|
||||||
|
}, '*');
|
||||||
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<div class="mk-app" :class="{ wallpaper }">
|
<div class="mk-app" :class="{ wallpaper }">
|
||||||
<XSidebar ref="nav" class="sidebar"/>
|
<XSidebar ref="nav" class="sidebar"/>
|
||||||
|
|
||||||
<div class="contents" ref="contents" @contextmenu.stop="onContextmenu">
|
<div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }">
|
||||||
<header class="header" ref="header" @click="onHeaderClick">
|
<header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }">
|
||||||
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
||||||
</header>
|
</header>
|
||||||
<main ref="main">
|
<main ref="main">
|
||||||
|
@ -258,7 +258,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
> .sidebar {
|
> .sidebar {
|
||||||
border-right: solid 0.5px var(--divider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .contents {
|
> .contents {
|
||||||
|
@ -313,7 +312,8 @@ export default defineComponent({
|
||||||
|
|
||||||
> .widgets {
|
> .widgets {
|
||||||
padding: 0 var(--margin);
|
padding: 0 var(--margin);
|
||||||
border-left: solid 0.5px var(--divider);
|
//border-left: solid 0.5px var(--divider);
|
||||||
|
background: var(--navBg);
|
||||||
|
|
||||||
@media (max-width: $widgets-hide-threshold) {
|
@media (max-width: $widgets-hide-threshold) {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
Loading…
Reference in a new issue