Konfigurasi Pencarian Panel
Sebuah resource menentukan apa yang dapat dicari; panel menentukan apakah palet pencarian tersedia, berapa banyak hasil yang dapat dikembalikan oleh satu pencarian, berapa lama palet menunggu sebelum mengirim request, dan shortcut apa yang digunakan untuk membukanya. Keempat pengaturan tersebut berada pada satu method: Panel::globalSearch().
Contoh minimal yang dapat digunakan
<?php
declare(strict_types=1);
namespace App\Panels\Admin;
use PandaPanel\Core\Panel;
use PandaPanel\Core\PanelProvider;
final class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->path('admin')
->auth()
->discoverResources(app_path('Panels/Admin/Resources'))
->globalSearch(
enabled: true,
limit: 50,
debounce: 300,
keyBindings: ['mod+k'],
);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Nilai di atas adalah konfigurasi default yang dituliskan secara eksplisit. Panel yang tidak pernah memanggil globalSearch() akan berperilaku sama persis seperti contoh tersebut.
Method
namespace PandaPanel\Core;
/**
* @param list<string> $keyBindings
*/
public function globalSearch(
bool $enabled = true,
int $limit = 50,
int $debounce = 300,
array $keyBindings = ['mod+k'],
): self;2
3
4
5
6
7
8
9
10
11
| Argument | Tipe | Default | Keterangan |
|---|---|---|---|
$enabled | bool | true | menentukan apakah panel memiliki palet pencarian |
$limit | int | 50 | jumlah hasil untuk seluruh pencarian, bukan per resource |
$debounce | int | 300 | waktu tunggu dalam milidetik setelah input terakhir |
$keyBindings | list<string> | ['mod+k'] | shortcut untuk membuka palet |
Semua argument memiliki nilai default, sehingga Anda dapat menentukan hanya argument yang ingin diubah:
$panel->globalSearch(limit: 20); // gunakan default untuk yang lain
$panel->globalSearch(false); // nonaktifkan sepenuhnya
$panel->globalSearch(debounce: 500, keyBindings: ['mod+k', 'mod+shift+f']);2
3
Perhatikan bahwa method ini bekerja sebagai setter, bukan accumulator. Memanggilnya dua kali akan mengganti keempat nilai menggunakan argument dari pemanggilan kedua, termasuk nilai default. Jadi ->globalSearch(limit: 20) yang diikuti ->globalSearch(debounce: 500) akan mengembalikan limit menjadi 50.
Method pembaca
public function hasGlobalSearch(): bool; // default true
public function getGlobalSearchLimit(): int; // default 50
public function getGlobalSearchDebounce(): int; // default 300
public function getGlobalSearchKeyBindings(): array; // list<string>, default ['mod+k']2
3
4
use PandaPanel\Core\PanelManager;
$panel = app(PanelManager::class)->get('admin');
$panel->hasGlobalSearch(); // true
$panel->getGlobalSearchLimit(); // 50
$panel->getGlobalSearchKeyBindings(); // ['mod+k']2
3
4
5
6
7
Method tersebut digunakan oleh PandaPanel\Search\GlobalSearch dan PandaPanel\Http\Middleware\SharePanelData; komponen lain tidak membaca properti private secara langsung.
Mengaktifkan atau menonaktifkan pencarian
hasGlobalSearch() hanya menjawab sebagian kondisi. Nilai yang benar-benar dikirim ke frontend adalah:
$enabled = $panel->hasGlobalSearch() && $searchable;$searchable bernilai true jika minimal satu resource yang terdaftar pada panel mengembalikan true dari isGloballySearchable(). Palet yang hanya akan selalu menghasilkan daftar kosong lebih buruk daripada tidak menampilkan palet sama sekali. Karena itu, panel dengan pencarian aktif tetapi tanpa resource yang dapat dicari tidak akan menampilkan palet — bahkan tombol pada header pun tidak ditampilkan.
$panel->globalSearch(false);Menonaktifkan pencarian menghasilkan dua efek. Shared prop search.enabled menjadi false dan search.url menjadi null sehingga palet tidak me-render apa pun; selain itu, GlobalSearch::for() mengembalikan [] untuk panel tersebut meskipun dipanggil secara langsung. Route pencarian tetap ada karena route didaftarkan untuk setiap panel, tetapi response-nya adalah {"groups": []}.
Limit
Limit milik panel adalah anggaran untuk satu kali pencarian dan digunakan sesuai urutan resource:
$remaining = $panel->getGlobalSearchLimit();
foreach ($resources as $resource) {
if ($remaining <= 0) {
break;
}
$results = $this->search($resource, $term, min($resource::globalSearchLimit(), $remaining));
if ($results === []) {
continue;
}
$remaining -= count($results);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Contoh dengan limit: 6 dan tiga resource yang masing-masing memiliki limit 5:
| Urutan | Resource | Diminta | Dikembalikan | Sisa |
|---|---|---|---|---|
| 1 | Users (sort 0) | min(5, 6) = 5 | 5 | 1 |
| 2 | Orders (sort 10) | min(5, 1) = 1 | 1 | 0 |
| 3 | Posts (sort 20) | — | tidak diproses | 0 |
Ada dua konsekuensi penting. Pertama, limit per resource yang terlalu besar pada urutan awal dapat menghabiskan jatah resource setelahnya. Kedua, resource yang tidak menghasilkan apa pun tidak mengurangi anggaran — tanpa hasil berarti tidak ada pengurangan dan tidak ada break.
Menentukan ukuran limit sebaiknya mempertimbangkan ukuran dialog yang kurang lebih menggunakan max-h-96. Lima puluh hasil biasanya sudah lebih banyak daripada yang benar-benar akan dibaca pengguna; tujuan limit adalah membatasi biaya query, bukan memenuhi dialog sampai penuh. Kurangi nilainya pada panel dengan banyak resource agar setiap grup tetap mendapat kesempatan tampil.
Debounce
debounce adalah jeda dalam milidetik antara input terakhir dan request pencarian. Palet juga tidak akan mengirim request untuk term kurang dari dua karakter, sehingga kata pendek hanya menghasilkan satu request, bukan beberapa request berturut-turut.
$panel->globalSearch(debounce: 500); // pencarian berat atau database sedang sibuk
$panel->globalSearch(debounce: 150); // dataset kecil, respons terasa lebih cepat2
Request tidak dimasukkan ke antrean. Input baru membatalkan fetch yang sedang berjalan melalui AbortController, sehingga response awal yang lambat tidak dapat menimpa response terbaru yang lebih cepat. debounce berkaitan dengan jumlah query yang harus dijalankan database, bukan dengan kebenaran hasil pencarian.
Shortcut keyboard
$panel->globalSearch(keyBindings: ['mod+k', 'mod+shift+f']);Satu binding ditulis sebagai string yang dipisahkan oleh +. Segmen terakhir adalah tombol utama dan dibandingkan secara case-insensitive dengan KeyboardEvent.key milik browser; seluruh segmen sebelumnya dianggap sebagai modifier:
| Modifier | Cocok dengan |
|---|---|
mod | metaKey atau ctrlKey — tombol command utama sesuai platform |
shift | shiftKey |
alt | altKey |
Modifier lain seperti ctrl, cmd, meta, atau option tidak dikenali dan membuat binding tidak pernah cocok. Gunakan mod.
// Berfungsi.
$panel->globalSearch(keyBindings: ['mod+k']);
$panel->globalSearch(keyBindings: ['mod+shift+p']);
$panel->globalSearch(keyBindings: ['mod+k', 'mod+/']);
// Tidak pernah dijalankan: 'ctrl' bukan nama modifier yang dikenali.
$panel->globalSearch(keyBindings: ['ctrl+k']);
// Daftar kosong: tombol header tetap dapat membuka palet.
$panel->globalSearch(keyBindings: []);2
3
4
5
6
7
8
9
10
Sebuah binding hanya memeriksa modifier yang disebutkan. Karena itu, mod+k juga cocok dengan ⌘⇧K. Tambahkan shift secara eksplisit jika kedua kombinasi tersebut harus diperlakukan sebagai shortcut yang berbeda.
Panel berbeda, konfigurasi pencarian berbeda
Konfigurasi pencarian bersifat per panel, sama seperti konfigurasi lain pada Panel:
// Admin: banyak resource, gunakan anggaran kecil agar setiap grup dapat tampil.
$panel->globalSearch(limit: 20, debounce: 250);
// Panel aplikasi untuk pelanggan dengan satu resource yang dapat dicari.
$panel->globalSearch(limit: 5, keyBindings: ['mod+k', 'mod+p']);
// Panel operasional yang tidak membutuhkan pencarian global.
$panel->globalSearch(false);2
3
4
5
6
7
8
Data yang diterima frontend
SharePanelData mengirim konfigurasi berikut pada setiap request panel:
[
'enabled' => $enabled,
'url' => $enabled ? route($panel->routeName('search'), absolute: false) : null,
'debounce' => $panel->getGlobalSearchDebounce(),
'keyBindings' => $panel->getGlobalSearchKeyBindings(),
]2
3
4
5
6
import { usePanel } from '@/panel/composables/usePanel';
const { search } = usePanel();
search.value.enabled; // boolean
search.value.url; // '/admin/search' | null
search.value.debounce; // 300
search.value.keyBindings; // ['mod+k']2
3
4
5
6
7
8
URL berupa relative path yang dibangun dari nama route panel.{panelId}.search, bukan string yang dirakit sendiri oleh frontend. Nilai debounce dan keyBindings tetap dikirim meskipun enabled bernilai false; hanya url yang diubah menjadi null karena tidak ada endpoint yang perlu dipanggil.
Validasi perilaku tersebut dalam test:
use Inertia\Testing\AssertableInertia;
$this->get('/admin')
->assertInertia(fn (AssertableInertia $page) => $page
->where('search.enabled', true)
->where('search.url', '/admin/search')
->where('search.debounce', 300)
->where('search.keyBindings', ['mod+k']));2
3
4
5
6
7
8
Hal yang perlu diperhatikan
globalSearch()mengganti keempat nilai sekaligus. Dua pemanggilan tidak bersifat menambahkan konfigurasi; tuliskan seluruh pengaturan yang dibutuhkan dalam satu pemanggilan.enabled: truetidak menjamin palet akan tampil. Jika tidak ada resource yang dapat dicari, palet tidak ditampilkan. Ini adalah perilaku yang memang dirancang, bukan tanda kesalahan konfigurasi.- Binding tanpa modifier dapat aktif ketika pengguna sedang mengetik. Listener dipasang pada
windowdan tidak memeriksa target event, sehinggakeyBindings: ['/']dapat membuka palet ketika pengguna mengetik/di dalam form. Sebaiknya gunakan modifier pada setiap binding. ctrl+ktidak pernah cocok. Hanyamod,shift, danaltyang dikenali; gunakanmod+k.- Route
searchtetap ada meskipun palet dinonaktifkan, dan akan mengembalikan hasil kosong. Tidak ada data yang bocor karenaGlobalSearch::for()memeriksahasGlobalSearch()terlebih dahulu. Jangan menggunakan keberadaan route sebagai indikator bahwa pencarian sedang aktif. - Tidak ada rate limit bawaan. Route hanya mewarisi middleware panel; tambahkan
throttle:60,1ke middleware stack jika endpoint perlu dibatasi. panel:cachemenyimpan class yang dimiliki panel, bukan konfigurasi panel. Perubahan padaglobalSearch()berlaku pada request berikutnya. Namun, menambahkan resource baru yang dapat dicari ke aplikasi yang sudah menggunakan cache tidak akan terdeteksi sampaiphp artisan panel:cachedijalankan lagi atauphp artisan panel:clearmenghapus manifest.