@nerd-bible/wordgard 0.5.3-beta7 → 0.6.2-beta1

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/editor.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { GardState, GardSelection, TextblockMap, BidiSpan, Transaction } from 'wordgard/state';
2
- import { Attributes, Elt, Node, Leaf, parse, Slice, Plot, serialize, Pos, ChangeSet, ValidationError, Mark } from 'wordgard/doc';
2
+ import { Attributes, Elt, Node, Mark, Leaf, parse, Slice, Plot, serialize, Pos, ChangeSet, ValidationError } from 'wordgard/doc';
3
3
  import { StyleModule } from 'style-mod';
4
4
  import { findClusterBreak } from '@marijn/find-cluster-break';
5
5
  import { findAbove, PointSet, RangeSet } from 'wordgard/set';
6
6
  import { enter, insertLineBreak, selectAll, undo, redo, killToLineEnd, yankKilled, transposeChars, Command, deleteUnit, deleteWord, deleteToLineEnd, moveByUnit, moveByLine, moveByWord, moveToLineSide, moveToDocSide, moveByPage, moveToTextblockSide, setAlignment, toggleUnderline, toggleEmphasis, toggleStrong, deleteLine, insertText, setDirection, deleteSelection, Menu, findWrappable, wrapBlockRange, autoJoinBlocks } from 'wordgard/command';
7
- import { PhraseSet, phrases } from 'wordgard/phrases';
7
+ import { phrases, PhraseSet } from 'wordgard/phrases';
8
+ import cr from 'crelt';
8
9
  import { history } from 'wordgard/history';
9
10
 
