Skip to content

Commit

Permalink
feat(core/react/playground): add freelayout feature. add custom sourc…
Browse files Browse the repository at this point in the history
…e icon feature (#184)
  • Loading branch information
jinphic authored Dec 30, 2021
1 parent dd06bfa commit 171c64f
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/core/src/effects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './useKeyboardEffect'
export * from './useAutoScrollEffect'
export * from './useWorkspaceEffect'
export * from './useContentEditableEffect'
export * from './useTranslateEffect'
80 changes: 80 additions & 0 deletions packages/core/src/effects/useTranslateEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Engine, CursorType } from '../models'
import { DragStartEvent, DragMoveEvent, DragStopEvent } from '../events'
import { TreeNode } from '../models'
import { Point } from '@designable/shared'

type TranslateData = {
element?: HTMLElement
node?: TreeNode
point?: Point
}

type TranslateStore = {
value?: TranslateData
}

export const useTranslateEffect = (engine: Engine) => {
const findStartNodeHandler = (target: HTMLElement): TranslateData => {
const handler = target?.closest(`*[${engine.props.nodeTranslateAttrName}]`)
if (handler) {
const type = handler.getAttribute(engine.props.nodeTranslateAttrName)
if (type) {
const element = handler.closest(
`*[${engine.props.nodeSelectionIdAttrName}]`
) as HTMLElement
if (element) {
const nodeId = element.getAttribute(
engine.props.nodeSelectionIdAttrName
)
if (nodeId) {
const node = engine.findNodeById(nodeId)
if (node) {
return { node, element }
}
}
}
}
}
return
}

const store: TranslateStore = {}

engine.subscribeTo(DragStartEvent, (event) => {
if (engine.cursor.type !== CursorType.Move) return
const target = event.data.target as HTMLElement
const data = findStartNodeHandler(target)
if (data) {
const point = new Point(event.data.clientX, event.data.clientY)
store.value = {
...data,
point,
}
}
})

engine.subscribeTo(DragMoveEvent, (event) => {
if (engine.cursor.type !== CursorType.Move) return
if (store.value) {
const { node, element, point } = store.value
const allowTranslate = node.allowTranslate()
if (!allowTranslate) return
const translatable = node.designerProps.translatable
const current = new Point(event.data.clientX, event.data.clientY)
const diffX = current.x - point?.x
const diffY = current.y - point?.y
const horizontal = translatable.x?.(node, element, diffX)
const vertical = translatable.y?.(node, element, diffY)
horizontal.translate()
vertical.translate()
store.value.point = current
}
})

engine.subscribeTo(DragStopEvent, () => {
if (engine.cursor.type !== CursorType.Move) return
if (store.value) {
store.value = null
}
})
}
1 change: 1 addition & 0 deletions packages/core/src/models/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export class Engine extends Event {
nodeDragHandlerAttrName: 'data-designer-node-handler',
nodeResizeHandlerAttrName: 'data-designer-node-resize-handler',
outlineNodeIdAttrName: 'data-designer-outline-node-id',
nodeTranslateAttrName: 'data-designer-node-translate-handler',
defaultScreenType: ScreenType.PC,
}
}
7 changes: 7 additions & 0 deletions packages/core/src/models/TreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,13 @@ export class TreeNode {
return ['y']
}

allowTranslate(): boolean {
if (this === this.root && !this.isSourceNode) return false
const { translatable } = this.designerProps
if (translatable?.x && translatable?.y) return true
return false
}

