Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@


[![Forkers repo roster for @RobinYang11/goji](https://reporoster.com/forks/RobinYang11/goji)](https://github.com/RobinYang11/goji/network/members)

### contributors
<a href="https://github.com/mfts2048">
<img style="border-radius:10;width:60px" src="https://avatars.githubusercontent.com/u/44958959?s=120&v=4" />
Expand Down
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<div class="left">left</div>
<div class="right">right</div>
</div>
<div id="root"></div>
</body>

</html>
34 changes: 34 additions & 0 deletions src/_test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,41 @@ function App() {
/> */}

<TestTable />

<h1>test</h1>
<button
onClick={() => {
setVisible(true)
}}
>
show modal
</button>
<Tab
onTabChange={() => {
setEv(true)
}}
hiddenStyle={{
height: '0px',
overflow: 'hidden'
}}
tabContentVisible={ev}
extSelector={'[aria-label="tab"]'}
extension={<div onClick={() => { setEv(!ev) }} className="ext">这是扩展的内容</div>}
items={[
{
title: "tab1",
key: "tab1",
children: <div>tab1</div>
},
{
title: "tab2",
key: "tab2",
children: <div>tab2</div>
}
]}
/>

<Modal
<h1>test</h1>
<button
onClick={() => {
Expand Down
73 changes: 73 additions & 0 deletions src/react/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { memo, useState } from 'react'
import type { FC, ReactNode } from 'react'

import Table from './components/Table'
import Popover from './components/Popover'

interface IProps {
children?: ReactNode
}

const hobby = ['乒乓球', '篮球', '足球', '羽毛球', '网球']
const address = ['福州', '杭州', '江苏', '厦门', '深圳']

const data = () => {
return new Array<Record<string, any>>(72).fill({}).map((_, index) => ({
name: `姓名${index + 1}`,
age: Math.floor(Math.random() * 100),
hobby: hobby[Math.floor(Math.random() * 5)],
address: address[Math.floor(Math.random() * 5)]
}))
}

const cols = [
{
title: '姓名',
dataIndex: 'name',
key: 'name'
},
{
title: '年龄',
dataIndex: 'age',
key: 'age'
},
{
title: '爱好',
dataIndex: 'hobby',
key: 'hobby'
},
{
title: '住址',
dataIndex: 'address',
key: 'address'
}
]

const App: FC<IProps> = () => {
return (
<div style={{ marginTop: '100px' }}>
<div style={{ display: 'flex', gap: '50px' }}>
<Popover title='hover' placement='top'>
<button>hover</button>
</Popover>

<Popover title='click' trigger='click' placement='bottom'>
<button>click</button>
</Popover>
</div>

<br />

<div style={{ marginTop: '100px' }}>
<h2>Table组件</h2>
<h4>点击title栏进行排序(随机排序)</h4>
<h4>点击白色方块进行筛选</h4>
<h4>底部分页栏</h4>

<Table data={data()} cols={cols} />
</div>
</div>
)
}

export default memo(App)
74 changes: 74 additions & 0 deletions src/react/components/Pagination/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { memo, ReactElement, useMemo, useRef, useState } from 'react'
import type { FC, ReactNode } from 'react'

import styles from './styles.module.less'

interface IProps {
children?: ReactNode
pageNo: number
pageSize: number
totalPage: number
onChange?: (curPage: number) => void
}

const Pagination: FC<IProps> = props => {
const { totalPage, pageNo, pageSize, onChange } = props

const [pageInfo, setPageInfo] = useState({ pageNo, pageSize })

const isCan = (pageNo: number, totalPage: number) => {
if (pageNo <= 0) return false
if (pageNo > totalPage) return false
return true
}

const onPreOrNextPage = (type: 'next' | 'pre' = 'next') => {
const curPageNo = type === 'next' ? pageNo + 1 : pageNo - 1
if (isCan(curPageNo, totalPage)) {
setPageInfo({ ...pageInfo, pageNo: curPageNo })
onChange && onChange(curPageNo)
}
}
const onCurPageClick = (pageNo: number) => {
if (isCan(pageNo, totalPage)) {
setPageInfo({ ...pageInfo, pageNo })
onChange && onChange(pageNo)
}
}

const allPageEl = useMemo(() => {
const nodes = []
for (let i = 0; i < totalPage; i++) {
const isActive = pageInfo.pageNo === i + 1
const classnames = [
styles.paginationItem,
isActive ? styles.active : ''
].join(' ')
nodes.push(
<div
className={classnames}
key={i}
onClick={e => onCurPageClick(i + 1)}>
{i + 1}
</div>
)
}
return nodes
}, [totalPage, pageInfo])

return (
<div className={styles.pagination}>
<div
className={styles.paginationItem}
onClick={e => onPreOrNextPage('pre')}>
&lt;
</div>
<>{allPageEl}</>
<div className={styles.paginationItem} onClick={e => onPreOrNextPage()}>
&gt;
</div>
</div>
)
}

export default memo(Pagination)
26 changes: 26 additions & 0 deletions src/react/components/Pagination/styles.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.pagination {
display: flex;
margin-top: 8px;
gap: 8px;

.paginationItem {
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
color: #d3d3d3;
background: #141414;
border: 1px solid #434343;

&:hover {
cursor: pointer;
color: #177ddb;
border: 1px solid #177ddb;
}
}

.paginationItem.active {
color: #177ddb;
border: 1px solid #177ddb;
}
}
108 changes: 108 additions & 0 deletions src/react/components/Popover/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { CSSProperties, memo, useCallback, useState } from 'react'

import type { FC, ReactNode } from 'react'

import styles from './styles.module.less'

interface IProps {
children?: ReactNode // 默认元素
content?: ReactNode // 展示内容元素
title?: string | ReactNode // 标题
action?: string | ReactNode // 底部操作栏
// position?: number // popover 偏移位置
trigger?: 'hover' | 'click' // 如何触发
placement?: 'top' | 'bottom' // popover 出现位置
}

/**
* TODO
* 1. 只能作用在行内元素,如果多个popover层叠排版会显示在最上层
* 2. 如果元素设置了偏移,显示位置不对
*/

const Popover: FC<IProps> = props => {
const {
children,
content,
title,
// position = 0,
trigger = 'hover',
placement = 'bottom'
} = props
const [show, setShow] = useState(false)
const [modalStyle, setModalStyle] = useState<CSSProperties>({})
const childrenRef = useCallback((node: HTMLDivElement) => {
if (node !== null) {
let rect = node.getBoundingClientRect()
if (node.children[0]) {
rect = node.children[0].getBoundingClientRect()
}
setModalStyle(setupModalStyle(rect))
}
}, [])

const setupModalStyle = (rect: DOMRect) => {
const position: Record<'top' | 'bottom', CSSProperties> = {
top: {
transform: `translate(${0}px, ${-rect.bottom + 60}px)`
},
bottom: {
transform: `translate(${0}px, ${16}px)`
}
}
return position[placement]
}

/* 三角形位置 */
const triangleClassNames = [
styles.triangle,
placement === 'top' ? styles.triangleTop : styles.triangleBottom
].join(' ')

/* 事件处理 */
const onMouseEnter = () => {
if (show) return
trigger == 'hover' && setShow(true)
}
const onMouseLeave = () => trigger === 'hover' && setShow(false)

const onMouseClick = () => trigger === 'click' && setShow(!show)

return (
<div className={styles.popover} onClick={e => e.stopPropagation()}>
<div
ref={childrenRef}
className={styles.children}
onMouseEnter={e => {
e.stopPropagation()
onMouseEnter()
}}
onMouseLeave={e => {
e.stopPropagation()
onMouseLeave()
}}
onClick={e => {
e.stopPropagation()
onMouseClick()
}}>
{children}
</div>
<div
className={styles.modal}
style={{ ...modalStyle, display: show ? 'block' : 'none' }}>
<div className={triangleClassNames}></div>
{title && (
<div className={[styles.title, styles.layout].join(' ')}>{title}</div>
)}
{content && (
<div className={[styles.content, styles.layout].join(' ')}>
{content}
</div>
)}
<div className={styles.action}></div>
</div>
</div>
)
}

export default memo(Popover)
46 changes: 46 additions & 0 deletions src/react/components/Popover/styles.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.popover {
position: relative;

.children {
width: 100%;
}

.modal {
position: absolute;
color: #d4d4d4;
background: #1f1f1f;
z-index: 1;

.triangle {
position: absolute;
width: 0;
height: 0;
}

.triangleTop {
bottom: -16px;
left: 8px;
border-top: 8px solid #1f1f1f;
border-right: 8px solid transparent;
border-bottom: 8px solid transparent;
border-left: 8px solid transparent;
}

.triangleBottom {
top: -16px;
left: 8px;
border-top: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid #1f1f1f;
border-left: 8px solid transparent;
}

.layout {
padding: 8px;
}

.title {
border-bottom: 1px solid #303030;
}
}
}
Loading