website refactor
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface AccordionProps {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Link } from './Link';
|
||||
import { Text } from './Text';
|
||||
import { Surface } from './Surface';
|
||||
import { Surface } from './primitives/Surface';
|
||||
|
||||
interface ActivityItemProps {
|
||||
headline: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { LoadingSpinner } from './LoadingSpinner';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface AuthLoadingProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Image } from './Image';
|
||||
import { User } from 'lucide-react';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -13,10 +13,11 @@ interface BadgeProps {
|
||||
bg?: string;
|
||||
color?: string;
|
||||
borderColor?: string;
|
||||
rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
|
||||
}
|
||||
|
||||
export function Badge({ children, className = '', variant = 'default', size = 'sm', icon, style, bg, color, borderColor }: BadgeProps) {
|
||||
const baseClasses = 'flex items-center gap-1.5 rounded-none border font-bold uppercase tracking-widest';
|
||||
export function Badge({ children, className = '', variant = 'default', size = 'sm', icon, style, bg, color, borderColor, rounded = 'none' }: BadgeProps) {
|
||||
const baseClasses = 'flex items-center gap-1.5 border font-bold uppercase tracking-widest';
|
||||
|
||||
const sizeClasses = {
|
||||
xs: 'px-1.5 py-0.5 text-[9px]',
|
||||
@@ -24,6 +25,16 @@ export function Badge({ children, className = '', variant = 'default', size = 's
|
||||
md: 'px-3 py-1 text-xs'
|
||||
};
|
||||
|
||||
const roundedClasses = {
|
||||
none: 'rounded-none',
|
||||
sm: 'rounded-sm',
|
||||
md: 'rounded-md',
|
||||
lg: 'rounded-lg',
|
||||
xl: 'rounded-xl',
|
||||
'2xl': 'rounded-2xl',
|
||||
full: 'rounded-full'
|
||||
};
|
||||
|
||||
const variantClasses = {
|
||||
default: 'bg-gray-500/10 border-gray-500/30 text-gray-400',
|
||||
primary: 'bg-primary-accent/10 border-primary-accent/30 text-primary-accent',
|
||||
@@ -36,6 +47,7 @@ export function Badge({ children, className = '', variant = 'default', size = 's
|
||||
const classes = [
|
||||
baseClasses,
|
||||
sizeClasses[size],
|
||||
roundedClasses[rounded],
|
||||
!bg && !color && !borderColor ? variantClasses[variant] : '',
|
||||
bg,
|
||||
color,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { Badge } from './Badge';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface Tab {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { ReactNode, MouseEventHandler, ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
import { Stack } from './Stack';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode, MouseEventHandler } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
type Spacing = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 72 | 80 | 96;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { ProgressBar } from './ProgressBar';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Image } from './Image';
|
||||
import { Tag } from 'lucide-react';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface CheckboxProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
type Spacing = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 72 | 80 | 96;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Heading } from './Heading';
|
||||
import { Card } from './Card';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Heading } from './Heading';
|
||||
import { Image } from './Image';
|
||||
import { Text } from './Text';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Calendar } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface DecorativeBlurProps {
|
||||
color?: 'blue' | 'green' | 'purple' | 'yellow' | 'red';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { Box } from './Box';
|
||||
import { Surface } from './Surface';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
|
||||
export interface ErrorBannerProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Image } from './Image';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface FilePickerProps {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Button } from './Button';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
|
||||
interface FilterOption {
|
||||
id: string;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface FormSectionProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface GlowProps {
|
||||
color?: 'primary' | 'aqua' | 'purple' | 'amber';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
import { Heading } from './Heading';
|
||||
import { Card } from './Card';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { ReactNode, ElementType } from 'react';
|
||||
import { Stack } from './Stack';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
interface ResponsiveFontSize {
|
||||
base?: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface HeroProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Card } from './Card';
|
||||
import { Stack } from './Stack';
|
||||
import { Surface } from './Surface';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface HorizontalStatCardProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface HorizontalStatItemProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
interface IconProps extends Omit<BoxProps<'svg'>, 'children' | 'as'> {
|
||||
icon: LucideIcon;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Image as ImageIcon, AlertCircle, Loader2 } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { Text } from './Text';
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { AlertTriangle, CheckCircle, Info, LucideIcon, XCircle } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Surface } from './Surface';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
|
||||
type BannerType = 'info' | 'warning' | 'success' | 'error';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Surface } from './Surface';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './Box';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { Text } from './Text';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { forwardRef, InputHTMLAttributes } from 'react';
|
||||
import { Text } from './Text';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
|
||||
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
variant?: 'default' | 'error';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface LeaderboardListProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Award, ChevronRight, LucideIcon } from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Button } from './Button';
|
||||
import { Heading } from './Heading';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface LeaderboardPreviewShellProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface LeaderboardTableShellProps {
|
||||
columns: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
interface LinkProps extends Omit<BoxProps<'a'>, 'children' | 'className' | 'onClick'> {
|
||||
href: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Info, FileText, Maximize2, Type, Calendar, LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { ImagePlaceholder } from './ImagePlaceholder';
|
||||
import { Image } from './Image';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface MiniStatProps {
|
||||
|
||||
@@ -4,10 +4,10 @@ import React, {
|
||||
type KeyboardEvent as ReactKeyboardEvent,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Button } from './Button';
|
||||
import { Heading } from './Heading';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface ModalProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
interface ModalIconProps {
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Heading } from './Heading';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Surface } from './Surface';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface PageHeaderProps {
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Heading } from './Heading';
|
||||
import { Button } from './Button';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
import { ModalIcon } from './ModalIcon';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Button } from './Button';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface PaginationProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Surface } from './Surface';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface PanelProps extends Omit<BoxProps<'div'>, 'variant' | 'padding'> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { ComponentProps } from 'react';
|
||||
import { Eye, EyeOff, Lock } from 'lucide-react';
|
||||
import { Input } from './Input';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface PasswordFieldProps extends ComponentProps<typeof Input> {
|
||||
showPassword?: boolean;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { User } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
export interface PlaceholderImageProps {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { Trophy } from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Heading } from './Heading';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface PodiumProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
interface ProgressBarProps extends Omit<BoxProps<'div'>, 'children'> {
|
||||
value: number;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { ChevronRight, LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { Link } from './Link';
|
||||
import { Text } from './Text';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Panel } from './Panel';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Button } from './Button';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Heading } from './Heading';
|
||||
import { Text } from './Text';
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Heading } from './Heading';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Surface } from './Surface';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface SectionHeaderProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface SegmentedControlOption {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ChangeEvent, SelectHTMLAttributes } from 'react';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface SelectOption {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ChevronRight, LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
import { Link } from './Link';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface CheckboxProps {
|
||||
checked: boolean;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
import { Surface } from './Surface';
|
||||
import { Surface } from './primitives/Surface';
|
||||
|
||||
interface StatBoxProps {
|
||||
icon: LucideIcon;
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
import { ArrowDownRight, ArrowUpRight, LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Card } from './Card';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface StatCardProps {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Grid } from './Grid';
|
||||
import { GridItem } from './GridItem';
|
||||
import { Surface } from './Surface';
|
||||
import { Grid } from './primitives/Grid';
|
||||
import { GridItem } from './primitives/GridItem';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
|
||||
type GridCols = 1 | 2 | 3 | 4 | 5 | 6 | 12;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
|
||||
interface StatGridItemProps {
|
||||
label: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface StatItemProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { Stack } from './Stack';
|
||||
import { Stack } from './primitives/Stack';
|
||||
|
||||
interface StatusBadgeProps {
|
||||
children: React.ReactNode;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface StatusDotProps {
|
||||
color?: string;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Surface } from './Surface';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface SummaryItemProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode, HTMLAttributes } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
interface TableProps extends HTMLAttributes<HTMLTableElement> {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode, ElementType, ComponentPropsWithoutRef } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps } from './primitives/Box';
|
||||
|
||||
type Spacing = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 72 | 80 | 96;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import React, { TextareaHTMLAttributes } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Stack } from './Stack';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface TextAreaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Box } from './Box';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface ToggleProps {
|
||||
|
||||
@@ -36,6 +36,8 @@ export interface BoxProps<T extends ElementType> {
|
||||
flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse';
|
||||
flexShrink?: number;
|
||||
flexGrow?: number;
|
||||
flex?: number | string;
|
||||
alignSelf?: 'auto' | 'start' | 'end' | 'center' | 'stretch' | 'baseline';
|
||||
gridCols?: number | string;
|
||||
responsiveGridCols?: {
|
||||
base?: number | string;
|
||||
@@ -55,10 +57,10 @@ export interface BoxProps<T extends ElementType> {
|
||||
left?: Spacing | string;
|
||||
right?: Spacing | string;
|
||||
overflow?: 'visible' | 'hidden' | 'scroll' | 'auto';
|
||||
maxWidth?: string;
|
||||
minWidth?: string;
|
||||
maxHeight?: string;
|
||||
minHeight?: string;
|
||||
maxWidth?: string | ResponsiveValue<string>;
|
||||
minWidth?: string | ResponsiveValue<string>;
|
||||
maxHeight?: string | ResponsiveValue<string>;
|
||||
minHeight?: string | ResponsiveValue<string>;
|
||||
zIndex?: number;
|
||||
w?: string | ResponsiveValue<string>;
|
||||
h?: string | ResponsiveValue<string>;
|
||||
@@ -75,14 +77,16 @@ export interface BoxProps<T extends ElementType> {
|
||||
color?: string;
|
||||
shadow?: string;
|
||||
hoverBorderColor?: string;
|
||||
transition?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
transition?: any;
|
||||
cursor?: 'pointer' | 'default' | 'wait' | 'text' | 'move' | 'not-allowed';
|
||||
lineClamp?: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
inset?: string;
|
||||
bgOpacity?: number;
|
||||
opacity?: number;
|
||||
blur?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
|
||||
animate?: 'pulse' | 'bounce' | 'spin' | 'fade-in' | 'none';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
animate?: any;
|
||||
translateX?: string;
|
||||
textAlign?: 'left' | 'center' | 'right' | 'justify';
|
||||
hoverScale?: boolean;
|
||||
@@ -93,6 +97,7 @@ export interface BoxProps<T extends ElementType> {
|
||||
groupHoverOpacity?: number;
|
||||
groupHoverWidth?: 'full' | '0' | 'auto';
|
||||
fontSize?: string;
|
||||
fontWeight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
||||
transform?: string;
|
||||
borderWidth?: string;
|
||||
hoverTextColor?: string;
|
||||
@@ -103,7 +108,16 @@ export interface BoxProps<T extends ElementType> {
|
||||
aspectRatio?: string;
|
||||
visibility?: 'visible' | 'hidden' | 'collapse';
|
||||
pointerEvents?: 'auto' | 'none';
|
||||
order?: number | string | ResponsiveValue<number | string>;
|
||||
hideScrollbar?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
whileHover?: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
whileTap?: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
initial?: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
exit?: any;
|
||||
onMouseEnter?: React.MouseEventHandler<T>;
|
||||
onMouseLeave?: React.MouseEventHandler<T>;
|
||||
onClick?: React.MouseEventHandler<T>;
|
||||
@@ -121,7 +135,7 @@ export interface BoxProps<T extends ElementType> {
|
||||
backgroundImage?: string;
|
||||
}
|
||||
|
||||
type ResponsiveValue<T> = {
|
||||
export type ResponsiveValue<T> = {
|
||||
base?: T;
|
||||
sm?: T;
|
||||
md?: T;
|
||||
@@ -175,8 +189,10 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
bg,
|
||||
color,
|
||||
shadow,
|
||||
flex,
|
||||
flexShrink,
|
||||
flexGrow,
|
||||
alignSelf,
|
||||
hoverBorderColor,
|
||||
transition,
|
||||
cursor,
|
||||
@@ -196,6 +212,7 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
groupHoverOpacity,
|
||||
groupHoverWidth,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
transform,
|
||||
borderWidth,
|
||||
hoverTextColor,
|
||||
@@ -206,7 +223,12 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
aspectRatio,
|
||||
visibility,
|
||||
pointerEvents,
|
||||
order,
|
||||
hideScrollbar,
|
||||
whileHover,
|
||||
whileTap,
|
||||
initial,
|
||||
exit,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
@@ -247,11 +269,11 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
return `${prefix}-${spacingMap[value]}`;
|
||||
};
|
||||
|
||||
const getResponsiveClasses = (prefix: string, value: string | ResponsiveValue<string> | undefined) => {
|
||||
const getResponsiveClasses = (prefix: string, value: string | number | ResponsiveValue<string | number> | undefined) => {
|
||||
if (value === undefined) return '';
|
||||
if (typeof value === 'object') {
|
||||
const classes = [];
|
||||
if (value.base !== undefined) classes.push(prefix ? `${prefix}-${value.base}` : value.base);
|
||||
if (value.base !== undefined) classes.push(prefix ? `${prefix}-${value.base}` : String(value.base));
|
||||
if (value.sm !== undefined) classes.push(prefix ? `sm:${prefix}-${value.sm}` : `sm:${value.sm}`);
|
||||
if (value.md !== undefined) classes.push(prefix ? `md:${prefix}-${value.md}` : `md:${value.md}`);
|
||||
if (value.lg !== undefined) classes.push(prefix ? `lg:${prefix}-${value.lg}` : `lg:${value.lg}`);
|
||||
@@ -259,7 +281,7 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
if (value['2xl'] !== undefined) classes.push(prefix ? `2xl:${prefix}-${value['2xl']}` : `2xl:${value['2xl']}`);
|
||||
return classes.join(' ');
|
||||
}
|
||||
return prefix ? `${prefix}-${value}` : value;
|
||||
return prefix ? `${prefix}-${value}` : String(value);
|
||||
};
|
||||
|
||||
const getFlexDirectionClass = (value: BoxProps<ElementType>['flexDirection']) => {
|
||||
@@ -329,6 +351,10 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
getSpacingClass('py', py),
|
||||
getResponsiveClasses('w', w),
|
||||
getResponsiveClasses('h', h),
|
||||
getResponsiveClasses('max-w', maxWidth),
|
||||
getResponsiveClasses('min-w', minWidth),
|
||||
getResponsiveClasses('max-h', maxHeight),
|
||||
getResponsiveClasses('min-h', minHeight),
|
||||
rounded ? `rounded-${rounded}` : '',
|
||||
border ? 'border' : '',
|
||||
borderStyle ? `border-${borderStyle}` : '',
|
||||
@@ -342,8 +368,10 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
color ? color : '',
|
||||
hoverColor ? `hover:${hoverColor}` : '',
|
||||
shadow ? shadow : '',
|
||||
flex !== undefined ? `flex-${flex}` : '',
|
||||
flexShrink !== undefined ? `flex-shrink-${flexShrink}` : '',
|
||||
flexGrow !== undefined ? `flex-grow-${flexGrow}` : '',
|
||||
alignSelf !== undefined ? `self-${alignSelf}` : '',
|
||||
flexWrap ? `flex-${flexWrap}` : '',
|
||||
hoverBorderColor ? `hover:${hoverBorderColor}` : '',
|
||||
hoverTextColor ? `hover:${hoverTextColor}` : '',
|
||||
@@ -365,6 +393,8 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
groupHoverScale ? 'group-hover:scale-[1.02]' : '',
|
||||
groupHoverOpacity !== undefined ? `group-hover:opacity-${groupHoverOpacity * 100}` : '',
|
||||
groupHoverWidth ? `group-hover:w-${groupHoverWidth}` : '',
|
||||
fontSize ? `text-${fontSize}` : '',
|
||||
fontWeight ? `font-${fontWeight}` : '',
|
||||
getResponsiveClasses('', display),
|
||||
getFlexDirectionClass(flexDirection),
|
||||
getAlignItemsClass(alignItems),
|
||||
@@ -378,6 +408,7 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
responsiveColSpan?.md ? `md:col-span-${responsiveColSpan.md}` : '',
|
||||
responsiveColSpan?.lg ? `lg:col-span-${responsiveColSpan.lg}` : '',
|
||||
getSpacingClass('gap', gap),
|
||||
getResponsiveClasses('order', order),
|
||||
position ? position : '',
|
||||
top !== undefined && spacingMap[top as string | number] ? `top-${spacingMap[top as string | number]}` : '',
|
||||
bottom !== undefined && spacingMap[bottom as string | number] ? `bottom-${spacingMap[bottom as string | number]}` : '',
|
||||
@@ -395,10 +426,10 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
const style: React.CSSProperties = {
|
||||
...(width ? { width } : {}),
|
||||
...(height ? { height } : {}),
|
||||
...(maxWidth ? { maxWidth } : {}),
|
||||
...(minWidth ? { minWidth } : {}),
|
||||
...(maxHeight ? { maxHeight } : {}),
|
||||
...(minHeight ? { minHeight } : {}),
|
||||
...(typeof maxWidth === 'string' ? { maxWidth } : {}),
|
||||
...(typeof minWidth === 'string' ? { minWidth } : {}),
|
||||
...(typeof maxHeight === 'string' ? { maxHeight } : {}),
|
||||
...(typeof minHeight === 'string' ? { minHeight } : {}),
|
||||
...(fontSize ? { fontSize } : {}),
|
||||
...(transform ? { transform } : {}),
|
||||
...(borderWidth ? { borderWidth } : {}),
|
||||
@@ -428,6 +459,11 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
onSubmit={onSubmit}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
whileHover={whileHover}
|
||||
whileTap={whileTap}
|
||||
initial={initial}
|
||||
animate={animate}
|
||||
exit={exit}
|
||||
{...props}
|
||||
style={style as React.CSSProperties}
|
||||
>
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
|
||||
interface GridProps extends Omit<BoxProps<'div'>, 'children' | 'display'> {
|
||||
export interface GridProps extends Omit<BoxProps<'div'>, 'children' | 'display'> {
|
||||
children: ReactNode;
|
||||
cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12;
|
||||
mdCols?: 1 | 2 | 3 | 4 | 5 | 6 | 12;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Box } from './Box';
|
||||
|
||||
interface GridItemProps {
|
||||
export interface GridItemProps {
|
||||
children: React.ReactNode;
|
||||
colSpan?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
mdSpan?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode, ElementType } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
import { Box, BoxProps, ResponsiveValue } from './Box';
|
||||
|
||||
type Spacing = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 72 | 80 | 96;
|
||||
|
||||
@@ -20,13 +20,13 @@ interface ResponsiveSpacing {
|
||||
'2xl'?: Spacing;
|
||||
}
|
||||
|
||||
interface StackProps<T extends ElementType> extends Omit<BoxProps<T>, 'children' | 'className' | 'gap'> {
|
||||
export interface StackProps<T extends ElementType> extends Omit<BoxProps<T>, 'children' | 'className' | 'gap'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
direction?: 'row' | 'col' | { base?: 'row' | 'col'; md?: 'row' | 'col'; lg?: 'row' | 'col' };
|
||||
gap?: number | ResponsiveGap;
|
||||
align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline';
|
||||
justify?: 'start' | 'center' | 'end' | 'between' | 'around';
|
||||
align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline' | ResponsiveValue<'start' | 'center' | 'end' | 'stretch' | 'baseline'>;
|
||||
justify?: 'start' | 'center' | 'end' | 'between' | 'around' | ResponsiveValue<'start' | 'center' | 'end' | 'between' | 'around'>;
|
||||
wrap?: boolean;
|
||||
center?: boolean;
|
||||
m?: Spacing | ResponsiveSpacing;
|
||||
@@ -53,8 +53,8 @@ export function Stack<T extends ElementType = 'div'>({
|
||||
className = '',
|
||||
direction = 'col',
|
||||
gap = 4,
|
||||
align = 'stretch',
|
||||
justify = 'start',
|
||||
align,
|
||||
justify,
|
||||
wrap = false,
|
||||
center = false,
|
||||
m, mt, mb, ml, mr,
|
||||
@@ -133,7 +133,6 @@ export function Stack<T extends ElementType = 'div'>({
|
||||
direction.lg === 'col' ? 'lg:flex-col' : (direction.lg === 'row' ? 'lg:flex-row' : ''),
|
||||
].filter(Boolean).join(' '),
|
||||
getGapClasses(gap) || 'gap-4',
|
||||
center ? 'items-center justify-center' : `items-${align} justify-${justify}`,
|
||||
wrap ? 'flex-wrap' : '',
|
||||
getSpacingClass('m', m),
|
||||
getSpacingClass('mt', mt),
|
||||
@@ -151,5 +150,18 @@ export function Stack<T extends ElementType = 'div'>({
|
||||
className
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
return <Box as={as} className={classes} {...props}>{children}</Box>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { alignItems, justifyContent, ...restProps } = props as any;
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={as}
|
||||
className={classes}
|
||||
alignItems={center ? 'center' : (align || alignItems)}
|
||||
justifyContent={center ? 'center' : (justify || justifyContent)}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { ReactNode, ElementType, ComponentPropsWithoutRef } from 'react';
|
||||
import { Box, BoxProps } from './Box';
|
||||
|
||||
interface SurfaceProps<T extends ElementType = 'div'> extends Omit<BoxProps<T>, 'children' | 'className' | 'display'> {
|
||||
export interface SurfaceProps<T extends ElementType = 'div'> extends Omit<BoxProps<T>, 'children' | 'className' | 'display'> {
|
||||
as?: T;
|
||||
children: ReactNode;
|
||||
variant?: 'default' | 'muted' | 'dark' | 'glass' | 'gradient-blue' | 'gradient-gold' | 'gradient-purple' | 'gradient-green' | 'discord' | 'discord-inner';
|
||||
Reference in New Issue
Block a user