feat(editor): link insertion full UI and flow

This commit is contained in:
Florian Sylvain committed 2026-10-12 17:12:36 +02:00
1 parent 03b39e64f2
commit 6f9ae6207a
16 files changed
+2865 -14

No files matched your search

+46 -5
View File
@@ -11,6 +11,7 @@ import ArticleEditor from '@/components/ArticleEditor.vue'
import ArticleDetails from '@/components/ArticleDetails.vue'
import SaveStatus from '@/components/SaveStatus.vue'
import PublishControl from '@/components/PublishControl.vue'
import { neutralizeUnsafeLinks } from '@/lib/articleLinkUrl'
import EditorStatusBar from '@/components/EditorStatusBar.vue'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import ImageUploader from '@/components/ImageUploader.vue'
@@ -90,6 +91,25 @@ async function loadArticle() {
}
const counts = computed(() => bodyCounts(draft.body.value))
const previewBody = computed(() => neutralizeUnsafeLinks(draft.body.value))
const countText = computed(() =>
t('{0} words · {1} characters', [
counts.value.words.toLocaleString('en-US'),
counts.value.characters.toLocaleString('en-US'),
]),
)
const overflowItems = computed(() => [
[
{
icon: 'i-lucide-trash-2',
color: 'error' as const,
label: t('Delete article'),
onSelect: () => {
handleDelete()
},
},
],
])
const saveEnabled = computed(() => draft.state.value === 'dirty' && loadState.value === 'loaded')
@@ -403,7 +423,24 @@ onBeforeRouteLeave(async () => {
</script>
<template>
<DashboardShell :mobile-title="t('Edit article')" full-bleed>
<DashboardShell
:mobile-title="t('Edit article')"
back-to="/article"
:back-label="t('Back to Articles')"
full-bleed
>
<template v-if="loadState === 'loaded'" #navbar-right>
<UDropdownMenu :items="overflowItems">
<UButton
icon="i-lucide-ellipsis"
color="neutral"
variant="ghost"
class="size-11 justify-center lg:hidden"
:ui="{ leadingIcon: 'size-5' }"
:aria-label="t('More actions')"
/>
</UDropdownMenu>
</template>
<template v-if="loadState === 'loaded'" #breadcrumb>
<UBreadcrumb
:items="[
@@ -467,17 +504,19 @@ onBeforeRouteLeave(async () => {
<div v-else-if="loadState === 'loaded' && article" class="flex min-h-0 flex-1 flex-col">
<div
class="flex flex-shrink-0 flex-wrap items-center gap-3 border-b border-default bg-default px-6 py-3.5"
class="flex flex-shrink-0 flex-wrap items-center gap-x-3 gap-y-2 border-b border-default bg-default px-4 py-3 lg:gap-3 lg:px-6 lg:py-3.5"
>
<UButton
icon="i-lucide-arrow-left"
color="neutral"
variant="ghost"
class="size-8 justify-center text-toned"
class="hidden size-8 justify-center text-toned lg:inline-flex"
:aria-label="t('Back to Articles')"
@click="goBack"
/>
<div class="flex min-w-50 flex-1 flex-wrap items-center gap-2.5">
<div
class="flex min-w-0 flex-1 basis-full flex-wrap items-center gap-2.5 lg:min-w-50 lg:basis-50"
>
<h1
class="text-[1.125rem]/6.5 font-semibold text-highlighted"
:title="t('The title can\'t be changed after creation.')"
@@ -491,6 +530,7 @@ onBeforeRouteLeave(async () => {
:saved-at="draft.savedAt.value"
@retry="retrySave"
/>
<span class="ml-auto font-mono text-xs text-muted lg:hidden">{{ countText }}</span>
<div class="hidden items-center gap-3 lg:flex">
<USwitch
v-model="previewOn"
@@ -545,7 +585,7 @@ onBeforeRouteLeave(async () => {
<h2 class="text-2xl font-semibold text-highlighted">
{{ article.title }}
</h2>
<div v-html="draft.body.value" />
<div v-html="previewBody" />
</div>
</div>
<div
@@ -566,6 +606,7 @@ onBeforeRouteLeave(async () => {
>
<template #footer>
<EditorStatusBar
class="max-lg:hidden"
:words="counts.words"
:characters="counts.characters"
:state="draft.state.value"