mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 11:26:42 +02:00
feat(shared-ui): add navigation components and form elements
NEW COMPONENTS: Navigation: - NavLink: Reusable navigation link with active state, tooltips, badges - Navbar: Horizontal top navigation with mobile menu support - Sidebar: Vertical collapsible sidebar with theme toggle support Form Elements: - Select: Dropdown select with placeholder, error states - Textarea: Multi-line input with auto-resize, character count - Checkbox: Accessible checkbox with indeterminate state support ENHANCED: - Card: Added header/footer slots, interactive mode, filled variant EXPORTS: - NavItem, NavbarProps, SidebarProps, NavLinkProps types - SelectOption type for Select component All components use HSL CSS variables from shared-tailwind for consistent theming across all apps. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
22cb7d2c5f
commit
afdc30bd5f
11 changed files with 1536 additions and 22 deletions
|
|
@ -1,48 +1,185 @@
|
|||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type CardVariant = 'elevated' | 'outlined' | 'ghost';
|
||||
type CardVariant = 'elevated' | 'outlined' | 'ghost' | 'filled';
|
||||
type CardPadding = 'none' | 'sm' | 'md' | 'lg';
|
||||
|
||||
interface Props {
|
||||
/** Visual variant of the card */
|
||||
variant?: CardVariant;
|
||||
/** Padding size */
|
||||
padding?: CardPadding;
|
||||
/** Make card interactive (adds hover effects) */
|
||||
interactive?: boolean;
|
||||
/** Makes card take full width */
|
||||
fullWidth?: boolean;
|
||||
/** Additional CSS classes */
|
||||
class?: string;
|
||||
/** Click handler */
|
||||
onclick?: (e: MouseEvent) => void;
|
||||
/** Header slot */
|
||||
header?: Snippet;
|
||||
/** Footer slot */
|
||||
footer?: Snippet;
|
||||
/** Main content */
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
variant = 'elevated',
|
||||
padding = 'md',
|
||||
interactive = false,
|
||||
fullWidth = false,
|
||||
class: className = '',
|
||||
onclick,
|
||||
header,
|
||||
footer,
|
||||
children
|
||||
}: Props = $props();
|
||||
|
||||
const variantClasses: Record<CardVariant, string> = {
|
||||
elevated: 'bg-menu shadow-md border border-theme',
|
||||
outlined: 'bg-content border border-theme',
|
||||
ghost: 'bg-transparent'
|
||||
};
|
||||
|
||||
const paddingClasses: Record<CardPadding, string> = {
|
||||
none: '',
|
||||
sm: 'p-4',
|
||||
md: 'p-6',
|
||||
lg: 'p-8'
|
||||
};
|
||||
|
||||
const classes = $derived(
|
||||
`rounded-lg ${variantClasses[variant]} ${paddingClasses[padding]} ${className}`
|
||||
);
|
||||
// Determine if card should be interactive
|
||||
const isInteractive = $derived(interactive || !!onclick);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={classes}
|
||||
class="card card--{variant} card--padding-{padding} {isInteractive ? 'card--interactive' : ''} {fullWidth ? 'card--full-width' : ''} {className}"
|
||||
{onclick}
|
||||
role={onclick ? 'button' : undefined}
|
||||
tabindex={onclick ? 0 : undefined}
|
||||
role={isInteractive ? 'button' : undefined}
|
||||
tabindex={isInteractive ? 0 : undefined}
|
||||
onkeydown={(e) => {
|
||||
if (isInteractive && onclick && (e.key === 'Enter' || e.key === ' ')) {
|
||||
e.preventDefault();
|
||||
onclick(e as unknown as MouseEvent);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{@render children()}
|
||||
{#if header}
|
||||
<div class="card__header">
|
||||
{@render header()}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="card__body">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
{#if footer}
|
||||
<div class="card__footer">
|
||||
{@render footer()}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
/* Variants */
|
||||
.card--elevated {
|
||||
background-color: hsl(var(--color-surface-elevated));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card--outlined {
|
||||
background-color: hsl(var(--color-surface));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.card--ghost {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.card--filled {
|
||||
background-color: hsl(var(--color-muted));
|
||||
}
|
||||
|
||||
/* Padding */
|
||||
.card--padding-none .card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card--padding-sm .card__body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.card--padding-md .card__body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card--padding-lg .card__body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Full width */
|
||||
.card--full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Interactive */
|
||||
.card--interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card--interactive:hover {
|
||||
border-color: hsl(var(--color-border-strong));
|
||||
}
|
||||
|
||||
.card--elevated.card--interactive:hover {
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.card--interactive:focus-visible {
|
||||
outline: 2px solid hsl(var(--color-ring));
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.card__header {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.card--padding-sm .card__header {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.card--padding-lg .card__header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.card--padding-none .card__header {
|
||||
padding: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Body */
|
||||
.card__body {
|
||||
/* Padding applied via variant classes above */
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.card__footer {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid hsl(var(--color-border));
|
||||
background-color: hsl(var(--color-muted) / 0.3);
|
||||
}
|
||||
|
||||
.card--padding-sm .card__footer {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.card--padding-lg .card__footer {
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.card--padding-none .card__footer {
|
||||
padding: 0;
|
||||
border-top: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue