diff options
Diffstat (limited to 'src/client/components')
| -rw-r--r-- | src/client/components/form/input.vue | 120 | ||||
| -rw-r--r-- | src/client/components/form/textarea.vue | 101 |
2 files changed, 118 insertions, 103 deletions
diff --git a/src/client/components/form/input.vue b/src/client/components/form/input.vue index c8c22e95c7..f0aa6b0534 100644 --- a/src/client/components/form/input.vue +++ b/src/client/components/form/input.vue @@ -1,63 +1,50 @@ <template> -<div class="ztzhwixg _formItem" :class="{ inline, disabled }"> - <div class="_formLabel"><slot></slot></div> - <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 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" - :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="_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> - <button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button> - <div class="_formCaption"><slot name="desc"></slot></div> -</div> + <template #caption><slot name="desc"></slot></template> + + <FormButton v-if="manualSave && changed" @click="updated" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton> +</FormGroup> </template> <script lang="ts"> import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import debounce from 'v-debounce'; -import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; +import { faExclamationCircle, faSave } from '@fortawesome/free-solid-svg-icons'; import './form.scss'; +import FormButton from './button.vue'; +import FormGroup from './group.vue'; export default defineComponent({ - directives: { - debounce + components: { + FormGroup, + FormButton, }, props: { value: { @@ -101,9 +88,6 @@ export default defineComponent({ step: { required: false }, - debounce: { - required: false - }, datalist: { type: Array, required: false, @@ -113,9 +97,10 @@ export default defineComponent({ required: false, default: false }, - save: { - type: Function, + manualSave: { + type: Boolean, required: false, + default: false }, }, emits: ['change', 'keydown', 'enter'], @@ -144,15 +129,22 @@ export default defineComponent({ } }; + const updated = () => { + changed.value = false; + if (type?.value === 'number') { + context.emit('update:value', parseFloat(v.value)); + } else { + context.emit('update:value', v.value); + } + }; + watch(value, 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) { + updated(); } invalid.value = inputEl.value.validity.badInput; @@ -198,7 +190,8 @@ export default defineComponent({ focus, onInput, onKeydown, - faExclamationCircle, + updated, + faExclamationCircle, faSave, }; }, }); @@ -285,11 +278,6 @@ export default defineComponent({ } } - > .save { - margin: 6px 0 0 0; - font-size: 0.8em; - } - &.inline { display: inline-block; margin: 0; diff --git a/src/client/components/form/textarea.vue b/src/client/components/form/textarea.vue index 711cd50124..135e16c259 100644 --- a/src/client/components/form/textarea.vue +++ b/src/client/components/form/textarea.vue @@ -1,29 +1,39 @@ <template> -<div class="rivhosbp _formItem" :class="{ tall, pre }"> - <div class="_formLabel"><slot></slot></div> - <div class="input _formPanel"> - <textarea ref="input" :class="{ code, _monospace: code }" - :value="value" - :required="required" - :readonly="readonly" - :pattern="pattern" - :autocomplete="autocomplete" - :spellcheck="!code" - @input="onInput" - @focus="focused = true" - @blur="focused = false" - ></textarea> +<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> - <button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button> - <div class="_formCaption"><slot name="desc"></slot></div> -</div> + <template #caption><slot name="desc"></slot></template> + + <FormButton v-if="manualSave && changed" @click="updated" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton> +</FormGroup> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, ref, toRefs, watch } from 'vue'; +import { faSave } from '@fortawesome/free-solid-svg-icons'; import './form.scss'; +import FormButton from './button.vue'; +import FormGroup from './group.vue'; export default defineComponent({ + components: { + FormGroup, + FormButton, + }, props: { value: { required: false @@ -58,24 +68,46 @@ export default defineComponent({ required: false, default: false }, - save: { - type: Function, + manualSave: { + type: Boolean, required: false, + default: false }, }, - data() { + setup(props, context) { + const { value } = toRefs(props); + const v = ref(value.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:value', v.value); + }; + + watch(value, newValue => { + v.value = newValue; + }); + + watch(v, newValue => { + if (!props.manualSave) { + updated(); + } + }); + return { - changed: false, - } - }, - methods: { - focus() { - this.$refs.input.focus(); - }, - onInput(ev) { - this.changed = true; - this.$emit('update:value', ev.target.value); - } + v, + updated, + changed, + focus, + onInput, + faSave, + }; } }); </script> @@ -112,11 +144,6 @@ export default defineComponent({ } } - > .save { - margin: 6px 0 0 0; - font-size: 0.8em; - } - &.tall { > .input { > textarea { |