Image: add image component (#15117)

pull/15050/head
Simona 2019-04-19 12:09:42 +08:00 committed by hetech
parent 686595627e
commit d9462ab771
67 changed files with 1106 additions and 2 deletions

View File

@ -70,5 +70,6 @@
"footer": "./packages/footer/index.js",
"timeline": "./packages/timeline/index.js",
"timeline-item": "./packages/timeline-item/index.js",
"divider": "./packages/divider/index.js"
"divider": "./packages/divider/index.js",
"image": "./packages/image/index.js"
}

View File

@ -0,0 +1,78 @@
@keyframes dot {
0% { width: 0; margin-right: 1em; }
100% { width: 1em; margin-right: 0; }
}
.demo-image {
.block {
padding: 30px 0;
text-align: center;
border-right: solid 1px #eff2f6;
display: inline-block;
width: 20%;
box-sizing: border-box;
vertical-align: top;
&:last-child {
border-right: none;
}
}
.demonstration {
display: block;
color: #8492a6;
font-size: 14px;
margin-bottom: 20px;
}
}
.demo-image__placeholder, .demo-image__error {
@extend .demo-image;
.block {
width: 49%;
}
.el-image {
width: 300px;
height: 200px;
}
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #909399;
font-size: 14px;
}
}
.demo-image__placeholder {
.dot {
animation: dot 2s infinite steps(3, start);
overflow: hidden;
}
}
.demo-image__error {
.image-slot {
font-size: 30px;
}
}
.demo-image__lazy {
height: 400px;
overflow-y: auto;
.el-image {
display: block;
min-height: 200px;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
}

View File

@ -39,3 +39,4 @@
@import "./typography.scss";
@import "./upload.scss";
@import "./divider.scss";
@import "./image.scss";

View File

@ -0,0 +1,132 @@
## Image
Besides the native features of img, support lazy load, custom placeholder and load failure, etc.
### Basic Usage
:::demo Indicate how the image should be resized to fit its container by `fit`, same as native [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)。
```html
<div class="demo-image">
<div class="block" v-for="fit in fits" :key="fit">
<span class="demonstration">{{ fit }}</span>
<el-image
style="width: 100px; height: 100px"
:src="url"
:fit="fit"></el-image>
</div>
</div>
<script>
export default {
data() {
return {
fits: ['fill', 'contain', 'cover', 'none', 'scale-down'],
url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
}
}
}
</script>
```
:::
### Placeholder
:::demo Custom placeholder content when image hasn't loaded yet by `slot = placeholder`
```html
<div class="demo-image__placeholder">
<div class="block">
<span class="demonstration">Default</span>
<el-image :src="src"></el-image>
</div>
<div class="block">
<span class="demonstration">Custom</span>
<el-image :src="src">
<div slot="placeholder" class="image-slot">
Loading<span class="dot">...</span>
</div>
</el-image>
</div>
</div>
<script>
export default {
data() {
return {
src: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg'
}
}
}
</script>
```
:::
### Load Failed
:::demo Custom failed content when error occurs to image load by `slot = error`
```html
<div class="demo-image__error">
<div class="block">
<span class="demonstration">Default</span>
<el-image></el-image>
</div>
<div class="block">
<span class="demonstration">Custom</span>
<el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
</div>
```
:::
### Lazy Load
:::demo Use lazy load by `lazy = true`. Image will load until scroll into view when set. You can indicate scroll container that adds scroll listener to by `scroll-container`. If undefined, will be the nearest parent container whose overflow property is auto or scroll.
```html
<div class="demo-image__lazy">
<el-image v-for="url in urls" :key="url" :src="url" lazy></el-image>
</div>
<script>
export default {
data() {
return {
urls: [
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg'
]
}
}
}
</script>
```
:::
### Attributes
| Attribute | Description | Type | Accepted values | Default |
|---------- |-------- |---------- |------------- |-------- |
| src | Image source, same as native | string | — | - |
| fit | Indicate how the image should be resized to fit its container, same as [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) | fill / contain / cover / none / scale-down | — | - |
| alt | Native alt | string | - | - |
| lazy | Whether to use lazy load | boolean | — | false |
| scroll-container | The container to add scroll listener when using lazy load | string / HTMLElement | — | The nearest parent container whose overflow property is auto or scroll |
### Events
| Event Name | Description | Parameters |
|---------- |-------- |---------- |
| load | Same as native load | (e: Event) |
| error | Same as native error | (e: Error) |
### Slots
| Slot Name | Description |
|---------|-------------|
| placeholder | Triggers when image load |
| error | Triggers when image load failed |

132
examples/docs/es/image.md Normal file
View File

@ -0,0 +1,132 @@
## Image
Besides the native features of img, support lazy load, custom placeholder and load failure, etc.
### Basic Usage
:::demo Indicate how the image should be resized to fit its container by `fit`, same as native [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)。
```html
<div class="demo-image">
<div class="block" v-for="fit in fits" :key="fit">
<span class="demonstration">{{ fit }}</span>
<el-image
style="width: 100px; height: 100px"
:src="url"
:fit="fit"></el-image>
</div>
</div>
<script>
export default {
data() {
return {
fits: ['fill', 'contain', 'cover', 'none', 'scale-down'],
url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
}
}
}
</script>
```
:::
### Placeholder
:::demo Custom placeholder content when image hasn't loaded yet by `slot = placeholder`
```html
<div class="demo-image__placeholder">
<div class="block">
<span class="demonstration">Default</span>
<el-image :src="src"></el-image>
</div>
<div class="block">
<span class="demonstration">Custom</span>
<el-image :src="src">
<div slot="placeholder" class="image-slot">
Loading<span class="dot">...</span>
</div>
</el-image>
</div>
</div>
<script>
export default {
data() {
return {
src: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg'
}
}
}
</script>
```
:::
### Load Failed
:::demo Custom failed content when error occurs to image load by `slot = error`
```html
<div class="demo-image__error">
<div class="block">
<span class="demonstration">Default</span>
<el-image></el-image>
</div>
<div class="block">
<span class="demonstration">Custom</span>
<el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
</div>
```
:::
### Lazy Load
:::demo Use lazy load by `lazy = true`. Image will load until scroll into view when set. You can indicate scroll container that adds scroll listener to by `scroll-container`. If undefined, will be the nearest parent container whose overflow property is auto or scroll.
```html
<div class="demo-image__lazy">
<el-image v-for="url in urls" :key="url" :src="url" lazy></el-image>
</div>
<script>
export default {
data() {
return {
urls: [
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg'
]
}
}
}
</script>
```
:::
### Attributes
| Attribute | Description | Type | Accepted values | Default |
|---------- |-------- |---------- |------------- |-------- |
| src | Image source, same as native | string | — | - |
| fit | Indicate how the image should be resized to fit its container, same as [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) | fill / contain / cover / none / scale-down | — | - |
| alt | Native alt | string | - | - |
| lazy | Whether to use lazy load | boolean | — | false |
| scroll-container | The container to add scroll listener when using lazy load | string / HTMLElement | — | The nearest parent container whose overflow property is auto or scroll |
### Events
| Event Name | Description | Parameters |
|---------- |-------- |---------- |
| load | Same as native load | (e: Event) |
| error | Same as native error | (e: Error) |
### Slots
| Slot Name | Description |
|---------|-------------|
| placeholder | Triggers when image load |
| error | Triggers when image load failed |

View File

@ -0,0 +1,132 @@
## Image
Besides the native features of img, support lazy load, custom placeholder and load failure, etc.
### Basic Usage
:::demo Indicate how the image should be resized to fit its container by `fit`, same as native [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)。
```html
<div class="demo-image">
<div class="block" v-for="fit in fits" :key="fit">
<span class="demonstration">{{ fit }}</span>
<el-image
style="width: 100px; height: 100px"
:src="url"
:fit="fit"></el-image>
</div>
</div>
<script>
export default {
data() {
return {
fits: ['fill', 'contain', 'cover', 'none', 'scale-down'],
url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
}
}
}
</script>
```
:::
### Placeholder
:::demo Custom placeholder content when image hasn't loaded yet by `slot = placeholder`
```html
<div class="demo-image__placeholder">
<div class="block">
<span class="demonstration">Default</span>
<el-image :src="src"></el-image>
</div>
<div class="block">
<span class="demonstration">Custom</span>
<el-image :src="src">
<div slot="placeholder" class="image-slot">
Loading<span class="dot">...</span>
</div>
</el-image>
</div>
</div>
<script>
export default {
data() {
return {
src: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg'
}
}
}
</script>
```
:::
### Load Failed
:::demo Custom failed content when error occurs to image load by `slot = error`
```html
<div class="demo-image__error">
<div class="block">
<span class="demonstration">Default</span>
<el-image></el-image>
</div>
<div class="block">
<span class="demonstration">Custom</span>
<el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
</div>
```
:::
### Lazy Load
:::demo Use lazy load by `lazy = true`. Image will load until scroll into view when set. You can indicate scroll container that adds scroll listener to by `scroll-container`. If undefined, will be the nearest parent container whose overflow property is auto or scroll.
```html
<div class="demo-image__lazy">
<el-image v-for="url in urls" :key="url" :src="url" lazy></el-image>
</div>
<script>
export default {
data() {
return {
urls: [
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg'
]
}
}
}
</script>
```
:::
### Attributes
| Attribute | Description | Type | Accepted values | Default |
|---------- |-------- |---------- |------------- |-------- |
| src | Image source, same as native | string | — | - |
| fit | Indicate how the image should be resized to fit its container, same as [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) | fill / contain / cover / none / scale-down | — | - |
| alt | Native alt | string | - | - |
| lazy | Whether to use lazy load | boolean | — | false |
| scroll-container | The container to add scroll listener when using lazy load | string / HTMLElement | — | The nearest parent container whose overflow property is auto or scroll |
### Events
| Event Name | Description | Parameters |
|---------- |-------- |---------- |
| load | Same as native load | (e: Event) |
| error | Same as native error | (e: Error) |
### Slots
| Slot Name | Description |
|---------|-------------|
| placeholder | Triggers when image load |
| error | Triggers when image load failed |

View File

@ -0,0 +1,132 @@
## Image 图片
图片容器在保留原生img的特性下支持懒加载自定义占位、加载失败等
### 基础用法
:::demo 可通过`fit`确定图片如何适应到容器框,同原生 [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)。
```html
<div class="demo-image">
<div class="block" v-for="fit in fits" :key="fit">
<span class="demonstration">{{ fit }}</span>
<el-image
style="width: 100px; height: 100px"
:src="url"
:fit="fit"></el-image>
</div>
</div>
<script>
export default {
data() {
return {
fits: ['fill', 'contain', 'cover', 'none', 'scale-down'],
url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
}
}
}
</script>
```
:::
### 占位内容
:::demo 可通过`slot = placeholder`可自定义占位内容
```html
<div class="demo-image__placeholder">
<div class="block">
<span class="demonstration">默认</span>
<el-image :src="src"></el-image>
</div>
<div class="block">
<span class="demonstration">自定义</span>
<el-image :src="src">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</div>
</div>
<script>
export default {
data() {
return {
src: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg'
}
}
}
</script>
```
:::
### 加载失败
:::demo 可通过`slot = error`可自定义加载失败内容
```html
<div class="demo-image__error">
<div class="block">
<span class="demonstration">默认</span>
<el-image></el-image>
</div>
<div class="block">
<span class="demonstration">自定义</span>
<el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
</div>
```
:::
### 懒加载
:::demo 可通过`lazy`开启懒加载功能,当图片滚动到可视范围内才会加载。可通过`scroll-container`来设置滚动容器,若未定义,则为最近一个`overflow`值为`auto`或`scroll`的父元素。
```html
<div class="demo-image__lazy">
<el-image v-for="url in urls" :key="url" :src="url" lazy></el-image>
</div>
<script>
export default {
data() {
return {
urls: [
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg'
]
}
}
}
</script>
```
:::
### Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| src | 图片源,同原生 | string | — | - |
| fit | 确定图片如何适应容器框,同原生 [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) | fill / contain / cover / none / scale-down | — | - |
| alt | 原生 alt | string | - | - |
| lazy | 是否开启懒加载 | boolean | — | false |
| scroll-container | 开启懒加载后,监听 scroll 事件的容器 | string / HTMLElement | — | 最近一个 overflow 值为 auto 或 scroll 的父元素 |
### Events
| 事件名称 | 说明 | 回调参数 |
|---------- |-------- |---------- |
| load | 图片加载成功触发 | (e: Event) |
| error | 图片加载失败触发 | (e: Error) |
### Slots
| 名称 | 说明 |
|---------|-------------|
| placeholder | 图片未加载的占位内容 |
| error | 加载失败的内容 |

View File

@ -255,6 +255,10 @@
{
"path": "/divider",
"title": "Divider 分割线"
},
{
"path": "/image",
"title": "Image"
}
]
}
@ -517,6 +521,10 @@
{
"path": "/divider",
"title": "Divider"
},
{
"path": "/image",
"title": "Image"
}
]
}
@ -779,6 +787,10 @@
{
"path": "/divider",
"title": "Divider"
},
{
"path": "/image",
"title": "Image"
}
]
}
@ -1041,6 +1053,10 @@
{
"path": "/divider",
"title": "Divider"
},
{
"path": "/image",
"title": "Image 图片"
}
]
}

