@altertable/data-app 0.65.0 → 0.66.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 (138) hide show
  1. package/AGENTS.md +38 -1
  2. package/CONTRIBUTING.md +37 -3
  3. package/README.md +1 -1
  4. package/dist/chunks/{contract-sm7j7k95.js → contract-13j5zb3c.js} +2 -144
  5. package/dist/chunks/contract-13j5zb3c.js.map +13 -0
  6. package/dist/chunks/contract-19ckn3n7.js +78 -0
  7. package/dist/chunks/contract-19ckn3n7.js.map +10 -0
  8. package/dist/chunks/{contract-a061yj6r.js → contract-e11m4y7f.js} +34 -70
  9. package/dist/chunks/contract-e11m4y7f.js.map +12 -0
  10. package/dist/chunks/{contract-g6hky7x6.js → contract-h8a6f559.js} +8 -4
  11. package/dist/chunks/contract-h8a6f559.js.map +12 -0
  12. package/dist/chunks/contract-havnjmdr.js +13011 -0
  13. package/dist/chunks/contract-havnjmdr.js.map +99 -0
  14. package/dist/chunks/{contract-txjy0en2.js → contract-m6n8ctfc.js} +64 -72
  15. package/dist/chunks/contract-m6n8ctfc.js.map +10 -0
  16. package/dist/chunks/contract-pk603qj7.js +146 -0
  17. package/dist/chunks/contract-pk603qj7.js.map +10 -0
  18. package/dist/chunks/{contract-yxbjea23.js → contract-rfbakka4.js} +179 -2
  19. package/dist/chunks/contract-rfbakka4.js.map +13 -0
  20. package/dist/client/index.js +11 -6
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/core/appearance.js +1 -1
  23. package/dist/core/contract.js +13 -3
  24. package/dist/core/contract.js.map +1 -1
  25. package/dist/embed/index.js +9 -7
  26. package/dist/embed/index.js.map +2 -2
  27. package/dist/local.js +1 -1
  28. package/dist/local.js.map +2 -2
  29. package/dist/react/embed/index.js +1 -1
  30. package/dist/react/index.js +2532 -11545
  31. package/dist/react/index.js.map +16 -91
  32. package/dist/react/ui/index.js +1614 -0
  33. package/dist/react/ui/index.js.map +22 -0
  34. package/dist/server.js +1 -1
  35. package/dist/server.js.map +2 -2
  36. package/dist/types/client/annotations.d.ts +23 -0
  37. package/dist/types/client/index.d.ts +1 -0
  38. package/dist/types/core/annotations.d.ts +92 -0
  39. package/dist/types/core/appearance.d.ts +2 -2
  40. package/dist/types/core/contract.d.ts +2 -0
  41. package/dist/types/core/presentation.d.ts +2 -0
  42. package/dist/types/core/variables.d.ts +2 -5
  43. package/dist/types/react/annotations/AnnotationBar.d.ts +24 -0
  44. package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
  45. package/dist/types/react/annotations/AnnotationEditor.d.ts +15 -0
  46. package/dist/types/react/annotations/AnnotationMarkers.d.ts +19 -0
  47. package/dist/types/react/annotations/AnnotationSelectionLayer.d.ts +12 -0
  48. package/dist/types/react/annotations/AnnotationTarget.d.ts +9 -0
  49. package/dist/types/react/annotations/AnnotationTooltip.d.ts +9 -0
  50. package/dist/types/react/annotations/AnnotationTrigger.d.ts +10 -0
  51. package/dist/types/react/annotations/annotation-editor-state.d.ts +56 -0
  52. package/dist/types/react/annotations/annotation-screenshot.d.ts +4 -0
  53. package/dist/types/react/annotations/annotation-targets.d.ts +33 -0
  54. package/dist/types/react/annotations/getAnnotationProps.d.ts +15 -0
  55. package/dist/types/react/annotations/styles.d.ts +3 -0
  56. package/dist/types/react/annotations/useAnnotationGeometry.d.ts +22 -0
  57. package/dist/types/react/annotations/useDataAppAnnotations.d.ts +19 -0
  58. package/dist/types/react/bindings.d.ts +110 -0
  59. package/dist/types/react/content.d.ts +24 -11
  60. package/dist/types/react/hooks.d.ts +29 -787
  61. package/dist/types/react/index.d.ts +30 -105
  62. package/dist/types/react/source-owner.d.ts +2 -0
  63. package/dist/types/react/style-contract.d.ts +60 -0
  64. package/dist/types/react/style-validation.d.ts +2 -0
  65. package/dist/types/react/ui/AboutData.d.ts +9 -1
  66. package/dist/types/react/ui/AppHeader.d.ts +1 -2
  67. package/dist/types/react/ui/AppLayout.d.ts +3 -9
  68. package/dist/types/react/ui/AppToolbar.d.ts +5 -8
  69. package/dist/types/react/ui/AreaChart.d.ts +7 -0
  70. package/dist/types/react/ui/BarChart.d.ts +6 -0
  71. package/dist/types/react/ui/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
  72. package/dist/types/react/ui/ContentSkeleton.d.ts +2 -0
  73. package/dist/types/react/ui/DataApp.d.ts +17 -58
  74. package/dist/types/react/ui/DataAppFrame.d.ts +42 -0
  75. package/dist/types/react/ui/DataBoundary.d.ts +4 -5
  76. package/dist/types/react/ui/DataSection.d.ts +6 -18
  77. package/dist/types/react/ui/DataSectionBoundary.d.ts +30 -0
  78. package/dist/types/react/ui/DataValue.d.ts +9 -0
  79. package/dist/types/react/ui/DataWidget.d.ts +2 -1
  80. package/dist/types/react/ui/DateTimeTooltip.d.ts +1 -1
  81. package/dist/types/react/ui/ExportControl.d.ts +1 -1
  82. package/dist/types/react/ui/HelpPopover.d.ts +3 -4
  83. package/dist/types/react/ui/InspectionContext.d.ts +2 -0
  84. package/dist/types/react/ui/InspectionProvider.d.ts +29 -0
  85. package/dist/types/react/ui/LineChart.d.ts +7 -0
  86. package/dist/types/react/ui/MetricWidget.d.ts +4 -3
  87. package/dist/types/react/ui/PieChart.d.ts +8 -0
  88. package/dist/types/react/ui/PresentStory.d.ts +2 -5
  89. package/dist/types/react/ui/RefreshControl.d.ts +1 -2
  90. package/dist/types/react/ui/ScatterChart.d.ts +17 -0
  91. package/dist/types/react/ui/SearchField.d.ts +1 -2
  92. package/dist/types/react/ui/SearchInput.d.ts +3 -1
  93. package/dist/types/react/ui/Sheet.d.ts +2 -1
  94. package/dist/types/react/ui/Skeleton.d.ts +5 -2
  95. package/dist/types/react/ui/StaticDataApp.d.ts +4 -0
  96. package/dist/types/react/ui/TableWidget.d.ts +19 -16
  97. package/dist/types/react/ui/Tabs.d.ts +6 -2
  98. package/dist/types/react/ui/TextWidget.d.ts +2 -1
  99. package/dist/types/react/ui/Tooltip.d.ts +4 -3
  100. package/dist/types/react/ui/TrendChart.d.ts +5 -0
  101. package/dist/types/react/ui/UpdatedAt.d.ts +1 -1
  102. package/dist/types/react/ui/VisualizationWidget.d.ts +12 -13
  103. package/dist/types/react/ui/WidgetViewTabs.d.ts +1 -1
  104. package/dist/types/react/ui/chart-data.d.ts +25 -0
  105. package/dist/types/react/ui/data-context.d.ts +18 -7
  106. package/dist/types/react/ui/icons.d.ts +1 -0
  107. package/dist/types/react/ui/index.d.ts +74 -0
  108. package/dist/types/react/ui/metric.d.ts +1 -1
  109. package/dist/types/react/ui/presentation.d.ts +5 -1
  110. package/dist/types/react/ui/shortcuts.d.ts +7 -1
  111. package/dist/types/react/view-runtime.d.ts +29 -0
  112. package/dist/types/react/view.d.ts +21 -4
  113. package/dist/types/react/widgets.d.ts +79 -0
  114. package/docs/app-authoring.md +58 -50
  115. package/docs/data-context.md +89 -0
  116. package/docs/formatting-and-appearance.md +52 -0
  117. package/docs/hosted-apps.md +0 -13
  118. package/docs/layout.md +4 -2
  119. package/docs/react-embed.md +2 -2
  120. package/docs/react.md +20 -316
  121. package/docs/stories-and-export.md +52 -0
  122. package/docs/styling.md +55 -0
  123. package/docs/ui-quality.md +20 -0
  124. package/docs/ui.md +41 -0
  125. package/docs/variables.md +98 -0
  126. package/docs/views.md +127 -0
  127. package/docs/widgets.md +142 -0
  128. package/examples/starter-data-app/index.tsx +82 -55
  129. package/package.json +12 -4
  130. package/dist/chunks/contract-a061yj6r.js.map +0 -12
  131. package/dist/chunks/contract-g6hky7x6.js.map +0 -12
  132. package/dist/chunks/contract-sm7j7k95.js.map +0 -14
  133. package/dist/chunks/contract-txjy0en2.js.map +0 -10
  134. package/dist/chunks/contract-yxbjea23.js.map +0 -12
  135. package/dist/types/react/ui/RefreshRegion.d.ts +0 -9
  136. package/dist/types/react/ui/SelectableBarChart.d.ts +0 -15
  137. package/docs/releasing.md +0 -79
  138. /package/dist/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
