@h-k-dev/angular-email-editor 0.0.2 → 0.0.3

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.
@@ -15626,12 +15626,16 @@ const createBubbleMenu = (options) => defineExtension({
15626
15626
  let mouseSelecting = false;
15627
15627
  let destroyed = false;
15628
15628
  let open = false;
15629
+ // Where the open menu stands (`anchor: 'first'`): the range or the
15630
+ // node it opened over, and the box measured there.
15631
+ let pinned = null;
15629
15632
  // Closed is said once: a caret moving through the text, or a press
15630
15633
  // elsewhere on the page, is no news to a host that already knows.
15631
15634
  const close = () => {
15632
15635
  if (!open)
15633
15636
  return;
15634
15637
  open = false;
15638
+ pinned = null;
15635
15639
  options.onStateChange({ isOpen: false, boundingBox: null });
15636
15640
  };
15637
15641
  // Whether the menu may show: not mid-drag, the host's say-so, and
@@ -15641,40 +15645,16 @@ const createBubbleMenu = (options) => defineExtension({
15641
15645
  !mouseSelecting &&
15642
15646
  (options.shouldShow || defaultBubbleShouldShow)(view.state) &&
15643
15647
  view.hasFocus();
15644
- // Reports the menu open over the current selection.
15645
- const show = () => {
15646
- // Asked again once the delay is over: in between, the host may
15647
- // have opened something of its own there (a click on a button
15648
- // opens its link editor) that its `shouldShow` now says no to.
15649
- if (!canShow())
15650
- return close();
15651
- // An image alone, or a button: the menu stands over the node
15652
- // itself.
15653
- const image = selectedImage(view.state);
15654
- const button = image ? null : selectedButton(view.state);
15655
- const node = image ?? button;
15656
- const dom = node && view.nodeDOM(node.pos);
15657
- if (dom && typeof dom.getBoundingClientRect === 'function') {
15658
- open = true;
15659
- options.onStateChange({
15660
- isOpen: true,
15661
- boundingBox: dom.getBoundingClientRect(),
15662
- kind: image ? 'image' : 'button',
15663
- });
15664
- return;
15665
- }
15666
- const { from, to } = view.state.selection;
15667
- // Get coordinates of the selection boundaries
15648
+ // The box of a range: from its start to its end, over every line it
15649
+ // takes — the virtual DOMRect the host's overlay anchors to.
15650
+ const rangeBox = (from, to) => {
15668
15651
  const start = view.coordsAtPos(from);
15669
15652
  const end = view.coordsAtPos(to, -1);
15670
- // Construct a virtual DOMRect representing the text selection.
15671
- // Angular CDK uses this to anchor the overlay!
15672
15653
  const top = Math.min(start.top, end.top);
15673
15654
  const bottom = Math.max(start.bottom, end.bottom);
15674
15655
  const left = Math.min(start.left, end.left);
15675
15656
  const right = Math.max(start.right, end.right);
15676
- // Construct a mathematically perfect virtual DOMRect
15677
- const boundingBox = {
15657
+ return {
15678
15658
  top,
15679
15659
  bottom,
15680
15660
  left,
@@ -15685,8 +15665,51 @@ const createBubbleMenu = (options) => defineExtension({
15685
15665
  y: top,
15686
15666
  toJSON: () => '',
15687
15667
  };
15668
+ };
15669
+ // The box of a node: its own element's, while it has one.
15670
+ const nodeBox = (pos) => {
15671
+ const dom = view.nodeDOM(pos);
15672
+ return dom && typeof dom.getBoundingClientRect === 'function'
15673
+ ? dom.getBoundingClientRect()
15674
+ : null;
15675
+ };
15676
+ // Reports the menu open over the current selection — standing
15677
+ // where it opened (`anchor: 'first'`), or over the selection of
15678
+ // the moment. `remeasure`: the pinned place has moved on screen
15679
+ // (a scroll), so its box is measured again.
15680
+ const show = (remeasure = false) => {
15681
+ // Asked again once the delay is over: in between, the host may
15682
+ // have opened something of its own there (a click on a button
15683
+ // opens its link editor) that its `shouldShow` now says no to.
15684
+ if (!canShow())
15685
+ return close();
15686
+ // An image alone, or a button: the menu is for the node, and
15687
+ // stands over the node itself — while it has an element to
15688
+ // stand over; text otherwise.
15689
+ const image = selectedImage(view.state);
15690
+ const button = image ? null : selectedButton(view.state);
15691
+ const node = image ?? button;
15692
+ const box = node ? nodeBox(node.pos) : null;
15693
+ const kind = box ? (image ? 'image' : 'button') : 'text';
15694
+ if (pinned && (options.anchor ?? 'first') === 'first') {
15695
+ if (remeasure) {
15696
+ // The same place, where it now is — clamped, in case the
15697
+ // document shrank under it since.
15698
+ const size = view.state.doc.content.size;
15699
+ const from = Math.min(pinned.from, size);
15700
+ pinned.box =
15701
+ (pinned.node ? nodeBox(from) : rangeBox(from, Math.min(pinned.to, size))) ??
15702
+ pinned.box;
15703
+ }
15704
+ options.onStateChange({ isOpen: true, boundingBox: pinned.box, kind });
15705
+ return;
15706
+ }
15707
+ const { from, to } = view.state.selection;
15708
+ pinned = box
15709
+ ? { from: node.pos, to: node.pos, node: true, box }
15710
+ : { from, to, node: false, box: rangeBox(from, to) };
15688
15711
  open = true;
15689
- options.onStateChange({ isOpen: true, boundingBox, kind: 'text' });
15712
+ options.onStateChange({ isOpen: true, boundingBox: pinned.box, kind });
15690
15713
  };
15691
15714
  const refresh = () => {
15692
15715
  if (destroyed)
@@ -15696,16 +15719,17 @@ const createBubbleMenu = (options) => defineExtension({
15696
15719
  close();
15697
15720
  return;
15698
15721
  }
15699
- // Open already: the menu follows the selection *now* — its kind
15700
- // and its box change in the same transaction the selection did,
15701
- // so what a host renders from the state and what it reads live
15702
- // from the editor (an image's alt, an action's enabled) never
15703
- // disagree. Delayed, the image's menu stood over a text range
15704
- // for the delay, showing the alt of an image no longer selected
15705
- // alone — empty — and its image tools disabled, before the
15706
- // text's menu replaced it. The delay is for *opening*: a range
15707
- // passing through, a drag's intermediate selections, never pop
15708
- // a menu of their own.
15722
+ // Open already: the menu answers the selection *now* — its kind
15723
+ // (and, following the selection, its box) changes in the same
15724
+ // transaction the selection did, so what a host renders from the
15725
+ // state and what it reads live from the editor (an image's alt,
15726
+ // an action's enabled) never disagree. Delayed, the image's menu
15727
+ // showed the alt of an image no longer selected alone — empty —
15728
+ // and its image tools disabled, for the delay, before the text's
15729
+ // menu replaced it. The delay is for *opening*: a range passing
15730
+ // through, a drag's intermediate selections, never pop a menu of
15731
+ // their own. Pinned (`anchor: 'first'`), the box stays where the
15732
+ // menu opened.
15709
15733
  if (open)
15710
15734
  return show();
15711
15735
  showTimer = setTimeout(show, options.updateDelay ?? 150);
@@ -15742,7 +15766,7 @@ const createBubbleMenu = (options) => defineExtension({
15742
15766
  close,
15743
15767
  refresh: () => {
15744
15768
  if (open)
15745
- show();
15769
+ show(true);
15746
15770
  },
15747
15771
  });
15748
15772
  return {