8
packages/image/index.js Normal file
View File

@ -0,0 +1,8 @@
import Image from './src/main';
/* istanbul ignore next */
Image.install = function(Vue) {
Vue.component(Image.name, Image);
};
export default Image;

123
packages/image/src/main.vue Normal file
View File

@ -0,0 +1,123 @@
<template>
<div class="el-image">
<slot v-if="loading" name="placeholder">
<div class="el-image__placeholder"></div>
</slot>
<slot v-else-if="error" name="error">
<div class="el-image__error">{{ t('el.image.error') }}</div>
</slot>
<img
v-else
class="el-image__inner"
:src="src"
:alt="alt"
:style="{ 'object-fit': fit }">
</div>
</template>
<script>
import Locale from 'element-ui/src/mixins/locale';
import { on, off, getScrollContainer, isInContainer } from 'element-ui/src/utils/dom';
import { isString, isHtmlElement } from 'element-ui/src/utils/types';
import throttle from 'throttle-debounce/throttle';
export default {
name: 'ElImage',
mixins: [Locale],
props: {
src: String,
fit: String,
lazy: Boolean,
scrollContainer: [String, HTMLElement],
alt: String
},
data() {
return {
loading: true,
error: false,
show: !this.lazy
};
},
watch: {
src: {
handler(val) {
this.show && this.loadImage(val);
},
immediate: true
},
show(val) {
val && this.loadImage(this.src);
}
},
mounted() {
this.lazy && this.addLazyLoadListener();
},
beforeDestroy() {
this.lazy && this.removeLazyLoadListener();
},
methods: {
loadImage(val) {
// reset status
this.loading = true;
this.error = false;
const img = new Image();
img.onload = this.handleLoad.bind(this);
img.onerror = this.handleError.bind(this);
img.src = val;
},
handleLoad(e) {
this.loading = false;
this.$emit('load', e);
},
handleError(e) {
this.loading = false;
this.error = true;
this.$emit('error', e);
},
handleLazyLoad() {
if (isInContainer(this.$el, this._scrollContainer)) {
this.show = true;
this.removeLazyLoadListener();
}
},
addLazyLoadListener() {
if (this.$isServer) return;
const { scrollContainer } = this;
let _scrollContainer = null;
if (isHtmlElement(scrollContainer)) {
_scrollContainer = scrollContainer;
} else if (isString(scrollContainer)) {
_scrollContainer = document.querySelector(scrollContainer);
} else {
_scrollContainer = getScrollContainer(this.$el);
}
if (_scrollContainer) {
this._scrollContainer = _scrollContainer;
this._lazyLoadHandler = throttle(200, this.handleLazyLoad);
on(_scrollContainer, 'scroll', this._lazyLoadHandler);
this.handleLazyLoad();
}
},
removeLazyLoadListener() {
const { _scrollContainer, _lazyLoadHandler } = this;
if (this.$isServer || !_scrollContainer || !_lazyLoadHandler) return;
off(_scrollContainer, 'scroll', _lazyLoadHandler);
this._scrollContainer = null;
this._lazyLoadHandler = null;
}
}
};
</script>

