Files
roa2web-service-auto/src/shared/components/mobile/MobileSelectionFooter.vue
Claude Agent e73fac8e1d feat(unified-mobile-material-design): Complete US-101c - Creare MobileSelectionFooter.vue component
Implemented by Ralph autonomous loop.
Iteration: 3

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-12 09:49:15 +00:00

167 lines
4.3 KiB
Vue

<template>
<Transition name="slide-up">
<footer v-if="visible" class="mobile-selection-footer">
<div class="selection-actions">
<Button
v-for="(action, index) in actions"
:key="index"
:label="action.label"
:icon="action.icon"
:severity="action.severity || 'secondary'"
class="selection-action-btn"
@click="handleAction(action)"
/>
</div>
</footer>
</Transition>
</template>
<script setup>
import Button from 'primevue/button'
/**
* MobileSelectionFooter - Material Design 3 inspired bottom action bar for selection mode
*
* Props:
* - visible: Whether the footer is visible (controls slide-up animation)
* - actions: Array of action buttons to display
* Each action: { label: string, icon: string, severity?: string, handler: Function }
*
* Behavior:
* - Slides up from the bottom when visible becomes true
* - Displays action buttons horizontally centered
* - Calls action.handler() when button is clicked
*
* Usage:
* <MobileSelectionFooter
* :visible="selectedItems.length > 0"
* :actions="[
* { label: 'Șterge', icon: 'pi pi-trash', severity: 'danger', handler: handleDelete },
* { label: 'Export', icon: 'pi pi-download', severity: 'secondary', handler: handleExport }
* ]"
* />
*/
const props = defineProps({
/**
* Controls visibility of the footer (triggers slide-up animation)
*/
visible: {
type: Boolean,
default: false
},
/**
* Array of action buttons to display
* Each action should have: { label: string, icon: string, severity?: string, handler: Function }
*/
actions: {
type: Array,
default: () => [],
validator: (actions) => {
return Array.isArray(actions) && actions.every(
action => typeof action.label === 'string' &&
typeof action.icon === 'string' &&
typeof action.handler === 'function'
)
}
}
})
/**
* Handle action button click
* Calls the action's handler function if defined
*/
const handleAction = (action) => {
if (action.handler && typeof action.handler === 'function') {
action.handler()
}
}
</script>
<style scoped>
/* ================================================
MobileSelectionFooter Component Styles
Material Design 3 inspired selection action bar
================================================ */
.mobile-selection-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--surface-card);
border-top: 1px solid var(--surface-border);
padding: var(--space-md);
display: flex;
justify-content: center;
align-items: center;
z-index: var(--z-fixed);
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
}
/* Container for action buttons */
.selection-actions {
display: flex;
gap: var(--space-sm);
width: 100%;
max-width: 400px;
justify-content: center;
}
/* Individual action buttons */
.selection-action-btn {
flex: 1;
height: 48px;
font-size: var(--text-base);
font-weight: var(--font-semibold);
/* Ensure minimum touch target size */
min-height: 48px;
}
/* Single action takes full width */
.selection-actions:has(.selection-action-btn:only-child) .selection-action-btn {
max-width: none;
}
/* ================================================
Slide-up Animation (Vue Transition)
================================================ */
.slide-up-enter-active,
.slide-up-leave-active {
transition: transform var(--transition-normal), opacity var(--transition-normal);
}
.slide-up-enter-from,
.slide-up-leave-to {
transform: translateY(100%);
opacity: 0;
}
.slide-up-enter-to,
.slide-up-leave-from {
transform: translateY(0);
opacity: 1;
}
/* ================================================
Dark Mode Support
================================================ */
/* Manual dark mode via data-theme attribute */
[data-theme="dark"] .mobile-selection-footer {
background: var(--surface-card);
border-top-color: var(--surface-border);
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.3);
}
/* Auto dark mode (when no manual theme is set) */
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) .mobile-selection-footer {
background: var(--surface-card);
border-top-color: var(--surface-border);
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.3);
}
}
</style>