feat: Implement a command palette for quick navigation within the administrator panel.
This commit is contained in:
Generated
+13052
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"animate.css": "^4.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"flyonui": "^2.1.0",
|
||||
"laravel-echo": "^1.16.1",
|
||||
"micromodal": "^0.6.1",
|
||||
|
||||
@@ -237,4 +237,6 @@
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<x-command-palette />
|
||||
</nav>
|
||||
@@ -0,0 +1,203 @@
|
||||
@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
|
||||
|
||||
<dialog id="cmdk-dialog" class="cmdk-dialog">
|
||||
<div class="cmdk">
|
||||
<input type="text" class="cmdk-input" placeholder="Search pages...">
|
||||
<div class="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>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
@push('custom-scripts')
|
||||
<script>
|
||||
(function() {
|
||||
const searchInput = document.getElementById('navbarForm');
|
||||
const dialog = document.getElementById('cmdk-dialog');
|
||||
|
||||
console.log('Search input:', searchInput);
|
||||
console.log('Dialog:', dialog);
|
||||
|
||||
if (!searchInput) {
|
||||
console.error('navbarForm not found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dialog) {
|
||||
console.error('cmdk-dialog not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const cmdkInput = dialog.querySelector('.cmdk-input');
|
||||
const cmdkItems = dialog.querySelectorAll('.cmdk-item');
|
||||
|
||||
console.log('CMDK items:', cmdkItems.length);
|
||||
|
||||
searchInput.addEventListener('focus', function() {
|
||||
console.log('Focus triggered');
|
||||
dialog.showModal();
|
||||
});
|
||||
|
||||
searchInput.addEventListener('input', function(e) {
|
||||
const query = e.target.value.toLowerCase();
|
||||
console.log('Search query:', query);
|
||||
|
||||
if (!dialog.hasAttribute('open')) {
|
||||
dialog.showModal();
|
||||
}
|
||||
|
||||
cmdkItems.forEach(function(item) {
|
||||
const value = item.dataset.value || item.textContent.toLowerCase();
|
||||
const match = query === '' || value.includes(query);
|
||||
item.style.display = match ? '' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
dialog.showModal();
|
||||
cmdkInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.addEventListener('click', function(e) {
|
||||
if (e.target === dialog) {
|
||||
dialog.close();
|
||||
searchInput.blur();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.addEventListener('close', function() {
|
||||
searchInput.value = '';
|
||||
cmdkItems.forEach(function(item) {
|
||||
item.style.display = '';
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.cmdk-dialog {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
max-width: 700px;
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
z-index: 1056;
|
||||
}
|
||||
|
||||
|
||||
.cmdk-dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.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,
|
||||
.cmdk-item[data-selected="true"] {
|
||||
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);
|
||||
}
|
||||
|
||||
[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,
|
||||
[data-bs-theme="dark"] .cmdk-item[data-selected="true"] {
|
||||
background: #334155;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user