summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkTextarea.vue
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-01-07 15:09:46 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2023-01-07 15:09:46 +0900
commit58bfb4dca4a0b0f0d8468667f5a6b0f0bdc374f5 (patch)
treece743174bfa62d80b77d29e0c473c577af13bead /packages/frontend/src/components/MkTextarea.vue
parentfix typo (diff)
downloadmisskey-58bfb4dca4a0b0f0d8468667f5a6b0f0bdc374f5.tar.gz
misskey-58bfb4dca4a0b0f0d8468667f5a6b0f0bdc374f5.tar.bz2
misskey-58bfb4dca4a0b0f0d8468667f5a6b0f0bdc374f5.zip
refactor
Diffstat (limited to 'packages/frontend/src/components/MkTextarea.vue')
-rw-r--r--packages/frontend/src/components/MkTextarea.vue260
1 files changed, 260 insertions, 0 deletions
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
new file mode 100644
index 0000000000..d34d7b1775
--- /dev/null
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -0,0 +1,260 @@
+<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"
+ v-model="v"
+ v-adaptive-border
+ :class="{ code, _monospace: code }"
+ :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" primary class="save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
+import { debounce } from 'throttle-debounce';
+import MkButton from '@/components/MkButton.vue';
+import { i18n } from '@/i18n';
+
+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,
+ i18n,
+ };
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.adhpbeos {
+ > .label {
+ font-size: 0.85em;
+ padding: 0 0 8px 0;
+ user-select: none;
+
+ &:empty {
+ display: none;
+ }
+ }
+
+ > .caption {
+ font-size: 0.85em;
+ padding: 8px 0 0 0;
+ 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 1px var(--panel);
+ border-radius: 6px;
+ outline: none;
+ box-shadow: none;
+ box-sizing: border-box;
+ transition: border-color 0.1s ease-out;
+
+ &:hover {
+ border-color: var(--inputBorderHover) !important;
+ }
+ }
+
+ &.focused {
+ > textarea {
+ border-color: var(--accent) !important;
+ }
+ }
+
+ &.disabled {
+ opacity: 0.7;
+
+ &, * {
+ cursor: not-allowed !important;
+ }
+ }
+
+ &.tall {
+ > textarea {
+ min-height: 200px;
+ }
+ }
+
+ &.pre {
+ > textarea {
+ white-space: pre;
+ }
+ }
+ }
+
+ > .save {
+ margin: 8px 0 0 0;
+ }
+}
+</style>