allowDelete() {
if (this === this.root) return false
return this.designerProps.deletable ?? true
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
useWorkspaceEffect,
useFreeSelectionEffect,
useContentEditableEffect,
useTranslateEffect,
} from './effects'
import {
SelectNodes,
Expand Down Expand Up @@ -44,6 +45,7 @@ export const DEFAULT_EFFECTS = [
useWorkspaceEffect,
useFreeSelectionEffect,
useContentEditableEffect,
useTranslateEffect,
]

export const DEFAULT_DRIVERS = [
Expand Down
19 changes: 19 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type IEngineProps<T = Event> = IEventProps<T> & {
nodeSelectionIdAttrName?: string //节点工具栏属性名
nodeDragHandlerAttrName?: string //节点拖拽手柄属性名
nodeResizeHandlerAttrName?: string //节点尺寸拖拽手柄属性名
nodeTranslateAttrName?: string // 节点自由布局的属性名
defaultComponentTree?: ITreeNode //默认组件树
defaultScreenType?: ScreenType
rootComponentName?: string
Expand Down Expand Up @@ -51,6 +52,23 @@ export type IResizable = {
}
}

export type ITranslate = {
x: (
node: TreeNode,
element: HTMLElement,
diffX: string | number
) => {
translate: () => void
}
y: (
node: TreeNode,
element: HTMLElement,
diffY: string | number
) => {
translate: () => void
}
}

export interface IDesignerProps {
package?: string //npm包名
registry?: string //web npm注册平台地址
Expand All @@ -64,6 +82,7 @@ export interface IDesignerProps {
deletable?: boolean //是否可删除,默认为true
cloneable?: boolean //是否可拷贝,默认为true
resizable?: IResizable
translatable?: ITranslate // 自由布局
inlineChildrenLayout?: boolean //子节点内联,用于指定复杂布局容器,强制内联
selfRenderChildren?: boolean //是否自己渲染子节点
propsSchema?: ISchema //Formily JSON Schema
Expand Down
11 changes: 11 additions & 0 deletions packages/react/src/icons/freemove.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'

export const FreeMove = (
<svg viewBox="0 0 1024 1024">
<path
d="M469.333333 938.666667c-17.066667 0-29.866667-8.533333-42.666666-17.066667l-209.066667-179.2 29.866667-34.133333c8.533333-8.533333 21.333333-12.8 29.866666-12.8h8.533334L426.666667 768V341.333333c0-25.6 17.066667-42.666667 42.666666-42.666666s42.666667 17.066667 42.666667 42.666666v192l51.2 4.266667 209.066667 93.866667c21.333333 8.533333 38.4 34.133333 38.4 55.466666v187.733334c0 34.133333-29.866667 64-64 64H469.333333z m128-640V42.666667l128 128-128 128z m0-85.333334H341.333333v85.333334L213.333333 170.666667l128-128v85.333333h256v85.333333z"
p-id="13294"
fill="#ffffff"
></path>
</svg>
)
1 change: 1 addition & 0 deletions packages/react/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './pc'
export * from './mobile'
export * from './responsive'
export * from './move'
export * from './freemove'
export * from './selection'
export * from './recover'
export * from './flip'
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/widgets/AuxToolWidget/Selection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '../../hooks'
import { observer } from '@formily/reactive-react'
import { TreeNode } from '@designable/core'
import { TranslateHandler } from './TranslateHandler'
export interface ISelectionBoxProps {
node: TreeNode
showHelpers: boolean
Expand Down Expand Up @@ -48,6 +49,7 @@ export const SelectionBox: React.FC<ISelectionBoxProps> = (props) => {
<div {...selectionId} className={prefix} style={createSelectionStyle()}>
<div className={innerPrefix}></div>
<ResizeHandler node={props.node} />
<TranslateHandler node={props.node} />
{props.showHelpers && (
<Helpers {...props} node={props.node} nodeRect={nodeRect} />
)}
Expand Down
29 changes: 29 additions & 0 deletions packages/react/src/widgets/AuxToolWidget/TranslateHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react'
import cls from 'classnames'
import { useDesigner, usePrefix } from '../../hooks'
import { TreeNode } from '@designable/core'
import { IconWidget } from '../IconWidget'

export interface ITranslateHandlerProps {
node: TreeNode
}

export const TranslateHandler: React.FC<ITranslateHandlerProps> = (props) => {
const designer = useDesigner()
const prefix = usePrefix('aux-node-translate-handler')
const createHandler = (value: string) => {
return {
[designer.props.nodeTranslateAttrName]: value,
className: cls(prefix, value),
}
}
const allowTranslate = props.node.allowTranslate()
if (!allowTranslate) return null
return (
<>
<div {...createHandler('translate')}>
<IconWidget infer="FreeMove" />
</div>
</>
)
}
13 changes: 13 additions & 0 deletions packages/react/src/widgets/AuxToolWidget/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,16 @@
cursor: ns-resize;
}
}

.@{prefix-cls}-aux-node-translate-handler {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
width: 40px;
height: 20px;
background: #1890ff;
opacity: 0.5;
pointer-events: all;
}
4 changes: 3 additions & 1 deletion packages/react/src/widgets/ResourceWidget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export const ResourceWidget: React.FC<IResourceWidgetProps> = observer(
data-designer-source-id={node.id}
>
{thumb && <img className={prefix + '-item-thumb'} src={thumb} />}
{icon && (
{icon && React.isValidElement(icon) ? (
<>{icon}</>
) : (
<IconWidget
className={prefix + '-item-icon'}
infer={icon}
Expand Down

0 comments on commit 171c64f

Please sign in to comment.