Başlangıç

MotifJS Dokümantasyonu

MotifJS'in ne yaptığı, bölümlerin dizini ve bir not listesi kesitiyle hızlı bakış.

MotifJS; hızı, reaktiviteyi ve mimari yapıyı tek çatı altında toplayan, modern web uygulamaları için tasarlanmış, JavaScript ve TypeScript için bir UI framework'üdür. Görünüm JSX ile yazılır; derleyici bunu derleme zamanında bileşen çağrılarına çevirir. Aynı çağrılar JSX olmadan, TypeScript ya da JavaScript'te doğrudan da yazılır. JSX'teki her etiket kendi DOM elemanını taşıyan bir bileşen nesnesine dönüşür ve bu nesneler controls ağacında yaşar. Bir getter'a bağlanan değer yalnızca bağlandığı elemanın o özelliğini günceller; bileşen yeniden kurulmaz.

MotifJS neyi farklı yapar?

MotifJS'te arayüz, uygulama açık kaldıkça yaşayan bileşen nesnelerinden kurulu bir ağaçtır. Görünüm bir kez kurulur; sonrasını bu nesneler taşır: bağlar değerleri günceller, ağaç elle ya da bağlarla değişir, ömür açıkça yönetilir.

MotifJS uzun ömürlü ve durum-yoğun arayüzler için tasarlanmıştır. Sunucu tarafı render (SSR) için henüz bir planlama yapılmadı; ileride değerlendirilebilir.

İçindekiler

Kategori Bölüm Konu
Başlangıç Başlarken Kurulum, build yapılandırması, ilk bileşen ve uygulama
Temel kavramlar Bileşenler Sınıf / fonksiyon / Options API bileşenleri, kök eleman, view() ve controls, sınıf üyelerinin adları
Temel kavramlar Bileşen Oluşturma Bir bileşeni tanımlamak, elemanını ve içeriğini vermek, kullanmak; yaratılma ve kurulma
Temel kavramlar Props ve Çocuklar props, bileşenler arası veri akışı, childs
Temel kavramlar controls ve Bileşen Ağacı controls koleksiyonu, bileşen ağacında gezinme, içeriği başka bir yere taşıma
Temel kavramlar Eleman ve Görünüş element, ref, onRefCreated; class, attr, style, setText
Temel kavramlar motif Ad Alanı show / hide, on / off / trigger, setDisposable, clear, motif.options
Temel kavramlar bindings JSX'teki canlı ifadelerin karşılığı, bindings üyeleri, watch, bağın ömrü
Temel kavramlar Durum, Bağlam ve Servisler Durum bayrakları, setState / reState, context, servislere erişim, build / dispose
Temel kavramlar using ile Asenkron Sonuç Serbest bırakılan bileşene gelen sonucu atmak, işleyiciler, MJX129, denetimin zamanı
Temel kavramlar doWork ile Asenkron Sonuç Bileşenin ömrüne bağlı söz, MJX108, catch ve finally, using ile farkı
Temel kavramlar JSX ve Şablonlar JSX kuralları, class/style/öznitelik, spread, x- direktifleri, fragment, derleme tanıları, explain
Temel kavramlar JSX'siz Görünümler motifComponent ve new Component çağrı biçimleri, props anahtarları, metin düğümleri, preconfig, dal ve liste için fragment, JSX'siz dosyada sınıf bileşeni, derlenmiş çıktıyı okuma
Temel kavramlar Reaktivite reactive, createSignal, createComputed, createLazyComputed, effect, untracked
Temel kavramlar Koşullu Gösterim ve Listeler x-wait/x-display, &&, üçlü operatör, .map, key ve öğe kimliği, switch, bağ API'si
Temel kavramlar Olaylar DOM olayları, değiştiriciler, bileşen, yaşam döngüsü ve uygulama olayları, servis olayları (Emitter)
Temel kavramlar Formlar ve İki Yönlü Bağlama Tek yönlü value + oninput, bindings.model ve x-model ile iki yönlü bağ, checked, value, <select>, form gönderimi, doğrulama deseni
Temel kavramlar Yaşam Döngüsü Yaşam döngüsü kancaları ve sıraları, initializeComponent, görünürlük, bekletme, serbest bırakma, uygulama yaşam döngüsü
Uygulama yapısı Stil ve Animasyon class/style/attr yardımcıları, satır içi <style>, Tailwind, Bootstrap ve harici CSS, geçiş (transition) sistemi: CSS sınıfı ve WAAPI keyframe geçişleri, giriş/çıkış kipi (mode)
Uygulama yapısı Bellek Yönetimi ve Serbest Bırakma IDisposable, otomatik temizlik, motif.register/setDisposable, serbest bırakma metotları, detach/silentDetach/silentUnlink, DisposableStore, using/doWork, sızıntı izleme, canlı bileşen sayımı
Uygulama yapısı Hata Yönetimi Fırlatma/rapor/uyarı yolları, MotifError, errorHandler, setUnexpectedErrorHandler, safeCall*, kullanıcı kodunda hatanın yakalandığı yer
Uygulama yapısı Dayanıklılık (Resilience) Resilience.create zinciri ve sıralama, retry, timeout, circuitBreaker, bulkhead, rateLimiter, fallback, decorate, hata kodları
Uygulama yapısı Pencereler app.windows: bir bileşeni yeni pencerede ya da hep üstte Picture-in-Picture penceresinde açmak, geri almak, stil ve tema aynalama, Electron
Yönlendirme Yönlendirmeye Giriş useRouter, gezinme modları, bulunamayan rota ve hata sayfası
Yönlendirme Rota Tablosu RouteItem alanları, iç içe rotalar, redirect/alias, yol parametreleri, resolve/href/routes, tembel rotalar
Yönlendirme RouterView Rota çıktısının yeri, iç içe ve adlandırılmış RouterView
Yönlendirme Bağlantılar rel="router", RouterLink, enableRouterClassing
Yönlendirme Gezinme ve Rota Durumu navigate/navigateByName, canlı rota durumu, direction/state, scrollMemory
Yönlendirme Rota Kancaları ve Guard'lar Rota kancaları, useGuard, onRouterChanged, app.use, yeniden başlatma ve kapatma
Yönlendirme Yığın Gezinmesi stack: tutulan sayfalar, geçiş animasyonu, kaydırarak geri
Bağımlılık Enjeksiyonu Bağımlılık Enjeksiyonuna Giriş Servisi kaydetmek, bileşenden kullanmak, tam örnek
Bağımlılık Enjeksiyonu Servis Kaydı ServiceCollection kayıt biçimleri, startup singleton'lar, @Injectable ve seçenekleri
Bağımlılık Enjeksiyonu Yaşam Süreleri ve Kapsamlar singleton / scoped / transient, gezinme kapsamı, scoped handle
Bağımlılık Enjeksiyonu Servis Çözümleme provider.get / getAsync, getService, FromService, inject, deps, useApplication
Bağımlılık Enjeksiyonu Servislerin Serbest Bırakılması app.dispose / provider.dispose, kapsamın kapanması, transient izleme
Rehberler Motif Üstüne Paket Yazmak Derleyici çıktısının açık API olması; tema, katman, yerleşim, eklenti, veri, sarmalayıcı, ayar ve geçiş paketlerinin dayandığı mekanizmalar
Rehberler Üçüncü Parti Kütüphane Sarmalama Kendi DOM'unu yöneten kütüphaneleri bileşene dönüştürme
Rehberler MotifJS için npm Kütüphanesi Yayımlama Bileşen/servis paketi hazırlama: derleme, tipler, CSS, prop sözleşmesi, yayın denetimi
Rehberler Bileşen Kütüphanesi Yazmak Uygulamaların kullandığı bir kitin kuralları: tek bağla izlenen prop'lar, olduğu gibi kullanılan veri, içeride izlenmeyen durum, elle çizim, değişince yazma, ömür tuzakları, ölçme
Rehberler DevTools sözleşmesi useDevelopment(true) ile açılan __MOTIF_DEVTOOLS__ nesnesi: ağaç, olaylar, reaktivite izi, sayaçlar, router ve hata kayıtları; paketler için registerDevtools
Referans API Referansı Dışa aktarılan tüm sembollerin özeti
Referans Hata ve Uyarı Kodları Bütün MJX kodları, türleri, konsoldaki mesajları ve anlamları
Referans Koleksiyon Sorguları (Query) Query.from(...) ile zincirlenebilir filtreleme, sıralama, gruplama
Referans Koleksiyonlar List, Dictionary, NameValuePair, LinkedList
Gelişmiş Frame Tek değişen içeriği tutan kapsayıcı; zamanlama ve geçişler
Gelişmiş Lazy Dinamik import() ya da herhangi bir Promise ile içerik; yükleme akışı, seçenekler, yeniden deneme, iptal, hata raporu
Gelişmiş Transport ve TransportTo İçeriği başka bir yuvada gösterme; replace/merge kipleri, Transporter
Gelişmiş ContentBody ve ContentBlock Birden çok bloğun birlikte doldurduğu gövde
Gelişmiş Sanal Listeler (Virtualization) Büyük veri için sanal kaydırma

