UNPKG

@antv/x6

Version:

JavaScript diagramming library that uses SVG and HTML for rendering

303 lines (258 loc) 7.3 kB
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 } }