Temel kavramlar

motif Ad Alanı

Bileşen üzerindeki çerçeve işlemleri: `show`/`hide`/`toggle`, `on`/`off`/`trigger`, `setDisposable`/`register`, `clear`, `stopAnimations` ve `motif.options`.

MotifJS'in bir bileşen üzerinde yaptığı işlemler motif ad alanında toplanır: sınıfta ve Options'ta this.motif, fonksiyon bileşeninde kökün ya da ref ile tutulan bir bileşenin motif'i. Bu işlemler sınıfın kendi üyeleriyle karışmaz; sınıfta show, on ya da clear adında kendi metodunuzu yazabilirsiniz (bkz. Sınıf üyelerinin adları).

Üyeler

Üye Ne yapar
show(), hide(), toggle() Bileşeni serbest bırakmadan gösterir ve gizler. Gizlenen elemanın yerinde <!--h--> izi kalır, gösterilince eleman izin yerine döner. show ve hide geçiş bitince çözülen bir söz döndürür (bkz. Görünürlük).
on(ad, fn), off(ad, fn) Bir DOM olayını ya da bileşen olayını dinler ve bırakır. "keydown:prevent", "click:once" gibi değiştiriciler yazılır. Dinleyiciler bileşen serbest bırakılınca kalkar (bkz. Bileşen olay API'si).
trigger(ad, veri) O adla kaydedilmiş işleyicileri veri ile çağırır; DOM'a olay göndermez.
addHandler(ad, fn) on(ad, fn) ile aynıdır, değer döndürmez.
setDisposable(fn) fn bileşen serbest bırakılırken çalışır: belgeye eklenen dinleyiciyi, zamanlayıcıyı ya da dış bir kütüphanenin kurulumunu bırakmak için (bkz. Bellek Yönetimi ve Serbest Bırakma).
register(nesne) dispose() metodu olan bir nesneyi bileşenin ömrüne bağlar; bileşenle birlikte serbest bırakılır.
clear() Bütün çocukları serbest bırakır (controls.clearAsync()). options.disableDisposal açıksa hiçbir şey yapmaz.
stopAnimations() Süren CSS sınıf geçişini bitirir, WAAPI animasyonlarını iptal eder (bkz. Stil ve Animasyon).
options Bileşenin ayarları; aşağıda.

Bir örnek

Not listesinin arama kutusu üç işi motif ile yapar. / tuşu sayfanın her yerinden aramaya odak verir; bu dinleyici belgeye eklendiği için bileşen kaldırılınca setDisposable ile bırakılır. Escape aramayı temizler: kutunun motif.on("keydown", …) dinleyicisi alandan kabaran olayı yakalar. Alan odaktayken / ipucu motif.hide() ile gizlenir, odak gidince motif.show() ile geri gelir. Aramanın kendisi ebeveynin reaktif state nesnesindedir:

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

export class SearchBox extends Component<HTMLDivElement, { state: { query: string } }> {
  input!: Component<HTMLInputElement>;
  hint!: Component<HTMLElement>;

  override onConfigured() {
    this.class.add("search");
  }

  override onConfig() {
    this.motif.on("keydown", (_box, e: KeyboardEvent) => {
      if (e.key === "Escape") this.props.state.query = "";
    });

    const focusSearch = (e: KeyboardEvent) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement as HTMLElement | null)?.tagName ?? "")) return;
      e.preventDefault();
      this.input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  }

  override view() {
    const { state } = this.props;
    return (
      <>
        <input
          ref={this.input}
          type="search"
          placeholder="Notlarda ara"
          aria-label="Notlarda ara"
          autocomplete="off"
          x-model={() => state.query}
          onfocus={() => this.hint.motif.hide()}
          onblur={() => this.hint.motif.show()}
        />
        <kbd ref={this.hint}>/</kbd>
      </>
    );
  }
}
import { Component, ComponentBase, motifComponent, motifFragment } from "@motifx/core";

export class SearchBox extends Component<HTMLDivElement, { state: { query: string } }> {
  static override elementTag = "div";
  input!: Component<HTMLInputElement>;
  hint!: Component<HTMLElement>;

  override onConfigured() {
    this.class.add("search");
  }

