header
This commit is contained in:
3
front/public/assets/icons/menu-svgrepo-com.svg
Normal file
3
front/public/assets/icons/menu-svgrepo-com.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 12H21M3 6H21M3 18H21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 225 B |
4
front/public/assets/icons/search-alt-1-svgrepo-com.svg
Normal file
4
front/public/assets/icons/search-alt-1-svgrepo-com.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.9536 14.9458L21 21M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 457 B |
3
front/public/assets/icons/search-svgrepo-com.svg
Normal file
3
front/public/assets/icons/search-svgrepo-com.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 21L16.514 16.506L21 21ZM19 10.5C19 15.194 15.194 19 10.5 19C5.806 19 2 15.194 2 10.5C2 5.806 5.806 2 10.5 2C15.194 2 19 5.806 19 10.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 340 B |
@@ -1 +1,86 @@
|
||||
<gallery></gallery>
|
||||
<header class="main-header">
|
||||
<div class="header-container">
|
||||
<!-- Logo/Marca -->
|
||||
<div class="logo">
|
||||
<h1>{{ brandName }}</h1>
|
||||
</div>
|
||||
|
||||
<!-- Buscador -->
|
||||
<div class="search-container">
|
||||
<div class="search-box">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Buscar fotos..."
|
||||
[(ngModel)]="searchQuery"
|
||||
(input)="onSearch($event)"
|
||||
class="search-input"
|
||||
/>
|
||||
<app-button
|
||||
label="Search"
|
||||
icon="assets/icons/search-alt-1-svgrepo-com.svg"
|
||||
class="search-button"
|
||||
(click)="performSearch()"
|
||||
tooltip="Buscar"
|
||||
></app-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Iconos de navegación -->
|
||||
<div class="nav-icons">
|
||||
<!-- Icono de perfil -->
|
||||
<app-button
|
||||
label="Profile"
|
||||
icon="assets/icons/user-svgrepo-com.svg"
|
||||
class="profile-button"
|
||||
(click)="openProfile()"
|
||||
tooltip="Mi perfil"
|
||||
></app-button>
|
||||
|
||||
<!-- Menú hamburguesa -->
|
||||
<app-button
|
||||
label="Menu"
|
||||
icon="assets/icons/menu-svgrepo-com.svg"
|
||||
class="menu-button"
|
||||
(click)="toggleMenu()"
|
||||
tooltip="Menú"
|
||||
[class.active]="isMenuOpen"
|
||||
></app-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Menú desplegable -->
|
||||
@if(isMenuOpen) {
|
||||
<div class="dropdown-menu" (click)="$event.stopPropagation()">
|
||||
<nav class="menu-nav">
|
||||
<a href="#" class="menu-item" (click)="navigateTo('gallery')">
|
||||
<svg-loader
|
||||
src="assets/icons/browsers-svgrepo-com.svg"
|
||||
></svg-loader>
|
||||
Galería
|
||||
</a>
|
||||
<a href="#" class="menu-item" (click)="navigateTo('about')">
|
||||
<svg-loader
|
||||
src="assets/icons/bubble-square-svgrepo-com.svg"
|
||||
></svg-loader>
|
||||
Acerca de
|
||||
</a>
|
||||
<a href="#" class="menu-item" (click)="navigateTo('contact')">
|
||||
<svg-loader
|
||||
src="assets/icons/bell-svgrepo-com.svg"
|
||||
></svg-loader>
|
||||
Contacto
|
||||
</a>
|
||||
<a href="#" class="menu-item" (click)="navigateTo('services')">
|
||||
<svg-loader
|
||||
src="assets/icons/cart-shopping-fast-svgrepo-com.svg"
|
||||
></svg-loader>
|
||||
Servicios
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<gallery></gallery>
|
||||
</main>
|
||||
|
@@ -0,0 +1,207 @@
|
||||
@use "../styles.scss" as *;
|
||||
@use "variables";
|
||||
@use "gallery/gallery";
|
||||
|
||||
.main-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(variables.$primary-white, 0.95);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid variables.$border-grey;
|
||||
box-shadow: 0 2px 20px rgba(42, 41, 38, 0.1);
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 2rem;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
gap: 2rem;
|
||||
|
||||
.logo {
|
||||
flex-shrink: 0;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: variables.$text-dark;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
flex: 1;
|
||||
max-width: 600px;
|
||||
margin: 0 2rem;
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: variables.$primary-white;
|
||||
border: 2px solid variables.$border-grey;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
|
||||
&:focus-within {
|
||||
border-color: variables.$edit-color;
|
||||
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
padding: 0.75rem 1rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 1rem;
|
||||
color: variables.$text-dark;
|
||||
outline: none;
|
||||
|
||||
&::placeholder {
|
||||
color: variables.$text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 2rem;
|
||||
background: variables.$primary-white;
|
||||
border: 1px solid variables.$border-grey;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 30px rgba(42, 41, 38, 0.15);
|
||||
min-width: 200px;
|
||||
overflow: hidden;
|
||||
animation: slideDown 0.3s ease;
|
||||
|
||||
.menu-nav {
|
||||
padding: 0.5rem 0;
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
color: variables.$text-dark;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s ease;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background: rgba(variables.$edit-color, 0.1);
|
||||
color: variables.$edit-color;
|
||||
}
|
||||
|
||||
svg-loader {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: calc(
|
||||
80px + (gallery.$gap-size * 2)
|
||||
); // Space for fixed header + 2rem
|
||||
min-height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
// Responsive design
|
||||
@media (max-width: 768px) {
|
||||
.main-header {
|
||||
.header-container {
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
|
||||
.logo h1 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
flex: 1;
|
||||
|
||||
.search-box .search-input {
|
||||
padding: 0.15rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-icons {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
right: 1rem;
|
||||
left: 1rem;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: calc(70px + 2rem);
|
||||
min-height: calc(100vh - 70px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.main-header {
|
||||
.header-container {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
|
||||
.logo {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.nav-icons {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
order: 3;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: calc(120px + 2rem);
|
||||
min-height: calc(100vh - 120px);
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +1,99 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit, HostListener } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Title, Meta } from '@angular/platform-browser';
|
||||
import { Gallery } from './gallery/gallery';
|
||||
import { Button } from './button/button';
|
||||
import { SvgLoader } from './svg/svg';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [ Gallery ],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss'
|
||||
selector: 'app-root',
|
||||
imports: [Gallery, Button, SvgLoader, FormsModule],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss',
|
||||
})
|
||||
export class App {
|
||||
protected title = 'mmorales.photo';
|
||||
protected description = 'Portfolio of Manuel Morales, a photographer based in La Vila Joiosa, Alicante, Spain.';
|
||||
protected keywords = 'photography, portfolio, Manuel Morales, La Vila Joiosa, Alicante, Spain';
|
||||
protected author = 'Manuel Morales';
|
||||
protected copyright = '© ' + new Date().getFullYear() + ' Manuel Morales. All rights reserved.';
|
||||
export class App implements OnInit {
|
||||
protected title = 'mmorales.photo';
|
||||
protected description =
|
||||
'Portfolio of Manuel Morales, a photographer based in La Vila Joiosa, Alicante, Spain.';
|
||||
protected keywords =
|
||||
'photography, portfolio, Manuel Morales, La Vila Joiosa, Alicante, Spain';
|
||||
protected author = 'Manuel Morales';
|
||||
protected copyright =
|
||||
'© ' +
|
||||
new Date().getFullYear() +
|
||||
' Manuel Morales. All rights reserved.';
|
||||
|
||||
protected gallery: Gallery = new Gallery();
|
||||
// Header properties
|
||||
protected brandName = 'MMORALES PHOTO';
|
||||
protected searchQuery = '';
|
||||
protected isMenuOpen = false;
|
||||
|
||||
constructor(private titleService: Title, private metaService: Meta) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Set page meta information
|
||||
this.titleService.setTitle(this.title);
|
||||
this.metaService.updateTag({
|
||||
name: 'description',
|
||||
content: this.description,
|
||||
});
|
||||
this.metaService.updateTag({
|
||||
name: 'keywords',
|
||||
content: this.keywords,
|
||||
});
|
||||
this.metaService.updateTag({ name: 'author', content: this.author });
|
||||
this.metaService.updateTag({
|
||||
name: 'copyright',
|
||||
content: this.copyright,
|
||||
});
|
||||
}
|
||||
|
||||
// Search functionality
|
||||
onSearch(event: Event): void {
|
||||
const target = event.target as HTMLInputElement;
|
||||
this.searchQuery = target.value;
|
||||
// TODO: Implement real-time search filtering
|
||||
console.log('Searching for:', this.searchQuery);
|
||||
}
|
||||
|
||||
performSearch(): void {
|
||||
if (this.searchQuery.trim()) {
|
||||
// TODO: Implement search logic
|
||||
console.log('Performing search for:', this.searchQuery);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation functionality
|
||||
openProfile(): void {
|
||||
// TODO: Implement profile functionality
|
||||
console.log('Opening profile...');
|
||||
}
|
||||
|
||||
toggleMenu(): void {
|
||||
this.isMenuOpen = !this.isMenuOpen;
|
||||
}
|
||||
|
||||
navigateTo(section: string): void {
|
||||
this.isMenuOpen = false; // Close menu after navigation
|
||||
// TODO: Implement navigation logic
|
||||
console.log('Navigating to:', section);
|
||||
}
|
||||
|
||||
// Close menu when clicking outside
|
||||
@HostListener('document:click', ['$event'])
|
||||
onDocumentClick(event: Event): void {
|
||||
const target = event.target as HTMLElement;
|
||||
if (!target.closest('.main-header')) {
|
||||
this.isMenuOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Close menu on escape key
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
onKeyDown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Escape' && this.isMenuOpen) {
|
||||
this.isMenuOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -118,4 +118,13 @@ button {
|
||||
fill: variables.$buy-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.search-button {
|
||||
border: none;
|
||||
&:hover {
|
||||
color: variables.$edit-color;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user