@elementor/editor-canvas 4.1.0-739 → 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;
@@ -2499,7 +2505,8 @@ var INLINE_EDITING_PROPERTY_PER_TYPE = {
2499
2505
  "e-button": "text",
2500
2506
  "e-form-label": "text",
2501
2507
  "e-heading": "title",
2502
- "e-paragraph": "paragraph"
2508
+ "e-paragraph": "paragraph",
2509
+ "e-form-submit-button": "text"
2503
2510
  };
2504
2511
  var getInlineEditorElement = (elementWrapper, expectedTag) => {
2505
2512
  return !expectedTag ? null : elementWrapper.querySelector(expectedTag);
@@ -2517,6 +2524,11 @@ var useOnClickOutsideIframe = (handleUnmount) => {
2517
2524
  };
2518
2525
  var useRenderToolbar = (ownerDocument, id) => {
2519
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]);
2520
2532
  const onSelectionEnd = (view) => {
2521
2533
  const hasSelection = !view.state.selection.empty;
2522
2534
  removeToolbarAnchor(ownerDocument, id);
@@ -2526,7 +2538,10 @@ var useRenderToolbar = (ownerDocument, id) => {
2526
2538
  setAnchor(null);
2527
2539
  }
2528
2540
  };
2529
- return { onSelectionEnd, anchor };
2541
+ const clearAnchor = (0, import_react11.useCallback)(() => {
2542
+ setAnchor(null);
2543
+ }, []);
2544
+ return { onSelectionEnd, anchor, clearAnchor };
2530
2545
  };
2531
2546
  var createAnchorBasedOnSelection = (ownerDocument, id) => {
2532
2547
  const frameWindow = ownerDocument.defaultView;
@@ -2593,46 +2608,59 @@ var horizontalShifterMiddleware = {
2593
2608
  };
2594
2609
 
2595
2610
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2596
- var EDITOR_WRAPPER_SELECTOR = "inline-editor-wrapper";
2597
2611
  var CanvasInlineEditor = ({
2598
2612
  elementClasses,
2599
2613
  initialValue,
2600
2614
  expectedTag,
2601
2615
  rootElement,
2616
+ contentElement,
2602
2617
  id,
2603
2618
  setValue,
2604
- ...props
2619
+ requestDestroy
2605
2620
  }) => {
2621
+ const [active, setActive] = (0, import_react12.useState)(true);
2606
2622
  const [editor, setEditor] = (0, import_react12.useState)(null);
2607
- const { onSelectionEnd, anchor: toolbarAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2608
- const onBlur = () => {
2609
- removeToolbarAnchor(rootElement.ownerDocument, id);
2610
- props.onBlur();
2611
- };
2612
- useOnClickOutsideIframe(onBlur);
2613
- return /* @__PURE__ */ React5.createElement(import_ui4.ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement("style", null, `
2614
- .ProseMirror > * {
2615
- height: 100%;
2616
- }
2617
- .${EDITOR_WRAPPER_SELECTOR} .ProseMirror > button[contenteditable="true"] {
2618
- height: auto;
2619
- cursor: text;
2620
- }
2621
- `), /* @__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(
2622
2650
  import_editor_controls2.InlineEditor,
2623
2651
  {
2624
2652
  onEditorCreate: setEditor,
2653
+ mountElement: contentElement,
2625
2654
  editorProps: {
2626
2655
  attributes: {
2627
- 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;"
2628
2657
  }
2629
2658
  },
2630
2659
  elementClasses,
2631
2660
  value: initialValue,
2632
2661
  setValue,
2633
- onBlur,
2662
+ onBlur: dismiss,
2634
2663
  autofocus: true,
2635
- expectedTag,
2636
2664
  onSelectionEnd
2637
2665
  }
2638
2666
  ), toolbarAnchor && editor && /* @__PURE__ */ React5.createElement(InlineEditingToolbar, { anchor: toolbarAnchor, editor, id }));
@@ -2661,7 +2689,18 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
2661
2689
  refs.setReference(anchor);
2662
2690
  return () => refs.setReference(null);
2663
2691
  }, [anchor, refs]);
2664
- 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
+ ));
2665
2704
  };
