summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components
diff options
context:
space:
mode:
author1STEP621 <86859447+1STEP621@users.noreply.github.com>2023-12-14 13:11:23 +0900
committerGitHub <noreply@github.com>2023-12-14 13:11:23 +0900
commitb33fe530476f89282e1e554aecf0cfe82e6d6edd (patch)
treefcf410283db79cdf3883a43543887a5836ed6f2f /packages/frontend/src/components
parentrefactor(frontend) $i の型情報にtokenを追加 (#12649) (diff)
downloadsharkey-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')
-rw-r--r--packages/frontend/src/components/MkFormDialog.vue4
-rw-r--r--packages/frontend/src/components/MkInput.vue15
-rw-r--r--packages/frontend/src/components/MkTextarea.vue29
-rw-r--r--packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts2
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;