Temel kavramlar
controls ve Bileşen Ağacı
Alt kontrol koleksiyonu `controls`, bileşen ağacında gezinme ve içeriği başka bir yere taşıma.
controls — alt kontrol koleksiyonu
Her bileşenin bir controls koleksiyonu vardır. Bu, bileşenin çocuklarını yöneten canlı bir listedir; listedeki her değişiklik DOM'a o anda yansır.
| Çağrı | Ne yapar |
|---|---|
controls.add(a, b) |
Sona ekler; eklenenleri dizi olarak döndürür. |
controls.add(2, a), controls.insert(2, a, b) |
Verilen dizine ekler. |
controls.remove(a) |
Çocuğu çıkarır ve serbest bırakır. |
await controls.detach(a) |
Çıkış geçişini oynatır, çocuğu ağaçtan ve DOM'dan söker; serbest bırakmaz. |
controls.clear() |
Bütün çocukları serbest bırakır. |
await controls.clearAsync() |
Aynısı; bütün serbest bırakmalar bitince çözülür. |
controls.move(a, b) |
a'yı b'nin önüne taşır; b verilmezse sona. |
controls.moveToIndex(a, 0) |
a'yı verilen dizine taşır. Dizin sonuçtaki konumdur; length - 1 ve üstü sona taşır. |
controls.items, controls.length |
Çocuk dizisi ve çocuk sayısı. |
controls.forEach(fn), controls.map(fn) |
Çocuklar üzerinde dolaşır; map sonuçları dizi olarak döndürür. |
Etkinlik günlüğü her kaydedilen not için bir satır ekler. En yeni satır başa gelir ve günlük son beş satırı tutar; fazlası serbest bırakılır:
import { Component } from "@motifx/core";
const LIMIT = 5;
export class ActivityLog extends Component<HTMLUListElement> {
override onConfigured() {
this.class.add("log");
}
override onBuilt() {
this.context.on("noteSaved", (title?: string) => {
this.controls.add(0, <li>{title?.trim() || "Adsız"} kaydedildi</li>);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
}
}
import { Component, ComponentBase, motifComponent } from "@motifx/core";
const LIMIT = 5;
export class ActivityLog extends Component<HTMLUListElement> {
static override elementTag = "ul";
override onConfigured() {
this.class.add("log");
}
override onBuilt() {
this.context.on("noteSaved", (title?: string) => {
const row = motifComponent("li", (li: ComponentBase) => li.setText(`${title?.trim() || "Adsız"} kaydedildi`));
this.controls.add(0, row);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
}
}
import type { ComponentBase } from "@motifx/core";
const LIMIT = 5;
export function ActivityLog() {
return (
<ul
class="log"
onBuilt={(log: ComponentBase) => {
log.context.on("noteSaved", (title?: string) => {
log.controls.add(0, <li>{title?.trim() || "Adsız"} kaydedildi</li>);
while (log.controls.length > LIMIT) log.controls.remove(log.controls.items[LIMIT]);
});
}}
/>
);
}
import { ComponentBase, motifComponent } from "@motifx/core";
const LIMIT = 5;
export function ActivityLog() {
return motifComponent("ul", {
class: "log",
onBuilt: (log: ComponentBase) => {
log.context.on("noteSaved", (title?: string) => {
const row = motifComponent("li", (li: ComponentBase) => li.setText(`${title?.trim() || "Adsız"} kaydedildi`));
log.controls.add(0, row);
while (log.controls.length > LIMIT) log.controls.remove(log.controls.items[LIMIT]);
});
},
});
}
import { motifOptions } from "@motifx/core";
const LIMIT = 5;
export const ActivityLog = () => motifOptions({
el: "ul",
onConfigured() {
this.class.add("log");
},
onBuilt() {
this.context.on("noteSaved", (title?: string) => {
this.controls.add(0, <li>{title?.trim() || "Adsız"} kaydedildi</li>);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
},
});
import { ComponentBase, motifComponent, motifOptions } from "@motifx/core";
const LIMIT = 5;
export const ActivityLog = () => motifOptions({
el: "ul",
onConfigured() {
this.class.add("log");
},
onBuilt() {
this.context.on("noteSaved", (title?: string) => {
const row = motifComponent("li", (li: ComponentBase) => li.setText(`${title?.trim() || "Adsız"} kaydedildi`));
this.controls.add(0, row);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
},
});
import { Component } from "@motifx/core";
const LIMIT = 5;
export class ActivityLog extends Component {
static elementTag = "ul";
onConfigured() {
this.class.add("log");
}
onBuilt() {
this.context.on("noteSaved", (title) => {
this.controls.add(0, <li>{title?.trim() || "Adsız"} kaydedildi</li>);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
}
}
import { Component, motifComponent } from "@motifx/core";
const LIMIT = 5;
export class ActivityLog extends Component {
static elementTag = "ul";
onConfigured() {
this.class.add("log");
}
onBuilt() {
this.context.on("noteSaved", (title) => {
const row = motifComponent("li", (li) => li.setText(`${title?.trim() || "Adsız"} kaydedildi`));
this.controls.add(0, row);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
}
}
const LIMIT = 5;
export function ActivityLog() {
return (
<ul
class="log"
onBuilt={(log) => {
log.context.on("noteSaved", (title) => {
log.controls.add(0, <li>{title?.trim() || "Adsız"} kaydedildi</li>);
while (log.controls.length > LIMIT) log.controls.remove(log.controls.items[LIMIT]);
});
}}
/>
);
}
import { motifComponent } from "@motifx/core";
const LIMIT = 5;
export function ActivityLog() {
return motifComponent("ul", {
class: "log",
onBuilt: (log) => {
log.context.on("noteSaved", (title) => {
const row = motifComponent("li", (li) => li.setText(`${title?.trim() || "Adsız"} kaydedildi`));
log.controls.add(0, row);
while (log.controls.length > LIMIT) log.controls.remove(log.controls.items[LIMIT]);
});
},
});
}
import { motifOptions } from "@motifx/core";
const LIMIT = 5;
export const ActivityLog = () => motifOptions({
el: "ul",
onConfigured() {
this.class.add("log");
},
onBuilt() {
this.context.on("noteSaved", (title) => {
this.controls.add(0, <li>{title?.trim() || "Adsız"} kaydedildi</li>);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
},
});
import { motifComponent, motifOptions } from "@motifx/core";
const LIMIT = 5;
export const ActivityLog = () => motifOptions({
el: "ul",
onConfigured() {
this.class.add("log");
},
onBuilt() {
this.context.on("noteSaved", (title) => {
const row = motifComponent("li", (li) => li.setText(`${title?.trim() || "Adsız"} kaydedildi`));
this.controls.add(0, row);
while (this.controls.length > LIMIT) this.controls.remove(this.controls.items[LIMIT]);
});
},
});
add/insertçalışma zamanında düz değer de kabul eder: string, sayı, boolean ve bigint birer metin düğümüne sarılır; TypeScript imzasıComponentBasebekler, tipli kodda metin düğümünümotifComponent("text", …)ile kurun. Zaten bir ebeveyni olan çocuk önce oradan ayrılır (detach), sonra eklenir. Serbest bırakılmış çocuk eklenmez.- Kurulmuş bir bileşene eklenen çocuk
addçağrısının içinde kurulur ve yerine konur; bir render ya da flush beklemek gerekmez. Henüz kurulmamış bir bileşene eklenen çocuk, sahibiyle birlikte kurulur. BileşenisWaitile bekletiliyorsa çocuk kaydedilir, bekletme kalkınca kurulur. Kapx-chunktaşıyorsa birlikte eklenen çocuklardan ilk 100'ü (sayı verilmişse ilk n'i) hemen kurulur, kalanı iz bırakır ve sonraki karelerde, kare süresinin izin verdiği kadar kurulur (bkz. Büyük listeler). detachçocuğu ağaçtan ve DOM'dan çıkarır ama serbest bırakmaz; başka bir yereaddile yeniden eklenebilir.removeveclearise serbest bırakır.moveyalnız aynı ebeveynin çocukları arasında çalışır; DOM'da da aynı sıraya taşır (fragment köklü çocuklar aralığıyla birlikte). Yer değişmiyorsa hiçbir şey yapmaz.onAdd/onRemove/onAddBeforeBuildgeri çağrıları çerçevenindir; elle atanmaz. Bir çocuğun eklendiğini dinlemek içinmotif.on('controladded', fn)kullanın.motif.on('controlremoved', fn)yalnız çocuk bu bileşendendetachile ayrıldığında gelir (başka bir bileşenincontrols.add'i çocuğu buradan aldığında da); olay, çıkış geçişi ve DOM'dan söküm bittikten sonra gelir.remove,clear,clearAsyncve çocuğun kendidispose()'u bu olayı tetiklemez. İki olayda dae.controleklenen ya da ayrılan bileşendir.
Bileşen ağacında gezinme
Bileşenler canlı bir nesne ağacı oluşturur; her bileşenden ağacın her yönüne ulaşılır. Bulunan bileşenin element'i, props'u, açık alanları ve metotları sıradan bir nesneninki gibi kullanılır.
| Üye | Ne verir |
|---|---|
parent |
Üst bileşen; hiçbir yere eklenmemiş bileşende null. |
controls.items |
Çocuklar. |
siblings.next(), siblings.prev() |
Sonraki ve önceki kardeş; sonda ve başta undefined. |
siblings.all() |
Ebeveynin çocukları, bileşenin kendisi dahil. |
siblings.nextAll(), siblings.prevAll() |
Sonraki ve önceki kardeşler. |
$(seçici).fromComponent() |
Seçiciye uyan alt bileşenler (ComponentBase[]). |
$(seçici).fromDom() |
Elemanın içinde seçiciye uyan DOM düğümleri (NodeList). |
JSX'teki düz etiket de bir bileşendir. İki parametreli bir olay işleyicisinin ilk parametresi (sender) o etiketin bileşenidir; ağaçta oradan gezilir: sender.parent, sender.siblings.all(). Tek parametreli işleyici yalnız olayı alır (bkz. Olaylar).
siblings, parent'ıncontrols.itemsdizisini okur; parent yoksa her metotundefineddöndürür.app.run'a verilen kök bileşeninparent'ınulldeğildir:run()'ın host elemanını sararak kurduğu uygulama kabuğudur (app.getAppShell()).parent'ınullolan en üstteki bileşen bu kabuktur; pencereye alınan içerik için pencereninhost'u, sayfa katmanındaki içerik içinapp.layer'dır (bkz. Sayfa katmanı).fromComponent()bileşen ağacını bu bileşenden başlayarak derinlik öncelikli yürür (eşleşirse kendisi de sonuçtadır) ve her bileşenin elemanını seçiciyle karşılaştırır. Kökü fragment olan bileşende arama, gerçek elemanı olan en yakın üst bileşenden başlar; bu yüzden sonuç o üst bileşenin bütün içeriğini kapsar: kardeş bileşenlerin eşleşen öğeleri de gelir,fromComponent()'te üst bileşen eşleşirse kendisi de. Yalnız kendi içeriğinde aramak için aramayırefile tuttuğunuz iç etiketten ya da elemanını bildiren bir sınıftan yapın.fromDom()aynı elemanın içindequerySelectorAllçalıştırır. Serbest bırakılmış bileşendenull/[]döner.refile alınan bileşen de aynı şekilde kullanılır (bkz. Bileşen içinde eleman erişimi).
İçeriği başka bir yere taşımak
Bir bileşenin içeriğini kendi ağacının dışında göstermek için iki yerleşik bileşen çifti vardır; ikisi de kendi sayfasında anlatılır:
- Transport ve TransportTo — bir gönderenin içeriğini adlandırılmış bir yuvada gösterir. Aynı yuvaya birden çok gönderen gelirse
mode: 'replace'(varsayılan) yalnız en son kurulanın içeriğini bırakır,'merge'hepsinin içeriğini kurulma sırasıyla ekler. Programatik taşıma içinTransporteryardımcısı da oradadır. - ContentBody ve ContentBlock — birden çok bloğun parçalarını tek bir gövdede biriktirir.
Sayfa katmanı
Bildirim, iletişim kutusu ve açılır menü gibi içerik onu açan bileşene aittir ama sayfadaki her şeyin üstünde görünmelidir. app.layer bunun için ayrılmış bileşendir: ilk okunduğunda <body>'nin sonuna, uygulamanın host elemanından sonra düz bir <div> koyar ve o elemanı saran bileşeni döndürür; sonraki okumalar aynı bileşeni verir. Host <body>'nin kendisiyse ya da katmanı kapsıyorsa katman host'un sonunda kalır: kabuğun kökü ve sonradan eklenen her bileşen katmanın önüne girer. run()'dan önce de okunabilir; <body> henüz yoksa eleman belge yüklenince yerleşir, o ana kadar eklenen içerik o zaman yerleşmiş sayılır ve onMounted o zaman çalışır. Eleman sonradan sayfadan çıkarılmışsa, örneğin açılış kodu gövdeyi boşalttıysa, katman içerik ilk yer istediğinde ya da run()'da içindekilerle birlikte gövdenin sonuna yeniden konur; o ana kadar eklenenler o zaman yerleşir. Çekirdek bu elemana stil ve öznitelik yazmaz; konumlandırma, z-index ve aria-live gibi işaretler uygulamanındır ve sıradan bir bileşene verildiği gibi verilir: app.layer.class.add("overlays"), app.layer.attr.add({ "aria-live": "polite" }). app.dispose() katmanı içindekilerle birlikte serbest bırakır ve elemanı kaldırır. Uygulama run() ile yeniden başlatılınca katman ilk okumada yeniden kurulur; dispose'dan sonra okunmuş bir katman varsa run() onu host'tan sonraya taşır ve sonraki app.dispose() kaldırır; uygulama bir daha başlatılmayacaksa app.dispose() yeniden çağrılır ve katman o anda kaldırılır.
Katmandaki içerik ağacın öteki üyeleri gibi davranır: controls.add ile girer; onMounted, onActivated ve onDeactivated alır; uygulamanın servislerini ve router'ını context üzerinden görür; remove serbest bırakır, detach saklar. Eklenen bileşenin parent'ı katmandır; katmanın kendisi kabuk gibi parent'ı null olan bir köktür.
app.layerOf(hedef?, ...bileşenler) içeriği hedefin bulunduğu belgenin katmanına koyar. Hedef bir bileşen ya da DOM düğümüyse belgesine bakılır: belge app.windows ile açılmış ya da bağlanmış bir pencereninse o pencerenin host'u, değilse app.layer döner; hedef verilmemiş ya da null ise app.layer döner. Ardından gelen bileşenler bulunan katmana controls.add ile eklenir ve katman döndürülür. İlk argüman ebeveyni olmayan ve elemanı henüz hiçbir belgede bulunmayan bir bileşense hedef değil içerik sayılır ve sayfa katmanına eklenir; elemanı bir belgede duran ebeveynsiz bileşen, örneğin new Component(document.body) ile sarılmış bir kök, hedeftir ve yerinde kalır. Böylece this.context.layerOf(this, new SaveToast()) içeriği bileşenin bulunduğu pencereye, this.context.layerOf(new SaveToast()) sayfaya koyar. Bileşen ve düğüm olmayan bir hedef MJX424 fırlatır. Doğrudan konan içeriğin ömrü katmanındır; açan bileşenle birlikte kalkması gerekiyorsa katmana bir Transport yuvası koyup içeriği TransportTo ile yazın (bkz. Transport ve TransportTo).
Bileşeni başka bir pencereye taşımak
Bir bileşenin elemanı aynı origin'li başka bir belgeye de taşınabilir: window.open ile açılan bir pencereye, hep üstte duran Picture-in-Picture penceresine ya da bir <iframe>'e. Bileşen yeniden kurulmaz; durumu, bağları ve servisleri sayfada kalır, yalnız elemanlar gider. Bunu app.windows yönetir: this.context.windows.open(editor, { width: 720, height: 560 }) bileşeni pencerede açar, pencere kapanınca eski yerine koyar, stil sayfalarını ve temayı pencereye taşır. Seçenekler, kapanış kuralları ve Electron için Pencereler sayfasına bakın. Pencereyi kendiniz açıp yalnız belgeyi uygulamaya tanıtmak isterseniz app.connectDocument(pencere.document) alt katman olarak durur; o zaman gövdeyi new Component(pencere.document.body) ile sarıp içeriği controls.add ile vermek ve kapanışta geri almak sizin işinizdir.
Sonraki adım
Bileşenin elemanına ve iç etiketlerine erişmek için Eleman ve Görünüş bölümüne geçin.