@elementor/editor-canvas 4.1.0-740 → 4.1.0-741

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { V1Element, V1ElementConfig, V1ElementModelProps } from '@elementor/editor-elements';
2
2
  import { TwingArrayLoader, TwingEnvironment } from '@elementor/twing';
3
+ import { Root } from 'react-dom/client';
3
4
  import * as _elementor_editor_props from '@elementor/editor-props';
4
5
  import { Props, PropValue, PropType, PropTypeKey, PropsSchema, AnyTransformable } from '@elementor/editor-props';
5
6
  import * as _elementor_utils from '@elementor/utils';
@@ -204,6 +205,8 @@ type ReplacementSettings = {
204
205
  id: string;
205
206
  element: HTMLElement;
206
207
  refreshView: () => void;
208
+ reactRoot: Root;
209
+ reactContainer: HTMLElement;
207
210
  };
208
211
 
209
212
  type CreateTemplatedElementTypeOptions = {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { V1Element, V1ElementConfig, V1ElementModelProps } from '@elementor/editor-elements';
2
2
  import { TwingArrayLoader, TwingEnvironment } from '@elementor/twing';
3
+ import { Root } from 'react-dom/client';
3
4
  import * as _elementor_editor_props from '@elementor/editor-props';
4
5
  import { Props, PropValue, PropType, PropTypeKey, PropsSchema, AnyTransformable } from '@elementor/editor-props';
5
6
  import * as _elementor_utils from '@elementor/utils';
@@ -204,6 +205,8 @@ type ReplacementSettings = {
204
205
  id: string;
205
206
  element: HTMLElement;
206
207
  refreshView: () => void;
208
+ reactRoot: Root;
209
+ reactContainer: HTMLElement;
207
210
  };
208
211
 
209
212
  type CreateTemplatedElementTypeOptions = {
package/dist/index.js CHANGED
@@ -2421,9 +2421,11 @@ function createNestedTemplatedElementView({
2421
2421
  });
2422
2422
  }
2423
2423
 
2424
+ // src/legacy/replacements/manager.ts
2425
+ var import_client = require("react-dom/client");
2426
+
2424
2427
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2425
2428
  var React6 = __toESM(require("react"));
2426
- var import_client = require("react-dom/client");
2427
2429
  var import_editor_elements5 = require("@elementor/editor-elements");
2428
2430
  var import_editor_props4 = require("@elementor/editor-props");
2429
2431
  var import_editor_v1_adapters12 = require("@elementor/editor-v1-adapters");
@@ -2442,6 +2444,8 @@ var ReplacementBase = class {
2442
2444
  type;
2443
2445
  id;
2444
2446
  refreshView;
2447
+ reactRoot;
2448
+ reactContainer;
2445
2449
  constructor(settings) {
2446
2450
  this.getSetting = settings.getSetting;
2447
2451
  this.setSetting = settings.setSetting;
@@ -2449,6 +2453,8 @@ var ReplacementBase = class {
2449
2453
  this.type = settings.type;
2450
2454
  this.id = settings.id;
2451
2455
  this.refreshView = settings.refreshView;
2456
+ this.reactRoot = settings.reactRoot;
2457
+ this.reactContainer = settings.reactContainer;
2452
2458
  }
2453
2459
  static getTypes() {
2454
2460
  return null;
@@ -2518,6 +2524,11 @@ var useOnClickOutsideIframe = (handleUnmount) => {
2518
2524
  };
2519
2525
  var useRenderToolbar = (ownerDocument, id) => {
2520
2526
  const [anchor, setAnchor] = (0, import_react11.useState)(null);
2527
+ (0, import_react11.useEffect)(() => {
2528
+ if (!anchor) {
2529
+ removeToolbarAnchor(ownerDocument, id);
2530
+ }
2531
+ }, [anchor, ownerDocument, id]);
2521
2532
  const onSelectionEnd = (view) => {
2522
2533
  const hasSelection = !view.state.selection.empty;
2523
2534
  removeToolbarAnchor(ownerDocument, id);
@@ -2527,7 +2538,10 @@ var useRenderToolbar = (ownerDocument, id) => {
2527
2538
  setAnchor(null);
2528
2539
  }
2529
2540
  };
2530
- return { onSelectionEnd, anchor };
2541
+ const clearAnchor = (0, import_react11.useCallback)(() => {
2542
+ setAnchor(null);
2543
+ }, []);
2544
+ return { onSelectionEnd, anchor, clearAnchor };
2531
2545
  };
2532
2546
  var createAnchorBasedOnSelection = (ownerDocument, id) => {
2533
2547
  const frameWindow = ownerDocument.defaultView;
@@ -2594,46 +2608,59 @@ var horizontalShifterMiddleware = {
2594
2608
  };
2595
2609
 
2596
2610
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2597
- var EDITOR_WRAPPER_SELECTOR = "inline-editor-wrapper";
2598
2611
  var CanvasInlineEditor = ({
2599
2612
  elementClasses,
2600
2613
  initialValue,
2601
2614
  expectedTag,
2602
2615
  rootElement,
2616
+ contentElement,
2603
2617
  id,
2604
2618
  setValue,
2605
- ...props
2619
+ requestDestroy
2606
2620
  }) => {
2621
+ const [active, setActive] = (0, import_react12.useState)(true);
2607
2622
  const [editor, setEditor] = (0, import_react12.useState)(null);
2608
- const { onSelectionEnd, anchor: toolbarAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2609
- const onBlur = () => {
2610
- removeToolbarAnchor(rootElement.ownerDocument, id);
2611
- props.onBlur();
2612
- };
2613
- useOnClickOutsideIframe(onBlur);
2614
- return /* @__PURE__ */ React5.createElement(import_ui4.ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement("style", null, `
2615
- .ProseMirror > * {
2616
- height: 100%;
2617
- }
2618
- .${EDITOR_WRAPPER_SELECTOR} .ProseMirror > button[contenteditable="true"] {
2619
- height: auto;
2620
- cursor: text;
2621
- }
2622
- `), /* @__PURE__ */ React5.createElement(
2623
+ const { onSelectionEnd, anchor: toolbarAnchor, clearAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2624
+ (0, import_react12.useEffect)(() => {
2625
+ if (!active) {
2626
+ clearAnchor();
2627
+ requestDestroy();
2628
+ }
2629
+ }, [active, clearAnchor, requestDestroy]);
2630
+ const dismiss = (0, import_react12.useCallback)(() => {
2631
+ setEditor(null);
2632
+ setActive(false);
2633
+ }, []);
2634
+ useOnClickOutsideIframe(dismiss);
2635
+ (0, import_react12.useEffect)(() => {
2636
+ const ownerDocument = contentElement.ownerDocument;
2637
+ const handleClickAway = (event) => {
2638
+ if (contentElement.contains(event.target)) {
2639
+ return;
2640
+ }
2641
+ dismiss();
2642
+ };
2643
+ ownerDocument.addEventListener("mousedown", handleClickAway);
2644
+ return () => ownerDocument.removeEventListener("mousedown", handleClickAway);
2645
+ }, [contentElement, dismiss]);
2646
+ if (!active) {
2647
+ return null;
2648
+ }
2649
+ return /* @__PURE__ */ React5.createElement(import_ui4.ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement(
2623
2650
  import_editor_controls2.InlineEditor,
2624
2651
  {
2625
2652
  onEditorCreate: setEditor,
2653
+ mountElement: contentElement,
2626
2654
  editorProps: {
2627
2655
  attributes: {
2628
- style: "outline: none;overflow-wrap: normal;height:100%"
2656
+ style: "outline: none; display: inherit; justify-content: inherit; align-items: inherit; flex-direction: inherit; text-align: inherit;"
2629
2657
  }
2630
2658
  },
2631
2659
  elementClasses,
2632
2660
  value: initialValue,
2633
2661
  setValue,
2634
- onBlur,
2662
+ onBlur: dismiss,
2635
2663
  autofocus: true,
2636
- expectedTag,
2637
2664
  onSelectionEnd
2638
2665
  }
2639
2666
  ), toolbarAnchor && editor && /* @__PURE__ */ React5.createElement(InlineEditingToolbar, { anchor: toolbarAnchor, editor, id }));
@@ -2662,7 +2689,18 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
2662
2689
  refs.setReference(anchor);
2663
2690
  return () => refs.setReference(null);
2664
2691
  }, [anchor, refs]);
2665
- return /* @__PURE__ */ React5.createElement(import_react13.FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React5.createElement(import_ui4.Box, { ref: refs.setFloating, role: "presentation", style: { ...floatingStyles, pointerEvents: "none" } }, /* @__PURE__ */ React5.createElement(import_editor_controls2.InlineEditorToolbar, { editor, elementId: id })));
2692
+ return /* @__PURE__ */ React5.createElement(import_react13.FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React5.createElement(
2693
+ import_ui4.Box,
2694
+ {
2695
+ ref: refs.setFloating,
2696
+ role: "presentation",
2697
+ style: {
2698
+ ...floatingStyles,
2699
+ pointerEvents: "none"
2700
+ }
2701
+ },
2702
+ /* @__PURE__ */ React5.createElement(import_editor_controls2.InlineEditorToolbar, { editor, elementId: id })
2703
+ ));
2666
2704
  };
2667
2705
 
2668
2706
  // src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
@@ -2696,8 +2734,8 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
2696
2734
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2697
2735
  var HISTORY_DEBOUNCE_WAIT = 800;
2698
2736
  var InlineEditingReplacement = class extends ReplacementBase {
2699
- inlineEditorRoot = null;
2700
2737
  handlerAttached = false;
2738
+ editing = false;
2701
2739
  getReplacementKey() {
2702
2740
  return "inline-editing";
2703
2741
  }
@@ -2705,7 +2743,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
2705
2743
  return Object.keys(INLINE_EDITING_PROPERTY_PER_TYPE);
2706
2744
  }
2707
2745
  isEditingModeActive() {
2708
- return !!this.inlineEditorRoot;
2746
+ return this.editing;
2709
2747
  }
2710
2748
  shouldRenderReplacement() {
2711
2749
  return this.isInlineEditingEligible() && (0, import_editor_v1_adapters12.getCurrentEditMode)() === "edit";
@@ -2748,8 +2786,8 @@ var InlineEditingReplacement = class extends ReplacementBase {
2748
2786
  resetInlineEditorRoot() {
2749
2787
  this.element.removeEventListener("click", this.handleRenderInlineEditor);
2750
2788
  this.handlerAttached = false;
2751
- this.inlineEditorRoot?.unmount?.();
2752
- this.inlineEditorRoot = null;
2789
+ this.reactRoot.render(null);
2790
+ this.editing = false;
2753
2791
  }
2754
2792
  unmountInlineEditor() {
2755
2793
  this.resetInlineEditorRoot();
@@ -2860,12 +2898,16 @@ var InlineEditingReplacement = class extends ReplacementBase {
2860
2898
  if (this.isEditingModeActive()) {
2861
2899
  this.resetInlineEditorRoot();
2862
2900
  }
2863
- const elementClasses = this.element.children?.[0]?.classList.toString() ?? "";
2901
+ const contentElement = this.element.children?.[0];
2902
+ if (!contentElement) {
2903
+ return;
2904
+ }
2905
+ const elementClasses = contentElement.classList.toString();
2864
2906
  const propValue = this.getExtractedContentValue();
2865
2907
  const expectedTag = this.getExpectedTag();
2866
- this.element.innerHTML = "";
2867
- this.inlineEditorRoot = (0, import_client.createRoot)(this.element);
2868
- this.inlineEditorRoot.render(
2908
+ contentElement.innerHTML = "";
2909
+ this.editing = true;
2910
+ this.reactRoot.render(
2869
2911
  /* @__PURE__ */ React6.createElement(
2870
2912
  CanvasInlineEditor,
2871
2913
  {
@@ -2873,9 +2915,10 @@ var InlineEditingReplacement = class extends ReplacementBase {
2873
2915
  initialValue: propValue,
2874
2916
  expectedTag,
2875
2917
  rootElement: this.element,
2918
+ contentElement,
2876
2919
  id: this.id,
2877
2920
  setValue: this.setContentValue.bind(this),
2878
- onBlur: this.unmountInlineEditor.bind(this)
2921
+ requestDestroy: this.unmountInlineEditor.bind(this)
2879
2922
  }
2880
2923
  )
2881
2924
  );
@@ -2904,16 +2947,24 @@ var createViewWithReplacements = (options) => {
2904
2947
  return class extends TemplatedView {
2905
2948
  #replacement = null;
2906
2949
  #config;
2950
+ #reactContainer;
2951
+ #reactRoot;
2907
2952
  constructor(...args) {
2908
2953
  super(...args);
2909
2954
  const settings = this.model.get("settings");
2955
+ this.#reactContainer = this.el.ownerDocument.createElement("div");
2956
+ this.#reactContainer.style.display = "none";
2957
+ this.el.ownerDocument.body.appendChild(this.#reactContainer);
2958
+ this.#reactRoot = (0, import_client.createRoot)(this.#reactContainer);
2910
2959
  this.#config = {
2911
2960
  getSetting: settings.get.bind(settings),
2912
2961
  setSetting: settings.set.bind(settings),
2913
2962
  element: this.el,
2914
2963
  type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
2915
2964
  id: this?.model?.get("id") ?? null,
2916
- refreshView: this.refreshView.bind(this)
2965
+ refreshView: this.refreshView.bind(this),
2966
+ reactRoot: this.#reactRoot,
2967
+ reactContainer: this.#reactContainer
2917
2968
  };
2918
2969
  }
2919
2970
  refreshView() {
@@ -2934,6 +2985,8 @@ var createViewWithReplacements = (options) => {
2934
2985
  }
2935
2986
  onDestroy() {
2936
2987
  this.#triggerAltMethod("onDestroy");
2988
+ this.#reactRoot.unmount();
2989
+ this.#reactContainer.remove();
2937
2990
  }
2938
2991
  _afterRender() {
2939
2992
  this.#triggerAltMethod("_afterRender");
package/dist/index.mjs CHANGED
@@ -2387,9 +2387,11 @@ function createNestedTemplatedElementView({
2387
2387
  });
2388
2388
  }
2389
2389
 
2390
+ // src/legacy/replacements/manager.ts
2391
+ import { createRoot } from "react-dom/client";
2392
+
2390
2393
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2391
2394
  import * as React6 from "react";
2392
- import { createRoot } from "react-dom/client";
2393
2395
  import { getContainer, getElementLabel, getElementType as getElementType2 } from "@elementor/editor-elements";
2394
2396
  import {
2395
2397
  htmlV3PropTypeUtil as htmlV3PropTypeUtil2,
@@ -2412,6 +2414,8 @@ var ReplacementBase = class {
2412
2414
  type;
2413
2415
  id;
2414
2416
  refreshView;
2417
+ reactRoot;
2418
+ reactContainer;
2415
2419
  constructor(settings) {
2416
2420
  this.getSetting = settings.getSetting;
2417
2421
  this.setSetting = settings.setSetting;
@@ -2419,6 +2423,8 @@ var ReplacementBase = class {
2419
2423
  this.type = settings.type;
2420
2424
  this.id = settings.id;
2421
2425
  this.refreshView = settings.refreshView;
2426
+ this.reactRoot = settings.reactRoot;
2427
+ this.reactContainer = settings.reactContainer;
2422
2428
  }
2423
2429
  static getTypes() {
2424
2430
  return null;
@@ -2439,7 +2445,7 @@ var ReplacementBase = class {
2439
2445
 
2440
2446
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2441
2447
  import * as React5 from "react";
2442
- import { useEffect as useEffect8, useLayoutEffect, useState as useState5 } from "react";
2448
+ import { useCallback as useCallback2, useEffect as useEffect8, useLayoutEffect, useState as useState5 } from "react";
2443
2449
  import { InlineEditor, InlineEditorToolbar } from "@elementor/editor-controls";
2444
2450
  import { Box as Box2, ThemeProvider } from "@elementor/ui";
2445
2451
  import { autoUpdate as autoUpdate2, flip, FloatingPortal as FloatingPortal2, useFloating as useFloating2 } from "@floating-ui/react";
@@ -2488,6 +2494,11 @@ var useOnClickOutsideIframe = (handleUnmount) => {
2488
2494
  };
2489
2495
  var useRenderToolbar = (ownerDocument, id) => {
2490
2496
  const [anchor, setAnchor] = useState4(null);
2497
+ useEffect7(() => {
2498
+ if (!anchor) {
2499
+ removeToolbarAnchor(ownerDocument, id);
2500
+ }
2501
+ }, [anchor, ownerDocument, id]);
2491
2502
  const onSelectionEnd = (view) => {
2492
2503
  const hasSelection = !view.state.selection.empty;
2493
2504
  removeToolbarAnchor(ownerDocument, id);
@@ -2497,7 +2508,10 @@ var useRenderToolbar = (ownerDocument, id) => {
2497
2508
  setAnchor(null);
2498
2509
  }
2499
2510
  };
2500
- return { onSelectionEnd, anchor };
2511
+ const clearAnchor = useCallback(() => {
2512
+ setAnchor(null);
2513
+ }, []);
2514
+ return { onSelectionEnd, anchor, clearAnchor };
2501
2515
  };
2502
2516
  var createAnchorBasedOnSelection = (ownerDocument, id) => {
2503
2517
  const frameWindow = ownerDocument.defaultView;
@@ -2564,46 +2578,59 @@ var horizontalShifterMiddleware = {
2564
2578
  };
2565
2579
 
2566
2580
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2567
- var EDITOR_WRAPPER_SELECTOR = "inline-editor-wrapper";
2568
2581
  var CanvasInlineEditor = ({
2569
2582
  elementClasses,
2570
2583
  initialValue,
2571
2584
  expectedTag,
2572
2585
  rootElement,
2586
+ contentElement,
2573
2587
  id,
2574
2588
  setValue,
2575
- ...props
2589
+ requestDestroy
2576
2590
  }) => {
2591
+ const [active, setActive] = useState5(true);
2577
2592
  const [editor, setEditor] = useState5(null);
2578
- const { onSelectionEnd, anchor: toolbarAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2579
- const onBlur = () => {
2580
- removeToolbarAnchor(rootElement.ownerDocument, id);
2581
- props.onBlur();
2582
- };
2583
- useOnClickOutsideIframe(onBlur);
2584
- return /* @__PURE__ */ React5.createElement(ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement("style", null, `
2585
- .ProseMirror > * {
2586
- height: 100%;
2587
- }
2588
- .${EDITOR_WRAPPER_SELECTOR} .ProseMirror > button[contenteditable="true"] {
2589
- height: auto;
2590
- cursor: text;
2591
- }
2592
- `), /* @__PURE__ */ React5.createElement(
2593
+ const { onSelectionEnd, anchor: toolbarAnchor, clearAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2594
+ useEffect8(() => {
2595
+ if (!active) {
2596
+ clearAnchor();
2597
+ requestDestroy();
2598
+ }
2599
+ }, [active, clearAnchor, requestDestroy]);
2600
+ const dismiss = useCallback2(() => {
2601
+ setEditor(null);
2602
+ setActive(false);
2603
+ }, []);
2604
+ useOnClickOutsideIframe(dismiss);
2605
+ useEffect8(() => {
2606
+ const ownerDocument = contentElement.ownerDocument;
2607
+ const handleClickAway = (event) => {
2608
+ if (contentElement.contains(event.target)) {
2609
+ return;
2610
+ }
2611
+ dismiss();
2612
+ };
2613
+ ownerDocument.addEventListener("mousedown", handleClickAway);
2614
+ return () => ownerDocument.removeEventListener("mousedown", handleClickAway);
2615
+ }, [contentElement, dismiss]);
2616
+ if (!active) {
2617
+ return null;
2618
+ }
2619
+ return /* @__PURE__ */ React5.createElement(ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement(
2593
2620
  InlineEditor,
2594
2621
  {
2595
2622
  onEditorCreate: setEditor,
2623
+ mountElement: contentElement,
2596
2624
  editorProps: {
2597
2625
  attributes: {
2598
- style: "outline: none;overflow-wrap: normal;height:100%"
2626
+ style: "outline: none; display: inherit; justify-content: inherit; align-items: inherit; flex-direction: inherit; text-align: inherit;"
2599
2627
  }
2600
2628
  },
2601
2629
  elementClasses,
2602
2630
  value: initialValue,
2603
2631
  setValue,
2604
- onBlur,
2632
+ onBlur: dismiss,
2605
2633
  autofocus: true,
2606
- expectedTag,
2607
2634
  onSelectionEnd
2608
2635
  }
2609
2636
  ), toolbarAnchor && editor && /* @__PURE__ */ React5.createElement(InlineEditingToolbar, { anchor: toolbarAnchor, editor, id }));
@@ -2632,7 +2659,18 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
2632
2659
  refs.setReference(anchor);
2633
2660
  return () => refs.setReference(null);
2634
2661
  }, [anchor, refs]);
2635
- return /* @__PURE__ */ React5.createElement(FloatingPortal2, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React5.createElement(Box2, { ref: refs.setFloating, role: "presentation", style: { ...floatingStyles, pointerEvents: "none" } }, /* @__PURE__ */ React5.createElement(InlineEditorToolbar, { editor, elementId: id })));
2662
+ return /* @__PURE__ */ React5.createElement(FloatingPortal2, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React5.createElement(
2663
+ Box2,
2664
+ {
2665
+ ref: refs.setFloating,
2666
+ role: "presentation",
2667
+ style: {
2668
+ ...floatingStyles,
2669
+ pointerEvents: "none"
2670
+ }
2671
+ },
2672
+ /* @__PURE__ */ React5.createElement(InlineEditorToolbar, { editor, elementId: id })
2673
+ ));
2636
2674
  };
2637
2675
 
2638
2676
  // src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
@@ -2666,8 +2704,8 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
2666
2704
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2667
2705
  var HISTORY_DEBOUNCE_WAIT = 800;
2668
2706
  var InlineEditingReplacement = class extends ReplacementBase {
2669
- inlineEditorRoot = null;
2670
2707
  handlerAttached = false;
2708
+ editing = false;
2671
2709
  getReplacementKey() {
2672
2710
  return "inline-editing";
2673
2711
  }
@@ -2675,7 +2713,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
2675
2713
  return Object.keys(INLINE_EDITING_PROPERTY_PER_TYPE);
2676
2714
  }
2677
2715
  isEditingModeActive() {
2678
- return !!this.inlineEditorRoot;
2716
+ return this.editing;
2679
2717
  }
2680
2718
  shouldRenderReplacement() {
2681
2719
  return this.isInlineEditingEligible() && getCurrentEditMode() === "edit";
@@ -2718,8 +2756,8 @@ var InlineEditingReplacement = class extends ReplacementBase {
2718
2756
  resetInlineEditorRoot() {
2719
2757
  this.element.removeEventListener("click", this.handleRenderInlineEditor);
2720
2758
  this.handlerAttached = false;
2721
- this.inlineEditorRoot?.unmount?.();
2722
- this.inlineEditorRoot = null;
2759
+ this.reactRoot.render(null);
2760
+ this.editing = false;
2723
2761
  }
2724
2762
  unmountInlineEditor() {
2725
2763
  this.resetInlineEditorRoot();
@@ -2830,12 +2868,16 @@ var InlineEditingReplacement = class extends ReplacementBase {
2830
2868
  if (this.isEditingModeActive()) {
2831
2869
  this.resetInlineEditorRoot();
2832
2870
  }
2833
- const elementClasses = this.element.children?.[0]?.classList.toString() ?? "";
2871
+ const contentElement = this.element.children?.[0];
2872
+ if (!contentElement) {
2873
+ return;
2874
+ }
2875
+ const elementClasses = contentElement.classList.toString();
2834
2876
  const propValue = this.getExtractedContentValue();
2835
2877
  const expectedTag = this.getExpectedTag();
2836
- this.element.innerHTML = "";
2837
- this.inlineEditorRoot = createRoot(this.element);
2838
- this.inlineEditorRoot.render(
2878
+ contentElement.innerHTML = "";
2879
+ this.editing = true;
2880
+ this.reactRoot.render(
2839
2881
  /* @__PURE__ */ React6.createElement(
2840
2882
  CanvasInlineEditor,
2841
2883
  {
@@ -2843,9 +2885,10 @@ var InlineEditingReplacement = class extends ReplacementBase {
2843
2885
  initialValue: propValue,
2844
2886
  expectedTag,
2845
2887
  rootElement: this.element,
2888
+ contentElement,
2846
2889
  id: this.id,
2847
2890
  setValue: this.setContentValue.bind(this),
2848
- onBlur: this.unmountInlineEditor.bind(this)
2891
+ requestDestroy: this.unmountInlineEditor.bind(this)
2849
2892
  }
2850
2893
  )
2851
2894
  );
@@ -2874,16 +2917,24 @@ var createViewWithReplacements = (options) => {
2874
2917
  return class extends TemplatedView {
2875
2918
  #replacement = null;
2876
2919
  #config;
2920
+ #reactContainer;
2921
+ #reactRoot;
2877
2922
  constructor(...args) {
2878
2923
  super(...args);
2879
2924
  const settings = this.model.get("settings");
2925
+ this.#reactContainer = this.el.ownerDocument.createElement("div");
2926
+ this.#reactContainer.style.display = "none";
2927
+ this.el.ownerDocument.body.appendChild(this.#reactContainer);
2928
+ this.#reactRoot = createRoot(this.#reactContainer);
2880
2929
  this.#config = {
2881
2930
  getSetting: settings.get.bind(settings),
2882
2931
  setSetting: settings.set.bind(settings),
2883
2932
  element: this.el,
2884
2933
  type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
2885
2934
  id: this?.model?.get("id") ?? null,
2886
- refreshView: this.refreshView.bind(this)
2935
+ refreshView: this.refreshView.bind(this),
2936
+ reactRoot: this.#reactRoot,
2937
+ reactContainer: this.#reactContainer
2887
2938
  };
2888
2939
  }
2889
2940
  refreshView() {
@@ -2904,6 +2955,8 @@ var createViewWithReplacements = (options) => {
2904
2955
  }
2905
2956
  onDestroy() {
2906
2957
  this.#triggerAltMethod("onDestroy");
2958
+ this.#reactRoot.unmount();
2959
+ this.#reactContainer.remove();
2907
2960
  }
2908
2961
  _afterRender() {
2909
2962
  this.#triggerAltMethod("_afterRender");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-canvas",
3
3
  "description": "Elementor Editor Canvas",
4
- "version": "4.1.0-740",
4
+ "version": "4.1.0-741",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,25 +37,25 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "4.1.0-740",
40
+ "@elementor/editor": "4.1.0-741",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.1.0-740",
43
- "@elementor/editor-documents": "4.1.0-740",
44
- "@elementor/editor-elements": "4.1.0-740",
45
- "@elementor/editor-interactions": "4.1.0-740",
46
- "@elementor/editor-mcp": "4.1.0-740",
47
- "@elementor/editor-notifications": "4.1.0-740",
48
- "@elementor/editor-props": "4.1.0-740",
49
- "@elementor/editor-responsive": "4.1.0-740",
50
- "@elementor/editor-styles": "4.1.0-740",
51
- "@elementor/editor-styles-repository": "4.1.0-740",
52
- "@elementor/editor-ui": "4.1.0-740",
53
- "@elementor/editor-v1-adapters": "4.1.0-740",
54
- "@elementor/schema": "4.1.0-740",
55
- "@elementor/twing": "4.1.0-740",
42
+ "@elementor/editor-controls": "4.1.0-741",
43
+ "@elementor/editor-documents": "4.1.0-741",
44
+ "@elementor/editor-elements": "4.1.0-741",
45
+ "@elementor/editor-interactions": "4.1.0-741",
46
+ "@elementor/editor-mcp": "4.1.0-741",
47
+ "@elementor/editor-notifications": "4.1.0-741",
48
+ "@elementor/editor-props": "4.1.0-741",
49
+ "@elementor/editor-responsive": "4.1.0-741",
50
+ "@elementor/editor-styles": "4.1.0-741",
51
+ "@elementor/editor-styles-repository": "4.1.0-741",
52
+ "@elementor/editor-ui": "4.1.0-741",
53
+ "@elementor/editor-v1-adapters": "4.1.0-741",
54
+ "@elementor/schema": "4.1.0-741",
55
+ "@elementor/twing": "4.1.0-741",
56
56
  "@elementor/ui": "1.36.17",
57
- "@elementor/utils": "4.1.0-740",
58
- "@elementor/wp-media": "4.1.0-740",
57
+ "@elementor/utils": "4.1.0-741",
58
+ "@elementor/wp-media": "4.1.0-741",
59
59
  "@floating-ui/react": "^0.27.5",
60
60
  "@wordpress/i18n": "^5.13.0"
61
61
  },
@@ -26,6 +26,8 @@ export class ReplacementBase implements ReplacementBaseInterface {
26
26
  protected type: ReplacementSettings[ 'type' ];
27
27
  protected id: ReplacementSettings[ 'id' ];
28
28
  protected refreshView: ReplacementSettings[ 'refreshView' ];
29
+ protected reactRoot: ReplacementSettings[ 'reactRoot' ];
30
+ protected reactContainer: ReplacementSettings[ 'reactContainer' ];
29
31
 
30
32
  constructor( settings: ReplacementSettings ) {
31
33
  this.getSetting = settings.getSetting;
@@ -34,6 +36,8 @@ export class ReplacementBase implements ReplacementBaseInterface {
34
36
  this.type = settings.type;
35
37
  this.id = settings.id;
36
38
  this.refreshView = settings.refreshView;
39
+ this.reactRoot = settings.reactRoot;
40
+ this.reactContainer = settings.reactContainer;
37
41
  }
38
42
 
39
43
  static getTypes(): string[] | null {
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useEffect, useLayoutEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
3
3
  import { InlineEditor, InlineEditorToolbar } from '@elementor/editor-controls';
4
4
  import { Box, ThemeProvider } from '@elementor/ui';
5
5
  import { autoUpdate, flip, FloatingPortal, useFloating } from '@floating-ui/react';
@@ -9,68 +9,83 @@ import {
9
9
  type Editor,
10
10
  getInlineEditorElement,
11
11
  horizontalShifterMiddleware as horizontalShifter,
12
- removeToolbarAnchor,
13
12
  useOnClickOutsideIframe,
14
13
  useRenderToolbar,
15
14
  } from './inline-editing-utils';
16
15
 
17
- const EDITOR_WRAPPER_SELECTOR = 'inline-editor-wrapper';
18
-
19
16
  export const CanvasInlineEditor = ( {
20
17
  elementClasses,
21
18
  initialValue,
22
19
  expectedTag,
23
20
  rootElement,
21
+ contentElement,
24
22
  id,
25
23
  setValue,
26
- ...props
24
+ requestDestroy,
27
25
  }: {
28
26
  elementClasses: string;
29
27
  initialValue: string | null;
30
28
  expectedTag: string | null;
31
29
  rootElement: HTMLElement;
30
+ contentElement: HTMLElement;
32
31
  id: string;
33
32
  setValue: ( value: string | null ) => void;
34
- onBlur: () => void;
33
+ requestDestroy: () => void;
35
34
  } ) => {
35
+ const [ active, setActive ] = useState( true );
36
36
  const [ editor, setEditor ] = useState< Editor | null >( null );
37
- const { onSelectionEnd, anchor: toolbarAnchor } = useRenderToolbar( rootElement.ownerDocument, id );
37
+ const { onSelectionEnd, anchor: toolbarAnchor, clearAnchor } = useRenderToolbar( rootElement.ownerDocument, id );
38
+
39
+ useEffect( () => {
40
+ if ( ! active ) {
41
+ clearAnchor();
42
+ requestDestroy();
43
+ }
44
+ }, [ active, clearAnchor, requestDestroy ] );
45
+
46
+ const dismiss = useCallback( () => {
47
+ setEditor( null );
48
+ setActive( false );
49
+ }, [] );
38
50
 
39
- const onBlur = () => {
40
- removeToolbarAnchor( rootElement.ownerDocument, id );
51
+ useOnClickOutsideIframe( dismiss );
41
52
 
42
- props.onBlur();
43
- };
53
+ useEffect( () => {
54
+ const ownerDocument = contentElement.ownerDocument;
55
+
56
+ const handleClickAway = ( event: MouseEvent ) => {
57
+ if ( contentElement.contains( event.target as Node ) ) {
58
+ return;
59
+ }
44
60
 
45
- useOnClickOutsideIframe( onBlur );
61
+ dismiss();
62
+ };
63
+
64
+ ownerDocument.addEventListener( 'mousedown', handleClickAway );
65
+
66
+ return () => ownerDocument.removeEventListener( 'mousedown', handleClickAway );
67
+ }, [ contentElement, dismiss ] );
68
+
69
+ if ( ! active ) {
70
+ return null;
71
+ }
46
72
 
47
73
  return (
48
74
  <ThemeProvider>
49
75
  <InlineEditingOverlay expectedTag={ expectedTag } rootElement={ rootElement } id={ id } />
50
- <style>
51
- { `
52
- .ProseMirror > * {
53
- height: 100%;
54
- }
55
- .${ EDITOR_WRAPPER_SELECTOR } .ProseMirror > button[contenteditable="true"] {
56
- height: auto;
57
- cursor: text;
58
- }
59
- ` }
60
- </style>
61
76
  <InlineEditor
62
77
  onEditorCreate={ setEditor }
78
+ mountElement={ contentElement }
63
79
  editorProps={ {
64
80
  attributes: {
65
- style: 'outline: none;overflow-wrap: normal;height:100%',
81
+ style: 'outline: none; display: inherit; justify-content: inherit; align-items: inherit; flex-direction: inherit; text-align: inherit;',
66
82
  },
67
83
  } }
68
84
  elementClasses={ elementClasses }
69
85
  value={ initialValue }
70
86
  setValue={ setValue }
71
- onBlur={ onBlur }
87
+ onBlur={ dismiss }
72
88
  autofocus
73
- expectedTag={ expectedTag }
74
89
  onSelectionEnd={ onSelectionEnd }
75
90
  />
76
91
  { toolbarAnchor && editor && <InlineEditingToolbar anchor={ toolbarAnchor } editor={ editor } id={ id } /> }
@@ -114,7 +129,14 @@ const InlineEditingToolbar = ( { anchor, editor, id }: { anchor: HTMLElement; ed
114
129
 
115
130
  return (
116
131
  <FloatingPortal id={ CANVAS_WRAPPER_ID }>
117
- <Box ref={ refs.setFloating } role="presentation" style={ { ...floatingStyles, pointerEvents: 'none' } }>
132
+ <Box
133
+ ref={ refs.setFloating }
134
+ role="presentation"
135
+ style={ {
136
+ ...floatingStyles,
137
+ pointerEvents: 'none',
138
+ } }
139
+ >
118
140
  <InlineEditorToolbar editor={ editor } elementId={ id } />
119
141
  </Box>
120
142
  </FloatingPortal>
@@ -1,5 +1,4 @@
1
1
  import * as React from 'react';
2
- import { createRoot, type Root } from 'react-dom/client';
3
2
  import { getContainer, getElementLabel, getElementType } from '@elementor/editor-elements';
4
3
  import {
5
4
  htmlV3PropTypeUtil,
@@ -25,8 +24,8 @@ type TagPropType = PropType< 'tag' > & {
25
24
  const HISTORY_DEBOUNCE_WAIT = 800;
26
25
 
27
26
  export default class InlineEditingReplacement extends ReplacementBase {
28
- private inlineEditorRoot: Root | null = null;
29
27
  private handlerAttached = false;
28
+ private editing = false;
30
29
 
31
30
  getReplacementKey() {
32
31
  return 'inline-editing';
@@ -37,7 +36,7 @@ export default class InlineEditingReplacement extends ReplacementBase {
37
36
  }
38
37
 
39
38
  isEditingModeActive() {
40
- return !! this.inlineEditorRoot;
39
+ return this.editing;
41
40
  }
42
41
 
43
42
  shouldRenderReplacement() {
@@ -91,8 +90,8 @@ export default class InlineEditingReplacement extends ReplacementBase {
91
90
  resetInlineEditorRoot() {
92
91
  this.element.removeEventListener( 'click', this.handleRenderInlineEditor );
93
92
  this.handlerAttached = false;
94
- this.inlineEditorRoot?.unmount?.();
95
- this.inlineEditorRoot = null;
93
+ this.reactRoot.render( null );
94
+ this.editing = false;
96
95
  }
97
96
 
98
97
  unmountInlineEditor() {
@@ -239,22 +238,29 @@ export default class InlineEditingReplacement extends ReplacementBase {
239
238
  this.resetInlineEditorRoot();
240
239
  }
241
240
 
242
- const elementClasses = this.element.children?.[ 0 ]?.classList.toString() ?? '';
241
+ const contentElement = this.element.children?.[ 0 ] as HTMLElement | undefined;
242
+
243
+ if ( ! contentElement ) {
244
+ return;
245
+ }
246
+
247
+ const elementClasses = contentElement.classList.toString();
243
248
  const propValue = this.getExtractedContentValue();
244
249
  const expectedTag = this.getExpectedTag();
245
250
 
246
- this.element.innerHTML = '';
251
+ contentElement.innerHTML = '';
252
+ this.editing = true;
247
253
 
248
- this.inlineEditorRoot = createRoot( this.element );
249
- this.inlineEditorRoot.render(
254
+ this.reactRoot.render(
250
255
  <CanvasInlineEditor
251
256
  elementClasses={ elementClasses }
252
257
  initialValue={ propValue }
253
258
  expectedTag={ expectedTag }
254
259
  rootElement={ this.element }
260
+ contentElement={ contentElement }
255
261
  id={ this.id }
256
262
  setValue={ this.setContentValue.bind( this ) }
257
- onBlur={ this.unmountInlineEditor.bind( this ) }
263
+ requestDestroy={ this.unmountInlineEditor.bind( this ) }
258
264
  />
259
265
  );
260
266
  }
@@ -80,6 +80,12 @@ export const useOnClickOutsideIframe = ( handleUnmount: () => void ) => {
80
80
  export const useRenderToolbar = ( ownerDocument: Document, id: string ) => {
81
81
  const [ anchor, setAnchor ] = useState< HTMLElement | null >( null );
82
82
 
83
+ useEffect( () => {
84
+ if ( ! anchor ) {
85
+ removeToolbarAnchor( ownerDocument, id );
86
+ }
87
+ }, [ anchor, ownerDocument, id ] );
88
+
83
89
  const onSelectionEnd = ( view: EditorView ) => {
84
90
  const hasSelection = ! view.state.selection.empty;
85
91
 
@@ -92,7 +98,11 @@ export const useRenderToolbar = ( ownerDocument: Document, id: string ) => {
92
98
  }
93
99
  };
94
100
 
95
- return { onSelectionEnd, anchor };
101
+ const clearAnchor = useCallback( () => {
102
+ setAnchor( null );
103
+ }, [] );
104
+
105
+ return { onSelectionEnd, anchor, clearAnchor };
96
106
  };
97
107
 
98
108
  const createAnchorBasedOnSelection = ( ownerDocument: Document, id: string ): HTMLElement | null => {
@@ -1,3 +1,5 @@
1
+ import { createRoot, type Root } from 'react-dom/client';
2
+
1
3
  import type { CreateTemplatedElementTypeOptions } from '../create-templated-element-type';
2
4
  import { createTemplatedElementView } from '../create-templated-element-type';
3
5
  import type { ElementType, ElementView, LegacyWindow, ReplacementSettings } from '../types';
@@ -34,11 +36,18 @@ export const createViewWithReplacements = ( options: CreateTemplatedElementTypeO
34
36
  return class extends TemplatedView {
35
37
  #replacement: ReplacementBaseInterface | null = null;
36
38
  #config: ReplacementSettings;
39
+ #reactContainer: HTMLElement;
40
+ #reactRoot: Root;
37
41
 
38
42
  constructor( ...args: unknown[] ) {
39
43
  super( ...args );
40
44
  const settings = this.model.get( 'settings' );
41
45
 
46
+ this.#reactContainer = this.el.ownerDocument.createElement( 'div' );
47
+ this.#reactContainer.style.display = 'none';
48
+ this.el.ownerDocument.body.appendChild( this.#reactContainer );
49
+ this.#reactRoot = createRoot( this.#reactContainer );
50
+
42
51
  this.#config = {
43
52
  getSetting: settings.get.bind( settings ),
44
53
  setSetting: settings.set.bind( settings ),
@@ -46,6 +55,8 @@ export const createViewWithReplacements = ( options: CreateTemplatedElementTypeO
46
55
  type: this?.model?.get( 'widgetType' ) ?? this.container?.model?.get( 'elType' ) ?? null,
47
56
  id: this?.model?.get( 'id' ) ?? null,
48
57
  refreshView: this.refreshView.bind( this ),
58
+ reactRoot: this.#reactRoot,
59
+ reactContainer: this.#reactContainer,
49
60
  };
50
61
  }
51
62
 
@@ -72,6 +83,8 @@ export const createViewWithReplacements = ( options: CreateTemplatedElementTypeO
72
83
 
73
84
  onDestroy() {
74
85
  this.#triggerAltMethod( 'onDestroy' );
86
+ this.#reactRoot.unmount();
87
+ this.#reactContainer.remove();
75
88
  }
76
89
 
77
90
  _afterRender() {
@@ -1,3 +1,4 @@
1
+ import { type Root } from 'react-dom/client';
1
2
  import { type V1Element } from '@elementor/editor-elements';
2
3
  import { type Props, type PropValue } from '@elementor/editor-props';
3
4
 
@@ -241,4 +242,6 @@ export type ReplacementSettings = {
241
242
  id: string;
242
243
  element: HTMLElement;
243
244
  refreshView: () => void;
245
+ reactRoot: Root;
246
+ reactContainer: HTMLElement;
244
247
  };