View File

@ -0,0 +1,32 @@
@import "mixins/mixins";
@import "common/var";
%size {
width: 100%;
height: 100%;
}
@include b(image) {
display: inline-block;
@include e(inner) {
@extend %size;
vertical-align: top;
}
@include e(placeholder) {
@extend %size;
background: $--background-color-base;
}
@include e(error) {
@extend %size;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
background: $--background-color-base;
color: $--color-text-placeholder;
vertical-align: middle;
}
}

View File

@ -68,3 +68,4 @@
@import "./timeline.scss";
@import "./timeline-item.scss";
@import "./divider.scss";
@import "./image.scss";

View File

@ -72,6 +72,7 @@ import Footer from '../packages/footer/index.js';
import Timeline from '../packages/timeline/index.js';
import TimelineItem from '../packages/timeline-item/index.js';
import Divider from '../packages/divider/index.js';
import Image from '../packages/image/index.js';
import locale from 'element-ui/src/locale';
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
@ -144,6 +145,7 @@ const components = [
Timeline,
TimelineItem,
Divider,
Image,
CollapseTransition
];
@ -254,5 +256,6 @@ export default {
Footer,
Timeline,
TimelineItem,
Divider
Divider,
Image
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Voer sleutelwoord in',
noCheckedFormat: '{total} items',
hasCheckedFormat: '{checked}/{total} gekies'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'ادخل كلمة',
noCheckedFormat: '{total} عناصر',
hasCheckedFormat: '{checked}/{total} مختار'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Introdueix la paraula clau',
noCheckedFormat: '{total} ítems',
hasCheckedFormat: '{checked}/{total} seleccionats'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -106,6 +106,9 @@ export default {
filterPlaceholder: 'Klíčové slovo',
noCheckedFormat: '{total} položek',
hasCheckedFormat: '{checked}/{total} vybráno'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Indtast søgeord',
noCheckedFormat: '{total} emner',
hasCheckedFormat: '{checked}/{total} valgt'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -105,6 +105,9 @@ export default {
filterPlaceholder: 'Einträge filtern',
noCheckedFormat: '{total} Einträge',
hasCheckedFormat: '{checked}/{total} ausgewählt'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Sisesta märksõna',
noCheckedFormat: '{total} objekti',
hasCheckedFormat: '{checked}/{total} valitud'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Αναζήτηση',
noCheckedFormat: '{total} Αντικείμενα',
hasCheckedFormat: '{checked}/{total} επιλεγμένα'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED'
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Ingresar palabra clave',
noCheckedFormat: '{total} artículos',
hasCheckedFormat: '{checked}/{total} revisados'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Sartu gako-hitza', // to be translated
noCheckedFormat: '{total} elementu', // to be translated
hasCheckedFormat: '{checked}/{total} hautatuta' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'کلید واژه هارو وارد کن',
noCheckedFormat: '{total} مورد',
hasCheckedFormat: '{checked} مورد از {total} مورد انتخاب شده است'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Syötä hakusana',
noCheckedFormat: '{total} kohdetta',
hasCheckedFormat: '{checked}/{total} valittu'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Entrer un mot clef',
noCheckedFormat: '{total} elements',
hasCheckedFormat: '{checked}/{total} coché(s)'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'הקלד',
noCheckedFormat: 'פריטים {total}',
hasCheckedFormat: ' אישור {checked}/{total}'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Unesite ključnu riječ', // to be translated
noCheckedFormat: '{total} stavki', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Kulcsszó',
noCheckedFormat: '{total} elem',
hasCheckedFormat: '{checked}/{total} kiválasztva'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Մուտքագրեք բանալի բառ',
noCheckedFormat: '{total} միաւոր',
hasCheckedFormat: '{checked}/{total} ընտրուած է'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Masukan kata kunci',
noCheckedFormat: '{total} butir',
hasCheckedFormat: '{checked}/{total} terpilih'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Inserisci filtro',
noCheckedFormat: '{total} elementi',
hasCheckedFormat: '{checked}/{total} selezionati'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'キーワードを入力',
noCheckedFormat: '総計 {total} 件',
hasCheckedFormat: '{checked}/{total} を選択した'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Сураныч, издөө кирет',
noCheckedFormat: 'бүтүндөй {total} сан',
hasCheckedFormat: 'Тандалган {checked}/{total} сан'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'បញ្ចូលពាក្យ',
noCheckedFormat: '{total} ធាតុ',
hasCheckedFormat: '{checked}/{total} បានគូសធីក'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: ' 입력하세요',
noCheckedFormat: '{total} 항목',
hasCheckedFormat: '{checked}/{total} 선택됨'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Binivîse',
noCheckedFormat: '{total} lib',
hasCheckedFormat: '{checked}/{total} bijartin'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Кілт сөзді енгізіңіз',
noCheckedFormat: '{total} элэмэнт',
hasCheckedFormat: '{checked}/{total} құсбелгісі қойылды'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Įvesk raktažodį',
noCheckedFormat: 'Viso: {total}',
hasCheckedFormat: 'Pažymėta {checked} iš {total}'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Ievadīt atslēgvārdu',
noCheckedFormat: '{total} vienības',
hasCheckedFormat: '{checked}/{total} atzīmēti'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Утга оруул',
noCheckedFormat: '{total} өгөгдөл',
hasCheckedFormat: '{checked}/{total} сонгосон'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Geef zoekwoerd',
noCheckedFormat: '{total} items',
hasCheckedFormat: '{checked}/{total} geselecteerd'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Wpisz szukaną frazę',
noCheckedFormat: 'razem: {total}',
hasCheckedFormat: 'wybranych: {checked}/{total}'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Digite uma palavra-chave',
noCheckedFormat: '{total} itens',
hasCheckedFormat: '{checked}/{total} selecionados'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Introduceți cuvântul cheie',
noCheckedFormat: '{total} elemente',
hasCheckedFormat: '{checked}/{total} verificate'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Введите ключевое слово',
noCheckedFormat: '{total} пунктов',
hasCheckedFormat: '{checked}/{total} выбрано'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -106,6 +106,9 @@ export default {
filterPlaceholder: 'Filtrovať podľa',
noCheckedFormat: '{total} položiek',
hasCheckedFormat: '{checked}/{total} označených'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Vnesi ključno besedo',
noCheckedFormat: '{total} elementov',
hasCheckedFormat: '{checked}/{total} izbranih'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Унеси кључну реч', // to be translated
noCheckedFormat: '{total} ставки', // to be translated
hasCheckedFormat: '{checked}/{total} обележених' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -103,6 +103,9 @@ export default {
filterPlaceholder: 'சொல்லை உள்ளீடு செய்',
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} தேர்வு செய்யப்பட்டவைகள்'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Gözleg sözlerini giriziň',
noCheckedFormat: '{total} sany',
hasCheckedFormat: '{checked}/{total} saýlanan'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Anahtar kelimeleri gir',
noCheckedFormat: '{total} adet',
hasCheckedFormat: '{checked}/{total} seçildi'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Введіть ключове слово',
noCheckedFormat: '{total} пунктів',
hasCheckedFormat: '{checked}/{total} вибрано'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'ئىزدىمەكچى بولغان مەزمۇننى كىرگۈزۈڭ',
noCheckedFormat: 'جەمئىي {total} تۈر',
hasCheckedFormat: 'تاللانغىنى {checked}/{total} تۈر'
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Nhập từ khóa',
noCheckedFormat: '{total} mục',
hasCheckedFormat: '{checked}/{total} đã chọn '
},
image: {
error: 'FAILED' // to be translated
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: '请输入搜索内容',
noCheckedFormat: '共 {total} 项',
hasCheckedFormat: '已选 {checked}/{total} 项'
},
image: {
error: '加载失败'
}
}
};

View File

@ -104,6 +104,9 @@ export default {
filterPlaceholder: 'Enter keyword', // to be translated
noCheckedFormat: '{total} items', // to be translated
hasCheckedFormat: '{checked}/{total} checked' // to be translated
},
image: {
error: '加載失敗'
}
}
};

