mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-02-16 18:31:26 +01:00
Fix Et2Number had lost its spinner buttons
Also fix some failed tests and adjust widget sizing
This commit is contained in:
parent
b294b5f999
commit
3f789cfdce
@ -154,8 +154,8 @@ export class Et2DateDuration extends Et2InputWidget(LitElement)
|
|||||||
|
|
||||||
.duration__input {
|
.duration__input {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
max-width: 4.5em;
|
width: min-content;
|
||||||
min-width: 3em;
|
min-width: 5em;
|
||||||
margin-right: -2px;
|
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.
|
* Works with the min and max attributes to limit the increments at which a numeric or date-time value can be set.
|
||||||
*/
|
*/
|
||||||
step: {
|
@property({type: Number, reflect: true})
|
||||||
type: String
|
step = 1;
|
||||||
}
|
|
||||||
|
|
||||||
protected static time_formats = {d: "d", h: "h", m: "m", s: "s"};
|
protected static time_formats = {d: "d", h: "h", m: "m", s: "s"};
|
||||||
protected _display = {value: "", unit: ""};
|
protected _display = {value: "", unit: ""};
|
||||||
@ -570,6 +569,7 @@ export class Et2DateDuration extends Et2InputWidget(LitElement)
|
|||||||
name=${input.name}
|
name=${input.name}
|
||||||
min=${typeof input.min === "number" ? input.min : nothing}
|
min=${typeof input.min === "number" ? input.min : nothing}
|
||||||
max=${typeof input.max === "number" ? input.max : nothing}
|
max=${typeof input.max === "number" ? input.max : nothing}
|
||||||
|
step=${this.step}
|
||||||
precision=${typeof input.precision === "number" ? input.precision : nothing}
|
precision=${typeof input.precision === "number" ? input.precision : nothing}
|
||||||
title=${input.title || nothing}
|
title=${input.title || nothing}
|
||||||
value=${input.value}
|
value=${input.value}
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
## Examples ##
|
## Examples ##
|
||||||
|
|
||||||
|
### Spinners ###
|
||||||
|
|
||||||
|
To add up / down arrow buttons to change the value, set `step`.
|
||||||
|
|
||||||
|
```html:preview
|
||||||
|
<et2-number label="0.5 step" step="0.5"></et2-number>
|
||||||
|
```
|
||||||
|
|
||||||
### Precision ###
|
### Precision ###
|
||||||
|
|
||||||
To enforce a certain number of decimal places, set `precision`.
|
To enforce a certain number of decimal places, set `precision`.
|
||||||
|
@ -9,10 +9,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Et2Textbox} from "./Et2Textbox";
|
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 {customElement} from "lit/decorators/custom-element.js";
|
||||||
import {property} from "lit/decorators/property.js";
|
import {property} from "lit/decorators/property.js";
|
||||||
import {number} from "prop-types";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,15 +43,20 @@ export class Et2Number extends Et2Textbox
|
|||||||
css`
|
css`
|
||||||
/* Scroll buttons */
|
/* Scroll buttons */
|
||||||
|
|
||||||
:host(:hover) et2-button-scroll {
|
:host(:hover) ::slotted(et2-button-scroll) {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
et2-button-scroll {
|
::slotted(et2-button-scroll) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
margin-left: var(--sl-spacing-small);
|
margin-left: var(--sl-spacing-small);
|
||||||
|
margin-inline-end: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([step]) .input--medium .input__control {
|
||||||
|
padding-right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control-input {
|
.form-control-input {
|
||||||
@ -143,6 +147,9 @@ export class Et2Number extends Et2Textbox
|
|||||||
const thousands = numberFormat ? numberFormat[1] : '';
|
const thousands = numberFormat ? numberFormat[1] : '';
|
||||||
this.decimalSeparator = this.decimalSeparator || decimal || ".";
|
this.decimalSeparator = this.decimalSeparator || decimal || ".";
|
||||||
this.thousandsSeparator = this.thousandsSeparator || thousands || "";
|
this.thousandsSeparator = this.thousandsSeparator || thousands || "";
|
||||||
|
|
||||||
|
// Add spinners
|
||||||
|
render(this._incrementButtonTemplate(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated()
|
firstUpdated()
|
||||||
@ -201,14 +208,10 @@ export class Et2Number extends Et2Textbox
|
|||||||
{
|
{
|
||||||
if("" + val !== "")
|
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
|
// Remove separator so parseFloat works
|
||||||
if(typeof val === 'string')
|
if(typeof val === 'string')
|
||||||
{
|
{
|
||||||
val = val.replace(",", '.');
|
val = val.replace(this.thousandsSeparator, "").replace(",", '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof this.precision !== 'undefined')
|
if(typeof this.precision !== 'undefined')
|
||||||
@ -220,7 +223,21 @@ export class Et2Number extends Et2Textbox
|
|||||||
val = parseFloat(val);
|
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
|
get value() : string
|
||||||
@ -240,7 +257,12 @@ export class Et2Number extends Et2Textbox
|
|||||||
|
|
||||||
get valueAsNumber() : number
|
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')
|
if(typeof this.precision !== 'undefined')
|
||||||
{
|
{
|
||||||
formattedValue = parseFloat(parseFloat(<string>formattedValue).toFixed(this.precision));
|
formattedValue = parseFloat(parseFloat(<string>formattedValue).toFixed(this.precision));
|
||||||
@ -294,12 +316,19 @@ export class Et2Number extends Et2Textbox
|
|||||||
let options = {
|
let options = {
|
||||||
...super.maskOptions,
|
...super.maskOptions,
|
||||||
skipInvalid: true,
|
skipInvalid: true,
|
||||||
|
scale: 5,
|
||||||
// The initial options need to match an actual number
|
// The initial options need to match an actual number
|
||||||
radix: this.decimalSeparator,
|
radix: this.decimalSeparator,
|
||||||
thousandsSeparator: this.thousandsSeparator,
|
thousandsSeparator: this.thousandsSeparator,
|
||||||
mask: this.mask ?? Number,
|
mask: this.mask ?? Number,
|
||||||
lazy: false,
|
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")
|
if(typeof this.precision != "undefined")
|
||||||
{
|
{
|
||||||
@ -318,18 +347,18 @@ export class Et2Number extends Et2Textbox
|
|||||||
|
|
||||||
updateMaskValue()
|
updateMaskValue()
|
||||||
{
|
{
|
||||||
this._mask.updateValue();
|
this._mask?.updateValue();
|
||||||
if(!this.mask)
|
if(!this.mask && this._mask)
|
||||||
{
|
{
|
||||||
// Number mask sometimes gets lost with different decimal characters
|
// Number mask sometimes gets lost with different decimal characters
|
||||||
this._mask.unmaskedValue = ("" + this.value);
|
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.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;
|
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.dispatchEvent(new CustomEvent("sl-change", {bubbles: true}));
|
||||||
this.requestUpdate("value", old_value);
|
this.requestUpdate("value", old_value);
|
||||||
}
|
}
|
||||||
@ -370,30 +402,6 @@ export class Et2Number extends Et2Textbox
|
|||||||
part="scroll"
|
part="scroll"
|
||||||
@et2-scroll=${this.handleScroll}></et2-button-scroll>`;
|
@et2-scroll=${this.handleScroll}></et2-button-scroll>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_inputTemplate()
|
|
||||||
{
|
|
||||||
return html`
|
|
||||||
<sl-input
|
|
||||||
part="input"
|
|
||||||
max=${this.max || nothing}
|
|
||||||
min=${this.min || nothing}
|
|
||||||
placeholder=${this.placeholder || nothing}
|
|
||||||
inputmode="numeric"
|
|
||||||
?disabled=${this.disabled}
|
|
||||||
?readonly=${this.readonly}
|
|
||||||
?required=${this.required}
|
|
||||||
.value=${this.formattedValue}
|
|
||||||
@blur=${this.handleBlur}
|
|
||||||
>
|
|
||||||
<slot name="prefix" slot="prefix"></slot>
|
|
||||||
${this.prefix ? html`<span slot="prefix">${this.prefix}</span>` : nothing}
|
|
||||||
${this.suffix ? html`<span slot="suffix">${this.suffix}</span>` : nothing}
|
|
||||||
<slot name="suffix" slot="suffix"></slot>
|
|
||||||
${this._incrementButtonTemplate()}
|
|
||||||
</sl-input>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,7 +415,7 @@ export function formatNumber(value : number, decimalSeparator : string = ".", th
|
|||||||
let parts = ("" + value).split(".");
|
let parts = ("" + value).split(".");
|
||||||
|
|
||||||
parts[0] = parts[0].replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, thousandsSeparator) || "0";
|
parts[0] = parts[0].replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, thousandsSeparator) || "0";
|
||||||
if(typeof decimalPlaces != "undefined")
|
if(typeof decimalPlaces != "undefined" && decimalPlaces != 0)
|
||||||
{
|
{
|
||||||
parts[1] = (parts[1] ?? "").padEnd(decimalPlaces, "0").substr(0, decimalPlaces);
|
parts[1] = (parts[1] ?? "").padEnd(decimalPlaces, "0").substr(0, decimalPlaces);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {css, html, nothing, PropertyValues} from "lit";
|
import {css, PropertyValues} from "lit";
|
||||||
import {customElement} from "lit/decorators/custom-element.js";
|
import {customElement} from "lit/decorators/custom-element.js";
|
||||||
import {property} from "lit/decorators/property.js";
|
import {property} from "lit/decorators/property.js";
|
||||||
import {Regex} from "../Validators/Regex";
|
import {Regex} from "../Validators/Regex";
|
||||||
@ -173,6 +173,12 @@ export class Et2Textbox extends Et2InputWidget(SlInput)
|
|||||||
|
|
||||||
protected updateMask()
|
protected updateMask()
|
||||||
{
|
{
|
||||||
|
// Skip if there's no mask desired
|
||||||
|
if(!this.maskOptions.mask)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const input = this.shadowRoot.querySelector("input")
|
const input = this.shadowRoot.querySelector("input")
|
||||||
if(!this._mask)
|
if(!this._mask)
|
||||||
{
|
{
|
||||||
@ -211,56 +217,4 @@ export class Et2Textbox extends Et2InputWidget(SlInput)
|
|||||||
// this._mask.updateValue();
|
// this._mask.updateValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _inputTemplate()
|
|
||||||
{
|
|
||||||
return html`
|
|
||||||
<sl-input
|
|
||||||
part="input"
|
|
||||||
placeholder=${this.placeholder || nothing}
|
|
||||||
inputmode="${this.inputMode}"
|
|
||||||
?disabled=${this.disabled}
|
|
||||||
?readonly=${this.readonly}
|
|
||||||
?required=${this.required}
|
|
||||||
.value=${this.value}
|
|
||||||
@input=${(e) =>
|
|
||||||
{
|
|
||||||
if(this.__mask)
|
|
||||||
{
|
|
||||||
this.__mask.updateCursor(this.__mask.cursorPos)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<slot name="prefix" slot="prefix"></slot>
|
|
||||||
<slot name="suffix" slot="suffix"></slot>
|
|
||||||
</sl-input>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
const labelTemplate = this._labelTemplate();
|
|
||||||
const helpTemplate = this._helpTextTemplate();
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
part="form-control"
|
|
||||||
class=${classMap({
|
|
||||||
'form-control': true,
|
|
||||||
'form-control--medium': true,
|
|
||||||
'form-control--has-label': labelTemplate !== nothing,
|
|
||||||
'form-control--has-help-text': helpTemplate !== nothing
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${labelTemplate}
|
|
||||||
<div part="form-control-input" class="form-control-input">
|
|
||||||
${this._inputTemplate()}
|
|
||||||
</div>
|
|
||||||
${helpTemplate}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
@ -44,25 +44,25 @@ describe("Number widget", () =>
|
|||||||
|
|
||||||
it("handles precision", () =>
|
it("handles precision", () =>
|
||||||
{
|
{
|
||||||
window.egw.preference = () => ".";
|
element.decimalSeparator = ".";
|
||||||
element.precision = 2;
|
element.precision = 2;
|
||||||
element.value = "1.234";
|
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.precision = 0;
|
||||||
element.value = "1.234";
|
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
|
// Now do it with comma decimal separator
|
||||||
window.egw.preference = () => ",";
|
element.decimalSeparator = ","
|
||||||
element.precision = 2;
|
element.precision = 2;
|
||||||
element.value = "1.234";
|
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";
|
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.precision = 0;
|
||||||
element.value = "1,234";
|
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", () =>
|
it("Min limit", () =>
|
||||||
|
Loading…
Reference in New Issue
Block a user