@elementor/editor-canvas 4.4.0-1135 → 4.4.0-1137

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.js CHANGED
@@ -187,7 +187,9 @@ var SINGLE_BLOCK_KEY = "default";
187
187
  var convertBlocks = async (blocks) => {
188
188
  const { data } = await (0, import_http_client2.httpService)().post(
189
189
  CSS_TO_ATOMIC_URL,
190
- { blocks }
190
+ {
191
+ blocks
192
+ }
191
193
  );
192
194
  return data.data;
193
195
  };
@@ -1795,6 +1797,7 @@ var import_i18n = require("@wordpress/i18n");
1795
1797
  // src/form-structure/utils.ts
1796
1798
  var import_editor_elements4 = require("@elementor/editor-elements");
1797
1799
  var FORM_ELEMENT_TYPE = "e-form";
1800
+ var FORM_HIDDEN_FIELD_ELEMENT_TYPE = "e-form-hidden";
1798
1801
  var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1799
1802
  "e-form-input",
1800
1803
  "e-form-textarea",
@@ -1805,7 +1808,8 @@ var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1805
1808
  "e-form-radio-button",
1806
1809
  "e-form-file-upload",
1807
1810
  "e-form-date-picker",
1808
- "e-form-time-picker"
1811
+ "e-form-time-picker",
1812
+ FORM_HIDDEN_FIELD_ELEMENT_TYPE
1809
1813
  ]);
1810
1814
  function getArgsElementType(args) {
1811
1815
  return args.model?.widgetType || args.model?.elType;
@@ -1922,25 +1926,49 @@ function handleBlockedFormField() {
1922
1926
  (0, import_editor_notifications.notify)(FORM_FIELDS_OUTSIDE_ALERT);
1923
1927
  }
1924
1928
 
1925
- // src/form-structure/prevent-form-nesting-commands.ts
1929
+ // src/form-structure/hidden-field-hint.ts
1926
1930
  var import_editor_notifications2 = require("@elementor/editor-notifications");
1927
1931
  var import_editor_v1_adapters12 = require("@elementor/editor-v1-adapters");
1928
1932
  var import_i18n2 = require("@wordpress/i18n");
1933
+ var MOVE_COMMAND = "document/elements/move";
1934
+ var notificationCounter = 0;
1935
+ function createHiddenFieldNotification() {
1936
+ return {
1937
+ type: "default",
1938
+ message: (0, import_i18n2.__)("Hidden field added - invisible on your form. Manage it in the Structure panel.", "elementor"),
1939
+ id: `hidden-field-added-${++notificationCounter}`
1940
+ };
1941
+ }
1942
+ function initHiddenFieldHint() {
1943
+ (0, import_editor_v1_adapters12.registerDataHook)("after", "document/elements/create", (args, _result, options) => {
1944
+ if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND)) {
1945
+ return;
1946
+ }
1947
+ if (getArgsElementType(args) === FORM_HIDDEN_FIELD_ELEMENT_TYPE) {
1948
+ (0, import_editor_notifications2.notify)(createHiddenFieldNotification());
1949
+ }
1950
+ });
1951
+ }
1952
+
1953
+ // src/form-structure/prevent-form-nesting-commands.ts
1954
+ var import_editor_notifications3 = require("@elementor/editor-notifications");
1955
+ var import_editor_v1_adapters13 = require("@elementor/editor-v1-adapters");
1956
+ var import_i18n3 = require("@wordpress/i18n");
1929
1957
  var FORM_NESTING_ALERT = {
1930
1958
  type: "default",
1931
- message: (0, import_i18n2.__)("Forms can't be nested. Create separate forms instead.", "elementor"),
1959
+ message: (0, import_i18n3.__)("Forms can't be nested. Create separate forms instead.", "elementor"),
1932
1960
  id: "form-nesting-blocked"
1933
1961
  };
