Compare commits

...

7 Commits

Author SHA1 Message Date
neverland
842c92d3c9
perf(@vant/cli): generate smaller web-types.json (#11226) 2022-11-05 22:13:06 +08:00
neverland
8dcfb5c29a
fix(Skeleton): round prop not work for title (#11225) 2022-11-05 21:38:15 +08:00
哭你几哇
19cd0667e4
feat(skeleton-item): add skeleton-item component (#11173)
* feat(skeleton-item): add skeleton-item component

* docs(Skeleton): update docs

* feat(Skeleton): add skeleton title avatar paragraph image componets

* chore: add uncommit files

* test(Skeleton): fix skeleton test case failed

* feat(Skeleton-Image): add skeleton-image style

* test(Skeleton): fix failed test case

* docs(Skeleton): update skeleton docs

* fix(Skeleton): add skeleton image theme vars

* perf(Skeleton): use Icon componet in Skeleton image to reduce bundle size

* docs(Skeleton): update skeleton docs

* docs(Skeleton): fix wrong component name
2022-11-05 21:17:23 +08:00
neverland
3991f27a02
docs(changelog): @vant/cli@5.1.0 (#11224) 2022-11-05 20:42:44 +08:00
chenjiahan
7c1ce45db8 release: @vant/cli 5.1.0 2022-11-05 20:40:28 +08:00
neverland
fe07c10729
feat(cli): support vite.config.ts (#11223) 2022-11-05 20:39:51 +08:00
neverland
d9fe675af4
feat(cli): allow to custom server.port (#11222) 2022-11-05 20:03:58 +08:00
26 changed files with 631 additions and 124 deletions

View File

@ -1,5 +1,12 @@
# 更新日志
## v5.1.0
`2022-11-05`
- 支持读取 `vite.config.ts` 文件来自定义 vite 配置
- 修复设置 vite 的 `server.port` 配置项不生效的问题
## v5.0.2
`2022-10-07`

View File

@ -177,17 +177,16 @@ When set to `true`, `export * from 'xxx'` will be used to export all modules and
### build.configureVite
- Type: `(config: InlineConfig): InlineConfig`
- Type: `(config: InlineConfig): InlineConfig | undefined`
- Default: `undefined`
Custom vite config(`@vant/cli>= 4.0.0`)
Custom [vite config](https://vitejs.dev/config/), requires `@vant/cli>= 4.0.0`.
```js
module.exports = {
build: {
configureVite(config) {
// add vite plugin
config.plugins.push(vitePluginXXX);
config.server.port = 3000;
return config;
},
},
@ -214,6 +213,10 @@ module.exports = {
};
```
Note that you are not allowed to import vite plugins in `vant.config.mjs`, because the file will be bundled into the website code.
If you need to configure some vite plugins, please create a `vite.config.ts` file in the same directory of `vant.config.mjs`, in which you can add any vite configuration (this feature requires @vant/cli 5.1.0).
### build.packageManager
- Type: `'npm' | 'yarn' | 'pnpm'`

View File

@ -177,17 +177,16 @@ module.exports = {
### build.configureVite
- Type: `(config: InlineConfig): InlineConfig`
- Type: `(config: InlineConfig): InlineConfig | undefined`
- Default: `undefined`
vant-cli 使用 vite 来构建组件库和文档站点,通过 `configureVite` 选项可以自定义 vite 配置(从 4.0.0 版本开始支持)。
vant-cli 使用 vite 来构建组件库和文档站点,通过 `configureVite` 选项可以自定义 [vite 配置](https://vitejs.dev/config/)(从 4.0.0 版本开始支持)。
```js
module.exports = {
build: {
configureVite(config) {
// 添加一个自定义插件
config.plugins.push(vitePluginXXX);
config.server.port = 3000;
return config;
},
},
@ -216,6 +215,10 @@ module.exports = {
};
```
注意,由于 `vant.config.mjs` 文件会被打包到文档网站的代码中,因此 `configureVite` 中不允许引用 vite 插件。
如果需要配置 vite 插件,可以在 `vant.config.mjs` 的同级目录下创建 `vite.config.ts` 文件,在该文件中你可以添加任意的 vite 配置(该特性从 @vant/cli 5.1.0 版本开始支持)。
### build.packageManager
- Type: `'npm' | 'yarn' | 'pnpm'`

View File

@ -1,6 +1,6 @@
{
"name": "@vant/cli",
"version": "5.0.2",
"version": "5.1.0",
"type": "module",
"main": "lib/index.js",
"typings": "lib/index.d.ts",

View File

@ -1,7 +1,7 @@
import fse from 'fs-extra';
import { sep, join } from 'path';
import { SRC_DIR, getVantConfig } from './constant.js';
import type { InlineConfig } from 'vite';
import { InlineConfig, loadConfigFromFile, mergeConfig } from 'vite';
const { lstatSync, existsSync, readdirSync, readFileSync, outputFileSync } =
fse;
@ -114,13 +114,33 @@ export function smartOutputFile(filePath: string, content: string) {
outputFileSync(filePath, content);
}
export function mergeCustomViteConfig(config: InlineConfig) {
export async function mergeCustomViteConfig(
config: InlineConfig,
mode: 'production' | 'development'
): Promise<InlineConfig> {
const vantConfig = getVantConfig();
const configureVite = vantConfig.build?.configureVite;
const userConfig = await loadConfigFromFile(
{
mode,
command: mode === 'development' ? 'serve' : 'build',
},
undefined,
process.cwd()
);
if (configureVite) {
return configureVite(config);
const ret = configureVite(config);
if (ret) {
config = ret;
}
}
if (userConfig) {
return mergeConfig(config, userConfig.config);
}
return config;
}

View File

@ -34,8 +34,13 @@ export async function compileBundles() {
getVantConfig().build?.bundleOptions || DEFAULT_OPTIONS;
await Promise.all(
bundleOptions.map((config) =>
build(mergeCustomViteConfig(getViteConfigForPackage(config)))
bundleOptions.map(async (config) =>
build(
await mergeCustomViteConfig(
getViteConfigForPackage(config),
'production'
)
)
)
);
}

View File

@ -29,12 +29,18 @@ export function genSiteEntry(): Promise<void> {
export async function compileSite(production = false) {
await genSiteEntry();
if (production) {
const config = mergeCustomViteConfig(getViteConfigForSiteProd());
const config = await mergeCustomViteConfig(
getViteConfigForSiteProd(),
'production'
);
await build(config);
} else {
const config = mergeCustomViteConfig(getViteConfigForSiteDev());
const config = await mergeCustomViteConfig(
getViteConfigForSiteDev(),
'development'
);
const server = await createServer(config);
await server.listen();
await server.listen(config.server?.port);
const require = createRequire(import.meta.url);
const { version } = require('vite/package.json');

View File

@ -1,11 +1,6 @@
/* eslint-disable no-continue */
import { Articles } from './parser.js';
import {
formatOptions,
formatType,
removeVersion,
toKebabCase,
} from './utils.js';
import { formatType, removeVersion, toKebabCase } from './utils.js';
import { VueEventArgument, VueTag } from './type.js';
function formatComponentName(name: string, tagPrefix: string) {
@ -66,9 +61,6 @@ function findTag(vueTags: VueTag[], name: string) {
const newTag: VueTag = {
name,
slots: [],
events: [],
attributes: [],
};
vueTags.push(newTag);
@ -107,12 +99,16 @@ export function formatter(
const tag = findTag(vueTags, name);
table.body.forEach((line) => {
const [name, desc, type, defaultVal, options] = line;
tag.attributes!.push({
const [name, desc, type, defaultVal] = line;
if (!tag.attributes) {
tag.attributes = [];
}
tag.attributes.push({
name: removeVersion(name),
default: defaultVal,
description: desc,
options: formatOptions(options),
value: {
type: formatType(type),
kind: 'expression',
@ -128,7 +124,12 @@ export function formatter(
table.body.forEach((line) => {
const [name, desc, args] = line;
tag.events!.push({
if (!tag.events) {
tag.events = [];
}
tag.events.push({
name: removeVersion(name),
description: desc,
arguments: formatArguments(args),
@ -143,7 +144,12 @@ export function formatter(
table.body.forEach((line) => {
const [name, desc] = line;
tag.slots!.push({
if (!tag.slots) {
tag.slots = [];
}
tag.slots.push({
name: removeVersion(name),
description: desc,
});

View File

@ -36,7 +36,7 @@ export async function parseAndWrite(options: Options) {
const webTypes = genWebTypes(vueTags, options);
fse.outputFileSync(
join(options.outputDir, 'web-types.json'),
JSON.stringify(webTypes, null, 2)
JSON.stringify(webTypes)
);
}

View File

@ -20,7 +20,6 @@ export type VueAttribute = {
name: string;
default: string;
description: string;
options: string[];
value: {
kind: 'expression';
type: string;

View File

@ -20,9 +20,3 @@ export function formatType(type: string) {
export function normalizePath(path: string): string {
return path.replace(/\\/g, '/');
}
// `default` `primary` -> ['default', 'primary']
export function formatOptions(options?: string) {
if (!options) return [];
return options.replace(/`/g, '').split(' ');
}

View File

@ -12,8 +12,8 @@ export function genWebTypes(tags: VueTag[], options: Options) {
html: {
tags,
attributes: [],
'types-syntax': 'typescript',
},
},
'js-types-syntax': 'typescript',
};
}

View File

@ -10,10 +10,20 @@ Register component globally via `app.use`, refer to [Component Registration](#/e
```js
import { createApp } from 'vue';
import { Skeleton } from 'vant';
import {
Skeleton,
SkeletonTitle,
SkeletonImage,
SkeletonAvatar,
SkeletonParagraph,
} from 'vant';
const app = createApp();
app.use(Skeleton);
app.use(SkeletonTitle);
app.use(SkeletonImage);
app.use(SkeletonAvatar);
app.use(SkeletonParagraph);
```
## Usage
@ -56,9 +66,29 @@ export default {
};
```
### Custom Content
Using `template` slots to custom skeleton content.
```html
<van-skeleton>
<template #template>
<div :style="{ display: 'flex', width: '100%' }">
<van-skeleton-image />
<div :style="{ flex: 1, marginLeft: '16px' }">
<van-skeleton-paragraph row-width="60%" />
<van-skeleton-paragraph />
<van-skeleton-paragraph />
<van-skeleton-paragraph />
</div>
</div>
</template>
</van-skeleton>
```
## API
### Props
### Skeleton Props
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
@ -68,17 +98,59 @@ export default {
| avatar | Whether to show avatar placeholder | _boolean_ | `false` |
| loading | Whether to show skeleton, pass `false` to show child component | _boolean_ | `true` |
| animate | Whether to enable animation | _boolean_ | `true` |
| round | Whether to show round title and row | _boolean_ | `false` |
| round | Whether to show round title and paragraph | _boolean_ | `false` |
| title-width | Title width | _number \| string_ | `40%` |
| avatar-size | Size of avatar placeholder | _number \| string_ | `32px` |
| avatar-shape | Shape of avatar placeholder, can be set to `square` | _string_ | `round` |
### SkeletonParagraph Props
| Attribute | Description | Type | Default |
| --------- | ------------------------------- | --------- | ------- |
| round | Whether to show round paragraph | _boolean_ | `false` |
| row-width | Paragraph width | _string_ | `100%` |
### SkeletonTitle Props
| Attribute | Description | Type | Default |
| ----------- | --------------------------- | ------------------ | ------- |
| round | Whether to show round title | _boolean_ | `false` |
| title-width | Title width | _number \| string_ | `40%` |
### SkeletonAvatar Props
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| avatar-size | Size of avatar placeholder | _number \| string_ | `32px` |
| avatar-shape | Shape of avatar placeholder, can be set to `square` | _string_ | `round` |
### SkeletonImage Props
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| image-size | Size of image placeholder | _number \| string_ | `32px` |
| image-shape | Shape of image placeholder, can be set to `square` | _string_ | `round` |
### Skeleton Slots
| Name | Description |
| -------- | -------------- |
| default | Default slot |
| template | Custom content |
### Types
The component exports the following type definitions:
```ts
import type { SkeletonProps, SkeletonAvatarShape } from 'vant';
import type {
SkeletonProps,
SkeletonImageProps,
SkeletonTitleProps,
SkeletonAvatarShape,
SkeletonImageShape,
SkeletonParagraphProps,
} from 'vant';
```
## Theming
@ -96,3 +168,5 @@ The component provides the following CSS variables, which can be used to customi
| --van-skeleton-avatar-size | _32px_ | - |
| --van-skeleton-avatar-background | _var(--van-active-color)_ | - |
| --van-skeleton-duration | _1.2s_ | - |
| --van-skeleton-image-size | _96px_ |
| --van-skeleton-image-radius | _24px_ | - |

View File

@ -10,10 +10,20 @@
```js
import { createApp } from 'vue';
import { Skeleton } from 'vant';
import {
Skeleton,
SkeletonTitle,
SkeletonImage,
SkeletonAvatar,
SkeletonParagraph,
} from 'vant';
const app = createApp();
app.use(Skeleton);
app.use(SkeletonTitle);
app.use(SkeletonImage);
app.use(SkeletonAvatar);
app.use(SkeletonParagraph);
```
## 代码演示
@ -62,9 +72,29 @@ export default {
};
```
### 自定义展示内容
通过 `template` 插槽完成自定义内容的展示。
```html
<van-skeleton>
<template #template>
<div :style="{ display: 'flex', width: '100%' }">
<van-skeleton-image />
<div :style="{ flex: 1, marginLeft: '16px' }">
<van-skeleton-paragraph row-width="60%" />
<van-skeleton-paragraph />
<van-skeleton-paragraph />
<van-skeleton-paragraph />
</div>
</div>
</template>
</van-skeleton>
```
## API
### Props
### Skeleton Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
@ -79,12 +109,54 @@ export default {
| avatar-size | 头像占位图大小 | _number \| string_ | `32px` |
| avatar-shape | 头像占位图形状,可选值为 `square` | _string_ | `round` |
### SkeletonParagraph Props
| 参数 | 说明 | 类型 | 默认值 |
| --------- | ------------------------ | --------- | ------- |
| round | 是否将段落显示为圆角风格 | _boolean_ | `false` |
| row-width | 段落占位图宽度 | _string_ | `100%` |
### SkeletonTitle Props
| 参数 | 说明 | 类型 | 默认值 |
| ----------- | ------------------------ | ------------------ | ------- |
| round | 是否将标题显示为圆角风格 | _boolean_ | `false` |
| title-width | 标题占位图宽度 | _number \| string_ | `40%` |
### SkeletonAvatar Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| avatar-size | 头像占位图大小 | _number \| string_ | `32px` |
| avatar-shape | 头像占位图形状,可选值为 `square` | _string_ | `round` |
### SkeletonImage Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| image-size | 图片占位图大小 | _number \| string_ | `32px` |
| image-shape | 图片占位图形状,可选值为 `square` | _string_ | `round` |
### Skeleton Slots
| 名称 | 说明 |
| -------- | ---------- |
| default | 骨架屏内容 |
| template | 自定义内容 |
### 类型定义
组件导出以下类型定义:
```ts
import type { SkeletonProps, SkeletonAvatarShape } from 'vant';
import type {
SkeletonProps,
SkeletonImageProps,
SkeletonTitleProps,
SkeletonImageShape,
SkeletonAvatarShape,
SkeletonParagraphProps,
} from 'vant';
```
## 主题定制
@ -93,12 +165,14 @@ import type { SkeletonProps, SkeletonAvatarShape } from 'vant';
组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/config-provider)。
| 名称 | 默认值 | 描述 |
| -------------------------------- | ------------------------- | ---- |
| --van-skeleton-row-height | _16px_ | - |
| --van-skeleton-row-background | _var(--van-active-color)_ | - |
| --van-skeleton-row-margin-top | _var(--van-padding-sm)_ | - |
| --van-skeleton-title-width | _40%_ | - |
| --van-skeleton-avatar-size | _32px_ | - |
| --van-skeleton-avatar-background | _var(--van-active-color)_ | - |
| --van-skeleton-duration | _1.2s_ | - |
| 名称 | 默认值 | 描述 |
| ----------------------------------- | ------------------------- | ---- |
| --van-skeleton-paragraph-height | _16px_ | - |
| --van-skeleton-paragraph-background | _var(--van-active-color)_ | - |
| --van-skeleton-paragraph-margin-top | _var(--van-padding-sm)_ | - |
| --van-skeleton-title-width | _40%_ | - |
| --van-skeleton-avatar-size | _32px_ | - |
| --van-skeleton-avatar-background | _var(--van-active-color)_ | - |
| --van-skeleton-duration | _1.2s_ | - |
| --van-skeleton-image-size | _96px_ |
| --van-skeleton-image-radius | _24px_ | - |

View File

@ -1,31 +1,37 @@
import { defineComponent, type PropType, type ExtractPropTypes } from 'vue';
// Utils
import {
addUnit,
truthProp,
numericProp,
getSizeStyle,
makeStringProp,
makeNumericProp,
createNamespace,
type Numeric,
} from '../utils';
const [name, bem] = createNamespace('skeleton');
const DEFAULT_ROW_WIDTH = '100%';
const DEFAULT_LAST_ROW_WIDTH = '60%';
// Components
import SkeletonTitle from './SkeletonTitle';
import SkeletonAvatar from './SkeletonAvatar';
import SkeletonParagraph, { DEFAULT_ROW_WIDTH } from './SkeletonParagraph';
export type SkeletonAvatarShape = 'square' | 'round';
// Types
import type { SkeletonAvatarShape } from './types';
const [name, bem] = createNamespace('skeleton');
const DEFAULT_LAST_ROW_WIDTH = '60%';
export const skeletonProps = {
row: makeNumericProp(0),
title: Boolean,
round: Boolean,
title: Boolean,
titleWidth: numericProp,
avatar: Boolean,
avatarSize: numericProp,
avatarShape: makeStringProp<SkeletonAvatarShape>('round'),
loading: truthProp,
animate: truthProp,
avatarSize: numericProp,
titleWidth: numericProp,
avatarShape: makeStringProp<SkeletonAvatarShape>('round'),
rowWidth: {
type: [Number, String, Array] as PropType<Numeric | Numeric[]>,
default: DEFAULT_ROW_WIDTH,
@ -45,9 +51,9 @@ export default defineComponent({
const renderAvatar = () => {
if (props.avatar) {
return (
<div
class={bem('avatar', props.avatarShape)}
style={getSizeStyle(props.avatarSize)}
<SkeletonAvatar
avatarShape={props.avatarShape}
avatarSize={props.avatarSize}
/>
);
}
@ -56,10 +62,7 @@ export default defineComponent({
const renderTitle = () => {
if (props.title) {
return (
<h3
class={bem('title')}
style={{ width: addUnit(props.titleWidth) }}
/>
<SkeletonTitle round={props.round} titleWidth={props.titleWidth} />
);
}
};
@ -82,9 +85,29 @@ export default defineComponent({
Array(+props.row)
.fill('')
.map((_, i) => (
<div class={bem('row')} style={{ width: addUnit(getRowWidth(i)) }} />
<SkeletonParagraph
key={i}
round={props.round}
rowWidth={addUnit(getRowWidth(i))}
/>
));
const renderContents = () => {
if (slots.template) {
return slots.template();
}
return (
<>
{renderAvatar()}
<div class={bem('content')}>
{renderTitle()}
{renderRows()}
</div>
</>
);
};
return () => {
if (!props.loading) {
return slots.default?.();
@ -95,11 +118,7 @@ export default defineComponent({
class={bem({ animate: props.animate, round: props.round })}
{...attrs}
>
{renderAvatar()}
<div class={bem('content')}>
{renderTitle()}
{renderRows()}
</div>
{renderContents()}
</div>
);
};

View File

@ -0,0 +1,36 @@
import { defineComponent, ExtractPropTypes } from 'vue';
// Utils
import {
numericProp,
getSizeStyle,
makeStringProp,
createNamespace,
} from '../utils';
// Types
import type { SkeletonAvatarShape } from './types';
const [name, bem] = createNamespace('skeleton-avatar');
export const skeletonAvatarProps = {
avatarSize: numericProp,
avatarShape: makeStringProp<SkeletonAvatarShape>('round'),
};
export type SkeletonAvatarProps = ExtractPropTypes<typeof skeletonAvatarProps>;
export default defineComponent({
name,
props: skeletonAvatarProps,
setup(props) {
return () => (
<div
class={bem([props.avatarShape])}
style={getSizeStyle(props.avatarSize)}
/>
);
},
});

View File

@ -0,0 +1,39 @@
import { defineComponent, type ExtractPropTypes } from 'vue';
import {
numericProp,
getSizeStyle,
makeStringProp,
createNamespace,
} from '../utils';
// Types
import type { SkeletonImageShape } from './types';
import { Icon } from '../icon';
const [name, bem] = createNamespace('skeleton-image');
export const skeletonImageProps = {
imageSize: numericProp,
imageShape: makeStringProp<SkeletonImageShape>('square'),
};
export type SkeletonImageProps = ExtractPropTypes<typeof skeletonImageProps>;
export default defineComponent({
name,
props: skeletonImageProps,
setup(props) {
return () => (
<div
class={bem([props.imageShape])}
style={getSizeStyle(props.imageSize)}
>
<Icon name={'photo'} class={bem('icon')} />
</div>
);
},
});

View File

@ -0,0 +1,34 @@
import { defineComponent, ExtractPropTypes } from 'vue';
import { createNamespace, numericProp } from '../utils';
export const DEFAULT_ROW_WIDTH = '100%';
export const skeletonParagraphProps = {
round: Boolean,
rowWidth: {
type: numericProp,
default: DEFAULT_ROW_WIDTH,
},
};
export type SkeletonParagraphProps = ExtractPropTypes<
typeof skeletonParagraphProps
>;
const [name, bem] = createNamespace('skeleton-paragraph');
export default defineComponent({
name,
props: skeletonParagraphProps,
setup(props) {
return () => (
<div
class={bem([{ round: props.round }])}
style={{ width: props.rowWidth }}
/>
);
},
});

View File

@ -0,0 +1,27 @@
import { defineComponent, type ExtractPropTypes } from 'vue';
import { createNamespace, numericProp, addUnit } from '../utils';
const [name, bem] = createNamespace('skeleton-title');
export const skeletonTitleProps = {
round: Boolean,
titleWidth: numericProp,
};
export type SkeletonTitleProps = ExtractPropTypes<typeof skeletonTitleProps>;
export default defineComponent({
name,
props: skeletonTitleProps,
setup(props) {
return () => (
<h3
class={bem([{ round: props.round }])}
style={{ width: addUnit(props.titleWidth) }}
/>
);
},
});

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import VanSkeleton from '..';
import VanSkeleton, { VanSkeletonParagraph, VanSkeletonImage } from '..';
import VanSwitch from '../../switch';
import { ref } from 'vue';
import { cdnURL, useTranslate } from '../../../docs/site';
@ -10,12 +10,14 @@ const t = useTranslate({
showChildren: '显示子组件',
title: '关于 Vant',
desc: 'Vant 是一套轻量、可靠的移动端 Vue 组件库,提供了丰富的基础组件和业务组件,帮助开发者快速搭建移动应用。',
customContent: '自定义展示内容',
},
'en-US': {
showAvatar: 'Show Avatar',
showChildren: 'Show Children',
title: 'About Vant',
desc: 'Vant is a set of Mobile UI Components built on Vue.',
customContent: 'Custom Content',
},
});
@ -43,6 +45,22 @@ const show = ref(false);
</div>
</van-skeleton>
</demo-block>
<demo-block :title="t('customContent')">
<van-skeleton>
<template #template>
<div :style="{ display: 'flex', width: '100%' }">
<van-skeleton-image />
<div :style="{ flex: 1, marginLeft: '16px' }">
<van-skeleton-paragraph row-width="60%" />
<van-skeleton-paragraph />
<van-skeleton-paragraph />
<van-skeleton-paragraph />
</div>
</div>
</template>
</van-skeleton>
</demo-block>
</template>
<style lang="less">

View File

@ -1,10 +1,12 @@
:root {
--van-skeleton-row-height: 16px;
--van-skeleton-row-background: var(--van-active-color);
--van-skeleton-row-margin-top: var(--van-padding-sm);
--van-skeleton-paragraph-height: 16px;
--van-skeleton-paragraph-background: var(--van-active-color);
--van-skeleton-paragraph-margin-top: var(--van-padding-sm);
--van-skeleton-title-width: 40%;
--van-skeleton-avatar-size: 32px;
--van-skeleton-avatar-background: var(--van-active-color);
--van-skeleton-image-size: 96px;
--van-skeleton-image-radius: 24px;
--van-skeleton-duration: 1.2s;
}
@ -12,7 +14,7 @@
display: flex;
padding: 0 var(--van-padding-md);
&__avatar {
&-avatar {
flex-shrink: 0;
width: var(--van-skeleton-avatar-size);
height: var(--van-skeleton-avatar-size);
@ -28,28 +30,32 @@
width: 100%;
}
&__avatar + &__content {
&-avatar + &__content {
padding-top: var(--van-padding-xs);
}
&__row,
&__title {
height: var(--van-skeleton-row-height);
background: var(--van-skeleton-row-background);
&-paragraph,
&-title {
height: var(--van-skeleton-paragraph-height);
background: var(--van-skeleton-paragraph-background);
&--round {
border-radius: var(--van-radius-max);
}
}
&__title {
&-title {
width: var(--van-skeleton-title-width);
margin: 0;
}
&__row {
&-paragraph {
&:not(:first-child) {
margin-top: var(--van-skeleton-row-margin-top);
margin-top: var(--van-skeleton-paragraph-margin-top);
}
}
&__title + &__row {
&-title + &-paragraph {
margin-top: 20px;
}
@ -58,10 +64,23 @@
infinite;
}
&--round {
.van-skeleton__row,
.van-skeleton__title {
border-radius: var(--van-radius-max);
&-image {
display: flex;
width: var(--van-skeleton-image-size);
height: var(--van-skeleton-image-size);
align-items: center;
justify-content: center;
background: var(--van-active-color);
&--round {
border-radius: var(--van-skeleton-image-radius);
}
&__icon {
width: calc(var(--van-skeleton-image-size) / 2);
height: calc(var(--van-skeleton-image-size) / 2);
font-size: calc(var(--van-skeleton-image-size) / 2);
color: var(--van-gray-5);
}
}
}

View File

@ -1,14 +1,51 @@
import { withInstall } from '../utils';
import _Skeleton from './Skeleton';
import _SkeletonImage from './SkeletonImage';
import _SkeletonTitle from './SkeletonTitle';
import _SkeletonAvatar from './SkeletonAvatar';
import _SkeletonParagraph from './SkeletonParagraph';
import { withInstall } from '../utils';
export const VanSkeletonImage = withInstall(_SkeletonImage);
export const VanSkeletonTitle = withInstall(_SkeletonTitle);
export const VanSkeletonAvatar = withInstall(_SkeletonAvatar);
export const VanSkeletonParagraph = withInstall(_SkeletonParagraph);
export const Skeleton = withInstall(_Skeleton);
export default Skeleton;
// Skeleton
export { skeletonProps } from './Skeleton';
export type { SkeletonProps, SkeletonAvatarShape } from './Skeleton';
export type { SkeletonThemeVars } from './types';
export type { SkeletonProps } from './Skeleton';
// SkeletonImage
export { skeletonImageProps } from './SkeletonImage';
export type { SkeletonImageProps } from './SkeletonImage';
// SkeletonAvatar
export { skeletonAvatarProps } from './SkeletonAvatar';
export type { SkeletonAvatarProps } from './SkeletonAvatar';
// SkeletonParagraph
export { skeletonParagraphProps } from './SkeletonParagraph';
export type { SkeletonParagraphProps } from './SkeletonParagraph';
// SkeletonTitle
export { skeletonTitleProps } from './SkeletonTitle';
export type { SkeletonTitleProps } from './SkeletonTitle';
export type {
SkeletonThemeVars,
SkeletonImageShape,
SkeletonAvatarShape,
} from './types';
declare module 'vue' {
export interface GlobalComponents {
VanSkeleton: typeof Skeleton;
VanSkeletonImage: typeof VanSkeletonImage;
VanSkeletonTitle: typeof VanSkeletonTitle;
VanSkeletonAvatar: typeof VanSkeletonAvatar;
VanSkeletonParagraph: typeof VanSkeletonParagraph;
}
}

View File

@ -4,17 +4,17 @@ exports[`should render demo and match snapshot 1`] = `
<div>
<div class="van-skeleton van-skeleton--animate">
<div class="van-skeleton__content">
<h3 class="van-skeleton__title">
<h3 class="van-skeleton-title">
</h3>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 60%;"
>
</div>
@ -23,20 +23,20 @@ exports[`should render demo and match snapshot 1`] = `
</div>
<div>
<div class="van-skeleton van-skeleton--animate">
<div class="van-skeleton__avatar van-skeleton__avatar--round">
<div class="van-skeleton-avatar van-skeleton-avatar--round">
</div>
<div class="van-skeleton__content">
<h3 class="van-skeleton__title">
<h3 class="van-skeleton-title">
</h3>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 60%;"
>
</div>
@ -53,24 +53,52 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
<div class="van-skeleton van-skeleton--animate">
<div class="van-skeleton__avatar van-skeleton__avatar--round">
<div class="van-skeleton-avatar van-skeleton-avatar--round">
</div>
<div class="van-skeleton__content">
<h3 class="van-skeleton__title">
<h3 class="van-skeleton-title">
</h3>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 60%;"
>
</div>
</div>
</div>
</div>
<div>
<div class="van-skeleton van-skeleton--animate">
<div style="display: flex; width: 100%;">
<div class="van-skeleton-image van-skeleton-image--square">
<i class="van-badge__wrapper van-icon van-icon-photo van-skeleton-image__icon">
</i>
</div>
<div style="flex: 1; margin-left: 16px;">
<div class="van-skeleton-paragraph"
style="width: 60%;"
>
</div>
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -1,7 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should Skeleton works with template slots 1`] = `
<div class="van-skeleton van-skeleton--animate">
custom content
</div>
`;
exports[`should change avatar shape when using avatar-shape prop 1`] = `
<div class="van-skeleton__avatar van-skeleton__avatar--square">
<div class="van-skeleton-avatar van-skeleton-avatar--square">
</div>
`;
@ -18,20 +24,29 @@ exports[`should render default slot when loading is false 1`] = `
exports[`should render with row width array correctly 1`] = `
<div class="van-skeleton van-skeleton--animate">
<div class="van-skeleton__content">
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 30px;"
>
</div>
<div class="van-skeleton__row"
<div class="van-skeleton-paragraph"
style="width: 5rem;"
>
</div>
<div class="van-skeleton__row">
<div class="van-skeleton-paragraph"
style="width: 100%;"
>
</div>
</div>
</div>
`;
exports[`should skeleton image render correctly 1`] = `
<div class="van-skeleton-image van-skeleton-image--square">
<i class="van-badge__wrapper van-icon van-icon-photo van-skeleton-image__icon">
</i>
</div>
`;

View File

@ -1,5 +1,5 @@
import { mount } from '../../../test';
import { Skeleton } from '..';
import { Skeleton, VanSkeletonImage } from '..';
test('should render with row width array correctly', () => {
const wrapper = mount(Skeleton, {
@ -30,7 +30,7 @@ test('should change avatar size when using avatar-size prop', () => {
},
});
const avatar = wrapper.find('.van-skeleton__avatar');
const avatar = wrapper.find('.van-skeleton-avatar');
expect(avatar.style.width).toMatchSnapshot('20rem');
expect(avatar.style.height).toMatchSnapshot('20ren');
});
@ -42,7 +42,7 @@ test('should change avatar shape when using avatar-shape prop', () => {
avatarShape: 'square',
},
});
expect(wrapper.find('.van-skeleton__avatar').html()).toMatchSnapshot();
expect(wrapper.find('.van-skeleton-avatar').html()).toMatchSnapshot();
});
test('should be round when using round prop', () => {
@ -56,6 +56,16 @@ test('should be round when using round prop', () => {
expect(wrapper.find('.van-skeleton--round').exists()).toBeTruthy();
});
test('should Skeleton works with template slots', () => {
const wrapper = mount(Skeleton, {
slots: {
template: () => 'custom content',
},
});
expect(wrapper.html()).toMatchSnapshot();
});
test('should allow to disable animation', async () => {
const wrapper = mount(Skeleton, {
props: {
@ -68,3 +78,31 @@ test('should allow to disable animation', async () => {
await wrapper.setProps({ animate: false });
expect(wrapper.find('.van-skeleton--animate').exists()).toBeFalsy();
});
test('should skeleton image render correctly', () => {
const wrapper = mount(VanSkeletonImage);
expect(wrapper.html()).toMatchSnapshot();
});
test('should skeleton image works with imageSize prop', () => {
const wrapper = mount(VanSkeletonImage, {
props: {
imageSize: '20rem',
},
});
const dom = wrapper.find('.van-skeleton-image');
expect(dom.style.width).toBe('20rem');
expect(dom.style.height).toBe('20rem');
});
test('should skeleton image works with imageShape prop', () => {
const wrapper = mount(VanSkeletonImage, {
props: {
imageShape: 'round',
},
});
expect(wrapper.find('.van-skeleton-image--round')).toBeTruthy();
});

View File

@ -1,9 +1,15 @@
export type SkeletonAvatarShape = 'square' | 'round';
export type SkeletonImageShape = 'square' | 'round';
export type SkeletonThemeVars = {
skeletonRowHeight?: string;
skeletonRowBackground?: string;
skeletonRowMarginTop?: string;
skeletonParagraphHeight?: string;
skeletonParagraphBackground?: string;
skeletonParagraphMarginTop?: string;
skeletonTitleWidth?: string;
skeletonAvatarSize?: string;
skeletonAvatarBackground?: string;
SkeletonImageSize?: string;
SkeletonImageRadius?: string;
skeletonDuration?: string;
};