@pythia-software/query-table-ui 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ and selection toolbar.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install @pythia-software/query-table-core @pythia-software/query-table-react @pythia-software/query-table-ui react
10
+ npm install @pythia-software/query-table-core @pythia-software/query-table-react @pythia-software/query-table-ui react react-dom
11
11
  ```
12
12
 
13
13
  ## Use
@@ -40,9 +40,44 @@ export function OrdersTable({ table, schema }) {
40
40
  }
41
41
  ```
42
42
 
43
+ `DataTable` virtualizes body rows and uses a 600px maximum-height scroll
44
+ viewport by default. Set `maxHeight` to match the surrounding layout;
45
+ `estimateRowHeight` (37px by default) and `overscan` (8 rows) are available for
46
+ unusually sized or expensive custom row renderers. Visible rows are measured,
47
+ so the estimate does not require every row to have a fixed height.
48
+
43
49
  The bundled `link` renderer allows only HTTP(S), email, telephone, relative,
44
50
  and fragment URLs. Applications remain responsible for validating URLs emitted
45
51
  by custom renderers.
46
52
 
53
+ The query builder's **Share** button copies a URL containing the complete
54
+ current query, including a saved default query. Configure `useQueryTable` with
55
+ `syncUrl: true` so recipients load shared query URLs. Query tokens are encoded,
56
+ not encrypted, and may contain raw filter values.
57
+
58
+ `SelectionToolbar` action callbacks receive `(selectedIds, selection)`, so
59
+ consumer-owned bulk actions can call `selection.replace(ids)` or
60
+ `selection.retain(ids)` atomically. Existing callbacks that use only the first
61
+ argument remain compatible.
62
+
63
+ ## Mobile behavior
64
+
65
+ The bundled theme keeps the table as a horizontally scrollable grid on narrow
66
+ screens so columns remain comparable. It adds a scroll cue, touch-sized query
67
+ controls, button-based reordering alongside desktop drag-and-drop, sticky row
68
+ selection, single-column metric cards, and bottom-sheet menus and dialogs. Keep
69
+ the standard viewport meta tag in the host page:
70
+
71
+ ```html
72
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
73
+ ```
74
+
75
+ Applications that omit `theme.css` and use only `classNames` are responsible for
76
+ providing their own responsive and touch-target styles.
77
+
47
78
  See the [repository README](https://github.com/Pythia-Software/query-table#readme)
48
79
  for customization and backend integration details.
80
+
81
+ ## SELECT editor
82
+
83
+ `QueryBuilder` includes an **Edit columns** action. The exported `SelectColumnEditor` also accepts `{ api, onClose }` for standalone use. It supports draft column ordering, a searchable catalogue, sampled value frequencies, and a lazy-loaded CodeMirror formula editor with grouped live previews and regex inspection. Shared-definition saves and query-layout application are separate actions.
@@ -0,0 +1,251 @@
1
+ // src/FormulaEditor.tsx
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { EditorState } from "@codemirror/state";
4
+ import {
5
+ EditorView,
6
+ keymap,
7
+ lineNumbers,
8
+ hoverTooltip
9
+ } from "@codemirror/view";
10
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
11
+ import {
12
+ autocompletion,
13
+ completionKeymap,
14
+ closeBrackets,
15
+ closeBracketsKeymap
16
+ } from "@codemirror/autocomplete";
17
+ import { linter } from "@codemirror/lint";
18
+ import {
19
+ StreamLanguage,
20
+ syntaxHighlighting,
21
+ defaultHighlightStyle,
22
+ bracketMatching
23
+ } from "@codemirror/language";
24
+ import {
25
+ FORMULA_FUNCTIONS,
26
+ FormulaError
27
+ } from "@pythia-software/query-table-core";
28
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
29
+ var language = StreamLanguage.define({
30
+ token(stream) {
31
+ if (stream.eatSpace()) return null;
32
+ if (stream.match(/^"(?:[^"\\]|\\.)*"?/)) return "string";
33
+ if (stream.match(/^\[(?:[^\]]|\]\])*\]/)) return "variableName";
34
+ if (stream.match(/^(?:\d+(?:\.\d*)?|\.\d+)/)) return "number";
35
+ if (stream.match(/^(?:AND|OR|NOT|IN|BETWEEN|NULL|TRUE|FALSE)\b/i))
36
+ return "keyword";
37
+ if (stream.match(/^[a-zA-Z_][a-zA-Z_0-9]*/))
38
+ return "function(variableName)";
39
+ stream.next();
40
+ return "operator";
41
+ }
42
+ });
43
+ function signatureAt(text) {
44
+ const stack = [];
45
+ let quoted = false, field = false;
46
+ for (let i = 0; i < text.length; i++) {
47
+ const c = text[i];
48
+ if (quoted) {
49
+ if (c === "\\") i++;
50
+ else if (c === '"') quoted = false;
51
+ continue;
52
+ }
53
+ if (field) {
54
+ if (c === "]") {
55
+ if (text[i + 1] === "]") i++;
56
+ else field = false;
57
+ }
58
+ continue;
59
+ }
60
+ if (c === '"') {
61
+ quoted = true;
62
+ continue;
63
+ }
64
+ if (c === "[") {
65
+ field = true;
66
+ continue;
67
+ }
68
+ if (c === "(")
69
+ stack.push({
70
+ name: text.slice(0, i).match(/([A-Za-z_]+)\s*$/)?.[1]?.toUpperCase() ?? "",
71
+ argument: 1
72
+ });
73
+ else if (c === ")") stack.pop();
74
+ else if (c === "," && stack.length) stack[stack.length - 1].argument++;
75
+ }
76
+ const call = stack[stack.length - 1], spec = FORMULA_FUNCTIONS.find((f) => f.name === call?.name);
77
+ return spec && call ? `${spec.signature} \xB7 Argument ${call.argument} \u2014 ${spec.description}` : "Use [field] references. Ctrl+Space opens suggestions.";
78
+ }
79
+ function FormulaEditor({
80
+ value,
81
+ onChange,
82
+ fields,
83
+ compile
84
+ }) {
85
+ const [hint, setHint] = useState(
86
+ "Use [field] references. Ctrl+Space opens suggestions."
87
+ );
88
+ const parent = useRef(null), editor = useRef();
89
+ const latest = useRef({ onChange, fields, compile });
90
+ latest.current = { onChange, fields, compile };
91
+ useEffect(() => {
92
+ if (!parent.current) return;
93
+ const view = new EditorView({
94
+ parent: parent.current,
95
+ state: EditorState.create({
96
+ doc: value,
97
+ extensions: [
98
+ lineNumbers(),
99
+ history(),
100
+ language,
101
+ syntaxHighlighting(defaultHighlightStyle),
102
+ bracketMatching(),
103
+ closeBrackets(),
104
+ EditorView.lineWrapping,
105
+ EditorView.contentAttributes.of({
106
+ "aria-label": "Computed column formula",
107
+ spellcheck: "false"
108
+ }),
109
+ keymap.of([
110
+ ...closeBracketsKeymap,
111
+ ...completionKeymap,
112
+ ...defaultKeymap,
113
+ ...historyKeymap
114
+ ]),
115
+ autocompletion({
116
+ override: [
117
+ (context) => {
118
+ const word = context.matchBefore(
119
+ /\[[^\]]*|[a-zA-Z_][a-zA-Z_0-9]*/
120
+ );
121
+ if (!word && !context.explicit) return null;
122
+ return {
123
+ from: word?.from ?? context.pos,
124
+ options: [
125
+ ...latest.current.fields.map((f) => ({
126
+ label: `[${f.name.replace(/\]/g, "]]")}]`,
127
+ displayLabel: f.label,
128
+ detail: f.type,
129
+ type: "variable",
130
+ info: f.name
131
+ })),
132
+ ...FORMULA_FUNCTIONS.map((f) => ({
133
+ label: f.name,
134
+ type: "function",
135
+ detail: f.signature,
136
+ info: f.description,
137
+ apply: `${f.name}(`
138
+ })),
139
+ ...[
140
+ "TRUE",
141
+ "FALSE",
142
+ "NULL",
143
+ "AND",
144
+ "OR",
145
+ "NOT",
146
+ "IN",
147
+ "BETWEEN"
148
+ ].map((label) => ({ label, type: "keyword" }))
149
+ ]
150
+ };
151
+ }
152
+ ]
153
+ }),
154
+ linter(
155
+ (view2) => {
156
+ try {
157
+ latest.current.compile(view2.state.doc.toString());
158
+ return [];
159
+ } catch (e) {
160
+ return [
161
+ {
162
+ from: Math.min(
163
+ e instanceof FormulaError ? e.from : 0,
164
+ view2.state.doc.length
165
+ ),
166
+ to: Math.min(
167
+ e instanceof FormulaError ? e.to : view2.state.doc.length,
168
+ view2.state.doc.length
169
+ ),
170
+ severity: "error",
171
+ message: e instanceof Error ? e.message : String(e)
172
+ }
173
+ ];
174
+ }
175
+ },
176
+ { delay: 250 }
177
+ ),
178
+ hoverTooltip((view2, pos) => {
179
+ const line = view2.state.doc.lineAt(pos);
180
+ const left = line.text.slice(0, pos - line.from).match(/[A-Za-z_0-9]*$/)?.[0] ?? "";
181
+ const right = line.text.slice(pos - line.from).match(/^[A-Za-z_0-9]*/)?.[0] ?? "";
182
+ const spec = FORMULA_FUNCTIONS.find(
183
+ (f) => f.name === (left + right).toUpperCase()
184
+ );
185
+ if (!spec) return null;
186
+ return {
187
+ pos: pos - left.length,
188
+ end: pos + right.length,
189
+ create() {
190
+ const dom = document.createElement("div");
191
+ dom.className = "qt-function-help";
192
+ dom.textContent = `${spec.signature} \u2014 ${spec.description}`;
193
+ return { dom };
194
+ }
195
+ };
196
+ }),
197
+ EditorView.updateListener.of((update) => {
198
+ if (update.docChanged)
199
+ latest.current.onChange(update.state.doc.toString());
200
+ if (update.docChanged || update.selectionSet)
201
+ setHint(
202
+ signatureAt(
203
+ update.state.doc.sliceString(
204
+ 0,
205
+ update.state.selection.main.head
206
+ )
207
+ )
208
+ );
209
+ }),
210
+ EditorView.theme({
211
+ "&": {
212
+ fontSize: "13px",
213
+ border: "1px solid var(--qt-border, #cbd5e1)",
214
+ borderRadius: "6px"
215
+ },
216
+ ".cm-scroller": {
217
+ minHeight: "130px",
218
+ maxHeight: "260px",
219
+ fontFamily: "ui-monospace, monospace"
220
+ },
221
+ ".cm-content": { padding: "12px 0" },
222
+ ".cm-gutters": {
223
+ background: "var(--qt-bg-muted, #f8fafc)",
224
+ color: "var(--qt-muted, #64748b)"
225
+ }
226
+ })
227
+ ]
228
+ })
229
+ });
230
+ editor.current = view;
231
+ return () => {
232
+ view.destroy();
233
+ editor.current = void 0;
234
+ };
235
+ }, []);
236
+ useEffect(() => {
237
+ const view = editor.current;
238
+ if (view && view.state.doc.toString() !== value)
239
+ view.dispatch({
240
+ changes: { from: 0, to: view.state.doc.length, insert: value }
241
+ });
242
+ }, [value]);
243
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
244
+ /* @__PURE__ */ jsx("div", { ref: parent }),
245
+ /* @__PURE__ */ jsx("p", { className: "qt-formula-hint", "aria-live": "polite", children: hint })
246
+ ] });
247
+ }
248
+ export {
249
+ FormulaEditor as default
250
+ };
251
+ //# sourceMappingURL=FormulaEditor-YCPICDIH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/FormulaEditor.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { EditorState } from \"@codemirror/state\";\nimport {\n EditorView,\n keymap,\n lineNumbers,\n hoverTooltip,\n} from \"@codemirror/view\";\nimport { defaultKeymap, history, historyKeymap } from \"@codemirror/commands\";\nimport {\n autocompletion,\n completionKeymap,\n closeBrackets,\n closeBracketsKeymap,\n} from \"@codemirror/autocomplete\";\nimport { linter } from \"@codemirror/lint\";\nimport {\n StreamLanguage,\n syntaxHighlighting,\n defaultHighlightStyle,\n bracketMatching,\n} from \"@codemirror/language\";\nimport {\n FORMULA_FUNCTIONS,\n FormulaError,\n type FieldDef,\n type FormulaPlan,\n} from \"@pythia-software/query-table-core\";\n\nexport interface FormulaEditorProps {\n value: string;\n onChange: (value: string) => void;\n fields: FieldDef[];\n compile: (value: string) => FormulaPlan;\n}\nconst language = StreamLanguage.define({\n token(stream) {\n if (stream.eatSpace()) return null;\n if (stream.match(/^\"(?:[^\"\\\\]|\\\\.)*\"?/)) return \"string\";\n if (stream.match(/^\\[(?:[^\\]]|\\]\\])*\\]/)) return \"variableName\";\n if (stream.match(/^(?:\\d+(?:\\.\\d*)?|\\.\\d+)/)) return \"number\";\n if (stream.match(/^(?:AND|OR|NOT|IN|BETWEEN|NULL|TRUE|FALSE)\\b/i))\n return \"keyword\";\n if (stream.match(/^[a-zA-Z_][a-zA-Z_0-9]*/))\n return \"function(variableName)\";\n stream.next();\n return \"operator\";\n },\n});\nfunction signatureAt(text: string): string {\n const stack: { name: string; argument: number }[] = [];\n let quoted = false,\n field = false;\n for (let i = 0; i < text.length; i++) {\n const c = text[i];\n if (quoted) {\n if (c === \"\\\\\") i++;\n else if (c === '\"') quoted = false;\n continue;\n }\n if (field) {\n if (c === \"]\") {\n if (text[i + 1] === \"]\") i++;\n else field = false;\n }\n continue;\n }\n if (c === '\"') {\n quoted = true;\n continue;\n }\n if (c === \"[\") {\n field = true;\n continue;\n }\n if (c === \"(\")\n stack.push({\n name:\n text\n .slice(0, i)\n .match(/([A-Za-z_]+)\\s*$/)?.[1]\n ?.toUpperCase() ?? \"\",\n argument: 1,\n });\n else if (c === \")\") stack.pop();\n else if (c === \",\" && stack.length) stack[stack.length - 1]!.argument++;\n }\n const call = stack[stack.length - 1],\n spec = FORMULA_FUNCTIONS.find((f) => f.name === call?.name);\n return spec && call\n ? `${spec.signature} · Argument ${call.argument} — ${spec.description}`\n : \"Use [field] references. Ctrl+Space opens suggestions.\";\n}\nexport default function FormulaEditor({\n value,\n onChange,\n fields,\n compile,\n}: FormulaEditorProps) {\n const [hint, setHint] = useState(\n \"Use [field] references. Ctrl+Space opens suggestions.\",\n );\n const parent = useRef<HTMLDivElement>(null),\n editor = useRef<EditorView>();\n const latest = useRef({ onChange, fields, compile });\n latest.current = { onChange, fields, compile };\n useEffect(() => {\n if (!parent.current) return;\n const view = new EditorView({\n parent: parent.current,\n state: EditorState.create({\n doc: value,\n extensions: [\n lineNumbers(),\n history(),\n language,\n syntaxHighlighting(defaultHighlightStyle),\n bracketMatching(),\n closeBrackets(),\n EditorView.lineWrapping,\n EditorView.contentAttributes.of({\n \"aria-label\": \"Computed column formula\",\n spellcheck: \"false\",\n }),\n keymap.of([\n ...closeBracketsKeymap,\n ...completionKeymap,\n ...defaultKeymap,\n ...historyKeymap,\n ]),\n autocompletion({\n override: [\n (context) => {\n const word = context.matchBefore(\n /\\[[^\\]]*|[a-zA-Z_][a-zA-Z_0-9]*/,\n );\n if (!word && !context.explicit) return null;\n return {\n from: word?.from ?? context.pos,\n options: [\n ...latest.current.fields.map((f) => ({\n label: `[${f.name.replace(/\\]/g, \"]]\")}]`,\n displayLabel: f.label,\n detail: f.type,\n type: \"variable\",\n info: f.name,\n })),\n ...FORMULA_FUNCTIONS.map((f) => ({\n label: f.name,\n type: \"function\",\n detail: f.signature,\n info: f.description,\n apply: `${f.name}(`,\n })),\n ...[\n \"TRUE\",\n \"FALSE\",\n \"NULL\",\n \"AND\",\n \"OR\",\n \"NOT\",\n \"IN\",\n \"BETWEEN\",\n ].map((label) => ({ label, type: \"keyword\" })),\n ],\n };\n },\n ],\n }),\n linter(\n (view) => {\n try {\n latest.current.compile(view.state.doc.toString());\n return [];\n } catch (e) {\n return [\n {\n from: Math.min(\n e instanceof FormulaError ? e.from : 0,\n view.state.doc.length,\n ),\n to: Math.min(\n e instanceof FormulaError ? e.to : view.state.doc.length,\n view.state.doc.length,\n ),\n severity: \"error\" as const,\n message: e instanceof Error ? e.message : String(e),\n },\n ];\n }\n },\n { delay: 250 },\n ),\n hoverTooltip((view, pos) => {\n const line = view.state.doc.lineAt(pos);\n const left =\n line.text\n .slice(0, pos - line.from)\n .match(/[A-Za-z_0-9]*$/)?.[0] ?? \"\";\n const right =\n line.text.slice(pos - line.from).match(/^[A-Za-z_0-9]*/)?.[0] ??\n \"\";\n const spec = FORMULA_FUNCTIONS.find(\n (f) => f.name === (left + right).toUpperCase(),\n );\n if (!spec) return null;\n return {\n pos: pos - left.length,\n end: pos + right.length,\n create() {\n const dom = document.createElement(\"div\");\n dom.className = \"qt-function-help\";\n dom.textContent = `${spec.signature} — ${spec.description}`;\n return { dom };\n },\n };\n }),\n EditorView.updateListener.of((update) => {\n if (update.docChanged)\n latest.current.onChange(update.state.doc.toString());\n if (update.docChanged || update.selectionSet)\n setHint(\n signatureAt(\n update.state.doc.sliceString(\n 0,\n update.state.selection.main.head,\n ),\n ),\n );\n }),\n EditorView.theme({\n \"&\": {\n fontSize: \"13px\",\n border: \"1px solid var(--qt-border, #cbd5e1)\",\n borderRadius: \"6px\",\n },\n \".cm-scroller\": {\n minHeight: \"130px\",\n maxHeight: \"260px\",\n fontFamily: \"ui-monospace, monospace\",\n },\n \".cm-content\": { padding: \"12px 0\" },\n \".cm-gutters\": {\n background: \"var(--qt-bg-muted, #f8fafc)\",\n color: \"var(--qt-muted, #64748b)\",\n },\n }),\n ],\n }),\n });\n editor.current = view;\n return () => {\n view.destroy();\n editor.current = undefined;\n };\n }, []);\n useEffect(() => {\n const view = editor.current;\n if (view && view.state.doc.toString() !== value)\n view.dispatch({\n changes: { from: 0, to: view.state.doc.length, insert: value },\n });\n }, [value]);\n return (\n <>\n <div ref={parent} />\n <p className=\"qt-formula-hint\" aria-live=\"polite\">\n {hint}\n </p>\n </>\n );\n}\n"],"mappings":"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","names":["view"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ReactNode, SetStateAction } from 'react';
2
2
  import { FieldDef, QueryState, RowId, FieldStats, WhereClause } from '@pythia-software/query-table-core';
