svelteplot 0.0.1-alpha.2 → 0.0.1-alpha.21

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 (87) hide show
  1. package/dist/Plot.svelte +56 -42
  2. package/dist/Plot.svelte.d.ts +3 -3
  3. package/dist/classes/Mark.svelte.js +4 -0
  4. package/dist/classes/Plot.svelte.js +50 -24
  5. package/dist/classes/Scale.svelte.js +94 -0
  6. package/dist/contants.d.ts +3 -3
  7. package/dist/contants.js +18 -16
  8. package/dist/helpers/GroupMultiple.svelte.d.ts +3 -3
  9. package/dist/helpers/autoTimeFormat.d.ts +2 -2
  10. package/dist/helpers/autoTimeFormat.js +1 -1
  11. package/dist/helpers/callWithProps.d.ts +4 -0
  12. package/dist/helpers/callWithProps.js +9 -0
  13. package/dist/helpers/colors.d.ts +1 -1
  14. package/dist/helpers/createScale.d.ts +2 -1
  15. package/dist/helpers/createScale.js +37 -16
  16. package/dist/helpers/curves.d.ts +3 -0
  17. package/dist/helpers/curves.js +42 -0
  18. package/dist/helpers/getBaseStyles.d.ts +1 -1
  19. package/dist/helpers/getBaseStyles.js +2 -3
  20. package/dist/helpers/getLogTicks.js +5 -5
  21. package/dist/helpers/isDataRecord.d.ts +1 -1
  22. package/dist/helpers/isRawValue.d.ts +2 -0
  23. package/dist/helpers/isRawValue.js +5 -0
  24. package/dist/helpers/resolveChannel.d.ts +6 -2
  25. package/dist/helpers/resolveChannel.js +16 -6
  26. package/dist/helpers/typeChecks.d.ts +1 -1
  27. package/dist/helpers/typeChecks.js +1 -1
  28. package/dist/helpers/wrapArray.d.ts +2 -2
  29. package/dist/helpers/wrapValueArray.d.ts +6 -0
  30. package/dist/helpers/wrapValueArray.js +5 -0
  31. package/dist/index.d.ts +15 -1
  32. package/dist/index.js +17 -1
  33. package/dist/marks/Area.svelte +95 -0
  34. package/dist/marks/Area.svelte.d.ts +28 -0
  35. package/dist/marks/AreaX.svelte +11 -0
  36. package/dist/marks/AreaX.svelte.d.ts +30 -0
  37. package/dist/marks/AreaY.svelte +12 -0
  38. package/dist/marks/AreaY.svelte.d.ts +30 -0
  39. package/dist/marks/AxisX.svelte +17 -17
  40. package/dist/marks/AxisX.svelte.d.ts +3 -3
  41. package/dist/marks/AxisY.svelte +25 -11
  42. package/dist/marks/AxisY.svelte.d.ts +3 -3
  43. package/dist/marks/BarX.svelte +52 -0
  44. package/dist/marks/BarX.svelte.d.ts +25 -0
  45. package/dist/marks/BarY.svelte +52 -0
  46. package/dist/marks/BarY.svelte.d.ts +25 -0
  47. package/dist/marks/BaseMark.svelte +10 -3
  48. package/dist/marks/BaseMark.svelte.d.ts +5 -4
  49. package/dist/marks/ColorLegend.svelte +26 -24
  50. package/dist/marks/ColorLegend.svelte.d.ts +2 -2
  51. package/dist/marks/Dot.svelte +28 -43
  52. package/dist/marks/Dot.svelte.d.ts +11 -3
  53. package/dist/marks/DotX.svelte.d.ts +3 -3
  54. package/dist/marks/DotY.svelte.d.ts +3 -3
  55. package/dist/marks/Frame.svelte +1 -1
  56. package/dist/marks/Frame.svelte.d.ts +4 -4
  57. package/dist/marks/GridX.svelte +29 -18
  58. package/dist/marks/GridX.svelte.d.ts +6 -6
  59. package/dist/marks/GridY.svelte +17 -7
  60. package/dist/marks/GridY.svelte.d.ts +3 -3
  61. package/dist/marks/Line.svelte +28 -29
  62. package/dist/marks/Line.svelte.d.ts +15 -3
  63. package/dist/marks/LineX.svelte +5 -7
  64. package/dist/marks/LineX.svelte.d.ts +13 -6
  65. package/dist/marks/LineY.svelte +4 -6
  66. package/dist/marks/LineY.svelte.d.ts +14 -6
  67. package/dist/marks/RuleX.svelte +17 -9
  68. package/dist/marks/RuleX.svelte.d.ts +3 -3
  69. package/dist/marks/RuleY.svelte +17 -10
  70. package/dist/marks/RuleY.svelte.d.ts +3 -3
  71. package/dist/marks/SymbolLegend.svelte +17 -17
  72. package/dist/marks/SymbolLegend.svelte.d.ts +2 -2
  73. package/dist/marks/TickX.svelte +30 -0
  74. package/dist/marks/TickX.svelte.d.ts +15 -0
  75. package/dist/marks/TickY.svelte +30 -0
  76. package/dist/marks/TickY.svelte.d.ts +15 -0
  77. package/dist/transforms/normalize.d.ts +18 -0
  78. package/dist/transforms/normalize.js +25 -0
  79. package/dist/transforms/recordize.d.ts +3 -0
  80. package/dist/transforms/recordize.js +36 -0
  81. package/dist/transforms/rename.d.ts +4 -0
  82. package/dist/transforms/rename.js +10 -0
  83. package/dist/transforms/stack.d.ts +11 -0
  84. package/dist/transforms/stack.js +80 -0
  85. package/dist/types.d.ts +60 -23
  86. package/package.json +28 -23
  87. package/dist/classes/Channel.svelte.js +0 -72
