feat(editor): 使用 floatbox 替换原抽屉栏

This commit is contained in:
moonszhang 2024-03-11 20:11:13 +08:00 committed by roymondchen
parent 260286f9cf
commit a035f02f83
13 changed files with 128 additions and 66 deletions

View File

@ -1,6 +1,5 @@
<template>
<component
:is="slideType === 'box' ? MFormBox : MFormDrawer"
<MFormBox
class="m-editor-code-block-editor"
ref="fomDrawer"
label-width="80px"
@ -20,7 +19,7 @@
<template #left>
<TMagicButton type="primary" link @click="difVisible = true">查看修改</TMagicButton>
</template>
</component>
</MFormBox>
<TMagicDialog v-model="difVisible" title="查看修改" fullscreen>
<div style="display: flex; margin-bottom: 10px">
@ -55,7 +54,7 @@ import { ColumnConfig, FormConfig, FormState, MFormBox, MFormDrawer } from '@tma
import type { CodeBlockContent } from '@tmagic/schema';
import CodeEditor from '@editor/layouts/CodeEditor.vue';
import type { Services, SlideType } from '@editor/type';
import type { Services } from '@editor/type';
import { getConfig } from '@editor/utils/config';
defineOptions({
@ -67,7 +66,6 @@ const props = defineProps<{
disabled?: boolean;
isDataSource?: boolean;
dataSourceType?: string;
slideType?: SlideType;
}>();
const emit = defineEmits<{

View File

@ -1,6 +1,6 @@
<template>
<Teleport to="body" v-if="visible">
<div ref="target" class="m-editor-float-box" :style="style">
<div ref="target" class="m-editor-float-box" :style="style" @mousedown="nextZIndex">
<div ref="dragTarget" class="m-editor-float-box-title">
<slot name="title">
<span>{{ title }}</span>
@ -21,7 +21,7 @@ import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import { Close } from '@element-plus/icons-vue';
import VanillaMoveable from 'moveable';
import { TMagicButton } from '@tmagic/design';
import { TMagicButton, useZIndex } from '@tmagic/design';
interface Position {
left: number;
@ -47,11 +47,15 @@ const emit = defineEmits<{
const target = ref<HTMLDivElement>();
const dragTarget = ref<HTMLDivElement>();
const zIndex = useZIndex();
const curZIndex = ref<number>(zIndex.nextZIndex());
const style = computed(() => ({
left: `${props.position.left}px`,
top: `${props.position.top}px`,
width: typeof props.rect.width === 'string' ? props.rect.width : `${props.rect.width}px`,
height: typeof props.rect.height === 'string' ? props.rect.height : `${props.rect.height}px`,
zIndex: curZIndex.value,
}));
let moveable: VanillaMoveable | null = null;
@ -70,6 +74,7 @@ const initMoveable = () => {
dragTargetSelf: false,
linePadding: 10,
controlPadding: 10,
bounds: { left: 0, top: 0, right: 0, bottom: 0, position: 'css' },
});
moveable.on('drag', (e) => {
@ -111,6 +116,10 @@ const closeHandler = () => {
emit('update:visible', false);
};
const nextZIndex = () => {
curZIndex.value = zIndex.nextZIndex();
};
defineExpose({
target,
});

View File

@ -54,7 +54,6 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
codeId.value = id;
await nextTick();
codeBlockEditor.value?.show();
};

View File

@ -1,5 +1,5 @@
<template>
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }">
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }" ref="navMenu">
<div v-for="key in keys" :class="`menu-${key}`" :key="key" :style="`width: ${columnWidth?.[key]}px`">
<ToolButton :data="item" v-for="(item, index) in buttons[key]" :key="index"></ToolButton>
</div>
@ -7,7 +7,7 @@
</template>
<script lang="ts" setup>
import { computed, inject, markRaw } from 'vue';
import { computed, inject, markRaw, onBeforeUnmount, onMounted, ref } from 'vue';
import { Back, Delete, FullScreen, Grid, Memo, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/schema';
@ -178,4 +178,23 @@ const buttons = computed(() => {
});
return data;
});
const resizeObserver = new ResizeObserver(() => {
const rect = navMenu.value?.getBoundingClientRect();
if (rect) {
uiService?.set('navMenuRect', {
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
});
}
});
const navMenu = ref<HTMLDivElement>();
onMounted(() => {
navMenu.value && resizeObserver.observe(navMenu.value);
});
onBeforeUnmount(() => {
resizeObserver.disconnect();
});
</script>

View File

@ -204,6 +204,11 @@ const getItemConfig = (data: SideItem): SideComponent => {
text: '代码编辑',
component: CodeBlockListPanel,
slots: {},
boxComponentConfig: {
props: {
slideType: 'box',
},
},
},
'data-source': {
$key: 'data-source',

View File

@ -3,7 +3,7 @@
<slot name="code-block-panel-header">
<div class="search-wrapper">
<SearchInput @search="filterTextChangeHandler"></SearchInput>
<TMagicButton v-if="editable" class="create-code-button" type="primary" size="small" @click="createCodeBlock"
<TMagicButton v-if="editable" class="create-code-button" type="primary" size="small" @click="showCreate"
>新增</TMagicButton
>
<slot name="code-block-panel-search"></slot>
@ -11,7 +11,7 @@
</slot>
<!-- 代码块列表 -->
<CodeBlockList ref="codeBlockList" :custom-error="customError" @edit="editCode" @remove="deleteCode">
<CodeBlockList ref="codeBlockList" :custom-error="customError" @edit="showEdit" @remove="deleteCode">
<template #code-block-panel-tool="{ id, data }">
<slot name="code-block-panel-tool" :id="id" :data="data"></slot>
</template>
@ -19,23 +19,31 @@
</TMagicScrollbar>
<!-- 代码块编辑区 -->
<FloatingBox v-model:visible="popVisible" title="代码编辑" :position="boxPosition">
<template #body>
<div ref="scrollBar"></div>
</template>
</FloatingBox>
<Teleport :to="scrollBar" :disabled="slideType === 'box'" v-if="editVisible">
<CodeBlockEditor
v-if="codeConfig"
ref="codeBlockEditor"
:disabled="!editable"
:content="codeConfig"
:slideType="slideType"
@submit="submitCodeBlockHandler"
></CodeBlockEditor>
</Teleport>
</template>
<script setup lang="ts">
import { computed, inject, ref } from 'vue';
import { computed, inject, nextTick, ref } from 'vue';
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
import type { Id } from '@tmagic/schema';
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
import FloatingBox from '@editor/components/FloatingBox.vue';
import SearchInput from '@editor/components/SearchInput.vue';
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
import type { CodeBlockListPanelSlots, CodeDeleteErrorType, Services, SlideType } from '@editor/type';
@ -48,12 +56,12 @@ defineOptions({
name: 'MEditorCodeBlockListPanel',
});
defineProps<{
const props = defineProps<{
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
slideType?: SlideType;
}>();
const { codeBlockService } = inject<Services>('services') || {};
const { codeBlockService, uiService } = inject<Services>('services') || {};
const editable = computed(() => codeBlockService?.getEditStatus());
@ -65,4 +73,35 @@ const codeBlockList = ref<InstanceType<typeof CodeBlockList>>();
const filterTextChangeHandler = (val: string) => {
codeBlockList.value?.filter(val);
};
const boxPosition = computed(() => {
const columnWidth = uiService?.get('columnWidth');
const navMenuRect = uiService?.get('navMenuRect');
return {
left: columnWidth?.left ?? 0,
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
};
});
const scrollBar = ref<HTMLDivElement>();
const popVisible = ref<boolean>(false);
const editVisible = ref<boolean>(false);
const beforeShowEdit = async () => {
if (props.slideType !== 'box') {
popVisible.value = true;
}
await nextTick();
editVisible.value = true;
};
const showEdit = async (id: string) => {
await beforeShowEdit();
editCode(id);
};
const showCreate = async () => {
await beforeShowEdit();
createCodeBlock();
};
</script>

View File

@ -47,8 +47,13 @@ const state = reactive<UiState>({
showRule: true,
propsPanelSize: 'small',
showAddPageButton: true,
floatBox: new Map(),
hideSlideBar: false,
navMenuRect: {
left: 0,
top: 0,
width: 0,
height: 0,
},
});
const canUsePluginMethods = {
@ -110,19 +115,6 @@ class Ui extends BaseService {
return Math.min((width - 60) / stageWidth || 1, (height - 80) / stageHeight || 1);
}
public async setFloatBox(keys: string[]) {
const map = state.floatBox;
for (const key of keys) {
if (map.get(key)) continue;
map.set(key, {
status: false,
zIndex: 99,
top: 0,
left: 0,
});
}
}
public resetState() {
this.set('showSrc', false);
this.set('uiSelectMode', false);

View File

@ -1,3 +1,9 @@
.m-container-vs-code {
.el-form-item {
margin-bottom: 0;
}
}
.magic-code-editor {
width: 100%;
}

View File

@ -72,12 +72,15 @@
}
.m-editor-slide-list-box {
min-width: 270px;
display: flex;
min-width: 240px;
min-height: 500px;
max-height: 1024px;
> div {
&:first-child {
width: 100%;
min-width: 240px;
}
}
.m-form-box {
border-left: 1px solid #e0e0e0;
}
}

View File

@ -236,20 +236,16 @@ export interface UiState {
propsPanelSize: 'large' | 'default' | 'small';
/** 是否显示新增页面按钮 */
showAddPageButton: boolean;
/** slide 拖拽悬浮窗 state */
floatBox: Map<
string,
{
status: boolean;
zIndex: number;
top: number;
left: number;
}
>;
/** 是否隐藏侧边栏 */
hideSlideBar: boolean;
// navMenu 的宽高
navMenuRect: {
left: number;
top: number;
width: number;
height: number;
};
}
export interface EditorNodeInfo {

View File

@ -2,7 +2,7 @@
display: flex;
flex-direction: column;
padding: 16px;
width: 100%;
max-width: 90%;
.el-box__header {
margin: 0;
}

View File

@ -1,4 +1,3 @@
import React, { constructor, useEffect, useMemo, useState } from 'react';
import type { MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
@ -16,8 +15,8 @@ const PageFragmentContainer: React.FC<PageFragmentContainerProps> = ({ config })
if (!app) return null;
const MagicUiContainer = app.resolveComponent('container');
let containerConfig = {}
const fragment = app?.dsl?.items?.find((page) => page.id === config.pageFragmentId)
let containerConfig = {};
const fragment = app?.dsl?.items?.find((page) => page.id === config.pageFragmentId);
if (fragment) {
const { id, type, items, ...others } = fragment;
const itemsWithoutId = items.map((item: MNode) => {
@ -32,8 +31,8 @@ const PageFragmentContainer: React.FC<PageFragmentContainerProps> = ({ config })
} else {
containerConfig = {
...others,
items
}
items,
};
}
}
@ -43,9 +42,7 @@ const PageFragmentContainer: React.FC<PageFragmentContainerProps> = ({ config })
className="magic-ui-page-fragment-container"
style={app.transformStyle(config.style || {})}
>
<MagicUiContainer
config={containerConfig}
></MagicUiContainer>
<MagicUiContainer config={containerConfig}></MagicUiContainer>
</div>
);
};

View File

@ -1,4 +1,3 @@
import React from 'react';
import type { MComponent, MContainer, MPageFragment } from '@tmagic/schema';