/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { isServer } from 'lit';
import { internals } from './element-internals.js';
/**
* A symbol property used to create a constraint validation `Validator`.
* Required for all `mixinConstraintValidation()` elements.
*/
export const createValidator = Symbol('createValidator');
/**
* A symbol property used to return an anchor for constraint validation popups.
* Required for all `mixinConstraintValidation()` elements.
*/
export const getValidityAnchor = Symbol('getValidityAnchor');
// Private symbol members, used to avoid name clashing.
const privateValidator = Symbol('privateValidator');
const privateSyncValidity = Symbol('privateSyncValidity');
const privateCustomValidationMessage = Symbol('privateCustomValidationMessage');
/**
* Mixes in constraint validation APIs for an element.
*
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation
* for more details.
*
* Implementations must provide a validator to cache and compute its validity,
* along with a shadow root element to anchor validation popups to.
*
* @example
* ```ts
* const baseClass = mixinConstraintValidation(
* mixinFormAssociated(mixinElementInternals(LitElement))
* );
*
* class MyCheckbox extends baseClass {
* \@property({type: Boolean}) checked = false;
* \@property({type: Boolean}) required = false;
*
* [createValidator]() {
* return new CheckboxValidator(() => this);
* }
*
* [getValidityAnchor]() {
* return this.renderRoot.querySelector('.root');
* }
* }
* ```
*
* @param base The class to mix functionality into.
* @return The provided class with `ConstraintValidation` mixed in.
*/
export function mixinConstraintValidation(base) {
var _a;
class ConstraintValidationElement extends base {
constructor() {
super(...arguments);
/**
* Needed for Safari, see https://bugs.webkit.org/show_bug.cgi?id=261432
* Replace with this[internals].validity.customError when resolved.
*/
this[_a] = '';
}
get validity() {
this[privateSyncValidity]();
return this[internals].validity;
}
get validationMessage() {
this[privateSyncValidity]();
return this[internals].validationMessage;
}
get willValidate() {
this[privateSyncValidity]();
return this[internals].willValidate;
}
checkValidity() {
this[privateSyncValidity]();
return this[internals].checkValidity();
}
reportValidity() {
this[privateSyncValidity]();
return this[internals].reportValidity();
}
setCustomValidity(error) {
this[privateCustomValidationMessage] = error;
this[privateSyncValidity]();
}
requestUpdate(name, oldValue, options) {
super.requestUpdate(name, oldValue, options);
this[privateSyncValidity]();
}
firstUpdated(changed) {
super.firstUpdated(changed);
// Sync the validity again when the element first renders, since the
// validity anchor is now available.
//
// Elements that `delegatesFocus: true` to an `<input>` will throw an
// error in Chrome and Safari when a form tries to submit or call
// `form.reportValidity()`:
// "An invalid form control with name='' is not focusable"
//
// The validity anchor MUST be provided in `internals.setValidity()` and
// MUST be the `<input>` element rendered.
//
// See https://lit.dev/playground/#gist=6c26e418e0010f7a5aac15005cde8bde
// for a reproduction.
this[privateSyncValidity]();
}
[(_a = privateCustomValidationMessage, privateSyncValidity)]() {
if (isServer) {
return;
}
if (!this[privateValidator]) {
this[privateValidator] = this[createValidator]();
}
const { validity, validationMessage: nonCustomValidationMessage } = this[privateValidator].getValidity();
const customError = !!this[privateCustomValidationMessage];
const validationMessage = this[privateCustomValidationMessage] || nonCustomValidationMessage;
this[internals].setValidity({ ...validity, customError }, validationMessage, this[getValidityAnchor]() ?? undefined);
}
[createValidator]() {
throw new Error('Implement [createValidator]');
}
[getValidityAnchor]() {
throw new Error('Implement [getValidityAnchor]');
}
}
return ConstraintValidationElement;
}
//# sourceMappingURL=constraint-validation.js.map