Merge branch 'master' of https://github.com/baidu/amis
This commit is contained in:
commit
39cfe0a1af
|
@ -68,6 +68,7 @@ amis 页面是通过 JSON 配置出来的,是由一个一个渲染模型组成
|
||||||
- [Service](./renderers/Service.md): 功能型容器,自身不负责展示内容,主要职责在于通过配置的 api 拉取数据
|
- [Service](./renderers/Service.md): 功能型容器,自身不负责展示内容,主要职责在于通过配置的 api 拉取数据
|
||||||
- [Chart](./renderers/Chart.md): 图表渲染器
|
- [Chart](./renderers/Chart.md): 图表渲染器
|
||||||
- [Collapse](./renderers/Collapse.md): 折叠器
|
- [Collapse](./renderers/Collapse.md): 折叠器
|
||||||
|
- [Carousel](./renderers/Carousel.md): 轮播图
|
||||||
- [Audio](./renderers/Audio.md): 音频播放器
|
- [Audio](./renderers/Audio.md): 音频播放器
|
||||||
- [Video](./renderers/Video.md): 视频播放器
|
- [Video](./renderers/Video.md): 视频播放器
|
||||||
- [Table](./renderers/Table.md): 表格展示
|
- [Table](./renderers/Table.md): 表格展示
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
### Carousel
|
||||||
|
|
||||||
|
轮播图
|
||||||
|
|
||||||
|
- `type` 请设置成 `carousel`
|
||||||
|
- `className` 外层 Dom 的类名
|
||||||
|
- `options` 轮播面板数据,默认`[]`,支持以下模式
|
||||||
|
- 图片
|
||||||
|
- `image` 图片链接
|
||||||
|
- `imageClassName` 图片类名
|
||||||
|
- `title` 图片标题
|
||||||
|
- `titleClassName` 图片标题类名
|
||||||
|
- `description` 图片描述
|
||||||
|
- `descriptionClassName` 图片描述类名
|
||||||
|
- `html` HTML 自定义,同[Tpl](./Tpl.md)一致
|
||||||
|
- `auto` 是否自动轮播,默认`true`
|
||||||
|
- `interval` 切换动画间隔,默认`5s`
|
||||||
|
- `duration` 切换动画时长,默认`0.5s`
|
||||||
|
- `width` 宽度,默认`auto`
|
||||||
|
- `height` 高度,默认`200px`
|
||||||
|
- `controls` 显示左右箭头、底部圆点索引,默认`['dots', 'arrows']`
|
||||||
|
- `controlsTheme` 左右箭头、底部圆点索引颜色,默认`light`,另有`dark`模式
|
||||||
|
- `animation` 切换动画效果,默认`fade`,另有`slide`模式
|
||||||
|
|
||||||
|
```schema:height="350" scope="body"
|
||||||
|
{
|
||||||
|
"type": "carousel",
|
||||||
|
"controlTheme": "light",
|
||||||
|
"height": "300",
|
||||||
|
"animation": "slide",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"image": "https://video-react.js.org/assets/poster.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"html": "<div style=\"width: 100%; height: 300px; background: #e3e3e3; text-align: center; line-height: 300px;\">carousel data</div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
|
@ -66,6 +66,7 @@ import ChartSchema from './Chart';
|
||||||
import HorizontalSchema from './Horizontal';
|
import HorizontalSchema from './Horizontal';
|
||||||
import VideoSchema from './Video';
|
import VideoSchema from './Video';
|
||||||
import AudioSchema from './Audio';
|
import AudioSchema from './Audio';
|
||||||
|
import CarouselSchema from './Carousel';
|
||||||
import TasksSchema from './Tasks';
|
import TasksSchema from './Tasks';
|
||||||
import ServicesDataSchema from './Services/Data';
|
import ServicesDataSchema from './Services/Data';
|
||||||
import ServicesSchemaSchema from './Services/Schema';
|
import ServicesSchemaSchema from './Services/Schema';
|
||||||
|
@ -459,6 +460,12 @@ const navigations = [
|
||||||
path: 'chart',
|
path: 'chart',
|
||||||
component: makeSchemaRenderer(ChartSchema)
|
component: makeSchemaRenderer(ChartSchema)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '轮播图',
|
||||||
|
icon: 'fa fa-pause',
|
||||||
|
path: 'carousel',
|
||||||
|
component: makeSchemaRenderer(CarouselSchema)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '音频',
|
label: '音频',
|
||||||
icon: 'fa fa-volume-up',
|
icon: 'fa fa-volume-up',
|
||||||
|
|
|
@ -5,7 +5,33 @@ export default {
|
||||||
{
|
{
|
||||||
"type": "audio",
|
"type": "audio",
|
||||||
"autoPlay": false,
|
"autoPlay": false,
|
||||||
|
"rates": [1.0, 1.5, 2.0],
|
||||||
"src": "http://www.ytmp3.cn/down/32791.mp3",
|
"src": "http://www.ytmp3.cn/down/32791.mp3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 'form',
|
||||||
|
"title": '',
|
||||||
|
"actions": [],
|
||||||
|
"className": 'b v-middle inline w-lg h-xs',
|
||||||
|
"controls": [
|
||||||
|
{
|
||||||
|
"type": "card",
|
||||||
|
"className": 'v-middle w inline no-border',
|
||||||
|
"header": {
|
||||||
|
"title": "歌曲名称",
|
||||||
|
"subTitle": "专辑名称",
|
||||||
|
"description": "description",
|
||||||
|
"avatarClassName": "pull-left thumb-md avatar m-r no-border",
|
||||||
|
"avatar": "http://hiphotos.baidu.com/fex/%70%69%63/item/c9fcc3cec3fdfc03ccabb38edd3f8794a4c22630.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "audio",
|
||||||
|
"className": 'v-middle no-border',
|
||||||
|
"src": "http://www.ytmp3.cn/down/32791.mp3",
|
||||||
|
"controls": ['play']
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -10,6 +10,12 @@ export default {
|
||||||
label: "ID",
|
label: "ID",
|
||||||
type: "text"
|
type: "text"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "carousel",
|
||||||
|
label: "轮播图",
|
||||||
|
type: "carousel",
|
||||||
|
width: "300"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "text",
|
name: "text",
|
||||||
label: "文本",
|
label: "文本",
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
export default {
|
||||||
|
type: 'page',
|
||||||
|
title: '轮播图',
|
||||||
|
data: {
|
||||||
|
carousel: [
|
||||||
|
{
|
||||||
|
html: '<div style="width: 100%; height: 300px; background: #e3e3e3; text-align: center; line-height: 300px;">carousel data in form</div>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: 'https://www.baidu.com/img/bd_logo1.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
type: 'grid',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
type: 'carousel',
|
||||||
|
controlsTheme: 'light',
|
||||||
|
height: '300',
|
||||||
|
className: 'm-t-xxl',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
image: 'https://video-react.js.org/assets/poster.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
html: '<div style="width: 100%; height: 300px; background: #e3e3e3; text-align: center; line-height: 300px;">carousel data</div>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'form',
|
||||||
|
title: '表单',
|
||||||
|
className: 'm-b-xxl',
|
||||||
|
controls: [
|
||||||
|
{
|
||||||
|
type: 'carousel',
|
||||||
|
controlsTheme: 'dark',
|
||||||
|
name: 'carousel',
|
||||||
|
label: 'carousel',
|
||||||
|
animation: 'slide',
|
||||||
|
height: '300'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -507,6 +507,13 @@ export default {
|
||||||
cb(null, makeMarkdownRenderer(doc));
|
cb(null, makeMarkdownRenderer(doc));
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Carousel',
|
||||||
|
path: '/docs/renderers/Carousel',
|
||||||
|
getComponent: (location, cb) => require(['../../docs/renderers/Carousel.md'], (doc) => {
|
||||||
|
cb(null, makeMarkdownRenderer(doc));
|
||||||
|
}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Audio',
|
label: 'Audio',
|
||||||
path: '/docs/renderers/Audio',
|
path: '/docs/renderers/Audio',
|
||||||
|
|
|
@ -24,6 +24,17 @@ module.exports = function(req, res) {
|
||||||
title: '{{name.title}}',
|
title: '{{name.title}}',
|
||||||
description: '{{lorem.words}}'
|
description: '{{lorem.words}}'
|
||||||
}), Math.round(Math.random() * 10)),
|
}), Math.round(Math.random() * 10)),
|
||||||
|
carousel: [
|
||||||
|
{
|
||||||
|
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
html: '<div style="width: 100%; height: 200px; background: #e3e3e3; text-align: center; line-height: 200px;">carousel data in crud</div>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: 'https://video-react.js.org/assets/poster.png'
|
||||||
|
}
|
||||||
|
],
|
||||||
date: Math.round(Date.now() / 1000),
|
date: Math.round(Date.now() / 1000),
|
||||||
// image: '{{image.imageUrl}}',
|
// image: '{{image.imageUrl}}',
|
||||||
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg',
|
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg',
|
||||||
|
|
|
@ -1334,3 +1334,21 @@ $Audio-svg-width: px2rem(20px) !default;
|
||||||
$Audio-svg-height: px2rem(20px) !default;
|
$Audio-svg-height: px2rem(20px) !default;
|
||||||
$Audio-svg-top: px2rem(6px) !default;
|
$Audio-svg-top: px2rem(6px) !default;
|
||||||
$Audio-item-margin: px2rem(10px) !default;
|
$Audio-item-margin: px2rem(10px) !default;
|
||||||
|
|
||||||
|
// Carousel
|
||||||
|
$Carousel-bg: #F6F8F8;
|
||||||
|
$Carousel-minWidth: px2rem(100px) !default;
|
||||||
|
$Carousel-height: px2rem(200px) !default;
|
||||||
|
$Carousel-arrowControl-width: px2rem(20px) !default;
|
||||||
|
$Carousel-arrowControl-height: px2rem(20px) !default;
|
||||||
|
$Carousel-svg-width: px2rem(20px) !default;
|
||||||
|
$Carousel-svg-height: px2rem(20px) !default;
|
||||||
|
$Carousel-dot-width: px2rem(8px) !default;
|
||||||
|
$Carousel-dot-height: px2rem(8px) !default;
|
||||||
|
$Carousel-dot-borderRadius: px2rem(4px) !default;
|
||||||
|
$Carousel-dot-margin: px2rem(7px) px2rem(5px) !default;
|
||||||
|
$Carousel--light-control: white !default;
|
||||||
|
$Carousel--dark-control: black !default;
|
||||||
|
$Carousel-transitionDuration: 0.3s !default;
|
||||||
|
$Carousel-imageTitle-bottom: px2rem(45px) !default;
|
||||||
|
$Carousel-imageDescription-bottom: px2rem(25px) !default;
|
|
@ -0,0 +1,165 @@
|
||||||
|
@mixin arrow-control {
|
||||||
|
width: 10%;
|
||||||
|
min-width: $Carousel-arrowControl-width;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
transition-duration: $Carousel-transitionDuration;
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
right: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: $Carousel-svg-width;
|
||||||
|
height: $Carousel-svg-height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Carousel {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
background: $Carousel-bg;
|
||||||
|
|
||||||
|
&.#{$ns}Carousel--light {
|
||||||
|
.#{$ns}Carousel-dot {
|
||||||
|
background-color: $Carousel--light-control;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $Carousel--light-control;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Carousel-item {
|
||||||
|
.title,
|
||||||
|
.description {
|
||||||
|
color: $Carousel--light-control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.#{$ns}Carousel--dark {
|
||||||
|
.#{$ns}Carousel-dot {
|
||||||
|
background-color: $Carousel--dark-control;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $Carousel--dark-control;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Carousel-item {
|
||||||
|
.title,
|
||||||
|
.description {
|
||||||
|
color: $Carousel--dark-control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-container {
|
||||||
|
min-width: $Carousel-minWidth;
|
||||||
|
height: $Carousel-height;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.#{$ns}Carousel-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
transition: ease-out all $Carousel-transitionDuration;
|
||||||
|
|
||||||
|
&.fade {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fade.in {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slide {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slide.in {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slide.out {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slideRight {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slideRight.in {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slideRight.out {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
position: absolute;
|
||||||
|
bottom: $Carousel-imageTitle-bottom;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
position: absolute;
|
||||||
|
bottom: $Carousel-imageDescription-bottom;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-dotsControl {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.#{$ns}Carousel-dot {
|
||||||
|
display: inline-block;
|
||||||
|
height: $Carousel-dot-width;
|
||||||
|
width: $Carousel-dot-height;
|
||||||
|
border-radius: $Carousel-dot-borderRadius;
|
||||||
|
margin: $Carousel-dot-margin;
|
||||||
|
transition-duration: $Carousel-transitionDuration;
|
||||||
|
opacity: 0.3;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrowsControl {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: inherit;
|
||||||
|
z-index: 100;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.#{$ns}Carousel-leftArrow {
|
||||||
|
@include arrow-control;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Carousel-rightArrow {
|
||||||
|
@include arrow-control;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -432,6 +432,7 @@ $TagControl-sugTip-color: $primary;
|
||||||
@import "../components/pagination";
|
@import "../components/pagination";
|
||||||
@import "../components/wrapper";
|
@import "../components/wrapper";
|
||||||
@import "../components/status";
|
@import "../components/status";
|
||||||
|
@import "../components/carousel";
|
||||||
|
|
||||||
@import "../components/form/fieldset";
|
@import "../components/form/fieldset";
|
||||||
@import "../components/form/group";
|
@import "../components/form/group";
|
||||||
|
|
|
@ -59,6 +59,7 @@ $Form-input-borderColor: #cfdadd;
|
||||||
@import "../components/pagination";
|
@import "../components/pagination";
|
||||||
@import "../components/wrapper";
|
@import "../components/wrapper";
|
||||||
@import "../components/status";
|
@import "../components/status";
|
||||||
|
@import "../components/carousel";
|
||||||
|
|
||||||
@import "../components/form/fieldset";
|
@import "../components/form/fieldset";
|
||||||
@import "../components/form/group";
|
@import "../components/form/group";
|
||||||
|
|
|
@ -75,3 +75,19 @@ export const pauseIcon = (
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
export const leftArrowIcon = (
|
||||||
|
<svg className="icon" viewBox="0 0 1024 1024" version="1.1">
|
||||||
|
<path
|
||||||
|
d="M324.211517 511.805631 787.889594 73.082583c16.19422-16.630365 16.19422-43.974704 0-60.605068-16.19422-16.630365-42.495607-16.630365-58.613976 0L235.750113 479.360302c-8.647031 8.969398-12.344775 20.934917-11.719003 32.445329-0.644735 11.90863 3.071972 23.874149 11.719003 32.824585l493.506542 466.882788c16.118369 16.649327 42.438718 16.649327 58.613976 0 16.19422-17.085471 16.19422-43.974704 0-60.605068L324.211517 511.805631"
|
||||||
|
p-id="2160"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
export const rightArrowIcon = (
|
||||||
|
<svg className="icon" viewBox="0 0 1024 1024" version="1.1">
|
||||||
|
<path
|
||||||
|
d="M311.559054 1013.77369L767.908116 512.684524 311.559054 12.234501a31.318073 31.318073 0 1 0-46.657538 41.544383L679.706197 512.684524 267.458094 969.672731a31.318073 31.318073 0 0 0 46.018393 42.183526z"
|
||||||
|
p-id="1981"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
|
@ -149,6 +149,7 @@ import './renderers/Wrapper';
|
||||||
import './renderers/IFrame';
|
import './renderers/IFrame';
|
||||||
import './renderers/QRCode';
|
import './renderers/QRCode';
|
||||||
import './renderers/Icon';
|
import './renderers/Icon';
|
||||||
|
import './renderers/Carousel';
|
||||||
import Scoped, {ScopedContext} from './Scoped';
|
import Scoped, {ScopedContext} from './Scoped';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -379,7 +379,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
const {muted} = this.state;
|
const {muted} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(inline ? 'Audio--inline' : '')}>
|
<div className={cx('Audio', className, inline ? 'Audio--inline' : '')}>
|
||||||
<audio
|
<audio
|
||||||
className={cx('Audio-original')}
|
className={cx('Audio-original')}
|
||||||
ref={this.audioRef}
|
ref={this.audioRef}
|
||||||
|
@ -390,7 +390,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
loop={loop}>
|
loop={loop}>
|
||||||
<source src={src} />
|
<source src={src} />
|
||||||
</audio>
|
</audio>
|
||||||
<div className={cx('Audio', className)}>
|
<div className={cx('Audio-controls')}>
|
||||||
{controls && controls.map((control:string, index:number) => {
|
{controls && controls.map((control:string, index:number) => {
|
||||||
control = 'render' + upperFirst(control);
|
control = 'render' + upperFirst(control);
|
||||||
const method:'renderRates'|'renderPlay'|'renderTime'|'renderProcess'|'renderVolume'|'render' = control as any;
|
const method:'renderRates'|'renderPlay'|'renderTime'|'renderProcess'|'renderVolume'|'render' = control as any;
|
||||||
|
|
|
@ -0,0 +1,301 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import Transition, {ENTERED, ENTERING, EXITING} from 'react-transition-group/Transition';
|
||||||
|
import {Renderer, RendererProps} from '../factory';
|
||||||
|
import {autobind, createObject} from '../utils/helper';
|
||||||
|
import {leftArrowIcon, rightArrowIcon} from '../components/icons';
|
||||||
|
|
||||||
|
const animationStyles: {
|
||||||
|
[propName: string]: string;
|
||||||
|
} = {
|
||||||
|
[ENTERING]: 'in',
|
||||||
|
[ENTERED]: 'in',
|
||||||
|
[EXITING]: 'out'
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface CarouselProps extends RendererProps {
|
||||||
|
className?: string;
|
||||||
|
auto?: boolean;
|
||||||
|
value?: any;
|
||||||
|
placeholder?: any;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
controls: string[];
|
||||||
|
interval: number;
|
||||||
|
duration: number;
|
||||||
|
controlsTheme: 'light' | 'dark';
|
||||||
|
animation: 'fade' | 'slide';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CarouselState {
|
||||||
|
current: number;
|
||||||
|
options: any[];
|
||||||
|
showArrows: boolean;
|
||||||
|
nextAnimation: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||||
|
wrapperRef: React.RefObject<HTMLDivElement>;
|
||||||
|
intervalTimeout: number;
|
||||||
|
durationTimeout: number;
|
||||||
|
|
||||||
|
static defaultProps: Pick<
|
||||||
|
CarouselProps,
|
||||||
|
'auto' | 'interval' | 'duration' | 'controlsTheme' | 'animation' | 'controls' | 'placeholder'
|
||||||
|
> = {
|
||||||
|
auto: true,
|
||||||
|
interval: 5000,
|
||||||
|
duration: 500,
|
||||||
|
controlsTheme: 'light',
|
||||||
|
animation: 'fade',
|
||||||
|
controls: ['dots', 'arrows'],
|
||||||
|
placeholder: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props:CarouselProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
current: 0,
|
||||||
|
options: this.props.value ? this.props.value : this.props.options ? this.props.options : [],
|
||||||
|
showArrows: false,
|
||||||
|
nextAnimation: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.wrapperRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.prepareAutoSlide();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.clearAutoTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
prepareAutoSlide () {
|
||||||
|
if (this.state.options.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearAutoTimeout();
|
||||||
|
if (this.props.auto) {
|
||||||
|
this.intervalTimeout = setTimeout(this.autoSlide, this.props.interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
autoSlide (rel?:string) {
|
||||||
|
this.clearAutoTimeout();
|
||||||
|
const {animation} = this.props;
|
||||||
|
let {nextAnimation} = this.state;
|
||||||
|
|
||||||
|
switch (rel) {
|
||||||
|
case 'prev':
|
||||||
|
animation === 'slide' ? nextAnimation = 'slideRight' : nextAnimation = '';
|
||||||
|
this.transitFramesTowards('right', nextAnimation);
|
||||||
|
break;
|
||||||
|
case 'next':
|
||||||
|
default:
|
||||||
|
nextAnimation = '';
|
||||||
|
this.transitFramesTowards('left', nextAnimation);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.durationTimeout = setTimeout(this.prepareAutoSlide, this.props.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
transitFramesTowards (direction:string, nextAnimation: string) {
|
||||||
|
let {current} = this.state;
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case 'left':
|
||||||
|
current = this.getFrameId('next');
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
current = this.getFrameId('prev');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
current,
|
||||||
|
nextAnimation
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
getFrameId (pos?:string) {
|
||||||
|
const {options, current} = this.state;
|
||||||
|
const total = options.length;
|
||||||
|
switch (pos) {
|
||||||
|
case 'prev':
|
||||||
|
return (current - 1 + total) % total;
|
||||||
|
case 'next':
|
||||||
|
return (current + 1) % total;
|
||||||
|
default:
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
next () {
|
||||||
|
this.autoSlide('next');
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
prev () {
|
||||||
|
this.autoSlide('prev');
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
clearAutoTimeout () {
|
||||||
|
clearTimeout(this.intervalTimeout);
|
||||||
|
clearTimeout(this.durationTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDots() {
|
||||||
|
const {classnames: cx} = this.props;
|
||||||
|
const {current, options} = this.state;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx('Carousel-dotsControl')}
|
||||||
|
onMouseEnter={this.handleMouseEnter}
|
||||||
|
onMouseLeave={this.handleMouseLeave}
|
||||||
|
>
|
||||||
|
{Array.from({length: options.length}).map((_, i) =>
|
||||||
|
<span key={i} className={cx('Carousel-dot', current === i ? 'is-active' : '')}></span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderArrows() {
|
||||||
|
const {classnames: cx} = this.props;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx('Carousel-arrowsControl')}
|
||||||
|
onMouseEnter={this.handleMouseEnter}
|
||||||
|
onMouseLeave={this.handleMouseLeave}
|
||||||
|
>
|
||||||
|
<div className={cx('Carousel-leftArrow')} onClick={this.prev}>{leftArrowIcon}</div>
|
||||||
|
<div className={cx('Carousel-rightArrow')} onClick={this.next}>{rightArrowIcon}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
defaultSchema() {
|
||||||
|
return {
|
||||||
|
type: 'tpl',
|
||||||
|
tpl:
|
||||||
|
"<% if (data.image) { %> " +
|
||||||
|
"<div style=\"background-image: url(<%= data.image %>)\" class=\"image <%= data.imageClassName %>\"></div>" +
|
||||||
|
"<% if (data.title) { %> " +
|
||||||
|
"<div class=\"title <%= data.titleClassName %>\"><%= data.title %></div>" +
|
||||||
|
"<% } if (data.description) { %> " +
|
||||||
|
"<div class=\"description <%= data.descriptionClassName %>\"><%= data.description %></div>" +
|
||||||
|
"<% } %>" +
|
||||||
|
"<% } else if (data.html) { %>" +
|
||||||
|
"<%= data.html %>" +
|
||||||
|
"<% } %>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleMouseEnter() {
|
||||||
|
this.setState({
|
||||||
|
showArrows: true
|
||||||
|
});
|
||||||
|
this.clearAutoTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleMouseLeave() {
|
||||||
|
this.setState({
|
||||||
|
showArrows: false
|
||||||
|
});
|
||||||
|
this.prepareAutoSlide();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
render,
|
||||||
|
className,
|
||||||
|
classnames: cx,
|
||||||
|
itemSchema,
|
||||||
|
animation,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
controls,
|
||||||
|
controlsTheme,
|
||||||
|
placeholder,
|
||||||
|
data
|
||||||
|
} = this.props;
|
||||||
|
const {
|
||||||
|
options,
|
||||||
|
showArrows,
|
||||||
|
current,
|
||||||
|
nextAnimation
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
let body:JSX.Element | null = null;
|
||||||
|
let carouselStyles: {
|
||||||
|
[propName: string]: string;
|
||||||
|
} = {};
|
||||||
|
width ? carouselStyles.width = width + 'px' : '';
|
||||||
|
height ? carouselStyles.height = height + 'px' : '';
|
||||||
|
const [dots, arrows] = [controls.indexOf('dots') > -1, controls.indexOf('arrows') > -1];
|
||||||
|
const animationName = nextAnimation || animation;
|
||||||
|
|
||||||
|
if (options && options.length) {
|
||||||
|
body = (
|
||||||
|
<div
|
||||||
|
ref={this.wrapperRef}
|
||||||
|
className={cx('Carousel-container')}
|
||||||
|
onMouseEnter={this.handleMouseEnter}
|
||||||
|
onMouseLeave={this.handleMouseLeave}
|
||||||
|
style={carouselStyles}
|
||||||
|
>
|
||||||
|
{options.map((option:any, key:number) => (
|
||||||
|
<Transition
|
||||||
|
mountOnEnter
|
||||||
|
unmountOnExit
|
||||||
|
in={key === current}
|
||||||
|
timeout={500}
|
||||||
|
key={key}
|
||||||
|
>
|
||||||
|
{(status:string) => {
|
||||||
|
if (status === ENTERING) {
|
||||||
|
this.wrapperRef.current && this.wrapperRef.current.childNodes.forEach((item:HTMLElement) => item.offsetHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('Carousel-item', animationName, animationStyles[status])}>
|
||||||
|
{render(`${current}/body`, itemSchema ? itemSchema : this.defaultSchema(), {
|
||||||
|
data: createObject(data, option)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Transition>
|
||||||
|
))}
|
||||||
|
{dots ? this.renderDots() : null}
|
||||||
|
{arrows && showArrows ? this.renderArrows() : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx(`Carousel Carousel--${controlsTheme}`, className)}>
|
||||||
|
{body ? body : placeholder}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Renderer({
|
||||||
|
test: /(^|\/)carousel/,
|
||||||
|
name: 'carousel',
|
||||||
|
})
|
||||||
|
export class CarouselRenderer extends Carousel {}
|
|
@ -1,5 +1,5 @@
|
||||||
import { reigsterTplEnginer, filter } from "./tpl";
|
import { reigsterTplEnginer, filter } from "./tpl";
|
||||||
import tempalte = require('lodash/template');
|
import template = require('lodash/template');
|
||||||
import { filters } from "./tpl-builtin";
|
import { filters } from "./tpl-builtin";
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
@ -31,7 +31,7 @@ const imports = {
|
||||||
delete imports.default; // default 是个关键字,不能 imports 到 lodash 里面去。
|
delete imports.default; // default 是个关键字,不能 imports 到 lodash 里面去。
|
||||||
function lodashCompile(str: string, data: object) {
|
function lodashCompile(str: string, data: object) {
|
||||||
try {
|
try {
|
||||||
const fn = tempalte(str, {
|
const fn = template(str, {
|
||||||
imports: imports,
|
imports: imports,
|
||||||
variable: 'data'
|
variable: 'data'
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue