@wildwinter/expr-editor 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -254,6 +254,10 @@ interface ExpressionEditorOptions {
254
254
  /** Render the editor's own validation message list under the pills (default true).
255
255
  * Hosts that display their own validation messages pass false to avoid doubling up. */
256
256
  messages?: boolean;
257
+ /** Where to append the popover micro-editors (default document.body). Pass a
258
+ * container inside a focus-trapping dialog (Radix, etc.) so opening a pill's
259
+ * popover does not dismiss the surrounding dialog. */
260
+ popoverContainer?: HTMLElement;
257
261
  /** Emitted on every edit (name-form; "" when cleared). */
258
262
  onChange: (src: string) => void;
259
263
  /** Notified when the author starts (true) / stops (false) editing inside a popover
@@ -296,6 +300,10 @@ interface EffectsEditorOptions {
296
300
  allowEmit?: boolean;
297
301
  /** Start each inline value editor in raw-text mode (host's global "show as text" toggle drives this). */
298
302
  text?: boolean;
303
+ /** Where to append popover micro-editors (default document.body). Pass a container
304
+ * inside a focus-trapping dialog so popovers do not dismiss it. Threaded to the
305
+ * target/event pickers here and to every inline value editor. */
306
+ popoverContainer?: HTMLElement;
299
307
  /** Emitted on every structural / value edit with the whole new list. */
300
308
  onChange: (effects: EditorEffect[]) => void;
301
309
  }
package/dist/index.d.ts CHANGED
@@ -254,6 +254,10 @@ interface ExpressionEditorOptions {
254
254
  /** Render the editor's own validation message list under the pills (default true).
255
255
  * Hosts that display their own validation messages pass false to avoid doubling up. */
256
256
  messages?: boolean;
257
+ /** Where to append the popover micro-editors (default document.body). Pass a
258
+ * container inside a focus-trapping dialog (Radix, etc.) so opening a pill's
259
+ * popover does not dismiss the surrounding dialog. */
260
+ popoverContainer?: HTMLElement;
257
261
  /** Emitted on every edit (name-form; "" when cleared). */
258
262
  onChange: (src: string) => void;
259
263
  /** Notified when the author starts (true) / stops (false) editing inside a popover
@@ -296,6 +300,10 @@ interface EffectsEditorOptions {
296
300
  allowEmit?: boolean;
297
301
  /** Start each inline value editor in raw-text mode (host's global "show as text" toggle drives this). */
298
302
  text?: boolean;
303
+ /** Where to append popover micro-editors (default document.body). Pass a container
304
+ * inside a focus-trapping dialog so popovers do not dismiss it. Threaded to the
305
+ * target/event pickers here and to every inline value editor. */
306
+ popoverContainer?: HTMLElement;
299
307
  /** Emitted on every structural / value edit with the whole new list. */
300
308
  onChange: (effects: EditorEffect[]) => void;
301
309
  }
package/dist/index.js CHANGED
@@ -310,7 +310,8 @@ function button(cls, label, onClick, title) {
310
310
  b.addEventListener("click", onClick);
311
311
  return b;
312
312
  }
