feat(lib-core): biblioteca atomica @pulse-libs/core v1.0.0-beta.1

Esta commit conteudo a estrutura atomica completa:

- types:     Result<T,E>, AsyncState<T>, Paginated<T>, SortConfig<T>
- utils:     date, str, num, cn, debounce, throttle, storage, arr, obj
- validators: Zod schemas — email, password, uuid, url, phone, CPF/CNPJ, sanitizedStr, safeParse
- hooks:     useToggle, useAsync, useDebounce, useLocalStorage, useMedia, useInterval, useOnClickOutside, useClipboard, useFetch
- components: Button, Input, Alert, Card, Spinner (atomic design pattern)
- build:     tsup v8 ESM+CJS + DTS + sourcemaps — 0 erros
- tests:     57 testes 100% usuarios
- docker:    multi-stage Dockerfile (node 20-alpine)
- config:    vitest, tsup, tsconfig strict, .npmignore

Filosofia atomica:/utils ← /types ← /validators ← /hooks ← /components
Build: npm run build | Test: npm test | Publish: npm publish

🤖 Generated with Pulse (openclaw + nova-self-improver)
This commit is contained in:
pulse-agent
2026-05-19 21:43:03 -03:00
parent ae39e45460
commit bbdb68a6de
7030 changed files with 2040595 additions and 0 deletions
@@ -0,0 +1,152 @@
/**
* Componentes atômicos — Receptor de className sempre no topo,
* spread de props SEMPRE por último.
*/
import { cn } from '../utils';
// =========================================================
// 🅱 Button
// =========================================================
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'success';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
const variantStyles: Record<ButtonVariant, string> = {
primary: 'bg-indigo-600 text-white hover:bg-indigo-700 active:bg-indigo-800 disabled:bg-indigo-300',
secondary: 'border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 active:bg-gray-100',
ghost: 'text-gray-600 hover:bg-gray-100 active:bg-gray-200',
danger: 'bg-red-600 text-white hover:bg-red-700 active:bg-red-800',
success: 'bg-emerald-600 text-white hover:bg-emerald-700 active:bg-emerald-800',
};
const sizeStyles: Record<ButtonSize, string> = {
sm: 'px-2.5 py-1 text-xs rounded',
md: 'px-4 py-2 text-sm rounded-md',
lg: 'px-6 py-3 text-base rounded-lg',
};
export function Button({
className, variant = 'primary', size = 'md',
loading, leftIcon, rightIcon,
children, disabled, ...rest
}: ButtonProps) {
return (
<button
className={cn(
'inline-flex items-center justify-center gap-2 font-medium transition-colors',
'focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2',
'disabled:cursor-not-allowed disabled:opacity-50',
variantStyles[variant], sizeStyles[size], className
)}
disabled={disabled || loading}
{...rest}
>
{loading ? <Spinner size={16} /> : (leftIcon ?? null)}
{children}
{!loading && rightIcon}
</button>
);
}
// =========================================================
// 🅸 Input
// =========================================================
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
hint?: string;
}
export function Input({ className, label, error, hint, id, ...rest }: InputProps) {
const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-');
return (
<div className="flex flex-col gap-1">
{label && <label htmlFor={inputId} className="text-sm font-medium text-gray-700">{label}</label>}
<input
id={inputId}
className={cn(
'w-full rounded-md border border-gray-300 px-3 py-2 text-sm',
'placeholder:text-gray-400',
'focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500',
'disabled:bg-gray-100 disabled:cursor-not-allowed',
error && 'border-red-500 focus:ring-red-500 focus:border-red-500',
className
)}
{...rest}
/>
{error && <span className="text-xs text-red-500">{error}</span>}
{hint && !error && <span className="text-xs text-gray-400">{hint}</span>}
</div>
);
}
// =========================================================
// 🔔 Toast / Alert
// =========================================================
type AlertVariant = 'info' | 'success' | 'warning' | 'error';
interface AlertProps {
variant?: AlertVariant;
title?: string;
children: React.ReactNode;
onClose?: () => void;
}
const alertStyles: Record<AlertVariant, { container: string; icon: string }> = {
info: { container: 'bg-blue-50 border-blue-200 text-blue-800', icon: '️' },
success: { container: 'bg-emerald-50 border-emerald-200 text-emerald-800', icon: '✅' },
warning: { container: 'bg-amber-50 border-amber-200 text-amber-800', icon: '⚠️' },
error: { container: 'bg-red-50 border-red-200 text-red-800', icon: '❌' },
};
export function Alert({ variant = 'info', title, children, onClose }: AlertProps) {
const s = alertStyles[variant];
return (
<div className={cn('flex items-start gap-2 rounded-lg border px-4 py-3', s.container)}>
<span className="text-base leading-none">{s.icon}</span>
<div className="flex-1">
{title && <p className="font-semibold text-sm">{title}</p>}
<p className="text-sm">{children}</p>
</div>
{onClose && <button onClick={onClose} className="ml-auto text-lg leading-none opacity-50 hover:opacity-100">×</button>}
</div>
);
}
// =========================================================
// 🃏 Card
// =========================================================
interface CardProps extends React.HTMLAttributes<HTMLDivElement> { children: React.ReactNode; }
export const Card: React.FC<CardProps> = ({ className, children, ...rest }) => (
<div className={cn('rounded-xl border border-gray-200 bg-white shadow-sm', className)} {...rest}>{children}</div>
);
export const CardHeader: React.FC<CardProps> = ({ className, children, ...rest }) => (
<div className={cn('px-5 py-4 border-b border-gray-100', className)} {...rest}>{children}</div>
);
export const CardTitle: React.FC<{ children: React.ReactNode }> = ({ children }) => (
<h3 className="font-semibold text-gray-900 text-base">{children}</h3>
);
export const CardBody: React.FC<CardProps> = ({ className, children, ...rest }) => (
<div className={cn('px-5 py-4', className)} {...rest}>{children}</div>
);
// ── Spinner (reutilizável) ─────────────────────────────────
export function Spinner({ size = 16, className = '' }: { size?: number; className?: string }) {
return (
<svg className={cn('animate-spin', className)} width={size} height={size} viewBox="0 0 24 24" fill="none">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
);
}
// ── Cn helper re-export ────────────────────────────────────
export { cn } from '../utils';