@fundar/data-chart-telling 0.0.13 → 0.0.15
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/README.md +121 -17
- package/dist/charts/{Chart.svelte.d.ts → BaseChart.svelte.d.ts} +3 -3
- package/dist/charts/bar/Chart.svelte +5 -5
- package/dist/charts/bar/Chart.svelte.d.ts +1 -2
- package/dist/charts/heatmap/Chart.svelte +8 -7
- package/dist/charts/heatmap/Chart.svelte.d.ts +2 -3
- package/dist/charts/line/Chart.svelte +5 -5
- package/dist/charts/line/Chart.svelte.d.ts +1 -2
- package/dist/charts/pyramid/Chart.svelte +7 -6
- package/dist/charts/pyramid/Chart.svelte.d.ts +2 -3
- package/dist/configuration/config.svelte.js +1 -1
- package/dist/configuration/themes/index.d.ts +56 -40
- package/dist/index.d.ts +26 -12
- package/dist/index.js +12 -7
- package/dist/layout/facet/FacetLayout.svelte +1 -1
- package/dist/layout/facet/FacetLayout.svelte.d.ts +1 -1
- package/dist/layout/geo/GeoLayout.svelte +47 -0
- package/dist/layout/geo/GeoLayout.svelte.d.ts +22 -0
- package/dist/layout/geo/resolveProjection.d.ts +10 -0
- package/dist/layout/geo/resolveProjection.js +48 -0
- package/dist/layout/legend/DiscreteSection.svelte +1 -1
- package/dist/layout/plot/AxisLayout.svelte +95 -0
- package/dist/layout/plot/AxisLayout.svelte.d.ts +39 -0
- package/dist/layout/plot/BasePlotLayout.svelte +302 -0
- package/dist/layout/plot/BasePlotLayout.svelte.d.ts +95 -0
- package/dist/layout/plot/GridLayout.svelte +30 -0
- package/dist/layout/plot/GridLayout.svelte.d.ts +27 -0
- package/dist/layout/plot/RuleLayout.svelte +111 -0
- package/dist/layout/plot/RuleLayout.svelte.d.ts +40 -0
- package/dist/layout/plot/margins.d.ts +34 -0
- package/dist/layout/plot/margins.js +26 -0
- package/dist/layout/timeline/TimelineLayout.svelte +1 -1
- package/dist/layout/timeline/TimelineLayout.svelte.d.ts +1 -1
- package/dist/layout/tooltip/Tooltip.svelte +15 -0
- package/dist/layout/tooltip/Tooltip.svelte.d.ts +7 -0
- package/dist/layout/tooltip/TooltipLayout.svelte +45 -0
- package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +41 -0
- package/dist/layout/tooltip/controller.svelte.d.ts +4 -3
- package/dist/layout/tooltip/controller.svelte.js +5 -6
- package/dist/layout/tooltip/hover.svelte.d.ts +1 -1
- package/dist/layout/tooltip/hover.svelte.js +1 -1
- package/dist/layout/tooltip/utils.d.ts +2 -2
- package/dist/markers/ContourClipMarker.svelte +77 -0
- package/dist/markers/ContourClipMarker.svelte.d.ts +10 -0
- package/dist/{plots/markers → markers}/DeltaMarker.svelte +4 -4
- package/dist/{plots/markers → markers}/DeltaMarker.svelte.d.ts +3 -3
- package/dist/{plots/markers → markers}/DotMarker.svelte +3 -3
- package/dist/{plots/markers → markers}/DotMarker.svelte.d.ts +3 -3
- package/dist/markers/HoverMarker.svelte +136 -0
- package/dist/{plots/markers → markers}/HoverMarker.svelte.d.ts +4 -4
- package/dist/{plots/markers → markers}/PeakMarker.svelte +2 -2
- package/dist/{plots/markers → markers}/PeakMarker.svelte.d.ts +2 -2
- package/dist/{plots/markers → markers}/TextMarker.svelte +11 -4
- package/dist/{plots/markers → markers}/TextMarker.svelte.d.ts +3 -3
- package/dist/markers/inset/InsetMarker.svelte +170 -0
- package/dist/markers/inset/InsetMarker.svelte.d.ts +51 -0
- package/dist/markers/inset/insetLayout.d.ts +25 -0
- package/dist/markers/inset/insetLayout.js +39 -0
- package/dist/markers/inset/insetProjection.d.ts +8 -0
- package/dist/markers/inset/insetProjection.js +39 -0
- package/dist/plots/bar/BarSegments.svelte +66 -0
- package/dist/plots/bar/BarSegments.svelte.d.ts +36 -0
- package/dist/plots/bar/Plot.svelte +132 -76
- package/dist/plots/bar/Plot.svelte.d.ts +7 -3
- package/dist/plots/bar/ValueLabels.svelte +89 -0
- package/dist/plots/bar/ValueLabels.svelte.d.ts +39 -0
- package/dist/plots/bar/hoverPoints.d.ts +15 -0
- package/dist/plots/bar/hoverPoints.js +35 -0
- package/dist/plots/bar/layout.svelte.d.ts +37 -0
- package/dist/plots/bar/layout.svelte.js +132 -0
- package/dist/plots/geo/Plot.svelte +548 -0
- package/dist/plots/geo/Plot.svelte.d.ts +29 -0
- package/dist/plots/geo/TileLayer.svelte +77 -0
- package/dist/plots/geo/TileLayer.svelte.d.ts +11 -0
- package/dist/plots/geo/projections/argentina.d.ts +8 -0
- package/dist/plots/geo/projections/argentina.js +35 -0
- package/dist/plots/geo/projections/fit.d.ts +8 -0
- package/dist/plots/geo/projections/fit.js +14 -0
- package/dist/plots/geo/rotate.svelte.d.ts +26 -0
- package/dist/plots/geo/rotate.svelte.js +79 -0
- package/dist/plots/geo/zoom.svelte.d.ts +32 -0
- package/dist/plots/geo/zoom.svelte.js +50 -0
- package/dist/plots/heatmap/Plot.svelte +129 -47
- package/dist/plots/heatmap/Plot.svelte.d.ts +7 -3
- package/dist/plots/line/Plot.svelte +44 -16
- package/dist/plots/line/Plot.svelte.d.ts +7 -3
- package/dist/plots/pyramid/Plot.svelte +70 -25
- package/dist/plots/pyramid/Plot.svelte.d.ts +7 -3
- package/dist/plots/utils/delta.d.ts +1 -1
- package/dist/plots/utils/geoAccessors.d.ts +64 -0
- package/dist/plots/utils/geoAccessors.js +188 -0
- package/dist/plots/utils/geoSegments.d.ts +14 -0
- package/dist/plots/utils/geoSegments.js +17 -0
- package/dist/plots/utils/segments.d.ts +14 -5
- package/dist/plots/utils/segments.js +25 -13
- package/dist/plots/utils/tiles.d.ts +89 -0
- package/dist/plots/utils/tiles.js +159 -0
- package/dist/plots/utils/topojson.d.ts +9 -0
- package/dist/plots/utils/topojson.js +30 -0
- package/dist/types/charts/common.d.ts +2 -1
- package/dist/types/charts/legend.d.ts +1 -1
- package/dist/types/charts/props.d.ts +9 -6
- package/dist/types/configuration/styling.d.ts +11 -5
- package/dist/types/layout/tooltip.d.ts +14 -21
- package/dist/types/{plots/markers.d.ts → markers/common.d.ts} +2 -18
- package/dist/types/markers/geo.d.ts +237 -0
- package/dist/types/markers/props.d.ts +11 -0
- package/dist/types/plots/axis.d.ts +55 -0
- package/dist/types/plots/axis.js +1 -0
- package/dist/types/plots/constants.d.ts +30 -0
- package/dist/types/plots/constants.js +1 -0
- package/dist/types/plots/data/common.d.ts +40 -0
- package/dist/types/plots/data/common.js +1 -0
- package/dist/types/plots/data/geo.d.ts +74 -0
- package/dist/types/plots/data/geo.js +1 -0
- package/dist/types/plots/delta.d.ts +2 -2
- package/dist/types/plots/props.d.ts +105 -16
- package/dist/types/plots/scales/geo.d.ts +44 -0
- package/dist/types/plots/scales/geo.js +1 -0
- package/dist/types/plots/segments/common.d.ts +25 -0
- package/dist/types/plots/segments/common.js +1 -0
- package/dist/types/plots/segments/config.d.ts +51 -0
- package/dist/types/plots/segments/config.js +1 -0
- package/dist/types/plots/styles/common.d.ts +37 -0
- package/dist/types/plots/styles/common.js +1 -0
- package/dist/types/plots/styles/geo.d.ts +42 -0
- package/dist/types/plots/styles/geo.js +1 -0
- package/dist/types/plots/styling.d.ts +45 -0
- package/dist/types/plots/styling.js +1 -0
- package/dist/utils/grouping.d.ts +2 -1
- package/dist/utils/interpolate.d.ts +2 -1
- package/package.json +27 -13
- package/dist/layout/plot/PlotLayout.svelte +0 -237
- package/dist/layout/plot/PlotLayout.svelte.d.ts +0 -70
- package/dist/plots/markers/HoverMarker.svelte +0 -98
- package/dist/types/plots/common.d.ts +0 -100
- package/dist/types/plots/styles.d.ts +0 -187
- /package/dist/charts/{Chart.svelte → BaseChart.svelte} +0 -0
- /package/dist/types/{plots → markers}/common.js +0 -0
- /package/dist/types/{plots/markers.js → markers/geo.js} +0 -0
- /package/dist/types/{plots/styles.js → markers/props.js} +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<script lang="ts" generics="TProps extends Record<string, unknown>">
|
|
2
|
+
import { geoPath } from 'd3-geo';
|
|
3
|
+
import { buildInsetProjection } from './insetProjection';
|
|
4
|
+
import { insetBox } from './insetLayout';
|
|
5
|
+
import { filterGeometryParts } from '../../plots/utils/geoAccessors';
|
|
6
|
+
import TooltipLayout from '../../layout/tooltip/TooltipLayout.svelte';
|
|
7
|
+
import type { GeoInsetMarker } from '../../types/markers/geo';
|
|
8
|
+
import type { GeoFeature } from '../../types/plots/data/geo';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A small, independently-projected map panel anchored to a corner/edge of
|
|
12
|
+
* the plot — see `GeoInsetMarker`'s doc comment. Renders raw `<path>`s via
|
|
13
|
+
* its own `d3-geo` `geoPath`, deliberately not svelteplot's `<Geo>` mark:
|
|
14
|
+
* that mark is bound to the plot's *shared* projection scale, but an inset
|
|
15
|
+
* needs its own, independently fit to just what it's showing.
|
|
16
|
+
*/
|
|
17
|
+
let {
|
|
18
|
+
marker,
|
|
19
|
+
features,
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
left = 0,
|
|
23
|
+
top = 0,
|
|
24
|
+
defaultFill,
|
|
25
|
+
}: {
|
|
26
|
+
marker: GeoInsetMarker<TProps>;
|
|
27
|
+
/**
|
|
28
|
+
* The plot's own already-tagged features (`__id` set), with full,
|
|
29
|
+
* unreduced geometry regardless of the plot's own `excludeParts` — used
|
|
30
|
+
* when `marker.data` is omitted, so an inset can show a part the main
|
|
31
|
+
* map excludes without needing a separate dataset.
|
|
32
|
+
*/
|
|
33
|
+
features: (GeoFeature<TProps> & { __id: string })[];
|
|
34
|
+
/**
|
|
35
|
+
* The plot's own *content* width/height — i.e. already net of margins,
|
|
36
|
+
* matching the frame the map itself is actually drawn into (svelteplot
|
|
37
|
+
* bakes margins into the projection's scale/translate rather than
|
|
38
|
+
* wrapping marks in a translating `<g>`, so this needs to match, not the
|
|
39
|
+
* plot's raw outer width/height) — `location`'s edge and `margin`'s gap
|
|
40
|
+
* are both measured against this box, not the full canvas.
|
|
41
|
+
*/
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
/** Top-left corner of that content box, in the plot's own absolute coordinate space (i.e. its margins). */
|
|
45
|
+
left?: number;
|
|
46
|
+
top?: number;
|
|
47
|
+
defaultFill: string;
|
|
48
|
+
} = $props();
|
|
49
|
+
|
|
50
|
+
const insetFeatures = $derived.by((): GeoFeature<Record<string, unknown>>[] => {
|
|
51
|
+
if (marker.data) {
|
|
52
|
+
return (
|
|
53
|
+
Array.isArray(marker.data) ? marker.data : marker.data.features
|
|
54
|
+
) as GeoFeature<Record<string, unknown>>[];
|
|
55
|
+
}
|
|
56
|
+
if (marker.features?.length) {
|
|
57
|
+
const wanted = new Set(marker.features);
|
|
58
|
+
const matched = features.filter((f) => wanted.has(f.__id));
|
|
59
|
+
if (!marker.featurePartsFilter) return matched;
|
|
60
|
+
const partsFilter = marker.featurePartsFilter;
|
|
61
|
+
return matched.flatMap((f) => {
|
|
62
|
+
const geometry = filterGeometryParts(f.geometry, (part) => partsFilter(part, f));
|
|
63
|
+
return geometry ? [{ ...f, geometry }] : [];
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const box = $derived(
|
|
70
|
+
insetBox(marker.location, marker.width ?? 0.26, marker.height ?? 0.22, marker.margin ?? 8, width, height),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const path = $derived.by(() => {
|
|
74
|
+
if (insetFeatures.length === 0 || box.width <= 0 || box.height <= 0) return null;
|
|
75
|
+
const projection = buildInsetProjection(marker.projection);
|
|
76
|
+
const collection: GeoJSON.FeatureCollection = { type: 'FeatureCollection', features: insetFeatures };
|
|
77
|
+
projection.fitExtent(
|
|
78
|
+
[
|
|
79
|
+
[0, 0],
|
|
80
|
+
[box.width, box.height],
|
|
81
|
+
],
|
|
82
|
+
collection,
|
|
83
|
+
);
|
|
84
|
+
return geoPath(projection);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Nearest-feature hover, local to this inset only — mirrors the main
|
|
89
|
+
* map's own "always nearest feature centroid" behaviour (see `Plot.svelte`),
|
|
90
|
+
* but computed against this inset's own fitted `path`/projection instead
|
|
91
|
+
* of the plot's shared one, since svelteplot's `<Pointer>` (which drives
|
|
92
|
+
* the main map's hover) only ever matches the main projection's own
|
|
93
|
+
* feature positions — it has no way to reach content an inset draws at a
|
|
94
|
+
* wholly different scale/location.
|
|
95
|
+
*/
|
|
96
|
+
const centroids = $derived.by(() => {
|
|
97
|
+
if (!path) return [];
|
|
98
|
+
return insetFeatures.map((f) => ({ feature: f, centroid: path.centroid(f as GeoJSON.Feature) }));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
let hovered = $state<{ feature: GeoFeature<Record<string, unknown>>; x: number; y: number } | null>(null);
|
|
102
|
+
|
|
103
|
+
// Any point within the inset box always matches its nearest feature —
|
|
104
|
+
// there's no reason for a hover inside a small, self-contained panel to
|
|
105
|
+
// ever come up empty, so the threshold is just the box's own diagonal.
|
|
106
|
+
const maxDistance = $derived(Math.hypot(box.width, box.height));
|
|
107
|
+
|
|
108
|
+
function onInsetPointerMove(e: PointerEvent) {
|
|
109
|
+
const rect = (e.currentTarget as SVGRectElement).getBoundingClientRect();
|
|
110
|
+
const x = e.clientX - rect.left;
|
|
111
|
+
const y = e.clientY - rect.top;
|
|
112
|
+
let nearest: (typeof centroids)[number] | null = null;
|
|
113
|
+
let nearestDist = Infinity;
|
|
114
|
+
for (const c of centroids) {
|
|
115
|
+
const d = Math.hypot(c.centroid[0] - x, c.centroid[1] - y);
|
|
116
|
+
if (d < nearestDist) {
|
|
117
|
+
nearestDist = d;
|
|
118
|
+
nearest = c;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
hovered = nearest && nearestDist <= maxDistance ? { feature: nearest.feature, x, y } : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function onInsetPointerLeave() {
|
|
125
|
+
hovered = null;
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
{#if path}
|
|
130
|
+
<g class="dct-geo-inset" transform="translate({box.left + left}, {box.top + top})">
|
|
131
|
+
{#each insetFeatures as f, i (i)}
|
|
132
|
+
<path
|
|
133
|
+
d={path(f as GeoJSON.Feature) ?? undefined}
|
|
134
|
+
fill={marker.style?.fill ?? defaultFill}
|
|
135
|
+
fill-opacity={marker.style?.fillOpacity}
|
|
136
|
+
stroke={marker.style?.stroke ?? 'currentColor'}
|
|
137
|
+
stroke-width={marker.style?.strokeWidth ?? 0.5}
|
|
138
|
+
style="vector-effect: non-scaling-stroke"
|
|
139
|
+
/>
|
|
140
|
+
{/each}
|
|
141
|
+
{#if marker.tooltip}
|
|
142
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
143
|
+
<rect
|
|
144
|
+
x={0}
|
|
145
|
+
y={0}
|
|
146
|
+
width={box.width}
|
|
147
|
+
height={box.height}
|
|
148
|
+
fill="transparent"
|
|
149
|
+
onpointermove={onInsetPointerMove}
|
|
150
|
+
onpointerleave={onInsetPointerLeave}
|
|
151
|
+
/>
|
|
152
|
+
{/if}
|
|
153
|
+
{#if marker.tooltip}
|
|
154
|
+
{@const tooltip = marker.tooltip}
|
|
155
|
+
<foreignObject x={0} y={0} width={box.width} height={box.height} style="overflow: visible; pointer-events: none;">
|
|
156
|
+
<div xmlns="http://www.w3.org/1999/xhtml" style="position: relative; width: 100%; height: 100%;">
|
|
157
|
+
<TooltipLayout
|
|
158
|
+
visible={hovered != null}
|
|
159
|
+
x={hovered?.x ?? 0}
|
|
160
|
+
y={hovered?.y ?? 0}
|
|
161
|
+
bounds={{ width: box.width, height: box.height }}
|
|
162
|
+
rows={hovered ? [hovered.feature] : []}
|
|
163
|
+
format={tooltip.format}
|
|
164
|
+
content={tooltip.content}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
</foreignObject>
|
|
168
|
+
{/if}
|
|
169
|
+
</g>
|
|
170
|
+
{/if}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { GeoInsetMarker } from '../../types/markers/geo';
|
|
2
|
+
import type { GeoFeature } from '../../types/plots/data/geo';
|
|
3
|
+
declare function $$render<TProps extends Record<string, unknown>>(): {
|
|
4
|
+
props: {
|
|
5
|
+
marker: GeoInsetMarker<TProps>;
|
|
6
|
+
/**
|
|
7
|
+
* The plot's own already-tagged features (`__id` set), with full,
|
|
8
|
+
* unreduced geometry regardless of the plot's own `excludeParts` — used
|
|
9
|
+
* when `marker.data` is omitted, so an inset can show a part the main
|
|
10
|
+
* map excludes without needing a separate dataset.
|
|
11
|
+
*/
|
|
12
|
+
features: (GeoFeature<TProps> & {
|
|
13
|
+
__id: string;
|
|
14
|
+
})[];
|
|
15
|
+
/**
|
|
16
|
+
* The plot's own *content* width/height — i.e. already net of margins,
|
|
17
|
+
* matching the frame the map itself is actually drawn into (svelteplot
|
|
18
|
+
* bakes margins into the projection's scale/translate rather than
|
|
19
|
+
* wrapping marks in a translating `<g>`, so this needs to match, not the
|
|
20
|
+
* plot's raw outer width/height) — `location`'s edge and `margin`'s gap
|
|
21
|
+
* are both measured against this box, not the full canvas.
|
|
22
|
+
*/
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
/** Top-left corner of that content box, in the plot's own absolute coordinate space (i.e. its margins). */
|
|
26
|
+
left?: number;
|
|
27
|
+
top?: number;
|
|
28
|
+
defaultFill: string;
|
|
29
|
+
};
|
|
30
|
+
exports: {};
|
|
31
|
+
bindings: "";
|
|
32
|
+
slots: {};
|
|
33
|
+
events: {};
|
|
34
|
+
};
|
|
35
|
+
declare class __sveltets_Render<TProps extends Record<string, unknown>> {
|
|
36
|
+
props(): ReturnType<typeof $$render<TProps>>['props'];
|
|
37
|
+
events(): ReturnType<typeof $$render<TProps>>['events'];
|
|
38
|
+
slots(): ReturnType<typeof $$render<TProps>>['slots'];
|
|
39
|
+
bindings(): "";
|
|
40
|
+
exports(): {};
|
|
41
|
+
}
|
|
42
|
+
interface $$IsomorphicComponent {
|
|
43
|
+
new <TProps extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TProps>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TProps>['props']>, ReturnType<__sveltets_Render<TProps>['events']>, ReturnType<__sveltets_Render<TProps>['slots']>> & {
|
|
44
|
+
$$bindings?: ReturnType<__sveltets_Render<TProps>['bindings']>;
|
|
45
|
+
} & ReturnType<__sveltets_Render<TProps>['exports']>;
|
|
46
|
+
<TProps extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TProps>['props']> & {}): ReturnType<__sveltets_Render<TProps>['exports']>;
|
|
47
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
48
|
+
}
|
|
49
|
+
declare const InsetMarker: $$IsomorphicComponent;
|
|
50
|
+
type InsetMarker<TProps extends Record<string, unknown>> = InstanceType<typeof InsetMarker<TProps>>;
|
|
51
|
+
export default InsetMarker;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { GeoInsetLocation } from '../../types/markers/geo';
|
|
2
|
+
type Horizontal = 'left' | 'middle' | 'right';
|
|
3
|
+
type Vertical = 'top' | 'middle' | 'bottom';
|
|
4
|
+
/**
|
|
5
|
+
* Parses a `side-anchor` position string (either word order accepted — see
|
|
6
|
+
* `GeoInsetLocation`) into independent horizontal/vertical placement
|
|
7
|
+
* keywords.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseInsetLocation(location: GeoInsetLocation): {
|
|
10
|
+
h: Horizontal;
|
|
11
|
+
v: Vertical;
|
|
12
|
+
};
|
|
13
|
+
export type InsetBox = {
|
|
14
|
+
left: number;
|
|
15
|
+
top: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Computes an inset's pixel rectangle within a `frameWidth`×`frameHeight`
|
|
21
|
+
* area, anchored per `location` and `margin` pixels from whichever edge(s)
|
|
22
|
+
* it's anchored to.
|
|
23
|
+
*/
|
|
24
|
+
export declare function insetBox(location: GeoInsetLocation, widthFraction: number, heightFraction: number, margin: number, frameWidth: number, frameHeight: number): InsetBox;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a `side-anchor` position string (either word order accepted — see
|
|
3
|
+
* `GeoInsetLocation`) into independent horizontal/vertical placement
|
|
4
|
+
* keywords.
|
|
5
|
+
*/
|
|
6
|
+
export function parseInsetLocation(location) {
|
|
7
|
+
const parts = location.split('-');
|
|
8
|
+
let h;
|
|
9
|
+
let v;
|
|
10
|
+
for (const part of parts) {
|
|
11
|
+
if (part === 'left' || part === 'right')
|
|
12
|
+
h = part;
|
|
13
|
+
else if (part === 'top' || part === 'bottom')
|
|
14
|
+
v = part;
|
|
15
|
+
}
|
|
16
|
+
// Any leftover 'middle' fills whichever axis wasn't claimed above.
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
if (part !== 'middle')
|
|
19
|
+
continue;
|
|
20
|
+
if (v === undefined)
|
|
21
|
+
v = 'middle';
|
|
22
|
+
else if (h === undefined)
|
|
23
|
+
h = 'middle';
|
|
24
|
+
}
|
|
25
|
+
return { h: h ?? 'middle', v: v ?? 'middle' };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Computes an inset's pixel rectangle within a `frameWidth`×`frameHeight`
|
|
29
|
+
* area, anchored per `location` and `margin` pixels from whichever edge(s)
|
|
30
|
+
* it's anchored to.
|
|
31
|
+
*/
|
|
32
|
+
export function insetBox(location, widthFraction, heightFraction, margin, frameWidth, frameHeight) {
|
|
33
|
+
const { h, v } = parseInsetLocation(location);
|
|
34
|
+
const width = frameWidth * widthFraction;
|
|
35
|
+
const height = frameHeight * heightFraction;
|
|
36
|
+
const left = h === 'left' ? margin : h === 'right' ? frameWidth - margin - width : (frameWidth - width) / 2;
|
|
37
|
+
const top = v === 'top' ? margin : v === 'bottom' ? frameHeight - margin - height : (frameHeight - height) / 2;
|
|
38
|
+
return { left, top, width, height };
|
|
39
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type GeoProjection } from 'd3-geo';
|
|
2
|
+
import type { GeoInsetProjectionConfig } from '../../types/markers/geo';
|
|
3
|
+
/**
|
|
4
|
+
* Builds a fresh, configured (but not yet fit) d3-geo projection instance
|
|
5
|
+
* for an `inset` marker — the caller still needs to `.fitExtent()` it to
|
|
6
|
+
* whatever the inset ends up showing.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildInsetProjection(config?: GeoInsetProjectionConfig): GeoProjection;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { geoAlbersUsa, geoAlbers, geoAzimuthalEqualArea, geoAzimuthalEquidistant, geoConicConformal, geoConicEqualArea, geoConicEquidistant, geoEqualEarth, geoEquirectangular, geoGnomonic, geoMercator, geoOrthographic, geoStereographic, geoTransverseMercator, } from 'd3-geo';
|
|
2
|
+
const NAMED_PROJECTIONS = {
|
|
3
|
+
'albers-usa': geoAlbersUsa,
|
|
4
|
+
albers: geoAlbers,
|
|
5
|
+
'azimuthal-equal-area': geoAzimuthalEqualArea,
|
|
6
|
+
'azimuthal-equidistant': geoAzimuthalEquidistant,
|
|
7
|
+
'conic-conformal': geoConicConformal,
|
|
8
|
+
'conic-equal-area': geoConicEqualArea,
|
|
9
|
+
'conic-equidistant': geoConicEquidistant,
|
|
10
|
+
'equal-earth': geoEqualEarth,
|
|
11
|
+
equirectangular: geoEquirectangular,
|
|
12
|
+
gnomonic: geoGnomonic,
|
|
13
|
+
mercator: geoMercator,
|
|
14
|
+
orthographic: geoOrthographic,
|
|
15
|
+
stereographic: geoStereographic,
|
|
16
|
+
'transverse-mercator': geoTransverseMercator,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Builds a fresh, configured (but not yet fit) d3-geo projection instance
|
|
20
|
+
* for an `inset` marker — the caller still needs to `.fitExtent()` it to
|
|
21
|
+
* whatever the inset ends up showing.
|
|
22
|
+
*/
|
|
23
|
+
export function buildInsetProjection(config = 'mercator') {
|
|
24
|
+
const isConfigObject = typeof config === 'object' && config !== null;
|
|
25
|
+
const name = (isConfigObject ? config.type : config) ?? 'mercator';
|
|
26
|
+
const projection = NAMED_PROJECTIONS[name]();
|
|
27
|
+
if (isConfigObject) {
|
|
28
|
+
if (config.rotate)
|
|
29
|
+
projection.rotate(config.rotate);
|
|
30
|
+
if (config.center)
|
|
31
|
+
projection.center(config.center);
|
|
32
|
+
if (config.parallels && 'parallels' in projection) {
|
|
33
|
+
projection.parallels(config.parallels);
|
|
34
|
+
}
|
|
35
|
+
if (typeof config.clip === 'number')
|
|
36
|
+
projection.clipAngle(config.clip);
|
|
37
|
+
}
|
|
38
|
+
return projection;
|
|
39
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
+
import { BarX, BarY } from 'svelteplot';
|
|
3
|
+
import { resolveAccessor } from '../utils/accessors';
|
|
4
|
+
import type { Series } from '../../types/plots/data/common';
|
|
5
|
+
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
6
|
+
import type { VisualGroup } from '../../types/plots/segments/config';
|
|
7
|
+
import type { BarLayout } from './layout.svelte';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders one series' bars — the `'stacked'` branch (cumulative baseline
|
|
11
|
+
* from {@link BarLayout.stackedBaseline}) or the `'grouped'`/`'overlap'`
|
|
12
|
+
* branch (per-segment insets from {@link BarLayout.seriesStep}/`seriesGap`).
|
|
13
|
+
*/
|
|
14
|
+
let {
|
|
15
|
+
group,
|
|
16
|
+
seriesIndex,
|
|
17
|
+
barLayout,
|
|
18
|
+
isHorizontal,
|
|
19
|
+
BarMark,
|
|
20
|
+
defaultColor,
|
|
21
|
+
}: {
|
|
22
|
+
group: VisualGroup<Series<TData>, BarSegmentStyle>;
|
|
23
|
+
seriesIndex: number;
|
|
24
|
+
barLayout: BarLayout<TData>;
|
|
25
|
+
isHorizontal: boolean;
|
|
26
|
+
BarMark: typeof BarX | typeof BarY;
|
|
27
|
+
defaultColor: string;
|
|
28
|
+
} = $props();
|
|
29
|
+
|
|
30
|
+
const xFn = $derived(resolveAccessor(group.series.x));
|
|
31
|
+
const yFn = $derived(resolveAccessor(group.series.y));
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#if barLayout.layout === 'stacked'}
|
|
35
|
+
{@const stackedBaselineFn = (d: TData) => barLayout.stackedBaseline(xFn(d), seriesIndex)}
|
|
36
|
+
{@const stackedTopFn = (d: TData) => barLayout.stackedBaseline(xFn(d), seriesIndex) + Number(yFn(d))}
|
|
37
|
+
<BarMark
|
|
38
|
+
data={group.series.data}
|
|
39
|
+
{...isHorizontal
|
|
40
|
+
? { y: xFn, x1: stackedBaselineFn, x2: stackedTopFn }
|
|
41
|
+
: { x: xFn, y1: stackedBaselineFn, y2: stackedTopFn }}
|
|
42
|
+
fill={defaultColor}
|
|
43
|
+
/>
|
|
44
|
+
{:else}
|
|
45
|
+
{#each group.visualSegments as seg, i (`${group.series.name}-${i}`)}
|
|
46
|
+
<BarMark
|
|
47
|
+
data={seg.data}
|
|
48
|
+
{...isHorizontal
|
|
49
|
+
? { y: xFn, x1: barLayout.valueBaseline, x2: yFn }
|
|
50
|
+
: { x: xFn, y1: barLayout.valueBaseline, y2: yFn }}
|
|
51
|
+
fill={seg.style.fill ?? defaultColor}
|
|
52
|
+
fillOpacity={seg.style.fillOpacity ?? 1}
|
|
53
|
+
{...barLayout.layout === 'grouped'
|
|
54
|
+
? isHorizontal
|
|
55
|
+
? {
|
|
56
|
+
insetTop: seriesIndex * barLayout.seriesStep + barLayout.seriesGap / 2,
|
|
57
|
+
insetBottom: (barLayout.seriesCount - 1 - seriesIndex) * barLayout.seriesStep + barLayout.seriesGap / 2,
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
insetLeft: seriesIndex * barLayout.seriesStep + barLayout.seriesGap / 2,
|
|
61
|
+
insetRight: (barLayout.seriesCount - 1 - seriesIndex) * barLayout.seriesStep + barLayout.seriesGap / 2,
|
|
62
|
+
}
|
|
63
|
+
: {}}
|
|
64
|
+
/>
|
|
65
|
+
{/each}
|
|
66
|
+
{/if}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BarX, BarY } from 'svelteplot';
|
|
2
|
+
import type { Series } from '../../types/plots/data/common';
|
|
3
|
+
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
4
|
+
import type { VisualGroup } from '../../types/plots/segments/config';
|
|
5
|
+
import type { BarLayout } from './layout.svelte';
|
|
6
|
+
declare function $$render<TData extends Record<string, unknown>>(): {
|
|
7
|
+
props: {
|
|
8
|
+
group: VisualGroup<Series<TData>, BarSegmentStyle>;
|
|
9
|
+
seriesIndex: number;
|
|
10
|
+
barLayout: BarLayout<TData>;
|
|
11
|
+
isHorizontal: boolean;
|
|
12
|
+
BarMark: typeof BarX | typeof BarY;
|
|
13
|
+
defaultColor: string;
|
|
14
|
+
};
|
|
15
|
+
exports: {};
|
|
16
|
+
bindings: "";
|
|
17
|
+
slots: {};
|
|
18
|
+
events: {};
|
|
19
|
+
};
|
|
20
|
+
declare class __sveltets_Render<TData extends Record<string, unknown>> {
|
|
21
|
+
props(): ReturnType<typeof $$render<TData>>['props'];
|
|
22
|
+
events(): ReturnType<typeof $$render<TData>>['events'];
|
|
23
|
+
slots(): ReturnType<typeof $$render<TData>>['slots'];
|
|
24
|
+
bindings(): "";
|
|
25
|
+
exports(): {};
|
|
26
|
+
}
|
|
27
|
+
interface $$IsomorphicComponent {
|
|
28
|
+
new <TData extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TData>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TData>['props']>, ReturnType<__sveltets_Render<TData>['events']>, ReturnType<__sveltets_Render<TData>['slots']>> & {
|
|
29
|
+
$$bindings?: ReturnType<__sveltets_Render<TData>['bindings']>;
|
|
30
|
+
} & ReturnType<__sveltets_Render<TData>['exports']>;
|
|
31
|
+
<TData extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TData>['props']> & {}): ReturnType<__sveltets_Render<TData>['exports']>;
|
|
32
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
33
|
+
}
|
|
34
|
+
declare const BarSegments: $$IsomorphicComponent;
|
|
35
|
+
type BarSegments<TData extends Record<string, unknown>> = InstanceType<typeof BarSegments<TData>>;
|
|
36
|
+
export default BarSegments;
|