diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php
index 2ec2360..27973bb 100644
--- a/app/Services/TemplateService.php
+++ b/app/Services/TemplateService.php
@@ -5,7 +5,10 @@ namespace App\Services;
use App\Models\HeaderTemplate;
use App\Models\SectionTemplate;
use App\Models\FooterTemplate;
+use App\Models\Setting;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\View;
use Filament\Forms\Components\{
TextInput, Textarea, Select, Checkbox, CheckboxList,
Radio, Toggle, ToggleButtons, DateTimePicker, DatePicker,
@@ -294,11 +297,15 @@ class TemplateService
*/
protected static function getSelectOptions(string $fieldName): array
{
- return config("template-options.{$fieldName}", [
+ $defaults = [
'option_1' => __('Option 1'),
'option_2' => __('Option 2'),
'option_3' => __('Option 3'),
- ]);
+ ];
+
+ $options = config("template-options.{$fieldName}", $defaults);
+
+ return is_array($options) ? $options : $defaults;
}
/**
@@ -376,6 +383,15 @@ class TemplateService
}
// Handle custom blade components: {custom.component_name} or {custom.component-name}
+ if (str_contains($html, '{custom.language-selector}')) {
+ $desktopLanguage = view('components.custom.language-selector', [
+ 'variant' => 'default',
+ ])->render();
+
+ $html = str_replace('{custom.language-selector}', $desktopLanguage, $html);
+ $html = self::injectOffcanvasLanguageSelector($html);
+ }
+
preg_match_all('/\{custom\.([a-z][a-z_-]*)\}/i', $html, $customMatches);
if (!empty($customMatches[0])) {
foreach ($customMatches[0] as $index => $fullMatch) {
@@ -383,8 +399,12 @@ class TemplateService
if ($componentName) {
// Normalize component name to lowercase for consistent file system access
$componentName = strtolower($componentName);
+
+ if ($componentName === 'language-selector') {
+ continue;
+ }
$viewPath = "components.custom.{$componentName}";
- if (view()->exists($viewPath)) {
+ if (View::exists($viewPath)) {
try {
$renderedComponent = view($viewPath)->render();
// Ensure rendered component is properly formatted
@@ -392,7 +412,7 @@ class TemplateService
$html = str_replace($fullMatch, $renderedComponent, $html);
} catch (\Exception $e) {
// Log the error for debugging
- \Log::error("Custom component render error: {$viewPath}", [
+ Log::error("Custom component render error: {$viewPath}", [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
@@ -502,10 +522,10 @@ class TemplateService
// Handle setting.* placeholders - get value from Setting model
if ($type === 'setting' && $field) {
$value = setting($field, '');
-
+
// Get setting type to determine if it's a file/image
- $settingModel = \App\Models\Setting::where('key', $field)->where('is_active', true)->first();
- if ($settingModel && in_array($settingModel->type, ['file', 'image'])) {
+ $settingModel = Setting::findActiveByKey($field);
+ if ($settingModel && in_array($settingModel->type, ['file', 'image'], true)) {
// For file/image settings, use file type for formatting
$type = 'file';
}
@@ -660,5 +680,32 @@ class TemplateService
return $value;
}
+
+ /**
+ * Inject mobile language selector at the top of the first offcanvas menu body.
+ */
+ protected static function injectOffcanvasLanguageSelector(string $html): string
+ {
+ if (str_contains($html, 'offcanvas-language-mobile-wrap')) {
+ return $html;
+ }
+
+ $mobileLanguage = view('components.custom.language-selector', [
+ 'variant' => 'offcanvas',
+ 'class' => 'offcanvas-language-mobile',
+ ])->render();
+
+ $injection = '
'
+ . $mobileLanguage
+ . '
';
+
+ $pattern = '/(]*class="[^"]*offcanvas-body[^"]*"[^>]*>)/i';
+
+ if (preg_match($pattern, $html)) {
+ return preg_replace($pattern, '$1' . $injection, $html, 1);
+ }
+
+ return $html;
+ }
}
diff --git a/resources/views/components/custom/language-selector.blade.php b/resources/views/components/custom/language-selector.blade.php
index 9736765..69db859 100644
--- a/resources/views/components/custom/language-selector.blade.php
+++ b/resources/views/components/custom/language-selector.blade.php
@@ -1,31 +1,77 @@
+@props([
+ 'variant' => 'default',
+])
+
@php
$languages = available_languages();
$currentLang = current_language_code();
$currentLanguage = $languages->firstWhere('code', $currentLang);
+ $languageCollapseId = 'offcanvas-language-collapse';
+
+ $wrapperClass = $variant === 'offcanvas'
+ ? 'language-select-offcanvas w-full'
+ : 'navbar-other !ml-5 header-language-desktop !hidden lg:!flex xl:!flex shrink-0';
+
+ $linkClass = 'nav-link dropdown-item dropdown-toggle xl:!text-[.85rem] lg:!text-[.85rem] max-lg:!text-[1.05rem]';
@endphp
-
+@endif
diff --git a/resources/views/layouts/site.blade.php b/resources/views/layouts/site.blade.php
index 48ae890..18aed61 100644
--- a/resources/views/layouts/site.blade.php
+++ b/resources/views/layouts/site.blade.php
@@ -85,9 +85,44 @@ $favicon_path = setting('site_favicon');
max-width: 100%;
}
- header .container,
- .wrapper > .container {
- max-width: 100%;
+
+
+ header .header-language-desktop {
+ display: none !important;
+ }
+
+ @media (min-width: 992px) {
+ header .header-language-desktop {
+ display: flex !important;
+ }
+ }
+
+ @media (max-width: 991.98px) {
+ header nav .container > .navbar-other:has(> .navbar-nav > .language-select) {
+ display: none !important;
+ }
+
+ .offcanvas .language-select-offcanvas__toggle {
+ width: 100%;
+ text-align: left;
+ }
+
+ .offcanvas .language-select-offcanvas__panel {
+ position: static !important;
+ inset: auto !important;
+ transform: none !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ border: 0 !important;
+ }
+
+ .offcanvas .language-select-offcanvas__panel .nav-link {
+ padding-left: 0 !important;
+ }
+
+ .offcanvas .language-select-offcanvas__toggle[aria-expanded="true"] .language-select-offcanvas__icon {
+ transform: rotate(180deg);
+ }
}
diff --git a/resources/views/partials/header.blade.php b/resources/views/partials/header.blade.php
index ac05b68..761091d 100644
--- a/resources/views/partials/header.blade.php
+++ b/resources/views/partials/header.blade.php
@@ -51,11 +51,14 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
-
+
@include("components.custom.language-selector")
+
+ @include('components.custom.language-selector', ['variant' => 'offcanvas'])
+
@@ -149,12 +154,12 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
{{-- Mobile menu is dark, so we only need the light (white) logo here --}}
-
- @include("components.custom.language-selector")
-
-
+
-
+
+
+ @include('components.custom.language-selector', ['variant' => 'offcanvas'])
+
@foreach($menuItems as $item)
@@ -218,6 +223,17 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
.hamburger.is-active::after {
transform: translateY(-6px) rotate(-45deg) !important;
}
+
+@media (max-width: 991.98px) {
+ header nav > .container > .flex.xl\:!hidden .language-select,
+ header .navbar-collapse-wrapper .language-select {
+ display: none !important;
+ }
+
+ .offcanvas .language-select-offcanvas__toggle[aria-expanded="true"] .language-select-offcanvas__icon {
+ transform: rotate(180deg);
+ }
+}
@endpush