From 4ef31630ef7f1b32d64b4bab23796e0aefdef5b3 Mon Sep 17 00:00:00 2001 From: panyushan <1193345664@qq.com> Date: Thu, 5 Jan 2023 18:18:34 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20rate=E7=BB=84=E4=BB=B6=E5=88=9D?= =?UTF-8?q?=E6=AD=A5=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/IconFont/icon.stories.tsx | 42 ++++++++-------- src/components/IconFont/index.tsx | 4 +- src/components/Rate/_style.scss | 25 ++++++++++ src/components/Rate/index.tsx | 62 ++++++++++++++++++++++++ src/components/Rate/rate.stories.tsx | 10 ++++ src/styles/_reboot.scss | 1 + src/styles/index.scss | 3 ++ 7 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 src/components/Rate/_style.scss create mode 100644 src/components/Rate/index.tsx create mode 100644 src/components/Rate/rate.stories.tsx 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..2bd95b3 --- /dev/null +++ b/src/components/Rate/_style.scss @@ -0,0 +1,25 @@ +.dui-rate { + display: flex; + + .dui-rate-star:not(:last-child) { + margin-right: 8px; + } + + .dui-rate-star { + position: relative; + + .dui-rate-star-first { + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 100%; + overflow: hidden; + opacity: 0; + + // &:hover{ + // color: #fadb14; + // } + } + } +} diff --git a/src/components/Rate/index.tsx b/src/components/Rate/index.tsx new file mode 100644 index 0000000..7f80759 --- /dev/null +++ b/src/components/Rate/index.tsx @@ -0,0 +1,62 @@ +import React from "react"; // , { ReactNode } +import classnames from "classnames"; +import Icon from "../IconFont"; + +export type OrientationType = "left" | "right" | "center"; + +export interface RateProps { + /** 评分样式类 */ + className?: string; + /** 评分样式对象 */ + style?: React.CSSProperties; + /** 是否允许半选 */ + allowHalf?: boolean; +} + +/** + * Rate 评分 + * 对评价进行展示 + * 对事物进行快速的评级操作 + * ### 引用方法 + * + * ~~~js + * import { Rate } from 'jadedui' + * ~~~ + */ +const Rate: React.FC = (props) => { + const { className, allowHalf } = props; + console.log("allowHalf: ", allowHalf); + const classNames = classnames("dui-rate", className); + + return ( +
    + {new Array(5).fill("").map(() => { + return ( +
  • +
    + +
    +
    + +
    +
  • + ); + })} +
+ ); +}; + +// Divider.defaultProps = { +// plain: false, +// orientation: "center", +// type: "horizontal", +// dashed: 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..45bc6d8 --- /dev/null +++ b/src/components/Rate/rate.stories.tsx @@ -0,0 +1,10 @@ +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 = () => ; + +storiesOf("Rate 评分", module).add("默认 Rate", defaultRate); 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/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"; From e86e6c1018d8c67e2e1b71590d6f143b64d409b2 Mon Sep 17 00:00:00 2001 From: panyushan <1193345664@qq.com> Date: Fri, 6 Jan 2023 18:30:54 +0800 Subject: [PATCH 2/4] =?UTF-8?q?perf:=20feat:=20rate=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Rate/_style.scss | 4 --- src/components/Rate/index.tsx | 51 ++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/components/Rate/_style.scss b/src/components/Rate/_style.scss index 2bd95b3..eb78e77 100644 --- a/src/components/Rate/_style.scss +++ b/src/components/Rate/_style.scss @@ -16,10 +16,6 @@ height: 100%; overflow: hidden; opacity: 0; - - // &:hover{ - // color: #fadb14; - // } } } } diff --git a/src/components/Rate/index.tsx b/src/components/Rate/index.tsx index 7f80759..ddd5f66 100644 --- a/src/components/Rate/index.tsx +++ b/src/components/Rate/index.tsx @@ -1,4 +1,4 @@ -import React from "react"; // , { ReactNode } +import React, { useState } from "react"; // , { ReactNode } import classnames from "classnames"; import Icon from "../IconFont"; @@ -26,20 +26,57 @@ export interface RateProps { const Rate: React.FC = (props) => { const { className, allowHalf } = props; console.log("allowHalf: ", allowHalf); - const classNames = classnames("dui-rate", className); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [ids, setIds] = useState([0, 1, 2, 3, 4]); + const [pos, setPos] = useState({ + curIndexLi: -1, + curIndexStar: -1, + }); + // const [classNames,setClassNames] = useState>({ + // 'dui-rate-star': true, // 基础类 + // 'dui-rate-star-zero': false, // 0 星 + // 'dui-rate-star-half': false, // 半星 + // 'dui-rate-star-full': false, // 满星 + // 'dui-rate-star-active': false, // 当前激活星 + // }) + + const onMouseFirstEnter = (d: number, curStar: number) => { + console.log("onMouseFirstEnter"); + const curIndex = ids.findIndex((id) => id == d); + console.log("curIndex: ", curIndex, curStar); + setPos({ + curIndexLi: curIndex, + curIndexStar: curStar, + }); + }; + + const renderClassNames = (key: number) => { + console.log("key: ", key); + let classNames = "dui-rate-star"; + if (pos.curIndexLi === -1) { + classNames += " dui-rate-star-zero"; + } + return classNames; + }; return ( -
    - {new Array(5).fill("").map(() => { +
      + {ids.map((key) => { return ( -
    • -
      +
    • +
      onMouseFirstEnter(key, 0)} + >
      -
      +
      onMouseFirstEnter(key, 1)} + > Date: Sun, 29 Jan 2023 18:14:16 +0800 Subject: [PATCH 3/4] =?UTF-8?q?perf:=20=E5=AE=8C=E5=96=84rate=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=2090%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Rate/_style.scss | 51 +++++++++++ src/components/Rate/index.tsx | 129 ++++++++++++++++++++------- src/components/Rate/rate.stories.tsx | 15 +++- src/styles/_variables.scss | 4 + todoList.md | 1 + 5 files changed, 167 insertions(+), 33 deletions(-) diff --git a/src/components/Rate/_style.scss b/src/components/Rate/_style.scss index eb78e77..7e1122e 100644 --- a/src/components/Rate/_style.scss +++ b/src/components/Rate/_style.scss @@ -1,12 +1,18 @@ .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; @@ -16,6 +22,51 @@ 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 index ddd5f66..78d69e7 100644 --- a/src/components/Rate/index.tsx +++ b/src/components/Rate/index.tsx @@ -1,16 +1,23 @@ -import React, { useState } from "react"; // , { ReactNode } +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; - /** 是否允许半选 */ - allowHalf?: boolean; + /** 当前数,受控值 */ + value?: number; + /** 默认值 */ + defaultValue?: number; + /** 只读,无法进行交互 */ + disabled?: boolean; + /** 自定义字符 */ + character?: ReactNode | ((index: number) => ReactNode); } /** @@ -24,63 +31,123 @@ export interface RateProps { * ~~~ */ const Rate: React.FC = (props) => { - const { className, allowHalf } = props; - console.log("allowHalf: ", allowHalf); + const { className, value, defaultValue, character, disabled } = 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 [classNames,setClassNames] = useState>({ - // 'dui-rate-star': true, // 基础类 - // 'dui-rate-star-zero': false, // 0 星 - // 'dui-rate-star-half': false, // 半星 - // 'dui-rate-star-full': false, // 满星 - // 'dui-rate-star-active': 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) => { - console.log("onMouseFirstEnter"); + if (disabled) return; const curIndex = ids.findIndex((id) => id == d); - console.log("curIndex: ", curIndex, curStar); setPos({ + ...pos, curIndexLi: curIndex, curIndexStar: curStar, }); }; const renderClassNames = (key: number) => { - console.log("key: ", key); 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, + }); + }; + + 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)}
        • ); @@ -89,11 +156,9 @@ const Rate: React.FC = (props) => { ); }; -// Divider.defaultProps = { -// plain: false, -// orientation: "center", -// type: "horizontal", -// dashed: false, -// }; +Rate.defaultProps = { + character: , + disabled: false, +}; export default Rate; diff --git a/src/components/Rate/rate.stories.tsx b/src/components/Rate/rate.stories.tsx index 45bc6d8..3978ef4 100644 --- a/src/components/Rate/rate.stories.tsx +++ b/src/components/Rate/rate.stories.tsx @@ -7,4 +7,17 @@ import Rate from "./index"; const defaultRate = () => ; -storiesOf("Rate 评分", module).add("默认 Rate", defaultRate); +const characterRate = () => ( + <> + + + } /> + +); + +const disableRate = () => ; + +storiesOf("Rate 评分", module) + .add("Rate", defaultRate) + .add("只读 Rate", disableRate) + .add("自定义字符 Rate", characterRate); 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/todoList.md b/todoList.md index 722e641..f9a32d4 100644 --- a/todoList.md +++ b/todoList.md @@ -4,5 +4,6 @@ - 图片水印 canvas、mutationObserver - Flip 动画思路 +- 滑动验证码 From ea5bbafd6d57723240f9deeb0f4cc19956c27915 Mon Sep 17 00:00:00 2001 From: panyushan <1193345664@qq.com> Date: Mon, 30 Jan 2023 10:28:33 +0800 Subject: [PATCH 4/4] perf: complete rate component --- src/components/Rate/index.tsx | 6 +++++- src/components/Rate/rate.stories.tsx | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/Rate/index.tsx b/src/components/Rate/index.tsx index 78d69e7..e4d2039 100644 --- a/src/components/Rate/index.tsx +++ b/src/components/Rate/index.tsx @@ -18,6 +18,8 @@ export interface RateProps { disabled?: boolean; /** 自定义字符 */ character?: ReactNode | ((index: number) => ReactNode); + /** 选择时的回调 */ + onChange?: (value: number) => void; } /** @@ -31,7 +33,8 @@ export interface RateProps { * ~~~ */ const Rate: React.FC = (props) => { - const { className, value, defaultValue, character, disabled } = 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]); @@ -113,6 +116,7 @@ const Rate: React.FC = (props) => { ...pos, isFocus: true, }); + onChange && onChange(type === "first" ? key + 0.5 : key + 1); }; const onMouseLeave = () => { diff --git a/src/components/Rate/rate.stories.tsx b/src/components/Rate/rate.stories.tsx index 3978ef4..d04beb7 100644 --- a/src/components/Rate/rate.stories.tsx +++ b/src/components/Rate/rate.stories.tsx @@ -5,7 +5,12 @@ import { storiesOf } from "@storybook/react"; import Rate from "./index"; -const defaultRate = () => ; +const defaultRate = () => { + const onChange = (v: any) => { + console.log("v: ", v); + }; + return ; +}; const characterRate = () => ( <>