Temel kavramlar

Bileşen Oluşturma

Bir bileşeni baştan sona oluşturmak: tanım, eleman, içerik, props ve kullanım; sınıf, fonksiyon ve Options biçimleriyle.

Bu sayfa not listesindeki satırı, NoteRow bileşenini baştan sona oluşturur. Kodlar sayfadaki kod tercihlerine göre gösterilir: yazım biçimi, bileşen stili ve dil. Note, Folder ve NotesService Bileşenler sayfasındaki src/services/notes.ts dosyasındandır.

Bileşeni yazmak

Bir bileşenin dört parçası vardır:

Satır, açık notu router'ın parametresinden okur ve o satırı active sınıfıyla işaretler:

import { Component, useNavigation } from "@motifx/core";
import type { Folder, Note } from "../services/notes";

export class NoteRow extends Component<HTMLAnchorElement, { note: Note; folder: Folder }> {
  nav = useNavigation();

  get open() {
    return this.nav.params.id === String(this.props.note.id);
  }

  override onConfigured() {
    const { note, folder } = this.props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  }

  override view() {
    const { note } = this.props;
    return (
      <>
        <b>
          <i class="pin" aria-label="Sabitlendi" x-display={() => note.pinned && !note.archived}></i>
          <span>{() => note.title.trim() || "Adsız"}</span>
        </b>
        <time>{() => new Date(note.updatedAt).toLocaleDateString("tr-TR")}</time>
        <p>{() => note.body.split("\n")[0] || "Metin yok"}</p>
      </>
    );
  }
}
import { Component, ComponentBase, motifComponent, motifFragment, useNavigation } from "@motifx/core";
import type { Folder, Note } from "../services/notes";

export class NoteRow extends Component<HTMLAnchorElement, { note: Note; folder: Folder }> {
  static override elementTag = "a";
  nav = useNavigation();

  get open() {
    return this.nav.params.id === String(this.props.note.id);
  }

  override onConfigured() {
    const { note, folder } = this.props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  }

  override view() {
    const { note } = this.props;
    const title = motifComponent("b", (b: ComponentBase) => {
      const pin = motifComponent("i", {
        preconfig: (i: ComponentBase) => i.bindings.display(() => note.pinned && !note.archived),
        initializeComponent: (i: ComponentBase) => {
          i.class.add("pin");
          i.attr.add({ "aria-label": "Sabitlendi" });
        },
      });
      const label = motifComponent("span", (span: ComponentBase) => span.bindings.text(() => note.title.trim() || "Adsız"));
      b.controls.add(pin, label);
    });
    const when = motifComponent("time", (time: ComponentBase) => time.bindings.text(() => new Date(note.updatedAt).toLocaleDateString("tr-TR")));
    const preview = motifComponent("p", (p: ComponentBase) => p.bindings.text(() => note.body.split("\n")[0] || "Metin yok"));
    return motifFragment({ childs: [title, when, preview] });
  }
}
import { useNavigation } from "@motifx/core";
import type { Folder, Note } from "../services/notes";

export function NoteRow(props: { note: Note; folder: Folder }) {
  const nav = useNavigation();
  const { note, folder } = props;
  const open = () => nav.params.id === String(note.id);
  return (
    <a
      rel="router"
      href={`/${folder}/${note.id}`}
      class={() => `row${open() ? " active" : ""}${note.title.trim() ? "" : " untitled"}`}
      aria-current={() => (open() ? "page" : null)}
    >
      <b>
        <i class="pin" aria-label="Sabitlendi" x-display={() => note.pinned && !note.archived}></i>
        <span>{() => note.title.trim() || "Adsız"}</span>
      </b>
      <time>{() => new Date(note.updatedAt).toLocaleDateString("tr-TR")}</time>
      <p>{() => note.body.split("\n")[0] || "Metin yok"}</p>
    </a>
  );
}
import { ComponentBase, motifComponent, useNavigation } from "@motifx/core";
import type { Folder, Note } from "../services/notes";

