svelteplot 0.0.1-alpha.11 → 0.0.1-alpha.13

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 (62) hide show
  1. package/dist/Plot.svelte +6 -6
  2. package/dist/classes/Mark.svelte.js +4 -0
  3. package/dist/classes/Plot.svelte.js +47 -20
  4. package/dist/classes/Scale.svelte.js +95 -0
  5. package/dist/contants.d.ts +3 -3
  6. package/dist/contants.js +18 -16
  7. package/dist/helpers/autoTimeFormat.d.ts +2 -2
  8. package/dist/helpers/createScale.js +19 -6
  9. package/dist/helpers/curves.d.ts +3 -0
  10. package/dist/helpers/curves.js +42 -0
  11. package/dist/helpers/getBaseStyles.js +2 -2
  12. package/dist/helpers/getLogTicks.js +5 -5
  13. package/dist/helpers/isRawValue.d.ts +2 -0
  14. package/dist/helpers/isRawValue.js +5 -0
  15. package/dist/helpers/resolveChannel.d.ts +6 -2
  16. package/dist/helpers/resolveChannel.js +15 -5
  17. package/dist/helpers/wrapValueArray.d.ts +6 -0
  18. package/dist/helpers/wrapValueArray.js +5 -0
  19. package/dist/index.d.ts +10 -3
  20. package/dist/index.js +11 -3
  21. package/dist/marks/Area.svelte +35 -21
  22. package/dist/marks/Area.svelte.d.ts +14 -1
  23. package/dist/marks/AreaX.svelte +8 -15
  24. package/dist/marks/AreaX.svelte.d.ts +17 -4
  25. package/dist/marks/AreaY.svelte +9 -10
  26. package/dist/marks/AreaY.svelte.d.ts +17 -4
  27. package/dist/marks/AxisX.svelte +7 -8
  28. package/dist/marks/AxisY.svelte +10 -6
  29. package/dist/marks/BarX.svelte +52 -0
  30. package/dist/marks/BarX.svelte.d.ts +25 -0
  31. package/dist/marks/BarY.svelte +52 -0
  32. package/dist/marks/BarY.svelte.d.ts +25 -0
  33. package/dist/marks/BaseMark.svelte +9 -2
  34. package/dist/marks/BaseMark.svelte.d.ts +2 -1
  35. package/dist/marks/Dot.svelte +26 -40
  36. package/dist/marks/Dot.svelte.d.ts +9 -1
  37. package/dist/marks/GridX.svelte +26 -15
  38. package/dist/marks/GridX.svelte.d.ts +3 -3
  39. package/dist/marks/GridY.svelte +15 -4
  40. package/dist/marks/Line.svelte +16 -27
  41. package/dist/marks/Line.svelte.d.ts +13 -1
  42. package/dist/marks/LineX.svelte +5 -7
  43. package/dist/marks/LineX.svelte.d.ts +11 -4
  44. package/dist/marks/LineY.svelte +4 -6
  45. package/dist/marks/LineY.svelte.d.ts +11 -3
  46. package/dist/marks/RuleX.svelte +15 -7
  47. package/dist/marks/RuleY.svelte +15 -7
  48. package/dist/marks/TickX.svelte +30 -0
  49. package/dist/marks/TickX.svelte.d.ts +15 -0
  50. package/dist/marks/TickY.svelte +30 -0
  51. package/dist/marks/TickY.svelte.d.ts +15 -0
  52. package/dist/transforms/normalize.d.ts +18 -0
  53. package/dist/transforms/normalize.js +25 -0
  54. package/dist/transforms/recordize.d.ts +3 -0
  55. package/dist/transforms/recordize.js +28 -0
  56. package/dist/transforms/rename.d.ts +4 -0
  57. package/dist/transforms/rename.js +10 -0
  58. package/dist/transforms/stack.d.ts +11 -0
  59. package/dist/transforms/stack.js +73 -0
  60. package/dist/types.d.ts +51 -35
  61. package/package.json +20 -15
  62. package/dist/classes/Channel.svelte.js +0 -74
