diff --git a/src/components/IconFont/icon.stories.tsx b/src/components/IconFont/icon.stories.tsx index 60d8759..2bf0848 100644 --- a/src/components/IconFont/icon.stories.tsx +++ b/src/components/IconFont/icon.stories.tsx @@ -3,33 +3,33 @@ import { storiesOf } from "@storybook/react"; // import { action } from "@storybook/addon-actions"; // import { withInfo } from '@storybook/addon-info' -import IconFont from "./index"; +import Icon from "./index"; const IconFontComp = () => (
- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
); -const SpinIconFont = () => ; +const SpinIconFont = () => ; storiesOf("Icon图标", module) .add("IconFont", IconFontComp) diff --git a/src/components/IconFont/index.tsx b/src/components/IconFont/index.tsx index 37d78d1..72cf1c9 100644 --- a/src/components/IconFont/index.tsx +++ b/src/components/IconFont/index.tsx @@ -32,7 +32,7 @@ function createScript() { * import { Icon } from 'jadedui' * ~~~ */ -const IconFont: FC = (props) => { +const Icon: FC = (props) => { const { type, color, spin = false, ...restProps } = props; createScript(); @@ -51,4 +51,4 @@ const IconFont: FC = (props) => { ); }; -export default IconFont; +export default Icon; diff --git a/src/components/Rate/_style.scss b/src/components/Rate/_style.scss new file mode 100644 index 0000000..7e1122e --- /dev/null +++ b/src/components/Rate/_style.scss @@ -0,0 +1,72 @@ +.dui-rate { + display: flex; + + svg { + color: $rate-color-default; + font-size: 25px; + } + + .dui-rate-star:not(:last-child) { + margin-right: 8px; + } + + .dui-rate-star { + position: relative; + transition: all 0.2s; + + .dui-rate-star-first { + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 100%; + overflow: hidden; + opacity: 0; + color: $rate-color-default; + font-size: 25px; + } + + .dui-rate-star-second { + color: $rate-color-default; + font-size: 25px; + } + } + + .dui-rate-star:hover { + transform: scale(1.1); + } + + .dui-rate-star-disabled:hover { + transform: none; + } + + .dui-rate-star.ant-rate-star-half { + .dui-rate-star-first { + opacity: 1; + color: $rate-color; + + svg { + color: $rate-color; + } + } + } + + .dui-rate-star.ant-rate-star-full { + .dui-rate-star-first { + opacity: 0; + color: $rate-color-default; + + svg { + color: $rate-color-default; + } + } + + .dui-rate-star-second { + color: $rate-color; + + svg { + color: $rate-color; + } + } + } +} diff --git a/src/components/Rate/index.tsx b/src/components/Rate/index.tsx new file mode 100644 index 0000000..e4d2039 --- /dev/null +++ b/src/components/Rate/index.tsx @@ -0,0 +1,168 @@ +import React, { useEffect, useState } from "react"; +import type { ReactNode } from "react"; +import classnames from "classnames"; +import Icon from "../IconFont"; + +export type OrientationType = "left" | "right" | "center"; + +export interface RateProps { + /** 自定义样式类名 */ + className?: string; + /** 自定义样式对象 */ + style?: React.CSSProperties; + /** 当前数,受控值 */ + value?: number; + /** 默认值 */ + defaultValue?: number; + /** 只读,无法进行交互 */ + disabled?: boolean; + /** 自定义字符 */ + character?: ReactNode | ((index: number) => ReactNode); + /** 选择时的回调 */ + onChange?: (value: number) => void; +} + +/** + * Rate 评分 + * 对评价进行展示 + * 对事物进行快速的评级操作 + * ### 引用方法 + * + * ~~~js + * import { Rate } from 'jadedui' + * ~~~ + */ +const Rate: React.FC = (props) => { + const { className, value, defaultValue, character, disabled, onChange } = + props; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [ids, setIds] = useState([0, 1, 2, 3, 4]); + const [rateValue, setRateValue] = useState<[number, number]>([-1, -1]); + // const [rateDefaultValue,setRateDefaultValue] = useState<[number,number]>([-1,-1]); + const [pos, setPos] = useState({ + curIndexLi: -1, + curIndexStar: -1, + isFocus: false, + }); + const cls = ["ant-rate-star-half", "ant-rate-star-full"]; + if ( + (value && typeof value !== "number") || + (defaultValue && typeof defaultValue !== "number") + ) { + throw new Error("value or defaultValue must be a number!"); + } + + const initRate = () => { + if (value || defaultValue) { + const transValue = String(value || defaultValue).split("."); + const curIndexLi = Number(transValue[0]); + const curIndexStar = transValue[1] + ? Number(transValue[1]) >= 5 + ? 0 + : -1 + : -1; + console.log("curIndexLi: ", curIndexLi); + console.log("curIndexStar: ", curIndexStar); + setRateValue([curIndexLi, curIndexStar]); + setPos({ + curIndexLi, + curIndexStar, + isFocus: true, + }); + } + }; + + useEffect(() => { + initRate(); + }, [value]); + + const onMouseFirstEnter = (d: number, curStar: number) => { + if (disabled) return; + const curIndex = ids.findIndex((id) => id == d); + setPos({ + ...pos, + curIndexLi: curIndex, + curIndexStar: curStar, + }); + }; + + const renderClassNames = (key: number) => { + let classNames = "dui-rate-star"; + if (disabled) classNames += " dui-rate-star-disabled"; + if (pos.curIndexLi === -1) { + classNames += " dui-rate-star-zero"; + } else { + if (key < pos.curIndexLi) { + classNames += " ant-rate-star-full"; + return classNames; + } + if (key == pos.curIndexLi) { + classNames += " " + cls[pos.curIndexStar]; + return classNames; + } + if (key > pos.curIndexLi) { + classNames += " dui-rate-star-zero"; + } + } + + return classNames; + }; + + const rateClickHandle = (type: string, key: number) => { + if (value || disabled) return; + onMouseFirstEnter(key, type === "first" ? 0 : 1); + setRateValue([key, type === "first" ? 0 : 1]); + setPos({ + ...pos, + isFocus: true, + }); + onChange && onChange(type === "first" ? key + 0.5 : key + 1); + }; + + const onMouseLeave = () => { + if (disabled) return; + if (!pos.isFocus) { + onMouseFirstEnter(-1, -1); + } else { + onMouseFirstEnter(...rateValue); + } + }; + + const renderNode = (key: number) => + typeof character === "function" ? character(key) : character; + + return ( +
    + {ids.map((key) => { + return ( +
  • +
    onMouseFirstEnter(key, 0)} + onClick={() => rateClickHandle("first", key)} + > + {renderNode(key)} +
    +
    onMouseFirstEnter(key, 1)} + onClick={() => rateClickHandle("second", key)} + > + {renderNode(key)} +
    +
  • + ); + })} +
+ ); +}; + +Rate.defaultProps = { + character: , + disabled: false, +}; + +export default Rate; diff --git a/src/components/Rate/rate.stories.tsx b/src/components/Rate/rate.stories.tsx new file mode 100644 index 0000000..d04beb7 --- /dev/null +++ b/src/components/Rate/rate.stories.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { storiesOf } from "@storybook/react"; +// import { action } from "@storybook/addon-actions"; +// import { withInfo } from '@storybook/addon-info' + +import Rate from "./index"; + +const defaultRate = () => { + const onChange = (v: any) => { + console.log("v: ", v); + }; + return ; +}; + +const characterRate = () => ( + <> + + + } /> + +); + +const disableRate = () => ; + +storiesOf("Rate 评分", module) + .add("Rate", defaultRate) + .add("只读 Rate", disableRate) + .add("自定义字符 Rate", characterRate); diff --git a/src/styles/_reboot.scss b/src/styles/_reboot.scss index 4f782d4..403bbd4 100644 --- a/src/styles/_reboot.scss +++ b/src/styles/_reboot.scss @@ -150,6 +150,7 @@ address { ol, ul { padding-left: 2rem; + list-style: none; } ol, diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index a2ceb94..084c518 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -274,3 +274,7 @@ $alert-padding-x-right: 1rem !default; $alert-font-color: #000000d9; $alert-message-font-size: $font-size-base * 1.1 !default; $alert-description-font-size: $font-size-base * 0.8 !default; + +// Rate +$rate-color: $yellow !default; +$rate-color-default: $gray-200 !default; diff --git a/src/styles/index.scss b/src/styles/index.scss index 3374646..3bc37db 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -28,3 +28,6 @@ //Divider @import "../components/Divider/style"; + +//Rate +@import "../components/Rate/style"; diff --git a/todoList.md b/todoList.md index 722e641..f9a32d4 100644 --- a/todoList.md +++ b/todoList.md @@ -4,5 +4,6 @@ - 图片水印 canvas、mutationObserver - Flip 动画思路 +- 滑动验证码