summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-10-16 19:55:44 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-10-16 19:55:44 +0900
commit8a1f3a4c0b5732d0f08f0788d93c5934de8960c8 (patch)
treebe6fbcf3a1bbd78306d91e19ef6f3e7023f41561 /src/client
parentMerge branch 'develop' (diff)
parent12.92.0 (diff)
downloadmisskey-8a1f3a4c0b5732d0f08f0788d93c5934de8960c8.tar.gz
misskey-8a1f3a4c0b5732d0f08f0788d93c5934de8960c8.tar.bz2
misskey-8a1f3a4c0b5732d0f08f0788d93c5934de8960c8.zip
Merge branch 'develop'
Diffstat (limited to 'src/client')
-rw-r--r--src/client/account.ts74
-rw-r--r--src/client/components/abuse-report-window.vue2
-rw-r--r--src/client/components/autocomplete.vue44
-rw-r--r--src/client/components/captcha.vue4
-rw-r--r--src/client/components/channel-follow-button.vue2
-rw-r--r--src/client/components/cw-button.vue8
-rw-r--r--src/client/components/debobigego/base.vue (renamed from src/client/components/form/base.vue)24
-rw-r--r--src/client/components/debobigego/button.vue (renamed from src/client/components/form/button.vue)10
-rw-r--r--src/client/components/debobigego/debobigego.scss (renamed from src/client/components/form/form.scss)24
-rw-r--r--src/client/components/debobigego/group.vue (renamed from src/client/components/form/group.vue)26
-rw-r--r--src/client/components/debobigego/info.vue (renamed from src/client/components/form/info.vue)4
-rw-r--r--src/client/components/debobigego/input.vue (renamed from src/client/components/ui/input.vue)173
-rw-r--r--src/client/components/debobigego/key-value-view.vue (renamed from src/client/components/form/key-value-view.vue)8
-rw-r--r--src/client/components/debobigego/link.vue (renamed from src/client/components/form/link.vue)8
-rw-r--r--src/client/components/debobigego/object-view.vue (renamed from src/client/components/form/object-view.vue)10
-rw-r--r--src/client/components/debobigego/pagination.vue (renamed from src/client/components/form/pagination.vue)2
-rw-r--r--src/client/components/debobigego/radios.vue112
-rw-r--r--src/client/components/debobigego/range.vue122
-rw-r--r--src/client/components/debobigego/select.vue145
-rw-r--r--src/client/components/debobigego/suspense.vue (renamed from src/client/components/form/suspense.vue)12
-rw-r--r--src/client/components/debobigego/switch.vue132
-rw-r--r--src/client/components/debobigego/textarea.vue161
-rw-r--r--src/client/components/debobigego/tuple.vue (renamed from src/client/components/form/tuple.vue)2
-rw-r--r--src/client/components/dialog.vue4
-rw-r--r--src/client/components/emoji-picker-window.vue2
-rw-r--r--src/client/components/emoji-picker.vue2
-rw-r--r--src/client/components/follow-button.vue2
-rw-r--r--src/client/components/forgot-password.vue2
-rw-r--r--src/client/components/form-dialog.vue28
-rw-r--r--src/client/components/form/input.vue183
-rw-r--r--src/client/components/form/radio.vue (renamed from src/client/components/ui/radio.vue)0
-rw-r--r--src/client/components/form/radios.vue88
-rw-r--r--src/client/components/form/range.vue141
-rw-r--r--src/client/components/form/section.vue31
-rw-r--r--src/client/components/form/select.vue243
-rw-r--r--src/client/components/form/slot.vue50
-rw-r--r--src/client/components/form/switch.vue182
-rw-r--r--src/client/components/form/textarea.vue195
-rw-r--r--src/client/components/global/header.vue359
-rw-r--r--src/client/components/global/spacer.vue76
-rw-r--r--src/client/components/index.ts4
-rw-r--r--src/client/components/instance-stats.vue2
-rw-r--r--src/client/components/media-caption.vue32
-rw-r--r--src/client/components/mfm.ts2
-rw-r--r--src/client/components/modal-page-window.vue51
-rw-r--r--src/client/components/note-detailed.vue8
-rw-r--r--src/client/components/note-preview.vue35
-rw-r--r--src/client/components/note-simple.vue113
-rw-r--r--src/client/components/note.sub.vue2
-rw-r--r--src/client/components/note.vue10
-rw-r--r--src/client/components/notification-setting-window.vue4
-rw-r--r--src/client/components/notifications.vue13
-rw-r--r--src/client/components/page-window.vue17
-rw-r--r--src/client/components/page/page.number-input.vue2
-rw-r--r--src/client/components/page/page.post.vue2
-rw-r--r--src/client/components/page/page.radio-button.vue2
-rw-r--r--src/client/components/page/page.switch.vue2
-rw-r--r--src/client/components/page/page.text-input.vue2
-rw-r--r--src/client/components/page/page.textarea-input.vue2
-rw-r--r--src/client/components/page/page.textarea.vue2
-rw-r--r--src/client/components/poll-editor.vue6
-rw-r--r--src/client/components/post-form.vue48
-rw-r--r--src/client/components/sample.vue8
-rwxr-xr-xsrc/client/components/signin.vue10
-rw-r--r--src/client/components/signup-dialog.vue6
-rw-r--r--src/client/components/signup.vue93
-rw-r--r--src/client/components/tab.vue21
-rw-r--r--src/client/components/taskmanager.api-window.vue2
-rw-r--r--src/client/components/taskmanager.vue2
-rw-r--r--src/client/components/token-generate-window.vue6
-rw-r--r--src/client/components/ui/button.vue67
-rw-r--r--src/client/components/ui/folder.vue25
-rw-r--r--src/client/components/ui/info.vue11
-rw-r--r--src/client/components/ui/menu.vue85
-rw-r--r--src/client/components/ui/popup-menu.vue4
-rw-r--r--src/client/components/ui/popup.vue5
-rw-r--r--src/client/components/ui/radios.vue58
-rw-r--r--src/client/components/ui/range.vue139
-rw-r--r--src/client/components/ui/select.vue262
-rw-r--r--src/client/components/ui/super-menu.vue151
-rw-r--r--src/client/components/ui/switch.vue144
-rw-r--r--src/client/components/ui/textarea.vue254
-rw-r--r--src/client/components/ui/window.vue23
-rw-r--r--src/client/components/user-select-dialog.vue4
-rw-r--r--src/client/components/widgets.vue2
-rw-r--r--src/client/directives/click-anime.ts3
-rw-r--r--src/client/directives/get-size.ts34
-rw-r--r--src/client/directives/index.ts2
-rw-r--r--src/client/directives/tooltip.ts9
-rw-r--r--src/client/events.ts4
-rw-r--r--src/client/pages/_error_.vue38
-rw-r--r--src/client/pages/about-misskey.vue14
-rw-r--r--src/client/pages/about.vue16
-rw-r--r--src/client/pages/advanced-theme-editor.vue30
-rw-r--r--src/client/pages/announcements.vue37
-rw-r--r--src/client/pages/antenna-timeline.vue2
-rw-r--r--src/client/pages/api-console.vue8
-rw-r--r--src/client/pages/channel-editor.vue4
-rw-r--r--src/client/pages/channels.vue2
-rw-r--r--src/client/pages/docs.vue4
-rw-r--r--src/client/pages/emojis.category.vue9
-rw-r--r--src/client/pages/emojis.emoji.vue14
-rw-r--r--src/client/pages/emojis.vue12
-rw-r--r--src/client/pages/explore.vue160
-rw-r--r--src/client/pages/favorites.vue14
-rw-r--r--src/client/pages/featured.vue17
-rw-r--r--src/client/pages/federation.vue189
-rw-r--r--src/client/pages/gallery/edit.vue24
-rw-r--r--src/client/pages/gallery/index.vue4
-rw-r--r--src/client/pages/instance-info.vue28
-rw-r--r--src/client/pages/instance/abuses.vue11
-rw-r--r--src/client/pages/instance/ads.vue98
-rw-r--r--src/client/pages/instance/announcements.vue59
-rw-r--r--src/client/pages/instance/bot-protection.vue42
-rw-r--r--src/client/pages/instance/database.vue13
-rw-r--r--src/client/pages/instance/email-settings.vue35
-rw-r--r--src/client/pages/instance/emoji-edit-dialog.vue8
-rw-r--r--src/client/pages/instance/emojis.vue33
-rw-r--r--src/client/pages/instance/file-dialog.vue2
-rw-r--r--src/client/pages/instance/files-settings.vue23
-rw-r--r--src/client/pages/instance/files.vue44
-rw-r--r--src/client/pages/instance/index.vue236
-rw-r--r--src/client/pages/instance/instance-block.vue21
-rw-r--r--src/client/pages/instance/instance.vue4
-rw-r--r--src/client/pages/instance/integrations-discord.vue18
-rw-r--r--src/client/pages/instance/integrations-github.vue18
-rw-r--r--src/client/pages/instance/integrations-twitter.vue18
-rw-r--r--src/client/pages/instance/integrations.vue19
-rw-r--r--src/client/pages/instance/logs.vue6
-rw-r--r--src/client/pages/instance/metrics.vue22
-rw-r--r--src/client/pages/instance/object-storage.vue39
-rw-r--r--src/client/pages/instance/other-settings.vue21
-rw-r--r--src/client/pages/instance/overview.vue37
-rw-r--r--src/client/pages/instance/proxy-account.vue19
-rw-r--r--src/client/pages/instance/queue.chart.vue6
-rw-r--r--src/client/pages/instance/queue.vue5
-rw-r--r--src/client/pages/instance/relays.vue11
-rw-r--r--src/client/pages/instance/security.vue24
-rw-r--r--src/client/pages/instance/service-worker.vue21
-rw-r--r--src/client/pages/instance/settings.vue49
-rw-r--r--src/client/pages/instance/users.vue63
-rw-r--r--src/client/pages/mentions.vue15
-rw-r--r--src/client/pages/messages.vue13
-rw-r--r--src/client/pages/messaging/index.vue74
-rw-r--r--src/client/pages/messaging/messaging-room.message.vue2
-rw-r--r--src/client/pages/messaging/messaging-room.vue2
-rw-r--r--src/client/pages/mfm-cheat-sheet.vue2
-rw-r--r--src/client/pages/my-antennas/editor.vue30
-rw-r--r--src/client/pages/my-groups/index.vue2
-rw-r--r--src/client/pages/my-lists/index.vue25
-rw-r--r--src/client/pages/my-lists/list.vue55
-rw-r--r--src/client/pages/notifications.vue66
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.button.vue6
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.canvas.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.counter.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.if.vue4
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.note.vue4
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.number-input.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.post.vue6
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.radio-button.vue4
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.section.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.switch.vue4
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.text-input.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.textarea-input.vue4
-rw-r--r--src/client/pages/page-editor/page-editor.blocks.vue13
-rw-r--r--src/client/pages/page-editor/page-editor.script-block.vue94
-rw-r--r--src/client/pages/page-editor/page-editor.vue182
-rw-r--r--src/client/pages/page.vue110
-rw-r--r--src/client/pages/pages.vue59
-rw-r--r--src/client/pages/reset-password.vue12
-rw-r--r--src/client/pages/reversi/game.setting.vue6
-rw-r--r--src/client/pages/room/room.vue2
-rw-r--r--src/client/pages/search.vue15
-rw-r--r--src/client/pages/settings/2fa.vue10
-rw-r--r--src/client/pages/settings/account-info.vue10
-rw-r--r--src/client/pages/settings/accounts.vue15
-rw-r--r--src/client/pages/settings/api.vue11
-rw-r--r--src/client/pages/settings/apps.vue13
-rw-r--r--src/client/pages/settings/custom-css.vue15
-rw-r--r--src/client/pages/settings/deck.vue21
-rw-r--r--src/client/pages/settings/delete-account.vue11
-rw-r--r--src/client/pages/settings/drive.vue21
-rw-r--r--src/client/pages/settings/email-address.vue11
-rw-r--r--src/client/pages/settings/email-notification.vue21
-rw-r--r--src/client/pages/settings/email.vue15
-rw-r--r--src/client/pages/settings/experimental-features.vue10
-rw-r--r--src/client/pages/settings/general.vue61
-rw-r--r--src/client/pages/settings/import-export.vue58
-rw-r--r--src/client/pages/settings/index.vue237
-rw-r--r--src/client/pages/settings/integration.vue23
-rw-r--r--src/client/pages/settings/menu.vue15
-rw-r--r--src/client/pages/settings/mute-block.vue13
-rw-r--r--src/client/pages/settings/notifications.vue13
-rw-r--r--src/client/pages/settings/other.vue17
-rw-r--r--src/client/pages/settings/plugin.install.vue15
-rw-r--r--src/client/pages/settings/plugin.manage.vue21
-rw-r--r--src/client/pages/settings/plugin.vue9
-rw-r--r--src/client/pages/settings/privacy.vue29
-rw-r--r--src/client/pages/settings/profile.vue35
-rw-r--r--src/client/pages/settings/reaction.vue19
-rw-r--r--src/client/pages/settings/registry.keys.vue13
-rw-r--r--src/client/pages/settings/registry.value.vue15
-rw-r--r--src/client/pages/settings/registry.vue13
-rw-r--r--src/client/pages/settings/security.vue15
-rw-r--r--src/client/pages/settings/sounds.vue15
-rw-r--r--src/client/pages/settings/theme.install.vue13
-rw-r--r--src/client/pages/settings/theme.manage.vue11
-rw-r--r--src/client/pages/settings/theme.vue27
-rw-r--r--src/client/pages/settings/update.vue15
-rw-r--r--src/client/pages/settings/word-mute.vue19
-rw-r--r--src/client/pages/signup-complete.vue50
-rw-r--r--src/client/pages/test.vue8
-rw-r--r--src/client/pages/theme-editor.vue30
-rw-r--r--src/client/pages/timeline.vue40
-rw-r--r--src/client/pages/user-ap-info.vue16
-rw-r--r--src/client/pages/user-info.vue26
-rw-r--r--src/client/pages/user-list-timeline.vue2
-rw-r--r--src/client/pages/user/clips.vue7
-rw-r--r--src/client/pages/user/follow-list.vue7
-rw-r--r--src/client/pages/user/gallery.vue7
-rw-r--r--src/client/pages/user/index.timeline.vue4
-rw-r--r--src/client/pages/user/index.vue349
-rw-r--r--src/client/pages/user/pages.vue7
-rw-r--r--src/client/pages/welcome.entrance.a.vue2
-rw-r--r--src/client/pages/welcome.entrance.b.vue2
-rw-r--r--src/client/pages/welcome.entrance.c.vue2
-rw-r--r--src/client/pages/welcome.setup.vue2
-rw-r--r--src/client/router.ts1
-rw-r--r--src/client/scripts/autocomplete.ts35
-rw-r--r--src/client/scripts/idb-proxy.ts5
-rw-r--r--src/client/scripts/physics.ts2
-rw-r--r--src/client/scripts/scroll.ts20
-rw-r--r--src/client/scripts/theme.ts7
-rw-r--r--src/client/style.scss34
-rw-r--r--src/client/themes/_dark.json53
-rw-r--r--src/client/themes/_light.json53
-rw-r--r--src/client/themes/d-astro.json52
-rw-r--r--src/client/themes/d-botanical.json526
-rw-r--r--src/client/themes/d-future.json52
-rw-r--r--src/client/ui/_common_/header.vue302
-rw-r--r--src/client/ui/_common_/sidebar.vue72
-rw-r--r--src/client/ui/chat/index.vue7
-rw-r--r--src/client/ui/chat/note-preview.vue2
-rw-r--r--src/client/ui/chat/note.sub.vue2
-rw-r--r--src/client/ui/chat/note.vue10
-rw-r--r--src/client/ui/chat/post-form.vue2
-rw-r--r--src/client/ui/chat/side.vue6
-rw-r--r--src/client/ui/deck/column.vue17
-rw-r--r--src/client/ui/deck/deck-store.ts10
-rw-r--r--src/client/ui/deck/main-column.vue9
-rw-r--r--src/client/ui/default.header.vue70
-rw-r--r--src/client/ui/default.side.vue8
-rw-r--r--src/client/ui/default.sidebar.vue70
-rw-r--r--src/client/ui/default.vue54
-rw-r--r--src/client/ui/universal.vue39
-rw-r--r--src/client/ui/zen.vue4
-rw-r--r--src/client/widgets/aiscript.vue2
-rw-r--r--src/client/widgets/memo.vue2
-rw-r--r--src/client/widgets/notifications.vue2
259 files changed, 5261 insertions, 3999 deletions
diff --git a/src/client/account.ts b/src/client/account.ts
index 6e26ac1f7d..a3fe082a22 100644
--- a/src/client/account.ts
+++ b/src/client/account.ts
@@ -1,9 +1,10 @@
import { del, get, set } from '@client/scripts/idb-proxy';
import { reactive } from 'vue';
import { apiUrl } from '@client/config';
-import { waiting } from '@client/os';
+import { waiting, api, popup, popupMenu, success } from '@client/os';
import { unisonReload, reloadChannel } from '@client/scripts/unison-reload';
import { showSuspendedDialog } from './scripts/show-suspended-dialog';
+import { i18n } from './i18n';
// TODO: 他のタブと永続化されたstateを同期
@@ -129,6 +130,77 @@ export async function login(token: Account['token'], redirect?: string) {
unisonReload();
}
+export async function openAccountMenu(ev: MouseEvent) {
+ function showSigninDialog() {
+ popup(import('@client/components/signin-dialog.vue'), {}, {
+ done: res => {
+ addAccount(res.id, res.i);
+ success();
+ },
+ }, 'closed');
+ }
+
+ function createAccount() {
+ popup(import('@client/components/signup-dialog.vue'), {}, {
+ done: res => {
+ addAccount(res.id, res.i);
+ switchAccountWithToken(res.i);
+ },
+ }, 'closed');
+ }
+
+ async function switchAccount(account: any) {
+ const storedAccounts = await getAccounts();
+ const token = storedAccounts.find(x => x.id === account.id).token;
+ switchAccountWithToken(token);
+ }
+
+ function switchAccountWithToken(token: string) {
+ login(token);
+ }
+
+ const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id));
+ const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) });
+
+ const accountItemPromises = storedAccounts.map(a => new Promise(res => {
+ accountsPromise.then(accounts => {
+ const account = accounts.find(x => x.id === a.id);
+ if (account == null) return res(null);
+ res({
+ type: 'user',
+ user: account,
+ action: () => { switchAccount(account); }
+ });
+ });
+ }));
+
+ popupMenu([...[{
+ type: 'link',
+ text: i18n.locale.profile,
+ to: `/@${ $i.username }`,
+ avatar: $i,
+ }, null, ...accountItemPromises, {
+ icon: 'fas fa-plus',
+ text: i18n.locale.addAccount,
+ action: () => {
+ popupMenu([{
+ text: i18n.locale.existingAccount,
+ action: () => { showSigninDialog(); },
+ }, {
+ text: i18n.locale.createAccount,
+ action: () => { createAccount(); },
+ }], ev.currentTarget || ev.target);
+ },
+ }, {
+ type: 'link',
+ icon: 'fas fa-users',
+ text: i18n.locale.manageAccounts,
+ to: `/settings/accounts`,
+ }]], ev.currentTarget || ev.target, {
+ align: 'left'
+ });
+}
+
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
diff --git a/src/client/components/abuse-report-window.vue b/src/client/components/abuse-report-window.vue
index 266c0d566f..21a19385ae 100644
--- a/src/client/components/abuse-report-window.vue
+++ b/src/client/components/abuse-report-window.vue
@@ -25,7 +25,7 @@
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import XWindow from '@client/components/ui/window.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import MkButton from '@client/components/ui/button.vue';
import * as os from '@client/os';
diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue
index 065ee6de2e..e621b26229 100644
--- a/src/client/components/autocomplete.vue
+++ b/src/client/components/autocomplete.vue
@@ -10,12 +10,12 @@
</li>
<li @click="chooseUser()" @keydown="onKeydown" tabindex="-1" class="choose">{{ $ts.selectUser }}</li>
</ol>
- <ol class="hashtags" ref="suggests" v-if="hashtags.length > 0">
+ <ol class="hashtags" ref="suggests" v-else-if="hashtags.length > 0">
<li v-for="hashtag in hashtags" @click="complete(type, hashtag)" @keydown="onKeydown" tabindex="-1">
<span class="name">{{ hashtag }}</span>
</li>
</ol>
- <ol class="emojis" ref="suggests" v-if="emojis.length > 0">
+ <ol class="emojis" ref="suggests" v-else-if="emojis.length > 0">
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
<span class="emoji" v-else-if="!$store.state.useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
@@ -24,6 +24,11 @@
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
</li>
</ol>
+ <ol class="mfmTags" ref="suggests" v-else-if="mfmTags.length > 0">
+ <li v-for="tag in mfmTags" @click="complete(type, tag)" @keydown="onKeydown" tabindex="-1">
+ <span class="tag">{{ tag }}</span>
+ </li>
+ </ol>
</div>
</template>
@@ -106,6 +111,8 @@ emojiDefinitions.sort((a, b) => a.name.length - b.name.length);
const emojiDb = markRaw(emojiDefinitions.concat(emjdb));
//#endregion
+const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle'];
+
export default defineComponent({
props: {
type: {
@@ -137,11 +144,6 @@ export default defineComponent({
type: Number,
required: true,
},
-
- showing: {
- type: Boolean,
- required: true
- },
},
emits: ['done', 'closed'],
@@ -154,18 +156,11 @@ export default defineComponent({
hashtags: [],
emojis: [],
items: [],
+ mfmTags: [],
select: -1,
}
},
- watch: {
- showing() {
- if (!this.showing) {
- this.$emit('closed');
- }
- }
- },
-
updated() {
this.setPosition();
this.items = (this.$refs.suggests as Element | undefined)?.children || [];
@@ -236,7 +231,7 @@ export default defineComponent({
}
}
- if (this.type == 'user') {
+ if (this.type === 'user') {
if (this.q == null) {
this.users = [];
this.fetching = false;
@@ -262,7 +257,7 @@ export default defineComponent({
sessionStorage.setItem(cacheKey, JSON.stringify(users));
});
}
- } else if (this.type == 'hashtag') {
+ } else if (this.type === 'hashtag') {
if (this.q == null || this.q == '') {
this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]');
this.fetching = false;
@@ -286,7 +281,7 @@ export default defineComponent({
});
}
}
- } else if (this.type == 'emoji') {
+ } else if (this.type === 'emoji') {
if (this.q == null || this.q == '') {
// 最近使った絵文字をサジェスト
this.emojis = this.$store.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji == emoji)).filter(x => x != null);
@@ -314,6 +309,13 @@ export default defineComponent({
}
this.emojis = matched;
+ } else if (this.type === 'mfmTag') {
+ if (this.q == null || this.q == '') {
+ this.mfmTags = MFM_TAGS;
+ return;
+ }
+
+ this.mfmTags = MFM_TAGS.filter(tag => tag.startsWith(this.q));
}
},
@@ -490,5 +492,11 @@ export default defineComponent({
margin: 0 0 0 8px;
}
}
+
+ > .mfmTags > li {
+
+ .name {
+ }
+ }
}
</style>
diff --git a/src/client/components/captcha.vue b/src/client/components/captcha.vue
index 5da8ede3b9..baa922506e 100644
--- a/src/client/components/captcha.vue
+++ b/src/client/components/captcha.vue
@@ -39,7 +39,7 @@ export default defineComponent({
type: String,
required: true,
},
- value: {
+ modelValue: {
type: String,
},
},
@@ -116,7 +116,7 @@ export default defineComponent({
}
},
callback(response?: string) {
- this.$emit('update:value', typeof response == 'string' ? response : null);
+ this.$emit('update:modelValue', typeof response == 'string' ? response : null);
},
},
});
diff --git a/src/client/components/channel-follow-button.vue b/src/client/components/channel-follow-button.vue
index 6f9405b97f..bd8627f6e8 100644
--- a/src/client/components/channel-follow-button.vue
+++ b/src/client/components/channel-follow-button.vue
@@ -91,7 +91,7 @@ export default defineComponent({
width: 31px;
}
- &:focus {
+ &:focus-visible {
&:after {
content: "";
pointer-events: none;
diff --git a/src/client/components/cw-button.vue b/src/client/components/cw-button.vue
index d2336085ad..3a172f5d5e 100644
--- a/src/client/components/cw-button.vue
+++ b/src/client/components/cw-button.vue
@@ -1,7 +1,7 @@
<template>
<button class="nrvgflfu _button" @click="toggle">
- <b>{{ value ? $ts._cw.hide : $ts._cw.show }}</b>
- <span v-if="!value">{{ label }}</span>
+ <b>{{ modelValue ? $ts._cw.hide : $ts._cw.show }}</b>
+ <span v-if="!modelValue">{{ label }}</span>
</button>
</template>
@@ -12,7 +12,7 @@ import { concat } from '../../prelude/array';
export default defineComponent({
props: {
- value: {
+ modelValue: {
type: Boolean,
required: true
},
@@ -36,7 +36,7 @@ export default defineComponent({
length,
toggle() {
- this.$emit('update:value', !this.value);
+ this.$emit('update:modelValue', !this.modelValue);
}
}
});
diff --git a/src/client/components/form/base.vue b/src/client/components/debobigego/base.vue
index 132942d527..f551a3478b 100644
--- a/src/client/components/form/base.vue
+++ b/src/client/components/debobigego/base.vue
@@ -21,39 +21,39 @@ export default defineComponent({
<style lang="scss" scoped>
.rbusrurv {
// 他のCSSからも参照されるので消さないように
- --formXPadding: 32px;
- --formYPadding: 32px;
+ --debobigegoXPadding: 32px;
+ --debobigegoYPadding: 32px;
- --formContentHMargin: 16px;
+ --debobigegoContentHMargin: 16px;
font-size: 95%;
line-height: 1.3em;
background: var(--bg);
- padding: var(--formYPadding) var(--formXPadding);
+ padding: var(--debobigegoYPadding) var(--debobigegoXPadding);
max-width: 750px;
margin: 0 auto;
&:not(.wide).max-width_400px {
- --formXPadding: 0px;
+ --debobigegoXPadding: 0px;
> ::v-deep(*) {
- ._formPanel {
+ ._debobigegoPanel {
border: solid 0.5px var(--divider);
border-radius: 0;
border-left: none;
border-right: none;
}
- ._form_group {
- > *:not(._formNoConcat) {
- &:not(:last-child):not(._formNoConcatPrev) {
- &._formPanel, ._formPanel {
+ ._debobigego_group {
+ > *:not(._debobigegoNoConcat) {
+ &:not(:last-child):not(._debobigegoNoConcatPrev) {
+ &._debobigegoPanel, ._debobigegoPanel {
border-bottom: solid 0.5px var(--divider);
}
}
- &:not(:first-child):not(._formNoConcatNext) {
- &._formPanel, ._formPanel {
+ &:not(:first-child):not(._debobigegoNoConcatNext) {
+ &._debobigegoPanel, ._debobigegoPanel {
border-top: none;
}
}
diff --git a/src/client/components/form/button.vue b/src/client/components/debobigego/button.vue
index b4f0890945..b883e817a4 100644
--- a/src/client/components/form/button.vue
+++ b/src/client/components/debobigego/button.vue
@@ -1,7 +1,7 @@
<template>
-<div class="yzpgjkxe _formItem">
- <div class="_formLabel"><slot name="label"></slot></div>
- <button class="main _button _formPanel _formClickable" :class="{ center, primary, danger }">
+<div class="yzpgjkxe _debobigegoItem">
+ <div class="_debobigegoLabel"><slot name="label"></slot></div>
+ <button class="main _button _debobigegoPanel _debobigegoClickable" :class="{ center, primary, danger }">
<slot></slot>
<div class="suffix">
<slot name="suffix"></slot>
@@ -10,13 +10,13 @@
</div>
</div>
</button>
- <div class="_formCaption"><slot name="desc"></slot></div>
+ <div class="_debobigegoCaption"><slot name="desc"></slot></div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import './form.scss';
+import './debobigego.scss';
export default defineComponent({
props: {
diff --git a/src/client/components/form/form.scss b/src/client/components/debobigego/debobigego.scss
index 00f40df9b1..833b656b66 100644
--- a/src/client/components/form/form.scss
+++ b/src/client/components/debobigego/debobigego.scss
@@ -1,9 +1,9 @@
-._formPanel {
+._debobigegoPanel {
background: var(--panel);
border-radius: var(--radius);
transition: background 0.2s ease;
- &._formClickable {
+ &._debobigegoClickable {
&:hover {
//background: var(--panelHighlight);
}
@@ -15,8 +15,8 @@
}
}
-._formLabel,
-._formCaption {
+._debobigegoLabel,
+._debobigegoCaption {
font-size: 80%;
color: var(--fgTransparentWeak);
@@ -25,28 +25,28 @@
}
}
-._formLabel {
+._debobigegoLabel {
position: sticky;
top: var(--stickyTop, 0px);
z-index: 2;
- margin: -8px calc(var(--formXPadding) * -1) 0 calc(var(--formXPadding) * -1);
- padding: 8px calc(var(--formContentHMargin) + var(--formXPadding)) 8px calc(var(--formContentHMargin) + var(--formXPadding));
+ margin: -8px calc(var(--debobigegoXPadding) * -1) 0 calc(var(--debobigegoXPadding) * -1);
+ padding: 8px calc(var(--debobigegoContentHMargin) + var(--debobigegoXPadding)) 8px calc(var(--debobigegoContentHMargin) + var(--debobigegoXPadding));
background: var(--X17);
-webkit-backdrop-filter: var(--blur, blur(10px));
backdrop-filter: var(--blur, blur(10px));
}
-._themeChanging_ ._formLabel {
+._themeChanging_ ._debobigegoLabel {
transition: none !important;
background: transparent;
}
-._formCaption {
- padding: 8px var(--formContentHMargin) 0 var(--formContentHMargin);
+._debobigegoCaption {
+ padding: 8px var(--debobigegoContentHMargin) 0 var(--debobigegoContentHMargin);
}
-._formItem {
- & + ._formItem {
+._debobigegoItem {
+ & + ._debobigegoItem {
margin-top: 24px;
}
}
diff --git a/src/client/components/form/group.vue b/src/client/components/debobigego/group.vue
index 34ccaeff07..cba2c6ec94 100644
--- a/src/client/components/form/group.vue
+++ b/src/client/components/debobigego/group.vue
@@ -1,10 +1,10 @@
<template>
-<div class="vrtktovg _formItem _formNoConcat" v-size="{ max: [500] }" v-sticky-container>
- <div class="_formLabel"><slot name="label"></slot></div>
- <div class="main _form_group" ref="child">
+<div class="vrtktovg _debobigegoItem _debobigegoNoConcat" v-size="{ max: [500] }" v-sticky-container>
+ <div class="_debobigegoLabel"><slot name="label"></slot></div>
+ <div class="main _debobigego_group" ref="child">
<slot></slot>
</div>
- <div class="_formCaption"><slot name="caption"></slot></div>
+ <div class="_debobigegoCaption"><slot name="caption"></slot></div>
</div>
</template>
@@ -20,9 +20,9 @@ export default defineComponent({
const els = Array.from(child.value.children);
for (let i = 0; i < els.length; i++) {
const el = els[i];
- if (el.classList.contains('_formNoConcat')) {
- if (els[i - 1]) els[i - 1].classList.add('_formNoConcatPrev');
- if (els[i + 1]) els[i + 1].classList.add('_formNoConcatNext');
+ if (el.classList.contains('_debobigegoNoConcat')) {
+ if (els[i - 1]) els[i - 1].classList.add('_debobigegoNoConcatPrev');
+ if (els[i + 1]) els[i + 1].classList.add('_debobigegoNoConcatNext');
}
}
};
@@ -52,21 +52,21 @@ export default defineComponent({
<style lang="scss" scoped>
.vrtktovg {
> .main {
- > ::v-deep(*):not(._formNoConcat) {
- &:not(._formNoConcatNext) {
+ > ::v-deep(*):not(._debobigegoNoConcat) {
+ &:not(._debobigegoNoConcatNext) {
margin: 0;
}
- &:not(:last-child):not(._formNoConcatPrev) {
- &._formPanel, ._formPanel {
+ &:not(:last-child):not(._debobigegoNoConcatPrev) {
+ &._debobigegoPanel, ._debobigegoPanel {
border-bottom: solid 0.5px var(--divider);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
- &:not(:first-child):not(._formNoConcatNext) {
- &._formPanel, ._formPanel {
+ &:not(:first-child):not(._debobigegoNoConcatNext) {
+ &._debobigegoPanel, ._debobigegoPanel {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
diff --git a/src/client/components/form/info.vue b/src/client/components/debobigego/info.vue
index 9fdcbdca62..41afb03304 100644
--- a/src/client/components/form/info.vue
+++ b/src/client/components/debobigego/info.vue
@@ -1,6 +1,6 @@
<template>
-<div class="fzenkabp _formItem">
- <div class="_formPanel" :class="{ warn }">
+<div class="fzenkabp _debobigegoItem">
+ <div class="_debobigegoPanel" :class="{ warn }">
<i v-if="warn" class="fas fa-exclamation-triangle"></i>
<i v-else class="fas fa-info-circle"></i>
<slot></slot>
diff --git a/src/client/components/ui/input.vue b/src/client/components/debobigego/input.vue
index a916a0b035..d113f04d27 100644
--- a/src/client/components/ui/input.vue
+++ b/src/client/components/debobigego/input.vue
@@ -1,49 +1,53 @@
<template>
-<div class="matxzzsk">
- <div class="label" @click="focus"><slot name="label"></slot></div>
- <div class="input" :class="{ inline, disabled, focused }">
- <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
- <input ref="inputEl"
- :type="type"
- v-model="v"
- :disabled="disabled"
- :required="required"
- :readonly="readonly"
- :placeholder="placeholder"
- :pattern="pattern"
- :autocomplete="autocomplete"
- :spellcheck="spellcheck"
- :step="step"
- @focus="focused = true"
- @blur="focused = false"
- @keydown="onKeydown($event)"
- @input="onInput"
- :list="id"
- >
- <datalist :id="id" v-if="datalist">
- <option v-for="data in datalist" :value="data"/>
- </datalist>
- <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
+<FormGroup class="_debobigegoItem">
+ <template #label><slot></slot></template>
+ <div class="ztzhwixg _debobigegoItem" :class="{ inline, disabled }">
+ <div class="icon" ref="icon"><slot name="icon"></slot></div>
+ <div class="input _debobigegoPanel">
+ <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
+ <input ref="inputEl"
+ :type="type"
+ v-model="v"
+ :disabled="disabled"
+ :required="required"
+ :readonly="readonly"
+ :placeholder="placeholder"
+ :pattern="pattern"
+ :autocomplete="autocomplete"
+ :spellcheck="spellcheck"
+ :step="step"
+ @focus="focused = true"
+ @blur="focused = false"
+ @keydown="onKeydown($event)"
+ @input="onInput"
+ :list="id"
+ >
+ <datalist :id="id" v-if="datalist">
+ <option v-for="data in datalist" :value="data"/>
+ </datalist>
+ <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
+ </div>
</div>
- <div class="caption"><slot name="caption"></slot></div>
+ <template #caption><slot name="desc"></slot></template>
- <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
-</div>
+ <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+</FormGroup>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
-import MkButton from './button.vue';
-import { debounce } from 'throttle-debounce';
+import './debobigego.scss';
+import FormButton from './button.vue';
+import FormGroup from './group.vue';
export default defineComponent({
components: {
- MkButton,
+ FormGroup,
+ FormButton,
},
-
props: {
modelValue: {
- required: true
+ required: false
},
type: {
type: String,
@@ -92,20 +96,13 @@ export default defineComponent({
required: false,
default: false
},
- debounce: {
- type: Boolean,
- required: false,
- default: false
- },
manualSave: {
type: Boolean,
required: false,
default: false
},
},
-
emits: ['change', 'keydown', 'enter', 'update:modelValue'],
-
setup(props, context) {
const { modelValue, type, autofocus } = toRefs(props);
const v = ref(modelValue.value);
@@ -140,19 +137,13 @@ export default defineComponent({
}
};
- const debouncedUpdated = debounce(1000, updated);
-
- watch(modelValue, newValue => {
+ watch(modelValue.value, newValue => {
v.value = newValue;
});
watch(v, newValue => {
if (!props.manualSave) {
- if (props.debounce) {
- debouncedUpdated();
- } else {
- updated();
- }
+ updated();
}
invalid.value = inputEl.value.validity.badInput;
@@ -205,68 +196,59 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
-.matxzzsk {
- margin: 1.5em 0;
+.ztzhwixg {
+ position: relative;
- > .label {
- font-size: 0.85em;
- padding: 0 0 8px 12px;
- user-select: none;
+ > .icon {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 24px;
+ text-align: center;
+ line-height: 32px;
- &:empty {
- display: none;
- }
- }
-
- > .caption {
- font-size: 0.8em;
- padding: 8px 0 0 12px;
- color: var(--fgTransparentWeak);
-
- &:empty {
- display: none;
+ &:not(:empty) + .input {
+ margin-left: 28px;
}
}
> .input {
- $height: 42px;
+ $height: 48px;
position: relative;
> input {
- appearance: none;
- -webkit-appearance: none;
display: block;
height: $height;
width: 100%;
margin: 0;
- padding: 0 12px;
+ padding: 0 16px;
font: inherit;
font-weight: normal;
font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 0.5px var(--inputBorder);
- border-radius: 6px;
+ line-height: $height;
+ color: var(--inputText);
+ background: transparent;
+ border: none;
+ border-radius: 0;
outline: none;
box-shadow: none;
box-sizing: border-box;
- transition: border-color 0.1s ease-out;
- &:hover {
- border-color: var(--inputBorderHover);
+ &[type='file'] {
+ display: none;
}
}
> .prefix,
> .suffix {
- display: flex;
- align-items: center;
+ display: block;
position: absolute;
z-index: 1;
top: 0;
- padding: 0 12px;
+ padding: 0 16px;
font-size: 1em;
- height: $height;
+ line-height: $height;
+ color: var(--inputLabel);
pointer-events: none;
&:empty {
@@ -285,32 +267,25 @@ export default defineComponent({
> .prefix {
left: 0;
- padding-right: 6px;
+ padding-right: 8px;
}
> .suffix {
right: 0;
- padding-left: 6px;
- }
-
- &.inline {
- display: inline-block;
- margin: 0;
+ padding-left: 8px;
}
+ }
- &.focused {
- > input {
- border-color: var(--accent);
- //box-shadow: 0 0 0 4px var(--focus);
- }
- }
+ &.inline {
+ display: inline-block;
+ margin: 0;
+ }
- &.disabled {
- opacity: 0.7;
+ &.disabled {
+ opacity: 0.7;
- &, * {
- cursor: not-allowed !important;
- }
+ &, * {
+ cursor: not-allowed !important;
}
}
}
diff --git a/src/client/components/form/key-value-view.vue b/src/client/components/debobigego/key-value-view.vue
index ca4c09867f..0e034a2d54 100644
--- a/src/client/components/form/key-value-view.vue
+++ b/src/client/components/debobigego/key-value-view.vue
@@ -1,6 +1,6 @@
<template>
-<div class="_formItem">
- <div class="_formPanel anocepby">
+<div class="_debobigegoItem">
+ <div class="_debobigegoPanel anocepby">
<span class="key"><slot name="key"></slot></span>
<span class="value"><slot name="value"></slot></span>
</div>
@@ -9,7 +9,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import './form.scss';
+import './debobigego.scss';
export default defineComponent({
@@ -20,7 +20,7 @@ export default defineComponent({
.anocepby {
display: flex;
align-items: center;
- padding: 14px var(--formContentHMargin);
+ padding: 14px var(--debobigegoContentHMargin);
> .key {
margin-right: 12px;
diff --git a/src/client/components/form/link.vue b/src/client/components/debobigego/link.vue
index e1d13c6431..885579eadf 100644
--- a/src/client/components/form/link.vue
+++ b/src/client/components/debobigego/link.vue
@@ -1,6 +1,6 @@
<template>
-<div class="qmfkfnzi _formItem">
- <a class="main _button _formPanel _formClickable" :href="to" target="_blank" v-if="external">
+<div class="qmfkfnzi _debobigegoItem">
+ <a class="main _button _debobigegoPanel _debobigegoClickable" :href="to" target="_blank" v-if="external">
<span class="icon"><slot name="icon"></slot></span>
<span class="text"><slot></slot></span>
<span class="right">
@@ -8,7 +8,7 @@
<i class="fas fa-external-link-alt icon"></i>
</span>
</a>
- <MkA class="main _button _formPanel _formClickable" :class="{ active }" :to="to" :behavior="behavior" v-else>
+ <MkA class="main _button _debobigegoPanel _debobigegoClickable" :class="{ active }" :to="to" :behavior="behavior" v-else>
<span class="icon"><slot name="icon"></slot></span>
<span class="text"><slot></slot></span>
<span class="right">
@@ -21,7 +21,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import './form.scss';
+import './debobigego.scss';
export default defineComponent({
props: {
diff --git a/src/client/components/form/object-view.vue b/src/client/components/debobigego/object-view.vue
index 59fb62b5e6..ea79daa915 100644
--- a/src/client/components/form/object-view.vue
+++ b/src/client/components/debobigego/object-view.vue
@@ -1,8 +1,8 @@
<template>
-<FormGroup class="_formItem">
+<FormGroup class="_debobigegoItem">
<template #label><slot></slot></template>
- <div class="drooglns _formItem" :class="{ tall }">
- <div class="input _formPanel">
+ <div class="drooglns _debobigegoItem" :class="{ tall }">
+ <div class="input _debobigegoPanel">
<textarea class="_monospace"
v-model="v"
readonly
@@ -17,7 +17,7 @@
<script lang="ts">
import { defineComponent, ref, toRefs, watch } from 'vue';
import * as JSON5 from 'json5';
-import './form.scss';
+import './debobigego.scss';
import FormGroup from './group.vue';
export default defineComponent({
@@ -75,7 +75,7 @@ export default defineComponent({
max-width: 100%;
min-height: 130px;
margin: 0;
- padding: 16px var(--formContentHMargin);
+ padding: 16px var(--debobigegoContentHMargin);
box-sizing: border-box;
font: inherit;
font-weight: normal;
diff --git a/src/client/components/form/pagination.vue b/src/client/components/debobigego/pagination.vue
index 0a2f1ff0e1..2166f5065f 100644
--- a/src/client/components/form/pagination.vue
+++ b/src/client/components/debobigego/pagination.vue
@@ -1,5 +1,5 @@
<template>
-<FormGroup class="uljviswt _formItem">
+<FormGroup class="uljviswt _debobigegoItem">
<template #label><slot name="label"></slot></template>
<slot :items="items"></slot>
<div class="empty" v-if="empty" key="_empty_">
diff --git a/src/client/components/debobigego/radios.vue b/src/client/components/debobigego/radios.vue
new file mode 100644
index 0000000000..071c013afb
--- /dev/null
+++ b/src/client/components/debobigego/radios.vue
@@ -0,0 +1,112 @@
+<script lang="ts">
+import { defineComponent, h } from 'vue';
+import MkRadio from '@client/components/form/radio.vue';
+import './debobigego.scss';
+
+export default defineComponent({
+ components: {
+ MkRadio
+ },
+ props: {
+ modelValue: {
+ required: false
+ },
+ },
+ data() {
+ return {
+ value: this.modelValue,
+ }
+ },
+ watch: {
+ modelValue() {
+ this.value = this.modelValue;
+ },
+ value() {
+ this.$emit('update:modelValue', this.value);
+ }
+ },
+ render() {
+ const label = this.$slots.desc();
+ let options = this.$slots.default();
+
+ // なぜかFragmentになることがあるため
+ if (options.length === 1 && options[0].props == null) options = options[0].children;
+
+ return h('div', {
+ class: 'cnklmpwm _debobigegoItem'
+ }, [
+ h('div', {
+ class: '_debobigegoLabel',
+ }, label),
+ ...options.map(option => h('button', {
+ class: '_button _debobigegoPanel _debobigegoClickable',
+ key: option.key,
+ onClick: () => this.value = option.props.value,
+ }, [h('span', {
+ class: ['check', { checked: this.value === option.props.value }],
+ }), option.children]))
+ ]);
+ }
+});
+</script>
+
+<style lang="scss">
+.cnklmpwm {
+ > button {
+ display: block;
+ width: 100%;
+ box-sizing: border-box;
+ padding: 14px 18px;
+ text-align: left;
+
+ &:not(:first-of-type) {
+ border-top: none !important;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+
+ &:not(:last-of-type) {
+ border-bottom: solid 0.5px var(--divider);
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ > .check {
+ display: inline-block;
+ vertical-align: bottom;
+ position: relative;
+ width: 16px;
+ height: 16px;
+ margin-right: 8px;
+ background: none;
+ border: 2px solid var(--inputBorder);
+ border-radius: 100%;
+ transition: inherit;
+
+ &:after {
+ content: "";
+ display: block;
+ position: absolute;
+ top: 3px;
+ right: 3px;
+ bottom: 3px;
+ left: 3px;
+ border-radius: 100%;
+ opacity: 0;
+ transform: scale(0);
+ transition: .4s cubic-bezier(.25,.8,.25,1);
+ }
+
+ &.checked {
+ border-color: var(--accent);
+
+ &:after {
+ background-color: var(--accent);
+ transform: scale(1);
+ opacity: 1;
+ }
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/debobigego/range.vue b/src/client/components/debobigego/range.vue
new file mode 100644
index 0000000000..26fb0f37c6
--- /dev/null
+++ b/src/client/components/debobigego/range.vue
@@ -0,0 +1,122 @@
+<template>
+<div class="ifitouly _debobigegoItem" :class="{ focused, disabled }">
+ <div class="_debobigegoLabel"><slot name="label"></slot></div>
+ <div class="_debobigegoPanel main">
+ <input
+ type="range"
+ ref="input"
+ v-model="v"
+ :disabled="disabled"
+ :min="min"
+ :max="max"
+ :step="step"
+ @focus="focused = true"
+ @blur="focused = false"
+ @input="$emit('update:value', $event.target.value)"
+ />
+ </div>
+ <div class="_debobigegoCaption"><slot name="caption"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+ props: {
+ value: {
+ type: Number,
+ required: false,
+ default: 0
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ min: {
+ type: Number,
+ required: false,
+ default: 0
+ },
+ max: {
+ type: Number,
+ required: false,
+ default: 100
+ },
+ step: {
+ type: Number,
+ required: false,
+ default: 1
+ },
+ },
+ data() {
+ return {
+ v: this.value,
+ focused: false
+ };
+ },
+ watch: {
+ value(v) {
+ this.v = parseFloat(v);
+ }
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.ifitouly {
+ position: relative;
+
+ > .main {
+ padding: 22px 16px;
+
+ > input {
+ display: block;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background: var(--X10);
+ height: 4px;
+ width: 100%;
+ box-sizing: border-box;
+ margin: 0;
+ outline: 0;
+ border: 0;
+ border-radius: 7px;
+
+ &.disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+
+ &::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ display: block;
+ border-radius: 50%;
+ border: none;
+ background: var(--accent);
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
+ box-sizing: content-box;
+ }
+
+ &::-moz-range-thumb {
+ -moz-appearance: none;
+ appearance: none;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ display: block;
+ border-radius: 50%;
+ border: none;
+ background: var(--accent);
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/debobigego/select.vue b/src/client/components/debobigego/select.vue
new file mode 100644
index 0000000000..7a31371afc
--- /dev/null
+++ b/src/client/components/debobigego/select.vue
@@ -0,0 +1,145 @@
+<template>
+<div class="yrtfrpux _debobigegoItem" :class="{ disabled, inline }">
+ <div class="_debobigegoLabel"><slot name="label"></slot></div>
+ <div class="icon" ref="icon"><slot name="icon"></slot></div>
+ <div class="input _debobigegoPanel _debobigegoClickable" @click="focus">
+ <div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
+ <select ref="input"
+ v-model="v"
+ :required="required"
+ :disabled="disabled"
+ @focus="focused = true"
+ @blur="focused = false"
+ >
+ <slot></slot>
+ </select>
+ <div class="suffix">
+ <i class="fas fa-chevron-down"></i>
+ </div>
+ </div>
+ <div class="_debobigegoCaption"><slot name="caption"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import './debobigego.scss';
+
+export default defineComponent({
+ props: {
+ modelValue: {
+ required: false
+ },
+ required: {
+ type: Boolean,
+ required: false
+ },
+ disabled: {
+ type: Boolean,
+ required: false
+ },
+ inline: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ },
+ data() {
+ return {
+ };
+ },
+ computed: {
+ v: {
+ get() {
+ return this.modelValue;
+ },
+ set(v) {
+ this.$emit('update:modelValue', v);
+ }
+ },
+ },
+ methods: {
+ focus() {
+ this.$refs.input.focus();
+ }
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.yrtfrpux {
+ position: relative;
+
+ > .icon {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 24px;
+ text-align: center;
+ line-height: 32px;
+
+ &:not(:empty) + .input {
+ margin-left: 28px;
+ }
+ }
+
+ > .input {
+ display: flex;
+ position: relative;
+
+ > select {
+ display: block;
+ flex: 1;
+ width: 100%;
+ padding: 0 16px;
+ font: inherit;
+ font-weight: normal;
+ font-size: 1em;
+ height: 48px;
+ background: none;
+ border: none;
+ border-radius: 0;
+ outline: none;
+ box-shadow: none;
+ appearance: none;
+ -webkit-appearance: none;
+ color: var(--fg);
+
+ option,
+ optgroup {
+ color: var(--fg);
+ background: var(--bg);
+ }
+ }
+
+ > .prefix,
+ > .suffix {
+ display: block;
+ align-self: center;
+ justify-self: center;
+ font-size: 1em;
+ line-height: 32px;
+ color: var(--inputLabel);
+ pointer-events: none;
+
+ &:empty {
+ display: none;
+ }
+
+ > * {
+ display: block;
+ min-width: 16px;
+ }
+ }
+
+ > .prefix {
+ padding-right: 4px;
+ }
+
+ > .suffix {
+ padding: 0 16px 0 0;
+ opacity: 0.7;
+ }
+ }
+}
+</style>
diff --git a/src/client/components/form/suspense.vue b/src/client/components/debobigego/suspense.vue
index d04dc07624..e59e0ba12d 100644
--- a/src/client/components/form/suspense.vue
+++ b/src/client/components/debobigego/suspense.vue
@@ -1,15 +1,15 @@
<template>
<transition name="fade" mode="out-in">
- <div class="_formItem" v-if="pending">
- <div class="_formPanel">
+ <div class="_debobigegoItem" v-if="pending">
+ <div class="_debobigegoPanel">
<MkLoading/>
</div>
</div>
- <div v-else-if="resolved" class="_formItem">
+ <div v-else-if="resolved" class="_debobigegoItem">
<slot :result="result"></slot>
</div>
- <div class="_formItem" v-else>
- <div class="_formPanel eiurkvay">
+ <div class="_debobigegoItem" v-else>
+ <div class="_debobigegoPanel eiurkvay">
<div><i class="fas fa-exclamation-triangle"></i> {{ $ts.somethingHappened }}</div>
<MkButton inline @click="retry" class="retry"><i class="fas fa-redo-alt"></i> {{ $ts.retry }}</MkButton>
</div>
@@ -19,7 +19,7 @@
<script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue';
-import './form.scss';
+import './debobigego.scss';
import MkButton from '@client/components/ui/button.vue';
export default defineComponent({
diff --git a/src/client/components/debobigego/switch.vue b/src/client/components/debobigego/switch.vue
new file mode 100644
index 0000000000..9a69e18302
--- /dev/null
+++ b/src/client/components/debobigego/switch.vue
@@ -0,0 +1,132 @@
+<template>
+<div class="ijnpvmgr _debobigegoItem">
+ <div class="main _debobigegoPanel _debobigegoClickable"
+ :class="{ disabled, checked }"
+ :aria-checked="checked"
+ :aria-disabled="disabled"
+ @click.prevent="toggle"
+ >
+ <input
+ type="checkbox"
+ ref="input"
+ :disabled="disabled"
+ @keydown.enter="toggle"
+ >
+ <span class="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff">
+ <span class="handle"></span>
+ </span>
+ <span class="label">
+ <span><slot></slot></span>
+ </span>
+ </div>
+ <div class="_debobigegoCaption"><slot name="desc"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import './debobigego.scss';
+
+export default defineComponent({
+ props: {
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ disabled: {
+ type: Boolean,
+ default: false
+ }
+ },
+ computed: {
+ checked(): boolean {
+ return this.modelValue;
+ }
+ },
+ methods: {
+ toggle() {
+ if (this.disabled) return;
+ this.$emit('update:modelValue', !this.checked);
+ }
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.ijnpvmgr {
+ > .main {
+ position: relative;
+ display: flex;
+ padding: 14px 16px;
+ cursor: pointer;
+
+ > * {
+ user-select: none;
+ }
+
+ > input {
+ position: absolute;
+ width: 0;
+ height: 0;
+ opacity: 0;
+ margin: 0;
+ }
+
+ > .button {
+ position: relative;
+ display: inline-block;
+ flex-shrink: 0;
+ margin: 0;
+ width: 34px;
+ height: 22px;
+ background: var(--switchBg);
+ outline: none;
+ border-radius: 999px;
+ transition: all 0.3s;
+ cursor: pointer;
+
+ > .handle {
+ position: absolute;
+ top: 0;
+ left: 3px;
+ bottom: 0;
+ margin: auto 0;
+ border-radius: 100%;
+ transition: background-color 0.3s, transform 0.3s;
+ width: 16px;
+ height: 16px;
+ background-color: #fff;
+ pointer-events: none;
+ }
+ }
+
+ > .label {
+ margin-left: 12px;
+ display: block;
+ transition: inherit;
+ color: var(--fg);
+
+ > span {
+ display: block;
+ line-height: 20px;
+ transition: inherit;
+ }
+ }
+
+ &.disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+
+ &.checked {
+ > .button {
+ background-color: var(--accent);
+
+ > .handle {
+ transform: translateX(12px);
+ }
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/debobigego/textarea.vue b/src/client/components/debobigego/textarea.vue
new file mode 100644
index 0000000000..64e8d47126
--- /dev/null
+++ b/src/client/components/debobigego/textarea.vue
@@ -0,0 +1,161 @@
+<template>
+<FormGroup class="_debobigegoItem">
+ <template #label><slot></slot></template>
+ <div class="rivhosbp _debobigegoItem" :class="{ tall, pre }">
+ <div class="input _debobigegoPanel">
+ <textarea ref="input" :class="{ code, _monospace: code }"
+ v-model="v"
+ :required="required"
+ :readonly="readonly"
+ :pattern="pattern"
+ :autocomplete="autocomplete"
+ :spellcheck="!code"
+ @input="onInput"
+ @focus="focused = true"
+ @blur="focused = false"
+ ></textarea>
+ </div>
+ </div>
+ <template #caption><slot name="desc"></slot></template>
+
+ <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+</FormGroup>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, toRefs, watch } from 'vue';
+import './debobigego.scss';
+import FormButton from './button.vue';
+import FormGroup from './group.vue';
+
+export default defineComponent({
+ components: {
+ FormGroup,
+ FormButton,
+ },
+ props: {
+ modelValue: {
+ required: false
+ },
+ required: {
+ type: Boolean,
+ required: false
+ },
+ readonly: {
+ type: Boolean,
+ required: false
+ },
+ pattern: {
+ type: String,
+ required: false
+ },
+ autocomplete: {
+ type: String,
+ required: false
+ },
+ code: {
+ type: Boolean,
+ required: false
+ },
+ tall: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ pre: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ manualSave: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ },
+ setup(props, context) {
+ const { modelValue } = toRefs(props);
+ const v = ref(modelValue.value);
+ const changed = ref(false);
+ const inputEl = ref(null);
+ const focus = () => inputEl.value.focus();
+ const onInput = (ev) => {
+ changed.value = true;
+ context.emit('change', ev);
+ };
+
+ const updated = () => {
+ changed.value = false;
+ context.emit('update:modelValue', v.value);
+ };
+
+ watch(modelValue.value, newValue => {
+ v.value = newValue;
+ });
+
+ watch(v, newValue => {
+ if (!props.manualSave) {
+ updated();
+ }
+ });
+
+ return {
+ v,
+ updated,
+ changed,
+ focus,
+ onInput,
+ };
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.rivhosbp {
+ position: relative;
+
+ > .input {
+ position: relative;
+
+ > textarea {
+ display: block;
+ width: 100%;
+ min-width: 100%;
+ max-width: 100%;
+ min-height: 130px;
+ margin: 0;
+ padding: 16px;
+ box-sizing: border-box;
+ font: inherit;
+ font-weight: normal;
+ font-size: 1em;
+ background: transparent;
+ border: none;
+ border-radius: 0;
+ outline: none;
+ box-shadow: none;
+ color: var(--fg);
+
+ &.code {
+ tab-size: 2;
+ }
+ }
+ }
+
+ &.tall {
+ > .input {
+ > textarea {
+ min-height: 200px;
+ }
+ }
+ }
+
+ &.pre {
+ > .input {
+ > textarea {
+ white-space: pre;
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/form/tuple.vue b/src/client/components/debobigego/tuple.vue
index 6c8a22d189..8a4599fd64 100644
--- a/src/client/components/form/tuple.vue
+++ b/src/client/components/debobigego/tuple.vue
@@ -1,5 +1,5 @@
<template>
-<div class="wthhikgt _formItem" v-size="{ max: [500] }">
+<div class="wthhikgt _debobigegoItem" v-size="{ max: [500] }">
<slot></slot>
</div>
</template>
diff --git a/src/client/components/dialog.vue b/src/client/components/dialog.vue
index f3611f050e..dd4932f61f 100644
--- a/src/client/components/dialog.vue
+++ b/src/client/components/dialog.vue
@@ -40,8 +40,8 @@
import { defineComponent } from 'vue';
import MkModal from '@client/components/ui/modal.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 MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
export default defineComponent({
components: {
diff --git a/src/client/components/emoji-picker-window.vue b/src/client/components/emoji-picker-window.vue
index 53b6ae6b32..b7b884565b 100644
--- a/src/client/components/emoji-picker-window.vue
+++ b/src/client/components/emoji-picker-window.vue
@@ -153,7 +153,7 @@ export default defineComponent({
height: var(--eachSize);
border-radius: 4px;
- &:focus {
+ &:focus-visible {
outline: solid 2px var(--focus);
z-index: 1;
}
diff --git a/src/client/components/emoji-picker.vue b/src/client/components/emoji-picker.vue
index d8703202c7..85a12a08e6 100644
--- a/src/client/components/emoji-picker.vue
+++ b/src/client/components/emoji-picker.vue
@@ -465,7 +465,7 @@ export default defineComponent({
height: var(--eachSize);
border-radius: 4px;
- &:focus {
+ &:focus-visible {
outline: solid 2px var(--focus);
z-index: 1;
}
diff --git a/src/client/components/follow-button.vue b/src/client/components/follow-button.vue
index 5685b86a51..5eba9b1f6b 100644
--- a/src/client/components/follow-button.vue
+++ b/src/client/components/follow-button.vue
@@ -161,7 +161,7 @@ export default defineComponent({
width: 31px;
}
- &:focus {
+ &:focus-visible {
&:after {
content: "";
pointer-events: none;
diff --git a/src/client/components/forgot-password.vue b/src/client/components/forgot-password.vue
index 3b5ad6d6ba..cb2380f483 100644
--- a/src/client/components/forgot-password.vue
+++ b/src/client/components/forgot-password.vue
@@ -35,7 +35,7 @@
import { defineComponent } from 'vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/components/form-dialog.vue b/src/client/components/form-dialog.vue
index e13592b488..6353b7287e 100644
--- a/src/client/components/form-dialog.vue
+++ b/src/client/components/form-dialog.vue
@@ -14,23 +14,23 @@
</template>
<FormBase class="xkpnjxcv">
<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
- <FormInput v-if="form[item].type === 'number'" v-model:value="values[item]" type="number" :step="form[item].step || 1">
+ <FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1">
<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span>
<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
</FormInput>
- <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model:value="values[item]" type="text">
+ <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text">
<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span>
<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
</FormInput>
- <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model:value="values[item]">
+ <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]">
<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span>
<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
</FormTextarea>
- <FormSwitch v-else-if="form[item].type === 'boolean'" v-model:value="values[item]">
+ <FormSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]">
<span v-text="form[item].label || item"></span>
<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
</FormSwitch>
- <FormSelect v-else-if="form[item].type === 'enum'" v-model:value="values[item]">
+ <FormSelect v-else-if="form[item].type === 'enum'" v-model="values[item]">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<option v-for="item in form[item].enum" :value="item.value" :key="item.value">{{ item.label }}</option>
</FormSelect>
@@ -38,7 +38,7 @@
<template #desc><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<option v-for="item in form[item].options" :value="item.value" :key="item.value">{{ item.label }}</option>
</FormRadios>
- <FormRange v-else-if="form[item].type === 'range'" v-model:value="values[item]" :min="form[item].mim" :max="form[item].max" :step="form[item].step">
+ <FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].mim" :max="form[item].max" :step="form[item].step">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
</FormRange>
@@ -53,14 +53,14 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
-import FormBase from './form/base.vue';
-import FormInput from './form/input.vue';
-import FormTextarea from './form/textarea.vue';
-import FormSwitch from './form/switch.vue';
-import FormSelect from './form/select.vue';
-import FormRange from './form/range.vue';
-import FormButton from './form/button.vue';
-import FormRadios from './form/radios.vue';
+import FormBase from './debobigego/base.vue';
+import FormInput from './debobigego/input.vue';
+import FormTextarea from './debobigego/textarea.vue';
+import FormSwitch from './debobigego/switch.vue';
+import FormSelect from './debobigego/select.vue';
+import FormRange from './debobigego/range.vue';
+import FormButton from './debobigego/button.vue';
+import FormRadios from './debobigego/radios.vue';
export default defineComponent({
components: {
diff --git a/src/client/components/form/input.vue b/src/client/components/form/input.vue
index 942ac4dfd2..d7b6f77519 100644
--- a/src/client/components/form/input.vue
+++ b/src/client/components/form/input.vue
@@ -1,53 +1,49 @@
<template>
-<FormGroup class="_formItem">
- <template #label><slot></slot></template>
- <div class="ztzhwixg _formItem" :class="{ inline, disabled }">
- <div class="icon" ref="icon"><slot name="icon"></slot></div>
- <div class="input _formPanel">
- <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
- <input ref="inputEl"
- :type="type"
- v-model="v"
- :disabled="disabled"
- :required="required"
- :readonly="readonly"
- :placeholder="placeholder"
- :pattern="pattern"
- :autocomplete="autocomplete"
- :spellcheck="spellcheck"
- :step="step"
- @focus="focused = true"
- @blur="focused = false"
- @keydown="onKeydown($event)"
- @input="onInput"
- :list="id"
- >
- <datalist :id="id" v-if="datalist">
- <option v-for="data in datalist" :value="data"/>
- </datalist>
- <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
- </div>
+<div class="matxzzsk">
+ <div class="label" @click="focus"><slot name="label"></slot></div>
+ <div class="input" :class="{ inline, disabled, focused }">
+ <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
+ <input ref="inputEl"
+ :type="type"
+ v-model="v"
+ :disabled="disabled"
+ :required="required"
+ :readonly="readonly"
+ :placeholder="placeholder"
+ :pattern="pattern"
+ :autocomplete="autocomplete"
+ :spellcheck="spellcheck"
+ :step="step"
+ @focus="focused = true"
+ @blur="focused = false"
+ @keydown="onKeydown($event)"
+ @input="onInput"
+ :list="id"
+ >
+ <datalist :id="id" v-if="datalist">
+ <option v-for="data in datalist" :value="data"/>
+ </datalist>
+ <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
</div>
- <template #caption><slot name="desc"></slot></template>
+ <div class="caption"><slot name="caption"></slot></div>
- <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
-</FormGroup>
+ <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
-import './form.scss';
-import FormButton from './button.vue';
-import FormGroup from './group.vue';
+import MkButton from '../ui/button.vue';
+import { debounce } from 'throttle-debounce';
export default defineComponent({
components: {
- FormGroup,
- FormButton,
+ MkButton,
},
+
props: {
- value: {
- required: false
+ modelValue: {
+ required: true
},
type: {
type: String,
@@ -96,16 +92,23 @@ export default defineComponent({
required: false,
default: false
},
+ debounce: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
manualSave: {
type: Boolean,
required: false,
default: false
},
},
- emits: ['change', 'keydown', 'enter'],
+
+ emits: ['change', 'keydown', 'enter', 'update:modelValue'],
+
setup(props, context) {
- const { value, type, autofocus } = toRefs(props);
- const v = ref(value.value);
+ const { modelValue, type, autofocus } = toRefs(props);
+ const v = ref(modelValue.value);
const id = Math.random().toString(); // TODO: uuid?
const focused = ref(false);
const changed = ref(false);
@@ -131,19 +134,25 @@ export default defineComponent({
const updated = () => {
changed.value = false;
if (type?.value === 'number') {
- context.emit('update:value', parseFloat(v.value));
+ context.emit('update:modelValue', parseFloat(v.value));
} else {
- context.emit('update:value', v.value);
+ context.emit('update:modelValue', v.value);
}
};
- watch(value, newValue => {
+ const debouncedUpdated = debounce(1000, updated);
+
+ watch(modelValue, newValue => {
v.value = newValue;
});
watch(v, newValue => {
if (!props.manualSave) {
- updated();
+ if (props.debounce) {
+ debouncedUpdated();
+ } else {
+ updated();
+ }
}
invalid.value = inputEl.value.validity.badInput;
@@ -196,59 +205,66 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
-.ztzhwixg {
- position: relative;
+.matxzzsk {
+ > .label {
+ font-size: 0.85em;
+ padding: 0 0 8px 12px;
+ user-select: none;
+
+ &:empty {
+ display: none;
+ }
+ }
- > .icon {
- position: absolute;
- top: 0;
- left: 0;
- width: 24px;
- text-align: center;
- line-height: 32px;
+ > .caption {
+ font-size: 0.8em;
+ padding: 8px 0 0 12px;
+ color: var(--fgTransparentWeak);
- &:not(:empty) + .input {
- margin-left: 28px;
+ &:empty {
+ display: none;
}
}
> .input {
- $height: 48px;
+ $height: 42px;
position: relative;
> input {
+ appearance: none;
+ -webkit-appearance: none;
display: block;
height: $height;
width: 100%;
margin: 0;
- padding: 0 16px;
+ padding: 0 12px;
font: inherit;
font-weight: normal;
font-size: 1em;
- line-height: $height;
- color: var(--inputText);
- background: transparent;
- border: none;
- border-radius: 0;
+ color: var(--fg);
+ background: var(--panel);
+ border: solid 0.5px var(--inputBorder);
+ border-radius: 6px;
outline: none;
box-shadow: none;
box-sizing: border-box;
+ transition: border-color 0.1s ease-out;
- &[type='file'] {
- display: none;
+ &:hover {
+ border-color: var(--inputBorderHover);
}
}
> .prefix,
> .suffix {
- display: block;
+ display: flex;
+ align-items: center;
position: absolute;
z-index: 1;
top: 0;
- padding: 0 16px;
+ padding: 0 12px;
font-size: 1em;
- line-height: $height;
- color: var(--inputLabel);
+ height: $height;
pointer-events: none;
&:empty {
@@ -267,25 +283,32 @@ export default defineComponent({
> .prefix {
left: 0;
- padding-right: 8px;
+ padding-right: 6px;
}
> .suffix {
right: 0;
- padding-left: 8px;
+ padding-left: 6px;
}
- }
- &.inline {
- display: inline-block;
- margin: 0;
- }
+ &.inline {
+ display: inline-block;
+ margin: 0;
+ }
+
+ &.focused {
+ > input {
+ border-color: var(--accent);
+ //box-shadow: 0 0 0 4px var(--focus);
+ }
+ }
- &.disabled {
- opacity: 0.7;
+ &.disabled {
+ opacity: 0.7;
- &, * {
- cursor: not-allowed !important;
+ &, * {
+ cursor: not-allowed !important;
+ }
}
}
}
diff --git a/src/client/components/ui/radio.vue b/src/client/components/form/radio.vue
index 0f31d8fa0a..0f31d8fa0a 100644
--- a/src/client/components/ui/radio.vue
+++ b/src/client/components/form/radio.vue
diff --git a/src/client/components/form/radios.vue b/src/client/components/form/radios.vue
index b660c37ace..1d3d80172a 100644
--- a/src/client/components/form/radios.vue
+++ b/src/client/components/form/radios.vue
@@ -1,7 +1,6 @@
<script lang="ts">
import { defineComponent, h } from 'vue';
-import MkRadio from '@client/components/ui/radio.vue';
-import './form.scss';
+import MkRadio from './radio.vue';
export default defineComponent({
components: {
@@ -18,9 +17,6 @@ export default defineComponent({
}
},
watch: {
- modelValue() {
- this.value = this.modelValue;
- },
value() {
this.$emit('update:modelValue', this.value);
}
@@ -33,80 +29,38 @@ export default defineComponent({
if (options.length === 1 && options[0].props == null) options = options[0].children;
return h('div', {
- class: 'cnklmpwm _formItem'
+ class: 'novjtcto'
}, [
- h('div', {
- class: '_formLabel',
- }, label),
- ...options.map(option => h('button', {
- class: '_button _formPanel _formClickable',
+ h('div', { class: 'label' }, label),
+ ...options.map(option => h(MkRadio, {
key: option.key,
- onClick: () => this.value = option.props.value,
- }, [h('span', {
- class: ['check', { checked: this.value === option.props.value }],
- }), option.children]))
+ value: option.props.value,
+ modelValue: this.value,
+ 'onUpdate:modelValue': value => this.value = value,
+ }, option.children))
]);
}
});
</script>
<style lang="scss">
-.cnklmpwm {
- > button {
- display: block;
- width: 100%;
- box-sizing: border-box;
- padding: 14px 18px;
- text-align: left;
-
- &:not(:first-of-type) {
- border-top: none !important;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- }
+.novjtcto {
+ > .label {
+ font-size: 0.85em;
+ padding: 0 0 8px 12px;
+ user-select: none;
- &:not(:last-of-type) {
- border-bottom: solid 0.5px var(--divider);
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
+ &:empty {
+ display: none;
}
+ }
- > .check {
- display: inline-block;
- vertical-align: bottom;
- position: relative;
- width: 16px;
- height: 16px;
- margin-right: 8px;
- background: none;
- border: 2px solid var(--inputBorder);
- border-radius: 100%;
- transition: inherit;
-
- &:after {
- content: "";
- display: block;
- position: absolute;
- top: 3px;
- right: 3px;
- bottom: 3px;
- left: 3px;
- border-radius: 100%;
- opacity: 0;
- transform: scale(0);
- transition: .4s cubic-bezier(.25,.8,.25,1);
- }
-
- &.checked {
- border-color: var(--accent);
+ &:first-child {
+ margin-top: 0;
+ }
- &:after {
- background-color: var(--accent);
- transform: scale(1);
- opacity: 1;
- }
- }
- }
+ &:last-child {
+ margin-bottom: 0;
}
}
</style>
diff --git a/src/client/components/form/range.vue b/src/client/components/form/range.vue
index 65d665c70a..4cfe66a8fc 100644
--- a/src/client/components/form/range.vue
+++ b/src/client/components/form/range.vue
@@ -1,21 +1,20 @@
<template>
-<div class="ifitouly _formItem" :class="{ focused, disabled }">
- <div class="_formLabel"><slot name="label"></slot></div>
- <div class="_formPanel main">
- <input
- type="range"
- ref="input"
- v-model="v"
- :disabled="disabled"
- :min="min"
- :max="max"
- :step="step"
- @focus="focused = true"
- @blur="focused = false"
- @input="$emit('update:value', $event.target.value)"
- />
- </div>
- <div class="_formCaption"><slot name="caption"></slot></div>
+<div class="timctyfi" :class="{ focused, disabled }">
+ <div class="icon"><slot name="icon"></slot></div>
+ <span class="label"><slot name="label"></slot></span>
+ <input
+ type="range"
+ ref="input"
+ v-model="v"
+ :disabled="disabled"
+ :min="min"
+ :max="max"
+ :step="step"
+ :autofocus="autofocus"
+ @focus="focused = true"
+ @blur="focused = false"
+ @input="$emit('update:value', $event.target.value)"
+ />
</div>
</template>
@@ -49,6 +48,10 @@ export default defineComponent({
required: false,
default: 1
},
+ autofocus: {
+ type: Boolean,
+ required: false
+ }
},
data() {
return {
@@ -61,61 +64,75 @@ export default defineComponent({
this.v = parseFloat(v);
}
},
+ mounted() {
+ if (this.autofocus) {
+ this.$nextTick(() => {
+ this.$refs.input.focus();
+ });
+ }
+ }
});
</script>
<style lang="scss" scoped>
-.ifitouly {
+.timctyfi {
position: relative;
+ margin: 8px;
- > .main {
- padding: 22px 16px;
+ > .icon {
+ display: inline-block;
+ width: 24px;
+ text-align: center;
+ }
- > input {
- display: block;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background: var(--X10);
- height: 4px;
- width: 100%;
- box-sizing: border-box;
- margin: 0;
- outline: 0;
- border: 0;
- border-radius: 7px;
+ > .title {
+ pointer-events: none;
+ font-size: 16px;
+ color: var(--inputLabel);
+ overflow: hidden;
+ }
- &.disabled {
- opacity: 0.6;
- cursor: not-allowed;
- }
+ > input {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background: var(--X10);
+ height: 7px;
+ margin: 0 8px;
+ outline: 0;
+ border: 0;
+ border-radius: 7px;
- &::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- cursor: pointer;
- width: 20px;
- height: 20px;
- display: block;
- border-radius: 50%;
- border: none;
- background: var(--accent);
- box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
- box-sizing: content-box;
- }
+ &.disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
- &::-moz-range-thumb {
- -moz-appearance: none;
- appearance: none;
- cursor: pointer;
- width: 20px;
- height: 20px;
- display: block;
- border-radius: 50%;
- border: none;
- background: var(--accent);
- box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
- }
+ &::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ display: block;
+ border-radius: 50%;
+ border: none;
+ background: var(--accent);
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
+ box-sizing: content-box;
+ }
+
+ &::-moz-range-thumb {
+ -moz-appearance: none;
+ appearance: none;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ display: block;
+ border-radius: 50%;
+ border: none;
+ background: var(--accent);
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
}
}
}
diff --git a/src/client/components/form/section.vue b/src/client/components/form/section.vue
new file mode 100644
index 0000000000..8eac40a0db
--- /dev/null
+++ b/src/client/components/form/section.vue
@@ -0,0 +1,31 @@
+<template>
+<div class="vrtktovh" v-size="{ max: [500] }" v-sticky-container>
+ <div class="label"><slot name="label"></slot></div>
+ <div class="main">
+ <slot></slot>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+
+});
+</script>
+
+<style lang="scss" scoped>
+.vrtktovh {
+ border-top: solid 0.5px var(--divider);
+
+ > .label {
+ font-weight: bold;
+ padding: 24px 0 16px 0;
+ }
+
+ > .main {
+ margin-bottom: 32px;
+ }
+}
+</style>
diff --git a/src/client/components/form/select.vue b/src/client/components/form/select.vue
index 1c5a473451..257e2cc990 100644
--- a/src/client/components/form/select.vue
+++ b/src/client/components/form/select.vue
@@ -1,125 +1,216 @@
<template>
-<div class="yrtfrpux _formItem" :class="{ disabled, inline }">
- <div class="_formLabel"><slot name="label"></slot></div>
- <div class="icon" ref="icon"><slot name="icon"></slot></div>
- <div class="input _formPanel _formClickable" @click="focus">
- <div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
- <select ref="input"
+<div class="vblkjoeq">
+ <div class="label" @click="focus"><slot name="label"></slot></div>
+ <div class="input" :class="{ inline, disabled, focused }">
+ <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
+ <select ref="inputEl"
v-model="v"
- :required="required"
:disabled="disabled"
+ :required="required"
+ :readonly="readonly"
+ :placeholder="placeholder"
@focus="focused = true"
@blur="focused = false"
+ @input="onInput"
>
<slot></slot>
</select>
- <div class="suffix">
- <i class="fas fa-chevron-down"></i>
- </div>
+ <div class="suffix" ref="suffixEl"><i class="fas fa-chevron-down"></i></div>
</div>
- <div class="_formCaption"><slot name="caption"></slot></div>
+ <div class="caption"><slot name="caption"></slot></div>
+
+ <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
</div>
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
-import './form.scss';
+import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
+import MkButton from '../ui/button.vue';
export default defineComponent({
+ components: {
+ MkButton,
+ },
+
props: {
- value: {
- required: false
+ modelValue: {
+ required: true
},
required: {
type: Boolean,
required: false
},
+ readonly: {
+ type: Boolean,
+ required: false
+ },
disabled: {
type: Boolean,
required: false
},
+ placeholder: {
+ type: String,
+ required: false
+ },
+ autofocus: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
inline: {
type: Boolean,
required: false,
default: false
},
+ manualSave: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
},
- data() {
- return {
+
+ emits: ['change', 'update:modelValue'],
+
+ setup(props, context) {
+ const { modelValue, autofocus } = toRefs(props);
+ const v = ref(modelValue.value);
+ const focused = ref(false);
+ const changed = ref(false);
+ const invalid = ref(false);
+ const filled = computed(() => v.value !== '' && v.value != null);
+ const inputEl = ref(null);
+ const prefixEl = ref(null);
+ const suffixEl = ref(null);
+
+ const focus = () => inputEl.value.focus();
+ const onInput = (ev) => {
+ changed.value = true;
+ context.emit('change', ev);
};
- },
- computed: {
- v: {
- get() {
- return this.value;
- },
- set(v) {
- this.$emit('update:value', v);
+
+ const updated = () => {
+ changed.value = false;
+ context.emit('update:modelValue', v.value);
+ };
+
+ watch(modelValue, newValue => {
+ v.value = newValue;
+ });
+
+ watch(v, newValue => {
+ if (!props.manualSave) {
+ updated();
}
- },
+
+ invalid.value = inputEl.value.validity.badInput;
+ });
+
+ onMounted(() => {
+ nextTick(() => {
+ if (autofocus.value) {
+ focus();
+ }
+
+ // このコンポーネントが作成された時、非表示状態である場合がある
+ // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
+ const clock = setInterval(() => {
+ if (prefixEl.value) {
+ if (prefixEl.value.offsetWidth) {
+ inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
+ }
+ }
+ if (suffixEl.value) {
+ if (suffixEl.value.offsetWidth) {
+ inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
+ }
+ }
+ }, 100);
+
+ onUnmounted(() => {
+ clearInterval(clock);
+ });
+ });
+ });
+
+ return {
+ v,
+ focused,
+ invalid,
+ changed,
+ filled,
+ inputEl,
+ prefixEl,
+ suffixEl,
+ focus,
+ onInput,
+ updated,
+ };
},
- methods: {
- focus() {
- this.$refs.input.focus();
- }
- }
});
</script>
<style lang="scss" scoped>
-.yrtfrpux {
- position: relative;
+.vblkjoeq {
+ > .label {
+ font-size: 0.85em;
+ padding: 0 0 8px 12px;
+ user-select: none;
- > .icon {
- position: absolute;
- top: 0;
- left: 0;
- width: 24px;
- text-align: center;
- line-height: 32px;
+ &:empty {
+ display: none;
+ }
+ }
- &:not(:empty) + .input {
- margin-left: 28px;
+ > .caption {
+ font-size: 0.8em;
+ padding: 8px 0 0 12px;
+ color: var(--fgTransparentWeak);
+
+ &:empty {
+ display: none;
}
}
> .input {
- display: flex;
+ $height: 42px;
position: relative;
> select {
+ appearance: none;
+ -webkit-appearance: none;
display: block;
- flex: 1;
+ height: $height;
width: 100%;
- padding: 0 16px;
+ margin: 0;
+ padding: 0 12px;
font: inherit;
font-weight: normal;
font-size: 1em;
- height: 48px;
- background: none;
- border: none;
- border-radius: 0;
+ color: var(--fg);
+ background: var(--panel);
+ border: solid 1px var(--inputBorder);
+ border-radius: 6px;
outline: none;
box-shadow: none;
- appearance: none;
- -webkit-appearance: none;
- color: var(--fg);
+ box-sizing: border-box;
+ cursor: pointer;
+ transition: border-color 0.1s ease-out;
- option,
- optgroup {
- color: var(--fg);
- background: var(--bg);
+ &:hover {
+ border-color: var(--inputBorderHover);
}
}
> .prefix,
> .suffix {
- display: block;
- align-self: center;
- justify-self: center;
+ display: flex;
+ align-items: center;
+ position: absolute;
+ z-index: 1;
+ top: 0;
+ padding: 0 12px;
font-size: 1em;
- line-height: 32px;
- color: var(--inputLabel);
+ height: $height;
pointer-events: none;
&:empty {
@@ -127,18 +218,42 @@ export default defineComponent({
}
> * {
- display: block;
+ display: inline-block;
min-width: 16px;
+ max-width: 150px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
}
> .prefix {
- padding-right: 4px;
+ left: 0;
+ padding-right: 6px;
}
> .suffix {
- padding: 0 16px 0 0;
+ right: 0;
+ padding-left: 6px;
+ }
+
+ &.inline {
+ display: inline-block;
+ margin: 0;
+ }
+
+ &.focused {
+ > select {
+ border-color: var(--accent);
+ }
+ }
+
+ &.disabled {
opacity: 0.7;
+
+ &, * {
+ cursor: not-allowed !important;
+ }
}
}
}
diff --git a/src/client/components/form/slot.vue b/src/client/components/form/slot.vue
new file mode 100644
index 0000000000..8580c1307d
--- /dev/null
+++ b/src/client/components/form/slot.vue
@@ -0,0 +1,50 @@
+<template>
+<div class="adhpbeou">
+ <div class="label" @click="focus"><slot name="label"></slot></div>
+ <div class="content">
+ <slot></slot>
+ </div>
+ <div class="caption"><slot name="caption"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+
+});
+</script>
+
+<style lang="scss" scoped>
+.adhpbeou {
+ margin: 1.5em 0;
+
+ > .label {
+ font-size: 0.85em;
+ padding: 0 0 8px 12px;
+ user-select: none;
+
+ &:empty {
+ display: none;
+ }
+ }
+
+ > .caption {
+ font-size: 0.8em;
+ padding: 8px 0 0 12px;
+ color: var(--fgTransparentWeak);
+
+ &:empty {
+ display: none;
+ }
+ }
+
+ > .content {
+ position: relative;
+ background: var(--panel);
+ border: solid 0.5px var(--inputBorder);
+ border-radius: 6px;
+ }
+}
+</style>
diff --git a/src/client/components/form/switch.vue b/src/client/components/form/switch.vue
index e7ef714c49..85f8b7c870 100644
--- a/src/client/components/form/switch.vue
+++ b/src/client/components/form/switch.vue
@@ -1,35 +1,34 @@
<template>
-<div class="ijnpvmgr _formItem">
- <div class="main _formPanel _formClickable"
- :class="{ disabled, checked }"
- :aria-checked="checked"
- :aria-disabled="disabled"
- @click.prevent="toggle"
+<div
+ class="ziffeoms"
+ :class="{ disabled, checked }"
+ role="switch"
+ :aria-checked="checked"
+ :aria-disabled="disabled"
+ @click.prevent="toggle"
+>
+ <input
+ type="checkbox"
+ ref="input"
+ :disabled="disabled"
+ @keydown.enter="toggle"
>
- <input
- type="checkbox"
- ref="input"
- :disabled="disabled"
- @keydown.enter="toggle"
- >
- <span class="button">
- <span></span>
- </span>
- <span class="label">
- <span><slot></slot></span>
- </span>
- </div>
- <div class="_formCaption"><slot name="desc"></slot></div>
+ <span class="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff">
+ <span class="handle"></span>
+ </span>
+ <span class="label">
+ <span><slot></slot></span>
+ <p><slot name="caption"></slot></p>
+ </span>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import './form.scss';
export default defineComponent({
props: {
- value: {
+ modelValue: {
type: Boolean,
default: false
},
@@ -40,91 +39,110 @@ export default defineComponent({
},
computed: {
checked(): boolean {
- return this.value;
+ return this.modelValue;
}
},
methods: {
toggle() {
if (this.disabled) return;
- this.$emit('update:value', !this.checked);
+ this.$emit('update:modelValue', !this.checked);
}
}
});
</script>
<style lang="scss" scoped>
-.ijnpvmgr {
- > .main {
- position: relative;
- display: flex;
- padding: 14px 16px;
- cursor: pointer;
+.ziffeoms {
+ position: relative;
+ display: flex;
+ cursor: pointer;
+ transition: all 0.3s;
- > * {
- user-select: none;
- }
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ > * {
+ user-select: none;
+ }
+
+ > input {
+ position: absolute;
+ width: 0;
+ height: 0;
+ opacity: 0;
+ margin: 0;
+ }
- &.disabled {
- opacity: 0.6;
- cursor: not-allowed;
+ > .button {
+ position: relative;
+ display: inline-block;
+ flex-shrink: 0;
+ margin: 0;
+ width: 36px;
+ height: 26px;
+ background: var(--switchBg);
+ outline: none;
+ border-radius: 999px;
+ transition: inherit;
+
+ > .handle {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 5px;
+ margin: auto 0;
+ border-radius: 100%;
+ transition: background-color 0.3s, transform 0.3s;
+ width: 16px;
+ height: 16px;
+ background-color: #fff;
}
+ }
- &.checked {
- > .button {
- background-color: var(--X10);
- border-color: var(--X10);
+ > .label {
+ margin-left: 16px;
+ margin-top: 2px;
+ display: block;
+ cursor: pointer;
+ transition: inherit;
+ color: var(--fg);
- > * {
- background-color: var(--accent);
- transform: translateX(14px);
- }
- }
+ > span {
+ display: block;
+ line-height: 20px;
+ transition: inherit;
}
- > input {
- position: absolute;
- width: 0;
- height: 0;
- opacity: 0;
+ > p {
margin: 0;
+ color: var(--fgTransparentWeak);
+ font-size: 90%;
}
+ }
+ &:hover {
> .button {
- position: relative;
- display: inline-block;
- flex-shrink: 0;
- margin: 3px 0 0 0;
- width: 34px;
- height: 14px;
- background: var(--X6);
- outline: none;
- border-radius: 14px;
- transition: all 0.3s;
- cursor: pointer;
-
- > * {
- position: absolute;
- top: -3px;
- left: 0;
- border-radius: 100%;
- transition: background-color 0.3s, transform 0.3s;
- width: 20px;
- height: 20px;
- background-color: #fff;
- box-shadow: 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12);
- }
+ background-color: var(--accentedBg);
}
+ }
- > .label {
- margin-left: 12px;
- display: block;
- transition: inherit;
- color: var(--fg);
+ &.disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+
+ &.checked {
+ > .button {
+ background-color: var(--accent);
+ border-color: var(--accent);
- > span {
- display: block;
- line-height: 20px;
- transition: inherit;
+ > .handle {
+ transform: translateX(10px);
}
}
}
diff --git a/src/client/components/form/textarea.vue b/src/client/components/form/textarea.vue
index 8f42581a9b..50be69f930 100644
--- a/src/client/components/form/textarea.vue
+++ b/src/client/components/form/textarea.vue
@@ -1,40 +1,45 @@
<template>
-<FormGroup class="_formItem">
- <template #label><slot></slot></template>
- <div class="rivhosbp _formItem" :class="{ tall, pre }">
- <div class="input _formPanel">
- <textarea ref="input" :class="{ code, _monospace: code }"
- v-model="v"
- :required="required"
- :readonly="readonly"
- :pattern="pattern"
- :autocomplete="autocomplete"
- :spellcheck="!code"
- @input="onInput"
- @focus="focused = true"
- @blur="focused = false"
- ></textarea>
- </div>
+<div class="adhpbeos">
+ <div class="label" @click="focus"><slot name="label"></slot></div>
+ <div class="input" :class="{ disabled, focused, tall, pre }">
+ <textarea ref="inputEl"
+ :class="{ code, _monospace: code }"
+ v-model="v"
+ :disabled="disabled"
+ :required="required"
+ :readonly="readonly"
+ :placeholder="placeholder"
+ :pattern="pattern"
+ :autocomplete="autocomplete"
+ :spellcheck="spellcheck"
+ @focus="focused = true"
+ @blur="focused = false"
+ @keydown="onKeydown($event)"
+ @input="onInput"
+ ></textarea>
</div>
- <template #caption><slot name="desc"></slot></template>
+ <div class="caption"><slot name="caption"></slot></div>
- <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
-</FormGroup>
+ <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+</div>
</template>
<script lang="ts">
-import { defineComponent, ref, toRefs, watch } from 'vue';
-import './form.scss';
-import FormButton from './button.vue';
-import FormGroup from './group.vue';
+import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
+import MkButton from '../ui/button.vue';
+import { debounce } from 'throttle-debounce';
export default defineComponent({
components: {
- FormGroup,
- FormButton,
+ MkButton,
},
+
props: {
- value: {
+ modelValue: {
+ required: true
+ },
+ type: {
+ type: String,
required: false
},
required: {
@@ -45,14 +50,29 @@ export default defineComponent({
type: Boolean,
required: false
},
+ disabled: {
+ type: Boolean,
+ required: false
+ },
pattern: {
type: String,
required: false
},
- autocomplete: {
+ placeholder: {
type: String,
required: false
},
+ autofocus: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ autocomplete: {
+ required: false
+ },
+ spellcheck: {
+ required: false
+ },
code: {
type: Boolean,
required: false
@@ -67,91 +87,162 @@ export default defineComponent({
required: false,
default: false
},
+ debounce: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
manualSave: {
type: Boolean,
required: false,
default: false
},
},
+
+ emits: ['change', 'keydown', 'enter', 'update:modelValue'],
+
setup(props, context) {
- const { value } = toRefs(props);
- const v = ref(value.value);
+ const { modelValue, autofocus } = toRefs(props);
+ const v = ref(modelValue.value);
+ const focused = ref(false);
const changed = ref(false);
+ const invalid = ref(false);
+ const filled = computed(() => v.value !== '' && v.value != null);
const inputEl = ref(null);
+
const focus = () => inputEl.value.focus();
const onInput = (ev) => {
changed.value = true;
context.emit('change', ev);
};
+ const onKeydown = (ev: KeyboardEvent) => {
+ context.emit('keydown', ev);
+
+ if (ev.code === 'Enter') {
+ context.emit('enter');
+ }
+ };
const updated = () => {
changed.value = false;
- context.emit('update:value', v.value);
+ context.emit('update:modelValue', v.value);
};
- watch(value, newValue => {
+ const debouncedUpdated = debounce(1000, updated);
+
+ watch(modelValue, newValue => {
v.value = newValue;
});
watch(v, newValue => {
if (!props.manualSave) {
- updated();
+ if (props.debounce) {
+ debouncedUpdated();
+ } else {
+ updated();
+ }
}
+
+ invalid.value = inputEl.value.validity.badInput;
});
-
+
+ onMounted(() => {
+ nextTick(() => {
+ if (autofocus.value) {
+ focus();
+ }
+ });
+ });
+
return {
v,
- updated,
+ focused,
+ invalid,
changed,
+ filled,
+ inputEl,
focus,
onInput,
+ onKeydown,
+ updated,
};
- }
+ },
});
</script>
<style lang="scss" scoped>
-.rivhosbp {
- position: relative;
+.adhpbeos {
+ > .label {
+ font-size: 0.85em;
+ padding: 0 0 8px 12px;
+ user-select: none;
+
+ &:empty {
+ display: none;
+ }
+ }
+
+ > .caption {
+ font-size: 0.8em;
+ padding: 8px 0 0 12px;
+ color: var(--fgTransparentWeak);
+
+ &:empty {
+ display: none;
+ }
+ }
> .input {
position: relative;
-
+
> textarea {
+ appearance: none;
+ -webkit-appearance: none;
display: block;
width: 100%;
min-width: 100%;
max-width: 100%;
min-height: 130px;
margin: 0;
- padding: 16px;
- box-sizing: border-box;
+ padding: 12px;
font: inherit;
font-weight: normal;
font-size: 1em;
- background: transparent;
- border: none;
- border-radius: 0;
+ color: var(--fg);
+ background: var(--panel);
+ border: solid 0.5px var(--inputBorder);
+ border-radius: 6px;
outline: none;
box-shadow: none;
- color: var(--fg);
+ box-sizing: border-box;
+ transition: border-color 0.1s ease-out;
- &.code {
- tab-size: 2;
+ &:hover {
+ border-color: var(--inputBorderHover);
+ }
+ }
+
+ &.focused {
+ > textarea {
+ border-color: var(--accent);
}
}
- }
- &.tall {
- > .input {
+ &.disabled {
+ opacity: 0.7;
+
+ &, * {
+ cursor: not-allowed !important;
+ }
+ }
+
+ &.tall {
> textarea {
min-height: 200px;
}
}
- }
- &.pre {
- > .input {
+ &.pre {
> textarea {
white-space: pre;
}
diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue
new file mode 100644
index 0000000000..a4466da498
--- /dev/null
+++ b/src/client/components/global/header.vue
@@ -0,0 +1,359 @@
+<template>
+<div class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick" ref="el">
+ <template v-if="info">
+ <div class="titleContainer" @click="showTabsPopup" v-if="!hideTitle">
+ <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"/>
+
+ <div 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 class="subtitle" v-if="!narrow && info.subtitle">
+ {{ info.subtitle }}
+ </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 class="tabs" v-if="!narrow || hideTitle">
+ <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>
+ </template>
+ <div class="buttons right">
+ <template v-if="info && info.actions && !narrow">
+ <template v-for="action in info.actions">
+ <MkButton class="fullButton" v-if="action.asFullButton" @click.stop="action.handler" primary><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton>
+ <button v-else 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>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, inject } from 'vue';
+import * as tinycolor from 'tinycolor2';
+import { popupMenu } from '@client/os';
+import { url } from '@client/config';
+import { scrollToTop } from '@client/scripts/scroll';
+import MkButton from '@client/components/ui/button.vue';
+import { i18n } from '@client/i18n';
+import { globalEvents } from '@client/events';
+
+export default defineComponent({
+ components: {
+ MkButton
+ },
+
+ props: {
+ info: {
+ type: Object as PropType<{
+ actions?: {}[];
+ tabs?: {}[];
+ }>,
+ required: true
+ },
+ menu: {
+ required: false
+ },
+ thin: {
+ required: false,
+ default: false
+ },
+ },
+
+ setup(props) {
+ const el = ref<HTMLElement>(null);
+ const bg = ref(null);
+ const narrow = ref(false);
+ const height = ref(0);
+ const hasTabs = computed(() => {
+ return props.info.tabs && props.info.tabs.length > 0;
+ });
+ const shouldShowMenu = computed(() => {
+ if (props.info == null) return false;
+ if (props.info.actions != null && narrow.value) return true;
+ if (props.info.menu != null) return true;
+ if (props.info.share != null) return true;
+ if (props.menu != null) return true;
+ return false;
+ });
+
+ const share = () => {
+ navigator.share({
+ url: url + props.info.path,
+ ...props.info.share,
+ });
+ };
+
+ const showMenu = (ev: MouseEvent) => {
+ let menu = props.info.menu ? props.info.menu() : [];
+ if (narrow.value && props.info.actions) {
+ menu = [...props.info.actions.map(x => ({
+ text: x.text,
+ icon: x.icon,
+ action: x.handler
+ })), menu.length > 0 ? null : undefined, ...menu];
+ }
+ if (props.info.share) {
+ if (menu.length > 0) menu.push(null);
+ menu.push({
+ text: i18n.locale.share,
+ icon: 'fas fa-share-alt',
+ action: share
+ });
+ }
+ if (props.menu) {
+ if (menu.length > 0) menu.push(null);
+ menu = menu.concat(props.menu);
+ }
+ popupMenu(menu, ev.currentTarget || ev.target);
+ };
+
+ const showTabsPopup = (ev: MouseEvent) => {
+ if (!hasTabs.value) return;
+ if (!narrow.value) return;
+ ev.preventDefault();
+ ev.stopPropagation();
+ const menu = props.info.tabs.map(tab => ({
+ text: tab.title,
+ icon: tab.icon,
+ action: tab.onClick,
+ }));
+ popupMenu(menu, ev.currentTarget || ev.target);
+ };
+
+ const preventDrag = (ev: TouchEvent) => {
+ ev.stopPropagation();
+ };
+
+ const onClick = () => {
+ scrollToTop(el.value, { behavior: 'smooth' });
+ };
+
+ const calcBg = () => {
+ const rawBg = props.info?.bg || 'var(--bg)';
+ const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
+ tinyBg.setAlpha(0.85);
+ bg.value = tinyBg.toRgbString();
+ };
+
+ onMounted(() => {
+ calcBg();
+ globalEvents.on('themeChanged', calcBg);
+ onUnmounted(() => {
+ globalEvents.off('themeChanged', calcBg);
+ });
+
+ if (el.value.parentElement) {
+ narrow.value = el.value.parentElement.offsetWidth < 500;
+ const ro = new ResizeObserver((entries, observer) => {
+ if (el.value) {
+ narrow.value = el.value.parentElement.offsetWidth < 500;
+ }
+ });
+ ro.observe(el.value.parentElement);
+ onUnmounted(() => {
+ ro.disconnect();
+ });
+ setTimeout(() => {
+ const currentStickyTop = getComputedStyle(el.value.parentElement).getPropertyValue('--stickyTop') || '0px';
+ el.value.style.setProperty('--stickyTop', currentStickyTop);
+ el.value.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${el.value.offsetHeight}px)`);
+ }, 100); // レンダリング順序の関係で親のstickyTopの設定が少し遅れることがあるため
+ }
+ });
+
+ return {
+ el,
+ bg,
+ narrow,
+ height,
+ hasTabs,
+ shouldShowMenu,
+ share,
+ showMenu,
+ showTabsPopup,
+ preventDrag,
+ onClick,
+ hideTitle: inject('shouldOmitHeaderTitle', false),
+ thin_: props.thin || inject('shouldHeaderThin', false)
+ };
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.fdidabkb {
+ --height: 60px;
+ display: flex;
+ position: sticky;
+ top: var(--stickyTop, 0);
+ z-index: 1000;
+ width: 100%;
+ -webkit-backdrop-filter: var(--blur, blur(15px));
+ backdrop-filter: var(--blur, blur(15px));
+ border-bottom: solid 0.5px var(--divider);
+
+ &.thin {
+ --height: 50px;
+ }
+
+ &.slim {
+ text-align: center;
+
+ > .titleContainer {
+ flex: 1;
+ margin: 0 auto;
+ margin-left: var(--height);
+
+ > *:first-child {
+ margin-left: auto;
+ }
+
+ > *:last-child {
+ margin-right: auto;
+ }
+ }
+ }
+
+ > .buttons {
+ --margin: 8px;
+ display: flex;
+ align-items: center;
+ height: var(--height);
+ margin: 0 var(--margin);
+
+ &.right {
+ margin-left: auto;
+ }
+
+ &:empty {
+ width: var(--height);
+ }
+
+ > .button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: calc(var(--height) - (var(--margin) * 2));
+ width: calc(var(--height) - (var(--margin) * 2));
+ box-sizing: border-box;
+ position: relative;
+ border-radius: 5px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.05);
+ }
+
+ &.highlighted {
+ color: var(--accent);
+ }
+ }
+
+ > .fullButton {
+ & + .fullButton {
+ margin-left: 12px;
+ }
+ }
+ }
+
+ > .titleContainer {
+ display: flex;
+ align-items: center;
+ overflow: auto;
+ white-space: nowrap;
+ text-align: left;
+ font-weight: bold;
+ flex-shrink: 0;
+ margin-left: 24px;
+
+ > .avatar {
+ $size: 32px;
+ display: inline-block;
+ width: $size;
+ height: $size;
+ vertical-align: bottom;
+ margin: 0 8px;
+ pointer-events: none;
+ }
+
+ > .icon {
+ margin-right: 8px;
+ }
+
+ > .title {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ line-height: 1.1;
+
+ > .subtitle {
+ opacity: 0.6;
+ font-size: 0.8em;
+ font-weight: normal;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ &.activeTab {
+ text-align: center;
+
+ > .chevron {
+ display: inline-block;
+ margin-left: 6px;
+ }
+ }
+ }
+ }
+ }
+
+ > .tabs {
+ margin-left: 16px;
+ font-size: 0.8em;
+ overflow: auto;
+ white-space: nowrap;
+
+ > .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;
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/global/spacer.vue b/src/client/components/global/spacer.vue
new file mode 100644
index 0000000000..1129d54c71
--- /dev/null
+++ b/src/client/components/global/spacer.vue
@@ -0,0 +1,76 @@
+<template>
+<div ref="root" :class="$style.root" :style="{ padding: margin + 'px' }">
+ <div ref="content" :class="$style.content">
+ <slot></slot>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
+
+export default defineComponent({
+ props: {
+ contentMax: {
+ type: Number,
+ required: false,
+ default: null,
+ }
+ },
+
+ setup(props, context) {
+ let ro: ResizeObserver;
+ const root = ref<HTMLElement>(null);
+ const content = ref<HTMLElement>(null);
+ const margin = ref(0);
+ const adjust = (rect: { width: number; height: number; }) => {
+ if (rect.width > (props.contentMax || 500)) {
+ margin.value = 32;
+ } else {
+ margin.value = 12;
+ }
+ };
+
+ onMounted(() => {
+ ro = new ResizeObserver((entries) => {
+ /* iOSが対応していない
+ adjust({
+ width: entries[0].borderBoxSize[0].inlineSize,
+ height: entries[0].borderBoxSize[0].blockSize,
+ });
+ */
+ adjust({
+ width: root.value.offsetWidth,
+ height: root.value.offsetHeight,
+ });
+ });
+ ro.observe(root.value);
+
+ if (props.contentMax) {
+ content.value.style.maxWidth = `${props.contentMax}px`;
+ }
+ });
+
+ onUnmounted(() => {
+ ro.disconnect();
+ });
+
+ return {
+ root,
+ content,
+ margin,
+ };
+ },
+});
+</script>
+
+<style lang="scss" module>
+.root {
+ box-sizing: border-box;
+ width: 100%;
+}
+
+.content {
+ margin: 0 auto;
+}
+</style>
diff --git a/src/client/components/index.ts b/src/client/components/index.ts
index 8b914c5eec..ecf66ea0e8 100644
--- a/src/client/components/index.ts
+++ b/src/client/components/index.ts
@@ -13,6 +13,8 @@ import i18n from './global/i18n';
import loading from './global/loading.vue';
import error from './global/error.vue';
import ad from './global/ad.vue';
+import header from './global/header.vue';
+import spacer from './global/spacer.vue';
export default function(app: App) {
app.component('I18n', i18n);
@@ -28,4 +30,6 @@ export default function(app: App) {
app.component('MkLoading', loading);
app.component('MkError', error);
app.component('MkAd', ad);
+ app.component('MkHeader', header);
+ app.component('MkSpacer', spacer);
}
diff --git a/src/client/components/instance-stats.vue b/src/client/components/instance-stats.vue
index 78044f0b16..5e7c71ea65 100644
--- a/src/client/components/instance-stats.vue
+++ b/src/client/components/instance-stats.vue
@@ -36,7 +36,7 @@
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import Chart from 'chart.js';
-import MkSelect from './ui/select.vue';
+import MkSelect from './form/select.vue';
import number from '@client/filters/number';
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
diff --git a/src/client/components/media-caption.vue b/src/client/components/media-caption.vue
index 690927d4c5..b35b101d06 100644
--- a/src/client/components/media-caption.vue
+++ b/src/client/components/media-caption.vue
@@ -3,10 +3,13 @@
<div class="container">
<div class="fullwidth top-caption">
<div class="mk-dialog">
- <header v-if="title"><Mfm :text="title"/></header>
+ <header>
+ <Mfm v-if="title" class="title" :text="title"/>
+ <span class="text-count" :class="{ over: remainingLength < 0 }">{{ remainingLength }}</span>
+ </header>
<textarea autofocus v-model="inputValue" :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea>
<div class="buttons" v-if="(showOkButton || showCancelButton)">
- <MkButton inline @click="ok" primary>{{ $ts.ok }}</MkButton>
+ <MkButton inline @click="ok" primary :disabled="remainingLength < 0">{{ $ts.ok }}</MkButton>
<MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton>
</div>
</div>
@@ -26,10 +29,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
+import { length } from 'stringz';
import MkModal from '@client/components/ui/modal.vue';
import MkButton from '@client/components/ui/button.vue';
import bytes from '@client/filters/bytes';
import number from '@client/filters/number';
+import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits';
export default defineComponent({
components: {
@@ -79,6 +84,13 @@ export default defineComponent({
document.removeEventListener('keydown', this.onKeydown);
},
+ computed: {
+ remainingLength(): number {
+ if (typeof this.inputValue != "string") return DB_MAX_IMAGE_COMMENT_LENGTH;
+ return DB_MAX_IMAGE_COMMENT_LENGTH - length(this.inputValue);
+ }
+ },
+
methods: {
bytes,
number,
@@ -156,8 +168,18 @@ export default defineComponent({
> header {
margin: 0 0 8px 0;
- font-weight: bold;
- font-size: 20px;
+ position: relative;
+
+ > .title {
+ font-weight: bold;
+ font-size: 20px;
+ }
+
+ > .text-count {
+ opacity: 0.7;
+ position: absolute;
+ right: 0;
+ }
}
> .buttons {
@@ -184,7 +206,7 @@ export default defineComponent({
min-width: 100%;
min-height: 90px;
- &:focus {
+ &:focus-visible {
outline: none;
}
diff --git a/src/client/components/mfm.ts b/src/client/components/mfm.ts
index a228ca4b8d..2bdd7d46ee 100644
--- a/src/client/components/mfm.ts
+++ b/src/client/components/mfm.ts
@@ -185,7 +185,7 @@ export default defineComponent({
}
}
if (style == null) {
- return h('span', {}, ['[', token.props.name, ...genEl(token.children), ']']);
+ return h('span', {}, ['[', token.props.name, ' ', ...genEl(token.children), ']']);
} else {
return h('span', {
style: 'display: inline-block;' + style,
diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue
index e7d96f7a6f..cb81a974f5 100644
--- a/src/client/components/modal-page-window.vue
+++ b/src/client/components/modal-page-window.vue
@@ -2,11 +2,15 @@
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
<div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
<div class="header" @contextmenu="onContextmenu">
- <span class="title">
- <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="$refs.modal.close()"/>
+ <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button>
+ <span v-else style="display: inline-block; width: 20px"></span>
+ <span v-if="pageInfo" class="title">
+ <i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon"></i>
+ <span>{{ pageInfo.title }}</span>
</span>
+ <button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button>
</div>
- <div class="body _flat_">
+ <div class="body _fitSide_">
<keep-alive>
<component :is="component" v-bind="props" :ref="changePage"/>
</keep-alive>
@@ -18,7 +22,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkModal from '@client/components/ui/modal.vue';
-import XHeader from '@client/ui/_common_/header.vue';
import { popout } from '@client/scripts/popout';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import { resolve } from '@client/router';
@@ -29,7 +32,6 @@ import * as os from '@client/os';
export default defineComponent({
components: {
MkModal,
- XHeader,
},
inject: {
@@ -42,7 +44,8 @@ export default defineComponent({
return {
navHook: (path) => {
this.navigate(path);
- }
+ },
+ shouldHeaderThin: true,
};
},
@@ -172,19 +175,39 @@ export default defineComponent({
$height-narrow: 42px;
display: flex;
flex-shrink: 0;
+ height: $height;
+ line-height: $height;
+ font-weight: bold;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
box-shadow: 0px 1px var(--divider);
- > .title {
- flex: 1;
+ > button {
height: $height;
- font-weight: bold;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
+ width: $height;
+
+ &:hover {
+ color: var(--fgHighlighted);
+ }
+ }
+
+ @media (max-width: 500px) {
+ height: $height-narrow;
+ line-height: $height-narrow;
+ padding-left: 16px;
- @media (max-width: 500px) {
+ > button {
height: $height-narrow;
- padding-left: 16px;
+ width: $height-narrow;
+ }
+ }
+
+ > .title {
+ flex: 1;
+
+ > .icon {
+ margin-right: 0.5em;
}
}
}
diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue
index e7f116d1fd..40b0a68c58 100644
--- a/src/client/components/note-detailed.vue
+++ b/src/client/components/note-detailed.vue
@@ -59,7 +59,7 @@
<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" :custom-emojis="appearNote.emojis"/>
- <XCwButton v-model:value="showContent" :note="appearNote"/>
+ <XCwButton v-model="showContent" :note="appearNote"/>
</p>
<div class="content" v-show="appearNote.cw == null || showContent">
<div class="text">
@@ -80,7 +80,7 @@
</div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="true" class="url-preview"/>
- <div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
+ <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div>
</div>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA>
</div>
@@ -132,7 +132,7 @@ import * as mfm from 'mfm-js';
import { sum } from '../../prelude/array';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
-import XNotePreview from './note-preview.vue';
+import XNoteSimple from './note-simple.vue';
import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
@@ -153,7 +153,7 @@ export default defineComponent({
components: {
XSub,
XNoteHeader,
- XNotePreview,
+ XNoteSimple,
XReactionsViewer,
XMediaList,
XCwButton,
diff --git a/src/client/components/note-preview.vue b/src/client/components/note-preview.vue
index 4248c2bb1d..a474a01341 100644
--- a/src/client/components/note-preview.vue
+++ b/src/client/components/note-preview.vue
@@ -1,15 +1,13 @@
<template>
-<div class="yohlumlk" v-size="{ min: [350, 500] }">
- <MkAvatar class="avatar" :user="note.user"/>
+<div class="fefdfafb" v-size="{ min: [350, 500] }">
+ <MkAvatar class="avatar" :user="$i"/>
<div class="main">
- <XNoteHeader class="header" :note="note" :mini="true"/>
+ <div class="header">
+ <MkUserName :user="$i"/>
+ </div>
<div class="body">
- <p v-if="note.cw != null" class="cw">
- <span class="text" v-if="note.cw != ''">{{ note.cw }}</span>
- <XCwButton v-model:value="showContent" :note="note"/>
- </p>
- <div class="content" v-show="note.cw == null || showContent">
- <XSubNote-content class="text" :note="note"/>
+ <div class="content">
+ <Mfm :text="text" :author="$i" :i="$i"/>
</div>
</div>
</div>
@@ -18,35 +16,22 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import XNoteHeader from './note-header.vue';
-import XSubNoteContent from './sub-note-content.vue';
-import XCwButton from './cw-button.vue';
-import * as os from '@client/os';
export default defineComponent({
components: {
- XNoteHeader,
- XSubNoteContent,
- XCwButton,
},
props: {
- note: {
- type: Object,
+ text: {
+ type: String,
required: true
}
},
-
- data() {
- return {
- showContent: false
- };
- }
});
</script>
<style lang="scss" scoped>
-.yohlumlk {
+.fefdfafb {
display: flex;
margin: 0;
padding: 0;
diff --git a/src/client/components/note-simple.vue b/src/client/components/note-simple.vue
new file mode 100644
index 0000000000..406a475cd9
--- /dev/null
+++ b/src/client/components/note-simple.vue
@@ -0,0 +1,113 @@
+<template>
+<div class="yohlumlk" v-size="{ min: [350, 500] }">
+ <MkAvatar class="avatar" :user="note.user"/>
+ <div class="main">
+ <XNoteHeader class="header" :note="note" :mini="true"/>
+ <div class="body">
+ <p v-if="note.cw != null" class="cw">
+ <span class="text" v-if="note.cw != ''">{{ note.cw }}</span>
+ <XCwButton v-model="showContent" :note="note"/>
+ </p>
+ <div class="content" v-show="note.cw == null || showContent">
+ <XSubNote-content class="text" :note="note"/>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import XNoteHeader from './note-header.vue';
+import XSubNoteContent from './sub-note-content.vue';
+import XCwButton from './cw-button.vue';
+import * as os from '@client/os';
+
+export default defineComponent({
+ components: {
+ XNoteHeader,
+ XSubNoteContent,
+ XCwButton,
+ },
+
+ props: {
+ note: {
+ type: Object,
+ required: true
+ }
+ },
+
+ data() {
+ return {
+ showContent: false
+ };
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.yohlumlk {
+ display: flex;
+ margin: 0;
+ padding: 0;
+ overflow: clip;
+ font-size: 0.95em;
+
+ &.min-width_350px {
+ > .avatar {
+ margin: 0 10px 0 0;
+ width: 44px;
+ height: 44px;
+ }
+ }
+
+ &.min-width_500px {
+ > .avatar {
+ margin: 0 12px 0 0;
+ width: 48px;
+ height: 48px;
+ }
+ }
+
+ > .avatar {
+ flex-shrink: 0;
+ display: block;
+ margin: 0 10px 0 0;
+ width: 40px;
+ height: 40px;
+ border-radius: 8px;
+ }
+
+ > .main {
+ flex: 1;
+ min-width: 0;
+
+ > .header {
+ margin-bottom: 2px;
+ }
+
+ > .body {
+
+ > .cw {
+ cursor: default;
+ display: block;
+ margin: 0;
+ padding: 0;
+ overflow-wrap: break-word;
+
+ > .text {
+ margin-right: 8px;
+ }
+ }
+
+ > .content {
+ > .text {
+ cursor: default;
+ margin: 0;
+ padding: 0;
+ }
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/note.sub.vue b/src/client/components/note.sub.vue
index 899c4b2f16..157b65ec5c 100644
--- a/src/client/components/note.sub.vue
+++ b/src/client/components/note.sub.vue
@@ -7,7 +7,7 @@
<div class="body">
<p v-if="note.cw != null" class="cw">
<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" />
- <XCwButton v-model:value="showContent" :note="note"/>
+ <XCwButton v-model="showContent" :note="note"/>
</p>
<div class="content" v-show="note.cw == null || showContent">
<XSubNote-content class="text" :note="note"/>
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 38b529dd91..91a3e3b87d 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -43,7 +43,7 @@
<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" :custom-emojis="appearNote.emojis"/>
- <XCwButton v-model:value="showContent" :note="appearNote"/>
+ <XCwButton v-model="showContent" :note="appearNote"/>
</p>
<div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent">
<div class="text">
@@ -64,7 +64,7 @@
</div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/>
- <div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
+ <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span>
</button>
@@ -114,7 +114,7 @@ import * as mfm from 'mfm-js';
import { sum } from '../../prelude/array';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
-import XNotePreview from './note-preview.vue';
+import XNoteSimple from './note-simple.vue';
import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
@@ -134,7 +134,7 @@ export default defineComponent({
components: {
XSub,
XNoteHeader,
- XNotePreview,
+ XNoteSimple,
XReactionsViewer,
XMediaList,
XCwButton,
@@ -888,7 +888,7 @@ export default defineComponent({
//content-visibility: auto;
//contain-intrinsic-size: 0 128px;
- &:focus {
+ &:focus-visible {
outline: none;
&:after {
diff --git a/src/client/components/notification-setting-window.vue b/src/client/components/notification-setting-window.vue
index c33106ae15..14e0b76cc6 100644
--- a/src/client/components/notification-setting-window.vue
+++ b/src/client/components/notification-setting-window.vue
@@ -29,10 +29,10 @@
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
-import MkSwitch from './ui/switch.vue';
+import MkSwitch from './form/switch.vue';
import MkInfo from './ui/info.vue';
import MkButton from './ui/button.vue';
-import { notificationTypes } from '../../types';
+import { notificationTypes } from '@/types';
export default defineComponent({
components: {
diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue
index e91f18a693..78c1cce0c7 100644
--- a/src/client/components/notifications.vue
+++ b/src/client/components/notifications.vue
@@ -26,7 +26,7 @@ import paging from '@client/scripts/paging';
import XNotification from './notification.vue';
import XList from './date-separated-list.vue';
import XNote from './note.vue';
-import { notificationTypes } from '../../types';
+import { notificationTypes } from '@/types';
import * as os from '@client/os';
import MkButton from '@client/components/ui/button.vue';
@@ -48,6 +48,11 @@ export default defineComponent({
required: false,
default: null,
},
+ unreadOnly: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
@@ -58,6 +63,7 @@ export default defineComponent({
limit: 10,
params: () => ({
includeTypes: this.allIncludeTypes || undefined,
+ unreadOnly: this.unreadOnly,
})
},
};
@@ -76,6 +82,11 @@ export default defineComponent({
},
deep: true
},
+ unreadOnly: {
+ handler() {
+ this.reload();
+ },
+ },
// TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $i が更新されると、
// mutingNotificationTypes に変化が無くてもこのハンドラーが呼び出され無駄なリロードが発生するのを直す
'$i.mutingNotificationTypes': {
diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue
index fbc9f0b7fd..7d15c75d62 100644
--- a/src/client/components/page-window.vue
+++ b/src/client/components/page-window.vue
@@ -3,14 +3,20 @@
:initial-width="500"
:initial-height="500"
:can-resize="true"
- :close-button="false"
+ :close-button="true"
:contextmenu="contextmenu"
@closed="$emit('closed')"
>
<template #header>
- <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()" :title-only="true"/>
+ <template v-if="pageInfo">
+ <i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon" style="margin-right: 0.5em;"></i>
+ <span>{{ pageInfo.title }}</span>
+ </template>
</template>
- <div class="yrolvcoq _flat_">
+ <template #headerLeft>
+ <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button>
+ </template>
+ <div class="yrolvcoq _fitSide_">
<component :is="component" v-bind="props" :ref="changePage"/>
</div>
</XWindow>
@@ -19,7 +25,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XWindow from '@client/components/ui/window.vue';
-import XHeader from '@client/ui/_common_/header.vue';
import { popout } from '@client/scripts/popout';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import { resolve } from '@client/router';
@@ -29,7 +34,6 @@ import * as symbols from '@client/symbols';
export default defineComponent({
components: {
XWindow,
- XHeader,
},
inject: {
@@ -42,7 +46,8 @@ export default defineComponent({
return {
navHook: (path) => {
this.navigate(path);
- }
+ },
+ shouldHeaderThin: true,
};
},
diff --git a/src/client/components/page/page.number-input.vue b/src/client/components/page/page.number-input.vue
index 9c4a537e15..5d9168f130 100644
--- a/src/client/components/page/page.number-input.vue
+++ b/src/client/components/page/page.number-input.vue
@@ -8,7 +8,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
-import MkInput from '../ui/input.vue';
+import MkInput from '../form/input.vue';
import * as os from '@client/os';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { NumberInputVarBlock } from '@client/scripts/hpml/block';
diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue
index 7b061d8cda..c20d7cade1 100644
--- a/src/client/components/page/page.post.vue
+++ b/src/client/components/page/page.post.vue
@@ -10,7 +10,7 @@
<script lang="ts">
import { defineComponent, PropType } from 'vue';
-import MkTextarea from '../ui/textarea.vue';
+import MkTextarea from '../form/textarea.vue';
import MkButton from '../ui/button.vue';
import { apiUrl } from '@client/config';
import * as os from '@client/os';
diff --git a/src/client/components/page/page.radio-button.vue b/src/client/components/page/page.radio-button.vue
index f6f146b52f..590e59d706 100644
--- a/src/client/components/page/page.radio-button.vue
+++ b/src/client/components/page/page.radio-button.vue
@@ -7,7 +7,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
-import MkRadio from '../ui/radio.vue';
+import MkRadio from '../form/radio.vue';
import * as os from '@client/os';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { RadioButtonVarBlock } from '@client/scripts/hpml/block';
diff --git a/src/client/components/page/page.switch.vue b/src/client/components/page/page.switch.vue
index 8818e6cbcf..4d74e5df39 100644
--- a/src/client/components/page/page.switch.vue
+++ b/src/client/components/page/page.switch.vue
@@ -6,7 +6,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
-import MkSwitch from '../ui/switch.vue';
+import MkSwitch from '../form/switch.vue';
import * as os from '@client/os';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { SwitchVarBlock } from '@client/scripts/hpml/block';
diff --git a/src/client/components/page/page.text-input.vue b/src/client/components/page/page.text-input.vue
index 752d3d7257..6e9ac0b543 100644
--- a/src/client/components/page/page.text-input.vue
+++ b/src/client/components/page/page.text-input.vue
@@ -8,7 +8,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
-import MkInput from '../ui/input.vue';
+import MkInput from '../form/input.vue';
import * as os from '@client/os';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { TextInputVarBlock } from '@client/scripts/hpml/block';
diff --git a/src/client/components/page/page.textarea-input.vue b/src/client/components/page/page.textarea-input.vue
index e6cf5117f9..dfcb398937 100644
--- a/src/client/components/page/page.textarea-input.vue
+++ b/src/client/components/page/page.textarea-input.vue
@@ -8,7 +8,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
-import MkTextarea from '../ui/textarea.vue';
+import MkTextarea from '../form/textarea.vue';
import * as os from '@client/os';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { HpmlTextInput } from '@client/scripts/hpml';
diff --git a/src/client/components/page/page.textarea.vue b/src/client/components/page/page.textarea.vue
index 974c7f2c57..cf953bf041 100644
--- a/src/client/components/page/page.textarea.vue
+++ b/src/client/components/page/page.textarea.vue
@@ -6,7 +6,7 @@
import { TextBlock } from '@client/scripts/hpml/block';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { defineComponent, PropType } from 'vue';
-import MkTextarea from '../ui/textarea.vue';
+import MkTextarea from '../form/textarea.vue';
export default defineComponent({
components: {
diff --git a/src/client/components/poll-editor.vue b/src/client/components/poll-editor.vue
index 0a9a1c6a03..b28a1c8baa 100644
--- a/src/client/components/poll-editor.vue
+++ b/src/client/components/poll-editor.vue
@@ -51,9 +51,9 @@
import { defineComponent } from 'vue';
import { addTime } from '../../prelude/time';
import { formatDateTimeString } from '@/misc/format-time-string';
-import MkInput from './ui/input.vue';
-import MkSelect from './ui/select.vue';
-import MkSwitch from './ui/switch.vue';
+import MkInput from './form/input.vue';
+import MkSelect from './form/select.vue';
+import MkSwitch from './form/switch.vue';
import MkButton from './ui/button.vue';
export default defineComponent({
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index 657053cc93..a1d89d2a2e 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -1,6 +1,6 @@
<template>
<div class="gafaadew" :class="{ modal, _popup: modal }"
- v-size="{ max: [500] }"
+ v-size="{ max: [310, 500] }"
@dragover.stop="onDragover"
@dragenter="onDragenter"
@dragleave="onDragleave"
@@ -17,12 +17,13 @@
<span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span>
<span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span>
</button>
- <button class="submit _buttonPrimary" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button>
+ <button class="_button preview" @click="showPreview = !showPreview" :class="{ active: showPreview }" v-tooltip="$ts.previewNoteText"><i class="fas fa-file-code"></i></button>
+ <button class="submit _buttonGradate" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button>
</div>
</header>
<div class="form" :class="{ fixed }">
- <XNotePreview class="preview" v-if="reply" :note="reply"/>
- <XNotePreview class="preview" v-if="renote" :note="renote"/>
+ <XNoteSimple class="preview" v-if="reply" :note="reply"/>
+ <XNoteSimple class="preview" v-if="renote" :note="renote"/>
<div class="with-quote" v-if="quoteId"><i class="fas fa-quote-left"></i> {{ $ts.quoteAttached }}<button @click="quoteId = null"><i class="fas fa-times"></i></button></div>
<div v-if="visibility === 'specified'" class="to-specified">
<span style="margin-right: 8px;">{{ $ts.recipient }}</span>
@@ -40,6 +41,7 @@
<input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags">
<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/>
<XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
+ <XNotePreview class="preview" v-if="showPreview" :text="text"/>
<footer>
<button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><i class="fas fa-photo-video"></i></button>
<button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><i class="fas fa-poll-h"></i></button>
@@ -61,6 +63,7 @@ import { defineComponent, defineAsyncComponent } from 'vue';
import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz';
import { toASCII } from 'punycode/';
+import XNoteSimple from './note-simple.vue';
import XNotePreview from './note-preview.vue';
import * as mfm from 'mfm-js';
import { host, url } from '@client/config';
@@ -80,6 +83,7 @@ import { defaultStore } from '@client/store';
export default defineComponent({
components: {
+ XNoteSimple,
XNotePreview,
XPostFormAttaches: defineAsyncComponent(() => import('./post-form-attaches.vue')),
XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue')),
@@ -143,6 +147,7 @@ export default defineComponent({
files: [],
poll: null,
useCw: false,
+ showPreview: false,
cw: null,
localOnly: this.$store.state.rememberNoteVisibility ? this.$store.state.localOnly : this.$store.state.defaultNoteLocalOnly,
visibility: this.$store.state.rememberNoteVisibility ? this.$store.state.visibility : this.$store.state.defaultNoteVisibility,
@@ -717,7 +722,7 @@ export default defineComponent({
> .visibility {
height: 34px;
width: 34px;
- margin: 0 8px;
+ margin: 0 0 0 8px;
& + .localOnly {
margin-left: 0 !important;
@@ -729,6 +734,24 @@ export default defineComponent({
opacity: 0.7;
}
+ > .preview {
+ display: inline-block;
+ padding: 0;
+ margin: 0 8px 0 0;
+ font-size: 16px;
+ width: 34px;
+ height: 34px;
+ border-radius: 6px;
+
+ &:hover {
+ background: var(--X5);
+ }
+
+ &.active {
+ color: var(--accent);
+ }
+ }
+
> .submit {
margin: 16px 16px 16px 0;
padding: 0 12px;
@@ -736,6 +759,7 @@ export default defineComponent({
font-weight: bold;
vertical-align: bottom;
border-radius: 4px;
+ font-size: 0.9em;
&:disabled {
opacity: 0.7;
@@ -819,7 +843,7 @@ export default defineComponent({
color: var(--fg);
font-family: inherit;
- &:focus {
+ &:focus-visible {
outline: none;
}
@@ -914,5 +938,17 @@ export default defineComponent({
}
}
}
+
+ &.max-width_310px {
+ > .form {
+ > footer {
+ > button {
+ font-size: 14px;
+ width: 44px;
+ height: 44px;
+ }
+ }
+ }
+ }
}
</style>
diff --git a/src/client/components/sample.vue b/src/client/components/sample.vue
index bce02466f6..c8b46a80e7 100644
--- a/src/client/components/sample.vue
+++ b/src/client/components/sample.vue
@@ -30,10 +30,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkRadio from '@client/components/ui/radio.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkRadio from '@client/components/form/radio.vue';
import * as os from '@client/os';
import * as config from '@client/config';
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index 69f527b7d6..d6e1ee8b68 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -1,17 +1,17 @@
<template>
<form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
- <div class="auth _section">
+ <div class="auth _section _formRoot">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<div class="normal-signin" v-if="!totpLogin">
- <MkInput v-model="username" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:modelValue="onUsernameChange" data-cy-signin-username>
+ <MkInput class="_formBlock" v-model="username" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:modelValue="onUsernameChange" data-cy-signin-username>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
- <MkInput v-model="password" :placeholder="$ts.password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required data-cy-signin-password>
+ <MkInput class="_formBlock" v-model="password" :placeholder="$ts.password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required data-cy-signin-password>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption><button class="_textButton" @click="resetPassword" type="button">{{ $ts.forgotPassword }}</button></template>
</MkInput>
- <MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
+ <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
</div>
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
@@ -49,7 +49,7 @@
import { defineComponent } from 'vue';
import { toUnicode } from 'punycode/';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import { apiUrl, host } from '@client/config';
import { byteify, hexify } from '@client/scripts/2fa';
import * as os from '@client/os';
diff --git a/src/client/components/signup-dialog.vue b/src/client/components/signup-dialog.vue
index df1a525055..9741e8c73b 100644
--- a/src/client/components/signup-dialog.vue
+++ b/src/client/components/signup-dialog.vue
@@ -9,7 +9,7 @@
<div class="_monolithic_">
<div class="_section">
- <XSignup :auto-set="autoSet" @signup="onSignup"/>
+ <XSignup :auto-set="autoSet" @signup="onSignup" @signupEmailPending="onSignupEmailPending"/>
</div>
</div>
</XModalWindow>
@@ -40,6 +40,10 @@ export default defineComponent({
onSignup(res) {
this.$emit('done', res);
this.$refs.dialog.close();
+ },
+
+ onSignupEmailPending() {
+ this.$refs.dialog.close();
}
}
});
diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue
index d332274111..cb25eadf06 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -1,25 +1,35 @@
<template>
-<form class="qlvuhzng" @submit.prevent="onSubmit" :autocomplete="Math.random()">
+<form class="qlvuhzng _formRoot" @submit.prevent="onSubmit" :autocomplete="Math.random()">
<template v-if="meta">
- <MkInput class="_inputNoTopMargin" v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
+ <MkInput class="_formBlock" v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
<template #label>{{ $ts.invitationCode }}</template>
<template #prefix><i class="fas fa-key"></i></template>
</MkInput>
- <MkInput class="_inputNoTopMargin" v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeUsername" data-cy-signup-username>
+ <MkInput class="_formBlock" v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeUsername" data-cy-signup-username>
<template #label>{{ $ts.username }} <div class="_button _help" v-tooltip:dialog="$ts.usernameInfo"><i class="far fa-question-circle"></i></div></template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
<template #caption>
- <span v-if="usernameState == 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
- <span v-if="usernameState == 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
- <span v-if="usernameState == 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
- <span v-if="usernameState == 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
- <span v-if="usernameState == 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span>
- <span v-if="usernameState == 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span>
- <span v-if="usernameState == 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
+ <span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
+ <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
+ <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
+ <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
+ <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span>
+ <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span>
+ <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
</template>
</MkInput>
- <MkInput v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password>
+ <MkInput v-if="meta.emailRequiredForSignup" class="_formBlock" v-model="email" type="email" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeEmail" data-cy-signup-email>
+ <template #label>{{ $ts.emailAddress }} <div class="_button _help" v-tooltip:dialog="$ts._signup.emailAddressInfo"><i class="far fa-question-circle"></i></div></template>
+ <template #prefix><i class="fas fa-envelope"></i></template>
+ <template #caption>
+ <span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
+ <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
+ <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
+ <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
+ </template>
+ </MkInput>
+ <MkInput class="_formBlock" v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password>
<template #label>{{ $ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
@@ -28,7 +38,7 @@
<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.strongPassword }}</span>
</template>
</MkInput>
- <MkInput v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePasswordRetype" data-cy-signup-password-retype>
+ <MkInput class="_formBlock" v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePasswordRetype" data-cy-signup-password-retype>
<template #label>{{ $ts.password }} ({{ $ts.retype }})</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
@@ -36,7 +46,7 @@
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span>
</template>
</MkInput>
- <label v-if="meta.tosUrl" class="tou">
+ <label v-if="meta.tosUrl" class="_formBlock tou">
<input type="checkbox" v-model="ToSAgreement">
<I18n :src="$ts.agreeTo">
<template #0>
@@ -44,9 +54,9 @@
</template>
</I18n>
</label>
- <captcha v-if="meta.enableHcaptcha" class="captcha" provider="hcaptcha" ref="hcaptcha" v-model:value="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/>
- <captcha v-if="meta.enableRecaptcha" class="captcha" provider="recaptcha" ref="recaptcha" v-model:value="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/>
- <MkButton type="submit" :disabled="shouldDisableSubmitting" primary data-cy-signup-submit>{{ $ts.start }}</MkButton>
+ <captcha v-if="meta.enableHcaptcha" class="_formBlock captcha" provider="hcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/>
+ <captcha v-if="meta.enableRecaptcha" class="_formBlock captcha" provider="recaptcha" ref="recaptcha" v-model="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/>
+ <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
</template>
</form>
</template>
@@ -57,8 +67,8 @@ const getPasswordStrength = require('syuilo-password-strength');
import { toUnicode } from 'punycode/';
import { host, url } from '@client/config';
import MkButton from './ui/button.vue';
-import MkInput from './ui/input.vue';
-import MkSwitch from './ui/switch.vue';
+import MkInput from './form/input.vue';
+import MkSwitch from './form/switch.vue';
import * as os from '@client/os';
import { login } from '@client/account';
@@ -87,8 +97,10 @@ export default defineComponent({
password: '',
retypedPassword: '',
invitationCode: '',
+ email: '',
url,
usernameState: null,
+ emailState: null,
passwordStrength: '',
passwordRetypeState: null,
submitting: false,
@@ -148,6 +160,23 @@ export default defineComponent({
});
},
+ onChangeEmail() {
+ if (this.email == '') {
+ this.emailState = null;
+ return;
+ }
+
+ this.emailState = 'wait';
+
+ os.api('email-address/available', {
+ emailAddress: this.email
+ }).then(result => {
+ this.emailState = result.available ? 'ok' : 'unavailable';
+ }).catch(err => {
+ this.emailState = 'error';
+ });
+ },
+
onChangePassword() {
if (this.password == '') {
this.passwordStrength = '';
@@ -174,20 +203,30 @@ export default defineComponent({
os.api('signup', {
username: this.username,
password: this.password,
+ emailAddress: this.email,
invitationCode: this.invitationCode,
'hcaptcha-response': this.hCaptchaResponse,
'g-recaptcha-response': this.reCaptchaResponse,
}).then(() => {
- return os.api('signin', {
- username: this.username,
- password: this.password
- }).then(res => {
- this.$emit('signup', res);
+ if (this.meta.emailRequiredForSignup) {
+ os.dialog({
+ type: 'success',
+ title: this.$ts._signup.almostThere,
+ text: this.$t('_signup.emailSent', { email: this.email }),
+ });
+ this.$emit('signupEmailPending');
+ } else {
+ os.api('signin', {
+ username: this.username,
+ password: this.password
+ }).then(res => {
+ this.$emit('signup', res);
- if (this.autoSet) {
- return login(res.i);
- }
- });
+ if (this.autoSet) {
+ login(res.i);
+ }
+ });
+ }
}).catch(() => {
this.submitting = false;
this.$refs.hcaptcha?.reset?.();
diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue
index 3902b7f98f..ce86af8f95 100644
--- a/src/client/components/tab.vue
+++ b/src/client/components/tab.vue
@@ -3,7 +3,7 @@ import { defineComponent, h, resolveDirective, withDirectives } from 'vue';
export default defineComponent({
props: {
- value: {
+ modelValue: {
required: true,
},
},
@@ -13,11 +13,11 @@ export default defineComponent({
return withDirectives(h('div', {
class: 'pxhvhrfw',
}, options.map(option => withDirectives(h('button', {
- class: ['_button', { active: this.value === option.props.value }],
+ class: ['_button', { active: this.modelValue === option.props.value }],
key: option.key,
- disabled: this.value === option.props.value,
+ disabled: this.modelValue === option.props.value,
onClick: () => {
- this.$emit('update:value', option.props.value);
+ this.$emit('update:modelValue', option.props.value);
}
}, option.children), [
[resolveDirective('click-anime')]
@@ -35,8 +35,8 @@ export default defineComponent({
> button {
flex: 1;
- padding: 15px 12px 12px 12px;
- border-bottom: solid 3px transparent;
+ padding: 10px 8px;
+ border-radius: 6px;
&:disabled {
opacity: 1 !important;
@@ -45,11 +45,16 @@ export default defineComponent({
&.active {
color: var(--accent);
- border-bottom-color: var(--accent);
+ background: var(--accentedBg);
}
&:not(.active):hover {
color: var(--fgHighlighted);
+ background: var(--panelHighlight);
+ }
+
+ &:not(:first-child) {
+ margin-left: 8px;
}
> .icon {
@@ -61,7 +66,7 @@ export default defineComponent({
font-size: 80%;
> button {
- padding: 11px 8px 8px 8px;
+ padding: 11px 8px;
}
}
}
diff --git a/src/client/components/taskmanager.api-window.vue b/src/client/components/taskmanager.api-window.vue
index c9b2c43413..807e4a0075 100644
--- a/src/client/components/taskmanager.api-window.vue
+++ b/src/client/components/taskmanager.api-window.vue
@@ -9,7 +9,7 @@
<template #header>Req Viewer</template>
<div class="rlkneywz">
- <MkTab v-model:value="tab" style="border-bottom: solid 0.5px var(--divider);">
+ <MkTab v-model="tab" style="border-bottom: solid 0.5px var(--divider);">
<option value="req">Request</option>
<option value="res">Response</option>
</MkTab>
diff --git a/src/client/components/taskmanager.vue b/src/client/components/taskmanager.vue
index cb8cb78748..6f3d1b0354 100644
--- a/src/client/components/taskmanager.vue
+++ b/src/client/components/taskmanager.vue
@@ -4,7 +4,7 @@
<i class="fas fa-terminal" style="margin-right: 0.5em;"></i>Task Manager
</template>
<div class="qljqmnzj _monospace">
- <MkTab v-model:value="tab" style="border-bottom: solid 0.5px var(--divider);">
+ <MkTab v-model="tab" style="border-bottom: solid 0.5px var(--divider);">
<option value="windows">Windows</option>
<option value="stream">Stream</option>
<option value="streamPool">Stream (Pool)</option>
diff --git a/src/client/components/token-generate-window.vue b/src/client/components/token-generate-window.vue
index fe61f61efa..86312564cc 100644
--- a/src/client/components/token-generate-window.vue
+++ b/src/client/components/token-generate-window.vue
@@ -31,9 +31,9 @@
import { defineComponent } from 'vue';
import { kinds } from '@/misc/api-permissions';
import XModalWindow from '@client/components/ui/modal-window.vue';
-import MkInput from './ui/input.vue';
-import MkTextarea from './ui/textarea.vue';
-import MkSwitch from './ui/switch.vue';
+import MkInput from './form/input.vue';
+import MkTextarea from './form/textarea.vue';
+import MkSwitch from './form/switch.vue';
import MkButton from './ui/button.vue';
import MkInfo from './ui/info.vue';
diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue
index d6ac42994f..b5f4547c84 100644
--- a/src/client/components/ui/button.vue
+++ b/src/client/components/ui/button.vue
@@ -1,7 +1,6 @@
<template>
-<component class="bghgjjyj _button"
- :is="link ? 'MkA' : 'button'"
- :class="{ inline, primary, danger, full }"
+<button v-if="!link" class="bghgjjyj _button"
+ :class="{ inline, primary, gradate, danger, rounded, full }"
:type="type"
@click="$emit('click', $event)"
@mousedown="onMousedown"
@@ -10,7 +9,17 @@
<div class="content">
<slot></slot>
</div>
-</component>
+</button>
+<MkA v-else class="bghgjjyj _button"
+ :class="{ inline, primary, gradate, danger, rounded, full }"
+ :to="to"
+ @mousedown="onMousedown"
+>
+ <div ref="ripples" class="ripples"></div>
+ <div class="content">
+ <slot></slot>
+ </div>
+</MkA>
</template>
<script lang="ts">
@@ -27,6 +36,16 @@ export default defineComponent({
required: false,
default: false
},
+ gradate: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ rounded: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
inline: {
type: Boolean,
required: false,
@@ -37,6 +56,10 @@ export default defineComponent({
required: false,
default: false
},
+ to: {
+ type: String,
+ required: false
+ },
autofocus: {
type: Boolean,
required: false,
@@ -119,13 +142,13 @@ export default defineComponent({
padding: 8px 14px;
text-align: center;
font-weight: normal;
- font-size: 0.9em;
- line-height: 24px;
+ font-size: 0.8em;
+ line-height: 22px;
box-shadow: none;
text-decoration: none;
background: var(--buttonBg);
- border-radius: 999px;
- overflow: hidden;
+ border-radius: 4px;
+ overflow: clip;
box-sizing: border-box;
transition: background 0.1s ease;
@@ -141,6 +164,10 @@ export default defineComponent({
width: 100%;
}
+ &.rounded {
+ border-radius: 999px;
+ }
+
&.primary {
font-weight: bold;
color: var(--fgOnAccent) !important;
@@ -155,6 +182,20 @@ export default defineComponent({
}
}
+ &.gradate {
+ font-weight: bold;
+ color: var(--fgOnAccent) !important;
+ background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+
+ &:not(:disabled):hover {
+ background: linear-gradient(90deg, var(--X8), var(--X8));
+ }
+
+ &:not(:disabled):active {
+ background: linear-gradient(90deg, var(--X8), var(--X8));
+ }
+ }
+
&.danger {
color: #ff2a2a;
@@ -176,19 +217,11 @@ export default defineComponent({
opacity: 0.7;
}
- &:focus {
+ &:focus-visible {
outline: solid 2px var(--focus);
outline-offset: 2px;
}
- &.inline + .bghgjjyj {
- margin-left: 12px;
- }
-
- &:not(.inline) + .bghgjjyj {
- margin-top: 16px;
- }
-
&.inline {
display: inline-block;
width: auto;
diff --git a/src/client/components/ui/folder.vue b/src/client/components/ui/folder.vue
index eecf1d8be1..d0616a57c1 100644
--- a/src/client/components/ui/folder.vue
+++ b/src/client/components/ui/folder.vue
@@ -1,6 +1,6 @@
<template>
<div class="ssazuxis" v-size="{ max: [500] }">
- <header @click="showBody = !showBody" class="_button">
+ <header @click="showBody = !showBody" class="_button" :style="{ background: bg }">
<div class="title"><slot name="header"></slot></div>
<div class="divider"></div>
<button class="_button">
@@ -23,6 +23,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
+import * as tinycolor from 'tinycolor2';
const localStoragePrefix = 'ui:folder:';
@@ -41,6 +42,7 @@ export default defineComponent({
},
data() {
return {
+ bg: null,
showBody: (this.persistKey && localStorage.getItem(localStoragePrefix + this.persistKey)) ? localStorage.getItem(localStoragePrefix + this.persistKey) === 't' : this.expanded,
};
},
@@ -51,6 +53,21 @@ export default defineComponent({
}
}
},
+ mounted() {
+ function getParentBg(el: Element | null): string {
+ if (el == null || el.tagName === 'BODY') return 'var(--bg)';
+ const bg = el.style.background || el.style.backgroundColor;
+ if (bg) {
+ return bg;
+ } else {
+ return getParentBg(el.parentElement);
+ }
+ }
+ const rawBg = getParentBg(this.$el);
+ const bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
+ bg.setAlpha(0.85);
+ this.bg = bg.toRgbString();
+ },
methods: {
toggleContent(show: boolean) {
this.showBody = show;
@@ -100,12 +117,8 @@ export default defineComponent({
position: sticky;
top: var(--stickyTop, 0px);
padding: var(--x-padding);
- background: var(--x-header, var(--panel));
- /* TODO panelの半透明バージョンをプログラマティックに作りたい
- background: var(--X17);
-webkit-backdrop-filter: var(--blur, blur(8px));
backdrop-filter: var(--blur, blur(20px));
- */
> .title {
margin: 0;
@@ -141,7 +154,7 @@ export default defineComponent({
}
}
-._flat_ .ssazuxis {
+._fitSide_ .ssazuxis {
> header {
padding: 0 16px;
}
diff --git a/src/client/components/ui/info.vue b/src/client/components/ui/info.vue
index 513682ef55..e16f2736f1 100644
--- a/src/client/components/ui/info.vue
+++ b/src/client/components/ui/info.vue
@@ -27,7 +27,6 @@ export default defineComponent({
<style lang="scss" scoped>
.fpezltsf {
- margin: 16px 0;
padding: 16px;
font-size: 90%;
background: var(--infoBg);
@@ -39,20 +38,12 @@ export default defineComponent({
color: var(--infoWarnFg);
}
- &:first-child {
- margin-top: 0;
- }
-
- &:last-child {
- margin-bottom: 0;
- }
-
> i {
margin-right: 4px;
}
}
-._flat_ .fpezltsf {
+._fitSide_ .fpezltsf {
border-radius: 0;
}
</style>
diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue
index 26b4b04b11..da24d90170 100644
--- a/src/client/components/ui/menu.vue
+++ b/src/client/components/ui/menu.vue
@@ -1,5 +1,5 @@
<template>
-<div class="rrevdjwt" :class="{ left: align === 'left', pointer: point === 'top' }"
+<div class="rrevdjwt" :class="{ center: align === 'center' }"
ref="items"
@contextmenu.self="e => e.preventDefault()"
v-hotkey="keymap"
@@ -27,7 +27,7 @@
<MkAvatar :user="item.user" class="avatar"/><MkUserName :user="item.user"/>
<span v-if="item.indicate" class="indicator"><i class="fas fa-circle"></i></span>
</button>
- <button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger }">
+ <button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active">
<i v-if="item.icon" class="fa-fw" :class="item.icon"></i>
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
@@ -59,10 +59,6 @@ export default defineComponent({
type: String,
requried: false
},
- point: {
- type: String,
- requried: false
- },
},
emits: ['close'],
data() {
@@ -145,68 +141,83 @@ export default defineComponent({
<style lang="scss" scoped>
.rrevdjwt {
padding: 8px 0;
+ min-width: 200px;
- &.pointer {
- &:before {
- --size: 8px;
- content: '';
- display: block;
- position: absolute;
- top: calc(0px - (var(--size) * 2));
- left: 0;
- right: 0;
- width: 0;
- margin: auto;
- border: solid var(--size) transparent;
- border-bottom-color: var(--popup);
- }
- }
-
- &.left {
+ &.center {
> .item {
- text-align: left;
+ text-align: center;
}
}
> .item {
display: block;
position: relative;
- padding: 8px 16px;
+ padding: 8px 18px;
width: 100%;
box-sizing: border-box;
white-space: nowrap;
font-size: 0.9em;
line-height: 20px;
- text-align: center;
+ text-align: left;
overflow: hidden;
text-overflow: ellipsis;
+ &:before {
+ content: "";
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ margin: auto;
+ width: calc(100% - 16px);
+ height: 100%;
+ border-radius: 6px;
+ }
+
+ > * {
+ position: relative;
+ }
+
&.danger {
color: #ff2a2a;
&:hover {
color: #fff;
- background: #ff4242;
+
+ &:before {
+ background: #ff4242;
+ }
}
&:active {
color: #fff;
- background: #d42e2e;
+
+ &:before {
+ background: #d42e2e;
+ }
}
}
- &:hover {
+ &.active {
color: var(--fgOnAccent);
- background: var(--accent);
- text-decoration: none;
+ opacity: 1;
+
+ &:before {
+ background: var(--accent);
+ }
}
- &:active {
- color: var(--fgOnAccent);
- background: var(--accentDarken);
+ &:not(:disabled):hover {
+ color: var(--accent);
+ text-decoration: none;
+
+ &:before {
+ background: var(--accentedBg);
+ }
}
- &:not(:active):focus {
+ &:not(:active):focus-visible {
box-shadow: 0 0 0 2px var(--focus) inset;
}
@@ -231,12 +242,12 @@ export default defineComponent({
}
> i {
- margin-right: 4px;
+ margin-right: 5px;
width: 20px;
}
> .avatar {
- margin-right: 4px;
+ margin-right: 5px;
width: 20px;
height: 20px;
}
diff --git a/src/client/components/ui/popup-menu.vue b/src/client/components/ui/popup-menu.vue
index 3590426172..23f7c89f3b 100644
--- a/src/client/components/ui/popup-menu.vue
+++ b/src/client/components/ui/popup-menu.vue
@@ -1,6 +1,6 @@
<template>
-<MkPopup ref="popup" :src="src" @closed="$emit('closed')" #default="{point}">
- <MkMenu :items="items" :align="align" :point="point" @close="$refs.popup.close()" class="_popup _shadow"/>
+<MkPopup ref="popup" :src="src" @closed="$emit('closed')">
+ <MkMenu :items="items" :align="align" @close="$refs.popup.close()" class="_popup _shadow"/>
</MkPopup>
</template>
diff --git a/src/client/components/ui/popup.vue b/src/client/components/ui/popup.vue
index 8497eedecb..0fb1780cc5 100644
--- a/src/client/components/ui/popup.vue
+++ b/src/client/components/ui/popup.vue
@@ -1,7 +1,7 @@
<template>
<transition :name="$store.state.animation ? 'popup-menu' : ''" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
<div v-show="manualShowing != null ? manualShowing : showing" class="ccczpooj" :class="{ front, fixed, top: position === 'top' }" ref="content" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
- <slot :point="point"></slot>
+ <slot></slot>
</div>
</transition>
</template>
@@ -52,7 +52,6 @@ export default defineComponent({
fixed: false,
transformOrigin: 'center',
contentClicking: false,
- point: null,
};
},
@@ -136,10 +135,8 @@ export default defineComponent({
}
if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) {
- this.point = 'top';
this.transformOrigin = 'center top';
} else {
- this.point = null;
this.transformOrigin = 'center';
}
diff --git a/src/client/components/ui/radios.vue b/src/client/components/ui/radios.vue
deleted file mode 100644
index 8a62b87683..0000000000
--- a/src/client/components/ui/radios.vue
+++ /dev/null
@@ -1,58 +0,0 @@
-<script lang="ts">
-import { defineComponent, h } from 'vue';
-import MkRadio from '@client/components/ui/radio.vue';
-
-export default defineComponent({
- components: {
- MkRadio
- },
- props: {
- modelValue: {
- required: false
- },
- },
- data() {
- return {
- value: this.modelValue,
- }
- },
- watch: {
- value() {
- this.$emit('update:modelValue', this.value);
- }
- },
- render() {
- const label = this.$slots.desc();
- let options = this.$slots.default();
-
- // なぜかFragmentになることがあるため
- if (options.length === 1 && options[0].props == null) options = options[0].children;
-
- return h('div', {
- class: 'novjtcto'
- }, [
- h('div', label),
- ...options.map(option => h(MkRadio, {
- key: option.key,
- value: option.props.value,
- modelValue: this.value,
- 'onUpdate:modelValue': value => this.value = value,
- }, option.children))
- ]);
- }
-});
-</script>
-
-<style lang="scss">
-.novjtcto {
- margin: 32px 0;
-
- &:first-child {
- margin-top: 0;
- }
-
- &:last-child {
- margin-bottom: 0;
- }
-}
-</style>
diff --git a/src/client/components/ui/range.vue b/src/client/components/ui/range.vue
deleted file mode 100644
index 4cfe66a8fc..0000000000
--- a/src/client/components/ui/range.vue
+++ /dev/null
@@ -1,139 +0,0 @@
-<template>
-<div class="timctyfi" :class="{ focused, disabled }">
- <div class="icon"><slot name="icon"></slot></div>
- <span class="label"><slot name="label"></slot></span>
- <input
- type="range"
- ref="input"
- v-model="v"
- :disabled="disabled"
- :min="min"
- :max="max"
- :step="step"
- :autofocus="autofocus"
- @focus="focused = true"
- @blur="focused = false"
- @input="$emit('update:value', $event.target.value)"
- />
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
- props: {
- value: {
- type: Number,
- required: false,
- default: 0
- },
- disabled: {
- type: Boolean,
- required: false,
- default: false
- },
- min: {
- type: Number,
- required: false,
- default: 0
- },
- max: {
- type: Number,
- required: false,
- default: 100
- },
- step: {
- type: Number,
- required: false,
- default: 1
- },
- autofocus: {
- type: Boolean,
- required: false
- }
- },
- data() {
- return {
- v: this.value,
- focused: false
- };
- },
- watch: {
- value(v) {
- this.v = parseFloat(v);
- }
- },
- mounted() {
- if (this.autofocus) {
- this.$nextTick(() => {
- this.$refs.input.focus();
- });
- }
- }
-});
-</script>
-
-<style lang="scss" scoped>
-.timctyfi {
- position: relative;
- margin: 8px;
-
- > .icon {
- display: inline-block;
- width: 24px;
- text-align: center;
- }
-
- > .title {
- pointer-events: none;
- font-size: 16px;
- color: var(--inputLabel);
- overflow: hidden;
- }
-
- > input {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background: var(--X10);
- height: 7px;
- margin: 0 8px;
- outline: 0;
- border: 0;
- border-radius: 7px;
-
- &.disabled {
- opacity: 0.6;
- cursor: not-allowed;
- }
-
- &::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- cursor: pointer;
- width: 20px;
- height: 20px;
- display: block;
- border-radius: 50%;
- border: none;
- background: var(--accent);
- box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
- box-sizing: content-box;
- }
-
- &::-moz-range-thumb {
- -moz-appearance: none;
- appearance: none;
- cursor: pointer;
- width: 20px;
- height: 20px;
- display: block;
- border-radius: 50%;
- border: none;
- background: var(--accent);
- box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
- }
- }
-}
-</style>
diff --git a/src/client/components/ui/select.vue b/src/client/components/ui/select.vue
deleted file mode 100644
index e9d43d8a64..0000000000
--- a/src/client/components/ui/select.vue
+++ /dev/null
@@ -1,262 +0,0 @@
-<template>
-<div class="vblkjoeq">
- <div class="label" @click="focus"><slot name="label"></slot></div>
- <div class="input" :class="{ inline, disabled, focused }">
- <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
- <select ref="inputEl"
- v-model="v"
- :disabled="disabled"
- :required="required"
- :readonly="readonly"
- :placeholder="placeholder"
- @focus="focused = true"
- @blur="focused = false"
- @input="onInput"
- >
- <slot></slot>
- </select>
- <div class="suffix" ref="suffixEl"><i class="fas fa-chevron-down"></i></div>
- </div>
- <div class="caption"><slot name="caption"></slot></div>
-
- <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
-import MkButton from './button.vue';
-
-export default defineComponent({
- components: {
- MkButton,
- },
-
- props: {
- modelValue: {
- required: true
- },
- required: {
- type: Boolean,
- required: false
- },
- readonly: {
- type: Boolean,
- required: false
- },
- disabled: {
- type: Boolean,
- required: false
- },
- placeholder: {
- type: String,
- required: false
- },
- autofocus: {
- type: Boolean,
- required: false,
- default: false
- },
- inline: {
- type: Boolean,
- required: false,
- default: false
- },
- manualSave: {
- type: Boolean,
- required: false,
- default: false
- },
- },
-
- emits: ['change', 'update:modelValue'],
-
- setup(props, context) {
- const { modelValue, autofocus } = toRefs(props);
- const v = ref(modelValue.value);
- const focused = ref(false);
- const changed = ref(false);
- const invalid = ref(false);
- const filled = computed(() => v.value !== '' && v.value != null);
- const inputEl = ref(null);
- const prefixEl = ref(null);
- const suffixEl = ref(null);
-
- const focus = () => inputEl.value.focus();
- const onInput = (ev) => {
- changed.value = true;
- context.emit('change', ev);
- };
-
- const updated = () => {
- changed.value = false;
- context.emit('update:modelValue', v.value);
- };
-
- watch(modelValue, newValue => {
- v.value = newValue;
- });
-
- watch(v, newValue => {
- if (!props.manualSave) {
- updated();
- }
-
- invalid.value = inputEl.value.validity.badInput;
- });
-
- onMounted(() => {
- nextTick(() => {
- if (autofocus.value) {
- focus();
- }
-
- // このコンポーネントが作成された時、非表示状態である場合がある
- // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
- const clock = setInterval(() => {
- if (prefixEl.value) {
- if (prefixEl.value.offsetWidth) {
- inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
- }
- }
- if (suffixEl.value) {
- if (suffixEl.value.offsetWidth) {
- inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
- }
- }
- }, 100);
-
- onUnmounted(() => {
- clearInterval(clock);
- });
- });
- });
-
- return {
- v,
- focused,
- invalid,
- changed,
- filled,
- inputEl,
- prefixEl,
- suffixEl,
- focus,
- onInput,
- updated,
- };
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.vblkjoeq {
- margin: 1.5em 0;
-
- > .label {
- font-size: 0.85em;
- padding: 0 0 8px 12px;
- user-select: none;
-
- &:empty {
- display: none;
- }
- }
-
- > .caption {
- font-size: 0.8em;
- padding: 8px 0 0 12px;
- color: var(--fgTransparentWeak);
-
- &:empty {
- display: none;
- }
- }
-
- > .input {
- $height: 42px;
- position: relative;
-
- > select {
- appearance: none;
- -webkit-appearance: none;
- display: block;
- height: $height;
- width: 100%;
- margin: 0;
- padding: 0 12px;
- font: inherit;
- font-weight: normal;
- font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--inputBorder);
- border-radius: 6px;
- outline: none;
- box-shadow: none;
- box-sizing: border-box;
- cursor: pointer;
- transition: border-color 0.1s ease-out;
-
- &:hover {
- border-color: var(--inputBorderHover);
- }
- }
-
- > .prefix,
- > .suffix {
- display: flex;
- align-items: center;
- position: absolute;
- z-index: 1;
- top: 0;
- padding: 0 12px;
- font-size: 1em;
- height: $height;
- pointer-events: none;
-
- &:empty {
- display: none;
- }
-
- > * {
- display: inline-block;
- min-width: 16px;
- max-width: 150px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
-
- > .prefix {
- left: 0;
- padding-right: 6px;
- }
-
- > .suffix {
- right: 0;
- padding-left: 6px;
- }
-
- &.inline {
- display: inline-block;
- margin: 0;
- }
-
- &.focused {
- > select {
- border-color: var(--accent);
- }
- }
-
- &.disabled {
- opacity: 0.7;
-
- &, * {
- cursor: not-allowed !important;
- }
- }
- }
-}
-</style>
diff --git a/src/client/components/ui/super-menu.vue b/src/client/components/ui/super-menu.vue
new file mode 100644
index 0000000000..35fc81550d
--- /dev/null
+++ b/src/client/components/ui/super-menu.vue
@@ -0,0 +1,151 @@
+<template>
+<div class="rrevdjwu" :class="{ grid }">
+ <div class="group" v-for="group in def">
+ <div class="title" v-if="group.title">{{ group.title }}</div>
+
+ <div class="items">
+ <template v-for="(item, i) in group.items">
+ <a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }">
+ <i v-if="item.icon" class="icon fa-fw" :class="item.icon"></i>
+ <span class="text">{{ item.text }}</span>
+ </a>
+ <button v-else-if="item.type === 'button'" @click="ev => item.action(ev)" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active">
+ <i v-if="item.icon" class="icon fa-fw" :class="item.icon"></i>
+ <span class="text">{{ item.text }}</span>
+ </button>
+ <MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }">
+ <i v-if="item.icon" class="icon fa-fw" :class="item.icon"></i>
+ <span class="text">{{ item.text }}</span>
+ </MkA>
+ </template>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, unref } from 'vue';
+
+export default defineComponent({
+ props: {
+ def: {
+ type: Array,
+ required: true
+ },
+ grid: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.rrevdjwu {
+ > .group {
+ & + .group {
+ margin-top: 16px;
+ padding-top: 16px;
+ border-top: solid 0.5px var(--divider);
+ }
+
+ margin-left: 16px;
+ margin-right: 16px;
+
+ > .title {
+ font-size: 0.9em;
+ opacity: 0.7;
+ margin: 0 0 8px 12px;
+ }
+
+ > .items {
+ > .item {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ box-sizing: border-box;
+ padding: 10px 16px 10px 8px;
+ border-radius: 9px;
+ font-size: 0.9em;
+
+ &:hover {
+ text-decoration: none;
+ background: var(--panelHighlight);
+ }
+
+ &.active {
+ color: var(--accent);
+ background: var(--accentedBg);
+ }
+
+ &.danger {
+ color: var(--error);
+ }
+
+ > .icon {
+ width: 32px;
+ margin-right: 2px;
+ flex-shrink: 0;
+ text-align: center;
+ opacity: 0.8;
+ }
+
+ > .text {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ padding-right: 12px;
+ }
+
+ }
+ }
+ }
+
+ &.grid {
+ > .group {
+ & + .group {
+ padding-top: 0;
+ border-top: none;
+ }
+
+ margin-left: 0;
+ margin-right: 0;
+
+ > .title {
+ font-size: 1em;
+ opacity: 0.7;
+ margin: 0 0 8px 16px;
+ }
+
+ > .items {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
+ grid-gap: 8px;
+ padding: 0 16px;
+
+ > .item {
+ flex-direction: column;
+ padding: 18px 16px 16px 16px;
+ background: var(--panel);
+ border-radius: 8px;
+ text-align: center;
+
+ > .icon {
+ display: block;
+ margin-right: 0;
+ margin-bottom: 12px;
+ font-size: 1.5em;
+ }
+
+ > .text {
+ padding-right: 0;
+ width: 100%;
+ font-size: 0.8em;
+ }
+ }
+ }
+ }
+ }
+}
+</style>
diff --git a/src/client/components/ui/switch.vue b/src/client/components/ui/switch.vue
deleted file mode 100644
index 7aa9c0619d..0000000000
--- a/src/client/components/ui/switch.vue
+++ /dev/null
@@ -1,144 +0,0 @@
-<template>
-<div
- class="ziffeoms"
- :class="{ disabled, checked }"
- role="switch"
- :aria-checked="checked"
- :aria-disabled="disabled"
- @click.prevent="toggle"
->
- <input
- type="checkbox"
- ref="input"
- :disabled="disabled"
- @keydown.enter="toggle"
- >
- <span class="button">
- <span></span>
- </span>
- <span class="label">
- <span><slot></slot></span>
- <p><slot name="caption"></slot></p>
- </span>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
- props: {
- modelValue: {
- type: Boolean,
- default: false
- },
- disabled: {
- type: Boolean,
- default: false
- }
- },
- computed: {
- checked(): boolean {
- return this.modelValue;
- }
- },
- methods: {
- toggle() {
- if (this.disabled) return;
- this.$emit('update:modelValue', !this.checked);
- }
- }
-});
-</script>
-
-<style lang="scss" scoped>
-.ziffeoms {
- position: relative;
- display: flex;
- margin: 32px 0;
- cursor: pointer;
- transition: all 0.3s;
-
- &:first-child {
- margin-top: 0;
- }
-
- &:last-child {
- margin-bottom: 0;
- }
-
- > * {
- user-select: none;
- }
-
- &.disabled {
- opacity: 0.6;
- cursor: not-allowed;
- }
-
- &.checked {
- > .button {
- background-color: var(--X10);
- border-color: var(--X10);
-
- > * {
- background-color: var(--accent);
- transform: translateX(14px);
- }
- }
- }
-
- > input {
- position: absolute;
- width: 0;
- height: 0;
- opacity: 0;
- margin: 0;
- }
-
- > .button {
- position: relative;
- display: inline-block;
- flex-shrink: 0;
- margin: 3px 0 0 0;
- width: 34px;
- height: 14px;
- background: var(--X6);
- outline: none;
- border-radius: 14px;
- transition: inherit;
-
- > * {
- position: absolute;
- top: -3px;
- left: 0;
- border-radius: 100%;
- transition: background-color 0.3s, transform 0.3s;
- width: 20px;
- height: 20px;
- background-color: #fff;
- box-shadow: 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12);
- }
- }
-
- > .label {
- margin-left: 8px;
- display: block;
- cursor: pointer;
- transition: inherit;
- color: var(--fg);
-
- > span {
- display: block;
- line-height: 20px;
- transition: inherit;
- }
-
- > p {
- margin: 0;
- color: var(--fgTransparentWeak);
- font-size: 90%;
- }
- }
-}
-</style>
diff --git a/src/client/components/ui/textarea.vue b/src/client/components/ui/textarea.vue
deleted file mode 100644
index 08ac3182a9..0000000000
--- a/src/client/components/ui/textarea.vue
+++ /dev/null
@@ -1,254 +0,0 @@
-<template>
-<div class="adhpbeos">
- <div class="label" @click="focus"><slot name="label"></slot></div>
- <div class="input" :class="{ disabled, focused, tall, pre }">
- <textarea ref="inputEl"
- :class="{ code, _monospace: code }"
- v-model="v"
- :disabled="disabled"
- :required="required"
- :readonly="readonly"
- :placeholder="placeholder"
- :pattern="pattern"
- :autocomplete="autocomplete"
- :spellcheck="spellcheck"
- @focus="focused = true"
- @blur="focused = false"
- @keydown="onKeydown($event)"
- @input="onInput"
- ></textarea>
- </div>
- <div class="caption"><slot name="caption"></slot></div>
-
- <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
-import MkButton from './button.vue';
-import { debounce } from 'throttle-debounce';
-
-export default defineComponent({
- components: {
- MkButton,
- },
-
- props: {
- modelValue: {
- required: true
- },
- type: {
- type: String,
- required: false
- },
- required: {
- type: Boolean,
- required: false
- },
- readonly: {
- type: Boolean,
- required: false
- },
- disabled: {
- type: Boolean,
- required: false
- },
- pattern: {
- type: String,
- required: false
- },
- placeholder: {
- type: String,
- required: false
- },
- autofocus: {
- type: Boolean,
- required: false,
- default: false
- },
- autocomplete: {
- required: false
- },
- spellcheck: {
- required: false
- },
- code: {
- type: Boolean,
- required: false
- },
- tall: {
- type: Boolean,
- required: false,
- default: false
- },
- pre: {
- type: Boolean,
- required: false,
- default: false
- },
- debounce: {
- type: Boolean,
- required: false,
- default: false
- },
- manualSave: {
- type: Boolean,
- required: false,
- default: false
- },
- },
-
- emits: ['change', 'keydown', 'enter', 'update:modelValue'],
-
- setup(props, context) {
- const { modelValue, autofocus } = toRefs(props);
- const v = ref(modelValue.value);
- const focused = ref(false);
- const changed = ref(false);
- const invalid = ref(false);
- const filled = computed(() => v.value !== '' && v.value != null);
- const inputEl = ref(null);
-
- const focus = () => inputEl.value.focus();
- const onInput = (ev) => {
- changed.value = true;
- context.emit('change', ev);
- };
- const onKeydown = (ev: KeyboardEvent) => {
- context.emit('keydown', ev);
-
- if (ev.code === 'Enter') {
- context.emit('enter');
- }
- };
-
- const updated = () => {
- changed.value = false;
- context.emit('update:modelValue', v.value);
- };
-
- const debouncedUpdated = debounce(1000, updated);
-
- watch(modelValue, newValue => {
- v.value = newValue;
- });
-
- watch(v, newValue => {
- if (!props.manualSave) {
- if (props.debounce) {
- debouncedUpdated();
- } else {
- updated();
- }
- }
-
- invalid.value = inputEl.value.validity.badInput;
- });
-
- onMounted(() => {
- nextTick(() => {
- if (autofocus.value) {
- focus();
- }
- });
- });
-
- return {
- v,
- focused,
- invalid,
- changed,
- filled,
- inputEl,
- focus,
- onInput,
- onKeydown,
- updated,
- };
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.adhpbeos {
- margin: 1.5em 0;
-
- > .label {
- font-size: 0.85em;
- padding: 0 0 8px 12px;
- user-select: none;
-
- &:empty {
- display: none;
- }
- }
-
- > .caption {
- font-size: 0.8em;
- padding: 8px 0 0 12px;
- color: var(--fgTransparentWeak);
-
- &:empty {
- display: none;
- }
- }
-
- > .input {
- position: relative;
-
- > textarea {
- appearance: none;
- -webkit-appearance: none;
- display: block;
- width: 100%;
- min-width: 100%;
- max-width: 100%;
- min-height: 130px;
- margin: 0;
- padding: 12px;
- font: inherit;
- font-weight: normal;
- font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 0.5px var(--inputBorder);
- border-radius: 6px;
- outline: none;
- box-shadow: none;
- box-sizing: border-box;
- transition: border-color 0.1s ease-out;
-
- &:hover {
- border-color: var(--inputBorderHover);
- }
- }
-
- &.focused {
- > textarea {
- border-color: var(--accent);
- }
- }
-
- &.disabled {
- opacity: 0.7;
-
- &, * {
- cursor: not-allowed !important;
- }
- }
-
- &.tall {
- > textarea {
- min-height: 200px;
- }
- }
-
- &.pre {
- > textarea {
- white-space: pre;
- }
- }
- }
-}
-</style>
diff --git a/src/client/components/ui/window.vue b/src/client/components/ui/window.vue
index 773c3b9b13..00284b0467 100644
--- a/src/client/components/ui/window.vue
+++ b/src/client/components/ui/window.vue
@@ -3,11 +3,16 @@
<div class="ebkgocck" :class="{ front }" v-if="showing">
<div class="body _window _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown">
<div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu">
- <button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button>
-
+ <span class="left">
+ <slot name="headerLeft"></slot>
+ </span>
<span class="title" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
<slot name="header"></slot>
</span>
+ <span class="right">
+ <slot name="headerRight"></slot>
+ <button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button>
+ </span>
</div>
<div class="body" v-if="padding">
<div class="_section">
@@ -377,7 +382,7 @@ export default defineComponent({
<style lang="scss" scoped>
.window-enter-active, .window-leave-active {
- transition: opacity 0.3s, transform 0.3s !important;
+ transition: opacity 0.2s, transform 0.2s !important;
}
.window-enter-from, .window-leave-to {
pointer-events: none;
@@ -418,12 +423,14 @@ export default defineComponent({
height: var(--height);
border-bottom: solid 1px var(--divider);
- > ::v-deep(button) {
- height: var(--height);
- width: var(--height);
+ > .left, > .right {
+ > ::v-deep(button) {
+ height: var(--height);
+ width: var(--height);
- &:hover {
- color: var(--fgHighlighted);
+ &:hover {
+ color: var(--fgHighlighted);
+ }
}
}
diff --git a/src/client/components/user-select-dialog.vue b/src/client/components/user-select-dialog.vue
index 87c32dab25..0f3ee2a126 100644
--- a/src/client/components/user-select-dialog.vue
+++ b/src/client/components/user-select-dialog.vue
@@ -10,7 +10,7 @@
<template #header>{{ $ts.selectUser }}</template>
<div class="tbhwbxda _monolithic_">
<div class="_section">
- <div class="_inputSplit _inputNoTopMargin _inputNoBottomMargin">
+ <div class="_inputSplit">
<MkInput v-model="username" class="input" @update:modelValue="search" ref="username">
<template #label>{{ $ts.username }}</template>
<template #prefix>@</template>
@@ -52,7 +52,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import MkInput from './ui/input.vue';
+import MkInput from './form/input.vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
import * as os from '@client/os';
diff --git a/src/client/components/widgets.vue b/src/client/components/widgets.vue
index 150d61c027..aef5de453c 100644
--- a/src/client/components/widgets.vue
+++ b/src/client/components/widgets.vue
@@ -30,7 +30,7 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';
import { v4 as uuid } from 'uuid';
-import MkSelect from '@client/components/ui/select.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkButton from '@client/components/ui/button.vue';
import { widgets as widgetDefs } from '@client/widgets';
diff --git a/src/client/directives/click-anime.ts b/src/client/directives/click-anime.ts
index 9fd583d6dd..0d5a6da94e 100644
--- a/src/client/directives/click-anime.ts
+++ b/src/client/directives/click-anime.ts
@@ -1,7 +1,10 @@
import { Directive } from 'vue';
+import { defaultStore } from '@client/store';
export default {
mounted(el, binding, vn) {
+ if (!defaultStore.state.animation) return;
+
el.classList.add('_anime_bounce_standBy');
el.addEventListener('mousedown', () => {
diff --git a/src/client/directives/get-size.ts b/src/client/directives/get-size.ts
new file mode 100644
index 0000000000..e3b5dea0f3
--- /dev/null
+++ b/src/client/directives/get-size.ts
@@ -0,0 +1,34 @@
+import { Directive } from 'vue';
+
+export default {
+ mounted(src, binding, vn) {
+ const calc = () => {
+ const height = src.clientHeight;
+ const width = src.clientWidth;
+
+ // 要素が(一時的に)DOMに存在しないときは計算スキップ
+ if (height === 0) return;
+
+ binding.value(width, height);
+ };
+
+ calc();
+
+ // Vue3では使えなくなった
+ // 無くても大丈夫か...?
+ // TODO: ↑大丈夫じゃなかったので解決策を探す
+ //vn.context.$on('hook:activated', calc);
+
+ const ro = new ResizeObserver((entries, observer) => {
+ calc();
+ });
+ ro.observe(src);
+
+ src._get_size_ro_ = ro;
+ },
+
+ unmounted(src, binding, vn) {
+ binding.value(0, 0);
+ src._get_size_ro_.unobserve(src);
+ }
+} as Directive;
diff --git a/src/client/directives/index.ts b/src/client/directives/index.ts
index f0a0123771..cd71bc26d3 100644
--- a/src/client/directives/index.ts
+++ b/src/client/directives/index.ts
@@ -2,6 +2,7 @@ import { App } from 'vue';
import userPreview from './user-preview';
import size from './size';
+import getSize from './get-size';
import particle from './particle';
import tooltip from './tooltip';
import hotkey from './hotkey';
@@ -14,6 +15,7 @@ export default function(app: App) {
app.directive('userPreview', userPreview);
app.directive('user-preview', userPreview);
app.directive('size', size);
+ app.directive('get-size', getSize);
app.directive('particle', particle);
app.directive('tooltip', tooltip);
app.directive('hotkey', hotkey);
diff --git a/src/client/directives/tooltip.ts b/src/client/directives/tooltip.ts
index ee690558af..32d137b2e2 100644
--- a/src/client/directives/tooltip.ts
+++ b/src/client/directives/tooltip.ts
@@ -36,7 +36,7 @@ export default {
});
}
- const show = e => {
+ self.show = () => {
if (!document.body.contains(el)) return;
if (self._close) return;
if (self.text == null) return;
@@ -60,7 +60,7 @@ export default {
el.addEventListener(start, () => {
clearTimeout(self.showTimer);
clearTimeout(self.hideTimer);
- self.showTimer = setTimeout(show, delay);
+ self.showTimer = setTimeout(self.show, delay);
}, { passive: true });
el.addEventListener(end, () => {
@@ -75,6 +75,11 @@ export default {
});
},
+ updated(el, binding) {
+ const self = el._tooltipDirective_;
+ self.text = binding.value as string;
+ },
+
unmounted(el, binding, vn) {
const self = el._tooltipDirective_;
clearInterval(self.checkTimer);
diff --git a/src/client/events.ts b/src/client/events.ts
new file mode 100644
index 0000000000..dbbd908b8f
--- /dev/null
+++ b/src/client/events.ts
@@ -0,0 +1,4 @@
+import { EventEmitter } from 'eventemitter3';
+
+// TODO: 型付け
+export const globalEvents = new EventEmitter();
diff --git a/src/client/pages/_error_.vue b/src/client/pages/_error_.vue
index 1d67d9b14d..d1cefad8dd 100644
--- a/src/client/pages/_error_.vue
+++ b/src/client/pages/_error_.vue
@@ -1,9 +1,16 @@
<template>
+<MkLoading v-if="!loaded" />
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
- <div class="mjndxjch">
+ <div class="mjndxjch" v-show="loaded">
<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
<p><b><i class="fas fa-exclamation-triangle"></i> {{ $ts.pageLoadError }}</b></p>
- <p>{{ $ts.pageLoadErrorDescription }}</p>
+ <p v-if="version === meta.version">{{ $ts.pageLoadErrorDescription }}</p>
+ <p v-else-if="serverIsDead">{{ $ts.serverIsDead }}</p>
+ <template v-else>
+ <p>{{ $ts.newVersionOfClientAvailable }}</p>
+ <p>{{ $ts.youShouldUpgradeClient }}</p>
+ <MkButton @click="reload" class="button primary">{{ $ts.reload }}</MkButton>
+ </template>
<p><MkA to="/docs/general/troubleshooting" class="_link">{{ $ts.troubleshooting }}</MkA></p>
<p v-if="error" class="error">ERROR: {{ error }}</p>
</div>
@@ -14,6 +21,9 @@
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
import * as symbols from '@client/symbols';
+import { version } from '@client/config';
+import * as os from '@client/os';
+import { unisonReload } from '@client/scripts/unison-reload';
export default defineComponent({
components: {
@@ -30,8 +40,30 @@ export default defineComponent({
title: this.$ts.error,
icon: 'fas fa-exclamation-triangle'
},
+ loaded: false,
+ serverIsDead: false,
+ meta: {} as any,
+ version,
};
},
+ created() {
+ os.api('meta', {
+ detail: false
+ }).then(meta => {
+ this.loaded = true;
+ this.serverIsDead = false;
+ this.meta = meta;
+ localStorage.setItem('v', meta.version);
+ }, () => {
+ this.loaded = true;
+ this.serverIsDead = true;
+ });
+ },
+ methods: {
+ reload() {
+ unisonReload();
+ },
+ },
});
</script>
@@ -45,7 +77,7 @@ export default defineComponent({
}
> .button {
- margin: 0 auto;
+ margin: 8px auto;
}
> img {
diff --git a/src/client/pages/about-misskey.vue b/src/client/pages/about-misskey.vue
index 384c7e8ccb..d2c0ec0550 100644
--- a/src/client/pages/about-misskey.vue
+++ b/src/client/pages/about-misskey.vue
@@ -2,15 +2,15 @@
<div style="overflow: clip;">
<FormBase class="znqjceqz">
<div id="debug"></div>
- <section class="_formItem about">
- <div class="_formPanel panel" :class="{ playing: easterEggEngine != null }" ref="about">
+ <section class="_debobigegoItem about">
+ <div class="_debobigegoPanel panel" :class="{ playing: easterEggEngine != null }" ref="about">
<img src="/static-assets/client/about-icon.png" alt="" class="icon" @load="iconLoaded" draggable="false" @click="gravity"/>
<div class="misskey">Misskey</div>
<div class="version">v{{ version }}</div>
<span class="emoji" v-for="emoji in easterEggEmojis" :key="emoji.id" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span>
</div>
</section>
- <section class="_formItem" style="text-align: center; padding: 0 16px;">
+ <section class="_debobigegoItem" style="text-align: center; padding: 0 16px;">
{{ $ts._aboutMisskey.about }}<br><MkA class="_link" to="/docs/general/misskey">{{ $ts.learnMore }}</MkA>
</section>
<FormGroup>
@@ -55,10 +55,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { version } from '@client/config';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
import MkLink from '@client/components/link.vue';
import { physics } from '@client/scripts/physics';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/about.vue b/src/client/pages/about.vue
index bdd4c78827..2c580c293a 100644
--- a/src/client/pages/about.vue
+++ b/src/client/pages/about.vue
@@ -1,7 +1,7 @@
<template>
<FormBase>
- <div class="_formItem">
- <div class="_formPanel fwhjspax">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoPanel fwhjspax">
<img :src="$instance.iconUrl || $instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/>
<span class="name">{{ $instance.name || host }}</span>
</div>
@@ -59,12 +59,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { version, instanceName } from '@client/config';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import number from '@client/filters/number';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/advanced-theme-editor.vue b/src/client/pages/advanced-theme-editor.vue
index c03d88b82d..8a63d74887 100644
--- a/src/client/pages/advanced-theme-editor.vue
+++ b/src/client/pages/advanced-theme-editor.vue
@@ -4,7 +4,7 @@
<div class="_content">
<details>
<summary>{{ $ts.import }}</summary>
- <MkTextarea v-model:value="themeToImport">
+ <MkTextarea v-model="themeToImport">
{{ $ts._theme.importInfo }}
</MkTextarea>
<MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $ts.import }}</MkButton>
@@ -14,9 +14,9 @@
<section class="_section">
<div class="_content _card _gap">
<div class="_content">
- <MkInput v-model:value="name" required><span>{{ $ts.name }}</span></MkInput>
- <MkInput v-model:value="author" required><span>{{ $ts.author }}</span></MkInput>
- <MkTextarea v-model:value="description"><span>{{ $ts.description }}</span></MkTextarea>
+ <MkInput v-model="name" required><span>{{ $ts.name }}</span></MkInput>
+ <MkInput v-model="author" required><span>{{ $ts.author }}</span></MkInput>
+ <MkTextarea v-model="description"><span>{{ $ts.description }}</span></MkTextarea>
<div class="_inputs">
<div v-text="$ts._theme.base" />
<MkRadio v-model="baseTheme" value="light">{{ $ts.light }}</MkRadio>
@@ -41,31 +41,31 @@
<!-- color -->
<div v-else-if="typeof v === 'string'" class="color">
<input type="color" :value="v" @input="colorChanged($event.target.value, i)"/>
- <MkInput class="select" :value="v" @update:value="colorChanged($event, i)"/>
+ <MkInput class="select" :value="v" @update:modelValue="colorChanged($event, i)"/>
</div>
<!-- ref const -->
- <MkInput v-else-if="v.type === 'refConst'" v-model:value="v.key">
+ <MkInput v-else-if="v.type === 'refConst'" v-model="v.key">
<template #prefix>$</template>
<span>{{ $ts.name }}</span>
</MkInput>
<!-- ref props -->
- <MkSelect class="select" v-else-if="v.type === 'refProp'" v-model:value="v.key">
+ <MkSelect class="select" v-else-if="v.type === 'refProp'" v-model="v.key">
<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
</MkSelect>
<!-- func -->
<template v-else-if="v.type === 'func'">
- <MkSelect class="select" v-model:value="v.name">
+ <MkSelect class="select" v-model="v.name">
<template #label>{{ $ts._theme.funcKind }}</template>
<option v-for="n in ['alpha', 'darken', 'lighten']" :value="n" :key="n">{{ $t('_theme.' + n) }}</option>
</MkSelect>
- <MkInput type="number" v-model:value="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput>
- <MkSelect class="select" v-model:value="v.value">
+ <MkInput type="number" v-model="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput>
+ <MkSelect class="select" v-model="v.value">
<template #label>{{ $ts._theme.basedProp }}</template>
<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
</MkSelect>
</template>
<!-- CSS -->
- <MkInput v-else-if="v.type === 'css'" v-model:value="v.value">
+ <MkInput v-else-if="v.type === 'css'" v-model="v.value">
<span>CSS</span>
</MkInput>
</div>
@@ -95,11 +95,11 @@ import { defineComponent } from 'vue';
import * as JSON5 from 'json5';
import { toUnicode } from 'punycode/';
-import MkRadio from '@client/components/ui/radio.vue';
+import MkRadio from '@client/components/form/radio.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkSelect from '@client/components/ui/select.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkSample from '@client/components/sample.vue';
import { convertToMisskeyTheme, ThemeValue, convertToViewModel, ThemeViewModel } from '@client/scripts/theme-editor';
diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue
index a7ccb03588..6a0cbd67ba 100644
--- a/src/client/pages/announcements.vue
+++ b/src/client/pages/announcements.vue
@@ -1,17 +1,20 @@
<template>
-<div class="_section">
- <MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content">
- <section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id">
- <div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
- <div class="_content">
- <Mfm :text="announcement.text"/>
- <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
- </div>
- <div class="_footer" v-if="$i && !announcement.isRead">
- <MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton>
- </div>
- </section>
- </MkPagination>
+<div>
+ <MkHeader :info="header"/>
+ <div class="_section">
+ <MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content">
+ <section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id">
+ <div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
+ <div class="_content">
+ <Mfm :text="announcement.text"/>
+ <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
+ </div>
+ <div class="_footer" v-if="$i && !announcement.isRead">
+ <MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton>
+ </div>
+ </section>
+ </MkPagination>
+ </div>
</div>
</template>
@@ -32,7 +35,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.announcements,
- icon: 'fas fa-broadcast-tower'
+ icon: 'fas fa-broadcast-tower',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.announcements,
+ icon: 'fas fa-broadcast-tower',
+ bg: 'var(--bg)',
},
pagination: {
endpoint: 'announcements',
diff --git a/src/client/pages/antenna-timeline.vue b/src/client/pages/antenna-timeline.vue
index 425bec6987..c99124dbdc 100644
--- a/src/client/pages/antenna-timeline.vue
+++ b/src/client/pages/antenna-timeline.vue
@@ -89,7 +89,7 @@ export default defineComponent({
},
top() {
- scroll(this.$el, 0);
+ scroll(this.$el, { top: 0 });
},
async timetravel() {
diff --git a/src/client/pages/api-console.vue b/src/client/pages/api-console.vue
index c6d459fd6d..9aa7d4ea4d 100644
--- a/src/client/pages/api-console.vue
+++ b/src/client/pages/api-console.vue
@@ -1,7 +1,7 @@
<template>
<div class="_root">
<div class="_block" style="padding: 24px;">
- <MkInput v-model="endpoint" :datalist="endpoints" @update:modelValue="onEndpointChange()" class="_inputNoTopMargin">
+ <MkInput v-model="endpoint" :datalist="endpoints" @update:modelValue="onEndpointChange()" class="">
<template #label>Endpoint</template>
</MkInput>
<MkTextarea v-model="body" code>
@@ -27,9 +27,9 @@
import { defineComponent } from 'vue';
import * as JSON5 from 'json5';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue
index eeea0b70aa..67e27896ce 100644
--- a/src/client/pages/channel-editor.vue
+++ b/src/client/pages/channel-editor.vue
@@ -27,9 +27,9 @@
<script lang="ts">
import { computed, defineComponent } from 'vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import { selectFile } from '@client/scripts/select-file';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/channels.vue b/src/client/pages/channels.vue
index 7e3302959b..fd1408c253 100644
--- a/src/client/pages/channels.vue
+++ b/src/client/pages/channels.vue
@@ -1,7 +1,7 @@
<template>
<div>
<div class="_section" style="padding: 0;" v-if="$i">
- <MkTab class="_content" v-model:value="tab">
+ <MkTab class="_content" v-model="tab">
<option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._channel.featured }}</option>
<option value="following"><i class="fas fa-heart"></i> {{ $ts._channel.following }}</option>
<option value="owned"><i class="fas fa-edit"></i> {{ $ts._channel.owned }}</option>
diff --git a/src/client/pages/docs.vue b/src/client/pages/docs.vue
index be4d4255db..629dc2be53 100644
--- a/src/client/pages/docs.vue
+++ b/src/client/pages/docs.vue
@@ -2,7 +2,7 @@
<div class="vtaihdtm">
<div class="body">
<div class="search">
- <MkInput v-model="query" :debounce="true" type="search" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search">
+ <MkInput v-model="query" :debounce="true" type="search" class="" :placeholder="$ts.search">
<template #prefix><i class="fas fa-search"></i></template>
</MkInput>
</div>
@@ -57,7 +57,7 @@ import { defineComponent } from 'vue';
import { url, lang } from '@client/config';
import * as symbols from '@client/symbols';
import MkFolder from '@client/components/ui/folder.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
export default defineComponent({
components: {
diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue
index 091c3f20a9..e725bcb31f 100644
--- a/src/client/pages/emojis.category.vue
+++ b/src/client/pages/emojis.category.vue
@@ -1,13 +1,15 @@
<template>
<div class="driuhtrh">
<div class="query">
- <MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search">
+ <MkInput v-model="q" class="" :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">
@@ -29,8 +31,8 @@
<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 MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkFolder from '@client/components/ui/folder.vue';
import MkTab from '@client/components/tab.vue';
import * as os from '@client/os';
@@ -120,7 +122,6 @@ export default defineComponent({
}
> .emojis {
- --x-header: var(--bg);
--x-padding: 0 16px;
.zuvgdzyt {
diff --git a/src/client/pages/emojis.emoji.vue b/src/client/pages/emojis.emoji.vue
index 3c9bb4debe..ca0ef2dbb7 100644
--- a/src/client/pages/emojis.emoji.vue
+++ b/src/client/pages/emojis.emoji.vue
@@ -23,12 +23,14 @@ export default defineComponent({
},
mounted() {
- VanillaTilt.init(this.$el, {
- reverse: true,
- gyroscope: false,
- scale: 1.1,
- speed: 500,
- });
+ if (this.$store.animation) {
+ VanillaTilt.init(this.$el, {
+ reverse: true,
+ gyroscope: false,
+ scale: 1.1,
+ speed: 500,
+ });
+ }
},
methods: {
diff --git a/src/client/pages/emojis.vue b/src/client/pages/emojis.vue
index 8918de2338..d61fd25d3c 100644
--- a/src/client/pages/emojis.vue
+++ b/src/client/pages/emojis.vue
@@ -1,6 +1,9 @@
<template>
-<div :class="$style.root">
- <XCategory v-if="tab === 'category'"/>
+<div>
+ <MkHeader :info="header"/>
+ <div :class="$style.root">
+ <XCategory v-if="tab === 'category'"/>
+ </div>
</div>
</template>
@@ -22,6 +25,11 @@ export default defineComponent({
icon: 'fas fa-laugh',
bg: 'var(--bg)',
})),
+ header: computed(() => ({
+ title: this.$ts.customEmojis,
+ icon: 'fas fa-laugh',
+ bg: 'var(--bg)',
+ })),
tab: 'category',
}
},
diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue
index 7054940a1a..2ca0668611 100644
--- a/src/client/pages/explore.vue
+++ b/src/client/pages/explore.vue
@@ -1,73 +1,80 @@
<template>
-<div class="lznhrdub _root">
- <div>
- <div class="_isolated">
- <MkInput v-model="query" :debounce="true" type="search">
- <template #prefix><i class="fas fa-search"></i></template>
- <template #label>{{ $ts.searchUser }}</template>
- </MkInput>
- </div>
+<div>
+ <MkHeader :info="header"/>
- <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/>
+ <MkSpacer :content-max="1200">
+ <div class="lznhrdub">
+ <div v-if="tab === 'local'">
+ <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
+ <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header>
+ <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div>
+ </div>
- <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
- <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header>
- <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div>
- </div>
+ <template v-if="tag == null">
+ <MkFolder class="_gap" persist-key="explore-pinned-users">
+ <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template>
+ <XUserList :pagination="pinnedUsers"/>
+ </MkFolder>
+ <MkFolder class="_gap" persist-key="explore-popular-users">
+ <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
+ <XUserList :pagination="popularUsers"/>
+ </MkFolder>
+ <MkFolder class="_gap" persist-key="explore-recently-updated-users">
+ <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
+ <XUserList :pagination="recentlyUpdatedUsers"/>
+ </MkFolder>
+ <MkFolder class="_gap" persist-key="explore-recently-registered-users">
+ <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template>
+ <XUserList :pagination="recentlyRegisteredUsers"/>
+ </MkFolder>
+ </template>
+ </div>
+ <div v-else-if="tab === 'remote'">
+ <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }">
+ <header><span>{{ $ts.exploreFediverse }}</span></header>
+ </div>
- <template v-if="tag == null">
- <MkFolder class="_gap" persist-key="explore-pinned-users">
- <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template>
- <XUserList :pagination="pinnedUsers"/>
- </MkFolder>
- <MkFolder class="_gap" persist-key="explore-popular-users">
- <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
- <XUserList :pagination="popularUsers"/>
- </MkFolder>
- <MkFolder class="_gap" persist-key="explore-recently-updated-users">
- <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
- <XUserList :pagination="recentlyUpdatedUsers"/>
- </MkFolder>
- <MkFolder class="_gap" persist-key="explore-recently-registered-users">
- <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template>
- <XUserList :pagination="recentlyRegisteredUsers"/>
- </MkFolder>
- </template>
- </div>
- <div>
- <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }">
- <header><span>{{ $ts.exploreFediverse }}</span></header>
- </div>
+ <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap">
+ <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template>
- <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap">
- <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template>
+ <div class="vxjfqztj">
+ <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA>
+ <MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA>
+ </div>
+ </MkFolder>
- <div class="vxjfqztj">
- <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA>
- <MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA>
- </div>
- </MkFolder>
+ <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap">
+ <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
+ <XUserList :pagination="tagUsers"/>
+ </MkFolder>
- <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap">
- <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
- <XUserList :pagination="tagUsers"/>
- </MkFolder>
+ <template v-if="tag == null">
+ <MkFolder class="_gap">
+ <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
+ <XUserList :pagination="popularUsersF"/>
+ </MkFolder>
+ <MkFolder class="_gap">
+ <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
+ <XUserList :pagination="recentlyUpdatedUsersF"/>
+ </MkFolder>
+ <MkFolder class="_gap">
+ <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template>
+ <XUserList :pagination="recentlyRegisteredUsersF"/>
+ </MkFolder>
+ </template>
+ </div>
+ <div v-else-if="tab === 'search'">
+ <div class="_isolated">
+ <MkInput v-model="query" :debounce="true" type="search">
+ <template #prefix><i class="fas fa-search"></i></template>
+ <template #label>{{ $ts.searchUser }}</template>
+ </MkInput>
+ </div>
- <template v-if="tag == null">
- <MkFolder class="_gap">
- <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
- <XUserList :pagination="popularUsersF"/>
- </MkFolder>
- <MkFolder class="_gap">
- <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
- <XUserList :pagination="recentlyUpdatedUsersF"/>
- </MkFolder>
- <MkFolder class="_gap">
- <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template>
- <XUserList :pagination="recentlyRegisteredUsersF"/>
- </MkFolder>
- </template>
- </div>
+ <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/>
+ </div>
+ </div>
+ </MkSpacer>
</div>
</template>
@@ -75,7 +82,7 @@
import { computed, defineComponent } from 'vue';
import XUserList from '@client/components/user-list.vue';
import MkFolder from '@client/components/ui/folder.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import number from '@client/filters/number';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -98,8 +105,28 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.explore,
- icon: 'fas fa-hashtag'
+ icon: 'fas fa-hashtag',
+ bg: 'var(--bg)',
},
+ tab: 'local',
+ header: computed(() => ({
+ title: this.$ts.explore,
+ icon: 'fas fa-hashtag',
+ bg: 'var(--bg)',
+ tabs: [{
+ active: this.tab === 'local',
+ title: this.$ts.local,
+ onClick: () => { this.tab = 'local'; },
+ }, {
+ active: this.tab === 'remote',
+ title: this.$ts.remote,
+ onClick: () => { this.tab = 'remote'; },
+ }, {
+ active: this.tab === 'search',
+ title: this.$ts.search,
+ onClick: () => { this.tab = 'search'; },
+ },]
+ })),
pinnedUsers: { endpoint: 'pinned-users' },
popularUsers: { endpoint: 'users', limit: 10, noPaging: true, params: {
state: 'alive',
@@ -189,11 +216,6 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
-.lznhrdub {
- max-width: 1400px;
- margin: 0 auto;
-}
-
.localfedi7 {
color: #fff;
padding: 16px;
diff --git a/src/client/pages/favorites.vue b/src/client/pages/favorites.vue
index f13723c2d1..bed78d1dbe 100644
--- a/src/client/pages/favorites.vue
+++ b/src/client/pages/favorites.vue
@@ -1,7 +1,10 @@
<template>
-<div class="jmelgwjh">
- <div class="body">
- <XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/>
+<div>
+ <MkHeader :info="header"/>
+ <div class="jmelgwjh">
+ <div class="body">
+ <XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/>
+ </div>
</div>
</div>
</template>
@@ -25,6 +28,11 @@ export default defineComponent({
icon: 'fas fa-star',
bg: 'var(--bg)',
},
+ header: {
+ title: this.$ts.favorites,
+ icon: 'fas fa-star',
+ bg: 'var(--bg)',
+ },
pagination: {
endpoint: 'i/favorites',
limit: 10,
diff --git a/src/client/pages/featured.vue b/src/client/pages/featured.vue
index 21818ba617..5d8da54541 100644
--- a/src/client/pages/featured.vue
+++ b/src/client/pages/featured.vue
@@ -1,6 +1,9 @@
<template>
-<div class="_section">
- <XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/>
+<div>
+ <MkHeader :info="header"/>
+ <div class="_section">
+ <XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/>
+ </div>
</div>
</template>
@@ -19,12 +22,18 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.featured,
- icon: 'fas fa-fire-alt'
+ icon: 'fas fa-fire-alt',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.featured,
+ icon: 'fas fa-fire-alt',
+ bg: 'var(--bg)',
},
pagination: {
endpoint: 'notes/featured',
limit: 10,
- offsetMode: true
+ offsetMode: true,
},
};
},
diff --git a/src/client/pages/federation.vue b/src/client/pages/federation.vue
index 2afe70eea6..ae0aed4cc7 100644
--- a/src/client/pages/federation.vue
+++ b/src/client/pages/federation.vue
@@ -1,103 +1,106 @@
<template>
-<div class="taeiyria">
- <div class="query">
- <MkInput v-model="host" :debounce="true" class="_inputNoTopMargin">
- <template #prefix><i class="fas fa-search"></i></template>
- <template #label>{{ $ts.host }}</template>
- </MkInput>
- <div class="_inputSplit _inputNoBottomMargin">
- <MkSelect v-model="state">
- <template #label>{{ $ts.state }}</template>
- <option value="all">{{ $ts.all }}</option>
- <option value="federating">{{ $ts.federating }}</option>
- <option value="subscribing">{{ $ts.subscribing }}</option>
- <option value="publishing">{{ $ts.publishing }}</option>
- <option value="suspended">{{ $ts.suspended }}</option>
- <option value="blocked">{{ $ts.blocked }}</option>
- <option value="notResponding">{{ $ts.notResponding }}</option>
- </MkSelect>
- <MkSelect v-model="sort">
- <template #label>{{ $ts.sort }}</template>
- <option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option>
- <option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option>
- <option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option>
- <option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option>
- <option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option>
- <option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option>
- <option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option>
- <option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option>
- <option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option>
- <option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option>
- <option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option>
- <option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option>
- <option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option>
- <option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option>
- <option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option>
- <option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option>
- <option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
- <option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
- </MkSelect>
+<div>
+ <MkHeader :info="header"/>
+ <div class="taeiyria">
+ <div class="query">
+ <MkInput v-model="host" :debounce="true" class="">
+ <template #prefix><i class="fas fa-search"></i></template>
+ <template #label>{{ $ts.host }}</template>
+ </MkInput>
+ <div class="_inputSplit">
+ <MkSelect v-model="state">
+ <template #label>{{ $ts.state }}</template>
+ <option value="all">{{ $ts.all }}</option>
+ <option value="federating">{{ $ts.federating }}</option>
+ <option value="subscribing">{{ $ts.subscribing }}</option>
+ <option value="publishing">{{ $ts.publishing }}</option>
+ <option value="suspended">{{ $ts.suspended }}</option>
+ <option value="blocked">{{ $ts.blocked }}</option>
+ <option value="notResponding">{{ $ts.notResponding }}</option>
+ </MkSelect>
+ <MkSelect v-model="sort">
+ <template #label>{{ $ts.sort }}</template>
+ <option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option>
+ <option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option>
+ <option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option>
+ <option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option>
+ <option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option>
+ <option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option>
+ <option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option>
+ <option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option>
+ <option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option>
+ <option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
+ <option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
+ </MkSelect>
+ </div>
</div>
- </div>
- <MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state">
- <div class="dqokceoi">
- <MkA class="instance" v-for="instance in items" :key="instance.id" :to="`/instance-info/${instance.host}`">
- <div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div>
- <div class="table">
- <div class="cell">
- <div class="key">{{ $ts.registeredAt }}</div>
- <div class="value"><MkTime :time="instance.caughtAt"/></div>
- </div>
- <div class="cell">
- <div class="key">{{ $ts.software }}</div>
- <div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div>
- </div>
- <div class="cell">
- <div class="key">{{ $ts.version }}</div>
- <div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div>
- </div>
- <div class="cell">
- <div class="key">{{ $ts.users }}</div>
- <div class="value">{{ instance.usersCount }}</div>
- </div>
- <div class="cell">
- <div class="key">{{ $ts.notes }}</div>
- <div class="value">{{ instance.notesCount }}</div>
+ <MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state">
+ <div class="dqokceoi">
+ <MkA class="instance" v-for="instance in items" :key="instance.id" :to="`/instance-info/${instance.host}`">
+ <div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div>
+ <div class="table">
+ <div class="cell">
+ <div class="key">{{ $ts.registeredAt }}</div>
+ <div class="value"><MkTime :time="instance.caughtAt"/></div>
+ </div>
+ <div class="cell">
+ <div class="key">{{ $ts.software }}</div>
+ <div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div>
+ </div>
+ <div class="cell">
+ <div class="key">{{ $ts.version }}</div>
+ <div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div>
+ </div>
+ <div class="cell">
+ <div class="key">{{ $ts.users }}</div>
+ <div class="value">{{ instance.usersCount }}</div>
+ </div>
+ <div class="cell">
+ <div class="key">{{ $ts.notes }}</div>
+ <div class="value">{{ instance.notesCount }}</div>
+ </div>
+ <div class="cell">
+ <div class="key">{{ $ts.sent }}</div>
+ <div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
+ </div>
+ <div class="cell">
+ <div class="key">{{ $ts.received }}</div>
+ <div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
+ </div>
</div>
- <div class="cell">
- <div class="key">{{ $ts.sent }}</div>
- <div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
+ <div class="footer">
+ <span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span>
+ <span class="pubSub">
+ <span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span>
+ <span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span>
+ <span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span>
+ <span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span>
+ </span>
+ <span class="right">
+ <span class="latestStatus">{{ instance.latestStatus || '-' }}</span>
+ <span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span>
+ </span>
</div>
- <div class="cell">
- <div class="key">{{ $ts.received }}</div>
- <div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
- </div>
- </div>
- <div class="footer">
- <span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span>
- <span class="pubSub">
- <span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span>
- <span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span>
- <span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span>
- <span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span>
- </span>
- <span class="right">
- <span class="latestStatus">{{ instance.latestStatus || '-' }}</span>
- <span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span>
- </span>
- </div>
- </MkA>
- </div>
- </MkPagination>
+ </MkA>
+ </div>
+ </MkPagination>
+ </div>
</div>
</template>
<script lang="ts">
import { defineComponent } 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 MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkPagination from '@client/components/ui/pagination.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -116,7 +119,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.federation,
- icon: 'fas fa-globe'
+ icon: 'fas fa-globe',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.federation,
+ icon: 'fas fa-globe',
+ bg: 'var(--bg)',
},
host: '',
state: 'federating',
diff --git a/src/client/pages/gallery/edit.vue b/src/client/pages/gallery/edit.vue
index cd6a0defdd..8e74b068ef 100644
--- a/src/client/pages/gallery/edit.vue
+++ b/src/client/pages/gallery/edit.vue
@@ -1,23 +1,23 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormInput v-model:value="title">
+ <FormInput v-model="title">
<span>{{ $ts.title }}</span>
</FormInput>
- <FormTextarea v-model:value="description" :max="500">
+ <FormTextarea v-model="description" :max="500">
<span>{{ $ts.description }}</span>
</FormTextarea>
<FormGroup>
- <div v-for="file in files" :key="file.id" class="_formItem _formPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
+ <div v-for="file in files" :key="file.id" class="_debobigegoItem _debobigegoPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
<div class="name">{{ file.name }}</div>
<button class="remove _button" @click="remove(file)" v-tooltip="$ts.remove"><i class="fas fa-times"></i></button>
</div>
<FormButton @click="selectFile" primary><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton>
</FormGroup>
- <FormSwitch v-model:value="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch>
+ <FormSwitch v-model="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch>
<FormButton v-if="postId" @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
<FormButton v-else @click="save" primary><i class="fas fa-save"></i> {{ $ts.publish }}</FormButton>
@@ -29,14 +29,14 @@
<script lang="ts">
import { computed, defineComponent } from 'vue';
-import FormButton from '@client/components/form/button.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormTuple from '@client/components/form/tuple.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormTuple from '@client/components/debobigego/tuple.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import { selectFile } from '@client/scripts/select-file';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/gallery/index.vue b/src/client/pages/gallery/index.vue
index 9e726e70f2..ffc599513e 100644
--- a/src/client/pages/gallery/index.vue
+++ b/src/client/pages/gallery/index.vue
@@ -1,6 +1,6 @@
<template>
<div class="xprsixdl _root">
- <MkTab v-model:value="tab" v-if="$i">
+ <MkTab v-model="tab" v-if="$i">
<option value="explore"><i class="fas fa-icons"></i> {{ $ts.gallery }}</option>
<option value="liked"><i class="fas fa-heart"></i> {{ $ts._gallery.liked }}</option>
<option value="my"><i class="fas fa-edit"></i> {{ $ts._gallery.my }}</option>
@@ -46,7 +46,7 @@
import { computed, defineComponent } from 'vue';
import XUserList from '@client/components/user-list.vue';
import MkFolder from '@client/components/ui/folder.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import MkButton from '@client/components/ui/button.vue';
import MkTab from '@client/components/tab.vue';
import MkPagination from '@client/components/ui/pagination.vue';
diff --git a/src/client/pages/instance-info.vue b/src/client/pages/instance-info.vue
index 7d03c0847d..4fbf104f0c 100644
--- a/src/client/pages/instance-info.vue
+++ b/src/client/pages/instance-info.vue
@@ -3,8 +3,8 @@
<FormGroup v-if="instance">
<template #label>{{ instance.host }}</template>
<FormGroup>
- <div class="_formItem">
- <div class="_formPanel fnfelxur">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoPanel fnfelxur">
<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
</div>
</div>
@@ -60,9 +60,9 @@
<template #value>{{ instance.openRegistrations ? $ts.yes : $ts.no }}</template>
</FormKeyValueView>
</FormGroup>
- <div class="_formItem">
- <div class="_formLabel">{{ $ts.statistics }}</div>
- <div class="_formPanel cmhjzshl">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoLabel">{{ $ts.statistics }}</div>
+ <div class="_debobigegoPanel cmhjzshl">
<div class="selects">
<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
<option value="requests">{{ $ts._instanceCharts.requests }}</option>
@@ -136,15 +136,15 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import Chart from 'chart.js';
-import FormObjectView from '@client/components/form/object-view.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
-import MkSelect from '@client/components/ui/select.vue';
+import FormObjectView from '@client/components/debobigego/object-view.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
+import MkSelect from '@client/components/form/select.vue';
import * as os from '@client/os';
import number from '@client/filters/number';
import bytes from '@client/filters/bytes';
diff --git a/src/client/pages/instance/abuses.vue b/src/client/pages/instance/abuses.vue
index ac20ebabe5..29da8cc2c5 100644
--- a/src/client/pages/instance/abuses.vue
+++ b/src/client/pages/instance/abuses.vue
@@ -24,10 +24,10 @@
</div>
<!-- TODO
<div class="inputs" style="display: flex; padding-top: 1.2em;">
- <MkInput v-model:value="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.reports.reload()">
+ <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()">
<span>{{ $ts.username }}</span>
</MkInput>
- <MkInput v-model:value="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'">
+ <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'">
<span>{{ $ts.host }}</span>
</MkInput>
</div>
@@ -65,8 +65,8 @@
import { defineComponent } from 'vue';
import { parseAcct } from '@/misc/acct';
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 MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkPagination from '@client/components/ui/pagination.vue';
import { acct } from '@client/filters/user';
import * as os from '@client/os';
@@ -86,7 +86,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.abuseReports,
- icon: 'fas fa-exclamation-circle'
+ icon: 'fas fa-exclamation-circle',
+ bg: 'var(--bg)',
},
searchUsername: '',
searchHost: '',
diff --git a/src/client/pages/instance/ads.vue b/src/client/pages/instance/ads.vue
index 50c8c29cbf..e776f99a4c 100644
--- a/src/client/pages/instance/ads.vue
+++ b/src/client/pages/instance/ads.vue
@@ -1,52 +1,54 @@
<template>
-<div class="uqshojas">
- <MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
- <section class="_card _gap ads" v-for="ad in ads">
- <div class="_content ad">
- <MkAd v-if="ad.url" :specify="ad"/>
- <MkInput v-model="ad.url" type="url">
- <template #label>URL</template>
- </MkInput>
- <MkInput v-model="ad.imageUrl">
- <template #label>{{ $ts.imageUrl }}</template>
- </MkInput>
- <div style="margin: 32px 0;">
- <MkRadio v-model="ad.place" value="square">square</MkRadio>
- <MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
- <MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
+<div>
+ <MkHeader :info="header"/>
+ <div class="uqshojas">
+ <section class="_card _gap ads" v-for="ad in ads">
+ <div class="_content ad">
+ <MkAd v-if="ad.url" :specify="ad"/>
+ <MkInput v-model="ad.url" type="url">
+ <template #label>URL</template>
+ </MkInput>
+ <MkInput v-model="ad.imageUrl">
+ <template #label>{{ $ts.imageUrl }}</template>
+ </MkInput>
+ <div style="margin: 32px 0;">
+ <MkRadio v-model="ad.place" value="square">square</MkRadio>
+ <MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
+ <MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
+ </div>
+ <!--
+ <div style="margin: 32px 0;">
+ {{ $ts.priority }}
+ <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
+ <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
+ <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
+ </div>
+ -->
+ <MkInput v-model="ad.ratio" type="number">
+ <template #label>{{ $ts.ratio }}</template>
+ </MkInput>
+ <MkInput v-model="ad.expiresAt" type="date">
+ <template #label>{{ $ts.expiration }}</template>
+ </MkInput>
+ <MkTextarea v-model="ad.memo">
+ <template #label>{{ $ts.memo }}</template>
+ </MkTextarea>
+ <div class="buttons">
+ <MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+ <MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ </div>
</div>
- <!--
- <div style="margin: 32px 0;">
- {{ $ts.priority }}
- <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
- <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
- <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
- </div>
- -->
- <MkInput v-model="ad.ratio" type="number">
- <template #label>{{ $ts.ratio }}</template>
- </MkInput>
- <MkInput v-model="ad.expiresAt" type="date">
- <template #label>{{ $ts.expiration }}</template>
- </MkInput>
- <MkTextarea v-model="ad.memo">
- <template #label>{{ $ts.memo }}</template>
- </MkTextarea>
- <div class="buttons">
- <MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
- </div>
- </div>
- </section>
+ </section>
+ </div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkRadio from '@client/components/ui/radio.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkRadio from '@client/components/form/radio.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -64,7 +66,19 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.ads,
- icon: 'fas fa-audio-description'
+ icon: 'fas fa-audio-description',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.ads,
+ icon: 'fas fa-audio-description',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: this.$ts.add,
+ handler: this.add,
+ }],
},
ads: [],
}
diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue
index d48e3737ad..78637c095a 100644
--- a/src/client/pages/instance/announcements.vue
+++ b/src/client/pages/instance/announcements.vue
@@ -1,32 +1,35 @@
<template>
-<div class="ztgjmzrw">
- <MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
- <section class="_card _gap announcements" v-for="announcement in announcements">
- <div class="_content announcement">
- <MkInput v-model="announcement.title">
- <template #label>{{ $ts.title }}</template>
- </MkInput>
- <MkTextarea v-model="announcement.text">
- <template #label>{{ $ts.text }}</template>
- </MkTextarea>
- <MkInput v-model="announcement.imageUrl">
- <template #label>{{ $ts.imageUrl }}</template>
- </MkInput>
- <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
- <div class="buttons">
- <MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+<div>
+ <MkHeader :info="header"/>
+
+ <div class="ztgjmzrw">
+ <section class="_card _gap announcements" v-for="announcement in announcements">
+ <div class="_content announcement">
+ <MkInput v-model="announcement.title">
+ <template #label>{{ $ts.title }}</template>
+ </MkInput>
+ <MkTextarea v-model="announcement.text">
+ <template #label>{{ $ts.text }}</template>
+ </MkTextarea>
+ <MkInput v-model="announcement.imageUrl">
+ <template #label>{{ $ts.imageUrl }}</template>
+ </MkInput>
+ <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
+ <div class="buttons">
+ <MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+ <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ </div>
</div>
- </div>
- </section>
+ </section>
+ </div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -43,7 +46,19 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.announcements,
- icon: 'fas fa-broadcast-tower'
+ icon: 'fas fa-broadcast-tower',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.announcements,
+ icon: 'fas fa-broadcast-tower',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: this.$ts.add,
+ handler: this.add,
+ }],
},
announcements: [],
}
diff --git a/src/client/pages/instance/bot-protection.vue b/src/client/pages/instance/bot-protection.vue
index 449b8a233d..731f114cc2 100644
--- a/src/client/pages/instance/bot-protection.vue
+++ b/src/client/pages/instance/bot-protection.vue
@@ -9,43 +9,43 @@
</FormRadios>
<template v-if="provider === 'hcaptcha'">
- <div class="_formItem _formNoConcat" v-sticky-container>
- <div class="_formLabel">hCaptcha</div>
+ <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container>
+ <div class="_debobigegoLabel">hCaptcha</div>
<div class="main">
- <FormInput v-model:value="hcaptchaSiteKey">
+ <FormInput v-model="hcaptchaSiteKey">
<template #prefix><i class="fas fa-key"></i></template>
<span>{{ $ts.hcaptchaSiteKey }}</span>
</FormInput>
- <FormInput v-model:value="hcaptchaSecretKey">
+ <FormInput v-model="hcaptchaSecretKey">
<template #prefix><i class="fas fa-key"></i></template>
<span>{{ $ts.hcaptchaSecretKey }}</span>
</FormInput>
</div>
</div>
- <div class="_formItem _formNoConcat" v-sticky-container>
- <div class="_formLabel">{{ $ts.preview }}</div>
- <div class="_formPanel" style="padding: var(--formContentHMargin);">
+ <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container>
+ <div class="_debobigegoLabel">{{ $ts.preview }}</div>
+ <div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);">
<MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/>
</div>
</div>
</template>
<template v-else-if="provider === 'recaptcha'">
- <div class="_formItem _formNoConcat" v-sticky-container>
- <div class="_formLabel">reCAPTCHA</div>
+ <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container>
+ <div class="_debobigegoLabel">reCAPTCHA</div>
<div class="main">
- <FormInput v-model:value="recaptchaSiteKey">
+ <FormInput v-model="recaptchaSiteKey">
<template #prefix><i class="fas fa-key"></i></template>
<span>{{ $ts.recaptchaSiteKey }}</span>
</FormInput>
- <FormInput v-model:value="recaptchaSecretKey">
+ <FormInput v-model="recaptchaSecretKey">
<template #prefix><i class="fas fa-key"></i></template>
<span>{{ $ts.recaptchaSecretKey }}</span>
</FormInput>
</div>
</div>
- <div v-if="recaptchaSiteKey" class="_formItem _formNoConcat" v-sticky-container>
- <div class="_formLabel">{{ $ts.preview }}</div>
- <div class="_formPanel" style="padding: var(--formContentHMargin);">
+ <div v-if="recaptchaSiteKey" class="_debobigegoItem _debobigegoNoConcat" v-sticky-container>
+ <div class="_debobigegoLabel">{{ $ts.preview }}</div>
+ <div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);">
<MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/>
</div>
</div>
@@ -58,13 +58,13 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import FormRadios from '@client/components/form/radios.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormRadios from '@client/components/debobigego/radios.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
diff --git a/src/client/pages/instance/database.vue b/src/client/pages/instance/database.vue
index a41d61ce2b..ffbeed8b30 100644
--- a/src/client/pages/instance/database.vue
+++ b/src/client/pages/instance/database.vue
@@ -18,11 +18,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSuspense from '@client/components/form/suspense.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import bytes from '@client/filters/bytes';
@@ -43,7 +43,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.database,
- icon: 'fas fa-database'
+ icon: 'fas fa-database',
+ bg: 'var(--bg)',
},
databasePromiseFactory: () => os.api('admin/get-table-stats', {}).then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)),
}
diff --git a/src/client/pages/instance/email-settings.vue b/src/client/pages/instance/email-settings.vue
index 9965a1420f..ebf724fcdd 100644
--- a/src/client/pages/instance/email-settings.vue
+++ b/src/client/pages/instance/email-settings.vue
@@ -1,30 +1,30 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch>
+ <FormSwitch v-model="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch>
<template v-if="enableEmail">
- <FormInput v-model:value="email" type="email">
+ <FormInput v-model="email" type="email">
<span>{{ $ts.emailAddress }}</span>
</FormInput>
- <div class="_formItem _formNoConcat" v-sticky-container>
- <div class="_formLabel">{{ $ts.smtpConfig }}</div>
+ <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container>
+ <div class="_debobigegoLabel">{{ $ts.smtpConfig }}</div>
<div class="main">
- <FormInput v-model:value="smtpHost">
+ <FormInput v-model="smtpHost">
<span>{{ $ts.smtpHost }}</span>
</FormInput>
- <FormInput v-model:value="smtpPort" type="number">
+ <FormInput v-model="smtpPort" type="number">
<span>{{ $ts.smtpPort }}</span>
</FormInput>
- <FormInput v-model:value="smtpUser">
+ <FormInput v-model="smtpUser">
<span>{{ $ts.smtpUser }}</span>
</FormInput>
- <FormInput v-model:value="smtpPass" type="password">
+ <FormInput v-model="smtpPass" type="password">
<span>{{ $ts.smtpPass }}</span>
</FormInput>
<FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
- <FormSwitch v-model:value="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch>
+ <FormSwitch v-model="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch>
</div>
</div>
@@ -38,13 +38,13 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -66,7 +66,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.emailServer,
- icon: 'fas fa-envelope'
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
},
enableEmail: false,
email: null,
diff --git a/src/client/pages/instance/emoji-edit-dialog.vue b/src/client/pages/instance/emoji-edit-dialog.vue
index 7e9bdc80dd..4854c69884 100644
--- a/src/client/pages/instance/emoji-edit-dialog.vue
+++ b/src/client/pages/instance/emoji-edit-dialog.vue
@@ -11,13 +11,13 @@
<div class="_monolithic_">
<div class="yigymqpb _section">
<img :src="emoji.url" class="img"/>
- <MkInput v-model="name">
+ <MkInput class="_formBlock" v-model="name">
<template #label>{{ $ts.name }}</template>
</MkInput>
- <MkInput v-model="category" :datalist="categories">
+ <MkInput class="_formBlock" v-model="category" :datalist="categories">
<template #label>{{ $ts.category }}</template>
</MkInput>
- <MkInput v-model="aliases">
+ <MkInput class="_formBlock" v-model="aliases">
<template #label>{{ $ts.tags }}</template>
<template #caption>{{ $ts.setMultipleBySeparatingWithSpace }}</template>
</MkInput>
@@ -31,7 +31,7 @@
import { defineComponent } from 'vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
import { unique } from '../../../prelude/array';
diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue
index 7badc9da02..4cd34b046d 100644
--- a/src/client/pages/instance/emojis.vue
+++ b/src/client/pages/instance/emojis.vue
@@ -1,12 +1,8 @@
<template>
<div class="ogwlenmc">
- <MkTab v-model:value="tab">
- <option value="local">{{ $ts.local }}</option>
- <option value="remote">{{ $ts.remote }}</option>
- </MkTab>
+ <MkHeader :info="header"/>
<div class="local" v-if="tab === 'local'">
- <MkButton primary @click="add" style="margin: var(--margin) auto;"><i class="fas fa-plus"></i> {{ $ts.addEmoji }}</MkButton>
<MkInput v-model="query" :debounce="true" type="search" style="margin: var(--margin);">
<template #prefix><i class="fas fa-search"></i></template>
<template #label>{{ $ts.search }}</template>
@@ -56,7 +52,7 @@
<script lang="ts">
import { computed, defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import MkPagination from '@client/components/ui/pagination.vue';
import MkTab from '@client/components/tab.vue';
import { selectFile } from '@client/scripts/select-file';
@@ -78,11 +74,28 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.customEmojis,
icon: 'fas fa-laugh',
- action: {
- icon: 'fas fa-plus',
- handler: this.add
- }
+ bg: 'var(--bg)',
},
+ header: computed(() => ({
+ title: this.$ts.customEmojis,
+ icon: 'fas fa-laugh',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: this.$ts.addEmoji,
+ handler: this.add,
+ }],
+ tabs: [{
+ active: this.tab === 'local',
+ title: this.$ts.local,
+ onClick: () => { this.tab = 'local'; },
+ }, {
+ active: this.tab === 'remote',
+ title: this.$ts.remote,
+ onClick: () => { this.tab = 'remote'; },
+ },]
+ })),
tab: 'local',
query: null,
queryRemote: null,
diff --git a/src/client/pages/instance/file-dialog.vue b/src/client/pages/instance/file-dialog.vue
index 8a03a11de7..02d83e5022 100644
--- a/src/client/pages/instance/file-dialog.vue
+++ b/src/client/pages/instance/file-dialog.vue
@@ -37,7 +37,7 @@
<script lang="ts">
import { computed, defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
import MkDriveFileThumbnail from '@client/components/drive-file-thumbnail.vue';
import Progress from '@client/scripts/loading';
diff --git a/src/client/pages/instance/files-settings.vue b/src/client/pages/instance/files-settings.vue
index 614c7d4dbb..8aefa9e90d 100644
--- a/src/client/pages/instance/files-settings.vue
+++ b/src/client/pages/instance/files-settings.vue
@@ -1,23 +1,23 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="cacheRemoteFiles">
+ <FormSwitch v-model="cacheRemoteFiles">
{{ $ts.cacheRemoteFiles }}
<template #desc>{{ $ts.cacheRemoteFilesDescription }}</template>
</FormSwitch>
- <FormSwitch v-model:value="proxyRemoteFiles">
+ <FormSwitch v-model="proxyRemoteFiles">
{{ $ts.proxyRemoteFiles }}
<template #desc>{{ $ts.proxyRemoteFilesDescription }}</template>
</FormSwitch>
- <FormInput v-model:value="localDriveCapacityMb" type="number">
+ <FormInput v-model="localDriveCapacityMb" type="number">
<span>{{ $ts.driveCapacityPerLocalAccount }}</span>
<template #suffix>MB</template>
<template #desc>{{ $ts.inMb }}</template>
</FormInput>
- <FormInput v-model:value="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">
+ <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">
<span>{{ $ts.driveCapacityPerRemoteAccount }}</span>
<template #suffix>MB</template>
<template #desc>{{ $ts.inMb }}</template>
@@ -30,12 +30,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -56,7 +56,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.files,
- icon: 'fas fa-cloud'
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
},
cacheRemoteFiles: false,
proxyRemoteFiles: false,
diff --git a/src/client/pages/instance/files.vue b/src/client/pages/instance/files.vue
index b7f472b7c8..55189cfd84 100644
--- a/src/client/pages/instance/files.vue
+++ b/src/client/pages/instance/files.vue
@@ -1,20 +1,14 @@
<template>
<div class="xrmjdkdw">
- <div class="_section">
- <div class="_content">
- <MkButton primary @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearCachedFiles }}</MkButton>
- </div>
- </div>
-
- <div class="_section lookup">
- <div class="_title"><i class="fas fa-search"></i> {{ $ts.lookup }}</div>
- <div class="_content">
- <MkInput class="target" v-model="q" type="text" @enter="find()">
+ <MkContainer :foldable="true" class="lookup">
+ <template #header><i class="fas fa-search"></i> {{ $ts.lookup }}</template>
+ <div class="xrmjdkdw-lookup">
+ <MkInput class="item" v-model="q" type="text" @enter="find()">
<template #label>{{ $ts.fileIdOrUrl }}</template>
</MkInput>
<MkButton @click="find()" primary><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton>
</div>
- </div>
+ </MkContainer>
<div class="_section">
<div class="_content">
@@ -31,7 +25,7 @@
</div>
<div class="inputs" style="display: flex; padding-top: 1.2em;">
<MkInput v-model="type" :debounce="true" type="search" style="margin: 0; flex: 1;">
- <template #label>{{ $ts.type }}</template>
+ <template #label>MIME type</template>
</MkInput>
</div>
<MkPagination :pagination="pagination" #default="{items}" class="urempief" ref="files">
@@ -63,9 +57,10 @@
<script lang="ts">
import { defineComponent } 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 MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkPagination from '@client/components/ui/pagination.vue';
+import MkContainer from '@client/components/ui/container.vue';
import MkDriveFileThumbnail from '@client/components/drive-file-thumbnail.vue';
import bytes from '@client/filters/bytes';
import * as os from '@client/os';
@@ -77,6 +72,7 @@ export default defineComponent({
MkInput,
MkSelect,
MkPagination,
+ MkContainer,
MkDriveFileThumbnail,
},
@@ -86,7 +82,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.files,
- icon: 'fas fa-cloud'
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
+ actions: [{
+ text: this.$ts.clearCachedFiles,
+ icon: 'fas fa-trash-alt',
+ handler: this.clear
+ }]
},
q: null,
origin: 'local',
@@ -161,6 +163,10 @@ export default defineComponent({
.xrmjdkdw {
margin: var(--margin);
+ > .lookup {
+ margin-bottom: 16px;
+ }
+
.urempief {
margin-top: var(--margin);
@@ -192,4 +198,12 @@ export default defineComponent({
}
}
}
+
+.xrmjdkdw-lookup {
+ padding: 16px;
+
+ > .item {
+ margin-bottom: 16px;
+ }
+}
</style>
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue
index 612bfa762a..7b07bf2dde 100644
--- a/src/client/pages/instance/index.vue
+++ b/src/client/pages/instance/index.vue
@@ -1,51 +1,16 @@
<template>
<div class="hiyeyicy" :class="{ wide: !narrow }" ref="el">
<div class="nav" v-if="!narrow || page == null">
- <FormBase>
- <FormGroup>
- <div class="_formItem">
- <div class="_formPanel lxpfedzu">
- <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
- </div>
- </div>
- <FormLink :active="page === 'overview'" replace to="/instance/overview"><template #icon><i class="fas fa-tachometer-alt"></i></template>{{ $ts.overview }}</FormLink>
- </FormGroup>
- <FormGroup>
- <template #label>{{ $ts.quickAction }}</template>
- <FormButton @click="lookup"><i class="fas fa-search"></i> {{ $ts.lookup }}</FormButton>
- <FormButton v-if="$instance.disableRegistration" @click="invite"><i class="fas fa-user"></i> {{ $ts.invite }}</FormButton>
- </FormGroup>
- <FormGroup>
- <template #label>{{ $ts.administration }}</template>
- <FormLink :active="page === 'users'" replace to="/instance/users"><template #icon><i class="fas fa-users"></i></template>{{ $ts.users }}</FormLink>
- <FormLink :active="page === 'emojis'" replace to="/instance/emojis"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.customEmojis }}</FormLink>
- <FormLink :active="page === 'federation'" replace to="/instance/federation"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.federation }}</FormLink>
- <FormLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</FormLink>
- <FormLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink>
- <FormLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</FormLink>
- <FormLink :active="page === 'ads'" replace to="/instance/ads"><template #icon><i class="fas fa-audio-description"></i></template>{{ $ts.ads }}</FormLink>
- <FormLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</FormLink>
- </FormGroup>
- <FormGroup>
- <template #label>{{ $ts.settings }}</template>
- <FormLink :active="page === 'settings'" replace to="/instance/settings"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.general }}</FormLink>
- <FormLink :active="page === 'files-settings'" replace to="/instance/files-settings"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink>
- <FormLink :active="page === 'email-settings'" replace to="/instance/email-settings"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.emailServer }}</FormLink>
- <FormLink :active="page === 'object-storage'" replace to="/instance/object-storage"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.objectStorage }}</FormLink>
- <FormLink :active="page === 'security'" replace to="/instance/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</FormLink>
- <FormLink :active="page === 'service-worker'" replace to="/instance/service-worker"><template #icon><i class="fas fa-bolt"></i></template>ServiceWorker</FormLink>
- <FormLink :active="page === 'relays'" replace to="/instance/relays"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.relays }}</FormLink>
- <FormLink :active="page === 'integrations'" replace to="/instance/integrations"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</FormLink>
- <FormLink :active="page === 'instance-block'" replace to="/instance/instance-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.instanceBlocking }}</FormLink>
- <FormLink :active="page === 'proxy-account'" replace to="/instance/proxy-account"><template #icon><i class="fas fa-ghost"></i></template>{{ $ts.proxyAccount }}</FormLink>
- <FormLink :active="page === 'other-settings'" replace to="/instance/other-settings"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.other }}</FormLink>
- </FormGroup>
- <FormGroup>
- <template #label>{{ $ts.info }}</template>
- <FormLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</FormLink>
- <FormLink :active="page === 'logs'" replace to="/instance/logs"><template #icon><i class="fas fa-stream"></i></template>{{ $ts.logs }}</FormLink>
- </FormGroup>
- </FormBase>
+ <MkHeader :info="header"></MkHeader>
+
+ <div class="lxpfedzu">
+ <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
+ </div>
+
+ <MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+ <MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+
+ <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
</div>
<div class="main">
<component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/>
@@ -56,11 +21,13 @@
<script lang="ts">
import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue';
import { i18n } from '@client/i18n';
-import FormLink from '@client/components/form/link.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
+import MkSuperMenu from '@client/components/ui/super-menu.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import MkInfo from '@client/components/ui/info.vue';
import { scroll } from '@client/scripts/scroll';
+import { instance } from '@client/instance';
import * as symbols from '@client/symbols';
import * as os from '@client/os';
import { lookupUser } from '@client/scripts/lookup-user';
@@ -68,9 +35,10 @@ import { lookupUser } from '@client/scripts/lookup-user';
export default defineComponent({
components: {
FormBase,
- FormLink,
+ MkSuperMenu,
FormGroup,
FormButton,
+ MkInfo,
},
props: {
@@ -83,7 +51,8 @@ export default defineComponent({
setup(props, context) {
const indexInfo = {
title: i18n.locale.instance,
- icon: 'fas fa-cog'
+ icon: 'fas fa-cog',
+ bg: 'var(--bg)',
};
const INFO = ref(indexInfo);
const page = ref(props.initialPage);
@@ -94,6 +63,151 @@ export default defineComponent({
INFO.value = viewInfo;
};
const pageProps = ref({});
+
+ const isEmpty = (x: any) => x == null || x == '';
+
+ const noMaintainerInformation = ref(false);
+ const noBotProtection = ref(false);
+
+ os.api('meta', { detail: true }).then(meta => {
+ // TODO: 設定が完了しても残ったままになるので、ストリーミングでmeta更新イベントを受け取ってよしなに更新する
+ noMaintainerInformation.value = isEmpty(meta.maintainerName) || isEmpty(meta.maintainerEmail);
+ noBotProtection.value = !meta.enableHcaptcha && !meta.enableRecaptcha;
+ });
+
+ const menuDef = computed(() => [{
+ title: i18n.locale.quickAction,
+ items: [{
+ type: 'button',
+ icon: 'fas fa-search',
+ text: i18n.locale.lookup,
+ action: lookup,
+ }, ...(instance.disableRegistration ? [{
+ type: 'button',
+ icon: 'fas fa-user',
+ text: i18n.locale.invite,
+ action: invite,
+ }] : [])],
+ }, {
+ title: i18n.locale.administration,
+ items: [{
+ icon: 'fas fa-tachometer-alt',
+ text: i18n.locale.dashboard,
+ to: '/instance/overview',
+ active: page.value === 'overview',
+ }, {
+ icon: 'fas fa-users',
+ text: i18n.locale.users,
+ to: '/instance/users',
+ active: page.value === 'users',
+ }, {
+ icon: 'fas fa-laugh',
+ text: i18n.locale.customEmojis,
+ to: '/instance/emojis',
+ active: page.value === 'emojis',
+ }, {
+ icon: 'fas fa-globe',
+ text: i18n.locale.federation,
+ to: '/instance/federation',
+ active: page.value === 'federation',
+ }, {
+ icon: 'fas fa-clipboard-list',
+ text: i18n.locale.jobQueue,
+ to: '/instance/queue',
+ active: page.value === 'queue',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.locale.files,
+ to: '/instance/files',
+ active: page.value === 'files',
+ }, {
+ icon: 'fas fa-broadcast-tower',
+ text: i18n.locale.announcements,
+ to: '/instance/announcements',
+ active: page.value === 'announcements',
+ }, {
+ icon: 'fas fa-audio-description',
+ text: i18n.locale.ads,
+ to: '/instance/ads',
+ active: page.value === 'ads',
+ }, {
+ icon: 'fas fa-exclamation-circle',
+ text: i18n.locale.abuseReports,
+ to: '/instance/abuses',
+ active: page.value === 'abuses',
+ }],
+ }, {
+ title: i18n.locale.settings,
+ items: [{
+ icon: 'fas fa-cog',
+ text: i18n.locale.general,
+ to: '/instance/settings',
+ active: page.value === 'settings',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.locale.files,
+ to: '/instance/files-settings',
+ active: page.value === 'files-settings',
+ }, {
+ icon: 'fas fa-envelope',
+ text: i18n.locale.emailServer,
+ to: '/instance/email-settings',
+ active: page.value === 'email-settings',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.locale.objectStorage,
+ to: '/instance/object-storage',
+ active: page.value === 'object-storage',
+ }, {
+ icon: 'fas fa-lock',
+ text: i18n.locale.security,
+ to: '/instance/security',
+ active: page.value === 'security',
+ }, {
+ icon: 'fas fa-bolt',
+ text: 'ServiceWorker',
+ to: '/instance/service-worker',
+ active: page.value === 'service-worker',
+ }, {
+ icon: 'fas fa-globe',
+ text: i18n.locale.relays,
+ to: '/instance/relays',
+ active: page.value === 'relays',
+ }, {
+ icon: 'fas fa-share-alt',
+ text: i18n.locale.integration,
+ to: '/instance/integrations',
+ active: page.value === 'integrations',
+ }, {
+ icon: 'fas fa-ban',
+ text: i18n.locale.instanceBlocking,
+ to: '/instance/instance-block',
+ active: page.value === 'instance-block',
+ }, {
+ icon: 'fas fa-ghost',
+ text: i18n.locale.proxyAccount,
+ to: '/instance/proxy-account',
+ active: page.value === 'proxy-account',
+ }, {
+ icon: 'fas fa-cogs',
+ text: i18n.locale.other,
+ to: '/instance/other-settings',
+ active: page.value === 'other-settings',
+ }],
+ }, {
+ title: i18n.locale.info,
+ items: [{
+ icon: 'fas fa-database',
+ text: i18n.locale.database,
+ to: '/instance/database',
+ active: page.value === 'database',
+ }, {
+ icon: 'fas fa-stream',
+ text: i18n.locale.logs,
+ to: '/instance/logs',
+ active: page.value === 'logs',
+ }],
+ }]);
const component = computed(() => {
if (page.value == null) return null;
switch (page.value) {
@@ -130,7 +244,7 @@ export default defineComponent({
pageProps.value = {};
nextTick(() => {
- scroll(el.value, 0);
+ scroll(el.value, { top: 0 });
});
}, { immediate: true });
@@ -196,6 +310,12 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: INFO,
+ menuDef,
+ header: {
+ title: i18n.locale.controllPanel,
+ },
+ noMaintainerInformation,
+ noBotProtection,
page,
narrow,
view,
@@ -214,28 +334,34 @@ export default defineComponent({
.hiyeyicy {
&.wide {
display: flex;
- max-width: 1100px;
margin: 0 auto;
height: 100%;
> .nav {
width: 32%;
+ max-width: 280px;
box-sizing: border-box;
border-right: solid 0.5px var(--divider);
overflow: auto;
+ height: 100%;
}
> .main {
flex: 1;
min-width: 0;
- overflow: auto;
--baseContentWidth: 100%;
}
}
+
+ > .nav {
+ > .info {
+ margin: 16px;
+ }
+ }
}
.lxpfedzu {
- padding: 16px;
+ margin: 16px;
> .icon {
display: block;
diff --git a/src/client/pages/instance/instance-block.vue b/src/client/pages/instance/instance-block.vue
index ed5740f339..105cdb4941 100644
--- a/src/client/pages/instance/instance-block.vue
+++ b/src/client/pages/instance/instance-block.vue
@@ -1,7 +1,7 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormTextarea v-model:value="blockedHosts">
+ <FormTextarea v-model="blockedHosts">
<span>{{ $ts.blockedInstances }}</span>
<template #desc>{{ $ts.blockedInstancesDescription }}</template>
</FormTextarea>
@@ -13,14 +13,14 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -43,7 +43,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.instanceBlocking,
- icon: 'fas fa-ban'
+ icon: 'fas fa-ban',
+ bg: 'var(--bg)',
},
blockedHosts: '',
}
diff --git a/src/client/pages/instance/instance.vue b/src/client/pages/instance/instance.vue
index c39f0d1ecb..6117f090de 100644
--- a/src/client/pages/instance/instance.vue
+++ b/src/client/pages/instance/instance.vue
@@ -127,9 +127,9 @@ import { defineComponent, markRaw } from 'vue';
import Chart from 'chart.js';
import XModalWindow from '@client/components/ui/modal-window.vue';
import MkUsersDialog from '@client/components/users-dialog.vue';
-import MkSelect from '@client/components/ui/select.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import MkInfo from '@client/components/ui/info.vue';
import bytes from '@client/filters/bytes';
import number from '@client/filters/number';
diff --git a/src/client/pages/instance/integrations-discord.vue b/src/client/pages/instance/integrations-discord.vue
index c7508918f8..c33b24f17f 100644
--- a/src/client/pages/instance/integrations-discord.vue
+++ b/src/client/pages/instance/integrations-discord.vue
@@ -1,19 +1,19 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="enableDiscordIntegration">
+ <FormSwitch v-model="enableDiscordIntegration">
{{ $ts.enable }}
</FormSwitch>
<template v-if="enableDiscordIntegration">
<FormInfo>Callback URL: {{ `${url}/api/dc/cb` }}</FormInfo>
- <FormInput v-model:value="discordClientId">
+ <FormInput v-model="discordClientId">
<template #prefix><i class="fas fa-key"></i></template>
Client ID
</FormInput>
- <FormInput v-model:value="discordClientSecret">
+ <FormInput v-model="discordClientSecret">
<template #prefix><i class="fas fa-key"></i></template>
Client Secret
</FormInput>
@@ -26,12 +26,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
diff --git a/src/client/pages/instance/integrations-github.vue b/src/client/pages/instance/integrations-github.vue
index 16586b15b4..cdf85868ff 100644
--- a/src/client/pages/instance/integrations-github.vue
+++ b/src/client/pages/instance/integrations-github.vue
@@ -1,19 +1,19 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="enableGithubIntegration">
+ <FormSwitch v-model="enableGithubIntegration">
{{ $ts.enable }}
</FormSwitch>
<template v-if="enableGithubIntegration">
<FormInfo>Callback URL: {{ `${url}/api/gh/cb` }}</FormInfo>
- <FormInput v-model:value="githubClientId">
+ <FormInput v-model="githubClientId">
<template #prefix><i class="fas fa-key"></i></template>
Client ID
</FormInput>
- <FormInput v-model:value="githubClientSecret">
+ <FormInput v-model="githubClientSecret">
<template #prefix><i class="fas fa-key"></i></template>
Client Secret
</FormInput>
@@ -26,12 +26,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
diff --git a/src/client/pages/instance/integrations-twitter.vue b/src/client/pages/instance/integrations-twitter.vue
index b08b7f40a5..ed7d097d0a 100644
--- a/src/client/pages/instance/integrations-twitter.vue
+++ b/src/client/pages/instance/integrations-twitter.vue
@@ -1,19 +1,19 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="enableTwitterIntegration">
+ <FormSwitch v-model="enableTwitterIntegration">
{{ $ts.enable }}
</FormSwitch>
<template v-if="enableTwitterIntegration">
<FormInfo>Callback URL: {{ `${url}/api/tw/cb` }}</FormInfo>
- <FormInput v-model:value="twitterConsumerKey">
+ <FormInput v-model="twitterConsumerKey">
<template #prefix><i class="fas fa-key"></i></template>
Consumer Key
</FormInput>
- <FormInput v-model:value="twitterConsumerSecret">
+ <FormInput v-model="twitterConsumerSecret">
<template #prefix><i class="fas fa-key"></i></template>
Consumer Secret
</FormInput>
@@ -26,12 +26,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
diff --git a/src/client/pages/instance/integrations.vue b/src/client/pages/instance/integrations.vue
index 7debedc367..6964ae5704 100644
--- a/src/client/pages/instance/integrations.vue
+++ b/src/client/pages/instance/integrations.vue
@@ -19,14 +19,14 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormLink from '@client/components/form/link.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -49,7 +49,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.integration,
- icon: 'fas fa-share-alt'
+ icon: 'fas fa-share-alt',
+ bg: 'var(--bg)',
},
enableTwitterIntegration: false,
enableGithubIntegration: false,
diff --git a/src/client/pages/instance/logs.vue b/src/client/pages/instance/logs.vue
index 4eee816f96..74aea0fc45 100644
--- a/src/client/pages/instance/logs.vue
+++ b/src/client/pages/instance/logs.vue
@@ -31,9 +31,9 @@
<script lang="ts">
import { defineComponent } 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 MkTextarea from '@client/components/ui/textarea.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/instance/metrics.vue b/src/client/pages/instance/metrics.vue
index 283b5939f0..1606063aee 100644
--- a/src/client/pages/instance/metrics.vue
+++ b/src/client/pages/instance/metrics.vue
@@ -1,7 +1,7 @@
<template>
-<div class="_formItem">
- <div class="_formLabel"><i class="fas fa-microchip"></i> {{ $ts.cpuAndMemory }}</div>
- <div class="_formPanel xhexznfu">
+<div class="_debobigegoItem">
+ <div class="_debobigegoLabel"><i class="fas fa-microchip"></i> {{ $ts.cpuAndMemory }}</div>
+ <div class="_debobigegoPanel xhexznfu">
<div>
<canvas :ref="cpumem"></canvas>
</div>
@@ -16,9 +16,9 @@
</div>
</div>
</div>
-<div class="_formItem">
- <div class="_formLabel"><i class="fas fa-hdd"></i> {{ $ts.disk }}</div>
- <div class="_formPanel xhexznfu">
+<div class="_debobigegoItem">
+ <div class="_debobigegoLabel"><i class="fas fa-hdd"></i> {{ $ts.disk }}</div>
+ <div class="_debobigegoPanel xhexznfu">
<div>
<canvas :ref="disk"></canvas>
</div>
@@ -33,9 +33,9 @@
</div>
</div>
</div>
-<div class="_formItem">
- <div class="_formLabel"><i class="fas fa-exchange-alt"></i> {{ $ts.network }}</div>
- <div class="_formPanel xhexznfu">
+<div class="_debobigegoItem">
+ <div class="_debobigegoLabel"><i class="fas fa-exchange-alt"></i> {{ $ts.network }}</div>
+ <div class="_debobigegoPanel xhexznfu">
<div>
<canvas :ref="net"></canvas>
</div>
@@ -54,8 +54,8 @@
import { defineComponent, markRaw } from 'vue';
import Chart from 'chart.js';
import MkButton from '@client/components/ui/button.vue';
-import MkSelect from '@client/components/ui/select.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkSelect from '@client/components/form/select.vue';
+import MkInput from '@client/components/form/input.vue';
import MkContainer from '@client/components/ui/container.vue';
import MkFolder from '@client/components/ui/folder.vue';
import MkwFederation from '../../widgets/federation.vue';
diff --git a/src/client/pages/instance/object-storage.vue b/src/client/pages/instance/object-storage.vue
index 814aeb6e48..2d765270e6 100644
--- a/src/client/pages/instance/object-storage.vue
+++ b/src/client/pages/instance/object-storage.vue
@@ -1,59 +1,59 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch>
+ <FormSwitch v-model="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch>
<template v-if="useObjectStorage">
- <FormInput v-model:value="objectStorageBaseUrl">
+ <FormInput v-model="objectStorageBaseUrl">
<span>{{ $ts.objectStorageBaseUrl }}</span>
<template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template>
</FormInput>
- <FormInput v-model:value="objectStorageBucket">
+ <FormInput v-model="objectStorageBucket">
<span>{{ $ts.objectStorageBucket }}</span>
<template #desc>{{ $ts.objectStorageBucketDesc }}</template>
</FormInput>
- <FormInput v-model:value="objectStoragePrefix">
+ <FormInput v-model="objectStoragePrefix">
<span>{{ $ts.objectStoragePrefix }}</span>
<template #desc>{{ $ts.objectStoragePrefixDesc }}</template>
</FormInput>
- <FormInput v-model:value="objectStorageEndpoint">
+ <FormInput v-model="objectStorageEndpoint">
<span>{{ $ts.objectStorageEndpoint }}</span>
<template #desc>{{ $ts.objectStorageEndpointDesc }}</template>
</FormInput>
- <FormInput v-model:value="objectStorageRegion">
+ <FormInput v-model="objectStorageRegion">
<span>{{ $ts.objectStorageRegion }}</span>
<template #desc>{{ $ts.objectStorageRegionDesc }}</template>
</FormInput>
- <FormInput v-model:value="objectStorageAccessKey">
+ <FormInput v-model="objectStorageAccessKey">
<template #prefix><i class="fas fa-key"></i></template>
<span>Access key</span>
</FormInput>
- <FormInput v-model:value="objectStorageSecretKey">
+ <FormInput v-model="objectStorageSecretKey">
<template #prefix><i class="fas fa-key"></i></template>
<span>Secret key</span>
</FormInput>
- <FormSwitch v-model:value="objectStorageUseSSL">
+ <FormSwitch v-model="objectStorageUseSSL">
{{ $ts.objectStorageUseSSL }}
<template #desc>{{ $ts.objectStorageUseSSLDesc }}</template>
</FormSwitch>
- <FormSwitch v-model:value="objectStorageUseProxy">
+ <FormSwitch v-model="objectStorageUseProxy">
{{ $ts.objectStorageUseProxy }}
<template #desc>{{ $ts.objectStorageUseProxyDesc }}</template>
</FormSwitch>
- <FormSwitch v-model:value="objectStorageSetPublicRead">
+ <FormSwitch v-model="objectStorageSetPublicRead">
{{ $ts.objectStorageSetPublicRead }}
</FormSwitch>
- <FormSwitch v-model:value="objectStorageS3ForcePathStyle">
+ <FormSwitch v-model="objectStorageS3ForcePathStyle">
s3ForcePathStyle
</FormSwitch>
</template>
@@ -65,12 +65,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -91,7 +91,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.objectStorage,
- icon: 'fas fa-cloud'
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
},
useObjectStorage: false,
objectStorageBaseUrl: null,
diff --git a/src/client/pages/instance/other-settings.vue b/src/client/pages/instance/other-settings.vue
index 4fa80b2b2c..4e55df41fb 100644
--- a/src/client/pages/instance/other-settings.vue
+++ b/src/client/pages/instance/other-settings.vue
@@ -2,17 +2,17 @@
<FormBase>
<FormSuspense :p="init">
<FormGroup>
- <FormInput v-model:value="summalyProxy">
+ <FormInput v-model="summalyProxy">
<template #prefix><i class="fas fa-link"></i></template>
Summaly Proxy URL
</FormInput>
</FormGroup>
<FormGroup>
- <FormInput v-model:value="deeplAuthKey">
+ <FormInput v-model="deeplAuthKey">
<template #prefix><i class="fas fa-key"></i></template>
DeepL Auth Key
</FormInput>
- <FormSwitch v-model:value="deeplIsPro">
+ <FormSwitch v-model="deeplIsPro">
Pro account
</FormSwitch>
</FormGroup>
@@ -23,12 +23,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -49,7 +49,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.other,
- icon: 'fas fa-cogs'
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)',
},
summalyProxy: '',
deeplAuthKey: '',
diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue
index 0d7a5d1501..c6db9d0c04 100644
--- a/src/client/pages/instance/overview.vue
+++ b/src/client/pages/instance/overview.vue
@@ -1,9 +1,6 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormInfo v-if="noMaintainerInformation" warn>{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></FormInfo>
- <FormInfo v-if="noBotProtection" warn>{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></FormInfo>
-
<FormSuspense :p="fetchStats" v-slot="{ result: stats }">
<FormGroup>
<FormKeyValueView>
@@ -17,8 +14,8 @@
</FormGroup>
</FormSuspense>
- <div class="_formItem">
- <div class="_formPanel">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoPanel">
<MkInstanceStats :chart-limit="300" :detailed="true"/>
</div>
</div>
@@ -47,18 +44,18 @@
<script lang="ts">
import { computed, defineComponent, markRaw } from 'vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import MkInstanceStats from '@client/components/instance-stats.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkSelect from '@client/components/ui/select.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkSelect from '@client/components/form/select.vue';
+import MkInput from '@client/components/form/input.vue';
import MkContainer from '@client/components/ui/container.vue';
import MkFolder from '@client/components/ui/folder.vue';
import { version, url } from '@client/config';
@@ -86,7 +83,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.overview,
- icon: 'fas fa-tachometer-alt'
+ icon: 'fas fa-tachometer-alt',
+ bg: 'var(--bg)',
},
page: 'index',
version,
@@ -97,8 +95,6 @@ export default defineComponent({
fetchServerInfo: () => os.api('admin/server-info', {}),
fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
fetchModLogs: () => os.api('admin/show-moderation-logs', {}),
- noMaintainerInformation: false,
- noBotProtection: false,
}
},
@@ -109,11 +105,6 @@ export default defineComponent({
methods: {
async init() {
this.meta = await os.api('meta', { detail: true });
-
- const isEmpty = (x: any) => x == null || x == '';
-
- this.noMaintainerInformation = isEmpty(this.meta.maintainerName) || isEmpty(this.meta.maintainerEmail);
- this.noBotProtection = !this.meta.enableHcaptcha && !this.meta.enableRecaptcha;
},
async showInstanceInfo(q) {
diff --git a/src/client/pages/instance/proxy-account.vue b/src/client/pages/instance/proxy-account.vue
index 3e2df8dcb4..b1ece19710 100644
--- a/src/client/pages/instance/proxy-account.vue
+++ b/src/client/pages/instance/proxy-account.vue
@@ -16,14 +16,14 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -46,7 +46,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.proxyAccount,
- icon: 'fas fa-ghost'
+ icon: 'fas fa-ghost',
+ bg: 'var(--bg)',
},
proxyAccount: null,
proxyAccountId: null,
diff --git a/src/client/pages/instance/queue.chart.vue b/src/client/pages/instance/queue.chart.vue
index 53d790598a..887fe9a574 100644
--- a/src/client/pages/instance/queue.chart.vue
+++ b/src/client/pages/instance/queue.chart.vue
@@ -1,7 +1,7 @@
<template>
-<div class="_formItem">
- <div class="_formLabel"><slot name="title"></slot></div>
- <div class="_formPanel pumxzjhg">
+<div class="_debobigegoItem">
+ <div class="_debobigegoLabel"><slot name="title"></slot></div>
+ <div class="_debobigegoPanel pumxzjhg">
<div class="_table status">
<div class="_row">
<div class="_cell"><div class="_label">Process</div>{{ number(activeSincePrevTick) }}</div>
diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue
index e8ec0bc97d..f88825eb19 100644
--- a/src/client/pages/instance/queue.vue
+++ b/src/client/pages/instance/queue.vue
@@ -14,8 +14,8 @@
import { defineComponent, markRaw } from 'vue';
import MkButton from '@client/components/ui/button.vue';
import XQueue from './queue.chart.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -34,6 +34,7 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.jobQueue,
icon: 'fas fa-clipboard-list',
+ bg: 'var(--bg)',
},
connection: markRaw(os.stream.useChannel('queueStats')),
}
diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue
index a3e4e7d1da..7d7888eaa8 100644
--- a/src/client/pages/instance/relays.vue
+++ b/src/client/pages/instance/relays.vue
@@ -2,8 +2,8 @@
<FormBase class="relaycxt">
<FormButton @click="addRelay" primary><i class="fas fa-plus"></i> {{ $ts.addRelay }}</FormButton>
- <div class="_formItem" v-for="relay in relays" :key="relay.inbox">
- <div class="_formPanel" style="padding: 16px;">
+ <div class="_debobigegoItem" v-for="relay in relays" :key="relay.inbox">
+ <div class="_debobigegoPanel" style="padding: 16px;">
<div>{{ relay.inbox }}</div>
<div>{{ $t(`_relayStatus.${relay.status}`) }}</div>
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
@@ -15,9 +15,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
+import MkInput from '@client/components/form/input.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -36,6 +36,7 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.relays,
icon: 'fas fa-globe',
+ bg: 'var(--bg)',
},
relays: [],
inbox: '',
diff --git a/src/client/pages/instance/security.vue b/src/client/pages/instance/security.vue
index e3397a113b..a854b6dbd0 100644
--- a/src/client/pages/instance/security.vue
+++ b/src/client/pages/instance/security.vue
@@ -8,7 +8,9 @@
<template #suffix v-else>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
</FormLink>
- <FormSwitch v-model:value="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch>
+ <FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch>
+
+ <FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch>
<FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
</FormSuspense>
@@ -17,13 +19,13 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import FormLink from '@client/components/form/link.vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -45,11 +47,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.security,
- icon: 'fas fa-lock'
+ icon: 'fas fa-lock',
+ bg: 'var(--bg)',
},
enableHcaptcha: false,
enableRecaptcha: false,
enableRegistration: false,
+ emailRequiredForSignup: false,
}
},
@@ -63,11 +67,13 @@ export default defineComponent({
this.enableHcaptcha = meta.enableHcaptcha;
this.enableRecaptcha = meta.enableRecaptcha;
this.enableRegistration = !meta.disableRegistration;
+ this.emailRequiredForSignup = meta.emailRequiredForSignup;
},
save() {
os.apiWithDialog('admin/update-meta', {
disableRegistration: !this.enableRegistration,
+ emailRequiredForSignup: this.emailRequiredForSignup,
}).then(() => {
fetchInstance();
});
diff --git a/src/client/pages/instance/service-worker.vue b/src/client/pages/instance/service-worker.vue
index a52932bb75..430e02ad2e 100644
--- a/src/client/pages/instance/service-worker.vue
+++ b/src/client/pages/instance/service-worker.vue
@@ -1,18 +1,18 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormSwitch v-model:value="enableServiceWorker">
+ <FormSwitch v-model="enableServiceWorker">
{{ $ts.enableServiceworker }}
<template #desc>{{ $ts.serviceworkerInfo }}</template>
</FormSwitch>
<template v-if="enableServiceWorker">
- <FormInput v-model:value="swPublicKey">
+ <FormInput v-model="swPublicKey">
<template #prefix><i class="fas fa-key"></i></template>
Public key
</FormInput>
- <FormInput v-model:value="swPrivateKey">
+ <FormInput v-model="swPrivateKey">
<template #prefix><i class="fas fa-key"></i></template>
Private key
</FormInput>
@@ -25,12 +25,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -51,7 +51,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: 'ServiceWorker',
- icon: 'fas fa-bolt'
+ icon: 'fas fa-bolt',
+ bg: 'var(--bg)',
},
enableServiceWorker: false,
swPublicKey: null,
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
index b68d784897..7bd363e5f3 100644
--- a/src/client/pages/instance/settings.vue
+++ b/src/client/pages/instance/settings.vue
@@ -1,50 +1,55 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <FormInput v-model:value="name">
+ <FormInput v-model="name">
<span>{{ $ts.instanceName }}</span>
</FormInput>
- <FormTextarea v-model:value="description">
+ <FormTextarea v-model="description">
<span>{{ $ts.instanceDescription }}</span>
</FormTextarea>
- <FormInput v-model:value="iconUrl">
+ <FormInput v-model="iconUrl">
<template #prefix><i class="fas fa-link"></i></template>
<span>{{ $ts.iconUrl }}</span>
</FormInput>
- <FormInput v-model:value="bannerUrl">
+ <FormInput v-model="bannerUrl">
<template #prefix><i class="fas fa-link"></i></template>
<span>{{ $ts.bannerUrl }}</span>
</FormInput>
- <FormInput v-model:value="backgroundImageUrl">
+ <FormInput v-model="backgroundImageUrl">
<template #prefix><i class="fas fa-link"></i></template>
<span>{{ $ts.backgroundImageUrl }}</span>
</FormInput>
- <FormInput v-model:value="tosUrl">
+ <FormInput v-model="tosUrl">
<template #prefix><i class="fas fa-link"></i></template>
<span>{{ $ts.tosUrl }}</span>
</FormInput>
- <FormInput v-model:value="maintainerName">
+ <FormInput v-model="maintainerName">
<span>{{ $ts.maintainerName }}</span>
</FormInput>
- <FormInput v-model:value="maintainerEmail" type="email">
+ <FormInput v-model="maintainerEmail" type="email">
<template #prefix><i class="fas fa-envelope"></i></template>
<span>{{ $ts.maintainerEmail }}</span>
</FormInput>
- <FormInput v-model:value="maxNoteTextLength" type="number">
+ <FormTextarea v-model="pinnedUsers">
+ <span>{{ $ts.pinnedUsers }}</span>
+ <template #desc>{{ $ts.pinnedUsersDescription }}</template>
+ </FormTextarea>
+
+ <FormInput v-model="maxNoteTextLength" type="number">
<template #prefix><i class="fas fa-pencil-alt"></i></template>
<span>{{ $ts.maxNoteTextLength }}</span>
</FormInput>
- <FormSwitch v-model:value="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch>
- <FormSwitch v-model:value="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch>
+ <FormSwitch v-model="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch>
+ <FormSwitch v-model="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch>
<FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo>
<FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
@@ -54,14 +59,14 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { fetchInstance } from '@client/instance';
@@ -84,7 +89,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.general,
- icon: 'fas fa-cog'
+ icon: 'fas fa-cog',
+ bg: 'var(--bg)',
},
name: null,
description: null,
@@ -97,6 +103,7 @@ export default defineComponent({
maxNoteTextLength: 0,
enableLocalTimeline: false,
enableGlobalTimeline: false,
+ pinnedUsers: '',
}
},
@@ -118,6 +125,7 @@ export default defineComponent({
this.maxNoteTextLength = meta.maxNoteTextLength;
this.enableLocalTimeline = !meta.disableLocalTimeline;
this.enableGlobalTimeline = !meta.disableGlobalTimeline;
+ this.pinnedUsers = meta.pinnedUsers.join('\n');
},
save() {
@@ -133,6 +141,7 @@ export default defineComponent({
maxNoteTextLength: this.maxNoteTextLength,
disableLocalTimeline: !this.enableLocalTimeline,
disableGlobalTimeline: !this.enableGlobalTimeline,
+ pinnedUsers: this.pinnedUsers.split('\n'),
}).then(() => {
fetchInstance();
});
diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue
index 8db62683ba..f7f9306b70 100644
--- a/src/client/pages/instance/users.vue
+++ b/src/client/pages/instance/users.vue
@@ -1,20 +1,17 @@
<template>
<div class="lknzcolw">
- <div class="actions">
- <MkButton inline primary @click="addUser()"><i class="fas fa-plus"></i> {{ $ts.addUser }}</MkButton>
- <MkButton inline primary @click="lookupUser()"><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton>
- </div>
+ <MkHeader :info="header"/>
<div class="users">
- <div class="inputs" style="display: flex;">
- <MkSelect v-model="sort" style="margin: 0; flex: 1;">
+ <div class="inputs">
+ <MkSelect v-model="sort" style="flex: 1;">
<template #label>{{ $ts.sort }}</template>
<option value="-createdAt">{{ $ts.registeredDate }} ({{ $ts.ascendingOrder }})</option>
<option value="+createdAt">{{ $ts.registeredDate }} ({{ $ts.descendingOrder }})</option>
<option value="-updatedAt">{{ $ts.lastUsed }} ({{ $ts.ascendingOrder }})</option>
<option value="+updatedAt">{{ $ts.lastUsed }} ({{ $ts.descendingOrder }})</option>
</MkSelect>
- <MkSelect v-model="state" style="margin: 0; flex: 1;">
+ <MkSelect v-model="state" style="flex: 1;">
<template #label>{{ $ts.state }}</template>
<option value="all">{{ $ts.all }}</option>
<option value="available">{{ $ts.normal }}</option>
@@ -23,18 +20,20 @@
<option value="silenced">{{ $ts.silence }}</option>
<option value="suspended">{{ $ts.suspend }}</option>
</MkSelect>
- <MkSelect v-model="origin" style="margin: 0; flex: 1;">
+ <MkSelect v-model="origin" style="flex: 1;">
<template #label>{{ $ts.instance }}</template>
<option value="combined">{{ $ts.all }}</option>
<option value="local">{{ $ts.local }}</option>
<option value="remote">{{ $ts.remote }}</option>
</MkSelect>
</div>
- <div class="inputs" style="display: flex; padding-top: 1.2em;">
- <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()">
+ <div class="inputs">
+ <MkInput v-model="searchUsername" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()">
+ <template #prefix>@</template>
<template #label>{{ $ts.username }}</template>
</MkInput>
- <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()" :disabled="pagination.params().origin === 'local'">
+ <MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()" :disabled="pagination.params().origin === 'local'">
+ <template #prefix>@</template>
<template #label>{{ $ts.host }}</template>
</MkInput>
</div>
@@ -67,8 +66,8 @@
<script lang="ts">
import { defineComponent } 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 MkInput from '@client/components/form/input.vue';
+import MkSelect from '@client/components/form/select.vue';
import MkPagination from '@client/components/ui/pagination.vue';
import { acct } from '@client/filters/user';
import * as os from '@client/os';
@@ -90,10 +89,27 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.users,
icon: 'fas fa-users',
- action: {
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.users,
+ icon: 'fas fa-users',
+ bg: 'var(--bg)',
+ actions: [{
icon: 'fas fa-search',
+ text: this.$ts.search,
handler: this.searchUser
- }
+ }, {
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: this.$ts.addUser,
+ handler: this.addUser
+ }, {
+ asFullButton: true,
+ icon: 'fas fa-search',
+ text: this.$ts.lookup,
+ handler: this.lookupUser
+ }]
},
sort: '+createdAt',
state: 'all',
@@ -172,12 +188,21 @@ export default defineComponent({
<style lang="scss" scoped>
.lknzcolw {
- > .actions {
- margin: var(--margin);
- }
-
> .users {
margin: var(--margin);
+
+ > .inputs {
+ display: flex;
+ margin-bottom: 16px;
+
+ > * {
+ margin-right: 16px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
> .users {
margin-top: var(--margin);
diff --git a/src/client/pages/mentions.vue b/src/client/pages/mentions.vue
index 798d3e342d..e1d2f096e1 100644
--- a/src/client/pages/mentions.vue
+++ b/src/client/pages/mentions.vue
@@ -1,6 +1,9 @@
<template>
-<div class="_section">
- <XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/>
+<div>
+ <MkHeader :info="header"/>
+ <div class="_section">
+ <XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/>
+ </div>
</div>
</template>
@@ -19,7 +22,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.mentions,
- icon: 'fas fa-at'
+ icon: 'fas fa-at',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.mentions,
+ icon: 'fas fa-at',
+ bg: 'var(--bg)',
},
pagination: {
endpoint: 'notes/mentions',
diff --git a/src/client/pages/messages.vue b/src/client/pages/messages.vue
index 6ac9746d4e..f4c68daab9 100644
--- a/src/client/pages/messages.vue
+++ b/src/client/pages/messages.vue
@@ -1,6 +1,9 @@
<template>
<div>
- <XNotes :pagination="pagination" @before="before()" @after="after()"/>
+ <MkHeader :info="header"/>
+ <div>
+ <XNotes :pagination="pagination" @before="before()" @after="after()"/>
+ </div>
</div>
</template>
@@ -19,7 +22,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.directNotes,
- icon: 'fas fa-envelope'
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.directNotes,
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
},
pagination: {
endpoint: 'notes/mentions',
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index 1e0d4dc64c..fef3b76e10 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -1,38 +1,42 @@
<template>
-<div class="yweeujhr _root" v-size="{ max: [400] }">
- <MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton>
+<div>
+ <MkHeader :info="header"/>
- <div class="history" v-if="messages.length > 0">
- <MkA v-for="(message, i) in messages"
- class="message _block"
- :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }"
- :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
- :data-index="i"
- :key="message.id"
- v-anim="i"
- >
- <div>
- <MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" :show-indicator="true"/>
- <header v-if="message.groupId">
- <span class="name">{{ message.group.name }}</span>
- <MkTime :time="message.createdAt" class="time"/>
- </header>
- <header v-else>
- <span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span>
- <span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span>
- <MkTime :time="message.createdAt" class="time"/>
- </header>
- <div class="body">
- <p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p>
+ <div class="yweeujhr _root" v-size="{ max: [400] }">
+ <MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton>
+
+ <div class="history" v-if="messages.length > 0">
+ <MkA v-for="(message, i) in messages"
+ class="message _block"
+ :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }"
+ :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
+ :data-index="i"
+ :key="message.id"
+ v-anim="i"
+ >
+ <div>
+ <MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" :show-indicator="true"/>
+ <header v-if="message.groupId">
+ <span class="name">{{ message.group.name }}</span>
+ <MkTime :time="message.createdAt" class="time"/>
+ </header>
+ <header v-else>
+ <span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span>
+ <span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span>
+ <MkTime :time="message.createdAt" class="time"/>
+ </header>
+ <div class="body">
+ <p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p>
+ </div>
</div>
- </div>
- </MkA>
- </div>
- <div class="_fullinfo" v-if="!fetching && messages.length == 0">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
- <div>{{ $ts.noHistory }}</div>
+ </MkA>
+ </div>
+ <div class="_fullinfo" v-if="!fetching && messages.length == 0">
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <div>{{ $ts.noHistory }}</div>
+ </div>
+ <MkLoading v-if="fetching"/>
</div>
- <MkLoading v-if="fetching"/>
</div>
</template>
@@ -53,7 +57,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.messaging,
- icon: 'fas fa-comments'
+ icon: 'fas fa-comments',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.messaging,
+ icon: 'fas fa-comments',
+ bg: 'var(--bg)',
},
fetching: true,
moreFetching: false,
diff --git a/src/client/pages/messaging/messaging-room.message.vue b/src/client/pages/messaging/messaging-room.message.vue
index dfac83ad6a..a2740c0bdc 100644
--- a/src/client/pages/messaging/messaging-room.message.vue
+++ b/src/client/pages/messaging/messaging-room.message.vue
@@ -302,7 +302,7 @@ export default defineComponent({
> .text {
&, ::v-deep(*) {
- color: #fff !important;
+ color: var(--fgOnAccent) !important;
}
}
}
diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue
index b6a2fbd3d4..76e58d5bc9 100644
--- a/src/client/pages/messaging/messaging-room.vue
+++ b/src/client/pages/messaging/messaging-room.vue
@@ -284,7 +284,7 @@ const Component = defineComponent({
},
scrollToBottom() {
- scroll(this.$el, this.$el.offsetHeight);
+ scroll(this.$el, { top: this.$el.offsetHeight });
},
onIndicatorClick() {
diff --git a/src/client/pages/mfm-cheat-sheet.vue b/src/client/pages/mfm-cheat-sheet.vue
index 314b5e2a5f..5ff4317627 100644
--- a/src/client/pages/mfm-cheat-sheet.vue
+++ b/src/client/pages/mfm-cheat-sheet.vue
@@ -286,7 +286,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import * as symbols from '@client/symbols';
export default defineComponent({
diff --git a/src/client/pages/my-antennas/editor.vue b/src/client/pages/my-antennas/editor.vue
index 882d48e643..93ab640030 100644
--- a/src/client/pages/my-antennas/editor.vue
+++ b/src/client/pages/my-antennas/editor.vue
@@ -1,10 +1,10 @@
<template>
<div class="shaynizk">
<div class="form">
- <MkInput v-model="name" class="_inputNoTopMargin">
+ <MkInput v-model="name" class="_formBlock">
<template #label>{{ $ts.name }}</template>
</MkInput>
- <MkSelect v-model="src">
+ <MkSelect v-model="src" class="_formBlock">
<template #label>{{ $ts.antennaSource }}</template>
<option value="all">{{ $ts._antennaSources.all }}</option>
<option value="home">{{ $ts._antennaSources.homeTimeline }}</option>
@@ -12,30 +12,30 @@
<option value="list">{{ $ts._antennaSources.userList }}</option>
<option value="group">{{ $ts._antennaSources.userGroup }}</option>
</MkSelect>
- <MkSelect v-model="userListId" v-if="src === 'list'">
+ <MkSelect v-model="userListId" v-if="src === 'list'" class="_formBlock">
<template #label>{{ $ts.userList }}</template>
<option v-for="list in userLists" :value="list.id" :key="list.id">{{ list.name }}</option>
</MkSelect>
- <MkSelect v-model="userGroupId" v-else-if="src === 'group'">
+ <MkSelect v-model="userGroupId" v-else-if="src === 'group'" class="_formBlock">
<template #label>{{ $ts.userGroup }}</template>
<option v-for="group in userGroups" :value="group.id" :key="group.id">{{ group.name }}</option>
</MkSelect>
- <MkTextarea v-model="users" v-else-if="src === 'users'">
+ <MkTextarea v-model="users" v-else-if="src === 'users'" class="_formBlock">
<template #label>{{ $ts.users }}</template>
<template #caption>{{ $ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ $ts.addUser }}</button></template>
</MkTextarea>
- <MkSwitch v-model="withReplies">{{ $ts.withReplies }}</MkSwitch>
- <MkTextarea v-model="keywords">
+ <MkSwitch v-model="withReplies" class="_formBlock">{{ $ts.withReplies }}</MkSwitch>
+ <MkTextarea v-model="keywords" class="_formBlock">
<template #label>{{ $ts.antennaKeywords }}</template>
<template #caption>{{ $ts.antennaKeywordsDescription }}</template>
</MkTextarea>
- <MkTextarea v-model="excludeKeywords">
+ <MkTextarea v-model="excludeKeywords" class="_formBlock">
<template #label>{{ $ts.antennaExcludeKeywords }}</template>
<template #caption>{{ $ts.antennaKeywordsDescription }}</template>
</MkTextarea>
- <MkSwitch v-model="caseSensitive">{{ $ts.caseSensitive }}</MkSwitch>
- <MkSwitch v-model="withFile">{{ $ts.withFileAntenna }}</MkSwitch>
- <MkSwitch v-model="notify">{{ $ts.notifyAntenna }}</MkSwitch>
+ <MkSwitch v-model="caseSensitive" class="_formBlock">{{ $ts.caseSensitive }}</MkSwitch>
+ <MkSwitch v-model="withFile" class="_formBlock">{{ $ts.withFileAntenna }}</MkSwitch>
+ <MkSwitch v-model="notify" class="_formBlock">{{ $ts.notifyAntenna }}</MkSwitch>
</div>
<div class="actions">
<MkButton inline @click="saveAntenna()" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
@@ -47,10 +47,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkSelect from '@client/components/ui/select.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkSelect from '@client/components/form/select.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import { getAcct } from '@/misc/acct';
import * as os from '@client/os';
diff --git a/src/client/pages/my-groups/index.vue b/src/client/pages/my-groups/index.vue
index 9f153ff9cc..34f82f8a71 100644
--- a/src/client/pages/my-groups/index.vue
+++ b/src/client/pages/my-groups/index.vue
@@ -1,7 +1,7 @@
<template>
<div class="">
<div class="_section" style="padding: 0;">
- <MkTab v-model:value="tab">
+ <MkTab v-model="tab">
<option value="owned">{{ $ts.ownedGroups }}</option>
<option value="joined">{{ $ts.joinedGroups }}</option>
<option value="invites"><i class="fas fa-envelope-open-text"></i> {{ $ts.invites }}</option>
diff --git a/src/client/pages/my-lists/index.vue b/src/client/pages/my-lists/index.vue
index 7de31bb308..b0e9bf9d54 100644
--- a/src/client/pages/my-lists/index.vue
+++ b/src/client/pages/my-lists/index.vue
@@ -1,13 +1,16 @@
<template>
-<div class="qkcjvfiv">
- <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton>
+<div>
+ <MkHeader :info="header"/>
+ <div class="qkcjvfiv">
+ <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton>
- <MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list">
- <MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
- <div class="name">{{ list.name }}</div>
- <MkAvatars :user-ids="list.userIds"/>
- </MkA>
- </MkPagination>
+ <MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list">
+ <MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
+ <div class="name">{{ list.name }}</div>
+ <MkAvatars :user-ids="list.userIds"/>
+ </MkA>
+ </MkPagination>
+ </div>
</div>
</template>
@@ -31,6 +34,12 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.manageLists,
icon: 'fas fa-list-ul',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.manageLists,
+ icon: 'fas fa-list-ul',
+ bg: 'var(--bg)',
action: {
icon: 'fas fa-plus',
handler: this.create
diff --git a/src/client/pages/my-lists/list.vue b/src/client/pages/my-lists/list.vue
index 049d370b4e..27c979bc88 100644
--- a/src/client/pages/my-lists/list.vue
+++ b/src/client/pages/my-lists/list.vue
@@ -1,34 +1,37 @@
<template>
-<div class="mk-list-page">
- <transition name="zoom" mode="out-in">
- <div v-if="list" class="_section">
- <div class="_content">
- <MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
- <MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton>
- <MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton>
+<div>
+ <MkHeader v-if="header" :info="header"/>
+ <div class="mk-list-page">
+ <transition name="zoom" mode="out-in">
+ <div v-if="list" class="_section">
+ <div class="_content">
+ <MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
+ <MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton>
+ <MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton>
+ </div>
</div>
- </div>
- </transition>
+ </transition>
- <transition name="zoom" mode="out-in">
- <div v-if="list" class="_section members _gap">
- <div class="_title">{{ $ts.members }}</div>
- <div class="_content">
- <div class="users">
- <div class="user _panel" v-for="user in users" :key="user.id">
- <MkAvatar :user="user" class="avatar" :show-indicator="true"/>
- <div class="body">
- <MkUserName :user="user" class="name"/>
- <MkAcct :user="user" class="acct"/>
- </div>
- <div class="action">
- <button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button>
+ <transition name="zoom" mode="out-in">
+ <div v-if="list" class="_section members _gap">
+ <div class="_title">{{ $ts.members }}</div>
+ <div class="_content">
+ <div class="users">
+ <div class="user _panel" v-for="user in users" :key="user.id">
+ <MkAvatar :user="user" class="avatar" :show-indicator="true"/>
+ <div class="body">
+ <MkUserName :user="user" class="name"/>
+ <MkAcct :user="user" class="acct"/>
+ </div>
+ <div class="action">
+ <button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button>
+ </div>
</div>
</div>
</div>
</div>
- </div>
- </transition>
+ </transition>
+ </div>
</div>
</template>
@@ -50,6 +53,10 @@ export default defineComponent({
title: this.list.name,
icon: 'fas fa-list-ul',
} : null),
+ header: computed(() => this.list ? {
+ title: this.list.name,
+ icon: 'fas fa-list-ul',
+ } : null),
list: null,
users: [],
};
diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue
index 06f8ad3cba..049d057d02 100644
--- a/src/client/pages/notifications.vue
+++ b/src/client/pages/notifications.vue
@@ -1,15 +1,21 @@
<template>
-<div class="clupoqwt" v-size="{ min: [800] }">
- <XNotifications class="notifications" @before="before" @after="after" page/>
+<div>
+ <MkHeader :info="header"/>
+ <MkSpacer :content-max="800">
+ <div class="clupoqwt">
+ <XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/>
+ </div>
+ </MkSpacer>
</div>
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { computed, defineComponent } from 'vue';
import Progress from '@client/scripts/loading';
import XNotifications from '@client/components/notifications.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
+import { notificationTypes } from '@/types';
export default defineComponent({
components: {
@@ -22,14 +28,35 @@ export default defineComponent({
title: this.$ts.notifications,
icon: 'fas fa-bell',
bg: 'var(--bg)',
+ },
+ tab: 'all',
+ includeTypes: null,
+ header: computed(() => ({
+ title: this.$ts.notifications,
+ icon: 'fas fa-bell',
+ bg: 'var(--bg)',
actions: [{
+ text: this.$ts.filter,
+ icon: 'fas fa-filter',
+ highlighted: this.includeTypes != null,
+ handler: this.setFilter,
+ }, {
text: this.$ts.markAllAsRead,
icon: 'fas fa-check',
handler: () => {
os.apiWithDialog('notifications/mark-all-as-read');
- }
- }]
- },
+ },
+ }],
+ tabs: [{
+ active: this.tab === 'all',
+ title: this.$ts.all,
+ onClick: () => { this.tab = 'all'; },
+ }, {
+ active: this.tab === 'unread',
+ title: this.$ts.unread,
+ onClick: () => { this.tab = 'unread'; },
+ },]
+ })),
};
},
@@ -40,6 +67,24 @@ export default defineComponent({
after() {
Progress.done();
+ },
+
+ setFilter(ev) {
+ const typeItems = notificationTypes.map(t => ({
+ text: this.$t(`_notification._types.${t}`),
+ active: this.includeTypes && this.includeTypes.includes(t),
+ action: () => {
+ this.includeTypes = [t];
+ }
+ }));
+ const items = this.includeTypes != null ? [{
+ icon: 'fas fa-times',
+ text: this.$ts.clear,
+ action: () => {
+ this.includeTypes = null;
+ }
+ }, null, ...typeItems] : typeItems;
+ os.popupMenu(items, ev.currentTarget || ev.target);
}
}
});
@@ -47,14 +92,5 @@ export default defineComponent({
<style lang="scss" scoped>
.clupoqwt {
- &.min-width_800px {
- background: var(--bg);
- padding: 32px 0;
-
- > .notifications {
- max-width: 800px;
- margin: 0 auto;
- }
- }
}
</style>
diff --git a/src/client/pages/page-editor/els/page-editor.el.button.vue b/src/client/pages/page-editor/els/page-editor.el.button.vue
index 3a43817cf6..85e9d7e711 100644
--- a/src/client/pages/page-editor/els/page-editor.el.button.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.button.vue
@@ -40,9 +40,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkSelect from '@client/components/ui/select.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkSelect from '@client/components/form/select.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.canvas.vue b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
index d8d5b990ca..c40d69a7c1 100644
--- a/src/client/pages/page-editor/els/page-editor.el.canvas.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
@@ -22,7 +22,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.counter.vue b/src/client/pages/page-editor/els/page-editor.el.counter.vue
index 973de50fc2..de7994e3ba 100644
--- a/src/client/pages/page-editor/els/page-editor.el.counter.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.counter.vue
@@ -20,7 +20,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.if.vue b/src/client/pages/page-editor/els/page-editor.el.if.vue
index 6eb0c7709f..52f4dac22e 100644
--- a/src/client/pages/page-editor/els/page-editor.el.if.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.if.vue
@@ -19,7 +19,7 @@
</optgroup>
</MkSelect>
- <XBlocks class="children" v-model:value="value.children" :hpml="hpml"/>
+ <XBlocks class="children" v-model="value.children" :hpml="hpml"/>
</section>
</XContainer>
</template>
@@ -28,7 +28,7 @@
import { defineComponent, defineAsyncComponent } from 'vue';
import { v4 as uuid } from 'uuid';
import XContainer from '../page-editor.container.vue';
-import MkSelect from '@client/components/ui/select.vue';
+import MkSelect from '@client/components/form/select.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.note.vue b/src/client/pages/page-editor/els/page-editor.el.note.vue
index 5766564c1a..9feec395b7 100644
--- a/src/client/pages/page-editor/els/page-editor.el.note.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.note.vue
@@ -18,8 +18,8 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import XNote from '@client/components/note.vue';
import XNoteDetailed from '@client/components/note-detailed.vue';
import * as os from '@client/os';
diff --git a/src/client/pages/page-editor/els/page-editor.el.number-input.vue b/src/client/pages/page-editor/els/page-editor.el.number-input.vue
index 892e7e1caa..57b1397824 100644
--- a/src/client/pages/page-editor/els/page-editor.el.number-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.number-input.vue
@@ -20,7 +20,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.post.vue b/src/client/pages/page-editor/els/page-editor.el.post.vue
index 4215b159d3..e21ccfd345 100644
--- a/src/client/pages/page-editor/els/page-editor.el.post.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.post.vue
@@ -13,9 +13,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
index 88be96f35d..62fb231f79 100644
--- a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
@@ -14,8 +14,8 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.section.vue b/src/client/pages/page-editor/els/page-editor.el.section.vue
index 16ef2598f9..75bdf120c0 100644
--- a/src/client/pages/page-editor/els/page-editor.el.section.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.section.vue
@@ -11,7 +11,7 @@
</template>
<section class="ilrvjyvi">
- <XBlocks class="children" v-model:value="value.children" :hpml="hpml"/>
+ <XBlocks class="children" v-model="value.children" :hpml="hpml"/>
</section>
</XContainer>
</template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.switch.vue b/src/client/pages/page-editor/els/page-editor.el.switch.vue
index ade1291410..cf15f58c82 100644
--- a/src/client/pages/page-editor/els/page-editor.el.switch.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.switch.vue
@@ -13,8 +13,8 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.text-input.vue b/src/client/pages/page-editor/els/page-editor.el.text-input.vue
index 3c8fcc04af..210199befd 100644
--- a/src/client/pages/page-editor/els/page-editor.el.text-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.text-input.vue
@@ -13,7 +13,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
index a4fbb08ffe..14f36db2a1 100644
--- a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
@@ -13,8 +13,8 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XContainer from '../page-editor.container.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkInput from '@client/components/form/input.vue';
import * as os from '@client/os';
export default defineComponent({
diff --git a/src/client/pages/page-editor/page-editor.blocks.vue b/src/client/pages/page-editor/page-editor.blocks.vue
index 0065b16c8c..c27162a26e 100644
--- a/src/client/pages/page-editor/page-editor.blocks.vue
+++ b/src/client/pages/page-editor/page-editor.blocks.vue
@@ -32,7 +32,7 @@ export default defineComponent({
},
props: {
- value: {
+ modelValue: {
type: Array,
required: true
},
@@ -41,15 +41,15 @@ export default defineComponent({
},
},
- emits: ['update:value'],
+ emits: ['update:modelValue'],
computed: {
blocks: {
get() {
- return this.value;
+ return this.modelValue;
},
set(value) {
- this.$emit('update:value', value);
+ this.$emit('update:modelValue', value);
}
}
},
@@ -62,17 +62,16 @@ export default defineComponent({
v,
...this.blocks.slice(i + 1)
];
- this.$emit('update:value', newValue);
+ this.$emit('update:modelValue', newValue);
},
removeItem(el) {
- console.log(el);
const i = this.blocks.findIndex(x => x.id === el.id);
const newValue = [
...this.blocks.slice(0, i),
...this.blocks.slice(i + 1)
];
- this.$emit('update:value', newValue);
+ this.$emit('update:modelValue', newValue);
},
}
});
diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue
index fedcd7b317..3313fc1ba9 100644
--- a/src/client/pages/page-editor/page-editor.script-block.vue
+++ b/src/client/pages/page-editor/page-editor.script-block.vue
@@ -7,23 +7,23 @@
</button>
</template>
- <section v-if="value.type === null" class="pbglfege" @click="changeType()">
+ <section v-if="modelValue.type === null" class="pbglfege" @click="changeType()">
{{ $ts._pages.script.emptySlot }}
</section>
- <section v-else-if="value.type === 'text'" class="tbwccoaw">
- <input v-model="value.value"/>
+ <section v-else-if="modelValue.type === 'text'" class="tbwccoaw">
+ <input v-model="modelValue.value"/>
</section>
- <section v-else-if="value.type === 'multiLineText'" class="tbwccoaw">
- <textarea v-model="value.value"></textarea>
+ <section v-else-if="modelValue.type === 'multiLineText'" class="tbwccoaw">
+ <textarea v-model="modelValue.value"></textarea>
</section>
- <section v-else-if="value.type === 'textList'" class="tbwccoaw">
- <textarea v-model="value.value" :placeholder="$ts._pages.script.blocks._textList.info"></textarea>
+ <section v-else-if="modelValue.type === 'textList'" class="tbwccoaw">
+ <textarea v-model="modelValue.value" :placeholder="$ts._pages.script.blocks._textList.info"></textarea>
</section>
- <section v-else-if="value.type === 'number'" class="tbwccoaw">
- <input v-model="value.value" type="number"/>
+ <section v-else-if="modelValue.type === 'number'" class="tbwccoaw">
+ <input v-model="modelValue.value" type="number"/>
</section>
- <section v-else-if="value.type === 'ref'" class="hpdwcrvs">
- <select v-model="value.value">
+ <section v-else-if="modelValue.type === 'ref'" class="hpdwcrvs">
+ <select v-model="modelValue.value">
<option v-for="v in hpml.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
<optgroup :label="$ts._pages.script.argVariables">
<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
@@ -36,21 +36,21 @@
</optgroup>
</select>
</section>
- <section v-else-if="value.type === 'aiScriptVar'" class="tbwccoaw">
- <input v-model="value.value"/>
+ <section v-else-if="modelValue.type === 'aiScriptVar'" class="tbwccoaw">
+ <input v-model="modelValue.value"/>
</section>
- <section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
+ <section v-else-if="modelValue.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
<MkTextarea v-model="slots">
<template #label>{{ $ts._pages.script.blocks._fn.slots }}</template>
<template #caption>{{ $t('_pages.script.blocks._fn.slots-info') }}</template>
</MkTextarea>
- <XV v-if="value.value.expression" v-model:value="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="value.value.slots" :name="name"/>
+ <XV v-if="modelValue.value.expression" v-model="modelValue.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="value.value.slots" :name="name"/>
</section>
- <section v-else-if="value.type.startsWith('fn:')" class="" style="padding:16px;">
- <XV v-for="(x, i) in value.args" v-model:value="value.args[i]" :title="hpml.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :hpml="hpml" :name="name" :key="i"/>
+ <section v-else-if="modelValue.type.startsWith('fn:')" class="" style="padding:16px;">
+ <XV v-for="(x, i) in modelValue.args" v-model="value.args[i]" :title="hpml.getVarByName(modelValue.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :hpml="hpml" :name="name" :key="i"/>
</section>
<section v-else class="" style="padding:16px;">
- <XV v-for="(x, i) in value.args" v-model:value="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :hpml="hpml" :name="name" :fn-slots="fnSlots" :key="i"/>
+ <XV v-for="(x, i) in modelValue.args" v-model="modelValue.args[i]" :title="$t(`_pages.script.blocks._${modelValue.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :hpml="hpml" :name="name" :fn-slots="fnSlots" :key="i"/>
</section>
</XContainer>
</template>
@@ -59,7 +59,7 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import { v4 as uuid } from 'uuid';
import XContainer from './page-editor.container.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import { blockDefs } from '@client/scripts/hpml/index';
import * as os from '@client/os';
import { isLiteralValue } from '@client/scripts/hpml/expr';
@@ -78,7 +78,7 @@ export default defineComponent({
required: false,
default: null
},
- value: {
+ modelValue: {
required: true
},
title: {
@@ -113,21 +113,21 @@ export default defineComponent({
computed: {
icon(): any {
- if (this.value.type === null) return null;
- if (this.value.type.startsWith('fn:')) return 'fas fa-plug';
- return blockDefs.find(x => x.type === this.value.type).icon;
+ if (this.modelValue.type === null) return null;
+ if (this.modelValue.type.startsWith('fn:')) return 'fas fa-plug';
+ return blockDefs.find(x => x.type === this.modelValue.type).icon;
},
typeText(): any {
- if (this.value.type === null) return null;
- if (this.value.type.startsWith('fn:')) return this.value.type.split(':')[1];
- return this.$t(`_pages.script.blocks.${this.value.type}`);
+ if (this.modelValue.type === null) return null;
+ if (this.modelValue.type.startsWith('fn:')) return this.modelValue.type.split(':')[1];
+ return this.$t(`_pages.script.blocks.${this.modelValue.type}`);
},
},
watch: {
slots: {
handler() {
- this.value.value.slots = this.slots.split('\n').map(x => ({
+ this.modelValue.value.slots = this.slots.split('\n').map(x => ({
name: x,
type: null
}));
@@ -137,24 +137,24 @@ export default defineComponent({
},
created() {
- if (this.value.value == null) this.value.value = null;
+ if (this.modelValue.value == null) this.modelValue.value = null;
- if (this.value.value && this.value.value.slots) this.slots = this.value.value.slots.map(x => x.name).join('\n');
+ if (this.modelValue.value && this.modelValue.value.slots) this.slots = this.modelValue.value.slots.map(x => x.name).join('\n');
- this.$watch(() => this.value.type, (t) => {
+ this.$watch(() => this.modelValue.type, (t) => {
this.warn = null;
- if (this.value.type === 'fn') {
+ if (this.modelValue.type === 'fn') {
const id = uuid();
- this.value.value = {
+ this.modelValue.value = {
slots: [],
expression: { id, type: null }
};
return;
}
- if (this.value.type && this.value.type.startsWith('fn:')) {
- const fnName = this.value.type.split(':')[1];
+ if (this.modelValue.type && this.modelValue.type.startsWith('fn:')) {
+ const fnName = this.modelValue.type.split(':')[1];
const fn = this.hpml.getVarByName(fnName);
const empties = [];
@@ -162,29 +162,29 @@ export default defineComponent({
const id = uuid();
empties.push({ id, type: null });
}
- this.value.args = empties;
+ this.modelValue.args = empties;
return;
}
- if (isLiteralValue(this.value)) return;
+ if (isLiteralValue(this.modelValue)) return;
const empties = [];
- for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
+ for (let i = 0; i < funcDefs[this.modelValue.type].in.length; i++) {
const id = uuid();
empties.push({ id, type: null });
}
- this.value.args = empties;
+ this.modelValue.args = empties;
- for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
- const inType = funcDefs[this.value.type].in[i];
+ for (let i = 0; i < funcDefs[this.modelValue.type].in.length; i++) {
+ const inType = funcDefs[this.modelValue.type].in[i];
if (typeof inType !== 'number') {
- if (inType === 'number') this.value.args[i].type = 'number';
- if (inType === 'string') this.value.args[i].type = 'text';
+ if (inType === 'number') this.modelValue.args[i].type = 'number';
+ if (inType === 'string') this.modelValue.args[i].type = 'text';
}
}
});
- this.$watch(() => this.value.args, (args) => {
+ this.$watch(() => this.modelValue.args, (args) => {
if (args == null) {
this.warn = null;
return;
@@ -202,8 +202,8 @@ export default defineComponent({
});
this.$watch(() => this.hpml.variables, () => {
- if (this.type != null && this.value) {
- this.error = this.hpml.typeCheck(this.value);
+ if (this.type != null && this.modelValue) {
+ this.error = this.hpml.typeCheck(this.modelValue);
}
}, {
deep: true
@@ -221,11 +221,11 @@ export default defineComponent({
showCancelButton: true
});
if (canceled) return;
- this.value.type = type;
+ this.modelValue.type = type;
},
_getExpectedType(slot: number) {
- return this.hpml.getExpectedType(this.value, slot);
+ return this.hpml.getExpectedType(this.modelValue, slot);
}
}
});
diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue
index dc6896ba12..e04039e634 100644
--- a/src/client/pages/page-editor/page-editor.vue
+++ b/src/client/pages/page-editor/page-editor.vue
@@ -1,85 +1,84 @@
<template>
-<div class="_root">
- <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkA>
+<div>
+ <MkHeader :info="header"/>
- <div class="buttons" style="margin: 16px;">
- <MkButton inline @click="save" primary class="save" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton>
- <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton>
- </div>
+ <div class="_root">
+ <div class="jqqmcavi" style="margin: 16px;">
+ <MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkButton>
+ <MkButton inline @click="save" primary class="button" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+ <MkButton inline @click="duplicate" class="button" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton>
+ <MkButton inline @click="del" class="button" v-if="pageId && !readonly" danger><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton>
+ </div>
- <MkContainer :foldable="true" :expanded="true" class="_gap">
- <template #header><i class="fas fa-cog"></i> {{ $ts._pages.pageSetting }}</template>
- <div style="padding: 16px;">
- <MkInput v-model="title">
- <template #label>{{ $ts._pages.title }}</template>
- </MkInput>
+ <div v-if="tab === 'settings'">
+ <div style="padding: 16px;" class="_formRoot">
+ <MkInput v-model="title" class="_formBlock">
+ <template #label>{{ $ts._pages.title }}</template>
+ </MkInput>
- <MkInput v-model="summary">
- <template #label>{{ $ts._pages.summary }}</template>
- </MkInput>
+ <MkInput v-model="summary" class="_formBlock">
+ <template #label>{{ $ts._pages.summary }}</template>
+ </MkInput>
- <MkInput v-model="name">
- <template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
- <template #label>{{ $ts._pages.url }}</template>
- </MkInput>
+ <MkInput v-model="name" class="_formBlock">
+ <template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
+ <template #label>{{ $ts._pages.url }}</template>
+ </MkInput>
- <MkSwitch v-model="alignCenter">{{ $ts._pages.alignCenter }}</MkSwitch>
+ <MkSwitch v-model="alignCenter" class="_formBlock">{{ $ts._pages.alignCenter }}</MkSwitch>
- <MkSelect v-model="font">
- <template #label>{{ $ts._pages.font }}</template>
- <option value="serif">{{ $ts._pages.fontSerif }}</option>
- <option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option>
- </MkSelect>
+ <MkSelect v-model="font" class="_formBlock">
+ <template #label>{{ $ts._pages.font }}</template>
+ <option value="serif">{{ $ts._pages.fontSerif }}</option>
+ <option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option>
+ </MkSelect>
- <MkSwitch v-model="hideTitleWhenPinned">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch>
+ <MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch>
- <div class="eyeCatch">
- <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="fas fa-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton>
- <div v-else-if="eyeCatchingImage">
- <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/>
- <MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><i class="fas fa-trash-alt"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton>
+ <div class="eyeCatch">
+ <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="fas fa-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton>
+ <div v-else-if="eyeCatchingImage">
+ <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/>
+ <MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><i class="fas fa-trash-alt"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton>
+ </div>
</div>
</div>
</div>
- </MkContainer>
- <MkContainer :foldable="true" :expanded="true" class="_gap">
- <template #header><i class="fas fa-sticky-note"></i> {{ $ts._pages.contents }}</template>
- <div style="padding: 16px;">
- <XBlocks class="content" v-model:value="content" :hpml="hpml"/>
+ <div v-else-if="tab === 'contents'">
+ <div style="padding: 16px;">
+ <XBlocks class="content" v-model="content" :hpml="hpml"/>
- <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton>
+ <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton>
+ </div>
</div>
- </MkContainer>
- <MkContainer :foldable="true" class="_gap">
- <template #header><i class="fas fa-magic"></i> {{ $ts._pages.variables }}</template>
- <div class="qmuvgica">
- <XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
- <template #item="{element}">
- <XVariable
- :value="element"
- :removable="true"
- @remove="() => removeVariable(element)"
- :hpml="hpml"
- :name="element.name"
- :title="element.name"
- :draggable="true"
- />
- </template>
- </XDraggable>
+ <div v-else-if="tab === 'variables'">
+ <div class="qmuvgica">
+ <XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
+ <template #item="{element}">
+ <XVariable
+ :modelValue="element"
+ :removable="true"
+ @remove="() => removeVariable(element)"
+ :hpml="hpml"
+ :name="element.name"
+ :title="element.name"
+ :draggable="true"
+ />
+ </template>
+ </XDraggable>
- <MkButton @click="addVariable()" class="add" v-if="!readonly"><i class="fas fa-plus"></i></MkButton>
+ <MkButton @click="addVariable()" class="add" v-if="!readonly"><i class="fas fa-plus"></i></MkButton>
+ </div>
</div>
- </MkContainer>
- <MkContainer :foldable="true" :expanded="true" class="_gap">
- <template #header><i class="fas fa-code"></i> {{ $ts.script }}</template>
- <div>
- <MkTextarea class="_code" v-model="script"/>
+ <div v-else-if="tab === 'script'">
+ <div>
+ <MkTextarea class="_code" v-model="script"/>
+ </div>
</div>
- </MkContainer>
+ </div>
</div>
</template>
@@ -94,12 +93,12 @@ import 'vue-prism-editor/dist/prismeditor.min.css';
import { v4 as uuid } from 'uuid';
import XVariable from './page-editor.script-block.vue';
import XBlocks from './page-editor.blocks.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
import MkContainer from '@client/components/ui/container.vue';
import MkButton from '@client/components/ui/button.vue';
-import MkSelect from '@client/components/ui/select.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkSelect from '@client/components/form/select.vue';
+import MkSwitch from '@client/components/form/switch.vue';
+import MkInput from '@client/components/form/input.vue';
import { blockDefs } from '@client/scripts/hpml/index';
import { HpmlTypeChecker } from '@client/scripts/hpml/type-checker';
import { url } from '@client/config';
@@ -142,6 +141,43 @@ export default defineComponent({
return {
title: title,
icon: 'fas fa-pencil-alt',
+ bg: 'var(--bg)',
+ };
+ }),
+ tab: 'settings',
+ header: computed(() => {
+ let title = this.$ts._pages.newPage;
+ if (this.initPageId) {
+ title = this.$ts._pages.editPage;
+ }
+ else if (this.initPageName && this.initUser) {
+ title = this.$ts._pages.readPage;
+ }
+ return {
+ title: title,
+ icon: 'fas fa-pencil-alt',
+ bg: 'var(--bg)',
+ tabs: [{
+ active: this.tab === 'settings',
+ title: this.$ts._pages.pageSetting,
+ icon: 'fas fa-cog',
+ onClick: () => { this.tab = 'settings'; },
+ }, {
+ active: this.tab === 'contents',
+ title: this.$ts._pages.contents,
+ icon: 'fas fa-sticky-note',
+ onClick: () => { this.tab = 'contents'; },
+ }, {
+ active: this.tab === 'variables',
+ title: this.$ts._pages.variables,
+ icon: 'fas fa-magic',
+ onClick: () => { this.tab = 'variables'; },
+ }, {
+ active: this.tab === 'script',
+ title: this.$ts.script,
+ icon: 'fas fa-code',
+ onClick: () => { this.tab = 'script'; },
+ }]
};
}),
author: this.$i,
@@ -455,6 +491,14 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
+.jqqmcavi {
+ > .button {
+ & + .button {
+ margin-left: 8px;
+ }
+ }
+}
+
.gwbmwxkm {
position: relative;
@@ -522,11 +566,7 @@ export default defineComponent({
}
.qmuvgica {
- padding: 32px;
-
- @media (max-width: 500px) {
- padding: 16px;
- }
+ padding: 16px;
> .variables {
margin-bottom: 16px;
diff --git a/src/client/pages/page.vue b/src/client/pages/page.vue
index 47a458df9c..b8d7507363 100644
--- a/src/client/pages/page.vue
+++ b/src/client/pages/page.vue
@@ -1,61 +1,65 @@
<template>
-<div class="_root">
- <transition name="fade" mode="out-in">
- <div v-if="page" class="xcukqgmh" :key="page.id" v-size="{ max: [450] }">
- <div class="_block main">
- <!--
- <div class="header">
- <h1>{{ page.title }}</h1>
- </div>
- -->
- <div class="banner">
- <img :src="page.eyeCatchingImage.url" v-if="page.eyeCatchingImageId"/>
- </div>
- <div class="content">
- <XPage :page="page"/>
- </div>
- <div class="actions">
- <div class="like">
- <MkButton class="button" @click="unlike()" v-if="page.isLiked" v-tooltip="$ts._pages.unlike" primary><i class="fas fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton>
- <MkButton class="button" @click="like()" v-else v-tooltip="$ts._pages.like"><i class="far fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton>
+<div>
+ <MkHeader :info="header"/>
+
+ <div class="_root">
+ <transition name="fade" mode="out-in">
+ <div v-if="page" class="xcukqgmh" :key="page.id" v-size="{ max: [450] }">
+ <div class="_block main">
+ <!--
+ <div class="header">
+ <h1>{{ page.title }}</h1>
</div>
- <div class="other">
- <button class="_button" @click="shareWithNote" v-tooltip="$ts.shareWithNote" v-click-anime><i class="fas fa-retweet fa-fw"></i></button>
- <button class="_button" @click="share" v-tooltip="$ts.share" v-click-anime><i class="fas fa-share-alt fa-fw"></i></button>
+ -->
+ <div class="banner">
+ <img :src="page.eyeCatchingImage.url" v-if="page.eyeCatchingImageId"/>
</div>
- </div>
- <div class="user">
- <MkAvatar :user="page.user" class="avatar"/>
- <div class="name">
- <MkUserName :user="page.user" style="display: block;"/>
- <MkAcct :user="page.user"/>
+ <div class="content">
+ <XPage :page="page"/>
+ </div>
+ <div class="actions">
+ <div class="like">
+ <MkButton class="button" @click="unlike()" v-if="page.isLiked" v-tooltip="$ts._pages.unlike" primary><i class="fas fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton>
+ <MkButton class="button" @click="like()" v-else v-tooltip="$ts._pages.like"><i class="far fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton>
+ </div>
+ <div class="other">
+ <button class="_button" @click="shareWithNote" v-tooltip="$ts.shareWithNote" v-click-anime><i class="fas fa-retweet fa-fw"></i></button>
+ <button class="_button" @click="share" v-tooltip="$ts.share" v-click-anime><i class="fas fa-share-alt fa-fw"></i></button>
+ </div>
+ </div>
+ <div class="user">
+ <MkAvatar :user="page.user" class="avatar"/>
+ <div class="name">
+ <MkUserName :user="page.user" style="display: block;"/>
+ <MkAcct :user="page.user"/>
+ </div>
+ <MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
+ </div>
+ <div class="links">
+ <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA>
+ <template v-if="$i && $i.id === page.userId">
+ <MkA :to="`/pages/edit/${page.id}`" class="link">{{ $ts._pages.editThisPage }}</MkA>
+ <button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $ts.unpin }}</button>
+ <button v-else @click="pin(true)" class="link _textButton">{{ $ts.pin }}</button>
+ </template>
</div>
- <MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
</div>
- <div class="links">
- <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA>
- <template v-if="$i && $i.id === page.userId">
- <MkA :to="`/pages/edit/${page.id}`" class="link">{{ $ts._pages.editThisPage }}</MkA>
- <button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $ts.unpin }}</button>
- <button v-else @click="pin(true)" class="link _textButton">{{ $ts.pin }}</button>
- </template>
+ <div class="footer">
+ <div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
+ <div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
</div>
+ <MkAd :prefer="['horizontal', 'horizontal-big']"/>
+ <MkContainer :max-height="300" :foldable="true" class="other">
+ <template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template>
+ <MkPagination :pagination="otherPostsPagination" #default="{items}">
+ <MkPagePreview v-for="page in items" :page="page" :key="page.id" class="_gap"/>
+ </MkPagination>
+ </MkContainer>
</div>
- <div class="footer">
- <div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
- <div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
- </div>
- <MkAd :prefer="['horizontal', 'horizontal-big']"/>
- <MkContainer :max-height="300" :foldable="true" class="other">
- <template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template>
- <MkPagination :pagination="otherPostsPagination" #default="{items}">
- <MkPagePreview v-for="page in items" :page="page" :key="page.id" class="_gap"/>
- </MkPagination>
- </MkContainer>
- </div>
- <MkError v-else-if="error" @retry="fetch()"/>
- <MkLoading v-else/>
- </transition>
+ <MkError v-else-if="error" @retry="fetch()"/>
+ <MkLoading v-else/>
+ </transition>
+ </div>
</div>
</template>
@@ -97,6 +101,10 @@ export default defineComponent({
[symbols.PAGE_INFO]: computed(() => this.page ? {
title: computed(() => this.page.title || this.page.name),
avatar: this.page.user,
+ } : null),
+ header: computed(() => this.page ? {
+ title: computed(() => this.page.title || this.page.name),
+ avatar: this.page.user,
path: `/@${this.page.user.username}/pages/${this.page.name}`,
share: {
title: this.page.title || this.page.name,
diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue
index 52a860be13..8300e8a6e4 100644
--- a/src/client/pages/pages.vue
+++ b/src/client/pages/pages.vue
@@ -1,31 +1,36 @@
<template>
<div>
- <MkTab v-model:value="tab" v-if="$i">
- <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option>
- <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option>
- <option value="liked"><i class="fas fa-heart"></i> {{ $ts._pages.liked }}</option>
- </MkTab>
+ <MkHeader :info="header"/>
- <div class="_section">
- <div class="rknalgpo _content" v-if="tab === 'featured'">
- <MkPagination :pagination="featuredPagesPagination" #default="{items}">
- <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/>
- </MkPagination>
- </div>
+ <MkSpacer>
+ <!-- TODO: MkHeaderに統合 -->
+ <MkTab v-model="tab" v-if="$i">
+ <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option>
+ <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option>
+ <option value="liked"><i class="fas fa-heart"></i> {{ $ts._pages.liked }}</option>
+ </MkTab>
- <div class="rknalgpo _content my" v-if="tab === 'my'">
- <MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton>
- <MkPagination :pagination="myPagesPagination" #default="{items}">
- <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/>
- </MkPagination>
- </div>
+ <div class="_section">
+ <div class="rknalgpo _content" v-if="tab === 'featured'">
+ <MkPagination :pagination="featuredPagesPagination" #default="{items}">
+ <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/>
+ </MkPagination>
+ </div>
+
+ <div class="rknalgpo _content my" v-if="tab === 'my'">
+ <MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton>
+ <MkPagination :pagination="myPagesPagination" #default="{items}">
+ <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/>
+ </MkPagination>
+ </div>
- <div class="rknalgpo _content" v-if="tab === 'liked'">
- <MkPagination :pagination="likedPagesPagination" #default="{items}">
- <MkPagePreview v-for="like in items" class="ckltabjg" :page="like.page" :key="like.page.id"/>
- </MkPagination>
+ <div class="rknalgpo _content" v-if="tab === 'liked'">
+ <MkPagination :pagination="likedPagesPagination" #default="{items}">
+ <MkPagePreview v-for="like in items" class="ckltabjg" :page="like.page" :key="like.page.id"/>
+ </MkPagination>
+ </div>
</div>
- </div>
+ </MkSpacer>
</div>
</template>
@@ -46,11 +51,17 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.pages,
icon: 'fas fa-sticky-note',
+ bg: 'var(--bg)',
+ },
+ header: {
+ title: this.$ts.pages,
+ icon: 'fas fa-sticky-note',
+ bg: 'var(--bg)',
actions: [{
icon: 'fas fa-plus',
text: this.$ts.create,
- handler: this.create
- }]
+ handler: this.create,
+ }],
},
tab: 'featured',
featuredPagesPagination: {
diff --git a/src/client/pages/reset-password.vue b/src/client/pages/reset-password.vue
index c331382132..6dd9f24259 100644
--- a/src/client/pages/reset-password.vue
+++ b/src/client/pages/reset-password.vue
@@ -1,6 +1,6 @@
<template>
<FormBase v-if="token">
- <FormInput v-model:value="password" type="password">
+ <FormInput v-model="password" type="password">
<template #prefix><i class="fas fa-lock"></i></template>
<span>{{ $ts.newPassword }}</span>
</FormInput>
@@ -11,11 +11,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/reversi/game.setting.vue b/src/client/pages/reversi/game.setting.vue
index 1cc623b790..eb6f24e4ab 100644
--- a/src/client/pages/reversi/game.setting.vue
+++ b/src/client/pages/reversi/game.setting.vue
@@ -127,8 +127,8 @@
import { defineComponent } from 'vue';
import * as maps from '../../../games/reversi/maps';
import MkButton from '@client/components/ui/button.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
-import MkRadio from '@client/components/ui/radio.vue';
+import MkSwitch from '@client/components/form/switch.vue';
+import MkRadio from '@client/components/form/radio.vue';
export default defineComponent({
components: {
@@ -303,7 +303,7 @@ export default defineComponent({
-moz-appearance: none;
appearance: none;
- &:focus,
+ &:focus-visible,
&:active {
border-color: var(--accent);
}
diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue
index 365ed5b803..671dca3577 100644
--- a/src/client/pages/room/room.vue
+++ b/src/client/pages/room/room.vue
@@ -57,7 +57,7 @@ import XPreview from './preview.vue';
const storeItems = require('@client/scripts/room/furnitures.json5');
import { query as urlQuery } from '../../../prelude/url';
import MkButton from '@client/components/ui/button.vue';
-import MkSelect from '@client/components/ui/select.vue';
+import MkSelect from '@client/components/form/select.vue';
import { selectFile } from '@client/scripts/select-file';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
diff --git a/src/client/pages/search.vue b/src/client/pages/search.vue
index bf228576be..fec138726f 100644
--- a/src/client/pages/search.vue
+++ b/src/client/pages/search.vue
@@ -1,7 +1,10 @@
<template>
-<div class="_section">
- <div class="_content">
- <XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/>
+<div>
+ <MkHeader :info="header"/>
+ <div class="_section">
+ <div class="_content">
+ <XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/>
+ </div>
</div>
</div>
</template>
@@ -21,7 +24,11 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: computed(() => this.$t('searchWith', { q: this.$route.query.q })),
- icon: 'fas fa-search'
+ icon: 'fas fa-search',
+ },
+ header: {
+ title: computed(() => this.$t('searchWith', { q: this.$route.query.q })),
+ icon: 'fas fa-search',
},
pagination: {
endpoint: 'notes/search',
diff --git a/src/client/pages/settings/2fa.vue b/src/client/pages/settings/2fa.vue
index 48b06eaa24..386e7c635a 100644
--- a/src/client/pages/settings/2fa.vue
+++ b/src/client/pages/settings/2fa.vue
@@ -72,11 +72,11 @@ import { hostname } from '@client/config';
import { byteify, hexify, stringify } from '@client/scripts/2fa';
import MkButton from '@client/components/ui/button.vue';
import MkInfo from '@client/components/ui/info.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/settings/account-info.vue b/src/client/pages/settings/account-info.vue
index 4d851b7b12..16ce91b12f 100644
--- a/src/client/pages/settings/account-info.vue
+++ b/src/client/pages/settings/account-info.vue
@@ -134,11 +134,11 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
import * as os from '@client/os';
import number from '@client/filters/number';
import bytes from '@client/filters/bytes';
diff --git a/src/client/pages/settings/accounts.vue b/src/client/pages/settings/accounts.vue
index ca6f53776a..d2966cc216 100644
--- a/src/client/pages/settings/accounts.vue
+++ b/src/client/pages/settings/accounts.vue
@@ -3,8 +3,8 @@
<FormSuspense :p="init">
<FormButton @click="addAccount" primary><i class="fas fa-plus"></i> {{ $ts.addAccount }}</FormButton>
- <div class="_formItem _button" v-for="account in accounts" :key="account.id" @click="menu(account, $event)">
- <div class="_formPanel lcjjdxlm">
+ <div class="_debobigegoItem _button" v-for="account in accounts" :key="account.id" @click="menu(account, $event)">
+ <div class="_debobigegoPanel lcjjdxlm">
<div class="avatar">
<MkAvatar :user="account" class="avatar"/>
</div>
@@ -24,11 +24,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSuspense from '@client/components/form/suspense.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import { getAccounts, addAccount, login } from '@client/account';
@@ -47,6 +47,7 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.accounts,
icon: 'fas fa-users',
+ bg: 'var(--bg)',
},
storedAccounts: getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)),
accounts: null,
diff --git a/src/client/pages/settings/api.vue b/src/client/pages/settings/api.vue
index 396d4405c3..5c7496e2f9 100644
--- a/src/client/pages/settings/api.vue
+++ b/src/client/pages/settings/api.vue
@@ -10,10 +10,10 @@
import { defineComponent } from 'vue';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -30,7 +30,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: 'API',
- icon: 'fas fa-key'
+ icon: 'fas fa-key',
+ bg: 'var(--bg)',
},
isDesktop: window.innerWidth >= 1100,
};
diff --git a/src/client/pages/settings/apps.vue b/src/client/pages/settings/apps.vue
index c864920ce1..da4f672adf 100644
--- a/src/client/pages/settings/apps.vue
+++ b/src/client/pages/settings/apps.vue
@@ -8,7 +8,7 @@
</div>
</template>
<template #default="{items}">
- <div class="_formPanel bfomjevm" v-for="token in items" :key="token.id">
+ <div class="_debobigegoPanel bfomjevm" v-for="token in items" :key="token.id">
<img class="icon" :src="token.iconUrl" alt="" v-if="token.iconUrl"/>
<div class="body">
<div class="name">{{ token.name }}</div>
@@ -39,12 +39,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormPagination from '@client/components/form/pagination.vue';
+import FormPagination from '@client/components/debobigego/pagination.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -61,6 +61,7 @@ export default defineComponent({
[symbols.PAGE_INFO]: {
title: this.$ts.installedApps,
icon: 'fas fa-plug',
+ bg: 'var(--bg)',
},
pagination: {
endpoint: 'i/apps',
diff --git a/src/client/pages/settings/custom-css.vue b/src/client/pages/settings/custom-css.vue
index 0781eeebd7..fd473a11fa 100644
--- a/src/client/pages/settings/custom-css.vue
+++ b/src/client/pages/settings/custom-css.vue
@@ -2,7 +2,7 @@
<FormBase>
<FormInfo warn>{{ $ts.customCssWarn }}</FormInfo>
- <FormTextarea v-model:value="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;">
+ <FormTextarea v-model="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;">
<span>{{ $ts.local }}</span>
</FormTextarea>
</FormBase>
@@ -13,11 +13,11 @@ import { defineComponent } from 'vue';
import FormTextarea from '@client/components/form/textarea.vue';
import FormSelect from '@client/components/form/select.vue';
import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormInfo from '@client/components/form/info.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
import { unisonReload } from '@client/scripts/unison-reload';
@@ -42,7 +42,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.customCss,
- icon: 'fas fa-code'
+ icon: 'fas fa-code',
+ bg: 'var(--bg)',
},
localCustomCss: localStorage.getItem('customCss')
}
diff --git a/src/client/pages/settings/deck.vue b/src/client/pages/settings/deck.vue
index 05f3061ca1..e4b5c697c4 100644
--- a/src/client/pages/settings/deck.vue
+++ b/src/client/pages/settings/deck.vue
@@ -2,10 +2,10 @@
<FormBase>
<FormGroup>
<template #label>{{ $ts.defaultNavigationBehaviour }}</template>
- <FormSwitch v-model:value="navWindow">{{ $ts.openInWindow }}</FormSwitch>
+ <FormSwitch v-model="navWindow">{{ $ts.openInWindow }}</FormSwitch>
</FormGroup>
- <FormSwitch v-model:value="alwaysShowMainColumn">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch>
+ <FormSwitch v-model="alwaysShowMainColumn">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch>
<FormRadios v-model="columnAlign">
<template #desc>{{ $ts._deck.columnAlign }}</template>
@@ -20,7 +20,7 @@
<option :value="48">{{ $ts.wide }}</option>
</FormRadios>
- <FormInput v-model:value="columnMargin" type="number">
+ <FormInput v-model="columnMargin" type="number">
<span>{{ $ts._deck.columnMargin }}</span>
<template #suffix>px</template>
</FormInput>
@@ -31,12 +31,12 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormRadios from '@client/components/form/radios.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormRadios from '@client/components/debobigego/radios.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import { deckStore } from '@client/ui/deck/deck-store';
import * as os from '@client/os';
import { unisonReload } from '@client/scripts/unison-reload';
@@ -58,7 +58,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.deck,
- icon: 'fas fa-columns'
+ icon: 'fas fa-columns',
+ bg: 'var(--bg)',
},
}
},
diff --git a/src/client/pages/settings/delete-account.vue b/src/client/pages/settings/delete-account.vue
index 3af1879857..6bac214e04 100644
--- a/src/client/pages/settings/delete-account.vue
+++ b/src/client/pages/settings/delete-account.vue
@@ -9,10 +9,10 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import { debug } from '@client/config';
import { signout } from '@client/account';
@@ -32,7 +32,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts._accountDelete.accountDelete,
- icon: 'fas fa-exclamation-triangle'
+ icon: 'fas fa-exclamation-triangle',
+ bg: 'var(--bg)',
},
debug,
}
diff --git a/src/client/pages/settings/drive.vue b/src/client/pages/settings/drive.vue
index 83068a8335..177bf058f3 100644
--- a/src/client/pages/settings/drive.vue
+++ b/src/client/pages/settings/drive.vue
@@ -2,8 +2,8 @@
<FormBase class="">
<FormGroup v-if="!fetching">
<template #label>{{ $ts.usageAmount }}</template>
- <div class="_formItem uawsfosz">
- <div class="_formPanel">
+ <div class="_debobigegoItem uawsfosz">
+ <div class="_debobigegoPanel">
<div class="meter"><div :style="meterStyle"></div></div>
</div>
</div>
@@ -17,9 +17,9 @@
</FormKeyValueView>
</FormGroup>
- <div class="_formItem">
- <div class="_formLabel">{{ $ts.statistics }}</div>
- <div class="_formPanel">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoLabel">{{ $ts.statistics }}</div>
+ <div class="_debobigegoPanel">
<div ref="chart"></div>
</div>
</div>
@@ -36,10 +36,10 @@
import { defineComponent } from 'vue';
import * as tinycolor from 'tinycolor2';
import ApexCharts from 'apexcharts';
-import FormButton from '@client/components/form/button.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormBase from '@client/components/form/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormBase from '@client/components/debobigego/base.vue';
import * as os from '@client/os';
import bytes from '@client/filters/bytes';
import * as symbols from '@client/symbols';
@@ -58,7 +58,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.drive,
- icon: 'fas fa-cloud'
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
},
fetching: true,
usage: null,
diff --git a/src/client/pages/settings/email-address.vue b/src/client/pages/settings/email-address.vue
index 28eeeb6b73..f98b22ada7 100644
--- a/src/client/pages/settings/email-address.vue
+++ b/src/client/pages/settings/email-address.vue
@@ -1,7 +1,7 @@
<template>
<FormBase>
<FormGroup>
- <FormInput v-model:value="emailAddress" type="email">
+ <FormInput v-model="emailAddress" type="email">
{{ $ts.emailAddress }}
<template #desc v-if="$i.email && !$i.emailVerified">{{ $ts.verificationEmailSent }}</template>
<template #desc v-else-if="emailAddress === $i.email && $i.emailVerified">{{ $ts.emailVerified }}</template>
@@ -13,10 +13,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormButton from '@client/components/form/button.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import FormInput from '@client/components/form/input.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -34,7 +34,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.emailAddress,
- icon: 'fas fa-envelope'
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
},
emailAddress: null,
code: null,
diff --git a/src/client/pages/settings/email-notification.vue b/src/client/pages/settings/email-notification.vue
index ac3402568a..1b78621c3f 100644
--- a/src/client/pages/settings/email-notification.vue
+++ b/src/client/pages/settings/email-notification.vue
@@ -1,22 +1,22 @@
<template>
<FormBase>
<FormGroup>
- <FormSwitch v-model:value="mention">
+ <FormSwitch v-model="mention">
{{ $ts._notification._types.mention }}
</FormSwitch>
- <FormSwitch v-model:value="reply">
+ <FormSwitch v-model="reply">
{{ $ts._notification._types.reply }}
</FormSwitch>
- <FormSwitch v-model:value="quote">
+ <FormSwitch v-model="quote">
{{ $ts._notification._types.quote }}
</FormSwitch>
- <FormSwitch v-model:value="follow">
+ <FormSwitch v-model="follow">
{{ $ts._notification._types.follow }}
</FormSwitch>
- <FormSwitch v-model:value="receiveFollowRequest">
+ <FormSwitch v-model="receiveFollowRequest">
{{ $ts._notification._types.receiveFollowRequest }}
</FormSwitch>
- <FormSwitch v-model:value="groupInvited">
+ <FormSwitch v-model="groupInvited">
{{ $ts._notification._types.groupInvited }}
</FormSwitch>
</FormGroup>
@@ -25,10 +25,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormButton from '@client/components/form/button.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import FormSwitch from '@client/components/form/switch.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
import * as symbols from '@client/symbols';
@@ -47,7 +47,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.emailNotification,
- icon: 'fas fa-envelope'
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
},
mention: this.$i.emailNotificationTypes.includes('mention'),
diff --git a/src/client/pages/settings/email.vue b/src/client/pages/settings/email.vue
index aa20d9d94e..adc62133ac 100644
--- a/src/client/pages/settings/email.vue
+++ b/src/client/pages/settings/email.vue
@@ -14,7 +14,7 @@
{{ $ts.emailNotification }}
</FormLink>
- <FormSwitch :value="$i.receiveAnnouncementEmail" @update:value="onChangeReceiveAnnouncementEmail">
+ <FormSwitch :value="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail">
{{ $ts.receiveAnnouncementFromInstance }}
</FormSwitch>
</FormBase>
@@ -22,11 +22,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormButton from '@client/components/form/button.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormSwitch from '@client/components/form/switch.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -45,7 +45,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.email,
- icon: 'fas fa-envelope'
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
},
}
},
diff --git a/src/client/pages/settings/experimental-features.vue b/src/client/pages/settings/experimental-features.vue
index f8d5e419e9..971c45a628 100644
--- a/src/client/pages/settings/experimental-features.vue
+++ b/src/client/pages/settings/experimental-features.vue
@@ -8,11 +8,11 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/settings/general.vue b/src/client/pages/settings/general.vue
index f8e8e6b24b..59dd251948 100644
--- a/src/client/pages/settings/general.vue
+++ b/src/client/pages/settings/general.vue
@@ -1,8 +1,8 @@
<template>
<FormBase>
- <FormSwitch v-model:value="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch>
+ <FormSwitch v-model="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch>
- <FormSelect v-model:value="lang">
+ <FormSelect v-model="lang">
<template #label>{{ $ts.uiLanguage }}</template>
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
<template #caption>
@@ -16,13 +16,13 @@
<FormGroup>
<template #label>{{ $ts.behavior }}</template>
- <FormSwitch v-model:value="imageNewTab">{{ $ts.openImageInNewTab }}</FormSwitch>
- <FormSwitch v-model:value="enableInfiniteScroll">{{ $ts.enableInfiniteScroll }}</FormSwitch>
- <FormSwitch v-model:value="useReactionPickerForContextMenu">{{ $ts.useReactionPickerForContextMenu }}</FormSwitch>
- <FormSwitch v-model:value="disablePagesScript">{{ $ts.disablePagesScript }}</FormSwitch>
+ <FormSwitch v-model="imageNewTab">{{ $ts.openImageInNewTab }}</FormSwitch>
+ <FormSwitch v-model="enableInfiniteScroll">{{ $ts.enableInfiniteScroll }}</FormSwitch>
+ <FormSwitch v-model="useReactionPickerForContextMenu">{{ $ts.useReactionPickerForContextMenu }}</FormSwitch>
+ <FormSwitch v-model="disablePagesScript">{{ $ts.disablePagesScript }}</FormSwitch>
</FormGroup>
- <FormSelect v-model:value="serverDisconnectedBehavior">
+ <FormSelect v-model="serverDisconnectedBehavior">
<template #label>{{ $ts.whenServerDisconnected }}</template>
<option value="reload">{{ $ts._serverDisconnectedBehavior.reload }}</option>
<option value="dialog">{{ $ts._serverDisconnectedBehavior.dialog }}</option>
@@ -31,22 +31,22 @@
<FormGroup>
<template #label>{{ $ts.appearance }}</template>
- <FormSwitch v-model:value="disableAnimatedMfm">{{ $ts.disableAnimatedMfm }}</FormSwitch>
- <FormSwitch v-model:value="reduceAnimation">{{ $ts.reduceUiAnimation }}</FormSwitch>
- <FormSwitch v-model:value="useBlurEffect">{{ $ts.useBlurEffect }}</FormSwitch>
- <FormSwitch v-model:value="useBlurEffectForModal">{{ $ts.useBlurEffectForModal }}</FormSwitch>
- <FormSwitch v-model:value="showGapBetweenNotesInTimeline">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch>
- <FormSwitch v-model:value="loadRawImages">{{ $ts.loadRawImages }}</FormSwitch>
- <FormSwitch v-model:value="disableShowingAnimatedImages">{{ $ts.disableShowingAnimatedImages }}</FormSwitch>
- <FormSwitch v-model:value="squareAvatars">{{ $ts.squareAvatars }}</FormSwitch>
- <FormSwitch v-model:value="useSystemFont">{{ $ts.useSystemFont }}</FormSwitch>
- <FormSwitch v-model:value="useOsNativeEmojis">{{ $ts.useOsNativeEmojis }}
+ <FormSwitch v-model="disableAnimatedMfm">{{ $ts.disableAnimatedMfm }}</FormSwitch>
+ <FormSwitch v-model="reduceAnimation">{{ $ts.reduceUiAnimation }}</FormSwitch>
+ <FormSwitch v-model="useBlurEffect">{{ $ts.useBlurEffect }}</FormSwitch>
+ <FormSwitch v-model="useBlurEffectForModal">{{ $ts.useBlurEffectForModal }}</FormSwitch>
+ <FormSwitch v-model="showGapBetweenNotesInTimeline">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch>
+ <FormSwitch v-model="loadRawImages">{{ $ts.loadRawImages }}</FormSwitch>
+ <FormSwitch v-model="disableShowingAnimatedImages">{{ $ts.disableShowingAnimatedImages }}</FormSwitch>
+ <FormSwitch v-model="squareAvatars">{{ $ts.squareAvatars }}</FormSwitch>
+ <FormSwitch v-model="useSystemFont">{{ $ts.useSystemFont }}</FormSwitch>
+ <FormSwitch v-model="useOsNativeEmojis">{{ $ts.useOsNativeEmojis }}
<div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" :key="useOsNativeEmojis"/></div>
</FormSwitch>
</FormGroup>
<FormGroup>
- <FormSwitch v-model:value="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch>
+ <FormSwitch v-model="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch>
</FormGroup>
<FormRadios v-model="fontSize">
@@ -57,14 +57,14 @@
<option value="veryLarge"><span style="font-size: 20px;">Aa</span></option>
</FormRadios>
- <FormSelect v-model:value="instanceTicker">
+ <FormSelect v-model="instanceTicker">
<template #label>{{ $ts.instanceTicker }}</template>
<option value="none">{{ $ts._instanceTicker.none }}</option>
<option value="remote">{{ $ts._instanceTicker.remote }}</option>
<option value="always">{{ $ts._instanceTicker.always }}</option>
</FormSelect>
- <FormSelect v-model:value="nsfw">
+ <FormSelect v-model="nsfw">
<template #label>{{ $ts.nsfw }}</template>
<option value="respect">{{ $ts._nsfw.respect }}</option>
<option value="ignore">{{ $ts._nsfw.ignore }}</option>
@@ -73,10 +73,10 @@
<FormGroup>
<template #label>{{ $ts.defaultNavigationBehaviour }}</template>
- <FormSwitch v-model:value="defaultSideView">{{ $ts.openInSideView }}</FormSwitch>
+ <FormSwitch v-model="defaultSideView">{{ $ts.openInSideView }}</FormSwitch>
</FormGroup>
- <FormSelect v-model:value="chatOpenBehavior">
+ <FormSelect v-model="chatOpenBehavior">
<template #label>{{ $ts.chatOpenBehavior }}</template>
<option value="page">{{ $ts.showInPage }}</option>
<option value="window">{{ $ts.openInWindow }}</option>
@@ -91,13 +91,13 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormSelect from '@client/components/form/select.vue';
-import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormSelect from '@client/components/debobigego/select.vue';
+import FormRadios from '@client/components/debobigego/radios.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import MkLink from '@client/components/link.vue';
import { langs } from '@client/config';
import { defaultStore } from '@client/store';
@@ -124,7 +124,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.general,
- icon: 'fas fa-cogs'
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)'
},
langs,
lang: localStorage.getItem('lang'),
diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue
index e77efb4429..2b49996dda 100644
--- a/src/client/pages/settings/import-export.vue
+++ b/src/client/pages/settings/import-export.vue
@@ -1,45 +1,42 @@
<template>
-<FormBase>
- <FormGroup>
+<div style="margin: 16px;">
+ <FormSection>
<template #label>{{ $ts._exportOrImport.allNotes }}</template>
- <FormButton @click="doExport('notes')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton>
- </FormGroup>
- <FormGroup>
+ <MkButton :class="$style.button" inline @click="doExport('notes')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+ </FormSection>
+ <FormSection>
<template #label>{{ $ts._exportOrImport.followingList }}</template>
- <FormButton @click="doExport('following')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton>
- <FormButton @click="doImport('following', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</FormButton>
- </FormGroup>
- <FormGroup>
+ <MkButton :class="$style.button" inline @click="doExport('following')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+ <MkButton :class="$style.button" inline @click="doImport('following', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton>
+ </FormSection>
+ <FormSection>
<template #label>{{ $ts._exportOrImport.userLists }}</template>
- <FormButton @click="doExport('user-lists')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton>
- <FormButton @click="doImport('user-lists', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</FormButton>
- </FormGroup>
- <FormGroup>
+ <MkButton :class="$style.button" inline @click="doExport('user-lists')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+ <MkButton :class="$style.button" inline @click="doImport('user-lists', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton>
+ </FormSection>
+ <FormSection>
<template #label>{{ $ts._exportOrImport.muteList }}</template>
- <FormButton @click="doExport('mute')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton>
- </FormGroup>
- <FormGroup>
+ <MkButton :class="$style.button" inline @click="doExport('mute')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+ </FormSection>
+ <FormSection>
<template #label>{{ $ts._exportOrImport.blockingList }}</template>
- <FormButton @click="doExport('blocking')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton>
- </FormGroup>
-</FormBase>
+ <MkButton :class="$style.button" inline @click="doExport('blocking')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+ </FormSection>
+</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSelect from '@client/components/form/select.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import MkButton from '@client/components/ui/button.vue';
+import FormSection from '@client/components/form/section.vue';
import * as os from '@client/os';
import { selectFile } from '@client/scripts/select-file';
import * as symbols from '@client/symbols';
export default defineComponent({
components: {
- FormBase,
- FormGroup,
- FormButton,
+ FormSection,
+ MkButton,
},
emits: ['info'],
@@ -48,7 +45,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.importAndExport,
- icon: 'fas fa-boxes'
+ icon: 'fas fa-boxes',
+ bg: 'var(--bg)',
},
}
},
@@ -102,3 +100,9 @@ export default defineComponent({
}
});
</script>
+
+<style module>
+.button {
+ margin-right: 16px;
+}
+</style>
diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue
index 3fb5f5f1e6..9da3031a41 100644
--- a/src/client/pages/settings/index.vue
+++ b/src/client/pages/settings/index.vue
@@ -1,53 +1,12 @@
<template>
<div class="vvcocwet" :class="{ wide: !narrow }" ref="el">
<div class="nav" v-if="!narrow || page == null">
- <FormBase>
- <FormGroup>
- <div class="_formItem">
- <div class="_formPanel lwjxoukj">
- <MkAvatar :user="$i" class="avatar"/>
- </div>
- </div>
- <FormLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</FormLink>
- </FormGroup>
- <FormInfo v-if="emailNotConfigured" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></FormInfo>
- <FormGroup>
- <template #label>{{ $ts.basicSettings }}</template>
- <FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</FormLink>
- <FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><i class="fas fa-lock-open"></i></template>{{ $ts.privacy }}</FormLink>
- <FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.reaction }}</FormLink>
- <FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.drive }}</FormLink>
- <FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><i class="fas fa-bell"></i></template>{{ $ts.notifications }}</FormLink>
- <FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.email }}</FormLink>
- <FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</FormLink>
- <FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</FormLink>
- </FormGroup>
- <FormGroup>
- <template #label>{{ $ts.clientSettings }}</template>
- <FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</FormLink>
- <FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</FormLink>
- <FormLink :active="page === 'menu'" replace to="/settings/menu"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.menu }}</FormLink>
- <FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</FormLink>
- <FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</FormLink>
- </FormGroup>
- <FormGroup>
- <template #label>{{ $ts.otherSettings }}</template>
- <FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><i class="fas fa-boxes"></i></template>{{ $ts.importAndExport }}</FormLink>
- <FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.muteAndBlock }}</FormLink>
- <FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><i class="fas fa-comment-slash"></i></template>{{ $ts.wordMute }}</FormLink>
- <FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</FormLink>
- <FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</FormLink>
- </FormGroup>
- <FormGroup>
- <FormButton @click="clear">{{ $ts.clearCache }}</FormButton>
- </FormGroup>
- <FormGroup>
- <FormButton @click="logout" danger>{{ $ts.logout }}</FormButton>
- </FormGroup>
- </FormBase>
+ <div class="title">{{ $ts.settings }}</div>
+ <MkInfo v-if="emailNotConfigured" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+ <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
</div>
<div class="main">
- <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/>
+ <component :is="component" :key="page" v-bind="pageProps"/>
</div>
</div>
</template>
@@ -55,11 +14,8 @@
<script lang="ts">
import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue';
import { i18n } from '@client/i18n';
-import FormLink from '@client/components/form/link.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormInfo from '@client/components/form/info.vue';
+import MkInfo from '@client/components/ui/info.vue';
+import MkSuperMenu from '@client/components/ui/super-menu.vue';
import { scroll } from '@client/scripts/scroll';
import { signout } from '@client/account';
import { unisonReload } from '@client/scripts/unison-reload';
@@ -69,11 +25,8 @@ import { $i } from '@client/account';
export default defineComponent({
components: {
- FormBase,
- FormLink,
- FormGroup,
- FormButton,
- FormInfo,
+ MkInfo,
+ MkSuperMenu,
},
props: {
@@ -94,9 +47,126 @@ export default defineComponent({
const narrow = ref(false);
const view = ref(null);
const el = ref(null);
- const onInfo = (viewInfo) => {
- INFO.value = viewInfo;
- };
+ const menuDef = computed(() => [{
+ title: i18n.locale.basicSettings,
+ items: [{
+ icon: 'fas fa-user',
+ text: i18n.locale.profile,
+ to: '/settings/profile',
+ active: page.value === 'profile',
+ }, {
+ icon: 'fas fa-lock-open',
+ text: i18n.locale.privacy,
+ to: '/settings/privacy',
+ active: page.value === 'privacy',
+ }, {
+ icon: 'fas fa-laugh',
+ text: i18n.locale.reaction,
+ to: '/settings/reaction',
+ active: page.value === 'reaction',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.locale.drive,
+ to: '/settings/drive',
+ active: page.value === 'drive',
+ }, {
+ icon: 'fas fa-bell',
+ text: i18n.locale.notifications,
+ to: '/settings/notifications',
+ active: page.value === 'notifications',
+ }, {
+ icon: 'fas fa-envelope',
+ text: i18n.locale.email,
+ to: '/settings/email',
+ active: page.value === 'email',
+ }, {
+ icon: 'fas fa-share-alt',
+ text: i18n.locale.integration,
+ to: '/settings/integration',
+ active: page.value === 'integration',
+ }, {
+ icon: 'fas fa-lock',
+ text: i18n.locale.security,
+ to: '/settings/security',
+ active: page.value === 'security',
+ }],
+ }, {
+ title: i18n.locale.clientSettings,
+ items: [{
+ icon: 'fas fa-cogs',
+ text: i18n.locale.general,
+ to: '/settings/general',
+ active: page.value === 'general',
+ }, {
+ icon: 'fas fa-palette',
+ text: i18n.locale.theme,
+ to: '/settings/theme',
+ active: page.value === 'theme',
+ }, {
+ icon: 'fas fa-list-ul',
+ text: i18n.locale.menu,
+ to: '/settings/menu',
+ active: page.value === 'menu',
+ }, {
+ icon: 'fas fa-music',
+ text: i18n.locale.sounds,
+ to: '/settings/sounds',
+ active: page.value === 'sounds',
+ }, {
+ icon: 'fas fa-plug',
+ text: i18n.locale.plugins,
+ to: '/settings/plugin',
+ active: page.value === 'plugin',
+ }],
+ }, {
+ title: i18n.locale.otherSettings,
+ items: [{
+ icon: 'fas fa-boxes',
+ text: i18n.locale.importAndExport,
+ to: '/settings/import-export',
+ active: page.value === 'import-export',
+ }, {
+ icon: 'fas fa-ban',
+ text: i18n.locale.muteAndBlock,
+ to: '/settings/mute-block',
+ active: page.value === 'mute-block',
+ }, {
+ icon: 'fas fa-comment-slash',
+ text: i18n.locale.wordMute,
+ to: '/settings/word-mute',
+ active: page.value === 'word-mute',
+ }, {
+ icon: 'fas fa-key',
+ text: 'API',
+ to: '/settings/api',
+ active: page.value === 'api',
+ }, {
+ icon: 'fas fa-ellipsis-h',
+ text: i18n.locale.other,
+ to: '/settings/other',
+ active: page.value === 'other',
+ }],
+ }, {
+ items: [{
+ type: 'button',
+ icon: 'fas fa-trash',
+ text: i18n.locale.clearCache,
+ action: () => {
+ localStorage.removeItem('locale');
+ localStorage.removeItem('theme');
+ unisonReload();
+ },
+ }, {
+ type: 'button',
+ icon: 'fas fa-sign-in-alt fa-flip-horizontal',
+ text: i18n.locale.logout,
+ action: () => {
+ signout();
+ },
+ danger: true,
+ },],
+ }]);
+
const pageProps = ref({});
const component = computed(() => {
if (page.value == null) return null;
@@ -159,7 +229,7 @@ export default defineComponent({
}
nextTick(() => {
- scroll(el.value, 0);
+ scroll(el.value, { top: 0 });
});
}, { immediate: true });
@@ -186,21 +256,13 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: INFO,
page,
+ menuDef,
narrow,
view,
el,
- onInfo,
pageProps,
component,
emailNotConfigured,
- logout: () => {
- signout();
- },
- clear: () => {
- localStorage.removeItem('locale');
- localStorage.removeItem('theme');
- unisonReload();
- },
};
},
});
@@ -208,17 +270,41 @@ export default defineComponent({
<style lang="scss" scoped>
.vvcocwet {
+ > .nav {
+ > .title {
+ margin: 16px;
+ font-size: 1.5em;
+ font-weight: bold;
+ }
+
+ > .info {
+ margin: 0 16px;
+ }
+
+ > .accounts {
+ > .avatar {
+ display: block;
+ width: 50px;
+ height: 50px;
+ margin: 8px auto 16px auto;
+ }
+ }
+ }
+
&.wide {
display: flex;
- max-width: 1100px;
+ max-width: 1000px;
margin: 0 auto;
height: 100%;
> .nav {
width: 32%;
box-sizing: border-box;
- border-right: solid 0.5px var(--divider);
overflow: auto;
+
+ > .title {
+ margin: 24px;
+ }
}
> .main {
@@ -229,15 +315,4 @@ export default defineComponent({
}
}
}
-
-.lwjxoukj {
- padding: 16px;
-
- > .avatar {
- display: block;
- margin: auto;
- width: 42px;
- height: 42px;
- }
-}
</style>
diff --git a/src/client/pages/settings/integration.vue b/src/client/pages/settings/integration.vue
index f1c0a88afc..7f398dde9d 100644
--- a/src/client/pages/settings/integration.vue
+++ b/src/client/pages/settings/integration.vue
@@ -1,26 +1,26 @@
<template>
<FormBase>
- <div class="_formItem" v-if="enableTwitterIntegration">
- <div class="_formLabel"><i class="fab fa-twitter"></i> Twitter</div>
- <div class="_formPanel" style="padding: 16px;">
+ <div class="_debobigegoItem" v-if="enableTwitterIntegration">
+ <div class="_debobigegoLabel"><i class="fab fa-twitter"></i> Twitter</div>
+ <div class="_debobigegoPanel" style="padding: 16px;">
<p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
<MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectService }}</MkButton>
<MkButton v-else @click="connectTwitter" primary>{{ $ts.connectService }}</MkButton>
</div>
</div>
- <div class="_formItem" v-if="enableDiscordIntegration">
- <div class="_formLabel"><i class="fab fa-discord"></i> Discord</div>
- <div class="_formPanel" style="padding: 16px;">
+ <div class="_debobigegoItem" v-if="enableDiscordIntegration">
+ <div class="_debobigegoLabel"><i class="fab fa-discord"></i> Discord</div>
+ <div class="_debobigegoPanel" style="padding: 16px;">
<p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
<MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectService }}</MkButton>
<MkButton v-else @click="connectDiscord" primary>{{ $ts.connectService }}</MkButton>
</div>
</div>
- <div class="_formItem" v-if="enableGithubIntegration">
- <div class="_formLabel"><i class="fab fa-github"></i> GitHub</div>
- <div class="_formPanel" style="padding: 16px;">
+ <div class="_debobigegoItem" v-if="enableGithubIntegration">
+ <div class="_debobigegoLabel"><i class="fab fa-github"></i> GitHub</div>
+ <div class="_debobigegoPanel" style="padding: 16px;">
<p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
<MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectService }}</MkButton>
<MkButton v-else @click="connectGithub" primary>{{ $ts.connectService }}</MkButton>
@@ -32,7 +32,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { apiUrl } from '@client/config';
-import FormBase from '@client/components/form/base.vue';
+import FormBase from '@client/components/debobigego/base.vue';
import MkButton from '@client/components/ui/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -49,7 +49,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.integration,
- icon: 'fas fa-share-alt'
+ icon: 'fas fa-share-alt',
+ bg: 'var(--bg)',
},
apiUrl,
twitterForm: null,
diff --git a/src/client/pages/settings/menu.vue b/src/client/pages/settings/menu.vue
index 4b315145e1..31472eb0c1 100644
--- a/src/client/pages/settings/menu.vue
+++ b/src/client/pages/settings/menu.vue
@@ -1,6 +1,6 @@
<template>
<FormBase>
- <FormTextarea v-model:value="items" tall manual-save>
+ <FormTextarea v-model="items" tall manual-save>
<span>{{ $ts.menu }}</span>
<template #desc><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template>
</FormTextarea>
@@ -19,12 +19,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormRadios from '@client/components/debobigego/radios.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import { menuDef } from '@client/menu';
import { defaultStore } from '@client/store';
@@ -45,7 +43,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.menu,
- icon: 'fas fa-list-ul'
+ icon: 'fas fa-list-ul',
+ bg: 'var(--bg)',
},
menuDef: menuDef,
items: defaultStore.state.menu.join('\n'),
diff --git a/src/client/pages/settings/mute-block.vue b/src/client/pages/settings/mute-block.vue
index dde0199e18..18b2fc0af4 100644
--- a/src/client/pages/settings/mute-block.vue
+++ b/src/client/pages/settings/mute-block.vue
@@ -1,6 +1,6 @@
<template>
<FormBase>
- <MkTab v-model:value="tab" style="margin-bottom: var(--margin);">
+ <MkTab v-model="tab" style="margin-bottom: var(--margin);">
<option value="mute">{{ $ts.mutedUsers }}</option>
<option value="block">{{ $ts.blockedUsers }}</option>
</MkTab>
@@ -35,10 +35,10 @@
import { defineComponent } from 'vue';
import MkPagination from '@client/components/ui/pagination.vue';
import MkTab from '@client/components/tab.vue';
-import FormInfo from '@client/components/form/info.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import { userPage } from '@client/filters/user';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -59,7 +59,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.muteAndBlock,
- icon: 'fas fa-ban'
+ icon: 'fas fa-ban',
+ bg: 'var(--bg)',
},
tab: 'mute',
mutingPagination: {
diff --git a/src/client/pages/settings/notifications.vue b/src/client/pages/settings/notifications.vue
index ec95452ba2..5f84349474 100644
--- a/src/client/pages/settings/notifications.vue
+++ b/src/client/pages/settings/notifications.vue
@@ -11,11 +11,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormButton from '@client/components/form/button.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import { notificationTypes } from '../../../types';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import { notificationTypes } from '@/types';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -33,7 +33,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.notifications,
- icon: 'fas fa-bell'
+ icon: 'fas fa-bell',
+ bg: 'var(--bg)',
},
}
},
diff --git a/src/client/pages/settings/other.vue b/src/client/pages/settings/other.vue
index 21b5439041..2eb922453f 100644
--- a/src/client/pages/settings/other.vue
+++ b/src/client/pages/settings/other.vue
@@ -2,18 +2,18 @@
<FormBase>
<FormLink to="/settings/update">Misskey Update</FormLink>
- <FormSwitch :value="$i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote">
+ <FormSwitch :value="$i.injectFeaturedNote" @update:modelValue="onChangeInjectFeaturedNote">
{{ $ts.showFeaturedNotesInTimeline }}
</FormSwitch>
- <FormSwitch v-model:value="reportError">{{ $ts.sendErrorReports }}<template #desc>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
+ <FormSwitch v-model="reportError">{{ $ts.sendErrorReports }}<template #desc>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
<FormLink to="/settings/account-info">{{ $ts.accountInfo }}</FormLink>
<FormLink to="/settings/experimental-features">{{ $ts.experimentalFeatures }}</FormLink>
<FormGroup>
<template #label>{{ $ts.developer }}</template>
- <FormSwitch v-model:value="debug" @update:value="changeDebug">
+ <FormSwitch v-model="debug" @update:modelValue="changeDebug">
DEBUG MODE
</FormSwitch>
<template v-if="debug">
@@ -34,10 +34,10 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import { debug } from '@client/config';
import { defaultStore } from '@client/store';
@@ -60,7 +60,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.other,
- icon: 'fas fa-ellipsis-h'
+ icon: 'fas fa-ellipsis-h',
+ bg: 'var(--bg)',
},
debug,
}
diff --git a/src/client/pages/settings/plugin.install.vue b/src/client/pages/settings/plugin.install.vue
index 30cbf58ad7..709ef11abb 100644
--- a/src/client/pages/settings/plugin.install.vue
+++ b/src/client/pages/settings/plugin.install.vue
@@ -3,7 +3,7 @@
<FormInfo warn>{{ $ts._plugin.installWarn }}</FormInfo>
<FormGroup>
- <FormTextarea v-model:value="code" tall>
+ <FormTextarea v-model="code" tall>
<span>{{ $ts.code }}</span>
</FormTextarea>
</FormGroup>
@@ -20,11 +20,11 @@ import { v4 as uuid } from 'uuid';
import FormTextarea from '@client/components/form/textarea.vue';
import FormSelect from '@client/components/form/select.vue';
import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormInfo from '@client/components/form/info.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
import { unisonReload } from '@client/scripts/unison-reload';
@@ -48,7 +48,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts._plugin.install,
- icon: 'fas fa-download'
+ icon: 'fas fa-download',
+ bg: 'var(--bg)',
},
code: null,
}
diff --git a/src/client/pages/settings/plugin.manage.vue b/src/client/pages/settings/plugin.manage.vue
index 3df87ca084..f1c27f1e3c 100644
--- a/src/client/pages/settings/plugin.manage.vue
+++ b/src/client/pages/settings/plugin.manage.vue
@@ -3,9 +3,9 @@
<FormGroup v-for="plugin in plugins" :key="plugin.id">
<template #label><span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span></template>
- <FormSwitch :value="plugin.active" @update:value="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch>
- <div class="_formItem">
- <div class="_formPanel" style="padding: 16px;">
+ <FormSwitch :value="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch>
+ <div class="_debobigegoItem">
+ <div class="_debobigegoPanel" style="padding: 16px;">
<div class="_keyValue">
<div>{{ $ts.author }}:</div>
<div>{{ plugin.author }}</div>
@@ -20,8 +20,8 @@
</div>
</div>
</div>
- <div class="_formItem">
- <div class="_formPanel" style="padding: 16px;">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoPanel" style="padding: 16px;">
<MkButton @click="config(plugin)" inline v-if="plugin.config"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton>
<MkButton @click="uninstall(plugin)" inline danger><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton>
</div>
@@ -33,11 +33,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkSelect from '@client/components/ui/select.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkSelect from '@client/components/form/select.vue';
import FormSwitch from '@client/components/form/switch.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
import * as symbols from '@client/symbols';
@@ -58,7 +58,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts._plugin.manage,
- icon: 'fas fa-plug'
+ icon: 'fas fa-plug',
+ bg: 'var(--bg)',
},
plugins: ColdDeviceStorage.get('plugins'),
}
diff --git a/src/client/pages/settings/plugin.vue b/src/client/pages/settings/plugin.vue
index 13eaca07fd..23f263bbbd 100644
--- a/src/client/pages/settings/plugin.vue
+++ b/src/client/pages/settings/plugin.vue
@@ -7,9 +7,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormLink from '@client/components/form/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormLink from '@client/components/debobigego/link.vue';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
import * as symbols from '@client/symbols';
@@ -26,7 +26,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.plugins,
- icon: 'fas fa-plug'
+ icon: 'fas fa-plug',
+ bg: 'var(--bg)',
},
plugins: ColdDeviceStorage.get('plugins').length,
}
diff --git a/src/client/pages/settings/privacy.vue b/src/client/pages/settings/privacy.vue
index 46d8c17ca2..7756158578 100644
--- a/src/client/pages/settings/privacy.vue
+++ b/src/client/pages/settings/privacy.vue
@@ -1,43 +1,43 @@
<template>
<FormBase>
<FormGroup>
- <FormSwitch v-model:value="isLocked" @update:value="save()">{{ $ts.makeFollowManuallyApprove }}</FormSwitch>
- <FormSwitch v-model:value="autoAcceptFollowed" :disabled="!isLocked" @update:value="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch>
+ <FormSwitch v-model="isLocked" @update:modelValue="save()">{{ $ts.makeFollowManuallyApprove }}</FormSwitch>
+ <FormSwitch v-model="autoAcceptFollowed" :disabled="!isLocked" @update:modelValue="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch>
<template #caption>{{ $ts.lockedAccountInfo }}</template>
</FormGroup>
- <FormSwitch v-model:value="hideOnlineStatus" @update:value="save()">
+ <FormSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
{{ $ts.hideOnlineStatus }}
<template #desc>{{ $ts.hideOnlineStatusDescription }}</template>
</FormSwitch>
- <FormSwitch v-model:value="noCrawle" @update:value="save()">
+ <FormSwitch v-model="noCrawle" @update:modelValue="save()">
{{ $ts.noCrawle }}
<template #desc>{{ $ts.noCrawleDescription }}</template>
</FormSwitch>
- <FormSwitch v-model:value="isExplorable" @update:value="save()">
+ <FormSwitch v-model="isExplorable" @update:modelValue="save()">
{{ $ts.makeExplorable }}
<template #desc>{{ $ts.makeExplorableDescription }}</template>
</FormSwitch>
- <FormSwitch v-model:value="rememberNoteVisibility" @update:value="save()">{{ $ts.rememberNoteVisibility }}</FormSwitch>
+ <FormSwitch v-model="rememberNoteVisibility" @update:modelValue="save()">{{ $ts.rememberNoteVisibility }}</FormSwitch>
<FormGroup v-if="!rememberNoteVisibility">
<template #label>{{ $ts.defaultNoteVisibility }}</template>
- <FormSelect v-model:value="defaultNoteVisibility">
+ <FormSelect v-model="defaultNoteVisibility">
<option value="public">{{ $ts._visibility.public }}</option>
<option value="home">{{ $ts._visibility.home }}</option>
<option value="followers">{{ $ts._visibility.followers }}</option>
<option value="specified">{{ $ts._visibility.specified }}</option>
</FormSelect>
- <FormSwitch v-model:value="defaultNoteLocalOnly">{{ $ts._visibility.localOnly }}</FormSwitch>
+ <FormSwitch v-model="defaultNoteLocalOnly">{{ $ts._visibility.localOnly }}</FormSwitch>
</FormGroup>
- <FormSwitch v-model:value="keepCw" @update:value="save()">{{ $ts.keepCw }}</FormSwitch>
+ <FormSwitch v-model="keepCw" @update:modelValue="save()">{{ $ts.keepCw }}</FormSwitch>
</FormBase>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormSelect from '@client/components/form/select.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormSelect from '@client/components/debobigego/select.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import * as os from '@client/os';
import { defaultStore } from '@client/store';
import * as symbols from '@client/symbols';
@@ -56,7 +56,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.privacy,
- icon: 'fas fa-lock-open'
+ icon: 'fas fa-lock-open',
+ bg: 'var(--bg)',
},
isLocked: false,
autoAcceptFollowed: false,
diff --git a/src/client/pages/settings/profile.vue b/src/client/pages/settings/profile.vue
index de7e86bd12..b993b5fc72 100644
--- a/src/client/pages/settings/profile.vue
+++ b/src/client/pages/settings/profile.vue
@@ -1,33 +1,33 @@
<template>
<FormBase>
<FormGroup>
- <div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
+ <div class="_debobigegoItem _debobigegoPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
<MkAvatar class="avatar" :user="$i"/>
</div>
<FormButton @click="changeAvatar" primary>{{ $ts._profile.changeAvatar }}</FormButton>
<FormButton @click="changeBanner" primary>{{ $ts._profile.changeBanner }}</FormButton>
</FormGroup>
- <FormInput v-model:value="name" :max="30" manual-save>
+ <FormInput v-model="name" :max="30" manual-save>
<span>{{ $ts._profile.name }}</span>
</FormInput>
- <FormTextarea v-model:value="description" :max="500" tall manual-save>
+ <FormTextarea v-model="description" :max="500" tall manual-save>
<span>{{ $ts._profile.description }}</span>
<template #desc>{{ $ts._profile.youCanIncludeHashtags }}</template>
</FormTextarea>
- <FormInput v-model:value="location" manual-save>
+ <FormInput v-model="location" manual-save>
<span>{{ $ts.location }}</span>
<template #prefix><i class="fas fa-map-marker-alt"></i></template>
</FormInput>
- <FormInput v-model:value="birthday" type="date" manual-save>
+ <FormInput v-model="birthday" type="date" manual-save>
<span>{{ $ts.birthday }}</span>
<template #prefix><i class="fas fa-birthday-cake"></i></template>
</FormInput>
- <FormSelect v-model:value="lang">
+ <FormSelect v-model="lang">
<template #label>{{ $ts.language }}</template>
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
</FormSelect>
@@ -37,23 +37,23 @@
<template #caption>{{ $ts._profile.metadataDescription }}</template>
</FormGroup>
- <FormSwitch v-model:value="isCat">{{ $ts.flagAsCat }}<template #desc>{{ $ts.flagAsCatDescription }}</template></FormSwitch>
+ <FormSwitch v-model="isCat">{{ $ts.flagAsCat }}<template #desc>{{ $ts.flagAsCatDescription }}</template></FormSwitch>
- <FormSwitch v-model:value="isBot">{{ $ts.flagAsBot }}<template #desc>{{ $ts.flagAsBotDescription }}</template></FormSwitch>
+ <FormSwitch v-model="isBot">{{ $ts.flagAsBot }}<template #desc>{{ $ts.flagAsBotDescription }}</template></FormSwitch>
- <FormSwitch v-model:value="alwaysMarkNsfw">{{ $ts.alwaysMarkSensitive }}</FormSwitch>
+ <FormSwitch v-model="alwaysMarkNsfw">{{ $ts.alwaysMarkSensitive }}</FormSwitch>
</FormBase>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormButton from '@client/components/form/button.vue';
-import FormInput from '@client/components/form/input.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormSelect from '@client/components/form/select.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormSelect from '@client/components/debobigego/select.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import { host, langs } from '@client/config';
import { selectFile } from '@client/scripts/select-file';
import * as os from '@client/os';
@@ -76,7 +76,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.profile,
- icon: 'fas fa-user'
+ icon: 'fas fa-user',
+ bg: 'var(--bg)',
},
host,
langs,
diff --git a/src/client/pages/settings/reaction.vue b/src/client/pages/settings/reaction.vue
index a0024234e4..a5ff46097d 100644
--- a/src/client/pages/settings/reaction.vue
+++ b/src/client/pages/settings/reaction.vue
@@ -1,8 +1,8 @@
<template>
<FormBase>
- <div class="_formItem">
- <div class="_formLabel">{{ $ts.reactionSettingDescription }}</div>
- <div class="_formPanel">
+ <div class="_debobigegoItem">
+ <div class="_debobigegoLabel">{{ $ts.reactionSettingDescription }}</div>
+ <div class="_debobigegoPanel">
<XDraggable class="zoaiodol" v-model="reactions" :item-key="item => item" animation="150" delay="100" delay-on-touch-only="true">
<template #item="{element}">
<button class="_button item" @click="remove(element, $event)">
@@ -14,7 +14,7 @@
</template>
</XDraggable>
</div>
- <div class="_formCaption">{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></div>
+ <div class="_debobigegoCaption">{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></div>
</div>
<FormRadios v-model="reactionPickerWidth">
@@ -37,10 +37,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XDraggable from 'vuedraggable';
-import FormInput from '@client/components/form/input.vue';
-import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormInput from '@client/components/debobigego/input.vue';
+import FormRadios from '@client/components/debobigego/radios.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import * as os from '@client/os';
import { defaultStore } from '@client/store';
import * as symbols from '@client/symbols';
@@ -64,7 +64,8 @@ export default defineComponent({
action: {
icon: 'fas fa-eye',
handler: this.preview
- }
+ },
+ bg: 'var(--bg)',
},
reactions: JSON.parse(JSON.stringify(this.$store.state.reactions)),
}
diff --git a/src/client/pages/settings/registry.keys.vue b/src/client/pages/settings/registry.keys.vue
index f71589ba4f..d99002e50f 100644
--- a/src/client/pages/settings/registry.keys.vue
+++ b/src/client/pages/settings/registry.keys.vue
@@ -25,11 +25,11 @@ import { defineAsyncComponent, defineComponent } from 'vue';
import * as JSON5 from 'json5';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -56,7 +56,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.registry,
- icon: 'fas fa-cogs'
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)',
},
keys: null,
}
diff --git a/src/client/pages/settings/registry.value.vue b/src/client/pages/settings/registry.value.vue
index 48245ae99f..06be5737e9 100644
--- a/src/client/pages/settings/registry.value.vue
+++ b/src/client/pages/settings/registry.value.vue
@@ -19,7 +19,7 @@
</FormGroup>
<FormGroup>
- <FormTextarea tall v-model:value="valueForEditor" class="_monospace" style="tab-size: 2;">
+ <FormTextarea tall v-model="valueForEditor" class="_monospace" style="tab-size: 2;">
<span>{{ $ts.value }} (JSON)</span>
</FormTextarea>
<FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
@@ -38,14 +38,14 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import * as JSON5 from 'json5';
-import FormInfo from '@client/components/form/info.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
import FormTextarea from '@client/components/form/textarea.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -76,7 +76,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.registry,
- icon: 'fas fa-cogs'
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)',
},
value: null,
valueForEditor: null,
diff --git a/src/client/pages/settings/registry.vue b/src/client/pages/settings/registry.vue
index 5ba1bc751b..e4fb230d5c 100644
--- a/src/client/pages/settings/registry.vue
+++ b/src/client/pages/settings/registry.vue
@@ -13,11 +13,11 @@ import { defineAsyncComponent, defineComponent } from 'vue';
import * as JSON5 from 'json5';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -38,7 +38,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.registry,
- icon: 'fas fa-cogs'
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)',
},
scopes: null,
}
diff --git a/src/client/pages/settings/security.vue b/src/client/pages/settings/security.vue
index b70fa5a9f3..e051685a82 100644
--- a/src/client/pages/settings/security.vue
+++ b/src/client/pages/settings/security.vue
@@ -6,7 +6,7 @@
<FormPagination :pagination="pagination">
<template #label>{{ $ts.signinHistory }}</template>
<template #default="{items}">
- <div class="_formPanel timnmucd" v-for="item in items" :key="item.id">
+ <div class="_debobigegoPanel timnmucd" v-for="item in items" :key="item.id">
<header>
<i v-if="item.success" class="fas fa-check icon succ"></i>
<i v-else class="fas fa-times-circle icon fail"></i>
@@ -25,11 +25,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormBase from '@client/components/form/base.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormPagination from '@client/components/form/pagination.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormPagination from '@client/components/debobigego/pagination.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -48,7 +48,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.security,
- icon: 'fas fa-lock'
+ icon: 'fas fa-lock',
+ bg: 'var(--bg)',
},
pagination: {
endpoint: 'i/signin-history',
diff --git a/src/client/pages/settings/sounds.vue b/src/client/pages/settings/sounds.vue
index 1c51685ce8..07310619c8 100644
--- a/src/client/pages/settings/sounds.vue
+++ b/src/client/pages/settings/sounds.vue
@@ -1,6 +1,6 @@
<template>
<FormBase>
- <FormRange v-model:value="masterVolume" :min="0" :max="1" :step="0.05">
+ <FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05">
<template #label><i class="fas fa-volume-icon"></i> {{ $ts.masterVolume }}</template>
</FormRange>
@@ -19,11 +19,11 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import FormRange from '@client/components/form/range.vue';
-import FormSelect from '@client/components/form/select.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormRange from '@client/components/debobigego/range.vue';
+import FormSelect from '@client/components/debobigego/select.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
import { playFile } from '@client/scripts/sound';
@@ -71,7 +71,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.sounds,
- icon: 'fas fa-music'
+ icon: 'fas fa-music',
+ bg: 'var(--bg)',
},
sounds: {},
}
diff --git a/src/client/pages/settings/theme.install.vue b/src/client/pages/settings/theme.install.vue
index d719cc801f..9fbb28929d 100644
--- a/src/client/pages/settings/theme.install.vue
+++ b/src/client/pages/settings/theme.install.vue
@@ -1,7 +1,7 @@
<template>
<FormBase>
<FormGroup>
- <FormTextarea v-model:value="installThemeCode">
+ <FormTextarea v-model="installThemeCode">
<span>{{ $ts._theme.code }}</span>
</FormTextarea>
<FormButton @click="() => preview(installThemeCode)" :disabled="installThemeCode == null" inline><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton>
@@ -17,10 +17,10 @@ import * as JSON5 from 'json5';
import FormTextarea from '@client/components/form/textarea.vue';
import FormSelect from '@client/components/form/select.vue';
import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import { applyTheme, validateTheme } from '@client/scripts/theme';
import * as os from '@client/os';
import { ColdDeviceStorage } from '@client/store';
@@ -44,7 +44,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts._theme.install,
- icon: 'fas fa-download'
+ icon: 'fas fa-download',
+ bg: 'var(--bg)',
},
installThemeCode: null,
}
diff --git a/src/client/pages/settings/theme.manage.vue b/src/client/pages/settings/theme.manage.vue
index 7cc7a0169a..da21a47a50 100644
--- a/src/client/pages/settings/theme.manage.vue
+++ b/src/client/pages/settings/theme.manage.vue
@@ -1,6 +1,6 @@
<template>
<FormBase>
- <FormSelect v-model:value="selectedThemeId">
+ <FormSelect v-model="selectedThemeId">
<template #label>{{ $ts.theme }}</template>
<optgroup :label="$ts._theme.installedThemes">
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@@ -31,10 +31,10 @@ import * as JSON5 from 'json5';
import FormTextarea from '@client/components/form/textarea.vue';
import FormSelect from '@client/components/form/select.vue';
import FormRadios from '@client/components/form/radios.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import FormInput from '@client/components/form/input.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import { Theme, builtinThemes } from '@client/scripts/theme';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import * as os from '@client/os';
@@ -59,7 +59,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts._theme.manage,
- icon: 'fas fa-folder-open'
+ icon: 'fas fa-folder-open',
+ bg: 'var(--bg)',
},
installedThemes: getThemes(),
builtinThemes,
diff --git a/src/client/pages/settings/theme.vue b/src/client/pages/settings/theme.vue
index 94eddb1b6f..c6be42251c 100644
--- a/src/client/pages/settings/theme.vue
+++ b/src/client/pages/settings/theme.vue
@@ -1,7 +1,7 @@
<template>
<FormBase>
<FormGroup>
- <div class="rfqxtzch _formItem _formPanel">
+ <div class="rfqxtzch _debobigegoItem _debobigegoPanel">
<div class="darkMode">
<div class="toggleWrapper">
<input type="checkbox" class="dn" id="dn" v-model="darkMode"/>
@@ -23,11 +23,11 @@
</div>
</div>
</div>
- <FormSwitch v-model:value="syncDeviceDarkMode">{{ $ts.syncDeviceDarkMode }}</FormSwitch>
+ <FormSwitch v-model="syncDeviceDarkMode">{{ $ts.syncDeviceDarkMode }}</FormSwitch>
</FormGroup>
<template v-if="darkMode">
- <FormSelect v-model:value="darkThemeId">
+ <FormSelect v-model="darkThemeId">
<template #label>{{ $ts.themeForDarkMode }}</template>
<optgroup :label="$ts.darkThemes">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@@ -36,7 +36,7 @@
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</FormSelect>
- <FormSelect v-model:value="lightThemeId">
+ <FormSelect v-model="lightThemeId">
<template #label>{{ $ts.themeForLightMode }}</template>
<optgroup :label="$ts.lightThemes">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@@ -47,7 +47,7 @@
</FormSelect>
</template>
<template v-else>
- <FormSelect v-model:value="lightThemeId">
+ <FormSelect v-model="lightThemeId">
<template #label>{{ $ts.themeForLightMode }}</template>
<optgroup :label="$ts.lightThemes">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@@ -56,7 +56,7 @@
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</FormSelect>
- <FormSelect v-model:value="darkThemeId">
+ <FormSelect v-model="darkThemeId">
<template #label>{{ $ts.themeForDarkMode }}</template>
<optgroup :label="$ts.darkThemes">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@@ -86,12 +86,12 @@
<script lang="ts">
import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormSelect from '@client/components/form/select.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormButton from '@client/components/form/button.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormSelect from '@client/components/debobigego/select.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormButton from '@client/components/debobigego/button.vue';
import { builtinThemes } from '@client/scripts/theme';
import { selectFile } from '@client/scripts/select-file';
import { isDeviceDarkmode } from '@client/scripts/is-device-darkmode';
@@ -116,7 +116,8 @@ export default defineComponent({
setup(props, { emit }) {
const INFO = {
title: i18n.locale.theme,
- icon: 'fas fa-palette'
+ icon: 'fas fa-palette',
+ bg: 'var(--bg)',
};
const installedThemes = ref(getThemes());
diff --git a/src/client/pages/settings/update.vue b/src/client/pages/settings/update.vue
index 8000327d0c..8bc459e936 100644
--- a/src/client/pages/settings/update.vue
+++ b/src/client/pages/settings/update.vue
@@ -32,12 +32,12 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import FormSwitch from '@client/components/form/switch.vue';
import FormSelect from '@client/components/form/select.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormInfo from '@client/components/form/info.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
import * as os from '@client/os';
import { version, instanceName } from '@client/config';
import * as symbols from '@client/symbols';
@@ -60,7 +60,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: 'Misskey Update',
- icon: 'fas fa-sync-alt'
+ icon: 'fas fa-sync-alt',
+ bg: 'var(--bg)',
},
version,
instanceName,
diff --git a/src/client/pages/settings/word-mute.vue b/src/client/pages/settings/word-mute.vue
index fe3fece844..53948b1b1e 100644
--- a/src/client/pages/settings/word-mute.vue
+++ b/src/client/pages/settings/word-mute.vue
@@ -1,21 +1,21 @@
<template>
<div>
- <MkTab v-model:value="tab">
+ <MkTab v-model="tab">
<option value="soft">{{ $ts._wordMute.soft }}</option>
<option value="hard">{{ $ts._wordMute.hard }}</option>
</MkTab>
<FormBase>
- <div class="_formItem">
+ <div class="_debobigegoItem">
<div v-show="tab === 'soft'">
<FormInfo>{{ $ts._wordMute.softDescription }}</FormInfo>
- <FormTextarea v-model:value="softMutedWords">
+ <FormTextarea v-model="softMutedWords">
<span>{{ $ts._wordMute.muteWords }}</span>
<template #desc>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
</FormTextarea>
</div>
<div v-show="tab === 'hard'">
<FormInfo>{{ $ts._wordMute.hardDescription }}</FormInfo>
- <FormTextarea v-model:value="hardMutedWords">
+ <FormTextarea v-model="hardMutedWords">
<span>{{ $ts._wordMute.muteWords }}</span>
<template #desc>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
</FormTextarea>
@@ -33,10 +33,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import FormTextarea from '@client/components/form/textarea.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormInfo from '@client/components/form/info.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormInfo from '@client/components/debobigego/info.vue';
import MkTab from '@client/components/tab.vue';
import * as os from '@client/os';
import number from '@client/filters/number';
@@ -58,7 +58,8 @@ export default defineComponent({
return {
[symbols.PAGE_INFO]: {
title: this.$ts.wordMute,
- icon: 'fas fa-comment-slash'
+ icon: 'fas fa-comment-slash',
+ bg: 'var(--bg)',
},
tab: 'soft',
softMutedWords: '',
diff --git a/src/client/pages/signup-complete.vue b/src/client/pages/signup-complete.vue
new file mode 100644
index 0000000000..dada92031a
--- /dev/null
+++ b/src/client/pages/signup-complete.vue
@@ -0,0 +1,50 @@
+<template>
+<div>
+ {{ $ts.processing }}
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import * as os from '@client/os';
+import * as symbols from '@client/symbols';
+import { login } from '@client/account';
+
+export default defineComponent({
+ components: {
+
+ },
+
+ props: {
+ code: {
+ type: String,
+ required: true
+ }
+ },
+
+ data() {
+ return {
+ [symbols.PAGE_INFO]: {
+ title: this.$ts.signup,
+ icon: 'fas fa-user'
+ },
+ }
+ },
+
+ mounted() {
+ os.apiWithDialog('signup-pending', {
+ code: this.code,
+ }).then(res => {
+ login(res.i, '/');
+ });
+ },
+
+ methods: {
+
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+
+</style>
diff --git a/src/client/pages/test.vue b/src/client/pages/test.vue
index 131571e9dd..fbab0112ed 100644
--- a/src/client/pages/test.vue
+++ b/src/client/pages/test.vue
@@ -133,10 +133,10 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
-import MkSwitch from '@client/components/ui/switch.vue';
-import MkTextarea from '@client/components/ui/textarea.vue';
-import MkRadio from '@client/components/ui/radio.vue';
+import MkInput from '@client/components/form/input.vue';
+import MkSwitch from '@client/components/form/switch.vue';
+import MkTextarea from '@client/components/form/textarea.vue';
+import MkRadio from '@client/components/form/radio.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue
index ce8bae4ff5..3b10396ab8 100644
--- a/src/client/pages/theme-editor.vue
+++ b/src/client/pages/theme-editor.vue
@@ -1,8 +1,8 @@
<template>
<FormBase class="cwepdizn">
- <div class="_formItem colorPicker">
- <div class="_formLabel">{{ $ts.backgroundColor }}</div>
- <div class="_formPanel colors">
+ <div class="_debobigegoItem colorPicker">
+ <div class="_debobigegoLabel">{{ $ts.backgroundColor }}</div>
+ <div class="_debobigegoPanel colors">
<div class="row">
<button v-for="color in bgColors.filter(x => x.kind === 'light')" :key="color.color" @click="setBgColor(color)" class="color _button" :class="{ active: theme.props.bg === color.color }">
<div class="preview" :style="{ background: color.forPreview }"></div>
@@ -15,9 +15,9 @@
</div>
</div>
</div>
- <div class="_formItem colorPicker">
- <div class="_formLabel">{{ $ts.accentColor }}</div>
- <div class="_formPanel colors">
+ <div class="_debobigegoItem colorPicker">
+ <div class="_debobigegoLabel">{{ $ts.accentColor }}</div>
+ <div class="_debobigegoPanel colors">
<div class="row">
<button v-for="color in accentColors" :key="color" @click="setAccentColor(color)" class="color rounded _button" :class="{ active: theme.props.accent === color }">
<div class="preview" :style="{ background: color }"></div>
@@ -25,9 +25,9 @@
</div>
</div>
</div>
- <div class="_formItem colorPicker">
- <div class="_formLabel">{{ $ts.textColor }}</div>
- <div class="_formPanel colors">
+ <div class="_debobigegoItem colorPicker">
+ <div class="_debobigegoLabel">{{ $ts.textColor }}</div>
+ <div class="_debobigegoPanel colors">
<div class="row">
<button v-for="color in fgColors" :key="color" @click="setFgColor(color)" class="color char _button" :class="{ active: (theme.props.fg === color.forLight) || (theme.props.fg === color.forDark) }">
<div class="preview" :style="{ color: color.forPreview ? color.forPreview : theme.base === 'light' ? '#5f5f5f' : '#dadada' }">A</div>
@@ -37,7 +37,7 @@
</div>
<FormGroup v-if="codeEnabled">
- <FormTextarea v-model:value="themeCode" tall>
+ <FormTextarea v-model="themeCode" tall>
<span>{{ $ts._theme.code }}</span>
</FormTextarea>
<FormButton @click="applyThemeCode" primary>{{ $ts.apply }}</FormButton>
@@ -45,7 +45,7 @@
<FormButton v-else @click="codeEnabled = true"><i class="fas fa-code"></i> {{ $ts.editCode }}</FormButton>
<FormGroup v-if="descriptionEnabled">
- <FormTextarea v-model:value="description">
+ <FormTextarea v-model="description">
<span>{{ $ts._theme.description }}</span>
</FormTextarea>
</FormGroup>
@@ -65,10 +65,10 @@ import * as tinycolor from 'tinycolor2';
import { v4 as uuid} from 'uuid';
import * as JSON5 from 'json5';
-import FormBase from '@client/components/form/base.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormGroup from '@client/components/form/group.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
import { Theme, applyTheme, validateTheme, darkTheme, lightTheme } from '@client/scripts/theme';
import { host } from '@client/config';
diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue
index 9dda82462d..dfabcbf84b 100644
--- a/src/client/pages/timeline.vue
+++ b/src/client/pages/timeline.vue
@@ -1,18 +1,21 @@
<template>
-<div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }">
- <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/>
- <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/>
+<div v-hotkey.global="keymap">
+ <MkHeader :info="header"/>
+ <div class="cmuxhskf" v-size="{ min: [800] }">
+ <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/>
+ <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/>
- <div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
- <div class="tl _block">
- <XTimeline ref="tl" class="tl"
- :key="src"
- :src="src"
- :sound="true"
- @before="before()"
- @after="after()"
- @queue="queueUpdated"
- />
+ <div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
+ <div class="tl _block">
+ <XTimeline ref="tl" class="tl"
+ :key="src"
+ :src="src"
+ :sound="true"
+ @before="before()"
+ @after="after()"
+ @queue="queueUpdated"
+ />
+ </div>
</div>
</div>
</template>
@@ -43,6 +46,11 @@ export default defineComponent({
title: this.$ts.timeline,
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)',
+ })),
+ header: computed(() => ({
+ title: this.$ts.timeline,
+ 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: [{
icon: 'fas fa-list-ul',
text: this.$ts.lists,
@@ -129,7 +137,7 @@ export default defineComponent({
},
top() {
- scroll(this.$el, 0);
+ scroll(this.$el, { top: 0 });
},
async chooseList(ev) {
@@ -207,6 +215,10 @@ export default defineComponent({
}
}
+ > .post-form {
+ border-radius: var(--radius);
+ }
+
> .tl {
background: var(--bg);
border-radius: var(--radius);
diff --git a/src/client/pages/user-ap-info.vue b/src/client/pages/user-ap-info.vue
index c08a352571..cbdff874ed 100644
--- a/src/client/pages/user-ap-info.vue
+++ b/src/client/pages/user-ap-info.vue
@@ -58,14 +58,14 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import FormObjectView from '@client/components/form/object-view.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormObjectView from '@client/components/debobigego/object-view.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import number from '@client/filters/number';
import bytes from '@client/filters/bytes';
diff --git a/src/client/pages/user-info.vue b/src/client/pages/user-info.vue
index 503982652b..bf67fc853a 100644
--- a/src/client/pages/user-info.vue
+++ b/src/client/pages/user-info.vue
@@ -1,7 +1,7 @@
<template>
<FormBase>
<FormSuspense :p="init">
- <div class="_formItem aeakzknw">
+ <div class="_debobigegoItem aeakzknw">
<MkAvatar class="avatar" :user="user" :show-indicator="true"/>
</div>
@@ -20,9 +20,9 @@
</FormGroup>
<FormGroup v-if="iAmModerator">
- <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $ts.moderator }}</FormSwitch>
- <FormSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $ts.silence }}</FormSwitch>
- <FormSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $ts.suspend }}</FormSwitch>
+ <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:modelValue="toggleModerator" v-model="moderator">{{ $ts.moderator }}</FormSwitch>
+ <FormSwitch @update:modelValue="toggleSilence" v-model="silenced">{{ $ts.silence }}</FormSwitch>
+ <FormSwitch @update:modelValue="toggleSuspend" v-model="suspended">{{ $ts.suspend }}</FormSwitch>
</FormGroup>
<FormGroup>
@@ -56,15 +56,15 @@
<script lang="ts">
import { computed, defineAsyncComponent, defineComponent } from 'vue';
-import FormObjectView from '@client/components/form/object-view.vue';
-import FormTextarea from '@client/components/form/textarea.vue';
-import FormSwitch from '@client/components/form/switch.vue';
-import FormLink from '@client/components/form/link.vue';
-import FormBase from '@client/components/form/base.vue';
-import FormGroup from '@client/components/form/group.vue';
-import FormButton from '@client/components/form/button.vue';
-import FormKeyValueView from '@client/components/form/key-value-view.vue';
-import FormSuspense from '@client/components/form/suspense.vue';
+import FormObjectView from '@client/components/debobigego/object-view.vue';
+import FormTextarea from '@client/components/debobigego/textarea.vue';
+import FormSwitch from '@client/components/debobigego/switch.vue';
+import FormLink from '@client/components/debobigego/link.vue';
+import FormBase from '@client/components/debobigego/base.vue';
+import FormGroup from '@client/components/debobigego/group.vue';
+import FormButton from '@client/components/debobigego/button.vue';
+import FormKeyValueView from '@client/components/debobigego/key-value-view.vue';
+import FormSuspense from '@client/components/debobigego/suspense.vue';
import * as os from '@client/os';
import number from '@client/filters/number';
import bytes from '@client/filters/bytes';
diff --git a/src/client/pages/user-list-timeline.vue b/src/client/pages/user-list-timeline.vue
index 491fe948c1..b5e37d4843 100644
--- a/src/client/pages/user-list-timeline.vue
+++ b/src/client/pages/user-list-timeline.vue
@@ -89,7 +89,7 @@ export default defineComponent({
},
top() {
- scroll(this.$el, 0);
+ scroll(this.$el, { top: 0 });
},
settings() {
diff --git a/src/client/pages/user/clips.vue b/src/client/pages/user/clips.vue
index fc40d583c6..53ee554383 100644
--- a/src/client/pages/user/clips.vue
+++ b/src/client/pages/user/clips.vue
@@ -12,7 +12,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkPagination from '@client/components/ui/pagination.vue';
-import { userPage, acct } from '@client/filters/user';
export default defineComponent({
components: {
@@ -43,12 +42,6 @@ export default defineComponent({
this.$refs.list.reload();
}
},
-
- methods: {
- userPage,
-
- acct
- }
});
</script>
diff --git a/src/client/pages/user/follow-list.vue b/src/client/pages/user/follow-list.vue
index f6df28309f..1f5ab5993c 100644
--- a/src/client/pages/user/follow-list.vue
+++ b/src/client/pages/user/follow-list.vue
@@ -12,7 +12,6 @@
import { defineComponent } from 'vue';
import MkUserInfo from '@client/components/user-info.vue';
import MkPagination from '@client/components/ui/pagination.vue';
-import { userPage, acct } from '@client/filters/user';
export default defineComponent({
components: {
@@ -51,12 +50,6 @@ export default defineComponent({
user() {
this.$refs.list.reload();
}
- },
-
- methods: {
- userPage,
-
- acct
}
});
</script>
diff --git a/src/client/pages/user/gallery.vue b/src/client/pages/user/gallery.vue
index 67a5fac109..c21b3e6428 100644
--- a/src/client/pages/user/gallery.vue
+++ b/src/client/pages/user/gallery.vue
@@ -12,7 +12,6 @@
import { defineComponent } from 'vue';
import MkGalleryPostPreview from '@client/components/gallery-post-preview.vue';
import MkPagination from '@client/components/ui/pagination.vue';
-import { userPage, acct } from '@client/filters/user';
export default defineComponent({
components: {
@@ -43,12 +42,6 @@ export default defineComponent({
user() {
this.$refs.list.reload();
}
- },
-
- methods: {
- userPage,
-
- acct
}
});
</script>
diff --git a/src/client/pages/user/index.timeline.vue b/src/client/pages/user/index.timeline.vue
index 287e6c8b22..c3444f26f6 100644
--- a/src/client/pages/user/index.timeline.vue
+++ b/src/client/pages/user/index.timeline.vue
@@ -1,6 +1,6 @@
<template>
<div class="yrzkoczt" v-sticky-container>
- <MkTab v-model:value="with_" class="_gap tab">
+ <MkTab v-model="with_" class="tab">
<option :value="null">{{ $ts.notes }}</option>
<option value="replies">{{ $ts.notesAndReplies }}</option>
<option value="files">{{ $ts.withFiles }}</option>
@@ -60,6 +60,8 @@ export default defineComponent({
<style lang="scss" scoped>
.yrzkoczt {
> .tab {
+ margin: calc(var(--margin) / 2) 0;
+ padding: calc(var(--margin) / 2) 0;
background: var(--bg);
}
}
diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue
index 86dc7361b5..0ddf73d572 100644
--- a/src/client/pages/user/index.vue
+++ b/src/client/pages/user/index.vue
@@ -1,98 +1,117 @@
<template>
-<transition name="fade" mode="out-in">
- <div class="ftskorzw wide" v-if="user && narrow === false">
- <MkRemoteCaution v-if="user.host != null" :href="user.url"/>
+<div>
+ <MkHeader :info="header"/>
+ <transition name="fade" mode="out-in">
+ <div class="ftskorzw wide" v-if="user && narrow === false">
+ <MkRemoteCaution v-if="user.host != null" :href="user.url"/>
- <div class="banner-container" :style="style">
- <div class="banner" ref="banner" :style="style"></div>
- </div>
- <div class="contents">
- <div class="side _forceContainerFull_">
- <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
- <div class="name">
- <MkUserName :user="user" :nowrap="false" class="name"/>
- <MkAcct :user="user" :detail="true" class="acct"/>
- </div>
- <div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div>
- <div class="status">
- <MkA :to="userPage(user)" :class="{ active: page === 'index' }">
- <b>{{ number(user.notesCount) }}</b>
- <span>{{ $ts.notes }}</span>
- </MkA>
- <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
- <b>{{ number(user.followingCount) }}</b>
- <span>{{ $ts.following }}</span>
- </MkA>
- <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
- <b>{{ number(user.followersCount) }}</b>
- <span>{{ $ts.followers }}</span>
- </MkA>
- </div>
- <div class="description">
- <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
- <p v-else class="empty">{{ $ts.noAccountDescription }}</p>
- </div>
- <div class="fields system">
- <dl class="field" v-if="user.location">
- <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
- <dd class="value">{{ user.location }}</dd>
- </dl>
- <dl class="field" v-if="user.birthday">
- <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
- <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
- </dl>
- <dl class="field">
- <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
- <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
- </dl>
- </div>
- <div class="fields" v-if="user.fields.length > 0">
- <dl class="field" v-for="(field, i) in user.fields" :key="i">
- <dt class="name">
- <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
- </dt>
- <dd class="value">
- <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
- </dd>
- </dl>
- </div>
- <XActivity :user="user" :key="user.id" class="_gap"/>
- <XPhotos :user="user" :key="user.id" class="_gap"/>
+ <div class="banner-container" :style="style">
+ <div class="banner" ref="banner" :style="style"></div>
</div>
- <div class="main">
- <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>
- <template v-if="page === 'index'">
- <div v-if="user.pinnedNotes.length > 0" class="_gap">
- <XNote v-for="note in user.pinnedNotes" class="note _gap" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
+ <div class="contents">
+ <div class="side _forceContainerFull_">
+ <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
+ <div class="name">
+ <MkUserName :user="user" :nowrap="false" class="name"/>
+ <MkAcct :user="user" :detail="true" class="acct"/>
</div>
- <div class="_gap">
- <XUserTimeline :user="user"/>
+ <div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div>
+ <div class="status">
+ <MkA :to="userPage(user)" :class="{ active: page === 'index' }">
+ <b>{{ number(user.notesCount) }}</b>
+ <span>{{ $ts.notes }}</span>
+ </MkA>
+ <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
+ <b>{{ number(user.followingCount) }}</b>
+ <span>{{ $ts.following }}</span>
+ </MkA>
+ <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
+ <b>{{ number(user.followersCount) }}</b>
+ <span>{{ $ts.followers }}</span>
+ </MkA>
</div>
- </template>
- <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_gap"/>
- <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_gap"/>
- <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
- <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
+ <div class="description">
+ <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
+ <p v-else class="empty">{{ $ts.noAccountDescription }}</p>
+ </div>
+ <div class="fields system">
+ <dl class="field" v-if="user.location">
+ <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
+ <dd class="value">{{ user.location }}</dd>
+ </dl>
+ <dl class="field" v-if="user.birthday">
+ <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
+ <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
+ </dl>
+ <dl class="field">
+ <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
+ <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
+ </dl>
+ </div>
+ <div class="fields" v-if="user.fields.length > 0">
+ <dl class="field" v-for="(field, i) in user.fields" :key="i">
+ <dt class="name">
+ <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
+ </dt>
+ <dd class="value">
+ <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
+ </dd>
+ </dl>
+ </div>
+ <XActivity :user="user" :key="user.id" class="_gap"/>
+ <XPhotos :user="user" :key="user.id" class="_gap"/>
+ </div>
+ <div class="main">
+ <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>
+ <template v-if="page === 'index'">
+ <div v-if="user.pinnedNotes.length > 0" class="_gap">
+ <XNote v-for="note in user.pinnedNotes" class="note _gap" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
+ </div>
+ <div class="_gap">
+ <XUserTimeline :user="user"/>
+ </div>
+ </template>
+ <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_gap"/>
+ <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_gap"/>
+ <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
+ <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
+ </div>
</div>
</div>
- </div>
- <div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }">
- <!-- TODO -->
- <!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> -->
- <!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> -->
+ <div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }">
+ <!-- TODO -->
+ <!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> -->
+ <!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> -->
- <div class="profile">
- <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/>
+ <div class="profile">
+ <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/>
- <div class="_block main" :key="user.id">
- <div class="banner-container" :style="style">
- <div class="banner" ref="banner" :style="style"></div>
- <div class="fade"></div>
+ <div class="_block main" :key="user.id">
+ <div class="banner-container" :style="style">
+ <div class="banner" ref="banner" :style="style"></div>
+ <div class="fade"></div>
+ <div class="title">
+ <MkUserName class="name" :user="user" :nowrap="true"/>
+ <div class="bottom">
+ <span class="username"><MkAcct :user="user" :detail="true" /></span>
+ <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
+ <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
+ <span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span>
+ <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
+ </div>
+ </div>
+ <span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span>
+ <div class="actions" v-if="$i">
+ <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
+ <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
+ </div>
+ </div>
+ <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
<div class="title">
- <MkUserName class="name" :user="user" :nowrap="true"/>
+ <MkUserName :user="user" :nowrap="false" class="name"/>
<div class="bottom">
<span class="username"><MkAcct :user="user" :detail="true" /></span>
<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
@@ -101,92 +120,76 @@
<span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
</div>
</div>
- <span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span>
- <div class="actions" v-if="$i">
- <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
- <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
+ <div class="description">
+ <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
+ <p v-else class="empty">{{ $ts.noAccountDescription }}</p>
</div>
- </div>
- <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
- <div class="title">
- <MkUserName :user="user" :nowrap="false" class="name"/>
- <div class="bottom">
- <span class="username"><MkAcct :user="user" :detail="true" /></span>
- <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
- <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
- <span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span>
- <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
+ <div class="fields system">
+ <dl class="field" v-if="user.location">
+ <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
+ <dd class="value">{{ user.location }}</dd>
+ </dl>
+ <dl class="field" v-if="user.birthday">
+ <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
+ <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
+ </dl>
+ <dl class="field">
+ <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
+ <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
+ </dl>
+ </div>
+ <div class="fields" v-if="user.fields.length > 0">
+ <dl class="field" v-for="(field, i) in user.fields" :key="i">
+ <dt class="name">
+ <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
+ </dt>
+ <dd class="value">
+ <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
+ </dd>
+ </dl>
+ </div>
+ <div class="status">
+ <MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime>
+ <b>{{ number(user.notesCount) }}</b>
+ <span>{{ $ts.notes }}</span>
+ </MkA>
+ <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime>
+ <b>{{ number(user.followingCount) }}</b>
+ <span>{{ $ts.following }}</span>
+ </MkA>
+ <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime>
+ <b>{{ number(user.followersCount) }}</b>
+ <span>{{ $ts.followers }}</span>
+ </MkA>
</div>
- </div>
- <div class="description">
- <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
- <p v-else class="empty">{{ $ts.noAccountDescription }}</p>
- </div>
- <div class="fields system">
- <dl class="field" v-if="user.location">
- <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
- <dd class="value">{{ user.location }}</dd>
- </dl>
- <dl class="field" v-if="user.birthday">
- <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
- <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
- </dl>
- <dl class="field">
- <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
- <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
- </dl>
- </div>
- <div class="fields" v-if="user.fields.length > 0">
- <dl class="field" v-for="(field, i) in user.fields" :key="i">
- <dt class="name">
- <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
- </dt>
- <dd class="value">
- <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
- </dd>
- </dl>
- </div>
- <div class="status">
- <MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime>
- <b>{{ number(user.notesCount) }}</b>
- <span>{{ $ts.notes }}</span>
- </MkA>
- <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime>
- <b>{{ number(user.followingCount) }}</b>
- <span>{{ $ts.following }}</span>
- </MkA>
- <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime>
- <b>{{ number(user.followersCount) }}</b>
- <span>{{ $ts.followers }}</span>
- </MkA>
</div>
</div>
- </div>
- <div class="contents">
- <template v-if="page === 'index'">
- <div>
- <div v-if="user.pinnedNotes.length > 0" class="_gap">
- <XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
+ <div class="contents">
+ <template v-if="page === 'index'">
+ <div>
+ <div v-if="user.pinnedNotes.length > 0" class="_gap">
+ <XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
+ </div>
+ <MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo>
+ <XPhotos :user="user" :key="user.id"/>
+ <XActivity :user="user" :key="user.id"/>
</div>
- <MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo>
- <XPhotos :user="user" :key="user.id"/>
- <XActivity :user="user" :key="user.id"/>
- </div>
- <div>
- <XUserTimeline :user="user"/>
- </div>
- </template>
- <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_content _gap"/>
- <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_content _gap"/>
- <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
- <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
- <XGallery v-else-if="page === 'gallery'" :user="user" class="_gap"/>
+ <div>
+ <XUserTimeline :user="user"/>
+ </div>
+ </template>
+ <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_content _gap"/>
+ <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_content _gap"/>
+ <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
+ <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
+ <XGallery v-else-if="page === 'gallery'" :user="user" class="_gap"/>
+ </div>
</div>
- </div>
- <MkError v-else-if="error" @retry="fetch()"/>
- <MkLoading v-else/>
-</transition>
+ <MkError v-else-if="error" @retry="fetch()"/>
+ <MkLoading v-else/>
+ </transition>
+</div>
</template>
<script lang="ts">
@@ -242,6 +245,15 @@ export default defineComponent({
data() {
return {
[symbols.PAGE_INFO]: computed(() => this.user ? {
+ icon: 'fas fa-user',
+ title: this.user.name ? `${this.user.name} (@${this.user.username})` : `@${this.user.username}`,
+ path: `/@${this.user.username}`,
+ share: {
+ title: this.user.name,
+ },
+ bg: 'var(--bg)',
+ } : null),
+ header: computed(() => this.user ? {
title: this.user.name ? `${this.user.name} (@${this.user.username})` : `@${this.user.username}`,
subtitle: `@${getAcct(this.user)}`,
userName: this.user,
@@ -255,21 +267,22 @@ export default defineComponent({
active: this.page === 'index',
title: this.$ts.overview,
icon: 'fas fa-home',
+ onClick: () => { this.$router.push('/@' + getAcct(this.user)); },
}, {
active: this.page === 'clips',
title: this.$ts.clips,
icon: 'fas fa-paperclip',
- onClick: () => { this.page = 'clips'; },
+ onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/clips'); },
}, {
active: this.page === 'pages',
title: this.$ts.pages,
icon: 'fas fa-file-alt',
- onClick: () => { this.page = 'pages'; },
+ onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/pages'); },
}, {
active: this.page === 'gallery',
title: this.$ts.gallery,
icon: 'fas fa-icons',
- onClick: () => { this.page = 'gallery'; },
+ onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/gallery'); },
}]
} : null),
user: null,
@@ -814,7 +827,7 @@ export default defineComponent({
}
}
-._flat_ .ftskorzw.narrow {
+._fitSide_ .ftskorzw.narrow {
> .profile {
> .warn {
margin: 0;
diff --git a/src/client/pages/user/pages.vue b/src/client/pages/user/pages.vue
index 819bd9f2ef..ece418cf62 100644
--- a/src/client/pages/user/pages.vue
+++ b/src/client/pages/user/pages.vue
@@ -10,7 +10,6 @@
import { defineComponent } from 'vue';
import MkPagePreview from '@client/components/page-preview.vue';
import MkPagination from '@client/components/ui/pagination.vue';
-import { userPage, acct } from '@client/filters/user';
export default defineComponent({
components: {
@@ -41,12 +40,6 @@ export default defineComponent({
user() {
this.$refs.list.reload();
}
- },
-
- methods: {
- userPage,
-
- acct
}
});
</script>
diff --git a/src/client/pages/welcome.entrance.a.vue b/src/client/pages/welcome.entrance.a.vue
index 82b439ddd3..13f0993793 100644
--- a/src/client/pages/welcome.entrance.a.vue
+++ b/src/client/pages/welcome.entrance.a.vue
@@ -27,7 +27,7 @@
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
- <MkButton @click="signup()" inline primary data-cy-signup>{{ $ts.signup }}</MkButton>
+ <MkButton @click="signup()" inline gradate data-cy-signup style="margin-right: 12px;">{{ $ts.signup }}</MkButton>
<MkButton @click="signin()" inline data-cy-signin>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
diff --git a/src/client/pages/welcome.entrance.b.vue b/src/client/pages/welcome.entrance.b.vue
index a5c12f09e2..163fc1e35f 100644
--- a/src/client/pages/welcome.entrance.b.vue
+++ b/src/client/pages/welcome.entrance.b.vue
@@ -12,7 +12,7 @@
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
- <MkButton class="signup" @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
+ <MkButton class="signup" @click="signup()" inline gradate>{{ $ts.signup }}</MkButton>
<MkButton class="signin" @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
diff --git a/src/client/pages/welcome.entrance.c.vue b/src/client/pages/welcome.entrance.c.vue
index 2c8db6e264..bf1c9b1998 100644
--- a/src/client/pages/welcome.entrance.c.vue
+++ b/src/client/pages/welcome.entrance.c.vue
@@ -24,7 +24,7 @@
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
- <MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
+ <MkButton @click="signup()" inline gradate>{{ $ts.signup }}</MkButton>
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
diff --git a/src/client/pages/welcome.setup.vue b/src/client/pages/welcome.setup.vue
index d0091bef67..dfefecc8fa 100644
--- a/src/client/pages/welcome.setup.vue
+++ b/src/client/pages/welcome.setup.vue
@@ -24,7 +24,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
-import MkInput from '@client/components/ui/input.vue';
+import MkInput from '@client/components/form/input.vue';
import { host } from '@client/config';
import * as os from '@client/os';
import { login } from '@client/account';
diff --git a/src/client/router.ts b/src/client/router.ts
index 573f285c79..56dc948669 100644
--- a/src/client/router.ts
+++ b/src/client/router.ts
@@ -23,6 +23,7 @@ const defaultRoutes = [
{ path: '/@:acct/room', props: true, component: page('room/room') },
{ path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) },
{ path: '/reset-password/:token?', component: page('reset-password'), props: route => ({ token: route.params.token }) },
+ { path: '/signup-complete/:code', component: page('signup-complete'), props: route => ({ code: route.params.code }) },
{ path: '/announcements', component: page('announcements') },
{ path: '/about', component: page('about') },
{ path: '/about-misskey', component: page('about-misskey') },
diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts
index 924d6a62ee..c0c33b2c7e 100644
--- a/src/client/scripts/autocomplete.ts
+++ b/src/client/scripts/autocomplete.ts
@@ -7,9 +7,9 @@ export class Autocomplete {
private suggestion: {
x: Ref<number>;
y: Ref<number>;
- q: Ref<string>;
+ q: Ref<string | null>;
close: Function;
- };
+ } | null;
private textarea: any;
private vm: any;
private currentType: string;
@@ -70,11 +70,13 @@ export class Autocomplete {
const mentionIndex = text.lastIndexOf('@');
const hashtagIndex = text.lastIndexOf('#');
const emojiIndex = text.lastIndexOf(':');
+ const mfmTagIndex = text.lastIndexOf('$');
const max = Math.max(
mentionIndex,
hashtagIndex,
- emojiIndex);
+ emojiIndex,
+ mfmTagIndex);
if (max == -1) {
this.close();
@@ -83,6 +85,7 @@ export class Autocomplete {
const isMention = mentionIndex != -1;
const isHashtag = hashtagIndex != -1;
+ const isMfmTag = mfmTagIndex != -1;
const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
let opened = false;
@@ -114,6 +117,14 @@ export class Autocomplete {
}
}
+ if (isMfmTag && !opened) {
+ const mfmTag = text.substr(mfmTagIndex + 1);
+ if (!mfmTag.includes(' ')) {
+ this.open('mfmTag', mfmTag.replace('[', ''));
+ opened = true;
+ }
+ }
+
if (!opened) {
this.close();
}
@@ -122,7 +133,7 @@ export class Autocomplete {
/**
* サジェストを提示します。
*/
- private async open(type: string, q: string) {
+ private async open(type: string, q: string | null) {
if (type != this.currentType) {
this.close();
}
@@ -244,6 +255,22 @@ export class Autocomplete {
const pos = trimmedBefore.length + value.length;
this.textarea.setSelectionRange(pos, pos);
});
+ } else if (type == 'mfmTag') {
+ const source = this.text;
+
+ const before = source.substr(0, caret);
+ const trimmedBefore = before.substring(0, before.lastIndexOf('$'));
+ const after = source.substr(caret);
+
+ // 挿入
+ this.text = `${trimmedBefore}$[${value} ]${after}`;
+
+ // キャレットを戻す
+ this.vm.$nextTick(() => {
+ this.textarea.focus();
+ const pos = trimmedBefore.length + (value.length + 3);
+ this.textarea.setSelectionRange(pos, pos);
+ });
}
}
}
diff --git a/src/client/scripts/idb-proxy.ts b/src/client/scripts/idb-proxy.ts
index 21c4dcff65..5f76ae30bb 100644
--- a/src/client/scripts/idb-proxy.ts
+++ b/src/client/scripts/idb-proxy.ts
@@ -4,7 +4,6 @@ import {
get as iget,
set as iset,
del as idel,
- createStore,
} from 'idb-keyval';
const fallbackName = (key: string) => `idbfallback::${key}`;
@@ -13,9 +12,9 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true;
if (idbAvailable) {
try {
- await createStore('keyval-store', 'keyval');
+ await iset('idb-test', 'test');
} catch (e) {
- console.error('idb open error', e);
+ console.error('idb error', e);
idbAvailable = false;
}
}
diff --git a/src/client/scripts/physics.ts b/src/client/scripts/physics.ts
index 8e971d5844..445b6296eb 100644
--- a/src/client/scripts/physics.ts
+++ b/src/client/scripts/physics.ts
@@ -79,7 +79,7 @@ export function physics(container: HTMLElement) {
objEl.offsetWidth,
objEl.offsetHeight,
{
- chamfer: { radius: parseInt(style.borderRadius, 10) },
+ chamfer: { radius: parseInt(style.borderRadius || '0', 10) },
restitution: 0.5
}
);
diff --git a/src/client/scripts/scroll.ts b/src/client/scripts/scroll.ts
index bc6d1530c5..621fe88105 100644
--- a/src/client/scripts/scroll.ts
+++ b/src/client/scripts/scroll.ts
@@ -1,3 +1,5 @@
+type ScrollBehavior = 'auto' | 'smooth' | 'instant';
+
export function getScrollContainer(el: Element | null): Element | null {
if (el == null || el.tagName === 'BODY') return null;
const overflow = window.getComputedStyle(el).getPropertyValue('overflow');
@@ -45,21 +47,25 @@ export function onScrollBottom(el: Element, cb) {
container.addEventListener('scroll', onScroll, { passive: true });
}
-export function scroll(el: Element, top: number) {
+export function scroll(el: Element, options: {
+ top?: number;
+ left?: number;
+ behavior?: ScrollBehavior;
+}) {
const container = getScrollContainer(el);
if (container == null) {
- window.scroll({ top: top, behavior: 'instant' });
+ window.scroll(options);
} else {
- container.scrollTop = top;
+ container.scroll(options);
}
}
-export function scrollToTop(el: Element) {
- scroll(el, 0);
+export function scrollToTop(el: Element, options: { behavior?: ScrollBehavior; } = {}) {
+ scroll(el, { top: 0, ...options });
}
-export function scrollToBottom(el: Element) {
- scroll(el, 99999); // TODO: ちゃんと計算する
+export function scrollToBottom(el: Element, options: { behavior?: ScrollBehavior; } = {}) {
+ scroll(el, { top: 99999, ...options }); // TODO: ちゃんと計算する
}
export function isBottom(el: Element, asobi = 0) {
diff --git a/src/client/scripts/theme.ts b/src/client/scripts/theme.ts
index 3fb5666a72..e79d54fa6d 100644
--- a/src/client/scripts/theme.ts
+++ b/src/client/scripts/theme.ts
@@ -1,3 +1,4 @@
+import { globalEvents } from '@client/events';
import * as tinycolor from 'tinycolor2';
export type Theme = {
@@ -24,6 +25,7 @@ export const builtinThemes = [
require('@client/themes/d-persimmon.json5'),
require('@client/themes/d-astro.json5'),
require('@client/themes/d-future.json5'),
+ require('@client/themes/d-botanical.json5'),
require('@client/themes/d-black.json5'),
] as Theme[];
@@ -62,6 +64,9 @@ export function applyTheme(theme: Theme, persist = true) {
if (persist) {
localStorage.setItem('theme', JSON.stringify(props));
}
+
+ // 色計算など再度行えるようにクライアント全体に通知
+ globalEvents.emit('themeChanged');
}
function compile(theme: Theme): Record<string, string> {
@@ -87,6 +92,8 @@ function compile(theme: Theme): Record<string, string> {
case 'darken': return color.darken(arg);
case 'lighten': return color.lighten(arg);
case 'alpha': return color.setAlpha(arg);
+ case 'hue': return color.spin(arg);
+ case 'saturate': return color.saturate(arg);
}
}
diff --git a/src/client/style.scss b/src/client/style.scss
index 0318013f60..d6bad5a24d 100644
--- a/src/client/style.scss
+++ b/src/client/style.scss
@@ -178,7 +178,7 @@ hr {
pointer-events: none;
}
- &:focus {
+ &:focus-visible {
outline: none;
}
@@ -202,6 +202,20 @@ hr {
}
}
+._buttonGradate {
+ @extend ._buttonPrimary;
+ color: var(--fgOnAccent);
+ background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+
+ &:not(:disabled):hover {
+ background: linear-gradient(90deg, var(--X8), var(--X8));
+ }
+
+ &:not(:disabled):active {
+ background: linear-gradient(90deg, var(--X8), var(--X8));
+ }
+}
+
._help {
color: var(--accent);
cursor: help
@@ -366,7 +380,7 @@ hr {
}
}
-._flat_ {
+._fitSide_ {
--root-margin: 0px;
--baseContentWidth: 100%;
--panelBorder: none;
@@ -425,12 +439,18 @@ hr {
}
}
-._inputNoTopMargin {
- margin-top: 0 !important;
+._formBlock {
+ margin: 20px 0;
}
-._inputNoBottomMargin {
- margin-bottom: 0 !important;
+._formRoot {
+ > ._formBlock:first-child {
+ margin-top: 0;
+ }
+
+ > ._formBlock:last-child {
+ margin-bottom: 0;
+ }
}
._table {
@@ -503,7 +523,7 @@ hr {
padding: 5px;
}
-.prism-editor__textarea:focus {
+.prism-editor__textarea:focus-visible {
outline: none;
}
diff --git a/src/client/themes/_dark.json5 b/src/client/themes/_dark.json5
index e1d5779a80..d8be16f60a 100644
--- a/src/client/themes/_dark.json5
+++ b/src/client/themes/_dark.json5
@@ -51,11 +51,14 @@
infoFg: '#fff',
infoWarnBg: '#42321c',
infoWarnFg: '#ffbd3e',
+ switchBg: 'rgba(255, 255, 255, 0.15)',
cwBg: '#687390',
cwFg: '#393f4f',
cwHoverBg: '#707b97',
buttonBg: 'rgba(255, 255, 255, 0.05)',
buttonHoverBg: 'rgba(255, 255, 255, 0.1)',
+ buttonGradateA: '@accent',
+ buttonGradateB: ':hue<20<@accent',
inputBorder: 'rgba(255, 255, 255, 0.1)',
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
diff --git a/src/client/themes/_light.json5 b/src/client/themes/_light.json5
index 87895e6406..251aa36c7a 100644
--- a/src/client/themes/_light.json5
+++ b/src/client/themes/_light.json5
@@ -51,11 +51,14 @@
infoFg: '#72818a',
infoWarnBg: '#fff0db',
infoWarnFg: '#8f6e31',
+ switchBg: 'rgba(0, 0, 0, 0.15)',
cwBg: '#b1b9c1',
cwFg: '#fff',
cwHoverBg: '#bbc4ce',
buttonBg: 'rgba(0, 0, 0, 0.05)',
buttonHoverBg: 'rgba(0, 0, 0, 0.1)',
+ buttonGradateA: '@accent',
+ buttonGradateB: ':hue<20<@accent',
inputBorder: 'rgba(0, 0, 0, 0.1)',
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
listItemHoverBg: 'rgba(0, 0, 0, 0.03)',
diff --git a/src/client/themes/d-astro.json5 b/src/client/themes/d-astro.json5
index 08846dec20..2350e3d46d 100644
--- a/src/client/themes/d-astro.json5
+++ b/src/client/themes/d-astro.json5
@@ -46,6 +46,8 @@
navIndicator: '@accent',
accentLighten: ':lighten<10<@accent',
buttonHoverBg: 'rgba(255, 255, 255, 0.1)',
+ buttonGradateA: '@accent',
+ buttonGradateB: ':hue<-20<@accent',
driveFolderBg: ':alpha<0.3<@accent',
fgHighlighted: ':lighten<3<@fg',
panelHeaderBg: ':lighten<3<@panel',
diff --git a/src/client/themes/d-botanical.json5 b/src/client/themes/d-botanical.json5
new file mode 100644
index 0000000000..c03b95e2d7
--- /dev/null
+++ b/src/client/themes/d-botanical.json5
@@ -0,0 +1,26 @@
+{
+ id: '504debaf-4912-6a4c-5059-1db08a76b737',
+
+ name: 'Mi Botanical Dark',
+ author: 'syuilo',
+
+ base: 'dark',
+
+ props: {
+ accent: 'rgb(148, 179, 0)',
+ bg: 'rgb(37, 38, 36)',
+ fg: 'rgb(216, 212, 199)',
+ fgHighlighted: '#fff',
+ divider: 'rgba(255, 255, 255, 0.14)',
+ panel: 'rgb(47, 47, 44)',
+ panelHeaderBg: '@panel',
+ panelHeaderDivider: '@divider',
+ header: ':alpha<0.7<@panel',
+ navBg: '#363636',
+ renote: '@accent',
+ mention: 'rgb(212, 153, 76)',
+ mentionMe: 'rgb(212, 210, 76)',
+ hashtag: '#5bcbb0',
+ link: '@accent',
+ },
+}
diff --git a/src/client/themes/d-future.json5 b/src/client/themes/d-future.json5
index 05ffe87bf0..1882609121 100644
--- a/src/client/themes/d-future.json5
+++ b/src/client/themes/d-future.json5
@@ -21,5 +21,7 @@
mentionMe: '@accent',
hashtag: '#70c0e8',
link: '#e88080',
+ buttonGradateA: '@accent',
+ buttonGradateB: ':saturate<30<:hue<30<@accent',
},
}
diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue
deleted file mode 100644
index 1e0db9a3a1..0000000000
--- a/src/client/ui/_common_/header.vue
+++ /dev/null
@@ -1,302 +0,0 @@
-<template>
-<div class="fdidabkb" :class="{ slim: titleOnly || narrow }" :style="`--height:${height};`" :key="key">
- <transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear>
- <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>
- </div>
- </transition>
- <template v-if="info">
- <div class="titleContainer" @click="showTabsPopup">
- <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"/>
-
- <div 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 class="subtitle" v-if="!narrow && info.subtitle">
- {{ info.subtitle }}
- </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 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>
- </template>
- <div class="buttons right">
- <template v-if="info && 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>
- </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="closeButton" class="_button button" @click.stop="$emit('close')" @touchstart="preventDrag" v-tooltip="$ts.close"><i class="fas fa-times"></i></button>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { popupMenu } from '@client/os';
-import { url } from '@client/config';
-
-export default defineComponent({
- props: {
- info: {
- required: true
- },
- menu: {
- required: false
- },
- backButton: {
- type: Boolean,
- required: false,
- default: false,
- },
- closeButton: {
- type: Boolean,
- required: false,
- default: false,
- },
- titleOnly: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- data() {
- return {
- narrow: false,
- height: 0,
- key: 0,
- };
- },
-
- computed: {
- hasTabs(): boolean {
- return this.info.tabs && this.info.tabs.length > 0;
- },
-
- shouldShowMenu() {
- if (this.info == null) return false;
- if (this.info.actions != null && this.narrow) return true;
- if (this.info.menu != null) return true;
- if (this.info.share != null) return true;
- if (this.menu != null) return true;
- return false;
- }
- },
-
- watch: {
- info() {
- this.key++;
- },
- },
-
- mounted() {
- this.height = this.$el.parentElement.offsetHeight + 'px';
- this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500;
- new ResizeObserver((entries, observer) => {
- this.height = this.$el.parentElement.offsetHeight + 'px';
- this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500;
- }).observe(this.$el);
- },
-
- methods: {
- share() {
- navigator.share({
- url: url + this.info.path,
- ...this.info.share,
- });
- },
-
- showMenu(ev) {
- let menu = this.info.menu ? this.info.menu() : [];
- if (this.narrow && this.info.actions) {
- menu = [...this.info.actions.map(x => ({
- text: x.text,
- icon: x.icon,
- action: x.handler
- })), menu.length > 0 ? null : undefined, ...menu];
- }
- if (this.info.share) {
- if (menu.length > 0) menu.push(null);
- menu.push({
- text: this.$ts.share,
- icon: 'fas fa-share-alt',
- action: this.share
- });
- }
- if (this.menu) {
- if (menu.length > 0) menu.push(null);
- menu = menu.concat(this.menu);
- }
- 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) {
- ev.stopPropagation();
- }
- }
-});
-</script>
-
-<style lang="scss" scoped>
-.fdidabkb {
- display: flex;
-
- &.slim {
- text-align: center;
-
- > .titleContainer {
- margin: 0 auto;
- }
-
- > .buttons {
- &.right {
- margin-left: 0;
- }
- }
- }
-
- > .buttons {
- --margin: 8px;
- display: flex;
- align-items: center;
- height: var(--height);
- margin: 0 var(--margin);
-
- &.right {
- margin-left: auto;
- }
-
- &:empty {
- width: var(--height);
- }
-
- > .button {
- display: flex;
- align-items: center;
- justify-content: center;
- height: calc(var(--height) - (var(--margin) * 2));
- width: calc(var(--height) - (var(--margin) * 2));
- box-sizing: border-box;
- position: relative;
- border-radius: 5px;
-
- &:hover {
- background: rgba(0, 0, 0, 0.05);
- }
-
- &.highlighted {
- color: var(--accent);
- }
- }
- }
-
- > .titleContainer {
- display: flex;
- align-items: center;
- overflow: auto;
- white-space: nowrap;
- text-align: left;
- font-weight: bold;
-
- > .avatar {
- $size: 32px;
- display: inline-block;
- width: $size;
- height: $size;
- vertical-align: bottom;
- margin: 0 8px;
- pointer-events: none;
- }
-
- > .icon {
- margin-right: 8px;
- }
-
- > .title {
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- line-height: 1.1;
-
- > .subtitle {
- opacity: 0.6;
- font-size: 0.8em;
- font-weight: normal;
- white-space: nowrap;
- overflow: hidden;
- 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;
- }
- }
- }
-}
-</style>
diff --git a/src/client/ui/_common_/sidebar.vue b/src/client/ui/_common_/sidebar.vue
index 9817a46e30..d00327b096 100644
--- a/src/client/ui/_common_/sidebar.vue
+++ b/src/client/ui/_common_/sidebar.vue
@@ -50,7 +50,7 @@ import { host } from '@client/config';
import { search } from '@client/scripts/search';
import * as os from '@client/os';
import { menuDef } from '@client/menu';
-import { getAccounts, addAccount, login } from '@client/account';
+import { openAccountMenu } from '@client/account';
export default defineComponent({
props: {
@@ -134,76 +134,12 @@ export default defineComponent({
search();
},
- async openAccountMenu(ev) {
- const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id));
- const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
-
- const accountItemPromises = storedAccounts.map(a => new Promise(res => {
- accountsPromise.then(accounts => {
- const account = accounts.find(x => x.id === a.id);
- if (account == null) return res(null);
- res({
- type: 'user',
- user: account,
- action: () => { this.switchAccount(account); }
- });
- });
- }));
-
- os.popupMenu([...[{
- type: 'link',
- text: this.$ts.profile,
- to: `/@${ this.$i.username }`,
- avatar: this.$i,
- }, null, ...accountItemPromises, {
- icon: 'fas fa-plus',
- text: this.$ts.addAccount,
- action: () => {
- os.popupMenu([{
- text: this.$ts.existingAccount,
- action: () => { this.addAccount(); },
- }, {
- text: this.$ts.createAccount,
- action: () => { this.createAccount(); },
- }], ev.currentTarget || ev.target);
- },
- }]], ev.currentTarget || ev.target, {
- align: 'left'
- });
- },
-
more(ev) {
os.popup(import('@client/components/launch-pad.vue'), {}, {
}, 'closed');
},
- addAccount() {
- os.popup(import('@client/components/signin-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- os.success();
- },
- }, 'closed');
- },
-
- createAccount() {
- os.popup(import('@client/components/signup-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- this.switchAccountWithToken(res.i);
- },
- }, 'closed');
- },
-
- async switchAccount(account: any) {
- const storedAccounts = await getAccounts();
- const token = storedAccounts.find(x => x.id === account.id).token;
- this.switchAccountWithToken(token);
- },
-
- switchAccountWithToken(token: string) {
- login(token);
- },
+ openAccountMenu,
}
});
</script>
@@ -395,7 +331,7 @@ export default defineComponent({
left: 0;
right: 0;
bottom: 0;
- border-radius: 8px;
+ border-radius: 999px;
background: var(--accentedBg);
}
}
@@ -436,7 +372,7 @@ export default defineComponent({
right: 0;
bottom: 0;
border-radius: 999px;
- background: var(--accent);
+ background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
}
&:hover, &.active {
diff --git a/src/client/ui/chat/index.vue b/src/client/ui/chat/index.vue
index e8275def81..4c068b0d94 100644
--- a/src/client/ui/chat/index.vue
+++ b/src/client/ui/chat/index.vue
@@ -74,7 +74,7 @@
<main class="main" @contextmenu.stop="onContextmenu">
<header class="header">
- <XHeader class="header" :info="pageInfo" :menu="menu" :center="false" :back-button="true" @back="back()" @click="onHeaderClick"/>
+ <MkHeader class="header" :info="pageInfo" :menu="menu" :center="false" @click="onHeaderClick"/>
</header>
<router-view v-slot="{ Component }">
<transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition">
@@ -101,7 +101,6 @@ import XSidebar from '@client/ui/_common_/sidebar.vue';
import XWidgets from './widgets.vue';
import XCommon from '../_common_/common.vue';
import XSide from './side.vue';
-import XHeader from '../_common_/header.vue';
import XHeaderClock from './header-clock.vue';
import * as os from '@client/os';
import { router } from '@client/router';
@@ -110,6 +109,7 @@ import { search } from '@client/scripts/search';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import { store } from './store';
import * as symbols from '@client/symbols';
+import { openAccountMenu } from '@client/account';
export default defineComponent({
components: {
@@ -117,7 +117,6 @@ export default defineComponent({
XSidebar,
XWidgets,
XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる
- XHeader,
XHeaderClock,
},
@@ -255,6 +254,8 @@ export default defineComponent({
}
}], e);
},
+
+ openAccountMenu,
}
});
</script>
diff --git a/src/client/ui/chat/note-preview.vue b/src/client/ui/chat/note-preview.vue
index 77949e314b..beb38de644 100644
--- a/src/client/ui/chat/note-preview.vue
+++ b/src/client/ui/chat/note-preview.vue
@@ -6,7 +6,7 @@
<div class="body">
<p v-if="note.cw != null" class="cw">
<span class="text" v-if="note.cw != ''">{{ note.cw }}</span>
- <XCwButton v-model:value="showContent" :note="note"/>
+ <XCwButton v-model="showContent" :note="note"/>
</p>
<div class="content" v-show="note.cw == null || showContent">
<XSubNote-content class="text" :note="note"/>
diff --git a/src/client/ui/chat/note.sub.vue b/src/client/ui/chat/note.sub.vue
index bb528dd936..a284ba2bf4 100644
--- a/src/client/ui/chat/note.sub.vue
+++ b/src/client/ui/chat/note.sub.vue
@@ -7,7 +7,7 @@
<div class="body">
<p v-if="note.cw != null" class="cw">
<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" />
- <XCwButton v-model:value="showContent" :note="note"/>
+ <XCwButton v-model="showContent" :note="note"/>
</p>
<div class="content" v-show="note.cw == null || showContent">
<XSubNote-content class="text" :note="note"/>
diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue
index 6d2b9bbf54..0a054d1057 100644
--- a/src/client/ui/chat/note.vue
+++ b/src/client/ui/chat/note.vue
@@ -42,7 +42,7 @@
<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" :custom-emojis="appearNote.emojis"/>
- <XCwButton v-model:value="showContent" :note="appearNote"/>
+ <XCwButton v-model="showContent" :note="appearNote"/>
</p>
<div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent">
<div class="text">
@@ -56,7 +56,7 @@
</div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/>
- <div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
+ <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span>
</button>
@@ -106,7 +106,7 @@ import * as mfm from 'mfm-js';
import { sum } from '../../../prelude/array';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
-import XNotePreview from './note-preview.vue';
+import XNoteSimple from './note-preview.vue';
import XReactionsViewer from '@client/components/reactions-viewer.vue';
import XMediaList from '@client/components/media-list.vue';
import XCwButton from '@client/components/cw-button.vue';
@@ -126,7 +126,7 @@ export default defineComponent({
components: {
XSub,
XNoteHeader,
- XNotePreview,
+ XNoteSimple,
XReactionsViewer,
XMediaList,
XCwButton,
@@ -872,7 +872,7 @@ export default defineComponent({
//content-visibility: auto;
//contain-intrinsic-size: 0 128px;
- &:focus {
+ &:focus-visible {
outline: none;
}
diff --git a/src/client/ui/chat/post-form.vue b/src/client/ui/chat/post-form.vue
index 0f9a206fab..0cacaf77e7 100644
--- a/src/client/ui/chat/post-form.vue
+++ b/src/client/ui/chat/post-form.vue
@@ -681,7 +681,7 @@ export default defineComponent({
color: var(--fg);
font-family: inherit;
- &:focus {
+ &:focus-visible {
outline: none;
}
diff --git a/src/client/ui/chat/side.vue b/src/client/ui/chat/side.vue
index ebf1cf9979..3fd0a0e77b 100644
--- a/src/client/ui/chat/side.vue
+++ b/src/client/ui/chat/side.vue
@@ -1,15 +1,14 @@
<template>
<div class="mrajymqm _narrow_" v-if="component">
<header class="header" @contextmenu.prevent.stop="onContextmenu">
- <XHeader class="title" :info="pageInfo" :center="false" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()"/>
+ <MkHeader class="title" :info="pageInfo" :center="false"/>
</header>
- <component :is="component" v-bind="props" :ref="changePage" class="body _flat_"/>
+ <component :is="component" v-bind="props" :ref="changePage" class="body _fitSide_"/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import XHeader from '../_common_/header.vue';
import * as os from '@client/os';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import { resolve } from '@client/router';
@@ -18,7 +17,6 @@ import * as symbols from '@client/symbols';
export default defineComponent({
components: {
- XHeader
},
provide() {
diff --git a/src/client/ui/deck/column.vue b/src/client/ui/deck/column.vue
index 842a6ff59f..c04297e384 100644
--- a/src/client/ui/deck/column.vue
+++ b/src/client/ui/deck/column.vue
@@ -37,6 +37,11 @@ import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownCo
import { deckStore } from './deck-store';
export default defineComponent({
+ provide: {
+ shouldHeaderThin: true,
+ shouldOmitHeaderTitle: true,
+ },
+
props: {
column: {
type: Object,
@@ -267,6 +272,7 @@ export default defineComponent({
height: 100%;
overflow: hidden;
contain: content;
+ box-shadow: 0 0 8px 0 var(--shadow);
&.draghover {
box-shadow: 0 0 0 2px var(--focus);
@@ -320,15 +326,6 @@ export default defineComponent({
&.paged {
background: var(--bg) !important;
-
- > header {
- background: transparent;
- box-shadow: none;
-
- > button {
- color: var(--fg);
- }
- }
}
> header {
@@ -365,7 +362,7 @@ export default defineComponent({
}
> .toggleActive,
- > .action > *,
+ > .action > ::v-deep(*),
> .menu {
z-index: 1;
width: var(--deckColumnHeaderHeight);
diff --git a/src/client/ui/deck/deck-store.ts b/src/client/ui/deck/deck-store.ts
index aa389d7610..6c61bf5539 100644
--- a/src/client/ui/deck/deck-store.ts
+++ b/src/client/ui/deck/deck-store.ts
@@ -219,10 +219,20 @@ export function stackLeftColumn(id: Column['id']) {
export function popRightColumn(id: Column['id']) {
let layout = copy(deckStore.state.layout);
const i = deckStore.state.layout.findIndex(ids => ids.includes(id));
+ const affected = layout[i];
layout = layout.map(ids => ids.filter(_id => _id !== id));
layout.splice(i + 1, 0, [id]);
layout = layout.filter(ids => ids.length > 0);
deckStore.set('layout', layout);
+
+ const columns = copy(deckStore.state.columns);
+ for (const column of columns) {
+ if (affected.includes(column.id)) {
+ column.active = true;
+ }
+ }
+ deckStore.set('columns', columns);
+
saveDeck();
}
diff --git a/src/client/ui/deck/main-column.vue b/src/client/ui/deck/main-column.vue
index 4c591022a5..baf88a9721 100644
--- a/src/client/ui/deck/main-column.vue
+++ b/src/client/ui/deck/main-column.vue
@@ -1,10 +1,13 @@
<template>
<XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked">
<template #header>
- <XHeader :info="pageInfo" :back-button="true" @back="back()"/>
+ <template v-if="pageInfo">
+ <i :class="pageInfo.icon"></i>
+ {{ pageInfo.title }}
+ </template>
</template>
- <router-view v-slot="{ Component }" class="_flat_">
+ <router-view v-slot="{ Component }" class="_fitSide_">
<transition>
<keep-alive :include="['timeline']">
<component :is="Component" :ref="changePage" @contextmenu.stop="onContextmenu"/>
@@ -18,7 +21,6 @@
import { defineComponent } from 'vue';
import XColumn from './column.vue';
import XNotes from '@client/components/notes.vue';
-import XHeader from '@client/ui/_common_/header.vue';
import { deckStore } from '@client/ui/deck/deck-store';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
@@ -26,7 +28,6 @@ import * as symbols from '@client/symbols';
export default defineComponent({
components: {
XColumn,
- XHeader,
XNotes
},
diff --git a/src/client/ui/default.header.vue b/src/client/ui/default.header.vue
index 6fbdd625c7..4f6363e82d 100644
--- a/src/client/ui/default.header.vue
+++ b/src/client/ui/default.header.vue
@@ -29,7 +29,7 @@
<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
</button>
<div class="post" @click="post">
- <MkButton class="button" primary full>
+ <MkButton class="button" gradate full rounded>
<i class="fas fa-pencil-alt fa-fw"></i>
</MkButton>
</div>
@@ -44,7 +44,7 @@ import { host } from '@client/config';
import { search } from '@client/scripts/search';
import * as os from '@client/os';
import { menuDef } from '@client/menu';
-import { getAccounts, addAccount, login } from '@client/account';
+import { openAccountMenu } from '@client/account';
import MkButton from '@client/components/ui/button.vue';
export default defineComponent({
@@ -100,76 +100,12 @@ export default defineComponent({
search();
},
- async openAccountMenu(ev) {
- const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id));
- const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
-
- const accountItemPromises = storedAccounts.map(a => new Promise(res => {
- accountsPromise.then(accounts => {
- const account = accounts.find(x => x.id === a.id);
- if (account == null) return res(null);
- res({
- type: 'user',
- user: account,
- action: () => { this.switchAccount(account); }
- });
- });
- }));
-
- os.popupMenu([...[{
- type: 'link',
- text: this.$ts.profile,
- to: `/@${ this.$i.username }`,
- avatar: this.$i,
- }, null, ...accountItemPromises, {
- icon: 'fas fa-plus',
- text: this.$ts.addAccount,
- action: () => {
- os.popupMenu([{
- text: this.$ts.existingAccount,
- action: () => { this.addAccount(); },
- }, {
- text: this.$ts.createAccount,
- action: () => { this.createAccount(); },
- }], ev.currentTarget || ev.target);
- },
- }]], ev.currentTarget || ev.target, {
- align: 'left'
- });
- },
-
more(ev) {
os.popup(import('@client/components/launch-pad.vue'), {}, {
}, 'closed');
},
- addAccount() {
- os.popup(import('@client/components/signin-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- os.success();
- },
- }, 'closed');
- },
-
- createAccount() {
- os.popup(import('@client/components/signup-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- this.switchAccountWithToken(res.i);
- },
- }, 'closed');
- },
-
- async switchAccount(account: any) {
- const storedAccounts = await getAccounts();
- const token = storedAccounts.find(x => x.id === account.id).token;
- this.switchAccountWithToken(token);
- },
-
- switchAccountWithToken(token: string) {
- login(token);
- },
+ openAccountMenu,
}
});
</script>
diff --git a/src/client/ui/default.side.vue b/src/client/ui/default.side.vue
index 4d65779612..c7d2abff26 100644
--- a/src/client/ui/default.side.vue
+++ b/src/client/ui/default.side.vue
@@ -4,9 +4,10 @@
<header class="header" @contextmenu.prevent.stop="onContextmenu">
<button class="_button" @click="back()" v-if="history.length > 0"><i class="fas fa-chevron-left"></i></button>
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
- <XHeader class="title" :info="pageInfo" :back-button="false"/>
+ <span class="title">{{ pageInfo.title }}</span>
<button class="_button" @click="close()"><i class="fas fa-times"></i></button>
</header>
+ <MkHeader class="pageHeader" :info="pageInfo"/>
<component :is="component" v-bind="props" :ref="changePage"/>
</div>
</div>
@@ -14,7 +15,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import XHeader from './_common_/header.vue';
import * as os from '@client/os';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import { resolve } from '@client/router';
@@ -22,10 +22,6 @@ import { url } from '@client/config';
import * as symbols from '@client/symbols';
export default defineComponent({
- components: {
- XHeader
- },
-
provide() {
return {
navHook: (path) => {
diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue
index be907aa2a4..e36febb7fa 100644
--- a/src/client/ui/default.sidebar.vue
+++ b/src/client/ui/default.sidebar.vue
@@ -4,7 +4,7 @@
<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
</button>
<div class="post" @click="post" data-cy-open-post-form>
- <MkButton class="button" primary full>
+ <MkButton class="button" gradate full rounded>
<i class="fas fa-pencil-alt fa-fw"></i><span class="text" v-if="!iconOnly">{{ $ts.note }}</span>
</MkButton>
</div>
@@ -46,7 +46,7 @@ import { host } from '@client/config';
import { search } from '@client/scripts/search';
import * as os from '@client/os';
import { menuDef } from '@client/menu';
-import { getAccounts, addAccount, login } from '@client/account';
+import { openAccountMenu } from '@client/account';
import MkButton from '@client/components/ui/button.vue';
import { StickySidebar } from '@client/scripts/sticky-sidebar';
import MisskeyLogo from '@/../assets/client/misskey.svg';
@@ -120,76 +120,12 @@ export default defineComponent({
search();
},
- async openAccountMenu(ev) {
- const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id));
- const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
-
- const accountItemPromises = storedAccounts.map(a => new Promise(res => {
- accountsPromise.then(accounts => {
- const account = accounts.find(x => x.id === a.id);
- if (account == null) return res(null);
- res({
- type: 'user',
- user: account,
- action: () => { this.switchAccount(account); }
- });
- });
- }));
-
- os.popupMenu([...[{
- type: 'link',
- text: this.$ts.profile,
- to: `/@${ this.$i.username }`,
- avatar: this.$i,
- }, null, ...accountItemPromises, {
- icon: 'fas fa-plus',
- text: this.$ts.addAccount,
- action: () => {
- os.popupMenu([{
- text: this.$ts.existingAccount,
- action: () => { this.addAccount(); },
- }, {
- text: this.$ts.createAccount,
- action: () => { this.createAccount(); },
- }], ev.currentTarget || ev.target);
- },
- }]], ev.currentTarget || ev.target, {
- align: 'left'
- });
- },
-
more(ev) {
os.popup(import('@client/components/launch-pad.vue'), {}, {
}, 'closed');
},
- addAccount() {
- os.popup(import('@client/components/signin-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- os.success();
- },
- }, 'closed');
- },
-
- createAccount() {
- os.popup(import('@client/components/signup-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- this.switchAccountWithToken(res.i);
- },
- }, 'closed');
- },
-
- async switchAccount(account: any) {
- const storedAccounts = await getAccounts();
- const token = storedAccounts.find(x => x.id === account.id).token;
- this.switchAccountWithToken(token);
- },
-
- switchAccountWithToken(token: string) {
- login(token);
- },
+ openAccountMenu,
}
});
</script>
diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue
index a5ec243e9e..3518b1a91a 100644
--- a/src/client/ui/default.vue
+++ b/src/client/ui/default.vue
@@ -1,6 +1,6 @@
<template>
-<div class="mk-app" :class="{ wallpaper, isMobile }">
- <XHeaderMenu v-if="showMenuOnTop"/>
+<div class="mk-app" :class="{ wallpaper, isMobile }" :style="`--globalHeaderHeight:${globalHeaderHeight}px`">
+ <XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/>
<div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }">
<template v-if="!isMobile">
@@ -13,10 +13,7 @@
</template>
<main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }">
- <header class="header" @click="onHeaderClick">
- <XHeader :info="pageInfo" :back-button="true" @back="back()"/>
- </header>
- <div class="content" :class="{ _flat_: !fullView }">
+ <div class="content" :class="{ _fitSide_: !fullView }">
<router-view v-slot="{ Component }">
<transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition">
<keep-alive :include="['timeline']">
@@ -67,7 +64,6 @@ import { StickySidebar } from '@client/scripts/sticky-sidebar';
import XSidebar from './default.sidebar.vue';
import XDrawerSidebar from '@client/ui/_common_/sidebar.vue';
import XCommon from './_common_/common.vue';
-import XHeader from './_common_/header.vue';
import * as os from '@client/os';
import { menuDef } from '@client/menu';
import * as symbols from '@client/symbols';
@@ -80,15 +76,21 @@ export default defineComponent({
XCommon,
XSidebar,
XDrawerSidebar,
- XHeader,
XHeaderMenu: defineAsyncComponent(() => import('./default.header.vue')),
XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')),
},
+ provide() {
+ return {
+ shouldHeaderThin: this.showMenuOnTop,
+ };
+ },
+
data() {
return {
pageInfo: null,
menuDef: menuDef,
+ globalHeaderHeight: 0,
isMobile: window.innerWidth <= MOBILE_THRESHOLD,
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
widgetsShowing: false,
@@ -193,10 +195,6 @@ export default defineComponent({
if (window._scroll) window._scroll();
},
- onHeaderClick() {
- window.scroll({ top: 0, behavior: 'smooth' });
- },
-
onContextmenu(e) {
const isLink = (el: HTMLElement) => {
if (el.tagName === 'A') return true;
@@ -257,7 +255,6 @@ export default defineComponent({
}
.mk-app {
- $header-height: 50px;
$ui-font-size: 1em;
$widgets-hide-threshold: 1200px;
$nav-icon-only-width: 78px; // TODO: どこかに集約したい
@@ -282,10 +279,6 @@ export default defineComponent({
border: none;
width: 100%;
border-radius: 0;
-
- > .header {
- width: 100%;
- }
}
}
}
@@ -325,30 +318,6 @@ export default defineComponent({
border-radius: 0;
overflow: clip;
--margin: 12px;
-
- > .header {
- position: sticky;
- z-index: 1000;
- top: var(--globalHeaderHeight, 0px);
- height: $header-height;
- -webkit-backdrop-filter: var(--blur, blur(32px));
- backdrop-filter: var(--blur, blur(32px));
- background-color: var(--header);
- border-bottom: solid 0.5px var(--divider);
- }
-
- > .content {
- --stickyTop: calc(var(--globalHeaderHeight, 0px) + #{$header-height});
- }
-
- @media (max-width: 850px) {
- padding-top: $header-height;
-
- > .header {
- position: fixed;
- width: calc(100% - #{$nav-icon-only-width});
- }
- }
}
> .widgets {
@@ -370,12 +339,11 @@ export default defineComponent({
}
&.withGlobalHeader {
- --globalHeaderHeight: 60px; // TODO: 60pxと決め打ちしているのを直す
-
> .main {
margin-top: 0;
border: solid 1px var(--divider);
border-radius: var(--radius);
+ --stickyTop: var(--globalHeaderHeight);
}
> .widgets {
diff --git a/src/client/ui/universal.vue b/src/client/ui/universal.vue
index ec9254b697..7c25d71bb3 100644
--- a/src/client/ui/universal.vue
+++ b/src/client/ui/universal.vue
@@ -3,9 +3,6 @@
<XSidebar ref="nav" class="sidebar"/>
<div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }">
- <header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }">
- <XHeader :info="pageInfo" :back-button="true" @back="back()"/>
- </header>
<main ref="main">
<div class="content">
<router-view v-slot="{ Component }">
@@ -58,7 +55,6 @@ import { instanceName } from '@client/config';
import { StickySidebar } from '@client/scripts/sticky-sidebar';
import XSidebar from '@client/ui/_common_/sidebar.vue';
import XCommon from './_common_/common.vue';
-import XHeader from './_common_/header.vue';
import XSide from './default.side.vue';
import * as os from '@client/os';
import { menuDef } from '@client/menu';
@@ -70,7 +66,6 @@ export default defineComponent({
components: {
XCommon,
XSidebar,
- XHeader,
XWidgets: defineAsyncComponent(() => import('./universal.widgets.vue')),
XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる
},
@@ -151,9 +146,6 @@ export default defineComponent({
adjustUI() {
const navWidth = this.$refs.nav.$el.offsetWidth;
this.navHidden = navWidth === 0;
- if (this.$refs.contents == null) return;
- const width = this.$refs.contents.offsetWidth;
- if (this.$refs.header) this.$refs.header.style.width = `${width}px`;
},
showNav() {
@@ -183,10 +175,6 @@ export default defineComponent({
if (window._scroll) window._scroll();
},
- onHeaderClick() {
- window.scroll({ top: 0, behavior: 'smooth' });
- },
-
onContextmenu(e) {
const isLink = (el: HTMLElement) => {
if (el.tagName === 'A') return true;
@@ -243,7 +231,6 @@ export default defineComponent({
}
.mk-app {
- $header-height: 58px; // TODO: どこかに集約したい
$ui-font-size: 1em; // TODO: どこかに集約したい
$widgets-hide-threshold: 1090px;
@@ -263,37 +250,11 @@ export default defineComponent({
> .contents {
width: 100%;
min-width: 0;
- --stickyTop: #{$header-height};
- padding-top: $header-height;
background: var(--panel);
- > .header {
- position: fixed;
- z-index: 1000;
- top: 0;
- height: $header-height;
- width: 100%;
- line-height: $header-height;
- text-align: center;
- font-weight: bold;
- //background-color: var(--panel);
- -webkit-backdrop-filter: var(--blur, blur(32px));
- backdrop-filter: var(--blur, blur(32px));
- background-color: var(--header);
- border-bottom: solid 0.5px var(--divider);
- user-select: none;
- }
-
> main {
min-width: 0;
- > .content {
- > * {
- // ほんとは単に calc(100vh - #{$header-height}) と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
- min-height: calc((var(--vh, 1vh) * 100) - #{$header-height});
- }
- }
-
> .spacer {
height: 82px;
diff --git a/src/client/ui/zen.vue b/src/client/ui/zen.vue
index 3756ddb5c3..98e2b8dac6 100644
--- a/src/client/ui/zen.vue
+++ b/src/client/ui/zen.vue
@@ -2,7 +2,7 @@
<div class="mk-app">
<div class="contents">
<header class="header">
- <XHeader :info="pageInfo"/>
+ <MkHeader :info="pageInfo"/>
</header>
<main ref="main">
<div class="content">
@@ -24,14 +24,12 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';
import { host } from '@client/config';
-import XHeader from './_common_/header.vue';
import XCommon from './_common_/common.vue';
import * as symbols from '@client/symbols';
export default defineComponent({
components: {
XCommon,
- XHeader,
},
data() {
diff --git a/src/client/widgets/aiscript.vue b/src/client/widgets/aiscript.vue
index 2ea6d09ff5..aaf0a0372e 100644
--- a/src/client/widgets/aiscript.vue
+++ b/src/client/widgets/aiscript.vue
@@ -125,7 +125,7 @@ export default defineComponent({
box-sizing: border-box;
font: inherit;
- &:focus {
+ &:focus-visible {
outline: none;
}
}
diff --git a/src/client/widgets/memo.vue b/src/client/widgets/memo.vue
index 13ab628f24..3f11e6409e 100644
--- a/src/client/widgets/memo.vue
+++ b/src/client/widgets/memo.vue
@@ -81,7 +81,7 @@ export default defineComponent({
font: inherit;
font-size: 0.9em;
- &:focus {
+ &:focus-visible {
outline: none;
}
}
diff --git a/src/client/widgets/notifications.vue b/src/client/widgets/notifications.vue
index 01c76850d8..b0245eed6a 100644
--- a/src/client/widgets/notifications.vue
+++ b/src/client/widgets/notifications.vue
@@ -3,7 +3,7 @@
<template #header><i class="fas fa-bell"></i>{{ $ts.notifications }}</template>
<template #func><button @click="configure()" class="_button"><i class="fas fa-cog"></i></button></template>
- <div class="_flat_">
+ <div class="_fitSide_">
<XNotifications :include-types="props.includingTypes"/>
</div>
</MkContainer>