Hızlı bakış

Bir not listesi: NotesService notları tek bir yerde tutar; NotesPane arama kutusunu, satırları ve seçili notun düzenleyicisini gösterir. SettingsService tema ve yoğunluk ayarını tutar ve uygulama açılırken kurulur. Aynı bileşen altı biçimde yazılmıştır: sınıf, fonksiyon ve Options API, her biri JSX ile ve JSX'siz.

import { reactive } from "@motifx/core";

export interface Note {
  id: number;
  title: string;
  body: string;
  pinned: boolean;
  archived: boolean;
  createdAt: number;
  updatedAt: number;
}

export type Folder = "notes" | "pinned" | "archive";

export const FOLDERS: Record<Folder, { title: string; empty: string }> = {
  notes: { title: "Tüm notlar", empty: "Henüz not yok. İlkini siz yazın." },
  pinned: { title: "Sabitlenenler", empty: "Sabitlenmiş not yok. Üstte kalmasını istediğiniz notu sabitleyin." },
  archive: { title: "Arşiv", empty: "Arşiv boş." },
};

const inFolder: Record<Folder, (note: Note) => boolean> = {
  notes: (note) => !note.archived,
  pinned: (note) => note.pinned && !note.archived,
  archive: (note) => note.archived,
};

export class NotesService {
  readonly state = reactive({ items: [] as Note[] });
  private nextId = this.state.items.length + 1;

  list(folder: Folder, query = ""): Note[] {
    const q = query.trim().toLowerCase();
    return this.state.items
      .filter(inFolder[folder])
      .filter((note) => !q || `${note.title}\n${note.body}`.toLowerCase().includes(q))
      .sort((a, b) => Number(b.pinned) - Number(a.pinned) || b.updatedAt - a.updatedAt);
  }

  count(folder: Folder): number {
    return this.state.items.filter(inFolder[folder]).length;
  }

  find(id: number | string): Note | undefined {
    const wanted = Number(id);
    return this.state.items.find((note) => note.id === wanted);
  }

  create(): Note {
    const now = Date.now();
    this.state.items.unshift({ id: this.nextId++, title: "", body: "", pinned: false, archived: false, createdAt: now, updatedAt: now });
    return this.state.items[0];
  }

  touch(note: Note): void {
    note.updatedAt = Date.now();
  }

  togglePin(note: Note): void {
    note.pinned = !note.pinned;
    this.touch(note);
  }

