import { Component, Fragment, ReactNodeArray, isValidElement, cloneElement, createElement, ReactNode, ComponentType, } from 'react'; import classNames from 'classnames'; import { observer, computed, Tip, globalContext, makeObservable } from '@alilc/lowcode-editor-core'; import { createIcon, isReactComponent } from '@alilc/lowcode-utils'; import { ActionContentObject, isActionContentObject } from '@alilc/lowcode-types'; import { BuiltinSimulatorHost } from '../host'; import { OffsetObserver } from '../../designer'; import { Node } from '../../document'; import NodeSelector from '../node-selector'; @observer export class BorderSelectingInstance extends Component<{ observed: OffsetObserver; highlight?: boolean; dragging?: boolean; }> { componentWillUnmount() { this.props.observed.purge(); } render() { const { observed, highlight, dragging } = this.props; if (!observed.hasOffset) { return null; } const { offsetWidth, offsetHeight, offsetTop, offsetLeft } = observed; const style = { width: offsetWidth, height: offsetHeight, transform: `translate3d(${offsetLeft}px, ${offsetTop}px, 0)`, }; const className = classNames('lc-borders lc-borders-selecting', { highlight, dragging, }); const hideSelectTools = observed.node.componentMeta.getMetadata().configure.advanced?.hideSelectTools; if (hideSelectTools) { return null; } return (
{!dragging && }
); } } @observer class Toolbar extends Component<{ observed: OffsetObserver }> { render() { const { observed } = this.props; const { height, width } = observed.viewport; const BAR_HEIGHT = 20; const MARGIN = 1; const BORDER = 2; const SPACE_HEIGHT = BAR_HEIGHT + MARGIN + BORDER; const SPACE_MINIMUM_WIDTH = 160; // magic number,大致是 toolbar 的宽度 let style: any; // 计算 toolbar 的上/下位置 if (observed.top > SPACE_HEIGHT) { style = { top: -SPACE_HEIGHT, height: BAR_HEIGHT, }; } else if (observed.bottom + SPACE_HEIGHT < height) { style = { bottom: -SPACE_HEIGHT, height: BAR_HEIGHT, }; } else { style = { height: BAR_HEIGHT, top: Math.max(MARGIN, MARGIN - observed.top), }; } // 计算 toolbar 的左/右位置 if (SPACE_MINIMUM_WIDTH > observed.left + observed.width) { style.left = Math.max(-BORDER, observed.left - width - BORDER); } else { style.right = Math.max(-BORDER, observed.right - width - BORDER); style.justifyContent = 'flex-start'; } const { node } = observed; const actions: ReactNodeArray = []; node.componentMeta.availableActions.forEach((action) => { const { important = true, condition, content, name } = action; if (node.isSlot() && (name === 'copy' || name === 'remove')) { // FIXME: need this? return; } if (important && (typeof condition === 'function' ? condition(node) !== false : condition !== false)) { actions.push(createAction(content, name, node)); } }); return (
{actions}
); } } function createAction(content: ReactNode | ComponentType | ActionContentObject, key: string, node: Node) { if (isValidElement(content)) { return cloneElement(content, { key, node }); } if (isReactComponent(content)) { return createElement(content, { key, node }); } if (isActionContentObject(content)) { const { action, title, icon } = content; return (
{ action && action(node); const editor = globalContext.get('editor'); const npm = node?.componentMeta?.npm; const selected = [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || ''; editor?.emit('designer.border.action', { name: key, selected, }); }} > {icon && createIcon(icon)} {title}
); } return null; } @observer export class BorderSelectingForNode extends Component<{ host: BuiltinSimulatorHost; node: Node }> { get host(): BuiltinSimulatorHost { return this.props.host; } get dragging(): boolean { return this.host.designer.dragon.dragging; } @computed get instances() { return this.host.getComponentInstances(this.props.node); } render() { const { instances } = this; const { node } = this.props; const { designer } = this.host; if (!instances || instances.length < 1) { return null; } return ( {instances.map((instance) => { const observed = designer.createOffsetObserver({ node, instance, }); if (!observed) { return null; } return ; })} ); } } @observer export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> { get host(): BuiltinSimulatorHost { return this.props.host; } get dragging(): boolean { return this.host.designer.dragon.dragging; } @computed get selecting() { const doc = this.host.currentDocument; if (!doc || doc.suspensed || this.host.liveEditing.editing) { return null; } const { selection } = doc; return this.dragging ? selection.getTopNodes() : selection.getNodes(); } render() { const { selecting } = this; if (!selecting || selecting.length < 1) { return null; } return ( {selecting.map((node) => ( ))} ); } }