svelteplot 0.0.1-alpha.2 → 0.0.1-alpha.20

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 +47 -41
  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,44 @@ 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
+ <svg
80
+ role="document"
81
+ {width}
82
+ height={plot.height}
83
+ onmousemove={onmousemove ? onMouseMove : null}
84
+ >
85
+ <!-- automatic grids -->
86
+ {#if (grid && plot.hasScaleX) || x?.grid}<GridX automatic />{/if}
87
+ {#if (grid && plot.hasScaleY) || y?.grid}<GridY automatic />{/if}
90
88
 
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 />
89
+ {#if !plot.hasAxisXMark && (plot.hasScaleX || x?.grid)}
90
+ <!-- automatic x axis -->
91
+ {#if plot.options.x.axis === 'bottom' || plot.options.x.axis === 'both'}
92
+ <AxisX anchor="bottom" automatic />
93
+ {/if}
94
+ {#if plot.options.x.axis === 'top' || plot.options.x.axis === 'both'}
95
+ <AxisX anchor="top" automatic />
96
+ {/if}
95
97
  {/if}
96
- {#if plot.options.x.axis === 'top' || plot.options.x.axis === 'both'}
97
- <AxisX anchor="top" automatic />
98
+ {#if !plot.hasAxisYMark && (plot.hasScaleY || y?.grid)}
99
+ <!-- automatic y axis -->
100
+ {#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
101
+ <AxisY anchor="left" automatic />
102
+ {/if}
103
+ {#if plot.options.y.axis === 'right' || plot.options.y.axis === 'both'}
104
+ <AxisY anchor="right" automatic />
105
+ {/if}
98
106
  {/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 />
104
- {/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>
107
+ <!-- automatic frame -->
108
+ {#if frame}<Frame />{/if}
109
+ {#if children}{@render children(plot)}{/if}
110
+ </svg>
111
+ <div class="overlay">
112
+ {#if overlay}{@render overlay(plot)}{/if}
113
+ </div>
114
+ </div>
113
115
 
114
116
  {#if plot.options.title || plot.options.subtitle || header || hasLegend}
115
117
  <div class="plot-header">
@@ -137,10 +139,6 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
137
139
  {#if footer}{@render footer(plot)}{/if}
138
140
  </div>
139
141
  {/if}
140
-
141
- <div class="overlay">
142
- {#if overlay}{@render overlay(plot)}{/if}
143
- </div>
144
142
  </figure>
145
143
 
146
144
  <style>
@@ -150,9 +148,17 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
150
148
  display: flex;
151
149
  flex-direction: column;
152
150
  }
151
+
152
+ .plot-body {
153
+ position: relative;
154
+ }
153
155
  figure .plot-header {
154
156
  order: 1;
155
157
  }
158
+ h2,
159
+ h3 {
160
+ margin: 0;
161
+ }
156
162
  svg {
157
163
  order: 2;
158
164
  overflow: visible;
@@ -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;
@@ -1,5 +1,4 @@
1
- import { MARK_PROP_CHANNEL } from '../contants';
2
- import resolveChannel from './resolveChannel';
1
+ import resolveChannel from './resolveChannel.js';
3
2
  const styleProps = {
4
3
  fill: 'fill',
5
4
  stroke: 'stroke',
@@ -13,6 +12,6 @@ const styleProps = {
13
12
  export default function (datum, props) {
14
13
  return Object.entries(styleProps)
15
14
  .filter(([key, cssKey]) => cssKey && props[key] != null)
16
- .map(([key, cssKey]) => `${cssKey}: ${resolveChannel(MARK_PROP_CHANNEL[key], datum, props[key])}`)
15
+ .map(([key, cssKey]) => `${cssKey}: ${resolveChannel(key, datum, props)}`)
17
16
  .join(';');
18
17
  }