  override onConfig() {
    this.motif.on("keydown", (_box, e: KeyboardEvent) => {
      if (e.key === "Escape") this.props.state.query = "";
    });

    const focusSearch = (e: KeyboardEvent) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement as HTMLElement | null)?.tagName ?? "")) return;
      e.preventDefault();
      this.input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  }

  override view() {
    const { state } = this.props;
    const input = motifComponent("input", {
      ref: (field: Component<HTMLInputElement>) => (this.input = field),
      initializeComponent: (field: ComponentBase) => {
        field.attr.add({ type: "search", placeholder: "Notlarda ara", "aria-label": "Notlarda ara", autocomplete: "off" });
        field.bindings.model(() => state.query, (value: string) => (state.query = value));
        field.motif.on("focus", () => this.hint.motif.hide());
        field.motif.on("blur", () => this.hint.motif.show());
      },
    });
    const hint = motifComponent("kbd", {
      ref: (kbd: Component<HTMLElement>) => (this.hint = kbd),
      initializeComponent: (kbd: ComponentBase) => kbd.setText("/"),
    });
    return motifFragment({ childs: [input, hint] });
  }
}
import type { Component, ComponentBase } from "@motifx/core";

export function SearchBox(props: { state: { query: string } }) {
  const { state } = props;
  let input!: Component<HTMLInputElement>;
  let hint!: Component<HTMLElement>;

  const configured = (box: ComponentBase) => {
    box.motif.on("keydown", (_box, e: KeyboardEvent) => {
      if (e.key === "Escape") state.query = "";
    });

    const focusSearch = (e: KeyboardEvent) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement as HTMLElement | null)?.tagName ?? "")) return;
      e.preventDefault();
      input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    box.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  };

  return (
    <div class="search" onConfig={configured}>
      <input
        ref={input}
        type="search"
        placeholder="Notlarda ara"
        aria-label="Notlarda ara"
        autocomplete="off"
        x-model={() => state.query}
        onfocus={() => hint.motif.hide()}
        onblur={() => hint.motif.show()}
      />
      <kbd ref={hint}>/</kbd>
    </div>
  );
}
import { Component, ComponentBase, motifComponent } from "@motifx/core";

export function SearchBox(props: { state: { query: string } }) {
  const { state } = props;
  let input!: Component<HTMLInputElement>;
  let hint!: Component<HTMLElement>;

  const configured = (box: ComponentBase) => {
    box.motif.on("keydown", (_box, e: KeyboardEvent) => {
      if (e.key === "Escape") state.query = "";
    });

    const focusSearch = (e: KeyboardEvent) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement as HTMLElement | null)?.tagName ?? "")) return;
      e.preventDefault();
      input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    box.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  };

  return motifComponent("div", {
    class: "search",
    onConfig: configured,
    initializeComponent: (box: ComponentBase) => {
      const field = motifComponent("input", {
        ref: (c: Component<HTMLInputElement>) => (input = c),
        initializeComponent: (c: ComponentBase) => {
          c.attr.add({ type: "search", placeholder: "Notlarda ara", "aria-label": "Notlarda ara", autocomplete: "off" });
          c.bindings.model(() => state.query, (value: string) => (state.query = value));
          c.motif.on("focus", () => hint.motif.hide());
          c.motif.on("blur", () => hint.motif.show());
        },
      });
      const kbd = motifComponent("kbd", {
        ref: (c: Component<HTMLElement>) => (hint = c),
        initializeComponent: (c: ComponentBase) => c.setText("/"),
      });
      box.controls.add(field, kbd);
    },
  });
}
import { motifOptions, type Component } from "@motifx/core";

export const SearchBox = (props: { state: { query: string } }) => motifOptions({
  el: "div",
  input: undefined as Component<HTMLInputElement> | undefined,
  hint: undefined as Component<HTMLElement> | undefined,

  onConfigured() {
    this.class.add("search");
    this.motif.on("keydown", (_box, e: KeyboardEvent) => {
      if (e.key === "Escape") props.state.query = "";
    });

    const focusSearch = (e: KeyboardEvent) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement as HTMLElement | null)?.tagName ?? "")) return;
      e.preventDefault();
      this.input!.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  },

  view() {
    const { state } = props;
    return (
      <>
        <input
          ref={this.input}
          type="search"
          placeholder="Notlarda ara"
          aria-label="Notlarda ara"
          autocomplete="off"
          x-model={() => state.query}
          onfocus={() => this.hint!.motif.hide()}
          onblur={() => this.hint!.motif.show()}
        />
        <kbd ref={this.hint}>/</kbd>
      </>
    );
  },
});
import { Component, ComponentBase, motifComponent, motifFragment, motifOptions } from "@motifx/core";

export const SearchBox = (props: { state: { query: string } }) => motifOptions({
  el: "div",
  input: undefined as Component<HTMLInputElement> | undefined,
  hint: undefined as Component<HTMLElement> | undefined,

  onConfigured() {
    this.class.add("search");
    this.motif.on("keydown", (_box, e: KeyboardEvent) => {
      if (e.key === "Escape") props.state.query = "";
    });

    const focusSearch = (e: KeyboardEvent) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement as HTMLElement | null)?.tagName ?? "")) return;
      e.preventDefault();
      this.input!.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  },

  view() {
    const { state } = props;
    const input = motifComponent("input", {
      ref: (field: Component<HTMLInputElement>) => (this.input = field),
      initializeComponent: (field: ComponentBase) => {
        field.attr.add({ type: "search", placeholder: "Notlarda ara", "aria-label": "Notlarda ara", autocomplete: "off" });
        field.bindings.model(() => state.query, (value: string) => (state.query = value));
        field.motif.on("focus", () => this.hint!.motif.hide());
        field.motif.on("blur", () => this.hint!.motif.show());
      },
    });
    const hint = motifComponent("kbd", {
      ref: (kbd: Component<HTMLElement>) => (this.hint = kbd),
      initializeComponent: (kbd: ComponentBase) => kbd.setText("/"),
    });
    return motifFragment({ childs: [input, hint] });
  },
});
import { Component } from "@motifx/core";

export class SearchBox extends Component {
  static elementTag = "div";
  input;
  hint;

  onConfigured() {
    this.class.add("search");
  }

  onConfig() {
    this.motif.on("keydown", (_box, e) => {
      if (e.key === "Escape") this.props.state.query = "";
    });

    const focusSearch = (e) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement)?.tagName ?? "")) return;
      e.preventDefault();
      this.input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  }

  view() {
    const { state } = this.props;
    return (
      <>
        <input
          ref={this.input}
          type="search"
          placeholder="Notlarda ara"
          aria-label="Notlarda ara"
          autocomplete="off"
          x-model={() => state.query}
          onfocus={() => this.hint.motif.hide()}
          onblur={() => this.hint.motif.show()}
        />
        <kbd ref={this.hint}>/</kbd>
      </>
    );
  }
}
import { Component, motifComponent, motifFragment } from "@motifx/core";

export class SearchBox extends Component {
  static elementTag = "div";
  input;
  hint;

  onConfigured() {
    this.class.add("search");
  }

  onConfig() {
    this.motif.on("keydown", (_box, e) => {
      if (e.key === "Escape") this.props.state.query = "";
    });

    const focusSearch = (e) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement)?.tagName ?? "")) return;
      e.preventDefault();
      this.input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  }

  view() {
    const { state } = this.props;
    const input = motifComponent("input", {
      ref: (field) => (this.input = field),
      initializeComponent: (field) => {
        field.attr.add({ type: "search", placeholder: "Notlarda ara", "aria-label": "Notlarda ara", autocomplete: "off" });
        field.bindings.model(() => state.query, (value) => (state.query = value));
        field.motif.on("focus", () => this.hint.motif.hide());
        field.motif.on("blur", () => this.hint.motif.show());
      },
    });
    const hint = motifComponent("kbd", {
      ref: (kbd) => (this.hint = kbd),
      initializeComponent: (kbd) => kbd.setText("/"),
    });
    return motifFragment({ childs: [input, hint] });
  }
}
export function SearchBox(props) {
  const { state } = props;
  let input;
  let hint;

  const configured = (box) => {
    box.motif.on("keydown", (_box, e) => {
      if (e.key === "Escape") state.query = "";
    });

    const focusSearch = (e) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement)?.tagName ?? "")) return;
      e.preventDefault();
      input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    box.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  };

  return (
    <div class="search" onConfig={configured}>
      <input
        ref={input}
        type="search"
        placeholder="Notlarda ara"
        aria-label="Notlarda ara"
        autocomplete="off"
        x-model={() => state.query}
        onfocus={() => hint.motif.hide()}
        onblur={() => hint.motif.show()}
      />
      <kbd ref={hint}>/</kbd>
    </div>
  );
}
import { motifComponent } from "@motifx/core";

export function SearchBox(props) {
  const { state } = props;
  let input;
  let hint;

  const configured = (box) => {
    box.motif.on("keydown", (_box, e) => {
      if (e.key === "Escape") state.query = "";
    });

    const focusSearch = (e) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement)?.tagName ?? "")) return;
      e.preventDefault();
      input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    box.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  };

  return motifComponent("div", {
    class: "search",
    onConfig: configured,
    initializeComponent: (box) => {
      const field = motifComponent("input", {
        ref: (c) => (input = c),
        initializeComponent: (c) => {
          c.attr.add({ type: "search", placeholder: "Notlarda ara", "aria-label": "Notlarda ara", autocomplete: "off" });
          c.bindings.model(() => state.query, (value) => (state.query = value));
          c.motif.on("focus", () => hint.motif.hide());
          c.motif.on("blur", () => hint.motif.show());
        },
      });
      const kbd = motifComponent("kbd", {
        ref: (c) => (hint = c),
        initializeComponent: (c) => c.setText("/"),
      });
      box.controls.add(field, kbd);
    },
  });
}
import { motifOptions } from "@motifx/core";

