UNPKG

d3-org-chart-cthacker-fork

Version:
753 lines (684 loc) 22.1 kB
import { HierarchyNode, StratifyOperator } from "d3-hierarchy"; import { BaseType, Selection, ValueFn } from "d3-selection"; import { DefaultLinkObject, Link } from "d3-shape"; import { ZoomBehavior, ZoomTransform } from "d3-zoom"; export type NodeId = string | number; export interface Connection { from: NodeId; to: NodeId; label: string; } export interface Point { x: number; y: number; } /** * The configuration attributes of an organization charts. * All of these properties are available as get / set pairs * of the organization chart object, per D3 standard. */ export interface StatePublic<Datum> { /** * Id for event handling */ id: string; /** * Display debugging info */ debug: boolean; /** * Whether chart is drawn for the first time */ firstDraw: boolean; /** * Configure svg width. default is: 800 */ svgWidth: number; /** * Configure svg height. default is: window.innerHeight - 100 */ svgHeight: number; /** * Configure zoom scale extent , if you don't want any kind of zooming, set it to [1,1] */ scaleExtent: [number, number]; /** * Set parent container, either CSS style selector or DOM element, for example "#my-chart" */ container: string; /** * Set default font, * for example "Helvetica" */ defaultFont: string; ctx: CanvasRenderingContext2D; /** * Set data, it must be an array of objects, where hierarchy is clearly defined via id and parent ID (property names are configurable) */ data: Datum[] | null; /** * Configure duration of transitions */ duration: number; /** * Configure exported PNG and SVG image name */ imageName: string; /** * Configure if active node should be centered when expanded and collapsed */ setActiveNodeCentered: boolean; initialExpandLevel: number; allowedNodesCount: {}; /** * Configure if compact mode is enabled , when enabled, nodes are shown in compact positions, instead of horizontal spread */ compact: boolean; /** * Configure how much root node is offset from top */ rootMargin: number; /** * CSS color, for example "#2C3E50" */ nodeDefaultBackground: string; /** * Sets connection data, array of objects, SAMPLE: [{from:"145",to:"201",label:"Conflicts of interest"}] */ connections: Connection[]; /** * Panning and zooming values */ lastTransform: ZoomTransform; /** * Configure accessor for node id, default is either odeId or id */ nodeId: (node: HierarchyNode<Datum> | Datum) => NodeId | undefined; /** * Configure accessor for parent node id, default is either parentNodeId or parentId */ parentNodeId: (node: HierarchyNode<Datum> | Datum) => NodeId | undefined; /** CSS color, for example "#2C3E50" */ backgroundColor: string; zoomBehavior: ZoomBehavior<Element, Datum>; generateRoot: StratifyOperator<Datum>; svg: Selection<SVGSVGElement, string, null, undefined>; /** Defining arrows with markers for connections */ defs: (state: State<Datum>, visibleConnections: Connection[]) => string; /** * You can update connections with custom styling using this function. * example: * ```javascript * d3.select(this) * .attr("stroke", d => '#E27396') * .attr('stroke-linecap', 'round') * .attr("stroke-width", d => '5') * .attr('pointer-events', 'none') * .attr("marker-start", d => `url(#${d.from + "_" + d.to})`) * .attr("marker-end", d => `url(#arrow-${d.from + "_" + d.to})`) * ``` */ connectionsUpdate: ValueFn<BaseType, Datum, void>; /** * You can access and modify actual link DOM element in runtime using this method. * @param node * @param index * @param nodes */ linkUpdate: ( node: HierarchyNode<Datum>, index: number, nodes: Array<HierarchyNode<Datum>> ) => void; /** * You can access and modify actual node DOM element in runtime using this method. * @param node * @param index * @param nodes */ nodeUpdate: ( node: HierarchyNode<Datum>, index: number, nodes: Array<HierarchyNode<Datum>> ) => void; /** * Custom handling of node update */ nodeEnter: (node: HierarchyNode<Datum>) => void; /** * Custom handling of exit node * @param node */ nodeExit: (node: HierarchyNode<Datum>) => void; /** * Configure each node width, use with caution, it is better to have the same value set for all nodes * @param node */ nodeWidth: (node: HierarchyNode<Datum>) => number; /** * Configure each node height, use with caution, it is better to have the same value set for all nodes * @param node */ nodeHeight: (node: HierarchyNode<Datum>) => number; /** * Configure margin between two siblings, use with caution, it is better to have the same value set for all nodes * @param node */ siblingsMargin: (node: HierarchyNode<Datum>) => number; /** * Configure margin between parent and children, use with caution, it is better to have the same value set for all nodes * @param node */ childrenMargin: (node: HierarchyNode<Datum>) => number; /** * Configure margin between two nodes, use with caution, it is better to have the same value set for all nodes * @param node1 * @param node2 */ neighbourMargin: ( node1: HierarchyNode<Datum>, node2: HierarchyNode<Datum> ) => number; /** * Configure margin between two nodes in compact mode, use with caution, it is better to have the same value set for all nodes * @param node */ compactMarginPair: (node: HierarchyNode<Datum>) => number; /** * Configure margin between two nodes in compact mode, use with caution, it is better to have the same value set for all nodes * @param node */ compactMarginBetween: (node: HierarchyNode<Datum>) => number; /** * Configure expand & collapse button width * @param node */ nodeButtonWidth: (node: HierarchyNode<Datum>) => number; /** * Configure expand & collapse button height * @param node */ nodeButtonHeight: (node: HierarchyNode<Datum>) => number; /** * Configure expand & collapse button x position * @param node */ nodeButtonX: (node: HierarchyNode<Datum>) => number; /** * Configure expand & collapse button y position * @param node */ nodeButtonY: (node: HierarchyNode<Datum>) => number; /** * When correcting links which is not working for safari */ linkYOffset: number; /** * Configure how many nodes to show when making new nodes appear * @param node */ pagingStep: (node: HierarchyNode<Datum>) => number; /** * Configure minimum number of visible nodes , after which paging button appears * @param node */ minPagingVisibleNodes: (node: HierarchyNode<Datum>) => number; createZoom: (node: HierarchyNode<Datum>) => ZoomBehavior<any, Datum>; /** * Callback for zoom & panning start * @param event */ onZoomStart: (event: any) => void; /** * Callback for zoom & panning * @param event */ onZoom: (event: any) => void; /** * Callback for zoom & panning end * @param event */ onZoomEnd: (event: any) => void; /** Callback for node click */ onNodeClick: (node: HierarchyNode<Datum>, event: Event) => void; /** * Callback for node expand or collapse * @param node */ onExpandOrCollapse: (node: HierarchyNode<Datum>) => void; /** * Link generator for connections */ linkGroupArc: Link<any, DefaultLinkObject, [number, number]>; /** * Node HTML content generation , remember that you can access some helper methods: * * node=> node.data - to access node's original data * node=> node.leaves() - to access node's leaves * node=> node.descendants() - to access node's descendants * node=> node.children - to access node's children * node=> node.parent - to access node's parent * node=> node.depth - to access node's depth * node=> node.hierarchyHeight - to access node's hierarchy height ( Height, which d3 assigns to hierarchy nodes) * node=> node.height - to access node's height * node=> node.width - to access node's width * * You can also access additional properties to style your node: * * d=>d.data._centeredWithDescendants - when node is centered with descendants * d=>d.data._directSubordinatesPaging - subordinates count in paging mode * d=>d.data._directSubordinates - subordinates count * d=>d.data._totalSubordinates - total subordinates count * d=>d._highlighted - when node is highlighted * d=>d._upToTheRootHighlighted - when node is highlighted up to the root * d=>d._expanded - when node is expanded * d=>d.data._centered - when node is centered * * @param node * @param index * @param nodes * @param state */ nodeContent: ( node: HierarchyNode<Datum>, index: number, nodes: Array<HierarchyNode<Datum>>, state: State<Datum> ) => string; /** * Configure layout direction , possible values are "top", "left", "right", "bottom" */ layout: Layout; /** * Node expand & collapse button content and styling. You can access same helper methods as above * @param params */ buttonContent: (params: { node: HierarchyNode<Datum>; state: State<Datum>; }) => string; /** * Node paging button content and styling. You can access same helper methods as above. * @param node * @param index * @param nodes * @param state */ pagingButton: ( node: HierarchyNode<Datum>, index: number, nodes: Array<HierarchyNode<Datum>>, state: State<Datum> ) => string; /** * You can customize/offset positions for each node and link by overriding these functions * For example, suppose you want to move link y position 30 px bellow in top layout. You can do it like this: * ```javascript * const layout = chart.layoutBindings(); * layout.top.linkY = node => node.y + 30; * chart.layoutBindings(layout); * ``` */ layoutBindings: Record<Layout, LayoutBinding<Datum>>; /** * Enables the drag and drop dependent on the node being selected * * @param node - Callback that provides a boolean */ enableDragDrop: () => boolean; /** * Specify the callback that fires when the user begins "dragging" on a node * * @param node - The node being dragged * @param event - The drag event */ onDragStart: (node: HierarchyNode<Datum>, event: any) => void; /** * Specify the callback that fires when the user is within the "drag" process * * @param node - The node currently being dragged * @param event - The drag event */ onDrag: (node: HierarchyNode<Datum>, event: any) => void; /** * Specify the callback that fires when the user finishes the "drag" process, and releases their mouse to initiate the "drop" * * @param node - The node that was dragged * @param targetNode - The node that is receiving the "dropping" action from the dragged node * @param dragEvent - The drag event */ onDrop: ( node: HierarchyNode<Datum>, targetNode: HierarchyNode<Datum>, event: any ) => void; /** * Specify the callback that fires when the user drags a node over another node * * @param dragNode - The node being dragged * @param targetNode - The node receiving the "dragover" event * @param dragEvent - The drag event */ onDragTarget: ( dragNode: HierarchyNode<Datum>, targetNode: HierarchyNode<Datum>, event: any ) => void; /** * Specify the callback for the "dragover" event for the target node * * @param dragNode - The node being dragged * @param targetNode - The node that is losing the "dragover" event * @param dragEvent - The drag event */ outDragTarget: ( dragNode: HierarchyNode<Datum>, targetNode: HierarchyNode<Datum>, dragEvent: any ) => void; /** * Specifies the callback that fires when a node enters the drop zone * * @param _callback - The callback to fire */ nodeEnter: () => void; /** * Whether or not to allow the drag to start * * @param node - The node about to be dragged * @param dragEvent - The drag event */ onDragFilter: (node: HierarchyNode<Datum>, event: any) => void; /** * Set the dragging node's className (defaults to 'dragging') * * @param classNameCallback - Callback that returns className of "dragging" nodes */ draggingClass: () => string; /** * Set the draggable node's className (defaults to 'draggable') * * @param classNameCallback - Callback that returns className of "draggable" nodes */ draggableClass: () => string; /** * Set the droppable node's className * * @param classNameCallback - Callback that returns className of "droppable" nodes */ droppableClass: () => string; } /** * Properties which are available on the State object, but not as get / set pairs. * These are more internal in scope, but can be used when overriding functions. */ export interface StateInternal<Datum> { readonly root: HierarchyNode<Datum>; readonly allNodes: ReadonlyArray<HierarchyNode<Datum>>; } export interface State<Datum> extends StatePublic<Datum>, StateInternal<Datum> { [key: string]: any; } export type Layout = "left" | "bottom" | "right" | "top"; export interface LayoutBinding<Datum> { nodeLeftX: (node: HierarchyNode<Datum>) => number; nodeRightX: (node: HierarchyNode<Datum>) => number; nodeTopY: (node: HierarchyNode<Datum>) => number; nodeBottomY: (node: HierarchyNode<Datum>) => number; nodeJoinX: (node: HierarchyNode<Datum>) => number; nodeJoinY: (node: HierarchyNode<Datum>) => number; linkJoinX: (node: HierarchyNode<Datum>) => number; linkJoinY: (node: HierarchyNode<Datum>) => number; linkX: (node: HierarchyNode<Datum>) => number; linkY: (node: HierarchyNode<Datum>) => number; linkCompactXStart: (node: HierarchyNode<Datum>) => number; linkCompactYStart: (node: HierarchyNode<Datum>) => number; compactLinkMidX: (node: HierarchyNode<Datum>, state: State<Datum>) => number; compactLinkMidY: (node: HierarchyNode<Datum>, state: State<Datum>) => number; linkParentX: (node: HierarchyNode<Datum>) => number; linkParentY: (node: HierarchyNode<Datum>) => number; buttonX: (node: HierarchyNode<Datum>) => number; buttonY: (node: HierarchyNode<Datum>) => number; /** Returns a CSS transform */ centerTransform: (params: { root: number; rootMargin: number; centerY: number; scale: number; centerX: number; chartWidth: number; chartHeight: number; }) => string; compactDimension: { sizeColumn: (node: HierarchyNode<Datum>) => number; sizeRow: (node: HierarchyNode<Datum>) => number; reverse<T>(a: T[]): T[]; }; nodeFlexSize: (params: { height: number; width: number; siblingsMargin: number; childrenMargin: number; state: State<Datum>; node: HierarchyNode<Datum>; }) => [number, number]; zoomTransform: (params: { centerY: number; scale: number }) => string; /** Swaps x and y coordinates */ swap: (d: Point) => Point; nodeUpdateTransform: ( params: { width: number; height: number } & Point ) => string; diagonal(source: Point, target: Point, m: Point): string; } // Helper type to remove the need to explicitly declare get / set methods export type StateGetSet<T, TSelf> = { [Property in keyof StatePublic<T>]: () => StatePublic<T>[Property]; } & { [Property in keyof StatePublic<T>]: ( value: StatePublic<T>[Property] ) => TSelf; }; // This is separated from the implementation declaration to not have to replicate the propertied of StateGetSet export interface OrgChart<Datum> extends StateGetSet<Datum, OrgChart<Datum>> {} export class OrgChart<Datum> { constructor(); getChartState(): State<Datum>; /** * This method can be invoked via chart.setZoomFactor API, it zooms to particulat scale * @param zoomLevel */ initialZoom(zoomLevel: number): this; initializeEnterExitUpdatePattern(): void; /** * This method retrieves passed node's children IDs (including node) * @param args * @param nodeStore */ getNodeChildren(args: HierarchyNode<Datum>, nodeStore: Datum[]): Datum[]; render(): this; /** * This function can be invoked via chart.addNode API, and it adds node in tree at runtime * * @param nodePayload - The node payload to add to the chart, must contain `id` field set to `'child'`, and `parentId` field which must be set to a valid `parentId`. * @returns - The modified chart instance */ addNode( nodePayload: Datum & { id?: number | string | "child"; parentId?: number | string; } ): this; /** * Edits the selected node, with the modified payload * * @param existingNodeId - The node to edit, must already exist * @param payload - The payload to override the contents of the node with */ editNode(existingNodeId: number | string, payload: Partial<Datum>): this; /** * This function can be invoked via chart.removeNode API, and it removes node from tree at runtime * @param nodeId */ removeNode(nodeId: NodeId): this; groupBy( array: HTMLCollection, accessor: (node: HierarchyNode<Datum>) => number, aggegator: any ): Array<[string, unknown]>; calculateCompactFlexDimensions(root: HierarchyNode<Datum>): void; calculateCompactFlexPositions(root: HierarchyNode<Datum>): void; /** * This function basically redraws visible graph, based on nodes state * @param params */ update( params: { x0: number; y0: number; width: number; height: number; } & Partial<Point> ): void; /** * This function detects whether current browser is edge */ isEdge(): boolean; /** * Horizontal diagonal generation algorithm - https://observablehq.com/@bumbeishvili/curved-edges-compact-horizontal * @param source * @param target * @param m */ hdiagonal(source: Point, target: Point, m: Point): string; /** * Vertical diagonal generation algorithm - https://observablehq.com/@bumbeishvili/curved-edges-compacty-vertical * @param source * @param target * @param m */ diagonal(source: Point, target: Point, m: Point): string; restyleForeignObjectElements(): void; /** * Toggle children on click. * @param event * @param node */ onButtonClick(event: any, node: HierarchyNode<Datum>): void; /** * This function changes `expanded` property to descendants * @param node * @param isExpanded */ setExpansionFlagToChildren( node: HierarchyNode<Datum>, isExpanded: boolean ): void; /** * Method which only expands nodes, which have property set "expanded=true" * @param node */ expandSomeNodes(node: HierarchyNode<Datum>): void; /** * This function updates nodes state and redraws graph, usually after data change */ updateNodesState(): void; setLayouts(params: { expandNodesFirst?: boolean }): void; /** * Function which collapses passed node and it's descendants * @param node */ collapse(node: HierarchyNode<Datum>): void; /** * Function which expands passed node and it's descendants * @param node */ expand(node: HierarchyNode<Datum>): void; /** * Zoom handler function * @param event * @param node */ zoomed(event: any, node: HierarchyNode<Datum>): void; zoomTreeBounds(params: { x0: number; x1: number; y0: number; y1: number; params: { animate?: boolean; scale?: boolean }; }): void; fit(params?: { animate?: boolean; nodes?: Iterable<HierarchyNode<Datum>>; scale?: boolean; }): this; /** * Load Paging Nodes * @param node */ loadPagingNodes(node: HierarchyNode<Datum>): void; /** * This function can be invoked via chart.setExpanded API, it expands or collapses particular node * @param nodeId * @param isExpanded */ setExpanded(nodeId: NodeId, isExpanded?: boolean): this; setCentered(nodeId: NodeId): this; setHighlighted(nodeId: NodeId): this; setUpToTheRootHighlighted(nodeId: NodeId): this; clearHighlighting(): this; /** * It can take selector which would go fullscreen * @param element */ fullscreen(element?: Element): void; /** * Zoom in exposed method */ zoomIn(): void; /** * Zoom out exposed method */ zoomOut(): void; toDataUrl( url: string, callback: (result: string | ArrayBuffer) => void ): void; exportImg(params?: { full?: boolean; scale?: number; onLoad?: (s: string) => void; save?: boolean; backgroundColor?: string; }): void; exportSvg(): this; expandAll(): this; collapseAll(): this; downloadImage(params?: { node: SVGElement; scale?: number; imageName?: string; isSvg?: boolean; save?: boolean; backgroundColor?: string; onAlreadySerialized?: () => void; onLoad?: (s: string) => void; }): void; /** * Calculate what size text will take * @param text * @param params */ getTextWidth( text: string, params: { fontSize?: number; fontWeight?: number; defaultFont?: string; ctx: CanvasRenderingContext2D; } ): number; /** * Clear after moving off from the page */ clear(): void; }