mirror of
https://github.com/acid-info/lsd.git
synced 2025-02-04 13:13:45 +00:00
feat: add table footer component and minor CSS updates
This commit is contained in:
parent
a7bfa14d9d
commit
067c2b0235
@ -32,6 +32,7 @@ import { TagStyles } from '../Tag/Tag.styles'
|
||||
import { TextFieldStyles } from '../TextField/TextField.styles'
|
||||
import { defaultThemes, Theme, withTheme } from '../Theme'
|
||||
import { TypographyStyles } from '../Typography/Typography.styles'
|
||||
import { TableFooterStyles } from '../TableFooter/TableFooter.styles'
|
||||
|
||||
const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
|
||||
[
|
||||
@ -66,6 +67,7 @@ const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
|
||||
TableBodyStyles,
|
||||
TableItemStyles,
|
||||
TableRowStyles,
|
||||
TableFooterStyles,
|
||||
]
|
||||
|
||||
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({
|
||||
|
@ -57,8 +57,13 @@ const headerOptions = new Array(8).fill(null).map((value, index) => ({
|
||||
name: `Title ${index + 1}`,
|
||||
}))
|
||||
|
||||
export const Root: Story<TableProps> = ({ type, ...args }) => {
|
||||
export const Root: Story<TableProps> = ({ type, pages, ...args }) => {
|
||||
const [rows, setRows] = useState(1)
|
||||
const [footerContent, setFooterContent] = useState('Footer content goes here')
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
setFooterContent(`Page ${page} of ${pages}`)
|
||||
}
|
||||
|
||||
const toolbar = (
|
||||
<>
|
||||
@ -105,7 +110,10 @@ export const Root: Story<TableProps> = ({ type, ...args }) => {
|
||||
header={header}
|
||||
toolbar={toolbar}
|
||||
headerOptions={headerOptions}
|
||||
footerContent={footerContent}
|
||||
pages={pages}
|
||||
{...args}
|
||||
onPageChange={onPageChange}
|
||||
>
|
||||
<TableRow>
|
||||
{headerOptions.map((item) => (
|
||||
@ -123,4 +131,6 @@ export const Root: Story<TableProps> = ({ type, ...args }) => {
|
||||
Root.args = {
|
||||
size: 'large',
|
||||
type: 'default',
|
||||
hasFooter: true,
|
||||
pages: 10,
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { TableBody } from '../TableBody'
|
||||
import { TableHeader } from '../TableHeader'
|
||||
import { tableClasses } from './Table.classes'
|
||||
import { TableContext } from './Table.context'
|
||||
import { TableFooter } from '../TableFooter'
|
||||
|
||||
export type TableProps = CommonProps &
|
||||
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
|
||||
@ -18,6 +19,10 @@ export type TableProps = CommonProps &
|
||||
headerOptions?: DropdownOption[]
|
||||
header?: React.ReactNode
|
||||
toolbar?: React.ReactNode
|
||||
hasFooter?: boolean
|
||||
pages: number
|
||||
onPageChange?: (page: number) => void
|
||||
footerContent?: React.ReactNode
|
||||
}
|
||||
|
||||
export const Table: React.FC<TableProps> & {
|
||||
@ -28,7 +33,11 @@ export const Table: React.FC<TableProps> & {
|
||||
headerOptions,
|
||||
header,
|
||||
toolbar,
|
||||
hasFooter,
|
||||
onPageChange,
|
||||
pages,
|
||||
children,
|
||||
footerContent,
|
||||
...props
|
||||
}) => {
|
||||
const commonProps = useCommonProps(props)
|
||||
@ -47,6 +56,11 @@ export const Table: React.FC<TableProps> & {
|
||||
<TableBody toolbar={toolbar} options={headerOptions}>
|
||||
{children}
|
||||
</TableBody>
|
||||
{hasFooter && !!footerContent && (
|
||||
<TableFooter onPageChange={onPageChange} pages={pages} size={size}>
|
||||
{footerContent}
|
||||
</TableFooter>
|
||||
)}
|
||||
</div>
|
||||
</TableContext.Provider>
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ export const TableBodyStyles = css`
|
||||
|
||||
.${tableBodyClasses.toolbar} {
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
padding: 15px;
|
||||
border: 1px solid rgb(var(--lsd-border-primary));
|
||||
border-bottom: none;
|
||||
display: flex;
|
||||
|
@ -0,0 +1,8 @@
|
||||
export const tableFooterClasses = {
|
||||
root: `lsd-table-footer`,
|
||||
large: 'lsd-table-footer--large',
|
||||
medium: 'lsd-table-footer--medium',
|
||||
small: 'lsd-table-footer--small',
|
||||
content: 'lsd-table-footer__content',
|
||||
paginationControls: 'lsd-table-footer__pagination-controls',
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { Meta, Story } from '@storybook/react'
|
||||
import { TableFooter, TableFooterProps } from './TableFooter'
|
||||
import { useState } from 'react'
|
||||
import { pickCommonProps } from '../../utils/useCommonProps'
|
||||
|
||||
export default {
|
||||
title: 'TableFooter',
|
||||
component: TableFooter,
|
||||
argTypes: {
|
||||
size: {
|
||||
type: {
|
||||
name: 'enum',
|
||||
value: ['small', 'medium', 'large'],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as Meta
|
||||
|
||||
export const Root: Story<TableFooterProps & { title: string }> = ({
|
||||
size,
|
||||
pages,
|
||||
...props
|
||||
}) => {
|
||||
const [content, setContent] = useState('Footer content goes here')
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
setContent(`Page ${page} of ${pages}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '800px' }}>
|
||||
<TableFooter
|
||||
{...pickCommonProps(props)}
|
||||
pages={pages}
|
||||
onPageChange={onPageChange}
|
||||
size={size}
|
||||
>
|
||||
{content}
|
||||
</TableFooter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Root.args = {
|
||||
size: 'large',
|
||||
pages: 10,
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
import { css } from '@emotion/react'
|
||||
import { tableFooterClasses } from './TableFooter.classes'
|
||||
import { iconButtonClasses } from '../IconButton/IconButton.classes'
|
||||
import { dropdownClasses } from '../Dropdown/Dropdown.classes'
|
||||
import { typographyClasses } from '../Typography/Typography.classes'
|
||||
|
||||
export const TableFooterStyles = css`
|
||||
.${tableFooterClasses.root} {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgb(var(--lsd-border-primary));
|
||||
|
||||
.${dropdownClasses.root} {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.${dropdownClasses.buttonContainer} {
|
||||
box-sizing: border-box;
|
||||
border-left: 0px !important;
|
||||
border-right: 0px !important;
|
||||
}
|
||||
|
||||
.${dropdownClasses.trigger} {
|
||||
padding-left: 16px !important;
|
||||
padding-right: 16px !important;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.${dropdownClasses.icons} {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.${tableFooterClasses.content} {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.${tableFooterClasses.paginationControls} {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.${tableFooterClasses.large} {
|
||||
height: 64px;
|
||||
|
||||
.${iconButtonClasses.large} {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.${dropdownClasses.buttonContainer} {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.${tableFooterClasses.paginationControls} {
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.${tableFooterClasses.medium} {
|
||||
height: 60px;
|
||||
|
||||
.${iconButtonClasses.medium} {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.${dropdownClasses.buttonContainer} {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.${tableFooterClasses.paginationControls} {
|
||||
padding: 0 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.${tableFooterClasses.small} {
|
||||
height: 56px;
|
||||
|
||||
.${iconButtonClasses.small} {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
.${typographyClasses.label1} {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.${dropdownClasses.buttonContainer} {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.${tableFooterClasses.paginationControls} {
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
`
|
107
packages/lsd-react/src/components/TableFooter/TableFooter.tsx
Normal file
107
packages/lsd-react/src/components/TableFooter/TableFooter.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import clsx from 'clsx'
|
||||
import React, { useEffect, useReducer, useState } from 'react'
|
||||
import {
|
||||
CommonProps,
|
||||
omitCommonProps,
|
||||
useCommonProps,
|
||||
} from '../../utils/useCommonProps'
|
||||
import { tableFooterClasses } from './TableFooter.classes'
|
||||
import { table } from 'console'
|
||||
import { IconButton } from '../IconButton'
|
||||
import { NavigateBeforeIcon, NavigateNextIcon } from '../Icons'
|
||||
import { Dropdown, DropdownOption } from '../Dropdown'
|
||||
|
||||
// 1. Define the reducer and its actions
|
||||
type Action =
|
||||
| { type: 'SET_PAGE'; payload: string | string[] }
|
||||
| { type: 'INCREMENT_PAGE' }
|
||||
| { type: 'DECREMENT_PAGE' }
|
||||
|
||||
export type TableFooterProps = CommonProps &
|
||||
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
pages: number
|
||||
onPageChange?: (page: number) => void
|
||||
}
|
||||
|
||||
export const TableFooter: React.FC<TableFooterProps> & {
|
||||
classes: typeof tableFooterClasses
|
||||
} = ({ size = 'large', pages, onPageChange, children, ...props }) => {
|
||||
const commonProps = useCommonProps(props)
|
||||
|
||||
const [pageNumber, dispatch] = useReducer(
|
||||
(state: number, action: Action): number => {
|
||||
switch (action.type) {
|
||||
case 'SET_PAGE':
|
||||
const pageNumber = parseInt(action.payload as string)
|
||||
// Ensure the page is between 1 and the maximum page number
|
||||
return Math.max(1, Math.min(pageNumber, pages))
|
||||
case 'INCREMENT_PAGE':
|
||||
// Ensure the page doesn't exceed the maximum
|
||||
return state < pages ? state + 1 : state
|
||||
case 'DECREMENT_PAGE':
|
||||
// Ensure the page doesn't go below 1
|
||||
return state > 1 ? state - 1 : state
|
||||
default:
|
||||
return state
|
||||
}
|
||||
},
|
||||
1,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (onPageChange) {
|
||||
onPageChange(pageNumber)
|
||||
}
|
||||
}, [pageNumber, onPageChange])
|
||||
|
||||
const dropdownOptions: DropdownOption[] =
|
||||
pages > 1
|
||||
? new Array(pages).fill(null).map((value, index) => ({
|
||||
value: `${index + 1}`,
|
||||
name: `${index + 1}/${pages}`,
|
||||
}))
|
||||
: []
|
||||
|
||||
return (
|
||||
<div
|
||||
{...omitCommonProps(props)}
|
||||
className={clsx(
|
||||
commonProps.className,
|
||||
props.className,
|
||||
tableFooterClasses.root,
|
||||
tableFooterClasses[size],
|
||||
)}
|
||||
>
|
||||
<div className={tableFooterClasses.content}>{children}</div>
|
||||
|
||||
{pages > 1 && (
|
||||
<div className={tableFooterClasses.paginationControls}>
|
||||
<IconButton
|
||||
size={size}
|
||||
onClick={() => dispatch({ type: 'DECREMENT_PAGE' })}
|
||||
>
|
||||
<NavigateBeforeIcon />
|
||||
</IconButton>
|
||||
<Dropdown
|
||||
{...commonProps}
|
||||
size={size}
|
||||
options={dropdownOptions}
|
||||
onChange={(newPage) =>
|
||||
dispatch({ type: 'SET_PAGE', payload: newPage })
|
||||
}
|
||||
value={dropdownOptions[pageNumber - 1].value}
|
||||
/>
|
||||
<IconButton
|
||||
size={size}
|
||||
onClick={() => dispatch({ type: 'INCREMENT_PAGE' })}
|
||||
>
|
||||
<NavigateNextIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
TableFooter.classes = tableFooterClasses
|
1
packages/lsd-react/src/components/TableFooter/index.ts
Normal file
1
packages/lsd-react/src/components/TableFooter/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './TableFooter'
|
@ -25,6 +25,7 @@ export * from './components/TableBody'
|
||||
export * from './components/TableHeader'
|
||||
export * from './components/TableItem'
|
||||
export * from './components/TableRow'
|
||||
export * from './components/TableFooter'
|
||||
export * from './components/Tabs'
|
||||
export * from './components/Tag'
|
||||
export * from './components/TextField'
|
||||
|
Loading…
x
Reference in New Issue
Block a user