mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor): 新增Layout
This commit is contained in:
parent
22c57f444f
commit
835189adc9
@ -277,8 +277,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
uiService.initColumnWidth();
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
editorService.destroy();
|
editorService.destroy();
|
||||||
historyService.destroy();
|
historyService.destroy();
|
||||||
|
@ -10,77 +10,96 @@
|
|||||||
@save="saveCode"
|
@save="saveCode"
|
||||||
></magic-code-editor>
|
></magic-code-editor>
|
||||||
|
|
||||||
<div class="m-editor-content" v-else>
|
<layout
|
||||||
<div class="m-editor-framework-left" :style="`width: ${columnWidth?.left}px`">
|
v-else
|
||||||
|
class="m-editor-content"
|
||||||
|
left-class="m-editor-framework-left"
|
||||||
|
center-class="m-editor-framework-center"
|
||||||
|
right-class="m-editor-framework-right"
|
||||||
|
v-model:left="columnWidth.left"
|
||||||
|
v-model:right="columnWidth.right"
|
||||||
|
:min-left="45"
|
||||||
|
:min-right="1"
|
||||||
|
@change="columnWidthChange"
|
||||||
|
>
|
||||||
|
<template #left>
|
||||||
<slot name="sidebar"></slot>
|
<slot name="sidebar"></slot>
|
||||||
</div>
|
|
||||||
|
|
||||||
<resizer type="left"></resizer>
|
|
||||||
|
|
||||||
<template v-if="pageLength > 0">
|
|
||||||
<div class="m-editor-framework-center" :style="`width: ${columnWidth?.center}px`">
|
|
||||||
<slot name="workspace"></slot>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<resizer type="right"></resizer>
|
|
||||||
|
|
||||||
<div class="m-editor-framework-right" :style="`width: ${columnWidth?.right}px`">
|
|
||||||
<el-scrollbar>
|
|
||||||
<slot name="props-panel"></slot>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<slot v-else name="empty">
|
<template #center>
|
||||||
<add-page-box></add-page-box>
|
<slot v-if="pageLength > 0" name="workspace"></slot>
|
||||||
</slot>
|
<slot v-else name="empty">
|
||||||
</div>
|
<add-page-box></add-page-box>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="pageLength > 0" #right>
|
||||||
|
<el-scrollbar>
|
||||||
|
<slot name="props-panel"></slot>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
</layout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { computed, defineComponent, inject } from 'vue';
|
import { computed, inject, ref } from 'vue';
|
||||||
|
|
||||||
import type { MApp } from '@tmagic/schema';
|
import type { MApp } from '@tmagic/schema';
|
||||||
|
|
||||||
import { GetColumnWidth, Services } from '../type';
|
import { GetColumnWidth, Services } from '../type';
|
||||||
|
|
||||||
import AddPageBox from './AddPageBox.vue';
|
import AddPageBox from './AddPageBox.vue';
|
||||||
import Resizer from './Resizer.vue';
|
import Layout from './Layout.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
const DEFAULT_LEFT_COLUMN_WIDTH = 310;
|
||||||
components: {
|
const DEFAULT_RIGHT_COLUMN_WIDTH = 480;
|
||||||
AddPageBox,
|
|
||||||
Resizer,
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
codeOptions?: Record<string, any>;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
codeOptions: () => ({}),
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
|
||||||
props: {
|
const { editorService, uiService } = inject<Services>('services') || {};
|
||||||
codeOptions: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
const root = computed(() => editorService?.get<MApp>('root'));
|
||||||
const { editorService, uiService } = inject<Services>('services') || {};
|
|
||||||
|
|
||||||
const root = computed(() => editorService?.get<MApp>('root'));
|
const pageLength = computed(() => editorService?.get<number>('pageLength') || 0);
|
||||||
|
const showSrc = computed(() => uiService?.get<boolean>('showSrc'));
|
||||||
return {
|
const columnWidth = ref({
|
||||||
root,
|
left: DEFAULT_LEFT_COLUMN_WIDTH,
|
||||||
pageLength: computed(() => editorService?.get<number>('pageLength') || 0),
|
center: globalThis.document.body.clientWidth - DEFAULT_LEFT_COLUMN_WIDTH - DEFAULT_RIGHT_COLUMN_WIDTH,
|
||||||
showSrc: computed(() => uiService?.get<boolean>('showSrc')),
|
right: DEFAULT_RIGHT_COLUMN_WIDTH,
|
||||||
columnWidth: computed(() => uiService?.get<GetColumnWidth>('columnWidth')),
|
|
||||||
|
|
||||||
saveCode(value: string) {
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line no-eval
|
|
||||||
editorService?.set('root', eval(value));
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
uiService?.set('columnWidth', columnWidth.value);
|
||||||
|
|
||||||
|
const saveCode = (value: string) => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-eval
|
||||||
|
editorService?.set('root', eval(value));
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorColumnWidthData';
|
||||||
|
|
||||||
|
const columnWidthCacheData = globalThis.localStorage.getItem(COLUMN_WIDTH_STORAGE_KEY);
|
||||||
|
if (columnWidthCacheData) {
|
||||||
|
try {
|
||||||
|
const columnWidthCache = JSON.parse(columnWidthCacheData);
|
||||||
|
columnWidth.value = columnWidthCache;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnWidthChange = (columnWidth: GetColumnWidth) => {
|
||||||
|
uiService?.set('columnWidth', columnWidth);
|
||||||
|
globalThis.localStorage.setItem(COLUMN_WIDTH_STORAGE_KEY, JSON.stringify(columnWidth));
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
100
packages/editor/src/layouts/Layout.vue
Normal file
100
packages/editor/src/layouts/Layout.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="el">
|
||||||
|
<template v-if="typeof props.left !== 'undefined'">
|
||||||
|
<div class="m-editor-layout-left" :class="leftClass" :style="`width: ${left}px`">
|
||||||
|
<slot name="left"></slot>
|
||||||
|
</div>
|
||||||
|
<Resizer @change="changeLeft"></Resizer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="m-editor-layout-center" :class="centerClass" :style="`width: ${center}px`">
|
||||||
|
<slot name="center"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="typeof props.right !== 'undefined'">
|
||||||
|
<Resizer @change="changeRight"></Resizer>
|
||||||
|
<div class="m-editor-layout-right" :class="rightClass" :style="`width: ${right}px`">
|
||||||
|
<slot name="right"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import Resizer from './Resizer.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:left', 'change', 'update:right']);
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
left?: number;
|
||||||
|
right?: number;
|
||||||
|
minLeft?: number;
|
||||||
|
minRight?: number;
|
||||||
|
leftClass?: string;
|
||||||
|
rightClass?: string;
|
||||||
|
centerClass?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
minLeft: 1,
|
||||||
|
minRight: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const el = ref<HTMLElement>();
|
||||||
|
|
||||||
|
let clientWidth = 0;
|
||||||
|
const resizerObserver = new ResizeObserver((entries) => {
|
||||||
|
for (const { contentRect } of entries) {
|
||||||
|
clientWidth = contentRect.width;
|
||||||
|
|
||||||
|
center.value = clientWidth - (props.left || 0) - (props.right || 0);
|
||||||
|
|
||||||
|
emit('change', {
|
||||||
|
left: props.left,
|
||||||
|
center: center.value,
|
||||||
|
right: props.right,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (el.value) {
|
||||||
|
resizerObserver.observe(el.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
resizerObserver.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
const center = ref(0);
|
||||||
|
|
||||||
|
const changeLeft = (deltaX: number) => {
|
||||||
|
if (typeof props.left === 'undefined') return;
|
||||||
|
const left = Math.max(props.left + deltaX, props.minLeft) || 0;
|
||||||
|
emit('update:left', left);
|
||||||
|
center.value = clientWidth - left - (props.right || 0);
|
||||||
|
|
||||||
|
emit('change', {
|
||||||
|
left,
|
||||||
|
center: center.value,
|
||||||
|
right: props.right,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeRight = (deltaX: number) => {
|
||||||
|
if (typeof props.right === 'undefined') return;
|
||||||
|
const right = Math.max(props.right - deltaX, props.minRight) || 0;
|
||||||
|
emit('update:right', right);
|
||||||
|
center.value = clientWidth - (props.left || 0) - right;
|
||||||
|
|
||||||
|
emit('change', {
|
||||||
|
left: props.left,
|
||||||
|
center: center.value,
|
||||||
|
right,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
@ -4,58 +4,29 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent, inject, onMounted, onUnmounted, ref, toRaw } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import Gesto from 'gesto';
|
import Gesto from 'gesto';
|
||||||
|
|
||||||
import { Services } from '../type';
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
export default defineComponent({
|
const target = ref<HTMLSpanElement>();
|
||||||
name: 'm-editor-resize',
|
|
||||||
|
|
||||||
props: {
|
let getso: Gesto;
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
onMounted(() => {
|
||||||
const services = inject<Services>('services');
|
if (!target.value) return;
|
||||||
|
getso = new Gesto(target.value, {
|
||||||
|
container: window,
|
||||||
|
pinchOutside: true,
|
||||||
|
}).on('drag', (e) => {
|
||||||
|
if (!target.value) return;
|
||||||
|
|
||||||
const target = ref<HTMLSpanElement>();
|
emit('change', e.deltaX);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
let getso: Gesto;
|
onUnmounted(() => {
|
||||||
|
getso?.unset();
|
||||||
onMounted(() => {
|
|
||||||
if (!target.value) return;
|
|
||||||
getso = new Gesto(target.value, {
|
|
||||||
container: window,
|
|
||||||
pinchOutside: true,
|
|
||||||
}).on('drag', (e) => {
|
|
||||||
if (!target.value || !services) return;
|
|
||||||
|
|
||||||
let { left, right } = {
|
|
||||||
...toRaw(services.uiService.get('columnWidth')),
|
|
||||||
};
|
|
||||||
if (props.type === 'left') {
|
|
||||||
left += e.deltaX;
|
|
||||||
} else if (props.type === 'right') {
|
|
||||||
right -= e.deltaX;
|
|
||||||
}
|
|
||||||
services.uiService.set('columnWidth', {
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
getso?.unset();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
target,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,28 +16,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { reactive, toRaw } from 'vue';
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
import type StageCore from '@tmagic/stage';
|
import type StageCore from '@tmagic/stage';
|
||||||
|
|
||||||
import editorService from '../services/editor';
|
import editorService from '../services/editor';
|
||||||
import type { GetColumnWidth, SetColumnWidth, StageRect, UiState } from '../type';
|
import type { StageRect, UiState } from '../type';
|
||||||
|
|
||||||
import BaseService from './BaseService';
|
import BaseService from './BaseService';
|
||||||
|
|
||||||
const DEFAULT_LEFT_COLUMN_WIDTH = 310;
|
|
||||||
const MIN_LEFT_COLUMN_WIDTH = 45;
|
|
||||||
const DEFAULT_RIGHT_COLUMN_WIDTH = 480;
|
|
||||||
const MIN_RIGHT_COLUMN_WIDTH = 1;
|
|
||||||
|
|
||||||
const COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorColumnWidthData';
|
|
||||||
|
|
||||||
const defaultColumnWidth = {
|
|
||||||
left: DEFAULT_LEFT_COLUMN_WIDTH,
|
|
||||||
center: globalThis.document.body.clientWidth - DEFAULT_LEFT_COLUMN_WIDTH - DEFAULT_RIGHT_COLUMN_WIDTH,
|
|
||||||
right: DEFAULT_RIGHT_COLUMN_WIDTH,
|
|
||||||
};
|
|
||||||
|
|
||||||
const state = reactive<UiState>({
|
const state = reactive<UiState>({
|
||||||
uiSelectMode: false,
|
uiSelectMode: false,
|
||||||
showSrc: false,
|
showSrc: false,
|
||||||
@ -50,7 +37,7 @@ const state = reactive<UiState>({
|
|||||||
width: 375,
|
width: 375,
|
||||||
height: 817,
|
height: 817,
|
||||||
},
|
},
|
||||||
columnWidth: defaultColumnWidth,
|
columnWidth: {},
|
||||||
showGuides: true,
|
showGuides: true,
|
||||||
showRule: true,
|
showRule: true,
|
||||||
propsPanelSize: 'small',
|
propsPanelSize: 'small',
|
||||||
@ -59,7 +46,7 @@ const state = reactive<UiState>({
|
|||||||
|
|
||||||
class Ui extends BaseService {
|
class Ui extends BaseService {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(['initColumnWidth', 'zoom', 'calcZoom']);
|
super(['zoom', 'calcZoom']);
|
||||||
globalThis.addEventListener('resize', () => {
|
globalThis.addEventListener('resize', () => {
|
||||||
this.setColumnWidth({
|
this.setColumnWidth({
|
||||||
center: 'auto',
|
center: 'auto',
|
||||||
@ -70,11 +57,6 @@ class Ui extends BaseService {
|
|||||||
public set<T = any>(name: keyof UiState, value: T) {
|
public set<T = any>(name: keyof UiState, value: T) {
|
||||||
const mask = editorService.get<StageCore>('stage')?.mask;
|
const mask = editorService.get<StageCore>('stage')?.mask;
|
||||||
|
|
||||||
if (name === 'columnWidth') {
|
|
||||||
this.setColumnWidth(value as unknown as SetColumnWidth);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'stageRect') {
|
if (name === 'stageRect') {
|
||||||
this.setStageRect(value as unknown as StageRect);
|
this.setStageRect(value as unknown as StageRect);
|
||||||
return;
|
return;
|
||||||
@ -95,18 +77,6 @@ class Ui extends BaseService {
|
|||||||
return (state as any)[name];
|
return (state as any)[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initColumnWidth() {
|
|
||||||
const columnWidthCacheData = globalThis.localStorage.getItem(COLUMN_WIDTH_STORAGE_KEY);
|
|
||||||
if (columnWidthCacheData) {
|
|
||||||
try {
|
|
||||||
const columnWidthCache = JSON.parse(columnWidthCacheData);
|
|
||||||
this.setColumnWidth(columnWidthCache);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async zoom(zoom: number) {
|
public async zoom(zoom: number) {
|
||||||
this.set('zoom', (this.get<number>('zoom') * 100 + zoom * 100) / 100);
|
this.set('zoom', (this.get<number>('zoom') * 100 + zoom * 100) / 100);
|
||||||
if (this.get<number>('zoom') < 0.1) this.set('zoom', 0.1);
|
if (this.get<number>('zoom') < 0.1) this.set('zoom', 0.1);
|
||||||
@ -132,36 +102,6 @@ class Ui extends BaseService {
|
|||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private setColumnWidth({ left, center, right }: SetColumnWidth) {
|
|
||||||
const columnWidth = {
|
|
||||||
...toRaw(this.get<GetColumnWidth>('columnWidth')),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (left) {
|
|
||||||
columnWidth.left = Math.max(left, MIN_LEFT_COLUMN_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (right) {
|
|
||||||
columnWidth.right = Math.max(right, MIN_RIGHT_COLUMN_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!center || center === 'auto') {
|
|
||||||
const bodyWidth = globalThis.document.body.clientWidth;
|
|
||||||
columnWidth.center = bodyWidth - (columnWidth?.left || 0) - (columnWidth?.right || 0);
|
|
||||||
if (columnWidth.center <= 0) {
|
|
||||||
columnWidth.left = defaultColumnWidth.left;
|
|
||||||
columnWidth.center = defaultColumnWidth.center;
|
|
||||||
columnWidth.right = defaultColumnWidth.right;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
columnWidth.center = center;
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.localStorage.setItem(COLUMN_WIDTH_STORAGE_KEY, JSON.stringify(columnWidth));
|
|
||||||
|
|
||||||
state.columnWidth = columnWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setStageRect(value: StageRect) {
|
private async setStageRect(value: StageRect) {
|
||||||
state.stageRect = {
|
state.stageRect = {
|
||||||
...state.stageRect,
|
...state.stageRect,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user