首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >具有自定义图标的分级组件

具有自定义图标的分级组件
EN

Stack Overflow用户
提问于 2020-06-09 21:19:25
回答 1查看 256关注 0票数 0

我正在努力使定制评分组件。我知道还有其他的库,但我想自己来做,这样我就可以理解底层的过程了。然而,我在自定义组件部分上苦苦挣扎。对于默认情况,它工作得很好。对于自定义组件,我尝试的是允许开发人员传递他们所需图标的svg组件,然后将该图标显示为评级组件。到目前为止,它是工作的,但我不知道如何处理鼠标移出功能。

以下是我尝试过的方法

代码语言:javascript
运行
复制
import React from "react";

const DefaultComponent = ({
  ratingRef,
  currentRating,
  handleMouseOut,
  handleMouseOver,
  handleStarClick,
  totalRating
}) => {
  return (
    <div
      className="rating"
      ref={ratingRef}
      data-rating={currentRating}
      onMouseOut={handleMouseOut}
    >
      {[...Array(+totalRating).keys()].map(n => {
        return (
          <span
            className="star"
            key={n + 1}
            data-value={n + 1}
            onMouseOver={handleMouseOver}
            onClick={handleStarClick}
          >
            &#9733;
          </span>
        );
      })}
    </div>
  );
};

const CustomComponent = ({
  ratingRef,
  currentRating,
  handleMouseOut,
  handleMouseOver,
  handleStarClick,
  totalRating,
  ratingComponent
}) => {
  return (
    <>
      {[...Array(+totalRating).keys()].map(n => {
        return ratingComponent({ key: n });
      })}
    </>
  );
};

const Rating = props => {
  const { totalRating = 5, onClick, ratingComponent } = props;
  const [currentRating, setCurrentRating] = React.useState(0);
  React.useEffect(() => {
    handleMouseOut();
  }, []);
  const handleMouseOver = ev => {
    const stars = ev.target.parentElement.getElementsByClassName("star");
    const hoverValue = ev.target.dataset.value;
    Array.from(stars).forEach(star => {
      star.style.color = hoverValue >= star.dataset.value ? "#FDC60A" : "#444";
    });
  };

  const handleMouseOut = ev => {
    const stars = ratingRef?.current?.getElementsByClassName("star");
    stars &&
      Array.from(stars).forEach(star => {
        star.style.color =
          currentRating >= star.dataset.value ? "#FDC60A" : "#444";
      });
  };

  const handleStarClick = ev => {
    let rating = ev.target.dataset.value;
    setCurrentRating(rating); // set state so the rating stays highlighted
    if (onClick) {
      onClick(rating); // emit the event up to the parent
    }
  };

  const ratingRef = React.useRef();
  console.log("ratingComponent", ratingComponent);
  return (
    <>
      {ratingComponent ? (
        <CustomComponent
          ratingRef={ratingRef}
          currentRating={currentRating}
          handleMouseOut={handleMouseOut}
          handleMouseOver={handleMouseOver}
          handleStarClick={handleStarClick}
          totalRating={totalRating}
          ratingComponent={ratingComponent}
        />
      ) : (
        <DefaultComponent
          ratingRef={ratingRef}
          currentRating={currentRating}
          handleMouseOut={handleMouseOut}
          handleMouseOver={handleMouseOver}
          handleStarClick={handleStarClick}
          totalRating={totalRating}
        />
      )}
    </>
  );
};

export default Rating;

我还创建了一个沙箱。

https://codesandbox.io/s/stoic-almeida-yz7ju?file=/src/components/Rating/Rating.js:0-2731

谁能告诉我如何制作可重用的评级组件,它可以支持自定义图标进行评级,就像我需要的那样,我需要的正是宝石和星形图标?

EN

回答 1

Stack Overflow用户

发布于 2020-06-09 22:20:37

我会这样做:

  • 重用您的DefaultComponent以接收图标
  • 使用类名而不是选择DOM元素;一个用于默认,另一个用于悬停。为悬停/选定的类名添加fillcolor属性
  • 使用hoverIndex状态来跟踪我正在悬停的元素并将悬停事件放在DefaultComponent内,因为它们只与图标相关,在父容器

中不需要

我在设计star svg时遇到了问题。从本质上讲,您需要覆盖它的fill颜色-但这似乎是一个复杂的颜色。如果您可以简化它,或者将填充作为prop传递到svg中并在内部使用它-这两种方法都可以!

沙盒:https://codesandbox.io/s/epic-pascal-j1d0c

希望这能有所帮助!

代码语言:javascript
运行
复制
const DefaultIcon = _ => "a"; //  = '&#9733'

const DefaultComponent = ({
  ratingComponent = DefaultIcon,
  ratingRef,
  currentRating,
  handleStarClick,
  totalRating
}) => {
  const RatingIconComponent = ratingComponent;

  const [hoverIndex, setHoverIndex] = React.useState(-Infinity);

  const handleMouseOver = index => {
    setHoverIndex(index);
  };

  const handleMouseOut = ev => {
    setHoverIndex(-Infinity);
  };

  return (
    <div
      className="rating"
      ref={ratingRef}
      data-rating={currentRating}
      onMouseOut={handleMouseOut}
    >
      {[...Array(+totalRating).keys()].map(n => {
        const isFilled = n < currentRating || n <= hoverIndex;

        return (
          <span
            className={`ratingIcon ${isFilled && "fill"}`}
            key={n + 1}
            data-value={n + 1}
            onMouseOver={_ => handleMouseOver(n)}
            onClick={_ => handleStarClick(n + 1)}
          >
            <RatingIconComponent />
          </span>
        );
      })}
    </div>
  );
};
代码语言:javascript
运行
复制
const Rating = props => {
  const { totalRating = 5, onClick, ratingComponent } = props;
  const [currentRating, setCurrentRating] = React.useState(0);

  const handleStarClick = rating => {
    setCurrentRating(rating); // set state so the rating stays highlighted
    if (onClick) {
      onClick(rating); // emit the event up to the parent
    }
  };

  const ratingRef = React.useRef();

  return (
    <DefaultComponent
      ratingComponent={ratingComponent}
      ratingRef={ratingRef}
      currentRating={currentRating}
      handleStarClick={handleStarClick}
      totalRating={totalRating}
    />
  );
};
代码语言:javascript
运行
复制
<Rating ratingComponent={Gem} />
代码语言:javascript
运行
复制
/* for normal text */
.ratingIcon {
  color: "gray";
}

.ratingIcon.fill {
  color: yellow;
}

/* for svg */
.ratingIcon svg path {
  fill: gray !important;
}

.ratingIcon.fill svg path {
  fill: yellow !important;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62283605

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档