From 96ceb2a6ba41cfc495fdc686bfbacdb04303b71d Mon Sep 17 00:00:00 2001 From: TechQuery Date: Fri, 18 Oct 2024 19:08:44 +0800 Subject: [PATCH 1/2] [add] User Rank component --- ReadMe.md | 1 + package.json | 2 +- preview/content.tsx | 27 +++- source/UserRank/Address.tsx | 20 +++ source/UserRank/index.module.less | 225 ++++++++++++++++++++++++++++++ source/UserRank/index.tsx | 118 ++++++++++++++++ source/index.ts | 1 + 7 files changed, 390 insertions(+), 4 deletions(-) create mode 100644 source/UserRank/Address.tsx create mode 100644 source/UserRank/index.module.less create mode 100644 source/UserRank/index.tsx diff --git a/ReadMe.md b/ReadMe.md index f6b9177..2392068 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -43,6 +43,7 @@ A **[React][1] advanced components library** based on [TypeScript][2] & [Bootstr 18. [Share Box](source/ShareBox.tsx) 19. [Overlay Box](source/OverlayBox.tsx) 20. [Dialog](source/Dialog.tsx) +21. [User Rank](source/UserRank.tsx) #### Data components diff --git a/package.json b/package.json index f2ae671..99fa91a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "idea-react", - "version": "2.0.0-rc.4", + "version": "2.0.0-rc.5", "license": "LGPL-3.0-or-later", "author": "shiy2008@gmail.com", "description": "A React advanced components library based on TypeScript & Bootstrap, built by idea2app remote developers team.", diff --git a/preview/content.tsx b/preview/content.tsx index 7182961..9080eda 100644 --- a/preview/content.tsx +++ b/preview/content.tsx @@ -1,6 +1,6 @@ import { observable } from 'mobx'; import { observer } from 'mobx-react'; -import { PureComponent } from 'react'; +import { Component } from 'react'; import { Button, Form, Image, Modal } from 'react-bootstrap'; import { formToJSON, sleep } from 'web-utility'; @@ -20,12 +20,13 @@ import { ShareBox, SpinnerButton, TimeDistance, - TypeEcho + TypeEcho, + UserRankView } from '../source'; import { CodeExample, Section } from './utility'; @observer -export class Content extends PureComponent { +export class Content extends Component { @observable accessor pageIndex = 1; @@ -219,6 +220,26 @@ export class Content extends PureComponent { {showLoading && 加载中...} + +
+ + ({ + id: index + 1, + name, + avatar: `https://github.com/${name}.png`, + website: `https://github.com/${name}`, + score: 100 - index + }))} + /> + +
); } diff --git a/source/UserRank/Address.tsx b/source/UserRank/Address.tsx new file mode 100644 index 0000000..d754aac --- /dev/null +++ b/source/UserRank/Address.tsx @@ -0,0 +1,20 @@ +import { FC } from 'react'; + +import { Icon } from '../Icon'; + +export type UserAddressProps = Partial>; + +export const UserAddress: FC = ({ email, website }) => ( +
+ {email && ( + + + + )} + {website && ( + + + + )} +
+); diff --git a/source/UserRank/index.module.less b/source/UserRank/index.module.less new file mode 100644 index 0000000..6d952b1 --- /dev/null +++ b/source/UserRank/index.module.less @@ -0,0 +1,225 @@ +@keyframes suspension { + 0% { + position: relative; + top: 0px; + } + 50% { + position: relative; + top: -0.8rem; + } + 100% { + position: relative; + top: 0px; + } +} + +.topUserRow { + --logo-image: url('https://github.com/idea2app.png'); + --title-background-image: url('https://hackathon-api.static.kaiyuanshe.cn/6342619375fa1817e0f56ce1/2022/10/09/rrrr.png'); + a { + text-decoration: none; + } + .imgBox { + width: 2.1rem; + &:hover img { + transition: all 0.3s; + transform: scale(1.1); + } + } + .showTitle { + position: relative; + height: 4.8rem; + padding-top: 1.2rem; + z-index: 4; + background-image: var(--title-background-image); + background-attachment: fixed; + background-size: 21rem auto; + margin-top: 1.5rem; + &::before { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 0.3rem; + background-color: rgba(161, 160, 152, 0.7); + } + .showMedal { + position: absolute; + top: -1.2rem; + left: 5%; + i { + width: 1.7rem; + height: 1.7rem; + background-image: var(--logo-image), + linear-gradient(#ffd83a, #fdeba0); + background-blend-mode: lighten; + background-position: center; + background-size: 1.5rem 1.5rem; + background-repeat: no-repeat; + border: 0.1rem solid #ffd83a; + box-shadow: 0 0 0.5rem 0 #ffd83a; + &:first-child { + border-color: #d8d8d8; + box-shadow: 0 0 0.5rem 0 #d8d8d8; + background-image: var(--logo-image), + linear-gradient(#d8d8d8, #eeeeee); + } + &:last-child { + border-color: #fab36e; + box-shadow: 0 0 0.5rem 0 #fab36e; + background-image: var(--logo-image), + linear-gradient(#fab36e, #fbe7d3); + } + } + } + h3 { + position: relative; + z-index: 2; + font-size: 2rem; + letter-spacing: 0.4rem; + width: 10rem; + cursor: default; + text-shadow: + 0px 1px 0px #c0c0c0, + 0px 2px 0px #b0b0b0, + 0px 3px 0px #a0a0a0, + 0px 4px 0px #909090, + 0px 5px 10px rgba(0, 0, 0, 0.6); + color: transparent; + font-family: "'book antiqua', palatino, serif"; + &::after { + content: attr(data-text); + display: block; + position: absolute; + z-index: 1; + top: 0; + left: 0; + width: 10rem; + margin: 0 auto; + text-shadow: none; + background-image: linear-gradient( + to right, + #d8d8d8 10%, + #ffd83a 60%, + #fab36e 75% + ); + background-clip: text; + } + } + } + .topUserUl { + z-index: 5; + li { + text-align: center; + cursor: default; + .showBox { + min-height: 6rem; + letter-spacing: 1px; + z-index: 3; + } + .imgBox { + z-index: 2; + animation: 3.5s suspension ease-in-out infinite; + transition: all 0.6s; + } + &:nth-child(1) { + order: 2; + padding-top: 0.3rem; + .showBox { + color: #ffd83a; + border-bottom: 1.5rem solid #ffd83a; + text-shadow: + 0px 1px 0px #969653, + 0px 2px 5px rgba(240, 219, 100, 0.4); + i { + width: 2.5rem; + height: 2.6rem; + background-image: var(--logo-image), + linear-gradient(#ffd83a, #fdeba0); + background-blend-mode: lighten; + background-position: center; + background-size: 2.5rem auto; + background-repeat: no-repeat; + } + } + .imgBox { + width: 6.4rem; + animation-delay: 3.5s; + } + } + &:nth-child(2) { + order: 1; + padding-top: 1rem; + .showBox { + color: #d8d8d8; + border-bottom: 1.2rem solid #d8d8d8; + text-shadow: 0px 1px 0px #d9cdcd 0px 2px 5px + rgba(133, 132, 130, 0.4); + i { + background-image: var(--logo-image), + linear-gradient(#d8d8d8, #eeeeee); + } + } + .imgBox { + width: 6rem; + animation-delay: 1.4s; + } + } + &:nth-child(3) { + order: 3; + padding-top: 1.5rem; + .showBox { + border-bottom: 1rem solid #fab36e; + color: #fab36e; + text-shadow: + 0px 1px 0px #aa8865, + 0px 2px 5px rgba(167, 129, 67, 0.4); + i { + background-image: var(--logo-image), + linear-gradient(#fab36e, #fbe7d3); + } + } + .imgBox { + width: 5.6rem; + animation-delay: 2.4s; + } + } + &:hover .imgBox { + animation-play-state: paused; + } + } + } + .topUserList { + tr { + &::before { + content: ''; + position: absolute; + display: block; + right: 0; + bottom: 0; + width: 0; + height: 2px; + background: #9fc2ef; + transition: all 0.5s; + } + &:hover::before { + left: 0; + width: 100%; + } + } + td { + cursor: default; + .usernameBox { + font-size: 0; + &:hover { + .imgBox > img { + transform: scale(1.2); + } + } + } + } + } +} diff --git a/source/UserRank/index.tsx b/source/UserRank/index.tsx new file mode 100644 index 0000000..84bcb53 --- /dev/null +++ b/source/UserRank/index.tsx @@ -0,0 +1,118 @@ +import { PureComponent } from 'react'; +import { Badge, Col, Image, Row, Table } from 'react-bootstrap'; + +import { UserAddress, UserAddressProps } from './Address'; +import * as style from './index.module.less'; + +export interface UserRank extends UserAddressProps { + id: number | string; + name: string; + avatar?: string; + score: number; +} + +export interface UserRankViewProps { + title: string; + value: UserRank[]; +} + +export class UserRankView extends PureComponent { + renderMedal = ({ id, name, avatar, email, website, score }: UserRank) => ( + +
+ {name} +
+
+
+ + + {name} + + {score} +
+ +
+ + ); + + renderRow = ( + { id, name, avatar, email, website, score }: UserRank, + index: number, + { length }: UserRank[] + ) => ( + + + + {(index + 4 + '').padStart(length, '0')} + + + +
+ {name} +
+ + {name} + + + {score} + + + + + ); + + render() { + const { title, value = [] } = this.props; + + return ( + + +
+
+ + + +
+

+ {title} +

+
+ + {value.slice(0, 3).map(this.renderMedal)} + + + + + {value.slice(3, 10).map(this.renderRow)} +
+ +
+ ); + } +} diff --git a/source/index.ts b/source/index.ts index cfd86f4..1c76f57 100644 --- a/source/index.ts +++ b/source/index.ts @@ -19,4 +19,5 @@ export * from './SpinnerButton'; export * from './TableSpinner'; export * from './TimeDistance'; export * from './TypeEcho'; +export * from './UserRank'; export * from './VerticalMarquee'; From 41ccd88491ee5967f04b1f5bc21d1bc62505651b Mon Sep 17 00:00:00 2001 From: TechQuery Date: Fri, 18 Oct 2024 21:35:34 +0800 Subject: [PATCH 2/2] [fix] some CSS & Props detail bugs --- preview/content.tsx | 23 ++++++++++-- source/HorizontalMarquee/index.tsx | 13 +++---- source/Icon.tsx | 2 +- source/UserRank/Address.tsx | 4 +-- source/UserRank/index.module.less | 2 ++ source/UserRank/index.tsx | 46 ++++++++++++------------ source/VerticalMarquee/index.module.less | 1 - source/VerticalMarquee/index.tsx | 26 +++++++++----- 8 files changed, 74 insertions(+), 43 deletions(-) diff --git a/preview/content.tsx b/preview/content.tsx index 9080eda..04388c0 100644 --- a/preview/content.tsx +++ b/preview/content.tsx @@ -9,6 +9,7 @@ import { CodeBlock, Dialog, DialogClose, + HorizontalMarquee, Icon, Loading, MonthCalendar, @@ -21,7 +22,8 @@ import { SpinnerButton, TimeDistance, TypeEcho, - UserRankView + UserRankView, + VerticalMarquee } from '../source'; import { CodeExample, Section } from './utility'; @@ -93,6 +95,22 @@ export class Content extends Component { +
+ + + {'idea2app '.repeat(15).trim()} + + +
+ +
+ + + + + +
+
@@ -225,7 +243,7 @@ export class Content extends Component { `/user/${id}`} />
diff --git a/source/HorizontalMarquee/index.tsx b/source/HorizontalMarquee/index.tsx index 0dbe84c..e6b83fb 100644 --- a/source/HorizontalMarquee/index.tsx +++ b/source/HorizontalMarquee/index.tsx @@ -1,18 +1,19 @@ -import { FC, PropsWithChildren } from 'react'; +import { FC, HTMLAttributes } from 'react'; import * as style from './index.module.less'; -export type HorizontalMarqueeProps = PropsWithChildren< - Partial> ->; +export type HorizontalMarqueeProps = HTMLAttributes & + Partial>; export const HorizontalMarquee: FC = ({ + className = '', children, maxWidth = '100%', duration, - height + height, + ...props }) => ( -
+
= ({ size, ...props }) => ( - >; export const UserAddress: FC = ({ email, website }) => ( -
+
{email && ( )} {website && ( - + )} diff --git a/source/UserRank/index.module.less b/source/UserRank/index.module.less index 6d952b1..ffc07f1 100644 --- a/source/UserRank/index.module.less +++ b/source/UserRank/index.module.less @@ -17,6 +17,7 @@ --logo-image: url('https://github.com/idea2app.png'); --title-background-image: url('https://hackathon-api.static.kaiyuanshe.cn/6342619375fa1817e0f56ce1/2022/10/09/rrrr.png'); a { + color: inherit; text-decoration: none; } .imgBox { @@ -212,6 +213,7 @@ } td { cursor: default; + background-color: transparent; .usernameBox { font-size: 0; &:hover { diff --git a/source/UserRank/index.tsx b/source/UserRank/index.tsx index 84bcb53..c06a053 100644 --- a/source/UserRank/index.tsx +++ b/source/UserRank/index.tsx @@ -13,16 +13,17 @@ export interface UserRank extends UserAddressProps { export interface UserRankViewProps { title: string; - value: UserRank[]; + rank: UserRank[]; + linkOf?: (user: UserRank) => string; } export class UserRankView extends PureComponent { - renderMedal = ({ id, name, avatar, email, website, score }: UserRank) => ( - + renderMedal = (user: UserRank) => ( +
- {name} + {user.name}
{ - {name} + {user.name} - {score} + {user.score}
- +
); - renderRow = ( - { id, name, avatar, email, website, score }: UserRank, - index: number, - { length }: UserRank[] - ) => ( - + renderRow = (user: UserRank, index: number, { length }: UserRank[]) => ( + {(index + 4 + '').padStart(length, '0')} @@ -59,27 +56,32 @@ export class UserRankView extends PureComponent {
- {name} + {user.name}
- {name} + {user.name} - {score} + {user.score} - + ); render() { - const { title, value = [] } = this.props; + const { title, rank = [] } = this.props; return ( @@ -101,7 +103,7 @@ export class UserRankView extends PureComponent { as="ul" className={`mt-2 g-0 align-items-end text-center ps-0 pt-2 list-unstyled ${style.topUserUl}`} > - {value.slice(0, 3).map(this.renderMedal)} + {rank.slice(0, 3).map(this.renderMedal)} @@ -109,7 +111,7 @@ export class UserRankView extends PureComponent { responsive className={`my-3 pt-2 ${style.topUserList}`} > - {value.slice(3, 10).map(this.renderRow)} + {rank.slice(3, 10).map(this.renderRow)} diff --git a/source/VerticalMarquee/index.module.less b/source/VerticalMarquee/index.module.less index ffaacdb..6441614 100644 --- a/source/VerticalMarquee/index.module.less +++ b/source/VerticalMarquee/index.module.less @@ -27,7 +27,6 @@ animation-iteration-count: infinite; animation-timing-function: linear; font-size: 0; - max-width: 100%; &:hover { animation-play-state: paused; .scrollItem { diff --git a/source/VerticalMarquee/index.tsx b/source/VerticalMarquee/index.tsx index b852846..3ac6031 100644 --- a/source/VerticalMarquee/index.tsx +++ b/source/VerticalMarquee/index.tsx @@ -1,18 +1,26 @@ -import { FC, PropsWithChildren } from 'react'; +import { FC, HTMLAttributes } from 'react'; import * as style from './index.module.less'; -export type VerticalMarqueeProps = PropsWithChildren<{ duration?: string }>; +export interface VerticalMarqueeProps extends HTMLAttributes { + duration?: string; +} export const VerticalMarquee: FC = ({ + className = '', children, - duration + duration, + ...props }) => ( -
-
{children}
+
+
+
+ {children} +
+
);