export function NoteRow(props: { note: Note; folder: Folder }) {
  const nav = useNavigation();
  const { note, folder } = props;
  const open = () => nav.params.id === String(note.id);

  return motifComponent("a", (row: ComponentBase) => {
    row.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (open() ? "page" : null) });
    row.class.add(() => `row${open() ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);

    const title = motifComponent("b", (b: ComponentBase) => {
      const pin = motifComponent("i", {
        preconfig: (i: ComponentBase) => i.bindings.display(() => note.pinned && !note.archived),
        initializeComponent: (i: ComponentBase) => {
          i.class.add("pin");
          i.attr.add({ "aria-label": "Sabitlendi" });
        },
      });
      const label = motifComponent("span", (span: ComponentBase) => span.bindings.text(() => note.title.trim() || "Adsız"));
      b.controls.add(pin, label);
    });
    const when = motifComponent("time", (time: ComponentBase) => time.bindings.text(() => new Date(note.updatedAt).toLocaleDateString("tr-TR")));
    const preview = motifComponent("p", (p: ComponentBase) => p.bindings.text(() => note.body.split("\n")[0] || "Metin yok"));

    row.controls.add(title, when, preview);
  });
}
import { motifOptions, useNavigation } from "@motifx/core";
import type { Folder, Note } from "../services/notes";

export const NoteRow = (props: { note: Note; folder: Folder }) => motifOptions({
  el: "a",
  nav: useNavigation(),

  get open() {
    return this.nav.params.id === String(props.note.id);
  },

  onConfigured() {
    const { note, folder } = props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  },

  view() {
    const { note } = props;
    return (
      <>
        <b>
          <i class="pin" aria-label="Sabitlendi" x-display={() => note.pinned && !note.archived}></i>
          <span>{() => note.title.trim() || "Adsız"}</span>
        </b>
        <time>{() => new Date(note.updatedAt).toLocaleDateString("tr-TR")}</time>
        <p>{() => note.body.split("\n")[0] || "Metin yok"}</p>
      </>
    );
  },
});
import { ComponentBase, motifComponent, motifFragment, motifOptions, useNavigation } from "@motifx/core";
import type { Folder, Note } from "../services/notes";

export const NoteRow = (props: { note: Note; folder: Folder }) => motifOptions({
  el: "a",
  nav: useNavigation(),

  get open() {
    return this.nav.params.id === String(props.note.id);
  },

  onConfigured() {
    const { note, folder } = props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  },

  view() {
    const { note } = props;
    const title = motifComponent("b", (b: ComponentBase) => {
      const pin = motifComponent("i", {
        preconfig: (i: ComponentBase) => i.bindings.display(() => note.pinned && !note.archived),
        initializeComponent: (i: ComponentBase) => {
          i.class.add("pin");
          i.attr.add({ "aria-label": "Sabitlendi" });
        },
      });
      const label = motifComponent("span", (span: ComponentBase) => span.bindings.text(() => note.title.trim() || "Adsız"));
      b.controls.add(pin, label);
    });
    const when = motifComponent("time", (time: ComponentBase) => time.bindings.text(() => new Date(note.updatedAt).toLocaleDateString("tr-TR")));
    const preview = motifComponent("p", (p: ComponentBase) => p.bindings.text(() => note.body.split("\n")[0] || "Metin yok"));
    return motifFragment({ childs: [title, when, preview] });
  },
});
import { Component, useNavigation } from "@motifx/core";

export class NoteRow extends Component {
  static elementTag = "a";
  nav = useNavigation();

  get open() {
    return this.nav.params.id === String(this.props.note.id);
  }

  onConfigured() {
    const { note, folder } = this.props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  }

  view() {
    const { note } = this.props;
    return (
      <>
        <b>
          <i class="pin" aria-label="Sabitlendi" x-display={() => note.pinned && !note.archived}></i>
          <span>{() => note.title.trim() || "Adsız"}</span>
        </b>
        <time>{() => new Date(note.updatedAt).toLocaleDateString("tr-TR")}</time>
        <p>{() => note.body.split("\n")[0] || "Metin yok"}</p>
      </>
    );
  }
}
import { Component, motifComponent, motifFragment, useNavigation } from "@motifx/core";

export class NoteRow extends Component {
  static elementTag = "a";
  nav = useNavigation();

  get open() {
    return this.nav.params.id === String(this.props.note.id);
  }

  onConfigured() {
    const { note, folder } = this.props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  }

  view() {
    const { note } = this.props;
    const title = motifComponent("b", (b) => {
      const pin = motifComponent("i", {
        preconfig: (i) => i.bindings.display(() => note.pinned && !note.archived),
        initializeComponent: (i) => {
          i.class.add("pin");
          i.attr.add({ "aria-label": "Sabitlendi" });
        },
      });
      const label = motifComponent("span", (span) => span.bindings.text(() => note.title.trim() || "Adsız"));
      b.controls.add(pin, label);
    });
    const when = motifComponent("time", (time) => time.bindings.text(() => new Date(note.updatedAt).toLocaleDateString("tr-TR")));
    const preview = motifComponent("p", (p) => p.bindings.text(() => note.body.split("\n")[0] || "Metin yok"));
    return motifFragment({ childs: [title, when, preview] });
  }
}
import { useNavigation } from "@motifx/core";

export function NoteRow(props) {
  const nav = useNavigation();
  const { note, folder } = props;
  const open = () => nav.params.id === String(note.id);
  return (
    <a
      rel="router"
      href={`/${folder}/${note.id}`}
      class={() => `row${open() ? " active" : ""}${note.title.trim() ? "" : " untitled"}`}
      aria-current={() => (open() ? "page" : null)}
    >
      <b>
        <i class="pin" aria-label="Sabitlendi" x-display={() => note.pinned && !note.archived}></i>
        <span>{() => note.title.trim() || "Adsız"}</span>
      </b>
      <time>{() => new Date(note.updatedAt).toLocaleDateString("tr-TR")}</time>
      <p>{() => note.body.split("\n")[0] || "Metin yok"}</p>
    </a>
  );
}
import { motifComponent, useNavigation } from "@motifx/core";

export function NoteRow(props) {
  const nav = useNavigation();
  const { note, folder } = props;
  const open = () => nav.params.id === String(note.id);

  return motifComponent("a", (row) => {
    row.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (open() ? "page" : null) });
    row.class.add(() => `row${open() ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);

    const title = motifComponent("b", (b) => {
      const pin = motifComponent("i", {
        preconfig: (i) => i.bindings.display(() => note.pinned && !note.archived),
        initializeComponent: (i) => {
          i.class.add("pin");
          i.attr.add({ "aria-label": "Sabitlendi" });
        },
      });
      const label = motifComponent("span", (span) => span.bindings.text(() => note.title.trim() || "Adsız"));
      b.controls.add(pin, label);
    });
    const when = motifComponent("time", (time) => time.bindings.text(() => new Date(note.updatedAt).toLocaleDateString("tr-TR")));
    const preview = motifComponent("p", (p) => p.bindings.text(() => note.body.split("\n")[0] || "Metin yok"));

    row.controls.add(title, when, preview);
  });
}
import { motifOptions, useNavigation } from "@motifx/core";

