/**
*
* The Image component derives from [Component](kiss.ui.Component.html).
*
* @param {object} config
* @param {string} config.src - The image source URL
* @param {string} [config.alt] - Alternative text for the image
* @param {string} [config.caption] - Text to be displayed below the image
* @param {string} [config.objectFit] - fill (default) | contain | cover | scale-down | none
* @param {string} [config.position]
* @param {string} [config.top]
* @param {string} [config.left]
* @param {string} [config.right]
* @param {string} [config.float]
* @param {string} [config.display]
* @param {string|number} [config.width]
* @param {string|number} [config.height]
* @param {string|number} [config.minWidth]
* @param {string|number} [config.minHeight]
* @param {string|number} [config.maxWidth]
* @param {string|number} [config.maxHeight]
* @param {string} [config.margin]
* @param {string} [config.padding]
* @param {string} [config.border]
* @param {string} [config.borderStyle]
* @param {string} [config.borderWidth]
* @param {string} [config.borderColor]
* @param {string} [config.borderRadius]
* @param {string} [config.boxShadow]
* @param {string} [config.background]
* @param {string} [config.backgroundColor]
* @param {number} [config.zIndex]
* @param {boolean} [config.draggable]
* @param {string} [config.cursor]
* @param {object} [config.record] - The record to bind the field to. This will automatically update the record when the field value changes, and the field will listen to database changes on the record.
* @returns this
*
* ## Generated markup
* ```
* <a-image class="a-image">
* <img class="image-content">
* </a-image>
* ```
*/
kiss.ui.Image = class Image extends kiss.ui.Component {
/**
* Its a Custom Web Component. Do not use the constructor directly with the **new** keyword.
* Instead, use one of the 3 following methods:
*
* Create the Web Component and call its **init** method:
* ```
* const myImage = document.createElement("a-image").init(config)
* ```
*
* Or use the shorthand for it:
* ```
* const myImage = createImage({
* src: "./logo.png",
* alt: "Company logo"
* })
*
* myImage.render()
* ```
*
* Or directly declare the config inside a container component:
* ```
* const myPanel = createPanel({
* title: "My panel",
* items: [
* {
* type: "image",
* src: "./logo.png",
* alt: "Company logo"
* }
* ]
* })
* myPanel.render()
* ```
*/
constructor() {
super()
}
/**
* Generates an Image from a JSON config
*
* @ignore
* @param {object} config - JSON config
* @returns {HTMLElement}
*/
init(config) {
super.init(config)
this.alt = config.alt || ""
this.caption = config.caption || ""
config.src = config.src || this._emptyImage()
if (!config.caption) {
// Template without caption
this.innerHTML = `<img id="image-content-${this.id}" ${(config.src) ? 'src="' + config.src + '"' : ""} ${(config.alt) ? `alt="${config.alt}"` : ""} class="image-content" loading="lazy">`
}
else {
// Template with caption
this.innerHTML =
`<figure>
<img id="image-content-${this.id}" ${(config.src) ? 'src="' + config.src + '"' : ""} ${(config.alt) ? `alt="${config.alt}"` : ""} class="image-content" loading="lazy">
<figcaption class="image-caption-text">${config.caption}</figcaption>
</figure>`.removeExtraSpaces()
}
// Attach event to handle token/session renewal
kiss.session.setupImg(this.querySelector('img'))
// Set properties
this.imageContent = this.querySelector(".image-content")
this._setProperties(config, [
[
["draggable"],
[this]
],
[
["minWidth", "minHeight", "width", "height","maxWidth", "maxHeight", "margin", "position", "top", "left", "right", "float", "boxShadow", "zIndex", "border", "borderStyle", "borderWidth", "borderColor", "borderRadius", "background", "backgroundColor"],
[this.style]
],
[
["minWidth", "minHeight", "width", "height", "maxWidth", "maxHeight", "padding", "cursor", "objectFit", "borderRadius"],
[this.imageContent.style]
]
])
// Bind the image to a record, if any
if (config.record) this._bindRecord(config.record)
return this
}
/**
* Bind the image to a record
* (this subscribes the image to react to database changes)
*
* @private
* @ignore
* @param {object} record
* @returns this
*/
_bindRecord(record) {
this.record = record
this.modelId = record.model.id
this.recordId = record.id
if (record[this.id]) {
this.imageContent.src = this.initialValue = record[this.id]
}
// React to changes on a single record of the binded model
this.subscriptions.push(
subscribe("EVT_DB_UPDATE:" + this.modelId.toUpperCase(), (msgData) => {
if ((msgData.modelId == this.modelId) && (msgData.id == this.recordId)) {
const updates = msgData.data
this._updateField(updates)
}
})
)
// React to changes on multiple records of the binded Model
this.subscriptions.push(
subscribe("EVT_DB_UPDATE_BULK", (msgData) => {
const operations = msgData.data
operations.forEach(operation => {
if ((operation.modelId == this.modelId) && (operation.recordId == this.recordId)) {
const updates = operation.updates
this._updateField(updates)
}
})
})
)
return this
}
/**
* Updates the field value internally
*
* @private
* @ignore
* @param {*} updates
*/
_updateField(updates) {
if (this.id in updates) {
const newValue = updates[this.id]
if (newValue || (newValue === 0) || (newValue === "")) {
this.imageContent.src = newValue
}
}
}
/**
* Set the field value
*
* @param {string} newValue - The new image source URL
* @param {boolean} [rawUpdate] - If true, it doesn't update the associated record and doesn't trigger "change" event
* @returns this
*/
setValue(newValue, rawUpdate) {
if (rawUpdate) {
this.imageContent.src = this.config.src = newValue
return this
}
if (this.record) {
// If the field is connected to a record, we update the database
this.record.updateFieldDeep(this.id, newValue).then(success => {
// Rollback the initial value if the update failed (ACL)
if (!success) {
this.imageContent.src = this.config.src = this.initialValue || ""
}
else {
this.initialValue = newValue
}
})
} else {
// Otherwise, we just change the field value
this.imageContent.src = this.config.src = newValue
}
return this
}
/**
* Get the src of the Image component
*
* @returns {string} The image src
*/
getValue() {
return this.imageContent.src
}
/**
* Set the image's width
*
* @param {*} width - A valid CSS width value
* @returns this
*/
setWidth(width) {
this.config.width = width
this.style.width = this._computeSize("width", width)
return this
}
/**
* Set the image's height
*
* @param {*} height - A valid CSS height value
* @returns this
*/
setHeight(height) {
this.config.height = height
this.style.height = this._computeSize("height", height)
return this
}
/**
* Update the alternative text for the image
*
* @param {string} alt
* @returns this
*/
updateAltText(alt) {
this.config.alt = this.alt = alt
this.imageContent.alt = alt
return this
}
/**
* Update the caption of the image, if it exists.
*
* @param {string} caption
* @returns this
*/
updateCaption(caption) {
this.config.caption = this.caption = caption
const figcaption = this.querySelector(".image-caption-text")
if (figcaption) figcaption.textContent = caption
return this
}
/**
* Returns the empty SVG image data URL
*
* @private
* @ignore
* @return {string} The empty image data URL
*/
_emptyImage() {
return `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTQgM0MyLjg5NTQzIDMgMiAzLjg5NTQzIDIgNVYxOUMyIDIwLjEwNDYgMi44OTU0MyAyMSA0IDIxSDIwQzIxLjEwNDYgMjEgMjIgMjAuMTA0NiAyMiAxOVY1QzIyIDMuODk1NDMgMjEuMTA0NiAzIDIwIDNINFpNMjAgNUg0VjE5SDIwVjVaTTcuMjUgOC41Qzc uMjUgOS4xOTAzNiA3LjgwOTY0IDkuNzUgOC41IDkuNzVDOS4xOTAzNiA5Ljc1IDkuNzUgOS4xOTAzNiA5Ljc1IDguNUM5Ljc1IDcuODA5NjQgOS4xOTAzNiA3LjI1IDguNSA3LjI1QzcuODA5NjQgNy4yNSA3LjI1IDcuODA5NjQgNy4yNSA4LjVaTTcuODExNDMgMTUuNDIxOUwxMC41Mjk0IDEyLjI1TDEzLjMxNTUgMTUuNDIxOUwxNS45MzY1IDEyLjY0ODdMMTkuNzUgMTcuMDY0NUg0LjI1T DcuODExNDMgMTUuNDIxOVoiIGZpbGw9ImN1cnJlbnRDb2xvciIvPjwvc3ZnPg==`
}
}
// Create a Custom Element and add a shortcut to create it
customElements.define("a-image", kiss.ui.Image)
/**
* Shorthand to create a new Image. See [kiss.ui.Image](kiss.ui.Image.html)
*
* @param {object} config
* @returns HTMLElement
*/
const createImage = (config) => document.createElement("a-image").init(config)
;
Source