svelteplot 0.0.1-alpha.1 → 0.0.1-alpha.3

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 (70) hide show
  1. package/dist/Plot.svelte +171 -0
  2. package/dist/Plot.svelte.d.ts +15 -0
  3. package/dist/classes/Channel.svelte.js +72 -0
  4. package/dist/classes/Mark.svelte.js +17 -0
  5. package/dist/classes/Plot.svelte.js +99 -0
  6. package/dist/contants.d.ts +3 -0
  7. package/dist/contants.js +40 -0
  8. package/dist/helpers/GroupMultiple.svelte +8 -0
  9. package/dist/helpers/GroupMultiple.svelte.d.ts +19 -0
  10. package/dist/helpers/autoTimeFormat.d.ts +2 -0
  11. package/dist/helpers/autoTimeFormat.js +10 -0
  12. package/dist/helpers/colors.d.ts +13 -0
  13. package/dist/helpers/colors.js +200 -0
  14. package/dist/helpers/createScale.d.ts +4 -0
  15. package/dist/helpers/createScale.js +47 -0
  16. package/dist/helpers/getBaseStyles.d.ts +2 -0
  17. package/dist/helpers/getBaseStyles.js +18 -0
  18. package/dist/helpers/getLogTicks.d.ts +1 -0
  19. package/dist/helpers/getLogTicks.js +57 -0
  20. package/dist/helpers/isDataRecord.d.ts +2 -0
  21. package/dist/helpers/isDataRecord.js +13 -0
  22. package/dist/helpers/mergeDeep.d.ts +5 -0
  23. package/dist/helpers/mergeDeep.js +26 -0
  24. package/dist/helpers/removeIdenticalLines.d.ts +1 -0
  25. package/dist/helpers/removeIdenticalLines.js +16 -0
  26. package/dist/helpers/resolveChannel.d.ts +2 -0
  27. package/dist/helpers/resolveChannel.js +28 -0
  28. package/dist/helpers/symbols.d.ts +5 -0
  29. package/dist/helpers/symbols.js +51 -0
  30. package/dist/helpers/typeChecks.d.ts +7 -0
  31. package/dist/helpers/typeChecks.js +21 -0
  32. package/dist/helpers/wrapArray.d.ts +2 -0
  33. package/dist/helpers/wrapArray.js +4 -0
  34. package/dist/index.d.ts +12 -0
  35. package/dist/index.js +13 -0
  36. package/dist/marks/AxisX.svelte +101 -0
  37. package/dist/marks/AxisX.svelte.d.ts +17 -0
  38. package/dist/marks/AxisY.svelte +69 -0
  39. package/dist/marks/AxisY.svelte.d.ts +15 -0
  40. package/dist/marks/BaseMark.svelte +22 -0
  41. package/dist/marks/BaseMark.svelte.d.ts +19 -0
  42. package/dist/marks/ColorLegend.svelte +52 -0
  43. package/dist/marks/ColorLegend.svelte.d.ts +14 -0
  44. package/dist/marks/Dot.svelte +83 -0
  45. package/dist/marks/Dot.svelte.d.ts +15 -0
  46. package/dist/marks/DotX.svelte +5 -0
  47. package/dist/marks/DotX.svelte.d.ts +17 -0
  48. package/dist/marks/DotY.svelte +5 -0
  49. package/dist/marks/DotY.svelte.d.ts +17 -0
  50. package/dist/marks/Frame.svelte +37 -0
  51. package/dist/marks/Frame.svelte.d.ts +15 -0
  52. package/dist/marks/GridX.svelte +42 -0
  53. package/dist/marks/GridX.svelte.d.ts +19 -0
  54. package/dist/marks/GridY.svelte +31 -0
  55. package/dist/marks/GridY.svelte.d.ts +15 -0
  56. package/dist/marks/Line.svelte +49 -0
  57. package/dist/marks/Line.svelte.d.ts +15 -0
  58. package/dist/marks/LineX.svelte +10 -0
  59. package/dist/marks/LineX.svelte.d.ts +17 -0
  60. package/dist/marks/LineY.svelte +10 -0
  61. package/dist/marks/LineY.svelte.d.ts +17 -0
  62. package/dist/marks/RuleX.svelte +30 -0
  63. package/dist/marks/RuleX.svelte.d.ts +15 -0
  64. package/dist/marks/RuleY.svelte +31 -0
  65. package/dist/marks/RuleY.svelte.d.ts +15 -0
  66. package/dist/marks/SymbolLegend.svelte +50 -0
  67. package/dist/marks/SymbolLegend.svelte.d.ts +14 -0
  68. package/dist/types.d.ts +188 -0
  69. package/dist/types.js +1 -0
  70. package/package.json +4 -2
