diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-08-06 22:29:19 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-08-06 22:29:19 +0900 |
| commit | 46d571107197c6c54a0dfa91ae7990f336ba925a (patch) | |
| tree | 1a2054da41ada79c54c81fab704063c5f1eaecab /src/client/components/ui | |
| parent | Improve doc (diff) | |
| download | misskey-46d571107197c6c54a0dfa91ae7990f336ba925a.tar.gz misskey-46d571107197c6c54a0dfa91ae7990f336ba925a.tar.bz2 misskey-46d571107197c6c54a0dfa91ae7990f336ba925a.zip | |
:art:
Diffstat (limited to 'src/client/components/ui')
| -rw-r--r-- | src/client/components/ui/input.vue | 285 | ||||
| -rw-r--r-- | src/client/components/ui/select.vue | 317 | ||||
| -rw-r--r-- | src/client/components/ui/switch.vue | 10 | ||||
| -rw-r--r-- | src/client/components/ui/textarea.vue | 277 |
4 files changed, 401 insertions, 488 deletions
diff --git a/src/client/components/ui/input.vue b/src/client/components/ui/input.vue index 7415d9896b..22dd0fe9a5 100644 --- a/src/client/components/ui/input.vue +++ b/src/client/components/ui/input.vue @@ -1,32 +1,9 @@ <template> -<div class="juejbjww" :class="{ focused, filled, inline, disabled }"> - <div class="icon" ref="icon"><slot name="icon"></slot></div> - <div class="input"> - <span class="label" ref="labelEl"><slot></slot></span> - <span class="title" ref="title"> - <slot name="title"></slot> - <span class="warning" v-if="invalid"><i class="fas fa-exclamation-circle"></i>{{ $refs.input.validationMessage }}</span> - </span> +<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 v-if="debounce" ref="inputEl" - v-debounce="500" - :type="type" - v-model.lazy="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" - > - <input v-else ref="inputEl" + <input ref="inputEl" :type="type" v-model="v" :disabled="disabled" @@ -48,23 +25,25 @@ </datalist> <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div> </div> - <button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button> - <div class="desc _caption"><slot name="desc"></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, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import debounce from 'v-debounce'; -import * as os from '@client/os'; +import MkButton from './button.vue'; +import { debounce } from 'throttle-debounce'; export default defineComponent({ - directives: { - debounce + components: { + MkButton, }, + props: { - value: { - required: false + modelValue: { + required: true }, type: { type: String, @@ -104,9 +83,6 @@ export default defineComponent({ step: { required: false }, - debounce: { - required: false - }, datalist: { type: Array, required: false, @@ -116,15 +92,23 @@ export default defineComponent({ required: false, default: false }, - save: { - type: Function, + 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); @@ -133,7 +117,6 @@ export default defineComponent({ const inputEl = ref(null); const prefixEl = ref(null); const suffixEl = ref(null); - const labelEl = ref(null); const focus = () => inputEl.value.focus(); const onInput = (ev) => { @@ -148,15 +131,28 @@ export default defineComponent({ } }; - watch(value, newValue => { + const updated = () => { + changed.value = false; + if (type?.value === 'number') { + context.emit('update:modelValue', parseFloat(v.value)); + } else { + context.emit('update:modelValue', v.value); + } + }; + + const debouncedUpdated = debounce(1000, updated); + + watch(modelValue, newValue => { v.value = newValue; }); watch(v, newValue => { - if (type?.value === 'number') { - context.emit('update:value', parseFloat(newValue)); - } else { - context.emit('update:value', newValue); + if (!props.manualSave) { + if (props.debounce) { + debouncedUpdated(); + } else { + updated(); + } } invalid.value = inputEl.value.validity.badInput; @@ -172,7 +168,6 @@ export default defineComponent({ // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する const clock = setInterval(() => { if (prefixEl.value) { - labelEl.value.style.left = (prefixEl.value.offsetLeft + prefixEl.value.offsetWidth) + 'px'; if (prefixEl.value.offsetWidth) { inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; } @@ -200,148 +195,70 @@ export default defineComponent({ inputEl, prefixEl, suffixEl, - labelEl, focus, onInput, onKeydown, + updated, }; }, }); </script> <style lang="scss" scoped> -.juejbjww { - position: relative; - margin: 32px 0; - - &:not(.inline):first-child { - margin-top: 8px; - } +.matxzzsk { + margin: 1em 0; - &:not(.inline):last-child { - margin-bottom: 8px; + > .label { + font-size: 0.85em; + padding: 0 0 6px 6px; + font-weight: bold; + user-select: none; } - > .icon { - position: absolute; - top: 0; - left: 0; - width: 24px; - text-align: center; - line-height: 32px; - - &:not(:empty) + .input { - margin-left: 28px; - } + > .caption { + font-size: 0.8em; + padding: 6px 0 0 6px; + color: var(--fgTransparentWeak); } > .input { + $height: 42px; position: relative; - &:before { - content: ''; - display: block; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 1px; - background: var(--inputBorder); - } - - &:after { - content: ''; - display: block; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 2px; - background: var(--accent); - opacity: 0; - transform: scaleX(0.12); - transition: border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); - will-change: border opacity transform; - } - - > .label { - position: absolute; - z-index: 1; - top: 0; - left: 0; - pointer-events: none; - transition: 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); - transition-duration: 0.3s; - font-size: 1em; - line-height: 32px; - color: var(--inputLabel); - pointer-events: none; - //will-change transform - transform-origin: top left; - transform: scale(1); - } - - > .title { - position: absolute; - z-index: 1; - top: -17px; - left: 0 !important; - pointer-events: none; - font-size: 1em; - line-height: 32px; - color: var(--inputLabel); - pointer-events: none; - //will-change transform - transform-origin: top left; - transform: scale(.75); - white-space: nowrap; - width: 133%; - overflow: hidden; - text-overflow: ellipsis; - - > .warning { - margin-left: 0.5em; - color: var(--infoWarnFg); - - > svg { - margin-right: 0.1em; - } - } - } - > input { - $height: 32px; + appearance: none; + -webkit-appearance: none; display: block; height: $height; width: 100%; margin: 0; - padding: 0; + 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 1px var(--inputBorder); + border-radius: 6px; outline: none; box-shadow: none; box-sizing: border-box; - &[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 12px; font-size: 1em; - line-height: 32px; - color: var(--inputLabel); + height: $height; pointer-events: none; &:empty { @@ -360,66 +277,32 @@ export default defineComponent({ > .prefix { left: 0; - padding-right: 4px; + padding-right: 6px; } > .suffix { right: 0; - padding-left: 4px; - } - } - - > .save { - margin: 6px 0 0 0; - font-size: 0.8em; - } - - > .desc { - margin: 6px 0 0 0; - - &:empty { - display: none; + padding-left: 6px; } - * { + &.inline { + display: inline-block; margin: 0; } - } - - &.focused { - > .input { - &:after { - opacity: 1; - transform: scaleX(1); - } - - > .label { - color: var(--accent); - } - } - } - &.focused, - &.filled { - > .input { - > .label { - top: -17px; - left: 0 !important; - transform: scale(0.75); + &.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/ui/select.vue b/src/client/components/ui/select.vue index e78c44fe0d..c38bd7230f 100644 --- a/src/client/components/ui/select.vue +++ b/src/client/components/ui/select.vue @@ -1,185 +1,210 @@ <template> -<div class="eiipwacr" :class="{ focused, disabled, filled, inline }"> - <div class="icon" ref="icon"><slot name="icon"></slot></div> - <div class="input" @click="focus"> - <span class="label" ref="label"><slot name="label"></slot></span> - <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"> - <slot name="suffix"> - <i class="fas fa-chevron-down"></i> - </slot> - </div> + <div class="suffix" ref="suffixEl"><i class="fas fa-chevron-down"></i></div> </div> - <div class="text"><slot name="text"></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 { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; +import MkButton from './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 { - focused: 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); }; - }, - 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(); } - }, - filled(): boolean { - return true; - } - }, - mounted() { - if (this.$refs.prefix) { - this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px'; - } + + 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> -.eiipwacr { - position: relative; - margin: 32px 0; +.vblkjoeq { + margin: 1em 0; - &:not(.inline):first-child { - margin-top: 8px; + > .label { + font-size: 0.85em; + padding: 0 0 6px 6px; + font-weight: bold; + user-select: none; } - &:not(.inline):last-child { - margin-bottom: 8px; - } - - > .icon { - position: absolute; - top: 0; - left: 0; - width: 24px; - text-align: center; - line-height: 32px; - - &:not(:empty) + .input { - margin-left: 28px; - } + > .caption { + font-size: 0.8em; + padding: 6px 0 0 6px; + color: var(--fgTransparentWeak); } > .input { - display: flex; + $height: 42px; position: relative; - &:before { - content: ''; - display: block; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 1px; - background: var(--inputBorder); - } - - &:after { - content: ''; - display: block; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 2px; - background: var(--accent); - opacity: 0; - transform: scaleX(0.12); - transition: border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); - will-change: border opacity transform; - } - - > .label { - position: absolute; - top: 0; - left: 0; - pointer-events: none; - transition: 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); - transition-duration: 0.3s; - font-size: 1em; - line-height: 32px; - pointer-events: none; - //will-change transform - transform-origin: top left; - transform: scale(1); - } - > select { + appearance: none; + -webkit-appearance: none; display: block; - flex: 1; + height: $height; width: 100%; - padding: 0; + margin: 0; + padding: 0 8px; font: inherit; font-weight: normal; font-size: 1em; - height: 32px; - 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; - 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 { @@ -187,53 +212,41 @@ 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-left: 4px; - } - } - - > .text { - margin: 6px 0; - font-size: 0.8em; - - &:empty { - display: none; + right: 0; + padding-left: 6px; } - * { + &.inline { + display: inline-block; margin: 0; } - } - - &.focused { - > .input { - &:after { - opacity: 1; - transform: scaleX(1); - } - > .label { - color: var(--accent); + &.focused { + > select { + border-color: var(--accent); } } - } - &.focused, - &.filled { - > .input { - > .label { - top: -17px; - left: 0 !important; - transform: scale(0.75); + &.disabled { + opacity: 0.7; + + &, * { + cursor: not-allowed !important; } } } diff --git a/src/client/components/ui/switch.vue b/src/client/components/ui/switch.vue index 762fba6d99..7aa9c0619d 100644 --- a/src/client/components/ui/switch.vue +++ b/src/client/components/ui/switch.vue @@ -18,7 +18,7 @@ </span> <span class="label"> <span><slot></slot></span> - <p><slot name="desc"></slot></p> + <p><slot name="caption"></slot></p> </span> </div> </template> @@ -28,7 +28,7 @@ import { defineComponent } from 'vue'; export default defineComponent({ props: { - value: { + modelValue: { type: Boolean, default: false }, @@ -39,13 +39,13 @@ 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); } } }); @@ -136,7 +136,7 @@ export default defineComponent({ > p { margin: 0; - opacity: 0.7; + color: var(--fgTransparentWeak); font-size: 90%; } } diff --git a/src/client/components/ui/textarea.vue b/src/client/components/ui/textarea.vue index 1032c10d14..592b8b9ca0 100644 --- a/src/client/components/ui/textarea.vue +++ b/src/client/components/ui/textarea.vue @@ -1,30 +1,45 @@ <template> -<div class="adhpbeos" :class="{ focused, filled, tall, pre }"> - <div class="input"> - <span class="label" ref="label"><slot></slot></span> - <textarea ref="input" :class="{ code, _monospace: code }" - :value="value" +<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="!code" - @input="onInput" + :spellcheck="spellcheck" @focus="focused = true" @blur="focused = false" + @keydown="onKeydown($event)" + @input="onInput" ></textarea> </div> - <button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button> - <div class="desc _caption"><slot name="desc"></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 { 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: { - value: { + modelValue: { + required: true + }, + type: { + type: String, required: false }, required: { @@ -35,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 @@ -57,169 +87,156 @@ export default defineComponent({ required: false, default: false }, - save: { - type: Function, + debounce: { + type: Boolean, + required: false, + default: false + }, + manualSave: { + type: Boolean, required: false, + default: false }, }, - data() { + + 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 { - focused: false, - changed: false, - } + v, + focused, + invalid, + changed, + filled, + inputEl, + focus, + onInput, + onKeydown, + updated, + }; }, - computed: { - filled(): boolean { - return this.value != '' && this.value != null; - } - }, - methods: { - focus() { - this.$refs.input.focus(); - }, - onInput(ev) { - this.changed = true; - this.$emit('update:value', ev.target.value); - } - } }); </script> <style lang="scss" scoped> .adhpbeos { - margin: 42px 0 32px 0; - position: relative; + margin: 1em 0; - &:first-child { - margin-top: 16px; + > .label { + font-size: 0.85em; + padding: 0 0 6px 6px; + font-weight: bold; + user-select: none; } - &:last-child { - margin-bottom: 0; + > .caption { + font-size: 0.8em; + padding: 6px 0 0 6px; + color: var(--fgTransparentWeak); } > .input { position: relative; - - &:before { - content: ''; - display: block; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: none; - border: solid 1px var(--inputBorder); - border-radius: 3px; - pointer-events: none; - } - - &:after { - content: ''; - display: block; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: none; - border: solid 2px var(--accent); - border-radius: 3px; - opacity: 0; - transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); - pointer-events: none; - } - - > .label { - position: absolute; - top: 6px; - left: 12px; - pointer-events: none; - transition: 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); - transition-duration: 0.3s; - font-size: 1em; - line-height: 32px; - pointer-events: none; - //will-change transform - transform-origin: top left; - transform: scale(1); - } > textarea { + appearance: none; + -webkit-appearance: none; display: block; width: 100%; min-width: 100%; max-width: 100%; min-height: 130px; + margin: 0; padding: 12px; - box-sizing: border-box; font: inherit; font-weight: normal; font-size: 1em; - background: transparent; - border: none; - border-radius: 0; + color: var(--fg); + background: var(--panel); + border: solid 1px var(--inputBorder); + border-radius: 6px; outline: none; box-shadow: none; - color: var(--fg); + box-sizing: border-box; - &.code { - tab-size: 2; + &:hover { + border-color: var(--inputBorderHover); } } - } - - > .save { - margin: 6px 0 0 0; - font-size: 0.8em; - } - - > .desc { - margin: 6px 0 0 0; - - &:empty { - display: none; - } - * { - margin: 0; - } - } - - &.focused { - > .input { - &:after { - opacity: 1; - } - - > .label { - color: var(--accent); + &.focused { + > textarea { + border-color: var(--accent); } } - } - &.focused, - &.filled { - > .input { - > .label { - top: -24px; - left: 0 !important; - transform: scale(0.75); + &.disabled { + opacity: 0.7; + + &, * { + cursor: not-allowed !important; } } - } - &.tall { - > .input { + &.tall { > textarea { min-height: 200px; } } - } - &.pre { - > .input { + &.pre { > textarea { white-space: pre; } |