diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-11-28 20:07:37 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-11-28 20:07:37 +0900 |
| commit | e8005c8d3a6edf2c8cdce3fe098fb9acff8a57c6 (patch) | |
| tree | 4283a0a36f5cb03f0fb3a534142c06783f8ff725 /packages/client/src/components/form | |
| parent | /antennas/notes API で日付による絞り込みができるようにする... (diff) | |
| download | sharkey-e8005c8d3a6edf2c8cdce3fe098fb9acff8a57c6.tar.gz sharkey-e8005c8d3a6edf2c8cdce3fe098fb9acff8a57c6.tar.bz2 sharkey-e8005c8d3a6edf2c8cdce3fe098fb9acff8a57c6.zip | |
client: refine ui
Diffstat (limited to 'packages/client/src/components/form')
| -rw-r--r-- | packages/client/src/components/form/group.vue | 35 | ||||
| -rw-r--r-- | packages/client/src/components/form/input.vue | 22 | ||||
| -rw-r--r-- | packages/client/src/components/form/link.vue | 112 | ||||
| -rw-r--r-- | packages/client/src/components/form/pagination.vue | 44 | ||||
| -rw-r--r-- | packages/client/src/components/form/radio.vue | 22 | ||||
| -rw-r--r-- | packages/client/src/components/form/radios.vue | 49 | ||||
| -rw-r--r-- | packages/client/src/components/form/range.vue | 272 | ||||
| -rw-r--r-- | packages/client/src/components/form/section.vue | 26 | ||||
| -rw-r--r-- | packages/client/src/components/form/select.vue | 11 | ||||
| -rw-r--r-- | packages/client/src/components/form/slot.vue | 15 | ||||
| -rw-r--r-- | packages/client/src/components/form/suspense.vue | 98 | ||||
| -rw-r--r-- | packages/client/src/components/form/switch.vue | 12 | ||||
| -rw-r--r-- | packages/client/src/components/form/textarea.vue | 16 |
13 files changed, 597 insertions, 137 deletions
diff --git a/packages/client/src/components/form/group.vue b/packages/client/src/components/form/group.vue new file mode 100644 index 0000000000..2fc203f1b9 --- /dev/null +++ b/packages/client/src/components/form/group.vue @@ -0,0 +1,35 @@ +<template> +<div v-sticky-container v-panel class="adfeebaf _formBlock"> + <div class="label"><slot name="label"></slot></div> + <div class="main _formRoot"> + <slot></slot> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ +}); +</script> + +<style lang="scss" scoped> +.adfeebaf { + padding: 24px 24px; + border-radius: var(--radius); + + > .label { + font-weight: bold; + padding: 0 0 16px 0; + + &:empty { + display: none; + } + } + + > .main { + + } +} +</style> diff --git a/packages/client/src/components/form/input.vue b/packages/client/src/components/form/input.vue index 99267f9231..c990b693f1 100644 --- a/packages/client/src/components/form/input.vue +++ b/packages/client/src/components/form/input.vue @@ -5,6 +5,7 @@ <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div> <input ref="inputEl" v-model="v" + v-panel :type="type" :disabled="disabled" :required="required" @@ -27,7 +28,7 @@ </div> <div class="caption"><slot name="caption"></slot></div> - <MkButton v-if="manualSave && changed" primary @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-check"></i> {{ $ts.save }}</MkButton> </div> </template> @@ -114,9 +115,9 @@ export default defineComponent({ 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 inputEl = ref<HTMLElement>(); + const prefixEl = ref<HTMLElement>(); + const suffixEl = ref<HTMLElement>(); const focus = () => inputEl.value.focus(); const onInput = (ev) => { @@ -208,7 +209,7 @@ export default defineComponent({ .matxzzsk { > .label { font-size: 0.85em; - padding: 0 0 8px 12px; + padding: 0 0 8px 0; user-select: none; &:empty { @@ -217,8 +218,8 @@ export default defineComponent({ } > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; + font-size: 0.85em; + padding: 8px 0 0 0; color: var(--fgTransparentWeak); &:empty { @@ -242,8 +243,7 @@ export default defineComponent({ font-weight: normal; font-size: 1em; color: var(--fg); - background: var(--panel); - border: solid 0.5px var(--inputBorder); + border: solid 0.5px var(--panel); border-radius: 6px; outline: none; box-shadow: none; @@ -311,5 +311,9 @@ export default defineComponent({ } } } + + > .save { + margin: 8px 0 0 0; + } } </style> diff --git a/packages/client/src/components/form/link.vue b/packages/client/src/components/form/link.vue new file mode 100644 index 0000000000..3eb74425b0 --- /dev/null +++ b/packages/client/src/components/form/link.vue @@ -0,0 +1,112 @@ +<template> +<div class="ffcbddfc" :class="{ inline }"> + <a v-if="external" class="main _button" :href="to" target="_blank"> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + <span class="right"> + <span class="text"><slot name="suffix"></slot></span> + <i class="fas fa-external-link-alt icon"></i> + </span> + </a> + <MkA v-else class="main _button" :class="{ active }" :to="to" :behavior="behavior"> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + <span class="right"> + <span class="text"><slot name="suffix"></slot></span> + <i class="fas fa-chevron-right icon"></i> + </span> + </MkA> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + props: { + to: { + type: String, + required: true + }, + active: { + type: Boolean, + required: false + }, + external: { + type: Boolean, + required: false + }, + behavior: { + type: String, + required: false, + }, + inline: { + type: Boolean, + required: false + }, + }, +}); +</script> + +<style lang="scss" scoped> +.ffcbddfc { + display: block; + + &.inline { + display: inline-block; + } + + > .main { + display: flex; + align-items: center; + width: 100%; + box-sizing: border-box; + padding: 12px 14px 12px 14px; + background: var(--buttonBg); + border-radius: 6px; + font-size: 0.9em; + + &:hover { + text-decoration: none; + background: var(--buttonHoverBg); + } + + &.active { + color: var(--accent); + background: var(--buttonHoverBg); + } + + > .icon { + margin-right: 0.75em; + flex-shrink: 0; + text-align: center; + opacity: 0.8; + + &:empty { + display: none; + + & + .text { + padding-left: 4px; + } + } + } + + > .text { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + padding-right: 12px; + } + + > .right { + margin-left: auto; + opacity: 0.7; + white-space: nowrap; + + > .text:not(:empty) { + margin-right: 0.75em; + } + } + } +} +</style> diff --git a/packages/client/src/components/form/pagination.vue b/packages/client/src/components/form/pagination.vue new file mode 100644 index 0000000000..3d3b40a783 --- /dev/null +++ b/packages/client/src/components/form/pagination.vue @@ -0,0 +1,44 @@ +<template> +<FormSlot> + <template #label><slot name="label"></slot></template> + <div class="abcaccfa"> + <slot :items="items"></slot> + <div v-if="empty" key="_empty_" class="empty"> + <slot name="empty"></slot> + </div> + <MkButton v-show="more" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore"> + <template v-if="!moreFetching">{{ $ts.loadMore }}</template> + <template v-if="moreFetching"><MkLoading inline/></template> + </MkButton> + </div> +</FormSlot> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import MkButton from '@/components/ui/button.vue'; +import FormSlot from './slot.vue'; +import paging from '@/scripts/paging'; + +export default defineComponent({ + components: { + MkButton, + FormSlot, + }, + + mixins: [ + paging({}), + ], + + props: { + pagination: { + required: true + }, + }, +}); +</script> + +<style lang="scss" scoped> +.abcaccfa { +} +</style> diff --git a/packages/client/src/components/form/radio.vue b/packages/client/src/components/form/radio.vue index 0f31d8fa0a..f0b8c71376 100644 --- a/packages/client/src/components/form/radio.vue +++ b/packages/client/src/components/form/radio.vue @@ -1,5 +1,6 @@ <template> <div + v-panel class="novjtctn" :class="{ disabled, checked }" :aria-checked="checked" @@ -50,9 +51,10 @@ export default defineComponent({ .novjtctn { position: relative; display: inline-block; - margin: 8px 20px 0 0; text-align: left; cursor: pointer; + padding: 11px 14px; + border-radius: 6px; transition: all 0.3s; > * { @@ -68,6 +70,14 @@ export default defineComponent({ } &.checked { + background: var(--accentedBg) !important; + border-color: var(--accent); + color: var(--accent); + + &, * { + cursor: default !important; + } + > .button { border-color: var(--accent); @@ -79,6 +89,11 @@ export default defineComponent({ } } + &:hover { + border-color: var(--inputBorderHover); + color: var(--accent); + } + > input { position: absolute; width: 0; @@ -89,8 +104,8 @@ export default defineComponent({ > .button { position: absolute; - width: 20px; - height: 20px; + width: 14px; + height: 14px; background: none; border: solid 2px var(--inputBorder); border-radius: 100%; @@ -114,7 +129,6 @@ export default defineComponent({ > .label { margin-left: 28px; display: block; - font-size: 16px; line-height: 20px; cursor: pointer; } diff --git a/packages/client/src/components/form/radios.vue b/packages/client/src/components/form/radios.vue index 998a738202..ff5d51f9c7 100644 --- a/packages/client/src/components/form/radios.vue +++ b/packages/client/src/components/form/radios.vue @@ -23,6 +23,8 @@ export default defineComponent({ }, render() { let options = this.$slots.default(); + const label = this.$slots.label && this.$slots.label(); + const caption = this.$slots.caption && this.$slots.caption(); // なぜかFragmentになることがあるため if (options.length === 1 && options[0].props == null) options = options[0].children; @@ -30,12 +32,21 @@ export default defineComponent({ return h('div', { class: 'novjtcto' }, [ - ...options.map(option => h(MkRadio, { - key: option.key, - value: option.props.value, - modelValue: this.value, - 'onUpdate:modelValue': value => this.value = value, - }, option.children)) + ...(label ? [h('div', { + class: 'label' + }, [label])] : []), + h('div', { + class: 'body' + }, options.map(option => h(MkRadio, { + key: option.key, + value: option.props.value, + modelValue: this.value, + 'onUpdate:modelValue': value => this.value = value, + }, option.children)), + ), + ...(caption ? [h('div', { + class: 'caption' + }, [caption])] : []), ]); } }); @@ -43,12 +54,30 @@ export default defineComponent({ <style lang="scss"> .novjtcto { - &:first-child { - margin-top: 0; + > .label { + font-size: 0.85em; + padding: 0 0 8px 0; + user-select: none; + + &:empty { + display: none; + } + } + + > .body { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + grid-gap: 12px; } - &:last-child { - margin-bottom: 0; + > .caption { + font-size: 0.85em; + padding: 8px 0 0 0; + color: var(--fgTransparentWeak); + + &:empty { + display: none; + } } } </style> diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue index dd771abfe2..79a83d6a93 100644 --- a/packages/client/src/components/form/range.vue +++ b/packages/client/src/components/form/range.vue @@ -1,29 +1,27 @@ <template> -<div class="timctyfi" :class="{ focused, disabled }"> - <div class="icon"><slot name="icon"></slot></div> - <span class="label"><slot name="label"></slot></span> - <input - ref="input" - v-model="v" - type="range" - :disabled="disabled" - :min="min" - :max="max" - :step="step" - :autofocus="autofocus" - @focus="focused = true" - @blur="focused = false" - @input="$emit('update:value', $event.target.value)" - /> +<div class="timctyfi" :class="{ disabled }"> + <div class="label"><slot name="label"></slot></div> + <div v-panel class="body"> + <div ref="containerEl" class="container"> + <div class="track"> + <div class="highlight" :style="{ width: (steppedValue * 100) + '%' }"></div> + </div> + <div v-if="steps" class="ticks"> + <div v-for="i in (steps + 1)" class="tick" :style="{ left: (((i - 1) / steps) * 100) + '%' }"></div> + </div> + <div ref="thumbEl" v-tooltip="textConverter(finalValue)" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div> + </div> + </div> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, ref, watch } from 'vue'; +import * as os from '@/os'; export default defineComponent({ props: { - value: { + modelValue: { type: Number, required: false, default: 0 @@ -51,88 +49,198 @@ export default defineComponent({ autofocus: { type: Boolean, required: false - } + }, + textConverter: { + type: Function, + required: false, + default: (v) => v.toString(), + }, }, - data() { + + setup(props, context) { + const rawValue = ref((props.modelValue - props.min) / (props.max - props.min)); + const steppedValue = computed(() => { + if (props.step) { + const step = props.step / (props.max - props.min); + return (step * Math.round(rawValue.value / step)); + } else { + return rawValue.value; + } + }); + const finalValue = computed(() => { + return (steppedValue.value * (props.max - props.min)) + props.min; + }); + watch(finalValue, () => { + context.emit('update:modelValue', finalValue.value); + }); + + const thumbWidth = computed(() => { + if (thumbEl.value == null) return 0; + return thumbEl.value!.offsetWidth; + }); + const thumbPosition = computed(() => { + if (containerEl.value == null) return 0; + return (containerEl.value.offsetWidth - thumbWidth.value) * steppedValue.value; + }); + const steps = computed(() => { + if (props.step) { + return (props.max - props.min) / props.step; + } else { + return 0; + } + }); + const containerEl = ref<HTMLElement>(); + const thumbEl = ref<HTMLElement>(); + + const onMousedown = (ev: MouseEvent | TouchEvent) => { + ev.preventDefault(); + + const tooltipShowing = ref(true); + os.popup(import('@/components/ui/tooltip.vue'), { + showing: tooltipShowing, + text: computed(() => { + return props.textConverter(finalValue.value); + }), + source: thumbEl, + }, {}, 'closed'); + + const style = document.createElement('style'); + style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }')); + document.head.appendChild(style); + + const onDrag = (ev: MouseEvent | TouchEvent) => { + ev.preventDefault(); + const containerRect = containerEl.value!.getBoundingClientRect(); + const pointerX = ev.touches && ev.touches.length > 0 ? ev.touches[0].clientX : ev.clientX; + const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth.value / 2)); + rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth.value))); + }; + + const onMouseup = () => { + document.head.removeChild(style); + tooltipShowing.value = false; + window.removeEventListener('mousemove', onDrag); + window.removeEventListener('touchmove', onDrag); + window.removeEventListener('mouseup', onMouseup); + window.removeEventListener('touchend', onMouseup); + }; + + window.addEventListener('mousemove', onDrag); + window.addEventListener('touchmove', onDrag); + window.addEventListener('mouseup', onMouseup, { once: true }); + window.addEventListener('touchend', onMouseup, { once: true }); + }; + return { - v: this.value, - focused: false + rawValue, + finalValue, + steppedValue, + onMousedown, + containerEl, + thumbEl, + thumbPosition, + steps, }; }, - watch: { - value(v) { - this.v = parseFloat(v); - } - }, - mounted() { - if (this.autofocus) { - this.$nextTick(() => { - this.$refs.input.focus(); - }); - } - } }); </script> <style lang="scss" scoped> +@use "sass:math"; + .timctyfi { position: relative; - margin: 8px; - > .icon { - display: inline-block; - width: 24px; - text-align: center; - } + > .label { + font-size: 0.85em; + padding: 0 0 8px 0; + user-select: none; - > .title { - pointer-events: none; - font-size: 16px; - color: var(--inputLabel); - overflow: hidden; + &:empty { + display: none; + } } - > input { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: var(--X10); - height: 7px; - margin: 0 8px; - outline: 0; - border: 0; - border-radius: 7px; + > .caption { + font-size: 0.85em; + padding: 8px 0 0 0; + color: var(--fgTransparentWeak); - &.disabled { - opacity: 0.6; - cursor: not-allowed; + &:empty { + display: none; } + } - &::-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; - } + $thumbHeight: 20px; + $thumbWidth: 20px; + + > .body { + padding: 12px; + border-radius: 6px; + + > .container { + position: relative; + height: $thumbHeight; + + > .track { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: calc(100% - #{$thumbWidth}); + height: 3px; + background: rgba(0, 0, 0, 0.1); + border-radius: 999px; + overflow: clip; + + > .highlight { + position: absolute; + top: 0; + left: 0; + height: 100%; + background: var(--accent); + opacity: 0.5; + transition: width 0.2s cubic-bezier(0,0,0,1); + } + } + + > .ticks { + $tickWidth: 3px; + + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: calc(100% - #{$thumbWidth}); + + > .tick { + position: absolute; + bottom: 0; + width: $tickWidth; + height: 3px; + margin-left: - math.div($tickWidth, 2); + background: var(--divider); + border-radius: 999px; + } + } + + > .thumb { + position: absolute; + width: $thumbWidth; + height: $thumbHeight; + cursor: grab; + background: var(--accent); + border-radius: 999px; + transition: left 0.2s cubic-bezier(0,0,0,1); - &::-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); + &:hover { + background: var(--accentLighten); + } + } } } } diff --git a/packages/client/src/components/form/section.vue b/packages/client/src/components/form/section.vue index 76db7ac5c3..bc2ab966b8 100644 --- a/packages/client/src/components/form/section.vue +++ b/packages/client/src/components/form/section.vue @@ -1,7 +1,7 @@ <template> -<div v-size="{ max: [500] }" v-sticky-container class="vrtktovh"> +<div v-size="{ max: [500] }" v-sticky-container class="vrtktovh _formBlock"> <div class="label"><slot name="label"></slot></div> - <div class="main"> + <div class="main _formRoot"> <slot></slot> </div> </div> @@ -17,15 +17,33 @@ export default defineComponent({ <style lang="scss" scoped> .vrtktovh { + margin: 0; border-top: solid 0.5px var(--divider); + border-bottom: solid 0.5px var(--divider); + padding: 24px 0; + + & + .vrtktovh { + border-top: none; + } + + &:first-child { + border-top: none; + } + + &:last-child { + border-bottom: none; + } > .label { font-weight: bold; - padding: 24px 0 16px 0; + padding: 0 0 16px 0; + + &:empty { + display: none; + } } > .main { - margin-bottom: 32px; } } </style> diff --git a/packages/client/src/components/form/select.vue b/packages/client/src/components/form/select.vue index fe2a4e3a7d..9ecff1aa6f 100644 --- a/packages/client/src/components/form/select.vue +++ b/packages/client/src/components/form/select.vue @@ -3,7 +3,7 @@ <div class="label" @click="focus"><slot name="label"></slot></div> <div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick"> <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div> - <select ref="inputEl" v-model="v" + <select ref="inputEl" v-model="v" v-panel class="select" :disabled="disabled" :required="required" @@ -201,7 +201,7 @@ export default defineComponent({ .vblkjoeq { > .label { font-size: 0.85em; - padding: 0 0 8px 12px; + padding: 0 0 8px 0; user-select: none; &:empty { @@ -210,8 +210,8 @@ export default defineComponent({ } > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; + font-size: 0.85em; + padding: 8px 0 0 0; color: var(--fgTransparentWeak); &:empty { @@ -242,8 +242,7 @@ export default defineComponent({ font-weight: normal; font-size: 1em; color: var(--fg); - background: var(--panel); - border: solid 1px var(--inputBorder); + border: solid 1px var(--panel); border-radius: 6px; outline: none; box-shadow: none; diff --git a/packages/client/src/components/form/slot.vue b/packages/client/src/components/form/slot.vue index 8580c1307d..d031b2effc 100644 --- a/packages/client/src/components/form/slot.vue +++ b/packages/client/src/components/form/slot.vue @@ -18,11 +18,9 @@ export default defineComponent({ <style lang="scss" scoped> .adhpbeou { - margin: 1.5em 0; - > .label { font-size: 0.85em; - padding: 0 0 8px 12px; + padding: 0 0 8px 0; user-select: none; &:empty { @@ -31,20 +29,13 @@ export default defineComponent({ } > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; + font-size: 0.85em; + padding: 8px 0 0 0; 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/packages/client/src/components/form/suspense.vue b/packages/client/src/components/form/suspense.vue new file mode 100644 index 0000000000..4d5debe604 --- /dev/null +++ b/packages/client/src/components/form/suspense.vue @@ -0,0 +1,98 @@ +<template> +<transition name="fade" mode="out-in"> + <div v-if="pending"> + <MkLoading/> + </div> + <div v-else-if="resolved"> + <slot :result="result"></slot> + </div> + <div v-else> + <div class="wszdbhzo"> + <div><i class="fas fa-exclamation-triangle"></i> {{ $ts.somethingHappened }}</div> + <MkButton inline class="retry" @click="retry"><i class="fas fa-redo-alt"></i> {{ $ts.retry }}</MkButton> + </div> + </div> +</transition> +</template> + +<script lang="ts"> +import { defineComponent, PropType, ref, watch } from 'vue'; +import MkButton from '@/components/ui/button.vue'; + +export default defineComponent({ + components: { + MkButton + }, + + props: { + p: { + type: Function as PropType<() => Promise<any>>, + required: true, + } + }, + + setup(props, context) { + const pending = ref(true); + const resolved = ref(false); + const rejected = ref(false); + const result = ref(null); + + const process = () => { + if (props.p == null) { + return; + } + const promise = props.p(); + pending.value = true; + resolved.value = false; + rejected.value = false; + promise.then((_result) => { + pending.value = false; + resolved.value = true; + result.value = _result; + }); + promise.catch(() => { + pending.value = false; + rejected.value = true; + }); + }; + + watch(() => props.p, () => { + process(); + }, { + immediate: true + }); + + const retry = () => { + process(); + }; + + return { + pending, + resolved, + rejected, + result, + retry, + }; + } +}); +</script> + +<style lang="scss" scoped> +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.125s ease; +} +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +.wszdbhzo { + padding: 16px; + text-align: center; + + > .retry { + margin-top: 16px; + } +} +</style> diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue index d6df68a07f..239303a55a 100644 --- a/packages/client/src/components/form/switch.vue +++ b/packages/client/src/components/form/switch.vue @@ -18,7 +18,7 @@ </span> <span class="label"> <span><slot></slot></span> - <p><slot name="caption"></slot></p> + <p class="caption"><slot name="caption"></slot></p> </span> </div> </template> @@ -118,10 +118,14 @@ export default defineComponent({ transition: inherit; } - > p { - margin: 0; + > .caption { + margin: 8px 0 0 0; color: var(--fgTransparentWeak); - font-size: 90%; + font-size: 0.85em; + + &:empty { + display: none; + } } } diff --git a/packages/client/src/components/form/textarea.vue b/packages/client/src/components/form/textarea.vue index f3a2c394f1..98fd0da94b 100644 --- a/packages/client/src/components/form/textarea.vue +++ b/packages/client/src/components/form/textarea.vue @@ -4,6 +4,7 @@ <div class="input" :class="{ disabled, focused, tall, pre }"> <textarea ref="inputEl" v-model="v" + v-panel :class="{ code, _monospace: code }" :disabled="disabled" :required="required" @@ -20,7 +21,7 @@ </div> <div class="caption"><slot name="caption"></slot></div> - <MkButton v-if="manualSave && changed" primary @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> </div> </template> @@ -174,7 +175,7 @@ export default defineComponent({ .adhpbeos { > .label { font-size: 0.85em; - padding: 0 0 8px 12px; + padding: 0 0 8px 0; user-select: none; &:empty { @@ -183,8 +184,8 @@ export default defineComponent({ } > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; + font-size: 0.85em; + padding: 8px 0 0 0; color: var(--fgTransparentWeak); &:empty { @@ -209,8 +210,7 @@ export default defineComponent({ font-weight: normal; font-size: 1em; color: var(--fg); - background: var(--panel); - border: solid 0.5px var(--inputBorder); + border: solid 0.5px var(--panel); border-radius: 6px; outline: none; box-shadow: none; @@ -248,5 +248,9 @@ export default defineComponent({ } } } + + > .save { + margin: 8px 0 0 0; + } } </style> |