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.
Files changed (251) hide show
  1. package/LICENSE.md +5 -0
  2. package/README.md +3 -36
  3. package/dist/Mark.svelte +290 -0
  4. package/dist/Mark.svelte.d.ts +22 -0
  5. package/dist/Plot.svelte +148 -156
  6. package/dist/Plot.svelte.d.ts +15 -15
  7. package/dist/constants.d.ts +14 -0
  8. package/dist/constants.js +109 -0
  9. package/dist/core/Facet.svelte +59 -0
  10. package/dist/core/Facet.svelte.d.ts +18 -0
  11. package/dist/core/FacetAxes.svelte +66 -0
  12. package/dist/core/FacetAxes.svelte.d.ts +4 -0
  13. package/dist/core/FacetGrid.svelte +86 -0
  14. package/dist/core/FacetGrid.svelte.d.ts +13 -0
  15. package/dist/core/Plot.svelte +567 -0
  16. package/dist/core/Plot.svelte.d.ts +14 -0
  17. package/dist/helpers/arrowPath.d.ts +14 -0
  18. package/dist/helpers/arrowPath.js +129 -0
  19. package/dist/helpers/autoProjection.d.ts +19 -0
  20. package/dist/helpers/autoProjection.js +87 -0
  21. package/dist/helpers/autoScales.d.ts +23 -0
  22. package/dist/helpers/autoScales.js +203 -0
  23. package/dist/helpers/autoTicks.d.ts +3 -0
  24. package/dist/helpers/autoTicks.js +40 -0
  25. package/dist/helpers/autoTimeFormat.d.ts +2 -2
  26. package/dist/helpers/autoTimeFormat.js +34 -5
  27. package/dist/helpers/callWithProps.d.ts +8 -0
  28. package/dist/helpers/callWithProps.js +13 -0
  29. package/dist/helpers/colors.js +17 -2
  30. package/dist/helpers/curves.d.ts +3 -0
  31. package/dist/helpers/curves.js +42 -0
  32. package/dist/helpers/data.d.ts +9 -0
  33. package/dist/helpers/data.js +16 -0
  34. package/dist/helpers/facets.d.ts +12 -0
  35. package/dist/helpers/facets.js +49 -0
  36. package/dist/helpers/formats.d.ts +3 -0
  37. package/dist/helpers/formats.js +3 -0
  38. package/dist/helpers/getBaseStyles.d.ts +7 -2
  39. package/dist/helpers/getBaseStyles.js +34 -10
  40. package/dist/helpers/getLogTicks.js +5 -5
  41. package/dist/helpers/group.d.ts +6 -0
  42. package/dist/helpers/group.js +53 -0
  43. package/dist/helpers/index.d.ts +18 -0
  44. package/dist/helpers/index.js +53 -0
  45. package/dist/helpers/isRawValue.d.ts +2 -0
  46. package/dist/helpers/isRawValue.js +5 -0
  47. package/dist/helpers/isValid.d.ts +6 -0
  48. package/dist/helpers/isValid.js +6 -0
  49. package/dist/helpers/math.d.ts +19 -0
  50. package/dist/helpers/math.js +116 -0
  51. package/dist/helpers/mergeDeep.d.ts +1 -1
  52. package/dist/helpers/noise.d.ts +1 -0
  53. package/dist/helpers/noise.js +72 -0
  54. package/dist/helpers/projection.d.ts +33 -0
  55. package/dist/helpers/projection.js +100 -0
  56. package/dist/helpers/reduce.d.ts +10 -0
  57. package/dist/helpers/reduce.js +85 -0
  58. package/dist/helpers/regressionLoess.d.ts +12 -0
  59. package/dist/helpers/regressionLoess.js +47 -0
  60. package/dist/helpers/removeIdenticalLines.d.ts +8 -1
  61. package/dist/helpers/removeIdenticalLines.js +14 -7
  62. package/dist/helpers/resolve.d.ts +17 -0
  63. package/dist/helpers/resolve.js +152 -0
  64. package/dist/helpers/roundedRect.d.ts +9 -0
  65. package/dist/helpers/roundedRect.js +31 -0
  66. package/dist/helpers/scales.d.ts +42 -0
  67. package/dist/helpers/scales.js +309 -0
  68. package/dist/helpers/time.d.ts +6 -0
  69. package/dist/helpers/time.js +282 -0
  70. package/dist/helpers/typeChecks.d.ts +8 -5
  71. package/dist/helpers/typeChecks.js +27 -6
  72. package/dist/index.d.ts +49 -1
  73. package/dist/index.js +53 -2
  74. package/dist/marks/Area.svelte +146 -0
  75. package/dist/marks/Area.svelte.d.ts +30 -0
  76. package/dist/marks/AreaX.svelte +27 -0
  77. package/dist/marks/AreaX.svelte.d.ts +12 -0
  78. package/dist/marks/AreaY.svelte +38 -0
  79. package/dist/marks/AreaY.svelte.d.ts +19 -0
  80. package/dist/marks/Arrow.svelte +139 -0
  81. package/dist/marks/Arrow.svelte.d.ts +44 -0
  82. package/dist/marks/AxisX.svelte +198 -93
  83. package/dist/marks/AxisX.svelte.d.ts +17 -16
  84. package/dist/marks/AxisY.svelte +176 -62
  85. package/dist/marks/AxisY.svelte.d.ts +17 -14
  86. package/dist/marks/BarX.svelte +81 -0
  87. package/dist/marks/BarX.svelte.d.ts +4 -0
  88. package/dist/marks/BarY.svelte +95 -0
  89. package/dist/marks/BarY.svelte.d.ts +4 -0
  90. package/dist/marks/BollingerX.svelte +44 -0
  91. package/dist/marks/BollingerX.svelte.d.ts +18 -0
  92. package/dist/marks/BollingerY.svelte +39 -0
  93. package/dist/marks/BollingerY.svelte.d.ts +18 -0
  94. package/dist/marks/BoxX.svelte +89 -0
  95. package/dist/marks/BoxX.svelte.d.ts +4 -0
  96. package/dist/marks/BoxY.svelte +110 -0
  97. package/dist/marks/BoxY.svelte.d.ts +29 -0
  98. package/dist/marks/Cell.svelte +110 -0
  99. package/dist/marks/Cell.svelte.d.ts +16 -0
  100. package/dist/marks/CellX.svelte +24 -0
  101. package/dist/marks/CellX.svelte.d.ts +3 -0
  102. package/dist/marks/CellY.svelte +24 -0
  103. package/dist/marks/CellY.svelte.d.ts +3 -0
  104. package/dist/marks/ColorLegend.svelte +148 -27
  105. package/dist/marks/ColorLegend.svelte.d.ts +12 -13
  106. package/dist/marks/CustomMark.svelte +43 -0
  107. package/dist/marks/CustomMark.svelte.d.ts +16 -0
  108. package/dist/marks/CustomMarkHTML.svelte +103 -0
  109. package/dist/marks/CustomMarkHTML.svelte.d.ts +17 -0
  110. package/dist/marks/DifferenceY.svelte +144 -0
  111. package/dist/marks/DifferenceY.svelte.d.ts +30 -0
  112. package/dist/marks/Dot.svelte +128 -73
  113. package/dist/marks/Dot.svelte.d.ts +24 -14
  114. package/dist/marks/DotX.svelte +15 -3
  115. package/dist/marks/DotX.svelte.d.ts +8 -16
  116. package/dist/marks/DotY.svelte +8 -3
  117. package/dist/marks/DotY.svelte.d.ts +5 -17
  118. package/dist/marks/Frame.svelte +32 -31
  119. package/dist/marks/Frame.svelte.d.ts +7 -14
  120. package/dist/marks/Geo.svelte +102 -0
  121. package/dist/marks/Geo.svelte.d.ts +10 -0
  122. package/dist/marks/Graticule.svelte +28 -0
  123. package/dist/marks/Graticule.svelte.d.ts +9 -0
  124. package/dist/marks/GridX.svelte +67 -36
  125. package/dist/marks/GridX.svelte.d.ts +7 -18
  126. package/dist/marks/GridY.svelte +64 -25
  127. package/dist/marks/GridY.svelte.d.ts +7 -14
  128. package/dist/marks/HTMLTooltip.svelte +91 -0
  129. package/dist/marks/HTMLTooltip.svelte.d.ts +11 -0
  130. package/dist/marks/Line.svelte +219 -58
  131. package/dist/marks/Line.svelte.d.ts +30 -14
  132. package/dist/marks/LineX.svelte +8 -8
  133. package/dist/marks/LineX.svelte.d.ts +4 -17
  134. package/dist/marks/LineY.svelte +7 -8
  135. package/dist/marks/LineY.svelte.d.ts +4 -17
  136. package/dist/marks/Link.svelte +173 -0
  137. package/dist/marks/Link.svelte.d.ts +21 -0
  138. package/dist/marks/Pointer.svelte +126 -0
  139. package/dist/marks/Pointer.svelte.d.ts +23 -0
  140. package/dist/marks/Rect.svelte +103 -0
  141. package/dist/marks/Rect.svelte.d.ts +15 -0
  142. package/dist/marks/RectX.svelte +33 -0
  143. package/dist/marks/RectX.svelte.d.ts +15 -0
  144. package/dist/marks/RectY.svelte +33 -0
  145. package/dist/marks/RectY.svelte.d.ts +15 -0
  146. package/dist/marks/RegressionX.svelte +26 -0
  147. package/dist/marks/RegressionX.svelte.d.ts +4 -0
  148. package/dist/marks/RegressionY.svelte +26 -0
  149. package/dist/marks/RegressionY.svelte.d.ts +4 -0
  150. package/dist/marks/RuleX.svelte +52 -28
  151. package/dist/marks/RuleX.svelte.d.ts +14 -14
  152. package/dist/marks/RuleY.svelte +52 -28
  153. package/dist/marks/RuleY.svelte.d.ts +14 -14
  154. package/dist/marks/Sphere.svelte +8 -0
  155. package/dist/marks/Sphere.svelte.d.ts +51 -0
  156. package/dist/marks/Spike.svelte +15 -0
  157. package/dist/marks/Spike.svelte.d.ts +4 -0
  158. package/dist/marks/SymbolLegend.svelte +27 -12
  159. package/dist/marks/SymbolLegend.svelte.d.ts +8 -14
  160. package/dist/marks/Text.svelte +185 -0
  161. package/dist/marks/Text.svelte.d.ts +26 -0
  162. package/dist/marks/TickX.svelte +89 -0
  163. package/dist/marks/TickX.svelte.d.ts +22 -0
  164. package/dist/marks/TickY.svelte +90 -0
  165. package/dist/marks/TickY.svelte.d.ts +22 -0
  166. package/dist/marks/Vector.svelte +213 -0
  167. package/dist/marks/Vector.svelte.d.ts +31 -0
  168. package/dist/marks/helpers/BaseAxisX.svelte +210 -0
  169. package/dist/marks/helpers/BaseAxisX.svelte.d.ts +24 -0
  170. package/dist/marks/helpers/BaseAxisY.svelte +187 -0
  171. package/dist/marks/helpers/BaseAxisY.svelte.d.ts +23 -0
  172. package/dist/marks/helpers/CanvasLayer.svelte +38 -0
  173. package/dist/marks/helpers/CanvasLayer.svelte.d.ts +13 -0
  174. package/dist/marks/helpers/DotCanvas.svelte +184 -0
  175. package/dist/marks/helpers/DotCanvas.svelte.d.ts +11 -0
  176. package/dist/marks/helpers/GeoCanvas.svelte +165 -0
  177. package/dist/marks/helpers/GeoCanvas.svelte.d.ts +13 -0
  178. package/dist/marks/helpers/GroupMultiple.svelte +17 -0
  179. package/dist/marks/helpers/GroupMultiple.svelte.d.ts +9 -0
  180. package/dist/marks/helpers/Marker.svelte +93 -0
  181. package/dist/marks/helpers/Marker.svelte.d.ts +10 -0
  182. package/dist/marks/helpers/MarkerPath.svelte +164 -0
  183. package/dist/marks/helpers/MarkerPath.svelte.d.ts +44 -0
  184. package/dist/marks/helpers/Regression.svelte +174 -0
  185. package/dist/marks/helpers/Regression.svelte.d.ts +26 -0
  186. package/dist/marks/helpers/events.d.ts +8 -0
  187. package/dist/marks/helpers/events.js +64 -0
  188. package/dist/transforms/bin.d.ts +51 -0
  189. package/dist/transforms/bin.js +171 -0
  190. package/dist/transforms/bollinger.d.ts +21 -0
  191. package/dist/transforms/bollinger.js +53 -0
  192. package/dist/transforms/centroid.d.ts +9 -0
  193. package/dist/transforms/centroid.js +13 -0
  194. package/dist/transforms/facet.d.ts +1 -0
  195. package/dist/transforms/facet.js +1 -0
  196. package/dist/transforms/filter.d.ts +2 -0
  197. package/dist/transforms/filter.js +8 -0
  198. package/dist/transforms/group.d.ts +66 -0
  199. package/dist/transforms/group.js +109 -0
  200. package/dist/transforms/interval.d.ts +11 -0
  201. package/dist/transforms/interval.js +34 -0
  202. package/dist/transforms/jitter.d.ts +0 -0
  203. package/dist/transforms/jitter.js +1 -0
  204. package/dist/transforms/map.d.ts +10 -0
  205. package/dist/transforms/map.js +89 -0
  206. package/dist/transforms/normalize.d.ts +9 -0
  207. package/dist/transforms/normalize.js +86 -0
  208. package/dist/transforms/recordize.d.ts +13 -0
  209. package/dist/transforms/recordize.js +75 -0
  210. package/dist/transforms/rename.d.ts +14 -0
  211. package/dist/transforms/rename.js +42 -0
  212. package/dist/transforms/select.d.ts +35 -0
  213. package/dist/transforms/select.js +55 -0
  214. package/dist/transforms/shift.d.ts +13 -0
  215. package/dist/transforms/shift.js +45 -0
  216. package/dist/transforms/sort.d.ts +28 -0
  217. package/dist/transforms/sort.js +61 -0
  218. package/dist/transforms/stack.d.ts +10 -0
  219. package/dist/transforms/stack.js +110 -0
  220. package/dist/transforms/window.d.ts +22 -0
  221. package/dist/transforms/window.js +73 -0
  222. package/dist/types.d.ts +625 -188
  223. package/dist/ui/Checkbox.svelte +6 -0
  224. package/dist/ui/Checkbox.svelte.d.ts +13 -0
  225. package/dist/ui/RadioInput.svelte +27 -0
  226. package/dist/ui/RadioInput.svelte.d.ts +9 -0
  227. package/dist/ui/Select.svelte +27 -0
  228. package/dist/ui/Select.svelte.d.ts +9 -0
  229. package/dist/ui/Slider.svelte +47 -0
  230. package/dist/ui/Slider.svelte.d.ts +11 -0
  231. package/dist/ui/Spiral.svelte +46 -0
  232. package/dist/ui/Spiral.svelte.d.ts +15 -0
  233. package/dist/ui/index.d.ts +4 -0
  234. package/dist/ui/index.js +4 -0
  235. package/package.json +79 -40
  236. package/LICENSE +0 -11
  237. package/dist/classes/Channel.svelte.js +0 -74
  238. package/dist/classes/Mark.svelte.js +0 -17
  239. package/dist/classes/Plot.svelte.js +0 -98
  240. package/dist/contants.d.ts +0 -3
  241. package/dist/contants.js +0 -40
  242. package/dist/helpers/GroupMultiple.svelte +0 -8
  243. package/dist/helpers/GroupMultiple.svelte.d.ts +0 -19
  244. package/dist/helpers/createScale.d.ts +0 -5
  245. package/dist/helpers/createScale.js +0 -57
  246. package/dist/helpers/resolveChannel.d.ts +0 -2
  247. package/dist/helpers/resolveChannel.js +0 -28
  248. package/dist/helpers/wrapArray.d.ts +0 -2
  249. package/dist/helpers/wrapArray.js +0 -4
  250. package/dist/marks/BaseMark.svelte +0 -22
  251. 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 isDateOrNull(v: RawValue): boolean;
4
- export declare function isNumberOrNull(v: RawValue): boolean;
5
- export declare function isStringOrNull(v: RawValue): boolean;
6
- export declare function isColorOrNull(v: RawValue): boolean;
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 chroma from 'chroma-js';
2
- import { isDate, isFinite } from 'underscore';
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
- // todo: maybe not use chroma.js here to save kb
17
- return v == null || (typeof v === 'string' && chroma.valid(v));
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
  }