svelteplot 0.0.1-alpha.9 → 0.1.3-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +5 -0
- package/README.md +3 -36
- package/dist/Mark.svelte +290 -0
- package/dist/Mark.svelte.d.ts +22 -0
- package/dist/Plot.svelte +148 -156
- package/dist/Plot.svelte.d.ts +15 -15
- package/dist/constants.d.ts +14 -0
- package/dist/constants.js +109 -0
- package/dist/core/Facet.svelte +59 -0
- package/dist/core/Facet.svelte.d.ts +18 -0
- package/dist/core/FacetAxes.svelte +66 -0
- package/dist/core/FacetAxes.svelte.d.ts +4 -0
- package/dist/core/FacetGrid.svelte +86 -0
- package/dist/core/FacetGrid.svelte.d.ts +13 -0
- package/dist/core/Plot.svelte +567 -0
- package/dist/core/Plot.svelte.d.ts +14 -0
- package/dist/helpers/arrowPath.d.ts +14 -0
- package/dist/helpers/arrowPath.js +129 -0
- package/dist/helpers/autoProjection.d.ts +19 -0
- package/dist/helpers/autoProjection.js +87 -0
- package/dist/helpers/autoScales.d.ts +23 -0
- package/dist/helpers/autoScales.js +203 -0
- package/dist/helpers/autoTicks.d.ts +3 -0
- package/dist/helpers/autoTicks.js +40 -0
- package/dist/helpers/autoTimeFormat.d.ts +2 -2
- package/dist/helpers/autoTimeFormat.js +34 -5
- package/dist/helpers/callWithProps.d.ts +8 -0
- package/dist/helpers/callWithProps.js +13 -0
- package/dist/helpers/colors.js +17 -2
- package/dist/helpers/curves.d.ts +3 -0
- package/dist/helpers/curves.js +42 -0
- package/dist/helpers/data.d.ts +9 -0
- package/dist/helpers/data.js +16 -0
- package/dist/helpers/facets.d.ts +12 -0
- package/dist/helpers/facets.js +49 -0
- package/dist/helpers/formats.d.ts +3 -0
- package/dist/helpers/formats.js +3 -0
- package/dist/helpers/getBaseStyles.d.ts +7 -2
- package/dist/helpers/getBaseStyles.js +34 -10
- package/dist/helpers/getLogTicks.js +5 -5
- package/dist/helpers/group.d.ts +6 -0
- package/dist/helpers/group.js +53 -0
- package/dist/helpers/index.d.ts +18 -0
- package/dist/helpers/index.js +53 -0
- package/dist/helpers/isRawValue.d.ts +2 -0
- package/dist/helpers/isRawValue.js +5 -0
- package/dist/helpers/isValid.d.ts +6 -0
- package/dist/helpers/isValid.js +6 -0
- package/dist/helpers/math.d.ts +19 -0
- package/dist/helpers/math.js +116 -0
- package/dist/helpers/mergeDeep.d.ts +1 -1
- package/dist/helpers/noise.d.ts +1 -0
- package/dist/helpers/noise.js +72 -0
- package/dist/helpers/projection.d.ts +33 -0
- package/dist/helpers/projection.js +100 -0
- package/dist/helpers/reduce.d.ts +10 -0
- package/dist/helpers/reduce.js +85 -0
- package/dist/helpers/regressionLoess.d.ts +12 -0
- package/dist/helpers/regressionLoess.js +47 -0
- package/dist/helpers/removeIdenticalLines.d.ts +8 -1
- package/dist/helpers/removeIdenticalLines.js +14 -7
- package/dist/helpers/resolve.d.ts +17 -0
- package/dist/helpers/resolve.js +152 -0
- package/dist/helpers/roundedRect.d.ts +9 -0
- package/dist/helpers/roundedRect.js +31 -0
- package/dist/helpers/scales.d.ts +42 -0
- package/dist/helpers/scales.js +309 -0
- package/dist/helpers/time.d.ts +6 -0
- package/dist/helpers/time.js +282 -0
- package/dist/helpers/typeChecks.d.ts +8 -5
- package/dist/helpers/typeChecks.js +27 -6
- package/dist/index.d.ts +49 -1
- package/dist/index.js +53 -2
- package/dist/marks/Area.svelte +146 -0
- package/dist/marks/Area.svelte.d.ts +30 -0
- package/dist/marks/AreaX.svelte +27 -0
- package/dist/marks/AreaX.svelte.d.ts +12 -0
- package/dist/marks/AreaY.svelte +38 -0
- package/dist/marks/AreaY.svelte.d.ts +19 -0
- package/dist/marks/Arrow.svelte +139 -0
- package/dist/marks/Arrow.svelte.d.ts +44 -0
- package/dist/marks/AxisX.svelte +198 -93
- package/dist/marks/AxisX.svelte.d.ts +17 -16
- package/dist/marks/AxisY.svelte +176 -62
- package/dist/marks/AxisY.svelte.d.ts +17 -14
- package/dist/marks/BarX.svelte +81 -0
- package/dist/marks/BarX.svelte.d.ts +4 -0
- package/dist/marks/BarY.svelte +95 -0
- package/dist/marks/BarY.svelte.d.ts +4 -0
- package/dist/marks/BollingerX.svelte +44 -0
- package/dist/marks/BollingerX.svelte.d.ts +18 -0
- package/dist/marks/BollingerY.svelte +39 -0
- package/dist/marks/BollingerY.svelte.d.ts +18 -0
- package/dist/marks/BoxX.svelte +89 -0
- package/dist/marks/BoxX.svelte.d.ts +4 -0
- package/dist/marks/BoxY.svelte +110 -0
- package/dist/marks/BoxY.svelte.d.ts +29 -0
- package/dist/marks/Cell.svelte +110 -0
- package/dist/marks/Cell.svelte.d.ts +16 -0
- package/dist/marks/CellX.svelte +24 -0
- package/dist/marks/CellX.svelte.d.ts +3 -0
- package/dist/marks/CellY.svelte +24 -0
- package/dist/marks/CellY.svelte.d.ts +3 -0
- package/dist/marks/ColorLegend.svelte +148 -27
- package/dist/marks/ColorLegend.svelte.d.ts +12 -13
- package/dist/marks/CustomMark.svelte +43 -0
- package/dist/marks/CustomMark.svelte.d.ts +16 -0
- package/dist/marks/CustomMarkHTML.svelte +103 -0
- package/dist/marks/CustomMarkHTML.svelte.d.ts +17 -0
- package/dist/marks/DifferenceY.svelte +144 -0
- package/dist/marks/DifferenceY.svelte.d.ts +30 -0
- package/dist/marks/Dot.svelte +128 -73
- package/dist/marks/Dot.svelte.d.ts +24 -14
- package/dist/marks/DotX.svelte +15 -3
- package/dist/marks/DotX.svelte.d.ts +8 -16
- package/dist/marks/DotY.svelte +8 -3
- package/dist/marks/DotY.svelte.d.ts +5 -17
- package/dist/marks/Frame.svelte +32 -31
- package/dist/marks/Frame.svelte.d.ts +7 -14
- package/dist/marks/Geo.svelte +102 -0
- package/dist/marks/Geo.svelte.d.ts +10 -0
- package/dist/marks/Graticule.svelte +28 -0
- package/dist/marks/Graticule.svelte.d.ts +9 -0
- package/dist/marks/GridX.svelte +67 -36
- package/dist/marks/GridX.svelte.d.ts +7 -18
- package/dist/marks/GridY.svelte +64 -25
- package/dist/marks/GridY.svelte.d.ts +7 -14
- package/dist/marks/HTMLTooltip.svelte +91 -0
- package/dist/marks/HTMLTooltip.svelte.d.ts +11 -0
- package/dist/marks/Line.svelte +219 -58
- package/dist/marks/Line.svelte.d.ts +30 -14
- package/dist/marks/LineX.svelte +8 -8
- package/dist/marks/LineX.svelte.d.ts +4 -17
- package/dist/marks/LineY.svelte +7 -8
- package/dist/marks/LineY.svelte.d.ts +4 -17
- package/dist/marks/Link.svelte +173 -0
- package/dist/marks/Link.svelte.d.ts +21 -0
- package/dist/marks/Pointer.svelte +126 -0
- package/dist/marks/Pointer.svelte.d.ts +23 -0
- package/dist/marks/Rect.svelte +103 -0
- package/dist/marks/Rect.svelte.d.ts +15 -0
- package/dist/marks/RectX.svelte +33 -0
- package/dist/marks/RectX.svelte.d.ts +15 -0
- package/dist/marks/RectY.svelte +33 -0
- package/dist/marks/RectY.svelte.d.ts +15 -0
- package/dist/marks/RegressionX.svelte +26 -0
- package/dist/marks/RegressionX.svelte.d.ts +4 -0
- package/dist/marks/RegressionY.svelte +26 -0
- package/dist/marks/RegressionY.svelte.d.ts +4 -0
- package/dist/marks/RuleX.svelte +52 -28
- package/dist/marks/RuleX.svelte.d.ts +14 -14
- package/dist/marks/RuleY.svelte +52 -28
- package/dist/marks/RuleY.svelte.d.ts +14 -14
- package/dist/marks/Sphere.svelte +8 -0
- package/dist/marks/Sphere.svelte.d.ts +51 -0
- package/dist/marks/Spike.svelte +15 -0
- package/dist/marks/Spike.svelte.d.ts +4 -0
- package/dist/marks/SymbolLegend.svelte +27 -12
- package/dist/marks/SymbolLegend.svelte.d.ts +8 -14
- package/dist/marks/Text.svelte +185 -0
- package/dist/marks/Text.svelte.d.ts +26 -0
- package/dist/marks/TickX.svelte +89 -0
- package/dist/marks/TickX.svelte.d.ts +22 -0
- package/dist/marks/TickY.svelte +90 -0
- package/dist/marks/TickY.svelte.d.ts +22 -0
- package/dist/marks/Vector.svelte +213 -0
- package/dist/marks/Vector.svelte.d.ts +31 -0
- package/dist/marks/helpers/BaseAxisX.svelte +210 -0
- package/dist/marks/helpers/BaseAxisX.svelte.d.ts +24 -0
- package/dist/marks/helpers/BaseAxisY.svelte +187 -0
- package/dist/marks/helpers/BaseAxisY.svelte.d.ts +23 -0
- package/dist/marks/helpers/CanvasLayer.svelte +38 -0
- package/dist/marks/helpers/CanvasLayer.svelte.d.ts +13 -0
- package/dist/marks/helpers/DotCanvas.svelte +184 -0
- package/dist/marks/helpers/DotCanvas.svelte.d.ts +11 -0
- package/dist/marks/helpers/GeoCanvas.svelte +165 -0
- package/dist/marks/helpers/GeoCanvas.svelte.d.ts +13 -0
- package/dist/marks/helpers/GroupMultiple.svelte +17 -0
- package/dist/marks/helpers/GroupMultiple.svelte.d.ts +9 -0
- package/dist/marks/helpers/Marker.svelte +93 -0
- package/dist/marks/helpers/Marker.svelte.d.ts +10 -0
- package/dist/marks/helpers/MarkerPath.svelte +164 -0
- package/dist/marks/helpers/MarkerPath.svelte.d.ts +44 -0
- package/dist/marks/helpers/Regression.svelte +174 -0
- package/dist/marks/helpers/Regression.svelte.d.ts +26 -0
- package/dist/marks/helpers/events.d.ts +8 -0
- package/dist/marks/helpers/events.js +64 -0
- package/dist/transforms/bin.d.ts +51 -0
- package/dist/transforms/bin.js +171 -0
- package/dist/transforms/bollinger.d.ts +21 -0
- package/dist/transforms/bollinger.js +53 -0
- package/dist/transforms/centroid.d.ts +9 -0
- package/dist/transforms/centroid.js +13 -0
- package/dist/transforms/facet.d.ts +1 -0
- package/dist/transforms/facet.js +1 -0
- package/dist/transforms/filter.d.ts +2 -0
- package/dist/transforms/filter.js +8 -0
- package/dist/transforms/group.d.ts +66 -0
- package/dist/transforms/group.js +109 -0
- package/dist/transforms/interval.d.ts +11 -0
- package/dist/transforms/interval.js +34 -0
- package/dist/transforms/jitter.d.ts +0 -0
- package/dist/transforms/jitter.js +1 -0
- package/dist/transforms/map.d.ts +10 -0
- package/dist/transforms/map.js +89 -0
- package/dist/transforms/normalize.d.ts +9 -0
- package/dist/transforms/normalize.js +86 -0
- package/dist/transforms/recordize.d.ts +13 -0
- package/dist/transforms/recordize.js +75 -0
- package/dist/transforms/rename.d.ts +14 -0
- package/dist/transforms/rename.js +42 -0
- package/dist/transforms/select.d.ts +35 -0
- package/dist/transforms/select.js +55 -0
- package/dist/transforms/shift.d.ts +13 -0
- package/dist/transforms/shift.js +45 -0
- package/dist/transforms/sort.d.ts +28 -0
- package/dist/transforms/sort.js +61 -0
- package/dist/transforms/stack.d.ts +10 -0
- package/dist/transforms/stack.js +110 -0
- package/dist/transforms/window.d.ts +22 -0
- package/dist/transforms/window.js +73 -0
- package/dist/types.d.ts +625 -188
- package/dist/ui/Checkbox.svelte +6 -0
- package/dist/ui/Checkbox.svelte.d.ts +13 -0
- package/dist/ui/RadioInput.svelte +27 -0
- package/dist/ui/RadioInput.svelte.d.ts +9 -0
- package/dist/ui/Select.svelte +27 -0
- package/dist/ui/Select.svelte.d.ts +9 -0
- package/dist/ui/Slider.svelte +47 -0
- package/dist/ui/Slider.svelte.d.ts +11 -0
- package/dist/ui/Spiral.svelte +46 -0
- package/dist/ui/Spiral.svelte.d.ts +15 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +4 -0
- package/package.json +79 -40
- package/LICENSE +0 -11
- package/dist/classes/Channel.svelte.js +0 -74
- package/dist/classes/Mark.svelte.js +0 -17
- package/dist/classes/Plot.svelte.js +0 -98
- package/dist/contants.d.ts +0 -3
- package/dist/contants.js +0 -40
- package/dist/helpers/GroupMultiple.svelte +0 -8
- package/dist/helpers/GroupMultiple.svelte.d.ts +0 -19
- package/dist/helpers/createScale.d.ts +0 -5
- package/dist/helpers/createScale.js +0 -57
- package/dist/helpers/resolveChannel.d.ts +0 -2
- package/dist/helpers/resolveChannel.js +0 -28
- package/dist/helpers/wrapArray.d.ts +0 -2
- package/dist/helpers/wrapArray.js +0 -4
- package/dist/marks/BaseMark.svelte +0 -22
- package/dist/marks/BaseMark.svelte.d.ts +0 -19
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { extent, range as d3Range, ascending } from 'd3-array';
|
|
2
|
+
import { isColorOrNull, isDateOrNull, isNumberOrNull, isNumberOrNullOrNaN, isStringOrNull } from './typeChecks.js';
|
|
3
|
+
import { CHANNEL_SCALE, VALID_SCALE_TYPES } from '../constants.js';
|
|
4
|
+
import { isSymbolOrNull } from './typeChecks.js';
|
|
5
|
+
import { resolveProp, toChannelOption } from './resolve.js';
|
|
6
|
+
import isDataRecord from './isDataRecord.js';
|
|
7
|
+
import { createProjection } from './projection.js';
|
|
8
|
+
/**
|
|
9
|
+
* compute the plot scales
|
|
10
|
+
*/
|
|
11
|
+
export function computeScales(plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, marks, plotDefaults) {
|
|
12
|
+
const x = createScale('x', plotOptions.x, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
13
|
+
const y = createScale('y', plotOptions.y, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
14
|
+
const r = createScale('r', plotOptions.r, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
15
|
+
const color = createScale('color', plotOptions.color, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
16
|
+
const opacity = createScale('opacity', plotOptions.opacity, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
17
|
+
const length = createScale('length', plotOptions.length, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
18
|
+
const symbol = createScale('symbol', plotOptions.symbol, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
19
|
+
// create fx and fy scales from mark data directly
|
|
20
|
+
const fx = createScale('fx', plotOptions.fx, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
21
|
+
const fy = createScale('fy', plotOptions.fy, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults);
|
|
22
|
+
const projection = plotOptions.projection
|
|
23
|
+
? createProjection({ projOptions: plotOptions.projection, inset: plotOptions.inset }, {
|
|
24
|
+
width: plotWidth,
|
|
25
|
+
height: plotHeight,
|
|
26
|
+
marginBottom: plotOptions.marginBottom,
|
|
27
|
+
marginLeft: plotOptions.marginLeft,
|
|
28
|
+
marginRight: plotOptions.marginRight,
|
|
29
|
+
marginTop: plotOptions.marginTop
|
|
30
|
+
})
|
|
31
|
+
: null;
|
|
32
|
+
return { x, y, r, color, opacity, length, symbol, fx, fy, projection };
|
|
33
|
+
}
|
|
34
|
+
export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, plotHeight, plotHasFilledDotMarks, plotDefaults) {
|
|
35
|
+
if (!plotOptions.implicitScales && !scaleOptions.scale) {
|
|
36
|
+
// no scale defined, return a dummy scale
|
|
37
|
+
const fn = name === 'color' ? () => 'currentColor' : () => 0;
|
|
38
|
+
fn.range = name === 'color' ? () => ['currentColor'] : () => [0];
|
|
39
|
+
return { type: 'linear', domain: [0], range: [0], fn, skip: new Map(), isDummy: true };
|
|
40
|
+
}
|
|
41
|
+
// gather all marks that use channels which support this scale
|
|
42
|
+
const dataValues = new Set();
|
|
43
|
+
const allDataValues = [];
|
|
44
|
+
const markTypes = new Set();
|
|
45
|
+
const skip = new Map();
|
|
46
|
+
let manualActiveMarks = 0;
|
|
47
|
+
const propNames = new Set();
|
|
48
|
+
const uniqueScaleProps = new Set();
|
|
49
|
+
let sortOrdinalDomain = true;
|
|
50
|
+
for (const mark of marks) {
|
|
51
|
+
// we only sort the scale domain alphabetically, if none of the
|
|
52
|
+
// marks that map to it are using the `sort` transform. Note that
|
|
53
|
+
// we're deliberately checking for !== undefined and not for != null
|
|
54
|
+
// since the explicit sort transforms like shuffle will set the
|
|
55
|
+
// sort channel to null to we know that there's an explicit order
|
|
56
|
+
if (mark.channels.sort !== undefined)
|
|
57
|
+
sortOrdinalDomain = false;
|
|
58
|
+
for (const channel of mark.channels) {
|
|
59
|
+
// channelOptions can be passed as prop, but most often users will just
|
|
60
|
+
// pass the channel accessor or constant value, so we may need to wrap
|
|
61
|
+
if (!skip.has(channel))
|
|
62
|
+
skip.set(channel, new Set());
|
|
63
|
+
if (mark.data.length > 0) {
|
|
64
|
+
const channelOptions = isDataRecord(mark.options[channel])
|
|
65
|
+
? mark.options[channel]
|
|
66
|
+
: { value: mark.options[channel], scale: CHANNEL_SCALE[channel] };
|
|
67
|
+
// check if this mark channel is using this scale, which users can prevent
|
|
68
|
+
// by passing `{ scale: null }` as prop
|
|
69
|
+
const useScale = channelOptions.scale === name &&
|
|
70
|
+
// only use scale if implicit scales are enabled or use has explicitly
|
|
71
|
+
// defined a scale
|
|
72
|
+
(plotOptions.implicitScales || scaleOptions.scale) &&
|
|
73
|
+
// type number means, someone is defining a channel as constant, e.g.
|
|
74
|
+
// <Dot r={10} /> in which case we don't want to pass it through a scale
|
|
75
|
+
// typeof channelOptions.value !== 'number' &&
|
|
76
|
+
typeof channelOptions.value !== 'undefined';
|
|
77
|
+
if (useScale) {
|
|
78
|
+
if (name === 'opacity' && looksLikeOpacity(channelOptions.value)) {
|
|
79
|
+
// special handling for opacity scales, where any accessor that looks like
|
|
80
|
+
// a number between 0 and 1 will be interpreted as output type
|
|
81
|
+
skip.get(channel).add(mark.id);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const isOutputType = name === 'color'
|
|
85
|
+
? isColorOrNull
|
|
86
|
+
: name === 'symbol'
|
|
87
|
+
? isSymbolOrNull
|
|
88
|
+
: false;
|
|
89
|
+
let allValuesAreOutputType = !!isOutputType && mark.data.length > 0;
|
|
90
|
+
if (isOutputType) {
|
|
91
|
+
for (const datum of mark.data) {
|
|
92
|
+
const val = resolveProp(channelOptions.value, datum);
|
|
93
|
+
allValuesAreOutputType =
|
|
94
|
+
allValuesAreOutputType && val !== null && isOutputType(val);
|
|
95
|
+
if (!allValuesAreOutputType)
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (allValuesAreOutputType) {
|
|
100
|
+
skip.get(channel).add(mark.id);
|
|
101
|
+
}
|
|
102
|
+
if (typeof channelOptions.value === 'string' &&
|
|
103
|
+
!looksLikeANumber(channelOptions.value) &&
|
|
104
|
+
!channelOptions.value.startsWith('__') &&
|
|
105
|
+
mark.data[0][channelOptions.value] !== undefined) {
|
|
106
|
+
propNames.add(channelOptions.value);
|
|
107
|
+
}
|
|
108
|
+
uniqueScaleProps.add(channelOptions.value);
|
|
109
|
+
if (channelOptions.value != null && !allValuesAreOutputType) {
|
|
110
|
+
manualActiveMarks++;
|
|
111
|
+
markTypes.add(mark.type);
|
|
112
|
+
// active mark channel
|
|
113
|
+
for (const datum of mark.data) {
|
|
114
|
+
const value = resolveProp(channelOptions.value, datum);
|
|
115
|
+
dataValues.add(value);
|
|
116
|
+
if (name === 'color' && scaleOptions.type === 'quantile') {
|
|
117
|
+
allDataValues.push(value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// special handling of marks using the stackX/stackY transform
|
|
124
|
+
if ((name === 'x' || name === 'y') &&
|
|
125
|
+
mark.options[`__${name}_origField`] &&
|
|
126
|
+
!mark.options[`__${name}_origField`].startsWith('__')) {
|
|
127
|
+
propNames.add(mark.options[`__${name}_origField`]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// also skip marks without data to prevent exceptions
|
|
132
|
+
// (skip.get(channel) as Set<symbol>).add(mark.id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// construct domain from data values
|
|
137
|
+
const valueArr = [...dataValues.values(), ...(scaleOptions.domain || [])].filter((d) => d != null);
|
|
138
|
+
const type = scaleOptions.type === 'auto'
|
|
139
|
+
? inferScaleType(name, valueArr, markTypes)
|
|
140
|
+
: scaleOptions.type;
|
|
141
|
+
if (VALID_SCALE_TYPES[name] && !VALID_SCALE_TYPES[name].has(type)) {
|
|
142
|
+
throw new Error(`Invalid scale type ${type} for scale
|
|
143
|
+
${name}. Valid types are ${[...VALID_SCALE_TYPES[name]].join(', ')}`);
|
|
144
|
+
}
|
|
145
|
+
const isOrdinal = type === 'band' || type === 'point' || type === 'ordinal' || type === 'categorical';
|
|
146
|
+
if (isOrdinal && sortOrdinalDomain) {
|
|
147
|
+
valueArr.sort(ascending);
|
|
148
|
+
}
|
|
149
|
+
const domain = scaleOptions.domain
|
|
150
|
+
? scaleOptions.domain
|
|
151
|
+
: type === 'band' ||
|
|
152
|
+
type === 'point' ||
|
|
153
|
+
type === 'ordinal' ||
|
|
154
|
+
type === 'categorical' ||
|
|
155
|
+
type === 'quantile' ||
|
|
156
|
+
type === 'quantile-cont'
|
|
157
|
+
? name === 'y'
|
|
158
|
+
? valueArr.toReversed()
|
|
159
|
+
: valueArr
|
|
160
|
+
: extent(scaleOptions.zero ? [0, ...valueArr] : valueArr);
|
|
161
|
+
if (!scaleOptions.scale) {
|
|
162
|
+
throw new Error(`No scale function defined for ${name}`);
|
|
163
|
+
}
|
|
164
|
+
const fn = scaleOptions.scale({
|
|
165
|
+
name,
|
|
166
|
+
type,
|
|
167
|
+
domain,
|
|
168
|
+
scaleOptions,
|
|
169
|
+
plotOptions,
|
|
170
|
+
plotWidth,
|
|
171
|
+
plotHeight,
|
|
172
|
+
plotHasFilledDotMarks,
|
|
173
|
+
plotDefaults
|
|
174
|
+
});
|
|
175
|
+
const range = fn.range();
|
|
176
|
+
return {
|
|
177
|
+
type,
|
|
178
|
+
domain,
|
|
179
|
+
range,
|
|
180
|
+
fn,
|
|
181
|
+
skip,
|
|
182
|
+
manualActiveMarks,
|
|
183
|
+
uniqueScaleProps,
|
|
184
|
+
autoTitle: type === 'time'
|
|
185
|
+
? null
|
|
186
|
+
: propNames.size === 1
|
|
187
|
+
? `${[...propNames.values()][0]}${type === 'log' ? ' (log)' : ''}`
|
|
188
|
+
: null
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Infer a scale type based on the scale name, the data values mapped to it and
|
|
193
|
+
* the mark types that are bound to the scale
|
|
194
|
+
*/
|
|
195
|
+
export function inferScaleType(name, dataValues, markTypes) {
|
|
196
|
+
if (name === 'color') {
|
|
197
|
+
if (!dataValues.length)
|
|
198
|
+
return 'ordinal';
|
|
199
|
+
if (dataValues.every(isNumberOrNullOrNaN))
|
|
200
|
+
return 'linear';
|
|
201
|
+
if (dataValues.every(isDateOrNull))
|
|
202
|
+
return 'linear';
|
|
203
|
+
if (dataValues.every(isStringOrNull))
|
|
204
|
+
return 'categorical';
|
|
205
|
+
return 'categorical';
|
|
206
|
+
}
|
|
207
|
+
if (name === 'symbol')
|
|
208
|
+
return 'ordinal';
|
|
209
|
+
// for positional scales, try to pick a scale that's required by the mark types
|
|
210
|
+
if ((name === 'x' || name === 'y') && markTypes.size === 1) {
|
|
211
|
+
if (name === 'y' &&
|
|
212
|
+
(markTypes.has('barX') || markTypes.has('tickX') || markTypes.has('cell')))
|
|
213
|
+
return 'band';
|
|
214
|
+
if (name === 'x' &&
|
|
215
|
+
(markTypes.has('barY') || markTypes.has('tickY') || markTypes.has('cell')))
|
|
216
|
+
return 'band';
|
|
217
|
+
}
|
|
218
|
+
if (!dataValues.length)
|
|
219
|
+
return 'linear';
|
|
220
|
+
if (dataValues.length === 1)
|
|
221
|
+
return 'point';
|
|
222
|
+
if (dataValues.every(isNumberOrNull))
|
|
223
|
+
return name === 'r' ? 'sqrt' : 'linear';
|
|
224
|
+
if (dataValues.every(isDateOrNull))
|
|
225
|
+
return 'time';
|
|
226
|
+
if (dataValues.every(isStringOrNull))
|
|
227
|
+
return markTypes.has('arrow') ? 'point' : 'band';
|
|
228
|
+
return 'linear';
|
|
229
|
+
}
|
|
230
|
+
const scaledChannelNames = [
|
|
231
|
+
'x',
|
|
232
|
+
'x1',
|
|
233
|
+
'x2',
|
|
234
|
+
'y',
|
|
235
|
+
'y1',
|
|
236
|
+
'y2',
|
|
237
|
+
'r',
|
|
238
|
+
'opacity',
|
|
239
|
+
'fill',
|
|
240
|
+
'fillOpacity',
|
|
241
|
+
'stroke',
|
|
242
|
+
'strokeOpacity',
|
|
243
|
+
'symbol',
|
|
244
|
+
'length'
|
|
245
|
+
];
|
|
246
|
+
/**
|
|
247
|
+
* Mark channels can explicitely or implicitely be exempt from being
|
|
248
|
+
* mapped to a scale, so everywhere where values are being mapped to
|
|
249
|
+
* scales, we need to check if the the scale is supposed to be used
|
|
250
|
+
* not. That's what this function is used for.
|
|
251
|
+
*/
|
|
252
|
+
export function getUsedScales(plot, options, mark) {
|
|
253
|
+
return Object.fromEntries(scaledChannelNames.map((channel) => {
|
|
254
|
+
const scale = CHANNEL_SCALE[channel];
|
|
255
|
+
const skipMarks = plot.scales[scale].skip.get(channel) || new Set();
|
|
256
|
+
return [
|
|
257
|
+
channel,
|
|
258
|
+
!skipMarks.has(mark.id) &&
|
|
259
|
+
toChannelOption(channel, options[channel]).scale !== null &&
|
|
260
|
+
!plot.scales[scale].isDummy
|
|
261
|
+
];
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
export function looksLikeANumber(input) {
|
|
265
|
+
return (Number.isFinite(input) ||
|
|
266
|
+
(typeof input === 'string' && input.trim().length > 0 && Number.isFinite(+input)));
|
|
267
|
+
}
|
|
268
|
+
function isWithin(number, min, max) {
|
|
269
|
+
return Number.isFinite(number) && number >= min && number <= max;
|
|
270
|
+
}
|
|
271
|
+
function looksLikeOpacity(input) {
|
|
272
|
+
return looksLikeANumber(input) && isWithin(+input, 0, 1);
|
|
273
|
+
}
|
|
274
|
+
export function projectXY(scales, x, y, useXScale = true, useYScale = true) {
|
|
275
|
+
if (scales.projection) {
|
|
276
|
+
// TODO: pretty sure this is not how projection streams are supposed to be used
|
|
277
|
+
// efficiantly, in observable plot, all data points of a mark are projected using
|
|
278
|
+
// the same stream
|
|
279
|
+
let x_, y_;
|
|
280
|
+
const stream = scales.projection.stream({
|
|
281
|
+
point(px, py) {
|
|
282
|
+
x_ = px;
|
|
283
|
+
y_ = py;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
stream.point(x, y);
|
|
287
|
+
return [x_, y_];
|
|
288
|
+
}
|
|
289
|
+
return [
|
|
290
|
+
useXScale ? projectX('x', scales, x) : x,
|
|
291
|
+
useYScale ? projectY('y', scales, y) : y
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
export function projectX(channel, scales, value) {
|
|
295
|
+
return (scales.x.fn(value) +
|
|
296
|
+
(channel === 'x' && scales.x.type === 'band'
|
|
297
|
+
? scales.x.fn.bandwidth() * 0.5
|
|
298
|
+
: channel === 'x2' && scales.x.type === 'band'
|
|
299
|
+
? scales.x.fn.bandwidth()
|
|
300
|
+
: 0));
|
|
301
|
+
}
|
|
302
|
+
export function projectY(channel, scales, value) {
|
|
303
|
+
return (scales.y.fn(value) +
|
|
304
|
+
(channel === 'y' && scales.y.type === 'band'
|
|
305
|
+
? scales.y.fn.bandwidth() * 0.5
|
|
306
|
+
: channel === 'y2' && scales.y.type === 'band'
|
|
307
|
+
? scales.y.fn.bandwidth()
|
|
308
|
+
: 0));
|
|
309
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const intervalDuration: unique symbol;
|
|
2
|
+
export declare const intervalType: unique symbol;
|
|
3
|
+
export declare function parseTimeInterval(input: any): (string | number)[];
|
|
4
|
+
export declare function maybeTimeInterval(input: any): any;
|
|
5
|
+
export declare function maybeUtcInterval(input: any): any;
|
|
6
|
+
export declare function generalizeTimeInterval(interval: any, n: any): any;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2020-2023 Observable, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
6
|
+
* with or without fee is hereby granted, provided that the above copyright notice
|
|
7
|
+
* and this permission notice appear in all copies.
|
|
8
|
+
*/
|
|
9
|
+
import { bisector } from 'd3-array';
|
|
10
|
+
import { utcSecond, utcMinute, utcHour, unixDay, utcWeek, utcMonth, utcYear, utcMonday, utcTuesday, utcWednesday, utcThursday, utcFriday, utcSaturday, utcSunday, timeSecond, timeMinute, timeHour, timeDay, timeWeek, timeMonth, timeYear, timeMonday, timeTuesday, timeWednesday, timeThursday, timeFriday, timeSaturday, timeSunday } from 'd3-time';
|
|
11
|
+
// import {orderof} from "./options.js";
|
|
12
|
+
const durationSecond = 1000;
|
|
13
|
+
const durationMinute = durationSecond * 60;
|
|
14
|
+
const durationHour = durationMinute * 60;
|
|
15
|
+
const durationDay = durationHour * 24;
|
|
16
|
+
const durationWeek = durationDay * 7;
|
|
17
|
+
const durationMonth = durationDay * 30;
|
|
18
|
+
const durationYear = durationDay * 365;
|
|
19
|
+
// See https://github.com/d3/d3-time/blob/9e8dc940f38f78d7588aad68a54a25b1f0c2d97b/src/ticks.js#L14-L33
|
|
20
|
+
const tickIntervals = [
|
|
21
|
+
['millisecond', 1],
|
|
22
|
+
['2 milliseconds', 2],
|
|
23
|
+
['5 milliseconds', 5],
|
|
24
|
+
['10 milliseconds', 10],
|
|
25
|
+
['20 milliseconds', 20],
|
|
26
|
+
['50 milliseconds', 50],
|
|
27
|
+
['100 milliseconds', 100],
|
|
28
|
+
['200 milliseconds', 200],
|
|
29
|
+
['500 milliseconds', 500],
|
|
30
|
+
['second', durationSecond],
|
|
31
|
+
['5 seconds', 5 * durationSecond],
|
|
32
|
+
['15 seconds', 15 * durationSecond],
|
|
33
|
+
['30 seconds', 30 * durationSecond],
|
|
34
|
+
['minute', durationMinute],
|
|
35
|
+
['5 minutes', 5 * durationMinute],
|
|
36
|
+
['15 minutes', 15 * durationMinute],
|
|
37
|
+
['30 minutes', 30 * durationMinute],
|
|
38
|
+
['hour', durationHour],
|
|
39
|
+
['3 hours', 3 * durationHour],
|
|
40
|
+
['6 hours', 6 * durationHour],
|
|
41
|
+
['12 hours', 12 * durationHour],
|
|
42
|
+
['day', durationDay],
|
|
43
|
+
['2 days', 2 * durationDay],
|
|
44
|
+
['week', durationWeek],
|
|
45
|
+
['2 weeks', 2 * durationWeek], // https://github.com/d3/d3-time/issues/46
|
|
46
|
+
['month', durationMonth],
|
|
47
|
+
['3 months', 3 * durationMonth],
|
|
48
|
+
['6 months', 6 * durationMonth], // https://github.com/d3/d3-time/issues/46
|
|
49
|
+
['year', durationYear],
|
|
50
|
+
['2 years', 2 * durationYear],
|
|
51
|
+
['5 years', 5 * durationYear],
|
|
52
|
+
['10 years', 10 * durationYear],
|
|
53
|
+
['20 years', 20 * durationYear],
|
|
54
|
+
['50 years', 50 * durationYear],
|
|
55
|
+
['100 years', 100 * durationYear] // TODO generalize to longer time scales
|
|
56
|
+
];
|
|
57
|
+
const durations = new Map([
|
|
58
|
+
['second', durationSecond],
|
|
59
|
+
['minute', durationMinute],
|
|
60
|
+
['hour', durationHour],
|
|
61
|
+
['day', durationDay],
|
|
62
|
+
['monday', durationWeek],
|
|
63
|
+
['tuesday', durationWeek],
|
|
64
|
+
['wednesday', durationWeek],
|
|
65
|
+
['thursday', durationWeek],
|
|
66
|
+
['friday', durationWeek],
|
|
67
|
+
['saturday', durationWeek],
|
|
68
|
+
['sunday', durationWeek],
|
|
69
|
+
['week', durationWeek],
|
|
70
|
+
['month', durationMonth],
|
|
71
|
+
['year', durationYear]
|
|
72
|
+
]);
|
|
73
|
+
const timeIntervals = new Map([
|
|
74
|
+
['second', timeSecond],
|
|
75
|
+
['minute', timeMinute],
|
|
76
|
+
['hour', timeHour],
|
|
77
|
+
['day', timeDay], // https://github.com/d3/d3-time/issues/62
|
|
78
|
+
['monday', timeMonday],
|
|
79
|
+
['tuesday', timeTuesday],
|
|
80
|
+
['wednesday', timeWednesday],
|
|
81
|
+
['thursday', timeThursday],
|
|
82
|
+
['friday', timeFriday],
|
|
83
|
+
['saturday', timeSaturday],
|
|
84
|
+
['sunday', timeSunday],
|
|
85
|
+
['week', timeWeek],
|
|
86
|
+
['month', timeMonth],
|
|
87
|
+
['year', timeYear]
|
|
88
|
+
]);
|
|
89
|
+
const utcIntervals = new Map([
|
|
90
|
+
['second', utcSecond],
|
|
91
|
+
['minute', utcMinute],
|
|
92
|
+
['hour', utcHour],
|
|
93
|
+
['day', unixDay],
|
|
94
|
+
['monday', utcMonday],
|
|
95
|
+
['tuesday', utcTuesday],
|
|
96
|
+
['wednesday', utcWednesday],
|
|
97
|
+
['thursday', utcThursday],
|
|
98
|
+
['friday', utcFriday],
|
|
99
|
+
['saturday', utcSaturday],
|
|
100
|
+
['sunday', utcSunday],
|
|
101
|
+
['week', utcWeek],
|
|
102
|
+
['month', utcMonth],
|
|
103
|
+
['year', utcYear]
|
|
104
|
+
]);
|
|
105
|
+
// These hidden fields describe standard intervals so that we can, for example,
|
|
106
|
+
// generalize a scale’s time interval to a larger ticks time interval to reduce
|
|
107
|
+
// the number of displayed ticks. TODO We could instead allow the interval
|
|
108
|
+
// implementation to expose a “generalize” method that returns a larger, aligned
|
|
109
|
+
// interval; that would allow us to move this logic to D3, and allow
|
|
110
|
+
// generalization even when a custom interval is provided.
|
|
111
|
+
export const intervalDuration = Symbol('intervalDuration');
|
|
112
|
+
export const intervalType = Symbol('intervalType');
|
|
113
|
+
// We greedily mutate D3’s standard intervals on load so that the hidden fields
|
|
114
|
+
// are available even if specified as e.g. d3.utcMonth instead of "month".
|
|
115
|
+
for (const [name, interval] of timeIntervals) {
|
|
116
|
+
interval[intervalDuration] = durations.get(name);
|
|
117
|
+
interval[intervalType] = 'time';
|
|
118
|
+
}
|
|
119
|
+
for (const [name, interval] of utcIntervals) {
|
|
120
|
+
interval[intervalDuration] = durations.get(name);
|
|
121
|
+
interval[intervalType] = 'utc';
|
|
122
|
+
}
|
|
123
|
+
const utcFormatIntervals = [
|
|
124
|
+
['year', utcYear, 'utc'],
|
|
125
|
+
['month', utcMonth, 'utc'],
|
|
126
|
+
['day', unixDay, 'utc', 6 * durationMonth],
|
|
127
|
+
['hour', utcHour, 'utc', 3 * durationDay],
|
|
128
|
+
['minute', utcMinute, 'utc', 6 * durationHour],
|
|
129
|
+
['second', utcSecond, 'utc', 30 * durationMinute]
|
|
130
|
+
];
|
|
131
|
+
const timeFormatIntervals = [
|
|
132
|
+
['year', timeYear, 'time'],
|
|
133
|
+
['month', timeMonth, 'time'],
|
|
134
|
+
['day', timeDay, 'time', 6 * durationMonth],
|
|
135
|
+
['hour', timeHour, 'time', 3 * durationDay],
|
|
136
|
+
['minute', timeMinute, 'time', 6 * durationHour],
|
|
137
|
+
['second', timeSecond, 'time', 30 * durationMinute]
|
|
138
|
+
];
|
|
139
|
+
// An interleaved array of UTC and local time intervals, in descending order
|
|
140
|
+
// from largest to smallest, used to determine the most specific standard time
|
|
141
|
+
// format for a given array of dates. This is a subset of the tick intervals
|
|
142
|
+
// listed above; we only need the breakpoints where the format changes.
|
|
143
|
+
const formatIntervals = [
|
|
144
|
+
utcFormatIntervals[0],
|
|
145
|
+
timeFormatIntervals[0],
|
|
146
|
+
utcFormatIntervals[1],
|
|
147
|
+
timeFormatIntervals[1],
|
|
148
|
+
utcFormatIntervals[2],
|
|
149
|
+
timeFormatIntervals[2],
|
|
150
|
+
// Below day, local time typically has an hourly offset from UTC and hence the
|
|
151
|
+
// two are aligned and indistinguishable; therefore, we only consider UTC, and
|
|
152
|
+
// we don’t consider these if the domain only has a single value.
|
|
153
|
+
...utcFormatIntervals.slice(3)
|
|
154
|
+
];
|
|
155
|
+
export function parseTimeInterval(input) {
|
|
156
|
+
let name = `${input}`.toLowerCase();
|
|
157
|
+
if (name.endsWith('s'))
|
|
158
|
+
name = name.slice(0, -1); // drop plural
|
|
159
|
+
let period = 1;
|
|
160
|
+
const match = /^(?:(\d+)\s+)/.exec(name);
|
|
161
|
+
if (match) {
|
|
162
|
+
name = name.slice(match[0].length);
|
|
163
|
+
period = +match[1];
|
|
164
|
+
}
|
|
165
|
+
switch (name) {
|
|
166
|
+
case 'quarter':
|
|
167
|
+
name = 'month';
|
|
168
|
+
period *= 3;
|
|
169
|
+
break;
|
|
170
|
+
case 'half':
|
|
171
|
+
name = 'month';
|
|
172
|
+
period *= 6;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
let interval = utcIntervals.get(name);
|
|
176
|
+
if (!interval)
|
|
177
|
+
throw new Error(`unknown interval: ${input}`);
|
|
178
|
+
if (period > 1 && !interval.every)
|
|
179
|
+
throw new Error(`non-periodic interval: ${name}`);
|
|
180
|
+
return [name, period];
|
|
181
|
+
}
|
|
182
|
+
export function maybeTimeInterval(input) {
|
|
183
|
+
return asInterval(parseTimeInterval(input), 'time');
|
|
184
|
+
}
|
|
185
|
+
export function maybeUtcInterval(input) {
|
|
186
|
+
return asInterval(parseTimeInterval(input), 'utc');
|
|
187
|
+
}
|
|
188
|
+
function asInterval([name, period], type) {
|
|
189
|
+
let interval = (type === 'time' ? timeIntervals : utcIntervals).get(name);
|
|
190
|
+
if (period > 1) {
|
|
191
|
+
interval = interval.every(period);
|
|
192
|
+
interval[intervalDuration] = durations.get(name) * period;
|
|
193
|
+
interval[intervalType] = type;
|
|
194
|
+
}
|
|
195
|
+
return interval;
|
|
196
|
+
}
|
|
197
|
+
// If the given interval is a standard time interval, we may be able to promote
|
|
198
|
+
// it a larger aligned time interval, rather than showing every nth tick.
|
|
199
|
+
export function generalizeTimeInterval(interval, n) {
|
|
200
|
+
if (!(n > 1))
|
|
201
|
+
return; // no need to generalize
|
|
202
|
+
const duration = interval[intervalDuration];
|
|
203
|
+
if (!tickIntervals.some(([, d]) => d === duration))
|
|
204
|
+
return; // nonstandard or unknown interval
|
|
205
|
+
if (duration % durationDay === 0 && durationDay < duration && duration < durationMonth)
|
|
206
|
+
return; // not generalizable
|
|
207
|
+
const [i] = tickIntervals[bisector(([, step]) => Math.log(step)).center(tickIntervals, Math.log(duration * n))];
|
|
208
|
+
return (interval[intervalType] === 'time' ? maybeTimeInterval : maybeUtcInterval)(i);
|
|
209
|
+
}
|
|
210
|
+
// function formatTimeInterval(name, type, anchor) {
|
|
211
|
+
// const format = type === 'time' ? timeFormat : utcFormat;
|
|
212
|
+
// // For tips and legends, use a format that doesn’t require context.
|
|
213
|
+
// if (anchor == null) {
|
|
214
|
+
// return format(
|
|
215
|
+
// name === 'year'
|
|
216
|
+
// ? '%Y'
|
|
217
|
+
// : name === 'month'
|
|
218
|
+
// ? '%Y-%m'
|
|
219
|
+
// : name === 'day'
|
|
220
|
+
// ? '%Y-%m-%d'
|
|
221
|
+
// : name === 'hour' || name === 'minute'
|
|
222
|
+
// ? '%Y-%m-%dT%H:%M'
|
|
223
|
+
// : name === 'second'
|
|
224
|
+
// ? '%Y-%m-%dT%H:%M:%S'
|
|
225
|
+
// : '%Y-%m-%dT%H:%M:%S.%L'
|
|
226
|
+
// );
|
|
227
|
+
// }
|
|
228
|
+
// // Otherwise, assume that this is for axis ticks.
|
|
229
|
+
// const template = getTimeTemplate(anchor);
|
|
230
|
+
// switch (name) {
|
|
231
|
+
// case 'millisecond':
|
|
232
|
+
// return formatConditional(format('.%L'), format(':%M:%S'), template);
|
|
233
|
+
// case 'second':
|
|
234
|
+
// return formatConditional(format(':%S'), format('%-I:%M'), template);
|
|
235
|
+
// case 'minute':
|
|
236
|
+
// return formatConditional(format('%-I:%M'), format('%p'), template);
|
|
237
|
+
// case 'hour':
|
|
238
|
+
// return formatConditional(format('%-I %p'), format('%b %-d'), template);
|
|
239
|
+
// case 'day':
|
|
240
|
+
// return formatConditional(format('%-d'), format('%b'), template);
|
|
241
|
+
// case 'month':
|
|
242
|
+
// return formatConditional(format('%b'), format('%Y'), template);
|
|
243
|
+
// case 'year':
|
|
244
|
+
// return format('%Y');
|
|
245
|
+
// }
|
|
246
|
+
// throw new Error('unable to format time ticks');
|
|
247
|
+
// }
|
|
248
|
+
// function getTimeTemplate(anchor) {
|
|
249
|
+
// return anchor === 'left' || anchor === 'right'
|
|
250
|
+
// ? (f1, f2) => `\n${f1}\n${f2}` // extra newline to keep f1 centered
|
|
251
|
+
// : anchor === 'top'
|
|
252
|
+
// ? (f1, f2) => `${f2}\n${f1}`
|
|
253
|
+
// : (f1, f2) => `${f1}\n${f2}`;
|
|
254
|
+
// }
|
|
255
|
+
// function getFormatIntervals(type) {
|
|
256
|
+
// return type === 'time'
|
|
257
|
+
// ? timeFormatIntervals
|
|
258
|
+
// : type === 'utc'
|
|
259
|
+
// ? utcFormatIntervals
|
|
260
|
+
// : formatIntervals;
|
|
261
|
+
// }
|
|
262
|
+
// Given an array of dates, returns the largest compatible standard time
|
|
263
|
+
// interval. If no standard interval is compatible (other than milliseconds,
|
|
264
|
+
// which is universally compatible), returns undefined.
|
|
265
|
+
// export function inferTimeFormat(type, dates, anchor) {
|
|
266
|
+
// const step = max(pairs(dates, (a, b) => Math.abs(b - a))); // maybe undefined!
|
|
267
|
+
// if (step < 1000) return formatTimeInterval('millisecond', 'utc', anchor);
|
|
268
|
+
// for (const [name, interval, intervalType, maxStep] of getFormatIntervals(type)) {
|
|
269
|
+
// if (step > maxStep) break; // e.g., 52 weeks
|
|
270
|
+
// if (name === 'hour' && !step) break; // e.g., domain with a single date
|
|
271
|
+
// if (dates.every((d) => interval.floor(d) >= d))
|
|
272
|
+
// return formatTimeInterval(name, intervalType, anchor);
|
|
273
|
+
// }
|
|
274
|
+
// }
|
|
275
|
+
// function formatConditional(format1, format2, template) {
|
|
276
|
+
// return (x, i, X) => {
|
|
277
|
+
// const f1 = format1(x, i); // always shown
|
|
278
|
+
// const f2 = format2(x, i); // only shown if different
|
|
279
|
+
// const j = i - orderof(X); // detect reversed domains
|
|
280
|
+
// return i !== j && X[j] !== undefined && f2 === format2(X[j], j) ? f1 : template(f1, f2);
|
|
281
|
+
// };
|
|
282
|
+
// }
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { RawValue } from '../types.js';
|
|
2
|
-
export declare function isBooleanOrNull(v: RawValue): boolean;
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
2
|
+
export declare function isBooleanOrNull(v: RawValue): v is boolean;
|
|
3
|
+
export declare function isDate(v: RawValue): boolean;
|
|
4
|
+
export declare function isDateOrNull(v: RawValue | null | undefined): boolean;
|
|
5
|
+
export declare function isNumberOrNull(v: RawValue | null | undefined): boolean;
|
|
6
|
+
export declare function isNumberOrNullOrNaN(v: RawValue | null | undefined): boolean;
|
|
7
|
+
export declare function isStringOrNull(v: RawValue | null | undefined): v is string | null | undefined;
|
|
8
|
+
export declare function isSymbolOrNull(v: RawValue | null | undefined): boolean;
|
|
9
|
+
export declare function isColorOrNull(v: RawValue | null | undefined): boolean;
|
|
7
10
|
export declare function isOpacityOrNull(v: RawValue): boolean;
|
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { isSymbol } from './symbols.js';
|
|
2
|
+
import { color } from 'd3-color';
|
|
3
|
+
import { CSS_COLOR, CSS_COLOR_MIX, CSS_COLOR_CONTRAST, CSS_VAR, CSS_URL, CSS_RGBA } from '../constants.js';
|
|
3
4
|
export function isBooleanOrNull(v) {
|
|
4
5
|
return v == null || typeof v === 'boolean';
|
|
5
6
|
}
|
|
7
|
+
export function isDate(v) {
|
|
8
|
+
return v instanceof Date && !isNaN(v.getTime());
|
|
9
|
+
}
|
|
6
10
|
export function isDateOrNull(v) {
|
|
7
11
|
return v == null || isDate(v);
|
|
8
12
|
}
|
|
9
13
|
export function isNumberOrNull(v) {
|
|
10
|
-
return v == null || isFinite(v);
|
|
14
|
+
return v == null || Number.isFinite(v) || (typeof v === 'string' && !Number.isNaN(+v));
|
|
15
|
+
}
|
|
16
|
+
export function isNumberOrNullOrNaN(v) {
|
|
17
|
+
return (v == null ||
|
|
18
|
+
Number.isFinite(v) ||
|
|
19
|
+
Number.isNaN(v) ||
|
|
20
|
+
(typeof v === 'string' && !Number.isNaN(+v)));
|
|
11
21
|
}
|
|
12
22
|
export function isStringOrNull(v) {
|
|
13
23
|
return v == null || typeof v === 'string';
|
|
14
24
|
}
|
|
25
|
+
export function isSymbolOrNull(v) {
|
|
26
|
+
return v == null || ((typeof v === 'string' || typeof v === 'object') && isSymbol(v));
|
|
27
|
+
}
|
|
15
28
|
export function isColorOrNull(v) {
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
return (v == null ||
|
|
30
|
+
(typeof v === 'string' &&
|
|
31
|
+
(v === 'currentColor' ||
|
|
32
|
+
CSS_VAR.test(v) ||
|
|
33
|
+
CSS_COLOR.test(v) ||
|
|
34
|
+
CSS_COLOR_MIX.test(v) ||
|
|
35
|
+
CSS_COLOR_CONTRAST.test(v) ||
|
|
36
|
+
CSS_RGBA.test(v) ||
|
|
37
|
+
CSS_URL.test(v) ||
|
|
38
|
+
color(v) !== null)));
|
|
18
39
|
}
|
|
19
40
|
export function isOpacityOrNull(v) {
|
|
20
|
-
return v == null || (typeof v === 'number' && isFinite(v) && v >= 0 && v <= 1);
|
|
41
|
+
return v == null || (typeof v === 'number' && Number.isFinite(v) && v >= 0 && v <= 1);
|
|
21
42
|
}
|