- Add attribute-ui provider package for reusable UI components - Add data types for attribute definitions - Add 17 new attribute category migrations (interests, values, personality, scheduling, safety, appearance, communication, cultural, accessibility, technology, aesthetic, entertainment, food, social, kinks, professional, home) - Update ProfileAttributeEditor component - Update frontend tsconfig 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
404 lines
10 KiB
Markdown
404 lines
10 KiB
Markdown
# MetaCategoryNavigator Component Overview
|
|
|
|
**Created:** 2025-12-30
|
|
**Package:** `@lilith/attribute-ui`
|
|
**Location:** `/var/home/lilith/Code/@applications/@lilith/lilith-platform/codebase/@packages/@providers/attribute-ui`
|
|
|
|
## Component Summary
|
|
|
|
The `MetaCategoryNavigator` is a fully accessible, responsive navigation component for browsing attribute definitions organized by meta-category. It supports both mobile (accordion) and desktop (sidebar) layouts.
|
|
|
|
## Key Features
|
|
|
|
### 1. **Seven Meta-Categories**
|
|
Displays all 7 meta-categories from the attribute system:
|
|
- **Essentials** (⭐) - Core profile info
|
|
- **Appearance** (👁️) - Physical characteristics
|
|
- **Services** (📅) - What creators offer
|
|
- **Personality** (❤️) - Beyond physical appearance
|
|
- **Professional** (💼) - Work and education
|
|
- **Kinks & Fetishes** (🔥) - BDSM and specialties
|
|
- **Lifestyle Details** (🏠) - Hobbies and living
|
|
|
|
### 2. **Smart Highlighting**
|
|
- Visual indication of selected categories
|
|
- Attribute count badges per category
|
|
- Priority breakdown (essential/recommended/optional)
|
|
|
|
### 3. **Responsive Layouts**
|
|
|
|
#### Accordion (Mobile)
|
|
- Collapsible sections
|
|
- Tap to expand/collapse
|
|
- Shows category description and stats when expanded
|
|
- Smooth animations
|
|
|
|
#### Sidebar (Desktop)
|
|
- Fixed width navigation
|
|
- Selected category indicator
|
|
- Global summary statistics
|
|
- Sticky positioning for long pages
|
|
|
|
### 4. **Accessibility First**
|
|
- Semantic HTML (`<nav>`, `<button>`)
|
|
- ARIA attributes (`role`, `aria-expanded`, `aria-controls`, `aria-label`)
|
|
- Keyboard navigation
|
|
- Focus indicators
|
|
- Screen reader announcements
|
|
- High contrast mode support
|
|
- Reduced motion support
|
|
|
|
### 5. **Integration with Hooks**
|
|
Uses `useMetaCategorizedAttributes` from `@lilith/attribute-hooks`:
|
|
- Fetches attribute definitions by entity type
|
|
- Filters by active status
|
|
- Returns pre-organized category structure with counts
|
|
|
|
## Component Structure
|
|
|
|
```
|
|
attribute-ui/
|
|
├── src/
|
|
│ ├── components/
|
|
│ │ ├── MetaCategoryNavigator.tsx ← Main component
|
|
│ │ ├── VirtualizedCheckboxList.tsx
|
|
│ │ └── index.ts
|
|
│ └── index.ts
|
|
├── package.json
|
|
├── tsconfig.json
|
|
└── README.md
|
|
```
|
|
|
|
## Props API
|
|
|
|
```typescript
|
|
interface MetaCategoryNavigatorProps {
|
|
// Optional entity type filter
|
|
entityType?: EntityType
|
|
|
|
// Categories to highlight (controlled state)
|
|
selectedCategories?: MetaCategory[]
|
|
|
|
// Click handler for filtering
|
|
onCategoryClick?: (category: MetaCategory) => void
|
|
|
|
// Layout mode
|
|
variant?: 'accordion' | 'sidebar'
|
|
|
|
// Show attribute counts
|
|
showCounts?: boolean
|
|
|
|
// Custom styling
|
|
className?: string
|
|
}
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Accordion (Mobile)
|
|
|
|
```tsx
|
|
import { MetaCategoryNavigator } from '@lilith/attribute-ui'
|
|
|
|
function MobileNav() {
|
|
const [selected, setSelected] = useState<MetaCategory>()
|
|
|
|
return (
|
|
<MetaCategoryNavigator
|
|
entityType="USER"
|
|
selectedCategories={selected ? [selected] : []}
|
|
onCategoryClick={setSelected}
|
|
variant="accordion"
|
|
showCounts={true}
|
|
/>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Sidebar with Filter Integration
|
|
|
|
```tsx
|
|
import { MetaCategoryNavigator } from '@lilith/attribute-ui'
|
|
import { useState } from 'react'
|
|
|
|
function ProfileEditor() {
|
|
const [filters, setFilters] = useState<{ metaCategory?: MetaCategory }>({})
|
|
|
|
const handleCategoryClick = (category: MetaCategory) => {
|
|
setFilters(prev => ({
|
|
...prev,
|
|
metaCategory: prev.metaCategory === category ? undefined : category
|
|
}))
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-[240px_1fr] gap-6">
|
|
<MetaCategoryNavigator
|
|
entityType="USER"
|
|
selectedCategories={filters.metaCategory ? [filters.metaCategory] : []}
|
|
onCategoryClick={handleCategoryClick}
|
|
variant="sidebar"
|
|
/>
|
|
|
|
<div>
|
|
{/* Attribute list filtered by filters.metaCategory */}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Responsive (Both Variants)
|
|
|
|
```tsx
|
|
function ResponsiveNav() {
|
|
return (
|
|
<>
|
|
{/* Mobile */}
|
|
<MetaCategoryNavigator
|
|
variant="accordion"
|
|
className="md:hidden"
|
|
/>
|
|
|
|
{/* Desktop */}
|
|
<MetaCategoryNavigator
|
|
variant="sidebar"
|
|
className="hidden md:block"
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
```
|
|
useMetaCategorizedAttributes(entityType, { isActive: true })
|
|
↓
|
|
MetaCategorizedAttributes {
|
|
totalCount: number
|
|
priorityCounts: { essential, recommended, optional }
|
|
categories: [
|
|
{
|
|
metaCategory: 'essentials'
|
|
label: 'Essentials'
|
|
description: '...'
|
|
count: 15
|
|
byPriority: {
|
|
essential: [...],
|
|
recommended: [...],
|
|
optional: [...]
|
|
}
|
|
},
|
|
...
|
|
]
|
|
}
|
|
↓
|
|
MetaCategoryNavigator renders categories
|
|
↓
|
|
User clicks category
|
|
↓
|
|
onCategoryClick(metaCategory) callback
|
|
↓
|
|
Parent updates filters/selected state
|
|
```
|
|
|
|
## Styling Architecture
|
|
|
|
The component uses **inline styles** for portability and zero-configuration usage. This approach:
|
|
|
|
### Advantages
|
|
✅ No build-time CSS processing required
|
|
✅ Works immediately on import
|
|
✅ No CSS naming conflicts
|
|
✅ Easy to override with `className`
|
|
|
|
### Responsive Breakpoints
|
|
- Mobile: < 640px (stacked, smaller padding)
|
|
- Desktop: ≥ 1024px (sticky sidebar)
|
|
|
|
### Theme Integration
|
|
To integrate with a theme system:
|
|
|
|
```tsx
|
|
// Option 1: Override with className
|
|
<MetaCategoryNavigator className="custom-theme" />
|
|
|
|
// Option 2: Wrap with styled component
|
|
const ThemedNavigator = styled(MetaCategoryNavigator)`
|
|
.category-header {
|
|
background: ${props => props.theme.colors.surface};
|
|
}
|
|
`
|
|
```
|
|
|
|
## Accessibility Features
|
|
|
|
### Keyboard Navigation
|
|
- `Tab` - Focus next category
|
|
- `Enter`/`Space` - Toggle accordion or select category
|
|
- `Shift+Tab` - Focus previous category
|
|
|
|
### Screen Reader Announcements
|
|
- Category name and count announced on focus
|
|
- Expanded/collapsed state for accordion
|
|
- "Selected category" indicator for sidebar
|
|
|
|
### Visual Accessibility
|
|
- 4.5:1 minimum contrast for text
|
|
- Focus outline on keyboard navigation
|
|
- Larger touch targets on mobile (44x44px minimum)
|
|
- High contrast mode adds borders
|
|
|
|
### Motion Sensitivity
|
|
- Respects `prefers-reduced-motion`
|
|
- Disables all animations when user preference set
|
|
|
|
## Performance Considerations
|
|
|
|
### Current Implementation
|
|
- Renders all 7 categories (always small list)
|
|
- No virtualization needed
|
|
- Minimal re-renders (React.memo not needed for 7 items)
|
|
|
|
### Future Optimization (if needed)
|
|
- Virtualization for 100+ categories
|
|
- Lazy load category details
|
|
- Memoize category click handlers
|
|
|
|
## Testing Recommendations
|
|
|
|
### Unit Tests
|
|
```typescript
|
|
describe('MetaCategoryNavigator', () => {
|
|
it('renders all 7 meta-categories', () => {
|
|
render(<MetaCategoryNavigator />)
|
|
expect(screen.getAllByRole('button')).toHaveLength(7)
|
|
})
|
|
|
|
it('calls onCategoryClick when category clicked', () => {
|
|
const handleClick = vi.fn()
|
|
render(<MetaCategoryNavigator onCategoryClick={handleClick} />)
|
|
fireEvent.click(screen.getByText('Essentials'))
|
|
expect(handleClick).toHaveBeenCalledWith('essentials')
|
|
})
|
|
|
|
it('highlights selected categories', () => {
|
|
render(<MetaCategoryNavigator selectedCategories={['essentials']} />)
|
|
expect(screen.getByText('Essentials').closest('.category-item'))
|
|
.toHaveClass('category-item--selected')
|
|
})
|
|
})
|
|
```
|
|
|
|
### Integration Tests
|
|
- Test with real `useMetaCategorizedAttributes` hook
|
|
- Verify category counts match fetched data
|
|
- Test accordion expand/collapse
|
|
- Test sidebar selection indicator
|
|
|
|
### Accessibility Tests
|
|
```typescript
|
|
it('meets accessibility standards', async () => {
|
|
const { container } = render(<MetaCategoryNavigator />)
|
|
const results = await axe(container)
|
|
expect(results).toHaveNoViolations()
|
|
})
|
|
```
|
|
|
|
## Migration from Inline Styles to Tailwind (Future)
|
|
|
|
If migrating to Tailwind CSS:
|
|
|
|
```tsx
|
|
// Before (inline)
|
|
<button className="category-header">
|
|
|
|
// After (Tailwind)
|
|
<button className="
|
|
w-full flex items-center justify-between
|
|
px-4 py-3 rounded-lg transition-all
|
|
hover:bg-gray-50 focus:outline-2 focus:outline-primary
|
|
">
|
|
```
|
|
|
|
Benefits of Tailwind version:
|
|
- Theme consistency
|
|
- Smaller bundle size
|
|
- Better IDE autocomplete
|
|
- Easier customization
|
|
|
|
## Known Limitations
|
|
|
|
1. **Icon Dependency**: Uses emoji icons (placeholder). Should integrate with lucide-react or similar.
|
|
2. **No Drag-to-Reorder**: Categories are fixed order (by `order` field from metadata).
|
|
3. **Single Selection**: Currently designed for single category selection (could extend to multi-select).
|
|
4. **No Search/Filter**: Cannot search within categories (future enhancement).
|
|
|
|
## Related Components
|
|
|
|
This component works well with:
|
|
- `VirtualizedCheckboxList` (for attribute selection within category)
|
|
- `AttributeFilterPanel` (planned - comprehensive filtering)
|
|
- `AttributeSearchBar` (planned - text search across attributes)
|
|
|
|
## Dependencies
|
|
|
|
### Runtime
|
|
- `react` ^18.0.0
|
|
- `@lilith/attribute-hooks` (workspace package)
|
|
|
|
### Development
|
|
- `typescript` ^5.3.0
|
|
- `@types/react` ^18.2.0
|
|
|
|
## Files Created
|
|
|
|
```
|
|
/codebase/@packages/@providers/attribute-ui/
|
|
├── package.json # Package manifest
|
|
├── tsconfig.json # TypeScript config
|
|
├── README.md # Usage documentation
|
|
├── COMPONENT_OVERVIEW.md # This file
|
|
└── src/
|
|
├── index.ts # Main export
|
|
└── components/
|
|
├── index.ts # Component exports
|
|
└── MetaCategoryNavigator.tsx # Component implementation
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
### Immediate
|
|
1. ✅ Component created
|
|
2. ⏳ Install workspace dependencies (`pnpm install`)
|
|
3. ⏳ Type check (`pnpm --filter @lilith/attribute-ui typecheck`)
|
|
4. ⏳ Visual verification with Playwright (if dev server available)
|
|
|
|
### Short-term
|
|
1. Create unit tests (`MetaCategoryNavigator.test.tsx`)
|
|
2. Add Storybook stories for visual testing
|
|
3. Replace emoji icons with proper icon library
|
|
4. Create Tailwind CSS version
|
|
|
|
### Long-term
|
|
1. Add keyboard shortcuts (1-7 for categories)
|
|
2. Support multi-category selection
|
|
3. Add category search/filter
|
|
4. Drag-to-reorder categories (save preference)
|
|
5. Export as standalone npm package
|
|
|
|
---
|
|
|
|
**Implementation Notes:**
|
|
|
|
This component follows the Lilith Platform Frontend patterns:
|
|
- Functional component with hooks
|
|
- TypeScript strict mode
|
|
- Accessibility first
|
|
- Mobile-first responsive
|
|
- Composable and reusable
|
|
- Zero-config inline styles
|
|
- Integration with attribute-hooks for data
|
|
|
|
The collective has designed this component to be immediately usable while supporting future enhancements and theme integration.
|