diff --git a/packages/element-plus/index.ts b/packages/element-plus/index.ts index 63483c7578..61c0c0b983 100644 --- a/packages/element-plus/index.ts +++ b/packages/element-plus/index.ts @@ -84,6 +84,8 @@ import ElTree from '@element-plus/tree' import ElUpload from '@element-plus/upload' import ElVirtualList from '@element-plus/virtual-list' import ElSpace from '@element-plus/space' +import ElSkeleton from '@element-plus/skeleton' +import ElSkeletonItem from '@element-plus/skeleton-item' import { use } from '@element-plus/locale' // if you encountered problems alike "Can't resolve './version'" // please run `yarn bootstrap` first @@ -181,6 +183,8 @@ const components = [ ElUpload, ElVirtualList, ElSpace, + ElSkeleton, + ElSkeletonItem, ] const plugins = [ @@ -292,6 +296,8 @@ export { ElUpload, ElVirtualList, ElSpace, + ElSkeleton, + ElSkeletonItem, version, install, locale, diff --git a/packages/hooks/index.ts b/packages/hooks/index.ts index b839a5ff54..bafcae6678 100644 --- a/packages/hooks/index.ts +++ b/packages/hooks/index.ts @@ -5,3 +5,4 @@ export { default as useRestoreActive } from './use-restore-active' export { default as useModal } from './use-modal' export { default as useMigrating } from './use-migrating' export { default as useFocus } from './use-focus' +export { default as useThrottleRender } from './use-throttle-render' diff --git a/packages/hooks/use-throttle-render/index.ts b/packages/hooks/use-throttle-render/index.ts new file mode 100644 index 0000000000..7cab4ffebf --- /dev/null +++ b/packages/hooks/use-throttle-render/index.ts @@ -0,0 +1,31 @@ +import { onMounted, ref, watch } from 'vue' +import type { Ref } from 'vue' + +export default function( + loading: Ref, + throttle = 0, +) { + + if (throttle === 0) return loading + const throttled = ref(false) + let timeoutHandle: TimeoutHandle = 0 + + const dispatchThrottling = () => { + if (timeoutHandle) { + clearTimeout(timeoutHandle) + } + timeoutHandle = window.setTimeout(() => { + throttled.value = loading.value + }, throttle) + } + onMounted(dispatchThrottling) + + watch(() => loading.value, val => { + if (val) { + dispatchThrottling() + } else { + throttled.value = val + } + }) + return throttled +} diff --git a/packages/skeleton-item/__tests__/skeleton-item.spec.ts b/packages/skeleton-item/__tests__/skeleton-item.spec.ts new file mode 100644 index 0000000000..80ea88135e --- /dev/null +++ b/packages/skeleton-item/__tests__/skeleton-item.spec.ts @@ -0,0 +1,24 @@ +import makeMount from '@element-plus/test-utils/make-mount' +import SkeletonItem from '../src/index.vue' + +describe('', () => { + const mount = makeMount(SkeletonItem, {}) + + test('should render correctly', () => { + const wrapper = mount() + + expect(wrapper.find('.el-skeleton__text').exists()).toBe(true) + }) + + test('should render image placeholder', () => { + + const wrapper = mount({ + props: { + variant: 'image', + }, + }) + + expect(wrapper.find('svg').exists()).toBe(true) + }) + +}) diff --git a/packages/skeleton-item/index.ts b/packages/skeleton-item/index.ts new file mode 100644 index 0000000000..82fe7c1f9b --- /dev/null +++ b/packages/skeleton-item/index.ts @@ -0,0 +1,8 @@ +import { App } from 'vue' +import SkeletonItem from './src/index.vue' + +SkeletonItem.install = (app: App): void => { + app.component(SkeletonItem.name, SkeletonItem) +} + +export default SkeletonItem diff --git a/packages/skeleton-item/package.json b/packages/skeleton-item/package.json new file mode 100644 index 0000000000..e90f9aab46 --- /dev/null +++ b/packages/skeleton-item/package.json @@ -0,0 +1,12 @@ +{ + "name": "@element-plus/skeleton-item", + "version": "0.0.0", + "main": "dist/index.js", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.3" + }, + "devDependencies": { + "@vue/test-utils": "^2.0.0-beta.3" + } +} diff --git a/packages/skeleton-item/src/constants.ts b/packages/skeleton-item/src/constants.ts new file mode 100644 index 0000000000..1543314994 --- /dev/null +++ b/packages/skeleton-item/src/constants.ts @@ -0,0 +1,10 @@ +export type Variants = + | 'circle' + | 'rect' + | 'h1' + | 'h3' + | 'text' + | 'caption' + | 'p' + | 'image' + | 'button' diff --git a/packages/skeleton-item/src/img-placeholder.vue b/packages/skeleton-item/src/img-placeholder.vue new file mode 100644 index 0000000000..b9b194b367 --- /dev/null +++ b/packages/skeleton-item/src/img-placeholder.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/skeleton-item/src/index.vue b/packages/skeleton-item/src/index.vue new file mode 100644 index 0000000000..6f31bec89e --- /dev/null +++ b/packages/skeleton-item/src/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/packages/skeleton/__tests__/skeleton.spec.ts b/packages/skeleton/__tests__/skeleton.spec.ts new file mode 100644 index 0000000000..c24ce7cbbb --- /dev/null +++ b/packages/skeleton/__tests__/skeleton.spec.ts @@ -0,0 +1,91 @@ +import { nextTick } from 'vue' +import makeMount from '@element-plus/test-utils/make-mount' +import Skeleton from '../src/index.vue' +const AXIOM = 'AXIOM is the best girl' + +jest.useFakeTimers() + +describe('Skeleton.vue', () => { + const mount = makeMount(Skeleton, { }) + test('render test', () => { + const wrapper = mount() + expect(wrapper.findAll('.el-skeleton__p')).toHaveLength(4) + expect(wrapper.classes()).toContain('el-skeleton') + + }) + + test('should render with animation', () => { + const wrapper = mount({ + props: { + animated: true, + }, + }) + + expect(wrapper.classes()).toContain('is-animated') + }) + + test('should render x times', async () => { + const wrapper = mount() + + expect(wrapper.findAll('.el-skeleton__p')).toHaveLength(4) + + await wrapper.setProps({ + count: 2, + }) + + expect(wrapper.findAll('.el-skeleton__p')).toHaveLength(8) + }) + + + test('should render x rows', () => { + const wrapper = mount({ + props: { + rows: 4, + }, + }) + + expect(wrapper.findAll('.el-skeleton__p')).toHaveLength(5) + }) + + test('should render default slots', () => { + const wrapper = mount({ + slots: { + default: () => AXIOM, + }, + props: { + loading: false, + }, + }) + + expect(wrapper.text()).toBe(AXIOM) + }) + + test('should render templates', () => { + + const wrapper = mount({ + slots: { + template: () => AXIOM, + }, + }) + + expect(wrapper.text()).toBe(AXIOM) + }) + + + test('should throttle rendering', async () => { + const wrapper = mount({ + props: { + throttle: 500, + }, + }) + + expect((wrapper.vm as any).uiLoading).toBe(false) + + jest.runAllTimers() + + await nextTick() + + expect((wrapper.vm as any).uiLoading).toBe(true) + }) + +}) diff --git a/packages/skeleton/index.ts b/packages/skeleton/index.ts new file mode 100644 index 0000000000..8e469d1c5d --- /dev/null +++ b/packages/skeleton/index.ts @@ -0,0 +1,8 @@ +import { App } from 'vue' +import Skeleton from './src/index.vue' + +Skeleton.install = (app: App): void => { + app.component(Skeleton.name, Skeleton) +} + +export default Skeleton diff --git a/packages/skeleton/package.json b/packages/skeleton/package.json new file mode 100644 index 0000000000..aa1e554921 --- /dev/null +++ b/packages/skeleton/package.json @@ -0,0 +1,12 @@ +{ + "name": "@element-plus/skeleton", + "version": "0.0.0", + "main": "dist/index.js", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.3" + }, + "devDependencies": { + "@vue/test-utils": "^2.0.0-beta.3" + } +} diff --git a/packages/skeleton/src/index.vue b/packages/skeleton/src/index.vue new file mode 100644 index 0000000000..480844d55e --- /dev/null +++ b/packages/skeleton/src/index.vue @@ -0,0 +1,68 @@ + + + diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 8dfd01e575..20a6b9fcea 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -65,6 +65,10 @@ $--border-color-lighter: #EBEEF5 !default; /// color|1|Border Color|3 $--border-color-extra-light: #F2F6FC !default; +// Variants + +$--heading-padding: 20px 0 !default; + // Background /// color|1|Background Color|4 $--background-color-base: #F5F7FA !default; @@ -96,6 +100,9 @@ $--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !de $--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default; /// boxShadow|1|Shadow|1 $--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default; +/* Svg +--------------- */ +$--svg-monochrome-grey: #DCDDE0; /* Fill -------------------------- */ @@ -964,6 +971,9 @@ $--avatar-medium-size: 36px !default; /// size|1|Avatar Size|3 $--avatar-small-size: 28px !default; +$--skeleton-color: #f2f2f2; +$--skeleton-to-color: #e6e6e6; + /* Break-point --------------------------*/ $--sm: 768px !default; diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index 7780484b73..cccab96a13 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -80,4 +80,6 @@ @import "./popconfirm.scss"; @import "./overlay.scss"; @import "./virtual-list.scss"; -@import "./space.scss"; \ No newline at end of file +@import "./space.scss"; +@import "./skeleton.scss"; +@import "./skeleton-item.scss"; \ No newline at end of file diff --git a/packages/theme-chalk/src/skeleton-item.scss b/packages/theme-chalk/src/skeleton-item.scss new file mode 100644 index 0000000000..687c3c4ea9 --- /dev/null +++ b/packages/theme-chalk/src/skeleton-item.scss @@ -0,0 +1,84 @@ +@import 'mixins/mixins'; +@import 'common/var'; + +@mixin circle-size($size) { + width: $size; + height: $size; + line-height: $size; +} + +@include b(skeleton) { + @include e(item) { + background: $--skeleton-color; + display: inline-block; + height: 16px; + border-radius: $--border-radius-base; + width: 100%; + } + + @include e(circle) { + border-radius: 50%; + @include circle-size($--avatar-medium-size); + + @include m(lg) { + @include circle-size($--avatar-large-size); + } + + @include m(md) { + @include circle-size($--avatar-small-size); + } + } + + @include e(button) { + height: 40px; + width: 64px; + border-radius: 4px; + } + + @include e(p) { + width: 100%; + @include when(last) { + width: 61%; + } + + @include when(first) { + width: 33%; + } + } + + @include e(text) { + width: 100%; + height: $--font-size-small; + } + + @include e(caption) { + height: $--font-size-extra-small; + } + + @include e(h1) { + height: $--font-size-extra-large; + } + + @include e(h3) { + height: $--font-size-large; + } + + @include e(h5) { + height: $--font-size-medium; + } + + @include e(image) { + width: unset; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0; + + svg { + fill: $--svg-monochrome-grey; + width: 22%; + height: 22%; + } + } + +} diff --git a/packages/theme-chalk/src/skeleton.scss b/packages/theme-chalk/src/skeleton.scss new file mode 100644 index 0000000000..01ed4f2c79 --- /dev/null +++ b/packages/theme-chalk/src/skeleton.scss @@ -0,0 +1,40 @@ +@import 'mixins/mixins'; +@import 'common/var'; +@import "./skeleton-item.scss"; + +@mixin skeleton-color() { + background: linear-gradient( + 90deg, + $--skeleton-color 25%, + $--skeleton-to-color 37%, + $--skeleton-color 63% + ); + background-size: 400% 100%; + animation: el-skeleton-loading 1.4s ease infinite; +} + +@keyframes el-skeleton-loading { + 0% { + background-position: 100% 50%; + } + 100% { + background-position: 0 50%; + } +} + +@include b(skeleton) { + width: 100%; + @each $unit in (first-line, paragraph) { + @include e($unit) { + height: 16px; + margin-top: 16px; + background: $--skeleton-color; + } + } + + @include when(animated) { + .el-skeleton__item { + @include skeleton-color(); + } + } +} diff --git a/website/demo-styles/index.scss b/website/demo-styles/index.scss index 2186b96d25..4df50dbb69 100644 --- a/website/demo-styles/index.scss +++ b/website/demo-styles/index.scss @@ -44,4 +44,4 @@ @import "./infinite-scroll.scss"; @import "./avatar.scss"; @import "./drawer.scss"; - +@import "./skeleton.scss"; diff --git a/website/demo-styles/skeleton.scss b/website/demo-styles/skeleton.scss new file mode 100644 index 0000000000..d68bd7068a --- /dev/null +++ b/website/demo-styles/skeleton.scss @@ -0,0 +1,40 @@ +.demo-block.demo-skeleton { + + .el-card { + margin-bottom: 16px; + } + + .time { + font-size: 13px; + color: #999; + } + + .bottom { + margin-top: 13px; + line-height: 12px; + } + + .button { + padding: 0; + float: right; + } + + .image { + &.multi-content { + width: 400px; + height: 267px; + } + width: 100%; + display: block; + } + + .clearfix:before, + .clearfix:after { + display: table; + content: ''; + } + + .clearfix:after { + clear: both; + } +} diff --git a/website/docs/en-US/skeleton.md b/website/docs/en-US/skeleton.md new file mode 100644 index 0000000000..00c868f3d7 --- /dev/null +++ b/website/docs/en-US/skeleton.md @@ -0,0 +1,319 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, for more precise rendering effect, the actual rendered row number will always be 1 row more than the given number, that is because we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the real DOM | boolean | true / false | false | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 3 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | \ No newline at end of file diff --git a/website/docs/es/skeleton.md b/website/docs/es/skeleton.md new file mode 100644 index 0000000000..00c868f3d7 --- /dev/null +++ b/website/docs/es/skeleton.md @@ -0,0 +1,319 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, for more precise rendering effect, the actual rendered row number will always be 1 row more than the given number, that is because we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the real DOM | boolean | true / false | false | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 3 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | \ No newline at end of file diff --git a/website/docs/fr-FR/skeleton.md b/website/docs/fr-FR/skeleton.md new file mode 100644 index 0000000000..00c868f3d7 --- /dev/null +++ b/website/docs/fr-FR/skeleton.md @@ -0,0 +1,319 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, for more precise rendering effect, the actual rendered row number will always be 1 row more than the given number, that is because we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the real DOM | boolean | true / false | false | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 3 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | \ No newline at end of file diff --git a/website/docs/jp/skeleton.md b/website/docs/jp/skeleton.md new file mode 100644 index 0000000000..00c868f3d7 --- /dev/null +++ b/website/docs/jp/skeleton.md @@ -0,0 +1,319 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, for more precise rendering effect, the actual rendered row number will always be 1 row more than the given number, that is because we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the real DOM | boolean | true / false | false | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 3 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default Value | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | \ No newline at end of file diff --git a/website/docs/zh-CN/skeleton.md b/website/docs/zh-CN/skeleton.md new file mode 100644 index 0000000000..b4de6f52c8 --- /dev/null +++ b/website/docs/zh-CN/skeleton.md @@ -0,0 +1,325 @@ +## Skeleton 骨架屏 + +在需要等待加载内容的位置设置一个骨架屏, 某些场景下比 Loading 的视觉效果更好。 + +### 基础用法 + +基础的骨架效果。 + +:::demo + +```html + +``` + +::: + +### 更多参数 + +可以配置骨架屏段落数量, 以便更接近真实渲染效果. 显示的数量会比传入的数量多 1, 首行会被渲染一个长度 33% 的段首. + +:::demo + +```html + +``` + +::: + +### 动画效果 + +显示动画效果。 + +:::demo + +```html + +``` + +::: + +### 自定义样式 + +ElementPlus 提供的排版模式有时候并不满足要求, 当您想要用自己定义的模板时, 可以通过一个具名 Slot 来自己设定模板. + +我们提供了不同的模板单元可供使用, 具体可选值请看 API 详细描述. 建议在描述模板的时候, 尽量靠近真实的 DOM 结构, 这样可以避免 DOM 高度差距引起的抖动. +:::demo + +```html + +``` + +::: + +### Loading 状态 + +当 Loading 结束之后, 我们往往需要显示真实的 UI, 可以通过 `loading` 的值来控制是否显示真实的 DOM. 然后通过 +具名 Slot 来设置当 loading 结束之后需要展示的 UI. + +:::demo + +```html + + + +``` + +::: + +### 渲染多条数据 + +大多时候, 骨架屏都被用来渲染列表, 当我们需要在从服务器获取数据的时候来渲染一个假的 UI. 利用 `count` 这个属性就能控制渲染多少条假的数据在页面上 + +:::tip +请注意, 请尽可能的将 `count` 的大小保持在最小状态, 即使是假的 UI, DOM 元素多了之后, 照样会引起性能问题, 并且在骨架屏销毁时所消耗的时间也会更长(相对的). +::: + +:::demo + + +```html + + + +``` + +::: + +### 防止渲染抖动 + +有的时候, API 的请求回来的特别快, 往往骨架占位刚刚被渲染, 真实的数据就已经回来了, 用户的界面会突然一闪, 此时为了避免这种情况, 就需要通过 `throttle` 属性来避免这个问题. + +:::demo + +```html + + + +``` + +::: + +### Skeleton Attributes + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| -------- | ------------------------------------------- | ------- | ------------ | ------ | +| animated | 是否使用动画 | boolean | true / false | false | +| count | 渲染多少个 template, 建议使用尽可能小的数字 | number | integer | 1 | +| loading | 是否显示真实的 DOM 结构 | boolean | true / false | false | +| rows | 骨架屏段落数量 | number | 正整数 | 3 | +| throttle | 延迟占位 DOM 渲染的时间, 单位是毫秒 | number | 正整数 | 0 | + +### Skeleton Item Attributes + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| ------- | ------------------------ | ------------ | -------------------------------------------------------------------- | ------ | +| variant | 当前显示的占位元素的样式 | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + +### Skeleton Slots + +| name | description | +| -------- | -------------------- | +| default |  用来展示真实 UI | +| template | 用来展示自定义占位符 | diff --git a/website/nav.config.json b/website/nav.config.json index a404976a5f..8b5444a20e 100644 --- a/website/nav.config.json +++ b/website/nav.config.json @@ -184,6 +184,10 @@ { "path": "/avatar", "title": "Avatar 头像" + }, + { + "path": "/skeleton", + "title": "Skeleton 骨架屏" } ] }, @@ -486,6 +490,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] }, @@ -792,6 +800,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] }, @@ -1098,6 +1110,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] }, @@ -1404,6 +1420,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] },