@ai-matrx/records-ui 0.93.72 → 0.93.73

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/dist/index.d.cts CHANGED
@@ -2290,7 +2290,7 @@ interface CustomFieldsAgentDoor {
2290
2290
  setValues: (values: CustomFieldValuesRequest) => Promise<CustomFieldSetResult>;
2291
2291
  }
2292
2292
  declare const ADDABLE_TYPES: {
2293
- value: "number" | "attachment" | "currency" | "datetime" | "email" | "text" | "member" | "percent" | "phone" | "url" | "long_text";
2293
+ value: "number" | "member" | "text" | "long_text" | "currency" | "percent" | "datetime" | "email" | "url" | "phone" | "attachment";
2294
2294
  label: string;
2295
2295
  }[];
2296
2296
  /**
package/dist/index.d.ts CHANGED
@@ -2290,7 +2290,7 @@ interface CustomFieldsAgentDoor {
2290
2290
  setValues: (values: CustomFieldValuesRequest) => Promise<CustomFieldSetResult>;
2291
2291
  }
2292
2292
  declare const ADDABLE_TYPES: {
2293
- value: "number" | "attachment" | "currency" | "datetime" | "email" | "text" | "member" | "percent" | "phone" | "url" | "long_text";
2293
+ value: "number" | "member" | "text" | "long_text" | "currency" | "percent" | "datetime" | "email" | "url" | "phone" | "attachment";
2294
2294
  label: string;
2295
2295
  }[];
2296
2296
  /**
package/dist/index.js CHANGED
@@ -9296,62 +9296,215 @@ function AddFirstRowButton({ onAdd, busy }) {
9296
9296
  // src/gridDrill.tsx
9297
9297
  import { useEffect as useEffect23, useMemo as useMemo16, useState as useState27 } from "react";
9298
9298
  import { useRecordsClient as useRecordsClient15 } from "@ai-matrx/records/react";
9299
- import { isRuleFilter as isRuleFilter2 } from "@ai-matrx/records";
9299
+ import {
9300
+ isRuleFilter as isRuleFilter2
9301
+ } from "@ai-matrx/records";
9300
9302
  import {
9301
9303
  bucketWindow,
9302
9304
  drillRequests,
9303
9305
  parseDimensionRef,
9304
9306
  useDrillUrlState
9305
9307
  } from "@ai-matrx/design-system/data-table";
9306
- var MEASURE_OPS = [
9307
- { op: "sum", word: "Sum of", additive: true },
9308
- { op: "avg", word: "Average", additive: false },
9309
- { op: "min", word: "Lowest", additive: false },
9310
- { op: "max", word: "Highest", additive: false },
9311
- { op: "median", word: "Median", additive: false }
9312
- ];
9313
- var DIMENSION_KINDS = {
9314
- select: "choice",
9315
- checkbox: "boolean",
9316
- text: "text",
9317
- datetime: "time"
9318
- };
9319
- function drillDimensionsFor(fields, options) {
9320
- const out = [];
9321
- for (const field2 of fields) {
9322
- const kind = DIMENSION_KINDS[String(editorKindFor(field2))];
9323
- if (!kind) continue;
9324
- if (field2.multi) continue;
9325
- const dim = { key: field2.key, label: fieldName(field2), kind };
9326
- if (kind === "text") dim.cardinality = "high";
9327
- if (kind === "choice" && isChoiceField(field2)) {
9308
+ var ADDITIVE_OPS = /* @__PURE__ */ new Set(["count", "sum", "filled", "empty"]);
9309
+ function drillDimensionsOf(def, labels) {
9310
+ if (!def) return [];
9311
+ return def.dimensions.map((d) => {
9312
+ const dim = { key: d.key, label: d.label, kind: d.kind };
9313
+ if (d.cardinality) dim.cardinality = d.cardinality;
9314
+ if (d.grains) dim.grains = d.grains.filter((g) => g !== "hour");
9315
+ const choices = new Map((d.choices ?? []).map((c) => [c.value, c.label]));
9316
+ if (d.kind === "choice" || d.kind === "relation") {
9328
9317
  dim.labelFor = (value) => {
9329
9318
  if (value === null || value === "") return "No value";
9330
- return optionFor(options.get(field2.key), value)?.label ?? value;
9319
+ return choices.get(value) ?? labels.get(d.key)?.get(value) ?? value;
9331
9320
  };
9332
9321
  }
9333
- out.push(dim);
9334
- }
9335
- return out;
9322
+ return dim;
9323
+ });
9336
9324
  }
