mirror of
https://github.com/ershisan99/live-bundlers.git
synced 2026-01-24 12:35:05 +00:00
initial commit
This commit is contained in:
44
src/components/button/button.module.scss
Normal file
44
src/components/button/button.module.scss
Normal 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;
|
||||
}
|
||||
69
src/components/button/button.stories.tsx
Normal file
69
src/components/button/button.stories.tsx
Normal 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',
|
||||
},
|
||||
}
|
||||
23
src/components/button/button.tsx
Normal file
23
src/components/button/button.tsx
Normal 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} />
|
||||
}
|
||||
1
src/components/button/index.ts
Normal file
1
src/components/button/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './button'
|
||||
2
src/components/index.ts
Normal file
2
src/components/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './button'
|
||||
export * from './slider'
|
||||
1
src/components/slider/index.ts
Normal file
1
src/components/slider/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './slider'
|
||||
52
src/components/slider/slider.module.scss
Normal file
52
src/components/slider/slider.module.scss
Normal 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;
|
||||
}
|
||||
43
src/components/slider/slider.tsx
Normal file
43
src/components/slider/slider.tsx
Normal 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
4
src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import './styles/index.scss'
|
||||
|
||||
export * from './components'
|
||||
export { clsx } from 'clsx'
|
||||
30
src/styles/_boilerplate.scss
Normal file
30
src/styles/_boilerplate.scss
Normal 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);
|
||||
}
|
||||
22
src/styles/_typography.scss
Normal file
22
src/styles/_typography.scss
Normal 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
2
src/styles/index.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@forward 'typography';
|
||||
@forward 'boilerplate';
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user