@synergy-design-system/components
Version:
This package provides the base of the Synergy Design System as native web components. It uses [lit](https://www.lit.dev) and parts of [shoelace](https://shoelace.style/). Synergy officially supports the latest two versions of all major browsers (as define
365 lines (361 loc) • 11.1 kB
JavaScript
import {
alertSizeForInput,
getEventNameForElement,
isBlurEvent,
isInvalidEvent,
isSynergyElement,
normalizeEventAttribute
} from "./chunk.2LRU3I3N.js";
import {
validate_styles_default
} from "./chunk.MVM3NN6Y.js";
import {
SynAlert
} from "./chunk.LLOONJ33.js";
import {
enableDefaultSettings
} from "./chunk.HYFCK7MM.js";
import {
watch
} from "./chunk.BVZQ6QSY.js";
import {
component_styles_default
} from "./chunk.NLYVOJGK.js";
import {
SynergyElement
} from "./chunk.3THJTCRO.js";
import {
__decorateClass
} from "./chunk.Z4XV3SMG.js";
// src/components/validate/validate.component.ts
import { html } from "lit";
import { property, queryAssignedElements, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
var SynValidate = class extends SynergyElement {
constructor() {
super(...arguments);
this.controller = new AbortController();
this.validationMessage = "";
this.eagerFirstMount = true;
this.isInternalTriggeredInvalid = false;
this.isValid = true;
this.variant = "native";
this.hideIcon = false;
this.on = "";
this.customValidationMessage = "";
this.eager = false;
/**
* Set the validation message from the input element
* @param e The event that was received
*/
this.internalRevalidate = (e) => {
var _a;
const input = e.currentTarget;
if ((_a = input.validity) == null ? void 0 : _a.valid) {
this.validationMessage = "";
}
};
/**
* Triggers a validation run, showing the validation message if needed.
*/
// eslint-disable-next-line complexity
this.validate = async (e) => {
var _a;
if (isInvalidEvent(e.type) && this.variant === "native" && this.isInternalTriggeredInvalid === true) {
this.isInternalTriggeredInvalid = false;
return;
}
if (isInvalidEvent(e.type) && this.variant !== "native") {
e.preventDefault();
e.stopPropagation();
}
const input = e.currentTarget;
if (isSynergyElement(input)) {
await input.updateComplete;
}
this.isValid = (_a = input.validity) == null ? void 0 : _a.valid;
if (this.eager && this.eagerFirstMount) {
this.eagerFirstMount = false;
this.setValidationMessage(input);
return;
}
if (!this.isValid && !isBlurEvent(e.type)) {
this.handleFocus(input);
}
this.setValidationMessage(input);
if (!isBlurEvent(e.type) && this.variant === "native") {
this.updateComplete.then(() => {
this.isInternalTriggeredInvalid = true;
input.reportValidity();
});
}
};
}
handleListenerChange() {
this.updateEvents();
}
async handleEagerChange() {
if (this.eager) {
const input = this.getInput();
await this.updateComplete;
input == null ? void 0 : input.reportValidity();
this.eagerFirstMount = true;
} else {
this.eagerFirstMount = false;
}
}
handleCustomValidationMessageChange() {
const input = this.getInput();
if (input) {
this.setCustomValidationMessage(input);
this.setValidationMessage(input);
}
}
/**
* Returns the validity state of the input component.
* `true` for valid and `false` for invalid.
*/
getValidity() {
return this.isValid;
}
/**
* Get the input element to validate. Defined as the first slotted element
* @returns The input element or undefined if not found
*/
getInput() {
const input = this.slottedChildren[0];
return input ? input : void 0;
}
setAlertSize() {
this.alertSize = alertSizeForInput(this.getInput());
}
/**
* Get the event names to listen for.
* If the input is a synergy element, will use syn- prefixes.
* @returns The event names to listen for
*/
// eslint-disable-next-line complexity
getUsedEventNames() {
const input = this.getInput();
if (!input) {
return [];
}
const on = normalizeEventAttribute(this.on);
const [...events] = on.filter(Boolean);
if (!events.includes("invalid")) {
events.push("invalid");
}
if (events.includes("live")) {
events.push("input");
events.push("blur");
}
return Array.from(new Set(
events.filter((e) => e !== "live").map((e) => getEventNameForElement(input, e))
));
}
/**
* Update the events on the input element.
*/
updateEvents() {
this.controller.abort();
this.controller = new AbortController();
const input = this.getInput();
if (!input) {
return;
}
const events = this.getUsedEventNames();
events.forEach((eventName) => {
input.addEventListener(eventName, this.validate, {
capture: isInvalidEvent(eventName),
signal: this.controller.signal
});
});
const usedChangeEvent = getEventNameForElement(input, "change");
if (!events.includes(usedChangeEvent)) {
input.addEventListener(usedChangeEvent, this.internalRevalidate, {
signal: this.controller.signal
});
}
}
setValidationMessage(input) {
const { customValidationMessage } = this;
const validationMessage = customValidationMessage || input.validationMessage;
this.validationMessage = validationMessage;
}
/**
* Set the custom validation message to the input. This will make sure to either:
* - use the custom message if one is set or
* - use the default message if the custom message is empty
*/
setCustomValidationMessage(input) {
input.setCustomValidity(this.customValidationMessage);
}
/**
* Handle the blur event during validation
*/
// eslint-disable-next-line class-methods-use-this
handleFocus(input) {
var _a;
const activeElement = document.activeElement;
const activeElementIsWrapped = activeElement.closest("syn-validate");
if (!((_a = activeElement.validity) == null ? void 0 : _a.valid) && activeElementIsWrapped) {
return;
}
input.scrollIntoView({ block: "nearest" });
input.focus();
}
async firstUpdated(changedProperties) {
var _a, _b;
super.firstUpdated(changedProperties);
this.updateEvents();
const input = this.getInput();
if (this.customValidationMessage) {
if (isSynergyElement(input)) {
await input.updateComplete;
}
input == null ? void 0 : input.setCustomValidity(this.customValidationMessage);
}
if (this.eager) {
await this.updateComplete;
this.isValid = (_b = (_a = input == null ? void 0 : input.validity) == null ? void 0 : _a.valid) != null ? _b : false;
input == null ? void 0 : input.reportValidity();
}
}
connectedCallback() {
super.connectedCallback();
this.sizeObserver = new MutationObserver((entries) => {
const input = this.getInput();
if (!input) {
return;
}
const hasSizeChanged = entries.filter(({ target }) => target === input).every(
(entry) => entry.attributeName === "size"
);
if (hasSizeChanged) {
this.setAlertSize();
}
});
this.sizeObserver.observe(this, {
attributeFilter: ["size"],
attributes: true,
subtree: true
});
this.observer = new MutationObserver((entries) => {
const input = this.getInput();
if (!input) {
return;
}
const hasDisabledOrReadonly = entries.filter(({ target }) => target === input).every((entry) => {
const target = entry.target;
return target.hasAttribute("disabled") || target.hasAttribute("readonly");
});
if (hasDisabledOrReadonly) {
this.isValid = true;
this.validationMessage = "";
} else {
const waitForPromise = isSynergyElement(input) ? input.updateComplete : Promise.resolve();
waitForPromise.then(() => {
var _a, _b, _c;
this.isValid = (_b = (_a = input == null ? void 0 : input.validity) == null ? void 0 : _a.valid) != null ? _b : false;
this.validationMessage = (_c = input == null ? void 0 : input.validationMessage) != null ? _c : "";
});
}
});
this.observer.observe(this, {
attributeFilter: ["disabled", "readonly"],
attributes: true,
subtree: true
});
}
disconnectedCallback() {
var _a, _b;
super.disconnectedCallback();
this.controller.abort();
(_a = this == null ? void 0 : this.observer) == null ? void 0 : _a.disconnect();
(_b = this == null ? void 0 : this.sizeObserver) == null ? void 0 : _b.disconnect();
}
renderInlineValidation() {
if (this.variant !== "inline" || !this.validationMessage) {
return "";
}
return html`
<syn-alert
open
exportparts="base:alert__base,message:alert__message,icon:alert__icon"
part="alert"
size=${ifDefined(this.alertSize)}
variant="danger"
>
${!this.hideIcon ? html`<syn-icon slot="icon" name="status-error" library="system"></syn-icon>` : ""}
${this.validationMessage}
</syn-alert>
`;
}
render() {
return html`
<div
class="validate"
part="base"
>
<slot
class="validate__input-wrapper"
part="input-wrapper"
></slot>
${this.renderInlineValidation()}
</div>
`;
}
};
SynValidate.styles = [component_styles_default, validate_styles_default];
SynValidate.dependencies = {
"syn-alert": SynAlert
};
__decorateClass([
queryAssignedElements()
], SynValidate.prototype, "slottedChildren", 2);
__decorateClass([
state()
], SynValidate.prototype, "validationMessage", 2);
__decorateClass([
state()
], SynValidate.prototype, "eagerFirstMount", 2);
__decorateClass([
state()
], SynValidate.prototype, "isInternalTriggeredInvalid", 2);
__decorateClass([
state()
], SynValidate.prototype, "isValid", 2);
__decorateClass([
state()
], SynValidate.prototype, "alertSize", 2);
__decorateClass([
property({ reflect: true })
], SynValidate.prototype, "variant", 2);
__decorateClass([
property({ attribute: "hide-icon", reflect: true, type: Boolean })
], SynValidate.prototype, "hideIcon", 2);
__decorateClass([
property({ reflect: true })
], SynValidate.prototype, "on", 2);
__decorateClass([
property({ attribute: "custom-validation-message", type: String })
], SynValidate.prototype, "customValidationMessage", 2);
__decorateClass([
property({ type: Boolean })
], SynValidate.prototype, "eager", 2);
__decorateClass([
watch("on", { waitUntilFirstUpdate: true })
], SynValidate.prototype, "handleListenerChange", 1);
__decorateClass([
watch("eager", { waitUntilFirstUpdate: false })
], SynValidate.prototype, "handleEagerChange", 1);
__decorateClass([
watch("customValidationMessage", { waitUntilFirstUpdate: true })
], SynValidate.prototype, "handleCustomValidationMessageChange", 1);
SynValidate = __decorateClass([
enableDefaultSettings("SynValidate")
], SynValidate);
export {
SynValidate
};
//# sourceMappingURL=chunk.F3KGIXQV.js.map