export const NoteRow = (props) => motifOptions({
  el: "a",
  nav: useNavigation(),

  get open() {
    return this.nav.params.id === String(props.note.id);
  },

  onConfigured() {
    const { note, folder } = props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  },

  view() {
    const { note } = props;
    return (
      <>
        <b>
          <i class="pin" aria-label="Sabitlendi" x-display={() => note.pinned && !note.archived}></i>
          <span>{() => note.title.trim() || "Adsız"}</span>
        </b>
        <time>{() => new Date(note.updatedAt).toLocaleDateString("tr-TR")}</time>
        <p>{() => note.body.split("\n")[0] || "Metin yok"}</p>
      </>
    );
  },
});
import { motifComponent, motifFragment, motifOptions, useNavigation } from "@motifx/core";

export const NoteRow = (props) => motifOptions({
  el: "a",
  nav: useNavigation(),

  get open() {
    return this.nav.params.id === String(props.note.id);
  },

  onConfigured() {
    const { note, folder } = props;
    this.attr.add({ rel: "router", href: `/${folder}/${note.id}`, "aria-current": () => (this.open ? "page" : null) });
    this.class.add(() => `row${this.open ? " active" : ""}${note.title.trim() ? "" : " untitled"}`);
  },

  view() {
    const { note } = props;
    const title = motifComponent("b", (b) => {
      const pin = motifComponent("i", {
        preconfig: (i) => i.bindings.display(() => note.pinned && !note.archived),
        initializeComponent: (i) => {
          i.class.add("pin");
          i.attr.add({ "aria-label": "Sabitlendi" });
        },
      });
      const label = motifComponent("span", (span) => span.bindings.text(() => note.title.trim() || "Adsız"));
      b.controls.add(pin, label);
    });
    const when = motifComponent("time", (time) => time.bindings.text(() => new Date(note.updatedAt).toLocaleDateString("tr-TR")));
    const preview = motifComponent("p", (p) => p.bindings.text(() => note.body.split("\n")[0] || "Metin yok"));
    return motifFragment({ childs: [title, when, preview] });
  },
});

