diff --git a/demo/Button.tsx b/demo/Button.tsx index 0efd23b..50a7310 100644 --- a/demo/Button.tsx +++ b/demo/Button.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Button, FloatLayout } from '../src'; +import { Button } from '../src'; /** * title: Basic Modal * title.zh-CN: 基础 Modal diff --git a/demo/Countdown.tsx b/demo/Countdown.tsx new file mode 100644 index 0000000..ef9dbf5 --- /dev/null +++ b/demo/Countdown.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Countdown } from '../src'; +import './index.less'; + +function CountdownDemo() { + return ( +
+
+
+
+
+
+
+
默认格式:时 : 分 : 秒
+ +
显示天数
+ +
+
+
自定义格式: 天 : hh : mm : ss
+ +
+
+
倒计时时间到,执行的回调函数
+ { + console.log('biu~'); + }} + /> +
+
+ ); +} +export default CountdownDemo; diff --git a/demo/index.less b/demo/index.less index 528cb81..888f4e5 100644 --- a/demo/index.less +++ b/demo/index.less @@ -7,3 +7,7 @@ border-radius: 10px; } } +* { + margin: 0; + padding: 0; +} diff --git a/docs/guide/Countdown.md b/docs/guide/Countdown.md new file mode 100644 index 0000000..e634975 --- /dev/null +++ b/docs/guide/Countdown.md @@ -0,0 +1,25 @@ +--- +title: Countdown 倒计时 +--- + +## Countdown 倒计时 + +倒计时组件 + +- 支持自定义格式 +- 支持 天 时 分 秒 + + + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| ---------- | -------------- | ------- | ------ | ------------------------------------------------ | +| isShowDay | 是否显示天数 | Boolean | - | false | +| isShowHour | 是否显示小时 | Boolean | - | true | +| format | 格式化分割符号 | Object | - | day: '天',hours: '时',minutes: '分 seconds : '秒 | +| day | 天数 | Number | - | 0 | +| hours | 小时 | Number | - | 0 | +| minutes | 分钟 | Number | - | 0 | +| seconds | 秒 | Number | - | 0 | + +tip: +后续考虑设计多定时器样式 依据 type 状态机选择定时器样式 diff --git a/src/.umi/core/routes.ts b/src/.umi/core/routes.ts index c1c3092..4d812e6 100644 --- a/src/.umi/core/routes.ts +++ b/src/.umi/core/routes.ts @@ -197,7 +197,7 @@ export function getRoutes() { exact: true, meta: { filePath: 'docs/guide/ArrowButton.md', - updatedTime: null, + updatedTime: 1617088633000, title: 'ArrowButton 不规则标签', slugs: [ { @@ -207,8 +207,8 @@ export function getRoutes() { }, { depth: 2, - value: 'API文档', - heading: 'api文档' + value: 'API 文档', + heading: 'api-文档' } ], nav: { @@ -241,6 +241,29 @@ export function getRoutes() { }, title: 'Button 按钮' }, + { + path: '/guide/countdown', + component: require('/Users/shide/PROJ/biuUI/docs/guide/Countdown.md') + .default, + exact: true, + meta: { + filePath: 'docs/guide/Countdown.md', + updatedTime: null, + title: 'Countdown 倒计时', + slugs: [ + { + depth: 2, + value: 'Countdown 倒计时', + heading: 'countdown-倒计时' + } + ], + nav: { + path: '/guide', + title: 'Guide' + } + }, + title: 'Countdown 倒计时' + }, { path: '/guide/curtain', component: require('/Users/shide/PROJ/biuUI/docs/guide/Curtain.md') @@ -276,7 +299,7 @@ export function getRoutes() { exact: true, meta: { filePath: 'docs/guide/Tag.md', - updatedTime: null, + updatedTime: 1617088633000, title: 'Tag 标签', slugs: [ { @@ -286,8 +309,8 @@ export function getRoutes() { }, { depth: 2, - value: 'API文档', - heading: 'api文档' + value: 'API 文档', + heading: 'api-文档' } ], nav: { @@ -304,7 +327,7 @@ export function getRoutes() { exact: true, meta: { filePath: 'docs/guide/index.md', - updatedTime: 1615798841000, + updatedTime: 1617088633000, title: '指引', order: 2, slugs: [ diff --git a/src/.umi/dumi/config.json b/src/.umi/dumi/config.json index 041e8b9..2b7aff2 100644 --- a/src/.umi/dumi/config.json +++ b/src/.umi/dumi/config.json @@ -50,6 +50,11 @@ "title": "Button 按钮", "meta": {} }, + { + "path": "/guide/countdown", + "title": "Countdown 倒计时", + "meta": {} + }, { "path": "/guide/curtain", "title": "Curtain 幕帘", diff --git a/src/.umi/dumi/demos/index.ts b/src/.umi/dumi/demos/index.ts index d1567b7..d5b4475 100644 --- a/src/.umi/dumi/demos/index.ts +++ b/src/.umi/dumi/demos/index.ts @@ -3,18 +3,75 @@ import React from 'react'; import { dynamic } from 'dumi'; export default { + 'biuui-arrowbutton': { + component: require('/Users/shide/PROJ/biuUI/demo/ArrowButton.tsx').default, + previewerProps: { + sources: { + _: { + tsx: + 'import React from \'react\';\nimport { ArrowButton } from \'../src\';\n\nfunction ArrowButtonDemo() {\n return (\n
\n \n
\n \n
\n \n
\n \n
\n \n
\n );\n}\nexport default ArrowButtonDemo;\n' + }, + 'src/index.ts': { + import: '../src', + content: + "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\nexport { default as Countdown } from './components/Countdown';\n" + }, + 'components/Button/index.tsx': { + import: './components/Button', + content: + "import * as React from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\n/**\n * @param {onClick} func 对外暴露的点击事件\n * @param {className} string 自定义类名\n * @param {type} string 按钮类型 primary | warning | info | default | pure\n * @param {shape} string 按钮形状 circle | radius(默认)\n */\ntype ButtonType =\n | 'primary'\n | 'warning'\n | 'info'\n | 'default'\n | 'pure'\n | 'slider1'\n | 'slider2';\n\ntype typeModel = 'nomal' | 'slide' | undefined;\n\ninterface IButton {\n typeModel?: typeModel;\n type?: ButtonType;\n children?: React.ReactNode;\n shape?: string;\n block?: string;\n className?: string;\n onClick?: React.MouseEventHandler;\n}\nconst typeModelArr = {\n nomal: 'biu-btn',\n slide: 'bit-btn-slide'\n};\nfunction Button(props: IButton) {\n const { children, onClick, className, type, shape, block, typeModel } = props;\n\n const handTypeModal = (typeModel: typeModel): string => {\n switch (true) {\n case typeModel === 'slide':\n return typeModelArr['slide'];\n default:\n return typeModelArr['nomal'];\n }\n };\n\n return (\n \n {children}\n \n );\n}\n\nexport default Button;\n" + }, + 'index.less': { + import: './index.less', + content: + '// @import "../../style/theme/default.scss";\n// @import "../../style/mixins/index.scss";\n\n\n\n\n// $font-size: $font-size-lg;\n\n// .at-count-down {\n// display: inline-block;\n// min-height: $font-size;\n\n// &__item {\n// display: inline-flex;\n// align-items: center;\n// }\n\n// &__time-box {\n// display: inline-block;\n// text-align: center;\n// min-width: $font-size;\n// font: {\n// size: $font-size;\n// family: countDownFont;\n// style: normal;\n// weight: 400;\n// variant: normal;\n// }\n\n// text-transform: none;\n// text-rendering: auto;\n// line-height: 1;\n// -webkit-font-smoothing: antialiased;\n// vertical-align: middle;\n// }\n\n// &__separator {\n// font-size: $font-size-base;\n// display: inline-flex;\n// align-items: center;\n// text-align: justify;\n// padding: 0 $spacing-v-xs;\n// }\n\n// &--card {\n// .at-count-down__time-box {\n// padding: $spacing-v-xs 0;\n// border: 1PX solid $color-border-grey;\n// border-radius: $border-radius-md;\n// color: #FF4949;\n// display: inline-block;\n// background-color: #fff;\n// position: relative;\n\n// .at-count-down__time {\n// position: relative;\n// z-index: $zindex-divider + 1;\n// }\n\n// &::after {\n// position: absolute;\n// content: \'\';\n// width: 100%;\n// height: 1PX;\n// top: 50%;\n// left: 0;\n// z-index: $zindex-divider;\n// background-color: $color-grey-3;\n// }\n// }\n// }\n// }\n.biu-countdown{\n display: inline-block;\n .biu-countdown__item{\n display: inline-flex;\n align-items: center;\n .biu-countdown__time-box{\n color: #333;\n display: inline-block;\n background-color: #fff;\n position: relative;\n .biu-countdown__time{\n \n }\n }\n .biu-countdown__separator{\n display: inline-flex;\n text-align: justify;\n align-items: center;\n }\n }\n}\n\n' + }, + 'components/Tag/index.tsx': { + import: './components/Tag', + content: + "import React from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\n/**\n * 标签组件\n * @param {closable} boolean 是否可关闭\n * @param {onClose} func 标签关闭时的回调\n * @param {color} string 标签的颜色,不设置则为默认颜色\n */\nfunction Tag(props) {\n let { children, closable, onClose, color } = props;\n let tag = React.createRef();\n let handleClose = () => {\n onClose && onClose();\n tag.current.style.display = 'none';\n };\n return (\n \n {children}\n {closable && (\n \n x\n \n )}\n \n );\n}\n\nexport default Tag;\n" + }, + 'components/Curtain/index.tsx': { + import: './components/Curtain', + content: + "import React from 'react';\n\nimport './index.less';\n\ninterface IProps {\n visible?: boolean;\n title?: string;\n onClose?: () => void;\n closeBtnPosition?:\n | 'top'\n | 'top-left'\n | 'top-right'\n | 'bottom'\n | 'bottom-left'\n | 'bottom-right'\n | 'none'\n | undefined;\n}\n\ninterface IState {\n _visible?: boolean;\n}\nclass Index extends React.Component {\n public constructor(props: IProps) {\n super(props);\n const { visible } = props;\n this.state = {\n _visible: visible\n };\n }\n\n public componentWillReceiveProps(nextProps: IProps) {\n const { visible } = nextProps;\n if (visible !== this.state._visible) {\n this.setState({\n _visible: visible\n });\n }\n }\n\n private handleClose = () => {\n if (typeof this.props.onClose === 'function') {\n this.props.onClose();\n }\n };\n\n private close = () => {\n this.setState(\n {\n _visible: false\n },\n this.handleClose\n );\n };\n\n render(): JSX.Element | null {\n const { _visible } = this.state;\n const { visible, title, closeBtnPosition = 'bottom' } = this.props;\n if (!visible) return null;\n return (\n \n
\n
\n {this.props.children}\n \n
\n
\n );\n }\n}\n\nexport default Index;\n" + }, + 'components/ArrowButton/index.tsx': { + import: './components/ArrowButton', + content: + "import React, { Component } from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching';\ninterface IProps {\n type: type;\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n );\n}\n\nexport default ArrowButton;\n" + }, + 'components/Countdown/index.tsx': { + import: './components/Countdown', + content: + "import React, { Component } from 'react'\nimport classNames from 'classnames'\nimport PropTypes from 'prop-types'\nimport { AtCountDownProps, AtCountdownState } from '@/types/countdown'\nimport AtCountdownItem from './Com'\nimport './index.less'\n\nconst toSeconds = (\n day: number,\n hours: number,\n minutes: number,\n seconds: number): number => day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds\n\n\n\nclass AtCountdown extends Component {\n public static defaultProps: AtCountDownProps\n\n private seconds: number\n private timer: NodeJS.Timeout | number | undefined\n\n public constructor(props: AtCountDownProps) {\n super(props);\n const { day, hours, minutes, seconds } = this.props\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n\n console.log('2232323', this.seconds);\n\n const {\n day: _day,\n hours: _hours,\n minutes: _minutes,\n seconds: _seconds\n } = this.calculateTime();\n this.state = {\n _day,\n _hours,\n _minutes,\n _seconds\n }\n this.timer = undefined\n }\n\n private setTimer(): void {\n if (!this.timer) this.countdonwn()\n }\n\n private clearTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer as number)\n this.timer = undefined\n }\n }\n\n private calculateTime() {\n let [day, hours, minutes, seconds] = [0, 0, 0, 0]\n\n // console.log('this.seconds', this.seconds);\n\n\n if (this.seconds > 0) {\n day = this.props.isShowDay ? Math.floor(this.seconds / (60 * 60 * 24)) : 0\n hours = Math.floor(this.seconds / (60 * 60)) - day * 24\n minutes = Math.floor(this.seconds / 60) - day * 24 * 60 - hours * 60\n seconds =\n Math.floor(this.seconds) -\n day * 24 * 60 * 60 -\n hours * 60 * 60 -\n minutes * 60\n }\n // console.log('hours', hours)\n return {\n day,\n hours,\n minutes,\n seconds\n }\n }\n\n // 倒计时\n private countdonwn(): void {\n // console.log('123')\n const { day, hours, minutes, seconds } = this.calculateTime()\n\n this.setState({\n _day: day,\n _hours: hours,\n _minutes: minutes,\n _seconds: seconds\n })\n this.seconds--\n\n if (this.seconds < 0) {\n this.clearTimer()\n this.props.onTimeUp && this.props.onTimeUp()\n return\n }\n\n this.timer = setTimeout(() => {\n this.countdonwn()\n }, 1000)\n }\n\n public componentWillReceiveProps(nextProps: AtCountDownProps): void {\n if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return\n\n const { day, hours, minutes, seconds } = nextProps\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n this.clearTimer()\n this.setTimer()\n }\n\n public componentDidMount(): void {\n this.setTimer()\n }\n\n public componentWillUnmount(): void {\n this.clearTimer()\n }\n\n\n\n\n render(): JSX.Element {\n\n\n const {\n className,\n customStyle,\n format, // day: '天',hours: '时',minutes: '分',seconds: '秒'\n isShowDay, //\t是否显示天数\n isShowHour, //是否显示小时\n } = this.props\n\n const { _day, _hours, _minutes, _seconds } = this.state\n\n return (\n \n {isShowDay && }\n {isShowHour && ()}\n \n \n \n )\n }\n}\nAtCountdown.defaultProps = {\n customStyle: '',\n className: '',\n isShowDay: false,\n isShowHour: true,\n format: {\n day: '天',\n hours: '时',\n minutes: '分',\n seconds: '秒'\n },\n day: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n onTimeUp() {}\n}\n\n\nexport default AtCountdown\n" + }, + 'Com/index.tsx': { + import: './Com', + content: + "import React, { Component } from 'react'\nimport PropTypes, { InferProps } from 'prop-types'\nimport { AtCountdownItemProps } from '@/types/countdown'\n\n\n\nclass AtCountdownItem extends Component {\n public static defaultProps: AtCountdownItemProps\n public static propTypes: InferProps\n\n private formatNum(num: number): string {\n return num <= 9 ? `0${num}` : `${num}`\n }\n\n public render(): JSX.Element {\n const { num, separator } = this.props\n\n return (\n
\n
\n {this.formatNum(num)}\n
\n {separator}\n
\n )\n }\n}\n\nexport default AtCountdownItem;\n\nAtCountdownItem.defaultProps = {\n num: 0,\n separator: ':'\n}\n\nAtCountdownItem.propTypes = {\n num: PropTypes.number.isRequired,\n separator: PropTypes.string\n}\n" + } + }, + dependencies: { + react: { version: '17.0.1' }, + classnames: { version: '2.2.6' }, + 'prop-types': { version: '15.7.2' } + }, + identifier: 'biuui-arrowbutton' + } + }, 'biuui-button': { component: require('/Users/shide/PROJ/biuUI/demo/Button.tsx').default, previewerProps: { sources: { _: { tsx: - 'import React from \'react\';\nimport { Button, FloatLayout } from \'../src\';\n/**\n * title: Basic Modal\n * title.zh-CN: 基础 Modal\n * desc: This is a basic example of the antd Modal component\n * desc.zh-CN: 这是 antd Modal 组件的基础示例\n */\n\nfunction ButtonDemo() {\n return (\n
\n 默认:\n
\n
\n
\n   \n   \n   \n   \n   \n
\n
\n
PC 悬浮效果
\n
\n
\n \n \n
\n
\n );\n}\n\nexport default ButtonDemo;\n' + 'import React from \'react\';\nimport { Button } from \'../src\';\n/**\n * title: Basic Modal\n * title.zh-CN: 基础 Modal\n * desc: This is a basic example of the antd Modal component\n * desc.zh-CN: 这是 antd Modal 组件的基础示例\n */\n\nfunction ButtonDemo() {\n return (\n
\n 默认:\n
\n
\n
\n   \n   \n   \n   \n   \n
\n
\n
PC 悬浮效果
\n
\n
\n \n \n
\n
\n );\n}\n\nexport default ButtonDemo;\n' }, 'src/index.ts': { import: '../src', content: - "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\n\n" + "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\nexport { default as Countdown } from './components/Countdown';\n" }, 'components/Button/index.tsx': { import: './components/Button', @@ -24,7 +81,7 @@ export default { 'index.less': { import: './index.less', content: - '.btn-wrap {\n margin: auto;\n & > div {\n filter: url(#outline);\n }\n}\n\n.btn {\n content: "";\n width: 200px;\n height: 64px;\n line-height: 64px;\n text-align: center;\n background: linear-gradient(#f49714, #fbe8c8, #f49714);\n color: #be9451;\n font-size: 24px;\n}\n\n.arc {\n width: 200px;\n height: 64px;\n background: radial-gradient(circle at top left, transparent 15px, #f49714 0)\n top left,\n radial-gradient(circle at top right, transparent 15px, #f49714 0) top\n right,\n radial-gradient(circle at bottom right, transparent 15px, #f49714 0)\n bottom right,\n radial-gradient(circle at bottom left, transparent 15px, #f49714 0)\n bottom left;\n background-size: 50% 50%;\n background-repeat: no-repeat;\n // filter: url(#outline);\n}\n\n.notching {\n width: 200px;\n height: 64px;\n background: linear-gradient(135deg, transparent 15px, #f49714 0) top left,\n linear-gradient(-135deg, transparent 15px, #f49714 0) top right,\n linear-gradient(-45deg, transparent 15px, #f49714 0) bottom right,\n linear-gradient(45deg, transparent 15px, #f49714 0) bottom left;\n background-size: 50% 50%;\n background-repeat: no-repeat;\n // filter: url(#outline);\n}\n\n.arrow {\n position: relative;\n width: 180px;\n height: 64px;\n background: #f49714;\n // filter: url(#outline);\n\n &::after {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n right: -32px;\n background: \n linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%),\n linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%);\n background-size: 32px 32px;\n background-repeat: no-repeat;\n background-position: 0 bottom, 0 top;\n }\n}\n\n.arrow2 {\n position: relative;\n width: 150px;\n height: 64px;\n background: #f49714;\n // filter: url(#outline);\n\n &::before {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n left: -32px;\n background: #000;\n background: \n linear-gradient(-45deg, #f49714 0, #f49714 24px, transparent 24px, transparent),\n linear-gradient(-135deg, #f49714 0, #f49714 24px, transparent 24px, transparent);\n }\n \n &::after {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n right: -32px;\n background: #000;\n background: \n linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 1px),\n linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 1px);\n background-size: 32px 32px;\n background-repeat: no-repeat;\n background-position: 0 bottom, 0 top;\n }\n}\n\n.coupon {\n position: relative;\n width: 200px;\n height: 64px;\n background-image: \n radial-gradient(circle at 1px 11px, transparent 8px, #f49714 8px, #f49714 0px),\n radial-gradient(circle at 99px 11px, transparent 8px, #f49714 8px, #f49714 0px);\n background-size: 100px 21px;\n background-position: 0 0, 100px 0;\n background-repeat-x: no-repeat;\n cursor: pointer;\n}\n\n\n\n' + '// @import "../../style/theme/default.scss";\n// @import "../../style/mixins/index.scss";\n\n\n\n\n// $font-size: $font-size-lg;\n\n// .at-count-down {\n// display: inline-block;\n// min-height: $font-size;\n\n// &__item {\n// display: inline-flex;\n// align-items: center;\n// }\n\n// &__time-box {\n// display: inline-block;\n// text-align: center;\n// min-width: $font-size;\n// font: {\n// size: $font-size;\n// family: countDownFont;\n// style: normal;\n// weight: 400;\n// variant: normal;\n// }\n\n// text-transform: none;\n// text-rendering: auto;\n// line-height: 1;\n// -webkit-font-smoothing: antialiased;\n// vertical-align: middle;\n// }\n\n// &__separator {\n// font-size: $font-size-base;\n// display: inline-flex;\n// align-items: center;\n// text-align: justify;\n// padding: 0 $spacing-v-xs;\n// }\n\n// &--card {\n// .at-count-down__time-box {\n// padding: $spacing-v-xs 0;\n// border: 1PX solid $color-border-grey;\n// border-radius: $border-radius-md;\n// color: #FF4949;\n// display: inline-block;\n// background-color: #fff;\n// position: relative;\n\n// .at-count-down__time {\n// position: relative;\n// z-index: $zindex-divider + 1;\n// }\n\n// &::after {\n// position: absolute;\n// content: \'\';\n// width: 100%;\n// height: 1PX;\n// top: 50%;\n// left: 0;\n// z-index: $zindex-divider;\n// background-color: $color-grey-3;\n// }\n// }\n// }\n// }\n.biu-countdown{\n display: inline-block;\n .biu-countdown__item{\n display: inline-flex;\n align-items: center;\n .biu-countdown__time-box{\n color: #333;\n display: inline-block;\n background-color: #fff;\n position: relative;\n .biu-countdown__time{\n \n }\n }\n .biu-countdown__separator{\n display: inline-flex;\n text-align: justify;\n align-items: center;\n }\n }\n}\n\n' }, 'components/Tag/index.tsx': { import: './components/Tag', @@ -39,28 +96,39 @@ export default { 'components/ArrowButton/index.tsx': { import: './components/ArrowButton', content: - "import React, { Component } from 'react'\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching'\ninterface IProps {\n type: type\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n )\n}\n\nexport default ArrowButton;\n" + "import React, { Component } from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching';\ninterface IProps {\n type: type;\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n );\n}\n\nexport default ArrowButton;\n" + }, + 'components/Countdown/index.tsx': { + import: './components/Countdown', + content: + "import React, { Component } from 'react'\nimport classNames from 'classnames'\nimport PropTypes from 'prop-types'\nimport { AtCountDownProps, AtCountdownState } from '@/types/countdown'\nimport AtCountdownItem from './Com'\nimport './index.less'\n\nconst toSeconds = (\n day: number,\n hours: number,\n minutes: number,\n seconds: number): number => day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds\n\n\n\nclass AtCountdown extends Component {\n public static defaultProps: AtCountDownProps\n\n private seconds: number\n private timer: NodeJS.Timeout | number | undefined\n\n public constructor(props: AtCountDownProps) {\n super(props);\n const { day, hours, minutes, seconds } = this.props\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n\n console.log('2232323', this.seconds);\n\n const {\n day: _day,\n hours: _hours,\n minutes: _minutes,\n seconds: _seconds\n } = this.calculateTime();\n this.state = {\n _day,\n _hours,\n _minutes,\n _seconds\n }\n this.timer = undefined\n }\n\n private setTimer(): void {\n if (!this.timer) this.countdonwn()\n }\n\n private clearTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer as number)\n this.timer = undefined\n }\n }\n\n private calculateTime() {\n let [day, hours, minutes, seconds] = [0, 0, 0, 0]\n\n // console.log('this.seconds', this.seconds);\n\n\n if (this.seconds > 0) {\n day = this.props.isShowDay ? Math.floor(this.seconds / (60 * 60 * 24)) : 0\n hours = Math.floor(this.seconds / (60 * 60)) - day * 24\n minutes = Math.floor(this.seconds / 60) - day * 24 * 60 - hours * 60\n seconds =\n Math.floor(this.seconds) -\n day * 24 * 60 * 60 -\n hours * 60 * 60 -\n minutes * 60\n }\n // console.log('hours', hours)\n return {\n day,\n hours,\n minutes,\n seconds\n }\n }\n\n // 倒计时\n private countdonwn(): void {\n // console.log('123')\n const { day, hours, minutes, seconds } = this.calculateTime()\n\n this.setState({\n _day: day,\n _hours: hours,\n _minutes: minutes,\n _seconds: seconds\n })\n this.seconds--\n\n if (this.seconds < 0) {\n this.clearTimer()\n this.props.onTimeUp && this.props.onTimeUp()\n return\n }\n\n this.timer = setTimeout(() => {\n this.countdonwn()\n }, 1000)\n }\n\n public componentWillReceiveProps(nextProps: AtCountDownProps): void {\n if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return\n\n const { day, hours, minutes, seconds } = nextProps\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n this.clearTimer()\n this.setTimer()\n }\n\n public componentDidMount(): void {\n this.setTimer()\n }\n\n public componentWillUnmount(): void {\n this.clearTimer()\n }\n\n\n\n\n render(): JSX.Element {\n\n\n const {\n className,\n customStyle,\n format, // day: '天',hours: '时',minutes: '分',seconds: '秒'\n isShowDay, //\t是否显示天数\n isShowHour, //是否显示小时\n } = this.props\n\n const { _day, _hours, _minutes, _seconds } = this.state\n\n return (\n \n {isShowDay && }\n {isShowHour && ()}\n \n \n \n )\n }\n}\nAtCountdown.defaultProps = {\n customStyle: '',\n className: '',\n isShowDay: false,\n isShowHour: true,\n format: {\n day: '天',\n hours: '时',\n minutes: '分',\n seconds: '秒'\n },\n day: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n onTimeUp() {}\n}\n\n\nexport default AtCountdown\n" + }, + 'Com/index.tsx': { + import: './Com', + content: + "import React, { Component } from 'react'\nimport PropTypes, { InferProps } from 'prop-types'\nimport { AtCountdownItemProps } from '@/types/countdown'\n\n\n\nclass AtCountdownItem extends Component {\n public static defaultProps: AtCountdownItemProps\n public static propTypes: InferProps\n\n private formatNum(num: number): string {\n return num <= 9 ? `0${num}` : `${num}`\n }\n\n public render(): JSX.Element {\n const { num, separator } = this.props\n\n return (\n
\n
\n {this.formatNum(num)}\n
\n {separator}\n
\n )\n }\n}\n\nexport default AtCountdownItem;\n\nAtCountdownItem.defaultProps = {\n num: 0,\n separator: ':'\n}\n\nAtCountdownItem.propTypes = {\n num: PropTypes.number.isRequired,\n separator: PropTypes.string\n}\n" } }, dependencies: { react: { version: '17.0.1' }, - classnames: { version: '2.2.6' } + classnames: { version: '2.2.6' }, + 'prop-types': { version: '15.7.2' } }, identifier: 'biuui-button' } }, - 'biuui-curtain': { - component: require('/Users/shide/PROJ/biuUI/demo/Curtain.tsx').default, + 'biuui-countdown': { + component: require('/Users/shide/PROJ/biuUI/demo/Countdown.tsx').default, previewerProps: { sources: { _: { tsx: - "import React, { useState } from 'react';\nimport { Curtain } from '../src';\nimport './index.less';\n\nconst Index = () => {\n const [isModalVisible, setIsModalVisible] = useState(true);\n const [closeBtnPosition, setCloseBtnPosition] = useState('bottom');\n\n return (\n
\n {\n setIsModalVisible(true);\n }}\n >\n 点击幕帘\n
\n\n {\n setIsModalVisible(false);\n }}\n closeBtnPosition=\"bottom\"\n >\n
这是内容这是内容这是内容这是内容
\n \n \n );\n};\n\nexport default Index;\n" + "import React from 'react';\nimport { Countdown } from '../src';\nimport './index.less'\n\nfunction CountdownDemo() {\n return (\n
\n




\n
\n
默认格式:时 : 分 : 秒
\n \n
显示天数
\n \n
\n
\n
自定义格式: 天 : hh : mm : ss
\n \n
\n
\n
\t倒计时时间到,执行的回调函数
\n { console.log('biu~') }}\n />\n
\n
\n );\n}\nexport default CountdownDemo;" }, 'src/index.ts': { import: '../src', content: - "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\n\n" + "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\nexport { default as Countdown } from './components/Countdown';\n" }, 'components/Button/index.tsx': { import: './components/Button', @@ -70,7 +138,7 @@ export default { 'index.less': { import: './index.less', content: - '.page-layout {\n padding-top: 30px;\n .layout {\n width: 200px;\n height: 200px;\n background-color: #fff;\n border-radius: 10px;\n }\n}\n' + '.page-layout {\n padding-top: 30px;\n .layout {\n width: 200px;\n height: 200px;\n background-color: #fff;\n border-radius: 10px;\n }\n}\n*{\n margin: 0;\n padding: 0;\n}' }, 'components/Tag/index.tsx': { import: './components/Tag', @@ -85,28 +153,39 @@ export default { 'components/ArrowButton/index.tsx': { import: './components/ArrowButton', content: - "import React, { Component } from 'react'\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching'\ninterface IProps {\n type: type\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n )\n}\n\nexport default ArrowButton;\n" + "import React, { Component } from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching';\ninterface IProps {\n type: type;\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n );\n}\n\nexport default ArrowButton;\n" + }, + 'components/Countdown/index.tsx': { + import: './components/Countdown', + content: + "import React, { Component } from 'react'\nimport classNames from 'classnames'\nimport PropTypes from 'prop-types'\nimport { AtCountDownProps, AtCountdownState } from '@/types/countdown'\nimport AtCountdownItem from './Com'\nimport './index.less'\n\nconst toSeconds = (\n day: number,\n hours: number,\n minutes: number,\n seconds: number): number => day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds\n\n\n\nclass AtCountdown extends Component {\n public static defaultProps: AtCountDownProps\n\n private seconds: number\n private timer: NodeJS.Timeout | number | undefined\n\n public constructor(props: AtCountDownProps) {\n super(props);\n const { day, hours, minutes, seconds } = this.props\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n\n console.log('2232323', this.seconds);\n\n const {\n day: _day,\n hours: _hours,\n minutes: _minutes,\n seconds: _seconds\n } = this.calculateTime();\n this.state = {\n _day,\n _hours,\n _minutes,\n _seconds\n }\n this.timer = undefined\n }\n\n private setTimer(): void {\n if (!this.timer) this.countdonwn()\n }\n\n private clearTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer as number)\n this.timer = undefined\n }\n }\n\n private calculateTime() {\n let [day, hours, minutes, seconds] = [0, 0, 0, 0]\n\n // console.log('this.seconds', this.seconds);\n\n\n if (this.seconds > 0) {\n day = this.props.isShowDay ? Math.floor(this.seconds / (60 * 60 * 24)) : 0\n hours = Math.floor(this.seconds / (60 * 60)) - day * 24\n minutes = Math.floor(this.seconds / 60) - day * 24 * 60 - hours * 60\n seconds =\n Math.floor(this.seconds) -\n day * 24 * 60 * 60 -\n hours * 60 * 60 -\n minutes * 60\n }\n // console.log('hours', hours)\n return {\n day,\n hours,\n minutes,\n seconds\n }\n }\n\n // 倒计时\n private countdonwn(): void {\n // console.log('123')\n const { day, hours, minutes, seconds } = this.calculateTime()\n\n this.setState({\n _day: day,\n _hours: hours,\n _minutes: minutes,\n _seconds: seconds\n })\n this.seconds--\n\n if (this.seconds < 0) {\n this.clearTimer()\n this.props.onTimeUp && this.props.onTimeUp()\n return\n }\n\n this.timer = setTimeout(() => {\n this.countdonwn()\n }, 1000)\n }\n\n public componentWillReceiveProps(nextProps: AtCountDownProps): void {\n if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return\n\n const { day, hours, minutes, seconds } = nextProps\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n this.clearTimer()\n this.setTimer()\n }\n\n public componentDidMount(): void {\n this.setTimer()\n }\n\n public componentWillUnmount(): void {\n this.clearTimer()\n }\n\n\n\n\n render(): JSX.Element {\n\n\n const {\n className,\n customStyle,\n format, // day: '天',hours: '时',minutes: '分',seconds: '秒'\n isShowDay, //\t是否显示天数\n isShowHour, //是否显示小时\n } = this.props\n\n const { _day, _hours, _minutes, _seconds } = this.state\n\n return (\n \n {isShowDay && }\n {isShowHour && ()}\n \n \n \n )\n }\n}\nAtCountdown.defaultProps = {\n customStyle: '',\n className: '',\n isShowDay: false,\n isShowHour: true,\n format: {\n day: '天',\n hours: '时',\n minutes: '分',\n seconds: '秒'\n },\n day: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n onTimeUp() {}\n}\n\n\nexport default AtCountdown\n" + }, + 'Com/index.tsx': { + import: './Com', + content: + "import React, { Component } from 'react'\nimport PropTypes, { InferProps } from 'prop-types'\nimport { AtCountdownItemProps } from '@/types/countdown'\n\n\n\nclass AtCountdownItem extends Component {\n public static defaultProps: AtCountdownItemProps\n public static propTypes: InferProps\n\n private formatNum(num: number): string {\n return num <= 9 ? `0${num}` : `${num}`\n }\n\n public render(): JSX.Element {\n const { num, separator } = this.props\n\n return (\n
\n
\n {this.formatNum(num)}\n
\n {separator}\n
\n )\n }\n}\n\nexport default AtCountdownItem;\n\nAtCountdownItem.defaultProps = {\n num: 0,\n separator: ':'\n}\n\nAtCountdownItem.propTypes = {\n num: PropTypes.number.isRequired,\n separator: PropTypes.string\n}\n" } }, dependencies: { react: { version: '17.0.1' }, - classnames: { version: '2.2.6' } + classnames: { version: '2.2.6' }, + 'prop-types': { version: '15.7.2' } }, - identifier: 'biuui-curtain' + identifier: 'biuui-countdown' } }, - 'biuui-tag': { - component: require('/Users/shide/PROJ/biuUI/demo/Tag.tsx').default, + 'biuui-curtain': { + component: require('/Users/shide/PROJ/biuUI/demo/Curtain.tsx').default, previewerProps: { sources: { _: { tsx: - 'import React from \'react\';\nimport { Tag, ArrowButton } from \'../src\';\n\n\nfunction TagDemo() {\n return (\n
\n 标签\n 标签\n 标签\n 标签
\n 标签\n 标签\n
\n );\n}\n\nexport default TagDemo\n\n\n' + "import React, { useState } from 'react';\nimport { Curtain } from '../src';\nimport './index.less';\n\nconst Index = () => {\n const [isModalVisible, setIsModalVisible] = useState(true);\n const [closeBtnPosition, setCloseBtnPosition] = useState('bottom');\n\n return (\n
\n {\n setIsModalVisible(true);\n }}\n >\n 点击幕帘\n
\n\n {\n setIsModalVisible(false);\n }}\n closeBtnPosition=\"bottom\"\n >\n
这是内容这是内容这是内容这是内容
\n \n \n );\n};\n\nexport default Index;\n" }, 'src/index.ts': { import: '../src', content: - "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\n\n" + "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\nexport { default as Countdown } from './components/Countdown';\n" }, 'components/Button/index.tsx': { import: './components/Button', @@ -116,7 +195,7 @@ export default { 'index.less': { import: './index.less', content: - '.btn-wrap {\n margin: auto;\n & > div {\n filter: url(#outline);\n }\n}\n\n.btn {\n content: "";\n width: 200px;\n height: 64px;\n line-height: 64px;\n text-align: center;\n background: linear-gradient(#f49714, #fbe8c8, #f49714);\n color: #be9451;\n font-size: 24px;\n}\n\n.arc {\n width: 200px;\n height: 64px;\n background: radial-gradient(circle at top left, transparent 15px, #f49714 0)\n top left,\n radial-gradient(circle at top right, transparent 15px, #f49714 0) top\n right,\n radial-gradient(circle at bottom right, transparent 15px, #f49714 0)\n bottom right,\n radial-gradient(circle at bottom left, transparent 15px, #f49714 0)\n bottom left;\n background-size: 50% 50%;\n background-repeat: no-repeat;\n // filter: url(#outline);\n}\n\n.notching {\n width: 200px;\n height: 64px;\n background: linear-gradient(135deg, transparent 15px, #f49714 0) top left,\n linear-gradient(-135deg, transparent 15px, #f49714 0) top right,\n linear-gradient(-45deg, transparent 15px, #f49714 0) bottom right,\n linear-gradient(45deg, transparent 15px, #f49714 0) bottom left;\n background-size: 50% 50%;\n background-repeat: no-repeat;\n // filter: url(#outline);\n}\n\n.arrow {\n position: relative;\n width: 180px;\n height: 64px;\n background: #f49714;\n // filter: url(#outline);\n\n &::after {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n right: -32px;\n background: \n linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%),\n linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%);\n background-size: 32px 32px;\n background-repeat: no-repeat;\n background-position: 0 bottom, 0 top;\n }\n}\n\n.arrow2 {\n position: relative;\n width: 150px;\n height: 64px;\n background: #f49714;\n // filter: url(#outline);\n\n &::before {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n left: -32px;\n background: #000;\n background: \n linear-gradient(-45deg, #f49714 0, #f49714 24px, transparent 24px, transparent),\n linear-gradient(-135deg, #f49714 0, #f49714 24px, transparent 24px, transparent);\n }\n \n &::after {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n right: -32px;\n background: #000;\n background: \n linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 1px),\n linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 1px);\n background-size: 32px 32px;\n background-repeat: no-repeat;\n background-position: 0 bottom, 0 top;\n }\n}\n\n.coupon {\n position: relative;\n width: 200px;\n height: 64px;\n background-image: \n radial-gradient(circle at 1px 11px, transparent 8px, #f49714 8px, #f49714 0px),\n radial-gradient(circle at 99px 11px, transparent 8px, #f49714 8px, #f49714 0px);\n background-size: 100px 21px;\n background-position: 0 0, 100px 0;\n background-repeat-x: no-repeat;\n cursor: pointer;\n}\n\n\n\n' + '.page-layout {\n padding-top: 30px;\n .layout {\n width: 200px;\n height: 200px;\n background-color: #fff;\n border-radius: 10px;\n }\n}\n*{\n margin: 0;\n padding: 0;\n}' }, 'components/Tag/index.tsx': { import: './components/Tag', @@ -131,28 +210,39 @@ export default { 'components/ArrowButton/index.tsx': { import: './components/ArrowButton', content: - "import React, { Component } from 'react'\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching'\ninterface IProps {\n type: type\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n )\n}\n\nexport default ArrowButton;\n" + "import React, { Component } from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching';\ninterface IProps {\n type: type;\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n );\n}\n\nexport default ArrowButton;\n" + }, + 'components/Countdown/index.tsx': { + import: './components/Countdown', + content: + "import React, { Component } from 'react'\nimport classNames from 'classnames'\nimport PropTypes from 'prop-types'\nimport { AtCountDownProps, AtCountdownState } from '@/types/countdown'\nimport AtCountdownItem from './Com'\nimport './index.less'\n\nconst toSeconds = (\n day: number,\n hours: number,\n minutes: number,\n seconds: number): number => day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds\n\n\n\nclass AtCountdown extends Component {\n public static defaultProps: AtCountDownProps\n\n private seconds: number\n private timer: NodeJS.Timeout | number | undefined\n\n public constructor(props: AtCountDownProps) {\n super(props);\n const { day, hours, minutes, seconds } = this.props\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n\n console.log('2232323', this.seconds);\n\n const {\n day: _day,\n hours: _hours,\n minutes: _minutes,\n seconds: _seconds\n } = this.calculateTime();\n this.state = {\n _day,\n _hours,\n _minutes,\n _seconds\n }\n this.timer = undefined\n }\n\n private setTimer(): void {\n if (!this.timer) this.countdonwn()\n }\n\n private clearTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer as number)\n this.timer = undefined\n }\n }\n\n private calculateTime() {\n let [day, hours, minutes, seconds] = [0, 0, 0, 0]\n\n // console.log('this.seconds', this.seconds);\n\n\n if (this.seconds > 0) {\n day = this.props.isShowDay ? Math.floor(this.seconds / (60 * 60 * 24)) : 0\n hours = Math.floor(this.seconds / (60 * 60)) - day * 24\n minutes = Math.floor(this.seconds / 60) - day * 24 * 60 - hours * 60\n seconds =\n Math.floor(this.seconds) -\n day * 24 * 60 * 60 -\n hours * 60 * 60 -\n minutes * 60\n }\n // console.log('hours', hours)\n return {\n day,\n hours,\n minutes,\n seconds\n }\n }\n\n // 倒计时\n private countdonwn(): void {\n // console.log('123')\n const { day, hours, minutes, seconds } = this.calculateTime()\n\n this.setState({\n _day: day,\n _hours: hours,\n _minutes: minutes,\n _seconds: seconds\n })\n this.seconds--\n\n if (this.seconds < 0) {\n this.clearTimer()\n this.props.onTimeUp && this.props.onTimeUp()\n return\n }\n\n this.timer = setTimeout(() => {\n this.countdonwn()\n }, 1000)\n }\n\n public componentWillReceiveProps(nextProps: AtCountDownProps): void {\n if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return\n\n const { day, hours, minutes, seconds } = nextProps\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n this.clearTimer()\n this.setTimer()\n }\n\n public componentDidMount(): void {\n this.setTimer()\n }\n\n public componentWillUnmount(): void {\n this.clearTimer()\n }\n\n\n\n\n render(): JSX.Element {\n\n\n const {\n className,\n customStyle,\n format, // day: '天',hours: '时',minutes: '分',seconds: '秒'\n isShowDay, //\t是否显示天数\n isShowHour, //是否显示小时\n } = this.props\n\n const { _day, _hours, _minutes, _seconds } = this.state\n\n return (\n \n {isShowDay && }\n {isShowHour && ()}\n \n \n \n )\n }\n}\nAtCountdown.defaultProps = {\n customStyle: '',\n className: '',\n isShowDay: false,\n isShowHour: true,\n format: {\n day: '天',\n hours: '时',\n minutes: '分',\n seconds: '秒'\n },\n day: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n onTimeUp() {}\n}\n\n\nexport default AtCountdown\n" + }, + 'Com/index.tsx': { + import: './Com', + content: + "import React, { Component } from 'react'\nimport PropTypes, { InferProps } from 'prop-types'\nimport { AtCountdownItemProps } from '@/types/countdown'\n\n\n\nclass AtCountdownItem extends Component {\n public static defaultProps: AtCountdownItemProps\n public static propTypes: InferProps\n\n private formatNum(num: number): string {\n return num <= 9 ? `0${num}` : `${num}`\n }\n\n public render(): JSX.Element {\n const { num, separator } = this.props\n\n return (\n
\n
\n {this.formatNum(num)}\n
\n {separator}\n
\n )\n }\n}\n\nexport default AtCountdownItem;\n\nAtCountdownItem.defaultProps = {\n num: 0,\n separator: ':'\n}\n\nAtCountdownItem.propTypes = {\n num: PropTypes.number.isRequired,\n separator: PropTypes.string\n}\n" } }, dependencies: { react: { version: '17.0.1' }, - classnames: { version: '2.2.6' } + classnames: { version: '2.2.6' }, + 'prop-types': { version: '15.7.2' } }, - identifier: 'biuui-tag' + identifier: 'biuui-curtain' } }, - 'biuui-arrowbutton': { - component: require('/Users/shide/PROJ/biuUI/demo/ArrowButton.tsx').default, + 'biuui-tag': { + component: require('/Users/shide/PROJ/biuUI/demo/Tag.tsx').default, previewerProps: { sources: { _: { tsx: - "import React from 'react';\nimport { ArrowButton } from '../src';\n\n\nfunction ArrowButtonDemo() {\n return (\n
\n \n
\n \n
\n \n
\n \n
\n \n
\n );\n}\nexport default ArrowButtonDemo" + 'import React from \'react\';\nimport { Tag, ArrowButton } from \'../src\';\n\nfunction TagDemo() {\n return (\n
\n 标签\n 标签\n 标签\n 标签\n
\n \n 标签\n \n 标签\n
\n );\n}\n\nexport default TagDemo;\n' }, 'src/index.ts': { import: '../src', content: - "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\n\n" + "export { default as Button } from './components/Button';\nexport { default as Tag } from './components/Tag';\nexport { default as Curtain } from './components/Curtain';\nexport { default as ArrowButton } from './components/ArrowButton';\nexport { default as Countdown } from './components/Countdown';\n" }, 'components/Button/index.tsx': { import: './components/Button', @@ -162,7 +252,7 @@ export default { 'index.less': { import: './index.less', content: - '.btn-wrap {\n margin: auto;\n & > div {\n filter: url(#outline);\n }\n}\n\n.btn {\n content: "";\n width: 200px;\n height: 64px;\n line-height: 64px;\n text-align: center;\n background: linear-gradient(#f49714, #fbe8c8, #f49714);\n color: #be9451;\n font-size: 24px;\n}\n\n.arc {\n width: 200px;\n height: 64px;\n background: radial-gradient(circle at top left, transparent 15px, #f49714 0)\n top left,\n radial-gradient(circle at top right, transparent 15px, #f49714 0) top\n right,\n radial-gradient(circle at bottom right, transparent 15px, #f49714 0)\n bottom right,\n radial-gradient(circle at bottom left, transparent 15px, #f49714 0)\n bottom left;\n background-size: 50% 50%;\n background-repeat: no-repeat;\n // filter: url(#outline);\n}\n\n.notching {\n width: 200px;\n height: 64px;\n background: linear-gradient(135deg, transparent 15px, #f49714 0) top left,\n linear-gradient(-135deg, transparent 15px, #f49714 0) top right,\n linear-gradient(-45deg, transparent 15px, #f49714 0) bottom right,\n linear-gradient(45deg, transparent 15px, #f49714 0) bottom left;\n background-size: 50% 50%;\n background-repeat: no-repeat;\n // filter: url(#outline);\n}\n\n.arrow {\n position: relative;\n width: 180px;\n height: 64px;\n background: #f49714;\n // filter: url(#outline);\n\n &::after {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n right: -32px;\n background: \n linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%),\n linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%);\n background-size: 32px 32px;\n background-repeat: no-repeat;\n background-position: 0 bottom, 0 top;\n }\n}\n\n.arrow2 {\n position: relative;\n width: 150px;\n height: 64px;\n background: #f49714;\n // filter: url(#outline);\n\n &::before {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n left: -32px;\n background: #000;\n background: \n linear-gradient(-45deg, #f49714 0, #f49714 24px, transparent 24px, transparent),\n linear-gradient(-135deg, #f49714 0, #f49714 24px, transparent 24px, transparent);\n }\n \n &::after {\n content: "";\n position: absolute;\n width: 32px;\n height: 64px;\n top: 0;\n right: -32px;\n background: #000;\n background: \n linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 1px),\n linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 1px);\n background-size: 32px 32px;\n background-repeat: no-repeat;\n background-position: 0 bottom, 0 top;\n }\n}\n\n.coupon {\n position: relative;\n width: 200px;\n height: 64px;\n background-image: \n radial-gradient(circle at 1px 11px, transparent 8px, #f49714 8px, #f49714 0px),\n radial-gradient(circle at 99px 11px, transparent 8px, #f49714 8px, #f49714 0px);\n background-size: 100px 21px;\n background-position: 0 0, 100px 0;\n background-repeat-x: no-repeat;\n cursor: pointer;\n}\n\n\n\n' + '// @import "../../style/theme/default.scss";\n// @import "../../style/mixins/index.scss";\n\n\n\n\n// $font-size: $font-size-lg;\n\n// .at-count-down {\n// display: inline-block;\n// min-height: $font-size;\n\n// &__item {\n// display: inline-flex;\n// align-items: center;\n// }\n\n// &__time-box {\n// display: inline-block;\n// text-align: center;\n// min-width: $font-size;\n// font: {\n// size: $font-size;\n// family: countDownFont;\n// style: normal;\n// weight: 400;\n// variant: normal;\n// }\n\n// text-transform: none;\n// text-rendering: auto;\n// line-height: 1;\n// -webkit-font-smoothing: antialiased;\n// vertical-align: middle;\n// }\n\n// &__separator {\n// font-size: $font-size-base;\n// display: inline-flex;\n// align-items: center;\n// text-align: justify;\n// padding: 0 $spacing-v-xs;\n// }\n\n// &--card {\n// .at-count-down__time-box {\n// padding: $spacing-v-xs 0;\n// border: 1PX solid $color-border-grey;\n// border-radius: $border-radius-md;\n// color: #FF4949;\n// display: inline-block;\n// background-color: #fff;\n// position: relative;\n\n// .at-count-down__time {\n// position: relative;\n// z-index: $zindex-divider + 1;\n// }\n\n// &::after {\n// position: absolute;\n// content: \'\';\n// width: 100%;\n// height: 1PX;\n// top: 50%;\n// left: 0;\n// z-index: $zindex-divider;\n// background-color: $color-grey-3;\n// }\n// }\n// }\n// }\n.biu-countdown{\n display: inline-block;\n .biu-countdown__item{\n display: inline-flex;\n align-items: center;\n .biu-countdown__time-box{\n color: #333;\n display: inline-block;\n background-color: #fff;\n position: relative;\n .biu-countdown__time{\n \n }\n }\n .biu-countdown__separator{\n display: inline-flex;\n text-align: justify;\n align-items: center;\n }\n }\n}\n\n' }, 'components/Tag/index.tsx': { import: './components/Tag', @@ -177,14 +267,25 @@ export default { 'components/ArrowButton/index.tsx': { import: './components/ArrowButton', content: - "import React, { Component } from 'react'\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching'\ninterface IProps {\n type: type\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n )\n}\n\nexport default ArrowButton;\n" + "import React, { Component } from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\ntype type = 'arc' | 'notching' | 'arrow' | 'arrow2' | 'coupon' | 'notching';\ninterface IProps {\n type: type;\n}\n\nfunction ArrowButton(props: IProps) {\n const { type = 'arrow' } = props;\n\n return (\n
\n
\n
\n );\n}\n\nexport default ArrowButton;\n" + }, + 'components/Countdown/index.tsx': { + import: './components/Countdown', + content: + "import React, { Component } from 'react'\nimport classNames from 'classnames'\nimport PropTypes from 'prop-types'\nimport { AtCountDownProps, AtCountdownState } from '@/types/countdown'\nimport AtCountdownItem from './Com'\nimport './index.less'\n\nconst toSeconds = (\n day: number,\n hours: number,\n minutes: number,\n seconds: number): number => day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds\n\n\n\nclass AtCountdown extends Component {\n public static defaultProps: AtCountDownProps\n\n private seconds: number\n private timer: NodeJS.Timeout | number | undefined\n\n public constructor(props: AtCountDownProps) {\n super(props);\n const { day, hours, minutes, seconds } = this.props\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n\n console.log('2232323', this.seconds);\n\n const {\n day: _day,\n hours: _hours,\n minutes: _minutes,\n seconds: _seconds\n } = this.calculateTime();\n this.state = {\n _day,\n _hours,\n _minutes,\n _seconds\n }\n this.timer = undefined\n }\n\n private setTimer(): void {\n if (!this.timer) this.countdonwn()\n }\n\n private clearTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer as number)\n this.timer = undefined\n }\n }\n\n private calculateTime() {\n let [day, hours, minutes, seconds] = [0, 0, 0, 0]\n\n // console.log('this.seconds', this.seconds);\n\n\n if (this.seconds > 0) {\n day = this.props.isShowDay ? Math.floor(this.seconds / (60 * 60 * 24)) : 0\n hours = Math.floor(this.seconds / (60 * 60)) - day * 24\n minutes = Math.floor(this.seconds / 60) - day * 24 * 60 - hours * 60\n seconds =\n Math.floor(this.seconds) -\n day * 24 * 60 * 60 -\n hours * 60 * 60 -\n minutes * 60\n }\n // console.log('hours', hours)\n return {\n day,\n hours,\n minutes,\n seconds\n }\n }\n\n // 倒计时\n private countdonwn(): void {\n // console.log('123')\n const { day, hours, minutes, seconds } = this.calculateTime()\n\n this.setState({\n _day: day,\n _hours: hours,\n _minutes: minutes,\n _seconds: seconds\n })\n this.seconds--\n\n if (this.seconds < 0) {\n this.clearTimer()\n this.props.onTimeUp && this.props.onTimeUp()\n return\n }\n\n this.timer = setTimeout(() => {\n this.countdonwn()\n }, 1000)\n }\n\n public componentWillReceiveProps(nextProps: AtCountDownProps): void {\n if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return\n\n const { day, hours, minutes, seconds } = nextProps\n this.seconds = toSeconds(day!, hours!, minutes!, seconds!)\n this.clearTimer()\n this.setTimer()\n }\n\n public componentDidMount(): void {\n this.setTimer()\n }\n\n public componentWillUnmount(): void {\n this.clearTimer()\n }\n\n\n\n\n render(): JSX.Element {\n\n\n const {\n className,\n customStyle,\n format, // day: '天',hours: '时',minutes: '分',seconds: '秒'\n isShowDay, //\t是否显示天数\n isShowHour, //是否显示小时\n } = this.props\n\n const { _day, _hours, _minutes, _seconds } = this.state\n\n return (\n \n {isShowDay && }\n {isShowHour && ()}\n \n \n \n )\n }\n}\nAtCountdown.defaultProps = {\n customStyle: '',\n className: '',\n isShowDay: false,\n isShowHour: true,\n format: {\n day: '天',\n hours: '时',\n minutes: '分',\n seconds: '秒'\n },\n day: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n onTimeUp() {}\n}\n\n\nexport default AtCountdown\n" + }, + 'Com/index.tsx': { + import: './Com', + content: + "import React, { Component } from 'react'\nimport PropTypes, { InferProps } from 'prop-types'\nimport { AtCountdownItemProps } from '@/types/countdown'\n\n\n\nclass AtCountdownItem extends Component {\n public static defaultProps: AtCountdownItemProps\n public static propTypes: InferProps\n\n private formatNum(num: number): string {\n return num <= 9 ? `0${num}` : `${num}`\n }\n\n public render(): JSX.Element {\n const { num, separator } = this.props\n\n return (\n
\n
\n {this.formatNum(num)}\n
\n {separator}\n
\n )\n }\n}\n\nexport default AtCountdownItem;\n\nAtCountdownItem.defaultProps = {\n num: 0,\n separator: ':'\n}\n\nAtCountdownItem.propTypes = {\n num: PropTypes.number.isRequired,\n separator: PropTypes.string\n}\n" } }, dependencies: { react: { version: '17.0.1' }, - classnames: { version: '2.2.6' } + classnames: { version: '2.2.6' }, + 'prop-types': { version: '15.7.2' } }, - identifier: 'biuui-arrowbutton' + identifier: 'biuui-tag' } } }; diff --git a/src/components/Countdown/Com/index.tsx b/src/components/Countdown/Com/index.tsx new file mode 100644 index 0000000..632a871 --- /dev/null +++ b/src/components/Countdown/Com/index.tsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import PropTypes, { InferProps } from 'prop-types'; +import { AtCountdownItemProps } from '@/types/countdown'; + +class AtCountdownItem extends Component { + public static defaultProps: AtCountdownItemProps; + public static propTypes: InferProps; + + private formatNum(num: number): string { + return num <= 9 ? `0${num}` : `${num}`; + } + + public render(): JSX.Element { + const { num, separator } = this.props; + + return ( +
+
+ {this.formatNum(num)} +
+ {separator} +
+ ); + } +} + +export default AtCountdownItem; + +AtCountdownItem.defaultProps = { + num: 0, + separator: ':' +}; + +AtCountdownItem.propTypes = { + num: PropTypes.number.isRequired, + separator: PropTypes.string +}; diff --git a/src/components/Countdown/index.less b/src/components/Countdown/index.less index e69de29..da68a77 100644 --- a/src/components/Countdown/index.less +++ b/src/components/Countdown/index.less @@ -0,0 +1,89 @@ +// @import "../../style/theme/default.scss"; +// @import "../../style/mixins/index.scss"; + +// $font-size: $font-size-lg; + +// .at-count-down { +// display: inline-block; +// min-height: $font-size; + +// &__item { +// display: inline-flex; +// align-items: center; +// } + +// &__time-box { +// display: inline-block; +// text-align: center; +// min-width: $font-size; +// font: { +// size: $font-size; +// family: countDownFont; +// style: normal; +// weight: 400; +// variant: normal; +// } + +// text-transform: none; +// text-rendering: auto; +// line-height: 1; +// -webkit-font-smoothing: antialiased; +// vertical-align: middle; +// } + +// &__separator { +// font-size: $font-size-base; +// display: inline-flex; +// align-items: center; +// text-align: justify; +// padding: 0 $spacing-v-xs; +// } + +// &--card { +// .at-count-down__time-box { +// padding: $spacing-v-xs 0; +// border: 1PX solid $color-border-grey; +// border-radius: $border-radius-md; +// color: #FF4949; +// display: inline-block; +// background-color: #fff; +// position: relative; + +// .at-count-down__time { +// position: relative; +// z-index: $zindex-divider + 1; +// } + +// &::after { +// position: absolute; +// content: ''; +// width: 100%; +// height: 1PX; +// top: 50%; +// left: 0; +// z-index: $zindex-divider; +// background-color: $color-grey-3; +// } +// } +// } +// } +.biu-countdown { + display: inline-block; + .biu-countdown__item { + display: inline-flex; + align-items: center; + .biu-countdown__time-box { + color: #333; + display: inline-block; + background-color: #fff; + position: relative; + .biu-countdown__time { + } + } + .biu-countdown__separator { + display: inline-flex; + text-align: justify; + align-items: center; + } + } +} diff --git a/src/components/Countdown/index.tsx b/src/components/Countdown/index.tsx index e69de29..2d6b8bc 100644 --- a/src/components/Countdown/index.tsx +++ b/src/components/Countdown/index.tsx @@ -0,0 +1,170 @@ +import React, { Component } from 'react'; +import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import { AtCountDownProps, AtCountdownState } from '@/types/countdown'; +import AtCountdownItem from './Com'; +import './index.less'; + +const toSeconds = ( + day: number, + hours: number, + minutes: number, + seconds: number +): number => day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds; + +class AtCountdown extends Component { + public static defaultProps: AtCountDownProps; + + private seconds: number; + private timer: NodeJS.Timeout | number | undefined; + + public constructor(props: AtCountDownProps) { + super(props); + const { day, hours, minutes, seconds } = this.props; + this.seconds = toSeconds(day!, hours!, minutes!, seconds!); + + console.log('2232323', this.seconds); + + const { + day: _day, + hours: _hours, + minutes: _minutes, + seconds: _seconds + } = this.calculateTime(); + this.state = { + _day, + _hours, + _minutes, + _seconds + }; + this.timer = undefined; + } + + private setTimer(): void { + if (!this.timer) this.countdonwn(); + } + + private clearTimer(): void { + if (this.timer) { + clearTimeout(this.timer as number); + this.timer = undefined; + } + } + + private calculateTime() { + let [day, hours, minutes, seconds] = [0, 0, 0, 0]; + + // console.log('this.seconds', this.seconds); + + if (this.seconds > 0) { + day = this.props.isShowDay + ? Math.floor(this.seconds / (60 * 60 * 24)) + : 0; + hours = Math.floor(this.seconds / (60 * 60)) - day * 24; + minutes = Math.floor(this.seconds / 60) - day * 24 * 60 - hours * 60; + seconds = + Math.floor(this.seconds) - + day * 24 * 60 * 60 - + hours * 60 * 60 - + minutes * 60; + } + // console.log('hours', hours) + return { + day, + hours, + minutes, + seconds + }; + } + + // 倒计时 + private countdonwn(): void { + // console.log('123') + const { day, hours, minutes, seconds } = this.calculateTime(); + + this.setState({ + _day: day, + _hours: hours, + _minutes: minutes, + _seconds: seconds + }); + this.seconds--; + + if (this.seconds < 0) { + this.clearTimer(); + this.props.onTimeUp && this.props.onTimeUp(); + return; + } + + this.timer = setTimeout(() => { + this.countdonwn(); + }, 1000); + } + + public componentWillReceiveProps(nextProps: AtCountDownProps): void { + if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return; + + const { day, hours, minutes, seconds } = nextProps; + this.seconds = toSeconds(day!, hours!, minutes!, seconds!); + this.clearTimer(); + this.setTimer(); + } + + public componentDidMount(): void { + this.setTimer(); + } + + public componentWillUnmount(): void { + this.clearTimer(); + } + + render(): JSX.Element { + const { + className, + customStyle, + format, // day: '天',hours: '时',minutes: '分',seconds: '秒' + isShowDay, // 是否显示天数 + isShowHour //是否显示小时 + } = this.props; + + const { _day, _hours, _minutes, _seconds } = this.state; + + return ( +
+ {isShowDay && } + {isShowHour && ( + + )} + + +
+ ); + } +} +AtCountdown.defaultProps = { + customStyle: '', + className: '', + isShowDay: false, + isShowHour: true, + format: { + day: '天', + hours: '时', + minutes: '分', + seconds: '秒' + }, + day: 0, + hours: 0, + minutes: 0, + seconds: 0, + onTimeUp() {} +}; + +export default AtCountdown; diff --git a/src/index.ts b/src/index.ts index 35046fb..0fc1532 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export { default as Button } from './components/Button'; export { default as Tag } from './components/Tag'; export { default as Curtain } from './components/Curtain'; export { default as ArrowButton } from './components/ArrowButton'; +export { default as Countdown } from './components/Countdown'; diff --git a/src/types/countdown.d.ts b/src/types/countdown.d.ts new file mode 100644 index 0000000..72a7f4d --- /dev/null +++ b/src/types/countdown.d.ts @@ -0,0 +1,82 @@ +import { ComponentClass } from 'react'; + +export interface FormatObject { + /** + * 格式化分割符号:天 + * @default '天' + */ + day?: string; + /** + * 格式化分割符号:小时 + * @default '时' + */ + hours: string; + /** + * 格式化分割符号:分钟 + * @default '分' + */ + minutes: string; + /** + * 格式化分割符号:秒 + * @default '秒' + */ + seconds: string; +} + +export interface AtCountDownProps { + /** + * 是否显示天数 + * @default false + */ + isShowDay?: boolean; + /** + * 是否显示小时 + * @default true + */ + isShowHour?: boolean; + /** + * 格式化分割符号 + * @default { day: '天', hours: '时', minutes: '分', seconds: '秒' } + */ + format?: FormatObject; + /** + * 天数 + * @default 0 + */ + day?: number; + /** + * 小时 + * @default 0 + */ + hours?: number; + /** + * 分钟 + * @default 0 + */ + minutes?: number; + /** + * 秒 + * @default 0 + */ + seconds?: number; + /** + * 倒计时时间到,执行的回调函数 + */ + onTimeUp?: Function; +} + +export interface AtCountdownState { + _day: number; + _hours: number; + _minutes: number; + _seconds: number; +} + +export interface AtCountdownItemProps { + num: number; + separator: string; +} + +declare const AtCountDown: ComponentClass; + +export default AtCountDown;