diff options
| author | 1STEP621 <86859447+1STEP621@users.noreply.github.com> | 2023-12-14 13:11:23 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-14 13:11:23 +0900 |
| commit | b33fe530476f89282e1e554aecf0cfe82e6d6edd (patch) | |
| tree | fcf410283db79cdf3883a43543887a5836ed6f2f /packages/frontend/src/components | |
| parent | refactor(frontend) $i の型情報にtokenを追加 (#12649) (diff) | |
| download | sharkey-b33fe530476f89282e1e554aecf0cfe82e6d6edd.tar.gz sharkey-b33fe530476f89282e1e554aecf0cfe82e6d6edd.tar.bz2 sharkey-b33fe530476f89282e1e554aecf0cfe82e6d6edd.zip | |
Enhance(frontend): MFMや絵文字が使える入力ボックスでオートコンプリートを使えるように (#12643)
* rich autocomplete for use in profiles, announcements, and channel descriptions
* implementation omissions
* add tab, apply to page editor, and fix something
* componentization
* fix nyaize doesn't working in profile preview
* detach autocomplete instance when unmounted
* fix: mismatched camelCase
* remove unused / unnecessary styles
* update CHANGELOG.md
* fix lint
* remove dump.rdb
* props.richAutocomplete -> autocomplete
* Update packages/frontend/src/scripts/autocomplete.ts
* clarify namings
メンションなども「MFM」に含まれるのか自信がなかったのでrichSyntaxなどとぼかしていましたが、含むようなので変更しました
* tweak
* Update MkFormDialog.vue
* rename
---------
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Diffstat (limited to 'packages/frontend/src/components')
4 files changed, 45 insertions, 5 deletions
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 24404728ca..6f882cfab7 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -26,11 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </MkInput> - <MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text"> + <MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" :mfmAutocomplete="form[item].treatAsMfm"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </MkInput> - <MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]"> + <MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" :mfmAutocomplete="form[item].treatAsMfm" :mfmPreview="form[item].treatAsMfm"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </MkTextarea> diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index 72babfac76..ae797eb7d2 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -43,11 +43,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; +import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; import { debounce } from 'throttle-debounce'; import MkButton from '@/components/MkButton.vue'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; +import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js'; const props = defineProps<{ modelValue: string | number | null; @@ -59,6 +60,7 @@ const props = defineProps<{ placeholder?: string; autofocus?: boolean; autocomplete?: string; + mfmAutocomplete?: boolean | SuggestionType[], autocapitalize?: string; spellcheck?: boolean; step?: any; @@ -93,6 +95,7 @@ const height = props.small ? 33 : props.large ? 39 : 36; +let autocomplete: Autocomplete; const focus = () => inputEl.value.focus(); const onInput = (ev: KeyboardEvent) => { @@ -160,6 +163,16 @@ onMounted(() => { focus(); } }); + + if (props.mfmAutocomplete) { + autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete); + } +}); + +onUnmounted(() => { + if (autocomplete) { + autocomplete.detach(); + } }); defineExpose({ diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue index 7c1ddcbbed..23fdd5bfe1 100644 --- a/packages/frontend/src/components/MkTextarea.vue +++ b/packages/frontend/src/components/MkTextarea.vue @@ -26,16 +26,21 @@ SPDX-License-Identifier: AGPL-3.0-only ></textarea> </div> <div :class="$style.caption"><slot name="caption"></slot></div> + <button style="font-size: 0.85em;" class="_textButton" type="button" @click="preview = !preview">{{ i18n.ts.preview }}</button> + <div 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="ti ti-device-floppy"></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) => { @@ -113,6 +122,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 +213,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> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index fe599dcead..28293b287c 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -37,7 +37,7 @@ type MfmProps = { isNote?: boolean; emojiUrls?: string[]; rootScale?: number; - nyaize: boolean | 'respect'; + nyaize?: boolean | 'respect'; parsedNodes?: mfm.MfmNode[] | null; enableEmojiMenu?: boolean; enableEmojiMenuReaction?: boolean; |