@pythia-software/query-table-ui 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -241,7 +241,10 @@ multi-select editor, with ANY / ALL / NONE / EMPTY modes, badge presentation,
241
241
  removable tags, keyboard controls, and a mobile bottom sheet. For stable tag
242
242
  keys, also set `arrayCaseSensitive: true` and provide static `{ value, label }`
243
243
  options. NONE includes empty/missing arrays while excluding every selected key.
244
- Other fields and existing predicates keep their current editors.
244
+ `enum` fields, and `text` fields with static `filter.values.options`, can opt in
245
+ too: they get ANY ("is one of") and NONE ("is none of", keeping empty rows)
246
+ without writing one `!=` term per value. Other fields and existing predicates
247
+ keep their current editors.
245
248
 
246
249
  ```ts
247
250
  filter: {
@@ -531,13 +534,32 @@ reserves only the tick gutter, allowing more width for data. Full charts retain
531
534
  their usual title placement. Metric loading uses an accessible spinner and
532
535
  respects reduced-motion preferences.
533
536
 
534
- Compact box plots label each row above its glyph and show its median. Each row
535
- uses an independent linear scale focused on its whiskers, omitting outlier
536
- markers. A visible guide explains that the box spans the middle 50%, the line
537
- marks the median, and scales differ between rows. Full statistics and outlier
538
- counts remain in accessible mark descriptions and the data table. Allow at least
539
- 52px per plotted row plus the guide; constrain the supplied bucket count when
540
- using a fixed-height cover. The default full chart retains its shared scale.
537
+ Compact box plots label each row above its glyph and show its median. All
538
+ visible rows share one axis spanning their full observed range, including
539
+ sampled outliers and true extremes. `display.xScale` selects linear or log
540
+ coordinates and retains explicit bounds; as in full charts, nonpositive boxes
541
+ are omitted on a log axis with a visible explanation. Allow about 30px per row,
542
+ 20px for the shared ticks, and room for the guide; constrain the supplied bucket
543
+ count when using a fixed-height cover.
544
+
545
+ Linear-interpolated quartiles in sparse samples can extend past the last
546
+ observed Tukey inlier. Visual whiskers meet the box edge in that case rather
547
+ than ending inside it. Hover retains the original quartiles, observed whisker
548
+ endpoints, full range, mean, sample count, and outlier count. Neither the
549
+ computation nor the returned summary is modified.
550
+
551
+ Tooltips include the hovered series color. Their hover/focus state is isolated
552
+ from plot construction, so moving between marks does not rebuild axes or
553
+ reformat chart labels. Escape dismisses the tooltip before reaching an enclosing
554
+ dialog. Compact histogram tooltips show the interval and its frequency; full
555
+ charts and data tables retain population and NULL counts.
556
+
557
+ Compact pie/ring legends in top/bottom positions use aligned category, value,
558
+ and share slots in two columns. Set `--qt-metric-legend-columns: 1` on the card
559
+ for a single column, or use the `MetricClassNames.legend` CSS hook. These are
560
+ host presentation choices and do not change the saved query or exact pie
561
+ denominator. Native SVG charts preserve the viewport aspect ratio, including
562
+ circular pie/ring geometry and circular scatter markers.
541
563
 
542
564
  ### Underline list bars
543
565
 
@@ -0,0 +1,7 @@
1
+ import {
2
+ FormulaEditor
3
+ } from "./chunk-2HUYXV2R.js";
4
+ export {
5
+ FormulaEditor as default
6
+ };
7
+ //# sourceMappingURL=FormulaEditor-5E3URZZD.js.map
@@ -220,6 +220,7 @@ var SEARCH_ALIASES = {
220
220
  RIGHT: "suffix ending last characters",
221
221
  SUBSTRING: "slice middle characters",
222
222
  LENGTH: "size count characters",
223
+ URL_ENCODE: "percent escape encode link query parameter uri",
223
224
  LOWER: "lowercase",
224
225
  UPPER: "uppercase",
225
226
  CONCAT: "concatenate combine append",
@@ -775,4 +776,4 @@ export {
775
776
  FormulaFunctionBrowser,
776
777
  FormulaEditor
777
778
  };
778
- //# sourceMappingURL=chunk-TC5KDYUF.js.map
779
+ //# sourceMappingURL=chunk-2HUYXV2R.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/FormulaEditor.tsx","../src/formulaEditorHelpers.ts","../src/FormulaFunctionBrowser.tsx","../src/formulaFunctionCatalogue.ts"],"sourcesContent":["import {\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type KeyboardEvent,\n type MutableRefObject,\n type Ref,\n} from \"react\";\nimport {\n FORMULA_FUNCTIONS,\n FormulaError,\n type FormulaFunction,\n type FieldDef,\n} from \"@pythia-software/query-table-core\";\nimport {\n formulaCompletions,\n formulaContext,\n signatureAt,\n type FormulaCompletions,\n type FormulaSuggestion,\n} from \"./formulaEditorHelpers\";\n\nimport { FormulaFunctionBrowser } from \"./FormulaFunctionBrowser\";\n\nexport interface FormulaEditorActions {\n insertFunction: (name: string) => void;\n}\n\nexport interface FormulaEditorProps {\n actionsRef?: Ref<FormulaEditorActions>;\n functions?: readonly FormulaFunction[];\n value: string;\n onChange: (value: string) => void;\n fields: FieldDef[];\n compile: (value: string) => unknown;\n /** Metric authoring uses a separate reference pane and no suggestions. */\n suggestions?: boolean;\n showLibrary?: boolean;\n rows?: number;\n ariaLabel?: string;\n inputRef?: MutableRefObject<HTMLTextAreaElement | null>;\n onFocus?: () => void;\n onReady?: (input: HTMLTextAreaElement) => void;\n}\n\nexport default function FormulaEditor({\n value,\n actionsRef,\n functions = FORMULA_FUNCTIONS,\n onChange,\n fields,\n compile,\n suggestions = true,\n showLibrary = true,\n ariaLabel = \"Computed column formula\",\n inputRef,\n onFocus,\n onReady,\n rows = 6,\n}: FormulaEditorProps) {\n const id = useId();\n const editor = useRef<HTMLTextAreaElement | null>(null);\n const applyingEdit = useRef(false);\n const composing = useRef(false);\n // Track generated pairs through edits; existing closing characters stay editable.\n const paired = useRef<{\n value: string;\n ranges: { open: number; close: number }[];\n }>({ value, ranges: [] });\n const trackEdit = (next: string) => {\n const previous = paired.current.value;\n if (previous === next) return;\n let from = 0;\n while (\n from < previous.length &&\n from < next.length &&\n previous[from] === next[from]\n )\n from++;\n let oldEnd = previous.length,\n newEnd = next.length;\n while (\n oldEnd > from &&\n newEnd > from &&\n previous[oldEnd - 1] === next[newEnd - 1]\n ) {\n oldEnd--;\n newEnd--;\n }\n const shift = newEnd - oldEnd;\n paired.current.ranges = paired.current.ranges\n .filter(\n ({ open, close }) =>\n !(open >= from && open < oldEnd) &&\n !(close >= from && close < oldEnd),\n )\n .map(({ open, close }) => ({\n open: open >= oldEnd ? open + shift : open,\n close: close >= oldEnd ? close + shift : close,\n }));\n paired.current.value = next;\n };\n const selection = useRef({ start: 0, end: 0 });\n const latestCompile = useRef(compile);\n latestCompile.current = compile;\n const [caret, setCaret] = useState(0);\n const [completions, setCompletions] = useState<FormulaCompletions | null>(\n null,\n );\n const [active, setActive] = useState(0);\n const [diagnostic, setDiagnostic] = useState<{\n message: string;\n from: number;\n to: number;\n } | null>(null);\n const options = suggestions ? (completions?.options.slice(0, 50) ?? []) : [];\n const hint = suggestions ? signatureAt(value.slice(0, caret), functions) : \"\";\n\n useEffect(() => {\n // Native edits already updated the DOM; avoid resetting selection/undo on each keystroke.\n const input = editor.current;\n if (input && input.value !== value) {\n input.value = value;\n paired.current = { value, ranges: [] };\n setCaret(input.selectionStart);\n setCompletions(null);\n }\n setDiagnostic(null);\n const timer = setTimeout(() => {\n try {\n latestCompile.current(value);\n setDiagnostic(null);\n } catch (error) {\n const from = error instanceof FormulaError ? error.from : 0;\n const to = error instanceof FormulaError ? error.to : value.length;\n setDiagnostic({\n message: error instanceof Error ? error.message : String(error),\n from: Math.max(0, Math.min(from, value.length)),\n to: Math.max(0, Math.min(to, value.length)),\n });\n }\n }, 250);\n return () => clearTimeout(timer);\n }, [value]);\n\n useEffect(() => {\n if (editor.current) onReady?.(editor.current);\n }, [onReady]);\n\n useEffect(() => {\n document\n .getElementById(`${id}-option-${active}`)\n ?.scrollIntoView?.({ block: \"nearest\" });\n }, [active, id]);\n\n const suggest = (explicit = false) => {\n const input = editor.current;\n if (!suggestions || !input || composing.current) return;\n selection.current = {\n start: input.selectionStart,\n end: input.selectionEnd,\n };\n setCaret(input.selectionStart);\n setActive(0);\n setCompletions(\n formulaCompletions(\n input.value,\n input.selectionStart,\n input.selectionEnd,\n fields,\n explicit,\n functions,\n ),\n );\n };\n\n const insert = (\n from: number,\n to: number,\n text: string,\n caretOffset = text.length,\n pair?: { open: number; close: number },\n ) => {\n const input = editor.current;\n if (!input) return;\n input.focus();\n input.setSelectionRange(from, to);\n applyingEdit.current = true;\n try {\n // insertText preserves native textarea undo in Chromium/WebKit. Browsers\n // without this editing command still have a plain-text insertion fallback.\n const before = input.value;\n try {\n document.execCommand?.(\"insertText\", false, text);\n } catch {\n /* use fallback */\n }\n if (input.value === before) input.setRangeText(text, from, to, \"end\");\n } finally {\n applyingEdit.current = false;\n }\n trackEdit(input.value);\n if (pair)\n paired.current.ranges.push({\n open: from + pair.open,\n close: from + pair.close,\n });\n input.setSelectionRange(from + caretOffset, from + caretOffset);\n setCaret(input.selectionStart);\n setCompletions(null);\n onChange(input.value);\n };\n\n const accept = (option: FormulaSuggestion) => {\n if (completions)\n insert(\n completions.from,\n completions.to,\n option.insert,\n option.caretOffset,\n option.insert.endsWith(\"()\")\n ? { open: option.insert.length - 2, close: option.insert.length - 1 }\n : undefined,\n );\n };\n\n const insertFunction = (name: string) => {\n const input = editor.current;\n if (!input) return;\n const from = input.selectionStart;\n const to = input.selectionEnd;\n const selected = input.value.slice(from, to);\n insert(\n from,\n to,\n `${name}(${selected})`,\n name.length + selected.length + 1,\n { open: name.length, close: name.length + selected.length + 1 },\n );\n };\n useImperativeHandle(actionsRef, () => ({ insertFunction }));\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n const input = event.currentTarget;\n if (composing.current || event.nativeEvent.isComposing) return;\n if (suggestions && event.ctrlKey && event.code === \"Space\") {\n event.preventDefault();\n suggest(true);\n return;\n }\n if (options.length) {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setCompletions(null);\n return;\n }\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setActive(\n (index) =>\n (index + (event.key === \"ArrowDown\" ? 1 : options.length - 1)) %\n options.length,\n );\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n const option = options[active];\n if (option) accept(option);\n return;\n }\n }\n if (event.ctrlKey || event.metaKey || event.altKey) return;\n const start = input.selectionStart,\n end = input.selectionEnd;\n const context = formulaContext(input.value, start);\n const close = event.key === \"(\" ? \")\" : event.key === '\"' ? '\"' : undefined;\n if (\n close &&\n !context.quoted &&\n context.fieldStart < 0 &&\n (start !== end ||\n end === input.value.length ||\n /[\\s)\\]}]/.test(input.value[end]!))\n ) {\n event.preventDefault();\n const selection = input.value.slice(start, end);\n insert(start, end, event.key + selection + close, selection.length + 1, {\n open: 0,\n close: selection.length + 1,\n });\n } else if (\n start === end &&\n input.value[start] === event.key &&\n paired.current.ranges.some((pair) => pair.close === start) &&\n ((event.key === \")\" && !context.quoted && context.fieldStart < 0) ||\n (event.key === '\"' && context.quoted && !context.escaped))\n ) {\n event.preventDefault();\n paired.current.ranges = paired.current.ranges.filter(\n (pair) => pair.close !== start,\n );\n input.setSelectionRange(start + 1, start + 1);\n setCaret(start + 1);\n setCompletions(null);\n } else if (\n event.key === \"Backspace\" &&\n start === end &&\n start > 0 &&\n [\"()\", \"[]\", '\"\"'].includes(input.value.slice(start - 1, start + 1)) &&\n (!context.quoted ||\n (input.value.slice(start - 1, start + 1) === '\"\"' && !context.escaped))\n ) {\n event.preventDefault();\n insert(start - 1, start + 1, \"\");\n }\n };\n\n return (\n <div className=\"qt-formula-layout\">\n <div className=\"qt-formula-editor\">\n <textarea\n ref={(input) => {\n editor.current = input;\n if (inputRef) inputRef.current = input;\n }}\n className=\"qt-formula-input\"\n aria-label={ariaLabel}\n aria-describedby={`${id}-hint${diagnostic ? ` ${id}-error` : \"\"}`}\n aria-invalid={diagnostic ? true : undefined}\n aria-autocomplete={suggestions ? \"list\" : \"none\"}\n aria-controls={options.length ? `${id}-suggestions` : undefined}\n aria-activedescendant={\n options.length ? `${id}-option-${active}` : undefined\n }\n defaultValue={value}\n rows={rows}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n onChange={(event) => {\n if (applyingEdit.current) return;\n trackEdit(event.currentTarget.value);\n onChange(event.currentTarget.value);\n if (!composing.current) suggest();\n }}\n onSelect={(event) => {\n setCaret(event.currentTarget.selectionStart);\n const input = event.currentTarget;\n if (\n !applyingEdit.current &&\n (input.selectionStart !== selection.current.start ||\n input.selectionEnd !== selection.current.end)\n )\n setCompletions(null);\n }}\n onKeyDown={onKeyDown}\n onCompositionStart={() => {\n composing.current = true;\n setCompletions(null);\n }}\n onCompositionEnd={() => {\n composing.current = false;\n suggest();\n }}\n onBlur={() => setCompletions(null)}\n onFocus={onFocus}\n />\n <div className=\"qt-formula-tools\">\n {suggestions && (\n <button\n type=\"button\"\n className=\"qt-link-btn\"\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n editor.current?.focus();\n suggest(true);\n }}\n >\n Suggestions\n </button>\n )}\n <span>\n Line {value.slice(0, caret).split(\"\\n\").length}, column{\" \"}\n {caret - value.slice(0, caret).lastIndexOf(\"\\n\")}\n </span>\n </div>\n {options.length > 0 && (\n <div\n className=\"qt-formula-completions\"\n data-qt-formula-suggestions=\"\"\n >\n <ul\n id={`${id}-suggestions`}\n role=\"listbox\"\n aria-label=\"Formula suggestions\"\n onMouseDown={(event) => event.preventDefault()}\n >\n {options.map((option, index) => (\n <li\n key={option.label}\n id={`${id}-option-${index}`}\n role=\"option\"\n aria-selected={index === active}\n title={option.description}\n onClick={() => accept(option)}\n >\n <strong>{option.label}</strong>\n <small>{option.detail}</small>\n </li>\n ))}\n </ul>\n <p>\n {options[active]?.description} · ↑↓ choose, Enter inserts, Esc\n closes\n </p>\n {completions && completions.options.length > options.length && (\n <p>Type to narrow the first {options.length} suggestions.</p>\n )}\n </div>\n )}\n <p id={`${id}-hint`} className=\"qt-formula-hint\" aria-live=\"polite\">\n {hint}\n </p>\n {diagnostic && (\n <div id={`${id}-error`} className=\"qt-formula-error\" role=\"status\">\n {diagnostic.message}{\" \"}\n <button\n type=\"button\"\n className=\"qt-link-btn\"\n onClick={() => {\n editor.current?.focus();\n editor.current?.setSelectionRange(\n diagnostic.from,\n diagnostic.to,\n );\n setCaret(diagnostic.from);\n }}\n >\n Select error location\n </button>\n </div>\n )}\n </div>\n {showLibrary && (\n <FormulaFunctionBrowser\n functions={functions}\n onInsert={insertFunction}\n />\n )}\n </div>\n );\n}\n","import {\n FORMULA_FUNCTIONS,\n type FieldDef,\n type FormulaFunction,\n} from \"@pythia-software/query-table-core\";\n\nexport const FORMULA_EDITOR_HELP =\n \"Use [field] references. Ctrl+Space opens suggestions.\";\n\nexport interface FormulaSuggestion {\n label: string;\n detail: string;\n description: string;\n insert: string;\n // Place the caret inside the function call after inserting paired parentheses.\n caretOffset?: number;\n}\n\nexport interface FormulaCompletions {\n from: number;\n to: number;\n options: FormulaSuggestion[];\n}\n\n/** Scan strings and escaped field names before interpreting punctuation. */\nexport function formulaContext(text: string, end = text.length) {\n let quoted = false;\n let fieldStart = -1;\n let escaped = false;\n for (let i = 0; i < end; i++) {\n const c = text[i];\n if (quoted) {\n if (c === \"\\\\\") {\n escaped = i + 1 >= end;\n i++;\n } else if (c === '\"') quoted = false;\n } else if (fieldStart >= 0) {\n if (c === \"]\") {\n if (text[i + 1] === \"]\") i++;\n else fieldStart = -1;\n }\n } else if (c === '\"') quoted = true;\n else if (c === \"[\") fieldStart = i;\n }\n return { quoted, fieldStart, escaped };\n}\n\nexport function formulaCompletions(\n text: string,\n start: number,\n end: number,\n fields: FieldDef[],\n explicit = false,\n functions: readonly FormulaFunction[] = FORMULA_FUNCTIONS,\n): FormulaCompletions | null {\n const before = text.slice(0, start);\n const { quoted, fieldStart } = formulaContext(text, start);\n if (quoted || (!explicit && start !== end)) return null;\n const word = before.match(/\\b[A-Za-z_][A-Za-z_0-9]*$/)?.[0] ?? \"\";\n if (!explicit && fieldStart < 0 && !word) return null;\n const from =\n start !== end ? start : fieldStart >= 0 ? fieldStart : start - word.length;\n let to = end;\n if (start === end && fieldStart >= 0) {\n // Replace the rest of a field reference too, including escaped ]] characters.\n for (let i = fieldStart + 1; i < text.length; i++) {\n if (text[i] === \"]\") {\n if (text[i + 1] === \"]\") {\n i++;\n continue;\n }\n to = i + 1;\n break;\n }\n if (text[i] === \"\\n\" || text[i] === \"[\") break;\n }\n } else if (start === end) {\n to += text.slice(end).match(/^[A-Za-z_0-9]*/)?.[0].length ?? 0;\n }\n const prefix = start !== end ? \"\" : before.slice(from).toLowerCase();\n const options: FormulaSuggestion[] = fields.map((f) => ({\n label: `[${f.name.replace(/\\]/g, \"]]\")}]`,\n detail: `${f.label} · ${f.type}`,\n description: f.name,\n insert: `[${f.name.replace(/\\]/g, \"]]\")}]`,\n }));\n if (fieldStart < 0) {\n options.push(\n ...functions.map((f) => ({\n label: f.name,\n detail: f.signature,\n description: f.description,\n insert: text[to] === \"(\" ? f.name : `${f.name}()`,\n caretOffset: f.name.length + 1,\n })),\n ...[\"TRUE\", \"FALSE\", \"NULL\", \"AND\", \"OR\", \"NOT\", \"IN\", \"BETWEEN\"].map(\n (label) => ({\n label,\n detail: \"Keyword\",\n description: \"\",\n insert: label,\n }),\n ),\n );\n }\n return {\n from,\n to,\n options: options.filter(\n (option) =>\n option.label.toLowerCase().startsWith(prefix) ||\n (fieldStart >= 0 &&\n option.detail.toLowerCase().includes(prefix.slice(1))),\n ),\n };\n}\n\nexport function signatureAt(\n text: string,\n functions: readonly FormulaFunction[] = FORMULA_FUNCTIONS,\n): string {\n const stack: { name: string; argument: number }[] = [];\n let quoted = false,\n field = false;\n for (let i = 0; i < text.length; i++) {\n const c = text[i];\n if (quoted) {\n if (c === \"\\\\\") i++;\n else if (c === '\"') quoted = false;\n continue;\n }\n if (field) {\n if (c === \"]\") {\n if (text[i + 1] === \"]\") i++;\n else field = false;\n }\n continue;\n }\n if (c === '\"') {\n quoted = true;\n continue;\n }\n if (c === \"[\") {\n field = true;\n continue;\n }\n if (c === \"(\")\n stack.push({\n name:\n text\n .slice(0, i)\n .match(/([A-Za-z_]+)\\s*$/)?.[1]\n ?.toUpperCase() ?? \"\",\n argument: 1,\n });\n else if (c === \")\") stack.pop();\n else if (c === \",\" && stack.length) stack[stack.length - 1]!.argument++;\n }\n const call = stack[stack.length - 1],\n spec = functions.find((f) => f.name === call?.name);\n return spec && call\n ? `${spec.signature} · Argument ${call.argument} — ${spec.description}`\n : FORMULA_EDITOR_HELP;\n}\n","import { useEffect, useId, useRef, useState } from \"react\";\nimport {\n FUNCTION_CATEGORIES,\n FUNCTION_RESULT_TYPES,\n findFormulaFunctions,\n functionCategory,\n functionResultLabel,\n type FunctionCategory,\n} from \"./formulaFunctionCatalogue\";\nimport {\n FORMULA_FUNCTIONS,\n type FormulaFunction,\n} from \"@pythia-software/query-table-core\";\n\nexport function FormulaFunctionBrowser({\n onInsert,\n functions = FORMULA_FUNCTIONS,\n variant = \"card\",\n}: {\n onInsert?: (name: string) => void;\n functions?: readonly FormulaFunction[];\n variant?: \"card\" | \"panel\";\n}) {\n const id = useId();\n const list = useRef<HTMLDivElement>(null);\n const [search, setSearch] = useState(\"\");\n const [category, setCategory] = useState<FunctionCategory | \"all\">(\"all\");\n const [result, setResult] = useState<FormulaFunction[\"result\"] | \"all\">(\n \"all\",\n );\n const [active, setActive] = useState(0);\n const matches = findFormulaFunctions(search, category, result, functions);\n const concept = FUNCTION_CATEGORIES.find((item) => item.id === category);\n\n useEffect(() => {\n // Scroll the list itself without moving the workbench while searching.\n const option = list.current?.children[active] as HTMLElement | undefined;\n const viewport = list.current;\n if (!option || !viewport) return;\n // The positioned list is the option’s offset parent.\n const top = option.offsetTop;\n if (top < viewport.scrollTop) viewport.scrollTop = top;\n else if (\n top + option.offsetHeight >\n viewport.scrollTop + viewport.clientHeight\n )\n viewport.scrollTop = top + option.offsetHeight - viewport.clientHeight;\n }, [active, search, category, result]);\n\n return (\n <aside\n className={`qt-function-library qt-function-library--${variant}`}\n aria-label=\"Function library\"\n >\n {variant === \"card\" && (\n <>\n <h4>Function library</h4>\n <p className=\"qt-muted\">Find a function by name or what it does.</p>\n </>\n )}\n <label className=\"qt-function-search\">\n Search functions\n <input\n className=\"qt-input\"\n type=\"search\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded={matches.length > 0}\n aria-controls={`${id}-results`}\n aria-activedescendant={\n matches[active] ? `${id}-${matches[active]!.name}` : undefined\n }\n placeholder=\"e.g. trim, minimum, date…\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setActive(0);\n }}\n onKeyDown={(event) => {\n if (\n event.nativeEvent.isComposing ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n !matches.length\n )\n return;\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setActive(\n (index) =>\n (index +\n (event.key === \"ArrowDown\" ? 1 : matches.length - 1)) %\n matches.length,\n );\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const fn = matches[active];\n if (fn) onInsert?.(fn.name);\n }\n }}\n />\n </label>\n <div className=\"qt-function-filters\">\n <label>\n Browse by concept\n <select\n value={category}\n onChange={(event) => {\n setCategory(event.target.value as FunctionCategory | \"all\");\n setActive(0);\n }}\n >\n <option value=\"all\">All functions</option>\n {FUNCTION_CATEGORIES.filter((item) =>\n functions.some((fn) => functionCategory(fn) === item.id),\n ).map((item) => (\n <option key={item.id} value={item.id}>\n {item.label}\n </option>\n ))}\n </select>\n </label>\n <label>\n Return type\n <select\n value={result}\n onChange={(event) => {\n setResult(\n event.target.value as FormulaFunction[\"result\"] | \"all\",\n );\n setActive(0);\n }}\n >\n <option value=\"all\">Any type</option>\n {FUNCTION_RESULT_TYPES.map((type) => (\n <option key={type.value} value={type.value}>\n {type.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n {concept && <p className=\"qt-function-concept\">{concept.description}</p>}\n <p className=\"qt-function-count\" aria-live=\"polite\">\n {matches.length} {matches.length === 1 ? \"function\" : \"functions\"}\n </p>\n <div\n ref={list}\n className=\"qt-function-list\"\n id={`${id}-results`}\n role=\"listbox\"\n aria-label=\"Function library results\"\n >\n {matches.map((fn, index) => (\n <button\n type=\"button\"\n role=\"option\"\n id={`${id}-${fn.name}`}\n key={fn.name}\n aria-selected={index === active}\n aria-label={onInsert ? `Insert ${fn.name}` : fn.name}\n disabled={!onInsert}\n onMouseDown={(event) => event.preventDefault()}\n onFocus={() => setActive(index)}\n onClick={() => onInsert?.(fn.name)}\n >\n <span className=\"qt-function-card-title\">\n <strong>{fn.name}</strong>\n <span>{functionResultLabel(fn.result)}</span>\n </span>\n <code>{fn.signature}</code>\n <small>{fn.description}</small>\n <span className=\"qt-function-card-concept\">\n {\n FUNCTION_CATEGORIES.find(\n (item) => item.id === functionCategory(fn),\n )!.label\n }\n </span>\n </button>\n ))}\n </div>\n {!matches.length && (\n <p className=\"qt-function-empty\">\n No functions match. Try a shorter search or a different concept or\n return type.\n </p>\n )}\n <p className=\"qt-function-insert-hint\">\n {onInsert\n ? \"Click a function or use ↑↓ and Enter in search to insert it at the cursor. Select formula text first to wrap it.\"\n : \"Select a formula column to insert a function.\"}\n </p>\n </aside>\n );\n}\n","import {\n FORMULA_FUNCTIONS,\n type FormulaFunction,\n type FormulaType,\n} from \"@pythia-software/query-table-core\";\n\nexport const FUNCTION_CATEGORIES = [\n {\n id: \"aggregates\",\n label: \"Aggregate functions\",\n description: \"Combine values from the rows in each group.\",\n keywords: \"aggregate summary group median average count sum\",\n },\n {\n id: \"strings\",\n label: \"String functions\",\n description: \"Slice, combine, format, and clean text.\",\n keywords: \"string text characters whitespace case concatenate\",\n },\n {\n id: \"matching\",\n label: \"Matching & regex\",\n description: \"Find text, test patterns, and extract matches.\",\n keywords: \"search matching regex regular expression pattern capture\",\n },\n {\n id: \"numbers\",\n label: \"Numbers & statistics\",\n description: \"Round, transform, and compare numbers within a row.\",\n keywords: \"math numeric arithmetic statistics statistical rounding\",\n },\n {\n id: \"logic\",\n label: \"Conditions & missing values\",\n description: \"Choose results, handle errors, and work with empty values.\",\n keywords: \"logic boolean conditional null missing empty fallback error\",\n },\n {\n id: \"conversion\",\n label: \"Type conversion\",\n description: \"Convert values to text, numbers, booleans, or dates.\",\n keywords: \"convert conversion cast type parse\",\n },\n {\n id: \"arrays\",\n label: \"Array functions\",\n description: \"Split, join, count, and organize lists of text.\",\n keywords: \"array list collection elements unique deduplicate\",\n },\n {\n id: \"dates\",\n label: \"Dates & times\",\n description:\n \"Extract date parts, format timestamps, and calculate durations.\",\n keywords: \"date datetime time timestamp calendar duration UTC\",\n },\n] as const;\nexport type FunctionCategory = (typeof FUNCTION_CATEGORIES)[number][\"id\"];\n\nexport const FUNCTION_RESULT_TYPES: {\n value: FormulaFunction[\"result\"];\n label: string;\n}[] = [\n { value: \"text\", label: \"Text\" },\n { value: \"number\", label: \"Number\" },\n { value: \"bool\", label: \"Boolean\" },\n { value: \"datetime\", label: \"Date/time\" },\n { value: \"textarray\", label: \"Text array\" },\n { value: \"branch\", label: \"Depends on arguments\" },\n];\nexport function functionResultLabel(result: FormulaType | \"branch\"): string {\n return (\n FUNCTION_RESULT_TYPES.find((type) => type.value === result)?.label ?? result\n );\n}\n\n/** Concept is separate from return type: LENGTH is a string operation returning a number. */\nexport function functionCategory(fn: FormulaFunction): FunctionCategory {\n if (\n [\"COUNT\", \"COUNT_DISTINCT\", \"SUM\", \"AVG\", \"MIN\", \"MAX\", \"MEDIAN\"].includes(\n fn.name,\n )\n )\n return \"aggregates\";\n if (fn.name.startsWith(\"TO_\")) return \"conversion\";\n if (\n fn.name.startsWith(\"REGEX_\") ||\n [\"CONTAINS\", \"STARTS_WITH\", \"ENDS_WITH\"].includes(fn.name)\n )\n return \"matching\";\n if (fn.name.startsWith(\"ARRAY_\") || [\"SPLIT\", \"JOIN\"].includes(fn.name))\n return \"arrays\";\n if (\n fn.name.includes(\"DATE\") ||\n (fn.args !== \"any\" && fn.args.includes(\"datetime\"))\n )\n return \"dates\";\n if (fn.result === \"branch\" || fn.args === \"any\") return \"logic\";\n if (fn.args.includes(\"text\")) return \"strings\";\n return \"numbers\";\n}\n\nconst SEARCH_ALIASES: Record<string, string> = {\n LEFT: \"prefix beginning first characters\",\n RIGHT: \"suffix ending last characters\",\n SUBSTRING: \"slice middle characters\",\n LENGTH: \"size count characters\",\n LOWER: \"lowercase\",\n UPPER: \"uppercase\",\n CONCAT: \"concatenate combine append\",\n CONCAT_WS: \"concatenate separator combine append\",\n LEAST: \"minimum smallest min\",\n GREATEST: \"maximum largest max\",\n ABS: \"absolute magnitude\",\n SQRT: \"square root\",\n CEIL: \"ceiling round up\",\n FLOOR: \"round down\",\n IF: \"if else decision\",\n COALESCE: \"default first non-null missing fallback\",\n NULLIF: \"equal missing null\",\n IFERROR: \"recover error default fallback\",\n ARRAY_LENGTH: \"size count elements\",\n DATE_DIFF: \"difference elapsed duration\",\n};\nconst normalize = (text: string) =>\n text.toLowerCase().replace(/[_-]/g, \" \").trim();\n\nexport function findFormulaFunctions(\n search: string,\n category: FunctionCategory | \"all\" = \"all\",\n result: FormulaFunction[\"result\"] | \"all\" = \"all\",\n catalogue: readonly FormulaFunction[] = FORMULA_FUNCTIONS,\n): FormulaFunction[] {\n const query = normalize(search);\n const terms = query.split(/\\s+/).filter(Boolean);\n const functions = catalogue.filter((fn) => {\n const concept = functionCategory(fn);\n if (\n (category !== \"all\" && concept !== category) ||\n (result !== \"all\" && fn.result !== result)\n )\n return false;\n const metadata = FUNCTION_CATEGORIES.find((item) => item.id === concept)!;\n const haystack = normalize(\n `${fn.name} ${fn.signature} ${fn.description} ${metadata.label} ${metadata.keywords} ${SEARCH_ALIASES[fn.name] ?? \"\"} ${functionResultLabel(fn.result)}`,\n );\n return terms.every((term) => haystack.includes(term));\n });\n // Exact/prefix name matches appear first; browsing stays alphabetical.\n const rank = (fn: FormulaFunction) =>\n !query\n ? 2\n : normalize(fn.name) === query\n ? 0\n : normalize(fn.name).startsWith(query)\n ? 1\n : 2;\n return functions.sort(\n (a, b) => rank(a) - rank(b) || a.name.localeCompare(b.name),\n );\n}\n"],"mappings":"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","names":["useEffect","useId","useRef","useState","FORMULA_FUNCTIONS","FORMULA_FUNCTIONS","FORMULA_FUNCTIONS","jsx","jsxs","FORMULA_FUNCTIONS","useId","useRef","useState","useEffect","selection"]}
1
+ {"version":3,"sources":["../src/FormulaEditor.tsx","../src/formulaEditorHelpers.ts","../src/FormulaFunctionBrowser.tsx","../src/formulaFunctionCatalogue.ts"],"sourcesContent":["import {\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type KeyboardEvent,\n type MutableRefObject,\n type Ref,\n} from \"react\";\nimport {\n FORMULA_FUNCTIONS,\n FormulaError,\n type FormulaFunction,\n type FieldDef,\n} from \"@pythia-software/query-table-core\";\nimport {\n formulaCompletions,\n formulaContext,\n signatureAt,\n type FormulaCompletions,\n type FormulaSuggestion,\n} from \"./formulaEditorHelpers\";\n\nimport { FormulaFunctionBrowser } from \"./FormulaFunctionBrowser\";\n\nexport interface FormulaEditorActions {\n insertFunction: (name: string) => void;\n}\n\nexport interface FormulaEditorProps {\n actionsRef?: Ref<FormulaEditorActions>;\n functions?: readonly FormulaFunction[];\n value: string;\n onChange: (value: string) => void;\n fields: FieldDef[];\n compile: (value: string) => unknown;\n /** Metric authoring uses a separate reference pane and no suggestions. */\n suggestions?: boolean;\n showLibrary?: boolean;\n rows?: number;\n ariaLabel?: string;\n inputRef?: MutableRefObject<HTMLTextAreaElement | null>;\n onFocus?: () => void;\n onReady?: (input: HTMLTextAreaElement) => void;\n}\n\nexport default function FormulaEditor({\n value,\n actionsRef,\n functions = FORMULA_FUNCTIONS,\n onChange,\n fields,\n compile,\n suggestions = true,\n showLibrary = true,\n ariaLabel = \"Computed column formula\",\n inputRef,\n onFocus,\n onReady,\n rows = 6,\n}: FormulaEditorProps) {\n const id = useId();\n const editor = useRef<HTMLTextAreaElement | null>(null);\n const applyingEdit = useRef(false);\n const composing = useRef(false);\n // Track generated pairs through edits; existing closing characters stay editable.\n const paired = useRef<{\n value: string;\n ranges: { open: number; close: number }[];\n }>({ value, ranges: [] });\n const trackEdit = (next: string) => {\n const previous = paired.current.value;\n if (previous === next) return;\n let from = 0;\n while (\n from < previous.length &&\n from < next.length &&\n previous[from] === next[from]\n )\n from++;\n let oldEnd = previous.length,\n newEnd = next.length;\n while (\n oldEnd > from &&\n newEnd > from &&\n previous[oldEnd - 1] === next[newEnd - 1]\n ) {\n oldEnd--;\n newEnd--;\n }\n const shift = newEnd - oldEnd;\n paired.current.ranges = paired.current.ranges\n .filter(\n ({ open, close }) =>\n !(open >= from && open < oldEnd) &&\n !(close >= from && close < oldEnd),\n )\n .map(({ open, close }) => ({\n open: open >= oldEnd ? open + shift : open,\n close: close >= oldEnd ? close + shift : close,\n }));\n paired.current.value = next;\n };\n const selection = useRef({ start: 0, end: 0 });\n const latestCompile = useRef(compile);\n latestCompile.current = compile;\n const [caret, setCaret] = useState(0);\n const [completions, setCompletions] = useState<FormulaCompletions | null>(\n null,\n );\n const [active, setActive] = useState(0);\n const [diagnostic, setDiagnostic] = useState<{\n message: string;\n from: number;\n to: number;\n } | null>(null);\n const options = suggestions ? (completions?.options.slice(0, 50) ?? []) : [];\n const hint = suggestions ? signatureAt(value.slice(0, caret), functions) : \"\";\n\n useEffect(() => {\n // Native edits already updated the DOM; avoid resetting selection/undo on each keystroke.\n const input = editor.current;\n if (input && input.value !== value) {\n input.value = value;\n paired.current = { value, ranges: [] };\n setCaret(input.selectionStart);\n setCompletions(null);\n }\n setDiagnostic(null);\n const timer = setTimeout(() => {\n try {\n latestCompile.current(value);\n setDiagnostic(null);\n } catch (error) {\n const from = error instanceof FormulaError ? error.from : 0;\n const to = error instanceof FormulaError ? error.to : value.length;\n setDiagnostic({\n message: error instanceof Error ? error.message : String(error),\n from: Math.max(0, Math.min(from, value.length)),\n to: Math.max(0, Math.min(to, value.length)),\n });\n }\n }, 250);\n return () => clearTimeout(timer);\n }, [value]);\n\n useEffect(() => {\n if (editor.current) onReady?.(editor.current);\n }, [onReady]);\n\n useEffect(() => {\n document\n .getElementById(`${id}-option-${active}`)\n ?.scrollIntoView?.({ block: \"nearest\" });\n }, [active, id]);\n\n const suggest = (explicit = false) => {\n const input = editor.current;\n if (!suggestions || !input || composing.current) return;\n selection.current = {\n start: input.selectionStart,\n end: input.selectionEnd,\n };\n setCaret(input.selectionStart);\n setActive(0);\n setCompletions(\n formulaCompletions(\n input.value,\n input.selectionStart,\n input.selectionEnd,\n fields,\n explicit,\n functions,\n ),\n );\n };\n\n const insert = (\n from: number,\n to: number,\n text: string,\n caretOffset = text.length,\n pair?: { open: number; close: number },\n ) => {\n const input = editor.current;\n if (!input) return;\n input.focus();\n input.setSelectionRange(from, to);\n applyingEdit.current = true;\n try {\n // insertText preserves native textarea undo in Chromium/WebKit. Browsers\n // without this editing command still have a plain-text insertion fallback.\n const before = input.value;\n try {\n document.execCommand?.(\"insertText\", false, text);\n } catch {\n /* use fallback */\n }\n if (input.value === before) input.setRangeText(text, from, to, \"end\");\n } finally {\n applyingEdit.current = false;\n }\n trackEdit(input.value);\n if (pair)\n paired.current.ranges.push({\n open: from + pair.open,\n close: from + pair.close,\n });\n input.setSelectionRange(from + caretOffset, from + caretOffset);\n setCaret(input.selectionStart);\n setCompletions(null);\n onChange(input.value);\n };\n\n const accept = (option: FormulaSuggestion) => {\n if (completions)\n insert(\n completions.from,\n completions.to,\n option.insert,\n option.caretOffset,\n option.insert.endsWith(\"()\")\n ? { open: option.insert.length - 2, close: option.insert.length - 1 }\n : undefined,\n );\n };\n\n const insertFunction = (name: string) => {\n const input = editor.current;\n if (!input) return;\n const from = input.selectionStart;\n const to = input.selectionEnd;\n const selected = input.value.slice(from, to);\n insert(\n from,\n to,\n `${name}(${selected})`,\n name.length + selected.length + 1,\n { open: name.length, close: name.length + selected.length + 1 },\n );\n };\n useImperativeHandle(actionsRef, () => ({ insertFunction }));\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n const input = event.currentTarget;\n if (composing.current || event.nativeEvent.isComposing) return;\n if (suggestions && event.ctrlKey && event.code === \"Space\") {\n event.preventDefault();\n suggest(true);\n return;\n }\n if (options.length) {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setCompletions(null);\n return;\n }\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setActive(\n (index) =>\n (index + (event.key === \"ArrowDown\" ? 1 : options.length - 1)) %\n options.length,\n );\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n const option = options[active];\n if (option) accept(option);\n return;\n }\n }\n if (event.ctrlKey || event.metaKey || event.altKey) return;\n const start = input.selectionStart,\n end = input.selectionEnd;\n const context = formulaContext(input.value, start);\n const close = event.key === \"(\" ? \")\" : event.key === '\"' ? '\"' : undefined;\n if (\n close &&\n !context.quoted &&\n context.fieldStart < 0 &&\n (start !== end ||\n end === input.value.length ||\n /[\\s)\\]}]/.test(input.value[end]!))\n ) {\n event.preventDefault();\n const selection = input.value.slice(start, end);\n insert(start, end, event.key + selection + close, selection.length + 1, {\n open: 0,\n close: selection.length + 1,\n });\n } else if (\n start === end &&\n input.value[start] === event.key &&\n paired.current.ranges.some((pair) => pair.close === start) &&\n ((event.key === \")\" && !context.quoted && context.fieldStart < 0) ||\n (event.key === '\"' && context.quoted && !context.escaped))\n ) {\n event.preventDefault();\n paired.current.ranges = paired.current.ranges.filter(\n (pair) => pair.close !== start,\n );\n input.setSelectionRange(start + 1, start + 1);\n setCaret(start + 1);\n setCompletions(null);\n } else if (\n event.key === \"Backspace\" &&\n start === end &&\n start > 0 &&\n [\"()\", \"[]\", '\"\"'].includes(input.value.slice(start - 1, start + 1)) &&\n (!context.quoted ||\n (input.value.slice(start - 1, start + 1) === '\"\"' && !context.escaped))\n ) {\n event.preventDefault();\n insert(start - 1, start + 1, \"\");\n }\n };\n\n return (\n <div className=\"qt-formula-layout\">\n <div className=\"qt-formula-editor\">\n <textarea\n ref={(input) => {\n editor.current = input;\n if (inputRef) inputRef.current = input;\n }}\n className=\"qt-formula-input\"\n aria-label={ariaLabel}\n aria-describedby={`${id}-hint${diagnostic ? ` ${id}-error` : \"\"}`}\n aria-invalid={diagnostic ? true : undefined}\n aria-autocomplete={suggestions ? \"list\" : \"none\"}\n aria-controls={options.length ? `${id}-suggestions` : undefined}\n aria-activedescendant={\n options.length ? `${id}-option-${active}` : undefined\n }\n defaultValue={value}\n rows={rows}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n onChange={(event) => {\n if (applyingEdit.current) return;\n trackEdit(event.currentTarget.value);\n onChange(event.currentTarget.value);\n if (!composing.current) suggest();\n }}\n onSelect={(event) => {\n setCaret(event.currentTarget.selectionStart);\n const input = event.currentTarget;\n if (\n !applyingEdit.current &&\n (input.selectionStart !== selection.current.start ||\n input.selectionEnd !== selection.current.end)\n )\n setCompletions(null);\n }}\n onKeyDown={onKeyDown}\n onCompositionStart={() => {\n composing.current = true;\n setCompletions(null);\n }}\n onCompositionEnd={() => {\n composing.current = false;\n suggest();\n }}\n onBlur={() => setCompletions(null)}\n onFocus={onFocus}\n />\n <div className=\"qt-formula-tools\">\n {suggestions && (\n <button\n type=\"button\"\n className=\"qt-link-btn\"\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n editor.current?.focus();\n suggest(true);\n }}\n >\n Suggestions\n </button>\n )}\n <span>\n Line {value.slice(0, caret).split(\"\\n\").length}, column{\" \"}\n {caret - value.slice(0, caret).lastIndexOf(\"\\n\")}\n </span>\n </div>\n {options.length > 0 && (\n <div\n className=\"qt-formula-completions\"\n data-qt-formula-suggestions=\"\"\n >\n <ul\n id={`${id}-suggestions`}\n role=\"listbox\"\n aria-label=\"Formula suggestions\"\n onMouseDown={(event) => event.preventDefault()}\n >\n {options.map((option, index) => (\n <li\n key={option.label}\n id={`${id}-option-${index}`}\n role=\"option\"\n aria-selected={index === active}\n title={option.description}\n onClick={() => accept(option)}\n >\n <strong>{option.label}</strong>\n <small>{option.detail}</small>\n </li>\n ))}\n </ul>\n <p>\n {options[active]?.description} · ↑↓ choose, Enter inserts, Esc\n closes\n </p>\n {completions && completions.options.length > options.length && (\n <p>Type to narrow the first {options.length} suggestions.</p>\n )}\n </div>\n )}\n <p id={`${id}-hint`} className=\"qt-formula-hint\" aria-live=\"polite\">\n {hint}\n </p>\n {diagnostic && (\n <div id={`${id}-error`} className=\"qt-formula-error\" role=\"status\">\n {diagnostic.message}{\" \"}\n <button\n type=\"button\"\n className=\"qt-link-btn\"\n onClick={() => {\n editor.current?.focus();\n editor.current?.setSelectionRange(\n diagnostic.from,\n diagnostic.to,\n );\n setCaret(diagnostic.from);\n }}\n >\n Select error location\n </button>\n </div>\n )}\n </div>\n {showLibrary && (\n <FormulaFunctionBrowser\n functions={functions}\n onInsert={insertFunction}\n />\n )}\n </div>\n );\n}\n","import {\n FORMULA_FUNCTIONS,\n type FieldDef,\n type FormulaFunction,\n} from \"@pythia-software/query-table-core\";\n\nexport const FORMULA_EDITOR_HELP =\n \"Use [field] references. Ctrl+Space opens suggestions.\";\n\nexport interface FormulaSuggestion {\n label: string;\n detail: string;\n description: string;\n insert: string;\n // Place the caret inside the function call after inserting paired parentheses.\n caretOffset?: number;\n}\n\nexport interface FormulaCompletions {\n from: number;\n to: number;\n options: FormulaSuggestion[];\n}\n\n/** Scan strings and escaped field names before interpreting punctuation. */\nexport function formulaContext(text: string, end = text.length) {\n let quoted = false;\n let fieldStart = -1;\n let escaped = false;\n for (let i = 0; i < end; i++) {\n const c = text[i];\n if (quoted) {\n if (c === \"\\\\\") {\n escaped = i + 1 >= end;\n i++;\n } else if (c === '\"') quoted = false;\n } else if (fieldStart >= 0) {\n if (c === \"]\") {\n if (text[i + 1] === \"]\") i++;\n else fieldStart = -1;\n }\n } else if (c === '\"') quoted = true;\n else if (c === \"[\") fieldStart = i;\n }\n return { quoted, fieldStart, escaped };\n}\n\nexport function formulaCompletions(\n text: string,\n start: number,\n end: number,\n fields: FieldDef[],\n explicit = false,\n functions: readonly FormulaFunction[] = FORMULA_FUNCTIONS,\n): FormulaCompletions | null {\n const before = text.slice(0, start);\n const { quoted, fieldStart } = formulaContext(text, start);\n if (quoted || (!explicit && start !== end)) return null;\n const word = before.match(/\\b[A-Za-z_][A-Za-z_0-9]*$/)?.[0] ?? \"\";\n if (!explicit && fieldStart < 0 && !word) return null;\n const from =\n start !== end ? start : fieldStart >= 0 ? fieldStart : start - word.length;\n let to = end;\n if (start === end && fieldStart >= 0) {\n // Replace the rest of a field reference too, including escaped ]] characters.\n for (let i = fieldStart + 1; i < text.length; i++) {\n if (text[i] === \"]\") {\n if (text[i + 1] === \"]\") {\n i++;\n continue;\n }\n to = i + 1;\n break;\n }\n if (text[i] === \"\\n\" || text[i] === \"[\") break;\n }\n } else if (start === end) {\n to += text.slice(end).match(/^[A-Za-z_0-9]*/)?.[0].length ?? 0;\n }\n const prefix = start !== end ? \"\" : before.slice(from).toLowerCase();\n const options: FormulaSuggestion[] = fields.map((f) => ({\n label: `[${f.name.replace(/\\]/g, \"]]\")}]`,\n detail: `${f.label} · ${f.type}`,\n description: f.name,\n insert: `[${f.name.replace(/\\]/g, \"]]\")}]`,\n }));\n if (fieldStart < 0) {\n options.push(\n ...functions.map((f) => ({\n label: f.name,\n detail: f.signature,\n description: f.description,\n insert: text[to] === \"(\" ? f.name : `${f.name}()`,\n caretOffset: f.name.length + 1,\n })),\n ...[\"TRUE\", \"FALSE\", \"NULL\", \"AND\", \"OR\", \"NOT\", \"IN\", \"BETWEEN\"].map(\n (label) => ({\n label,\n detail: \"Keyword\",\n description: \"\",\n insert: label,\n }),\n ),\n );\n }\n return {\n from,\n to,\n options: options.filter(\n (option) =>\n option.label.toLowerCase().startsWith(prefix) ||\n (fieldStart >= 0 &&\n option.detail.toLowerCase().includes(prefix.slice(1))),\n ),\n };\n}\n\nexport function signatureAt(\n text: string,\n functions: readonly FormulaFunction[] = FORMULA_FUNCTIONS,\n): string {\n const stack: { name: string; argument: number }[] = [];\n let quoted = false,\n field = false;\n for (let i = 0; i < text.length; i++) {\n const c = text[i];\n if (quoted) {\n if (c === \"\\\\\") i++;\n else if (c === '\"') quoted = false;\n continue;\n }\n if (field) {\n if (c === \"]\") {\n if (text[i + 1] === \"]\") i++;\n else field = false;\n }\n continue;\n }\n if (c === '\"') {\n quoted = true;\n continue;\n }\n if (c === \"[\") {\n field = true;\n continue;\n }\n if (c === \"(\")\n stack.push({\n name:\n text\n .slice(0, i)\n .match(/([A-Za-z_]+)\\s*$/)?.[1]\n ?.toUpperCase() ?? \"\",\n argument: 1,\n });\n else if (c === \")\") stack.pop();\n else if (c === \",\" && stack.length) stack[stack.length - 1]!.argument++;\n }\n const call = stack[stack.length - 1],\n spec = functions.find((f) => f.name === call?.name);\n return spec && call\n ? `${spec.signature} · Argument ${call.argument} — ${spec.description}`\n : FORMULA_EDITOR_HELP;\n}\n","import { useEffect, useId, useRef, useState } from \"react\";\nimport {\n FUNCTION_CATEGORIES,\n FUNCTION_RESULT_TYPES,\n findFormulaFunctions,\n functionCategory,\n functionResultLabel,\n type FunctionCategory,\n} from \"./formulaFunctionCatalogue\";\nimport {\n FORMULA_FUNCTIONS,\n type FormulaFunction,\n} from \"@pythia-software/query-table-core\";\n\nexport function FormulaFunctionBrowser({\n onInsert,\n functions = FORMULA_FUNCTIONS,\n variant = \"card\",\n}: {\n onInsert?: (name: string) => void;\n functions?: readonly FormulaFunction[];\n variant?: \"card\" | \"panel\";\n}) {\n const id = useId();\n const list = useRef<HTMLDivElement>(null);\n const [search, setSearch] = useState(\"\");\n const [category, setCategory] = useState<FunctionCategory | \"all\">(\"all\");\n const [result, setResult] = useState<FormulaFunction[\"result\"] | \"all\">(\n \"all\",\n );\n const [active, setActive] = useState(0);\n const matches = findFormulaFunctions(search, category, result, functions);\n const concept = FUNCTION_CATEGORIES.find((item) => item.id === category);\n\n useEffect(() => {\n // Scroll the list itself without moving the workbench while searching.\n const option = list.current?.children[active] as HTMLElement | undefined;\n const viewport = list.current;\n if (!option || !viewport) return;\n // The positioned list is the option’s offset parent.\n const top = option.offsetTop;\n if (top < viewport.scrollTop) viewport.scrollTop = top;\n else if (\n top + option.offsetHeight >\n viewport.scrollTop + viewport.clientHeight\n )\n viewport.scrollTop = top + option.offsetHeight - viewport.clientHeight;\n }, [active, search, category, result]);\n\n return (\n <aside\n className={`qt-function-library qt-function-library--${variant}`}\n aria-label=\"Function library\"\n >\n {variant === \"card\" && (\n <>\n <h4>Function library</h4>\n <p className=\"qt-muted\">Find a function by name or what it does.</p>\n </>\n )}\n <label className=\"qt-function-search\">\n Search functions\n <input\n className=\"qt-input\"\n type=\"search\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded={matches.length > 0}\n aria-controls={`${id}-results`}\n aria-activedescendant={\n matches[active] ? `${id}-${matches[active]!.name}` : undefined\n }\n placeholder=\"e.g. trim, minimum, date…\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setActive(0);\n }}\n onKeyDown={(event) => {\n if (\n event.nativeEvent.isComposing ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n !matches.length\n )\n return;\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setActive(\n (index) =>\n (index +\n (event.key === \"ArrowDown\" ? 1 : matches.length - 1)) %\n matches.length,\n );\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const fn = matches[active];\n if (fn) onInsert?.(fn.name);\n }\n }}\n />\n </label>\n <div className=\"qt-function-filters\">\n <label>\n Browse by concept\n <select\n value={category}\n onChange={(event) => {\n setCategory(event.target.value as FunctionCategory | \"all\");\n setActive(0);\n }}\n >\n <option value=\"all\">All functions</option>\n {FUNCTION_CATEGORIES.filter((item) =>\n functions.some((fn) => functionCategory(fn) === item.id),\n ).map((item) => (\n <option key={item.id} value={item.id}>\n {item.label}\n </option>\n ))}\n </select>\n </label>\n <label>\n Return type\n <select\n value={result}\n onChange={(event) => {\n setResult(\n event.target.value as FormulaFunction[\"result\"] | \"all\",\n );\n setActive(0);\n }}\n >\n <option value=\"all\">Any type</option>\n {FUNCTION_RESULT_TYPES.map((type) => (\n <option key={type.value} value={type.value}>\n {type.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n {concept && <p className=\"qt-function-concept\">{concept.description}</p>}\n <p className=\"qt-function-count\" aria-live=\"polite\">\n {matches.length} {matches.length === 1 ? \"function\" : \"functions\"}\n </p>\n <div\n ref={list}\n className=\"qt-function-list\"\n id={`${id}-results`}\n role=\"listbox\"\n aria-label=\"Function library results\"\n >\n {matches.map((fn, index) => (\n <button\n type=\"button\"\n role=\"option\"\n id={`${id}-${fn.name}`}\n key={fn.name}\n aria-selected={index === active}\n aria-label={onInsert ? `Insert ${fn.name}` : fn.name}\n disabled={!onInsert}\n onMouseDown={(event) => event.preventDefault()}\n onFocus={() => setActive(index)}\n onClick={() => onInsert?.(fn.name)}\n >\n <span className=\"qt-function-card-title\">\n <strong>{fn.name}</strong>\n <span>{functionResultLabel(fn.result)}</span>\n </span>\n <code>{fn.signature}</code>\n <small>{fn.description}</small>\n <span className=\"qt-function-card-concept\">\n {\n FUNCTION_CATEGORIES.find(\n (item) => item.id === functionCategory(fn),\n )!.label\n }\n </span>\n </button>\n ))}\n </div>\n {!matches.length && (\n <p className=\"qt-function-empty\">\n No functions match. Try a shorter search or a different concept or\n return type.\n </p>\n )}\n <p className=\"qt-function-insert-hint\">\n {onInsert\n ? \"Click a function or use ↑↓ and Enter in search to insert it at the cursor. Select formula text first to wrap it.\"\n : \"Select a formula column to insert a function.\"}\n </p>\n </aside>\n );\n}\n","import {\n FORMULA_FUNCTIONS,\n type FormulaFunction,\n type FormulaType,\n} from \"@pythia-software/query-table-core\";\n\nexport const FUNCTION_CATEGORIES = [\n {\n id: \"aggregates\",\n label: \"Aggregate functions\",\n description: \"Combine values from the rows in each group.\",\n keywords: \"aggregate summary group median average count sum\",\n },\n {\n id: \"strings\",\n label: \"String functions\",\n description: \"Slice, combine, format, and clean text.\",\n keywords: \"string text characters whitespace case concatenate\",\n },\n {\n id: \"matching\",\n label: \"Matching & regex\",\n description: \"Find text, test patterns, and extract matches.\",\n keywords: \"search matching regex regular expression pattern capture\",\n },\n {\n id: \"numbers\",\n label: \"Numbers & statistics\",\n description: \"Round, transform, and compare numbers within a row.\",\n keywords: \"math numeric arithmetic statistics statistical rounding\",\n },\n {\n id: \"logic\",\n label: \"Conditions & missing values\",\n description: \"Choose results, handle errors, and work with empty values.\",\n keywords: \"logic boolean conditional null missing empty fallback error\",\n },\n {\n id: \"conversion\",\n label: \"Type conversion\",\n description: \"Convert values to text, numbers, booleans, or dates.\",\n keywords: \"convert conversion cast type parse\",\n },\n {\n id: \"arrays\",\n label: \"Array functions\",\n description: \"Split, join, count, and organize lists of text.\",\n keywords: \"array list collection elements unique deduplicate\",\n },\n {\n id: \"dates\",\n label: \"Dates & times\",\n description:\n \"Extract date parts, format timestamps, and calculate durations.\",\n keywords: \"date datetime time timestamp calendar duration UTC\",\n },\n] as const;\nexport type FunctionCategory = (typeof FUNCTION_CATEGORIES)[number][\"id\"];\n\nexport const FUNCTION_RESULT_TYPES: {\n value: FormulaFunction[\"result\"];\n label: string;\n}[] = [\n { value: \"text\", label: \"Text\" },\n { value: \"number\", label: \"Number\" },\n { value: \"bool\", label: \"Boolean\" },\n { value: \"datetime\", label: \"Date/time\" },\n { value: \"textarray\", label: \"Text array\" },\n { value: \"branch\", label: \"Depends on arguments\" },\n];\nexport function functionResultLabel(result: FormulaType | \"branch\"): string {\n return (\n FUNCTION_RESULT_TYPES.find((type) => type.value === result)?.label ?? result\n );\n}\n\n/** Concept is separate from return type: LENGTH is a string operation returning a number. */\nexport function functionCategory(fn: FormulaFunction): FunctionCategory {\n if (\n [\"COUNT\", \"COUNT_DISTINCT\", \"SUM\", \"AVG\", \"MIN\", \"MAX\", \"MEDIAN\"].includes(\n fn.name,\n )\n )\n return \"aggregates\";\n if (fn.name.startsWith(\"TO_\")) return \"conversion\";\n if (\n fn.name.startsWith(\"REGEX_\") ||\n [\"CONTAINS\", \"STARTS_WITH\", \"ENDS_WITH\"].includes(fn.name)\n )\n return \"matching\";\n if (fn.name.startsWith(\"ARRAY_\") || [\"SPLIT\", \"JOIN\"].includes(fn.name))\n return \"arrays\";\n if (\n fn.name.includes(\"DATE\") ||\n (fn.args !== \"any\" && fn.args.includes(\"datetime\"))\n )\n return \"dates\";\n if (fn.result === \"branch\" || fn.args === \"any\") return \"logic\";\n if (fn.args.includes(\"text\")) return \"strings\";\n return \"numbers\";\n}\n\nconst SEARCH_ALIASES: Record<string, string> = {\n LEFT: \"prefix beginning first characters\",\n RIGHT: \"suffix ending last characters\",\n SUBSTRING: \"slice middle characters\",\n LENGTH: \"size count characters\",\n URL_ENCODE: \"percent escape encode link query parameter uri\",\n LOWER: \"lowercase\",\n UPPER: \"uppercase\",\n CONCAT: \"concatenate combine append\",\n CONCAT_WS: \"concatenate separator combine append\",\n LEAST: \"minimum smallest min\",\n GREATEST: \"maximum largest max\",\n ABS: \"absolute magnitude\",\n SQRT: \"square root\",\n CEIL: \"ceiling round up\",\n FLOOR: \"round down\",\n IF: \"if else decision\",\n COALESCE: \"default first non-null missing fallback\",\n NULLIF: \"equal missing null\",\n IFERROR: \"recover error default fallback\",\n ARRAY_LENGTH: \"size count elements\",\n DATE_DIFF: \"difference elapsed duration\",\n};\nconst normalize = (text: string) =>\n text.toLowerCase().replace(/[_-]/g, \" \").trim();\n\nexport function findFormulaFunctions(\n search: string,\n category: FunctionCategory | \"all\" = \"all\",\n result: FormulaFunction[\"result\"] | \"all\" = \"all\",\n catalogue: readonly FormulaFunction[] = FORMULA_FUNCTIONS,\n): FormulaFunction[] {\n const query = normalize(search);\n const terms = query.split(/\\s+/).filter(Boolean);\n const functions = catalogue.filter((fn) => {\n const concept = functionCategory(fn);\n if (\n (category !== \"all\" && concept !== category) ||\n (result !== \"all\" && fn.result !== result)\n )\n return false;\n const metadata = FUNCTION_CATEGORIES.find((item) => item.id === concept)!;\n const haystack = normalize(\n `${fn.name} ${fn.signature} ${fn.description} ${metadata.label} ${metadata.keywords} ${SEARCH_ALIASES[fn.name] ?? \"\"} ${functionResultLabel(fn.result)}`,\n );\n return terms.every((term) => haystack.includes(term));\n });\n // Exact/prefix name matches appear first; browsing stays alphabetical.\n const rank = (fn: FormulaFunction) =>\n !query\n ? 2\n : normalize(fn.name) === query\n ? 0\n : normalize(fn.name).startsWith(query)\n ? 1\n : 2;\n return functions.sort(\n (a, b) => rank(a) - rank(b) || a.name.localeCompare(b.name),\n );\n}\n"],"mappings":"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","names":["useEffect","useId","useRef","useState","FORMULA_FUNCTIONS","FORMULA_FUNCTIONS","FORMULA_FUNCTIONS","jsx","jsxs","FORMULA_FUNCTIONS","useId","useRef","useState","useEffect","selection"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, SetStateAction, ButtonHTMLAttributes, CSSProperties, PointerEvent, Ref, MutableRefObject } from 'react';
3
- import { FieldDef, QueryState, JsonCellDisplay, RowId, AggregationClause, AggregationBucket, AggregationResultEntry, MetricValue, SavedQuery, FieldStats, WhereClause, MetricValueFormat, FieldSchema, PropertyViewState, RelationshipAdapter, ObjectReference, PropertyFormat, PropertyViewStorage, ItemPropertyMode, FormulaFunction, GroupedRow } from '@pythia-software/query-table-core';
3
+ import { FieldDef, QueryState, LinkTargets, CellDisplay, JsonCellDisplay, RowId, AggregationClause, AggregationBucket, AggregationResultEntry, MetricValue, SavedQuery, FieldStats, WhereClause, MetricValueFormat, FieldSchema, PropertyViewState, RelationshipAdapter, ObjectReference, PropertyFormat, PropertyViewStorage, ItemPropertyMode, FormulaFunction, GroupedRow } from '@pythia-software/query-table-core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { ItemDetailsApi, SelectionApi, ColumnDragApi, QueryTableApi, QueryFilterApi, AggregationsApi, SavedQueriesApi, RequestActivityApi, PropertyViewApi, GroupingApi } from '@pythia-software/query-table-react';
6
6
  import { Placement } from '@floating-ui/react';
@@ -23,9 +23,22 @@ declare function safeLinkHref(value: unknown): string | null;
23
23
  /** Generic, domain-free renderers shipped with the package. Keys are stable and
24
24
  * referenced from FieldDef.render. Consumers spread these and add their own. */
25
25
  declare const defaultRenderers: RenderRegistry;
26
- /** Resolve a FieldDef's renderer: inline fn wins, else registry lookup, else
27
- * declarative display, else the `text` fallback. */
28
- declare function resolveRenderer<Row>(field: FieldDef<Row>, registry: RenderRegistry<Row>): CellRenderer<Row>;
26
+ interface GroupLinkOptions {
27
+ /** Host routes for query-kind link columns. */
28
+ linkTargets?: LinkTargets | undefined;
29
+ /** Called for unmodified primary clicks only. Return true when the host
30
+ * handled navigation (for example with its router); the default action is
31
+ * then prevented. Modifier and middle clicks keep native link behavior. */
32
+ onNavigate?: (href: string, event: MouseEvent) => boolean;
33
+ }
34
+ /** Renders group link columns (`SummaryColumn` with `op: "link"`) as real
35
+ * `<a href>` elements. Every href goes through `safeLinkHref`; blocked or
36
+ * unresolvable links render as muted text whose title explains why. */
37
+ declare function createGroupLinkRenderer(options?: GroupLinkOptions): CellRenderer;
38
+ /** Resolve a FieldDef's renderer: a per-column display override (from
39
+ * `SelectColumn.display`) wins, then an inline fn, then a registry lookup, then
40
+ * the field's declarative display, else the `text` fallback. */
41
+ declare function resolveRenderer<Row>(field: FieldDef<Row>, registry: RenderRegistry<Row>, columnDisplay?: CellDisplay): CellRenderer<Row>;
29
42
 
30
43
  declare const artwork: {
31
44
  externalLink: react_jsx_runtime.JSX.Element;
@@ -159,6 +172,15 @@ interface DataTableProps<Row> {
159
172
  onQueryChange: (q: SetStateAction<QueryState>) => void;
160
173
  /** Resolves FieldDef.render keys. Pass `{ ...defaultRenderers, ...yours }`. */
161
174
  renderers: RenderRegistry<Row>;
175
+ /** Host routes for grouped link columns whose destination is a query
176
+ * (`to: { kind: "query", target }`). The host turns the composed query into
177
+ * an href; it is safety-checked before rendering. */
178
+ linkTargets?: LinkTargets;
179
+ /** Called when a link column is clicked without modifiers. Return true when
180
+ * the host handled navigation (e.g. client-side routing); the browser's
181
+ * default is then prevented. Modifier, middle and "open in new tab" clicks
182
+ * keep native link behavior. */
183
+ onNavigate?: (href: string, event: MouseEvent) => boolean;
162
184
  /** Row → stable id (defaults to reading the schema's idField). */
163
185
  rowId: (row: Row) => RowId | null;
164
186
  /** Optional checkbox column + selection behavior (from `api.selection`). */
@@ -192,7 +214,7 @@ declare function DataTable<Row>(props: DataTableProps<Row>): ReactNode;
192
214
  type MetricBucket = AggregationBucket;
193
215
  type RenderingClause = AggregationClause;
194
216
  /** Optional exact remainder for transports that truncate additive groups. */
195
- type MetricRenderResult = Pick<AggregationResultEntry, "buckets" | "error" | "groupCount" | "coverage" | "scope" | "processedRows"> & {
217
+ type MetricRenderResult = Pick<AggregationResultEntry, "buckets" | "error" | "groupCount" | "coverage" | "scope" | "processedRows" | "hiddenGroups"> & {
196
218
  other?: AggregationBucket;
197
219
  };
198
220
 
@@ -300,7 +322,7 @@ interface MetricsPanelProps<Row> {
300
322
  }
301
323
  declare function MetricsPanel<Row>({ aggregations, api, onEditMetric, onEditDashboard, fields, renderers, emptyMessage, classNames, theme, locale, onInspect, }: MetricsPanelProps<Row>): ReactNode;
302
324
  interface MetricCardProps<Row> {
303
- /** Sparse cover axes and labelled box rows with independent whisker scales. */
325
+ /** Sparse cover axes and dense labelled box rows sharing one scale and observed extremes. */
304
326
  compact?: boolean | undefined;
305
327
  onEdit?: (() => void) | undefined;
306
328
  clause: RenderingClause;
@@ -315,7 +337,7 @@ interface MetricCardProps<Row> {
315
337
  classNames?: MetricClassNames | undefined;
316
338
  onInspect?: ((bucket: MetricBucket, clause: RenderingClause) => void) | undefined;
317
339
  }
318
- declare function MetricCard<Row>({ compact, clause, buckets: explicitBuckets, onEdit, result, fields, loading, renderers, theme, locale, className, classNames, onInspect, }: MetricCardProps<Row>): ReactNode;
340
+ declare function MetricCard<Row>({ compact, clause: sourceClause, buckets: explicitBuckets, onEdit, result, fields, loading, renderers, theme, locale, className, classNames, onInspect, }: MetricCardProps<Row>): ReactNode;
319
341
 
320
342
  type OverlayAnchor = HTMLElement | {
321
343
  x: number;
@@ -735,4 +757,4 @@ declare function GroupedColumnsEditor({ controller, onClose, renderers, initialF
735
757
  renderers?: RenderRegistry<GroupedRow>;
736
758
  }): react_jsx_runtime.JSX.Element;
737
759
 
738
- export { type CellContext, CellMenu, type CellMenuProps, type CellRenderer, type ColumnRole, ColumnRoleBadge, DataTable, type DataTableProps, EditorChoice, EditorContext, EditorModal, type EditorPaneSize, EditorPaneTitle, EditorPanelSeparator, EditorPanelToggle, type EditorWorkbench, FieldPicker, type FieldPickerProps, type FilterValuePresentation, FilterValueProvider, FormulaEditor, type FormulaEditorActions, type FormulaEditorProps, FormulaFunctionBrowser, GroupedColumnsEditor, Icon, type IconName, type IconProps, ItemDetailShare, JsonCell, type JsonCellProps, type JsonEntry, type JsonInspection, JsonValueView, Menu, type MenuAnchor, MenuButton, type MenuButtonProps, type MenuClassNames, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemProps, type MenuProps, MenuSeparator, MenuSubmenu, type MenuSubmenuProps, MetricCard, type MetricCardProps, type MetricClassNames, type MetricTheme, MetricsEditor, type MetricsEditorProps, MetricsPanel, type MetricsPanelProps, ModalSurface, ObjectInspector, type ObjectInspectorProps, ObjectProperties, type ObjectPropertiesProps, PickerAnchor, PickerSurface, Popover, type PopoverProps, PresentedFilterValue, PropertyViewControls, PropertyViewEditor, QueryBuilder, type QueryBuilderClassNames, type QueryBuilderProps, QueryFilterRow, type RenderRegistry, ReorderList, RequestActivity, type RequestActivityProps, SavedQueriesModal, type SavedQueriesModalProps, SelectColumnEditor, type SelectColumnEditorProps, type SelectPresentation, SelectionToolbar, type SelectionToolbarProps, type TableClassNames, createMetricColorResolver, defaultRenderers, formatMetricOutput, formatPropertyValue, inspectJsonValue, itemDetailHref, metricTimestamp, resolveRenderer, safeLinkHref, searchJsonEntries, useEditorWorkbench, useMobileLayout, validateMetricFormat };
760
+ export { type CellContext, CellMenu, type CellMenuProps, type CellRenderer, type ColumnRole, ColumnRoleBadge, DataTable, type DataTableProps, EditorChoice, EditorContext, EditorModal, type EditorPaneSize, EditorPaneTitle, EditorPanelSeparator, EditorPanelToggle, type EditorWorkbench, FieldPicker, type FieldPickerProps, type FilterValuePresentation, FilterValueProvider, FormulaEditor, type FormulaEditorActions, type FormulaEditorProps, FormulaFunctionBrowser, type GroupLinkOptions, GroupedColumnsEditor, Icon, type IconName, type IconProps, ItemDetailShare, JsonCell, type JsonCellProps, type JsonEntry, type JsonInspection, JsonValueView, Menu, type MenuAnchor, MenuButton, type MenuButtonProps, type MenuClassNames, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemProps, type MenuProps, MenuSeparator, MenuSubmenu, type MenuSubmenuProps, MetricCard, type MetricCardProps, type MetricClassNames, type MetricTheme, MetricsEditor, type MetricsEditorProps, MetricsPanel, type MetricsPanelProps, ModalSurface, ObjectInspector, type ObjectInspectorProps, ObjectProperties, type ObjectPropertiesProps, PickerAnchor, PickerSurface, Popover, type PopoverProps, PresentedFilterValue, PropertyViewControls, PropertyViewEditor, QueryBuilder, type QueryBuilderClassNames, type QueryBuilderProps, QueryFilterRow, type RenderRegistry, ReorderList, RequestActivity, type RequestActivityProps, SavedQueriesModal, type SavedQueriesModalProps, SelectColumnEditor, type SelectColumnEditorProps, type SelectPresentation, SelectionToolbar, type SelectionToolbarProps, type TableClassNames, createGroupLinkRenderer, createMetricColorResolver, defaultRenderers, formatMetricOutput, formatPropertyValue, inspectJsonValue, itemDetailHref, metricTimestamp, resolveRenderer, safeLinkHref, searchJsonEntries, useEditorWorkbench, useMobileLayout, validateMetricFormat };