Implemented by Ralph autonomous loop. Iteration: 1 Co-Authored-By: Claude <noreply@anthropic.com>
219 lines
5.1 KiB
Vue
219 lines
5.1 KiB
Vue
<template>
|
|
<header class="mobile-top-bar" :class="{ 'selection-active': selectionActive }">
|
|
<!-- Left section -->
|
|
<div class="top-bar-left">
|
|
<Button
|
|
v-if="showBack"
|
|
icon="pi pi-arrow-left"
|
|
text
|
|
rounded
|
|
class="top-bar-btn"
|
|
@click="$emit('back-click')"
|
|
aria-label="Înapoi"
|
|
/>
|
|
<Button
|
|
v-else-if="showMenu"
|
|
icon="pi pi-bars"
|
|
text
|
|
rounded
|
|
class="top-bar-btn"
|
|
@click="$emit('menu-click')"
|
|
aria-label="Meniu"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Title section -->
|
|
<h1 class="top-bar-title">{{ title }}</h1>
|
|
|
|
<!-- Right section - actions -->
|
|
<div class="top-bar-right">
|
|
<Button
|
|
v-for="(action, index) in actions"
|
|
:key="index"
|
|
:icon="action.icon"
|
|
text
|
|
rounded
|
|
class="top-bar-btn"
|
|
:class="{ 'active': action.active }"
|
|
@click="(e) => $emit('action-click', action, e)"
|
|
:aria-label="action.label || action.icon"
|
|
v-tooltip.bottom="action.tooltip"
|
|
/>
|
|
</div>
|
|
</header>
|
|
</template>
|
|
|
|
<script setup>
|
|
import Button from 'primevue/button'
|
|
|
|
/**
|
|
* MobileTopBar - Material Design 3 inspired top navigation bar for mobile views
|
|
*
|
|
* Props:
|
|
* - title: The text displayed in the center of the bar
|
|
* - showBack: Shows back arrow button on the left
|
|
* - showMenu: Shows hamburger menu button on the left (ignored if showBack is true)
|
|
* - actions: Array of action buttons for the right side
|
|
* - selectionActive: Whether selection mode is active (changes background)
|
|
*
|
|
* Events:
|
|
* - menu-click: Emitted when menu button is clicked
|
|
* - back-click: Emitted when back button is clicked
|
|
* - action-click: Emitted when any action button is clicked, passes action object
|
|
*/
|
|
|
|
defineProps({
|
|
/**
|
|
* Title displayed in the center of the top bar
|
|
*/
|
|
title: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
/**
|
|
* Whether to show the back arrow button
|
|
*/
|
|
showBack: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
/**
|
|
* Whether to show the hamburger menu button
|
|
* (ignored if showBack is true)
|
|
*/
|
|
showMenu: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
/**
|
|
* Array of action buttons for the right side
|
|
* Each action should have: { icon: string, label?: string, tooltip?: string, active?: boolean }
|
|
*/
|
|
actions: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
/**
|
|
* Whether selection mode is active (changes background color)
|
|
*/
|
|
selectionActive: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
})
|
|
|
|
defineEmits(['menu-click', 'back-click', 'action-click'])
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* ================================================
|
|
MobileTopBar Component Styles
|
|
Material Design 3 inspired mobile navigation
|
|
================================================ */
|
|
|
|
.mobile-top-bar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 56px;
|
|
background: var(--surface-card);
|
|
border-bottom: 1px solid var(--surface-border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 var(--space-xs);
|
|
z-index: 1000;
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
/* Selection mode active state */
|
|
.mobile-top-bar.selection-active {
|
|
background: var(--blue-50);
|
|
border-bottom-color: var(--blue-200);
|
|
}
|
|
|
|
/* Left and right button containers */
|
|
.top-bar-left,
|
|
.top-bar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
/* Ensure minimum width for consistent layout */
|
|
min-width: 48px;
|
|
}
|
|
|
|
/* Individual action buttons */
|
|
.top-bar-btn {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: var(--radius-full);
|
|
color: var(--text-color);
|
|
}
|
|
|
|
/* Active state for buttons (e.g., filter active) */
|
|
.top-bar-btn.active {
|
|
color: var(--color-primary);
|
|
background: var(--blue-50);
|
|
}
|
|
|
|
/* Title in the center */
|
|
.top-bar-title {
|
|
font-size: var(--text-lg);
|
|
font-weight: var(--font-semibold);
|
|
color: var(--text-color);
|
|
margin: 0;
|
|
flex: 1;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* ================================================
|
|
Dark Mode Support
|
|
================================================ */
|
|
|
|
/* Manual dark mode via data-theme attribute */
|
|
[data-theme="dark"] .mobile-top-bar {
|
|
background: var(--surface-card);
|
|
border-bottom-color: var(--surface-border);
|
|
}
|
|
|
|
[data-theme="dark"] .mobile-top-bar.selection-active {
|
|
background: var(--blue-900);
|
|
border-bottom-color: var(--blue-700);
|
|
}
|
|
|
|
[data-theme="dark"] .top-bar-btn {
|
|
color: var(--text-color);
|
|
}
|
|
|
|
[data-theme="dark"] .top-bar-btn.active {
|
|
color: var(--blue-400);
|
|
background: var(--blue-900);
|
|
}
|
|
|
|
/* Auto dark mode (when no manual theme is set) */
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme]) .mobile-top-bar {
|
|
background: var(--surface-card);
|
|
border-bottom-color: var(--surface-border);
|
|
}
|
|
|
|
:root:not([data-theme]) .mobile-top-bar.selection-active {
|
|
background: var(--blue-900);
|
|
border-bottom-color: var(--blue-700);
|
|
}
|
|
|
|
:root:not([data-theme]) .top-bar-btn {
|
|
color: var(--text-color);
|
|
}
|
|
|
|
:root:not([data-theme]) .top-bar-btn.active {
|
|
color: var(--blue-400);
|
|
background: var(--blue-900);
|
|
}
|
|
}
|
|
</style>
|