1934
1962
  function initFormNestingPrevention() {
1935
- (0, import_editor_v1_adapters12.blockCommand)({
1963
+ (0, import_editor_v1_adapters13.blockCommand)({
1936
1964
  command: "document/elements/create",
1937
1965
  condition: blockFormCreate
1938
1966
  });
1939
- (0, import_editor_v1_adapters12.blockCommand)({
1967
+ (0, import_editor_v1_adapters13.blockCommand)({
1940
1968
  command: "document/elements/move",
1941
1969
  condition: blockFormMove
1942
1970
  });
1943
- (0, import_editor_v1_adapters12.blockCommand)({
1971
+ (0, import_editor_v1_adapters13.blockCommand)({
1944
1972
  command: "document/elements/paste",
1945
1973
  condition: blockFormPaste
1946
1974
  });
@@ -1984,7 +2012,7 @@ function blockFormPaste(args) {
1984
2012
  return false;
1985
2013
  }
1986
2014
  function handleBlockedFormField2() {
1987
- (0, import_editor_notifications2.notify)(FORM_NESTING_ALERT);
2015
+ (0, import_editor_notifications3.notify)(FORM_NESTING_ALERT);
1988
2016
  }
1989
2017
 
1990
2018
  // src/settings-transformers-registry.ts
@@ -2180,6 +2208,8 @@ var svgSrcTransformer = createTransformer(async (value, { signal }) => {
2180
2208
  var EMPTY_ICON_RESULT = { html: null, url: null };
2181
2209
  var ICON_SVG_SIZE = "100%";
2182
2210
  var ICON_SVG_OVERFLOW = "visible";
2211
+ var FONT_AWESOME_LIBRARY_PREFIX = "fa-";
2212
+ var EICONS_LIBRARY = "eicons";
2183
2213
  var DEFAULT_SVG_RELATIVE_PATH = "images/default-svg.svg";
2184
2214
  var iconTransformer = createTransformer(async (value, { signal }) => {
2185
2215
  const iconValue = typeof value.value === "string" ? value.value : null;
@@ -2191,14 +2221,14 @@ var iconTransformer = createTransformer(async (value, { signal }) => {
2191
2221
  if (fontAwesomeHtml) {
2192
2222
  return { html: fontAwesomeHtml, url: null };
2193
2223
  }
2194
- if ((0, import_editor_controls.isDeletedCustomIconLibrary)(library, iconValue)) {
2195
- return resolveDefaultSvg(signal);
2224
+ if (library.startsWith(FONT_AWESOME_LIBRARY_PREFIX) || EICONS_LIBRARY === library) {
2225
+ return EMPTY_ICON_RESULT;
2196
2226
  }
2197
- const customHtml = await resolveCustomSvg(library, iconValue, signal);
2198
- if (customHtml) {
2199
- return { html: customHtml, url: null };
2227
+ const providerHtml = await resolveProviderSvg(library, iconValue, signal);
2228
+ if (providerHtml) {
2229
+ return { html: providerHtml, url: null };
2200
2230
  }
2201
- return EMPTY_ICON_RESULT;
2231
+ return resolveDefaultSvg(signal);
2202
2232
  });
2203
2233
  async function resolveDefaultSvg(signal) {
2204
2234
  const assetsUrl = window.elementorCommon?.config?.urls?.assets;
@@ -2213,8 +2243,8 @@ async function resolveDefaultSvg(signal) {
2213
2243
  { key: "svg", signal }
2214
2244
  );
2215
2245
  }
2216
- async function resolveCustomSvg(library, iconValue, signal) {
2217
- const markup = await (0, import_editor_controls.resolveCustomIconSvg)(library, iconValue, signal);
2246
+ async function resolveProviderSvg(library, iconValue, signal) {
2247
+ const markup = await (0, import_editor_controls.resolveProviderIconSvg)(library, iconValue, signal);
2218
2248
  return markup ? processIconSvgContent(markup) : null;
2219
2249
  }
2220
2250
  async function resolveFontAwesomeSvg(library, iconValue, signal) {
@@ -2674,7 +2704,7 @@ function initStyleTransformers() {
2674
2704
 
2675
2705
  // src/legacy/init-legacy-views.ts
2676
2706
  var import_editor_elements8 = require("@elementor/editor-elements");
2677
- var import_editor_v1_adapters14 = require("@elementor/editor-v1-adapters");
2707
+ var import_editor_v1_adapters15 = require("@elementor/editor-v1-adapters");
2678
2708
 
2679
2709
  // src/renderers/create-dom-renderer.ts
2680
2710
  var import_twing = require("@elementor/twing");
@@ -3425,8 +3455,8 @@ var import_client = require("react-dom/client");
3425
3455
  var React11 = __toESM(require("react"));
3426
3456
  var import_editor_elements7 = require("@elementor/editor-elements");
3427
3457
  var import_editor_props4 = require("@elementor/editor-props");
3428
- var import_editor_v1_adapters13 = require("@elementor/editor-v1-adapters");
3429
- var import_i18n3 = require("@wordpress/i18n");
3458
+ var import_editor_v1_adapters14 = require("@elementor/editor-v1-adapters");
3459
+ var import_i18n4 = require("@wordpress/i18n");
3430
3460
 
3431
3461
  // src/legacy/replacements/base.ts
3432
3462
  var TRIGGER_TIMING = {
@@ -3743,7 +3773,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3743
3773
  return this.editing;
3744
3774
  }
3745
3775
  shouldRenderReplacement() {
3746
- return this.isInlineEditingEligible() && (0, import_editor_v1_adapters13.getCurrentEditMode)() === "edit";
3776
+ return this.isInlineEditingEligible() && (0, import_editor_v1_adapters14.getCurrentEditMode)() === "edit";
3747
3777
  }
3748
3778
  handleRenderInlineEditor = () => {
3749
3779
  if (this.isEditingModeActive() || !this.isInlineEditingEligible()) {
@@ -3830,7 +3860,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3830
3860
  setContentValue(value) {
3831
3861
  const settingKey = this.getInlineEditablePropertyName();
3832
3862
  const valueToSave = this.createContentPropValue(value);
3833
- (0, import_editor_v1_adapters13.undoable)(
3863
+ (0, import_editor_v1_adapters14.undoable)(
3834
3864
  {
3835
3865
  do: () => {
3836
3866
  const prevValue = this.getInlineEditablePropValue();
@@ -3844,7 +3874,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3844
3874
  {
3845
3875
  title: (0, import_editor_elements7.getElementLabel)(this.id),
3846
3876
  // translators: %s is the name of the property that was edited.
3847
- subtitle: (0, import_i18n3.__)("%s edited", "elementor").replace(
3877
+ subtitle: (0, import_i18n4.__)("%s edited", "elementor").replace(
3848
3878
  "%s",
3849
3879
  this.getInlineEditablePropTypeKey() ?? "Inline editing"
3850
3880
  ),
@@ -3872,7 +3902,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3872
3902
  return null;
3873
3903
  }
3874
3904
  runCommand(key, value) {
3875
- (0, import_editor_v1_adapters13.__privateRunCommandSync)(
3905
+ (0, import_editor_v1_adapters14.__privateRunCommandSync)(
3876
3906
  "document/elements/set-settings",
3877
3907
  {
3878
3908
  container: (0, import_editor_elements7.getContainer)(this.id),
@@ -3882,7 +3912,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3882
3912
  },
3883
3913
  { internal: true }
3884
3914
  );
3885
- (0, import_editor_v1_adapters13.__privateRunCommandSync)("document/save/set-is-modified", { status: true }, { internal: true });
3915
+ (0, import_editor_v1_adapters14.__privateRunCommandSync)("document/save/set-is-modified", { status: true }, { internal: true });
3886
3916
  }
3887
3917
  getExpectedTag() {
3888
3918
  const tagPropType = this.getTagPropType();
@@ -4047,12 +4077,12 @@ function registerModelExtensions(type, extensions) {
4047
4077
  }
4048
4078
  function registerElementType(type, elementTypeGenerator) {
4049
4079
  elementsLegacyTypes[type] = elementTypeGenerator;
4050
- if ((0, import_editor_v1_adapters14.__privateIsReady)()) {
4080
+ if ((0, import_editor_v1_adapters15.__privateIsReady)()) {
4051
4081
  registerElementInLegacyManager(type, createDomRenderer());
4052
4082
  }
4053
4083
  }
4054
4084
  function initLegacyViews() {
4055
- (0, import_editor_v1_adapters14.__privateListenTo)((0, import_editor_v1_adapters14.v1ReadyEvent)(), () => {
4085
+ (0, import_editor_v1_adapters15.__privateListenTo)((0, import_editor_v1_adapters15.v1ReadyEvent)(), () => {
4056
4086
  const widgetsCache = (0, import_editor_elements8.getWidgetsCache)() ?? {};
4057
4087
  const renderer = createDomRenderer();
4058
4088
  registerProPromotionTypes(widgetsCache);
@@ -4190,9 +4220,9 @@ function initTabsModelExtensions() {
4190
4220
  var import_editor_props8 = require("@elementor/editor-props");
4191
4221
 
4192
4222
  // src/mcp/icon-library-experiment.ts
4193
- var import_editor_v1_adapters15 = require("@elementor/editor-v1-adapters");
4223
+ var import_editor_v1_adapters16 = require("@elementor/editor-v1-adapters");
4194
4224
  var ICON_LIBRARY_EXPERIMENT = "e_svg_library";
4195
- var isIconLibraryActive = () => (0, import_editor_v1_adapters15.isExperimentActive)(ICON_LIBRARY_EXPERIMENT);
4225
+ var isIconLibraryActive = () => (0, import_editor_v1_adapters16.isExperimentActive)(ICON_LIBRARY_EXPERIMENT);
4196
4226
 
4197
4227
  // src/mcp/resources/available-widgets-resource.ts
4198
4228
  var import_http_client3 = require("@elementor/http-client");
@@ -4270,7 +4300,7 @@ var initBestPracticesResource = (reg) => {
4270
4300
 
4271
4301
  // src/mcp/resources/document-structure-resource.ts
4272
4302
  var import_editor_elements9 = require("@elementor/editor-elements");
4273
- var import_editor_v1_adapters16 = require("@elementor/editor-v1-adapters");
4303
+ var import_editor_v1_adapters17 = require("@elementor/editor-v1-adapters");
4274
4304
  var DOCUMENT_STRUCTURE_URI = "elementor://document/structure";
4275
4305
  var initDocumentStructureResource = (reg) => {
4276
4306
  const { resource, sendResourceUpdated } = reg;
@@ -4283,15 +4313,15 @@ var initDocumentStructureResource = (reg) => {
4283
4313
  sendResourceUpdated({ uri: DOCUMENT_STRUCTURE_URI });
4284
4314
  }
4285
4315
  };
4286
- (0, import_editor_v1_adapters16.__privateListenTo)(
4316
+ (0, import_editor_v1_adapters17.__privateListenTo)(
4287
4317
  [
4288
- (0, import_editor_v1_adapters16.commandEndEvent)("document/elements/create"),
4289
- (0, import_editor_v1_adapters16.commandEndEvent)("document/elements/delete"),
4290
- (0, import_editor_v1_adapters16.commandEndEvent)("document/elements/move"),
4291
- (0, import_editor_v1_adapters16.commandEndEvent)("document/elements/copy"),
4292
- (0, import_editor_v1_adapters16.commandEndEvent)("document/elements/paste"),
4293
- (0, import_editor_v1_adapters16.commandEndEvent)("editor/documents/attach-preview"),
4294
- (0, import_editor_v1_adapters16.commandEndEvent)("editor/documents/switch")
4318
+ (0, import_editor_v1_adapters17.commandEndEvent)("document/elements/create"),
4319
+ (0, import_editor_v1_adapters17.commandEndEvent)("document/elements/delete"),
4320
+ (0, import_editor_v1_adapters17.commandEndEvent)("document/elements/move"),
4321
+ (0, import_editor_v1_adapters17.commandEndEvent)("document/elements/copy"),
4322
+ (0, import_editor_v1_adapters17.commandEndEvent)("document/elements/paste"),
4323
+ (0, import_editor_v1_adapters17.commandEndEvent)("editor/documents/attach-preview"),
4324
+ (0, import_editor_v1_adapters17.commandEndEvent)("editor/documents/switch")
4295
4325
  ],
4296
4326
  updateDocumentStructure
4297
4327
  );
@@ -4394,7 +4424,7 @@ var initDynamicTagsResource = (reg) => {
4394
4424
  };
4395
4425
 
4396
4426
  // src/mcp/resources/editor-state-resource.ts
4397
- var import_editor_v1_adapters17 = require("@elementor/editor-v1-adapters");
4427
+ var import_editor_v1_adapters18 = require("@elementor/editor-v1-adapters");
4398
4428
  var CURRENTLY_VIEWED_SCREEN = "The user is currently viewing the Elementor editor";
4399
4429
  var PAGE_CONTENT_CHARACTER_LIMIT = 500;
4400
4430
  var PREVIEW_TEXT_NODE_MIN_LENGTH = 2;
@@ -4415,8 +4445,8 @@ var initEditorStateResource = (reg) => {
4415
4445
  lastSerializedState = serialized;
4416
4446
  sendResourceUpdated({ uri: EDITOR_STATE_URI });
4417
4447
  };
4418
- (0, import_editor_v1_adapters17.__privateListenTo)(
4419
- [(0, import_editor_v1_adapters17.commandEndEvent)("editor/documents/switch"), (0, import_editor_v1_adapters17.commandEndEvent)("editor/documents/attach-preview")],
4448
+ (0, import_editor_v1_adapters18.__privateListenTo)(
4449
+ [(0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/switch"), (0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/attach-preview")],
4420
4450
  notifyIfChanged
4421
4451
  );
4422
4452
  lastSerializedState = JSON.stringify(buildState());
@@ -4490,7 +4520,7 @@ function getPageTitle() {
4490
4520
  }
4491
4521
 
4492
4522
  // src/mcp/resources/general-context-resource.ts
4493
- var import_editor_v1_adapters18 = require("@elementor/editor-v1-adapters");
4523
+ var import_editor_v1_adapters19 = require("@elementor/editor-v1-adapters");
4494
4524
  var GENERAL_CONTEXT_URI = "elementor://context/general";
4495
4525
  var initGeneralContextResource = (reg) => {
4496
4526
  const { resource, sendResourceUpdated } = reg;
@@ -4551,11 +4581,11 @@ var initGeneralContextResource = (reg) => {
4551
4581
  };
4552
4582
  }
4553
4583
  );
4554
- (0, import_editor_v1_adapters18.__privateListenTo)(
4584
+ (0, import_editor_v1_adapters19.__privateListenTo)(
4555
4585
  [
4556
- (0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/switch"),
4557
- (0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/attach-preview"),
4558
- (0, import_editor_v1_adapters18.commandEndEvent)("document/elements/settings")
4586
+ (0, import_editor_v1_adapters19.commandEndEvent)("editor/documents/switch"),
4587
+ (0, import_editor_v1_adapters19.commandEndEvent)("editor/documents/attach-preview"),
4588
+ (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/settings")
4559
4589
  ],
4560
4590
  pushUpdateIfChanged
4561
4591
  );
@@ -4564,7 +4594,7 @@ var initGeneralContextResource = (reg) => {
4564
4594
 
4565
4595
  // src/mcp/resources/selected-element-resource.ts
4566
4596
  var import_editor_elements10 = require("@elementor/editor-elements");
4567
- var import_editor_v1_adapters19 = require("@elementor/editor-v1-adapters");
4597
+ var import_editor_v1_adapters20 = require("@elementor/editor-v1-adapters");
4568
4598
  var SELECTED_ELEMENT_URI = "elementor://context/selected-element";
4569
4599
  var initSelectedElementResource = (reg) => {
4570
4600
  const { resource, sendResourceUpdated } = reg;
@@ -4595,11 +4625,11 @@ var initSelectedElementResource = (reg) => {
4595
4625
  }
4596
4626
  publishIfChanged(readSelectionFromEditor());
4597
4627
  };
4598
- (0, import_editor_v1_adapters19.__privateListenTo)(
4628
+ (0, import_editor_v1_adapters20.__privateListenTo)(
4599
4629
  [
4600
- (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/select"),
4601
- (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/deselect-all"),
4602
- (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/settings")
4630
+ (0, import_editor_v1_adapters20.commandEndEvent)("document/elements/select"),
4631
+ (0, import_editor_v1_adapters20.commandEndEvent)("document/elements/deselect-all"),
4632
+ (0, import_editor_v1_adapters20.commandEndEvent)("document/elements/settings")
4603
4633
  ],
4604
4634
  onCommand
4605
4635
  );
@@ -4717,7 +4747,7 @@ var import_editor_props7 = require("@elementor/editor-props");
4717
4747
  var import_editor_elements12 = require("@elementor/editor-elements");
4718
4748
  var import_editor_props6 = require("@elementor/editor-props");
4719
4749
  var import_editor_styles4 = require("@elementor/editor-styles");
4720
- var import_editor_v1_adapters21 = require("@elementor/editor-v1-adapters");
4750
+ var import_editor_v1_adapters22 = require("@elementor/editor-v1-adapters");
4721
4751
 
4722
4752
  // src/mcp/utils/merge-custom-css.ts
4723
4753
  var CUSTOM_CSS_SEPARATOR = "\n";
@@ -4786,11 +4816,11 @@ function resolveCanonicalPropKeys(elementType, props) {
4786
4816
  }
4787
4817
 
4788
4818
  // src/mcp/utils/resolve-dynamic-tag.ts
4789
- var import_editor_v1_adapters20 = require("@elementor/editor-v1-adapters");
4819
+ var import_editor_v1_adapters21 = require("@elementor/editor-v1-adapters");
4790
4820
  var DYNAMIC_PROP_TYPE_KEY = "dynamic";
4791
4821
  var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
4792
4822
  var getAtomicDynamicTags = () => {
4793
- const config = (0, import_editor_v1_adapters20.getElementorConfig)();
4823
+ const config = (0, import_editor_v1_adapters21.getElementorConfig)();
4794
4824
  return config.atomicDynamicTags?.tags ?? {};
4795
4825
  };
4796
4826
  var getDynamicTagNamesByCategories = (categories) => {
@@ -4991,7 +5021,7 @@ Expected Schema: ${jsonSchema}`
4991
5021
  },
4992
5022
  withHistory: false
4993
5023
  });
4994
- (0, import_editor_v1_adapters21.__privateRunCommandSync)("document/save/set-is-modified", { status: true }, { internal: true });
5024
+ (0, import_editor_v1_adapters22.__privateRunCommandSync)("document/save/set-is-modified", { status: true }, { internal: true });
4995
5025
  };
4996
5026
 
4997
5027
  // src/mcp/tools/configure-element/prompt.ts
@@ -5601,15 +5631,15 @@ Note: The "size" property controls image resolution/loading, not visual size. Se
5601
5631
 
5602
5632
  // src/prevent-link-in-link-commands.ts
5603
5633
  var import_editor_elements14 = require("@elementor/editor-elements");
5604
- var import_editor_notifications3 = require("@elementor/editor-notifications");
5605
- var import_editor_v1_adapters22 = require("@elementor/editor-v1-adapters");
5606
- var import_i18n4 = require("@wordpress/i18n");
5634
+ var import_editor_notifications4 = require("@elementor/editor-notifications");
5635
+ var import_editor_v1_adapters23 = require("@elementor/editor-v1-adapters");
5636
+ var import_i18n5 = require("@wordpress/i18n");
5607
5637
  function initLinkInLinkPrevention() {
5608
- (0, import_editor_v1_adapters22.blockCommand)({
5638
+ (0, import_editor_v1_adapters23.blockCommand)({
5609
5639
  command: "document/elements/paste",
5610
5640
  condition: blockLinkInLinkPaste
5611
5641
  });
5612
- (0, import_editor_v1_adapters22.blockCommand)({
5642
+ (0, import_editor_v1_adapters23.blockCommand)({
5613
5643
  command: "document/elements/move",
5614
5644
  condition: blockLinkInLinkMove
5615
5645
  });
@@ -5637,7 +5667,7 @@ function blockLinkInLinkPaste(args) {
5637
5667
  const sourceElements = data.clipboard.elements;
5638
5668
  const notification = {
5639
5669
  type: "default",
5640
- message: (0, import_i18n4.__)(
5670
+ message: (0, import_i18n5.__)(
5641
5671
  "To paste a link to this element, first remove the link from it's parent container.",
5642
5672
  "elementor"
5643
5673
  ),
@@ -5646,7 +5676,7 @@ function blockLinkInLinkPaste(args) {
5646
5676
  };
5647
5677
  const blocked = shouldBlock(sourceElements, targetElements);
5648
5678
  if (blocked) {
5649
- (0, import_editor_notifications3.notify)(notification);
5679
+ (0, import_editor_notifications4.notify)(notification);
5650
5680
  }
5651
5681
  return blocked;
5652
5682
  }
@@ -5656,13 +5686,13 @@ function blockLinkInLinkMove(args) {
5656
5686
  const targetElement = target;
5657
5687
  const notification = {
5658
5688
  type: "default",
5659
- message: (0, import_i18n4.__)("To drag a link to this element, first remove the link from it's parent container.", "elementor"),
5689
+ message: (0, import_i18n5.__)("To drag a link to this element, first remove the link from it's parent container.", "elementor"),
5660
5690
  id: "move-in-link-blocked",
5661
5691
  additionalActionProps: [learnMoreActionProps]
5662
5692
  };
5663
5693
  const isBlocked = shouldBlock(sourceElements, [targetElement]);
5664
5694
  if (isBlocked) {
5665
- (0, import_editor_notifications3.notify)(notification);
5695
+ (0, import_editor_notifications4.notify)(notification);
5666
5696
  }
5667
5697
  return isBlocked;
5668
5698
  }
@@ -5685,12 +5715,12 @@ function shouldBlock(sourceElements, targetElements) {
5685
5715
  // src/style-commands/paste-style.ts
5686
5716
  var import_editor_elements17 = require("@elementor/editor-elements");
5687
5717
  var import_editor_props10 = require("@elementor/editor-props");
5688
- var import_editor_v1_adapters24 = require("@elementor/editor-v1-adapters");
5718
+ var import_editor_v1_adapters25 = require("@elementor/editor-v1-adapters");
5689
5719
 
5690
5720
  // src/utils/command-utils.ts
5691
5721
  var import_editor_elements15 = require("@elementor/editor-elements");
5692
5722
  var import_editor_props9 = require("@elementor/editor-props");
5693
- var import_i18n5 = require("@wordpress/i18n");
5723
+ var import_i18n6 = require("@wordpress/i18n");
5694
5724
  function hasAtomicWidgets(args) {
5695
5725
  const { containers = [args.container] } = args;
5696
5726
  return containers.some(isAtomicWidget);
@@ -5726,15 +5756,15 @@ function getClipboardElements(storageKey = "clipboard") {
5726
5756
  }
5727
5757
  }
5728
5758
  function getTitleForContainers(containers) {
5729
- return containers.length > 1 ? (0, import_i18n5.__)("Elements", "elementor") : (0, import_editor_elements15.getElementLabel)(containers[0].id);
5759
+ return containers.length > 1 ? (0, import_i18n6.__)("Elements", "elementor") : (0, import_editor_elements15.getElementLabel)(containers[0].id);
5730
5760
  }
5731
5761
 
5732
5762
  // src/style-commands/undoable-actions/paste-element-style.ts
5733
5763
  var import_editor_elements16 = require("@elementor/editor-elements");
5734
5764
  var import_editor_styles_repository4 = require("@elementor/editor-styles-repository");
5735
- var import_editor_v1_adapters23 = require("@elementor/editor-v1-adapters");
5736
- var import_i18n6 = require("@wordpress/i18n");
5737
- var undoablePasteElementStyle = () => (0, import_editor_v1_adapters23.undoable)(
5765
+ var import_editor_v1_adapters24 = require("@elementor/editor-v1-adapters");
5766
+ var import_i18n7 = require("@wordpress/i18n");
5767
+ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters24.undoable)(
5738
5768
  {
5739
5769
  do: ({ containers, newStyle }) => {
5740
5770
  return containers.map((container) => {
@@ -5803,19 +5833,19 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters23.undoable)(
5803
5833
  },
5804
5834
  {
5805
5835
  title: ({ containers }) => getTitleForContainers(containers),
5806
- subtitle: (0, import_i18n6.__)("Style Pasted", "elementor")
5836
+ subtitle: (0, import_i18n7.__)("Style Pasted", "elementor")
5807
5837
  }
5808
5838
  );
5809
5839
 
5810
5840
  // src/style-commands/paste-style.ts
5811
5841
  function initPasteStyleCommand() {
5812
5842
  const pasteElementStyleCommand = undoablePasteElementStyle();
5813
- (0, import_editor_v1_adapters24.blockCommand)({
5843
+ (0, import_editor_v1_adapters25.blockCommand)({
5814
5844
  command: "document/elements/paste-style",
5815
5845
  condition: hasAtomicWidgets
5816
5846
  });
5817
- (0, import_editor_v1_adapters24.__privateListenTo)(
5818
- (0, import_editor_v1_adapters24.commandStartEvent)("document/elements/paste-style"),
5847
+ (0, import_editor_v1_adapters25.__privateListenTo)(
5848
+ (0, import_editor_v1_adapters25.commandStartEvent)("document/elements/paste-style"),
5819
5849
  (e) => pasteStyles(e.args, pasteElementStyleCommand)
5820
5850
  );
5821
5851
  }
@@ -5866,14 +5896,14 @@ function pasteClasses(containers, classes) {
5866
5896
  }
5867
5897
 
5868
5898
  // src/style-commands/reset-style.ts
5869
- var import_editor_v1_adapters26 = require("@elementor/editor-v1-adapters");
5899
+ var import_editor_v1_adapters27 = require("@elementor/editor-v1-adapters");
5870
5900
 
5871
5901
  // src/style-commands/undoable-actions/reset-element-style.ts
5872
5902
  var import_editor_elements18 = require("@elementor/editor-elements");
5873
5903
  var import_editor_styles_repository5 = require("@elementor/editor-styles-repository");
5874
- var import_editor_v1_adapters25 = require("@elementor/editor-v1-adapters");
5875
- var import_i18n7 = require("@wordpress/i18n");
5876
- var undoableResetElementStyle = () => (0, import_editor_v1_adapters25.undoable)(
5904
+ var import_editor_v1_adapters26 = require("@elementor/editor-v1-adapters");
5905
+ var import_i18n8 = require("@wordpress/i18n");
5906
+ var undoableResetElementStyle = () => (0, import_editor_v1_adapters26.undoable)(
5877
5907
  {
5878
5908
  do: ({ containers }) => {
5879
5909
  return containers.map((container) => {
@@ -5910,19 +5940,19 @@ var undoableResetElementStyle = () => (0, import_editor_v1_adapters25.undoable)(
5910
5940
  },
5911
5941
  {
5912
5942
  title: ({ containers }) => getTitleForContainers(containers),
5913
- subtitle: (0, import_i18n7.__)("Style Reset", "elementor")
5943
+ subtitle: (0, import_i18n8.__)("Style Reset", "elementor")
5914
5944
  }
5915
5945
  );
5916
5946
 
5917
5947
  // src/style-commands/reset-style.ts
5918
5948
  function initResetStyleCommand() {
5919
5949
  const resetElementStyles = undoableResetElementStyle();
5920
- (0, import_editor_v1_adapters26.blockCommand)({
5950
+ (0, import_editor_v1_adapters27.blockCommand)({
5921
5951
  command: "document/elements/reset-style",
5922
5952
  condition: hasAtomicWidgets
5923
5953
  });
5924
- (0, import_editor_v1_adapters26.__privateListenTo)(
5925
- (0, import_editor_v1_adapters26.commandStartEvent)("document/elements/reset-style"),
5954
+ (0, import_editor_v1_adapters27.__privateListenTo)(
5955
+ (0, import_editor_v1_adapters27.commandStartEvent)("document/elements/reset-style"),
5926
5956
  (e) => resetStyles(e.args, resetElementStyles)
5927
5957
  );
5928
5958
  }
@@ -5948,6 +5978,7 @@ function init() {
5948
5978
  initLinkInLinkPrevention();
5949
5979
  initFormNestingPrevention();
5950
5980
  initFormAncestorEnforcement();
5981
+ initHiddenFieldHint();
5951
5982
  initViewReplacements();
5952
5983
  initLegacyViews();
5953
5984
  initSettingsTransformers();
@@ -6071,9 +6102,9 @@ function getRectClipPath(rect, viewport) {
6071
6102
  }
6072
6103
 
6073
6104
  // src/hooks/use-canvas-document.ts
6074
- var import_editor_v1_adapters27 = require("@elementor/editor-v1-adapters");
6105
+ var import_editor_v1_adapters28 = require("@elementor/editor-v1-adapters");
6075
6106
  function useCanvasDocument() {
6076
- return (0, import_editor_v1_adapters27.__privateUseListenTo)((0, import_editor_v1_adapters27.commandEndEvent)("editor/documents/attach-preview"), () => (0, import_editor_v1_adapters27.getCanvasIframeDocument)());
6107
+ return (0, import_editor_v1_adapters28.__privateUseListenTo)((0, import_editor_v1_adapters28.commandEndEvent)("editor/documents/attach-preview"), () => (0, import_editor_v1_adapters28.getCanvasIframeDocument)());
6077
6108
  }
6078
6109
 
6079
6110
  // src/hooks/use-escape-on-canvas.ts
package/dist/index.mjs CHANGED
@@ -118,7 +118,9 @@ var SINGLE_BLOCK_KEY = "default";
118
118
  var convertBlocks = async (blocks) => {
119
119
  const { data } = await httpService2().post(
120
120
  CSS_TO_ATOMIC_URL,
121
- { blocks }
121
+ {
122
+ blocks
123
+ }
122
124
  );
123
125
  return data.data;
124
126
  };
@@ -1747,6 +1749,7 @@ import { __ } from "@wordpress/i18n";
1747
1749
  // src/form-structure/utils.ts
1748
1750
  import { getAllDescendants } from "@elementor/editor-elements";
1749
1751
  var FORM_ELEMENT_TYPE = "e-form";
1752
+ var FORM_HIDDEN_FIELD_ELEMENT_TYPE = "e-form-hidden";
1750
1753
  var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1751
1754
  "e-form-input",
1752
1755
  "e-form-textarea",
@@ -1757,7 +1760,8 @@ var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1757
1760
  "e-form-radio-button",
1758
1761
  "e-form-file-upload",
1759
1762
  "e-form-date-picker",
1760
- "e-form-time-picker"
1763
+ "e-form-time-picker",
1764
+ FORM_HIDDEN_FIELD_ELEMENT_TYPE
1761
1765
  ]);
1762
1766
  function getArgsElementType(args) {
1763
1767
  return args.model?.widgetType || args.model?.elType;
@@ -1874,13 +1878,37 @@ function handleBlockedFormField() {
1874
1878
  notify(FORM_FIELDS_OUTSIDE_ALERT);
1875
1879
  }
1876
1880
 
1877
- // src/form-structure/prevent-form-nesting-commands.ts
1881
+ // src/form-structure/hidden-field-hint.ts
1878
1882
  import { notify as notify2 } from "@elementor/editor-notifications";
1879
- import { blockCommand as blockCommand2 } from "@elementor/editor-v1-adapters";
1883
+ import { registerDataHook as registerDataHook3 } from "@elementor/editor-v1-adapters";
1880
1884
  import { __ as __2 } from "@wordpress/i18n";
1885
+ var MOVE_COMMAND = "document/elements/move";
1886
+ var notificationCounter = 0;
1887
+ function createHiddenFieldNotification() {
1888
+ return {
1889
+ type: "default",
1890
+ message: __2("Hidden field added - invisible on your form. Manage it in the Structure panel.", "elementor"),
1891
+ id: `hidden-field-added-${++notificationCounter}`
1892
+ };
1893
+ }
1894
+ function initHiddenFieldHint() {
1895
+ registerDataHook3("after", "document/elements/create", (args, _result, options) => {
1896
+ if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND)) {
1897
+ return;
1898
+ }
1899
+ if (getArgsElementType(args) === FORM_HIDDEN_FIELD_ELEMENT_TYPE) {
1900
+ notify2(createHiddenFieldNotification());
1901
+ }
1902
+ });
1903
+ }
1904
+
1905
+ // src/form-structure/prevent-form-nesting-commands.ts
1906
+ import { notify as notify3 } from "@elementor/editor-notifications";
1907
+ import { blockCommand as blockCommand2 } from "@elementor/editor-v1-adapters";
1908
+ import { __ as __3 } from "@wordpress/i18n";
1881
1909
  var FORM_NESTING_ALERT = {
1882
1910
  type: "default",
1883
- message: __2("Forms can't be nested. Create separate forms instead.", "elementor"),
1911
+ message: __3("Forms can't be nested. Create separate forms instead.", "elementor"),
1884
1912
  id: "form-nesting-blocked"
1885
1913
  };
1886
1914
  function initFormNestingPrevention() {
@@ -1936,7 +1964,7 @@ function blockFormPaste(args) {
1936
1964
  return false;
1937
1965
  }
1938
1966
  function handleBlockedFormField2() {
1939
- notify2(FORM_NESTING_ALERT);
1967
+ notify3(FORM_NESTING_ALERT);
1940
1968
  }
1941
1969
 
1942
1970
  // src/settings-transformers-registry.ts
@@ -2066,9 +2094,8 @@ var timeRangeTransformer = createTransformer((value) => {
2066
2094
  // src/transformers/shared/icon-transformer.ts
2067
2095
  import {
2068
2096
  getFontAwesome7IconName,
2069
- isDeletedCustomIconLibrary,
2070
- resolveCustomIconSvg,
2071
- resolveFontAwesome7Icon
2097
+ resolveFontAwesome7Icon,
2098
+ resolveProviderIconSvg
2072
2099
  } from "@elementor/editor-controls";
2073
2100
 
2074
2101
  // src/transformers/shared/process-svg-content.ts
@@ -2137,6 +2164,8 @@ var svgSrcTransformer = createTransformer(async (value, { signal }) => {
2137
2164
  var EMPTY_ICON_RESULT = { html: null, url: null };
2138
2165
  var ICON_SVG_SIZE = "100%";
2139
2166
  var ICON_SVG_OVERFLOW = "visible";
2167
+ var FONT_AWESOME_LIBRARY_PREFIX = "fa-";
2168
+ var EICONS_LIBRARY = "eicons";
2140
2169
  var DEFAULT_SVG_RELATIVE_PATH = "images/default-svg.svg";
2141
2170
  var iconTransformer = createTransformer(async (value, { signal }) => {
2142
2171
  const iconValue = typeof value.value === "string" ? value.value : null;
@@ -2148,14 +2177,14 @@ var iconTransformer = createTransformer(async (value, { signal }) => {
2148
2177
  if (fontAwesomeHtml) {
2149
2178
  return { html: fontAwesomeHtml, url: null };
2150
2179
  }
2151
- if (isDeletedCustomIconLibrary(library, iconValue)) {
2152
- return resolveDefaultSvg(signal);
2180
+ if (library.startsWith(FONT_AWESOME_LIBRARY_PREFIX) || EICONS_LIBRARY === library) {
2181
+ return EMPTY_ICON_RESULT;
2153
2182
  }
2154
- const customHtml = await resolveCustomSvg(library, iconValue, signal);
2155
- if (customHtml) {
2156
- return { html: customHtml, url: null };
2183
+ const providerHtml = await resolveProviderSvg(library, iconValue, signal);
2184
+ if (providerHtml) {
2185
+ return { html: providerHtml, url: null };
2157
2186
  }
2158
- return EMPTY_ICON_RESULT;
2187
+ return resolveDefaultSvg(signal);
2159
2188
  });
2160
2189
  async function resolveDefaultSvg(signal) {
2161
2190
  const assetsUrl = window.elementorCommon?.config?.urls?.assets;
@@ -2170,8 +2199,8 @@ async function resolveDefaultSvg(signal) {
2170
2199
  { key: "svg", signal }
2171
2200
  );
2172
2201
  }
2173
- async function resolveCustomSvg(library, iconValue, signal) {
2174
- const markup = await resolveCustomIconSvg(library, iconValue, signal);
2202
+ async function resolveProviderSvg(library, iconValue, signal) {
2203
+ const markup = await resolveProviderIconSvg(library, iconValue, signal);
2175
2204
  return markup ? processIconSvgContent(markup) : null;
2176
2205
  }
2177
2206
  async function resolveFontAwesomeSvg(library, iconValue, signal) {
@@ -3393,7 +3422,7 @@ import {
3393
3422
  stringPropTypeUtil as stringPropTypeUtil2
3394
3423
  } from "@elementor/editor-props";
3395
3424
  import { __privateRunCommandSync as runCommandSync, getCurrentEditMode, undoable } from "@elementor/editor-v1-adapters";
3396
- import { __ as __3 } from "@wordpress/i18n";
3425
+ import { __ as __4 } from "@wordpress/i18n";
3397
3426
 
3398
3427
  // src/legacy/replacements/base.ts
3399
3428
  var TRIGGER_TIMING = {
@@ -3815,7 +3844,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3815
3844
  {
3816
3845
  title: getElementLabel(this.id),
3817
3846
  // translators: %s is the name of the property that was edited.
3818
- subtitle: __3("%s edited", "elementor").replace(
3847
+ subtitle: __4("%s edited", "elementor").replace(
3819
3848
  "%s",
3820
3849
  this.getInlineEditablePropTypeKey() ?? "Inline editing"
3821
3850
  ),
@@ -5587,9 +5616,9 @@ import {
5587
5616
  getAnchoredDescendantId,
5588
5617
  isElementAnchored
5589
5618
  } from "@elementor/editor-elements";
5590
- import { notify as notify3 } from "@elementor/editor-notifications";
5619
+ import { notify as notify4 } from "@elementor/editor-notifications";
5591
5620
  import { blockCommand as blockCommand3 } from "@elementor/editor-v1-adapters";
5592
- import { __ as __4 } from "@wordpress/i18n";
5621
+ import { __ as __5 } from "@wordpress/i18n";
5593
5622
  function initLinkInLinkPrevention() {
5594
5623
  blockCommand3({
5595
5624
  command: "document/elements/paste",
@@ -5623,7 +5652,7 @@ function blockLinkInLinkPaste(args) {
5623
5652
  const sourceElements = data.clipboard.elements;
5624
5653
  const notification = {
5625
5654
  type: "default",
5626
- message: __4(
5655
+ message: __5(
5627
5656
  "To paste a link to this element, first remove the link from it's parent container.",
5628
5657
  "elementor"
5629
5658
  ),
@@ -5632,7 +5661,7 @@ function blockLinkInLinkPaste(args) {
5632
5661
  };
5633
5662
  const blocked = shouldBlock(sourceElements, targetElements);
5634
5663
  if (blocked) {
5635
- notify3(notification);
5664
+ notify4(notification);
5636
5665
  }
5637
5666
  return blocked;
5638
5667
  }
@@ -5642,13 +5671,13 @@ function blockLinkInLinkMove(args) {
5642
5671
  const targetElement = target;
5643
5672
  const notification = {
5644
5673
  type: "default",
5645
- message: __4("To drag a link to this element, first remove the link from it's parent container.", "elementor"),
5674
+ message: __5("To drag a link to this element, first remove the link from it's parent container.", "elementor"),
5646
5675
  id: "move-in-link-blocked",
5647
5676
  additionalActionProps: [learnMoreActionProps]
5648
5677
  };
5649
5678
  const isBlocked = shouldBlock(sourceElements, [targetElement]);
5650
5679
  if (isBlocked) {
5651
- notify3(notification);
5680
+ notify4(notification);
5652
5681
  }
5653
5682
  return isBlocked;
5654
5683
  }
@@ -5680,7 +5709,7 @@ import {
5680
5709
  // src/utils/command-utils.ts
5681
5710
  import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
5682
5711
  import { CLASSES_PROP_KEY } from "@elementor/editor-props";
5683
- import { __ as __5 } from "@wordpress/i18n";
5712
+ import { __ as __6 } from "@wordpress/i18n";
5684
5713
  function hasAtomicWidgets(args) {
5685
5714
  const { containers = [args.container] } = args;
5686
5715
  return containers.some(isAtomicWidget);
@@ -5716,7 +5745,7 @@ function getClipboardElements(storageKey = "clipboard") {
5716
5745
  }
5717
5746
  }
5718
5747
  function getTitleForContainers(containers) {
5719
- return containers.length > 1 ? __5("Elements", "elementor") : getElementLabel2(containers[0].id);
5748
+ return containers.length > 1 ? __6("Elements", "elementor") : getElementLabel2(containers[0].id);
5720
5749
  }
5721
5750
 
5722
5751
  // src/style-commands/undoable-actions/paste-element-style.ts
@@ -5728,7 +5757,7 @@ import {
5728
5757
  } from "@elementor/editor-elements";
5729
5758
  import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
5730
5759
  import { undoable as undoable2 } from "@elementor/editor-v1-adapters";
5731
- import { __ as __6 } from "@wordpress/i18n";
5760
+ import { __ as __7 } from "@wordpress/i18n";
5732
5761
  var undoablePasteElementStyle = () => undoable2(
5733
5762
  {
5734
5763
  do: ({ containers, newStyle }) => {
@@ -5798,7 +5827,7 @@ var undoablePasteElementStyle = () => undoable2(
5798
5827
  },
5799
5828
  {
5800
5829
  title: ({ containers }) => getTitleForContainers(containers),
5801
- subtitle: __6("Style Pasted", "elementor")
5830
+ subtitle: __7("Style Pasted", "elementor")
5802
5831
  }
5803
5832
  );
5804
5833
 
@@ -5871,7 +5900,7 @@ import {
5871
5900
  import { createElementStyle as createElementStyle3, deleteElementStyle as deleteElementStyle2, getElementStyles as getElementStyles3 } from "@elementor/editor-elements";
5872
5901
  import { ELEMENTS_STYLES_RESERVED_LABEL as ELEMENTS_STYLES_RESERVED_LABEL2 } from "@elementor/editor-styles-repository";
5873
5902
  import { undoable as undoable3 } from "@elementor/editor-v1-adapters";
5874
- import { __ as __7 } from "@wordpress/i18n";
5903
+ import { __ as __8 } from "@wordpress/i18n";
5875
5904
  var undoableResetElementStyle = () => undoable3(
5876
5905
  {
5877
5906
  do: ({ containers }) => {
@@ -5909,7 +5938,7 @@ var undoableResetElementStyle = () => undoable3(
5909
5938
  },
5910
5939
  {
5911
5940
  title: ({ containers }) => getTitleForContainers(containers),
5912
- subtitle: __7("Style Reset", "elementor")
5941
+ subtitle: __8("Style Reset", "elementor")
5913
5942
  }
5914
5943
  );
5915
5944
 
@@ -5947,6 +5976,7 @@ function init() {
5947
5976
  initLinkInLinkPrevention();
5948
5977
  initFormNestingPrevention();
5949
5978
  initFormAncestorEnforcement();
5979
+ initHiddenFieldHint();
5950
5980
  initViewReplacements();
5951
5981
  initLegacyViews();
5952
5982
  initSettingsTransformers();
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.4.0-1135",
4
+ "version": "4.4.0-1137",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,26 +37,26 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "4.4.0-1135",
41
- "@elementor/editor-controls": "4.4.0-1135",
42
- "@elementor/editor-documents": "4.4.0-1135",
43
- "@elementor/editor-elements": "4.4.0-1135",
44
- "@elementor/editor-interactions": "4.4.0-1135",
45
- "@elementor/editor-mcp": "4.4.0-1135",
46
- "@elementor/editor-notifications": "4.4.0-1135",
47
- "@elementor/editor-props": "4.4.0-1135",
48
- "@elementor/editor-responsive": "4.4.0-1135",
49
- "@elementor/editor-styles": "4.4.0-1135",
50
- "@elementor/editor-styles-repository": "4.4.0-1135",
51
- "@elementor/editor-ui": "4.4.0-1135",
52
- "@elementor/editor-v1-adapters": "4.4.0-1135",
53
- "@elementor/events": "4.4.0-1135",
54
- "@elementor/http-client": "4.4.0-1135",
55
- "@elementor/schema": "4.4.0-1135",
56
- "@elementor/twing": "4.4.0-1135",
40
+ "@elementor/editor": "4.4.0-1137",
41
+ "@elementor/editor-controls": "4.4.0-1137",
42
+ "@elementor/editor-documents": "4.4.0-1137",
43
+ "@elementor/editor-elements": "4.4.0-1137",
44
+ "@elementor/editor-interactions": "4.4.0-1137",
45
+ "@elementor/editor-mcp": "4.4.0-1137",
46
+ "@elementor/editor-notifications": "4.4.0-1137",
47
+ "@elementor/editor-props": "4.4.0-1137",
48
+ "@elementor/editor-responsive": "4.4.0-1137",
49
+ "@elementor/editor-styles": "4.4.0-1137",
50
+ "@elementor/editor-styles-repository": "4.4.0-1137",
51
+ "@elementor/editor-ui": "4.4.0-1137",
52
+ "@elementor/editor-v1-adapters": "4.4.0-1137",
53
+ "@elementor/events": "4.4.0-1137",
54
+ "@elementor/http-client": "4.4.0-1137",
55
+ "@elementor/schema": "4.4.0-1137",
56
+ "@elementor/twing": "4.4.0-1137",
57
57
  "@elementor/ui": "1.41.3",
58
- "@elementor/utils": "4.4.0-1135",
59
- "@elementor/wp-media": "4.4.0-1135",
58
+ "@elementor/utils": "4.4.0-1137",
59
+ "@elementor/wp-media": "4.4.0-1137",
60
60
  "@floating-ui/react": "^0.27.5",
61
61
  "@wordpress/i18n": "^5.13.0",
62
62
  "dompurify": "^3.2.6"
@@ -114,6 +114,16 @@ describe( 'form-structure utils', () => {
114
114
  expect( errors[ 0 ] ).toContain( 'e-form-input' );
115
115
  expect( errors[ 0 ] ).toContain( 'e-form' );
116
116
  } );
117
+ it( 'returns an error when a hidden field has no e-form ancestor (invalid)', () => {
118
+ const xml = parseXml( `
119
+ <e-flexbox>
120
+ <e-form-hidden />
121
+ </e-flexbox>
122
+ ` );
123
+ const errors = collectFormAncestorErrors( xml );
124
+ expect( errors ).toHaveLength( 1 );
125
+ expect( errors[ 0 ] ).toContain( 'e-form-hidden' );
126
+ } );
117
127
  it( 'returns an error for each orphaned form field', () => {
118
128
  const xml = parseXml( `
119
129
  <e-flexbox>
@@ -0,0 +1,137 @@
1
+ import { createHooksRegistry, setupHooksRegistry } from 'test-utils';
2
+ import { notify } from '@elementor/editor-notifications';
3
+
4
+ import { initHiddenFieldHint } from '../hidden-field-hint';
5
+
6
+ jest.mock( '@elementor/editor-notifications', () => ( {
7
+ notify: jest.fn(),
8
+ } ) );
9
+
10
+ const CREATE_COMMAND = 'document/elements/create';
11
+ const MOVE_COMMAND = 'document/elements/move';
12
+ const HIDDEN_FIELD_TYPE = 'e-form-hidden';
13
+
14
+ describe( 'hidden field hint', () => {
15
+ const hooksRegistry = createHooksRegistry();
16
+
17
+ beforeEach( () => {
18
+ jest.mocked( notify ).mockClear();
19
+ setupHooksRegistry( hooksRegistry );
20
+ initHiddenFieldHint();
21
+ } );
22
+
23
+ it( 'should notify when a hidden field is added', () => {
24
+ // Arrange
25
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
26
+
27
+ // Act
28
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
29
+
30
+ // Assert
31
+ expect( notify ).toHaveBeenCalledWith(
32
+ expect.objectContaining( {
33
+ message: 'Hidden field added - invisible on your form. Manage it in the Structure panel.',
34
+ } )
35
+ );
36
+ } );
37
+
38
+ it( 'should use unique notification ids for back-to-back adds (not collapsed by the notification system)', () => {
39
+ // Arrange
40
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
41
+
42
+ // Act
43
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
44
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
45
+
46
+ // Assert
47
+ expect( notify ).toHaveBeenCalledTimes( 2 );
48
+ const firstCallId = jest.mocked( notify ).mock.calls[ 0 ][ 0 ].id;
49
+ const secondCallId = jest.mocked( notify ).mock.calls[ 1 ][ 0 ].id;
50
+ expect( firstCallId ).not.toBe( secondCallId );
51
+ } );
52
+
53
+ it( 'should notify again after remove and re-add (simulated by separate create calls)', () => {
54
+ // Arrange
55
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
56
+
57
+ // Act - first add
58
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
59
+
60
+ // simulate removal (no hook for delete affects this)
61
+
62
+ // Act - re-add
63
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
64
+
65
+ // Assert
66
+ expect( notify ).toHaveBeenCalledTimes( 2 );
67
+ } );
68
+
69
+ it( 'should not notify when another element type is created', () => {
70
+ // Arrange
71
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
72
+
73
+ // Act
74
+ hook?.apply( { model: { widgetType: 'e-form-input' } } );
75
+
76
+ // Assert
77
+ expect( notify ).not.toHaveBeenCalled();
78
+ } );
79
+
80
+ it( 'should not notify when the hidden field is created as part of a move', () => {
81
+ // Arrange
82
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
83
+ const eWindow = window as unknown as { $e: { commands?: { currentTrace?: string[] } } };
84
+ eWindow.$e.commands = { currentTrace: [ MOVE_COMMAND, CREATE_COMMAND ] };
85
+
86
+ // Act
87
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
88
+
89
+ // Assert
90
+ expect( notify ).not.toHaveBeenCalled();
91
+ } );
92
+
93
+ it( 'should not notify on selection (selection does not trigger create command)', () => {
94
+ // Arrange - selection uses a different command path, not document/elements/create
95
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
96
+
97
+ // Act - no create call is made for selection
98
+ // Selection triggers commands like document/elements/toggle-selection, not create
99
+
100
+ // Assert
101
+ expect( hook ).toBeDefined();
102
+ expect( notify ).not.toHaveBeenCalled();
103
+ } );
104
+
105
+ it( 'should notify on paste when it triggers create (current behavior: paste triggers toast)', () => {
106
+ // Arrange
107
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
108
+
109
+ // Act - paste internally calls create, so it will trigger the notification
110
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
111
+
112
+ // Assert
113
+ expect( notify ).toHaveBeenCalledTimes( 1 );
114
+ } );
115
+
116
+ it( 'should notify on duplicate when it triggers create (current behavior: duplicate triggers toast)', () => {
117
+ // Arrange
118
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
119
+
120
+ // Act - duplicate internally calls create, so it will trigger the notification
121
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
122
+
123
+ // Assert
124
+ expect( notify ).toHaveBeenCalledTimes( 1 );
125
+ } );
126
+
127
+ it( 'should notify on undo of delete (undo-delete triggers create, so toast shows)', () => {
128
+ // Arrange
129
+ const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
130
+
131
+ // Act - undo of a delete restores via create command
132
+ hook?.apply( { model: { widgetType: HIDDEN_FIELD_TYPE } } );
133
+
134
+ // Assert
135
+ expect( notify ).toHaveBeenCalledTimes( 1 );
136
+ } );
137
+ } );
@@ -0,0 +1,29 @@
1
+ import { type NotificationData, notify } from '@elementor/editor-notifications';
2
+ import { type HookOptions, registerDataHook } from '@elementor/editor-v1-adapters';
3
+ import { __ } from '@wordpress/i18n';
4
+
5
+ import { type CreateArgs, FORM_HIDDEN_FIELD_ELEMENT_TYPE, getArgsElementType } from './utils';
6
+
7
+ const MOVE_COMMAND = 'document/elements/move';
8
+
9
+ let notificationCounter = 0;
10
+
11
+ function createHiddenFieldNotification(): NotificationData {
12
+ return {
13
+ type: 'default',
14
+ message: __( 'Hidden field added - invisible on your form. Manage it in the Structure panel.', 'elementor' ),
15
+ id: `hidden-field-added-${ ++notificationCounter }`,
16
+ };
17
+ }
18
+
19
+ export function initHiddenFieldHint() {
20
+ registerDataHook( 'after', 'document/elements/create', ( args: CreateArgs, _result, options?: HookOptions ) => {
21
+ if ( options?.commandsCurrentTrace?.includes( MOVE_COMMAND ) ) {
22
+ return;
23
+ }
24
+
25
+ if ( getArgsElementType( args ) === FORM_HIDDEN_FIELD_ELEMENT_TYPE ) {
26
+ notify( createHiddenFieldNotification() );
27
+ }
28
+ } );
29
+ }
@@ -35,6 +35,7 @@ export type StorageContent = {
35
35
  };
36
36
 
37
37
  export const FORM_ELEMENT_TYPE = 'e-form';
38
+ export const FORM_HIDDEN_FIELD_ELEMENT_TYPE = 'e-form-hidden';
38
39
  export const FORM_FIELD_ELEMENT_TYPES = new Set( [
39
40
  'e-form-input',
40
41
  'e-form-textarea',
@@ -46,6 +47,7 @@ export const FORM_FIELD_ELEMENT_TYPES = new Set( [
46
47
  'e-form-file-upload',
47
48
  'e-form-date-picker',
48
49
  'e-form-time-picker',
50
+ FORM_HIDDEN_FIELD_ELEMENT_TYPE,
49
51
  ] );
50
52
 
51
53
  export function getArgsElementType( args: CreateArgs ): string | undefined {
package/src/init.tsx CHANGED
@@ -6,6 +6,7 @@ import { ElementsOverlays } from './components/elements-overlays';
6
6
  import { InteractionsRenderer } from './components/interactions-renderer';
7
7
  import { StyleRenderer } from './components/style-renderer';
8
8
  import { initFormAncestorEnforcement } from './form-structure/enforce-form-ancestor-commands';
9
+ import { initHiddenFieldHint } from './form-structure/hidden-field-hint';
9
10
  import { initFormNestingPrevention } from './form-structure/prevent-form-nesting-commands';
10
11
  import { initSettingsTransformers } from './init-settings-transformers';
11
12
  import { initStyleTransformers } from './init-style-transformers';
@@ -25,6 +26,7 @@ export function init() {
25
26
  initLinkInLinkPrevention();
26
27
  initFormNestingPrevention();
27
28
  initFormAncestorEnforcement();
29
+ initHiddenFieldHint();
28
30
 
29
31
  initViewReplacements();
30
32
 
@@ -22,7 +22,9 @@ const convertBlocks = async (
22
22
  ): Promise< Record< string, CssConversionResult > > => {
23
23
  const { data } = await httpService().post< HttpResponse< Record< string, CssConversionResult > > >(
24
24
  CSS_TO_ATOMIC_URL,
25
- { blocks }
25
+ {
26
+ blocks,
27
+ }
26
28
  );
27
29
 
28
30
  return data.data;
@@ -1,10 +1,8 @@
1
- jest.mock( '@elementor/http-client', () => ( {
2
- httpService: () => ( {
3
- get: jest.fn().mockRejectedValue( new Error( 'no custom svg' ) ),
4
- } ),
5
- } ) );
6
-
7
- import { resetFontAwesome7IconsCache } from '@elementor/editor-controls';
1
+ import {
2
+ registerIconLibraryProvider,
3
+ resetFontAwesome7IconsCache,
4
+ unregisterIconLibraryProvider,
5
+ } from '@elementor/editor-controls';
8
6
  import { iconPropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
9
7
 
10
8
  import { iconPropType } from '../../../__tests__/prop-types';
@@ -186,15 +184,12 @@ describe( 'iconTransformer', () => {
186
184
  expect( result ).toEqual( { html: null, url: null } );
187
185
  } );
188
186
 
189
- it( 'falls back to the default svg when a custom library has been deleted', async () => {
187
+ it( 'falls back to the default svg when no provider resolves a non-native library', async () => {
190
188
  // Arrange.
191
- window.elementor = {
192
- config: {
193
- icons: {
194
- libraries: [ { name: 'fa-solid', prefix: 'fa-', native: true } ],
195
- },
196
- },
197
- } as typeof window.elementor;
189
+ registerIconLibraryProvider( {
190
+ id: 'test-provider',
191
+ useIcons: () => ( { data: [], isLoading: false } ),
192
+ } );
198
193
  const defaultSvg =
199
194
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path d="M24.9999 4.31543"></path></svg>';
200
195
  global.fetch = jest.fn().mockResolvedValue( {
@@ -206,6 +201,8 @@ describe( 'iconTransformer', () => {
206
201
  // Act.
207
202
  const result = await resolveSavedIcon( 'missing-set missing-set-ghost', 'missing-set' );
208
203
 
204
+ unregisterIconLibraryProvider( 'test-provider' );
205
+
209
206
  // Assert.
210
207
  expect( global.fetch ).toHaveBeenCalledWith(
211
208
  'https://example.com/assets/images/default-svg.svg',
@@ -217,6 +214,59 @@ describe( 'iconTransformer', () => {
217
214
  } );
218
215
  } );
219
216
 
217
+ it( 'falls back to the default svg for a non-native library when no provider is registered', async () => {
218
+ // Arrange.
219
+ const defaultSvg =
220
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path d="M24.9999 4.31543"></path></svg>';
221
+ global.fetch = jest.fn().mockResolvedValue( {
222
+ ok: true,
223
+ headers: new Headers( { 'content-type': 'image/svg+xml' } ),
224
+ text: () => Promise.resolve( defaultSvg ),
225
+ } );
226
+
227
+ // Act.
228
+ const result = await resolveSavedIcon( 'my-set my-set-star', 'my-set' );
229
+
230
+ // Assert.
231
+ expect( result ).toEqual( {
232
+ html: expect.stringContaining( 'M24.9999 4.31543' ),
233
+ url: 'https://example.com/assets/images/default-svg.svg',
234
+ } );
235
+ } );
236
+
237
+ it( 'does not replace PHP-managed eicons with the default svg', async () => {
238
+ // Arrange.
239
+ global.fetch = jest.fn();
240
+
241
+ // Act.
242
+ const result = await resolveSavedIcon( 'eicon-edit', 'eicons' );
243
+
244
+ // Assert.
245
+ expect( result ).toEqual( { html: null, url: null } );
246
+ expect( global.fetch ).not.toHaveBeenCalled();
247
+ } );
248
+
249
+ it( 'renders and sanitizes svg returned by a provider', async () => {
250
+ // Arrange.
251
+ registerIconLibraryProvider( {
252
+ id: 'test-provider',
253
+ useIcons: () => ( { data: [], isLoading: false } ),
254
+ resolveSvg: () =>
255
+ Promise.resolve(
256
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><script>alert(1)</script><path d="M1 1h8v8H1z"></path></svg>'
257
+ ),
258
+ } );
259
+
260
+ // Act.
261
+ const result = ( await resolveSavedIcon( 'my-set my-set-star', 'my-set' ) ) as { html: string };
262
+
263
+ unregisterIconLibraryProvider( 'test-provider' );
264
+
265
+ // Assert.
266
+ expect( result.html ).toContain( 'M1 1h8v8H1z' );
267
+ expect( result.html ).not.toContain( 'script' );
268
+ } );
269
+
220
270
  it( 'returns null html when value or library is missing', async () => {
221
271
  // Act.
222
272
  const resolve = createPropsResolver( {
@@ -47,7 +47,10 @@ describe( 'svgSrcTransformer', () => {
47
47
  const result = ( await svgSrcTransformer(
48
48
  { id: null, url: 'https://example.com/icon.svg' },
49
49
  { key: 'svg' }
50
- ) ) as { html: string; url: string };
50
+ ) ) as {
51
+ html: string;
52
+ url: string;
53
+ };
51
54
 
52
55
  // Assert.
53
56
  expect( result.html ).toContain( 'width: 100%' );
@@ -91,7 +94,10 @@ describe( 'svgSrcTransformer', () => {
91
94
  const result = ( await svgSrcTransformer(
92
95
  { id: 99, url: 'https://example.com/fallback.svg' },
93
96
  { key: 'svg' }
94
- ) ) as { html: string; url: string };
97
+ ) ) as {
98
+ html: string;
99
+ url: string;
100
+ };
95
101
 
96
102
  // Assert.
97
103
  expect( result.url ).toBe( 'https://example.com/fallback.svg' );
@@ -146,7 +152,10 @@ describe( 'svgSrcTransformer', () => {
146
152
  const result = ( await svgSrcTransformer(
147
153
  { id: null, url: 'https://example.com/styled.svg' },
148
154
  { key: 'svg' }
149
- ) ) as { html: string; url: string };
155
+ ) ) as {
156
+ html: string;
157
+ url: string;
158
+ };
150
159
 
151
160
  // Assert.
152
161
  expect( result.html ).toContain( 'display: block' );
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  type FontAwesome7IconDefinition,
3
3
  getFontAwesome7IconName,
4
- isDeletedCustomIconLibrary,
5
- resolveCustomIconSvg,
6
4
  resolveFontAwesome7Icon,
5
+ resolveProviderIconSvg,
7
6
  } from '@elementor/editor-controls';
8
7
 
9
8
  import { createTransformer } from '../create-transformer';
@@ -14,6 +13,8 @@ import { svgSrcTransformer } from './svg-src-transformer';
14
13
  const EMPTY_ICON_RESULT = { html: null, url: null };
15
14
  const ICON_SVG_SIZE = '100%';
16
15
  const ICON_SVG_OVERFLOW = 'visible';
16
+ const FONT_AWESOME_LIBRARY_PREFIX = 'fa-';
17
+ const EICONS_LIBRARY = 'eicons';
17
18
  const DEFAULT_SVG_RELATIVE_PATH = 'images/default-svg.svg';
18
19
 
19
20
  type IconValue = {
@@ -35,17 +36,17 @@ export const iconTransformer = createTransformer( async ( value: IconValue, { si
35
36
  return { html: fontAwesomeHtml, url: null };
36
37
  }
37
38
 
38
- if ( isDeletedCustomIconLibrary( library, iconValue ) ) {
39
- return resolveDefaultSvg( signal );
39
+ if ( library.startsWith( FONT_AWESOME_LIBRARY_PREFIX ) || EICONS_LIBRARY === library ) {
40
+ return EMPTY_ICON_RESULT;
40
41
  }
41
42
 
42
- const customHtml = await resolveCustomSvg( library, iconValue, signal );
43
+ const providerHtml = await resolveProviderSvg( library, iconValue, signal );
43
44
 
44
- if ( customHtml ) {
45
- return { html: customHtml, url: null };
45
+ if ( providerHtml ) {
46
+ return { html: providerHtml, url: null };
46
47
  }
47
48
 
48
- return EMPTY_ICON_RESULT;
49
+ return resolveDefaultSvg( signal );
49
50
  } );
50
51
 
51
52
  async function resolveDefaultSvg( signal?: AbortSignal ) {
@@ -64,8 +65,12 @@ async function resolveDefaultSvg( signal?: AbortSignal ) {
64
65
  );
65
66
  }
66
67
 
67
- async function resolveCustomSvg( library: string, iconValue: string, signal?: AbortSignal ): Promise< string | null > {
68
- const markup = await resolveCustomIconSvg( library, iconValue, signal );
68
+ async function resolveProviderSvg(
69
+ library: string,
70
+ iconValue: string,
71
+ signal?: AbortSignal
72
+ ): Promise< string | null > {
73
+ const markup = await resolveProviderIconSvg( library, iconValue, signal );
69
74
 
70
75
  return markup ? processIconSvgContent( markup ) : null;
71
76
  }