9337
- function drillMeasuresFor(fields) {
9338
- const out = [{ key: "count", label: "Count", additive: true }];
9339
- for (const field2 of fields) {
9340
- const kind = String(editorKindFor(field2));
9341
- if (kind !== "number" && kind !== "currency" && kind !== "percent") continue;
9342
- for (const m of MEASURE_OPS) {
9343
- out.push({ key: `${m.op}_${field2.key}`, label: `${m.word} ${fieldName(field2)}`, additive: m.additive });
9344
- }
9325
+ function drillMeasuresOf(def) {
9326
+ if (!def) return [];
9327
+ return def.measures.map((m) => ({ key: m.key, label: m.label, additive: m.additive ?? ADDITIVE_OPS.has(m.op) }));
9328
+ }
9329
+ function drillPathsOf(def) {
9330
+ return (def?.paths ?? []).map((p) => p.levels);
9331
+ }
9332
+ function useDrillDefinition(source) {
9333
+ const client = useRecordsClient15();
9334
+ const key = source ? JSON.stringify(source) : null;
9335
+ const [state, setState] = useState27({ key: null, def: null, error: null });
9336
+ useEffect23(() => {
9337
+ if (!key) return;
9338
+ let cancelled = false;
9339
+ void client.drillDescribe({ source: JSON.parse(key) }).then((got) => {
9340
+ if (cancelled) return;
9341
+ setState(got.ok ? { key, def: got.data ?? null, error: null } : { key, def: null, error: got.error.message || "This table's groupings could not be read." });
9342
+ });
9343
+ return () => {
9344
+ cancelled = true;
9345
+ };
9346
+ }, [client, key]);
9347
+ return state.key === key ? { def: state.def, error: state.error } : { def: null, error: null };
9348
+ }
9349
+ function drillWhere(base, trail, booleans) {
9350
+ const where = { ...base ?? {} };
9351
+ for (const w of trail) {
9352
+ const { key, grain } = parseDimensionRef(w.dim);
9353
+ if (grain) where[w.dim] = w.value;
9354
+ else if (booleans.has(key) && (w.value === "true" || w.value === "false")) where[key] = w.value === "true";
9355
+ else where[key] = w.value;
9345
9356
  }
9346
- return out;
9357
+ return where;
9358
+ }
9359
+ function rowOf(row) {
9360
+ const groups = {};
9361
+ for (const [k, v] of Object.entries(row.groups ?? {})) groups[k] = v === null || v === void 0 ? null : String(v);
9362
+ const measures = {};
9363
+ for (const [k, v] of Object.entries(row.measures ?? {})) measures[k] = v === null || v === void 0 ? null : Number(v);
9364
+ return { groups, measures, row_count: Number(row.row_count ?? 0) };
9347
9365
  }
9348
- function measureFor(key) {
9366
+ function storeMeasure(key) {
9349
9367
  if (key === "count") return { op: "count" };
9350
9368
  const at = key.indexOf("_");
9351
9369
  if (at <= 0) return null;
9352
- const op2 = key.slice(0, at);
9353
- if (!MEASURE_OPS.some((m) => m.op === op2)) return null;
9354
- return { op: op2, key: key.slice(at + 1) };
9370
+ return { op: key.slice(0, at), key: key.slice(at + 1) };
9371
+ }
9372
+ function useDrillAnswers(args) {
9373
+ const client = useRecordsClient15();
9374
+ const { source, question, enabled, version } = args;
9375
+ const [answers, setAnswers] = useState27({});
9376
+ const [labels, setLabels] = useState27(() => /* @__PURE__ */ new Map());
9377
+ const [error, setError] = useState27(null);
9378
+ const [says, setSays] = useState27([]);
9379
+ const askKey2 = JSON.stringify({
9380
+ source,
9381
+ by: question.by,
9382
+ across: question.across ?? null,
9383
+ show: question.show,
9384
+ where: args.where,
9385
+ rule: args.rule && isRuleFilter2(args.rule) ? args.rule : null
9386
+ });
9387
+ useEffect23(() => {
9388
+ if (!enabled || !source || question.by.length === 0) return;
9389
+ const asked2 = JSON.parse(askKey2);
9390
+ let cancelled = false;
9391
+ setAnswers({});
9392
+ setError(null);
9393
+ const requests = drillRequests({ by: asked2.by, across: asked2.across, show: asked2.show, where: [] });
9394
+ void Promise.all(
9395
+ requests.map(async (request) => {
9396
+ if (asked2.rule && asked2.source.kind === "table") {
9397
+ const time = request.by.find((ref) => parseDimensionRef(ref).grain !== null) ?? null;
9398
+ const t = time ? parseDimensionRef(time) : null;
9399
+ const measures = asked2.show.map(storeMeasure).filter((m) => m !== null);
9400
+ const got2 = await client.recordAggregate({
9401
+ table_id: asked2.source.id,
9402
+ groupBy: request.by.filter((ref) => ref !== time),
9403
+ measures,
9404
+ bucket: t && t.grain ? { key: t.key, by: t.grain } : null,
9405
+ filter: asked2.rule,
9406
+ limit: 200
9407
+ });
9408
+ if (!got2.ok) return { request, error: got2.error.message };
9409
+ return {
9410
+ request,
9411
+ rows: (got2.data ?? []).map((row) => {
9412
+ const groups = {};
9413
+ for (const ref of request.by) {
9414
+ const k = ref === time && t ? t.key : ref;
9415
+ groups[ref] = row.groups[k] ?? null;
9416
+ }
9417
+ return { groups, measures: row.measures, row_count: row.row_count };
9418
+ }),
9419
+ labels: {},
9420
+ says: []
9421
+ };
9422
+ }
9423
+ const got = await client.drillAsk({
9424
+ source: asked2.source,
9425
+ question: { by: request.by, show: asked2.show, where: asked2.where }
9426
+ });
9427
+ if (!got.ok) return { request, error: got.error.message || "The groups could not be read." };
9428
+ const answer = got.data;
9429
+ const kind = request.by.length === 0 ? "total" : "group";
9430
+ const rows = answer.rows.filter((r) => r.kind === kind && (kind === "group" || !r.groups || Object.keys(r.groups).length === 0)).map(rowOf);
9431
+ const found = {};
9432
+ for (const r of answer.rows) {
9433
+ for (const [dim, text] of Object.entries(r.labels ?? {})) {
9434
+ const value = r.groups?.[dim];
9435
+ if (value === null || value === void 0) continue;
9436
+ (found[dim] ??= {})[String(value)] = text;
9437
+ }
9438
+ }
9439
+ return { request, rows, labels: found, says: answer.says };
9440
+ })
9441
+ ).then((results) => {
9442
+ if (cancelled) return;
9443
+ const failed = results.find((r) => "error" in r);
9444
+ if (failed) {
9445
+ setError(failed.error);
9446
+ return;
9447
+ }
9448
+ const out = {};
9449
+ const nextLabels = /* @__PURE__ */ new Map();
9450
+ const sentences = [];
9451
+ for (const r of results) {
9452
+ if (!("rows" in r)) continue;
9453
+ out[r.request.key] = r.rows;
9454
+ for (const [dim, map] of Object.entries(r.labels ?? {})) {
9455
+ const held = nextLabels.get(dim) ?? /* @__PURE__ */ new Map();
9456
+ for (const [v, t] of Object.entries(map)) held.set(v, t);
9457
+ nextLabels.set(dim, held);
9458
+ }
9459
+ for (const s of r.says ?? []) if (!sentences.includes(s)) sentences.push(s);
9460
+ }
9461
+ setLabels(nextLabels);
9462
+ setSays(sentences);
9463
+ setAnswers(out);
9464
+ });
9465
+ return () => {
9466
+ cancelled = true;
9467
+ };
9468
+ }, [client, askKey2, enabled, version, question.by.length]);
9469
+ return { answers, labels, error, says };
9470
+ }
9471
+ function drillQuestionFor(question, def) {
9472
+ if (!def) return question;
9473
+ const known = (ref) => def.dimensions.some((d) => d.key === parseDimensionRef(ref).key);
9474
+ const show = question.show.filter((key) => def.measures.some((m) => m.key === key));
9475
+ return {
9476
+ ...question,
9477
+ by: question.by.filter(known),
9478
+ across: question.across && known(question.across) ? question.across : null,
9479
+ show: show.length > 0 ? show : [def.measures[0]?.key ?? "count"],
9480
+ where: question.where.filter((w) => known(w.dim))
9481
+ };
9482
+ }
9483
+ function useDrill(args) {
9484
+ const enabled = args.enabled ?? true;
9485
+ const { def, error: describeError } = useDrillDefinition(enabled ? args.source : null);
9486
+ const question = useMemo16(() => drillQuestionFor(args.question, def), [args.question, def]);
9487
+ const booleans = useMemo16(() => new Set((def?.dimensions ?? []).filter((d) => d.kind === "boolean").map((d) => d.key)), [def]);
9488
+ const where = useMemo16(() => drillWhere(args.filter, question.where, booleans), [args.filter, question.where, booleans]);
9489
+ const { answers, labels, error } = useDrillAnswers({
9490
+ source: args.source,
9491
+ question,
9492
+ where,
9493
+ rule: args.rule,
9494
+ version: args.version,
9495
+ enabled: enabled && def !== null
9496
+ });
9497
+ const dimensions = useMemo16(() => drillDimensionsOf(def, labels), [def, labels]);
9498
+ const measures = useMemo16(() => drillMeasuresOf(def), [def]);
9499
+ const paths = useMemo16(() => drillPathsOf(def), [def]);
9500
+ const { onQuestionChange } = args;
9501
+ const rowNoun = args.rowNoun ?? "record";
9502
+ const rootLabel = args.rootLabel ?? "All records";
9503
+ const config = useMemo16(() => {
9504
+ if (!enabled || !def || dimensions.length === 0) return null;
9505
+ return { dimensions, measures, paths, question, answers, error: describeError ?? error, rowNoun, rootLabel, onQuestionChange };
9506
+ }, [enabled, def, dimensions, measures, paths, question, answers, describeError, error, rowNoun, rootLabel, onQuestionChange]);
9507
+ return { config, def };
9355
9508
  }
9356
9509
  function questionFromGrouping(grouping) {
9357
9510
  if (!grouping) return { by: [], across: null, show: ["count"], where: [] };
@@ -9412,110 +9565,49 @@ function filterWithTrail(fields, base, trail) {
9412
9565
  }
9413
9566
  return { op: "and", args };
9414
9567
  }
9415
- function useDrillAnswers(args) {
9416
- const client = useRecordsClient15();
9417
- const { tableId, question, enabled, version } = args;
9418
- const [answers, setAnswers] = useState27({});
9419
- const [error, setError] = useState27(null);
9420
- const askKey2 = JSON.stringify({ by: question.by, across: question.across ?? null, show: question.show, filter: args.filter ?? null });
9421
- useEffect23(() => {
9422
- if (!enabled || question.by.length === 0) return;
9423
- const asked2 = JSON.parse(askKey2);
9424
- const measures = asked2.show.map(measureFor).filter((m) => m !== null);
9425
- if (!measures.some((m) => m.op === "count")) measures.unshift({ op: "count" });
9426
- let cancelled = false;
9427
- setAnswers({});
9428
- setError(null);
9429
- const requests = drillRequests({ by: asked2.by, across: asked2.across, show: asked2.show, where: [] });
9430
- void Promise.all(
9431
- requests.map(async (request) => {
9432
- const time = request.by.find((ref) => parseDimensionRef(ref).grain !== null) ?? null;
9433
- const plain = request.by.filter((ref) => ref !== time);
9434
- const grain = time ? parseDimensionRef(time) : null;
9435
- const got = await client.recordAggregate({
9436
- table_id: tableId,
9437
- groupBy: plain,
9438
- measures,
9439
- bucket: grain && grain.grain ? { key: grain.key, by: grain.grain } : null,
9440
- ...asked2.filter ? { filter: asked2.filter } : {},
9441
- limit: 200
9442
- });
9443
- if (!got.ok) return { request, error: got.error.message || "The groups could not be read." };
9444
- const rows = (got.data ?? []).map((row) => {
9445
- const groups = {};
9446
- for (const ref of plain) groups[ref] = row.groups[ref] ?? null;
9447
- if (time && grain) groups[time] = row.groups[grain.key] ?? null;
9448
- return { groups, measures: row.measures, row_count: row.row_count };
9449
- });
9450
- return { request, rows };
9451
- })
9452
- ).then((results) => {
9453
- if (cancelled) return;
9454
- const failed = results.find((r) => "error" in r);
9455
- if (failed) {
9456
- setError(failed.error);
9457
- return;
9458
- }
9459
- const out = {};
9460
- for (const r of results) if ("rows" in r) out[r.request.key] = r.rows;
9461
- setAnswers(out);
9462
- });
9463
- return () => {
9464
- cancelled = true;
9465
- };
9466
- }, [client, tableId, askKey2, enabled, version, question.by.length]);
9467
- return { answers, error };
9468
- }
9469
9568
  function useGridDrillQuestion(args) {
9470
- const { fields, presentation, filter } = args;
9471
- const choiceKeys = useMemo16(() => fields.filter((f) => isChoiceField(f) && !f.multi).map((f) => f.key), [fields]);
9472
- const options = useChoiceOptions(fields, args.enabled ? choiceKeys : []);
9473
- const dimensions = useMemo16(() => drillDimensionsFor(fields, options), [fields, options]);
9474
- const measures = useMemo16(() => drillMeasuresFor(fields), [fields]);
9569
+ const { tableId, fields, presentation, filter } = args;
9570
+ const source = useMemo16(() => ({ kind: "table", id: tableId }), [tableId]);
9571
+ const { def } = useDrillDefinition(args.enabled ? source : null);
9475
9572
  const saved = useMemo16(() => questionFromGrouping(presentation.grouping), [presentation.grouping]);
9476
9573
  const url = useDrillUrlState({ fallback: saved });
9477
- const question = useMemo16(() => {
9478
- const known = (ref) => dimensions.some((d) => d.key === parseDimensionRef(ref).key);
9479
- const show = url.question.show.filter((key) => measures.some((m) => m.key === key));
9480
- return {
9481
- ...url.question,
9482
- by: url.question.by.filter(known),
9483
- across: url.question.across && known(url.question.across) ? url.question.across : null,
9484
- show: show.length > 0 ? show : ["count"],
9485
- where: url.question.where.filter((w) => known(w.dim))
9486
- };
9487
- }, [url.question, dimensions, measures]);
9488
- const enabled = args.enabled && dimensions.length > 0;
9574
+ const question = useMemo16(() => drillQuestionFor(url.question, def), [url.question, def]);
9575
+ const enabled = args.enabled && def !== null && def.dimensions.length > 0;
9489
9576
  const trailFilter = useMemo16(
9490
9577
  () => enabled ? filterWithTrail(fields, filter, question.where) : filter,
9491
9578
  [enabled, fields, filter, question.where]
9492
9579
  );
9493
- return { enabled, dimensions, measures, question, setQuestion: url.setQuestion, trailFilter };
9580
+ return { enabled, source, def, question, setQuestion: url.setQuestion, filter, trailFilter };
9494
9581
  }
9495
9582
  function useGridDrillConfig(args) {
9496
- const { tableId, state, presentation, onPresentationChange, version } = args;
9497
- const { enabled, dimensions, measures, question, setQuestion, trailFilter } = state;
9498
- const { answers, error } = useDrillAnswers({ tableId, question, filter: trailFilter, version, enabled });
9499
- return useMemo16(() => {
9500
- if (!enabled) return null;
9501
- return {
9502
- dimensions,
9503
- measures,
9504
- question,
9505
- answers,
9506
- error,
9507
- rootLabel: "All records",
9508
- rowNoun: "record",
9509
- onQuestionChange: (next) => {
9510
- setQuestion(next);
9511
- if (onPresentationChange) {
9512
- const grouping = groupingFromQuestion(next, presentation.grouping);
9513
- const { grouping: _dropped, ...rest } = presentation;
9514
- onPresentationChange(grouping ? { ...rest, grouping } : rest);
9515
- }
9583
+ const { state, presentation, onPresentationChange, version } = args;
9584
+ const { setQuestion } = state;
9585
+ const onQuestionChange = useMemo16(
9586
+ () => (next) => {
9587
+ setQuestion(next);
9588
+ if (onPresentationChange) {
9589
+ const grouping = groupingFromQuestion(next, presentation.grouping);
9590
+ const { grouping: _dropped, ...rest } = presentation;
9591
+ onPresentationChange(grouping ? { ...rest, grouping } : rest);
9516
9592
  }
9517
- };
9518
- }, [enabled, dimensions, measures, question, answers, error, setQuestion, onPresentationChange, presentation]);
9593
+ },
9594
+ [setQuestion, onPresentationChange, presentation]
9595
+ );
9596
+ const flat = state.filter && !isRuleFilter2(state.filter) ? state.filter : void 0;
9597
+ const rule = state.filter && isRuleFilter2(state.filter) ? filterWithTrailRuleOnly(state) : void 0;
9598
+ const { config } = useDrill({
9599
+ source: state.source,
9600
+ question: state.question,
9601
+ onQuestionChange,
9602
+ filter: flat,
9603
+ rule,
9604
+ version,
9605
+ enabled: state.enabled
9606
+ });
9607
+ return config;
9608
+ }
9609
+ function filterWithTrailRuleOnly(state) {
9610
+ return state.trailFilter;
9519
9611
  }
9520
9612
 
9521
9613
  // src/Enrich.tsx
@@ -13335,7 +13427,7 @@ function useGridAssist(args) {
13335
13427
  (ok, message) => ok ? host.notify?.success(message) : host.notify?.error(message),
13336
13428
  [host.notify]
13337
13429
  );
13338
- const rowOf = useCallback17((rowId) => rows.find((r) => r.id === rowId), [rows]);
13430
+ const rowOf2 = useCallback17((rowId) => rows.find((r) => r.id === rowId), [rows]);
13339
13431
  const titleOf = useCallback17(
13340
13432
  (row) => row ? recordNameIn(table, row.document ?? {}) : "",
13341
13433
  [table]
@@ -13425,18 +13517,18 @@ function useGridAssist(args) {
13425
13517
  );
13426
13518
  const openWhole = useCallback17(
13427
13519
  (rowId, key) => {
13428
- const row = rowOf(rowId);
13520
+ const row = rowOf2(rowId);
13429
13521
  if (!row) return;
13430
13522
  const document2 = row.document ?? {};
13431
13523
  const value = editing.valueFor(row, fieldOf2(key));
13432
13524
  const pointer = typeof document2[key] === "string" ? wholeValueOf3(document2, key) : null;
13433
13525
  setWhole({ rowId, key, rowTitle: titleOf(row), value, pointer });
13434
13526
  },
13435
- [rowOf, fieldOf2, titleOf, editing]
13527
+ [rowOf2, fieldOf2, titleOf, editing]
13436
13528
  );
13437
13529
  const cellItems = useCallback17(
13438
13530
  (rowId, columnId) => {
13439
- const row = rowOf(rowId);
13531
+ const row = rowOf2(rowId);
13440
13532
  const field2 = fieldOf2(columnId);
13441
13533
  if (!row || !field2) return [];
13442
13534
  const items = [];
@@ -13461,11 +13553,11 @@ function useGridAssist(args) {
13461
13553
  items.push(...refItem("cell-reference", "Copy cell reference", refFor.cell(row, field2), "The cell"));
13462
13554
  return items;
13463
13555
  },
13464
- [rowOf, fieldOf2, openWhole, host.cleanText, editing, args, refItem, refFor]
13556
+ [rowOf2, fieldOf2, openWhole, host.cleanText, editing, args, refItem, refFor]
13465
13557
  );
13466
13558
  const rowItems = useCallback17(
13467
13559
  (rowId) => {
13468
- const row = rowOf(rowId);
13560
+ const row = rowOf2(rowId);
13469
13561
  const askAgent = args.askAgent;
13470
13562
  return [
13471
13563
  ...rowActionMenuItems({
@@ -13480,7 +13572,7 @@ function useGridAssist(args) {
13480
13572
  ...row ? refItem("row-reference", "Copy row reference", refFor.row(row), "The row") : []
13481
13573
  ];
13482
13574
  },
13483
- [rowOf, args, fields, refItem, refFor]
13575
+ [rowOf2, args, fields, refItem, refFor]
13484
13576
  );
13485
13577
  const tableItems = useCallback17(
13486
13578
  () => refItem("table-reference", "Copy table reference", refFor.table(), "The table"),
@@ -14421,6 +14513,7 @@ function Grid({
14421
14513
  [merged, fields.data, filter, query.columnFilters]
14422
14514
  );
14423
14515
  const drillState = useGridDrillQuestion({
14516
+ tableId,
14424
14517
  fields: fields.data ?? NO_DRILL_FIELDS,
14425
14518
  presentation,
14426
14519
  filter: question.filter,