summaryrefslogtreecommitdiff
path: root/src/client/components
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-09-30 00:50:45 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-09-30 00:50:45 +0900
commit1ac1a968b9edd801aa13e8dae56ca378744d9e2e (patch)
tree858fc2a3f93db6590334e1056fc4a93814c8cfcc /src/client/components
parentadd todo (diff)
downloadsharkey-1ac1a968b9edd801aa13e8dae56ca378744d9e2e.tar.gz
sharkey-1ac1a968b9edd801aa13e8dae56ca378744d9e2e.tar.bz2
sharkey-1ac1a968b9edd801aa13e8dae56ca378744d9e2e.zip
refactor components
Diffstat (limited to 'src/client/components')
-rw-r--r--src/client/components/abuse-report-window.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/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/instance-stats.vue2
-rw-r--r--src/client/components/note-detailed.vue2
-rw-r--r--src/client/components/note-preview.vue2
-rw-r--r--src/client/components/note.sub.vue2
-rw-r--r--src/client/components/note.vue2
-rw-r--r--src/client/components/notification-setting-window.vue2
-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/sample.vue8
-rwxr-xr-xsrc/client/components/signin.vue10
-rw-r--r--src/client/components/signup.vue22
-rw-r--r--src/client/components/tab.vue8
-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.vue8
-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/switch.vue144
-rw-r--r--src/client/components/ui/textarea.vue254
-rw-r--r--src/client/components/user-select-dialog.vue4
-rw-r--r--src/client/components/widgets.vue2
60 files changed, 1587 insertions, 1506 deletions
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/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..0ee0881236
--- /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></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;
+ }
+
+ &.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: 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);
+ }
+ }
+
+ > .label {
+ margin-left: 12px;
+ display: block;
+ transition: inherit;
+ color: var(--fg);
+
+ > span {
+ display: block;
+ line-height: 20px;
+ transition: inherit;
+ }
+ }
+ }
+}
+</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/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/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/note-detailed.vue b/src/client/components/note-detailed.vue
index e7f116d1fd..68e7c87f2e 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">
diff --git a/src/client/components/note-preview.vue b/src/client/components/note-preview.vue
index 4248c2bb1d..406a475cd9 100644
--- a/src/client/components/note-preview.vue
+++ b/src/client/components/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/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..9fa986836d 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">
diff --git a/src/client/components/notification-setting-window.vue b/src/client/components/notification-setting-window.vue
index c33106ae15..0b41672170 100644
--- a/src/client/components/notification-setting-window.vue
+++ b/src/client/components/notification-setting-window.vue
@@ -29,7 +29,7 @@
<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';
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/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.vue b/src/client/components/signup.vue
index d332274111..f555c1df6d 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -1,11 +1,11 @@
<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>
@@ -19,7 +19,7 @@
<span v-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 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 +28,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 +36,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 +44,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" primary data-cy-signup-submit>{{ $ts.start }}</MkButton>
</template>
</form>
</template>
@@ -57,8 +57,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';
diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue
index 3902b7f98f..7705fc3d6d 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.modelValue }],
key: option.key,
- disabled: this.value === option.props.value,
+ disabled: this.modelValue === option.props.modelValue,
onClick: () => {
- this.$emit('update:value', option.props.value);
+ this.$emit('update:modelValue', option.props.modelValue);
}
}, option.children), [
[resolveDirective('click-anime')]
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..6e3cd485c8 100644
--- a/src/client/components/ui/button.vue
+++ b/src/client/components/ui/button.vue
@@ -181,14 +181,6 @@ export default defineComponent({
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/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/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/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';