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]);
    });
  },
});

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).

İç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:

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.