2021-07-22 22:54:10 +02:00
|
|
|
/**
|
2021-08-13 23:26:18 +02:00
|
|
|
* EGroupware eTemplate2 - Textbox widget (WebComponent)
|
2021-07-22 22:54:10 +02:00
|
|
|
*
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package etemplate
|
|
|
|
* @subpackage api
|
|
|
|
* @link https://www.egroupware.org
|
|
|
|
* @author Nathan Gray
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
import {css, html, nothing, PropertyValues} from "lit";
|
|
|
|
import {customElement} from "lit/decorators/custom-element.js";
|
|
|
|
import {property} from "lit/decorators/property.js";
|
2022-03-02 22:22:19 +01:00
|
|
|
import {Regex} from "../Validators/Regex";
|
2022-07-21 20:39:00 +02:00
|
|
|
import shoelace from "../Styles/shoelace";
|
|
|
|
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
2024-08-06 16:41:11 +02:00
|
|
|
import IMask, {InputMask} from "imask";
|
|
|
|
import {SlInput} from "@shoelace-style/shoelace";
|
2021-07-22 22:54:10 +02:00
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
@customElement("et2-textbox")
|
2022-07-21 20:39:00 +02:00
|
|
|
export class Et2Textbox extends Et2InputWidget(SlInput)
|
2021-07-22 22:54:10 +02:00
|
|
|
{
|
|
|
|
|
2021-08-13 23:26:18 +02:00
|
|
|
static get styles()
|
|
|
|
{
|
|
|
|
return [
|
2022-07-21 20:39:00 +02:00
|
|
|
...shoelace,
|
2022-04-06 23:12:39 +02:00
|
|
|
...super.styles,
|
2021-08-13 23:26:18 +02:00
|
|
|
css`
|
2022-07-21 20:39:00 +02:00
|
|
|
:host([type="hidden"]) {
|
|
|
|
display: none;
|
|
|
|
}
|
2024-06-25 00:46:30 +02:00
|
|
|
|
|
|
|
.form_control {
|
|
|
|
display: inline-flex;
|
|
|
|
}
|
2022-07-21 20:39:00 +02:00
|
|
|
.input__control {
|
|
|
|
border: none;
|
2022-07-22 17:04:14 +02:00
|
|
|
width: 100%;
|
2022-07-21 20:39:00 +02:00
|
|
|
}
|
|
|
|
.input:hover:not(.input--disabled) .input__control {
|
|
|
|
color: var(--input-text-color, inherit);
|
|
|
|
}
|
|
|
|
`,
|
2021-08-13 23:26:18 +02:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
@property()
|
|
|
|
value = "";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Placeholder text to show as a hint when the input is empty.
|
|
|
|
*/
|
|
|
|
@property()
|
|
|
|
placeholder;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mask the input to enforce format. The mask is enforced as the user types, preventing invalid input.
|
|
|
|
*/
|
|
|
|
@property()
|
|
|
|
mask;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disables the input. It is still visible.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
@property({type: Boolean})
|
|
|
|
disabled = false;
|
|
|
|
|
|
|
|
@property({type: Function})
|
|
|
|
onkeypress;
|
|
|
|
|
|
|
|
private __validator : any;
|
|
|
|
private _mask : InputMask;
|
|
|
|
protected _value : string = "";
|
|
|
|
|
|
|
|
inputMode = "text";
|
|
|
|
|
2021-08-10 23:02:52 +02:00
|
|
|
|
2022-11-25 19:38:36 +01:00
|
|
|
static get translate()
|
|
|
|
{
|
|
|
|
return Object.assign({
|
|
|
|
helpText: true
|
|
|
|
}, super.translate);
|
|
|
|
}
|
|
|
|
|
2021-08-26 20:59:13 +02:00
|
|
|
constructor(...args : any[])
|
2021-08-13 23:26:18 +02:00
|
|
|
{
|
2021-08-26 20:59:13 +02:00
|
|
|
super(...args);
|
2021-08-13 23:26:18 +02:00
|
|
|
}
|
2021-08-10 23:02:52 +02:00
|
|
|
|
2021-08-13 23:26:18 +02:00
|
|
|
connectedCallback()
|
|
|
|
{
|
|
|
|
super.connectedCallback();
|
|
|
|
}
|
2022-03-02 22:22:19 +01:00
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
disconnectedCallback()
|
|
|
|
{
|
|
|
|
super.disconnectedCallback();
|
|
|
|
this.removeEventListener("focus", this.handleFocus);
|
|
|
|
}
|
|
|
|
|
|
|
|
firstUpdated()
|
|
|
|
{
|
|
|
|
if(this.maskOptions.mask)
|
|
|
|
{
|
|
|
|
this.updateMask();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-07 22:46:44 +02:00
|
|
|
/** @param changedProperties */
|
2022-03-02 22:22:19 +01:00
|
|
|
updated(changedProperties : PropertyValues)
|
|
|
|
{
|
|
|
|
super.updated(changedProperties);
|
|
|
|
if(changedProperties.has('validator'))
|
|
|
|
{
|
|
|
|
// Remove all existing Pattern validators (avoids duplicates)
|
2023-03-08 19:00:27 +01:00
|
|
|
this.validators = (this.validators || []).filter((validator) => !(validator instanceof Regex))
|
2022-03-02 22:22:19 +01:00
|
|
|
this.validators.push(new Regex(this.validator));
|
|
|
|
}
|
2024-08-06 16:41:11 +02:00
|
|
|
if(changedProperties.has('mask'))
|
|
|
|
{
|
|
|
|
this.updateMask();
|
|
|
|
}
|
2022-03-02 22:22:19 +01:00
|
|
|
}
|
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
@property()
|
2022-03-02 22:22:19 +01:00
|
|
|
get validator()
|
|
|
|
{
|
|
|
|
return this.__validator;
|
|
|
|
}
|
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
set validator(value : string | RegExp)
|
2022-03-02 22:22:19 +01:00
|
|
|
{
|
|
|
|
if(typeof value == 'string')
|
|
|
|
{
|
|
|
|
let parts = value.split('/');
|
|
|
|
let flags = parts.pop();
|
|
|
|
if(parts.length < 2 || parts[0] !== '')
|
|
|
|
{
|
|
|
|
this.egw().debug(this.egw().lang("'%1' has an invalid format !!!", value));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
parts.shift();
|
|
|
|
this.__validator = new RegExp(parts.join('/'), flags);
|
|
|
|
|
|
|
|
this.requestUpdate("validator");
|
|
|
|
}
|
2024-08-06 16:41:11 +02:00
|
|
|
else if(value instanceof RegExp)
|
|
|
|
{
|
|
|
|
this.__validator = value;
|
|
|
|
this.requestUpdate("validator");
|
|
|
|
}
|
2022-03-02 22:22:19 +01:00
|
|
|
}
|
2021-08-10 23:02:52 +02:00
|
|
|
|
2024-08-06 16:41:11 +02:00
|
|
|
/**
|
|
|
|
* Get the options for masking.
|
|
|
|
* Can be overridden by subclass for additional options.
|
|
|
|
*
|
|
|
|
* @see https://imask.js.org/guide.html#masked
|
|
|
|
*/
|
|
|
|
protected get maskOptions()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
mask: this.mask,
|
|
|
|
lazy: this.placeholder ? true : false,
|
|
|
|
autofix: true,
|
|
|
|
eager: "append",
|
|
|
|
overwrite: "shift"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected updateMask()
|
|
|
|
{
|
|
|
|
const input = this.shadowRoot.querySelector("input")
|
|
|
|
if(!this._mask)
|
|
|
|
{
|
|
|
|
this._mask = IMask(input, this.maskOptions);
|
|
|
|
this.addEventListener("focus", this.handleFocus)
|
|
|
|
window.setTimeout(() =>
|
|
|
|
{
|
|
|
|
this._mask.updateControl();
|
|
|
|
}, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this._mask.updateOptions(this.maskOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this._mask)
|
|
|
|
{
|
|
|
|
this.updateMaskValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected updateMaskValue()
|
|
|
|
{
|
|
|
|
this._mask.unmaskedValue = "" + this.value;
|
|
|
|
this._mask.updateValue();
|
|
|
|
this.updateComplete.then(() =>
|
|
|
|
{
|
|
|
|
this._mask.updateControl();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
protected handleFocus(event)
|
|
|
|
{
|
|
|
|
if(this._mask)
|
|
|
|
{
|
|
|
|
// 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>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
*/
|
|
|
|
}
|