View File

@ -172,3 +172,56 @@ export function setStyle(element, styleName, value) {
}
}
};
export const isScroll = (el, vertical) => {
if (isServer) return;
const determinedDirection = vertical !== null || vertical !== undefined;
const overflow = determinedDirection
? vertical
? getStyle(el, 'overflow-y')
: getStyle(el, 'overflow-x')
: getStyle(el, 'overflow');
return overflow.match(/(scroll|auto)/);
};
export const getScrollContainer = (el, vertical) => {
if (isServer) return;
let parent = el;
while (parent) {
if ([window, document, document.documentElement].includes(parent)) {
return window;
}
if (isScroll(parent, vertical)) {
return parent;
}
parent = parent.parentNode;
}
return parent;
};
export const isInContainer = (el, container) => {
if (isServer || !el || !container) return false;
const elRect = el.getBoundingClientRect();
let containerRect;
if ([window, document, document.documentElement, null, undefined].includes(container)) {
containerRect = {
top: 0,
right: window.innerWidth,
bottom: window.innerHeight,
left: 0
};
} else {
containerRect = container.getBoundingClientRect();
}
return elRect.top < containerRect.bottom &&
elRect.bottom > containerRect.top &&
elRect.right > containerRect.left &&
elRect.left < containerRect.right;
};

