Skip to content

Commit

Permalink
feat: support resizable to designerProps
Browse files Browse the repository at this point in the history
  • Loading branch information
janryWang committed Dec 2, 2021
1 parent a279713 commit 0215c27
Show file tree
Hide file tree
Showing 17 changed files with 246 additions and 88 deletions.
24 changes: 24 additions & 0 deletions formily/antd/src/components/FormGrid/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ FormGrid.Behavior = createBehavior(
selector: (node) => node.props['x-component'] === 'FormGrid.GridColumn',
designerProps: {
droppable: true,
resizable: {
step: 30,
width(node) {
const span = Number(node.props['x-component-props']?.gridSpan ?? 1)
return {
plus: () => {
if (span + 1 > 12) return
node.props['x-component-props'] =
node.props['x-component-props'] || {}
node.props['x-component-props'].gridSpan = span + 1
},
minus: () => {
if (span - 1 < 1) return
node.props['x-component-props'] =
node.props['x-component-props'] || {}
node.props['x-component-props'].gridSpan = span - 1
},
}
},
},
resizeXPath: 'x-component-props.gridSpan',
resizeStep: 1,
resizeMin: 1,
resizeMax: 12,
allowDrop: (node) => node.props['x-component'] === 'FormGrid',
propsSchema: createFieldSchema(AllSchemas.FormGrid.GridColumn),
},
Expand Down
20 changes: 20 additions & 0 deletions formily/next/src/components/FormGrid/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ FormGrid.Behavior = createBehavior(
selector: (node) => node.props['x-component'] === 'FormGrid.GridColumn',
designerProps: {
droppable: true,
resizable: {
step: 30,
width(node) {
const span = Number(node.props['x-component-props']?.gridSpan ?? 1)
return {
plus: () => {
if (span + 1 > 12) return
node.props['x-component-props'] =
node.props['x-component-props'] || {}
node.props['x-component-props'].gridSpan = span + 1
},
minus: () => {
if (span - 1 < 1) return
node.props['x-component-props'] =
node.props['x-component-props'] || {}
node.props['x-component-props'].gridSpan = span - 1
},
}
},
},
allowDrop: (node) => node.props['x-component'] === 'FormGrid',
propsSchema: createFieldSchema(AllSchemas.FormGrid.GridColumn),
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/effects/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './useCursorEffect'
export * from './useViewportEffect'
export * from './useDragDropEffect'
export * from './useResizeEffect'
export * from './useSelectionEffect'
export * from './useFreeSelectionEffect'
export * from './useKeyboardEffect'
Expand Down
8 changes: 6 additions & 2 deletions packages/core/src/effects/useDragDropEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ export const useDragDropEffect = (engine: Engine) => {
const handler = target?.closest(
`*[${engine.props.nodeDragHandlerAttrName}]`
)
const helper = handler?.closest(`*[${engine.props.nodeHelpersIdAttrName}]`)
const helper = handler?.closest(
`*[${engine.props.nodeSelectionIdAttrName}]`
)
if (!el?.getAttribute && !handler) return
const sourceId = el?.getAttribute(engine.props.sourceIdAttrName)
const outlineId = el?.getAttribute(engine.props.outlineNodeIdAttrName)
const handlerId = helper?.getAttribute(engine.props.nodeHelpersIdAttrName)
const handlerId = helper?.getAttribute(engine.props.nodeSelectionIdAttrName)
const nodeId = el?.getAttribute(engine.props.nodeIdAttrName)
engine.workbench.eachWorkspace((currentWorkspace) => {
const operation = currentWorkspace.operation
Expand All @@ -50,6 +52,7 @@ export const useDragDropEffect = (engine: Engine) => {
}
}
})
engine.cursor.setStyle('move')
})

engine.subscribeTo(DragMoveEvent, (event) => {
Expand Down Expand Up @@ -159,5 +162,6 @@ export const useDragDropEffect = (engine: Engine) => {
}
operation.dragClean()
})
engine.cursor.setStyle('')
})
}
119 changes: 119 additions & 0 deletions packages/core/src/effects/useResizeEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { Engine, CursorType } from '../models'
import { DragStartEvent, DragMoveEvent, DragStopEvent } from '../events'
import { TreeNode } from '../models'
import { Point } from '@designable/shared'

type ResizeData = {
element?: Element
node?: TreeNode
axis?: 'x' | 'y' | (string & {})
type?: 'x-start' | 'x-end' | 'y-start' | 'y-end' | (string & {})
start?: Point
point?: Point
xIndex?: number
yIndex?: number
}

type ResizeStore = {
value?: ResizeData
}

