diff --git a/packages/shared-ui/src/atoms/Card.svelte b/packages/shared-ui/src/atoms/Card.svelte index bfa949288..64a8d3f5e 100644 --- a/packages/shared-ui/src/atoms/Card.svelte +++ b/packages/shared-ui/src/atoms/Card.svelte @@ -1,48 +1,185 @@
{ + if (isInteractive && onclick && (e.key === 'Enter' || e.key === ' ')) { + e.preventDefault(); + onclick(e as unknown as MouseEvent); + } + }} > - {@render children()} + {#if header} +
+ {@render header()} +
+ {/if} + +
+ {@render children()} +
+ + {#if footer} + + {/if}
+ + diff --git a/packages/shared-ui/src/index.ts b/packages/shared-ui/src/index.ts index 81eadfd0d..83e2a5fb2 100644 --- a/packages/shared-ui/src/index.ts +++ b/packages/shared-ui/src/index.ts @@ -2,8 +2,13 @@ export { Text, Button, Badge, Card } from './atoms'; // Molecules -export { Toggle, Input } from './molecules'; +export { Toggle, Input, Select, Textarea, Checkbox } from './molecules'; +export type { SelectOption } from './molecules'; // Organisms export { Modal, AppSlider } from './organisms'; export type { AppItem } from './organisms'; + +// Navigation +export { NavLink, Navbar, Sidebar } from './navigation'; +export type { NavItem, NavbarProps, SidebarProps, NavLinkProps } from './navigation'; diff --git a/packages/shared-ui/src/molecules/Checkbox.svelte b/packages/shared-ui/src/molecules/Checkbox.svelte new file mode 100644 index 000000000..ea6d47d19 --- /dev/null +++ b/packages/shared-ui/src/molecules/Checkbox.svelte @@ -0,0 +1,161 @@ + + + + + diff --git a/packages/shared-ui/src/molecules/Select.svelte b/packages/shared-ui/src/molecules/Select.svelte new file mode 100644 index 000000000..863eb7422 --- /dev/null +++ b/packages/shared-ui/src/molecules/Select.svelte @@ -0,0 +1,160 @@ + + +
+ {#if label} + + {/if} + +
+ +
+ + + +
+
+ + {#if error} +

{error}

+ {/if} +
+ + diff --git a/packages/shared-ui/src/molecules/Textarea.svelte b/packages/shared-ui/src/molecules/Textarea.svelte new file mode 100644 index 000000000..66172a629 --- /dev/null +++ b/packages/shared-ui/src/molecules/Textarea.svelte @@ -0,0 +1,195 @@ + + +
+ {#if label} + + {/if} + + + + +
+ + diff --git a/packages/shared-ui/src/molecules/index.ts b/packages/shared-ui/src/molecules/index.ts index 823bc0749..5ed2eeb05 100644 --- a/packages/shared-ui/src/molecules/index.ts +++ b/packages/shared-ui/src/molecules/index.ts @@ -1,2 +1,6 @@ export { default as Toggle } from './Toggle.svelte'; export { default as Input } from './Input.svelte'; +export { default as Select } from './Select.svelte'; +export { default as Textarea } from './Textarea.svelte'; +export { default as Checkbox } from './Checkbox.svelte'; +export type { SelectOption } from './Select.svelte'; diff --git a/packages/shared-ui/src/navigation/NavLink.svelte b/packages/shared-ui/src/navigation/NavLink.svelte new file mode 100644 index 000000000..b2738c420 --- /dev/null +++ b/packages/shared-ui/src/navigation/NavLink.svelte @@ -0,0 +1,210 @@ + + + + {#if item.icon} + + {#if item.icon.startsWith(' + {@html item.icon.startsWith('M') ? `` : item.icon} + {:else} + + {item.icon} + {/if} + + {/if} + + {#if !minimized} + {item.label} + {/if} + + {#if item.badge !== undefined && !minimized} + {item.badge} + {/if} + + {#if item.shortcut && !minimized} + {item.shortcut} + {/if} + + {#if minimized && showTooltip} + {item.label} + {/if} + + + diff --git a/packages/shared-ui/src/navigation/Navbar.svelte b/packages/shared-ui/src/navigation/Navbar.svelte new file mode 100644 index 000000000..4b5da706f --- /dev/null +++ b/packages/shared-ui/src/navigation/Navbar.svelte @@ -0,0 +1,270 @@ + + + + + diff --git a/packages/shared-ui/src/navigation/Sidebar.svelte b/packages/shared-ui/src/navigation/Sidebar.svelte new file mode 100644 index 000000000..ca3ae475b --- /dev/null +++ b/packages/shared-ui/src/navigation/Sidebar.svelte @@ -0,0 +1,289 @@ + + + + + diff --git a/packages/shared-ui/src/navigation/index.ts b/packages/shared-ui/src/navigation/index.ts new file mode 100644 index 000000000..5dbbc3ea3 --- /dev/null +++ b/packages/shared-ui/src/navigation/index.ts @@ -0,0 +1,4 @@ +export { default as NavLink } from './NavLink.svelte'; +export { default as Navbar } from './Navbar.svelte'; +export { default as Sidebar } from './Sidebar.svelte'; +export type { NavItem, NavbarProps, SidebarProps, NavLinkProps } from './types'; diff --git a/packages/shared-ui/src/navigation/types.ts b/packages/shared-ui/src/navigation/types.ts new file mode 100644 index 000000000..9ba9d0bb5 --- /dev/null +++ b/packages/shared-ui/src/navigation/types.ts @@ -0,0 +1,79 @@ +import type { Snippet } from 'svelte'; + +export interface NavItem { + /** Display label for the navigation item */ + label: string; + /** URL to navigate to */ + href: string; + /** Icon - can be emoji, SVG path, or component name */ + icon?: string; + /** Whether this item is currently active */ + active?: boolean; + /** Badge text (e.g., notification count) */ + badge?: string | number; + /** Whether the item is disabled */ + disabled?: boolean; + /** Keyboard shortcut hint */ + shortcut?: string; +} + +export interface NavbarProps { + /** Navigation items to display */ + items: NavItem[]; + /** Logo snippet or component */ + logo?: Snippet; + /** App name to display next to logo */ + appName?: string; + /** Current pathname for active state detection */ + currentPath?: string; + /** User email to display */ + userEmail?: string; + /** Show mobile menu */ + showMobile?: boolean; + /** Called when sign out is clicked */ + onSignOut?: () => void; + /** Additional CSS classes */ + class?: string; +} + +export interface SidebarProps { + /** Navigation items to display */ + items: NavItem[]; + /** Logo snippet or component */ + logo?: Snippet; + /** App name to display */ + appName?: string; + /** Current pathname for active state detection */ + currentPath?: string; + /** Whether sidebar is minimized/collapsed */ + minimized?: boolean; + /** Called when minimize toggle is clicked */ + onToggleMinimize?: () => void; + /** User email to display */ + userEmail?: string; + /** Called when sign out is clicked */ + onSignOut?: () => void; + /** Show theme toggle */ + showThemeToggle?: boolean; + /** Called when theme toggle is clicked */ + onToggleTheme?: () => void; + /** Current theme mode (for icon display) */ + isDark?: boolean; + /** Additional CSS classes */ + class?: string; + /** Footer items (shortcuts, etc.) */ + footerItems?: NavItem[]; +} + +export interface NavLinkProps { + /** Navigation item data */ + item: NavItem; + /** Whether the link is active */ + active?: boolean; + /** Display variant */ + variant?: 'default' | 'sidebar' | 'mobile' | 'pill'; + /** Whether in minimized sidebar mode (show tooltip) */ + minimized?: boolean; + /** Additional CSS classes */ + class?: string; +}