initial commit

This commit is contained in:
2023-11-15 18:41:25 +01:00
commit 555fd347cc
26 changed files with 10168 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
.button {
all: unset;
cursor: pointer;
box-sizing: border-box;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
&:focus-visible {
outline: 2px solid lightcoral;
outline-offset: 2px;
}
}
.fullWidth {
width: 100%;
}
.primary {
color: white;
background-color: blue;
}
.secondary {
color: black;
background-color: lightblue;
}
.tertiary {
color: blue;
border: 1px solid blue;
}
.link {
color: blue;
text-decoration: underline;
}
.icon {
width: 1.5rem;
height: 1.5rem;
}

View File

@@ -0,0 +1,69 @@
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './'
const meta = {
argTypes: {
onClick: { action: 'clicked' },
variant: {
control: { type: 'radio' },
options: ['primary', 'secondary', 'tertiary', 'link'],
},
},
component: Button,
tags: ['autodocs'],
title: 'Components/Button',
} satisfies Meta<typeof Button>
export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {
args: {
children: 'Primary Button',
disabled: false,
variant: 'primary',
},
}
export const Secondary: Story = {
args: {
children: 'Secondary Button',
disabled: false,
variant: 'secondary',
},
}
export const Tertiary: Story = {
args: {
children: 'Tertiary Button',
disabled: false,
variant: 'tertiary',
},
}
export const Link: Story = {
args: {
children: 'Button that looks like a link',
disabled: false,
variant: 'link',
},
}
export const FullWidth: Story = {
args: {
children: 'Full Width Button',
disabled: false,
fullWidth: true,
variant: 'primary',
},
}
export const AsLink: Story = {
args: {
as: 'a',
children: 'Link that looks like a button',
href: 'https://google.com',
rel: 'noopener noreferrer',
target: '_blank',
variant: 'primary',
},
}

View File

@@ -0,0 +1,23 @@
import { ComponentPropsWithoutRef, ElementType } from 'react'
import { clsx } from 'clsx'
import s from './button.module.scss'
export const buttonVariant = ['icon', 'link', 'primary', 'secondary', 'tertiary'] as const
export type ButtonVariant = (typeof buttonVariant)[number]
export type ButtonProps<T extends ElementType = 'button'> = {
as?: T
fullWidth?: boolean
variant?: ButtonVariant
} & ComponentPropsWithoutRef<T>
export const Button = <T extends ElementType = 'button'>(props: ButtonProps<T>) => {
const { as: Component = 'button', className, fullWidth, variant = 'primary', ...rest } = props
const classNames = clsx(s.button, s[variant], fullWidth && s.fullWidth, className)
return <Component className={classNames} {...rest} />
}

View File

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

2
src/components/index.ts Normal file
View File

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

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,43 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef, useEffect } 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>,
Omit<ComponentPropsWithoutRef<typeof SliderPrimitive.Root>, 'value'> & {
value?: (number | undefined)[]
}
>(({ className, max, onValueChange, value, ...props }, ref) => {
useEffect(() => {
if (value?.[1] === undefined || value?.[1] === null) {
onValueChange?.([value?.[0] ?? 0, max ?? 0])
}
}, [max, value, onValueChange])
return (
<div className={s.container}>
<span>{value?.[0]}</span>
<SliderPrimitive.Root
className={clsx(s.root, className)}
max={max}
onValueChange={onValueChange}
ref={ref}
{...props}
value={[value?.[0] ?? 0, value?.[1] ?? max ?? 0]}
>
<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>{value?.[1]}</span>
</div>
)
})
Slider.displayName = SliderPrimitive.Root.displayName
export { Slider }

4
src/index.ts Normal file
View File

@@ -0,0 +1,4 @@
import './styles/index.scss'
export * from './components'
export { clsx } from 'clsx'

View File

@@ -0,0 +1,30 @@
html {
box-sizing: border-box;
font-size: 100%;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
input,
button,
select,
textarea,
optgroup,
option {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
font-style: inherit;
color: inherit;
}
body {
margin: 0;
padding: 0;
font-family: var(--font-family);
line-height: var(--line-height-m);
}

View File

@@ -0,0 +1,22 @@
:root {
/* globals */
--font-family: system-ui, sans-serif;
/* font-sizes */
--font-size-xs: 12px;
--font-size-s: 14px;
--font-size-m: 16px;
--font-size-l: 18px;
--font-size-xl: 20px;
--font-size-xxl: 22px;
/* line-heights */
--line-height-m: 24px;
--line-height-xl: 36px;
/* font-weights */
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semi-bold: 600;
--font-weight-bold: 700;
}

2
src/styles/index.scss Normal file
View File

@@ -0,0 +1,2 @@
@forward 'typography';
@forward 'boilerplate';

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />