@sentropic/dataviz-svelte 0.2.0 → 0.3.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.
Files changed (69) hide show
  1. package/dist/adapter.d.ts +39 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/adapter.js +39 -0
  4. package/dist/index.d.ts +39 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +26 -0
  7. package/dist/lib/CrossfilteredBarChart.svelte.d.ts.map +1 -0
  8. package/dist/lib/DashboardFilterBar.svelte.d.ts.map +1 -0
  9. package/dist/lib/DateRangeFilter.svelte +39 -0
  10. package/dist/lib/DateRangeFilter.svelte.d.ts +21 -0
  11. package/dist/lib/DateRangeFilter.svelte.d.ts.map +1 -0
  12. package/dist/lib/DrillBarChart.svelte.d.ts.map +1 -0
  13. package/dist/lib/DrillBreadcrumb.svelte.d.ts.map +1 -0
  14. package/dist/lib/ExportMenu.svelte +74 -0
  15. package/dist/lib/ExportMenu.svelte.d.ts +23 -0
  16. package/dist/lib/ExportMenu.svelte.d.ts.map +1 -0
  17. package/dist/lib/FieldPane.svelte.d.ts.map +1 -0
  18. package/dist/lib/KpiCardGroup.svelte.d.ts.map +1 -0
  19. package/dist/lib/PivotDataTable.svelte.d.ts.map +1 -0
  20. package/dist/lib/RecordsTable.svelte.d.ts.map +1 -0
  21. package/dist/lib/SelectionLegend.svelte.d.ts.map +1 -0
  22. package/dist/{SmallMultiples.svelte → lib/SmallMultiples.svelte} +5 -1
  23. package/dist/lib/SmallMultiples.svelte.d.ts.map +1 -0
  24. package/dist/lib/TopNFilter.svelte +51 -0
  25. package/dist/lib/TopNFilter.svelte.d.ts +18 -0
  26. package/dist/lib/TopNFilter.svelte.d.ts.map +1 -0
  27. package/dist/lib/ValueSlicer.svelte +59 -0
  28. package/dist/lib/ValueSlicer.svelte.d.ts +15 -0
  29. package/dist/lib/ValueSlicer.svelte.d.ts.map +1 -0
  30. package/package.json +3 -3
  31. package/dist/CrossfilteredBarChart.svelte.d.ts.map +0 -1
  32. package/dist/CrossfilteredBarChart.test.js +0 -63
  33. package/dist/DashboardFilterBar.svelte.d.ts.map +0 -1
  34. package/dist/DashboardFilterBar.test.js +0 -59
  35. package/dist/DrillBarChart.svelte.d.ts.map +0 -1
  36. package/dist/DrillBarChart.test.js +0 -52
  37. package/dist/DrillBreadcrumb.svelte.d.ts.map +0 -1
  38. package/dist/DrillBreadcrumb.test.js +0 -37
  39. package/dist/FieldPane.svelte.d.ts.map +0 -1
  40. package/dist/FieldPane.test.js +0 -36
  41. package/dist/KpiCardGroup.svelte.d.ts.map +0 -1
  42. package/dist/KpiCardGroup.test.js +0 -33
  43. package/dist/PivotDataTable.svelte.d.ts.map +0 -1
  44. package/dist/PivotDataTable.test.js +0 -37
  45. package/dist/RecordsTable.svelte.d.ts.map +0 -1
  46. package/dist/RecordsTable.test.js +0 -43
  47. package/dist/SelectionLegend.svelte.d.ts.map +0 -1
  48. package/dist/SelectionLegend.test.js +0 -47
  49. package/dist/SmallMultiples.svelte.d.ts.map +0 -1
  50. package/dist/SmallMultiples.test.js +0 -47
  51. /package/dist/{CrossfilteredBarChart.svelte → lib/CrossfilteredBarChart.svelte} +0 -0
  52. /package/dist/{CrossfilteredBarChart.svelte.d.ts → lib/CrossfilteredBarChart.svelte.d.ts} +0 -0
  53. /package/dist/{DashboardFilterBar.svelte → lib/DashboardFilterBar.svelte} +0 -0
  54. /package/dist/{DashboardFilterBar.svelte.d.ts → lib/DashboardFilterBar.svelte.d.ts} +0 -0
  55. /package/dist/{DrillBarChart.svelte → lib/DrillBarChart.svelte} +0 -0
  56. /package/dist/{DrillBarChart.svelte.d.ts → lib/DrillBarChart.svelte.d.ts} +0 -0
  57. /package/dist/{DrillBreadcrumb.svelte → lib/DrillBreadcrumb.svelte} +0 -0
  58. /package/dist/{DrillBreadcrumb.svelte.d.ts → lib/DrillBreadcrumb.svelte.d.ts} +0 -0
  59. /package/dist/{FieldPane.svelte → lib/FieldPane.svelte} +0 -0
  60. /package/dist/{FieldPane.svelte.d.ts → lib/FieldPane.svelte.d.ts} +0 -0
  61. /package/dist/{KpiCardGroup.svelte → lib/KpiCardGroup.svelte} +0 -0
  62. /package/dist/{KpiCardGroup.svelte.d.ts → lib/KpiCardGroup.svelte.d.ts} +0 -0
  63. /package/dist/{PivotDataTable.svelte → lib/PivotDataTable.svelte} +0 -0
  64. /package/dist/{PivotDataTable.svelte.d.ts → lib/PivotDataTable.svelte.d.ts} +0 -0
  65. /package/dist/{RecordsTable.svelte → lib/RecordsTable.svelte} +0 -0
  66. /package/dist/{RecordsTable.svelte.d.ts → lib/RecordsTable.svelte.d.ts} +0 -0
  67. /package/dist/{SelectionLegend.svelte → lib/SelectionLegend.svelte} +0 -0
  68. /package/dist/{SelectionLegend.svelte.d.ts → lib/SelectionLegend.svelte.d.ts} +0 -0
  69. /package/dist/{SmallMultiples.svelte.d.ts → lib/SmallMultiples.svelte.d.ts} +0 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Svelte 5 adapter for the framework-agnostic dataviz-core store. It bridges the
