@altertable/data-app 0.67.0 → 0.68.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.
@@ -59,6 +59,18 @@ export declare const dataAppStyleClasses: {
59
59
  readonly component: 'ChartLegend.Label';
60
60
  readonly kind: 'root';
61
61
  };
62
+ readonly 'altertable-funnel-chart': {
63
+ readonly component: 'FunnelChart';
64
+ readonly kind: 'root';
65
+ };
66
+ readonly 'altertable-retention-chart': {
67
+ readonly component: 'RetentionChart';
68
+ readonly kind: 'root';
69
+ };
70
+ readonly 'altertable-journey-chart': {
71
+ readonly component: 'JourneyChart';
72
+ readonly kind: 'root';
73
+ };
62
74
  readonly 'altertable-composed-chart': {
63
75
  readonly component: 'ComposedChart';
64
76
  readonly kind: 'root';
@@ -14,7 +14,7 @@ export type ChartLegendItemProps = ComponentPropsWithRef<'li'> & {
14
14
  };
15
15
  /** Descriptive by default; compose a button inside the item for an authored series toggle. */
16
16
  declare function ChartLegendItem({ inactive, className, ...props }: ChartLegendItemProps): import("react").JSX.Element;
17
- export type ChartLegendMarkerKind = 'bar' | 'line' | 'area' | 'point' | 'slice';
17
+ export type ChartLegendMarkerKind = 'square' | 'bar' | 'line' | 'area' | 'point' | 'slice';
18
18
  export type ChartLegendMarkerProps = Omit<ComponentPropsWithRef<'svg'>, 'children'> & {
19
19
  kind: ChartLegendMarkerKind;
20
20
  };
@@ -1,4 +1,4 @@
1
- import type { ComponentProps } from 'react';
1
+ import { type ComponentProps } from 'react';
2
2
  import { ComposedChartLegend as Legend } from './ComposedChartLegend.js';
3
3
  import { ComposedChart as RechartsComposedChart, ZAxis, ReferenceLine, ReferenceArea, ReferenceDot, Brush, Label, LabelList, ErrorBar } from 'recharts';
4
4
  import { ChartBar as Bar, ChartDot as Dot, ChartLine as Line, ChartArea as Area, ChartScatter as Scatter, ChartXAxis as XAxis, ChartYAxis as YAxis, ChartGrid as CartesianGrid, ChartTooltip as Tooltip } from './chart-primitives.js';
@@ -0,0 +1,20 @@
1
+ import type { CountChartProps } from './chart-data.js';
2
+ /** Ordered step with a unique, nonblank ID. */
3
+ export type FunnelChartStep = {
4
+ id: string;
5
+ label: string;
6
+ };
7
+ export type FunnelChartSeries = {
8
+ /** Unique, nonblank population ID. */
9
+ id: string;
10
+ label: string;
11
+ /** Finite, nonnegative counts aligned with steps and nonincreasing. */
12
+ values: readonly number[];
13
+ };
14
+ export type FunnelChartProps = CountChartProps & {
15
+ steps: readonly FunnelChartStep[];
16
+ series: readonly FunnelChartSeries[];
17
+ };
18
+ /** Conversion columns with previous-step drop-off, normalized to each series' first step.
19
+ * Owns series highlighting and portion-specific inspection; compose inside VisualizationWidget. */
20
+ export declare function FunnelChart({ steps, series, unit, ariaLabel, className, formatValue, }: FunnelChartProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { CountChartProps } from './chart-data.js';
2
+ import { type JourneyChartPath } from './journey-flow.js';
3
+ export type { JourneyChartPath } from './journey-flow.js';
4
+ export type JourneyChartProps = CountChartProps & {
5
+ /** Full paths and their population counts. Branches and outcomes are derived internally. */
6
+ paths: readonly JourneyChartPath[];
7
+ };
8
+ /** Path exploration with two initial steps, expandable prefixes, and three events per depth.
9
+ * Counts and flows use the Step 1 population. Compose inside VisualizationWidget. */
10
+ export declare function JourneyChart({ paths, unit, ariaLabel, className, formatValue, }: JourneyChartProps): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import type { CountChartProps } from './chart-data.js';
2
+ export type RetentionChartPoint = {
3
+ /** Nonnegative integer, unique per series; uneven offsets retain their distance. */
4
+ offset: number;
5
+ /** Must agree across series for the same offset. */
6
+ label: string;
7
+ /** Observed retention fraction in [0, 1], or null for an unobserved period. */
8
+ rate: number | null;
9
+ /** Finite count in [0, cohortSize]; null exactly when rate is null. */
10
+ retainedCount: number | null;
11
+ /** Observed but still accumulating; drawn as a dotted tail. */
12
+ incomplete?: boolean;
13
+ };
14
+ export type RetentionChartSeries = {
15
+ /** Unique, nonblank cohort or segment ID. */
16
+ id: string;
17
+ label: string;
18
+ /** Finite, nonnegative starting population. */
19
+ cohortSize: number;
20
+ points: readonly RetentionChartPoint[];
21
+ };
22
+ export type RetentionChartProps = CountChartProps & {
23
+ series: readonly RetentionChartSeries[];
24
+ };
25
+ /** Query-defined retention curves with count inspection and dotted incomplete periods.
26
+ * Rates are not recomputed from counts. Compose inside VisualizationWidget. */
27
+ export declare function RetentionChart({ series, unit, ariaLabel, className, formatValue, }: RetentionChartProps): import("react").JSX.Element;
@@ -11,6 +11,15 @@ export type ValueChartProps = {
11
11
  ariaLabel: string;
12
12
  formatValue?: (value: number) => string;
13
13
  };
14
+ export type CountChartProps = {
15
+ /** Accessible measure and scope description. */
16
+ ariaLabel: string;
17
+ /** Population unit, such as users or sessions. */
18
+ unit: string;
19
+ /** Formats counts; rates use percentage formatting. */
20
+ formatValue?: (value: number) => string;
21
+ className?: string;
22
+ };
14
23
  /** Independent observation on two finite numeric axes. */
15
24
  export type ScatterChartItem = {
16
25
  id: string;
@@ -1,6 +1,5 @@
1
1
  import type { ComponentProps } from 'react';
2
- import { type DotProps, Scatter, XAxis, YAxis, CartesianGrid, Tooltip, type BarProps, type LineProps, type AreaProps } from 'recharts';
3
- export declare const chartBarFill = "var(--atbl-chart-fill, color-mix(in srgb, var(--atbl-accent) 75%, var(--atbl-surface)))";
2
+ import { type DotProps, Scatter, XAxis, YAxis, CartesianGrid, Tooltip, DefaultTooltipContent, type BarProps, type LineProps, type AreaProps } from 'recharts';
4
3
  /** Hollow sample marker shared by standalone and composed series. */
5
4
  export declare function ChartDot({ active, stroke, fill, r, ...props }: DotProps & {
6
5
  active?: boolean;
@@ -16,5 +15,8 @@ export declare function ChartScatter<Row = unknown, Value = number>(props: Compo
16
15
  export declare function ChartXAxis<Row = unknown, Value = string | number>(props: ComponentProps<typeof XAxis<Row, Value>>): import("react").JSX.Element;
17
16
  export declare function ChartYAxis<Row = unknown, Value = number>(props: ComponentProps<typeof YAxis<Row, Value>>): import("react").JSX.Element;
18
17
  export declare function ChartGrid(props: ComponentProps<typeof CartesianGrid>): import("react").JSX.Element;
18
+ export declare function ChartTooltipContent(props: ComponentProps<typeof DefaultTooltipContent> & {
19
+ active?: boolean;
20
+ }): import("react").JSX.Element | null;
19
21
  /** Uses the same surface as single-series inspection; Recharts owns interaction. */
20
22
  export declare function ChartTooltip(props: ComponentProps<typeof Tooltip>): import("react").JSX.Element;
@@ -1 +1,2 @@
1
1
  export declare function chartColor(id: string): string;
2
+ export declare const chartBarFill = "var(--atbl-chart-fill, color-mix(in srgb, var(--atbl-accent) 75%, var(--atbl-surface)))";
@@ -96,3 +96,10 @@ export type { ComposedChartProps } from './ComposedChart.js';
96
96
  export { ChartLegend } from './ChartLegend.js';
97
97
  export type { ChartLegendProps, ChartLegendItemProps, ChartLegendMarkerProps, ChartLegendLabelProps, ChartLegendMarkerKind, } from './ChartLegend.js';
98
98
  export type { ComposedChartLegendProps } from './ComposedChartLegend.js';
99
+ export { FunnelChart } from './FunnelChart.js';
100
+ export type { FunnelChartProps, FunnelChartStep, FunnelChartSeries, } from './FunnelChart.js';
101
+ export { RetentionChart } from './RetentionChart.js';
102
+ export type { RetentionChartProps, RetentionChartPoint, RetentionChartSeries, } from './RetentionChart.js';
103
+ export { JourneyChart } from './JourneyChart.js';
104
+ export type { JourneyChartProps, JourneyChartPath, } from './JourneyChart.js';
105
+ export { useSvgId } from './useSvgId.js';
@@ -0,0 +1,49 @@
1
+ /** Aggregated route; repeated events remain distinct by depth and prefix. */
2
+ export type JourneyChartPath = {
3
+ /** Unique, nonblank path ID. */
4
+ id: string;
5
+ /** At least one nonblank event; null property means no property grouping. */
6
+ steps: readonly {
7
+ event: string;
8
+ property: string | null;
9
+ }[];
10
+ /** Finite, nonnegative population following this route. */
11
+ count: number;
12
+ /** True: converted; false: dropped off; null: no conversion classification. */
13
+ converted: boolean | null;
14
+ /** Stops at the last loaded event, without adding an outcome or expand control. */
15
+ truncated: boolean;
16
+ };
17
+ export type JourneyNode = {
18
+ key: string;
19
+ label: string;
20
+ depth: number;
21
+ outcome: 'event' | 'converted' | 'drop-off' | 'truncated' | 'end' | 'other';
22
+ count: number;
23
+ pathKeys: string[];
24
+ branchKeys?: string[];
25
+ expandable?: boolean;
26
+ expanded?: boolean;
27
+ hiddenBranchCount?: number;
28
+ };
29
+ export declare function journeyNodes(path: JourneyChartPath): Omit<JourneyNode, 'count' | 'pathKeys'>[];
30
+ export declare function buildJourneyFlow(paths: readonly JourneyChartPath[], expandedBranch?: string[]): {
31
+ nodes: JourneyNode[];
32
+ links: {
33
+ key: string;
34
+ source: string;
35
+ target: string;
36
+ count: number;
37
+ pathKeys: string[];
38
+ }[];
39
+ };
40
+ export declare function limitJourneyFlow(graph: ReturnType<typeof buildJourneyFlow>, visibleEventsByDepth?: Record<number, number>): {
41
+ nodes: JourneyNode[];
42
+ links: {
43
+ key: string;
44
+ source: string;
45
+ target: string;
46
+ count: number;
47
+ pathKeys: string[];
48
+ }[];
49
+ };
@@ -0,0 +1,2 @@
1
+ /** Stable React ID for SVG definitions and their URL references; safe with identifier prefixes. */
2
+ export declare function useSvgId(): string;
package/dist/worker.js CHANGED
@@ -65,7 +65,7 @@ function trustedParent(config, searchParams) {
65
65
  }
66
66
 
67
67
  // src/worker/index.ts
68
- var TOKEN_RE = /^(?=.{1,63}$)[a-z0-9]+(?:-[a-z0-9]+)+-app-[1-9][0-9]*$/;
68
+ var TOKEN_RE = /^(?=.{1,63}$)[a-z0-9]+(?:-+[a-z0-9]+)+$/;
69
69
  function isPreviewHost(hostname, domainName) {
70
70
  const suffix = `.${domainName}`;
71
71
  return hostname.endsWith(suffix) && TOKEN_RE.test(hostname.slice(0, -suffix.length));
package/docs/contract.md CHANGED
@@ -11,7 +11,8 @@ import operation types using `import type`.
11
11
  Declare SQL once with `defineDataApp()` to preserve exact query and parameter
12
12
  names. Use stable `lowerCamelCase` IDs describing the result, such as `products`,
13
13
  `productsByCategory`, or `dailyRevenue`. Use plural names for row lists; keep parameter
14
- values in `params`.
14
+ values in `params`. Every query must include `params`; use `{}` when it declares no
15
+ parameters.
15
16
  The app owns an immutable registry snapshot. Define operations with
16
17
  `dataApp.defineOperation()`.
17
18
 
package/docs/ui.md CHANGED
@@ -100,6 +100,22 @@ the samples. Pie slices represent mutually exclusive parts of one total; shares
100
100
  use the sum of supplied items, so include Other when showing a subset of the
101
101
  whole. Scatter points represent independent X/Y observations.
102
102
 
103
+ Use `useSvgId()` for stable IDs when composing custom SVG definitions.
104
+
105
+ ### Product analytics charts
106
+
107
+ Use `<FunnelChart>`, `<RetentionChart>`, and `<JourneyChart>` from `/react/ui`
108
+ inside `<VisualizationWidget>`. Derive inputs from the displayed rows; the
109
+ binding owns loading, evidence, CSV, and story content. Types and JSDoc describe
110
+ input constraints; the gallery includes complete examples.
111
+
112
+ - `<FunnelChart>` compares ordered step counts across populations. Each series
113
+ uses its first step as the baseline; hatching shows previous-step drop-off.
114
+ - `<RetentionChart>` plots query-defined rates and retained counts by interval
115
+ offset. Use `null` for unobserved periods; `incomplete` produces a dotted tail.
116
+ - `<JourneyChart>` derives expandable branches and outcomes from full paths.
117
+ Truncated paths stop at the last loaded event.
118
+
103
119
  ### Composed charts
104
120
 
105
121
  Use `<ComposedChart>` from `/react/ui` for multiple series or mixed marks inside
@@ -134,10 +150,12 @@ its columns aligned with the displayed measures.
134
150
 
135
151
  ### Legends
136
152
 
137
- Include `<ComposedChart.Legend />` to derive labels and markers from the series;
153
+ Include `<ComposedChart.Legend />` to derive labels and colored square markers from the series;
138
154
  omit it when no legend is needed. For any chart, compose `<ChartLegend>` with
139
155
  `<ChartLegend.Item>`, `<ChartLegend.Marker>`, and `<ChartLegend.Label>` using the
140
- same names and colors as the plot.
156
+ same names and colors as the plot. Square markers are
157
+ shared with chart tooltips and funnel summaries. Pass `kind="square"` explicitly
158
+ when composing a marker.
141
159
  `<PieChart>` includes a legend by default; `showLegend={false}` omits it.
142
160
 
143
161
  Legends align cells in a container-responsive grid, truncate labels, and reserve
package/docs/widgets.md CHANGED
@@ -28,18 +28,21 @@ Choose the component by the reader's question. Compose dataset visuals inside
28
28
  charts come from `/react/ui`; `<Ranking>`, `<Breakdown>`, and `<Comparison>`
29
29
  are also available from `/react`.
30
30
 
31
- | Question or need | Component | Guidance |
32
- | --------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ |
33
- | How do categories compare? | `<BarChart>` | Compare nonnegative category values; order by value for rank or by a meaningful category order. |
34
- | Which items lead? | `<Ranking>` | Use a compact ordered list with values and optional detail. Tracks scale to the largest visible item, not a total. |
35
- | How does a measure change over time? | `<LineChart>` | Use ordered, equally spaced samples to emphasize the trend. |
36
- | How large is the measure over time? | `<AreaChart>` | Use the same samples as a line chart when filled magnitude relative to zero helps answer the question. |
37
- | What makes up the whole? | `<PieChart>` | Use a few mutually exclusive parts of one total; include the remainder as Other. |
38
- | What share of an observed total does each part represent? | `<Breakdown>` | Use a compact display of values and shares with an explicit total, including when only some parts are shown. |
39
- | How do several measures relate on one plot? | `<ComposedChart>` | Compose bars, lines, areas, or scatter series; label units clearly and use separate axes for different units. |
40
- | How are two numeric measures related? | `<ScatterChart>` | Use independent X/Y observations to explore relationships, clusters, and outliers. |
41
- | How did one metric change between periods? | `<Comparison>` | Use the metric reading and its displayed comparison period. |
42
- | What are the exact values or row details? | `<TableWidget>` | Use a bound table for lookup, search, and precise comparisons. |
31
+ | Question or need | Component | Guidance |
32
+ | --------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------ |
33
+ | How do categories compare? | `<BarChart>` | Compare nonnegative category values; order by value for rank or by a meaningful category order. |
34
+ | Which items lead? | `<Ranking>` | Use a compact ordered list with values and optional detail. Tracks scale to the largest visible item, not a total. |
35
+ | How does a measure change over time? | `<LineChart>` | Use ordered, equally spaced samples to emphasize the trend. |
36
+ | How large is the measure over time? | `<AreaChart>` | Use the same samples as a line chart when filled magnitude relative to zero helps answer the question. |
37
+ | What makes up the whole? | `<PieChart>` | Use a few mutually exclusive parts of one total; include the remainder as Other. |
38
+ | What share of an observed total does each part represent? | `<Breakdown>` | Use a compact display of values and shares with an explicit total, including when only some parts are shown. |
39
+ | How do several measures relate on one plot? | `<ComposedChart>` | Compose bars, lines, areas, or scatter series; label units clearly and use separate axes for different units. |
40
+ | How are two numeric measures related? | `<ScatterChart>` | Use independent X/Y observations to explore relationships, clusters, and outliers. |
41
+ | Where do users drop out of a sequence? | `<FunnelChart>` | Use ordered counts from the same population reaching each successive step. |
42
+ | Do cohorts return over time? | `<RetentionChart>` | Use query-defined rates and retained counts by numeric offset; mark incomplete periods. |
43
+ | Which paths do users take? | `<JourneyChart>` | Use full paths with population counts and outcomes; explore branches step by step. |
44
+ | How did one metric change between periods? | `<Comparison>` | Use the metric reading and its displayed comparison period. |
45
+ | What are the exact values or row details? | `<TableWidget>` | Use a bound table for lookup, search, and precise comparisons. |
43
46
 
44
47
  Prefer bars or a ranking when readers need to compare similarly sized categories;
45
48
  use a pie for a simple composition question. Prefer a line when the trend is
package/docs/worker.md CHANGED
@@ -28,7 +28,8 @@ and these string bindings:
28
28
  | `PARENT_ORIGINS` | Space-separated trusted HTTP(S) origins; first exact origin is the default | `https://app.example.com https://*.preview.example.com` |
29
29
 
30
30
  The Worker serves only `/` on a single preview label under `DOMAIN_NAME` matching
31
- `/^(?=.{1,63}$)[a-z0-9]+(?:-[a-z0-9]+)+-app-[1-9][0-9]*$/`. Other hosts and paths
31
+ `/^(?=.{1,63}$)[a-z0-9]+(?:-+[a-z0-9]+)+$/`
32
+ (any hyphenated lowercase DNS label; the host keeps each app's label unique). Other hosts and paths
32
33
  return 404. GET returns HTML; HEAD returns the same headers with no body. Other
33
34
  methods return 405 with `Allow: GET, HEAD`.
34
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@altertable/data-app",
3
- "version": "0.67.0",
3
+ "version": "0.68.0",
4
4
  "description": "Contracts, transport, and React UI for Altertable data apps",
5
5
  "keywords": [
6
6
  "altertable",