Skip to content

Commit

Permalink
feat: node port
Browse files Browse the repository at this point in the history
  • Loading branch information
bubkoo committed Feb 19, 2020
1 parent 0325a0d commit 4b7b16a
Show file tree
Hide file tree
Showing 3 changed files with 863 additions and 0 deletions.
299 changes: 299 additions & 0 deletions packages/x6/src/research/core/port-data.ts
@@ -0,0 +1,299 @@
import { Size } from '../../types'
import { Attribute } from '../attr'
import { JSONObject, JSONExt } from '../../util'
import { Point, Rectangle } from '../../geometry'
import { PortLayout } from './port-layout'
import { PortLabelLayout } from './port-label-layout'

export class PortData {
ports: PortData.Port[]
groups: { [name: string]: PortData.Group }

constructor(data: PortData.Data) {
this.ports = []
this.groups = {}
this.init(JSONExt.deepCopy(data as any))
}

getPorts() {
return this.ports
}

getGroup(groupName: string): PortData.Group {
return this.groups[groupName] || {}
}

getPortsByGroup(groupName: string): PortData.Port[] {
return this.ports.filter(port => port.group === groupName)
}

getGroupPortsMetrics(groupName: string, elemBBox: Rectangle) {
const group = this.getGroup(groupName)
const ports = this.getPortsByGroup(groupName)

const groupPosition = group.position
const groupPositionName = groupPosition.name

const layoutFn =
(groupPositionName && (PortLayout as any)[groupPositionName]) ||
PortLayout.left

const portsArgs = ports.map(
port => (port && port.position && port.position.args) || {},
)
const groupArgs = groupPosition.args || {}
const results: PortLayout.Result[] = layoutFn(
portsArgs,
elemBBox,
groupArgs,
)

const accumulator: PortData.Metadata = {
ports,
result: [],
}

results.reduce((memo, portLayout, index) => {
const port = memo.ports[index]
memo.result.push({
portLayout,
portId: port.id,
portSize: port.size,
portAttrs: port.attrs,
portLabelLayout: this.getPortLabelLayout(
port,
Point.create(portLayout),
elemBBox,
),
})
return memo
}, accumulator)

return accumulator.result
}

protected init(data: PortData.Data) {
const { groups, items } = data

if (groups != null) {
Object.keys(groups).forEach(key => {
this.groups[key] = this.evaluateGroup(groups[key])
})
}

if (Array.isArray(items)) {
items.forEach(item => {
this.ports.push(this.evaluatePort(item))
})
}
}

protected evaluateGroup(group: PortData.GroupData) {
return {
...group,
label: this.getLabel(group, true),
position: this.getPortPosition(group.position, true),
} as PortData.Group
}

protected evaluatePort(port: PortData.PortData) {
const result = { ...port } as PortData.Port
const group = this.getGroup(port.group)

result.markup = result.markup || group.markup
result.attrs = { ...group.attrs, ...result.attrs }
result.position = this.createPosition(group, result)
result.label = { ...group.label, ...this.getLabel(result) }
result.zIndex = this.getZIndex(group, result)
result.size = Object.assign({}, group.size, result.size)

return result
}

protected getZIndex(group: PortData.Group, port: PortData.PortData) {
if (typeof port.zIndex === 'number') {
return port.zIndex
}

if (typeof group.zIndex === 'number' || group.zIndex === 'auto') {
return group.zIndex
}

return 'auto'
}

protected createPosition(group: PortData.Group, port: PortData.PortData) {
return {
name: 'left',
...group.position,
args: port.args,
} as PortData.PortPosition
}

protected getPortPosition(
position?: PortData.PortPositionData,
setDefault: boolean = false,
): PortData.PortPosition {
if (position == null) {
if (setDefault) {
return { name: 'left', args: {} }
}
} else {
if (typeof position === 'string') {
return {
name: position,
args: {},
}
}

if (typeof position === 'function') {
return {
name: 'fn',
args: { fn: position },
}
}

if (Array.isArray(position)) {
return {
name: 'absolute',
args: { x: position[0], y: position[1] },
}
}

if (typeof position === 'object') {
return position
}
}

return { args: {} }
}

protected getPortLabelPosition(
position?: PortData.PortLabelPositionData,
setDefault: boolean = false,
): PortData.PortLabelPosition {
if (position == null) {
if (setDefault) {
return { name: 'left', args: {} }
}
} else {
if (typeof position === 'string') {
return {
name: position,
args: {},
}
}

if (typeof position === 'object') {
return position
}
}

return { args: {} }
}

protected getLabel(item: PortData.GroupData, setDefaults: boolean = false) {
const label = item.label || {}
label.position = this.getPortLabelPosition(label.position, setDefaults)
return label as PortData.Label
}

protected getPortLabelLayout(
port: PortData.Port,
portPosition: Point,
elemBBox: Rectangle,
) {
const layoutFn = PortLabelLayout[port.label.position.name || 'left']
if (layoutFn) {
return layoutFn(portPosition, elemBBox, port.label.position.args)
}

return null
}
}

export namespace PortData {
export interface Data {
groups?: { [name: string]: GroupData }
items: PortData[]
}

export interface PortPosition<
T extends PortLayout.LayoutNames = PortLayout.LayoutNames
> {
name?: T
args: PortLayout.LayoutArgs[T]
}

export type PortPositionData =
| PortLayout.LayoutNames
| Point.PointData // absolute layout
| PortPosition
| PortLayout.CustomLayoutFunction

export interface PortLabelPosition<
T extends PortLabelLayout.LayoutNames = PortLabelLayout.LayoutNames
> {
name?: T
args: PortLabelLayout.LayoutArgs[T]
}

export type PortLabelPositionData =
| PortLabelLayout.LayoutNames
| PortLabelPosition

export interface LabelData {
markup?: string
position?: PortLabelPositionData
}

export interface Label {
markup: string
position: PortLabelPosition
}

interface Common {
markup: string
attrs: Attribute.CellAttributes
zIndex: number | 'auto'
size: Size
}

interface PortBase {
id?: string
group: string
/**
* Arguments for the port layout function.
*/
args?: JSONObject
}

export interface GroupData extends Partial<Common> {
label?: LabelData
position?: PortPositionData
}

export interface Group extends Partial<Common> {
label: Label
position: PortPosition
}

export interface PortData extends Partial<Common>, PortBase {
label?: LabelData
}

export interface Port extends Group, PortBase {}

export interface LayoutResult {
portId?: string
portAttrs?: Attribute.CellAttributes
portSize?: Size
portLayout: PortLayout.Result
portLabelLayout: PortLabelLayout.Result | null
}

export interface Metadata {
ports: Port[]
result: LayoutResult[]
}
}

0 comments on commit 4b7b16a

Please sign in to comment.