add components

This commit is contained in:
andres
2023-09-09 17:03:16 +02:00
parent 4e301916f4
commit 450d664f34
35 changed files with 550 additions and 25 deletions

View File

View File

@@ -52,7 +52,6 @@
width: 36px;
height: 36px;
background-color: var(--color-dark-900);
border-radius: 50%;
&.disabled {

View File

@@ -3,5 +3,8 @@ export * from './card'
export * from './typography'
export * from './checkbox'
export * from './text-field'
export * from './table'
export * from './controlled'
export * from './radio-group'
export * from './page'
export * from './slider'

View File

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

View File

@@ -0,0 +1,6 @@
.root {
display: flex;
justify-content: center;
margin-top: 36px;
padding-inline: 24px;
}

View File

@@ -0,0 +1,15 @@
import { ComponentPropsWithoutRef, forwardRef } from 'react'
import { clsx } from 'clsx'
import s from './page.module.scss'
export type PageProps = ComponentPropsWithoutRef<'div'>
export const Page = forwardRef<HTMLDivElement, PageProps>(({ className, ...props }, ref) => {
const classNames = {
root: clsx(s.root, className),
}
return <div {...props} ref={ref} className={classNames.root} />
})

View File

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

View File

@@ -0,0 +1,52 @@
.container {
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
width: 100%;
}
.root {
touch-action: none;
user-select: none;
position: relative;
display: flex;
align-items: center;
width: 100%;
}
.track {
position: relative;
width: 100%;
height: 4px;
opacity: 0.5;
background-color: var(--color-accent-500);
border-radius: 2px;
}
.range {
position: absolute;
height: 100%;
background-color: var(--color-accent-500);
}
.thumb {
touch-action: pan-x;
cursor: pointer;
display: block;
width: 16px;
height: 16px;
background-color: var(--color-light-100);
border-radius: 9999px;
transition: transform 0.2s ease-in-out;
}

View File

@@ -0,0 +1,26 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'
import * as SliderPrimitive from '@radix-ui/react-slider'
import { clsx } from 'clsx'
import s from './slider.module.scss'
const Slider = forwardRef<
ElementRef<typeof SliderPrimitive.Root>,
ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<div className={s.container}>
<span>{props?.value?.[0]}</span>
<SliderPrimitive.Root ref={ref} className={clsx(s.root, className)} {...props}>
<SliderPrimitive.Track className={s.track}>
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className={s.thumb} />
<SliderPrimitive.Thumb className={s.thumb} />
</SliderPrimitive.Root>
<span>{props?.value?.[1]}</span>
</div>
))
Slider.displayName = SliderPrimitive.Root.displayName
export { Slider }

View File

@@ -1,5 +1,6 @@
.table {
border-collapse: collapse;
width: 100%;
color: var(--color-light-100);
border: 1px solid var(--color-dark-500);
}

View File

@@ -75,3 +75,49 @@ export const TableEmpty: FC<ComponentProps<'div'> & { mt?: string; mb?: string }
</Typography>
)
}
export type Column = {
key: string
title: string
sortable?: boolean
}
export type Sort = {
key: string
direction: 'asc' | 'desc'
} | null
export const TableHeader: FC<
Omit<
ComponentPropsWithoutRef<'thead'> & {
columns: Column[]
sort?: Sort
onSort?: (sort: Sort) => void
},
'children'
>
> = ({ columns, sort, onSort, ...restProps }) => {
const handleSort = (key: string, sortable?: boolean) => () => {
if (!onSort || !sortable) return
if (sort?.key !== key) return onSort({ key, direction: 'asc' })
if (sort.direction === 'desc') return onSort(null)
return onSort({
key,
direction: sort?.direction === 'asc' ? 'desc' : 'asc',
})
}
return (
<TableHead {...restProps}>
<TableRow>
{columns.map(({ title, key, sortable = true }) => (
<TableHeadCell key={key} onClick={handleSort(key, sortable)}>
{title}
{sort && sort.key === key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
</TableHeadCell>
))}
</TableRow>
</TableHead>
)
}

View File

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

View File

@@ -0,0 +1,34 @@
.list {
display: inline-flex;
background-color: var(--color-dark-900);
}
.trigger {
cursor: pointer;
display: inline-flex;
flex-shrink: 0;
flex-wrap: nowrap;
padding: 6px 24px;
background-color: var(--color-dark-900);
border: 1px solid var(--color-dark-300);
&:last-of-type {
border-radius: 0 2px 2px 0;
}
&:first-of-type {
border-radius: 2px 0 0 2px;
}
&[data-state='active'] {
background-color: var(--color-accent-500);
border-color: var(--color-accent-500);
}
}
.content {
color: inherit;
}

View File

@@ -0,0 +1,37 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'
import * as TabsPrimitive from '@radix-ui/react-tabs'
import { clsx } from 'clsx'
import s from './tabs.module.scss'
const Tabs = TabsPrimitive.Root
const TabsList = forwardRef<
ElementRef<typeof TabsPrimitive.List>,
ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List ref={ref} className={clsx(s.list, className)} {...props} />
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = forwardRef<
ElementRef<typeof TabsPrimitive.Trigger>,
ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger ref={ref} className={clsx(s.trigger, className)} {...props} />
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = forwardRef<
ElementRef<typeof TabsPrimitive.Content>,
ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content ref={ref} className={clsx(s.content, className)} {...props} />
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }

View File

@@ -40,6 +40,10 @@
color: var(--color-danger-300);
border-color: var(--color-danger-300);
}
&.hasLeadingIcon {
padding-left: 41px;
}
}
.label {
@@ -47,6 +51,27 @@
color: var(--color-dark-100);
}
.leadingIcon {
position: absolute;
top: 50%;
bottom: 50%;
left: 0;
transform: translateY(-50%);
width: 20px;
height: 20px;
margin-left: 12px;
padding: 0;
background: transparent;
border: 0;
outline: 0;
&:focus-visible {
outline: var(--outline-focus);
}
}
.showPassword {
cursor: pointer;

View File

@@ -4,7 +4,7 @@ import { clsx } from 'clsx'
import s from './text-field.module.scss'
import { VisibilityOff, Eye } from '@/assets'
import { VisibilityOff, Eye, Search } from '@/assets'
import { Typography } from '@/components'
export type TextFieldProps = {
@@ -13,6 +13,7 @@ export type TextFieldProps = {
labelProps?: ComponentProps<'label'>
errorMessage?: string
label?: string
search?: boolean
} & ComponentPropsWithoutRef<'input'>
export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
@@ -27,6 +28,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
label,
onChange,
onValueChange,
search,
...restProps
},
ref
@@ -45,9 +47,10 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
const classNames = {
root: clsx(s.root, containerProps?.className),
fieldContainer: clsx(s.fieldContainer),
field: clsx(s.field, !!errorMessage && s.error, className),
field: clsx(s.field, !!errorMessage && s.error, search && s.hasLeadingIcon, className),
label: clsx(s.label, labelProps?.className),
error: clsx(s.error),
leadingIcon: s.leadingIcon,
}
return (
@@ -58,6 +61,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
</Typography>
)}
<div className={classNames.fieldContainer}>
{search && <Search className={classNames.leadingIcon} />}
<input
className={classNames.field}
placeholder={placeholder}