Composable'lar
Kit composable'ları artık pakete dahil edilmiştir ve varsayılan olarak doğrudan vendor kütüphanesinden çalışır — tüketici uygulamasına kopyalanmaları gerekmez. Uygulama genelindeki importlar daha önce olduğu gibi @/composables/<name> (veya yalnızca @/composables barrel'ı) üzerinden yapılır; Vite customResolver ve buna eşlik eden tsconfig path girişi bu yolları önce local, sonra vendor olarak çözer: tüketicinin resources/js/composables/ dizininde bir dosya varsa o kullanılır, yoksa vendor kopyası otomatik devreye girer.
useAdminMenu, stub olarak gönderilmeye devam eden tek composable'dır (resources/js/composables/useAdminMenu.ts). Tüketicinin ürettiği @/routes/* dosyalarına bağımlıdır ve projeye özgü menü tanımını barındırır; bu nedenle düzenlenebilir olarak kalmalıdır. @/composables/index.ts barrel'ı da stub olarak kalmaya devam eder.
Composer üzerinden composable güncellemeleri
Kit composable'ları pakette yer aldığından, composer update lvntr/laravel-starter-kit çalıştırıldığında otomatik olarak güncellenir. Elle dosya kopyalamaya gerek yoktur.
Özelleştirmek için composable yayımlama
Bir composable'ı düzenlemek için önce tüketici uygulamasına yayımlayın:
php artisan sk:publish --tag=composables
Bu komut, vendor'daki güncel sürümleri resources/js/composables/ dizinine kopyalar. Local kopya oluştuğunda local-first resolver onu otomatik olarak seçer — alias değişikliği veya build config düzenlemesi gerekmez.
Mevcut kurulumlar için geçiş notu
Bu değişiklikten önce oluşturulan projelerde tüm composable'lar resources/js/composables/ altında zaten mevcuttur. Local-first resolver bu local kopyaları kullanmaya devam eder; hiçbir şey bozulmaz. Ancak bu projelerde composer update ile gelen upstream düzeltmeleri otomatik olarak alınmaz. Vendor tarafından yönetilen güncellemelere geçmek için özelleştirmediğiniz composable dosyalarını resources/js/composables/ dizininden silin — useAdminMenu.ts, index.ts ve kasıtlı olarak düzenlediğiniz dosyaları koruyun. Kit, local dosyaları hiçbir zaman otomatik silmez.
Sık Kullanılan Composable'lar
- API response zarfını kullanan JSON istekleri için
useApi - Inertia shared props içinden role ve permission kontrolü için
useCan - cache'li definition yükleme için
useDefinition - dialog durumu ve uzaktan veri yükleme akışları için
useDialog - FileManager ve file-upload alanlarından tam ekran görsel önizleme için
useImageLightbox - onay işlemleri için
useConfirm - flash mesaj yönetimi için
useFlash - dark mode kalıcılığı için
useDarkMode - aktif runtime temasını (
main/aura) uygulamak içinuseTheme - Inertia yüklenme durumu için
usePageLoading - tablo veya widget yenilemek için
useRefreshBus - responsive sidebar durumu için
useSidebar - URL ile senkron sekme durumu için
useUrlTab - admin navigasyonu üretmek için
useAdminMenuveuseMenuBuilder
Temel İstek ve Dialog Yardımcıları
useApi()
Projenin to_api() / ApiResponse JSON yapısına göre hazırlanmış küçük bir fetch() sarmalayıcısıdır.
Accept: application/jsonveX-Requested-With: XMLHttpRequestheader'larını ekler- Uygunsa
X-XSRF-TOKENheader'ını ekler datapayload'ını doğrudan çözer- Başarısız cevaplarda
ApiErrorfırlatır toast: falseverilmezse PrimeVue toast hata mesajı gösterebilir
const api = useApi();
const user = await api.get<User>('/api/v1/users/1');
await api.post('/api/v1/users', { name: 'John Doe' });
await api.put('/api/v1/users/1', { name: 'Jane Doe' });
await api.patch('/api/v1/users/1', { status: 'active' });
await api.delete('/api/v1/users/1');
useConfirm()
PrimeVue ConfirmationService üzerine kurulmuş iki yardımcı döndürür:
confirmDelete(onAccept, message?, icon?)confirmAction({ message, onAccept, header?, icon?, acceptLabel?, rejectLabel?, acceptClass? })
const { confirmDelete, confirmAction } = useConfirm();
confirmDelete(() => {
console.log('Silme onaylandı');
});
confirmAction({
message: 'Bu kaydı şimdi yayınlamak istiyor musun?',
acceptLabel: 'Yayınla',
onAccept: () => console.log('Onaylandı'),
});
useDialog()
@lvntr/components/ui/AppDialog.vue ile birlikte çalışan global dialog yöneticisidir.
open(component, props?, header?, options?)openAsync(component, url, header?, options?, baseProps?)close()setLoading(val)
Options içinde refreshKey verilirse onSuccess ve onCancel callback'leri otomatik eklenir.
useImageLightbox()
AdminLayout.vue içindeki global ImageLightbox overlay'i üzerinden çalışan, ortak tam ekran görsel önizleme state'idir.
open(url, name?)close()state.visible,state.url,state.name
Resimler için bunu kullanın. Resim olmayan dosyalarda FilePreviewModal ile useDialog() akışı kullanılmaya devam eder.
Yetki ve Gezinme Yardımcıları
useCan()
Inertia shared props içindeki permission ve role verilerini okur.
can(permission)canAny(permissions)hasRole(role)
useAdminMenu()
Projeye özel admin sidebar menü öğelerini tanımlar ve görünürlük ile aktiflik davranışını useMenuBuilder() composable'ına devreder.
useMenuBuilder()
Sidebar benzeri gezinme yapıları için ortak menü yardımcısıdır.
- Üst seviye ve alt menü öğelerini permission ve role'a göre filtreler
- Filtreleme sonrası boşta kalan section başlıklarını kaldırır
- Düz URL'lerde ve query parametreli URL'lerde aktif linki doğru belirler
- Çocuk öğelerden biri aktifse parent group'u açık tutar
const allItems: MenuItem[] = [{ title: 'sk-menu.dashboard', href: '/dashboard' }];
return useMenuBuilder(allItems);
useUrlTab()
Sekme seçimini ?tab=security gibi bir query string anahtarı ile senkron tutar. tabs argümanı düz bir dizi (bir reactive dizi dahil — değişiklikler canlı izlenir, böylece mount sonrası değişen bir sekme listesi, örn. permission'a göre filtrelenen, senkron kalır), bir ref veya bir getter (MaybeRefOrGetter<TabDefinition[]>) kabul eder; her erişimde toValue() ile okunur. Aktif sekmeyi zaten seçili olduğu değere ayarlamak no-op'tur (navigasyon tetiklenmez); listenin ilk öğesine ayarlamak query parametresini yazmak yerine kaldırır; mevcut URL'deki #hash geçiş boyunca korunur. Opsiyonel üçüncü bir argüman olan { history: 'push' | 'replace' }, geçişte yazılan history girdisini kontrol eder — 'replace' (varsayılan) mevcut girdinin üzerine yazar, 'push' her geçişe kendi girdisini verir. SkTabs artık doğrudan bu composable'ın üzerine kurulu değildir — kendi internal, işlevsel olarak eşdeğer bir aktif-sekme state'ine sahiptir; böylece davranışı bir uygulamanın yayınladığı useUrlTab kopyasına bağlı kalmaz.
useRefreshBus()
Özellikle DataTable gibi bileşenler için kullanılan basit bir global yenileme bus'ıdır. Kayıtlı callback'ler bileşen unmount olduğunda otomatik temizlenir.
on(key, callback)— refresh callback'i kaydetrefresh(...keys)— bir veya birden fazla refresh anahtarını tetiklerefreshAll()— tüm kayıtlı callback'leri tetikle
const bus = useRefreshBus();
bus.on('users-table', () => fetchData());
bus.refresh('users-table');
UI State Yardımcıları
useSidebar()
Admin sidebar için masaüstü daraltma ve mobil açık/kapalı durumlarını yönetir.
useDarkMode()
Dark mode tercihini local storage'da saklar ve <html> üzerinde .dark sınıfını değiştirir.
useTheme()
Inertia shared props'taki admin geneli appearance.theme değerine göre, data-sk-theme attribute'u üzerinden aktif runtime temasını (main, aura) <html> öğesine uygular.
theme— çözümlenmiş runtime tema adı (appearanceprop'unun eksik olduğu partial reload sırasındaundefined)runtimeThemes— anında geçiş yapılabilen temaların kümesi; sunucuruntime_themesgöndermezse['main', 'aura']varsayılanına dönerapplyTheme(value)—<html>üzerindedata-sk-themeattribute'unu ayarlar veya kaldırır
usePageLoading()
inertia:start ve inertia:finish tarayıcı event'leri ile sayfa geçiş durumunu izler.
useFlash()
Inertia shared props içindeki flash verisini reactive olarak sunar.
Bu projede flash mesajlar composable içinde değil, AdminLayout.vue içinde toast olarak gösterilir.
Definition Yardımcıları
useDefinition()
Definition kayıtlarını giriş gerektiren /definitions endpoint'inden yükler ve ortak bir reactive cache içinde tutar.
load(keys)— yalnızca istenen key'leri yükler; ortak cache sayesinde tekrar istek atmazloadAll()— mevcut tüm definition key'lerini yüklerlist(key, filter?)— ham definition öğelerini döner, isteğe bağlı filtreyleoptions(key, filter?)— select'ler için{ label, value }formatında öğeler dönerfind(key, value)— değere göre tek bir öğe bulurclearCache()— reactive cache'i sıfırlarloaded— herhangi bir yükleme tamamlandığındatrueolan reactive boolean
Bu projede tipik key'ler userStatus, gender, identityType ve yesNo değerleridir.
const { load, options, find } = useDefinition();
await load(['userStatus', 'gender']);
const statusOptions = options('userStatus');
const activeStatus = find('userStatus', 'active');
list() ve options(), definition listesinin alt kümesini almak istediğinde only veya except dizileri içeren isteğe bağlı bir filter nesnesi alır:
// Yalnızca active ve pending durumlarını göster
const filteredOptions = options('userStatus', { only: ['active', 'pending'] });
// Belirli bir durumu dışla
const filteredOptions = options('userStatus', { except: ['archived'] });
DataTable Seçimi
useDatatableSelection()
SkDatatable için satır seçimi ve toplu işlem (bulk action) gönderimini yönetir. Consumer sayfalar bunu doğrudan import eder — herhangi bir index ekranına onay kutusu ve toplu işlem eklemek için önerilen yol budur.
Dışa aktarılan tipler:
type BulkSelectionMode = 'page' | 'all';
interface BulkActionPayload {
action: string;
ids: (string | number)[];
select_all_filtered: boolean;
filter_snapshot: Record<string, unknown>;
[key: string]: unknown;
}
interface BulkActionResult {
processed: number;
skipped: number;
failed: Array<{ id: number | string; reason: string }>;
message: string;
}
İmza:
const selection = useDatatableSelection({
bulkUrl: string; // Bulk endpoint'in mutlak URL'si (Wayfinder kullanın: users.bulk.url())
idKey?: string; // Satır ID property'si — varsayılan: 'id'
onSuccess?: () => void; // Başarılı bulk işlem sonrası çağrılır (tabloyu yenilemek için)
});
Döndürdükleri:
| Özellik / Metod | Tip | Açıklama |
|---|---|---|
selectedIds |
Ref<Set<string|number>> |
Seçili satır ID'lerinin kümesi |
selectionMode |
Ref<BulkSelectionMode> |
'page' (geçerli sayfa) veya 'all' (çapraz sayfa filtrelenmiş) |
submitting |
Ref<boolean> |
Bulk istek devam ederken true |
selectedCount |
ComputedRef<number> |
Seçili ID sayısı |
hasSelection |
ComputedRef<boolean> |
En az bir satır seçiliyse true |
isAllFilteredMode |
ComputedRef<boolean> |
Çapraz sayfa seçimi aktifse true |
toggleRow(row) |
function | Bir satırı seç veya seçimi kaldır |
isRowSelected(row) |
function | Satır seçiliyse true döner |
togglePageSelection(rows, selected) |
function | Geçerli sayfadaki tüm satırları seç veya kaldır |
isPageFullySelected(rows) |
function | Sayfadaki tüm satırlar seçiliyse true |
isPagePartiallySelected(rows) |
function | Sayfadaki bazı (ama hepsi değil) satırlar seçiliyse true |
selectAllFiltered() |
function | Çapraz sayfa modunu etkinleştir — backend filtreye göre yeniden hesaplar |
clearSelection() |
function | Tüm seçimleri temizle ve page moduna geri dön |
executeBulkAction(action, filterSnapshot?, overrideUrl?) |
function | Bulk payload'ı Inertia router ile gönder |
Kullanım:
import { useDatatableSelection } from '@/composables/useDatatableSelection';
import { useRefreshBus } from '@/composables/useRefreshBus';
import users from '@/routes/users';
const bus = useRefreshBus();
const selection = useDatatableSelection({
bulkUrl: users.bulk.url(),
idKey: 'id',
onSuccess: () => bus.refresh('users-table'),
});
// SkDatatable'a bağlayın:
// <SkDatatable :selection="selection" ...>
// Aktif filtrelerle bulk delete tetikleyin:
selection.executeBulkAction('delete', activeFilterSnapshot.value);
selection nesnesini <SkDatatable :selection="selection"> ile bağlayın — bu onay kutusu sütununu oluşturur. Toplu işlem butonları #bulk-actions slot'una eklenir; satır seçiliyken SkDatatable, viewport'un altında kayan koyu bir işlem çubuğu gösterir. Backend'deki BulkAction arayüzü dahil tam toplu işlem deseni için docs/datatable.md belgesine bakın.
Dahili Composable'lar
Aşağıdaki composable'lar vendor UI tarafından kullanılır ve consumer sayfalardan doğrudan çağrılmaları beklenmez. Referans amacıyla listelenmiştir.
useFileShare() — Dahili
FileManager medyası için imzalı paylaşım linkleri oluşturmak ve iptal etmek amacıyla vendor Files modülü (ShareLinkModal, MyShareLinksDrawer) tarafından kullanılır. Consumer sayfalar bunu doğrudan çağırmaz — paylaşım linki işlemleri dahili Files arayüzü üzerinden sunulur. Paylaşım linki API'si için docs/file-manager.md belgesine bakın.
createShare(mediaId: number, ttlHours: number): Promise<ShareLinkResult | null>— imzalı paylaşım linki oluşturur (TTL: 1–720 saat);{ url, expires_at, token_hash }veya hata durumundanulldönerrevokeShare(mediaId: number, token: string): Promise<boolean>— token hash ile mevcut bir linki iptal eder
useAccentColor() — Dahili
Kullanıcı başına accent renk tercihini ve sidebar yüzeyini yönetmek için AdminLayout ve Appearance sekmesi tarafından kullanılır. Consumer'lar accent rengiyle header popover üzerinden etkileşime girer; bu composable'ı doğrudan çağırmaz. Accent renk sistemi için docs/theme.md belgesine bakın.
useAppearanceDefaults() — Dahili
Her sayfa yüklenişinde Inertia shared props'tan global görünüm varsayılanlarını (accent renk, dark mode, sidebar stili, logo ve favicon URL'leri) okur. useAccentColor, useDarkMode ve layout'lar tarafından, kullanıcıya özgü herhangi bir override uygulanmadan önce başlangıç değerini belirlemek için kullanılır.
getXsrfToken() — Dahili
useCsrf.ts dosyasından dışa aktarılır. Laravel XSRF-TOKEN cookie'sini okumak için tek doğruluk kaynağıdır — useApi(), FileManager upload XHR'ı ve zengin metin editörünün görsel yükleme akışı tutarlı cookie parsing için bunun üzerinden geçer. Cookie yoksa (SSR veya henüz set edilmemişse) '' döner.
getXsrfToken(): string
withBasePath() — Dahili
useBasePath.ts dosyasından dışa aktarılır. Kendi URL'sini oluşturan ham fetch/XMLHttpRequest çağrıları için uygulamanın deploy alt-path'ini eklemek amacıyla vendor UI (zengin metin EditorInput, FileManager) tarafından kullanılır; Inertia navigasyonu base'i zaten otomatik olarak hesaba katar.
withBasePath(path: string): string
Öneri
Bir arayüz davranışı birden fazla sayfada görünmeye başladığında, aynı kodu tekrar etmek yerine bunu bir composable içine taşıyın.