-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Dony] headless Table 구현 #95
Closed
Closed
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
0d98705
:sparkles: add ColumnTable, RowTable, BasicTable components.
jindonyy ab673ea
:sparkles: add sort function in ColumnTable.
jindonyy f1f2d2b
:recycle: add throw Error to sortRows, add restProps to Row, Cell.
jindonyy d03c589
:lipstick: add cursor css to th tag. change className to data attribute.
jindonyy 9fac1fd
:sparkles: add Button component to ColumnTable head cell.
jindonyy ea4a500
:white_check_mark: add ColumnTable storybook.
jindonyy fad7793
:sparkles: add sort function in RowTable.
jindonyy 676663b
:recycle: remove context in ColumnTable.
jindonyy 33fb57f
:recycle: Replace Table cponents by headScope used as table body with…
jindonyy 1b8fe51
:lipstick: add offscreen commonStyle.
jindonyy 9d3a51c
:bug: fix RowTable sort function bug.
jindonyy 4cff3cd
:white_check_mark: add RowTable, BasicTable storybook.
jindonyy a907381
:fire: remove test code in RowTable.
jindonyy 711d5ca
:recycle: separate Cell component in ColumnTable, RowTable.
jindonyy 9ccbc6b
:white_check_mark: refact styled code in Table components.
jindonyy 0c82af9
:recycle: refact sort table function.
jindonyy File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { ReactElement, ReactNode } from 'react'; | ||
|
||
import { offscreen } from '@styles/commonStyles'; | ||
|
||
export type BasicTableProps = { | ||
caption: string; | ||
columnsWidth?: string[]; | ||
children: (ReactElement | ReactElement[])[]; | ||
}; | ||
|
||
const BasicTable = ({ | ||
caption, | ||
columnsWidth, | ||
children, | ||
...restProps | ||
}: BasicTableProps) => ( | ||
<table {...restProps}> | ||
<caption css={offscreen}>{caption}</caption> | ||
{columnsWidth && ( | ||
<colgroup> | ||
{columnsWidth.map((width, index) => ( | ||
// 유동적으로 변하지 않는 리스트 | ||
// eslint-disable-next-line react/no-array-index-key | ||
<col key={index} width={width} /> | ||
))} | ||
</colgroup> | ||
)} | ||
<tbody>{children}</tbody> | ||
</table> | ||
); | ||
|
||
type RowProps = { | ||
children: ReactElement | ReactElement[]; | ||
}; | ||
|
||
const Row = ({ children, ...restProps }: RowProps) => ( | ||
<tr {...restProps}>{children}</tr> | ||
); | ||
|
||
type CellProps = { | ||
colSpan?: number; | ||
rowSpan?: number; | ||
children: ReactNode; | ||
}; | ||
|
||
const Cell = ({ children, ...restProps }: CellProps) => ( | ||
<td {...restProps}>{children}</td> | ||
); | ||
|
||
BasicTable.Row = Row; | ||
BasicTable.Cell = Cell; | ||
|
||
export default BasicTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { ReactNode, Dispatch, SetStateAction } from 'react'; | ||
|
||
import Button from '@components/common/Button'; | ||
|
||
const DIRECTION = { | ||
ASCENDING: 'ascending', | ||
DESCENDING: 'descending', | ||
}; | ||
|
||
export type SortState = { | ||
name: string; | ||
direction: typeof DIRECTION[keyof typeof DIRECTION]; | ||
sortIndices: number[]; | ||
} | null; | ||
|
||
export type SortConfig = { | ||
sortValues?: { [key in string]: (string | number)[] }; | ||
sortState: SortState; | ||
setSortState: Dispatch<SetStateAction<SortState>>; | ||
}; | ||
|
||
type CellProps = { | ||
colSpan?: number; | ||
rowSpan?: number; | ||
name?: string; | ||
sortConfig?: SortConfig; | ||
children: ReactNode; | ||
}; | ||
|
||
interface HeadCellProps extends CellProps { | ||
scope?: 'col' | 'row'; | ||
sortConfig: SortConfig; | ||
} | ||
|
||
const HeadCell = ({ | ||
name = '', | ||
scope, | ||
sortConfig, | ||
children, | ||
...restProps | ||
}: HeadCellProps) => { | ||
const { sortValues = {}, sortState, setSortState } = sortConfig; | ||
const direction = | ||
sortState && | ||
sortState.name === name && | ||
sortState.direction === DIRECTION.DESCENDING | ||
? DIRECTION.ASCENDING | ||
: DIRECTION.DESCENDING; | ||
|
||
const getSortIndices = () => { | ||
const sortValuesByName = sortValues[name].map((value, index) => ({ | ||
value, | ||
index, | ||
})); | ||
sortValuesByName.sort((a, b) => { | ||
if (a.value > b.value) return direction === DIRECTION.ASCENDING ? 1 : -1; | ||
if (a.value < b.value) return direction === DIRECTION.ASCENDING ? -1 : 1; | ||
return 0; | ||
}); | ||
|
||
return sortValuesByName.map(({ index }) => index); | ||
}; | ||
Comment on lines
+50
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updateSortState를 실행할 때 매번 이 연산을 수행하게 되는데 같은 컬럼을 여러번 클릭하는 경우에는 매번 계산하는 게 불필요할 수 있을 것 같아요! 현재 sortState에 있는 name과 같다면 가지고 있는 sortIndices를 reverse해서 반환해줘도 되지 않을까요? |
||
|
||
const updateSortState = () => { | ||
if (!sortValues[name]) | ||
throw new Error('Please set sortValues[name] in Table props.'); | ||
|
||
const newSortState = { | ||
name, | ||
direction, | ||
sortIndices: getSortIndices(), | ||
}; | ||
setSortState(newSortState); | ||
}; | ||
|
||
return ( | ||
<th | ||
scope={scope} | ||
data-sort={sortState?.name === name ? sortState?.direction : undefined} | ||
{...restProps} | ||
> | ||
{name ? ( | ||
<Button type="button" onClick={updateSortState}> | ||
{children} | ||
</Button> | ||
) : ( | ||
children | ||
)} | ||
</th> | ||
); | ||
}; | ||
|
||
const BodyCell = ({ children, ...restProps }: CellProps) => ( | ||
<td {...restProps}>{children}</td> | ||
); | ||
|
||
const Cell = ({ name, sortConfig, children, ...restProps }: CellProps) => | ||
sortConfig ? ( | ||
<HeadCell sortConfig={sortConfig} name={name} {...restProps}> | ||
{children} | ||
</HeadCell> | ||
) : ( | ||
<BodyCell {...restProps}>{children}</BodyCell> | ||
); | ||
|
||
export default Cell; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React, { cloneElement, ReactElement, useState } from 'react'; | ||
|
||
import Cell, { SortState, SortConfig } from '@components/common/Table/Cell'; | ||
import { offscreen } from '@styles/commonStyles'; | ||
|
||
export type ColumnTableProps = { | ||
caption: string; | ||
columnsWidth?: string[]; | ||
sortValues?: { [key in string]: (string | number)[] }; | ||
children: (ReactElement | ReactElement[])[]; | ||
}; | ||
|
||
const ColumnTable = ({ | ||
caption, | ||
columnsWidth, | ||
sortValues, | ||
children, | ||
...restProps | ||
}: ColumnTableProps) => { | ||
const Children = React.Children.toArray(children); | ||
const [headRow, bodyRows] = [Children[0], Children.splice(1)]; | ||
const [sortState, setSortState] = useState<SortState>(null); | ||
const sortConfig = { sortValues, sortState, setSortState }; | ||
|
||
return ( | ||
<table {...restProps}> | ||
<caption css={offscreen}>{caption}</caption> | ||
{columnsWidth && ( | ||
<colgroup> | ||
{columnsWidth.map((width, index) => ( | ||
// 유동적으로 변하지 않는 리스트 | ||
// eslint-disable-next-line react/no-array-index-key | ||
<col key={index} width={width} /> | ||
))} | ||
</colgroup> | ||
)} | ||
<thead> | ||
{cloneElement(headRow as ReactElement, { scope: 'col', sortConfig })} | ||
</thead> | ||
<tbody> | ||
{sortState | ||
? sortState.sortIndices.map((index) => bodyRows[index]) | ||
: bodyRows} | ||
</tbody> | ||
</table> | ||
); | ||
}; | ||
|
||
type RowProps = { | ||
scope?: 'col'; | ||
sortConfig?: SortConfig; | ||
children: ReactElement | ReactElement[]; | ||
}; | ||
|
||
const Row = ({ scope, sortConfig, children, ...restProps }: RowProps) => ( | ||
<tr {...restProps}> | ||
{sortConfig | ||
? React.Children.toArray(children).map((child) => | ||
cloneElement(child as ReactElement, { scope, sortConfig }), | ||
) | ||
: children} | ||
</tr> | ||
); | ||
|
||
ColumnTable.Row = Row; | ||
ColumnTable.Cell = Cell; | ||
|
||
export default ColumnTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import React, { cloneElement, ReactElement, useState } from 'react'; | ||
|
||
import Cell, { SortState, SortConfig } from '@components/common/Table/Cell'; | ||
import { offscreen } from '@styles/commonStyles'; | ||
|
||
export type RowTableProps = { | ||
caption: string; | ||
columnsWidth?: string[]; | ||
sortValues?: { [key in string]: (string | number)[] }; | ||
children: ReactElement | ReactElement[]; | ||
}; | ||
|
||
const RowTable = ({ | ||
caption, | ||
columnsWidth, | ||
sortValues, | ||
children, | ||
...restProps | ||
}: RowTableProps) => { | ||
const [sortState, setSortState] = useState<SortState>(null); | ||
const sortConfig = { sortValues, sortState, setSortState }; | ||
|
||
return ( | ||
<table {...restProps}> | ||
<caption css={offscreen}>{caption}</caption> | ||
{columnsWidth && ( | ||
<colgroup> | ||
{columnsWidth.map((width, index) => ( | ||
// 유동적으로 변하지 않는 리스트 | ||
// eslint-disable-next-line react/no-array-index-key | ||
<col key={index} width={width} /> | ||
))} | ||
</colgroup> | ||
)} | ||
<tbody> | ||
{React.Children.toArray(children).map((child) => | ||
cloneElement(child as ReactElement, { scope: 'row', sortConfig }), | ||
)} | ||
</tbody> | ||
</table> | ||
); | ||
}; | ||
|
||
type RowProps = { | ||
scope?: 'row'; | ||
sortConfig?: SortConfig; | ||
children: (ReactElement | ReactElement[])[]; | ||
}; | ||
|
||
const Row = ({ scope, sortConfig, children, ...restProps }: RowProps) => { | ||
const Children = React.Children.toArray(children); | ||
const [headCell, bodyCells] = [Children[0], Children.splice(1)]; | ||
|
||
return ( | ||
<tr {...restProps}> | ||
{cloneElement(headCell as ReactElement, { scope, sortConfig })} | ||
{sortConfig?.sortState | ||
? sortConfig.sortState.sortIndices.map((index) => bodyCells[index]) | ||
: bodyCells} | ||
</tr> | ||
); | ||
}; | ||
|
||
RowTable.Row = Row; | ||
RowTable.Cell = Cell; | ||
|
||
export default RowTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import BasicTable from '@components/common/Table/BasicTable'; | ||
import ColumnTable from '@components/common/Table/ColumnTable'; | ||
import RowTable from '@components/common/Table/RowTable'; | ||
|
||
export { BasicTable, ColumnTable, RowTable }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
별 건 아니지만 문득 도니가 사용하신 [key in string]과 [key: string]이 무슨 차이인지 궁금해서 찾아봤었는데요! 제 생각엔 [key: string] 쪽이 좀 더 적절하지 않나 싶었던 것 같아요.
관련해서 정리한 링크입니다!
그리고 sortState가 현재 어떤 것을 기준으로 정렬하고 있는지에 관한 정보니까 이런 부분을 좀 더 명확히 나타낼 수 있는 이름이면 더 좋겠다는 생각도 드네요!