diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 62aade568dab58a49b259522e9175f82a62784a5..74964e366ab04b366a4c8839c94a9f0eaebc61da 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -743,7 +743,7 @@ online: "オンライン" active: "アクティブ" offline: "オフライン" notRecommended: "éžæŽ¨å¥¨" -botProtection: "Bot防御" +botProtection: "Botプãƒãƒ†ã‚¯ã‚·ãƒ§ãƒ³" instanceBlocking: "インスタンスブãƒãƒƒã‚¯" selectAccount: "アカウントをé¸æŠž" enabled: "有効" @@ -754,7 +754,7 @@ administration: "管ç†" accounts: "アカウント" switch: "切り替ãˆ" noMaintainerInformationWarning: "管ç†è€…æƒ…å ±ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。" -noBotProtectionWarning: "Bot防御ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。" +noBotProtectionWarning: "Botプãƒãƒ†ã‚¯ã‚·ãƒ§ãƒ³ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。" configure: "è¨å®šã™ã‚‹" postToGallery: "ギャラリーã¸æŠ•ç¨¿" gallery: "ギャラリー" diff --git a/packages/client/@types/vue.d.ts b/packages/client/@types/vue.d.ts index 8cb613062990e28bd0ec5a962f86c618000abf69..f6b66228f6012dacde50a6b4b1af1fe4a818537c 100644 --- a/packages/client/@types/vue.d.ts +++ b/packages/client/@types/vue.d.ts @@ -1,3 +1,5 @@ +/// <reference types="vue/macros-global" /> + declare module '*.vue' { import type { DefineComponent } from 'vue'; const component: DefineComponent<{}, {}, any>; diff --git a/packages/client/package.json b/packages/client/package.json index 198bcbcef6f426496313e007efd2975eaeccff6c..fe149890dd69c019b8cbde8d3cf4846d0e308ac2 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -116,7 +116,7 @@ "v-debounce": "0.1.2", "vanilla-tilt": "1.7.2", "vue": "3.2.26", - "vue-loader": "16.8.3", + "vue-loader": "17.0.0", "vue-prism-editor": "2.0.0-alpha.2", "vue-router": "4.0.5", "vue-style-loader": "4.1.3", diff --git a/packages/client/src/components/form/section.vue b/packages/client/src/components/form/section.vue index bc2ab966b8219f9a97e747547c142da1f4a76228..ab9fbe5fcd43309195339f9badf708482000d8f2 100644 --- a/packages/client/src/components/form/section.vue +++ b/packages/client/src/components/form/section.vue @@ -7,12 +7,7 @@ </div> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; - -export default defineComponent({ - -}); +<script lang="ts" setup> </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/form/split.vue b/packages/client/src/components/form/split.vue new file mode 100644 index 0000000000000000000000000000000000000000..676b293967e2d8137d9cda767984e029a758e789 --- /dev/null +++ b/packages/client/src/components/form/split.vue @@ -0,0 +1,27 @@ +<template> +<div class="terlnhxf _formBlock"> + <slot></slot> +</div> +</template> + +<script lang="ts" setup> +const props = withDefaults(defineProps<{ + minWidth: number; +}>(), { + minWidth: 210, +}); + +const minWidth = props.minWidth + 'px'; +</script> + +<style lang="scss" scoped> +.terlnhxf { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(v-bind('minWidth'), 1fr)); + grid-gap: 12px; + + > ::v-deep(*) { + margin: 0 !important; + } +} +</style> diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue index aa9b09215ee2f5c885f1e6fcd1b0693d24dd1333..ac3284e7dae318e9de49f01a4b4df0549701c8c7 100644 --- a/packages/client/src/components/form/switch.vue +++ b/packages/client/src/components/form/switch.vue @@ -13,7 +13,8 @@ <i class="check fas fa-check"></i> </span> <span class="label"> - <span @click="toggle"><slot></slot></span> + <!-- TODO: ç„¡åslotã®æ–¹ã¯å»ƒæ¢ --> + <span @click="toggle"><slot name="label"></slot><slot></slot></span> <p class="caption"><slot name="caption"></slot></p> </span> </div> diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue index ba2975478bfdcbddb9ac58a4b423f6a01ce32166..dbef34d547bc04313069f4480dc34778f2a094c1 100644 --- a/packages/client/src/components/user-select-dialog.vue +++ b/packages/client/src/components/user-select-dialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow ref="dialog" +<XModalWindow ref="dialogEl" :with-ok-button="true" :ok-button-disabled="selected == null" @click="cancel()" @@ -8,20 +8,20 @@ @closed="$emit('closed')" > <template #header>{{ $ts.selectUser }}</template> - <div class="tbhwbxda _monolithic_"> - <div class="_section"> - <div class="_inputSplit"> - <MkInput ref="username" v-model="username" class="input" @update:modelValue="search"> + <div class="tbhwbxda"> + <div class="form"> + <FormSplit :min-width="170"> + <MkInput ref="usernameEl" v-model="username" @update:modelValue="search"> <template #label>{{ $ts.username }}</template> <template #prefix>@</template> </MkInput> - <MkInput v-model="host" class="input" @update:modelValue="search"> + <MkInput v-model="host" @update:modelValue="search"> <template #label>{{ $ts.host }}</template> <template #prefix>@</template> </MkInput> - </div> + </FormSplit> </div> - <div v-if="username != '' || host != ''" class="_section result" :class="{ hit: users.length > 0 }"> + <div v-if="username != '' || host != ''" class="result" :class="{ hit: users.length > 0 }"> <div v-if="users.length > 0" class="users"> <div v-for="user in users" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()"> <MkAvatar :user="user" class="avatar" :show-indicator="true"/> @@ -35,7 +35,7 @@ <span>{{ $ts.noUsers }}</span> </div> </div> - <div v-if="username == '' && host == ''" class="_section recent"> + <div v-if="username == '' && host == ''" class="recent"> <div class="users"> <div v-for="user in recentUsers" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()"> <MkAvatar :user="user" class="avatar" :show-indicator="true"/> @@ -50,87 +50,89 @@ </XModalWindow> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; -import MkInput from './form/input.vue'; +<script lang="ts" setup> +import { nextTick, onMounted } from 'vue'; +import * as misskey from 'misskey-js'; +import MkInput from '@/components/form/input.vue'; +import FormSplit from '@/components/form/split.vue'; import XModalWindow from '@/components/ui/modal-window.vue'; import * as os from '@/os'; +import { defaultStore } from '@/store'; + +const emit = defineEmits<{ + (e: 'ok', selected: misskey.entities.UserDetailed): void; + (e: 'cancel'): void; + (e: 'closed'): void; +}>(); + +let username = $ref(''); +let host = $ref(''); +let users: misskey.entities.UserDetailed[] = $ref([]); +let recentUsers: misskey.entities.UserDetailed[] = $ref([]); +let selected: misskey.entities.UserDetailed | null = $ref(null); +let usernameEl: HTMLElement = $ref(); +let dialogEl = $ref(); + +const focus = () => { + if (usernameEl) { + usernameEl.focus(); + } +}; -export default defineComponent({ - components: { - MkInput, - XModalWindow, - }, - - props: { - }, - - emits: ['ok', 'cancel', 'closed'], - - data() { - return { - username: '', - host: '', - recentUsers: [], - users: [], - selected: null, - }; - }, - - async mounted() { - this.focus(); - - this.$nextTick(() => { - this.focus(); - }); - - this.recentUsers = await os.api('users/show', { - userIds: this.$store.state.recentlyUsedUsers - }); - }, - - methods: { - search() { - if (this.username == '' && this.host == '') { - this.users = []; - return; - } - os.api('users/search-by-username-and-host', { - username: this.username, - host: this.host, - limit: 10, - detail: false - }).then(users => { - this.users = users; - }); - }, - - focus() { - this.$refs.username.focus(); - }, - - ok() { - this.$emit('ok', this.selected); - this.$refs.dialog.close(); - - // 最近使ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼æ›´æ–° - let recents = this.$store.state.recentlyUsedUsers; - recents = recents.filter(x => x !== this.selected.id); - recents.unshift(this.selected.id); - this.$store.set('recentlyUsedUsers', recents.splice(0, 16)); - }, - - cancel() { - this.$emit('cancel'); - this.$refs.dialog.close(); - }, +const search = () => { + if (username === '' && host === '') { + users = []; + return; } + os.api('users/search-by-username-and-host', { + username: username, + host: host, + limit: 10, + detail: false + }).then(_users => { + users = _users; + }); +}; + +const ok = () => { + if (selected == null) return; + emit('ok', selected); + dialogEl.close(); + + // 最近使ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼æ›´æ–° + let recents = defaultStore.state.recentlyUsedUsers; + recents = recents.filter(x => x !== selected.id); + recents.unshift(selected.id); + defaultStore.set('recentlyUsedUsers', recents.splice(0, 16)); +}; + +const cancel = () => { + emit('cancel'); + dialogEl.close(); +}; + +onMounted(() => { + focus(); + + nextTick(() => { + focus(); + }); + + os.api('users/show', { + userIds: defaultStore.state.recentlyUsedUsers, + }).then(users => { + recentUsers = users; + }); }); </script> <style lang="scss" scoped> .tbhwbxda { - > ._section { + > .form { + padding: 0 var(--root-margin); + } + + > .result, > .recent { display: flex; flex-direction: column; overflow: auto; diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue index 04f68b720177ebff0ac9890826f29d983d804198..618e569839b44882f4e6780abaa17ca3cd70b78b 100644 --- a/packages/client/src/pages/about.vue +++ b/packages/client/src/pages/about.vue @@ -24,7 +24,7 @@ </FormSection> <FormSection> - <div class="_inputSplit _formBlock"> + <FormSplit> <MkKeyValue class="_formBlock"> <template #key>{{ $ts.administrator }}</template> <template #value>{{ $instance.maintainerName }}</template> @@ -33,14 +33,14 @@ <template #key>{{ $ts.contact }}</template> <template #value>{{ $instance.maintainerEmail }}</template> </MkKeyValue> - </div> + </FormSplit> <FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ $ts.tos }}</FormLink> </FormSection> <FormSuspense :p="initStats"> <FormSection> <template #label>{{ $ts.statistics }}</template> - <div class="_inputSplit"> + <FormSplit> <MkKeyValue class="_formBlock"> <template #key>{{ $ts.users }}</template> <template #value>{{ number(stats.originalUsersCount) }}</template> @@ -49,7 +49,7 @@ <template #key>{{ $ts.notes }}</template> <template #value>{{ number(stats.originalNotesCount) }}</template> </MkKeyValue> - </div> + </FormSplit> </FormSection> </FormSuspense> @@ -73,6 +73,7 @@ import { version, instanceName } from '@/config'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import FormSuspense from '@/components/form/suspense.vue'; +import FormSplit from '@/components/form/split.vue'; import MkKeyValue from '@/components/key-value.vue'; import * as os from '@/os'; import number from '@/filters/number'; @@ -85,6 +86,7 @@ export default defineComponent({ FormSection, FormLink, FormSuspense, + FormSplit, }, data() { diff --git a/packages/client/src/pages/admin/ads.vue b/packages/client/src/pages/admin/ads.vue index d12ed8563edc12e40df77c7c4b2873062b1ea740..0396dae10c63b59244ef8754e531f1a408eb3fba 100644 --- a/packages/client/src/pages/admin/ads.vue +++ b/packages/client/src/pages/admin/ads.vue @@ -23,14 +23,14 @@ <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio> </div> --> - <div class="_inputSplit"> + <FormSplit> <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> - </div> + </FormSplit> <MkTextarea v-model="ad.memo" class="_formBlock"> <template #label>{{ $ts.memo }}</template> </MkTextarea> @@ -49,6 +49,7 @@ import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkTextarea from '@/components/form/textarea.vue'; import FormRadios from '@/components/form/radios.vue'; +import FormSplit from '@/components/form/split.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; @@ -58,6 +59,7 @@ export default defineComponent({ MkInput, MkTextarea, FormRadios, + FormSplit, }, emits: ['info'], diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue index 873a853918037fcb853075b5d0d495fb46819edb..0799755a4d362ad2858c8548ddd89825446dc9ba 100644 --- a/packages/client/src/pages/admin/email-settings.vue +++ b/packages/client/src/pages/admin/email-settings.vue @@ -1,50 +1,55 @@ <template> -<FormBase> +<MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <FormSwitch v-model="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch> + <div class="_formRoot"> + <FormSwitch v-model="enableEmail" class="_formBlock"> + <template #label>{{ $ts.enableEmail }}</template> + <template #caption>{{ $ts.emailConfigInfo }}</template> + </FormSwitch> - <template v-if="enableEmail"> - <FormInput v-model="email" type="email"> - <span>{{ $ts.emailAddress }}</span> - </FormInput> + <template v-if="enableEmail"> + <FormInput v-model="email" type="email" class="_formBlock"> + <template #label>{{ $ts.emailAddress }}</template> + </FormInput> - <div v-sticky-container class="_debobigegoItem _debobigegoNoConcat"> - <div class="_debobigegoLabel">{{ $ts.smtpConfig }}</div> - <div class="main"> - <FormInput v-model="smtpHost"> - <span>{{ $ts.smtpHost }}</span> - </FormInput> - <FormInput v-model="smtpPort" type="number"> - <span>{{ $ts.smtpPort }}</span> - </FormInput> - <FormInput v-model="smtpUser"> - <span>{{ $ts.smtpUser }}</span> - </FormInput> - <FormInput v-model="smtpPass" type="password"> - <span>{{ $ts.smtpPass }}</span> - </FormInput> - <FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo> - <FormSwitch v-model="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch> - </div> - </div> - - <FormButton @click="testEmail">{{ $ts.testEmail }}</FormButton> - </template> - - <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormSection> + <template #label>{{ $ts.smtpConfig }}</template> + <FormSplit :min-width="280"> + <FormInput v-model="smtpHost" class="_formBlock"> + <template #label>{{ $ts.smtpHost }}</template> + </FormInput> + <FormInput v-model="smtpPort" type="number" class="_formBlock"> + <template #label>{{ $ts.smtpPort }}</template> + </FormInput> + </FormSplit> + <FormSplit :min-width="280"> + <FormInput v-model="smtpUser" class="_formBlock"> + <template #label>{{ $ts.smtpUser }}</template> + </FormInput> + <FormInput v-model="smtpPass" type="password" class="_formBlock"> + <template #label>{{ $ts.smtpPass }}</template> + </FormInput> + </FormSplit> + <FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo> + <FormSwitch v-model="smtpSecure" class="_formBlock"> + <template #label>{{ $ts.smtpSecure }}</template> + <template #caption>{{ $ts.smtpSecureInfo }}</template> + </FormSwitch> + </FormSection> + </template> + </div> </FormSuspense> -</FormBase> +</MkSpacer> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@/components/debobigego/switch.vue'; -import FormInput from '@/components/debobigego/input.vue'; -import FormButton from '@/components/debobigego/button.vue'; -import FormBase from '@/components/debobigego/base.vue'; -import FormGroup from '@/components/debobigego/group.vue'; -import FormInfo from '@/components/debobigego/info.vue'; -import FormSuspense from '@/components/debobigego/suspense.vue'; +import FormSwitch from '@/components/form/switch.vue'; +import FormInput from '@/components/form/input.vue'; +import FormInfo from '@/components/ui/info.vue'; +import FormSuspense from '@/components/form/suspense.vue'; +import FormSplit from '@/components/form/split.vue'; +import FormSection from '@/components/form/section.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; @@ -53,9 +58,8 @@ export default defineComponent({ components: { FormSwitch, FormInput, - FormBase, - FormGroup, - FormButton, + FormSplit, + FormSection, FormInfo, FormSuspense, }, @@ -68,6 +72,16 @@ export default defineComponent({ title: this.$ts.emailServer, icon: 'fas fa-envelope', bg: 'var(--bg)', + actions: [{ + asFullButton: true, + text: this.$ts.testEmail, + handler: this.testEmail, + }, { + asFullButton: true, + icon: 'fas fa-check', + text: this.$ts.save, + handler: this.save, + }], }, enableEmail: false, email: null, diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue index 49277325a0ab55c2c83229b1638f5a74aee2d424..df5d234d6fdf71195a5447d66ec079e79177ec44 100644 --- a/packages/client/src/pages/admin/emojis.vue +++ b/packages/client/src/pages/admin/emojis.vue @@ -23,7 +23,7 @@ </div> <div v-else-if="tab === 'remote'" class="remote"> - <div class="_inputSplit"> + <FormSplit> <MkInput v-model="queryRemote" :debounce="true" type="search"> <template #prefix><i class="fas fa-search"></i></template> <template #label>{{ $ts.search }}</template> @@ -31,7 +31,7 @@ <MkInput v-model="host" :debounce="true"> <template #label>{{ $ts.host }}</template> </MkInput> - </div> + </FormSplit> <MkPagination ref="remoteEmojis" :pagination="remotePagination"> <template #empty><span>{{ $ts.noCustomEmojis }}</span></template> <template v-slot="{items}"> @@ -57,6 +57,7 @@ import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkPagination from '@/components/ui/pagination.vue'; import MkTab from '@/components/tab.vue'; +import FormSplit from '@/components/form/split.vue'; import { selectFiles } from '@/scripts/select-file'; import * as os from '@/os'; import * as symbols from '@/symbols'; @@ -67,6 +68,7 @@ export default defineComponent({ MkButton, MkInput, MkPagination, + FormSplit, }, emits: ['info'], diff --git a/packages/client/src/pages/admin/files-settings.vue b/packages/client/src/pages/admin/files-settings.vue index df25bd0fb24160fdc6a41acdd0604f591af44e1a..2ac81843f076cb7d18b82ec334c58fb7302109b7 100644 --- a/packages/client/src/pages/admin/files-settings.vue +++ b/packages/client/src/pages/admin/files-settings.vue @@ -1,41 +1,41 @@ <template> -<FormBase> +<MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <FormSwitch v-model="cacheRemoteFiles"> - {{ $ts.cacheRemoteFiles }} - <template #desc>{{ $ts.cacheRemoteFilesDescription }}</template> - </FormSwitch> + <div class="_formRoot"> + <FormSwitch v-model="cacheRemoteFiles" class="_formBlock"> + <template #label>{{ $ts.cacheRemoteFiles }}</template> + <template #caption>{{ $ts.cacheRemoteFilesDescription }}</template> + </FormSwitch> - <FormSwitch v-model="proxyRemoteFiles"> - {{ $ts.proxyRemoteFiles }} - <template #desc>{{ $ts.proxyRemoteFilesDescription }}</template> - </FormSwitch> + <FormSwitch v-model="proxyRemoteFiles" class="_formBlock"> + <template #label>{{ $ts.proxyRemoteFiles }}</template> + <template #caption>{{ $ts.proxyRemoteFilesDescription }}</template> + </FormSwitch> - <FormInput v-model="localDriveCapacityMb" type="number"> - <span>{{ $ts.driveCapacityPerLocalAccount }}</span> - <template #suffix>MB</template> - <template #desc>{{ $ts.inMb }}</template> - </FormInput> + <FormSplit :min-width="280"> + <FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock"> + <template #label>{{ $ts.driveCapacityPerLocalAccount }}</template> + <template #suffix>MB</template> + <template #caption>{{ $ts.inMb }}</template> + </FormInput> - <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles"> - <span>{{ $ts.driveCapacityPerRemoteAccount }}</span> - <template #suffix>MB</template> - <template #desc>{{ $ts.inMb }}</template> - </FormInput> - - <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock"> + <template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template> + <template #suffix>MB</template> + <template #caption>{{ $ts.inMb }}</template> + </FormInput> + </FormSplit> + </div> </FormSuspense> -</FormBase> +</MkSpacer> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@/components/debobigego/switch.vue'; -import FormInput from '@/components/debobigego/input.vue'; -import FormButton from '@/components/debobigego/button.vue'; -import FormBase from '@/components/debobigego/base.vue'; -import FormGroup from '@/components/debobigego/group.vue'; -import FormSuspense from '@/components/debobigego/suspense.vue'; +import FormSwitch from '@/components/form/switch.vue'; +import FormInput from '@/components/form/input.vue'; +import FormSuspense from '@/components/form/suspense.vue'; +import FormSplit from '@/components/form/split.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; @@ -44,9 +44,7 @@ export default defineComponent({ components: { FormSwitch, FormInput, - FormBase, - FormGroup, - FormButton, + FormSplit, FormSuspense, }, @@ -58,6 +56,12 @@ export default defineComponent({ title: this.$ts.files, icon: 'fas fa-cloud', bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: this.$ts.save, + handler: this.save, + }], }, cacheRemoteFiles: false, proxyRemoteFiles: false, diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue index e363d1bd0354f7118e827b442cf3c0b5b9d5604e..e66a2f6c01dbfac65e2c28580dd8c84228d1fa0d 100644 --- a/packages/client/src/pages/admin/index.vue +++ b/packages/client/src/pages/admin/index.vue @@ -3,7 +3,7 @@ <div v-if="!narrow || page == null" class="nav"> <MkHeader :info="header"></MkHeader> - <MkSpacer :content-max="700"> + <MkSpacer :content-max="700" :margin-min="16"> <div class="lxpfedzu"> <div class="banner"> <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue index 8984686b5edbab7a3888f7756f483c561fb09fbb..0f74865b10f481239f76746a8d7d5610a339d247 100644 --- a/packages/client/src/pages/admin/object-storage.vue +++ b/packages/client/src/pages/admin/object-storage.vue @@ -1,76 +1,78 @@ <template> -<FormBase> +<MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <FormSwitch v-model="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch> - - <template v-if="useObjectStorage"> - <FormInput v-model="objectStorageBaseUrl"> - <span>{{ $ts.objectStorageBaseUrl }}</span> - <template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template> - </FormInput> - - <FormInput v-model="objectStorageBucket"> - <span>{{ $ts.objectStorageBucket }}</span> - <template #desc>{{ $ts.objectStorageBucketDesc }}</template> - </FormInput> - - <FormInput v-model="objectStoragePrefix"> - <span>{{ $ts.objectStoragePrefix }}</span> - <template #desc>{{ $ts.objectStoragePrefixDesc }}</template> - </FormInput> - - <FormInput v-model="objectStorageEndpoint"> - <span>{{ $ts.objectStorageEndpoint }}</span> - <template #desc>{{ $ts.objectStorageEndpointDesc }}</template> - </FormInput> - - <FormInput v-model="objectStorageRegion"> - <span>{{ $ts.objectStorageRegion }}</span> - <template #desc>{{ $ts.objectStorageRegionDesc }}</template> - </FormInput> - - <FormInput v-model="objectStorageAccessKey"> - <template #prefix><i class="fas fa-key"></i></template> - <span>Access key</span> - </FormInput> - - <FormInput v-model="objectStorageSecretKey"> - <template #prefix><i class="fas fa-key"></i></template> - <span>Secret key</span> - </FormInput> - - <FormSwitch v-model="objectStorageUseSSL"> - {{ $ts.objectStorageUseSSL }} - <template #desc>{{ $ts.objectStorageUseSSLDesc }}</template> - </FormSwitch> - - <FormSwitch v-model="objectStorageUseProxy"> - {{ $ts.objectStorageUseProxy }} - <template #desc>{{ $ts.objectStorageUseProxyDesc }}</template> - </FormSwitch> - - <FormSwitch v-model="objectStorageSetPublicRead"> - {{ $ts.objectStorageSetPublicRead }} - </FormSwitch> - - <FormSwitch v-model="objectStorageS3ForcePathStyle"> - s3ForcePathStyle - </FormSwitch> - </template> - - <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <div class="_formRoot"> + <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch> + + <template v-if="useObjectStorage"> + <FormInput v-model="objectStorageBaseUrl" class="_formBlock"> + <template #label>{{ $ts.objectStorageBaseUrl }}</template> + <template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template> + </FormInput> + + <FormInput v-model="objectStorageBucket" class="_formBlock"> + <template #label>{{ $ts.objectStorageBucket }}</template> + <template #caption>{{ $ts.objectStorageBucketDesc }}</template> + </FormInput> + + <FormInput v-model="objectStoragePrefix" class="_formBlock"> + <template #label>{{ $ts.objectStoragePrefix }}</template> + <template #caption>{{ $ts.objectStoragePrefixDesc }}</template> + </FormInput> + + <FormInput v-model="objectStorageEndpoint" class="_formBlock"> + <template #label>{{ $ts.objectStorageEndpoint }}</template> + <template #caption>{{ $ts.objectStorageEndpointDesc }}</template> + </FormInput> + + <FormInput v-model="objectStorageRegion" class="_formBlock"> + <template #label>{{ $ts.objectStorageRegion }}</template> + <template #caption>{{ $ts.objectStorageRegionDesc }}</template> + </FormInput> + + <FormSplit :min-width="280"> + <FormInput v-model="objectStorageAccessKey" class="_formBlock"> + <template #prefix><i class="fas fa-key"></i></template> + <template #label>Access key</template> + </FormInput> + + <FormInput v-model="objectStorageSecretKey" class="_formBlock"> + <template #prefix><i class="fas fa-key"></i></template> + <template #label>Secret key</template> + </FormInput> + </FormSplit> + + <FormSwitch v-model="objectStorageUseSSL" class="_formBlock"> + <template #label>{{ $ts.objectStorageUseSSL }}</template> + <template #caption>{{ $ts.objectStorageUseSSLDesc }}</template> + </FormSwitch> + + <FormSwitch v-model="objectStorageUseProxy" class="_formBlock"> + <template #label>{{ $ts.objectStorageUseProxy }}</template> + <template #caption>{{ $ts.objectStorageUseProxyDesc }}</template> + </FormSwitch> + + <FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock"> + <template #label>{{ $ts.objectStorageSetPublicRead }}</template> + </FormSwitch> + + <FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock"> + <template #label>s3ForcePathStyle</template> + </FormSwitch> + </template> + </div> </FormSuspense> -</FormBase> +</MkSpacer> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@/components/debobigego/switch.vue'; -import FormInput from '@/components/debobigego/input.vue'; -import FormButton from '@/components/debobigego/button.vue'; -import FormBase from '@/components/debobigego/base.vue'; -import FormGroup from '@/components/debobigego/group.vue'; -import FormSuspense from '@/components/debobigego/suspense.vue'; +import FormSwitch from '@/components/form/switch.vue'; +import FormInput from '@/components/form/input.vue'; +import FormGroup from '@/components/form/group.vue'; +import FormSuspense from '@/components/form/suspense.vue'; +import FormSplit from '@/components/form/split.vue'; +import FormSection from '@/components/form/section.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; @@ -79,10 +81,10 @@ export default defineComponent({ components: { FormSwitch, FormInput, - FormBase, FormGroup, - FormButton, FormSuspense, + FormSplit, + FormSection, }, emits: ['info'], @@ -93,6 +95,12 @@ export default defineComponent({ title: this.$ts.objectStorage, icon: 'fas fa-cloud', bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: this.$ts.save, + handler: this.save, + }], }, useObjectStorage: false, objectStorageBaseUrl: null, diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue index adfb2e786c63d9127c316022558d6e460ced5b4f..ae0eaf2572d16ec67cc58a18f52dbaaf5083d04e 100644 --- a/packages/client/src/pages/admin/security.vue +++ b/packages/client/src/pages/admin/security.vue @@ -1,31 +1,35 @@ <template> -<FormBase> +<MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <FormLink to="/admin/bot-protection"> - <i class="fas fa-shield-alt"></i> {{ $ts.botProtection }} - <template v-if="enableHcaptcha" #suffix>hCaptcha</template> - <template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template> - <template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template> - </FormLink> + <div class="_formRoot"> + <FormSection> + <FormSwitch v-model="enableRegistration" class="_formBlock"> + <template #label>{{ $ts.enableRegistration }}</template> + </FormSwitch> - <FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch> + <FormSwitch v-model="emailRequiredForSignup" class="_formBlock"> + <template #label>{{ $ts.emailRequiredForSignup }}</template> + </FormSwitch> + </FormSection> - <FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch> - - <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormLink to="/admin/bot-protection" class="_formBlock"> + <i class="fas fa-shield-alt"></i> {{ $ts.botProtection }} + <template v-if="enableHcaptcha" #suffix>hCaptcha</template> + <template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template> + <template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template> + </FormLink> + </div> </FormSuspense> -</FormBase> +</MkSpacer> </template> <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import FormLink from '@/components/debobigego/link.vue'; -import FormSwitch from '@/components/debobigego/switch.vue'; -import FormButton from '@/components/debobigego/button.vue'; -import FormBase from '@/components/debobigego/base.vue'; -import FormGroup from '@/components/debobigego/group.vue'; -import FormInfo from '@/components/debobigego/info.vue'; -import FormSuspense from '@/components/debobigego/suspense.vue'; +import FormLink from '@/components/form/link.vue'; +import FormSwitch from '@/components/form/switch.vue'; +import FormInfo from '@/components/ui/info.vue'; +import FormSuspense from '@/components/form/suspense.vue'; +import FormSection from '@/components/form/section.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; @@ -34,10 +38,8 @@ export default defineComponent({ components: { FormLink, FormSwitch, - FormBase, - FormGroup, - FormButton, FormInfo, + FormSection, FormSuspense, }, @@ -49,6 +51,12 @@ export default defineComponent({ title: this.$ts.security, icon: 'fas fa-lock', bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: this.$ts.save, + handler: this.save, + }], }, enableHcaptcha: false, enableRecaptcha: false, diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue index d88445abdbb75ecd2e4613b2ba95f84fda05eb1e..78c2616051f28ca7603d63d3e41d48aa42440a60 100644 --- a/packages/client/src/pages/admin/settings.vue +++ b/packages/client/src/pages/admin/settings.vue @@ -1,72 +1,75 @@ <template> -<FormBase> +<MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <FormInput v-model="name"> - <span>{{ $ts.instanceName }}</span> - </FormInput> - - <FormTextarea v-model="description"> - <span>{{ $ts.instanceDescription }}</span> - </FormTextarea> - - <FormInput v-model="iconUrl"> - <template #prefix><i class="fas fa-link"></i></template> - <span>{{ $ts.iconUrl }}</span> - </FormInput> - - <FormInput v-model="bannerUrl"> - <template #prefix><i class="fas fa-link"></i></template> - <span>{{ $ts.bannerUrl }}</span> - </FormInput> - - <FormInput v-model="backgroundImageUrl"> - <template #prefix><i class="fas fa-link"></i></template> - <span>{{ $ts.backgroundImageUrl }}</span> - </FormInput> - - <FormInput v-model="tosUrl"> - <template #prefix><i class="fas fa-link"></i></template> - <span>{{ $ts.tosUrl }}</span> - </FormInput> - - <FormInput v-model="maintainerName"> - <span>{{ $ts.maintainerName }}</span> - </FormInput> - - <FormInput v-model="maintainerEmail" type="email"> - <template #prefix><i class="fas fa-envelope"></i></template> - <span>{{ $ts.maintainerEmail }}</span> - </FormInput> - - <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="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch> - <FormSwitch v-model="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch> - <FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo> - - <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <div class="_formRoot"> + <FormInput v-model="name" class="_formBlock"> + <template #label>{{ $ts.instanceName }}</template> + </FormInput> + + <FormTextarea v-model="description" class="_formBlock"> + <template #label>{{ $ts.instanceDescription }}</template> + </FormTextarea> + + <FormInput v-model="iconUrl" class="_formBlock"> + <template #prefix><i class="fas fa-link"></i></template> + <template #label>{{ $ts.iconUrl }}</template> + </FormInput> + + <FormInput v-model="bannerUrl" class="_formBlock"> + <template #prefix><i class="fas fa-link"></i></template> + <template #label>{{ $ts.bannerUrl }}</template> + </FormInput> + + <FormInput v-model="backgroundImageUrl" class="_formBlock"> + <template #prefix><i class="fas fa-link"></i></template> + <template #label>{{ $ts.backgroundImageUrl }}</template> + </FormInput> + + <FormInput v-model="tosUrl" class="_formBlock"> + <template #prefix><i class="fas fa-link"></i></template> + <template #label>{{ $ts.tosUrl }}</template> + </FormInput> + + <FormSplit :min-width="300"> + <FormInput v-model="maintainerName" class="_formBlock"> + <template #label>{{ $ts.maintainerName }}</template> + </FormInput> + + <FormInput v-model="maintainerEmail" type="email" class="_formBlock"> + <template #prefix><i class="fas fa-envelope"></i></template> + <template #label>{{ $ts.maintainerEmail }}</template> + </FormInput> + </FormSplit> + + <FormTextarea v-model="pinnedUsers" class="_formBlock"> + <template #label>{{ $ts.pinnedUsers }}</template> + <template #caption>{{ $ts.pinnedUsersDescription }}</template> + </FormTextarea> + + <FormInput v-model="maxNoteTextLength" type="number" class="_formBlock"> + <template #prefix><i class="fas fa-pencil-alt"></i></template> + <template #label>{{ $ts.maxNoteTextLength }}</template> + </FormInput> + + <FormSection> + <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch> + <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch> + <FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo> + </FormSection> + </div> </FormSuspense> -</FormBase> +</MkSpacer> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@/components/debobigego/switch.vue'; -import FormInput from '@/components/debobigego/input.vue'; -import FormButton from '@/components/debobigego/button.vue'; -import FormBase from '@/components/debobigego/base.vue'; -import FormGroup from '@/components/debobigego/group.vue'; -import FormTextarea from '@/components/debobigego/textarea.vue'; -import FormInfo from '@/components/debobigego/info.vue'; -import FormSuspense from '@/components/debobigego/suspense.vue'; +import FormSwitch from '@/components/form/switch.vue'; +import FormInput from '@/components/form/input.vue'; +import FormTextarea from '@/components/form/textarea.vue'; +import FormInfo from '@/components/ui/info.vue'; +import FormSection from '@/components/form/section.vue'; +import FormSplit from '@/components/form/split.vue'; +import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; @@ -75,12 +78,11 @@ export default defineComponent({ components: { FormSwitch, FormInput, - FormBase, - FormGroup, - FormButton, + FormSuspense, FormTextarea, FormInfo, - FormSuspense, + FormSection, + FormSplit, }, emits: ['info'], @@ -91,6 +93,12 @@ export default defineComponent({ title: this.$ts.general, icon: 'fas fa-cog', bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: this.$ts.save, + handler: this.save, + }], }, name: null, description: null, diff --git a/packages/client/src/pages/federation.vue b/packages/client/src/pages/federation.vue index 4e5f428ff91d653ce1e4bfc0612e3b473ec83e16..a467c5eeb86e768d80cd9f5ca9a8dc4c75666ae0 100644 --- a/packages/client/src/pages/federation.vue +++ b/packages/client/src/pages/federation.vue @@ -6,7 +6,7 @@ <template #prefix><i class="fas fa-search"></i></template> <template #label>{{ $ts.host }}</template> </MkInput> - <div class="_inputSplit" style="margin-top: var(--margin);"> + <FormSplit style="margin-top: var(--margin);"> <MkSelect v-model="state"> <template #label>{{ $ts.state }}</template> <option value="all">{{ $ts.all }}</option> @@ -38,7 +38,7 @@ <option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option> <option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option> </MkSelect> - </div> + </FormSplit> </div> <MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination"> @@ -101,6 +101,7 @@ import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkSelect from '@/components/form/select.vue'; import MkPagination from '@/components/ui/pagination.vue'; +import FormSplit from '@/components/form/split.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; @@ -110,6 +111,7 @@ export default defineComponent({ MkInput, MkSelect, MkPagination, + FormSplit, }, emits: ['info'], diff --git a/packages/client/src/pages/settings/drive.vue b/packages/client/src/pages/settings/drive.vue index 9ab99c6efe8ca16029efcffbde23765912e4ac1f..c123159b61b4aee36a37350eaf65e98d16962ccb 100644 --- a/packages/client/src/pages/settings/drive.vue +++ b/packages/client/src/pages/settings/drive.vue @@ -5,7 +5,7 @@ <div class="_formBlock uawsfosz"> <div class="meter"><div :style="meterStyle"></div></div> </div> - <div class="_inputSplit _formBlock"> + <FormSplit> <MkKeyValue class="_formBlock"> <template #key>{{ $ts.capacity }}</template> <template #value>{{ bytes(capacity, 1) }}</template> @@ -14,7 +14,7 @@ <template #key>{{ $ts.inUse }}</template> <template #value>{{ bytes(usage, 1) }}</template> </MkKeyValue> - </div> + </FormSplit> </FormSection> <FormSection> @@ -38,6 +38,7 @@ import * as tinycolor from 'tinycolor2'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkKeyValue from '@/components/key-value.vue'; +import FormSplit from '@/components/form/split.vue'; import * as os from '@/os'; import bytes from '@/filters/bytes'; import * as symbols from '@/symbols'; @@ -49,6 +50,7 @@ export default defineComponent({ FormLink, FormSection, MkKeyValue, + FormSplit, }, emits: ['info'], diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss index 181521b4f5a4b0a8f0cd68a29249b8ef1b173fc0..b95a5c395089946851c03dd03782a4bc1095b186 100644 --- a/packages/client/src/style.scss +++ b/packages/client/src/style.scss @@ -386,16 +386,6 @@ hr { backdrop-filter: var(--blur, blur(15px)); } -._inputSplit { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); - grid-gap: 12px; - - > * { - margin: 0 !important; - } -} - ._formBlock { margin: 1.5em 0; } diff --git a/packages/client/webpack.config.js b/packages/client/webpack.config.js index 7bcfdcb15d8b7d9fe01775a477b373389b1b94e8..a50851e17fe516f5843897f26d440f6a7a5aa044 100644 --- a/packages/client/webpack.config.js +++ b/packages/client/webpack.config.js @@ -47,6 +47,7 @@ module.exports = { loader: 'vue-loader', options: { cssSourceMap: false, + reactivityTransform: true, compilerOptions: { preserveWhitespace: false } diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index 4f666c252b8201e487b4efc0e4c37d91a3ed8245..10607feb27be53e49e9703fad5989a3fd41db78e 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -6162,10 +6162,10 @@ vue-eslint-parser@^8.0.1: lodash "^4.17.21" semver "^7.3.5" -vue-loader@16.8.3: - version "16.8.3" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-16.8.3.tgz#d43e675def5ba9345d6c7f05914c13d861997087" - integrity sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA== +vue-loader@17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.0.tgz#2eaa80aab125b19f00faa794b5bd867b17f85acb" + integrity sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg== dependencies: chalk "^4.1.0" hash-sum "^2.0.0"