fable-editor 1.2.6 → 1.2.8
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/angular/core/editor.d.ts +17 -1
- package/dist/angular/esm2022/core/editor.mjs +66 -15
- package/dist/angular/fesm2022/fable-editor.mjs +65 -14
- package/dist/angular/fesm2022/fable-editor.mjs.map +1 -1
- package/dist/core/editor.css +15 -2
- package/dist/core/editor.d.ts +17 -1
- package/dist/core/editor.d.ts.map +1 -1
- package/dist/core/index.cjs +3 -3
- package/dist/core/index.mjs +105 -83
- package/dist/react/index.cjs +5 -5
- package/dist/react/index.mjs +71 -49
- package/package.json +1 -1
- package/style.css +15 -2
|
@@ -1828,7 +1828,12 @@ class FableEditor {
|
|
|
1828
1828
|
foreColor = '#000000';
|
|
1829
1829
|
backColor = '#FACC15';
|
|
1830
1830
|
openPop = null;
|
|
1831
|
-
|
|
1831
|
+
/** Stack of currently-open flyout submenus, shallowest first (e.g. for
|
|
1832
|
+
* Table > Cell > Cell background: [Cell flyout, Cell background flyout]).
|
|
1833
|
+
* A single slot isn't enough past one level deep - opening a 2nd-level
|
|
1834
|
+
* flyout would remove the 1st-level one still needed to bridge visually
|
|
1835
|
+
* back to the top-level popup, leaving a gap. */
|
|
1836
|
+
subStack = [];
|
|
1832
1837
|
popAnchor = null;
|
|
1833
1838
|
dlgOvl = null;
|
|
1834
1839
|
tblActive = null;
|
|
@@ -2018,7 +2023,7 @@ class FableEditor {
|
|
|
2018
2023
|
if (this.openPop &&
|
|
2019
2024
|
!this.openPop.contains(e.target) &&
|
|
2020
2025
|
!this.popAnchor.contains(e.target) &&
|
|
2021
|
-
!
|
|
2026
|
+
!this.subContains(e.target)) {
|
|
2022
2027
|
this.closePop();
|
|
2023
2028
|
}
|
|
2024
2029
|
});
|
|
@@ -2175,7 +2180,17 @@ class FableEditor {
|
|
|
2175
2180
|
this.positionVidCtx();
|
|
2176
2181
|
this.positionCodeCtx();
|
|
2177
2182
|
});
|
|
2178
|
-
this.onWin('scroll', () => {
|
|
2183
|
+
this.onWin('scroll', (e) => {
|
|
2184
|
+
/* this fires for ANY scroll in the document (capture:true is what
|
|
2185
|
+
lets it catch a scrollable ancestor, not just the window) -
|
|
2186
|
+
including the popup's own overflow:auto scrolling through a long
|
|
2187
|
+
menu. Only close it for a scroll that happened outside the open
|
|
2188
|
+
menu/flyouts; otherwise scrolling a long dropdown would close it
|
|
2189
|
+
on the very first scroll tick instead of scrolling its content. */
|
|
2190
|
+
const t = e.target;
|
|
2191
|
+
const withinOpenMenu = t instanceof Node && ((this.openPop?.contains(t) ?? false) || this.subContains(t));
|
|
2192
|
+
if (!withinOpenMenu)
|
|
2193
|
+
this.closePop();
|
|
2179
2194
|
this.positionTableHandles();
|
|
2180
2195
|
this.positionImageHandles();
|
|
2181
2196
|
this.positionImgPhCtx();
|
|
@@ -2259,7 +2274,7 @@ class FableEditor {
|
|
|
2259
2274
|
(this.vidCtx && this.vidCtx.contains(e.target)) ||
|
|
2260
2275
|
(this.codeCtx && this.codeCtx.contains(e.target)) ||
|
|
2261
2276
|
(this.openPop && this.openPop.contains(e.target)) ||
|
|
2262
|
-
|
|
2277
|
+
this.subContains(e.target))
|
|
2263
2278
|
return;
|
|
2264
2279
|
this.clearTableHandles();
|
|
2265
2280
|
this.clearImageHandles();
|
|
@@ -2600,8 +2615,28 @@ class FableEditor {
|
|
|
2600
2615
|
}
|
|
2601
2616
|
/* ---------------------------------------------------------- popups / menus */
|
|
2602
2617
|
closeSub() {
|
|
2603
|
-
this.
|
|
2604
|
-
|
|
2618
|
+
this.closeSubFrom(0);
|
|
2619
|
+
}
|
|
2620
|
+
/** Removes stacked flyouts from `level` (0 = shallowest) onward, keeping
|
|
2621
|
+
* anything shallower - e.g. hovering a sibling item inside the "Cell"
|
|
2622
|
+
* flyout should drop a deeper "Cell background" flyout but keep "Cell"
|
|
2623
|
+
* itself and the top-level popup it bridges to. */
|
|
2624
|
+
closeSubFrom(level) {
|
|
2625
|
+
while (this.subStack.length > level)
|
|
2626
|
+
this.subStack.pop().remove();
|
|
2627
|
+
}
|
|
2628
|
+
/** True if `node` is inside any currently-open flyout submenu (any depth). */
|
|
2629
|
+
subContains(node) {
|
|
2630
|
+
return this.subStack.some((el) => el.contains(node));
|
|
2631
|
+
}
|
|
2632
|
+
/** 0 for the top-level popup itself; otherwise this container's depth in
|
|
2633
|
+
* the flyout stack + 1. Used to know how many deeper flyouts to close
|
|
2634
|
+
* when opening or hovering within `container`. */
|
|
2635
|
+
subLevelOf(container) {
|
|
2636
|
+
if (container === this.openPop)
|
|
2637
|
+
return 0;
|
|
2638
|
+
const idx = this.subStack.indexOf(container);
|
|
2639
|
+
return idx === -1 ? this.subStack.length : idx + 1;
|
|
2605
2640
|
}
|
|
2606
2641
|
closePop() {
|
|
2607
2642
|
this.closeSub();
|
|
@@ -2616,7 +2651,17 @@ class FableEditor {
|
|
|
2616
2651
|
}
|
|
2617
2652
|
}
|
|
2618
2653
|
openSubFor(item, anchor) {
|
|
2619
|
-
|
|
2654
|
+
/* captured before truncating the stack - for a flyout nested inside
|
|
2655
|
+
another flyout (e.g. Table > Cell > Cell background), anchor is itself
|
|
2656
|
+
a descendant of the currently-open sub-popup; measuring after removing
|
|
2657
|
+
it would read a detached, zeroed-out rect and land the new flyout at
|
|
2658
|
+
the top-left corner of the screen */
|
|
2659
|
+
const r = anchor.getBoundingClientRect();
|
|
2660
|
+
/* only close flyouts deeper than the one anchor lives in - closing
|
|
2661
|
+
everything (the old behavior) would also remove that same containing
|
|
2662
|
+
flyout, leaving a visual gap back to the top-level popup */
|
|
2663
|
+
const parentPop = anchor.closest('.pop');
|
|
2664
|
+
this.closeSubFrom(parentPop ? this.subLevelOf(parentPop) : 0);
|
|
2620
2665
|
const sub = document.createElement('div');
|
|
2621
2666
|
sub.className = 'pop sub';
|
|
2622
2667
|
sub.dir = this.dir();
|
|
@@ -2629,7 +2674,6 @@ class FableEditor {
|
|
|
2629
2674
|
e.preventDefault();
|
|
2630
2675
|
});
|
|
2631
2676
|
document.body.appendChild(sub);
|
|
2632
|
-
const r = anchor.getBoundingClientRect();
|
|
2633
2677
|
const isRtl = this.dir() === 'rtl';
|
|
2634
2678
|
// overlap the parent item slightly so the pointer can travel into the flyout
|
|
2635
2679
|
let x = isRtl ? r.left - sub.offsetWidth + 2 : r.right - 2;
|
|
@@ -2638,7 +2682,7 @@ class FableEditor {
|
|
|
2638
2682
|
y = Math.max(8 + scrollY, Math.min(y, scrollY + innerHeight - sub.offsetHeight - 8));
|
|
2639
2683
|
sub.style.left = x + 'px';
|
|
2640
2684
|
sub.style.top = y + 'px';
|
|
2641
|
-
this.
|
|
2685
|
+
this.subStack.push(sub);
|
|
2642
2686
|
}
|
|
2643
2687
|
popup(anchor, build, cls) {
|
|
2644
2688
|
if (this.openPop && this.popAnchor === anchor) {
|
|
@@ -2654,7 +2698,11 @@ class FableEditor {
|
|
|
2654
2698
|
const r = anchor.getBoundingClientRect();
|
|
2655
2699
|
// align the menu with its control: at least as wide as the anchor
|
|
2656
2700
|
el.style.minWidth = Math.max(160, Math.round(r.width)) + 'px';
|
|
2657
|
-
|
|
2701
|
+
// clamp so a long menu (its own max-height/overflow makes it scrollable)
|
|
2702
|
+
// stays fully on-screen instead of running off the bottom of the viewport
|
|
2703
|
+
let y = r.bottom + scrollY + 2;
|
|
2704
|
+
y = Math.max(8 + scrollY, Math.min(y, scrollY + innerHeight - el.offsetHeight - 8));
|
|
2705
|
+
el.style.top = y + 'px';
|
|
2658
2706
|
const isRtl = this.dir() === 'rtl';
|
|
2659
2707
|
let x = isRtl ? r.right - el.offsetWidth : r.left;
|
|
2660
2708
|
x = Math.max(8, Math.min(x + scrollX, scrollX + innerWidth - el.offsetWidth - 8));
|
|
@@ -2686,8 +2734,11 @@ class FableEditor {
|
|
|
2686
2734
|
b.addEventListener('mouseenter', () => {
|
|
2687
2735
|
if (hasSub)
|
|
2688
2736
|
this.openSubFor(it, b);
|
|
2689
|
-
|
|
2690
|
-
|
|
2737
|
+
/* hovering a no-submenu item drops whatever deeper flyout was open
|
|
2738
|
+
from a previously-hovered sibling, but keeps el itself and any
|
|
2739
|
+
shallower ancestors intact */
|
|
2740
|
+
else
|
|
2741
|
+
this.closeSubFrom(this.subLevelOf(el));
|
|
2691
2742
|
it.hover?.(true);
|
|
2692
2743
|
});
|
|
2693
2744
|
b.addEventListener('mouseleave', () => it.hover?.(false));
|
|
@@ -3273,8 +3324,8 @@ class FableEditor {
|
|
|
3273
3324
|
buildToolbarRegistry() {
|
|
3274
3325
|
const tableBtn = this.tbtn(IC.tableic, this.t('quicktable'), () => this.tableGrid(tableBtn));
|
|
3275
3326
|
const registry = {
|
|
3276
|
-
undo: () => this.tbtn(IC.undo, this.t('undo'), () => this.exec('undo')),
|
|
3277
|
-
redo: () => this.tbtn(IC.redo, this.t('redo'), () => this.exec('redo')),
|
|
3327
|
+
undo: () => this.tbtn(IC.undo, this.t('undo'), () => this.exec('undo'), 'undo'),
|
|
3328
|
+
redo: () => this.tbtn(IC.redo, this.t('redo'), () => this.exec('redo'), 'redo'),
|
|
3278
3329
|
preview: () => this.tbtn(IC.prevw, this.t('preview'), () => this.previewDlg()),
|
|
3279
3330
|
print: () => this.tbtn(IC.printic, this.t('print'), () => this.printDoc()),
|
|
3280
3331
|
importword: () => this.tbtn(IC.wordic, this.t('importword'), () => this.pickWordDoc()),
|