package/dist/Plot.svelte CHANGED
@@ -1,11 +1,7 @@
1
1
  <script>import { setContext } from "svelte";
2
- import { Frame, GridX, GridY } from "./";
3
- import { DEFAULT_PLOT_OPTIONS, Plot } from "./classes/Plot.svelte";
4
- import mergeDeep from "./helpers/mergeDeep";
5
- import AxisX from "./marks/AxisX.svelte";
6
- import AxisY from "./marks/AxisY.svelte";
7
- import ColorLegend from "./marks/ColorLegend.svelte";
8
- import SymbolLegend from "./marks/SymbolLegend.svelte";
2
+ import { Frame, GridX, GridY, AxisX, AxisY, ColorLegend, SymbolLegend } from "./index.js";
3
+ import { DEFAULT_PLOT_OPTIONS, Plot } from "./classes/Plot.svelte.js";
4
+ import mergeDeep from "./helpers/mergeDeep.js";
9
5
  let {
10
6
  // snippets
11
7
  header,
@@ -25,6 +21,7 @@ let {
25
21
  title = "",
26
22
  subtitle = "",
27
23
  caption = "",
24
+ testid,
28
25
  // scales
29
26
  radius = null,
30
27
  color = null,
@@ -77,39 +74,45 @@ function onMouseMove(evt) {
77
74
  let hasLegend = $derived(color?.legend || symbol?.legend);
78
75
  </script>
79
76
 
80
- <figure class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
81
- <svg
82
- role="document"
83
- {width}
84
- height={plot.height}
85
- onmousemove={onmousemove ? onMouseMove : null}
86
- >
87
- <!-- automatic grids -->
88
- {#if grid || x?.grid}<GridX automatic />{/if}
89
- {#if grid || y?.grid}<GridY automatic />{/if}
77
+ <figure data-testid={testid} class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
78
+ <div class="plot-body">
79
+ <div class="overlay">
80
+ {#if overlay}{@render overlay(plot)}{/if}
81
+ </div>
82
+ <svg
83
+ role="document"
84
+ {width}
85
+ height={plot.height}
86
+ onmousemove={onmousemove ? onMouseMove : null}
87
+ >
88
+ <!-- automatic grids -->
89
+ {#if (grid && plot.hasScaleX) || x?.grid}<GridX automatic />{/if}
90
+ {#if (grid && plot.hasScaleY) || y?.grid}<GridY automatic />{/if}
90
91
 
91
- {#if !plot.hasAxisXMark && plot.hasChannelX}
92
- <!-- automatic x axis -->
93
- {#if plot.options.x.axis === 'bottom' || plot.options.x.axis === 'both'}
94
- <AxisX anchor="bottom" automatic />
95
- {/if}
96
- {#if plot.options.x.axis === 'top' || plot.options.x.axis === 'both'}
97
- <AxisX anchor="top" automatic />
92
+ {#if !plot.hasAxisXMark && (plot.hasScaleX || x?.grid)}
93
+ <!-- automatic x axis -->
94
+ {#if plot.options.x.axis === 'bottom' || plot.options.x.axis === 'both'}
95
+ <AxisX anchor="bottom" automatic />
96
+ {/if}
97
+ {#if plot.options.x.axis === 'top' || plot.options.x.axis === 'both'}
98
+ <AxisX anchor="top" automatic />
99
+ {/if}
98
100
  {/if}
99
- {/if}
100
- {#if !plot.hasAxisYMark && plot.hasChannelY}
101
- <!-- automatic y axis -->
102
- {#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
103
- <AxisY anchor="left" automatic />
101
+ {#if !plot.hasAxisYMark && (plot.hasScaleY || y?.grid)}
102
+ <!-- automatic y axis -->
103
+ {#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
104
+ <AxisY anchor="left" automatic />
105
+ {/if}
106
+ {#if plot.options.y.axis === 'right' || plot.options.y.axis === 'both'}
107
+ <AxisY anchor="right" automatic />
108
+ {/if}
104
109
  {/if}
105
- {#if plot.options.y.axis === 'right' || plot.options.y.axis === 'both'}
106
- <AxisY anchor="right" automatic />
107
- {/if}
108
- {/if}
109
- <!-- automatic frame -->
110
- {#if frame}<Frame />{/if}
111
- {#if children}{@render children(plot)}{/if}
112
- </svg>
110
+ <!-- automatic frame -->
111
+ {#if frame}<Frame />{/if}
112
+ {#if children}{@render children(plot)}{/if}
113
+ </svg>
114
+
115
+ </div>
113
116
 
114
117
  {#if plot.options.title || plot.options.subtitle || header || hasLegend}
115
118
  <div class="plot-header">
@@ -137,10 +140,6 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
137
140
  {#if footer}{@render footer(plot)}{/if}
138
141
  </div>
139
142
  {/if}
140
-
141
- <div class="overlay">
142
- {#if overlay}{@render overlay(plot)}{/if}
143
- </div>
144
143
  </figure>
145
144
 
146
145
  <style>
@@ -150,12 +149,27 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
150
149
  display: flex;
151
150
  flex-direction: column;
152
151
  }
152
+
153
+ .plot-body {
154
+ position: relative;
155
+ }
153
156
  figure .plot-header {
154
157
  order: 1;
155
158
  }
156
- svg {
159
+ .overlay {
160
+ z-index: 2;
161
+ }
162
+ h2,
163
+ h3 {
164
+ margin: 0;
165
+ }
166
+ .plot-body {
157
167
  order: 2;
168
+
169
+ }
170
+ svg {
158
171
  overflow: visible;
172
+ z-index: 1;
159
173
  }
160
174
  figure .plot-footer {
161
175
  order: 3;
@@ -1,5 +1,5 @@
1
- import { SvelteComponent } from "svelte";
2
- import type { PlotProps } from './types';
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { PlotProps } from './types.js';
3
3
  declare const __propDef: {
4
4
  props: PlotProps;
5
5
  events: {
@@ -11,5 +11,5 @@ type PlotProps_ = typeof __propDef.props;
11
11
  export { PlotProps_ as PlotProps };
12
12
  export type PlotEvents = typeof __propDef.events;
13
13
  export type PlotSlots = typeof __propDef.slots;
14
- export default class Plot extends SvelteComponent<PlotProps, PlotEvents, PlotSlots> {
14
+ export default class Plot extends SvelteComponentTyped<PlotProps, PlotEvents, PlotSlots> {
15
15
  }
@@ -1,9 +1,13 @@
1
+ import { CHANNEL_SCALE } from '../contants.js';
1
2
  export class Mark {
2
3
  id;
3
4
  type;
4
5
  automatic;
5
6
  channels = $state(new Set());
6
7
  props = $state();
8
+ scales = $derived(new Set(Array.from(this.channels.values())
9
+ .filter((channel) => this.props[channel] != null && !(typeof this.props[channel] === 'number'))
10
+ .map((channel) => CHANNEL_SCALE[channel])));
7
11
  constructor(type, channels, automatic, props) {
8
12
  this.id = Symbol();
9
13
  this.type = type;
@@ -1,7 +1,7 @@
1
- import { createScale, createColorScale } from '../helpers/createScale';
2
- import mergeDeep from '../helpers/mergeDeep';
3
- import { Channel } from './Channel.svelte';
4
- import { uniq } from 'underscore';
1
+ import { createScale, createColorScale } from '../helpers/createScale.js';
2
+ import mergeDeep from '../helpers/mergeDeep.js';
3
+ import { Scale } from './Scale.svelte.js';
4
+ import pick from 'underscore/modules/pick.js';
5
5
  export const DEFAULT_PLOT_OPTIONS = {
6
6
  title: '',
7
7
  subtitle: '',
@@ -37,16 +37,24 @@ export class Plot {
37
37
  _height = $state(400);
38
38
  options = $state(DEFAULT_PLOT_OPTIONS);
39
39
  marks = $state([]);
40
- hasChannelX = $derived(!!this.marks.find((mark) => mark.channels.has('x')));
41
- hasChannelY = $derived(!!this.marks.find((mark) => mark.channels.has('y')));
40
+ hasScaleX = $derived(!!this.marks.find((mark) => !mark.automatic && mark.scales.has('x')));
41
+ hasScaleY = $derived(!!this.marks.find((mark) => !mark.automatic && mark.scales.has('y')));
42
42
  hasFilledDotMarks = $derived(!!this.marks.find((d) => d.type === 'dot' && d.props?.fill));
43
43
  manualMarks = $derived(this.marks.filter((mark) => !mark.automatic));
44
- singlePosChannelMark = $derived(this.manualMarks.length === 1 &&
44
+ singlePosScaleMark = $derived(this.manualMarks.length === 1 &&
45
45
  (!this.manualMarks[0].channels.has('x') || !this.manualMarks[0].channels.has('y')));
46
- height = $derived(this._height === 'auto' ? (this.hasChannelY ? 400 : 90) : this._height);
46
+ height = $derived(this._height === 'auto'
47
+ ? this.hasScaleY
48
+ ? this.y.scaleType === 'band'
49
+ ? this.y.domain.length * 30
50
+ : this.y.scaleType === 'point'
51
+ ? this.y.domain.length * 18
52
+ : 400
53
+ : 90
54
+ : this._height);
47
55
  inset = $derived(typeof this.options.inset === 'number'
48
56
  ? this.options.inset
49
- : this.singlePosChannelMark
57
+ : this.singlePosScaleMark
50
58
  ? 10
51
59
  : 0);
52
60
  // derived props
@@ -59,25 +67,29 @@ export class Plot {
59
67
  // margins = $state<Margins>({ left: 0, right: 0, top: 0, bottom: 0 });
60
68
  plotWidth = $derived(this.width - this.margins.left - this.margins.right);
61
69
  plotHeight = $derived(this.height - this.margins.top - this.margins.bottom);
62
- x = new Channel('x', this);
63
- y = new Channel('y', this);
64
- radius = new Channel('radius', this);
65
- color = new Channel('color', this);
66
- symbol = new Channel('symbol', this);
70
+ x = new Scale('x', this);
71
+ y = new Scale('y', this);
72
+ radius = new Scale('radius', this);
73
+ color = new Scale('color', this);
74
+ symbol = new Scale('symbol', this);
67
75
  colorSymbolRedundant = $derived(this.color.uniqueMarkProps.length === 1 &&
68
76
  this.symbol.uniqueMarkProps.length === 1 &&
69
77
  this.color.uniqueMarkProps[0] === this.symbol.uniqueMarkProps[0]);
70
- xScale = $derived(createScale(this.x.scaleType === 'linear' && this.options.x.log ? 'log' : this.x.scaleType, this.options?.x?.domain || this.x.domain, this.options?.x?.reverse
78
+ xScale = $derived(createScale(this.x.scaleType, this.options?.x?.domain || this.x.domain, this.options?.x?.reverse
71
79
  ? [this.margins.left + this.plotWidth - this.inset, this.margins.left + this.inset]
72
- : [this.margins.left + this.inset, this.margins.left + this.plotWidth - this.inset], this.x.scaleType === 'linear' && this.options.x.log ? { base: 10 } : {}));
73
- yScale = $derived(createScale(this.y.scaleType === 'linear' && this.options.y.log ? 'log' : this.y.scaleType, this.options.y?.domain || this.y.domain, this.options.y?.reverse
80
+ : [this.margins.left + this.inset, this.margins.left + this.plotWidth - this.inset],
81
+ // options
82
+ getScaleOptions(this.x.scaleType, this.options.x)));
83
+ yScale = $derived(createScale(this.y.scaleType, this.options.y?.domain || this.y.domain, this.options.y?.reverse
74
84
  ? [this.margins.top + this.inset, this.height - this.margins.bottom - this.inset]
75
- : [this.height - this.margins.bottom - this.inset, this.margins.top + this.inset], this.y.scaleType === 'linear' && this.options.y.log ? { base: '10' } : {}));
85
+ : [this.height - this.margins.bottom - this.inset, this.margins.top + this.inset],
86
+ // options
87
+ getScaleOptions(this.y.scaleType, this.options.y)));
76
88
  radiusScale = $derived(createScale(this.radius.scaleType, [0, Math.max(this.radius.domain[0], this.radius.domain[1])], this.options.radius.range));
77
89
  symbolScale = $derived(createScale('ordinal', this.symbol.domain, this.options.symbol?.range || this.hasFilledDotMarks
78
90
  ? ['circle', 'cross', 'diamond', 'square', 'star', 'triangle', 'wye']
79
- : ['circle', 'plus', 'times', 'triangle2', 'asterisk', 'square2', 'diamond2']));
80
- colorScale = $derived(createColorScale(this.color.scaleType, this.color.domain, this.options.color.scheme));
91
+ : ['circle', 'plus', 'times', 'triangle2', 'asterisk', 'square2', 'diamond2'], getScaleOptions(this.symbol.scaleType, this.options.symbol || {})));
92
+ colorScale = $derived(createColorScale(this.color.scaleType, this.color.domain, this.options.color?.range || null, this.options.color?.scheme));
81
93
  hasAxisXMark = $derived(!!this.marks.find((mark) => mark.type === 'axis-x' && !mark.automatic));
82
94
  hasAxisYMark = $derived(!!this.marks.find((mark) => mark.type === 'axis-y' && !mark.automatic));
83
95
  constructor(width, height, options) {
@@ -87,13 +99,27 @@ export class Plot {
87
99
  this.options = opts;
88
100
  }
89
101
  addMark(mark) {
90
- // console.log('addMark: ' + mark);
91
102
  this.marks = [...this.marks, mark];
92
- // add mark to respective channels
93
- if (mark.channels.has('color'))
94
- console.log(this.color.uniqueMarkProps);
95
103
  }
96
104
  removeMark(removeMark) {
97
105
  this.marks = this.marks.filter((mark) => mark.id !== removeMark.id);
98
106
  }
99
107
  }
108
+ /**
109
+ * users can pass options to the D3 scale function
110
+ */
111
+ function getScaleOptions(scaleType, options = {}) {
112
+ return scaleType === 'linear'
113
+ ? pick(options, ['clamp', 'unknown'])
114
+ : scaleType === 'pow'
115
+ ? pick(options, ['exponent'])
116
+ : scaleType === 'log'
117
+ ? { base: 10, ...pick(options, ['base']) }
118
+ : scaleType === 'symlog'
119
+ ? pick(options, ['constant'])
120
+ : scaleType === 'point'
121
+ ? pick(options, ['padding', 'align', 'round'])
122
+ : scaleType === 'band'
123
+ ? pick(options, ['padding', 'paddingInner', 'paddingOuter', 'align', 'round'])
124
+ : {};
125
+ }
@@ -0,0 +1,94 @@
1
+ import resolveChannel from '../helpers/resolveChannel.js';
2
+ import { extent } from 'd3-array';
3
+ import { CHANNEL_SCALE } from '../contants.js';
4
+ import { isDateOrNull, isNumberOrNull, isStringOrNull } from '../helpers/typeChecks.js';
5
+ import uniq from 'underscore/modules/uniq.js';
6
+ const FUNCTION = '(function)';
7
+ export class Scale {
8
+ name = undefined;
9
+ plot = $state(undefined);
10
+ constructor(name, plot) {
11
+ this.name = name;
12
+ this.plot = plot;
13
+ }
14
+ // readonly type: ScaleType = SCALE_TYPES.position;
15
+ // all marks that have this channel
16
+ marks = $derived(this.plot?.marks ?? []);
17
+ scaleOptions = $derived(this.plot && this.name !== undefined ? this.plot.options[this.name] || {} : {});
18
+ forceDomain = $derived(this.plot && (this.name === 'x' || this.name === 'y')
19
+ ? this.scaleOptions?.domain || null
20
+ : null);
21
+ possibleChannels = $derived(Object.entries(CHANNEL_SCALE)
22
+ .filter(([, channel]) => channel === this.name)
23
+ .map(([prop]) => prop));
24
+ activeMarks = $derived(this.marks.filter((mark) => this.possibleChannels.find((channel) => mark.channels.has(channel) && mark.props[channel] !== undefined)));
25
+ manualActiveMarks = $derived(this.activeMarks.filter((mark) => !mark.automatic));
26
+ propNames = $derived(uniq(this.manualActiveMarks
27
+ .map((mark) => this.possibleChannels
28
+ .filter((channel) => mark.channels.has(channel) &&
29
+ (typeof mark.props[channel] === 'string' ||
30
+ typeof mark.props[channel] === 'function') &&
31
+ !String(mark.props[channel]).startsWith('__'))
32
+ .map((channel) => typeof mark.props[channel] === 'string' ? mark.props[channel] : FUNCTION))
33
+ .flat(2)));
34
+ autoTitle = $derived(this.propNames.length === 1 && this.propNames[0] !== FUNCTION ? this.propNames[0] : null);
35
+ uniqueMarkProps = $derived(uniq(this.manualActiveMarks
36
+ .map((mark) => this.possibleChannels
37
+ .filter((prop) => mark.props[prop] !== undefined)
38
+ .map((prop) => mark.props[prop]))
39
+ .flat(2)));
40
+ dataValues = $derived([
41
+ ...this.activeMarks
42
+ // only check marks with data
43
+ .filter((mark) => mark.props.data.length)
44
+ .map((mark) => this.possibleChannels.map((prop) => mark.props.data.map((row) => resolveChannel(prop, row, mark.props))))
45
+ .flat(3)
46
+ .filter((d) => d != null),
47
+ ...(this.forceDomain || [])
48
+ ]);
49
+ isPosition = $derived(this.name === 'x' || this.name === 'y');
50
+ isColor = $derived(this.name === 'color');
51
+ scaleType = $derived(this.scaleOptions.type ||
52
+ inferScaleType(this.dataValues, { isPosition: this.isPosition, isColor: this.isColor }));
53
+ // readonly valueType = $derived(
54
+ // this.dataValues.every((v) => v == null)
55
+ // ? 'null'
56
+ // : this.dataValues.every(isColorOrNull)
57
+ // ? 'color'
58
+ // : this.dataValues.every(isBooleanOrNull)
59
+ // ? 'boolean'
60
+ // : this.dataValues.every(isStringOrNull)
61
+ // ? 'text'
62
+ // : this.dataValues.every(isNumberOrNull)
63
+ // ? 'number'
64
+ // : this.dataValues.every(isDateOrNull)
65
+ // ? 'date'
66
+ // : 'mixed'
67
+ // );
68
+ domain = $derived(this.scaleOptions.domain || inferScaleDomain(this.dataValues, this.scaleType));
69
+ }
70
+ // opacity: typeof === 'number' && between [0,1]
71
+ function inferScaleType(dataValues, { isPosition, isColor }) {
72
+ if (!dataValues.length)
73
+ return 'linear';
74
+ if (dataValues.every(isNumberOrNull))
75
+ return 'linear';
76
+ if (dataValues.every(isDateOrNull))
77
+ return 'time';
78
+ if (dataValues.every(isStringOrNull))
79
+ return 'band';
80
+ return 'linear';
81
+ }
82
+ function inferScaleDomain(dataValues, scaleType) {
83
+ if (scaleType === 'point' || scaleType === 'band') {
84
+ return uniq(dataValues);
85
+ }
86
+ if (scaleType === 'linear' ||
87
+ scaleType === 'pow' ||
88
+ scaleType === 'log' ||
89
+ scaleType === 'sqrt' ||
90
+ scaleType === 'sequential' ||
91
+ scaleType === 'time') {
92
+ return extent(dataValues);
93
+ }
94
+ }
@@ -1,3 +1,3 @@
1
- import type { ChannelName, MarkProps, MarkStyleProps } from './types';
2
- export declare const CHANNEL_TYPES: Record<ChannelName, symbol>;
3
- export declare const MARK_PROP_CHANNEL: Record<MarkProps & MarkStyleProps, ChannelName>;
1
+ import type { ScaleName, ChannelName } from './types.js';
2
+ export declare const SCALE_TYPES: Record<ScaleName, symbol>;
3
+ export declare const CHANNEL_SCALE: Record<ChannelName, ScaleName>;
package/dist/contants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const CHANNEL_TYPES = {
1
+ export const SCALE_TYPES = {
2
2
  opacity: Symbol('opacity'),
3
3
  color: Symbol('color'),
4
4
  x: Symbol('position'),
@@ -6,9 +6,10 @@ export const CHANNEL_TYPES = {
6
6
  angle: Symbol('angle'),
7
7
  symbol: Symbol('symbol'),
8
8
  radius: Symbol('radius'),
9
- width: Symbol('width')
9
+ width: Symbol('width'),
10
+ fontSize: Symbol('width')
10
11
  };
11
- export const MARK_PROP_CHANNEL = {
12
+ export const CHANNEL_SCALE = {
12
13
  x: 'x',
13
14
  x1: 'x',
14
15
  x2: 'x',
@@ -23,18 +24,19 @@ export const MARK_PROP_CHANNEL = {
23
24
  opacity: 'opacity',
24
25
  fillOpacity: 'opacity',
25
26
  strokeOpacity: 'opacity',
26
- strokeWidth: 'width'
27
+ strokeWidth: 'width',
28
+ fontSize: 'fontSize'
27
29
  };
28
- // export const CHANNEL_MAP: Record<ChannelName, ValueOf<typeof CHANNEL_TYPES>> = {
29
- // x: CHANNEL_TYPES.x,
30
- // y: CHANNEL_TYPES.y,
31
- // opacity: CHANNEL_TYPES.opacity,
32
- // strokeOpacity: CHANNEL_TYPES.opacity,
33
- // strokeWidth: CHANNEL_TYPES.width,
34
- // fillOpacity: CHANNEL_TYPES.opacity,
35
- // stroke: CHANNEL_TYPES.color,
36
- // fill: CHANNEL_TYPES.color,
37
- // r: CHANNEL_TYPES.radius,
38
- // rotate: CHANNEL_TYPES.angle,
39
- // symbol: CHANNEL_TYPES.symbol
30
+ // export const CHANNEL_MAP: Record<ScaleName, ValueOf<typeof SCALE_TYPES>> = {
31
+ // x: SCALE_TYPES.x,
32
+ // y: SCALE_TYPES.y,
33
+ // opacity: SCALE_TYPES.opacity,
34
+ // strokeOpacity: SCALE_TYPES.opacity,
35
+ // strokeWidth: SCALE_TYPES.width,
36
+ // fillOpacity: SCALE_TYPES.opacity,
37
+ // stroke: SCALE_TYPES.color,
38
+ // fill: SCALE_TYPES.color,
39
+ // r: SCALE_TYPES.radius,
40
+ // rotate: SCALE_TYPES.angle,
41
+ // symbol: SCALE_TYPES.symbol
40
42
  // };
@@ -1,5 +1,5 @@
1
- import { SvelteComponent } from "svelte";
2
- import type { DataRow } from '../types';
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { DataRow } from '../types.js';
3
3
  declare const __propDef: {
4
4
  props: {
5
5
  data: DataRow[];
@@ -14,6 +14,6 @@ declare const __propDef: {
14
14
  export type GroupMultipleProps = typeof __propDef.props;
15
15
  export type GroupMultipleEvents = typeof __propDef.events;
16
16
  export type GroupMultipleSlots = typeof __propDef.slots;
17
- export default class GroupMultiple extends SvelteComponent<GroupMultipleProps, GroupMultipleEvents, GroupMultipleSlots> {
17
+ export default class GroupMultiple extends SvelteComponentTyped<GroupMultipleProps, GroupMultipleEvents, GroupMultipleSlots> {
18
18
  }
19
19
  export {};
@@ -1,2 +1,2 @@
1
- import type { Channel } from '../classes/Channel.svelte';
2
- export default function autoTimeFormat(x: Channel, plotWidth: number): (date: Date) => string[];
1
+ import type { Scale } from '../classes/Scale.svelte.js';
2
+ export default function autoTimeFormat(x: Scale, plotWidth: number): (date: Date) => string[];
@@ -1,5 +1,5 @@
1
1
  import dayjs from 'dayjs';
2
- import { isDate } from 'underscore';
2
+ import isDate from 'underscore/modules/isDate.js';
3
3
  export default function autoTimeFormat(x, plotWidth) {
4
4
  const daysPer100Px = ((toNumber(x.domain[1]) - toNumber(x.domain[0])) / plotWidth / 864e5) * 100;
5
5
  const format = daysPer100Px < 1 ? 'HH:mm\nMMM DD' : daysPer100Px < 30 ? 'DD\nMMM' : 'MMM\nYYYY';
@@ -0,0 +1,4 @@
1
+ import type { RawValue } from '../types.js';
2
+ type Setter = (v: any) => void;
3
+ export default function (d3func: () => Record<string, Setter>, args: RawValue[], props?: Record<string, RawValue>): Record<string, Setter>;
4
+ export {};
@@ -0,0 +1,9 @@
1
+ export default function (d3func, args, props = {}) {
2
+ const res = d3func(...args);
3
+ for (const [key, val] of Object.entries(props)) {
4
+ if (typeof res[key] !== 'function')
5
+ throw new Error(`function ${key} does not exist`);
6
+ res[key](val);
7
+ }
8
+ return res;
9
+ }
@@ -1,5 +1,5 @@
1
1
  import { interpolateBrBG } from 'd3-scale-chromatic';
2
- import type { ColorScheme } from '../types';
2
+ import type { ColorScheme } from '../types.js';
3
3
  export declare const categoricalSchemes: Map<string, readonly string[]>;
4
4
  export declare function isCategoricalScheme(scheme: string): boolean;
5
5
  type SchemeGetter = (n: number) => readonly string[];
@@ -1,4 +1,5 @@
1
+ import type { ColorScheme, RawValue } from '../types.js';
1
2
  declare const Scales: Record<string, (domain: number[], range: [number, number]) => (val: any) => any>;
2
3
  export declare function createScale(type: keyof typeof Scales, domain: any, range: any, options?: {}): (val: any) => any;
3
- export declare function createColorScale(type: any, domain: string[] | [number, number] | [Date, Date] | [boolean | boolean], scheme: any): ((d: any) => any) | (unknown[] & string[] & import("d3-scale").ScaleOrdinal<string, unknown, never>);
4
+ export declare function createColorScale(type: any, domain: string[] | [number, number] | [Date, Date] | [boolean | boolean], range: RawValue[] | null, scheme: ColorScheme | null): ((d: any) => any) | (unknown[] & string[] & import("d3-scale").ScaleOrdinal<string, unknown, never>) | import("d3-scale").ScaleSequential<string, never>;
4
5
  export {};
@@ -1,47 +1,68 @@
1
- import { scaleBand, scaleLinear, scaleTime, scaleSqrt, scaleLog, scaleOrdinal } from 'd3-scale';
2
- import { getLogTicks } from './getLogTicks';
3
- import { categoricalSchemes, isCategoricalScheme, isOrdinalScheme, ordinalScheme } from './colors';
4
- import { isColorOrNull } from './typeChecks';
1
+ import { scaleBand, scaleLinear, scaleTime, scaleSqrt, scaleLog, scaleOrdinal, scalePoint, scaleSymlog, scalePow } from 'd3-scale';
2
+ import { scaleSequential, scaleDiverging } from 'd3-scale';
3
+ import { getLogTicks } from './getLogTicks.js';
4
+ import { categoricalSchemes, isCategoricalScheme, isQuantitativeScheme, ordinalScheme, quantitativeScheme } from './colors.js';
5
+ import { isColorOrNull } from './typeChecks.js';
5
6
  const Scales = {
7
+ point: scalePoint,
6
8
  band: scaleBand,
7
9
  linear: scaleLinear,
8
10
  time: scaleTime,
9
11
  sqrt: scaleSqrt,
12
+ pow: scalePow,
10
13
  log: scaleLog,
11
- ordinal: scaleOrdinal
14
+ symlog: scaleSymlog,
15
+ ordinal: scaleOrdinal,
16
+ sequential: scaleSequential,
17
+ diverging: scaleDiverging
12
18
  };
13
19
  export function createScale(type, domain, range, options = {}) {
14
- const scale = Scales[type](domain, range);
20
+ const scale = Scales[type]();
21
+ // scale defaults
22
+ if (type === 'band' &&
23
+ options.padding === undefined &&
24
+ options.paddingInner === undefined &&
25
+ options.paddingOuter === undefined) {
26
+ options.padding = 0.2;
27
+ }
15
28
  // allow setting arbiraty scale options
16
- for (const [key, val] of Object.entries(options)) {
29
+ // callWithProps(scale, { domain,})
30
+ for (const [key, val] of Object.entries({ domain, range, ...options })) {
17
31
  if (typeof scale[key] === 'function')
18
32
  scale[key](val);
19
33
  else
20
34
  console.warn('unknown scale setter ' + key);
21
35
  }
36
+ if (type === 'band' || type === 'point') {
37
+ scale.ticks = () => domain;
38
+ }
22
39
  if (type === 'log') {
23
- // overwrite scaleLog's internal ticks() method
24
40
  scale.ticks = (count) => getLogTicks(domain, count);
25
- // console.log({domain})
26
- // console.log(getLogTicks(domain, 5))
27
41
  }
28
42
  return scale;
29
43
  }
30
44
  const identity = (d) => d;
31
- export function createColorScale(type, domain, scheme) {
45
+ export function createColorScale(type, domain, range, scheme) {
46
+ if (type === 'identity')
47
+ return identity;
32
48
  if (type === 'band') {
33
49
  if (domain.every(isColorOrNull)) {
34
- console.log('domain is colors', domain);
35
50
  return identity;
36
51
  }
37
- const colorRange = !scheme
38
- ? categoricalSchemes.get('tableau10')
39
- : Array.isArray(scheme)
40
- ? scheme
52
+ const colorRange = Array.isArray(range)
53
+ ? range
54
+ : !scheme
55
+ ? categoricalSchemes.get('tableau10')
41
56
  : isCategoricalScheme(scheme)
42
57
  ? categoricalSchemes.get(scheme)
43
58
  : ordinalScheme(scheme)(domain.length);
44
59
  return scaleOrdinal().domain(domain).range(colorRange);
45
60
  }
61
+ else if (type === 'linear') {
62
+ const colorInterpolator = isQuantitativeScheme(scheme)
63
+ ? quantitativeScheme(scheme)
64
+ : quantitativeScheme('blues');
65
+ return scaleSequential(domain, colorInterpolator);
66
+ }
46
67
  return (d) => d;
47
68
  }
@@ -0,0 +1,3 @@
1
+ import type { Curve } from '../types.js';
2
+ import { type CurveFactory, type CurveBundleFactory } from 'd3-shape';
3
+ export declare function maybeCurve(curve: Curve | CurveFactory | undefined, tension: number): CurveFactory | CurveBundleFactory;
@@ -0,0 +1,42 @@
1
+ import { curveBasis, curveBasisClosed, curveBasisOpen, curveBundle, curveBumpX, curveBumpY, curveCardinal, curveCardinalClosed, curveCardinalOpen, curveCatmullRom, curveCatmullRomClosed, curveCatmullRomOpen, curveLinear, curveLinearClosed, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore } from 'd3-shape';
2
+ const curves = new Map([
3
+ ['basis', curveBasis],
4
+ ['basis-closed', curveBasisClosed],
5
+ ['basis-open', curveBasisOpen],
6
+ ['bundle', curveBundle],
7
+ ['bump-x', curveBumpX],
8
+ ['bump-y', curveBumpY],
9
+ ['cardinal', curveCardinal],
10
+ ['cardinal-closed', curveCardinalClosed],
11
+ ['cardinal-open', curveCardinalOpen],
12
+ ['catmull-rom', curveCatmullRom],
13
+ ['catmull-rom-closed', curveCatmullRomClosed],
14
+ ['catmull-rom-open', curveCatmullRomOpen],
15
+ ['linear', curveLinear],
16
+ ['linear-closed', curveLinearClosed],
17
+ ['monotone-x', curveMonotoneX],
18
+ ['monotone-y', curveMonotoneY],
19
+ ['natural', curveNatural],
20
+ ['step', curveStep],
21
+ ['step-after', curveStepAfter],
22
+ ['step-before', curveStepBefore]
23
+ ]);
24
+ export function maybeCurve(curve = curveLinear, tension) {
25
+ if (typeof curve === 'function')
26
+ return curve; // custom curve
27
+ const c = curves.get(`${curve}`.toLowerCase());
28
+ if (!c)
29
+ throw new Error(`unknown curve: ${curve}`);
30
+ if (tension !== undefined) {
31
+ if ('beta' in c) {
32
+ return c.beta(tension);
33
+ }
34
+ else if ('tension' in c) {
35
+ return c.tension(tension);
36
+ }
37
+ else if ('alpha' in c) {
38
+ return c.alpha(tension);
39
+ }
40
+ }
41
+ return c;
42
+ }
@@ -1,2 +1,2 @@
1
- import type { ChannelAccessor, MarkStyleProps, DataRow } from '../types';
1
+ import type { ChannelAccessor, MarkStyleProps, DataRow } from '../types.js';
2
2
  export default function (datum: DataRow, props: Partial<Record<MarkStyleProps, ChannelAccessor>>): string;