3
3
  import { SelectionApi, ColumnDragApi, QueryTableApi, AggregationsApi, SavedQueriesApi } from '@pythia-software/query-table-react';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
5
 
5
6
  interface CellContext<Row = any, V = unknown> {
6
7
  value: V;
@@ -29,6 +30,7 @@ interface TableClassNames {
29
30
  headerRow?: string;
30
31
  th?: string;
31
32
  resizeHandle?: string;
33
+ resizeGuide?: string;
32
34
  tbody?: string;
33
35
  row?: string;
34
36
  rowSelected?: string;
@@ -79,6 +81,12 @@ interface DataTableProps<Row> {
79
81
  total?: number | null;
80
82
  loading?: boolean;
81
83
  emptyMessage?: string;
84
+ /** Maximum height of the scroll viewport. Defaults to 600px. */
85
+ maxHeight?: number | string;
86
+ /** Estimated row height in pixels; measured rows replace the estimate. Defaults to 37. */
87
+ estimateRowHeight?: number;
88
+ /** Extra rows rendered above and below the visible range. Defaults to 8. */
89
+ overscan?: number;
82
90
  /** Tailwind / bespoke styling slots (Mode B). */
83
91
  classNames?: TableClassNames;
84
92
  }
@@ -146,9 +154,9 @@ declare function CellMenu<Row>({ field, value, x, y, onAddFilter, onClose, class
146
154
 
147
155
  interface SelectionToolbarProps {
148
156
  selection: SelectionApi;
149
- /** Consumer-rendered action buttons. Receives the selected ids so the consumer
150
- * can gate/enable actions (e.g. mixed-state rules). */
151
- actions: (selectedIds: RowId[]) => ReactNode;
157
+ /** Consumer-rendered action buttons. Receives the selected ids and the public
158
+ * selection API. Existing callbacks that accept only ids remain supported. */
159
+ actions: (selectedIds: RowId[], selection: SelectionApi) => ReactNode;
152
160
  /** Hidden when nothing is selected unless `alwaysShow`. */
153
161
  alwaysShow?: boolean;
154
162
  className?: string;
@@ -162,4 +170,10 @@ interface SavedQueriesModalProps {
162
170
  }
163
171
  declare function SavedQueriesModal({ saved, onClose, onLoad }: SavedQueriesModalProps): ReactNode;
164
172
 
165
- export { type CellContext, CellMenu, type CellMenuProps, type CellRenderer, DataTable, type DataTableProps, FieldPicker, type FieldPickerProps, type MenuClassNames, MetricsPanel, type MetricsPanelProps, QueryBuilder, type QueryBuilderClassNames, type QueryBuilderProps, type RenderRegistry, SavedQueriesModal, type SavedQueriesModalProps, SelectionToolbar, type SelectionToolbarProps, type TableClassNames, defaultRenderers, resolveRenderer, safeLinkHref };
173
+ interface SelectColumnEditorProps<Row> {
174
+ api: QueryTableApi<Row>;
175
+ onClose: () => void;
176
+ }
177
+ declare function SelectColumnEditor<Row>({ api, onClose, }: SelectColumnEditorProps<Row>): react_jsx_runtime.JSX.Element;
178
+
179
+ export { type CellContext, CellMenu, type CellMenuProps, type CellRenderer, DataTable, type DataTableProps, FieldPicker, type FieldPickerProps, type MenuClassNames, MetricsPanel, type MetricsPanelProps, QueryBuilder, type QueryBuilderClassNames, type QueryBuilderProps, type RenderRegistry, SavedQueriesModal, type SavedQueriesModalProps, SelectColumnEditor, type SelectColumnEditorProps, SelectionToolbar, type SelectionToolbarProps, type TableClassNames, defaultRenderers, resolveRenderer, safeLinkHref };