View File

@ -1,3 +1,11 @@
export function isString(obj) {
return Object.prototype.toString.call(obj) === '[object String]';
}
export function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
export function isHtmlElement(node) {
return node && node.nodeType === Node.ELEMENT_NODE;
}

View File

@ -0,0 +1,68 @@
import { createTest, createVue, destroyVM, wait } from '../util';
import Image from 'packages/image';
const src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFmklEQVR4Xu2ba2wUVRTH/2cWtlaooFQwKpr4CkESTQBNUAkodGeaKFGD2u6WGPyAmmAEEZmpj1VhBghE/KKGBIztDIXAByWBnQ1oQREfARNiDDHiF1+ooBiksVt295hFiqSdxx3KdmcH5uv+z+3//O65c19TwgX+0AWePy4CuFgBISCQ3J6+DFLuVQKmg2k4gK3xuvwr6+9e+Xe57YViCCRttY1ALX2S/asIPLRB1jvLCSEUAFK2egKgYf0T5a6TxdhtmxqXfl8uCBUHMHfP4rqeE0OOeyS4w5T1hsgCmLNTvaWYp299EpxoyvpX5YBQ8QpotlvvkMBfeCbHvMpUjOejCSCjPSYROrySY/B+SzYmRRJA0tZWEfCcJwBGz81yvDZN6eL5hlDxIZCyta8BTPBLjIv5sVbjyp/8dEF/ryiAlL3kJkD6TsQ0STS5vWHZPhFtEE1FAYiUf28yBaZ7OpRle4IkJ6KtGIDZexfU1hyv/QXASBGjBeYpHYrxmYg2iKZiAFIZdSGIVouaLVJx0obE8v2ielFdRQA83pkemc/lDgE0StRopF6CSVuzCGgWTb6ku7T+aHztpLUng8SIaAe9ApJ265MEflvE3P8a/t2UjTHBYsTUgwqgKauOk4p0gAhxMXunVcwfmYpxX6AYQfGgAWjqTNdL3bndRDRe0NsZGYPXWLKxIGiciH5QAJye8r4UWfE5mWbCE1ZCXy+SUFBN2QEo2+fXjKK6zSDcH9Rcr55i0vj2mUsPnmu8V1xZAZxKXhq+G6A7z9U8Mx+zFOOKc433iysbgNKYj3XnNoHoXj8T3r/z+6ZsPDiwNtyjHQE07dTGSAU8QkX4LlSY6Ndh9UfWnT1Ht2QXjeZifC8INw7UOAMfE2MgB6NdhaFo65ih/+bkxRFAMqPuIaK7xM3zu6ZszO3Vt2S1CcwobXPD8TAfoZjU6LSb7AdgWmd6yLW5nqArrm5T1mtDC+A/Y3tNWe/XqY4VkLK1fwBcItp9DGZLNqQwA2DmgiUbQ0Hgs/NyBpBRD4PoqigBKOWSGxuv2XxruscXQNJWDxJoXJQAMPCnJev9XuouQ0D9PMjcXQ1DAIx9pqJP7tupLrOAZhMhEaUKAGOjqehNYgBsdSOBHo0UAOBlU9ZfFwOQUd8honmRAsA8y1SMrWIAbHUFgRZHCUCe6LqNiWU/igHIaCoR9KgAYMZRS9GvFF4Kp7LqU2B6KzoAeKulGLOEASRtrZkAK0IAVEsxlgsDaM6ojRLRtqgAIImntTcYu4UBpLKtU8D8aRQAMJD/uSZeu2t6Oi8MILldHU8SfRMFAG67wN7cHFeCcz5svaZ4koWvosO9FGbdlI1Wt850BNCSXTSMOX4iEhVAaDAT+o5AAEriVEYrgHBmj+8FI7QVwJzLjei+fPOUN0rnG46P66FoMqMdIUK9SBWEGIDvjZIrgFRGOyR6qBlWAMzQLEU3vDrRvQJsdR+BJlZzBYh8VuM1BHYSQehCMpwVwF1mwqjrewYotBk69RK0tS0AHq7eChC7UPEYAto6As6c9VfdLED8tJkwfL9DcH8J2upqgBZWbwXgalPWD/v59wCgvQTgNb8GSr+H7x3AB0zZuF3EuyuA5oz6jET0pkgjYQPAzEstxSh1oO/jVQFzALzn20IIK0CKFa5vm7niBxHvHgsh9QEQfSDSSP8KWDSaOe54GyvS3sA0vMuUjemibbgPgWzrVInZ8RChb+N9AQSdRkXNiul4qikbn4hp4f5/g7N3vDAiXpCOEcj/IwpG0VT02Nl/9NSOshjvGMinMaJJ9OqYeYulGLODxHkml7TVZ8GYT0Q3uDfKXQxaY8n6i06aZnvJDGKaB8JMAo0IYk5UW7r5BbAtNhRz22YYf4jGlXT+vRuktSrUXgRQhZ12Xi1f8BXwL38cy1+mrtJNAAAAAElFTkSuQmCC';
describe('Image', () => {
let vm;
afterEach(() => {
destroyVM(vm);
});
it('create', async() => {
vm = createTest(Image, {
src,
fit: 'fill'
}, true);
const placeholder = vm.$el.querySelector('.el-image__placeholder');
const error = vm.$el.querySelector('.el-image__error');
let img = vm.$el.querySelector('.el-image__inner');
expect(placeholder).to.exist;
expect(error).to.be.null;
expect(img).to.be.null;
await wait();
img = vm.$el.querySelector('.el-image__inner');
expect(img.style.objectFit).to.equal('fill');
});
it('load failed', async() => {
vm = createTest(Image, true);
await wait();
const error = vm.$el.querySelector('.el-image__error');
expect(error).to.be.exist;
});
it('lazy load', async() => {
vm = createVue({
template: `
<div ref="wrapper" style="height: 200px; overflow: auto;">
<el-image
v-for="item in 2"
:key="item"
:src="src"
ref="image"
style="display: block; height: 200px;"
lazy></el-image>
</div>
`,
data() {
return {
src
};
}
}, true);
const { image, wrapper } = vm.$refs;
const [image1, image2] = image;
await wait();
expect(image1.loading).to.be.false;
expect(image2.loading).to.be.true;
wrapper.scrollTop = 10;
await wait();
expect(image2.loading).to.be.false;
});
});

34
types/image.d.ts vendored Normal file
View File

@ -0,0 +1,34 @@
import { VNode } from 'vue'
import { ElementUIComponent } from './component'
export type ObjectFit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
export interface ImageSlots {
/** Placeholder content when image hasn't loaded yet */
placeholder: VNode[]
/** Error content when error occurs to image load */
error: VNode[]
[key: string]: VNode[]
}
/** Image Component */
export declare class ElImage extends ElementUIComponent {
/** Image source */
src: string
/** Indicate how the image should be resized to fit its container, same as native 'object-fit' */
fit: ObjectFit
/** Whether to use lazy load */
lazy: boolean
/** Scroll container that to add scroll listener when using lazy load */
scrollContainer: string | HTMLElement
/** Native 'alt' attribute */
alt: string
$slots: ImageSlots
}