10
11
  function addUpdated(sections, updated) {
@@ -211,19 +212,19 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
211
212
  function shape(type, shape, config) {
212
213
  let tp = Node.Type.get(type);
213
214
  let shapeFunc = typeof shape == "function"
214
- ? tag => addMarkAttributes(shape(tag), tag)
215
- : tag => addMarkAttributes(shape, tag);
215
+ ? memo(tag => shape(tag))
216
+ : () => shape;
216
217
  let atom = typeof shape == "function" ? config?.atom : !shape.hasContent;
217
- let ext = tagShape.of({ type: tp, shape: memo(shapeFunc) });
218
+ let ext = tagShape.of({ type: tp, shape: shapeFunc });
218
219
  if (tp.isPlot && atom != null)
219
220
  ext = [ext, GardState.isAtom.of([tp, atom])];
220
221
  return ext;
221
222
  }
222
223
  Tag.shape = shape;
223
224
  (function (shape_1) {
224
- function dynamic( type, shape, config) {
225
+ function derived(type, shape, config) {
225
226
  let tp = Node.Type.get(type);
226
- let ext = tagShape.compute(state => {
227
+ let ext = tagShape.derive(state => {
227
228
  let s = shape(state);
228
229
  return { type: tp, shape: typeof s == "function" ? memo(s) : () => s };
229
230
  });
@@ -232,7 +233,7 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
232
233
  ext = [ext, GardState.isAtom.of([tp, atom])];
233
234
  return ext;
234
235
  }
235
- shape_1.dynamic = dynamic;
236
+ shape_1.derived = derived;
236
237
  })(shape = Tag.shape || (Tag.shape = {}));
237
238
  function wrapper(type, wrapper, options) {
238
239
  if (!wrapper.hasContent)
@@ -256,10 +257,10 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
256
257
  }
257
258
  Tag.widget = widget;
258
259
  (function (widget_1) {
259
- function dynamic(type, place, widget) {
260
+ function derived(type, place, widget) {
260
261
  let tp = Node.Type.get(type);
261
262
  let p = getPlace(place);
262
- return tagWidget.compute(state => {
263
+ return tagWidget.derive(state => {
263
264
  let w = widget(state);
264
265
  return {
265
266
  type: tp,
@@ -268,7 +269,7 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
268
269
  };
269
270
  });
270
271
  }
271
- widget_1.dynamic = dynamic;
272
+ widget_1.derived = derived;
272
273
  })(widget = Tag.widget || (Tag.widget = {}));
273
274
  function attribute(type, attr, value, options) {
274
275
  let tp = Node.Type.get(type);
@@ -277,6 +278,16 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
277
278
  }
278
279
  Tag.attribute = attribute;
279
280
  })(Decoration.Tag || (Decoration.Tag = {}));
281
+ (function (Mark$1) {
282
+ function shape(type, shape) {
283
+ if (type instanceof Mark)
284
+ type = type.type;
285
+ if (type.spanning && !("element" in shape))
286
+ throw new Error("Spanning marks must have an element shape");
287
+ return markShape.of({ type, modified: Mark.Type.define(type.name, { ...type.spec, shape }) });
288
+ }
289
+ Mark$1.shape = shape;
290
+ })(Decoration.Mark || (Decoration.Mark = {}));
280
291
  class Point {
281
292
  constructor() { }
282
293
  static widget(widget, options) {
@@ -324,10 +335,15 @@ const Decoration = /*@__PURE__*/(function (Decoration) {
324
335
  }
325
336
  Decoration.Range = Range;
326
337
  ;return Decoration})({});
327
- const tagShape = /*@__PURE__*/GardState.Facet.define();
338
+ const tagShape = /*@__PURE__*/GardState.Facet.define({
339
+ combine: shapes => new Map(shapes.map(s => [s.type, s.shape]).reverse())
340
+ });
328
341
  const tagWrapper = /*@__PURE__*/GardState.Facet.define();
329
342
  const tagWidget = /*@__PURE__*/GardState.Facet.define();
330
343
  const tagAttribute = /*@__PURE__*/GardState.Facet.define();
344
+ const markShape = /*@__PURE__*/GardState.Facet.define({
345
+ combine: shapes => new Map(shapes.map(s => [s.type, s.modified]).reverse())
346
+ });
331
347
  function memo(f) {
332
348
  let map = new WeakMap();
333
349
  return (arg) => {
@@ -337,11 +353,12 @@ function memo(f) {
337
353
  return found;
338
354
  };
339
355
  }
340
- function addMarkAttributes(shape, tag) {
356
+ function addMarkAttributes(overrides, shape, tag) {
341
357
  let attrs;
342
358
  for (let mark of tag.marks) {
343
- if (mark.type.attribute && (mark.spanning || !tag.isText)) {
344
- let { get, target } = mark.type.attribute;
359
+ let type = overrides.get(mark.type) || mark.type;
360
+ if (type.attribute && (mark.spanning || !tag.isText)) {
361
+ let { get, target } = type.attribute;
345
362
  let markAttrs = get(mark.value);
346
363
  if (markAttrs.length) {
347
364
  if (target && shape instanceof Elt)
@@ -367,13 +384,15 @@ function applyDeco(shape, deco, tag) {
367
384
  return shape;
368
385
  }
369
386
  const baseTagShape = /*@__PURE__*/memo((tag) => {
370
- return addMarkAttributes(tag.is(Leaf.Text) ? Widget.editableText.of(tag.param)
371
- : tag.type.shape.create(tag.param), tag);
387
+ return tag.is(Leaf.Text) ? Widget.editableText.of(tag.param) : tag.type.shape.create(tag.param);
372
388
  });
373
- function renderMarks(marks, around) {
374
- let result = addMarkAttributes(Elt.create("span", Attributes.none, [around]), Leaf.text(around, marks));
389
+ function renderMarks(state, marks, around) {
390
+ let markShapes = state.facet(markShape);
391
+ let result = addMarkAttributes(markShapes, Elt.create("span", Attributes.none, [around]), Leaf.text(around, marks));
375
392
  for (let i = marks.length - 1; i >= 0; i--) {
376
- let mark = marks[i];
393
+ let mark = marks[i], shape = markShapes.get(mark.type);
394
+ if (shape)
395
+ mark = shape.of(mark.value);
377
396
  if (mark.type.element)
378
397
  result = renderMarkWrapper(mark).fill([result]);
379
398
  }
@@ -494,13 +513,10 @@ function compareDecoSet(setA, setB, cmp) {
494
513
  if (!setA.has(srcB))
495
514
  cmp(null, valB);
496
515
  }
497
- function compareGlobal(stateA, stateB, facet) {
498
- return stateA.facet(facet) != stateB.facet(facet);
499
- }
516
+ const globalDecoFacets = [tagShape, tagWidget, tagWrapper, tagAttribute, markShape];
500
517
  function findChangedRanges(prevState, prevDeco, state, deco, sections) {
501
518
  let result = [];
502
- let globalChange = compareGlobal(prevState, state, tagShape) || compareGlobal(prevState, state, tagWidget) ||
503
- compareGlobal(prevState, state, tagWrapper) || compareGlobal(prevState, state, tagAttribute);
519
+ let globalChange = globalDecoFacets.some(facet => prevState.facet(facet) !== state.facet(facet));
504
520
  let shapeChanges = [];
505
521
  for (let i = 0, posA = 0, posB = 0; i < sections.length;) {
506
522
  let len = sections[i++], ins = sections[i++];
@@ -652,24 +668,6 @@ class SpanIterator {
652
668
  return this;
653
669
  }
654
670
  }
655
- function nodeWrappers(schema, tag, active, atom) {
656
- let wrappers;
657
- for (let mark of tag.marks)
658
- if (mark.type.element)
659
- (wrappers || (wrappers = [])).push(mark);
660
- if (active.length) {
661
- for (let val of active) {
662
- if (val instanceof WrapperRangeDecoration && (tagScope(tag, atom) & val.scope) &&
663
- (!val.query || schema.matchNode(tag.type, val.query)))
664
- (wrappers || (wrappers = [])).push(val);
665
- }
666
- }
667
- if (!wrappers)
668
- return none;
669
- if (wrappers.length > 1)
670
- wrappers.sort((a, b) => (a.spanning == b.spanning ? 0 : a.spanning ? -1 : 1) || a.rank - b.rank);
671
- return wrappers;
672
- }
673
671
  function tagScope(tag, atom) {
674
672
  return 4 |
675
673
  (atom ? 1 | (tag.type.isInline ? 2 : 0) : 0);
@@ -690,6 +688,7 @@ class DecoIterator {
690
688
  globalWidgets;
691
689
  globalWrappers;
692
690
  globalAttrs;
691
+ markShapes;
693
692
  schema;
694
693
  pos;
695
694
  rangeCursor;
@@ -705,6 +704,7 @@ class DecoIterator {
705
704
  .some(w => (w.place == 1 || w.place == 3) && typeof w.widget == "function");
706
705
  this.globalWrappers = state.facet(tagWrapper);
707
706
  this.globalAttrs = state.facet(tagAttribute);
707
+ this.markShapes = state.facet(markShape);
708
708
  this.pos = state.doc.resolve(0);
709
709
  this.schema = state.schema;
710
710
  this.rangeCursor = RangeSet.cursor(state.facet(Decoration.Range.source).map(s => s(state)));
@@ -729,6 +729,26 @@ class DecoIterator {
729
729
  (tw.place == 3 || tw.place == 1) &&
730
730
  typeof tw.widget == "function");
731
731
  }
732
+ nodeWrappers(tag, active, atom) {
733
+ let wrappers;
734
+ for (let mark of tag.marks) {
735
+ let shape = this.markShapes.get(mark.type) || mark.type;
736
+ if (shape.element)
737
+ (wrappers || (wrappers = [])).push(shape == mark.type ? mark : shape.of(mark.value));
738
+ }
739
+ if (active.length) {
740
+ for (let val of active) {
741
+ if (val instanceof WrapperRangeDecoration && (tagScope(tag, atom) & val.scope) &&
742
+ (!val.query || this.schema.matchNode(tag.type, val.query)))
743
+ (wrappers || (wrappers = [])).push(val);
744
+ }
745
+ }
746
+ if (!wrappers)
747
+ return none;
748
+ if (wrappers.length > 1)
749
+ wrappers.sort((a, b) => (a.spanning == b.spanning ? 0 : a.spanning ? -1 : 1) || a.rank - b.rank);
750
+ return wrappers;
751
+ }
732
752
  walk(from, inclusiveStart, to, walker) {
733
753
  this.rangeCursor.goto(from);
734
754
  this.pointCursor.goto(from, inclusiveStart ? -1e9 : 1000000000);
@@ -753,7 +773,7 @@ class DecoIterator {
753
773
  shape = applyDeco(shape, deco, node.tag);
754
774
  if (shape.hasContent)
755
775
  throw new Error("Leaf nodes shapes shouldn't have a content hole");
756
- walker.node(node, shape, nodeWrappers(this.schema, node.tag, iter.active, true), undefined);
776
+ walker.node(node, shape, this.nodeWrappers(node.tag, iter.active, true), undefined);
757
777
  this.widgets(node.tag, 1, walker);
758
778
  },
759
779
  enterPlot: (node, pos) => {
@@ -766,7 +786,7 @@ class DecoIterator {
766
786
  if (pendingPos == pos)
767
787
  for (let deco of pendingDeco)
768
788
  shape = applyDeco(shape, deco, node.tag);
769
- let wrappers = nodeWrappers(this.schema, node.tag, iter.active, !shape.hasContent);
789
+ let wrappers = this.nodeWrappers(node.tag, iter.active, !shape.hasContent);
770
790
  let atom = !shape.hasContent;
771
791
  if (atom)
772
792
  walker.node(node, shape, wrappers, pos + node.length > to ? to - pos : undefined);
@@ -842,15 +862,10 @@ class DecoIterator {
842
862
  this.pos = pos;
843
863
  }
844
864
  tagShape(tag, active) {
845
- let shape;
846
- if (!tag.is(Leaf.Text))
847
- for (let src of this.tagShapes)
848
- if (src.type == tag.type) {
849
- shape = src.shape(tag);
850
- break;
851
- }
852
- if (!shape)
853
- shape = baseTagShape(tag);
865
+ let override = this.tagShapes.get(tag.type);
866
+ let shape = override ? override(tag) : baseTagShape(tag);
867
+ if (tag.marks.length)
868
+ shape = addMarkAttributes(this.markShapes, shape, tag);
854
869
  let add;
855
870
  for (let src of this.globalAttrs)
856
871
  if (tag.type == src.type) {
@@ -1423,6 +1438,13 @@ class CompositeTile extends Tile {
1423
1438
  return null;
1424
1439
  let { closest, rect } = result;
1425
1440
  let pos = this.posBeforeChild(closest, start);
1441
+ if (textblock && textblock.start == start && (x < rect.left || x > rect.right)) {
1442
+ let start = (x < rect.left) == textblock.ltr;
1443
+ let span = start ? textblock.order[0] : textblock.order[textblock.order.length - 1];
1444
+ if (span.ltr != textblock.ltr && pos >= span.from + textblock.start && pos <= span.to + textblock.start)
1445
+ return start ? CoordPos.create(textblock.start, -1)
1446
+ : CoordPos.create(textblock.start + textblock.node.contentLength, 1);
1447
+ }
1426
1448
  if (closest.dom.nodeName == "BR")
1427
1449
  return CoordPos.create(pos, 1);
1428
1450
  if (closest.tag && closest.tag.isPlot && closest.tag.type.isInline) {
@@ -1949,7 +1971,7 @@ class TilePointer {
1949
1971
  }
1950
1972
  tileAfter() {
1951
1973
  let { tile, index } = this;
1952
- if (tile.isText)
1974
+ if (tile.isAtom)
1953
1975
  return tile;
1954
1976
  return index < tile.children.length ? tile.children[index] : null;
1955
1977
  }
@@ -1975,25 +1997,26 @@ class TilePointer {
1975
1997
  }
1976
1998
  matchingWidget(widget, sideFlag, reused) {
1977
1999
  let { index, tile, parent } = this;
1978
- for (;;) {
1979
- if (!index) {
1980
- if (!parent || (tile instanceof EltTile ? tile.tag : !(tile instanceof TextTile)))
1981
- break;
1982
- ({ index, tile, parent } = parent);
1983
- }
1984
- else {
1985
- if (tile instanceof TextTile)
1986
- break;
1987
- let before = tile.children[--index];
1988
- if (!before.isPoint)
1989
- break;
1990
- if (!reused.has(before) && before instanceof WidgetTile && before.widget.eq(widget) &&
1991
- (before.flags & 96) == sideFlag && !(before.flags & 8192)) {
1992
- reused.set(before, 1);
1993
- return before;
2000
+ if (!tile.isAtom)
2001
+ for (;;) {
2002
+ if (!index) {
2003
+ if (!parent || (tile instanceof EltTile ? tile.tag : !(tile instanceof TextTile)))
2004
+ break;
2005
+ ({ index, tile, parent } = parent);
2006
+ }
2007
+ else {
2008
+ if (tile instanceof TextTile)
2009
+ break;
2010
+ let before = tile.children[--index];
2011
+ if (!before.isPoint)
2012
+ break;
2013
+ if (!reused.has(before) && before instanceof WidgetTile && before.widget.eq(widget) &&
2014
+ (before.flags & 96) == sideFlag && !(before.flags & 8192)) {
2015
+ reused.set(before, 1);
2016
+ return before;
2017
+ }
1994
2018
  }
1995
2019
  }
1996
- }
1997
2020
  return null;
1998
2021
  }
1999
2022
  }
@@ -2093,7 +2116,7 @@ class ContentUpdate {
2093
2116
  let cut = [], end = this.posB + len;
2094
2117
  if (this.deco.endWidgets)
2095
2118
  for (let nw = this.new, { tile, index, parent } = this.old, pos = this.posB; pos < end;) {
2096
- if (tile instanceof TextTile) {
2119
+ if (tile.isAtom) {
2097
2120
  pos += tile.length - index;
2098
2121
  ({ tile, index, parent } = parent);
2099
2122
  index++;
@@ -2543,6 +2566,16 @@ class Coords {
2543
2566
  }
2544
2567
  }
2545
2568
  function coordsAtPos(wg, pos, assoc) {
2569
+ let textblock = wg.state.doc.resolve(pos).textblockParent;
2570
+ let map = textblock ? wg.state.textblockMap(textblock) : null;
2571
+ if (textblock && textblock.node.contentLength &&
2572
+ (pos == textblock.start && assoc < 0 || pos == textblock.end && assoc > 0)) {
2573
+ let span = map.order[pos == textblock.start ? 0 : map.order.length - 1];
2574
+ if (span.ltr != map.ltr)
2575
+ pos = textblock.start + (pos == textblock.start ? span.to : span.from);
2576
+ else
2577
+ assoc = -assoc;
2578
+ }
2546
2579
  let { offset, tile, pos: tilePos } = wg.docTile.resolve(pos, assoc);
2547
2580
  if (tile instanceof TextTile) {
2548
2581
  let from = offset, to = offset;
@@ -2551,7 +2584,7 @@ function coordsAtPos(wg, pos, assoc) {
2551
2584
  to++;
2552
2585
  else
2553
2586
  from--;
2554
- return new Coords(tile, flattenV(singleRect(textRange(tile.dom, from, to), side, true), (side < 0) == ltrAt(wg.state, pos, assoc)));
2587
+ return new Coords(tile, flattenV(singleRect(textRange(tile.dom, from, to), side, true), (side < 0) == ltrAt(wg.state, pos, assoc, map)));
2555
2588
  }
2556
2589
  let tagTile = tile;
2557
2590
  while (!tagTile.tag)
@@ -2562,7 +2595,7 @@ function coordsAtPos(wg, pos, assoc) {
2562
2595
  if (tile.widget.type.inFlow) {
2563
2596
  let rect = singleRect(tile.dom, after ? 1 : -1);
2564
2597
  if (rect.width || rect.height)
2565
- return new Coords(tile, horizontal ? flattenH(rect, !after) : flattenV(rect, ltrAt(wg.state, pos, 1) == !after));
2598
+ return new Coords(tile, horizontal ? flattenH(rect, !after) : flattenV(rect, ltrAt(wg.state, pos, 1, map) == !after));
2566
2599
  }
2567
2600
  if (!tile.parent)
2568
2601
  return new Coords(tile, new DOMRect);
@@ -2579,7 +2612,7 @@ function coordsAtPos(wg, pos, assoc) {
2579
2612
  break;
2580
2613
  let rect = singleRect(before.dom, 1);
2581
2614
  if (rect.width || rect.height)
2582
- return new Coords(before, horizontal ? flattenH(rect, false) : flattenV(rect, !ltrAt(wg.state, pos, 1)));
2615
+ return new Coords(before, horizontal ? flattenH(rect, false) : flattenV(rect, !ltrAt(wg.state, pos, 1, map)));
2583
2616
  }
2584
2617
  }
2585
2618
  else { for (let i = offset; i < tile.children.length; i++) {
@@ -2588,7 +2621,7 @@ function coordsAtPos(wg, pos, assoc) {
2588
2621
  continue;
2589
2622
  let rect = singleRect(after.dom, -1);
2590
2623
  if (rect.width || rect.height)
2591
- return new Coords(after, horizontal ? flattenH(rect, true) : flattenV(rect, ltrAt(wg.state, pos, 1)));
2624
+ return new Coords(after, horizontal ? flattenH(rect, true) : flattenV(rect, ltrAt(wg.state, pos, 1, map)));
2592
2625
  }
2593
2626
  }
2594
2627
  }
@@ -2632,19 +2665,18 @@ const clipboardInputFilter = /*@__PURE__*/GardState.Facet.define();
2632
2665
  const clipboardInputHTMLFilter = /*@__PURE__*/GardState.Facet.define();
2633
2666
  const clipboardTextParser = /*@__PURE__*/GardState.Facet.define();
2634
2667
  const clipboardInputTextFilter = /*@__PURE__*/GardState.Facet.define();
2635
- function writeClipboard(state, slice, context, data) {
2668
+ function writeClipboard(state, slice, data) {
2636
2669
  for (let filter of state.facet(clipboardOutputFilter))
2637
2670
  slice = filter(slice, state);
2638
2671
  let includeContext = 0;
2639
- for (let i = 0; i < context.length; i++) {
2640
- let next = context[i];
2641
- if (next.type.defining && (!includeContext || next.type != context[includeContext - 1].type))
2672
+ for (let i = 0; i < slice.context.length; i++) {
2673
+ let next = slice.context[i];
2674
+ if (next.type.defining && (!includeContext || next.type != slice.context[includeContext - 1].type))
2642
2675
  includeContext = i + 1;
2643
2676
  else if (next.type.defining || !next.isTextblock)
2644
2677
  break;
2645
2678
  }
2646
2679
  let doc = detachedDoc(), dom = serialize.slice(slice, {
2647
- context,
2648
2680
  includeContext,
2649
2681
  openAttr: "wg-open"
2650
2682
  }).toDOM();
@@ -2668,7 +2700,7 @@ function writeClipboard(state, slice, context, data) {
2668
2700
  data.setData("text/html", html);
2669
2701
  let text;
2670
2702
  for (let serialize of state.facet(clipboardTextSerializer)) {
2671
- if ((text = serialize(slice, context, state)) != null)
2703
+ if ((text = serialize(slice, state)) != null)
2672
2704
  break;
2673
2705
  }
2674
2706
  if (text == null)
@@ -2683,7 +2715,7 @@ function isOpen(elt) {
2683
2715
  function readClipboard(state, data, targetContext, plain) {
2684
2716
  let html = data.getData("text/html");
2685
2717
  let text = data.getData("text/plain") || data.getData("Text") || data.getData("text/uri-list").replace(/\r?\n/g, " ");
2686
- let slice, context = [];
2718
+ let slice;
2687
2719
  if (text && (targetContext.parent.node.type.hasRole(Node.Role.Code) || !html || plain)) {
2688
2720
  for (let filter of state.facet(clipboardInputTextFilter))
2689
2721
  text = filter(text, state);
@@ -2699,14 +2731,14 @@ function readClipboard(state, data, targetContext, plain) {
2699
2731
  if (browser.webkit)
2700
2732
  restoreReplacedSpaces(dom);
2701
2733
  let fromWordgard = dom.querySelector("[wg-content=true]");
2702
- ({ slice, context } = parse.slice(state.schema, dom, {
2734
+ slice = parse.slice(state.schema, dom, {
2703
2735
  collapseWhiteSpace: !fromWordgard,
2704
2736
  isOpen: fromWordgard ? isOpen : undefined
2705
- }));
2737
+ });
2706
2738
  }
2707
2739
  for (let filter of state.facet(clipboardInputFilter))
2708
2740
  slice = filter(slice, state);
2709
- return { slice, context };
2741
+ return slice;
2710
2742
  }
2711
2743
  function readClipboardText(state, text, context, plain) {
2712
2744
  if (!plain)
@@ -2839,7 +2871,6 @@ const baseStyles = /*@__PURE__*/buildTheme("." + styleID, {
2839
2871
  whiteSpace: "pre-wrap",
2840
2872
  overflowWrap: "anywhere",
2841
2873
  wordBreak: "break-word", boxSizing: "border-box",
2842
- minHeight: "100%",
2843
2874
  padding: "4px 12px",
2844
2875
  outline: "none",
2845
2876
  caretColor: "transparent",
@@ -2924,14 +2955,26 @@ const baseStyles = /*@__PURE__*/buildTheme("." + styleID, {
2924
2955
  },
2925
2956
  ".wg-panels-top": { top: "0" },
2926
2957
  ".wg-panels-bottom": { bottom: "0" },
2927
- "wg-dialog": {
2928
- display: "block",
2929
- padding: "5px 19px 5px 6px",
2930
- position: "relative",
2931
- "& label, & .wg-label": {
2932
- fontSize: "90%"
2933
- },
2934
- borderBottom: "1px solid var(--wg-border-color)"
2958
+ dialog: {
2959
+ font: "var(--wg-dialog-font)",
2960
+ padding: "7px 22px 7px 8px",
2961
+ minWidth: "120px",
2962
+ border: "1px solid var(--wg-border-color)",
2963
+ borderRadius: "5px",
2964
+ boxShadow: "0 0 8px 0 rgba(128, 128, 128, 0.2)",
2965
+ inset: "unset",
2966
+ "&.wg-free-dialog": {
2967
+ position: "fixed"
2968
+ }
2969
+ },
2970
+ "h3.wg-dialog-title": {
2971
+ fontSize: "90%",
2972
+ fontWeight: "bold",
2973
+ textAlign: "center",
2974
+ margin: "0 0 0.8em"
2975
+ },
2976
+ "dialog label, dialog .wg-label": {
2977
+ fontSize: "90%"
2935
2978
  },
2936
2979
  ".wg-dialog-close": {
2937
2980
  position: "absolute",
@@ -2961,6 +3004,24 @@ const baseStyles = /*@__PURE__*/buildTheme("." + styleID, {
2961
3004
  backgroundColor: "#222"
2962
3005
  }
2963
3006
  },
3007
+ ".wg-key-help": {
3008
+ padding: "15px 4px 4px",
3009
+ maxWidth: "350px"
3010
+ },
3011
+ ".wg-key-list": {
3012
+ padding: "11px",
3013
+ outline: "none",
3014
+ maxHeight: "calc(min(70vh, 300px))",
3015
+ overflowY: "auto",
3016
+ display: "grid",
3017
+ gridTemplateColumns: "max-content auto",
3018
+ gap: "8px 15px",
3019
+ "& h4": {
3020
+ gridColumn: "span 2",
3021
+ margin: "0.5em 0",
3022
+ fontSize: "110%"
3023
+ }
3024
+ }
2964
3025
  }, lightDarkIDs);
2965
3026
 
2966
3027
  function setDOMSelection(wg) {
@@ -3013,7 +3074,7 @@ function rangeForClick(wg, pos, type) {
3013
3074
  }
3014
3075
  else { let cx = wg.state.doc.resolve(pos.pos), block = cx.textblockParent;
3015
3076
  if (block)
3016
- return GardSelection.range(block.start, block.end);
3077
+ return GardSelection.undirectionalRange(block.start, block.end);
3017
3078
  else
3018
3079
  return GardSelection.near(wg.state, pos.pos, pos.side || -1);
3019
3080
  }
@@ -3126,12 +3187,23 @@ function moveToLineBoundary(wg, start, forward) {
3126
3187
  let startCoords = wg.coordsAtPos(start.head, start.headSide);
3127
3188
  let ltr = wg.state.textblockLTR(block.node);
3128
3189
  let y = (startCoords.top + startCoords.bottom) / 2, left = forward != ltr;
3129
- let { pos } = wg.posAtCoords({ x: left ? -1e7 : 1e7, y });
3190
+ let { pos, side } = wg.posAtCoords({ x: left ? -1e7 : 1e7, y });
3130
3191
  if (pos < block.start || pos > block.end) {
3131
3192
  let blockRect = wg.docTile.nodeTile(block.before).dom.getBoundingClientRect();
3132
- pos = wg.posAtCoords({ x: left ? blockRect.left : blockRect.right, y }).pos;
3193
+ let result = wg.posAtCoords({ x: left ? blockRect.left + 0.2 : blockRect.right - 0.2, y });
3194
+ if (result.pos < block.start) {
3195
+ pos = block.start;
3196
+ side = -1;
3197
+ }
3198
+ else if (result.pos > block.end) {
3199
+ pos = block.end;
3200
+ side = 1;
3201
+ }
3202
+ else {
3203
+ ({ pos, side } = result);
3204
+ }
3133
3205
  }
3134
- return GardSelection.cursor(pos, forward ? -1 : 1);
3206
+ return GardSelection.cursor(pos, side);
3135
3207
  }
3136
3208
 
3137
3209
  const observeOptions = {
@@ -3269,7 +3341,7 @@ class DOMObserver {
3269
3341
  if (selRange)
3270
3342
  range = buildSelectionRangeFromRange(wg, selRange);
3271
3343
  }
3272
- if (!range || this.selectionRange.eq(range))
3344
+ if (!range || !range.focusNode || this.selectionRange.eq(range))
3273
3345
  return false;
3274
3346
  let context = range.anchorNode && wg.docTile.nearest(range.anchorNode);
3275
3347
  if (context instanceof WidgetTile)
@@ -3281,10 +3353,6 @@ class DOMObserver {
3281
3353
  this.selectionRange.set(anchor.dom, anchor.offset, head.dom, head.offset);
3282
3354
  this.selectionChanged = false;
3283
3355
  }
3284
- clearSelectionRange() {
3285
- this.selectionRange.set(null, 0, null, 0);
3286
- this.selectionChanged = false;
3287
- }
3288
3356
  ignore(f) {
3289
3357
  let result = f();
3290
3358
  this.clear();
@@ -3378,18 +3446,46 @@ class KeyBinding {
3378
3446
  static of(spec) { return new KeyBinding(spec); }
3379
3447
  }
3380
3448
  ;KeyBinding = /*@__PURE__*/(function (KeyBinding) {
3381
- function runScopeHandlers(wg, event, scope) {
3382
- let map = getKeymap(wg.state.facet(KeyBinding.source), wg.state.facet(KeyBinding.useDefaultKeymap));
3383
- return runHandlers(map, event, wg, scope);
3384
- }
3385
- KeyBinding.runScopeHandlers = runScopeHandlers;
3386
- KeyBinding.source = GardState.Facet.define();
3449
+ function handle(wg, event) {
3450
+ let map = getKeymap(wg.state);
3451
+ return runHandlers(map, event, wg);
3452
+ }
3453
+ KeyBinding.handle = handle;
3454
+ KeyBinding.keymap = GardState.Field.Derived.define(s => {
3455
+ let bindings = s.facet(KeyBinding.source);
3456
+ return s.facet(KeyBinding.useDefaultKeymap) ? bindings.concat(KeyBinding.defaultKeymap) : bindings;
3457
+ });
3458
+ KeyBinding.source = GardState.Facet.define({
3459
+ enables: KeyBinding.keymap
3460
+ });
3387
3461
  KeyBinding.useDefaultKeymap = GardState.Facet.define({
3388
3462
  combine: input => input.length ? input[0] : true
3389
3463
  });
3464
+ class Group {
3465
+ description;
3466
+ rank;
3467
+ constructor(
3468
+ description,
3469
+ rank) {
3470
+ this.description = description;
3471
+ this.rank = rank;
3472
+ }
3473
+ static define(spec) {
3474
+ return new Group(spec.description, spec.rank || 1000);
3475
+ }
3476
+ static Inline = new Group(phrases.ref("group_inline"), 20);
3477
+ static Block = new Group(phrases.ref("group_block"), 30);
3478
+ static History = new Group(phrases.ref("group_history"), 70);
3479
+ }
3480
+ KeyBinding.Group = Group;
3481
+ function lookupByDescription(state, desc) {
3482
+ return getDescMap(state).get(desc) || null;
3483
+ }
3484
+ KeyBinding.lookupByDescription = lookupByDescription;
3390
3485
  KeyBinding.defaultKeymap = [
3391
3486
  { key: "Enter", run: enter },
3392
- { key: "Shift-Enter", run: insertLineBreak },
3487
+ { key: "Shift-Enter", run: insertLineBreak,
3488
+ description: phrases.ref("insert_break"), group: Group.Inline },
3393
3489
  { key: "Backspace", run: Command.bind(deleteUnit, "backward") },
3394
3490
  { key: "Delete", run: Command.bind(deleteUnit, "forward") },
3395
3491
  { key: "Ctrl-Backspace", mac: "Alt-Backspace", run: Command.bind(deleteWord, "backward") },
@@ -3433,9 +3529,9 @@ class KeyBinding {
3433
3529
  { key: "Mod-End", run: Command.bind(moveToDocSide, { side: "end" }),
3434
3530
  shift: Command.bind(moveToDocSide, { side: "end", extend: true }) },
3435
3531
  { key: "Mod-a", run: selectAll },
3436
- { key: "Mod-z", run: undo },
3437
- { key: "Mod-y", mac: "Mod-Shift-z", run: redo },
3438
- { linux: "Ctrl-Shift-z", run: redo },
3532
+ { key: "Mod-z", run: undo, description: phrases.ref("undo"), group: Group.History },
3533
+ { key: "Mod-y", mac: "Mod-Shift-z", run: redo, description: phrases.ref("redo"), group: Group.History },
3534
+ { linux: "Ctrl-Shift-z", run: redo, description: phrases.ref("redo"), group: Group.History },
3439
3535
  { mac: "Ctrl-b", run: Command.bind(moveByUnit, { dir: "backward" }),
3440
3536
  shift: Command.bind(moveByUnit, { dir: "backward", extend: true }) },
3441
3537
  { mac: "Ctrl-f", run: Command.bind(moveByUnit, { dir: "forward" }),
@@ -3469,15 +3565,15 @@ function normalizeKeyName(name, platform) {
3469
3565
  let alt, ctrl, shift, meta;
3470
3566
  for (let i = 0; i < parts.length - 1; ++i) {
3471
3567
  const mod = parts[i];
3472
- if (/^(cmd|meta|m)$/i.test(mod))
3568
+ if (mod == "Cmd" || mod == "Meta")
3473
3569
  meta = true;
3474
- else if (/^a(lt)?$/i.test(mod))
3570
+ else if (mod == "Alt")
3475
3571
  alt = true;
3476
- else if (/^(c|ctrl|control)$/i.test(mod))
3572
+ else if (mod == "Ctrl")
3477
3573
  ctrl = true;
3478
- else if (/^s(hift)?$/i.test(mod))
3574
+ else if (mod == "Shift")
3479
3575
  shift = true;
3480
- else if (/^mod$/i.test(mod)) {
3576
+ else if (mod == "Mod") {
3481
3577
  if (platform == "mac")
3482
3578
  meta = true;
3483
3579
  else
@@ -3491,18 +3587,18 @@ function normalizeKeyName(name, platform) {
3491
3587
  if (ctrl)
3492
3588
  result = "Ctrl-" + result;
3493
3589
  if (meta)
3494
- result = "Meta-" + result;
3590
+ result = (platform == "mac" ? "Cmd-" : "Meta-") + result;
3495
3591
  if (shift)
3496
3592
  result = "Shift-" + result;
3497
3593
  return result;
3498
3594
  }
3499
- function modifiers(name, event) {
3595
+ function modifiers(name, event, platform) {
3500
3596
  if (event.altKey)
3501
3597
  name = "Alt-" + name;
3502
3598
  if (event.ctrlKey)
3503
3599
  name = "Ctrl-" + name;
3504
3600
  if (event.metaKey)
3505
- name = "Meta-" + name;
3601
+ name = (platform == "mac" ? "Cmd-" : "Meta-") + name;
3506
3602
  if (event.shiftKey)
3507
3603
  name = "Shift-" + name;
3508
3604
  return name;
@@ -3518,56 +3614,65 @@ class NormalizedBinding {
3518
3614
  }
3519
3615
  }
3520
3616
  const keymapCache = /*@__PURE__*/(() => new WeakMap())();
3521
- function getKeymap(bindings, addDefault) {
3617
+ function getKeymap(state) {
3618
+ let bindings = state.field(KeyBinding.keymap);
3522
3619
  let found = keymapCache.get(bindings);
3523
- if (!found || found.deflt != addDefault) {
3524
- found = {
3525
- map: buildKeymap(addDefault ? bindings.concat(KeyBinding.defaultKeymap) : bindings, currentPlatform),
3526
- deflt: addDefault
3527
- };
3528
- keymapCache.set(bindings, found);
3620
+ if (!found)
3621
+ keymapCache.set(bindings, found = buildKeymap(bindings, currentPlatform));
3622
+ return found;
3623
+ }
3624
+ const lookupCache = /*@__PURE__*/(() => new WeakMap())();
3625
+ function getDescMap(state) {
3626
+ let bindings = state.field(KeyBinding.keymap);
3627
+ let map = lookupCache.get(bindings);
3628
+ if (!map) {
3629
+ lookupCache.set(bindings, map = new Map);
3630
+ for (let binding of bindings) {
3631
+ let { description, shiftDescription } = binding.spec;
3632
+ if (description || shiftDescription) {
3633
+ let name = binding.spec[currentPlatform] || binding.spec.key;
3634
+ if (name && description)
3635
+ map.set(description, normalizeKeyName(name, currentPlatform));
3636
+ if (name && shiftDescription)
3637
+ map.set(shiftDescription, "Shift-" + normalizeKeyName(name, currentPlatform));
3638
+ }
3639
+ }
3529
3640
  }
3530
- return found.map;
3641
+ return map;
3531
3642
  }
3532
3643
  function bind(run) {
3533
3644
  return (wg) => Command.dispatch(wg, run);
3534
3645
  }
3535
3646
  function buildKeymap(bindings, platform) {
3536
- let scopes = Object.create(null);
3647
+ let map = [];
3537
3648
  for (let { spec: b } of bindings) {
3538
3649
  let baseFlags = (b.allowDefault ? 8 : 0);
3539
- for (let scope of b.scope ? b.scope.split(" ") : ["editor"]) {
3540
- let array = scopes[scope] || (scopes[scope] = []);
3541
- let key = b[platform] || b.key;
3542
- if (b.char) {
3543
- if (key)
3544
- throw new Error("A key binding may not provide both a char and a key field");
3545
- if (b.shift)
3546
- throw new Error("Shift-modified bindings are not supported for char bindings");
3547
- array.push(new NormalizedBinding(baseFlags | 1, b.char, bind(b.run)));
3548
- }
3650
+ let key = b[platform] || b.key;
3651
+ if (b.char) {
3549
3652
  if (key)
3550
- array.push(new NormalizedBinding(baseFlags | 2, normalizeKeyName(key, platform), bind(b.run)));
3551
- if (key && b.shift)
3552
- array.push(new NormalizedBinding(baseFlags | 2, normalizeKeyName("Shift-" + key, platform), bind(b.shift)));
3553
- if (b.any)
3554
- array.push(new NormalizedBinding(4, "", b.any));
3653
+ throw new Error("A key binding may not provide both a char and a key field");
3654
+ if (b.shift)
3655
+ throw new Error("Shift-modified bindings are not supported for char bindings");
3656
+ map.push(new NormalizedBinding(baseFlags | 1, b.char, bind(b.run)));
3555
3657
  }
3658
+ if (key)
3659
+ map.push(new NormalizedBinding(baseFlags | 2, normalizeKeyName(key, platform), bind(b.run)));
3660
+ if (key && b.shift)
3661
+ map.push(new NormalizedBinding(baseFlags | 2, normalizeKeyName("Shift-" + key, platform), bind(b.shift)));
3662
+ if (b.any)
3663
+ map.push(new NormalizedBinding(4, "", b.any));
3556
3664
  }
3557
- return scopes;
3665
+ return map;
3558
3666
  }
3559
- function runHandlers(map, event, wg, scope) {
3560
- let handlers = map[scope];
3561
- if (!handlers)
3562
- return false;
3667
+ function runHandlers(map, event, wg) {
3563
3668
  let key = event.key, charCode = key.codePointAt(0);
3564
3669
  let altGr = event.getModifierState("AltGraph"), fromCode = charKeyCodes[event.keyCode];
3565
3670
  let isChar = codePointSize(charCode) == key.length;
3566
3671
  let char = isChar ? String.fromCodePoint(charCode) : null;
3567
- let base = modifiers(key, event);
3568
- let fallback = isChar && !altGr && fromCode && fromCode != base ? modifiers(fromCode, event) : null;
3672
+ let base = modifiers(key, event, currentPlatform);
3673
+ let fallback = isChar && !altGr && fromCode && fromCode != base ? modifiers(fromCode, event, currentPlatform) : null;
3569
3674
  let handled = false, didMatch = false, allowDefault = false;
3570
- for (let binding of handlers) {
3675
+ for (let binding of map) {
3571
3676
  let matched = ((binding.flags & 1) && (altGr || !event.ctrlKey && !event.metaKey) && binding.name == char) ||
3572
3677
  ((binding.flags & 2) && (binding.name == base || binding.name == fallback)) ||
3573
3678
  (binding.flags & 4);
@@ -3614,6 +3719,11 @@ const eventObserver = /*@__PURE__*/GardState.Facet.define({
3614
3719
  return result;
3615
3720
  }
3616
3721
  });
3722
+ const compositionInputTypes = /*@__PURE__*/(() => new Set([
3723
+ "insertCompositionText",
3724
+ "deleteCompositionText",
3725
+ "insertFromComposition"
3726
+ ]))();
3617
3727
  class InputState {
3618
3728
  wg;
3619
3729
  shiftKey = false;
@@ -3772,11 +3882,12 @@ class InputState {
3772
3882
  }
3773
3883
  }
3774
3884
  let command = inputTypeCommands[type];
3775
- if ((type == "deleteContentBackward" || type == "deleteContentForward") && range &&
3776
- range.from != range.to && !this.unflushedSelection && (sel.empty
3777
- ? !isSingleChar(this.domDoc, data.domRange.from, data.domRange.to) ||
3778
- sel.head != (type == "deleteContentBackward" ? range.to : range.from)
3779
- : sel.from != range.from || sel.to != range.to)) {
3885
+ if (isDeletionInputEvent(type) && range &&
3886
+ (!command ||
3887
+ (range.from != range.to && !this.unflushedSelection && (sel.empty
3888
+ ? !isSingleChar(this.domDoc, data.domRange.from, data.domRange.to) ||
3889
+ sel.head != (type == "deleteContentBackward" ? range.to : range.from)
3890
+ : sel.from != range.from || sel.to != range.to)))) {
3780
3891
  wg.dispatch({ changes: { from: range.from, to: range.to, fit: true }, userEvent: "delete" });
3781
3892
  }
3782
3893
  else if (command) {
@@ -3787,13 +3898,28 @@ class InputState {
3787
3898
  let { from, to } = this.unflushedSelection ? wg.state.selection : range;
3788
3899
  Command.dispatch(wg, insertText, { from, to, insert, userEvent: "input.type" });
3789
3900
  }
3901
+ else if (type == "insertFromPaste") {
3902
+ let { from, to } = this.unflushedSelection ? wg.state.selection.replacementRange : range;
3903
+ let slice = readClipboard(wg.state, event.dataTransfer, wg.state.doc.resolve(from), wg.inputState.shiftKey);
3904
+ let isSel = from == wg.state.selection.replacementRange.from && to == wg.state.selection.replacementRange.to;
3905
+ if (isSel && wg.state.facet(pasteHandler).some(h => h(wg, event, slice || Slice.empty)))
3906
+ return;
3907
+ if (slice) {
3908
+ wg.dispatch({
3909
+ changes: { from, to, insert: slice, fit: true },
3910
+ selection: isSel ? (cx, changes) => GardSelection.near(cx, changes.mapPos(to, 1), -1) : undefined,
3911
+ userEvent: "input.paste",
3912
+ scrollIntoView: isSel
3913
+ });
3914
+ }
3915
+ }
3790
3916
  else if ((type == "insertReplacementText" || type == "insertFromYank")) {
3791
3917
  let read = readClipboard(wg.state, event.dataTransfer, wg.state.sel.head, true);
3792
3918
  let { from, to } = this.unflushedSelection ? wg.state.selection : range;
3793
3919
  let sel = wg.state.selection, touchesSel = from <= sel.to && to >= sel.from;
3794
3920
  if (read)
3795
3921
  wg.dispatch({
3796
- changes: { from, to, insert: read.slice, fit: read.context },
3922
+ changes: { from, to, insert: read, fit: true },
3797
3923
  selection: touchesSel ? (cx, changes) => {
3798
3924
  return GardSelection.near(cx, changes.mapPos(to, 1), -1);
3799
3925
  } : undefined,
@@ -3801,7 +3927,7 @@ class InputState {
3801
3927
  userEvent: "insert.replacementText"
3802
3928
  });
3803
3929
  }
3804
- else if (type == "insertCompositionText") {
3930
+ else if (compositionInputTypes.has(type)) {
3805
3931
  let compositionStart = true;
3806
3932
  if (wg.inputState.composing) {
3807
3933
  compositionStart = !wg.inputState.composing.changes;
@@ -3811,7 +3937,7 @@ class InputState {
3811
3937
  if (!sel.focusNode)
3812
3938
  return false;
3813
3939
  let userEvent = "input.type.compose" + (compositionStart ? ".start" : "");
3814
- Command.dispatch(wg, insertText, { from: range.from, to: range.to, insert: event.data, userEvent });
3940
+ Command.dispatch(wg, insertText, { from: range.from, to: range.to, insert: event.data || "", userEvent });
3815
3941
  }
3816
3942
  else if (type == "formatSetBlockTextDirection") {
3817
3943
  if (event.data == "ltr" || event.data == "rtl")
@@ -3890,6 +4016,7 @@ class InputState {
3890
4016
  clearComposition() {
3891
4017
  let comp = this.composing;
3892
4018
  this.composing = null;
4019
+ this.compositionPendingKey = true;
3893
4020
  this.compositionEndedAt = Date.now();
3894
4021
  if (!comp.target)
3895
4022
  return false;
@@ -4131,32 +4258,38 @@ function basicMouseSelection(wg, event) {
4131
4258
  get(event, extend) {
4132
4259
  let cur = queryPos(wg, event), range = rangeForClick(wg, cur, type), { from, to } = range;
4133
4260
  if (extend) {
4261
+ let startAnchor = !(startSel instanceof GardSelection.Text) || startSel.directional ? startSel.anchor
4262
+ : from < startSel.from ? startSel.to : startSel.from;
4134
4263
  if (from < startSel.anchor)
4135
- return GardSelection.range(startSel.anchor, from, from < to ? 1 : cur.side);
4264
+ return GardSelection.range(startAnchor, from, from < to ? 1 : cur.side);
4136
4265
  else
4137
- return GardSelection.range(startSel.anchor, to, from < to ? -1 : cur.side);
4266
+ return GardSelection.range(startAnchor, to, from < to ? -1 : cur.side);
4138
4267
  }
4268
+ let { anchor, head } = range;
4139
4269
  if (start.pos != cur.pos) {
4140
4270
  let startRange = rangeForClick(wg, start, type);
4141
- from = Math.min(startRange.from, from);
4142
- to = Math.max(startRange.to, to);
4271
+ let from = Math.min(range.from, startRange.from), to = Math.max(range.to, startRange.to);
4272
+ if (startRange.from < range.from) {
4273
+ anchor = from;
4274
+ head = to;
4275
+ }
4276
+ else {
4277
+ anchor = to;
4278
+ head = from;
4279
+ }
4143
4280
  }
4144
- return from == range.from && to == range.to ? range : GardSelection.range(from, to, cur.side);
4281
+ return anchor == range.anchor && head == range.head ? range
4282
+ : GardSelection.range(anchor, head, head == cur.pos ? cur.side : undefined);
4145
4283
  }
4146
4284
  };
4147
4285
  }
4148
4286
  const dropHandler = /*@__PURE__*/GardState.Facet.define();
4149
4287
  const pasteHandler = /*@__PURE__*/GardState.Facet.define();
4150
- function selectionSlice(state) {
4151
- return {
4152
- slice: state.doc.slice(state.selection.from, state.selection.to),
4153
- context: state.doc.contextAt(state.selection.from)
4154
- };
4155
- }
4156
4288
  function copy(wg, event) {
4157
4289
  let { state } = wg;
4158
- if (!state.selection.empty && event.clipboardData) { let { slice, context } = selectionSlice(state);
4159
- writeClipboard(state, slice, context, event.clipboardData);
4290
+ if (!state.selection.empty && event.clipboardData) {
4291
+ let slice = state.selection.toSlice(state.doc);
4292
+ writeClipboard(state, slice, event.clipboardData);
4160
4293
  if (event.type == "cut" && !state.readOnly)
4161
4294
  wg.dispatch({
4162
4295
  changes: state.selection.ranges.map(r => ({ from: r.from, to: r.to, fit: true })),
@@ -4216,7 +4349,7 @@ function inEditableDOM(wg, node) {
4216
4349
  let tile = wg.docTile.nearest(node);
4217
4350
  return tile ? !(tile.isPoint || tile instanceof WidgetTile) : false;
4218
4351
  }
4219
- function isDeletionInputEvent(type) { return /^delete(Content|Word)/.test(type); }
4352
+ function isDeletionInputEvent(type) { return /^delete(Content|Word|ByComposition)/.test(type); }
4220
4353
  const inputTypeCommands = /*@__PURE__*/(() => ({
4221
4354
  historyUndo: undo,
4222
4355
  historyRedo: redo,
@@ -4249,7 +4382,7 @@ const baseHandlers = {
4249
4382
  keydown(wg, event) {
4250
4383
  if ((browser.ios || browser.android) && (event.key == "Backspace" || event.key == "Enter"))
4251
4384
  return false;
4252
- return KeyBinding.runScopeHandlers(wg, event, "editor");
4385
+ return KeyBinding.handle(wg, event);
4253
4386
  },
4254
4387
  mousedown(wg, event) {
4255
4388
  wg.inputState.shiftKey = event.shiftKey;
@@ -4288,8 +4421,8 @@ const baseHandlers = {
4288
4421
  inputState.mouseSelection.dragging = true;
4289
4422
  inputState.draggedContent = selection;
4290
4423
  if (event.dataTransfer) {
4291
- let { slice, context } = selectionSlice(wg.state);
4292
- writeClipboard(wg.state, slice, context, event.dataTransfer);
4424
+ let slice = wg.state.selection.toSlice(wg.state.doc);
4425
+ writeClipboard(wg.state, slice, event.dataTransfer);
4293
4426
  event.dataTransfer.effectAllowed = "copyMove";
4294
4427
  }
4295
4428
  return false;
@@ -4303,52 +4436,31 @@ const baseHandlers = {
4303
4436
  drop(wg, event) {
4304
4437
  if (!event.dataTransfer || wg.state.readOnly)
4305
4438
  return true;
4306
- let content = readClipboard(wg.state, event.dataTransfer, wg.state.sel.head, false);
4307
- if (!content)
4439
+ let slice = readClipboard(wg.state, event.dataTransfer, wg.state.sel.head, false);
4440
+ if (!slice)
4308
4441
  return false;
4309
4442
  let dropPos = wg.posAtCoords({ x: event.clientX, y: event.clientY }).pos;
4310
4443
  let { draggedContent } = wg.inputState;
4311
4444
  let del = draggedContent && dragMovesSelection(wg, event)
4312
4445
  ? { from: draggedContent.from, to: draggedContent.to } : null;
4313
- if (wg.state.facet(dropHandler).some(f => f(wg, event, dropPos, del, content.slice, content.context)))
4446
+ if (wg.state.facet(dropHandler).some(f => f(wg, event, dropPos, del, slice)))
4314
4447
  return true;
4315
- let ins = { from: dropPos, insert: content.slice, fit: content.context };
4448
+ let ins = { from: dropPos, insert: slice, fit: true };
4316
4449
  let changes = ChangeSet.create(wg.state.doc, del ? [del, ins] : ins);
4317
4450
  wg.focus();
4318
4451
  wg.dispatch({
4319
4452
  changes,
4320
- selection: GardSelection.range(changes.mapPos(dropPos, -1), changes.mapPos(dropPos, 1)),
4453
+ selection: GardSelection.undirectionalRange(changes.mapPos(dropPos, -1), changes.mapPos(dropPos, 1)),
4321
4454
  userEvent: del ? "move.drop" : "input.drop"
4322
4455
  });
4323
4456
  wg.inputState.draggedContent = null;
4324
4457
  return true;
4325
4458
  },
4326
- paste(wg, event) {
4327
- if (wg.state.readOnly || !event.clipboardData)
4328
- return true;
4329
- let { state } = wg;
4330
- let content = readClipboard(state, event.clipboardData, state.sel.head, wg.inputState.shiftKey);
4331
- if (wg.state.facet(pasteHandler).some(h => h(wg, event, content ? content.slice : Slice.empty, content ? content.context : [])))
4332
- return true;
4333
- if (content) { wg.dispatch({
4334
- changes: {
4335
- from: state.selection.from,
4336
- to: state.selection.to,
4337
- insert: content.slice,
4338
- fit: content.context
4339
- },
4340
- selection: (cx, changes) => GardSelection.near(cx, changes.mapPos(state.selection.to, 1), -1),
4341
- userEvent: "input.paste",
4342
- scrollIntoView: true
4343
- });
4344
- }
4345
- return true;
4346
- },
4347
4459
  beforeinput(wg, event) {
4348
4460
  let type = event.inputType;
4349
4461
  if (browser.safari && type == "insertText" && wg.inputState.composing)
4350
4462
  compositionEnd(wg);
4351
- if (type == "insertCompositionText" && !wg.inputState.composing)
4463
+ if (compositionInputTypes.has(type) && !wg.inputState.composing)
4352
4464
  wg.inputState.composing = { changes: 0, target: null };
4353
4465
  let data = {
4354
4466
  inputType: type,
@@ -4364,7 +4476,7 @@ const baseHandlers = {
4364
4476
  }
4365
4477
  wg.inputState.beforeInput(event, wg.inputState.pendingInputEvent = data);
4366
4478
  wg.scheduleFlush();
4367
- let allow = type == "insertCompositionText" ||
4479
+ let allow = compositionInputTypes.has(type) ||
4368
4480
  editable && (type == "insertText" || isDeletionInputEvent(type) &&
4369
4481
  data.domRange && inlineContext(wg.inputState.domDoc, data.domRange));
4370
4482
  return !allow;
@@ -4375,8 +4487,8 @@ const baseHandlers = {
4375
4487
  return false;
4376
4488
  wg.inputState.pendingInputEvent = null;
4377
4489
  let change;
4378
- if (event.inputType == "insertCompositionText" || event.inputType == "insertText") {
4379
- change = { from: pending.domRange.from, to: pending.domRange.to, insert: [Leaf.text(pending.data)] };
4490
+ if (compositionInputTypes.has(event.inputType) || event.inputType == "insertText") {
4491
+ change = { from: pending.domRange.from, to: pending.domRange.to, insert: [Leaf.text(pending.data || "")] };
4380
4492
  }
4381
4493
  else if (isDeletionInputEvent(event.inputType)) {
4382
4494
  change = { from: pending.domRange.from, to: pending.domRange.to };
@@ -4403,7 +4515,6 @@ const baseObservers = {
4403
4515
  updateForFocusChange(wg);
4404
4516
  },
4405
4517
  blur(wg) {
4406
- wg.observer.clearSelectionRange();
4407
4518
  updateForFocusChange(wg);
4408
4519
  },
4409
4520
  compositionstart: compositionUpdate,
@@ -4487,15 +4598,18 @@ class ViewState {
4487
4598
  measure(wg) {
4488
4599
  let dom = wg.contentDOM, style = window.getComputedStyle(dom);
4489
4600
  this.styleLTR = style.direction == "ltr";
4490
- let domRect = dom.getBoundingClientRect();
4491
- this.contentDOMHeight = domRect.height;
4492
4601
  let result = 0;
4602
+ let domRect = dom.getBoundingClientRect();
4603
+ if (Math.abs(this.contentDOMHeight - domRect.height) > 0.5) {
4604
+ result |= 2;
4605
+ this.contentDOMHeight = domRect.height;
4606
+ }
4493
4607
  if (this.editorWidth != wg.scrollDOM.clientWidth) {
4494
4608
  this.editorWidth = wg.scrollDOM.clientWidth;
4495
4609
  result |= 2;
4496
4610
  }
4497
4611
  let contentWidth = domRect.width;
4498
- if (this.contentDOMWidth != contentWidth || this.editorHeight != wg.scrollDOM.clientHeight ||
4612
+ if (Math.abs(this.contentDOMWidth - contentWidth) > 0.5 || this.editorHeight != wg.scrollDOM.clientHeight ||
4499
4613
  this.editorOffset != wg.scrollDOM.offsetTop) {
4500
4614
  this.contentDOMWidth = domRect.width;
4501
4615
  this.editorHeight = wg.scrollDOM.clientHeight;
@@ -4684,7 +4798,7 @@ function getCursorInfo(wg, plugin, cont) {
4684
4798
  return finish(null);
4685
4799
  let target = sel.head.parent.node.isDoc ? wg.contentDOM : wg.nodeDOM(sel.head.parent.before);
4686
4800
  wg.scheduleDOMWrite(() => {
4687
- let temp = renderMarks(marks, "M");
4801
+ let temp = renderMarks(wg.state, marks, "M");
4688
4802
  temp.style.position = "absolute";
4689
4803
  wg.observer.ignore(() => target.insertBefore(temp, target.firstChild));
4690
4804
  wg.scheduleDOMRead(() => {
@@ -5398,844 +5512,719 @@ class PluginInstance {
5398
5512
  }
5399
5513
  }
5400
5514
 
5401
- function offset(add, offset) {
5402
- return (v, from, to) => add(v, from + offset, to == null ? undefined : to + offset);
5403
- }
5404
- function init(type, create, source, doc) {
5405
- return create(add => {
5406
- doc.iterate((node, pos) => {
5407
- if (node.isPlot && doc.schema.matchNode(node.type, type))
5408
- source(node, pos + 1, add);
5409
- });
5410
- });
5411
- }
5412
- function refreshByPred(type, source, doc, deco, pred) {
5413
- let recreate = [];
5414
- doc.iterate((node, pos) => {
5415
- if (doc.schema.matchNode(node.type, type) && pred(node))
5416
- recreate.push({
5417
- from: pos, to: pos + node.length,
5418
- add: add => source(node, pos + 1, add)
5419
- });
5420
- });
5421
- return recreate.length ? deco.modify({ replace: recreate }) : deco;
5422
- }
5423
- function update(config, create, source, deco, tr) {
5424
- let doc = tr.newDoc;
5425
- let refresh = config.refresh && config.refresh(tr);
5426
- if (refresh === true)
5427
- return init(config.type, create, source, doc);
5428
- if (refresh)
5429
- deco = refreshByPred(config.type, source, tr.startState.doc, deco, refresh);
5430
- if (!tr.docChanged && !refresh)
5431
- return deco;
5432
- let recreate = [];
5433
- let clear = [];
5434
- tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
5435
- let covered = false;
5436
- doc.iterate(fromB, toB, (node, pos) => {
5437
- if (doc.schema.matchNode(node.type, config.type)) {
5438
- recreate.push({
5439
- from: pos, to: pos + node.length,
5440
- add: add => source(node, pos + 1, add)
5441
- });
5442
- if (pos < toB && pos + node.length > toB)
5443
- covered = true;
5444
- return false;
5515
+ const Outside = "-10000px";
5516
+ class TooltipViewManager {
5517
+ facet;
5518
+ createTooltipView;
5519
+ removeTooltipView;
5520
+ input;
5521
+ tooltips;
5522
+ tooltipViews;
5523
+ constructor(wg, facet, createTooltipView, removeTooltipView) {
5524
+ this.facet = facet;
5525
+ this.createTooltipView = createTooltipView;
5526
+ this.removeTooltipView = removeTooltipView;
5527
+ this.input = wg.state.facet(facet);
5528
+ this.tooltips = this.input.filter(t => t);
5529
+ let prev = null;
5530
+ this.tooltipViews = this.tooltips.map(t => prev = createTooltipView(t, prev));
5531
+ }
5532
+ update(update, above) {
5533
+ let input = update.state.facet(this.facet);
5534
+ let tooltips = input.filter(x => x);
5535
+ if (input === this.input) {
5536
+ for (let t of this.tooltipViews)
5537
+ if (t.update)
5538
+ t.update(update);
5539
+ return false;
5540
+ }
5541
+ let tooltipViews = [], newAbove = above ? [] : null;
5542
+ for (let i = 0; i < tooltips.length; i++) {
5543
+ let tip = tooltips[i], known = -1;
5544
+ if (!tip)
5545
+ continue;
5546
+ for (let i = 0; i < this.tooltips.length; i++) {
5547
+ let other = this.tooltips[i];
5548
+ if (other && other.create == tip.create)
5549
+ known = i;
5550
+ }
5551
+ if (known < 0) {
5552
+ tooltipViews[i] = this.createTooltipView(tip, i ? tooltipViews[i - 1] : null);
5553
+ if (newAbove)
5554
+ newAbove[i] = !!tip.above;
5555
+ }
5556
+ else {
5557
+ let tooltipView = tooltipViews[i] = this.tooltipViews[known];
5558
+ if (newAbove)
5559
+ newAbove[i] = above[known];
5560
+ if (tooltipView.update)
5561
+ tooltipView.update(update);
5445
5562
  }
5446
- });
5447
- if (!covered) {
5448
- let end = tr.startState.doc.resolve(toA);
5449
- let open = end.matchingParent(p => doc.schema.matchNode(p.type, config.type));
5450
- if (open && open.before >= fromA)
5451
- clear.push({ from: open.before, to: open.after });
5452
5563
  }
5453
- });
5454
- if (clear.length)
5455
- deco = deco.modify({ replace: clear });
5456
- deco = deco.map(tr.changes, recreate);
5457
- return deco;
5458
- }
5459
- function decoratePlots(config) {
5460
- let result = [];
5461
- let { type, points, ranges } = config;
5462
- if (points) {
5463
- let source = (plot, start, add) => points(plot, offset(add, start));
5464
- let field = GardState.Field.define({
5465
- create(state) { return init(type, PointSet.create, source, state.doc); },
5466
- update(value, tr) { return update(config, PointSet.create, source, value, tr); }
5467
- });
5468
- result.push(field, Decoration.Point.source.of(s => s.field(field)));
5469
- }
5470
- if (ranges) {
5471
- let source = (plot, start, add) => ranges(plot, offset(add, start));
5472
- let field = GardState.Field.define({
5473
- create(state) { return init(type, RangeSet.create, source, state.doc); },
5474
- update(value, tr) { return update(config, RangeSet.create, source, value, tr); }
5475
- });
5476
- result.push(field, Decoration.Range.source.of(s => s.field(field)));
5477
- }
5478
- return result;
5479
- }
5480
-
5481
- const panelConfig = /*@__PURE__*/GardState.Facet.define({
5482
- combine(configs) {
5483
- let topContainer, bottomContainer;
5484
- for (let c of configs) {
5485
- topContainer ||= c.topContainer;
5486
- bottomContainer ||= c.bottomContainer;
5564
+ for (let t of this.tooltipViews)
5565
+ if (tooltipViews.indexOf(t) < 0) {
5566
+ this.removeTooltipView(t);
5567
+ if (update.editor.connected)
5568
+ t.disconnect?.(update.editor);
5569
+ t.remove?.(update.editor);
5570
+ }
5571
+ if (above) {
5572
+ newAbove.forEach((val, i) => above[i] = val);
5573
+ above.length = newAbove.length;
5487
5574
  }
5488
- return { topContainer, bottomContainer };
5575
+ this.input = input;
5576
+ this.tooltips = tooltips;
5577
+ this.tooltipViews = tooltipViews;
5578
+ return true;
5489
5579
  }
5580
+ }
5581
+ const tooltipConfig = /*@__PURE__*/GardState.Facet.define({
5582
+ combine: values => ({
5583
+ position: browser.ios ? "absolute" : values.find(conf => conf.position)?.position || "fixed",
5584
+ parent: values.find(conf => conf.parent)?.parent || null,
5585
+ tooltipSpace: values.find(conf => conf.tooltipSpace)?.tooltipSpace || (wg => windowRect(wg.win)),
5586
+ })
5490
5587
  });
5491
- const panelPlugin = /*@__PURE__*/Wordgard.Plugin.fromClass(class {
5492
- input;
5493
- specs;
5494
- panels;
5495
- top;
5496
- bottom;
5497
- constructor(wg) {
5498
- this.input = wg.state.facet(Panel.show);
5499
- this.specs = this.input.filter(s => s);
5500
- this.panels = this.specs.map(spec => spec(wg));
5501
- for (let p of this.panels)
5502
- p.dom.classList.add("wg-panel");
5503
- let conf = wg.state.facet(panelConfig);
5504
- this.top = new PanelGroup(wg, true, conf.topContainer);
5505
- this.bottom = new PanelGroup(wg, false, conf.bottomContainer);
5506
- this.top.sync(this.panels.filter(p => p.top), wg);
5507
- this.bottom.sync(this.panels.filter(p => !p.top), wg);
5508
- }
5588
+ const knownHeight = /*@__PURE__*/(() => new WeakMap())();
5589
+ const tooltipPlugin = /*@__PURE__*/Wordgard.Plugin.fromClass(class {
5590
+ wg;
5591
+ manager;
5592
+ above = [];
5593
+ inView = true;
5594
+ position;
5595
+ madeAbsolute = false;
5596
+ parent;
5597
+ classes;
5598
+ intersectionObserver;
5599
+ resizeObserver;
5600
+ lastTransaction = 0;
5601
+ measureTimeout = -1;
5602
+ constructor(wg) {
5603
+ this.wg = wg;
5604
+ let config = wg.state.facet(tooltipConfig);
5605
+ this.position = config.position;
5606
+ this.parent = config.parent;
5607
+ this.classes = wg.themeClasses;
5608
+ this.createContainer();
5609
+ this.measure = this.measure.bind(this);
5610
+ this.resizeObserver = typeof ResizeObserver == "function" ? new ResizeObserver(() => this.measureSoon()) : null;
5611
+ this.manager = new TooltipViewManager(wg, Tooltip.show, (t, p) => this.createTooltip(t, p), t => {
5612
+ if (this.resizeObserver)
5613
+ this.resizeObserver.unobserve(t.dom);
5614
+ t.dom.remove();
5615
+ });
5616
+ this.above = this.manager.tooltips.map(t => !!t.above);
5617
+ this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => {
5618
+ if (Date.now() > this.lastTransaction - 50 &&
5619
+ entries.length > 0 && entries[entries.length - 1].intersectionRatio < 1)
5620
+ this.measureSoon();
5621
+ }, { threshold: [1] }) : null;
5622
+ this.observeIntersection();
5623
+ this.maybeMeasure();
5624
+ }
5625
+ createContainer() {
5626
+ if (this.parent) {
5627
+ this.container = document.createElement("wg-tooltip-root");
5628
+ this.container.style.position = "relative";
5629
+ this.container.className = this.wg.themeClasses;
5630
+ this.parent.appendChild(this.container);
5631
+ }
5632
+ else {
5633
+ this.container = this.wg.dom;
5634
+ }
5635
+ }
5636
+ observeIntersection() {
5637
+ if (this.intersectionObserver && this.wg.connected) {
5638
+ this.intersectionObserver.disconnect();
5639
+ for (let tooltip of this.manager.tooltipViews)
5640
+ this.intersectionObserver.observe(tooltip.dom);
5641
+ }
5642
+ }
5643
+ measureSoon() {
5644
+ if (this.measureTimeout < 0)
5645
+ this.measureTimeout = setTimeout(() => {
5646
+ this.measureTimeout = -1;
5647
+ this.maybeMeasure();
5648
+ }, 50);
5649
+ }
5509
5650
  update(update) {
5510
- let conf = update.state.facet(panelConfig);
5511
- if (this.top.container != conf.topContainer) {
5512
- this.top.sync([], update.editor);
5513
- this.top = new PanelGroup(update.editor, true, conf.topContainer);
5651
+ if (update.transactions.length)
5652
+ this.lastTransaction = Date.now();
5653
+ let updated = this.manager.update(update, this.above);
5654
+ if (updated)
5655
+ this.observeIntersection();
5656
+ let shouldMeasure = updated || update.geometryChanged;
5657
+ let newConfig = update.state.facet(tooltipConfig);
5658
+ if (newConfig.position != this.position && !this.madeAbsolute) {
5659
+ this.position = newConfig.position;
5660
+ for (let t of this.manager.tooltipViews)
5661
+ t.dom.style.position = this.position;
5662
+ shouldMeasure = true;
5514
5663
  }
5515
- if (this.bottom.container != conf.bottomContainer) {
5516
- this.bottom.sync([], update.editor);
5517
- this.bottom = new PanelGroup(update.editor, false, conf.bottomContainer);
5664
+ if (newConfig.parent != this.parent) {
5665
+ if (this.parent)
5666
+ this.container.remove();
5667
+ this.parent = newConfig.parent;
5668
+ this.createContainer();
5669
+ for (let t of this.manager.tooltipViews)
5670
+ this.container.appendChild(t.dom);
5671
+ shouldMeasure = true;
5518
5672
  }
5519
- this.top.syncClasses();
5520
- this.bottom.syncClasses();
5521
- let input = update.state.facet(Panel.show);
5522
- if (input != this.input) {
5523
- let specs = input.filter(x => x);
5524
- let panels = [], top = [], bottom = [], mount = [];
5525
- for (let spec of specs) {
5526
- let known = this.specs.indexOf(spec), panel;
5527
- if (known < 0) {
5528
- panel = spec(update.editor);
5529
- mount.push(panel);
5530
- }
5531
- else {
5532
- panel = this.panels[known];
5533
- if (panel.update)
5534
- panel.update(update);
5535
- }
5536
- panels.push(panel);
5537
- (panel.top ? top : bottom).push(panel);
5538
- }
5539
- this.specs = specs;
5540
- this.panels = panels;
5541
- this.top.sync(top, update.editor);
5542
- this.bottom.sync(bottom, update.editor);
5543
- for (let p of mount) {
5544
- p.dom.classList.add("wg-panel");
5545
- if (p.connect && update.editor.connected)
5546
- p.connect(update.editor);
5547
- }
5673
+ else if (this.parent && this.wg.themeClasses != this.classes) {
5674
+ this.classes = this.container.className = this.wg.themeClasses;
5548
5675
  }
5549
- else {
5550
- for (let p of this.panels)
5551
- if (p.update)
5552
- p.update(update);
5676
+ if (shouldMeasure)
5677
+ this.maybeMeasure();
5678
+ }
5679
+ createTooltip(tooltip, prev) {
5680
+ let tooltipView = tooltip.create(this.wg);
5681
+ let before = prev ? prev.dom : null;
5682
+ tooltipView.dom.classList.add("wg-tooltip");
5683
+ if (tooltip.arrow && !tooltipView.dom.querySelector(".wg-tooltip > wg-tooltip-arrow")) {
5684
+ let arrow = document.createElement("wg-tooltip-arrow");
5685
+ tooltipView.dom.appendChild(arrow);
5553
5686
  }
5687
+ tooltipView.dom.style.position = this.position;
5688
+ tooltipView.dom.style.top = Outside;
5689
+ tooltipView.dom.style.left = "0px";
5690
+ this.container.insertBefore(tooltipView.dom, before);
5691
+ if (this.wg.connected)
5692
+ tooltipView.connect?.(this.wg);
5693
+ if (this.resizeObserver && this.wg.connected)
5694
+ this.resizeObserver.observe(tooltipView.dom);
5695
+ return tooltipView;
5554
5696
  }
5555
5697
  connect(wg) {
5556
- for (let p of this.panels)
5557
- p.connect?.(wg);
5698
+ wg.win.addEventListener("resize", this.measureSoon = this.measureSoon.bind(this));
5699
+ for (let t of this.manager.tooltipViews) {
5700
+ t.connect?.(wg);
5701
+ if (this.resizeObserver)
5702
+ this.resizeObserver.observe(t.dom);
5703
+ }
5704
+ this.observeIntersection();
5558
5705
  }
5559
5706
  disconnect(wg) {
5560
- for (let p of this.panels)
5561
- p.disconnect?.(wg);
5562
- }
5563
- remove(wg) {
5564
- this.top.sync([], wg);
5565
- this.bottom.sync([], wg);
5566
- }
5567
- }, plugin => Wordgard.coveredMargins.of(wg => {
5568
- let value = wg.plugin(plugin);
5569
- return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
5570
- }));
5571
- const Panel = /*@__PURE__*/(function (Panel) {
5572
- Panel.show = GardState.Facet.define({
5573
- enables: panelPlugin
5574
- });
5575
- function get(wg, constructor) {
5576
- let plugin = wg.plugin(panelPlugin);
5577
- let index = plugin ? plugin.specs.indexOf(constructor) : -1;
5578
- return index > -1 ? plugin.panels[index] : null;
5707
+ this.wg.win.removeEventListener("resize", this.measureSoon);
5708
+ for (let t of this.manager.tooltipViews) {
5709
+ t.disconnect?.(wg);
5710
+ if (this.resizeObserver)
5711
+ this.resizeObserver.unobserve(t.dom);
5712
+ }
5713
+ if (this.intersectionObserver)
5714
+ this.intersectionObserver.disconnect();
5579
5715
  }
5580
- Panel.get = get;
5581
- function configure(config) {
5582
- return config ? [panelConfig.of(config)] : [];
5716
+ remove() {
5717
+ for (let tooltipView of this.manager.tooltipViews) {
5718
+ tooltipView.dom.remove();
5719
+ if (this.wg.connected)
5720
+ tooltipView.disconnect?.(this.wg);
5721
+ tooltipView.remove?.(this.wg);
5722
+ }
5723
+ if (this.parent)
5724
+ this.container.remove();
5725
+ clearTimeout(this.measureTimeout);
5583
5726
  }
5584
- Panel.configure = configure;
5585
- ;return Panel})({});
5586
- class PanelGroup {
5587
- wg;
5588
- top;
5589
- container;
5590
- dom = undefined;
5591
- classes = "";
5592
- panels = [];
5593
- constructor(wg, top, container) {
5594
- this.wg = wg;
5595
- this.top = top;
5596
- this.container = container;
5597
- this.syncClasses();
5727
+ measure() {
5728
+ let measure = this.readMeasure();
5729
+ this.wg.scheduleDOMWrite(() => this.writeMeasure(measure));
5598
5730
  }
5599
- sync(panels, wg) {
5600
- for (let p of this.panels)
5601
- if (!panels.includes(p)) {
5602
- if (wg.connected)
5603
- p.disconnect?.(wg);
5604
- p.remove?.(wg);
5731
+ readMeasure() {
5732
+ let scaleX = 1, scaleY = 1, makeAbsolute = false;
5733
+ if (this.position == "fixed" && this.manager.tooltipViews.length) {
5734
+ let { dom } = this.manager.tooltipViews[0];
5735
+ if (browser.safari) {
5736
+ let rect = dom.getBoundingClientRect();
5737
+ makeAbsolute = Math.abs(rect.top + 10000) > 1 || Math.abs(rect.left) > 1;
5605
5738
  }
5606
- this.panels = panels;
5607
- this.syncDOM();
5608
- }
5609
- syncDOM() {
5610
- if (this.panels.length == 0) {
5611
- if (this.dom) {
5612
- this.dom.remove();
5613
- this.dom = undefined;
5739
+ else {
5740
+ makeAbsolute = !!dom.offsetParent && dom.offsetParent != this.container.ownerDocument.body;
5614
5741
  }
5615
- return;
5616
- }
5617
- if (!this.dom) {
5618
- this.dom = document.createElement("wg-panels");
5619
- this.dom.className = this.top ? "wg-panels-top" : "wg-panels-bottom";
5620
- let parent = this.container || this.wg.dom;
5621
- parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
5622
5742
  }
5623
- let curDOM = this.dom.firstChild;
5624
- for (let panel of this.panels) {
5625
- if (panel.dom.parentNode == this.dom) {
5626
- while (curDOM != panel.dom)
5627
- curDOM = rmDOM(curDOM);
5628
- curDOM = curDOM.nextSibling;
5629
- }
5630
- else {
5631
- this.dom.insertBefore(panel.dom, curDOM);
5743
+ if (makeAbsolute || this.position == "absolute") {
5744
+ let measure = this.parent || this.container, rect = measure.getBoundingClientRect();
5745
+ if (rect.width && rect.height) {
5746
+ scaleX = rect.width / measure.offsetWidth;
5747
+ scaleY = rect.height / measure.offsetHeight;
5632
5748
  }
5633
5749
  }
5634
- while (curDOM)
5635
- curDOM = rmDOM(curDOM);
5636
- }
5637
- scrollMargin() {
5638
- return !this.dom || this.container ? 0
5639
- : Math.max(0, this.top ?
5640
- this.dom.getBoundingClientRect().bottom - Math.max(0, this.wg.scrollDOM.getBoundingClientRect().top) :
5641
- Math.min(innerHeight, this.wg.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
5642
- }
5643
- syncClasses() {
5644
- if (!this.container || this.classes == this.wg.themeClasses)
5645
- return;
5646
- for (let cls of this.classes.split(" "))
5647
- if (cls)
5648
- this.container.classList.remove(cls);
5649
- for (let cls of (this.classes = this.wg.themeClasses).split(" "))
5650
- if (cls)
5651
- this.container.classList.add(cls);
5652
- }
5653
- }
5654
-
5655
- let nextID = 0;
5656
- function id(prefix) {
5657
- return prefix + "-" + (nextID++ % 0xffffff).toString(16);
5658
- }
5659
- const SVG = "http://www.w3.org/2000/svg";
5660
- function labelButton(wg, button, label) {
5661
- button.textContent = "";
5662
- if (!label) ;
5663
- else if (typeof label == "function" || typeof label == "string") {
5664
- let span = button.appendChild(document.createElement("span"));
5665
- span.className = "wg-button-label";
5666
- span.textContent = typeof label == "string" ? label : label(wg.state);
5667
- }
5668
- else if ("icon" in label) {
5669
- let svg = button.appendChild(document.createElementNS(SVG, "svg"));
5670
- svg.classList.add("wg-icon");
5671
- svg.setAttribute("viewBox", "0 0 100 100");
5672
- let path = svg.appendChild(document.createElementNS(SVG, "path"));
5673
- path.setAttribute("d", label.icon);
5674
- if (label.directional && !wg.state.textLTR)
5675
- svg.setAttribute("transform", "scale(-1, 1)");
5750
+ let visible = this.wg.scrollDOM.getBoundingClientRect(), margins = this.wg.getScrollMargins();
5751
+ let visLeft = visible.left + margins.left, visTop = visible.top + margins.top;
5752
+ return {
5753
+ visible: new DOMRect(visLeft, visTop, visible.right - margins.right - visLeft, visible.bottom - margins.bottom - visTop),
5754
+ parent: this.parent ? this.container.getBoundingClientRect() : this.wg.dom.getBoundingClientRect(),
5755
+ pos: this.manager.tooltips.map((t, i) => {
5756
+ let tv = this.manager.tooltipViews[i];
5757
+ return tv.getCoords ? tv.getCoords(t.pos) : this.wg.coordsAtPos(t.pos);
5758
+ }),
5759
+ size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
5760
+ space: this.wg.state.facet(tooltipConfig).tooltipSpace(this.wg),
5761
+ scaleX, scaleY, makeAbsolute
5762
+ };
5676
5763
  }
5677
- }
5678
- class BarButton {
5679
- item;
5680
- dom;
5681
- flags = 0;
5682
- index = 0;
5683
- constructor(item, wg) {
5684
- this.item = item;
5685
- this.dom = document.createElement("button");
5686
- this.dom.className = "wg-menu-button";
5687
- this.dom.type = "button";
5688
- this.dom.tabIndex = -1;
5689
- labelButton(wg, this.dom, item.label);
5690
- if (item.description) {
5691
- let desc = typeof item.description == "function" ? item.description(wg.state) : item.description;
5692
- this.dom.title = desc;
5693
- this.dom.setAttribute("aria-label", desc);
5764
+ writeMeasure(measured) {
5765
+ if (measured.makeAbsolute) {
5766
+ this.madeAbsolute = true;
5767
+ this.position = "absolute";
5768
+ for (let t of this.manager.tooltipViews)
5769
+ t.dom.style.position = "absolute";
5694
5770
  }
5695
- }
5696
- get focusDOM() { return this.dom; }
5697
- update(flags, wg, update) {
5698
- if (flags != this.flags) {
5699
- if ((flags & 32) != (this.flags & 32))
5700
- this.dom.style.display = flags & 32 ? "none" : "";
5701
- if ((flags & 8) != (this.flags & 8)) {
5702
- if (flags & 8)
5703
- this.dom.setAttribute("aria-disabled", "true");
5704
- else
5705
- this.dom.removeAttribute("aria-disabled");
5771
+ let { visible, space, scaleX, scaleY } = measured;
5772
+ let others = [];
5773
+ for (let i = 0; i < this.manager.tooltips.length; i++) {
5774
+ let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
5775
+ let pos = measured.pos[i], size = measured.size[i];
5776
+ if (!pos || tooltip.clip !== false && (pos.bottom <= Math.max(visible.top, space.top) ||
5777
+ pos.top >= Math.min(visible.bottom, space.bottom) ||
5778
+ pos.right < Math.max(visible.left, space.left) - .1 ||
5779
+ pos.left > Math.min(visible.right, space.right) + .1)) {
5780
+ dom.style.top = Outside;
5781
+ continue;
5706
5782
  }
5707
- if ((flags & 4) != (this.flags & 4)) {
5708
- if (flags & 4)
5709
- this.dom.setAttribute("aria-selected", "true");
5710
- else
5711
- this.dom.removeAttribute("aria-selected");
5712
- this.dom.tabIndex = flags & 4 ? 0 : -1;
5783
+ let arrow = tooltip.arrow ? tView.dom.querySelector("wg-tooltip-arrow") : null;
5784
+ let arrowHeight = arrow ? 7 : 0;
5785
+ let width = size.right - size.left, height = knownHeight.get(tView) ?? size.bottom - size.top;
5786
+ let offset = tView.offset || noOffset, ltr = this.wg.state.textLTR;
5787
+ let left = size.width > space.right - space.left
5788
+ ? (ltr ? space.left : space.right - size.width)
5789
+ : ltr ? Math.max(space.left, Math.min(pos.left - (arrow ? 14 : 0) + offset.x, space.right - width))
5790
+ : Math.min(Math.max(space.left, pos.left - width + (arrow ? 14 : 0) - offset.x), space.right - width);
5791
+ let above = this.above[i];
5792
+ if (!tooltip.strictSide && (above
5793
+ ? pos.top - height - arrowHeight - offset.y < space.top
5794
+ : pos.bottom + height + arrowHeight + offset.y > space.bottom) &&
5795
+ above == (space.bottom - pos.bottom > pos.top - space.top))
5796
+ above = this.above[i] = !above;
5797
+ let spaceVert = (above ? pos.top - space.top : space.bottom - pos.bottom) - arrowHeight;
5798
+ if (spaceVert < height && tView.resize !== false) {
5799
+ if (spaceVert < 15) {
5800
+ dom.style.top = Outside;
5801
+ continue;
5802
+ }
5803
+ knownHeight.set(tView, height);
5804
+ dom.style.height = (height = spaceVert) / scaleY + "px";
5713
5805
  }
5714
- if ((flags & 16) != (this.flags & 16)) {
5715
- if (flags & 16)
5716
- this.dom.setAttribute("aria-pressed", "true");
5717
- else
5718
- this.dom.removeAttribute("aria-pressed");
5806
+ else if (dom.style.height) {
5807
+ dom.style.height = "";
5719
5808
  }
5720
- this.flags = flags;
5809
+ let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
5810
+ let right = left + width;
5811
+ if (tView.overlap !== true)
5812
+ for (let r of others)
5813
+ if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
5814
+ top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
5815
+ if (this.position == "absolute") {
5816
+ dom.style.top = (top - measured.parent.top) / scaleY + "px";
5817
+ setLeftStyle(dom, (left - measured.parent.left) / scaleX);
5818
+ }
5819
+ else {
5820
+ dom.style.top = top / scaleY + "px";
5821
+ setLeftStyle(dom, left / scaleX);
5822
+ }
5823
+ if (arrow) {
5824
+ let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 - 7);
5825
+ arrow.style.left = arrowLeft / scaleX + "px";
5826
+ }
5827
+ if (tView.overlap !== true)
5828
+ others.push({ left, top, right, bottom: top + height });
5829
+ dom.classList.toggle("wg-tooltip-above", above);
5830
+ dom.classList.toggle("wg-tooltip-below", !above);
5831
+ if (tView.positioned)
5832
+ tView.positioned(measured.space);
5721
5833
  }
5722
5834
  }
5723
- get children() { return null; }
5724
- run(wg) {
5725
- Command.dispatch(wg, this.item.run);
5835
+ maybeMeasure() {
5836
+ if (this.manager.tooltips.length)
5837
+ this.wg.scheduleDOMRead(this.measure);
5726
5838
  }
5839
+ }, plugin => plugin.eventObserver("scroll", (event, wg, value) => value.maybeMeasure()));
5840
+ function setLeftStyle(elt, value) {
5841
+ let current = parseInt(elt.style.left, 10);
5842
+ if (isNaN(current) || Math.abs(value - current) > 1)
5843
+ elt.style.left = value + "px";
5727
5844
  }
5728
- class BarControl {
5729
- item;
5730
- dom;
5731
- focusDOM;
5732
- flags = 0;
5733
- index = 0;
5734
- constructor(item, wg, done) {
5735
- this.item = item;
5736
- let { dom, focus } = item.render(wg, done);
5737
- this.dom = dom;
5738
- this.focusDOM = focus || dom;
5739
- this.focusDOM.tabIndex = -1;
5740
- }
5741
- update(flags, wg, update) {
5742
- if (flags != this.flags) {
5743
- if ((flags & 32) != (this.flags & 32))
5744
- this.dom.style.display = flags & 32 ? "none" : "";
5745
- if ((flags & 8) != (this.flags & 8) && this.item.setEnabled)
5746
- this.item.setEnabled(this.dom, !(flags & 8));
5747
- if ((flags & 4) != (this.flags & 4)) {
5748
- if (flags & 4)
5749
- this.focusDOM.setAttribute("aria-selected", "true");
5750
- else
5751
- this.focusDOM.removeAttribute("aria-selected");
5752
- this.focusDOM.tabIndex = flags & 4 ? 0 : -1;
5845
+ const styles = /*@__PURE__*/Wordgard.styles({
5846
+ ".wg-tooltip": {
5847
+ zIndex: 500,
5848
+ boxSizing: "border-box",
5849
+ backgroundColor: "var(--wg-panel-color)",
5850
+ boxShadow: "0 0 8px 0 rgba(128, 128, 128, 0.2)",
5851
+ font: "var(--wg-dialog-font)",
5852
+ },
5853
+ ".wg-tooltip-section:not(:first-child)": {
5854
+ borderTop: "1px solid var(--wg-border-color)",
5855
+ },
5856
+ "wg-tooltip-arrow": {
5857
+ display: "block",
5858
+ height: `${7}px`,
5859
+ width: `${7 * 2}px`,
5860
+ position: "absolute",
5861
+ zIndex: -1,
5862
+ overflow: "hidden",
5863
+ "&:before, &:after": {
5864
+ content: "''",
5865
+ position: "absolute",
5866
+ width: 0,
5867
+ height: 0,
5868
+ borderLeft: `${7}px solid transparent`,
5869
+ borderRight: `${7}px solid transparent`,
5870
+ },
5871
+ ".wg-tooltip-above &": {
5872
+ bottom: `-${7}px`,
5873
+ "&:before": {
5874
+ borderTop: `${7}px solid var(--wg-border-color)`,
5875
+ },
5876
+ "&:after": {
5877
+ borderTop: `${7}px solid var(--wg-panel-color)`,
5878
+ bottom: "1px"
5753
5879
  }
5754
- this.flags = flags;
5755
- }
5880
+ },
5881
+ ".wg-tooltip-below &": {
5882
+ top: `-${7}px`,
5883
+ "&:before": {
5884
+ borderBottom: `${7}px solid var(--wg-border-color)`,
5885
+ },
5886
+ "&:after": {
5887
+ borderBottom: `${7}px solid var(--wg-panel-color)`,
5888
+ top: "1px"
5889
+ }
5890
+ },
5891
+ },
5892
+ });
5893
+ const closeHoverTooltipEffect = /*@__PURE__*/Transaction.Effect.define();
5894
+ const Tooltip = /*@__PURE__*/(function (Tooltip) {
5895
+ function configure(config = {}) {
5896
+ return tooltipConfig.of(config);
5756
5897
  }
5757
- get children() { return null; }
5758
- get run() { return null; }
5759
- }
5760
- class BarSubmenu {
5761
- item;
5762
- dom;
5763
- button;
5764
- list;
5765
- flags = 0;
5766
- activeChild = -3;
5767
- index = 0;
5768
- children;
5769
- constructor(item, children, wg) {
5770
- this.item = item;
5771
- this.dom = document.createElement("wg-submenu");
5772
- this.button = this.dom.appendChild(document.createElement("button"));
5773
- this.button.type = "button";
5774
- this.button.tabIndex = -1;
5775
- this.button.className = "wg-menu-button";
5776
- this.button.setAttribute("aria-haspopup", "true");
5777
- this.button.setAttribute("aria-expanded", "false");
5778
- if (item.description) {
5779
- let desc = typeof item.description == "function" ? item.description(wg.state) : item.description;
5780
- this.dom.title = desc;
5781
- this.dom.setAttribute("aria-label", desc);
5782
- }
5783
- if (item.label) {
5784
- labelButton(wg, this.button, item.label);
5785
- this.activeChild = -2;
5786
- }
5787
- if (item.width != null)
5788
- this.dom.style.setProperty("--wg-submenu-width", item.width + "ch");
5789
- if (item.arrow)
5790
- this.button.classList.add("wg-submenu-arrow");
5791
- this.list = this.dom.appendChild(document.createElement("wg-menu-list"));
5792
- this.list.style.display = "none";
5793
- this.list.role = "menu";
5794
- this.list.id = id("wg-popup");
5795
- this.list.setAttribute("aria-label", this.button.title);
5796
- this.button.setAttribute("aria-controls", this.list.id);
5797
- this.children = children.filter((ch) => !(ch instanceof BarSpacer));
5798
- for (let child of children) {
5799
- this.list.appendChild(child.dom);
5800
- if (!(child instanceof BarSpacer))
5801
- child.focusDOM.role = "menuitem";
5802
- }
5898
+ Tooltip.configure = configure;
5899
+ Tooltip.show = GardState.Facet.define({
5900
+ enables: [tooltipPlugin, styles]
5901
+ });
5902
+ function get(wg, tooltip) {
5903
+ let plugin = wg.plugin(tooltipPlugin);
5904
+ if (!plugin)
5905
+ return null;
5906
+ let found = plugin.manager.tooltips.findIndex(typeof tooltip == "function" ? p => p.create == tooltip : p => p == tooltip);
5907
+ return found < 0 ? null : plugin.manager.tooltipViews[found];
5803
5908
  }
5804
- get focusDOM() { return this.button; }
5805
- update(flags, wg) {
5806
- if (flags != this.flags) {
5807
- if ((flags & 32) != (this.flags & 32))
5808
- this.dom.style.display = flags & 32 ? "none" : "";
5809
- if ((flags & 8) != (this.flags & 8)) {
5810
- if (flags & 8)
5811
- this.button.setAttribute("aria-disabled", "true");
5812
- else
5813
- this.button.removeAttribute("aria-disabled");
5814
- }
5815
- if ((flags & 4) != (this.flags & 4)) {
5816
- if (flags & 4)
5817
- this.button.setAttribute("aria-selected", "true");
5818
- else
5819
- this.button.removeAttribute("aria-selected");
5820
- this.button.tabIndex = flags & 4 ? 0 : -1;
5821
- }
5822
- if ((flags & 2) != (this.flags & 2))
5823
- this.list.style.display = flags & 2 ? "" : "none";
5824
- this.flags = flags;
5825
- }
5826
- if (this.activeChild != -2) {
5827
- let activeChild = this.children.findIndex(ch => ch.flags & 16);
5828
- if (this.activeChild != activeChild) {
5829
- this.activeChild = activeChild;
5830
- let label = activeChild < 0 ? this.item.defaultLabel : this.children[activeChild].item.label;
5831
- labelButton(wg, this.button, label);
5832
- }
5833
- }
5909
+ Tooltip.get = get;
5910
+ function reposition(wg) {
5911
+ let plugin = wg.plugin(tooltipPlugin);
5912
+ if (plugin)
5913
+ plugin.maybeMeasure();
5834
5914
  }
5835
- get run() { return null; }
5836
- }
5837
- class BarSpacer {
5838
- dom;
5839
- constructor() {
5840
- this.dom = document.createElement("wg-menu-spacer");
5915
+ Tooltip.reposition = reposition;
5916
+ function hover(source, options = {}) {
5917
+ let setHover = Transaction.Effect.define();
5918
+ let hoverState = GardState.Field.define({
5919
+ create() { return []; },
5920
+ update(value, tr) {
5921
+ if (value.length) {
5922
+ if (options.hideOnChange && (tr.docChanged || tr.selection))
5923
+ value = [];
5924
+ else if (options.hideOn)
5925
+ value = value.filter(v => !options.hideOn(tr, v));
5926
+ if (tr.docChanged) {
5927
+ let mapped = [];
5928
+ for (let tooltip of value) {
5929
+ let newPos = tr.changes.mapPos(tooltip.pos, -1, "around");
5930
+ if (newPos != null) {
5931
+ let copy = Object.assign(Object.create(null), tooltip);
5932
+ copy.pos = newPos;
5933
+ if (copy.end != null)
5934
+ copy.end = tr.changes.mapPos(copy.end);
5935
+ mapped.push(copy);
5936
+ }
5937
+ }
5938
+ value = mapped;
5939
+ }
5940
+ }
5941
+ for (let effect of tr.effects) {
5942
+ if (effect.is(setHover))
5943
+ value = effect.value;
5944
+ if (effect.is(closeHoverTooltipEffect))
5945
+ value = [];
5946
+ }
5947
+ return value;
5948
+ },
5949
+ provide: f => showHoverTooltip.from(f)
5950
+ });
5951
+ return {
5952
+ active: hoverState,
5953
+ extension: [
5954
+ hoverState,
5955
+ Wordgard.Plugin.define(wg => new HoverPlugin(wg, source, hoverState, setHover, options.hoverTime || 300)),
5956
+ showHoverTooltipHost
5957
+ ]
5958
+ };
5841
5959
  }
5842
- }
5843
- function instantiate(item, bar, flat) {
5844
- let elt;
5845
- if (item instanceof Menu.Submenu.Resolved)
5846
- elt = new BarSubmenu(item.item, item.content.map(i => instantiate(i, bar, flat)), bar.wg);
5847
- else if (item === "|")
5848
- return new BarSpacer();
5849
- else if (item instanceof Menu.Button)
5850
- elt = new BarButton(item, bar.wg);
5851
- else
5852
- elt = new BarControl(item, bar.wg, () => bar.up());
5853
- elt.index = flat.length;
5854
- flat.push(elt);
5855
- return elt;
5856
- }
5857
- class MenuBar {
5960
+ Tooltip.hover = hover;
5961
+ (function (hover) {
5962
+ function has(state) {
5963
+ return state.facet(showHoverTooltip).some(x => x);
5964
+ }
5965
+ hover.has = has;
5966
+ hover.closeAll = closeHoverTooltipEffect.of(null);
5967
+ })(hover = Tooltip.hover || (Tooltip.hover = {}));
5968
+ ;return Tooltip})({});
5969
+ const noOffset = { x: 0, y: 0 };
5970
+ const showHoverTooltip = /*@__PURE__*/GardState.Facet.define({
5971
+ combine: inputs => inputs.reduce((a, i) => a.concat(i), [])
5972
+ });
5973
+ class HoverTooltipHost {
5858
5974
  wg;
5975
+ manager;
5859
5976
  dom;
5860
- focusTimeout = -1;
5861
- items;
5977
+ connected = false;
5978
+ static create(wg) {
5979
+ return new HoverTooltipHost(wg);
5980
+ }
5862
5981
  constructor(wg) {
5863
5982
  this.wg = wg;
5864
- this.dom = document.createElement("wg-menubar");
5865
- this.dom.role = "toolbar";
5866
- this.dom.addEventListener("keydown", this.key.bind(this));
5867
- this.dom.addEventListener("mousedown", this.click.bind(this));
5868
- this.dom.addEventListener("focusout", this.focusout.bind(this));
5869
- this.items = wg.state.facet(Menu.Item.source);
5870
- this.init();
5871
- this.globalClick = this.globalClick.bind(this);
5983
+ this.dom = document.createElement("wg-tooltip-hover");
5984
+ this.manager = new TooltipViewManager(wg, showHoverTooltip, (t, p) => this.createHostedView(t, p), t => t.dom.remove());
5872
5985
  }
5873
- static create(wg) { return new MenuBar(wg); }
5874
- init() {
5875
- let elts = [];
5876
- this.elts = elts;
5877
- let children = Menu.resolve(this.items, this.wg.state.facet(barTemplate)).map(i => instantiate(i, this, elts));
5878
- this.children = children.filter((ch) => !(ch instanceof BarSpacer));
5879
- for (let elt of children)
5880
- this.dom.appendChild(elt.dom);
5881
- this.selection = this.children.length ? [this.children[0]] : [];
5882
- this.updateElts(true, this.selection);
5986
+ createHostedView(tooltip, prev) {
5987
+ let hostedView = tooltip.create(this.wg);
5988
+ hostedView.dom.classList.add("wg-tooltip-section");
5989
+ this.dom.insertBefore(hostedView.dom, prev ? prev.dom.nextSibling : this.dom.firstChild);
5990
+ if (this.connected && hostedView.connect)
5991
+ hostedView.connect(this.wg);
5992
+ return hostedView;
5883
5993
  }
5884
- update(update) {
5885
- let items = update.state.facet(Menu.Item.source);
5886
- if (items != this.items ||
5887
- update.startState.facet(barTemplate) != update.state.facet(barTemplate) ||
5888
- update.startState.textLTR != update.state.textLTR ||
5889
- PhraseSet.didChange(update.startState, update.state)) {
5890
- this.items = items;
5891
- this.dom.textContent = "";
5892
- this.init();
5893
- }
5894
- else {
5895
- this.updateElts(update, this.selection);
5896
- }
5994
+ connect(wg) {
5995
+ for (let t of this.manager.tooltipViews)
5996
+ t.connect?.(wg);
5997
+ this.connected = true;
5897
5998
  }
5898
- connect() {
5899
- this.dom.setAttribute("aria-controls", this.wg.contentDOM.id);
5999
+ disconnect(wg) {
6000
+ for (let t of this.manager.tooltipViews)
6001
+ t.disconnect?.(wg);
6002
+ this.connected = false;
5900
6003
  }
5901
- updateElts(update, selection) {
5902
- let { state } = this.wg;
5903
- let changed = typeof update == "boolean" ? update : update.docChanged || update.selectionSet;
5904
- let updateObj = typeof update == "boolean" ? null : update;
5905
- for (let i = 0; i < this.elts.length; i++) {
5906
- let elt = this.elts[i], flags;
5907
- if (update && (update === true || (elt.item.updateFor ? update.transactions.some(tr => elt.item.updateFor(tr)) : changed))) {
5908
- flags = ((elt.item.select ? elt.item.select(state) : true) ? 0 : 32) |
5909
- ((elt.item.enable ? elt.item.enable(state) : true) ? 0 : 8) |
5910
- ((elt.item instanceof Menu.Button && elt.item.active ? elt.item.active(state) : false) ? 16 : 0);
5911
- }
5912
- else {
5913
- flags = elt.flags & (32 | 8 | 16);
5914
- }
5915
- if (selection.length) {
5916
- let selected = selection.indexOf(elt);
5917
- if (selected == selection.length - 1)
5918
- flags |= 4;
5919
- else if (selected > -1)
5920
- flags |= 2;
5921
- }
5922
- elt.update(flags, this.wg, updateObj);
5923
- }
5924
- if (update && selection.some(e => e.flags & 32)) {
5925
- let reset = selection[0].flags & 32 ? findChild(this.children, true) : selection[0];
5926
- this.setSelection(reset ? [reset] : [], this.dom.contains(this.wg.root.activeElement));
6004
+ positioned(space) {
6005
+ for (let hostedView of this.manager.tooltipViews) {
6006
+ if (hostedView.positioned)
6007
+ hostedView.positioned(space);
5927
6008
  }
5928
6009
  }
5929
- setSelection(selection, focus = true) {
5930
- this.updateElts(false, selection);
5931
- if (selection.length > 1 && this.selection.length <= 1)
5932
- this.dom.ownerDocument.addEventListener("mousedown", this.globalClick);
5933
- this.selection = selection;
5934
- if (focus && selection.length)
5935
- selection[selection.length - 1].focusDOM.focus();
6010
+ update(update) {
6011
+ this.manager.update(update);
5936
6012
  }
5937
- key(event) {
5938
- if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented)
5939
- return;
5940
- let sLen = this.selection.length;
5941
- if (event.key == "ArrowLeft" || event.key == "ArrowRight") {
5942
- if (sLen) {
5943
- let forward = (event.key == "ArrowRight") == (getComputedStyle(this.dom).direction == "ltr");
5944
- let next = findNextChild(this.children, this.selection[0], forward ? 1 : -1);
5945
- this.setSelection(next ? [next] : []);
5946
- }
5947
- }
5948
- else if (event.key == "ArrowDown" || event.key == "ArrowUp") {
5949
- if (sLen > 1) {
5950
- let parent = this.selection[sLen - 2];
5951
- let next = findNextChild(parent.children, this.selection[sLen - 1], event.key == "ArrowUp" ? -1 : 1);
5952
- if (next)
5953
- this.setSelection(this.selection.slice(0, sLen - 1).concat(next));
5954
- }
5955
- else if (sLen == 1 && this.selection[0].children) {
5956
- let inner = defaultChild(this.selection[0].children);
5957
- if (inner)
5958
- this.setSelection([this.selection[0], inner]);
5959
- }
5960
- }
5961
- else if (event.key == "Home" || event.key == "End") {
5962
- let child = findChild(sLen > 1 ? this.selection[sLen - 2].children : this.children, event.key == "Home");
5963
- if (child)
5964
- this.setSelection(this.selection.slice(0, sLen - 1).concat(child));
5965
- }
5966
- else if (event.key == " " || event.key == "Enter") {
5967
- if (sLen) {
5968
- let child = this.selection[sLen - 1];
5969
- if (child.flags & 8) ;
5970
- else if (child.children) {
5971
- let inner = defaultChild(child.children);
5972
- if (inner)
5973
- this.setSelection(this.selection.concat(inner));
5974
- }
5975
- else {
5976
- if (child.run)
5977
- child.run(this.wg);
5978
- this.setSelection([this.selection[0]]);
5979
- }
6013
+ remove(wg) {
6014
+ for (let t of this.manager.tooltipViews)
6015
+ t.remove?.(wg);
6016
+ }
6017
+ passProp(name) {
6018
+ let value = undefined;
6019
+ for (let view of this.manager.tooltipViews) {
6020
+ let given = view[name];
6021
+ if (given !== undefined) {
6022
+ if (value === undefined)
6023
+ value = given;
6024
+ else if (value !== given)
6025
+ return undefined;
5980
6026
  }
5981
6027
  }
5982
- else if (event.key == "Escape" && sLen > 1) {
5983
- this.setSelection(this.selection.slice(0, sLen - 1));
5984
- }
5985
- else {
5986
- return;
6028
+ return value;
6029
+ }
6030
+ get offset() { return this.passProp("offset"); }
6031
+ get getCoords() { return this.passProp("getCoords"); }
6032
+ get overlap() { return this.passProp("overlap"); }
6033
+ get resize() { return this.passProp("resize"); }
6034
+ }
6035
+ const showHoverTooltipHost = /*@__PURE__*/Tooltip.show.derive(state => {
6036
+ let tooltips = state.facet(showHoverTooltip);
6037
+ if (tooltips.length === 0)
6038
+ return null;
6039
+ return {
6040
+ pos: Math.min(...tooltips.map(t => t.pos)),
6041
+ end: Math.max(...tooltips.map(t => t.end ?? t.pos)),
6042
+ create: HoverTooltipHost.create,
6043
+ above: tooltips[0].above,
6044
+ arrow: tooltips.some(t => t.arrow),
6045
+ };
6046
+ });
6047
+ class HoverPlugin {
6048
+ wg;
6049
+ source;
6050
+ field;
6051
+ setHover;
6052
+ hoverTime;
6053
+ lastMove;
6054
+ hoverTimeout = -1;
6055
+ restartTimeout = -1;
6056
+ pending = null;
6057
+ constructor(wg, source, field, setHover, hoverTime) {
6058
+ this.wg = wg;
6059
+ this.source = source;
6060
+ this.field = field;
6061
+ this.setHover = setHover;
6062
+ this.hoverTime = hoverTime;
6063
+ this.lastMove = { x: 0, y: 0, target: wg.dom, time: 0 };
6064
+ this.checkHover = this.checkHover.bind(this);
6065
+ wg.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
6066
+ wg.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
6067
+ }
6068
+ update() {
6069
+ if (this.pending) {
6070
+ this.pending = null;
6071
+ clearTimeout(this.restartTimeout);
6072
+ this.restartTimeout = setTimeout(() => this.startHover(), 20);
5987
6073
  }
5988
- event.preventDefault();
5989
6074
  }
5990
- click(event) {
5991
- if (event.defaultPrevented)
6075
+ get active() {
6076
+ return this.wg.state.field(this.field);
6077
+ }
6078
+ checkHover() {
6079
+ this.hoverTimeout = -1;
6080
+ if (this.active.length)
5992
6081
  return;
5993
- let target, idx;
5994
- for (let node = event.target;; node = node.parentNode) {
5995
- if (!node || node == this.dom)
5996
- return;
5997
- target = this.elts.find(e => e.dom == node);
5998
- if (target)
5999
- break;
6000
- }
6001
- if (target.flags & 8) ;
6002
- else if (target.children) {
6003
- if ((idx = this.selection.indexOf(target)) > -1 && idx < this.selection.length - 1) {
6004
- this.setSelection(this.selection.slice(0, idx + 1), false);
6005
- }
6006
- else {
6007
- for (let i = 0; i < this.selection.length; i++) {
6008
- let { children } = i ? this.selection[i - 1] : this;
6009
- if (children.includes(target)) {
6010
- let next = defaultChild(target.children);
6011
- if (next)
6012
- this.setSelection(this.selection.slice(0, i).concat([target, next]), false);
6013
- }
6082
+ let hovered = Date.now() - this.lastMove.time;
6083
+ if (hovered < this.hoverTime)
6084
+ this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
6085
+ else
6086
+ this.startHover();
6087
+ }
6088
+ startHover() {
6089
+ clearTimeout(this.restartTimeout);
6090
+ let { wg, lastMove } = this;
6091
+ let { pos, side } = wg.posAtCoords(lastMove);
6092
+ let open = this.source(wg, pos, side || -1);
6093
+ if (open?.then) {
6094
+ let pending = this.pending = { pos };
6095
+ open.then(result => {
6096
+ if (this.pending == pending) {
6097
+ this.pending = null;
6098
+ if (result && !(Array.isArray(result) && !result.length))
6099
+ wg.dispatch({ effects: this.setHover.of(Array.isArray(result) ? result : [result]) });
6014
6100
  }
6015
- }
6101
+ }, e => logException(wg.state, e, "hover tooltip"));
6016
6102
  }
6017
- else {
6018
- if (target.run)
6019
- target.run(this.wg);
6020
- this.setSelection(this.children.includes(target) ? [target] : this.selection.length ? [this.selection[0]] : [], false);
6103
+ else if (open && !(Array.isArray(open) && !open.length)) {
6104
+ wg.dispatch({ effects: this.setHover.of(Array.isArray(open) ? open : [open]) });
6021
6105
  }
6022
- event.preventDefault();
6023
6106
  }
6024
- globalClick(event) {
6025
- if (!event.composedPath().includes(this.dom)) {
6026
- this.dom.ownerDocument.removeEventListener("mousedown", this.globalClick);
6027
- if (this.selection.length > 1)
6028
- this.setSelection([this.selection[0]], false);
6107
+ get tooltip() {
6108
+ let plugin = this.wg.plugin(tooltipPlugin);
6109
+ let index = plugin ? plugin.manager.tooltips.findIndex(t => t.create == HoverTooltipHost.create) : -1;
6110
+ return index > -1 ? plugin.manager.tooltipViews[index] : null;
6111
+ }
6112
+ mousemove(event) {
6113
+ this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
6114
+ if (this.hoverTimeout < 0)
6115
+ this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
6116
+ let { active, tooltip } = this;
6117
+ if (active.length && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
6118
+ let { pos } = active[0] || this.pending, end = active[0]?.end ?? pos;
6119
+ if ((pos == end ? this.wg.posAtCoords(this.lastMove).pos != pos
6120
+ : !isOverRange(this.wg, pos, end, event.clientX, event.clientY))) {
6121
+ this.wg.dispatch({ effects: this.setHover.of([]) });
6122
+ this.pending = null;
6123
+ }
6029
6124
  }
6030
6125
  }
6031
- focusout() {
6032
- clearTimeout(this.focusTimeout);
6033
- if (this.selection.length > 1) {
6034
- this.focusTimeout = setTimeout(() => {
6035
- let active = this.wg.root.activeElement;
6036
- for (let i = 0; i < this.selection.length - 1; i++) {
6037
- if (!active || !this.selection[i].dom.contains(active)) {
6038
- this.setSelection([this.selection[0]], false);
6039
- break;
6040
- }
6041
- }
6042
- }, 20);
6126
+ mouseleave(event) {
6127
+ clearTimeout(this.hoverTimeout);
6128
+ this.hoverTimeout = -1;
6129
+ let { active } = this;
6130
+ if (active.length) {
6131
+ let { tooltip } = this;
6132
+ let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
6133
+ if (!inTooltip)
6134
+ this.wg.dispatch({ effects: this.setHover.of([]) });
6135
+ else
6136
+ this.watchTooltipLeave(tooltip.dom);
6043
6137
  }
6044
6138
  }
6045
- up() {
6046
- if (this.selection.length > 1)
6047
- this.setSelection([this.selection[0]], false);
6139
+ watchTooltipLeave(tooltip) {
6140
+ let watch = (event) => {
6141
+ tooltip.removeEventListener("mouseleave", watch);
6142
+ if (this.active.length && !this.wg.dom.contains(event.relatedTarget))
6143
+ this.wg.dispatch({ effects: this.setHover.of([]) });
6144
+ };
6145
+ tooltip.addEventListener("mouseleave", watch);
6048
6146
  }
6049
- get top() { return true; }
6050
- }
6051
- const menuBarPanel = /*@__PURE__*/(() => Panel.show.of(MenuBar.create))();
6052
- function findChild(children, start) {
6053
- for (let i = start ? 0 : children.length - 1; start ? i < children.length : i >= 0; start ? i++ : i--) {
6054
- let child = children[i];
6055
- if (!(child.flags & 8))
6056
- return child;
6147
+ remove() {
6148
+ clearTimeout(this.hoverTimeout);
6149
+ this.wg.dom.removeEventListener("mouseleave", this.mouseleave);
6150
+ this.wg.dom.removeEventListener("mousemove", this.mousemove);
6057
6151
  }
6058
- return null;
6059
6152
  }
6060
- function findNextChild(children, child, dir) {
6061
- let index = children.indexOf(child);
6062
- if (index < 0)
6063
- return null;
6064
- for (let i = index + dir;; i += dir) {
6065
- if (i < 0)
6066
- i = children.length - 1;
6067
- else if (i >= children.length)
6068
- i = 0;
6069
- if (i == index)
6070
- return null;
6071
- let child = children[i];
6072
- if (!(child.flags & 32))
6073
- return child;
6153
+ const tooltipMargin = 4;
6154
+ function isInTooltip(tooltip, event) {
6155
+ let { left, right, top, bottom } = tooltip.getBoundingClientRect(), arrow;
6156
+ if (arrow = tooltip.querySelector(".wg-tooltip-arrow")) {
6157
+ let arrowRect = arrow.getBoundingClientRect();
6158
+ top = Math.min(arrowRect.top, top);
6159
+ bottom = Math.max(arrowRect.bottom, bottom);
6074
6160
  }
6161
+ return event.clientX >= left - tooltipMargin && event.clientX <= right + tooltipMargin &&
6162
+ event.clientY >= top - tooltipMargin && event.clientY <= bottom + tooltipMargin;
6075
6163
  }
6076
- function defaultChild(children) {
6077
- return children.length ? children.find(ch => ch.flags & 16) || children[0] : null;
6078
- }
6079
- const theme = /*@__PURE__*/Wordgard.styles({
6080
- "&": {
6081
- "--wg-menu-item-size": "20px"
6082
- },
6083
- "&light": {
6084
- "--wg-menu-color": "#555"
6085
- },
6086
- "&dark": {
6087
- "--wg-menu-color": "#ccc"
6088
- },
6089
- "wg-menubar": {
6090
- display: "flex",
6091
- flexWrap: "wrap",
6092
- gap: "5px",
6093
- padding: "3px",
6094
- color: "var(--wg-menu-color)",
6095
- borderBottom: "1px solid var(--wg-border-color)",
6096
- },
6097
- ".wg-menu-button:focus": {
6098
- outline: "1.5px solid var(--wg-highlight-color)"
6099
- },
6100
- ".wg-menu-button": {
6101
- border: 0,
6102
- padding: "3px",
6103
- borderRadius: "4px",
6104
- boxSizing: "content-box",
6105
- backgroundColor: "transparent",
6106
- font: "inherit",
6107
- color: "inherit",
6108
- height: "var(--wg-menu-item-size)",
6109
- textAlign: "left",
6110
- "&[aria-disabled]": {
6111
- opacity: "0.3"
6112
- },
6113
- "&[aria-pressed]": {
6114
- color: "var(--wg-highlight-color)"
6115
- },
6116
- "&:hover": {
6117
- backgroundColor: "#88888820",
6118
- },
6119
- },
6120
- ".wg-button-label": {
6121
- display: "inline-block",
6122
- minWidth: "var(--wg-submenu-width)",
6123
- padding: "0 3px",
6124
- },
6125
- "svg.wg-icon": {
6126
- fill: "currentColor",
6127
- width: "var(--wg-menu-item-size)",
6128
- height: "var(--wg-menu-item-size)",
6129
- },
6130
- "wg-menu-spacer": {
6131
- display: "block",
6132
- width: "7px"
6133
- },
6134
- "wg-submenu": {
6135
- display: "block",
6136
- position: "relative",
6137
- lineHeight: ".6",
6138
- whiteSpace: "nowrap"
6139
- },
6140
- "wg-menu-list": {
6141
- display: "flex",
6142
- flexDirection: "column",
6143
- alignItems: "stretch",
6144
- gap: "5px",
6145
- padding: "5px",
6146
- borderRadius: "4px",
6147
- backgroundColor: "var(--wg-panel-color)",
6148
- position: "absolute",
6149
- zIndex: "10",
6150
- top: "100%",
6151
- boxShadow: "0 2px 8px 0 rgba(128, 128, 128, 0.2)",
6152
- "& wg-menu-list": {
6153
- left: "100%",
6154
- top: 0
6155
- }
6156
- },
6157
- ".wg-submenu-arrow:after": {
6158
- padding: "0 2px",
6159
- fontSize: "80%",
6160
- verticalAlign: "10%",
6161
- opacity: "0.4",
6162
- content: "'▾'"
6163
- },
6164
- ".wg-submenu-arrow.wg-submenu-arrow-open:after": {
6165
- content: "'▴'"
6166
- },
6167
- });
6168
- const barTemplate = /*@__PURE__*/GardState.Facet.define({
6169
- combine: inputs => inputs.length ? inputs[0] : [Menu.Group.top.template()]
6170
- });
6171
- function menuBar(config = {}) {
6172
- let extensions = [menuBarPanel, theme];
6173
- if (config.template)
6174
- extensions.push(barTemplate.of(Array.isArray(config.template) ? config.template : [config.template]));
6175
- return extensions;
6164
+ function isOverRange(wg, from, to, x, y, margin) {
6165
+ let rect = wg.contentDOM.getBoundingClientRect();
6166
+ if (rect.left > x || rect.right < x || rect.top > y || rect.bottom < y)
6167
+ return false;
6168
+ let pos = wg.posAtCoords({ x, y }).pos;
6169
+ return pos >= from && pos <= to;
6176
6170
  }
6177
6171
 
6178
6172
  const Dialog = /*@__PURE__*/(function (Dialog) {
6179
6173
  function show(wg, config) {
6180
6174
  let resolve;
6181
- let promise = new Promise(r => resolve = r);
6182
- let panelCtor = (wg) => createDialog(wg, config, resolve);
6183
- if (wg.state.field(dialogField, false)) {
6184
- wg.dispatch({ effects: openDialogEffect.of(panelCtor) });
6175
+ let result = new Promise(r => resolve = r);
6176
+ let hint = config.positioningHint === true ? PositioningHint.get() : config.positioningHint;
6177
+ let tooltip = hint || !config.tooltip ? null : {
6178
+ ...config.tooltip,
6179
+ create: (wg) => createDialog(wg, dialog),
6180
+ }, offset = null;
6181
+ if (hint) {
6182
+ let editorRect = wg.dom.getBoundingClientRect();
6183
+ offset = {
6184
+ x: wg.state.textLTR ? hint.left - editorRect.left : editorRect.right - hint.right,
6185
+ y: hint.bottom - editorRect.top
6186
+ };
6185
6187
  }
6186
- else {
6187
- wg.dispatch({ effects: GardState.appendConfig.of(dialogField.init(() => [panelCtor])) });
6188
- }
6189
- let close = closeDialogEffect.of(panelCtor);
6190
- return { close, result: promise.then(form => {
6191
- let queue = wg.win.queueMicrotask || ((f) => wg.win.setTimeout(f, 10));
6192
- queue(() => {
6193
- if (wg.state.field(dialogField).indexOf(panelCtor) > -1)
6194
- wg.dispatch({ effects: close });
6195
- });
6196
- return form;
6197
- }) };
6188
+ let dialog = new OpenDialog(config, resolve, tooltip, offset);
6189
+ let effects = [openDialog.of(dialog)];
6190
+ if (!wg.state.field(dialogs, false))
6191
+ effects.push(GardState.appendConfig.of([dialogs, renderDialogs]));
6192
+ wg.dispatch({ effects });
6193
+ wg.scheduleDOMRead(() => {
6194
+ if (!wg.state.field(dialogs).includes(dialog))
6195
+ dialog.resolve(null);
6196
+ });
6197
+ return {
6198
+ close: () => doCloseDialog(wg, dialog),
6199
+ result
6200
+ };
6198
6201
  }
6199
6202
  Dialog.show = show;
6200
- function get(wg, className) {
6201
- let dialogs = wg.state.field(dialogField, false) || [];
6202
- for (let open of dialogs) {
6203
- let panel = Panel.get(wg, open);
6204
- if (panel && panel.dom.classList.contains(className))
6205
- return panel;
6206
- }
6207
- return null;
6203
+ function get(wg, key) {
6204
+ let active = wg.state.field(dialogs, false);
6205
+ let found = active && active.find(d => d.spec.key === key);
6206
+ if (!found)
6207
+ return null;
6208
+ return { dom: dialogDOM(wg, found), close: () => doCloseDialog(wg, found) };
6208
6209
  }
6209
6210
  Dialog.get = get;
6210
- function close(wg, className) {
6211
- let dialogs = wg.state.field(dialogField, false) || [];
6212
- for (let open of dialogs) {
6213
- let panel = Panel.get(wg, open);
6214
- if (panel && panel.dom.classList.contains(className)) {
6215
- wg.dispatch({ effects: closeDialogEffect.of(open) });
6216
- return true;
6217
- }
6218
- }
6219
- return false;
6220
- }
6221
- Dialog.close = close;
6222
6211
  ;return Dialog})({});
6223
- const dialogField = /*@__PURE__*/GardState.Field.define({
6224
- create() { return []; },
6225
- update(dialogs, tr) {
6226
- for (let e of tr.effects) {
6227
- if (e.is(openDialogEffect))
6228
- dialogs = [e.value].concat(dialogs);
6229
- else if (e.is(closeDialogEffect))
6230
- dialogs = dialogs.filter(d => d != e.value);
6231
- }
6232
- return dialogs;
6233
- },
6234
- provide: f => Panel.show.computeN(state => state.field(f))
6235
- });
6236
- const openDialogEffect = /*@__PURE__*/Transaction.Effect.define();
6237
- const closeDialogEffect = /*@__PURE__*/Transaction.Effect.define();
6238
- function createDialog(wg, config, result) {
6212
+ function dialogDOM(wg, dialog) {
6213
+ let plugin = wg.plugin(renderDialogs);
6214
+ let index = plugin ? plugin.active.indexOf(dialog) : -1;
6215
+ return index > -1 ? plugin.elements[index] : null;
6216
+ }
6217
+ function doCloseDialog(wg, dialog) {
6218
+ let active = wg.state.field(dialogs, false);
6219
+ if (active && active.includes(dialog)) {
6220
+ let dom = dialogDOM(wg, dialog);
6221
+ if (dom && dom.contains(wg.win.document.activeElement))
6222
+ wg.focus();
6223
+ wg.dispatch({ effects: closeDialog.of(dialog) });
6224
+ }
6225
+ }
6226
+ function createDialog(wg, dialog) {
6227
+ let config = dialog.spec;
6239
6228
  let content = config.content ? config.content(wg, () => done(null)) : null;
6240
6229
  if (!content) {
6241
6230
  content = document.createElement("form");
@@ -6270,10 +6259,7 @@ function createDialog(wg, config, result) {
6270
6259
  for (let i = 0; i < forms.length; i++) {
6271
6260
  let form = forms[i];
6272
6261
  form.addEventListener("keydown", (event) => {
6273
- if (event.keyCode == 27) { event.preventDefault();
6274
- done(null);
6275
- }
6276
- else if (event.keyCode == 13) { event.preventDefault();
6262
+ if (event.keyCode == 13) { event.preventDefault();
6277
6263
  done(form);
6278
6264
  }
6279
6265
  });
@@ -6288,691 +6274,1057 @@ function createDialog(wg, config, result) {
6288
6274
  close.className = "wg-dialog-close";
6289
6275
  close.type = "button";
6290
6276
  close.append("×");
6291
- let panel = document.createElement("wg-dialog");
6292
- panel.append(content, close);
6277
+ let dom = document.createElement("dialog");
6278
+ dom.open = true;
6279
+ dom.append(content, close);
6280
+ dom.addEventListener("keydown", event => {
6281
+ if (event.keyCode == 27) { event.preventDefault();
6282
+ done(null);
6283
+ }
6284
+ });
6293
6285
  if (config.class)
6294
- panel.className = config.class;
6286
+ dom.className = config.class;
6295
6287
  function done(form) {
6296
- if (panel.contains(panel.ownerDocument.activeElement))
6297
- wg.focus();
6298
- result(form);
6288
+ dialog.resolve(form);
6289
+ doCloseDialog(wg, dialog);
6290
+ }
6291
+ let focus = null;
6292
+ if (config.focus) {
6293
+ if (typeof config.focus == "string")
6294
+ focus = dom.querySelector(config.focus);
6295
+ else
6296
+ focus = content.querySelector("input") || content.querySelector("button");
6299
6297
  }
6300
- let mustFocus = config.focus;
6301
6298
  return {
6302
- dom: panel,
6303
- top: config.top !== false,
6299
+ dom,
6304
6300
  connect: () => {
6305
- if (mustFocus) {
6306
- mustFocus = false;
6307
- let focus;
6308
- if (typeof config.focus == "string")
6309
- focus = content.querySelector(config.focus);
6310
- else
6311
- focus = content.querySelector("input") || content.querySelector("button");
6312
- if (focus && "select" in focus)
6301
+ if (focus) {
6302
+ if ("select" in focus)
6313
6303
  focus.select();
6314
- else if (focus && "focus" in focus)
6304
+ else if ("focus" in focus)
6315
6305
  focus.focus();
6306
+ focus = null;
6316
6307
  }
6317
6308
  }
6318
6309
  };
6319
6310
  }
6320
-
6321
- const Outside = "-10000px";
6322
- class TooltipViewManager {
6323
- facet;
6324
- createTooltipView;
6325
- removeTooltipView;
6326
- input;
6327
- tooltips;
6328
- tooltipViews;
6329
- constructor(wg, facet, createTooltipView, removeTooltipView) {
6330
- this.facet = facet;
6331
- this.createTooltipView = createTooltipView;
6332
- this.removeTooltipView = removeTooltipView;
6333
- this.input = wg.state.facet(facet);
6334
- this.tooltips = this.input.filter(t => t);
6335
- let prev = null;
6336
- this.tooltipViews = this.tooltips.map(t => prev = createTooltipView(t, prev));
6311
+ class OpenDialog {
6312
+ spec;
6313
+ resolve;
6314
+ tooltip;
6315
+ offset;
6316
+ constructor(spec, resolve, tooltip, offset) {
6317
+ this.spec = spec;
6318
+ this.resolve = resolve;
6319
+ this.tooltip = tooltip;
6320
+ this.offset = offset;
6337
6321
  }
6338
- update(update, above) {
6339
- let input = update.state.facet(this.facet);
6340
- let tooltips = input.filter(x => x);
6341
- if (input === this.input) {
6342
- for (let t of this.tooltipViews)
6343
- if (t.update)
6344
- t.update(update);
6345
- return false;
6346
- }
6347
- let tooltipViews = [], newAbove = above ? [] : null;
6348
- for (let i = 0; i < tooltips.length; i++) {
6349
- let tip = tooltips[i], known = -1;
6350
- if (!tip)
6351
- continue;
6352
- for (let i = 0; i < this.tooltips.length; i++) {
6353
- let other = this.tooltips[i];
6354
- if (other && other.create == tip.create)
6355
- known = i;
6322
+ }
6323
+ const openDialog = /*@__PURE__*/Transaction.Effect.define();
6324
+ const closeDialog = /*@__PURE__*/Transaction.Effect.define();
6325
+ const dialogs = /*@__PURE__*/GardState.Field.define({
6326
+ create: () => [],
6327
+ update: (value, tr) => {
6328
+ for (let e of tr.effects) {
6329
+ if (e.is(openDialog))
6330
+ value = value.concat(e.value);
6331
+ else if (e.is(closeDialog))
6332
+ value = value.filter(d => d != e.value);
6333
+ }
6334
+ for (let i = 0; i < value.length; i++) {
6335
+ let { closedBy } = value[i].spec;
6336
+ if (closedBy && closedBy(tr)) {
6337
+ value = value.slice(0, i).concat(value.slice(i + 1));
6338
+ i--;
6356
6339
  }
6357
- if (known < 0) {
6358
- tooltipViews[i] = this.createTooltipView(tip, i ? tooltipViews[i - 1] : null);
6359
- if (newAbove)
6360
- newAbove[i] = !!tip.above;
6340
+ }
6341
+ return value;
6342
+ },
6343
+ provide: f => Tooltip.show.deriveN(state => {
6344
+ let tooltips = [];
6345
+ for (let d of state.field(f))
6346
+ if (d.tooltip)
6347
+ tooltips.push(d.tooltip);
6348
+ return tooltips;
6349
+ })
6350
+ });
6351
+ const renderDialogs = /*@__PURE__*/Wordgard.Plugin.fromClass(class {
6352
+ active = [];
6353
+ elements = [];
6354
+ mustPosition = false;
6355
+ constructor(wg) {
6356
+ this.positionDialogs = this.positionDialogs.bind(this);
6357
+ this.updateTo(wg, wg.state.field(dialogs));
6358
+ if (this.mustPosition)
6359
+ wg.scheduleDOMRead(this.positionDialogs);
6360
+ }
6361
+ update(update) {
6362
+ let active = update.state.field(dialogs), changed = this.active != active;
6363
+ if (changed)
6364
+ this.updateTo(update.editor, active);
6365
+ if ((update.geometryChanged || changed) && this.mustPosition)
6366
+ update.editor.scheduleDOMRead(this.positionDialogs);
6367
+ }
6368
+ updateTo(wg, active) {
6369
+ for (let i = 0; i < this.active.length; i++)
6370
+ if (!active.includes(this.active[i])) {
6371
+ let elt = this.elements[i], dialog = this.active[i];
6372
+ if (elt)
6373
+ elt.remove();
6374
+ dialog.resolve(null);
6375
+ }
6376
+ this.mustPosition = false;
6377
+ this.elements = active.map((dialog, i) => {
6378
+ if (dialog.tooltip)
6379
+ return null;
6380
+ this.mustPosition = true;
6381
+ let panel;
6382
+ let found = this.active.indexOf(dialog);
6383
+ if (found > -1) {
6384
+ panel = this.elements[found];
6361
6385
  }
6362
6386
  else {
6363
- let tooltipView = tooltipViews[i] = this.tooltipViews[known];
6364
- if (newAbove)
6365
- newAbove[i] = above[known];
6366
- if (tooltipView.update)
6367
- tooltipView.update(update);
6387
+ let { dom, connect } = createDialog(wg, dialog);
6388
+ dom.classList.add("wg-free-dialog");
6389
+ wg.dom.appendChild(dom);
6390
+ connect();
6391
+ panel = dom;
6392
+ }
6393
+ panel.style.zIndex = String(400 + i);
6394
+ return panel;
6395
+ });
6396
+ this.active = active;
6397
+ }
6398
+ positionDialogs(wg) {
6399
+ let sizes = this.elements.map(e => e && e.getBoundingClientRect());
6400
+ let editor = wg.dom.getBoundingClientRect();
6401
+ let windowWidth = wg.win.innerWidth, windowHeight = wg.win.innerHeight;
6402
+ let midX = (Math.max(0, editor.left) + Math.min(windowWidth, editor.right)) / 2;
6403
+ let midY = (Math.max(0, editor.top) + Math.min(windowHeight, editor.bottom)) / 2;
6404
+ let ltr = wg.state.textLTR;
6405
+ wg.scheduleDOMWrite(() => {
6406
+ for (let i = 0; i < sizes.length; i++) {
6407
+ let size = sizes[i];
6408
+ if (!size)
6409
+ continue;
6410
+ let { offset } = this.active[i];
6411
+ let baseX, baseY;
6412
+ if (offset) {
6413
+ baseY = editor.top + offset.y;
6414
+ baseX = ltr ? editor.left + offset.x : editor.right - offset.x - size.width;
6415
+ }
6416
+ else {
6417
+ baseX = midX - size.width / 2;
6418
+ baseY = midY - size.height / 2;
6419
+ }
6420
+ if (baseX < 0)
6421
+ baseX = 0;
6422
+ else if (baseX + size.width > windowWidth && size.width < windowWidth)
6423
+ baseX = windowWidth - size.width;
6424
+ if (baseY < 0)
6425
+ baseY = 0;
6426
+ else if (baseY + size.height > windowHeight && size.height < windowHeight)
6427
+ baseY = windowHeight - size.height;
6428
+ let elt = this.elements[i];
6429
+ elt.style.left = baseX + "px";
6430
+ elt.style.top = baseY + "px";
6368
6431
  }
6432
+ });
6433
+ }
6434
+ destroy() {
6435
+ for (let elt of this.elements)
6436
+ if (elt)
6437
+ elt.remove();
6438
+ for (let dialog of this.active)
6439
+ dialog.resolve(null);
6440
+ }
6441
+ }, plugin => plugin.eventHandler("scroll", (event, wg, val) => {
6442
+ if (val.mustPosition)
6443
+ wg.scheduleDOMRead(val.positionDialogs);
6444
+ }));
6445
+ const PositioningHint = /*@__PURE__*/(function (PositioningHint) {
6446
+ let currentHint = null;
6447
+ function runWith(hint, run) {
6448
+ let prev = currentHint;
6449
+ currentHint = hint;
6450
+ try {
6451
+ return run();
6369
6452
  }
6370
- for (let t of this.tooltipViews)
6371
- if (tooltipViews.indexOf(t) < 0) {
6372
- this.removeTooltipView(t);
6373
- if (update.editor.connected)
6374
- t.disconnect?.(update.editor);
6375
- t.remove?.(update.editor);
6376
- }
6377
- if (above) {
6378
- newAbove.forEach((val, i) => above[i] = val);
6379
- above.length = newAbove.length;
6453
+ finally {
6454
+ currentHint = prev;
6380
6455
  }
6381
- this.input = input;
6382
- this.tooltips = tooltips;
6383
- this.tooltipViews = tooltipViews;
6384
- return true;
6385
6456
  }
6457
+ PositioningHint.runWith = runWith;
6458
+ function get() {
6459
+ return currentHint ? currentHint() : null;
6460
+ }
6461
+ PositioningHint.get = get;
6462
+ ;return PositioningHint})({});
6463
+
6464
+ function collectKeyDescs(bindings) {
6465
+ let groups = [];
6466
+ let add = (group, key, desc) => {
6467
+ let found = groups.find(g => g.group == group);
6468
+ if (!found)
6469
+ groups.push(found = { group, bindings: [] });
6470
+ found.bindings.push({ key, desc });
6471
+ };
6472
+ for (let binding of bindings) {
6473
+ let group = binding.spec.group || null;
6474
+ let name = binding.spec[currentPlatform] || binding.spec.key;
6475
+ if (name) {
6476
+ if (binding.spec.description)
6477
+ add(group, normalizeKeyName(name, currentPlatform), binding.spec.description);
6478
+ if (binding.spec.shiftDescription)
6479
+ add(group, "Shift-" + normalizeKeyName(name, currentPlatform), binding.spec.shiftDescription);
6480
+ }
6481
+ }
6482
+ return groups.sort((a, b) => (a.group?.rank ?? 0) - (b.group?.rank || 0));
6483
+ }
6484
+ function resolveText(state, text) {
6485
+ return typeof text == "string" ? text : text(state);
6486
+ }
6487
+ function renderKeyDescs(state, groups) {
6488
+ let frag = document.createDocumentFragment();
6489
+ frag.append(cr("h3", { class: "wg-dialog-title" }, phrases.get(state, "key_bindings")));
6490
+ frag.append(cr("div", { class: "wg-key-list", tabindex: "-1" }, groups.map(group => [
6491
+ group.group ? cr("h4", resolveText(state, group.group.description)) : null,
6492
+ group.bindings.map(({ key, desc }) => [
6493
+ cr("div", cr("kbd", key)),
6494
+ cr("div", resolveText(state, desc))
6495
+ ])
6496
+ ])));
6497
+ return frag;
6498
+ }
6499
+ function keyBindingHelp() {
6500
+ return [keyBindingHelp.keyBindings, keyBindingHelp.button];
6501
+ }
6502
+ ;keyBindingHelp = /*@__PURE__*/(function (keyBindingHelp) {
6503
+ keyBindingHelp.showKeyBindings = wg => {
6504
+ let open = Dialog.get(wg, keyBindingHelp.showKeyBindings);
6505
+ if (open) {
6506
+ open.close();
6507
+ return true;
6508
+ }
6509
+ let descs = collectKeyDescs(wg.state.field(KeyBinding.keymap));
6510
+ if (!descs.length)
6511
+ return false;
6512
+ Dialog.show(wg, {
6513
+ key: keyBindingHelp.showKeyBindings,
6514
+ content: () => renderKeyDescs(wg.state, descs),
6515
+ class: "wg-key-help",
6516
+ focus: ".wg-key-list",
6517
+ positioningHint: true
6518
+ });
6519
+ return true;
6520
+ };
6521
+ keyBindingHelp.button = Menu.Button.define({
6522
+ run: keyBindingHelp.showKeyBindings,
6523
+ label: { icon: "M24 31c-2 0-4-2-4-4C22 14 32 5 49 5c17 0 28 10 28 24 0 10-5 17-13 22-8 5-11 8-11 15v0a4 4 0 0 1-4 4h-6a4 4 0 0 1-4-4l0-1c0-9 4-15 12-20 8-5 10-9 10-15 0-7-6-13-14-13-8 0-13 4-15 11 0 2-2 3-4 3h-6ZM46 95c5 0 8-4 8-8 0-5-4-8-8-8-5 0-8 4-8 8S42 95 46 95" },
6524
+ description: phrases.ref("show_keys"),
6525
+ parent: Menu.Group.top,
6526
+ rank: 99
6527
+ });
6528
+ keyBindingHelp.keyBindings = [
6529
+ KeyBinding.of({ key: "Mod-?", run: keyBindingHelp.showKeyBindings, description: phrases.ref("show_keys"),
6530
+ shift: keyBindingHelp.showKeyBindings }),
6531
+ KeyBinding.of({ key: "Mod-Shift-/", run: keyBindingHelp.showKeyBindings }),
6532
+ ];
6533
+ ;return keyBindingHelp})(keyBindingHelp);
6534
+
6535
+ function offset(add, offset) {
6536
+ return (v, from, to) => add(v, from + offset, to == null ? undefined : to + offset);
6386
6537
  }
6387
- const tooltipConfig = /*@__PURE__*/GardState.Facet.define({
6388
- combine: values => ({
6389
- position: browser.ios ? "absolute" : values.find(conf => conf.position)?.position || "fixed",
6390
- parent: values.find(conf => conf.parent)?.parent || null,
6391
- tooltipSpace: values.find(conf => conf.tooltipSpace)?.tooltipSpace || (wg => windowRect(wg.win)),
6392
- })
6393
- });
6394
- const knownHeight = /*@__PURE__*/(() => new WeakMap())();
6395
- const tooltipPlugin = /*@__PURE__*/Wordgard.Plugin.fromClass(class {
6396
- wg;
6397
- manager;
6398
- above = [];
6399
- inView = true;
6400
- position;
6401
- madeAbsolute = false;
6402
- parent;
6403
- classes;
6404
- intersectionObserver;
6405
- resizeObserver;
6406
- lastTransaction = 0;
6407
- measureTimeout = -1;
6408
- constructor(wg) {
6409
- this.wg = wg;
6410
- let config = wg.state.facet(tooltipConfig);
6411
- this.position = config.position;
6412
- this.parent = config.parent;
6413
- this.classes = wg.themeClasses;
6414
- this.createContainer();
6415
- this.measure = this.measure.bind(this);
6416
- this.resizeObserver = typeof ResizeObserver == "function" ? new ResizeObserver(() => this.measureSoon()) : null;
6417
- this.manager = new TooltipViewManager(wg, Tooltip.show, (t, p) => this.createTooltip(t, p), t => {
6418
- if (this.resizeObserver)
6419
- this.resizeObserver.unobserve(t.dom);
6420
- t.dom.remove();
6538
+ function init(type, create, source, doc) {
6539
+ return create(add => {
6540
+ doc.iterate((node, pos) => {
6541
+ if (node.isPlot && doc.schema.matchNode(node.type, type))
6542
+ source(node, pos + 1, add);
6421
6543
  });
6422
- this.above = this.manager.tooltips.map(t => !!t.above);
6423
- this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => {
6424
- if (Date.now() > this.lastTransaction - 50 &&
6425
- entries.length > 0 && entries[entries.length - 1].intersectionRatio < 1)
6426
- this.measureSoon();
6427
- }, { threshold: [1] }) : null;
6428
- this.observeIntersection();
6429
- this.maybeMeasure();
6430
- }
6431
- createContainer() {
6432
- if (this.parent) {
6433
- this.container = document.createElement("wg-tooltip-root");
6434
- this.container.style.position = "relative";
6435
- this.container.className = this.wg.themeClasses;
6436
- this.parent.appendChild(this.container);
6437
- }
6438
- else {
6439
- this.container = this.wg.dom;
6544
+ });
6545
+ }
6546
+ function refreshByPred(type, source, doc, deco, pred) {
6547
+ let recreate = [];
6548
+ doc.iterate((node, pos) => {
6549
+ if (doc.schema.matchNode(node.type, type) && pred(node))
6550
+ recreate.push({
6551
+ from: pos, to: pos + node.length,
6552
+ add: add => source(node, pos + 1, add)
6553
+ });
6554
+ });
6555
+ return recreate.length ? deco.modify({ replace: recreate }) : deco;
6556
+ }
6557
+ function update(config, create, source, deco, tr) {
6558
+ let doc = tr.newDoc;
6559
+ let refresh = config.refresh && config.refresh(tr);
6560
+ if (refresh === true)
6561
+ return init(config.type, create, source, doc);
6562
+ if (refresh)
6563
+ deco = refreshByPred(config.type, source, tr.startState.doc, deco, refresh);
6564
+ if (!tr.docChanged && !refresh)
6565
+ return deco;
6566
+ let recreate = [];
6567
+ let clear = [];
6568
+ tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
6569
+ let covered = false;
6570
+ doc.iterate(fromB, toB, (node, pos) => {
6571
+ if (doc.schema.matchNode(node.type, config.type) && !recreate.some(r => r.from == pos)) {
6572
+ recreate.push({
6573
+ from: pos, to: pos + node.length,
6574
+ add: add => source(node, pos + 1, add)
6575
+ });
6576
+ if (pos < toB && pos + node.length > toB)
6577
+ covered = true;
6578
+ return false;
6579
+ }
6580
+ });
6581
+ if (!covered) {
6582
+ let end = tr.startState.doc.resolve(toA);
6583
+ let open = end.matchingParent(p => doc.schema.matchNode(p.type, config.type));
6584
+ if (open && open.before >= fromA)
6585
+ clear.push({ from: open.before, to: open.after });
6440
6586
  }
6587
+ });
6588
+ if (clear.length)
6589
+ deco = deco.modify({ replace: clear });
6590
+ deco = deco.map(tr.changes, recreate);
6591
+ return deco;
6592
+ }
6593
+ function decoratePlots(config) {
6594
+ let result = [];
6595
+ let { type, points, ranges } = config;
6596
+ if (points) {
6597
+ let source = (plot, start, add) => points(plot, offset(add, start));
6598
+ let field = GardState.Field.define({
6599
+ create(state) { return init(type, PointSet.create, source, state.doc); },
6600
+ update(value, tr) { return update(config, PointSet.create, source, value, tr); }
6601
+ });
6602
+ result.push(field, Decoration.Point.source.of(s => s.field(field)));
6441
6603
  }
6442
- observeIntersection() {
6443
- if (this.intersectionObserver && this.wg.connected) {
6444
- this.intersectionObserver.disconnect();
6445
- for (let tooltip of this.manager.tooltipViews)
6446
- this.intersectionObserver.observe(tooltip.dom);
6604
+ if (ranges) {
6605
+ let source = (plot, start, add) => ranges(plot, offset(add, start));
6606
+ let field = GardState.Field.define({
6607
+ create(state) { return init(type, RangeSet.create, source, state.doc); },
6608
+ update(value, tr) { return update(config, RangeSet.create, source, value, tr); }
6609
+ });
6610
+ result.push(field, Decoration.Range.source.of(s => s.field(field)));
6611
+ }
6612
+ return result;
6613
+ }
6614
+
6615
+ const panelConfig = /*@__PURE__*/GardState.Facet.define({
6616
+ combine(configs) {
6617
+ let topContainer, bottomContainer;
6618
+ for (let c of configs) {
6619
+ topContainer ||= c.topContainer;
6620
+ bottomContainer ||= c.bottomContainer;
6447
6621
  }
6622
+ return { topContainer, bottomContainer };
6448
6623
  }
6449
- measureSoon() {
6450
- if (this.measureTimeout < 0)
6451
- this.measureTimeout = setTimeout(() => {
6452
- this.measureTimeout = -1;
6453
- this.maybeMeasure();
6454
- }, 50);
6624
+ });
6625
+ const panelPlugin = /*@__PURE__*/Wordgard.Plugin.fromClass(class {
6626
+ input;
6627
+ specs;
6628
+ panels;
6629
+ top;
6630
+ bottom;
6631
+ constructor(wg) {
6632
+ this.input = wg.state.facet(Panel.show);
6633
+ this.specs = this.input.filter(s => s);
6634
+ this.panels = this.specs.map(spec => spec(wg));
6635
+ for (let p of this.panels)
6636
+ p.dom.classList.add("wg-panel");
6637
+ let conf = wg.state.facet(panelConfig);
6638
+ this.top = new PanelGroup(wg, true, conf.topContainer);
6639
+ this.bottom = new PanelGroup(wg, false, conf.bottomContainer);
6640
+ this.top.sync(this.panels.filter(p => p.top), wg);
6641
+ this.bottom.sync(this.panels.filter(p => !p.top), wg);
6455
6642
  }
6456
6643
  update(update) {
6457
- if (update.transactions.length)
6458
- this.lastTransaction = Date.now();
6459
- let updated = this.manager.update(update, this.above);
6460
- if (updated)
6461
- this.observeIntersection();
6462
- let shouldMeasure = updated || update.geometryChanged;
6463
- let newConfig = update.state.facet(tooltipConfig);
6464
- if (newConfig.position != this.position && !this.madeAbsolute) {
6465
- this.position = newConfig.position;
6466
- for (let t of this.manager.tooltipViews)
6467
- t.dom.style.position = this.position;
6468
- shouldMeasure = true;
6644
+ let conf = update.state.facet(panelConfig);
6645
+ if (this.top.container != conf.topContainer) {
6646
+ this.top.sync([], update.editor);
6647
+ this.top = new PanelGroup(update.editor, true, conf.topContainer);
6469
6648
  }
6470
- if (newConfig.parent != this.parent) {
6471
- if (this.parent)
6472
- this.container.remove();
6473
- this.parent = newConfig.parent;
6474
- this.createContainer();
6475
- for (let t of this.manager.tooltipViews)
6476
- this.container.appendChild(t.dom);
6477
- shouldMeasure = true;
6649
+ if (this.bottom.container != conf.bottomContainer) {
6650
+ this.bottom.sync([], update.editor);
6651
+ this.bottom = new PanelGroup(update.editor, false, conf.bottomContainer);
6478
6652
  }
6479
- else if (this.parent && this.wg.themeClasses != this.classes) {
6480
- this.classes = this.container.className = this.wg.themeClasses;
6653
+ this.top.syncClasses();
6654
+ this.bottom.syncClasses();
6655
+ let input = update.state.facet(Panel.show);
6656
+ if (input != this.input) {
6657
+ let specs = input.filter(x => x);
6658
+ let panels = [], top = [], bottom = [], mount = [];
6659
+ for (let spec of specs) {
6660
+ let known = this.specs.indexOf(spec), panel;
6661
+ if (known < 0) {
6662
+ panel = spec(update.editor);
6663
+ mount.push(panel);
6664
+ }
6665
+ else {
6666
+ panel = this.panels[known];
6667
+ if (panel.update)
6668
+ panel.update(update);
6669
+ }
6670
+ panels.push(panel);
6671
+ (panel.top ? top : bottom).push(panel);
6672
+ }
6673
+ this.specs = specs;
6674
+ this.panels = panels;
6675
+ this.top.sync(top, update.editor);
6676
+ this.bottom.sync(bottom, update.editor);
6677
+ for (let p of mount) {
6678
+ p.dom.classList.add("wg-panel");
6679
+ if (p.connect && update.editor.connected)
6680
+ p.connect(update.editor);
6681
+ }
6481
6682
  }
6482
- if (shouldMeasure)
6483
- this.maybeMeasure();
6484
- }
6485
- createTooltip(tooltip, prev) {
6486
- let tooltipView = tooltip.create(this.wg);
6487
- let before = prev ? prev.dom : null;
6488
- tooltipView.dom.classList.add("wg-tooltip");
6489
- if (tooltip.arrow && !tooltipView.dom.querySelector(".wg-tooltip > wg-tooltip-arrow")) {
6490
- let arrow = document.createElement("wg-tooltip-arrow");
6491
- tooltipView.dom.appendChild(arrow);
6683
+ else {
6684
+ for (let p of this.panels)
6685
+ if (p.update)
6686
+ p.update(update);
6492
6687
  }
6493
- tooltipView.dom.style.position = this.position;
6494
- tooltipView.dom.style.top = Outside;
6495
- tooltipView.dom.style.left = "0px";
6496
- this.container.insertBefore(tooltipView.dom, before);
6497
- if (this.wg.connected)
6498
- tooltipView.connect?.(this.wg);
6499
- if (this.resizeObserver && this.wg.connected)
6500
- this.resizeObserver.observe(tooltipView.dom);
6501
- return tooltipView;
6502
6688
  }
6503
6689
  connect(wg) {
6504
- wg.win.addEventListener("resize", this.measureSoon = this.measureSoon.bind(this));
6505
- for (let t of this.manager.tooltipViews) {
6506
- t.connect?.(wg);
6507
- if (this.resizeObserver)
6508
- this.resizeObserver.observe(t.dom);
6509
- }
6510
- this.observeIntersection();
6690
+ for (let p of this.panels)
6691
+ p.connect?.(wg);
6511
6692
  }
6512
6693
  disconnect(wg) {
6513
- this.wg.win.removeEventListener("resize", this.measureSoon);
6514
- for (let t of this.manager.tooltipViews) {
6515
- t.disconnect?.(wg);
6516
- if (this.resizeObserver)
6517
- this.resizeObserver.unobserve(t.dom);
6518
- }
6519
- if (this.intersectionObserver)
6520
- this.intersectionObserver.disconnect();
6521
- }
6522
- remove() {
6523
- for (let tooltipView of this.manager.tooltipViews) {
6524
- tooltipView.dom.remove();
6525
- if (this.wg.connected)
6526
- tooltipView.disconnect?.(this.wg);
6527
- tooltipView.remove?.(this.wg);
6528
- }
6529
- if (this.parent)
6530
- this.container.remove();
6531
- clearTimeout(this.measureTimeout);
6694
+ for (let p of this.panels)
6695
+ p.disconnect?.(wg);
6532
6696
  }
6533
- measure() {
6534
- let measure = this.readMeasure();
6535
- this.wg.scheduleDOMWrite(() => this.writeMeasure(measure));
6697
+ remove(wg) {
6698
+ this.top.sync([], wg);
6699
+ this.bottom.sync([], wg);
6536
6700
  }
6537
- readMeasure() {
6538
- let scaleX = 1, scaleY = 1, makeAbsolute = false;
6539
- if (this.position == "fixed" && this.manager.tooltipViews.length) {
6540
- let { dom } = this.manager.tooltipViews[0];
6541
- if (browser.safari) {
6542
- let rect = dom.getBoundingClientRect();
6543
- makeAbsolute = Math.abs(rect.top + 10000) > 1 || Math.abs(rect.left) > 1;
6544
- }
6545
- else {
6546
- makeAbsolute = !!dom.offsetParent && dom.offsetParent != this.container.ownerDocument.body;
6701
+ }, plugin => Wordgard.coveredMargins.of(wg => {
6702
+ let value = wg.plugin(plugin);
6703
+ return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
6704
+ }));
6705
+ const Panel = /*@__PURE__*/(function (Panel) {
6706
+ Panel.show = GardState.Facet.define({
6707
+ enables: panelPlugin
6708
+ });
6709
+ function get(wg, constructor) {
6710
+ let plugin = wg.plugin(panelPlugin);
6711
+ let index = plugin ? plugin.specs.indexOf(constructor) : -1;
6712
+ return index > -1 ? plugin.panels[index] : null;
6713
+ }
6714
+ Panel.get = get;
6715
+ function configure(config) {
6716
+ return config ? [panelConfig.of(config)] : [];
6717
+ }
6718
+ Panel.configure = configure;
6719
+ ;return Panel})({});
6720
+ class PanelGroup {
6721
+ wg;
6722
+ top;
6723
+ container;
6724
+ dom = undefined;
6725
+ classes = "";
6726
+ panels = [];
6727
+ constructor(wg, top, container) {
6728
+ this.wg = wg;
6729
+ this.top = top;
6730
+ this.container = container;
6731
+ this.syncClasses();
6732
+ }
6733
+ sync(panels, wg) {
6734
+ for (let p of this.panels)
6735
+ if (!panels.includes(p)) {
6736
+ if (wg.connected)
6737
+ p.disconnect?.(wg);
6738
+ p.remove?.(wg);
6547
6739
  }
6548
- }
6549
- if (makeAbsolute || this.position == "absolute") {
6550
- let measure = this.parent || this.container, rect = measure.getBoundingClientRect();
6551
- if (rect.width && rect.height) {
6552
- scaleX = rect.width / measure.offsetWidth;
6553
- scaleY = rect.height / measure.offsetHeight;
6740
+ this.panels = panels;
6741
+ this.syncDOM();
6742
+ }
6743
+ syncDOM() {
6744
+ if (this.panels.length == 0) {
6745
+ if (this.dom) {
6746
+ this.dom.remove();
6747
+ this.dom = undefined;
6554
6748
  }
6749
+ return;
6555
6750
  }
6556
- let visible = this.wg.scrollDOM.getBoundingClientRect(), margins = this.wg.getScrollMargins();
6557
- let visLeft = visible.left + margins.left, visTop = visible.top + margins.top;
6558
- return {
6559
- visible: new DOMRect(visLeft, visTop, visible.right - margins.right - visLeft, visible.bottom - margins.bottom - visTop),
6560
- parent: this.parent ? this.container.getBoundingClientRect() : this.wg.dom.getBoundingClientRect(),
6561
- pos: this.manager.tooltips.map((t, i) => {
6562
- let tv = this.manager.tooltipViews[i];
6563
- return tv.getCoords ? tv.getCoords(t.pos) : this.wg.coordsAtPos(t.pos);
6564
- }),
6565
- size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
6566
- space: this.wg.state.facet(tooltipConfig).tooltipSpace(this.wg),
6567
- scaleX, scaleY, makeAbsolute
6568
- };
6569
- }
6570
- writeMeasure(measured) {
6571
- if (measured.makeAbsolute) {
6572
- this.madeAbsolute = true;
6573
- this.position = "absolute";
6574
- for (let t of this.manager.tooltipViews)
6575
- t.dom.style.position = "absolute";
6751
+ if (!this.dom) {
6752
+ this.dom = document.createElement("wg-panels");
6753
+ this.dom.className = this.top ? "wg-panels-top" : "wg-panels-bottom";
6754
+ let parent = this.container || this.wg.dom;
6755
+ parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
6576
6756
  }
6577
- let { visible, space, scaleX, scaleY } = measured;
6578
- let others = [];
6579
- for (let i = 0; i < this.manager.tooltips.length; i++) {
6580
- let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
6581
- let pos = measured.pos[i], size = measured.size[i];
6582
- if (!pos || tooltip.clip !== false && (pos.bottom <= Math.max(visible.top, space.top) ||
6583
- pos.top >= Math.min(visible.bottom, space.bottom) ||
6584
- pos.right < Math.max(visible.left, space.left) - .1 ||
6585
- pos.left > Math.min(visible.right, space.right) + .1)) {
6586
- dom.style.top = Outside;
6587
- continue;
6588
- }
6589
- let arrow = tooltip.arrow ? tView.dom.querySelector("wg-tooltip-arrow") : null;
6590
- let arrowHeight = arrow ? 7 : 0;
6591
- let width = size.right - size.left, height = knownHeight.get(tView) ?? size.bottom - size.top;
6592
- let offset = tView.offset || noOffset, ltr = this.wg.state.textLTR;
6593
- let left = size.width > space.right - space.left
6594
- ? (ltr ? space.left : space.right - size.width)
6595
- : ltr ? Math.max(space.left, Math.min(pos.left - (arrow ? 14 : 0) + offset.x, space.right - width))
6596
- : Math.min(Math.max(space.left, pos.left - width + (arrow ? 14 : 0) - offset.x), space.right - width);
6597
- let above = this.above[i];
6598
- if (!tooltip.strictSide && (above
6599
- ? pos.top - height - arrowHeight - offset.y < space.top
6600
- : pos.bottom + height + arrowHeight + offset.y > space.bottom) &&
6601
- above == (space.bottom - pos.bottom > pos.top - space.top))
6602
- above = this.above[i] = !above;
6603
- let spaceVert = (above ? pos.top - space.top : space.bottom - pos.bottom) - arrowHeight;
6604
- if (spaceVert < height && tView.resize !== false) {
6605
- if (spaceVert < 15) {
6606
- dom.style.top = Outside;
6607
- continue;
6608
- }
6609
- knownHeight.set(tView, height);
6610
- dom.style.height = (height = spaceVert) / scaleY + "px";
6611
- }
6612
- else if (dom.style.height) {
6613
- dom.style.height = "";
6614
- }
6615
- let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
6616
- let right = left + width;
6617
- if (tView.overlap !== true)
6618
- for (let r of others)
6619
- if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
6620
- top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
6621
- if (this.position == "absolute") {
6622
- dom.style.top = (top - measured.parent.top) / scaleY + "px";
6623
- setLeftStyle(dom, (left - measured.parent.left) / scaleX);
6757
+ let curDOM = this.dom.firstChild;
6758
+ for (let panel of this.panels) {
6759
+ if (panel.dom.parentNode == this.dom) {
6760
+ while (curDOM != panel.dom)
6761
+ curDOM = rmDOM(curDOM);
6762
+ curDOM = curDOM.nextSibling;
6624
6763
  }
6625
6764
  else {
6626
- dom.style.top = top / scaleY + "px";
6627
- setLeftStyle(dom, left / scaleX);
6628
- }
6629
- if (arrow) {
6630
- let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 - 7);
6631
- arrow.style.left = arrowLeft / scaleX + "px";
6765
+ this.dom.insertBefore(panel.dom, curDOM);
6632
6766
  }
6633
- if (tView.overlap !== true)
6634
- others.push({ left, top, right, bottom: top + height });
6635
- dom.classList.toggle("wg-tooltip-above", above);
6636
- dom.classList.toggle("wg-tooltip-below", !above);
6637
- if (tView.positioned)
6638
- tView.positioned(measured.space);
6639
6767
  }
6768
+ while (curDOM)
6769
+ curDOM = rmDOM(curDOM);
6640
6770
  }
6641
- maybeMeasure() {
6642
- if (this.manager.tooltips.length)
6643
- this.wg.scheduleDOMRead(this.measure);
6771
+ scrollMargin() {
6772
+ return !this.dom || this.container ? 0
6773
+ : Math.max(0, this.top ?
6774
+ this.dom.getBoundingClientRect().bottom - Math.max(0, this.wg.scrollDOM.getBoundingClientRect().top) :
6775
+ Math.min(innerHeight, this.wg.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
6776
+ }
6777
+ syncClasses() {
6778
+ if (!this.container || this.classes == this.wg.themeClasses)
6779
+ return;
6780
+ for (let cls of this.classes.split(" "))
6781
+ if (cls)
6782
+ this.container.classList.remove(cls);
6783
+ for (let cls of (this.classes = this.wg.themeClasses).split(" "))
6784
+ if (cls)
6785
+ this.container.classList.add(cls);
6644
6786
  }
6645
- }, plugin => plugin.eventObserver("scroll", (event, wg, value) => value.maybeMeasure()));
6646
- function setLeftStyle(elt, value) {
6647
- let current = parseInt(elt.style.left, 10);
6648
- if (isNaN(current) || Math.abs(value - current) > 1)
6649
- elt.style.left = value + "px";
6650
6787
  }
6651
- const styles = /*@__PURE__*/Wordgard.styles({
6652
- ".wg-tooltip": {
6653
- zIndex: 500,
6654
- boxSizing: "border-box",
6655
- backgroundColor: "var(--wg-panel-color)",
6656
- boxShadow: "0 0 8px 0 rgba(128, 128, 128, 0.2)",
6657
- font: "var(--wg-dialog-font)",
6658
- },
6659
- ".wg-tooltip-section:not(:first-child)": {
6660
- borderTop: "1px solid var(--wg-border-color)",
6661
- },
6662
- "wg-tooltip-arrow": {
6663
- display: "block",
6664
- height: `${7}px`,
6665
- width: `${7 * 2}px`,
6666
- position: "absolute",
6667
- zIndex: -1,
6668
- overflow: "hidden",
6669
- "&:before, &:after": {
6670
- content: "''",
6671
- position: "absolute",
6672
- width: 0,
6673
- height: 0,
6674
- borderLeft: `${7}px solid transparent`,
6675
- borderRight: `${7}px solid transparent`,
6676
- },
6677
- ".wg-tooltip-above &": {
6678
- bottom: `-${7}px`,
6679
- "&:before": {
6680
- borderTop: `${7}px solid var(--wg-border-color)`,
6681
- },
6682
- "&:after": {
6683
- borderTop: `${7}px solid var(--wg-panel-color)`,
6684
- bottom: "1px"
6788
+
6789
+ let nextID = 0;
6790
+ function id(prefix) {
6791
+ return prefix + "-" + (nextID++ % 0xffffff).toString(16);
6792
+ }
6793
+ const SVG = "http://www.w3.org/2000/svg";
6794
+ function labelButton(wg, button, label) {
6795
+ button.textContent = "";
6796
+ if (!label) ;
6797
+ else if (typeof label == "function" || typeof label == "string") {
6798
+ let span = button.appendChild(document.createElement("span"));
6799
+ span.className = "wg-button-label";
6800
+ span.textContent = typeof label == "string" ? label : label(wg.state);
6801
+ }
6802
+ else if ("icon" in label) {
6803
+ let svg = button.appendChild(document.createElementNS(SVG, "svg"));
6804
+ svg.classList.add("wg-icon");
6805
+ svg.setAttribute("viewBox", "0 0 100 100");
6806
+ let path = svg.appendChild(document.createElementNS(SVG, "path"));
6807
+ path.setAttribute("d", label.icon);
6808
+ if (label.directional && !wg.state.textLTR)
6809
+ svg.setAttribute("transform", "scale(-1, 1)");
6810
+ }
6811
+ }
6812
+ class BarButton {
6813
+ item;
6814
+ dom;
6815
+ flags = 0;
6816
+ index = 0;
6817
+ constructor(item, wg) {
6818
+ this.item = item;
6819
+ this.dom = document.createElement("button");
6820
+ this.dom.className = "wg-menu-button";
6821
+ this.dom.type = "button";
6822
+ this.dom.tabIndex = -1;
6823
+ labelButton(wg, this.dom, item.label);
6824
+ this.setDesc(wg);
6825
+ }
6826
+ setDesc(wg) {
6827
+ let { item: { label, description } } = this;
6828
+ let someDesc = description || (typeof label == "string" || typeof label == "function" ? label : null);
6829
+ let key = someDesc ? KeyBinding.lookupByDescription(wg.state, someDesc) : null;
6830
+ let desc = typeof someDesc == "function" ? someDesc(wg.state) : someDesc;
6831
+ if (description)
6832
+ this.dom.setAttribute("aria-label", desc);
6833
+ else
6834
+ this.dom.removeAttribute("aria-label");
6835
+ if (key)
6836
+ this.dom.setAttribute("aria-keyshortcuts", key);
6837
+ else
6838
+ this.dom.removeAttribute("aria-keyshortcuts");
6839
+ this.dom.title = desc ? desc + (key ? ` (${key})` : "") : key || "";
6840
+ }
6841
+ get focusDOM() { return this.dom; }
6842
+ update(flags, wg, update) {
6843
+ if (flags != this.flags) {
6844
+ if ((flags & 32) != (this.flags & 32))
6845
+ this.dom.style.display = flags & 32 ? "none" : "";
6846
+ if ((flags & 8) != (this.flags & 8)) {
6847
+ if (flags & 8)
6848
+ this.dom.setAttribute("aria-disabled", "true");
6849
+ else
6850
+ this.dom.removeAttribute("aria-disabled");
6685
6851
  }
6686
- },
6687
- ".wg-tooltip-below &": {
6688
- top: `-${7}px`,
6689
- "&:before": {
6690
- borderBottom: `${7}px solid var(--wg-border-color)`,
6691
- },
6692
- "&:after": {
6693
- borderBottom: `${7}px solid var(--wg-panel-color)`,
6694
- top: "1px"
6852
+ if ((flags & 4) != (this.flags & 4)) {
6853
+ if (flags & 4)
6854
+ this.dom.setAttribute("aria-selected", "true");
6855
+ else
6856
+ this.dom.removeAttribute("aria-selected");
6857
+ this.dom.tabIndex = flags & 4 ? 0 : -1;
6695
6858
  }
6696
- },
6697
- },
6698
- });
6699
- const closeHoverTooltipEffect = /*@__PURE__*/Transaction.Effect.define();
6700
- const Tooltip = /*@__PURE__*/(function (Tooltip) {
6701
- function configure(config = {}) {
6702
- return tooltipConfig.of(config);
6859
+ if ((flags & 16) != (this.flags & 16)) {
6860
+ if (flags & 16)
6861
+ this.dom.setAttribute("aria-pressed", "true");
6862
+ else
6863
+ this.dom.removeAttribute("aria-pressed");
6864
+ }
6865
+ this.flags = flags;
6866
+ }
6703
6867
  }
6704
- Tooltip.configure = configure;
6705
- Tooltip.show = GardState.Facet.define({
6706
- enables: [tooltipPlugin, styles]
6707
- });
6708
- function get(wg, tooltip) {
6709
- let plugin = wg.plugin(tooltipPlugin);
6710
- if (!plugin)
6711
- return null;
6712
- let found = plugin.manager.tooltips.findIndex(typeof tooltip == "function" ? p => p.create == tooltip : p => p == tooltip);
6713
- return found < 0 ? null : plugin.manager.tooltipViews[found];
6868
+ get children() { return null; }
6869
+ run(wg) {
6870
+ PositioningHint.runWith(() => this.dom.getBoundingClientRect(), () => {
6871
+ Command.dispatch(wg, this.item.run);
6872
+ });
6714
6873
  }
6715
- Tooltip.get = get;
6716
- function reposition(wg) {
6717
- let plugin = wg.plugin(tooltipPlugin);
6718
- if (plugin)
6719
- plugin.maybeMeasure();
6874
+ }
6875
+ class BarControl {
6876
+ item;
6877
+ dom;
6878
+ focusDOM;
6879
+ flags = 0;
6880
+ index = 0;
6881
+ constructor(item, wg, done) {
6882
+ this.item = item;
6883
+ let { dom, focus } = item.render(wg, done);
6884
+ this.dom = dom;
6885
+ this.focusDOM = focus || dom;
6886
+ this.focusDOM.tabIndex = -1;
6720
6887
  }
6721
- Tooltip.reposition = reposition;
6722
- function hover(source, options = {}) {
6723
- let setHover = Transaction.Effect.define();
6724
- let hoverState = GardState.Field.define({
6725
- create() { return []; },
6726
- update(value, tr) {
6727
- if (value.length) {
6728
- if (options.hideOnChange && (tr.docChanged || tr.selection))
6729
- value = [];
6730
- else if (options.hideOn)
6731
- value = value.filter(v => !options.hideOn(tr, v));
6732
- if (tr.docChanged) {
6733
- let mapped = [];
6734
- for (let tooltip of value) {
6735
- let newPos = tr.changes.mapPos(tooltip.pos, -1, "around");
6736
- if (newPos != null) {
6737
- let copy = Object.assign(Object.create(null), tooltip);
6738
- copy.pos = newPos;
6739
- if (copy.end != null)
6740
- copy.end = tr.changes.mapPos(copy.end);
6741
- mapped.push(copy);
6742
- }
6743
- }
6744
- value = mapped;
6745
- }
6746
- }
6747
- for (let effect of tr.effects) {
6748
- if (effect.is(setHover))
6749
- value = effect.value;
6750
- if (effect.is(closeHoverTooltipEffect))
6751
- value = [];
6752
- }
6753
- return value;
6754
- },
6755
- provide: f => showHoverTooltip.from(f)
6756
- });
6757
- return {
6758
- active: hoverState,
6759
- extension: [
6760
- hoverState,
6761
- Wordgard.Plugin.define(wg => new HoverPlugin(wg, source, hoverState, setHover, options.hoverTime || 300)),
6762
- showHoverTooltipHost
6763
- ]
6764
- };
6888
+ update(flags, wg, update) {
6889
+ if (flags != this.flags) {
6890
+ if ((flags & 32) != (this.flags & 32))
6891
+ this.dom.style.display = flags & 32 ? "none" : "";
6892
+ if ((flags & 8) != (this.flags & 8) && this.item.setEnabled)
6893
+ this.item.setEnabled(this.dom, !(flags & 8));
6894
+ if ((flags & 4) != (this.flags & 4)) {
6895
+ if (flags & 4)
6896
+ this.focusDOM.setAttribute("aria-selected", "true");
6897
+ else
6898
+ this.focusDOM.removeAttribute("aria-selected");
6899
+ this.focusDOM.tabIndex = flags & 4 ? 0 : -1;
6900
+ }
6901
+ this.flags = flags;
6902
+ }
6765
6903
  }
6766
- Tooltip.hover = hover;
6767
- (function (hover) {
6768
- function has(state) {
6769
- return state.facet(showHoverTooltip).some(x => x);
6904
+ get children() { return null; }
6905
+ get run() { return null; }
6906
+ }
6907
+ class BarSubmenu {
6908
+ item;
6909
+ dom;
6910
+ button;
6911
+ list;
6912
+ flags = 0;
6913
+ activeChild = -3;
6914
+ index = 0;
6915
+ children;
6916
+ constructor(item, children, wg) {
6917
+ this.item = item;
6918
+ this.dom = document.createElement("wg-submenu");
6919
+ this.button = this.dom.appendChild(document.createElement("button"));
6920
+ this.button.type = "button";
6921
+ this.button.tabIndex = -1;
6922
+ this.button.className = "wg-menu-button";
6923
+ this.button.setAttribute("aria-haspopup", "true");
6924
+ this.button.setAttribute("aria-expanded", "false");
6925
+ if (item.description) {
6926
+ let desc = typeof item.description == "function" ? item.description(wg.state) : item.description;
6927
+ this.dom.title = desc;
6928
+ this.dom.setAttribute("aria-label", desc);
6929
+ }
6930
+ if (item.label) {
6931
+ labelButton(wg, this.button, item.label);
6932
+ this.activeChild = -2;
6933
+ }
6934
+ if (item.width != null)
6935
+ this.dom.style.setProperty("--wg-submenu-width", item.width + "ch");
6936
+ if (item.arrow)
6937
+ this.button.classList.add("wg-submenu-arrow");
6938
+ this.list = this.dom.appendChild(document.createElement("wg-menu-list"));
6939
+ this.list.style.display = "none";
6940
+ this.list.role = "menu";
6941
+ this.list.id = id("wg-popup");
6942
+ this.list.setAttribute("aria-label", this.button.title);
6943
+ this.button.setAttribute("aria-controls", this.list.id);
6944
+ this.children = children.filter((ch) => !(ch instanceof BarSpacer));
6945
+ for (let child of children) {
6946
+ this.list.appendChild(child.dom);
6947
+ if (!(child instanceof BarSpacer))
6948
+ child.focusDOM.role = "menuitem";
6949
+ }
6950
+ }
6951
+ get focusDOM() { return this.button; }
6952
+ update(flags, wg) {
6953
+ if (flags != this.flags) {
6954
+ if ((flags & 32) != (this.flags & 32))
6955
+ this.dom.style.display = flags & 32 ? "none" : "";
6956
+ if ((flags & 8) != (this.flags & 8)) {
6957
+ if (flags & 8)
6958
+ this.button.setAttribute("aria-disabled", "true");
6959
+ else
6960
+ this.button.removeAttribute("aria-disabled");
6961
+ }
6962
+ if ((flags & 4) != (this.flags & 4)) {
6963
+ if (flags & 4)
6964
+ this.button.setAttribute("aria-selected", "true");
6965
+ else
6966
+ this.button.removeAttribute("aria-selected");
6967
+ this.button.tabIndex = flags & 4 ? 0 : -1;
6968
+ }
6969
+ if ((flags & 2) != (this.flags & 2))
6970
+ this.list.style.display = flags & 2 ? "" : "none";
6971
+ this.flags = flags;
6770
6972
  }
6771
- hover.has = has;
6772
- hover.closeAll = closeHoverTooltipEffect.of(null);
6773
- })(hover = Tooltip.hover || (Tooltip.hover = {}));
6774
- ;return Tooltip})({});
6775
- const noOffset = { x: 0, y: 0 };
6776
- const showHoverTooltip = /*@__PURE__*/GardState.Facet.define({
6777
- combine: inputs => inputs.reduce((a, i) => a.concat(i), [])
6778
- });
6779
- class HoverTooltipHost {
6780
- wg;
6781
- manager;
6973
+ if (this.activeChild != -2) {
6974
+ let activeChild = this.children.findIndex(ch => ch.flags & 16);
6975
+ if (this.activeChild != activeChild) {
6976
+ this.activeChild = activeChild;
6977
+ let label = activeChild < 0 ? this.item.defaultLabel : this.children[activeChild].item.label;
6978
+ labelButton(wg, this.button, label);
6979
+ }
6980
+ }
6981
+ }
6982
+ get run() { return null; }
6983
+ }
6984
+ class BarSpacer {
6782
6985
  dom;
6783
- connected = false;
6784
- static create(wg) {
6785
- return new HoverTooltipHost(wg);
6986
+ constructor() {
6987
+ this.dom = document.createElement("wg-menu-spacer");
6786
6988
  }
6989
+ }
6990
+ function instantiate(item, bar, flat) {
6991
+ let elt;
6992
+ if (item instanceof Menu.Submenu.Resolved)
6993
+ elt = new BarSubmenu(item.item, item.content.map(i => instantiate(i, bar, flat)), bar.wg);
6994
+ else if (item === "|")
6995
+ return new BarSpacer();
6996
+ else if (item instanceof Menu.Button)
6997
+ elt = new BarButton(item, bar.wg);
6998
+ else
6999
+ elt = new BarControl(item, bar.wg, () => bar.up());
7000
+ elt.index = flat.length;
7001
+ flat.push(elt);
7002
+ return elt;
7003
+ }
7004
+ class MenuBar {
7005
+ wg;
7006
+ dom;
7007
+ focusTimeout = -1;
7008
+ items;
6787
7009
  constructor(wg) {
6788
7010
  this.wg = wg;
6789
- this.dom = document.createElement("wg-tooltip-hover");
6790
- this.manager = new TooltipViewManager(wg, showHoverTooltip, (t, p) => this.createHostedView(t, p), t => t.dom.remove());
6791
- }
6792
- createHostedView(tooltip, prev) {
6793
- let hostedView = tooltip.create(this.wg);
6794
- hostedView.dom.classList.add("wg-tooltip-section");
6795
- this.dom.insertBefore(hostedView.dom, prev ? prev.dom.nextSibling : this.dom.firstChild);
6796
- if (this.connected && hostedView.connect)
6797
- hostedView.connect(this.wg);
6798
- return hostedView;
6799
- }
6800
- connect(wg) {
6801
- for (let t of this.manager.tooltipViews)
6802
- t.connect?.(wg);
6803
- this.connected = true;
6804
- }
6805
- disconnect(wg) {
6806
- for (let t of this.manager.tooltipViews)
6807
- t.disconnect?.(wg);
6808
- this.connected = false;
7011
+ this.dom = document.createElement("wg-menubar");
7012
+ this.dom.role = "toolbar";
7013
+ this.dom.addEventListener("keydown", this.key.bind(this));
7014
+ this.dom.addEventListener("mousedown", this.click.bind(this));
7015
+ this.dom.addEventListener("focusout", this.focusout.bind(this));
7016
+ this.items = wg.state.facet(Menu.Item.source);
7017
+ this.init();
7018
+ this.globalClick = this.globalClick.bind(this);
6809
7019
  }
6810
- positioned(space) {
6811
- for (let hostedView of this.manager.tooltipViews) {
6812
- if (hostedView.positioned)
6813
- hostedView.positioned(space);
6814
- }
7020
+ static create(wg) { return new MenuBar(wg); }
7021
+ init() {
7022
+ let elts = [];
7023
+ this.elts = elts;
7024
+ let children = Menu.resolve(this.items, this.wg.state.facet(barTemplate)).map(i => instantiate(i, this, elts));
7025
+ this.children = children.filter((ch) => !(ch instanceof BarSpacer));
7026
+ for (let elt of children)
7027
+ this.dom.appendChild(elt.dom);
7028
+ this.selection = this.children.length ? [this.children[0]] : [];
7029
+ this.updateElts(true, this.selection);
6815
7030
  }
6816
7031
  update(update) {
6817
- this.manager.update(update);
7032
+ let items = update.state.facet(Menu.Item.source);
7033
+ if (items != this.items ||
7034
+ update.startState.facet(barTemplate) != update.state.facet(barTemplate) ||
7035
+ update.startState.textLTR != update.state.textLTR ||
7036
+ PhraseSet.didChange(update.startState, update.state)) {
7037
+ this.items = items;
7038
+ this.dom.textContent = "";
7039
+ this.init();
7040
+ }
7041
+ else {
7042
+ this.updateElts(update, this.selection);
7043
+ if (update.startState.facet(KeyBinding.source) != update.state.facet(KeyBinding.source)) {
7044
+ for (let elt of this.elts)
7045
+ if (elt instanceof BarButton)
7046
+ elt.setDesc(update.editor);
7047
+ }
7048
+ }
6818
7049
  }
6819
- remove(wg) {
6820
- for (let t of this.manager.tooltipViews)
6821
- t.remove?.(wg);
7050
+ connect() {
7051
+ this.dom.setAttribute("aria-controls", this.wg.contentDOM.id);
6822
7052
  }
6823
- passProp(name) {
6824
- let value = undefined;
6825
- for (let view of this.manager.tooltipViews) {
6826
- let given = view[name];
6827
- if (given !== undefined) {
6828
- if (value === undefined)
6829
- value = given;
6830
- else if (value !== given)
6831
- return undefined;
7053
+ updateElts(update, selection) {
7054
+ let { state } = this.wg;
7055
+ let changed = typeof update == "boolean" ? update : update.docChanged || update.selectionSet;
7056
+ let updateObj = typeof update == "boolean" ? null : update;
7057
+ for (let i = 0; i < this.elts.length; i++) {
7058
+ let elt = this.elts[i], flags;
7059
+ if (update && (update === true || (elt.item.updateFor ? update.transactions.some(tr => elt.item.updateFor(tr)) : changed))) {
7060
+ flags = ((elt.item.select ? elt.item.select(state) : true) ? 0 : 32) |
7061
+ ((elt.item.enable ? elt.item.enable(state) : true) ? 0 : 8) |
7062
+ ((elt.item instanceof Menu.Button && elt.item.active ? elt.item.active(state) : false) ? 16 : 0);
6832
7063
  }
7064
+ else {
7065
+ flags = elt.flags & (32 | 8 | 16);
7066
+ }
7067
+ if (selection.length) {
7068
+ let selected = selection.indexOf(elt);
7069
+ if (selected == selection.length - 1)
7070
+ flags |= 4;
7071
+ else if (selected > -1)
7072
+ flags |= 2;
7073
+ }
7074
+ elt.update(flags, this.wg, updateObj);
7075
+ }
7076
+ if (update && selection.some(e => e.flags & 32)) {
7077
+ let reset = selection[0].flags & 32 ? findChild(this.children, true) : selection[0];
7078
+ this.setSelection(reset ? [reset] : [], this.dom.contains(this.wg.root.activeElement));
6833
7079
  }
6834
- return value;
6835
7080
  }
6836
- get offset() { return this.passProp("offset"); }
6837
- get getCoords() { return this.passProp("getCoords"); }
6838
- get overlap() { return this.passProp("overlap"); }
6839
- get resize() { return this.passProp("resize"); }
6840
- }
6841
- const showHoverTooltipHost = /*@__PURE__*/Tooltip.show.compute(state => {
6842
- let tooltips = state.facet(showHoverTooltip);
6843
- if (tooltips.length === 0)
6844
- return null;
6845
- return {
6846
- pos: Math.min(...tooltips.map(t => t.pos)),
6847
- end: Math.max(...tooltips.map(t => t.end ?? t.pos)),
6848
- create: HoverTooltipHost.create,
6849
- above: tooltips[0].above,
6850
- arrow: tooltips.some(t => t.arrow),
6851
- };
6852
- });
6853
- class HoverPlugin {
6854
- wg;
6855
- source;
6856
- field;
6857
- setHover;
6858
- hoverTime;
6859
- lastMove;
6860
- hoverTimeout = -1;
6861
- restartTimeout = -1;
6862
- pending = null;
6863
- constructor(wg, source, field, setHover, hoverTime) {
6864
- this.wg = wg;
6865
- this.source = source;
6866
- this.field = field;
6867
- this.setHover = setHover;
6868
- this.hoverTime = hoverTime;
6869
- this.lastMove = { x: 0, y: 0, target: wg.dom, time: 0 };
6870
- this.checkHover = this.checkHover.bind(this);
6871
- wg.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
6872
- wg.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
7081
+ setSelection(selection, focus = true) {
7082
+ this.updateElts(false, selection);
7083
+ if (selection.length > 1 && this.selection.length <= 1)
7084
+ this.dom.ownerDocument.addEventListener("mousedown", this.globalClick);
7085
+ this.selection = selection;
7086
+ if (focus && selection.length)
7087
+ selection[selection.length - 1].focusDOM.focus();
6873
7088
  }
6874
- update() {
6875
- if (this.pending) {
6876
- this.pending = null;
6877
- clearTimeout(this.restartTimeout);
6878
- this.restartTimeout = setTimeout(() => this.startHover(), 20);
7089
+ key(event) {
7090
+ if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented)
7091
+ return;
7092
+ let sLen = this.selection.length;
7093
+ if (event.key == "ArrowLeft" || event.key == "ArrowRight") {
7094
+ if (sLen) {
7095
+ let forward = (event.key == "ArrowRight") == (getComputedStyle(this.dom).direction == "ltr");
7096
+ let next = findNextChild(this.children, this.selection[0], forward ? 1 : -1);
7097
+ this.setSelection(next ? [next] : []);
7098
+ }
7099
+ }
7100
+ else if (event.key == "ArrowDown" || event.key == "ArrowUp") {
7101
+ if (sLen > 1) {
7102
+ let parent = this.selection[sLen - 2];
7103
+ let next = findNextChild(parent.children, this.selection[sLen - 1], event.key == "ArrowUp" ? -1 : 1);
7104
+ if (next)
7105
+ this.setSelection(this.selection.slice(0, sLen - 1).concat(next));
7106
+ }
7107
+ else if (sLen == 1 && this.selection[0].children) {
7108
+ let inner = defaultChild(this.selection[0].children);
7109
+ if (inner)
7110
+ this.setSelection([this.selection[0], inner]);
7111
+ }
7112
+ }
7113
+ else if (event.key == "Home" || event.key == "End") {
7114
+ let child = findChild(sLen > 1 ? this.selection[sLen - 2].children : this.children, event.key == "Home");
7115
+ if (child)
7116
+ this.setSelection(this.selection.slice(0, sLen - 1).concat(child));
7117
+ }
7118
+ else if (event.key == " " || event.key == "Enter") {
7119
+ if (sLen) {
7120
+ let child = this.selection[sLen - 1];
7121
+ if (child.flags & 8) ;
7122
+ else if (child.children) {
7123
+ let inner = defaultChild(child.children);
7124
+ if (inner)
7125
+ this.setSelection(this.selection.concat(inner));
7126
+ }
7127
+ else {
7128
+ if (child.run)
7129
+ child.run(this.wg);
7130
+ this.setSelection([this.selection[0]]);
7131
+ }
7132
+ }
7133
+ }
7134
+ else if (event.key == "Escape" && sLen > 1) {
7135
+ this.setSelection(this.selection.slice(0, sLen - 1));
7136
+ }
7137
+ else {
7138
+ return;
6879
7139
  }
7140
+ event.preventDefault();
6880
7141
  }
6881
- get active() {
6882
- return this.wg.state.field(this.field);
6883
- }
6884
- checkHover() {
6885
- this.hoverTimeout = -1;
6886
- if (this.active.length)
7142
+ click(event) {
7143
+ if (event.defaultPrevented)
6887
7144
  return;
6888
- let hovered = Date.now() - this.lastMove.time;
6889
- if (hovered < this.hoverTime)
6890
- this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
6891
- else
6892
- this.startHover();
6893
- }
6894
- startHover() {
6895
- clearTimeout(this.restartTimeout);
6896
- let { wg, lastMove } = this;
6897
- let { pos, side } = wg.posAtCoords(lastMove);
6898
- let open = this.source(wg, pos, side || -1);
6899
- if (open?.then) {
6900
- let pending = this.pending = { pos };
6901
- open.then(result => {
6902
- if (this.pending == pending) {
6903
- this.pending = null;
6904
- if (result && !(Array.isArray(result) && !result.length))
6905
- wg.dispatch({ effects: this.setHover.of(Array.isArray(result) ? result : [result]) });
7145
+ let target, idx;
7146
+ for (let node = event.target;; node = node.parentNode) {
7147
+ if (!node || node == this.dom)
7148
+ return;
7149
+ target = this.elts.find(e => e.dom == node);
7150
+ if (target)
7151
+ break;
7152
+ }
7153
+ if (target.flags & 8) ;
7154
+ else if (target.children) {
7155
+ if ((idx = this.selection.indexOf(target)) > -1 && idx < this.selection.length - 1) {
7156
+ this.setSelection(this.selection.slice(0, idx + 1), false);
7157
+ }
7158
+ else {
7159
+ for (let i = 0; i < this.selection.length; i++) {
7160
+ let { children } = i ? this.selection[i - 1] : this;
7161
+ if (children.includes(target)) {
7162
+ let next = defaultChild(target.children);
7163
+ if (next)
7164
+ this.setSelection(this.selection.slice(0, i).concat([target, next]), false);
7165
+ }
6906
7166
  }
6907
- }, e => logException(wg.state, e, "hover tooltip"));
7167
+ }
6908
7168
  }
6909
- else if (open && !(Array.isArray(open) && !open.length)) {
6910
- wg.dispatch({ effects: this.setHover.of(Array.isArray(open) ? open : [open]) });
7169
+ else {
7170
+ if (target.run)
7171
+ target.run(this.wg);
7172
+ this.setSelection(this.children.includes(target) ? [target] : this.selection.length ? [this.selection[0]] : [], false);
6911
7173
  }
7174
+ event.preventDefault();
6912
7175
  }
6913
- get tooltip() {
6914
- let plugin = this.wg.plugin(tooltipPlugin);
6915
- let index = plugin ? plugin.manager.tooltips.findIndex(t => t.create == HoverTooltipHost.create) : -1;
6916
- return index > -1 ? plugin.manager.tooltipViews[index] : null;
6917
- }
6918
- mousemove(event) {
6919
- this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
6920
- if (this.hoverTimeout < 0)
6921
- this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
6922
- let { active, tooltip } = this;
6923
- if (active.length && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
6924
- let { pos } = active[0] || this.pending, end = active[0]?.end ?? pos;
6925
- if ((pos == end ? this.wg.posAtCoords(this.lastMove).pos != pos
6926
- : !isOverRange(this.wg, pos, end, event.clientX, event.clientY))) {
6927
- this.wg.dispatch({ effects: this.setHover.of([]) });
6928
- this.pending = null;
6929
- }
7176
+ globalClick(event) {
7177
+ if (!event.composedPath().includes(this.dom)) {
7178
+ this.dom.ownerDocument.removeEventListener("mousedown", this.globalClick);
7179
+ if (this.selection.length > 1)
7180
+ this.setSelection([this.selection[0]], false);
6930
7181
  }
6931
7182
  }
6932
- mouseleave(event) {
6933
- clearTimeout(this.hoverTimeout);
6934
- this.hoverTimeout = -1;
6935
- let { active } = this;
6936
- if (active.length) {
6937
- let { tooltip } = this;
6938
- let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
6939
- if (!inTooltip)
6940
- this.wg.dispatch({ effects: this.setHover.of([]) });
6941
- else
6942
- this.watchTooltipLeave(tooltip.dom);
7183
+ focusout() {
7184
+ clearTimeout(this.focusTimeout);
7185
+ if (this.selection.length > 1) {
7186
+ this.focusTimeout = setTimeout(() => {
7187
+ let active = this.wg.root.activeElement;
7188
+ for (let i = 0; i < this.selection.length - 1; i++) {
7189
+ if (!active || !this.selection[i].dom.contains(active)) {
7190
+ this.setSelection([this.selection[0]], false);
7191
+ break;
7192
+ }
7193
+ }
7194
+ }, 20);
6943
7195
  }
6944
7196
  }
6945
- watchTooltipLeave(tooltip) {
6946
- let watch = (event) => {
6947
- tooltip.removeEventListener("mouseleave", watch);
6948
- if (this.active.length && !this.wg.dom.contains(event.relatedTarget))
6949
- this.wg.dispatch({ effects: this.setHover.of([]) });
6950
- };
6951
- tooltip.addEventListener("mouseleave", watch);
7197
+ up() {
7198
+ if (this.selection.length > 1)
7199
+ this.setSelection([this.selection[0]], false);
6952
7200
  }
6953
- remove() {
6954
- clearTimeout(this.hoverTimeout);
6955
- this.wg.dom.removeEventListener("mouseleave", this.mouseleave);
6956
- this.wg.dom.removeEventListener("mousemove", this.mousemove);
7201
+ get top() { return true; }
7202
+ }
7203
+ const menuBarPanel = /*@__PURE__*/(() => Panel.show.of(MenuBar.create))();
7204
+ function findChild(children, start) {
7205
+ for (let i = start ? 0 : children.length - 1; start ? i < children.length : i >= 0; start ? i++ : i--) {
7206
+ let child = children[i];
7207
+ if (!(child.flags & 8))
7208
+ return child;
6957
7209
  }
7210
+ return null;
6958
7211
  }
6959
- const tooltipMargin = 4;
6960
- function isInTooltip(tooltip, event) {
6961
- let { left, right, top, bottom } = tooltip.getBoundingClientRect(), arrow;
6962
- if (arrow = tooltip.querySelector(".wg-tooltip-arrow")) {
6963
- let arrowRect = arrow.getBoundingClientRect();
6964
- top = Math.min(arrowRect.top, top);
6965
- bottom = Math.max(arrowRect.bottom, bottom);
7212
+ function findNextChild(children, child, dir) {
7213
+ let index = children.indexOf(child);
7214
+ if (index < 0)
7215
+ return null;
7216
+ for (let i = index + dir;; i += dir) {
7217
+ if (i < 0)
7218
+ i = children.length - 1;
7219
+ else if (i >= children.length)
7220
+ i = 0;
7221
+ if (i == index)
7222
+ return null;
7223
+ let child = children[i];
7224
+ if (!(child.flags & 32))
7225
+ return child;
6966
7226
  }
6967
- return event.clientX >= left - tooltipMargin && event.clientX <= right + tooltipMargin &&
6968
- event.clientY >= top - tooltipMargin && event.clientY <= bottom + tooltipMargin;
6969
7227
  }
6970
- function isOverRange(wg, from, to, x, y, margin) {
6971
- let rect = wg.contentDOM.getBoundingClientRect();
6972
- if (rect.left > x || rect.right < x || rect.top > y || rect.bottom < y)
6973
- return false;
6974
- let pos = wg.posAtCoords({ x, y }).pos;
6975
- return pos >= from && pos <= to;
7228
+ function defaultChild(children) {
7229
+ return children.length ? children.find(ch => ch.flags & 16) || children[0] : null;
7230
+ }
7231
+ const theme = /*@__PURE__*/Wordgard.styles({
7232
+ "&": {
7233
+ "--wg-menu-item-size": "20px"
7234
+ },
7235
+ "&light": {
7236
+ "--wg-menu-color": "#555"
7237
+ },
7238
+ "&dark": {
7239
+ "--wg-menu-color": "#ccc"
7240
+ },
7241
+ "wg-menubar": {
7242
+ display: "flex",
7243
+ flexWrap: "wrap",
7244
+ gap: "5px",
7245
+ padding: "3px",
7246
+ color: "var(--wg-menu-color)",
7247
+ borderBottom: "1px solid var(--wg-border-color)",
7248
+ },
7249
+ ".wg-menu-button:focus": {
7250
+ outline: "1.5px solid var(--wg-highlight-color)"
7251
+ },
7252
+ ".wg-menu-button": {
7253
+ border: 0,
7254
+ padding: "3px",
7255
+ borderRadius: "4px",
7256
+ boxSizing: "content-box",
7257
+ backgroundColor: "transparent",
7258
+ font: "inherit",
7259
+ color: "inherit",
7260
+ height: "var(--wg-menu-item-size)",
7261
+ textAlign: "left",
7262
+ "&[aria-disabled]": {
7263
+ opacity: "0.3"
7264
+ },
7265
+ "&[aria-pressed]": {
7266
+ color: "var(--wg-highlight-color)"
7267
+ },
7268
+ "&:hover": {
7269
+ backgroundColor: "#88888820",
7270
+ },
7271
+ },
7272
+ ".wg-button-label": {
7273
+ display: "inline-block",
7274
+ minWidth: "var(--wg-submenu-width)",
7275
+ padding: "0 3px",
7276
+ },
7277
+ "svg.wg-icon": {
7278
+ fill: "currentColor",
7279
+ width: "var(--wg-menu-item-size)",
7280
+ height: "var(--wg-menu-item-size)",
7281
+ },
7282
+ "wg-menu-spacer": {
7283
+ display: "block",
7284
+ width: "7px"
7285
+ },
7286
+ "wg-submenu": {
7287
+ display: "block",
7288
+ position: "relative",
7289
+ lineHeight: ".6",
7290
+ whiteSpace: "nowrap"
7291
+ },
7292
+ "wg-menu-list": {
7293
+ display: "flex",
7294
+ flexDirection: "column",
7295
+ alignItems: "stretch",
7296
+ gap: "5px",
7297
+ padding: "5px",
7298
+ borderRadius: "4px",
7299
+ backgroundColor: "var(--wg-panel-color)",
7300
+ position: "absolute",
7301
+ zIndex: "10",
7302
+ top: "100%",
7303
+ boxShadow: "0 2px 8px 0 rgba(128, 128, 128, 0.2)",
7304
+ "& wg-menu-list": {
7305
+ left: "100%",
7306
+ top: 0
7307
+ }
7308
+ },
7309
+ ".wg-submenu-arrow:after": {
7310
+ padding: "0 2px",
7311
+ fontSize: "80%",
7312
+ verticalAlign: "10%",
7313
+ opacity: "0.4",
7314
+ content: "'▾'"
7315
+ },
7316
+ ".wg-submenu-arrow.wg-submenu-arrow-open:after": {
7317
+ content: "'▴'"
7318
+ },
7319
+ });
7320
+ const barTemplate = /*@__PURE__*/GardState.Facet.define({
7321
+ combine: inputs => inputs.length ? inputs[0] : [Menu.Group.top.template()]
7322
+ });
7323
+ function menuBar(config = {}) {
7324
+ let extensions = [menuBarPanel, theme];
7325
+ if (config.template)
7326
+ extensions.push(barTemplate.of(Array.isArray(config.template) ? config.template : [config.template]));
7327
+ return extensions;
6976
7328
  }
6977
7329
 
6978
7330
  const inputRule = /*@__PURE__*/GardState.Facet.define();
@@ -7237,4 +7589,4 @@ function dropCursor() {
7237
7589
  return [dropCursorPos, drawDropCursor];
7238
7590
  }
7239
7591
 
7240
- export { Decoration, Dialog, InputRule, KeyBinding, Panel, Tooltip, Widget, Wordgard, decoratePlots, dropCursor, menuBar, placeholder };
7592
+ export { Decoration, Dialog, InputRule, KeyBinding, Panel, PositioningHint, Tooltip, Widget, Wordgard, decoratePlots, dropCursor, keyBindingHelp, menuBar, placeholder };