diff options
Diffstat (limited to 'packages/frontend/src/components/MkTextarea.vue')
| -rw-r--r-- | packages/frontend/src/components/MkTextarea.vue | 41 |
1 files changed, 39 insertions, 2 deletions
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue index c35274959e..5c70adde11 100644 --- a/packages/frontend/src/components/MkTextarea.vue +++ b/packages/frontend/src/components/MkTextarea.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only :readonly="readonly" :placeholder="placeholder" :pattern="pattern" - :autocomplete="autocomplete" + :autocomplete="props.autocomplete" :spellcheck="spellcheck" @focus="focused = true" @blur="focused = false" @@ -26,16 +26,21 @@ SPDX-License-Identifier: AGPL-3.0-only ></textarea> </div> <div :class="$style.caption"><slot name="caption"></slot></div> + <button v-if="mfmPreview" style="font-size: 0.85em;" class="_textButton" type="button" @click="preview = !preview">{{ i18n.ts.preview }}</button> + <div v-if="mfmPreview" v-show="preview" v-panel :class="$style.mfmPreview"> + <Mfm :text="v"/> + </div> <MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton> </div> </template> <script lang="ts" setup> -import { onMounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue'; +import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue'; import { debounce } from 'throttle-debounce'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; +import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js'; const props = defineProps<{ modelValue: string | null; @@ -46,6 +51,8 @@ const props = defineProps<{ placeholder?: string; autofocus?: boolean; autocomplete?: string; + mfmAutocomplete?: boolean | SuggestionType[], + mfmPreview?: boolean; spellcheck?: boolean; debounce?: boolean; manualSave?: boolean; @@ -68,6 +75,8 @@ const changed = ref(false); const invalid = ref(false); const filled = computed(() => v.value !== '' && v.value != null); const inputEl = shallowRef<HTMLTextAreaElement>(); +const preview = ref(false); +let autocomplete: Autocomplete; const focus = () => inputEl.value.focus(); const onInput = (ev) => { @@ -82,6 +91,16 @@ const onKeydown = (ev: KeyboardEvent) => { if (ev.code === 'Enter') { emit('enter'); } + + if (props.code && ev.key === 'Tab') { + const pos = inputEl.value?.selectionStart ?? 0; + const posEnd = inputEl.value?.selectionEnd ?? v.value.length; + v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd); + nextTick(() => { + inputEl.value?.setSelectionRange(pos + 1, pos + 1); + }); + ev.preventDefault(); + } }; const updated = () => { @@ -113,6 +132,16 @@ onMounted(() => { focus(); } }); + + if (props.mfmAutocomplete) { + autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete); + } +}); + +onUnmounted(() => { + if (autocomplete) { + autocomplete.detach(); + } }); </script> @@ -194,4 +223,12 @@ onMounted(() => { .save { margin: 8px 0 0 0; } + +.mfmPreview { + padding: 12px; + border-radius: var(--radius); + box-sizing: border-box; + min-height: 130px; + pointer-events: none; +} </style> |