2666
2705
 
2667
2706
  // src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
@@ -2695,8 +2734,8 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
2695
2734
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2696
2735
  var HISTORY_DEBOUNCE_WAIT = 800;
2697
2736
  var InlineEditingReplacement = class extends ReplacementBase {
2698
- inlineEditorRoot = null;
2699
2737
  handlerAttached = false;
2738
+ editing = false;
2700
2739
  getReplacementKey() {
2701
2740
  return "inline-editing";
2702
2741
  }
@@ -2704,7 +2743,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
2704
2743
  return Object.keys(INLINE_EDITING_PROPERTY_PER_TYPE);
2705
2744
  }
2706
2745
  isEditingModeActive() {
2707
- return !!this.inlineEditorRoot;
2746
+ return this.editing;
2708
2747
  }
2709
2748
  shouldRenderReplacement() {
2710
2749
  return this.isInlineEditingEligible() && (0, import_editor_v1_adapters12.getCurrentEditMode)() === "edit";
@@ -2747,8 +2786,8 @@ var InlineEditingReplacement = class extends ReplacementBase {
2747
2786
  resetInlineEditorRoot() {
2748
2787
  this.element.removeEventListener("click", this.handleRenderInlineEditor);
2749
2788
  this.handlerAttached = false;
2750
- this.inlineEditorRoot?.unmount?.();
2751
- this.inlineEditorRoot = null;
2789
+ this.reactRoot.render(null);
2790
+ this.editing = false;
2752
2791
  }
2753
2792
  unmountInlineEditor() {
2754
2793
  this.resetInlineEditorRoot();
@@ -2859,12 +2898,16 @@ var InlineEditingReplacement = class extends ReplacementBase {
2859
2898
  if (this.isEditingModeActive()) {
2860
2899
  this.resetInlineEditorRoot();
2861
2900
  }
2862
- 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();
2863
2906
  const propValue = this.getExtractedContentValue();
2864
2907
  const expectedTag = this.getExpectedTag();
2865
- this.element.innerHTML = "";
2866
- this.inlineEditorRoot = (0, import_client.createRoot)(this.element);
2867
- this.inlineEditorRoot.render(
2908
+ contentElement.innerHTML = "";
2909
+ this.editing = true;
2910
+ this.reactRoot.render(
2868
2911
  /* @__PURE__ */ React6.createElement(
2869
2912
  CanvasInlineEditor,
2870
2913
  {
@@ -2872,9 +2915,10 @@ var InlineEditingReplacement = class extends ReplacementBase {
2872
2915
  initialValue: propValue,
2873
2916
  expectedTag,
2874
2917
  rootElement: this.element,
2918
+ contentElement,
2875
2919
  id: this.id,
2876
2920
  setValue: this.setContentValue.bind(this),
2877
- onBlur: this.unmountInlineEditor.bind(this)
2921
+ requestDestroy: this.unmountInlineEditor.bind(this)
2878
2922
  }
2879
2923
  )
2880
2924
  );
@@ -2903,16 +2947,24 @@ var createViewWithReplacements = (options) => {
2903
2947
  return class extends TemplatedView {
2904
2948
  #replacement = null;
2905
2949
  #config;
2950
+ #reactContainer;
2951
+ #reactRoot;
2906
2952
  constructor(...args) {
2907
2953
  super(...args);
2908
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);
2909
2959
  this.#config = {
2910
2960
  getSetting: settings.get.bind(settings),
2911
2961
  setSetting: settings.set.bind(settings),
2912
2962
  element: this.el,
2913
2963
  type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
2914
2964
  id: this?.model?.get("id") ?? null,
2915
- refreshView: this.refreshView.bind(this)
2965
+ refreshView: this.refreshView.bind(this),
2966
+ reactRoot: this.#reactRoot,
2967
+ reactContainer: this.#reactContainer
2916
2968
  };
2917
2969
  }
2918
2970
  refreshView() {
@@ -2933,6 +2985,8 @@ var createViewWithReplacements = (options) => {
2933
2985
  }
2934
2986
  onDestroy() {
2935
2987
  this.#triggerAltMethod("onDestroy");
2988
+ this.#reactRoot.unmount();
2989
+ this.#reactContainer.remove();
2936
2990
  }
2937
2991
  _afterRender() {
2938
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";
@@ -2469,7 +2475,8 @@ var INLINE_EDITING_PROPERTY_PER_TYPE = {
2469
2475
  "e-button": "text",
2470
2476
  "e-form-label": "text",
2471
2477
  "e-heading": "title",
2472
- "e-paragraph": "paragraph"
2478
+ "e-paragraph": "paragraph",
2479
+ "e-form-submit-button": "text"
2473
2480
  };
2474
2481
  var getInlineEditorElement = (elementWrapper, expectedTag) => {
2475
2482
  return !expectedTag ? null : elementWrapper.querySelector(expectedTag);
@@ -2487,6 +2494,11 @@ var useOnClickOutsideIframe = (handleUnmount) => {
2487
2494
  };
2488
2495
  var useRenderToolbar = (ownerDocument, id) => {
2489
2496
  const [anchor, setAnchor] = useState4(null);
2497
+ useEffect7(() => {
2498
+ if (!anchor) {
2499
+ removeToolbarAnchor(ownerDocument, id);
2500
+ }
2501
+ }, [anchor, ownerDocument, id]);
2490
2502
  const onSelectionEnd = (view) => {
2491
2503
  const hasSelection = !view.state.selection.empty;
2492
2504
  removeToolbarAnchor(ownerDocument, id);
@@ -2496,7 +2508,10 @@ var useRenderToolbar = (ownerDocument, id) => {
2496
2508
  setAnchor(null);
2497
2509
  }
2498
2510
  };
2499
- return { onSelectionEnd, anchor };
2511
+ const clearAnchor = useCallback(() => {
2512
+ setAnchor(null);
2513
+ }, []);
2514
+ return { onSelectionEnd, anchor, clearAnchor };
2500
2515
  };
2501
2516
  var createAnchorBasedOnSelection = (ownerDocument, id) => {
2502
2517
  const frameWindow = ownerDocument.defaultView;
@@ -2563,46 +2578,59 @@ var horizontalShifterMiddleware = {
2563
2578
  };
2564
2579
 
2565
2580
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2566
- var EDITOR_WRAPPER_SELECTOR = "inline-editor-wrapper";
2567
2581
  var CanvasInlineEditor = ({
2568
2582
  elementClasses,
2569
2583
  initialValue,
2570
2584
  expectedTag,
2571
2585
  rootElement,
2586
+ contentElement,
2572
2587
  id,
2573
2588
  setValue,
2574
- ...props
2589
+ requestDestroy
2575
2590
  }) => {
2591
+ const [active, setActive] = useState5(true);
2576
2592
  const [editor, setEditor] = useState5(null);
2577
- const { onSelectionEnd, anchor: toolbarAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2578
- const onBlur = () => {
2579
- removeToolbarAnchor(rootElement.ownerDocument, id);
2580
- props.onBlur();
2581
- };
2582
- useOnClickOutsideIframe(onBlur);
2583
- return /* @__PURE__ */ React5.createElement(ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement("style", null, `
2584
- .ProseMirror > * {
2585
- height: 100%;
2586
- }
2587
- .${EDITOR_WRAPPER_SELECTOR} .ProseMirror > button[contenteditable="true"] {
2588
- height: auto;
2589
- cursor: text;
2590
- }
2591
- `), /* @__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(
2592
2620
  InlineEditor,
2593
2621
  {
2594
2622
  onEditorCreate: setEditor,
2623
+ mountElement: contentElement,
2595
2624
  editorProps: {
2596
2625
  attributes: {
2597
- 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;"
2598
2627
  }
2599
2628
  },
2600
2629
  elementClasses,
2601
2630
  value: initialValue,
2602
2631
  setValue,
2603
- onBlur,
2632
+ onBlur: dismiss,
2604
2633
  autofocus: true,
2605
- expectedTag,
2606
2634
  onSelectionEnd
2607
2635
  }
2608
2636
  ), toolbarAnchor && editor && /* @__PURE__ */ React5.createElement(InlineEditingToolbar, { anchor: toolbarAnchor, editor, id }));
@@ -2631,7 +2659,18 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
2631
2659
  refs.setReference(anchor);
2632
2660
  return () => refs.setReference(null);
2633
2661
  }, [anchor, refs]);
2634
- 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
+ ));
2635
2674
  };
2636
2675
 
2637
2676
  // src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
@@ -2665,8 +2704,8 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
2665
2704
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2666
2705
  var HISTORY_DEBOUNCE_WAIT = 800;
2667
2706
  var InlineEditingReplacement = class extends ReplacementBase {
2668
- inlineEditorRoot = null;
2669
2707
  handlerAttached = false;
2708
+ editing = false;
2670
2709
  getReplacementKey() {
2671
2710
  return "inline-editing";
2672
2711
  }
@@ -2674,7 +2713,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
2674
2713
  return Object.keys(INLINE_EDITING_PROPERTY_PER_TYPE);
2675
2714
  }
2676
2715
  isEditingModeActive() {
2677
- return !!this.inlineEditorRoot;
2716
+ return this.editing;
2678
2717
  }
2679
2718
  shouldRenderReplacement() {
2680
2719
  return this.isInlineEditingEligible() && getCurrentEditMode() === "edit";
@@ -2717,8 +2756,8 @@ var InlineEditingReplacement = class extends ReplacementBase {
2717
2756
  resetInlineEditorRoot() {
2718
2757
  this.element.removeEventListener("click", this.handleRenderInlineEditor);
2719
2758
  this.handlerAttached = false;
2720
- this.inlineEditorRoot?.unmount?.();
2721
- this.inlineEditorRoot = null;
2759
+ this.reactRoot.render(null);
2760
+ this.editing = false;
2722
2761
  }
2723
2762
  unmountInlineEditor() {
2724
2763
  this.resetInlineEditorRoot();
@@ -2829,12 +2868,16 @@ var InlineEditingReplacement = class extends ReplacementBase {
2829
2868
  if (this.isEditingModeActive()) {
2830
2869
  this.resetInlineEditorRoot();
2831
2870
  }
2832
- 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();
2833
2876
  const propValue = this.getExtractedContentValue();
2834
2877
  const expectedTag = this.getExpectedTag();
2835
- this.element.innerHTML = "";
2836
- this.inlineEditorRoot = createRoot(this.element);
2837
- this.inlineEditorRoot.render(
2878
+ contentElement.innerHTML = "";
2879
+ this.editing = true;
2880
+ this.reactRoot.render(
2838
2881
  /* @__PURE__ */ React6.createElement(
2839
2882
  CanvasInlineEditor,
2840
2883
  {
@@ -2842,9 +2885,10 @@ var InlineEditingReplacement = class extends ReplacementBase {
2842
2885
  initialValue: propValue,
2843
2886
  expectedTag,
2844
2887
  rootElement: this.element,
2888
+ contentElement,
2845
2889
  id: this.id,
2846
2890
  setValue: this.setContentValue.bind(this),
2847
- onBlur: this.unmountInlineEditor.bind(this)
2891
+ requestDestroy: this.unmountInlineEditor.bind(this)
2848
2892
  }
2849
2893
  )
2850
2894
  );
@@ -2873,16 +2917,24 @@ var createViewWithReplacements = (options) => {
2873
2917
  return class extends TemplatedView {
2874
2918
  #replacement = null;
2875
2919
  #config;
2920
+ #reactContainer;
2921
+ #reactRoot;
2876
2922
  constructor(...args) {
2877
2923
  super(...args);
2878
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);
2879
2929
  this.#config = {
2880
2930
  getSetting: settings.get.bind(settings),
2881
2931
  setSetting: settings.set.bind(settings),
2882
2932
  element: this.el,
2883
2933
  type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
2884
2934
  id: this?.model?.get("id") ?? null,
2885
- refreshView: this.refreshView.bind(this)
2935
+ refreshView: this.refreshView.bind(this),
2936
+ reactRoot: this.#reactRoot,
2937
+ reactContainer: this.#reactContainer
2886
2938
  };
2887
2939
  }
2888
2940
  refreshView() {
@@ -2903,6 +2955,8 @@ var createViewWithReplacements = (options) => {
2903
2955
  }
2904
2956
  onDestroy() {
2905
2957
  this.#triggerAltMethod("onDestroy");
2958
+ this.#reactRoot.unmount();
2959
+ this.#reactContainer.remove();
2906
2960
  }
2907
2961
  _afterRender() {
2908
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-739",
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-739",
40
+ "@elementor/editor": "4.1.0-741",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.1.0-739",
43
- "@elementor/editor-documents": "4.1.0-739",
44
- "@elementor/editor-elements": "4.1.0-739",
45
- "@elementor/editor-interactions": "4.1.0-739",
46
- "@elementor/editor-mcp": "4.1.0-739",
47
- "@elementor/editor-notifications": "4.1.0-739",
48
- "@elementor/editor-props": "4.1.0-739",
49
- "@elementor/editor-responsive": "4.1.0-739",
50
- "@elementor/editor-styles": "4.1.0-739",
51
- "@elementor/editor-styles-repository": "4.1.0-739",
52
- "@elementor/editor-ui": "4.1.0-739",
53
- "@elementor/editor-v1-adapters": "4.1.0-739",
54
- "@elementor/schema": "4.1.0-739",
55
- "@elementor/twing": "4.1.0-739",
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-739",
58
- "@elementor/wp-media": "4.1.0-739",
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
  }
@@ -43,6 +43,7 @@ export const INLINE_EDITING_PROPERTY_PER_TYPE: Record< string, string > = {
43
43
  'e-form-label': 'text',
44
44
  'e-heading': 'title',
45
45
  'e-paragraph': 'paragraph',
46
+ 'e-form-submit-button': 'text',
46
47
  };
47
48
 
48
49
  export const legacyWindow = window as unknown as LegacyWindow;
@@ -79,6 +80,12 @@ export const useOnClickOutsideIframe = ( handleUnmount: () => void ) => {
79
80
  export const useRenderToolbar = ( ownerDocument: Document, id: string ) => {
80
81
  const [ anchor, setAnchor ] = useState< HTMLElement | null >( null );
81
82
 
83
+ useEffect( () => {
84
+ if ( ! anchor ) {
85
+ removeToolbarAnchor( ownerDocument, id );
86
+ }
87
+ }, [ anchor, ownerDocument, id ] );
88
+
82
89
  const onSelectionEnd = ( view: EditorView ) => {
83
90
  const hasSelection = ! view.state.selection.empty;
84
91
 
@@ -91,7 +98,11 @@ export const useRenderToolbar = ( ownerDocument: Document, id: string ) => {
91
98
  }
92
99
  };
93
100
 
94
- return { onSelectionEnd, anchor };
101
+ const clearAnchor = useCallback( () => {
102
+ setAnchor( null );
103
+ }, [] );
104
+
105
+ return { onSelectionEnd, anchor, clearAnchor };
95
106
  };
96
107
 
97
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
  };