feat: Add the new design for edit article page (#10285)

This commit is contained in:
Sivin Varghese
2024-10-16 23:20:44 +05:30
committed by GitHub
parent 6a383dc3ca
commit 902a9aa7d7
4 changed files with 208 additions and 1 deletions

View File

@@ -23,6 +23,10 @@ defineProps({
type: Number,
default: 25,
},
showHeaderTitle: {
type: Boolean,
default: true,
},
showPaginationFooter: {
type: Boolean,
default: true,
@@ -49,7 +53,10 @@ const togglePortalSwitcher = () => {
class="sticky top-0 z-10 px-6 pb-3 bg-white lg:px-0 dark:bg-slate-900"
>
<div class="w-full max-w-[900px] mx-auto">
<div class="flex items-center justify-start h-20 gap-2">
<div
v-if="showHeaderTitle"
class="flex items-center justify-start h-20 gap-2"
>
<span class="text-xl font-medium text-slate-900 dark:text-white">
{{ header }}
</span>

View File

@@ -0,0 +1,146 @@
<script setup>
import { computed } from 'vue';
import { debounce } from '@chatwoot/utils';
import { ARTICLE_EDITOR_MENU_OPTIONS } from 'dashboard/constants/editor';
import HelpCenterLayout from 'dashboard/components-next/HelpCenter/HelpCenterLayout.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import TextArea from 'dashboard/components-next/textarea/TextArea.vue';
import FullEditor from 'dashboard/components/widgets/WootWriter/FullEditor.vue';
const { article } = defineProps({
article: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(['saveArticle']);
const saveArticle = debounce(value => emit('saveArticle', value), 400, false);
const articleTitle = computed({
get: () => article.title,
set: title => {
saveArticle({ title });
},
});
const articleContent = computed({
get: () => article.content,
set: content => {
saveArticle({ content });
},
});
</script>
<!-- eslint-disable vue/no-bare-strings-in-template -->
<template>
<HelpCenterLayout :show-header-title="false" :show-pagination-footer="false">
<template #header-actions>
<div class="flex items-center justify-between h-20">
<Button
label="Back to articles"
icon="chevron-lucide-left"
icon-lib="lucide"
variant="link"
text-variant="info"
size="sm"
/>
<div class="flex items-center gap-4">
<span class="text-xs font-medium text-slate-500 dark:text-slate-400">
Saved
</span>
<div class="flex items-center gap-2">
<Button label="Preview" variant="secondary" size="sm" />
<Button
label="Publish"
icon="chevron-lucide-down"
icon-position="right"
icon-lib="lucide"
size="sm"
/>
</div>
</div>
</div>
</template>
<template #content>
<div class="flex flex-col gap-3 pl-4 mb-3 rtl:pr-3 rtl:pl-0">
<TextArea
v-model="articleTitle"
class="h-12"
custom-text-area-class="border-0 !text-[32px] !bg-transparent !py-0 !px-0 !h-auto !leading-[48px] !font-medium !tracking-[0.2px]"
placeholder="Title"
/>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<div class="w-5 h-5 rounded-full bg-slate-100 dark:bg-slate-700" />
<span class="text-sm text-slate-500 dark:text-slate-400">
John Doe
</span>
</div>
<div class="w-px h-3 bg-slate-50 dark:bg-slate-800" />
<Button
label="Uncategorized"
icon="play-shape"
variant="ghost"
class="!px-2 font-normal"
text-variant="info"
/>
<div class="w-px h-3 bg-slate-50 dark:bg-slate-800" />
<Button
label="More properties"
icon="add"
variant="ghost"
class="!px-2 font-normal"
/>
</div>
</div>
<FullEditor
v-model="articleContent"
class="py-0 pb-10 pl-4 rtl:pr-4 rtl:pl-0 h-fit"
placeholder="Write something"
:enabled-menu-options="ARTICLE_EDITOR_MENU_OPTIONS"
/>
</template>
</HelpCenterLayout>
</template>
<style lang="scss" scoped>
::v-deep {
.ProseMirror .empty-node::before {
@apply text-slate-200 dark:text-slate-500 text-base;
}
.ProseMirror-menubar-wrapper {
.ProseMirror-woot-style {
@apply min-h-[15rem] max-h-full;
}
}
.ProseMirror-menubar {
display: none; // Hide by default
}
.editor-root .has-selection {
.ProseMirror-menubar {
@apply h-8 rounded-lg !px-2 z-50 bg-slate-50 dark:bg-slate-800 items-center gap-4 ml-0 mb-0 shadow-md border border-slate-75 dark:border-slate-700/50;
display: flex;
top: var(--selection-top, auto) !important;
left: var(--selection-left, 0) !important;
width: fit-content !important;
position: absolute !important;
.ProseMirror-menuitem {
@apply mr-0;
.ProseMirror-icon {
@apply p-0 mt-1 !mr-0;
svg {
width: 20px !important;
height: 20px !important;
}
}
}
}
}
}
</style>

View File

@@ -59,6 +59,7 @@ export default {
data() {
return {
plugins: [imagePastePlugin(this.handleImageUpload)],
isTextSelected: false, // Tracks text selection and prevents unnecessary re-renders on mouse selection
};
},
watch: {
@@ -181,6 +182,7 @@ export default {
if (tx.docChanged) {
this.emitOnChange();
}
this.checkSelection(state);
},
handleDOMEvents: {
keyup: this.onKeyup,
@@ -226,6 +228,56 @@ export default {
onFocus() {
this.$emit('focus');
},
checkSelection(editorState) {
const { from, to } = editorState.selection;
// Check if there's a selection (from and to are different)
const hasSelection = from !== to;
// If the selection state is the same as the previous state, do nothing
if (hasSelection === this.isTextSelected) return;
// Update the selection state
this.isTextSelected = hasSelection;
const { editor } = this.$refs;
// Toggle the 'has-selection' class based on whether there's a selection
editor.classList.toggle('has-selection', hasSelection);
// If there's a selection, update the menubar position
if (hasSelection) this.setMenubarPosition(editorState);
},
setMenubarPosition(editorState) {
if (!editorState.selection) return;
// Get the start and end positions of the selection
const { from, to } = editorState.selection;
const { editor } = this.$refs;
// Get the editor's position relative to the viewport
const { left: editorLeft, top: editorTop } =
editor.getBoundingClientRect();
// Get the editor's width
const editorWidth = editor.offsetWidth;
const menubarWidth = 480; // Menubar width (adjust as needed (px))
// Get the end position of the selection
const { bottom: endBottom, right: endRight } = editorView.coordsAtPos(to);
// Get the start position of the selection
const { left: startLeft } = editorView.coordsAtPos(from);
// Calculate the top position for the menubar (10px below the selection)
const top = endBottom - editorTop + 10;
// Calculate the left position for the menubar
// This centers the menubar on the selection while keeping it within the editor's bounds
const left = Math.max(
0,
Math.min(
(startLeft + endRight) / 2 - editorLeft,
editorWidth - menubarWidth
)
);
// Set the CSS custom properties for positioning the menubar
editor.style.setProperty('--selection-top', `${top}px`);
editor.style.setProperty('--selection-left', `${left}px`);
},
},
};
</script>
@@ -259,6 +311,7 @@ export default {
}
.editor-root {
position: relative;
width: 100%;
}

View File

@@ -287,6 +287,7 @@
],
"scan-person-outline": "M5.25 3.5A1.75 1.75 0 0 0 3.5 5.25v3a.75.75 0 0 1-1.5 0v-3A3.25 3.25 0 0 1 5.25 2h3a.75.75 0 0 1 0 1.5zm0 17a1.75 1.75 0 0 1-1.75-1.75v-3a.75.75 0 0 0-1.5 0v3A3.25 3.25 0 0 0 5.25 22h3a.75.75 0 0 0 .707-1l-.005-.015a.75.75 0 0 0-.702-.485zM20.5 5.25a1.75 1.75 0 0 0-1.75-1.75h-3a.75.75 0 0 1 0-1.5h3A3.25 3.25 0 0 1 22 5.25v3a.75.75 0 0 1-1.5 0zM18.75 20.5a1.75 1.75 0 0 0 1.75-1.75v-3a.75.75 0 0 1 1.5 0v3A3.25 3.25 0 0 1 18.75 22h-3a.75.75 0 0 1 0-1.5zM6.5 18.616q0 .465.258.884H5.25a1 1 0 0 1-.129-.011A3.1 3.1 0 0 1 5 18.616v-.366A2.25 2.25 0 0 1 7.25 16h9.5A2.25 2.25 0 0 1 19 18.25v.366c0 .31-.047.601-.132.875a1 1 0 0 1-.118.009h-1.543a1.56 1.56 0 0 0 .293-.884v-.366a.75.75 0 0 0-.75-.75h-9.5a.75.75 0 0 0-.75.75zm8.25-8.866a2.75 2.75 0 1 0-5.5 0a2.75 2.75 0 0 0 5.5 0m1.5 0a4.25 4.25 0 1 1-8.5 0a4.25 4.25 0 0 1 8.5 0",
"play-shape-outline": "M3.7 11q-.575 0-.862-.488t-.013-.987l3.3-5.95q.275-.5.875-.5t.875.5l3.3 5.95q.275.5-.012.988T10.3 11zM7 21q-1.65 0-2.825-1.175T3 17q0-1.675 1.175-2.838T7 13q1.65 0 2.825 1.175T11 17q0 1.65-1.175 2.825T7 21m0-2q.825 0 1.412-.587T9 17q0-.825-.587-1.412T7 15q-.825 0-1.412.588T5 17q0 .825.588 1.413T7 19M5.4 9h3.2L7 6.125zM14 21q-.425 0-.712-.288T13 20v-6q0-.425.288-.712T14 13h6q.425 0 .713.288T21 14v6q0 .425-.288.713T20 21zm1-2h4v-4h-4zm.6-12.5l-1.9-1.9q-.275-.275-.275-.7t.275-.7q.275-.275.7-.275t.7.275L17 5.1l1.9-1.9q.275-.275.7-.275t.7.275q.275.275.275.7t-.275.7l-1.9 1.9l1.9 1.9q.275.275.275.7t-.275.7q-.275.275-.7.275t-.7-.275L17 7.9l-1.9 1.9q-.275.275-.7.275t-.7-.275q-.275-.275-.275-.7t.275-.7z",
"upload-lucide-outline": "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4m14-7l-5-5l-5 5m5-5v12",
"chevrons-lucide-left-outline": "m11 17l-5-5l5-5m7 10l-5-5l5-5",
"chevrons-lucide-right-outline": "m6 17l5-5l-5-5m7 10l5-5l-5-5",