feat: improve editor

zio/stable
Anthony Fu 2022-11-30 12:50:29 +08:00
parent b784264d96
commit ccf6a17f72
12 changed files with 68 additions and 24 deletions

View File

@ -9,6 +9,7 @@ const {
draftKey,
initial = getDefaultDraft() as never /* Bug of vue-core */,
expanded: _expanded = false,
placeholder,
} = defineProps<{
draftKey: string
initial?: () => Draft
@ -18,6 +19,7 @@ const {
expanded?: boolean
}>()
const { t } = useI18n()
// eslint-disable-next-line prefer-const
let { draft, isEmpty } = $(useDraft(draftKey, initial))
@ -30,7 +32,7 @@ const { editor } = useTiptap({
get: () => draft.params.status,
set: newVal => draft.params.status = newVal,
}),
placeholder: computed(() => draft.placeholder),
placeholder: computed(() => placeholder || draft.params.inReplyToId ? t('placeholder.replying') : t('placeholder.default_1')),
autofocus: shouldExpanded,
onSubmit: publish,
onFocus() { isExpanded = true },

View File

@ -20,7 +20,7 @@ const selectedLanguage = computed({
<template>
<NodeViewWrapper>
<div relative my2>
<div relative my2 class="code-block">
<select v-model="selectedLanguage" contenteditable="false" absolute top-1 right-1 rounded px2 op0 hover:op100 focus:op100 transition>
<option :value="null">
plain

View File

@ -125,12 +125,26 @@ function treeToText(input: Node): string {
pre = '\n'
if (input.nodeName === 'code') {
if (input.parentNode?.nodeName === 'pre') {
const clz = input.attrs.find(attr => attr.name === 'class')
const lang = clz?.value.replace('language-', '')
pre = `\`\`\`${lang || ''}\n`
post = '\n```'
}
else {
pre = '`'
post = '`'
}
}
else if (input.nodeName === 'b' || input.nodeName === 'strong') {
pre = '**'
post = '**'
}
else if (input.nodeName === 'i' || input.nodeName === 'em') {
pre = '*'
post = '*'
}
if ('childNodes' in input)
body = input.childNodes.map(n => treeToText(n)).join('')

View File

@ -8,7 +8,6 @@ export interface Draft {
status?: Exclude<CreateStatusParams['status'], null>
}
attachments: Attachment[]
placeholder: string
}
export type DraftMap = Record<string, Draft>
@ -24,14 +23,13 @@ export const currentUserDrafts = computed(() => {
})
export function getDefaultDraft(options: Partial<Draft['params'] & Omit<Draft, 'params'>> = {}): Draft {
const { t } = useI18n()
const {
status = '',
inReplyToId,
visibility = 'public',
placeholder = t('placeholder.default_1'),
attachments = [],
} = options
return {
params: {
status,
@ -39,7 +37,6 @@ export function getDefaultDraft(options: Partial<Draft['params'] & Omit<Draft, '
visibility,
},
attachments,
placeholder,
}
}
@ -53,12 +50,10 @@ export function getDraftFromStatus(status: Status, text?: null | string): Draft
}
export function getReplyDraft(status: Status) {
const { t } = useI18n()
return {
key: `reply-${status.id}`,
draft: () => getDefaultDraft({
inReplyToId: status!.id,
placeholder: t('placeholder.reply_to_account', [status?.account ? getDisplayName(status.account) : t('placeholder.the_thread')]),
visibility: status.visibility,
}),
}
@ -76,7 +71,7 @@ export const isEmptyDraft = (draft: Draft | null | undefined) => {
export function useDraft(
draftKey: string,
initial: () => Draft = () => getDefaultDraft(),
initial: () => Draft = () => getDefaultDraft({}),
) {
const draft = computed({
get() {

View File

@ -98,6 +98,7 @@
"placeholder": {
"default_1": "What is on your mind?",
"reply_to_account": "Reply to {0}",
"replying": "Replying",
"the_thread": "the thread"
},
"state": {

View File

@ -67,9 +67,15 @@ body {
p {
--at-apply: my-2;
}
code {
--at-apply: bg-code text-code px1 py0.5 rounded text-sm;
}
pre code {
--at-apply: text-base bg-transparent px0 py0 rounded-none;
}
.code-block {
--at-apply: bg-code text-0.9rem p3 mt-2 rounded overflow-auto leading-1.6em;
--at-apply: font-mono bg-code text-base p3 mt-2 rounded overflow-auto leading-1.6em;
.shiki {
background: transparent !important;
@ -78,15 +84,7 @@ body {
}
.content-editor {
--at-apply: 'outline-none flex-1';
pre {
--at-apply: font-mono bg-code rounded px3 py2;
code {
--at-apply: bg-transparent text-0.8rem p0;
}
}
--at-apply: outline-none flex-1;
}
.content-editor.content-rich {

View File

@ -2,13 +2,17 @@
--c-primary: #EA9E44;
--c-primary-active: #C16929;
--c-border: #88888820;
--c-bg-base: #fff;
--c-bg-active: #f6f6f6;
--c-bg-code: #00000006;
--c-bg-selection: #8885;
--c-text-base: #232323;
--c-text-code: #7ca72f;
--c-text-secondary: #686868;
--c-text-secondary-light: #919191;
--c-bg-btn-disabled: #a1a1a1;
--c-text-btn-disabled: #fff;
--c-text-btn: #232323;
@ -18,9 +22,12 @@
--c-bg-base: #111;
--c-bg-active: #191919;
--c-bg-code: #ffffff06;
--c-text-base: #fff;
--c-text-code: #90be3d;
--c-text-secondary: #888;
--c-text-secondary-light: #686868;
--c-bg-btn-disabled: #2a2a2a;
--c-text-btn-disabled: #919191;
}

View File

@ -22,7 +22,7 @@ exports[`content-rich > custom emoji 1`] = `
"Daniel Roe
<img
src=\\"https://media.mas.to/masto-public/cache/custom_emojis/images/000/288/667/original/c96ba3cb0e0e1eac.png\\"
alt=\\"nuxt\\"
alt=\\":nuxt:\\"
class=\\"custom-emoji\\"
/>
"

View File

@ -0,0 +1,24 @@
import { describe, expect, it } from 'vitest'
import { htmlToText } from '../composables/content'
describe('html-to-text', () => {
it('inline code', () => {
expect(htmlToText('<p>text <code>code</code> inline</p>'))
.toMatchInlineSnapshot('"text `code` inline"')
})
it('code block', () => {
expect(htmlToText('<p>text </p><pre><code class="language-js">code</code></pre>'))
.toMatchInlineSnapshot(`
"text
\`\`\`js
code
\`\`\`"
`)
})
it('bold & italic', () => {
expect(htmlToText('<p>text <b>bold</b> <em>italic</em></p>'))
.toMatchInlineSnapshot('"text **bold** *italic*"')
})
})

View File

@ -29,3 +29,5 @@ export interface GroupedNotifications {
type: string
items: Notification[]
}
export type TranslateFn = ReturnType<typeof useI18n>['t']

View File

@ -22,6 +22,7 @@ export default defineConfig({
// text colors
'text-base': 'text-$c-text-base',
'text-code': 'text-$c-text-code',
'text-inverted': 'text-$c-bg-base',
'text-secondary': 'text-$c-text-secondary',
'text-secondary-light': 'text-$c-text-secondary-light',