diff --git a/packages/designer/src/builtin-simulator/host-view.tsx b/packages/designer/src/builtin-simulator/host-view.tsx index fe89c0a35..b5b5640d0 100644 --- a/packages/designer/src/builtin-simulator/host-view.tsx +++ b/packages/designer/src/builtin-simulator/host-view.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { observer } from '@ali/lowcode-editor-core'; +import { observer, globalContext } from '@ali/lowcode-editor-core'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; import { BemTools } from './bem-tools'; import { Project } from '../project'; @@ -73,14 +73,43 @@ class Canvas extends Component<{ host: BuiltinSimulatorHost }> { @observer class Content extends Component<{ host: BuiltinSimulatorHost }> { + state = { + disabledEvents: false, + }; + + private dispose?: () => void; + + componentDidMount() { + const editor = globalContext.get('editor'); + const onEnableEvents = (type: boolean) => { + this.setState({ + disabledEvents: type, + }); + }; + + editor.on('designer.builtinSimulator.disabledEvents', onEnableEvents); + + this.dispose = () => { + editor.removeListener('designer.builtinSimulator.disabledEvents', onEnableEvents); + }; + } + + componentWillUnmount() { + this.dispose?.(); + } + render() { const sim = this.props.host; + const { disabledEvents } = this.state; const { viewport } = sim; - const frameStyle = { + const frameStyle: any = { transform: `scale(${viewport.scale})`, height: viewport.contentHeight, width: viewport.contentWidth, }; + if (disabledEvents) { + frameStyle.pointerEvents = 'none'; + } return (
diff --git a/packages/editor-skeleton/src/components/draggable-line/index.less b/packages/editor-skeleton/src/components/draggable-line/index.less new file mode 100644 index 000000000..ea27b2d9a --- /dev/null +++ b/packages/editor-skeleton/src/components/draggable-line/index.less @@ -0,0 +1,15 @@ +.lc-draggable-line-vertical { + position: absolute; + width: 4px; + height: 100%; + background-color: transparent; + cursor: col-resize; +} + +.lc-draggable-line-horizontal { + position: absolute; + width: 100%; + height: 4px; + background-color: transparent; + cursor: row-resize; +} diff --git a/packages/editor-skeleton/src/components/draggable-line/index.tsx b/packages/editor-skeleton/src/components/draggable-line/index.tsx new file mode 100644 index 000000000..e3871c35f --- /dev/null +++ b/packages/editor-skeleton/src/components/draggable-line/index.tsx @@ -0,0 +1,146 @@ +import { Component } from 'react'; +import classNames from 'classnames'; +import './index.less'; + +export interface DraggableLineProps { + onDrag: (l: number, e: any) => any; + onDragStart?: () => any; + onDragEnd?: () => any; + position?: 'right' | 'left' | 'top'; + className?: string; + maxIncrement?: number; + maxDecrement?: number; +} + +export default class DraggableLine extends Component { + static displayName = 'DraggableLine'; + + static defaultProps = { + onDrag() {}, + position: 'right', + className: '', + maxIncrement: 100, + maxDecrement: 0, + }; + + private startDrag: boolean; + private canDrag: boolean; + private offset: number; + private currentOffset: number; + private offEvent: any; + private offDragEvent: any; + private startOffset: any; + private shell: HTMLElement | null = null; + + constructor(props: DraggableLineProps) { + super(props); + this.startDrag = false; + this.canDrag = false; + this.offset = 0; + this.currentOffset = 0; + } + + componentDidMount() { + this.offEvent = this.initEvent(); + } + + componentWillUnmount() { + if (this.offEvent) { + this.offEvent(); + } + } + + onSelectStart(e: any) { + if (this.startDrag) { + e.preventDefault(); + } + } + + onStartMove(e: any) { + const { onDragStart } = this.props; + if (!this.startDrag) { + onDragStart && onDragStart(); + } + this.startDrag = true; + this.canDrag = true; + this.currentOffset = 0; + this.offDragEvent = this.initDragEvent(); + this.startOffset = this.getClientPosition(e); + } + + onEndMove() { + const { onDragEnd } = this.props; + if (this.startDrag) { + if (this.offDragEvent) { + this.offDragEvent(); + } + this.startDrag = false; + this.offset = this.currentOffset; + } + onDragEnd && onDragEnd(); + } + + onDrag(e: any) { + const { position, onDrag, maxIncrement = 100, maxDecrement = 0 } = this.props; + if (this.startDrag) { + if (position === 'left' || position === 'top') { + this.currentOffset = this.offset + this.startOffset - this.getClientPosition(e); + } else { + this.currentOffset = this.offset + this.getClientPosition(e) - this.startOffset; + } + + if (this.currentOffset < -maxDecrement) { + this.currentOffset = -maxDecrement; + } else if (this.currentOffset > maxIncrement) { + this.currentOffset = maxIncrement; + } + + onDrag(this.currentOffset, e); + } + } + + getClientPosition(e: any) { + const { position } = this.props; + return position === 'left' || position === 'right' ? e.clientX : e.clientY; + } + + initEvent() { + const selectStart = this.onSelectStart.bind(this); + document.addEventListener('selectstart', selectStart); + return () => document.removeEventListener('selectstart', selectStart); + } + + initDragEvent() { + const onDrag = this.onDrag.bind(this); + const onEndMove = this.onEndMove.bind(this); + document.addEventListener('mousemove', onDrag); + document.addEventListener('mouseup', onEndMove); + return () => { + document.removeEventListener('mousemove', onDrag); + document.removeEventListener('mouseup', onEndMove); + }; + } + + getParent() { + return this.shell?.parentElement; + } + + render() { + const { className = '', position } = this.props; + + return ( +
{ this.shell = ref; }} + className={classNames( + position === 'left' || position === 'right' + ? 'lc-draggable-line-vertical' + : 'lc-draggable-line-horizontal', + { + [className]: !!className, + }, + )} + onMouseDown={(e) => this.onStartMove(e)} + /> + ); + } +} diff --git a/packages/editor-skeleton/src/components/widget-views/index.less b/packages/editor-skeleton/src/components/widget-views/index.less index 239885900..b392752be 100644 --- a/packages/editor-skeleton/src/components/widget-views/index.less +++ b/packages/editor-skeleton/src/components/widget-views/index.less @@ -5,10 +5,24 @@ &.hidden { display: none; } - } .lc-widget-disabled { pointer-events: none; opacity: 0.4; -} \ No newline at end of file +} + +.lc-draggable-line-vertical { + position: absolute; + width: 4px; + height: 100%; + top: 0; + background-color: transparent; + cursor: col-resize; + right: -2px; + z-index: 99; +} + +.lc-engine-slate-draggable-line-right { + right: -2px; +} diff --git a/packages/editor-skeleton/src/components/widget-views/index.tsx b/packages/editor-skeleton/src/components/widget-views/index.tsx index 4a6a38d94..33d72ff1c 100644 --- a/packages/editor-skeleton/src/components/widget-views/index.tsx +++ b/packages/editor-skeleton/src/components/widget-views/index.tsx @@ -9,6 +9,8 @@ import WidgetContainer from '../../widget/widget-container'; import Panel from '../../widget/panel'; import { IWidget } from '../../widget/widget'; import { SkeletonEvents } from '../../skeleton'; +import DraggableLine from '../draggable-line'; +import PanelOperationRow from './panel-operation-row'; import './index.less'; @@ -84,6 +86,75 @@ export class PanelDockView extends Component { export class DialogDockView extends Component {} +export class DraggableLineView extends Component<{ panel: Panel }> { + private shell: any; + private defaultWidth: number; + + private getDefaultWidth() { + const configWidth = this.props.panel?.config.props?.width; + if (configWidth) { + return configWidth; + } + if (this.defaultWidth) { + return this.defaultWidth; + } + const containerRef = this.shell?.getParent(); + if (containerRef) { + this.defaultWidth = containerRef.offsetWidth; + return this.defaultWidth; + } + return 300; + } + + onDrag(value: number) { + const defaultWidth = this.getDefaultWidth(); + const width = defaultWidth + value; + + const containerRef = this.shell?.getParent(); + if (containerRef) { + containerRef.style.width = `${width}px`; + } + + // 抛出事件,对于有些需要 panel 插件随着 度变化进行再次渲染的,由panel插件内部监听事件实现 + const editor = globalContext.get(Editor); + editor?.emit('dockpane.drag', width); + } + + onDragChange(type: 'start' | 'end') { + const editor = globalContext.get(Editor); + editor?.emit('dockpane.dragchange', type); + // builtinSimulator 屏蔽掉 鼠标事件 + editor?.emit('designer.builtinSimulator.disabledEvents', type === 'start'); + } + + render() { + // left fixed 下不允许改变宽度 + // 默认 关闭,通过配置开启 + const enableDrag = this.props.panel.config.props?.enableDrag; + const isRightArea = this.props.panel.config?.area === 'rightArea'; + if (isRightArea || !enableDrag || this.props.panel?.parent.name === 'leftFixedArea') { + return null; + } + return ( + { + this.shell = ref; + }} + position="right" + className="lc-engine-slate-draggable-line-right" + onDrag={(e) => this.onDrag(e)} + onDragStart={() => this.onDragChange('start')} + onDragEnd={() => this.onDragChange('end')} + maxIncrement={500} + maxDecrement={0} + // TODO: 优化 + // maxIncrement={dock.getMaxWidth() - this.cachedSize.width} + // maxDecrement={this.cachedSize.width - dock.getWidth()} + /> + ); + } +} + @observer export class TitledPanelView extends Component<{ panel: Panel; area?: string }> { shouldComponentUpdate() { @@ -131,15 +202,22 @@ export class TitledPanelView extends Component<{ panel: Panel; area?: string }> })} id={panelName} > +
{panel.body}
+
); } } @observer -export class PanelView extends Component<{ panel: Panel; area?: string }> { +export class PanelView extends Component<{ + panel: Panel; + area?: string; + hideOperationRow?: boolean; + hideDragLine?: boolean; +}> { shouldComponentUpdate() { return false; } @@ -172,7 +250,7 @@ export class PanelView extends Component<{ panel: Panel; area?: string }> { } render() { - const { panel, area } = this.props; + const { panel, area, hideOperationRow, hideDragLine } = this.props; if (!panel.inited) { return null; } @@ -189,7 +267,9 @@ export class PanelView extends Component<{ panel: Panel; area?: string }> { })} id={panelName} > + {!hideOperationRow && } {panel.body} + {!hideDragLine && }
); } @@ -203,7 +283,7 @@ export class TabsPanelView extends Component<{ container: WidgetContainer const contents: ReactElement[] = []; container.items.forEach((item: any) => { titles.push(); - contents.push(); + contents.push(); }); return ( @@ -302,11 +382,7 @@ export class WidgetView extends Component<{ widget: IWidget }> { return null; } if (widget.disabled) { - return ( -
- {widget.body} -
- ); + return
{widget.body}
; } return widget.body; } diff --git a/packages/editor-skeleton/src/components/widget-views/panel-operation-row.tsx b/packages/editor-skeleton/src/components/widget-views/panel-operation-row.tsx new file mode 100644 index 000000000..3dc06493b --- /dev/null +++ b/packages/editor-skeleton/src/components/widget-views/panel-operation-row.tsx @@ -0,0 +1,67 @@ +import { Component, Fragment } from 'react'; +import { Button, Icon } from '@alifd/next'; +import { IconFix } from '../../icons/fix'; +import { IconFloat } from '../../icons/float'; +import Panel from '../../widget/panel'; + +export default class PanelOperationRow extends Component<{ panel: Panel }> { + // fix or float + setDisplay() { + const { panel } = this.props; + const current = panel; + if (!current) { + return; + } + if (panel?.parent?.name === 'leftFloatArea') { + panel.skeleton.leftFloatArea.remove(current); + panel.skeleton.leftFixedArea.add(current); + panel.skeleton.leftFixedArea.container.active(current); + } else { + panel.skeleton.leftFixedArea.remove(current); + panel.skeleton.leftFloatArea.add(current); + panel.skeleton.leftFloatArea.container.active(current); + } + } + + render() { + const { panel } = this.props; + const isRightArea = this.props.panel.config?.area === 'rightArea'; + if (isRightArea) { + return null; + } + // can be set fixed by default + let canSetFixed = true; + if (panel?.config.props?.canSetFixed === false) { + canSetFixed = false; + } + + const hideTitleBar = panel?.config.props?.hideTitleBar; + + const areaName = panel?.parent?.name; + const area = panel.skeleton[areaName]; + + return ( + + {!hideTitleBar && ( + + {canSetFixed && ( + // eslint-disable-next-line react/jsx-no-bind + + )} + + + )} + + ); + } +} diff --git a/packages/editor-skeleton/src/layouts/left-fixed-pane.tsx b/packages/editor-skeleton/src/layouts/left-fixed-pane.tsx index 283f787f4..18dd3c8e9 100644 --- a/packages/editor-skeleton/src/layouts/left-fixed-pane.tsx +++ b/packages/editor-skeleton/src/layouts/left-fixed-pane.tsx @@ -1,12 +1,10 @@ import { Component, Fragment } from 'react'; import classNames from 'classnames'; import { observer } from '@ali/lowcode-editor-core'; -import { Button, Icon } from '@alifd/next'; import Area from '../area'; import { PanelConfig } from '../types'; import Panel from '../widget/panel'; import { Designer } from '@ali/lowcode-designer'; -import { IconFloat } from '../icons/float'; @observer export default class LeftFixedPane extends Component<{ area: Area }> { @@ -19,21 +17,9 @@ export default class LeftFixedPane extends Component<{ area: Area - {!hideTitleBar && ( - - - - - )} ); diff --git a/packages/editor-skeleton/src/layouts/left-float-pane.tsx b/packages/editor-skeleton/src/layouts/left-float-pane.tsx index 064bb83f7..cdf0c6868 100644 --- a/packages/editor-skeleton/src/layouts/left-float-pane.tsx +++ b/packages/editor-skeleton/src/layouts/left-float-pane.tsx @@ -1,13 +1,12 @@ import { Component, Fragment } from 'react'; import classNames from 'classnames'; import { observer, Focusable, focusTracker } from '@ali/lowcode-editor-core'; -import { Button, Icon } from '@alifd/next'; -import { IconFix } from '../icons/fix'; import Area from '../area'; import Panel from '../widget/panel'; @observer export default class LeftFloatPane extends Component<{ area: Area }> { + shouldComponentUpdate() { return false; } @@ -22,6 +21,7 @@ export default class LeftFloatPane extends Component<{ area: Area }> const { area } = this.props; const triggerClose = () => area.setVisible(false); area.skeleton.editor.on('designer.dragstart', triggerClose); + this.dispose = () => { area.skeleton.editor.removeListener('designer.dragstart', triggerClose); }; @@ -95,29 +95,10 @@ export default class LeftFloatPane extends Component<{ area: Area }> this.dispose?.(); } - // 固定 - setFixed() { - const { area } = this.props; - const { current } = area; - if (!current) { - return; - } - - area.skeleton.leftFloatArea.remove(current); - area.skeleton.leftFixedArea.add(current); - area.skeleton.leftFixedArea.container.active(current); - } - render() { const { area } = this.props; const width = area.current?.config.props?.width; - // can be set fixed by default - let canSetFixed = true; - if (area.current?.config.props?.canSetFixed === false) { - canSetFixed = false; - } - const hideTitleBar = area.current?.config.props?.hideTitleBar; const style = width ? { width, } : undefined; @@ -129,32 +110,6 @@ export default class LeftFloatPane extends Component<{ area: Area }> })} style={style} > - { - !hideTitleBar && ( - - { - canSetFixed && ( - - ) - } - - - ) - } ); diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less index 9d8102e3e..a8541e3ae 100644 --- a/packages/editor-skeleton/src/layouts/workbench.less +++ b/packages/editor-skeleton/src/layouts/workbench.less @@ -49,6 +49,8 @@ body { width: 100%; height: 100%; position: relative; + background-color: #fff; + background-color: var(--color-pane-background); &.hidden { display: none; } @@ -122,6 +124,9 @@ body { .lc-panel { height: 100%; width: 100%; + position: relative; + background-color: #fff; + background-color: var(--color-pane-background); // overflow: auto; &.hidden { display: none; diff --git a/packages/editor-skeleton/src/types.ts b/packages/editor-skeleton/src/types.ts index 83dc0f1f0..c6485f2cd 100644 --- a/packages/editor-skeleton/src/types.ts +++ b/packages/editor-skeleton/src/types.ts @@ -103,6 +103,7 @@ export interface PanelProps { onInit?: (widget: IWidget) => any; onDestroy?: () => any; shortcut?: string; // 只有在特定位置,可触发 toggle show + enableDrag?: boolean; // 是否开启通过 drag 调整 宽度 } export interface PanelDockConfig extends IDockBaseConfig { diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index c783073a3..4cb6f1e73 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -25,6 +25,7 @@ registerDefaults(); const editor = new Editor(); globalContext.register(editor, Editor); +globalContext.register(editor, 'editor'); const skeleton = new Skeleton(editor); editor.set(Skeleton, skeleton); diff --git a/packages/vision-polyfill/src/panes.ts b/packages/vision-polyfill/src/panes.ts index 0a61d860e..47fc38742 100644 --- a/packages/vision-polyfill/src/panes.ts +++ b/packages/vision-polyfill/src/panes.ts @@ -49,6 +49,7 @@ export interface OldPaneConfig { fullScreen?: boolean; // todo canSetFixed?: boolean; // 是否可以设置固定模式 defaultFixed?: boolean; // 是否默认固定 + enableDrag?: boolean; } function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: string } { @@ -83,6 +84,7 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin isAction, canSetFixed, defaultFixed, + enableDrag, } = config; if (menu) { newConfig.props.title = menu; @@ -99,6 +101,7 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin height, maxHeight, canSetFixed, + enableDrag, }; if (defaultFixed) { @@ -135,7 +138,6 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin } newConfig.props.onInit = init; newConfig.props.onDestroy = destroy; - return newConfig; }