feat: first

This commit is contained in:
EleanorMao
2023-04-22 23:00:38 +08:00
parent edf473c172
commit 5ae1d9c5b2
44 changed files with 10913 additions and 16089 deletions

View File

@ -0,0 +1,8 @@
import React, { FC, PropsWithChildren } from 'react'
import { BreakpointProps, useBreakpoint } from './useBreakpoint'
export const Breakpoint: FC<PropsWithChildren<BreakpointProps>> = ({ children, ...props }) => {
const enabled = useBreakpoint(props)
return enabled ? <>{children}</> : null
}

View File

@ -0,0 +1,25 @@
import React, { FC, PropsWithChildren } from 'react'
import { Size, useSize } from './useSize'
export const breakpointList = {
xs: 0,
s: 576,
m: 768,
l: 992,
xl: 1200,
}
export interface BreakpointContextProps {
size: Size,
breakpointList: typeof breakpointList
}
export const BreakpointContext = React.createContext<BreakpointContextProps>({ breakpointList, size: {} } as any)
export const BreakpointProvider: FC<PropsWithChildren> = ({ children }) => {
const size = useSize()
return (
<BreakpointContext.Provider value={{ size, breakpointList }}>{children}</BreakpointContext.Provider>
)
}

View File

@ -0,0 +1,3 @@
export { BreakpointProvider } from './BreakpointProvider'
export { useBreakpoint } from './useBreakpoint'
export { Breakpoint } from './Breakpoint'

View File

@ -0,0 +1,81 @@
import { useContext } from 'react';
import { BreakpointContext } from './BreakpointProvider'
import { uaParser } from '../../utils/uaParser'
export interface DeviceType {
mobile?: boolean;
tablet?: boolean;
desktop?: boolean;
}
export interface OrientationType {
landscape?: boolean
portrait?: boolean
}
export interface Breakpoints {
xs?: boolean
s?: boolean
m?: boolean
l?: boolean
xl?: boolean
// greater than the width
up?: boolean
// less than the width
down?: boolean
width?: {
min?: number
max?: number
}
}
export type BreakpointProps = DeviceType & Breakpoints & OrientationType
const deviceType = ['mobile', 'tablet', 'desktop']
const currentDeviceType = ['mobile', 'tablet'].includes(uaParser.device.model || '') ? uaParser.device.model : 'desktop'
function matchWidth(width: number, min?: number, max?: number): boolean {
if (typeof min === 'number' && typeof max === 'number') {
return width >= min && width <= max
}
if (typeof min === 'number') {
return width >= min
}
if (typeof max === 'number') {
return width <= max
}
return false
}
export const useBreakpoint = (props: BreakpointProps): boolean => {
const { breakpointList, size: { orientation, ...size } } = useContext(BreakpointContext)
let enabled: boolean
const allKeys = Object.keys(props)
const keysWithoutDeviceType = allKeys.filter(key => !deviceType.includes(key))
const keysWithDeviceType = allKeys.filter(key => deviceType.includes(key))
enabled = keysWithoutDeviceType.every(key => {
if (typeof breakpointList[key as keyof typeof breakpointList] === 'number' && !props.width) {
if (props.up) {
return size.width > breakpointList[key as keyof typeof breakpointList]
}
if (props.down) {
return size.width <= breakpointList[key as keyof typeof breakpointList]
}
return size.width === breakpointList[key as keyof typeof breakpointList]
}
if (key === 'width') {
return matchWidth(3000, props.width?.min, props.width?.max)
}
if (key === 'landscape' || key === 'portrait') {
return key === orientation
}
return true
}) && (keysWithDeviceType.length ? keysWithDeviceType.some(key => currentDeviceType === key) : true)
return enabled
}

View File