Bileşeni kullanmak

Bileşen kullanıldığı yerde, props'uyla birlikte yaratılır. JSX'te etiketi yazmak yeter; derleyici etiketi motifComponent(NoteRow, { note, folder }) çağrısına çevirir. JSX'siz yazımda sınıf new ile, fonksiyon ve Options bileşeni motifComponent ile yaratılır; motifComponent sınıfı da kabul eder.

<NoteRow note={note} folder={this.folder} />
<NoteRow note={note} folder={folder()} />
new NoteRow({ note, folder: this.folder });
motifComponent(NoteRow, { note, folder: this.folder });
motifComponent(NoteRow, { note, folder: folder() });
<NoteRow note={note} folder={this.folder} />
<NoteRow note={note} folder={folder()} />
new NoteRow({ note, folder: this.folder });
motifComponent(NoteRow, { note, folder: this.folder });
motifComponent(NoteRow, { note, folder: folder() });

Fonksiyon bileşeninde folder() klasörü döndüren yerel fonksiyondur.

Yaratılan bileşen bir yere yerleşince görünür:

Yaratılma ve kurulma

Yaratmak ile kurmak ayrı adımlardır. Bileşenin elemanı yaratılırken hazırlanır: sınıfta yapıcıda, fonksiyon ve Options bileşeninde fonksiyon çağrıldığında. İçerik ise bileşen kurulurken (build()) bir kez gelir: view(), initializeComponent ve çocuklar o sırada çalışır. Kurulmuş bir ebeveyne eklenen bileşen hemen kurulur, henüz kurulmamış bir ebeveyne eklenen bileşen ebeveyniyle birlikte kurulur.

Bu yüzden alan başlatıcısında ve yapıcıda this.element vardır ama içi boştur. İçeriğe dokunan kod onBuilt içine, belgeye bağlı olmasını bekleyen kod (odak, ölçü) onMounted içine yazılır (bkz. Yaşam Döngüsü).

JSX'siz yazımda new Component ve motifComponent'in aldığı bütün argüman biçimleri, elemansız bileşenler ve props nesnesinin anahtarları JSX'siz Görünümler sayfasındadır.

Sonraki adım

Bileşene veri ve içerik vermek için Props ve Çocuklar bölümüne geçin.