@pythia-software/query-table-ui 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 +31 -0
- package/dist/FormulaEditor-YCPICDIH.js +251 -0
- package/dist/FormulaEditor-YCPICDIH.js.map +1 -0
- package/dist/index.d.ts +23 -4
- package/dist/index.js +1612 -477
- package/dist/index.js.map +1 -1
- package/dist/theme.css +566 -1
- package/dist/theme.css.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -137,17 +137,72 @@ function formatRelative(iso) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// src/DataTable.tsx
|
|
140
|
-
import {
|
|
140
|
+
import { isComputedCellError } from "@pythia-software/query-table-core";
|
|
141
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState3 } from "react";
|
|
141
142
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
142
143
|
import { isSortable, readFieldValue } from "@pythia-software/query-table-core";
|
|
143
144
|
import { useColumnDrag } from "@pythia-software/query-table-react";
|
|
144
145
|
|
|
146
|
+
// src/FilterValuePresentation.tsx
|
|
147
|
+
import { createContext, useContext, useState } from "react";
|
|
148
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
149
|
+
var Context = createContext({});
|
|
150
|
+
var FilterValueProvider = Context.Provider;
|
|
151
|
+
function useFilterValuePresentation() {
|
|
152
|
+
return useContext(Context);
|
|
153
|
+
}
|
|
154
|
+
function PresentedFilterValue({ field, value }) {
|
|
155
|
+
const presentation = useContext(Context);
|
|
156
|
+
return /* @__PURE__ */ jsx(Fragment, { children: presentation.render?.(field, value) ?? presentation.label?.(field, value) ?? value });
|
|
157
|
+
}
|
|
158
|
+
function PresentedValueInput({ field, value, options, onChange }) {
|
|
159
|
+
const presentation = useContext(Context);
|
|
160
|
+
const [open, setOpen] = useState(false), [search, setSearch] = useState("");
|
|
161
|
+
const choices = options.map((option) => typeof option === "string" ? { value: option, label: presentation.label?.(field, option) ?? option } : option);
|
|
162
|
+
const visible = choices.filter((option) => `${option.value} ${option.label}`.toLowerCase().includes(search.toLowerCase()));
|
|
163
|
+
return /* @__PURE__ */ jsxs("span", { style: { position: "relative" }, children: [
|
|
164
|
+
/* @__PURE__ */ jsxs("button", { type: "button", "aria-label": `Edit ${field} filter value`, "aria-expanded": open, onClick: () => setOpen(!open), children: [
|
|
165
|
+
/* @__PURE__ */ jsx(PresentedFilterValue, { field, value }),
|
|
166
|
+
!value && "Choose value"
|
|
167
|
+
] }),
|
|
168
|
+
open && /* @__PURE__ */ jsxs("span", { style: { position: "absolute", top: "100%", left: 0, zIndex: 30, background: "var(--qt-bg, #fff)", padding: 8, minWidth: 220, maxHeight: 300, overflowY: "auto" }, onKeyDown: (e) => {
|
|
169
|
+
if (e.key === "Escape") setOpen(false);
|
|
170
|
+
}, children: [
|
|
171
|
+
/* @__PURE__ */ jsx("input", { autoFocus: true, "aria-label": `Search ${field} values`, value: search, onChange: (e) => setSearch(e.target.value), onKeyDown: (e) => {
|
|
172
|
+
if (e.key === "Enter" && search) {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
onChange(search);
|
|
175
|
+
setOpen(false);
|
|
176
|
+
}
|
|
177
|
+
} }),
|
|
178
|
+
visible.map((option) => /* @__PURE__ */ jsxs("button", { type: "button", style: { display: "block" }, onClick: () => {
|
|
179
|
+
onChange(option.value);
|
|
180
|
+
setOpen(false);
|
|
181
|
+
}, children: [
|
|
182
|
+
/* @__PURE__ */ jsx(PresentedFilterValue, { field, value: option.value }),
|
|
183
|
+
option.label !== option.value && /* @__PURE__ */ jsxs("small", { children: [
|
|
184
|
+
" ",
|
|
185
|
+
option.value
|
|
186
|
+
] })
|
|
187
|
+
] }, option.value)),
|
|
188
|
+
search && !choices.some((option) => option.value === search) && /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => {
|
|
189
|
+
onChange(search);
|
|
190
|
+
setOpen(false);
|
|
191
|
+
}, children: [
|
|
192
|
+
"Use \u201C",
|
|
193
|
+
search,
|
|
194
|
+
"\u201D"
|
|
195
|
+
] })
|
|
196
|
+
] })
|
|
197
|
+
] });
|
|
198
|
+
}
|
|
199
|
+
|
|
145
200
|
// src/CellMenu.tsx
|
|
146
201
|
import { useEffect } from "react";
|
|
147
|
-
import { NULLARY_OPS, opsForField } from "@pythia-software/query-table-core";
|
|
148
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
202
|
+
import { NULLARY_OPS, negateClause, opsForField, opPairsForField, isFilterable } from "@pythia-software/query-table-core";
|
|
203
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
149
204
|
var cx = (...parts) => parts.filter(Boolean).join(" ");
|
|
150
|
-
var MENU_WIDTH =
|
|
205
|
+
var MENU_WIDTH = 320;
|
|
151
206
|
var ROW_HEIGHT = 32;
|
|
152
207
|
var POINTER_OFFSET = 4;
|
|
153
208
|
function CellMenu({ field, value, x, y, onAddFilter, onClose, classNames }) {
|
|
@@ -166,73 +221,36 @@ function CellMenu({ field, value, x, y, onAddFilter, onClose, classNames }) {
|
|
|
166
221
|
clearTimeout(t);
|
|
167
222
|
};
|
|
168
223
|
}, [onClose]);
|
|
169
|
-
const ops = opsForField(field);
|
|
224
|
+
const ops = isFilterable(field) ? opsForField(field) : [];
|
|
225
|
+
const pairs = isFilterable(field) ? opPairsForField(field) : [];
|
|
226
|
+
const hasNullity = pairs.some((p) => p.keep.op === "is_null");
|
|
170
227
|
const isArray = Array.isArray(value);
|
|
171
228
|
const isNullish = value == null || value === "" || isArray && value.length === 0;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
onAddFilter(
|
|
229
|
+
const cellText = valueToString(value);
|
|
230
|
+
function applyClause(clause) {
|
|
231
|
+
onAddFilter(clause);
|
|
175
232
|
onClose();
|
|
176
233
|
}
|
|
177
|
-
const
|
|
234
|
+
const rows = [];
|
|
235
|
+
const pairRow = (op, raw) => {
|
|
236
|
+
const positive = { field: field.name, op, value: NULLARY_OPS.has(op) ? "" : raw };
|
|
237
|
+
rows.push({ positive, negative: negateClause(positive, ops) });
|
|
238
|
+
};
|
|
178
239
|
if (isArray && !isNullish) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
" ",
|
|
187
|
-
/* @__PURE__ */ jsx("code", { children: s })
|
|
188
|
-
] }, `el-${i}`)
|
|
189
|
-
);
|
|
190
|
-
});
|
|
240
|
+
const arrayOp = pairs.find((p) => p.keep.op === "includes")?.keep.op ?? pairs[0]?.keep.op ?? "=";
|
|
241
|
+
value.forEach((el) => pairRow(arrayOp, String(el)));
|
|
242
|
+
if (hasNullity) pairRow("is_null", cellText);
|
|
243
|
+
} else if (!isNullish) {
|
|
244
|
+
for (const pair of pairs) pairRow(pair.keep.op, cellText);
|
|
245
|
+
} else if (hasNullity) {
|
|
246
|
+
pairRow("is_null", cellText);
|
|
191
247
|
}
|
|
192
|
-
for (const op of ops) {
|
|
193
|
-
const nullary = NULLARY_OPS.has(op);
|
|
194
|
-
if (isNullish && !nullary) continue;
|
|
195
|
-
if (isArray && !nullary && !isNullish) continue;
|
|
196
|
-
filterItems.push(
|
|
197
|
-
/* @__PURE__ */ jsxs(MenuItem, { classNames, onClick: () => apply(op), children: [
|
|
198
|
-
"filter: ",
|
|
199
|
-
opSymbol(op),
|
|
200
|
-
!nullary && !isNullish ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
201
|
-
" ",
|
|
202
|
-
/* @__PURE__ */ jsx("code", { children: previewValue(value) })
|
|
203
|
-
] }) : null
|
|
204
|
-
] }, op)
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
const copyItem = /* @__PURE__ */ jsx(
|
|
208
|
-
MenuItem,
|
|
209
|
-
{
|
|
210
|
-
classNames,
|
|
211
|
-
onClick: async () => {
|
|
212
|
-
try {
|
|
213
|
-
await navigator.clipboard.writeText(valueToString(value));
|
|
214
|
-
} catch {
|
|
215
|
-
}
|
|
216
|
-
onClose();
|
|
217
|
-
},
|
|
218
|
-
children: "copy value"
|
|
219
|
-
},
|
|
220
|
-
"copy"
|
|
221
|
-
);
|
|
222
|
-
const items = [
|
|
223
|
-
/* @__PURE__ */ jsxs("div", { className: "qt-cm-header", children: [
|
|
224
|
-
/* @__PURE__ */ jsx("span", { className: "qt-cm-field", children: field.label }),
|
|
225
|
-
/* @__PURE__ */ jsx("span", { className: "qt-cm-value", children: previewValue(value) })
|
|
226
|
-
] }, "header"),
|
|
227
|
-
copyItem,
|
|
228
|
-
/* @__PURE__ */ jsx("div", { className: cx("qt-cm-sep", classNames?.separator) }, "sep"),
|
|
229
|
-
...filterItems
|
|
230
|
-
];
|
|
231
248
|
const vw = typeof window !== "undefined" ? window.innerWidth : 9999;
|
|
232
249
|
const vh = typeof window !== "undefined" ? window.innerHeight : 9999;
|
|
250
|
+
const approxRows = 3 + rows.length;
|
|
233
251
|
const left = Math.max(8, Math.min(x + POINTER_OFFSET, vw - MENU_WIDTH - 8));
|
|
234
|
-
const top = Math.max(8, Math.min(y + POINTER_OFFSET, vh - ROW_HEIGHT *
|
|
235
|
-
return /* @__PURE__ */
|
|
252
|
+
const top = Math.max(8, Math.min(y + POINTER_OFFSET, vh - ROW_HEIGHT * approxRows - 8));
|
|
253
|
+
return /* @__PURE__ */ jsxs2(
|
|
236
254
|
"div",
|
|
237
255
|
{
|
|
238
256
|
className: cx("qt-cell-menu", classNames?.popover),
|
|
@@ -240,7 +258,69 @@ function CellMenu({ field, value, x, y, onAddFilter, onClose, classNames }) {
|
|
|
240
258
|
onClick: (e) => e.stopPropagation(),
|
|
241
259
|
onContextMenu: (e) => e.preventDefault(),
|
|
242
260
|
role: "menu",
|
|
243
|
-
children:
|
|
261
|
+
children: [
|
|
262
|
+
/* @__PURE__ */ jsxs2("div", { className: "qt-cm-header", children: [
|
|
263
|
+
/* @__PURE__ */ jsx2("span", { className: "qt-cm-field", children: field.label }),
|
|
264
|
+
/* @__PURE__ */ jsx2("span", { className: "qt-cm-value", children: Array.isArray(value) ? value.map((v) => /* @__PURE__ */ jsx2(PresentedFilterValue, { field: field.name, value: String(v) }, String(v))) : /* @__PURE__ */ jsx2(PresentedFilterValue, { field: field.name, value: String(value ?? "") }) })
|
|
265
|
+
] }),
|
|
266
|
+
/* @__PURE__ */ jsx2(
|
|
267
|
+
MenuItem,
|
|
268
|
+
{
|
|
269
|
+
classNames,
|
|
270
|
+
onClick: async () => {
|
|
271
|
+
try {
|
|
272
|
+
await navigator.clipboard.writeText(cellText);
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
onClose();
|
|
276
|
+
},
|
|
277
|
+
children: "copy value"
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
/* @__PURE__ */ jsx2("div", { className: cx("qt-cm-sep", classNames?.separator) }),
|
|
281
|
+
rows.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "qt-cm-filter-grid", role: "group", "aria-label": "Filter by this cell", children: [
|
|
282
|
+
/* @__PURE__ */ jsx2("span", { className: "qt-cm-col-head", children: "keep" }),
|
|
283
|
+
/* @__PURE__ */ jsx2("span", { className: "qt-cm-col-head qt-cm-col-head--neg", children: "exclude" }),
|
|
284
|
+
rows.map((row, i) => /* @__PURE__ */ jsx2(FilterPairRow, { row, value, classNames, onApply: applyClause }, i))
|
|
285
|
+
] })
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
function FilterPairRow({
|
|
291
|
+
row,
|
|
292
|
+
value,
|
|
293
|
+
classNames,
|
|
294
|
+
onApply
|
|
295
|
+
}) {
|
|
296
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
297
|
+
/* @__PURE__ */ jsx2(FilterButton, { clause: row.positive, value, classNames, onApply }),
|
|
298
|
+
/* @__PURE__ */ jsx2(FilterButton, { clause: row.negative, value, negative: true, classNames, onApply })
|
|
299
|
+
] });
|
|
300
|
+
}
|
|
301
|
+
function FilterButton({
|
|
302
|
+
clause,
|
|
303
|
+
value,
|
|
304
|
+
negative,
|
|
305
|
+
classNames,
|
|
306
|
+
onApply
|
|
307
|
+
}) {
|
|
308
|
+
if (!clause) return /* @__PURE__ */ jsx2("span", { className: "qt-cm-filter-empty", "aria-hidden": true });
|
|
309
|
+
const nullary = NULLARY_OPS.has(clause.op);
|
|
310
|
+
return /* @__PURE__ */ jsxs2(
|
|
311
|
+
"button",
|
|
312
|
+
{
|
|
313
|
+
type: "button",
|
|
314
|
+
className: cx("qt-cm-item", "qt-cm-filter", negative && "qt-cm-filter--neg", classNames?.item),
|
|
315
|
+
onClick: () => onApply(clause),
|
|
316
|
+
role: "menuitem",
|
|
317
|
+
children: [
|
|
318
|
+
/* @__PURE__ */ jsx2("span", { className: "qt-cm-op", children: predicateSymbol(clause) }),
|
|
319
|
+
!nullary ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
320
|
+
" ",
|
|
321
|
+
/* @__PURE__ */ jsx2("code", { children: /* @__PURE__ */ jsx2(PresentedFilterValue, { field: clause.field, value: clause.value }) })
|
|
322
|
+
] }) : null
|
|
323
|
+
]
|
|
244
324
|
}
|
|
245
325
|
);
|
|
246
326
|
}
|
|
@@ -249,7 +329,11 @@ function MenuItem({
|
|
|
249
329
|
onClick,
|
|
250
330
|
classNames
|
|
251
331
|
}) {
|
|
252
|
-
return /* @__PURE__ */
|
|
332
|
+
return /* @__PURE__ */ jsx2("button", { type: "button", className: cx("qt-cm-item", classNames?.item), onClick, role: "menuitem", children });
|
|
333
|
+
}
|
|
334
|
+
function predicateSymbol(clause) {
|
|
335
|
+
const base = opSymbol(clause.op);
|
|
336
|
+
return clause.negated ? `not ${base}` : base;
|
|
253
337
|
}
|
|
254
338
|
function opSymbol(op) {
|
|
255
339
|
switch (op) {
|
|
@@ -259,6 +343,14 @@ function opSymbol(op) {
|
|
|
259
343
|
return "\u2265";
|
|
260
344
|
case "<=":
|
|
261
345
|
return "\u2264";
|
|
346
|
+
case "starts_with":
|
|
347
|
+
return "starts with";
|
|
348
|
+
case "ends_with":
|
|
349
|
+
return "ends with";
|
|
350
|
+
case "matches_regex":
|
|
351
|
+
return "matches";
|
|
352
|
+
case "not_matches_regex":
|
|
353
|
+
return "not matches";
|
|
262
354
|
case "is_null":
|
|
263
355
|
return "is null";
|
|
264
356
|
case "is_not_null":
|
|
@@ -272,17 +364,9 @@ function valueToString(v) {
|
|
|
272
364
|
if (Array.isArray(v)) return v.join(", ");
|
|
273
365
|
return String(v);
|
|
274
366
|
}
|
|
275
|
-
function previewValue(v) {
|
|
276
|
-
if (v == null || v === "") return "(empty)";
|
|
277
|
-
if (Array.isArray(v)) {
|
|
278
|
-
return v.length === 0 ? "(empty)" : `[${v.length}] ${v.slice(0, 2).join(", ")}${v.length > 2 ? "\u2026" : ""}`;
|
|
279
|
-
}
|
|
280
|
-
const s = String(v);
|
|
281
|
-
return s.length > 50 ? s.slice(0, 50) + "\u2026" : s;
|
|
282
|
-
}
|
|
283
367
|
|
|
284
368
|
// src/DataTable.tsx
|
|
285
|
-
import { Fragment as
|
|
369
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
286
370
|
var cx2 = (...parts) => parts.filter((p) => Boolean(p)).join(" ");
|
|
287
371
|
var MIN_WIDTH = 1;
|
|
288
372
|
var MIN_SELECTION_WIDTH = 1;
|
|
@@ -316,12 +400,12 @@ function DataTable(props) {
|
|
|
316
400
|
overscan = DEFAULT_OVERSCAN,
|
|
317
401
|
classNames
|
|
318
402
|
} = props;
|
|
319
|
-
const [menu, setMenu] =
|
|
320
|
-
const [selectionColumnWidth, setSelectionColumnWidth] =
|
|
321
|
-
const [tableColumnOrder, setTableColumnOrder] =
|
|
322
|
-
const [copiedCellKey, setCopiedCellKey] =
|
|
323
|
-
const [headerHeight, setHeaderHeight] =
|
|
324
|
-
const [showHorizontalCue, setShowHorizontalCue] =
|
|
403
|
+
const [menu, setMenu] = useState3(null);
|
|
404
|
+
const [selectionColumnWidth, setSelectionColumnWidth] = useState3(DEFAULT_SELECTION_WIDTH);
|
|
405
|
+
const [tableColumnOrder, setTableColumnOrder] = useState3([]);
|
|
406
|
+
const [copiedCellKey, setCopiedCellKey] = useState3(null);
|
|
407
|
+
const [headerHeight, setHeaderHeight] = useState3(0);
|
|
408
|
+
const [showHorizontalCue, setShowHorizontalCue] = useState3(false);
|
|
325
409
|
const tableWrapRef = useRef(null);
|
|
326
410
|
const tableHeadRef = useRef(null);
|
|
327
411
|
const tableRef = useRef(null);
|
|
@@ -649,7 +733,7 @@ function DataTable(props) {
|
|
|
649
733
|
}
|
|
650
734
|
}
|
|
651
735
|
function resizeHandle(name, label, width) {
|
|
652
|
-
return /* @__PURE__ */
|
|
736
|
+
return /* @__PURE__ */ jsx3(
|
|
653
737
|
"span",
|
|
654
738
|
{
|
|
655
739
|
className: cx2("qt-resize-handle", classNames?.resizeHandle),
|
|
@@ -697,12 +781,12 @@ function DataTable(props) {
|
|
|
697
781
|
const lastVirtualRow = virtualRows[virtualRows.length - 1];
|
|
698
782
|
const paddingTop = firstVirtualRow ? Math.max(0, firstVirtualRow.start - headerHeight) : 0;
|
|
699
783
|
const paddingBottom = lastVirtualRow ? Math.max(0, rowVirtualizer.getTotalSize() - (lastVirtualRow.end - headerHeight)) : 0;
|
|
700
|
-
return /* @__PURE__ */
|
|
701
|
-
showHorizontalCue ? /* @__PURE__ */
|
|
784
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
785
|
+
showHorizontalCue ? /* @__PURE__ */ jsxs3("div", { className: "qt-table-scroll-cue", "aria-hidden": "true", children: [
|
|
702
786
|
"Swipe to see more ",
|
|
703
|
-
/* @__PURE__ */
|
|
787
|
+
/* @__PURE__ */ jsx3("span", { children: "\u2192" })
|
|
704
788
|
] }) : null,
|
|
705
|
-
/* @__PURE__ */
|
|
789
|
+
/* @__PURE__ */ jsxs3(
|
|
706
790
|
"div",
|
|
707
791
|
{
|
|
708
792
|
ref: tableWrapRef,
|
|
@@ -722,8 +806,8 @@ function DataTable(props) {
|
|
|
722
806
|
"aria-label": "Scrollable data table",
|
|
723
807
|
tabIndex: 0,
|
|
724
808
|
children: [
|
|
725
|
-
loading && /* @__PURE__ */
|
|
726
|
-
/* @__PURE__ */
|
|
809
|
+
loading && /* @__PURE__ */ jsx3("div", { className: cx2("qt-loading-bar", classNames?.loadingBar), role: "progressbar", "aria-label": "loading" }),
|
|
810
|
+
/* @__PURE__ */ jsx3(
|
|
727
811
|
"div",
|
|
728
812
|
{
|
|
729
813
|
ref: resizeGuideRef,
|
|
@@ -731,21 +815,21 @@ function DataTable(props) {
|
|
|
731
815
|
"aria-hidden": "true"
|
|
732
816
|
}
|
|
733
817
|
),
|
|
734
|
-
/* @__PURE__ */
|
|
735
|
-
/* @__PURE__ */
|
|
818
|
+
/* @__PURE__ */ jsxs3("table", { ref: tableRef, className: cx2("qt-table", classNames?.table), style: { minWidth: tableWidth }, children: [
|
|
819
|
+
/* @__PURE__ */ jsxs3("colgroup", { children: [
|
|
736
820
|
renderedColumnNames.map((name) => {
|
|
737
821
|
const f = fieldByName.get(name);
|
|
738
822
|
const w = displayColumnWidthFor(name, f);
|
|
739
|
-
if (name === SELECTION_COLUMN) return /* @__PURE__ */
|
|
823
|
+
if (name === SELECTION_COLUMN) return /* @__PURE__ */ jsx3("col", { "data-qt-column": name, style: { width: w } }, name);
|
|
740
824
|
if (!f) return null;
|
|
741
|
-
return /* @__PURE__ */
|
|
825
|
+
return /* @__PURE__ */ jsx3("col", { "data-qt-column": f.name, style: { width: w } }, f.name);
|
|
742
826
|
}),
|
|
743
|
-
trailing && /* @__PURE__ */
|
|
827
|
+
trailing && /* @__PURE__ */ jsx3("col", { style: { width: 40 } })
|
|
744
828
|
] }),
|
|
745
|
-
/* @__PURE__ */
|
|
829
|
+
/* @__PURE__ */ jsx3("thead", { ref: tableHeadRef, className: classNames?.thead, children: /* @__PURE__ */ jsxs3("tr", { className: classNames?.headerRow, children: [
|
|
746
830
|
renderedColumnNames.map((name) => {
|
|
747
831
|
if (name === SELECTION_COLUMN) {
|
|
748
|
-
return /* @__PURE__ */
|
|
832
|
+
return /* @__PURE__ */ jsxs3(
|
|
749
833
|
"th",
|
|
750
834
|
{
|
|
751
835
|
"data-qt-field": name,
|
|
@@ -789,7 +873,7 @@ function DataTable(props) {
|
|
|
789
873
|
},
|
|
790
874
|
onDragEnd: () => columnDrag.end(),
|
|
791
875
|
children: [
|
|
792
|
-
/* @__PURE__ */
|
|
876
|
+
/* @__PURE__ */ jsx3(
|
|
793
877
|
"input",
|
|
794
878
|
{
|
|
795
879
|
type: "checkbox",
|
|
@@ -811,7 +895,7 @@ function DataTable(props) {
|
|
|
811
895
|
if (!f) return null;
|
|
812
896
|
const info = sortInfo(f);
|
|
813
897
|
const sortable = isSortable(f);
|
|
814
|
-
return /* @__PURE__ */
|
|
898
|
+
return /* @__PURE__ */ jsxs3(
|
|
815
899
|
"th",
|
|
816
900
|
{
|
|
817
901
|
"data-qt-field": f.name,
|
|
@@ -855,7 +939,7 @@ function DataTable(props) {
|
|
|
855
939
|
},
|
|
856
940
|
onDragEnd: () => columnDrag.end(),
|
|
857
941
|
children: [
|
|
858
|
-
/* @__PURE__ */
|
|
942
|
+
/* @__PURE__ */ jsxs3(
|
|
859
943
|
"span",
|
|
860
944
|
{
|
|
861
945
|
className: "qt-th-label",
|
|
@@ -864,9 +948,9 @@ function DataTable(props) {
|
|
|
864
948
|
title: "Click to open column actions",
|
|
865
949
|
children: [
|
|
866
950
|
f.label,
|
|
867
|
-
info && /* @__PURE__ */
|
|
951
|
+
info && /* @__PURE__ */ jsxs3("span", { className: "qt-sort-indicator", children: [
|
|
868
952
|
info.dir === "asc" ? " \u2191" : " \u2193",
|
|
869
|
-
info.priority != null && /* @__PURE__ */
|
|
953
|
+
info.priority != null && /* @__PURE__ */ jsx3("sup", { className: "qt-sort-priority", children: info.priority })
|
|
870
954
|
] })
|
|
871
955
|
]
|
|
872
956
|
}
|
|
@@ -877,17 +961,17 @@ function DataTable(props) {
|
|
|
877
961
|
f.name
|
|
878
962
|
);
|
|
879
963
|
}),
|
|
880
|
-
trailing && /* @__PURE__ */
|
|
964
|
+
trailing && /* @__PURE__ */ jsx3("th", { className: cx2("qt-th", classNames?.th), children: trailingLabel ?? "" })
|
|
881
965
|
] }) }),
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
rows.length === 0 && /* @__PURE__ */
|
|
884
|
-
paddingTop > 0 && /* @__PURE__ */
|
|
966
|
+
/* @__PURE__ */ jsxs3("tbody", { className: classNames?.tbody, children: [
|
|
967
|
+
rows.length === 0 && /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: totalCols, className: cx2("qt-cell", "qt-empty", classNames?.empty), children: emptyMessage ?? "No rows." }) }),
|
|
968
|
+
paddingTop > 0 && /* @__PURE__ */ jsx3("tr", { className: "qt-virtual-spacer", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("td", { colSpan: totalCols, style: { height: paddingTop, padding: 0, border: 0 } }) }),
|
|
885
969
|
virtualRows.map((virtualRow) => {
|
|
886
970
|
const i = virtualRow.index;
|
|
887
971
|
const row = rows[i];
|
|
888
972
|
const id = rowIds[i] ?? null;
|
|
889
973
|
const selected = id != null && selection ? selection.isSelected(id) : false;
|
|
890
|
-
return /* @__PURE__ */
|
|
974
|
+
return /* @__PURE__ */ jsxs3(
|
|
891
975
|
"tr",
|
|
892
976
|
{
|
|
893
977
|
"data-index": i,
|
|
@@ -896,7 +980,7 @@ function DataTable(props) {
|
|
|
896
980
|
children: [
|
|
897
981
|
renderedColumnNames.map((name) => {
|
|
898
982
|
if (name === SELECTION_COLUMN) {
|
|
899
|
-
return /* @__PURE__ */
|
|
983
|
+
return /* @__PURE__ */ jsx3(
|
|
900
984
|
"td",
|
|
901
985
|
{
|
|
902
986
|
"data-qt-field": name,
|
|
@@ -907,7 +991,7 @@ function DataTable(props) {
|
|
|
907
991
|
classNames?.cell,
|
|
908
992
|
classNames?.checkboxCell
|
|
909
993
|
),
|
|
910
|
-
children: id != null && /* @__PURE__ */
|
|
994
|
+
children: id != null && /* @__PURE__ */ jsx3(
|
|
911
995
|
"input",
|
|
912
996
|
{
|
|
913
997
|
type: "checkbox",
|
|
@@ -927,7 +1011,7 @@ function DataTable(props) {
|
|
|
927
1011
|
const render = resolveRenderer(f, renderers);
|
|
928
1012
|
const value = readFieldValue(f, row);
|
|
929
1013
|
const key = cellKey(id, i, f.name);
|
|
930
|
-
return /* @__PURE__ */
|
|
1014
|
+
return /* @__PURE__ */ jsx3(
|
|
931
1015
|
"td",
|
|
932
1016
|
{
|
|
933
1017
|
"data-qt-field": f.name,
|
|
@@ -936,21 +1020,21 @@ function DataTable(props) {
|
|
|
936
1020
|
onClick: (e) => openMenu(e, f, row),
|
|
937
1021
|
onContextMenu: (e) => openMenu(e, f, row),
|
|
938
1022
|
onDoubleClick: (e) => copyCellToClipboard(e, f, id, i, value),
|
|
939
|
-
children: copiedCellKey === key ? /* @__PURE__ */
|
|
1023
|
+
children: copiedCellKey === key ? /* @__PURE__ */ jsx3("span", { className: "qt-cell-copy-chip", children: "\u2713 copied" }) : isComputedCellError(value) ? /* @__PURE__ */ jsx3("span", { className: "qt-formula-error", title: value.computedError, children: value.computedError === "Calculating\u2026" ? "Calculating\u2026" : "Error" }) : render({ value, row, field: f, query })
|
|
940
1024
|
},
|
|
941
1025
|
f.name
|
|
942
1026
|
);
|
|
943
1027
|
}),
|
|
944
|
-
trailing && /* @__PURE__ */
|
|
1028
|
+
trailing && /* @__PURE__ */ jsx3("td", { className: cx2("qt-cell", "qt-trailing-cell", classNames?.cell), children: trailing(row) })
|
|
945
1029
|
]
|
|
946
1030
|
},
|
|
947
1031
|
virtualRow.key
|
|
948
1032
|
);
|
|
949
1033
|
}),
|
|
950
|
-
paddingBottom > 0 && /* @__PURE__ */
|
|
1034
|
+
paddingBottom > 0 && /* @__PURE__ */ jsx3("tr", { className: "qt-virtual-spacer", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("td", { colSpan: totalCols, style: { height: paddingBottom, padding: 0, border: 0 } }) })
|
|
951
1035
|
] }),
|
|
952
|
-
/* @__PURE__ */
|
|
953
|
-
/* @__PURE__ */
|
|
1036
|
+
/* @__PURE__ */ jsx3("tfoot", { children: /* @__PURE__ */ jsx3("tr", { className: cx2("qt-table-summary-row", classNames?.summaryRow), children: /* @__PURE__ */ jsx3("td", { colSpan: totalCols, className: cx2("qt-cell", "qt-table-summary-cell", classNames?.summaryCell), children: /* @__PURE__ */ jsxs3("div", { className: cx2("qt-table-summary", classNames?.summaryControls), children: [
|
|
1037
|
+
/* @__PURE__ */ jsx3(
|
|
954
1038
|
"button",
|
|
955
1039
|
{
|
|
956
1040
|
type: "button",
|
|
@@ -960,8 +1044,8 @@ function DataTable(props) {
|
|
|
960
1044
|
children: "\u2190 prev"
|
|
961
1045
|
}
|
|
962
1046
|
),
|
|
963
|
-
/* @__PURE__ */
|
|
964
|
-
/* @__PURE__ */
|
|
1047
|
+
/* @__PURE__ */ jsx3("span", { className: cx2("qt-table-summary-hint", classNames?.summaryHint), children: summaryText }),
|
|
1048
|
+
/* @__PURE__ */ jsx3(
|
|
965
1049
|
"button",
|
|
966
1050
|
{
|
|
967
1051
|
type: "button",
|
|
@@ -976,7 +1060,7 @@ function DataTable(props) {
|
|
|
976
1060
|
]
|
|
977
1061
|
}
|
|
978
1062
|
),
|
|
979
|
-
menu && (menu.kind === "cell" ? /* @__PURE__ */
|
|
1063
|
+
menu && (menu.kind === "cell" ? /* @__PURE__ */ jsx3(
|
|
980
1064
|
CellMenu,
|
|
981
1065
|
{
|
|
982
1066
|
field: menu.field,
|
|
@@ -986,7 +1070,7 @@ function DataTable(props) {
|
|
|
986
1070
|
onAddFilter: (clause) => onQueryChange((prev) => ({ ...prev, offset: 0, where: [...prev.where, clause] })),
|
|
987
1071
|
onClose: () => setMenu(null)
|
|
988
1072
|
}
|
|
989
|
-
) : /* @__PURE__ */
|
|
1073
|
+
) : /* @__PURE__ */ jsx3(
|
|
990
1074
|
HeaderMenu,
|
|
991
1075
|
{
|
|
992
1076
|
field: menu.field,
|
|
@@ -1028,7 +1112,7 @@ function HeaderMenu({
|
|
|
1028
1112
|
}, [onClose]);
|
|
1029
1113
|
const items = [];
|
|
1030
1114
|
const addSortItem = (placement, dir, label) => items.push(
|
|
1031
|
-
/* @__PURE__ */
|
|
1115
|
+
/* @__PURE__ */ jsx3(MenuItem2, { disabled: !sortable, onClick: () => onSetSort(placement, dir), children: label }, `${placement}-${dir}`)
|
|
1032
1116
|
);
|
|
1033
1117
|
addSortItem("set", "asc", "Set Sort (Asc)");
|
|
1034
1118
|
addSortItem("set", "desc", "Set Sort (Desc)");
|
|
@@ -1036,15 +1120,15 @@ function HeaderMenu({
|
|
|
1036
1120
|
addSortItem("append", "desc", "Append Sort (Desc)");
|
|
1037
1121
|
addSortItem("prepend", "asc", "Prepend Sort (Asc)");
|
|
1038
1122
|
addSortItem("prepend", "desc", "Prepend Sort (Desc)");
|
|
1039
|
-
items.push(/* @__PURE__ */
|
|
1123
|
+
items.push(/* @__PURE__ */ jsx3("div", { className: "qt-cm-sep" }, "sep"));
|
|
1040
1124
|
items.push(
|
|
1041
|
-
/* @__PURE__ */
|
|
1125
|
+
/* @__PURE__ */ jsx3(MenuItem2, { disabled: !canRemoveColumn, onClick: onRemove, children: "Remove Column" }, "remove")
|
|
1042
1126
|
);
|
|
1043
1127
|
const vw = typeof window !== "undefined" ? window.innerWidth : 9999;
|
|
1044
1128
|
const vh = typeof window !== "undefined" ? window.innerHeight : 9999;
|
|
1045
1129
|
const left = Math.max(8, Math.min(x, vw - MENU_WIDTH2 - 8));
|
|
1046
1130
|
const top = Math.max(8, Math.min(y, vh - MENU_ROW_HEIGHT * items.length - 8));
|
|
1047
|
-
return /* @__PURE__ */
|
|
1131
|
+
return /* @__PURE__ */ jsxs3(
|
|
1048
1132
|
"div",
|
|
1049
1133
|
{
|
|
1050
1134
|
className: "qt-cell-menu",
|
|
@@ -1053,7 +1137,7 @@ function HeaderMenu({
|
|
|
1053
1137
|
onContextMenu: (e) => e.preventDefault(),
|
|
1054
1138
|
role: "menu",
|
|
1055
1139
|
children: [
|
|
1056
|
-
/* @__PURE__ */
|
|
1140
|
+
/* @__PURE__ */ jsx3("div", { className: "qt-cm-header", children: /* @__PURE__ */ jsx3("span", { className: "qt-cm-field", children: field.label }) }),
|
|
1057
1141
|
items
|
|
1058
1142
|
]
|
|
1059
1143
|
}
|
|
@@ -1064,7 +1148,7 @@ function MenuItem2({
|
|
|
1064
1148
|
onClick,
|
|
1065
1149
|
disabled
|
|
1066
1150
|
}) {
|
|
1067
|
-
return /* @__PURE__ */
|
|
1151
|
+
return /* @__PURE__ */ jsx3(
|
|
1068
1152
|
"button",
|
|
1069
1153
|
{
|
|
1070
1154
|
type: "button",
|
|
@@ -1114,8 +1198,814 @@ function valueToString2(value) {
|
|
|
1114
1198
|
return String(value);
|
|
1115
1199
|
}
|
|
1116
1200
|
|
|
1201
|
+
// src/SelectColumnEditor.tsx
|
|
1202
|
+
import {
|
|
1203
|
+
lazy,
|
|
1204
|
+
Suspense,
|
|
1205
|
+
useEffect as useEffect3,
|
|
1206
|
+
useId,
|
|
1207
|
+
useMemo as useMemo2,
|
|
1208
|
+
useRef as useRef2,
|
|
1209
|
+
useState as useState4
|
|
1210
|
+
} from "react";
|
|
1211
|
+
import {
|
|
1212
|
+
computedFieldName,
|
|
1213
|
+
FORMULA_FUNCTIONS,
|
|
1214
|
+
isComputedField,
|
|
1215
|
+
isSelectable
|
|
1216
|
+
} from "@pythia-software/query-table-core";
|
|
1217
|
+
|
|
1218
|
+
// src/previewOrdering.ts
|
|
1219
|
+
var collator = new Intl.Collator(void 0, {
|
|
1220
|
+
numeric: true,
|
|
1221
|
+
sensitivity: "base"
|
|
1222
|
+
});
|
|
1223
|
+
var valueLabel = (value) => value === null ? "NULL" : typeof value === "string" ? value : JSON.stringify(value);
|
|
1224
|
+
function mixedRank(group) {
|
|
1225
|
+
const value = JSON.stringify([
|
|
1226
|
+
group.inputs,
|
|
1227
|
+
group.result.value,
|
|
1228
|
+
group.result.error ?? null
|
|
1229
|
+
]);
|
|
1230
|
+
let hash = 2166136261;
|
|
1231
|
+
for (let i = 0; i < value.length; i++)
|
|
1232
|
+
hash = Math.imul(hash ^ value.charCodeAt(i), 16777619);
|
|
1233
|
+
return hash >>> 0;
|
|
1234
|
+
}
|
|
1235
|
+
function compareValue(a, b) {
|
|
1236
|
+
return collator.compare(valueLabel(a), valueLabel(b));
|
|
1237
|
+
}
|
|
1238
|
+
function sortValue(group, key) {
|
|
1239
|
+
switch (key.kind) {
|
|
1240
|
+
case "input":
|
|
1241
|
+
return group.inputs[key.index] ?? null;
|
|
1242
|
+
case "result":
|
|
1243
|
+
return group.result.error ?? group.result.value;
|
|
1244
|
+
case "count":
|
|
1245
|
+
case "percentage":
|
|
1246
|
+
return group.count;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
function orderPreviewGroups(groups, sort) {
|
|
1250
|
+
return groups.map((group, index) => ({ group, index })).sort((a, b) => {
|
|
1251
|
+
if (!sort) {
|
|
1252
|
+
const rank = mixedRank(a.group) - mixedRank(b.group);
|
|
1253
|
+
return rank || a.index - b.index;
|
|
1254
|
+
}
|
|
1255
|
+
const comparison = compareValue(
|
|
1256
|
+
sortValue(a.group, sort.key),
|
|
1257
|
+
sortValue(b.group, sort.key)
|
|
1258
|
+
);
|
|
1259
|
+
return (sort.direction === "asc" ? comparison : -comparison) || a.index - b.index;
|
|
1260
|
+
}).map(({ group }) => group);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/SelectColumnEditor.tsx
|
|
1264
|
+
import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1265
|
+
var FormulaEditor = lazy(() => import("./FormulaEditor-YCPICDIH.js"));
|
|
1266
|
+
var fieldExpression = (name) => `[${name.replace(/\]/g, "]]")}]`;
|
|
1267
|
+
var display = (v) => v === null ? "NULL" : typeof v === "string" ? v === "" ? '""' : v : JSON.stringify(v);
|
|
1268
|
+
function SelectColumnEditor({
|
|
1269
|
+
api,
|
|
1270
|
+
onClose
|
|
1271
|
+
}) {
|
|
1272
|
+
const titleId = useId(), dialog = useRef2(null);
|
|
1273
|
+
const closeRef = useRef2(onClose);
|
|
1274
|
+
closeRef.current = onClose;
|
|
1275
|
+
const [columns, setColumns] = useState4(
|
|
1276
|
+
() => api.select.visible.map((c) => ({ ...c }))
|
|
1277
|
+
);
|
|
1278
|
+
const [search, setSearch] = useState4(""), [active, setActive] = useState4(api.select.visible[0]?.field ?? "");
|
|
1279
|
+
const [editing, setEditing] = useState4(false), [id, setId] = useState4(""), [revision, setRevision] = useState4(null);
|
|
1280
|
+
const [label, setLabel] = useState4(""), [source, setSource] = useState4("");
|
|
1281
|
+
const [count, setCount] = useState4(1e3), [preview, setPreview] = useState4(null), [loading, setLoading] = useState4(false);
|
|
1282
|
+
const [error, setError] = useState4(null), [notice, setNotice] = useState4(""), [saving, setSaving] = useState4(false);
|
|
1283
|
+
const [filter, setFilter] = useState4("all"), [page, setPage] = useState4(0), [retry, setRetry] = useState4(0), [functionSearch, setFunctionSearch] = useState4("");
|
|
1284
|
+
const [previewSort, setPreviewSort] = useState4(null);
|
|
1285
|
+
const [fieldStats, setFieldStats] = useState4({});
|
|
1286
|
+
const drag = useRef2(null);
|
|
1287
|
+
const [inspection, setInspection] = useState4(null);
|
|
1288
|
+
const catalogue = api.computed.catalogue.filter(isSelectable);
|
|
1289
|
+
const chosen = catalogue.find((f) => f.name === active);
|
|
1290
|
+
const expression = editing ? source : active ? fieldExpression(active) : "";
|
|
1291
|
+
const validation = useMemo2(() => {
|
|
1292
|
+
if (!expression)
|
|
1293
|
+
return { error: "Choose a column or create a computed column." };
|
|
1294
|
+
try {
|
|
1295
|
+
return {
|
|
1296
|
+
plan: api.computed.compile(expression, editing ? id : void 0)
|
|
1297
|
+
};
|
|
1298
|
+
} catch (e) {
|
|
1299
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
1300
|
+
}
|
|
1301
|
+
}, [expression, editing, id, api.computed.compile]);
|
|
1302
|
+
const previewPlanKey = validation.error ? `error:${validation.error}` : `plan:${JSON.stringify(validation.plan)}`;
|
|
1303
|
+
const previewRequest = useRef2(api.computed.preview);
|
|
1304
|
+
previewRequest.current = api.computed.preview;
|
|
1305
|
+
const fieldStatsRequest = useRef2(api.fieldStats);
|
|
1306
|
+
fieldStatsRequest.current = api.fieldStats;
|
|
1307
|
+
const catalogueKey = JSON.stringify(catalogue.map((field) => field.name));
|
|
1308
|
+
useEffect3(() => {
|
|
1309
|
+
const previous = document.activeElement;
|
|
1310
|
+
dialog.current?.focus();
|
|
1311
|
+
const onKey = (e) => {
|
|
1312
|
+
if (e.key === "Escape") {
|
|
1313
|
+
if (document.querySelector(".cm-tooltip-autocomplete")) return;
|
|
1314
|
+
closeRef.current();
|
|
1315
|
+
}
|
|
1316
|
+
if (e.key === "Tab") {
|
|
1317
|
+
const focusable = Array.from(
|
|
1318
|
+
dialog.current?.querySelectorAll(
|
|
1319
|
+
'button:not([disabled]), input:not([disabled]), select, textarea, [tabindex="0"], [contenteditable="true"]'
|
|
1320
|
+
) ?? []
|
|
1321
|
+
).filter((el) => el.getClientRects().length);
|
|
1322
|
+
const first = focusable[0], last = focusable[focusable.length - 1];
|
|
1323
|
+
if (e.shiftKey && (document.activeElement === first || document.activeElement === dialog.current)) {
|
|
1324
|
+
e.preventDefault();
|
|
1325
|
+
last?.focus();
|
|
1326
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
1327
|
+
e.preventDefault();
|
|
1328
|
+
first?.focus();
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
document.addEventListener("keydown", onKey);
|
|
1333
|
+
const old = document.body.style.overflow;
|
|
1334
|
+
document.body.style.overflow = "hidden";
|
|
1335
|
+
return () => {
|
|
1336
|
+
document.removeEventListener("keydown", onKey);
|
|
1337
|
+
document.body.style.overflow = old;
|
|
1338
|
+
previous?.focus();
|
|
1339
|
+
};
|
|
1340
|
+
}, []);
|
|
1341
|
+
useEffect3(() => {
|
|
1342
|
+
const ac = new AbortController();
|
|
1343
|
+
void fieldStatsRequest.current(
|
|
1344
|
+
catalogue.map((field) => field.name),
|
|
1345
|
+
ac.signal
|
|
1346
|
+
).then((result) => {
|
|
1347
|
+
if (!ac.signal.aborted) setFieldStats(result);
|
|
1348
|
+
}).catch(() => {
|
|
1349
|
+
});
|
|
1350
|
+
return () => ac.abort();
|
|
1351
|
+
}, [catalogueKey]);
|
|
1352
|
+
useEffect3(() => {
|
|
1353
|
+
const ac = new AbortController();
|
|
1354
|
+
setPreview(null);
|
|
1355
|
+
setError(null);
|
|
1356
|
+
setPage(0);
|
|
1357
|
+
setInspection(null);
|
|
1358
|
+
if (validation.error || !Number.isInteger(count) || count < 1 || count > 1e4) {
|
|
1359
|
+
setLoading(false);
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
setLoading(true);
|
|
1363
|
+
const timer = setTimeout(() => {
|
|
1364
|
+
const request = previewRequest.current;
|
|
1365
|
+
void request(
|
|
1366
|
+
expression,
|
|
1367
|
+
count,
|
|
1368
|
+
ac.signal,
|
|
1369
|
+
editing ? id : void 0,
|
|
1370
|
+
editing
|
|
1371
|
+
).then((result) => {
|
|
1372
|
+
if (!ac.signal.aborted) setPreview(result);
|
|
1373
|
+
}).catch((e) => {
|
|
1374
|
+
if (!ac.signal.aborted)
|
|
1375
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1376
|
+
}).finally(() => {
|
|
1377
|
+
if (!ac.signal.aborted) setLoading(false);
|
|
1378
|
+
});
|
|
1379
|
+
}, 400);
|
|
1380
|
+
return () => {
|
|
1381
|
+
ac.abort();
|
|
1382
|
+
clearTimeout(timer);
|
|
1383
|
+
};
|
|
1384
|
+
}, [expression, count, editing, id, previewPlanKey, retry]);
|
|
1385
|
+
const move = (field, index) => setColumns((cols) => {
|
|
1386
|
+
const next = cols.filter((c) => c.field !== field), item = cols.find((c) => c.field === field);
|
|
1387
|
+
if (item) next.splice(Math.max(0, Math.min(index, next.length)), 0, item);
|
|
1388
|
+
return next;
|
|
1389
|
+
});
|
|
1390
|
+
const toggle = (field) => setColumns(
|
|
1391
|
+
(cols) => cols.some((c) => c.field === field) ? cols.filter((c) => c.field !== field) : [...cols, { field }]
|
|
1392
|
+
);
|
|
1393
|
+
function browse(field) {
|
|
1394
|
+
setActive(field);
|
|
1395
|
+
setEditing(false);
|
|
1396
|
+
setNotice("");
|
|
1397
|
+
}
|
|
1398
|
+
function edit(defId) {
|
|
1399
|
+
const def = api.computed.definitions.find((d) => d.id === defId);
|
|
1400
|
+
setId(def?.id ?? defId ?? crypto.randomUUID());
|
|
1401
|
+
setRevision(def?.revision ?? null);
|
|
1402
|
+
setLabel(def?.label ?? defId ?? "");
|
|
1403
|
+
setSource(
|
|
1404
|
+
def?.expression.source ?? (defId ? "" : active ? fieldExpression(active) : "LEFT([field], 3)")
|
|
1405
|
+
);
|
|
1406
|
+
setEditing(true);
|
|
1407
|
+
setNotice("");
|
|
1408
|
+
}
|
|
1409
|
+
async function save() {
|
|
1410
|
+
setSaving(true);
|
|
1411
|
+
setError(null);
|
|
1412
|
+
try {
|
|
1413
|
+
const def = await api.computed.save(
|
|
1414
|
+
{
|
|
1415
|
+
id,
|
|
1416
|
+
label: label.trim(),
|
|
1417
|
+
expression: { language: "qt-expr", version: 1, source }
|
|
1418
|
+
},
|
|
1419
|
+
revision
|
|
1420
|
+
);
|
|
1421
|
+
setRevision(def.revision);
|
|
1422
|
+
const field = computedFieldName(def.id);
|
|
1423
|
+
setActive(field);
|
|
1424
|
+
setColumns(
|
|
1425
|
+
(cols) => cols.some((c) => c.field === field) ? cols : [...cols, { field }]
|
|
1426
|
+
);
|
|
1427
|
+
setNotice(
|
|
1428
|
+
"Definition saved. All queries referencing this ID now use this definition. Apply columns to update this query\u2019s layout."
|
|
1429
|
+
);
|
|
1430
|
+
} catch (e) {
|
|
1431
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1432
|
+
} finally {
|
|
1433
|
+
setSaving(false);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
const groups = useMemo2(
|
|
1437
|
+
() => orderPreviewGroups(
|
|
1438
|
+
(preview?.groups ?? []).filter(
|
|
1439
|
+
(g) => filter === "errors" ? !!g.result.error : filter === "nulls" ? !g.result.error && g.result.value === null : true
|
|
1440
|
+
),
|
|
1441
|
+
previewSort
|
|
1442
|
+
),
|
|
1443
|
+
[preview, filter, previewSort]
|
|
1444
|
+
);
|
|
1445
|
+
const catalogueResults = useMemo2(
|
|
1446
|
+
() => catalogue.filter(
|
|
1447
|
+
(f) => `${f.label} ${f.name} ${f.type} ${f.group ?? ""}`.toLowerCase().includes(search.toLowerCase())
|
|
1448
|
+
).map((field, index) => ({ field, index })).sort((a, b) => {
|
|
1449
|
+
const aLow = (fieldStats[a.field.name]?.distinct ?? 2) <= 1;
|
|
1450
|
+
const bLow = (fieldStats[b.field.name]?.distinct ?? 2) <= 1;
|
|
1451
|
+
return Number(aLow) - Number(bLow) || a.index - b.index;
|
|
1452
|
+
}).map(({ field }) => field),
|
|
1453
|
+
[catalogue, search, fieldStats]
|
|
1454
|
+
);
|
|
1455
|
+
const shown = groups.slice(page * 50, (page + 1) * 50);
|
|
1456
|
+
const samePreviewSortKey = (a, b) => {
|
|
1457
|
+
if (a.kind !== b.kind) return false;
|
|
1458
|
+
if (a.kind === "input" && b.kind === "input")
|
|
1459
|
+
return a.index === b.index;
|
|
1460
|
+
return true;
|
|
1461
|
+
};
|
|
1462
|
+
const selectPreviewSort = (key, direction) => {
|
|
1463
|
+
setPreviewSort((current) => {
|
|
1464
|
+
const sameKey = !!current && samePreviewSortKey(current.key, key);
|
|
1465
|
+
return sameKey ? { key, direction: current.direction === "asc" ? "desc" : "asc" } : { key, direction };
|
|
1466
|
+
});
|
|
1467
|
+
setPage(0);
|
|
1468
|
+
};
|
|
1469
|
+
const previewSortDirection = (key) => previewSort && samePreviewSortKey(previewSort.key, key) ? previewSort.direction : void 0;
|
|
1470
|
+
const previewAriaSort = (key) => {
|
|
1471
|
+
const direction = previewSortDirection(key);
|
|
1472
|
+
return direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none";
|
|
1473
|
+
};
|
|
1474
|
+
const previewSortHeader = (label2, key, direction = "asc") => {
|
|
1475
|
+
const currentDirection = previewSortDirection(key);
|
|
1476
|
+
return /* @__PURE__ */ jsxs4(
|
|
1477
|
+
"button",
|
|
1478
|
+
{
|
|
1479
|
+
type: "button",
|
|
1480
|
+
className: "qt-preview-sort",
|
|
1481
|
+
onClick: () => selectPreviewSort(key, direction),
|
|
1482
|
+
title: `Sort by ${label2}`,
|
|
1483
|
+
children: [
|
|
1484
|
+
label2,
|
|
1485
|
+
currentDirection && (currentDirection === "asc" ? " \u2191" : " \u2193")
|
|
1486
|
+
]
|
|
1487
|
+
}
|
|
1488
|
+
);
|
|
1489
|
+
};
|
|
1490
|
+
return /* @__PURE__ */ jsx4("div", { className: "qt-modal-backdrop qt-select-editor-backdrop", children: /* @__PURE__ */ jsxs4(
|
|
1491
|
+
"div",
|
|
1492
|
+
{
|
|
1493
|
+
className: "qt-select-editor",
|
|
1494
|
+
role: "dialog",
|
|
1495
|
+
"aria-modal": "true",
|
|
1496
|
+
"aria-labelledby": titleId,
|
|
1497
|
+
ref: dialog,
|
|
1498
|
+
tabIndex: -1,
|
|
1499
|
+
children: [
|
|
1500
|
+
/* @__PURE__ */ jsxs4("header", { className: "qt-select-editor-header", children: [
|
|
1501
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1502
|
+
/* @__PURE__ */ jsx4("h2", { id: titleId, children: "Select columns" }),
|
|
1503
|
+
/* @__PURE__ */ jsx4("p", { children: "Arrange your view, explore values, and create reusable computed columns." })
|
|
1504
|
+
] }),
|
|
1505
|
+
/* @__PURE__ */ jsx4(
|
|
1506
|
+
"button",
|
|
1507
|
+
{
|
|
1508
|
+
type: "button",
|
|
1509
|
+
className: "qt-btn",
|
|
1510
|
+
onClick: onClose,
|
|
1511
|
+
"aria-label": "Close column editor",
|
|
1512
|
+
children: "\u2715"
|
|
1513
|
+
}
|
|
1514
|
+
)
|
|
1515
|
+
] }),
|
|
1516
|
+
/* @__PURE__ */ jsxs4("div", { className: "qt-select-editor-body", children: [
|
|
1517
|
+
/* @__PURE__ */ jsxs4("aside", { className: "qt-selected-panel", children: [
|
|
1518
|
+
/* @__PURE__ */ jsxs4("h3", { children: [
|
|
1519
|
+
"Selected ",
|
|
1520
|
+
/* @__PURE__ */ jsx4("span", { className: "qt-muted", children: columns.length })
|
|
1521
|
+
] }),
|
|
1522
|
+
/* @__PURE__ */ jsx4("p", { className: "qt-muted", children: "Drag to reorder, or use the arrow buttons." }),
|
|
1523
|
+
/* @__PURE__ */ jsx4("ol", { className: "qt-editor-columns", children: columns.map((c, i) => {
|
|
1524
|
+
const f = catalogue.find((f2) => f2.name === c.field);
|
|
1525
|
+
return /* @__PURE__ */ jsxs4(
|
|
1526
|
+
"li",
|
|
1527
|
+
{
|
|
1528
|
+
draggable: true,
|
|
1529
|
+
onDragStart: (e) => {
|
|
1530
|
+
drag.current = c.field;
|
|
1531
|
+
e.dataTransfer.setData("text/plain", c.field);
|
|
1532
|
+
},
|
|
1533
|
+
onDragOver: (e) => e.preventDefault(),
|
|
1534
|
+
onDrop: (e) => {
|
|
1535
|
+
e.preventDefault();
|
|
1536
|
+
if (drag.current) move(drag.current, i);
|
|
1537
|
+
drag.current = null;
|
|
1538
|
+
},
|
|
1539
|
+
onDragEnd: () => {
|
|
1540
|
+
drag.current = null;
|
|
1541
|
+
},
|
|
1542
|
+
children: [
|
|
1543
|
+
/* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u283F" }),
|
|
1544
|
+
/* @__PURE__ */ jsx4(
|
|
1545
|
+
"button",
|
|
1546
|
+
{
|
|
1547
|
+
type: "button",
|
|
1548
|
+
className: "qt-column-name",
|
|
1549
|
+
onClick: () => browse(c.field),
|
|
1550
|
+
children: f?.label ?? c.field
|
|
1551
|
+
}
|
|
1552
|
+
),
|
|
1553
|
+
/* @__PURE__ */ jsx4(
|
|
1554
|
+
"button",
|
|
1555
|
+
{
|
|
1556
|
+
type: "button",
|
|
1557
|
+
className: "qt-icon-btn",
|
|
1558
|
+
disabled: i === 0,
|
|
1559
|
+
onClick: () => move(c.field, i - 1),
|
|
1560
|
+
"aria-label": `Move ${f?.label ?? c.field} up`,
|
|
1561
|
+
children: "\u2191"
|
|
1562
|
+
}
|
|
1563
|
+
),
|
|
1564
|
+
/* @__PURE__ */ jsx4(
|
|
1565
|
+
"button",
|
|
1566
|
+
{
|
|
1567
|
+
type: "button",
|
|
1568
|
+
className: "qt-icon-btn",
|
|
1569
|
+
disabled: i === columns.length - 1,
|
|
1570
|
+
onClick: () => move(c.field, i + 1),
|
|
1571
|
+
"aria-label": `Move ${f?.label ?? c.field} down`,
|
|
1572
|
+
children: "\u2193"
|
|
1573
|
+
}
|
|
1574
|
+
),
|
|
1575
|
+
/* @__PURE__ */ jsx4(
|
|
1576
|
+
"button",
|
|
1577
|
+
{
|
|
1578
|
+
type: "button",
|
|
1579
|
+
className: "qt-icon-btn",
|
|
1580
|
+
onClick: () => toggle(c.field),
|
|
1581
|
+
"aria-label": `Remove ${f?.label ?? c.field}`,
|
|
1582
|
+
children: "\xD7"
|
|
1583
|
+
}
|
|
1584
|
+
)
|
|
1585
|
+
]
|
|
1586
|
+
},
|
|
1587
|
+
c.field
|
|
1588
|
+
);
|
|
1589
|
+
}) }),
|
|
1590
|
+
/* @__PURE__ */ jsx4(
|
|
1591
|
+
"button",
|
|
1592
|
+
{
|
|
1593
|
+
type: "button",
|
|
1594
|
+
className: "qt-link-btn",
|
|
1595
|
+
onClick: () => setColumns(
|
|
1596
|
+
api.defaults.select.length ? api.defaults.select : catalogue.filter((f) => f.select?.default).map((f) => ({ field: f.name }))
|
|
1597
|
+
),
|
|
1598
|
+
children: "Use default columns"
|
|
1599
|
+
}
|
|
1600
|
+
)
|
|
1601
|
+
] }),
|
|
1602
|
+
/* @__PURE__ */ jsxs4("aside", { className: "qt-catalogue-panel", children: [
|
|
1603
|
+
/* @__PURE__ */ jsx4("h3", { children: "Column catalogue" }),
|
|
1604
|
+
/* @__PURE__ */ jsx4(
|
|
1605
|
+
"input",
|
|
1606
|
+
{
|
|
1607
|
+
className: "qt-input",
|
|
1608
|
+
"aria-label": "Search columns",
|
|
1609
|
+
placeholder: "Search name or type\u2026",
|
|
1610
|
+
value: search,
|
|
1611
|
+
onChange: (e) => setSearch(e.target.value)
|
|
1612
|
+
}
|
|
1613
|
+
),
|
|
1614
|
+
/* @__PURE__ */ jsx4(
|
|
1615
|
+
"button",
|
|
1616
|
+
{
|
|
1617
|
+
type: "button",
|
|
1618
|
+
className: "qt-btn qt-create-computed",
|
|
1619
|
+
onClick: () => edit(),
|
|
1620
|
+
children: "\uFF0B Computed column"
|
|
1621
|
+
}
|
|
1622
|
+
),
|
|
1623
|
+
api.computed.loading && /* @__PURE__ */ jsx4("p", { role: "status", children: "Loading definitions\u2026" }),
|
|
1624
|
+
api.computed.error && /* @__PURE__ */ jsx4("p", { role: "alert", children: api.computed.error }),
|
|
1625
|
+
/* @__PURE__ */ jsx4("div", { className: "qt-catalogue-list", children: catalogueResults.map((f) => {
|
|
1626
|
+
const distinct = fieldStats[f.name]?.distinct;
|
|
1627
|
+
return /* @__PURE__ */ jsxs4(
|
|
1628
|
+
"div",
|
|
1629
|
+
{
|
|
1630
|
+
className: `qt-catalogue-item ${active === f.name && !editing ? "is-active" : ""} ${distinct != null && distinct <= 1 ? "is-low-cardinality" : ""}`,
|
|
1631
|
+
children: [
|
|
1632
|
+
/* @__PURE__ */ jsx4(
|
|
1633
|
+
"input",
|
|
1634
|
+
{
|
|
1635
|
+
type: "checkbox",
|
|
1636
|
+
"aria-label": `Include ${f.label}`,
|
|
1637
|
+
checked: columns.some((c) => c.field === f.name),
|
|
1638
|
+
onChange: () => toggle(f.name)
|
|
1639
|
+
}
|
|
1640
|
+
),
|
|
1641
|
+
/* @__PURE__ */ jsxs4("button", { type: "button", onClick: () => browse(f.name), children: [
|
|
1642
|
+
/* @__PURE__ */ jsx4("strong", { children: f.label }),
|
|
1643
|
+
/* @__PURE__ */ jsxs4("small", { children: [
|
|
1644
|
+
isComputedField(f.name) ? "\u0192 \xB7 " : "",
|
|
1645
|
+
f.type,
|
|
1646
|
+
" \xB7 ",
|
|
1647
|
+
f.group ?? f.name,
|
|
1648
|
+
distinct != null && ` \xB7 ${distinct.toLocaleString()} distinct`
|
|
1649
|
+
] })
|
|
1650
|
+
] })
|
|
1651
|
+
]
|
|
1652
|
+
},
|
|
1653
|
+
f.name
|
|
1654
|
+
);
|
|
1655
|
+
}) }),
|
|
1656
|
+
/* @__PURE__ */ jsx4(
|
|
1657
|
+
"button",
|
|
1658
|
+
{
|
|
1659
|
+
type: "button",
|
|
1660
|
+
className: "qt-link-btn",
|
|
1661
|
+
onClick: () => void api.computed.reload(),
|
|
1662
|
+
children: "Reload shared definitions"
|
|
1663
|
+
}
|
|
1664
|
+
)
|
|
1665
|
+
] }),
|
|
1666
|
+
/* @__PURE__ */ jsxs4("section", { className: "qt-column-workbench", children: [
|
|
1667
|
+
editing ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1668
|
+
/* @__PURE__ */ jsxs4("div", { className: "qt-editor-title", children: [
|
|
1669
|
+
/* @__PURE__ */ jsx4("h3", { children: revision ? "Edit shared computed column" : "New computed column" }),
|
|
1670
|
+
/* @__PURE__ */ jsx4("span", { className: "qt-type-badge", children: validation.plan?.type ?? "Check formula" })
|
|
1671
|
+
] }),
|
|
1672
|
+
/* @__PURE__ */ jsxs4("label", { className: "qt-editor-label", children: [
|
|
1673
|
+
"Column name",
|
|
1674
|
+
/* @__PURE__ */ jsx4(
|
|
1675
|
+
"input",
|
|
1676
|
+
{
|
|
1677
|
+
className: "qt-input",
|
|
1678
|
+
value: label,
|
|
1679
|
+
maxLength: 200,
|
|
1680
|
+
onChange: (e) => setLabel(e.target.value),
|
|
1681
|
+
placeholder: "e.g. Email domain"
|
|
1682
|
+
}
|
|
1683
|
+
)
|
|
1684
|
+
] }),
|
|
1685
|
+
/* @__PURE__ */ jsx4(
|
|
1686
|
+
Suspense,
|
|
1687
|
+
{
|
|
1688
|
+
fallback: /* @__PURE__ */ jsx4(
|
|
1689
|
+
"textarea",
|
|
1690
|
+
{
|
|
1691
|
+
"aria-label": "Computed column formula",
|
|
1692
|
+
value: source,
|
|
1693
|
+
onChange: (e) => setSource(e.target.value)
|
|
1694
|
+
}
|
|
1695
|
+
),
|
|
1696
|
+
children: /* @__PURE__ */ jsx4(
|
|
1697
|
+
FormulaEditor,
|
|
1698
|
+
{
|
|
1699
|
+
value: source,
|
|
1700
|
+
onChange: setSource,
|
|
1701
|
+
fields: api.computed.catalogue.filter(
|
|
1702
|
+
(f) => f.name !== computedFieldName(id)
|
|
1703
|
+
),
|
|
1704
|
+
compile: (value) => api.computed.compile(value, id)
|
|
1705
|
+
}
|
|
1706
|
+
)
|
|
1707
|
+
}
|
|
1708
|
+
),
|
|
1709
|
+
/* @__PURE__ */ jsxs4("details", { className: "qt-function-library", children: [
|
|
1710
|
+
/* @__PURE__ */ jsx4("summary", { children: "Functions & examples" }),
|
|
1711
|
+
/* @__PURE__ */ jsx4(
|
|
1712
|
+
"input",
|
|
1713
|
+
{
|
|
1714
|
+
className: "qt-input",
|
|
1715
|
+
"aria-label": "Search functions",
|
|
1716
|
+
placeholder: "Search functions\u2026",
|
|
1717
|
+
value: functionSearch,
|
|
1718
|
+
onChange: (e) => setFunctionSearch(e.target.value)
|
|
1719
|
+
}
|
|
1720
|
+
),
|
|
1721
|
+
/* @__PURE__ */ jsx4("div", { className: "qt-function-list", children: FORMULA_FUNCTIONS.filter(
|
|
1722
|
+
(f) => `${f.name} ${f.description}`.toLowerCase().includes(functionSearch.toLowerCase())
|
|
1723
|
+
).map((f) => /* @__PURE__ */ jsxs4(
|
|
1724
|
+
"button",
|
|
1725
|
+
{
|
|
1726
|
+
type: "button",
|
|
1727
|
+
title: f.description,
|
|
1728
|
+
onClick: () => setSource(`${f.name}(${source})`),
|
|
1729
|
+
children: [
|
|
1730
|
+
/* @__PURE__ */ jsx4("code", { children: f.signature }),
|
|
1731
|
+
/* @__PURE__ */ jsx4("small", { children: f.description })
|
|
1732
|
+
]
|
|
1733
|
+
},
|
|
1734
|
+
f.name
|
|
1735
|
+
)) }),
|
|
1736
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1737
|
+
"Examples: ",
|
|
1738
|
+
/* @__PURE__ */ jsx4("code", { children: "LEFT([name], 3)" }),
|
|
1739
|
+
" \xB7",
|
|
1740
|
+
" ",
|
|
1741
|
+
/* @__PURE__ */ jsx4("code", { children: 'IF([amount] > 100, "High", "Low")' }),
|
|
1742
|
+
" \xB7",
|
|
1743
|
+
" ",
|
|
1744
|
+
/* @__PURE__ */ jsx4("code", { children: 'REGEX_EXTRACT([email], "@(.+)$", 1)' })
|
|
1745
|
+
] })
|
|
1746
|
+
] }),
|
|
1747
|
+
/* @__PURE__ */ jsxs4("div", { className: "qt-definition-actions", children: [
|
|
1748
|
+
/* @__PURE__ */ jsx4(
|
|
1749
|
+
"button",
|
|
1750
|
+
{
|
|
1751
|
+
type: "button",
|
|
1752
|
+
className: "qt-btn",
|
|
1753
|
+
disabled: saving || !!validation.error || !label.trim(),
|
|
1754
|
+
onClick: () => void save(),
|
|
1755
|
+
children: saving ? "Saving\u2026" : revision ? "Save shared definition" : "Save definition & add column"
|
|
1756
|
+
}
|
|
1757
|
+
),
|
|
1758
|
+
revision && /* @__PURE__ */ jsx4(
|
|
1759
|
+
"button",
|
|
1760
|
+
{
|
|
1761
|
+
type: "button",
|
|
1762
|
+
className: "qt-link-btn",
|
|
1763
|
+
onClick: () => edit(id),
|
|
1764
|
+
children: "Reload editor from catalogue"
|
|
1765
|
+
}
|
|
1766
|
+
)
|
|
1767
|
+
] }),
|
|
1768
|
+
/* @__PURE__ */ jsx4("p", { className: "qt-muted", children: "Saving changes this definition across all queries. Cancel below only discards column layout changes." })
|
|
1769
|
+
] }) : /* @__PURE__ */ jsxs4("div", { className: "qt-editor-title", children: [
|
|
1770
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1771
|
+
/* @__PURE__ */ jsx4("h3", { children: chosen?.label ?? "Explore a column" }),
|
|
1772
|
+
/* @__PURE__ */ jsxs4("p", { className: "qt-muted", children: [
|
|
1773
|
+
chosen?.name,
|
|
1774
|
+
" ",
|
|
1775
|
+
chosen ? `\xB7 ${chosen.type}` : ""
|
|
1776
|
+
] })
|
|
1777
|
+
] }),
|
|
1778
|
+
chosen && isComputedField(chosen.name) && /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1779
|
+
/* @__PURE__ */ jsx4(
|
|
1780
|
+
"button",
|
|
1781
|
+
{
|
|
1782
|
+
type: "button",
|
|
1783
|
+
className: "qt-btn",
|
|
1784
|
+
onClick: () => edit(chosen.name.slice(10)),
|
|
1785
|
+
children: "Edit definition"
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ jsx4(
|
|
1789
|
+
"button",
|
|
1790
|
+
{
|
|
1791
|
+
type: "button",
|
|
1792
|
+
className: "qt-btn",
|
|
1793
|
+
onClick: () => {
|
|
1794
|
+
const def = api.computed.definitions.find(
|
|
1795
|
+
(d) => computedFieldName(d.id) === chosen.name
|
|
1796
|
+
);
|
|
1797
|
+
edit();
|
|
1798
|
+
if (def) {
|
|
1799
|
+
setLabel(`${def.label} copy`);
|
|
1800
|
+
setSource(def.expression.source);
|
|
1801
|
+
}
|
|
1802
|
+
},
|
|
1803
|
+
children: "Duplicate"
|
|
1804
|
+
}
|
|
1805
|
+
)
|
|
1806
|
+
] })
|
|
1807
|
+
] }),
|
|
1808
|
+
/* @__PURE__ */ jsxs4("div", { className: "qt-preview-controls", children: [
|
|
1809
|
+
/* @__PURE__ */ jsxs4("label", { children: [
|
|
1810
|
+
"Rows to process",
|
|
1811
|
+
" ",
|
|
1812
|
+
/* @__PURE__ */ jsx4(
|
|
1813
|
+
"input",
|
|
1814
|
+
{
|
|
1815
|
+
className: "qt-input",
|
|
1816
|
+
type: "number",
|
|
1817
|
+
min: 1,
|
|
1818
|
+
max: 1e4,
|
|
1819
|
+
step: 1,
|
|
1820
|
+
value: Number.isNaN(count) ? "" : count,
|
|
1821
|
+
onChange: (e) => setCount(e.target.valueAsNumber)
|
|
1822
|
+
}
|
|
1823
|
+
)
|
|
1824
|
+
] }),
|
|
1825
|
+
/* @__PURE__ */ jsx4(
|
|
1826
|
+
"button",
|
|
1827
|
+
{
|
|
1828
|
+
type: "button",
|
|
1829
|
+
className: "qt-btn",
|
|
1830
|
+
onClick: () => setRetry((n) => n + 1),
|
|
1831
|
+
children: "Refresh preview"
|
|
1832
|
+
}
|
|
1833
|
+
),
|
|
1834
|
+
/* @__PURE__ */ jsxs4("label", { children: [
|
|
1835
|
+
"Show",
|
|
1836
|
+
" ",
|
|
1837
|
+
/* @__PURE__ */ jsxs4(
|
|
1838
|
+
"select",
|
|
1839
|
+
{
|
|
1840
|
+
value: filter,
|
|
1841
|
+
onChange: (e) => {
|
|
1842
|
+
setFilter(e.target.value);
|
|
1843
|
+
setPage(0);
|
|
1844
|
+
},
|
|
1845
|
+
children: [
|
|
1846
|
+
/* @__PURE__ */ jsx4("option", { value: "all", children: "All results" }),
|
|
1847
|
+
/* @__PURE__ */ jsx4("option", { value: "nulls", children: "Null results" }),
|
|
1848
|
+
/* @__PURE__ */ jsx4("option", { value: "errors", children: "Errors" })
|
|
1849
|
+
]
|
|
1850
|
+
}
|
|
1851
|
+
)
|
|
1852
|
+
] })
|
|
1853
|
+
] }),
|
|
1854
|
+
/* @__PURE__ */ jsx4("p", { className: "qt-muted", children: "First matching rows in current sort order, starting at row 1. Counts describe the processed sample. Maximum 10,000 rows." }),
|
|
1855
|
+
(!Number.isInteger(count) || count < 1 || count > 1e4) && /* @__PURE__ */ jsx4("p", { role: "alert", children: "Choose a whole number between 1 and 10,000." }),
|
|
1856
|
+
validation.error && /* @__PURE__ */ jsx4("p", { className: "qt-formula-error", role: "alert", children: validation.error }),
|
|
1857
|
+
error && /* @__PURE__ */ jsx4("p", { className: "qt-formula-error", role: "alert", children: error }),
|
|
1858
|
+
notice && /* @__PURE__ */ jsx4("p", { className: "qt-editor-notice", role: "status", children: notice }),
|
|
1859
|
+
loading && /* @__PURE__ */ jsx4("p", { role: "status", children: "Processing preview\u2026" }),
|
|
1860
|
+
preview && /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1861
|
+
/* @__PURE__ */ jsxs4("p", { className: "qt-preview-summary", role: "status", children: [
|
|
1862
|
+
/* @__PURE__ */ jsx4("strong", { children: preview.processed.toLocaleString() }),
|
|
1863
|
+
" of",
|
|
1864
|
+
" ",
|
|
1865
|
+
preview.total.toLocaleString(),
|
|
1866
|
+
" matching rows \xB7",
|
|
1867
|
+
" ",
|
|
1868
|
+
/* @__PURE__ */ jsx4("strong", { children: preview.groups.length.toLocaleString() }),
|
|
1869
|
+
" ",
|
|
1870
|
+
"distinct combinations \xB7 ",
|
|
1871
|
+
preview.nulls.toLocaleString(),
|
|
1872
|
+
" null \xB7 ",
|
|
1873
|
+
preview.errors.toLocaleString(),
|
|
1874
|
+
" errors",
|
|
1875
|
+
preview.processed === preview.total ? " \xB7 complete filtered set" : " \xB7 sample"
|
|
1876
|
+
] }),
|
|
1877
|
+
/* @__PURE__ */ jsx4("div", { className: "qt-preview-table-wrap", children: /* @__PURE__ */ jsxs4("table", { className: "qt-preview-table", children: [
|
|
1878
|
+
/* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsxs4("tr", { children: [
|
|
1879
|
+
editing && preview.dependencies.map((name, index) => /* @__PURE__ */ jsx4(
|
|
1880
|
+
"th",
|
|
1881
|
+
{
|
|
1882
|
+
"aria-sort": previewAriaSort({
|
|
1883
|
+
kind: "input",
|
|
1884
|
+
index
|
|
1885
|
+
}),
|
|
1886
|
+
children: previewSortHeader(
|
|
1887
|
+
api.computed.catalogue.find(
|
|
1888
|
+
(f) => f.name === name
|
|
1889
|
+
)?.label ?? name,
|
|
1890
|
+
{ kind: "input", index }
|
|
1891
|
+
)
|
|
1892
|
+
},
|
|
1893
|
+
name
|
|
1894
|
+
)),
|
|
1895
|
+
/* @__PURE__ */ jsx4("th", { "aria-sort": previewAriaSort({ kind: "result" }), children: previewSortHeader(editing ? "Result" : "Value", {
|
|
1896
|
+
kind: "result"
|
|
1897
|
+
}) }),
|
|
1898
|
+
/* @__PURE__ */ jsx4("th", { "aria-sort": previewAriaSort({ kind: "count" }), children: previewSortHeader("Count", { kind: "count" }, "desc") }),
|
|
1899
|
+
/* @__PURE__ */ jsx4("th", { "aria-sort": previewAriaSort({ kind: "percentage" }), children: previewSortHeader("%", { kind: "percentage" }, "desc") })
|
|
1900
|
+
] }) }),
|
|
1901
|
+
/* @__PURE__ */ jsx4("tbody", { children: shown.map((group, i) => /* @__PURE__ */ jsxs4("tr", { children: [
|
|
1902
|
+
editing && group.inputs.map((value, j) => /* @__PURE__ */ jsx4("td", { title: display(value), children: display(value) }, j)),
|
|
1903
|
+
/* @__PURE__ */ jsxs4(
|
|
1904
|
+
"td",
|
|
1905
|
+
{
|
|
1906
|
+
className: group.result.error ? "qt-formula-error" : "",
|
|
1907
|
+
title: group.result.error ?? display(group.result.value),
|
|
1908
|
+
children: [
|
|
1909
|
+
group.result.error ?? display(group.result.value),
|
|
1910
|
+
group.result.regex && /* @__PURE__ */ jsx4(
|
|
1911
|
+
"button",
|
|
1912
|
+
{
|
|
1913
|
+
type: "button",
|
|
1914
|
+
className: "qt-link-btn",
|
|
1915
|
+
onClick: () => setInspection(group.result.regex),
|
|
1916
|
+
children: "Inspect match"
|
|
1917
|
+
}
|
|
1918
|
+
)
|
|
1919
|
+
]
|
|
1920
|
+
}
|
|
1921
|
+
),
|
|
1922
|
+
/* @__PURE__ */ jsx4("td", { children: group.count.toLocaleString() }),
|
|
1923
|
+
/* @__PURE__ */ jsxs4("td", { children: [
|
|
1924
|
+
(100 * group.count / preview.processed).toFixed(
|
|
1925
|
+
1
|
|
1926
|
+
),
|
|
1927
|
+
"%"
|
|
1928
|
+
] })
|
|
1929
|
+
] }, i)) })
|
|
1930
|
+
] }) }),
|
|
1931
|
+
inspection && /* @__PURE__ */ jsxs4("div", { className: "qt-regex-inspector", children: [
|
|
1932
|
+
/* @__PURE__ */ jsx4("h4", { children: "Regex match" }),
|
|
1933
|
+
/* @__PURE__ */ jsxs4("pre", { children: [
|
|
1934
|
+
inspection.input.slice(0, inspection.start),
|
|
1935
|
+
/* @__PURE__ */ jsx4("mark", { children: inspection.input.slice(
|
|
1936
|
+
inspection.start,
|
|
1937
|
+
inspection.end
|
|
1938
|
+
) }),
|
|
1939
|
+
inspection.input.slice(inspection.end)
|
|
1940
|
+
] }),
|
|
1941
|
+
/* @__PURE__ */ jsx4("dl", { children: inspection.groups.map((value, i) => /* @__PURE__ */ jsxs4("div", { children: [
|
|
1942
|
+
/* @__PURE__ */ jsxs4("dt", { children: [
|
|
1943
|
+
"Group ",
|
|
1944
|
+
i,
|
|
1945
|
+
i === 0 ? " (whole match)" : ""
|
|
1946
|
+
] }),
|
|
1947
|
+
/* @__PURE__ */ jsx4("dd", { children: display(value) })
|
|
1948
|
+
] }, i)) })
|
|
1949
|
+
] }),
|
|
1950
|
+
!groups.length && /* @__PURE__ */ jsx4("p", { children: "No matching results in this sample." }),
|
|
1951
|
+
groups.length > 50 && /* @__PURE__ */ jsxs4("div", { className: "qt-preview-pages", children: [
|
|
1952
|
+
/* @__PURE__ */ jsx4(
|
|
1953
|
+
"button",
|
|
1954
|
+
{
|
|
1955
|
+
type: "button",
|
|
1956
|
+
className: "qt-btn",
|
|
1957
|
+
disabled: page === 0,
|
|
1958
|
+
onClick: () => setPage((n) => n - 1),
|
|
1959
|
+
children: "Previous"
|
|
1960
|
+
}
|
|
1961
|
+
),
|
|
1962
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
1963
|
+
"Combinations ",
|
|
1964
|
+
page * 50 + 1,
|
|
1965
|
+
"\u2013",
|
|
1966
|
+
Math.min((page + 1) * 50, groups.length),
|
|
1967
|
+
" of",
|
|
1968
|
+
" ",
|
|
1969
|
+
groups.length
|
|
1970
|
+
] }),
|
|
1971
|
+
/* @__PURE__ */ jsx4(
|
|
1972
|
+
"button",
|
|
1973
|
+
{
|
|
1974
|
+
type: "button",
|
|
1975
|
+
className: "qt-btn",
|
|
1976
|
+
disabled: (page + 1) * 50 >= groups.length,
|
|
1977
|
+
onClick: () => setPage((n) => n + 1),
|
|
1978
|
+
children: "Next"
|
|
1979
|
+
}
|
|
1980
|
+
)
|
|
1981
|
+
] })
|
|
1982
|
+
] })
|
|
1983
|
+
] })
|
|
1984
|
+
] }),
|
|
1985
|
+
/* @__PURE__ */ jsxs4("footer", { className: "qt-select-editor-footer", children: [
|
|
1986
|
+
/* @__PURE__ */ jsx4("span", { className: "qt-muted", children: "Computed columns are display-only. Select at least one column." }),
|
|
1987
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "qt-btn", onClick: onClose, children: "Cancel layout changes" }),
|
|
1988
|
+
/* @__PURE__ */ jsx4(
|
|
1989
|
+
"button",
|
|
1990
|
+
{
|
|
1991
|
+
type: "button",
|
|
1992
|
+
className: "qt-btn qt-primary",
|
|
1993
|
+
disabled: !columns.length || columns.length > 200,
|
|
1994
|
+
onClick: () => {
|
|
1995
|
+
api.setQuery((q) => ({ ...q, select: columns }));
|
|
1996
|
+
onClose();
|
|
1997
|
+
},
|
|
1998
|
+
children: "Apply columns"
|
|
1999
|
+
}
|
|
2000
|
+
)
|
|
2001
|
+
] })
|
|
2002
|
+
]
|
|
2003
|
+
}
|
|
2004
|
+
) });
|
|
2005
|
+
}
|
|
2006
|
+
|
|
1117
2007
|
// src/QueryBuilder.tsx
|
|
1118
|
-
import { useEffect as
|
|
2008
|
+
import { Fragment as Fragment6, useEffect as useEffect5, useId as useId2, useMemo as useMemo4, useRef as useRef3, useState as useState6 } from "react";
|
|
1119
2009
|
import {
|
|
1120
2010
|
NULLARY_OPS as NULLARY_OPS2,
|
|
1121
2011
|
aggOpNeedsField,
|
|
@@ -1124,16 +2014,19 @@ import {
|
|
|
1124
2014
|
filterValues as filterValuesFor,
|
|
1125
2015
|
isGroupable,
|
|
1126
2016
|
isMeasurable,
|
|
2017
|
+
isOrGroup,
|
|
2018
|
+
opPairsForField as opPairsForField2,
|
|
2019
|
+
predicatesOf,
|
|
1127
2020
|
queriesEqual,
|
|
1128
|
-
isFilterable,
|
|
1129
|
-
isSelectable,
|
|
2021
|
+
isFilterable as isFilterable2,
|
|
2022
|
+
isSelectable as isSelectable2,
|
|
1130
2023
|
isSortable as isSortable2,
|
|
1131
2024
|
opsForField as opsForField2
|
|
1132
2025
|
} from "@pythia-software/query-table-core";
|
|
1133
2026
|
|
|
1134
2027
|
// src/FieldPicker.tsx
|
|
1135
|
-
import { useMemo as
|
|
1136
|
-
import { Fragment as
|
|
2028
|
+
import { useMemo as useMemo3, useState as useState5 } from "react";
|
|
2029
|
+
import { Fragment as Fragment5, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1137
2030
|
var UNGROUPED = "other";
|
|
1138
2031
|
function FieldPicker({
|
|
1139
2032
|
fields,
|
|
@@ -1143,10 +2036,10 @@ function FieldPicker({
|
|
|
1143
2036
|
onPick,
|
|
1144
2037
|
onClose
|
|
1145
2038
|
}) {
|
|
1146
|
-
const [search, setSearch] =
|
|
2039
|
+
const [search, setSearch] = useState5("");
|
|
1147
2040
|
const needle = search.trim().toLowerCase();
|
|
1148
|
-
const excludedSet =
|
|
1149
|
-
const matched =
|
|
2041
|
+
const excludedSet = useMemo3(() => new Set(excluded ?? []), [excluded]);
|
|
2042
|
+
const matched = useMemo3(
|
|
1150
2043
|
() => fields.filter((f) => {
|
|
1151
2044
|
if (excludedSet.has(f.name)) return false;
|
|
1152
2045
|
if (!needle) return true;
|
|
@@ -1154,10 +2047,10 @@ function FieldPicker({
|
|
|
1154
2047
|
}),
|
|
1155
2048
|
[fields, excludedSet, needle]
|
|
1156
2049
|
);
|
|
1157
|
-
const grouped =
|
|
2050
|
+
const grouped = useMemo3(() => groupByGroup(matched), [matched]);
|
|
1158
2051
|
const firstEnabled = matched.find((f) => isEnabled(f, stats, gateOnDistinct));
|
|
1159
|
-
return /* @__PURE__ */
|
|
1160
|
-
/* @__PURE__ */
|
|
2052
|
+
return /* @__PURE__ */ jsxs5("div", { className: "qt-picker", children: [
|
|
2053
|
+
/* @__PURE__ */ jsx5(
|
|
1161
2054
|
"input",
|
|
1162
2055
|
{
|
|
1163
2056
|
autoFocus: true,
|
|
@@ -1176,13 +2069,13 @@ function FieldPicker({
|
|
|
1176
2069
|
onBlur: () => setTimeout(() => onClose?.(), 150)
|
|
1177
2070
|
}
|
|
1178
2071
|
),
|
|
1179
|
-
/* @__PURE__ */
|
|
1180
|
-
/* @__PURE__ */
|
|
2072
|
+
/* @__PURE__ */ jsx5("div", { className: "qt-picker-list", children: matched.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "qt-picker-empty", children: "no matches" }) : grouped.map(([group, fs]) => /* @__PURE__ */ jsxs5("div", { className: "qt-picker-group", children: [
|
|
2073
|
+
/* @__PURE__ */ jsx5("div", { className: "qt-picker-group-label", children: group }),
|
|
1181
2074
|
fs.map((f) => {
|
|
1182
2075
|
const enabled = isEnabled(f, stats, gateOnDistinct);
|
|
1183
2076
|
const s = stats?.[f.name];
|
|
1184
2077
|
const aliasHit = aliasMatch(f, needle);
|
|
1185
|
-
return /* @__PURE__ */
|
|
2078
|
+
return /* @__PURE__ */ jsxs5(
|
|
1186
2079
|
"button",
|
|
1187
2080
|
{
|
|
1188
2081
|
type: "button",
|
|
@@ -1196,20 +2089,20 @@ function FieldPicker({
|
|
|
1196
2089
|
setSearch("");
|
|
1197
2090
|
},
|
|
1198
2091
|
children: [
|
|
1199
|
-
/* @__PURE__ */
|
|
1200
|
-
/* @__PURE__ */
|
|
1201
|
-
/* @__PURE__ */
|
|
2092
|
+
/* @__PURE__ */ jsxs5("span", { className: "qt-picker-row", children: [
|
|
2093
|
+
/* @__PURE__ */ jsx5("span", { className: "qt-picker-label", children: f.label }),
|
|
2094
|
+
/* @__PURE__ */ jsx5("span", { className: "qt-picker-name", children: f.name })
|
|
1202
2095
|
] }),
|
|
1203
|
-
aliasHit && /* @__PURE__ */
|
|
2096
|
+
aliasHit && /* @__PURE__ */ jsxs5("span", { className: "qt-picker-alias", children: [
|
|
1204
2097
|
"matches: ",
|
|
1205
2098
|
aliasHit
|
|
1206
2099
|
] }),
|
|
1207
|
-
s && /* @__PURE__ */
|
|
1208
|
-
s.distinct != null && /* @__PURE__ */
|
|
2100
|
+
s && /* @__PURE__ */ jsxs5("span", { className: "qt-picker-stats", children: [
|
|
2101
|
+
s.distinct != null && /* @__PURE__ */ jsxs5(Fragment5, { children: [
|
|
1209
2102
|
s.distinct,
|
|
1210
2103
|
" distinct"
|
|
1211
2104
|
] }),
|
|
1212
|
-
s.min != null && s.max != null && /* @__PURE__ */
|
|
2105
|
+
s.min != null && s.max != null && /* @__PURE__ */ jsxs5(Fragment5, { children: [
|
|
1213
2106
|
" ",
|
|
1214
2107
|
"\xB7 ",
|
|
1215
2108
|
fmtStat(s.min),
|
|
@@ -1263,17 +2156,17 @@ function fmtStat(v) {
|
|
|
1263
2156
|
}
|
|
1264
2157
|
|
|
1265
2158
|
// src/SavedQueriesModal.tsx
|
|
1266
|
-
import { useEffect as
|
|
1267
|
-
import { jsx as
|
|
2159
|
+
import { useEffect as useEffect4 } from "react";
|
|
2160
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1268
2161
|
function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
1269
|
-
|
|
2162
|
+
useEffect4(() => {
|
|
1270
2163
|
function onKey(e) {
|
|
1271
2164
|
if (e.key === "Escape") onClose();
|
|
1272
2165
|
}
|
|
1273
2166
|
window.addEventListener("keydown", onKey);
|
|
1274
2167
|
return () => window.removeEventListener("keydown", onKey);
|
|
1275
2168
|
}, [onClose]);
|
|
1276
|
-
return /* @__PURE__ */
|
|
2169
|
+
return /* @__PURE__ */ jsx6("div", { className: "qt-modal-backdrop", onClick: onClose, children: /* @__PURE__ */ jsxs6(
|
|
1277
2170
|
"div",
|
|
1278
2171
|
{
|
|
1279
2172
|
className: "qt-modal",
|
|
@@ -1282,12 +2175,12 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1282
2175
|
"aria-modal": "true",
|
|
1283
2176
|
"aria-label": "Saved queries",
|
|
1284
2177
|
children: [
|
|
1285
|
-
/* @__PURE__ */
|
|
1286
|
-
saved.loading ? /* @__PURE__ */
|
|
2178
|
+
/* @__PURE__ */ jsx6("h3", { className: "qt-modal-title", children: "Saved queries" }),
|
|
2179
|
+
saved.loading ? /* @__PURE__ */ jsx6("p", { className: "qt-muted", children: "Loading\u2026" }) : saved.items.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "qt-muted", children: "None saved yet. Save the current query from the toolbar." }) : /* @__PURE__ */ jsx6("ul", { className: "qt-saved-list", children: saved.items.map((s) => {
|
|
1287
2180
|
const filters = s.query.where.length;
|
|
1288
2181
|
const isDefault = saved.defaultId === s.id;
|
|
1289
|
-
return /* @__PURE__ */
|
|
1290
|
-
/* @__PURE__ */
|
|
2182
|
+
return /* @__PURE__ */ jsxs6("li", { className: "qt-saved-item", children: [
|
|
2183
|
+
/* @__PURE__ */ jsxs6(
|
|
1291
2184
|
"button",
|
|
1292
2185
|
{
|
|
1293
2186
|
type: "button",
|
|
@@ -1298,8 +2191,8 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1298
2191
|
onClose();
|
|
1299
2192
|
},
|
|
1300
2193
|
children: [
|
|
1301
|
-
/* @__PURE__ */
|
|
1302
|
-
/* @__PURE__ */
|
|
2194
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-saved-name", children: s.name }),
|
|
2195
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-saved-meta qt-muted", children: [
|
|
1303
2196
|
isDefault ? "Default view \xB7 " : "",
|
|
1304
2197
|
new Date(s.savedAt).toLocaleString(),
|
|
1305
2198
|
" \xB7 ",
|
|
@@ -1310,7 +2203,7 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1310
2203
|
]
|
|
1311
2204
|
}
|
|
1312
2205
|
),
|
|
1313
|
-
isDefault ? /* @__PURE__ */
|
|
2206
|
+
isDefault ? /* @__PURE__ */ jsx6(
|
|
1314
2207
|
"button",
|
|
1315
2208
|
{
|
|
1316
2209
|
type: "button",
|
|
@@ -1319,7 +2212,7 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1319
2212
|
onClick: () => void saved.clearDefault(),
|
|
1320
2213
|
children: "default"
|
|
1321
2214
|
}
|
|
1322
|
-
) : /* @__PURE__ */
|
|
2215
|
+
) : /* @__PURE__ */ jsx6(
|
|
1323
2216
|
"button",
|
|
1324
2217
|
{
|
|
1325
2218
|
type: "button",
|
|
@@ -1329,7 +2222,7 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1329
2222
|
children: "make default"
|
|
1330
2223
|
}
|
|
1331
2224
|
),
|
|
1332
|
-
/* @__PURE__ */
|
|
2225
|
+
/* @__PURE__ */ jsx6(
|
|
1333
2226
|
"button",
|
|
1334
2227
|
{
|
|
1335
2228
|
type: "button",
|
|
@@ -1341,14 +2234,14 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1341
2234
|
)
|
|
1342
2235
|
] }, s.id);
|
|
1343
2236
|
}) }),
|
|
1344
|
-
/* @__PURE__ */
|
|
2237
|
+
/* @__PURE__ */ jsx6("div", { className: "qt-modal-footer", children: /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-btn", onClick: onClose, children: "close" }) })
|
|
1345
2238
|
]
|
|
1346
2239
|
}
|
|
1347
2240
|
) });
|
|
1348
2241
|
}
|
|
1349
2242
|
|
|
1350
2243
|
// src/QueryBuilder.tsx
|
|
1351
|
-
import { jsx as
|
|
2244
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1352
2245
|
var cx3 = (...parts) => parts.filter((p) => Boolean(p)).join(" ");
|
|
1353
2246
|
var MAX_AUTO_REFRESH_POLLS = 1e3;
|
|
1354
2247
|
var DEFAULT_AUTO_REFRESH_FREQUENCY_MS = 2e4;
|
|
@@ -1392,8 +2285,8 @@ function formatStatusTime(ms) {
|
|
|
1392
2285
|
return new Date(ms).toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
|
|
1393
2286
|
}
|
|
1394
2287
|
function useFieldHasNull(api, fieldName) {
|
|
1395
|
-
const [hasNull, setHasNull] =
|
|
1396
|
-
|
|
2288
|
+
const [hasNull, setHasNull] = useState6(void 0);
|
|
2289
|
+
useEffect5(() => {
|
|
1397
2290
|
if (!fieldName) return;
|
|
1398
2291
|
let cancelled = false;
|
|
1399
2292
|
void api.filterValues(fieldName, "").then((r) => {
|
|
@@ -1409,17 +2302,29 @@ function useFieldHasNull(api, fieldName) {
|
|
|
1409
2302
|
}, [api, fieldName]);
|
|
1410
2303
|
return hasNull;
|
|
1411
2304
|
}
|
|
1412
|
-
|
|
2305
|
+
var CONVERGED_NEGATIVE_OPS = /* @__PURE__ */ new Set(["!=", "is_not_null", "not_matches_regex"]);
|
|
2306
|
+
function positiveOpsForField(field) {
|
|
2307
|
+
return opsForField2(field).filter((op) => !CONVERGED_NEGATIVE_OPS.has(op));
|
|
2308
|
+
}
|
|
2309
|
+
function predicateAsText(clause, byName) {
|
|
1413
2310
|
const field = byName.get(clause.field)?.label ?? clause.field;
|
|
1414
2311
|
const op = clause.op.replace(/_/g, " ");
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
2312
|
+
const not = clause.negated ? "not " : "";
|
|
2313
|
+
if (NULLARY_OPS2.has(clause.op)) return `${field} ${not}${op}`;
|
|
2314
|
+
const strategy = byName.get(clause.field)?.filter?.values;
|
|
2315
|
+
const option = strategy?.source === "static" ? strategy.options.find((o) => typeof o !== "string" && o.value === clause.value) : void 0;
|
|
2316
|
+
const value = clause.value === "" ? "''" : typeof option === "object" ? option.label : clause.value;
|
|
2317
|
+
return `${field} ${not}${op} ${value}`;
|
|
2318
|
+
}
|
|
2319
|
+
function whereTermAsText(term, byName) {
|
|
2320
|
+
if (isOrGroup(term)) return `(${term.any.map((c) => predicateAsText(c, byName)).join(" or ")})`;
|
|
2321
|
+
return predicateAsText(term, byName);
|
|
1418
2322
|
}
|
|
1419
2323
|
function orderByAsText(term, byName) {
|
|
1420
2324
|
const field = byName.get(term.field)?.label ?? term.field;
|
|
2325
|
+
const expression = term.extract ? `regex_extract(${field}, /${term.extract.regex}/)` : field;
|
|
1421
2326
|
const nulls = term.nulls ? ` nulls ${term.nulls}` : "";
|
|
1422
|
-
return `${
|
|
2327
|
+
return `${expression} ${term.dir}${nulls}`;
|
|
1423
2328
|
}
|
|
1424
2329
|
function chipFieldWidthChars(text, fallback = 5, max = 30) {
|
|
1425
2330
|
return Math.min(max, Math.max(fallback, (text || "").length + 1));
|
|
@@ -1428,16 +2333,13 @@ function chipFieldWidthForInput(text, fieldType, fallback = 5, padding = 0) {
|
|
|
1428
2333
|
const cap = fieldType === "number" ? 10 : 30;
|
|
1429
2334
|
return chipFieldWidthChars(text, fallback, cap) + padding;
|
|
1430
2335
|
}
|
|
1431
|
-
function chipFieldWidthForSelect(text, fallback = 3, max = 30) {
|
|
1432
|
-
return chipFieldWidthChars(text, fallback, max) + 2;
|
|
1433
|
-
}
|
|
1434
2336
|
function buildCollapsedSummary(where, orderBy, byName) {
|
|
1435
|
-
const whereText = where
|
|
2337
|
+
const whereText = whereClauseSummaryText(where, byName);
|
|
1436
2338
|
const orderText = orderBy.length === 0 ? "(default)" : orderBy.map((term) => orderByAsText(term, byName)).join(", ");
|
|
1437
2339
|
return `WHERE ${whereText} ORDER BY ${orderText}`;
|
|
1438
2340
|
}
|
|
1439
2341
|
function whereClauseSummaryText(where, byName) {
|
|
1440
|
-
return where.length === 0 ? "all rows" : where.map((
|
|
2342
|
+
return where.length === 0 ? "all rows" : where.map((t) => whereTermAsText(t, byName)).join(" and ");
|
|
1441
2343
|
}
|
|
1442
2344
|
function orderBySummaryText(orderBy, byName) {
|
|
1443
2345
|
return orderBy.length === 0 ? "(default)" : orderBy.map((term) => orderByAsText(term, byName)).join(", ");
|
|
@@ -1453,15 +2355,15 @@ function QueryBuilder({
|
|
|
1453
2355
|
defaultCollapsed = false
|
|
1454
2356
|
}) {
|
|
1455
2357
|
const UNSAVED_QUERY_EDIT_ID = "__qt-unsaved-query__";
|
|
1456
|
-
const [showSaved, setShowSaved] =
|
|
1457
|
-
const [shareCopied, setShareCopied] =
|
|
1458
|
-
const [showAutoRefresh, setShowAutoRefresh] =
|
|
1459
|
-
const [autoRefreshFrequencyMs, setAutoRefreshFrequencyMs] =
|
|
1460
|
-
const [autoRefreshTurnOffAfterMs, setAutoRefreshTurnOffAfterMs] =
|
|
1461
|
-
const [editingSavedId, setEditingSavedId] =
|
|
1462
|
-
const [editingSavedName, setEditingSavedName] =
|
|
1463
|
-
const [lastSavedId, setLastSavedId] =
|
|
1464
|
-
const [collapsedFallback, setCollapsedFallback] =
|
|
2358
|
+
const [showSaved, setShowSaved] = useState6(false);
|
|
2359
|
+
const [shareCopied, setShareCopied] = useState6(false);
|
|
2360
|
+
const [showAutoRefresh, setShowAutoRefresh] = useState6(false);
|
|
2361
|
+
const [autoRefreshFrequencyMs, setAutoRefreshFrequencyMs] = useState6(DEFAULT_AUTO_REFRESH_FREQUENCY_MS);
|
|
2362
|
+
const [autoRefreshTurnOffAfterMs, setAutoRefreshTurnOffAfterMs] = useState6(DEFAULT_AUTO_REFRESH_TURN_OFF_AFTER_MS);
|
|
2363
|
+
const [editingSavedId, setEditingSavedId] = useState6(null);
|
|
2364
|
+
const [editingSavedName, setEditingSavedName] = useState6("");
|
|
2365
|
+
const [lastSavedId, setLastSavedId] = useState6(null);
|
|
2366
|
+
const [collapsedFallback, setCollapsedFallback] = useState6(defaultCollapsed);
|
|
1465
2367
|
const collapsedControlled = typeof collapsed !== "undefined";
|
|
1466
2368
|
const isCollapsed = collapsedControlled ? collapsed : collapsedFallback;
|
|
1467
2369
|
const setCollapsed = (next) => {
|
|
@@ -1471,31 +2373,31 @@ function QueryBuilder({
|
|
|
1471
2373
|
}
|
|
1472
2374
|
setCollapsedFallback(next);
|
|
1473
2375
|
};
|
|
1474
|
-
const [lastUpdatedAt, setLastUpdatedAt] =
|
|
1475
|
-
const [lastFailureAt, setLastFailureAt] =
|
|
1476
|
-
const byName =
|
|
1477
|
-
const activeSavedQuery =
|
|
2376
|
+
const [lastUpdatedAt, setLastUpdatedAt] = useState6(null);
|
|
2377
|
+
const [lastFailureAt, setLastFailureAt] = useState6(null);
|
|
2378
|
+
const byName = useMemo4(() => new Map(fields.map((f) => [f.name, f])), [fields]);
|
|
2379
|
+
const activeSavedQuery = useMemo4(
|
|
1478
2380
|
() => api.saved.items.find((item) => queriesEqual(item.query, api.query)),
|
|
1479
2381
|
[api.query, api.saved.items]
|
|
1480
2382
|
);
|
|
1481
|
-
const lastSavedQuery =
|
|
2383
|
+
const lastSavedQuery = useMemo4(
|
|
1482
2384
|
() => api.saved.items.find((item) => item.id === lastSavedId) ?? null,
|
|
1483
2385
|
[api.saved.items, lastSavedId]
|
|
1484
2386
|
);
|
|
1485
2387
|
const isSavedQuery = activeSavedQuery != null;
|
|
1486
2388
|
const isEditingUnsaved = editingSavedId === UNSAVED_QUERY_EDIT_ID;
|
|
1487
|
-
const bodyId =
|
|
2389
|
+
const bodyId = useId2();
|
|
1488
2390
|
const isEditingSaved = editingSavedId != null && activeSavedQuery?.id === editingSavedId;
|
|
1489
2391
|
const showUpdateButton = Boolean(lastSavedQuery) && !activeSavedQuery;
|
|
1490
|
-
const collapsedSummary =
|
|
2392
|
+
const collapsedSummary = useMemo4(
|
|
1491
2393
|
() => buildCollapsedSummary(api.query.where, api.query.orderBy, byName),
|
|
1492
2394
|
[api.query.where, api.query.orderBy, byName]
|
|
1493
2395
|
);
|
|
1494
|
-
const collapsedWhereText =
|
|
2396
|
+
const collapsedWhereText = useMemo4(
|
|
1495
2397
|
() => whereClauseSummaryText(api.query.where, byName),
|
|
1496
2398
|
[api.query.where, byName]
|
|
1497
2399
|
);
|
|
1498
|
-
const collapsedOrderText =
|
|
2400
|
+
const collapsedOrderText = useMemo4(
|
|
1499
2401
|
() => orderBySummaryText(api.query.orderBy, byName),
|
|
1500
2402
|
[api.query.orderBy, byName]
|
|
1501
2403
|
);
|
|
@@ -1507,27 +2409,27 @@ function QueryBuilder({
|
|
|
1507
2409
|
const lastUpdatedText = formatStatusTime(lastUpdatedAt);
|
|
1508
2410
|
const failureText = api.error ? `Error: ${api.error.message}` : lastFailureAt ? `Last failure at ${formatStatusTime(lastFailureAt)}` : null;
|
|
1509
2411
|
const isBusy = Boolean(running) || api.loading;
|
|
1510
|
-
const lastRowsRef =
|
|
1511
|
-
const lastTotalRef =
|
|
1512
|
-
const previousLoadingRef =
|
|
1513
|
-
const autoRefreshRef =
|
|
1514
|
-
const autoRefreshPopoverRef =
|
|
1515
|
-
const [autoRefreshPopoverAlignRight, setAutoRefreshPopoverAlignRight] =
|
|
1516
|
-
const shareFeedbackTimerRef =
|
|
1517
|
-
|
|
2412
|
+
const lastRowsRef = useRef3(null);
|
|
2413
|
+
const lastTotalRef = useRef3(null);
|
|
2414
|
+
const previousLoadingRef = useRef3(api.loading);
|
|
2415
|
+
const autoRefreshRef = useRef3(null);
|
|
2416
|
+
const autoRefreshPopoverRef = useRef3(null);
|
|
2417
|
+
const [autoRefreshPopoverAlignRight, setAutoRefreshPopoverAlignRight] = useState6(false);
|
|
2418
|
+
const shareFeedbackTimerRef = useRef3(null);
|
|
2419
|
+
useEffect5(
|
|
1518
2420
|
() => () => {
|
|
1519
2421
|
if (shareFeedbackTimerRef.current) clearTimeout(shareFeedbackTimerRef.current);
|
|
1520
2422
|
},
|
|
1521
2423
|
[]
|
|
1522
2424
|
);
|
|
1523
|
-
|
|
2425
|
+
useEffect5(() => {
|
|
1524
2426
|
setShareCopied(false);
|
|
1525
2427
|
if (shareFeedbackTimerRef.current) {
|
|
1526
2428
|
clearTimeout(shareFeedbackTimerRef.current);
|
|
1527
2429
|
shareFeedbackTimerRef.current = null;
|
|
1528
2430
|
}
|
|
1529
2431
|
}, [api.query]);
|
|
1530
|
-
|
|
2432
|
+
useEffect5(() => {
|
|
1531
2433
|
if (activeSavedQuery) {
|
|
1532
2434
|
setLastSavedId(activeSavedQuery.id);
|
|
1533
2435
|
if (!isEditingSaved) {
|
|
@@ -1541,11 +2443,11 @@ function QueryBuilder({
|
|
|
1541
2443
|
setEditingSavedName("");
|
|
1542
2444
|
}
|
|
1543
2445
|
}, [activeSavedQuery, isEditingSaved, isEditingUnsaved]);
|
|
1544
|
-
|
|
2446
|
+
useEffect5(() => {
|
|
1545
2447
|
if (!lastSavedId || api.saved.items.some((item) => item.id === lastSavedId)) return;
|
|
1546
2448
|
setLastSavedId(null);
|
|
1547
2449
|
}, [api.saved.items, lastSavedId]);
|
|
1548
|
-
|
|
2450
|
+
useEffect5(() => {
|
|
1549
2451
|
const hasData = api.rows.length > 0 || total != null;
|
|
1550
2452
|
const rowsChanged = lastRowsRef.current !== api.rows;
|
|
1551
2453
|
const totalChanged = lastTotalRef.current !== total;
|
|
@@ -1640,15 +2542,15 @@ function QueryBuilder({
|
|
|
1640
2542
|
return;
|
|
1641
2543
|
}
|
|
1642
2544
|
}
|
|
1643
|
-
|
|
2545
|
+
useEffect5(() => {
|
|
1644
2546
|
if (isCollapsed) setShowSaved(false);
|
|
1645
2547
|
}, [isCollapsed]);
|
|
1646
|
-
|
|
2548
|
+
useEffect5(() => {
|
|
1647
2549
|
if (!showAutoRefresh || !autoRefreshStatus) return;
|
|
1648
2550
|
setAutoRefreshFrequencyMs(autoRefreshStatus.frequencyMs);
|
|
1649
2551
|
setAutoRefreshTurnOffAfterMs(autoRefreshStatus.turnOffAfterMs);
|
|
1650
2552
|
}, [showAutoRefresh, autoRefreshStatus]);
|
|
1651
|
-
|
|
2553
|
+
useEffect5(() => {
|
|
1652
2554
|
if (!showAutoRefresh) return;
|
|
1653
2555
|
function onDocumentClick(event) {
|
|
1654
2556
|
const wrapper = autoRefreshRef.current;
|
|
@@ -1707,11 +2609,11 @@ function QueryBuilder({
|
|
|
1707
2609
|
window.prompt("Copy this query URL", url.toString());
|
|
1708
2610
|
}
|
|
1709
2611
|
}
|
|
1710
|
-
return /* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
2612
|
+
return /* @__PURE__ */ jsxs7("div", { className: cx3("qt-qb", classNames?.root), children: [
|
|
2613
|
+
/* @__PURE__ */ jsxs7("div", { className: "qt-qb-bar qt-qb-state-bar qt-qb-row", children: [
|
|
2614
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-qb-state-main", children: [
|
|
2615
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-qb-saved", children: [
|
|
2616
|
+
/* @__PURE__ */ jsx7(
|
|
1715
2617
|
"button",
|
|
1716
2618
|
{
|
|
1717
2619
|
type: "button",
|
|
@@ -1722,8 +2624,8 @@ function QueryBuilder({
|
|
|
1722
2624
|
children: isSavedQuery ? "\u2605" : "\u2606"
|
|
1723
2625
|
}
|
|
1724
2626
|
),
|
|
1725
|
-
isEditingSaved || isEditingUnsaved ? /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
2627
|
+
isEditingSaved || isEditingUnsaved ? /* @__PURE__ */ jsxs7("span", { className: "qt-qb-saved-input-wrap", children: [
|
|
2628
|
+
/* @__PURE__ */ jsx7(
|
|
1727
2629
|
"input",
|
|
1728
2630
|
{
|
|
1729
2631
|
type: "text",
|
|
@@ -1746,7 +2648,7 @@ function QueryBuilder({
|
|
|
1746
2648
|
onClick: (e) => e.stopPropagation()
|
|
1747
2649
|
}
|
|
1748
2650
|
),
|
|
1749
|
-
/* @__PURE__ */
|
|
2651
|
+
/* @__PURE__ */ jsx7(
|
|
1750
2652
|
"button",
|
|
1751
2653
|
{
|
|
1752
2654
|
type: "button",
|
|
@@ -1760,7 +2662,7 @@ function QueryBuilder({
|
|
|
1760
2662
|
children: "\u2715"
|
|
1761
2663
|
}
|
|
1762
2664
|
)
|
|
1763
|
-
] }) : /* @__PURE__ */
|
|
2665
|
+
] }) : /* @__PURE__ */ jsx7(
|
|
1764
2666
|
"button",
|
|
1765
2667
|
{
|
|
1766
2668
|
type: "button",
|
|
@@ -1770,17 +2672,17 @@ function QueryBuilder({
|
|
|
1770
2672
|
}
|
|
1771
2673
|
)
|
|
1772
2674
|
] }),
|
|
1773
|
-
isCollapsed ? /* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
2675
|
+
isCollapsed ? /* @__PURE__ */ jsxs7("span", { className: "qt-qb-summary qt-truncate", title: collapsedSummary, children: [
|
|
2676
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-summary-kw", children: "WHERE" }),
|
|
1775
2677
|
" ",
|
|
1776
|
-
/* @__PURE__ */
|
|
2678
|
+
/* @__PURE__ */ jsx7("strong", { children: collapsedWhereText }),
|
|
1777
2679
|
" ",
|
|
1778
|
-
/* @__PURE__ */
|
|
2680
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-summary-kw", children: "ORDER BY" }),
|
|
1779
2681
|
" ",
|
|
1780
|
-
/* @__PURE__ */
|
|
2682
|
+
/* @__PURE__ */ jsx7("strong", { children: collapsedOrderText })
|
|
1781
2683
|
] }) : null
|
|
1782
2684
|
] }),
|
|
1783
|
-
showUpdateButton ? /* @__PURE__ */
|
|
2685
|
+
showUpdateButton ? /* @__PURE__ */ jsx7(
|
|
1784
2686
|
"button",
|
|
1785
2687
|
{
|
|
1786
2688
|
type: "button",
|
|
@@ -1790,7 +2692,7 @@ function QueryBuilder({
|
|
|
1790
2692
|
children: `Update Query '${lastSavedQuery?.name ?? "query"}'`
|
|
1791
2693
|
}
|
|
1792
2694
|
) : null,
|
|
1793
|
-
/* @__PURE__ */
|
|
2695
|
+
/* @__PURE__ */ jsx7(
|
|
1794
2696
|
"button",
|
|
1795
2697
|
{
|
|
1796
2698
|
type: "button",
|
|
@@ -1801,11 +2703,11 @@ function QueryBuilder({
|
|
|
1801
2703
|
children: shareCopied ? "Copied!" : "Share"
|
|
1802
2704
|
}
|
|
1803
2705
|
),
|
|
1804
|
-
/* @__PURE__ */
|
|
2706
|
+
/* @__PURE__ */ jsxs7("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: () => setShowSaved(true), children: [
|
|
1805
2707
|
"Saved",
|
|
1806
2708
|
api.saved.items.length > 0 ? ` (${api.saved.items.length})` : ""
|
|
1807
2709
|
] }),
|
|
1808
|
-
/* @__PURE__ */
|
|
2710
|
+
/* @__PURE__ */ jsxs7(
|
|
1809
2711
|
"button",
|
|
1810
2712
|
{
|
|
1811
2713
|
type: "button",
|
|
@@ -1815,7 +2717,7 @@ function QueryBuilder({
|
|
|
1815
2717
|
"aria-controls": bodyId,
|
|
1816
2718
|
title: isCollapsed ? "Expand query builder" : "Collapse query builder",
|
|
1817
2719
|
children: [
|
|
1818
|
-
/* @__PURE__ */
|
|
2720
|
+
/* @__PURE__ */ jsx7(
|
|
1819
2721
|
"span",
|
|
1820
2722
|
{
|
|
1821
2723
|
className: cx3("qt-qb-collapse-caret", isCollapsed && "qt-qb-collapse-caret--collapsed"),
|
|
@@ -1823,38 +2725,38 @@ function QueryBuilder({
|
|
|
1823
2725
|
children: "\u25BE"
|
|
1824
2726
|
}
|
|
1825
2727
|
),
|
|
1826
|
-
/* @__PURE__ */
|
|
2728
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-sr-only", children: isCollapsed ? "Expand query builder" : "Collapse query builder" })
|
|
1827
2729
|
]
|
|
1828
2730
|
}
|
|
1829
2731
|
)
|
|
1830
2732
|
] }),
|
|
1831
|
-
!isCollapsed ? /* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */
|
|
1833
|
-
/* @__PURE__ */
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
1836
|
-
/* @__PURE__ */
|
|
1837
|
-
/* @__PURE__ */
|
|
2733
|
+
!isCollapsed ? /* @__PURE__ */ jsxs7("div", { className: "qt-qb-bar qt-qb-editor-bar", children: [
|
|
2734
|
+
/* @__PURE__ */ jsxs7("div", { id: bodyId, className: "qt-qb-body", children: [
|
|
2735
|
+
/* @__PURE__ */ jsx7(SelectRow, { api, fields, classNames, disabled: isBusy }),
|
|
2736
|
+
/* @__PURE__ */ jsx7(WhereRow, { api, byName, fields, classNames, disabled: isBusy }),
|
|
2737
|
+
/* @__PURE__ */ jsx7(OrderRow, { api, fields, classNames, disabled: isBusy }),
|
|
2738
|
+
/* @__PURE__ */ jsx7(WindowRow, { api, classNames, disabled: isBusy }),
|
|
2739
|
+
/* @__PURE__ */ jsx7(MetricsRow, { api, fields, classNames, disabled: isBusy })
|
|
1838
2740
|
] }),
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
/* @__PURE__ */
|
|
1841
|
-
/* @__PURE__ */
|
|
1842
|
-
/* @__PURE__ */
|
|
2741
|
+
/* @__PURE__ */ jsxs7("div", { className: "qt-qb-editor-stack", children: [
|
|
2742
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: api.undo, disabled: !api.canUndo || isBusy, children: "\u21B6 Undo" }),
|
|
2743
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: api.redo, disabled: !api.canRedo || isBusy, children: "\u21B7 Redo" }),
|
|
2744
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: api.resetAll, disabled: isBusy, children: "Reset All" })
|
|
1843
2745
|
] })
|
|
1844
2746
|
] }) : null,
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
/* @__PURE__ */
|
|
1847
|
-
/* @__PURE__ */
|
|
2747
|
+
/* @__PURE__ */ jsxs7("div", { className: "qt-qb-bar qt-qb-run-bar qt-qb-row", children: [
|
|
2748
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-run-metric qt-qb-run-metric--strong", children: api.loading ? "loading\u2026" : `${rowSummary} rows` }),
|
|
2749
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-qb-run-metric", children: [
|
|
1848
2750
|
"Last updated ",
|
|
1849
2751
|
lastUpdatedText
|
|
1850
2752
|
] }),
|
|
1851
|
-
failureText ? /* @__PURE__ */
|
|
1852
|
-
/* @__PURE__ */
|
|
1853
|
-
autoRefreshStatus ? /* @__PURE__ */
|
|
2753
|
+
failureText ? /* @__PURE__ */ jsx7("span", { className: "qt-qb-run-error", children: failureText }) : null,
|
|
2754
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-qb-run-actions", children: [
|
|
2755
|
+
autoRefreshStatus ? /* @__PURE__ */ jsx7("span", { className: "qt-qb-auto-status", children: `Every ${formatDuration2(autoRefreshStatus.frequencyMs)}; clear after ${formatDuration2(
|
|
1854
2756
|
autoRefreshStatus.turnOffAfterMs
|
|
1855
2757
|
)} (${autoRefreshStatus.pollCount} polls run)` }) : null,
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
2758
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-auto-refresh", ref: autoRefreshRef, children: [
|
|
2759
|
+
/* @__PURE__ */ jsx7(
|
|
1858
2760
|
"button",
|
|
1859
2761
|
{
|
|
1860
2762
|
type: "button",
|
|
@@ -1865,7 +2767,7 @@ function QueryBuilder({
|
|
|
1865
2767
|
children: autoRefreshButtonText
|
|
1866
2768
|
}
|
|
1867
2769
|
),
|
|
1868
|
-
showAutoRefresh ? /* @__PURE__ */
|
|
2770
|
+
showAutoRefresh ? /* @__PURE__ */ jsxs7(
|
|
1869
2771
|
"span",
|
|
1870
2772
|
{
|
|
1871
2773
|
ref: autoRefreshPopoverRef,
|
|
@@ -1873,27 +2775,27 @@ function QueryBuilder({
|
|
|
1873
2775
|
role: "dialog",
|
|
1874
2776
|
"aria-label": "Auto-refresh settings",
|
|
1875
2777
|
children: [
|
|
1876
|
-
/* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
2778
|
+
/* @__PURE__ */ jsxs7("label", { className: "qt-auto-refresh-field", children: [
|
|
2779
|
+
/* @__PURE__ */ jsx7("span", { children: "Frequency" }),
|
|
2780
|
+
/* @__PURE__ */ jsx7(
|
|
1879
2781
|
"select",
|
|
1880
2782
|
{
|
|
1881
2783
|
className: "qt-auto-refresh-select",
|
|
1882
2784
|
value: autoRefreshFrequencyMs,
|
|
1883
2785
|
onChange: (e) => setAutoRefreshFrequencyMs(Number(e.target.value)),
|
|
1884
|
-
children: AUTO_REFRESH_FREQUENCIES.map((option) => /* @__PURE__ */
|
|
2786
|
+
children: AUTO_REFRESH_FREQUENCIES.map((option) => /* @__PURE__ */ jsx7("option", { value: option.value, children: option.label }, option.value))
|
|
1885
2787
|
}
|
|
1886
2788
|
)
|
|
1887
2789
|
] }),
|
|
1888
|
-
/* @__PURE__ */
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
2790
|
+
/* @__PURE__ */ jsxs7("label", { className: "qt-auto-refresh-field", children: [
|
|
2791
|
+
/* @__PURE__ */ jsx7("span", { children: "Turn off after" }),
|
|
2792
|
+
/* @__PURE__ */ jsx7(
|
|
1891
2793
|
"select",
|
|
1892
2794
|
{
|
|
1893
2795
|
className: "qt-auto-refresh-select",
|
|
1894
2796
|
value: autoRefreshTurnOffAfterMs,
|
|
1895
2797
|
onChange: (e) => setAutoRefreshTurnOffAfterMs(Number(e.target.value)),
|
|
1896
|
-
children: AUTO_REFRESH_TURN_OFF_AFTER.map((option) => /* @__PURE__ */
|
|
2798
|
+
children: AUTO_REFRESH_TURN_OFF_AFTER.map((option) => /* @__PURE__ */ jsx7(
|
|
1897
2799
|
"option",
|
|
1898
2800
|
{
|
|
1899
2801
|
value: option.value,
|
|
@@ -1905,8 +2807,8 @@ function QueryBuilder({
|
|
|
1905
2807
|
}
|
|
1906
2808
|
)
|
|
1907
2809
|
] }),
|
|
1908
|
-
/* @__PURE__ */
|
|
1909
|
-
autoRefreshStatus ? /* @__PURE__ */
|
|
2810
|
+
/* @__PURE__ */ jsx7("span", { className: cx3("qt-auto-refresh-status", !canSubmitAutoRefresh && "qt-auto-refresh-status--error"), children: canSubmitAutoRefresh ? `${selectedAutoRefreshPolls} polls maximum` : `Choose between 1 and 1000 polls` }),
|
|
2811
|
+
autoRefreshStatus ? /* @__PURE__ */ jsxs7("span", { className: "qt-auto-refresh-status", children: [
|
|
1910
2812
|
"Active: every ",
|
|
1911
2813
|
formatDuration2(autoRefreshStatus.frequencyMs),
|
|
1912
2814
|
", clears after",
|
|
@@ -1916,8 +2818,8 @@ function QueryBuilder({
|
|
|
1916
2818
|
autoRefreshStatus.pollCount,
|
|
1917
2819
|
" polls run."
|
|
1918
2820
|
] }) : null,
|
|
1919
|
-
/* @__PURE__ */
|
|
1920
|
-
/* @__PURE__ */
|
|
2821
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-auto-refresh-actions", children: [
|
|
2822
|
+
/* @__PURE__ */ jsx7(
|
|
1921
2823
|
"button",
|
|
1922
2824
|
{
|
|
1923
2825
|
type: "button",
|
|
@@ -1927,7 +2829,7 @@ function QueryBuilder({
|
|
|
1927
2829
|
children: autoRefreshStatus ? "Update" : "Start"
|
|
1928
2830
|
}
|
|
1929
2831
|
),
|
|
1930
|
-
autoRefreshStatus ? /* @__PURE__ */
|
|
2832
|
+
autoRefreshStatus ? /* @__PURE__ */ jsx7(
|
|
1931
2833
|
"button",
|
|
1932
2834
|
{
|
|
1933
2835
|
type: "button",
|
|
@@ -1944,7 +2846,7 @@ function QueryBuilder({
|
|
|
1944
2846
|
}
|
|
1945
2847
|
) : null
|
|
1946
2848
|
] }),
|
|
1947
|
-
/* @__PURE__ */
|
|
2849
|
+
/* @__PURE__ */ jsx7(
|
|
1948
2850
|
"button",
|
|
1949
2851
|
{
|
|
1950
2852
|
type: "button",
|
|
@@ -1956,7 +2858,7 @@ function QueryBuilder({
|
|
|
1956
2858
|
)
|
|
1957
2859
|
] })
|
|
1958
2860
|
] }),
|
|
1959
|
-
!isCollapsed && showSaved ? /* @__PURE__ */
|
|
2861
|
+
!isCollapsed && showSaved ? /* @__PURE__ */ jsx7(SavedQueriesModal, { saved: api.saved, onClose: () => setShowSaved(false), onLoad: setLastSavedId }) : null
|
|
1960
2862
|
] });
|
|
1961
2863
|
}
|
|
1962
2864
|
function MoveButtons({
|
|
@@ -1967,8 +2869,8 @@ function MoveButtons({
|
|
|
1967
2869
|
onMove
|
|
1968
2870
|
}) {
|
|
1969
2871
|
if (count < 2) return null;
|
|
1970
|
-
return /* @__PURE__ */
|
|
1971
|
-
/* @__PURE__ */
|
|
2872
|
+
return /* @__PURE__ */ jsxs7("span", { className: "qt-chip-move-buttons", children: [
|
|
2873
|
+
/* @__PURE__ */ jsx7(
|
|
1972
2874
|
"button",
|
|
1973
2875
|
{
|
|
1974
2876
|
type: "button",
|
|
@@ -1980,7 +2882,7 @@ function MoveButtons({
|
|
|
1980
2882
|
children: "\u2190"
|
|
1981
2883
|
}
|
|
1982
2884
|
),
|
|
1983
|
-
/* @__PURE__ */
|
|
2885
|
+
/* @__PURE__ */ jsx7(
|
|
1984
2886
|
"button",
|
|
1985
2887
|
{
|
|
1986
2888
|
type: "button",
|
|
@@ -2001,19 +2903,22 @@ function SelectRow({
|
|
|
2001
2903
|
disabled
|
|
2002
2904
|
}) {
|
|
2003
2905
|
const { select, columnDrag } = api;
|
|
2004
|
-
const [
|
|
2005
|
-
const
|
|
2906
|
+
const [editingColumns, setEditingColumns] = useState6(false);
|
|
2907
|
+
const [adding, setAdding] = useState6(false);
|
|
2908
|
+
const fieldLabelByName = useMemo4(() => new Map(select.fields.map((f) => [f.name, f.label])), [select.fields]);
|
|
2006
2909
|
function reorderByIndex(from, overIndex) {
|
|
2007
2910
|
select.move(from, overIndex);
|
|
2008
2911
|
}
|
|
2009
2912
|
const fieldNames = select.visible.map((c) => c.field);
|
|
2010
2913
|
const dragSource = columnDrag.source;
|
|
2011
2914
|
const rendered = columnDrag.preview(fieldNames);
|
|
2012
|
-
return /* @__PURE__ */
|
|
2013
|
-
/* @__PURE__ */
|
|
2915
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-qb-row", children: [
|
|
2916
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-kw", children: "select" }),
|
|
2917
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-btn", disabled, onClick: () => setEditingColumns(true), children: "Edit columns" }),
|
|
2918
|
+
editingColumns && /* @__PURE__ */ jsx7(SelectColumnEditor, { api, onClose: () => setEditingColumns(false) }),
|
|
2014
2919
|
rendered.map((name, position) => {
|
|
2015
2920
|
const label = fieldLabelByName.get(name) ?? name;
|
|
2016
|
-
return /* @__PURE__ */
|
|
2921
|
+
return /* @__PURE__ */ jsxs7(
|
|
2017
2922
|
"span",
|
|
2018
2923
|
{
|
|
2019
2924
|
className: cx3(
|
|
@@ -2052,9 +2957,9 @@ function SelectRow({
|
|
|
2052
2957
|
onDragEnd: () => columnDrag.end(),
|
|
2053
2958
|
title: "drag to reorder",
|
|
2054
2959
|
children: [
|
|
2055
|
-
/* @__PURE__ */
|
|
2056
|
-
/* @__PURE__ */
|
|
2057
|
-
/* @__PURE__ */
|
|
2960
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: "qt-chip-grip", children: "\u22EE\u22EE" }),
|
|
2961
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-chip-field", children: label }),
|
|
2962
|
+
/* @__PURE__ */ jsx7(
|
|
2058
2963
|
MoveButtons,
|
|
2059
2964
|
{
|
|
2060
2965
|
label,
|
|
@@ -2064,23 +2969,23 @@ function SelectRow({
|
|
|
2064
2969
|
onMove: (nextPosition) => select.move(name, nextPosition)
|
|
2065
2970
|
}
|
|
2066
2971
|
),
|
|
2067
|
-
/* @__PURE__ */
|
|
2972
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-chip-x", onClick: () => select.hide(name), disabled, children: "\u2715" })
|
|
2068
2973
|
]
|
|
2069
2974
|
},
|
|
2070
2975
|
name
|
|
2071
2976
|
);
|
|
2072
2977
|
}),
|
|
2073
|
-
adding ? /* @__PURE__ */
|
|
2978
|
+
adding ? /* @__PURE__ */ jsx7(
|
|
2074
2979
|
FieldPicker,
|
|
2075
2980
|
{
|
|
2076
|
-
fields: select.hidden.filter(
|
|
2981
|
+
fields: select.hidden.filter(isSelectable2),
|
|
2077
2982
|
onPick: (f) => {
|
|
2078
2983
|
select.show(f.name);
|
|
2079
2984
|
setAdding(false);
|
|
2080
2985
|
},
|
|
2081
2986
|
onClose: () => setAdding(false)
|
|
2082
2987
|
}
|
|
2083
|
-
) : /* @__PURE__ */
|
|
2988
|
+
) : /* @__PURE__ */ jsx7(
|
|
2084
2989
|
"button",
|
|
2085
2990
|
{
|
|
2086
2991
|
type: "button",
|
|
@@ -2090,9 +2995,16 @@ function SelectRow({
|
|
|
2090
2995
|
children: "+ add column"
|
|
2091
2996
|
}
|
|
2092
2997
|
),
|
|
2093
|
-
api.query.select.length > 0 && /* @__PURE__ */
|
|
2998
|
+
api.query.select.length > 0 && /* @__PURE__ */ jsx7("button", { type: "button", className: "qt-link-btn", onClick: select.reset, disabled, title: "Reset to default columns", children: "reset" })
|
|
2094
2999
|
] });
|
|
2095
3000
|
}
|
|
3001
|
+
function dropModeFor(e) {
|
|
3002
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
3003
|
+
const x = e.clientX - rect.left;
|
|
3004
|
+
if (x < rect.width * 0.3) return "before";
|
|
3005
|
+
if (x > rect.width * 0.7) return "after";
|
|
3006
|
+
return "merge";
|
|
3007
|
+
}
|
|
2096
3008
|
function WhereRow({
|
|
2097
3009
|
api,
|
|
2098
3010
|
byName,
|
|
@@ -2100,148 +3012,319 @@ function WhereRow({
|
|
|
2100
3012
|
classNames,
|
|
2101
3013
|
disabled
|
|
2102
3014
|
}) {
|
|
2103
|
-
const [adding, setAdding] =
|
|
3015
|
+
const [adding, setAdding] = useState6(false);
|
|
3016
|
+
const [drag, setDrag] = useState6(null);
|
|
3017
|
+
const where = api.query.where;
|
|
3018
|
+
const hasOr = where.some(isOrGroup);
|
|
2104
3019
|
function addClause(f) {
|
|
2105
|
-
const op = opsForField2(f)[0] ?? "=";
|
|
3020
|
+
const op = positiveOpsForField(f)[0] ?? opsForField2(f)[0] ?? "=";
|
|
2106
3021
|
api.addFilter({ field: f.name, op, value: "" });
|
|
2107
3022
|
setAdding(false);
|
|
2108
3023
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
)
|
|
2136
|
-
|
|
3024
|
+
function handleOver(e, index) {
|
|
3025
|
+
if (!drag) return;
|
|
3026
|
+
e.preventDefault();
|
|
3027
|
+
e.dataTransfer.dropEffect = "move";
|
|
3028
|
+
const mode = dropModeFor(e);
|
|
3029
|
+
setDrag((d) => d && (d.over?.index !== index || d.over?.mode !== mode) ? { from: d.from, over: { index, mode } } : d);
|
|
3030
|
+
}
|
|
3031
|
+
function handleDrop() {
|
|
3032
|
+
if (!drag || !drag.over) {
|
|
3033
|
+
setDrag(null);
|
|
3034
|
+
return;
|
|
3035
|
+
}
|
|
3036
|
+
const { from } = drag;
|
|
3037
|
+
const { index: target, mode } = drag.over;
|
|
3038
|
+
if (mode === "merge") {
|
|
3039
|
+
if (target !== from) api.mergeFilters(from, target);
|
|
3040
|
+
} else {
|
|
3041
|
+
const tPrime = target < from ? target : target - 1;
|
|
3042
|
+
const insertAt = mode === "after" ? tPrime + 1 : tPrime;
|
|
3043
|
+
if (insertAt !== from) api.reorderFilters(from, insertAt);
|
|
3044
|
+
}
|
|
3045
|
+
setDrag(null);
|
|
3046
|
+
}
|
|
3047
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-qb-row", children: [
|
|
3048
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-kw", children: "where" }),
|
|
3049
|
+
where.length === 0 && !adding && /* @__PURE__ */ jsx7("span", { className: "qt-qb-hint", children: "all rows" }),
|
|
3050
|
+
where.map((term, index) => /* @__PURE__ */ jsxs7("span", { className: "qt-where-term-wrap", children: [
|
|
3051
|
+
index > 0 && hasOr && /* @__PURE__ */ jsx7("span", { className: "qt-where-and", children: "AND" }),
|
|
3052
|
+
/* @__PURE__ */ jsx7(
|
|
3053
|
+
TermChip,
|
|
3054
|
+
{
|
|
3055
|
+
api,
|
|
3056
|
+
term,
|
|
3057
|
+
index,
|
|
3058
|
+
byName,
|
|
3059
|
+
classNames,
|
|
3060
|
+
disabled,
|
|
3061
|
+
isDragSource: drag?.from === index,
|
|
3062
|
+
dropHint: drag?.over?.index === index ? drag.over.mode : null,
|
|
3063
|
+
onDragStart: () => setDrag({ from: index, over: null }),
|
|
3064
|
+
onDragOver: (e) => handleOver(e, index),
|
|
3065
|
+
onDrop: handleDrop,
|
|
3066
|
+
onDragEnd: () => setDrag(null)
|
|
3067
|
+
}
|
|
3068
|
+
)
|
|
3069
|
+
] }, index)),
|
|
3070
|
+
adding ? /* @__PURE__ */ jsx7(
|
|
2137
3071
|
FieldPicker,
|
|
2138
3072
|
{
|
|
2139
|
-
fields: fields.filter(
|
|
3073
|
+
fields: fields.filter(isFilterable2),
|
|
2140
3074
|
gateOnDistinct: true,
|
|
2141
3075
|
onPick: addClause,
|
|
2142
3076
|
onClose: () => setAdding(false)
|
|
2143
3077
|
}
|
|
2144
|
-
) : /* @__PURE__ */
|
|
2145
|
-
|
|
3078
|
+
) : /* @__PURE__ */ jsx7("button", { type: "button", className: "qt-add", onClick: () => setAdding(true), disabled, children: "+ add filter" }),
|
|
3079
|
+
where.length > 0 && /* @__PURE__ */ jsx7("button", { type: "button", className: "qt-link-btn", onClick: api.clearFilters, disabled, title: "Reset filters", children: "reset" })
|
|
2146
3080
|
] });
|
|
2147
3081
|
}
|
|
2148
|
-
function
|
|
3082
|
+
function TermChip({
|
|
2149
3083
|
api,
|
|
2150
|
-
|
|
2151
|
-
|
|
3084
|
+
term,
|
|
3085
|
+
index,
|
|
3086
|
+
byName,
|
|
2152
3087
|
classNames,
|
|
2153
3088
|
disabled,
|
|
2154
|
-
|
|
2155
|
-
|
|
3089
|
+
isDragSource,
|
|
3090
|
+
dropHint,
|
|
3091
|
+
onDragStart,
|
|
3092
|
+
onDragOver,
|
|
3093
|
+
onDrop,
|
|
3094
|
+
onDragEnd
|
|
2156
3095
|
}) {
|
|
2157
|
-
const
|
|
2158
|
-
const
|
|
2159
|
-
const
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
3096
|
+
const group = isOrGroup(term);
|
|
3097
|
+
const predicates = predicatesOf(term);
|
|
3098
|
+
const grip = /* @__PURE__ */ jsx7(
|
|
3099
|
+
"span",
|
|
3100
|
+
{
|
|
3101
|
+
className: "qt-chip-grip",
|
|
3102
|
+
"aria-hidden": true,
|
|
3103
|
+
draggable: !disabled,
|
|
3104
|
+
onDragStart: (e) => {
|
|
3105
|
+
onDragStart();
|
|
3106
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3107
|
+
e.dataTransfer.setData("text/plain", String(index));
|
|
3108
|
+
},
|
|
3109
|
+
onDragEnd,
|
|
3110
|
+
title: "drag to reorder, or drop onto another filter to make an OR",
|
|
3111
|
+
children: "\u22EE\u22EE"
|
|
3112
|
+
}
|
|
3113
|
+
);
|
|
3114
|
+
return /* @__PURE__ */ jsxs7(
|
|
3115
|
+
"span",
|
|
3116
|
+
{
|
|
3117
|
+
className: cx3(
|
|
3118
|
+
"qt-chip",
|
|
3119
|
+
"qt-chip--where",
|
|
3120
|
+
group && "qt-chip--or",
|
|
3121
|
+
isDragSource && "qt-chip--dragging",
|
|
3122
|
+
dropHint ? `qt-chip--drop-${dropHint}` : void 0,
|
|
3123
|
+
classNames?.chip
|
|
3124
|
+
),
|
|
3125
|
+
onDragOver,
|
|
3126
|
+
onDrop: (e) => {
|
|
3127
|
+
e.preventDefault();
|
|
3128
|
+
onDrop();
|
|
3129
|
+
},
|
|
3130
|
+
children: [
|
|
3131
|
+
grip,
|
|
3132
|
+
group ? /* @__PURE__ */ jsx7("span", { className: "qt-or-group", children: predicates.map((clause, predIndex) => /* @__PURE__ */ jsxs7("span", { className: "qt-or-member", children: [
|
|
3133
|
+
predIndex > 0 && /* @__PURE__ */ jsx7("span", { className: "qt-chip-and qt-chip-or", children: "OR" }),
|
|
3134
|
+
/* @__PURE__ */ jsx7(
|
|
3135
|
+
PredicateEditor,
|
|
3136
|
+
{
|
|
3137
|
+
api,
|
|
3138
|
+
field: byName.get(clause.field),
|
|
3139
|
+
clause,
|
|
3140
|
+
classNames,
|
|
3141
|
+
disabled,
|
|
3142
|
+
onChange: (next) => api.updatePredicate(index, predIndex, next),
|
|
3143
|
+
onRemove: () => api.removePredicate(index, predIndex)
|
|
3144
|
+
}
|
|
3145
|
+
)
|
|
3146
|
+
] }, predIndex)) }) : /* @__PURE__ */ jsx7(
|
|
3147
|
+
PredicateEditor,
|
|
2172
3148
|
{
|
|
2173
|
-
|
|
2174
|
-
|
|
3149
|
+
api,
|
|
3150
|
+
field: byName.get(term.field),
|
|
3151
|
+
clause: term,
|
|
3152
|
+
classNames,
|
|
2175
3153
|
disabled,
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
children: clauseOps.map((op) => /* @__PURE__ */ jsx5("option", { value: op, children: op }, op))
|
|
3154
|
+
onChange: (next) => api.updatePredicate(index, 0, next),
|
|
3155
|
+
onRemove: () => api.removeFilter(index)
|
|
2179
3156
|
}
|
|
2180
|
-
)
|
|
2181
|
-
|
|
2182
|
-
|
|
3157
|
+
)
|
|
3158
|
+
]
|
|
3159
|
+
}
|
|
3160
|
+
);
|
|
3161
|
+
}
|
|
3162
|
+
var OP_LABEL = {
|
|
3163
|
+
"=": "=",
|
|
3164
|
+
"!=": "\u2260",
|
|
3165
|
+
">": ">",
|
|
3166
|
+
">=": "\u2265",
|
|
3167
|
+
"<": "<",
|
|
3168
|
+
"<=": "\u2264",
|
|
3169
|
+
contains: "contains",
|
|
3170
|
+
starts_with: "starts with",
|
|
3171
|
+
ends_with: "ends with",
|
|
3172
|
+
matches_regex: "matches",
|
|
3173
|
+
not_matches_regex: "not matches",
|
|
3174
|
+
includes: "includes",
|
|
3175
|
+
is_null: "is null",
|
|
3176
|
+
is_not_null: "is not null"
|
|
3177
|
+
};
|
|
3178
|
+
function choiceLabel(op, negated) {
|
|
3179
|
+
return negated ? `not ${OP_LABEL[op]}` : OP_LABEL[op];
|
|
3180
|
+
}
|
|
3181
|
+
function OpPicker({
|
|
3182
|
+
field,
|
|
3183
|
+
clause,
|
|
3184
|
+
disabled,
|
|
3185
|
+
classNames,
|
|
3186
|
+
onPick
|
|
3187
|
+
}) {
|
|
3188
|
+
const [open, setOpen] = useState6(false);
|
|
3189
|
+
const wrapRef = useRef3(null);
|
|
3190
|
+
useEffect5(() => {
|
|
3191
|
+
if (!open) return;
|
|
3192
|
+
function onDocClick(e) {
|
|
3193
|
+
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
3194
|
+
}
|
|
3195
|
+
function onKey(e) {
|
|
3196
|
+
if (e.key === "Escape") setOpen(false);
|
|
3197
|
+
}
|
|
3198
|
+
const t = setTimeout(() => window.addEventListener("click", onDocClick), 0);
|
|
3199
|
+
window.addEventListener("keydown", onKey);
|
|
3200
|
+
return () => {
|
|
3201
|
+
clearTimeout(t);
|
|
3202
|
+
window.removeEventListener("click", onDocClick);
|
|
3203
|
+
window.removeEventListener("keydown", onKey);
|
|
3204
|
+
};
|
|
3205
|
+
}, [open]);
|
|
3206
|
+
const pairs = useMemo4(() => field ? opPairsForField2(field) : [], [field]);
|
|
3207
|
+
const isActive = (choice) => choice.op === clause.op && Boolean(choice.negated) === Boolean(clause.negated);
|
|
3208
|
+
return /* @__PURE__ */ jsxs7("span", { className: "qt-op-picker", ref: wrapRef, children: [
|
|
3209
|
+
/* @__PURE__ */ jsxs7(
|
|
3210
|
+
"button",
|
|
3211
|
+
{
|
|
3212
|
+
type: "button",
|
|
3213
|
+
className: cx3("qt-chip-op-btn", "qt-op-trigger", classNames?.select),
|
|
3214
|
+
disabled,
|
|
3215
|
+
"aria-haspopup": "dialog",
|
|
3216
|
+
"aria-expanded": open,
|
|
3217
|
+
onClick: () => setOpen((o) => !o),
|
|
3218
|
+
children: [
|
|
3219
|
+
choiceLabel(clause.op, Boolean(clause.negated)),
|
|
3220
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-op-caret", "aria-hidden": true, children: "\u25BE" })
|
|
3221
|
+
]
|
|
3222
|
+
}
|
|
3223
|
+
),
|
|
3224
|
+
open && pairs.length > 0 && /* @__PURE__ */ jsxs7("span", { className: "qt-op-pop", role: "dialog", "aria-label": "Choose condition", children: [
|
|
3225
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-cm-col-head", children: "keep" }),
|
|
3226
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-cm-col-head qt-cm-col-head--neg", children: "exclude" }),
|
|
3227
|
+
pairs.map((pair, i) => /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
3228
|
+
/* @__PURE__ */ jsx7(
|
|
3229
|
+
"button",
|
|
2183
3230
|
{
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
3231
|
+
type: "button",
|
|
3232
|
+
className: cx3("qt-op-cell", isActive(pair.keep) && "qt-op-cell--on"),
|
|
3233
|
+
onClick: () => {
|
|
3234
|
+
onPick(pair.keep);
|
|
3235
|
+
setOpen(false);
|
|
3236
|
+
},
|
|
3237
|
+
children: choiceLabel(pair.keep.op, pair.keep.negated)
|
|
2189
3238
|
}
|
|
2190
3239
|
),
|
|
2191
|
-
/* @__PURE__ */
|
|
3240
|
+
/* @__PURE__ */ jsx7(
|
|
2192
3241
|
"button",
|
|
2193
3242
|
{
|
|
2194
3243
|
type: "button",
|
|
2195
|
-
className: "qt-
|
|
2196
|
-
onClick: () =>
|
|
2197
|
-
|
|
2198
|
-
|
|
3244
|
+
className: cx3("qt-op-cell", "qt-op-cell--neg", isActive(pair.exclude) && "qt-op-cell--on"),
|
|
3245
|
+
onClick: () => {
|
|
3246
|
+
onPick(pair.exclude);
|
|
3247
|
+
setOpen(false);
|
|
3248
|
+
},
|
|
3249
|
+
children: choiceLabel(pair.exclude.op, pair.exclude.negated)
|
|
2199
3250
|
}
|
|
2200
3251
|
)
|
|
2201
|
-
] },
|
|
2202
|
-
|
|
3252
|
+
] }, i))
|
|
3253
|
+
] })
|
|
3254
|
+
] });
|
|
3255
|
+
}
|
|
3256
|
+
function PredicateEditor({
|
|
3257
|
+
api,
|
|
3258
|
+
field,
|
|
3259
|
+
clause,
|
|
3260
|
+
classNames,
|
|
3261
|
+
disabled,
|
|
3262
|
+
onChange,
|
|
3263
|
+
onRemove
|
|
3264
|
+
}) {
|
|
3265
|
+
const needsValue = !NULLARY_OPS2.has(clause.op);
|
|
3266
|
+
const isRegex = clause.op === "matches_regex" || clause.op === "not_matches_regex";
|
|
3267
|
+
function pickOp(choice) {
|
|
3268
|
+
const next = { field: clause.field, op: choice.op, value: clause.value };
|
|
3269
|
+
if (choice.negated) next.negated = true;
|
|
3270
|
+
onChange(next);
|
|
3271
|
+
}
|
|
3272
|
+
return /* @__PURE__ */ jsxs7("span", { className: "qt-pred", children: [
|
|
3273
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-chip-field", children: field?.label ?? clause.field }),
|
|
3274
|
+
/* @__PURE__ */ jsx7(OpPicker, { field, clause, disabled, classNames, onPick: pickOp }),
|
|
3275
|
+
needsValue && /* @__PURE__ */ jsx7(
|
|
3276
|
+
ValueInput,
|
|
3277
|
+
{
|
|
3278
|
+
api,
|
|
3279
|
+
field,
|
|
3280
|
+
value: clause.value,
|
|
3281
|
+
forceFreeform: isRegex,
|
|
3282
|
+
placeholder: isRegex ? "regex" : "value",
|
|
3283
|
+
classNames,
|
|
3284
|
+
onChange: (v) => onChange({ ...clause, value: v })
|
|
3285
|
+
}
|
|
3286
|
+
),
|
|
3287
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-chip-x", onClick: onRemove, disabled, children: "\u2715" })
|
|
2203
3288
|
] });
|
|
2204
3289
|
}
|
|
2205
3290
|
function ValueInput({
|
|
2206
3291
|
api,
|
|
2207
3292
|
field,
|
|
2208
3293
|
value,
|
|
3294
|
+
forceFreeform,
|
|
3295
|
+
placeholder = "value",
|
|
2209
3296
|
classNames,
|
|
2210
3297
|
onChange
|
|
2211
3298
|
}) {
|
|
3299
|
+
const presentation = useFilterValuePresentation();
|
|
2212
3300
|
const strategy = field ? filterValuesFor(field) : { source: "freeform" };
|
|
2213
|
-
if (strategy.source === "static") {
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
{
|
|
2219
|
-
|
|
2220
|
-
value,
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
/* @__PURE__ */ jsx5("option", { value: "", children: "\u2014" }),
|
|
2225
|
-
strategy.options.map((o) => /* @__PURE__ */ jsx5("option", { value: o, children: o }, o))
|
|
2226
|
-
]
|
|
2227
|
-
}
|
|
2228
|
-
);
|
|
3301
|
+
if (!forceFreeform && strategy.source === "static") {
|
|
3302
|
+
if (presentation.render || presentation.label || strategy.options.some((o) => typeof o !== "string")) {
|
|
3303
|
+
return /* @__PURE__ */ jsx7(PresentedValueInput, { field: field?.name ?? "", value, options: strategy.options, onChange });
|
|
3304
|
+
}
|
|
3305
|
+
return /* @__PURE__ */ jsxs7("select", { className: cx3("qt-chip-val", classNames?.select), value, onChange: (e) => onChange(e.target.value), children: [
|
|
3306
|
+
/* @__PURE__ */ jsx7("option", { value: "", children: "\u2014" }),
|
|
3307
|
+
strategy.options.map((o) => {
|
|
3308
|
+
const option = typeof o === "string" ? { value: o, label: o } : o;
|
|
3309
|
+
return /* @__PURE__ */ jsx7("option", { value: option.value, children: option.label }, option.value);
|
|
3310
|
+
})
|
|
3311
|
+
] });
|
|
2229
3312
|
}
|
|
2230
|
-
if (strategy.source === "freeform" || !field) {
|
|
2231
|
-
const widthChars = chipFieldWidthForInput(value ||
|
|
2232
|
-
return /* @__PURE__ */
|
|
3313
|
+
if (forceFreeform || strategy.source === "freeform" || !field) {
|
|
3314
|
+
const widthChars = chipFieldWidthForInput(value || placeholder, field?.type, 5, 4);
|
|
3315
|
+
return /* @__PURE__ */ jsx7(
|
|
2233
3316
|
"input",
|
|
2234
3317
|
{
|
|
2235
3318
|
className: cx3("qt-chip-val", classNames?.input),
|
|
2236
3319
|
value,
|
|
2237
|
-
placeholder
|
|
3320
|
+
placeholder,
|
|
2238
3321
|
size: widthChars,
|
|
2239
3322
|
style: { width: `${widthChars}ch` },
|
|
2240
3323
|
onChange: (e) => onChange(e.target.value)
|
|
2241
3324
|
}
|
|
2242
3325
|
);
|
|
2243
3326
|
}
|
|
2244
|
-
return /* @__PURE__ */
|
|
3327
|
+
return /* @__PURE__ */ jsx7(AutocompleteInput, { api, field, value, classNames, onChange });
|
|
2245
3328
|
}
|
|
2246
3329
|
function AutocompleteInput({
|
|
2247
3330
|
api,
|
|
@@ -2250,9 +3333,9 @@ function AutocompleteInput({
|
|
|
2250
3333
|
classNames,
|
|
2251
3334
|
onChange
|
|
2252
3335
|
}) {
|
|
2253
|
-
const [result, setResult] =
|
|
2254
|
-
const listId =
|
|
2255
|
-
|
|
3336
|
+
const [result, setResult] = useState6({ values: [], hasMore: false });
|
|
3337
|
+
const listId = useId2();
|
|
3338
|
+
useEffect5(() => {
|
|
2256
3339
|
let cancelled = false;
|
|
2257
3340
|
const t = setTimeout(() => {
|
|
2258
3341
|
void api.filterValues(field.name, value).then((r) => {
|
|
@@ -2266,8 +3349,8 @@ function AutocompleteInput({
|
|
|
2266
3349
|
clearTimeout(t);
|
|
2267
3350
|
};
|
|
2268
3351
|
}, [api, field.name, value]);
|
|
2269
|
-
return /* @__PURE__ */
|
|
2270
|
-
/* @__PURE__ */
|
|
3352
|
+
return /* @__PURE__ */ jsxs7("span", { className: "qt-autocomplete", children: [
|
|
3353
|
+
/* @__PURE__ */ jsx7(
|
|
2271
3354
|
"input",
|
|
2272
3355
|
{
|
|
2273
3356
|
className: cx3("qt-chip-val", classNames?.input),
|
|
@@ -2279,8 +3362,8 @@ function AutocompleteInput({
|
|
|
2279
3362
|
onChange: (e) => onChange(e.target.value)
|
|
2280
3363
|
}
|
|
2281
3364
|
),
|
|
2282
|
-
/* @__PURE__ */
|
|
2283
|
-
result.hasMore && /* @__PURE__ */
|
|
3365
|
+
/* @__PURE__ */ jsx7("datalist", { id: listId, children: result.values.map((v) => /* @__PURE__ */ jsx7("option", { value: v }, v)) }),
|
|
3366
|
+
result.hasMore && /* @__PURE__ */ jsx7("span", { className: "qt-autocomplete-hint", children: "keep typing to refine\u2026" })
|
|
2284
3367
|
] });
|
|
2285
3368
|
}
|
|
2286
3369
|
function OrderTermChip({
|
|
@@ -2297,6 +3380,7 @@ function OrderTermChip({
|
|
|
2297
3380
|
onDragEnd,
|
|
2298
3381
|
disabled,
|
|
2299
3382
|
updateTerm,
|
|
3383
|
+
clearExtract,
|
|
2300
3384
|
removeTerm,
|
|
2301
3385
|
position,
|
|
2302
3386
|
count,
|
|
@@ -2305,7 +3389,7 @@ function OrderTermChip({
|
|
|
2305
3389
|
label
|
|
2306
3390
|
}) {
|
|
2307
3391
|
const fieldHasNull = useFieldHasNull(api, field?.name);
|
|
2308
|
-
return /* @__PURE__ */
|
|
3392
|
+
return /* @__PURE__ */ jsxs7(
|
|
2309
3393
|
"span",
|
|
2310
3394
|
{
|
|
2311
3395
|
className: cx3("qt-chip", "qt-chip--col", "qt-chip--sort", isDragSource && "qt-chip--dragging", classNames?.chip),
|
|
@@ -2323,10 +3407,10 @@ function OrderTermChip({
|
|
|
2323
3407
|
onDragEnd,
|
|
2324
3408
|
title: "drag to reorder sort priority",
|
|
2325
3409
|
children: [
|
|
2326
|
-
/* @__PURE__ */
|
|
2327
|
-
showPriority && /* @__PURE__ */
|
|
2328
|
-
/* @__PURE__ */
|
|
2329
|
-
/* @__PURE__ */
|
|
3410
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: "qt-chip-grip", children: "\u22EE\u22EE" }),
|
|
3411
|
+
showPriority && /* @__PURE__ */ jsx7("span", { className: "qt-chip-priority", children: priority }),
|
|
3412
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-chip-field", children: label }),
|
|
3413
|
+
/* @__PURE__ */ jsx7(
|
|
2330
3414
|
"button",
|
|
2331
3415
|
{
|
|
2332
3416
|
type: "button",
|
|
@@ -2336,7 +3420,45 @@ function OrderTermChip({
|
|
|
2336
3420
|
children: term.dir === "asc" ? "\u2191 asc" : "\u2193 desc"
|
|
2337
3421
|
}
|
|
2338
3422
|
),
|
|
2339
|
-
|
|
3423
|
+
term.extract ? /* @__PURE__ */ jsxs7("span", { className: "qt-sort-extract", children: [
|
|
3424
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-chip-and", children: "extract" }),
|
|
3425
|
+
/* @__PURE__ */ jsx7(
|
|
3426
|
+
"input",
|
|
3427
|
+
{
|
|
3428
|
+
className: cx3("qt-chip-val", classNames?.input),
|
|
3429
|
+
value: term.extract.regex,
|
|
3430
|
+
placeholder: "regex",
|
|
3431
|
+
"aria-label": `Regex extract for ${label}`,
|
|
3432
|
+
size: chipFieldWidthForInput(term.extract.regex || "regex", "text", 5, 4),
|
|
3433
|
+
style: { width: `${chipFieldWidthForInput(term.extract.regex || "regex", "text", 5, 4)}ch` },
|
|
3434
|
+
disabled,
|
|
3435
|
+
onChange: (e) => updateTerm(dataIndex, { extract: { regex: e.target.value } })
|
|
3436
|
+
}
|
|
3437
|
+
),
|
|
3438
|
+
/* @__PURE__ */ jsx7(
|
|
3439
|
+
"button",
|
|
3440
|
+
{
|
|
3441
|
+
type: "button",
|
|
3442
|
+
className: "qt-chip-x",
|
|
3443
|
+
title: "Remove regex extraction",
|
|
3444
|
+
"aria-label": `Remove regex extract for ${label}`,
|
|
3445
|
+
disabled,
|
|
3446
|
+
onClick: () => clearExtract(dataIndex),
|
|
3447
|
+
children: "\u2715"
|
|
3448
|
+
}
|
|
3449
|
+
)
|
|
3450
|
+
] }) : /* @__PURE__ */ jsx7(
|
|
3451
|
+
"button",
|
|
3452
|
+
{
|
|
3453
|
+
type: "button",
|
|
3454
|
+
className: "qt-chip-op-btn",
|
|
3455
|
+
title: "Sort by text extracted with a regular expression",
|
|
3456
|
+
disabled,
|
|
3457
|
+
onClick: () => updateTerm(dataIndex, { extract: { regex: "" } }),
|
|
3458
|
+
children: "+ regex extract"
|
|
3459
|
+
}
|
|
3460
|
+
),
|
|
3461
|
+
(term.extract != null || fieldHasNull !== false) && /* @__PURE__ */ jsxs7(
|
|
2340
3462
|
"button",
|
|
2341
3463
|
{
|
|
2342
3464
|
type: "button",
|
|
@@ -2350,7 +3472,7 @@ function OrderTermChip({
|
|
|
2350
3472
|
]
|
|
2351
3473
|
}
|
|
2352
3474
|
),
|
|
2353
|
-
/* @__PURE__ */
|
|
3475
|
+
/* @__PURE__ */ jsx7(
|
|
2354
3476
|
MoveButtons,
|
|
2355
3477
|
{
|
|
2356
3478
|
label,
|
|
@@ -2360,7 +3482,7 @@ function OrderTermChip({
|
|
|
2360
3482
|
onMove: moveTerm
|
|
2361
3483
|
}
|
|
2362
3484
|
),
|
|
2363
|
-
/* @__PURE__ */
|
|
3485
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-chip-x", onClick: () => removeTerm(dataIndex), disabled, children: "\u2715" })
|
|
2364
3486
|
]
|
|
2365
3487
|
}
|
|
2366
3488
|
);
|
|
@@ -2372,15 +3494,24 @@ function OrderRow({
|
|
|
2372
3494
|
disabled
|
|
2373
3495
|
}) {
|
|
2374
3496
|
const orderBy = api.query.orderBy;
|
|
2375
|
-
const byName =
|
|
2376
|
-
const [drag, setDrag] =
|
|
2377
|
-
const [adding, setAdding] =
|
|
3497
|
+
const byName = useMemo4(() => new Map(fields.map((f) => [f.name, f])), [fields]);
|
|
3498
|
+
const [drag, setDrag] = useState6(null);
|
|
3499
|
+
const [adding, setAdding] = useState6(false);
|
|
2378
3500
|
function setOrderBy(next) {
|
|
2379
3501
|
api.setSort(next);
|
|
2380
3502
|
}
|
|
2381
3503
|
function updateTerm(i, patch) {
|
|
2382
3504
|
setOrderBy(orderBy.map((o, k) => k === i ? { ...o, ...patch } : o));
|
|
2383
3505
|
}
|
|
3506
|
+
function clearExtract(i) {
|
|
3507
|
+
setOrderBy(
|
|
3508
|
+
orderBy.map((term, index) => {
|
|
3509
|
+
if (index !== i) return term;
|
|
3510
|
+
const { extract: _extract, ...plainTerm } = term;
|
|
3511
|
+
return plainTerm;
|
|
3512
|
+
})
|
|
3513
|
+
);
|
|
3514
|
+
}
|
|
2384
3515
|
function removeTerm(i) {
|
|
2385
3516
|
setOrderBy(orderBy.filter((_, k) => k !== i));
|
|
2386
3517
|
}
|
|
@@ -2406,7 +3537,7 @@ function OrderRow({
|
|
|
2406
3537
|
setAdding(false);
|
|
2407
3538
|
}
|
|
2408
3539
|
const termFields = orderBy.map((o) => o.field);
|
|
2409
|
-
const termByField =
|
|
3540
|
+
const termByField = useMemo4(() => new Map(orderBy.map((o) => [o.field, o])), [orderBy]);
|
|
2410
3541
|
function previewFields() {
|
|
2411
3542
|
if (!drag) return termFields;
|
|
2412
3543
|
const without = termFields.filter((f) => f !== drag.source);
|
|
@@ -2436,14 +3567,14 @@ function OrderRow({
|
|
|
2436
3567
|
const sortable = fields.filter(isSortable2);
|
|
2437
3568
|
const isDefaultOrderBy = orderBy.length === api.defaults.orderBy.length && orderBy.every((term, i) => {
|
|
2438
3569
|
const next = api.defaults.orderBy[i];
|
|
2439
|
-
return term.field === next?.field && term.dir === next?.dir && (term.nulls ?? "last") === (next?.nulls ?? "last");
|
|
3570
|
+
return term.field === next?.field && term.dir === next?.dir && (term.nulls ?? "last") === (next?.nulls ?? "last") && (term.extract?.regex ?? null) === (next?.extract?.regex ?? null);
|
|
2440
3571
|
});
|
|
2441
|
-
return /* @__PURE__ */
|
|
2442
|
-
/* @__PURE__ */
|
|
2443
|
-
orderBy.length === 0 && !adding && /* @__PURE__ */
|
|
3572
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-qb-row", children: [
|
|
3573
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-kw", children: "order by" }),
|
|
3574
|
+
orderBy.length === 0 && !adding && /* @__PURE__ */ jsx7("span", { className: "qt-qb-hint", children: "(default)" }),
|
|
2444
3575
|
rendered.map((f, pos) => {
|
|
2445
3576
|
const term = termByField.get(f);
|
|
2446
|
-
return /* @__PURE__ */
|
|
3577
|
+
return /* @__PURE__ */ jsx7(
|
|
2447
3578
|
OrderTermChip,
|
|
2448
3579
|
{
|
|
2449
3580
|
api,
|
|
@@ -2459,6 +3590,7 @@ function OrderRow({
|
|
|
2459
3590
|
onDragEnd: () => setDrag(null),
|
|
2460
3591
|
disabled,
|
|
2461
3592
|
updateTerm,
|
|
3593
|
+
clearExtract,
|
|
2462
3594
|
removeTerm,
|
|
2463
3595
|
position: pos,
|
|
2464
3596
|
count: rendered.length,
|
|
@@ -2476,8 +3608,8 @@ function OrderRow({
|
|
|
2476
3608
|
f
|
|
2477
3609
|
);
|
|
2478
3610
|
}),
|
|
2479
|
-
adding ? /* @__PURE__ */
|
|
2480
|
-
!isDefaultOrderBy && /* @__PURE__ */
|
|
3611
|
+
adding ? /* @__PURE__ */ jsx7(FieldPicker, { fields: sortable, onPick: addTerm, onClose: () => setAdding(false) }) : /* @__PURE__ */ jsx7("button", { type: "button", className: "qt-add", onClick: () => setAdding(true), disabled, children: "+ add sort" }),
|
|
3612
|
+
!isDefaultOrderBy && /* @__PURE__ */ jsx7("button", { type: "button", className: "qt-link-btn", onClick: () => api.setSort(api.defaults.orderBy), disabled, children: "reset" })
|
|
2481
3613
|
] });
|
|
2482
3614
|
}
|
|
2483
3615
|
function WindowRow({
|
|
@@ -2486,10 +3618,10 @@ function WindowRow({
|
|
|
2486
3618
|
disabled
|
|
2487
3619
|
}) {
|
|
2488
3620
|
const { query } = api;
|
|
2489
|
-
const [limitDraft, setLimitDraft] =
|
|
2490
|
-
const [offsetDraft, setOffsetDraft] =
|
|
2491
|
-
|
|
2492
|
-
|
|
3621
|
+
const [limitDraft, setLimitDraft] = useState6(String(query.limit));
|
|
3622
|
+
const [offsetDraft, setOffsetDraft] = useState6(String(query.offset));
|
|
3623
|
+
useEffect5(() => setLimitDraft(String(query.limit)), [query.limit]);
|
|
3624
|
+
useEffect5(() => setOffsetDraft(String(query.offset)), [query.offset]);
|
|
2493
3625
|
const hasLimitDefault = query.limit === api.defaults.limit;
|
|
2494
3626
|
const hasOffsetDefault = query.offset === api.defaults.offset;
|
|
2495
3627
|
const hasWindowDefault = hasLimitDefault && hasOffsetDefault;
|
|
@@ -2499,10 +3631,10 @@ function WindowRow({
|
|
|
2499
3631
|
function commitOffset() {
|
|
2500
3632
|
api.setOffset(Math.max(0, Number(offsetDraft) || 0));
|
|
2501
3633
|
}
|
|
2502
|
-
return /* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2505
|
-
/* @__PURE__ */
|
|
3634
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-qb-row qt-qb-pagination", children: [
|
|
3635
|
+
/* @__PURE__ */ jsxs7("label", { className: "qt-qb-window-field", children: [
|
|
3636
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-kw", children: "limit" }),
|
|
3637
|
+
/* @__PURE__ */ jsx7(
|
|
2506
3638
|
"input",
|
|
2507
3639
|
{
|
|
2508
3640
|
className: cx3("qt-qb-num", classNames?.input),
|
|
@@ -2516,9 +3648,9 @@ function WindowRow({
|
|
|
2516
3648
|
}
|
|
2517
3649
|
)
|
|
2518
3650
|
] }),
|
|
2519
|
-
/* @__PURE__ */
|
|
2520
|
-
/* @__PURE__ */
|
|
2521
|
-
/* @__PURE__ */
|
|
3651
|
+
/* @__PURE__ */ jsxs7("label", { className: "qt-qb-window-field", children: [
|
|
3652
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-kw", children: "offset" }),
|
|
3653
|
+
/* @__PURE__ */ jsx7(
|
|
2522
3654
|
"input",
|
|
2523
3655
|
{
|
|
2524
3656
|
className: cx3("qt-qb-num", classNames?.input),
|
|
@@ -2532,7 +3664,7 @@ function WindowRow({
|
|
|
2532
3664
|
}
|
|
2533
3665
|
)
|
|
2534
3666
|
] }),
|
|
2535
|
-
!hasWindowDefault && /* @__PURE__ */
|
|
3667
|
+
!hasWindowDefault && /* @__PURE__ */ jsx7(
|
|
2536
3668
|
"button",
|
|
2537
3669
|
{
|
|
2538
3670
|
type: "button",
|
|
@@ -2565,11 +3697,11 @@ function MetricsRow({
|
|
|
2565
3697
|
}) {
|
|
2566
3698
|
const { aggregations } = api;
|
|
2567
3699
|
const clauses = aggregations.clauses;
|
|
2568
|
-
const measurable =
|
|
2569
|
-
const groupable =
|
|
2570
|
-
const [drag, setDrag] =
|
|
3700
|
+
const measurable = useMemo4(() => fields.filter(isMeasurable), [fields]);
|
|
3701
|
+
const groupable = useMemo4(() => fields.filter(isGroupable), [fields]);
|
|
3702
|
+
const [drag, setDrag] = useState6(null);
|
|
2571
3703
|
const ids = clauses.map((c) => c.id);
|
|
2572
|
-
const byId =
|
|
3704
|
+
const byId = useMemo4(() => new Map(clauses.map((c) => [c.id, c])), [clauses]);
|
|
2573
3705
|
function previewIds() {
|
|
2574
3706
|
if (!drag) return ids;
|
|
2575
3707
|
const without = ids.filter((i) => i !== drag.source);
|
|
@@ -2595,12 +3727,12 @@ function MetricsRow({
|
|
|
2595
3727
|
setDrag(null);
|
|
2596
3728
|
}
|
|
2597
3729
|
const rendered = previewIds();
|
|
2598
|
-
return /* @__PURE__ */
|
|
2599
|
-
/* @__PURE__ */
|
|
2600
|
-
clauses.length === 0 && /* @__PURE__ */
|
|
3730
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-qb-row qt-qb-row--metrics", children: [
|
|
3731
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-qb-kw", children: "metrics" }),
|
|
3732
|
+
clauses.length === 0 && /* @__PURE__ */ jsx7("span", { className: "qt-qb-hint", children: "none" }),
|
|
2601
3733
|
rendered.map((id, position) => {
|
|
2602
3734
|
const clause = byId.get(id);
|
|
2603
|
-
return /* @__PURE__ */
|
|
3735
|
+
return /* @__PURE__ */ jsx7(
|
|
2604
3736
|
MetricChip,
|
|
2605
3737
|
{
|
|
2606
3738
|
clause,
|
|
@@ -2625,8 +3757,8 @@ function MetricsRow({
|
|
|
2625
3757
|
id
|
|
2626
3758
|
);
|
|
2627
3759
|
}),
|
|
2628
|
-
/* @__PURE__ */
|
|
2629
|
-
clauses.length > 0 && /* @__PURE__ */
|
|
3760
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-add", onClick: () => aggregations.add(), disabled, children: "+ add metric" }),
|
|
3761
|
+
clauses.length > 0 && /* @__PURE__ */ jsx7("button", { type: "button", className: "qt-link-btn", onClick: aggregations.clear, disabled, title: "Remove all metrics", children: "reset" })
|
|
2630
3762
|
] });
|
|
2631
3763
|
}
|
|
2632
3764
|
function changeMetricOp(aggregations, clause, measurable, op) {
|
|
@@ -2655,11 +3787,11 @@ function MetricChip({
|
|
|
2655
3787
|
count,
|
|
2656
3788
|
onMove
|
|
2657
3789
|
}) {
|
|
2658
|
-
const [addingGroup, setAddingGroup] =
|
|
2659
|
-
const byName =
|
|
3790
|
+
const [addingGroup, setAddingGroup] = useState6(false);
|
|
3791
|
+
const byName = useMemo4(() => new Map(groupable.map((f) => [f.name, f])), [groupable]);
|
|
2660
3792
|
const needsField = aggOpNeedsField(clause.op);
|
|
2661
3793
|
const measureOptions = measurable.filter((f) => aggOpsForField(f).includes(clause.op));
|
|
2662
|
-
return /* @__PURE__ */
|
|
3794
|
+
return /* @__PURE__ */ jsxs7(
|
|
2663
3795
|
"span",
|
|
2664
3796
|
{
|
|
2665
3797
|
className: cx3("qt-chip", "qt-chip--agg", isDragSource && "qt-chip--dragging", classNames?.chip),
|
|
@@ -2677,18 +3809,18 @@ function MetricChip({
|
|
|
2677
3809
|
onDragEnd,
|
|
2678
3810
|
title: "drag to reorder metrics",
|
|
2679
3811
|
children: [
|
|
2680
|
-
/* @__PURE__ */
|
|
2681
|
-
/* @__PURE__ */
|
|
3812
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: "qt-chip-grip", children: "\u22EE\u22EE" }),
|
|
3813
|
+
/* @__PURE__ */ jsx7(
|
|
2682
3814
|
"select",
|
|
2683
3815
|
{
|
|
2684
3816
|
className: cx3("qt-chip-op", classNames?.select),
|
|
2685
3817
|
value: clause.op,
|
|
2686
3818
|
disabled,
|
|
2687
3819
|
onChange: (e) => onChangeOp(e.target.value),
|
|
2688
|
-
children: AGG_OPS.map((op) => /* @__PURE__ */
|
|
3820
|
+
children: AGG_OPS.map((op) => /* @__PURE__ */ jsx7("option", { value: op, children: AGG_OP_LABELS[op] }, op))
|
|
2689
3821
|
}
|
|
2690
3822
|
),
|
|
2691
|
-
/* @__PURE__ */
|
|
3823
|
+
/* @__PURE__ */ jsxs7(
|
|
2692
3824
|
"select",
|
|
2693
3825
|
{
|
|
2694
3826
|
className: cx3("qt-chip-val", classNames?.select),
|
|
@@ -2696,18 +3828,18 @@ function MetricChip({
|
|
|
2696
3828
|
disabled,
|
|
2697
3829
|
onChange: (e) => onChangeField(e.target.value || void 0),
|
|
2698
3830
|
children: [
|
|
2699
|
-
!needsField && /* @__PURE__ */
|
|
2700
|
-
needsField && clause.field == null && /* @__PURE__ */
|
|
2701
|
-
measureOptions.map((f) => /* @__PURE__ */
|
|
3831
|
+
!needsField && /* @__PURE__ */ jsx7("option", { value: "", children: "(all rows)" }),
|
|
3832
|
+
needsField && clause.field == null && /* @__PURE__ */ jsx7("option", { value: "", children: "measure\u2026" }),
|
|
3833
|
+
measureOptions.map((f) => /* @__PURE__ */ jsx7("option", { value: f.name, children: f.label }, f.name))
|
|
2702
3834
|
]
|
|
2703
3835
|
}
|
|
2704
3836
|
),
|
|
2705
|
-
/* @__PURE__ */
|
|
2706
|
-
clause.groupBy.map((g) => /* @__PURE__ */
|
|
2707
|
-
/* @__PURE__ */
|
|
2708
|
-
/* @__PURE__ */
|
|
3837
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-chip-agg-by", children: [
|
|
3838
|
+
clause.groupBy.map((g) => /* @__PURE__ */ jsxs7("span", { className: "qt-chip-agg-group", children: [
|
|
3839
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-chip-agg-group-label", children: byName.get(g)?.label ?? g }),
|
|
3840
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-chip-x", onClick: () => onRemoveGroup(g), disabled, children: "\u2715" })
|
|
2709
3841
|
] }, g)),
|
|
2710
|
-
addingGroup ? /* @__PURE__ */
|
|
3842
|
+
addingGroup ? /* @__PURE__ */ jsx7(
|
|
2711
3843
|
FieldPicker,
|
|
2712
3844
|
{
|
|
2713
3845
|
fields: groupable,
|
|
@@ -2718,7 +3850,7 @@ function MetricChip({
|
|
|
2718
3850
|
},
|
|
2719
3851
|
onClose: () => setAddingGroup(false)
|
|
2720
3852
|
}
|
|
2721
|
-
) : /* @__PURE__ */
|
|
3853
|
+
) : /* @__PURE__ */ jsx7(
|
|
2722
3854
|
"button",
|
|
2723
3855
|
{
|
|
2724
3856
|
type: "button",
|
|
@@ -2730,7 +3862,7 @@ function MetricChip({
|
|
|
2730
3862
|
}
|
|
2731
3863
|
)
|
|
2732
3864
|
] }),
|
|
2733
|
-
/* @__PURE__ */
|
|
3865
|
+
/* @__PURE__ */ jsx7(
|
|
2734
3866
|
MoveButtons,
|
|
2735
3867
|
{
|
|
2736
3868
|
label: clause.label ?? `${AGG_OP_LABELS[clause.op]} metric`,
|
|
@@ -2740,16 +3872,16 @@ function MetricChip({
|
|
|
2740
3872
|
onMove
|
|
2741
3873
|
}
|
|
2742
3874
|
),
|
|
2743
|
-
/* @__PURE__ */
|
|
3875
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "qt-chip-x", onClick: onRemove, disabled, title: "Remove metric", children: "\u2715" })
|
|
2744
3876
|
]
|
|
2745
3877
|
}
|
|
2746
3878
|
);
|
|
2747
3879
|
}
|
|
2748
3880
|
|
|
2749
3881
|
// src/MetricsPanel.tsx
|
|
2750
|
-
import { useMemo as
|
|
3882
|
+
import { useMemo as useMemo5 } from "react";
|
|
2751
3883
|
import { EMPTY_QUERY } from "@pythia-software/query-table-core";
|
|
2752
|
-
import { jsx as
|
|
3884
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2753
3885
|
var cx4 = (...parts) => parts.filter((p) => Boolean(p)).join(" ");
|
|
2754
3886
|
var OP_LABELS = {
|
|
2755
3887
|
count: "Count",
|
|
@@ -2767,18 +3899,18 @@ function MetricsPanel({
|
|
|
2767
3899
|
emptyMessage,
|
|
2768
3900
|
classNames
|
|
2769
3901
|
}) {
|
|
2770
|
-
const byName =
|
|
3902
|
+
const byName = useMemo5(() => new Map(fields.map((f) => [f.name, f])), [fields]);
|
|
2771
3903
|
const { clauses, results, loading, error } = aggregations;
|
|
2772
3904
|
if (clauses.length === 0) {
|
|
2773
|
-
return emptyMessage ? /* @__PURE__ */
|
|
3905
|
+
return emptyMessage ? /* @__PURE__ */ jsx8("div", { className: cx4("qt-metrics", classNames?.root), children: emptyMessage }) : null;
|
|
2774
3906
|
}
|
|
2775
3907
|
const resultById = new Map((results?.metrics ?? []).map((m) => [m.id, m.buckets]));
|
|
2776
|
-
return /* @__PURE__ */
|
|
2777
|
-
error ? /* @__PURE__ */
|
|
3908
|
+
return /* @__PURE__ */ jsxs8("div", { className: cx4("qt-metrics", classNames?.root), children: [
|
|
3909
|
+
error ? /* @__PURE__ */ jsxs8("div", { className: "qt-metrics-error", children: [
|
|
2778
3910
|
"Metrics failed: ",
|
|
2779
3911
|
error.message
|
|
2780
3912
|
] }) : null,
|
|
2781
|
-
clauses.map((clause) => /* @__PURE__ */
|
|
3913
|
+
clauses.map((clause) => /* @__PURE__ */ jsx8(
|
|
2782
3914
|
MetricCard,
|
|
2783
3915
|
{
|
|
2784
3916
|
clause,
|
|
@@ -2802,23 +3934,23 @@ function MetricCard({
|
|
|
2802
3934
|
}) {
|
|
2803
3935
|
const measure = clause.field ? byName.get(clause.field) : void 0;
|
|
2804
3936
|
const groupLabels = clause.groupBy.map((g) => byName.get(g)?.label ?? g);
|
|
2805
|
-
const decimals =
|
|
3937
|
+
const decimals = useMemo5(() => cardDecimals(buckets), [buckets]);
|
|
2806
3938
|
const fmt = (v) => formatMetricValue(v, clause.op, measure, renderers, decimals);
|
|
2807
|
-
return /* @__PURE__ */
|
|
2808
|
-
/* @__PURE__ */
|
|
2809
|
-
/* @__PURE__ */
|
|
2810
|
-
groupLabels.length > 0 && /* @__PURE__ */
|
|
3939
|
+
return /* @__PURE__ */ jsxs8("div", { className: cx4("qt-metric", `qt-metric--gb${Math.min(clause.groupBy.length, 3)}`, className), children: [
|
|
3940
|
+
/* @__PURE__ */ jsxs8("div", { className: "qt-metric-head", children: [
|
|
3941
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-metric-title", children: metricTitle(clause, measure) }),
|
|
3942
|
+
groupLabels.length > 0 && /* @__PURE__ */ jsxs8("span", { className: "qt-metric-by", children: [
|
|
2811
3943
|
"by ",
|
|
2812
3944
|
groupLabels.join(" \xD7 ")
|
|
2813
3945
|
] })
|
|
2814
3946
|
] }),
|
|
2815
|
-
loading && buckets.length === 0 ? /* @__PURE__ */
|
|
3947
|
+
loading && buckets.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "qt-metric-loading", children: "\u2026" }) : buckets.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "qt-metric-empty", children: "no data" }) : clause.groupBy.length === 0 ? /* @__PURE__ */ jsx8(BigNumber, { bucket: buckets[0], fmt }) : clause.groupBy.length === 1 ? /* @__PURE__ */ jsx8(BarList, { buckets, fmt }) : clause.groupBy.length === 2 ? /* @__PURE__ */ jsx8(Pivot, { buckets, fmt, rowLabel: groupLabels[0], colLabel: groupLabels[1] }) : /* @__PURE__ */ jsx8(FlatTable, { buckets, fmt, groupLabels, valueLabel: metricTitle(clause, measure) })
|
|
2816
3948
|
] });
|
|
2817
3949
|
}
|
|
2818
3950
|
function BigNumber({ bucket, fmt }) {
|
|
2819
|
-
return /* @__PURE__ */
|
|
2820
|
-
/* @__PURE__ */
|
|
2821
|
-
/* @__PURE__ */
|
|
3951
|
+
return /* @__PURE__ */ jsxs8("div", { className: "qt-metric-big", children: [
|
|
3952
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-metric-big-value", children: fmt(bucket.value) }),
|
|
3953
|
+
/* @__PURE__ */ jsxs8("span", { className: "qt-metric-big-sub", children: [
|
|
2822
3954
|
bucket.count.toLocaleString(),
|
|
2823
3955
|
" rows"
|
|
2824
3956
|
] })
|
|
@@ -2826,12 +3958,12 @@ function BigNumber({ bucket, fmt }) {
|
|
|
2826
3958
|
}
|
|
2827
3959
|
function BarList({ buckets, fmt }) {
|
|
2828
3960
|
const max = Math.max(...buckets.map((b) => barMagnitude(b)), 0);
|
|
2829
|
-
return /* @__PURE__ */
|
|
3961
|
+
return /* @__PURE__ */ jsx8("div", { className: "qt-metric-bars", children: buckets.map((b, i) => {
|
|
2830
3962
|
const pct = max > 0 ? barMagnitude(b) / max * 100 : 0;
|
|
2831
|
-
return /* @__PURE__ */
|
|
2832
|
-
/* @__PURE__ */
|
|
2833
|
-
/* @__PURE__ */
|
|
2834
|
-
/* @__PURE__ */
|
|
3963
|
+
return /* @__PURE__ */ jsxs8("div", { className: "qt-metric-bar-row", children: [
|
|
3964
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-metric-bar-label", title: keyText(b.keys[0]), children: keyNode(b.keys[0]) }),
|
|
3965
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-metric-bar-track", children: /* @__PURE__ */ jsx8("span", { className: "qt-metric-bar-fill", style: { width: `${pct}%` } }) }),
|
|
3966
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-metric-bar-value", children: fmt(b.value) })
|
|
2835
3967
|
] }, `${keyText(b.keys[0])}-${i}`);
|
|
2836
3968
|
}) });
|
|
2837
3969
|
}
|
|
@@ -2845,22 +3977,22 @@ function Pivot({
|
|
|
2845
3977
|
const colKeys = distinctKeys(buckets, 1);
|
|
2846
3978
|
const cell = /* @__PURE__ */ new Map();
|
|
2847
3979
|
for (const b of buckets) cell.set(`${keyText(b.keys[0])}\0${keyText(b.keys[1])}`, b.value);
|
|
2848
|
-
return /* @__PURE__ */
|
|
2849
|
-
/* @__PURE__ */
|
|
2850
|
-
/* @__PURE__ */
|
|
3980
|
+
return /* @__PURE__ */ jsx8("div", { className: "qt-metric-pivot-wrap", children: /* @__PURE__ */ jsxs8("table", { className: "qt-metric-pivot", children: [
|
|
3981
|
+
/* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs8("tr", { children: [
|
|
3982
|
+
/* @__PURE__ */ jsx8("th", { className: "qt-metric-pivot-corner", children: /* @__PURE__ */ jsxs8("span", { className: "qt-metric-pivot-axes", children: [
|
|
2851
3983
|
rowLabel,
|
|
2852
3984
|
" ",
|
|
2853
|
-
/* @__PURE__ */
|
|
3985
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-muted", children: "\\" }),
|
|
2854
3986
|
" ",
|
|
2855
3987
|
colLabel
|
|
2856
3988
|
] }) }),
|
|
2857
|
-
colKeys.map((c) => /* @__PURE__ */
|
|
3989
|
+
colKeys.map((c) => /* @__PURE__ */ jsx8("th", { className: "qt-metric-pivot-col", children: keyNode(c) }, keyText(c)))
|
|
2858
3990
|
] }) }),
|
|
2859
|
-
/* @__PURE__ */
|
|
2860
|
-
/* @__PURE__ */
|
|
3991
|
+
/* @__PURE__ */ jsx8("tbody", { children: rowKeys.map((r) => /* @__PURE__ */ jsxs8("tr", { children: [
|
|
3992
|
+
/* @__PURE__ */ jsx8("th", { className: "qt-metric-pivot-row", children: keyNode(r) }),
|
|
2861
3993
|
colKeys.map((c) => {
|
|
2862
3994
|
const v = cell.get(`${keyText(r)}\0${keyText(c)}`);
|
|
2863
|
-
return /* @__PURE__ */
|
|
3995
|
+
return /* @__PURE__ */ jsx8("td", { className: "qt-metric-pivot-cell", children: v === void 0 ? /* @__PURE__ */ jsx8("span", { className: "qt-muted", children: "\xB7" }) : fmt(v) }, keyText(c));
|
|
2864
3996
|
})
|
|
2865
3997
|
] }, keyText(r))) })
|
|
2866
3998
|
] }) });
|
|
@@ -2869,16 +4001,16 @@ function FlatTable({
|
|
|
2869
4001
|
buckets,
|
|
2870
4002
|
fmt,
|
|
2871
4003
|
groupLabels,
|
|
2872
|
-
valueLabel
|
|
4004
|
+
valueLabel: valueLabel2
|
|
2873
4005
|
}) {
|
|
2874
|
-
return /* @__PURE__ */
|
|
2875
|
-
/* @__PURE__ */
|
|
2876
|
-
groupLabels.map((g) => /* @__PURE__ */
|
|
2877
|
-
/* @__PURE__ */
|
|
4006
|
+
return /* @__PURE__ */ jsx8("div", { className: "qt-metric-pivot-wrap", children: /* @__PURE__ */ jsxs8("table", { className: "qt-metric-flat", children: [
|
|
4007
|
+
/* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs8("tr", { children: [
|
|
4008
|
+
groupLabels.map((g) => /* @__PURE__ */ jsx8("th", { children: g }, g)),
|
|
4009
|
+
/* @__PURE__ */ jsx8("th", { className: "qt-metric-flat-value", children: valueLabel2 })
|
|
2878
4010
|
] }) }),
|
|
2879
|
-
/* @__PURE__ */
|
|
2880
|
-
groupLabels.map((_, j) => /* @__PURE__ */
|
|
2881
|
-
/* @__PURE__ */
|
|
4011
|
+
/* @__PURE__ */ jsx8("tbody", { children: buckets.map((b, i) => /* @__PURE__ */ jsxs8("tr", { children: [
|
|
4012
|
+
groupLabels.map((_, j) => /* @__PURE__ */ jsx8("td", { children: keyNode(b.keys[j]) }, j)),
|
|
4013
|
+
/* @__PURE__ */ jsx8("td", { className: "qt-metric-flat-value", children: fmt(b.value) })
|
|
2882
4014
|
] }, i)) })
|
|
2883
4015
|
] }) });
|
|
2884
4016
|
}
|
|
@@ -2889,7 +4021,7 @@ function metricTitle(clause, measure) {
|
|
|
2889
4021
|
}
|
|
2890
4022
|
var UNIT_RENDER_KEYS = /* @__PURE__ */ new Set(["duration_ms", "byte_size"]);
|
|
2891
4023
|
function formatMetricValue(value, op, measure, renderers, decimals) {
|
|
2892
|
-
if (value == null) return /* @__PURE__ */
|
|
4024
|
+
if (value == null) return /* @__PURE__ */ jsx8("span", { className: "qt-muted", children: "\u2014" });
|
|
2893
4025
|
if (op === "count" || op === "count_distinct") {
|
|
2894
4026
|
return typeof value === "number" ? formatNumber(value, 0) : String(value);
|
|
2895
4027
|
}
|
|
@@ -2952,21 +4084,21 @@ function keyText(k) {
|
|
|
2952
4084
|
return k == null ? NULL_KEY : String(k);
|
|
2953
4085
|
}
|
|
2954
4086
|
function keyNode(k) {
|
|
2955
|
-
return k == null ? /* @__PURE__ */
|
|
4087
|
+
return k == null ? /* @__PURE__ */ jsx8("span", { className: "qt-muted", children: NULL_KEY }) : String(k);
|
|
2956
4088
|
}
|
|
2957
4089
|
|
|
2958
4090
|
// src/SelectionToolbar.tsx
|
|
2959
|
-
import { jsx as
|
|
4091
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2960
4092
|
var cx5 = (...parts) => parts.filter(Boolean).join(" ");
|
|
2961
4093
|
function SelectionToolbar({ selection, actions, alwaysShow, className }) {
|
|
2962
4094
|
if (selection.count === 0 && !alwaysShow) return null;
|
|
2963
4095
|
const selectedIds = [...selection.selected];
|
|
2964
|
-
return /* @__PURE__ */
|
|
2965
|
-
/* @__PURE__ */
|
|
4096
|
+
return /* @__PURE__ */ jsxs9("div", { className: cx5("qt-selection-toolbar", className), role: "toolbar", children: [
|
|
4097
|
+
/* @__PURE__ */ jsxs9("span", { className: "qt-selection-count", children: [
|
|
2966
4098
|
selection.count,
|
|
2967
4099
|
" selected"
|
|
2968
4100
|
] }),
|
|
2969
|
-
/* @__PURE__ */
|
|
4101
|
+
/* @__PURE__ */ jsx9(
|
|
2970
4102
|
"button",
|
|
2971
4103
|
{
|
|
2972
4104
|
type: "button",
|
|
@@ -2976,16 +4108,19 @@ function SelectionToolbar({ selection, actions, alwaysShow, className }) {
|
|
|
2976
4108
|
children: "clear"
|
|
2977
4109
|
}
|
|
2978
4110
|
),
|
|
2979
|
-
/* @__PURE__ */
|
|
4111
|
+
/* @__PURE__ */ jsx9("span", { className: "qt-selection-actions", children: actions(selectedIds, selection) })
|
|
2980
4112
|
] });
|
|
2981
4113
|
}
|
|
2982
4114
|
export {
|
|
2983
4115
|
CellMenu,
|
|
2984
4116
|
DataTable,
|
|
2985
4117
|
FieldPicker,
|
|
4118
|
+
FilterValueProvider,
|
|
2986
4119
|
MetricsPanel,
|
|
4120
|
+
PresentedFilterValue,
|
|
2987
4121
|
QueryBuilder,
|
|
2988
4122
|
SavedQueriesModal,
|
|
4123
|
+
SelectColumnEditor,
|
|
2989
4124
|
SelectionToolbar,
|
|
2990
4125
|
defaultRenderers,
|
|
2991
4126
|
resolveRenderer,
|