@pythia-software/query-table-react 0.2.0 → 0.4.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/README.md CHANGED
@@ -20,6 +20,11 @@ function Orders({ schema, rows }) {
20
20
  }
21
21
  ```
22
22
 
23
+ `table.selection.replace(ids)` atomically makes any iterable of stable row IDs
24
+ the complete selection, including IDs outside the displayed page.
25
+ `table.selection.retain(ids)` atomically intersects the current selection with
26
+ an iterable. Both operations reset the Shift-click range anchor.
27
+
23
28
  ## Privacy defaults
24
29
 
25
30
  URL synchronization and durable browser storage are disabled by default. Set
@@ -31,3 +36,21 @@ is appropriate; sensitive applications should use an access-controlled server
31
36
 
32
37
  See the [repository README](https://github.com/Pythia-Software/query-table#readme)
33
38
  for transport and schema examples.
39
+
40
+ ## Shared computed column definitions
41
+
42
+ Pass a stable `computedColumnStore` (for example, `httpComputedColumnStore("/api/computed-columns")` from core) to `useQueryTable`. `api.computed` exposes the catalogue, compile/preview, save with revision checking, and reload operations. Queries store only `@computed/<id>` SELECT references; formula evaluation stays in browser workers. Without a store, definitions are in memory for the mounted hook. See the repository’s `docs/computed-columns.md` for the complete integration contract and PostgreSQL adapter.
43
+
44
+ ### Canonical keys and unavailable filters
45
+
46
+ `useQueryTable` accepts two optional pure callbacks:
47
+
48
+ - `canonicalizeQuery(query)` returns a query with external aliases resolved. It must
49
+ not throw. The returned query drives evaluation, sharing, and saved state.
50
+ - `validateQuery(query)` may throw an actionable error before row or metric execution.
51
+ Both local and server transports honor it, including restored queries. Rejection
52
+ clears results/totals while retaining the query so the user can repair its filters.
53
+
54
+ Memoize these callbacks and change their identity when external validation metadata
55
+ changes. Return immutable query values from the canonicalizer. No callback is required
56
+ for ordinary tables; the existing execution behavior remains the default.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,21 @@
1
- import { RowId, SelectColumn, FieldDef, QueryState, FieldSchema, SavedQuery, StorageAdapter, AggOp, AggregationClause, AggregationResult, WhereClause, DistinctValuesResult, OrderByClause, Transport } from '@pythia-software/query-table-core';
1
+ import { FormulaNode, FormulaValue, FormulaResult, ComputedColumn, FieldDef, ComputedColumnDraft, FormulaPlan, ColumnPreview, RowId, SelectColumn, QueryState, FieldSchema, SavedQuery, StorageAdapter, AggOp, AggregationClause, AggregationResult, WhereClause, DistinctValuesResult, FieldStats, OrderByClause, Transport, ComputedColumnStore } from '@pythia-software/query-table-core';
2
+
3
+ type FormulaWorkerFactory = () => Worker;
4
+ /** Host applications with a restrictive CSP can supply their own worker factory. */
5
+ declare const createFormulaWorker: FormulaWorkerFactory;
6
+ declare function evaluateFormulaRows(ast: FormulaNode, inputs: Record<string, FormulaValue>[], signal?: AbortSignal, factory?: FormulaWorkerFactory): Promise<FormulaResult[]>;
7
+
8
+ interface ComputedColumnsApi<Row> {
9
+ definitions: ComputedColumn[];
10
+ catalogue: FieldDef<Row>[];
11
+ loading: boolean;
12
+ error: string | null;
13
+ reload: () => Promise<void>;
14
+ save: (column: ComputedColumnDraft, expectedRevision: string | null) => Promise<ComputedColumn>;
15
+ compile: (source: string, editingId?: string) => FormulaPlan;
16
+ /** Sample the first N matching rows in current sort order, independent of table paging. */
17
+ preview: (source: string, count: number, signal?: AbortSignal, editingId?: string, includeInputs?: boolean) => Promise<ColumnPreview>;
18
+ }
2
19
 
3
20
  interface SelectionApi {
4
21
  selected: Set<RowId>;
@@ -9,6 +26,10 @@ interface SelectionApi {
9
26
  toggle: (id: RowId, shiftKey?: boolean) => void;
10
27
  /** Select / clear every row currently on the page. */
11
28
  setPage: (ids: RowId[], selected: boolean) => void;
29
+ /** Atomically replace the complete selection with arbitrary stable row ids. */
30
+ replace: (ids: Iterable<RowId>) => void;
31
+ /** Atomically retain only selected ids that are also present in `ids`. */
32
+ retain: (ids: Iterable<RowId>) => void;
12
33
  /** Header checkbox tri-state for the current page. */
13
34
  pageState: (pageIds: RowId[]) => "none" | "some" | "all";
14
35
  clear: () => void;
@@ -76,6 +97,10 @@ declare function useSavedQueries(key: string, currentQuery: QueryState, applyQue
76
97
  now: () => number): SavedQueriesApi;
77
98
 
78
99
  interface UseQueryTableOptions<Row> {
100
+ /** Normalize external aliases without storing presentation in query state. Must be pure and not throw. */
101
+ canonicalizeQuery?: (query: QueryState) => QueryState;
102
+ /** Reject an entire query before rows or metrics execute. Errors retain editable query state. */
103
+ validateQuery?: (query: QueryState) => void;
79
104
  schema: FieldSchema<Row>;
80
105
  /** Data access. Omit for a purely client-side table driven by `clientRows`. */
81
106
  transport?: Transport<Row>;
@@ -85,6 +110,10 @@ interface UseQueryTableOptions<Row> {
85
110
  * Pass localStorageAdapter() explicitly only when filter values are safe to
86
111
  * retain as cleartext JSON on the device. */
87
112
  storage?: StorageAdapter;
113
+ /** Shared reusable definitions, independent of saved-query storage. */
114
+ computedColumnStore?: ComputedColumnStore;
115
+ /** Override the worker factory when CSP disallows blob workers. */
116
+ formulaWorkerFactory?: FormulaWorkerFactory;
88
117
  /** Seed query, normalized and resource-bounded before use. When omitted the
89
118
  * hook resolves: opted-in URL → storage.loadLast → schema defaults. */
90
119
  initialQuery?: QueryState;
@@ -168,17 +197,36 @@ interface QueryTableApi<Row> {
168
197
  autoRefresh: AutoRefreshApi;
169
198
  refreshRow: (id: RowId) => Promise<void>;
170
199
  aggregations: AggregationsApi;
200
+ /** Append a predicate as its own AND term. */
171
201
  addFilter: (clause: WhereClause) => void;
202
+ /** Replace the whole term at `index` with a single predicate. */
172
203
  updateFilter: (index: number, clause: WhereClause) => void;
204
+ /** Remove the whole term at `index`. */
173
205
  removeFilter: (index: number) => void;
174
206
  clearFilters: () => void;
207
+ /** Replace one predicate inside a term (predIndex 0 for a literal term). */
208
+ updatePredicate: (termIndex: number, predIndex: number, clause: WhereClause) => void;
209
+ /** Remove one predicate; a group of one flattens to a literal, an emptied term drops. */
210
+ removePredicate: (termIndex: number, predIndex: number) => void;
211
+ /** Logically invert one predicate (flips the op, else toggles its NOT flag). */
212
+ negatePredicate: (termIndex: number, predIndex: number) => void;
213
+ /** Move the term at `fromIndex` to `toIndex` (an index in the list with the
214
+ * dragged term removed, so a drag preview's slot maps straight to the result). */
215
+ reorderFilters: (fromIndex: number, toIndex: number) => void;
216
+ /** OR-merge the term at `sourceIndex` into the term at `targetIndex`: the
217
+ * target becomes (or stays) an OR group holding both terms' predicates, and
218
+ * the source term is removed. This is the drag-onto-another-filter gesture. */
219
+ mergeFilters: (sourceIndex: number, targetIndex: number) => void;
175
220
  /** Reset the complete query state back to schema defaults. */
176
221
  resetAll: () => void;
177
222
  /** Keystroke-driven filter-value autocomplete (Transport.fetchDistinctValues). */
178
223
  filterValues: (field: string, search: string) => Promise<DistinctValuesResult>;
224
+ /** Dataset-wide field statistics for catalogue and picker affordances. */
225
+ fieldStats: (fields: string[], signal?: AbortSignal) => Promise<Record<string, FieldStats>>;
179
226
  toggleSort: (field: string, additive?: boolean) => void;
180
227
  setSort: (orderBy: OrderByClause[]) => void;
181
228
  select: SelectApi<Row>;
229
+ computed: ComputedColumnsApi<Row>;
182
230
  setLimit: (limit: number) => void;
183
231
  setOffset: (offset: number) => void;
184
232
  nextPage: () => void;
@@ -197,6 +245,6 @@ interface AggregationsApiState {
197
245
  loading: boolean;
198
246
  error: Error | null;
199
247
  }
200
- declare function useAggregations<Row>(query: QueryState, schema: FieldSchema<Row>, transport: Transport<Row> | undefined, clientRows: Row[] | undefined, debounceMs: number, nonce: number): AggregationsApiState;
248
+ declare function useAggregations<Row>(query: QueryState, schema: FieldSchema<Row>, transport: Transport<Row> | undefined, clientRows: Row[] | undefined, debounceMs: number, nonce: number, validateQuery?: (query: QueryState) => void): AggregationsApiState;
201
249
 
202
- export { type AggregationPatch, type AggregationsApi, type AggregationsApiState, type AutoRefreshApi, type AutoRefreshConfig, type AutoRefreshStatus, type ColumnDragApi, type QueryTableApi, type SavedQueriesApi, type SelectApi, type SelectionApi, type UseQueryTableOptions, useAggregations, useColumnDrag, useQueryTable, useSavedQueries, useSelect, useSelection };
250
+ export { type AggregationPatch, type AggregationsApi, type AggregationsApiState, type AutoRefreshApi, type AutoRefreshConfig, type AutoRefreshStatus, type ColumnDragApi, type ComputedColumnsApi, type FormulaWorkerFactory, type QueryTableApi, type SavedQueriesApi, type SelectApi, type SelectionApi, type UseQueryTableOptions, createFormulaWorker, evaluateFormulaRows, useAggregations, useColumnDrag, useQueryTable, useSavedQueries, useSelect, useSelection };