  archive(note: Note): void {
    note.archived = true;
    note.pinned = false;
    this.touch(note);
  }

  restore(note: Note): void {
    note.archived = false;
    this.touch(note);
  }

  remove(id: number): boolean {
    const index = this.state.items.findIndex((note) => note.id === id);
    if (index < 0) return false;
    this.state.items.splice(index, 1);
    return true;
  }

  emptyArchive(): number {
    let removed = 0;
    for (let i = this.state.items.length - 1; i >= 0; i--) {
      if (!this.state.items[i].archived) continue;
      this.state.items.splice(i, 1);
      removed++;
    }
    return removed;
  }
}
import { reactive } from "@motifx/core";

export const FOLDERS = {
  notes: { title: "Tüm notlar", empty: "Henüz not yok. İlkini siz yazın." },
  pinned: { title: "Sabitlenenler", empty: "Sabitlenmiş not yok. Üstte kalmasını istediğiniz notu sabitleyin." },
  archive: { title: "Arşiv", empty: "Arşiv boş." },
};

const inFolder = {
  notes: (note) => !note.archived,
  pinned: (note) => note.pinned && !note.archived,
  archive: (note) => note.archived,
};

export class NotesService {
  state = reactive({ items: [] });
  nextId = this.state.items.length + 1;

  list(folder, query = "") {
    const q = query.trim().toLowerCase();
    return this.state.items
      .filter(inFolder[folder])
      .filter((note) => !q || `${note.title}\n${note.body}`.toLowerCase().includes(q))
      .sort((a, b) => Number(b.pinned) - Number(a.pinned) || b.updatedAt - a.updatedAt);
  }

  count(folder) {
    return this.state.items.filter(inFolder[folder]).length;
  }

  find(id) {
    const wanted = Number(id);
    return this.state.items.find((note) => note.id === wanted);
  }

  create() {
    const now = Date.now();
    this.state.items.unshift({ id: this.nextId++, title: "", body: "", pinned: false, archived: false, createdAt: now, updatedAt: now });
    return this.state.items[0];
  }

  touch(note) {
    note.updatedAt = Date.now();
  }

  togglePin(note) {
    note.pinned = !note.pinned;
    this.touch(note);
  }

  archive(note) {
    note.archived = true;
    note.pinned = false;
    this.touch(note);
  }

  restore(note) {
    note.archived = false;
    this.touch(note);
  }

  remove(id) {
    const index = this.state.items.findIndex((note) => note.id === id);
    if (index < 0) return false;
    this.state.items.splice(index, 1);
    return true;
  }

  emptyArchive() {
    let removed = 0;
    for (let i = this.state.items.length - 1; i >= 0; i--) {
      if (!this.state.items[i].archived) continue;
      this.state.items.splice(i, 1);
      removed++;
    }
    return removed;
  }
}
import { reactive } from "@motifx/core";

export type Theme = "system" | "light" | "dark";
export type Density = "comfortable" | "compact";

export const THEMES: readonly Theme[] = ["system", "light", "dark"];
export const DENSITIES: readonly Density[] = ["comfortable", "compact"];

const STORAGE_KEY = "settings";

export class SettingsService {
  readonly state = reactive({ theme: "system" as Theme, density: "comfortable" as Density });

  constructor() {
    try {
      const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "{}");
      if (THEMES.includes(saved.theme)) this.state.theme = saved.theme;
      if (DENSITIES.includes(saved.density)) this.state.density = saved.density;
    } catch {}
    this.apply();
  }

  setTheme(theme: Theme) {
    this.state.theme = theme;
    this.save();
  }

  setDensity(density: Density) {
    this.state.density = density;
    this.save();
  }

  private save() {
    this.apply();
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify({ theme: this.state.theme, density: this.state.density }));
    } catch {}
  }

  private apply() {
    const root = document.documentElement;
    if (this.state.theme === "system") delete root.dataset.theme;
    else root.dataset.theme = this.state.theme;
    if (this.state.density === "comfortable") delete root.dataset.density;
    else root.dataset.density = this.state.density;
  }
}
import { reactive } from "@motifx/core";

export const THEMES = ["system", "light", "dark"];
export const DENSITIES = ["comfortable", "compact"];

const STORAGE_KEY = "settings";

export class SettingsService {
  state = reactive({ theme: "system", density: "comfortable" });

  constructor() {
    try {
      const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "{}");
      if (THEMES.includes(saved.theme)) this.state.theme = saved.theme;
      if (DENSITIES.includes(saved.density)) this.state.density = saved.density;
    } catch {}
    this.apply();
  }

  setTheme(theme) {
    this.state.theme = theme;
    this.save();
  }

  setDensity(density) {
    this.state.density = density;
    this.save();
  }

  save() {
    this.apply();
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify({ theme: this.state.theme, density: this.state.density }));
    } catch {}
  }

  apply() {
    const root = document.documentElement;
    if (this.state.theme === "system") delete root.dataset.theme;
    else root.dataset.theme = this.state.theme;
    if (this.state.density === "comfortable") delete root.dataset.density;
    else root.dataset.density = this.state.density;
  }
}
import { Component, reactive } from "@motifx/core";
import { NotesService, type Note } from "../services/notes";

export class NotesPane extends Component {
  notes = this.getService(NotesService)!;
  state = reactive({ query: "", current: null as Note | null });

  get visible() {
    return this.notes.list("notes", this.state.query);
  }

  create() {
    this.state.current = this.notes.create();
  }

