From ae5a604f5c8394a9d21c752e4edecd4d1b8d3db5 Mon Sep 17 00:00:00 2001 From: pk Date: Mon, 10 Jun 2024 23:11:36 +0200 Subject: [PATCH] First version --- package-lock.json | 122 ++++++++++++++++++++++++++ package.json | 11 +-- tp-element.js | 35 -------- tp-textarea.js | 213 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 341 insertions(+), 40 deletions(-) create mode 100644 package-lock.json delete mode 100644 tp-element.js create mode 100644 tp-textarea.js diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a7276c3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,122 @@ +{ + "name": "@tp/tp-textarea", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@tp/tp-textarea", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@tp/helpers": "^2.2.0", + "lit": "^3.0.0" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", + "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, + "node_modules/@tp/helpers": { + "version": "2.2.0", + "resolved": "https://gitea.codeblob.work/api/packages/tp-elements/npm/%40tp%2Fhelpers/-/2.2.0/helpers-2.2.0.tgz", + "integrity": "sha512-kf+ieedMdYZOnQ6xUUQxTlP8cQEp4obpSs3vI7V/3xnex7VvMbiWzgFHU+wr1D2eR/IA0Cl1cZxvJ3E052EQew==", + "license": "Apache-2.0" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "node_modules/lit": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.4.tgz", + "integrity": "sha512-q6qKnKXHy2g1kjBaNfcoLlgbI3+aSOZ9Q4tiGa9bGYXq5RBXxkVTqTIVmP2VWMp29L4GyvCFm8ZQ2o56eUAMyA==", + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.0.4", + "lit-html": "^3.1.2" + } + }, + "node_modules/lit-element": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.6.tgz", + "integrity": "sha512-U4sdJ3CSQip7sLGZ/uJskO5hGiqtlpxndsLr6mt3IQIjheg93UKYeGQjWMRql1s/cXNOaRrCzC2FQwjIwSUqkg==", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.1.2" + } + }, + "node_modules/lit-html": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.4.tgz", + "integrity": "sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + } + }, + "dependencies": { + "@lit-labs/ssr-dom-shim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", + "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" + }, + "@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "requires": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, + "@tp/helpers": { + "version": "2.2.0", + "resolved": "https://gitea.codeblob.work/api/packages/tp-elements/npm/%40tp%2Fhelpers/-/2.2.0/helpers-2.2.0.tgz", + "integrity": "sha512-kf+ieedMdYZOnQ6xUUQxTlP8cQEp4obpSs3vI7V/3xnex7VvMbiWzgFHU+wr1D2eR/IA0Cl1cZxvJ3E052EQew==" + }, + "@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "lit": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.4.tgz", + "integrity": "sha512-q6qKnKXHy2g1kjBaNfcoLlgbI3+aSOZ9Q4tiGa9bGYXq5RBXxkVTqTIVmP2VWMp29L4GyvCFm8ZQ2o56eUAMyA==", + "requires": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.0.4", + "lit-html": "^3.1.2" + } + }, + "lit-element": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.6.tgz", + "integrity": "sha512-U4sdJ3CSQip7sLGZ/uJskO5hGiqtlpxndsLr6mt3IQIjheg93UKYeGQjWMRql1s/cXNOaRrCzC2FQwjIwSUqkg==", + "requires": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.1.2" + } + }, + "lit-html": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.4.tgz", + "integrity": "sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==", + "requires": { + "@types/trusted-types": "^2.0.2" + } + } + } +} diff --git a/package.json b/package.json index c39fdff..9f16107 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { - "name": "@tp/tp-element", - "version": "0.0.1", + "name": "@tp/tp-textarea", + "version": "1.0.0", "description": "", - "main": "tp-element.js", + "main": "tp-textarea.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", - "url": "https://gitea.codeblob.work/tp-elements/tp-element.git" + "url": "https://gitea.codeblob.work/tp-textareas/tp-textarea.git" }, "author": "trading_peter", "license": "Apache-2.0", "dependencies": { - "lit": "^2.2.0" + "@tp/helpers": "^2.2.0", + "lit": "^3.0.0" } } diff --git a/tp-element.js b/tp-element.js deleted file mode 100644 index 6a92a2f..0000000 --- a/tp-element.js +++ /dev/null @@ -1,35 +0,0 @@ -/** -@license -Copyright (c) 2022 trading_peter -This program is available under Apache License Version 2.0 -*/ - -import { LitElement, html, css } from 'lit'; - -class TpElement extends LitElement { - static get styles() { - return [ - css` - :host { - display: block; - } - ` - ]; - } - - render() { - const { } = this; - - return html` - - `; - } - - static get properties() { - return { }; - } - - -} - -window.customElements.define('tp-element', TpElement); diff --git a/tp-textarea.js b/tp-textarea.js new file mode 100644 index 0000000..7c3b520 --- /dev/null +++ b/tp-textarea.js @@ -0,0 +1,213 @@ +/** +@license +Copyright (c) 2024 trading_peter +This program is available under Apache License Version 2.0 +*/ + +import { FormElement } from '@tp/helpers/form-element.js'; +import { DomQuery } from '@tp/helpers/dom-query.js'; +import { LitElement, html, css } from 'lit'; + +class TpElement extends FormElement(DomQuery(LitElement)) { + static get styles() { + return [ + css` + :host { + display: inline-block; + position: relative; + width: 400px; + border: solid 1px #9E9E9E; + overflow: hidden; + } + + :host([invalid]) { + border: solid 1px #B71C1C; + } + + .error-message { + position: absolute; + bottom: 0; + left: 0; + right: 0; + font-size: 10px; + color: var(--tp-textarea-text-color-invalid, #B71C1C); + transition: opacity 0.3s; + opacity: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + pointer-events: none; + background: #ffffff; + } + + :host([invalid]) .error-message { + opacity: 1; + } + + .wrap { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + .mirror-text { + visibility: hidden; + word-wrap: break-word; + } + + .wrap ::slotted(textarea) { + position: relative; + outline: none; + border: none; + resize: none; + background: inherit; + color: inherit; + width: 100%; + height: 100%; + font-size: inherit; + font-family: inherit; + line-height: inherit; + text-align: inherit; + box-sizing: border-box; + overflow: hidden; + padding: 0; + } + ` + ]; + } + + render() { + const { errorMessage } = this; + + return html` + + +
+ +
+ +
${errorMessage}
+ `; + } + + static get properties() { + return { + readonly: { type: Boolean }, + errorMessage: { type: String }, + invalid: { type: Boolean, reflect: true }, + }; + } + + connectedCallback() { + super.connectedCallback(); + + this.addEventListener('input', this.onInput); + } + + disconnectedCallback() { + super.disconnectedCallback(); + + this.removeEventListener('input', this.onInput); + } + + shouldUpdate(changes) { + if (changes.has('value')) { + this._syncToTextarea(); + } + + if (changes.has('readonly')) { + this._readOnlyChanged(); + } + + return true; + } + + get textarea() { + return this.querySelector('textarea'); + } + + get selectionStart() { + return this.textarea.selectionStart; + } + + get selectionEnd() { + return this.textarea.selectionEnd; + } + + validate() { + // Use the nested input's native validity. + let valid = this.textarea.validity.valid; + + // Only do extra checking if the browser thought this was valid. + if (valid) { + // Empty, required input is invalid + if (this.required && this.value === '') { + valid = false; + } else if (this.hasValidator()) { + valid = IronValidatableBehavior.validate.call(this, this.value); + } + } + + this.invalid = !valid; + return valid; + } + + reset() { + this.value = ''; + this.textarea.value = ''; + this.invalid = false; + } + + _syncToTextarea() { + if (typeof this.value !== 'string') { + return this.value = ''; + } + + if (this.textarea) { + this.textarea.value = this.value || ''; + this.$.mirror.innerHTML = this._valueForMirror(); + } + } + + _readOnlyChanged(state) { + this.textarea.readOnly = state; + } + + _onInput() { + this.$.mirror.innerHTML = this._valueForMirror(); + this.value = this.textarea.value; + } + + _constrain(tokens) { + let _tokens; + tokens = tokens || ['']; + // Enforce the min and max heights for a multiline input to avoid measurement + if (this.maxRows > 0 && tokens.length > this.maxRows) { + _tokens = tokens.slice(0, this.maxRows); + } else { + _tokens = tokens.slice(0); + } + while (this.rows > 0 && _tokens.length < this.rows) { + _tokens.push(''); + } + // Use   instead   of to allow this element to be used in XHTML. + return _tokens.join('
') + ' '; + } + + _valueForMirror() { + const input = this.textarea; + if (!input) { + return; + } + this.tokens = (input && input.value) ? input.value.replace(/&/gm, '&').replace(/"/gm, '"').replace(/'/gm, ''').replace(//gm, '>').split('\n') : ['']; + return this._constrain(this.tokens); + } + + _updateCached() { + this.$.mirror.innerHTML = this._constrain(this.tokens); + } +} + +window.customElements.define('tp-element', TpElement);