// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ Copyright (c) 2017, the Perspective Authors. ┃ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ // ┃ This file is part of the Perspective library, distributed under the terms ┃ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ import { drawArc, arcVisible } from "./sunburstArc"; import { labelVisible, labelTransform, cropLabel } from "./sunburstLabel"; import { clickHandler } from "./sunburstClick"; import { D3Scale, Settings } from "../../types"; export interface SunburstData extends d3.HierarchyNode { children: this[]; crossValue: string[]; current: this; data: { name: string; children: d3.HierarchyNode[]; }; depth: number; height: number; key: string; label: any; // is NaN, but should this be string? mainValues: number; parent: any; value: number; x0: number; x1: number; y0: number; y1: number; } export interface SunburstSeries { (sunburstElement: any): void; settings(): Settings; settings(settings: Settings): SunburstSeries; split(): string; split(split: string): SunburstSeries; data(): SunburstData; data(data: SunburstData): SunburstSeries; color(): D3Scale; color(color: D3Scale): SunburstSeries; radius(): number; radius(radius: number): SunburstSeries; } export function sunburstSeries(): SunburstSeries { let settings: Settings | null = null; let split: string | null = null; let data: SunburstData | null = null; let color: D3Scale | null = null; let radius = null; const _sunburstSeries: Partial = (sunburstElement) => { const segment = sunburstElement .selectAll("g.segment") .data(data.descendants().slice(1)); const segmentEnter = segment .enter() .append("g") .attr("class", "segment"); segmentEnter.append("path"); segmentEnter .append("text") .attr("class", "segment") .attr("dy", "0.35em"); const segmentMerge = segmentEnter.merge(segment); const path = segmentMerge .select("path") .attr("fill-opacity", (d) => (arcVisible(d.current) ? 1 : 0)) .attr("user-select", (d) => arcVisible(d.current) ? "initial" : "none", ) .style("pointer-events", (d) => arcVisible(d.current) ? "initial" : "none", ) .attr("d", (d) => drawArc(radius)(d.current)); color && path.style("fill", (d) => color(d.data.color)); const label = segmentMerge .select("text") .attr("fill-opacity", (d) => +labelVisible(d.current)) .attr("transform", (d) => labelTransform(d.current, radius)) .text((d) => d.label) .each(function (d) { cropLabel.call(this, d, radius); }); const parentTitle = sunburstElement.select("text.parent"); const parent = sunburstElement .select("circle") .attr("r", radius) .datum(data); const onClick = clickHandler( data, sunburstElement, parent, parentTitle, path, label, radius, split, settings, ); // NOTE: Double check to make sure that settings.sunburstLevel is a thing. if (settings.sunburstLevel) { const currentLevel = data .descendants() .find((d) => d.data.name === settings.sunburstLevel[split]); currentLevel && onClick(currentLevel, true); } else { settings.sunburstLevel = {}; } parent.on("click", (d) => onClick(d, false)); path.filter((d) => d.children) .style("cursor", "pointer") .on("click", (d) => onClick(d, false)); }; _sunburstSeries.settings = (...args: Settings[]): any => { if (!args.length) { return settings; } settings = args[0]; return _sunburstSeries; }; _sunburstSeries.split = (...args: string[]): any => { if (!args.length) { return split; } split = args[0]; return _sunburstSeries; }; _sunburstSeries.data = (...args: SunburstData[]): any => { if (!args.length) { return data; } data = args[0]; return _sunburstSeries; }; _sunburstSeries.color = (...args: D3Scale[]): any => { if (!args.length) { return color; } color = args[0]; return _sunburstSeries; }; _sunburstSeries.radius = (...args: number[]): any => { if (!args.length) { return radius; } radius = args[0]; return _sunburstSeries; }; return _sunburstSeries as SunburstSeries; }