@@ -0,0 +1,29 @@
1
+ import type { DataContext } from './ui/data-context.js';
2
+ import type { ReactNode } from 'react';
3
+ import type { DataView, DisplayedSnapshot } from '../core/data-view.js';
4
+ import type { DisclosedQuery } from '../core/contract.js';
5
+ import type { EmptyContent } from './ui/presentation.js';
6
+ export type ViewResult<Data, Input> = {
7
+ dataContext: DataContext;
8
+ view: DataView<Data, Input>;
9
+ snapshot?: DisplayedSnapshot<Data, Input>;
10
+ refetch: () => unknown;
11
+ cancel: () => unknown;
12
+ refreshing: boolean;
13
+ queries?: DisclosedQuery[];
14
+ controls?: ReactNode;
15
+ emptyFallback: EmptyContent;
16
+ };
17
+ /** The executor is private; declarations expose data composition, not subscription hooks. */
18
+ export declare class DeclaredView<Data, Input, Definition = unknown> {
19
+ #private;
20
+ constructor(useResult: () => ViewResult<Data, Input>, definition?: Definition);
21
+ static definition<Data, Input, Definition>(view: DeclaredView<Data, Input, Definition>): Definition;
22
+ static useResult<Data, Input>(view: DeclaredView<Data, Input>): ViewResult<Data, Input>;
23
+ }
24
+ export declare function useDeclaredResult<Data, Input>(view: DeclaredView<Data, Input>): ViewResult<Data, Input>;
25
+ export declare const PrimaryViewContext: import("react").Context<{
26
+ declaration: object;
27
+ result: ViewResult<unknown, unknown>;
28
+ } | null>;
29
+ export declare function getViewDefinition<Data, Input, Definition>(view: DeclaredView<Data, Input, Definition>): Definition;
@@ -1,3 +1,4 @@
1
+ import type { DataContext } from './ui/data-context.js';
1
2
  import type { EmptyContent } from './ui/presentation.js';
