@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
303 lines (258 loc) • 7.3 kB
text/typescript
import { type JSONObject, type KeyValue, ObjectExt, type Size } from '../common'
import { Point, type Rectangle } from '../geometry'
import {
type CellAttrs,
type PortLabelLayoutManualItem,
type PortLabelLayoutNativeItem,
type PortLabelLayoutNativeNames,
type PortLabelLayoutResult,
type PortLayoutDefinition,
type PortLayoutManualItem,
type PortLayoutNativeItem,
type PortLayoutNativeNames,
type PortLayoutResult,
portLabelLayoutPresets,
portLabelLayoutRegistry,
portLayoutPresets,
portLayoutRegistry,
} from '../registry'
import type { MarkupType } from '../view/markup'
import type { PointData } from '../types'
export interface Metadata {
groups?: { [name: string]: GroupMetadata }
items: PortMetadata[]
}
export type PortPosition =
| Partial<PortLayoutNativeItem>
| Partial<PortLayoutManualItem>
export type PortPositionMetadata =
| PortLayoutNativeNames
| Exclude<string, PortLayoutNativeNames>
| PointData // absolute layout
| PortPosition
export type PortLabelPosition =
| Partial<PortLabelLayoutNativeItem>
| Partial<PortLabelLayoutManualItem>
export type PortLabelPositionMetadata =
| PortLabelLayoutNativeNames
| Exclude<string, PortLabelLayoutNativeNames>
| PortLabelPosition
export interface LabelMetadata {
markup?: MarkupType
size?: Size
position?: PortLabelPositionMetadata
}
export interface Label {
markup: string
size?: Size
position: PortLabelPosition
}
interface Common {
markup: MarkupType
attrs: CellAttrs
zIndex: number | 'auto'
size?: Size
}
export interface GroupMetadata extends Partial<Common>, KeyValue {
label?: LabelMetadata
position?: PortPositionMetadata
}
export interface Group extends Partial<Common> {
label: Label
position: PortPosition
}
interface PortBase {
group?: string
/**
* Arguments for the port layout function.
*/
args?: JSONObject
}
export interface PortMetadata extends Partial<Common>, PortBase, KeyValue {
id?: string
label?: LabelMetadata
}
export interface Port extends Group, PortBase {
id: string
}
export interface LayoutResult {
portId: string
portAttrs?: CellAttrs
portSize?: Size
portLayout: PortLayoutResult
labelSize?: Size
labelLayout: PortLabelLayoutResult | null
}
export class PortManager {
ports: Port[]
groups: { [name: string]: Group }
constructor(data: Metadata) {
this.ports = []
this.groups = {}
this.init(ObjectExt.cloneDeep(data))
}
getPorts() {
return this.ports
}
getGroup(groupName?: string | null) {
return groupName != null ? this.groups[groupName] : null
}
getPortsByGroup(groupName?: string): Port[] {
return this.ports.filter(
(p) => p.group === groupName || (p.group == null && groupName == null),
)
}
getPortsLayoutByGroup(groupName: string | undefined, elemBBox: Rectangle) {
const ports = this.getPortsByGroup(groupName)
const group = groupName ? this.getGroup(groupName) : null
const groupPosition = group ? group.position : null
const groupPositionName = groupPosition ? groupPosition.name : null
let layoutFn: PortLayoutDefinition<any>
if (groupPositionName != null) {
const fn = portLayoutRegistry.get(groupPositionName)
if (fn == null) {
return portLayoutRegistry.onNotFound(groupPositionName)
}
layoutFn = fn
} else {
layoutFn = portLayoutPresets.left
}
const portsArgs = ports.map(
(port) => (port && port.position && port.position.args) || {},
)
const groupArgs = (groupPosition && groupPosition.args) || {}
const layouts = layoutFn(portsArgs, elemBBox, groupArgs)
return layouts.map<LayoutResult>((portLayout, index) => {
const port = ports[index]
return {
portLayout,
portId: port.id!,
portSize: port.size,
portAttrs: port.attrs,
labelSize: port.label.size,
labelLayout: this.getPortLabelLayout(
port,
Point.create(portLayout.position),
elemBBox,
),
}
})
}
protected init(data: Metadata) {
const { groups, items } = data
if (groups != null) {
Object.keys(groups).forEach((key) => {
this.groups[key] = this.parseGroup(groups[key])
})
}
if (Array.isArray(items)) {
items.forEach((item) => {
this.ports.push(this.parsePort(item) as unknown as Port)
})
}
}
protected parseGroup(group: GroupMetadata) {
return {
...group,
label: this.getLabel(group, true),
position: this.getPortPosition(group.position, true),
} as Group
}
protected parsePort(port: PortMetadata) {
const result = { ...port }
const group = this.getGroup(port.group) || ({} as Group)
result.markup = result.markup || group.markup
result.attrs = ObjectExt.merge({}, group.attrs, result.attrs)
result.position = this.createPosition(group, result)
result.label = ObjectExt.merge({}, group.label, this.getLabel(result))
result.zIndex = this.getZIndex(group, result)
result.size = { ...group.size, ...result.size } as Size
return result
}
protected getZIndex(group: Group, port: PortMetadata) {
if (typeof port.zIndex === 'number') {
return port.zIndex
}
if (typeof group.zIndex === 'number' || group.zIndex === 'auto') {
return group.zIndex
}
return 'auto'
}
protected createPosition(group: Group, port: PortMetadata) {
return ObjectExt.merge(
{
name: 'left',
args: {},
},
group.position,
{ args: port.args },
) as PortPosition
}
protected getPortPosition(
position?: PortPositionMetadata,
setDefault = false,
): PortPosition {
if (position == null) {
if (setDefault) {
return { name: 'left', args: {} }
}
} else {
if (typeof position === 'string') {
return {
name: position,
args: {},
}
}
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?: PortLabelPositionMetadata,
setDefault = false,
): 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: GroupMetadata, setDefaults = false) {
const label = item.label || {}
label.position = this.getPortLabelPosition(label.position, setDefaults)
return label as Label
}
protected getPortLabelLayout(
port: Port,
portPosition: Point,
elemBBox: Rectangle,
) {
const name = port.label.position.name || 'left'
const args = port.label.position.args || {}
const layoutFn =
portLabelLayoutRegistry.get(name) || portLabelLayoutPresets.left
if (layoutFn) {
return layoutFn(portPosition, elemBBox, args)
}
return null
}
}