Add improved production UI 3
This commit is contained in:
@@ -25,11 +25,12 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
||||
...props
|
||||
}, ref) => {
|
||||
const baseClasses = [
|
||||
'inline-flex items-center justify-center font-medium',
|
||||
'inline-flex items-center justify-center font-semibold tracking-wide',
|
||||
'transition-all duration-200 ease-in-out',
|
||||
'focus:outline-none focus:ring-2 focus:ring-offset-2',
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
'border rounded-lg'
|
||||
'border rounded-xl shadow-sm',
|
||||
'hover:shadow-md active:shadow-sm'
|
||||
];
|
||||
|
||||
const variantClasses = {
|
||||
@@ -78,11 +79,11 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
||||
};
|
||||
|
||||
const sizeClasses = {
|
||||
xs: 'px-2 py-1 text-xs gap-1 min-h-6',
|
||||
sm: 'px-3 py-1.5 text-sm gap-1.5 min-h-8 sm:min-h-10', // Better touch target on mobile
|
||||
md: 'px-4 py-2 text-sm gap-2 min-h-10 sm:min-h-12',
|
||||
lg: 'px-6 py-2.5 text-base gap-2 min-h-12 sm:min-h-14',
|
||||
xl: 'px-8 py-3 text-lg gap-3 min-h-14 sm:min-h-16'
|
||||
xs: 'px-3 py-1.5 text-xs gap-1.5 min-h-7',
|
||||
sm: 'px-4 py-2 text-sm gap-2 min-h-9 sm:min-h-10',
|
||||
md: 'px-6 py-3 text-sm gap-2.5 min-h-11 sm:min-h-12',
|
||||
lg: 'px-8 py-3.5 text-base gap-3 min-h-13 sm:min-h-14',
|
||||
xl: 'px-10 py-4 text-lg gap-3.5 min-h-15 sm:min-h-16'
|
||||
};
|
||||
|
||||
const loadingSpinner = (
|
||||
@@ -128,13 +129,13 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
||||
>
|
||||
{isLoading && loadingSpinner}
|
||||
{!isLoading && leftIcon && (
|
||||
<span className="flex-shrink-0">{leftIcon}</span>
|
||||
<span className="flex-shrink-0 w-5 h-5 flex items-center justify-center">{leftIcon}</span>
|
||||
)}
|
||||
<span>
|
||||
<span className="relative">
|
||||
{isLoading && loadingText ? loadingText : children}
|
||||
</span>
|
||||
{!isLoading && rightIcon && (
|
||||
<span className="flex-shrink-0">{rightIcon}</span>
|
||||
<span className="flex-shrink-0 w-5 h-5 flex items-center justify-center">{rightIcon}</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
121
frontend/src/components/ui/Toggle/Toggle.tsx
Normal file
121
frontend/src/components/ui/Toggle/Toggle.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface ToggleProps {
|
||||
checked: boolean;
|
||||
onChange: (checked: boolean) => void;
|
||||
disabled?: boolean;
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
label?: string;
|
||||
description?: string;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Toggle: React.FC<ToggleProps> = ({
|
||||
checked,
|
||||
onChange,
|
||||
disabled = false,
|
||||
size = 'md',
|
||||
label,
|
||||
description,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
className = ''
|
||||
}) => {
|
||||
const sizeClasses = {
|
||||
sm: 'w-8 h-4',
|
||||
md: 'w-11 h-6',
|
||||
lg: 'w-14 h-8'
|
||||
};
|
||||
|
||||
const thumbSizeClasses = {
|
||||
sm: 'h-3 w-3',
|
||||
md: 'h-5 w-5',
|
||||
lg: 'h-7 w-7'
|
||||
};
|
||||
|
||||
const thumbPositionClasses = {
|
||||
sm: 'after:top-[2px] after:left-[2px] peer-checked:after:translate-x-4',
|
||||
md: 'after:top-[2px] after:left-[2px] peer-checked:after:translate-x-full',
|
||||
lg: 'after:top-[2px] after:left-[2px] peer-checked:after:translate-x-6'
|
||||
};
|
||||
|
||||
const handleToggle = () => {
|
||||
if (!disabled) {
|
||||
onChange(!checked);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex items-center ${className}`}>
|
||||
{leftIcon && (
|
||||
<div className="mr-3 flex-shrink-0">
|
||||
{leftIcon}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center flex-1">
|
||||
<div className="flex items-center">
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={handleToggle}
|
||||
disabled={disabled}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
${sizeClasses[size]}
|
||||
bg-[var(--bg-tertiary)]
|
||||
peer-focus:outline-none
|
||||
rounded-full
|
||||
peer
|
||||
${thumbPositionClasses[size]}
|
||||
peer-checked:after:border-white
|
||||
after:content-['']
|
||||
after:absolute
|
||||
${thumbSizeClasses[size]}
|
||||
after:bg-white
|
||||
after:rounded-full
|
||||
after:transition-all
|
||||
peer-checked:bg-[var(--color-primary)]
|
||||
${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
|
||||
`}
|
||||
/>
|
||||
</label>
|
||||
|
||||
{(label || description) && (
|
||||
<div className="ml-3">
|
||||
{label && (
|
||||
<span className={`
|
||||
text-sm font-medium text-[var(--text-primary)]
|
||||
${disabled ? 'opacity-50' : ''}
|
||||
`}>
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
{description && (
|
||||
<p className={`
|
||||
text-xs text-[var(--text-secondary)]
|
||||
${disabled ? 'opacity-50' : ''}
|
||||
`}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{rightIcon && (
|
||||
<div className="ml-3 flex-shrink-0">
|
||||
{rightIcon}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Toggle;
|
||||
2
frontend/src/components/ui/Toggle/index.ts
Normal file
2
frontend/src/components/ui/Toggle/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as Toggle } from './Toggle';
|
||||
export type { ToggleProps } from './Toggle';
|
||||
@@ -12,6 +12,7 @@ export { default as Select } from './Select';
|
||||
export { default as DatePicker } from './DatePicker';
|
||||
export { Tabs } from './Tabs';
|
||||
export { ThemeToggle } from './ThemeToggle';
|
||||
export { Toggle } from './Toggle';
|
||||
export { ProgressBar } from './ProgressBar';
|
||||
export { StatusIndicator } from './StatusIndicator';
|
||||
export { ListItem } from './ListItem';
|
||||
@@ -34,6 +35,7 @@ export type { SelectProps, SelectOption } from './Select';
|
||||
export type { DatePickerProps } from './DatePicker';
|
||||
export type { TabsProps, TabItem } from './Tabs';
|
||||
export type { ThemeToggleProps } from './ThemeToggle';
|
||||
export type { ToggleProps } from './Toggle';
|
||||
export type { ProgressBarProps } from './ProgressBar';
|
||||
export type { StatusIndicatorProps } from './StatusIndicator';
|
||||
export type { ListItemProps } from './ListItem';
|
||||
|
||||
Reference in New Issue
Block a user