export const SearchBox = (props) => motifOptions({
  el: "div",
  input: undefined,
  hint: undefined,

  onConfigured() {
    this.class.add("search");
    this.motif.on("keydown", (_box, e) => {
      if (e.key === "Escape") props.state.query = "";
    });

    const focusSearch = (e) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement)?.tagName ?? "")) return;
      e.preventDefault();
      this.input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  },

  view() {
    const { state } = props;
    return (
      <>
        <input
          ref={this.input}
          type="search"
          placeholder="Notlarda ara"
          aria-label="Notlarda ara"
          autocomplete="off"
          x-model={() => state.query}
          onfocus={() => this.hint.motif.hide()}
          onblur={() => this.hint.motif.show()}
        />
        <kbd ref={this.hint}>/</kbd>
      </>
    );
  },
});
import { motifComponent, motifFragment, motifOptions } from "@motifx/core";

export const SearchBox = (props) => motifOptions({
  el: "div",
  input: undefined,
  hint: undefined,

  onConfigured() {
    this.class.add("search");
    this.motif.on("keydown", (_box, e) => {
      if (e.key === "Escape") props.state.query = "";
    });

    const focusSearch = (e) => {
      if (e.key !== "/" || /^(INPUT|TEXTAREA)$/.test((document.activeElement)?.tagName ?? "")) return;
      e.preventDefault();
      this.input.element.focus();
    };
    document.addEventListener("keydown", focusSearch);
    this.motif.setDisposable(() => document.removeEventListener("keydown", focusSearch));
  },

  view() {
    const { state } = props;
    const input = motifComponent("input", {
      ref: (field) => (this.input = field),
      initializeComponent: (field) => {
        field.attr.add({ type: "search", placeholder: "Notlarda ara", "aria-label": "Notlarda ara", autocomplete: "off" });
        field.bindings.model(() => state.query, (value) => (state.query = value));
        field.motif.on("focus", () => this.hint.motif.hide());
        field.motif.on("blur", () => this.hint.motif.show());
      },
    });
    const hint = motifComponent("kbd", {
      ref: (kbd) => (this.hint = kbd),
      initializeComponent: (kbd) => kbd.setText("/"),
    });
    return motifFragment({ childs: [input, hint] });
  },
});

Not listesi kutuyu kendi arama durumuyla yerleştirir: <SearchBox state={this.state} />.

Not listesi kutuyu kendi arama durumuyla yerleştirir: new SearchBox({ state: this.state }).

Not listesi kutuyu kendi arama durumuyla yerleştirir: motifComponent(SearchBox, { state: this.state }).

motif.on ile eklenen dinleyiciyi bileşen kendisi bırakır; belgeye ya da window'a doğrudan eklenen dinleyiciyi ise bileşen tanımaz, onu setDisposable bırakır. Aynı kural zamanlayıcılar, gözlemciler (ResizeObserver, IntersectionObserver) ve dış kütüphanelerin kurulumları için de geçerlidir.

motif.options

Alan Anlamı
hideStrategy Gizlenen elemanın DOM'da nasıl duracağı: "auto" (varsayılan), "placeholder" ya da "detach" (bkz. Gizleme stratejisi).
disableDisposal true ise motif.clear() çocukları serbest bırakmaz.
transition Geçiş ayarı ve WAAPI ile in/out animasyonları (bkz. Stil ve Animasyon).
enableRouterClassing Yalnız yazılır: elemana geçerli adrese göre sınıf verir (bkz. Rota sınıfları).
display Okununca isVisible'ı verir; true yazmak show(), false yazmak hide() çağırır.
hasEvent(ad) O adla kaydedilmiş bir işleyici var mı.
getInstance() Bileşenin kendisi.

hideStrategy ve disableDisposal bileşen yaratılırken options prop'uyla da verilir: JSX'te options={{ hideStrategy: "detach" }}, JSX'siz yazımda props nesnesinin options anahtarı.

Sonraki adım

Bileşenin bağlarını tanımak için bindings bölümüne geçin.