@ -0,0 +1,56 @@
import { useCallback, useLayoutEffect, useState } from 'react'
type Orientation = "portrait" | "landscape"
function getOrientation(): Orientation {
if (
window.screen.orientation &&
Object.prototype.hasOwnProperty.call(window, 'onorientationchange')
) {
return window.screen.orientation.type.includes('portrait') ? 'portrait' : 'landscape'
}
if (
Object.prototype.hasOwnProperty.call(window, 'orientation')
) {
return Math.abs(window.orientation) !== 90 ? 'portrait' : 'landscape'
}
return window.innerHeight / window.innerWidth > 1 ? "portrait" : "landscape"
}
export interface Size {
width: number
height: number
orientation: Orientation
}
export function useSize():Size {
const [width, setWidth] = useState<number>(document.documentElement.clientWidth)
const [height, setHeight] = useState<number>(document.documentElement.clientHeight)
const [orientation, setOrientation] = useState<Orientation>(getOrientation())
const resizeListener = useCallback(() => {
setWidth(document.documentElement.clientWidth)
setHeight(document.documentElement.clientHeight)
}, [])
const orientationListener =useCallback( () => {
setOrientation(getOrientation())
},[])
useLayoutEffect(() => {
window.addEventListener('resize', resizeListener)
window.addEventListener('orientationchange', orientationListener)
return () => {
window.removeEventListener('resize', resizeListener)
window.removeEventListener('orientationchange', orientationListener)
}
})
return {
width,
height,
orientation
}
}

View File

@ -0,0 +1,9 @@
import React, { FC } from 'react'
import { Button as AntdButton, ButtonProps as AntdButtonProps } from 'antd'
export type ButtonProps = Omit<AntdButtonProps, 'danger' | 'ghost' | 'shape' | 'size' | 'type'> & {
type?: 'primary' | 'link' | 'text'
}
export const Button: FC<ButtonProps> = (props) => {
return <AntdButton type="primary" {...props} />
}

View File

@ -0,0 +1 @@
export * from './Button'

View File

@ -0,0 +1,8 @@
import React, { FC } from 'react'
import { Modal as AntdModal, ModalProps as AntdModalProps } from 'antd'
import { ReactComponent as CloseIcon } from '../../icons/close.svg'
export type ModalProps = Omit<AntdModalProps, 'footer' | 'closeIcon' | 'title' | 'okText' | 'okType' | 'okButtonProps' | 'cancelText' | 'cancelButtonProps'>
export const Modal: FC<ModalProps> = (props) => {
return <AntdModal centered {...props} footer={null} closeIcon={<CloseIcon />} />
}

View File

@ -0,0 +1 @@
export * from './Modal'

View File

@ -0,0 +1,24 @@
import React, { FC, PropsWithChildren } from 'react'
import styled from 'styled-components'
const StyledStepIcon = styled.div<{ $active?: boolean }>`
background: ${props => props.$active ? '#FFAB83' : '#FFFFFF'};
border: 1px solid ${props => props.$active ? '#FFAB83' : '#FF5200'};
width: 24px;
height: 24px;
line-height: 24px;
margin-top: 0;
margin-bottom: 0;
margin-inline-start: 0;
margin-inline-end: 8px;
font-size: 12px;
color: #1E1D1F;
font-weight: 400;
text-align: center;
border-radius: 50%;
transition: background-color .3s, border-color .3s;
`
export const StepIcon: FC<PropsWithChildren<{ active: boolean }>> = ({ children, active }) => {
return <StyledStepIcon $active={active}>{children}</StyledStepIcon>
}

View File

@ -0,0 +1,43 @@
import React, { FC } from 'react'
import { Steps as AntdSteps, StepsProps as AntdStepsProps } from 'antd'
import { StepProps } from 'antd/es/steps'
import styled from 'styled-components'
import {StepIcon} from './StepIcon'
export type StepsProps = Omit<AntdStepsProps, 'labelPlacement' | 'size' | 'status' | 'type' | 'items'> & {
items: Omit<StepProps, 'icon' | 'description'>[]
}
const StyledSteps = styled(AntdSteps)`
&.ant-steps-label-vertical {
.ant-steps-item-content {
margin-top: 5px;
}
.ant-steps-item-tail {
top: 8px;
margin-inline-start: 53px;
padding: 4px 12px;
}
}
.ant-steps-item-title {
font-size: 12px;
line-height: 14px;
}
.ant-steps-item-finish > .ant-steps-item-container > .ant-steps-item-content > .ant-steps-item-title {
color: #8F9296;
}
`
export const Steps: FC<StepsProps> = ({ items, current = 0, ...props }) => {
const formattedItems = items.map((item, index) => {
return {
...item,
icon: <StepIcon active={index <= current}>{index + 1}</StepIcon>
}
})
return (
<StyledSteps labelPlacement="vertical" {...props} current={current} items={formattedItems} />
)
}

