@apptane/react-ui-charts
Version:
Chart components in Apptane React UI framework
204 lines (169 loc) • 7.8 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
const _excluded = ["pri", "sec", "color"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
import take from "lodash/take";
import { getColorMap, resolveMappedColor, resolvePaletteReference } from "@apptane/react-ui-core";
import { useMemo } from "react";
import { forceCollide, forceSimulation, forceX, forceY } from "d3-force";
import { quantize } from "d3-interpolate";
import { scaleLinear, scaleQuantize } from "d3-scale";
import { getColorInterpolator, mutedColor } from "../common/ColorScheme.js";
import { Domain } from "../common/Types.js";
const ITERATIONS = 60;
function useComputedData(scaleX, compareX, domainXType, scaleY, compareY, domainYType, minDomainZ, maxDomainZ, minExtentZ, maxExtentZ, _ref, exact) {
let {
data,
palette,
colorMode,
colorScheme,
color: colorFn
} = _ref;
return useMemo(() => {
var _ref2, _ref3;
if (data == null || data.length === 0) {
return [undefined, undefined, undefined, undefined];
}
const count = data.length; // minimum number of colors to quantize the spectrum into
const MIN_SHADES = 5;
const back = colorMode === "dark" ? 16 : 255;
let colorScale;
if (colorScheme != null) {
const interpolator = getColorInterpolator(palette, colorScheme);
const reduced = t => interpolator(t * 0.8 + 0.1); // reduce range to avoid too light/dark colors
colorScale = scaleQuantize(take(quantize(reduced, Math.max(count, MIN_SHADES)), count)).domain([0, count]);
} else {
const colors = getColorMap(palette, count).map(r => resolvePaletteReference(palette, r));
colorScale = scaleQuantize(take(colors, count)).domain([0, count]);
}
let minZ;
let maxZ;
const valuesX = new Set();
const valuesY = new Set();
const computed = data.map((datum, datumIndex) => {
const {
pri,
sec,
color
} = datum,
other = _objectWithoutProperties(datum, _excluded);
let c = color;
if (typeof colorFn === "function") {
c = colorFn(datum);
} // use series index to establish the color based on the color scheme
if (c == null) {
c = colorScale(datumIndex);
} else {
c = resolveMappedColor(palette, c);
}
function processValue(value, valueIndex) {
const cx = scaleX(value.x);
const cy = scaleY(value.y);
valuesX.add(value.x);
valuesY.add(value.y);
if (value.z != null && isFinite(value.z)) {
if (minZ == null || value.z < minZ) {
minZ = value.z;
}
if (maxZ == null || value.z > maxZ) {
maxZ = value.z;
}
}
return _objectSpread(_objectSpread({}, value), {}, {
index: valueIndex,
c: cx != null && cy != null ? {
x: cx,
y: cy,
d: NaN
} : undefined
});
}
const priEx = pri.map((p, i) => processValue(p, i));
const secEx = sec === null || sec === void 0 ? void 0 : sec.map((p, i) => processValue(p, i));
return _objectSpread(_objectSpread({}, other), {}, {
index: datumIndex,
color: c,
loColor: mutedColor(c, 0.2, back),
hiColor: mutedColor(c, 0.5, back),
pri: priEx,
sec: secEx
});
});
const scaleZ = scaleLinear().rangeRound([minExtentZ, Math.round(maxExtentZ / Math.sqrt(data.length))]).domain([(_ref2 = minDomainZ !== null && minDomainZ !== void 0 ? minDomainZ : minZ) !== null && _ref2 !== void 0 ? _ref2 : 0, (_ref3 = maxDomainZ !== null && maxDomainZ !== void 0 ? maxDomainZ : maxZ) !== null && _ref3 !== void 0 ? _ref3 : 0]); // second pass to generate node sizes
computed.forEach(datum => [datum.pri, datum.sec].forEach(_ => _ === null || _ === void 0 ? void 0 : _.forEach(value => {
if (value.c != null) {
if (value.z != null && isFinite(value.z)) {
value.c.d = scaleZ(value.z);
} else {
value.c = undefined;
}
}
}))); // use force simulation to compute positioning
if (!exact) {
const nodes = [];
computed.forEach(datum => [datum.pri, datum.sec].forEach((_, i) => _ === null || _ === void 0 ? void 0 : _.forEach(value => {
if (value.c != null) {
nodes.push({
datumIndex: datum.index,
valueIndex: value.index,
valueType: i === 0 ? "pri" : "sec",
valueData: value.c
});
}
})));
const spacing = 0.5;
const fc = forceCollide(d => d.valueData.d * 0.5 + spacing);
const fx = forceX(d => d.valueData.x).strength(0.1);
const fy = forceY(d => d.valueData.y).strength(0.1);
const simulation = forceSimulation(nodes).force("x", fx).force("y", fy).force("collide", fc).stop();
simulation.tick(ITERATIONS);
simulation.nodes().forEach(node => {
const datum = computed[node.datumIndex];
const value = node.valueType === "pri" ? datum.pri[node.valueIndex] : datum.sec != null ? datum.sec[node.valueIndex] : undefined;
if (value && value.c != null && node.x != null && node.y != null) {
value.c.x = node.x;
value.c.y = node.y;
}
});
}
const domainX = new Domain(Array.from(valuesX.values()), compareX, domainXType === "ordinal");
const domainY = new Domain(Array.from(valuesY.values()), compareY, domainYType === "ordinal");
return [computed, domainX, domainY, scaleZ];
}, [data, minExtentZ, maxExtentZ, minDomainZ, maxDomainZ, scaleX, compareX, domainXType, scaleY, compareY, domainYType, palette, colorScheme, colorMode, colorFn, exact]);
}
/**
* Generates puzzle pieces for XY pane with Z dimension.
*/
export function useXYZPaneData(context, scaleY, compareY, domainYType, minDomainZ, maxDomainZ, minExtentZ, maxExtentZ, props, exact) {
const [computed, domainX, domainY, scaleZ] = useComputedData(context.scaleX, context.compareX, context.domainType, scaleY, compareY, domainYType, minDomainZ, maxDomainZ, minExtentZ, maxExtentZ, props, exact);
return [computed, domainX, domainY, scaleZ, []];
}
/**
* Finds bubble datum via (X,Y) coordinates.
*/
export function findDatum(cx, cy, data) {
function match(values) {
for (let j = 0; j < values.length; ++j) {
const p = values[j];
if (p != null) {
const _p = p;
if (_p.c != null) {
const distance = Math.pow(_p.c.d * 0.5, 2);
if (Math.pow(_p.c.x - cx, 2) < distance && Math.pow(_p.c.y - cy, 2) < distance) {
return true;
}
}
}
}
return false;
}
for (let i = 0; i < data.length; ++i) {
const d = data[i];
if (match(d.pri) || d.sec != null && match(d.sec)) {
return d;
}
}
return undefined;
}
//# sourceMappingURL=commonXYZ.js.map