3
+ * core's `getState`/`subscribe` contract onto Svelte's `Readable` store contract
4
+ * so the dashboard state can be consumed with `$store` syntax. All presentation
5
+ * comes from `@sentropic/design-system-svelte`; this module only wires state.
6
+ */
7
+ import { type DashboardState, type DashboardStore, type DashboardStoreConfig } from '@sentropic/dataviz-core';
8
+ export * from '@sentropic/dataviz-core';
9
+ export type { BarChartTone } from '@sentropic/design-system-svelte';
10
+ export type { TenantTheme } from '@sentropic/design-system-themes';
11
+ /** Minimal Svelte `Readable` contract (avoids a hard dep on `svelte/store`). */
12
+ export interface SvelteReadable<T> {
13
+ subscribe(run: (value: T) => void): () => void;
14
+ }
15
+ /**
16
+ * Wrap a core {@link DashboardStore} as a Svelte `Readable<DashboardState>`.
17
+ *
18
+ * Following the Svelte store contract, `subscribe(run)` calls `run` immediately
19
+ * with the current state, then again after every core notification, and returns
20
+ * an unsubscribe function.
21
+ */
22
+ export declare function toSvelteStore(store: DashboardStore): SvelteReadable<DashboardState>;
23
+ /** Result of {@link createDashboard}: the core store plus its Svelte view. */
24
+ export interface SvelteDashboard {
25
+ /** The underlying core store (use its mutators: setFilter, toggleSelection…). */
26
+ store: DashboardStore;
27
+ /** Svelte-readable snapshot, usable with `$state` in a component. */
28
+ state: SvelteReadable<DashboardState>;
29
+ }
30
+ /**
31
+ * Create a dashboard store and its Svelte-readable view in one call.
32
+ */
33
+ export declare function createDashboard(config: DashboardStoreConfig): SvelteDashboard;
34
+ /**
35
+ * Canonical adapter hook (parity with React/Vue `useDashboard`).
36
+ * Returns the Svelte-readable state for a given core store.
37
+ */
38
+ export declare function useDashboard(store: DashboardStore): SvelteReadable<DashboardState>;
39
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAC1B,MAAM,yBAAyB,CAAC;AAGjC,cAAc,yBAAyB,CAAC;AAIxC,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,YAAY,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,gFAAgF;AAChF,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAChD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAAC,cAAc,CAAC,CAQnF;AAED,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,KAAK,EAAE,cAAc,CAAC;IACtB,qEAAqE;IACrE,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,eAAe,CAG7E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAAC,cAAc,CAAC,CAElF"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Svelte 5 adapter for the framework-agnostic dataviz-core store. It bridges the
3
+ * core's `getState`/`subscribe` contract onto Svelte's `Readable` store contract
4
+ * so the dashboard state can be consumed with `$store` syntax. All presentation
5
+ * comes from `@sentropic/design-system-svelte`; this module only wires state.
6
+ */
7
+ import { createDashboardStore, } from '@sentropic/dataviz-core';
8
+ // Re-export the full core surface so consumers need a single import.
9
+ export * from '@sentropic/dataviz-core';
10
+ /**
11
+ * Wrap a core {@link DashboardStore} as a Svelte `Readable<DashboardState>`.
12
+ *
13
+ * Following the Svelte store contract, `subscribe(run)` calls `run` immediately
14
+ * with the current state, then again after every core notification, and returns
15
+ * an unsubscribe function.
16
+ */
17
+ export function toSvelteStore(store) {
18
+ return {
19
+ subscribe(run) {
20
+ run(store.getState());
21
+ const off = store.subscribe(() => run(store.getState()));
22
+ return off;
23
+ },
24
+ };
25
+ }
26
+ /**
27
+ * Create a dashboard store and its Svelte-readable view in one call.
28
+ */
29
+ export function createDashboard(config) {
30
+ const store = createDashboardStore(config);
31
+ return { store, state: toSvelteStore(store) };
32
+ }
33
+ /**
34
+ * Canonical adapter hook (parity with React/Vue `useDashboard`).
35
+ * Returns the Svelte-readable state for a given core store.
36
+ */
37
+ export function useDashboard(store) {
38
+ return toSvelteStore(store);
39
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @sentropic/dataviz-svelte
3
+ *
4
+ * Svelte 5 adapter + dashboard components for @sentropic/dataviz-core, built on
5
+ * @sentropic/design-system-svelte. The adapter wires the core's observable store
6
+ * onto Svelte reactivity; the components compose design-system presentational
7
+ * pieces and bind them to the shared dashboard state. No presentation is
8
+ * authored here — it all comes from the design system.
9
+ */
10
+ export * from './adapter.js';
11
+ export { default as DashboardFilterBar } from './lib/DashboardFilterBar.svelte';
12
+ export type { DashboardFilterBarProps } from './lib/DashboardFilterBar.svelte';
13
+ export { default as SelectionLegend } from './lib/SelectionLegend.svelte';
14
+ export type { SelectionLegendProps } from './lib/SelectionLegend.svelte';
15
+ export { default as CrossfilteredBarChart } from './lib/CrossfilteredBarChart.svelte';
16
+ export type { CrossfilteredBarChartProps } from './lib/CrossfilteredBarChart.svelte';
17
+ export { default as SmallMultiples } from './lib/SmallMultiples.svelte';
18
+ export type { SmallMultiplesProps } from './lib/SmallMultiples.svelte';
19
+ export { default as FieldPane } from './lib/FieldPane.svelte';
20
+ export type { FieldPaneProps } from './lib/FieldPane.svelte';
21
+ export { default as PivotDataTable } from './lib/PivotDataTable.svelte';
22
+ export type { PivotDataTableProps } from './lib/PivotDataTable.svelte';
23
+ export { default as KpiCardGroup } from './lib/KpiCardGroup.svelte';
24
+ export type { KpiCardGroupProps } from './lib/KpiCardGroup.svelte';
25
+ export { default as RecordsTable } from './lib/RecordsTable.svelte';
26
+ export type { RecordsTableProps } from './lib/RecordsTable.svelte';
27
+ export { default as DrillBarChart } from './lib/DrillBarChart.svelte';
28
+ export type { DrillBarChartProps } from './lib/DrillBarChart.svelte';
29
+ export { default as DrillBreadcrumb } from './lib/DrillBreadcrumb.svelte';
30
+ export type { DrillBreadcrumbProps } from './lib/DrillBreadcrumb.svelte';
31
+ export { default as TopNFilter } from './lib/TopNFilter.svelte';
32
+ export type { TopNFilterProps } from './lib/TopNFilter.svelte';
33
+ export { default as ValueSlicer } from './lib/ValueSlicer.svelte';
34
+ export type { ValueSlicerProps } from './lib/ValueSlicer.svelte';
35
+ export { default as ExportMenu, rowsToCsv } from './lib/ExportMenu.svelte';
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
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;AAC/D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @sentropic/dataviz-svelte
3
+ *
4
+ * Svelte 5 adapter + dashboard components for @sentropic/dataviz-core, built on
5
+ * @sentropic/design-system-svelte. The adapter wires the core's observable store
6
+ * onto Svelte reactivity; the components compose design-system presentational
7
+ * pieces and bind them to the shared dashboard state. No presentation is
8
+ * authored here — it all comes from the design system.
9
+ */
10
+ // Adapter + full core surface re-export (incl. the core's `describeFilterSpec`).
11
+ export * from './adapter.js';
12
+ // Dashboard components (state consumers built on the design system).
13
+ export { default as DashboardFilterBar } from './lib/DashboardFilterBar.svelte';
14
+ export { default as SelectionLegend } from './lib/SelectionLegend.svelte';
15
+ export { default as CrossfilteredBarChart } from './lib/CrossfilteredBarChart.svelte';
16
+ export { default as SmallMultiples } from './lib/SmallMultiples.svelte';
17
+ export { default as FieldPane } from './lib/FieldPane.svelte';
18
+ export { default as PivotDataTable } from './lib/PivotDataTable.svelte';
19
+ export { default as KpiCardGroup } from './lib/KpiCardGroup.svelte';
20
+ export { default as RecordsTable } from './lib/RecordsTable.svelte';
21
+ export { default as DrillBarChart } from './lib/DrillBarChart.svelte';
22
+ export { default as DrillBreadcrumb } from './lib/DrillBreadcrumb.svelte';
23
+ export { default as TopNFilter } from './lib/TopNFilter.svelte';
24
+ export { default as ValueSlicer } from './lib/ValueSlicer.svelte';
25
+ export { default as ExportMenu, rowsToCsv } from './lib/ExportMenu.svelte';
26
+ export { default as DateRangeFilter, dateRangeToSpec } from './lib/DateRangeFilter.svelte';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CrossfilteredBarChart.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/CrossfilteredBarChart.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,0BAA0B,GAAG;IACvC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAkDJ,QAAA,MAAM,qBAAqB,gEAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardFilterBar.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/DashboardFilterBar.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,uBAAuB,GAAG;IACpC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAwCJ,QAAA,MAAM,kBAAkB,6DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -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 @@
1
+ {"version":3,"file":"DrillBarChart.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/DrillBarChart.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,kBAAkB,GAAG;IAC/B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA+DJ,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DrillBreadcrumb.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/DrillBreadcrumb.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAmDJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -0,0 +1,74 @@
1
+ <script lang="ts" module>
2
+ import type { DashboardStore, Row } from '@sentropic/dataviz-core';
3
+
4
+ export type ExportMenuProps = {
5
+ /** The dashboard store to bind to. */
6
+ store: DashboardStore;
7
+ /** View whose cross-filtered rows are exported (omit for global filters). */
8
+ viewId?: string;
9
+ /** Field ids (and order) to export; defaults to all model fields. */
10
+ fields?: string[];
11
+ /** Downloaded file name. */
12
+ filename?: string;
13
+ /** Button label. */
14
+ label?: string;
15
+ class?: string;
16
+ };
17
+
18
+ function escapeCsv(value: unknown): string {
19
+ const s = value == null ? '' : String(value);
20
+ return /[",\n\r]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
21
+ }
22
+
23
+ /** Pure: rows + columns → a CSV string (RFC-4180-ish escaping). */
24
+ export function rowsToCsv(
25
+ rows: readonly Row[],
26
+ columns: { key: string; label: string }[],
27
+ ): string {
28
+ const header = columns.map((c) => escapeCsv(c.label)).join(',');
29
+ const body = rows
30
+ .map((r) => columns.map((c) => escapeCsv(r[c.key] ?? '')).join(','))
31
+ .join('\n');
32
+ return body ? `${header}\n${body}` : header;
33
+ }
34
+ </script>
35
+
36
+ <script lang="ts">
37
+ import { Button } from '@sentropic/design-system-svelte';
38
+ import { findMeasure } from '@sentropic/dataviz-core';
39
+
40
+ let {
41
+ store,
42
+ viewId,
43
+ fields,
44
+ filename = 'export.csv',
45
+ label = 'Exporter (CSV)',
46
+ class: className,
47
+ }: ExportMenuProps = $props();
48
+
49
+ function columns() {
50
+ const ids = fields ?? [
51
+ ...store.model.dimensions.map((d) => d.id),
52
+ ...store.model.measures.map((m) => m.id),
53
+ ];
54
+ return ids.map((id) => {
55
+ const dim = store.model.dimensions.find((d) => d.id === id);
56
+ const meas = findMeasure(store.model, id);
57
+ return { key: id, label: dim?.label ?? meas?.label ?? id };
58
+ });
59
+ }
60
+
61
+ function exportCsv() {
62
+ const csv = rowsToCsv(store.applyCrossfilter(viewId), columns());
63
+ if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function') return;
64
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
65
+ const url = URL.createObjectURL(blob);
66
+ const a = document.createElement('a');
67
+ a.href = url;
68
+ a.download = filename;
69
+ a.click();
70
+ URL.revokeObjectURL(url);
71
+ }
72
+ </script>
73
+
74
+ <Button variant="secondary" class={className} onclick={exportCsv}>{label}</Button>
@@ -0,0 +1,23 @@
1
+ import type { DashboardStore, Row } from '@sentropic/dataviz-core';
2
+ export type ExportMenuProps = {
3
+ /** The dashboard store to bind to. */
4
+ store: DashboardStore;
5
+ /** View whose cross-filtered rows are exported (omit for global filters). */
6
+ viewId?: string;
7
+ /** Field ids (and order) to export; defaults to all model fields. */
8
+ fields?: string[];
9
+ /** Downloaded file name. */
10
+ filename?: string;
11
+ /** Button label. */
12
+ label?: string;
13
+ class?: string;
14
+ };
15
+ /** Pure: rows + columns → a CSV string (RFC-4180-ish escaping). */
16
+ export declare function rowsToCsv(rows: readonly Row[], columns: {
17
+ key: string;
18
+ label: string;
19
+ }[]): string;
20
+ declare const ExportMenu: import("svelte").Component<ExportMenuProps, {}, "">;
21
+ type ExportMenu = ReturnType<typeof ExportMenu>;
22
+ export default ExportMenu;
23
+ //# sourceMappingURL=ExportMenu.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExportMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ExportMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnE,MAAM,MAAM,eAAe,GAAG;IAC5B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAOF,mEAAmE;AACnE,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,GAAG,EAAE,EACpB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GACxC,MAAM,CAMR;AAkDH,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldPane.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/FieldPane.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6BJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KpiCardGroup.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/KpiCardGroup.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,WAAW,EACZ,MAAM,iCAAiC,CAAC;AAEzC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,SAAS,GAAG,EAAE,CAAC;IAChC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4CJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PivotDataTable.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/PivotDataTable.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3E,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA0DJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordsTable.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/RecordsTable.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyCJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionLegend.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/SelectionLegend.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAmCJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,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
- return { panels, domain: panels.length ? [0, max] : undefined };
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
 
@@ -0,0 +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;AAwEJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,51 @@
1
+ <script lang="ts" module>
2
+ import type { DashboardStore } from '@sentropic/dataviz-core';
3
+
4
+ export type TopNFilterProps = {
5
+ /** The dashboard store to bind to. */
6
+ store: DashboardStore;
7
+ /** Dimension to restrict to its top-N values. */
8
+ dimension: string;
9
+ /** Measure used to rank the dimension's values (descending). */
10
+ measure: string;
11
+ /** Initial N. Defaults to 5. */
12
+ defaultN?: number;
13
+ /** Field label of the number input. */
14
+ label?: string;
15
+ class?: string;
16
+ };
17
+ </script>
18
+
19
+ <script lang="ts">
20
+ import { untrack } from 'svelte';
21
+ import { NumberInput } from '@sentropic/design-system-svelte';
22
+ import { findMeasure, groupAggregate, type Row } from '@sentropic/dataviz-core';
23
+
24
+ let {
25
+ store,
26
+ dimension,
27
+ measure,
28
+ defaultN = 5,
29
+ label = 'Top N',
30
+ class: className,
31
+ }: TopNFilterProps = $props();
32
+
33
+ // Uncontrolled initial value captured intentionally from the prop.
34
+ let n = $state(untrack(() => defaultN));
35
+
36
+ // Apply (and keep in sync) an `include` filter of the dimension's top-N values
37
+ // by the measure, ranked over the full dataset (a stable, predictable Top-N).
38
+ $effect(() => {
39
+ const count = n;
40
+ const m = findMeasure(store.model, measure);
41
+ if (!m || !Number.isFinite(count) || count < 1) return;
42
+ const ranked = groupAggregate([...(store.data as readonly Row[])], dimension, m)
43
+ .slice()
44
+ .sort((a, b) => b.value - a.value)
45
+ .slice(0, count)
46
+ .map((r) => r.key);
47
+ store.setFilter(dimension, { kind: 'include', values: ranked });
48
+ });
49
+ </script>
50
+
51
+ <NumberInput {label} bind:value={n} min={1} step={1} class={className} />
@@ -0,0 +1,18 @@
1
+ import type { DashboardStore } from '@sentropic/dataviz-core';
2
+ export type TopNFilterProps = {
3
+ /** The dashboard store to bind to. */
4
+ store: DashboardStore;
5
+ /** Dimension to restrict to its top-N values. */
6
+ dimension: string;
7
+ /** Measure used to rank the dimension's values (descending). */
8
+ measure: string;
9
+ /** Initial N. Defaults to 5. */
10
+ defaultN?: number;
11
+ /** Field label of the number input. */
12
+ label?: string;
13
+ class?: string;
14
+ };
15
+ declare const TopNFilter: import("svelte").Component<TopNFilterProps, {}, "">;
16
+ type TopNFilter = ReturnType<typeof TopNFilter>;
17
+ export default TopNFilter;
18
+ //# sourceMappingURL=TopNFilter.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopNFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/TopNFilter.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,eAAe,GAAG;IAC5B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6CJ,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,59 @@
1
+ <script lang="ts" module>
2
+ import type { DashboardStore } from '@sentropic/dataviz-core';
3
+
4
+ export type ValueSlicerProps = {
5
+ /** The dashboard store to bind to. */
6
+ store: DashboardStore;
7
+ /** Dimension whose distinct values become the slicer's checkboxes. */
8
+ dimension: string;
9
+ /** Legend; defaults to the dimension label. */
10
+ legend?: string;
11
+ orientation?: 'vertical' | 'horizontal';
12
+ class?: string;
13
+ };
14
+ </script>
15
+
16
+ <script lang="ts">
17
+ import { CheckboxGroup, type CheckboxGroupOption } from '@sentropic/design-system-svelte';
18
+ import { findDimension } from '@sentropic/dataviz-core';
19
+ import { useDashboard } from '../adapter.js';
20
+
21
+ let { store, dimension, legend, orientation = 'vertical', class: className }: ValueSlicerProps = $props();
22
+
23
+ const dash = $derived(useDashboard(store));
24
+ const key = (v: unknown) => (v == null ? 'null' : String(v));
25
+ const resolvedLegend = $derived(legend ?? findDimension(store.model, dimension)?.label ?? dimension);
26
+
27
+ const options = $derived.by((): CheckboxGroupOption[] => {
28
+ const seen = new Set<string>();
29
+ const opts: CheckboxGroupOption[] = [];
30
+ for (const row of store.data) {
31
+ const k = key(row[dimension]);
32
+ if (!seen.has(k)) {
33
+ seen.add(k);
34
+ opts.push({ label: k, value: k });
35
+ }
36
+ }
37
+ return opts;
38
+ });
39
+
40
+ // Checked = the dimension's active `include` filter (an OR over values).
41
+ const value = $derived.by((): string[] => {
42
+ const f = $dash.filters[dimension];
43
+ return f && f.kind === 'include' ? [...f.values] : [];
44
+ });
45
+
46
+ function onchange(values: string[]) {
47
+ if (values.length) store.setFilter(dimension, { kind: 'include', values });
48
+ else store.clearFilter(dimension);
49
+ }
50
+ </script>
51
+
52
+ <CheckboxGroup
53
+ legend={resolvedLegend}
54
+ {options}
55
+ {value}
56
+ {orientation}
57
+ class={className}
58
+ {onchange}
59
+ />
@@ -0,0 +1,15 @@
1
+ import type { DashboardStore } from '@sentropic/dataviz-core';
2
+ export type ValueSlicerProps = {
3
+ /** The dashboard store to bind to. */
4
+ store: DashboardStore;
5
+ /** Dimension whose distinct values become the slicer's checkboxes. */
6
+ dimension: string;
7
+ /** Legend; defaults to the dimension label. */
8
+ legend?: string;
9
+ orientation?: 'vertical' | 'horizontal';
10
+ class?: string;
11
+ };
12
+ declare const ValueSlicer: import("svelte").Component<ValueSlicerProps, {}, "">;
13
+ type ValueSlicer = ReturnType<typeof ValueSlicer>;
14
+ export default ValueSlicer;
15
+ //# sourceMappingURL=ValueSlicer.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValueSlicer.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ValueSlicer.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiDJ,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentropic/dataviz-svelte",
3
- "version": "0.2.0",
3
+ "version": "0.3.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",
@@ -25,12 +25,12 @@
25
25
  "**/*.css"
26
26
  ],
27
27
  "scripts": {
28
- "build": "svelte-package",
28
+ "build": "svelte-package --input src && rm -f dist/*.test.* dist/lib/*.test.*",
29
29
  "check": "svelte-check --tsconfig ./tsconfig.json",
30
30
  "test": "vitest run src"
31
31
  },
32
32
  "dependencies": {
33
- "@sentropic/dataviz-core": "0.2.0",
33
+ "@sentropic/dataviz-core": "0.3.0",
34
34
  "@sentropic/design-system-svelte": "0.23.0",
35
35
  "@sentropic/design-system-themes": "0.11.0"
36
36
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"CrossfilteredBarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CrossfilteredBarChart.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,0BAA0B,GAAG;IACvC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAkDJ,QAAA,MAAM,qBAAqB,gEAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
@@ -1,63 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import CrossfilteredBarChart from './CrossfilteredBarChart.svelte';
6
- const model = {
7
- dimensions: [
8
- { id: 'country', label: 'Pays', type: 'discrete' },
9
- { id: 'product', label: 'Produit', type: 'discrete' },
10
- ],
11
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
12
- };
13
- const data = [
14
- { country: 'FR', product: 'A', sales: 10 },
15
- { country: 'FR', product: 'B', sales: 5 },
16
- { country: 'US', product: 'A', sales: 20 },
17
- ];
18
- const newStore = () => createDashboardStore({ model, data });
19
- const bars = (c) => c.querySelectorAll('.st-barChart__bar').length;
20
- const baseProps = { viewId: 'byCountry', dimension: 'country', measure: 'sales', label: 'Ventes par pays' };
21
- describe('CrossfilteredBarChart', () => {
22
- it('exposes the chart with its accessible label', () => {
23
- const { getByRole } = render(CrossfilteredBarChart, { props: { store: newStore(), ...baseProps } });
24
- expect(getByRole('img', { name: 'Ventes par pays' })).toBeTruthy();
25
- });
26
- it('aggregates rows into one bar (and one selection chip) per distinct value', () => {
27
- const { container, getByRole } = render(CrossfilteredBarChart, { props: { store: newStore(), ...baseProps } });
28
- expect(bars(container)).toBe(2);
29
- expect(getByRole('button', { name: /^FR:/ })).toBeTruthy();
30
- expect(getByRole('button', { name: /^US:/ })).toBeTruthy();
31
- });
32
- it('toggles this view selection when a bar chip is clicked (brushing input)', () => {
33
- const store = newStore();
34
- const { getByRole } = render(CrossfilteredBarChart, { props: { store, ...baseProps } });
35
- getByRole('button', { name: /^FR:/ }).click();
36
- expect(store.getState().selections.byCountry).toEqual(['FR']);
37
- });
38
- it('re-aggregates reactively as the shared filter state narrows the rows', async () => {
39
- const store = newStore();
40
- const { container, queryByRole } = render(CrossfilteredBarChart, { props: { store, ...baseProps } });
41
- expect(bars(container)).toBe(2);
42
- store.setFilter('country', { kind: 'include', values: ['FR'] });
43
- await tick();
44
- expect(bars(container)).toBe(1);
45
- expect(queryByRole('button', { name: /^US:/ })).toBeNull();
46
- });
47
- it('is output-only when selectable=false (no selection chips)', () => {
48
- const { queryByRole } = render(CrossfilteredBarChart, {
49
- props: { store: newStore(), ...baseProps, selectable: false },
50
- });
51
- expect(queryByRole('button', { name: /^FR:/ })).toBeNull();
52
- });
53
- it('renders no bars when the measure or dimension is unknown', () => {
54
- const { container: c1 } = render(CrossfilteredBarChart, {
55
- props: { store: newStore(), viewId: 'v', dimension: 'country', measure: 'nope', label: 'Vide' },
56
- });
57
- expect(bars(c1)).toBe(0);
58
- const { container: c2 } = render(CrossfilteredBarChart, {
59
- props: { store: newStore(), viewId: 'v', dimension: 'nope', measure: 'sales', label: 'Vide' },
60
- });
61
- expect(bars(c2)).toBe(0);
62
- });
63
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"DashboardFilterBar.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DashboardFilterBar.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,uBAAuB,GAAG;IACpC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAwCJ,QAAA,MAAM,kBAAkB,6DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -1,59 +0,0 @@
1
- import { render, screen } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import DashboardFilterBar from './DashboardFilterBar.svelte';
6
- const model = {
7
- dimensions: [
8
- { id: 'country', label: 'Pays', type: 'discrete' },
9
- { id: 'price', label: 'Prix', type: 'continuous' },
10
- ],
11
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
12
- };
13
- const newStore = () => createDashboardStore({ model, data: [] });
14
- describe('DashboardFilterBar', () => {
15
- it('exposes the filter group with its aria-label', () => {
16
- const store = newStore();
17
- render(DashboardFilterBar, { props: { store, label: 'Filtres actifs' } });
18
- expect(screen.getByRole('group', { name: 'Filtres actifs' })).toBeTruthy();
19
- });
20
- it('renders one pill per active filter, labelled by the dimension', () => {
21
- const store = newStore();
22
- store.setFilter('country', { kind: 'include', values: ['France', 'Italie'] });
23
- render(DashboardFilterBar, { props: { store } });
24
- expect(screen.getByText('Pays')).toBeTruthy();
25
- expect(screen.getByText('France, Italie')).toBeTruthy();
26
- });
27
- it('clears a single filter via its pill remove button', () => {
28
- const store = newStore();
29
- store.setFilter('country', { kind: 'include', values: ['France'] });
30
- render(DashboardFilterBar, { props: { store } });
31
- const remove = screen.getByRole('button', { name: 'Retirer le filtre Pays' });
32
- remove.click();
33
- expect(store.getState().filters.country).toBeUndefined();
34
- });
35
- it('clears every filter — but preserves selections — via the clear-all button', () => {
36
- const store = newStore();
37
- store.setFilter('country', { kind: 'include', values: ['France'] });
38
- store.setFilter('price', { kind: 'range', min: 10 });
39
- store.toggleSelection('byCountry', 'FR');
40
- render(DashboardFilterBar, { props: { store } });
41
- screen.getByRole('button', { name: 'Tout effacer' }).click();
42
- expect(Object.keys(store.getState().filters)).toHaveLength(0);
43
- expect(store.getState().selections.byCountry).toEqual(['FR']);
44
- });
45
- it('shows no clear-all button when there are no filters', () => {
46
- const store = newStore();
47
- render(DashboardFilterBar, { props: { store } });
48
- expect(screen.queryByRole('button', { name: 'Tout effacer' })).toBeNull();
49
- });
50
- it('reacts to a filter set after mount', async () => {
51
- const store = newStore();
52
- render(DashboardFilterBar, { props: { store } });
53
- expect(screen.queryByText('≥ 5')).toBeNull();
54
- store.setFilter('price', { kind: 'range', min: 5 });
55
- await tick();
56
- expect(screen.getByText('≥ 5')).toBeTruthy();
57
- expect(screen.getByText('Prix')).toBeTruthy();
58
- });
59
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"DrillBarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DrillBarChart.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,kBAAkB,GAAG;IAC/B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA+DJ,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -1,52 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import DrillBarChart from './DrillBarChart.svelte';
6
- const model = {
7
- dimensions: [
8
- { id: 'region', label: 'Région', type: 'discrete' },
9
- { id: 'city', label: 'Ville', type: 'discrete' },
10
- ],
11
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
12
- };
13
- const data = [
14
- { region: 'Nord', city: 'Lille', sales: 10 },
15
- { region: 'Nord', city: 'Lille', sales: 5 },
16
- { region: 'Nord', city: 'Rouen', sales: 7 },
17
- { region: 'Sud', city: 'Nice', sales: 20 },
18
- ];
19
- const newStore = () => createDashboardStore({ model, data });
20
- const base = { viewId: 'd', hierarchy: ['region', 'city'], measure: 'sales', label: 'Ventes' };
21
- describe('DrillBarChart', () => {
22
- it('groups by the first hierarchy level initially', () => {
23
- const { getByRole } = render(DrillBarChart, { props: { store: newStore(), ...base } });
24
- expect(getByRole('button', { name: /^Nord:/ })).toBeTruthy();
25
- expect(getByRole('button', { name: /^Sud:/ })).toBeTruthy();
26
- });
27
- it('drills down on bar click: filters the clicked value and pushes the next level', () => {
28
- const store = newStore();
29
- const { getByRole } = render(DrillBarChart, { props: { store, ...base } });
30
- getByRole('button', { name: /^Nord:/ }).click();
31
- expect(store.getState().drill.d).toEqual(['city']);
32
- expect(store.getState().filters.region).toEqual({ kind: 'include', values: ['Nord'] });
33
- });
34
- it('shows the cities of the drilled value after drilling', async () => {
35
- const store = newStore();
36
- const { getByRole, queryByRole } = render(DrillBarChart, { props: { store, ...base } });
37
- getByRole('button', { name: /^Nord:/ }).click();
38
- await tick();
39
- expect(getByRole('button', { name: /^Lille:/ })).toBeTruthy();
40
- expect(getByRole('button', { name: /^Rouen:/ })).toBeTruthy();
41
- expect(queryByRole('button', { name: /^Nice:/ })).toBeNull();
42
- });
43
- it('toggles selection at the deepest level instead of drilling', async () => {
44
- const store = newStore();
45
- const { getByRole } = render(DrillBarChart, { props: { store, ...base } });
46
- getByRole('button', { name: /^Nord:/ }).click();
47
- await tick();
48
- getByRole('button', { name: /^Lille:/ }).click();
49
- expect(store.getState().selections.d).toEqual(['Lille']);
50
- expect(store.getState().drill.d).toEqual(['city']);
51
- });
52
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"DrillBreadcrumb.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DrillBreadcrumb.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAmDJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -1,37 +0,0 @@
1
- import { render, screen } from '@testing-library/svelte';
2
- import { describe, it, expect } from 'vitest';
3
- import { createDashboardStore } from '@sentropic/dataviz-core';
4
- import DrillBreadcrumb from './DrillBreadcrumb.svelte';
5
- const model = {
6
- dimensions: [
7
- { id: 'region', label: 'Région', type: 'discrete' },
8
- { id: 'city', label: 'Ville', type: 'discrete' },
9
- ],
10
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
11
- };
12
- const newStore = () => createDashboardStore({ model, data: [] });
13
- const base = { viewId: 'd', hierarchy: ['region', 'city'] };
14
- describe('DrillBreadcrumb', () => {
15
- it('shows only the root level and no back button initially', () => {
16
- render(DrillBreadcrumb, { props: { store: newStore(), ...base } });
17
- expect(screen.getByText('Région')).toBeTruthy();
18
- expect(screen.queryByRole('button', { name: 'Remonter' })).toBeNull();
19
- });
20
- it('shows the drilled trail and a back button after drilling', () => {
21
- const store = newStore();
22
- store.drillDown('d', 'city');
23
- render(DrillBreadcrumb, { props: { store, ...base } });
24
- expect(screen.getByText('Région')).toBeTruthy();
25
- expect(screen.getByText('Ville')).toBeTruthy();
26
- expect(screen.getByRole('button', { name: 'Remonter' })).toBeTruthy();
27
- });
28
- it('back pops the drill path and clears the level filter', () => {
29
- const store = newStore();
30
- store.setFilter('region', { kind: 'include', values: ['Nord'] });
31
- store.drillDown('d', 'city');
32
- render(DrillBreadcrumb, { props: { store, ...base } });
33
- screen.getByRole('button', { name: 'Remonter' }).click();
34
- expect(store.getState().drill.d ?? []).toEqual([]);
35
- expect(store.getState().filters.region).toBeUndefined();
36
- });
37
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"FieldPane.svelte.d.ts","sourceRoot":"","sources":["../src/lib/FieldPane.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6BJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -1,36 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { describe, expect, it } from 'vitest';
3
- import {} from '@sentropic/dataviz-core';
4
- import FieldPane from './FieldPane.svelte';
5
- const model = {
6
- dimensions: [
7
- { id: 'country', label: 'Pays', type: 'discrete', folder: 'Geo' },
8
- { id: 'age', label: 'Age', type: 'continuous' },
9
- ],
10
- measures: [{ id: 'revenue', label: 'Revenu', aggregation: 'sum', folder: 'Finance' }],
11
- };
12
- describe('FieldPane (svelte)', () => {
13
- it('renders the core field tree through DS TreeView', () => {
14
- const { getByText } = render(FieldPane, { props: { model } });
15
- expect(getByText('Dimensions')).toBeTruthy();
16
- expect(getByText('Measures')).toBeTruthy();
17
- expect(getByText('Geo')).toBeTruthy();
18
- expect(getByText('Pays')).toBeTruthy();
19
- expect(getByText('Revenu')).toBeTruthy();
20
- });
21
- it('filters field groups with core field pane options', () => {
22
- const { queryByText, getByText } = render(FieldPane, {
23
- props: { model, includeDimensions: false },
24
- });
25
- expect(queryByText('Dimensions')).toBeNull();
26
- expect(queryByText('Pays')).toBeNull();
27
- expect(getByText('Measures')).toBeTruthy();
28
- expect(getByText('Revenu')).toBeTruthy();
29
- });
30
- it('passes selectedId to the DS TreeView', () => {
31
- const { getByText } = render(FieldPane, { props: { model, selectedId: 'measure:revenue' } });
32
- expect(getByText('Revenu')
33
- .closest('.st-treeView__row')
34
- ?.classList.contains('st-treeView__row--selected')).toBe(true);
35
- });
36
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"KpiCardGroup.svelte.d.ts","sourceRoot":"","sources":["../src/lib/KpiCardGroup.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,WAAW,EACZ,MAAM,iCAAiC,CAAC;AAEzC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,SAAS,GAAG,EAAE,CAAC;IAChC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4CJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -1,33 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import KpiCardGroup from './KpiCardGroup.svelte';
6
- const model = {
7
- dimensions: [{ id: 'month', label: 'Mois', type: 'discrete' }],
8
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
9
- };
10
- const data = [
11
- { month: 'Jan', sales: 10 },
12
- { month: 'Feb', sales: 20 },
13
- ];
14
- describe('KpiCardGroup (svelte)', () => {
15
- it('renders DS KPI cards from core KPI configs', () => {
16
- const store = createDashboardStore({ model, data });
17
- const { getByText } = render(KpiCardGroup, {
18
- props: { store, configs: [{ id: 'sales', measure: 'sales', sparklineDimension: 'month' }] },
19
- });
20
- expect(getByText('Ventes')).toBeTruthy();
21
- expect(getByText('30')).toBeTruthy();
22
- });
23
- it('updates when the shared filter state changes', async () => {
24
- const store = createDashboardStore({ model, data });
25
- const { getByText } = render(KpiCardGroup, {
26
- props: { store, configs: [{ id: 'sales', measure: 'sales' }] },
27
- });
28
- expect(getByText('30')).toBeTruthy();
29
- store.setFilter('month', { kind: 'include', values: ['Jan'] });
30
- await tick();
31
- expect(getByText('10')).toBeTruthy();
32
- });
33
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"PivotDataTable.svelte.d.ts","sourceRoot":"","sources":["../src/lib/PivotDataTable.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3E,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA0DJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -1,37 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import PivotDataTable from './PivotDataTable.svelte';
6
- const model = {
7
- dimensions: [{ id: 'country', label: 'Pays', type: 'discrete' }],
8
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
9
- };
10
- const data = [
11
- { country: 'FR', sales: 10 },
12
- { country: 'FR', sales: 5 },
13
- { country: 'US', sales: 20 },
14
- ];
15
- const newStore = () => createDashboardStore({ model, data });
16
- describe('PivotDataTable (svelte)', () => {
17
- it('renders a DS DataTable from the core pivot model', () => {
18
- const { getByText } = render(PivotDataTable, {
19
- props: { store: newStore(), rows: ['country'], measures: ['sales'], caption: 'Pivot' },
20
- });
21
- expect(getByText('Pivot')).toBeTruthy();
22
- expect(getByText('Pays')).toBeTruthy();
23
- expect(getByText('Ventes')).toBeTruthy();
24
- expect(getByText('FR')).toBeTruthy();
25
- expect(getByText('15')).toBeTruthy();
26
- });
27
- it('updates when the shared filter state changes', async () => {
28
- const store = newStore();
29
- const { getByText, queryByText } = render(PivotDataTable, {
30
- props: { store, rows: ['country'], measures: ['sales'] },
31
- });
32
- expect(getByText('US')).toBeTruthy();
33
- store.setFilter('country', { kind: 'include', values: ['FR'] });
34
- await tick();
35
- expect(queryByText('US')).toBeNull();
36
- });
37
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"RecordsTable.svelte.d.ts","sourceRoot":"","sources":["../src/lib/RecordsTable.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyCJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -1,43 +0,0 @@
1
- import { render, screen } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import RecordsTable from './RecordsTable.svelte';
6
- const model = {
7
- dimensions: [{ id: 'country', label: 'Pays', type: 'discrete' }],
8
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
9
- };
10
- const data = [
11
- { country: 'FR', sales: 10 },
12
- { country: 'US', sales: 20 },
13
- ];
14
- const newStore = () => createDashboardStore({ model, data });
15
- const bodyRows = (c) => c.querySelectorAll('tbody tr').length;
16
- describe('RecordsTable', () => {
17
- it('renders a table with a column per model field', () => {
18
- const { container } = render(RecordsTable, { props: { store: newStore() } });
19
- expect(container.querySelector('table')).toBeTruthy();
20
- expect(screen.getByText('Pays')).toBeTruthy();
21
- expect(screen.getByText('Ventes')).toBeTruthy();
22
- });
23
- it('renders one row per visible record', () => {
24
- const { container } = render(RecordsTable, { props: { store: newStore() } });
25
- expect(bodyRows(container)).toBe(2);
26
- expect(screen.getByText('FR')).toBeTruthy();
27
- expect(screen.getByText('US')).toBeTruthy();
28
- });
29
- it('reflects the cross-filtered rows', async () => {
30
- const store = newStore();
31
- const { container } = render(RecordsTable, { props: { store } });
32
- expect(bodyRows(container)).toBe(2);
33
- store.setFilter('country', { kind: 'include', values: ['FR'] });
34
- await tick();
35
- expect(bodyRows(container)).toBe(1);
36
- expect(screen.queryByText('US')).toBeNull();
37
- });
38
- it('respects a fields subset/order', () => {
39
- render(RecordsTable, { props: { store: newStore(), fields: ['sales'] } });
40
- expect(screen.getByText('Ventes')).toBeTruthy();
41
- expect(screen.queryByText('Pays')).toBeNull();
42
- });
43
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"SelectionLegend.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SelectionLegend.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,sCAAsC;IACtC,KAAK,EAAE,cAAc,CAAC;IACtB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAmCJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -1,47 +0,0 @@
1
- import { render, screen } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import { describe, it, expect } from 'vitest';
4
- import { createDashboardStore } from '@sentropic/dataviz-core';
5
- import SelectionLegend from './SelectionLegend.svelte';
6
- const model = {
7
- dimensions: [{ id: 'country', label: 'Pays', type: 'discrete' }],
8
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
9
- };
10
- const newStore = () => createDashboardStore({ model, data: [] });
11
- describe('SelectionLegend', () => {
12
- it('renders nothing while there are no selections', () => {
13
- const store = newStore();
14
- const { container } = render(SelectionLegend, { props: { store } });
15
- expect(container.querySelector('[role="group"]')).toBeNull();
16
- });
17
- it('renders one chip per view with a selection count and a friendly label', () => {
18
- const store = newStore();
19
- store.toggleSelection('byCountry', 'FR');
20
- store.toggleSelection('byCountry', 'US');
21
- render(SelectionLegend, { props: { store, labels: { byCountry: 'Carte' } } });
22
- expect(screen.getByRole('group', { name: 'Sélections actives' })).toBeTruthy();
23
- expect(screen.getByText('Carte')).toBeTruthy();
24
- expect(screen.getByText('(2)')).toBeTruthy();
25
- });
26
- it('falls back to the view id when no label is supplied', () => {
27
- const store = newStore();
28
- store.toggleSelection('byCountry', 'FR');
29
- render(SelectionLegend, { props: { store } });
30
- expect(screen.getByText('byCountry')).toBeTruthy();
31
- });
32
- it('clears a view selection via the chip clear button', () => {
33
- const store = newStore();
34
- store.toggleSelection('byCountry', 'FR');
35
- render(SelectionLegend, { props: { store, labels: { byCountry: 'Carte' } } });
36
- screen.getByRole('button', { name: 'Effacer Carte' }).click();
37
- expect(store.getState().selections.byCountry).toBeUndefined();
38
- });
39
- it('appears reactively when a selection is made after mount', async () => {
40
- const store = newStore();
41
- const { container } = render(SelectionLegend, { props: { store } });
42
- expect(container.querySelector('[role="group"]')).toBeNull();
43
- store.toggleSelection('byCountry', 'FR');
44
- await tick();
45
- expect(container.querySelector('[role="group"]')).not.toBeNull();
46
- });
47
- });
@@ -1 +0,0 @@
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;AAoEJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -1,47 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { describe, it, expect } from 'vitest';
3
- import { createDashboardStore } from '@sentropic/dataviz-core';
4
- import SmallMultiples from './SmallMultiples.svelte';
5
- const model = {
6
- dimensions: [
7
- { id: 'region', label: 'Région', type: 'discrete' },
8
- { id: 'product', label: 'Produit', type: 'discrete' },
9
- ],
10
- measures: [{ id: 'sales', label: 'Ventes', aggregation: 'sum' }],
11
- };
12
- const data = [
13
- { region: 'Nord', product: 'A', sales: 10 },
14
- { region: 'Nord', product: 'B', sales: 5 },
15
- { region: 'Sud', product: 'A', sales: 20 },
16
- ];
17
- const newStore = () => createDashboardStore({ model, data });
18
- const base = { viewId: 'sm', facetBy: 'region', dimension: 'product', measure: 'sales', label: 'Ventes par produit' };
19
- describe('SmallMultiples', () => {
20
- it('renders the facet group with its aria-label', () => {
21
- const { getByRole } = render(SmallMultiples, { props: { store: newStore(), ...base } });
22
- expect(getByRole('group', { name: 'Ventes par produit' })).toBeTruthy();
23
- });
24
- it('renders one facet chart per distinct facetBy value, labelled by facet', () => {
25
- const { getAllByRole } = render(SmallMultiples, { props: { store: newStore(), ...base } });
26
- const charts = getAllByRole('img');
27
- expect(charts).toHaveLength(2);
28
- expect(charts.map((c) => c.getAttribute('aria-label'))).toEqual([
29
- 'Ventes par produit — Nord',
30
- 'Ventes par produit — Sud',
31
- ]);
32
- });
33
- it('shares one value domain across facets (the Nord facet shows the global max 20)', () => {
34
- const { container } = render(SmallMultiples, { props: { store: newStore(), ...base } });
35
- const charts = container.querySelectorAll('.st-barChart');
36
- expect(charts).toHaveLength(2);
37
- const nordTicks = Array.from(charts[0].querySelectorAll('.st-barChart__tickLabel')).map((t) => t.textContent?.trim());
38
- // Without a shared domain the Nord facet (own max 10) would top out at 10.
39
- expect(nordTicks).toContain('20');
40
- });
41
- it('renders no facets when the facet dimension is unknown', () => {
42
- const { container } = render(SmallMultiples, {
43
- props: { store: newStore(), ...base, facetBy: 'nope' },
44
- });
45
- expect(container.querySelectorAll('.st-barChart')).toHaveLength(0);
46
- });
47
- });
File without changes