@sentropic/dataviz-svelte 0.2.1 → 0.4.0
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/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/lib/DateRangeFilter.svelte +39 -0
- package/dist/lib/DateRangeFilter.svelte.d.ts +21 -0
- package/dist/lib/DateRangeFilter.svelte.d.ts.map +1 -0
- package/dist/lib/RangeSliderFilter.svelte +75 -0
- package/dist/lib/RangeSliderFilter.svelte.d.ts +32 -0
- package/dist/lib/RangeSliderFilter.svelte.d.ts.map +1 -0
- package/dist/lib/RelativeDateFilter.svelte +80 -0
- package/dist/lib/RelativeDateFilter.svelte.d.ts +35 -0
- package/dist/lib/RelativeDateFilter.svelte.d.ts.map +1 -0
- package/dist/lib/SmallMultiples.svelte +5 -1
- package/dist/lib/SmallMultiples.svelte.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -34,4 +34,10 @@ export { default as ValueSlicer } from './lib/ValueSlicer.svelte';
|
|
|
34
34
|
export type { ValueSlicerProps } from './lib/ValueSlicer.svelte';
|
|
35
35
|
export { default as ExportMenu, rowsToCsv } from './lib/ExportMenu.svelte';
|
|
36
36
|
export type { ExportMenuProps } from './lib/ExportMenu.svelte';
|
|
37
|
+
export { default as DateRangeFilter, dateRangeToSpec } from './lib/DateRangeFilter.svelte';
|
|
38
|
+
export type { DateRangeFilterProps } from './lib/DateRangeFilter.svelte';
|
|
39
|
+
export { default as RelativeDateFilter, relativeRangeToSpec, DEFAULT_RELATIVE_PRESETS, } from './lib/RelativeDateFilter.svelte';
|
|
40
|
+
export type { RelativeDateFilterProps, RelativeDatePreset } from './lib/RelativeDateFilter.svelte';
|
|
41
|
+
export { default as RangeSliderFilter, numericDomain, rangeBoundsToSpec, } from './lib/RangeSliderFilter.svelte';
|
|
42
|
+
export type { RangeSliderFilterProps, NumericDomain } from './lib/RangeSliderFilter.svelte';
|
|
37
43
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAChF,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACtF,YAAY,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAChF,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACtF,YAAY,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EACL,OAAO,IAAI,kBAAkB,EAC7B,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACnG,OAAO,EACL,OAAO,IAAI,iBAAiB,EAC5B,aAAa,EACb,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -23,3 +23,6 @@ export { default as DrillBreadcrumb } from './lib/DrillBreadcrumb.svelte';
|
|
|
23
23
|
export { default as TopNFilter } from './lib/TopNFilter.svelte';
|
|
24
24
|
export { default as ValueSlicer } from './lib/ValueSlicer.svelte';
|
|
25
25
|
export { default as ExportMenu, rowsToCsv } from './lib/ExportMenu.svelte';
|
|
26
|
+
export { default as DateRangeFilter, dateRangeToSpec } from './lib/DateRangeFilter.svelte';
|
|
27
|
+
export { default as RelativeDateFilter, relativeRangeToSpec, DEFAULT_RELATIVE_PRESETS, } from './lib/RelativeDateFilter.svelte';
|
|
28
|
+
export { default as RangeSliderFilter, numericDomain, rangeBoundsToSpec, } from './lib/RangeSliderFilter.svelte';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { DashboardStore, FilterSpec } from '@sentropic/dataviz-core';
|
|
3
|
+
|
|
4
|
+
export type DateRangeFilterProps = {
|
|
5
|
+
/** The dashboard store to bind to. */
|
|
6
|
+
store: DashboardStore;
|
|
7
|
+
/** Date dimension to filter (its cells must be epoch-millisecond numbers). */
|
|
8
|
+
dimension: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Pure: a DatePicker `{ start, end }` range → a core `range` {@link FilterSpec}
|
|
15
|
+
* in epoch milliseconds (bounds optional), or `null` when the range is empty.
|
|
16
|
+
*/
|
|
17
|
+
export function dateRangeToSpec(range: { start: Date | null; end: Date | null }): FilterSpec | null {
|
|
18
|
+
const min = range.start ? range.start.getTime() : undefined;
|
|
19
|
+
const max = range.end ? range.end.getTime() : undefined;
|
|
20
|
+
if (min === undefined && max === undefined) return null;
|
|
21
|
+
return { kind: 'range', min, max };
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
import { DatePicker, type DatePickerRange } from '@sentropic/design-system-svelte';
|
|
27
|
+
|
|
28
|
+
let { store, dimension, label = 'Période', class: className }: DateRangeFilterProps = $props();
|
|
29
|
+
|
|
30
|
+
let range = $state<DatePickerRange>({ start: null, end: null });
|
|
31
|
+
|
|
32
|
+
$effect(() => {
|
|
33
|
+
const spec = dateRangeToSpec(range);
|
|
34
|
+
if (spec) store.setFilter(dimension, spec);
|
|
35
|
+
else store.clearFilter(dimension);
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<DatePicker mode="range" {label} bind:value={range} class={className} />
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DashboardStore, FilterSpec } from '@sentropic/dataviz-core';
|
|
2
|
+
export type DateRangeFilterProps = {
|
|
3
|
+
/** The dashboard store to bind to. */
|
|
4
|
+
store: DashboardStore;
|
|
5
|
+
/** Date dimension to filter (its cells must be epoch-millisecond numbers). */
|
|
6
|
+
dimension: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Pure: a DatePicker `{ start, end }` range → a core `range` {@link FilterSpec}
|
|
12
|
+
* in epoch milliseconds (bounds optional), or `null` when the range is empty.
|
|
13
|
+
*/
|
|
14
|
+
export declare function dateRangeToSpec(range: {
|
|
15
|
+
start: Date | null;
|
|
16
|
+
end: Date | null;
|
|
17
|
+
}): FilterSpec | null;
|
|
18
|
+
declare const DateRangeFilter: import("svelte").Component<DateRangeFilterProps, {}, "">;
|
|
19
|
+
type DateRangeFilter = ReturnType<typeof DateRangeFilter>;
|
|
20
|
+
export default DateRangeFilter;
|
|
21
|
+
//# sourceMappingURL=DateRangeFilter.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateRangeFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/DateRangeFilter.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1E,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAA;CAAE,GAAG,UAAU,GAAG,IAAI,CAKlG;AAyBH,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { DashboardStore, FilterSpec, Row } from '@sentropic/dataviz-core';
|
|
3
|
+
|
|
4
|
+
export type NumericDomain = { min: number; max: number };
|
|
5
|
+
|
|
6
|
+
export type RangeSliderFilterProps = {
|
|
7
|
+
/** The dashboard store to bind to. */
|
|
8
|
+
store: DashboardStore;
|
|
9
|
+
/** Continuous numeric dimension to filter. */
|
|
10
|
+
dimension: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Lower bound of the track; defaults to the data minimum. */
|
|
13
|
+
min?: number;
|
|
14
|
+
/** Upper bound of the track; defaults to the data maximum. */
|
|
15
|
+
max?: number;
|
|
16
|
+
step?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pure: the numeric `[min, max]` domain of a dimension across rows (finite
|
|
21
|
+
* values only). Returns `{ min: 0, max: 0 }` when no finite value is present.
|
|
22
|
+
*/
|
|
23
|
+
export function numericDomain(data: readonly Row[], dimension: string): NumericDomain {
|
|
24
|
+
let min = Infinity;
|
|
25
|
+
let max = -Infinity;
|
|
26
|
+
for (const row of data) {
|
|
27
|
+
const v = row[dimension];
|
|
28
|
+
if (typeof v === 'number' && Number.isFinite(v)) {
|
|
29
|
+
if (v < min) min = v;
|
|
30
|
+
if (v > max) max = v;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return min === Infinity ? { min: 0, max: 0 } : { min, max };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Pure: two slider handles → a core `range` {@link FilterSpec} (bounds
|
|
38
|
+
* normalized so `lo <= hi`), or `null` when they span the whole domain (no
|
|
39
|
+
* effective constraint).
|
|
40
|
+
*/
|
|
41
|
+
export function rangeBoundsToSpec(lower: number, upper: number, domain: NumericDomain): FilterSpec | null {
|
|
42
|
+
const lo = Math.min(lower, upper);
|
|
43
|
+
const hi = Math.max(lower, upper);
|
|
44
|
+
if (lo <= domain.min && hi >= domain.max) return null;
|
|
45
|
+
return { kind: 'range', min: lo, max: hi };
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<script lang="ts">
|
|
50
|
+
import { untrack } from 'svelte';
|
|
51
|
+
import { Slider } from '@sentropic/design-system-svelte';
|
|
52
|
+
import { findDimension } from '@sentropic/dataviz-core';
|
|
53
|
+
|
|
54
|
+
let { store, dimension, label, min, max, step = 1 }: RangeSliderFilterProps = $props();
|
|
55
|
+
|
|
56
|
+
// The track domain is resolved once (props override the data extent).
|
|
57
|
+
const domain = untrack((): NumericDomain => {
|
|
58
|
+
const d = numericDomain(store.data, dimension);
|
|
59
|
+
return { min: min ?? d.min, max: max ?? d.max };
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const resolvedLabel = $derived(label ?? findDimension(store.model, dimension)?.label ?? dimension);
|
|
63
|
+
|
|
64
|
+
let lower = $state(domain.min);
|
|
65
|
+
let upper = $state(domain.max);
|
|
66
|
+
|
|
67
|
+
$effect(() => {
|
|
68
|
+
const spec = rangeBoundsToSpec(lower, upper, domain);
|
|
69
|
+
if (spec) store.setFilter(dimension, spec);
|
|
70
|
+
else store.clearFilter(dimension);
|
|
71
|
+
});
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<Slider label={`${resolvedLabel} (min)`} bind:value={lower} min={domain.min} max={domain.max} {step} />
|
|
75
|
+
<Slider label={`${resolvedLabel} (max)`} bind:value={upper} min={domain.min} max={domain.max} {step} />
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DashboardStore, FilterSpec, Row } from '@sentropic/dataviz-core';
|
|
2
|
+
export type NumericDomain = {
|
|
3
|
+
min: number;
|
|
4
|
+
max: number;
|
|
5
|
+
};
|
|
6
|
+
export type RangeSliderFilterProps = {
|
|
7
|
+
/** The dashboard store to bind to. */
|
|
8
|
+
store: DashboardStore;
|
|
9
|
+
/** Continuous numeric dimension to filter. */
|
|
10
|
+
dimension: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Lower bound of the track; defaults to the data minimum. */
|
|
13
|
+
min?: number;
|
|
14
|
+
/** Upper bound of the track; defaults to the data maximum. */
|
|
15
|
+
max?: number;
|
|
16
|
+
step?: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Pure: the numeric `[min, max]` domain of a dimension across rows (finite
|
|
20
|
+
* values only). Returns `{ min: 0, max: 0 }` when no finite value is present.
|
|
21
|
+
*/
|
|
22
|
+
export declare function numericDomain(data: readonly Row[], dimension: string): NumericDomain;
|
|
23
|
+
/**
|
|
24
|
+
* Pure: two slider handles → a core `range` {@link FilterSpec} (bounds
|
|
25
|
+
* normalized so `lo <= hi`), or `null` when they span the whole domain (no
|
|
26
|
+
* effective constraint).
|
|
27
|
+
*/
|
|
28
|
+
export declare function rangeBoundsToSpec(lower: number, upper: number, domain: NumericDomain): FilterSpec | null;
|
|
29
|
+
declare const RangeSliderFilter: import("svelte").Component<RangeSliderFilterProps, {}, "">;
|
|
30
|
+
type RangeSliderFilter = ReturnType<typeof RangeSliderFilter>;
|
|
31
|
+
export default RangeSliderFilter;
|
|
32
|
+
//# sourceMappingURL=RangeSliderFilter.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RangeSliderFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/RangeSliderFilter.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAE/E,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,MAAM,sBAAsB,GAAG;IACnC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,CAWpF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,UAAU,GAAG,IAAI,CAKxG;AAuCH,QAAA,MAAM,iBAAiB,4DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { DashboardStore, FilterSpec } from '@sentropic/dataviz-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A relative-date preset: a labelled choice resolving to a trailing window of
|
|
6
|
+
* `days` ending "now", or `null` days for "all" (no filter).
|
|
7
|
+
*/
|
|
8
|
+
export type RelativeDatePreset = {
|
|
9
|
+
value: string;
|
|
10
|
+
label: string;
|
|
11
|
+
/** Trailing window length in days, or `null` for "all" (clears the filter). */
|
|
12
|
+
days: number | null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type RelativeDateFilterProps = {
|
|
16
|
+
/** The dashboard store to bind to. */
|
|
17
|
+
store: DashboardStore;
|
|
18
|
+
/** Date dimension to filter (its cells must be epoch-millisecond numbers). */
|
|
19
|
+
dimension: string;
|
|
20
|
+
label?: string;
|
|
21
|
+
/** Selectable presets; defaults to {@link DEFAULT_RELATIVE_PRESETS}. */
|
|
22
|
+
presets?: RelativeDatePreset[];
|
|
23
|
+
/** Reference "now" for the trailing windows; defaults to the current time. */
|
|
24
|
+
now?: Date;
|
|
25
|
+
class?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Default relative-date presets: all / 7 / 30 / 90 days / 12 months. */
|
|
29
|
+
export const DEFAULT_RELATIVE_PRESETS: RelativeDatePreset[] = [
|
|
30
|
+
{ value: 'all', label: 'Tout', days: null },
|
|
31
|
+
{ value: '7d', label: '7 derniers jours', days: 7 },
|
|
32
|
+
{ value: '30d', label: '30 derniers jours', days: 30 },
|
|
33
|
+
{ value: '90d', label: '90 derniers jours', days: 90 },
|
|
34
|
+
{ value: '365d', label: '12 derniers mois', days: 365 },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Pure: a trailing window of `days` ending at `now` → a core `range`
|
|
41
|
+
* {@link FilterSpec} in epoch milliseconds, or `null` when `days` is `null`
|
|
42
|
+
* ("all", no filter).
|
|
43
|
+
*/
|
|
44
|
+
export function relativeRangeToSpec(days: number | null, now: Date): FilterSpec | null {
|
|
45
|
+
if (days == null) return null;
|
|
46
|
+
const max = now.getTime();
|
|
47
|
+
return { kind: 'range', min: max - days * DAY_MS, max };
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<script lang="ts">
|
|
52
|
+
import { untrack } from 'svelte';
|
|
53
|
+
import { Select } from '@sentropic/design-system-svelte';
|
|
54
|
+
|
|
55
|
+
let {
|
|
56
|
+
store,
|
|
57
|
+
dimension,
|
|
58
|
+
label = 'Période',
|
|
59
|
+
presets = DEFAULT_RELATIVE_PRESETS,
|
|
60
|
+
now = new Date(),
|
|
61
|
+
class: className,
|
|
62
|
+
}: RelativeDateFilterProps = $props();
|
|
63
|
+
|
|
64
|
+
// Initial selection is the first preset; thereafter `selected` is user-driven
|
|
65
|
+
// state, so we read the prop's initial value without a reactive dependency.
|
|
66
|
+
let selected = $state(untrack(() => presets[0]?.value ?? 'all'));
|
|
67
|
+
|
|
68
|
+
$effect(() => {
|
|
69
|
+
const preset = presets.find((p) => p.value === selected);
|
|
70
|
+
const spec = preset ? relativeRangeToSpec(preset.days, now) : null;
|
|
71
|
+
if (spec) store.setFilter(dimension, spec);
|
|
72
|
+
else store.clearFilter(dimension);
|
|
73
|
+
});
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<Select {label} bind:value={selected} class={className}>
|
|
77
|
+
{#each presets as preset (preset.value)}
|
|
78
|
+
<option value={preset.value}>{preset.label}</option>
|
|
79
|
+
{/each}
|
|
80
|
+
</Select>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DashboardStore, FilterSpec } from '@sentropic/dataviz-core';
|
|
2
|
+
/**
|
|
3
|
+
* A relative-date preset: a labelled choice resolving to a trailing window of
|
|
4
|
+
* `days` ending "now", or `null` days for "all" (no filter).
|
|
5
|
+
*/
|
|
6
|
+
export type RelativeDatePreset = {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
/** Trailing window length in days, or `null` for "all" (clears the filter). */
|
|
10
|
+
days: number | null;
|
|
11
|
+
};
|
|
12
|
+
export type RelativeDateFilterProps = {
|
|
13
|
+
/** The dashboard store to bind to. */
|
|
14
|
+
store: DashboardStore;
|
|
15
|
+
/** Date dimension to filter (its cells must be epoch-millisecond numbers). */
|
|
16
|
+
dimension: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
/** Selectable presets; defaults to {@link DEFAULT_RELATIVE_PRESETS}. */
|
|
19
|
+
presets?: RelativeDatePreset[];
|
|
20
|
+
/** Reference "now" for the trailing windows; defaults to the current time. */
|
|
21
|
+
now?: Date;
|
|
22
|
+
class?: string;
|
|
23
|
+
};
|
|
24
|
+
/** Default relative-date presets: all / 7 / 30 / 90 days / 12 months. */
|
|
25
|
+
export declare const DEFAULT_RELATIVE_PRESETS: RelativeDatePreset[];
|
|
26
|
+
/**
|
|
27
|
+
* Pure: a trailing window of `days` ending at `now` → a core `range`
|
|
28
|
+
* {@link FilterSpec} in epoch milliseconds, or `null` when `days` is `null`
|
|
29
|
+
* ("all", no filter).
|
|
30
|
+
*/
|
|
31
|
+
export declare function relativeRangeToSpec(days: number | null, now: Date): FilterSpec | null;
|
|
32
|
+
declare const RelativeDateFilter: import("svelte").Component<RelativeDateFilterProps, {}, "">;
|
|
33
|
+
type RelativeDateFilter = ReturnType<typeof RelativeDateFilter>;
|
|
34
|
+
export default RelativeDateFilter;
|
|
35
|
+
//# sourceMappingURL=RelativeDateFilter.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RelativeDateFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/RelativeDateFilter.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1E;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC/B,8EAA8E;IAC9E,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,wBAAwB,EAAE,kBAAkB,EAMxD,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,UAAU,GAAG,IAAI,CAIrF;AAyCH,QAAA,MAAM,kBAAkB,6DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -61,16 +61,20 @@
|
|
|
61
61
|
keys.push(k);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
let min = 0;
|
|
64
65
|
let max = 0;
|
|
65
66
|
const panels = keys.map((k) => {
|
|
66
67
|
const facetRows = rows.filter((row) => key(row[facetBy]) === k);
|
|
67
68
|
const data = groupAggregate(facetRows, dimension, m).map(({ key: barKey, value }) => {
|
|
69
|
+
if (value < min) min = value;
|
|
68
70
|
if (value > max) max = value;
|
|
69
71
|
return tone ? { label: barKey, value, tone } : { label: barKey, value };
|
|
70
72
|
});
|
|
71
73
|
return { key: k, data };
|
|
72
74
|
});
|
|
73
|
-
|
|
75
|
+
// Shared domain anchored at 0 so positive-only facets get [0, max] and mixed
|
|
76
|
+
// facets keep negatives in view.
|
|
77
|
+
return { panels, domain: panels.length ? [min, max] : undefined };
|
|
74
78
|
});
|
|
75
79
|
</script>
|
|
76
80
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmallMultiples.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/SmallMultiples.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;
|
|
1
|
+
{"version":3,"file":"SmallMultiples.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/SmallMultiples.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAwEJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentropic/dataviz-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Svelte 5 adapter + dashboard components for @sentropic/dataviz-core, built on @sentropic/design-system-svelte.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test": "vitest run src"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@sentropic/dataviz-core": "0.
|
|
33
|
+
"@sentropic/dataviz-core": "0.4.0",
|
|
34
34
|
"@sentropic/design-system-svelte": "0.23.0",
|
|
35
35
|
"@sentropic/design-system-themes": "0.11.0"
|
|
36
36
|
},
|