fable-editor 1.2.7 → 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 -16
- package/dist/angular/fesm2022/fable-editor.mjs +65 -15
- package/dist/angular/fesm2022/fable-editor.mjs.map +1 -1
- package/dist/core/editor.css +14 -1
- 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 +14 -1
|
@@ -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,8 +2180,17 @@ class FableEditor {
|
|
|
2175
2180
|
this.positionVidCtx();
|
|
2176
2181
|
this.positionCodeCtx();
|
|
2177
2182
|
});
|
|
2178
|
-
this.onWin('scroll', () => {
|
|
2179
|
-
this
|
|
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();
|
|
2180
2194
|
this.positionTableHandles();
|
|
2181
2195
|
this.positionImageHandles();
|
|
2182
2196
|
this.positionImgPhCtx();
|
|
@@ -2260,7 +2274,7 @@ class FableEditor {
|
|
|
2260
2274
|
(this.vidCtx && this.vidCtx.contains(e.target)) ||
|
|
2261
2275
|
(this.codeCtx && this.codeCtx.contains(e.target)) ||
|
|
2262
2276
|
(this.openPop && this.openPop.contains(e.target)) ||
|
|
2263
|
-
|
|
2277
|
+
this.subContains(e.target))
|
|
2264
2278
|
return;
|
|
2265
2279
|
this.clearTableHandles();
|
|
2266
2280
|
this.clearImageHandles();
|
|
@@ -2601,8 +2615,28 @@ class FableEditor {
|
|
|
2601
2615
|
}
|
|
2602
2616
|
/* ---------------------------------------------------------- popups / menus */
|
|
2603
2617
|
closeSub() {
|
|
2604
|
-
this.
|
|
2605
|
-
|
|
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;
|
|
2606
2640
|
}
|
|
2607
2641
|
closePop() {
|
|
2608
2642
|
this.closeSub();
|
|
@@ -2617,7 +2651,17 @@ class FableEditor {
|
|
|
2617
2651
|
}
|
|
2618
2652
|
}
|
|
2619
2653
|
openSubFor(item, anchor) {
|
|
2620
|
-
|
|
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);
|
|
2621
2665
|
const sub = document.createElement('div');
|
|
2622
2666
|
sub.className = 'pop sub';
|
|
2623
2667
|
sub.dir = this.dir();
|
|
@@ -2630,7 +2674,6 @@ class FableEditor {
|
|
|
2630
2674
|
e.preventDefault();
|
|
2631
2675
|
});
|
|
2632
2676
|
document.body.appendChild(sub);
|
|
2633
|
-
const r = anchor.getBoundingClientRect();
|
|
2634
2677
|
const isRtl = this.dir() === 'rtl';
|
|
2635
2678
|
// overlap the parent item slightly so the pointer can travel into the flyout
|
|
2636
2679
|
let x = isRtl ? r.left - sub.offsetWidth + 2 : r.right - 2;
|
|
@@ -2639,7 +2682,7 @@ class FableEditor {
|
|
|
2639
2682
|
y = Math.max(8 + scrollY, Math.min(y, scrollY + innerHeight - sub.offsetHeight - 8));
|
|
2640
2683
|
sub.style.left = x + 'px';
|
|
2641
2684
|
sub.style.top = y + 'px';
|
|
2642
|
-
this.
|
|
2685
|
+
this.subStack.push(sub);
|
|
2643
2686
|
}
|
|
2644
2687
|
popup(anchor, build, cls) {
|
|
2645
2688
|
if (this.openPop && this.popAnchor === anchor) {
|
|
@@ -2655,7 +2698,11 @@ class FableEditor {
|
|
|
2655
2698
|
const r = anchor.getBoundingClientRect();
|
|
2656
2699
|
// align the menu with its control: at least as wide as the anchor
|
|
2657
2700
|
el.style.minWidth = Math.max(160, Math.round(r.width)) + 'px';
|
|
2658
|
-
|
|
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';
|
|
2659
2706
|
const isRtl = this.dir() === 'rtl';
|
|
2660
2707
|
let x = isRtl ? r.right - el.offsetWidth : r.left;
|
|
2661
2708
|
x = Math.max(8, Math.min(x + scrollX, scrollX + innerWidth - el.offsetWidth - 8));
|
|
@@ -2687,8 +2734,11 @@ class FableEditor {
|
|
|
2687
2734
|
b.addEventListener('mouseenter', () => {
|
|
2688
2735
|
if (hasSub)
|
|
2689
2736
|
this.openSubFor(it, b);
|
|
2690
|
-
|
|
2691
|
-
|
|
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));
|
|
2692
2742
|
it.hover?.(true);
|
|
2693
2743
|
});
|
|
2694
2744
|
b.addEventListener('mouseleave', () => it.hover?.(false));
|
|
@@ -3274,8 +3324,8 @@ class FableEditor {
|
|
|
3274
3324
|
buildToolbarRegistry() {
|
|
3275
3325
|
const tableBtn = this.tbtn(IC.tableic, this.t('quicktable'), () => this.tableGrid(tableBtn));
|
|
3276
3326
|
const registry = {
|
|
3277
|
-
undo: () => this.tbtn(IC.undo, this.t('undo'), () => this.exec('undo')),
|
|
3278
|
-
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'),
|
|
3279
3329
|
preview: () => this.tbtn(IC.prevw, this.t('preview'), () => this.previewDlg()),
|
|
3280
3330
|
print: () => this.tbtn(IC.printic, this.t('print'), () => this.printDoc()),
|
|
3281
3331
|
importword: () => this.tbtn(IC.wordic, this.t('importword'), () => this.pickWordDoc()),
|