chore(ui,icons,ui-preset,toolbox): Move design system packages to monorepo (#5470)

This commit is contained in:
Kasper Fabricius Kristensen
2023-11-07 22:17:44 +01:00
committed by GitHub
parent 71853eafdd
commit e4ce2f4e07
722 changed files with 30300 additions and 186 deletions

View File

@@ -0,0 +1 @@
export * from "./table"

View File

@@ -0,0 +1,280 @@
import type { Meta, StoryObj } from "@storybook/react"
import * as React from "react"
import { Table } from "./table"
const meta: Meta<typeof Table> = {
title: "Components/Table",
component: Table,
parameters: {
layout: "centered",
},
}
export default meta
type Story = StoryObj<typeof Table>
type Order = {
id: string
displayId: number
customer: string
email: string
amount: number
currency: string
}
const firstNames = [
"Charles",
"Cooper",
"Johhny",
"Elvis",
"John",
"Jane",
"Joe",
"Jack",
"Jill",
"Jenny",
]
const lastNames = [
"Brown",
"Smith",
"Johnson",
"Williams",
"Jones",
"Miller",
"Davis",
"Garcia",
"Rodriguez",
"Wilson",
]
const currencies = ["USD", "EUR", "GBP", "JPY"]
function makeDate(x: number): Order[] {
// get random name
const getRandomName = () => {
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]
return `${firstName} ${lastName}`
}
const getRandomId = () => {
return `order_${Math.floor(Math.random() * 100000)}`
}
const getRandomDisplayId = () => {
return Math.floor(Math.random() * 100000)
}
const getRandomAmount = () => {
return Math.floor(Math.random() * 1000)
}
const getRandomCurrency = () => {
return currencies[Math.floor(Math.random() * currencies.length)]
}
const getRandomEmail = () => {
return `${Math.floor(Math.random() * 100000)}@gmail.com`
}
// Create x random orders and resolve them after 1 second
const orders = Array.from({ length: x }, () => ({
id: getRandomId(),
displayId: getRandomDisplayId(),
customer: getRandomName(),
email: getRandomEmail(),
amount: getRandomAmount(),
currency: getRandomCurrency(),
}))
return orders
}
type UseFakeOrdersProps = {
limit: number
offset: number
}
const useFakeOrders = ({ offset, limit }: UseFakeOrdersProps) => {
const COUNT = 1000
const [orders, setOrders] = React.useState<Order[]>(makeDate(limit))
const [offsetState, setOffsetState] = React.useState<number | undefined>(0)
const [isLoading, setIsLoading] = React.useState(false)
// Fake API call
React.useEffect(() => {
const fetchOrders = async () => {
if (offset === offsetState) {
return
}
await new Promise((resolve) => setTimeout(resolve, 500))
if (offset > COUNT) {
return
}
const newOrders = makeDate(limit)
setOrders(newOrders)
setOffsetState(offset)
}
setIsLoading(true)
fetchOrders().then(() => {
setIsLoading(false)
})
}, [offset, limit, orders, offsetState])
return {
orders,
isLoading,
count: COUNT,
}
}
const fakeData = makeDate(10)
console.log(JSON.stringify(fakeData, null, 2))
const formatCurrency = (amount: number, currency: string) => {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
signDisplay: "always",
}).format(amount)
}
export const Default: Story = {
render: () => {
return (
<div className="flex w-[80vw] items-center justify-center">
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>#</Table.HeaderCell>
<Table.HeaderCell>Customer</Table.HeaderCell>
<Table.HeaderCell>Email</Table.HeaderCell>
<Table.HeaderCell className="text-right">Amount</Table.HeaderCell>
<Table.HeaderCell></Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{fakeData.map((order) => {
return (
<Table.Row
key={order.id}
className="[&_td:last-child]:w-[1%] [&_td:last-child]:whitespace-nowrap"
>
<Table.Cell>{order.displayId}</Table.Cell>
<Table.Cell>{order.customer}</Table.Cell>
<Table.Cell>{order.email}</Table.Cell>
<Table.Cell className="text-right">
{formatCurrency(order.amount, order.currency)}
</Table.Cell>
<Table.Cell className="text-ui-fg-muted">
{order.currency}
</Table.Cell>
</Table.Row>
)
})}
</Table.Body>
</Table>
</div>
)
},
}
const PaginatedDemo = () => {
const [pageIndex, setPageIndex] = React.useState(0)
const pageSize = 10
const { orders, isLoading, count } = useFakeOrders({
offset: pageIndex * pageSize,
limit: pageSize,
})
const pageCount = Math.ceil(count / pageSize)
const canNextPage = pageIndex < pageCount - 1 && !isLoading
const canPreviousPage = pageIndex > 0 && !isLoading
const nextPage = () => {
if (canNextPage) {
setPageIndex(pageIndex + 1)
}
}
const previousPage = () => {
if (canPreviousPage) {
setPageIndex(pageIndex - 1)
}
}
return (
<div className="flex w-[80vw] flex-col items-center justify-center">
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>#</Table.HeaderCell>
<Table.HeaderCell>Customer</Table.HeaderCell>
<Table.HeaderCell>Email</Table.HeaderCell>
<Table.HeaderCell className="text-right">Amount</Table.HeaderCell>
<Table.HeaderCell></Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{orders.map((order) => {
return (
<Table.Row
key={order.id}
className="[&_td:last-child]:w-[1%] [&_td:last-child]:whitespace-nowrap"
>
<Table.Cell>{order.displayId}</Table.Cell>
<Table.Cell>{order.customer}</Table.Cell>
<Table.Cell>{order.email}</Table.Cell>
<Table.Cell className="text-right">{order.amount}</Table.Cell>
<Table.Cell className="text-ui-fg-muted">
{order.currency}
</Table.Cell>
</Table.Row>
)
})}
</Table.Body>
</Table>
<Table.Pagination
pageCount={pageCount}
canNextPage={canNextPage}
canPreviousPage={canPreviousPage}
count={count}
pageSize={pageSize}
pageIndex={pageIndex}
nextPage={nextPage}
previousPage={previousPage}
/>
<div className="mt-12 flex flex-col items-center gap-y-4 font-mono text-xs">
<div className="flex items-center gap-x-4">
<p>Page Index: {pageIndex}</p>
<p>Page Count: {pageCount}</p>
<p>Count: {count}</p>
<p>Page Size: {pageSize}</p>
</div>
<div className="flex items-center gap-x-4">
<p>Can Next Page: {canNextPage ? "true" : "false"}</p>
<p>Can Previous Page: {canPreviousPage ? "true" : "false"}</p>
<p>Is Loading: {isLoading ? "true" : "false"}</p>
</div>
</div>
</div>
)
}
export const Paginated: Story = {
render: () => {
return <PaginatedDemo />
},
}

