layerchart 0.39.0 → 0.40.1
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.
- package/dist/components/Brush.svelte +244 -0
- package/dist/components/Brush.svelte.d.ts +53 -0
- package/dist/components/Chart.svelte +3 -4
- package/dist/components/Chart.svelte.d.ts +2 -4
- package/dist/components/ChartClipPath.svelte +6 -6
- package/dist/components/ChartClipPath.svelte.d.ts +1 -1
- package/dist/components/Frame.svelte +5 -0
- package/dist/components/Frame.svelte.d.ts +3 -0
- package/dist/components/Group.svelte +1 -0
- package/dist/components/Group.svelte.d.ts +1 -0
- package/dist/components/Rect.svelte +1 -0
- package/dist/components/Rect.svelte.d.ts +1 -0
- package/dist/components/TooltipContext.svelte +34 -19
- package/dist/components/TransformContext.svelte +1 -0
- package/dist/components/TransformContext.svelte.d.ts +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/utils/genData.d.ts +6 -0
- package/dist/utils/genData.js +9 -0
- package/package.json +4 -4
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
<script>import { createEventDispatcher, getContext } from 'svelte';
|
|
2
|
+
import { extent } from 'd3-array';
|
|
3
|
+
import { clamp, cls } from 'svelte-ux';
|
|
4
|
+
import Frame from './Frame.svelte';
|
|
5
|
+
import { localPoint } from '../utils/event.js';
|
|
6
|
+
import Group from './Group.svelte';
|
|
7
|
+
const { xScale, yScale, width, height, padding } = getContext('LayerCake');
|
|
8
|
+
const dispatch = createEventDispatcher();
|
|
9
|
+
/** Axis to apply brushing */
|
|
10
|
+
export let axis = 'x';
|
|
11
|
+
/** Size of draggable handles (width/height) */
|
|
12
|
+
export let handleSize = 5;
|
|
13
|
+
/** Only show range while actively brushing. Useful with `brushEnd` event */
|
|
14
|
+
export let resetOnEnd = false;
|
|
15
|
+
export let xDomain = $xScale.domain();
|
|
16
|
+
export let yDomain = $yScale.domain();
|
|
17
|
+
// Capture original domains for reset()
|
|
18
|
+
const originalXDomain = $xScale.domain();
|
|
19
|
+
const originalYDomain = $yScale.domain();
|
|
20
|
+
$: [xDomainMin, xDomainMax] = extent($xScale.domain());
|
|
21
|
+
$: [yDomainMin, yDomainMax] = extent($yScale.domain());
|
|
22
|
+
/** Attributes passed to range <rect> element */
|
|
23
|
+
export let range = undefined;
|
|
24
|
+
/** Attributes passed to handle <rect> elements */
|
|
25
|
+
export let handle = undefined;
|
|
26
|
+
export let classes = {};
|
|
27
|
+
let frameEl;
|
|
28
|
+
function handler(fn) {
|
|
29
|
+
return (e) => {
|
|
30
|
+
const start = {
|
|
31
|
+
xDomain: [xDomain[0] ?? xDomainMin, xDomain[1] ?? xDomainMax],
|
|
32
|
+
yDomain: [yDomain[0] ?? yDomainMin, yDomain[1] ?? yDomainMax],
|
|
33
|
+
value: {
|
|
34
|
+
x: $xScale.invert(localPoint(frameEl, e)?.x - $padding.left),
|
|
35
|
+
y: $yScale.invert(localPoint(frameEl, e)?.y - $padding.top),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
dispatch('brushStart', { xDomain, yDomain });
|
|
39
|
+
const onPointerMove = (e) => {
|
|
40
|
+
fn(start, {
|
|
41
|
+
x: $xScale.invert(localPoint(frameEl, e)?.x - $padding.left),
|
|
42
|
+
y: $yScale.invert(localPoint(frameEl, e)?.y - $padding.top),
|
|
43
|
+
});
|
|
44
|
+
// if (xDomain[0] === xDomain[1] || yDomain[0] === yDomain[1]) {
|
|
45
|
+
// // Ignore?
|
|
46
|
+
// // TODO: What about when using `x` or `y` axis?
|
|
47
|
+
// } else {
|
|
48
|
+
dispatch('change', { xDomain, yDomain });
|
|
49
|
+
// }
|
|
50
|
+
};
|
|
51
|
+
const onPointerUp = (e) => {
|
|
52
|
+
if (e.target === frameEl) {
|
|
53
|
+
reset();
|
|
54
|
+
dispatch('change', { xDomain, yDomain });
|
|
55
|
+
}
|
|
56
|
+
dispatch('brushEnd', { xDomain, yDomain });
|
|
57
|
+
if (resetOnEnd) {
|
|
58
|
+
reset();
|
|
59
|
+
}
|
|
60
|
+
window.removeEventListener('pointermove', onPointerMove);
|
|
61
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
62
|
+
};
|
|
63
|
+
window.addEventListener('pointermove', onPointerMove);
|
|
64
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const createRange = handler((start, value) => {
|
|
68
|
+
isActive = true;
|
|
69
|
+
xDomain = [
|
|
70
|
+
clamp(Math.min(start.value.x, value.x), xDomainMin, xDomainMax),
|
|
71
|
+
clamp(Math.max(start.value.x, value.x), xDomainMin, xDomainMax),
|
|
72
|
+
];
|
|
73
|
+
yDomain = [
|
|
74
|
+
clamp(Math.min(start.value.y, value.y), yDomainMin, yDomainMax),
|
|
75
|
+
clamp(Math.max(start.value.y, value.y), yDomainMin, yDomainMax),
|
|
76
|
+
];
|
|
77
|
+
});
|
|
78
|
+
const adjustRange = handler((start, value) => {
|
|
79
|
+
const dx = clamp(value.x - start.value.x, xDomainMin - start.xDomain[0], xDomainMax - start.xDomain[1]);
|
|
80
|
+
xDomain = [Number(start.xDomain[0]) + dx, Number(start.xDomain[1]) + dx];
|
|
81
|
+
const dy = clamp(value.y - start.value.y, yDomainMin - start.yDomain[0], yDomainMax - start.yDomain[1]);
|
|
82
|
+
yDomain = [Number(start.yDomain[0]) + dy, Number(start.yDomain[1]) + dy];
|
|
83
|
+
});
|
|
84
|
+
const adjustBottom = handler((start, value) => {
|
|
85
|
+
yDomain = [
|
|
86
|
+
clamp(value.y > start.yDomain[1] ? start.yDomain[1] : value.y, yDomainMin, yDomainMax),
|
|
87
|
+
clamp(value.y > start.yDomain[1] ? value.y : start.yDomain[1], yDomainMin, yDomainMax),
|
|
88
|
+
];
|
|
89
|
+
});
|
|
90
|
+
const adjustTop = handler((start, value) => {
|
|
91
|
+
yDomain = [
|
|
92
|
+
clamp(value.y < start.yDomain[1] ? value.y : start.yDomain[0], yDomainMin, yDomainMax),
|
|
93
|
+
clamp(value.y < start.yDomain[1] ? start.yDomain[0] : value.y, yDomainMin, yDomainMax),
|
|
94
|
+
];
|
|
95
|
+
});
|
|
96
|
+
const adjustLeft = handler((start, value) => {
|
|
97
|
+
xDomain = [
|
|
98
|
+
clamp(value.x > start.xDomain[1] ? start.xDomain[1] : value.x, xDomainMin, xDomainMax),
|
|
99
|
+
clamp(value.x > start.xDomain[1] ? value.x : start.xDomain[1], xDomainMin, xDomainMax),
|
|
100
|
+
];
|
|
101
|
+
});
|
|
102
|
+
const adjustRight = handler((start, value) => {
|
|
103
|
+
xDomain = [
|
|
104
|
+
clamp(value.x < start.xDomain[0] ? value.x : start.xDomain[0], xDomainMin, xDomainMax),
|
|
105
|
+
clamp(value.x < start.xDomain[0] ? start.xDomain[0] : value.x, xDomainMin, xDomainMax),
|
|
106
|
+
];
|
|
107
|
+
});
|
|
108
|
+
function reset() {
|
|
109
|
+
isActive = false;
|
|
110
|
+
xDomain = originalXDomain;
|
|
111
|
+
yDomain = originalYDomain;
|
|
112
|
+
}
|
|
113
|
+
function selectAll() {
|
|
114
|
+
xDomain = [xDomainMin, xDomainMax];
|
|
115
|
+
yDomain = [yDomainMin, yDomainMax];
|
|
116
|
+
}
|
|
117
|
+
$: top = $yScale(yDomain[1]);
|
|
118
|
+
$: bottom = $yScale(yDomain[0]);
|
|
119
|
+
$: left = $xScale(xDomain[0]);
|
|
120
|
+
$: right = $xScale(xDomain[1]);
|
|
121
|
+
$: rangeTop = axis === 'both' || axis === 'y' ? top : 0;
|
|
122
|
+
$: rangeLeft = axis === 'both' || axis === 'x' ? left : 0;
|
|
123
|
+
$: rangeWidth = axis === 'both' || axis === 'x' ? right - left : $width;
|
|
124
|
+
$: rangeHeight = axis === 'both' || axis === 'y' ? bottom - top : $height;
|
|
125
|
+
// Set reactively to handle cases where xDomain/yDomain are set externally (ex. `bind:xDomain`)
|
|
126
|
+
$: isActive =
|
|
127
|
+
xDomain[0]?.valueOf() !== originalXDomain[0]?.valueOf() ||
|
|
128
|
+
xDomain[1]?.valueOf() !== originalXDomain[1]?.valueOf() ||
|
|
129
|
+
yDomain[0]?.valueOf() !== originalYDomain[0]?.valueOf() ||
|
|
130
|
+
yDomain[1]?.valueOf() !== originalYDomain[1]?.valueOf();
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<g class={cls('Brush select-none', classes.root, $$props.class)}>
|
|
134
|
+
<Frame
|
|
135
|
+
class={cls('frame', 'fill-transparent', classes.frame)}
|
|
136
|
+
on:pointerdown={createRange}
|
|
137
|
+
on:dblclick={() => selectAll()}
|
|
138
|
+
bind:rectEl={frameEl}
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
{#if isActive}
|
|
142
|
+
<Group x={rangeLeft} y={rangeTop} class="range">
|
|
143
|
+
<slot name="range" {rangeWidth} {rangeHeight}>
|
|
144
|
+
<rect
|
|
145
|
+
width={rangeWidth}
|
|
146
|
+
height={rangeHeight}
|
|
147
|
+
class={cls(
|
|
148
|
+
'cursor-move select-none',
|
|
149
|
+
range?.fill == null && 'fill-surface-content/10',
|
|
150
|
+
classes.range
|
|
151
|
+
)}
|
|
152
|
+
on:pointerdown={adjustRange}
|
|
153
|
+
on:dblclick={() => reset()}
|
|
154
|
+
{...range}
|
|
155
|
+
/>
|
|
156
|
+
</slot>
|
|
157
|
+
</Group>
|
|
158
|
+
|
|
159
|
+
{#if axis === 'both' || axis === 'y'}
|
|
160
|
+
<Group
|
|
161
|
+
x={rangeLeft}
|
|
162
|
+
y={rangeTop}
|
|
163
|
+
class="handle top"
|
|
164
|
+
on:pointerdown={adjustTop}
|
|
165
|
+
on:dblclick={() => {
|
|
166
|
+
yDomain[0] = yDomainMin;
|
|
167
|
+
dispatch('change', { xDomain, yDomain });
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
<slot name="handle" edge="top" {rangeWidth} {rangeHeight}>
|
|
171
|
+
<rect
|
|
172
|
+
width={rangeWidth}
|
|
173
|
+
height={handleSize}
|
|
174
|
+
class={cls('fill-transparent cursor-ns-resize select-none', classes.handle)}
|
|
175
|
+
{...handle}
|
|
176
|
+
/>
|
|
177
|
+
</slot>
|
|
178
|
+
</Group>
|
|
179
|
+
|
|
180
|
+
<Group
|
|
181
|
+
x={rangeLeft}
|
|
182
|
+
y={bottom - handleSize + 1}
|
|
183
|
+
class="handle bottom"
|
|
184
|
+
on:pointerdown={adjustBottom}
|
|
185
|
+
on:dblclick={() => {
|
|
186
|
+
yDomain[1] = yDomainMax;
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
<slot name="handle" edge="bottom" {rangeWidth} {rangeHeight}>
|
|
190
|
+
<rect
|
|
191
|
+
width={rangeWidth}
|
|
192
|
+
height={handleSize}
|
|
193
|
+
class={cls('fill-transparent cursor-ns-resize select-none', classes.handle)}
|
|
194
|
+
{...handle}
|
|
195
|
+
/>
|
|
196
|
+
</slot>
|
|
197
|
+
</Group>
|
|
198
|
+
{/if}
|
|
199
|
+
|
|
200
|
+
{#if axis === 'both' || axis === 'x'}
|
|
201
|
+
<Group
|
|
202
|
+
x={rangeLeft}
|
|
203
|
+
y={rangeTop}
|
|
204
|
+
class="handle left"
|
|
205
|
+
on:pointerdown={adjustLeft}
|
|
206
|
+
on:dblclick={() => {
|
|
207
|
+
xDomain[0] = xDomainMin;
|
|
208
|
+
dispatch('change', { xDomain, yDomain });
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
<slot name="handle" edge="left" {rangeWidth} {rangeHeight}>
|
|
212
|
+
<rect
|
|
213
|
+
width={handleSize}
|
|
214
|
+
height={rangeHeight}
|
|
215
|
+
class={cls('fill-transparent cursor-ew-resize select-none', classes.handle)}
|
|
216
|
+
{...handle}
|
|
217
|
+
/>
|
|
218
|
+
</slot>
|
|
219
|
+
</Group>
|
|
220
|
+
|
|
221
|
+
<Group
|
|
222
|
+
x={right - handleSize + 1}
|
|
223
|
+
y={rangeTop}
|
|
224
|
+
class="handle right"
|
|
225
|
+
on:pointerdown={adjustRight}
|
|
226
|
+
on:dblclick={() => {
|
|
227
|
+
xDomain[1] = xDomainMax;
|
|
228
|
+
dispatch('change', { xDomain, yDomain });
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
<slot name="handle" edge="right" {rangeWidth} {rangeHeight}>
|
|
232
|
+
<rect
|
|
233
|
+
width={handleSize}
|
|
234
|
+
height={rangeHeight}
|
|
235
|
+
class={cls('fill-transparent cursor-ew-resize select-none', classes.handle)}
|
|
236
|
+
{...handle}
|
|
237
|
+
/>
|
|
238
|
+
</slot>
|
|
239
|
+
</Group>
|
|
240
|
+
{/if}
|
|
241
|
+
|
|
242
|
+
<!-- TODO: Add diagonal/corner handles -->
|
|
243
|
+
{/if}
|
|
244
|
+
</g>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
import type { SVGAttributes } from 'svelte/elements';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
[x: string]: any;
|
|
6
|
+
axis?: "x" | "y" | "both" | undefined;
|
|
7
|
+
handleSize?: number | undefined;
|
|
8
|
+
resetOnEnd?: boolean | undefined;
|
|
9
|
+
xDomain?: [number | null, number | null] | undefined;
|
|
10
|
+
yDomain?: [number | null, number | null] | undefined;
|
|
11
|
+
range?: SVGAttributes<SVGRectElement> | undefined;
|
|
12
|
+
handle?: SVGAttributes<SVGRectElement> | undefined;
|
|
13
|
+
classes?: {
|
|
14
|
+
root?: string | undefined;
|
|
15
|
+
frame?: string | undefined;
|
|
16
|
+
range?: string | undefined;
|
|
17
|
+
handle?: string | undefined;
|
|
18
|
+
} | undefined;
|
|
19
|
+
};
|
|
20
|
+
events: {
|
|
21
|
+
change: CustomEvent<{
|
|
22
|
+
xDomain?: [any, any] | undefined;
|
|
23
|
+
yDomain?: [any, any] | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
brushStart: CustomEvent<{
|
|
26
|
+
xDomain?: [any, any] | undefined;
|
|
27
|
+
yDomain?: [any, any] | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
brushEnd: CustomEvent<{
|
|
30
|
+
xDomain?: [any, any] | undefined;
|
|
31
|
+
yDomain?: [any, any] | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
} & {
|
|
34
|
+
[evt: string]: CustomEvent<any>;
|
|
35
|
+
};
|
|
36
|
+
slots: {
|
|
37
|
+
range: {
|
|
38
|
+
rangeWidth: any;
|
|
39
|
+
rangeHeight: any;
|
|
40
|
+
};
|
|
41
|
+
handle: {
|
|
42
|
+
edge: string;
|
|
43
|
+
rangeWidth: any;
|
|
44
|
+
rangeHeight: any;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export type BrushProps = typeof __propDef.props;
|
|
49
|
+
export type BrushEvents = typeof __propDef.events;
|
|
50
|
+
export type BrushSlots = typeof __propDef.slots;
|
|
51
|
+
export default class Brush extends SvelteComponentTyped<BrushProps, BrushEvents, BrushSlots> {
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -76,8 +76,6 @@ export let tooltip = undefined;
|
|
|
76
76
|
/** Props passed to TransformContext */
|
|
77
77
|
export let transform = undefined;
|
|
78
78
|
export let transformContext = undefined;
|
|
79
|
-
/** Set transformContext's initial translate and scale based on geo object. Requires `geo.projection` to also be set */
|
|
80
|
-
export let fitGeoObject = undefined;
|
|
81
79
|
// Binded for access within TransformContext
|
|
82
80
|
let geoProjection = undefined;
|
|
83
81
|
// Track when mounted since LayerCake initializes width/height with `100` until binded `clientWidth`/`clientWidth` can run
|
|
@@ -115,9 +113,10 @@ onMount(() => {
|
|
|
115
113
|
let:data
|
|
116
114
|
let:flatData
|
|
117
115
|
>
|
|
116
|
+
<!-- Apply fitGeojson using TransformContext instead of GeoContext if `applyTransform` is used -->
|
|
118
117
|
{@const initialTransform =
|
|
119
|
-
|
|
120
|
-
? geoFitObjectTransform(geo.projection(), [width, height],
|
|
118
|
+
geo?.applyTransform && geo?.fitGeojson && geo?.projection
|
|
119
|
+
? geoFitObjectTransform(geo.projection(), [width, height], geo.fitGeojson)
|
|
121
120
|
: undefined}
|
|
122
121
|
|
|
123
122
|
{#key isMounted}
|
|
@@ -11,13 +11,12 @@ import { type ComponentProps } from 'svelte';
|
|
|
11
11
|
import GeoContext from './GeoContext.svelte';
|
|
12
12
|
import TooltipContext from './TooltipContext.svelte';
|
|
13
13
|
import TransformContext from './TransformContext.svelte';
|
|
14
|
-
import { geoFitObjectTransform } from '../utils/geo.js';
|
|
15
14
|
declare const __propDef: {
|
|
16
15
|
props: {
|
|
17
16
|
[x: string]: any;
|
|
18
17
|
data?: any[] | undefined;
|
|
19
|
-
x?: (string | ((d: any) => number)) | (string | ((d: any) => number))[] | undefined;
|
|
20
|
-
y?: (string | ((d: any) => number)) | (string | ((d: any) => number))[] | undefined;
|
|
18
|
+
x?: (string | number | ((d: any) => number)) | (string | number | ((d: any) => number))[] | undefined;
|
|
19
|
+
y?: (string | number | ((d: any) => number)) | (string | number | ((d: any) => number))[] | undefined;
|
|
21
20
|
yScale?: Function | undefined;
|
|
22
21
|
xBaseline?: number | null | undefined;
|
|
23
22
|
yBaseline?: number | null | undefined;
|
|
@@ -25,7 +24,6 @@ declare const __propDef: {
|
|
|
25
24
|
tooltip?: Partial<ComponentProps<TooltipContext>> | boolean | undefined;
|
|
26
25
|
transform?: Partial<ComponentProps<TransformContext>> | undefined;
|
|
27
26
|
transformContext?: TransformContext | undefined;
|
|
28
|
-
fitGeoObject?: Parameters<typeof geoFitObjectTransform>[2] | undefined;
|
|
29
27
|
};
|
|
30
28
|
events: {
|
|
31
29
|
transform: CustomEvent<{
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<script>import { getContext } from 'svelte';
|
|
2
2
|
import RectClipPath from './RectClipPath.svelte';
|
|
3
3
|
const { width, height, padding } = getContext('LayerCake');
|
|
4
|
-
/**
|
|
5
|
-
export let
|
|
4
|
+
/** Include padding area (ex. axis) */
|
|
5
|
+
export let full = false;
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<RectClipPath
|
|
9
|
-
x={
|
|
10
|
-
y={
|
|
11
|
-
width={$width + (
|
|
12
|
-
height={$height + (
|
|
9
|
+
x={full ? -$padding.left : 0}
|
|
10
|
+
y={full ? -$padding.top : 0}
|
|
11
|
+
width={$width + (full ? $padding.left + $padding.right : 0)}
|
|
12
|
+
height={$height + (full ? $padding.top + $padding.bottom : 0)}
|
|
13
13
|
on:click
|
|
14
14
|
{...$$restProps}
|
|
15
15
|
>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
const { width, height, padding } = getContext('LayerCake');
|
|
3
3
|
/** Include padding area */
|
|
4
4
|
export let full = false;
|
|
5
|
+
/** Access underlying `<rect>` element */
|
|
6
|
+
export let rectEl;
|
|
5
7
|
</script>
|
|
6
8
|
|
|
7
9
|
<rect
|
|
@@ -10,5 +12,8 @@ export let full = false;
|
|
|
10
12
|
width={$width + (full ? $padding.left + $padding.right : 0)}
|
|
11
13
|
height={$height + (full ? $padding.top + $padding.bottom : 0)}
|
|
12
14
|
on:click
|
|
15
|
+
on:pointerdown
|
|
16
|
+
on:dblclick
|
|
17
|
+
bind:this={rectEl}
|
|
13
18
|
{...$$restProps}
|
|
14
19
|
/>
|
|
@@ -3,9 +3,12 @@ declare const __propDef: {
|
|
|
3
3
|
props: {
|
|
4
4
|
[x: string]: any;
|
|
5
5
|
full?: boolean | undefined;
|
|
6
|
+
rectEl: SVGRectElement;
|
|
6
7
|
};
|
|
7
8
|
events: {
|
|
8
9
|
click: MouseEvent;
|
|
10
|
+
pointerdown: PointerEvent;
|
|
11
|
+
dblclick: MouseEvent;
|
|
9
12
|
} & {
|
|
10
13
|
[evt: string]: CustomEvent<any>;
|
|
11
14
|
};
|
|
@@ -112,6 +112,14 @@ function showTooltip(e, tooltipData) {
|
|
|
112
112
|
const point = localPoint(referenceNode, e);
|
|
113
113
|
const localX = point?.x ?? 0;
|
|
114
114
|
const localY = point?.y ?? 0;
|
|
115
|
+
if (e.offsetX < e.currentTarget.offsetLeft ||
|
|
116
|
+
e.offsetX > e.currentTarget.offsetLeft + e.currentTarget.offsetWidth ||
|
|
117
|
+
e.offsetY < e.currentTarget.offsetTop ||
|
|
118
|
+
e.offsetY > e.currentTarget.offsetTop + e.currentTarget.offsetHeight) {
|
|
119
|
+
// Ignore if within padding of chart
|
|
120
|
+
hideTooltip();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
115
123
|
// If tooltipData not provided already (voronoi, etc), attempt to find it
|
|
116
124
|
// TODO: When using bisect-x/y/band, values should be sorted. Tyipcally are for `x`, but not `y` (and band depends on if x or y scale)
|
|
117
125
|
if (tooltipData == null) {
|
|
@@ -267,26 +275,30 @@ $: if (mode === 'bounds' || mode === 'band') {
|
|
|
267
275
|
}
|
|
268
276
|
</script>
|
|
269
277
|
|
|
270
|
-
<slot tooltip={$tooltip} />
|
|
271
|
-
|
|
272
278
|
{#if ['bisect-x', 'bisect-y', 'bisect-band', 'quadtree'].includes(mode)}
|
|
273
|
-
<
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
279
|
+
<div
|
|
280
|
+
style:width="{$width}px"
|
|
281
|
+
style:height="{$height}px"
|
|
282
|
+
style:top="{$padding.top}px"
|
|
283
|
+
style:left="{$padding.left}px"
|
|
284
|
+
class={cls(
|
|
285
|
+
'tooltip-trigger absolute touch-none',
|
|
286
|
+
debug && 'bg-danger/10 outline outline-danger'
|
|
287
|
+
)}
|
|
288
|
+
on:pointerenter={showTooltip}
|
|
289
|
+
on:pointermove={showTooltip}
|
|
290
|
+
on:pointerleave={hideTooltip}
|
|
291
|
+
on:click={(e) => {
|
|
292
|
+
onClick({ data: $tooltip?.data });
|
|
293
|
+
}}
|
|
294
|
+
>
|
|
295
|
+
<!-- Rendering slot within tooltip-trigger allows pointer events to bubble up (ex. Brush) -->
|
|
296
|
+
<div class="absolute" style:top="-{$padding.top}px" style:left="-{$padding.left}px">
|
|
297
|
+
<slot tooltip={$tooltip} />
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
289
300
|
{:else if mode === 'voronoi'}
|
|
301
|
+
<slot tooltip={$tooltip} />
|
|
290
302
|
<Svg>
|
|
291
303
|
<Voronoi
|
|
292
304
|
on:pointerenter={(e) => showTooltip(e.detail.event, e.detail.data)}
|
|
@@ -299,6 +311,7 @@ $: if (mode === 'bounds' || mode === 'band') {
|
|
|
299
311
|
/>
|
|
300
312
|
</Svg>
|
|
301
313
|
{:else if mode === 'bounds' || mode === 'band'}
|
|
314
|
+
<slot tooltip={$tooltip} />
|
|
302
315
|
<Svg>
|
|
303
316
|
<g class="tooltip-rects">
|
|
304
317
|
{#each rects as rect}
|
|
@@ -318,10 +331,12 @@ $: if (mode === 'bounds' || mode === 'band') {
|
|
|
318
331
|
{/each}
|
|
319
332
|
</g>
|
|
320
333
|
</Svg>
|
|
334
|
+
{:else}
|
|
335
|
+
<slot tooltip={$tooltip} />
|
|
321
336
|
{/if}
|
|
322
337
|
|
|
323
338
|
{#if mode === 'quadtree' && debug}
|
|
324
|
-
<Svg>
|
|
339
|
+
<Svg pointerEvents={false}>
|
|
325
340
|
<ChartClipPath>
|
|
326
341
|
<g class="tooltip-quadtree">
|
|
327
342
|
{#each quadtreeRects(quadtree, false) as rect}
|
|
@@ -31,6 +31,7 @@ function setTransformContext(transform) {
|
|
|
31
31
|
import { motionStore, motionFinishHandler } from '../stores/motionStore.js';
|
|
32
32
|
const { width, height } = getContext('LayerCake');
|
|
33
33
|
export let mode = 'none';
|
|
34
|
+
/** Translate towards point (ex. mouse cursor/center) while zooming in/out */
|
|
34
35
|
export let translateOnScale = false;
|
|
35
36
|
export let spring = undefined;
|
|
36
37
|
export let tweened = undefined;
|
|
@@ -37,7 +37,7 @@ import { motionStore, type MotionOptions } from '../stores/motionStore.js';
|
|
|
37
37
|
declare const __propDef: {
|
|
38
38
|
props: {
|
|
39
39
|
mode?: TransformMode | undefined;
|
|
40
|
-
translateOnScale?: boolean | undefined;
|
|
40
|
+
/** Translate towards point (ex. mouse cursor/center) while zooming in/out */ translateOnScale?: boolean | undefined;
|
|
41
41
|
spring?: boolean | Parameters<typeof motionStore>[1]['spring'];
|
|
42
42
|
tweened?: boolean | Parameters<typeof motionStore>[1]['tweened'];
|
|
43
43
|
processTranslate?: ((x: number, y: number, deltaX: number, deltaY: number, scale: number) => {
|
|
@@ -6,6 +6,7 @@ export { default as Axis } from './Axis.svelte';
|
|
|
6
6
|
export { default as Bar } from './Bar.svelte';
|
|
7
7
|
export { default as Bars } from './Bars.svelte';
|
|
8
8
|
export { default as Blur } from './Blur.svelte';
|
|
9
|
+
export { default as Brush } from './Brush.svelte';
|
|
9
10
|
export { default as Bounds } from './Bounds.svelte';
|
|
10
11
|
export { default as Calendar } from './Calendar.svelte';
|
|
11
12
|
export { default as Canvas } from './layout/Canvas.svelte';
|
package/dist/components/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { default as Axis } from './Axis.svelte';
|
|
|
7
7
|
export { default as Bar } from './Bar.svelte';
|
|
8
8
|
export { default as Bars } from './Bars.svelte';
|
|
9
9
|
export { default as Blur } from './Blur.svelte';
|
|
10
|
+
export { default as Brush } from './Brush.svelte';
|
|
10
11
|
export { default as Bounds } from './Bounds.svelte';
|
|
11
12
|
export { default as Calendar } from './Calendar.svelte';
|
|
12
13
|
export { default as Canvas } from './layout/Canvas.svelte';
|
package/dist/utils/genData.d.ts
CHANGED
|
@@ -8,6 +8,12 @@ export declare function getRandomNumber(min: number, max: number): number;
|
|
|
8
8
|
* see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_integer_between_two_values_inclusive
|
|
9
9
|
*/
|
|
10
10
|
export declare function getRandomInteger(min: number, max: number, includeMax?: boolean): number;
|
|
11
|
+
/**
|
|
12
|
+
* @see: https://observablehq.com/@d3/d3-cumsum
|
|
13
|
+
*/
|
|
14
|
+
export declare function randomWalk(options?: {
|
|
15
|
+
count?: number;
|
|
16
|
+
}): number[];
|
|
11
17
|
export declare function createSeries(options: {
|
|
12
18
|
count?: number;
|
|
13
19
|
min: number;
|
package/dist/utils/genData.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { addMinutes, startOfDay, startOfToday, subDays } from 'date-fns';
|
|
2
|
+
import { cumsum } from 'd3-array';
|
|
3
|
+
import { randomNormal } from 'd3-random';
|
|
2
4
|
import { degreesToRadians, radiansToDegrees } from './math.js';
|
|
3
5
|
/**
|
|
4
6
|
* Get random number between min (inclusive) and max (exclusive)
|
|
@@ -16,6 +18,13 @@ export function getRandomInteger(min, max, includeMax = true) {
|
|
|
16
18
|
max = Math.floor(max);
|
|
17
19
|
return Math.floor(Math.random() * (max - min + (includeMax ? 1 : 0)) + min);
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* @see: https://observablehq.com/@d3/d3-cumsum
|
|
23
|
+
*/
|
|
24
|
+
export function randomWalk(options) {
|
|
25
|
+
const random = randomNormal();
|
|
26
|
+
return Array.from(cumsum({ length: options?.count ?? 100 }, random));
|
|
27
|
+
}
|
|
19
28
|
export function createSeries(options) {
|
|
20
29
|
const count = options.count ?? 10;
|
|
21
30
|
const min = options.min;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"author": "Sean Lynch <techniq35@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "techniq/layerchart",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.40.1",
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@changesets/cli": "^2.27.5",
|
|
10
10
|
"@mdi/js": "^7.4.47",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"typescript": "^5.4.5",
|
|
59
59
|
"unist-util-visit": "^5.0.0",
|
|
60
60
|
"us-atlas": "^3.0.1",
|
|
61
|
-
"vite": "^5.2.
|
|
61
|
+
"vite": "^5.2.13"
|
|
62
62
|
},
|
|
63
63
|
"type": "module",
|
|
64
64
|
"dependencies": {
|
|
@@ -85,9 +85,9 @@
|
|
|
85
85
|
"date-fns": "^3.6.0",
|
|
86
86
|
"layercake": "^8.3.0",
|
|
87
87
|
"lodash-es": "^4.17.21",
|
|
88
|
-
"posthog-js": "^1.138.
|
|
88
|
+
"posthog-js": "^1.138.3",
|
|
89
89
|
"shapefile": "^0.6.6",
|
|
90
|
-
"svelte-ux": "^0.66.
|
|
90
|
+
"svelte-ux": "^0.66.6",
|
|
91
91
|
"topojson-client": "^3.1.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|