refactor: reimplement command palette using a custom overlay instead of the native dialog element
This commit is contained in:
@@ -24,111 +24,113 @@ 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 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>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
@push('custom-scripts')
|
||||
<script>
|
||||
(function() {
|
||||
const searchInput = document.getElementById('navbarForm');
|
||||
const dialog = document.getElementById('cmdk-dialog');
|
||||
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');
|
||||
|
||||
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 = '';
|
||||
function showPalette() {
|
||||
container.style.display = 'block';
|
||||
input.value = searchInput ? searchInput.value : '';
|
||||
input.focus();
|
||||
cmdkItems.forEach(function(item) {
|
||||
item.style.display = '';
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
var query = e.target.value.toLowerCase();
|
||||
cmdkItems.forEach(function(item) {
|
||||
var value = item.dataset.value || item.textContent.toLowerCase();
|
||||
item.style.display = (query === '' || value.includes(query)) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
.cmdk-dialog {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
max-width: 700px;
|
||||
#command-palette-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
z-index: 1056;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
|
||||
.cmdk-dialog::backdrop {
|
||||
.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;
|
||||
@@ -166,8 +168,7 @@ $validMenuItems = collect($menuItems)->filter(fn($item) => Route::has($item['rou
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.cmdk-item:hover,
|
||||
.cmdk-item[data-selected="true"] {
|
||||
.cmdk-item:hover {
|
||||
background: var(--bs-tertiary-bg, #f1f5f9);
|
||||
}
|
||||
|
||||
@@ -195,8 +196,7 @@ $validMenuItems = collect($menuItems)->filter(fn($item) => Route::has($item['rou
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] .cmdk-item:hover,
|
||||
[data-bs-theme="dark"] .cmdk-item[data-selected="true"] {
|
||||
[data-bs-theme="dark"] .cmdk-item:hover {
|
||||
background: #334155;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user