View File

@ -0,0 +1 @@
export * from './Steps'

View File

@ -0,0 +1,21 @@
import React, { PropsWithChildren } from 'react'
import styled from 'styled-components'
import {TinyText} from '../Typography'
const StyledTag = styled.div`
background: #FFAB83;
border-radius: 7px;
text-align: center;
height: 14px;
line-height: 14px;
display: inline-block;
> span {
position: relative;
display: inline-block;
top: -4px;
}
`
export const Tag: React.FC<PropsWithChildren> = (props) => {
return <StyledTag><TinyText>{props.children}</TinyText></StyledTag>
}

View File

@ -0,0 +1 @@
export * from './Tag'

View File

@ -0,0 +1,33 @@
import React, { FC, useCallback } from 'react'
import { Tooltip as AntdTooltip, TooltipProps as AntdTooltipProps } from 'antd'
import { ReactComponent as CloseIcon } from '../../icons/close.svg'
import styled from 'styled-components'
export type TooltipProps = AntdTooltipProps & { closeable?: boolean; primary?: boolean }
const Close = styled.span`
line-height: 17px;
vertical-align: middle;
cursor: pointer;
padding-left: 4px;
> svg {
width: 14px;
height: 14px;
line-height: 17px;
}
`
export const Tooltip: FC<TooltipProps> = ({ closeable, primary, title, overlayClassName, ...props }) => {
const handleClick = useCallback(() => {
document.documentElement.click()
}, [])
return (
<AntdTooltip
trigger="click"
overlayClassName={`health-tooltip${primary ? '-highlighted' : ''} ${overlayClassName || ''}`}
{...props}
title={<>{title} {closeable ? <Close><CloseIcon onClick={handleClick} /></Close> : null}</>}
/>
)
}

View File

@ -0,0 +1 @@
export * from './Tooltip'

View File

@ -0,0 +1,22 @@
import React, { FC, ComponentProps } from 'react'
import { Typography, TypographyProps } from 'antd'
import styled from 'styled-components'
type ButtonTextProps = ComponentProps<TypographyProps['Text']> & {
level?: 1 | 2
}
const StyledButtonText = styled(Typography.Text)<{ $level: 1 | 2 }>`
${props => props.$level === 1 ? `
font-weight: 500;
font-size: 20px;
line-height: 20px;
` : `
font-weight: 400;
font-size: 12px;
line-height: 14px;
`}
`
export const ButtonText: FC<ButtonTextProps> = ({ level = 1, ...props }) => {
return <StyledButtonText {...props} $level={level} />
}

View File

@ -0,0 +1,17 @@
import React, { FC, ComponentProps } from 'react'
import { Typography, TypographyProps } from 'antd'
import styled from 'styled-components'
type AdditionalParagraphProps = ComponentProps<TypographyProps['Paragraph']> & {
level?: 1 | 2
}
const StyledParagraph2 = styled(Typography.Paragraph)`
font-size: 12px;
`
export const Paragraph: FC<AdditionalParagraphProps> = ({ level = 1, ...props }) => {
if (level === 2) {
return <StyledParagraph2 {...props} />
}
return <Typography.Paragraph {...props} />
}

View File

@ -0,0 +1,16 @@
import React, { FC, ComponentProps } from 'react'
import { Typography, TypographyProps } from 'antd'
import styled from 'styled-components'
type TinyTextProps = ComponentProps<TypographyProps['Text']>
const StyledTinyText = styled(Typography.Text)`
font-weight: 400;
font-size: 12px;
transform: scale(0.66667);
line-height: 1.75;
color: #1E1D1F;
`
export const TinyText: FC<TinyTextProps> = (props) => {
return <StyledTinyText {...props} />
}

View File

@ -0,0 +1,8 @@
import { Typography } from 'antd'
const { Text, Title, Link } = Typography
export * from './Paragraph'
export * from './ButtonText'
export * from './TinyText'
export { Text, Title, Link }