UNPKG

wj-elements

Version:

WebJET Elements is a modern set of user interface tools harnessing the power of web components designed to simplify web application development.

230 lines (229 loc) 13.6 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { F as FormAssociatedElement } from "./form-associated-element-o0UjvdUp.js"; import { event } from "./event.js"; const styles = "/*\n[ WJ Textarea ]\n*/\n\n:host {\n width: 100%;\n margin-bottom: var(--wje-textarea-margin-bottom);\n display: block;\n .wrapper {\n display: flex;\n width: 100%;\n border-width: var(--wje-textarea-border-width);\n border-style: var(--wje-textarea-border-style);\n border-color: var(--wje-textarea-border-color);\n border-radius: var(--wje-textarea-border-radius);\n box-sizing: border-box;\n }\n textarea {\n font-family: var(--wje-textarea-font-family);\n color: var(--wje-textarea-color);\n font-size: 14px;\n border: 0 none;\n padding: var(--wje-textarea-padding);\n &:focus {\n outline: none;\n }\n }\n}\n\n:host([invalid]) {\n .error-message {\n display: block;\n }\n .default {\n label {\n opacity: 1 !important;\n color: var(--wje-input-color-invalid) !important;\n animation-name: shake;\n animation-duration: 0.4s;\n animation-iteration-count: 1;\n }\n }\n}\n\n:host([required]) .wrapper::after {\n color: var(--wje-input-color-invalid);\n content: var(--wje-input-required-symbol);\n font-size: 24px;\n position: absolute;\n right: 12px;\n top: 0;\n}\n\n:host([required]) .standard .input-wrapper::after {\n right: 13px;\n top: -20px;\n}\n\n:host([resize='auto']) textarea,\n:host([resize='none']) textarea {\n resize: none;\n}\n\n.native-textarea {\n .input-wrapper {\n width: 100%;\n line-height: normal;\n }\n &.default {\n background-color: var(--wje-textarea-background-color);\n font-family: var(--wje-textarea-font-family);\n position: relative;\n padding-inline: 0;\n padding-top: 0;\n transition: background-color 0.2s ease;\n cursor: text;\n &.focused {\n .wrapper {\n border-color: var(--wje-textarea-border-color-focus) !important;\n }\n label {\n opacity: 0.67;\n font-size: 12px;\n letter-spacing: normal;\n }\n }\n textarea {\n border: none;\n padding-top: 0;\n background: none;\n box-shadow: none;\n width: calc(100% - var(--wje-textarea-padding) * 2);\n max-width: calc(100% - var(--wje-textarea-padding) * 2);\n min-width: calc(100% - var(--wje-textarea-padding) * 2);\n padding: 0 var(--wje-textarea-padding);\n }\n label {\n padding: 0 var(--wje-textarea-padding);\n display: block;\n opacity: 1;\n cursor: text;\n transition: opacity 0.2s ease;\n line-height: var(--wje-textarea-line-height);\n padding-top: 0.25rem;\n &.fade {\n opacity: 0.5;\n font-size: 12px;\n letter-spacing: normal;\n }\n }\n ::slotted([slot='start']) {\n border-left: none;\n border-top: none;\n border-bottom: none;\n }\n\n ::slotted([slot='end']) {\n border-right: none;\n border-top: none;\n border-bottom: none;\n }\n }\n &.standard {\n position: relative;\n border-radius: var(--wje-textarea-border-radius);\n padding: 0;\n transition: background-color 0.2s ease;\n cursor: text;\n &.focused {\n .wrapper {\n border-color: var(--wje-textarea-border-color-focus) !important;\n }\n }\n textarea {\n background-color: var(--wje-textarea-background-color);\n display: block;\n min-height: 32px;\n box-shadow: none;\n width: 100%;\n box-sizing: border-box;\n border-radius: var(--wje-textarea-border-radius);\n }\n label {\n margin: 0;\n display: inline-block;\n opacity: 1;\n cursor: text;\n transition: opacity 0.2s ease;\n line-height: var(--wje-textarea-line-height);\n }\n ::slotted([slot='start']) {\n border-right: none;\n border-radius: var(--wje-textarea-border-radius) 0 0 var(--wje-textarea-border-radius);\n }\n\n ::slotted([slot='end']) {\n border-left: none;\n border-radius: 0 var(--wje-textarea-border-radius) var(--wje-textarea-border-radius) 0;\n }\n\n &.has-start textarea {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n\n &.has-end textarea {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n slot[name='error'] {\n position: static;\n background: transparent;\n padding: 0.25rem 0;\n left: auto;\n transform: none;\n color: var(--wje-textarea-color-invalid);\n font-size: 12px;\n line-height: normal;\n }\n }\n}\n\n.counter {\n float: right;\n}\n\nslot[name='error'] {\n display: none;\n}\n\n:host([invalid]) slot[name='error'] {\n display: block;\n}\n\nslot[name='error'] {\n display: none;\n position: absolute;\n max-width: 100%;\n min-width: auto;\n border-radius: 50px;\n background: black;\n padding: 0.25rem 0.5rem;\n top: 0;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-size: var(--wje-font-size-small);\n width: max-content;\n line-height: normal;\n}\n\n@keyframes shake {\n 8%,\n 41% {\n transform: translateX(-4px);\n }\n 25%,\n 58% {\n transform: translateX(4px);\n }\n 75% {\n transform: translateX(-2px);\n }\n 92% {\n transform: translateX(2px);\n }\n 0%,\n 100% {\n transform: translateX(0);\n }\n}\n"; class Textarea extends FormAssociatedElement { /** * Creates an instance of Textarea. * @class */ constructor() { super(); __publicField(this, "className", "Textarea"); /** * Sets the height of the textarea. */ __publicField(this, "setTextareaHeight", () => { if (this.getAttribute("resize") === "auto") { this.input.style.height = "auto"; this.input.style.height = this.input.scrollHeight + "px"; } }); /** * Updates the counter for the textarea. * @param {Event} e The event object. */ __publicField(this, "counterFn", (e) => { this.counterElement.innerText = e.target.value.length + "/" + this.input.maxLength; }); this.invalid = false; this.pristine = true; } /** * Setter for the value attribute. * @param {string} value The value to set. */ set value(value) { this.internals.setFormValue(value); if (this.input) this.input.value = value; this.pristine = false; this._value = value; } /** * Getter for the value attribute. * @returns {string} The value of the attribute. */ get value() { var _a; return ((_a = this.input) == null ? void 0 : _a.value) ?? this._value ?? ""; } /** * Sets the `validateOnChange` property. If set to a truthy value, it adds the * `validate-on-change` attribute to the element. If set to a falsy value, it * removes the `validate-on-change` attribute from the element. * @param {boolean} value Determines whether to add or remove the * `validate-on-change` attribute. A truthy value adds the attribute, whereas a * falsy value removes it. */ set validateOnChange(value) { if (value) { this.setAttribute("validate-on-change", ""); } else { this.removeAttribute("validate-on-change"); } } /** * Getter for the validateOnChange attribute. * @returns {boolean} Whether the attribute is present. */ get validateOnChange() { return this.hasAttribute("validate-on-change"); } set placeholder(value) { this.setAttribute("placeholder", value); } get placeholder() { return this.getAttribute("placeholder"); } /** * Returns the CSS styles for the component. * @static * @returns {CSSStyleSheet} The CSS stylesheet */ static get cssStyleSheet() { return styles; } /** * Returns the list of attributes to observe for changes. * @static * @returns {Array<string>} */ static get observedAttributes() { return []; } /** * Sets up the attributes for the component. */ setupAttributes() { this.isShadowRoot = "open"; if (this.pristine) { this.value = this.innerHTML; this.pristine = false; } } /** * Draws the component for the textarea. * @returns {DocumentFragment} */ draw() { let fragment = document.createDocumentFragment(); let native = document.createElement("div"); native.classList.add("native-textarea", this.variant || "default"); native.setAttribute("part", "native"); if (this.hasAttribute("invalid")) native.classList.add("has-error"); let wrapper = document.createElement("div"); wrapper.setAttribute("part", "wrapper"); wrapper.classList.add("wrapper"); let inputWrapper = document.createElement("div"); inputWrapper.classList.add("input-wrapper"); let label = document.createElement("label"); label.setAttribute("part", "label"); label.htmlFor = "textarea"; label.innerHTML = this.label || ""; let input = document.createElement("textarea"); input.id = "textarea"; input.name = this.name; input.disabled = this.hasAttribute("disabled"); input.innerText = this.value; input.placeholder = this.placeholder || ""; input.classList.add("form-control"); input.setAttribute("part", "input"); input.setAttribute("rows", this.rows || 3); input.setAttribute("spellcheck", false); const attributes = Array.from(this.attributes).map((attr) => attr.name); attributes.forEach((attr) => { if (this.hasAttribute(attr)) { input.setAttribute(attr, this[attr] || ""); } }); let error = document.createElement("div"); error.setAttribute("slot", "error"); let errorSlot = document.createElement("slot"); errorSlot.setAttribute("name", "error"); if (this.resize === "auto") input.addEventListener("input", this.setTextareaHeight); if (this.variant === "standard") { if (this.label) native.appendChild(label); } else { inputWrapper.appendChild(label); } inputWrapper.appendChild(input); wrapper.appendChild(inputWrapper); native.appendChild(wrapper); native.append(errorSlot); this.append(error); fragment.appendChild(native); if (this.hasAttribute("counter")) { input.maxLength = this.maxLength || 1e3; input.addEventListener("input", this.counterFn); let counter = document.createElement("div"); counter.classList.add("counter"); counter.innerText = `${input.value.length}/${input.maxLength}`; this.counterElement = counter; fragment.appendChild(counter); } this.native = native; this.labelElement = label; this.input = input; return fragment; } /** * Sets up the event listeners after the component is drawn. */ afterDraw() { this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight); if (!this.hasAttribute("disabled")) { event.addListener(this, "click", "wje-textarea:change"); event.addListener(this, "click", "wje-textarea:input"); } this.input.addEventListener("focus", (e) => { this.labelElement.classList.add("fade"); this.native.classList.add("focused"); }); this.input.addEventListener("blur", (e) => { this.native.classList.remove("focused"); if (!e.target.value) this.labelElement.classList.remove("fade"); }); this.input.addEventListener("input", (e) => { this.validate(); if (this.validateOnChange) { this.pristine = false; this.propagateValidation(); } this.input.classList.remove("pristine"); this.labelElement.classList.add("fade"); const clone = new e.constructor(e.type, e); this.dispatchEvent(clone); event.dispatchCustomEvent(this, "wje-textarea:input", { value: this.input.value }); this.value = this.input.value; }); this.addEventListener("wje-textarea:invalid", (e) => { this.invalid = true; this.pristine = false; this.showInvalidMessage(); if (this.customErrorDisplay) { e.preventDefault(); } }); this.validate(); } componentCleanup() { var _a; (_a = this.resizeObserver) == null ? void 0 : _a.unobserve(this.input); } /** * Disconnects the component. */ beforeDisconnect() { var _a; (_a = this.resizeObserver) == null ? void 0 : _a.unobserve(this.input); } } Textarea.define("wje-textarea", Textarea); export { Textarea as default }; //# sourceMappingURL=wje-textarea.js.map