diff --git a/packages/arcodesign/components/index.ts b/packages/arcodesign/components/index.ts index 2131facf..b462e1a9 100644 --- a/packages/arcodesign/components/index.ts +++ b/packages/arcodesign/components/index.ts @@ -43,6 +43,7 @@ export { default as PullRefresh } from './pull-refresh'; export { default as Radio } from './radio'; export { default as Rate } from './rate'; export { default as SearchBar } from './search-bar'; +export { default as Skeleton } from './skeleton'; export { default as ShowMonitor } from './show-monitor'; export { default as Slider } from './slider'; export { default as Stepper } from './stepper'; diff --git a/packages/arcodesign/components/skeleton/README.en-US.md b/packages/arcodesign/components/skeleton/README.en-US.md new file mode 100644 index 00000000..eb0527ba --- /dev/null +++ b/packages/arcodesign/components/skeleton/README.en-US.md @@ -0,0 +1,101 @@ +### Data Display + +# Skeleton + +Display a set of placeholder graphics during content loading + +====== + +> Props + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|title|Show title placeholder|boolean \| SkeletonTitleProps|true| +|paragraph|Show paragraph placeholder|boolean \| SkeletonParagraphProps|true| +|avatar|Show Avatar placeholder|boolean \| SkeletonAvatarProps|false| +|grid|Show Grid placeholder\. When it's value is present, the paragraph, avatar or title placeholder will not be displayed, and four columns will be displayed by default|boolean \| SkeletonGridProps|false| +|showAnimation|Show loading effect|boolean|true| +|animation|Animation of loading effect, 'gradient' and 'breath' effects are optional|"gradient" \| "breath"|"gradient"| +|animationGradientColor|Highlight color of gradient animation|string|"rgba(0, 0, 0, 0.04)"| +|backgroundColor|Background color of skeleton item|string|"#F7F8FA"| +|children|Children element|ReactNode|null| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> Refs + +|Property|Description|Type| +|----------|-------------|------| +|dom|The outer DOM element of the component|HTMLDivElement| + +> Skeleton.Node + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|children|Custom component content|ReactNode|null| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> Skeleton.Title + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|width|The width of title|ReactText|"40%"| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> Skeleton.Paragraph + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|rows|Number of lines for paragraph|number|3| +|width|The width of paragraph\. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line|string \| number \| ReactText\[\]|"60%"| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> Skeleton.Avatar + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> Skeleton.Grid + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|columns|columns of grid|number|4| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> SkeletonTitleProps + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|width|The width of title|ReactText|"40%"| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> SkeletonParagraphProps + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|rows|Number of lines for paragraph|number|3| +|width|The width of paragraph\. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line|string \| number \| ReactText\[\]|"60%"| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> SkeletonAvatarProps + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| + +> SkeletonGridProps + +|Property|Description|Type|DefaultValue| +|----------|-------------|------|------| +|columns|columns of grid|number|4| +|className|Custom classname|string|""| +|style|Custom stylesheet|CSSProperties|{}| diff --git a/packages/arcodesign/components/skeleton/README.md b/packages/arcodesign/components/skeleton/README.md new file mode 100644 index 00000000..0a4852aa --- /dev/null +++ b/packages/arcodesign/components/skeleton/README.md @@ -0,0 +1,101 @@ +### 信息展示 + +# 骨架屏 Skeleton + +在内容加载过程中展示一组占位图形。 + +====== + +> 属性/Props + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|title|是否显示标题占位图|boolean \| SkeletonTitleProps|true| +|paragraph|是否显示段落占位图|boolean \| SkeletonParagraphProps|true| +|avatar|是否显示头像占位图|boolean \| SkeletonAvatarProps|false| +|grid|是否显示金刚位占位图(如该参数非空时,默认展示四列金刚位,且不展示标题/段落/头像占位符)|boolean \| SkeletonGridProps|false| +|showAnimation|是否展示动画效果|boolean|true| +|animation|加载动画效果,可选“扫光”、“呼吸”两种效果|"gradient" \| "breath"|"gradient"| +|animationGradientColor|扫光动效高光颜色|string|"rgba(0, 0, 0, 0.04)"| +|backgroundColor|占位块背景色|string|"#F7F8FA"| +|children|子元素|ReactNode|null| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> 引用/Refs + +|参数|描述|类型| +|----------|-------------|------| +|dom|最外层 DOM 元素|HTMLDivElement| + +> Skeleton.Node + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|children|自定义组件内容|ReactNode|null| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> Skeleton.Title + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|width|标题占位图宽度|ReactText|"40%"| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> Skeleton.Paragraph + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|rows|段落占位图的行数|number|3| +|width|段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度|string \| number \| ReactText\[\]|"60%"| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> Skeleton.Avatar + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> Skeleton.Grid + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|columns|金刚位列数|number|4| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> SkeletonTitleProps + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|width|标题占位图宽度|ReactText|"40%"| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> SkeletonParagraphProps + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|rows|段落占位图的行数|number|3| +|width|段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度|string \| number \| ReactText\[\]|"60%"| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> SkeletonAvatarProps + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| + +> SkeletonGridProps + +|参数|描述|类型|默认值| +|----------|-------------|------|------| +|columns|金刚位列数|number|4| +|className|自定义类名|string|""| +|style|自定义样式|CSSProperties|{}| diff --git a/packages/arcodesign/components/skeleton/__ast__/index.ast.json b/packages/arcodesign/components/skeleton/__ast__/index.ast.json new file mode 100644 index 00000000..2a021601 --- /dev/null +++ b/packages/arcodesign/components/skeleton/__ast__/index.ast.json @@ -0,0 +1,1007 @@ +{ + "description": "在内容加载过程中展示一组占位图形。", + "descriptionTags": { + "en": "Display a set of placeholder graphics during content loading", + "type": "信息展示", + "type_en": "Data Display", + "name": "骨架屏", + "name_en": "Skeleton" + }, + "displayName": "Skeleton", + "methods": [], + "props": { + "title": { + "defaultValue": { + "value": "true" + }, + "description": "是否显示标题占位图\n@en Show title placeholder", + "name": "title", + "tags": { + "en": "Show title placeholder", + "default": "true" + }, + "descWithTags": "是否显示标题占位图", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "boolean | SkeletonTitleProps" + } + }, + "paragraph": { + "defaultValue": { + "value": "true" + }, + "description": "是否显示段落占位图\n@en Show paragraph placeholder", + "name": "paragraph", + "tags": { + "en": "Show paragraph placeholder", + "default": "true" + }, + "descWithTags": "是否显示段落占位图", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "boolean | SkeletonParagraphProps" + } + }, + "avatar": { + "defaultValue": { + "value": "false" + }, + "description": "是否显示头像占位图\n@en Show Avatar placeholder", + "name": "avatar", + "tags": { + "en": "Show Avatar placeholder", + "default": "false" + }, + "descWithTags": "是否显示头像占位图", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "boolean | SkeletonAvatarProps" + } + }, + "grid": { + "defaultValue": { + "value": "false" + }, + "description": "是否显示金刚位占位图(如该参数非空时,默认展示四列金刚位,且不展示标题/段落/头像占位符)\n@en Show Grid placeholder. When it's value is present, the paragraph, avatar or title placeholder will not be displayed, and four columns will be displayed by default", + "name": "grid", + "tags": { + "en": "Show Grid placeholder. When it's value is present, the paragraph, avatar or title placeholder will not be displayed, and four columns will be displayed by default", + "default": "false" + }, + "descWithTags": "是否显示金刚位占位图(如该参数非空时,默认展示四列金刚位,且不展示标题/段落/头像占位符)", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "boolean | SkeletonGridProps" + } + }, + "showAnimation": { + "defaultValue": { + "value": "true" + }, + "description": "是否展示动画效果\n@en Show loading effect", + "name": "showAnimation", + "tags": { + "en": "Show loading effect", + "default": "true" + }, + "descWithTags": "是否展示动画效果", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "boolean" + } + }, + "animation": { + "defaultValue": { + "value": "\"gradient\"" + }, + "description": "加载动画效果,可选“扫光”、“呼吸”两种效果\n@en Animation of loading effect, 'gradient' and 'breath' effects are optional", + "name": "animation", + "tags": { + "en": "Animation of loading effect, 'gradient' and 'breath' effects are optional", + "default": "\"gradient\"" + }, + "descWithTags": "加载动画效果,可选“扫光”、“呼吸”两种效果", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "enum", + "raw": "\"gradient\" | \"breath\"", + "value": [ + { + "value": "\"gradient\"" + }, + { + "value": "\"breath\"" + } + ] + } + }, + "animationGradientColor": { + "defaultValue": { + "value": "\"rgba(0, 0, 0, 0.04)\"" + }, + "description": "扫光动效高光颜色\n@en Highlight color of gradient animation", + "name": "animationGradientColor", + "tags": { + "en": "Highlight color of gradient animation", + "default": "\"rgba(0, 0, 0, 0.04)\"" + }, + "descWithTags": "扫光动效高光颜色", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "backgroundColor": { + "defaultValue": { + "value": "\"#F7F8FA\"" + }, + "description": "占位块背景色\n@en Background color of skeleton item", + "name": "backgroundColor", + "tags": { + "en": "Background color of skeleton item", + "default": "\"#F7F8FA\"" + }, + "descWithTags": "占位块背景色", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "children": { + "defaultValue": { + "value": "null" + }, + "description": "子元素\n@en Children element", + "name": "children", + "tags": { + "en": "Children element", + "default": "null" + }, + "descWithTags": "子元素", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonProps" + }, + "required": false, + "type": { + "name": "ReactNode" + } + }, + "className": { + "defaultValue": { + "value": "\"\"" + }, + "description": "自定义类名\n@en Custom classname", + "name": "className", + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "style": { + "defaultValue": { + "value": "{}" + }, + "description": "自定义样式\n@en Custom stylesheet", + "name": "style", + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "CSSProperties" + } + }, + "ref": { + "defaultValue": null, + "description": "", + "name": "ref", + "tags": {}, + "descWithTags": "", + "parent": { + "fileName": "arcom-github/node_modules/@types/react/index.d.ts", + "name": "RefAttributes" + }, + "required": false, + "type": { + "name": "Ref" + } + } + }, + "deps": { + "SkeletonTitleProps": { + "width": { + "name": "width", + "required": false, + "description": "标题占位图宽度\n@en The width of title", + "defaultValue": { + "value": "\"40%\"" + }, + "type": { + "name": "ReactText" + }, + "tags": { + "en": "The width of title", + "default": "\"40%\"" + }, + "descWithTags": "标题占位图宽度" + }, + "className": { + "name": "className", + "required": false, + "description": "自定义类名\n@en Custom classname", + "defaultValue": { + "value": "\"\"" + }, + "type": { + "name": "string" + }, + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名" + }, + "style": { + "name": "style", + "required": false, + "description": "自定义样式\n@en Custom stylesheet", + "defaultValue": { + "value": "{}" + }, + "type": { + "name": "CSSProperties" + }, + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式" + } + }, + "SkeletonParagraphProps": { + "rows": { + "name": "rows", + "required": false, + "description": "段落占位图的行数\n@en Number of lines for paragraph", + "defaultValue": { + "value": "3" + }, + "type": { + "name": "number" + }, + "tags": { + "en": "Number of lines for paragraph", + "default": "3" + }, + "descWithTags": "段落占位图的行数" + }, + "width": { + "name": "width", + "required": false, + "description": "段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度\n@en The width of paragraph. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line", + "defaultValue": { + "value": "\"60%\"" + }, + "type": { + "name": "string | number | ReactText[]" + }, + "tags": { + "en": "The width of paragraph. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line", + "default": "\"60%\"" + }, + "descWithTags": "段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度" + }, + "className": { + "name": "className", + "required": false, + "description": "自定义类名\n@en Custom classname", + "defaultValue": { + "value": "\"\"" + }, + "type": { + "name": "string" + }, + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名" + }, + "style": { + "name": "style", + "required": false, + "description": "自定义样式\n@en Custom stylesheet", + "defaultValue": { + "value": "{}" + }, + "type": { + "name": "CSSProperties" + }, + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式" + } + }, + "SkeletonAvatarProps": { + "className": { + "name": "className", + "required": false, + "description": "自定义类名\n@en Custom classname", + "defaultValue": { + "value": "\"\"" + }, + "type": { + "name": "string" + }, + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名" + }, + "style": { + "name": "style", + "required": false, + "description": "自定义样式\n@en Custom stylesheet", + "defaultValue": { + "value": "{}" + }, + "type": { + "name": "CSSProperties" + }, + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式" + } + }, + "SkeletonGridProps": { + "columns": { + "name": "columns", + "required": false, + "description": "金刚位列数\n@en columns of grid", + "defaultValue": { + "value": "4" + }, + "type": { + "name": "number" + }, + "tags": { + "en": "columns of grid", + "default": "4" + }, + "descWithTags": "金刚位列数" + }, + "className": { + "name": "className", + "required": false, + "description": "自定义类名\n@en Custom classname", + "defaultValue": { + "value": "\"\"" + }, + "type": { + "name": "string" + }, + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名" + }, + "style": { + "name": "style", + "required": false, + "description": "自定义样式\n@en Custom stylesheet", + "defaultValue": { + "value": "{}" + }, + "type": { + "name": "CSSProperties" + }, + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式" + } + }, + "SkeletonRef": { + "dom": { + "name": "dom", + "required": true, + "description": "最外层 DOM 元素\n@en The outer DOM element of the component", + "defaultValue": null, + "type": { + "name": "HTMLDivElement" + }, + "tags": { + "en": "The outer DOM element of the component" + }, + "descWithTags": "最外层 DOM 元素" + } + } + }, + "depComps": { + "Node": { + "description": "", + "descriptionTags": {}, + "displayName": "SkeletonNode", + "methods": [], + "props": { + "children": { + "defaultValue": { + "value": "null" + }, + "description": "自定义组件内容\n@en Custom component content", + "name": "children", + "tags": { + "en": "Custom component content", + "default": "null" + }, + "descWithTags": "自定义组件内容", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "BaseProps" + }, + "required": false, + "type": { + "name": "ReactNode" + } + }, + "className": { + "defaultValue": { + "value": "\"\"" + }, + "description": "自定义类名\n@en Custom classname", + "name": "className", + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "style": { + "defaultValue": { + "value": "{}" + }, + "description": "自定义样式\n@en Custom stylesheet", + "name": "style", + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "CSSProperties" + } + }, + "ref": { + "defaultValue": null, + "description": "", + "name": "ref", + "tags": {}, + "descWithTags": "", + "parent": { + "fileName": "arcom-github/node_modules/@types/react/index.d.ts", + "name": "RefAttributes" + }, + "required": false, + "type": { + "name": "Ref" + } + } + }, + "deps": { + "SkeletonRef": { + "dom": { + "name": "dom", + "required": true, + "description": "最外层 DOM 元素\n@en The outer DOM element of the component", + "defaultValue": null, + "type": { + "name": "HTMLDivElement" + }, + "tags": { + "en": "The outer DOM element of the component" + }, + "descWithTags": "最外层 DOM 元素" + } + } + }, + "depComps": {}, + "typeNameInfo": { + "props": "BaseProps", + "ref": "SkeletonRef" + } + }, + "Title": { + "description": "", + "descriptionTags": {}, + "displayName": "SkeletonTitle", + "methods": [], + "props": { + "width": { + "defaultValue": { + "value": "\"40%\"" + }, + "description": "标题占位图宽度\n@en The width of title", + "name": "width", + "tags": { + "en": "The width of title", + "default": "\"40%\"" + }, + "descWithTags": "标题占位图宽度", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonTitleProps" + }, + "required": false, + "type": { + "name": "ReactText" + } + }, + "className": { + "defaultValue": { + "value": "\"\"" + }, + "description": "自定义类名\n@en Custom classname", + "name": "className", + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "style": { + "defaultValue": { + "value": "{}" + }, + "description": "自定义样式\n@en Custom stylesheet", + "name": "style", + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "CSSProperties" + } + }, + "ref": { + "defaultValue": null, + "description": "", + "name": "ref", + "tags": {}, + "descWithTags": "", + "parent": { + "fileName": "arcom-github/node_modules/@types/react/index.d.ts", + "name": "RefAttributes" + }, + "required": false, + "type": { + "name": "Ref" + } + } + }, + "deps": { + "SkeletonRef": { + "dom": { + "name": "dom", + "required": true, + "description": "最外层 DOM 元素\n@en The outer DOM element of the component", + "defaultValue": null, + "type": { + "name": "HTMLDivElement" + }, + "tags": { + "en": "The outer DOM element of the component" + }, + "descWithTags": "最外层 DOM 元素" + } + } + }, + "depComps": {}, + "typeNameInfo": { + "props": "SkeletonTitleProps", + "ref": "SkeletonRef" + } + }, + "Paragraph": { + "description": "", + "descriptionTags": {}, + "displayName": "SkeletonParagraph", + "methods": [], + "props": { + "rows": { + "defaultValue": { + "value": "3" + }, + "description": "段落占位图的行数\n@en Number of lines for paragraph", + "name": "rows", + "tags": { + "en": "Number of lines for paragraph", + "default": "3" + }, + "descWithTags": "段落占位图的行数", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonParagraphProps" + }, + "required": false, + "type": { + "name": "number" + } + }, + "width": { + "defaultValue": { + "value": "\"60%\"" + }, + "description": "段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度\n@en The width of paragraph. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line", + "name": "width", + "tags": { + "en": "The width of paragraph. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line", + "default": "\"60%\"" + }, + "descWithTags": "段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonParagraphProps" + }, + "required": false, + "type": { + "name": "string | number | ReactText[]" + } + }, + "className": { + "defaultValue": { + "value": "\"\"" + }, + "description": "自定义类名\n@en Custom classname", + "name": "className", + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "style": { + "defaultValue": { + "value": "{}" + }, + "description": "自定义样式\n@en Custom stylesheet", + "name": "style", + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "CSSProperties" + } + }, + "ref": { + "defaultValue": null, + "description": "", + "name": "ref", + "tags": {}, + "descWithTags": "", + "parent": { + "fileName": "arcom-github/node_modules/@types/react/index.d.ts", + "name": "RefAttributes" + }, + "required": false, + "type": { + "name": "Ref" + } + } + }, + "deps": { + "SkeletonRef": { + "dom": { + "name": "dom", + "required": true, + "description": "最外层 DOM 元素\n@en The outer DOM element of the component", + "defaultValue": null, + "type": { + "name": "HTMLDivElement" + }, + "tags": { + "en": "The outer DOM element of the component" + }, + "descWithTags": "最外层 DOM 元素" + } + } + }, + "depComps": {}, + "typeNameInfo": { + "props": "SkeletonParagraphProps", + "ref": "SkeletonRef" + } + }, + "Avatar": { + "description": "", + "descriptionTags": {}, + "displayName": "SkeletonAvatar", + "methods": [], + "props": { + "className": { + "defaultValue": { + "value": "\"\"" + }, + "description": "自定义类名\n@en Custom classname", + "name": "className", + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "style": { + "defaultValue": { + "value": "{}" + }, + "description": "自定义样式\n@en Custom stylesheet", + "name": "style", + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "CSSProperties" + } + }, + "ref": { + "defaultValue": null, + "description": "", + "name": "ref", + "tags": {}, + "descWithTags": "", + "parent": { + "fileName": "arcom-github/node_modules/@types/react/index.d.ts", + "name": "RefAttributes" + }, + "required": false, + "type": { + "name": "Ref" + } + } + }, + "deps": { + "SkeletonRef": { + "dom": { + "name": "dom", + "required": true, + "description": "最外层 DOM 元素\n@en The outer DOM element of the component", + "defaultValue": null, + "type": { + "name": "HTMLDivElement" + }, + "tags": { + "en": "The outer DOM element of the component" + }, + "descWithTags": "最外层 DOM 元素" + } + } + }, + "depComps": {}, + "typeNameInfo": { + "props": "SimpleBaseProps", + "ref": "SkeletonRef" + } + }, + "Grid": { + "description": "", + "descriptionTags": {}, + "displayName": "SkeletonGrid", + "methods": [], + "props": { + "columns": { + "defaultValue": { + "value": "4" + }, + "description": "金刚位列数\n@en columns of grid", + "name": "columns", + "tags": { + "en": "columns of grid", + "default": "4" + }, + "descWithTags": "金刚位列数", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/skeleton/type.ts", + "name": "SkeletonGridProps" + }, + "required": false, + "type": { + "name": "number" + } + }, + "className": { + "defaultValue": { + "value": "\"\"" + }, + "description": "自定义类名\n@en Custom classname", + "name": "className", + "tags": { + "en": "Custom classname", + "default": "\"\"" + }, + "descWithTags": "自定义类名", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "string" + } + }, + "style": { + "defaultValue": { + "value": "{}" + }, + "description": "自定义样式\n@en Custom stylesheet", + "name": "style", + "tags": { + "en": "Custom stylesheet", + "default": "{}" + }, + "descWithTags": "自定义样式", + "parent": { + "fileName": "arcom-github/packages/arcodesign/components/_helpers/type.ts", + "name": "SimpleBaseProps" + }, + "required": false, + "type": { + "name": "CSSProperties" + } + }, + "ref": { + "defaultValue": null, + "description": "", + "name": "ref", + "tags": {}, + "descWithTags": "", + "parent": { + "fileName": "arcom-github/node_modules/@types/react/index.d.ts", + "name": "RefAttributes" + }, + "required": false, + "type": { + "name": "Ref" + } + } + }, + "deps": { + "SkeletonRef": { + "dom": { + "name": "dom", + "required": true, + "description": "最外层 DOM 元素\n@en The outer DOM element of the component", + "defaultValue": null, + "type": { + "name": "HTMLDivElement" + }, + "tags": { + "en": "The outer DOM element of the component" + }, + "descWithTags": "最外层 DOM 元素" + } + } + }, + "depComps": {}, + "typeNameInfo": { + "props": "SkeletonGridProps", + "ref": "SkeletonRef" + } + } + }, + "typeNameInfo": { + "props": "SkeletonProps", + "ref": "SkeletonRef" + }, + "isDefaultExport": true +} \ No newline at end of file diff --git a/packages/arcodesign/components/skeleton/__test__/__snapshots__/index.spec.js.snap b/packages/arcodesign/components/skeleton/__test__/__snapshots__/index.spec.js.snap new file mode 100644 index 00000000..149464ba --- /dev/null +++ b/packages/arcodesign/components/skeleton/__test__/__snapshots__/index.spec.js.snap @@ -0,0 +1,574 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`skeleton demo test skeleton demo: animation.md renders correctly 1`] = ` +
+
+
+
+
+
+
+ show animation +
+
+
+
+
+
+
+
+
+
+
+
+
+ type +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`skeleton demo test skeleton demo: avatar.md renders correctly 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`skeleton demo test skeleton demo: custom.md renders correctly 1`] = ` +
+
+
+
+
+
+
+ loading +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`skeleton demo test skeleton demo: grid.md renders correctly 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`skeleton demo test skeleton demo: index.md renders correctly 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/packages/arcodesign/components/skeleton/__test__/index.spec.js b/packages/arcodesign/components/skeleton/__test__/index.spec.js new file mode 100644 index 00000000..c47d753d --- /dev/null +++ b/packages/arcodesign/components/skeleton/__test__/index.spec.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import demoTest from '../../../tests/demoTest'; +import mountTest from '../../../tests/mountTest'; +import { defaultContext } from '../../context-provider'; +import Skeleton from '..'; + +demoTest('skeleton'); + +mountTest(Skeleton, 'Skeleton'); + +const prefix = `${defaultContext.prefixCls}-skeleton`; + +describe('Skeleton', () => { + it('should render correctly when set ref', () => { + const ref = React.createRef(); + const titleRef = React.createRef(); + const paragraphRef = React.createRef(); + const avatarRef = React.createRef(); + const nodeRef = React.createRef(); + mount( + + + + + +
+ + , + ); + expect(ref.current.dom).not.toBeUndefined(); + expect(titleRef.current.dom).not.toBeUndefined(); + expect(paragraphRef.current.dom).not.toBeUndefined(); + expect(avatarRef.current.dom).not.toBeUndefined(); + expect(nodeRef.current.dom).not.toBeUndefined(); + }); + it('should render correctly when set avatar', () => { + const comp = mount(); + expect(comp.find(`.${prefix}-avatar`).length).toBe(1); + }); + it('should render correctly when set grid', () => { + const comp = mount(); + expect(comp.find(`.${prefix}-grid-item`).length).toBe(5); + }); + it('should render correctly when set children', () => { + const comp = mount( + + +
+ + , + ); + expect(comp.find(`.${prefix}-node`).length).toBe(1); + expect(comp.find(`.${prefix}-title`).length).toBe(1); + expect(comp.find(`.${prefix}-paragraph`).length).toBe(1); + }); + it('should render correctly when set animation', () => { + const comp = mount( + + +
+ + , + ); + expect(comp.find(`.${prefix}-animation-gradient`).length).toBe(5); + expect(comp.find(`.${prefix}-animation-breath`).length).toBe(0); + comp.setProps({ + animation: 'breath', + }); + expect(comp.find(`.${prefix}-animation-gradient`).length).toBe(0); + expect(comp.find(`.${prefix}-animation-breath`).length).toBe(5); + comp.setProps({ + showAnimation: false, + }); + expect(comp.find(`.${prefix}-animation-gradient`).length).toBe(0); + expect(comp.find(`.${prefix}-animation-breath`).length).toBe(0); + }); +}); diff --git a/packages/arcodesign/components/skeleton/demo/animation.md b/packages/arcodesign/components/skeleton/demo/animation.md new file mode 100644 index 00000000..12abd610 --- /dev/null +++ b/packages/arcodesign/components/skeleton/demo/animation.md @@ -0,0 +1,39 @@ +## 动效 @en{Animation} + +#### 5 + +```js +import { Cell, Checkbox, Radio, Skeleton, Switch } from '@arco-design/mobile-react'; + +const options = [ + { label: 'gradient', value: 'gradient' }, + { label: 'breath', value: 'breath' }, +]; + +const color = 'rgba(0, 0, 0, 4%)'; + +export default function SkeletonDemo() { + const [type, setType] = React.useState('gradient'); + const [checked, setChecked] = React.useState(true); + return ( +
+ + + + + {checked && ( + + + + )} + + +
+ ); +} +``` diff --git a/packages/arcodesign/components/skeleton/demo/avatar.md b/packages/arcodesign/components/skeleton/demo/avatar.md new file mode 100644 index 00000000..05165490 --- /dev/null +++ b/packages/arcodesign/components/skeleton/demo/avatar.md @@ -0,0 +1,11 @@ +## 含头像 @en{With avatar} + +#### 2 + +```js +import { Skeleton } from '@arco-design/mobile-react'; + +export default function SkeletonDemo() { + return ; +} +``` diff --git a/packages/arcodesign/components/skeleton/demo/custom.md b/packages/arcodesign/components/skeleton/demo/custom.md new file mode 100644 index 00000000..a79d378f --- /dev/null +++ b/packages/arcodesign/components/skeleton/demo/custom.md @@ -0,0 +1,46 @@ +## 独立节点自由组合 @en{Custom} + +#### 5 + +```js +import { Cell, Skeleton, Switch } from '@arco-design/mobile-react'; + +export default function SkeletonDemo() { + const [loading, setLoading] = React.useState(true); + return ( +
+ + + + + + {loading ? ( + + +
+ + +
+ +
+ + + + ) : ( +
actual content
+ )} +
+ ); +} +``` diff --git a/packages/arcodesign/components/skeleton/demo/grid.md b/packages/arcodesign/components/skeleton/demo/grid.md new file mode 100644 index 00000000..7bbb9fe8 --- /dev/null +++ b/packages/arcodesign/components/skeleton/demo/grid.md @@ -0,0 +1,11 @@ +## 金刚区 @en{Grid} + +#### 3 + +```js +import { Skeleton } from '@arco-design/mobile-react'; + +export default function SkeletonDemo() { + return ; +} +``` diff --git a/packages/arcodesign/components/skeleton/demo/index.md b/packages/arcodesign/components/skeleton/demo/index.md new file mode 100644 index 00000000..3d033861 --- /dev/null +++ b/packages/arcodesign/components/skeleton/demo/index.md @@ -0,0 +1,11 @@ +## 基础用法 @en{Basic} + +#### 1 + +```js +import { Skeleton } from '@arco-design/mobile-react'; + +export default function SkeletonDemo() { + return ; +} +``` diff --git a/packages/arcodesign/components/skeleton/demo/style/mobile.less b/packages/arcodesign/components/skeleton/demo/style/mobile.less new file mode 100644 index 00000000..366ec254 --- /dev/null +++ b/packages/arcodesign/components/skeleton/demo/style/mobile.less @@ -0,0 +1,13 @@ +@import '../../../../style/mixin.less'; + +#demo-skeleton { + .arcodesign-mobile-demo-content { + .arco-cell-group { + margin-bottom: 0.32rem; + } + .arco-cell { + margin-left: 0; + padding-right: 0; + } + } +} diff --git a/packages/arcodesign/components/skeleton/elements.tsx b/packages/arcodesign/components/skeleton/elements.tsx new file mode 100644 index 00000000..48dfa65d --- /dev/null +++ b/packages/arcodesign/components/skeleton/elements.tsx @@ -0,0 +1,278 @@ +import React, { + Ref, + forwardRef, + useContext, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react'; +import { cls, isArray, nextTick } from '@arco-design/mobile-utils'; +import { useListenResize } from '../_helpers'; +import { GlobalContext } from '../context-provider'; +import { + SkeletonAvatarProps, + SkeletonGridProps, + SkeletonNodeProps, + SkeletonParagraphProps, + SkeletonRef, + SkeletonTitleProps, +} from './type'; +import { SkeletonContext } from './skeleton-context'; + +const calcOffset = (dom?: HTMLElement | null, useRtl?: boolean) => { + if (!dom) { + return 0; + } + if (useRtl) { + return dom.offsetLeft - dom.offsetTop; + } + return dom.offsetLeft + dom.offsetTop; +}; + +function useOffset | T[]>( + dom?: K, + useRtl?: boolean, +) { + const [offset, setOffset] = useState(); + const calcLayout = () => { + const isList = Array.isArray(dom); + if ( + !(isList ? (dom as T[]).length > 0 : (dom as React.MutableRefObject)?.current) + ) { + return; + } + setOffset( + isList + ? (dom as T[]).map(item => calcOffset(item, useRtl)) + : calcOffset((dom as React.MutableRefObject).current, useRtl), + ); + }; + + useEffect(() => { + nextTick(() => { + calcLayout(); + }); + }, [dom, useRtl]); + + useListenResize(calcLayout); + return offset as K extends T[] ? number[] : number; +} + +export const SkeletonNode = forwardRef((props: SkeletonNodeProps, ref: Ref) => { + const { className = '', style, children } = props; + const { useRtl, prefixCls } = useContext(GlobalContext); + const { backgroundColor, showAnimation, animation } = useContext(SkeletonContext); + const domRef = useRef(null); + const isGradientAnimation = showAnimation && animation === 'gradient'; + const offset = useOffset(isGradientAnimation ? domRef : undefined, useRtl); + + useImperativeHandle(ref, () => ({ + dom: domRef.current, + })); + + return ( +
+ {children} + {isGradientAnimation && offset !== undefined && ( +
+ )} +
+ ); +}); + +export const SkeletonTitle = forwardRef((props: SkeletonTitleProps, ref: Ref) => { + const { className = '', style, width = '40%' } = props; + const { useRtl, prefixCls } = useContext(GlobalContext); + const { backgroundColor, showAnimation, animation } = useContext(SkeletonContext); + const domRef = useRef(null); + const isGradientAnimation = showAnimation && animation === 'gradient'; + const offset = useOffset(isGradientAnimation ? domRef : undefined, useRtl); + + useImperativeHandle(ref, () => ({ + dom: domRef.current, + })); + + return ( +
+ {isGradientAnimation && offset !== undefined && ( +
+ )} +
+ ); +}); + +export const SkeletonParagraph = forwardRef( + (props: SkeletonParagraphProps, ref: Ref) => { + const { className = '', style, rows = 3, width = '60%' } = props; + const { useRtl, prefixCls } = useContext(GlobalContext); + const { backgroundColor, showAnimation, animation } = useContext(SkeletonContext); + const domRef = useRef(null); + const lineDomRefs = useRef([]); + const isGradientAnimation = showAnimation && animation === 'gradient'; + const offsets = useOffset(isGradientAnimation ? lineDomRefs.current : undefined, useRtl); + + useImperativeHandle(ref, () => ({ + dom: domRef.current, + })); + + const getWidth = (idx: number) => { + if (isArray(width)) { + return width[idx]; + } + if (rows - 1 === idx) { + return width; + } + return undefined; + }; + + return ( +
+ {Array.from(new Array(rows)).map((_, idx) => ( +
el && (lineDomRefs.current[idx] = el)} + > + {isGradientAnimation && offsets !== undefined && ( +
+ )} +
+ ))} +
+ ); + }, +); + +export const SkeletonAvatar = forwardRef((props: SkeletonAvatarProps, ref: Ref) => { + const { className = '', style } = props; + const { useRtl, prefixCls } = useContext(GlobalContext); + const { backgroundColor, showAnimation, animation } = useContext(SkeletonContext); + const domRef = useRef(null); + const isGradientAnimation = showAnimation && animation === 'gradient'; + const offset = useOffset(isGradientAnimation ? domRef : undefined, useRtl); + + useImperativeHandle(ref, () => ({ + dom: domRef.current, + })); + + return ( +
+ {isGradientAnimation && offset !== undefined && ( +
+ )} +
+ ); +}); + +export const SkeletonGrid = forwardRef((props: SkeletonGridProps, ref: Ref) => { + const { className = '', style, columns = 4 } = props; + const { useRtl, prefixCls } = useContext(GlobalContext); + const { backgroundColor, showAnimation, animation } = useContext(SkeletonContext); + const domRef = useRef(null); + const iconDomRefs = useRef([]); + const textDomRefs = useRef([]); + const isGradientAnimation = showAnimation && animation === 'gradient'; + const iconOffsets = useOffset(isGradientAnimation ? iconDomRefs.current : undefined, useRtl); + const textOffsets = useOffset(isGradientAnimation ? textDomRefs.current : undefined, useRtl); + + useImperativeHandle(ref, () => ({ + dom: domRef.current, + })); + + return ( +
+ {Array.from(new Array(columns)).map((_, idx) => ( +
+
el && (iconDomRefs.current[idx] = el)} + > + {isGradientAnimation && iconOffsets !== undefined && ( +
+ )} +
+
el && (textDomRefs.current[idx] = el)} + > + {isGradientAnimation && textOffsets !== undefined && ( +
+ )} +
+
+ ))} +
+ ); +}); diff --git a/packages/arcodesign/components/skeleton/index.tsx b/packages/arcodesign/components/skeleton/index.tsx new file mode 100644 index 00000000..a1f3e683 --- /dev/null +++ b/packages/arcodesign/components/skeleton/index.tsx @@ -0,0 +1,94 @@ +import React, { useRef, forwardRef, Ref, useImperativeHandle, useContext } from 'react'; +import { cls, componentWrapper } from '@arco-design/mobile-utils'; +import { GlobalContext } from '../context-provider'; +import { SkeletonProps, SkeletonRef } from './type'; +import { + SkeletonAvatar as Avatar, + SkeletonGrid as Grid, + SkeletonNode as Node, + SkeletonParagraph as Paragraph, + SkeletonTitle as Title, +} from './elements'; +import { SkeletonContext } from './skeleton-context'; + +function getComponentProps(prop?: T | boolean): T | {} { + if (prop && typeof prop === 'object') { + return prop; + } + return {}; +} + +const Skeleton = forwardRef((props: SkeletonProps, ref: Ref) => { + const { + className = '', + style, + children, + title = true, + paragraph = true, + avatar = false, + grid, + showAnimation = true, + animation = 'gradient', + animationGradientColor, + backgroundColor, + } = props; + const domRef = useRef(null); + const { prefixCls } = useContext(GlobalContext); + + useImperativeHandle(ref, () => ({ + dom: domRef.current, + })); + + const isGrid = !!grid; + const hasTitle = !!title; + const hasParagraph = !!paragraph; + const hasAvatar = !!avatar; + const content = isGrid ? ( + + ) : ( + <> + {hasAvatar && } + {(hasTitle || hasParagraph) && ( +
+ {hasTitle && } + {hasParagraph && <Paragraph {...getComponentProps(paragraph)} />} + </div> + )} + </> + ); + + return ( + <div + className={cls( + `${prefixCls}-skeleton`, + { + [`${prefixCls}-skeleton-with-avatar`]: hasAvatar, + }, + className, + )} + style={{ color: animationGradientColor, ...style }} + ref={domRef} + > + <SkeletonContext.Provider value={{ showAnimation, animation, backgroundColor }}> + {content} + {children} + </SkeletonContext.Provider> + </div> + ); +}); + +/** + * 在内容加载过程中展示一组占位图形。 + * @en Display a set of placeholder graphics during content loading + * @type 信息展示 + * @type_en Data Display + * @name 骨架屏 + * @name_en Skeleton + */ +export default componentWrapper(Skeleton, { + Node, + Title, + Paragraph, + Avatar, + Grid, +}); diff --git a/packages/arcodesign/components/skeleton/skeleton-context.tsx b/packages/arcodesign/components/skeleton/skeleton-context.tsx new file mode 100644 index 00000000..81cb0107 --- /dev/null +++ b/packages/arcodesign/components/skeleton/skeleton-context.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import { SkeletonContextParams, SkeletonProps } from './type'; + +export const SkeletonContext = React.createContext<SkeletonContextParams>({ + showAnimation: true, + animation: 'gradient' as SkeletonProps['animation'], +}); diff --git a/packages/arcodesign/components/skeleton/style/index.less b/packages/arcodesign/components/skeleton/style/index.less new file mode 100644 index 00000000..1a6788fc --- /dev/null +++ b/packages/arcodesign/components/skeleton/style/index.less @@ -0,0 +1,147 @@ +@import '../../../style/mixin.less'; + +.@{prefix}-skeleton { + position: relative; + .use-var(color, skeleton-gradient-animation-color); +} + +.@{prefix}-skeleton-title { + .use-var(height, skeleton-title-height); +} + +.@{prefix}-skeleton-paragraph { + &-line { + width: 100%; + .use-var(height, skeleton-paragraph-height); + } + + &-line + &-line { + .use-var(margin-top, skeleton-paragraph-margin-top); + } +} + +.@{prefix}-skeleton-avatar.@{prefix}-skeleton-item { + .use-var(width, skeleton-avatar-size); + .use-var(height, skeleton-avatar-size); + border-radius: 100%; + transform: translateZ(0); +} + +.@{prefix}-skeleton-content { + width: 100%; + + .@{prefix}-skeleton-title + .@{prefix}-skeleton-paragraph { + .use-var(margin-top, skeleton-large-gutter); + } +} + +.@{prefix}-skeleton-with-avatar { + display: flex; + align-items: flex-start; + + .@{prefix}-skeleton-avatar { + flex: none; + } + + .@{prefix}-skeleton-content { + .@{prefix}-skeleton-title { + .use-var(margin-top, skeleton-medium-gutter); + } + } + + .@{prefix}-skeleton-avatar + .@{prefix}-skeleton-content { + .use-var-with-rtl(margin-left, skeleton-medium-gutter); + } +} + +.@{prefix}-skeleton-grid { + display: flex; + justify-content: space-between; + width: 100%; + + &-item { + display: flex; + flex-direction: column; + align-items: center; + } + &-icon { + .use-var(width, skeleton-grid-icon-size); + .use-var(height, skeleton-grid-icon-size); + } + &-text { + .use-var(margin-top, skeleton-medium-gutter); + .use-var(width, skeleton-grid-text-width); + .use-var(height, skeleton-grid-text-height); + } +} + +.@{prefix}-skeleton-node { + display: inline-block; +} + +.@{prefix}-skeleton-item { + position: relative; + overflow: hidden; + .use-var(border-radius, skeleton-border-radius); + .use-var(background-color, skeleton-background-color); +} + +.@{prefix}-skeleton-animation-item { + position: absolute; + width: 100vw; + height: 100%; + top: 0; + left: 0; + background-image: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 35%, + currentColor 50%, + rgba(255, 255, 255, 0) 65% + ); + transform-origin: top left; + animation-name: skeleton-gradient; + animation-iteration-count: infinite; + .use-var(animation-timing-function, skeleton-gradient-animation-timing-function); + .use-var(animation-duration, skeleton-gradient-animation-duration); + [dir="rtl"] & { + animation-name: skeleton-gradient-reverse; + } +} +.@{prefix}-skeleton-animation-breath { + animation: skeleton-breath linear infinite; + .use-var(animation-duration, skeleton-breath-animation-duration); +} + +@keyframes skeleton-gradient { + 0% { + transform: translateX(-65%) skewX(-45deg); + } + + 100% { + transform: translateX(135%) skewX(-45deg); + } +} + +@keyframes skeleton-gradient-reverse { + 0% { + transform: translateX(65%) skewX(45deg); + } + + 100% { + transform: translateX(-135%) skewX(45deg); + } +} + +@keyframes skeleton-breath { + 0% { + opacity: 1; + } + + 50% { + .use-var(opacity, skeleton-breath-opacity); + } + + 100% { + opacity: 1; + } +} diff --git a/packages/arcodesign/components/skeleton/style/index.ts b/packages/arcodesign/components/skeleton/style/index.ts new file mode 100644 index 00000000..d33b50ec --- /dev/null +++ b/packages/arcodesign/components/skeleton/style/index.ts @@ -0,0 +1,3 @@ +import '../../../style/public.less'; +import '../../avatar/style'; +import './index.less'; diff --git a/packages/arcodesign/components/skeleton/type.ts b/packages/arcodesign/components/skeleton/type.ts new file mode 100644 index 00000000..ad6bad5b --- /dev/null +++ b/packages/arcodesign/components/skeleton/type.ts @@ -0,0 +1,109 @@ +import { BaseProps, SimpleBaseProps } from '../_helpers'; + +export interface SkeletonProps extends SimpleBaseProps { + /** + * 是否显示标题占位图 + * @en Show title placeholder + * @default true + */ + title?: boolean | SkeletonTitleProps; + /** + * 是否显示段落占位图 + * @en Show paragraph placeholder + * @default true + */ + paragraph?: boolean | SkeletonParagraphProps; + /** + * 是否显示头像占位图 + * @en Show Avatar placeholder + * @default false + */ + avatar?: boolean | SkeletonAvatarProps; + /** + * 是否显示金刚位占位图(如该参数非空时,默认展示四列金刚位,且不展示标题/段落/头像占位符) + * @en Show Grid placeholder. When it's value is present, the paragraph, avatar or title placeholder will not be displayed, and four columns will be displayed by default + * @default false + */ + grid?: boolean | SkeletonGridProps; + /** + * 是否展示动画效果 + * @en Show loading effect + * @default true + */ + showAnimation?: boolean; + /** + * 加载动画效果,可选“扫光”、“呼吸”两种效果 + * @en Animation of loading effect, 'gradient' and 'breath' effects are optional + * @default "gradient" + */ + animation?: 'gradient' | 'breath'; + /** + * 扫光动效高光颜色 + * @en Highlight color of gradient animation + * @default "rgba(0, 0, 0, 0.04)" + */ + animationGradientColor?: string; + /** + * 占位块背景色 + * @en Background color of skeleton item + * @default "#F7F8FA" + */ + backgroundColor?: string; + /** + * 子元素 + * @en Children element + * @default null + */ + children?: React.ReactNode; +} + +export interface SkeletonTitleProps extends SimpleBaseProps { + /** + * 标题占位图宽度 + * @en The width of title + * @default "40%" + */ + width?: number | string; +} + +export interface SkeletonParagraphProps extends SimpleBaseProps { + /** + * 段落占位图的行数 + * @en Number of lines for paragraph + * @default 3 + */ + rows?: number; + /** + * 段落占位图宽度,若为数组格式对应每行宽度,否则表示最后一行的宽度 + * @en The width of paragraph. If width is an Array, it corresponds to the width of each line, otherwise it indicates the width of the last line + * @default "60%" + */ + width?: number | string | Array<number | string>; +} + +export interface SkeletonAvatarProps extends SimpleBaseProps {} + +export interface SkeletonGridProps extends SimpleBaseProps { + /** + * 金刚位列数 + * @en columns of grid + * @default 4 + */ + columns?: number; +} + +export interface SkeletonNodeProps extends BaseProps {} + +export interface SkeletonRef { + /** + * 最外层 DOM 元素 + * @en The outer DOM element of the component + */ + dom: HTMLDivElement | null; +} + +export interface SkeletonContextParams { + backgroundColor?: string; + showAnimation: boolean; + animation: SkeletonProps['animation']; +} diff --git a/packages/arcodesign/components/style.ts b/packages/arcodesign/components/style.ts index 40740798..bdb044de 100644 --- a/packages/arcodesign/components/style.ts +++ b/packages/arcodesign/components/style.ts @@ -44,6 +44,7 @@ import './pull-refresh/style'; import './radio/style'; import './rate/style'; import './search-bar/style'; +import './skeleton/style'; import './show-monitor/style'; import './slider/style'; import './stepper/style'; diff --git a/packages/arcodesign/tokens/app/arcodesign/default/css-variables.less b/packages/arcodesign/tokens/app/arcodesign/default/css-variables.less index 9f16beae..4b1ea3e3 100644 --- a/packages/arcodesign/tokens/app/arcodesign/default/css-variables.less +++ b/packages/arcodesign/tokens/app/arcodesign/default/css-variables.less @@ -793,4 +793,20 @@ --divider-right-width: ~`pxtorem(28)`; --divider-content-padding: ~`pxtorem(12)`; --divider-padding: ~`pxtorem(16)`; + --skeleton-border-radius: ~`pxtorem(0)`; + --skeleton-background-color: #F2F3F5; + --skeleton-gradient-animation-color: rgba(255, 255, 255, 0.6); + --skeleton-breath-opacity: 0.4; + --skeleton-gradient-animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); + --skeleton-gradient-animation-duration: 1.5s; + --skeleton-breath-animation-duration: 1.5s; + --skeleton-title-height: ~`pxtorem(16)`; + --skeleton-paragraph-height: ~`pxtorem(16)`; + --skeleton-paragraph-margin-top: ~`pxtorem(12)`; + --skeleton-avatar-size: ~`pxtorem(32)`; + --skeleton-grid-icon-size: ~`pxtorem(32)`; + --skeleton-grid-text-width: ~`pxtorem(64)`; + --skeleton-grid-text-height: ~`pxtorem(16)`; + --skeleton-medium-gutter: ~`pxtorem(8)`; + --skeleton-large-gutter: ~`pxtorem(20)`; } diff --git a/packages/arcodesign/tokens/app/arcodesign/default/index.d.ts b/packages/arcodesign/tokens/app/arcodesign/default/index.d.ts index 69b0c649..5f111f56 100644 --- a/packages/arcodesign/tokens/app/arcodesign/default/index.d.ts +++ b/packages/arcodesign/tokens/app/arcodesign/default/index.d.ts @@ -792,6 +792,22 @@ export interface ArcodesignToken extends Record<string, string> { 'divider-right-width': string; 'divider-content-padding': string; 'divider-padding': string; + 'skeleton-border-radius': string; + 'skeleton-background-color': string; + 'skeleton-gradient-animation-color': string; + 'skeleton-breath-opacity': string; + 'skeleton-gradient-animation-timing-function': string; + 'skeleton-gradient-animation-duration': string; + 'skeleton-breath-animation-duration': string; + 'skeleton-title-height': string; + 'skeleton-paragraph-height': string; + 'skeleton-paragraph-margin-top': string; + 'skeleton-avatar-size': string; + 'skeleton-grid-icon-size': string; + 'skeleton-grid-text-width': string; + 'skeleton-grid-text-height': string; + 'skeleton-medium-gutter': string; + 'skeleton-large-gutter': string; } declare const tokens: ArcodesignToken; export default tokens; \ No newline at end of file diff --git a/packages/arcodesign/tokens/app/arcodesign/default/index.js b/packages/arcodesign/tokens/app/arcodesign/default/index.js index f48a7f69..bdbb81a6 100644 --- a/packages/arcodesign/tokens/app/arcodesign/default/index.js +++ b/packages/arcodesign/tokens/app/arcodesign/default/index.js @@ -803,7 +803,23 @@ var tokens = { "divider-left-width": "0.56rem", "divider-right-width": "0.56rem", "divider-content-padding": "0.24rem", - "divider-padding": "0.32rem" + "divider-padding": "0.32rem", + "skeleton-border-radius": "0", + "skeleton-background-color": "#F2F3F5", + "skeleton-gradient-animation-color": "rgba(255, 255, 255, 0.6)", + "skeleton-breath-opacity": "0.4", + "skeleton-gradient-animation-timing-function": "cubic-bezier(0.42, 0, 0.58, 1)", + "skeleton-gradient-animation-duration": "1.5s", + "skeleton-breath-animation-duration": "1.5s", + "skeleton-title-height": "0.32rem", + "skeleton-paragraph-height": "0.32rem", + "skeleton-paragraph-margin-top": "0.24rem", + "skeleton-avatar-size": "0.64rem", + "skeleton-grid-icon-size": "0.64rem", + "skeleton-grid-text-width": "1.28rem", + "skeleton-grid-text-height": "0.32rem", + "skeleton-medium-gutter": "0.16rem", + "skeleton-large-gutter": "0.4rem" }; var _default = tokens; exports["default"] = _default; \ No newline at end of file diff --git a/packages/arcodesign/tokens/app/arcodesign/default/index.json b/packages/arcodesign/tokens/app/arcodesign/default/index.json index 43a012d4..a1f18333 100644 --- a/packages/arcodesign/tokens/app/arcodesign/default/index.json +++ b/packages/arcodesign/tokens/app/arcodesign/default/index.json @@ -6629,6 +6629,196 @@ "en": "Size of the rounded corners of the square SearchBar" } }, + "skeletonAvatarSize": { + "cssKey": "skeleton-avatar-size", + "desc": "骨架屏头像大小", + "override": "", + "value": "~`pxtorem(32)`", + "jsValue": "@getRem@32", + "staticValue": "0.64rem", + "localeDesc": { + "ch": "骨架屏头像大小", + "en": "Skeleton avatar size" + } + }, + "skeletonBackgroundColor": { + "cssKey": "skeleton-background-color", + "desc": "骨架屏元素背景色", + "override": "", + "value": "#F2F3F5", + "jsValue": "#F2F3F5", + "staticValue": "#F2F3F5", + "localeDesc": { + "ch": "骨架屏元素背景色", + "en": "Skeleton element background color" + } + }, + "skeletonBorderRadius": { + "cssKey": "skeleton-border-radius", + "desc": "骨架屏元素圆角", + "override": "", + "value": "~`pxtorem(0)`", + "jsValue": "@getRem@0", + "staticValue": "0", + "localeDesc": { + "ch": "骨架屏元素圆角", + "en": "Skeleton element border radius" + } + }, + "skeletonBreathAnimationDuration": { + "cssKey": "skeleton-breath-animation-duration", + "desc": "骨架屏呼吸动效时间", + "override": "", + "value": "1.5s", + "jsValue": "1.5s", + "staticValue": "1.5s", + "localeDesc": { + "ch": "骨架屏呼吸动效时间", + "en": "Skeleton element breath animation duration" + } + }, + "skeletonBreathOpacity": { + "cssKey": "skeleton-breath-opacity", + "desc": "骨架屏呼吸动效透明度", + "override": "", + "value": "0.4", + "jsValue": "0.4", + "staticValue": "0.4", + "localeDesc": { + "ch": "骨架屏呼吸动效透明度", + "en": "Skeleton element breath animation opacity" + } + }, + "skeletonGradientAnimationColor": { + "cssKey": "skeleton-gradient-animation-color", + "desc": "骨架屏扫光动效高亮色", + "override": "", + "value": "rgba(255, 255, 255, 0.6)", + "jsValue": "rgba(255, 255, 255, 0.6)", + "staticValue": "rgba(255, 255, 255, 0.6)", + "localeDesc": { + "ch": "骨架屏扫光动效高亮色", + "en": "Skeleton element gradient animation highlight color" + } + }, + "skeletonGradientAnimationDuration": { + "cssKey": "skeleton-gradient-animation-duration", + "desc": "骨架屏扫光动效时间", + "override": "", + "value": "1.5s", + "jsValue": "1.5s", + "staticValue": "1.5s", + "localeDesc": { + "ch": "骨架屏扫光动效时间", + "en": "Skeleton element gradient animation duration" + } + }, + "skeletonGradientAnimationTimingFunction": { + "cssKey": "skeleton-gradient-animation-timing-function", + "desc": "骨架屏扫光动效曲线", + "override": "", + "value": "cubic-bezier(0.42, 0, 0.58, 1)", + "jsValue": "cubic-bezier(0.42, 0, 0.58, 1)", + "staticValue": "cubic-bezier(0.42, 0, 0.58, 1)", + "localeDesc": { + "ch": "骨架屏扫光动效曲线", + "en": "Skeleton element gradient animation timing function" + } + }, + "skeletonGridIconSize": { + "cssKey": "skeleton-grid-icon-size", + "desc": "骨架屏金刚位图标区宽度", + "override": "", + "value": "~`pxtorem(32)`", + "jsValue": "@getRem@32", + "staticValue": "0.64rem", + "localeDesc": { + "ch": "骨架屏金刚位图标区宽度", + "en": "Skeleton grid item icon width" + } + }, + "skeletonGridTextHeight": { + "cssKey": "skeleton-grid-text-height", + "desc": "骨架屏金刚位文字区高度", + "override": "", + "value": "~`pxtorem(16)`", + "jsValue": "@getRem@16", + "staticValue": "0.32rem", + "localeDesc": { + "ch": "骨架屏金刚位文字区高度", + "en": "Skeleton grid item text height" + } + }, + "skeletonGridTextWidth": { + "cssKey": "skeleton-grid-text-width", + "desc": "骨架屏金刚位文字区宽度", + "override": "", + "value": "~`pxtorem(64)`", + "jsValue": "@getRem@64", + "staticValue": "1.28rem", + "localeDesc": { + "ch": "骨架屏金刚位文字区宽度", + "en": "Skeleton grid item text width" + } + }, + "skeletonLargeGutter": { + "cssKey": "skeleton-large-gutter", + "desc": "骨架屏元素外边距,大尺寸", + "override": "", + "value": "~`pxtorem(20)`", + "jsValue": "@getRem@20", + "staticValue": "0.4rem", + "localeDesc": { + "ch": "骨架屏元素外边距,大尺寸" + } + }, + "skeletonMediumGutter": { + "cssKey": "skeleton-medium-gutter", + "desc": "骨架屏元素外边距,中尺寸", + "override": "", + "value": "~`pxtorem(8)`", + "jsValue": "@getRem@8", + "staticValue": "0.16rem", + "localeDesc": { + "ch": "骨架屏元素外边距,中尺寸" + } + }, + "skeletonParagraphHeight": { + "cssKey": "skeleton-paragraph-height", + "desc": "骨架屏段落行高度", + "override": "", + "value": "~`pxtorem(16)`", + "jsValue": "@getRem@16", + "staticValue": "0.32rem", + "localeDesc": { + "ch": "骨架屏段落行高度", + "en": "Skeleton paragraph line height" + } + }, + "skeletonParagraphMarginTop": { + "cssKey": "skeleton-paragraph-margin-top", + "desc": "骨架屏各段落行间距", + "override": "", + "value": "~`pxtorem(12)`", + "jsValue": "@getRem@12", + "staticValue": "0.24rem", + "localeDesc": { + "ch": "骨架屏各段落行间距", + "en": "Margin top between skeleton paragraph lines" + } + }, + "skeletonTitleHeight": { + "cssKey": "skeleton-title-height", + "desc": "骨架屏标题高度", + "override": "", + "value": "~`pxtorem(16)`", + "jsValue": "@getRem@16", + "staticValue": "0.32rem", + "localeDesc": { + "ch": "骨架屏标题高度", + "en": "Skeleton title height" + } + }, "sliderHasMarkPaddingBottom": { "cssKey": "slider-has-mark-padding-bottom", "desc": "slider 有标记时的底部内边距", diff --git a/packages/arcodesign/tokens/app/arcodesign/default/index.less b/packages/arcodesign/tokens/app/arcodesign/default/index.less index 9c3860b9..dc2ab27c 100644 --- a/packages/arcodesign/tokens/app/arcodesign/default/index.less +++ b/packages/arcodesign/tokens/app/arcodesign/default/index.less @@ -792,4 +792,20 @@ @divider-right-width: ~`pxtorem(28)`; @divider-content-padding: ~`pxtorem(12)`; @divider-padding: ~`pxtorem(16)`; +@skeleton-border-radius: ~`pxtorem(0)`; +@skeleton-background-color: #F2F3F5; +@skeleton-gradient-animation-color: rgba(255, 255, 255, 0.6); +@skeleton-breath-opacity: 0.4; +@skeleton-gradient-animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); +@skeleton-gradient-animation-duration: 1.5s; +@skeleton-breath-animation-duration: 1.5s; +@skeleton-title-height: ~`pxtorem(16)`; +@skeleton-paragraph-height: ~`pxtorem(16)`; +@skeleton-paragraph-margin-top: ~`pxtorem(12)`; +@skeleton-avatar-size: ~`pxtorem(32)`; +@skeleton-grid-icon-size: ~`pxtorem(32)`; +@skeleton-grid-text-width: ~`pxtorem(64)`; +@skeleton-grid-text-height: ~`pxtorem(16)`; +@skeleton-medium-gutter: ~`pxtorem(8)`; +@skeleton-large-gutter: ~`pxtorem(20)`; diff --git a/packages/arcodesign/tokens/src/arcodesign/default/index.js b/packages/arcodesign/tokens/src/arcodesign/default/index.js index 1fcd953f..1a4d7d2d 100644 --- a/packages/arcodesign/tokens/src/arcodesign/default/index.js +++ b/packages/arcodesign/tokens/src/arcodesign/default/index.js @@ -4018,6 +4018,84 @@ function getCompTokens() { * @en Top and Bottom padding of divider */ dividerPadding: getRem(16), + /** + * 骨架屏元素圆角 + * @en Skeleton element border radius + */ + skeletonBorderRadius: getRem(0), + /** + * 骨架屏元素背景色 + * @en Skeleton element background color + */ + skeletonBackgroundColor: '#F2F3F5', + /** + * 骨架屏扫光动效高亮色 + * @en Skeleton element gradient animation highlight color + */ + skeletonGradientAnimationColor: 'rgba(255, 255, 255, 0.6)', + /** + * 骨架屏呼吸动效透明度 + * @en Skeleton element breath animation opacity + */ + skeletonBreathOpacity: '0.4', + /** + * 骨架屏扫光动效曲线 + * @en Skeleton element gradient animation timing function + */ + skeletonGradientAnimationTimingFunction: 'cubic-bezier(0.42, 0, 0.58, 1)', + /** + * 骨架屏扫光动效时间 + * @en Skeleton element gradient animation duration + */ + skeletonGradientAnimationDuration: '1.5s', + /** + * 骨架屏呼吸动效时间 + * @en Skeleton element breath animation duration + */ + skeletonBreathAnimationDuration: '1.5s', + /** + * 骨架屏标题高度 + * @en Skeleton title height + */ + skeletonTitleHeight: getRem(16), + /** + * 骨架屏段落行高度 + * @en Skeleton paragraph line height + */ + skeletonParagraphHeight: getRem(16), + /** + * 骨架屏各段落行间距 + * @en Margin top between skeleton paragraph lines + */ + skeletonParagraphMarginTop: getRem(12), + /** + * 骨架屏头像大小 + * @en Skeleton avatar size + */ + skeletonAvatarSize: getRem(32), + /** + * 骨架屏金刚位图标区宽度 + * @en Skeleton grid item icon width + */ + skeletonGridIconSize: getRem(32), + /** + * 骨架屏金刚位文字区宽度 + * @en Skeleton grid item text width + */ + skeletonGridTextWidth: getRem(64), + /** + * 骨架屏金刚位文字区高度 + * @en Skeleton grid item text height + */ + skeletonGridTextHeight: getRem(16), + /** + * 骨架屏元素外边距,中尺寸 + */ + skeletonMediumGutter: getRem(8), + /** + * 骨架屏元素外边距,大尺寸 + */ + skeletonLargeGutter: getRem(20), }; }