@elementor/editor-editing-panel 0.3.0 → 0.4.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [0.4.0](https://github.com/elementor/elementor-packages/compare/@elementor/editor-editing-panel@0.3.0...@elementor/editor-editing-panel@0.4.0) (2024-07-16)
7
+
8
+
9
+ ### Features
10
+
11
+ * **editor-editing-panel:** settings control infra for atomic controls [EDS-236] ([#201](https://github.com/elementor/elementor-packages/issues/201)) ([25214f3](https://github.com/elementor/elementor-packages/commit/25214f35970572be059fa3ea98c0905f4535f9f9))
12
+
13
+
14
+
15
+
16
+
6
17
  # [0.3.0](https://github.com/elementor/elementor-packages/compare/@elementor/editor-editing-panel@0.2.0...@elementor/editor-editing-panel@0.3.0) (2024-07-14)
7
18
 
8
19
 
package/dist/index.js CHANGED
@@ -26,8 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  var import_editor_panels2 = require("@elementor/editor-panels");
27
27
 
28
28
  // src/components/editing-panel.tsx
29
- var React2 = __toESM(require("react"));
30
- var import_editor_panels = require("@elementor/editor-panels");
29
+ var React5 = __toESM(require("react"));
31
30
  var import_i18n = require("@wordpress/i18n");
32
31
 
33
32
  // src/hooks/use-selected-elements.ts
@@ -92,16 +91,130 @@ function useElementType(type) {
92
91
  );
93
92
  }
94
93
 
95
- // src/contexts/settings-controls.tsx
94
+ // src/components/editing-panel.tsx
95
+ var import_editor_panels = require("@elementor/editor-panels");
96
+
97
+ // src/components/controls/control-types/select-control.tsx
96
98
  var React = __toESM(require("react"));
99
+ var import_ui = require("@elementor/ui");
100
+
101
+ // src/components/controls/control-context.ts
97
102
  var import_react = require("react");
98
- var Context = (0, import_react.createContext)(null);
103
+ var ControlContext = (0, import_react.createContext)(null);
104
+ function useControl(defaultValue) {
105
+ const controlContext = (0, import_react.useContext)(ControlContext);
106
+ if (!controlContext) {
107
+ throw new Error("useControl must be used within a ControlContext");
108
+ }
109
+ return { ...controlContext, value: controlContext.value ?? defaultValue };
110
+ }
111
+
112
+ // src/components/controls/control-types/select-control.tsx
113
+ var SelectControl = ({ options }) => {
114
+ const { value, setValue } = useControl();
115
+ const handleChange = (event) => {
116
+ setValue(event.target.value);
117
+ };
118
+ return /* @__PURE__ */ React.createElement(import_ui.Select, { size: "tiny", value: value ?? "", onChange: handleChange }, options.map((option) => /* @__PURE__ */ React.createElement(import_ui.MenuItem, { key: option.value, value: option.value, disabled: option.disabled }, option.label)));
119
+ };
120
+
121
+ // src/components/controls/control-types/text-control.tsx
122
+ var React2 = __toESM(require("react"));
123
+ var import_ui2 = require("@elementor/ui");
124
+ var TextControl = ({ placeholder }) => {
125
+ const { value, setValue } = useControl("");
126
+ const handleChange = (event) => {
127
+ setValue(event.target.value);
128
+ };
129
+ return /* @__PURE__ */ React2.createElement(import_ui2.TextareaAutosize, { size: "tiny", minRows: 3, value, onChange: handleChange, placeholder });
130
+ };
131
+
132
+ // src/components/controls/settings-control.tsx
133
+ var React3 = __toESM(require("react"));
134
+ var import_ui3 = require("@elementor/ui");
135
+
136
+ // src/sync/update-settings.ts
137
+ var import_editor_v1_adapters3 = require("@elementor/editor-v1-adapters");
138
+
139
+ // src/sync/get-container.ts
140
+ function getContainer(id) {
141
+ const extendedWindow = window;
142
+ const container = extendedWindow.elementor?.getContainer?.(id);
143
+ return container ?? null;
144
+ }
145
+
146
+ // src/sync/update-settings.ts
147
+ var updateSettings = ({ id, props }) => {
148
+ const container = getContainer(id);
149
+ (0, import_editor_v1_adapters3.__privateRunCommand)("document/elements/settings", {
150
+ container,
151
+ settings: {
152
+ ...props
153
+ }
154
+ });
155
+ };
156
+
157
+ // src/hooks/use-widget-settings.ts
158
+ var import_editor_v1_adapters4 = require("@elementor/editor-v1-adapters");
159
+ var useWidgetSettings = ({ id, bind }) => {
160
+ return (0, import_editor_v1_adapters4.__privateUseListenTo)(
161
+ (0, import_editor_v1_adapters4.commandEndEvent)("document/elements/settings"),
162
+ () => {
163
+ const container = getContainer(id);
164
+ const value = container?.settings?.get(bind) ?? null;
165
+ return value;
166
+ },
167
+ []
168
+ );
169
+ };
170
+
171
+ // src/components/controls/settings-control.tsx
172
+ var SettingsControl = ({ bind, children, elementID }) => {
173
+ const value = useWidgetSettings({ id: elementID, bind });
174
+ const setValue = (newValue) => {
175
+ updateSettings({
176
+ id: elementID,
177
+ props: {
178
+ [bind]: newValue
179
+ }
180
+ });
181
+ };
182
+ return /* @__PURE__ */ React3.createElement(ControlContext.Provider, { value: { setValue, value, bind } }, children);
183
+ };
184
+ var Label = ({ children }) => {
185
+ return /* @__PURE__ */ React3.createElement(import_ui3.Typography, { component: "label", variant: "caption" }, children);
186
+ };
187
+ var Container = ({ children }) => /* @__PURE__ */ React3.createElement(
188
+ import_ui3.Stack,
189
+ {
190
+ spacing: 1,
191
+ flexDirection: "row",
192
+ alignItems: "center",
193
+ justifyContent: "space-between",
194
+ flexWrap: "wrap",
195
+ sx: { px: 2 }
196
+ },
197
+ children
198
+ );
199
+ SettingsControl.Label = Label;
200
+ SettingsControl.Container = Container;
201
+
202
+ // src/components/editing-panel.tsx
203
+ var import_ui4 = require("@elementor/ui");
204
+
205
+ // src/contexts/settings-controls.tsx
206
+ var React4 = __toESM(require("react"));
207
+ var import_react2 = require("react");
208
+ var Context = (0, import_react2.createContext)(null);
99
209
  function ElementContext({ children, element }) {
100
- return /* @__PURE__ */ React.createElement(Context.Provider, { value: { element } }, children);
210
+ return /* @__PURE__ */ React4.createElement(Context.Provider, { value: { element } }, children);
101
211
  }
102
212
 
103
213
  // src/components/editing-panel.tsx
104
- var import_ui = require("@elementor/ui");
214
+ var controlTypes = {
215
+ select: SelectControl,
216
+ text: TextControl
217
+ };
105
218
  var EditingPanel = () => {
106
219
  const elements = useSelectedElements();
107
220
  const selectedElement = elements[0];
@@ -110,7 +223,16 @@ var EditingPanel = () => {
110
223
  return null;
111
224
  }
112
225
  const panelTitle = (0, import_i18n.__)("Edit %s", "elementor").replace("%s", elementType.title);
113
- return /* @__PURE__ */ React2.createElement(import_editor_panels.Panel, null, /* @__PURE__ */ React2.createElement(import_editor_panels.PanelHeader, null, /* @__PURE__ */ React2.createElement(import_editor_panels.PanelHeaderTitle, null, panelTitle)), /* @__PURE__ */ React2.createElement(import_editor_panels.PanelBody, null, /* @__PURE__ */ React2.createElement(ElementContext, { element: selectedElement }, /* @__PURE__ */ React2.createElement(import_ui.Box, { padding: 2 }, /* @__PURE__ */ React2.createElement(import_ui.Typography, null, "Here should be the controls.")))));
226
+ return /* @__PURE__ */ React5.createElement(import_editor_panels.Panel, null, /* @__PURE__ */ React5.createElement(import_editor_panels.PanelHeader, null, /* @__PURE__ */ React5.createElement(import_editor_panels.PanelHeaderTitle, null, panelTitle)), /* @__PURE__ */ React5.createElement(import_editor_panels.PanelBody, null, /* @__PURE__ */ React5.createElement(ElementContext, { element: selectedElement }, /* @__PURE__ */ React5.createElement(import_ui4.Stack, { spacing: 2 }, elementType.controls.map((control) => {
227
+ if (control.type === "control") {
228
+ const ControlComponent = controlTypes[control.value.type];
229
+ if (!ControlComponent) {
230
+ return null;
231
+ }
232
+ return /* @__PURE__ */ React5.createElement(SettingsControl, { key: control.value.bind, bind: control.value.bind, elementID: elements[0].id }, /* @__PURE__ */ React5.createElement(SettingsControl.Container, null, /* @__PURE__ */ React5.createElement(SettingsControl.Label, null, control.value.label), /* @__PURE__ */ React5.createElement(ControlComponent, { ...control.value.props })));
233
+ }
234
+ return null;
235
+ })))));
114
236
  };
115
237
 
116
238
  // src/panel.ts
@@ -137,13 +259,13 @@ var shouldUseV2Panel = () => {
137
259
  };
138
260
 
139
261
  // src/hooks/use-open-editor-panel.ts
140
- var import_react2 = require("react");
141
- var import_editor_v1_adapters3 = require("@elementor/editor-v1-adapters");
262
+ var import_react3 = require("react");
263
+ var import_editor_v1_adapters5 = require("@elementor/editor-v1-adapters");
142
264
  var useOpenEditorPanel = () => {
143
265
  const { open } = usePanelActions();
144
- (0, import_react2.useEffect)(() => {
145
- return (0, import_editor_v1_adapters3.__privateListenTo)(
146
- (0, import_editor_v1_adapters3.commandStartEvent)("panel/editor/open"),
266
+ (0, import_react3.useEffect)(() => {
267
+ return (0, import_editor_v1_adapters5.__privateListenTo)(
268
+ (0, import_editor_v1_adapters5.commandStartEvent)("panel/editor/open"),
147
269
  () => {
148
270
  if (shouldUseV2Panel()) {
149
271
  open();
@@ -161,7 +283,7 @@ var EditingPanelHooks = () => {
161
283
 
162
284
  // src/init.ts
163
285
  var import_editor_panels3 = require("@elementor/editor-panels");
164
- var import_editor_v1_adapters4 = require("@elementor/editor-v1-adapters");
286
+ var import_editor_v1_adapters6 = require("@elementor/editor-v1-adapters");
165
287
  function init() {
166
288
  (0, import_editor_panels3.__registerPanel)(panel);
167
289
  blockV1Panel();
@@ -171,7 +293,7 @@ function init() {
171
293
  });
172
294
  }
173
295
  var blockV1Panel = () => {
174
- (0, import_editor_v1_adapters4.__privateBlockDataCommand)({
296
+ (0, import_editor_v1_adapters6.__privateBlockDataCommand)({
175
297
  command: "panel/editor/open",
176
298
  condition: shouldUseV2Panel
177
299
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/panel.ts","../src/components/editing-panel.tsx","../src/hooks/use-selected-elements.ts","../src/sync/get-selected-elements.ts","../src/hooks/use-element-type.ts","../src/sync/get-widgets-cache.ts","../src/contexts/settings-controls.tsx","../src/init.ts","../src/sync/should-use-v2-panel.ts","../src/hooks/use-open-editor-panel.ts","../src/components/editing-panel-hooks.tsx","../src/index.ts"],"sourcesContent":["import { __createPanel as createPanel } from '@elementor/editor-panels';\nimport { EditingPanel } from './components/editing-panel';\n\nexport const {\n\tpanel,\n\tusePanelActions,\n\tusePanelStatus,\n} = createPanel( {\n\tid: 'editing-panel',\n\tcomponent: EditingPanel,\n} );\n","import * as React from 'react';\nimport {\n\tPanel,\n\tPanelBody,\n\tPanelHeader,\n\tPanelHeaderTitle,\n} from '@elementor/editor-panels';\nimport { __ } from '@wordpress/i18n';\nimport useSelectedElements from '../hooks/use-selected-elements';\nimport useElementType from '../hooks/use-element-type';\nimport { ElementContext } from '../contexts/settings-controls';\nimport { Box, Typography } from '@elementor/ui';\n\nexport const EditingPanel = () => {\n\tconst elements = useSelectedElements();\n\n\tconst selectedElement = elements[ 0 ];\n\n\tconst elementType = useElementType( selectedElement?.type );\n\n\tif ( elements.length !== 1 || ! elementType ) {\n\t\treturn null;\n\t}\n\n\t/* translators: %s: Element type title. */\n\tconst panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', elementType.title );\n\n\treturn (\n\t\t<Panel>\n\t\t\t<PanelHeader>\n\t\t\t\t<PanelHeaderTitle>{ panelTitle }</PanelHeaderTitle>\n\t\t\t</PanelHeader>\n\t\t\t<PanelBody>\n\t\t\t\t<ElementContext element={ selectedElement }>\n\t\t\t\t\t<Box padding={ 2 }>\n\t\t\t\t\t\t<Typography>\n\t\t\t\t\t\t\tHere should be the controls.\n\t\t\t\t\t\t</Typography>\n\t\t\t\t\t</Box>\n\t\t\t\t</ElementContext>\n\t\t\t</PanelBody>\n\t\t</Panel>\n\t);\n};\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getSelectedElements from '../sync/get-selected-elements';\n\nexport default function useSelectedElements() {\n\treturn useListenTo(\n\t\t[\n\t\t\tcommandEndEvent( 'document/elements/select' ),\n\t\t\tcommandEndEvent( 'document/elements/deselect' ),\n\t\t],\n\t\t() => getSelectedElements()\n\t);\n}\n","import { ExtendedWindow } from './types';\nimport { Element } from '../types';\n\nexport default function getSelectedElements(): Element[] {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\tconst selectedElements = extendedWindow.elementor?.selection?.getElements?.() ?? [];\n\n\treturn selectedElements.reduce<Element[]>( ( acc, el ) => {\n\t\tconst type = el.model.get( 'widgetType' ) || el.model.get( 'elType' );\n\n\t\tif ( type ) {\n\t\t\tacc.push( {\n\t\t\t\tid: el.model.get( 'id' ),\n\t\t\t\ttype,\n\t\t\t} );\n\t\t}\n\n\t\treturn acc;\n\t}, [] );\n}\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getWidgetsCache from '../sync/get-widgets-cache';\nimport { ElementType } from '../types';\n\nexport default function useElementType( type?: string ) {\n\treturn useListenTo(\n\t\tcommandEndEvent( 'editor/documents/load' ),\n\t\t(): ElementType | null => {\n\t\t\tif ( ! type ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst widgetsCache = getWidgetsCache();\n\t\t\tconst elementType = widgetsCache?.[ type ];\n\n\t\t\tif ( ! elementType?.atomic_controls ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tkey: type,\n\t\t\t\tcontrols: elementType.atomic_controls,\n\t\t\t\ttitle: elementType.title,\n\t\t\t};\n\t\t},\n\t\t[ type ]\n\t);\n}\n","import { ExtendedWindow } from './types';\n\nexport default function getWidgetsCache() {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\treturn extendedWindow?.elementor?.widgetsCache || null;\n}\n","import * as React from 'react';\nimport { createContext, ReactNode, useContext } from 'react';\nimport { Element } from '../types';\n\ntype ContextValue = {\n\telement: Element;\n}\n\nconst Context = createContext<ContextValue | null>( null );\n\ntype Props = {\n\telement: Element;\n\tchildren?: ReactNode;\n}\n\nexport function ElementContext( { children, element }: Props ) {\n\treturn (\n\t\t<Context.Provider value={ { element } }>\n\t\t\t{ children }\n\t\t</Context.Provider>\n\t);\n}\n\nexport function useElementContext() {\n\tconst context = useContext( Context );\n\n\tif ( ! context ) {\n\t\tthrow new Error( 'useElementContext must be used within a ElementContextProvider' );\n\t}\n\n\treturn context;\n}\n","import { panel } from './panel';\nimport { injectIntoLogic } from '@elementor/editor';\nimport { shouldUseV2Panel } from './sync/should-use-v2-panel';\nimport { EditingPanelHooks } from './components/editing-panel-hooks';\nimport { __registerPanel as registerPanel } from '@elementor/editor-panels';\nimport { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';\n\nexport default function init() {\n\tregisterPanel( panel );\n\tblockV1Panel();\n\n\tinjectIntoLogic( {\n\t\tid: 'editing-panel-hooks',\n\t\tcomponent: EditingPanelHooks,\n\t} );\n}\n\nconst blockV1Panel = () => {\n\tblockDataCommand( {\n\t\tcommand: 'panel/editor/open',\n\t\tcondition: shouldUseV2Panel,\n\t} );\n};\n","import getSelectedElements from './get-selected-elements';\nimport getWidgetsCache from './get-widgets-cache';\n\nexport const shouldUseV2Panel = () => {\n\tconst selectedElements = getSelectedElements();\n\tconst widgetCache = getWidgetsCache();\n\n\tif ( selectedElements.length !== 1 ) {\n\t\treturn false;\n\t}\n\n\t// Check if the selected element has atomic controls, meaning it's a V2 element.\n\treturn !! widgetCache?.[ selectedElements[ 0 ].type ]?.atomic_controls;\n};\n","import { useEffect } from 'react';\nimport { commandStartEvent, __privateListenTo as listenTo } from '@elementor/editor-v1-adapters';\nimport { usePanelActions } from '../panel';\nimport { shouldUseV2Panel } from '../sync/should-use-v2-panel';\n\nexport const useOpenEditorPanel = () => {\n\tconst { open } = usePanelActions();\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandStartEvent( 'panel/editor/open' ),\n\t\t\t() => {\n\t\t\t\tif ( shouldUseV2Panel() ) {\n\t\t\t\t\topen();\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] ); // eslint-disable-line react-hooks/exhaustive-deps\n};\n","import { useOpenEditorPanel } from '../hooks/use-open-editor-panel';\n\nexport const EditingPanelHooks = () => {\n\tuseOpenEditorPanel();\n\n\treturn null;\n};\n","import init from './init';\n\ninit();\n"],"mappings":"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","names":["import_editor_panels","React","useListenTo","import_editor_v1_adapters","useListenTo","createPanel","import_react","import_editor_v1_adapters","listenTo","import_editor_panels","import_editor_v1_adapters","registerPanel","blockDataCommand"]}
1
+ {"version":3,"sources":["../src/panel.ts","../src/components/editing-panel.tsx","../src/hooks/use-selected-elements.ts","../src/sync/get-selected-elements.ts","../src/hooks/use-element-type.ts","../src/sync/get-widgets-cache.ts","../src/components/controls/control-types/select-control.tsx","../src/components/controls/control-context.ts","../src/components/controls/control-types/text-control.tsx","../src/components/controls/settings-control.tsx","../src/sync/update-settings.ts","../src/sync/get-container.ts","../src/hooks/use-widget-settings.ts","../src/contexts/settings-controls.tsx","../src/init.ts","../src/sync/should-use-v2-panel.ts","../src/hooks/use-open-editor-panel.ts","../src/components/editing-panel-hooks.tsx","../src/index.ts"],"sourcesContent":["import { __createPanel as createPanel } from '@elementor/editor-panels';\nimport { EditingPanel } from './components/editing-panel';\n\nexport const {\n\tpanel,\n\tusePanelActions,\n\tusePanelStatus,\n} = createPanel( {\n\tid: 'editing-panel',\n\tcomponent: EditingPanel,\n} );\n","import * as React from 'react';\nimport { __ } from '@wordpress/i18n';\nimport useSelectedElements from '../hooks/use-selected-elements';\nimport useElementType from '../hooks/use-element-type';\nimport { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';\nimport { SelectControl } from './controls/control-types/select-control';\nimport { TextControl } from './controls/control-types/text-control';\nimport { SettingsControl } from '../components/controls/settings-control';\nimport { Stack } from '@elementor/ui';\nimport { ElementContext } from '../contexts/settings-controls';\n\nconst controlTypes = {\n\tselect: SelectControl,\n\ttext: TextControl,\n};\n\nexport const EditingPanel = () => {\n\tconst elements = useSelectedElements();\n\n\tconst selectedElement = elements[ 0 ];\n\n\tconst elementType = useElementType( selectedElement?.type );\n\n\tif ( elements.length !== 1 || ! elementType ) {\n\t\treturn null;\n\t}\n\n\t/* translators: %s: Element type title. */\n\tconst panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', elementType.title );\n\n\treturn (\n\t\t<Panel>\n\t\t\t<PanelHeader>\n\t\t\t\t<PanelHeaderTitle>{ panelTitle }</PanelHeaderTitle>\n\t\t\t</PanelHeader>\n\t\t\t<PanelBody>\n\t\t\t\t<ElementContext element={ selectedElement }>\n\t\t\t\t\t<Stack spacing={ 2 }>\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\telementType.controls.map( ( control ) => {\n\t\t\t\t\t\t\t\tif ( control.type === 'control' ) {\n\t\t\t\t\t\t\t\t\tconst ControlComponent = controlTypes[ control.value.type as keyof typeof controlTypes ];\n\n\t\t\t\t\t\t\t\t\tif ( ! ControlComponent ) {\n\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<SettingsControl key={ control.value.bind } bind={ control.value.bind } elementID={ elements[ 0 ].id }>\n\t\t\t\t\t\t\t\t\t\t\t<SettingsControl.Container>\n\t\t\t\t\t\t\t\t\t\t\t\t<SettingsControl.Label>{ control.value.label }</SettingsControl.Label>\n\t\t\t\t\t\t\t\t\t\t\t\t{ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ }\n\t\t\t\t\t\t\t\t\t\t\t\t<ControlComponent { ...control.value.props as any } />\n\t\t\t\t\t\t\t\t\t\t\t</SettingsControl.Container>\n\t\t\t\t\t\t\t\t\t\t</SettingsControl>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t}\n\t\t\t\t\t</Stack>\n\t\t\t\t</ElementContext>\n\t\t\t</PanelBody>\n\t\t</Panel>\n\t);\n};\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getSelectedElements from '../sync/get-selected-elements';\n\nexport default function useSelectedElements() {\n\treturn useListenTo(\n\t\t[\n\t\t\tcommandEndEvent( 'document/elements/select' ),\n\t\t\tcommandEndEvent( 'document/elements/deselect' ),\n\t\t],\n\t\t() => getSelectedElements()\n\t);\n}\n","import { ExtendedWindow } from './types';\nimport { Element } from '../types';\n\nexport default function getSelectedElements(): Element[] {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\tconst selectedElements = extendedWindow.elementor?.selection?.getElements?.() ?? [];\n\n\treturn selectedElements.reduce<Element[]>( ( acc, el ) => {\n\t\tconst type = el.model.get( 'widgetType' ) || el.model.get( 'elType' );\n\n\t\tif ( type ) {\n\t\t\tacc.push( {\n\t\t\t\tid: el.model.get( 'id' ),\n\t\t\t\ttype,\n\t\t\t} );\n\t\t}\n\n\t\treturn acc;\n\t}, [] );\n}\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getWidgetsCache from '../sync/get-widgets-cache';\nimport { ElementType } from '../types';\n\nexport default function useElementType( type?: string ) {\n\treturn useListenTo(\n\t\tcommandEndEvent( 'editor/documents/load' ),\n\t\t(): ElementType | null => {\n\t\t\tif ( ! type ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst widgetsCache = getWidgetsCache();\n\t\t\tconst elementType = widgetsCache?.[ type ];\n\n\t\t\tif ( ! elementType?.atomic_controls ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tkey: type,\n\t\t\t\tcontrols: elementType.atomic_controls,\n\t\t\t\ttitle: elementType.title,\n\t\t\t};\n\t\t},\n\t\t[ type ]\n\t);\n}\n","import { ExtendedWindow } from './types';\n\nexport default function getWidgetsCache() {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\treturn extendedWindow?.elementor?.widgetsCache || null;\n}\n","import * as React from 'react';\nimport { MenuItem, Select, SelectChangeEvent } from '@elementor/ui';\nimport { useControl } from '../control-context';\nimport { PropValue } from '../../../types';\n\nexport type SelectControlProps<T> = {\n\toptions: Array<{ label: string; value: T; disabled?: boolean }>\n};\n\nexport const SelectControl = <T extends PropValue>( { options }: SelectControlProps<T> ) => {\n\tconst { value, setValue } = useControl<T>();\n\n\tconst handleChange = ( event: SelectChangeEvent<T> ) => {\n\t\tsetValue( event.target.value as T );\n\t};\n\n\treturn (\n\t\t<Select size=\"tiny\" value={ value ?? '' } onChange={ handleChange }>\n\t\t\t{ options.map( ( option ) => (\n\t\t\t\t<MenuItem key={ option.value } value={ option.value } disabled={ option.disabled }>\n\t\t\t\t\t{ option.label }\n\t\t\t\t</MenuItem>\n\t\t\t) ) }\n\t\t</Select>\n\t);\n};\n","import { createContext, useContext } from 'react';\nimport { PropKey, PropValue } from '../../types';\n\nexport type ControlContext<T extends PropValue> = null | {\n\tbind: PropKey;\n\tsetValue: ( value: T ) => void;\n\tvalue: T;\n};\n\nexport const ControlContext = createContext<ControlContext<PropValue>>( null );\n\nexport function useControl<T extends PropValue>( defaultValue?: T ) {\n\tconst controlContext = useContext<ControlContext<T>>( ControlContext as never );\n\n\tif ( ! controlContext ) {\n\t\tthrow new Error( 'useControl must be used within a ControlContext' );\n\t}\n\n\treturn { ...controlContext, value: controlContext.value ?? defaultValue };\n}\n","import * as React from 'react';\nimport { TextareaAutosize } from '@elementor/ui';\nimport { useControl } from '../control-context';\n\nexport type TextControlProps = {\n\tplaceholder?: string;\n}\n\nexport const TextControl = ( { placeholder }: TextControlProps ) => {\n\tconst { value, setValue } = useControl<string>( '' );\n\n\tconst handleChange = ( event: React.ChangeEvent<HTMLInputElement> ) => {\n\t\tsetValue( event.target.value );\n\t};\n\n\treturn (\n\t\t<TextareaAutosize size=\"tiny\" minRows={ 3 }value={ value } onChange={ handleChange } placeholder={ placeholder } />\n\t);\n};\n","import * as React from 'react';\nimport { ControlContext } from './control-context';\nimport { Stack, Typography } from '@elementor/ui';\nimport { updateSettings } from '../../sync/update-settings';\nimport { useWidgetSettings } from '../../hooks/use-widget-settings';\nimport { PropKey, PropValue } from '../../types';\n\ntype Props = {\n\tbind: PropKey;\n\tchildren: React.ReactNode;\n\telementID: Element['id'];\n}\n\nconst SettingsControl = ( { bind, children, elementID }: Props ) => {\n\tconst value = useWidgetSettings( { id: elementID, bind } );\n\n\tconst setValue = ( newValue: PropValue ) => {\n\t\tupdateSettings( {\n\t\t\tid: elementID,\n\t\t\tprops: {\n\t\t\t\t[ bind ]: newValue,\n\t\t\t},\n\t\t} );\n\t};\n\n\treturn (\n\t\t<ControlContext.Provider value={ { setValue, value, bind } }>\n\t\t\t{ children }\n\t\t</ControlContext.Provider>\n\t);\n};\n\nconst Label = ( { children }: { children: React.ReactNode } ) => {\n\treturn <Typography component=\"label\" variant=\"caption\">{ children }</Typography>;\n};\n\nconst Container = ( { children }: { children: React.ReactNode} ) => (\n\t<Stack\n\t\tspacing={ 1 }\n\t\tflexDirection=\"row\"\n\t\talignItems=\"center\"\n\t\tjustifyContent=\"space-between\"\n\t\tflexWrap=\"wrap\"\n\t\tsx={ { px: 2 } }\n\t>\n\t\t{ children }\n\t</Stack>\n);\n\nSettingsControl.Label = Label;\nSettingsControl.Container = Container;\n\nexport { SettingsControl };\n","import { __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters';\nimport { Props } from '../types';\nimport getContainer from './get-container';\n\nexport const updateSettings = ( { id, props }: { id: string, props: Props } ) => {\n\tconst container = getContainer( id );\n\n\trunCommand( 'document/elements/settings', {\n\t\tcontainer,\n\t\tsettings: {\n\t\t\t...props,\n\t\t},\n\t} );\n};\n","import { ExtendedWindow } from './types';\n\nexport default function getContainer( id: string ) {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\tconst container = extendedWindow.elementor?.getContainer?.( id );\n\n\treturn container ?? null;\n}\n","import { commandEndEvent, __privateUseListenTo as useListenTo } from '@elementor/editor-v1-adapters';\nimport { PropValue } from '../types';\nimport getContainer from '../sync/get-container';\n\nexport const useWidgetSettings = ( { id, bind }: { id: string; bind: string } ): PropValue => {\n\treturn useListenTo(\n\t\tcommandEndEvent( 'document/elements/settings' ),\n\t\t() => {\n\t\t\tconst container = getContainer( id );\n\t\t\tconst value = container?.settings?.get( bind ) ?? null;\n\n\t\t\treturn value;\n\t\t},\n\t\t[]\n\t);\n};\n","import * as React from 'react';\nimport { createContext, ReactNode, useContext } from 'react';\nimport { Element } from '../types';\n\ntype ContextValue = {\n\telement: Element;\n}\n\nconst Context = createContext<ContextValue | null>( null );\n\ntype Props = {\n\telement: Element;\n\tchildren?: ReactNode;\n}\n\nexport function ElementContext( { children, element }: Props ) {\n\treturn (\n\t\t<Context.Provider value={ { element } }>\n\t\t\t{ children }\n\t\t</Context.Provider>\n\t);\n}\n\nexport function useElementContext() {\n\tconst context = useContext( Context );\n\n\tif ( ! context ) {\n\t\tthrow new Error( 'useElementContext must be used within a ElementContextProvider' );\n\t}\n\n\treturn context;\n}\n","import { panel } from './panel';\nimport { injectIntoLogic } from '@elementor/editor';\nimport { shouldUseV2Panel } from './sync/should-use-v2-panel';\nimport { EditingPanelHooks } from './components/editing-panel-hooks';\nimport { __registerPanel as registerPanel } from '@elementor/editor-panels';\nimport { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';\n\nexport default function init() {\n\tregisterPanel( panel );\n\tblockV1Panel();\n\n\tinjectIntoLogic( {\n\t\tid: 'editing-panel-hooks',\n\t\tcomponent: EditingPanelHooks,\n\t} );\n}\n\nconst blockV1Panel = () => {\n\tblockDataCommand( {\n\t\tcommand: 'panel/editor/open',\n\t\tcondition: shouldUseV2Panel,\n\t} );\n};\n","import getSelectedElements from './get-selected-elements';\nimport getWidgetsCache from './get-widgets-cache';\n\nexport const shouldUseV2Panel = () => {\n\tconst selectedElements = getSelectedElements();\n\tconst widgetCache = getWidgetsCache();\n\n\tif ( selectedElements.length !== 1 ) {\n\t\treturn false;\n\t}\n\n\t// Check if the selected element has atomic controls, meaning it's a V2 element.\n\treturn !! widgetCache?.[ selectedElements[ 0 ].type ]?.atomic_controls;\n};\n","import { useEffect } from 'react';\nimport { commandStartEvent, __privateListenTo as listenTo } from '@elementor/editor-v1-adapters';\nimport { usePanelActions } from '../panel';\nimport { shouldUseV2Panel } from '../sync/should-use-v2-panel';\n\nexport const useOpenEditorPanel = () => {\n\tconst { open } = usePanelActions();\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandStartEvent( 'panel/editor/open' ),\n\t\t\t() => {\n\t\t\t\tif ( shouldUseV2Panel() ) {\n\t\t\t\t\topen();\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] ); // eslint-disable-line react-hooks/exhaustive-deps\n};\n","import { useOpenEditorPanel } from '../hooks/use-open-editor-panel';\n\nexport const EditingPanelHooks = () => {\n\tuseOpenEditorPanel();\n\n\treturn null;\n};\n","import init from './init';\n\ninit();\n"],"mappings":"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","names":["import_editor_panels","React","useListenTo","import_editor_v1_adapters","useListenTo","React","import_ui","React","import_ui","import_editor_v1_adapters","runCommand","import_editor_v1_adapters","useListenTo","import_ui","React","import_react","createPanel","import_react","import_editor_v1_adapters","listenTo","import_editor_panels","import_editor_v1_adapters","registerPanel","blockDataCommand"]}
package/dist/index.mjs CHANGED
@@ -2,13 +2,7 @@
2
2
  import { __createPanel as createPanel } from "@elementor/editor-panels";
3
3
 
4
4
  // src/components/editing-panel.tsx
5
- import * as React2 from "react";
6
- import {
7
- Panel,
8
- PanelBody,
9
- PanelHeader,
10
- PanelHeaderTitle
11
- } from "@elementor/editor-panels";
5
+ import * as React5 from "react";
12
6
  import { __ } from "@wordpress/i18n";
13
7
 
14
8
  // src/hooks/use-selected-elements.ts
@@ -73,16 +67,130 @@ function useElementType(type) {
73
67
  );
74
68
  }
75
69
 
76
- // src/contexts/settings-controls.tsx
70
+ // src/components/editing-panel.tsx
71
+ import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from "@elementor/editor-panels";
72
+
73
+ // src/components/controls/control-types/select-control.tsx
77
74
  import * as React from "react";
75
+ import { MenuItem, Select } from "@elementor/ui";
76
+
77
+ // src/components/controls/control-context.ts
78
78
  import { createContext, useContext } from "react";
79
- var Context = createContext(null);
79
+ var ControlContext = createContext(null);
80
+ function useControl(defaultValue) {
81
+ const controlContext = useContext(ControlContext);
82
+ if (!controlContext) {
83
+ throw new Error("useControl must be used within a ControlContext");
84
+ }
85
+ return { ...controlContext, value: controlContext.value ?? defaultValue };
86
+ }
87
+
88
+ // src/components/controls/control-types/select-control.tsx
89
+ var SelectControl = ({ options }) => {
90
+ const { value, setValue } = useControl();
91
+ const handleChange = (event) => {
92
+ setValue(event.target.value);
93
+ };
94
+ return /* @__PURE__ */ React.createElement(Select, { size: "tiny", value: value ?? "", onChange: handleChange }, options.map((option) => /* @__PURE__ */ React.createElement(MenuItem, { key: option.value, value: option.value, disabled: option.disabled }, option.label)));
95
+ };
96
+
97
+ // src/components/controls/control-types/text-control.tsx
98
+ import * as React2 from "react";
99
+ import { TextareaAutosize } from "@elementor/ui";
100
+ var TextControl = ({ placeholder }) => {
101
+ const { value, setValue } = useControl("");
102
+ const handleChange = (event) => {
103
+ setValue(event.target.value);
104
+ };
105
+ return /* @__PURE__ */ React2.createElement(TextareaAutosize, { size: "tiny", minRows: 3, value, onChange: handleChange, placeholder });
106
+ };
107
+
108
+ // src/components/controls/settings-control.tsx
109
+ import * as React3 from "react";
110
+ import { Stack, Typography } from "@elementor/ui";
111
+
112
+ // src/sync/update-settings.ts
113
+ import { __privateRunCommand as runCommand } from "@elementor/editor-v1-adapters";
114
+
115
+ // src/sync/get-container.ts
116
+ function getContainer(id) {
117
+ const extendedWindow = window;
118
+ const container = extendedWindow.elementor?.getContainer?.(id);
119
+ return container ?? null;
120
+ }
121
+
122
+ // src/sync/update-settings.ts
123
+ var updateSettings = ({ id, props }) => {
124
+ const container = getContainer(id);
125
+ runCommand("document/elements/settings", {
126
+ container,
127
+ settings: {
128
+ ...props
129
+ }
130
+ });
131
+ };
132
+
133
+ // src/hooks/use-widget-settings.ts
134
+ import { commandEndEvent as commandEndEvent3, __privateUseListenTo as useListenTo3 } from "@elementor/editor-v1-adapters";
135
+ var useWidgetSettings = ({ id, bind }) => {
136
+ return useListenTo3(
137
+ commandEndEvent3("document/elements/settings"),
138
+ () => {
139
+ const container = getContainer(id);
140
+ const value = container?.settings?.get(bind) ?? null;
141
+ return value;
142
+ },
143
+ []
144
+ );
145
+ };
146
+
147
+ // src/components/controls/settings-control.tsx
148
+ var SettingsControl = ({ bind, children, elementID }) => {
149
+ const value = useWidgetSettings({ id: elementID, bind });
150
+ const setValue = (newValue) => {
151
+ updateSettings({
152
+ id: elementID,
153
+ props: {
154
+ [bind]: newValue
155
+ }
156
+ });
157
+ };
158
+ return /* @__PURE__ */ React3.createElement(ControlContext.Provider, { value: { setValue, value, bind } }, children);
159
+ };
160
+ var Label = ({ children }) => {
161
+ return /* @__PURE__ */ React3.createElement(Typography, { component: "label", variant: "caption" }, children);
162
+ };
163
+ var Container = ({ children }) => /* @__PURE__ */ React3.createElement(
164
+ Stack,
165
+ {
166
+ spacing: 1,
167
+ flexDirection: "row",
168
+ alignItems: "center",
169
+ justifyContent: "space-between",
170
+ flexWrap: "wrap",
171
+ sx: { px: 2 }
172
+ },
173
+ children
174
+ );
175
+ SettingsControl.Label = Label;
176
+ SettingsControl.Container = Container;
177
+
178
+ // src/components/editing-panel.tsx
179
+ import { Stack as Stack2 } from "@elementor/ui";
180
+
181
+ // src/contexts/settings-controls.tsx
182
+ import * as React4 from "react";
183
+ import { createContext as createContext2, useContext as useContext2 } from "react";
184
+ var Context = createContext2(null);
80
185
  function ElementContext({ children, element }) {
81
- return /* @__PURE__ */ React.createElement(Context.Provider, { value: { element } }, children);
186
+ return /* @__PURE__ */ React4.createElement(Context.Provider, { value: { element } }, children);
82
187
  }
83
188
 
84
189
  // src/components/editing-panel.tsx
85
- import { Box, Typography } from "@elementor/ui";
190
+ var controlTypes = {
191
+ select: SelectControl,
192
+ text: TextControl
193
+ };
86
194
  var EditingPanel = () => {
87
195
  const elements = useSelectedElements();
88
196
  const selectedElement = elements[0];
@@ -91,7 +199,16 @@ var EditingPanel = () => {
91
199
  return null;
92
200
  }
93
201
  const panelTitle = __("Edit %s", "elementor").replace("%s", elementType.title);
94
- return /* @__PURE__ */ React2.createElement(Panel, null, /* @__PURE__ */ React2.createElement(PanelHeader, null, /* @__PURE__ */ React2.createElement(PanelHeaderTitle, null, panelTitle)), /* @__PURE__ */ React2.createElement(PanelBody, null, /* @__PURE__ */ React2.createElement(ElementContext, { element: selectedElement }, /* @__PURE__ */ React2.createElement(Box, { padding: 2 }, /* @__PURE__ */ React2.createElement(Typography, null, "Here should be the controls.")))));
202
+ return /* @__PURE__ */ React5.createElement(Panel, null, /* @__PURE__ */ React5.createElement(PanelHeader, null, /* @__PURE__ */ React5.createElement(PanelHeaderTitle, null, panelTitle)), /* @__PURE__ */ React5.createElement(PanelBody, null, /* @__PURE__ */ React5.createElement(ElementContext, { element: selectedElement }, /* @__PURE__ */ React5.createElement(Stack2, { spacing: 2 }, elementType.controls.map((control) => {
203
+ if (control.type === "control") {
204
+ const ControlComponent = controlTypes[control.value.type];
205
+ if (!ControlComponent) {
206
+ return null;
207
+ }
208
+ return /* @__PURE__ */ React5.createElement(SettingsControl, { key: control.value.bind, bind: control.value.bind, elementID: elements[0].id }, /* @__PURE__ */ React5.createElement(SettingsControl.Container, null, /* @__PURE__ */ React5.createElement(SettingsControl.Label, null, control.value.label), /* @__PURE__ */ React5.createElement(ControlComponent, { ...control.value.props })));
209
+ }
210
+ return null;
211
+ })))));
95
212
  };
96
213
 
97
214
  // src/panel.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/panel.ts","../src/components/editing-panel.tsx","../src/hooks/use-selected-elements.ts","../src/sync/get-selected-elements.ts","../src/hooks/use-element-type.ts","../src/sync/get-widgets-cache.ts","../src/contexts/settings-controls.tsx","../src/init.ts","../src/sync/should-use-v2-panel.ts","../src/hooks/use-open-editor-panel.ts","../src/components/editing-panel-hooks.tsx","../src/index.ts"],"sourcesContent":["import { __createPanel as createPanel } from '@elementor/editor-panels';\nimport { EditingPanel } from './components/editing-panel';\n\nexport const {\n\tpanel,\n\tusePanelActions,\n\tusePanelStatus,\n} = createPanel( {\n\tid: 'editing-panel',\n\tcomponent: EditingPanel,\n} );\n","import * as React from 'react';\nimport {\n\tPanel,\n\tPanelBody,\n\tPanelHeader,\n\tPanelHeaderTitle,\n} from '@elementor/editor-panels';\nimport { __ } from '@wordpress/i18n';\nimport useSelectedElements from '../hooks/use-selected-elements';\nimport useElementType from '../hooks/use-element-type';\nimport { ElementContext } from '../contexts/settings-controls';\nimport { Box, Typography } from '@elementor/ui';\n\nexport const EditingPanel = () => {\n\tconst elements = useSelectedElements();\n\n\tconst selectedElement = elements[ 0 ];\n\n\tconst elementType = useElementType( selectedElement?.type );\n\n\tif ( elements.length !== 1 || ! elementType ) {\n\t\treturn null;\n\t}\n\n\t/* translators: %s: Element type title. */\n\tconst panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', elementType.title );\n\n\treturn (\n\t\t<Panel>\n\t\t\t<PanelHeader>\n\t\t\t\t<PanelHeaderTitle>{ panelTitle }</PanelHeaderTitle>\n\t\t\t</PanelHeader>\n\t\t\t<PanelBody>\n\t\t\t\t<ElementContext element={ selectedElement }>\n\t\t\t\t\t<Box padding={ 2 }>\n\t\t\t\t\t\t<Typography>\n\t\t\t\t\t\t\tHere should be the controls.\n\t\t\t\t\t\t</Typography>\n\t\t\t\t\t</Box>\n\t\t\t\t</ElementContext>\n\t\t\t</PanelBody>\n\t\t</Panel>\n\t);\n};\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getSelectedElements from '../sync/get-selected-elements';\n\nexport default function useSelectedElements() {\n\treturn useListenTo(\n\t\t[\n\t\t\tcommandEndEvent( 'document/elements/select' ),\n\t\t\tcommandEndEvent( 'document/elements/deselect' ),\n\t\t],\n\t\t() => getSelectedElements()\n\t);\n}\n","import { ExtendedWindow } from './types';\nimport { Element } from '../types';\n\nexport default function getSelectedElements(): Element[] {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\tconst selectedElements = extendedWindow.elementor?.selection?.getElements?.() ?? [];\n\n\treturn selectedElements.reduce<Element[]>( ( acc, el ) => {\n\t\tconst type = el.model.get( 'widgetType' ) || el.model.get( 'elType' );\n\n\t\tif ( type ) {\n\t\t\tacc.push( {\n\t\t\t\tid: el.model.get( 'id' ),\n\t\t\t\ttype,\n\t\t\t} );\n\t\t}\n\n\t\treturn acc;\n\t}, [] );\n}\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getWidgetsCache from '../sync/get-widgets-cache';\nimport { ElementType } from '../types';\n\nexport default function useElementType( type?: string ) {\n\treturn useListenTo(\n\t\tcommandEndEvent( 'editor/documents/load' ),\n\t\t(): ElementType | null => {\n\t\t\tif ( ! type ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst widgetsCache = getWidgetsCache();\n\t\t\tconst elementType = widgetsCache?.[ type ];\n\n\t\t\tif ( ! elementType?.atomic_controls ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tkey: type,\n\t\t\t\tcontrols: elementType.atomic_controls,\n\t\t\t\ttitle: elementType.title,\n\t\t\t};\n\t\t},\n\t\t[ type ]\n\t);\n}\n","import { ExtendedWindow } from './types';\n\nexport default function getWidgetsCache() {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\treturn extendedWindow?.elementor?.widgetsCache || null;\n}\n","import * as React from 'react';\nimport { createContext, ReactNode, useContext } from 'react';\nimport { Element } from '../types';\n\ntype ContextValue = {\n\telement: Element;\n}\n\nconst Context = createContext<ContextValue | null>( null );\n\ntype Props = {\n\telement: Element;\n\tchildren?: ReactNode;\n}\n\nexport function ElementContext( { children, element }: Props ) {\n\treturn (\n\t\t<Context.Provider value={ { element } }>\n\t\t\t{ children }\n\t\t</Context.Provider>\n\t);\n}\n\nexport function useElementContext() {\n\tconst context = useContext( Context );\n\n\tif ( ! context ) {\n\t\tthrow new Error( 'useElementContext must be used within a ElementContextProvider' );\n\t}\n\n\treturn context;\n}\n","import { panel } from './panel';\nimport { injectIntoLogic } from '@elementor/editor';\nimport { shouldUseV2Panel } from './sync/should-use-v2-panel';\nimport { EditingPanelHooks } from './components/editing-panel-hooks';\nimport { __registerPanel as registerPanel } from '@elementor/editor-panels';\nimport { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';\n\nexport default function init() {\n\tregisterPanel( panel );\n\tblockV1Panel();\n\n\tinjectIntoLogic( {\n\t\tid: 'editing-panel-hooks',\n\t\tcomponent: EditingPanelHooks,\n\t} );\n}\n\nconst blockV1Panel = () => {\n\tblockDataCommand( {\n\t\tcommand: 'panel/editor/open',\n\t\tcondition: shouldUseV2Panel,\n\t} );\n};\n","import getSelectedElements from './get-selected-elements';\nimport getWidgetsCache from './get-widgets-cache';\n\nexport const shouldUseV2Panel = () => {\n\tconst selectedElements = getSelectedElements();\n\tconst widgetCache = getWidgetsCache();\n\n\tif ( selectedElements.length !== 1 ) {\n\t\treturn false;\n\t}\n\n\t// Check if the selected element has atomic controls, meaning it's a V2 element.\n\treturn !! widgetCache?.[ selectedElements[ 0 ].type ]?.atomic_controls;\n};\n","import { useEffect } from 'react';\nimport { commandStartEvent, __privateListenTo as listenTo } from '@elementor/editor-v1-adapters';\nimport { usePanelActions } from '../panel';\nimport { shouldUseV2Panel } from '../sync/should-use-v2-panel';\n\nexport const useOpenEditorPanel = () => {\n\tconst { open } = usePanelActions();\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandStartEvent( 'panel/editor/open' ),\n\t\t\t() => {\n\t\t\t\tif ( shouldUseV2Panel() ) {\n\t\t\t\t\topen();\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] ); // eslint-disable-line react-hooks/exhaustive-deps\n};\n","import { useOpenEditorPanel } from '../hooks/use-open-editor-panel';\n\nexport const EditingPanelHooks = () => {\n\tuseOpenEditorPanel();\n\n\treturn null;\n};\n","import init from './init';\n\ninit();\n"],"mappings":"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","names":["React","useListenTo","commandEndEvent","useListenTo","commandEndEvent"]}
1
+ {"version":3,"sources":["../src/panel.ts","../src/components/editing-panel.tsx","../src/hooks/use-selected-elements.ts","../src/sync/get-selected-elements.ts","../src/hooks/use-element-type.ts","../src/sync/get-widgets-cache.ts","../src/components/controls/control-types/select-control.tsx","../src/components/controls/control-context.ts","../src/components/controls/control-types/text-control.tsx","../src/components/controls/settings-control.tsx","../src/sync/update-settings.ts","../src/sync/get-container.ts","../src/hooks/use-widget-settings.ts","../src/contexts/settings-controls.tsx","../src/init.ts","../src/sync/should-use-v2-panel.ts","../src/hooks/use-open-editor-panel.ts","../src/components/editing-panel-hooks.tsx","../src/index.ts"],"sourcesContent":["import { __createPanel as createPanel } from '@elementor/editor-panels';\nimport { EditingPanel } from './components/editing-panel';\n\nexport const {\n\tpanel,\n\tusePanelActions,\n\tusePanelStatus,\n} = createPanel( {\n\tid: 'editing-panel',\n\tcomponent: EditingPanel,\n} );\n","import * as React from 'react';\nimport { __ } from '@wordpress/i18n';\nimport useSelectedElements from '../hooks/use-selected-elements';\nimport useElementType from '../hooks/use-element-type';\nimport { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';\nimport { SelectControl } from './controls/control-types/select-control';\nimport { TextControl } from './controls/control-types/text-control';\nimport { SettingsControl } from '../components/controls/settings-control';\nimport { Stack } from '@elementor/ui';\nimport { ElementContext } from '../contexts/settings-controls';\n\nconst controlTypes = {\n\tselect: SelectControl,\n\ttext: TextControl,\n};\n\nexport const EditingPanel = () => {\n\tconst elements = useSelectedElements();\n\n\tconst selectedElement = elements[ 0 ];\n\n\tconst elementType = useElementType( selectedElement?.type );\n\n\tif ( elements.length !== 1 || ! elementType ) {\n\t\treturn null;\n\t}\n\n\t/* translators: %s: Element type title. */\n\tconst panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', elementType.title );\n\n\treturn (\n\t\t<Panel>\n\t\t\t<PanelHeader>\n\t\t\t\t<PanelHeaderTitle>{ panelTitle }</PanelHeaderTitle>\n\t\t\t</PanelHeader>\n\t\t\t<PanelBody>\n\t\t\t\t<ElementContext element={ selectedElement }>\n\t\t\t\t\t<Stack spacing={ 2 }>\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\telementType.controls.map( ( control ) => {\n\t\t\t\t\t\t\t\tif ( control.type === 'control' ) {\n\t\t\t\t\t\t\t\t\tconst ControlComponent = controlTypes[ control.value.type as keyof typeof controlTypes ];\n\n\t\t\t\t\t\t\t\t\tif ( ! ControlComponent ) {\n\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<SettingsControl key={ control.value.bind } bind={ control.value.bind } elementID={ elements[ 0 ].id }>\n\t\t\t\t\t\t\t\t\t\t\t<SettingsControl.Container>\n\t\t\t\t\t\t\t\t\t\t\t\t<SettingsControl.Label>{ control.value.label }</SettingsControl.Label>\n\t\t\t\t\t\t\t\t\t\t\t\t{ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ }\n\t\t\t\t\t\t\t\t\t\t\t\t<ControlComponent { ...control.value.props as any } />\n\t\t\t\t\t\t\t\t\t\t\t</SettingsControl.Container>\n\t\t\t\t\t\t\t\t\t\t</SettingsControl>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t}\n\t\t\t\t\t</Stack>\n\t\t\t\t</ElementContext>\n\t\t\t</PanelBody>\n\t\t</Panel>\n\t);\n};\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getSelectedElements from '../sync/get-selected-elements';\n\nexport default function useSelectedElements() {\n\treturn useListenTo(\n\t\t[\n\t\t\tcommandEndEvent( 'document/elements/select' ),\n\t\t\tcommandEndEvent( 'document/elements/deselect' ),\n\t\t],\n\t\t() => getSelectedElements()\n\t);\n}\n","import { ExtendedWindow } from './types';\nimport { Element } from '../types';\n\nexport default function getSelectedElements(): Element[] {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\tconst selectedElements = extendedWindow.elementor?.selection?.getElements?.() ?? [];\n\n\treturn selectedElements.reduce<Element[]>( ( acc, el ) => {\n\t\tconst type = el.model.get( 'widgetType' ) || el.model.get( 'elType' );\n\n\t\tif ( type ) {\n\t\t\tacc.push( {\n\t\t\t\tid: el.model.get( 'id' ),\n\t\t\t\ttype,\n\t\t\t} );\n\t\t}\n\n\t\treturn acc;\n\t}, [] );\n}\n","import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';\nimport getWidgetsCache from '../sync/get-widgets-cache';\nimport { ElementType } from '../types';\n\nexport default function useElementType( type?: string ) {\n\treturn useListenTo(\n\t\tcommandEndEvent( 'editor/documents/load' ),\n\t\t(): ElementType | null => {\n\t\t\tif ( ! type ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst widgetsCache = getWidgetsCache();\n\t\t\tconst elementType = widgetsCache?.[ type ];\n\n\t\t\tif ( ! elementType?.atomic_controls ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tkey: type,\n\t\t\t\tcontrols: elementType.atomic_controls,\n\t\t\t\ttitle: elementType.title,\n\t\t\t};\n\t\t},\n\t\t[ type ]\n\t);\n}\n","import { ExtendedWindow } from './types';\n\nexport default function getWidgetsCache() {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\treturn extendedWindow?.elementor?.widgetsCache || null;\n}\n","import * as React from 'react';\nimport { MenuItem, Select, SelectChangeEvent } from '@elementor/ui';\nimport { useControl } from '../control-context';\nimport { PropValue } from '../../../types';\n\nexport type SelectControlProps<T> = {\n\toptions: Array<{ label: string; value: T; disabled?: boolean }>\n};\n\nexport const SelectControl = <T extends PropValue>( { options }: SelectControlProps<T> ) => {\n\tconst { value, setValue } = useControl<T>();\n\n\tconst handleChange = ( event: SelectChangeEvent<T> ) => {\n\t\tsetValue( event.target.value as T );\n\t};\n\n\treturn (\n\t\t<Select size=\"tiny\" value={ value ?? '' } onChange={ handleChange }>\n\t\t\t{ options.map( ( option ) => (\n\t\t\t\t<MenuItem key={ option.value } value={ option.value } disabled={ option.disabled }>\n\t\t\t\t\t{ option.label }\n\t\t\t\t</MenuItem>\n\t\t\t) ) }\n\t\t</Select>\n\t);\n};\n","import { createContext, useContext } from 'react';\nimport { PropKey, PropValue } from '../../types';\n\nexport type ControlContext<T extends PropValue> = null | {\n\tbind: PropKey;\n\tsetValue: ( value: T ) => void;\n\tvalue: T;\n};\n\nexport const ControlContext = createContext<ControlContext<PropValue>>( null );\n\nexport function useControl<T extends PropValue>( defaultValue?: T ) {\n\tconst controlContext = useContext<ControlContext<T>>( ControlContext as never );\n\n\tif ( ! controlContext ) {\n\t\tthrow new Error( 'useControl must be used within a ControlContext' );\n\t}\n\n\treturn { ...controlContext, value: controlContext.value ?? defaultValue };\n}\n","import * as React from 'react';\nimport { TextareaAutosize } from '@elementor/ui';\nimport { useControl } from '../control-context';\n\nexport type TextControlProps = {\n\tplaceholder?: string;\n}\n\nexport const TextControl = ( { placeholder }: TextControlProps ) => {\n\tconst { value, setValue } = useControl<string>( '' );\n\n\tconst handleChange = ( event: React.ChangeEvent<HTMLInputElement> ) => {\n\t\tsetValue( event.target.value );\n\t};\n\n\treturn (\n\t\t<TextareaAutosize size=\"tiny\" minRows={ 3 }value={ value } onChange={ handleChange } placeholder={ placeholder } />\n\t);\n};\n","import * as React from 'react';\nimport { ControlContext } from './control-context';\nimport { Stack, Typography } from '@elementor/ui';\nimport { updateSettings } from '../../sync/update-settings';\nimport { useWidgetSettings } from '../../hooks/use-widget-settings';\nimport { PropKey, PropValue } from '../../types';\n\ntype Props = {\n\tbind: PropKey;\n\tchildren: React.ReactNode;\n\telementID: Element['id'];\n}\n\nconst SettingsControl = ( { bind, children, elementID }: Props ) => {\n\tconst value = useWidgetSettings( { id: elementID, bind } );\n\n\tconst setValue = ( newValue: PropValue ) => {\n\t\tupdateSettings( {\n\t\t\tid: elementID,\n\t\t\tprops: {\n\t\t\t\t[ bind ]: newValue,\n\t\t\t},\n\t\t} );\n\t};\n\n\treturn (\n\t\t<ControlContext.Provider value={ { setValue, value, bind } }>\n\t\t\t{ children }\n\t\t</ControlContext.Provider>\n\t);\n};\n\nconst Label = ( { children }: { children: React.ReactNode } ) => {\n\treturn <Typography component=\"label\" variant=\"caption\">{ children }</Typography>;\n};\n\nconst Container = ( { children }: { children: React.ReactNode} ) => (\n\t<Stack\n\t\tspacing={ 1 }\n\t\tflexDirection=\"row\"\n\t\talignItems=\"center\"\n\t\tjustifyContent=\"space-between\"\n\t\tflexWrap=\"wrap\"\n\t\tsx={ { px: 2 } }\n\t>\n\t\t{ children }\n\t</Stack>\n);\n\nSettingsControl.Label = Label;\nSettingsControl.Container = Container;\n\nexport { SettingsControl };\n","import { __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters';\nimport { Props } from '../types';\nimport getContainer from './get-container';\n\nexport const updateSettings = ( { id, props }: { id: string, props: Props } ) => {\n\tconst container = getContainer( id );\n\n\trunCommand( 'document/elements/settings', {\n\t\tcontainer,\n\t\tsettings: {\n\t\t\t...props,\n\t\t},\n\t} );\n};\n","import { ExtendedWindow } from './types';\n\nexport default function getContainer( id: string ) {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\tconst container = extendedWindow.elementor?.getContainer?.( id );\n\n\treturn container ?? null;\n}\n","import { commandEndEvent, __privateUseListenTo as useListenTo } from '@elementor/editor-v1-adapters';\nimport { PropValue } from '../types';\nimport getContainer from '../sync/get-container';\n\nexport const useWidgetSettings = ( { id, bind }: { id: string; bind: string } ): PropValue => {\n\treturn useListenTo(\n\t\tcommandEndEvent( 'document/elements/settings' ),\n\t\t() => {\n\t\t\tconst container = getContainer( id );\n\t\t\tconst value = container?.settings?.get( bind ) ?? null;\n\n\t\t\treturn value;\n\t\t},\n\t\t[]\n\t);\n};\n","import * as React from 'react';\nimport { createContext, ReactNode, useContext } from 'react';\nimport { Element } from '../types';\n\ntype ContextValue = {\n\telement: Element;\n}\n\nconst Context = createContext<ContextValue | null>( null );\n\ntype Props = {\n\telement: Element;\n\tchildren?: ReactNode;\n}\n\nexport function ElementContext( { children, element }: Props ) {\n\treturn (\n\t\t<Context.Provider value={ { element } }>\n\t\t\t{ children }\n\t\t</Context.Provider>\n\t);\n}\n\nexport function useElementContext() {\n\tconst context = useContext( Context );\n\n\tif ( ! context ) {\n\t\tthrow new Error( 'useElementContext must be used within a ElementContextProvider' );\n\t}\n\n\treturn context;\n}\n","import { panel } from './panel';\nimport { injectIntoLogic } from '@elementor/editor';\nimport { shouldUseV2Panel } from './sync/should-use-v2-panel';\nimport { EditingPanelHooks } from './components/editing-panel-hooks';\nimport { __registerPanel as registerPanel } from '@elementor/editor-panels';\nimport { __privateBlockDataCommand as blockDataCommand } from '@elementor/editor-v1-adapters';\n\nexport default function init() {\n\tregisterPanel( panel );\n\tblockV1Panel();\n\n\tinjectIntoLogic( {\n\t\tid: 'editing-panel-hooks',\n\t\tcomponent: EditingPanelHooks,\n\t} );\n}\n\nconst blockV1Panel = () => {\n\tblockDataCommand( {\n\t\tcommand: 'panel/editor/open',\n\t\tcondition: shouldUseV2Panel,\n\t} );\n};\n","import getSelectedElements from './get-selected-elements';\nimport getWidgetsCache from './get-widgets-cache';\n\nexport const shouldUseV2Panel = () => {\n\tconst selectedElements = getSelectedElements();\n\tconst widgetCache = getWidgetsCache();\n\n\tif ( selectedElements.length !== 1 ) {\n\t\treturn false;\n\t}\n\n\t// Check if the selected element has atomic controls, meaning it's a V2 element.\n\treturn !! widgetCache?.[ selectedElements[ 0 ].type ]?.atomic_controls;\n};\n","import { useEffect } from 'react';\nimport { commandStartEvent, __privateListenTo as listenTo } from '@elementor/editor-v1-adapters';\nimport { usePanelActions } from '../panel';\nimport { shouldUseV2Panel } from '../sync/should-use-v2-panel';\n\nexport const useOpenEditorPanel = () => {\n\tconst { open } = usePanelActions();\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandStartEvent( 'panel/editor/open' ),\n\t\t\t() => {\n\t\t\t\tif ( shouldUseV2Panel() ) {\n\t\t\t\t\topen();\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] ); // eslint-disable-line react-hooks/exhaustive-deps\n};\n","import { useOpenEditorPanel } from '../hooks/use-open-editor-panel';\n\nexport const EditingPanelHooks = () => {\n\tuseOpenEditorPanel();\n\n\treturn null;\n};\n","import init from './init';\n\ninit();\n"],"mappings":"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","names":["React","useListenTo","commandEndEvent","useListenTo","commandEndEvent","React","React","commandEndEvent","useListenTo","useListenTo","commandEndEvent","Stack","React","createContext","useContext","Stack"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-editing-panel",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -41,5 +41,5 @@
41
41
  "peerDependencies": {
42
42
  "react": "^18.3.1"
43
43
  },
44
- "gitHead": "24e239cdbfb5a4ec3776a7694d6800a5a79c08e8"
44
+ "gitHead": "d35136851ca21a11d375734e8e078d5ef5b9d52d"
45
45
  }
@@ -1,16 +1,27 @@
1
- import { V1Element, V1ElementModelProps } from '../sync/types';
1
+ import { V1Element, V1ElementModelProps, V1ElementSettingsProps } from '../sync/types';
2
2
 
3
- export function mockV1Element( partialSettings: Partial<V1ElementModelProps> ): V1Element {
4
- const settings = {
3
+ export function mockV1Element( {
4
+ model: partialModel = {},
5
+ settings: partialSettings = {},
6
+ }: {
7
+ model?: Partial<V1ElementModelProps>,
8
+ settings?: Partial<V1ElementSettingsProps>
9
+ } ): V1Element {
10
+ const model = {
5
11
  elType: 'widget',
6
12
  id: '1',
7
- ...partialSettings,
13
+ ...partialModel,
8
14
  };
9
15
 
10
16
  return {
11
17
  model: {
12
18
  get: ( key ) => {
13
- return settings[ key ];
19
+ return model[ key ];
20
+ },
21
+ },
22
+ settings: {
23
+ get: ( key ) => {
24
+ return partialSettings[ key ];
14
25
  },
15
26
  },
16
27
  };
@@ -51,7 +51,7 @@ describe( '<EditingPanel />', () => {
51
51
  expect( screen.queryByText( 'Edit Atomic Heading' ) ).not.toBeInTheDocument();
52
52
 
53
53
  // Act.
54
- selectElements( [ mockV1Element( { widgetType: 'atomic-heading' } ) ] );
54
+ selectElements( [ mockV1Element( { model: { widgetType: 'atomic-heading' } } ) ] );
55
55
 
56
56
  // Assert.
57
57
  expect( screen.getByText( 'Edit Atomic Heading' ) ).toBeInTheDocument();
@@ -67,20 +67,20 @@ describe( '<EditingPanel />', () => {
67
67
  {
68
68
  title: 'multiple elements are selected',
69
69
  selected: [
70
- mockV1Element( { widgetType: 'atomic-heading' } ),
71
- mockV1Element( { widgetType: 'atomic-heading' } ),
70
+ mockV1Element( { model: { widgetType: 'atomic-heading' } } ),
71
+ mockV1Element( { model: { widgetType: 'atomic-heading' } } ),
72
72
  ],
73
73
  },
74
74
  {
75
75
  title: 'the element type does not exist',
76
76
  selected: [
77
- mockV1Element( { widgetType: 'atomic-button' } ),
77
+ mockV1Element( { model: { widgetType: 'atomic-button' } } ),
78
78
  ],
79
79
  },
80
80
  {
81
81
  title: 'the element does not have atomic controls',
82
82
  selected: [
83
- mockV1Element( { widgetType: 'heading' } ),
83
+ mockV1Element( { model: { widgetType: 'heading' } } ),
84
84
  ],
85
85
  },
86
86
  ] )( 'should not render panel when $title', ( { selected } ) => {
@@ -0,0 +1,14 @@
1
+ import { renderHook } from '@testing-library/react';
2
+ import { useControl } from '../control-context';
3
+
4
+ describe( 'ControlContext', () => {
5
+ it( 'should throw error if used outside of ControlContext', () => {
6
+ // Act & Assert.
7
+ expect( () => {
8
+ renderHook( () => useControl( '' ) );
9
+ } ).toThrow( 'useControl must be used within a ControlContext' );
10
+
11
+ // Suppress console.error from React.
12
+ expect( console ).toHaveErrored();
13
+ } );
14
+ } );
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { SettingsControl } from '../settings-control';
4
+ import { useControl } from '../control-context';
5
+ import { updateSettings } from '../../../sync/update-settings';
6
+ import { useWidgetSettings } from '../../../hooks/use-widget-settings';
7
+
8
+ jest.mock( '../../../sync/update-settings' );
9
+ jest.mock( '../../../hooks/use-widget-settings' );
10
+
11
+ describe( 'SettingsControl', () => {
12
+ beforeEach( () => {
13
+ jest.mocked( useWidgetSettings ).mockReturnValue( 'Hello, World!' );
14
+ } );
15
+
16
+ it( 'should set the initial value', () => {
17
+ // Arrange.
18
+ const elementID = '1-heading';
19
+ const bind = 'text';
20
+
21
+ // Act.
22
+ render(
23
+ <SettingsControl bind={ bind } elementID={ elementID }>
24
+ <MockControl />
25
+ </SettingsControl>
26
+ );
27
+
28
+ // Assert.
29
+ expect( screen.getByRole( 'textbox', { name: bind } ) ).toHaveValue( 'Hello, World!' );
30
+ } );
31
+
32
+ it( 'should pass the updated payload when input value changes', () => {
33
+ // Arrange.
34
+ const elementID = '1-heading';
35
+ const bind = 'text';
36
+
37
+ // Act.
38
+ render(
39
+ <SettingsControl bind={ bind } elementID={ elementID }>
40
+ <MockControl />
41
+ </SettingsControl>
42
+ );
43
+
44
+ const input = screen.getByRole( 'textbox', { name: bind } );
45
+ const newValue = 'Goodbye, World!';
46
+
47
+ fireEvent.change( input, { target: { value: newValue } } );
48
+
49
+ // Assert.
50
+ expect( jest.mocked( updateSettings ) ).toHaveBeenCalledWith( {
51
+ id: elementID,
52
+ props: { [ bind ]: newValue },
53
+ } );
54
+ } );
55
+ } );
56
+
57
+ const MockControl = () => {
58
+ const { value, setValue, bind } = useControl<string>( '' );
59
+
60
+ const handleChange = ( event: React.ChangeEvent<HTMLInputElement> ) => {
61
+ setValue( event.target.value );
62
+ };
63
+
64
+ return (
65
+ <input type="text" aria-label={ bind } value={ value } onChange={ handleChange } />
66
+ );
67
+ };
@@ -0,0 +1,20 @@
1
+ import { createContext, useContext } from 'react';
2
+ import { PropKey, PropValue } from '../../types';
3
+
4
+ export type ControlContext<T extends PropValue> = null | {
5
+ bind: PropKey;
6
+ setValue: ( value: T ) => void;
7
+ value: T;
8
+ };
9
+
10
+ export const ControlContext = createContext<ControlContext<PropValue>>( null );
11
+
12
+ export function useControl<T extends PropValue>( defaultValue?: T ) {
13
+ const controlContext = useContext<ControlContext<T>>( ControlContext as never );
14
+
15
+ if ( ! controlContext ) {
16
+ throw new Error( 'useControl must be used within a ControlContext' );
17
+ }
18
+
19
+ return { ...controlContext, value: controlContext.value ?? defaultValue };
20
+ }
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { ControlContext } from '../../control-context';
4
+ import { SelectControl } from '../select-control';
5
+
6
+ describe( 'SelectControl', () => {
7
+ it( 'should pass the updated payload when select value changes', () => {
8
+ // Arrange.
9
+ const setValue = jest.fn();
10
+ const options = [
11
+ { label: 'Option 1', value: 'value1' },
12
+ { label: 'Option 2', value: 'value2' },
13
+ ];
14
+
15
+ // Act.
16
+ render(
17
+ <ControlContext.Provider value={ { setValue, value: 'value1', bind: 'select' } }>
18
+ <SelectControl options={ options } />
19
+ </ControlContext.Provider>
20
+ );
21
+
22
+ const select = screen.getByRole( 'combobox' );
23
+
24
+ // Assert.
25
+ expect( screen.getByText( 'Option 1' ) ).toBeInTheDocument();
26
+ expect( screen.queryByText( 'Option 2' ) ).not.toBeInTheDocument();
27
+
28
+ // Act.
29
+ fireEvent.mouseDown( select );
30
+
31
+ const option2 = screen.getByText( 'Option 2' );
32
+
33
+ fireEvent.click( option2 );
34
+
35
+ // Assert.
36
+ expect( setValue ).toHaveBeenCalledWith( 'value2' );
37
+ } );
38
+
39
+ it( 'should disable the select options', () => {
40
+ // Arrange.
41
+ const setValue = jest.fn();
42
+ const options = [
43
+ { label: 'Option 1', value: 'value1' },
44
+ { label: 'Option 2', value: 'value2', disabled: true },
45
+ { label: 'Option 3', value: 'value3', disabled: false },
46
+ ];
47
+
48
+ // Act.
49
+ render(
50
+ <ControlContext.Provider value={ { setValue, value: '', bind: 'select' } }>
51
+ <SelectControl options={ options } />
52
+ </ControlContext.Provider>
53
+ );
54
+
55
+ const select = screen.getByRole( 'combobox' );
56
+
57
+ // Act.
58
+ fireEvent.mouseDown( select );
59
+
60
+ const option2 = screen.getByText( 'Option 2' );
61
+ const option3 = screen.getByText( 'Option 3' );
62
+
63
+ // Assert.
64
+ expect( option2 ).toHaveAttribute( 'aria-disabled', 'true' );
65
+ expect( option3 ).not.toHaveAttribute( 'aria-disabled', 'true' );
66
+ } );
67
+ } );
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { TextControl } from '../text-control';
4
+ import { ControlContext } from '../../control-context';
5
+
6
+ describe( 'TextControl', () => {
7
+ it( 'should pass the updated payload when input value changes', () => {
8
+ // Arrange.
9
+ const setValue = jest.fn();
10
+
11
+ // Act.
12
+ render(
13
+ <ControlContext.Provider value={ { setValue, value: 'Hi', bind: 'text' } }>
14
+ <TextControl placeholder="type text here" />
15
+ </ControlContext.Provider>
16
+ );
17
+
18
+ const input = screen.getByRole( 'textbox' );
19
+
20
+ // Assert.
21
+ expect( input ).toHaveValue( 'Hi' );
22
+
23
+ // Act.
24
+ fireEvent.input( input, { target: { value: 'Cool Heading!' } } );
25
+
26
+ // Assert.
27
+ expect( setValue ).toHaveBeenCalledWith( 'Cool Heading!' );
28
+ } );
29
+ } );
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { MenuItem, Select, SelectChangeEvent } from '@elementor/ui';
3
+ import { useControl } from '../control-context';
4
+ import { PropValue } from '../../../types';
5
+
6
+ export type SelectControlProps<T> = {
7
+ options: Array<{ label: string; value: T; disabled?: boolean }>
8
+ };
9
+
10
+ export const SelectControl = <T extends PropValue>( { options }: SelectControlProps<T> ) => {
11
+ const { value, setValue } = useControl<T>();
12
+
13
+ const handleChange = ( event: SelectChangeEvent<T> ) => {
14
+ setValue( event.target.value as T );
15
+ };
16
+
17
+ return (
18
+ <Select size="tiny" value={ value ?? '' } onChange={ handleChange }>
19
+ { options.map( ( option ) => (
20
+ <MenuItem key={ option.value } value={ option.value } disabled={ option.disabled }>
21
+ { option.label }
22
+ </MenuItem>
23
+ ) ) }
24
+ </Select>
25
+ );
26
+ };
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import { TextareaAutosize } from '@elementor/ui';
3
+ import { useControl } from '../control-context';
4
+
5
+ export type TextControlProps = {
6
+ placeholder?: string;
7
+ }
8
+
9
+ export const TextControl = ( { placeholder }: TextControlProps ) => {
10
+ const { value, setValue } = useControl<string>( '' );
11
+
12
+ const handleChange = ( event: React.ChangeEvent<HTMLInputElement> ) => {
13
+ setValue( event.target.value );
14
+ };
15
+
16
+ return (
17
+ <TextareaAutosize size="tiny" minRows={ 3 }value={ value } onChange={ handleChange } placeholder={ placeholder } />
18
+ );
19
+ };
@@ -0,0 +1,53 @@
1
+ import * as React from 'react';
2
+ import { ControlContext } from './control-context';
3
+ import { Stack, Typography } from '@elementor/ui';
4
+ import { updateSettings } from '../../sync/update-settings';
5
+ import { useWidgetSettings } from '../../hooks/use-widget-settings';
6
+ import { PropKey, PropValue } from '../../types';
7
+
8
+ type Props = {
9
+ bind: PropKey;
10
+ children: React.ReactNode;
11
+ elementID: Element['id'];
12
+ }
13
+
14
+ const SettingsControl = ( { bind, children, elementID }: Props ) => {
15
+ const value = useWidgetSettings( { id: elementID, bind } );
16
+
17
+ const setValue = ( newValue: PropValue ) => {
18
+ updateSettings( {
19
+ id: elementID,
20
+ props: {
21
+ [ bind ]: newValue,
22
+ },
23
+ } );
24
+ };
25
+
26
+ return (
27
+ <ControlContext.Provider value={ { setValue, value, bind } }>
28
+ { children }
29
+ </ControlContext.Provider>
30
+ );
31
+ };
32
+
33
+ const Label = ( { children }: { children: React.ReactNode } ) => {
34
+ return <Typography component="label" variant="caption">{ children }</Typography>;
35
+ };
36
+
37
+ const Container = ( { children }: { children: React.ReactNode} ) => (
38
+ <Stack
39
+ spacing={ 1 }
40
+ flexDirection="row"
41
+ alignItems="center"
42
+ justifyContent="space-between"
43
+ flexWrap="wrap"
44
+ sx={ { px: 2 } }
45
+ >
46
+ { children }
47
+ </Stack>
48
+ );
49
+
50
+ SettingsControl.Label = Label;
51
+ SettingsControl.Container = Container;
52
+
53
+ export { SettingsControl };
@@ -1,15 +1,18 @@
1
1
  import * as React from 'react';
2
- import {
3
- Panel,
4
- PanelBody,
5
- PanelHeader,
6
- PanelHeaderTitle,
7
- } from '@elementor/editor-panels';
8
2
  import { __ } from '@wordpress/i18n';
9
3
  import useSelectedElements from '../hooks/use-selected-elements';
10
4
  import useElementType from '../hooks/use-element-type';
5
+ import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
6
+ import { SelectControl } from './controls/control-types/select-control';
7
+ import { TextControl } from './controls/control-types/text-control';
8
+ import { SettingsControl } from '../components/controls/settings-control';
9
+ import { Stack } from '@elementor/ui';
11
10
  import { ElementContext } from '../contexts/settings-controls';
12
- import { Box, Typography } from '@elementor/ui';
11
+
12
+ const controlTypes = {
13
+ select: SelectControl,
14
+ text: TextControl,
15
+ };
13
16
 
14
17
  export const EditingPanel = () => {
15
18
  const elements = useSelectedElements();
@@ -32,11 +35,31 @@ export const EditingPanel = () => {
32
35
  </PanelHeader>
33
36
  <PanelBody>
34
37
  <ElementContext element={ selectedElement }>
35
- <Box padding={ 2 }>
36
- <Typography>
37
- Here should be the controls.
38
- </Typography>
39
- </Box>
38
+ <Stack spacing={ 2 }>
39
+ {
40
+ elementType.controls.map( ( control ) => {
41
+ if ( control.type === 'control' ) {
42
+ const ControlComponent = controlTypes[ control.value.type as keyof typeof controlTypes ];
43
+
44
+ if ( ! ControlComponent ) {
45
+ return null;
46
+ }
47
+
48
+ return (
49
+ <SettingsControl key={ control.value.bind } bind={ control.value.bind } elementID={ elements[ 0 ].id }>
50
+ <SettingsControl.Container>
51
+ <SettingsControl.Label>{ control.value.label }</SettingsControl.Label>
52
+ { /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ }
53
+ <ControlComponent { ...control.value.props as any } />
54
+ </SettingsControl.Container>
55
+ </SettingsControl>
56
+ );
57
+ }
58
+
59
+ return null;
60
+ } )
61
+ }
62
+ </Stack>
40
63
  </ElementContext>
41
64
  </PanelBody>
42
65
  </Panel>
@@ -0,0 +1,81 @@
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { useWidgetSettings } from '../use-widget-settings';
3
+ import { ExtendedWindow } from '../../sync/types';
4
+ import { mockV1Element } from '../../__tests__/utils';
5
+ import { dispatchCommandAfter } from 'test-utils';
6
+ import getContainer from '../../sync/get-container';
7
+
8
+ jest.mock( '../../sync/get-container' );
9
+
10
+ describe( 'useWidgetSettings', () => {
11
+ beforeEach( () => {
12
+ const extendedWindow = window as unknown as ExtendedWindow;
13
+
14
+ extendedWindow.elementor = {
15
+ widgetsCache: {
16
+ 'v1-heading': {
17
+ controls: [],
18
+ title: 'Heading',
19
+ },
20
+ 'v2-heading': {
21
+ controls: [],
22
+ atomic_controls: [],
23
+ title: 'Heading',
24
+ },
25
+ 'v2-container': {
26
+ controls: [],
27
+ atomic_controls: [],
28
+ title: 'Container',
29
+ },
30
+ },
31
+ };
32
+ } );
33
+
34
+ it( 'should return the value of the setting', () => {
35
+ // Arrange.
36
+ const bind = 'title';
37
+ const element = { id: 'element-id' };
38
+ jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: { [ bind ]: 'Hello, World!' } } ) );
39
+
40
+ // Act.
41
+ const result = renderHook( () => useWidgetSettings( { id: element.id, bind } ) ).result.current;
42
+
43
+ // Assert.
44
+ expect( result ).toEqual( 'Hello, World!' );
45
+ } );
46
+
47
+ it( 'should return null if the setting is not found', () => {
48
+ // Arrange.
49
+ const bind = 'title';
50
+ const element = { id: 'element-id' };
51
+ jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: {} } ) );
52
+
53
+ // Act.
54
+ const result = renderHook( () => useWidgetSettings( { id: element.id, bind } ) ).result.current;
55
+
56
+ // Assert.
57
+ expect( result ).toEqual( null );
58
+ } );
59
+
60
+ it( 'should update the state value on settings change', () => {
61
+ // Arrange.
62
+ const bind = 'title';
63
+ const element = { id: 'element-id' };
64
+ jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: { [ bind ]: 'Hello, World!' } } ) );
65
+
66
+ // Act.
67
+ const { result } = renderHook( () => useWidgetSettings( { id: element.id, bind } ) );
68
+
69
+ // Assert.
70
+ expect( result.current ).toEqual( 'Hello, World!' );
71
+
72
+ // Act.
73
+ act( () => {
74
+ jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: { [ bind ]: 'Goodbye, World!' } } ) );
75
+ dispatchCommandAfter( 'document/elements/settings' );
76
+ } );
77
+
78
+ // Assert.
79
+ expect( result.current ).toEqual( 'Goodbye, World!' );
80
+ } );
81
+ } );
@@ -0,0 +1,16 @@
1
+ import { commandEndEvent, __privateUseListenTo as useListenTo } from '@elementor/editor-v1-adapters';
2
+ import { PropValue } from '../types';
3
+ import getContainer from '../sync/get-container';
4
+
5
+ export const useWidgetSettings = ( { id, bind }: { id: string; bind: string } ): PropValue => {
6
+ return useListenTo(
7
+ commandEndEvent( 'document/elements/settings' ),
8
+ () => {
9
+ const container = getContainer( id );
10
+ const value = container?.settings?.get( bind ) ?? null;
11
+
12
+ return value;
13
+ },
14
+ []
15
+ );
16
+ };
@@ -0,0 +1,40 @@
1
+ import getContainer from '../get-container';
2
+ import { ExtendedWindow } from '../types';
3
+
4
+ describe( 'getContainer', () => {
5
+ const mockGetContainer = jest.fn();
6
+
7
+ beforeEach( () => {
8
+ const extendedWindow = window as unknown as ExtendedWindow;
9
+
10
+ extendedWindow.elementor = {
11
+ getContainer: mockGetContainer,
12
+ };
13
+ } );
14
+
15
+ it( 'should return the container', () => {
16
+ // Arrange.
17
+ const elementID = '1-heading';
18
+ const container = { id: elementID };
19
+ mockGetContainer.mockReturnValue( container );
20
+
21
+ // Act.
22
+ const result = getContainer( elementID );
23
+
24
+ // Assert.
25
+ expect( result ).toBe( container );
26
+ } );
27
+
28
+ it( 'should return null if the container is not found', () => {
29
+ // Arrange.
30
+ const elementID = '1-heading';
31
+ const container = undefined;
32
+ mockGetContainer.mockReturnValue( container );
33
+
34
+ // Act.
35
+ const result = getContainer( elementID );
36
+
37
+ // Assert.
38
+ expect( result ).toBe( null );
39
+ } );
40
+ } );
@@ -41,7 +41,7 @@ describe( 'shouldUseV2Panel', () => {
41
41
 
42
42
  it( 'should return true for v2 element', () => {
43
43
  // Arrange.
44
- getElements.mockReturnValue( [ mockV1Element( { widgetType: 'v2-heading' } ) ] );
44
+ getElements.mockReturnValue( [ mockV1Element( { model: { widgetType: 'v2-heading' } } ) ] );
45
45
 
46
46
  // Assert.
47
47
  expect( shouldUseV2Panel() ).toBe( true );
@@ -49,7 +49,7 @@ describe( 'shouldUseV2Panel', () => {
49
49
 
50
50
  it( 'should return true for v2 element', () => {
51
51
  // Arrange.
52
- getElements.mockReturnValue( [ mockV1Element( { elType: 'v2-container' } ) ] );
52
+ getElements.mockReturnValue( [ mockV1Element( { model: { elType: 'v2-container' } } ) ] );
53
53
 
54
54
  // Assert.
55
55
  expect( shouldUseV2Panel() ).toBe( true );
@@ -57,7 +57,7 @@ describe( 'shouldUseV2Panel', () => {
57
57
 
58
58
  it( 'should return false for v1 element', () => {
59
59
  // Arrange.
60
- getElements.mockReturnValue( [ mockV1Element( { widgetType: 'v1-heading' } ) ] );
60
+ getElements.mockReturnValue( [ mockV1Element( { model: { widgetType: 'v1-heading' } } ) ] );
61
61
 
62
62
  // Assert.
63
63
  expect( shouldUseV2Panel() ).toBe( false );
@@ -65,7 +65,7 @@ describe( 'shouldUseV2Panel', () => {
65
65
 
66
66
  it( 'should return false for non-existing element', () => {
67
67
  // Arrange.
68
- getElements.mockReturnValue( [ mockV1Element( { widgetType: 'non-existing' } ) ] );
68
+ getElements.mockReturnValue( [ mockV1Element( { model: { widgetType: 'non-existing' } } ) ] );
69
69
 
70
70
  // Assert.
71
71
  expect( shouldUseV2Panel() ).toBe( false );
@@ -74,8 +74,8 @@ describe( 'shouldUseV2Panel', () => {
74
74
  it( 'should return false if there is more than 1 selected element with mixed versions', () => {
75
75
  // Arrange.
76
76
  getElements.mockReturnValue( [
77
- mockV1Element( { widgetType: 'v2-heading' } ),
78
- mockV1Element( { widgetType: 'v1-heading' } ),
77
+ mockV1Element( { model: { widgetType: 'v2-heading' } } ),
78
+ mockV1Element( { model: { widgetType: 'v1-heading' } } ),
79
79
  ] );
80
80
 
81
81
  // Assert.
@@ -85,8 +85,8 @@ describe( 'shouldUseV2Panel', () => {
85
85
  it( 'should return false if there is more than 1 selected element with same version', () => {
86
86
  // Arrange.
87
87
  getElements.mockReturnValue( [
88
- mockV1Element( { widgetType: 'v2-heading' } ),
89
- mockV1Element( { widgetType: 'v2-container' } ),
88
+ mockV1Element( { model: { widgetType: 'v2-heading' } } ),
89
+ mockV1Element( { model: { widgetType: 'v2-container' } } ),
90
90
  ] );
91
91
 
92
92
  // Assert.
@@ -0,0 +1,8 @@
1
+ import { ExtendedWindow } from './types';
2
+
3
+ export default function getContainer( id: string ) {
4
+ const extendedWindow = window as unknown as ExtendedWindow;
5
+ const container = extendedWindow.elementor?.getContainer?.( id );
6
+
7
+ return container ?? null;
8
+ }
package/src/sync/types.ts CHANGED
@@ -1,18 +1,22 @@
1
+ import { AtomicControl, PropValue } from '../types';
2
+
1
3
  export type ExtendedWindow = Window & {
2
4
  elementor?: {
3
5
  selection?: {
4
6
  getElements: () => V1Element[];
5
7
  },
6
8
  widgetsCache?: Record<string, {
7
- atomic_controls?: unknown[],
9
+ atomic_controls?: AtomicControl[],
8
10
  controls: object,
9
11
  title: string,
10
- }>
12
+ }>,
13
+ getContainer?: ( id: string ) => V1Element
11
14
  }
12
15
  }
13
16
 
14
17
  export type V1Element = {
15
- model: V1ElementModel<V1ElementModelProps>
18
+ model: V1Model<V1ElementModelProps>,
19
+ settings?: V1Model<V1ElementSettingsProps>,
16
20
  }
17
21
 
18
22
  export type V1ElementModelProps = {
@@ -21,6 +25,8 @@ export type V1ElementModelProps = {
21
25
  id: string;
22
26
  }
23
27
 
24
- type V1ElementModel<T> = {
28
+ export type V1ElementSettingsProps = Record<string, PropValue>;
29
+
30
+ type V1Model<T> = {
25
31
  get: <K extends keyof T>( key: K ) => T[K],
26
32
  }
@@ -0,0 +1,14 @@
1
+ import { __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters';
2
+ import { Props } from '../types';
3
+ import getContainer from './get-container';
4
+
5
+ export const updateSettings = ( { id, props }: { id: string, props: Props } ) => {
6
+ const container = getContainer( id );
7
+
8
+ runCommand( 'document/elements/settings', {
9
+ container,
10
+ settings: {
11
+ ...props,
12
+ },
13
+ } );
14
+ };
package/src/types.ts CHANGED
@@ -5,6 +5,34 @@ export type Element = {
5
5
 
6
6
  export type ElementType = {
7
7
  key: string;
8
- controls: unknown[];
8
+ controls: AtomicControl[];
9
9
  title: string;
10
10
  }
11
+
12
+ export type AtomicControl = {
13
+ type: 'control',
14
+ value: {
15
+ bind: string,
16
+ label: string,
17
+ description?: string,
18
+ type: string,
19
+ props: Record<string, unknown>,
20
+ },
21
+ }
22
+
23
+ type MaybeArray<T> = T | T[];
24
+
25
+ type TransformablePropValue = {
26
+ $$type: string,
27
+ value: unknown,
28
+ }
29
+
30
+ export type PlainPropValue = MaybeArray<string | number | boolean | object | null | undefined>;
31
+
32
+ export type PropValue = PlainPropValue | TransformablePropValue;
33
+
34
+ export type PropKey = string;
35
+
36
+ export type Props = Record<PropKey, PropValue>;
37
+
38
+ export type PlainProps = Record<PropKey, PlainPropValue>;