diff --git a/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts b/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts index fc0ad642e5..35963e7266 100644 --- a/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts +++ b/api/js/etemplate/Et2InputWidget/Et2InputWidget.ts @@ -1,8 +1,9 @@ import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "../et2_core_interfaces"; import {Et2Widget} from "../Et2Widget/Et2Widget"; import {css, dedupeMixin, LitElement, PropertyValues} from "@lion/core"; -import {ManualMessage} from "../Validators/ManualMessage"; import {Required} from "../Validators/Required"; +import {ManualMessage} from "../Validators/ManualMessage"; +import {LionValidationFeedback} from "@lion/form-core"; /** * This mixin will allow any LitElement to become an Et2InputWidget @@ -320,9 +321,100 @@ const Et2InputWidgetMixin = >(superclass : T) } } + /** + * Massively simplified validate, as compared to what ValidatorMixin gives us, since ValidatorMixin extends + * FormControlMixin which breaks SlSelect's render() + */ + async validate() + { + let validators = [...(this.validators || []), ...(this.defaultValidators || [])]; + let fieldName = this.id; + let feedbackData = []; + let resultPromises = []; + this.querySelector("lion-validation-feedback")?.remove(); + const doValidate = async function(validator, value) + { + if(validator.config.fieldName) + { + fieldName = await validator.config.fieldName; + } + // @ts-ignore [allow-protected] + return validator._getMessage({ + modelValue: value, + formControl: this, + fieldName, + }).then((message) => + { + feedbackData.push({message, type: validator.type, validator}); + }); + }.bind(this); + const doCheck = async(value, validator) => + { + const result = validator.execute(value, validator.param, {node: this}); + if(result === true) + { + resultPromises.push(doValidate(validator, value)); + } + else if(result !== false && typeof result.then === 'function') + { + result.then(doValidate(validator, value)); + resultPromises.push(result); + } + }; + + validators.map(async validator => + { + let values = this.value; + if(!Array.isArray(values)) + { + values = [values]; + } + if(!values.length) + { + values = ['']; + } // so required validation works + + // Run manual validation messages just once, doesn't usually matter what the value is + if(validator instanceof ManualMessage) + { + doCheck(values, validator); + } + else + { + // Validate each individual item + values.forEach((value) => doCheck(value, validator)); + } + }); + await Promise.all(resultPromises); + + if(feedbackData.length > 0) + { + let feedback = document.createElement("lion-validation-feedback"); + feedback.feedbackData = feedbackData; + feedback.slot = "help-text"; + this.append(feedback); + // Not always visible? + (this.shadowRoot.querySelector("#help-text")).style.display = "initial"; + } + } + set_validation_error(err : string | false) { - // ToDo - implement Lion validators properly, most likely by adding to this.validators + /* Shoelace uses constraint validation API + https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-constraint-validation-api + + if(err === false && this.setCustomValidity) + { + // Remove custom validity + this.setCustomValidity(''); + return; + } + this.setCustomValidity(err); + + // must call reportValidity() or nothing will happen + this.reportValidity(); + + */ if(err === false) { diff --git a/api/js/etemplate/Et2Select/Et2Select.ts b/api/js/etemplate/Et2Select/Et2Select.ts index 614894e741..27be048674 100644 --- a/api/js/etemplate/Et2Select/Et2Select.ts +++ b/api/js/etemplate/Et2Select/Et2Select.ts @@ -18,7 +18,6 @@ import {Et2WithSearchMixin} from "./SearchMixin"; import {Et2Tag} from "./Tag/Et2Tag"; import {LionValidationFeedback} from "@lion/form-core"; import {RowLimitedMixin} from "../Layout/RowLimitedMixin"; -import {ManualMessage} from "../Validators/ManualMessage"; // export Et2WidgetWithSelect which is used as type in other modules export class Et2WidgetWithSelect extends RowLimitedMixin(Et2widgetWithSelectMixin(SlSelect)) @@ -437,83 +436,6 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect) return [...this.querySelectorAll(this.optionTag)]; } - /** - * Massively simplified validate, as compared to what ValidatorMixin gives us, since ValidatorMixin extends - * FormControlMixin which breaks SlSelect's render() - */ - async validate() - { - let validators = [...(this.validators || []), ...(this.defaultValidators || [])]; - let fieldName = this.id; - let feedbackData = []; - let resultPromises = []; - this.querySelector("lion-validation-feedback")?.remove(); - const doValidate = async function(validator, value) - { - if(validator.config.fieldName) - { - fieldName = await validator.config.fieldName; - } - // @ts-ignore [allow-protected] - return validator._getMessage({ - modelValue: value, - formControl: this, - fieldName, - }).then((message) => - { - feedbackData.push({message, type: validator.type, validator}); - }); - }.bind(this); - const doCheck = async(value, validator) => - { - const result = validator.execute(value, validator.param, {node: this}); - if(result === true) - { - resultPromises.push(doValidate(validator, value)); - } - else if(result !== false && typeof result.then === 'function') - { - result.then(doValidate(validator, value)); - resultPromises.push(result); - } - }; - - validators.map(async validator => - { - let values = this.value; - if(!Array.isArray(values)) - { - values = [values]; - } - if(!values.length) - { - values = ['']; - } // so required validation works - - // Run manual validation messages just once, doesn't usually matter what the value is - if(validator instanceof ManualMessage) - { - doCheck(values, validator); - } - else - { - // Validate each individual item - values.forEach((value) => doCheck(value, validator)); - } - }); - await Promise.all(resultPromises); - - if(feedbackData.length > 0) - { - let feedback = document.createElement("lion-validation-feedback"); - feedback.feedbackData = feedbackData; - feedback.slot = "help-text"; - this.append(feedback); - // Not always visible? - (this.shadowRoot.querySelector("#help-text")).style.display = "initial"; - } - } - /** * Override parent to always call validate(), as our simple implementation needs to validate on clear as well. * diff --git a/api/js/etemplate/Et2Textbox/Et2Textbox.ts b/api/js/etemplate/Et2Textbox/Et2Textbox.ts index 59a80f7742..244fcc46a0 100644 --- a/api/js/etemplate/Et2Textbox/Et2Textbox.ts +++ b/api/js/etemplate/Et2Textbox/Et2Textbox.ts @@ -10,22 +10,30 @@ import {css, PropertyValues} from "@lion/core"; -import {LionInput} from "@lion/input"; -import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import {Regex} from "../Validators/Regex"; +import {SlInput} from "@shoelace-style/shoelace"; +import shoelace from "../Styles/shoelace"; +import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; -export class Et2Textbox extends Et2InputWidget(LionInput) +export class Et2Textbox extends Et2InputWidget(SlInput) { static get styles() { return [ + ...shoelace, ...super.styles, css` - :host([type="hidden"]) { - display: none; - } - `, + :host([type="hidden"]) { + display: none; + } + .input__control { + border: none; + } + .input:hover:not(.input--disabled) .input__control { + color: var(--input-text-color, inherit); + } + `, ]; } diff --git a/api/js/etemplate/Styles/shoelace.ts b/api/js/etemplate/Styles/shoelace.ts index 8be1b790ca..8b272fcc4d 100644 --- a/api/js/etemplate/Styles/shoelace.ts +++ b/api/js/etemplate/Styles/shoelace.ts @@ -16,7 +16,7 @@ registerIconLibrary('default', { /** * Customise shoelace styles to match our stuff - * External CSS will override this + * External CSS & widget styles will override this */ export default [sl_css, css` :root, diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 52237aabcc..3400e4cffd 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -2102,6 +2102,8 @@ div.message.floating, lion-validation-feedback[type] { position: absolute; margin: 0px; z-index: 1; + top: 100%; + left: 0px; } .message.validation_error, lion-validation-feedback[type="error"] {