refactor: icon component

This commit is contained in:
chenjiahan 2020-07-04 22:39:13 +08:00
parent 425ffb87eb
commit 3bc6495b04
9 changed files with 611 additions and 8 deletions

99
src-next/icon/README.md Normal file
View File

@ -0,0 +1,99 @@
# Icon
### Install
```js
import Vue from 'vue';
import { Icon } from 'vant';
Vue.use(Icon);
```
## Usage
### Basic Usage
Use `name` prop to set icon name or icon URL
```html
<van-icon name="chat-o" />
<van-icon name="https://b.yzcdn.cn/vant/icon-demo-1126.png" />
```
### Show Badge
Use `dot` prop, a small red dot will be displayed in the upper right corner of the icon.
Use `badge` prop, the badge will be displayed in the upper right corner of the icon.
```html
<van-icon name="chat-o" dot />
<van-icon name="chat-o" badge="9" />
<van-icon name="chat-o" badge="99+" />
```
### Icon Color
Use `color` prop to set icon color
```html
<van-icon name="chat-o" color="#1989fa" />
<van-icon name="chat-o" color="#07c160" />
```
### Icon Size
Use `size` prop to set icon size
```html
<van-icon name="chat-o" size="40" /> <van-icon name="chat-o" size="3rem" />
```
### Use local font file
Icon uses font file in `yzcdn.cn` by defaultif you want to use the local font fileplease import the following css file.
```js
import 'vant/lib/icon/local.css';
```
### Add custom iconfont
```css
@font-face {
font-family: 'my-icon';
src: url('./my-icon.ttf') format('truetype');
}
.my-icon {
font-family: 'my-icon';
}
.my-icon-extra::before {
content: '\e626';
}
```
```html
<van-icon class-prefix="my-icon" name="extra" />
```
## API
### Props
| Attribute | Description | Type | Default |
| -------------- | ----------------------- | ------------------ | ---------- |
| name | Icon name or URL | _string_ | `''` |
| dot `v2.2.1` | Whether to show red dot | _boolean_ | `false` |
| badge `v2.5.6` | Content of the badge | _number \| string_ | `''` |
| color | Icon color | _string_ | `inherit` |
| size | Icon size | _number \| string_ | `inherit` |
| class-prefix | ClassName prefix | _string_ | `van-icon` |
| tag | HTML Tag | _string_ | `i` |
### Events
| Event | Description | Arguments |
| ----- | ------------------------- | -------------- |
| click | Triggered when click icon | _event: Event_ |

View File

