Skip to content

Commit

Permalink
feat: add supports for hide the sidebar of editor (#4942)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/area console
/area editor
/kind feature
/milestone 2.11.0

#### What this PR does / why we need it:

支持隐藏/显示默认编辑器的侧边栏。

<img width="1451" alt="图片" src="https://github.com/halo-dev/halo/assets/21301288/6f641230-8f31-4f6b-83d2-e92dbc0303e8">
<img width="1363" alt="图片" src="https://github.com/halo-dev/halo/assets/21301288/921aa75e-ffd5-4785-9507-0b3b361efa31">

#### Which issue(s) this PR fixes:

Fixes #

#### Special notes for your reviewer:

测试截图中隐藏/显示侧边栏的按钮是否正常工作即可。

#### Does this PR introduce a user-facing change?

```release-note
支持隐藏/显示默认编辑器的侧边栏。
```
  • Loading branch information
ruibaby authored Nov 30, 2023
1 parent 8f83df4 commit 7a84f55
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { VTooltip } from "floating-vue";
const props = withDefaults(
defineProps<{
tooltip?: string;
modelValue: string;
modelValue?: string;
}>(),
{
modelValue: "",
tooltip: undefined,
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ref, type Component } from "vue";
const props = withDefaults(
defineProps<{
editor: Editor;
isActive: ({ editor }: { editor: Editor }) => boolean;
isActive?: ({ editor }: { editor: Editor }) => boolean;
visible?: ({ editor }: { editor: Editor }) => boolean;
icon?: Component;
iconStyle?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ onMounted(() => {
<template>
<node-view-wrapper as="div" class="inline-block w-full">
<div
class="inline-block overflow-hidden transition-all text-center relative h-full"
class="inline-block overflow-hidden transition-all text-center relative h-full max-w-full"
:style="{
width: node.attrs.width,
}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { i18n } from "@/locales";
import type { Editor } from "@/tiptap/vue-3";
import { computed, type Component, onUnmounted, ref, watch } from "vue";
import { computed, type Component } from "vue";
import Image from "./index";
import {
BlockActionButton,
Expand All @@ -12,30 +12,16 @@ import MdiBackupRestore from "~icons/mdi/backup-restore";
import MdiImageSizeSelectActual from "~icons/mdi/image-size-select-actual";
import MdiImageSizeSelectLarge from "~icons/mdi/image-size-select-large";
import MdiImageSizeSelectSmall from "~icons/mdi/image-size-select-small";
import { useResizeObserver } from "@vueuse/core";
const props = defineProps<{
editor: Editor;
isActive: ({ editor }: { editor: Editor }) => boolean;
isActive?: ({ editor }: { editor: Editor }) => boolean;
visible?: ({ editor }: { editor: Editor }) => boolean;
icon?: Component;
title?: string;
action?: ({ editor }: { editor: Editor }) => void;
}>();
const nodeDom = computed(() => {
if (!props.editor.isActive(Image.name)) {
return;
}
const nodeDomParent = props.editor.view.nodeDOM(
props.editor.state.selection.from
) as HTMLElement;
if (nodeDomParent && nodeDomParent.hasChildNodes()) {
return nodeDomParent.childNodes[0] as HTMLElement;
}
return undefined;
});
const width = computed({
get: () => {
return props.editor.getAttributes(Image.name).width;
Expand All @@ -54,70 +40,13 @@ const height = computed({
},
});
let mounted = false;
const imgScale = ref<number>(0);
watch(nodeDom, () => {
resetResizeObserver();
});
const reuseResizeObserver = () => {
let init = true;
return useResizeObserver(
nodeDom.value,
(entries) => {
// Skip first call
if (!mounted) {
mounted = true;
return;
}
const entry = entries[0];
const { width: w, height: h } = entry.contentRect;
if (init) {
imgScale.value = parseFloat((h / w).toFixed(2));
init = false;
return;
}
const node = props.editor.view.nodeDOM(props.editor.state.selection.from);
if (!node) {
return;
}
props.editor
.chain()
.updateAttributes(Image.name, {
width: w + "px",
height: w * imgScale.value + "px",
})
.setNodeSelection(props.editor.state.selection.from)
.focus()
.run();
},
{ box: "border-box" }
);
};
let resizeObserver = reuseResizeObserver();
window.addEventListener("resize", resetResizeObserver);
onUnmounted(() => {
window.removeEventListener("resize", resetResizeObserver);
});
function resetResizeObserver() {
resizeObserver.stop();
resizeObserver = reuseResizeObserver();
}
function handleSetSize(width?: string, height?: string) {
resizeObserver.stop();
props.editor
.chain()
.updateAttributes(Image.name, { width, height })
.setNodeSelection(props.editor.state.selection.from)
.focus()
.run();
resizeObserver = reuseResizeObserver();
}
</script>

Expand Down
58 changes: 42 additions & 16 deletions console/packages/editor/src/extensions/image/ImageView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Editor, Node } from "@/tiptap/vue-3";
import { NodeViewWrapper } from "@/tiptap/vue-3";
import type { Node as ProseMirrorNode, Decoration } from "@/tiptap/pm";
import { computed, onMounted, ref } from "vue";
import { useResizeObserver } from "@vueuse/core";
import Image from "./index";
const props = defineProps<{
editor: Editor;
Expand Down Expand Up @@ -45,29 +45,55 @@ const href = computed({
props.updateAttributes({ href: href });
},
});
function handleSetFocus() {
props.editor.commands.setNodeSelection(props.getPos());
}
const inputRef = ref();
const resizeRef = ref();
const init = ref(true);
const resizeRef = ref<HTMLDivElement>();
onMounted(() => {
if (!src.value) {
inputRef.value.focus();
} else {
useResizeObserver(resizeRef.value, (entries) => {
const entry = entries[0];
const { height } = entry.contentRect;
if (height == 0) {
return;
}
if (!props.selected && !init.value) {
handleSetFocus();
}
init.value = false;
});
return;
}
if (!resizeRef.value) return;
let startX: number, startWidth: number;
resizeRef.value.addEventListener("mousedown", function (e) {
startX = e.clientX;
startWidth = resizeRef.value?.clientWidth || 1;
document.documentElement.addEventListener("mousemove", doDrag, false);
document.documentElement.addEventListener("mouseup", stopDrag, false);
});
function doDrag(e: MouseEvent) {
if (!resizeRef.value) return;
const aspectRatio =
resizeRef.value.clientWidth / resizeRef.value.clientHeight;
const newWidth = Math.min(
startWidth + e.clientX - startX,
resizeRef.value.parentElement?.clientWidth || 0
);
const width = newWidth.toFixed(0) + "px";
const height = (newWidth / aspectRatio).toFixed(0) + "px";
props.editor
.chain()
.updateAttributes(Image.name, { width, height })
.setNodeSelection(props.editor.state.selection.from)
.focus()
.run();
}
function stopDrag() {
document.documentElement.removeEventListener("mousemove", doDrag, false);
document.documentElement.removeEventListener("mouseup", stopDrag, false);
}
});
</script>
Expand All @@ -87,7 +113,7 @@ onMounted(() => {
<div
v-else
ref="resizeRef"
class="resize-x inline-block overflow-hidden text-center relative rounded-md"
class="resize-x inline-block overflow-hidden text-center relative rounded-md max-w-full"
:class="{
'ring-2 rounded': selected,
}"
Expand Down
1 change: 1 addition & 0 deletions console/packages/editor/src/extensions/image/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ const Image = TiptapImage.extend<ExtensionOptions & ImageOptions>({
shouldShow: ({ state }: { state: EditorState }): boolean => {
return isActive(state, Image.name);
},
defaultAnimation: false,
items: [
{
priority: 10,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Video from "./index";
import { i18n } from "@/locales";
const props = defineProps<{
editor: Editor;
isActive: ({ editor }: { editor: Editor }) => boolean;
isActive?: ({ editor }: { editor: Editor }) => boolean;
visible?: ({ editor }: { editor: Editor }) => boolean;
icon?: Component;
title?: string;
Expand Down
2 changes: 1 addition & 1 deletion console/packages/editor/src/extensions/video/VideoView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ onMounted(() => {
<template>
<node-view-wrapper as="div" class="inline-block w-full">
<div
class="inline-block overflow-hidden transition-all text-center relative h-full"
class="inline-block overflow-hidden transition-all text-center relative h-full max-w-full"
:style="{
width: node.attrs.width,
}"
Expand Down
29 changes: 26 additions & 3 deletions console/src/components/editor/DefaultEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
ExtensionColumn,
ExtensionNodeSelected,
ExtensionTrailingNode,
ToolbarItem,
} from "@halo-dev/richtext-editor";
import {
IconCalendar,
Expand All @@ -62,6 +63,7 @@ import MdiFormatHeader3 from "~icons/mdi/format-header-3";
import MdiFormatHeader4 from "~icons/mdi/format-header-4";
import MdiFormatHeader5 from "~icons/mdi/format-header-5";
import MdiFormatHeader6 from "~icons/mdi/format-header-6";
import RiLayoutRightLine from "~icons/ri/layout-right-line";
import {
inject,
markRaw,
Expand All @@ -82,7 +84,7 @@ import { i18n } from "@/locales";
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
import { usePluginModuleStore } from "@/stores/plugin";
import type { PluginModule } from "@halo-dev/console-shared";
import { useDebounceFn } from "@vueuse/core";
import { useDebounceFn, useLocalStorage } from "@vueuse/core";
import { onBeforeUnmount } from "vue";
import { generateAnchor } from "@/utils/anchor";
import { usePermission } from "@/utils/permission";
Expand Down Expand Up @@ -137,6 +139,8 @@ const editor = shallowRef<Editor>();
const { pluginModules } = usePluginModuleStore();
const showSidebar = useLocalStorage("halo:editor:show-sidebar", true);
onMounted(() => {
const extensionsFromPlugins: AnyExtension[] = [];
pluginModules.forEach((pluginModule: PluginModule) => {
Expand Down Expand Up @@ -259,6 +263,23 @@ onMounted(() => {
},
];
},
getToolbarItems({ editor }: { editor: Editor }) {
return {
priority: 1000,
component: markRaw(ToolbarItem),
props: {
editor,
isActive: showSidebar.value,
icon: markRaw(RiLayoutRightLine),
title: i18n.global.t(
"core.components.default_editor.toolbox.show_hide_sidebar"
),
action: () => {
showSidebar.value = !showSidebar.value;
},
},
};
},
};
},
}),
Expand Down Expand Up @@ -463,7 +484,7 @@ const currentLocale = i18n.global.locale.value as
@select="onAttachmentSelect"
/>
<RichTextEditor v-if="editor" :editor="editor" :locale="currentLocale">
<template #extra>
<template v-if="showSidebar" #extra>
<OverlayScrollbarsComponent
element="div"
:options="{ scrollbars: { autoHide: 'scroll' } }"
Expand Down Expand Up @@ -496,7 +517,9 @@ const currentLocale = i18n.global.locale.value as
:is="headingIcons[node.level]"
class="h-4 w-4 rounded-sm bg-gray-100 p-0.5 group-hover:bg-white"
:class="[
{ '!bg-white': node.id === selectedHeadingNode?.id },
{
'!bg-white': node.id === selectedHeadingNode?.id,
},
]"
/>
<span class="flex-1 truncate">{{ node.text }}</span>
Expand Down
1 change: 1 addition & 0 deletions console/src/locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,7 @@ core:
placeholder: "Enter / to select input type."
toolbox:
attachment: Attachment
show_hide_sidebar: Show/Hide Sidebar
global_search:
placeholder: Enter keywords to search
no_results: No search results
Expand Down
1 change: 1 addition & 0 deletions console/src/locales/zh-CN.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,7 @@ core:
placeholder: "输入 / 以选择输入类型"
toolbox:
attachment: 选择附件
show_hide_sidebar: 显示 / 隐藏侧边栏
global_search:
placeholder: 输入关键词以搜索
no_results: 没有搜索结果
Expand Down
1 change: 1 addition & 0 deletions console/src/locales/zh-TW.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,7 @@ core:
placeholder: "輸入 / 以選擇輸入類型"
toolbox:
attachment: 選擇附件
show_hide_sidebar: 顯示 / 隱藏側邊欄
global_search:
placeholder: 輸入關鍵字以搜尋
no_results: 沒有搜尋結果
Expand Down

0 comments on commit 7a84f55

Please sign in to comment.