diff --git a/docs/MOBILE_PATTERNS.md b/docs/MOBILE_PATTERNS.md new file mode 100644 index 0000000..1b8c703 --- /dev/null +++ b/docs/MOBILE_PATTERNS.md @@ -0,0 +1,905 @@ +# ROA2WEB Mobile Patterns Library + +**Version:** 1.0.0 +**Last Updated:** 2026-01-12 +**Status:** ✅ Complete + +--- + +## Table of Contents + +1. [Quick Start](#quick-start) +2. [Mobile Layout Overview](#mobile-layout-overview) +3. [MobileTopBar](#mobiletopbar) +4. [MobileBottomNav](#mobilebottomnav) +5. [MobileSelectionFooter](#mobileselectionfooter) +6. [BottomSheet](#bottomsheet) +7. [SwipeableCards](#swipeablecards) +8. [Design Tokens for Mobile](#design-tokens-for-mobile) +9. [Best Practices](#best-practices) +10. [Troubleshooting](#troubleshooting) + +--- + +## Quick Start + +### For New Developers + +Get a mobile view running in **5 minutes**: + +```vue + + + + + +``` + +### Import Paths + +All mobile components are located in `src/shared/components/mobile/`: + +```javascript +import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue' +import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue' +import MobileSelectionFooter from '@shared/components/mobile/MobileSelectionFooter.vue' +import BottomSheet from '@shared/components/mobile/BottomSheet.vue' +import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue' +``` + +--- + +## Mobile Layout Overview + +### ASCII Diagram: Standard Mobile Layout + +``` +┌─────────────────────────────────────────┐ +│ MobileTopBar (56px) │ +│ [≡] Page Title [🔍] [⋮] │ +├─────────────────────────────────────────┤ +│ │ +│ │ +│ MAIN CONTENT │ +│ │ +│ (scrollable area) │ +│ │ +│ padding-top: 56px │ +│ padding-bottom: 56px │ +│ │ +│ │ +├─────────────────────────────────────────┤ +│ MobileBottomNav (56px) │ +│ 🏠 📋 📊 ⚙️ │ +│ Home Upload Reports Settings │ +└─────────────────────────────────────────┘ +``` + +### ASCII Diagram: Selection Mode Layout + +``` +┌─────────────────────────────────────────┐ +│ MobileTopBar (selection-active) │ +│ [←] "3 selectate" [☑] [✕] │ +├─────────────────────────────────────────┤ +│ │ +│ ☑ Item 1 │ +│ ☐ Item 2 │ +│ ☑ Item 3 │ +│ ☑ Item 4 │ +│ ☐ Item 5 │ +│ │ +│ │ +├─────────────────────────────────────────┤ +│ MobileSelectionFooter │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ 🗑 Șterge │ │ 📤 Export │ │ +│ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────┘ +``` + +### ASCII Diagram: BottomSheet Open + +``` +┌─────────────────────────────────────────┐ +│ MobileTopBar (56px) │ +├─────────────────────────────────────────┤ +│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ +│ ░░░░░░░░░░░░ OVERLAY ░░░░░░░░░░░░░░░░░░ │ +│ ░░░░░░░░░░ (tap to close) ░░░░░░░░░░░░░ │ +│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ +├─────────────────────────────────────────┤ ←─ BottomSheet +│ ─────────── │ slides up +│ (drag handle) │ +│ │ +│ Filter Options: │ +│ ☐ Option A │ +│ ☑ Option B │ +│ ☐ Option C │ +│ │ +│ [ Apply Filters ] │ +│ │ +└─────────────────────────────────────────┘ +``` + +### ASCII Diagram: SwipeableCards + +``` +┌─────────────────────────────────────────┐ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ │ │ +│ │ KPI Card Content │ │ ← Swipe left/right +│ │ │ │ to navigate +│ │ $125,430 │ │ +│ │ Total Sales │ │ +│ │ │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ ●━━━━●───● │ ← Dots indicator +│ │ (active = expanded) +└─────────────────────────────────────────┘ +``` + +--- + +## MobileTopBar + +Material Design 3 inspired top navigation bar for mobile views. + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | String | `''` | Center title text | +| `showBack` | Boolean | `false` | Show back arrow button on left | +| `showMenu` | Boolean | `false` | Show hamburger menu on left (ignored if showBack is true) | +| `actions` | Array | `[]` | Right-side action buttons | +| `selectionActive` | Boolean | `false` | Enable selection mode styling | + +### Events + +| Event | Payload | Description | +|-------|---------|-------------| +| `menu-click` | - | Hamburger menu clicked | +| `back-click` | - | Back arrow clicked | +| `action-click` | `action` | Action button clicked | + +### Action Object Structure + +```typescript +interface TopBarAction { + icon: string // PrimeIcons class (e.g., 'pi pi-filter') + label?: string // Accessibility label + tooltip?: string // Tooltip text + active?: boolean // Highlight when active +} +``` + +### Basic Usage + +```vue + + + +``` + +### With Back Navigation + +```vue + +``` + +### Selection Mode + +```vue + +``` + +### CSS Classes + +| Class | Description | +|-------|-------------| +| `.mobile-top-bar` | Base container (fixed, 56px height) | +| `.selection-active` | Blue background for selection mode | +| `.top-bar-left` | Left button container | +| `.top-bar-right` | Right action buttons container | +| `.top-bar-title` | Center title (ellipsis on overflow) | +| `.top-bar-btn` | Individual button (48x48px touch target) | +| `.top-bar-btn.active` | Highlighted state | + +--- + +## MobileBottomNav + +Material Design 3 inspired bottom navigation bar with router integration. + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `items` | Array | Default nav items | Navigation items array | + +### Events + +| Event | Payload | Description | +|-------|---------|-------------| +| `item-click` | `item` | Non-router item clicked | + +### Item Object Structure + +```typescript +interface NavItem { + to?: string // Route path (uses router-link when provided) + icon: string // PrimeIcons class (e.g., 'pi pi-receipt') + label: string // Display text + active?: boolean // Force active state (for non-router items) +} +``` + +### Default Items + +```javascript +[ + { to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri' }, + { icon: 'pi pi-cloud-upload', label: 'Upload' }, // Action button + { to: '/reports/dashboard', icon: 'pi pi-chart-bar', label: 'Rapoarte' }, + { to: '/data-entry/ocr-metrics', icon: 'pi pi-cog', label: 'Setări' } +] +``` + +### Basic Usage + +```vue + +``` + +### Custom Items + +```vue + + + +``` + +### CSS Classes + +| Class | Description | +|-------|-------------| +| `.mobile-bottom-nav` | Base container (fixed, 56px height) | +| `.bottom-nav-item` | Individual nav item (flex: 1) | +| `.bottom-nav-item.active` | Active state (primary color) | +| `.bottom-nav-item.router-link-active` | Auto-active via Vue Router | + +--- + +## MobileSelectionFooter + +Slide-up action bar for batch operations on selected items. + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `visible` | Boolean | `false` | Controls visibility (triggers animation) | +| `actions` | Array | `[]` | Action buttons to display | + +### Action Object Structure + +```typescript +interface SelectionAction { + label: string // Button text + icon: string // PrimeIcons class + severity?: string // PrimeVue severity ('secondary', 'danger', etc.) + handler: () => void // Click handler function +} +``` + +### Basic Usage + +```vue + + + +``` + +### Single Action + +```vue + +``` + +### CSS Classes + +| Class | Description | +|-------|-------------| +| `.mobile-selection-footer` | Base container (fixed, z-index 1030) | +| `.selection-actions` | Button container (max-width: 400px) | +| `.selection-action-btn` | Individual button (48px min-height) | + +### Animation + +Uses Vue ``: +- Slides up from bottom when `visible` becomes `true` +- Slides down when `visible` becomes `false` +- Duration: `var(--transition-normal)` (250ms) + +--- + +## BottomSheet + +Modal-style sheet that slides up from the bottom, ideal for filters and forms. + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `modelValue` | Boolean | `false` | v-model for visibility | +| `closeOnOverlay` | Boolean | `true` | Close when tapping overlay | +| `closeOnSwipeDown` | Boolean | `true` | Close when swiping down on handle | + +### Events + +| Event | Payload | Description | +|-------|---------|-------------| +| `update:modelValue` | `boolean` | v-model update | + +### Slots + +| Slot | Description | +|------|-------------| +| `default` | Content inside the sheet | + +### Basic Usage + +```vue + + + +``` + +### Prevent Close on Overlay + +```vue + + + +``` + +### CSS Classes + +| Class | Description | +|-------|-------------| +| `.bottom-sheet-overlay` | Full-screen overlay (50% opacity black) | +| `.bottom-sheet` | Sheet container (max-height: 90vh) | +| `.bottom-sheet-handle` | Handle area at top (32px min-height) | +| `.handle-bar` | Visual drag indicator (40px × 4px) | +| `.bottom-sheet-content` | Scrollable content area | + +### Touch Gestures + +| Gesture | Action | +|---------|--------| +| Tap overlay | Close (if `closeOnOverlay: true`) | +| Tap handle | Close | +| Swipe down > 100px | Close (if `closeOnSwipeDown: true`) | + +--- + +## SwipeableCards + +Touch-swipeable carousel for KPI cards with dots indicator. + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `totalCards` | Number | **required** | Number of cards in carousel | +| `showDots` | Boolean | `true` | Show navigation dots | +| `autoPlay` | Boolean | `false` | Auto-advance cards | +| `autoPlayInterval` | Number | `5000` | Auto-play interval (ms) | + +### Events + +| Event | Payload | Description | +|-------|---------|-------------| +| `update:currentIndex` | `number` | Current card index changed | + +### Slots + +Named slots for each card: `card-0`, `card-1`, `card-2`, etc. + +### Exposed Methods + +| Method | Description | +|--------|-------------| +| `goToCard(index)` | Navigate to specific card | +| `nextCard()` | Go to next card | +| `prevCard()` | Go to previous card | +| `currentIndex` | Current card index (ref) | + +### Basic Usage + +```vue + +``` + +### With Auto-Play + +```vue + + + +``` + +### Programmatic Navigation + +```vue + + + +``` + +### CSS Classes + +| Class | Description | +|-------|-------------| +| `.swipeable-cards-container` | Base container with overflow hidden | +| `.cards-track` | Horizontal flex container (will-change: transform) | +| `.card-slide` | Individual card (flex: 0 0 100%) | +| `.dots-indicator` | Navigation dots container | +| `.dot` | Individual dot (8px, expands to 24px when active) | +| `.dot.active` | Active dot (primary color) | + +### Touch Thresholds + +| Threshold | Value | Description | +|-----------|-------|-------------| +| Swipe distance | 50px | Minimum swipe to change card | +| Velocity | 0.3 | Quick swipe detection | +| Angle | 30° | Max angle for horizontal detection | + +--- + +## Design Tokens for Mobile + +### Layout Tokens + +| Token | Value | Use | +|-------|-------|-----| +| `--header-height` | 56px | MobileTopBar & MobileBottomNav height | +| `--z-fixed` | 1030 | Fixed elements z-index | +| `--z-modal-backdrop` | 1040 | BottomSheet overlay | +| `--z-modal` | 1050 | BottomSheet content | + +### Touch Target Sizes + +```css +/* Minimum touch target: 48x48px */ +.touch-target { + min-width: 48px; + min-height: 48px; +} + +/* Button in top bar */ +.top-bar-btn { + width: 48px; + height: 48px; +} +``` + +### Spacing for Mobile + +| Token | Value | Mobile Use | +|-------|-------|------------| +| `var(--space-xs)` | 4px | Icon gaps, tight spacing | +| `var(--space-sm)` | 8px | Between nav items, card gaps | +| `var(--space-md)` | 16px | Card padding, content margins | +| `var(--space-lg)` | 24px | Section spacing | + +### Mobile Content Padding + +```css +/* Standard mobile content area */ +.mobile-content { + padding-top: 56px; /* MobileTopBar */ + padding-bottom: 56px; /* MobileBottomNav */ + padding-left: var(--space-md); + padding-right: var(--space-md); +} + +/* When selection footer is visible */ +.mobile-content-with-selection { + padding-bottom: 80px; /* Higher for selection footer */ +} +``` + +--- + +## Best Practices + +### 1. Always Use Design Tokens + +```css +/* ✅ CORRECT */ +.mobile-card { + padding: var(--space-md); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); +} + +/* ❌ WRONG */ +.mobile-card { + padding: 16px; + border-radius: 8px; + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +``` + +### 2. Account for Fixed Headers/Footers + +```vue + + + +``` + +### 3. Test Both Light and Dark Mode + +All mobile components support: +- Manual dark mode: `[data-theme="dark"]` +- System preference: `@media (prefers-color-scheme: dark)` + +```javascript +// Toggle theme in DevTools: +document.documentElement.setAttribute('data-theme', 'dark') +``` + +### 4. Handle Safe Areas (Notch/Home Indicator) + +```css +.mobile-bottom-nav { + padding-bottom: env(safe-area-inset-bottom); +} + +.mobile-content { + padding-bottom: calc(56px + env(safe-area-inset-bottom)); +} +``` + +### 5. Selection Mode Pattern + +```vue + +``` + +### 6. Combine Components for Complex Views + +```vue + +``` + +--- + +## Troubleshooting + +### Top/Bottom bars not fixed + +**Problem**: Bars scroll with content. + +**Solution**: Ensure `.mobile-top-bar` and `.mobile-bottom-nav` have `position: fixed`. Check for `overflow: hidden` on ancestors. + +### Content hidden behind bars + +**Problem**: Content is cut off at top/bottom. + +**Solution**: Add padding to content area: +```css +.content { + padding-top: 56px; + padding-bottom: 56px; +} +``` + +### BottomSheet z-index issues + +**Problem**: BottomSheet appears behind other elements. + +**Solution**: BottomSheet uses `` to avoid stacking context issues. Check for `z-index` wars in your CSS. + +### Swipe conflicts with scrolling + +**Problem**: SwipeableCards interferes with vertical scroll. + +**Solution**: Component uses angle detection (30° threshold). Ensure cards have sufficient content area. Check `touch-action: pan-y` is set. + +### Dark mode not working + +**Problem**: Components don't respond to theme changes. + +**Solution**: All components support both mechanisms: +1. Manual: `[data-theme="dark"]` on `` +2. System: `@media (prefers-color-scheme: dark)` + +Check that `data-theme` attribute is set correctly. + +### Selection footer overlaps content + +**Problem**: When selection footer appears, it covers list items. + +**Solution**: Use dynamic padding: +```css +.content { + padding-bottom: v-bind(isSelectionMode ? '96px' : '56px'); +} +``` + +--- + +## Related Documentation + +- [Design Tokens](./DESIGN_TOKENS.md) - Color, spacing, typography tokens +- [CSS Patterns](./CSS_PATTERNS.md) - General UI patterns +- [Onboarding CSS](./ONBOARDING_CSS.md) - Quick start for CSS + +--- + +**Last Updated:** 2026-01-12 +**Version:** 1.0.0 +**Maintained By:** Frontend Team diff --git a/scripts/ralph/prd.json b/scripts/ralph/prd.json index 7905899..d4dec61 100644 --- a/scripts/ralph/prd.json +++ b/scripts/ralph/prd.json @@ -274,8 +274,8 @@ "Secțiune Quick Start pentru devs noi", "Diagrame ASCII pentru layout mobile" ], - "passes": false, - "notes": "" + "passes": true, + "notes": "Completed in iteration 3" }, { "id": "US-115", diff --git a/scripts/ralph/progress.txt b/scripts/ralph/progress.txt index 38c933a..695a726 100644 --- a/scripts/ralph/progress.txt +++ b/scripts/ralph/progress.txt @@ -538,3 +538,9 @@ Mon Jan 12 09:44:54 AM UTC 2026 [2026-01-12 11:10:10] Working on story: US-113 [2026-01-12 11:10:10] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-113.log) [2026-01-12 11:12:51] SUCCESS: Story US-113 passed! +[2026-01-12 11:12:51] Changes committed +[2026-01-12 11:12:51] Progress: 15/20 stories completed +[2026-01-12 11:12:53] === Iteration 3/50 === +[2026-01-12 11:12:53] Working on story: US-118 +[2026-01-12 11:12:53] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-118.log) +[2026-01-12 11:16:15] SUCCESS: Story US-118 passed!