diff --git a/api/js/etemplate/Et2Date/Et2DateDuration.ts b/api/js/etemplate/Et2Date/Et2DateDuration.ts index 209e4a6d79..0008ec7389 100644 --- a/api/js/etemplate/Et2Date/Et2DateDuration.ts +++ b/api/js/etemplate/Et2Date/Et2DateDuration.ts @@ -154,8 +154,8 @@ export class Et2DateDuration extends Et2InputWidget(LitElement) .duration__input { flex: 1 1 auto; - max-width: 4.5em; - min-width: 3em; + width: min-content; + min-width: 5em; margin-right: -2px; } @@ -257,9 +257,8 @@ export class Et2DateDuration extends Et2InputWidget(LitElement) * * Works with the min and max attributes to limit the increments at which a numeric or date-time value can be set. */ - step: { - type: String - } + @property({type: Number, reflect: true}) + step = 1; protected static time_formats = {d: "d", h: "h", m: "m", s: "s"}; protected _display = {value: "", unit: ""}; @@ -570,6 +569,7 @@ export class Et2DateDuration extends Et2InputWidget(LitElement) name=${input.name} min=${typeof input.min === "number" ? input.min : nothing} max=${typeof input.max === "number" ? input.max : nothing} + step=${this.step} precision=${typeof input.precision === "number" ? input.precision : nothing} title=${input.title || nothing} value=${input.value} diff --git a/api/js/etemplate/Et2Textbox/Et2Number.md b/api/js/etemplate/Et2Textbox/Et2Number.md index d10774db7d..0fcb756368 100644 --- a/api/js/etemplate/Et2Textbox/Et2Number.md +++ b/api/js/etemplate/Et2Textbox/Et2Number.md @@ -1,5 +1,13 @@ ## Examples ## +### Spinners ### + +To add up / down arrow buttons to change the value, set `step`. + +```html:preview + +``` + ### Precision ### To enforce a certain number of decimal places, set `precision`. diff --git a/api/js/etemplate/Et2Textbox/Et2Number.ts b/api/js/etemplate/Et2Textbox/Et2Number.ts index 78c09413de..a8bb5199e9 100644 --- a/api/js/etemplate/Et2Textbox/Et2Number.ts +++ b/api/js/etemplate/Et2Textbox/Et2Number.ts @@ -9,10 +9,9 @@ */ import {Et2Textbox} from "./Et2Textbox"; -import {css, html, nothing} from "lit"; +import {css, html, nothing, render} from "lit"; import {customElement} from "lit/decorators/custom-element.js"; import {property} from "lit/decorators/property.js"; -import {number} from "prop-types"; /** @@ -44,15 +43,20 @@ export class Et2Number extends Et2Textbox css` /* Scroll buttons */ - :host(:hover) et2-button-scroll { + :host(:hover) ::slotted(et2-button-scroll) { visibility: visible; } - et2-button-scroll { + ::slotted(et2-button-scroll) { visibility: hidden; padding: 0px; margin: 0px; margin-left: var(--sl-spacing-small); + margin-inline-end: 0px; + } + + :host([step]) .input--medium .input__control { + padding-right: 0px; } .form-control-input { @@ -143,6 +147,9 @@ export class Et2Number extends Et2Textbox const thousands = numberFormat ? numberFormat[1] : ''; this.decimalSeparator = this.decimalSeparator || decimal || "."; this.thousandsSeparator = this.thousandsSeparator || thousands || ""; + + // Add spinners + render(this._incrementButtonTemplate(), this); } firstUpdated() @@ -201,14 +208,10 @@ export class Et2Number extends Et2Textbox { if("" + val !== "") { - // use decimal separator from user prefs - const format = this.egw().preference('number_format'); - const sep = format ? format[0] : '.'; - // Remove separator so parseFloat works if(typeof val === 'string') { - val = val.replace(",", '.'); + val = val.replace(this.thousandsSeparator, "").replace(",", '.'); } if(typeof this.precision !== 'undefined') @@ -220,7 +223,21 @@ export class Et2Number extends Et2Textbox val = parseFloat(val); } } - super.value = val; + if(isNaN(val)) + { + super.value = val; + return; + } + if(this.max && val > this.max) + { + val = this.max; + } + if(this.min && val < this.min) + { + val = this.min; + } + super.value = formatNumber(val, this.decimalSeparator, this.thousandsSeparator, this.precision); + this.updateMaskValue(); } get value() : string @@ -240,7 +257,12 @@ export class Et2Number extends Et2Textbox get valueAsNumber() : number { - let formattedValue = (this.mask && this._mask?.value ? this.stripFormat(this._mask.value) : this._mask?.unmaskedValue) ?? this.value; + this.updateMaskValue(); + let formattedValue = (this._mask?.value ? this.stripFormat(this._mask.value) : this._mask?.unmaskedValue) ?? this.value; + if(formattedValue == "") + { + return 0; + } if(typeof this.precision !== 'undefined') { formattedValue = parseFloat(parseFloat(formattedValue).toFixed(this.precision)); @@ -294,12 +316,19 @@ export class Et2Number extends Et2Textbox let options = { ...super.maskOptions, skipInvalid: true, + scale: 5, // The initial options need to match an actual number radix: this.decimalSeparator, thousandsSeparator: this.thousandsSeparator, mask: this.mask ?? Number, lazy: false, - padFractionalZeros: (typeof this.precision !== "undefined") + padFractionalZeros: (typeof this.precision !== "undefined"), + definitions: { + '#': { + mask: RegExp("[-\\d\\" + this.thousandsSeparator + "\\" + this.decimalSeparator + "]") + //RegExp("-?[\\d\\" + this.thousandsSeparator + "]+" + (this.precision ? "\\" + this.decimalSeparator + "\\d{" + this.precision + "}" : '')) + } + } } if(typeof this.precision != "undefined") { @@ -318,18 +347,18 @@ export class Et2Number extends Et2Textbox updateMaskValue() { - this._mask.updateValue(); - if(!this.mask) + this._mask?.updateValue(); + if(!this.mask && this._mask) { // Number mask sometimes gets lost with different decimal characters this._mask.unmaskedValue = ("" + this.value); } - if(this.value !== "") + if(this.value !== "" && this._mask) { this._mask.value = formatNumber(this.value, this.decimalSeparator, this.thousandsSeparator, this.precision); } - this._mask.updateValue(); + this._mask?.updateValue(); } @@ -347,7 +376,10 @@ export class Et2Number extends Et2Textbox { max = Number.MAX_SAFE_INTEGER; } - this.value = "" + Math.min(Math.max(this.valueAsNumber + e.detail * (parseFloat(this.step) || 1), min), max); + this.value = formatNumber( + Math.min(Math.max((isNaN(this.valueAsNumber) ? 0 : this.valueAsNumber) + e.detail * (parseFloat(this.step) || 1), min), max), + this.decimalSeparator, this.thousandsSeparator, this.precision + ); this.dispatchEvent(new CustomEvent("sl-change", {bubbles: true})); this.requestUpdate("value", old_value); } @@ -370,30 +402,6 @@ export class Et2Number extends Et2Textbox part="scroll" @et2-scroll=${this.handleScroll}>`; } - - _inputTemplate() - { - return html` - - - ${this.prefix ? html`${this.prefix}` : nothing} - ${this.suffix ? html`${this.suffix}` : nothing} - - ${this._incrementButtonTemplate()} - - `; - } } /** @@ -407,7 +415,7 @@ export function formatNumber(value : number, decimalSeparator : string = ".", th let parts = ("" + value).split("."); parts[0] = parts[0].replace(/\B(? - { - if(this.__mask) - { - this.__mask.updateCursor(this.__mask.cursorPos) - } - }} - > - - - - `; - } - - /* - render() - { - const labelTemplate = this._labelTemplate(); - const helpTemplate = this._helpTextTemplate(); - - return html` -
- ${labelTemplate} -
- ${this._inputTemplate()} -
- ${helpTemplate} -
- `; - } - - */ } diff --git a/api/js/etemplate/Et2Textbox/test/Et2Number.test.ts b/api/js/etemplate/Et2Textbox/test/Et2Number.test.ts index adf1c9064b..3f70e632b1 100644 --- a/api/js/etemplate/Et2Textbox/test/Et2Number.test.ts +++ b/api/js/etemplate/Et2Textbox/test/Et2Number.test.ts @@ -44,25 +44,25 @@ describe("Number widget", () => it("handles precision", () => { - window.egw.preference = () => "."; + element.decimalSeparator = "."; element.precision = 2; element.value = "1.234"; - assert.equal(element.value, "1.23", "Wrong number of decimals"); + assert.equal(element.value, "1.23", "Wrong number of decimals (. separator"); element.precision = 0; element.value = "1.234"; - assert.equal(element.value, "1", "Wrong number of decimals"); + assert.equal(element.value, "1", "Wrong number of decimals (. separator"); // Now do it with comma decimal separator - window.egw.preference = () => ","; + element.decimalSeparator = "," element.precision = 2; element.value = "1.234"; - assert.equal(element.value, "1,23", "Wrong number of decimals"); + assert.equal(element.value, "1,23", "Wrong number of decimals ( . -> , separator)"); element.value = "1,234"; - assert.equal(element.value, "1,23", "Wrong number of decimals"); + assert.equal(element.value, "1,23", "Wrong number of decimals (, separator)"); element.precision = 0; element.value = "1,234"; - assert.equal(element.value, "1", "Wrong number of decimals"); + assert.equal(element.value, "1", "Wrong number of decimals (, separator)"); }) it("Min limit", () =>