Files
laravel-koneko-layout-porto/resources/views/components/header/header-background.blade.php

334 lines
15 KiB
PHP

@props([
/**
* =============================================================================
* header-background.blade.php — Porto v12
* Componente de Header con imagen/patrón/parallax/video + overlay y animaciones
* =============================================================================
*
* ➤ Props principales
* - title : string | Título principal (permite HTML).
* - subtitle : string | Subtítulo opcional (permite HTML).
* - breadcrumbs : array | [ ['name'=>..., 'href|route|params'=>..., 'active'=>bool], ... ]
* - align : string | left | center | right (layout de columnas)
* - size : string | sm | md | lg (alto del header)
* - container : string | container | container-fluid
*
* ➤ Fondo / Modo
* - mode : string | image | fixed | parallax | video | pattern
* - bgImage : string | URL (img, patrón o poster de video)
* - bgPosition : string | CSS background-position (ej: '50% 50%')
* - bgPatternClass : string | Clase extra p/ patrones (opcional)
* - parallaxSpeed : float | Velocidad (default 1.2) — sólo mode=parallax
* - videoPath : string | Ruta del video — sólo mode=video
* - videoPosterType : string | 'jpg' | 'png' (default 'jpg') — mode=video
*
* ➤ Overlay
* - overlayColor : string | primary | secondary | tertiary | quaternary | dark | light
* - overlayOpacity : string | '0-10' | '0-9' → se mapea a 'overlay-op-X' (string por compat)
* - showOverlay : bool | Mostrar/cargar overlay
*
* ➤ Tipografía y color
* - titleColor : string | auto | primary | secondary | tertiary | quaternary | dark | light | <clases>
* - titleSize : string | sm | md | lg
* - uppercaseTitle : bool | TRUE = text-uppercase
* - breadcrumbLight : bool | fuerza breadcrumb-light
*
* ➤ Animaciones (Porto appear-animation)
* - titleAnimation : ?string | nombre de animación o null
* - titleDelay : int | ms
* - titleDuration : ?string | ej: '1s'
* - subtitleAnimation: ?string | idem
* - subtitleDelay : int | ms
* - subtitleDuration : ?string
* - crumbAnimation : ?string | idem
* - crumbDelay : int | ms
* - crumbDuration : ?string
* - mediaAnimation : ?string | idem
* - mediaDelay : int | ms
* - mediaDuration : ?string
* - overflowTitle : bool | envuelve <h1> en .overflow-hidden
* - overflowSubtitle : bool | idem para subtítulo
* - overflowBreadcrumb: bool | idem para breadcrumb
*
* ➤ Slots
* - media : Columna derecha (imágenes, mockups, etc.)
* - cta : Botones/acciones bajo el título
*
* ────────────────────────────────────────────────────────────────────────────
* USO (ejemplos)
* ---------------------------------------------------------------------------
* 1) Imagen + overlay
* <x-porto::header-background
* title="<strong>Servicios</strong>"
* subtitle="Soluciones integrales para tu condominio"
* :breadcrumbs="[['name'=>'Inicio','href'=>url('/')],['name'=>'Servicios','active'=>true]]"
* mode="image"
* bgImage="{{ asset('vendor/porto/img/page-header/header.jpg') }}"
* overlayColor="primary"
* overlayOpacity="8"
* titleAnimation="fadeInUpShorter"
* crumbAnimation="fadeInUpShorter"
* />
*
* 2) Parallax + media + CTA
* <x-porto::header-background
* title="Introducing the new <strong>Product</strong>"
* :breadcrumbs="[['name'=>'Home','href'=>'/'],['name'=>'Features','active'=>true]]"
* mode="parallax"
* bgImage="{{ asset('img/page-header/page-header-parallax.jpg') }}"
* parallaxSpeed="1.2"
* align="left"
* >
* <x-slot:cta>
* <a href="#" class="btn btn-modern btn-dark mt-4">Buy Now <i class="fas fa-arrow-right ms-1"></i></a>
* </x-slot:cta>
* <x-slot:media>
* <img src="{{ asset('img/custom-header.png') }}" class="img-fluid" alt="">
* </x-slot:media>
* </x-porto::header-background>
*
* 3) Video de fondo
* <x-porto::header-background
* title="<strong>Memories</strong>"
* mode="video"
* videoPath="{{ asset('video/memory-of-a-woman.mp4') }}"
* overlayColor="dark"
* overlayOpacity="7"
* size="lg"
* />
*/
// ── Texto y breadcrumbs
'title' => '',
'subtitle' => '',
'breadcrumbs' => [['name' => 'Inicio', 'href' => '/', 'active' => true]],
// ── Layout
'align' => 'center', // left | center | right
'size' => 'md', // sm | md | lg
'container' => 'container', // container | container-fluid
// ── Fondo / Modo
'mode' => 'image', // image | fixed | parallax | video | pattern
'bgImage' => null, // URL
'bgPosition' => '50% 50%',
'bgPatternClass' => 'page-header-background-pattern',
'parallaxSpeed' => 1.2,
'videoPath' => null,
'videoPosterType' => 'jpg',
// ── Overlay
'overlayColor' => 'dark', // primary|secondary|tertiary|quaternary|dark|light
'overlayOpacity' => '7', // '0'..'10' (string por compat con Porto)
'showOverlay' => true,
// ── Tipografía
'titleColor' => 'auto', // auto|primary|secondary|tertiary|quaternary|dark|light|<clases>
'titleSize' => 'lg', // sm|md|lg (aplica a clase text-*)
'uppercaseTitle' => false,
'breadcrumbLight' => null, // null => auto; true/false fuerza
// ── Animaciones
'titleAnimation' => null,
'titleDelay' => 0,
'titleDuration' => null,
'subtitleAnimation' => null,
'subtitleDelay' => 0,
'subtitleDuration' => null,
'crumbAnimation' => null,
'crumbDelay' => 0,
'crumbDuration' => null,
'mediaAnimation' => null,
'mediaDelay' => 0,
'mediaDuration' => null,
'overflowTitle' => false,
'overflowSubtitle' => false,
'overflowBreadcrumb'=> false,
])
@php
// ===== Normalizaciones =====
$align = in_array($align, ['left','center','right'], true) ? $align : 'center';
$sizeMap = ['sm' => 'page-header-sm', 'md' => 'page-header-md', 'lg' => 'page-header-lg'];
$sizeClass = $sizeMap[$size] ?? $sizeMap['md'];
// ===== Clases base del header =====
$classes = ['page-header','page-header-modern', $sizeClass];
// Modo de fondo
$hasBackground = in_array($mode, ['image','fixed','parallax','video','pattern'], true);
if ($hasBackground) {
$classes[] = 'page-header-background';
if ($mode === 'pattern') {
$classes[] = $bgPatternClass; // ej: page-header-background-pattern
}
}
// Overlay
$overlayClasses = [];
if ($showOverlay) {
$overlayClasses = ['overlay', 'overlay-show', "overlay-color-{$overlayColor}", "overlay-op-{$overlayOpacity}"];
}
// Color de título
$semanticMap = [
'primary' => 'text-color-primary',
'secondary' => 'text-color-secondary',
'tertiary' => 'text-color-tertiary',
'quaternary' => 'text-color-quaternary',
'dark' => 'text-dark',
'light' => 'text-light',
];
$titleClasses = ['font-weight-bold'];
$titleSizeMap = ['sm' => 'text-6', 'md' => 'text-9', 'lg' => 'text-10'];
$titleClasses[] = $titleSizeMap[$titleSize] ?? $titleSizeMap['lg'];
if ($titleColor === 'auto') {
// Heurística: si hay overlay dark -> texto claro; si overlay light -> texto oscuro
$titleClasses[] = (in_array($overlayColor, ['dark','primary','secondary','tertiary','quaternary'], true)) ? 'text-light' : 'text-dark';
} elseif (isset($semanticMap[$titleColor])) {
$titleClasses[] = $semanticMap[$titleColor];
} elseif (is_string($titleColor) && $titleColor !== '') {
$titleClasses[] = $titleColor; // acepta clases crudas
}
if ($uppercaseTitle) $titleClasses[] = 'text-uppercase';
// Breadcrumbs
$ulClasses = ['breadcrumb','d-block'];
$ulClasses[] = match ($align) {
'left' => 'text-md-start',
'right' => 'text-md-end',
default => 'text-center',
};
// Breadcrumb light/dark: auto si null
if ($breadcrumbLight === null) {
$isDarkishOverlay = in_array($overlayColor, ['dark','primary','secondary','tertiary','quaternary'], true);
if ($isDarkishOverlay && $showOverlay) $ulClasses[] = 'breadcrumb-light';
} elseif ($breadcrumbLight === true) {
$ulClasses[] = 'breadcrumb-light';
}
// Columnas
$titleCol = 'col-sm-12 col-md-'.($slot('media')->isNotEmpty() ? '5' : '12');
$mediaCol = 'col-sm-12 col-md-7 d-flex align-items-end justify-content-end';
if ($align === 'left') {
$titleWrap = 'order-2 order-sm-1 align-self-center p-static';
$crumbWrap = 'order-1';
} elseif ($align === 'right') {
$titleWrap = 'order-2 order-sm-2 align-self-center p-static text-end';
$crumbWrap = 'order-1 text-md-start';
} else { // center
$titleWrap = 'align-self-center p-static order-2 text-center';
$crumbWrap = 'order-1';
}
// Helpers de animación -> atributos data-*
$animAttrs = function (?string $name, int $delay = 0, ?string $duration = null) {
if (!$name) return '';
$out = 'appear-animation';
$out .= '" data-appear-animation="'.$name.'"';
if ($delay > 0) $out .= ' data-appear-animation-delay="'.$delay.'"';
if ($duration) $out .= ' data-appear-animation-duration="'.$duration.'"';
return $out;
};
@endphp
<section class="{{ implode(' ', $classes) }} {{ implode(' ', $overlayClasses) }} mb-0"
@if($hasBackground && $mode !== 'parallax' && $mode !== 'video')
style="background-image: url('{{ $bgImage }}'); background-position: {{ $bgPosition }};"
@endif
@if($mode === 'parallax')
data-plugin-parallax data-plugin-options='{"speed": {{ $parallaxSpeed }} }' data-image-src="{{ $bgImage }}"
@endif
@if($mode === 'video' && $videoPath)
data-plugin-video-background data-video-path="{{ $videoPath }}" data-plugin-options='{"posterType":"{{ $videoPosterType }}", "position":"{{ $bgPosition }}"}'
@endif
>
<div class="{{ $container }}">
<div class="row">
{{-- Breadcrumbs fila superior (centrado/izq/der según align) --}}
<div class="col-12 align-self-center {{ $crumbWrap }}">
@if($overflowBreadcrumb)<div class="overflow-hidden">@endif
<ul class="{{ implode(' ', $ulClasses) }} {{ $crumbAnimation ? 'appear-animation' : '' }}"
@if($crumbAnimation)
data-appear-animation="{{ $crumbAnimation }}"
@if($crumbDelay) data-appear-animation-delay="{{ $crumbDelay }}" @endif
@if($crumbDuration) data-appear-animation-duration="{{ $crumbDuration }}" @endif
@endif
>
@foreach ($breadcrumbs as $breadcrumb)
@php
$name = $breadcrumb['name'] ?? '';
$active = (bool)($breadcrumb['active'] ?? false);
$route = $breadcrumb['route'] ?? null;
$params = $breadcrumb['params'] ?? [];
$href = $breadcrumb['href'] ?? null;
$routeExists = $route && \Illuminate\Support\Facades\Route::has($route);
$url = $routeExists ? route($route, $params) : ($href ?: null);
@endphp
<li class="{{ $active ? 'active' : '' }}" @if($active) aria-current="page" @endif>
@if (!$active && $url)
<a href="{{ $url }}">{{ $name }}</a>
@else
<span>{{ $name }}</span>
@endif
</li>
@endforeach
</ul>
@if($overflowBreadcrumb)</div>@endif
</div>
</div>
<div class="row">
{{-- Columna de título + subtítulo + CTA --}}
<div class="{{ $titleCol }} {{ $titleWrap }}">
@if($overflowTitle)<div class="overflow-hidden">@endif
<h1 class="{{ implode(' ', $titleClasses) }} {{ $titleAnimation ? 'appear-animation' : '' }}"
@if($titleAnimation)
data-appear-animation="{{ $titleAnimation }}"
@if($titleDelay) data-appear-animation-delay="{{ $titleDelay }}" @endif
@if($titleDuration) data-appear-animation-duration="{{ $titleDuration }}" @endif
@endif
>{!! $title !!}</h1>
@if($overflowTitle)</div>@endif
@if(!empty($subtitle))
@if($overflowSubtitle)<div class="overflow-hidden">@endif
<span class="sub-title text-4 mt-2 d-block {{ $subtitleAnimation ? 'appear-animation' : '' }}"
@if($subtitleAnimation)
data-appear-animation="{{ $subtitleAnimation }}"
@if($subtitleDelay) data-appear-animation-delay="{{ $subtitleDelay }}" @endif
@if($subtitleDuration) data-appear-animation-duration="{{ $subtitleDuration }}" @endif
@endif
>{!! $subtitle !!}</span>
@if($overflowSubtitle)</div>@endif
@endif
@if(trim($slot('cta')))
<div class="mt-3 {{ $mediaAnimation ? '' : '' }}">{{ $slot('cta') }}</div>
@endif
</div>
{{-- Columna media opcional --}}
@if(trim($slot('media')))
<div class="{{ $mediaCol }}">
<div class="overflow-hidden" style="min-height: 200px;">
<div class="{{ $mediaAnimation ? 'appear-animation' : '' }}"
@if($mediaAnimation)
data-appear-animation="{{ $mediaAnimation }}"
@if($mediaDelay) data-appear-animation-delay="{{ $mediaDelay }}" @endif
@if($mediaDuration) data-appear-animation-duration="{{ $mediaDuration }}" @endif
@endif
>
{{ $slot('media') }}
</div>
</div>
</div>
@endif
</div>
</div>
</section>