diff --git a/addons/html_builder/static/src/builder/builder_actions.js b/addons/html_builder/static/src/builder/builder_actions.js
index 30bfc47d9d315..1313f67a94660 100644
--- a/addons/html_builder/static/src/builder/builder_actions.js
+++ b/addons/html_builder/static/src/builder/builder_actions.js
@@ -1,13 +1,46 @@
import { registry } from "@web/core/registry";
-registry.category("website-builder-actions").add("setClass", {
- isActive: ({ editingElement, params }) => {
- return editingElement.classList.contains(params);
+registry.category("website-builder-actions").add("classAction", {
+ isActive: ({ editingElement, param: className }) => {
+ return editingElement.classList.contains(className);
},
- apply: ({ editingElement, params }) => {
- editingElement.classList.add(params);
+ apply: ({ editingElement, param: className, value }) => {
+ editingElement.classList.add(className);
},
- clean: ({ editingElement, params }) => {
- editingElement.classList.remove(params);
+ clean: ({ editingElement, param: className, value }) => {
+ editingElement.classList.remove(className);
+ },
+});
+
+const styleMap = {
+ borderWidth: {
+ getValue: (editingElement) => {
+ return parseInt(
+ getComputedStyle(editingElement).getPropertyValue("border-width")
+ ).toString();
+ },
+ apply: (editingElement, value) => {
+ const parsedValue = parseInt(value);
+ const hasBorderClass = editingElement.classList.contains("border");
+ if (!parsedValue || parsedValue < 0) {
+ if (hasBorderClass) {
+ editingElement.classList.remove("border");
+ }
+ } else {
+ if (!hasBorderClass) {
+ editingElement.classList.add("border");
+ }
+ }
+ editingElement.style.setProperty("border-width", `${parsedValue}px`, "important");
+ },
+ },
+};
+
+registry.category("website-builder-actions").add("styleAction", {
+ getValue: ({ editingElement, param: styleName }) => {
+ return styleMap[styleName]?.getValue(editingElement);
+ },
+ apply: ({ editingElement, param: styleName, value }) => {
+ styleMap[styleName]?.apply(editingElement, value);
},
});
diff --git a/addons/html_builder/static/src/builder/builder_helpers.js b/addons/html_builder/static/src/builder/builder_helpers.js
index aad4666ad6978..59623c709dd3a 100644
--- a/addons/html_builder/static/src/builder/builder_helpers.js
+++ b/addons/html_builder/static/src/builder/builder_helpers.js
@@ -1,4 +1,4 @@
-import { useComponent, useState } from "@odoo/owl";
+import { Component, useComponent, useState, useSubEnv, xml } from "@odoo/owl";
import { useBus } from "@web/core/utils/hooks";
export function useDomState(getState) {
@@ -9,3 +9,42 @@ export function useDomState(getState) {
});
return state;
}
+
+export class WithSubEnv extends Component {
+ static template = xml``;
+ static props = {
+ env: Object,
+ slots: Object,
+ };
+
+ setup() {
+ useSubEnv(this.props.env);
+ }
+}
+
+export function useWeComponent() {
+ const comp = useComponent();
+ if (comp.props.applyTo) {
+ // todo: react to the change of applyTo
+ // todo: make sure that the code that read env.editingElement properly react to the change if editingElement changes
+ // todo: make sure that if there is an action that changes the structure of the dom, the applyTo is re-calculed.
+ useSubEnv({
+ editingElement: comp.env.editingElement.querySelector(comp.props.applyTo),
+ });
+ }
+}
+
+export const basicContainerWeWidgetProps = {
+ applyTo: { type: String, optional: true },
+ // preview: { type: Boolean, optional: true },
+ // reloadPage: { type: Boolean, optional: true },
+
+ action: { type: String, optional: true },
+ actionParam: { validate: () => true, optional: true },
+
+ // Shorthand actions.
+ classAction: { type: String, optional: true },
+ attributeAction: { type: String, optional: true },
+ dataAttributeAction: { type: String, optional: true },
+ styleAction: { type: String, optional: true },
+};
diff --git a/addons/html_builder/static/src/builder/components/Button.js b/addons/html_builder/static/src/builder/components/Button.js
index 564bec97acae6..f5507f6248453 100644
--- a/addons/html_builder/static/src/builder/components/Button.js
+++ b/addons/html_builder/static/src/builder/components/Button.js
@@ -9,7 +9,7 @@ export class Button extends Component {
iconImg: { type: String, optional: true },
iconImgAlt: { type: String, optional: true },
onClick: Function,
- isActive: Function,
+ isActive: { Function, optional: true },
};
setup() {
diff --git a/addons/html_builder/static/src/builder/components/ButtonGroup.js b/addons/html_builder/static/src/builder/components/ButtonGroup.js
index be2acbad3ce7f..21744991fb2cf 100644
--- a/addons/html_builder/static/src/builder/components/ButtonGroup.js
+++ b/addons/html_builder/static/src/builder/components/ButtonGroup.js
@@ -1,15 +1,15 @@
import { Component, EventBus, useSubEnv } from "@odoo/owl";
+import { basicContainerWeWidgetProps, useWeComponent } from "../builder_helpers";
export class ButtonGroup extends Component {
static template = "html_builder.ButtonGroup";
static props = {
- activeState: { type: Object, optional: true },
- isActive: { type: Boolean, optional: true },
- onClick: { type: Function, optional: true },
+ ...basicContainerWeWidgetProps,
slots: { type: Object, optional: true },
};
setup() {
+ useWeComponent();
const bus = new EventBus();
useSubEnv({
buttonGroupBus: bus,
diff --git a/addons/html_builder/static/src/builder/components/NumberInput.js b/addons/html_builder/static/src/builder/components/NumberInput.js
new file mode 100644
index 0000000000000..86db096b9b8e5
--- /dev/null
+++ b/addons/html_builder/static/src/builder/components/NumberInput.js
@@ -0,0 +1,61 @@
+import { Component, useState } from "@odoo/owl";
+import { registry } from "@web/core/registry";
+import { basicContainerWeWidgetProps, useWeComponent } from "../builder_helpers";
+
+const actionsRegistry = registry.category("website-builder-actions");
+
+export class NumberInput extends Component {
+ static template = "html_builder.NumberInput";
+ static props = {
+ ...basicContainerWeWidgetProps,
+ unit: { type: String, optional: true },
+ };
+
+ setup() {
+ useWeComponent();
+ this.state = useState(this.getState());
+ this.applyValue = this.env.editor.shared.makePreviewableOperation((value) => {
+ for (const [actionId, actionParam] of this.getActions()) {
+ actionsRegistry.get(actionId).apply({
+ editingElement: this.env.editingElement,
+ param: actionParam,
+ value,
+ });
+ }
+ });
+ }
+ getState() {
+ const [actionId, actionParam] = this.getActions()[0];
+ return {
+ value: actionsRegistry.get(actionId).getValue({
+ editingElement: this.env.editingElement,
+ param: actionParam,
+ }),
+ };
+ }
+ getActions() {
+ const actions = [];
+ if (this.props.classAction) {
+ actions.push(["classAction", this.props.classAction]);
+ }
+ if (this.props.attributeAction) {
+ actions.push(["attributeAction", this.props.attributeAction]);
+ }
+ if (this.props.dataAttributeAction) {
+ actions.push(["dataAttributeAction", this.props.dataAttributeAction]);
+ }
+ if (this.props.styleAction) {
+ actions.push(["styleAction", this.props.styleAction]);
+ }
+ if (this.props.action) {
+ actions.push([this.props.action, this.props.actionParam]);
+ }
+ return actions;
+ }
+ onChange(e) {
+ this.applyValue.commit(e.target.value);
+ }
+ onInput(e) {
+ this.applyValue.preview(e.target.value);
+ }
+}
diff --git a/addons/html_builder/static/src/builder/components/NumberInput.xml b/addons/html_builder/static/src/builder/components/NumberInput.xml
new file mode 100644
index 0000000000000..9b27896f2c225
--- /dev/null
+++ b/addons/html_builder/static/src/builder/components/NumberInput.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/html_builder/static/src/builder/components/WeButton.js b/addons/html_builder/static/src/builder/components/WeButton.js
index bc76070cb1b37..7949855560c6e 100644
--- a/addons/html_builder/static/src/builder/components/WeButton.js
+++ b/addons/html_builder/static/src/builder/components/WeButton.js
@@ -1,32 +1,47 @@
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { useBus } from "@web/core/utils/hooks";
-import { useDomState } from "../builder_helpers";
+import { basicContainerWeWidgetProps, useDomState, useWeComponent } from "../builder_helpers";
const actionsRegistry = registry.category("website-builder-actions");
export class WeButton extends Component {
static template = "html_builder.WeButton";
static props = {
- actions: Object,
+ ...basicContainerWeWidgetProps,
+
title: { type: String, optional: true },
label: { type: String, optional: true },
iconImg: { type: String, optional: true },
iconImgAlt: { type: String, optional: true },
- applyTo: { type: Function, optional: true },
+
+ actionValue: {
+ type: [Boolean, String, Number, { type: Array, element: [Boolean, String, Number] }],
+ optional: true,
+ },
+
+ // Shorthand actions values.
+ classActionValue: { type: [String, Array], optional: true },
+ attributeActionValue: { type: [String, Array], optional: true },
+ dataAttributeActionValue: { type: [String, Array], optional: true },
+ styleActionValue: { type: [String, Array], optional: true },
+
+ slots: { type: Object, optional: true },
};
setup() {
+ useWeComponent();
this.state = useDomState(() => ({
isActive: this.isActive(),
}));
if (this.env.buttonGroupBus) {
useBus(this.env.buttonGroupBus, "BEFORE_CALL_ACTIONS", () => {
- for (const [actionId, actionParams] of Object.entries(this.props.actions)) {
+ for (const [actionId, actionParam, actionValue] of this.getActions()) {
actionsRegistry.get(actionId).clean({
- editingElement: this.getEditedElement(),
- params: actionParams,
+ editingElement: this.env.editingElement,
+ param: actionParam,
+ value: actionValue,
});
}
});
@@ -37,13 +52,41 @@ export class WeButton extends Component {
}
callActions() {
this.env.buttonGroupBus?.trigger("BEFORE_CALL_ACTIONS");
- for (const [actionId, actionParams] of Object.entries(this.props.actions)) {
+ for (const [actionId, actionParam, actionValue] of this.getActions()) {
actionsRegistry.get(actionId).apply({
- editingElement: this.getEditedElement(),
- params: actionParams,
+ editingElement: this.env.editingElement,
+ param: actionParam,
+ value: actionValue,
});
}
}
+ getActions() {
+ const actions = [];
+ if (this.props.classAction) {
+ actions.push(["classAction", this.props.classAction, this.props.classActionValue]);
+ }
+ if (this.props.attributeAction) {
+ actions.push([
+ "attributeAction",
+ this.props.attributeAction,
+ this.props.attributeActionValue,
+ ]);
+ }
+ if (this.props.dataAttributeAction) {
+ actions.push([
+ "dataAttributeAction",
+ this.props.dataAttributeAction,
+ this.props.dataAttributeActionValue,
+ ]);
+ }
+ if (this.props.styleAction) {
+ actions.push(["styleAction", this.props.styleAction, this.props.styleActionValue]);
+ }
+ if (this.props.action) {
+ actions.push([this.props.action, this.props.actionParam, this.props.actionValue]);
+ }
+ return actions;
+ }
onClick() {
this.call.commit();
}
@@ -54,16 +97,12 @@ export class WeButton extends Component {
this.call.revert();
}
isActive() {
- return Object.entries(this.props.actions).every(([actionId, actionParams]) => {
+ return this.getActions().every(([actionId, actionParam, actionValue]) => {
return actionsRegistry.get(actionId).isActive({
- editingElement: this.getEditedElement(),
- params: actionParams,
+ editingElement: this.env.editingElement,
+ param: actionParam,
+ value: actionValue,
});
});
}
- getEditedElement() {
- return this.props.applyTo
- ? this.env.editingElement.querySelector(this.props.applyTo)
- : this.env.editingElement;
- }
}
diff --git a/addons/html_builder/static/src/builder/components/WeButton.xml b/addons/html_builder/static/src/builder/components/WeButton.xml
index c81c47b0ad90c..0daf7259a3818 100644
--- a/addons/html_builder/static/src/builder/components/WeButton.xml
+++ b/addons/html_builder/static/src/builder/components/WeButton.xml
@@ -10,6 +10,7 @@
t-on-mouseleave="() => this.onMouseleave?.(props.id)">
+
diff --git a/addons/html_builder/static/src/builder/components/defaultComponents.js b/addons/html_builder/static/src/builder/components/defaultComponents.js
index 91fbf192c9b9f..a423605a7ac8d 100644
--- a/addons/html_builder/static/src/builder/components/defaultComponents.js
+++ b/addons/html_builder/static/src/builder/components/defaultComponents.js
@@ -4,6 +4,7 @@ import { Dropdown } from "@web/core/dropdown/dropdown";
import { ToolboxRow } from "./ToolboxRow";
import { WeButton } from "./WeButton";
import { Button } from "./Button";
+import { NumberInput } from "./NumberInput";
export const defaultOptionComponents = {
ToolboxRow,
@@ -12,4 +13,5 @@ export const defaultOptionComponents = {
ButtonGroup,
WeButton,
Button,
+ NumberInput,
};
diff --git a/addons/html_builder/static/src/builder/components/options/AddElementOption.xml b/addons/html_builder/static/src/builder/components/options/AddElementOption.xml
index 517ec29621f00..c7a442af668b1 100644
--- a/addons/html_builder/static/src/builder/components/options/AddElementOption.xml
+++ b/addons/html_builder/static/src/builder/components/options/AddElementOption.xml
@@ -4,9 +4,9 @@
-
-
-
+
+
+
diff --git a/addons/html_builder/static/src/builder/components/options/BorderOption.js b/addons/html_builder/static/src/builder/components/options/BorderOption.js
new file mode 100644
index 0000000000000..47cc53434385b
--- /dev/null
+++ b/addons/html_builder/static/src/builder/components/options/BorderOption.js
@@ -0,0 +1,9 @@
+import { Component } from "@odoo/owl";
+import { defaultOptionComponents } from "../defaultComponents";
+
+export class BorderOption extends Component {
+ static template = "html_builder.BorderOption";
+ static components = {
+ ...defaultOptionComponents,
+ };
+}
diff --git a/addons/html_builder/static/src/builder/components/options/BorderOption.xml b/addons/html_builder/static/src/builder/components/options/BorderOption.xml
new file mode 100644
index 0000000000000..479b38156d70e
--- /dev/null
+++ b/addons/html_builder/static/src/builder/components/options/BorderOption.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/html_builder/static/src/builder/components/options/LayoutOption.xml b/addons/html_builder/static/src/builder/components/options/LayoutOption.xml
index 22ae19aead6df..f6f1b97af74c0 100644
--- a/addons/html_builder/static/src/builder/components/options/LayoutOption.xml
+++ b/addons/html_builder/static/src/builder/components/options/LayoutOption.xml
@@ -30,11 +30,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.js b/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.js
index 5e46194317b63..7043ef70fcb42 100644
--- a/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.js
+++ b/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.js
@@ -1,11 +1,13 @@
import { registry } from "@web/core/registry";
import { ElementToolboxContainer } from "../../components/ElementToolboxContainer";
import { Component } from "@odoo/owl";
+import { BorderOption } from "../../components/options/BorderOption";
class RowDivElementToolbox extends Component {
static template = "html_builder.RowDivElementToolbox";
static components = {
ElementToolboxContainer,
+ BorderOption,
};
}
diff --git a/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.xml b/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.xml
index 8321236240f68..25182be703e90 100644
--- a/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.xml
+++ b/addons/html_builder/static/src/builder/snippets/row-div/RowDivElementToolbox.xml
@@ -3,7 +3,7 @@
- mytool
+
diff --git a/addons/html_builder/static/src/builder/snippets/section/SectionToolbox.js b/addons/html_builder/static/src/builder/snippets/section/SectionToolbox.js
index a16c21b2acc1b..27b3028510a79 100644
--- a/addons/html_builder/static/src/builder/snippets/section/SectionToolbox.js
+++ b/addons/html_builder/static/src/builder/snippets/section/SectionToolbox.js
@@ -1,6 +1,6 @@
import { registry } from "@web/core/registry";
import { ElementToolboxContainer } from "../../components/ElementToolboxContainer";
-import { Component, useSubEnv } from "@odoo/owl";
+import { Component } from "@odoo/owl";
import { LayoutOption } from "../../components/options/LayoutOption";
class SectionToolbox extends Component {
@@ -9,14 +9,6 @@ class SectionToolbox extends Component {
ElementToolboxContainer,
LayoutOption,
};
- static props = {
- editingElement: Object,
- };
- setup() {
- useSubEnv({
- editingElement: this.props.editingElement,
- });
- }
}
registry.category("sidebar-element-toolbox").add("SectionToolbox", {
diff --git a/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.js b/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.js
index 0bf39e6611341..a5d0f47c48b74 100644
--- a/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.js
+++ b/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.js
@@ -1,9 +1,10 @@
import { Component, useSubEnv } from "@odoo/owl";
import { Toolbar } from "@html_editor/main/toolbar/toolbar";
+import { WithSubEnv } from "../builder_helpers";
export class CustomizeTab extends Component {
static template = "html_builder.CustomizeTab";
- static components = { Toolbar };
+ static components = { Toolbar, WithSubEnv };
static props = {
editor: { type: Object },
selectedToolboxes: { type: Object },
diff --git a/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.xml b/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.xml
index 0c0f3fc7f79e6..e619cc50b431a 100644
--- a/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.xml
+++ b/addons/html_builder/static/src/builder/snippets_menu_tabs/customize_tab.xml
@@ -9,7 +9,9 @@
-
+
+
+