feat: basic integration with TipTap (#87)

This commit is contained in:
Anthony Fu 2022-11-25 21:21:02 +08:00 committed by GitHub
parent 019a36c9bb
commit c2810fd5eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 722 additions and 31 deletions

View file

@ -14,6 +14,6 @@ const emojiObject = emojisArrayToObject(props.emojis || [])
export default () => h(
'div',
{ class: 'rich-content' },
{ class: 'content-rich' },
contentToVNode(props.content, emojiObject),
)

View file

@ -13,6 +13,6 @@ import { isPreviewHelpOpen, isPublishDialogOpen, isSigninDialogOpen, isUserSwitc
<HelpPreview />
</ModalDialog>
<ModalDialog v-model="isPublishDialogOpen">
<PublishWidget draft-key="dialog" expanded min-w-180 p6 />
<PublishWidget draft-key="dialog" expanded min-w-180 />
</ModalDialog>
</template>

View file

@ -2,6 +2,7 @@
import type { CreateStatusParams, StatusVisibility } from 'masto'
import { fileOpen } from 'browser-fs-access'
import { useDropZone } from '@vueuse/core'
import { EditorContent } from '@tiptap/vue-3'
const {
draftKey,
@ -15,10 +16,19 @@ const {
expanded?: boolean
}>()
const expanded = $ref(_expanded)
let isExpanded = $ref(_expanded)
let isSending = $ref(false)
let { draft } = $(useDraft(draftKey, inReplyToId))
const { editor } = useTiptap({
content: toRef(draft.params, 'status'),
placeholder,
autofocus: isExpanded,
onSubimit: publish,
onFocus() { isExpanded = true },
onPaste: handlePaste,
})
const status = $computed(() => {
return {
...draft.params,
@ -87,11 +97,16 @@ function chooseVisibility(visibility: StatusVisibility) {
}
async function publish() {
if (process.dev) {
alert(JSON.stringify(draft.params, null, 2))
return
}
try {
isSending = true
if (!draft.editingStatus)
await masto.statuses.create(status)
else await masto.statuses.update(draft.editingStatus.id, status)
else
await masto.statuses.update(draft.editingStatus.id, status)
draft = getDefaultDraft({ inReplyToId })
isPublishDialogOpen.value = false
@ -111,6 +126,7 @@ async function onDrop(files: File[] | null) {
const { isOverDropZone } = useDropZone(dropZoneRef, onDrop)
onUnmounted(() => {
// Remove draft if it's empty
if (!draft.attachments.length && !draft.params.status) {
nextTick(() => {
delete currentUserDrafts.value[draftKey]
@ -138,7 +154,7 @@ onUnmounted(() => {
<div
ref="dropZoneRef"
flex flex-col gap-3 flex-1
border="2 dashed transparent" p-1
border="2 dashed transparent"
:class="[isSending ? 'pointer-events-none' : '', isOverDropZone ? '!border-primary' : '']"
>
<div v-if="draft.params.sensitive">
@ -151,22 +167,17 @@ onUnmounted(() => {
>
</div>
<textarea
v-model="draft.params.status"
:placeholder="placeholder"
h-80px
:class="expanded ? '!h-200px' : ''"
p2 border-rounded w-full bg-transparent
transition="height"
outline-none border="~ base"
@paste="handlePaste"
@focus="expanded = true"
@keydown.esc="expanded = false"
@keydown.ctrl.enter="publish"
@keydown.meta.enter="publish"
<EditorContent
:editor="editor"
:class="isExpanded ? 'min-h-120px' : ''"
/>
<div flex="~ col gap-2" max-h-50vh overflow-auto>
<div v-if="isUploading" flex gap-1 items-center text-sm p1 text-primary>
<div i-ri:loader-2-fill animate-spin />
Uploading...
</div>
<div v-if="draft.attachments.length" flex="~ col gap-2" overflow-auto>
<PublishAttachment
v-for="(att, idx) in draft.attachments" :key="att.id"
:attachment="att"
@ -174,18 +185,28 @@ onUnmounted(() => {
/>
</div>
<div v-if="isUploading" flex gap-2 justify-end items-center>
<div op50 i-ri:loader-2-fill animate-spin text-2xl />
Uploading...
</div>
<div flex="~ gap-2">
<div
v-if="isExpanded" flex="~ gap-2" m="l--1" pt-2
border="t base"
>
<CommonTooltip placement="bottom" content="Add images, a video or an audio file">
<button btn-action-icon @click="pickAttachments">
<div i-ri:upload-line />
<div i-ri:image-add-line />
</button>
</CommonTooltip>
<template v-if="editor">
<CommonTooltip placement="bottom" content="Toggle code block">
<button
btn-action-icon
:class="editor.isActive('codeBlock') ? 'op100' : 'op50'"
@click="editor?.chain().focus().toggleCodeBlock().run()"
>
<div i-ri:code-s-slash-line />
</button>
</CommonTooltip>
</template>
<div flex-auto />
<CommonTooltip placement="bottom" content="Add content warning">

View file

@ -0,0 +1,58 @@
<script setup lang="ts">
const { items, command } = defineProps<{
items: any[]
command: Function
}>()
let selectedIndex = $ref(0)
watch(items, () => {
selectedIndex = 0
})
function onKeyDown(event: KeyboardEvent) {
if (event.key === 'ArrowUp') {
selectedIndex = ((selectedIndex + items.length) - 1) % items.length
return true
}
else if (event.key === 'ArrowDown') {
selectedIndex = (selectedIndex + 1) % items.length
return true
}
else if (event.key === 'Enter') {
selectItem(selectedIndex)
return true
}
return false
}
function selectItem(index: number) {
const item = items[index]
if (item)
command({ id: item })
}
defineExpose({
onKeyDown,
})
</script>
<template>
<div relative bg-base text-base shadow border="~ base rounded" text-sm>
<template v-if="items.length">
<button
v-for="(item, index) in items"
:key="index"
:class="index === selectedIndex ? 'bg-active' : 'op50'"
block m0 w-full text-left px2 py1
@click="selectItem(index)"
>
{{ item }}asd
</button>
</template>
<div v-else block m0 w-full text-left px2 py1 italic op30>
No result
</div>
</div>
</template>