hazo_dataviz 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 (91) hide show
  1. package/CHANGE_LOG.md +54 -0
  2. package/README.md +115 -0
  3. package/SETUP_CHECKLIST.md +54 -0
  4. package/dist/api/index.d.ts +4 -0
  5. package/dist/api/index.d.ts.map +1 -0
  6. package/dist/api/index.js +11 -0
  7. package/dist/charts/line_chart.d.ts +14 -0
  8. package/dist/charts/line_chart.d.ts.map +1 -0
  9. package/dist/charts/line_chart.js +26 -0
  10. package/dist/charts/multi_line_chart.d.ts +13 -0
  11. package/dist/charts/multi_line_chart.d.ts.map +1 -0
  12. package/dist/charts/multi_line_chart.js +32 -0
  13. package/dist/hooks/use_data_query.d.ts +11 -0
  14. package/dist/hooks/use_data_query.d.ts.map +1 -0
  15. package/dist/hooks/use_data_query.js +35 -0
  16. package/dist/index.client.d.ts +17 -0
  17. package/dist/index.client.d.ts.map +1 -0
  18. package/dist/index.client.js +11 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +2 -0
  22. package/dist/lib/auto_encode.d.ts +24 -0
  23. package/dist/lib/auto_encode.d.ts.map +1 -0
  24. package/dist/lib/auto_encode.js +46 -0
  25. package/dist/lib/contract-version.d.ts +2 -0
  26. package/dist/lib/contract-version.d.ts.map +1 -0
  27. package/dist/lib/contract-version.js +1 -0
  28. package/dist/lib/format.d.ts +5 -0
  29. package/dist/lib/format.d.ts.map +1 -0
  30. package/dist/lib/format.js +31 -0
  31. package/dist/lib/index.d.ts +3 -0
  32. package/dist/lib/index.d.ts.map +1 -0
  33. package/dist/lib/index.js +2 -0
  34. package/dist/lib/picker.d.ts +4 -0
  35. package/dist/lib/picker.d.ts.map +1 -0
  36. package/dist/lib/picker.js +20 -0
  37. package/dist/lib/runners/in_memory.d.ts +4 -0
  38. package/dist/lib/runners/in_memory.d.ts.map +1 -0
  39. package/dist/lib/runners/in_memory.js +303 -0
  40. package/dist/lib/shape.d.ts +11 -0
  41. package/dist/lib/shape.d.ts.map +1 -0
  42. package/dist/lib/shape.js +27 -0
  43. package/dist/schema/descriptor.d.ts +162 -0
  44. package/dist/schema/descriptor.d.ts.map +1 -0
  45. package/dist/schema/descriptor.js +56 -0
  46. package/dist/schema/query.d.ts +96 -0
  47. package/dist/schema/query.d.ts.map +1 -0
  48. package/dist/schema/query.js +37 -0
  49. package/dist/testing/fixtures.d.ts +163 -0
  50. package/dist/testing/fixtures.d.ts.map +1 -0
  51. package/dist/testing/fixtures.js +59 -0
  52. package/dist/testing/index.d.ts +3 -0
  53. package/dist/testing/index.d.ts.map +1 -0
  54. package/dist/testing/index.js +2 -0
  55. package/dist/types/index.d.ts +62 -0
  56. package/dist/types/index.d.ts.map +1 -0
  57. package/dist/types/index.js +1 -0
  58. package/dist/visualizers/area.d.ts +3 -0
  59. package/dist/visualizers/area.d.ts.map +1 -0
  60. package/dist/visualizers/area.js +66 -0
  61. package/dist/visualizers/bar.d.ts +3 -0
  62. package/dist/visualizers/bar.d.ts.map +1 -0
  63. package/dist/visualizers/bar.js +41 -0
  64. package/dist/visualizers/builtins.d.ts +3 -0
  65. package/dist/visualizers/builtins.d.ts.map +1 -0
  66. package/dist/visualizers/builtins.js +7 -0
  67. package/dist/visualizers/chart_frame.d.ts +97 -0
  68. package/dist/visualizers/chart_frame.d.ts.map +1 -0
  69. package/dist/visualizers/chart_frame.js +146 -0
  70. package/dist/visualizers/index.d.ts +7 -0
  71. package/dist/visualizers/index.d.ts.map +1 -0
  72. package/dist/visualizers/index.js +4 -0
  73. package/dist/visualizers/line.d.ts +3 -0
  74. package/dist/visualizers/line.d.ts.map +1 -0
  75. package/dist/visualizers/line.js +66 -0
  76. package/dist/visualizers/palette.d.ts +13 -0
  77. package/dist/visualizers/palette.d.ts.map +1 -0
  78. package/dist/visualizers/palette.js +24 -0
  79. package/dist/visualizers/single_stat.d.ts +3 -0
  80. package/dist/visualizers/single_stat.d.ts.map +1 -0
  81. package/dist/visualizers/single_stat.js +60 -0
  82. package/dist/visualizers/stacked_bar.d.ts +3 -0
  83. package/dist/visualizers/stacked_bar.d.ts.map +1 -0
  84. package/dist/visualizers/stacked_bar.js +75 -0
  85. package/dist/visualizers/table.d.ts +3 -0
  86. package/dist/visualizers/table.d.ts.map +1 -0
  87. package/dist/visualizers/table.js +55 -0
  88. package/dist/visualizers/visualizer_view.d.ts +12 -0
  89. package/dist/visualizers/visualizer_view.d.ts.map +1 -0
  90. package/dist/visualizers/visualizer_view.js +6 -0
  91. package/package.json +88 -0
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ import { fromZodValidation } from 'hazo_core';
3
+ import { AggSchema, TimeGrainSchema } from './descriptor.js';
4
+ export const FilterOpSchema = z.enum([
5
+ 'eq', 'neq', 'in', 'gt', 'gte', 'lt', 'lte', 'between', 'is_null',
6
+ ]);
7
+ export const MeasureRefSchema = z.object({
8
+ field: z.string().min(1),
9
+ agg: AggSchema.optional(),
10
+ });
11
+ export const FilterSchema = z.object({
12
+ field: z.string().min(1),
13
+ op: FilterOpSchema,
14
+ value: z.unknown(),
15
+ });
16
+ export const SortSchema = z.object({
17
+ field: z.string().min(1),
18
+ dir: z.enum(['asc', 'desc']),
19
+ });
20
+ export const QuerySchema = z.object({
21
+ measures: z.array(MeasureRefSchema),
22
+ dimensions: z.array(z.string()),
23
+ filters: z.array(FilterSchema).optional(),
24
+ timeGrain: TimeGrainSchema.optional(),
25
+ sort: z.array(SortSchema).optional(),
26
+ limit: z.number().int().positive().optional(),
27
+ }).refine((q) => q.measures.length > 0 || q.dimensions.length > 0, { message: 'Query must have at least one measure or dimension' });
28
+ export function parseQuery(input) {
29
+ const result = QuerySchema.safeParse(input);
30
+ if (!result.success) {
31
+ throw fromZodValidation(result.error, {
32
+ pkg: 'hazo_dataviz',
33
+ code: 'HAZO_DATAVIZ_INVALID_QUERY',
34
+ });
35
+ }
36
+ return result.data;
37
+ }
@@ -0,0 +1,163 @@
1
+ export declare const pricesDescriptor: {
2
+ name: string;
3
+ version: string;
4
+ grain: string[];
5
+ fields: Record<string, {
6
+ role: "measure";
7
+ agg: "sum" | "avg" | "min" | "max" | "last" | "count";
8
+ format?: {
9
+ kind: "currency";
10
+ ccy: string;
11
+ } | {
12
+ kind: "percent";
13
+ } | {
14
+ kind: "number";
15
+ decimals?: number | undefined;
16
+ } | {
17
+ kind: "compact";
18
+ } | {
19
+ kind: "date";
20
+ dateFormat?: string | undefined;
21
+ } | undefined;
22
+ } | {
23
+ role: "dimension";
24
+ type: "category" | "time";
25
+ timeGrains?: ("day" | "week" | "month" | "quarter" | "year")[] | undefined;
26
+ format?: {
27
+ kind: "currency";
28
+ ccy: string;
29
+ } | {
30
+ kind: "percent";
31
+ } | {
32
+ kind: "number";
33
+ decimals?: number | undefined;
34
+ } | {
35
+ kind: "compact";
36
+ } | {
37
+ kind: "date";
38
+ dateFormat?: string | undefined;
39
+ } | undefined;
40
+ }>;
41
+ defaults?: {
42
+ measures?: string[] | undefined;
43
+ dimensions?: string[] | undefined;
44
+ split?: string | undefined;
45
+ viz?: string | undefined;
46
+ } | undefined;
47
+ joinable?: {
48
+ key: string;
49
+ with?: string | undefined;
50
+ }[] | undefined;
51
+ };
52
+ export declare const pricesRows: Record<string, unknown>[];
53
+ export declare const sectorDescriptor: {
54
+ name: string;
55
+ version: string;
56
+ grain: string[];
57
+ fields: Record<string, {
58
+ role: "measure";
59
+ agg: "sum" | "avg" | "min" | "max" | "last" | "count";
60
+ format?: {
61
+ kind: "currency";
62
+ ccy: string;
63
+ } | {
64
+ kind: "percent";
65
+ } | {
66
+ kind: "number";
67
+ decimals?: number | undefined;
68
+ } | {
69
+ kind: "compact";
70
+ } | {
71
+ kind: "date";
72
+ dateFormat?: string | undefined;
73
+ } | undefined;
74
+ } | {
75
+ role: "dimension";
76
+ type: "category" | "time";
77
+ timeGrains?: ("day" | "week" | "month" | "quarter" | "year")[] | undefined;
78
+ format?: {
79
+ kind: "currency";
80
+ ccy: string;
81
+ } | {
82
+ kind: "percent";
83
+ } | {
84
+ kind: "number";
85
+ decimals?: number | undefined;
86
+ } | {
87
+ kind: "compact";
88
+ } | {
89
+ kind: "date";
90
+ dateFormat?: string | undefined;
91
+ } | undefined;
92
+ }>;
93
+ defaults?: {
94
+ measures?: string[] | undefined;
95
+ dimensions?: string[] | undefined;
96
+ split?: string | undefined;
97
+ viz?: string | undefined;
98
+ } | undefined;
99
+ joinable?: {
100
+ key: string;
101
+ with?: string | undefined;
102
+ }[] | undefined;
103
+ };
104
+ export declare const sectorRows: Record<string, unknown>[];
105
+ export declare const sparseDescriptor: {
106
+ name: string;
107
+ version: string;
108
+ grain: string[];
109
+ fields: Record<string, {
110
+ role: "measure";
111
+ agg: "sum" | "avg" | "min" | "max" | "last" | "count";
112
+ format?: {
113
+ kind: "currency";
114
+ ccy: string;
115
+ } | {
116
+ kind: "percent";
117
+ } | {
118
+ kind: "number";
119
+ decimals?: number | undefined;
120
+ } | {
121
+ kind: "compact";
122
+ } | {
123
+ kind: "date";
124
+ dateFormat?: string | undefined;
125
+ } | undefined;
126
+ } | {
127
+ role: "dimension";
128
+ type: "category" | "time";
129
+ timeGrains?: ("day" | "week" | "month" | "quarter" | "year")[] | undefined;
130
+ format?: {
131
+ kind: "currency";
132
+ ccy: string;
133
+ } | {
134
+ kind: "percent";
135
+ } | {
136
+ kind: "number";
137
+ decimals?: number | undefined;
138
+ } | {
139
+ kind: "compact";
140
+ } | {
141
+ kind: "date";
142
+ dateFormat?: string | undefined;
143
+ } | undefined;
144
+ }>;
145
+ defaults?: {
146
+ measures?: string[] | undefined;
147
+ dimensions?: string[] | undefined;
148
+ split?: string | undefined;
149
+ viz?: string | undefined;
150
+ } | undefined;
151
+ joinable?: {
152
+ key: string;
153
+ with?: string | undefined;
154
+ }[] | undefined;
155
+ };
156
+ export declare const sparseRows: Record<string, unknown>[];
157
+ export declare const badDescriptorInput: {
158
+ name: string;
159
+ version: string;
160
+ grain: never[];
161
+ fields: {};
162
+ };
163
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/testing/fixtures.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAW3B,CAAC;AAEH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAQ/C,CAAC;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQ3B,CAAC;AAEH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAI/C,CAAC;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAS3B,CAAC;AAEH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAK/C,CAAC;AAGF,eAAO,MAAM,kBAAkB;;;;;CAK9B,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { parseDescriptor } from '../schema/descriptor.js';
2
+ export const pricesDescriptor = parseDescriptor({
3
+ name: 'prices',
4
+ version: '1',
5
+ grain: ['date', 'symbol'],
6
+ fields: {
7
+ date: { role: 'dimension', type: 'time', timeGrains: ['day', 'week', 'month', 'quarter', 'year'] },
8
+ symbol: { role: 'dimension', type: 'category' },
9
+ close: { role: 'measure', agg: 'last', format: { kind: 'number', decimals: 2 } },
10
+ volume: { role: 'measure', agg: 'sum' },
11
+ },
12
+ defaults: { measures: ['close'], dimensions: ['date'], split: 'symbol' },
13
+ });
14
+ export const pricesRows = [
15
+ { date: '2024-01-01', symbol: 'AAPL', close: 185, volume: 1000 },
16
+ { date: '2024-01-02', symbol: 'AAPL', close: 190, volume: 1200 },
17
+ { date: '2024-01-03', symbol: 'AAPL', close: 188, volume: 900 },
18
+ { date: '2024-01-01', symbol: 'MSFT', close: 370, volume: 800 },
19
+ { date: '2024-01-02', symbol: 'MSFT', close: 375, volume: 850 },
20
+ { date: '2024-02-01', symbol: 'AAPL', close: 192, volume: 1100 },
21
+ { date: '2024-02-01', symbol: 'MSFT', close: 380, volume: 900 },
22
+ ];
23
+ export const sectorDescriptor = parseDescriptor({
24
+ name: 'sector_breakdown',
25
+ version: '1',
26
+ grain: ['sector'],
27
+ fields: {
28
+ sector: { role: 'dimension', type: 'category' },
29
+ revenue: { role: 'measure', agg: 'sum', format: { kind: 'currency', ccy: 'USD' } },
30
+ },
31
+ });
32
+ export const sectorRows = [
33
+ { sector: 'Tech', revenue: 100 },
34
+ { sector: 'Tech', revenue: 200 },
35
+ { sector: 'Finance', revenue: 150 },
36
+ ];
37
+ export const sparseDescriptor = parseDescriptor({
38
+ name: 'sparse',
39
+ version: '1',
40
+ grain: ['id'],
41
+ fields: {
42
+ category: { role: 'dimension', type: 'category' },
43
+ value: { role: 'measure', agg: 'sum' },
44
+ other: { role: 'measure', agg: 'avg' },
45
+ },
46
+ });
47
+ export const sparseRows = [
48
+ { category: 'A', value: 10, other: null },
49
+ { category: 'A', value: null, other: 5 },
50
+ { category: 'B', value: null, other: null },
51
+ { category: 'A', value: 20, other: 15 },
52
+ ];
53
+ // bad_descriptor: fails parseDescriptor (no fields, empty grain)
54
+ export const badDescriptorInput = {
55
+ name: 'bad',
56
+ version: '1',
57
+ grain: [], // must have at least 1
58
+ fields: {},
59
+ };
@@ -0,0 +1,3 @@
1
+ export { pricesDescriptor, pricesRows, sectorDescriptor, sectorRows, sparseDescriptor, sparseRows, badDescriptorInput, } from './fixtures.js';
2
+ export { createInMemoryRunner } from '../lib/runners/in_memory.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { pricesDescriptor, pricesRows, sectorDescriptor, sectorRows, sparseDescriptor, sparseRows, badDescriptorInput, } from './fixtures.js';
2
+ export { createInMemoryRunner } from '../lib/runners/in_memory.js';
@@ -0,0 +1,62 @@
1
+ import type * as React from 'react';
2
+ import type { Agg, Format, TimeGrain, DatasetDescriptor } from '../schema/descriptor.js';
3
+ import type { Query } from '../schema/query.js';
4
+ /** One series value; null is a gap the chart skips. */
5
+ export type ChartDataPoint = number | null;
6
+ /** Ordered series for a single line. */
7
+ export type ChartDataSeries = ChartDataPoint[];
8
+ /** Multi-line series with its own color + label. */
9
+ export interface MultiSeries {
10
+ label: string;
11
+ color: string;
12
+ data: ChartDataSeries;
13
+ }
14
+ export type { Agg, Format, TimeGrain, Query };
15
+ export interface Column {
16
+ key: string;
17
+ role: 'measure' | 'dimension';
18
+ label: string;
19
+ format?: Format;
20
+ agg?: Agg;
21
+ }
22
+ export interface Result {
23
+ rows: Record<string, unknown>[];
24
+ columns: Column[];
25
+ query: Query;
26
+ }
27
+ export interface Encoding {
28
+ x: string;
29
+ series?: string;
30
+ y: string[];
31
+ }
32
+ export type QueryRunner = (query: Query) => Promise<Result>;
33
+ export interface VisualizerOpts {
34
+ width?: number;
35
+ height?: number;
36
+ colors?: string[];
37
+ showLegend?: boolean;
38
+ showTooltip?: boolean;
39
+ }
40
+ export interface Visualizer {
41
+ id: 'line' | 'bar' | 'stacked_bar' | 'area' | 'table' | 'single_stat' | (string & {});
42
+ label: string;
43
+ /** 0 = cannot render; higher = better fit. Pure, no React. */
44
+ accepts(descriptor: DatasetDescriptor, query: Query, result?: Result): number;
45
+ /** client-only React renderer */
46
+ render(result: Result, encoding: Encoding, opts?: VisualizerOpts): React.ReactElement;
47
+ }
48
+ export interface PickerMeasure {
49
+ field: string;
50
+ aggs: Agg[];
51
+ }
52
+ export interface PickerDimension {
53
+ field: string;
54
+ type: 'category' | 'time';
55
+ timeGrains?: TimeGrain[];
56
+ }
57
+ export interface PickerOptions {
58
+ measures: PickerMeasure[];
59
+ dimensions: PickerDimension[];
60
+ filterableFields: string[];
61
+ }
62
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAMhD,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,IAAI,CAAC;AAC3C,wCAAwC;AACxC,MAAM,MAAM,eAAe,GAAG,cAAc,EAAE,CAAC;AAC/C,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAE9C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,GAAG,WAAW,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,CAAC,EAAE,MAAM,EAAE,CAAC;CACb;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,OAAO,CAAC,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9E,iCAAiC;IACjC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAAC;CACvF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { Visualizer } from '../types/index.js';
2
+ export declare const area: Visualizer;
3
+ //# sourceMappingURL=area.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area.d.ts","sourceRoot":"","sources":["../../src/visualizers/area.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAoC,MAAM,mBAAmB,CAAC;AA+CtF,eAAO,MAAM,IAAI,EAAE,UAgFlB,CAAC"}
@@ -0,0 +1,66 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, } from 'recharts';
4
+ import { classifyShape } from '../lib/shape.js';
5
+ import { pickColor } from './palette.js';
6
+ import { makeFormatter, makeTooltip, EmptyState, axisProps, gridProps, legendProps, CHART_MARGIN, CHART_THEME, } from './chart_frame.js';
7
+ function pivotData(result, encoding) {
8
+ if (!encoding.series) {
9
+ return { data: result.rows, seriesKeys: encoding.y };
10
+ }
11
+ const xKey = encoding.x;
12
+ const seriesKey = encoding.series;
13
+ const measureKey = encoding.y[0] ?? '';
14
+ const seriesValues = Array.from(new Set(result.rows.map((r) => String(r[seriesKey] ?? ''))));
15
+ const grouped = new Map();
16
+ for (const row of result.rows) {
17
+ const xVal = row[xKey];
18
+ if (!grouped.has(xVal)) {
19
+ grouped.set(xVal, { [xKey]: xVal });
20
+ }
21
+ const entry = grouped.get(xVal);
22
+ const sv = String(row[seriesKey] ?? '');
23
+ entry[sv] = row[measureKey];
24
+ }
25
+ return {
26
+ data: Array.from(grouped.values()),
27
+ seriesKeys: seriesValues,
28
+ };
29
+ }
30
+ export const area = {
31
+ id: 'area',
32
+ label: 'Area Chart',
33
+ accepts(descriptor, query) {
34
+ const shape = classifyShape(descriptor, query);
35
+ if (shape.temporalDims.length >= 1 && shape.measureCount >= 1)
36
+ return 0.5;
37
+ return 0;
38
+ },
39
+ render(result, encoding, opts) {
40
+ if (result.rows.length === 0)
41
+ return _jsx(EmptyState, {});
42
+ const xCol = result.columns.find((c) => c.key === encoding.x);
43
+ if (!xCol && encoding.x) {
44
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
45
+ console.warn(`[hazo_dataviz/area] encoding.x "${encoding.x}" not found in result.columns`);
46
+ }
47
+ return _jsx(EmptyState, {});
48
+ }
49
+ const { data, seriesKeys } = pivotData(result, encoding);
50
+ const xFormatter = makeFormatter(xCol);
51
+ const showLegend = opts?.showLegend ?? seriesKeys.length > 1;
52
+ const showTooltip = opts?.showTooltip ?? true;
53
+ const yCol = result.columns.find((c) => c.role === 'measure');
54
+ const yFormatter = makeFormatter(yCol);
55
+ const chartHeight = opts?.height ?? 300;
56
+ const chartWidth = opts?.width;
57
+ const chart = (_jsxs(AreaChart, { data: data, width: chartWidth, height: chartWidth !== undefined ? chartHeight : undefined, margin: CHART_MARGIN, children: [_jsx("defs", { children: seriesKeys.map((key, i) => {
58
+ const color = pickColor(i, opts?.colors);
59
+ return (_jsxs("linearGradient", { id: `hzdv-area-${i}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: color, stopOpacity: 0.34 }), _jsx("stop", { offset: "100%", stopColor: color, stopOpacity: 0.02 })] }, key));
60
+ }) }), _jsx(CartesianGrid, { ...gridProps }), _jsx(XAxis, { dataKey: encoding.x, tickFormatter: xFormatter, ...axisProps }), _jsx(YAxis, { tickFormatter: yFormatter, ...axisProps, width: 48 }), showTooltip && (_jsx(Tooltip, { cursor: { stroke: CHART_THEME.hairline, strokeWidth: 1.5 }, content: makeTooltip(xFormatter, yFormatter) })), showLegend && _jsx(Legend, { ...legendProps }), seriesKeys.map((key, i) => (_jsx(Area, { type: "monotone", dataKey: key, stroke: pickColor(i, opts?.colors), strokeWidth: CHART_THEME.strokeWidth, fill: `url(#hzdv-area-${i})`, activeDot: { r: 4, strokeWidth: 2, stroke: CHART_THEME.surface } }, key)))] }));
61
+ if (chartWidth !== undefined) {
62
+ return (_jsx("div", { style: { width: chartWidth, height: chartHeight, overflow: 'hidden' }, children: chart }));
63
+ }
64
+ return (_jsx(ResponsiveContainer, { width: "100%", height: chartHeight, children: chart }));
65
+ },
66
+ };
@@ -0,0 +1,3 @@
1
+ import type { Visualizer } from '../types/index.js';
2
+ export declare const bar: Visualizer;
3
+ //# sourceMappingURL=bar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bar.d.ts","sourceRoot":"","sources":["../../src/visualizers/bar.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAoC,MAAM,mBAAmB,CAAC;AAetF,eAAO,MAAM,GAAG,EAAE,UA+DjB,CAAC"}
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, } from 'recharts';
4
+ import { classifyShape } from '../lib/shape.js';
5
+ import { pickColor } from './palette.js';
6
+ import { makeFormatter, makeTooltip, EmptyState, axisProps, gridProps, legendProps, CHART_MARGIN, } from './chart_frame.js';
7
+ export const bar = {
8
+ id: 'bar',
9
+ label: 'Bar Chart',
10
+ accepts(descriptor, query) {
11
+ const shape = classifyShape(descriptor, query);
12
+ if (shape.dimCount === 1 && shape.categoricalDims.length === 1 && shape.measureCount >= 1) {
13
+ return 0.9;
14
+ }
15
+ return 0;
16
+ },
17
+ render(result, encoding, opts) {
18
+ if (result.rows.length === 0)
19
+ return _jsx(EmptyState, {});
20
+ const xCol = result.columns.find((c) => c.key === encoding.x);
21
+ if (!xCol && encoding.x) {
22
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
23
+ console.warn(`[hazo_dataviz/bar] encoding.x "${encoding.x}" not found in result.columns`);
24
+ }
25
+ return _jsx(EmptyState, {});
26
+ }
27
+ const yKeys = encoding.y;
28
+ const xFormatter = makeFormatter(xCol);
29
+ const showLegend = opts?.showLegend ?? yKeys.length > 1;
30
+ const showTooltip = opts?.showTooltip ?? true;
31
+ const yCol = result.columns.find((c) => c.key === yKeys[0]);
32
+ const yFormatter = makeFormatter(yCol);
33
+ const chartHeight = opts?.height ?? 300;
34
+ const chartWidth = opts?.width;
35
+ const chart = (_jsxs(BarChart, { data: result.rows, width: chartWidth, height: chartWidth !== undefined ? chartHeight : undefined, margin: CHART_MARGIN, barCategoryGap: "28%", children: [_jsx(CartesianGrid, { ...gridProps }), _jsx(XAxis, { dataKey: encoding.x, tickFormatter: xFormatter, ...axisProps }), _jsx(YAxis, { tickFormatter: yFormatter, ...axisProps, width: 48 }), showTooltip && (_jsx(Tooltip, { cursor: { fill: 'rgba(47, 42, 37, 0.05)' }, content: makeTooltip(xFormatter, yFormatter) })), showLegend && _jsx(Legend, { ...legendProps }), yKeys.map((key, i) => (_jsx(Bar, { dataKey: key, fill: pickColor(i, opts?.colors), radius: [6, 6, 0, 0], maxBarSize: 64 }, key)))] }));
36
+ if (chartWidth !== undefined) {
37
+ return (_jsx("div", { style: { width: chartWidth, height: chartHeight, overflow: 'hidden' }, children: chart }));
38
+ }
39
+ return (_jsx(ResponsiveContainer, { width: "100%", height: chartHeight, children: chart }));
40
+ },
41
+ };
@@ -0,0 +1,3 @@
1
+ import type { Visualizer } from '../types/index.js';
2
+ export declare const builtinVisualizers: Visualizer[];
3
+ //# sourceMappingURL=builtins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../../src/visualizers/builtins.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,eAAO,MAAM,kBAAkB,EAAE,UAAU,EAAqD,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { line } from './line.js';
2
+ import { bar } from './bar.js';
3
+ import { stackedBar } from './stacked_bar.js';
4
+ import { area } from './area.js';
5
+ import { table } from './table.js';
6
+ import { singleStat } from './single_stat.js';
7
+ export const builtinVisualizers = [line, bar, stackedBar, area, table, singleStat];
@@ -0,0 +1,97 @@
1
+ import * as React from 'react';
2
+ import type { Column, VisualizerOpts } from '../types/index.js';
3
+ /**
4
+ * "Studio" chart theme — a warm, editorial token set shared by every recharts
5
+ * visualizer so the package renders one coherent look out of the box.
6
+ *
7
+ * These are plain hex/number tokens (not Tailwind) on purpose: the charts are
8
+ * SVG and must look right in any consumer app regardless of its CSS pipeline.
9
+ * Font family is deliberately omitted so SVG text inherits the host page font.
10
+ */
11
+ export declare const CHART_THEME: {
12
+ /** Primary text — warm near-black. */
13
+ readonly ink: "#2f2a25";
14
+ /** Axis ticks / secondary labels — warm grey. */
15
+ readonly muted: "#9a9087";
16
+ /** Hairline grid + axis lines — warm sand. */
17
+ readonly hairline: "#ece6dd";
18
+ /** Tooltip surface — warm white. */
19
+ readonly surface: "#fffdf9";
20
+ /** Tooltip / card border. */
21
+ readonly surfaceBorder: "#e7ddd0";
22
+ /** Base tick font size. */
23
+ readonly fontSize: 12;
24
+ /** Default series stroke width. */
25
+ readonly strokeWidth: 2.5;
26
+ };
27
+ /** Shared inner margin so axis labels never clip against a card edge. */
28
+ export declare const CHART_MARGIN: {
29
+ readonly top: 12;
30
+ readonly right: 16;
31
+ readonly bottom: 4;
32
+ readonly left: 4;
33
+ };
34
+ /** Spread onto `<XAxis>` / `<YAxis>` for the Studio look (hairline, no tick marks). */
35
+ export declare const axisProps: {
36
+ readonly tick: {
37
+ readonly fill: "#9a9087";
38
+ readonly fontSize: 12;
39
+ };
40
+ readonly axisLine: {
41
+ readonly stroke: "#ece6dd";
42
+ };
43
+ readonly tickLine: false;
44
+ readonly tickMargin: 8;
45
+ };
46
+ /** Spread onto `<CartesianGrid>` — horizontal hairlines only, no vertical clutter. */
47
+ export declare const gridProps: {
48
+ readonly stroke: "#ece6dd";
49
+ readonly vertical: false;
50
+ };
51
+ /**
52
+ * Returns a recharts tick/value formatter for a given column.
53
+ * Uses formatValue when the column has a format, else a sane default.
54
+ */
55
+ export declare function makeFormatter(column: Column | undefined): (v: unknown) => string;
56
+ /**
57
+ * Returns the chart height to use. When opts has explicit height, use it;
58
+ * otherwise default to 300.
59
+ */
60
+ export declare function resolveChartDimensions(opts: VisualizerOpts | undefined): {
61
+ width?: number;
62
+ height: number;
63
+ };
64
+ interface TooltipPayloadEntry {
65
+ name?: string | number;
66
+ value?: unknown;
67
+ color?: string;
68
+ dataKey?: string | number;
69
+ }
70
+ interface TooltipRenderProps {
71
+ active?: boolean;
72
+ payload?: TooltipPayloadEntry[];
73
+ label?: unknown;
74
+ }
75
+ /**
76
+ * Builds a recharts `content` renderer for `<Tooltip>` styled to match the
77
+ * Studio theme — a warm floating card with a colored swatch per series.
78
+ *
79
+ * @param labelFormatter formats the x value shown as the tooltip heading
80
+ * @param valueFormatter formats each series value
81
+ */
82
+ export declare function makeTooltip(labelFormatter: (v: unknown) => string, valueFormatter: (v: unknown) => string): (props: TooltipRenderProps) => any;
83
+ /** Props spread onto `<Legend>` for the Studio look. */
84
+ export declare const legendProps: {
85
+ iconType: "circle";
86
+ wrapperStyle: {
87
+ fontSize: number;
88
+ color: "#9a9087";
89
+ paddingTop: number;
90
+ };
91
+ };
92
+ /**
93
+ * Empty state element returned when result.rows is empty.
94
+ */
95
+ export declare function EmptyState(): React.ReactElement;
96
+ export {};
97
+ //# sourceMappingURL=chart_frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart_frame.d.ts","sourceRoot":"","sources":["../../src/visualizers/chart_frame.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;IACtB,sCAAsC;;IAEtC,iDAAiD;;IAEjD,8CAA8C;;IAE9C,oCAAoC;;IAEpC,6BAA6B;;IAE7B,2BAA2B;;IAE3B,mCAAmC;;CAE3B,CAAC;AAEX,yEAAyE;AACzE,eAAO,MAAM,YAAY;;;;;CAAsD,CAAC;AAEhF,uFAAuF;AACvF,eAAO,MAAM,SAAS;;;;;;;;;;CAKZ,CAAC;AAEX,sFAAsF;AACtF,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,CAYhF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAK3G;AAED,UAAU,mBAAmB;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;GAMG;AAMH,wBAAgB,WAAW,CACzB,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,EACtC,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GACrC,CAAC,KAAK,EAAE,kBAAkB,KAAK,GAAG,CAqEpC;AAED,wDAAwD;AACxD,eAAO,MAAM,WAAW;;;;;;;CAOvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,IAAI,KAAK,CAAC,YAAY,CAqB/C"}