313
- function openPopover(anchor, render, onClose) {
313
+ function openPopover(anchor, render, opts = {}) {
314
+ const container = opts.container ?? document.body;
314
315
  const pop = el("div", "exed-pop");
315
316
  let closed = false;
316
317
  const close = () => {
@@ -319,7 +320,7 @@ function openPopover(anchor, render, onClose) {
319
320
  document.removeEventListener("pointerdown", onDown, true);
320
321
  document.removeEventListener("keydown", onKey, true);
321
322
  pop.remove();
322
- onClose?.();
323
+ opts.onClose?.();
323
324
  };
324
325
  const onDown = (e) => {
325
326
  const t = e.target;
@@ -332,15 +333,21 @@ function openPopover(anchor, render, onClose) {
332
333
  }
333
334
  };
334
335
  pop.append(render(close));
335
- document.body.append(pop);
336
+ container.append(pop);
336
337
  const a = anchor.getBoundingClientRect();
337
338
  const ph = pop.getBoundingClientRect();
338
339
  let top = a.bottom + 4;
339
340
  if (top + ph.height > window.innerHeight - 8 && a.top - ph.height - 4 > 8) top = a.top - ph.height - 4;
340
341
  let left = a.left;
341
342
  if (left + ph.width > window.innerWidth - 8) left = Math.max(8, window.innerWidth - 8 - ph.width);
342
- pop.style.top = `${Math.round(top + window.scrollY)}px`;
343
- pop.style.left = `${Math.round(left + window.scrollX)}px`;
343
+ if (container === document.body) {
344
+ pop.style.top = `${Math.round(top + window.scrollY)}px`;
345
+ pop.style.left = `${Math.round(left + window.scrollX)}px`;
346
+ } else {
347
+ const cr = container.getBoundingClientRect();
348
+ pop.style.top = `${Math.round(top - cr.top + container.scrollTop)}px`;
349
+ pop.style.left = `${Math.round(left - cr.left + container.scrollLeft)}px`;
350
+ }
344
351
  setTimeout(() => {
345
352
  document.addEventListener("pointerdown", onDown, true);
346
353
  document.addEventListener("keydown", onKey, true);
@@ -1037,9 +1044,12 @@ function mountExpressionEditor(host, opts) {
1037
1044
  apply: (next) => emit(toSrc(next)),
1038
1045
  openPopover: (anchor, r) => {
1039
1046
  closePopover();
1040
- const pop = openPopover(anchor, r, () => {
1041
- if (activePopover === pop) activePopover = null;
1042
- setEditing(false);
1047
+ const pop = openPopover(anchor, r, {
1048
+ container: opts.popoverContainer,
1049
+ onClose: () => {
1050
+ if (activePopover === pop) activePopover = null;
1051
+ setEditing(false);
1052
+ }
1043
1053
  });
1044
1054
  activePopover = pop;
1045
1055
  setEditing(true);
@@ -1331,7 +1341,7 @@ function mountEffectsEditor(host, opts) {
1331
1341
  close();
1332
1342
  },
1333
1343
  onCancel: close
1334
- }));
1344
+ }), { container: opts.popoverContainer });
1335
1345
  };
1336
1346
  const targetType = (target) => opts.catalogue.find((e) => refOf(e, defaultScope) === target)?.type;
1337
1347
  const valueDisplay = (value, onChange, type) => {
@@ -1346,6 +1356,7 @@ function mountEffectsEditor(host, opts) {
1346
1356
  functions: opts.functions,
1347
1357
  mode: "flat",
1348
1358
  ...addTerm ? { addTerm } : {},
1359
+ ...opts.popoverContainer ? { popoverContainer: opts.popoverContainer } : {},
1349
1360
  text: textMode,
1350
1361
  onChange
1351
1362
  }));
@@ -1385,7 +1396,7 @@ function mountEffectsEditor(host, opts) {
1385
1396
  wrap.append(search, list);
1386
1397
  setTimeout(() => search.focus(), 0);
1387
1398
  return wrap;
1388
- });
1399
+ }, { container: opts.popoverContainer });
1389
1400
  };
1390
1401
  const iconBtn = (glyph, title, onClick, danger = false) => button(`exed-eff-icon${danger ? " danger" : ""}`, glyph, onClick, title);
1391
1402
  function setRow(eff, i) {
@@ -1458,7 +1469,7 @@ function mountEffectsEditor(host, opts) {
1458
1469
  wrap.append(button("exed-btn primary", "Apply", apply));
1459
1470
  setTimeout(() => input.focus(), 0);
1460
1471
  return wrap;
1461
- });
1472
+ }, { container: opts.popoverContainer });
1462
1473
  };
1463
1474
  const rowActions2 = (i) => {
1464
1475
  const acts = el("div", "exed-effect-acts");