@reportwright/viewer 0.12.0 → 0.13.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +133 -1
  2. package/README.md +43 -6
  3. package/THIRD-PARTY-NOTICES.md +52 -0
  4. package/dist/{chunk-SBD55ZES.js → chunk-BF7GCWZY.js} +103 -41
  5. package/dist/chunk-BF7GCWZY.js.map +6 -0
  6. package/dist/chunk-BR5K6SBL.js +324 -0
  7. package/dist/chunk-BR5K6SBL.js.map +6 -0
  8. package/dist/chunk-CNDUJ7IV.js +758 -0
  9. package/dist/chunk-CNDUJ7IV.js.map +6 -0
  10. package/dist/{chunk-RYHJUQSV.js → chunk-DXHLIJEY.js} +2 -2
  11. package/dist/{chunk-M6GENFAH.js → chunk-EK5CJY22.js} +4021 -4631
  12. package/dist/chunk-EK5CJY22.js.map +6 -0
  13. package/dist/{chunk-KJTRZJHN.js → chunk-F5EPQTPF.js} +4 -3
  14. package/dist/chunk-F5EPQTPF.js.map +6 -0
  15. package/dist/{chunk-SR2TQ2ZK.js → chunk-F6RMRXIN.js} +2 -2
  16. package/dist/chunk-F6RMRXIN.js.map +6 -0
  17. package/dist/chunk-FEM2QVOO.js +18 -0
  18. package/dist/{chunk-ODREGGKA.js → chunk-IFYCJZNS.js} +9 -6
  19. package/dist/chunk-IFYCJZNS.js.map +6 -0
  20. package/dist/{chunk-EOVGKHAS.js → chunk-L6YEIVVD.js} +86 -57
  21. package/dist/chunk-L6YEIVVD.js.map +6 -0
  22. package/dist/{chunk-APRPSQOD.js → chunk-PQNM6ET7.js} +28 -9
  23. package/dist/chunk-PQNM6ET7.js.map +6 -0
  24. package/dist/chunk-QFLVVM3H.js +856 -0
  25. package/dist/chunk-QFLVVM3H.js.map +6 -0
  26. package/dist/{chunk-5N6OFCHJ.js → chunk-TZXEEVKE.js} +1319 -76
  27. package/dist/chunk-TZXEEVKE.js.map +6 -0
  28. package/dist/{chunk-FARG6X2U.js → chunk-UHHURTSV.js} +15 -9
  29. package/dist/chunk-UHHURTSV.js.map +6 -0
  30. package/dist/chunk-UJL7C2AS.js +546 -0
  31. package/dist/chunk-UJL7C2AS.js.map +6 -0
  32. package/dist/chunk-YIX3OJXU.js +34 -0
  33. package/dist/designer.d.ts +19 -4
  34. package/dist/designer.js +616 -405
  35. package/dist/designer.js.map +3 -3
  36. package/dist/{peer/chunk-GBITRNHE.js → engine.worker.js} +11006 -9030
  37. package/dist/engine.worker.js.map +6 -0
  38. package/dist/peer/{chunk-NPYKZINU.js → chunk-2CD5COHN.js} +66 -37
  39. package/dist/peer/chunk-2CD5COHN.js.map +6 -0
  40. package/dist/peer/{chunk-IPU2YRL2.js → chunk-3CBV5SAI.js} +28 -9
  41. package/dist/peer/chunk-3CBV5SAI.js.map +6 -0
  42. package/dist/peer/{chunk-MCJPN2KL.js → chunk-45GVDKP5.js} +15 -9
  43. package/dist/peer/chunk-45GVDKP5.js.map +6 -0
  44. package/dist/peer/{chunk-76ZABVTI.js → chunk-LWXQX3NJ.js} +4 -3
  45. package/dist/peer/chunk-MKY3SDXU.js +758 -0
  46. package/dist/peer/chunk-MKY3SDXU.js.map +6 -0
  47. package/dist/peer/chunk-S6LSPHSU.js +17 -0
  48. package/dist/peer/chunk-S6LSPHSU.js.map +6 -0
  49. package/dist/peer/{chunk-M4AGFUWE.js → chunk-V42KN7HD.js} +2 -2
  50. package/dist/peer/{chunk-KBT6ZGGO.js → chunk-VZHHNQYB.js} +1319 -76
  51. package/dist/peer/chunk-VZHHNQYB.js.map +6 -0
  52. package/dist/peer/{chunk-Y5CAHLXS.js → chunk-YITWGAR4.js} +9 -6
  53. package/dist/peer/chunk-YITWGAR4.js.map +6 -0
  54. package/dist/peer/chunk-Z5II55QT.js +33 -0
  55. package/dist/peer/chunk-Z5II55QT.js.map +6 -0
  56. package/dist/peer/designer.js +473 -262
  57. package/dist/peer/designer.js.map +3 -3
  58. package/dist/peer/engine.worker.js +16782 -0
  59. package/dist/peer/engine.worker.js.map +6 -0
  60. package/dist/peer/viewer.js +17 -7
  61. package/dist/peer/viewer.js.map +1 -1
  62. package/dist/viewer.d.ts +11 -2
  63. package/dist/viewer.js +17 -7
  64. package/dist/viewer.js.map +1 -1
  65. package/package.json +4 -2
  66. package/dist/chunk-3D43XGIN.js +0 -1167
  67. package/dist/chunk-3D43XGIN.js.map +0 -6
  68. package/dist/chunk-5N6OFCHJ.js.map +0 -6
  69. package/dist/chunk-AH47FS6Y.js +0 -13
  70. package/dist/chunk-APRPSQOD.js.map +0 -6
  71. package/dist/chunk-EOVGKHAS.js.map +0 -6
  72. package/dist/chunk-FARG6X2U.js.map +0 -6
  73. package/dist/chunk-M6GENFAH.js.map +0 -6
  74. package/dist/chunk-ODREGGKA.js.map +0 -6
  75. package/dist/chunk-SBD55ZES.js.map +0 -6
  76. package/dist/chunk-SR2TQ2ZK.js.map +0 -6
  77. package/dist/peer/chunk-76ZABVTI.js.map +0 -6
  78. package/dist/peer/chunk-GBITRNHE.js.map +0 -6
  79. package/dist/peer/chunk-IMQRHDR4.js +0 -12
  80. package/dist/peer/chunk-IPU2YRL2.js.map +0 -6
  81. package/dist/peer/chunk-KBT6ZGGO.js.map +0 -6
  82. package/dist/peer/chunk-MCJPN2KL.js.map +0 -6
  83. package/dist/peer/chunk-NPYKZINU.js.map +0 -6
  84. package/dist/peer/chunk-Y5CAHLXS.js.map +0 -6
  85. /package/dist/{chunk-RYHJUQSV.js.map → chunk-DXHLIJEY.js.map} +0 -0
  86. /package/dist/{chunk-AH47FS6Y.js.map → chunk-FEM2QVOO.js.map} +0 -0
  87. /package/dist/{peer/chunk-IMQRHDR4.js.map → chunk-YIX3OJXU.js.map} +0 -0
  88. /package/dist/{chunk-KJTRZJHN.js.map → peer/chunk-LWXQX3NJ.js.map} +0 -0
  89. /package/dist/peer/{chunk-M4AGFUWE.js.map → chunk-V42KN7HD.js.map} +0 -0
