diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-color/package.json b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-color/package.json index 70acf698a9f5..4cd9cb82eed1 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-color/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-color/package.json @@ -26,7 +26,7 @@ "access": "public" }, "dependencies": { - "@types/d3-scale": "^2.0.2", + "@types/d3-scale": "^2.1.1", "d3-scale": "^3.0.0" }, "peerDependencies": { diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/package.json b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/package.json index 734fac79dcb6..dce87ad98949 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/package.json @@ -27,9 +27,10 @@ }, "private": true, "dependencies": { + "@types/d3-array": "^2.0.0", "@types/d3-interpolate": "^1.3.1", - "@types/d3-scale": "^2.1.1", "@types/d3-time": "^1.0.10", + "d3-array": "^2.3.1", "d3-interpolate": "^1.3.2", "d3-scale": "^3.0.1", "d3-time": "^1.0.11", diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/encoders/ChannelEncoder.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/encoders/ChannelEncoder.ts new file mode 100644 index 000000000000..83b42d88b7e8 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/encoders/ChannelEncoder.ts @@ -0,0 +1,129 @@ +import { extent as d3Extent } from 'd3-array'; +import { ChannelType, ChannelInput } from '../types/Channel'; +import { PlainObject, Dataset } from '../types/Data'; +import { ChannelDef } from '../types/ChannelDef'; +import createGetterFromChannelDef, { Getter } from '../parsers/createGetterFromChannelDef'; +import completeChannelDef, { CompleteChannelDef } from '../fillers/completeChannelDef'; +import createFormatterFromChannelDef from '../parsers/format/createFormatterFromChannelDef'; +import createScaleFromScaleConfig from '../parsers/scale/createScaleFromScaleConfig'; +import identity from '../utils/identity'; +import { HasToString, IdentityFunction } from '../types/Base'; +import { isTypedFieldDef, isValueDef } from '../typeGuards/ChannelDef'; +import { isX, isY, isXOrY } from '../typeGuards/Channel'; +import { Value } from '../types/VegaLite'; + +type EncodeFunction = (value: ChannelInput | Output) => Output | null | undefined; + +export default class ChannelEncoder, Output extends Value = Value> { + readonly name: string | Symbol | number; + readonly channelType: ChannelType; + readonly originalDefinition: Def; + readonly definition: CompleteChannelDef; + readonly scale: false | ReturnType; + + private readonly getValue: Getter; + readonly encodeValue: IdentityFunction | EncodeFunction; + readonly formatValue: (value: ChannelInput | HasToString) => string; + + constructor({ + name, + channelType, + definition: originalDefinition, + }: { + name: string; + channelType: ChannelType; + definition: Def; + }) { + this.name = name; + this.channelType = channelType; + + this.originalDefinition = originalDefinition; + this.definition = completeChannelDef(this.channelType, originalDefinition); + + this.getValue = createGetterFromChannelDef(this.definition); + this.formatValue = createFormatterFromChannelDef(this.definition); + + const scale = this.definition.scale && createScaleFromScaleConfig(this.definition.scale); + this.encodeValue = scale === false ? identity : (value: ChannelInput) => scale(value); + this.scale = scale; + } + + encodeDatum: { + (datum: PlainObject): Output | null | undefined; + (datum: PlainObject, otherwise: Output): Output; + } = (datum: PlainObject, otherwise?: Output) => { + const value = this.getValueFromDatum(datum); + + if (otherwise !== undefined && (value === null || value === undefined)) { + return otherwise; + } + + return this.encodeValue(value) as Output; + }; + + formatDatum = (datum: PlainObject): string => this.formatValue(this.getValueFromDatum(datum)); + + getValueFromDatum = (datum: PlainObject, otherwise?: T) => { + const value = this.getValue(datum); + + return otherwise !== undefined && (value === null || value === undefined) + ? otherwise + : (value as T); + }; + + getDomain = (data: Dataset) => { + if (isValueDef(this.definition)) { + const { value } = this.definition; + + return [value]; + } + + const { type } = this.definition; + if (type === 'nominal' || type === 'ordinal') { + return Array.from(new Set(data.map(d => this.getValueFromDatum(d)))) as string[]; + } else if (type === 'quantitative') { + const extent = d3Extent(data, d => this.getValueFromDatum(d)); + + return typeof extent[0] === 'undefined' ? [0, 1] : (extent as [number, number]); + } else if (type === 'temporal') { + const extent = d3Extent(data, d => this.getValueFromDatum(d)); + + return typeof extent[0] === 'undefined' + ? [0, 1] + : (extent as [number, number] | [Date, Date]); + } + + return []; + }; + + getTitle() { + return this.definition.title; + } + + isGroupBy() { + if (isTypedFieldDef(this.definition)) { + const { type } = this.definition; + + return ( + this.channelType === 'Category' || + this.channelType === 'Text' || + (this.channelType === 'Color' && (type === 'nominal' || type === 'ordinal')) || + (isXOrY(this.channelType) && (type === 'nominal' || type === 'ordinal')) + ); + } + + return false; + } + + isX() { + return isX(this.channelType); + } + + isXOrY() { + return isXOrY(this.channelType); + } + + isY() { + return isY(this.channelType); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeChannelDef.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeChannelDef.ts index 3f0c199c88cd..bd02f77ffbfd 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeChannelDef.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeChannelDef.ts @@ -1,27 +1,51 @@ -import { ChannelDef } from '../types/ChannelDef'; +import { ChannelDef, NonValueDef } from '../types/ChannelDef'; import { ChannelType } from '../types/Channel'; -import { isFieldDef } from '../typeGuards/ChannelDef'; +import { isFieldDef, isValueDef, isTypedFieldDef } from '../typeGuards/ChannelDef'; import completeAxisConfig, { CompleteAxisConfig } from './completeAxisConfig'; import completeScaleConfig, { CompleteScaleConfig } from './completeScaleConfig'; -import { Value } from '../types/VegaLite'; +import { Value, ValueDef, Type } from '../types/VegaLite'; +import inferFieldType from './inferFieldType'; -type CompleteChannelDef = Omit< - ChannelDef, +export interface CompleteValueDef extends ValueDef { + axis: false; + scale: false; + title: ''; +} + +export type CompleteFieldDef = Omit< + NonValueDef, 'title' | 'axis' | 'scale' > & { + type: Type; axis: CompleteAxisConfig; scale: CompleteScaleConfig; title: string; }; -export default function completeChannelDef( +export type CompleteChannelDef = + | CompleteValueDef + | CompleteFieldDef; + +export default function completeChannelDef( channelType: ChannelType, channelDef: ChannelDef, ): CompleteChannelDef { + if (isValueDef(channelDef)) { + return { + ...channelDef, + axis: false, + scale: false, + title: '', + }; + } + // Fill top-level properties const copy = { ...channelDef, title: isFieldDef(channelDef) ? channelDef.title || channelDef.field : '', + type: isTypedFieldDef(channelDef) + ? channelDef.type + : inferFieldType(channelType, channelDef.field), }; return { diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeScaleConfig.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeScaleConfig.ts index 2c5f9714eafe..dffbc3add864 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeScaleConfig.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/completeScaleConfig.ts @@ -9,7 +9,7 @@ import { Value } from '../types/VegaLite'; export type CompleteScaleConfig = false | ScaleConfig; -export default function completeScaleConfig( +export default function completeScaleConfig( channelType: ChannelType, channelDef: ChannelDef, ): CompleteScaleConfig { diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/inferFieldType.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/inferFieldType.ts new file mode 100644 index 000000000000..6ea19a47a6a8 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/fillers/inferFieldType.ts @@ -0,0 +1,13 @@ +import { ChannelType } from '../types/Channel'; +import { isXOrY } from '../typeGuards/Channel'; +import { Type } from '../types/VegaLite'; + +const temporalFieldNames = new Set(['time', 'date', 'datetime', 'timestamp']); + +export default function inferFieldType(channelType: ChannelType, field: string = ''): Type { + if (isXOrY(channelType) || channelType === 'Numeric') { + return temporalFieldNames.has(field.toLowerCase()) ? 'temporal' : 'quantitative'; + } + + return 'nominal'; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/index.ts index e69de29bb2d1..f5e7b25d892e 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/index.ts @@ -0,0 +1,3 @@ +export { default as ChannelEncoder } from './encoders/ChannelEncoder'; +export { default as completeChannelDef } from './fillers/completeChannelDef'; +export { default as createScaleFromScaleConfig } from './parsers/scale/createScaleFromScaleConfig'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/createGetterFromChannelDef.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/createGetterFromChannelDef.ts index f4955af4fc92..88ae659c90f8 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/createGetterFromChannelDef.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/createGetterFromChannelDef.ts @@ -1,17 +1,20 @@ import { get } from 'lodash/fp'; -import identity from '../utils/identity'; import { ChannelDef } from '../types/ChannelDef'; import { isValueDef } from '../typeGuards/ChannelDef'; import { PlainObject } from '../types/Data'; +import { Value } from '../types/VegaLite'; +import { ChannelInput } from '../types/Channel'; -export default function createGetterFromChannelDef( - definition: ChannelDef, -): (x?: PlainObject) => any { +export type Getter = (x?: PlainObject) => ChannelInput | Output | undefined; + +export default function createGetterFromChannelDef( + definition: ChannelDef, +): Getter { if (isValueDef(definition)) { return () => definition.value; } else if (typeof definition.field !== 'undefined') { return get(definition.field); } - return identity; + return () => undefined; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/createScaleFromScaleType.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/createScaleFromScaleType.ts index 3adc1a51b53f..3580846ff76c 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/createScaleFromScaleType.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/createScaleFromScaleType.ts @@ -12,8 +12,7 @@ import { scalePoint, scaleBand, } from 'd3-scale'; -import { HasToString } from '../../types/Base'; -import { ScaleConfig } from '../../types/Scale'; +import { ScaleConfig, CategoricalScaleInput } from '../../types/Scale'; import { ScaleType, Value } from '../../types/VegaLite'; // eslint-disable-next-line complexity @@ -44,11 +43,11 @@ export default function createScaleFromScaleType( case ScaleType.THRESHOLD: return scaleThreshold(); case ScaleType.ORDINAL: - return scaleOrdinal(); + return scaleOrdinal(); case ScaleType.POINT: - return scalePoint(); + return scalePoint(); case ScaleType.BAND: - return scaleBand(); + return scaleBand(); case ScaleType.SYMLOG: // TODO: d3-scale typings does not include scaleSymlog yet // needs to patch the declaration file before continue. diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Base.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Base.ts index c758949a1fe6..0e18bea848dd 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Base.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Base.ts @@ -14,3 +14,6 @@ export type RequiredSome = { { [Field in RequiredFields]-?: T[Field]; }; + +/** Signature of an identity function */ +export type IdentityFunction = (value: T) => T; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts index ad6c452e703b..668526365962 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts @@ -191,6 +191,8 @@ export interface WithScale { /** Each ScaleCategory contains one or more ScaleType */ export type ScaleCategory = 'continuous' | 'discrete' | 'discretizing'; +export type CategoricalScaleInput = HasToString | null | undefined; + export interface ScaleTypeToD3ScaleType { [ScaleType.LINEAR]: ScaleLinear; [ScaleType.LOG]: ScaleLogarithmic; @@ -202,10 +204,10 @@ export interface ScaleTypeToD3ScaleType { [ScaleType.QUANTILE]: ScaleQuantile; [ScaleType.QUANTIZE]: ScaleQuantize; [ScaleType.THRESHOLD]: ScaleThreshold; - [ScaleType.BIN_ORDINAL]: ScaleOrdinal; - [ScaleType.ORDINAL]: ScaleOrdinal; - [ScaleType.POINT]: ScalePoint; - [ScaleType.BAND]: ScaleBand; + [ScaleType.BIN_ORDINAL]: ScaleOrdinal; + [ScaleType.ORDINAL]: ScaleOrdinal; + [ScaleType.POINT]: ScalePoint; + [ScaleType.BAND]: ScaleBand; } export type ContinuousD3Scale = @@ -219,6 +221,6 @@ export type D3Scale = | ScaleQuantile | ScaleQuantize | ScaleThreshold - | ScaleOrdinal - | ScalePoint - | ScaleBand; + | ScaleOrdinal + | ScalePoint + | ScaleBand; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/encoders/ChannelEncoder.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/encoders/ChannelEncoder.test.ts new file mode 100644 index 000000000000..1d75cd17b607 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/encoders/ChannelEncoder.test.ts @@ -0,0 +1,408 @@ +import { ChannelEncoder } from '../../src'; + +describe('ChannelEncoder', () => { + describe('new ChannelEncoder({ name, channelType, definition })', () => { + it('completes the definition and creates an encoder for it', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + }, + }); + expect(encoder).toBeDefined(); + expect(encoder.definition.scale !== false && encoder.definition.scale.type).toEqual('linear'); + }); + it('creates a new encoder without scale', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + value: 1, + }, + }); + expect(encoder).toBeDefined(); + }); + }); + + describe('.encodeDatum()', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + title: 'Speed', + scale: { + domain: [0, 100], + range: [0, 1], + }, + }, + }); + it('encodes value from datum', () => { + expect(encoder.encodeDatum({ speed: 100 })).toEqual(1); + }); + it('returns fallback value if value is null or undefined', () => { + expect(encoder.encodeDatum({ speed: null }, 20)).toEqual(20); + expect(encoder.encodeDatum({}, 20)).toEqual(20); + }); + }); + + describe('.encodeValue()', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + title: 'Speed', + scale: { + domain: [0, 100], + range: [0, 1], + }, + }, + }); + it('encodes value', () => { + expect(encoder.encodeValue(20)).toEqual(0.2); + }); + }); + + describe('.formatDatum()', () => { + it('formats value from datum', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + title: 'Speed', + }, + }); + expect(encoder.formatDatum({ speed: 100000 })).toEqual('100k'); + }); + }); + + describe('.formatValue()', () => { + it('formats value', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + title: 'Speed', + }, + }); + expect(encoder.formatValue(100000)).toEqual('100k'); + }); + }); + + describe('.getValueFromDatum()', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + title: 'Speed', + }, + }); + it('returns value', () => { + expect(encoder.getValueFromDatum({ speed: 100 })).toEqual(100); + }); + it('returns fallback value if value is null or undefined', () => { + expect(encoder.getValueFromDatum({ speed: null }, 20)).toEqual(20); + expect(encoder.getValueFromDatum({}, 20)).toEqual(20); + }); + }); + + describe('.getDomain()', () => { + describe('for ValueDef', () => { + it('returns an array of fixed value', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + value: 1, + }, + }); + expect(encoder.getDomain([])).toEqual([1]); + }); + }); + + describe('for nominal field', () => { + it('returns all possible values', () => { + const encoder = new ChannelEncoder({ + name: 'pieColor', + channelType: 'Color', + definition: { + type: 'nominal', + field: 'brand', + }, + }); + expect(encoder.getDomain([{ brand: 'Forever 31' }, { brand: 'Super Wet' }])).toEqual([ + 'Forever 31', + 'Super Wet', + ]); + }); + }); + + describe('for ordinal field', () => { + it('returns all possible values', () => { + const encoder = new ChannelEncoder({ + name: 'pieColor', + channelType: 'Color', + definition: { + type: 'ordinal', + field: 'size', + }, + }); + expect(encoder.getDomain([{ size: 'L' }, { size: 'XXL' }])).toEqual(['L', 'XXL']); + }); + }); + + describe('for quantitative field', () => { + it('returns min/max values', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'price', + }, + }); + expect(encoder.getDomain([{ price: 1 }, { price: 5 }])).toEqual([1, 5]); + }); + it('returns [0, 1] when cannot determine', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'price', + }, + }); + expect(encoder.getDomain([{}, {}])).toEqual([0, 1]); + }); + }); + + describe('for temporal field', () => { + it('returns min/max values', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'temporal', + field: 'time', + }, + }); + expect( + encoder.getDomain([ + { time: new Date(Date.UTC(2019, 1, 1)) }, + { time: new Date(Date.UTC(2019, 2, 1)) }, + ]), + ).toEqual([new Date(Date.UTC(2019, 1, 1)), new Date(Date.UTC(2019, 2, 1))]); + }); + it('returns [0, 1] when cannot determine', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'temporal', + field: 'time', + }, + }); + expect(encoder.getDomain([{}, {}])).toEqual([0, 1]); + }); + }); + + it('returns empty array otherwise', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'geojson', + field: 'time', + }, + }); + expect(encoder.getDomain([{}, {}])).toEqual([]); + }); + }); + + describe('.getTitle()', () => { + it('returns title', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + title: 'Speed', + }, + }); + expect(encoder.getTitle()).toEqual('Speed'); + }); + }); + + describe('.isGroupBy()', () => { + it('returns true if the field of this channel is considered a group by clause', () => { + expect( + new ChannelEncoder({ + name: 'bubbleColor', + channelType: 'Color', + definition: { + type: 'nominal', + field: 'brand', + }, + }).isGroupBy(), + ).toBeTruthy(); + expect( + new ChannelEncoder({ + name: 'bubbleColor', + channelType: 'Color', + definition: { + type: 'ordinal', + field: 'size', + }, + }).isGroupBy(), + ).toBeTruthy(); + expect( + new ChannelEncoder({ + name: 'highlighted', + channelType: 'Category', + definition: { + type: 'nominal', + field: 'isLimitedEdition', + }, + }).isGroupBy(), + ).toBeTruthy(); + expect( + new ChannelEncoder({ + name: 'tooltip', + channelType: 'Text', + definition: { + type: 'nominal', + field: 'description', + }, + }).isGroupBy(), + ).toBeTruthy(); + expect( + new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'nominal', + field: 'brand', + }, + }).isGroupBy(), + ).toBeTruthy(); + expect( + new ChannelEncoder({ + name: 'y', + channelType: 'Y', + definition: { + type: 'ordinal', + field: 'size', + }, + }).isGroupBy(), + ).toBeTruthy(); + }); + it('returns false otherwise', () => { + expect( + new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + }, + }).isGroupBy(), + ).toBeFalsy(); + expect( + new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + value: 1, + }, + }).isGroupBy(), + ).toBeFalsy(); + }); + }); + + describe('.isX()', () => { + it('returns true if channel is Y', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + }, + }); + expect(encoder.isX()).toBeTruthy(); + }); + it('returns false otherwise', () => { + const encoder = new ChannelEncoder({ + name: 'y', + channelType: 'Y', + definition: { + type: 'quantitative', + field: 'speed', + }, + }); + expect(encoder.isX()).toBeFalsy(); + }); + }); + + describe('.isY()', () => { + it('returns true if channel is Y', () => { + const encoder = new ChannelEncoder({ + name: 'y', + channelType: 'Y', + definition: { + type: 'quantitative', + field: 'speed', + }, + }); + expect(encoder.isY()).toBeTruthy(); + }); + it('returns false otherwise', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + }, + }); + expect(encoder.isY()).toBeFalsy(); + }); + }); + + describe('.isXOrY()', () => { + it('returns true if channel is X or Y', () => { + const encoder = new ChannelEncoder({ + name: 'x', + channelType: 'X', + definition: { + type: 'quantitative', + field: 'speed', + }, + }); + expect(encoder.isXOrY()).toBeTruthy(); + }); + it('returns false otherwise', () => { + const encoder = new ChannelEncoder({ + name: 'bubbleColor', + channelType: 'Color', + definition: { + type: 'nominal', + field: 'brand', + }, + }); + expect(encoder.isXOrY()).toBeFalsy(); + }); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/completeChannelDef.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/completeChannelDef.test.ts index bdf8acd42eab..36e3ecb1fb4a 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/completeChannelDef.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/completeChannelDef.test.ts @@ -51,4 +51,10 @@ describe('completeChannelDef(channelType, channelDef)', () => { }), ).toEqual({ axis: false, scale: false, title: '', value: 1 }); }); + it('leaves the title blank for invalid Def', () => { + expect( + // @ts-ignore + completeChannelDef('X', {}), + ).toEqual({ axis: false, scale: false, title: '', type: 'quantitative' }); + }); }); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/inferFieldType.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/inferFieldType.test.ts new file mode 100644 index 000000000000..cb751021e6f2 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/fillers/inferFieldType.test.ts @@ -0,0 +1,16 @@ +import inferFieldType from '../../src/fillers/inferFieldType'; + +describe('inferFieldType(channelType, field)', () => { + it('returns quantitative when it should', () => { + expect(inferFieldType('XBand', 'speed')).toEqual('quantitative'); + expect(inferFieldType('YBand', 'price')).toEqual('quantitative'); + }); + it('returns temporal when it should', () => { + expect(inferFieldType('X', 'time')).toEqual('temporal'); + expect(inferFieldType('Numeric', 'timestamp')).toEqual('temporal'); + }); + it('returns nominal when it should', () => { + expect(inferFieldType('Color', 'brand')).toEqual('nominal'); + expect(inferFieldType('Category', 'material')).toEqual('nominal'); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/createGetterFromChannelDef.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/createGetterFromChannelDef.test.ts index 468deae6932e..0fd22886ea00 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/createGetterFromChannelDef.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/createGetterFromChannelDef.test.ts @@ -16,6 +16,6 @@ describe('createGetterFromChannelDef(definition)', () => { it('otherwise return identity', () => { // @ts-ignore const getter = createGetterFromChannelDef({}); - expect(getter(300)).toBe(300); + expect(getter(300)).toBeUndefined(); }); }); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/types/d3-scale/index.d.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/types/d3-scale/index.d.ts new file mode 100644 index 000000000000..e2e889f15363 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/types/d3-scale/index.d.ts @@ -0,0 +1,1859 @@ +/* eslint-disable import/export */ + +declare module 'd3-scale' { + // Type definitions for D3JS d3-scale module 2.1 + // Project: https://github.com/d3/d3-scale/, https://d3js.org/d3-scale + // Definitions by: Tom Wanzek + // Alex Ford + // Boris Yankov + // denisname + // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + // TypeScript Version: 2.3 + + // Last module patch version validated against: 2.1.2 + + import { CountableTimeInterval, TimeInterval } from 'd3-time'; + + // ------------------------------------------------------------------------------- + // Shared Types and Interfaces + // ------------------------------------------------------------------------------- + + /** + * An Interpolator factory returns an interpolator function. + * + * The first generic corresponds to the data type of the interpolation boundaries. + * The second generic corresponds to the data type of the return type of the interpolator. + */ + export interface InterpolatorFactory { + /** + * Construct a new interpolator function, based on the provided interpolation boundaries. + * + * @param a Start boundary of the interpolation interval. + * @param b End boundary of the interpolation interval. + */ + (a: T, b: T): (t: number) => U; + } + + /** + * A helper interface for a continuous scale defined over a numeric domain. + */ + export interface ScaleContinuousNumeric { + /** + * Given a value from the domain, returns the corresponding value from the range, subject to interpolation, if any. + * + * If the given value is outside the domain, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the range. + * + * Note: The interpolation function applied by the scale may change the output type from the range type as part of the interpolation. + * + * @param value A numeric value from the domain. + */ + (value: number | { valueOf(): number }): Output; + + /** + * Given a value from the range, returns the corresponding value from the domain. Inversion is useful for interaction, + * say to determine the data value corresponding to the position of the mouse. + * + * If the given value is outside the range, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the domain. + * + * IMPORTANT: This method is only supported if the range is numeric. If the range is not numeric, returns NaN. + * + * For a valid value y in the range, continuous(continuous.invert(y)) approximately equals y; + * similarly, for a valid value x in the domain, continuous.invert(continuous(x)) approximately equals x. + * The scale and its inverse may not be exact due to the limitations of floating point precision. + * + * @param value A numeric value from the range. + */ + invert(value: number | { valueOf(): number }): number; + + /** + * Returns a copy of the scale’s current domain. + */ + domain(): number[]; + /** + * Sets the scale’s domain to the specified array of numbers. The array must contain two or more elements. + * If the elements in the given array are not numbers, they will be coerced to numbers + * + * Although continuous scales typically have two values each in their domain and range, specifying more than two values produces a piecewise scale. + * + * Internally, a piecewise scale performs a binary search for the range interpolator corresponding to the given domain value. + * Thus, the domain must be in ascending or descending order. If the domain and range have different lengths N and M, only the first min(N,M) elements in each are observed. + * + * @param domain Array of numeric domain values. + */ + domain(domain: Array): this; + + /** + * Returns a copy of the scale’s current range. + */ + range(): Range[]; + /** + * Sets the scale’s range to the specified array of values. + * + * The array must contain two or more elements. Unlike the domain, elements in the given array need not be numbers; + * any value that is supported by the underlying interpolator will work, though note that numeric ranges are required for invert. + * + * @param range Array of range values. + */ + range(range: ReadonlyArray): this; + + /** + * Sets the scale’s range to the specified array of values while also setting the scale’s interpolator to interpolateRound. + * + * The rounding interpolator is sometimes useful for avoiding antialiasing artifacts, + * though also consider the shape-rendering “crispEdges” styles. Note that this interpolator can only be used with numeric ranges. + * + * The array must contain two or more elements. Unlike the domain, elements in the given array need not be numbers; + * any value that is supported by the underlying interpolator will work, though note that numeric ranges are required for invert. + * + * @param range Array of range values. + */ + rangeRound(range: Array): this; + + /** + * Returns whether or not the scale currently clamps values to within the range. + */ + clamp(): boolean; + /** + * Enables or disables clamping, respectively. If clamping is disabled and the scale is passed a value outside the domain, + * the scale may return a value outside the range through extrapolation. + * + * If clamping is enabled, the return value of the scale is always within the scale’s range. Clamping similarly applies to the "invert" method. + * + * @param clamp A flag to enable (true) or disable (false) clamping. + */ + clamp(clamp: boolean): this; + + /** + * Returns approximately count representative values from the scale’s domain. + * + * If count is not specified, it defaults to 10. + * + * The returned tick values are uniformly spaced, have human-readable values (such as multiples of powers of 10), + * and are guaranteed to be within the extent of the domain. Ticks are often used to display reference lines, or tick marks, in conjunction with the visualized data. + * The specified count is only a hint; the scale may return more or fewer values depending on the domain. See also d3-array’s ticks. + * + * @param count Optional approximate number of ticks to be returned. If count is not specified, it defaults to 10. + */ + ticks(count?: number): number[]; + + /** + * Returns a number format function suitable for displaying a tick value, automatically computing the appropriate precision based on the fixed interval between tick values. + * The specified count should have the same value as the count that is used to generate the tick values. + * + * @param count Approximate number of ticks to be used when calculating precision for the number format function. + * @param specifier An optional valid format specifier string which allows a custom format where the precision of the format is automatically set by the scale as appropriate for the tick interval. + * If specifier uses the format type "s", the scale will return a SI-prefix format based on the largest value in the domain. + * If the specifier already specifies a precision, this method is equivalent to locale.format. + */ + tickFormat(count?: number, specifier?: string): (d: number | { valueOf(): number }) => string; + + /** + * Extends the domain so that it starts and ends on nice round values. + * This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. + * An optional tick count argument allows greater control over the step size used to extend the bounds, + * guaranteeing that the returned ticks will exactly cover the domain. + * Nicing is useful if the domain is computed from data, say using extent, and may be irregular. + * For example, for a domain of [0.201479…, 0.996679…], a nice domain might be [0.2, 1.0]. + * If the domain has more than two values, nicing the domain only affects the first and last value. + * + * Nicing a scale only modifies the current domain; it does not automatically nice domains that are subsequently set using continuous.domain. + * You must re-nice the scale after setting the new domain, if desired. + * + * @param count An optional number of ticks expected to be used. + */ + nice(count?: number): this; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + // ------------------------------------------------------------------------------- + // Linear Scale Factory + // ------------------------------------------------------------------------------- + + /** + * A linear continuous scale defined over a numeric domain. + * + * Continuous scales map a continuous, quantitative input domain to a continuous output range. + * Each range value y can be expressed as a function of the domain value x: y = mx + b. + * + * If the range is also numeric, the mapping may be inverted. + * + * Note that the data types of the range and output of the scale must be compatible with the interpolator applied by the scale. + * + * The first generic corresponds to the data type of the range elements. + * + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + */ + export interface ScaleLinear extends ScaleContinuousNumeric { + /** + * Returns the scale’s current interpolator factory, which defaults to interpolate. + */ + interpolate(): InterpolatorFactory; + + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate(interpolate: InterpolatorFactory): this; + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * The generic "NewOutput" can be used to change the scale to have a different output element type corresponding to the new interpolation factory. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate( + interpolate: InterpolatorFactory, + ): ScaleLinear; + } + + /** + * Constructs a new continuous linear scale with the unit domain [0, 1], the unit range [0, 1], the default interpolator and clamping disabled. + * + * The scale will have range and output of data type number. + */ + export function scaleLinear(): ScaleLinear; + /** + * Constructs a new continuous linear scale with the unit domain [0, 1], the default interpolator and clamping disabled. + * + * The generic corresponds to the data type of the range and output elements to be used. + * + * As range type and output type are the same, the interpolator factory used with the scale must match this behavior. + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleLinear(): ScaleLinear; + /** + * Constructs a new continuous linear scale with the unit domain [0, 1], the default interpolator and clamping disabled. + * + * The first generic corresponds to the data type of the range elements. + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleLinear(): ScaleLinear; + + // ------------------------------------------------------------------------------- + // Power Scale Factories + // ------------------------------------------------------------------------------- + + /** + * A continuous power scale defined over a numeric domain. + * + * Continuous scales map a continuous, quantitative input domain to a continuous output range. + * + * Each range value y can be expressed as a function of the domain value x: y = mx^k + b, where k is the exponent value. + * Power scales also support negative domain values, in which case the input value and the resulting output value are multiplied by -1. + * + * If the range is also numeric, the mapping may be inverted. + * + * Note that the data types of the range and output of the scale must be compatible with the interpolator applied by the scale. + * + * The first generic corresponds to the data type of the range elements. + * + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + */ + export interface ScalePower extends ScaleContinuousNumeric { + /** + * Returns the scale’s current interpolator factory, which defaults to interpolate. + */ + interpolate(): InterpolatorFactory; + + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate(interpolate: InterpolatorFactory): this; + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * The generic "NewOutput" can be used to change the scale to have a different output element type corresponding to the new interpolation factory. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate( + interpolate: InterpolatorFactory, + ): ScalePower; + + /** + * If exponent is not specified, returns the current exponent, which defaults to 1. + * (Note that this is effectively a linear scale until you set a different exponent.) + */ + exponent(): number; + /** + * Sets the current exponent to the given numeric value. + * (Note that this is effectively a linear scale until you set a different exponent.) + */ + exponent(exponent: number): this; + } + + /** + * Constructs a new continuous power scale with the unit domain [0, 1], the unit range [0, 1], the exponent 1, the default interpolator and clamping disabled. + * (Note that this is effectively a linear scale until you set a different exponent.) + * + * The scale will have range and output of data type number. + */ + export function scalePow(): ScalePower; + /** + * Constructs a new continuous power scale with the unit domain [0, 1], the exponent 1, the default interpolator and clamping disabled. + * (Note that this is effectively a linear scale until you set a different exponent.) + * + * The generic corresponds to the data type of the range and output elements to be used. + * + * As range type and output type are the same, the interpolator factory used with the scale must match this behavior. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scalePow(): ScalePower; + /** + * Constructs a new continuous power scale with the unit domain [0, 1], the exponent 1, the default interpolator and clamping disabled. + * (Note that this is effectively a linear scale until you set a different exponent.) + * + * The first generic corresponds to the data type of the range elements. + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scalePow(): ScalePower; + + /** + * Constructs a new continuous power scale with the unit domain [0, 1], the unit range [0, 1], the exponent 0.5, the default interpolator and clamping disabled. + * This is a convenience method equivalent to d3.scalePow().exponent(0.5). + * + * The scale will have range and output of data type number. + */ + export function scaleSqrt(): ScalePower; + /** + * Constructs a new continuous power scale with the unit domain [0, 1], the exponent 0.5, the default interpolator and clamping disabled. + * This is a convenience method equivalent to d3.scalePow().exponent(0.5). + * + * The generic corresponds to the data type of the range and output elements to be used. + * + * As range type and output type are the same, the interpolator factory used with the scale must match this behavior. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleSqrt(): ScalePower; + /** + * Constructs a new continuous power scale with the unit domain [0, 1], the exponent 0.5, the default interpolator and clamping disabled. + * This is a convenience method equivalent to d3.scalePow().exponent(0.5). + * + * The first generic corresponds to the data type of the range elements. + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleSqrt(): ScalePower; + + // ------------------------------------------------------------------------------- + // Logarithmic Scale Factory + // ------------------------------------------------------------------------------- + + /** + * A continuous logarithmic scale defined over a numeric domain. + * + * Continuous scales map a continuous, quantitative input domain to a continuous output range. + * + * The mapping to the range value y can be expressed as a function of the domain value x: y = m log(x) + b. + * + * As log(0) = -∞, a log scale domain must be strictly-positive or strictly-negative; the domain must not include or cross zero. + * A log scale with a positive domain has a well-defined behavior for positive values, and a log scale with a negative domain has a well-defined behavior for negative values. + * (For a negative domain, input and output values are implicitly multiplied by -1.) + * The behavior of the scale is undefined if you pass a negative value to a log scale with a positive domain or vice versa. + * + * If the range is also numeric, the mapping may be inverted. + * + * Note that the data types of the range and output of the scale must be compatible with the interpolator applied by the scale. + * + * The first generic corresponds to the data type of the range elements. + * + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + */ + export interface ScaleLogarithmic extends ScaleContinuousNumeric { + /** + * Returns a copy of the scale’s current domain. + */ + domain(): number[]; + /** + * Sets the scale’s domain to the specified array of numbers. The array must contain two or more elements. + * If the elements in the given array are not numbers, they will be coerced to numbers + * + * As log(0) = -∞, a log scale domain must be strictly-positive or strictly-negative; the domain must not include or cross zero. + * A log scale with a positive domain has a well-defined behavior for positive values, and a log scale with a negative domain has a well-defined behavior for negative values. + * (For a negative domain, input and output values are implicitly multiplied by -1.) + * The behavior of the scale is undefined if you pass a negative value to a log scale with a positive domain or vice versa. + * + * Although continuous scales typically have two values each in their domain and range, specifying more than two values produces a piecewise scale. + * + * Internally, a piecewise scale performs a binary search for the range interpolator corresponding to the given domain value. + * Thus, the domain must be in ascending or descending order. If the domain and range have different lengths N and M, only the first min(N,M) elements in each are observed. + * + * @param domain Array of numeric domain values. + */ + domain(domain: Array): this; + + /** + * Returns the scale’s current interpolator factory, which defaults to interpolate. + */ + interpolate(): InterpolatorFactory; + + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate(interpolate: InterpolatorFactory): this; + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * The generic "NewOutput" can be used to change the scale to have a different output element type corresponding to the new interpolation factory. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate( + interpolate: InterpolatorFactory, + ): ScaleLogarithmic; + + /** + * Returns approximately count representative values from the scale’s domain. + * + * If count is not specified, it defaults to 10. + * + * If the base is an integer, the returned ticks are uniformly spaced within each integer power of base; otherwise, one tick per power of base is returned. + * The returned ticks are guaranteed to be within the extent of the domain. If the orders of magnitude in the domain is greater than count, then at most one tick per power is returned. + * Otherwise, the tick values are unfiltered, but note that you can use log.tickFormat to filter the display of tick labels. + * + * @param count Optional approximate number of ticks to be returned. If count is not specified, it defaults to 10. + */ + ticks(count?: number): number[]; + + /** + * Returns a number format function suitable for displaying a tick value, automatically computing the appropriate precision based on the fixed interval between tick values. + * + * The specified count typically has the same value as the count that is used to generate the tick values. + * If there are too many ticks, the formatter may return the empty string for some of the tick labels; + * however, note that the ticks are still shown. + * To disable filtering, specify a count of Infinity. When specifying a count, you may also provide a format specifier or format function. + * For example, to get a tick formatter that will display 20 ticks of a currency, say log.tickFormat(20, "$,f"). + * If the specifier does not have a defined precision, the precision will be set automatically by the scale, returning the appropriate format. + * This provides a convenient way of specifying a format whose precision will be automatically set by the scale. + * + * @param count Approximate number of ticks to be used when calculating precision for the number format function. + * @param specifier An optional valid format specifier string which allows a custom format where the precision of the format is automatically set by the scale as appropriate for the tick interval. + * For example, to get a tick formatter that will display 20 ticks of a currency, say log.tickFormat(20, "$,f"). + * If the specifier does not have a defined precision, the precision will be set automatically by the scale, returning the appropriate format. + * This provides a convenient way of specifying a format whose precision will be automatically set by the scale. + */ + tickFormat(count?: number, specifier?: string): (d: number | { valueOf(): number }) => string; + + /** + * Extends the domain to integer powers of base. For example, for a domain of [0.201479…, 0.996679…], and base 10, the nice domain is [0.1, 1]. + * If the domain has more than two values, nicing the domain only affects the first and last value. + * + * Nicing a scale only modifies the current domain; it does not automatically nice domains that are subsequently set using continuous.domain. + * You must re-nice the scale after setting the new domain, if desired. + */ + nice(): this; + + /** + * Returns the current base, which defaults to 10. + */ + base(): number; + /** + * Sets the base for this logarithmic scale to the specified value. + */ + base(base: number): this; + } + + /** + * Constructs a new continuous logarithmic scale with the domain [1, 10], the unit range [0, 1], the base 10, the default interpolator and clamping disabled. + * + * The scale will have range and output of data type number. + */ + export function scaleLog(): ScaleLogarithmic; + /** + * Constructs a new continuous logarithmic scale with the domain [1, 10], the base 10, the default interpolator and clamping disabled. + * + * The generic corresponds to the data type of the range and output elements to be used. + * + * As range type and output type are the same, the interpolator factory used with the scale must match this behavior. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleLog(): ScaleLogarithmic; + /** + * Constructs a new continuous logarithmic scale with the domain [1, 10], the base 10, the default interpolator and clamping disabled. + * + * The first generic corresponds to the data type of the range elements. + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleLog(): ScaleLogarithmic; + + // ------------------------------------------------------------------------------- + // Identity Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Identity scales are a special case of linear scales where the domain and range are identical; the scale and its invert method are thus the identity function. + * These scales are occasionally useful when working with pixel coordinates, say in conjunction with an axis or brush. + */ + export interface ScaleIdentity { + /** + * Given a value from the domain, returns the corresponding value from the range, subject to interpolation, if any. + * + * If the given value is outside the domain, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the range. + * + * Note: The interpolation function applied by the scale may change the output type from the range type as part of the interpolation. + * + * @param value A numeric value from the domain. + */ + (value: number | { valueOf(): number }): number; + + /** + * Given a value from the range, returns the corresponding value from the domain. Inversion is useful for interaction, + * say to determine the data value corresponding to the position of the mouse. + * + * If the given value is outside the range, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the domain. + * + * IMPORTANT: This method is only supported if the range is numeric. If the range is not numeric, returns NaN. + * + * For a valid value y in the range, continuous(continuous.invert(y)) approximately equals y; + * similarly, for a valid value x in the domain, continuous.invert(continuous(x)) approximately equals x. + * The scale and its inverse may not be exact due to the limitations of floating point precision. + * + * @param value A numeric value from the range. + */ + invert(value: number | { valueOf(): number }): number; + + /** + * Returns a copy of the scale’s current domain. + */ + domain(): number[]; + /** + * Sets the scale’s domain to the specified array of numbers. The array must contain two or more elements. + * If the elements in the given array are not numbers, they will be coerced to numbers + * + * Although continuous scales typically have two values each in their domain and range, specifying more than two values produces a piecewise scale. + * + * Internally, a piecewise scale performs a binary search for the range interpolator corresponding to the given domain value. + * Thus, the domain must be in ascending or descending order. If the domain and range have different lengths N and M, only the first min(N,M) elements in each are observed. + * + * @param domain Array of numeric domain values. + */ + domain(domain: Array): this; + + /** + * Returns a copy of the scale’s current range. + */ + range(): number[]; + /** + * Sets the scale’s range to the specified array of values. + * + * The array must contain two or more elements. Unlike the domain, elements in the given array need not be numbers; + * any value that is supported by the underlying interpolator will work, though note that numeric ranges are required for invert. + * + * @param range Array of range values. + */ + range(range: Array): this; + + /** + * Returns approximately count representative values from the scale’s domain. + * + * If count is not specified, it defaults to 10. + * + * The returned tick values are uniformly spaced, have human-readable values (such as multiples of powers of 10), + * and are guaranteed to be within the extent of the domain. Ticks are often used to display reference lines, or tick marks, in conjunction with the visualized data. + * The specified count is only a hint; the scale may return more or fewer values depending on the domain. See also d3-array’s ticks. + * + * @param count Optional approximate number of ticks to be returned. If count is not specified, it defaults to 10. + */ + ticks(count?: number): number[]; + + /** + * Returns a number format function suitable for displaying a tick value, automatically computing the appropriate precision based on the fixed interval between tick values. + * The specified count should have the same value as the count that is used to generate the tick values. + * + * @param count Approximate number of ticks to be used when calculating precision for the number format function. + * @param specifier An optional valid format specifier string which allows a custom format where the precision of the format is automatically set by the scale as appropriate for the tick interval. + * If specifier uses the format type "s", the scale will return a SI-prefix format based on the largest value in the domain. + * If the specifier already specifies a precision, this method is equivalent to locale.format. + */ + tickFormat(count?: number, specifier?: string): (d: number | { valueOf(): number }) => string; + + /** + * Extends the domain so that it starts and ends on nice round values. + * This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. + * An optional tick count argument allows greater control over the step size used to extend the bounds, + * guaranteeing that the returned ticks will exactly cover the domain. + * Nicing is useful if the domain is computed from data, say using extent, and may be irregular. + * For example, for a domain of [0.201479…, 0.996679…], a nice domain might be [0.2, 1.0]. + * If the domain has more than two values, nicing the domain only affects the first and last value. + * + * Nicing a scale only modifies the current domain; it does not automatically nice domains that are subsequently set using continuous.domain. + * You must re-nice the scale after setting the new domain, if desired. + * + * @param count An optional number of ticks expected to be used. + */ + nice(count?: number): this; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): ScaleIdentity; + } + + /** + * Constructs a new identity scale with the unit domain [0, 1] and the unit range [0, 1]. + */ + export function scaleIdentity(): ScaleIdentity; + + // ------------------------------------------------------------------------------- + // Time Scale Factories + // ------------------------------------------------------------------------------- + + /** + * A linear scale defined over a temporal domain. + * + * Time scales implement ticks based on calendar intervals, taking the pain out of generating axes for temporal domains. + * + * If the range is numeric, the mapping may be inverted to return a date. + * + * Note that the data types of the range and output of the scale must be compatible with the interpolator applied by the scale. + * + * The first generic corresponds to the data type of the range elements. + * + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + */ + export interface ScaleTime { + /** + * Given a value from the domain, returns the corresponding value from the range, subject to interpolation, if any. + * + * If the given value is outside the domain, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the range. + * + * Note: The interpolation function applied by the scale may change the output type from the range type as part of the interpolation. + * + * @param value A temporal value from the domain. If the value is not a Date, it will be coerced to Date. + */ + (value: Date | number | { valueOf(): number }): Output; + + /** + * Given a value from the range, returns the corresponding value from the domain. Inversion is useful for interaction, + * say to determine the data value corresponding to the position of the mouse. + * + * If the given value is outside the range, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the domain. + * + * IMPORTANT: This method is only supported if the range is numeric. If the range is not numeric, returns Invalid Date. + * + * For a valid value y in the range, time(time.invert(y)) equals y; similarly, for a valid value x in the domain, time.invert(time(x)) equals x. + * The invert method is useful for interaction, say to determine the value in the domain that corresponds to the pixel location under the mouse. + * + * @param value A numeric value from the range. + */ + invert(value: number | { valueOf(): number }): Date; + + /** + * Returns a copy of the scale’s current domain. + */ + domain(): Date[]; + + /** + * Sets the scale’s domain to the specified array of temporal domain values. The array must contain two or more elements. + * If the elements in the given array are not dates, they will be coerced to dates. + * + * Although continuous scales typically have two values each in their domain and range, specifying more than two values produces a piecewise scale. + * + * Internally, a piecewise scale performs a binary search for the range interpolator corresponding to the given domain value. + * Thus, the domain must be in ascending or descending order. If the domain and range have different lengths N and M, only the first min(N,M) elements in each are observed. + * + * @param domain Array of temporal domain values. Numeric values will be coerced to dates. + */ + domain(domain: Array): this; + + /** + * Returns a copy of the scale’s current range. + */ + range(): Range[]; + /** + * Sets the scale’s range to the specified array of values. + * + * The array must contain two or more elements. Unlike the domain, elements in the given array need not be temporal domain values; + * any value that is supported by the underlying interpolator will work, though note that numeric ranges are required for invert. + * + * @param range Array of range values. + */ + range(range: ReadonlyArray): this; + + /** + * Sets the scale’s range to the specified array of values while also setting the scale’s interpolator to interpolateRound. + * + * The rounding interpolator is sometimes useful for avoiding antialiasing artifacts, + * though also consider the shape-rendering “crispEdges” styles. Note that this interpolator can only be used with numeric ranges. + * + * The array must contain two or more elements. Unlike the domain, elements in the given array need not be temporal domain values; + * any value that is supported by the underlying interpolator will work, though note that numeric ranges are required for invert. + * + * @param range Array of range values. + */ + rangeRound(range: Array): this; + + /** + * Returns whether or not the scale currently clamps values to within the range. + */ + clamp(): boolean; + /** + * Enables or disables clamping, respectively. If clamping is disabled and the scale is passed a value outside the domain, + * the scale may return a value outside the range through extrapolation. + * + * If clamping is enabled, the return value of the scale is always within the scale’s range. Clamping similarly applies to the "invert" method. + * + * @param clamp A flag to enable (true) or disable (false) clamping. + */ + clamp(clamp: boolean): this; + + /** + * Returns the scale’s current interpolator factory, which defaults to interpolate. + */ + interpolate(): InterpolatorFactory; + + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate(interpolate: InterpolatorFactory): this; + /** + * Sets the scale’s range interpolator factory. This interpolator factory is used to create interpolators for each adjacent pair of values from the range; + * these interpolators then map a normalized domain parameter t in [0, 1] to the corresponding value in the range. + * + * Note: the default interpolator may reuse return values. For example, if the range values are objects, then the value interpolator always returns the same object, modifying it in-place. + * If the scale is used to set an attribute or style, this is typically acceptable (and desirable for performance); + * however, if you need to store the scale’s return value, you must specify your own interpolator or make a copy as appropriate. + * + * As part of the interpolation process the interpolated value from the range may be converted to a corresponding output value. + * + * The generic "NewOutput" can be used to change the scale to have a different output element type corresponding to the new interpolation factory. + * + * @param interpolate An interpolation factory. The generics for Range and Output of the scale must correspond to the interpolation factory applied to the scale. + */ + interpolate( + interpolate: InterpolatorFactory, + ): ScaleTime; + + /** + * Returns representative dates from the scale’s domain. The returned tick values are uniformly-spaced (mostly), + * have sensible values (such as every day at midnight), and are guaranteed to be within the extent of the domain. + * Ticks are often used to display reference lines, or tick marks, in conjunction with the visualized data. + * + * Without specifying a count or time interval to control the number of ticks returned, a default count of 10 is used. + * The specified count is only a hint; the scale may return more or fewer values depending on the domain. + */ + ticks(): Date[]; + /** + * Returns representative dates from the scale’s domain. The returned tick values are uniformly-spaced (mostly), + * have sensible values (such as every day at midnight), and are guaranteed to be within the extent of the domain. + * Ticks are often used to display reference lines, or tick marks, in conjunction with the visualized data. + * + * The specified count controls the number of ticks to be returned. The specified count is only a hint; + * the scale may return more or fewer values depending on the domain. + * + * @param count Expected number of ticks. + */ + ticks(count: number): Date[]; + /** + * Returns representative dates from the scale’s domain. The returned tick values are uniformly-spaced (mostly), + * have sensible values (such as every day at midnight), and are guaranteed to be within the extent of the domain. + * Ticks are often used to display reference lines, or tick marks, in conjunction with the visualized data. + * + * The specified time interval controls the ticks generated and returned. To prune the generated ticks for a given time interval, + * use interval.every(...) or interval.filter(...). + * + * @param interval A time interval to specify the expected ticks. + */ + ticks(interval: TimeInterval): Date[]; + + /** + * Returns a time format function suitable for displaying tick values. + * + * The default multi-scale time format chooses a human-readable representation based on the specified date as follows: + * + * - %Y - for year boundaries, such as 2011. + * - %B - for month boundaries, such as February. + * - %b %d - for week boundaries, such as Feb 06. + * - %a %d - for day boundaries, such as Mon 07. + * - %I %p - for hour boundaries, such as 01 AM. + * - %I:%M - for minute boundaries, such as 01:23. + * - :%S - for second boundaries, such as :45. + * - .%L - milliseconds for all other times, such as .012. + * + * Although somewhat unusual, this default behavior has the benefit of providing both local and global context: + * for example, formatting a sequence of ticks as [11 PM, Mon 07, 01 AM] reveals information about hours, dates, and day simultaneously, + * rather than just the hours [11 PM, 12 AM, 01 AM]. + */ + tickFormat(): (d: Date) => string; + /** + * Returns a time format function suitable for displaying tick values. + * + * The specified count is currently ignored, but is accepted for consistency with other scales such as continuous.tickFormat. + * + * @param count Expected number of ticks. (Currently ignored) + * @param specifier An optional valid date format specifier string (see d3-time-format). + */ + tickFormat(count: number, specifier?: string): (d: Date) => string; + /** + * Returns a time format function suitable for displaying tick values. + * + * The specified time interval is currently ignored, but is accepted for consistency with other scales such as continuous.tickFormat. + * + * @param interval A time interval to specify the expected ticks. (Currently ignored) + * @param specifier An optional valid date format specifier string (see d3-time-format). + */ + tickFormat(interval: TimeInterval, specifier?: string): (d: Date) => string; + + /** + * Extends the domain so that it starts and ends on nice round values. + * This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. + * + * Nicing is useful if the domain is computed from data, say using extent, and may be irregular. + * For example, for a domain of [2009-07-13T00:02, 2009-07-13T23:48], the nice domain is [2009-07-13, 2009-07-14]. + * If the domain has more than two values, nicing the domain only affects the first and last value. + */ + nice(): this; + /** + * Extends the domain so that it starts and ends on nice round values. + * This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. + * + * A tick count argument allows greater control over the step size used to extend the bounds, guaranteeing that the returned ticks will exactly cover the domain. + * + * Nicing is useful if the domain is computed from data, say using extent, and may be irregular. + * For example, for a domain of [2009-07-13T00:02, 2009-07-13T23:48], the nice domain is [2009-07-13, 2009-07-14]. + * If the domain has more than two values, nicing the domain only affects the first and last value. + * + * @param count Expected number of ticks. + */ + nice(count: number): this; + /** + * Extends the domain so that it starts and ends on nice round values. + * This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. + * + * a time interval may be specified to explicitly set the ticks. If an interval is specified, an optional step may also be specified to skip some ticks. + * For example, time.nice(d3.timeSecond, 10) will extend the domain to an even ten seconds (0, 10, 20, etc.). + * + * Nicing is useful if the domain is computed from data, say using extent, and may be irregular. + * For example, for a domain of [2009-07-13T00:02, 2009-07-13T23:48], the nice domain is [2009-07-13, 2009-07-14]. + * If the domain has more than two values, nicing the domain only affects the first and last value. + * + * @param interval A time interval to specify the expected ticks. + * @param step An optional step number to be applied to the time interval when considering ticks. + */ + nice(interval: CountableTimeInterval, step?: number): this; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new time scale using local time with the domain [2000-01-01, 2000-01-02], the unit range [0, 1], the default interpolator and clamping disabled. + * + * The scale will have range and output of data type number. + */ + export function scaleTime(): ScaleTime; + /** + * Constructs a new time scale using local time with the domain [2000-01-01, 2000-01-02], the default interpolator and clamping disabled. + * + * The generic corresponds to the data type of the range and output elements to be used. + * + * As range type and output type are the same, the interpolator factory used with the scale must match this behavior. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleTime(): ScaleTime; + /** + * Constructs a new time scale using local time with the domain [2000-01-01, 2000-01-02], the default interpolator and clamping disabled. + * + * The first generic corresponds to the data type of the range elements. + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleTime(): ScaleTime; + + /** + * Constructs a new time scale using Coordinated Universal Time (UTC) with the domain [2000-01-01, 2000-01-02], the unit range [0, 1], the default interpolator and clamping disabled. + * + * The scale will have range and output of data type number. + */ + export function scaleUtc(): ScaleTime; + /** + * Constructs a new time scale using Coordinated Universal Time (UTC) with the domain [2000-01-01, 2000-01-02], the default interpolator and clamping disabled. + * + * The generic corresponds to the data type of the range and output elements to be used. + * + * As range type and output type are the same, the interpolator factory used with the scale must match this behavior. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleUtc(): ScaleTime; + /** + * Constructs a new time scale using Coordinated Universal Time (UTC) with the domain [2000-01-01, 2000-01-02], the default interpolator and clamping disabled. + * + * The first generic corresponds to the data type of the range elements. + * The second generic corresponds to the data type of the output elements generated by the scale. + * + * If range element and output element type differ, the interpolator factory used with the scale must match this behavior and + * convert the interpolated range element to a corresponding output element. + * + * The range must be set in accordance with the range element type. + * + * The interpolator factory may be set using the interpolate(...) method of the scale. + */ + export function scaleUtc(): ScaleTime; + + // ------------------------------------------------------------------------------- + // Sequential Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Sequential scales are similar to continuous scales in that they map a continuous, + * numeric input domain to a continuous output range. However, unlike continuous scales, + * the output range of a sequential scale is fixed by its interpolator and not configurable. + * + * The generic corresponds to the data type of the output of the interpolator underlying the scale. + */ + export interface ScaleSequential { + /** + * Given a value from the domain, returns the corresponding value from the output range, subject to interpolation. + * + * If the given value is outside the domain, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the range. + * + * @param value A numeric value from the domain. + */ + (value: number | { valueOf(): number }): Output; + + /** + * Returns a copy of the scale’s current domain. + */ + domain(): [number, number]; + /** + * Sets the scale’s domain to the specified array of numbers. The array must contain exactly two elements. + * If the elements in the given array are not numbers, they will be coerced to numbers + * + * @param domain A two-element array of numeric domain values. + */ + domain(domain: [number | { valueOf(): number }, number | { valueOf(): number }]): this; + + /** + * Returns whether or not the scale currently clamps values to within the range. + */ + clamp(): boolean; + /** + * Enables or disables clamping, respectively. If clamping is disabled and the scale is passed a value outside the domain, + * the scale may return a value outside the range through extrapolation. + * + * If clamping is enabled, the return value of the scale is always within the scale’s range. Clamping similarly applies to the "invert" method. + * + * @param clamp A flag to enable (true) or disable (false) clamping. + */ + clamp(clamp: boolean): this; + + /** + * Returns the current interpolator underlying the scale. + */ + interpolator(): (t: number) => Output; + /** + * Sets the scale’s interpolator to the specified function. + * + * @param interpolator An interpolator function mapping a value from the [0, 1] interval to an output value. + */ + interpolator(interpolator: (t: number) => Output): this; + /** + * Sets the scale’s interpolator to the specified function. + * + * The generic corresponds to a the new output type of the scale. The output type of the scale is determined by the output type of the interpolator function. + * + * @param interpolator An interpolator function mapping a value from the [0, 1] interval to an output value. + */ + interpolator(interpolator: (t: number) => NewOutput): ScaleSequential; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): ScaleSequential; + } + + /** + * Constructs a new sequential scale with the given interpolator function. When the scale is applied, the interpolator will be invoked with a value typically in the range [0, 1], + * where 0 represents the start of the domain, and 1 represents the end of the domain. + * + * The generic corresponds to the data type of the output of the interpolator underlying the scale. + * + * @param interpolator The interpolator function to be used with the scale. + */ + export function scaleSequential( + interpolator: (t: number) => Output, + ): ScaleSequential; + + // ------------------------------------------------------------------------------- + // Diverging Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Diverging scales, like sequential scales, are similar to continuous scales in that they map a continuous, numeric input domain to a continuous output range. + * However, unlike continuous scales, the output range of a diverging scale is fixed by its interpolator and not configurable. + * These scales do not expose invert, range, rangeRound and interpolate methods. + * + * The generic corresponds to the data type of the interpolator return type. + */ + export interface ScaleDiverging { + /** + * Given a value from the domain, returns the corresponding value subject to interpolation. + * + * If the given value is outside the domain, and clamping is not enabled, the mapping may be extrapolated such that the returned value is outside the range. + * + * @param value A numeric value from the domain. + */ + (value: number | { valueOf(): number }): Output; + + /** + * Returns a copy of the scale’s current domain. + */ + domain(): [number, number, number]; + /** + * Sets the scale’s domain to the specified array of numbers. + * The domain must be numeric and must contain exactly three values. The default domain is [0, 0.5, 1]. + * If the elements in the given array are not numbers, they will be coerced to numbers + * + * @param domain Array of three numeric domain values. + */ + domain( + domain: [ + number | { valueOf(): number }, + number | { valueOf(): number }, + number | { valueOf(): number }, + ], + ): this; + + /** + * Returns whether or not the scale currently clamps values to within the range. + */ + clamp(): boolean; + /** + * Enables or disables clamping, respectively. If clamping is disabled and the scale is passed a value outside the domain, + * the scale may return a value outside the range through extrapolation. + * + * If clamping is enabled, the return value of the scale is always within the interpolator scale’s range. + * + * @param clamp A flag to enable (true) or disable (false) clamping. + */ + clamp(clamp: boolean): this; + + /** + * Returns the scale’s current interpolator. + */ + interpolator(): (t: number) => Output; + /** + * Sets the scale’s interpolator to the specified function. + * + * @param interpolator The scale’s interpolator. + */ + interpolator(interpolator?: (t: number) => Output): this; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new diverging scale with the given interpolator function. + * When the scale is applied, the interpolator will be invoked with a value typically in the range [0, 1], + * where 0 represents the extreme negative value, 0.5 represents the neutral value, and 1 represents the extreme positive value. + * + * The generic corresponds to the data type of the interpolator return type. + * + * @param interpolator The scale’s interpolator. + */ + export function scaleDiverging(interpolator: (t: number) => T): ScaleDiverging; + + // ------------------------------------------------------------------------------- + // Quantize Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Quantize scales are similar to linear scales, except they use a discrete rather than continuous range. + * The continuous input domain is divided into uniform segments based on the number of values in (i.e., the cardinality of) the output range. + * + * Each range value y can be expressed as a quantized linear function of the domain value x: y = m round(x) + b. + * + * The generic corresponds to the data type of the range elements. + */ + export interface ScaleQuantize { + /** + * Given a value in the input domain, returns the corresponding value in the output range. + */ + (value: number | { valueOf(): number }): Range; + /** + * Returns the extent of values in the domain [x0, x1] for the corresponding value in the range: the inverse of quantize. + * This method is useful for interaction, say to determine the value in the domain that corresponds to the pixel location under the mouse. + * + * If an invalid range value is entered, returns [NaN, NaN]. + * + * @param value A value from the range. + */ + invertExtent(value: Range): [number, number]; + + /** + * Returns the scale’s current domain. + */ + domain(): [number, number]; + + /** + * Sets the scale’s domain to the specified two-element array of numbers. + * If the elements in the given array are not numbers, they will be coerced to numbers. + * + * @param domain A two-element array of numeric values defining the domain. + */ + domain(domain: [number | { valueOf(): number }, number | { valueOf(): number }]): this; + + /** + * Returns the scale’s current range. + */ + range(): Range[]; + /** + * Sets the scale’s range to the specified array of values. The array may contain any number of discrete values. + * + * @param range Array of range values. + */ + range(range: ReadonlyArray): this; + + /** + * Returns approximately count representative values from the scale’s domain. + * + * If count is not specified, it defaults to 10. + * + * The returned tick values are uniformly spaced, have human-readable values (such as multiples of powers of 10), + * and are guaranteed to be within the extent of the domain. Ticks are often used to display reference lines, or tick marks, in conjunction with the visualized data. + * The specified count is only a hint; the scale may return more or fewer values depending on the domain. See also d3-array’s ticks. + * + * @param count Optional approximate number of ticks to be returned. If count is not specified, it defaults to 10. + */ + ticks(count?: number): number[]; + + /** + * Returns a number format function suitable for displaying a tick value, automatically computing the appropriate precision based on the fixed interval between tick values. + * The specified count should have the same value as the count that is used to generate the tick values. + * + * @param count Approximate number of ticks to be used when calculating precision for the number format function. + * @param specifier An optional valid format specifier string which allows a custom format where the precision of the format is automatically set by the scale as appropriate for the tick interval. + * If specifier uses the format type "s", the scale will return a SI-prefix format based on the largest value in the domain. + * If the specifier already specifies a precision, this method is equivalent to locale.format. + */ + tickFormat(count?: number, specifier?: string): (d: number | { valueOf(): number }) => string; + + /** + * Extends the domain so that it starts and ends on nice round values. + * This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. + * + * Nicing is useful if the domain is computed from data, say using extent, and may be irregular. + * For example, for a domain of [0.201479…, 0.996679…], a nice domain might be [0.2, 1.0]. + * + * Nicing a scale only modifies the current domain; it does not automatically nice domains that are subsequently set using continuous.domain. + * You must re-nice the scale after setting the new domain, if desired. + * + * @param count An optional number of ticks expected to be used. + */ + nice(count?: number): this; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new quantize scale with the unit domain [0, 1] and the unit range [0, 1]. + * Thus, the default quantize scale is equivalent to the Math.round function. + */ + export function scaleQuantize(): ScaleQuantize; + + /** + * Constructs a new quantize scale with the unit domain [0, 1]. + * + * The range must be set corresponding to the type of the range elements. + * + * The generic corresponds to the data type of the range elements. + */ + export function scaleQuantize(): ScaleQuantize; + + // ------------------------------------------------------------------------------- + // Quantile Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Quantile scales map a sampled input domain to a discrete range. + * The domain is considered continuous and thus the scale will accept any reasonable input value; + * however, the domain is specified as a discrete set of sample values. + * The number of values in (the cardinality of) the output range determines the number of quantiles that will be computed from the domain. + * To compute the quantiles, the domain is sorted, and treated as a population of discrete values; see d3-array’s quantile. + * + * The generic corresponds to the data type of range elements. + */ + export interface ScaleQuantile { + /** + * Given a value in the input domain, returns the corresponding value in the output range. + * + * @param value A numeric value in the input domain. + */ + (value: number | { valueOf(): number }): Range; + + /** + * Returns the extent of values in the domain [x0, x1] for the corresponding value in the range: the inverse of quantile. + * This method is useful for interaction, say to determine the value in the domain that corresponds to the pixel location under the mouse. + * + * @param value A value from the range. + */ + invertExtent(value: Range): [number, number]; + + /** + * Returns the scale’s current domain. + */ + domain(): number[]; + /** + * Sets the domain of the quantile scale to the specified set of discrete numeric values. + * The array must not be empty, and must contain at least one numeric value; NaN, null and undefined values are ignored and not considered part of the sample population. + * + * If the elements in the given array are not numbers, they will be coerced to numbers. A copy of the input array is sorted and stored internally. + * + * @param domain Array of domain values. + */ + domain(domain: Array): this; + + /** + * Returns the current range. + */ + range(): Range[]; + /** + * Sets the discrete values in the range. The array must not be empty. + * The number of values in (the cardinality, or length, of) the range array determines the number of quantiles that are computed. + * + * For example, to compute quartiles, range must be an array of four elements such as [0, 1, 2, 3]. + * + * @param range Array of range values. + */ + range(range: ReadonlyArray): this; + + /** + * Returns the quantile thresholds. If the range contains n discrete values, the returned array will contain n - 1 thresholds. + * Values less than the first threshold are considered in the first quantile; + * values greater than or equal to the first threshold but less than the second threshold are in the second quantile, and so on. + * Internally, the thresholds array is used with bisect to find the output quantile associated with the given input value. + */ + quantiles(): number[]; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new quantile scale with an empty domain and an empty range. + * The quantile scale is invalid until both a domain and range are specified. + */ + export function scaleQuantile(): ScaleQuantile; + + /** + * Constructs a new quantile scale with an empty domain and an empty range. + * The quantile scale is invalid until both a domain and range are specified. + * + * The generic corresponds to the data type of range elements. + */ + export function scaleQuantile(): ScaleQuantile; + + // ------------------------------------------------------------------------------- + // Threshold Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Threshold scales are similar to quantize scales, except they allow you to map arbitrary subsets of the domain to discrete values in the range. + * The input domain is still continuous, and divided into slices based on a set of threshold values. + * + * If the number of values in the scale’s range is N+1, the number of values in the scale’s domain must be N. + * If there are fewer than N elements in the domain, the additional values in the range are ignored. + * If there are more than N elements in the domain, the scale may return undefined for some inputs. + * + * The first generic corresponds to the data type of domain values. + * The second generic corresponds to the data type of range values. + */ + export interface ScaleThreshold { + /** + * Given a value in the input domain, returns the corresponding value in the output range. + * + * @param value A domain value. + */ + (value: Domain): Range; + + /** + * Returns the extent of values in the domain [x0, x1] for the corresponding value in the range, representing the inverse mapping from range to domain. + * This method is useful for interaction, say to determine the value in the domain that corresponds to the pixel location under the mouse. + * + * @param value A range value. + */ + invertExtent(value: Range): [Domain | undefined, Domain | undefined]; + + /** + * Returns the scale’s current domain. + */ + domain(): Domain[]; + /** + * Sets the scale’s domain to the specified array of values. The values must be in sorted ascending order, or the behavior of the scale is undefined. + * The values are typically numbers, but any naturally ordered values (such as strings) will work; a threshold scale can be used to encode any type that is ordered. + * If the number of values in the scale’s range is N+1, the number of values in the scale’s domain must be N. + * If there are fewer than N elements in the domain, the additional values in the range are ignored. + * If there are more than N elements in the domain, the scale may return undefined for some inputs. + * + * @param domain Array of domain values. + */ + domain(domain: ReadonlyArray): this; + + /** + * Returns the scale’s current range. + */ + range(): Range[]; + /** + * Sets the scale’s range to the specified array of values. If the number of values in the scale’s domain is N, the number of values in the scale’s range must be N+1. + * If there are fewer than N+1 elements in the range, the scale may return undefined for some inputs. + * If there are more than N+1 elements in the range, the additional values are ignored. + * + * @param range Array of range values. + */ + range(range: ReadonlyArray): this; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new threshold scale with the default domain [0.5] and the default range [0, 1]. + * Thus, the default threshold scale is equivalent to the Math.round function for numbers; for example threshold(0.49) returns 0, and threshold(0.51) returns 1. + */ + export function scaleThreshold(): ScaleThreshold; + /** + * Constructs a new threshold scale. The domain and range must be set corresponding to the type of the corresponding generic. + * + * The first generic corresponds to the data type of domain values. + * The second generic corresponds to the data type of range values. + */ + export function scaleThreshold(): ScaleThreshold< + Domain, + Range + >; + + // ------------------------------------------------------------------------------- + // Ordinal Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Unlike continuous scales, ordinal scales have a discrete domain and range. For example, an ordinal scale might map a set of named categories to a set of colors, + * or determine the horizontal positions of columns in a column chart. + * + * 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. + * + * The first generic corresponds to the data type of domain values. + * The second generic corresponds to the data type of range values. + */ + export interface ScaleOrdinal { + /** + * Given a value in the input domain, returns the corresponding value in the output range. + * If the given value is not in the scale’s domain, returns the unknown; or, if the unknown value is implicit (the default), + * then the value is implicitly added to the domain and the next-available value in the range is assigned to value, + * such that this and subsequent invocations of the scale given the same input value return the same output value. + * + * @param x A value from the domain. + */ + (x: Domain): Range; + + /** + * Returns the scale's current domain. + */ + domain(): Domain[]; + /** + * Sets the domain to the specified array of values. + * + * The first element in domain will be mapped to the first element in the range, + * the second domain value to the second range value, and so on. + * + * Domain values are stored internally in a map from stringified value to index; the resulting index is then used to retrieve a value from the range. + * Thus, an ordinal scale’s values must be coercible to a string, and the stringified version of the domain value uniquely identifies the corresponding range value. + * + * Setting the domain on an ordinal scale is optional if the unknown value is implicit (the default). + * In this case, the domain will be inferred implicitly from usage by assigning each unique value passed to the scale a new value from the range. + * Note that an explicit domain is recommended to ensure deterministic behavior, as inferring the domain from usage will be dependent on ordering. + * + * @param domain Array of domain values. + */ + domain(domain: ReadonlyArray): this; + + /** + * Returns the scale's current range. + */ + range(): Range[]; + /** + * 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(range: ReadonlyArray): this; + + /** + * Returns the current unknown value, which defaults to "implicit". + */ + unknown(): Range | { 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: Range | { name: 'implicit' }): this; + + /** + * Returns an exact copy of this ordinal scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new ordinal scale with an empty domain and the specified range. + * If a range is not specified, it defaults to the empty array; an ordinal scale always returns undefined until a non-empty range is defined. + * + * By default, the domain is configured to generate implicitly, if the scale is invoked with an unknown value. + * See the "unknown(...)" method of the scale to change this behavior. + * + * The generic corresponds to the data type of range elements. + * + * @parm range An optional array of range values to initialize the scale with. + */ + export function scaleOrdinal(range?: ReadonlyArray): ScaleOrdinal; + /** + * Constructs a new ordinal scale with an empty domain and the specified range. + * If a range is not specified, it defaults to the empty array; an ordinal scale always returns undefined until a non-empty range is defined. + * + * By default, the domain is configured to generate implicitly, if the scale is invoked with an unknown value. + * See the "unknown(...)" method of the scale to change this behavior. + * + * The first generic corresponds to the data type of domain elements. + * The second generic corresponds to the data type of range elements. + * + * @parm range An optional array of range values to initialize the scale with. + */ + export function scaleOrdinal( + range?: ReadonlyArray, + ): ScaleOrdinal; + + /** + * A special value for ordinal.unknown that enables implicit domain construction: unknown values are implicitly added to the domain. + */ + export const scaleImplicit: { name: 'implicit' }; + + // ------------------------------------------------------------------------------- + // Band Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Band scales are like ordinal scales except the output range is continuous and numeric. + * Discrete output values are automatically computed by the scale by dividing the continuous range into uniform bands. + * Band scales are typically used for bar charts with an ordinal or categorical dimension. + * The unknown value of a band scale is effectively undefined: they do not allow implicit domain construction. + * + * The generic corresponds to the data type of domain elements. + */ + export interface ScaleBand { + /** + * Given a value in the input domain, returns the start of the corresponding band derived from the output range. + * If the given value is not in the scale’s domain, returns undefined. + * + * @param x A value from the domain. + */ + (x: Domain): number | undefined; + + /** + * Returns to scale's current domain + */ + domain(): Domain[]; + /** + * Sets the domain to the specified array of values. The first element in domain will be mapped to the first band, the second domain value to the second band, and so on. + * Domain values are stored internally in a map from stringified value to index; the resulting index is then used to determine the band. + * Thus, a band scale’s values must be coercible to a string, and the stringified version of the domain value uniquely identifies the corresponding band. + * + * @param domain Array of domain values. + */ + domain(domain: ReadonlyArray): this; + + /** + * Returns the scale’s current range, which defaults to [0, 1]. + */ + range(): [number, number]; + /** + * Sets the scale’s range to the specified two-element array of numbers. If the elements in the given array are not numbers, they will be coerced to numbers. + * The default range is [0, 1]. + * + * @param range A two-element array of numeric values. + */ + range(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this; + + /** + * Sets the scale’s range to the specified two-element array of numbers while also enabling rounding. + * If the elements in the given array are not numbers, they will be coerced to numbers. + * + * Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the shape-rendering “crispEdges” styles. + * + * @param range A two-element array of numeric values. + */ + rangeRound(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this; + + /** + * Returns the current rounding status for the scale: enabled (= true) or disabled (= false). + */ + round(): boolean; + /** + * Enables or disables rounding accordingly. If rounding is enabled, the start and stop of each band will be integers. + * Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the shape-rendering “crispEdges” styles. + * Note that if the width of the domain is not a multiple of the cardinality of the range, there may be leftover unused space, even without padding! + * Use band.align to specify how the leftover space is distributed. + * + * @param round Enable rounding (= true), disable rounding (= false). + */ + round(round: boolean): this; + + /** + * Returns the current inner padding which defaults to 0. + */ + paddingInner(): number; + /** + * Sets the inner padding to the specified value which must be in the range [0, 1]. + * The inner padding determines the ratio of the range that is reserved for blank space between bands. + * + * The default setting is 0. + * + * @param padding Value for inner padding in [0, 1] interval. + */ + paddingInner(padding: number): this; + + /** + * Returns the current outer padding which defaults to 0. + */ + paddingOuter(): number; + /** + * Sets the outer padding to the specified value which must be in the range [0, 1]. + * The outer padding determines the ratio of the range that is reserved for blank space before the first band and after the last band. + * + * The default setting is 0. + * + * @param padding Value for outer padding in [0, 1] interval. + */ + paddingOuter(padding: number): this; + + /** + * Returns the inner padding. + */ + padding(): number; + /** + * A convenience method for setting the inner and outer padding to the same padding value. + * + * @param padding Value for inner and outer padding in [0, 1] interval. + */ + padding(padding: number): this; + + /** + * Returns the current alignment which defaults to 0.5. + */ + align(): number; + /** + * Sets the alignment to the specified value which must be in the range [0, 1]. + * + * The default is 0.5. + * + * The alignment determines how any leftover unused space in the range is distributed. + * A value of 0.5 indicates that the leftover space should be equally distributed before the first band and after the last band; + * i.e., the bands should be centered within the range. A value of 0 or 1 may be used to shift the bands to one side, say to position them adjacent to an axis. + * + * @param align Value for alignment setting in [0, 1] interval. + */ + align(align: number): this; + + /** + * Returns the width of each band. + */ + bandwidth(): number; + + /** + * Returns the distance between the starts of adjacent bands. + */ + step(): number; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new band scale with the empty domain, the unit range [0, 1], no padding, no rounding and center alignment. + */ + export function scaleBand(): ScaleBand; + /** + * Constructs a new band scale with the empty domain, the unit range [0, 1], no padding, no rounding and center alignment. + * + * The generic corresponds to the data type of domain elements. + */ + export function scaleBand(): ScaleBand< + Domain + >; + + // ------------------------------------------------------------------------------- + // Point Scale Factory + // ------------------------------------------------------------------------------- + + /** + * Point scales are a variant of band scales with the bandwidth fixed to zero. + * Point scales are typically used for scatterplots with an ordinal or categorical dimension. + * The unknown value of a point scale is always undefined: they do not allow implicit domain construction. + * + * The generic corresponds to the data type of domain elements. + */ + export interface ScalePoint { + /** + * Given a value in the input domain, returns the corresponding point derived from the output range. + * If the given value is not in the scale’s domain, returns undefined. + * + * @param x A value from the domain. + */ + (x: Domain): number | undefined; + + /** + * Returns the scale's current domain. + */ + domain(): Domain[]; + /** + * Sets the domain to the specified array of values. The first element in domain will be mapped to the first point, the second domain value to the second point, and so on. + * Domain values are stored internally in a map from stringified value to index; the resulting index is then used to determine the point. + * Thus, a point scale’s values must be coercible to a string, and the stringified version of the domain value uniquely identifies the corresponding point. + * + * @param domain Array of domain values. + */ + domain(domain: ReadonlyArray): this; + + /** + * Returns the scale’s current range, which defaults to [0, 1]. + */ + range(): [number, number]; + /** + * Sets the scale’s range to the specified two-element array of numbers. + * If the elements in the given array are not numbers, they will be coerced to numbers. + * The default range is [0, 1]. + * + * @param range A two-element array of numeric values. + */ + range(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this; + + /** + * Sets the scale’s range to the specified two-element array of numbers while also enabling rounding. + * If the elements in the given array are not numbers, they will be coerced to numbers. + * + * Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the shape-rendering “crispEdges” styles. + * + * @param range A two-element array of numeric values. + */ + rangeRound(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this; + + /** + * Returns the current rounding status for the scale: enabled (= true) or disabled (= false). + */ + round(): boolean; + /** + * Enables or disables rounding accordingly. If rounding is enabled, the position of each point will be integers. + * Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the shape-rendering “crispEdges” styles. + * Note that if the width of the domain is not a multiple of the cardinality of the range, there may be leftover unused space, even without padding! + * Use point.align to specify how the leftover space is distributed. + * + * @param round Enable rounding (= true), disable rounding (= false). + */ + round(round: boolean): this; + + /** + * Returns the current outer padding which defaults to 0. + * The outer padding determines the ratio of the range that is reserved for blank space + * before the first point and after the last point. + * + */ + padding(): number; + /** + * Sets the outer padding to the specified value which must be in the range [0, 1]. + * The outer padding determines the ratio of the range that is reserved for blank space + * before the first point and after the last point. + * + * The default is 0. + * + * @param padding Value for outer padding in [0, 1] interval. + */ + padding(padding: number): this; + + /** + * Returns the current alignment which defaults to 0.5. + */ + align(): number; + /** + * Sets the alignment to the specified value which must be in the range [0, 1]. + * + * The alignment determines how any leftover unused space in the range is distributed. + * A value of 0.5 indicates that the leftover space should be equally distributed before the first point and after the last point; + * i.e., the points should be centered within the range. A value of 0 or 1 may be used to shift the points to one side, say to position them adjacent to an axis. + * + * The default value is 0.5. + * + * @param align Value for alignment setting in [0, 1] interval. + */ + align(align: number): this; + + /** + * Return 0. + */ + bandwidth(): number; + + /** + * Returns the distance between the starts of adjacent points. + */ + step(): number; + + /** + * Returns an exact copy of this scale. Changes to this scale will not affect the returned scale, and vice versa. + */ + copy(): this; + } + + /** + * Constructs a new point scale with the empty domain, the unit range [0, 1], no padding, no rounding and center alignment. + */ + export function scalePoint(): ScalePoint; + /** + * Constructs a new point scale with the empty domain, the unit range [0, 1], no padding, no rounding and center alignment. + * + * The generic corresponds to the data type of domain elements. + */ + export function scalePoint< + Domain extends { toString(): string } | null | undefined + >(): ScalePoint; +}