  override view() {
    return (
      <div class="notes">
        <header class="list__head">
          <h2>Notlar</h2>
          <button type="button" onclick={() => this.create()}>Yeni not</button>
        </header>
        <input type="search" placeholder="Notlarda ara" x-model={() => this.state.query} />
        <div class="rows">
          {this.visible.map((note) => (
            <button type="button" key={note.id} class={() => (note === this.state.current ? "row active" : "row")} onclick={() => (this.state.current = note)}>
              <i class="pin" x-display={() => note.pinned} />
              <span>{() => note.title.trim() || "Adsız"}</span>
            </button>
          ))}
          <p class="list__empty" x-display={() => this.visible.length === 0}>
            {() => (this.state.query.trim() ? "Arama sonucu yok." : "Henüz not yok.")}
          </p>
        </div>
        <article class="detail" x-display={() => this.state.current !== null}>
          <input placeholder="Başlık" x-model={() => this.state.current!.title} />
          <textarea placeholder="Not" x-model={() => this.state.current!.body} />
          <button type="button" onclick={() => this.notes.togglePin(this.state.current!)}>
            {() => (this.state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle")}
          </button>
        </article>
      </div>
    );
  }
}
import { FromService, reactive } from "@motifx/core";
import { NotesService, type Note } from "../services/notes";

export function NotesPane() {
  const notes = FromService(NotesService)!;
  const state = reactive({ query: "", current: null as Note | null });
  const visible = () => notes.list("notes", state.query);
  const create = () => (state.current = notes.create());

  return (
    <div class="notes">
      <header class="list__head">
        <h2>Notlar</h2>
        <button type="button" onclick={create}>Yeni not</button>
      </header>
      <input type="search" placeholder="Notlarda ara" x-model={() => state.query} />
      <div class="rows">
        {visible().map((note) => (
          <button type="button" key={note.id} class={() => (note === state.current ? "row active" : "row")} onclick={() => (state.current = note)}>
            <i class="pin" x-display={() => note.pinned} />
            <span>{() => note.title.trim() || "Adsız"}</span>
          </button>
        ))}
        <p class="list__empty" x-display={() => visible().length === 0}>
          {() => (state.query.trim() ? "Arama sonucu yok." : "Henüz not yok.")}
        </p>
      </div>
      <article class="detail" x-display={() => state.current !== null}>
        <input placeholder="Başlık" x-model={() => state.current!.title} />
        <textarea placeholder="Not" x-model={() => state.current!.body} />
        <button type="button" onclick={() => notes.togglePin(state.current!)}>
          {() => (state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle")}
        </button>
      </article>
    </div>
  );
}
import { FromService, motifOptions, reactive } from "@motifx/core";
import { NotesService, type Note } from "../services/notes";

export const NotesPane = () => {
  const notes = FromService(NotesService)!;

  return motifOptions({
    el: "div",
    data: reactive({ query: "", current: null as Note | null }),

    visible() {
      return notes.list("notes", this.data.query);
    },

    create() {
      this.data.current = notes.create();
    },

    view() {
      this.class.add("notes");
      return (
        <>
          <header class="list__head">
            <h2>Notlar</h2>
            <button type="button" onclick={() => this.create()}>Yeni not</button>
          </header>
          <input type="search" placeholder="Notlarda ara" x-model={() => this.data.query} />
          <div class="rows">
            {this.visible().map((note) => (
              <button type="button" key={note.id} class={() => (note === this.data.current ? "row active" : "row")} onclick={() => (this.data.current = note)}>
                <i class="pin" x-display={() => note.pinned} />
                <span>{() => note.title.trim() || "Adsız"}</span>
              </button>
            ))}
            <p class="list__empty" x-display={() => this.visible().length === 0}>
              {() => (this.data.query.trim() ? "Arama sonucu yok." : "Henüz not yok.")}
            </p>
          </div>
          <article class="detail" x-display={() => this.data.current !== null}>
            <input placeholder="Başlık" x-model={() => this.data.current!.title} />
            <textarea placeholder="Not" x-model={() => this.data.current!.body} />
            <button type="button" onclick={() => notes.togglePin(this.data.current!)}>
              {() => (this.data.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle")}
            </button>
          </article>
        </>
      );
    },
  });
};
import { Component, ComponentBase, motifComponent, reactive } from "@motifx/core";
import { NotesService, type Note } from "../services/notes";

export class NotesPane extends Component {
  notes = this.getService(NotesService)!;
  state = reactive({ query: "", current: null as Note | null });

  get visible() {
    return this.notes.list("notes", this.state.query);
  }

  create() {
    this.state.current = this.notes.create();
  }
  
  override view() {
    return motifComponent("div", (root: ComponentBase) => {
      root.class.add("notes");
      root.controls.add(this.head(), this.search(), this.rows(), this.detail());
    });
  }

  private head() {
    return motifComponent("header", (head: ComponentBase) => {
      head.class.add("list__head");
      const title = motifComponent("h2", (h2: ComponentBase) => {
        h2.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Notlar")));
      });
      const newNote = motifComponent("button", (button: ComponentBase) => {
        button.attr.add({ type: "button" });
        button.motif.on("click", () => this.create());
        button.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Yeni not")));
      });
      head.controls.add(title, newNote);
    });
  }

  private search() {
    return motifComponent("input", (field: ComponentBase) => {
      field.attr.add({ type: "search", placeholder: "Notlarda ara" });
      field.bindings.model(() => this.state.query, (value: string) => (this.state.query = value));
    });
  }

  private rows() {
    return motifComponent("div", (rows: ComponentBase) => {
      rows.class.add("rows");
      const list = motifComponent({
        initializeComponent: (holder: ComponentBase) => {
          holder.bindings.list(() => this.visible, (note: Note) => this.row(note));
        },
      });
      const empty = motifComponent("p", {
        preconfig: (p: ComponentBase) => p.bindings.display(() => this.visible.length === 0),
        initializeComponent: (p: ComponentBase) => {
          p.class.add("list__empty");
          p.bindings.text(() => (this.state.query.trim() ? "Arama sonucu yok." : "Henüz not yok."));
        },
      });
      rows.controls.add(list, empty);
    });
  }

  private row(note: Note) {
    return motifComponent("button", {
      indexkey: () => note.id,
      initializeComponent: (row: ComponentBase) => {
        row.attr.add({ type: "button" });
        row.class.add(() => (note === this.state.current ? "row active" : "row"));
        row.motif.on("click", () => (this.state.current = note));
        const pin = motifComponent("i", {
          preconfig: (i: ComponentBase) => i.bindings.display(() => note.pinned),
          initializeComponent: (i: ComponentBase) => i.class.add("pin"),
        });
        const title = motifComponent("span", (span: ComponentBase) => span.bindings.text(() => note.title.trim() || "Adsız"));
        row.controls.add(pin, title);
      },
    });
  }

  private detail() {
    return motifComponent("article", {
      preconfig: (article: ComponentBase) => article.bindings.display(() => this.state.current !== null),
      initializeComponent: (article: ComponentBase) => {
        article.class.add("detail");
        const title = motifComponent("input", (field: ComponentBase) => {
          field.attr.add({ placeholder: "Başlık" });
          field.bindings.model(() => this.state.current!.title, (value: string) => (this.state.current!.title = value));
        });
        const body = motifComponent("textarea", (field: ComponentBase) => {
          field.attr.add({ placeholder: "Not" });
          field.bindings.model(() => this.state.current!.body, (value: string) => (this.state.current!.body = value));
        });
        const pin = motifComponent("button", (button: ComponentBase) => {
          button.attr.add({ type: "button" });
          button.motif.on("click", () => this.notes.togglePin(this.state.current!));
          button.bindings.text(() => (this.state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle"));
        });
        article.controls.add(title, body, pin);
      },
    });
  }
}
import { ComponentBase, FromService, motifComponent, reactive } from "@motifx/core";
import { NotesService, type Note } from "../services/notes";

export function NotesPane() {
  const notes = FromService(NotesService)!;
  const state = reactive({ query: "", current: null as Note | null });
  const visible = () => notes.list("notes", state.query);
  const create = () => (state.current = notes.create());

  const head = motifComponent("header", (bar: ComponentBase) => {
    bar.class.add("list__head");
    const title = motifComponent("h2", (h2: ComponentBase) => {
      h2.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Notlar")));
    });
    const newNote = motifComponent("button", (button: ComponentBase) => {
      button.attr.add({ type: "button" });
      button.motif.on("click", create);
      button.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Yeni not")));
    });
    bar.controls.add(title, newNote);
  });

  const search = motifComponent("input", (field: ComponentBase) => {
    field.attr.add({ type: "search", placeholder: "Notlarda ara" });
    field.bindings.model(() => state.query, (value: string) => (state.query = value));
  });

  const row = (note: Note) =>
    motifComponent("button", {
      indexkey: () => note.id,
      initializeComponent: (item: ComponentBase) => {
        item.attr.add({ type: "button" });
        item.class.add(() => (note === state.current ? "row active" : "row"));
        item.motif.on("click", () => (state.current = note));
        const pin = motifComponent("i", {
          preconfig: (i: ComponentBase) => i.bindings.display(() => note.pinned),
          initializeComponent: (i: ComponentBase) => i.class.add("pin"),
        });
        const title = motifComponent("span", (span: ComponentBase) => span.bindings.text(() => note.title.trim() || "Adsız"));
        item.controls.add(pin, title);
      },
    });

  const rows = motifComponent("div", (box: ComponentBase) => {
    box.class.add("rows");
    const list = motifComponent({
      initializeComponent: (holder: ComponentBase) => holder.bindings.list(visible, row),
    });
    const empty = motifComponent("p", {
      preconfig: (p: ComponentBase) => p.bindings.display(() => visible().length === 0),
      initializeComponent: (p: ComponentBase) => {
        p.class.add("list__empty");
        p.bindings.text(() => (state.query.trim() ? "Arama sonucu yok." : "Henüz not yok."));
      },
    });
    box.controls.add(list, empty);
  });

  const detail = motifComponent("article", {
    preconfig: (article: ComponentBase) => article.bindings.display(() => state.current !== null),
    initializeComponent: (article: ComponentBase) => {
      article.class.add("detail");
      const title = motifComponent("input", (field: ComponentBase) => {
        field.attr.add({ placeholder: "Başlık" });
        field.bindings.model(() => state.current!.title, (value: string) => (state.current!.title = value));
      });
      const body = motifComponent("textarea", (field: ComponentBase) => {
        field.attr.add({ placeholder: "Not" });
        field.bindings.model(() => state.current!.body, (value: string) => (state.current!.body = value));
      });
      const pin = motifComponent("button", (button: ComponentBase) => {
        button.attr.add({ type: "button" });
        button.motif.on("click", () => notes.togglePin(state.current!));
        button.bindings.text(() => (state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle"));
      });
      article.controls.add(title, body, pin);
    },
  });

  return motifComponent("div", (root: ComponentBase) => {
    root.class.add("notes");
    root.controls.add(head, search, rows, detail);
  });
}
import { ComponentBase, FromService, motifComponent, motifOptions, reactive } from "@motifx/core";
import { NotesService, type Note } from "../services/notes";

export const NotesPane = () => {
  const notes = FromService(NotesService)!;

  return motifOptions({
    el: "div",
    data: reactive({ query: "", current: null as Note | null }),

    visible() {
      return notes.list("notes", this.data.query);
    },

    create() {
      this.data.current = notes.create();
    },

    view() {
      this.class.add("notes");
      return [this.head(), this.search(), this.rows(), this.detail()];
    },

    head() {
      return motifComponent("header", (head: ComponentBase) => {
        head.class.add("list__head");
        const title = motifComponent("h2", (h2: ComponentBase) => {
          h2.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Notlar")));
        });
        const newNote = motifComponent("button", (button: ComponentBase) => {
          button.attr.add({ type: "button" });
          button.motif.on("click", () => this.create());
          button.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Yeni not")));
        });
        head.controls.add(title, newNote);
      });
    },

    search() {
      return motifComponent("input", (field: ComponentBase) => {
        field.attr.add({ type: "search", placeholder: "Notlarda ara" });
        field.bindings.model(() => this.data.query, (value: string) => (this.data.query = value));
      });
    },

    rows() {
      return motifComponent("div", (rows: ComponentBase) => {
        rows.class.add("rows");
        const list = motifComponent({
          initializeComponent: (holder: ComponentBase) => {
            holder.bindings.list(() => this.visible(), (note: Note) => this.row(note));
          },
        });
        const empty = motifComponent("p", {
          preconfig: (p: ComponentBase) => p.bindings.display(() => this.visible().length === 0),
          initializeComponent: (p: ComponentBase) => {
            p.class.add("list__empty");
            p.bindings.text(() => (this.data.query.trim() ? "Arama sonucu yok." : "Henüz not yok."));
          },
        });
        rows.controls.add(list, empty);
      });
    },

    row(note: Note) {
      return motifComponent("button", {
        indexkey: () => note.id,
        initializeComponent: (row: ComponentBase) => {
          row.attr.add({ type: "button" });
          row.class.add(() => (note === this.data.current ? "row active" : "row"));
          row.motif.on("click", () => (this.data.current = note));
          const pin = motifComponent("i", {
            preconfig: (i: ComponentBase) => i.bindings.display(() => note.pinned),
            initializeComponent: (i: ComponentBase) => i.class.add("pin"),
          });
          const title = motifComponent("span", (span: ComponentBase) => span.bindings.text(() => note.title.trim() || "Adsız"));
          row.controls.add(pin, title);
        },
      });
    },

    detail() {
      return motifComponent("article", {
        preconfig: (article: ComponentBase) => article.bindings.display(() => this.data.current !== null),
        initializeComponent: (article: ComponentBase) => {
          article.class.add("detail");
          const title = motifComponent("input", (field: ComponentBase) => {
            field.attr.add({ placeholder: "Başlık" });
            field.bindings.model(() => this.data.current!.title, (value: string) => (this.data.current!.title = value));
          });
          const body = motifComponent("textarea", (field: ComponentBase) => {
            field.attr.add({ placeholder: "Not" });
            field.bindings.model(() => this.data.current!.body, (value: string) => (this.data.current!.body = value));
          });
          const pin = motifComponent("button", (button: ComponentBase) => {
            button.attr.add({ type: "button" });
            button.motif.on("click", () => notes.togglePin(this.data.current!));
            button.bindings.text(() => (this.data.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle"));
          });
          article.controls.add(title, body, pin);
        },
      });
    },
  });
};
import { Component, reactive } from "@motifx/core";
import { NotesService } from "../services/notes";

export class NotesPane extends Component {
  notes = this.getService(NotesService);
  state = reactive({ query: "", current: null });

  get visible() {
    return this.notes.list("notes", this.state.query);
  }

  create() {
    this.state.current = this.notes.create();
  }

  view() {
    return (
      <div class="notes">
        <header class="list__head">
          <h2>Notlar</h2>
          <button type="button" onclick={() => this.create()}>Yeni not</button>
        </header>
        <input type="search" placeholder="Notlarda ara" x-model={() => this.state.query} />
        <div class="rows">
          {this.visible.map((note) => (
            <button type="button" key={note.id} class={() => (note === this.state.current ? "row active" : "row")} onclick={() => (this.state.current = note)}>
              <i class="pin" x-display={() => note.pinned} />
              <span>{() => note.title.trim() || "Adsız"}</span>
            </button>
          ))}
          <p class="list__empty" x-display={() => this.visible.length === 0}>
            {() => (this.state.query.trim() ? "Arama sonucu yok." : "Henüz not yok.")}
          </p>
        </div>
        <article class="detail" x-display={() => this.state.current !== null}>
          <input placeholder="Başlık" x-model={() => this.state.current.title} />
          <textarea placeholder="Not" x-model={() => this.state.current.body} />
          <button type="button" onclick={() => this.notes.togglePin(this.state.current)}>
            {() => (this.state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle")}
          </button>
        </article>
      </div>
    );
  }
}
import { FromService, reactive } from "@motifx/core";
import { NotesService } from "../services/notes";

export function NotesPane() {
  const notes = FromService(NotesService);
  const state = reactive({ query: "", current: null });
  const visible = () => notes.list("notes", state.query);
  const create = () => (state.current = notes.create());

  return (
    <div class="notes">
      <header class="list__head">
        <h2>Notlar</h2>
        <button type="button" onclick={create}>Yeni not</button>
      </header>
      <input type="search" placeholder="Notlarda ara" x-model={() => state.query} />
      <div class="rows">
        {visible().map((note) => (
          <button type="button" key={note.id} class={() => (note === state.current ? "row active" : "row")} onclick={() => (state.current = note)}>
            <i class="pin" x-display={() => note.pinned} />
            <span>{() => note.title.trim() || "Adsız"}</span>
          </button>
        ))}
        <p class="list__empty" x-display={() => visible().length === 0}>
          {() => (state.query.trim() ? "Arama sonucu yok." : "Henüz not yok.")}
        </p>
      </div>
      <article class="detail" x-display={() => state.current !== null}>
        <input placeholder="Başlık" x-model={() => state.current.title} />
        <textarea placeholder="Not" x-model={() => state.current.body} />
        <button type="button" onclick={() => notes.togglePin(state.current)}>
          {() => (state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle")}
        </button>
      </article>
    </div>
  );
}
import { FromService, motifOptions, reactive } from "@motifx/core";
import { NotesService } from "../services/notes";

export const NotesPane = () => {
  const notes = FromService(NotesService);

  return motifOptions({
    el: "div",
    data: reactive({ query: "", current: null }),

    visible() {
      return notes.list("notes", this.data.query);
    },

    create() {
      this.data.current = notes.create();
    },

    view() {
      this.class.add("notes");
      return (
        <>
          <header class="list__head">
            <h2>Notlar</h2>
            <button type="button" onclick={() => this.create()}>Yeni not</button>
          </header>
          <input type="search" placeholder="Notlarda ara" x-model={() => this.data.query} />
          <div class="rows">
            {this.visible().map((note) => (
              <button type="button" key={note.id} class={() => (note === this.data.current ? "row active" : "row")} onclick={() => (this.data.current = note)}>
                <i class="pin" x-display={() => note.pinned} />
                <span>{() => note.title.trim() || "Adsız"}</span>
              </button>
            ))}
            <p class="list__empty" x-display={() => this.visible().length === 0}>
              {() => (this.data.query.trim() ? "Arama sonucu yok." : "Henüz not yok.")}
            </p>
          </div>
          <article class="detail" x-display={() => this.data.current !== null}>
            <input placeholder="Başlık" x-model={() => this.data.current.title} />
            <textarea placeholder="Not" x-model={() => this.data.current.body} />
            <button type="button" onclick={() => notes.togglePin(this.data.current)}>
              {() => (this.data.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle")}
            </button>
          </article>
        </>
      );
    },
  });
};
import { Component, motifComponent, reactive } from "@motifx/core";
import { NotesService } from "../services/notes";

export class NotesPane extends Component {
  notes = this.getService(NotesService);
  state = reactive({ query: "", current: null });

  get visible() {
    return this.notes.list("notes", this.state.query);
  }

  create() {
    this.state.current = this.notes.create();
  }

  view() {
    return motifComponent("div", (root) => {
      root.class.add("notes");
      root.controls.add(this.head(), this.search(), this.rows(), this.detail());
    });
  }

  head() {
    return motifComponent("header", (head) => {
      head.class.add("list__head");
      const title = motifComponent("h2", (h2) => {
        h2.controls.add(motifComponent("text", (node) => node.setText("Notlar")));
      });
      const newNote = motifComponent("button", (button) => {
        button.attr.add({ type: "button" });
        button.motif.on("click", () => this.create());
        button.controls.add(motifComponent("text", (node) => node.setText("Yeni not")));
      });
      head.controls.add(title, newNote);
    });
  }

  search() {
    return motifComponent("input", (field) => {
      field.attr.add({ type: "search", placeholder: "Notlarda ara" });
      field.bindings.model(() => this.state.query, (value) => (this.state.query = value));
    });
  }

  rows() {
    return motifComponent("div", (rows) => {
      rows.class.add("rows");
      const list = motifComponent({
        initializeComponent: (holder) => {
          holder.bindings.list(() => this.visible, (note) => this.row(note));
        },
      });
      const empty = motifComponent("p", {
        preconfig: (p) => p.bindings.display(() => this.visible.length === 0),
        initializeComponent: (p) => {
          p.class.add("list__empty");
          p.bindings.text(() => (this.state.query.trim() ? "Arama sonucu yok." : "Henüz not yok."));
        },
      });
      rows.controls.add(list, empty);
    });
  }

  row(note) {
    return motifComponent("button", {
      indexkey: () => note.id,
      initializeComponent: (row) => {
        row.attr.add({ type: "button" });
        row.class.add(() => (note === this.state.current ? "row active" : "row"));
        row.motif.on("click", () => (this.state.current = note));
        const pin = motifComponent("i", {
          preconfig: (i) => i.bindings.display(() => note.pinned),
          initializeComponent: (i) => i.class.add("pin"),
        });
        const title = motifComponent("span", (span) => span.bindings.text(() => note.title.trim() || "Adsız"));
        row.controls.add(pin, title);
      },
    });
  }

  detail() {
    return motifComponent("article", {
      preconfig: (article) => article.bindings.display(() => this.state.current !== null),
      initializeComponent: (article) => {
        article.class.add("detail");
        const title = motifComponent("input", (field) => {
          field.attr.add({ placeholder: "Başlık" });
          field.bindings.model(() => this.state.current.title, (value) => (this.state.current.title = value));
        });
        const body = motifComponent("textarea", (field) => {
          field.attr.add({ placeholder: "Not" });
          field.bindings.model(() => this.state.current.body, (value) => (this.state.current.body = value));
        });
        const pin = motifComponent("button", (button) => {
          button.attr.add({ type: "button" });
          button.motif.on("click", () => this.notes.togglePin(this.state.current));
          button.bindings.text(() => (this.state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle"));
        });
        article.controls.add(title, body, pin);
      },
    });
  }
}
import { FromService, motifComponent, reactive } from "@motifx/core";
import { NotesService } from "../services/notes";

export function NotesPane() {
  const notes = FromService(NotesService);
  const state = reactive({ query: "", current: null });
  const visible = () => notes.list("notes", state.query);
  const create = () => (state.current = notes.create());

  const head = motifComponent("header", (bar) => {
    bar.class.add("list__head");
    const title = motifComponent("h2", (h2) => {
      h2.controls.add(motifComponent("text", (node) => node.setText("Notlar")));
    });
    const newNote = motifComponent("button", (button) => {
      button.attr.add({ type: "button" });
      button.motif.on("click", create);
      button.controls.add(motifComponent("text", (node) => node.setText("Yeni not")));
    });
    bar.controls.add(title, newNote);
  });

  const search = motifComponent("input", (field) => {
    field.attr.add({ type: "search", placeholder: "Notlarda ara" });
    field.bindings.model(() => state.query, (value) => (state.query = value));
  });

  const row = (note) =>
    motifComponent("button", {
      indexkey: () => note.id,
      initializeComponent: (item) => {
        item.attr.add({ type: "button" });
        item.class.add(() => (note === state.current ? "row active" : "row"));
        item.motif.on("click", () => (state.current = note));
        const pin = motifComponent("i", {
          preconfig: (i) => i.bindings.display(() => note.pinned),
          initializeComponent: (i) => i.class.add("pin"),
        });
        const title = motifComponent("span", (span) => span.bindings.text(() => note.title.trim() || "Adsız"));
        item.controls.add(pin, title);
      },
    });

  const rows = motifComponent("div", (box) => {
    box.class.add("rows");
    const list = motifComponent({
      initializeComponent: (holder) => holder.bindings.list(visible, row),
    });
    const empty = motifComponent("p", {
      preconfig: (p) => p.bindings.display(() => visible().length === 0),
      initializeComponent: (p) => {
        p.class.add("list__empty");
        p.bindings.text(() => (state.query.trim() ? "Arama sonucu yok." : "Henüz not yok."));
      },
    });
    box.controls.add(list, empty);
  });

  const detail = motifComponent("article", {
    preconfig: (article) => article.bindings.display(() => state.current !== null),
    initializeComponent: (article) => {
      article.class.add("detail");
      const title = motifComponent("input", (field) => {
        field.attr.add({ placeholder: "Başlık" });
        field.bindings.model(() => state.current.title, (value) => (state.current.title = value));
      });
      const body = motifComponent("textarea", (field) => {
        field.attr.add({ placeholder: "Not" });
        field.bindings.model(() => state.current.body, (value) => (state.current.body = value));
      });
      const pin = motifComponent("button", (button) => {
        button.attr.add({ type: "button" });
        button.motif.on("click", () => notes.togglePin(state.current));
        button.bindings.text(() => (state.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle"));
      });
      article.controls.add(title, body, pin);
    },
  });

  return motifComponent("div", (root) => {
    root.class.add("notes");
    root.controls.add(head, search, rows, detail);
  });
}
import { FromService, motifComponent, motifOptions, reactive } from "@motifx/core";
import { NotesService } from "../services/notes";

export const NotesPane = () => {
  const notes = FromService(NotesService);

  return motifOptions({
    el: "div",
    data: reactive({ query: "", current: null }),

    visible() {
      return notes.list("notes", this.data.query);
    },

    create() {
      this.data.current = notes.create();
    },

    view() {
      this.class.add("notes");
      return [this.head(), this.search(), this.rows(), this.detail()];
    },

    head() {
      return motifComponent("header", (head) => {
        head.class.add("list__head");
        const title = motifComponent("h2", (h2) => {
          h2.controls.add(motifComponent("text", (node) => node.setText("Notlar")));
        });
        const newNote = motifComponent("button", (button) => {
          button.attr.add({ type: "button" });
          button.motif.on("click", () => this.create());
          button.controls.add(motifComponent("text", (node) => node.setText("Yeni not")));
        });
        head.controls.add(title, newNote);
      });
    },

    search() {
      return motifComponent("input", (field) => {
        field.attr.add({ type: "search", placeholder: "Notlarda ara" });
        field.bindings.model(() => this.data.query, (value) => (this.data.query = value));
      });
    },

    rows() {
      return motifComponent("div", (rows) => {
        rows.class.add("rows");
        const list = motifComponent({
          initializeComponent: (holder) => {
            holder.bindings.list(() => this.visible(), (note) => this.row(note));
          },
        });
        const empty = motifComponent("p", {
          preconfig: (p) => p.bindings.display(() => this.visible().length === 0),
          initializeComponent: (p) => {
            p.class.add("list__empty");
            p.bindings.text(() => (this.data.query.trim() ? "Arama sonucu yok." : "Henüz not yok."));
          },
        });
        rows.controls.add(list, empty);
      });
    },

    row(note) {
      return motifComponent("button", {
        indexkey: () => note.id,
        initializeComponent: (row) => {
          row.attr.add({ type: "button" });
          row.class.add(() => (note === this.data.current ? "row active" : "row"));
          row.motif.on("click", () => (this.data.current = note));
          const pin = motifComponent("i", {
            preconfig: (i) => i.bindings.display(() => note.pinned),
            initializeComponent: (i) => i.class.add("pin"),
          });
          const title = motifComponent("span", (span) => span.bindings.text(() => note.title.trim() || "Adsız"));
          row.controls.add(pin, title);
        },
      });
    },

    detail() {
      return motifComponent("article", {
        preconfig: (article) => article.bindings.display(() => this.data.current !== null),
        initializeComponent: (article) => {
          article.class.add("detail");
          const title = motifComponent("input", (field) => {
            field.attr.add({ placeholder: "Başlık" });
            field.bindings.model(() => this.data.current.title, (value) => (this.data.current.title = value));
          });
          const body = motifComponent("textarea", (field) => {
            field.attr.add({ placeholder: "Not" });
            field.bindings.model(() => this.data.current.body, (value) => (this.data.current.body = value));
          });
          const pin = motifComponent("button", (button) => {
            button.attr.add({ type: "button" });
            button.motif.on("click", () => notes.togglePin(this.data.current));
            button.bindings.text(() => (this.data.current?.pinned ? "Sabitlemeyi kaldır" : "Sabitle"));
          });
          article.controls.add(title, body, pin);
        },
      });
    },
  });
};
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", new NotesPane());
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", <NotesPane />);
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", new NotesPane());
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", NotesPane());
import { Application, motifComponent } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", motifComponent(NotesPane));
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", new NotesPane());
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", <NotesPane />);
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", new NotesPane());
import { Application } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", NotesPane());
import { Application, motifComponent } from "@motifx/core";
import { NotesPane } from "./pages/NotesPane";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
app.run("#app", motifComponent(NotesPane));

Örnekte dikkat çeken noktalar:

Sonraki adım

Başlamak için Başlarken bölümüne geçin.