@@ -0,0 +1,171 @@
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";
9
+ let {
10
+ // snippets
11
+ header,
12
+ footer,
13
+ overlay,
14
+ children,
15
+ // props
16
+ height = "auto",
17
+ marginLeft = 30,
18
+ marginRight = 10,
19
+ marginTop = 20,
20
+ marginBottom = 40,
21
+ inset = null,
22
+ grid = false,
23
+ frame = false,
24
+ maxWidth = null,
25
+ title = "",
26
+ subtitle = "",
27
+ caption = "",
28
+ // scales
29
+ radius = null,
30
+ color = null,
31
+ symbol = null,
32
+ x = null,
33
+ y = null,
34
+ onmousemove = null
35
+ } = $props();
36
+ let width = $state(400);
37
+ const plot = new Plot(600, height || defaultPlotHeight, {
38
+ marginTop,
39
+ marginLeft,
40
+ marginRight,
41
+ marginBottom,
42
+ // scales
43
+ symbol,
44
+ radius,
45
+ x,
46
+ y,
47
+ color,
48
+ // other
49
+ title,
50
+ subtitle,
51
+ caption
52
+ });
53
+ setContext("svelteplot", plot);
54
+ $effect(() => {
55
+ plot.width = width;
56
+ plot._height = height;
57
+ plot.options = mergeDeep({}, DEFAULT_PLOT_OPTIONS, {
58
+ marginBottom,
59
+ marginLeft,
60
+ marginRight,
61
+ marginTop,
62
+ inset,
63
+ symbol,
64
+ radius,
65
+ color,
66
+ x,
67
+ y,
68
+ title,
69
+ subtitle,
70
+ caption
71
+ });
72
+ });
73
+ function onMouseMove(evt) {
74
+ if (onmousemove)
75
+ onmousemove({ ...evt, plot });
76
+ }
77
+ let hasLegend = $derived(color?.legend || symbol?.legend);
78
+ </script>
79
+
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}
90
+
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 />
98
+ {/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>
113
+
114
+ {#if plot.options.title || plot.options.subtitle || header || hasLegend}
115
+ <div class="plot-header">
116
+ {#if plot.options.title}
117
+ <h2>{@html plot.options.title}</h2>
118
+ {/if}
119
+ {#if plot.options.subtitle}
120
+ <h3>{@html plot.options.subtitle}</h3>
121
+ {/if}
122
+ {#if color?.legend}
123
+ <ColorLegend />
124
+ {/if}
125
+ {#if symbol?.legend}
126
+ <SymbolLegend />
127
+ {/if}
128
+ {#if header}{@render header(plot)}{/if}
129
+ </div>
130
+ {/if}
131
+
132
+ {#if footer || plot.options.caption}
133
+ <div class="plot-footer">
134
+ {#if plot.options.caption}
135
+ <figcaption>{@html plot.options.caption}</figcaption>
136
+ {/if}
137
+ {#if footer}{@render footer(plot)}{/if}
138
+ </div>
139
+ {/if}
140
+
141
+ <div class="overlay">
142
+ {#if overlay}{@render overlay(plot)}{/if}
143
+ </div>
144
+ </figure>
145
+
146
+ <style>
147
+ figure {
148
+ margin: 1em 0;
149
+ position: relative;
150
+ display: flex;
151
+ flex-direction: column;
152
+ }
153
+ figure .plot-header {
154
+ order: 1;
155
+ }
156
+ svg {
157
+ order: 2;
158
+ overflow: visible;
159
+ }
160
+ figure .plot-footer {
161
+ order: 3;
162
+ }
163
+ .overlay {
164
+ position: absolute;
165
+ pointer-events: none;
166
+ top: 0;
167
+ left: 0;
168
+ bottom: 0;
169
+ right: 0;
170
+ }
171
+ </style>
@@ -0,0 +1,15 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { PlotProps } from './types';
3
+ declare const __propDef: {
4
+ props: PlotProps;
5
+ events: {
6
+ [evt: string]: CustomEvent<any>;
7
+ };
8
+ slots: {};
9
+ };
10
+ type PlotProps_ = typeof __propDef.props;
11
+ export { PlotProps_ as PlotProps };
12
+ export type PlotEvents = typeof __propDef.events;
13
+ export type PlotSlots = typeof __propDef.slots;
14
+ export default class Plot extends SvelteComponent<PlotProps, PlotEvents, PlotSlots> {
15
+ }
@@ -0,0 +1,72 @@
1
+ import resolveChannel from '../helpers/resolveChannel';
2
+ import { extent } from 'd3-array';
3
+ import { MARK_PROP_CHANNEL, CHANNEL_TYPES } from '../contants';
4
+ import { isBooleanOrNull, isColorOrNull, isDateOrNull, isNumberOrNull, isStringOrNull } from '../helpers/typeChecks';
5
+ import { uniq } from 'underscore';
6
+ export class Channel {
7
+ name = undefined;
8
+ plot = undefined;
9
+ constructor(name, plot) {
10
+ this.name = name;
11
+ this.plot = plot;
12
+ }
13
+ // readonly type: ChannelType = CHANNEL_TYPES.position;
14
+ // all marks that have this channel
15
+ marks = $derived(this.plot?.marks ?? []);
16
+ forceDomain = $derived(this.plot && (this.name === 'x' || this.name === 'y')
17
+ ? this.plot.options[this.name]?.domain || null
18
+ : null);
19
+ possibleProps = $derived(Object.entries(MARK_PROP_CHANNEL)
20
+ .filter(([, channel]) => channel === this.name)
21
+ .map(([prop]) => prop));
22
+ activeMarks = $derived(this.marks.filter((mark) => mark.channels.has(this.name) && this.possibleProps.find((prop) => mark.props[prop])));
23
+ manualActiveMarks = $derived(this.activeMarks.filter((mark) => !mark.automatic));
24
+ autoTitle = $derived(this.manualActiveMarks.length === 1 &&
25
+ typeof this.manualActiveMarks[0].props?.[this.name] === 'string'
26
+ ? this.manualActiveMarks[0].props?.[this.name]
27
+ : null);
28
+ uniqueMarkProps = $derived(uniq(this.manualActiveMarks
29
+ .map((mark) => this.possibleProps
30
+ .filter((prop) => mark.props[prop])
31
+ .map((prop) => mark.props[prop]))
32
+ .flat(2)));
33
+ dataValues = $derived([
34
+ ...this.activeMarks
35
+ // only check marks with data
36
+ .filter((mark) => mark.props.data.length)
37
+ .map((mark) => this.possibleProps.map((prop) => mark.props.data.map((row) => resolveChannel(this.name, row, mark.props[prop]))))
38
+ .flat(3)
39
+ .filter((d) => d != null),
40
+ ...(this.forceDomain || [])
41
+ ]);
42
+ valueType = $derived(this.dataValues.every((v) => v == null)
43
+ ? 'null'
44
+ : this.dataValues.every(isColorOrNull)
45
+ ? 'color'
46
+ : this.dataValues.every(isBooleanOrNull)
47
+ ? 'boolean'
48
+ : this.dataValues.every(isStringOrNull)
49
+ ? 'text'
50
+ : this.dataValues.every(isNumberOrNull)
51
+ ? 'number'
52
+ : this.dataValues.every(isDateOrNull)
53
+ ? 'date'
54
+ : 'mixed');
55
+ domain = $derived(!this.dataValues.length
56
+ ? [0, 1]
57
+ : this.valueType === 'boolean' ||
58
+ this.valueType === 'text' ||
59
+ this.valueType === 'color'
60
+ ? uniq(this.dataValues)
61
+ : extent(this.dataValues));
62
+ scaleType = $derived(this.name === 'radius'
63
+ ? 'sqrt'
64
+ : this.valueType === 'date'
65
+ ? 'time'
66
+ : this.valueType === 'number'
67
+ ? 'linear'
68
+ : this.valueType === 'text'
69
+ ? 'band'
70
+ : 'linear');
71
+ }
72
+ // opacity: typeof === 'number' && between [0,1]
@@ -0,0 +1,17 @@
1
+ export class Mark {
2
+ id;
3
+ type;
4
+ automatic;
5
+ channels = $state(new Set());
6
+ props = $state();
7
+ constructor(type, channels, automatic, props) {
8
+ this.id = Symbol();
9
+ this.type = type;
10
+ this.automatic = automatic;
11
+ this.channels = new Set(channels);
12
+ this.props = props;
13
+ }
14
+ toString() {
15
+ return `Mark[${this.type}]`;
16
+ }
17
+ }
@@ -0,0 +1,99 @@
1
+ import { createScale, createColorScale } from '../helpers/createScale';
2
+ import mergeDeep from '../helpers/mergeDeep';
3
+ import { Channel } from './Channel.svelte';
4
+ import { uniq } from 'underscore';
5
+ export const DEFAULT_PLOT_OPTIONS = {
6
+ title: '',
7
+ subtitle: '',
8
+ caption: '',
9
+ marginLeft: 0,
10
+ marginRight: 0,
11
+ marginTop: 30,
12
+ marginBottom: 0,
13
+ radius: { range: [1, 10] },
14
+ symbol: {},
15
+ color: {},
16
+ x: {
17
+ domain: undefined,
18
+ grid: false,
19
+ ticks: undefined,
20
+ tickSpacing: 80,
21
+ axis: 'bottom',
22
+ log: false,
23
+ reverse: false
24
+ },
25
+ y: {
26
+ domain: undefined,
27
+ grid: false,
28
+ ticks: undefined,
29
+ tickSpacing: 60,
30
+ axis: 'left',
31
+ log: false,
32
+ reverse: false
33
+ }
34
+ };
35
+ export class Plot {
36
+ width = $state(600);
37
+ _height = $state(400);
38
+ options = $state(DEFAULT_PLOT_OPTIONS);
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')));
42
+ hasFilledDotMarks = $derived(!!this.marks.find((d) => d.type === 'dot' && d.props?.fill));
43
+ manualMarks = $derived(this.marks.filter((mark) => !mark.automatic));
44
+ singlePosChannelMark = $derived(this.manualMarks.length === 1 &&
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);
47
+ inset = $derived(typeof this.options.inset === 'number'
48
+ ? this.options.inset
49
+ : this.singlePosChannelMark
50
+ ? 10
51
+ : 0);
52
+ // derived props
53
+ margins = $derived({
54
+ top: this.options.marginTop,
55
+ left: this.options.marginLeft,
56
+ bottom: this.options.marginBottom,
57
+ right: this.options.marginRight
58
+ });
59
+ // margins = $state<Margins>({ left: 0, right: 0, top: 0, bottom: 0 });
60
+ plotWidth = $derived(this.width - this.margins.left - this.margins.right);
61
+ 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);
67
+ colorSymbolRedundant = $derived(this.color.uniqueMarkProps.length === 1 &&
68
+ this.symbol.uniqueMarkProps.length === 1 &&
69
+ 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
71
+ ? [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
74
+ ? [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' } : {}));
76
+ radiusScale = $derived(createScale(this.radius.scaleType, [0, Math.max(this.radius.domain[0], this.radius.domain[1])], this.options.radius.range));
77
+ symbolScale = $derived(createScale('ordinal', this.symbol.domain, this.options.symbol?.range || this.hasFilledDotMarks
78
+ ? ['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));
81
+ hasAxisXMark = $derived(!!this.marks.find((mark) => mark.type === 'axis-x' && !mark.automatic));
82
+ hasAxisYMark = $derived(!!this.marks.find((mark) => mark.type === 'axis-y' && !mark.automatic));
83
+ constructor(width, height, options) {
84
+ const opts = mergeDeep({}, DEFAULT_PLOT_OPTIONS, options);
85
+ this.width = width;
86
+ this._height = height;
87
+ this.options = opts;
88
+ }
89
+ addMark(mark) {
90
+ // console.log('addMark: ' + mark);
91
+ this.marks = [...this.marks, mark];
92
+ // add mark to respective channels
93
+ if (mark.channels.has('color'))
94
+ console.log(this.color.uniqueMarkProps);
95
+ }
96
+ removeMark(removeMark) {
97
+ this.marks = this.marks.filter((mark) => mark.id !== removeMark.id);
98
+ }
99
+ }
@@ -0,0 +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>;
@@ -0,0 +1,40 @@
1
+ export const CHANNEL_TYPES = {
2
+ opacity: Symbol('opacity'),
3
+ color: Symbol('color'),
4
+ x: Symbol('position'),
5
+ y: Symbol('position'),
6
+ angle: Symbol('angle'),
7
+ symbol: Symbol('symbol'),
8
+ radius: Symbol('radius'),
9
+ width: Symbol('width')
10
+ };
11
+ export const MARK_PROP_CHANNEL = {
12
+ x: 'x',
13
+ x1: 'x',
14
+ x2: 'x',
15
+ y: 'y',
16
+ y1: 'y',
17
+ y2: 'y',
18
+ rotate: 'angle',
19
+ r: 'radius',
20
+ symbol: 'symbol',
21
+ fill: 'color',
22
+ stroke: 'color',
23
+ opacity: 'opacity',
24
+ fillOpacity: 'opacity',
25
+ strokeOpacity: 'opacity',
26
+ strokeWidth: 'width'
27
+ };
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
40
+ // };
@@ -0,0 +1,8 @@
1
+ <script>let { data = [], ...rest } = $props();
2
+ </script>
3
+
4
+ {#if data.length !== 1}
5
+ <g {...rest}><slot /></g>
6
+ {:else}
7
+ <slot />
8
+ {/if}
@@ -0,0 +1,19 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { DataRow } from '../types';
3
+ declare const __propDef: {
4
+ props: {
5
+ data: DataRow[];
6
+ };
7
+ events: {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {
11
+ default: {};
12
+ };
13
+ };
14
+ export type GroupMultipleProps = typeof __propDef.props;
15
+ export type GroupMultipleEvents = typeof __propDef.events;
16
+ export type GroupMultipleSlots = typeof __propDef.slots;
17
+ export default class GroupMultiple extends SvelteComponent<GroupMultipleProps, GroupMultipleEvents, GroupMultipleSlots> {
18
+ }
19
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { Channel } from '../classes/Channel.svelte';
2
+ export default function autoTimeFormat(x: Channel, plotWidth: number): (date: Date) => string[];
@@ -0,0 +1,10 @@
1
+ import dayjs from 'dayjs';
2
+ import { isDate } from 'underscore';
3
+ export default function autoTimeFormat(x, plotWidth) {
4
+ const daysPer100Px = ((toNumber(x.domain[1]) - toNumber(x.domain[0])) / plotWidth / 864e5) * 100;
5
+ const format = daysPer100Px < 1 ? 'HH:mm\nMMM DD' : daysPer100Px < 30 ? 'DD\nMMM' : 'MMM\nYYYY';
6
+ return (date) => dayjs(date).format(format).split('\n');
7
+ }
8
+ function toNumber(d) {
9
+ return isDate(d) ? d.getTime() : +d;
10
+ }
@@ -0,0 +1,13 @@
1
+ import { interpolateBrBG } from 'd3-scale-chromatic';
2
+ import type { ColorScheme } from '../types';
3
+ export declare const categoricalSchemes: Map<string, readonly string[]>;
4
+ export declare function isCategoricalScheme(scheme: string): boolean;
5
+ type SchemeGetter = (n: number) => readonly string[];
6
+ export declare function isOrdinalScheme(scheme: ColorScheme): boolean;
7
+ export declare function ordinalScheme(scheme: string): SchemeGetter | undefined;
8
+ export declare function ordinalRange(scheme: string, length: number): readonly string[] | undefined;
9
+ export declare function maybeBooleanRange(domain: boolean[], scheme?: string): unknown[] | undefined;
10
+ export declare function isQuantitativeScheme(scheme: string): boolean;
11
+ export declare function quantitativeScheme(scheme: string): typeof interpolateBrBG | undefined;
12
+ export declare function isDivergingScheme(scheme: string): boolean;
13
+ export {};
@@ -0,0 +1,200 @@
1
+ import { interpolateBlues, interpolateBrBG, interpolateBuGn, interpolateBuPu, interpolateGnBu, interpolateGreens, interpolateGreys, interpolateOranges, interpolateOrRd, interpolatePiYG, interpolatePRGn, interpolatePuBu, interpolatePuBuGn, interpolatePuOr, interpolatePuRd, interpolatePurples, interpolateRdBu, interpolateRdGy, interpolateRdPu, interpolateRdYlBu, interpolateRdYlGn, interpolateReds, interpolateSpectral, interpolateYlGn, interpolateYlGnBu, interpolateYlOrBr, interpolateYlOrRd, interpolateTurbo, interpolateViridis, interpolateMagma, interpolateInferno, interpolatePlasma, interpolateCividis, interpolateCubehelixDefault, interpolateWarm, interpolateCool, interpolateRainbow, interpolateSinebow, schemeAccent, schemeBlues, schemeBrBG, schemeBuGn, schemeBuPu, schemeCategory10, schemeDark2, schemeGnBu, schemeGreens, schemeGreys, schemeOranges, schemeOrRd, schemePaired, schemePastel1, schemePastel2, schemePiYG, schemePRGn, schemePuBu, schemePuBuGn, schemePuOr, schemePuRd, schemePurples, schemeRdBu, schemeRdGy, schemeRdPu, schemeRdYlBu, schemeRdYlGn, schemeReds, schemeSet1, schemeSet2, schemeSet3, schemeSpectral, schemeTableau10, schemeYlGn, schemeYlGnBu, schemeYlOrBr, schemeYlOrRd } from 'd3-scale-chromatic';
2
+ import { quantize } from 'd3-interpolate';
3
+ export const categoricalSchemes = new Map([
4
+ ['accent', schemeAccent],
5
+ ['category10', schemeCategory10],
6
+ ['dark2', schemeDark2],
7
+ ['paired', schemePaired],
8
+ ['pastel1', schemePastel1],
9
+ ['pastel2', schemePastel2],
10
+ ['set1', schemeSet1],
11
+ ['set2', schemeSet2],
12
+ ['set3', schemeSet3],
13
+ ['tableau10', schemeTableau10]
14
+ ]);
15
+ export function isCategoricalScheme(scheme) {
16
+ return scheme != null && categoricalSchemes.has(`${scheme}`.toLowerCase());
17
+ }
18
+ const ordinalSchemes = new Map([
19
+ // diverging
20
+ ['brbg', scheme11(schemeBrBG, interpolateBrBG)],
21
+ ['prgn', scheme11(schemePRGn, interpolatePRGn)],
22
+ ['piyg', scheme11(schemePiYG, interpolatePiYG)],
23
+ ['puor', scheme11(schemePuOr, interpolatePuOr)],
24
+ ['rdbu', scheme11(schemeRdBu, interpolateRdBu)],
25
+ ['rdgy', scheme11(schemeRdGy, interpolateRdGy)],
26
+ ['rdylbu', scheme11(schemeRdYlBu, interpolateRdYlBu)],
27
+ ['rdylgn', scheme11(schemeRdYlGn, interpolateRdYlGn)],
28
+ ['spectral', scheme11(schemeSpectral, interpolateSpectral)],
29
+ // reversed diverging (for temperature data)
30
+ ['burd', scheme11r(schemeRdBu, interpolateRdBu)],
31
+ ['buylrd', scheme11r(schemeRdYlBu, interpolateRdYlBu)],
32
+ // sequential (single-hue)
33
+ ['blues', scheme9(schemeBlues, interpolateBlues)],
34
+ ['greens', scheme9(schemeGreens, interpolateGreens)],
35
+ ['greys', scheme9(schemeGreys, interpolateGreys)],
36
+ ['oranges', scheme9(schemeOranges, interpolateOranges)],
37
+ ['purples', scheme9(schemePurples, interpolatePurples)],
38
+ ['reds', scheme9(schemeReds, interpolateReds)],
39
+ // sequential (multi-hue)
40
+ ['turbo', schemei(interpolateTurbo)],
41
+ ['viridis', schemei(interpolateViridis)],
42
+ ['magma', schemei(interpolateMagma)],
43
+ ['inferno', schemei(interpolateInferno)],
44
+ ['plasma', schemei(interpolatePlasma)],
45
+ ['cividis', schemei(interpolateCividis)],
46
+ ['cubehelix', schemei(interpolateCubehelixDefault)],
47
+ ['warm', schemei(interpolateWarm)],
48
+ ['cool', schemei(interpolateCool)],
49
+ ['bugn', scheme9(schemeBuGn, interpolateBuGn)],
50
+ ['bupu', scheme9(schemeBuPu, interpolateBuPu)],
51
+ ['gnbu', scheme9(schemeGnBu, interpolateGnBu)],
52
+ ['orrd', scheme9(schemeOrRd, interpolateOrRd)],
53
+ ['pubu', scheme9(schemePuBu, interpolatePuBu)],
54
+ ['pubugn', scheme9(schemePuBuGn, interpolatePuBuGn)],
55
+ ['purd', scheme9(schemePuRd, interpolatePuRd)],
56
+ ['rdpu', scheme9(schemeRdPu, interpolateRdPu)],
57
+ ['ylgn', scheme9(schemeYlGn, interpolateYlGn)],
58
+ ['ylgnbu', scheme9(schemeYlGnBu, interpolateYlGnBu)],
59
+ ['ylorbr', scheme9(schemeYlOrBr, interpolateYlOrBr)],
60
+ ['ylorrd', scheme9(schemeYlOrRd, interpolateYlOrRd)],
61
+ // cyclical
62
+ ['rainbow', schemeicyclical(interpolateRainbow)],
63
+ ['sinebow', schemeicyclical(interpolateSinebow)]
64
+ ]);
65
+ export function isOrdinalScheme(scheme) {
66
+ return ordinalSchemes.has(scheme);
67
+ }
68
+ function scheme9(scheme, interpolate) {
69
+ return (n) => {
70
+ if (n === 1)
71
+ return [scheme[3][1]]; // favor midpoint
72
+ if (n === 2)
73
+ return [scheme[3][1], scheme[3][2]]; // favor darker
74
+ n = Math.max(3, Math.floor(n));
75
+ return n > 9 ? quantize(interpolate, n) : scheme[n];
76
+ };
77
+ }
78
+ function scheme11(scheme, interpolate) {
79
+ return (n) => {
80
+ if (n === 2)
81
+ return [scheme[3][0], scheme[3][2]]; // favor diverging extrema
82
+ n = Math.max(3, Math.floor(n));
83
+ return n > 11 ? quantize(interpolate, n) : scheme[n];
84
+ };
85
+ }
86
+ function scheme11r(scheme, interpolate) {
87
+ return (n) => {
88
+ if (n === 2)
89
+ return [scheme[3][2], scheme[3][0]]; // favor diverging extrema
90
+ n = Math.max(3, Math.floor(n));
91
+ return n > 11 ? quantize((t) => interpolate(1 - t), n) : scheme[n].slice().reverse();
92
+ };
93
+ }
94
+ function schemei(interpolate) {
95
+ return (n) => quantize(interpolate, Math.max(2, Math.floor(n)));
96
+ }
97
+ function schemeicyclical(interpolate) {
98
+ return (n) => quantize(interpolate, Math.floor(n) + 1).slice(0, -1);
99
+ }
100
+ export function ordinalScheme(scheme) {
101
+ const s = `${scheme}`.toLowerCase();
102
+ if (!ordinalSchemes.has(s))
103
+ throw new Error(`unknown ordinal scheme: ${s}`);
104
+ return ordinalSchemes.get(s);
105
+ }
106
+ export function ordinalRange(scheme, length) {
107
+ const s = ordinalScheme(scheme);
108
+ const r = typeof s === 'function' ? s({ length }) : s;
109
+ return r.length !== length ? r.slice(0, length) : r;
110
+ }
111
+ // If the specified domain contains only booleans (ignoring null and undefined),
112
+ // returns a corresponding range where false is mapped to the low color and true
113
+ // is mapped to the high color of the specified scheme.
114
+ export function maybeBooleanRange(domain, scheme = 'greys') {
115
+ const range = new Set();
116
+ const [f, t] = ordinalRange(scheme, 2);
117
+ for (const value of domain) {
118
+ if (value == null)
119
+ continue;
120
+ if (value === true)
121
+ range.add(t);
122
+ else if (value === false)
123
+ range.add(f);
124
+ else
125
+ return;
126
+ }
127
+ return [...range];
128
+ }
129
+ const quantitativeSchemes = new Map([
130
+ // diverging
131
+ ['brbg', interpolateBrBG],
132
+ ['prgn', interpolatePRGn],
133
+ ['piyg', interpolatePiYG],
134
+ ['puor', interpolatePuOr],
135
+ ['rdbu', interpolateRdBu],
136
+ ['rdgy', interpolateRdGy],
137
+ ['rdylbu', interpolateRdYlBu],
138
+ ['rdylgn', interpolateRdYlGn],
139
+ ['spectral', interpolateSpectral],
140
+ // reversed diverging (for temperature data)
141
+ ['burd', (t) => interpolateRdBu(1 - t)],
142
+ ['buylrd', (t) => interpolateRdYlBu(1 - t)],
143
+ // sequential (single-hue)
144
+ ['blues', interpolateBlues],
145
+ ['greens', interpolateGreens],
146
+ ['greys', interpolateGreys],
147
+ ['purples', interpolatePurples],
148
+ ['reds', interpolateReds],
149
+ ['oranges', interpolateOranges],
150
+ // sequential (multi-hue)
151
+ ['turbo', interpolateTurbo],
152
+ ['viridis', interpolateViridis],
153
+ ['magma', interpolateMagma],
154
+ ['inferno', interpolateInferno],
155
+ ['plasma', interpolatePlasma],
156
+ ['cividis', interpolateCividis],
157
+ ['cubehelix', interpolateCubehelixDefault],
158
+ ['warm', interpolateWarm],
159
+ ['cool', interpolateCool],
160
+ ['bugn', interpolateBuGn],
161
+ ['bupu', interpolateBuPu],
162
+ ['gnbu', interpolateGnBu],
163
+ ['orrd', interpolateOrRd],
164
+ ['pubugn', interpolatePuBuGn],
165
+ ['pubu', interpolatePuBu],
166
+ ['purd', interpolatePuRd],
167
+ ['rdpu', interpolateRdPu],
168
+ ['ylgnbu', interpolateYlGnBu],
169
+ ['ylgn', interpolateYlGn],
170
+ ['ylorbr', interpolateYlOrBr],
171
+ ['ylorrd', interpolateYlOrRd],
172
+ // cyclical
173
+ ['rainbow', interpolateRainbow],
174
+ ['sinebow', interpolateSinebow]
175
+ ]);
176
+ export function isQuantitativeScheme(scheme) {
177
+ return quantitativeSchemes.has(scheme);
178
+ }
179
+ export function quantitativeScheme(scheme) {
180
+ const s = `${scheme}`.toLowerCase();
181
+ if (!quantitativeSchemes.has(s))
182
+ throw new Error(`unknown quantitative scheme: ${s}`);
183
+ return quantitativeSchemes.get(s);
184
+ }
185
+ const divergingSchemes = new Set([
186
+ 'brbg',
187
+ 'prgn',
188
+ 'piyg',
189
+ 'puor',
190
+ 'rdbu',
191
+ 'rdgy',
192
+ 'rdylbu',
193
+ 'rdylgn',
194
+ 'spectral',
195
+ 'burd',
196
+ 'buylrd'
197
+ ]);
198
+ export function isDivergingScheme(scheme) {
199
+ return scheme != null && divergingSchemes.has(`${scheme}`.toLowerCase());
200
+ }