@empathyco/x-components 3.0.0-alpha.255 → 3.0.0-alpha.256

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file. See
4
4
  [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.0.0-alpha.256](https://github.com/empathyco/x/compare/@empathyco/x-components@3.0.0-alpha.255...@empathyco/x-components@3.0.0-alpha.256) (2022-12-22)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **components:** image flickering on re-rendering (#945)
11
+ ([3b1b558](https://github.com/empathyco/x/commit/3b1b55857ac1c5169a771c465fa32fb4313ab63c)),
12
+ closes [EX-7610](https://searchbroker.atlassian.net/browse/EX-7610)
13
+
14
+ # Change Log
15
+
16
+ All notable changes to this project will be documented in this file. See
17
+ [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
18
+
6
19
  ## [3.0.0-alpha.255](https://github.com/empathyco/x/compare/@empathyco/x-components@3.0.0-alpha.254...@empathyco/x-components@3.0.0-alpha.255) (2022-12-22)
7
20
 
8
21
  **Note:** Version bump only for package @empathyco/x-components
@@ -1357,145 +1357,6 @@
1357
1357
  --x-size-border-radius-bottom-right-input-pill: var(--x-size-border-radius-input-pill);
1358
1358
  --x-size-border-radius-bottom-left-input-pill: var(--x-size-border-radius-input-pill);
1359
1359
  }
1360
- .x-list {
1361
- display: flex;
1362
- flex-flow: var(--x-string-flow-list);
1363
- list-style: none;
1364
- gap: var(--x-size-gap-list);
1365
- margin: 0;
1366
- padding: var(--x-size-padding-list);
1367
- justify-content: var(--x-size-justify-list);
1368
- align-items: var(--x-size-align-list);
1369
- min-width: 0;
1370
- }
1371
- @media not all and (-webkit-min-device-pixel-ratio: 0), not all and (min-resolution: 0.001dpcm) {
1372
- .x-list:not(.x-list--horizontal), .x-list.x-list--vertical {
1373
- gap: 0;
1374
- }
1375
- .x-list:not(.x-list--horizontal) > *:not(:last-child), .x-list.x-list--vertical > *:not(:last-child) {
1376
- margin-bottom: var(--x-size-gap-list);
1377
- }
1378
- .x-list.x-list--horizontal {
1379
- gap: 0;
1380
- }
1381
- .x-list.x-list--horizontal > *:not(:last-child) {
1382
- margin-right: var(--x-size-gap-list);
1383
- }
1384
- .x-list.x-list--wrap, .x-list.x-list--wrap-reverse {
1385
- gap: 0;
1386
- }
1387
- .x-list.x-list--wrap > *:not(:last-child), .x-list.x-list--wrap-reverse > *:not(:last-child) {
1388
- margin-right: var(--x-size-gap-list);
1389
- margin-bottom: var(--x-size-gap-list);
1390
- }
1391
- }
1392
-
1393
- .x-list--vertical.x-list {
1394
- flex-flow: column nowrap;
1395
- }
1396
-
1397
- .x-list--horizontal.x-list {
1398
- flex-flow: row nowrap;
1399
- }
1400
-
1401
- .x-list--wrap.x-list {
1402
- flex-flow: row wrap;
1403
- }
1404
-
1405
- .x-list--wrap-reverse.x-list {
1406
- flex-flow: row wrap-reverse;
1407
- }
1408
-
1409
- .x-list--justify-stretch.x-list {
1410
- justify-content: stretch;
1411
- }
1412
-
1413
- .x-list--justify-center.x-list {
1414
- justify-content: center;
1415
- }
1416
-
1417
- .x-list--justify-end.x-list {
1418
- justify-content: flex-end;
1419
- }
1420
-
1421
- .x-list--justify-start.x-list {
1422
- justify-content: flex-start;
1423
- }
1424
-
1425
- .x-list--align-stretch.x-list {
1426
- align-items: stretch;
1427
- }
1428
-
1429
- .x-list--align-center.x-list {
1430
- align-items: center;
1431
- }
1432
-
1433
- .x-list--align-baseline.x-list {
1434
- align-items: baseline;
1435
- }
1436
-
1437
- .x-list--align-end.x-list {
1438
- align-items: flex-end;
1439
- }
1440
-
1441
- .x-list--align-start.x-list {
1442
- align-items: flex-start;
1443
- }
1444
-
1445
- .x-list > .x-list__item--expand {
1446
- flex: 1 1 auto;
1447
- }
1448
- .x-list > .x-list__item--no-expand {
1449
- flex: 0 0 auto;
1450
- }
1451
- .x-list.x-list--horizontal > .x-list__item--expand {
1452
- min-width: 0;
1453
- }
1454
- .x-list:not(.x-list--horizontal) > .x-list__item--expand {
1455
- min-height: 0;
1456
- }
1457
- .x-list > .x-list__item--stretch {
1458
- align-self: stretch;
1459
- }
1460
- .x-list > .x-list__item--flex-none {
1461
- flex: none;
1462
- }
1463
- .x-list > .x-list__item--01 {
1464
- flex: 1 12 auto;
1465
- }
1466
- .x-list > .x-list__item--02 {
1467
- flex: 2 11 auto;
1468
- }
1469
- .x-list > .x-list__item--03 {
1470
- flex: 3 10 auto;
1471
- }
1472
- .x-list > .x-list__item--04 {
1473
- flex: 4 9 auto;
1474
- }
1475
- .x-list > .x-list__item--05 {
1476
- flex: 5 8 auto;
1477
- }
1478
- .x-list > .x-list__item--06 {
1479
- flex: 6 7 auto;
1480
- }
1481
- .x-list > .x-list__item--07 {
1482
- flex: 7 6 auto;
1483
- }
1484
- .x-list > .x-list__item--08 {
1485
- flex: 8 5 auto;
1486
- }
1487
- .x-list > .x-list__item--09 {
1488
- flex: 9 4 auto;
1489
- }
1490
- .x-list > .x-list__item--10 {
1491
- flex: 10 3 auto;
1492
- }
1493
- .x-list > .x-list__item--11 {
1494
- flex: 11 2 auto;
1495
- }
1496
- .x-list > .x-list__item--12 {
1497
- flex: 12 1 auto;
1498
- }
1499
1360
  :root {
1500
1361
  --x-string-flow-list: column nowrap;
1501
1362
  --x-size-padding-list: 0;
@@ -7292,3 +7153,143 @@
7292
7153
  .x-normal-case {
7293
7154
  text-transform: none;
7294
7155
  }
7156
+
7157
+ .x-list {
7158
+ display: flex;
7159
+ flex-flow: var(--x-string-flow-list);
7160
+ list-style: none;
7161
+ gap: var(--x-size-gap-list);
7162
+ margin: 0;
7163
+ padding: var(--x-size-padding-list);
7164
+ justify-content: var(--x-size-justify-list);
7165
+ align-items: var(--x-size-align-list);
7166
+ min-width: 0;
7167
+ }
7168
+ @media not all and (-webkit-min-device-pixel-ratio: 0), not all and (min-resolution: 0.001dpcm) {
7169
+ .x-list:not(.x-list--horizontal), .x-list.x-list--vertical {
7170
+ gap: 0;
7171
+ }
7172
+ .x-list:not(.x-list--horizontal) > *:not(:last-child), .x-list.x-list--vertical > *:not(:last-child) {
7173
+ margin-bottom: var(--x-size-gap-list);
7174
+ }
7175
+ .x-list.x-list--horizontal {
7176
+ gap: 0;
7177
+ }
7178
+ .x-list.x-list--horizontal > *:not(:last-child) {
7179
+ margin-right: var(--x-size-gap-list);
7180
+ }
7181
+ .x-list.x-list--wrap, .x-list.x-list--wrap-reverse {
7182
+ gap: 0;
7183
+ }
7184
+ .x-list.x-list--wrap > *:not(:last-child), .x-list.x-list--wrap-reverse > *:not(:last-child) {
7185
+ margin-right: var(--x-size-gap-list);
7186
+ margin-bottom: var(--x-size-gap-list);
7187
+ }
7188
+ }
7189
+
7190
+ .x-list--vertical.x-list {
7191
+ flex-flow: column nowrap;
7192
+ }
7193
+
7194
+ .x-list--horizontal.x-list {
7195
+ flex-flow: row nowrap;
7196
+ }
7197
+
7198
+ .x-list--wrap.x-list {
7199
+ flex-flow: row wrap;
7200
+ }
7201
+
7202
+ .x-list--wrap-reverse.x-list {
7203
+ flex-flow: row wrap-reverse;
7204
+ }
7205
+
7206
+ .x-list--justify-stretch.x-list {
7207
+ justify-content: stretch;
7208
+ }
7209
+
7210
+ .x-list--justify-center.x-list {
7211
+ justify-content: center;
7212
+ }
7213
+
7214
+ .x-list--justify-end.x-list {
7215
+ justify-content: flex-end;
7216
+ }
7217
+
7218
+ .x-list--justify-start.x-list {
7219
+ justify-content: flex-start;
7220
+ }
7221
+
7222
+ .x-list--align-stretch.x-list {
7223
+ align-items: stretch;
7224
+ }
7225
+
7226
+ .x-list--align-center.x-list {
7227
+ align-items: center;
7228
+ }
7229
+
7230
+ .x-list--align-baseline.x-list {
7231
+ align-items: baseline;
7232
+ }
7233
+
7234
+ .x-list--align-end.x-list {
7235
+ align-items: flex-end;
7236
+ }
7237
+
7238
+ .x-list--align-start.x-list {
7239
+ align-items: flex-start;
7240
+ }
7241
+
7242
+ .x-list > .x-list__item--expand {
7243
+ flex: 1 1 auto;
7244
+ }
7245
+ .x-list > .x-list__item--no-expand {
7246
+ flex: 0 0 auto;
7247
+ }
7248
+ .x-list.x-list--horizontal > .x-list__item--expand {
7249
+ min-width: 0;
7250
+ }
7251
+ .x-list:not(.x-list--horizontal) > .x-list__item--expand {
7252
+ min-height: 0;
7253
+ }
7254
+ .x-list > .x-list__item--stretch {
7255
+ align-self: stretch;
7256
+ }
7257
+ .x-list > .x-list__item--flex-none {
7258
+ flex: none;
7259
+ }
7260
+ .x-list > .x-list__item--01 {
7261
+ flex: 1 12 auto;
7262
+ }
7263
+ .x-list > .x-list__item--02 {
7264
+ flex: 2 11 auto;
7265
+ }
7266
+ .x-list > .x-list__item--03 {
7267
+ flex: 3 10 auto;
7268
+ }
7269
+ .x-list > .x-list__item--04 {
7270
+ flex: 4 9 auto;
7271
+ }
7272
+ .x-list > .x-list__item--05 {
7273
+ flex: 5 8 auto;
7274
+ }
7275
+ .x-list > .x-list__item--06 {
7276
+ flex: 6 7 auto;
7277
+ }
7278
+ .x-list > .x-list__item--07 {
7279
+ flex: 7 6 auto;
7280
+ }
7281
+ .x-list > .x-list__item--08 {
7282
+ flex: 8 5 auto;
7283
+ }
7284
+ .x-list > .x-list__item--09 {
7285
+ flex: 9 4 auto;
7286
+ }
7287
+ .x-list > .x-list__item--10 {
7288
+ flex: 10 3 auto;
7289
+ }
7290
+ .x-list > .x-list__item--11 {
7291
+ flex: 11 2 auto;
7292
+ }
7293
+ .x-list > .x-list__item--12 {
7294
+ flex: 12 1 auto;
7295
+ }
@@ -75,7 +75,7 @@ __vue_render__._withStripped = true;
75
75
  /* style */
76
76
  const __vue_inject_styles__ = undefined;
77
77
  /* scoped */
78
- const __vue_scope_id__ = "data-v-36866428";
78
+ const __vue_scope_id__ = "data-v-49539530";
79
79
  /* module identifier */
80
80
  const __vue_module_identifier__ = undefined;
81
81
  /* functional template */
@@ -1 +1 @@
1
- {"version":3,"file":"base-result-image.vue.js","sources":["../../../../src/components/result/base-result-image.vue"],"sourcesContent":["<template>\n <!-- This is a div because using a picture causes the onload event of the image to fire twice. -->\n <!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->\n <div\n @mouseenter.once=\"userHasHoveredImage = true\"\n @mouseenter=\"isHovering = true\"\n @mouseleave=\"isHovering = false\"\n class=\"x-picture x-result-picture\"\n data-test=\"result-picture\"\n >\n <img\n v-if=\"shouldLoadNextImage\"\n @load=\"flagImageLoaded\"\n @error=\"flagImageAsFailed\"\n loading=\"lazy\"\n :src=\"pendingImages[0]\"\n :style=\"loaderStyles\"\n data-test=\"result-picture-loader\"\n alt=\"\"\n role=\"presentation\"\n />\n <component :is=\"animation\" class=\"x-picture__image\" :appear=\"false\">\n <!-- @slot Fallback image content. It will be rendered when all the images failed -->\n <slot v-if=\"!loadedImages.length && !pendingImages.length\" name=\"fallback\" />\n\n <!-- @slot Loading image content. It will be rendered while the real image is not loaded -->\n <slot v-else-if=\"!loadedImages.length\" name=\"placeholder\" />\n\n <img\n v-else\n :key=\"imageSrc\"\n :alt=\"result.name\"\n :src=\"imageSrc\"\n class=\"x-picture__image x-result-picture__image\"\n data-test=\"result-picture-image\"\n />\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\n import { Result } from '@empathyco/x-types';\n import Vue from 'vue';\n import { Component, Prop, Watch } from 'vue-property-decorator';\n import { NoElement } from '../no-element';\n\n /**\n * Component to be reused that renders an `<img>`.\n *\n * @public\n */\n @Component({\n components: {\n NoElement\n }\n })\n export default class BaseResultImage extends Vue {\n /**\n * (Required) The {@link @empathyco/x-types#Result | result} information.\n *\n * @public\n */\n @Prop({ required: true })\n protected result!: Result;\n\n /**\n * Animation to use when switching between the placeholder, the loaded image, or the failed\n * image fallback.\n *\n * @public\n */\n @Prop({ default: () => NoElement })\n public loadAnimation!: string | typeof Vue;\n\n /**\n * Animation to use when switching between the loaded image and the hover image.\n *\n * @public\n */\n @Prop()\n public hoverAnimation!: string | typeof Vue | undefined;\n\n /**\n * Indicates if the next valid image should be displayed on hover.\n *\n * @public\n */\n @Prop({ type: Boolean, default: false })\n public showNextImageOnHover!: boolean;\n\n /**\n * Copy of the images of the result.\n *\n * It is used as a queue of images to load, once an image loads/fails to load, it is removed\n * from this array.\n *\n * @internal\n */\n protected pendingImages: string[] = [];\n\n /**\n * Contains the images that have been loaded successfully.\n *\n * @internal\n */\n protected loadedImages: string[] = [];\n\n /**\n * Indicates if the user is hovering the image.\n *\n * @internal\n */\n protected isHovering = false;\n\n /**\n * Indicates if the user has hovered the image.\n *\n * @internal\n */\n protected userHasHoveredImage = false;\n\n /**.\n * Styles to use inline in the image loader, to prevent override from CSS\n *\n * @internal\n */\n protected loaderStyles: Partial<CSSStyleDeclaration> = {\n position: 'absolute !important',\n top: '0 !important',\n left: '0 !important',\n width: '100% !important',\n height: '100% !important',\n pointerEvents: 'none !important',\n visibility: 'hidden !important'\n };\n\n /**\n * Initializes images state and resets when the result's images change.\n *\n * @internal\n */\n @Watch('result.images', { immediate: true })\n resetImagesState(): void {\n this.pendingImages = [...(this.result.images ?? [])];\n this.loadedImages = [];\n }\n\n /**\n * Animation to be used.\n *\n * @returns The animation to be used, taking into account if the user has hovered the image.\n *\n * @internal\n */\n protected get animation(): string | typeof Vue {\n return this.userHasHoveredImage\n ? this.hoverAnimation ?? this.loadAnimation\n : this.loadAnimation;\n }\n\n /**\n * Gets the src from the result image.\n *\n * @returns The result image src.\n *\n * @internal\n */\n protected get imageSrc(): string {\n return this.loadedImages[\n !this.showNextImageOnHover || !this.isHovering ? 0 : this.loadedImages.length - 1\n ];\n }\n\n /**\n * Indicates if the loader should try to load the next image.\n *\n * @returns True if it should try to load the next image.\n *\n * @internal\n */\n protected get shouldLoadNextImage(): boolean {\n const numImagesToLoad = this.showNextImageOnHover && this.userHasHoveredImage ? 2 : 1;\n return !!this.pendingImages.length && this.loadedImages.length < numImagesToLoad;\n }\n\n /**\n * Sets an image as failed.\n *\n * @internal\n */\n protected flagImageAsFailed(): void {\n this.pendingImages.shift();\n }\n\n /**\n * Sets an image as loaded.\n *\n * @internal\n */\n protected flagImageLoaded(): void {\n const image = this.pendingImages.shift();\n if (image) {\n this.loadedImages.push(image);\n }\n }\n }\n</script>\n\n<style lang=\"scss\" scoped>\n .x-result-picture {\n min-width: 1px;\n min-height: 1px;\n position: relative;\n\n &__image {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n }\n }\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\n### Basic example\n\nThis component is for the result image. It may be part of the search result page, recommendations or\nother section which needs to include results.\n\nThe result prop is required. It will render a `<img/>` with the result image:\n\n```vue\n<BaseResultImage :result=\"result\" />\n```\n\n### Showing the next image on hover\n\nIf a result has multiple images, it can show the next one on hover.\n\n```vue\n<BaseResultImage :result=\"result\" showNextImageOnHover />\n```\n\n### Customizing slots content\n\nFallback and placeholder contents can be customized.\n\nThe fallback slot allows you to replace the content of the fallback image.\n\nThe other slot is called `placeholder`, and allows you to set the image that its going to be\ndisplayed while the real one is loaded.\n\n```vue\n<BaseResultImage :result=\"result\">\n <template #placeholder>\n <img class=\"x-result-picture-placeholder\" src=\"./placeholder-image.svg\"/>\n </template>\n <template #fallback>\n <img class=\"x-result-picture-fallback\" src=\"./fallback-image.svg\"/>\n </template>\n</BaseResultImage>\n```\n\n### Customizing the animations\n\nTwo animations can be used this component.\n\nThe `loadAnimation` is used to transition between the placeholder, the fallback and the image.\n\nThe `hoverAnimation` is used to transition between the image and the hover image, if the\n`showNextImageOnHover` prop is `true`.\n\n`hoverAnimation` will default to `loadAnimation` if it is not provided.\n\n```vue\n<template>\n <BaseResultImage\n :result=\"result\"\n :loadAnimation=\"loadAnimation\"\n :hoverAnimation=\"hoverAnimation\"\n showNextImageOnHover\n />\n</template>\n\n<script>\n import { BaseResultImage } from '@empathyco/x-components';\n import { CrossFade, CollapseHeight } from '@empathyco/x-components/animations';\n\n export default {\n name: 'BaseResultImageAnimations',\n components: {\n BaseResultImage\n },\n data() {\n return {\n loadAnimation: CrossFade,\n hoverAnimation: CollapseHeight,\n result: {\n name: 'jacket',\n images: ['https://some-image', 'https://some-image-2']\n }\n };\n }\n };\n</script>\n```\n</docs>\n"],"names":[],"mappings":";;;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"base-result-image.vue.js","sources":["../../../../src/components/result/base-result-image.vue"],"sourcesContent":["<template>\n <!-- This is a div because using a picture causes the onload event of the image to fire twice. -->\n <!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->\n <div\n @mouseenter.once=\"userHasHoveredImage = true\"\n @mouseenter=\"isHovering = true\"\n @mouseleave=\"isHovering = false\"\n class=\"x-picture x-result-picture\"\n data-test=\"result-picture\"\n >\n <img\n v-if=\"shouldLoadNextImage\"\n @load=\"flagImageLoaded\"\n @error=\"flagImageAsFailed\"\n loading=\"lazy\"\n :src=\"pendingImages[0]\"\n :style=\"loaderStyles\"\n data-test=\"result-picture-loader\"\n alt=\"\"\n role=\"presentation\"\n />\n <component :is=\"animation\" class=\"x-picture__image\" :appear=\"false\">\n <!-- @slot Fallback image content. It will be rendered when all the images failed -->\n <slot v-if=\"!loadedImages.length && !pendingImages.length\" name=\"fallback\" />\n\n <!-- @slot Loading image content. It will be rendered while the real image is not loaded -->\n <slot v-else-if=\"!loadedImages.length\" name=\"placeholder\" />\n\n <img\n v-else\n :key=\"imageSrc\"\n :alt=\"result.name\"\n :src=\"imageSrc\"\n class=\"x-picture__image x-result-picture__image\"\n data-test=\"result-picture-image\"\n />\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\n import { Result } from '@empathyco/x-types';\n import Vue from 'vue';\n import { Component, Prop, Watch } from 'vue-property-decorator';\n import { NoElement } from '../no-element';\n\n /**\n * Component to be reused that renders an `<img>`.\n *\n * @public\n */\n @Component({\n components: {\n NoElement\n }\n })\n export default class BaseResultImage extends Vue {\n /**\n * (Required) The {@link @empathyco/x-types#Result | result} information.\n *\n * @public\n */\n @Prop({ required: true })\n protected result!: Result;\n\n /**\n * Animation to use when switching between the placeholder, the loaded image, or the failed\n * image fallback.\n *\n * @public\n */\n @Prop({ default: () => NoElement })\n public loadAnimation!: string | typeof Vue;\n\n /**\n * Animation to use when switching between the loaded image and the hover image.\n *\n * @public\n */\n @Prop()\n public hoverAnimation!: string | typeof Vue | undefined;\n\n /**\n * Indicates if the next valid image should be displayed on hover.\n *\n * @public\n */\n @Prop({ type: Boolean, default: false })\n public showNextImageOnHover!: boolean;\n\n /**\n * Copy of the images of the result.\n *\n * It is used as a queue of images to load, once an image loads/fails to load, it is removed\n * from this array.\n *\n * @internal\n */\n protected pendingImages: string[] = [];\n\n /**\n * Contains the images that have been loaded successfully.\n *\n * @internal\n */\n protected loadedImages: string[] = [];\n\n /**\n * Indicates if the user is hovering the image.\n *\n * @internal\n */\n protected isHovering = false;\n\n /**\n * Indicates if the user has hovered the image.\n *\n * @internal\n */\n protected userHasHoveredImage = false;\n\n /**.\n * Styles to use inline in the image loader, to prevent override from CSS\n *\n * @internal\n */\n protected loaderStyles: Partial<CSSStyleDeclaration> = {\n position: 'absolute !important',\n top: '0 !important',\n left: '0 !important',\n width: '100% !important',\n height: '100% !important',\n pointerEvents: 'none !important',\n visibility: 'hidden !important'\n };\n\n /**\n * Initializes images state and resets when the result's images change.\n *\n * @internal\n */\n @Watch('result.images', { immediate: true })\n resetImagesState(): void {\n this.pendingImages = [...(this.result.images ?? [])];\n this.loadedImages = this.pendingImages.filter(image => this.loadedImages.includes(image));\n }\n\n /**\n * Animation to be used.\n *\n * @returns The animation to be used, taking into account if the user has hovered the image.\n *\n * @internal\n */\n protected get animation(): string | typeof Vue {\n return this.userHasHoveredImage\n ? this.hoverAnimation ?? this.loadAnimation\n : this.loadAnimation;\n }\n\n /**\n * Gets the src from the result image.\n *\n * @returns The result image src.\n *\n * @internal\n */\n protected get imageSrc(): string {\n return this.loadedImages[\n !this.showNextImageOnHover || !this.isHovering ? 0 : this.loadedImages.length - 1\n ];\n }\n\n /**\n * Indicates if the loader should try to load the next image.\n *\n * @returns True if it should try to load the next image.\n *\n * @internal\n */\n protected get shouldLoadNextImage(): boolean {\n const numImagesToLoad = this.showNextImageOnHover && this.userHasHoveredImage ? 2 : 1;\n return !!this.pendingImages.length && this.loadedImages.length < numImagesToLoad;\n }\n\n /**\n * Sets an image as failed.\n *\n * @internal\n */\n protected flagImageAsFailed(): void {\n this.pendingImages.shift();\n }\n\n /**\n * Sets an image as loaded.\n *\n * @internal\n */\n protected flagImageLoaded(): void {\n const image = this.pendingImages.shift();\n if (image) {\n this.loadedImages.push(image);\n }\n }\n }\n</script>\n\n<style lang=\"scss\" scoped>\n .x-result-picture {\n min-width: 1px;\n min-height: 1px;\n position: relative;\n\n &__image {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n }\n }\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\n### Basic example\n\nThis component is for the result image. It may be part of the search result page, recommendations or\nother section which needs to include results.\n\nThe result prop is required. It will render a `<img/>` with the result image:\n\n```vue\n<BaseResultImage :result=\"result\" />\n```\n\n### Showing the next image on hover\n\nIf a result has multiple images, it can show the next one on hover.\n\n```vue\n<BaseResultImage :result=\"result\" showNextImageOnHover />\n```\n\n### Customizing slots content\n\nFallback and placeholder contents can be customized.\n\nThe fallback slot allows you to replace the content of the fallback image.\n\nThe other slot is called `placeholder`, and allows you to set the image that its going to be\ndisplayed while the real one is loaded.\n\n```vue\n<BaseResultImage :result=\"result\">\n <template #placeholder>\n <img class=\"x-result-picture-placeholder\" src=\"./placeholder-image.svg\"/>\n </template>\n <template #fallback>\n <img class=\"x-result-picture-fallback\" src=\"./fallback-image.svg\"/>\n </template>\n</BaseResultImage>\n```\n\n### Customizing the animations\n\nTwo animations can be used this component.\n\nThe `loadAnimation` is used to transition between the placeholder, the fallback and the image.\n\nThe `hoverAnimation` is used to transition between the image and the hover image, if the\n`showNextImageOnHover` prop is `true`.\n\n`hoverAnimation` will default to `loadAnimation` if it is not provided.\n\n```vue\n<template>\n <BaseResultImage\n :result=\"result\"\n :loadAnimation=\"loadAnimation\"\n :hoverAnimation=\"hoverAnimation\"\n showNextImageOnHover\n />\n</template>\n\n<script>\n import { BaseResultImage } from '@empathyco/x-components';\n import { CrossFade, CollapseHeight } from '@empathyco/x-components/animations';\n\n export default {\n name: 'BaseResultImageAnimations',\n components: {\n BaseResultImage\n },\n data() {\n return {\n loadAnimation: CrossFade,\n hoverAnimation: CollapseHeight,\n result: {\n name: 'jacket',\n images: ['https://some-image', 'https://some-image-2']\n }\n };\n }\n };\n</script>\n```\n</docs>\n"],"names":[],"mappings":";;;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -60,7 +60,7 @@ let BaseResultImage = class BaseResultImage extends Vue {
60
60
  */
61
61
  resetImagesState() {
62
62
  this.pendingImages = [...(this.result.images ?? [])];
63
- this.loadedImages = [];
63
+ this.loadedImages = this.pendingImages.filter(image => this.loadedImages.includes(image));
64
64
  }
65
65
  /**
66
66
  * Animation to be used.
@@ -1 +1 @@
1
- {"version":3,"file":"base-result-image.vue_rollup-plugin-vue_script.vue.js","sources":["../../../../src/components/result/base-result-image.vue?rollup-plugin-vue=script.ts"],"sourcesContent":["\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nimport { Result } from '@empathyco/x-types';\nimport Vue from 'vue';\nimport { Component, Prop, Watch } from 'vue-property-decorator';\nimport { NoElement } from '../no-element';\n\n/**\n * Component to be reused that renders an `<img>`.\n *\n * @public\n */\n@Component({\n components: {\n NoElement\n }\n})\nexport default class BaseResultImage extends Vue {\n /**\n * (Required) The {@link @empathyco/x-types#Result | result} information.\n *\n * @public\n */\n @Prop({ required: true })\n protected result!: Result;\n\n /**\n * Animation to use when switching between the placeholder, the loaded image, or the failed\n * image fallback.\n *\n * @public\n */\n @Prop({ default: () => NoElement })\n public loadAnimation!: string | typeof Vue;\n\n /**\n * Animation to use when switching between the loaded image and the hover image.\n *\n * @public\n */\n @Prop()\n public hoverAnimation!: string | typeof Vue | undefined;\n\n /**\n * Indicates if the next valid image should be displayed on hover.\n *\n * @public\n */\n @Prop({ type: Boolean, default: false })\n public showNextImageOnHover!: boolean;\n\n /**\n * Copy of the images of the result.\n *\n * It is used as a queue of images to load, once an image loads/fails to load, it is removed\n * from this array.\n *\n * @internal\n */\n protected pendingImages: string[] = [];\n\n /**\n * Contains the images that have been loaded successfully.\n *\n * @internal\n */\n protected loadedImages: string[] = [];\n\n /**\n * Indicates if the user is hovering the image.\n *\n * @internal\n */\n protected isHovering = false;\n\n /**\n * Indicates if the user has hovered the image.\n *\n * @internal\n */\n protected userHasHoveredImage = false;\n\n /**.\n * Styles to use inline in the image loader, to prevent override from CSS\n *\n * @internal\n */\n protected loaderStyles: Partial<CSSStyleDeclaration> = {\n position: 'absolute !important',\n top: '0 !important',\n left: '0 !important',\n width: '100% !important',\n height: '100% !important',\n pointerEvents: 'none !important',\n visibility: 'hidden !important'\n };\n\n /**\n * Initializes images state and resets when the result's images change.\n *\n * @internal\n */\n @Watch('result.images', { immediate: true })\n resetImagesState(): void {\n this.pendingImages = [...(this.result.images ?? [])];\n this.loadedImages = [];\n }\n\n /**\n * Animation to be used.\n *\n * @returns The animation to be used, taking into account if the user has hovered the image.\n *\n * @internal\n */\n protected get animation(): string | typeof Vue {\n return this.userHasHoveredImage\n ? this.hoverAnimation ?? this.loadAnimation\n : this.loadAnimation;\n }\n\n /**\n * Gets the src from the result image.\n *\n * @returns The result image src.\n *\n * @internal\n */\n protected get imageSrc(): string {\n return this.loadedImages[\n !this.showNextImageOnHover || !this.isHovering ? 0 : this.loadedImages.length - 1\n ];\n }\n\n /**\n * Indicates if the loader should try to load the next image.\n *\n * @returns True if it should try to load the next image.\n *\n * @internal\n */\n protected get shouldLoadNextImage(): boolean {\n const numImagesToLoad = this.showNextImageOnHover && this.userHasHoveredImage ? 2 : 1;\n return !!this.pendingImages.length && this.loadedImages.length < numImagesToLoad;\n }\n\n /**\n * Sets an image as failed.\n *\n * @internal\n */\n protected flagImageAsFailed(): void {\n this.pendingImages.shift();\n }\n\n /**\n * Sets an image as loaded.\n *\n * @internal\n */\n protected flagImageLoaded(): void {\n const image = this.pendingImages.shift();\n if (image) {\n this.loadedImages.push(image);\n }\n }\n}\n"],"names":[],"mappings":";;;;;AA8CA;;;;;AAUA,IAAqB,eAAe,GAApC,MAAqB,eAAgB,SAAQ,GAAG;IAAhD;;;;;;;;;;QA0CY,kBAAa,GAAa,EAAE,CAAC;;;;;;QAO7B,iBAAY,GAAa,EAAE,CAAC;;;;;;QAO5B,eAAU,GAAG,KAAK,CAAC;;;;;;QAOnB,wBAAmB,GAAG,KAAK,CAAC;;;;;;QAO5B,iBAAY,GAAiC;YACrD,QAAQ,EAAE,qBAAqB;YAC/B,GAAG,EAAE,cAAc;YACnB,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,iBAAiB;YACxB,MAAM,EAAE,iBAAiB;YACzB,aAAa,EAAE,iBAAiB;YAChC,UAAU,EAAE,mBAAmB;SAChC,CAAC;KAuEH;;;;;;IA/DC,gBAAgB;QACd,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;KACxB;;;;;;;;IASD,IAAc,SAAS;QACrB,OAAO,IAAI,CAAC,mBAAmB;cAC3B,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,aAAa;cACzC,IAAI,CAAC,aAAa,CAAC;KACxB;;;;;;;;IASD,IAAc,QAAQ;QACpB,OAAO,IAAI,CAAC,YAAY,CACtB,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAClF,CAAC;KACH;;;;;;;;IASD,IAAc,mBAAmB;QAC/B,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,mBAAmB,GAAG,CAAC,GAAG,CAAC,CAAC;QACtF,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,eAAe,CAAC;KAClF;;;;;;IAOS,iBAAiB;QACzB,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;KAC5B;;;;;;IAOS,eAAe;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACzC,IAAI,KAAK,EAAE;YACT,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SAC/B;KACF;CACF,CAAA;AA9IC;IADC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;+CACC;AAS1B;IADC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE,CAAC;sDACQ;AAQ3C;IADC,IAAI,EAAE;uDACiD;AAQxD;IADC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;6DACF;AAsDtC;IADC,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;uDAI3C;AAzFkB,eAAe;IALnC,SAAS,CAAC;QACT,UAAU,EAAE;YACV,SAAS;SACV;KACF,CAAC;GACmB,eAAe,CAqJnC;aArJoB,eAAe;;;;"}
1
+ {"version":3,"file":"base-result-image.vue_rollup-plugin-vue_script.vue.js","sources":["../../../../src/components/result/base-result-image.vue?rollup-plugin-vue=script.ts"],"sourcesContent":["\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nimport { Result } from '@empathyco/x-types';\nimport Vue from 'vue';\nimport { Component, Prop, Watch } from 'vue-property-decorator';\nimport { NoElement } from '../no-element';\n\n/**\n * Component to be reused that renders an `<img>`.\n *\n * @public\n */\n@Component({\n components: {\n NoElement\n }\n})\nexport default class BaseResultImage extends Vue {\n /**\n * (Required) The {@link @empathyco/x-types#Result | result} information.\n *\n * @public\n */\n @Prop({ required: true })\n protected result!: Result;\n\n /**\n * Animation to use when switching between the placeholder, the loaded image, or the failed\n * image fallback.\n *\n * @public\n */\n @Prop({ default: () => NoElement })\n public loadAnimation!: string | typeof Vue;\n\n /**\n * Animation to use when switching between the loaded image and the hover image.\n *\n * @public\n */\n @Prop()\n public hoverAnimation!: string | typeof Vue | undefined;\n\n /**\n * Indicates if the next valid image should be displayed on hover.\n *\n * @public\n */\n @Prop({ type: Boolean, default: false })\n public showNextImageOnHover!: boolean;\n\n /**\n * Copy of the images of the result.\n *\n * It is used as a queue of images to load, once an image loads/fails to load, it is removed\n * from this array.\n *\n * @internal\n */\n protected pendingImages: string[] = [];\n\n /**\n * Contains the images that have been loaded successfully.\n *\n * @internal\n */\n protected loadedImages: string[] = [];\n\n /**\n * Indicates if the user is hovering the image.\n *\n * @internal\n */\n protected isHovering = false;\n\n /**\n * Indicates if the user has hovered the image.\n *\n * @internal\n */\n protected userHasHoveredImage = false;\n\n /**.\n * Styles to use inline in the image loader, to prevent override from CSS\n *\n * @internal\n */\n protected loaderStyles: Partial<CSSStyleDeclaration> = {\n position: 'absolute !important',\n top: '0 !important',\n left: '0 !important',\n width: '100% !important',\n height: '100% !important',\n pointerEvents: 'none !important',\n visibility: 'hidden !important'\n };\n\n /**\n * Initializes images state and resets when the result's images change.\n *\n * @internal\n */\n @Watch('result.images', { immediate: true })\n resetImagesState(): void {\n this.pendingImages = [...(this.result.images ?? [])];\n this.loadedImages = this.pendingImages.filter(image => this.loadedImages.includes(image));\n }\n\n /**\n * Animation to be used.\n *\n * @returns The animation to be used, taking into account if the user has hovered the image.\n *\n * @internal\n */\n protected get animation(): string | typeof Vue {\n return this.userHasHoveredImage\n ? this.hoverAnimation ?? this.loadAnimation\n : this.loadAnimation;\n }\n\n /**\n * Gets the src from the result image.\n *\n * @returns The result image src.\n *\n * @internal\n */\n protected get imageSrc(): string {\n return this.loadedImages[\n !this.showNextImageOnHover || !this.isHovering ? 0 : this.loadedImages.length - 1\n ];\n }\n\n /**\n * Indicates if the loader should try to load the next image.\n *\n * @returns True if it should try to load the next image.\n *\n * @internal\n */\n protected get shouldLoadNextImage(): boolean {\n const numImagesToLoad = this.showNextImageOnHover && this.userHasHoveredImage ? 2 : 1;\n return !!this.pendingImages.length && this.loadedImages.length < numImagesToLoad;\n }\n\n /**\n * Sets an image as failed.\n *\n * @internal\n */\n protected flagImageAsFailed(): void {\n this.pendingImages.shift();\n }\n\n /**\n * Sets an image as loaded.\n *\n * @internal\n */\n protected flagImageLoaded(): void {\n const image = this.pendingImages.shift();\n if (image) {\n this.loadedImages.push(image);\n }\n }\n}\n"],"names":[],"mappings":";;;;;AA8CA;;;;;AAUA,IAAqB,eAAe,GAApC,MAAqB,eAAgB,SAAQ,GAAG;IAAhD;;;;;;;;;;QA0CY,kBAAa,GAAa,EAAE,CAAC;;;;;;QAO7B,iBAAY,GAAa,EAAE,CAAC;;;;;;QAO5B,eAAU,GAAG,KAAK,CAAC;;;;;;QAOnB,wBAAmB,GAAG,KAAK,CAAC;;;;;;QAO5B,iBAAY,GAAiC;YACrD,QAAQ,EAAE,qBAAqB;YAC/B,GAAG,EAAE,cAAc;YACnB,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,iBAAiB;YACxB,MAAM,EAAE,iBAAiB;YACzB,aAAa,EAAE,iBAAiB;YAChC,UAAU,EAAE,mBAAmB;SAChC,CAAC;KAuEH;;;;;;IA/DC,gBAAgB;QACd,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;KAC3F;;;;;;;;IASD,IAAc,SAAS;QACrB,OAAO,IAAI,CAAC,mBAAmB;cAC3B,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,aAAa;cACzC,IAAI,CAAC,aAAa,CAAC;KACxB;;;;;;;;IASD,IAAc,QAAQ;QACpB,OAAO,IAAI,CAAC,YAAY,CACtB,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAClF,CAAC;KACH;;;;;;;;IASD,IAAc,mBAAmB;QAC/B,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,mBAAmB,GAAG,CAAC,GAAG,CAAC,CAAC;QACtF,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,eAAe,CAAC;KAClF;;;;;;IAOS,iBAAiB;QACzB,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;KAC5B;;;;;;IAOS,eAAe;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACzC,IAAI,KAAK,EAAE;YACT,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SAC/B;KACF;CACF,CAAA;AA9IC;IADC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;+CACC;AAS1B;IADC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE,CAAC;sDACQ;AAQ3C;IADC,IAAI,EAAE;uDACiD;AAQxD;IADC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;6DACF;AAsDtC;IADC,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;uDAI3C;AAzFkB,eAAe;IALnC,SAAS,CAAC;QACT,UAAU,EAAE;YACV,SAAS;SACV;KACF,CAAC;GACmB,eAAe,CAqJnC;aArJoB,eAAe;;;;"}
@@ -1,6 +1,6 @@
1
1
  import { createInjector, createInjectorSSR } from 'vue-runtime-helpers';
2
2
 
3
- var css = ".x-result-picture[data-v-36866428] {\n min-width: 1px;\n min-height: 1px;\n position: relative;\n}\n.x-result-picture__image[data-v-36866428] {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}";
3
+ var css = ".x-result-picture[data-v-49539530] {\n min-width: 1px;\n min-height: 1px;\n position: relative;\n}\n.x-result-picture__image[data-v-49539530] {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}";
4
4
  const isBrowser = /*#__PURE__*/ (function () {
5
5
  return (
6
6
  Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) !==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empathyco/x-components",
3
- "version": "3.0.0-alpha.255",
3
+ "version": "3.0.0-alpha.256",
4
4
  "description": "Empathy X Components",
5
5
  "author": "Empathy Systems Corporation S.L.",
6
6
  "license": "Apache-2.0",
@@ -135,5 +135,5 @@
135
135
  "access": "public",
136
136
  "directory": "dist"
137
137
  },
138
- "gitHead": "e072108ff965ff6373b9b10fa1e87484162505fd"
138
+ "gitHead": "27a7d6843b5a5177b9d89ee300cb7a18b7c5cce5"
139
139
  }