mirror of https://github.com/OCA/web.git
[IMP] web_widget_product_label_section_and_note: Auto-resize textarea for multiline text.
Before this change, when the text was large and multiline, the textarea did not resize, and the text was not displayed correctly. Now, the textarea adjusts according to the text. These changes are backported from V18.pull/3080/head
parent
8d42b1676d
commit
fe81a03937
|
@ -7,7 +7,7 @@ Web widget product label section and note
|
|||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:b34f3ca01ada6453f702e988c896537cf322d1f7dae827582f4e07313735dc66
|
||||
!! source digest: sha256:db9a30e28410d3ef4ad626f850f766eedb3845976f424d1bbae3350674397d0d
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
|
@ -57,7 +57,7 @@ Usage
|
|||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
- Add compatibility with `sale` module
|
||||
- Add compatibility with `sale_product_configurator` module
|
||||
- Add compatibility with `purchase_product_matrix` module
|
||||
- When this module is installed, the PDF report will always display the column `name` as a combination of the `product code`, `product name`, and `description`. This behavior may differ from the expected behavior, where only the column `name` is displayed and can be customized independently.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Web widget product label section and note",
|
||||
"version": "16.0.1.0.0",
|
||||
"version": "16.0.1.0.1",
|
||||
"summary": "unify the product and name into a single column",
|
||||
"author": "Tecnativa, Odoo Community Association (OCA), Odoo S.A.",
|
||||
"website": "https://github.com/OCA/web",
|
||||
|
@ -13,6 +13,7 @@
|
|||
],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"web_widget_product_label_section_and_note/static/src/core/utils/**/*",
|
||||
"web_widget_product_label_section_and_note/static/src/components/**/*",
|
||||
],
|
||||
},
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
- Add compatibility with `sale` module
|
||||
- Add compatibility with `sale_product_configurator` module
|
||||
- Add compatibility with `purchase_product_matrix` module
|
||||
- When this module is installed, the PDF report will always display the column `name` as a combination of the `product code`, `product name`, and `description`. This behavior may differ from the expected behavior, where only the column `name` is displayed and can be customized independently.
|
|
@ -367,7 +367,7 @@ ul.auto-toc {
|
|||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:b34f3ca01ada6453f702e988c896537cf322d1f7dae827582f4e07313735dc66
|
||||
!! source digest: sha256:db9a30e28410d3ef4ad626f850f766eedb3845976f424d1bbae3350674397d0d
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/web/tree/16.0/web_widget_product_label_section_and_note"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_widget_product_label_section_and_note"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>This module unifies the product and name into a single column, making it more user-friendly and space-saving.</p>
|
||||
|
@ -407,7 +407,7 @@ in a new module, it is necessary to create an inherited view to change the widge
|
|||
<div class="section" id="known-issues-roadmap">
|
||||
<h1><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h1>
|
||||
<ul class="simple">
|
||||
<li>Add compatibility with <cite>sale</cite> module</li>
|
||||
<li>Add compatibility with <cite>sale_product_configurator</cite> module</li>
|
||||
<li>Add compatibility with <cite>purchase_product_matrix</cite> module</li>
|
||||
<li>When this module is installed, the PDF report will always display the column <cite>name</cite> as a combination of the <cite>product code</cite>, <cite>product name</cite>, and <cite>description</cite>. This behavior may differ from the expected behavior, where only the column <cite>name</cite> is displayed and can be customized independently.</li>
|
||||
</ul>
|
||||
|
|
|
@ -20,6 +20,7 @@ import {X2ManyField} from "@web/views/fields/x2many/x2many_field";
|
|||
import {_t} from "@web/core/l10n/translation";
|
||||
import {getActiveHotkey} from "@web/core/hotkeys/hotkey_service";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {useProductAndLabelAutoresize} from "../../core/utils/product_and_label_autoresize.esm";
|
||||
|
||||
export class ProductLabelSectionAndNoteListRender extends SectionAndNoteListRenderer {
|
||||
setup() {
|
||||
|
@ -138,7 +139,13 @@ export class ProductLabelSectionAndNoteField extends Many2OneField {
|
|||
value: this.props.record.columnIsProductAndLabel,
|
||||
});
|
||||
this.labelNode = useRef("labelNodeRef");
|
||||
useProductAndLabelAutoresize(this.labelNode, {
|
||||
targetParentName: this.props.name,
|
||||
});
|
||||
this.productNode = useRef("productNodeRef");
|
||||
useProductAndLabelAutoresize(this.productNode, {
|
||||
targetParentName: this.props.name,
|
||||
});
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/** @odoo-module **/
|
||||
/* Copyright Odoo S.A.
|
||||
* Copyright 2024 Tecnativa - Carlos Lopez
|
||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
||||
|
||||
import {useEffect} from "@odoo/owl";
|
||||
import {browser} from "@web/core/browser/browser";
|
||||
|
||||
function resizeInput(input) {
|
||||
// This mesures the maximum width of the input which can get from the flex layout.
|
||||
input.style.width = "100%";
|
||||
const maxWidth = input.clientWidth;
|
||||
// Somehow Safari 16 computes input sizes incorrectly. This is fixed in Safari 17
|
||||
const isSafari16 = /Version\/16.+Safari/i.test(browser.navigator.userAgent);
|
||||
// Minimum width of the input
|
||||
input.style.width = "10px";
|
||||
if (input.value === "" && input.placeholder !== "") {
|
||||
input.style.width = "auto";
|
||||
return;
|
||||
}
|
||||
if (input.scrollWidth + 5 + (isSafari16 ? 8 : 0) > maxWidth) {
|
||||
input.style.width = "100%";
|
||||
return;
|
||||
}
|
||||
input.style.width = input.scrollWidth + 5 + (isSafari16 ? 8 : 0) + "px";
|
||||
}
|
||||
|
||||
export function resizeTextArea(textarea, options = {}) {
|
||||
const minimumHeight = options.minimumHeight || 0;
|
||||
let heightOffset = 0;
|
||||
const style = window.getComputedStyle(textarea);
|
||||
if (style.boxSizing === "border-box") {
|
||||
const paddingHeight =
|
||||
parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
||||
const borderHeight =
|
||||
parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
|
||||
heightOffset = borderHeight + paddingHeight;
|
||||
}
|
||||
const previousStyle = {
|
||||
borderTopWidth: style.borderTopWidth,
|
||||
borderBottomWidth: style.borderBottomWidth,
|
||||
padding: style.padding,
|
||||
};
|
||||
Object.assign(textarea.style, {
|
||||
height: "auto",
|
||||
borderTopWidth: 0,
|
||||
borderBottomWidth: 0,
|
||||
paddingTop: 0,
|
||||
paddingRight: style.paddingRight,
|
||||
paddingBottom: 0,
|
||||
paddingLeft: style.paddingLeft,
|
||||
});
|
||||
textarea.style.height = "auto";
|
||||
const height = Math.max(minimumHeight, textarea.scrollHeight + heightOffset);
|
||||
Object.assign(textarea.style, previousStyle, {height: `${height}px`});
|
||||
textarea.parentElement.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used on text inputs or textareas to automatically resize it based on its
|
||||
* content each time it is updated. It takes the reference of the element as
|
||||
* parameter and some options. Do note that it may introduce mild performance issues
|
||||
* since it will force a reflow of the layout each time the element is updated.
|
||||
* Do also note that it only works with textareas that are nested as only child
|
||||
* of some parent div (like in the text_field component).
|
||||
*
|
||||
* @param {Ref} ref
|
||||
*/
|
||||
export function useAutoresize(ref, options = {}) {
|
||||
let wasProgrammaticallyResized = false;
|
||||
let resize = null;
|
||||
useEffect(
|
||||
(el) => {
|
||||
if (el) {
|
||||
resize = (programmaticResize = false) => {
|
||||
wasProgrammaticallyResized = programmaticResize;
|
||||
if (el instanceof HTMLInputElement) {
|
||||
resizeInput(el, options);
|
||||
} else {
|
||||
resizeTextArea(el, options);
|
||||
}
|
||||
if (options.onResize) {
|
||||
options.onResize(el, options);
|
||||
}
|
||||
};
|
||||
el.addEventListener("input", () => resize(true));
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
// This ensures that the resize function is not called twice on input or page load
|
||||
if (wasProgrammaticallyResized) {
|
||||
wasProgrammaticallyResized = false;
|
||||
return;
|
||||
}
|
||||
resize();
|
||||
});
|
||||
resizeObserver.observe(el);
|
||||
return () => {
|
||||
el.removeEventListener("input", resize);
|
||||
resizeObserver.unobserve(el);
|
||||
resizeObserver.disconnect();
|
||||
resize = null;
|
||||
};
|
||||
}
|
||||
},
|
||||
() => [ref.el]
|
||||
);
|
||||
useEffect(() => {
|
||||
if (resize) {
|
||||
resize(true);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/** @odoo-module **/
|
||||
/* Copyright Odoo S.A.
|
||||
* Copyright 2024 Tecnativa - Carlos Lopez
|
||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
||||
|
||||
import {useAutoresize} from "./autoresize.esm";
|
||||
|
||||
export function productAndLabelResizeTextArea(textarea, options = {}) {
|
||||
const style = window.getComputedStyle(textarea);
|
||||
if (options.targetParentName) {
|
||||
let target = textarea.parentElement;
|
||||
let shouldContinue = true;
|
||||
while (target && shouldContinue) {
|
||||
const totalParentHeight = Array.from(target.children).reduce(
|
||||
(total, child) => {
|
||||
const childHeight = child.style.height || style.lineHeight;
|
||||
return total + parseFloat(childHeight);
|
||||
},
|
||||
0
|
||||
);
|
||||
target.style.height = `${totalParentHeight}px`;
|
||||
if (target.getAttribute("name") === options.targetParentName) {
|
||||
shouldContinue = false;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This overriden version of the resizeTextArea method is specificly done for the product_label_section_and_note widget
|
||||
* His necessity is found in the fact that the cell of said widget doesn't contain only the input or textarea to resize
|
||||
* but also another node containing the name of the product if said data is available. This means that the autoresize
|
||||
* method which sets the height of the parent cell should sometimes add an additional row to the parent cell so that
|
||||
* no text overflows
|
||||
*
|
||||
* @param {Ref} ref
|
||||
*/
|
||||
export function useProductAndLabelAutoresize(ref, options = {}) {
|
||||
useAutoresize(ref, {onResize: productAndLabelResizeTextArea, ...options});
|
||||
}
|
Loading…
Reference in New Issue