From 5b4761aefbbc1880b7b2d414d44df571fd80b1ef Mon Sep 17 00:00:00 2001 From: Ann Barmina Date: Wed, 7 Aug 2024 21:21:30 +0200 Subject: [PATCH 01/12] feat(esl-image-utils): create esl-img-container mixin to provide img container functionality Co-authored-by: ala'n (Alexey Stsefanovich) --- .commitlintrc.yml | 1 + site/src/playground/export/lib.ts | 1 + site/views/examples/image-utils.njk | 61 +++++++++++++++++++ src/modules/all.ts | 1 + src/modules/esl-image-utils/core.ts | 1 + .../core/esl-image-container.mixin.ts | 59 ++++++++++++++++++ 6 files changed, 124 insertions(+) create mode 100644 site/views/examples/image-utils.njk create mode 100644 src/modules/esl-image-utils/core.ts create mode 100644 src/modules/esl-image-utils/core/esl-image-container.mixin.ts diff --git a/.commitlintrc.yml b/.commitlintrc.yml index b5669e87b..13705946e 100644 --- a/.commitlintrc.yml +++ b/.commitlintrc.yml @@ -38,6 +38,7 @@ rules: - esl-footnotes - esl-forms - esl-image + - esl-image-utils - esl-media - esl-media-query - esl-mixin-element diff --git a/site/src/playground/export/lib.ts b/site/src/playground/export/lib.ts index 76edf6080..a0f5b29a3 100644 --- a/site/src/playground/export/lib.ts +++ b/site/src/playground/export/lib.ts @@ -1 +1,2 @@ +import '@exadel/esl/modules/lib'; export * from '@exadel/esl'; diff --git a/site/views/examples/image-utils.njk b/site/views/examples/image-utils.njk new file mode 100644 index 000000000..e81195a74 --- /dev/null +++ b/site/views/examples/image-utils.njk @@ -0,0 +1,61 @@ +--- +layout: content +title: Image Utils +seoTitle: Example image using ESL Image Container Mixin +name: Image Utils +tags: [examples, playground] +icon: examples/image.svg +aside: + components: + - esl-image +--- +{% import 'lorem.njk' as lorem %} + +{% set imageSrcBase = '/assets/' | url %} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/src/modules/all.ts b/src/modules/all.ts index 8d2dae0ab..a08219257 100644 --- a/src/modules/all.ts +++ b/src/modules/all.ts @@ -11,6 +11,7 @@ export * from './esl-a11y-group/core'; // Image export * from './esl-image/core'; +export * from './esl-image-utils/core'; // Media export * from './esl-media/core'; diff --git a/src/modules/esl-image-utils/core.ts b/src/modules/esl-image-utils/core.ts new file mode 100644 index 000000000..0811a7e05 --- /dev/null +++ b/src/modules/esl-image-utils/core.ts @@ -0,0 +1 @@ +export * from './core/esl-image-container.mixin'; diff --git a/src/modules/esl-image-utils/core/esl-image-container.mixin.ts b/src/modules/esl-image-utils/core/esl-image-container.mixin.ts new file mode 100644 index 000000000..943ea71c2 --- /dev/null +++ b/src/modules/esl-image-utils/core/esl-image-container.mixin.ts @@ -0,0 +1,59 @@ +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 {ExportNs} from '../../esl-utils/environment/export-ns'; + +/** + * ESLImgContainerMixin - mixin to provide image container functionality + * @author Anna Barmina, Alexey Stsefanovich (ala'n) + * + * Use example: + * ``` + * + * img + * + * ``` + * + * This mixin is used to enhance an image element by adding specific classes to a target when the image has completely loaded or not + */ + +@ExportNs('ImgContainer') +export class ESLImgContainerMixin extends ESLMixinElement { + static override is = 'esl-img-container'; + + /** 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; + + public override $host!: HTMLImageElement; + + get $target(): Element | null { + return ESLTraversingQuery.first(this.target || '::parent', this.$host); + } + + 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); + } + + @listen({ + event: 'load error', + auto: false, + once: true + }) + 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); + } +} From 16fc5cbcc5bc97c3499cb5d2eb94189e4ff3e283 Mon Sep 17 00:00:00 2001 From: "ala'n (Alexey Stsefanovich)" Date: Wed, 7 Aug 2024 23:25:50 +0200 Subject: [PATCH 02/12] feat(esl-image-utils): helper container classes for native img / picture containers --- src/modules/all.less | 1 + src/modules/esl-image-utils/all.less | 1 + .../core/esl-image-conyainer.less | 44 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/modules/esl-image-utils/all.less create mode 100644 src/modules/esl-image-utils/core/esl-image-conyainer.less diff --git a/src/modules/all.less b/src/modules/all.less index a6e1c4333..226a41ad5 100644 --- a/src/modules/all.less +++ b/src/modules/all.less @@ -1,6 +1,7 @@ @import './esl-utils/all.less'; @import './esl-image/core.less'; +@import './esl-image-utils/all.less'; @import './esl-media/core.less'; @import './esl-scrollbar/core.less'; diff --git a/src/modules/esl-image-utils/all.less b/src/modules/esl-image-utils/all.less new file mode 100644 index 000000000..d21f06b8b --- /dev/null +++ b/src/modules/esl-image-utils/all.less @@ -0,0 +1 @@ +@import './core/esl-image-conyainer.less'; diff --git a/src/modules/esl-image-utils/core/esl-image-conyainer.less b/src/modules/esl-image-utils/core/esl-image-conyainer.less new file mode 100644 index 000000000..2eb45b4bf --- /dev/null +++ b/src/modules/esl-image-utils/core/esl-image-conyainer.less @@ -0,0 +1,44 @@ +// Image Container Defaults +.img-container { + position: relative; + overflow: hidden; + + img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + + &[lodaing='lazy'] { + opacity: 0; + .img-container-loaded & { + opacity: 1; + transition: opacity 0.4s; + } + } + + &.img-cover { + object-fit: cover; + } + &.img-contain { + object-fit: contain; + } + } + + &.img-container-1-1 { + aspect-ratio: 1; + } + + &.img-container-4-3 { + aspect-ratio: 4 / 3; + } + + &.img-container-16-9 { + aspect-ratio: 16 / 9; + } +} + +picture.img-container { + display: block; +} From a6afcbd5b9904126d448d48006db58be4caa15cd Mon Sep 17 00:00:00 2001 From: "ala'n (Alexey Stsefanovich)" Date: Wed, 7 Aug 2024 23:26:24 +0200 Subject: [PATCH 03/12] docs(esl-image-utils): documentation and site pages --- site/views/components/esl-image-utils.njk | 13 ++++ site/views/examples/image-utils.njk | 31 ++++---- src/modules/esl-image-utils/README.md | 89 +++++++++++++++++++++++ 3 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 site/views/components/esl-image-utils.njk create mode 100644 src/modules/esl-image-utils/README.md diff --git a/site/views/components/esl-image-utils.njk b/site/views/components/esl-image-utils.njk new file mode 100644 index 000000000..83fd0bdc4 --- /dev/null +++ b/site/views/components/esl-image-utils.njk @@ -0,0 +1,13 @@ +--- +layout: content +title: ESL Image Utils +seoTitle: Common image ESL utilities +name: ESL Image Utils +tags: components +aside: + source: src/modules/esl-image-utils +examples: + - image-utils +--- + +{% mdRender 'src/modules/esl-image-utils/README.md', 'intro' %} diff --git a/site/views/examples/image-utils.njk b/site/views/examples/image-utils.njk index e81195a74..35629aa71 100644 --- a/site/views/examples/image-utils.njk +++ b/site/views/examples/image-utils.njk @@ -7,7 +7,7 @@ tags: [examples, playground] icon: examples/image.svg aside: components: - - esl-image + - esl-image-utils --- {% import 'lorem.njk' as lorem %} @@ -22,7 +22,12 @@ aside: uip-snippet-js="js-snippet-image-element" uip-isolated> - img + img @@ -37,22 +42,20 @@ aside: - - - - - - + + + + - - - - + + + + + + - - diff --git a/src/modules/esl-image-utils/README.md b/src/modules/esl-image-utils/README.md new file mode 100644 index 000000000..086091a13 --- /dev/null +++ b/src/modules/esl-image-utils/README.md @@ -0,0 +1,89 @@ +# [ESL](../../../) Image Utils + +Version: *1.0.0* + +Authors: *Anna Barmina*, *Alexey Stsefanovich (ala'n)* + + + +Lightweight helpers to use with native img and picture elements. + +## ESL Image Container + +ESLImageContainerMixin (`esl-img-container`) is a custom attribute to set image container class as soon as image is loaded. +The custom attribute should be placed on the img tag. + +### Attributes + +- `esl-img-container` (primary) - mixin attribute, provides ESLTraversingQuery to find the container element. Default `::parent` (direct parent). +- `esl-img-container-cls` - optional, class to set on the container element. Supports CSSClassUtils query. Default `img-container-loaded`. +- `esl-img-container-error-cls` - optional, class to set on the container element in case of image loading error. Supports CSSClassUtils query. Default `img-container-error`. + +### Usage + +Set `img-container-loaded` class on the direct parent of the image element upon image load. +```html +
+ +
+``` + +Set class `img-container-loaded` on closest parent with class `img-container` upon image load. +```html +
+ + + + +
+``` +Note: it is not necessary to use `::closest` query in current case. `::parent` query with selector `img-container` will work as well. + +Set custom class `loaded` on the direct parent of the image element upon image load. +```html +
+ +
+``` + +Set custom class `loaded` on the direct parent of the image element upon image load and class `error` in case of image loading error. +```html +
+ +
+``` + +Set custom class `loaded` on the direct parent of the image element upon image successful load. +Note: the error class query executed after the plain class query. So error class query could override the plain class query. +```html +
+ +
+``` + +--- + +## ESL Image Container (CSS Only) +A set of common CSS classes to use with native images. Seamless integration with ESLImageContainerMixin defaults. + +### Main container classes +- `img-container` - mandatory container class. (Can be set on `picture` element as well) +- `img-container-loaded` (Automatic) - class to set on the container element upon image load. ESLImageContainerMixin maintains this class. + +### Aspect Ratio Container Classes + - `img-container-16-9` - aspect ratio 16:9 container class. + - `img-container-4-3` - aspect ratio 4:3 container class. + - `img-container-1-1` - aspect ratio 3:2 container class. + +### Image Classes +By default images inside `img-container` will be stretched to cover the container area. + - `img-cover` - class to set on `img` element to cover the container area maintaining aspect ratio. + - `img-contain` - class to set on `img` element to fit (inscribe) the container area maintaining aspect ratio. + +### Usage +```html + + + + +``` From 29fdf08e382e4465d8f92b22ee6d6023c046a85f Mon Sep 17 00:00:00 2001 From: "ala'n (Alexey Stsefanovich)" Date: Thu, 8 Aug 2024 00:21:15 +0200 Subject: [PATCH 04/12] docs(esl-image): deprecation warning added --- README.md | 3 +- site/src/common/badge.less | 18 +++-- site/src/common/card.less | 3 + site/src/common/markdown.less | 3 +- .../_includes/navigation/sidebar-item.njk | 4 ++ site/views/_layouts/content.njk | 11 +++ site/views/components/esl-image-utils.njk | 6 +- site/views/components/esl-image.njk | 6 +- site/views/examples/image-utils.njk | 67 ++++++++++++++++--- site/views/examples/image.njk | 10 ++- src/modules/esl-image/README.md | 8 ++- 11 files changed, 116 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 48b10cde0..8531afd46 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ with excellent performance. - ##### [ESL Alert](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-alert/README.md) - ##### [ESL Animate](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-animate/README.md) - ##### [ESL Footnotes](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-footnotes/README.md) (beta) -- ##### [ESL Image](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-image/README.md) +- ##### [ESL Image Utils](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-image-utils/README.md) +- ##### [ESL Image (Legacy)](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-image/README.md) - ##### [ESL Media](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-media/README.md) - ##### [ESL Panel](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-panel/README.md) - ##### [ESL Panel Group](https://github.com/exadel-inc/esl/blob/HEAD/src/modules/esl-panel-group/README.md) diff --git a/site/src/common/badge.less b/site/src/common/badge.less index 3c397bf85..562435d42 100644 --- a/site/src/common/badge.less +++ b/site/src/common/badge.less @@ -69,16 +69,26 @@ &-img { width: 20px; height: 20px; - } - &-playground { &::after { position: absolute; - content: ''; inset: 0; + background-size: contain; + background-repeat: no-repeat; background-position: center; + } + } + + &-playground { + &::after { + content: ''; background-image: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 26.1 11.7' fill='white' xml:space='preserve'%3E%3Cpath d='M2.8 5.9 6.7 2a1 1 0 0 0 0-1.6C6.2-.1 5.5-.1 5 .4L.7 4.6l-.2.3-.2.2a1 1 0 0 0 0 1.6L5 11.4c.5.4 1.2.4 1.6 0a1 1 0 0 0 0-1.6L2.8 5.9zM14.4 1.3l2 1.2L12 9.9l-2-1.1zM11.9 10.2 9.9 9l-.2 2.6zM14.8.6c.3-.5 1-.7 1.6-.4.6.3.7 1 .4 1.6l-.3.5-2-1.1.3-.6zM25.7 6.7l-.2.2-.2.3-4.3 4.2c-.5.4-1.2.4-1.6 0a1 1 0 0 1 0-1.6L23.3 6l-3.9-4a1 1 0 0 1 0-1.6c.5-.4 1.2-.4 1.6 0l4.7 4.7c.5.4.5 1.1 0 1.6z'/%3E%3C/svg%3E"); - background-repeat: no-repeat; + } + } + &-deprecated { + &::after { + content: ''; + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3e%3ccircle cx='50' cy='50' r='45' fill='none' stroke='%236c757d' stroke-width='6'/%3e%3cpath fill='%236c757d' d='M20 45h60v10H20z'/%3e%3c/svg%3e"); } } diff --git a/site/src/common/card.less b/site/src/common/card.less index 87b5df4cb..247a1cc90 100644 --- a/site/src/common/card.less +++ b/site/src/common/card.less @@ -8,6 +8,9 @@ background: #fff; box-shadow: 1px 1px 4px 2px rgba(0, 0, 0, 0.3); + img { + pointer-events: none; + } &-image { flex: 0 0 auto; } diff --git a/site/src/common/markdown.less b/site/src/common/markdown.less index 994fde257..19a31d5fb 100644 --- a/site/src/common/markdown.less +++ b/site/src/common/markdown.less @@ -36,7 +36,8 @@ letter-spacing: 1px; } - &.no-margin { + &.no-margin, + .alert & { p:last-child { margin-block-end: 0; } diff --git a/site/views/_includes/navigation/sidebar-item.njk b/site/views/_includes/navigation/sidebar-item.njk index 7c2d07ad9..471deae9d 100644 --- a/site/views/_includes/navigation/sidebar-item.njk +++ b/site/views/_includes/navigation/sidebar-item.njk @@ -42,6 +42,7 @@ {% set isDraft = [].concat(item.data.tags).includes('draft') %} {% set isNew = [].concat(item.data.tags).includes('new') %} {% set isBeta = [].concat(item.data.tags).includes('beta') %} + {% set isDeprecated = [].concat(item.data.tags).includes('deprecated') %} {% set isPlayground = [].concat(item.data.tags).includes('playground') %}