export const useResizeEffect = (engine: Engine) => {
const findStartNodeHandler = (target: HTMLElement): ResizeData => {
const handler = target?.closest(
`*[${engine.props.nodeResizeHandlerAttrName}]`
)
if (handler) {
const type = handler.getAttribute(engine.props.nodeResizeHandlerAttrName)
if (type) {
const element = handler.closest(
`*[${engine.props.nodeSelectionIdAttrName}]`
)
if (element) {
const nodeId = element.getAttribute(
engine.props.nodeSelectionIdAttrName
)
if (nodeId) {
const node = engine.findNodeById(nodeId)
if (node) {
const axis = type.includes('x') ? 'x' : 'y'
return { axis, type, node, element, xIndex: 0, yIndex: 0 }
}
}
}
}
}
return
}

const store: ResizeStore = {}

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 start = new Point(event.data.clientX, event.data.clientY)
store.value = {
...data,
start,
point: start,
}
if (data.axis === 'x') {
engine.cursor.setStyle('ew-resize')
} else if (data.axis === 'y') {
engine.cursor.setStyle('ns-resize')
}
}
})

engine.subscribeTo(DragMoveEvent, (event) => {
if (engine.cursor.type !== CursorType.Move) return
if (store.value) {
const { axis, type, node, element, point, start } = store.value
const allowResize = node.allowResize()
if (!allowResize) return
const resizable = node.designerProps.resizable
const step = resizable.step ?? 1
const current = new Point(event.data.clientX, event.data.clientY)
const xIndex = Math.floor((current.x - start.x) / step)
const yIndex = Math.floor((current.x - start.x) / step)
const plusX = type === 'x-end' ? current.x > point.x : current.x < point.x
const plusY = type === 'y-end' ? current.y > point.y : current.y < point.y
const allowX = allowResize.includes('x')
const allowY = allowResize.includes('y')
const width = resizable.width?.(node, element)
const height = resizable.height?.(node, element)
if (axis === 'x') {
if (xIndex === store.value.xIndex) return
if (allowX) {
if (plusX) {
width.plus()
} else {
width.minus()
}
}
} else if (axis === 'y') {
if (yIndex === store.value.yIndex) return
if (allowY) {
if (plusY) {
height.plus()
} else {
height.minus()
}
}
}
store.value.point = current
store.value.xIndex = xIndex
store.value.yIndex = yIndex
}
})

