184 lines
5.7 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
ref="content"
:class="['m-editor', theme ? `m-editor--${theme}` : '', theme ? `m-theme--${theme}` : '']"
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 : minLeftColumnWidth"
:min-right="minRightColumnWidth"
:min-center="minCenterColumnWidth"
: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,
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;
/** 主题名称,会在根节点追加 `m-editor--<theme>` 修饰类 */
theme?: string;
}>();
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'));
const minLeftColumnWidth = computed(() => uiService.get('minLeftColumnWidth'));
const minCenterColumnWidth = computed(() => uiService.get('minCenterColumnWidth'));
const minRightColumnWidth = computed(() => uiService.get('minRightColumnWidth'));
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), { historySource: 'root-code' });
} catch (e: any) {
console.error(e);
}
};
</script>