blob: 6616706ab2ff78016a0db400d3ced991b4168697 [file] [log] [blame]
/* eslint-disable no-dupe-class-members */
import { scaleOrdinal, ScaleOrdinal } from 'd3-scale';
import { ExtensibleFunction } from '../models';
import { ColorsLookup } from './types';
import stringifyAndTrim from './stringifyAndTrim';
// Use type augmentation to correct the fact that
// an instance of CategoricalScale is also a function
interface CategoricalColorScale {
(x: { toString(): string }): string;
}
class CategoricalColorScale extends ExtensibleFunction {
colors: string[];
scale: ScaleOrdinal<{ toString(): string }, string>;
parentForcedColors?: ColorsLookup;
forcedColors: ColorsLookup;
/**
* Constructor
* @param {*} colors an array of colors
* @param {*} parentForcedColors optional parameter that comes from parent
* (usually CategoricalColorNamespace) and supersede this.forcedColors
*/
constructor(colors: string[], parentForcedColors?: ColorsLookup) {
super((value: string) => this.getColor(value));
this.colors = colors;
this.scale = scaleOrdinal<{ toString(): string }, string>();
this.scale.range(colors);
this.parentForcedColors = parentForcedColors;
this.forcedColors = {};
}
getColor(value?: string) {
const cleanedValue = stringifyAndTrim(value);
const parentColor = this.parentForcedColors && this.parentForcedColors[cleanedValue];
if (parentColor) {
return parentColor;
}
const forcedColor = this.forcedColors[cleanedValue];
if (forcedColor) {
return forcedColor;
}
return this.scale(cleanedValue);
}
/**
* Enforce specific color for given value
* @param {*} value value
* @param {*} forcedColor forcedColor
*/
setColor(value: string, forcedColor: string) {
this.forcedColors[stringifyAndTrim(value)] = forcedColor;
return this;
}
/**
* Get a mapping of data values to colors
* @returns an object where the key is the data value and the value is the hex color code
*/
getColorMap() {
const colorMap: { [key: string]: string | undefined } = {};
this.scale.domain().forEach(value => {
colorMap[value.toString()] = this.scale(value);
});
return {
...colorMap,
...this.forcedColors,
...this.parentForcedColors,
};
}
/**
* Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa.
*/
copy() {
const copy = new CategoricalColorScale(this.scale.range(), this.parentForcedColors);
copy.forcedColors = { ...this.forcedColors };
copy.domain(this.domain());
copy.unknown(this.unknown());
return copy;
}
/**
* Returns the scale's current domain.
*/
domain(): { toString(): string }[];
/**
* Expands the domain to include the specified array of values.
*/
domain(newDomain: { toString(): string }[]): this;
domain(newDomain?: { toString(): string }[]): unknown {
if (typeof newDomain === 'undefined') {
return this.scale.domain();
}
this.scale.domain(newDomain);
return this;
}
/**
* Returns the scale's current range.
*/
range(): string[];
/**
* Sets the range of the ordinal scale to the specified array of values.
*
* The first element in the domain will be mapped to the first element in range, the second domain value to the second range value, and so on.
*
* If there are fewer elements in the range than in the domain, the scale will reuse values from the start of the range.
*
* @param range Array of range values.
*/
range(newRange: string[]): this;
range(newRange?: string[]): unknown {
if (typeof newRange === 'undefined') {
return this.scale.range();
}
this.colors = newRange;
this.scale.range(newRange);
return this;
}
/**
* Returns the current unknown value, which defaults to "implicit".
*/
unknown(): string | { name: 'implicit' };
/**
* Sets the output value of the scale for unknown input values and returns this scale.
* The implicit value enables implicit domain construction. scaleImplicit can be used as a convenience to set the implicit value.
*
* @param value Unknown value to be used or scaleImplicit to set implicit scale generation.
*/
unknown(value: string | { name: 'implicit' }): this;
unknown(value?: string | { name: 'implicit' }): unknown {
if (typeof value === 'undefined') {
return this.scale.unknown();
}
this.scale.unknown(value);
return this;
}
}
export default CategoricalColorScale;