import BaseCustomElement from "./BaseCustomElement"
import RichString from "./RichString"
import I18nTranslation from "./I18nTranslation"
/** Like {@link Message} but specific to constraint violations of input fields. This accepts the name
* of an input field via `input-name`, which can be used to locate the field's localized name.
*
* Here is how the field's name is determined:
*
* 1. It will look for a `<brut-i18n-translation>` element with the `key` `cv.fe.fieldNames.«input-name»`.
* 2. If that's not found, it will attempt to use "this field" by locating a `<brut-i18n-translation>` element with the `key`
* `cv.this_field` (the underscore being what is used on Brut's server side).
* 3. If that is not found, it will use the literaly string "this field" and emit a console warning.
*
* @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or
* the element will not render any text.
* @property {string} input-name - the name of the input, used to insert into the message, e.g. "Title is required".
* @property {boolean} server-side if true, this indicates the element contains constraint violation messages
* from the server. Currently doesn't affect this element's behavior, however
* AjaxSubmit will use it to locate where it should insert server-side errors.
*
* @see I18nTranslation
* @see ConstraintViolationMessages
* @see Message
*
* @customElement brut-cv
*/
class ConstraintViolationMessage extends BaseCustomElement {
static tagName = "brut-cv"
static observedAttributes = [
"show-warnings",
"key",
"input-name",
"server-side",
]
static createElement(document,attributes) {
const element = document.createElement(ConstraintViolationMessage.tagName)
element.setAttribute("key",this.i18nKey("fe", attributes.key))
element.setAttribute("input-name",attributes["input-name"])
if (Object.hasOwn(attributes,"show-warnings")) {
element.setAttribute("show-warnings",attributes["show-warnings"])
}
return element
}
static markServerSide(element) {
if (element.tagName.toLowerCase() == this.tagName) {
element.setAttribute("server-side", true)
}
}
static clientSideSelector() {
return `${this.tagName}:not([server-side])`
}
static serverSideSelector() {
return `${this.tagName}[server-side]`
}
/** Returns the I18N key used for front-end constraint violations. This is useful
* if you need to construct a key and want to follow Brut's conventions on how they
* are managed.
*
* @param {...String} keyPath - parts of the path of the key after the namespace that Brut manages.
*/
static i18nKey(...keyPath) {
const path = [ "cv" ]
return path.concat(keyPath).join(".")
}
#key = null
#inputNameKey = null
#thisFieldKey = this.#i18nKey("this_field")
keyChangedCallback({newValue}) {
this.#key = newValue
}
inputNameChangedCallback({newValue}) {
this.#inputNameKey = this.#i18nKey("fe", "fieldNames", newValue)
}
serverSideChangedCallback({newValueAsBoolean}) {
// attribute listed for documentation purposes only
}
update() {
if (!this.#key) {
this.logger.info("No key attribute, so can't do anything")
return
}
const selector = `${I18nTranslation.tagName}[key='${this.#key}']`
const translation = document.querySelector(selector)
if (!translation) {
this.logger.info("Could not find translation based on selector '%s'",selector)
return
}
const fieldNameSelector = `${I18nTranslation.tagName}[key='${this.#inputNameKey}']`
const thisFieldSelector = `${I18nTranslation.tagName}[key='${this.#thisFieldKey}']`
let fieldNameTranslation = document.querySelector(fieldNameSelector)
if (!fieldNameTranslation) {
this.logger.info("Could not find translation for input/field name based on selector '%s'. Will try 'this field' fallback",fieldNameSelector)
fieldNameTranslation = document.querySelector(thisFieldSelector)
if (!fieldNameTranslation) {
this.logger.info("Could not find translation for 'this field' fallback key, based on selector '%s'",thisFieldSelector)
}
}
const fieldName = fieldNameTranslation ? fieldNameTranslation.translation() : "this field"
this.textContent = RichString.fromString(translation.translation({ field: fieldName })).capitalize().toString()
}
/** Helper that calls the static version */
#i18nKey(...keyPath) {
return this.constructor.i18nKey(...keyPath)
}
}
export default ConstraintViolationMessage