package/dist/Plot.svelte CHANGED
@@ -21,6 +21,7 @@ let {
21
21
  title = "",
22
22
  subtitle = "",
23
23
  caption = "",
24
+ testid,
24
25
  // scales
25
26
  radius = null,
26
27
  color = null,
@@ -70,11 +71,10 @@ function onMouseMove(evt) {
70
71
  if (onmousemove)
71
72
  onmousemove({ ...evt, plot });
72
73
  }
73
- $inspect(plot.x.domain);
74
74
  let hasLegend = $derived(color?.legend || symbol?.legend);
75
75
  </script>
76
76
 
77
- <figure class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
77
+ <figure data-testid={testid} class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
78
78
  <svg
79
79
  role="document"
80
80
  {width}
@@ -82,10 +82,10 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
82
82
  onmousemove={onmousemove ? onMouseMove : null}
83
83
  >
84
84
  <!-- automatic grids -->
85
- {#if (grid || x?.grid) && plot.hasChannelX}<GridX automatic />{/if}
86
- {#if (grid || y?.grid) && plot.hasChannelY}<GridY automatic />{/if}
85
+ {#if (grid && plot.hasScaleX) || x?.grid}<GridX automatic />{/if}
86
+ {#if (grid && plot.hasScaleY) || y?.grid}<GridY automatic />{/if}
87
87
 
88
- {#if !plot.hasAxisXMark && plot.hasChannelX}
88
+ {#if !plot.hasAxisXMark && (plot.hasScaleX || x?.grid)}
89
89
  <!-- automatic x axis -->
90
90
  {#if plot.options.x.axis === 'bottom' || plot.options.x.axis === 'both'}
91
91
  <AxisX anchor="bottom" automatic />
@@ -94,7 +94,7 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
94
94
  <AxisX anchor="top" automatic />
95
95
  {/if}
96
96
  {/if}
97
- {#if !plot.hasAxisYMark && plot.hasChannelY}
97
+ {#if !plot.hasAxisYMark && (plot.hasScaleY || y?.grid)}
98
98
  <!-- automatic y axis -->
99
99
  {#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
100
100
  <AxisY anchor="left" automatic />
@@ -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
1
  import { createScale, createColorScale } from '../helpers/createScale.js';
2
2
  import mergeDeep from '../helpers/mergeDeep.js';
3
- import { Channel } from './Channel.svelte';
4
- import { get } from 'underscore';
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.automatic && mark.channels.has('x')));
41
- hasChannelY = $derived(!!this.marks.find((mark) => !mark.automatic && 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,24 +67,28 @@ 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']));
91
+ : ['circle', 'plus', 'times', 'triangle2', 'asterisk', 'square2', 'diamond2'], getScaleOptions(this.symbol.scaleType, this.options.symbol || {})));
80
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));
@@ -87,12 +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
- console.log('y', this.hasChannelY, this.marks.filter(m => !m.automatic && m.channels.has('y')));
94
103
  }
95
104
  removeMark(removeMark) {
96
105
  this.marks = this.marks.filter((mark) => mark.id !== removeMark.id);
97
106
  }
98
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,95 @@
1
+ import resolveChannel from '../helpers/resolveChannel.js';
2
+ import { extent } from 'd3-array';
3
+ import { CHANNEL_SCALE } from '../contants.js';
4
+ import { isBooleanOrNull, isColorOrNull, isDateOrNull, isNumberOrNull, isStringOrNull } from '../helpers/typeChecks.js';
5
+ import { uniq } from 'underscore';
6
+ import isDataRecord from '../helpers/isDataRecord.js';
7
+ const FUNCTION = '(function)';
8
+ export class Scale {
9
+ name = undefined;
10
+ plot = $state(undefined);
11
+ constructor(name, plot) {
12
+ this.name = name;
13
+ this.plot = plot;
14
+ }
15
+ // readonly type: ScaleType = SCALE_TYPES.position;
16
+ // all marks that have this channel
17
+ marks = $derived(this.plot?.marks ?? []);
18
+ scaleOptions = $derived(this.plot && this.name !== undefined ? this.plot.options[this.name] || {} : {});
19
+ forceDomain = $derived(this.plot && (this.name === 'x' || this.name === 'y')
20
+ ? this.scaleOptions?.domain || null
21
+ : null);
22
+ possibleChannels = $derived(Object.entries(CHANNEL_SCALE)
23
+ .filter(([, channel]) => channel === this.name)
24
+ .map(([prop]) => prop));
25
+ activeMarks = $derived(this.marks.filter((mark) => this.possibleChannels.find((channel) => mark.channels.has(channel) && mark.props[channel] !== undefined)));
26
+ manualActiveMarks = $derived(this.activeMarks.filter((mark) => !mark.automatic));
27
+ propNames = $derived(uniq(this.manualActiveMarks
28
+ .map((mark) => this.possibleChannels
29
+ .filter((channel) => mark.channels.has(channel) &&
30
+ (typeof mark.props[channel] === 'string' ||
31
+ typeof mark.props[channel] === 'function') &&
32
+ !String(mark.props[channel]).startsWith('__'))
33
+ .map((channel) => typeof mark.props[channel] === 'string' ? mark.props[channel] : FUNCTION))
34
+ .flat(2)));
35
+ autoTitle = $derived(this.propNames.length === 1 && this.propNames[0] !== FUNCTION ? this.propNames[0] : null);
36
+ uniqueMarkProps = $derived(uniq(this.manualActiveMarks
37
+ .map((mark) => this.possibleChannels
38
+ .filter((prop) => mark.props[prop] !== undefined)
39
+ .map((prop) => mark.props[prop]))
40
+ .flat(2)));
41
+ dataValues = $derived([
42
+ ...this.activeMarks
43
+ // only check marks with data
44
+ .filter((mark) => mark.props.data.length)
45
+ .map((mark) => this.possibleChannels.map((prop) => mark.props.data.map((row) => resolveChannel(prop, row, mark.props))))
46
+ .flat(3)
47
+ .filter((d) => d != null),
48
+ ...(this.forceDomain || [])
49
+ ]);
50
+ isPosition = $derived(this.name === 'x' || this.name === 'y');
51
+ isColor = $derived(this.name === 'color');
52
+ scaleType = $derived(this.scaleOptions.type ||
53
+ inferScaleType(this.dataValues, { isPosition: this.isPosition, isColor: this.isColor }));
54
+ // readonly valueType = $derived(
55
+ // this.dataValues.every((v) => v == null)
56
+ // ? 'null'
57
+ // : this.dataValues.every(isColorOrNull)
58
+ // ? 'color'
59
+ // : this.dataValues.every(isBooleanOrNull)
60
+ // ? 'boolean'
61
+ // : this.dataValues.every(isStringOrNull)
62
+ // ? 'text'
63
+ // : this.dataValues.every(isNumberOrNull)
64
+ // ? 'number'
65
+ // : this.dataValues.every(isDateOrNull)
66
+ // ? 'date'
67
+ // : 'mixed'
68
+ // );
69
+ domain = $derived(this.scaleOptions.domain || inferScaleDomain(this.dataValues, this.scaleType));
70
+ }
71
+ // opacity: typeof === 'number' && between [0,1]
72
+ function inferScaleType(dataValues, { isPosition, isColor }) {
73
+ if (!dataValues.length)
74
+ return 'linear';
75
+ if (dataValues.every(isNumberOrNull))
76
+ return 'linear';
77
+ if (dataValues.every(isDateOrNull))
78
+ return 'time';
79
+ if (dataValues.every(isStringOrNull))
80
+ return 'band';
81
+ return 'linear';
82
+ }
83
+ function inferScaleDomain(dataValues, scaleType) {
84
+ if (scaleType === 'point' || scaleType === 'band') {
85
+ return uniq(dataValues);
86
+ }
87
+ if (scaleType === 'linear' ||
88
+ scaleType === 'pow' ||
89
+ scaleType === 'log' ||
90
+ scaleType === 'sqrt' ||
91
+ scaleType === 'sequential' ||
92
+ scaleType === 'time') {
93
+ return extent(dataValues);
94
+ }
95
+ }
@@ -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';
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,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';
2
+ export default function autoTimeFormat(x: Scale, plotWidth: number): (date: Date) => string[];
@@ -1,32 +1,45 @@
1
- import { scaleBand, scaleLinear, scaleTime, scaleSqrt, scaleLog, scaleOrdinal } from 'd3-scale';
1
+ import { scaleBand, scaleLinear, scaleTime, scaleSqrt, scaleLog, scaleOrdinal, scalePoint, scaleSymlog, scalePow } from 'd3-scale';
2
2
  import { scaleSequential, scaleDiverging } from 'd3-scale';
3
3
  import { getLogTicks } from './getLogTicks.js';
4
4
  import { categoricalSchemes, isCategoricalScheme, isQuantitativeScheme, ordinalScheme, quantitativeScheme } from './colors.js';
5
5
  import { isColorOrNull } from './typeChecks.js';
6
+ import callWithProps from './callWithProps.js';
7
+ import { count, nice } from 'd3-array';
6
8
  const Scales = {
9
+ point: scalePoint,
7
10
  band: scaleBand,
8
11
  linear: scaleLinear,
9
12
  time: scaleTime,
10
13
  sqrt: scaleSqrt,
14
+ pow: scalePow,
11
15
  log: scaleLog,
16
+ symlog: scaleSymlog,
12
17
  ordinal: scaleOrdinal,
13
18
  sequential: scaleSequential,
14
19
  diverging: scaleDiverging
15
20
  };
16
21
  export function createScale(type, domain, range, options = {}) {
17
- const scale = Scales[type](domain, range);
22
+ const scale = Scales[type]();
23
+ // scale defaults
24
+ if (type === 'band' &&
25
+ options.padding === undefined &&
26
+ options.paddingInner === undefined &&
27
+ options.paddingOuter === undefined) {
28
+ options.padding = 0.2;
29
+ }
18
30
  // allow setting arbiraty scale options
19
- for (const [key, val] of Object.entries(options)) {
31
+ // callWithProps(scale, { domain,})
32
+ for (const [key, val] of Object.entries({ domain, range, ...options })) {
20
33
  if (typeof scale[key] === 'function')
21
34
  scale[key](val);
22
35
  else
23
36
  console.warn('unknown scale setter ' + key);
24
37
  }
38
+ if (type === 'band' || type === 'point') {
39
+ scale.ticks = () => domain;
40
+ }
25
41
  if (type === 'log') {
26
- // overwrite scaleLog's internal ticks() method
27
42
  scale.ticks = (count) => getLogTicks(domain, count);
28
- // console.log({domain})
29
- // console.log(getLogTicks(domain, 5))
30
43
  }
31
44
  return scale;
32
45
  }
@@ -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,4 +1,4 @@
1
- import { MARK_PROP_CHANNEL } from '../contants.js';
1
+ import { CHANNEL_SCALE } from '../contants.js';
2
2
  import resolveChannel from './resolveChannel.js';
3
3
  const styleProps = {
4
4
  fill: 'fill',
@@ -13,6 +13,6 @@ const styleProps = {
13
13
  export default function (datum, props) {
14
14
  return Object.entries(styleProps)
15
15
  .filter(([key, cssKey]) => cssKey && props[key] != null)
16
- .map(([key, cssKey]) => `${cssKey}: ${resolveChannel(MARK_PROP_CHANNEL[key], datum, props[key])}`)
16
+ .map(([key, cssKey]) => `${cssKey}: ${resolveChannel(key, datum, props)}`)
17
17
  .join(';');
18
18
  }
@@ -10,7 +10,7 @@ export function getLogTicks(domain, count = 6) {
10
10
  return [];
11
11
  if (domain[0] === 0)
12
12
  return ticksArray(domain[0], domain[1], count - 2);
13
- let mult = Math.pow(10, Math.floor(Math.log10(Math.abs(domain[1] - domain[0]))) - 1);
13
+ let mult = 1; //Math.pow(10, Math.floor(Math.log10(Math.abs(domain[1] - domain[0]))) - 1);
14
14
  count += 2;
15
15
  let candidates = getTickCandidates(domain, mult);
16
16
  if (candidates[0].num > count) {
@@ -46,12 +46,12 @@ function getTickCandidates(domain, mult = 1) {
46
46
  return logSeries.map((factors) => {
47
47
  let i = Math.pow(10, Math.floor(Math.log10(domain[0])));
48
48
  let f = 0;
49
- const r = [i];
50
- while (i < domain[1] && r.length < 50) {
49
+ const ticks = [i];
50
+ while (i < domain[1] && ticks.length < 50) {
51
51
  i *= factors[f] * mult;
52
- r.push(i);
52
+ ticks.push(i);
53
53
  f = (f + 1) % factors.length;
54
54
  }
55
- return { ticks: r, num: r.length };
55
+ return { ticks, num: ticks.length };
56
56
  });
57
57
  }
@@ -0,0 +1,2 @@
1
+ import type { RawValue } from '../types.js';
2
+ export default function (value: any): value is RawValue;
@@ -0,0 +1,5 @@
1
+ import { isDate } from 'underscore';
2
+ export default function (value) {
3
+ const t = typeof value;
4
+ return t === 'string' || t === 'number' || t === 'boolean' || isDate(value) || t === null;
5
+ }
@@ -1,2 +1,6 @@
1
- import type { ChannelName, ChannelAccessor, DataRow, RawValue } from '../types';
2
- export default function (channel: ChannelName, datum: DataRow, accessor?: ChannelAccessor): RawValue;
1
+ import type { ChannelName, ChannelAccessor, DataRow, RawValue } from '../types.js';
2
+ type ChannelAlias = {
3
+ channel: ChannelName;
4
+ };
5
+ export default function (channel: ChannelName, datum: DataRow, channels: Partial<Record<ChannelName, ChannelAccessor | ChannelAlias>>): RawValue;
6
+ export {};
@@ -1,5 +1,14 @@
1
+ import { CHANNEL_SCALE } from '../contants.js';
1
2
  import isDataRecord from './isDataRecord.js';
2
- export default function (channel, datum, accessor = null) {
3
+ import isRawValue from './isRawValue.js';
4
+ export default function (channel, datum, channels) {
5
+ const scale = CHANNEL_SCALE[channel];
6
+ const maybeAccessor = channel === 'z' ? channels.z || channels.fill || channels.stroke : channels[channel];
7
+ const accessor = isDataRecord(maybeAccessor) && maybeAccessor?.channel
8
+ ? channels[maybeAccessor?.channel]
9
+ : maybeAccessor;
10
+ if (isDataRecord(accessor) && accessor.channel)
11
+ throw new Error('multiple channel aliases are not allowed');
3
12
  if ((channel === 'x' || channel === 'y') && Array.isArray(datum) && accessor === null) {
4
13
  // special case for [[x0,y0], [x1,y1], ...] format
5
14
  return datum[channel === 'x' ? 0 : 1];
@@ -14,15 +23,16 @@ export default function (channel, datum, accessor = null) {
14
23
  // fallback to channel name as accessor
15
24
  if (accessor === null && datum[channel] !== undefined)
16
25
  return datum[channel];
17
- // interpret accessor as constant
18
- return accessor;
26
+ return isRawValue(accessor) ? accessor : null;
19
27
  }
20
28
  else {
21
29
  // return single value or accessor
22
30
  return typeof accessor === 'function'
23
31
  ? accessor(datum)
24
- : accessor !== null
32
+ : accessor !== null && isRawValue(accessor)
25
33
  ? accessor
26
- : datum;
34
+ : !Array.isArray(datum) && (scale === 'x' || scale === 'y')
35
+ ? datum
36
+ : null;
27
37
  }
28
38
  }
@@ -0,0 +1,6 @@
1
+ import type { RawValue } from '../types.js';
2
+ export default function (values: RawValue[]): {
3
+ value: RawValue;
4
+ index: number;
5
+ ___orig___: RawValue;
6
+ }[];
@@ -0,0 +1,5 @@
1
+ export default function (values) {
2
+ const arr = values.map((value, index) => ({ value, index, ___orig___: value }));
3
+ arr.__wrapped__ = true;
4
+ return arr;
5
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- export { default as AxisX } from './marks/AxisX.svelte';
2
- export { default as AxisY } from './marks/AxisY.svelte';
3
- export { default as ColorLegend } from './marks/ColorLegend.svelte';
4
1
  export { default as Area } from './marks/Area.svelte';
5
2
  export { default as AreaX } from './marks/AreaX.svelte';
6
3
  export { default as AreaY } from './marks/AreaY.svelte';
4
+ export { default as AxisX } from './marks/AxisX.svelte';
5
+ export { default as AxisY } from './marks/AxisY.svelte';
6
+ export { default as BarX } from './marks/BarX.svelte';
7
+ export { default as BarY } from './marks/BarY.svelte';
8
+ export { default as ColorLegend } from './marks/ColorLegend.svelte';
7
9
  export { default as Dot } from './marks/Dot.svelte';
8
10
  export { default as DotX } from './marks/DotX.svelte';
9
11
  export { default as DotY } from './marks/DotY.svelte';
@@ -17,3 +19,8 @@ export { default as Plot } from './Plot.svelte';
17
19
  export { default as RuleX } from './marks/RuleX.svelte';
18
20
  export { default as RuleY } from './marks/RuleY.svelte';
19
21
  export { default as SymbolLegend } from './marks/SymbolLegend.svelte';
22
+ export { default as TickX } from './marks/TickX.svelte';
23
+ export { default as TickY } from './marks/TickY.svelte';
24
+ export { stackX, stackY } from './transforms/stack.js';
25
+ export { recordizeX, recordizeY } from './transforms/recordize.js';
26
+ export { renameChannels } from './transforms/rename.js';
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  // Reexport your entry components here
2
- export { default as AxisX } from './marks/AxisX.svelte';
3
- export { default as AxisY } from './marks/AxisY.svelte';
4
- export { default as ColorLegend } from './marks/ColorLegend.svelte';
5
2
  export { default as Area } from './marks/Area.svelte';
6
3
  export { default as AreaX } from './marks/AreaX.svelte';
7
4
  export { default as AreaY } from './marks/AreaY.svelte';
5
+ export { default as AxisX } from './marks/AxisX.svelte';
6
+ export { default as AxisY } from './marks/AxisY.svelte';
7
+ export { default as BarX } from './marks/BarX.svelte';
8
+ export { default as BarY } from './marks/BarY.svelte';
9
+ export { default as ColorLegend } from './marks/ColorLegend.svelte';
8
10
  export { default as Dot } from './marks/Dot.svelte';
9
11
  export { default as DotX } from './marks/DotX.svelte';
10
12
  export { default as DotY } from './marks/DotY.svelte';
@@ -18,3 +20,9 @@ export { default as Plot } from './Plot.svelte';
18
20
  export { default as RuleX } from './marks/RuleX.svelte';
19
21
  export { default as RuleY } from './marks/RuleY.svelte';
20
22
  export { default as SymbolLegend } from './marks/SymbolLegend.svelte';
23
+ export { default as TickX } from './marks/TickX.svelte';
24
+ export { default as TickY } from './marks/TickY.svelte';
25
+ // transforms
26
+ export { stackX, stackY } from './transforms/stack.js';
27
+ export { recordizeX, recordizeY } from './transforms/recordize.js';
28
+ export { renameChannels } from './transforms/rename.js';