Files
portal-api/resources/views/components/command-palette.blade.php
T

242 lines
6.7 KiB
PHP

@php
use Illuminate\Support\Facades\Route;
$sidebar = config('admin-sidebar');
$menuItems = [];
function extractRoutes($items, &$menuItems, $group = '') {
foreach ($items as $item) {
if (isset($item['type']) && $item['type'] === 'group' && isset($item['children'])) {
extractRoutes($item['children'], $menuItems, $item['title']);
} elseif (isset($item['route'])) {
$menuItems[] = [
'title' => $item['title'],
'route' => $item['route'],
'params' => $item['params'] ?? [],
'group' => $group,
];
}
}
}
extractRoutes($sidebar, $menuItems);
$validMenuItems = collect($menuItems)->filter(fn($item) => Route::has($item['route']))->values();
@endphp
<div id="command-palette-container" style="display: none;">
<div class="command-palette-backdrop" id="cmdk-backdrop"></div>
<div class="command-palette-wrapper" id="cmdk-wrapper">
<div class="cmdk">
<input type="text" class="cmdk-input" placeholder="Search pages..." id="cmdk-input">
<div class="cmdk-list" id="cmdk-list">
@forelse($validMenuItems as $item)
<a href="{{ route($item['route'], $item['params']) }}"
class="cmdk-item"
data-value="{{ strtolower($item['title']) }}">
<span>{{ $item['title'] }}</span>
@if($item['group'])
<span class="cmdk-group">{{ $item['group'] }}</span>
@endif
</a>
@empty
<div class="cmdk-empty">No pages found</div>
@endforelse
<div class="cmdk-empty-state" id="cmdk-empty" style="display: none;">
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-box"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
<span>No results found</span>
</div>
</div>
</div>
</div>
</div>
@push('custom-scripts')
<script>
(function() {
var container = document.getElementById('command-palette-container');
var backdrop = document.getElementById('cmdk-backdrop');
var wrapper = document.getElementById('cmdk-wrapper');
var input = document.getElementById('cmdk-input');
var searchInput = document.getElementById('navbarForm');
var cmdkItems = document.querySelectorAll('.cmdk-item');
var cmdkEmpty = document.getElementById('cmdk-empty');
var cmdkList = document.getElementById('cmdk-list');
function showPalette() {
container.style.display = 'block';
input.value = searchInput ? searchInput.value : '';
input.focus();
cmdkItems.forEach(function(item) {
item.style.display = '';
});
if (cmdkEmpty) cmdkEmpty.style.display = 'none';
}
function hidePalette() {
container.style.display = 'none';
if (searchInput) searchInput.blur();
}
function filterItems(query) {
var visibleCount = 0;
cmdkItems.forEach(function(item) {
var value = item.dataset.value || item.textContent.toLowerCase();
var matches = query === '' || value.includes(query);
item.style.display = matches ? '' : 'none';
if (matches) visibleCount++;
});
if (cmdkEmpty) {
cmdkEmpty.style.display = visibleCount === 0 ? 'flex' : 'none';
}
}
function hidePalette() {
container.style.display = 'none';
if (searchInput) searchInput.blur();
}
if (searchInput) {
searchInput.addEventListener('focus', showPalette);
searchInput.addEventListener('input', function(e) {
if (container.style.display === 'none') {
showPalette();
}
filterItems(e.target.value.toLowerCase());
});
}
input.addEventListener('input', function(e) {
filterItems(e.target.value.toLowerCase());
});
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
showPalette();
}
if (e.key === 'Escape' && container.style.display === 'block') {
hidePalette();
}
});
if (backdrop) {
backdrop.addEventListener('click', hidePalette);
}
})();
</script>
<style>
#command-palette-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
}
.command-palette-backdrop {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.command-palette-wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
max-width: 700px;
padding: 0 20px;
}
.cmdk {
background: var(--bs-body-bg, #fff);
border-radius: 12px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
overflow: hidden;
border: 1px solid var(--bs-border-color, #e2e8f0);
}
.cmdk-input {
width: 100%;
padding: 16px;
font-size: 1.125rem;
border: none;
border-bottom: 1px solid var(--bs-border-color, #e2e8f0);
background: transparent;
outline: none;
color: var(--bs-body-color, #1e293b);
}
.cmdk-list {
max-height: 400px;
overflow-y: auto;
padding: 8px;
}
.cmdk-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-radius: 8px;
text-decoration: none;
color: var(--bs-body-color, #1e293b);
cursor: pointer;
transition: background 0.15s;
}
.cmdk-item:hover {
background: var(--bs-tertiary-bg, #f1f5f9);
}
.cmdk-group {
font-size: 0.75rem;
color: var(--bs-secondary-color, #64748b);
}
.cmdk-empty {
padding: 24px;
text-align: center;
color: var(--bs-secondary-color, #64748b);
}
.cmdk-empty-state {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding: 32px;
color: var(--bs-secondary-color, #64748b);
}
.cmdk-empty-state svg {
opacity: 0.5;
}
[data-bs-theme="dark"] .cmdk {
background: #1e293b;
border-color: #334155;
}
[data-bs-theme="dark"] .cmdk-input {
color: #f8fafc;
}
[data-bs-theme="dark"] .cmdk-item {
color: #f8fafc;
}
[data-bs-theme="dark"] .cmdk-item:hover {
background: #334155;
}
</style>
@endpush