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.

316 lines (315 loc) 17.4 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-DEQ4y-jn.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\n label {\n margin: var(--wje-textarea-label-margin);\n padding: var(--wje-textarea-label-padding);\n display: var(--wje-textarea-label-display);\n opacity: 1;\n cursor: text;\n transition: opacity 0.2s ease;\n line-height: var(--wje-textarea-label-line-height);\n font-size: var(--wje-textarea-label-font-size);\n }\n\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 margin: var(--wje-textarea-label-margin);\n padding: var(--wje-textarea-label-padding);\n display: var(--wje-textarea-label-display);\n opacity: 1;\n cursor: text;\n transition: opacity 0.2s ease;\n line-height: var(--wje-textarea-label-line-height);\n font-size: var(--wje-textarea-label-font-size);\n }\n /*label {*/\n /* padding: 0 var(--wje-textarea-padding);*/\n /* display: block;*/\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: var(--wje-textarea-label-margin);*/\n /* display: inline-block;*/\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-color: var(--wje-textarea-error-background-color, var(--wje-tooltip-background));\n padding: 0.25rem 0.5rem;\n top: 0;\n left: 50%;\n transform: translate(-50%, -50%);\n color: var(--wje-textarea-error-color, var(--wje-tooltip-color));\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"; const _Textarea = 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; this._instanceId = ++_Textarea._instanceId; } /** * 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; this.syncAria(); } /** * 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 label attribute of the element. * @param {string} value The value to set as the label attribute. */ set label(value) { if (value === null || value === void 0) { this.removeAttribute("label"); } else { this.setAttribute("label", value); } } /** * Retrieves the value of the 'label' attribute if it exists. * If the 'label' attribute is not set, it returns false. * @returns {string|boolean} The value of the 'label' attribute as a string, or false if the attribute is not set. */ get label() { return this.getAttribute("label") || false; } /** * 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 ["value", "name", "disabled", "placeholder", "label", "required", "readonly", "invalid", "rows"]; } /** * Sets up the attributes for the component. */ setupAttributes() { this.isShadowRoot = "open"; if (this.pristine) { const attrValue = this.getAttribute("value"); this.value = attrValue !== null ? attrValue : this.innerHTML; this.pristine = false; } this.syncAria(); } attributeChangedCallback(name, oldValue, newValue) { if (oldValue === newValue) return; if (name === "label") { this.refresh(); return; } if (!this.input) { this.syncAria(); return; } if (name === "value") { this._value = newValue ?? ""; this.input.value = this.value; this.internals.setFormValue(this.value); } else if (name === "name") { this.input.name = this.name; } else if (name === "disabled") { this.input.disabled = this.hasAttribute("disabled"); } else if (name === "required") { this.input.required = this.required; } else if (name === "readonly") { this.input.readOnly = this.hasAttribute("readonly"); } else if (name === "placeholder") { this.input.placeholder = this.placeholder || ""; } else if (name === "rows") { this.input.rows = Number(newValue || 3); } this.syncAria(); } /** * 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.rows = Number(this.getAttribute("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"); this._ariaErrorId = this.id ? `${this.id}-error` : `wje-textarea-${this._instanceId}-error`; errorSlot.id = this._ariaErrorId; if (this.getAttribute("resize") === "auto") input.addEventListener("input", this.setTextareaHeight); if (this.label) { if (this.variant === "standard") { 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; this.syncAria(); return fragment; } /** * Sets up the event listeners after the component is drawn. */ afterDraw() { if (this.getAttribute("resize") === "auto" && typeof ResizeObserver === "function") { this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight()); this.resizeObserver.observe(this.input); } 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(); } if (this.invalid) { this.invalid = false; this.internals.setValidity({}, ""); } 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("invalid", (e) => { this.invalid = true; this.pristine = false; this.showInvalidMessage(); if (this.customErrorDisplay) { e.preventDefault(); } }); this.validate(); this.syncAria(); } /** * Syncs ARIA attributes on the host element. */ syncAria() { const requiredInvalid = this.required && !this.value; const invalid = this.invalid || requiredInvalid; const label = this.label && this.label !== false ? this.label.trim() : ""; this.setAriaState({ role: "textbox", disabled: this.disabled, required: this.required, readonly: this.hasAttribute("readonly"), invalid, describedBy: this._ariaErrorId, ...label ? { label } : {} }); } componentCleanup() { var _a, _b; (_a = this.resizeObserver) == null ? void 0 : _a.unobserve(this.input); (_b = this.resizeObserver) == null ? void 0 : _b.disconnect(); } /** * Disconnects the component. */ beforeDisconnect() { var _a, _b; (_a = this.resizeObserver) == null ? void 0 : _a.unobserve(this.input); (_b = this.resizeObserver) == null ? void 0 : _b.disconnect(); } }; __publicField(_Textarea, "_instanceId", 0); let Textarea = _Textarea; Textarea.define("wje-textarea", Textarea); export { Textarea as default }; //# sourceMappingURL=wje-textarea.js.map