@ -0,0 +1,106 @@
# Icon 图标
### 介绍
基于字体的图标集,可以通过 Icon 组件使用,也可以在其他组件中通过`icon`属性引用
### 引入
```js
import Vue from 'vue';
import { Icon } from 'vant';
Vue.use(Icon);
```
## 代码演示
### 基础用法
`Icon``name`属性支持传入图标名称或图片链接,所有可用的图标名称见右侧示例
```html
<van-icon name="chat-o" />
<van-icon name="https://b.yzcdn.cn/vant/icon-demo-1126.png" />
```
### 徽标提示
设置`dot`属性后,会在图标右上角展示一个小红点。设置`badge`属性后,会在图标右上角展示相应的徽标
```html
<van-icon name="chat-o" dot />
<van-icon name="chat-o" badge="9" />
<van-icon name="chat-o" badge="99+" />
```
### 图标颜色
`Icon``color`属性用来设置图标的颜色
```html
<van-icon name="chat-o" color="#1989fa" />
<van-icon name="chat-o" color="#07c160" />
```
### 图标大小
`Icon``size`属性用来设置图标的尺寸大小,默认单位为`px`
```html
<van-icon name="chat-o" size="40" /> <van-icon name="chat-o" size="3rem" />
```
### 使用本地字体文件
Icon 组件默认引用有赞 CDN 提供的字体文件,并通过网络下载。如果需要在项目中使用本地字体文件,请引入下面的 CSS 文件,并在项目中配置`url-loader`
```js
import 'vant/lib/icon/local.css';
```
### 自定义图标
如果需要在现有 Icon 的基础上使用更多图标,可以引入第三方 iconfont 对应的字体文件和 CSS 文件,之后就可以在 Icon 组件中直接使用
```css
/* 引入第三方或自定义的字体图标样式 */
@font-face {
font-family: 'my-icon';
src: url('./my-icon.ttf') format('truetype');
}
.my-icon {
font-family: 'my-icon';
}
.my-icon-extra::before {
content: '\e626';
}
```
```html
<!-- 通过 class-prefix 指定类名为 my-icon -->
<van-icon class-prefix="my-icon" name="extra" />
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| name | 图标名称或图片链接 | _string_ | - |
| dot `v2.2.1` | 是否显示图标右上角小红点 | _boolean_ | `false` |
| badge `v2.5.6` | 图标右上角徽标的内容 | _number \| string_ | - |
| info | 图标右上角徽标的内容(已废弃,请使用 badge 属性) | _number \| string_ | - |
| color | 图标颜色 | _string_ | `inherit` |
| size | 图标大小,如 `20px` `2em`,默认单位为`px` | _number \| string_ | `inherit` |
| class-prefix | 类名前缀,用于使用自定义图标 | _string_ | `van-icon` |
| tag | HTML 标签 | _string_ | `i` |
### Events
| 事件名 | 说明 | 回调参数 |
| ------ | -------------- | -------------- |
| click | 点击图标时触发 | _event: Event_ |

View File

@ -0,0 +1,229 @@
<template>
<demo-section>
<van-tabs v-model="tab" sticky :color="BLUE">
<van-tab :title="t('demo')">
<demo-block :title="t('basicUsage')">
<van-col span="6" @click="copy(demoIcon)">
<van-icon :name="demoIcon" />
</van-col>
<van-col span="6" @click="copy(demoImage)">
<van-icon :name="demoImage" />
</van-col>
</demo-block>
<demo-block :title="t('badge')">
<van-col span="6" @click="copy(demoIcon, { dot: true })">
<van-icon :name="demoIcon" dot />
</van-col>
<van-col span="6" @click="copy(demoIcon, { badge: '9' })">
<van-icon :name="demoIcon" badge="9" />
</van-col>
<van-col span="6" @click="copy(demoIcon, { badge: '99+' })">
<van-icon :name="demoIcon" badge="99+" />
</van-col>
</demo-block>
<demo-block :title="t('color')">
<van-col span="6" @click="copy(demoIcon, { color: BLUE })">
<van-icon :name="demoIcon" :color="BLUE" />
</van-col>
<van-col span="6" @click="copy(demoIcon, { color: GREEN })">
<van-icon :name="demoIcon" :color="GREEN" />
</van-col>
</demo-block>
<demo-block :title="t('size')">
<van-col span="6" @click="copy(demoIcon, { size: '40' })">
<van-icon :name="demoIcon" size="40" />
</van-col>
<van-col span="6" @click="copy(demoIcon, { size: '3rem' })">
<van-icon :name="demoIcon" size="3rem" />
</van-col>
</demo-block>
</van-tab>
<van-tab :title="t('basic')">
<van-col
v-for="icon in icons.basic"
:key="icon"
span="6"
@click="copy(icon)"
>
<van-icon :name="icon" />
<span>{{ icon }}</span>
</van-col>
</van-tab>
<van-tab :title="t('outline')">
<van-col
v-for="icon in icons.outline"
:key="icon"
span="6"
@click="copy(icon)"
>
<van-icon :name="icon" />
<span>{{ icon }}</span>
</van-col>
</van-tab>
<van-tab :title="t('filled')">
<van-col
v-for="icon in icons.filled"
:key="icon"
span="6"
@click="copy(icon)"
>
<van-icon :name="icon" />
<span>{{ icon }}</span>
</van-col>
</van-tab>
</van-tabs>
</demo-section>
</template>
<script>
import icons from '@vant/icons';
import { BLUE, GREEN } from '../../../src/utils/constant';
// from https://30secondsofcode.org
function copyToClipboard(str) {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
const selected =
document.getSelection().rangeCount > 0
? document.getSelection().getRangeAt(0)
: false;
el.select();
document.execCommand('copy');
document.body.removeChild(el);
if (selected) {
document.getSelection().removeAllRanges();
document.getSelection().addRange(selected);
}
}
export default {
i18n: {
'zh-CN': {
title: '图标列表',
badge: '徽标提示',
basic: '基础图标',
copied: '复制成功',
outline: '线框风格',
filled: '实底风格',
demo: '用法示例',
color: '图标颜色',
size: '图标大小',
},
'en-US': {
title: 'Icon List',
badge: 'Show Badge',
basic: 'Basic',
copied: 'Copied',
outline: 'Outline',
filled: 'Filled',
demo: 'Demo',
color: 'Icon Color',
size: 'Icon Size',
},
},
data() {
this.BLUE = BLUE;
this.GREEN = GREEN;
this.icons = icons;
return {
tab: 0,
demoIcon: 'chat-o',
demoImage: 'https://b.yzcdn.cn/vant/icon-demo-1126.png',
};
},
methods: {
copy(icon, option = {}) {
let tag = `<van-icon name="${icon}"`;
if ('dot' in option) {
tag = `${tag} ${option.dot ? 'dot' : ''}`;
}
if ('badge' in option) {
tag = `${tag} badge="${option.badge}"`;
}
if ('color' in option) {
tag = `${tag} color="${option.color}"`;
}
if ('size' in option) {
tag = `${tag} size="${option.size}"`;
}
tag = `${tag} />`;
copyToClipboard(tag);
this.$notify({
type: 'success',
duration: 1500,
className: 'demo-icon-notify',
message: `${this.t('copied')}${tag}`,
});
},
},
};
</script>
<style lang="less">
@import '../../style/var';
.demo-icon {
font-size: 0;
&-list {
box-sizing: border-box;
min-height: calc(100vh - 65px);
padding-top: 10px;
}
&-notify {
font-size: 13px;
}
.van-col {
display: inline-block;
float: none;
text-align: center;
vertical-align: middle;
cursor: pointer;
span {
display: block;
height: 36px;
margin: -4px 0 4px;
padding: 0 5px;
color: @gray-7;
font-size: 12px;
line-height: 18px;
}
&:active {
background-color: @active-color;
}
}
.van-icon {
margin: 16px 0 16px;
color: @text-color;
font-size: 32px;
}
.van-tab__pane {
width: auto;
margin: 20px;
background-color: #fff;
border-radius: 12px;
}
}
</style>

55
src-next/icon/index.js Normal file
View File

@ -0,0 +1,55 @@
// Utils
import { addUnit, isDef } from '../../src/utils';
import { createNamespace } from '../utils/create';
// Components
import Info from '../info';
const [createComponent, bem] = createNamespace('icon');
function isImage(name) {
return name ? name.indexOf('/') !== -1 : false;
}
export default createComponent({
props: {
dot: Boolean,
name: String,
size: [Number, String],
badge: [Number, String],
color: String,
tag: {
type: String,
default: 'i',
},
classPrefix: {
type: String,
default: bem(),
},
},
render() {
const { name } = this;
const imageIcon = isImage(name);
return (
<this.tag
class={[
this.classPrefix,
imageIcon ? '' : `${this.classPrefix}-${name}`,
]}
style={{
color: this.color,
fontSize: addUnit(this.size),
}}
>
{/* {slots.default && slots.default()} */}
{imageIcon && <img class={bem('image')} src={name} />}
<Info
dot={this.dot}
info={this.badge}
/>
</this.tag>
);
}
});

10
src-next/icon/index.less Normal file
View File

@ -0,0 +1,10 @@
@import '../style/var';
@import '~@vant/icons/src/index.less';
.van-icon {
&__image {
width: 1em;
height: 1em;
object-fit: contain;
}
}

1
src-next/icon/local.less Normal file
View File

@ -0,0 +1 @@
@import '~@vant/icons/src/encode.less';

View File

@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`dot prop 1`] = `
<i class="van-icon van-icon-undefined">
<div class="van-info van-info--dot"></div>
</i>
`;
exports[`render icon default slot 1`] = `
<i class="van-icon van-icon-success">Default slot
<!----></i>
`;
exports[`render icon with builtin icon name 1`] = `
<i class="van-icon van-icon-success">
<!----></i>
`;
exports[`render icon with local image 1`] = `
<i class="van-icon"><img src="/assets/icon.jpg" class="van-icon__image">
<!----></i>
`;
exports[`render icon with url name 1`] = `
<i class="van-icon"><img src="https://img.yzcdn.com/icon.jpg" class="van-icon__image">
<!----></i>
`;
exports[`size without unit 1`] = `
<i class="van-icon van-icon-undefined" style="font-size: 20px;">
<!----></i>
`;
exports[`tag prop 1`] = `
<div class="van-icon van-icon-undefined">
<!---->
</div>
`;

View File

@ -0,0 +1,65 @@
import Icon from '..';
import { mount } from '../../../test';
test('render icon with builtin icon name', () => {
const wrapper = mount(Icon, {
propsData: {
name: 'success',
},
});
expect(wrapper).toMatchSnapshot();
});
test('render icon with url name', () => {
const wrapper = mount(Icon, {
propsData: {
name: 'https://img.yzcdn.com/icon.jpg',
},
});
expect(wrapper).toMatchSnapshot();
});
test('render icon with local image', () => {
const wrapper = mount(Icon, {
propsData: {
name: '/assets/icon.jpg',
},
});
expect(wrapper).toMatchSnapshot();
});
test('render icon default slot', () => {
const wrapper = mount({
render(h) {
return h(Icon, { props: { name: 'success' } }, ['Default slot']);
},
});
expect(wrapper).toMatchSnapshot();
});
test('tag prop', () => {
const wrapper = mount(Icon, {
propsData: {
tag: 'div',
},
});
expect(wrapper).toMatchSnapshot();
});
test('dot prop', () => {
const wrapper = mount(Icon, {
propsData: {
dot: true,
},
});
expect(wrapper).toMatchSnapshot();
});
test('size without unit', () => {
const wrapper = mount(Icon, {
propsData: {
size: 20,
},
});
expect(wrapper).toMatchSnapshot();
});

View File

@ -86,10 +86,10 @@ module.exports = {
// path: 'cell', // path: 'cell',
// title: 'Cell 单元格', // title: 'Cell 单元格',
// }, // },
// { {
// path: 'icon', path: 'icon',
// title: 'Icon 图标', title: 'Icon 图标',
// }, },
// { // {
// path: 'image', // path: 'image',
// title: 'Image 图片', // title: 'Image 图片',
@ -433,10 +433,10 @@ module.exports = {
// path: 'cell', // path: 'cell',
// title: 'Cell', // title: 'Cell',
// }, // },
// { {
// path: 'icon', path: 'icon',
// title: 'Icon', title: 'Icon',
// }, },
// { // {
// path: 'image', // path: 'image',
// title: 'Image', // title: 'Image',