@altertable/data-app 0.66.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +22 -16
- package/dist/chunks/{contract-19ckn3n7.js → index-23dktynd.js} +8 -6
- package/dist/chunks/index-23dktynd.js.map +10 -0
- package/dist/chunks/{contract-havnjmdr.js → index-8qfxv0h5.js} +2272 -1123
- package/dist/chunks/index-8qfxv0h5.js.map +108 -0
- package/dist/chunks/{contract-rfbakka4.js → index-ag0cgnvq.js} +169 -35
- package/dist/chunks/index-ag0cgnvq.js.map +13 -0
- package/dist/chunks/index-dq7b35fn.js +307 -0
- package/dist/chunks/index-dq7b35fn.js.map +13 -0
- package/dist/chunks/{contract-e11m4y7f.js → index-ev2b5aaf.js} +11 -6
- package/dist/chunks/{contract-e11m4y7f.js.map → index-ev2b5aaf.js.map} +3 -3
- package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
- package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
- package/dist/chunks/{contract-13j5zb3c.js → index-ktmvs916.js} +8 -7
- package/dist/chunks/index-ktmvs916.js.map +13 -0
- package/dist/chunks/index-mt93ff2b.js +28 -0
- package/dist/chunks/index-mt93ff2b.js.map +10 -0
- package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
- package/dist/chunks/index-qd5xgx6y.js.map +13 -0
- package/dist/chunks/{contract-pk603qj7.js → index-x69p7cv7.js} +12 -9
- package/dist/chunks/index-x69p7cv7.js.map +10 -0
- package/dist/client/index.js +6 -6
- package/dist/core/appearance.js +1 -1
- package/dist/core/config.js +7 -3
- package/dist/core/config.js.map +1 -1
- package/dist/core/contract.js +14 -6
- package/dist/core/contract.js.map +1 -1
- package/dist/core/format.js +1 -1
- package/dist/embed/index.js +12 -10
- package/dist/embed/index.js.map +4 -4
- package/dist/index.js +12 -0
- package/dist/index.js.map +9 -0
- package/dist/local.js +20 -30
- package/dist/local.js.map +7 -8
- package/dist/react/embed/index.js +1 -1
- package/dist/react/embed/index.js.map +2 -2
- package/dist/react/index.js +57 -19
- package/dist/react/index.js.map +4 -4
- package/dist/react/ui/index.js +836 -178
- package/dist/react/ui/index.js.map +15 -9
- package/dist/server.js +13 -26
- package/dist/server.js.map +6 -7
- package/dist/types/client/annotations.d.ts +4 -0
- package/dist/types/client/data-client.d.ts +1 -2
- package/dist/types/client/iframe.d.ts +4 -2
- package/dist/types/core/annotations.d.ts +2 -0
- package/dist/types/core/config.d.ts +48 -4
- package/dist/types/core/contract.d.ts +20 -12
- package/dist/types/core/dimension.d.ts +6 -4
- package/dist/types/core/filters.d.ts +44 -0
- package/dist/types/core/messages.d.ts +2 -0
- package/dist/types/core/operation-types.d.ts +5 -2
- package/dist/types/core/operation.d.ts +3 -4
- package/dist/types/core/queries.d.ts +16 -0
- package/dist/types/core/uuid.d.ts +2 -0
- package/dist/types/core/variables.d.ts +32 -13
- package/dist/types/embed/runtime-html.d.ts +2 -0
- package/dist/types/embed/source.d.ts +2 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/react/annotations/AnnotationBar.d.ts +7 -1
- package/dist/types/react/annotations/AnnotationEditor.d.ts +2 -1
- package/dist/types/react/annotations/useDataAppAnnotations.d.ts +3 -0
- package/dist/types/react/app-context.d.ts +3 -0
- package/dist/types/react/index.d.ts +3 -3
- package/dist/types/react/mount.d.ts +6 -4
- package/dist/types/react/style-contract.d.ts +22 -2
- package/dist/types/react/ui/Button.d.ts +3 -3
- package/dist/types/react/ui/ChartLegend.d.ts +31 -0
- package/dist/types/react/ui/Checkbox.d.ts +15 -4
- package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
- package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
- package/dist/types/react/ui/ComposedChart.d.ts +41 -0
- package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
- package/dist/types/react/ui/DataAppFrame.d.ts +1 -2
- package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
- package/dist/types/react/ui/FilterActions.d.ts +10 -0
- package/dist/types/react/ui/FilterBar.d.ts +3 -0
- package/dist/types/react/ui/GettingStarted.d.ts +2 -4
- package/dist/types/react/ui/Menu.d.ts +12 -0
- package/dist/types/react/ui/NumberField.d.ts +27 -0
- package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
- package/dist/types/react/ui/PieChart.d.ts +5 -2
- package/dist/types/react/ui/RadioGroup.d.ts +18 -0
- package/dist/types/react/ui/SearchInput.d.ts +2 -2
- package/dist/types/react/ui/Select.d.ts +14 -0
- package/dist/types/react/ui/SelectionMark.d.ts +2 -1
- package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
- package/dist/types/react/ui/chart-data.d.ts +1 -1
- package/dist/types/react/ui/chart-primitives.d.ts +20 -0
- package/dist/types/react/ui/icons.d.ts +1 -0
- package/dist/types/react/ui/index.d.ts +26 -2
- package/dist/types/react/ui/keyboard.d.ts +6 -0
- package/dist/types/react/ui/shortcuts.d.ts +7 -0
- package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
- package/dist/types/react/ui/variables.d.ts +4 -2
- package/dist/types/react/view-controls.d.ts +3 -1
- package/dist/types/server/handler.d.ts +4 -4
- package/dist/worker.js +20 -737
- package/docs/app-authoring.md +15 -1
- package/docs/client.md +8 -8
- package/docs/contract.md +42 -20
- package/docs/data-context.md +8 -6
- package/docs/embed.md +10 -2
- package/docs/formatting-and-appearance.md +23 -7
- package/docs/hosted-apps.md +9 -2
- package/docs/layout.md +1 -1
- package/docs/react-embed.md +3 -2
- package/docs/react.md +4 -3
- package/docs/server-bun.md +1 -1
- package/docs/server.md +4 -4
- package/docs/styling.md +10 -3
- package/docs/ui.md +158 -3
- package/docs/variables.md +80 -7
- package/docs/views.md +4 -8
- package/docs/widgets.md +16 -13
- package/examples/starter-data-app/index.tsx +34 -32
- package/package.json +16 -11
- package/dist/chunks/contract-13j5zb3c.js.map +0 -13
- package/dist/chunks/contract-19ckn3n7.js.map +0 -10
- package/dist/chunks/contract-h8a6f559.js +0 -286
- package/dist/chunks/contract-h8a6f559.js.map +0 -12
- package/dist/chunks/contract-havnjmdr.js.map +0 -99
- package/dist/chunks/contract-pk603qj7.js.map +0 -10
- package/dist/chunks/contract-rfbakka4.js.map +0 -13
- package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
- package/dist/chunks/contract-wz59z8pq.js +0 -8
- package/dist/chunks/contract-wz59z8pq.js.map +0 -10
- /package/dist/chunks/{contract-m6n8ctfc.js → index-m6n8ctfc.js} +0 -0
- /package/dist/chunks/{contract-m6n8ctfc.js.map → index-m6n8ctfc.js.map} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
package/docs/app-authoring.md
CHANGED
|
@@ -7,6 +7,13 @@ queries, definitions, and evidence.
|
|
|
7
7
|
|
|
8
8
|
## Inspect the data
|
|
9
9
|
|
|
10
|
+
Declare the query source of truth with `defineDataApp()`. Generate
|
|
11
|
+
operations with `dataApp.defineOperation()` and execute queries by
|
|
12
|
+
name with parameter values. When a query registry is supplied, use only its queries
|
|
13
|
+
and preserve its SQL. Otherwise, inspect the data and declare the queries the app needs.
|
|
14
|
+
Derive displays, exports, and findings from their results. See
|
|
15
|
+
[named queries](contract.md#execute-named-queries).
|
|
16
|
+
|
|
10
17
|
Inspect the relevant catalogs, tables, and fields, their time coverage, and
|
|
11
18
|
existing definitions. Choose a question the available data can answer.
|
|
12
19
|
|
|
@@ -21,13 +28,20 @@ Choose the execution path:
|
|
|
21
28
|
|
|
22
29
|
Lead with a supported finding and expose the relevant fields as filter variables. Use a date filter for questions worth exploring over time,
|
|
23
30
|
or a fixed period snapshot for a deliberate historical analysis.
|
|
31
|
+
|
|
32
|
+
Use relevant user-controlled query parameters as app filters. Map filter values to
|
|
33
|
+
declared parameters through operation inputs, and reuse query defaults when they
|
|
34
|
+
match the initial selection. One filter may supply several parameters, such as a
|
|
35
|
+
date range supplying `start` and `end`. Keep protected parameters, such as `orgId`,
|
|
36
|
+
and internal execution settings out of filter controls.
|
|
37
|
+
|
|
24
38
|
Register inspected tables and fields with `defineDataIdentifiers()` and use
|
|
25
39
|
`<DataIdentifier>` when naming sources. Register terms and query evidence so
|
|
26
40
|
readers can inspect the source of each claim.
|
|
27
41
|
|
|
28
42
|
Connect visualizations with introductions and explanations. Use `<TextWidget>`
|
|
29
43
|
for a narrative panel with the standard widget frame, or `<TextContent>` for
|
|
30
|
-
borderless prose. Render
|
|
44
|
+
borderless prose. Render the app title, description, and instructions immediately. Use metric and dataset
|
|
31
45
|
bindings for dynamic values and `<DataValue>` for values within static prose.
|
|
32
46
|
Skeletonize only the content that needs data. Reuse bindings and the displayed
|
|
33
47
|
source in narrative so values, formatting, and evidence follow filter changes,
|
package/docs/client.md
CHANGED
|
@@ -15,8 +15,9 @@ const response = await client.query('activity', input, { signal });
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
The app defines `operations`, `input`, and an optional cancellation `signal`.
|
|
18
|
-
Operation input and output types are inferred from the server
|
|
19
|
-
|
|
18
|
+
Operation input and output types are inferred from the server operation map. Keep the
|
|
19
|
+
server operation map import type-only so its implementations stay on the server.
|
|
20
|
+
The shared app declaration contains the query registry and is browser-safe.
|
|
20
21
|
|
|
21
22
|
The default endpoint is `/api/data`. Override it with
|
|
22
23
|
`createDataClient({ endpoint, fetch })` to change the base URL or supply a Fetch
|
|
@@ -24,8 +25,7 @@ implementation. Calls POST JSON to `/api/data/:operation`; the client sends
|
|
|
24
25
|
operation inputs rather than SQL or credentials.
|
|
25
26
|
|
|
26
27
|
`DataResponse` contains `data`, the exact request `input`, `requestId`,
|
|
27
|
-
`queriedAt`,
|
|
28
|
-
and the execution runtime permits disclosure. Use the returned input when labeling stale data during a refresh.
|
|
28
|
+
`queriedAt`, `queryIds`, and executed `queries`. Use the returned input when labeling stale data during a refresh.
|
|
29
29
|
HTTP and iframe delivery validate the same success envelope: data, request ID,
|
|
30
30
|
query timestamp, query IDs, and optional query evidence. Malformed responses
|
|
31
31
|
reject with `invalid_response`.
|
|
@@ -46,17 +46,17 @@ Bundle apps pass their operation registry as a value:
|
|
|
46
46
|
import { connectionCheck } from '@altertable/data-app/contract';
|
|
47
47
|
import { createDataClient } from '@altertable/data-app/client';
|
|
48
48
|
|
|
49
|
+
// dataApp declares a connection query.
|
|
49
50
|
const client = createDataClient({
|
|
50
|
-
operations: { connection: connectionCheck() },
|
|
51
|
+
operations: { connection: connectionCheck(dataApp.queries) },
|
|
51
52
|
});
|
|
52
53
|
const response = await client.query('connection', {});
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
The client runs input parsing, operation logic, and output parsing in the browser.
|
|
56
|
-
Operation policy bounds rows, duration, and response size and records query evidence. Each query sends `{ statement, limit }`
|
|
57
|
+
Operation policy bounds rows, duration, and response size and records query evidence. Each query sends `{ statement, limit, params? }`
|
|
57
58
|
to the installed iframe bridge's `data:sql` route; the host needs no operation
|
|
58
|
-
registry.
|
|
59
|
-
only controls evidence in the returned response. Credentials remain backend-owned.
|
|
59
|
+
registry. Results include query evidence. Credentials remain backend-owned.
|
|
60
60
|
|
|
61
61
|
The trusted bootstrap installs the bridge for bundle apps. A custom runtime must
|
|
62
62
|
install it before querying. An explicit `lakehouse` can supply another authorized
|
package/docs/contract.md
CHANGED
|
@@ -2,35 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
Import operation definitions, parsers, and shared types from
|
|
4
4
|
`@altertable/data-app/contract`. This entry is safe to import in browser and server
|
|
5
|
-
modules.
|
|
6
|
-
|
|
5
|
+
modules. The app declaration is browser-safe and includes its SQL registry. For HTTP apps,
|
|
6
|
+
keep operation implementations, credentials, and authorization on the server; browser modules
|
|
7
|
+
import operation types using `import type`.
|
|
7
8
|
|
|
8
9
|
## Execute named queries
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
Declare SQL once with `defineDataApp()` to preserve exact query and parameter
|
|
12
|
+
names. Use stable `lowerCamelCase` IDs describing the result, such as `products`,
|
|
13
|
+
`productsByCategory`, or `dailyRevenue`. Use plural names for row lists; keep parameter
|
|
14
|
+
values in `params`.
|
|
15
|
+
The app owns an immutable registry snapshot. Define operations with
|
|
16
|
+
`dataApp.defineOperation()`.
|
|
12
17
|
|
|
13
18
|
```ts
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
import { defineDataApp } from '@altertable/data-app';
|
|
20
|
+
import { rowsAsRecords } from '@altertable/data-app/contract';
|
|
21
|
+
|
|
22
|
+
const dataApp = defineDataApp({
|
|
23
|
+
title: 'Products',
|
|
24
|
+
description: 'Explore products for the selected organization.',
|
|
25
|
+
scope: { organization: 'demo', environment: 'production' },
|
|
26
|
+
appearance: { theme: 'system' },
|
|
27
|
+
queries: {
|
|
28
|
+
products: {
|
|
29
|
+
statement: 'SELECT * FROM products WHERE org_id = $orgId LIMIT $limit',
|
|
30
|
+
params: { orgId: {}, limit: { defaultValue: 10 } },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
18
34
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
checks: [checkInput],
|
|
25
|
-
policy: { maxQueryRows: 100, maxDurationMs: 15000, exposeSql: true },
|
|
35
|
+
const products = dataApp.defineOperation({
|
|
36
|
+
input: parseProductInput,
|
|
37
|
+
output: parseProducts,
|
|
38
|
+
checks: [{}],
|
|
39
|
+
policy: { maxQueryRows: 100, maxDurationMs: 15000 },
|
|
26
40
|
async run({ query }, input) {
|
|
27
|
-
const result = await query(
|
|
28
|
-
return
|
|
41
|
+
const result = await query('products', input);
|
|
42
|
+
return rowsAsRecords(result, ['org_id']);
|
|
29
43
|
},
|
|
30
44
|
});
|
|
31
45
|
```
|
|
32
46
|
|
|
33
|
-
`
|
|
47
|
+
`parseProductInput()` and `parseProducts()` are app-owned example functions, not
|
|
48
|
+
package exports. Validate
|
|
49
|
+
filter values in the input parser. `{}` declares a required parameter; `{ defaultValue }` supplies
|
|
50
|
+
a fallback. `query(name, params, { limit })` executes only registered queries and
|
|
51
|
+
inherits the operation's row limit and cancellation signal.
|
|
52
|
+
|
|
53
|
+
SQL and resolved parameter values pass unchanged to the backend. Results include
|
|
54
|
+
query evidence; use `products.queryNames` with `createDataContext()` to bind it.
|
|
55
|
+
For HTTP apps, authorization can supply protected `queryParams`, such as `orgId`.
|
|
56
|
+
The host/backend enforces data access and limits.
|
|
34
57
|
|
|
35
58
|
## Shared date ranges
|
|
36
59
|
|
|
@@ -50,8 +73,7 @@ export const calendar = defineDateRangeContract({
|
|
|
50
73
|
```
|
|
51
74
|
|
|
52
75
|
`parseEmptyInput()`, `parseTrue()`, `parseCount()`, and `parseDateRangeInput()` validate
|
|
53
|
-
common inputs and results. `connectionCheck()`
|
|
54
|
-
operation. A successful connectivity check confirms access; it is not an
|
|
76
|
+
common inputs and results. `connectionCheck(dataApp.queries)` runs the registered `connection` query. A successful connectivity check confirms access; it is not an
|
|
55
77
|
analysis result.
|
|
56
78
|
|
|
57
79
|
See [server authorization](server.md) and [React views](react.md) for the two
|
package/docs/data-context.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Data context and evidence
|
|
2
2
|
|
|
3
|
-
Set `dataContext: context` in each view
|
|
4
|
-
|
|
3
|
+
Set `dataContext: context` in each view for source descriptions, glossary, and
|
|
4
|
+
permitted query evidence. App inspection and the view's sections use that context.
|
|
5
|
+
Render registered identifiers where `<DataIdentifier>` is used.
|
|
5
6
|
|
|
6
7
|
Physical source identifiers name inspected tables and columns. Glossary entries
|
|
7
8
|
explain business meaning. Query names link those definitions and displayed claims
|
|
@@ -38,8 +39,7 @@ registry.
|
|
|
38
39
|
## Bind evidence
|
|
39
40
|
|
|
40
41
|
```tsx
|
|
41
|
-
const queries =
|
|
42
|
-
// In the server operation: queryNames: queries
|
|
42
|
+
const queries = activity.queryNames;
|
|
43
43
|
const context = createDataContext(queries)({
|
|
44
44
|
identifiers: identifiers.definitions,
|
|
45
45
|
description: (
|
|
@@ -69,7 +69,7 @@ const trackedIdentities = activityView.metric(
|
|
|
69
69
|
{
|
|
70
70
|
id: 'identities',
|
|
71
71
|
glossaryId: 'identities',
|
|
72
|
-
format: { kind: 'count' },
|
|
72
|
+
format: { kind: 'count', compact: true },
|
|
73
73
|
},
|
|
74
74
|
data => ({ current: data.count })
|
|
75
75
|
);
|
|
@@ -81,7 +81,9 @@ const finding = context.finding({
|
|
|
81
81
|
});
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Query inspection shows the executed SQL and resolved parameters; copy actions include both.
|
|
85
|
+
|
|
86
|
+
Import context and identifier factories from `/react`. Use the operation’s derived `queryNames` for evidence.
|
|
85
87
|
|
|
86
88
|
Use `view.metric(definition, select)` to register and bind a metric in one call.
|
|
87
89
|
Declare dataset evidence references inside `view.dataset()`; the view registers
|
package/docs/embed.md
CHANGED
|
@@ -31,11 +31,18 @@ A bundle source has this shape:
|
|
|
31
31
|
```ts
|
|
32
32
|
const source = {
|
|
33
33
|
type: 'bundle' as const,
|
|
34
|
-
bootstrapUrl: 'https://my-report-app-1.apps.example.net/',
|
|
35
34
|
javascript: bundle.javascript,
|
|
36
35
|
};
|
|
37
36
|
```
|
|
38
37
|
|
|
38
|
+
Omit `bootstrapUrl` to load the SDK's packaged bootstrap as a `data:` document.
|
|
39
|
+
This supports MCP hosts that permit opaque child frames but block remote frame
|
|
40
|
+
URLs. The SDK embeds the exact host origin and applies a CSP that prohibits
|
|
41
|
+
direct network access. Data requests go through the host's dispatcher.
|
|
42
|
+
|
|
43
|
+
Supply an HTTP(S) `bootstrapUrl` to use an externally served trusted bootstrap,
|
|
44
|
+
such as the [Worker asset](worker.md).
|
|
45
|
+
|
|
39
46
|
The host provides a self-contained JavaScript bundle. React bridges replace the
|
|
40
47
|
iframe whenever its JavaScript content changes. Serve the bootstrap page with a CSP compatible with the bundle.
|
|
41
48
|
Bundle mode uses `sandbox="allow-scripts"` and an opaque origin; it cannot read
|
|
@@ -129,7 +136,8 @@ as public `source_*` errors with request IDs. Authorization failures return
|
|
|
129
136
|
public failures with `MessageRoutingError`.
|
|
130
137
|
|
|
131
138
|
`SqlQueryInput` (exported from `/contract`) carries
|
|
132
|
-
`{ statement: string, limit: number }`;
|
|
139
|
+
`{ statement: string, limit: number, params?: QueryParameters }`; the backend
|
|
140
|
+
interprets the unchanged statement and parameter values. Responses are
|
|
133
141
|
`{ columns: { name: string, type?: string }[], rows: unknown[][], queryId?: string }`.
|
|
134
142
|
The route rejects empty statements, unsafe or nonpositive limits, malformed
|
|
135
143
|
results, and results exceeding the requested limit. The bridge's existing payload
|
|
@@ -16,10 +16,21 @@ Import formatting helpers from `@altertable/data-app/format`.
|
|
|
16
16
|
| `formatDateRange()` | Inclusive calendar ranges |
|
|
17
17
|
| `pluralize()` | Count-dependent labels |
|
|
18
18
|
|
|
19
|
-
Missing values render distinctly from measured zero.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
Missing values render distinctly from measured zero. Declare formatting once on
|
|
20
|
+
`view.metric()` so widgets, comparisons, and narrative share it; reuse the
|
|
21
|
+
definition with `formatMetric()` in story headlines.
|
|
22
|
+
|
|
23
|
+
Use compact counts for headline metrics and chart labels. Declare count metrics
|
|
24
|
+
with `format: { kind: 'count', compact: true }`. Keep full counts in tables used
|
|
25
|
+
for precise comparisons and raw numeric values in datasets for CSV export.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
formatCount(2_200_000, { compact: true }); // "2.2M"
|
|
29
|
+
formatCount(2_200_000); // "2,200,000"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For signed or fractional measures, use `formatNumber()` with
|
|
33
|
+
`notation: 'compact'` and preserve the measure's units.
|
|
23
34
|
|
|
24
35
|
Import `chartColor()` from `/react` to select colors from the configured palette.
|
|
25
36
|
`<PeriodSummary>` describes reporting periods; `<UpdatedAt>` and
|
|
@@ -29,22 +40,27 @@ Import `chartColor()` from `/react` to select colors from the configured palette
|
|
|
29
40
|
## Configure identity and appearance
|
|
30
41
|
|
|
31
42
|
```ts
|
|
32
|
-
import
|
|
43
|
+
import { defineDataApp } from '@altertable/data-app';
|
|
33
44
|
|
|
34
|
-
const
|
|
45
|
+
const dataApp = defineDataApp({
|
|
35
46
|
title: 'Product activity',
|
|
47
|
+
description: 'Explore product usage and trends.',
|
|
36
48
|
scope: { organization: 'Acme', environment: 'Production' },
|
|
37
49
|
appearance: {
|
|
38
50
|
theme: 'system',
|
|
39
51
|
accentColor: '#405d47',
|
|
40
52
|
density: 'comfortable',
|
|
41
53
|
},
|
|
42
|
-
}
|
|
54
|
+
queries: {},
|
|
55
|
+
});
|
|
43
56
|
```
|
|
44
57
|
|
|
45
58
|
Scope labels describe the configured connection; they do not grant access.
|
|
46
59
|
`dataAppTitle()` produces the scoped document title used by `mountDataApp()`.
|
|
47
60
|
|
|
61
|
+
Title and description explain the app and can be revised when regenerating it.
|
|
62
|
+
Omit appearance to use the standard defaults.
|
|
63
|
+
|
|
48
64
|
Appearance controls the brand palette, typography, density, corner radius, and
|
|
49
65
|
elevation. Those settings apply consistently to the app instead of tuning each
|
|
50
66
|
widget. Theme preference is reader-owned in standalone apps. A trusted parent's
|
package/docs/hosted-apps.md
CHANGED
|
@@ -4,7 +4,14 @@ Follow the [shared authoring flow](app-authoring.md) using [index.tsx](../exampl
|
|
|
4
4
|
one file with public package imports; omit server files, HTML, credentials, and
|
|
5
5
|
relative or app-alias imports.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Declare exactly one top-level const initialized with `defineDataApp({ ... })`, imported
|
|
8
|
+
from `@altertable/data-app`. Keep executable composition outside its literal argument.
|
|
9
|
+
For the future backend extractor, use schema-valid literals without spreads, computed keys,
|
|
10
|
+
references, calls, or template interpolation. Extraction will read and validate the argument
|
|
11
|
+
before bundling, without evaluating app code or parsing SQL. Import aliases and any variable
|
|
12
|
+
name are allowed; export only for other modules. Static apps use `queries: {}`.
|
|
13
|
+
|
|
14
|
+
Replace the sample app declaration, operations, filters, data context, CSV export, and story with
|
|
8
15
|
an exploration of the source data you inspected. The starter uses two SQL
|
|
9
16
|
`VALUES` rows, so it needs no production table.
|
|
10
17
|
|
|
@@ -13,7 +20,7 @@ For execution details, see [browser-owned operations](client.md#browser-owned-op
|
|
|
13
20
|
## Convert a local data app
|
|
14
21
|
|
|
15
22
|
1. Combine the app's operations and parsers, data context, views, story,
|
|
16
|
-
CSV export,
|
|
23
|
+
CSV export, app declaration, and browser entry into one `index.tsx`, following the
|
|
17
24
|
[single-file starter](../examples/starter-data-app/index.tsx).
|
|
18
25
|
2. Replace the HTTP client with `createDataClient({ operations })`, using the
|
|
19
26
|
operation registry as a value. See [browser-owned operations](client.md#browser-owned-operations-for-bundle-apps)
|
package/docs/layout.md
CHANGED
|
@@ -7,7 +7,7 @@ external spacing. Keep widget customization inside the widget so its parent can
|
|
|
7
7
|
spacing between sections and cards.
|
|
8
8
|
|
|
9
9
|
By default, section and widget gaps use `--atbl-layout-gap`, with density configured once
|
|
10
|
-
in `
|
|
10
|
+
in `app.appearance`. The package owns wrapping and span collapse based
|
|
11
11
|
on the available container width and configured gap. Custom length values for
|
|
12
12
|
`--atbl-layout-gap`, `--atbl-space-sm`, and `--atbl-space-md` also drive span
|
|
13
13
|
collapse; a two-column span activates only when two minimum-width tracks fit.
|
package/docs/react-embed.md
CHANGED
|
@@ -45,10 +45,11 @@ The local URL must have a different origin from its shell. For hosted bundles,
|
|
|
45
45
|
replace `source` with:
|
|
46
46
|
|
|
47
47
|
```tsx
|
|
48
|
-
source={{ type: 'bundle',
|
|
48
|
+
source={{ type: 'bundle', javascript }}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
The host supplies
|
|
51
|
+
The host supplies the JavaScript bundle. Omit `bootstrapUrl` for the packaged
|
|
52
|
+
opaque bootstrap, or supply an HTTP(S) URL for an external bootstrap. See [embedding](embed.md) for trust,
|
|
52
53
|
sandbox, CSP, and bootstrap setup.
|
|
53
54
|
|
|
54
55
|
Source URL or JavaScript content changes replace the entire iframe. Change the
|
package/docs/react.md
CHANGED
|
@@ -5,14 +5,15 @@ React 19.2 or newer and React DOM 19.2 or newer are peer dependencies.
|
|
|
5
5
|
|
|
6
6
|
## Mount the app
|
|
7
7
|
|
|
8
|
-
Use `mountDataApp({
|
|
9
|
-
through another framework,
|
|
8
|
+
Use `mountDataApp({ app, component })` to mount into `#root`. When mounting
|
|
9
|
+
through another framework, wrap the root in `<DataAppProvider app={app}>` from `/react/ui`.
|
|
10
|
+
`<DataApp>` inherits identity and appearance from that root. Mounting sets the document title before React renders.
|
|
10
11
|
|
|
11
12
|
```tsx
|
|
12
13
|
import { injectDataAppStyles, mountDataApp } from '@altertable/data-app/react';
|
|
13
14
|
|
|
14
15
|
injectDataAppStyles();
|
|
15
|
-
mountDataApp({
|
|
16
|
+
mountDataApp({ app, component: App });
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Call `injectDataAppStyles()` before mounting; no separate stylesheet is needed.
|
package/docs/server-bun.md
CHANGED
|
@@ -24,6 +24,6 @@ server-only `ALTERTABLE_LAKEHOUSE_USERNAME` and
|
|
|
24
24
|
`ALTERTABLE_LAKEHOUSE_PASSWORD`, with an optional `ALTERTABLE_API_BASE`.
|
|
25
25
|
Missing credentials fail the query. Keep these variables out of browser code.
|
|
26
26
|
|
|
27
|
-
Local serving
|
|
27
|
+
Local serving does not authenticate hosted viewers.
|
|
28
28
|
Use [the portable server handler](server.md) with per-request authorization when
|
|
29
29
|
hosting an app for other people.
|
package/docs/server.md
CHANGED
|
@@ -15,7 +15,7 @@ export const handleDataRequest = createDataHandler(
|
|
|
15
15
|
const viewer = await authenticate(request);
|
|
16
16
|
return {
|
|
17
17
|
lakehouse: await lakehouseFor(viewer, operation),
|
|
18
|
-
|
|
18
|
+
queryParams: { orgId: viewer.organizationId },
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
);
|
|
@@ -23,12 +23,12 @@ export const handleDataRequest = createDataHandler(
|
|
|
23
23
|
|
|
24
24
|
The app supplies `authenticate()` and `lakehouseFor()`, then routes `/api/data/*`
|
|
25
25
|
requests to the handler. Authorize every viewer and operation, and scope the
|
|
26
|
-
returned lakehouse to the viewer's permitted data.
|
|
26
|
+
returned lakehouse to the viewer's permitted data. Supply trusted registry
|
|
27
|
+
parameters through `queryParams`; query callers cannot override these values. Origin and Fetch Metadata
|
|
27
28
|
checks reject cross-site browser requests; they do not authenticate viewers.
|
|
28
29
|
|
|
29
30
|
The handler validates operation input and output, enforces query row and duration
|
|
30
|
-
bounds, propagates cancellation, and returns request IDs
|
|
31
|
-
disclosed only when both the operation policy and `canDiscloseSql` allow it.
|
|
31
|
+
bounds, propagates cancellation, and returns query evidence and request IDs.
|
|
32
32
|
Keep credentials and operation implementations on the server.
|
|
33
33
|
|
|
34
34
|
See [operation contracts](contract.md), the [client](client.md), and the
|
package/docs/styling.md
CHANGED
|
@@ -5,7 +5,7 @@ and `<TextContent>` for prose. Standard widgets use [views and bindings](widgets
|
|
|
5
5
|
custom controls and direct widget shells use [`/react/ui`](ui.md).
|
|
6
6
|
|
|
7
7
|
Configure theme, palette, accent, typography, density, radius, and elevation through
|
|
8
|
-
`
|
|
8
|
+
`app.appearance`; see [appearance](formatting-and-appearance.md). Call
|
|
9
9
|
`injectDataAppStyles()` before mounting. Use one `<DataApp>` per document.
|
|
10
10
|
|
|
11
11
|
## Customize
|
|
@@ -36,6 +36,12 @@ are consumed, so local colors, fonts, and spacing compose. Portals inherit from
|
|
|
36
36
|
their actual DOM ancestors. Normal unlayered app CSS overrides package rules.
|
|
37
37
|
`DataAppStyle` checks public inline custom-property names.
|
|
38
38
|
|
|
39
|
+
`--atbl-control-text-size` controls editable text, including compact search fields,
|
|
40
|
+
date segments, and annotation editors. It defaults to the body text size.
|
|
41
|
+
Package controls and custom controls with `data-atbl-control="text"` enforce a
|
|
42
|
+
16px minimum on narrow or touch viewports to prevent browser focus zoom. Keep
|
|
43
|
+
that minimum when adding app-owned input styles.
|
|
44
|
+
|
|
39
45
|
When overriding `--atbl-accent` directly, also choose `--atbl-on-accent` with
|
|
40
46
|
sufficient contrast. Prefer appearance configuration for brand and chart colors.
|
|
41
47
|
|
|
@@ -51,5 +57,6 @@ Use package controls when possible. For a custom native control:
|
|
|
51
57
|
|
|
52
58
|
These hooks provide cursor and focus styling; keep native semantics, accessible
|
|
53
59
|
names, disabled state, and keyboard behavior. `DataAppStyleHooks` checks hook
|
|
54
|
-
values. Use `
|
|
55
|
-
|
|
60
|
+
values. Use `default` for tooltip targets that only reveal information on hover
|
|
61
|
+
or tap, and `action` for controls that perform an action. Use `inset` focus inside
|
|
62
|
+
clipped surfaces and `group` when a wrapper owns an input's outline. See [UI quality](ui-quality.md) for rendered verification.
|
package/docs/ui.md
CHANGED
|
@@ -13,22 +13,82 @@ request states to populate a shell.
|
|
|
13
13
|
Direct `<DateRangePicker>`, `<DimensionPicker>`, and `useAppVariables()` support
|
|
14
14
|
custom control ownership. The caller supplies values and change handlers.
|
|
15
15
|
Custom tables use `<DataTable>` and its cell helpers; controls and overlays
|
|
16
|
-
include `<SearchField>`, `<
|
|
16
|
+
include `<SearchField>`, `<ChoicePicker>`, `<Tabs>`, `<Sheet>`, and `<HelpPopover>`.
|
|
17
17
|
|
|
18
18
|
Standalone `<AboutData>`, glossary components, and `<PresentStory>` support
|
|
19
19
|
custom inspection and presentation. Supply registered context and evidence, and
|
|
20
20
|
derive findings from the displayed data. Standard widgets and `<DataApp>`
|
|
21
21
|
already own these experiences.
|
|
22
22
|
|
|
23
|
-
For framework mounting, `<DataAppProvider>` supplies shared requests and one inspection sheet. Wrap custom shells in it.
|
|
23
|
+
For framework mounting, `<DataAppProvider app={app}>` supplies app identity, shared requests and one inspection sheet. Wrap custom shells in it; app components inherit identity and appearance.
|
|
24
24
|
Call `injectDataAppStyles()` before mounting either entry; imports do not install
|
|
25
25
|
styles.
|
|
26
26
|
|
|
27
|
+
## Keyboard actions
|
|
28
|
+
|
|
29
|
+
Use `isPlainKeyEvent()` from `/react/ui` in custom key handlers so local actions
|
|
30
|
+
leave shortcut modifiers and IME composition untouched:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
onKeyDown={event => {
|
|
34
|
+
if (event.key === 'Enter' && isPlainKeyEvent(event.nativeEvent)) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
submit();
|
|
37
|
+
}
|
|
38
|
+
}}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Pass `{ allowShift: true }` when the handler also supports Shift gestures.
|
|
42
|
+
|
|
43
|
+
## Choose a selection control
|
|
44
|
+
|
|
45
|
+
Use `<DimensionPicker>` for typed field filters and `<ChoicePicker>` for searchable
|
|
46
|
+
values. A single value uses a checkmark and closes after selection; multiple
|
|
47
|
+
values use checkbox indicators and keep the popup open. These pickers expose
|
|
48
|
+
listbox options, including search and loading feedback.
|
|
49
|
+
|
|
50
|
+
Use `<MenuTrigger>`, `<MenuButton>`, `<MenuPopover>`, `<Menu>`, and `<MenuItem>`
|
|
51
|
+
for commands or short choice menus
|
|
52
|
+
such as sort order or display mode. Actions use `onAction`. Set
|
|
53
|
+
`selectionMode="single"` for mutually exclusive choices (radio menu items), or
|
|
54
|
+
`selectionMode="multiple"` for independent toggles (checkbox menu items). Menus
|
|
55
|
+
support arrow keys, typeahead, Escape, and focus return to their trigger. Keep
|
|
56
|
+
search fields and other form inputs outside menus.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<MenuTrigger>
|
|
60
|
+
<MenuButton>Sort order</MenuButton>
|
|
61
|
+
<MenuPopover>
|
|
62
|
+
<Menu
|
|
63
|
+
aria-label="Sort order"
|
|
64
|
+
selectionMode="single"
|
|
65
|
+
selectedKeys={[sortOrder]}
|
|
66
|
+
onSelectionChange={keys => {
|
|
67
|
+
if (keys !== 'all') setSortOrder(String([...keys][0]));
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<MenuItem id="highest">Highest first</MenuItem>
|
|
71
|
+
<MenuItem id="lowest">Lowest first</MenuItem>
|
|
72
|
+
</Menu>
|
|
73
|
+
</MenuPopover>
|
|
74
|
+
</MenuTrigger>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`<MenuButton>` accepts text or icons; give icon-only buttons an accessible name.
|
|
78
|
+
`<MenuPopover>` owns placement and popup styling.
|
|
79
|
+
|
|
80
|
+
Use `<MenuSection>` to group choices with their own selection state and
|
|
81
|
+
`<MenuSeparator>` between groups or commands. Menu selection closes the popup
|
|
82
|
+
by default; set `shouldCloseOnSelect={false}` for repeated toggles. Selection
|
|
83
|
+
indicators derive from the menu or section's selection mode; do not add checkbox
|
|
84
|
+
markup to single-choice items.
|
|
85
|
+
|
|
27
86
|
## Charts
|
|
28
87
|
|
|
29
88
|
Choose a chart using the [visualization guide](widgets.md#choose-a-visualization).
|
|
30
89
|
Import `<BarChart>`, `<LineChart>`, `<AreaChart>`, `<PieChart>`, or `<ScatterChart>` from
|
|
31
|
-
`/react/ui
|
|
90
|
+
`/react/ui`; `<ComposedChart>` exposes the series primitives for custom plots.
|
|
91
|
+
Compose the visual inside `<VisualizationWidget dataset={dataset} source={result}>`
|
|
32
92
|
so rows, loading state, evidence, and inspection come from that dataset.
|
|
33
93
|
Pass ordered items with unique, nonblank IDs and finite numbers. Bar and pie
|
|
34
94
|
values must be nonnegative. Use `formatValue` for domain formatting.
|
|
@@ -39,3 +99,98 @@ intervals. Distinguish a missing observation from measured zero when preparing
|
|
|
39
99
|
the samples. Pie slices represent mutually exclusive parts of one total; shares
|
|
40
100
|
use the sum of supplied items, so include Other when showing a subset of the
|
|
41
101
|
whole. Scatter points represent independent X/Y observations.
|
|
102
|
+
|
|
103
|
+
### Composed charts
|
|
104
|
+
|
|
105
|
+
Use `<ComposedChart>` from `/react/ui` for multiple series or mixed marks inside
|
|
106
|
+
`<VisualizationWidget>`. Pass the displayed rows and select fields with `dataKey`.
|
|
107
|
+
`<ComposedChart.Bar>`, `<ComposedChart.Line>`, and `<ComposedChart.Area>` share the
|
|
108
|
+
standalone charts' themed series primitives; axes, scatter, tooltip, and reference
|
|
109
|
+
primitives are also available.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<VisualizationWidget dataset={revenue} source={result}>
|
|
113
|
+
{rows => (
|
|
114
|
+
<ComposedChart data={rows} ariaLabel="Monthly revenue and target in euros">
|
|
115
|
+
<ComposedChart.XAxis dataKey="month" />
|
|
116
|
+
<ComposedChart.YAxis />
|
|
117
|
+
<ComposedChart.Tooltip />
|
|
118
|
+
<ComposedChart.Legend />
|
|
119
|
+
<ComposedChart.Bar dataKey="revenue" name="Revenue (€)" />
|
|
120
|
+
<ComposedChart.Line
|
|
121
|
+
dataKey="target"
|
|
122
|
+
name="Target (€)"
|
|
123
|
+
stroke="var(--atbl-chart-2)"
|
|
124
|
+
/>
|
|
125
|
+
</ComposedChart>
|
|
126
|
+
)}
|
|
127
|
+
</VisualizationWidget>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Label series and units clearly. See component JSDoc and the
|
|
131
|
+
[Recharts API](https://recharts.github.io/en-US/api/ComposedChart/) for options.
|
|
132
|
+
The dataset owns loading, empty results, evidence, CSV, and story content; keep
|
|
133
|
+
its columns aligned with the displayed measures.
|
|
134
|
+
|
|
135
|
+
### Legends
|
|
136
|
+
|
|
137
|
+
Include `<ComposedChart.Legend />` to derive labels and markers from the series;
|
|
138
|
+
omit it when no legend is needed. For any chart, compose `<ChartLegend>` with
|
|
139
|
+
`<ChartLegend.Item>`, `<ChartLegend.Marker>`, and `<ChartLegend.Label>` using the
|
|
140
|
+
same names and colors as the plot.
|
|
141
|
+
`<PieChart>` includes a legend by default; `showLegend={false}` omits it.
|
|
142
|
+
|
|
143
|
+
Legends align cells in a container-responsive grid, truncate labels, and reserve
|
|
144
|
+
an overflow cell for “+X more.” Use `maxVisibleItems` to choose the limit or
|
|
145
|
+
`layout="vertical"` for a list. Legends describe series; authored toggles or a
|
|
146
|
+
composed legend's `onClick` leave series visibility to the app.
|
|
147
|
+
|
|
148
|
+
## Filter controls
|
|
149
|
+
|
|
150
|
+
Direct controls own accessible input and call the supplied change handler;
|
|
151
|
+
filter declarations own URL state, validation, and operation meaning.
|
|
152
|
+
|
|
153
|
+
| Control | Use |
|
|
154
|
+
| ---------------------------------- | ----------------------------------------------------------------- |
|
|
155
|
+
| `<SearchField>` | Search text |
|
|
156
|
+
| `<Select>` | One choice from a small fixed list |
|
|
157
|
+
| `<ChoicePicker>` | Searchable choices with explicit `selectionMode` |
|
|
158
|
+
| `<RadioGroup>` and `<Radio>` | Visible exclusive choices |
|
|
159
|
+
| `<CheckboxGroup>` and `<Checkbox>` | Visible independent choices |
|
|
160
|
+
| `<SegmentedControl>` | Compact radio choices |
|
|
161
|
+
| `<NumberField>` | Localized numeric input; empty is `null` |
|
|
162
|
+
| `<NumberRangeField>` | Exact, inclusive minimum/maximum; omitted bounds are unrestricted |
|
|
163
|
+
| `<DateRangePicker>` | Explicit or relative periods |
|
|
164
|
+
| `<DimensionPicker>` | Typed categorical predicates with fixed or facet options |
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<RadioGroup label="Metric" value={metric} onChange={setMetric}>
|
|
168
|
+
<Radio value="orders">Orders</Radio>
|
|
169
|
+
<Radio value="revenue">Revenue</Radio>
|
|
170
|
+
</RadioGroup>
|
|
171
|
+
|
|
172
|
+
<CheckboxGroup label="Statuses" values={statuses} onChange={setStatuses}>
|
|
173
|
+
<Checkbox value="paid" label="Paid" />
|
|
174
|
+
<Checkbox value="pending" label="Pending" />
|
|
175
|
+
</CheckboxGroup>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
A standalone `<Checkbox>` uses `checked` and `onChange`; a group item supplies
|
|
179
|
+
`value`. `<SegmentedControl>` accepts the same labeled options as `<Select>` and
|
|
180
|
+
keeps radio-group semantics. Numeric range controls retain invalid draft bounds
|
|
181
|
+
and emit only valid intervals. Numeric input and compact selects respect the
|
|
182
|
+
shared mobile text-size minimum.
|
|
183
|
+
|
|
184
|
+
Use `<FilterBar>` to arrange predicates and `<VariableBar>` for mixed app
|
|
185
|
+
parameters. Each filter shows its current value; categorical single selection includes All.
|
|
186
|
+
`<FilterActions>` provides one Clear icon with a tooltip, plus optional paired
|
|
187
|
+
Apply/Cancel actions for app-owned drafts. The caller owns those actions; the
|
|
188
|
+
components do not infer query state.
|
|
189
|
+
|
|
190
|
+
`<NumberFilterPicker>` composes numeric fields inside a dedicated popover. Edits
|
|
191
|
+
remain local until Apply; Cancel and dismissal discard them. Use `<NumberField>`
|
|
192
|
+
and `<NumberRangeField>` as inline input primitives when composing forms.
|
|
193
|
+
|
|
194
|
+
Use `<Button variant="primary">` for the main committing action, such as Apply.
|
|
195
|
+
Primary buttons use the theme foreground as their fill and the theme background
|
|
196
|
+
for contrasting text. Use outline and ghost buttons for secondary actions.
|