Skip to content

Commit

Permalink
refactor(esl-image-utils): update implementation to use json attr and…
Browse files Browse the repository at this point in the history
… new compact representation

BREAKING-CHANGE: legacy implementation of `esl-image` no longer distributes aspect-ratio styles
  • Loading branch information
ala-n committed Aug 12, 2024
1 parent 29fdf08 commit 602afc7
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 87 deletions.
4 changes: 3 additions & 1 deletion src/modules/esl-image-utils/all.less
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
@import './core/esl-image-conyainer.less';
@import './core/esl-image-utils.container.less';
@import './core/esl-image-utils.fade.less';
@import './core/esl-image-utils.ratios.less';
93 changes: 61 additions & 32 deletions src/modules/esl-image-utils/core/esl-image-container.mixin.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,88 @@
import {ESLMixinElement} from '../../esl-mixin-element/core';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';
import {attr, listen} from '../../esl-utils/decorators';
import {CSSClassUtils} from '../../esl-utils/dom';
import {jsonAttr, listen, memoize} from '../../esl-utils/decorators';
import {ExportNs} from '../../esl-utils/environment/export-ns';
import {findAll} from '../../esl-utils/dom/traversing';

/**
* ESLImgContainerMixin - mixin to provide image container functionality
* ESLImageContainerConfig - interface for ESLImageContainerMixin config
*/
export interface ESLImageContainerConfig {
/** Class to add to the target element when the image is loaded (successfully) */
readyCls: string;
/** Class to add to the target element when the image is not loaded (error) */
errorCls: string;
/** Image element selector */
selector: string;
}

/**
* ESLImageContainerMixin - mixin to provide image loading state class functionality
* @author Anna Barmina, Alexey Stsefanovich (ala'n)
*
* Use example:
* ```
* <picture class="img-container img-container-16-9">
* <img esl-img-container loading="lazy" alt="img" src="img.png"/>
* <picture class="img-container img-container-16-9" esl-image-container>
* <img loading="lazy" alt="img" src="img.png"/>
* </picture>
* ```
*
* This mixin is used to enhance an image element by adding specific classes to a target when the image has completely loaded or not
* This mixin is used to enhance native image developer experience by adding specific classes when the image has completely loaded or not
*/
@ExportNs('ImageContainer')
export class ESLImageContainerMixin extends ESLMixinElement {
public static override is = 'esl-image-container';

/** Default configuration object */
public static DEFAULT_CONFIG: ESLImageContainerConfig = {
readyCls: 'img-container-loaded',
errorCls: '',
selector: 'img',
};

@ExportNs('ImgContainer')
export class ESLImgContainerMixin extends ESLMixinElement {
static override is = 'esl-img-container';
/** Configuration object */
@jsonAttr({name: ESLImageContainerMixin.is}) public rawConfig: Partial<ESLImageContainerConfig>;

/** Target element selector ('::parent' by default) */
@attr({name: ESLImgContainerMixin.is}) public target: string;
/** Class to add to the target element when the image is loaded */
@attr({name: ESLImgContainerMixin.is + '-cls', defaultValue: 'img-container-loaded'}) public targetCls: string;
/** Class to add to the target element when the image is not loaded */
@attr({name: ESLImgContainerMixin.is + '-error-cls'}) public targetErrorCls: string;
/** Merged configuration object */
@memoize()
public get config(): ESLImageContainerConfig {
return {...ESLImageContainerMixin.DEFAULT_CONFIG, ...this.rawConfig};
}

public override $host!: HTMLImageElement;
/** Image element */
@memoize()
protected get $images(): HTMLImageElement[] {
if (this.$host.tagName === 'IMG') return [this.$host as HTMLImageElement];
return findAll(this.$host, this.config.selector) as HTMLImageElement[];
}

get $target(): Element | null {
return ESLTraversingQuery.first(this.target || '::parent', this.$host);
/** Check if all images are loaded */
public get complete(): boolean {
return this.$images.every(img => img.complete);
}
/** Check if any image has loading error */
public get hasError(): boolean {
return this.$images.some(img => !img.naturalHeight && !img.naturalWidth);
}

protected override connectedCallback(): void {
if (this.$host.tagName !== 'IMG') return;
super.connectedCallback();
if (this.$host.complete) {
const eventType = this.$host.naturalHeight && this.$host.naturalWidth ? 'load' : 'error';
this._onReady(new Event(eventType));
}
else this.$$on(this._onReady);
this._onReady();
}

protected override attributeChangedCallback(name: string): void {
if (name !== ESLImageContainerMixin.is) return;
memoize.clear(this, ['config', '$images']);
this._onReady();
}

@listen({
event: 'load error',
auto: false,
once: true
target: (that: ESLImageContainerMixin) => that.$images
})
protected _onReady(event: Event): void {
const {$target} = this;
if (!$target) return;
CSSClassUtils.add($target, this.targetCls);
if (event.type === 'error') CSSClassUtils.add($target, this.targetErrorCls);
protected _onReady(): void {
if (!this.complete) return;
this.$$off();
this.$$cls(this.config.readyCls, true);
this.$$cls(this.config.errorCls, this.hasError);
}
}
44 changes: 0 additions & 44 deletions src/modules/esl-image-utils/core/esl-image-conyainer.less

This file was deleted.

28 changes: 28 additions & 0 deletions src/modules/esl-image-utils/core/esl-image-utils.container.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Image Container Defaults
.img-container {
position: relative;
overflow: hidden;

> img {
width: 100%;
}

> img:is(.img-stretch, .img-cover, .img-contain) {
position: absolute;
top: 0;
left: 0;
height: 100%;
}

> img.img-cover {
object-fit: cover;
}

> img.img-contain {
object-fit: contain;
}
}

picture.img-container {
display: block;
}
9 changes: 9 additions & 0 deletions src/modules/esl-image-utils/core/esl-image-utils.fade.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.img-fade {
opacity: 0;

&.img-container-loaded,
.img-container-loaded & {
opacity: 1;
transition: opacity 0.4s;
}
}
17 changes: 17 additions & 0 deletions src/modules/esl-image-utils/core/esl-image-utils.ratios.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.img-container {
&.img-container-1-1 {
aspect-ratio: 1;
}

&.img-container-4-3 {
aspect-ratio: ~'4 / 3';
}

&.img-container-16-9 {
aspect-ratio: ~'16 / 9';
}

&.img-container-26-9 {
aspect-ratio: ~'26 / 9';
}
}
10 changes: 0 additions & 10 deletions src/modules/esl-image/core/esl-image-container.less
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,5 @@
transition: opacity 0.4s;
}
}

&.@{containerClass}-1-1 {
padding-top: 100%;
}
&.@{containerClass}-4-3 {
padding-top: 75%;
}
&.@{containerClass}-16-9 {
padding-top: (9/16 * 100%);
}
}
}

0 comments on commit 602afc7

Please sign in to comment.