roymondchen a3333e2b4e feat(editor): 新增 hideSidebar 配置支持隐藏左侧面板
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 16:49:10 +08:00

177 lines
5.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="m-editor" ref="content" style="min-width: 900px">
<slot name="header"></slot>
<slot name="nav"></slot>
<slot name="content-before"></slot>
<slot name="src-code" v-if="showSrc">
<CodeEditor
class="m-editor-content"
editor-custom-type="m-editor-content"
:init-values="root"
:options="codeOptions"
@save="saveCode"
></CodeEditor>
</slot>
<SplitView
v-show="!showSrc"
ref="splitView"
class="m-editor-content"
left-class="m-editor-framework-left"
center-class="m-editor-framework-center"
right-class="m-editor-framework-right"
:left="hideSidebar ? undefined : columnWidth.left"
:right="columnWidth.right"
:min-left="hideSidebar ? 0 : MIN_LEFT_COLUMN_WIDTH"
:min-right="MIN_RIGHT_COLUMN_WIDTH"
:min-center="MIN_CENTER_COLUMN_WIDTH"
:width="frameworkRect.width"
@change="columnWidthChange"
>
<template v-if="!hideSidebar" #left>
<slot name="sidebar"></slot>
</template>
<template #center>
<slot v-if="page" name="workspace"></slot>
<slot v-else name="empty">
<AddPageBox :disabled-page-fragment="disabledPageFragment"></AddPageBox>
</slot>
<slot name="page-bar">
<PageBar
:disabled-page-fragment="disabledPageFragment"
:page-bar-sort-options="pageBarSortOptions"
:filter-function="pageFilterFunction"
>
<template #page-bar-add-button><slot name="page-bar-add-button"></slot></template>
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
<template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
</PageBar>
</slot>
</template>
<template v-if="page" #right>
<slot name="props-panel"></slot>
</template>
</SplitView>
<slot name="content-after"></slot>
<slot name="footer"></slot>
</div>
</template>
<script lang="ts" setup>
import { computed, inject, onBeforeUnmount, onMounted, useTemplateRef, watch } from 'vue';
import type { MPage, MPageFragment } from '@tmagic/core';
import SplitView from '@editor/components/SplitView.vue';
import { useServices } from '@editor/hooks/use-services';
import { Protocol } from '@editor/services/storage';
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config';
import {
DEFAULT_LEFT_COLUMN_WIDTH,
LEFT_COLUMN_WIDTH_STORAGE_KEY,
MIN_CENTER_COLUMN_WIDTH,
MIN_LEFT_COLUMN_WIDTH,
MIN_RIGHT_COLUMN_WIDTH,
RIGHT_COLUMN_WIDTH_STORAGE_KEY,
} from '@editor/utils/const';
import PageBar from './page-bar/PageBar.vue';
import AddPageBox from './AddPageBox.vue';
import CodeEditor from './CodeEditor.vue';
defineSlots<FrameworkSlots>();
defineOptions({
name: 'MEditorFramework',
});
const props = defineProps<{
disabledPageFragment: boolean;
pageBarSortOptions?: PageBarSortOptions;
pageFilterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean;
/** 是否隐藏左侧面板 */
hideSidebar?: boolean;
}>();
const codeOptions = inject('codeOptions', {});
const { editorService, uiService, storageService } = useServices();
const contentEl = useTemplateRef<HTMLDivElement>('content');
const splitViewRef = useTemplateRef<InstanceType<typeof SplitView>>('splitView');
const root = computed(() => editorService.get('root'));
const page = computed(() => editorService.get('page'));
const pageLength = computed(() => editorService.get('pageLength') || 0);
const showSrc = computed(() => uiService.get('showSrc'));
const columnWidth = computed(() => uiService.get('columnWidth'));
watch(pageLength, () => {
splitViewRef.value?.updateWidth();
});
watch(
() => uiService.get('hideSlideBar'),
(hideSlideBar) => {
uiService.set('columnWidth', {
...columnWidth.value,
left: hideSlideBar
? 0
: storageService.getItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, { protocol: Protocol.NUMBER }) ||
DEFAULT_LEFT_COLUMN_WIDTH,
});
},
);
const columnWidthChange = (columnW: GetColumnWidth) => {
// 隐藏左侧面板时 left 恒为 0不覆盖已持久化的左栏宽度避免重新展示时丢失宽度
if (!props.hideSidebar) {
storageService.setItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, columnW.left, { protocol: Protocol.NUMBER });
}
storageService.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, columnW.right, { protocol: Protocol.NUMBER });
uiService.set('columnWidth', columnW);
};
const frameworkRect = computed(() => uiService.get('frameworkRect'));
const resizerObserver = new ResizeObserver((entries) => {
const { contentRect } = entries[0];
uiService.set('frameworkRect', {
width: contentRect.width,
height: contentRect.height,
left: contentRect.left,
top: contentRect.top,
});
});
onMounted(() => {
if (contentEl.value) {
resizerObserver.observe(contentEl.value);
}
});
onBeforeUnmount(() => {
resizerObserver.disconnect();
});
const saveCode = (value: string) => {
try {
const parseDSL = getEditorConfig('parseDSL');
editorService.set('root', parseDSL(value));
} catch (e: any) {
console.error(e);
}
};
</script>