@pythia-software/query-table-ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/FormulaEditor-YCPICDIH.js +251 -0
- package/dist/FormulaEditor-YCPICDIH.js.map +1 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +1463 -402
- package/dist/index.js.map +1 -1
- package/dist/theme.css +563 -1
- package/dist/theme.css.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -137,6 +137,7 @@ function formatRelative(iso) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// src/DataTable.tsx
|
|
140
|
+
import { isComputedCellError } from "@pythia-software/query-table-core";
|
|
140
141
|
import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
|
|
141
142
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
142
143
|
import { isSortable, readFieldValue } from "@pythia-software/query-table-core";
|
|
@@ -144,10 +145,10 @@ import { useColumnDrag } from "@pythia-software/query-table-react";
|
|
|
144
145
|
|
|
145
146
|
// src/CellMenu.tsx
|
|
146
147
|
import { useEffect } from "react";
|
|
147
|
-
import { NULLARY_OPS, opsForField } from "@pythia-software/query-table-core";
|
|
148
|
+
import { NULLARY_OPS, negateClause, opsForField, opPairsForField, isFilterable } from "@pythia-software/query-table-core";
|
|
148
149
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
149
150
|
var cx = (...parts) => parts.filter(Boolean).join(" ");
|
|
150
|
-
var MENU_WIDTH =
|
|
151
|
+
var MENU_WIDTH = 320;
|
|
151
152
|
var ROW_HEIGHT = 32;
|
|
152
153
|
var POINTER_OFFSET = 4;
|
|
153
154
|
function CellMenu({ field, value, x, y, onAddFilter, onClose, classNames }) {
|
|
@@ -166,73 +167,36 @@ function CellMenu({ field, value, x, y, onAddFilter, onClose, classNames }) {
|
|
|
166
167
|
clearTimeout(t);
|
|
167
168
|
};
|
|
168
169
|
}, [onClose]);
|
|
169
|
-
const ops = opsForField(field);
|
|
170
|
+
const ops = isFilterable(field) ? opsForField(field) : [];
|
|
171
|
+
const pairs = isFilterable(field) ? opPairsForField(field) : [];
|
|
172
|
+
const hasNullity = pairs.some((p) => p.keep.op === "is_null");
|
|
170
173
|
const isArray = Array.isArray(value);
|
|
171
174
|
const isNullish = value == null || value === "" || isArray && value.length === 0;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
onAddFilter(
|
|
175
|
+
const cellText = valueToString(value);
|
|
176
|
+
function applyClause(clause) {
|
|
177
|
+
onAddFilter(clause);
|
|
175
178
|
onClose();
|
|
176
179
|
}
|
|
177
|
-
const
|
|
180
|
+
const rows = [];
|
|
181
|
+
const pairRow = (op, raw) => {
|
|
182
|
+
const positive = { field: field.name, op, value: NULLARY_OPS.has(op) ? "" : raw };
|
|
183
|
+
rows.push({ positive, negative: negateClause(positive, ops) });
|
|
184
|
+
};
|
|
178
185
|
if (isArray && !isNullish) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
" ",
|
|
187
|
-
/* @__PURE__ */ jsx("code", { children: s })
|
|
188
|
-
] }, `el-${i}`)
|
|
189
|
-
);
|
|
190
|
-
});
|
|
186
|
+
const arrayOp = pairs.find((p) => p.keep.op === "includes")?.keep.op ?? pairs[0]?.keep.op ?? "=";
|
|
187
|
+
value.forEach((el) => pairRow(arrayOp, String(el)));
|
|
188
|
+
if (hasNullity) pairRow("is_null", cellText);
|
|
189
|
+
} else if (!isNullish) {
|
|
190
|
+
for (const pair of pairs) pairRow(pair.keep.op, cellText);
|
|
191
|
+
} else if (hasNullity) {
|
|
192
|
+
pairRow("is_null", cellText);
|
|
191
193
|
}
|
|
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
194
|
const vw = typeof window !== "undefined" ? window.innerWidth : 9999;
|
|
232
195
|
const vh = typeof window !== "undefined" ? window.innerHeight : 9999;
|
|
196
|
+
const approxRows = 3 + rows.length;
|
|
233
197
|
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__ */
|
|
198
|
+
const top = Math.max(8, Math.min(y + POINTER_OFFSET, vh - ROW_HEIGHT * approxRows - 8));
|
|
199
|
+
return /* @__PURE__ */ jsxs(
|
|
236
200
|
"div",
|
|
237
201
|
{
|
|
238
202
|
className: cx("qt-cell-menu", classNames?.popover),
|
|
@@ -240,7 +204,69 @@ function CellMenu({ field, value, x, y, onAddFilter, onClose, classNames }) {
|
|
|
240
204
|
onClick: (e) => e.stopPropagation(),
|
|
241
205
|
onContextMenu: (e) => e.preventDefault(),
|
|
242
206
|
role: "menu",
|
|
243
|
-
children:
|
|
207
|
+
children: [
|
|
208
|
+
/* @__PURE__ */ jsxs("div", { className: "qt-cm-header", children: [
|
|
209
|
+
/* @__PURE__ */ jsx("span", { className: "qt-cm-field", children: field.label }),
|
|
210
|
+
/* @__PURE__ */ jsx("span", { className: "qt-cm-value", children: previewValue(value) })
|
|
211
|
+
] }),
|
|
212
|
+
/* @__PURE__ */ jsx(
|
|
213
|
+
MenuItem,
|
|
214
|
+
{
|
|
215
|
+
classNames,
|
|
216
|
+
onClick: async () => {
|
|
217
|
+
try {
|
|
218
|
+
await navigator.clipboard.writeText(cellText);
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
onClose();
|
|
222
|
+
},
|
|
223
|
+
children: "copy value"
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
/* @__PURE__ */ jsx("div", { className: cx("qt-cm-sep", classNames?.separator) }),
|
|
227
|
+
rows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "qt-cm-filter-grid", role: "group", "aria-label": "Filter by this cell", children: [
|
|
228
|
+
/* @__PURE__ */ jsx("span", { className: "qt-cm-col-head", children: "keep" }),
|
|
229
|
+
/* @__PURE__ */ jsx("span", { className: "qt-cm-col-head qt-cm-col-head--neg", children: "exclude" }),
|
|
230
|
+
rows.map((row, i) => /* @__PURE__ */ jsx(FilterPairRow, { row, value, classNames, onApply: applyClause }, i))
|
|
231
|
+
] })
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
function FilterPairRow({
|
|
237
|
+
row,
|
|
238
|
+
value,
|
|
239
|
+
classNames,
|
|
240
|
+
onApply
|
|
241
|
+
}) {
|
|
242
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
243
|
+
/* @__PURE__ */ jsx(FilterButton, { clause: row.positive, value, classNames, onApply }),
|
|
244
|
+
/* @__PURE__ */ jsx(FilterButton, { clause: row.negative, value, negative: true, classNames, onApply })
|
|
245
|
+
] });
|
|
246
|
+
}
|
|
247
|
+
function FilterButton({
|
|
248
|
+
clause,
|
|
249
|
+
value,
|
|
250
|
+
negative,
|
|
251
|
+
classNames,
|
|
252
|
+
onApply
|
|
253
|
+
}) {
|
|
254
|
+
if (!clause) return /* @__PURE__ */ jsx("span", { className: "qt-cm-filter-empty", "aria-hidden": true });
|
|
255
|
+
const nullary = NULLARY_OPS.has(clause.op);
|
|
256
|
+
return /* @__PURE__ */ jsxs(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
type: "button",
|
|
260
|
+
className: cx("qt-cm-item", "qt-cm-filter", negative && "qt-cm-filter--neg", classNames?.item),
|
|
261
|
+
onClick: () => onApply(clause),
|
|
262
|
+
role: "menuitem",
|
|
263
|
+
children: [
|
|
264
|
+
/* @__PURE__ */ jsx("span", { className: "qt-cm-op", children: predicateSymbol(clause) }),
|
|
265
|
+
!nullary ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
266
|
+
" ",
|
|
267
|
+
/* @__PURE__ */ jsx("code", { children: previewValue(value) })
|
|
268
|
+
] }) : null
|
|
269
|
+
]
|
|
244
270
|
}
|
|
245
271
|
);
|
|
246
272
|
}
|
|
@@ -251,6 +277,10 @@ function MenuItem({
|
|
|
251
277
|
}) {
|
|
252
278
|
return /* @__PURE__ */ jsx("button", { type: "button", className: cx("qt-cm-item", classNames?.item), onClick, role: "menuitem", children });
|
|
253
279
|
}
|
|
280
|
+
function predicateSymbol(clause) {
|
|
281
|
+
const base = opSymbol(clause.op);
|
|
282
|
+
return clause.negated ? `not ${base}` : base;
|
|
283
|
+
}
|
|
254
284
|
function opSymbol(op) {
|
|
255
285
|
switch (op) {
|
|
256
286
|
case "!=":
|
|
@@ -259,6 +289,14 @@ function opSymbol(op) {
|
|
|
259
289
|
return "\u2265";
|
|
260
290
|
case "<=":
|
|
261
291
|
return "\u2264";
|
|
292
|
+
case "starts_with":
|
|
293
|
+
return "starts with";
|
|
294
|
+
case "ends_with":
|
|
295
|
+
return "ends with";
|
|
296
|
+
case "matches_regex":
|
|
297
|
+
return "matches";
|
|
298
|
+
case "not_matches_regex":
|
|
299
|
+
return "not matches";
|
|
262
300
|
case "is_null":
|
|
263
301
|
return "is null";
|
|
264
302
|
case "is_not_null":
|
|
@@ -936,7 +974,7 @@ function DataTable(props) {
|
|
|
936
974
|
onClick: (e) => openMenu(e, f, row),
|
|
937
975
|
onContextMenu: (e) => openMenu(e, f, row),
|
|
938
976
|
onDoubleClick: (e) => copyCellToClipboard(e, f, id, i, value),
|
|
939
|
-
children: copiedCellKey === key ? /* @__PURE__ */ jsx2("span", { className: "qt-cell-copy-chip", children: "\u2713 copied" }) : render({ value, row, field: f, query })
|
|
977
|
+
children: copiedCellKey === key ? /* @__PURE__ */ jsx2("span", { className: "qt-cell-copy-chip", children: "\u2713 copied" }) : isComputedCellError(value) ? /* @__PURE__ */ jsx2("span", { className: "qt-formula-error", title: value.computedError, children: value.computedError === "Calculating\u2026" ? "Calculating\u2026" : "Error" }) : render({ value, row, field: f, query })
|
|
940
978
|
},
|
|
941
979
|
f.name
|
|
942
980
|
);
|
|
@@ -1114,8 +1152,783 @@ function valueToString2(value) {
|
|
|
1114
1152
|
return String(value);
|
|
1115
1153
|
}
|
|
1116
1154
|
|
|
1155
|
+
// src/SelectColumnEditor.tsx
|
|
1156
|
+
import {
|
|
1157
|
+
lazy,
|
|
1158
|
+
Suspense,
|
|
1159
|
+
useEffect as useEffect3,
|
|
1160
|
+
useId,
|
|
1161
|
+
useMemo as useMemo2,
|
|
1162
|
+
useRef as useRef2,
|
|
1163
|
+
useState as useState3
|
|
1164
|
+
} from "react";
|
|
1165
|
+
import {
|
|
1166
|
+
computedFieldName,
|
|
1167
|
+
FORMULA_FUNCTIONS,
|
|
1168
|
+
isComputedField,
|
|
1169
|
+
isSelectable
|
|
1170
|
+
} from "@pythia-software/query-table-core";
|
|
1171
|
+
|
|
1172
|
+
// src/previewOrdering.ts
|
|
1173
|
+
var collator = new Intl.Collator(void 0, {
|
|
1174
|
+
numeric: true,
|
|
1175
|
+
sensitivity: "base"
|
|
1176
|
+
});
|
|
1177
|
+
var valueLabel = (value) => value === null ? "NULL" : typeof value === "string" ? value : JSON.stringify(value);
|
|
1178
|
+
function mixedRank(group) {
|
|
1179
|
+
const value = JSON.stringify([
|
|
1180
|
+
group.inputs,
|
|
1181
|
+
group.result.value,
|
|
1182
|
+
group.result.error ?? null
|
|
1183
|
+
]);
|
|
1184
|
+
let hash = 2166136261;
|
|
1185
|
+
for (let i = 0; i < value.length; i++)
|
|
1186
|
+
hash = Math.imul(hash ^ value.charCodeAt(i), 16777619);
|
|
1187
|
+
return hash >>> 0;
|
|
1188
|
+
}
|
|
1189
|
+
function compareValue(a, b) {
|
|
1190
|
+
return collator.compare(valueLabel(a), valueLabel(b));
|
|
1191
|
+
}
|
|
1192
|
+
function sortValue(group, key) {
|
|
1193
|
+
switch (key.kind) {
|
|
1194
|
+
case "input":
|
|
1195
|
+
return group.inputs[key.index] ?? null;
|
|
1196
|
+
case "result":
|
|
1197
|
+
return group.result.error ?? group.result.value;
|
|
1198
|
+
case "count":
|
|
1199
|
+
case "percentage":
|
|
1200
|
+
return group.count;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
function orderPreviewGroups(groups, sort) {
|
|
1204
|
+
return groups.map((group, index) => ({ group, index })).sort((a, b) => {
|
|
1205
|
+
if (!sort) {
|
|
1206
|
+
const rank = mixedRank(a.group) - mixedRank(b.group);
|
|
1207
|
+
return rank || a.index - b.index;
|
|
1208
|
+
}
|
|
1209
|
+
const comparison = compareValue(
|
|
1210
|
+
sortValue(a.group, sort.key),
|
|
1211
|
+
sortValue(b.group, sort.key)
|
|
1212
|
+
);
|
|
1213
|
+
return (sort.direction === "asc" ? comparison : -comparison) || a.index - b.index;
|
|
1214
|
+
}).map(({ group }) => group);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/SelectColumnEditor.tsx
|
|
1218
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1219
|
+
var FormulaEditor = lazy(() => import("./FormulaEditor-YCPICDIH.js"));
|
|
1220
|
+
var fieldExpression = (name) => `[${name.replace(/\]/g, "]]")}]`;
|
|
1221
|
+
var display = (v) => v === null ? "NULL" : typeof v === "string" ? v === "" ? '""' : v : JSON.stringify(v);
|
|
1222
|
+
function SelectColumnEditor({
|
|
1223
|
+
api,
|
|
1224
|
+
onClose
|
|
1225
|
+
}) {
|
|
1226
|
+
const titleId = useId(), dialog = useRef2(null);
|
|
1227
|
+
const closeRef = useRef2(onClose);
|
|
1228
|
+
closeRef.current = onClose;
|
|
1229
|
+
const [columns, setColumns] = useState3(
|
|
1230
|
+
() => api.select.visible.map((c) => ({ ...c }))
|
|
1231
|
+
);
|
|
1232
|
+
const [search, setSearch] = useState3(""), [active, setActive] = useState3(api.select.visible[0]?.field ?? "");
|
|
1233
|
+
const [editing, setEditing] = useState3(false), [id, setId] = useState3(""), [revision, setRevision] = useState3(null);
|
|
1234
|
+
const [label, setLabel] = useState3(""), [source, setSource] = useState3("");
|
|
1235
|
+
const [count, setCount] = useState3(1e3), [preview, setPreview] = useState3(null), [loading, setLoading] = useState3(false);
|
|
1236
|
+
const [error, setError] = useState3(null), [notice, setNotice] = useState3(""), [saving, setSaving] = useState3(false);
|
|
1237
|
+
const [filter, setFilter] = useState3("all"), [page, setPage] = useState3(0), [retry, setRetry] = useState3(0), [functionSearch, setFunctionSearch] = useState3("");
|
|
1238
|
+
const [previewSort, setPreviewSort] = useState3(null);
|
|
1239
|
+
const drag = useRef2(null);
|
|
1240
|
+
const [inspection, setInspection] = useState3(null);
|
|
1241
|
+
const catalogue = api.computed.catalogue.filter(isSelectable);
|
|
1242
|
+
const chosen = catalogue.find((f) => f.name === active);
|
|
1243
|
+
const expression = editing ? source : active ? fieldExpression(active) : "";
|
|
1244
|
+
const validation = useMemo2(() => {
|
|
1245
|
+
if (!expression)
|
|
1246
|
+
return { error: "Choose a column or create a computed column." };
|
|
1247
|
+
try {
|
|
1248
|
+
return {
|
|
1249
|
+
plan: api.computed.compile(expression, editing ? id : void 0)
|
|
1250
|
+
};
|
|
1251
|
+
} catch (e) {
|
|
1252
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
1253
|
+
}
|
|
1254
|
+
}, [expression, editing, id, api.computed.compile]);
|
|
1255
|
+
useEffect3(() => {
|
|
1256
|
+
const previous = document.activeElement;
|
|
1257
|
+
dialog.current?.focus();
|
|
1258
|
+
const onKey = (e) => {
|
|
1259
|
+
if (e.key === "Escape") {
|
|
1260
|
+
if (document.querySelector(".cm-tooltip-autocomplete")) return;
|
|
1261
|
+
closeRef.current();
|
|
1262
|
+
}
|
|
1263
|
+
if (e.key === "Tab") {
|
|
1264
|
+
const focusable = Array.from(
|
|
1265
|
+
dialog.current?.querySelectorAll(
|
|
1266
|
+
'button:not([disabled]), input:not([disabled]), select, textarea, [tabindex="0"], [contenteditable="true"]'
|
|
1267
|
+
) ?? []
|
|
1268
|
+
).filter((el) => el.getClientRects().length);
|
|
1269
|
+
const first = focusable[0], last = focusable[focusable.length - 1];
|
|
1270
|
+
if (e.shiftKey && (document.activeElement === first || document.activeElement === dialog.current)) {
|
|
1271
|
+
e.preventDefault();
|
|
1272
|
+
last?.focus();
|
|
1273
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
1274
|
+
e.preventDefault();
|
|
1275
|
+
first?.focus();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
document.addEventListener("keydown", onKey);
|
|
1280
|
+
const old = document.body.style.overflow;
|
|
1281
|
+
document.body.style.overflow = "hidden";
|
|
1282
|
+
return () => {
|
|
1283
|
+
document.removeEventListener("keydown", onKey);
|
|
1284
|
+
document.body.style.overflow = old;
|
|
1285
|
+
previous?.focus();
|
|
1286
|
+
};
|
|
1287
|
+
}, []);
|
|
1288
|
+
useEffect3(() => {
|
|
1289
|
+
const ac = new AbortController();
|
|
1290
|
+
setPreview(null);
|
|
1291
|
+
setError(null);
|
|
1292
|
+
setPage(0);
|
|
1293
|
+
setInspection(null);
|
|
1294
|
+
if (validation.error || !Number.isInteger(count) || count < 1 || count > 1e4) {
|
|
1295
|
+
setLoading(false);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
setLoading(true);
|
|
1299
|
+
const timer = setTimeout(() => {
|
|
1300
|
+
void api.computed.preview(
|
|
1301
|
+
expression,
|
|
1302
|
+
count,
|
|
1303
|
+
ac.signal,
|
|
1304
|
+
editing ? id : void 0,
|
|
1305
|
+
editing
|
|
1306
|
+
).then((result) => {
|
|
1307
|
+
if (!ac.signal.aborted) setPreview(result);
|
|
1308
|
+
}).catch((e) => {
|
|
1309
|
+
if (!ac.signal.aborted)
|
|
1310
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1311
|
+
}).finally(() => {
|
|
1312
|
+
if (!ac.signal.aborted) setLoading(false);
|
|
1313
|
+
});
|
|
1314
|
+
}, 400);
|
|
1315
|
+
return () => {
|
|
1316
|
+
ac.abort();
|
|
1317
|
+
clearTimeout(timer);
|
|
1318
|
+
};
|
|
1319
|
+
}, [expression, count, editing, id, validation, api.computed.preview, retry]);
|
|
1320
|
+
const move = (field, index) => setColumns((cols) => {
|
|
1321
|
+
const next = cols.filter((c) => c.field !== field), item = cols.find((c) => c.field === field);
|
|
1322
|
+
if (item) next.splice(Math.max(0, Math.min(index, next.length)), 0, item);
|
|
1323
|
+
return next;
|
|
1324
|
+
});
|
|
1325
|
+
const toggle = (field) => setColumns(
|
|
1326
|
+
(cols) => cols.some((c) => c.field === field) ? cols.filter((c) => c.field !== field) : [...cols, { field }]
|
|
1327
|
+
);
|
|
1328
|
+
function browse(field) {
|
|
1329
|
+
setActive(field);
|
|
1330
|
+
setEditing(false);
|
|
1331
|
+
setNotice("");
|
|
1332
|
+
}
|
|
1333
|
+
function edit(defId) {
|
|
1334
|
+
const def = api.computed.definitions.find((d) => d.id === defId);
|
|
1335
|
+
setId(def?.id ?? defId ?? crypto.randomUUID());
|
|
1336
|
+
setRevision(def?.revision ?? null);
|
|
1337
|
+
setLabel(def?.label ?? defId ?? "");
|
|
1338
|
+
setSource(
|
|
1339
|
+
def?.expression.source ?? (defId ? "" : active ? fieldExpression(active) : "LEFT([field], 3)")
|
|
1340
|
+
);
|
|
1341
|
+
setEditing(true);
|
|
1342
|
+
setNotice("");
|
|
1343
|
+
}
|
|
1344
|
+
async function save() {
|
|
1345
|
+
setSaving(true);
|
|
1346
|
+
setError(null);
|
|
1347
|
+
try {
|
|
1348
|
+
const def = await api.computed.save(
|
|
1349
|
+
{
|
|
1350
|
+
id,
|
|
1351
|
+
label: label.trim(),
|
|
1352
|
+
expression: { language: "qt-expr", version: 1, source }
|
|
1353
|
+
},
|
|
1354
|
+
revision
|
|
1355
|
+
);
|
|
1356
|
+
setRevision(def.revision);
|
|
1357
|
+
const field = computedFieldName(def.id);
|
|
1358
|
+
setActive(field);
|
|
1359
|
+
setColumns(
|
|
1360
|
+
(cols) => cols.some((c) => c.field === field) ? cols : [...cols, { field }]
|
|
1361
|
+
);
|
|
1362
|
+
setNotice(
|
|
1363
|
+
"Definition saved. All queries referencing this ID now use this definition. Apply columns to update this query\u2019s layout."
|
|
1364
|
+
);
|
|
1365
|
+
} catch (e) {
|
|
1366
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1367
|
+
} finally {
|
|
1368
|
+
setSaving(false);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
const groups = useMemo2(
|
|
1372
|
+
() => orderPreviewGroups(
|
|
1373
|
+
(preview?.groups ?? []).filter(
|
|
1374
|
+
(g) => filter === "errors" ? !!g.result.error : filter === "nulls" ? !g.result.error && g.result.value === null : true
|
|
1375
|
+
),
|
|
1376
|
+
previewSort
|
|
1377
|
+
),
|
|
1378
|
+
[preview, filter, previewSort]
|
|
1379
|
+
);
|
|
1380
|
+
const shown = groups.slice(page * 50, (page + 1) * 50);
|
|
1381
|
+
const samePreviewSortKey = (a, b) => {
|
|
1382
|
+
if (a.kind !== b.kind) return false;
|
|
1383
|
+
if (a.kind === "input" && b.kind === "input")
|
|
1384
|
+
return a.index === b.index;
|
|
1385
|
+
return true;
|
|
1386
|
+
};
|
|
1387
|
+
const selectPreviewSort = (key, direction) => {
|
|
1388
|
+
setPreviewSort((current) => {
|
|
1389
|
+
const sameKey = !!current && samePreviewSortKey(current.key, key);
|
|
1390
|
+
return sameKey ? { key, direction: current.direction === "asc" ? "desc" : "asc" } : { key, direction };
|
|
1391
|
+
});
|
|
1392
|
+
setPage(0);
|
|
1393
|
+
};
|
|
1394
|
+
const previewSortDirection = (key) => previewSort && samePreviewSortKey(previewSort.key, key) ? previewSort.direction : void 0;
|
|
1395
|
+
const previewAriaSort = (key) => {
|
|
1396
|
+
const direction = previewSortDirection(key);
|
|
1397
|
+
return direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none";
|
|
1398
|
+
};
|
|
1399
|
+
const previewSortHeader = (label2, key, direction = "asc") => {
|
|
1400
|
+
const currentDirection = previewSortDirection(key);
|
|
1401
|
+
return /* @__PURE__ */ jsxs3(
|
|
1402
|
+
"button",
|
|
1403
|
+
{
|
|
1404
|
+
type: "button",
|
|
1405
|
+
className: "qt-preview-sort",
|
|
1406
|
+
onClick: () => selectPreviewSort(key, direction),
|
|
1407
|
+
title: `Sort by ${label2}`,
|
|
1408
|
+
children: [
|
|
1409
|
+
label2,
|
|
1410
|
+
currentDirection && (currentDirection === "asc" ? " \u2191" : " \u2193")
|
|
1411
|
+
]
|
|
1412
|
+
}
|
|
1413
|
+
);
|
|
1414
|
+
};
|
|
1415
|
+
return /* @__PURE__ */ jsx3("div", { className: "qt-modal-backdrop qt-select-editor-backdrop", children: /* @__PURE__ */ jsxs3(
|
|
1416
|
+
"div",
|
|
1417
|
+
{
|
|
1418
|
+
className: "qt-select-editor",
|
|
1419
|
+
role: "dialog",
|
|
1420
|
+
"aria-modal": "true",
|
|
1421
|
+
"aria-labelledby": titleId,
|
|
1422
|
+
ref: dialog,
|
|
1423
|
+
tabIndex: -1,
|
|
1424
|
+
children: [
|
|
1425
|
+
/* @__PURE__ */ jsxs3("header", { className: "qt-select-editor-header", children: [
|
|
1426
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1427
|
+
/* @__PURE__ */ jsx3("h2", { id: titleId, children: "Select columns" }),
|
|
1428
|
+
/* @__PURE__ */ jsx3("p", { children: "Arrange your view, explore values, and create reusable computed columns." })
|
|
1429
|
+
] }),
|
|
1430
|
+
/* @__PURE__ */ jsx3(
|
|
1431
|
+
"button",
|
|
1432
|
+
{
|
|
1433
|
+
type: "button",
|
|
1434
|
+
className: "qt-btn",
|
|
1435
|
+
onClick: onClose,
|
|
1436
|
+
"aria-label": "Close column editor",
|
|
1437
|
+
children: "\u2715"
|
|
1438
|
+
}
|
|
1439
|
+
)
|
|
1440
|
+
] }),
|
|
1441
|
+
/* @__PURE__ */ jsxs3("div", { className: "qt-select-editor-body", children: [
|
|
1442
|
+
/* @__PURE__ */ jsxs3("aside", { className: "qt-selected-panel", children: [
|
|
1443
|
+
/* @__PURE__ */ jsxs3("h3", { children: [
|
|
1444
|
+
"Selected ",
|
|
1445
|
+
/* @__PURE__ */ jsx3("span", { className: "qt-muted", children: columns.length })
|
|
1446
|
+
] }),
|
|
1447
|
+
/* @__PURE__ */ jsx3("p", { className: "qt-muted", children: "Drag to reorder, or use the arrow buttons." }),
|
|
1448
|
+
/* @__PURE__ */ jsx3("ol", { className: "qt-editor-columns", children: columns.map((c, i) => {
|
|
1449
|
+
const f = catalogue.find((f2) => f2.name === c.field);
|
|
1450
|
+
return /* @__PURE__ */ jsxs3(
|
|
1451
|
+
"li",
|
|
1452
|
+
{
|
|
1453
|
+
draggable: true,
|
|
1454
|
+
onDragStart: (e) => {
|
|
1455
|
+
drag.current = c.field;
|
|
1456
|
+
e.dataTransfer.setData("text/plain", c.field);
|
|
1457
|
+
},
|
|
1458
|
+
onDragOver: (e) => e.preventDefault(),
|
|
1459
|
+
onDrop: (e) => {
|
|
1460
|
+
e.preventDefault();
|
|
1461
|
+
if (drag.current) move(drag.current, i);
|
|
1462
|
+
drag.current = null;
|
|
1463
|
+
},
|
|
1464
|
+
onDragEnd: () => {
|
|
1465
|
+
drag.current = null;
|
|
1466
|
+
},
|
|
1467
|
+
children: [
|
|
1468
|
+
/* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: "\u283F" }),
|
|
1469
|
+
/* @__PURE__ */ jsx3(
|
|
1470
|
+
"button",
|
|
1471
|
+
{
|
|
1472
|
+
type: "button",
|
|
1473
|
+
className: "qt-column-name",
|
|
1474
|
+
onClick: () => browse(c.field),
|
|
1475
|
+
children: f?.label ?? c.field
|
|
1476
|
+
}
|
|
1477
|
+
),
|
|
1478
|
+
/* @__PURE__ */ jsx3(
|
|
1479
|
+
"button",
|
|
1480
|
+
{
|
|
1481
|
+
type: "button",
|
|
1482
|
+
className: "qt-icon-btn",
|
|
1483
|
+
disabled: i === 0,
|
|
1484
|
+
onClick: () => move(c.field, i - 1),
|
|
1485
|
+
"aria-label": `Move ${f?.label ?? c.field} up`,
|
|
1486
|
+
children: "\u2191"
|
|
1487
|
+
}
|
|
1488
|
+
),
|
|
1489
|
+
/* @__PURE__ */ jsx3(
|
|
1490
|
+
"button",
|
|
1491
|
+
{
|
|
1492
|
+
type: "button",
|
|
1493
|
+
className: "qt-icon-btn",
|
|
1494
|
+
disabled: i === columns.length - 1,
|
|
1495
|
+
onClick: () => move(c.field, i + 1),
|
|
1496
|
+
"aria-label": `Move ${f?.label ?? c.field} down`,
|
|
1497
|
+
children: "\u2193"
|
|
1498
|
+
}
|
|
1499
|
+
),
|
|
1500
|
+
/* @__PURE__ */ jsx3(
|
|
1501
|
+
"button",
|
|
1502
|
+
{
|
|
1503
|
+
type: "button",
|
|
1504
|
+
className: "qt-icon-btn",
|
|
1505
|
+
onClick: () => toggle(c.field),
|
|
1506
|
+
"aria-label": `Remove ${f?.label ?? c.field}`,
|
|
1507
|
+
children: "\xD7"
|
|
1508
|
+
}
|
|
1509
|
+
)
|
|
1510
|
+
]
|
|
1511
|
+
},
|
|
1512
|
+
c.field
|
|
1513
|
+
);
|
|
1514
|
+
}) }),
|
|
1515
|
+
/* @__PURE__ */ jsx3(
|
|
1516
|
+
"button",
|
|
1517
|
+
{
|
|
1518
|
+
type: "button",
|
|
1519
|
+
className: "qt-link-btn",
|
|
1520
|
+
onClick: () => setColumns(
|
|
1521
|
+
api.defaults.select.length ? api.defaults.select : catalogue.filter((f) => f.select?.default).map((f) => ({ field: f.name }))
|
|
1522
|
+
),
|
|
1523
|
+
children: "Use default columns"
|
|
1524
|
+
}
|
|
1525
|
+
)
|
|
1526
|
+
] }),
|
|
1527
|
+
/* @__PURE__ */ jsxs3("aside", { className: "qt-catalogue-panel", children: [
|
|
1528
|
+
/* @__PURE__ */ jsx3("h3", { children: "Column catalogue" }),
|
|
1529
|
+
/* @__PURE__ */ jsx3(
|
|
1530
|
+
"input",
|
|
1531
|
+
{
|
|
1532
|
+
className: "qt-input",
|
|
1533
|
+
"aria-label": "Search columns",
|
|
1534
|
+
placeholder: "Search name or type\u2026",
|
|
1535
|
+
value: search,
|
|
1536
|
+
onChange: (e) => setSearch(e.target.value)
|
|
1537
|
+
}
|
|
1538
|
+
),
|
|
1539
|
+
/* @__PURE__ */ jsx3(
|
|
1540
|
+
"button",
|
|
1541
|
+
{
|
|
1542
|
+
type: "button",
|
|
1543
|
+
className: "qt-btn qt-create-computed",
|
|
1544
|
+
onClick: () => edit(),
|
|
1545
|
+
children: "\uFF0B Computed column"
|
|
1546
|
+
}
|
|
1547
|
+
),
|
|
1548
|
+
api.computed.loading && /* @__PURE__ */ jsx3("p", { role: "status", children: "Loading definitions\u2026" }),
|
|
1549
|
+
api.computed.error && /* @__PURE__ */ jsx3("p", { role: "alert", children: api.computed.error }),
|
|
1550
|
+
/* @__PURE__ */ jsx3("div", { className: "qt-catalogue-list", children: catalogue.filter(
|
|
1551
|
+
(f) => `${f.label} ${f.name} ${f.type} ${f.group ?? ""}`.toLowerCase().includes(search.toLowerCase())
|
|
1552
|
+
).map((f) => /* @__PURE__ */ jsxs3(
|
|
1553
|
+
"div",
|
|
1554
|
+
{
|
|
1555
|
+
className: `qt-catalogue-item ${active === f.name && !editing ? "is-active" : ""}`,
|
|
1556
|
+
children: [
|
|
1557
|
+
/* @__PURE__ */ jsx3(
|
|
1558
|
+
"input",
|
|
1559
|
+
{
|
|
1560
|
+
type: "checkbox",
|
|
1561
|
+
"aria-label": `Include ${f.label}`,
|
|
1562
|
+
checked: columns.some((c) => c.field === f.name),
|
|
1563
|
+
onChange: () => toggle(f.name)
|
|
1564
|
+
}
|
|
1565
|
+
),
|
|
1566
|
+
/* @__PURE__ */ jsxs3("button", { type: "button", onClick: () => browse(f.name), children: [
|
|
1567
|
+
/* @__PURE__ */ jsx3("strong", { children: f.label }),
|
|
1568
|
+
/* @__PURE__ */ jsxs3("small", { children: [
|
|
1569
|
+
isComputedField(f.name) ? "\u0192 \xB7 " : "",
|
|
1570
|
+
f.type,
|
|
1571
|
+
" \xB7 ",
|
|
1572
|
+
f.group ?? f.name
|
|
1573
|
+
] })
|
|
1574
|
+
] })
|
|
1575
|
+
]
|
|
1576
|
+
},
|
|
1577
|
+
f.name
|
|
1578
|
+
)) }),
|
|
1579
|
+
/* @__PURE__ */ jsx3(
|
|
1580
|
+
"button",
|
|
1581
|
+
{
|
|
1582
|
+
type: "button",
|
|
1583
|
+
className: "qt-link-btn",
|
|
1584
|
+
onClick: () => void api.computed.reload(),
|
|
1585
|
+
children: "Reload shared definitions"
|
|
1586
|
+
}
|
|
1587
|
+
)
|
|
1588
|
+
] }),
|
|
1589
|
+
/* @__PURE__ */ jsxs3("section", { className: "qt-column-workbench", children: [
|
|
1590
|
+
editing ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1591
|
+
/* @__PURE__ */ jsxs3("div", { className: "qt-editor-title", children: [
|
|
1592
|
+
/* @__PURE__ */ jsx3("h3", { children: revision ? "Edit shared computed column" : "New computed column" }),
|
|
1593
|
+
/* @__PURE__ */ jsx3("span", { className: "qt-type-badge", children: validation.plan?.type ?? "Check formula" })
|
|
1594
|
+
] }),
|
|
1595
|
+
/* @__PURE__ */ jsxs3("label", { className: "qt-editor-label", children: [
|
|
1596
|
+
"Column name",
|
|
1597
|
+
/* @__PURE__ */ jsx3(
|
|
1598
|
+
"input",
|
|
1599
|
+
{
|
|
1600
|
+
className: "qt-input",
|
|
1601
|
+
value: label,
|
|
1602
|
+
maxLength: 200,
|
|
1603
|
+
onChange: (e) => setLabel(e.target.value),
|
|
1604
|
+
placeholder: "e.g. Email domain"
|
|
1605
|
+
}
|
|
1606
|
+
)
|
|
1607
|
+
] }),
|
|
1608
|
+
/* @__PURE__ */ jsx3(
|
|
1609
|
+
Suspense,
|
|
1610
|
+
{
|
|
1611
|
+
fallback: /* @__PURE__ */ jsx3(
|
|
1612
|
+
"textarea",
|
|
1613
|
+
{
|
|
1614
|
+
"aria-label": "Computed column formula",
|
|
1615
|
+
value: source,
|
|
1616
|
+
onChange: (e) => setSource(e.target.value)
|
|
1617
|
+
}
|
|
1618
|
+
),
|
|
1619
|
+
children: /* @__PURE__ */ jsx3(
|
|
1620
|
+
FormulaEditor,
|
|
1621
|
+
{
|
|
1622
|
+
value: source,
|
|
1623
|
+
onChange: setSource,
|
|
1624
|
+
fields: api.computed.catalogue.filter(
|
|
1625
|
+
(f) => f.name !== computedFieldName(id)
|
|
1626
|
+
),
|
|
1627
|
+
compile: (value) => api.computed.compile(value, id)
|
|
1628
|
+
}
|
|
1629
|
+
)
|
|
1630
|
+
}
|
|
1631
|
+
),
|
|
1632
|
+
/* @__PURE__ */ jsxs3("details", { className: "qt-function-library", children: [
|
|
1633
|
+
/* @__PURE__ */ jsx3("summary", { children: "Functions & examples" }),
|
|
1634
|
+
/* @__PURE__ */ jsx3(
|
|
1635
|
+
"input",
|
|
1636
|
+
{
|
|
1637
|
+
className: "qt-input",
|
|
1638
|
+
"aria-label": "Search functions",
|
|
1639
|
+
placeholder: "Search functions\u2026",
|
|
1640
|
+
value: functionSearch,
|
|
1641
|
+
onChange: (e) => setFunctionSearch(e.target.value)
|
|
1642
|
+
}
|
|
1643
|
+
),
|
|
1644
|
+
/* @__PURE__ */ jsx3("div", { className: "qt-function-list", children: FORMULA_FUNCTIONS.filter(
|
|
1645
|
+
(f) => `${f.name} ${f.description}`.toLowerCase().includes(functionSearch.toLowerCase())
|
|
1646
|
+
).map((f) => /* @__PURE__ */ jsxs3(
|
|
1647
|
+
"button",
|
|
1648
|
+
{
|
|
1649
|
+
type: "button",
|
|
1650
|
+
title: f.description,
|
|
1651
|
+
onClick: () => setSource(`${f.name}(${source})`),
|
|
1652
|
+
children: [
|
|
1653
|
+
/* @__PURE__ */ jsx3("code", { children: f.signature }),
|
|
1654
|
+
/* @__PURE__ */ jsx3("small", { children: f.description })
|
|
1655
|
+
]
|
|
1656
|
+
},
|
|
1657
|
+
f.name
|
|
1658
|
+
)) }),
|
|
1659
|
+
/* @__PURE__ */ jsxs3("p", { children: [
|
|
1660
|
+
"Examples: ",
|
|
1661
|
+
/* @__PURE__ */ jsx3("code", { children: "LEFT([name], 3)" }),
|
|
1662
|
+
" \xB7",
|
|
1663
|
+
" ",
|
|
1664
|
+
/* @__PURE__ */ jsx3("code", { children: 'IF([amount] > 100, "High", "Low")' }),
|
|
1665
|
+
" \xB7",
|
|
1666
|
+
" ",
|
|
1667
|
+
/* @__PURE__ */ jsx3("code", { children: 'REGEX_EXTRACT([email], "@(.+)$", 1)' })
|
|
1668
|
+
] })
|
|
1669
|
+
] }),
|
|
1670
|
+
/* @__PURE__ */ jsxs3("div", { className: "qt-definition-actions", children: [
|
|
1671
|
+
/* @__PURE__ */ jsx3(
|
|
1672
|
+
"button",
|
|
1673
|
+
{
|
|
1674
|
+
type: "button",
|
|
1675
|
+
className: "qt-btn",
|
|
1676
|
+
disabled: saving || !!validation.error || !label.trim(),
|
|
1677
|
+
onClick: () => void save(),
|
|
1678
|
+
children: saving ? "Saving\u2026" : revision ? "Save shared definition" : "Save definition & add column"
|
|
1679
|
+
}
|
|
1680
|
+
),
|
|
1681
|
+
revision && /* @__PURE__ */ jsx3(
|
|
1682
|
+
"button",
|
|
1683
|
+
{
|
|
1684
|
+
type: "button",
|
|
1685
|
+
className: "qt-link-btn",
|
|
1686
|
+
onClick: () => edit(id),
|
|
1687
|
+
children: "Reload editor from catalogue"
|
|
1688
|
+
}
|
|
1689
|
+
)
|
|
1690
|
+
] }),
|
|
1691
|
+
/* @__PURE__ */ jsx3("p", { className: "qt-muted", children: "Saving changes this definition across all queries. Cancel below only discards column layout changes." })
|
|
1692
|
+
] }) : /* @__PURE__ */ jsxs3("div", { className: "qt-editor-title", children: [
|
|
1693
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1694
|
+
/* @__PURE__ */ jsx3("h3", { children: chosen?.label ?? "Explore a column" }),
|
|
1695
|
+
/* @__PURE__ */ jsxs3("p", { className: "qt-muted", children: [
|
|
1696
|
+
chosen?.name,
|
|
1697
|
+
" ",
|
|
1698
|
+
chosen ? `\xB7 ${chosen.type}` : ""
|
|
1699
|
+
] })
|
|
1700
|
+
] }),
|
|
1701
|
+
chosen && isComputedField(chosen.name) && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1702
|
+
/* @__PURE__ */ jsx3(
|
|
1703
|
+
"button",
|
|
1704
|
+
{
|
|
1705
|
+
type: "button",
|
|
1706
|
+
className: "qt-btn",
|
|
1707
|
+
onClick: () => edit(chosen.name.slice(10)),
|
|
1708
|
+
children: "Edit definition"
|
|
1709
|
+
}
|
|
1710
|
+
),
|
|
1711
|
+
/* @__PURE__ */ jsx3(
|
|
1712
|
+
"button",
|
|
1713
|
+
{
|
|
1714
|
+
type: "button",
|
|
1715
|
+
className: "qt-btn",
|
|
1716
|
+
onClick: () => {
|
|
1717
|
+
const def = api.computed.definitions.find(
|
|
1718
|
+
(d) => computedFieldName(d.id) === chosen.name
|
|
1719
|
+
);
|
|
1720
|
+
edit();
|
|
1721
|
+
if (def) {
|
|
1722
|
+
setLabel(`${def.label} copy`);
|
|
1723
|
+
setSource(def.expression.source);
|
|
1724
|
+
}
|
|
1725
|
+
},
|
|
1726
|
+
children: "Duplicate"
|
|
1727
|
+
}
|
|
1728
|
+
)
|
|
1729
|
+
] })
|
|
1730
|
+
] }),
|
|
1731
|
+
/* @__PURE__ */ jsxs3("div", { className: "qt-preview-controls", children: [
|
|
1732
|
+
/* @__PURE__ */ jsxs3("label", { children: [
|
|
1733
|
+
"Rows to process",
|
|
1734
|
+
" ",
|
|
1735
|
+
/* @__PURE__ */ jsx3(
|
|
1736
|
+
"input",
|
|
1737
|
+
{
|
|
1738
|
+
className: "qt-input",
|
|
1739
|
+
type: "number",
|
|
1740
|
+
min: 1,
|
|
1741
|
+
max: 1e4,
|
|
1742
|
+
step: 1,
|
|
1743
|
+
value: Number.isNaN(count) ? "" : count,
|
|
1744
|
+
onChange: (e) => setCount(e.target.valueAsNumber)
|
|
1745
|
+
}
|
|
1746
|
+
)
|
|
1747
|
+
] }),
|
|
1748
|
+
/* @__PURE__ */ jsx3(
|
|
1749
|
+
"button",
|
|
1750
|
+
{
|
|
1751
|
+
type: "button",
|
|
1752
|
+
className: "qt-btn",
|
|
1753
|
+
onClick: () => setRetry((n) => n + 1),
|
|
1754
|
+
children: "Refresh preview"
|
|
1755
|
+
}
|
|
1756
|
+
),
|
|
1757
|
+
/* @__PURE__ */ jsxs3("label", { children: [
|
|
1758
|
+
"Show",
|
|
1759
|
+
" ",
|
|
1760
|
+
/* @__PURE__ */ jsxs3(
|
|
1761
|
+
"select",
|
|
1762
|
+
{
|
|
1763
|
+
value: filter,
|
|
1764
|
+
onChange: (e) => {
|
|
1765
|
+
setFilter(e.target.value);
|
|
1766
|
+
setPage(0);
|
|
1767
|
+
},
|
|
1768
|
+
children: [
|
|
1769
|
+
/* @__PURE__ */ jsx3("option", { value: "all", children: "All results" }),
|
|
1770
|
+
/* @__PURE__ */ jsx3("option", { value: "nulls", children: "Null results" }),
|
|
1771
|
+
/* @__PURE__ */ jsx3("option", { value: "errors", children: "Errors" })
|
|
1772
|
+
]
|
|
1773
|
+
}
|
|
1774
|
+
)
|
|
1775
|
+
] })
|
|
1776
|
+
] }),
|
|
1777
|
+
/* @__PURE__ */ jsx3("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." }),
|
|
1778
|
+
(!Number.isInteger(count) || count < 1 || count > 1e4) && /* @__PURE__ */ jsx3("p", { role: "alert", children: "Choose a whole number between 1 and 10,000." }),
|
|
1779
|
+
validation.error && /* @__PURE__ */ jsx3("p", { className: "qt-formula-error", role: "alert", children: validation.error }),
|
|
1780
|
+
error && /* @__PURE__ */ jsx3("p", { className: "qt-formula-error", role: "alert", children: error }),
|
|
1781
|
+
notice && /* @__PURE__ */ jsx3("p", { className: "qt-editor-notice", role: "status", children: notice }),
|
|
1782
|
+
loading && /* @__PURE__ */ jsx3("p", { role: "status", children: "Processing preview\u2026" }),
|
|
1783
|
+
preview && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1784
|
+
/* @__PURE__ */ jsxs3("p", { className: "qt-preview-summary", role: "status", children: [
|
|
1785
|
+
/* @__PURE__ */ jsx3("strong", { children: preview.processed.toLocaleString() }),
|
|
1786
|
+
" of",
|
|
1787
|
+
" ",
|
|
1788
|
+
preview.total.toLocaleString(),
|
|
1789
|
+
" matching rows \xB7",
|
|
1790
|
+
" ",
|
|
1791
|
+
/* @__PURE__ */ jsx3("strong", { children: preview.groups.length.toLocaleString() }),
|
|
1792
|
+
" ",
|
|
1793
|
+
"distinct combinations \xB7 ",
|
|
1794
|
+
preview.nulls.toLocaleString(),
|
|
1795
|
+
" null \xB7 ",
|
|
1796
|
+
preview.errors.toLocaleString(),
|
|
1797
|
+
" errors",
|
|
1798
|
+
preview.processed === preview.total ? " \xB7 complete filtered set" : " \xB7 sample"
|
|
1799
|
+
] }),
|
|
1800
|
+
/* @__PURE__ */ jsx3("div", { className: "qt-preview-table-wrap", children: /* @__PURE__ */ jsxs3("table", { className: "qt-preview-table", children: [
|
|
1801
|
+
/* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs3("tr", { children: [
|
|
1802
|
+
editing && preview.dependencies.map((name, index) => /* @__PURE__ */ jsx3(
|
|
1803
|
+
"th",
|
|
1804
|
+
{
|
|
1805
|
+
"aria-sort": previewAriaSort({
|
|
1806
|
+
kind: "input",
|
|
1807
|
+
index
|
|
1808
|
+
}),
|
|
1809
|
+
children: previewSortHeader(
|
|
1810
|
+
api.computed.catalogue.find(
|
|
1811
|
+
(f) => f.name === name
|
|
1812
|
+
)?.label ?? name,
|
|
1813
|
+
{ kind: "input", index }
|
|
1814
|
+
)
|
|
1815
|
+
},
|
|
1816
|
+
name
|
|
1817
|
+
)),
|
|
1818
|
+
/* @__PURE__ */ jsx3("th", { "aria-sort": previewAriaSort({ kind: "result" }), children: previewSortHeader(editing ? "Result" : "Value", {
|
|
1819
|
+
kind: "result"
|
|
1820
|
+
}) }),
|
|
1821
|
+
/* @__PURE__ */ jsx3("th", { "aria-sort": previewAriaSort({ kind: "count" }), children: previewSortHeader("Count", { kind: "count" }, "desc") }),
|
|
1822
|
+
/* @__PURE__ */ jsx3("th", { "aria-sort": previewAriaSort({ kind: "percentage" }), children: previewSortHeader("%", { kind: "percentage" }, "desc") })
|
|
1823
|
+
] }) }),
|
|
1824
|
+
/* @__PURE__ */ jsx3("tbody", { children: shown.map((group, i) => /* @__PURE__ */ jsxs3("tr", { children: [
|
|
1825
|
+
editing && group.inputs.map((value, j) => /* @__PURE__ */ jsx3("td", { title: display(value), children: display(value) }, j)),
|
|
1826
|
+
/* @__PURE__ */ jsxs3(
|
|
1827
|
+
"td",
|
|
1828
|
+
{
|
|
1829
|
+
className: group.result.error ? "qt-formula-error" : "",
|
|
1830
|
+
title: group.result.error ?? display(group.result.value),
|
|
1831
|
+
children: [
|
|
1832
|
+
group.result.error ?? display(group.result.value),
|
|
1833
|
+
group.result.regex && /* @__PURE__ */ jsx3(
|
|
1834
|
+
"button",
|
|
1835
|
+
{
|
|
1836
|
+
type: "button",
|
|
1837
|
+
className: "qt-link-btn",
|
|
1838
|
+
onClick: () => setInspection(group.result.regex),
|
|
1839
|
+
children: "Inspect match"
|
|
1840
|
+
}
|
|
1841
|
+
)
|
|
1842
|
+
]
|
|
1843
|
+
}
|
|
1844
|
+
),
|
|
1845
|
+
/* @__PURE__ */ jsx3("td", { children: group.count.toLocaleString() }),
|
|
1846
|
+
/* @__PURE__ */ jsxs3("td", { children: [
|
|
1847
|
+
(100 * group.count / preview.processed).toFixed(
|
|
1848
|
+
1
|
|
1849
|
+
),
|
|
1850
|
+
"%"
|
|
1851
|
+
] })
|
|
1852
|
+
] }, i)) })
|
|
1853
|
+
] }) }),
|
|
1854
|
+
inspection && /* @__PURE__ */ jsxs3("div", { className: "qt-regex-inspector", children: [
|
|
1855
|
+
/* @__PURE__ */ jsx3("h4", { children: "Regex match" }),
|
|
1856
|
+
/* @__PURE__ */ jsxs3("pre", { children: [
|
|
1857
|
+
inspection.input.slice(0, inspection.start),
|
|
1858
|
+
/* @__PURE__ */ jsx3("mark", { children: inspection.input.slice(
|
|
1859
|
+
inspection.start,
|
|
1860
|
+
inspection.end
|
|
1861
|
+
) }),
|
|
1862
|
+
inspection.input.slice(inspection.end)
|
|
1863
|
+
] }),
|
|
1864
|
+
/* @__PURE__ */ jsx3("dl", { children: inspection.groups.map((value, i) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
1865
|
+
/* @__PURE__ */ jsxs3("dt", { children: [
|
|
1866
|
+
"Group ",
|
|
1867
|
+
i,
|
|
1868
|
+
i === 0 ? " (whole match)" : ""
|
|
1869
|
+
] }),
|
|
1870
|
+
/* @__PURE__ */ jsx3("dd", { children: display(value) })
|
|
1871
|
+
] }, i)) })
|
|
1872
|
+
] }),
|
|
1873
|
+
!groups.length && /* @__PURE__ */ jsx3("p", { children: "No matching results in this sample." }),
|
|
1874
|
+
groups.length > 50 && /* @__PURE__ */ jsxs3("div", { className: "qt-preview-pages", children: [
|
|
1875
|
+
/* @__PURE__ */ jsx3(
|
|
1876
|
+
"button",
|
|
1877
|
+
{
|
|
1878
|
+
type: "button",
|
|
1879
|
+
className: "qt-btn",
|
|
1880
|
+
disabled: page === 0,
|
|
1881
|
+
onClick: () => setPage((n) => n - 1),
|
|
1882
|
+
children: "Previous"
|
|
1883
|
+
}
|
|
1884
|
+
),
|
|
1885
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
1886
|
+
"Combinations ",
|
|
1887
|
+
page * 50 + 1,
|
|
1888
|
+
"\u2013",
|
|
1889
|
+
Math.min((page + 1) * 50, groups.length),
|
|
1890
|
+
" of",
|
|
1891
|
+
" ",
|
|
1892
|
+
groups.length
|
|
1893
|
+
] }),
|
|
1894
|
+
/* @__PURE__ */ jsx3(
|
|
1895
|
+
"button",
|
|
1896
|
+
{
|
|
1897
|
+
type: "button",
|
|
1898
|
+
className: "qt-btn",
|
|
1899
|
+
disabled: (page + 1) * 50 >= groups.length,
|
|
1900
|
+
onClick: () => setPage((n) => n + 1),
|
|
1901
|
+
children: "Next"
|
|
1902
|
+
}
|
|
1903
|
+
)
|
|
1904
|
+
] })
|
|
1905
|
+
] })
|
|
1906
|
+
] })
|
|
1907
|
+
] }),
|
|
1908
|
+
/* @__PURE__ */ jsxs3("footer", { className: "qt-select-editor-footer", children: [
|
|
1909
|
+
/* @__PURE__ */ jsx3("span", { className: "qt-muted", children: "Computed columns are display-only. Select at least one column." }),
|
|
1910
|
+
/* @__PURE__ */ jsx3("button", { type: "button", className: "qt-btn", onClick: onClose, children: "Cancel layout changes" }),
|
|
1911
|
+
/* @__PURE__ */ jsx3(
|
|
1912
|
+
"button",
|
|
1913
|
+
{
|
|
1914
|
+
type: "button",
|
|
1915
|
+
className: "qt-btn qt-primary",
|
|
1916
|
+
disabled: !columns.length || columns.length > 200,
|
|
1917
|
+
onClick: () => {
|
|
1918
|
+
api.setQuery((q) => ({ ...q, select: columns }));
|
|
1919
|
+
onClose();
|
|
1920
|
+
},
|
|
1921
|
+
children: "Apply columns"
|
|
1922
|
+
}
|
|
1923
|
+
)
|
|
1924
|
+
] })
|
|
1925
|
+
]
|
|
1926
|
+
}
|
|
1927
|
+
) });
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1117
1930
|
// src/QueryBuilder.tsx
|
|
1118
|
-
import { useEffect as
|
|
1931
|
+
import { Fragment as Fragment5, useEffect as useEffect5, useId as useId2, useMemo as useMemo4, useRef as useRef3, useState as useState5 } from "react";
|
|
1119
1932
|
import {
|
|
1120
1933
|
NULLARY_OPS as NULLARY_OPS2,
|
|
1121
1934
|
aggOpNeedsField,
|
|
@@ -1124,16 +1937,19 @@ import {
|
|
|
1124
1937
|
filterValues as filterValuesFor,
|
|
1125
1938
|
isGroupable,
|
|
1126
1939
|
isMeasurable,
|
|
1940
|
+
isOrGroup,
|
|
1941
|
+
opPairsForField as opPairsForField2,
|
|
1942
|
+
predicatesOf,
|
|
1127
1943
|
queriesEqual,
|
|
1128
|
-
isFilterable,
|
|
1129
|
-
isSelectable,
|
|
1944
|
+
isFilterable as isFilterable2,
|
|
1945
|
+
isSelectable as isSelectable2,
|
|
1130
1946
|
isSortable as isSortable2,
|
|
1131
1947
|
opsForField as opsForField2
|
|
1132
1948
|
} from "@pythia-software/query-table-core";
|
|
1133
1949
|
|
|
1134
1950
|
// src/FieldPicker.tsx
|
|
1135
|
-
import { useMemo as
|
|
1136
|
-
import { Fragment as
|
|
1951
|
+
import { useMemo as useMemo3, useState as useState4 } from "react";
|
|
1952
|
+
import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1137
1953
|
var UNGROUPED = "other";
|
|
1138
1954
|
function FieldPicker({
|
|
1139
1955
|
fields,
|
|
@@ -1143,10 +1959,10 @@ function FieldPicker({
|
|
|
1143
1959
|
onPick,
|
|
1144
1960
|
onClose
|
|
1145
1961
|
}) {
|
|
1146
|
-
const [search, setSearch] =
|
|
1962
|
+
const [search, setSearch] = useState4("");
|
|
1147
1963
|
const needle = search.trim().toLowerCase();
|
|
1148
|
-
const excludedSet =
|
|
1149
|
-
const matched =
|
|
1964
|
+
const excludedSet = useMemo3(() => new Set(excluded ?? []), [excluded]);
|
|
1965
|
+
const matched = useMemo3(
|
|
1150
1966
|
() => fields.filter((f) => {
|
|
1151
1967
|
if (excludedSet.has(f.name)) return false;
|
|
1152
1968
|
if (!needle) return true;
|
|
@@ -1154,10 +1970,10 @@ function FieldPicker({
|
|
|
1154
1970
|
}),
|
|
1155
1971
|
[fields, excludedSet, needle]
|
|
1156
1972
|
);
|
|
1157
|
-
const grouped =
|
|
1973
|
+
const grouped = useMemo3(() => groupByGroup(matched), [matched]);
|
|
1158
1974
|
const firstEnabled = matched.find((f) => isEnabled(f, stats, gateOnDistinct));
|
|
1159
|
-
return /* @__PURE__ */
|
|
1160
|
-
/* @__PURE__ */
|
|
1975
|
+
return /* @__PURE__ */ jsxs4("div", { className: "qt-picker", children: [
|
|
1976
|
+
/* @__PURE__ */ jsx4(
|
|
1161
1977
|
"input",
|
|
1162
1978
|
{
|
|
1163
1979
|
autoFocus: true,
|
|
@@ -1176,13 +1992,13 @@ function FieldPicker({
|
|
|
1176
1992
|
onBlur: () => setTimeout(() => onClose?.(), 150)
|
|
1177
1993
|
}
|
|
1178
1994
|
),
|
|
1179
|
-
/* @__PURE__ */
|
|
1180
|
-
/* @__PURE__ */
|
|
1995
|
+
/* @__PURE__ */ jsx4("div", { className: "qt-picker-list", children: matched.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "qt-picker-empty", children: "no matches" }) : grouped.map(([group, fs]) => /* @__PURE__ */ jsxs4("div", { className: "qt-picker-group", children: [
|
|
1996
|
+
/* @__PURE__ */ jsx4("div", { className: "qt-picker-group-label", children: group }),
|
|
1181
1997
|
fs.map((f) => {
|
|
1182
1998
|
const enabled = isEnabled(f, stats, gateOnDistinct);
|
|
1183
1999
|
const s = stats?.[f.name];
|
|
1184
2000
|
const aliasHit = aliasMatch(f, needle);
|
|
1185
|
-
return /* @__PURE__ */
|
|
2001
|
+
return /* @__PURE__ */ jsxs4(
|
|
1186
2002
|
"button",
|
|
1187
2003
|
{
|
|
1188
2004
|
type: "button",
|
|
@@ -1196,20 +2012,20 @@ function FieldPicker({
|
|
|
1196
2012
|
setSearch("");
|
|
1197
2013
|
},
|
|
1198
2014
|
children: [
|
|
1199
|
-
/* @__PURE__ */
|
|
1200
|
-
/* @__PURE__ */
|
|
1201
|
-
/* @__PURE__ */
|
|
2015
|
+
/* @__PURE__ */ jsxs4("span", { className: "qt-picker-row", children: [
|
|
2016
|
+
/* @__PURE__ */ jsx4("span", { className: "qt-picker-label", children: f.label }),
|
|
2017
|
+
/* @__PURE__ */ jsx4("span", { className: "qt-picker-name", children: f.name })
|
|
1202
2018
|
] }),
|
|
1203
|
-
aliasHit && /* @__PURE__ */
|
|
2019
|
+
aliasHit && /* @__PURE__ */ jsxs4("span", { className: "qt-picker-alias", children: [
|
|
1204
2020
|
"matches: ",
|
|
1205
2021
|
aliasHit
|
|
1206
2022
|
] }),
|
|
1207
|
-
s && /* @__PURE__ */
|
|
1208
|
-
s.distinct != null && /* @__PURE__ */
|
|
2023
|
+
s && /* @__PURE__ */ jsxs4("span", { className: "qt-picker-stats", children: [
|
|
2024
|
+
s.distinct != null && /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1209
2025
|
s.distinct,
|
|
1210
2026
|
" distinct"
|
|
1211
2027
|
] }),
|
|
1212
|
-
s.min != null && s.max != null && /* @__PURE__ */
|
|
2028
|
+
s.min != null && s.max != null && /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1213
2029
|
" ",
|
|
1214
2030
|
"\xB7 ",
|
|
1215
2031
|
fmtStat(s.min),
|
|
@@ -1263,17 +2079,17 @@ function fmtStat(v) {
|
|
|
1263
2079
|
}
|
|
1264
2080
|
|
|
1265
2081
|
// src/SavedQueriesModal.tsx
|
|
1266
|
-
import { useEffect as
|
|
1267
|
-
import { jsx as
|
|
2082
|
+
import { useEffect as useEffect4 } from "react";
|
|
2083
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1268
2084
|
function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
1269
|
-
|
|
2085
|
+
useEffect4(() => {
|
|
1270
2086
|
function onKey(e) {
|
|
1271
2087
|
if (e.key === "Escape") onClose();
|
|
1272
2088
|
}
|
|
1273
2089
|
window.addEventListener("keydown", onKey);
|
|
1274
2090
|
return () => window.removeEventListener("keydown", onKey);
|
|
1275
2091
|
}, [onClose]);
|
|
1276
|
-
return /* @__PURE__ */
|
|
2092
|
+
return /* @__PURE__ */ jsx5("div", { className: "qt-modal-backdrop", onClick: onClose, children: /* @__PURE__ */ jsxs5(
|
|
1277
2093
|
"div",
|
|
1278
2094
|
{
|
|
1279
2095
|
className: "qt-modal",
|
|
@@ -1282,12 +2098,12 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1282
2098
|
"aria-modal": "true",
|
|
1283
2099
|
"aria-label": "Saved queries",
|
|
1284
2100
|
children: [
|
|
1285
|
-
/* @__PURE__ */
|
|
1286
|
-
saved.loading ? /* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsx5("h3", { className: "qt-modal-title", children: "Saved queries" }),
|
|
2102
|
+
saved.loading ? /* @__PURE__ */ jsx5("p", { className: "qt-muted", children: "Loading\u2026" }) : saved.items.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "qt-muted", children: "None saved yet. Save the current query from the toolbar." }) : /* @__PURE__ */ jsx5("ul", { className: "qt-saved-list", children: saved.items.map((s) => {
|
|
1287
2103
|
const filters = s.query.where.length;
|
|
1288
2104
|
const isDefault = saved.defaultId === s.id;
|
|
1289
|
-
return /* @__PURE__ */
|
|
1290
|
-
/* @__PURE__ */
|
|
2105
|
+
return /* @__PURE__ */ jsxs5("li", { className: "qt-saved-item", children: [
|
|
2106
|
+
/* @__PURE__ */ jsxs5(
|
|
1291
2107
|
"button",
|
|
1292
2108
|
{
|
|
1293
2109
|
type: "button",
|
|
@@ -1298,8 +2114,8 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1298
2114
|
onClose();
|
|
1299
2115
|
},
|
|
1300
2116
|
children: [
|
|
1301
|
-
/* @__PURE__ */
|
|
1302
|
-
/* @__PURE__ */
|
|
2117
|
+
/* @__PURE__ */ jsx5("span", { className: "qt-saved-name", children: s.name }),
|
|
2118
|
+
/* @__PURE__ */ jsxs5("span", { className: "qt-saved-meta qt-muted", children: [
|
|
1303
2119
|
isDefault ? "Default view \xB7 " : "",
|
|
1304
2120
|
new Date(s.savedAt).toLocaleString(),
|
|
1305
2121
|
" \xB7 ",
|
|
@@ -1310,7 +2126,7 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1310
2126
|
]
|
|
1311
2127
|
}
|
|
1312
2128
|
),
|
|
1313
|
-
isDefault ? /* @__PURE__ */
|
|
2129
|
+
isDefault ? /* @__PURE__ */ jsx5(
|
|
1314
2130
|
"button",
|
|
1315
2131
|
{
|
|
1316
2132
|
type: "button",
|
|
@@ -1319,7 +2135,7 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1319
2135
|
onClick: () => void saved.clearDefault(),
|
|
1320
2136
|
children: "default"
|
|
1321
2137
|
}
|
|
1322
|
-
) : /* @__PURE__ */
|
|
2138
|
+
) : /* @__PURE__ */ jsx5(
|
|
1323
2139
|
"button",
|
|
1324
2140
|
{
|
|
1325
2141
|
type: "button",
|
|
@@ -1329,7 +2145,7 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1329
2145
|
children: "make default"
|
|
1330
2146
|
}
|
|
1331
2147
|
),
|
|
1332
|
-
/* @__PURE__ */
|
|
2148
|
+
/* @__PURE__ */ jsx5(
|
|
1333
2149
|
"button",
|
|
1334
2150
|
{
|
|
1335
2151
|
type: "button",
|
|
@@ -1341,14 +2157,14 @@ function SavedQueriesModal({ saved, onClose, onLoad }) {
|
|
|
1341
2157
|
)
|
|
1342
2158
|
] }, s.id);
|
|
1343
2159
|
}) }),
|
|
1344
|
-
/* @__PURE__ */
|
|
2160
|
+
/* @__PURE__ */ jsx5("div", { className: "qt-modal-footer", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "qt-btn", onClick: onClose, children: "close" }) })
|
|
1345
2161
|
]
|
|
1346
2162
|
}
|
|
1347
2163
|
) });
|
|
1348
2164
|
}
|
|
1349
2165
|
|
|
1350
2166
|
// src/QueryBuilder.tsx
|
|
1351
|
-
import { jsx as
|
|
2167
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1352
2168
|
var cx3 = (...parts) => parts.filter((p) => Boolean(p)).join(" ");
|
|
1353
2169
|
var MAX_AUTO_REFRESH_POLLS = 1e3;
|
|
1354
2170
|
var DEFAULT_AUTO_REFRESH_FREQUENCY_MS = 2e4;
|
|
@@ -1392,8 +2208,8 @@ function formatStatusTime(ms) {
|
|
|
1392
2208
|
return new Date(ms).toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
|
|
1393
2209
|
}
|
|
1394
2210
|
function useFieldHasNull(api, fieldName) {
|
|
1395
|
-
const [hasNull, setHasNull] =
|
|
1396
|
-
|
|
2211
|
+
const [hasNull, setHasNull] = useState5(void 0);
|
|
2212
|
+
useEffect5(() => {
|
|
1397
2213
|
if (!fieldName) return;
|
|
1398
2214
|
let cancelled = false;
|
|
1399
2215
|
void api.filterValues(fieldName, "").then((r) => {
|
|
@@ -1409,17 +2225,27 @@ function useFieldHasNull(api, fieldName) {
|
|
|
1409
2225
|
}, [api, fieldName]);
|
|
1410
2226
|
return hasNull;
|
|
1411
2227
|
}
|
|
1412
|
-
|
|
2228
|
+
var CONVERGED_NEGATIVE_OPS = /* @__PURE__ */ new Set(["!=", "is_not_null", "not_matches_regex"]);
|
|
2229
|
+
function positiveOpsForField(field) {
|
|
2230
|
+
return opsForField2(field).filter((op) => !CONVERGED_NEGATIVE_OPS.has(op));
|
|
2231
|
+
}
|
|
2232
|
+
function predicateAsText(clause, byName) {
|
|
1413
2233
|
const field = byName.get(clause.field)?.label ?? clause.field;
|
|
1414
2234
|
const op = clause.op.replace(/_/g, " ");
|
|
1415
|
-
|
|
2235
|
+
const not = clause.negated ? "not " : "";
|
|
2236
|
+
if (NULLARY_OPS2.has(clause.op)) return `${field} ${not}${op}`;
|
|
1416
2237
|
const value = clause.value === "" ? "''" : clause.value;
|
|
1417
|
-
return `${field} ${op} ${value}`;
|
|
2238
|
+
return `${field} ${not}${op} ${value}`;
|
|
2239
|
+
}
|
|
2240
|
+
function whereTermAsText(term, byName) {
|
|
2241
|
+
if (isOrGroup(term)) return `(${term.any.map((c) => predicateAsText(c, byName)).join(" or ")})`;
|
|
2242
|
+
return predicateAsText(term, byName);
|
|
1418
2243
|
}
|
|
1419
2244
|
function orderByAsText(term, byName) {
|
|
1420
2245
|
const field = byName.get(term.field)?.label ?? term.field;
|
|
2246
|
+
const expression = term.extract ? `regex_extract(${field}, /${term.extract.regex}/)` : field;
|
|
1421
2247
|
const nulls = term.nulls ? ` nulls ${term.nulls}` : "";
|
|
1422
|
-
return `${
|
|
2248
|
+
return `${expression} ${term.dir}${nulls}`;
|
|
1423
2249
|
}
|
|
1424
2250
|
function chipFieldWidthChars(text, fallback = 5, max = 30) {
|
|
1425
2251
|
return Math.min(max, Math.max(fallback, (text || "").length + 1));
|
|
@@ -1432,12 +2258,12 @@ function chipFieldWidthForSelect(text, fallback = 3, max = 30) {
|
|
|
1432
2258
|
return chipFieldWidthChars(text, fallback, max) + 2;
|
|
1433
2259
|
}
|
|
1434
2260
|
function buildCollapsedSummary(where, orderBy, byName) {
|
|
1435
|
-
const whereText = where
|
|
2261
|
+
const whereText = whereClauseSummaryText(where, byName);
|
|
1436
2262
|
const orderText = orderBy.length === 0 ? "(default)" : orderBy.map((term) => orderByAsText(term, byName)).join(", ");
|
|
1437
2263
|
return `WHERE ${whereText} ORDER BY ${orderText}`;
|
|
1438
2264
|
}
|
|
1439
2265
|
function whereClauseSummaryText(where, byName) {
|
|
1440
|
-
return where.length === 0 ? "all rows" : where.map((
|
|
2266
|
+
return where.length === 0 ? "all rows" : where.map((t) => whereTermAsText(t, byName)).join(" and ");
|
|
1441
2267
|
}
|
|
1442
2268
|
function orderBySummaryText(orderBy, byName) {
|
|
1443
2269
|
return orderBy.length === 0 ? "(default)" : orderBy.map((term) => orderByAsText(term, byName)).join(", ");
|
|
@@ -1453,15 +2279,15 @@ function QueryBuilder({
|
|
|
1453
2279
|
defaultCollapsed = false
|
|
1454
2280
|
}) {
|
|
1455
2281
|
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] =
|
|
2282
|
+
const [showSaved, setShowSaved] = useState5(false);
|
|
2283
|
+
const [shareCopied, setShareCopied] = useState5(false);
|
|
2284
|
+
const [showAutoRefresh, setShowAutoRefresh] = useState5(false);
|
|
2285
|
+
const [autoRefreshFrequencyMs, setAutoRefreshFrequencyMs] = useState5(DEFAULT_AUTO_REFRESH_FREQUENCY_MS);
|
|
2286
|
+
const [autoRefreshTurnOffAfterMs, setAutoRefreshTurnOffAfterMs] = useState5(DEFAULT_AUTO_REFRESH_TURN_OFF_AFTER_MS);
|
|
2287
|
+
const [editingSavedId, setEditingSavedId] = useState5(null);
|
|
2288
|
+
const [editingSavedName, setEditingSavedName] = useState5("");
|
|
2289
|
+
const [lastSavedId, setLastSavedId] = useState5(null);
|
|
2290
|
+
const [collapsedFallback, setCollapsedFallback] = useState5(defaultCollapsed);
|
|
1465
2291
|
const collapsedControlled = typeof collapsed !== "undefined";
|
|
1466
2292
|
const isCollapsed = collapsedControlled ? collapsed : collapsedFallback;
|
|
1467
2293
|
const setCollapsed = (next) => {
|
|
@@ -1471,31 +2297,31 @@ function QueryBuilder({
|
|
|
1471
2297
|
}
|
|
1472
2298
|
setCollapsedFallback(next);
|
|
1473
2299
|
};
|
|
1474
|
-
const [lastUpdatedAt, setLastUpdatedAt] =
|
|
1475
|
-
const [lastFailureAt, setLastFailureAt] =
|
|
1476
|
-
const byName =
|
|
1477
|
-
const activeSavedQuery =
|
|
2300
|
+
const [lastUpdatedAt, setLastUpdatedAt] = useState5(null);
|
|
2301
|
+
const [lastFailureAt, setLastFailureAt] = useState5(null);
|
|
2302
|
+
const byName = useMemo4(() => new Map(fields.map((f) => [f.name, f])), [fields]);
|
|
2303
|
+
const activeSavedQuery = useMemo4(
|
|
1478
2304
|
() => api.saved.items.find((item) => queriesEqual(item.query, api.query)),
|
|
1479
2305
|
[api.query, api.saved.items]
|
|
1480
2306
|
);
|
|
1481
|
-
const lastSavedQuery =
|
|
2307
|
+
const lastSavedQuery = useMemo4(
|
|
1482
2308
|
() => api.saved.items.find((item) => item.id === lastSavedId) ?? null,
|
|
1483
2309
|
[api.saved.items, lastSavedId]
|
|
1484
2310
|
);
|
|
1485
2311
|
const isSavedQuery = activeSavedQuery != null;
|
|
1486
2312
|
const isEditingUnsaved = editingSavedId === UNSAVED_QUERY_EDIT_ID;
|
|
1487
|
-
const bodyId =
|
|
2313
|
+
const bodyId = useId2();
|
|
1488
2314
|
const isEditingSaved = editingSavedId != null && activeSavedQuery?.id === editingSavedId;
|
|
1489
2315
|
const showUpdateButton = Boolean(lastSavedQuery) && !activeSavedQuery;
|
|
1490
|
-
const collapsedSummary =
|
|
2316
|
+
const collapsedSummary = useMemo4(
|
|
1491
2317
|
() => buildCollapsedSummary(api.query.where, api.query.orderBy, byName),
|
|
1492
2318
|
[api.query.where, api.query.orderBy, byName]
|
|
1493
2319
|
);
|
|
1494
|
-
const collapsedWhereText =
|
|
2320
|
+
const collapsedWhereText = useMemo4(
|
|
1495
2321
|
() => whereClauseSummaryText(api.query.where, byName),
|
|
1496
2322
|
[api.query.where, byName]
|
|
1497
2323
|
);
|
|
1498
|
-
const collapsedOrderText =
|
|
2324
|
+
const collapsedOrderText = useMemo4(
|
|
1499
2325
|
() => orderBySummaryText(api.query.orderBy, byName),
|
|
1500
2326
|
[api.query.orderBy, byName]
|
|
1501
2327
|
);
|
|
@@ -1507,27 +2333,27 @@ function QueryBuilder({
|
|
|
1507
2333
|
const lastUpdatedText = formatStatusTime(lastUpdatedAt);
|
|
1508
2334
|
const failureText = api.error ? `Error: ${api.error.message}` : lastFailureAt ? `Last failure at ${formatStatusTime(lastFailureAt)}` : null;
|
|
1509
2335
|
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
|
-
|
|
2336
|
+
const lastRowsRef = useRef3(null);
|
|
2337
|
+
const lastTotalRef = useRef3(null);
|
|
2338
|
+
const previousLoadingRef = useRef3(api.loading);
|
|
2339
|
+
const autoRefreshRef = useRef3(null);
|
|
2340
|
+
const autoRefreshPopoverRef = useRef3(null);
|
|
2341
|
+
const [autoRefreshPopoverAlignRight, setAutoRefreshPopoverAlignRight] = useState5(false);
|
|
2342
|
+
const shareFeedbackTimerRef = useRef3(null);
|
|
2343
|
+
useEffect5(
|
|
1518
2344
|
() => () => {
|
|
1519
2345
|
if (shareFeedbackTimerRef.current) clearTimeout(shareFeedbackTimerRef.current);
|
|
1520
2346
|
},
|
|
1521
2347
|
[]
|
|
1522
2348
|
);
|
|
1523
|
-
|
|
2349
|
+
useEffect5(() => {
|
|
1524
2350
|
setShareCopied(false);
|
|
1525
2351
|
if (shareFeedbackTimerRef.current) {
|
|
1526
2352
|
clearTimeout(shareFeedbackTimerRef.current);
|
|
1527
2353
|
shareFeedbackTimerRef.current = null;
|
|
1528
2354
|
}
|
|
1529
2355
|
}, [api.query]);
|
|
1530
|
-
|
|
2356
|
+
useEffect5(() => {
|
|
1531
2357
|
if (activeSavedQuery) {
|
|
1532
2358
|
setLastSavedId(activeSavedQuery.id);
|
|
1533
2359
|
if (!isEditingSaved) {
|
|
@@ -1541,11 +2367,11 @@ function QueryBuilder({
|
|
|
1541
2367
|
setEditingSavedName("");
|
|
1542
2368
|
}
|
|
1543
2369
|
}, [activeSavedQuery, isEditingSaved, isEditingUnsaved]);
|
|
1544
|
-
|
|
2370
|
+
useEffect5(() => {
|
|
1545
2371
|
if (!lastSavedId || api.saved.items.some((item) => item.id === lastSavedId)) return;
|
|
1546
2372
|
setLastSavedId(null);
|
|
1547
2373
|
}, [api.saved.items, lastSavedId]);
|
|
1548
|
-
|
|
2374
|
+
useEffect5(() => {
|
|
1549
2375
|
const hasData = api.rows.length > 0 || total != null;
|
|
1550
2376
|
const rowsChanged = lastRowsRef.current !== api.rows;
|
|
1551
2377
|
const totalChanged = lastTotalRef.current !== total;
|
|
@@ -1640,15 +2466,15 @@ function QueryBuilder({
|
|
|
1640
2466
|
return;
|
|
1641
2467
|
}
|
|
1642
2468
|
}
|
|
1643
|
-
|
|
2469
|
+
useEffect5(() => {
|
|
1644
2470
|
if (isCollapsed) setShowSaved(false);
|
|
1645
2471
|
}, [isCollapsed]);
|
|
1646
|
-
|
|
2472
|
+
useEffect5(() => {
|
|
1647
2473
|
if (!showAutoRefresh || !autoRefreshStatus) return;
|
|
1648
2474
|
setAutoRefreshFrequencyMs(autoRefreshStatus.frequencyMs);
|
|
1649
2475
|
setAutoRefreshTurnOffAfterMs(autoRefreshStatus.turnOffAfterMs);
|
|
1650
2476
|
}, [showAutoRefresh, autoRefreshStatus]);
|
|
1651
|
-
|
|
2477
|
+
useEffect5(() => {
|
|
1652
2478
|
if (!showAutoRefresh) return;
|
|
1653
2479
|
function onDocumentClick(event) {
|
|
1654
2480
|
const wrapper = autoRefreshRef.current;
|
|
@@ -1707,11 +2533,11 @@ function QueryBuilder({
|
|
|
1707
2533
|
window.prompt("Copy this query URL", url.toString());
|
|
1708
2534
|
}
|
|
1709
2535
|
}
|
|
1710
|
-
return /* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
2536
|
+
return /* @__PURE__ */ jsxs6("div", { className: cx3("qt-qb", classNames?.root), children: [
|
|
2537
|
+
/* @__PURE__ */ jsxs6("div", { className: "qt-qb-bar qt-qb-state-bar qt-qb-row", children: [
|
|
2538
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-qb-state-main", children: [
|
|
2539
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-qb-saved", children: [
|
|
2540
|
+
/* @__PURE__ */ jsx6(
|
|
1715
2541
|
"button",
|
|
1716
2542
|
{
|
|
1717
2543
|
type: "button",
|
|
@@ -1722,8 +2548,8 @@ function QueryBuilder({
|
|
|
1722
2548
|
children: isSavedQuery ? "\u2605" : "\u2606"
|
|
1723
2549
|
}
|
|
1724
2550
|
),
|
|
1725
|
-
isEditingSaved || isEditingUnsaved ? /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
2551
|
+
isEditingSaved || isEditingUnsaved ? /* @__PURE__ */ jsxs6("span", { className: "qt-qb-saved-input-wrap", children: [
|
|
2552
|
+
/* @__PURE__ */ jsx6(
|
|
1727
2553
|
"input",
|
|
1728
2554
|
{
|
|
1729
2555
|
type: "text",
|
|
@@ -1746,7 +2572,7 @@ function QueryBuilder({
|
|
|
1746
2572
|
onClick: (e) => e.stopPropagation()
|
|
1747
2573
|
}
|
|
1748
2574
|
),
|
|
1749
|
-
/* @__PURE__ */
|
|
2575
|
+
/* @__PURE__ */ jsx6(
|
|
1750
2576
|
"button",
|
|
1751
2577
|
{
|
|
1752
2578
|
type: "button",
|
|
@@ -1760,7 +2586,7 @@ function QueryBuilder({
|
|
|
1760
2586
|
children: "\u2715"
|
|
1761
2587
|
}
|
|
1762
2588
|
)
|
|
1763
|
-
] }) : /* @__PURE__ */
|
|
2589
|
+
] }) : /* @__PURE__ */ jsx6(
|
|
1764
2590
|
"button",
|
|
1765
2591
|
{
|
|
1766
2592
|
type: "button",
|
|
@@ -1770,17 +2596,17 @@ function QueryBuilder({
|
|
|
1770
2596
|
}
|
|
1771
2597
|
)
|
|
1772
2598
|
] }),
|
|
1773
|
-
isCollapsed ? /* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
2599
|
+
isCollapsed ? /* @__PURE__ */ jsxs6("span", { className: "qt-qb-summary qt-truncate", title: collapsedSummary, children: [
|
|
2600
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-summary-kw", children: "WHERE" }),
|
|
1775
2601
|
" ",
|
|
1776
|
-
/* @__PURE__ */
|
|
2602
|
+
/* @__PURE__ */ jsx6("strong", { children: collapsedWhereText }),
|
|
1777
2603
|
" ",
|
|
1778
|
-
/* @__PURE__ */
|
|
2604
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-summary-kw", children: "ORDER BY" }),
|
|
1779
2605
|
" ",
|
|
1780
|
-
/* @__PURE__ */
|
|
2606
|
+
/* @__PURE__ */ jsx6("strong", { children: collapsedOrderText })
|
|
1781
2607
|
] }) : null
|
|
1782
2608
|
] }),
|
|
1783
|
-
showUpdateButton ? /* @__PURE__ */
|
|
2609
|
+
showUpdateButton ? /* @__PURE__ */ jsx6(
|
|
1784
2610
|
"button",
|
|
1785
2611
|
{
|
|
1786
2612
|
type: "button",
|
|
@@ -1790,7 +2616,7 @@ function QueryBuilder({
|
|
|
1790
2616
|
children: `Update Query '${lastSavedQuery?.name ?? "query"}'`
|
|
1791
2617
|
}
|
|
1792
2618
|
) : null,
|
|
1793
|
-
/* @__PURE__ */
|
|
2619
|
+
/* @__PURE__ */ jsx6(
|
|
1794
2620
|
"button",
|
|
1795
2621
|
{
|
|
1796
2622
|
type: "button",
|
|
@@ -1801,11 +2627,11 @@ function QueryBuilder({
|
|
|
1801
2627
|
children: shareCopied ? "Copied!" : "Share"
|
|
1802
2628
|
}
|
|
1803
2629
|
),
|
|
1804
|
-
/* @__PURE__ */
|
|
2630
|
+
/* @__PURE__ */ jsxs6("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: () => setShowSaved(true), children: [
|
|
1805
2631
|
"Saved",
|
|
1806
2632
|
api.saved.items.length > 0 ? ` (${api.saved.items.length})` : ""
|
|
1807
2633
|
] }),
|
|
1808
|
-
/* @__PURE__ */
|
|
2634
|
+
/* @__PURE__ */ jsxs6(
|
|
1809
2635
|
"button",
|
|
1810
2636
|
{
|
|
1811
2637
|
type: "button",
|
|
@@ -1815,7 +2641,7 @@ function QueryBuilder({
|
|
|
1815
2641
|
"aria-controls": bodyId,
|
|
1816
2642
|
title: isCollapsed ? "Expand query builder" : "Collapse query builder",
|
|
1817
2643
|
children: [
|
|
1818
|
-
/* @__PURE__ */
|
|
2644
|
+
/* @__PURE__ */ jsx6(
|
|
1819
2645
|
"span",
|
|
1820
2646
|
{
|
|
1821
2647
|
className: cx3("qt-qb-collapse-caret", isCollapsed && "qt-qb-collapse-caret--collapsed"),
|
|
@@ -1823,38 +2649,38 @@ function QueryBuilder({
|
|
|
1823
2649
|
children: "\u25BE"
|
|
1824
2650
|
}
|
|
1825
2651
|
),
|
|
1826
|
-
/* @__PURE__ */
|
|
2652
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-sr-only", children: isCollapsed ? "Expand query builder" : "Collapse query builder" })
|
|
1827
2653
|
]
|
|
1828
2654
|
}
|
|
1829
2655
|
)
|
|
1830
2656
|
] }),
|
|
1831
|
-
!isCollapsed ? /* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */
|
|
1833
|
-
/* @__PURE__ */
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
1836
|
-
/* @__PURE__ */
|
|
1837
|
-
/* @__PURE__ */
|
|
2657
|
+
!isCollapsed ? /* @__PURE__ */ jsxs6("div", { className: "qt-qb-bar qt-qb-editor-bar", children: [
|
|
2658
|
+
/* @__PURE__ */ jsxs6("div", { id: bodyId, className: "qt-qb-body", children: [
|
|
2659
|
+
/* @__PURE__ */ jsx6(SelectRow, { api, fields, classNames, disabled: isBusy }),
|
|
2660
|
+
/* @__PURE__ */ jsx6(WhereRow, { api, byName, fields, classNames, disabled: isBusy }),
|
|
2661
|
+
/* @__PURE__ */ jsx6(OrderRow, { api, fields, classNames, disabled: isBusy }),
|
|
2662
|
+
/* @__PURE__ */ jsx6(WindowRow, { api, classNames, disabled: isBusy }),
|
|
2663
|
+
/* @__PURE__ */ jsx6(MetricsRow, { api, fields, classNames, disabled: isBusy })
|
|
1838
2664
|
] }),
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
/* @__PURE__ */
|
|
1841
|
-
/* @__PURE__ */
|
|
1842
|
-
/* @__PURE__ */
|
|
2665
|
+
/* @__PURE__ */ jsxs6("div", { className: "qt-qb-editor-stack", children: [
|
|
2666
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: api.undo, disabled: !api.canUndo || isBusy, children: "\u21B6 Undo" }),
|
|
2667
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: api.redo, disabled: !api.canRedo || isBusy, children: "\u21B7 Redo" }),
|
|
2668
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: cx3("qt-btn", classNames?.button), onClick: api.resetAll, disabled: isBusy, children: "Reset All" })
|
|
1843
2669
|
] })
|
|
1844
2670
|
] }) : null,
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
/* @__PURE__ */
|
|
1847
|
-
/* @__PURE__ */
|
|
2671
|
+
/* @__PURE__ */ jsxs6("div", { className: "qt-qb-bar qt-qb-run-bar qt-qb-row", children: [
|
|
2672
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-run-metric qt-qb-run-metric--strong", children: api.loading ? "loading\u2026" : `${rowSummary} rows` }),
|
|
2673
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-qb-run-metric", children: [
|
|
1848
2674
|
"Last updated ",
|
|
1849
2675
|
lastUpdatedText
|
|
1850
2676
|
] }),
|
|
1851
|
-
failureText ? /* @__PURE__ */
|
|
1852
|
-
/* @__PURE__ */
|
|
1853
|
-
autoRefreshStatus ? /* @__PURE__ */
|
|
2677
|
+
failureText ? /* @__PURE__ */ jsx6("span", { className: "qt-qb-run-error", children: failureText }) : null,
|
|
2678
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-qb-run-actions", children: [
|
|
2679
|
+
autoRefreshStatus ? /* @__PURE__ */ jsx6("span", { className: "qt-qb-auto-status", children: `Every ${formatDuration2(autoRefreshStatus.frequencyMs)}; clear after ${formatDuration2(
|
|
1854
2680
|
autoRefreshStatus.turnOffAfterMs
|
|
1855
2681
|
)} (${autoRefreshStatus.pollCount} polls run)` }) : null,
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
2682
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-auto-refresh", ref: autoRefreshRef, children: [
|
|
2683
|
+
/* @__PURE__ */ jsx6(
|
|
1858
2684
|
"button",
|
|
1859
2685
|
{
|
|
1860
2686
|
type: "button",
|
|
@@ -1865,7 +2691,7 @@ function QueryBuilder({
|
|
|
1865
2691
|
children: autoRefreshButtonText
|
|
1866
2692
|
}
|
|
1867
2693
|
),
|
|
1868
|
-
showAutoRefresh ? /* @__PURE__ */
|
|
2694
|
+
showAutoRefresh ? /* @__PURE__ */ jsxs6(
|
|
1869
2695
|
"span",
|
|
1870
2696
|
{
|
|
1871
2697
|
ref: autoRefreshPopoverRef,
|
|
@@ -1873,27 +2699,27 @@ function QueryBuilder({
|
|
|
1873
2699
|
role: "dialog",
|
|
1874
2700
|
"aria-label": "Auto-refresh settings",
|
|
1875
2701
|
children: [
|
|
1876
|
-
/* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
2702
|
+
/* @__PURE__ */ jsxs6("label", { className: "qt-auto-refresh-field", children: [
|
|
2703
|
+
/* @__PURE__ */ jsx6("span", { children: "Frequency" }),
|
|
2704
|
+
/* @__PURE__ */ jsx6(
|
|
1879
2705
|
"select",
|
|
1880
2706
|
{
|
|
1881
2707
|
className: "qt-auto-refresh-select",
|
|
1882
2708
|
value: autoRefreshFrequencyMs,
|
|
1883
2709
|
onChange: (e) => setAutoRefreshFrequencyMs(Number(e.target.value)),
|
|
1884
|
-
children: AUTO_REFRESH_FREQUENCIES.map((option) => /* @__PURE__ */
|
|
2710
|
+
children: AUTO_REFRESH_FREQUENCIES.map((option) => /* @__PURE__ */ jsx6("option", { value: option.value, children: option.label }, option.value))
|
|
1885
2711
|
}
|
|
1886
2712
|
)
|
|
1887
2713
|
] }),
|
|
1888
|
-
/* @__PURE__ */
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
2714
|
+
/* @__PURE__ */ jsxs6("label", { className: "qt-auto-refresh-field", children: [
|
|
2715
|
+
/* @__PURE__ */ jsx6("span", { children: "Turn off after" }),
|
|
2716
|
+
/* @__PURE__ */ jsx6(
|
|
1891
2717
|
"select",
|
|
1892
2718
|
{
|
|
1893
2719
|
className: "qt-auto-refresh-select",
|
|
1894
2720
|
value: autoRefreshTurnOffAfterMs,
|
|
1895
2721
|
onChange: (e) => setAutoRefreshTurnOffAfterMs(Number(e.target.value)),
|
|
1896
|
-
children: AUTO_REFRESH_TURN_OFF_AFTER.map((option) => /* @__PURE__ */
|
|
2722
|
+
children: AUTO_REFRESH_TURN_OFF_AFTER.map((option) => /* @__PURE__ */ jsx6(
|
|
1897
2723
|
"option",
|
|
1898
2724
|
{
|
|
1899
2725
|
value: option.value,
|
|
@@ -1905,8 +2731,8 @@ function QueryBuilder({
|
|
|
1905
2731
|
}
|
|
1906
2732
|
)
|
|
1907
2733
|
] }),
|
|
1908
|
-
/* @__PURE__ */
|
|
1909
|
-
autoRefreshStatus ? /* @__PURE__ */
|
|
2734
|
+
/* @__PURE__ */ jsx6("span", { className: cx3("qt-auto-refresh-status", !canSubmitAutoRefresh && "qt-auto-refresh-status--error"), children: canSubmitAutoRefresh ? `${selectedAutoRefreshPolls} polls maximum` : `Choose between 1 and 1000 polls` }),
|
|
2735
|
+
autoRefreshStatus ? /* @__PURE__ */ jsxs6("span", { className: "qt-auto-refresh-status", children: [
|
|
1910
2736
|
"Active: every ",
|
|
1911
2737
|
formatDuration2(autoRefreshStatus.frequencyMs),
|
|
1912
2738
|
", clears after",
|
|
@@ -1916,8 +2742,8 @@ function QueryBuilder({
|
|
|
1916
2742
|
autoRefreshStatus.pollCount,
|
|
1917
2743
|
" polls run."
|
|
1918
2744
|
] }) : null,
|
|
1919
|
-
/* @__PURE__ */
|
|
1920
|
-
/* @__PURE__ */
|
|
2745
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-auto-refresh-actions", children: [
|
|
2746
|
+
/* @__PURE__ */ jsx6(
|
|
1921
2747
|
"button",
|
|
1922
2748
|
{
|
|
1923
2749
|
type: "button",
|
|
@@ -1927,7 +2753,7 @@ function QueryBuilder({
|
|
|
1927
2753
|
children: autoRefreshStatus ? "Update" : "Start"
|
|
1928
2754
|
}
|
|
1929
2755
|
),
|
|
1930
|
-
autoRefreshStatus ? /* @__PURE__ */
|
|
2756
|
+
autoRefreshStatus ? /* @__PURE__ */ jsx6(
|
|
1931
2757
|
"button",
|
|
1932
2758
|
{
|
|
1933
2759
|
type: "button",
|
|
@@ -1944,7 +2770,7 @@ function QueryBuilder({
|
|
|
1944
2770
|
}
|
|
1945
2771
|
) : null
|
|
1946
2772
|
] }),
|
|
1947
|
-
/* @__PURE__ */
|
|
2773
|
+
/* @__PURE__ */ jsx6(
|
|
1948
2774
|
"button",
|
|
1949
2775
|
{
|
|
1950
2776
|
type: "button",
|
|
@@ -1956,7 +2782,7 @@ function QueryBuilder({
|
|
|
1956
2782
|
)
|
|
1957
2783
|
] })
|
|
1958
2784
|
] }),
|
|
1959
|
-
!isCollapsed && showSaved ? /* @__PURE__ */
|
|
2785
|
+
!isCollapsed && showSaved ? /* @__PURE__ */ jsx6(SavedQueriesModal, { saved: api.saved, onClose: () => setShowSaved(false), onLoad: setLastSavedId }) : null
|
|
1960
2786
|
] });
|
|
1961
2787
|
}
|
|
1962
2788
|
function MoveButtons({
|
|
@@ -1967,8 +2793,8 @@ function MoveButtons({
|
|
|
1967
2793
|
onMove
|
|
1968
2794
|
}) {
|
|
1969
2795
|
if (count < 2) return null;
|
|
1970
|
-
return /* @__PURE__ */
|
|
1971
|
-
/* @__PURE__ */
|
|
2796
|
+
return /* @__PURE__ */ jsxs6("span", { className: "qt-chip-move-buttons", children: [
|
|
2797
|
+
/* @__PURE__ */ jsx6(
|
|
1972
2798
|
"button",
|
|
1973
2799
|
{
|
|
1974
2800
|
type: "button",
|
|
@@ -1980,7 +2806,7 @@ function MoveButtons({
|
|
|
1980
2806
|
children: "\u2190"
|
|
1981
2807
|
}
|
|
1982
2808
|
),
|
|
1983
|
-
/* @__PURE__ */
|
|
2809
|
+
/* @__PURE__ */ jsx6(
|
|
1984
2810
|
"button",
|
|
1985
2811
|
{
|
|
1986
2812
|
type: "button",
|
|
@@ -2001,19 +2827,22 @@ function SelectRow({
|
|
|
2001
2827
|
disabled
|
|
2002
2828
|
}) {
|
|
2003
2829
|
const { select, columnDrag } = api;
|
|
2004
|
-
const [
|
|
2005
|
-
const
|
|
2830
|
+
const [editingColumns, setEditingColumns] = useState5(false);
|
|
2831
|
+
const [adding, setAdding] = useState5(false);
|
|
2832
|
+
const fieldLabelByName = useMemo4(() => new Map(select.fields.map((f) => [f.name, f.label])), [select.fields]);
|
|
2006
2833
|
function reorderByIndex(from, overIndex) {
|
|
2007
2834
|
select.move(from, overIndex);
|
|
2008
2835
|
}
|
|
2009
2836
|
const fieldNames = select.visible.map((c) => c.field);
|
|
2010
2837
|
const dragSource = columnDrag.source;
|
|
2011
2838
|
const rendered = columnDrag.preview(fieldNames);
|
|
2012
|
-
return /* @__PURE__ */
|
|
2013
|
-
/* @__PURE__ */
|
|
2839
|
+
return /* @__PURE__ */ jsxs6("div", { className: "qt-qb-row", children: [
|
|
2840
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-kw", children: "select" }),
|
|
2841
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-btn", disabled, onClick: () => setEditingColumns(true), children: "Edit columns" }),
|
|
2842
|
+
editingColumns && /* @__PURE__ */ jsx6(SelectColumnEditor, { api, onClose: () => setEditingColumns(false) }),
|
|
2014
2843
|
rendered.map((name, position) => {
|
|
2015
2844
|
const label = fieldLabelByName.get(name) ?? name;
|
|
2016
|
-
return /* @__PURE__ */
|
|
2845
|
+
return /* @__PURE__ */ jsxs6(
|
|
2017
2846
|
"span",
|
|
2018
2847
|
{
|
|
2019
2848
|
className: cx3(
|
|
@@ -2052,9 +2881,9 @@ function SelectRow({
|
|
|
2052
2881
|
onDragEnd: () => columnDrag.end(),
|
|
2053
2882
|
title: "drag to reorder",
|
|
2054
2883
|
children: [
|
|
2055
|
-
/* @__PURE__ */
|
|
2056
|
-
/* @__PURE__ */
|
|
2057
|
-
/* @__PURE__ */
|
|
2884
|
+
/* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "qt-chip-grip", children: "\u22EE\u22EE" }),
|
|
2885
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-chip-field", children: label }),
|
|
2886
|
+
/* @__PURE__ */ jsx6(
|
|
2058
2887
|
MoveButtons,
|
|
2059
2888
|
{
|
|
2060
2889
|
label,
|
|
@@ -2064,23 +2893,23 @@ function SelectRow({
|
|
|
2064
2893
|
onMove: (nextPosition) => select.move(name, nextPosition)
|
|
2065
2894
|
}
|
|
2066
2895
|
),
|
|
2067
|
-
/* @__PURE__ */
|
|
2896
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-chip-x", onClick: () => select.hide(name), disabled, children: "\u2715" })
|
|
2068
2897
|
]
|
|
2069
2898
|
},
|
|
2070
2899
|
name
|
|
2071
2900
|
);
|
|
2072
2901
|
}),
|
|
2073
|
-
adding ? /* @__PURE__ */
|
|
2902
|
+
adding ? /* @__PURE__ */ jsx6(
|
|
2074
2903
|
FieldPicker,
|
|
2075
2904
|
{
|
|
2076
|
-
fields: select.hidden.filter(
|
|
2905
|
+
fields: select.hidden.filter(isSelectable2),
|
|
2077
2906
|
onPick: (f) => {
|
|
2078
2907
|
select.show(f.name);
|
|
2079
2908
|
setAdding(false);
|
|
2080
2909
|
},
|
|
2081
2910
|
onClose: () => setAdding(false)
|
|
2082
2911
|
}
|
|
2083
|
-
) : /* @__PURE__ */
|
|
2912
|
+
) : /* @__PURE__ */ jsx6(
|
|
2084
2913
|
"button",
|
|
2085
2914
|
{
|
|
2086
2915
|
type: "button",
|
|
@@ -2090,9 +2919,16 @@ function SelectRow({
|
|
|
2090
2919
|
children: "+ add column"
|
|
2091
2920
|
}
|
|
2092
2921
|
),
|
|
2093
|
-
api.query.select.length > 0 && /* @__PURE__ */
|
|
2922
|
+
api.query.select.length > 0 && /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-link-btn", onClick: select.reset, disabled, title: "Reset to default columns", children: "reset" })
|
|
2094
2923
|
] });
|
|
2095
2924
|
}
|
|
2925
|
+
function dropModeFor(e) {
|
|
2926
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
2927
|
+
const x = e.clientX - rect.left;
|
|
2928
|
+
if (x < rect.width * 0.3) return "before";
|
|
2929
|
+
if (x > rect.width * 0.7) return "after";
|
|
2930
|
+
return "merge";
|
|
2931
|
+
}
|
|
2096
2932
|
function WhereRow({
|
|
2097
2933
|
api,
|
|
2098
2934
|
byName,
|
|
@@ -2100,120 +2936,295 @@ function WhereRow({
|
|
|
2100
2936
|
classNames,
|
|
2101
2937
|
disabled
|
|
2102
2938
|
}) {
|
|
2103
|
-
const [adding, setAdding] =
|
|
2939
|
+
const [adding, setAdding] = useState5(false);
|
|
2940
|
+
const [drag, setDrag] = useState5(null);
|
|
2941
|
+
const where = api.query.where;
|
|
2942
|
+
const hasOr = where.some(isOrGroup);
|
|
2104
2943
|
function addClause(f) {
|
|
2105
|
-
const op = opsForField2(f)[0] ?? "=";
|
|
2944
|
+
const op = positiveOpsForField(f)[0] ?? opsForField2(f)[0] ?? "=";
|
|
2106
2945
|
api.addFilter({ field: f.name, op, value: "" });
|
|
2107
2946
|
setAdding(false);
|
|
2108
2947
|
}
|
|
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
|
-
|
|
2948
|
+
function handleOver(e, index) {
|
|
2949
|
+
if (!drag) return;
|
|
2950
|
+
e.preventDefault();
|
|
2951
|
+
e.dataTransfer.dropEffect = "move";
|
|
2952
|
+
const mode = dropModeFor(e);
|
|
2953
|
+
setDrag((d) => d && (d.over?.index !== index || d.over?.mode !== mode) ? { from: d.from, over: { index, mode } } : d);
|
|
2954
|
+
}
|
|
2955
|
+
function handleDrop() {
|
|
2956
|
+
if (!drag || !drag.over) {
|
|
2957
|
+
setDrag(null);
|
|
2958
|
+
return;
|
|
2959
|
+
}
|
|
2960
|
+
const { from } = drag;
|
|
2961
|
+
const { index: target, mode } = drag.over;
|
|
2962
|
+
if (mode === "merge") {
|
|
2963
|
+
if (target !== from) api.mergeFilters(from, target);
|
|
2964
|
+
} else {
|
|
2965
|
+
const tPrime = target < from ? target : target - 1;
|
|
2966
|
+
const insertAt = mode === "after" ? tPrime + 1 : tPrime;
|
|
2967
|
+
if (insertAt !== from) api.reorderFilters(from, insertAt);
|
|
2968
|
+
}
|
|
2969
|
+
setDrag(null);
|
|
2970
|
+
}
|
|
2971
|
+
return /* @__PURE__ */ jsxs6("div", { className: "qt-qb-row", children: [
|
|
2972
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-kw", children: "where" }),
|
|
2973
|
+
where.length === 0 && !adding && /* @__PURE__ */ jsx6("span", { className: "qt-qb-hint", children: "all rows" }),
|
|
2974
|
+
where.map((term, index) => /* @__PURE__ */ jsxs6("span", { className: "qt-where-term-wrap", children: [
|
|
2975
|
+
index > 0 && hasOr && /* @__PURE__ */ jsx6("span", { className: "qt-where-and", children: "AND" }),
|
|
2976
|
+
/* @__PURE__ */ jsx6(
|
|
2977
|
+
TermChip,
|
|
2978
|
+
{
|
|
2979
|
+
api,
|
|
2980
|
+
term,
|
|
2981
|
+
index,
|
|
2982
|
+
byName,
|
|
2983
|
+
classNames,
|
|
2984
|
+
disabled,
|
|
2985
|
+
isDragSource: drag?.from === index,
|
|
2986
|
+
dropHint: drag?.over?.index === index ? drag.over.mode : null,
|
|
2987
|
+
onDragStart: () => setDrag({ from: index, over: null }),
|
|
2988
|
+
onDragOver: (e) => handleOver(e, index),
|
|
2989
|
+
onDrop: handleDrop,
|
|
2990
|
+
onDragEnd: () => setDrag(null)
|
|
2991
|
+
}
|
|
2992
|
+
)
|
|
2993
|
+
] }, index)),
|
|
2994
|
+
adding ? /* @__PURE__ */ jsx6(
|
|
2137
2995
|
FieldPicker,
|
|
2138
2996
|
{
|
|
2139
|
-
fields: fields.filter(
|
|
2997
|
+
fields: fields.filter(isFilterable2),
|
|
2140
2998
|
gateOnDistinct: true,
|
|
2141
2999
|
onPick: addClause,
|
|
2142
3000
|
onClose: () => setAdding(false)
|
|
2143
3001
|
}
|
|
2144
|
-
) : /* @__PURE__ */
|
|
2145
|
-
|
|
3002
|
+
) : /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-add", onClick: () => setAdding(true), disabled, children: "+ add filter" }),
|
|
3003
|
+
where.length > 0 && /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-link-btn", onClick: api.clearFilters, disabled, title: "Reset filters", children: "reset" })
|
|
2146
3004
|
] });
|
|
2147
3005
|
}
|
|
2148
|
-
function
|
|
3006
|
+
function TermChip({
|
|
2149
3007
|
api,
|
|
2150
|
-
|
|
2151
|
-
|
|
3008
|
+
term,
|
|
3009
|
+
index,
|
|
3010
|
+
byName,
|
|
2152
3011
|
classNames,
|
|
2153
3012
|
disabled,
|
|
2154
|
-
|
|
2155
|
-
|
|
3013
|
+
isDragSource,
|
|
3014
|
+
dropHint,
|
|
3015
|
+
onDragStart,
|
|
3016
|
+
onDragOver,
|
|
3017
|
+
onDrop,
|
|
3018
|
+
onDragEnd
|
|
2156
3019
|
}) {
|
|
2157
|
-
const
|
|
2158
|
-
const
|
|
2159
|
-
const
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
3020
|
+
const group = isOrGroup(term);
|
|
3021
|
+
const predicates = predicatesOf(term);
|
|
3022
|
+
const grip = /* @__PURE__ */ jsx6(
|
|
3023
|
+
"span",
|
|
3024
|
+
{
|
|
3025
|
+
className: "qt-chip-grip",
|
|
3026
|
+
"aria-hidden": true,
|
|
3027
|
+
draggable: !disabled,
|
|
3028
|
+
onDragStart: (e) => {
|
|
3029
|
+
onDragStart();
|
|
3030
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3031
|
+
e.dataTransfer.setData("text/plain", String(index));
|
|
3032
|
+
},
|
|
3033
|
+
onDragEnd,
|
|
3034
|
+
title: "drag to reorder, or drop onto another filter to make an OR",
|
|
3035
|
+
children: "\u22EE\u22EE"
|
|
3036
|
+
}
|
|
3037
|
+
);
|
|
3038
|
+
return /* @__PURE__ */ jsxs6(
|
|
3039
|
+
"span",
|
|
3040
|
+
{
|
|
3041
|
+
className: cx3(
|
|
3042
|
+
"qt-chip",
|
|
3043
|
+
"qt-chip--where",
|
|
3044
|
+
group && "qt-chip--or",
|
|
3045
|
+
isDragSource && "qt-chip--dragging",
|
|
3046
|
+
dropHint ? `qt-chip--drop-${dropHint}` : void 0,
|
|
3047
|
+
classNames?.chip
|
|
3048
|
+
),
|
|
3049
|
+
onDragOver,
|
|
3050
|
+
onDrop: (e) => {
|
|
3051
|
+
e.preventDefault();
|
|
3052
|
+
onDrop();
|
|
3053
|
+
},
|
|
3054
|
+
children: [
|
|
3055
|
+
grip,
|
|
3056
|
+
group ? /* @__PURE__ */ jsx6("span", { className: "qt-or-group", children: predicates.map((clause, predIndex) => /* @__PURE__ */ jsxs6("span", { className: "qt-or-member", children: [
|
|
3057
|
+
predIndex > 0 && /* @__PURE__ */ jsx6("span", { className: "qt-chip-and qt-chip-or", children: "OR" }),
|
|
3058
|
+
/* @__PURE__ */ jsx6(
|
|
3059
|
+
PredicateEditor,
|
|
3060
|
+
{
|
|
3061
|
+
api,
|
|
3062
|
+
field: byName.get(clause.field),
|
|
3063
|
+
clause,
|
|
3064
|
+
classNames,
|
|
3065
|
+
disabled,
|
|
3066
|
+
onChange: (next) => api.updatePredicate(index, predIndex, next),
|
|
3067
|
+
onRemove: () => api.removePredicate(index, predIndex)
|
|
3068
|
+
}
|
|
3069
|
+
)
|
|
3070
|
+
] }, predIndex)) }) : /* @__PURE__ */ jsx6(
|
|
3071
|
+
PredicateEditor,
|
|
2172
3072
|
{
|
|
2173
|
-
|
|
2174
|
-
|
|
3073
|
+
api,
|
|
3074
|
+
field: byName.get(term.field),
|
|
3075
|
+
clause: term,
|
|
3076
|
+
classNames,
|
|
2175
3077
|
disabled,
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
children: clauseOps.map((op) => /* @__PURE__ */ jsx5("option", { value: op, children: op }, op))
|
|
3078
|
+
onChange: (next) => api.updatePredicate(index, 0, next),
|
|
3079
|
+
onRemove: () => api.removeFilter(index)
|
|
2179
3080
|
}
|
|
2180
|
-
)
|
|
2181
|
-
|
|
2182
|
-
|
|
3081
|
+
)
|
|
3082
|
+
]
|
|
3083
|
+
}
|
|
3084
|
+
);
|
|
3085
|
+
}
|
|
3086
|
+
var OP_LABEL = {
|
|
3087
|
+
"=": "=",
|
|
3088
|
+
"!=": "\u2260",
|
|
3089
|
+
">": ">",
|
|
3090
|
+
">=": "\u2265",
|
|
3091
|
+
"<": "<",
|
|
3092
|
+
"<=": "\u2264",
|
|
3093
|
+
contains: "contains",
|
|
3094
|
+
starts_with: "starts with",
|
|
3095
|
+
ends_with: "ends with",
|
|
3096
|
+
matches_regex: "matches",
|
|
3097
|
+
not_matches_regex: "not matches",
|
|
3098
|
+
includes: "includes",
|
|
3099
|
+
is_null: "is null",
|
|
3100
|
+
is_not_null: "is not null"
|
|
3101
|
+
};
|
|
3102
|
+
function choiceLabel(op, negated) {
|
|
3103
|
+
return negated ? `not ${OP_LABEL[op]}` : OP_LABEL[op];
|
|
3104
|
+
}
|
|
3105
|
+
function OpPicker({
|
|
3106
|
+
field,
|
|
3107
|
+
clause,
|
|
3108
|
+
disabled,
|
|
3109
|
+
classNames,
|
|
3110
|
+
onPick
|
|
3111
|
+
}) {
|
|
3112
|
+
const [open, setOpen] = useState5(false);
|
|
3113
|
+
const wrapRef = useRef3(null);
|
|
3114
|
+
useEffect5(() => {
|
|
3115
|
+
if (!open) return;
|
|
3116
|
+
function onDocClick(e) {
|
|
3117
|
+
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
3118
|
+
}
|
|
3119
|
+
function onKey(e) {
|
|
3120
|
+
if (e.key === "Escape") setOpen(false);
|
|
3121
|
+
}
|
|
3122
|
+
const t = setTimeout(() => window.addEventListener("click", onDocClick), 0);
|
|
3123
|
+
window.addEventListener("keydown", onKey);
|
|
3124
|
+
return () => {
|
|
3125
|
+
clearTimeout(t);
|
|
3126
|
+
window.removeEventListener("click", onDocClick);
|
|
3127
|
+
window.removeEventListener("keydown", onKey);
|
|
3128
|
+
};
|
|
3129
|
+
}, [open]);
|
|
3130
|
+
const pairs = useMemo4(() => field ? opPairsForField2(field) : [], [field]);
|
|
3131
|
+
const isActive = (choice) => choice.op === clause.op && Boolean(choice.negated) === Boolean(clause.negated);
|
|
3132
|
+
return /* @__PURE__ */ jsxs6("span", { className: "qt-op-picker", ref: wrapRef, children: [
|
|
3133
|
+
/* @__PURE__ */ jsxs6(
|
|
3134
|
+
"button",
|
|
3135
|
+
{
|
|
3136
|
+
type: "button",
|
|
3137
|
+
className: cx3("qt-chip-op-btn", "qt-op-trigger", classNames?.select),
|
|
3138
|
+
disabled,
|
|
3139
|
+
"aria-haspopup": "dialog",
|
|
3140
|
+
"aria-expanded": open,
|
|
3141
|
+
onClick: () => setOpen((o) => !o),
|
|
3142
|
+
children: [
|
|
3143
|
+
choiceLabel(clause.op, Boolean(clause.negated)),
|
|
3144
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-op-caret", "aria-hidden": true, children: "\u25BE" })
|
|
3145
|
+
]
|
|
3146
|
+
}
|
|
3147
|
+
),
|
|
3148
|
+
open && pairs.length > 0 && /* @__PURE__ */ jsxs6("span", { className: "qt-op-pop", role: "dialog", "aria-label": "Choose condition", children: [
|
|
3149
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-cm-col-head", children: "keep" }),
|
|
3150
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-cm-col-head qt-cm-col-head--neg", children: "exclude" }),
|
|
3151
|
+
pairs.map((pair, i) => /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
3152
|
+
/* @__PURE__ */ jsx6(
|
|
3153
|
+
"button",
|
|
2183
3154
|
{
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
3155
|
+
type: "button",
|
|
3156
|
+
className: cx3("qt-op-cell", isActive(pair.keep) && "qt-op-cell--on"),
|
|
3157
|
+
onClick: () => {
|
|
3158
|
+
onPick(pair.keep);
|
|
3159
|
+
setOpen(false);
|
|
3160
|
+
},
|
|
3161
|
+
children: choiceLabel(pair.keep.op, pair.keep.negated)
|
|
2189
3162
|
}
|
|
2190
3163
|
),
|
|
2191
|
-
/* @__PURE__ */
|
|
3164
|
+
/* @__PURE__ */ jsx6(
|
|
2192
3165
|
"button",
|
|
2193
3166
|
{
|
|
2194
3167
|
type: "button",
|
|
2195
|
-
className: "qt-
|
|
2196
|
-
onClick: () =>
|
|
2197
|
-
|
|
2198
|
-
|
|
3168
|
+
className: cx3("qt-op-cell", "qt-op-cell--neg", isActive(pair.exclude) && "qt-op-cell--on"),
|
|
3169
|
+
onClick: () => {
|
|
3170
|
+
onPick(pair.exclude);
|
|
3171
|
+
setOpen(false);
|
|
3172
|
+
},
|
|
3173
|
+
children: choiceLabel(pair.exclude.op, pair.exclude.negated)
|
|
2199
3174
|
}
|
|
2200
3175
|
)
|
|
2201
|
-
] },
|
|
2202
|
-
|
|
3176
|
+
] }, i))
|
|
3177
|
+
] })
|
|
3178
|
+
] });
|
|
3179
|
+
}
|
|
3180
|
+
function PredicateEditor({
|
|
3181
|
+
api,
|
|
3182
|
+
field,
|
|
3183
|
+
clause,
|
|
3184
|
+
classNames,
|
|
3185
|
+
disabled,
|
|
3186
|
+
onChange,
|
|
3187
|
+
onRemove
|
|
3188
|
+
}) {
|
|
3189
|
+
const needsValue = !NULLARY_OPS2.has(clause.op);
|
|
3190
|
+
const isRegex = clause.op === "matches_regex" || clause.op === "not_matches_regex";
|
|
3191
|
+
function pickOp(choice) {
|
|
3192
|
+
const next = { field: clause.field, op: choice.op, value: clause.value };
|
|
3193
|
+
if (choice.negated) next.negated = true;
|
|
3194
|
+
onChange(next);
|
|
3195
|
+
}
|
|
3196
|
+
return /* @__PURE__ */ jsxs6("span", { className: "qt-pred", children: [
|
|
3197
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-chip-field", children: field?.label ?? clause.field }),
|
|
3198
|
+
/* @__PURE__ */ jsx6(OpPicker, { field, clause, disabled, classNames, onPick: pickOp }),
|
|
3199
|
+
needsValue && /* @__PURE__ */ jsx6(
|
|
3200
|
+
ValueInput,
|
|
3201
|
+
{
|
|
3202
|
+
api,
|
|
3203
|
+
field,
|
|
3204
|
+
value: clause.value,
|
|
3205
|
+
forceFreeform: isRegex,
|
|
3206
|
+
placeholder: isRegex ? "regex" : "value",
|
|
3207
|
+
classNames,
|
|
3208
|
+
onChange: (v) => onChange({ ...clause, value: v })
|
|
3209
|
+
}
|
|
3210
|
+
),
|
|
3211
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-chip-x", onClick: onRemove, disabled, children: "\u2715" })
|
|
2203
3212
|
] });
|
|
2204
3213
|
}
|
|
2205
3214
|
function ValueInput({
|
|
2206
3215
|
api,
|
|
2207
3216
|
field,
|
|
2208
3217
|
value,
|
|
3218
|
+
forceFreeform,
|
|
3219
|
+
placeholder = "value",
|
|
2209
3220
|
classNames,
|
|
2210
3221
|
onChange
|
|
2211
3222
|
}) {
|
|
2212
3223
|
const strategy = field ? filterValuesFor(field) : { source: "freeform" };
|
|
2213
|
-
if (strategy.source === "static") {
|
|
3224
|
+
if (!forceFreeform && strategy.source === "static") {
|
|
2214
3225
|
const selectedText = value || "\u2014";
|
|
2215
3226
|
const widthChars = chipFieldWidthForSelect(selectedText, 4);
|
|
2216
|
-
return /* @__PURE__ */
|
|
3227
|
+
return /* @__PURE__ */ jsxs6(
|
|
2217
3228
|
"select",
|
|
2218
3229
|
{
|
|
2219
3230
|
className: cx3("qt-chip-val", classNames?.select),
|
|
@@ -2221,27 +3232,27 @@ function ValueInput({
|
|
|
2221
3232
|
style: { width: `${widthChars}ch` },
|
|
2222
3233
|
onChange: (e) => onChange(e.target.value),
|
|
2223
3234
|
children: [
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
strategy.options.map((o) => /* @__PURE__ */
|
|
3235
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: "\u2014" }),
|
|
3236
|
+
strategy.options.map((o) => /* @__PURE__ */ jsx6("option", { value: o, children: o }, o))
|
|
2226
3237
|
]
|
|
2227
3238
|
}
|
|
2228
3239
|
);
|
|
2229
3240
|
}
|
|
2230
|
-
if (strategy.source === "freeform" || !field) {
|
|
2231
|
-
const widthChars = chipFieldWidthForInput(value ||
|
|
2232
|
-
return /* @__PURE__ */
|
|
3241
|
+
if (forceFreeform || strategy.source === "freeform" || !field) {
|
|
3242
|
+
const widthChars = chipFieldWidthForInput(value || placeholder, field?.type, 5, 4);
|
|
3243
|
+
return /* @__PURE__ */ jsx6(
|
|
2233
3244
|
"input",
|
|
2234
3245
|
{
|
|
2235
3246
|
className: cx3("qt-chip-val", classNames?.input),
|
|
2236
3247
|
value,
|
|
2237
|
-
placeholder
|
|
3248
|
+
placeholder,
|
|
2238
3249
|
size: widthChars,
|
|
2239
3250
|
style: { width: `${widthChars}ch` },
|
|
2240
3251
|
onChange: (e) => onChange(e.target.value)
|
|
2241
3252
|
}
|
|
2242
3253
|
);
|
|
2243
3254
|
}
|
|
2244
|
-
return /* @__PURE__ */
|
|
3255
|
+
return /* @__PURE__ */ jsx6(AutocompleteInput, { api, field, value, classNames, onChange });
|
|
2245
3256
|
}
|
|
2246
3257
|
function AutocompleteInput({
|
|
2247
3258
|
api,
|
|
@@ -2250,9 +3261,9 @@ function AutocompleteInput({
|
|
|
2250
3261
|
classNames,
|
|
2251
3262
|
onChange
|
|
2252
3263
|
}) {
|
|
2253
|
-
const [result, setResult] =
|
|
2254
|
-
const listId =
|
|
2255
|
-
|
|
3264
|
+
const [result, setResult] = useState5({ values: [], hasMore: false });
|
|
3265
|
+
const listId = useId2();
|
|
3266
|
+
useEffect5(() => {
|
|
2256
3267
|
let cancelled = false;
|
|
2257
3268
|
const t = setTimeout(() => {
|
|
2258
3269
|
void api.filterValues(field.name, value).then((r) => {
|
|
@@ -2266,8 +3277,8 @@ function AutocompleteInput({
|
|
|
2266
3277
|
clearTimeout(t);
|
|
2267
3278
|
};
|
|
2268
3279
|
}, [api, field.name, value]);
|
|
2269
|
-
return /* @__PURE__ */
|
|
2270
|
-
/* @__PURE__ */
|
|
3280
|
+
return /* @__PURE__ */ jsxs6("span", { className: "qt-autocomplete", children: [
|
|
3281
|
+
/* @__PURE__ */ jsx6(
|
|
2271
3282
|
"input",
|
|
2272
3283
|
{
|
|
2273
3284
|
className: cx3("qt-chip-val", classNames?.input),
|
|
@@ -2279,8 +3290,8 @@ function AutocompleteInput({
|
|
|
2279
3290
|
onChange: (e) => onChange(e.target.value)
|
|
2280
3291
|
}
|
|
2281
3292
|
),
|
|
2282
|
-
/* @__PURE__ */
|
|
2283
|
-
result.hasMore && /* @__PURE__ */
|
|
3293
|
+
/* @__PURE__ */ jsx6("datalist", { id: listId, children: result.values.map((v) => /* @__PURE__ */ jsx6("option", { value: v }, v)) }),
|
|
3294
|
+
result.hasMore && /* @__PURE__ */ jsx6("span", { className: "qt-autocomplete-hint", children: "keep typing to refine\u2026" })
|
|
2284
3295
|
] });
|
|
2285
3296
|
}
|
|
2286
3297
|
function OrderTermChip({
|
|
@@ -2297,6 +3308,7 @@ function OrderTermChip({
|
|
|
2297
3308
|
onDragEnd,
|
|
2298
3309
|
disabled,
|
|
2299
3310
|
updateTerm,
|
|
3311
|
+
clearExtract,
|
|
2300
3312
|
removeTerm,
|
|
2301
3313
|
position,
|
|
2302
3314
|
count,
|
|
@@ -2305,7 +3317,7 @@ function OrderTermChip({
|
|
|
2305
3317
|
label
|
|
2306
3318
|
}) {
|
|
2307
3319
|
const fieldHasNull = useFieldHasNull(api, field?.name);
|
|
2308
|
-
return /* @__PURE__ */
|
|
3320
|
+
return /* @__PURE__ */ jsxs6(
|
|
2309
3321
|
"span",
|
|
2310
3322
|
{
|
|
2311
3323
|
className: cx3("qt-chip", "qt-chip--col", "qt-chip--sort", isDragSource && "qt-chip--dragging", classNames?.chip),
|
|
@@ -2323,10 +3335,10 @@ function OrderTermChip({
|
|
|
2323
3335
|
onDragEnd,
|
|
2324
3336
|
title: "drag to reorder sort priority",
|
|
2325
3337
|
children: [
|
|
2326
|
-
/* @__PURE__ */
|
|
2327
|
-
showPriority && /* @__PURE__ */
|
|
2328
|
-
/* @__PURE__ */
|
|
2329
|
-
/* @__PURE__ */
|
|
3338
|
+
/* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "qt-chip-grip", children: "\u22EE\u22EE" }),
|
|
3339
|
+
showPriority && /* @__PURE__ */ jsx6("span", { className: "qt-chip-priority", children: priority }),
|
|
3340
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-chip-field", children: label }),
|
|
3341
|
+
/* @__PURE__ */ jsx6(
|
|
2330
3342
|
"button",
|
|
2331
3343
|
{
|
|
2332
3344
|
type: "button",
|
|
@@ -2336,7 +3348,45 @@ function OrderTermChip({
|
|
|
2336
3348
|
children: term.dir === "asc" ? "\u2191 asc" : "\u2193 desc"
|
|
2337
3349
|
}
|
|
2338
3350
|
),
|
|
2339
|
-
|
|
3351
|
+
term.extract ? /* @__PURE__ */ jsxs6("span", { className: "qt-sort-extract", children: [
|
|
3352
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-chip-and", children: "extract" }),
|
|
3353
|
+
/* @__PURE__ */ jsx6(
|
|
3354
|
+
"input",
|
|
3355
|
+
{
|
|
3356
|
+
className: cx3("qt-chip-val", classNames?.input),
|
|
3357
|
+
value: term.extract.regex,
|
|
3358
|
+
placeholder: "regex",
|
|
3359
|
+
"aria-label": `Regex extract for ${label}`,
|
|
3360
|
+
size: chipFieldWidthForInput(term.extract.regex || "regex", "text", 5, 4),
|
|
3361
|
+
style: { width: `${chipFieldWidthForInput(term.extract.regex || "regex", "text", 5, 4)}ch` },
|
|
3362
|
+
disabled,
|
|
3363
|
+
onChange: (e) => updateTerm(dataIndex, { extract: { regex: e.target.value } })
|
|
3364
|
+
}
|
|
3365
|
+
),
|
|
3366
|
+
/* @__PURE__ */ jsx6(
|
|
3367
|
+
"button",
|
|
3368
|
+
{
|
|
3369
|
+
type: "button",
|
|
3370
|
+
className: "qt-chip-x",
|
|
3371
|
+
title: "Remove regex extraction",
|
|
3372
|
+
"aria-label": `Remove regex extract for ${label}`,
|
|
3373
|
+
disabled,
|
|
3374
|
+
onClick: () => clearExtract(dataIndex),
|
|
3375
|
+
children: "\u2715"
|
|
3376
|
+
}
|
|
3377
|
+
)
|
|
3378
|
+
] }) : /* @__PURE__ */ jsx6(
|
|
3379
|
+
"button",
|
|
3380
|
+
{
|
|
3381
|
+
type: "button",
|
|
3382
|
+
className: "qt-chip-op-btn",
|
|
3383
|
+
title: "Sort by text extracted with a regular expression",
|
|
3384
|
+
disabled,
|
|
3385
|
+
onClick: () => updateTerm(dataIndex, { extract: { regex: "" } }),
|
|
3386
|
+
children: "+ regex extract"
|
|
3387
|
+
}
|
|
3388
|
+
),
|
|
3389
|
+
(term.extract != null || fieldHasNull !== false) && /* @__PURE__ */ jsxs6(
|
|
2340
3390
|
"button",
|
|
2341
3391
|
{
|
|
2342
3392
|
type: "button",
|
|
@@ -2350,7 +3400,7 @@ function OrderTermChip({
|
|
|
2350
3400
|
]
|
|
2351
3401
|
}
|
|
2352
3402
|
),
|
|
2353
|
-
/* @__PURE__ */
|
|
3403
|
+
/* @__PURE__ */ jsx6(
|
|
2354
3404
|
MoveButtons,
|
|
2355
3405
|
{
|
|
2356
3406
|
label,
|
|
@@ -2360,7 +3410,7 @@ function OrderTermChip({
|
|
|
2360
3410
|
onMove: moveTerm
|
|
2361
3411
|
}
|
|
2362
3412
|
),
|
|
2363
|
-
/* @__PURE__ */
|
|
3413
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-chip-x", onClick: () => removeTerm(dataIndex), disabled, children: "\u2715" })
|
|
2364
3414
|
]
|
|
2365
3415
|
}
|
|
2366
3416
|
);
|
|
@@ -2372,15 +3422,24 @@ function OrderRow({
|
|
|
2372
3422
|
disabled
|
|
2373
3423
|
}) {
|
|
2374
3424
|
const orderBy = api.query.orderBy;
|
|
2375
|
-
const byName =
|
|
2376
|
-
const [drag, setDrag] =
|
|
2377
|
-
const [adding, setAdding] =
|
|
3425
|
+
const byName = useMemo4(() => new Map(fields.map((f) => [f.name, f])), [fields]);
|
|
3426
|
+
const [drag, setDrag] = useState5(null);
|
|
3427
|
+
const [adding, setAdding] = useState5(false);
|
|
2378
3428
|
function setOrderBy(next) {
|
|
2379
3429
|
api.setSort(next);
|
|
2380
3430
|
}
|
|
2381
3431
|
function updateTerm(i, patch) {
|
|
2382
3432
|
setOrderBy(orderBy.map((o, k) => k === i ? { ...o, ...patch } : o));
|
|
2383
3433
|
}
|
|
3434
|
+
function clearExtract(i) {
|
|
3435
|
+
setOrderBy(
|
|
3436
|
+
orderBy.map((term, index) => {
|
|
3437
|
+
if (index !== i) return term;
|
|
3438
|
+
const { extract: _extract, ...plainTerm } = term;
|
|
3439
|
+
return plainTerm;
|
|
3440
|
+
})
|
|
3441
|
+
);
|
|
3442
|
+
}
|
|
2384
3443
|
function removeTerm(i) {
|
|
2385
3444
|
setOrderBy(orderBy.filter((_, k) => k !== i));
|
|
2386
3445
|
}
|
|
@@ -2406,7 +3465,7 @@ function OrderRow({
|
|
|
2406
3465
|
setAdding(false);
|
|
2407
3466
|
}
|
|
2408
3467
|
const termFields = orderBy.map((o) => o.field);
|
|
2409
|
-
const termByField =
|
|
3468
|
+
const termByField = useMemo4(() => new Map(orderBy.map((o) => [o.field, o])), [orderBy]);
|
|
2410
3469
|
function previewFields() {
|
|
2411
3470
|
if (!drag) return termFields;
|
|
2412
3471
|
const without = termFields.filter((f) => f !== drag.source);
|
|
@@ -2436,14 +3495,14 @@ function OrderRow({
|
|
|
2436
3495
|
const sortable = fields.filter(isSortable2);
|
|
2437
3496
|
const isDefaultOrderBy = orderBy.length === api.defaults.orderBy.length && orderBy.every((term, i) => {
|
|
2438
3497
|
const next = api.defaults.orderBy[i];
|
|
2439
|
-
return term.field === next?.field && term.dir === next?.dir && (term.nulls ?? "last") === (next?.nulls ?? "last");
|
|
3498
|
+
return term.field === next?.field && term.dir === next?.dir && (term.nulls ?? "last") === (next?.nulls ?? "last") && (term.extract?.regex ?? null) === (next?.extract?.regex ?? null);
|
|
2440
3499
|
});
|
|
2441
|
-
return /* @__PURE__ */
|
|
2442
|
-
/* @__PURE__ */
|
|
2443
|
-
orderBy.length === 0 && !adding && /* @__PURE__ */
|
|
3500
|
+
return /* @__PURE__ */ jsxs6("div", { className: "qt-qb-row", children: [
|
|
3501
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-kw", children: "order by" }),
|
|
3502
|
+
orderBy.length === 0 && !adding && /* @__PURE__ */ jsx6("span", { className: "qt-qb-hint", children: "(default)" }),
|
|
2444
3503
|
rendered.map((f, pos) => {
|
|
2445
3504
|
const term = termByField.get(f);
|
|
2446
|
-
return /* @__PURE__ */
|
|
3505
|
+
return /* @__PURE__ */ jsx6(
|
|
2447
3506
|
OrderTermChip,
|
|
2448
3507
|
{
|
|
2449
3508
|
api,
|
|
@@ -2459,6 +3518,7 @@ function OrderRow({
|
|
|
2459
3518
|
onDragEnd: () => setDrag(null),
|
|
2460
3519
|
disabled,
|
|
2461
3520
|
updateTerm,
|
|
3521
|
+
clearExtract,
|
|
2462
3522
|
removeTerm,
|
|
2463
3523
|
position: pos,
|
|
2464
3524
|
count: rendered.length,
|
|
@@ -2476,8 +3536,8 @@ function OrderRow({
|
|
|
2476
3536
|
f
|
|
2477
3537
|
);
|
|
2478
3538
|
}),
|
|
2479
|
-
adding ? /* @__PURE__ */
|
|
2480
|
-
!isDefaultOrderBy && /* @__PURE__ */
|
|
3539
|
+
adding ? /* @__PURE__ */ jsx6(FieldPicker, { fields: sortable, onPick: addTerm, onClose: () => setAdding(false) }) : /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-add", onClick: () => setAdding(true), disabled, children: "+ add sort" }),
|
|
3540
|
+
!isDefaultOrderBy && /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-link-btn", onClick: () => api.setSort(api.defaults.orderBy), disabled, children: "reset" })
|
|
2481
3541
|
] });
|
|
2482
3542
|
}
|
|
2483
3543
|
function WindowRow({
|
|
@@ -2486,10 +3546,10 @@ function WindowRow({
|
|
|
2486
3546
|
disabled
|
|
2487
3547
|
}) {
|
|
2488
3548
|
const { query } = api;
|
|
2489
|
-
const [limitDraft, setLimitDraft] =
|
|
2490
|
-
const [offsetDraft, setOffsetDraft] =
|
|
2491
|
-
|
|
2492
|
-
|
|
3549
|
+
const [limitDraft, setLimitDraft] = useState5(String(query.limit));
|
|
3550
|
+
const [offsetDraft, setOffsetDraft] = useState5(String(query.offset));
|
|
3551
|
+
useEffect5(() => setLimitDraft(String(query.limit)), [query.limit]);
|
|
3552
|
+
useEffect5(() => setOffsetDraft(String(query.offset)), [query.offset]);
|
|
2493
3553
|
const hasLimitDefault = query.limit === api.defaults.limit;
|
|
2494
3554
|
const hasOffsetDefault = query.offset === api.defaults.offset;
|
|
2495
3555
|
const hasWindowDefault = hasLimitDefault && hasOffsetDefault;
|
|
@@ -2499,10 +3559,10 @@ function WindowRow({
|
|
|
2499
3559
|
function commitOffset() {
|
|
2500
3560
|
api.setOffset(Math.max(0, Number(offsetDraft) || 0));
|
|
2501
3561
|
}
|
|
2502
|
-
return /* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2505
|
-
/* @__PURE__ */
|
|
3562
|
+
return /* @__PURE__ */ jsxs6("div", { className: "qt-qb-row qt-qb-pagination", children: [
|
|
3563
|
+
/* @__PURE__ */ jsxs6("label", { className: "qt-qb-window-field", children: [
|
|
3564
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-kw", children: "limit" }),
|
|
3565
|
+
/* @__PURE__ */ jsx6(
|
|
2506
3566
|
"input",
|
|
2507
3567
|
{
|
|
2508
3568
|
className: cx3("qt-qb-num", classNames?.input),
|
|
@@ -2516,9 +3576,9 @@ function WindowRow({
|
|
|
2516
3576
|
}
|
|
2517
3577
|
)
|
|
2518
3578
|
] }),
|
|
2519
|
-
/* @__PURE__ */
|
|
2520
|
-
/* @__PURE__ */
|
|
2521
|
-
/* @__PURE__ */
|
|
3579
|
+
/* @__PURE__ */ jsxs6("label", { className: "qt-qb-window-field", children: [
|
|
3580
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-kw", children: "offset" }),
|
|
3581
|
+
/* @__PURE__ */ jsx6(
|
|
2522
3582
|
"input",
|
|
2523
3583
|
{
|
|
2524
3584
|
className: cx3("qt-qb-num", classNames?.input),
|
|
@@ -2532,7 +3592,7 @@ function WindowRow({
|
|
|
2532
3592
|
}
|
|
2533
3593
|
)
|
|
2534
3594
|
] }),
|
|
2535
|
-
!hasWindowDefault && /* @__PURE__ */
|
|
3595
|
+
!hasWindowDefault && /* @__PURE__ */ jsx6(
|
|
2536
3596
|
"button",
|
|
2537
3597
|
{
|
|
2538
3598
|
type: "button",
|
|
@@ -2565,11 +3625,11 @@ function MetricsRow({
|
|
|
2565
3625
|
}) {
|
|
2566
3626
|
const { aggregations } = api;
|
|
2567
3627
|
const clauses = aggregations.clauses;
|
|
2568
|
-
const measurable =
|
|
2569
|
-
const groupable =
|
|
2570
|
-
const [drag, setDrag] =
|
|
3628
|
+
const measurable = useMemo4(() => fields.filter(isMeasurable), [fields]);
|
|
3629
|
+
const groupable = useMemo4(() => fields.filter(isGroupable), [fields]);
|
|
3630
|
+
const [drag, setDrag] = useState5(null);
|
|
2571
3631
|
const ids = clauses.map((c) => c.id);
|
|
2572
|
-
const byId =
|
|
3632
|
+
const byId = useMemo4(() => new Map(clauses.map((c) => [c.id, c])), [clauses]);
|
|
2573
3633
|
function previewIds() {
|
|
2574
3634
|
if (!drag) return ids;
|
|
2575
3635
|
const without = ids.filter((i) => i !== drag.source);
|
|
@@ -2595,12 +3655,12 @@ function MetricsRow({
|
|
|
2595
3655
|
setDrag(null);
|
|
2596
3656
|
}
|
|
2597
3657
|
const rendered = previewIds();
|
|
2598
|
-
return /* @__PURE__ */
|
|
2599
|
-
/* @__PURE__ */
|
|
2600
|
-
clauses.length === 0 && /* @__PURE__ */
|
|
3658
|
+
return /* @__PURE__ */ jsxs6("div", { className: "qt-qb-row qt-qb-row--metrics", children: [
|
|
3659
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-qb-kw", children: "metrics" }),
|
|
3660
|
+
clauses.length === 0 && /* @__PURE__ */ jsx6("span", { className: "qt-qb-hint", children: "none" }),
|
|
2601
3661
|
rendered.map((id, position) => {
|
|
2602
3662
|
const clause = byId.get(id);
|
|
2603
|
-
return /* @__PURE__ */
|
|
3663
|
+
return /* @__PURE__ */ jsx6(
|
|
2604
3664
|
MetricChip,
|
|
2605
3665
|
{
|
|
2606
3666
|
clause,
|
|
@@ -2625,8 +3685,8 @@ function MetricsRow({
|
|
|
2625
3685
|
id
|
|
2626
3686
|
);
|
|
2627
3687
|
}),
|
|
2628
|
-
/* @__PURE__ */
|
|
2629
|
-
clauses.length > 0 && /* @__PURE__ */
|
|
3688
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-add", onClick: () => aggregations.add(), disabled, children: "+ add metric" }),
|
|
3689
|
+
clauses.length > 0 && /* @__PURE__ */ jsx6("button", { type: "button", className: "qt-link-btn", onClick: aggregations.clear, disabled, title: "Remove all metrics", children: "reset" })
|
|
2630
3690
|
] });
|
|
2631
3691
|
}
|
|
2632
3692
|
function changeMetricOp(aggregations, clause, measurable, op) {
|
|
@@ -2655,11 +3715,11 @@ function MetricChip({
|
|
|
2655
3715
|
count,
|
|
2656
3716
|
onMove
|
|
2657
3717
|
}) {
|
|
2658
|
-
const [addingGroup, setAddingGroup] =
|
|
2659
|
-
const byName =
|
|
3718
|
+
const [addingGroup, setAddingGroup] = useState5(false);
|
|
3719
|
+
const byName = useMemo4(() => new Map(groupable.map((f) => [f.name, f])), [groupable]);
|
|
2660
3720
|
const needsField = aggOpNeedsField(clause.op);
|
|
2661
3721
|
const measureOptions = measurable.filter((f) => aggOpsForField(f).includes(clause.op));
|
|
2662
|
-
return /* @__PURE__ */
|
|
3722
|
+
return /* @__PURE__ */ jsxs6(
|
|
2663
3723
|
"span",
|
|
2664
3724
|
{
|
|
2665
3725
|
className: cx3("qt-chip", "qt-chip--agg", isDragSource && "qt-chip--dragging", classNames?.chip),
|
|
@@ -2677,18 +3737,18 @@ function MetricChip({
|
|
|
2677
3737
|
onDragEnd,
|
|
2678
3738
|
title: "drag to reorder metrics",
|
|
2679
3739
|
children: [
|
|
2680
|
-
/* @__PURE__ */
|
|
2681
|
-
/* @__PURE__ */
|
|
3740
|
+
/* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "qt-chip-grip", children: "\u22EE\u22EE" }),
|
|
3741
|
+
/* @__PURE__ */ jsx6(
|
|
2682
3742
|
"select",
|
|
2683
3743
|
{
|
|
2684
3744
|
className: cx3("qt-chip-op", classNames?.select),
|
|
2685
3745
|
value: clause.op,
|
|
2686
3746
|
disabled,
|
|
2687
3747
|
onChange: (e) => onChangeOp(e.target.value),
|
|
2688
|
-
children: AGG_OPS.map((op) => /* @__PURE__ */
|
|
3748
|
+
children: AGG_OPS.map((op) => /* @__PURE__ */ jsx6("option", { value: op, children: AGG_OP_LABELS[op] }, op))
|
|
2689
3749
|
}
|
|
2690
3750
|
),
|
|
2691
|
-
/* @__PURE__ */
|
|
3751
|
+
/* @__PURE__ */ jsxs6(
|
|
2692
3752
|
"select",
|
|
2693
3753
|
{
|
|
2694
3754
|
className: cx3("qt-chip-val", classNames?.select),
|
|
@@ -2696,18 +3756,18 @@ function MetricChip({
|
|
|
2696
3756
|
disabled,
|
|
2697
3757
|
onChange: (e) => onChangeField(e.target.value || void 0),
|
|
2698
3758
|
children: [
|
|
2699
|
-
!needsField && /* @__PURE__ */
|
|
2700
|
-
needsField && clause.field == null && /* @__PURE__ */
|
|
2701
|
-
measureOptions.map((f) => /* @__PURE__ */
|
|
3759
|
+
!needsField && /* @__PURE__ */ jsx6("option", { value: "", children: "(all rows)" }),
|
|
3760
|
+
needsField && clause.field == null && /* @__PURE__ */ jsx6("option", { value: "", children: "measure\u2026" }),
|
|
3761
|
+
measureOptions.map((f) => /* @__PURE__ */ jsx6("option", { value: f.name, children: f.label }, f.name))
|
|
2702
3762
|
]
|
|
2703
3763
|
}
|
|
2704
3764
|
),
|
|
2705
|
-
/* @__PURE__ */
|
|
2706
|
-
clause.groupBy.map((g) => /* @__PURE__ */
|
|
2707
|
-
/* @__PURE__ */
|
|
2708
|
-
/* @__PURE__ */
|
|
3765
|
+
/* @__PURE__ */ jsxs6("span", { className: "qt-chip-agg-by", children: [
|
|
3766
|
+
clause.groupBy.map((g) => /* @__PURE__ */ jsxs6("span", { className: "qt-chip-agg-group", children: [
|
|
3767
|
+
/* @__PURE__ */ jsx6("span", { className: "qt-chip-agg-group-label", children: byName.get(g)?.label ?? g }),
|
|
3768
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-chip-x", onClick: () => onRemoveGroup(g), disabled, children: "\u2715" })
|
|
2709
3769
|
] }, g)),
|
|
2710
|
-
addingGroup ? /* @__PURE__ */
|
|
3770
|
+
addingGroup ? /* @__PURE__ */ jsx6(
|
|
2711
3771
|
FieldPicker,
|
|
2712
3772
|
{
|
|
2713
3773
|
fields: groupable,
|
|
@@ -2718,7 +3778,7 @@ function MetricChip({
|
|
|
2718
3778
|
},
|
|
2719
3779
|
onClose: () => setAddingGroup(false)
|
|
2720
3780
|
}
|
|
2721
|
-
) : /* @__PURE__ */
|
|
3781
|
+
) : /* @__PURE__ */ jsx6(
|
|
2722
3782
|
"button",
|
|
2723
3783
|
{
|
|
2724
3784
|
type: "button",
|
|
@@ -2730,7 +3790,7 @@ function MetricChip({
|
|
|
2730
3790
|
}
|
|
2731
3791
|
)
|
|
2732
3792
|
] }),
|
|
2733
|
-
/* @__PURE__ */
|
|
3793
|
+
/* @__PURE__ */ jsx6(
|
|
2734
3794
|
MoveButtons,
|
|
2735
3795
|
{
|
|
2736
3796
|
label: clause.label ?? `${AGG_OP_LABELS[clause.op]} metric`,
|
|
@@ -2740,16 +3800,16 @@ function MetricChip({
|
|
|
2740
3800
|
onMove
|
|
2741
3801
|
}
|
|
2742
3802
|
),
|
|
2743
|
-
/* @__PURE__ */
|
|
3803
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "qt-chip-x", onClick: onRemove, disabled, title: "Remove metric", children: "\u2715" })
|
|
2744
3804
|
]
|
|
2745
3805
|
}
|
|
2746
3806
|
);
|
|
2747
3807
|
}
|
|
2748
3808
|
|
|
2749
3809
|
// src/MetricsPanel.tsx
|
|
2750
|
-
import { useMemo as
|
|
3810
|
+
import { useMemo as useMemo5 } from "react";
|
|
2751
3811
|
import { EMPTY_QUERY } from "@pythia-software/query-table-core";
|
|
2752
|
-
import { jsx as
|
|
3812
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2753
3813
|
var cx4 = (...parts) => parts.filter((p) => Boolean(p)).join(" ");
|
|
2754
3814
|
var OP_LABELS = {
|
|
2755
3815
|
count: "Count",
|
|
@@ -2767,18 +3827,18 @@ function MetricsPanel({
|
|
|
2767
3827
|
emptyMessage,
|
|
2768
3828
|
classNames
|
|
2769
3829
|
}) {
|
|
2770
|
-
const byName =
|
|
3830
|
+
const byName = useMemo5(() => new Map(fields.map((f) => [f.name, f])), [fields]);
|
|
2771
3831
|
const { clauses, results, loading, error } = aggregations;
|
|
2772
3832
|
if (clauses.length === 0) {
|
|
2773
|
-
return emptyMessage ? /* @__PURE__ */
|
|
3833
|
+
return emptyMessage ? /* @__PURE__ */ jsx7("div", { className: cx4("qt-metrics", classNames?.root), children: emptyMessage }) : null;
|
|
2774
3834
|
}
|
|
2775
3835
|
const resultById = new Map((results?.metrics ?? []).map((m) => [m.id, m.buckets]));
|
|
2776
|
-
return /* @__PURE__ */
|
|
2777
|
-
error ? /* @__PURE__ */
|
|
3836
|
+
return /* @__PURE__ */ jsxs7("div", { className: cx4("qt-metrics", classNames?.root), children: [
|
|
3837
|
+
error ? /* @__PURE__ */ jsxs7("div", { className: "qt-metrics-error", children: [
|
|
2778
3838
|
"Metrics failed: ",
|
|
2779
3839
|
error.message
|
|
2780
3840
|
] }) : null,
|
|
2781
|
-
clauses.map((clause) => /* @__PURE__ */
|
|
3841
|
+
clauses.map((clause) => /* @__PURE__ */ jsx7(
|
|
2782
3842
|
MetricCard,
|
|
2783
3843
|
{
|
|
2784
3844
|
clause,
|
|
@@ -2802,23 +3862,23 @@ function MetricCard({
|
|
|
2802
3862
|
}) {
|
|
2803
3863
|
const measure = clause.field ? byName.get(clause.field) : void 0;
|
|
2804
3864
|
const groupLabels = clause.groupBy.map((g) => byName.get(g)?.label ?? g);
|
|
2805
|
-
const decimals =
|
|
3865
|
+
const decimals = useMemo5(() => cardDecimals(buckets), [buckets]);
|
|
2806
3866
|
const fmt = (v) => formatMetricValue(v, clause.op, measure, renderers, decimals);
|
|
2807
|
-
return /* @__PURE__ */
|
|
2808
|
-
/* @__PURE__ */
|
|
2809
|
-
/* @__PURE__ */
|
|
2810
|
-
groupLabels.length > 0 && /* @__PURE__ */
|
|
3867
|
+
return /* @__PURE__ */ jsxs7("div", { className: cx4("qt-metric", `qt-metric--gb${Math.min(clause.groupBy.length, 3)}`, className), children: [
|
|
3868
|
+
/* @__PURE__ */ jsxs7("div", { className: "qt-metric-head", children: [
|
|
3869
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-metric-title", children: metricTitle(clause, measure) }),
|
|
3870
|
+
groupLabels.length > 0 && /* @__PURE__ */ jsxs7("span", { className: "qt-metric-by", children: [
|
|
2811
3871
|
"by ",
|
|
2812
3872
|
groupLabels.join(" \xD7 ")
|
|
2813
3873
|
] })
|
|
2814
3874
|
] }),
|
|
2815
|
-
loading && buckets.length === 0 ? /* @__PURE__ */
|
|
3875
|
+
loading && buckets.length === 0 ? /* @__PURE__ */ jsx7("div", { className: "qt-metric-loading", children: "\u2026" }) : buckets.length === 0 ? /* @__PURE__ */ jsx7("div", { className: "qt-metric-empty", children: "no data" }) : clause.groupBy.length === 0 ? /* @__PURE__ */ jsx7(BigNumber, { bucket: buckets[0], fmt }) : clause.groupBy.length === 1 ? /* @__PURE__ */ jsx7(BarList, { buckets, fmt }) : clause.groupBy.length === 2 ? /* @__PURE__ */ jsx7(Pivot, { buckets, fmt, rowLabel: groupLabels[0], colLabel: groupLabels[1] }) : /* @__PURE__ */ jsx7(FlatTable, { buckets, fmt, groupLabels, valueLabel: metricTitle(clause, measure) })
|
|
2816
3876
|
] });
|
|
2817
3877
|
}
|
|
2818
3878
|
function BigNumber({ bucket, fmt }) {
|
|
2819
|
-
return /* @__PURE__ */
|
|
2820
|
-
/* @__PURE__ */
|
|
2821
|
-
/* @__PURE__ */
|
|
3879
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-metric-big", children: [
|
|
3880
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-metric-big-value", children: fmt(bucket.value) }),
|
|
3881
|
+
/* @__PURE__ */ jsxs7("span", { className: "qt-metric-big-sub", children: [
|
|
2822
3882
|
bucket.count.toLocaleString(),
|
|
2823
3883
|
" rows"
|
|
2824
3884
|
] })
|
|
@@ -2826,12 +3886,12 @@ function BigNumber({ bucket, fmt }) {
|
|
|
2826
3886
|
}
|
|
2827
3887
|
function BarList({ buckets, fmt }) {
|
|
2828
3888
|
const max = Math.max(...buckets.map((b) => barMagnitude(b)), 0);
|
|
2829
|
-
return /* @__PURE__ */
|
|
3889
|
+
return /* @__PURE__ */ jsx7("div", { className: "qt-metric-bars", children: buckets.map((b, i) => {
|
|
2830
3890
|
const pct = max > 0 ? barMagnitude(b) / max * 100 : 0;
|
|
2831
|
-
return /* @__PURE__ */
|
|
2832
|
-
/* @__PURE__ */
|
|
2833
|
-
/* @__PURE__ */
|
|
2834
|
-
/* @__PURE__ */
|
|
3891
|
+
return /* @__PURE__ */ jsxs7("div", { className: "qt-metric-bar-row", children: [
|
|
3892
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-metric-bar-label", title: keyText(b.keys[0]), children: keyNode(b.keys[0]) }),
|
|
3893
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-metric-bar-track", children: /* @__PURE__ */ jsx7("span", { className: "qt-metric-bar-fill", style: { width: `${pct}%` } }) }),
|
|
3894
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-metric-bar-value", children: fmt(b.value) })
|
|
2835
3895
|
] }, `${keyText(b.keys[0])}-${i}`);
|
|
2836
3896
|
}) });
|
|
2837
3897
|
}
|
|
@@ -2845,22 +3905,22 @@ function Pivot({
|
|
|
2845
3905
|
const colKeys = distinctKeys(buckets, 1);
|
|
2846
3906
|
const cell = /* @__PURE__ */ new Map();
|
|
2847
3907
|
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__ */
|
|
3908
|
+
return /* @__PURE__ */ jsx7("div", { className: "qt-metric-pivot-wrap", children: /* @__PURE__ */ jsxs7("table", { className: "qt-metric-pivot", children: [
|
|
3909
|
+
/* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
|
|
3910
|
+
/* @__PURE__ */ jsx7("th", { className: "qt-metric-pivot-corner", children: /* @__PURE__ */ jsxs7("span", { className: "qt-metric-pivot-axes", children: [
|
|
2851
3911
|
rowLabel,
|
|
2852
3912
|
" ",
|
|
2853
|
-
/* @__PURE__ */
|
|
3913
|
+
/* @__PURE__ */ jsx7("span", { className: "qt-muted", children: "\\" }),
|
|
2854
3914
|
" ",
|
|
2855
3915
|
colLabel
|
|
2856
3916
|
] }) }),
|
|
2857
|
-
colKeys.map((c) => /* @__PURE__ */
|
|
3917
|
+
colKeys.map((c) => /* @__PURE__ */ jsx7("th", { className: "qt-metric-pivot-col", children: keyNode(c) }, keyText(c)))
|
|
2858
3918
|
] }) }),
|
|
2859
|
-
/* @__PURE__ */
|
|
2860
|
-
/* @__PURE__ */
|
|
3919
|
+
/* @__PURE__ */ jsx7("tbody", { children: rowKeys.map((r) => /* @__PURE__ */ jsxs7("tr", { children: [
|
|
3920
|
+
/* @__PURE__ */ jsx7("th", { className: "qt-metric-pivot-row", children: keyNode(r) }),
|
|
2861
3921
|
colKeys.map((c) => {
|
|
2862
3922
|
const v = cell.get(`${keyText(r)}\0${keyText(c)}`);
|
|
2863
|
-
return /* @__PURE__ */
|
|
3923
|
+
return /* @__PURE__ */ jsx7("td", { className: "qt-metric-pivot-cell", children: v === void 0 ? /* @__PURE__ */ jsx7("span", { className: "qt-muted", children: "\xB7" }) : fmt(v) }, keyText(c));
|
|
2864
3924
|
})
|
|
2865
3925
|
] }, keyText(r))) })
|
|
2866
3926
|
] }) });
|
|
@@ -2869,16 +3929,16 @@ function FlatTable({
|
|
|
2869
3929
|
buckets,
|
|
2870
3930
|
fmt,
|
|
2871
3931
|
groupLabels,
|
|
2872
|
-
valueLabel
|
|
3932
|
+
valueLabel: valueLabel2
|
|
2873
3933
|
}) {
|
|
2874
|
-
return /* @__PURE__ */
|
|
2875
|
-
/* @__PURE__ */
|
|
2876
|
-
groupLabels.map((g) => /* @__PURE__ */
|
|
2877
|
-
/* @__PURE__ */
|
|
3934
|
+
return /* @__PURE__ */ jsx7("div", { className: "qt-metric-pivot-wrap", children: /* @__PURE__ */ jsxs7("table", { className: "qt-metric-flat", children: [
|
|
3935
|
+
/* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
|
|
3936
|
+
groupLabels.map((g) => /* @__PURE__ */ jsx7("th", { children: g }, g)),
|
|
3937
|
+
/* @__PURE__ */ jsx7("th", { className: "qt-metric-flat-value", children: valueLabel2 })
|
|
2878
3938
|
] }) }),
|
|
2879
|
-
/* @__PURE__ */
|
|
2880
|
-
groupLabels.map((_, j) => /* @__PURE__ */
|
|
2881
|
-
/* @__PURE__ */
|
|
3939
|
+
/* @__PURE__ */ jsx7("tbody", { children: buckets.map((b, i) => /* @__PURE__ */ jsxs7("tr", { children: [
|
|
3940
|
+
groupLabels.map((_, j) => /* @__PURE__ */ jsx7("td", { children: keyNode(b.keys[j]) }, j)),
|
|
3941
|
+
/* @__PURE__ */ jsx7("td", { className: "qt-metric-flat-value", children: fmt(b.value) })
|
|
2882
3942
|
] }, i)) })
|
|
2883
3943
|
] }) });
|
|
2884
3944
|
}
|
|
@@ -2889,7 +3949,7 @@ function metricTitle(clause, measure) {
|
|
|
2889
3949
|
}
|
|
2890
3950
|
var UNIT_RENDER_KEYS = /* @__PURE__ */ new Set(["duration_ms", "byte_size"]);
|
|
2891
3951
|
function formatMetricValue(value, op, measure, renderers, decimals) {
|
|
2892
|
-
if (value == null) return /* @__PURE__ */
|
|
3952
|
+
if (value == null) return /* @__PURE__ */ jsx7("span", { className: "qt-muted", children: "\u2014" });
|
|
2893
3953
|
if (op === "count" || op === "count_distinct") {
|
|
2894
3954
|
return typeof value === "number" ? formatNumber(value, 0) : String(value);
|
|
2895
3955
|
}
|
|
@@ -2952,21 +4012,21 @@ function keyText(k) {
|
|
|
2952
4012
|
return k == null ? NULL_KEY : String(k);
|
|
2953
4013
|
}
|
|
2954
4014
|
function keyNode(k) {
|
|
2955
|
-
return k == null ? /* @__PURE__ */
|
|
4015
|
+
return k == null ? /* @__PURE__ */ jsx7("span", { className: "qt-muted", children: NULL_KEY }) : String(k);
|
|
2956
4016
|
}
|
|
2957
4017
|
|
|
2958
4018
|
// src/SelectionToolbar.tsx
|
|
2959
|
-
import { jsx as
|
|
4019
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2960
4020
|
var cx5 = (...parts) => parts.filter(Boolean).join(" ");
|
|
2961
4021
|
function SelectionToolbar({ selection, actions, alwaysShow, className }) {
|
|
2962
4022
|
if (selection.count === 0 && !alwaysShow) return null;
|
|
2963
4023
|
const selectedIds = [...selection.selected];
|
|
2964
|
-
return /* @__PURE__ */
|
|
2965
|
-
/* @__PURE__ */
|
|
4024
|
+
return /* @__PURE__ */ jsxs8("div", { className: cx5("qt-selection-toolbar", className), role: "toolbar", children: [
|
|
4025
|
+
/* @__PURE__ */ jsxs8("span", { className: "qt-selection-count", children: [
|
|
2966
4026
|
selection.count,
|
|
2967
4027
|
" selected"
|
|
2968
4028
|
] }),
|
|
2969
|
-
/* @__PURE__ */
|
|
4029
|
+
/* @__PURE__ */ jsx8(
|
|
2970
4030
|
"button",
|
|
2971
4031
|
{
|
|
2972
4032
|
type: "button",
|
|
@@ -2976,7 +4036,7 @@ function SelectionToolbar({ selection, actions, alwaysShow, className }) {
|
|
|
2976
4036
|
children: "clear"
|
|
2977
4037
|
}
|
|
2978
4038
|
),
|
|
2979
|
-
/* @__PURE__ */
|
|
4039
|
+
/* @__PURE__ */ jsx8("span", { className: "qt-selection-actions", children: actions(selectedIds, selection) })
|
|
2980
4040
|
] });
|
|
2981
4041
|
}
|
|
2982
4042
|
export {
|
|
@@ -2986,6 +4046,7 @@ export {
|
|
|
2986
4046
|
MetricsPanel,
|
|
2987
4047
|
QueryBuilder,
|
|
2988
4048
|
SavedQueriesModal,
|
|
4049
|
+
SelectColumnEditor,
|
|
2989
4050
|
SelectionToolbar,
|
|
2990
4051
|
defaultRenderers,
|
|
2991
4052
|
resolveRenderer,
|