2
3
  import type { AppVariableValues, DateRangeVariable, VariableCollection } from '../core/variables.js';
3
4
  import type { DateRangeRequest } from '../core/contract.js';
@@ -16,21 +17,37 @@ export type ViewDate<Variables extends VariableCollection, Input> = {
16
17
  export type ViewBindings<Variables extends VariableCollection, Input> = Partial<{
17
18
  [Key in keyof Variables]: (input: Input) => ResolvedVariables<Variables>[Key];
18
19
  }>;
20
+ type SameViewInput<Variables extends VariableCollection, Input> = [
21
+ ResolvedVariables<Variables>
22
+ ] extends [Input] ? [Input] extends [ResolvedVariables<Variables>] ? [Input] extends [Record<string, never>] ? true : [keyof Input] extends [keyof Variables] ? true : false : false : false;
23
+ type ViewInputMapping<Variables extends VariableCollection, Input> = SameViewInput<Variables, Input> extends true ? {
24
+ input?: (values: ResolvedVariables<Variables>) => Input;
25
+ } : {
26
+ input: (values: ResolvedVariables<Variables>) => Input;
27
+ };
19
28
  export type DataViewDefinition<Name extends string, Variables extends VariableCollection, Input, Data> = {
20
29
  operation: Name;
21
- variables: Variables;
22
- input: (values: ResolvedVariables<Variables>) => Input;
30
+ dataContext: DataContext;
23
31
  bindings?: ViewBindings<Variables, Input>;
24
32
  /** App-owned semantics: measured zero need not mean an empty result. */
25
33
  isEmpty: (data: Data) => boolean;
26
- empty: EmptyContent;
27
- } & ({
34
+ /** Inherited by DataSection unless it supplies its own copy. */
35
+ emptyFallback: EmptyContent;
36
+ } & (keyof Variables extends never ? {
37
+ variables?: Variables;
38
+ } : {
39
+ variables: Variables;
40
+ }) & ViewInputMapping<NoInfer<Variables>, Input> & ({
28
41
  date: ViewDate<Variables, Input>;
29
42
  describeInput?: (input: Input) => string;
30
43
  } | {
31
44
  date?: never;
32
45
  describeInput: (input: Input) => string;
33
46
  });
47
+ export type ResolvedDataViewDefinition<Name extends string, Variables extends VariableCollection, Input, Data> = Omit<DataViewDefinition<Name, Variables, Input, Data>, 'variables' | 'input'> & {
48
+ variables: Variables;
49
+ input: (values: ResolvedVariables<Variables>) => Input;
50
+ };
34
51
  export declare function describeViewInput<Input>(definition: {
35
52
  variables: VariableCollection;
36
53
  bindings?: Partial<Record<string, (input: Input) => unknown>>;
@@ -0,0 +1,79 @@
1
+ import { type Dataset, type Metric, type ViewSource } from './bindings.js';
2
+ import { type MetricWidgetProps as DisplayMetricProps } from './ui/MetricWidget.js';
3
+ import { type TableWidgetProps as DisplayTableProps, type TableDisplayMode } from './ui/TableWidget.js';
4
+ import { type VisualizationWidgetProps as DisplayVisualProps, type VisualizationWidgetView as DisplayVisualizationView } from './ui/VisualizationWidget.js';
5
+ import { type TextWidgetProps as DisplayTextProps } from './ui/TextWidget.js';
6
+ import { type DataValueProps as DisplayValueProps } from './ui/DataValue.js';
7
+ import type { MetricValues } from '../core/reading.js';
8
+ import type { MetricDefinition } from './ui/metric.js';
9
+ import type { ReactNode } from 'react';
10
+ import type { DataReading } from '../core/reading.js';
11
+ export type MetricWidgetProps<Data = unknown, Input = unknown> = Omit<Extract<DisplayMetricProps, {
12
+ metric: MetricDefinition;
13
+ }>, 'metric' | 'reading'> & {
14
+ metric: Metric<Data, Input>;
15
+ source: ViewSource<Data, Input>;
16
+ };
17
+ export declare function MetricWidget<Data, Input>({ metric, source, ...presentation }: MetricWidgetProps<Data, Input>): import("react").JSX.Element;
18
+ type BoundTableProps<Row> = Extract<DisplayTableProps<Row>, {
19
+ reading: DataReading<readonly Row[]>;
20
+ }>;
21
+ type TablePresentation<Row> = Pick<BoundTableProps<Row>, 'title' | 'columns' | 'rowKey' | 'reading' | 'evidence' | 'emptyFallback'>;
22
+ export type TableWidgetProps<Row, Data = unknown, Input = unknown> = Omit<BoundTableProps<Row>, keyof TablePresentation<Row> | 'limit' | 'pagination'> & TableDisplayMode & {
23
+ dataset: Dataset<Data, Input, Row>;
24
+ source: ViewSource<Data, Input>;
25
+ title?: ReactNode;
26
+ };
27
+ export declare function TableWidget<Row, Data, Input>({ dataset, source, title, ...presentation }: TableWidgetProps<Row, Data, Input>): import("react").JSX.Element;
28
+ type BoundVisual<Row> = Extract<DisplayVisualProps<readonly Row[]>, {
29
+ reading: DataReading<readonly Row[]>;
30
+ }>;
31
+ export type VisualizationWidgetView<Row> = DisplayVisualizationView<readonly Row[]>;
32
+ type VisualContent<Row> = {
33
+ children: (rows: readonly Row[]) => ReactNode;
34
+ views?: never;
35
+ viewLabel?: never;
36
+ initialView?: never;
37
+ } | {
38
+ children?: never;
39
+ views: readonly VisualizationWidgetView<Row>[];
40
+ viewLabel: string;
41
+ initialView?: string;
42
+ };
43
+ export type VisualizationWidgetProps<Row, Data = unknown, Input = unknown> = Omit<BoundVisual<Row>, 'reading' | 'isEmpty' | 'emptyFallback' | 'evidence' | 'title' | 'children' | 'views' | 'viewLabel' | 'initialView'> & VisualContent<Row> & {
44
+ dataset: Dataset<Data, Input, Row>;
45
+ source: ViewSource<Data, Input>;
46
+ title?: ReactNode;
47
+ };
48
+ export declare function VisualizationWidget<Row, Data, Input>({ dataset, source, title, ...presentation }: VisualizationWidgetProps<Row, Data, Input>): import("react").JSX.Element;
49
+ type NarrativeBinding<Data, Input, Row> = {
50
+ metric: Metric<Data, Input>;
51
+ dataset?: never;
52
+ source: ViewSource<Data, Input>;
53
+ children?: (values: MetricValues) => ReactNode;
54
+ } | {
55
+ metric?: never;
56
+ dataset: Dataset<Data, Input, Row>;
57
+ source: ViewSource<Data, Input>;
58
+ children: (rows: readonly Row[]) => ReactNode;
59
+ };
60
+ export type TextWidgetProps<Data = unknown, Input = unknown, Row = never> = Omit<Extract<DisplayTextProps<unknown>, {
61
+ reading: DataReading<unknown>;
62
+ }>, 'reading' | 'evidence' | 'children' | 'title' | 'emptyFallback'> & NarrativeBinding<Data, Input, Row> & {
63
+ title?: ReactNode;
64
+ };
65
+ export declare function TextWidget<Data, Input, Row>(props: TextWidgetProps<Data, Input, Row>): import("react").JSX.Element;
66
+ type InlinePresentation = Omit<DisplayValueProps<unknown>, 'reading' | 'children' | 'loadingFallback'> & {
67
+ loadingFallback?: ReactNode;
68
+ };
69
+ export type DataValueProps<Data = unknown, Input = unknown, Row = never> = InlinePresentation & ((NarrativeBinding<Data, Input, Row> & {
70
+ scope?: never;
71
+ }) | {
72
+ metric?: never;
73
+ dataset?: never;
74
+ source?: never;
75
+ scope: DataReading<string>;
76
+ children?: (scope: string) => ReactNode;
77
+ });
78
+ export declare function DataValue<Data, Input, Row>(props: DataValueProps<Data, Input, Row>): import("react").JSX.Element;
79
+ export {};
@@ -27,63 +27,71 @@ readers can inspect the source of each claim.
27
27
 
28
28
  Connect visualizations with introductions and explanations. Use `<TextWidget>`
29
29
  for a narrative panel with the standard widget frame, or `<TextContent>` for
30
- borderless prose. Bind claims to `result.select((data, input) => ...)` so their
31
- values and scope follow the displayed results through filter changes, refresh,
32
- and failure.
33
-
34
- | Task | Documentation |
35
- | ------------------------------------------ | ---------------------------------------------------------- |
36
- | Define queries, inputs, and result parsing | [Operations](contract.md) |
37
- | Build views, filters, and request states | [React](react.md) |
38
- | Introduce and explain visualizations | [Narrative text](react.md#narrative-text) |
39
- | Find formatters and presentation helpers | [App helpers](react.md#reuse-app-helpers) |
40
- | Register source names | [Source identifiers](react.md#register-source-identifiers) |
41
- | Choose date and field filters | [Filter variables](react.md#time-views-and-field-filters) |
42
- | Handle refresh and stale results | [Displayed results](react.md#preserve-displayed-results) |
43
- | Export displayed data as CSV | [CSV export](react.md#export-displayed-data-as-csv) |
44
- | Bind definitions and source evidence | [Data context](react.md#bind-evidence) |
45
-
46
- Use the exported types for configuration, appearance, formatting, and component
47
- options. Declare configuration with `satisfies DataAppConfig` so appearance
48
- fields and values are checked before bundling.
49
-
50
- ## Compose the layout
51
-
52
- See the [layout contract](layout.md).
53
-
54
- ## Export the displayed results
55
-
56
- Provide `DataApp.csvExport` in every analytical app. The request-backed API
57
- requires a callback that selects an explicit filename and named datasets with ordered columns and raw
58
- rows from the displayed snapshot. Follow [CSV export](react.md#export-displayed-data-as-csv)
59
- and the [starter](../examples/starter-data-app/index.tsx); use the built-in toolbar
60
- action rather than adding a custom download button.
61
-
62
- Export every distinct analytical dataset at its displayed grain, including relevant
63
- dimensions and measures. Reuse one dataset for charts or metrics derived from the
64
- same rows. One dataset downloads as CSV; multiple datasets offer individual CSVs
65
- and **Export all** as a ZIP archive. Use the displayed input for scope labels and filenames. Export the
66
- bounded result the app already has; do not issue a different query or mix pending
67
- filters into the visible result. Setup and static screens may omit export.
68
-
69
- ## Present the findings
70
-
71
- Compose a [story](react.md#present-data-with-stories) from the exploration's
72
- findings. Lead with the answer, then show the evidence and comparisons that
73
- explain it. Select the findings that matter to the audience; do not turn every
74
- row or chart into a step.
30
+ borderless prose. Render static titles, descriptions, and instructions immediately. Use metric and dataset
31
+ bindings for dynamic values and `<DataValue>` for values within static prose.
32
+ Skeletonize only the content that needs data. Reuse bindings and the displayed
33
+ source in narrative so values, formatting, and evidence follow filter changes,
34
+ refresh, and failure.
35
+
36
+ | Task | Documentation |
37
+ | ------------------------------------------------ | ----------------------------------------------------------------- |
38
+ | Choose components, CSS tokens, and styling hooks | [Styling](styling.md) |
39
+ | Define queries, inputs, and result parsing | [Operations](contract.md) |
40
+ | Build views, filters, and request states | [Views](views.md) |
41
+ | Introduce and explain visualizations | [Narrative text](widgets.md#narrative-text) |
42
+ | Find formatters and presentation helpers | [App helpers](formatting-and-appearance.md) |
43
+ | Register source names | [Source identifiers](data-context.md#register-source-identifiers) |
44
+ | Choose date and field filters | [Filter variables](variables.md) |
45
+ | Handle refresh and stale results | [Displayed results](views.md#preserve-displayed-results) |
46
+ | Export displayed data as CSV | [CSV export](stories-and-export.md#export-displayed-data-as-csv) |
47
+ | Render widgets and custom visuals | [Widgets](widgets.md) |
48
+ | Bind definitions and source evidence | [Data context](data-context.md#bind-evidence) |
49
+
50
+ Declare reusable [datasets and metrics](widgets.md#declare-datasets-and-metrics)
51
+ on the view so tables, exports, and stories share values and evidence.
52
+
53
+ Every data app supplies a [story and CSV export](stories-and-export.md)
54
+ from the displayed result. Export all distinct datasets at their displayed grain;
55
+ reuse a dataset when several visuals derive from the same rows. Present the
56
+ findings that answer the reader's question, rather than every row or chart.
57
+ Use the [standard layout](layout.md) and built-in toolbar actions.
58
+
59
+ ### Organize the exploration
60
+
61
+ Use sections for a focused question and for findings readers should compare
62
+ side by side. Add app-level `<Tabs>` from `/react/ui` when the exploration has
63
+ distinct analytical questions, such as Overview, Retention, and Segments, each
64
+ with its own context and group of widgets. Lead with the most useful overview
65
+ and label tabs by the question or subject they explore.
66
+
67
+ Use `<VisualizationWidget>`'s `views` for alternate representations of the same
68
+ dataset, such as a chart and its rows. Keep these choices within the widget;
69
+ use filter variables when the reader is changing the data scope.
70
+
71
+ Navigation tabs, widget views, and declared data views have different roles.
72
+ A declared view owns inputs, requests, and displayed results; a tab does not
73
+ require a separate data view. Keep related datasets in one view for a coherent
74
+ snapshot. Use independent views and `<DataSection>` boundaries when content
75
+ needs separate requests. Keep filter placement consistent across tabs and make
76
+ each filter's scope clear. Derive findings, story, and export from the displayed
77
+ results and their inputs.
78
+
79
+ Choose each visual for the question it answers; see
80
+ [visualization selection](widgets.md#choose-a-visualization).
75
81
 
76
82
  ## Verify the app
77
83
 
84
+ Follow the [UI quality contract](ui-quality.md) for hierarchy, responsive
85
+ composition, typography, and control states.
86
+
78
87
  Verify findings against the source and the user's question. Distinguish measured
79
88
  zero, unavailable values, and empty results. Check filters, refresh, loading,
80
89
  empty, error, and stale states, then present the story. Download CSV from the
81
90
  standalone and embedded toolbar and verify its filename, columns, raw values,
82
91
  and filter scope against the displayed result. Export and Present story must be
83
- available once analytical results are shown; initial loading, empty, and initial
84
- errors have neither action. Inspect both experiences
92
+ available once results are shown; initial loading, empty, and initial
93
+ errors keep both actions visible and disabled. Inspect both experiences
85
94
  at phone and desktop widths in light and dark themes.
86
95
 
87
- The app owns its queries, result parsing, business definitions, configuration,
88
- and presentation. Credentials, authorization, and enforced access/query limits
89
- stay backend-owned. Edit app-owned files; installed package files are dependencies.
96
+ Keep credentials, authorization, and enforced query limits in the host/backend.
97
+ Edit app-owned files; installed package files are dependencies.
@@ -0,0 +1,89 @@
1
+ # Data context and evidence
2
+
3
+ Set `dataContext: context` in each view. The app and its sections inherit the
4
+ view's description, glossary, and permitted query evidence. Render registered identifiers where `<DataIdentifier>` is used.
5
+
6
+ Physical source identifiers name inspected tables and columns. Glossary entries
7
+ explain business meaning. Query names link those definitions and displayed claims
8
+ to their execution evidence. Keep all three explicit.
9
+
10
+ ## Register source identifiers
11
+
12
+ Use `defineDataIdentifiers()` from `/react` to register exact catalog, schema,
13
+ table, and field names. Use `<DataIdentifier>` in descriptions and glossary
14
+ entries to render those source references consistently.
15
+
16
+ ```tsx
17
+ import { defineDataIdentifiers } from '@altertable/data-app/react';
18
+
19
+ const identifiers = defineDataIdentifiers({
20
+ tables: {
21
+ events: {
22
+ catalog: 'product_analytics',
23
+ schema: 'analytics',
24
+ name: 'events',
25
+ },
26
+ },
27
+ columns: { identity: { table: 'events', name: 'identity_uuid' } },
28
+ });
29
+ const { DataIdentifier } = identifiers;
30
+
31
+ <DataIdentifier id="tables.events" />;
32
+ <DataIdentifier id="columns.events.identity" />;
33
+ ```
34
+
35
+ Pass `identifiers.definitions` to `createDataContext()` to reuse the source
36
+ registry.
37
+
38
+ ## Bind evidence
39
+
40
+ ```tsx
41
+ const queries = defineQueryNames({ activity: 'feature-activity' });
42
+ // In the server operation: queryNames: queries
43
+ const context = createDataContext(queries)({
44
+ identifiers: identifiers.definitions,
45
+ description: (
46
+ <>
47
+ Explore activity in <DataIdentifier id="tables.events" />.
48
+ </>
49
+ ),
50
+ glossary: {
51
+ identities: {
52
+ term: 'Tracked identities',
53
+ definition: (
54
+ <>
55
+ Distinct <DataIdentifier id="columns.events.identity" /> values.
56
+ </>
57
+ ),
58
+ queryNames: [queries.activity],
59
+ },
60
+ },
61
+ });
62
+ const evidence = context.evidence({
63
+ id: 'identities',
64
+ glossaryIds: ['identities'],
65
+ queryNames: [queries.activity],
66
+ });
67
+ // activityView is declared with dataContext: context.
68
+ const trackedIdentities = activityView.metric(
69
+ {
70
+ id: 'identities',
71
+ glossaryId: 'identities',
72
+ format: { kind: 'count' },
73
+ },
74
+ data => ({ current: data.count })
75
+ );
76
+ const finding = context.finding({
77
+ id: 'activity',
78
+ headline: 'What people do',
79
+ visual: <ActivityChart />,
80
+ evidence: { id: 'activity-evidence', queryNames: [queries.activity] },
81
+ });
82
+ ```
83
+
84
+ Import `defineQueryNames()` from `/contract` and the context/identifier factories from `/react`. Use the same registry in `defineOperation({ queryNames: queries, ... })`.
85
+
86
+ Use `view.metric(definition, select)` to register and bind a metric in one call.
87
+ Declare dataset evidence references inside `view.dataset()`; the view registers
88
+ and validates them too.
89
+ Widgets and stories share its values and evidence; see [datasets and metrics](widgets.md#declare-datasets-and-metrics).
@@ -0,0 +1,52 @@
1
+ # Formatting and appearance
2
+
3
+ Use package helpers so a value has the same meaning in prose, tables, charts,
4
+ and stories. TypeScript and JSDoc describe options and supported values.
5
+
6
+ ## Format values
7
+
8
+ Import formatting helpers from `@altertable/data-app/format`.
9
+
10
+ | Helper | Use |
11
+ | ------------------- | ------------------------------------------------------ |
12
+ | `formatNumber()` | General numerical values |
13
+ | `formatCount()` | Nonnegative integer counts, optionally compact |
14
+ | `formatPercent()` | Ratios represented as a fraction, such as 0.12 for 12% |
15
+ | `formatMetric()` | A metric definition's count, ratio, or currency format |
16
+ | `formatDateRange()` | Inclusive calendar ranges |
17
+ | `pluralize()` | Count-dependent labels |
18
+
19
+ Missing values render distinctly from measured zero. Define metric formatting
20
+ once with `view.metric()`; comparisons derive from its displayed source. Previous
21
+ values, range labels, and favorable direction belong to that metric and displayed
22
+ input; see [data context](data-context.md) and [views](views.md).
23
+
24
+ Import `chartColor()` from `/react` to select colors from the configured palette.
25
+ `<PeriodSummary>` describes reporting periods; `<UpdatedAt>` and
26
+ `<DateTimeTooltip>` expose readable timestamps with exact-date details.
27
+ `<DataTableTimestamp>` and `<DataTableShare>` format custom table cells.
28
+
29
+ ## Configure identity and appearance
30
+
31
+ ```ts
32
+ import type { DataAppConfig } from '@altertable/data-app/config';
33
+
34
+ const config = {
35
+ title: 'Product activity',
36
+ scope: { organization: 'Acme', environment: 'Production' },
37
+ appearance: {
38
+ theme: 'system',
39
+ accentColor: '#405d47',
40
+ density: 'comfortable',
41
+ },
42
+ } satisfies DataAppConfig;
43
+ ```
44
+
45
+ Scope labels describe the configured connection; they do not grant access.
46
+ `dataAppTitle()` produces the scoped document title used by `mountDataApp()`.
47
+
48
+ Appearance controls the brand palette, typography, density, corner radius, and
49
+ elevation. Those settings apply consistently to the app instead of tuning each
50
+ widget. Theme preference is reader-owned in standalone apps. A trusted parent's
51
+ resolved theme takes precedence and hides local theme controls; see
52
+ [parent presentation](embed.md#parent-presentation).
@@ -24,16 +24,3 @@ For execution details, see [browser-owned operations](client.md#browser-owned-op
24
24
  4. Confirm the host can query the same catalogs, tables, and fields.
25
25
  [Verify the app](app-authoring.md#verify-the-app) in the hosted runtime against
26
26
  the local version's filters and findings.
27
-
28
- ## Preview in this repository
29
-
30
- ```fish
31
- bun install --frozen-lockfile
32
- bun run build
33
- bun browser-tests/server.ts
34
- ```
35
-
36
- Open [the starter preview](http://127.0.0.1:27418/starter-data-app).
37
- Its test host executes the sample SQL through the iframe bridge using SQLite.
38
-
39
- For checks, see [Contributing](../CONTRIBUTING.md).
package/docs/layout.md CHANGED
@@ -6,9 +6,11 @@ for sections, `<Grid>` for peer widgets, and `<GridItem>` for spans. Use
6
6
  external spacing. Keep widget customization inside the widget so its parent can maintain consistent
7
7
  spacing between sections and cards.
8
8
 
9
- By default, section and widget gaps use `--at-layout-gap`, with density configured once
9
+ By default, section and widget gaps use `--atbl-layout-gap`, with density configured once
10
10
  in `DataAppConfig.appearance`. The package owns wrapping and span collapse based
11
- on the available container width.
11
+ on the available container width and configured gap. Custom length values for
12
+ `--atbl-layout-gap`, `--atbl-space-sm`, and `--atbl-space-md` also drive span
13
+ collapse; a two-column span activates only when two minimum-width tracks fit.
12
14
 
13
15
  ```tsx
14
16
  <Stack>
@@ -93,8 +93,8 @@ are mutually exclusive.
93
93
 
94
94
  ## Loading an embedded app
95
95
 
96
- Use `<DataAppSkeleton>` from `/react` while the host builds or starts an app.
97
- Call `injectDataAppShellStyles()` from `/react` before rendering the placeholder.
96
+ Use `<DataAppSkeleton>` from `/react/ui` while the host builds or starts an app.
97
+ Call `injectDataAppShellStyles()` from `/react/ui` before rendering the placeholder.
98
98
  The host owns when to show it and supplies any surrounding header or footer.
99
99
  `/react/embed` itself remains independent of UI components and styles.
100
100