engine.subscribeTo(DragStopEvent, () => {
if (engine.cursor.type !== CursorType.Move) return
if (store.value) {
store.value = null
engine.cursor.setStyle('')
}
})
}
2 changes: 1 addition & 1 deletion packages/core/src/effects/useSelectionEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const useSelectionEffect = (engine: Engine) => {
*[${engine.props.outlineNodeIdAttrName}]
`)
const isHelpers = target?.closest?.(
`*[${engine.props.nodeHelpersIdAttrName}]`
`*[${engine.props.nodeSelectionIdAttrName}]`
)
const currentWorkspace = engine.workbench.activeWorkspace
if (!currentWorkspace) return
Expand Down
21 changes: 18 additions & 3 deletions packages/core/src/models/Cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ export enum CursorStatus {
export enum CursorType {
Move = 'MOVE',
Selection = 'SELECTION',
Resize = 'RESIZE',
ResizeWidth = 'RESIZE_WIDTH',
ResizeHeight = 'RESIZE_HEIGHT',
}

export interface ICursorPosition {
Expand Down Expand Up @@ -67,6 +64,17 @@ const DEFAULT_SCROLL_OFFSET = {
scrollY: 0,
}

const setCursorStyle = (contentWindow: Window, style: string) => {
const currentRoot = document?.getElementsByTagName?.('html')?.[0]
const root = contentWindow?.document?.getElementsByTagName('html')?.[0]
if (root && root.style.cursor !== style) {
root.style.cursor = style
}
if (currentRoot && currentRoot.style.cursor !== style) {
currentRoot.style.cursor = style
}
}

export class Cursor {
engine: Engine

Expand Down Expand Up @@ -101,6 +109,7 @@ export class Cursor {
dragEndPosition: observable.ref,
dragEndScrollOffset: observable.ref,
view: observable.ref,
setStyle: action,
setPosition: action,
setStatus: action,
setType: action,
Expand All @@ -115,6 +124,12 @@ export class Cursor {
this.type = type
}

setStyle(style: string) {
this.engine.workbench.eachWorkspace((workspace) => {
setCursorStyle(workspace.viewport.contentWindow, style)
})
}

setPosition(position?: ICursorPosition) {
this.position = {
...this.position,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/models/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class Engine extends Event {
contentEditableAttrName: 'data-content-editable',
contentEditableNodeIdAttrName: 'data-content-editable-node-id',
clickStopPropagationAttrName: 'data-click-stop-propagation',
nodeHelpersIdAttrName: 'data-designer-node-helpers-id',
nodeSelectionIdAttrName: 'data-designer-node-helpers-id',
nodeDragHandlerAttrName: 'data-designer-node-handler',
nodeResizeHandlerAttrName: 'data-designer-node-resize-handler',
outlineNodeIdAttrName: 'data-designer-outline-node-id',
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/models/TreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,13 @@ export class TreeNode {
return this.designerProps.draggable ?? true
}

allowResize() {
allowResize(): false | Array<'x' | 'y'> {
if (this === this.root && !this.isSourceNode) return false
return (
(this.designerProps.resizeXPath || this.designerProps.resizeYPath) ??
false
)
const { resizable } = this.designerProps
if (!resizable) return false
if (resizable.width && resizable.height) return ['x', 'y']
if (resizable.width) return ['x']
return ['y']
}

allowDelete() {
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 @@ -11,6 +11,7 @@ import {
useViewportEffect,
useDragDropEffect,
useSelectionEffect,
useResizeEffect,
useKeyboardEffect,
useAutoScrollEffect,
useWorkspaceEffect,
Expand All @@ -36,6 +37,7 @@ export const DEFAULT_EFFECTS = [
useCursorEffect,
useViewportEffect,
useDragDropEffect,
useResizeEffect,
useSelectionEffect,
useKeyboardEffect,
useAutoScrollEffect,
Expand Down
26 changes: 20 additions & 6 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type IEngineProps<T = Event> = IEventProps<T> & {
contentEditableNodeIdAttrName?: string //原地编辑指定Node Id属性名
clickStopPropagationAttrName?: string //点击阻止冒泡属性
outlineNodeIdAttrName?: string //大纲树节点ID的dom属性名
nodeHelpersIdAttrName?: string //节点工具栏属性名
nodeSelectionIdAttrName?: string //节点工具栏属性名
nodeDragHandlerAttrName?: string //节点拖拽手柄属性名
nodeResizeHandlerAttrName?: string //节点尺寸拖拽手柄属性名
defaultComponentTree?: ITreeNode //默认组件树
Expand All @@ -34,6 +34,24 @@ export type IEngineContext = {
viewport: Viewport
}

export type IResizable = {
width?: (
node: TreeNode,
element: Element
) => {
plus: () => void
minus: () => void
}
height?: (
node: TreeNode,
element: Element
) => {
plus: () => void
minus: () => void
}
step?: number
}

export interface IDesignerProps {
package?: string //npm包名
registry?: string //web npm注册平台地址
Expand All @@ -46,11 +64,7 @@ export interface IDesignerProps {
draggable?: boolean //是否可拖拽,默认为true
deletable?: boolean //是否可删除,默认为true
cloneable?: boolean //是否可拷贝,默认为true
resizeXPath?: string //尺寸X轴属性修改路径
resizeYPath?: string //尺寸Y轴属性修改路径
resizeStep?: number //尺寸拖拽步数
resizeMin?: number //尺寸拖拽最小值
resizeMax?: number //尺寸拖拽最大值
resizable?: IResizable
inlineChildrenLayout?: boolean //子节点内联,用于指定复杂布局容器,强制内联
selfRenderChildren?: boolean //是否自己渲染子节点
propsSchema?: ISchema //Formily JSON Schema
Expand Down
10 changes: 8 additions & 2 deletions packages/react/src/simulators/ResponsiveSimulator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ const useResizeEffect = (
let animationX = null
let animationY = null

const getStyle = (status: ResizeHandleType) => {
if (status === ResizeHandleType.Resize) return 'nwse-resize'
if (status === ResizeHandleType.ResizeHeight) return 'ns-resize'
if (status === ResizeHandleType.ResizeWidth) return 'ew-resize'
}

const updateSize = (deltaX: number, deltaY: number) => {
const containerRect = container.current?.getBoundingClientRect()
if (status === ResizeHandleType.Resize) {
Expand Down Expand Up @@ -63,7 +69,7 @@ const useResizeEffect = (
status = target.getAttribute(
'data-designer-resize-handle'
) as ResizeHandleType
engine.cursor.setType(status)
engine.cursor.setStyle(getStyle(status))
startX = e.data.topClientX
startY = e.data.topClientY
startWidth = rect.width
Expand Down Expand Up @@ -108,7 +114,7 @@ const useResizeEffect = (
engine.subscribeTo(DragStopEvent, () => {
if (!status) return
status = null
engine.cursor.setType(CursorType.Move)
engine.cursor.setStyle('')
if (animationX) {
animationX = animationX()
}
Expand Down
Loading

0 comments on commit 0215c27

Please sign in to comment.