package/dist/designer.js CHANGED
@@ -13,7 +13,18 @@ import {
13
13
  setShadowStyles,
14
14
  t,
15
15
  uiFontCss
16
- } from "./chunk-EOVGKHAS.js";
16
+ } from "./chunk-L6YEIVVD.js";
17
+ import {
18
+ customFonts,
19
+ fontsBase,
20
+ loadFontStore,
21
+ loadModelFonts,
22
+ origin,
23
+ reportFetch,
24
+ setAssetBase,
25
+ setFetchPolicy,
26
+ setStandalone
27
+ } from "./chunk-CNDUJ7IV.js";
17
28
  import {
18
29
  BASE_STYLE,
19
30
  DEFAULT_THEME,
@@ -22,7 +33,6 @@ import {
22
33
  ITEMS,
23
34
  ODATA_DEFAULT_PAGES,
24
35
  ODATA_MAX_PAGES,
25
- PAGE_SIZES,
26
36
  PALETTES,
27
37
  TEXT_STYLE_PROPS,
28
38
  THEME_COLORS,
@@ -32,14 +42,10 @@ import {
32
42
  buildDataSets,
33
43
  checkFunctions,
34
44
  checkValue,
35
- customFonts,
36
45
  detectFields,
37
46
  evalValue,
38
47
  findItemNamed,
39
48
  finishText,
40
- fontStore,
41
- fontsBase,
42
- fromPt,
43
49
  functionTable,
44
50
  isDynamic,
45
51
  isTablix,
@@ -47,52 +53,53 @@ import {
47
53
  jsonPath,
48
54
  layoutItems,
49
55
  loadBarcodes,
50
- loadModelFonts,
51
56
  loadSources,
52
- normBox,
53
57
  normalizeMatrix,
54
58
  odataMetadata,
55
- origin,
56
59
  parseBorder,
57
60
  parseXml,
58
61
  render,
59
- reportFetch,
60
- resolvePage,
61
62
  resolveParameters,
62
- setAssetBase,
63
- setStandalone,
64
63
  shiftItems,
65
64
  sourceRows,
66
65
  sourceUrl,
67
66
  stylePropsFor,
68
67
  themeContext,
69
- toPt,
70
68
  toText,
71
69
  validate,
72
70
  withPartProps,
73
71
  xmlRowPaths
74
- } from "./chunk-M6GENFAH.js";
72
+ } from "./chunk-EK5CJY22.js";
73
+ import {
74
+ PAGE_SIZES,
75
+ fromPt,
76
+ normBox,
77
+ resolvePage,
78
+ toPt
79
+ } from "./chunk-BR5K6SBL.js";
80
+ import "./chunk-72S6DETS.js";
75
81
  import {
76
82
  currencyOf
77
83
  } from "./chunk-GJS242RR.js";
78
- import "./chunk-72S6DETS.js";
79
- import "./chunk-SR2TQ2ZK.js";
84
+ import "./chunk-F6RMRXIN.js";
85
+ import {
86
+ fontFaceCss,
87
+ fontFamilyCss,
88
+ pageToSvg
89
+ } from "./chunk-UJL7C2AS.js";
80
90
  import {
81
91
  BUNDLED_FAMILIES,
82
92
  FONT_FAMILIES,
83
93
  allFontKeys,
84
94
  fallbackKeys,
85
- fontFaceCss,
86
- fontFamilyCss,
87
- pageToSvg,
88
95
  resolveFontKey
89
- } from "./chunk-3D43XGIN.js";
96
+ } from "./chunk-QFLVVM3H.js";
90
97
  import {
91
98
  __toESM
92
99
  } from "./chunk-OLLMACWA.js";
93
100
 
94
101
  // src/embed/designer.js
95
- var import_react22 = __toESM(require_react(), 1);
102
+ var import_react23 = __toESM(require_react(), 1);
96
103
  var import_client = __toESM(require_client(), 1);
97
104
 
98
105
  // src/importers/rdlx.js
@@ -6146,7 +6153,8 @@ function toRdlx3(root, notes, reportName) {
6146
6153
  const on = prop(b, "aggregateOn") ?? simpleList(b, "aggregateOn")[0]?.text;
6147
6154
  const gc = { ...c, group: on && on !== "All" ? on : void 0 };
6148
6155
  if (fn3) {
6149
- const arg = listProp(b, "arguments").map((x) => prop(x, "value")).find((x) => x != null);
6156
+ const arg = listProp(b, "arguments").map((x) => prop(x, "value")).find((x) => x != null) ?? prop(b, "expression");
6157
+ if (arg == null && fn3 !== "count") throw new Untranslatable2(`the aggregate ${fn3.toUpperCase()} with no expression`);
6150
6158
  const inner2 = arg != null ? vb2(arg, { ...c, inSet: false }, depth) : "1";
6151
6159
  if (prop(b, "filterExpr")) throw new Untranslatable2(`the bound column "${prop(b, "name")}", an aggregate with a filter`);
6152
6160
  const all = gc.group ?? c.region;
@@ -6859,10 +6867,10 @@ function readReportFile(name2, text) {
6859
6867
  }
6860
6868
 
6861
6869
  // src/designer/Designer.jsx
6862
- var import_react21 = __toESM(require_react(), 1);
6870
+ var import_react22 = __toESM(require_react(), 1);
6863
6871
 
6864
6872
  // src/designer/ui.jsx
6865
- var import_react2 = __toESM(require_react(), 1);
6873
+ var import_react3 = __toESM(require_react(), 1);
6866
6874
 
6867
6875
  // src/designer/fieldCheck.js
6868
6876
  function exprParts(value) {
@@ -6960,7 +6968,7 @@ function nestedFields(rows) {
6960
6968
  return [...top2].filter(([, m]) => m.size).map(([name2, m]) => ({ name: name2, type: "object", fields: [...m].map(([n, t3]) => ({ name: n, type: t3 === "null" ? "string" : t3 })) }));
6961
6969
  }
6962
6970
 
6963
- // ../reportwright/node_modules/zustand/esm/vanilla.mjs
6971
+ // node_modules/zustand/esm/vanilla.mjs
6964
6972
  var createStoreImpl = (createState) => {
6965
6973
  let state;
6966
6974
  const listeners = /* @__PURE__ */ new Set();
@@ -6984,7 +6992,7 @@ var createStoreImpl = (createState) => {
6984
6992
  };
6985
6993
  var createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
6986
6994
 
6987
- // ../reportwright/node_modules/zustand/esm/react.mjs
6995
+ // node_modules/zustand/esm/react.mjs
6988
6996
  var import_react = __toESM(require_react(), 1);
6989
6997
  var identity = (arg) => arg;
6990
6998
  function useStore(api, selector = identity) {
@@ -6996,15 +7004,11 @@ function useStore(api, selector = identity) {
6996
7004
  import_react.default.useDebugValue(slice);
6997
7005
  return slice;
6998
7006
  }
6999
- var createImpl = (createState) => {
7000
- const api = createStore(createState);
7001
- const useBoundStore = (selector) => useStore(api, selector);
7002
- Object.assign(useBoundStore, api);
7003
- return useBoundStore;
7004
- };
7005
- var create = ((createState) => createState ? createImpl(createState) : createImpl);
7006
7007
 
7007
- // ../reportwright/node_modules/immer/dist/immer.mjs
7008
+ // src/designer/store.js
7009
+ var import_react2 = __toESM(require_react(), 1);
7010
+
7011
+ // node_modules/immer/dist/immer.mjs
7008
7012
  var NOTHING = /* @__PURE__ */ Symbol.for("immer-nothing");
7009
7013
  var DRAFTABLE = /* @__PURE__ */ Symbol.for("immer-draftable");
7010
7014
  var DRAFT_STATE = /* @__PURE__ */ Symbol.for("immer-state");
@@ -7851,6 +7855,17 @@ function pageOf2(def, key2 = "body") {
7851
7855
  return resolvePage({ ...def.page || {}, ...sec?.page || {} });
7852
7856
  }
7853
7857
  var rid = () => Math.random().toString(36).slice(2, 10);
7858
+ function stableId(key2, taken) {
7859
+ let h = 2166136261;
7860
+ for (let i2 = 0; i2 < key2.length; i2++) {
7861
+ h ^= key2.charCodeAt(i2);
7862
+ h = Math.imul(h, 16777619);
7863
+ }
7864
+ let id = `i${(h >>> 0).toString(36)}`;
7865
+ for (let k = 2; taken.has(id); k++) id = `i${(h >>> 0).toString(36)}_${k}`;
7866
+ taken.add(id);
7867
+ return id;
7868
+ }
7854
7869
  function walk(def, fn3) {
7855
7870
  const go = (list2, parent, section) => list2.forEach((it, i2) => {
7856
7871
  fn3(it, list2, i2, parent, section);
@@ -7876,9 +7891,15 @@ function normalize(def) {
7876
7891
  if (x && typeof x === "object") x.items || (x.items = []);
7877
7892
  }
7878
7893
  const used = allNames(d);
7894
+ const ids = /* @__PURE__ */ new Set();
7879
7895
  walk(d, (it) => {
7880
- it.id || (it.id = rid());
7896
+ if (it.id) ids.add(it.id);
7897
+ });
7898
+ const path = /* @__PURE__ */ new Map();
7899
+ walk(d, (it, list2, i2, parent, section) => {
7900
+ path.set(it, `${parent ? path.get(parent) : section}/${i2}`);
7881
7901
  if (!it.name) it.name = uniqueName(used, ITEMS[it.type]?.label.replace(/\s/g, "") || "Item");
7902
+ it.id || (it.id = stableId(`${path.get(it)}/${it.name}`, ids));
7882
7903
  if (NESTING.has(it.type)) it.items || (it.items = []);
7883
7904
  if (it.type === "table" && Array.isArray(it.columns)) tableSync(it);
7884
7905
  });
@@ -7902,7 +7923,7 @@ function findItem(def, id) {
7902
7923
  });
7903
7924
  return hit;
7904
7925
  }
7905
- var useDesigner = create((set2, get2) => ({
7926
+ var newDesignerStore = () => createStore((set2, get2) => ({
7906
7927
  id: null,
7907
7928
  def: null,
7908
7929
  saved: true,
@@ -8033,7 +8054,18 @@ var useDesigner = create((set2, get2) => ({
8033
8054
  set2({ rev: (res.headers.get("etag") || "").replace(/^W\/|"/g, "") || null, conflict: null });
8034
8055
  }
8035
8056
  }));
8036
- var S = () => useDesigner.getState();
8057
+ var active = newDesignerStore();
8058
+ var DesignerStore = (0, import_react2.createContext)(null);
8059
+ var activateStore = (store) => {
8060
+ active = store;
8061
+ };
8062
+ var activeStore2 = () => active;
8063
+ var useDesignerStore = () => (0, import_react2.useContext)(DesignerStore) || active;
8064
+ var useDesigner = (sel) => useStore(useDesignerStore(), sel);
8065
+ useDesigner.getState = () => active.getState();
8066
+ useDesigner.setState = (...a) => active.setState(...a);
8067
+ useDesigner.subscribe = (fn3) => active.subscribe(fn3);
8068
+ var S = () => active.getState();
8037
8069
  function bodyWidth(def, key2 = "body") {
8038
8070
  return pageOf2(def, key2).bodyWidth;
8039
8071
  }
@@ -8081,7 +8113,7 @@ function addItem(type, section, x, y, extra = {}, parentId = null) {
8081
8113
  } else {
8082
8114
  const sec = secOf(d, section);
8083
8115
  sec.items.push(it);
8084
- if (it.y + it.h > sec.height) sec.height = Math.ceil(it.y + it.h + (isBodyKey(section) ? 24 : 4));
8116
+ if (it.y + it.h > (sec.height || 0)) sec.height = Math.ceil(it.y + it.h + (isBodyKey(section) ? 24 : 4));
8085
8117
  }
8086
8118
  });
8087
8119
  S().select({ kind: "items", ids: [id] });
@@ -8107,9 +8139,21 @@ function insertItem(type, section, x, y, extra = {}, parentId = null) {
8107
8139
  function updateItem(id, recipe, key2) {
8108
8140
  S().apply((d) => {
8109
8141
  const f = findItem(d, id);
8110
- if (f) recipe(f.item, d, f.section, f);
8142
+ if (!f) return;
8143
+ recipe(f.item, d, f.section, f);
8144
+ minSize(f.item);
8145
+ const nf = findItem(d, id);
8146
+ if (nf) growToFit(d, nf);
8111
8147
  }, key2);
8112
8148
  }
8149
+ function minSize(it) {
8150
+ for (const k of ["w", "h"]) if (typeof it[k] === "number" && !(it[k] >= 0)) it[k] = 0;
8151
+ if (it.type === "line") {
8152
+ if (it.w === 0 && it.h === 0) it.w = 1;
8153
+ return;
8154
+ }
8155
+ for (const k of ["w", "h"]) if (typeof it[k] === "number" && it[k] < 1) it[k] = 1;
8156
+ }
8113
8157
  function moveItems(ids, dx, dy) {
8114
8158
  if (!dx && !dy) return;
8115
8159
  if (S().cells && wouldOverlap(S().def, ids, (it) => ({ x: it.x + dx, y: it.y + dy, w: it.w, h: it.h }))) {
@@ -8240,14 +8284,16 @@ function duplicateSelection() {
8240
8284
  const f = findItem(d, id);
8241
8285
  if (!f) continue;
8242
8286
  const copy = cloneWithIds(f.item, used);
8243
- copy.x += 8;
8244
- copy.y += 8;
8287
+ copy.x += PASTE_OFFSET;
8288
+ copy.y += PASTE_OFFSET;
8289
+ growToFit(d, { ...f, item: copy });
8245
8290
  f.list.push(copy);
8246
8291
  newIds.push(copy.id);
8247
8292
  }
8248
8293
  });
8249
8294
  if (newIds.length) S().select({ kind: "items", ids: newIds });
8250
8295
  }
8296
+ var PASTE_OFFSET = 12;
8251
8297
  var CLIP = "pagewright/items@1";
8252
8298
  async function copySelection() {
8253
8299
  const { sel, def } = S();
@@ -8262,43 +8308,41 @@ async function copySelection() {
8262
8308
  return items.length;
8263
8309
  }
8264
8310
  var memClip = null;
8265
- async function pasteItems(section = "body") {
8266
- let text = null;
8267
- try {
8268
- text = await navigator.clipboard.readText();
8269
- } catch {
8270
- text = memClip;
8271
- }
8272
- if (!text || !text.includes(CLIP)) text = memClip;
8273
- let data;
8311
+ var clipOf = (text) => {
8274
8312
  try {
8275
- data = JSON.parse(text);
8313
+ const d = JSON.parse(text);
8314
+ return d?.kind === CLIP && Array.isArray(d.items) ? d : null;
8276
8315
  } catch {
8277
- return 0;
8316
+ return null;
8278
8317
  }
8279
- if (data?.kind !== CLIP || !Array.isArray(data.items)) return 0;
8318
+ };
8319
+ function pasteItems(section = "body", text = null) {
8320
+ const st = active;
8321
+ const data = clipOf(text) || clipOf(memClip);
8322
+ if (!data) return 0;
8280
8323
  const ids = [];
8281
- S().apply((d) => {
8324
+ st.getState().apply((d) => {
8282
8325
  const used = allNames(d);
8283
8326
  for (const it of data.items) {
8284
8327
  const c = cloneWithIds(it, used);
8285
- c.x += 12;
8286
- c.y += 12;
8328
+ c.x += PASTE_OFFSET;
8329
+ c.y += PASTE_OFFSET;
8287
8330
  if (c.type === "table" && !isBodyKey(section)) continue;
8288
8331
  secOf(d, section).items.push(c);
8332
+ growToFit(d, { section, parent: null, item: c });
8289
8333
  ids.push(c.id);
8290
8334
  }
8291
8335
  });
8292
- if (ids.length) S().select({ kind: "items", ids });
8336
+ if (ids.length) st.getState().select({ kind: "items", ids });
8293
8337
  return ids.length;
8294
8338
  }
8295
- function rowsOf(t3, part, create2 = false) {
8339
+ function rowsOf(t3, part, create = false) {
8296
8340
  const m = /^g(\d+)([hf])$/.exec(part);
8297
8341
  const holder = m ? t3.groups?.[Number(m[1])] : t3;
8298
8342
  if (!holder) return null;
8299
8343
  const key2 = m ? m[2] === "h" ? "header" : "footer" : part;
8300
8344
  if (!holder[key2]) {
8301
- if (!create2) return [];
8345
+ if (!create) return [];
8302
8346
  holder[key2] = [];
8303
8347
  }
8304
8348
  return holder[key2];
@@ -8498,6 +8542,7 @@ function tableRemoveGroup(id, level) {
8498
8542
  }
8499
8543
  var IMAGE_MAX = 10 * 1024 * 1024;
8500
8544
  async function uploadImages(files, T) {
8545
+ const st = active;
8501
8546
  const names = [];
8502
8547
  let err = null;
8503
8548
  for (const f of files) {
@@ -8516,7 +8561,7 @@ async function uploadImages(files, T) {
8516
8561
  r.readAsDataURL(f);
8517
8562
  });
8518
8563
  const base2 = f.name.replace(/\.[^.]+$/, "").replace(/\W+/g, "_").replace(/^_+|_+$/g, "") || "image";
8519
- S().apply((d) => {
8564
+ st.getState().apply((d) => {
8520
8565
  d.images || (d.images = {});
8521
8566
  let n = base2, k = 2;
8522
8567
  while (d.images[n]) n = `${base2}${k++}`;
@@ -8583,6 +8628,13 @@ function wouldOverlap(def, ids, rectOf2) {
8583
8628
  }
8584
8629
  return false;
8585
8630
  }
8631
+ function setMatrixShow(v, show) {
8632
+ const pct = !!show && show !== "value";
8633
+ if (pct) v.show = show;
8634
+ else delete v.show;
8635
+ if (pct && (!v.format || v.format === "N0")) v.format = "P1";
8636
+ else if (!pct && v.format === "P1") v.format = "N0";
8637
+ }
8586
8638
 
8587
8639
  // src/designer/ui.jsx
8588
8640
  var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
@@ -8594,8 +8646,8 @@ function TextInput({ value, onChange, mono, invalid, className, ...rest }) {
8594
8646
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: `input${mono ? " mono" : ""}${invalid ? " invalid" : ""}${className ? ` ${className}` : ""}`, value: value ?? "", onChange: (e) => onChange(e.target.value), ...rest });
8595
8647
  }
8596
8648
  function NumberInput({ value, onChange, step = 0.5, min, ...rest }) {
8597
- const [draft, setDraft] = (0, import_react2.useState)(value ?? "");
8598
- (0, import_react2.useEffect)(() => {
8649
+ const [draft, setDraft] = (0, import_react3.useState)(value ?? "");
8650
+ (0, import_react3.useEffect)(() => {
8599
8651
  setDraft(value ?? "");
8600
8652
  }, [value]);
8601
8653
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -8631,18 +8683,18 @@ function ColorInput({ value, onChange }) {
8631
8683
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextInput, { value, onChange: (v) => onChange(v === "" ? void 0 : v), mono: true, placeholder: T("i_colorPh") })
8632
8684
  ] });
8633
8685
  }
8634
- var FieldScope = (0, import_react2.createContext)(void 0);
8686
+ var FieldScope = (0, import_react3.createContext)(void 0);
8635
8687
  var issueText = (T, i2) => i2.suggest ? T("ux_unknownFieldSuggest", { name: i2.name, suggest: i2.suggest }) : T("ux_unknownField", { name: i2.name });
8636
8688
  function ExprInput({ value, onChange, onOpen, placeholder: placeholder2 }) {
8637
8689
  const T = useT();
8638
- const scope = (0, import_react2.useContext)(FieldScope);
8690
+ const scope = (0, import_react3.useContext)(FieldScope);
8639
8691
  const dataSets = useDesigner((s) => s.def?.dataSets);
8640
8692
  const err = value ? checkValue(value) : null;
8641
8693
  const issues = !err && value && scope !== void 0 ? fieldIssues(value, dataSets, scope) : [];
8642
8694
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
8643
8695
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "expr-row", children: [
8644
8696
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextInput, { value, onChange, mono: true, invalid: !!err, className: issues.length ? "squiggle" : void 0, "aria-invalid": err || issues.length ? true : void 0, placeholder: placeholder2 || T("i_exprPh") }),
8645
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "fx", title: T("i_openExprEditor"), "aria-label": T("i_openExprEditor"), onClick: onOpen, type: "button", children: "fx" })
8697
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "fx", title: T("i_openExprEditor"), "aria-label": `fx: ${T("i_openExprEditor")}`, onClick: onOpen, type: "button", children: "fx" })
8646
8698
  ] }),
8647
8699
  err && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "err", children: err }),
8648
8700
  issues.map((i2, k) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "warn", "data-testid": "field-warning", children: issueText(T, i2) }, k))
@@ -8662,14 +8714,19 @@ function Check({ label, checked, onChange }) {
8662
8714
  ] });
8663
8715
  }
8664
8716
  function Dialog({ title, children, onClose, footer, wide, className = "" }) {
8665
- (0, import_react2.useEffect)(() => {
8717
+ const ref = (0, import_react3.useRef)(null);
8718
+ (0, import_react3.useEffect)(() => {
8719
+ const inOther = (e) => {
8720
+ const mine = ref.current?.closest(".designer");
8721
+ return e.composedPath().some((n) => n !== mine && n?.classList?.contains("designer"));
8722
+ };
8666
8723
  const k = (e) => {
8667
- if (e.key === "Escape" && !e.defaultPrevented) onClose();
8724
+ if (e.key === "Escape" && !e.defaultPrevented && !inOther(e)) onClose();
8668
8725
  };
8669
8726
  window.addEventListener("keydown", k);
8670
8727
  return () => window.removeEventListener("keydown", k);
8671
8728
  }, [onClose]);
8672
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scrim", onPointerDown: (e) => {
8729
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scrim", ref, onPointerDown: (e) => {
8673
8730
  if (e.target === e.currentTarget) onClose();
8674
8731
  }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `dialog${wide ? " wide" : ""}${className ? ` ${className}` : ""}`, role: "dialog", "aria-modal": "true", "aria-label": title, children: [
8675
8732
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", { children: title }),
@@ -8679,20 +8736,20 @@ function Dialog({ title, children, onClose, footer, wide, className = "" }) {
8679
8736
  }
8680
8737
 
8681
8738
  // src/embed/link.js
8682
- var import_react3 = __toESM(require_react(), 1);
8739
+ var import_react4 = __toESM(require_react(), 1);
8683
8740
  function Link({ href, children, prefetch, replace: replace2, scroll, shallow, ...rest }) {
8684
- return (0, import_react3.createElement)("a", { href: typeof href === "string" ? href : String(href?.pathname || ""), ...rest }, children);
8741
+ return (0, import_react4.createElement)("a", { href: typeof href === "string" ? href : String(href?.pathname || ""), ...rest }, children);
8685
8742
  }
8686
8743
 
8687
8744
  // src/designer/SvgHtml.jsx
8688
- var import_react4 = __toESM(require_react(), 1);
8745
+ var import_react5 = __toESM(require_react(), 1);
8689
8746
  var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
8690
8747
  function SvgHtml({ html, as: Tag2 = "span", ...rest }) {
8691
- const ref = (0, import_react4.useRef)(
8748
+ const ref = (0, import_react5.useRef)(
8692
8749
  /** @type {HTMLElement|null} */
8693
8750
  null
8694
8751
  );
8695
- (0, import_react4.useLayoutEffect)(() => {
8752
+ (0, import_react5.useLayoutEffect)(() => {
8696
8753
  if (!ref.current) return;
8697
8754
  ref.current.innerHTML = deferStyleAttributes(html);
8698
8755
  applyStyleAttributes(ref.current);
@@ -8701,7 +8758,7 @@ function SvgHtml({ html, as: Tag2 = "span", ...rest }) {
8701
8758
  }
8702
8759
 
8703
8760
  // src/designer/Canvas.jsx
8704
- var import_react7 = __toESM(require_react(), 1);
8761
+ var import_react8 = __toESM(require_react(), 1);
8705
8762
 
8706
8763
  // src/designer/tableGen.js
8707
8764
  var HEAD = { fontWeight: "bold", backgroundColor: "#f1f5f9", borderBottom: "0.75 solid #94a3b8" };
@@ -8833,24 +8890,28 @@ function instantPicks(fields, rows = []) {
8833
8890
  }
8834
8891
 
8835
8892
  // src/designer/Reuse.jsx
8836
- var import_react6 = __toESM(require_react(), 1);
8893
+ var import_react7 = __toESM(require_react(), 1);
8837
8894
 
8838
8895
  // src/designer/Fonts.jsx
8839
- var import_react5 = __toESM(require_react(), 1);
8896
+ var import_react6 = __toESM(require_react(), 1);
8840
8897
  var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
8841
8898
  function useFontFamilies() {
8842
8899
  const fonts = useDesigner((s) => s.fonts);
8843
8900
  return [...BUNDLED_FAMILIES, ...[...new Set(fonts.map((f) => f.family))].sort()];
8844
8901
  }
8845
- var refresh = async () => useDesigner.getState().setUi({ fonts: await customFonts(true) });
8902
+ var refresh = async () => {
8903
+ const api = activeStore2();
8904
+ const fonts = await customFonts(true);
8905
+ api.getState().setUi({ fonts });
8906
+ };
8846
8907
  function FontsDialog({ onClose }) {
8847
8908
  const fonts = useDesigner((s) => s.fonts);
8848
- const [file, setFile] = (0, import_react5.useState)(null);
8849
- const [family, setFamily] = (0, import_react5.useState)("");
8850
- const [weight, setWeight] = (0, import_react5.useState)("");
8851
- const [style, setStyle] = (0, import_react5.useState)("");
8852
- const [busy, setBusy] = (0, import_react5.useState)(false);
8853
- const [msg, setMsg] = (0, import_react5.useState)(null);
8909
+ const [file, setFile] = (0, import_react6.useState)(null);
8910
+ const [family, setFamily] = (0, import_react6.useState)("");
8911
+ const [weight, setWeight] = (0, import_react6.useState)("");
8912
+ const [style, setStyle] = (0, import_react6.useState)("");
8913
+ const [busy, setBusy] = (0, import_react6.useState)(false);
8914
+ const [msg, setMsg] = (0, import_react6.useState)(null);
8854
8915
  const upload = async () => {
8855
8916
  if (!file) return;
8856
8917
  setBusy(true);
@@ -8941,30 +9002,33 @@ var apply = (fn3, key2) => S2().apply(fn3, key2);
8941
9002
  var server = () => S2().embed?.server || "";
8942
9003
  var noServer = () => !!S2().embed && !S2().embed.server;
8943
9004
  var pending = /* @__PURE__ */ new Map();
8944
- function fetchReused(id, force = false) {
9005
+ function fetchReused(id, force = false, api = activeStore2()) {
8945
9006
  if (!id || typeof id !== "string" || id.startsWith("=")) return Promise.resolve(null);
8946
- if (!force && S2().reused[id]) return Promise.resolve(S2().reused[id]);
8947
- if (pending.has(id)) return pending.get(id);
8948
- if (noServer()) return Promise.resolve(null);
8949
- const p = fetch(`${server()}/api/reports/${encodeURIComponent(id)}`).then((r) => r.ok ? r.json() : { __error: `HTTP ${r.status}` }).catch((e) => ({ __error: e.message })).then((d) => {
8950
- pending.delete(id);
8951
- useDesigner.setState((s) => ({ reused: { ...s.reused, [id]: d } }));
9007
+ const st = api.getState();
9008
+ if (!force && st.reused[id]) return Promise.resolve(st.reused[id]);
9009
+ const server2 = st.embed?.server || "", key2 = `${server2} ${id}`;
9010
+ const keep = (d) => {
9011
+ api.setState((s) => ({ reused: { ...s.reused, [id]: d } }));
8952
9012
  return d;
8953
- });
8954
- pending.set(id, p);
8955
- return p;
9013
+ };
9014
+ if (pending.has(key2)) return pending.get(key2).then(keep);
9015
+ if (st.embed && !server2) return Promise.resolve(null);
9016
+ const p = fetch(`${server2}/api/reports/${encodeURIComponent(id)}`).then((r) => r.ok ? r.json() : { __error: `HTTP ${r.status}` }).catch((e) => ({ __error: e.message })).finally(() => pending.delete(key2));
9017
+ pending.set(key2, p);
9018
+ return p.then(keep);
8956
9019
  }
8957
9020
  function useReused(id) {
9021
+ const api = useDesignerStore();
8958
9022
  const d = useDesigner((s) => id ? s.reused[id] : void 0);
8959
- (0, import_react6.useEffect)(() => {
8960
- fetchReused(id);
8961
- }, [id]);
9023
+ (0, import_react7.useEffect)(() => {
9024
+ fetchReused(id, false, api);
9025
+ }, [id, api]);
8962
9026
  return d && !d.__error ? d : null;
8963
9027
  }
8964
9028
  var listCache = null;
8965
9029
  function ReportSelect({ value, onChange, exclude, ...rest }) {
8966
- const [list2, setList] = (0, import_react6.useState)(listCache || []);
8967
- (0, import_react6.useEffect)(() => {
9030
+ const [list2, setList] = (0, import_react7.useState)(listCache || []);
9031
+ (0, import_react7.useEffect)(() => {
8968
9032
  if (noServer()) return;
8969
9033
  fetch(`${server()}/api/reports`).then((r) => r.ok ? r.json() : []).then((l) => {
8970
9034
  listCache = Array.isArray(l) ? l : [];
@@ -9031,7 +9095,7 @@ function ThemeSection() {
9031
9095
  const id = useDesigner((s) => s.id);
9032
9096
  const families = useFontFamilies();
9033
9097
  const src = def.theme;
9034
- const [mode, setMode] = (0, import_react6.useState)(!src ? "own" : String(src).startsWith("=") ? "expr" : "report");
9098
+ const [mode, setMode] = (0, import_react7.useState)(!src ? "own" : String(src).startsWith("=") ? "expr" : "report");
9035
9099
  const td = def.themeDef || {};
9036
9100
  const setTd = (fn3, key2) => apply((d) => {
9037
9101
  d.themeDef || (d.themeDef = {});
@@ -9050,7 +9114,7 @@ function ThemeSection() {
9050
9114
  else x.fonts[k][p] = v;
9051
9115
  }, `theme.font.${k}.${p}`);
9052
9116
  const consts = Object.entries(td.constants || {});
9053
- const [newConst, setNewConst] = (0, import_react6.useState)("");
9117
+ const [newConst, setNewConst] = (0, import_react7.useState)("");
9054
9118
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cat", "data-testid": "theme", children: [
9055
9119
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CatHead, { children: T("theme") }),
9056
9120
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: T("themeSource"), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -9110,7 +9174,7 @@ function LayersPanel() {
9110
9174
  const T = useT2();
9111
9175
  const layers = useDesigner((s) => s.def.layers) || [];
9112
9176
  const readOnly2 = useDesigner((s) => s.readOnly);
9113
- const [name2, setName] = (0, import_react6.useState)("");
9177
+ const [name2, setName] = (0, import_react7.useState)("");
9114
9178
  const set2 = (fn3, key2) => apply((d) => {
9115
9179
  d.layers || (d.layers = []);
9116
9180
  fn3(d.layers, d);
@@ -9265,7 +9329,7 @@ function PartsPanel() {
9265
9329
  const libs = useDesigner((s) => s.def.libraries) || [];
9266
9330
  const id = useDesigner((s) => s.id);
9267
9331
  const readOnly2 = useDesigner((s) => s.readOnly);
9268
- const [pick, setPick] = (0, import_react6.useState)("");
9332
+ const [pick, setPick] = (0, import_react7.useState)("");
9269
9333
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pane", "data-testid": "parts-panel", children: [
9270
9334
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h4", { children: [
9271
9335
  T("parts"),
@@ -9394,7 +9458,7 @@ function PublishPart({ item }) {
9394
9458
  if (v === void 0 || v === "") delete q2[k];
9395
9459
  else q2[k] = v;
9396
9460
  }, `part${i2}.q${j}.${k}`);
9397
- const [newProp, setNewProp] = (0, import_react6.useState)("");
9461
+ const [newProp, setNewProp] = (0, import_react7.useState)("");
9398
9462
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cat", "data-testid": "publish-part", children: [
9399
9463
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CatHead, { children: T("reportPart") }),
9400
9464
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Check, { label: T("publishPart"), checked: !!p, onChange: (on) => set2((l) => {
@@ -9523,12 +9587,12 @@ function useDesignIssues() {
9523
9587
  const def = useDesigner((s) => s.def);
9524
9588
  const extents = useDesigner((s) => s.extents);
9525
9589
  const saveCheck = useDesigner((s) => s.saveCheck);
9526
- return (0, import_react7.useMemo)(() => {
9590
+ return (0, import_react8.useMemo)(() => {
9527
9591
  const bands = sectionList(def).map((s) => ({ key: s.key, items: secOf(def, s.key)?.items || [], width: bodyWidth(def, s.key) }));
9528
9592
  return [...designIssues(def, bands, extents || {}), ...checkIssues(bands, [...validate(def).errors, ...saveCheck?.missing || []])];
9529
9593
  }, [def, extents, saveCheck]);
9530
9594
  }
9531
- var IssueMap = (0, import_react7.createContext)(/* @__PURE__ */ new Map());
9595
+ var IssueMap = (0, import_react8.createContext)(/* @__PURE__ */ new Map());
9532
9596
  function issueLine(T, i2) {
9533
9597
  if (i2.kind === "overlap") return T("ux_issueOverlap", { name: i2.name, other: i2.other });
9534
9598
  if (i2.kind === "wide") return T("ux_issueWide", { name: i2.name, width: i2.width, room: i2.room });
@@ -9593,9 +9657,9 @@ function Canvas() {
9593
9657
  const bands = sectionList(def);
9594
9658
  const widths2 = Object.fromEntries(bands.map((s) => [s.key, bodyWidth(def, s.key)]));
9595
9659
  const bw = Math.max(...Object.values(widths2));
9596
- const scrollRef = (0, import_react7.useRef)(null);
9597
- const [fitW, setFitW] = (0, import_react7.useState)(0);
9598
- (0, import_react7.useEffect)(() => {
9660
+ const scrollRef = (0, import_react8.useRef)(null);
9661
+ const [fitW, setFitW] = (0, import_react8.useState)(0);
9662
+ (0, import_react8.useEffect)(() => {
9599
9663
  if (zoom !== "fit" || !scrollRef.current) return;
9600
9664
  const el = scrollRef.current;
9601
9665
  const ro = new ResizeObserver(() => setFitW(el.clientWidth));
@@ -9603,10 +9667,10 @@ function Canvas() {
9603
9667
  return () => ro.disconnect();
9604
9668
  }, [zoom]);
9605
9669
  const scale = zoom === "fit" ? Math.max(0.3, Math.min(3, ((fitW || 900) - 80) / bw)) : zoom * PX;
9606
- const [drag, setDrag] = (0, import_react7.useState)(null);
9670
+ const [drag, setDrag] = (0, import_react8.useState)(null);
9607
9671
  const T = useT2();
9608
9672
  const issues = useDesignIssues();
9609
- const byId = (0, import_react7.useMemo)(() => {
9673
+ const byId = (0, import_react8.useMemo)(() => {
9610
9674
  const m = /* @__PURE__ */ new Map();
9611
9675
  for (const i2 of issues) m.set(i2.id, [...m.get(i2.id) || [], issueLine(T, i2)]);
9612
9676
  return m;
@@ -9687,9 +9751,9 @@ function Band({ section, label, scale, bw: bandW, drag, setDrag, phBox = null })
9687
9751
  const off = phBox || mb?.off || null;
9688
9752
  const contentList = useDesigner((s) => s.def.sections?.content);
9689
9753
  const bw = off?.w || bandW;
9690
- const ref = (0, import_react7.useRef)(null);
9691
- const [marquee, setMarquee] = (0, import_react7.useState)(null);
9692
- const [dropping, setDropping] = (0, import_react7.useState)(false);
9754
+ const ref = (0, import_react8.useRef)(null);
9755
+ const [marquee, setMarquee] = (0, import_react8.useState)(null);
9756
+ const [dropping, setDropping] = (0, import_react8.useState)(false);
9693
9757
  const h = drag?.mode === "band" && drag.section === section ? drag.h : sec.height || 0;
9694
9758
  const DROP_ROOM = 96;
9695
9759
  const toPt3 = (e) => {
@@ -9863,8 +9927,9 @@ var alerted = 0;
9863
9927
  function alertOnce(msg) {
9864
9928
  if (Date.now() - alerted > 3e3) {
9865
9929
  alerted = Date.now();
9866
- useDesigner.setState({ saveError: msg });
9867
- setTimeout(() => useDesigner.setState({ saveError: null }), 3e3);
9930
+ const api = activeStore2();
9931
+ api.setState({ saveError: msg });
9932
+ setTimeout(() => api.setState({ saveError: null }), 3e3);
9868
9933
  }
9869
9934
  }
9870
9935
  function fieldStyle(f) {
@@ -9872,11 +9937,31 @@ function fieldStyle(f) {
9872
9937
  const format = formatFor({ name: f.field, type: f.type }, (st.sample?.dataSets?.[f.dataSet] || []).slice(0, 200), { currency: st.def?.currency });
9873
9938
  return format ? { format } : {};
9874
9939
  }
9940
+ function useItemKeys(item) {
9941
+ const T = useT2();
9942
+ const type = T(`item_${item.type}`) === `item_${item.type}` ? ITEMS[item.type]?.label || item.type : T(`item_${item.type}`);
9943
+ return {
9944
+ tabIndex: 0,
9945
+ role: "group",
9946
+ "data-item-key": item.id,
9947
+ "aria-label": T("b9_itemLabel", { type, name: item.name, x: round(item.x), y: round(item.y) }),
9948
+ onKeyDown: (e) => {
9949
+ if (e.target !== e.currentTarget || e.key !== "Enter" && e.key !== " ") return;
9950
+ e.preventDefault();
9951
+ e.stopPropagation();
9952
+ const st = useDesigner.getState();
9953
+ const cur2 = st.sel.kind === "items" ? st.sel.ids : [];
9954
+ const ids = !e.shiftKey ? [item.id] : cur2.includes(item.id) ? cur2.filter((i2) => i2 !== item.id) : [...cur2, item.id];
9955
+ st.select(ids.length ? { kind: "items", ids } : { kind: "report" });
9956
+ }
9957
+ };
9958
+ }
9875
9959
  function useItemDrag(item, scale, setDrag) {
9876
9960
  return (e) => {
9877
9961
  if (e.button !== 0) return;
9878
9962
  e.preventDefault();
9879
9963
  e.stopPropagation();
9964
+ e.currentTarget.closest?.("[data-item-key]")?.focus({ preventScroll: true });
9880
9965
  const st = useDesigner.getState();
9881
9966
  let ids;
9882
9967
  if (e.shiftKey) {
@@ -9949,8 +10034,8 @@ function AnyItem(props) {
9949
10034
  return layer?.locked ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "layer-locked", "data-locked": props.item.name, children: el }) : el;
9950
10035
  }
9951
10036
  function NestChildren({ item, scale, drag, setDrag, section }) {
9952
- const ref = (0, import_react7.useRef)(null);
9953
- const [over, setOver] = (0, import_react7.useState)(false);
10037
+ const ref = (0, import_react8.useRef)(null);
10038
+ const [over, setOver] = (0, import_react8.useState)(false);
9954
10039
  const accepts = (e) => e.dataTransfer.types.includes(MIME_TOOL) || e.dataTransfer.types.includes(MIME_FIELD) || e.dataTransfer.types.includes(MIME_PART);
9955
10040
  const onDrop = (e) => {
9956
10041
  setOver(false);
@@ -9995,8 +10080,9 @@ function ItemView({ item, scale, drag, setDrag, bw, nest, section }) {
9995
10080
  const selected = useDesigner((s) => s.sel.kind === "items" && s.sel.ids.includes(item.id));
9996
10081
  const single = useDesigner((s) => s.sel.kind === "items" && s.sel.ids.length === 1);
9997
10082
  const onDown = useItemDrag(item, scale, setDrag);
10083
+ const keys = useItemKeys(item);
9998
10084
  const r = rectOf(item, drag);
9999
- const problems = (0, import_react7.useContext)(IssueMap).get(item.id);
10085
+ const problems = (0, import_react8.useContext)(IssueMap).get(item.id);
10000
10086
  const onHandle = (dir) => (e) => {
10001
10087
  e.preventDefault();
10002
10088
  e.stopPropagation();
@@ -10055,6 +10141,7 @@ function ItemView({ item, scale, drag, setDrag, bw, nest, section }) {
10055
10141
  title: problems?.join("\n"),
10056
10142
  style: { left: r.x * scale, top: r.y * scale, width: Math.max(r.w * scale, 2), height: Math.max(r.h * scale, 2) },
10057
10143
  onPointerDown: onDown,
10144
+ ...keys,
10058
10145
  children: [
10059
10146
  nest ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NestBox, { item: { ...item, ...r }, scale }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ItemBody, { item: { ...item, ...r }, scale }),
10060
10147
  nest && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NestChildren, { item, scale, drag, setDrag, section }),
@@ -10072,8 +10159,8 @@ function NestBox({ item, scale }) {
10072
10159
  }
10073
10160
  var LIVE = /* @__PURE__ */ new Set(["chart", "matrix", "barcode", "sparkline", "databar", "bullet", "iconset", "rangebar", "richtext"]);
10074
10161
  var measurer = null;
10075
- async function paintItemSvg(shape2, def, sample) {
10076
- const store = fontStore();
10162
+ async function paintItemSvg(shape2, def, sample, api = activeStore2()) {
10163
+ const store = await loadFontStore();
10077
10164
  await Promise.all([store.load(), customFonts()]);
10078
10165
  if (shape2.type === "barcode" || hasType(shape2.items, "barcode")) await loadBarcodes();
10079
10166
  measurer || (measurer = new TextMeasurer(store));
@@ -10084,7 +10171,7 @@ async function paintItemSvg(shape2, def, sample) {
10084
10171
  } catch {
10085
10172
  }
10086
10173
  const base2 = { fontFamily: "Inter", fontSize: 9, color: "#1f2937", padding: 2, lineHeight: 1.25, textAlign: "auto", verticalAlign: "top", ...def.styles?.base || {} };
10087
- const theme3 = themeContext(typeof def.theme === "string" && useDesigner.getState().reused[def.theme]?.themeDef || def.themeDef);
10174
+ const theme3 = themeContext(typeof def.theme === "string" && api.getState().reused[def.theme]?.themeDef || def.themeDef);
10088
10175
  const ctxBase = { params, globals: { PageNumber: 1, TotalPages: 1, ExecutionTime: /* @__PURE__ */ new Date(), ReportName: def.name }, dataSets, fields: null, locale: def.locale, currency: def.currency, functions: functionTable(def), theme: theme3 };
10089
10176
  let seq = 0;
10090
10177
  const lc = {
@@ -10126,20 +10213,21 @@ async function paintItemSvg(shape2, def, sample) {
10126
10213
  return { html: pageToSvg({ items }, shape2.w, h, { background: false }), h, w };
10127
10214
  }
10128
10215
  function LivePreview({ item, scale }) {
10216
+ const api = useDesignerStore();
10129
10217
  const sample = useDesigner((s) => s.sample);
10130
10218
  const def = useDesigner((s) => s.def);
10131
- const [svg, setSvg] = (0, import_react7.useState)(null);
10219
+ const [svg, setSvg] = (0, import_react8.useState)(null);
10132
10220
  const { x, y, ...shape2 } = item;
10133
10221
  const key2 = JSON.stringify(shape2);
10134
- (0, import_react7.useEffect)(() => {
10222
+ (0, import_react8.useEffect)(() => {
10135
10223
  let alive = true;
10136
10224
  const t3 = setTimeout(async () => {
10137
10225
  try {
10138
- const out = await paintItemSvg(shape2, def, sample);
10226
+ const out = await paintItemSvg(shape2, def, sample, api);
10139
10227
  if (alive) {
10140
10228
  setSvg(out);
10141
- const ex2 = useDesigner.getState().extents || {};
10142
- if (Math.round(ex2[item.id]?.w || 0) !== Math.round(out.w)) useDesigner.setState({ extents: { ...ex2, [item.id]: { w: out.w } } });
10229
+ const ex2 = api.getState().extents || {};
10230
+ if (Math.round(ex2[item.id]?.w || 0) !== Math.round(out.w)) api.setState({ extents: { ...ex2, [item.id]: { w: out.w } } });
10143
10231
  }
10144
10232
  } catch (e) {
10145
10233
  if (alive) setSvg({ error: e.message });
@@ -10161,9 +10249,9 @@ function PartPreview({ item, scale }) {
10161
10249
  const p = (lib?.parts || []).find((q2) => q2.name === item.part);
10162
10250
  const src = p && findItemNamed(lib, p.item);
10163
10251
  const props = { ...Object.fromEntries((p?.properties || []).map((q2) => [q2.name, q2.default ?? null])), ...item.properties || {} };
10164
- const [svg, setSvg] = (0, import_react7.useState)(null);
10252
+ const [svg, setSvg] = (0, import_react8.useState)(null);
10165
10253
  const key2 = JSON.stringify([src, props]);
10166
- (0, import_react7.useEffect)(() => {
10254
+ (0, import_react8.useEffect)(() => {
10167
10255
  if (!src) return void 0;
10168
10256
  let alive = true;
10169
10257
  const t3 = setTimeout(() => {
@@ -10273,17 +10361,18 @@ function TableView({ item, scale, drag, setDrag }) {
10273
10361
  const readOnly2 = useDesigner((s) => s.readOnly);
10274
10362
  const tableSelected = sel.kind === "items" && sel.ids.includes(item.id);
10275
10363
  const onGrip = useItemDrag(item, scale, setDrag);
10364
+ const keys = useItemKeys(item);
10276
10365
  const r = rectOf(item, drag);
10277
10366
  const widths2 = item.columns.map((c, i2) => drag?.mode === "col" && drag.id === item.id && drag.col === i2 ? drag.w : c.width);
10278
- const [dropCell, setDropCell] = (0, import_react7.useState)(null);
10279
- const [menu, setMenu] = (0, import_react7.useState)(null);
10280
- const problems = (0, import_react7.useContext)(IssueMap).get(item.id);
10281
- const tblRef = (0, import_react7.useRef)(null);
10367
+ const [dropCell, setDropCell] = (0, import_react8.useState)(null);
10368
+ const [menu, setMenu] = (0, import_react8.useState)(null);
10369
+ const problems = (0, import_react8.useContext)(IssueMap).get(item.id);
10370
+ const tblRef = (0, import_react8.useRef)(null);
10282
10371
  const chosen = sel.kind === "cell" && sel.id === item.id ? selCells(sel) : [];
10283
- (0, import_react7.useEffect)(() => {
10372
+ (0, import_react8.useEffect)(() => {
10284
10373
  if (!menu) return void 0;
10285
10374
  const close = (e) => {
10286
- if (e.type === "keydown" ? e.key === "Escape" : !e.target.closest?.(".cell-menu")) setMenu(null);
10375
+ if (e.type === "keydown" ? e.key === "Escape" : !e.composedPath().some((n) => n.classList?.contains("cell-menu"))) setMenu(null);
10287
10376
  };
10288
10377
  window.addEventListener("pointerdown", close, true);
10289
10378
  window.addEventListener("keydown", close);
@@ -10296,6 +10385,7 @@ function TableView({ item, scale, drag, setDrag }) {
10296
10385
  e.stopPropagation();
10297
10386
  if (e.button !== 0) return;
10298
10387
  e.preventDefault();
10388
+ e.currentTarget.closest("[data-item-key]")?.focus({ preventScroll: true });
10299
10389
  const st = useDesigner.getState();
10300
10390
  const here = { part, row: ri, col: ci };
10301
10391
  const cur2 = st.sel.kind === "cell" && st.sel.id === item.id ? st.sel : null;
@@ -10371,7 +10461,7 @@ function TableView({ item, scale, drag, setDrag }) {
10371
10461
  const h = t3.header?.[t3.header.length - 1]?.cells?.[ci];
10372
10462
  if (h && (!h.value || /^Column \d+$/.test(h.value))) h.value = f.field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (c) => c.toUpperCase());
10373
10463
  if (h && f.type === "number") h.style = { ...h.style || {}, textAlign: "right" };
10374
- } else if ((part === "footer" || /^g\d+f$/.test(part)) && f.type === "number") {
10464
+ } else if ((part === "footer" || /^g\d+[hf]$/.test(part)) && f.type === "number") {
10375
10465
  cell.value = `=Sum(Fields.${f.field})`;
10376
10466
  cell.style = { ...cell.style || {}, ...fieldStyle(f) };
10377
10467
  } else cell.value = `=Fields.${f.field}`;
@@ -10379,7 +10469,7 @@ function TableView({ item, scale, drag, setDrag }) {
10379
10469
  useDesigner.getState().select({ kind: "cell", id: item.id, part, row: ri, col: ci });
10380
10470
  };
10381
10471
  let first = true;
10382
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: tblRef, className: `tbl${tableSelected ? " sel" : ""}${problems ? " issue" : ""}`, "data-item": item.name, style: { left: r.x * scale, top: r.y * scale }, children: [
10472
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: tblRef, className: `tbl${tableSelected ? " sel" : ""}${problems ? " issue" : ""}`, "data-item": item.name, style: { left: r.x * scale, top: r.y * scale }, ...keys, children: [
10383
10473
  problems && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "issue-tag", title: problems.join("\n"), "aria-label": problems.join(". "), children: "!" }),
10384
10474
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "grip", title: "Select or move the table", onPointerDown: onGrip, children: "⠿" }),
10385
10475
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "col-handles", "aria-label": T("ux_columns"), role: "group", children: widths2.map((w, ci) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -10509,15 +10599,15 @@ function TableView({ item, scale, drag, setDrag }) {
10509
10599
  }
10510
10600
 
10511
10601
  // src/designer/Inspector.jsx
10512
- var import_react11 = __toESM(require_react(), 1);
10602
+ var import_react12 = __toESM(require_react(), 1);
10513
10603
 
10514
10604
  // src/designer/ExpressionEditor.jsx
10515
- var import_react9 = __toESM(require_react(), 1);
10605
+ var import_react10 = __toESM(require_react(), 1);
10516
10606
 
10517
10607
  // src/designer/ExprCode.jsx
10518
- var import_react8 = __toESM(require_react(), 1);
10608
+ var import_react9 = __toESM(require_react(), 1);
10519
10609
 
10520
- // ../reportwright/node_modules/@marijn/find-cluster-break/src/index.js
10610
+ // node_modules/@marijn/find-cluster-break/src/index.js
10521
10611
  var rangeFrom = [];
10522
10612
  var rangeTo = [];
10523
10613
  (() => {
@@ -10594,7 +10684,7 @@ function codePointSize(code2) {
10594
10684
  return code2 < 65536 ? 1 : 2;
10595
10685
  }
10596
10686
 
10597
- // ../reportwright/node_modules/@codemirror/state/dist/index.js
10687
+ // node_modules/@codemirror/state/dist/index.js
10598
10688
  var Text = class _Text {
10599
10689
  /**
10600
10690
  Get the line description around the given position.
@@ -12259,8 +12349,8 @@ var StateField = class _StateField {
12259
12349
  way it is initialized. Can be useful when you need to provide a
12260
12350
  non-default starting value for the field.
12261
12351
  */
12262
- init(create2) {
12263
- return [this, initField.of({ field: this, create: create2 })];
12352
+ init(create) {
12353
+ return [this, initField.of({ field: this, create })];
12264
12354
  }
12265
12355
  /**
12266
12356
  State field instances can be used as
@@ -13488,10 +13578,10 @@ var RangeSet = class _RangeSet {
13488
13578
  for (; ; ) {
13489
13579
  let curTo = Math.min(cursor.to, to);
13490
13580
  if (cursor.point) {
13491
- let active = cursor.activeForPoint(cursor.to);
13492
- let openCount = cursor.pointFrom < from ? active.length + 1 : cursor.point.startSide < 0 ? active.length : Math.min(active.length, openRanges);
13493
- iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank);
13494
- openRanges = Math.min(cursor.openEnd(curTo), active.length);
13581
+ let active2 = cursor.activeForPoint(cursor.to);
13582
+ let openCount = cursor.pointFrom < from ? active2.length + 1 : cursor.point.startSide < 0 ? active2.length : Math.min(active2.length, openRanges);
13583
+ iterator.point(pos, curTo, cursor.point, active2, openCount, cursor.pointRank);
13584
+ openRanges = Math.min(cursor.openEnd(curTo), active2.length);
13495
13585
  } else if (curTo > pos) {
13496
13586
  iterator.span(pos, curTo, cursor.active, openRanges);
13497
13587
  openRanges = cursor.openEnd(curTo);
@@ -13905,14 +13995,14 @@ var SpanCursor = class {
13905
13995
  activeForPoint(to) {
13906
13996
  if (!this.active.length)
13907
13997
  return this.active;
13908
- let active = [];
13998
+ let active2 = [];
13909
13999
  for (let i2 = this.active.length - 1; i2 >= 0; i2--) {
13910
14000
  if (this.activeRank[i2] < this.pointRank)
13911
14001
  break;
13912
14002
  if (this.activeTo[i2] > to || this.activeTo[i2] == to && this.active[i2].endSide >= this.point.endSide)
13913
- active.push(this.active[i2]);
14003
+ active2.push(this.active[i2]);
13914
14004
  }
13915
- return active.reverse();
14005
+ return active2.reverse();
13916
14006
  }
13917
14007
  openEnd(to) {
13918
14008
  let open = 0;
@@ -14006,7 +14096,7 @@ function findColumn(string2, col, tabSize, strict) {
14006
14096
  return strict === true ? -1 : string2.length;
14007
14097
  }
14008
14098
 
14009
- // ../reportwright/node_modules/style-mod/src/style-mod.js
14099
+ // node_modules/style-mod/src/style-mod.js
14010
14100
  var C = "ͼ";
14011
14101
  var COUNT = typeof Symbol == "undefined" ? "__" + C : Symbol.for(C);
14012
14102
  var SET = typeof Symbol == "undefined" ? "__styleSet" + Math.floor(Math.random() * 1e8) : /* @__PURE__ */ Symbol("styleSet");
@@ -14142,7 +14232,7 @@ var StyleSet = class {
14142
14232
  }
14143
14233
  };
14144
14234
 
14145
- // ../reportwright/node_modules/w3c-keyname/index.js
14235
+ // node_modules/w3c-keyname/index.js
14146
14236
  var base = {
14147
14237
  8: "Backspace",
14148
14238
  9: "Tab",
@@ -14248,7 +14338,7 @@ function keyName(event) {
14248
14338
  return name2;
14249
14339
  }
14250
14340
 
14251
- // ../reportwright/node_modules/@codemirror/view/dist/index.js
14341
+ // node_modules/@codemirror/view/dist/index.js
14252
14342
  var nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
14253
14343
  var doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
14254
14344
  var ie_edge = /* @__PURE__ */ /Edge\/(\d+)/.exec(nav.userAgent);
@@ -15431,9 +15521,9 @@ var viewPlugin = /* @__PURE__ */ Facet.define({
15431
15521
  }
15432
15522
  });
15433
15523
  var ViewPlugin = class _ViewPlugin {
15434
- constructor(id, create2, domEventHandlers, domEventObservers, buildExtensions) {
15524
+ constructor(id, create, domEventHandlers, domEventObservers, buildExtensions) {
15435
15525
  this.id = id;
15436
- this.create = create2;
15526
+ this.create = create;
15437
15527
  this.domEventHandlers = domEventHandlers;
15438
15528
  this.domEventObservers = domEventObservers;
15439
15529
  this.baseExtensions = buildExtensions(this);
@@ -15449,9 +15539,9 @@ var ViewPlugin = class _ViewPlugin {
15449
15539
  Define a plugin from a constructor function that creates the
15450
15540
  plugin's value, given an editor view.
15451
15541
  */
15452
- static define(create2, spec) {
15542
+ static define(create, spec) {
15453
15543
  const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
15454
- return new _ViewPlugin(nextPluginID++, create2, eventHandlers, eventObservers, (plugin) => {
15544
+ return new _ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, (plugin) => {
15455
15545
  let ext = [];
15456
15546
  if (deco)
15457
15547
  ext.push(decorations.of((view) => {
@@ -15539,11 +15629,11 @@ function getIsolatedRanges(view, line2) {
15539
15629
  RangeSet.spans(sets, line2.from, line2.to, {
15540
15630
  point() {
15541
15631
  },
15542
- span(fromDoc, toDoc, active, open) {
15632
+ span(fromDoc, toDoc, active2, open) {
15543
15633
  let from = fromDoc - line2.from, to = toDoc - line2.from;
15544
15634
  let level = result;
15545
- for (let i2 = active.length - 1; i2 >= 0; i2--, open--) {
15546
- let direction = active[i2].spec.bidiIsolate, update;
15635
+ for (let i2 = active2.length - 1; i2 >= 0; i2--, open--) {
15636
+ let direction = active2[i2].spec.bidiIsolate, update;
15547
15637
  if (direction == null)
15548
15638
  direction = autoDirection(line2.text, from, to);
15549
15639
  if (open > 0 && level.length && (update = level[level.length - 1]).to == from && update.direction == direction) {
@@ -16773,7 +16863,7 @@ var TileUpdate = class {
16773
16863
  let pendingLineAttrs = null;
16774
16864
  let b = this.builder, markCount = -1;
16775
16865
  let openEnd = RangeSet.spans(this.decorations, from, to, {
16776
- point: (from2, to2, deco, active, openStart, index) => {
16866
+ point: (from2, to2, deco, active2, openStart, index) => {
16777
16867
  if (deco instanceof PointDecoration) {
16778
16868
  if (this.disallowBlockEffectsFor[index]) {
16779
16869
  if (deco.block)
@@ -16781,8 +16871,8 @@ var TileUpdate = class {
16781
16871
  if (to2 > this.view.state.doc.lineAt(from2).to)
16782
16872
  throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
16783
16873
  }
16784
- markCount = active.length;
16785
- if (openStart > active.length) {
16874
+ markCount = active2.length;
16875
+ if (openStart > active2.length) {
16786
16876
  b.continueWidget(to2 - from2);
16787
16877
  } else {
16788
16878
  let widget = deco.widget || (deco.block ? NullWidget.block : NullWidget.inline);
@@ -16794,7 +16884,7 @@ var TileUpdate = class {
16794
16884
  b.addBlockWidget(tile);
16795
16885
  } else {
16796
16886
  b.ensureLine(pendingLineAttrs);
16797
- b.addInlineWidget(tile, active, openStart);
16887
+ b.addInlineWidget(tile, active2, openStart);
16798
16888
  }
16799
16889
  }
16800
16890
  pendingLineAttrs = null;
@@ -16804,7 +16894,7 @@ var TileUpdate = class {
16804
16894
  if (to2 > from2)
16805
16895
  this.text.skip(to2 - from2);
16806
16896
  },
16807
- span: (from2, to2, active, openStart) => {
16897
+ span: (from2, to2, active2, openStart) => {
16808
16898
  for (let pos = from2; pos < to2; ) {
16809
16899
  let chars = this.text.next(Math.min(512, to2 - pos));
16810
16900
  if (chars == null) {
@@ -16813,12 +16903,12 @@ var TileUpdate = class {
16813
16903
  pos++;
16814
16904
  } else {
16815
16905
  b.ensureLine(pendingLineAttrs);
16816
- b.addText(chars, active, pos == from2 ? openStart : active.length);
16906
+ b.addText(chars, active2, pos == from2 ? openStart : active2.length);
16817
16907
  pos += chars.length;
16818
16908
  }
16819
16909
  pendingLineAttrs = null;
16820
16910
  }
16821
- markCount = active.length;
16911
+ markCount = active2.length;
16822
16912
  }
16823
16913
  });
16824
16914
  if (markCount > -1)
@@ -18807,9 +18897,9 @@ handlers.mousedown = (view, event) => {
18807
18897
  if (mustFocus)
18808
18898
  view.observer.ignore(() => {
18809
18899
  focusPreventScroll(view.contentDOM);
18810
- let active = view.root.activeElement;
18811
- if (active && !active.contains(view.contentDOM))
18812
- active.blur();
18900
+ let active2 = view.root.activeElement;
18901
+ if (active2 && !active2.contains(view.contentDOM))
18902
+ active2.blur();
18813
18903
  });
18814
18904
  let mouseSel = view.inputState.mouseSelection;
18815
18905
  if (mouseSel) {
@@ -23124,7 +23214,7 @@ GutterMarker.prototype.mapMode = MapMode.TrackBefore;
23124
23214
  GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
23125
23215
  GutterMarker.prototype.point = true;
23126
23216
 
23127
- // ../reportwright/node_modules/@lezer/common/dist/index.js
23217
+ // node_modules/@lezer/common/dist/index.js
23128
23218
  var DefaultBufferLength = 1024;
23129
23219
  var nextPropID = 0;
23130
23220
  var Range2 = class {
@@ -24748,7 +24838,7 @@ var StringInput = class {
24748
24838
  };
24749
24839
  var stoppedInner = new NodeProp({ perNode: true });
24750
24840
 
24751
- // ../reportwright/node_modules/@lezer/highlight/dist/index.js
24841
+ // node_modules/@lezer/highlight/dist/index.js
24752
24842
  var nextTagID = 0;
24753
24843
  var Tag = class _Tag {
24754
24844
  /**
@@ -25472,7 +25562,7 @@ var classHighlighter = tagHighlighter([
25472
25562
  { tag: tags.punctuation, class: "tok-punctuation" }
25473
25563
  ]);
25474
25564
 
25475
- // ../reportwright/node_modules/@codemirror/language/dist/index.js
25565
+ // node_modules/@codemirror/language/dist/index.js
25476
25566
  var _a;
25477
25567
  var languageDataProp = /* @__PURE__ */ new NodeProp();
25478
25568
  function defineLanguageFacet(baseData) {
@@ -27099,7 +27189,7 @@ var marks = {
27099
27189
  auto: /* @__PURE__ */ Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
27100
27190
  };
27101
27191
 
27102
- // ../reportwright/node_modules/@codemirror/commands/dist/index.js
27192
+ // node_modules/@codemirror/commands/dist/index.js
27103
27193
  var toggleComment = (target) => {
27104
27194
  let { state } = target, line2 = state.doc.lineAt(state.selection.main.from), config2 = getConfig(target.state, line2.from);
27105
27195
  return config2.line ? toggleLineComment(target) : config2.block ? toggleBlockCommentByLine(target) : false;
@@ -28193,7 +28283,7 @@ var defaultKeymap = /* @__PURE__ */ [
28193
28283
  { key: "Ctrl-m", mac: "Shift-Alt-m", run: toggleTabFocusMode }
28194
28284
  ].concat(standardKeymap);
28195
28285
 
28196
- // ../reportwright/node_modules/@codemirror/autocomplete/dist/index.js
28286
+ // node_modules/@codemirror/autocomplete/dist/index.js
28197
28287
  var CompletionContext = class {
28198
28288
  /**
28199
28289
  Create a new completion context. (Mostly useful for testing
@@ -28852,7 +28942,7 @@ function scrollIntoView2(container2, element) {
28852
28942
  function score(option) {
28853
28943
  return (option.boost || 0) * 100 + (option.apply ? 10 : 0) + (option.info ? 5 : 0) + (option.type ? 1 : 0);
28854
28944
  }
28855
- function sortOptions(active, state) {
28945
+ function sortOptions(active2, state) {
28856
28946
  let options = [];
28857
28947
  let sections = null, dynamicSectionScore = null;
28858
28948
  let addOption = (option) => {
@@ -28867,7 +28957,7 @@ function sortOptions(active, state) {
28867
28957
  }
28868
28958
  };
28869
28959
  let conf = state.facet(completionConfig);
28870
- for (let a of active)
28960
+ for (let a of active2)
28871
28961
  if (a.hasResult()) {
28872
28962
  let getMatch = a.result.getMatch;
28873
28963
  if (a.result.filter === false) {
@@ -28930,12 +29020,12 @@ var CompletionDialog = class _CompletionDialog {
28930
29020
  setSelected(selected, id) {
28931
29021
  return selected == this.selected || selected >= this.options.length ? this : new _CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
28932
29022
  }
28933
- static build(active, state, id, prev, conf, didSetActive) {
28934
- if (prev && !didSetActive && active.some((s) => s.isPending))
29023
+ static build(active2, state, id, prev, conf, didSetActive) {
29024
+ if (prev && !didSetActive && active2.some((s) => s.isPending))
28935
29025
  return prev.setDisabled();
28936
- let options = sortOptions(active, state);
29026
+ let options = sortOptions(active2, state);
28937
29027
  if (!options.length)
28938
- return prev && active.some((a) => a.isPending) ? prev.setDisabled() : null;
29028
+ return prev && active2.some((a) => a.isPending) ? prev.setDisabled() : null;
28939
29029
  let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
28940
29030
  if (prev && prev.selected != selected && prev.selected != -1) {
28941
29031
  let selectedValue = prev.options[prev.selected].completion;
@@ -28946,7 +29036,7 @@ var CompletionDialog = class _CompletionDialog {
28946
29036
  }
28947
29037
  }
28948
29038
  return new _CompletionDialog(options, makeAttrs(id, selected), {
28949
- pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
29039
+ pos: active2.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
28950
29040
  create: createTooltip,
28951
29041
  above: conf.aboveCursor
28952
29042
  }, prev ? prev.timestamp : Date.now(), selected, false);
@@ -28959,8 +29049,8 @@ var CompletionDialog = class _CompletionDialog {
28959
29049
  }
28960
29050
  };
28961
29051
  var CompletionState = class _CompletionState {
28962
- constructor(active, id, open) {
28963
- this.active = active;
29052
+ constructor(active2, id, open) {
29053
+ this.active = active2;
28964
29054
  this.id = id;
28965
29055
  this.open = open;
28966
29056
  }
@@ -28970,7 +29060,7 @@ var CompletionState = class _CompletionState {
28970
29060
  update(tr) {
28971
29061
  let { state } = tr, conf = state.facet(completionConfig);
28972
29062
  let sources = conf.override || state.languageDataAt("autocomplete", cur(state)).map(asSource);
28973
- let active = sources.map((source) => {
29063
+ let active2 = sources.map((source) => {
28974
29064
  let value = this.active.find((s) => s.source == source) || new ActiveSource(
28975
29065
  source,
28976
29066
  this.active.some(
@@ -28981,17 +29071,17 @@ var CompletionState = class _CompletionState {
28981
29071
  );
28982
29072
  return value.update(tr, conf);
28983
29073
  });
28984
- if (active.length == this.active.length && active.every((a, i2) => a == this.active[i2]))
28985
- active = this.active;
29074
+ if (active2.length == this.active.length && active2.every((a, i2) => a == this.active[i2]))
29075
+ active2 = this.active;
28986
29076
  let open = this.open, didSet = tr.effects.some((e) => e.is(setActiveEffect));
28987
29077
  if (open && tr.docChanged)
28988
29078
  open = open.map(tr.changes);
28989
- if (tr.selection || active.some((a) => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) || !sameResults(active, this.active) || didSet)
28990
- open = CompletionDialog.build(active, state, this.id, open, conf, didSet);
28991
- else if (open && open.disabled && !active.some((a) => a.isPending))
29079
+ if (tr.selection || active2.some((a) => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) || !sameResults(active2, this.active) || didSet)
29080
+ open = CompletionDialog.build(active2, state, this.id, open, conf, didSet);
29081
+ else if (open && open.disabled && !active2.some((a) => a.isPending))
28992
29082
  open = null;
28993
- if (!open && active.every((a) => !a.isPending) && active.some((a) => a.hasResult()))
28994
- active = active.map((a) => a.hasResult() ? new ActiveSource(
29083
+ if (!open && active2.every((a) => !a.isPending) && active2.some((a) => a.hasResult()))
29084
+ active2 = active2.map((a) => a.hasResult() ? new ActiveSource(
28995
29085
  a.source,
28996
29086
  0
28997
29087
  /* State.Inactive */
@@ -28999,7 +29089,7 @@ var CompletionState = class _CompletionState {
28999
29089
  for (let effect of tr.effects)
29000
29090
  if (effect.is(setSelectedEffect))
29001
29091
  open = open && open.setSelected(effect.value, this.id);
29002
- return active == this.active && open == this.open ? this : new _CompletionState(active, this.id, open);
29092
+ return active2 == this.active && open == this.open ? this : new _CompletionState(active2, this.id, open);
29003
29093
  }
29004
29094
  get tooltip() {
29005
29095
  return this.open ? this.open.tooltip : null;
@@ -29084,9 +29174,9 @@ var ActiveSource = class _ActiveSource {
29084
29174
  /* State.Inactive */
29085
29175
  );
29086
29176
  else if (effect.is(setActiveEffect)) {
29087
- for (let active of effect.value)
29088
- if (active.source == value.source)
29089
- value = active;
29177
+ for (let active2 of effect.value)
29178
+ if (active2.source == value.source)
29179
+ value = active2;
29090
29180
  }
29091
29181
  }
29092
29182
  return value;
@@ -29230,8 +29320,8 @@ var closeCompletion = (view) => {
29230
29320
  return true;
29231
29321
  };
29232
29322
  var RunningQuery = class {
29233
- constructor(active, context) {
29234
- this.active = active;
29323
+ constructor(active2, context) {
29324
+ this.active = active2;
29235
29325
  this.context = context;
29236
29326
  this.time = Date.now();
29237
29327
  this.updates = [];
@@ -29248,9 +29338,9 @@ var completionPlugin = /* @__PURE__ */ ViewPlugin.fromClass(class {
29248
29338
  this.debounceAccept = -1;
29249
29339
  this.pendingStart = false;
29250
29340
  this.composing = 0;
29251
- for (let active of view.state.field(completionState).active)
29252
- if (active.isPending)
29253
- this.startQuery(active);
29341
+ for (let active2 of view.state.field(completionState).active)
29342
+ if (active2.isPending)
29343
+ this.startQuery(active2);
29254
29344
  }
29255
29345
  update(update) {
29256
29346
  let cState = update.state.field(completionState);
@@ -29295,19 +29385,19 @@ var completionPlugin = /* @__PURE__ */ ViewPlugin.fromClass(class {
29295
29385
  this.debounceUpdate = -1;
29296
29386
  this.pendingStart = false;
29297
29387
  let { state } = this.view, cState = state.field(completionState);
29298
- for (let active of cState.active) {
29299
- if (active.isPending && !this.running.some((r) => r.active.source == active.source))
29300
- this.startQuery(active);
29388
+ for (let active2 of cState.active) {
29389
+ if (active2.isPending && !this.running.some((r) => r.active.source == active2.source))
29390
+ this.startQuery(active2);
29301
29391
  }
29302
29392
  if (this.running.length && cState.open && cState.open.disabled)
29303
29393
  this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime);
29304
29394
  }
29305
- startQuery(active) {
29395
+ startQuery(active2) {
29306
29396
  let { state } = this.view, pos = cur(state);
29307
- let context = new CompletionContext(state, pos, active.explicit, this.view);
29308
- let pending2 = new RunningQuery(active, context);
29397
+ let context = new CompletionContext(state, pos, active2.explicit, this.view);
29398
+ let pending2 = new RunningQuery(active2, context);
29309
29399
  this.running.push(pending2);
29310
- Promise.resolve(active.source(context)).then((result) => {
29400
+ Promise.resolve(active2.source(context)).then((result) => {
29311
29401
  if (!pending2.context.aborted) {
29312
29402
  pending2.done = result || null;
29313
29403
  this.scheduleAccept();
@@ -29340,26 +29430,26 @@ var completionPlugin = /* @__PURE__ */ ViewPlugin.fromClass(class {
29340
29430
  if (query.done) {
29341
29431
  let pos = cur(query.updates.length ? query.updates[0].startState : this.view.state);
29342
29432
  let limit = Math.min(pos, query.done.from + (query.active.explicit ? 0 : 1));
29343
- let active = new ActiveResult(query.active.source, query.active.explicit, limit, query.done, query.done.from, (_a2 = query.done.to) !== null && _a2 !== void 0 ? _a2 : pos);
29433
+ let active2 = new ActiveResult(query.active.source, query.active.explicit, limit, query.done, query.done.from, (_a2 = query.done.to) !== null && _a2 !== void 0 ? _a2 : pos);
29344
29434
  for (let tr of query.updates)
29345
- active = active.update(tr, conf);
29346
- if (active.hasResult()) {
29347
- updated.push(active);
29435
+ active2 = active2.update(tr, conf);
29436
+ if (active2.hasResult()) {
29437
+ updated.push(active2);
29348
29438
  continue;
29349
29439
  }
29350
29440
  }
29351
29441
  let current2 = cState.active.find((a) => a.source == query.active.source);
29352
29442
  if (current2 && current2.isPending) {
29353
29443
  if (query.done == null) {
29354
- let active = new ActiveSource(
29444
+ let active2 = new ActiveSource(
29355
29445
  query.active.source,
29356
29446
  0
29357
29447
  /* State.Inactive */
29358
29448
  );
29359
29449
  for (let tr of query.updates)
29360
- active = active.update(tr, conf);
29361
- if (!active.isPending)
29362
- updated.push(active);
29450
+ active2 = active2.update(tr, conf);
29451
+ if (!active2.isPending)
29452
+ updated.push(active2);
29363
29453
  } else {
29364
29454
  this.startQuery(current2);
29365
29455
  }
@@ -29816,12 +29906,12 @@ var theme2 = EditorView.theme({
29816
29906
  "&.cm-focused .cm-selectionBackground, .cm-selectionBackground": { background: "var(--accent-soft)" }
29817
29907
  });
29818
29908
  var UNKNOWN = Decoration.mark({ class: "cm-unknown-field" });
29819
- var ExprCode = (0, import_react8.forwardRef)(function ExprCode2({ value, onChange, fields, parameters, invalid, check }, ref) {
29820
- const host = (0, import_react8.useRef)(null);
29821
- const view = (0, import_react8.useRef)(null);
29822
- const latest2 = (0, import_react8.useRef)({ onChange, fields, parameters, check });
29909
+ var ExprCode = (0, import_react9.forwardRef)(function ExprCode2({ value, onChange, fields, parameters, invalid, check }, ref) {
29910
+ const host = (0, import_react9.useRef)(null);
29911
+ const view = (0, import_react9.useRef)(null);
29912
+ const latest2 = (0, import_react9.useRef)({ onChange, fields, parameters, check });
29823
29913
  latest2.current = { onChange, fields, parameters, check };
29824
- (0, import_react8.useImperativeHandle)(ref, () => ({
29914
+ (0, import_react9.useImperativeHandle)(ref, () => ({
29825
29915
  insert(text) {
29826
29916
  const v = view.current;
29827
29917
  if (!v) return;
@@ -29835,7 +29925,7 @@ var ExprCode = (0, import_react8.forwardRef)(function ExprCode2({ value, onChang
29835
29925
  view.current?.focus();
29836
29926
  }
29837
29927
  }));
29838
- (0, import_react8.useEffect)(() => {
29928
+ (0, import_react9.useEffect)(() => {
29839
29929
  const complete = (ctx) => {
29840
29930
  const m = ctx.matchBefore(/(Fields|Parameters|Globals)\.\w*$/);
29841
29931
  if (m) {
@@ -29924,8 +30014,8 @@ var SNIPPETS = {
29924
30014
  function ExpressionEditor({ value, dataSet, onSave, onClose, title }) {
29925
30015
  const def = useDesigner((s) => s.def);
29926
30016
  const sample = useDesigner((s) => s.sample);
29927
- const [text, setText] = (0, import_react9.useState)(value ?? "");
29928
- const ta = (0, import_react9.useRef)(null);
30017
+ const [text, setText] = (0, import_react10.useState)(value ?? "");
30018
+ const ta = (0, import_react10.useRef)(null);
29929
30019
  const dsName = dataSet || def.dataSets?.[0]?.name;
29930
30020
  const ds = def.dataSets?.find((d) => d.name === dsName);
29931
30021
  const fields = fieldNames(ds).filter((n) => !(ds?.fields || []).some((f) => f.name === n && f.type === "object"));
@@ -29935,7 +30025,7 @@ function ExpressionEditor({ value, dataSet, onSave, onClose, title }) {
29935
30025
  const insert2 = (s) => ta.current?.insert(s);
29936
30026
  const err = checkValue(text);
29937
30027
  const issues = err ? [] : check(text);
29938
- const preview = (0, import_react9.useMemo)(() => {
30028
+ const preview = (0, import_react10.useMemo)(() => {
29939
30029
  if (err || !sample?.dataSets) return null;
29940
30030
  const rows = sample.dataSets[dsName] || [];
29941
30031
  const params = Object.fromEntries((def.parameters || []).map((p) => [p.name, p.default ?? null]));
@@ -30008,7 +30098,7 @@ function ExpressionEditor({ value, dataSet, onSave, onClose, title }) {
30008
30098
  }
30009
30099
 
30010
30100
  // src/designer/CondFormat.jsx
30011
- var import_react10 = __toESM(require_react(), 1);
30101
+ var import_react11 = __toESM(require_react(), 1);
30012
30102
 
30013
30103
  // src/designer/cfRules.js
30014
30104
  var CF_OPS = ["eq", "ne", "lt", "le", "gt", "ge", "between", "top", "bottom", "aboveAvg", "belowAvg", "contains", "startsWith", "empty", "notEmpty"];
@@ -30128,7 +30218,7 @@ function CondFormat({ title, cf: start, fields, thisValue, kind, custom, onApply
30128
30218
  const lang2 = useDesigner((s) => s.lang);
30129
30219
  const T = (k, v) => t(lang2, k, v);
30130
30220
  const blank = () => ({ field: thisValue ? void 0 : fields[0], op: "gt", value: "", format: kind === "chart" ? { backgroundColor: "#dc2626" } : { color: "#b42318", fontWeight: "bold" } });
30131
- const [cf, setCf] = (0, import_react10.useState)(() => JSON.parse(JSON.stringify(start || { mode: "first", rules: [blank()] })));
30221
+ const [cf, setCf] = (0, import_react11.useState)(() => JSON.parse(JSON.stringify(start || { mode: "first", rules: [blank()] })));
30132
30222
  const rules = cf.rules || [];
30133
30223
  const setRule = (i2, patch) => setCf((c) => ({ ...c, rules: c.rules.map((r, j) => j === i2 ? { ...r, ...patch } : r) }));
30134
30224
  const setFmt = (i2, k, v) => setCf((c) => ({ ...c, rules: c.rules.map((r, j) => {
@@ -30219,7 +30309,7 @@ var catLabel = (tk, cat) => tk(`cat_${cat}`, cat);
30219
30309
  function Inspector() {
30220
30310
  const sel = useDesigner((s) => s.sel);
30221
30311
  const def = useDesigner((s) => s.def);
30222
- const [expr, setExpr] = (0, import_react11.useState)(null);
30312
+ const [expr, setExpr] = (0, import_react12.useState)(null);
30223
30313
  const openExpr = (cfg) => setExpr(cfg);
30224
30314
  let body;
30225
30315
  if (sel.kind === "items" && sel.ids.length > 1) body = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Multi, { ids: sel.ids });
@@ -30234,13 +30324,13 @@ function Inspector() {
30234
30324
  const one = sel.kind === "items" && sel.ids.length === 1 ? findItem(def, sel.ids[0])?.item : sel.kind === "cell" ? findItem(def, sel.id)?.item : null;
30235
30325
  const scope = one ? one.dataSet || def.dataSets?.[0]?.name || null : void 0;
30236
30326
  const selKey = sel.kind === "items" ? `i:${sel.ids.join(",")}` : sel.kind === "cell" ? `c:${sel.id}:${sel.part}:${sel.row}:${sel.col}:${sel.cells?.length || 0}` : `${sel.kind}:${sel.section || ""}`;
30237
- const ref = (0, import_react11.useRef)(null);
30238
- (0, import_react11.useLayoutEffect)(() => {
30327
+ const ref = (0, import_react12.useRef)(null);
30328
+ (0, import_react12.useLayoutEffect)(() => {
30239
30329
  const pane = ref.current?.closest(".dright");
30240
30330
  if (pane) pane.scrollTop = 0;
30241
30331
  }, [selKey]);
30242
- const [closed, setClosed] = (0, import_react11.useState)(() => /* @__PURE__ */ new Set());
30243
- (0, import_react11.useLayoutEffect)(() => {
30332
+ const [closed, setClosed] = (0, import_react12.useState)(() => /* @__PURE__ */ new Set());
30333
+ (0, import_react12.useLayoutEffect)(() => {
30244
30334
  for (const h of ref.current?.querySelectorAll(".cat > h5") || []) {
30245
30335
  const shut = closed.has(h.textContent);
30246
30336
  h.querySelector("button")?.setAttribute("aria-expanded", String(!shut));
@@ -30259,13 +30349,13 @@ function Inspector() {
30259
30349
  });
30260
30350
  };
30261
30351
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { "data-testid": "inspector", ref, onClick: toggle, children: [
30262
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldScope.Provider, { value: scope, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.Fragment, { children: body }, selKey) }),
30352
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldScope.Provider, { value: scope, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react12.Fragment, { children: body }, selKey) }),
30263
30353
  expr && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ExpressionEditor, { ...expr, onClose: () => setExpr(null) })
30264
30354
  ] });
30265
30355
  }
30266
- function Geo({ label, value, onChange }) {
30356
+ function Geo({ label, value, onChange, min }) {
30267
30357
  const unit = useDesigner((s) => s.unit);
30268
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: `${label} (${unit})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NumberInput, { value: round(fromPt(value ?? 0, unit) * 100) / 100, step: unit === "pt" ? 1 : unit === "in" ? 0.05 : 0.5, onChange: (v) => onChange(round(toPt(v, unit))) }) });
30358
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: `${label} (${unit})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NumberInput, { value: round(fromPt(value ?? 0, unit) * 100) / 100, min: min == null ? void 0 : round(fromPt(min, unit) * 100) / 100, step: unit === "pt" ? 1 : unit === "in" ? 0.05 : 0.5, onChange: (v) => onChange(round(toPt(v, unit))) }) });
30269
30359
  }
30270
30360
  function PropEditor({ p: spec, value, onChange, openExpr, dataSet, item }) {
30271
30361
  const T = useT3();
@@ -30330,7 +30420,7 @@ function OverflowTarget({ value, onChange, self }) {
30330
30420
  }
30331
30421
  function ExprOr({ p, value, onChange, openExpr, dataSet, children }) {
30332
30422
  const T = useT3();
30333
- const [on, setOn] = (0, import_react11.useState)(isDynamic(value));
30423
+ const [on, setOn] = (0, import_react12.useState)(isDynamic(value));
30334
30424
  const label = T(on ? "useFixed" : "useExpr");
30335
30425
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: p.label, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "expr-row", children: [
30336
30426
  on ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ExprInput, { value, onChange: (v) => onChange(v || void 0), onOpen: () => openExpr({ value, dataSet, onSave: onChange, title: p.label }), placeholder: "=IIf(Fields.amount < 0, 'bold', 'normal')" }) : children,
@@ -30343,7 +30433,7 @@ function ExprOr({ p, value, onChange, openExpr, dataSet, children }) {
30343
30433
  function PaletteEditor({ label, value, onChange }) {
30344
30434
  const T = useT3();
30345
30435
  const custom = Array.isArray(value);
30346
- const [text, setText] = (0, import_react11.useState)(custom ? value.join(", ") : "");
30436
+ const [text, setText] = (0, import_react12.useState)(custom ? value.join(", ") : "");
30347
30437
  const parse2 = (t3) => t3.split(/[\s,;]+/).map((x) => x.trim()).filter((x) => /^#[0-9a-f]{6}$/i.test(x));
30348
30438
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "grid", gap: 6 }, children: [
30349
30439
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
@@ -30370,17 +30460,18 @@ function PaletteEditor({ label, value, onChange }) {
30370
30460
  custom && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: "flex", gap: 3, flexWrap: "wrap" }, "aria-hidden": "true", children: value.map((c, i2) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { width: 14, height: 14, borderRadius: 3, background: c, border: "1px solid var(--line)" } }, i2)) })
30371
30461
  ] });
30372
30462
  }
30373
- var reportList = null;
30463
+ var reportLists = /* @__PURE__ */ new Map();
30374
30464
  function ReportPicker({ value, onChange }) {
30375
- const [list2, setList] = (0, import_react11.useState)(reportList || []);
30376
- const listId = (0, import_react11.useState)(() => `reports-${Math.random().toString(36).slice(2, 8)}`)[0];
30377
- (0, import_react11.useEffect)(() => {
30465
+ const [list2, setList] = (0, import_react12.useState)(() => reportLists.get(useDesigner.getState().embed?.server || "") || []);
30466
+ const listId = (0, import_react12.useState)(() => `reports-${Math.random().toString(36).slice(2, 8)}`)[0];
30467
+ (0, import_react12.useEffect)(() => {
30378
30468
  const embed = useDesigner.getState().embed;
30379
- if (reportList || embed && !embed.server) return;
30380
30469
  const server2 = embed?.server || "";
30470
+ if (reportLists.has(server2) || embed && !embed.server) return;
30381
30471
  fetch(`${server2}/api/reports`).then((r) => r.ok ? r.json() : []).then((l) => {
30382
- reportList = Array.isArray(l) ? l : [];
30383
- setList(reportList);
30472
+ const v = Array.isArray(l) ? l : [];
30473
+ reportLists.set(server2, v);
30474
+ setList(v);
30384
30475
  }).catch(() => {
30385
30476
  });
30386
30477
  }, []);
@@ -30414,8 +30505,8 @@ function ItemProps({ item, section, openExpr }) {
30414
30505
  const tk = useTk();
30415
30506
  const def = useDesigner((s) => s.def);
30416
30507
  const spec = ITEMS[item.type];
30417
- const [nameErr, setNameErr] = (0, import_react11.useState)(null);
30418
- const [find2, setFind] = (0, import_react11.useState)("");
30508
+ const [nameErr, setNameErr] = (0, import_react12.useState)(null);
30509
+ const [find2, setFind] = (0, import_react12.useState)("");
30419
30510
  const q2 = find2.trim().toLowerCase();
30420
30511
  const hit = (p, type) => !q2 || propLabel(tk, p, type).toLowerCase().includes(q2) || p.key.toLowerCase().includes(q2);
30421
30512
  const props = (spec?.props || []).filter((p) => (!p.showIf || p.showIf(item)) && hit(p, item.type));
@@ -30467,8 +30558,8 @@ function ItemProps({ item, section, openExpr }) {
30467
30558
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid2", children: [
30468
30559
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Geo, { label: "X", value: item.x, onChange: (v) => setGeo("x", Math.max(0, v)) }),
30469
30560
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Geo, { label: "Y", value: item.y, onChange: (v) => setGeo("y", Math.max(0, v)) }),
30470
- item.type !== "table" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Geo, { label: T("i_width"), value: item.w, onChange: (v) => setGeo("w", Math.max(0, v)) }),
30471
- item.type !== "table" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Geo, { label: T("i_height"), value: item.h, onChange: (v) => setGeo("h", Math.max(0, v)) }),
30561
+ item.type !== "table" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Geo, { label: T("i_width"), value: item.w, min: item.type === "line" ? 0 : 1, onChange: (v) => setGeo("w", Math.max(0, v)) }),
30562
+ item.type !== "table" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Geo, { label: T("i_height"), value: item.h, min: item.type === "line" ? 0 : 1, onChange: (v) => setGeo("h", Math.max(0, v)) }),
30472
30563
  item.type === "table" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("tableSize"), hint: T("tableSizeHint"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TextInput, { value: `${round(item.w)} × ${round(item.h)} pt`, readOnly: true, "data-testid": "table-size" }) })
30473
30564
  ] }),
30474
30565
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hint", children: (() => {
@@ -30559,7 +30650,7 @@ function PivotEditor({ item, openExpr }) {
30559
30650
  const def = useDesigner((s) => s.def);
30560
30651
  const fields = fieldsOf2(def, item.dataSet);
30561
30652
  const spec = normalizeMatrix(item);
30562
- const [pick, setPick] = (0, import_react11.useState)({ rowGroups: "", columnGroups: "", values: "" });
30653
+ const [pick, setPick] = (0, import_react12.useState)({ rowGroups: "", columnGroups: "", values: "" });
30563
30654
  const edit = (fn3, key2) => updateItem(item.id, (it) => {
30564
30655
  if (!isTablix(it)) {
30565
30656
  const n = normalizeMatrix(it);
@@ -30638,7 +30729,17 @@ function PivotEditor({ item, openExpr }) {
30638
30729
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("groupExpandLevels"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NumberInput, { value: g.expandLevels ?? "", step: 1, onChange: (v) => setK(list2, i2, "expandLevels", Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0) }) })
30639
30730
  ] })
30640
30731
  ] }),
30641
- (i2 < spec[list2].length - 1 || list2 === "rowGroups" && g.parent) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Check, { label: T("pivotCollapsible"), checked: g.collapsible, onChange: (v) => setK(list2, i2, "collapsible", v) }),
30732
+ (i2 < spec[list2].length - 1 || list2 === "rowGroups" && g.parent) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Check, { label: T("pivotCollapsible"), checked: g.collapsible, onChange: (v) => edit((it) => {
30733
+ const o = it[list2][i2];
30734
+ if (v) {
30735
+ o.collapsible = true;
30736
+ o.initiallyCollapsed = true;
30737
+ } else {
30738
+ delete o.collapsible;
30739
+ delete o.initiallyCollapsed;
30740
+ }
30741
+ }, `${item.id}.${list2}${i2}.collapsible`) }),
30742
+ " ",
30642
30743
  g.collapsible && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Check, { label: T("pivotStartCollapsed"), checked: g.initiallyCollapsed, onChange: (v) => setK(list2, i2, "initiallyCollapsed", v) }),
30643
30744
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-testid": `pivot-${list2}${i2}-action`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionEditor, { label: T("ux_headerAction"), value: g.action, onChange: (v) => setK(list2, i2, "action", v), openExpr, dataSet: item.dataSet }) }),
30644
30745
  g.total && g.total !== "none" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-testid": `pivot-${list2}${i2}-total-action`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionEditor, { label: T("ux_totalAction"), value: g.totalAction, onChange: (v) => setK(list2, i2, "totalAction", v), openExpr, dataSet: item.dataSet }) })
@@ -30664,7 +30765,7 @@ function PivotEditor({ item, openExpr }) {
30664
30765
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid2", children: [
30665
30766
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("pivotLabel"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TextInput, { value: v.label || "", onChange: (x) => setK("values", i2, "label", x) }) }),
30666
30767
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("pivotFormat"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TextInput, { value: v.format || "", onChange: (x) => setK("values", i2, "format", x), placeholder: "N0" }) }),
30667
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("pivotShow"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Select, { value: v.show || "value", options: shows, onChange: (x) => setK("values", i2, "show", x === "value" ? void 0 : x) }) })
30768
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("pivotShow"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Select, { value: v.show || "value", options: shows, onChange: (x) => edit((it) => setMatrixShow(it.values[i2], x), `${item.id}.values${i2}.show`) }) })
30668
30769
  ] }),
30669
30770
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-testid": `pivot-values${i2}-action`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionEditor, { label: T("ux_cellAction"), value: v.action, onChange: (x) => setK("values", i2, "action", x), openExpr, dataSet: item.dataSet }) }),
30670
30771
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label: T("cellBookmark"), children: exprField("values", i2, "bookmark", v.bookmark, T("cellBookmark")) })
@@ -30706,8 +30807,8 @@ function GroupsEditor({ table: table2, openExpr }) {
30706
30807
  const T = useT3();
30707
30808
  const def = useDesigner((s) => s.def);
30708
30809
  const fields = fieldsOf2(def, table2.dataSet);
30709
- const [pick, setPick] = (0, import_react11.useState)("");
30710
- const [drill, setDrill] = (0, import_react11.useState)(false);
30810
+ const [pick, setPick] = (0, import_react12.useState)("");
30811
+ const [drill, setDrill] = (0, import_react12.useState)(false);
30711
30812
  const setG = (i2, k, v) => updateItem(table2.id, (t3) => {
30712
30813
  if (v === void 0 || v === "" || v === false) delete t3.groups[i2][k];
30713
30814
  else t3.groups[i2][k] = v;
@@ -30949,7 +31050,7 @@ rimary" ? void 0 : v), "aria-label": `${T("overlayAxis")} ${i2 + 1}` }) }),
30949
31050
  }
30950
31051
  function CfButton({ label, title, target, at: at2, kind, fields, apply: apply2, testid }) {
30951
31052
  const T = useT3();
30952
- const [open, setOpen] = (0, import_react11.useState)(false);
31053
+ const [open, setOpen] = (0, import_react12.useState)(false);
30953
31054
  const n = target?.cf?.rules?.length || 0;
30954
31055
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
30955
31056
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { type: "button", className: "btn sm", onClick: () => setOpen(true), "data-testid": testid, children: [
@@ -31016,14 +31117,16 @@ function VisualEditor({ cell, setCell, openExpr, dataSet }) {
31016
31117
  }
31017
31118
  var reportParams = /* @__PURE__ */ new Map();
31018
31119
  function useReportParams(id) {
31019
- const [list2, setList] = (0, import_react11.useState)(() => reportParams.get(id) ?? null);
31020
- (0, import_react11.useEffect)(() => {
31120
+ const srv = () => useDesigner.getState().embed?.server || "";
31121
+ const [list2, setList] = (0, import_react12.useState)(() => reportParams.get(`${srv()} ${id}`) ?? null);
31122
+ (0, import_react12.useEffect)(() => {
31021
31123
  if (!id || isDynamic(id)) {
31022
31124
  setList(null);
31023
31125
  return;
31024
31126
  }
31025
- if (reportParams.has(id)) {
31026
- setList(reportParams.get(id));
31127
+ const key2 = `${srv()} ${id}`;
31128
+ if (reportParams.has(key2)) {
31129
+ setList(reportParams.get(key2));
31027
31130
  return;
31028
31131
  }
31029
31132
  let alive = true;
@@ -31036,7 +31139,7 @@ function useReportParams(id) {
31036
31139
  fetch(`${server2}/api/reports/${encodeURIComponent(id)}`).then((r) => r.ok ? r.json() : null).then((r) => {
31037
31140
  const d = r?.definition || r;
31038
31141
  const ps = d && typeof d === "object" ? (d.parameters || []).map((p) => ({ name: p.name, required: !!p.required && p.default == null, prompt: p.prompt })) : null;
31039
- if (ps) reportParams.set(id, ps);
31142
+ if (ps) reportParams.set(key2, ps);
31040
31143
  if (alive) setList(ps);
31041
31144
  }).catch(() => {
31042
31145
  if (alive) setList(null);
@@ -31050,7 +31153,7 @@ function useReportParams(id) {
31050
31153
  function ParamMap({ value, onChange, known, openExpr, dataSet, testid }) {
31051
31154
  const T = useT3();
31052
31155
  const map = value || {};
31053
- const [extra, setExtra] = (0, import_react11.useState)("");
31156
+ const [extra, setExtra] = (0, import_react12.useState)("");
31054
31157
  const knownNames = new Set((known || []).map((p) => p.name));
31055
31158
  const names = [...(known || []).map((p) => p.name), ...Object.keys(map).filter((k) => !knownNames.has(k))];
31056
31159
  const set2 = (k, v, keep) => {
@@ -31469,7 +31572,7 @@ function ReportProps() {
31469
31572
  const T = useT3();
31470
31573
  const def = useDesigner((s) => s.def);
31471
31574
  const families = useFontFamilies();
31472
- const [fontsOpen, setFontsOpen] = (0, import_react11.useState)(false);
31575
+ const [fontsOpen, setFontsOpen] = (0, import_react12.useState)(false);
31473
31576
  const set2 = (fn3, key2) => useDesigner.getState().apply(fn3, key2);
31474
31577
  const m = Array.isArray(def.page?.margins) ? def.page.margins : [36, 36, 36, 36];
31475
31578
  const base2 = def.styles?.base || {};
@@ -31548,7 +31651,7 @@ function ReportProps() {
31548
31651
  function ReportImages() {
31549
31652
  const T = useT3();
31550
31653
  const images = useDesigner((s) => s.def.images) || {};
31551
- const [err, setErr] = (0, import_react11.useState)(null);
31654
+ const [err, setErr] = (0, import_react12.useState)(null);
31552
31655
  const set2 = (fn3) => useDesigner.getState().apply((d) => {
31553
31656
  d.images || (d.images = {});
31554
31657
  fn3(d.images);
@@ -31587,7 +31690,7 @@ function EmbeddedPicker({ item, onPick }) {
31587
31690
  const images = useDesigner((s) => s.def.images) || {};
31588
31691
  const names = Object.keys(images);
31589
31692
  const cur2 = /^embedded:(.+)$/.exec(String(item.src || ""))?.[1];
31590
- const [err, setErr] = (0, import_react11.useState)(null);
31693
+ const [err, setErr] = (0, import_react12.useState)(null);
31591
31694
  const upload = async (files) => {
31592
31695
  setErr(null);
31593
31696
  const r = await uploadImages(files, T);
@@ -31672,7 +31775,7 @@ function NamedStyles() {
31672
31775
  const styleNames = useStyleNames();
31673
31776
  const def = useDesigner((s) => s.def);
31674
31777
  const named = def.styles?.named || {};
31675
- const [newName, setNewName] = (0, import_react11.useState)("");
31778
+ const [newName, setNewName] = (0, import_react12.useState)("");
31676
31779
  const setS = (name2, k, v) => useDesigner.getState().apply((d) => {
31677
31780
  var _a2, _b;
31678
31781
  d.styles || (d.styles = {});
@@ -31707,7 +31810,7 @@ function NamedStyles() {
31707
31810
  }
31708
31811
 
31709
31812
  // src/designer/DataPanel.jsx
31710
- var import_react14 = __toESM(require_react(), 1);
31813
+ var import_react15 = __toESM(require_react(), 1);
31711
31814
 
31712
31815
  // src/designer/explore.js
31713
31816
  var key = (k) => /^[A-Za-z_$][\w$]*$/.test(k) ? `.${k}` : `['${k}']`;
@@ -31743,7 +31846,7 @@ function bestRows(root) {
31743
31846
  }
31744
31847
 
31745
31848
  // src/designer/ODataEditor.jsx
31746
- var import_react12 = __toESM(require_react(), 1);
31849
+ var import_react13 = __toESM(require_react(), 1);
31747
31850
  var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
31748
31851
  var OPS3 = ["eq", "ne", "gt", "ge", "lt", "le", "contains", "startswith", "endswith"];
31749
31852
  var OP_LABEL = { eq: "=", ne: "≠", gt: ">", ge: "≥", lt: "<", le: "≤" };
@@ -31758,7 +31861,7 @@ async function readMetadata(s) {
31758
31861
  return odataMetadata(await res.text());
31759
31862
  }
31760
31863
  function ODataEditor({ s, setS, T }) {
31761
- const [meta2, setMeta] = (0, import_react12.useState)(null);
31864
+ const [meta2, setMeta] = (0, import_react13.useState)(null);
31762
31865
  const sets = meta2?.sets || [];
31763
31866
  const set2 = sets.find((x) => x.name === s.entitySet);
31764
31867
  const props = set2 ? set2.properties : (s.select || []).map((name2) => ({ name: name2, type: s.types?.[name2] || "" }));
@@ -31849,7 +31952,7 @@ function ODataEditor({ s, setS, T }) {
31849
31952
  }
31850
31953
 
31851
31954
  // src/designer/SqlBuilder.jsx
31852
- var import_react13 = __toESM(require_react(), 1);
31955
+ var import_react14 = __toESM(require_react(), 1);
31853
31956
 
31854
31957
  // src/designer/sqlBuild.js
31855
31958
  var FILTER_OPS2 = ["=", "<>", "<", "<=", ">", ">=", "like", "in", "is null", "is not null"];
@@ -31922,8 +32025,8 @@ function normalizeSchema(body) {
31922
32025
  return tables;
31923
32026
  }
31924
32027
  function useSchema(connection) {
31925
- const [st, setSt] = (0, import_react13.useState)(() => schemas.has(connection) ? { tables: schemas.get(connection) } : { loading: !!connection });
31926
- (0, import_react13.useEffect)(() => {
32028
+ const [st, setSt] = (0, import_react14.useState)(() => schemas.has(connection) ? { tables: schemas.get(connection) } : { loading: !!connection });
32029
+ (0, import_react14.useEffect)(() => {
31927
32030
  if (!connection) {
31928
32031
  setSt({});
31929
32032
  return void 0;
@@ -31957,8 +32060,8 @@ function SqlBuilder({ s, setS, T, kind, onManual }) {
31957
32060
  const nb = { ...b, ...patch };
31958
32061
  setS({ ...s, builder: nb, query: buildSql(nb, kind) });
31959
32062
  };
31960
- const [find2, setFind] = (0, import_react13.useState)("");
31961
- const [preview, setPreview] = (0, import_react13.useState)(null);
32063
+ const [find2, setFind] = (0, import_react14.useState)("");
32064
+ const [preview, setPreview] = (0, import_react14.useState)(null);
31962
32065
  const sql = buildSql(b, kind);
31963
32066
  const tableOf2 = (name2) => (schema.tables || []).find((t3) => t3.name === name2);
31964
32067
  const colsOf = (alias) => tableOf2(b.tables.find((t3) => t3.alias === alias)?.name)?.columns || [];
@@ -31987,7 +32090,7 @@ function SqlBuilder({ s, setS, T, kind, onManual }) {
31987
32090
  if (from.t === to.t || b.joins.some((j) => j.from.t === from.t && j.from.c === from.c && j.to.t === to.t && j.to.c === to.c)) return;
31988
32091
  set2({ joins: [...b.joins, { type: "inner", from, to }] });
31989
32092
  };
31990
- const offered = (0, import_react13.useMemo)(() => b.tables.flatMap((t3) => suggestJoins(b, schema.tables, t3.alias)).filter((j, i2, a) => !b.joins.some((x) => x.from.t === j.from.t && x.from.c === j.from.c && x.to.t === j.to.t && x.to.c === j.to.c) && a.findIndex((y) => JSON.stringify(y) === JSON.stringify(j)) === i2), [b, schema.tables]);
32093
+ const offered = (0, import_react14.useMemo)(() => b.tables.flatMap((t3) => suggestJoins(b, schema.tables, t3.alias)).filter((j, i2, a) => !b.joins.some((x) => x.from.t === j.from.t && x.from.c === j.from.c && x.to.t === j.to.t && x.to.c === j.to.c) && a.findIndex((y) => JSON.stringify(y) === JSON.stringify(j)) === i2), [b, schema.tables]);
31991
32094
  const runPreview = async () => {
31992
32095
  setPreview({ loading: true });
31993
32096
  try {
@@ -32150,7 +32253,7 @@ function SqlBuilder({ s, setS, T, kind, onManual }) {
32150
32253
  ] });
32151
32254
  }
32152
32255
  function JoinAdder({ allCols, split, onAdd, T }) {
32153
- const [a, setA] = (0, import_react13.useState)(""), [z, setZ] = (0, import_react13.useState)("");
32256
+ const [a, setA] = (0, import_react14.useState)(""), [z, setZ] = (0, import_react14.useState)("");
32154
32257
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "qb-row", children: [
32155
32258
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Select, { value: a, allowEmpty: true, options: allCols, onChange: (v) => setA(v || ""), "aria-label": T("qb_joinFrom") }),
32156
32259
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "=" }),
@@ -32167,17 +32270,18 @@ function JoinAdder({ allCols, split, onAdd, T }) {
32167
32270
  var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
32168
32271
  var NAME2 = /^[A-Za-z_][\w]*$/;
32169
32272
  var noSourceKey = (def) => (def?.dataSources || []).length ? null : "r4_noDataSource";
32170
- async function loadSample() {
32171
- const { def } = useDesigner.getState();
32172
- useDesigner.setState({ sample: { loading: true } });
32273
+ async function loadSample(store) {
32274
+ const api = store?.getState ? store : activeStore();
32275
+ const { def } = api.getState();
32276
+ api.setState({ sample: { loading: true } });
32173
32277
  try {
32174
32278
  const params = resolveParameters(def, {});
32175
32279
  const sources = await loadSources(def, { params, baseUrl: origin(), sqlPreview: true, fetch: reportFetch(origin(), void 0, { sqlPreview: true }) });
32176
32280
  const ctx = { params, globals: { ExecutionTime: /* @__PURE__ */ new Date() }, dataSets: {}, fields: null, locale: def.locale, currency: def.currency, functions: functionTable(def) };
32177
32281
  const dataSets = buildDataSets(def, sources, ctx);
32178
- useDesigner.setState({ sample: { dataSets, sources, at: Date.now() } });
32282
+ api.setState({ sample: { dataSets, sources, at: Date.now() } });
32179
32283
  } catch (e) {
32180
- useDesigner.setState({ sample: { error: e.message } });
32284
+ api.setState({ sample: { error: e.message } });
32181
32285
  }
32182
32286
  }
32183
32287
  function DataPanel() {
@@ -32185,8 +32289,8 @@ function DataPanel() {
32185
32289
  const lang2 = useDesigner((s) => s.lang);
32186
32290
  const T = (k, v) => t(lang2, k, v);
32187
32291
  const sample = useDesigner((s) => s.sample);
32188
- const [dlg, setDlg] = (0, import_react14.useState)(null);
32189
- const [open, setOpen] = (0, import_react14.useState)(() => new Set((def.dataSets || []).map((d) => d.name)));
32292
+ const [dlg, setDlg] = (0, import_react15.useState)(null);
32293
+ const [open, setOpen] = (0, import_react15.useState)(() => new Set((def.dataSets || []).map((d) => d.name)));
32190
32294
  const toggle = (n) => setOpen((s) => {
32191
32295
  const x = new Set(s);
32192
32296
  x.has(n) ? x.delete(n) : x.add(n);
@@ -32335,10 +32439,10 @@ function ParamDialog({ index, onClose }) {
32335
32439
  const lang2 = useDesigner((s) => s.lang);
32336
32440
  const T = (k, v) => t(lang2, k, v);
32337
32441
  const orig = index != null ? def.parameters[index] : null;
32338
- const [p, setP] = (0, import_react14.useState)(orig || { name: "", type: "string", prompt: "", default: "", required: false });
32339
- const [columns, setColumns] = (0, import_react14.useState)(String(def.parameterLayout?.columns || 1));
32340
- const [live, setLive] = (0, import_react14.useState)(def.liveParameters !== false);
32341
- const [choicesText, setChoicesText] = (0, import_react14.useState)(() => (p.options || []).map(optionLine).join("\n"));
32442
+ const [p, setP] = (0, import_react15.useState)(orig || { name: "", type: "string", prompt: "", default: "", required: false });
32443
+ const [columns, setColumns] = (0, import_react15.useState)(String(def.parameterLayout?.columns || 1));
32444
+ const [live, setLive] = (0, import_react15.useState)(def.liveParameters !== false);
32445
+ const [choicesText, setChoicesText] = (0, import_react15.useState)(() => (p.options || []).map(optionLine).join("\n"));
32342
32446
  const fromFields = (def.dataSets?.find((d) => d.name === p.optionsFrom?.dataSet)?.fields || []).map((f) => f.name);
32343
32447
  const labelOpts = [...fromFields.map((f) => ({ value: `=Fields.${f}`, label: f })), ...p.optionsFrom?.label && !fromFields.some((f) => `=Fields.${f}` === p.optionsFrom.label) ? [{ value: p.optionsFrom.label, label: p.optionsFrom.label }] : []];
32344
32448
  const err = !NAME2.test(p.name) ? T("paramNameRule") : !uniqueIn(def.parameters, p.name, index) ? T("paramNameTaken") : p.optionsFrom && !p.optionsFrom.value ? T("ux_paramPickValueField") : null;
@@ -32485,7 +32589,7 @@ function SourceDialog({ index, onClose }) {
32485
32589
  const lang2 = useDesigner((s2) => s2.lang);
32486
32590
  const T = (k, v) => t(lang2, k, v);
32487
32591
  const orig = index != null ? def.dataSources[index] : null;
32488
- const [s, setS] = (0, import_react14.useState)(() => ({
32592
+ const [s, setS] = (0, import_react15.useState)(() => ({
32489
32593
  name: "",
32490
32594
  type: "rest",
32491
32595
  method: "GET",
@@ -32549,11 +32653,11 @@ function SourceDialog({ index, onClose }) {
32549
32653
  if (!err && s.type === "sql" && !s.connection) err = "Pick a connection";
32550
32654
  if (!err && s.type === "sql" && s.procedure != null && !s.procedure) err = "Pick a stored procedure";
32551
32655
  if (!err && s.type === "sql" && s.procedure == null && !/^\s*(select|with)\b/i.test(s.query || "")) err = "Write a SELECT query";
32552
- const [conns, setConns] = (0, import_react14.useState)(null);
32656
+ const [conns, setConns] = (0, import_react15.useState)(null);
32553
32657
  const procs = (conns || []).find((c) => c.name === s.connection)?.procs || [];
32554
32658
  const sqlKind = (conns || []).find((c) => c.name === s.connection)?.kind || "sqlite";
32555
- const [probe, setProbe] = (0, import_react14.useState)(null);
32556
- (0, import_react14.useEffect)(() => {
32659
+ const [probe, setProbe] = (0, import_react15.useState)(null);
32660
+ (0, import_react15.useEffect)(() => {
32557
32661
  if (s.type !== "sql" || conns) return;
32558
32662
  fetch("/api/data/sql/connections").then((r) => r.ok ? r.json() : []).then(setConns).catch(() => setConns([]));
32559
32663
  }, [s.type, conns]);
@@ -32746,13 +32850,13 @@ function DataSetDialog({ index, onClose, onSaved }) {
32746
32850
  const lang2 = useDesigner((s) => s.lang);
32747
32851
  const T = (k, v) => t(lang2, k, v);
32748
32852
  const orig = index != null ? def.dataSets[index] : null;
32749
- const [d, setD] = (0, import_react14.useState)(() => ({ name: "", source: def.dataSources?.[0]?.name, path: "$", fields: [], sort: [], ...orig ? JSON.parse(JSON.stringify(orig)) : {} }));
32750
- const [detect, setDetect] = (0, import_react14.useState)({ state: "idle" });
32751
- const [pathTyped, setPathTyped] = (0, import_react14.useState)(!!orig);
32853
+ const [d, setD] = (0, import_react15.useState)(() => ({ name: "", source: def.dataSources?.[0]?.name, path: "$", fields: [], sort: [], ...orig ? JSON.parse(JSON.stringify(orig)) : {} }));
32854
+ const [detect, setDetect] = (0, import_react15.useState)({ state: "idle" });
32855
+ const [pathTyped, setPathTyped] = (0, import_react15.useState)(!!orig);
32752
32856
  const nested = !!d.relation;
32753
32857
  const parents = (def.dataSets || []).filter((x, i2) => i2 !== index && x.name !== d.name);
32754
32858
  const err = !NAME2.test(d.name) ? T("dsNameRule") : !uniqueIn(def.dataSets, d.name, index) ? T("dsNameTaken") : nested ? !d.relation.parent ? T("dsPickParent") : null : !d.source ? T("dsPickSource") : null;
32755
- const [explore, setExplore] = (0, import_react14.useState)(null);
32859
+ const [explore, setExplore] = (0, import_react15.useState)(null);
32756
32860
  const loadSourceRaw = async (name2) => {
32757
32861
  const params = resolveParameters(useDesigner.getState().def, {});
32758
32862
  const src = useDesigner.getState().def.dataSources.find((s) => s.name === name2);
@@ -33002,7 +33106,7 @@ function DataSetDialog({ index, onClose, onSaved }) {
33002
33106
  }
33003
33107
 
33004
33108
  // src/designer/Outline.jsx
33005
- var import_react15 = __toESM(require_react(), 1);
33109
+ var import_react16 = __toESM(require_react(), 1);
33006
33110
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
33007
33111
  var MIME = "application/x-pw-outline";
33008
33112
  function Outline() {
@@ -33011,11 +33115,32 @@ function Outline() {
33011
33115
  const lang2 = useDesigner((s) => s.lang);
33012
33116
  const readOnly2 = useDesigner((s) => s.readOnly);
33013
33117
  const T = (k, v) => t(lang2, k, v);
33014
- const [drop, setDrop] = (0, import_react15.useState)(null);
33015
- const [said, setSaid] = (0, import_react15.useState)("");
33016
- const dragged = (0, import_react15.useRef)(null);
33017
- const body = (0, import_react15.useRef)(null);
33118
+ const [drop, setDrop] = (0, import_react16.useState)(null);
33119
+ const [said, setSaid] = (0, import_react16.useState)("");
33120
+ const [cur2, setCur] = (0, import_react16.useState)(null);
33121
+ const dragged = (0, import_react16.useRef)(null);
33122
+ const body = (0, import_react16.useRef)(null);
33018
33123
  const chosen = new Set(sel.kind === "items" ? sel.ids : sel.kind === "cell" ? [sel.id] : []);
33124
+ const keys = sectionList(def).flatMap(({ key: key2 }) => {
33125
+ const out = [`band:${key2}`];
33126
+ const go = (l) => l.forEach((it) => {
33127
+ out.push(it.id);
33128
+ if (NESTING.has(it.type)) go(it.items || []);
33129
+ });
33130
+ go(secOf(def, key2)?.items || []);
33131
+ return out;
33132
+ });
33133
+ const stop = keys.includes(cur2) ? cur2 : keys.find((k) => chosen.has(k) || sel.kind === "section" && k === `band:${sel.section}`) || keys[0];
33134
+ const roving = (k) => ({ tabIndex: k === stop ? 0 : -1, onFocus: () => setCur(k) });
33135
+ const nav2 = (e) => {
33136
+ if (e.defaultPrevented || e.altKey || e.metaKey || e.ctrlKey || e.target.getAttribute("role") !== "option") return;
33137
+ const all = [...e.currentTarget.querySelectorAll("[role=option]")];
33138
+ const i2 = all.indexOf(e.target);
33139
+ const j = e.key === "ArrowDown" ? i2 + 1 : e.key === "ArrowUp" ? i2 - 1 : e.key === "Home" ? 0 : e.key === "End" ? all.length - 1 : null;
33140
+ if (j == null) return;
33141
+ e.preventDefault();
33142
+ all[Math.max(0, Math.min(all.length - 1, j))]?.focus();
33143
+ };
33019
33144
  const pick = (e, id) => {
33020
33145
  const st = useDesigner.getState();
33021
33146
  if (e.shiftKey && st.sel.kind === "items") {
@@ -33127,30 +33252,32 @@ function Outline() {
33127
33252
  };
33128
33253
  const rows = (list2, parent, depth, section) => list2.map((it, i2) => {
33129
33254
  const mark = drop && drop.id != null && drop.id === it.id ? ` drop-${drop.where}` : "";
33130
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("li", { children: [
33255
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("li", { role: "none", children: [
33131
33256
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `trow outline-row${chosen.has(it.id) ? " sel" : ""}${mark}`, style: { paddingInlineStart: depth * 14 }, "data-outline": it.name, "data-outline-id": it.id, ...dragProps(it, i2, parent, section), children: [
33132
33257
  !readOnly2 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "grip", "aria-hidden": "true", children: "⠿" }),
33133
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "linklike nm", onClick: (e) => pick(e, it.id), onKeyDown: (e) => onKey(e, it, list2, i2, parent), "aria-pressed": chosen.has(it.id), "aria-describedby": readOnly2 ? void 0 : "outline-keys", children: it.name }),
33258
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", role: "option", className: "linklike nm", onClick: (e) => pick(e, it.id), onKeyDown: (e) => onKey(e, it, list2, i2, parent), "aria-selected": chosen.has(it.id), "aria-describedby": readOnly2 ? "outline-nav" : "outline-nav outline-keys", ...roving(it.id), children: it.name }),
33134
33259
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ty", children: T(`item_${it.type}`) === `item_${it.type}` ? ITEMS[it.type]?.label || it.type : T(`item_${it.type}`) }),
33135
33260
  !readOnly2 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "act", style: { display: "inline-flex", gap: 2 }, children: [
33136
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "btn sm ghost icon", disabled: i2 === 0, onClick: () => reorderItem(it.id, -1), title: T("outlineBackward"), "aria-label": `${T("outlineBackward")}: ${it.name}`, children: "↑" }),
33137
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "btn sm ghost icon", disabled: i2 === list2.length - 1, onClick: () => reorderItem(it.id, 1), title: T("outlineForward"), "aria-label": `${T("outlineForward")}: ${it.name}`, children: "↓" }),
33138
- parent && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "btn sm ghost icon", onClick: () => moveOutOfParent(it.id), title: T("outlineMoveOut", { name: parent.name }), "aria-label": `${T("outlineMoveOut", { name: parent.name })}: ${it.name}`, children: "⇱" })
33261
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "btn sm ghost icon", tabIndex: -1, disabled: i2 === 0, onClick: () => reorderItem(it.id, -1), title: T("outlineBackward"), "aria-label": `${T("outlineBackward")}: ${it.name}`, children: "↑" }),
33262
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "btn sm ghost icon", tabIndex: -1, disabled: i2 === list2.length - 1, onClick: () => reorderItem(it.id, 1), title: T("outlineForward"), "aria-label": `${T("outlineForward")}: ${it.name}`, children: "↓" }),
33263
+ parent && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "btn sm ghost icon", tabIndex: -1, onClick: () => moveOutOfParent(it.id), title: T("outlineMoveOut", { name: parent.name }), "aria-label": `${T("outlineMoveOut", { name: parent.name })}: ${it.name}`, children: "⇱" })
33139
33264
  ] })
33140
33265
  ] }),
33141
- NESTING.has(it.type) && (it.items || []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "tree", children: rows(it.items, it, depth + 1, section) })
33266
+ NESTING.has(it.type) && (it.items || []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "tree", role: "none", children: rows(it.items, it, depth + 1, section) })
33142
33267
  ] }, it.id);
33143
33268
  });
33144
33269
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pane", "data-testid": "outline", children: [
33145
33270
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h4", { children: T("outline") }),
33146
33271
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pane-body", ref: body, children: [
33147
- sectionList(def).map(({ key: key2, label }) => {
33272
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { role: "listbox", "aria-label": T("outline"), "aria-multiselectable": "true", onKeyDown: nav2, children: sectionList(def).map(({ key: key2, label }) => {
33148
33273
  const items = secOf(def, key2)?.items || [];
33149
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `outline-sec${drop?.band === key2 ? " drop-band" : ""}`, "data-outline-band": key2, ...bandDrop(key2), children: [
33150
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: `linklike outline-band${sel.kind === "section" && sel.section === key2 ? " sel" : ""}`, onClick: () => useDesigner.getState().select({ kind: "section", section: key2 }), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("b", { children: label }) }),
33151
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "tree", children: items.length ? rows(items, null, 1, key2) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("li", { className: "subtle", children: T("outlineEmpty") }) })
33274
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { role: "group", "aria-label": label, className: `outline-sec${drop?.band === key2 ? " drop-band" : ""}`, "data-outline-band": key2, ...bandDrop(key2), children: [
33275
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", role: "option", "aria-selected": sel.kind === "section" && sel.section === key2, "aria-describedby": "outline-nav", className: `linklike outline-band${sel.kind === "section" && sel.section === key2 ? " sel" : ""}`, onClick: () => useDesigner.getState().select({ kind: "section", section: key2 }), ...roving(`band:${key2}`),
33276
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("b", { children: label }) }),
33277
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "tree", role: "none", children: items.length ? rows(items, null, 1, key2) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("li", { className: "subtle", role: "none", children: T("outlineEmpty") }) })
33152
33278
  ] }, key2);
33153
- }),
33279
+ }) }),
33280
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sr-only", id: "outline-nav", children: T("b9_outlineKeys") }),
33154
33281
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "hint", children: T("outlineHint") }),
33155
33282
  !readOnly2 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "hint", id: "outline-keys", children: T("outlineKeys") }),
33156
33283
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sr-only", role: "status", "aria-live": "polite", children: said })
@@ -33159,7 +33286,7 @@ function Outline() {
33159
33286
  }
33160
33287
 
33161
33288
  // src/theme/ThemeToggle.jsx
33162
- var import_react16 = __toESM(require_react(), 1);
33289
+ var import_react17 = __toESM(require_react(), 1);
33163
33290
 
33164
33291
  // src/theme/theme.js
33165
33292
  var THEME_KEY = "pw-theme";
@@ -33201,8 +33328,8 @@ function Icon2({ pref }) {
33201
33328
  ] });
33202
33329
  }
33203
33330
  function ThemeToggle({ className = "btn sm ghost icon" }) {
33204
- const [pref, setPref] = (0, import_react16.useState)("system");
33205
- (0, import_react16.useEffect)(() => {
33331
+ const [pref, setPref] = (0, import_react17.useState)("system");
33332
+ (0, import_react17.useEffect)(() => {
33206
33333
  setPref(readPreference());
33207
33334
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
33208
33335
  const onChange = () => {
@@ -33220,7 +33347,7 @@ function ThemeToggle({ className = "btn sm ghost icon" }) {
33220
33347
  }
33221
33348
 
33222
33349
  // src/designer/ChartWizard.jsx
33223
- var import_react17 = __toESM(require_react(), 1);
33350
+ var import_react18 = __toESM(require_react(), 1);
33224
33351
  var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
33225
33352
  var TYPES = ["column", "bar", "line", "area", "pie", "donut", "scatter", "bubble", "radar", "funnel", "gauge", "treemap", "histogram", "boxplot", "waterfall", "gantt"];
33226
33353
  var PER_ROW = /* @__PURE__ */ new Set(["scatter", "bubble"]);
@@ -33336,14 +33463,14 @@ function ChartWizard() {
33336
33463
  const close = () => useDesigner.getState().setUi({ wizard: null });
33337
33464
  const lang2 = useDesigner((s) => s.lang);
33338
33465
  const tr = (k, v) => t(lang2, `cw_${k}`, v);
33339
- const [c, setC] = (0, import_react17.useState)(() => {
33466
+ const [c, setC] = (0, import_react18.useState)(() => {
33340
33467
  if (editing) return configFromChart(editing);
33341
33468
  const score2 = (d) => ((d.fields || []).some((f) => f.type === "number") ? 1e6 : 0) + (sample?.dataSets?.[d.name]?.length || 0);
33342
33469
  const ds2 = [...def.dataSets || []].sort((a, b) => score2(b) - score2(a))[0];
33343
33470
  return { title: "", split: "", stacked: false, format: "N0", ...guess(ds2, sample?.dataSets?.[ds2?.name] || []) };
33344
33471
  });
33345
33472
  const set2 = (patch) => setC((x) => ({ ...x, ...patch, custom: false }));
33346
- (0, import_react17.useEffect)(() => {
33473
+ (0, import_react18.useEffect)(() => {
33347
33474
  if (!sample?.dataSets && !sample?.loading) loadSample();
33348
33475
  }, []);
33349
33476
  const ds = (def.dataSets || []).find((d) => d.name === c.dataSet);
@@ -33354,7 +33481,7 @@ function ChartWizard() {
33354
33481
  const good = suggested(c, rows, fields);
33355
33482
  const dateCategory = fields.find((f) => f.name === c.category)?.type === "date";
33356
33483
  const monthish = fields.find((f) => f.name === c.category)?.type === "number" && /month/i.test(c.category) && rows.length > 0 && rows.every((r) => r[c.category] == null || Number.isInteger(r[c.category]) && r[c.category] >= 1 && r[c.category] <= 12);
33357
- const compact = (0, import_react17.useMemo)(() => {
33484
+ const compact = (0, import_react18.useMemo)(() => {
33358
33485
  if (!c.value || c.agg === "Count" || c.format === "P1") return null;
33359
33486
  const per = /* @__PURE__ */ new Map();
33360
33487
  for (const r of rows) {
@@ -33370,14 +33497,14 @@ function ChartWizard() {
33370
33497
  const d = def.dataSets.find((x) => x.name === name2);
33371
33498
  setC((x) => ({ ...x, split: "", ...guess(d, sample?.dataSets?.[name2] || []) }));
33372
33499
  };
33373
- (0, import_react17.useEffect)(() => {
33500
+ (0, import_react18.useEffect)(() => {
33374
33501
  if (editing || !rows.length || c.touched) return;
33375
33502
  setC((x) => ({ ...x, ...guess(ds, rows), touched: true }));
33376
33503
  }, [rows.length]);
33377
- const [thumbs, setThumbs] = (0, import_react17.useState)({});
33378
- const [big, setBig] = (0, import_react17.useState)(null);
33504
+ const [thumbs, setThumbs] = (0, import_react18.useState)({});
33505
+ const [big, setBig] = (0, import_react18.useState)(null);
33379
33506
  const cfgKey = JSON.stringify({ ...c, touched: void 0 });
33380
- (0, import_react17.useEffect)(() => {
33507
+ (0, import_react18.useEffect)(() => {
33381
33508
  if (!sample?.dataSets || !c.dataSet) return;
33382
33509
  let alive = true;
33383
33510
  const t3 = setTimeout(async () => {
@@ -33565,7 +33692,7 @@ function rawText(raw) {
33565
33692
  }
33566
33693
 
33567
33694
  // src/designer/TableWizard.jsx
33568
- var import_react18 = __toESM(require_react(), 1);
33695
+ var import_react19 = __toESM(require_react(), 1);
33569
33696
  var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
33570
33697
  var LAYOUTS = [
33571
33698
  { id: "list", label: "List", when: "Every row, with totals" },
@@ -33578,12 +33705,13 @@ function TableWizard() {
33578
33705
  const wiz = useDesigner((s) => s.tableWizard);
33579
33706
  const def = useDesigner((s) => s.def);
33580
33707
  const sample = useDesigner((s) => s.sample);
33708
+ const lang2 = useDesigner((s) => s.lang);
33581
33709
  const close = () => useDesigner.getState().setUi({ tableWizard: null });
33582
33710
  const parent = wiz?.parentId ? findItem(def, wiz.parentId)?.item : null;
33583
33711
  const width = Math.max(120, Math.round((parent ? parent.w : bodyWidth(def, wiz?.section || "body")) - (wiz?.x || 0)));
33584
- const [c, setC] = (0, import_react18.useState)(() => ({ dataSet: wiz?.dataSet || def.dataSets?.[0]?.name, layout: wiz?.type === "matrix" ? "matrix" : "list", groupBy: "", fields: null, mRows: "", mCols: "", mValue: "" }));
33712
+ const [c, setC] = (0, import_react19.useState)(() => ({ dataSet: wiz?.dataSet || def.dataSets?.[0]?.name, layout: wiz?.type === "matrix" ? "matrix" : "list", groupBy: "", fields: null, mRows: "", mCols: "", mValue: "" }));
33585
33713
  const set2 = (patch) => setC((x) => ({ ...x, ...patch }));
33586
- (0, import_react18.useEffect)(() => {
33714
+ (0, import_react19.useEffect)(() => {
33587
33715
  if (!sample?.dataSets && !sample?.loading) loadSample();
33588
33716
  }, []);
33589
33717
  const ds = (def.dataSets || []).find((d) => d.name === c.dataSet);
@@ -33597,9 +33725,9 @@ function TableWizard() {
33597
33725
  const groupBy2 = c.groupBy || groupGuess;
33598
33726
  const mRows = c.mRows || groupBy2, mCols = c.mCols || text.find((f) => f.name !== mRows)?.name || "", mValue = c.mValue || nums[0]?.name || "";
33599
33727
  const itemFor = (layout, w) => layout === "matrix" ? mRows && mCols ? matrixFor(ds, { rows: mRows, columns: mCols, value: mValue, agg: mValue ? "Sum" : "Count", width: w, sample: rows, currency: def.currency }) : null : tableFor(ds, { layout, groupBy: groupBy2, width: w, rows, fields: chosen, currency: def.currency });
33600
- const [thumbs, setThumbs] = (0, import_react18.useState)({});
33728
+ const [thumbs, setThumbs] = (0, import_react19.useState)({});
33601
33729
  const key2 = JSON.stringify([c, groupBy2, mRows, mCols, mValue, chosen]);
33602
- (0, import_react18.useEffect)(() => {
33730
+ (0, import_react19.useEffect)(() => {
33603
33731
  if (!ds || !sample?.dataSets) return;
33604
33732
  let alive = true;
33605
33733
  const t3 = setTimeout(async () => {
@@ -33644,7 +33772,7 @@ function TableWizard() {
33644
33772
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { className: "btn ghost", onClick: () => {
33645
33773
  useDesigner.getState().setUi({ tableWizard: null, pivotWizard: { section: wiz.section, x: wiz.x, y: wiz.y, parentId: wiz.parentId, dataSet: c.dataSet } });
33646
33774
  }, "data-testid": "open-pivot-wizard", children: [
33647
- t(useDesigner.getState().lang, "pivotWizard"),
33775
+ t(lang2, "pivotWizard"),
33648
33776
  "…"
33649
33777
  ] }),
33650
33778
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "btn", onClick: close, children: "Cancel" }),
@@ -33657,7 +33785,7 @@ function TableWizard() {
33657
33785
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("section", { className: "cw-side", "aria-label": "Data for the table", children: [
33658
33786
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Field, { label: "Data set", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Select, { value: c.dataSet, options: (def.dataSets || []).map((d) => d.name), onChange: (v) => set2({ dataSet: v, fields: null, groupBy: "", mRows: "", mCols: "", mValue: "" }) }) }),
33659
33787
  sample?.loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "hint", children: "Calling the API…" }),
33660
- noSourceKey(def) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "hint", role: "status", "data-testid": "no-source", children: t(useDesigner.getState().lang, noSourceKey(def)) }),
33788
+ noSourceKey(def) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "hint", role: "status", "data-testid": "no-source", children: t(lang2, noSourceKey(def)) }),
33661
33789
  sample?.error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "err", children: sample.error }),
33662
33790
  c.layout !== "matrix" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
33663
33791
  c.layout !== "list" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Field, { label: "Group by", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Select, { value: groupBy2, options: text.map((f) => f.name), onChange: (v) => set2({ groupBy: v }) }) }),
@@ -33687,7 +33815,7 @@ function TableWizard() {
33687
33815
  }
33688
33816
 
33689
33817
  // src/designer/AskAi.jsx
33690
- var import_react19 = __toESM(require_react(), 1);
33818
+ var import_react20 = __toESM(require_react(), 1);
33691
33819
  var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
33692
33820
  var trim = (v, n = 200) => Array.isArray(v) ? v.slice(0, n).map((x) => trim(x, n)) : v && typeof v === "object" ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, trim(x, n)])) : v;
33693
33821
  function changes(before, after) {
@@ -33703,9 +33831,9 @@ function changes(before, after) {
33703
33831
  function AskAi({ onClose }) {
33704
33832
  const def = useDesigner((s) => s.def);
33705
33833
  const sample = useDesigner((s) => s.sample);
33706
- const [prompt, setPrompt] = (0, import_react19.useState)("");
33707
- const [st, setSt] = (0, import_react19.useState)({ state: "idle" });
33708
- (0, import_react19.useEffect)(() => {
33834
+ const [prompt, setPrompt] = (0, import_react20.useState)("");
33835
+ const [st, setSt] = (0, import_react20.useState)({ state: "idle" });
33836
+ (0, import_react20.useEffect)(() => {
33709
33837
  if (!sample?.dataSets && !sample?.loading) loadSample();
33710
33838
  }, []);
33711
33839
  const ask = async () => {
@@ -33719,7 +33847,7 @@ function AskAi({ onClose }) {
33719
33847
  let preview = null;
33720
33848
  try {
33721
33849
  const params = resolveParameters(out.definition, {}, { lenient: true });
33722
- const model = await render(out.definition, { fontStore: fontStore(), parameters: params, sources: sources ? Object.fromEntries((out.definition.dataSources || []).map((s) => [s.name, sources[s.name] ?? (s.type === "json" ? s.data : [])])) : void 0, baseUrl: origin(), fetch: reportFetch(origin()) });
33850
+ const model = await render(out.definition, { fontStore: await loadFontStore(), parameters: params, sources: sources ? Object.fromEntries((out.definition.dataSources || []).map((s) => [s.name, sources[s.name] ?? (s.type === "json" ? s.data : [])])) : void 0, baseUrl: origin(), fetch: reportFetch(origin()) });
33723
33851
  preview = { html: pageToSvg(model.pages[0], model.width, model.height), pages: model.pages.length };
33724
33852
  } catch (e) {
33725
33853
  preview = { error: e.message };
@@ -33790,7 +33918,7 @@ function AskAi({ onClose }) {
33790
33918
  }
33791
33919
 
33792
33920
  // src/designer/PivotWizard.jsx
33793
- var import_react20 = __toESM(require_react(), 1);
33921
+ var import_react21 = __toESM(require_react(), 1);
33794
33922
  var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
33795
33923
  var MIME_PF = "application/x-pw-pivot-field";
33796
33924
  var AGGS2 = ["Sum", "Avg", "Count", "Min", "Max"];
@@ -33822,9 +33950,9 @@ function Thumb({ item }) {
33822
33950
  const T = useT2();
33823
33951
  const def = useDesigner((s) => s.def);
33824
33952
  const sample = useDesigner((s) => s.sample);
33825
- const [svg, setSvg] = (0, import_react20.useState)(null);
33953
+ const [svg, setSvg] = (0, import_react21.useState)(null);
33826
33954
  const key2 = JSON.stringify(item);
33827
- (0, import_react20.useEffect)(() => {
33955
+ (0, import_react21.useEffect)(() => {
33828
33956
  if (!item || !sample?.dataSets) return void 0;
33829
33957
  let alive = true;
33830
33958
  const t3 = setTimeout(() => {
@@ -33846,7 +33974,7 @@ function Thumb({ item }) {
33846
33974
  function useData(name2) {
33847
33975
  const def = useDesigner((s) => s.def);
33848
33976
  const sample = useDesigner((s) => s.sample);
33849
- (0, import_react20.useEffect)(() => {
33977
+ (0, import_react21.useEffect)(() => {
33850
33978
  if (!sample?.dataSets && !sample?.loading) loadSample();
33851
33979
  }, []);
33852
33980
  const ds = (def.dataSets || []).find((d) => d.name === name2);
@@ -33861,10 +33989,10 @@ function placeOf(w, def) {
33861
33989
  function PivotWizard() {
33862
33990
  const T = useT2();
33863
33991
  const w = useDesigner((s) => s.pivotWizard);
33864
- const [dsName, setDs] = (0, import_react20.useState)(() => w?.dataSet || useDesigner.getState().def.dataSets?.[0]?.name);
33992
+ const [dsName, setDs] = (0, import_react21.useState)(() => w?.dataSet || useDesigner.getState().def.dataSets?.[0]?.name);
33865
33993
  const { def, ds, fields, rows } = useData(dsName);
33866
- const [lay, setLay] = (0, import_react20.useState)({ rows: [], columns: [], values: [] });
33867
- const [totals, setTotals] = (0, import_react20.useState)(true);
33994
+ const [lay, setLay] = (0, import_react21.useState)({ rows: [], columns: [], values: [] });
33995
+ const [totals, setTotals] = (0, import_react21.useState)(true);
33868
33996
  const close = () => useDesigner.getState().setUi({ pivotWizard: null });
33869
33997
  const width = Math.round(bodyWidth(def, w?.section || "body") - (w?.x || 0));
33870
33998
  const add = (zone2, f) => setLay((l) => {
@@ -33985,13 +34113,13 @@ function InstantVisuals() {
33985
34113
  const T = useT2();
33986
34114
  const w = useDesigner((s) => s.instantVisuals);
33987
34115
  const { def, ds, fields, rows } = useData(w?.dataSet);
33988
- const [picked, setPickedRaw] = (0, import_react20.useState)(() => instantPicks(ds?.fields || [], rows));
33989
- const [touched, setTouched] = (0, import_react20.useState)(false);
34116
+ const [picked, setPickedRaw] = (0, import_react21.useState)(() => instantPicks(ds?.fields || [], rows));
34117
+ const [touched, setTouched] = (0, import_react21.useState)(false);
33990
34118
  const setPicked = (fn3) => {
33991
34119
  setTouched(true);
33992
34120
  setPickedRaw(fn3);
33993
34121
  };
33994
- (0, import_react20.useEffect)(() => {
34122
+ (0, import_react21.useEffect)(() => {
33995
34123
  if (!touched) setPickedRaw(instantPicks(ds?.fields || [], rows));
33996
34124
  }, [rows.length, ds]);
33997
34125
  const close = () => useDesigner.getState().setUi({ instantVisuals: null });
@@ -34191,9 +34319,10 @@ var TOOLS = [
34191
34319
  ] }) }
34192
34320
  ];
34193
34321
  function Toolbox({ T, hide = [] }) {
34322
+ const api = useDesignerStore();
34194
34323
  const name2 = (type) => T(`item_${type}`) === `item_${type}` ? ITEMS[type].label : T(`item_${type}`);
34195
34324
  const add = (type) => {
34196
- const s = useDesigner.getState();
34325
+ const s = api.getState();
34197
34326
  const at2 = insertSpot(s.def, s.sel);
34198
34327
  insertItem(type, at2.section, 0, at2.y, {}, at2.parentId);
34199
34328
  };
@@ -34242,12 +34371,15 @@ var TbGroup = ({ label, children }) => /* @__PURE__ */ (0, import_jsx_runtime20.
34242
34371
  ] });
34243
34372
  var QUICK = [["textbox", "text"], ["table", "table"], ["chart", "chart"], ["image", "image"]];
34244
34373
  function LeftRail({ T, readOnly: readOnly2, embed }) {
34245
- const body = (0, import_react21.useRef)(null);
34246
- const [at2, setAt] = (0, import_react21.useState)("tools");
34374
+ const body = (0, import_react22.useRef)(null);
34375
+ const [at2, setAt] = (0, import_react22.useState)("tools");
34247
34376
  const tabs = [!readOnly2 && ["tools", "b2_toolbox", "tools"], embed?.panels?.data !== false && ["data", "b2_data", "data"], ["outline", "b2_outline", "outline"]].filter(Boolean);
34248
34377
  const go = (id) => {
34249
34378
  const el = body.current?.querySelector(`[data-rail="${id}"]`);
34250
- if (el) body.current.scrollTo({ top: el.offsetTop - body.current.offsetTop, behavior: "smooth" });
34379
+ if (el) {
34380
+ body.current.scrollTo({ top: el.offsetTop - body.current.offsetTop, behavior: "smooth" });
34381
+ (el.querySelector('[role=listbox] [tabindex="0"]') || el.querySelector("button, input, select, textarea"))?.focus({ preventScroll: true });
34382
+ }
34251
34383
  setAt(id);
34252
34384
  };
34253
34385
  const spy = () => {
@@ -34349,8 +34481,9 @@ var framed = () => {
34349
34481
  }
34350
34482
  };
34351
34483
  function useAutosave(id, T, lang2, user) {
34352
- const [offer, setOffer] = (0, import_react21.useState)(null);
34353
- (0, import_react21.useEffect)(() => {
34484
+ const api = useDesignerStore();
34485
+ const [offer, setOffer] = (0, import_react22.useState)(null);
34486
+ (0, import_react22.useEffect)(() => {
34354
34487
  if (!id || user == null || framed()) return void 0;
34355
34488
  let store;
34356
34489
  try {
@@ -34360,10 +34493,10 @@ function useAutosave(id, T, lang2, user) {
34360
34493
  }
34361
34494
  clearDrafts(store, user);
34362
34495
  const d = readDraft(store, user, id);
34363
- if (d && plain(mergeDraft(d.def, useDesigner.getState().def)) !== plain(useDesigner.getState().def)) setOffer(d);
34496
+ if (d && plain(mergeDraft(d.def, api.getState().def)) !== plain(api.getState().def)) setOffer(d);
34364
34497
  else if (d) clearDraft(store, user, id);
34365
34498
  const keep = () => {
34366
- const s = useDesigner.getState();
34499
+ const s = api.getState();
34367
34500
  if (!s.saved && !s.readOnly) writeDraft(store, user, id, s.def, Date.now(), s.rev || null);
34368
34501
  };
34369
34502
  const t3 = setInterval(keep, AUTOSAVE_MS);
@@ -34372,7 +34505,7 @@ function useAutosave(id, T, lang2, user) {
34372
34505
  };
34373
34506
  window.addEventListener("pagehide", keep);
34374
34507
  document.addEventListener("visibilitychange", hide);
34375
- const unsub = useDesigner.subscribe((s, prev) => {
34508
+ const unsub = api.subscribe((s, prev) => {
34376
34509
  if (s.saved && !prev.saved) clearDraft(store, user, id);
34377
34510
  });
34378
34511
  return () => {
@@ -34384,13 +34517,13 @@ function useAutosave(id, T, lang2, user) {
34384
34517
  }, [id, user]);
34385
34518
  if (!offer) return null;
34386
34519
  const when = new Date(offer.at).toLocaleString(lang2 === "en" ? "en-IN" : lang2, { dateStyle: "medium", timeStyle: "short" });
34387
- const stale = staleDraft(offer, useDesigner.getState().rev);
34520
+ const stale = staleDraft(offer, api.getState().rev);
34388
34521
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "draft-bar", role: "alert", "data-testid": "draft-offer", "data-stale": stale || void 0, children: [
34389
34522
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: T(stale ? "ux_draftStale" : "ux_draftFound", { when }) }),
34390
34523
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: `btn sm${stale ? "" : " primary"}`, "data-testid": "draft-restore", onClick: () => {
34391
- const d = mergeDraft(offer.def, useDesigner.getState().def);
34392
- useDesigner.getState().apply(() => JSON.parse(JSON.stringify(d)));
34393
- useDesigner.getState().fixSelection();
34524
+ const d = mergeDraft(offer.def, api.getState().def);
34525
+ api.getState().apply(() => JSON.parse(JSON.stringify(d)));
34526
+ api.getState().fixSelection();
34394
34527
  setOffer(null);
34395
34528
  }, children: T(stale ? "ux_draftRestoreAnyway" : "ux_draftRestore") }),
34396
34529
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn sm", "data-testid": "draft-discard", onClick: () => {
@@ -34403,12 +34536,13 @@ function useAutosave(id, T, lang2, user) {
34403
34536
  ] });
34404
34537
  }
34405
34538
  function ConflictBar({ T }) {
34539
+ const api = useDesignerStore();
34406
34540
  const conflict = useDesigner((s) => s.conflict);
34407
34541
  if (!conflict) return null;
34408
34542
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "draft-bar", role: "alert", "data-testid": "save-conflict", children: [
34409
34543
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: T("ux_conflict") }),
34410
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn sm", "data-testid": "conflict-mine", onClick: () => useDesigner.getState().save({ overwrite: true }), children: T("ux_conflictMine") }),
34411
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn sm", "data-testid": "conflict-theirs", onClick: () => useDesigner.getState().takeTheirs(), children: T("ux_conflictTheirs") })
34544
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn sm", "data-testid": "conflict-mine", onClick: () => api.getState().save({ overwrite: true }), children: T("ux_conflictMine") }),
34545
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn sm", "data-testid": "conflict-theirs", onClick: () => api.getState().takeTheirs(), children: T("ux_conflictTheirs") })
34412
34546
  ] });
34413
34547
  }
34414
34548
  var AL = (d) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("svg", { width: "16", height: "14", viewBox: "0 0 16 14", "aria-hidden": "true", children: d });
@@ -34458,7 +34592,7 @@ var ALIGN2 = [
34458
34592
  ];
34459
34593
  function AlignBar({ T }) {
34460
34594
  const sel = useDesigner((s) => s.sel);
34461
- const [toPage, setToPage] = (0, import_react21.useState)(false);
34595
+ const [toPage, setToPage] = (0, import_react22.useState)(false);
34462
34596
  if (sel.kind !== "items" || !sel.ids.length) return null;
34463
34597
  const n = sel.ids.length;
34464
34598
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "align-bar", role: "toolbar", "aria-label": T("ux_alignBar"), "data-testid": "align-bar", children: [
@@ -34473,13 +34607,14 @@ function AlignBar({ T }) {
34473
34607
  ] });
34474
34608
  }
34475
34609
  function IssuesButton({ T }) {
34610
+ const api = useDesignerStore();
34476
34611
  const issues = useDesignIssues();
34477
- const [open, setOpen] = (0, import_react21.useState)(false);
34478
- (0, import_react21.useEffect)(() => {
34612
+ const [open, setOpen] = (0, import_react22.useState)(false);
34613
+ (0, import_react22.useEffect)(() => {
34479
34614
  if (!issues.length) setOpen(false);
34480
34615
  }, [issues.length]);
34481
- const listRef = (0, import_react21.useRef)(null);
34482
- (0, import_react21.useLayoutEffect)(() => {
34616
+ const listRef = (0, import_react22.useRef)(null);
34617
+ (0, import_react22.useLayoutEffect)(() => {
34483
34618
  const el = listRef.current;
34484
34619
  if (!el) return void 0;
34485
34620
  const place = () => {
@@ -34496,7 +34631,7 @@ function IssuesButton({ T }) {
34496
34631
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "issues-wrap", children: [
34497
34632
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", className: "btn sm issues-btn", "aria-expanded": open, onClick: () => setOpen((v) => !v), "data-testid": "issues", children: T("ux_issues", { n: issues.length }) }),
34498
34633
  open && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ul", { className: "issues-list", "data-testid": "issues-list", ref: listRef, children: issues.map((i2, k) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", className: "linklike", onClick: () => {
34499
- if (i2.id) useDesigner.getState().select({ kind: "items", ids: [i2.id] });
34634
+ if (i2.id) api.getState().select({ kind: "items", ids: [i2.id] });
34500
34635
  }, children: issueLine(T, i2) }) }, k)) })
34501
34636
  ] });
34502
34637
  }
@@ -34520,9 +34655,10 @@ var SHORTCUTS = [
34520
34655
  ["?", "sc_help"]
34521
34656
  ];
34522
34657
  function HistoryDialog({ id, onClose, T, lang: lang2 }) {
34523
- const [list2, setList] = (0, import_react21.useState)(null);
34524
- const [err, setErr] = (0, import_react21.useState)(null);
34525
- (0, import_react21.useEffect)(() => {
34658
+ const api = useDesignerStore();
34659
+ const [list2, setList] = (0, import_react22.useState)(null);
34660
+ const [err, setErr] = (0, import_react22.useState)(null);
34661
+ (0, import_react22.useEffect)(() => {
34526
34662
  fetch(`/api/reports/${id}/versions`).then((r) => r.json()).then(setList).catch((e) => setErr(e.message));
34527
34663
  }, [id]);
34528
34664
  const restore = async (v) => {
@@ -34532,7 +34668,7 @@ function HistoryDialog({ id, onClose, T, lang: lang2 }) {
34532
34668
  return;
34533
34669
  }
34534
34670
  const def = await res.json();
34535
- useDesigner.getState().init(id, def);
34671
+ api.getState().init(id, def);
34536
34672
  onClose();
34537
34673
  };
34538
34674
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Dialog, { title: T("versionHistory"), onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn", onClick: onClose, children: T("close") }), children: [
@@ -34554,18 +34690,48 @@ function HistoryDialog({ id, onClose, T, lang: lang2 }) {
34554
34690
  ] }, v.version))
34555
34691
  ] });
34556
34692
  }
34557
- function Designer({ id, initial, rev = null, lang: langIn, embed = null, user = null, ai = false }) {
34693
+ function Designer({ store: storeIn = null, ...props }) {
34694
+ const [store] = (0, import_react22.useState)(() => {
34695
+ const s = storeIn || newDesignerStore();
34696
+ activateStore(s);
34697
+ return s;
34698
+ });
34699
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DesignerStore.Provider, { value: store, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DesignerIn, { ...props }) });
34700
+ }
34701
+ var ACTIVATE = [
34702
+ "onPointerDownCapture",
34703
+ "onMouseDownCapture",
34704
+ "onKeyDownCapture",
34705
+ "onFocusCapture",
34706
+ "onClickCapture",
34707
+ "onInputCapture",
34708
+ "onChangeCapture",
34709
+ "onDragStartCapture",
34710
+ "onDragOverCapture",
34711
+ "onDropCapture",
34712
+ "onPasteCapture",
34713
+ "onWheelCapture",
34714
+ "onContextMenuCapture",
34715
+ "onDoubleClickCapture",
34716
+ "onSubmitCapture"
34717
+ ];
34718
+ function DesignerIn({ id, initial, rev = null, lang: langIn, embed = null, user = null, ai = false }) {
34719
+ const api = useDesignerStore();
34720
+ const activators = (0, import_react22.useMemo)(() => {
34721
+ const on = () => activateStore(api);
34722
+ return Object.fromEntries(ACTIVATE.map((k) => [k, on]));
34723
+ }, [api]);
34558
34724
  const lang2 = pickLang(langIn);
34559
34725
  const T = (k, v) => t(lang2, k, v);
34560
34726
  const q2 = lang2 === "en" ? "" : `?lang=${encodeURIComponent(lang2)}`;
34561
- const ready = (0, import_react21.useRef)(false);
34727
+ const ready = (0, import_react22.useRef)(false);
34562
34728
  if (!ready.current && typeof window !== "undefined") {
34563
- useDesigner.getState().init(id, initial);
34564
- useDesigner.getState().setUi({ lang: lang2, embed, readOnly: !!embed?.readOnly, rev, conflict: null });
34729
+ api.getState().init(id, initial);
34730
+ api.getState().setUi({ lang: lang2, embed, readOnly: !!embed?.readOnly, rev, conflict: null });
34565
34731
  ready.current = true;
34566
34732
  }
34567
34733
  const readOnly2 = !!embed?.readOnly;
34568
- const rootRef = (0, import_react21.useRef)(null);
34734
+ const rootRef = (0, import_react22.useRef)(null);
34569
34735
  const def = useDesigner((s) => s.def);
34570
34736
  const tab = useDesigner((s) => s.tab);
34571
34737
  const saved = useDesigner((s) => s.saved);
@@ -34575,23 +34741,23 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34575
34741
  const snap = useDesigner((s) => s.snap);
34576
34742
  const unit = useDesigner((s) => s.unit);
34577
34743
  const fonts = useDesigner((s) => s.fonts);
34578
- const fontCss = (0, import_react21.useMemo)(() => fontFaceCss(allFontKeys(), fontsBase().replace(/\/$/, "")), [fonts]);
34579
- (0, import_react21.useEffect)(() => {
34744
+ const fontCss = (0, import_react22.useMemo)(() => fontFaceCss(allFontKeys(), fontsBase().replace(/\/$/, "")), [fonts]);
34745
+ (0, import_react22.useEffect)(() => {
34580
34746
  if (embed) declareDocumentStyles("pw-designer-fonts", fontCss);
34581
34747
  }, [embed, fontCss]);
34582
- (0, import_react21.useEffect)(() => {
34583
- customFonts().then((list2) => useDesigner.getState().setUi({ fonts: list2 }));
34748
+ (0, import_react22.useEffect)(() => {
34749
+ customFonts().then((list2) => api.getState().setUi({ fonts: list2 }));
34584
34750
  }, []);
34585
- (0, import_react21.useEffect)(() => {
34751
+ (0, import_react22.useEffect)(() => {
34586
34752
  const z = savedZoom();
34587
- if (z != null) useDesigner.getState().setUi({ zoom: z });
34753
+ if (z != null) api.getState().setUi({ zoom: z });
34588
34754
  }, []);
34589
34755
  const canUndo = useDesigner((s) => s.past.length > 0);
34590
34756
  const canRedo = useDesigner((s) => s.future.length > 0);
34591
- const st = useDesigner.getState;
34592
- const [dialog, setDialog] = (0, import_react21.useState)(null);
34593
- const [showLeft, setShowLeft] = (0, import_react21.useState)(true);
34594
- const [showRight, setShowRight] = (0, import_react21.useState)(true);
34757
+ const st = api.getState;
34758
+ const [dialog, setDialog] = (0, import_react22.useState)(null);
34759
+ const [showLeft, setShowLeft] = (0, import_react22.useState)(true);
34760
+ const [showRight, setShowRight] = (0, import_react22.useState)(true);
34595
34761
  const selKind = useDesigner((s) => s.sel.kind);
34596
34762
  const wizard = useDesigner((s) => s.wizard);
34597
34763
  const tableWizard = useDesigner((s) => s.tableWizard);
@@ -34600,12 +34766,18 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34600
34766
  const cells = useDesigner((s) => s.cells);
34601
34767
  const cellSize = useDesigner((s) => s.cellSize);
34602
34768
  const dataKey = useDesigner((s) => `${(s.def?.dataSources || []).map((x) => x.name)}|${(s.def?.dataSets || []).map((x) => x.name)}`);
34603
- (0, import_react21.useEffect)(() => {
34604
- loadSample();
34769
+ (0, import_react22.useEffect)(() => {
34770
+ loadSample(api);
34605
34771
  }, [dataKey]);
34606
- (0, import_react21.useEffect)(() => {
34772
+ (0, import_react22.useEffect)(() => {
34773
+ let lastInside = false;
34774
+ const inside = (e) => (e.composedPath?.() || []).includes(rootRef.current);
34775
+ const onDown = (e) => {
34776
+ lastInside = inside(e);
34777
+ };
34778
+ const ours = (e) => inside(e) || lastInside && (e.target === document.body || e.target === document.documentElement);
34607
34779
  const onKey = (e) => {
34608
- if (embed && !(e.composedPath?.() || []).includes(rootRef.current)) return;
34780
+ if (embed && !ours(e)) return;
34609
34781
  const mod = e.metaKey || e.ctrlKey;
34610
34782
  if (mod && e.key.toLowerCase() === "s") {
34611
34783
  e.preventDefault();
@@ -34624,7 +34796,7 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34624
34796
  st().save();
34625
34797
  return;
34626
34798
  }
34627
- if (isTyping(e) || st().tab !== "design") return;
34799
+ if (isTyping(e) || st().tab !== "design" || e.defaultPrevented) return;
34628
34800
  if (mod && e.key.toLowerCase() === "z") {
34629
34801
  e.preventDefault();
34630
34802
  e.shiftKey ? st().redo() : st().undo();
@@ -34647,11 +34819,7 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34647
34819
  }
34648
34820
  return;
34649
34821
  }
34650
- if (mod && e.key.toLowerCase() === "v") {
34651
- e.preventDefault();
34652
- pasteItems(st().sel.kind === "section" ? st().sel.section : "body");
34653
- return;
34654
- }
34822
+ if (mod && e.key.toLowerCase() === "v") return;
34655
34823
  if (e.key === "Delete" || e.key === "Backspace") {
34656
34824
  if (st().sel.kind === "items") {
34657
34825
  e.preventDefault();
@@ -34683,10 +34851,24 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34683
34851
  e.returnValue = "";
34684
34852
  }
34685
34853
  };
34854
+ const onPaste = (e) => {
34855
+ if (embed && !ours(e) || isTyping(e) || st().tab !== "design") return;
34856
+ e.preventDefault();
34857
+ if (!pasteItems(st().sel.kind === "section" ? st().sel.section : "body", e.clipboardData?.getData("text/plain"))) {
34858
+ st().setUi({ saveError: t(lang2, "b9_nothingToPaste") });
34859
+ setTimeout(() => st().setUi({ saveError: null }), 3e3);
34860
+ }
34861
+ };
34686
34862
  window.addEventListener("keydown", onKey);
34863
+ window.addEventListener("paste", onPaste);
34864
+ window.addEventListener("pointerdown", onDown, true);
34865
+ window.addEventListener("focusin", onDown, true);
34687
34866
  if (!embed) window.addEventListener("beforeunload", onLeave);
34688
34867
  return () => {
34689
34868
  window.removeEventListener("keydown", onKey);
34869
+ window.removeEventListener("paste", onPaste);
34870
+ window.removeEventListener("pointerdown", onDown, true);
34871
+ window.removeEventListener("focusin", onDown, true);
34690
34872
  window.removeEventListener("beforeunload", onLeave);
34691
34873
  };
34692
34874
  }, [st, embed]);
@@ -34705,17 +34887,17 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34705
34887
  const d = await embed.onOpen();
34706
34888
  if (d && typeof d === "object") {
34707
34889
  st().init(id, d);
34708
- loadSample();
34890
+ loadSample(api);
34709
34891
  }
34710
34892
  };
34711
- const importInput = (0, import_react21.useRef)(null);
34712
- const [imported, setImported] = (0, import_react21.useState)(null);
34893
+ const importInput = (0, import_react22.useRef)(null);
34894
+ const [imported, setImported] = (0, import_react22.useState)(null);
34713
34895
  const importFile = async (file) => {
34714
34896
  if (!file) return;
34715
34897
  const r = readReportFile(file.name, await file.text());
34716
34898
  if (r.definition) {
34717
34899
  st().init(id, r.definition);
34718
- loadSample();
34900
+ loadSample(api);
34719
34901
  }
34720
34902
  setImported({ name: file.name, ok: !!r.definition, warnings: r.warnings || [], error: r.error });
34721
34903
  };
@@ -34729,7 +34911,7 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34729
34911
  ] }),
34730
34912
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "hint", children: T("loadingDesigner") })
34731
34913
  ] }) });
34732
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "designer", "data-testid": "designer", dir: dirOf(lang2), lang: lang2, ref: rootRef, "data-readonly": readOnly2 || void 0, children: [
34914
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "designer", "data-testid": "designer", dir: dirOf(lang2), lang: lang2, ref: rootRef, "data-readonly": readOnly2 || void 0, ...activators, children: [
34733
34915
  !embed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("style", { children: fontCss }),
34734
34916
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("header", { className: "dtop dbar", children: [
34735
34917
  embed ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "brand", children: [
@@ -34836,7 +35018,7 @@ function Designer({ id, initial, rev = null, lang: langIn, embed = null, user =
34836
35018
  imported.warnings.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ul", { children: imported.warnings.map((w, i2) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("li", { children: w }, i2)) })
34837
35019
  ] }),
34838
35020
  dialog === "keys" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Dialog, { title: T("shortcuts"), onClose: () => setDialog(null), footer: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { className: "btn", onClick: () => setDialog(null), children: T("close") }), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "kbd-grid", children: SHORTCUTS.map(([k, d]) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
34839
- import_react21.Fragment, { children: [
35021
+ import_react22.Fragment, { children: [
34840
35022
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "kbd", children: k }),
34841
35023
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: T(d) })
34842
35024
  ] }, d)) }) }),
@@ -34918,6 +35100,7 @@ var CSS2 = true ? `/* Layout: a drafting table. Cool grey workspace, white paper
34918
35100
  --ink: #16202a;
34919
35101
  --muted: #5f6b77;
34920
35102
  --faint: #64717e; /* 5:1 on panel (was #8e99a4, 2.9:1) */
35103
+ --faint-desk: #5a6672; /* small text on the desk: 4.85:1 on #e6eaee (--faint is 4.13:1 there) */
34921
35104
  --line-strong: #848f9b; /* control borders, 3:1 */
34922
35105
  --accent: #0e7490;
34923
35106
  --accent-ink: #ffffff;
@@ -34971,6 +35154,7 @@ var CSS2 = true ? `/* Layout: a drafting table. Cool grey workspace, white paper
34971
35154
  --ink: #e6edf3;
34972
35155
  --muted: #a3afbb;
34973
35156
  --faint: #8693a0;
35157
+ --faint-desk: #8693a0;
34974
35158
  --accent: #3cbfd4;
34975
35159
  --accent-ink: #03222a;
34976
35160
  --accent-soft: #123a44;
@@ -35309,7 +35493,7 @@ a.cell-name:hover { color: var(--accent); }
35309
35493
  .tbl .grip { position: absolute; left: -18px; top: -18px; width: 14px; height: 14px; border-radius: 3px; background: var(--panel-2); border: 1px solid var(--line); display: grid; place-items: center; cursor: move; font-size: var(--fs-2xs); color: var(--muted); }
35310
35494
  .tbl.sel .grip { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
35311
35495
  .tbl .row { position: relative; display: flex; }
35312
- .tbl .row-tag { position: absolute; left: -18px; width: 14px; display: grid; place-items: center; font: var(--fs-2xs) var(--mono); color: var(--faint); height: 100%; }
35496
+ .tbl .row-tag { position: absolute; left: -18px; width: 14px; display: grid; place-items: center; font: var(--fs-2xs) var(--mono); color: var(--faint-desk); height: 100%; }
35313
35497
  .tbl .cell { position: relative; overflow: hidden; display: flex; border-right: 1px dashed rgba(95,107,119,.3); border-bottom: 1px dashed rgba(95,107,119,.3); cursor: cell; }
35314
35498
  .tbl .cell:first-child { border-left: 1px dashed rgba(95,107,119,.3); }
35315
35499
  .tbl .row:first-child .cell { border-top: 1px dashed rgba(95,107,119,.3); }
@@ -35451,6 +35635,7 @@ a.cell-name:hover { color: var(--accent); }
35451
35635
  .vfrozen-head { position: absolute; top: 0; background: var(--paper); box-shadow: 0 3px 8px rgba(16, 24, 40, .18); pointer-events: none; }
35452
35636
  .vfrozen-head svg { display: block; width: 100%; height: 100%; }
35453
35637
  .search .opt { font-size: var(--fs-xs); font-weight: var(--fw-strong); }
35638
+ .search .opt .ww::before { content: "ab"; text-decoration: underline; } /* CSS, not text: the name is "Whole word" alone */
35454
35639
  .search .opt[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
35455
35640
  .vmatches button { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
35456
35641
  .vmatches button[aria-current] { background: var(--accent-soft); color: var(--accent); }
@@ -35828,13 +36013,14 @@ input[type=checkbox], input[type=radio] { width: 16px; height: 16px; accent-colo
35828
36013
 
35829
36014
  .pw-embed{display:block;font:var(--fw-regular) var(--fs-base)/var(--lh) var(--ui);color:var(--ink);background:var(--desk);border:1px solid var(--line);border-radius:8px;overflow:hidden}
35830
36015
  ` : "";
35831
- var BLANK = { $schema: "pagewright/report@1", name: "New report", page: { size: "A4", margins: [36, 36, 36, 36] }, dataSources: [], dataSets: [], sections: { body: { items: [] } } };
36016
+ var BLANK = { $schema: "pagewright/report@1", name: "Untitled report", page: { size: "A4", margins: [36, 36, 36, 36] }, dataSources: [], dataSets: [], sections: { body: { items: [] } } };
35832
36017
  function mountDesigner(target, opts = {}) {
35833
36018
  const el = typeof target === "string" ? document.querySelector(target) : target;
35834
36019
  if (!el) throw new Error(`mountDesigner: no element for ${target}`);
35835
36020
  const server2 = String(opts.server || "").replace(/\/$/, "");
35836
36021
  setAssetBase(server2);
35837
36022
  setStandalone({ standalone: !server2, fontsUrl: opts.fontsUrl });
36023
+ setFetchPolicy({ allowHosts: opts.allowHosts, unsafeFetch: opts.unsafeFetch, fetch: opts.fetch });
35838
36024
  if (server2) declareDocumentStyles("pw-embed-fonts", uiFontCss(server2), opts.nonce);
35839
36025
  const shadow = el.shadowRoot || el.attachShadow({ mode: "open" });
35840
36026
  shadow.innerHTML = '<div class="pw-embed"></div>';
@@ -35845,18 +36031,43 @@ function mountDesigner(target, opts = {}) {
35845
36031
  );
35846
36032
  box.style.height = opts.height || "760px";
35847
36033
  const root = (0, import_client.createRoot)(box);
36034
+ const store = newDesignerStore();
35848
36035
  const embed = { onSave: opts.onSave, onOpen: opts.onOpen, readOnly: !!opts.readOnly, toolbox: opts.toolbox, panels: opts.panels, dataSourceTemplates: opts.dataSourceTemplates, server: server2 };
35849
36036
  const show = (definition) => loadLanguage(opts.lang).catch(() => {
35850
- }).then(() => root.render((0, import_react22.createElement)(Designer, { id: opts.report, initial: definition, lang: opts.lang, embed })));
36037
+ }).then(() => root.render((0, import_react23.createElement)(Designer, { id: opts.report, initial: definition, lang: opts.lang, embed, store })));
35851
36038
  if (opts.definition || !opts.report) show(opts.definition || BLANK);
35852
36039
  else fetch(`${server2}/api/reports/${encodeURIComponent(opts.report)}`).then((r) => r.ok ? r.json() : r.json().then((j) => Promise.reject(new Error(j.error || `HTTP ${r.status}`)))).then(show).catch((e) => {
35853
36040
  box.innerHTML = `<div class="vmsg error">The report did not load: ${String(e.message).replace(/</g, "&lt;")}</div>`;
35854
36041
  });
36042
+ const onLeave = (e) => {
36043
+ e.preventDefault();
36044
+ e.returnValue = "";
36045
+ };
36046
+ let dirty = false;
36047
+ const unsub = store.subscribe((s) => {
36048
+ if (!s.saved === dirty) return;
36049
+ dirty = !s.saved;
36050
+ if (opts.warnOnLeave !== false) {
36051
+ if (dirty) window.addEventListener("beforeunload", onLeave);
36052
+ else window.removeEventListener("beforeunload", onLeave);
36053
+ }
36054
+ try {
36055
+ opts.onDirtyChange?.(dirty);
36056
+ } catch (e) {
36057
+ console.error(e);
36058
+ }
36059
+ });
35855
36060
  return {
35856
- unmount: () => root.unmount(),
36061
+ unmount: () => {
36062
+ unsub();
36063
+ window.removeEventListener("beforeunload", onLeave);
36064
+ root.unmount();
36065
+ },
36066
+ /** whether there are changes not saved yet */
36067
+ isDirty: () => !store.getState().saved,
35857
36068
  /** the definition as it is now (a plain copy) */
35858
- getDefinition: () => JSON.parse(JSON.stringify(useDesigner.getState().def)),
35859
- save: () => useDesigner.getState().save()
36069
+ getDefinition: () => JSON.parse(JSON.stringify(store.getState().def)),
36070
+ save: () => store.getState().save()
35860
36071
  };
35861
36072
  }
35862
36073
  export {