diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index eed40112f6..165f56ce17 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -123,6 +123,11 @@ jobs: with: token: ${{ secrets.NPM_TOKEN }} package: ./packages/react-card/package.json + - name: 📦 @uiw/react-carousel publish to NPM + uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + package: ./packages/react-carousel/package.json - name: 📦 @uiw/react-checkbox publish to NPM uses: JS-DevTools/npm-publish@v1 with: diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 3abd32ed25..5ebd7a38c1 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -192,6 +192,11 @@ jobs: with: token: ${{ secrets.NPM_TOKEN }} package: ./packages/react-card/package.json + - name: 📦 @uiw/react-carousel publish to NPM + uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + package: ./packages/react-carousel/package.json - name: 📦 @uiw/react-checkbox publish to NPM uses: JS-DevTools/npm-publish@v1 with: diff --git a/packages/react-carousel/README.md b/packages/react-carousel/README.md new file mode 100644 index 0000000000..a8df0173d9 --- /dev/null +++ b/packages/react-carousel/README.md @@ -0,0 +1,140 @@ +Carousel 走马灯 +=== + +[![Open in unpkg](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/react-carousel/file/README.md) +[![NPM Downloads](https://img.shields.io/npm/dm/@uiw/react-carousel.svg?style=flat)](https://www.npmjs.com/package/@uiw/react-carousel) +[![npm version](https://img.shields.io/npm/v/@uiw/react-carousel.svg?label=@uiw/react-carousel)](https://npmjs.com/@uiw/react-carousel) + +滚动播放。在 v4.15.0+ 添加。 + +## 基础用法 + +最简单的用法。 + + +```jsx +import React from 'react'; +import { Carousel } from 'uiw'; + +function Demo() { + return ( + +
+ 1 +
+
+ 2 +
+
+ 3 +
+
+ 4 +
+
+ ); +} + +ReactDOM.render(, _mount_); +``` + +## 控制播放频率 + +palyTime设置每帧停留时间,scrollTime设置切换帧的速度 + + +```jsx +import React from 'react'; +import { Carousel } from 'uiw'; + +function Demo() { + return ( + +
+ 1 +
+
+ 2 +
+
+ 3 +
+
+ 4 +
+
+ ); +} + +ReactDOM.render(, _mount_); +``` + +## 切换到指定帧 + +手动切换到指定帧的位置 + + +```jsx +import React from 'react'; +import { Carousel } from 'uiw'; + +function Demo() { + + const ref=React.useRef() + const [autoPlay,autoPlaySet]=React.useState(true) + + return ( + + +
+ 1 +
+
+ 2 +
+
+ 3 +
+
+ 4 +
+
+ + + + +
+ ); + } + +ReactDOM.render(, _mount_); +``` + +## Props + +## API + +| 参数 | 说明 | 类型 | 默认值 | +|--------- |-------- |--------- |-------- | +| width | 宽度 | number | 400 | +| height | 高度 | number | 200 | +| position | 设置初始帧位置 | number | 0 | +| palyTime | 每帧停留时间(ms) | number | 2000 | +| scrollTime | 滚动动画的速度(ms) | number | 200 | +| autoPlay | 是否自动播放 | boolean | true | + + +### ref + +```ts + // 跳转到指定帧 + gotoSlide: (slideNumber: number) => void; + // 上一针 + prevSlide: () => void; + // 下一帧 + nextSlide: () => void; + // 暂停播放 + stopPlay: () => void; +``` + + diff --git a/packages/react-carousel/package.json b/packages/react-carousel/package.json new file mode 100644 index 0000000000..99482d7e71 --- /dev/null +++ b/packages/react-carousel/package.json @@ -0,0 +1,47 @@ +{ + "name": "@uiw/react-carousel", + "version": "4.14.2", + "description": "Carousel component", + "homepage": "https://uiwjs.github.io/#/components/carousel", + "repository": { + "type": "git", + "url": "https://github.com/uiwjs/uiw.git" + }, + "license": "MIT", + "main": "cjs/index.js", + "module": "esm/index.js", + "files": [ + "dist.css", + "cjs", + "esm", + "src" + ], + "publishConfig": { + "access": "public" + }, + "keywords": [ + "carousel", + "design", + "uiw", + "uiw-react", + "react.js", + "react", + "react-component", + "component", + "components", + "ui", + "css", + "uikit", + "react-ui", + "framework", + "front-end", + "frontend" + ], + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "dependencies": { + "@uiw/utils": "^4.14.2" + } +} diff --git a/packages/react-carousel/src/index.tsx b/packages/react-carousel/src/index.tsx new file mode 100644 index 0000000000..72e70c44d5 --- /dev/null +++ b/packages/react-carousel/src/index.tsx @@ -0,0 +1,103 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { IProps, HTMLDivProps } from '@uiw/utils'; +import './style/index.less'; + +export interface CarouselProps extends IProps, HTMLDivProps { + width?: number; + height?: number; + position?: number; + palyTime?: number; + scrollTime?: number; + autoPlay?: boolean; +} + +export interface CarouselRef { + gotoSlide: (slide: number, dontAnimate?: boolean) => void; + prevSlide: () => void; + nextSlide: () => void; + stopPlay: () => void; +} + +function Carousel(props: CarouselProps, ref: CarouselRef) { + const { + position = 0, + width = 400, + height = 200, + palyTime = 2000, + scrollTime = 200, + autoPlay = true, + + prefixCls = 'w-carousel', + className, + style, + } = props; + + const cls = useMemo(() => [prefixCls, className].filter(Boolean).join(' ').trim(), [prefixCls, className]); + + const [currentPosition, currentPositionSet] = useState(position); + const positionRef = useRef(currentPosition); + const childCount = React.Children.count(props.children); + const stopPlay = useRef(() => {}); + + React.useImperativeHandle( + ref, + () => ({ + gotoSlide, + prevSlide: () => gotoSlide(positionRef.current - 1), + nextSlide: () => gotoSlide(positionRef.current + 1), + stopPlay: () => stopPlay.current(), + }), + [ref], + ); + + const gotoSlide = (slidNumber: number) => { + stopPlay.current(); + const maxSlid = childCount - 1; + let slidNumberTemp = slidNumber > maxSlid ? maxSlid : slidNumber; + slidNumberTemp = slidNumber < 0 ? 0 : slidNumberTemp; + positionRef.current = slidNumberTemp; + currentPositionSet(slidNumberTemp); + play(); + }; + + const play = (ms: number = palyTime) => { + if (autoPlay) { + const time = setInterval(() => { + positionRef.current++; + if (positionRef.current >= childCount) { + positionRef.current = 0; + } + currentPositionSet(positionRef.current); + }, ms); + stopPlay.current = () => { + clearInterval(time); + }; + } + }; + + useEffect(() => { + play(); + return () => { + stopPlay.current(); + }; + }, [autoPlay]); + + return ( +
+
+ {React.Children.map(props.children, (child) => { + return
{child}
; + })} +
+
+ ); +} + +export default React.forwardRef(Carousel); diff --git a/packages/react-carousel/src/style/index.less b/packages/react-carousel/src/style/index.less new file mode 100644 index 0000000000..a940dec0e6 --- /dev/null +++ b/packages/react-carousel/src/style/index.less @@ -0,0 +1,13 @@ +@w-carousel: ~'w-carousel'; + +.@{w-carousel} { + overflow: hidden; + + &-content { + height: 200px; + // transform: translate3d(); + display: flex; + flex-direction: row; + transition: 0.6s ease-in-out; + } +} diff --git a/packages/react-carousel/tsconfig.json b/packages/react-carousel/tsconfig.json new file mode 100644 index 0000000000..41a3f0ac82 --- /dev/null +++ b/packages/react-carousel/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig", + "include": ["src/**/*"], + "compilerOptions": { + "outDir": "./cjs", + "baseUrl": "." + } +} diff --git a/packages/react-transfer/package.json b/packages/react-transfer/package.json index 0d8cca6396..0c86cc9ca6 100644 --- a/packages/react-transfer/package.json +++ b/packages/react-transfer/package.json @@ -44,7 +44,6 @@ "dependencies": { "@uiw/react-card": "^4.14.2", "@uiw/react-checkbox": "^4.14.2", - "@uiw/react-grid": "^4.14.2", "@uiw/react-icon": "^4.14.2", "@uiw/react-input": "^4.14.2", "@uiw/react-tree": "^4.14.2", diff --git a/packages/uiw/package.json b/packages/uiw/package.json index d152fc8f0f..adc4a2e487 100644 --- a/packages/uiw/package.json +++ b/packages/uiw/package.json @@ -67,6 +67,7 @@ "@uiw/react-button-group": "^4.14.2", "@uiw/react-calendar": "^4.14.2", "@uiw/react-card": "^4.14.2", + "@uiw/react-carousel": "^4.14.2", "@uiw/react-checkbox": "^4.14.2", "@uiw/react-collapse": "^4.14.2", "@uiw/react-copy-to-clipboard": "^4.14.2", diff --git a/packages/uiw/src/index.ts b/packages/uiw/src/index.ts index 8c34dd6119..1940b672e7 100644 --- a/packages/uiw/src/index.ts +++ b/packages/uiw/src/index.ts @@ -9,6 +9,7 @@ export * from '@uiw/react-button'; export * from '@uiw/react-button-group'; export * from '@uiw/react-calendar'; export * from '@uiw/react-card'; +export * from '@uiw/react-carousel'; export * from '@uiw/react-checkbox'; export * from '@uiw/react-collapse'; export * from '@uiw/react-copy-to-clipboard'; @@ -69,6 +70,7 @@ export { default as Button } from '@uiw/react-button'; export { default as ButtonGroup } from '@uiw/react-button-group'; export { default as Calendar } from '@uiw/react-calendar'; export { default as Card } from '@uiw/react-card'; +export { default as Carousel } from '@uiw/react-carousel'; export { default as Checkbox } from '@uiw/react-checkbox'; export { default as Collapse } from '@uiw/react-collapse'; export { default as CopyToClipboard } from '@uiw/react-copy-to-clipboard'; diff --git a/website/src/menu.json b/website/src/menu.json index f6acaae0d4..78cea3bdbd 100755 --- a/website/src/menu.json +++ b/website/src/menu.json @@ -70,6 +70,7 @@ { "name": "Avatar 头像", "path": "avatar" }, { "name": "Badge 标记", "path": "badge" }, { "name": "Card 卡片", "path": "card" }, + { "name": "Carousel 走马灯", "path": "carousel" }, { "name": "Collapse 折叠面板", "path": "collapse" }, { "name": "Descriptions 描述列表", "path": "descriptions" }, { "name": "Tag 标签", "path": "tag" }, diff --git a/website/src/routers.tsx b/website/src/routers.tsx index 29c27c341e..cb02b2ba70 100755 --- a/website/src/routers.tsx +++ b/website/src/routers.tsx @@ -34,6 +34,7 @@ const Checkbox = Loadable(lazy(() => import('./routes/components/checkbox'))); const CopyToClipboard = Loadable(lazy(() => import('./routes/components/copy-to-clipboard'))); const Collapse = Loadable(lazy(() => import('./routes/components/collapse'))); const Card = Loadable(lazy(() => import('./routes/components/card'))); +const Carousel = Loadable(lazy(() => import('./routes/components/carousel'))); const Descriptions = Loadable(lazy(() => import('./routes/components/descriptions'))); const Loader = Loadable(lazy(() => import('./routes/components/loader'))); const Icon = Loadable(lazy(() => import('./routes/components/icon'))); @@ -126,6 +127,7 @@ export const routes: RouteObject[] = [ { path: '/components/copy-to-clipboard', element: }, { path: '/components/collapse', element: }, { path: '/components/card', element: }, + { path: '/components/carousel', element: }, { path: '/components/descriptions', element: }, { path: '/components/loader', element: }, { path: '/components/icon', element: }, diff --git a/website/src/routes/components/carousel/index.tsx b/website/src/routes/components/carousel/index.tsx new file mode 100755 index 0000000000..7497a0ed9a --- /dev/null +++ b/website/src/routes/components/carousel/index.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Carousel, Row, Col } from 'uiw'; +import Markdown from '../../../components/Markdown'; + +export default function Page() { + return ( + { + const md = await import('uiw/node_modules/@uiw/react-carousel/README.md'); + return md.default || md; + }} + /> + ); +} diff --git a/website/src/routes/components/transfer/index.tsx b/website/src/routes/components/transfer/index.tsx index 688d05a017..e481270814 100644 --- a/website/src/routes/components/transfer/index.tsx +++ b/website/src/routes/components/transfer/index.tsx @@ -4,7 +4,7 @@ import Markdown from '../../../components/Markdown'; export default () => ( { const md = await import('uiw/node_modules/@uiw/react-transfer/README.md');