Skip to content

Commit cff0a3e

Browse files
agau-odoobso-odoo
authored andcommitted
s_donation options
1 parent 470cf0f commit cff0a3e

File tree

3 files changed

+370
-0
lines changed

3 files changed

+370
-0
lines changed

addons/html_builder/static/src/website_builder/translate.inside.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ html[lang] > body.editor_enable [data-oe-translation-state] {
2828
pointer-events: none;
2929
}
3030
}
31+
32+
html[data-edit_translations="1"] {
33+
.o_translate_mode_hidden {
34+
display: none !important;
35+
}
36+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="website_payment.DonationOption">
5+
<BuilderRow label.translate="Recipient Email">
6+
<BuilderTextInput dataAttributeAction="'donationEmail'" />
7+
</BuilderRow>
8+
<BuilderRow label.translate="Display Options" preview="false">
9+
<BuilderCheckbox id="'display_options_opt'" action="'toggleDisplayOptions'" />
10+
</BuilderRow>
11+
<BuilderRow t-if="!isActiveItem('no_input_opt')"
12+
label.translate="Pre-filled Options" preview="false">
13+
<BuilderCheckbox id="'pre_filled_opt'" action="'togglePrefilledOptions'" />
14+
</BuilderRow>
15+
<t t-if="isActiveItem('no_input_opt') || isActiveItem('pre_filled_opt')">
16+
<t t-set="translatedDefaultDescription">Add a description here</t>
17+
<BuilderList
18+
action="'setPrefilledOptions'"
19+
addItemTitle.translate="Add new pre-filled option"
20+
itemShape="{ value: 'number', description: 'text' }"
21+
default="{ value: '50', description: translatedDefaultDescription }"
22+
hiddenProperties="isActiveItem('pre_filled_descriptions_opt') ? [] : ['description']"
23+
/>
24+
<BuilderRow label.translate="Descriptions" level="1" preview="false">
25+
<BuilderCheckbox id="'pre_filled_descriptions_opt'" action="'toggleDescriptions'" />
26+
</BuilderRow>
27+
</t>
28+
<BuilderRow label.translate="Custom Amount" preview="false">
29+
<BuilderSelect action="'selectAmountInput'">
30+
<t t-if="!isActiveItem('display_options_opt') || isActiveItem('pre_filled_opt') || isActiveItem('no_input_opt')">
31+
<BuilderSelectItem id="'free_amount_opt'" actionParam="'freeAmount'">Input</BuilderSelectItem>
32+
</t>
33+
<t t-if="isActiveItem('display_options_opt')">
34+
<BuilderSelectItem id="'slider_opt'" actionParam="'slider'">Slider</BuilderSelectItem>
35+
</t>
36+
<t t-if="isActiveItem('no_input_opt') || isActiveItem('pre_filled_opt')">
37+
<BuilderSelectItem id="'no_input_opt'" actionParam="''">None</BuilderSelectItem>
38+
</t>
39+
</BuilderSelect>
40+
</BuilderRow>
41+
<BuilderRow t-if="!isActiveItem('no_input_opt')"
42+
label.translate="Minimum" level="1">
43+
<BuilderNumberInput step="1" action="'setMinimumAmount'"/>
44+
</BuilderRow>
45+
<t t-if="isActiveItem('slider_opt')">
46+
<BuilderRow label.translate="Maximum" level="1">
47+
<BuilderNumberInput step="1" action="'setMaximumAmount'"/>
48+
</BuilderRow>
49+
<BuilderRow label.translate="Step" level="1">
50+
<BuilderNumberInput step="1" action="'setSliderStep'"/>
51+
</BuilderRow>
52+
</t>
53+
<BuilderRow label.translate="Default Amount">
54+
<BuilderNumberInput step="1" default="25" dataAttributeAction="'defaultAmount'"/>
55+
</BuilderRow>
56+
</t>
57+
58+
<t t-name="website_payment.donation.descriptionTranslationInputs">
59+
<t t-foreach="descriptions" t-as="description" t-key="description_index">
60+
<input type="hidden" class="o_translatable_input_hidden d-block mb-1 w-100" name="donation_descriptions" t-att-value="description"/>
61+
</t>
62+
</t>
63+
64+
<t t-name="website_payment.donation.prefilledButtons">
65+
<div class="s_donation_prefilled_buttons mb-2">
66+
<t t-foreach="prefilled_buttons" t-as="prefilled_button_value" t-key="prefilled_button_value_index">
67+
<button class="s_donation_btn btn btn-outline-primary btn-lg mb-2 me-1 o_not_editable"
68+
type="button"
69+
contenteditable="false"
70+
t-att-data-donation-value="prefilled_button_value"
71+
t-esc="prefilled_button_value"/>
72+
</t>
73+
<span t-if="custom_input" class="s_donation_btn s_donation_custom_btn btn btn-outline-primary btn-lg mb-2 me-1">
74+
<input id="s_donation_amount_input" type="number" t-att-min="minimum_amount" class="" placeholder="Custom Amount" aria-label="Amount"/>
75+
</span>
76+
</div>
77+
</t>
78+
<t t-name="website_payment.donation.prefilledButtonsDescriptions">
79+
<div class="s_donation_prefilled_buttons my-4">
80+
<t t-foreach="prefilled_buttons" t-as="prefilled_button" t-key="prefilled_button_value_index">
81+
<div class="s_donation_btn_description d-sm-flex align-items-center my-3 o_not_editable o_translate_mode_hidden" contenteditable="false">
82+
<button class="s_donation_btn btn btn-outline-primary btn-lg me-3"
83+
type="button"
84+
t-att-data-donation-value="prefilled_button.value"
85+
t-esc="prefilled_button.value"/>
86+
<p class="s_donation_description mt-2 my-sm-auto text-muted fst-italic" t-esc="prefilled_button.description"></p>
87+
</div>
88+
</t>
89+
<div t-if="custom_input" class="d-sm-flex align-items-center my-3">
90+
<span class="s_donation_btn s_donation_custom_btn btn btn-outline-primary btn-lg">
91+
<input id="s_donation_amount_input" type="number" t-att-min="minimum_amount" placeholder="Custom Amount" aria-label="Amount"/>
92+
</span>
93+
</div>
94+
</div>
95+
</t>
96+
<t t-name="website_payment.donation.slider">
97+
<div class="s_donation_range_slider_wrap mb-2 position-relative">
98+
<label for="s_donation_range_slider">Choose Your Amount</label>
99+
<input type="range" class="form-range" t-att-min="minimum_amount" t-att-max="maximum_amount" t-att-step="slider_step" id="s_donation_range_slider" contenteditable="false"/>
100+
<output class="s_range_bubble" contenteditable="false">25</output>
101+
</div>
102+
</t>
103+
104+
</templates>
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import { SNIPPET_SPECIFIC } from "@html_builder/utils/option_sequence";
2+
import { Plugin } from "@html_editor/plugin";
3+
import { withSequence } from "@html_editor/utils/resource";
4+
import { _t } from "@web/core/l10n/translation";
5+
import { registry } from "@web/core/registry";
6+
import { renderToElement, renderToFragment } from "@web/core/utils/render";
7+
8+
class DonationOptionPlugin extends Plugin {
9+
static id = "DonationOption";
10+
resources = {
11+
builder_options: [
12+
withSequence(SNIPPET_SPECIFIC, {
13+
template: "website_payment.DonationOption",
14+
selector: ".s_donation",
15+
// TODO AGAU: remove when merging https://github.com/odoo-dev/odoo/pull/4240
16+
cleanForSave: this.cleanForSave.bind(this),
17+
}),
18+
],
19+
builder_actions: {
20+
toggleDisplayOptions: this.makeToggleDataAttributeAction(
21+
"displayOptions",
22+
this.toggleDisplayOptions.bind(this)
23+
),
24+
togglePrefilledOptions: this.makeToggleDataAttributeAction(
25+
"prefilledOptions",
26+
this.togglePrefilledOptions.bind(this)
27+
),
28+
toggleDescriptions: this.makeToggleDataAttributeAction(
29+
"descriptions",
30+
this.toggleDescriptions.bind(this)
31+
),
32+
setPrefilledOptions: {
33+
getValue: this.getPrefilledOptionsList.bind(this),
34+
apply: this.applyPrefilledOptionsList.bind(this),
35+
},
36+
selectAmountInput: {
37+
isApplied: this.isAmountInputApplied.bind(this),
38+
apply: this.setAmountInput.bind(this),
39+
},
40+
setMinimumAmount: {
41+
getValue: this.getMinimumAmount.bind(this),
42+
apply: this.setMinimumAmount.bind(this),
43+
},
44+
setMaximumAmount: {
45+
getValue: this.getMaximumAmount.bind(this),
46+
apply: this.setMaximumAmount.bind(this),
47+
},
48+
setSliderStep: {
49+
getValue: this.getSliderStep.bind(this),
50+
apply: this.setSliderStep.bind(this),
51+
},
52+
},
53+
};
54+
55+
makeToggleDataAttributeAction(dataAttributeName, toggleFunction) {
56+
return {
57+
isApplied: ({ editingElement }) => !!editingElement.dataset[dataAttributeName],
58+
apply: (obj, ...restArgs) => {
59+
const { editingElement } = obj;
60+
editingElement.dataset[dataAttributeName] = "true";
61+
toggleFunction({ ...obj, value: true }, ...restArgs);
62+
},
63+
clean: (obj, ...restArgs) => {
64+
const { editingElement } = obj;
65+
delete editingElement.dataset[dataAttributeName];
66+
toggleFunction({ ...obj, value: false }, ...restArgs);
67+
},
68+
};
69+
}
70+
71+
toggleDisplayOptions({ editingElement, value }) {
72+
if (!value && editingElement.dataset.customAmount === "slider") {
73+
editingElement.dataset.customAmount = "freeAmount";
74+
} else if (value && !editingElement.dataset.prefilledOptions) {
75+
editingElement.dataset.customAmount = "slider";
76+
}
77+
this.rebuildPrefilledOptions(editingElement);
78+
}
79+
80+
togglePrefilledOptions({ editingElement, value }) {
81+
if (!value && editingElement.dataset.displayOptions) {
82+
editingElement.dataset.customAmount = "slider";
83+
}
84+
this.rebuildPrefilledOptions(editingElement);
85+
}
86+
87+
toggleDescriptions({ editingElement }) {
88+
this.rebuildPrefilledOptions(editingElement);
89+
}
90+
91+
getPrefilledOptionsList({ editingElement }) {
92+
const savedOptions = editingElement.dataset.prefilledOptionsList;
93+
94+
// TODO AGAU: remove when merging https://github.com/odoo-dev/odoo/pull/4240
95+
{
96+
if (savedOptions) {
97+
return savedOptions;
98+
} else {
99+
const options = [];
100+
const amounts = JSON.parse(editingElement.dataset.donationAmounts || "[]");
101+
const descriptionEls = editingElement.querySelectorAll(
102+
"#s_donation_description_inputs input"
103+
);
104+
const descriptions = Array.from(descriptionEls).map(
105+
(descriptionEl) => descriptionEl.value
106+
);
107+
for (let i = 0; i < amounts.length; i++) {
108+
options.push({
109+
value: amounts[i],
110+
description:
111+
typeof descriptions[i] === "string"
112+
? descriptions[i]
113+
: _t("Add a description here"),
114+
});
115+
}
116+
return JSON.stringify(options);
117+
}
118+
}
119+
120+
// TODO AGAU: uncomment when merging https://github.com/odoo-dev/odoo/pull/4240
121+
// return savedOptions || "[]";
122+
}
123+
124+
applyPrefilledOptionsList({ editingElement, value }) {
125+
// TODO AGAU: remove when merging https://github.com/odoo-dev/odoo/pull/4240
126+
{
127+
const options = JSON.parse(value);
128+
const amounts = options.map((option) => option.value);
129+
editingElement.dataset.donationAmounts = JSON.stringify(amounts);
130+
}
131+
132+
editingElement.dataset.prefilledOptionsList = value;
133+
this.rebuildPrefilledOptions(editingElement, value);
134+
}
135+
136+
isAmountInputApplied({ editingElement, params }) {
137+
return editingElement.dataset.customAmount === params.mainParam;
138+
}
139+
140+
setAmountInput({ editingElement, params }) {
141+
editingElement.dataset.customAmount = params.mainParam;
142+
this.rebuildPrefilledOptions(editingElement);
143+
}
144+
145+
getMinimumAmount({ editingElement }) {
146+
return editingElement.dataset.minimumAmount;
147+
}
148+
149+
setMinimumAmount({ editingElement, value }) {
150+
editingElement.dataset.minimumAmount = value;
151+
const rangeSliderEl = editingElement.querySelector("#s_donation_range_slider");
152+
const amountInputEl = editingElement.querySelector("#s_donation_amount_input");
153+
if (rangeSliderEl) {
154+
rangeSliderEl.min = value;
155+
} else if (amountInputEl) {
156+
amountInputEl.min = value;
157+
}
158+
}
159+
160+
getMaximumAmount({ editingElement }) {
161+
return editingElement.dataset.maximumAmount;
162+
}
163+
164+
setMaximumAmount({ editingElement, value }) {
165+
editingElement.dataset.maximumAmount = value;
166+
const rangeSliderEl = editingElement.querySelector("#s_donation_range_slider");
167+
const amountInputEl = editingElement.querySelector("#s_donation_amount_input");
168+
if (rangeSliderEl) {
169+
rangeSliderEl.max = value;
170+
} else if (amountInputEl) {
171+
amountInputEl.max = value;
172+
}
173+
}
174+
175+
getSliderStep({ editingElement }) {
176+
return editingElement.dataset.sliderStep;
177+
}
178+
179+
setSliderStep({ editingElement, value }) {
180+
editingElement.dataset.sliderStep = value;
181+
const rangeSliderEl = editingElement.querySelector("#s_donation_range_slider");
182+
if (rangeSliderEl) {
183+
rangeSliderEl.step = value;
184+
}
185+
}
186+
187+
// TODO AGAU: remove when merging https://github.com/odoo-dev/odoo/pull/4240
188+
cleanForSave(editingElement) {
189+
delete editingElement.dataset.prefilledOptionsList;
190+
}
191+
192+
rebuildPrefilledOptions(editingElement, options) {
193+
if (!options) {
194+
options = this.getPrefilledOptionsList({ editingElement });
195+
}
196+
197+
// TODO AGAU: remove when merging https://github.com/odoo-dev/odoo/pull/4240
198+
editingElement.dataset.prefilledOptionsList = options;
199+
200+
options = JSON.parse(options);
201+
202+
const displayOptions = editingElement.dataset.displayOptions;
203+
const formEl = editingElement.querySelector(".s_donation_form");
204+
const donateButtonEl = editingElement.querySelector(".s_donation_donate_btn");
205+
const prefilledOptions = editingElement.dataset.prefilledOptions;
206+
const showDescriptions = prefilledOptions && editingElement.dataset.descriptions;
207+
208+
// Slider
209+
const layout = editingElement.dataset.customAmount;
210+
const sliderEl = editingElement.querySelector(".s_donation_range_slider_wrap");
211+
if (layout !== "slider" || !displayOptions) {
212+
sliderEl?.remove();
213+
} else if (layout === "slider" && displayOptions && !sliderEl) {
214+
const sliderEl = renderToElement("website_payment.donation.slider", {
215+
minimum_amount: editingElement.dataset.minimumAmount,
216+
maximum_amount: editingElement.dataset.maximumAmount,
217+
slider_step: editingElement.dataset.sliderStep,
218+
});
219+
formEl.insertBefore(sliderEl, donateButtonEl);
220+
}
221+
222+
// Hidden inputs for descriptions translation
223+
const descriptionInputContainerEl = editingElement.querySelector(
224+
"#s_donation_description_inputs"
225+
);
226+
descriptionInputContainerEl.innerHTML = "";
227+
if (showDescriptions) {
228+
descriptionInputContainerEl.insertBefore(
229+
renderToFragment("website_payment.donation.descriptionTranslationInputs", {
230+
descriptions: options.map((option) => option.description),
231+
}),
232+
null
233+
);
234+
}
235+
236+
// Displayed prefilled options
237+
editingElement.querySelector(".s_donation_prefilled_buttons")?.remove();
238+
if (displayOptions) {
239+
// TODO AGAU: remove when merging https://github.com/odoo-dev/odoo/pull/4240
240+
{
241+
if (!showDescriptions) {
242+
options = options.map((option) => option.value);
243+
}
244+
}
245+
246+
const prefilledButtonsEl = renderToElement(
247+
`website_payment.donation.prefilledButtons${
248+
showDescriptions ? "Descriptions" : ""
249+
}`,
250+
{
251+
prefilled_buttons: prefilledOptions ? options : [],
252+
custom_input: layout === "freeAmount",
253+
minimum_amount: editingElement.dataset.minimumAmount,
254+
}
255+
);
256+
formEl.insertBefore(prefilledButtonsEl, descriptionInputContainerEl.nextSibling);
257+
}
258+
}
259+
}
260+
registry.category("website-plugins").add(DonationOptionPlugin.id, DonationOptionPlugin);

0 commit comments

Comments
 (0)