@fundar/data-chart-telling 0.0.5 → 0.0.7
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/charts/Chart.svelte +1 -0
- package/dist/charts/heatmap/Chart.svelte +1 -1
- package/dist/layout/timeline/TimelineLayout.svelte +18 -13
- package/dist/layout/timeline/TimelineLayout.svelte.d.ts +1 -0
- package/dist/plots/bar/Plot.svelte +1 -1
- package/dist/plots/heatmap/Plot.svelte +1 -1
- package/dist/plots/line/Plot.svelte +1 -1
- package/dist/plots/pyramid/Plot.svelte +1 -1
- package/dist/plots/utils/segments.d.ts +3 -3
- package/dist/plots/utils/segments.js +17 -18
- package/dist/types/charts/common.d.ts +1 -0
- package/dist/types/plots/styles.d.ts +13 -34
- package/package.json +1 -1
package/dist/charts/Chart.svelte
CHANGED
|
@@ -108,6 +108,7 @@
|
|
|
108
108
|
progress={timeline.progress}
|
|
109
109
|
values={timeline.values}
|
|
110
110
|
orientation={timeline.orientation}
|
|
111
|
+
show={timeline.show}
|
|
111
112
|
thickness={timeline.thickness ?? cfg.timeline.thickness}
|
|
112
113
|
filterMode={timeline.filterMode}
|
|
113
114
|
interpolate={typeof timeline.interpolate === 'object' ? timeline.interpolate : undefined}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
progress,
|
|
23
23
|
values,
|
|
24
24
|
orientation = 'vertical',
|
|
25
|
+
show = true,
|
|
25
26
|
thickness = 60,
|
|
26
27
|
filterMode = 'exact',
|
|
27
28
|
interpolate,
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
progress: number;
|
|
35
36
|
values?: TimeValue[];
|
|
36
37
|
orientation?: Orientation;
|
|
38
|
+
show?: boolean;
|
|
37
39
|
thickness?: number;
|
|
38
40
|
filterMode?: 'exact' | 'upTo';
|
|
39
41
|
interpolate?: TimelineInterpolation<TRow>;
|
|
@@ -126,32 +128,37 @@
|
|
|
126
128
|
});
|
|
127
129
|
|
|
128
130
|
const isVertical = $derived(orientation === 'vertical');
|
|
129
|
-
const gap = 12;
|
|
131
|
+
const gap = $derived(show ? 12 : 0);
|
|
132
|
+
const effectiveThickness = $derived(show ? thickness : 0);
|
|
130
133
|
const innerWidth = $derived(
|
|
131
|
-
Math.max(0, isVertical ? width -
|
|
134
|
+
Math.max(0, isVertical ? width - effectiveThickness - gap : width),
|
|
132
135
|
);
|
|
133
136
|
const innerHeight = $derived(
|
|
134
|
-
Math.max(0, isVertical ? height : height -
|
|
137
|
+
Math.max(0, isVertical ? height : height - effectiveThickness - gap),
|
|
135
138
|
);
|
|
136
139
|
</script>
|
|
137
140
|
|
|
138
141
|
{#if isVertical}
|
|
139
|
-
<div class="dct-timeline dct-timeline--vertical" style:width="{width}px" style:height="{height}px">
|
|
142
|
+
<div class="dct-timeline dct-timeline--vertical" style:width="{width}px" style:height="{height}px" style:gap="{gap}px">
|
|
140
143
|
<div class="dct-timeline__plot">
|
|
141
144
|
{@render child({ selectedValue, data: filtered, width: innerWidth, height: innerHeight })}
|
|
142
145
|
</div>
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
+
{#if show}
|
|
147
|
+
<div class="dct-timeline__gutter" style:width="{thickness}px">
|
|
148
|
+
<Timeline currentValue={selectedValue} values={steps} orientation="vertical" width={thickness} height={innerHeight} />
|
|
149
|
+
</div>
|
|
150
|
+
{/if}
|
|
146
151
|
</div>
|
|
147
152
|
{:else}
|
|
148
|
-
<div class="dct-timeline dct-timeline--horizontal" style:width="{width}px" style:height="{height}px">
|
|
153
|
+
<div class="dct-timeline dct-timeline--horizontal" style:width="{width}px" style:height="{height}px" style:gap="{gap}px">
|
|
149
154
|
<div class="dct-timeline__plot">
|
|
150
155
|
{@render child({ selectedValue, data: filtered, width: innerWidth, height: innerHeight })}
|
|
151
156
|
</div>
|
|
152
|
-
|
|
153
|
-
<
|
|
154
|
-
|
|
157
|
+
{#if show}
|
|
158
|
+
<div class="dct-timeline__gutter" style:height="{thickness}px">
|
|
159
|
+
<Timeline currentValue={selectedValue} values={steps} orientation="horizontal" width={innerWidth} height={thickness} />
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
155
162
|
</div>
|
|
156
163
|
{/if}
|
|
157
164
|
|
|
@@ -161,11 +168,9 @@
|
|
|
161
168
|
}
|
|
162
169
|
.dct-timeline--vertical {
|
|
163
170
|
flex-direction: row;
|
|
164
|
-
gap: 12px;
|
|
165
171
|
}
|
|
166
172
|
.dct-timeline--horizontal {
|
|
167
173
|
flex-direction: column;
|
|
168
|
-
gap: 12px;
|
|
169
174
|
}
|
|
170
175
|
.dct-timeline__plot {
|
|
171
176
|
flex: 1 1 auto;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AxisValue, Series } from '../../types/plots/common';
|
|
2
2
|
import type { Segment, Segments, LineSegmentStyle, VisualGroup } from '../../types/plots/styles';
|
|
3
3
|
/**
|
|
4
|
-
* Returns the flat {@link Segment} list for one series
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Returns the flat {@link Segment} list for one series.
|
|
5
|
+
* Series-specific segments are added before `'default'` segments so that
|
|
6
|
+
* series-specific catch-alls win over default catch-alls on tie-breaks.
|
|
7
7
|
*/
|
|
8
8
|
export declare function resolveSegmentsForSeries<TStyle>(segments: Segments<TStyle>, seriesName: string): Segment<TStyle>[];
|
|
9
9
|
/** Returns true when `xVal` falls inside any of a segment's x ranges. */
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import { resolveAccessor } from './accessors';
|
|
2
2
|
const DEFAULT_KEY = 'default';
|
|
3
3
|
/**
|
|
4
|
-
* Returns the flat {@link Segment} list for one series
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Returns the flat {@link Segment} list for one series.
|
|
5
|
+
* Series-specific segments are added before `'default'` segments so that
|
|
6
|
+
* series-specific catch-alls win over default catch-alls on tie-breaks.
|
|
7
7
|
*/
|
|
8
8
|
export function resolveSegmentsForSeries(segments, seriesName) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const defaults = group[DEFAULT_KEY];
|
|
13
|
-
if (specific)
|
|
14
|
-
result.push(...specific);
|
|
15
|
-
if (defaults)
|
|
16
|
-
result.push(...defaults);
|
|
17
|
-
}
|
|
18
|
-
return result;
|
|
9
|
+
const specific = segments[seriesName] ?? [];
|
|
10
|
+
const defaults = segments[DEFAULT_KEY] ?? [];
|
|
11
|
+
return [...specific, ...defaults];
|
|
19
12
|
}
|
|
20
13
|
/** Returns true when `xVal` falls inside any of a segment's x ranges. */
|
|
21
14
|
export function matchesSegmentX(xVal, seg) {
|
|
@@ -71,10 +64,8 @@ export function validateSegments(segments) {
|
|
|
71
64
|
`only the first catch-all's style will be used.`);
|
|
72
65
|
}
|
|
73
66
|
};
|
|
74
|
-
for (const
|
|
75
|
-
|
|
76
|
-
check(key === DEFAULT_KEY ? '(default)' : `"${key}"`, segs);
|
|
77
|
-
}
|
|
67
|
+
for (const [key, segs] of Object.entries(segments)) {
|
|
68
|
+
check(key === DEFAULT_KEY ? '(default)' : `"${key}"`, segs);
|
|
78
69
|
}
|
|
79
70
|
}
|
|
80
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -146,7 +137,15 @@ function resolveSeriesSegments(series, segs) {
|
|
|
146
137
|
defaultVisual.push({ data: bridged, style: {} });
|
|
147
138
|
}
|
|
148
139
|
else {
|
|
149
|
-
|
|
140
|
+
const seg = segs[run.segIdx];
|
|
141
|
+
const segStyle = seg.style ?? {};
|
|
142
|
+
// Area-specific segments merge the catch-all as a baseline: catch-all
|
|
143
|
+
// keys fill in whatever the area segment omits; the area wins on conflict.
|
|
144
|
+
const isAreaSpecific = seg.areas != null && seg.areas.length > 0;
|
|
145
|
+
const resolvedStyle = isAreaSpecific && catchAll?.style
|
|
146
|
+
? { ...catchAll.style, ...segStyle }
|
|
147
|
+
: segStyle;
|
|
148
|
+
userVisual.push({ data: run.data, style: resolvedStyle });
|
|
150
149
|
// When the next run is also a user segment (no unmatched data between them),
|
|
151
150
|
// insert a line-only connector so the lines join without a gap. Using a
|
|
152
151
|
// separate mark (rather than appending a bridge point to the current run)
|
|
@@ -52,6 +52,7 @@ export type TimelineConfig<TRow extends Record<string, unknown>> = {
|
|
|
52
52
|
progress: number;
|
|
53
53
|
values?: TimeValue[];
|
|
54
54
|
orientation?: Orientation;
|
|
55
|
+
show?: boolean;
|
|
55
56
|
thickness?: number;
|
|
56
57
|
filterMode?: 'exact' | 'upTo';
|
|
57
58
|
interpolate?: boolean | TimelineInterpolation<TRow>;
|
|
@@ -146,50 +146,29 @@ export type Segment<TStyle> = {
|
|
|
146
146
|
areas?: Area[];
|
|
147
147
|
style?: TStyle;
|
|
148
148
|
};
|
|
149
|
-
/** Per-series list of {@link Segment}s, keyed by series name. */
|
|
150
|
-
export type SeriesAwareSegmentsMap<TStyle> = Record<string, Segment<TStyle>[]>;
|
|
151
|
-
/**
|
|
152
|
-
* A segment group that applies its segments to **every** series. Use the
|
|
153
|
-
* `'default'` key to target all series at once.
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* ```ts
|
|
157
|
-
* const group: DefaultSegmentsMap<LineSegmentStyle> = {
|
|
158
|
-
* default: [{ areas: [{ x: { from: 2012, to: 2014 } }], style: { dots: { dotSymbol: 'circle' } } }],
|
|
159
|
-
* };
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
export type DefaultSegmentsMap<TStyle> = {
|
|
163
|
-
default: Segment<TStyle>[];
|
|
164
|
-
};
|
|
165
|
-
/**
|
|
166
|
-
* One element of a {@link Segments} array. Either a {@link DefaultSegmentsMap}
|
|
167
|
-
* (applies its segments to every series via the `'default'` key) or a
|
|
168
|
-
* {@link SeriesAwareSegmentsMap} (applies segments only to the named series).
|
|
169
|
-
* Both forms can be mixed in the same array.
|
|
170
|
-
*/
|
|
171
|
-
export type SegmentsStyle<TStyle> = DefaultSegmentsMap<TStyle> | SeriesAwareSegmentsMap<TStyle>;
|
|
172
149
|
/**
|
|
173
150
|
* The `segments` prop accepted by every plot kind.
|
|
174
151
|
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
152
|
+
* A record mapping series names (or `'default'` for all series) to lists of
|
|
153
|
+
* {@link Segment}s. Segments in a named key apply only to that series;
|
|
154
|
+
* segments under `'default'` apply to every series.
|
|
178
155
|
*
|
|
179
156
|
* ```ts
|
|
180
|
-
* segments:
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* { default: [{ areas: [{ x: { from: 2012, to: 2014 } }], style: { dots: { dotSymbol: 'circle' } } }] },
|
|
186
|
-
* ]
|
|
157
|
+
* segments: {
|
|
158
|
+
* female: [{ style: { stroke: { stroke: 'tomato' } } }], // catch-all for female
|
|
159
|
+
* male: [{ style: { stroke: { stroke: 'steelblue' } } }], // catch-all for male
|
|
160
|
+
* default: [{ areas: [{ x: { from: 2012, to: 2014 } }], style: { dots: { dotSymbol: 'circle' } } }],
|
|
161
|
+
* }
|
|
187
162
|
* ```
|
|
188
163
|
*
|
|
189
164
|
* Within each resolved series list, catch-alls (no `areas`) are evaluated last
|
|
190
165
|
* so that area-specific segments always override them, regardless of position.
|
|
166
|
+
* Area-specific segments also inherit any style key absent from their own style
|
|
167
|
+
* by merging the catch-all as a baseline — the area's own style wins on conflict.
|
|
168
|
+
* This lets you set a series colour once in a catch-all and add per-range dots
|
|
169
|
+
* or dash patterns without repeating the colour on every area segment.
|
|
191
170
|
*/
|
|
192
|
-
export type Segments<TStyle> =
|
|
171
|
+
export type Segments<TStyle> = Record<string, Segment<TStyle>[]>;
|
|
193
172
|
/** One contiguous, styled slice of a series' data — ready to draw as its own mark. */
|
|
194
173
|
export type VisualSegment<T extends Record<string, unknown>, TStyle = LineSegmentStyle> = {
|
|
195
174
|
data: T[];
|