View File

@@ -0,0 +1,163 @@
import { Minus } from "@medusajs/icons"
import * as React from "react"
import { Button } from "@/components/button"
import { clx } from "@/utils/clx"
const Root = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<table
ref={ref}
className={clx("text-ui-fg-subtle txt-compact-small w-full", className)}
{...props}
/>
))
Root.displayName = "Table"
const Row = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={clx(
"bg-ui-bg-base hover:bg-ui-bg-base-hover border-ui-border-base transition-fg border-b",
"[&_td:last-child]:pr-8 [&_th:last-child]:pr-8",
"[&_td:first-child]:pl-8 [&_th:first-child]:pl-8",
className
)}
{...props}
/>
))
Row.displayName = "Table.Row"
const Cell = React.forwardRef<
HTMLTableCellElement,
React.HTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td ref={ref} className={clx("h-12 pr-3", className)} {...props} />
))
Cell.displayName = "Table.Cell"
const Header = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={clx(
"border-ui-border-base txt-compact-small-plus [&_tr:hover]:bg-ui-bg-base border-y",
className
)}
{...props}
/>
))
Header.displayName = "Table.Header"
const HeaderCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th ref={ref} className={clx("h-12 pr-3 text-left", className)} {...props} />
))
HeaderCell.displayName = "Table.HeaderCell"
const Body = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={clx("border-ui-border-base border-b", className)}
{...props}
/>
))
Body.displayName = "Table.Body"
interface TablePaginationProps extends React.HTMLAttributes<HTMLDivElement> {
count: number
pageSize: number
pageIndex: number
pageCount: number
canPreviousPage: boolean
canNextPage: boolean
previousPage: () => void
nextPage: () => void
}
const Pagination = React.forwardRef<HTMLDivElement, TablePaginationProps>(
(
{
className,
count,
pageSize,
pageCount,
pageIndex,
canPreviousPage,
canNextPage,
nextPage,
previousPage,
...props
},
ref
) => {
const { from, to } = React.useMemo(() => {
const from = count === 0 ? count : pageIndex * pageSize + 1
const to = Math.min(count, (pageIndex + 1) * pageSize)
return { from, to }
}, [count, pageIndex, pageSize])
return (
<div
ref={ref}
className={clx(
"text-ui-fg-subtle txt-compact-small-plus flex w-full items-center justify-between px-5 pb-6 pt-4",
className
)}
{...props}
>
<div className="inline-flex items-center gap-x-1 px-3 py-[5px]">
<p>{from}</p>
<Minus className="text-ui-fg-muted" />
<p>{`${to} of ${count} results`}</p>
</div>
<div className="flex items-center gap-x-2">
<div className="inline-flex items-center gap-x-1 px-3 py-[5px]">
<p>
{pageIndex + 1} of {Math.max(pageCount, 1)}
</p>
</div>
<Button
variant={"transparent"}
onClick={previousPage}
disabled={!canPreviousPage}
>
Prev
</Button>
<Button
variant={"transparent"}
onClick={nextPage}
disabled={!canNextPage}
>
Next
</Button>
</div>
</div>
)
}
)
Pagination.displayName = "Table.Pagination"
const Table = Object.assign(Root, {
Row,
Cell,
Header,
HeaderCell,
Body,
Pagination,
})
export { Table }