Olaylar ve mesaj protokolü
Widget, iframe içinden üst sayfaya mesaj gönderir; widget.js bunları kapsayıcı öğe üzerinde kabarcıklanan (bubbling) CustomEvent’lere çevirir. Böylece sonuçları kendi arayüzünüzde kullanabilir, analitik gönderebilir veya hataları izleyebilirsiniz.
Olaylar
Bölüm başlığı “Olaylar”| Olay | detail |
Ne zaman |
|---|---|---|
mo:ready |
{ id, height, tool, title, features } |
Widget yüklendi ve çizildi (bir kez; iframe yeniden yüklenirse tekrar). |
mo:resize |
{ id, height } |
İçerik yüksekliği değişti. |
mo:input |
{ id, field, inputs } |
Kullanıcı bir girdiyi düzenledi (250 ms geciktirilmiş). Yalnızca hesaplayıcı. |
mo:result |
{ id, data: { result, breakdown }, meta, inputs, requestId } |
Başarılı bir hesaplamadan sonra. |
mo:error |
{ id, error: { code, message, fields? }, requestId } |
Hesaplama başarısız olduğunda. |
mo:select |
{ id, kind, key, title, url } |
Arama/kategori widget’ında bir araç veya kategori seçildiğinde. |
id, yükleyicinin bu widget örneğine atadığı kimliktir.mo:readyiçindetoolhesaplayıcı widget’ında{ key, version, title }, diğer türlerde (ve eski embed sayfalarında)null’dır;titleiframe’in güncel başlığıdır;featuresembed sayfasının yetenekleridir (config,inputs,calculate,reset,input-events).mo:inputiçindeinputs, form geçerliyse isteğe giden kanonik girdilerdir, değilsenull.fieldkullanıcının düzenlediği alanın adıdır.mo:resultiçindedatavemeta, hesaplama yanıtıyla aynı biçimdedir;inputshesaplamanın istek girdileridir.requestId, sonucun sizincalculate()çağrınıza yanıt olması durumunda doludur, aksi haldenull.mo:erroriçindeerror.fields, doğrulama hatalarında[{ field, reason }]listesidir.requestIdaynı mantıkla doludur.mo:select:kindtoolveyacategory,keyseçilenin anahtarı,titlebaşlığı,urlhedef adrestir. Ayrıntı: Widget türleri.
const container = document.querySelector('[data-mo-tool="kdv"]');
container.addEventListener('mo:ready', (event) => { console.log('Hazır:', event.detail.tool?.title, event.detail.features);});
container.addEventListener('mo:input', (event) => { // form geçerliyse event.detail.inputs dolu olur console.log(event.detail.field, event.detail.inputs);});
container.addEventListener('mo:result', (event) => { const { data, meta, inputs } = event.detail; console.log('Brüt tutar:', data.result.gross, '· araç sürümü', meta.toolVersion, '· girdiler', inputs);});
container.addEventListener('mo:error', (event) => { console.warn('Hesaplama hatası:', event.detail.error.code, event.detail.error.fields);});Olaylar kabarcıklandığı için sayfadaki tüm widget’ları tek yerden de dinleyebilirsiniz:
document.addEventListener('mo:result', (event) => { // event.target: ilgili kapsayıcı öğe analytics.track('calculation', { tool: event.target.dataset.moTool, id: event.detail.id });});mo:ready ve mo:resize için yükleyici iframe yüksekliğini de ayarlar (120–10000 px arası) ve son ölçülen yüksekliği hatırlar. Sizin ayrıca yükseklik yönetmenize gerek yoktur.
Hata kodları
Bölüm başlığı “Hata kodları”error.code, ya API hata kodlarından biridir (VALIDATION_ERROR, RATE_LIMITED, CALCULATION_ERROR, …) ya da API’ye ulaşılamadığında yerel bir koddur:
| Kod | Anlamı |
|---|---|
NETWORK_ERROR |
API’ye bağlanılamadı. |
TIMEOUT |
İstek zaman aşımına uğradı. |
INVALID_RESPONSE |
Yanıt beklenen sözleşmeye uymuyor. |
calculate() çağrıları ayrıca yükleyici tarafında NOT_MOUNTED, NOT_READY, UNSUPPORTED, TIMEOUT ve UNMOUNTED kodlarıyla reddedilebilir: JavaScript API.
error.message, widget’ın kullanıcıya kendisinin gösterdiği yerelleştirilmiş metindir; kendi arayüzünüzde mantığı code üzerine kurun.
postMessage protokolü (v1)
Bölüm başlığı “postMessage protokolü (v1)”Düz iframe kullanıyorsanız veya yükleyiciyi kullanmadan kendiniz dinlemek istiyorsanız, embed sayfası üst pencereye şu biçimde mesaj gönderir (targetOrigin: '*'):
{ source: 'mirac-ozdemir-widget', version: 1, id, type, ...payload }type |
Payload | Ne zaman |
|---|---|---|
ready |
{ height, tool: { key, version, title }, features: string[] } (arama/kategori için tool yerine kind) |
Hidrasyondan sonra bir kez. |
resize |
{ height } |
Belge yüksekliği her değiştiğinde (tamsayı CSS px). |
input |
{ field, inputs } |
Kullanıcı düzenlemesinden sonra (250 ms geciktirilmiş); form geçerli değilse inputs null. |
result |
{ data: { result, breakdown }, meta, inputs, requestId? } |
Her başarılı hesaplamadan sonra. requestId, bir calculate komutuna yanıt olduğunda gelir. |
error |
{ error: { code, message, fields? }, requestId? } |
Hesaplama isteği başarısız olduğunda. |
select |
{ kind: 'tool' | 'category', key, title, url } |
Yalnızca arama/kategori sayfaları. |
id, adres satırındakiidparametresinin aynen geri gönderilmiş halidir ([A-Za-z0-9_-], 1–64). Parametre verilmediysenulldır.sourceveversionalanları, zarfın değişmez parçalarıdır; yük (payload) bunları ezemez.- Yükseklik her zaman yukarı yuvarlanmış tamsayıdır (içerik kırpılmaz).
- v1 içinde yeni mesaj türleri ve yük alanları eklenebilir; bilmediklerinizi yok sayın (sürüm politikası).
Mesajları güvenle işleme
Bölüm başlığı “Mesajları güvenle işleme”window.addEventListener('message', (event) => { if (event.origin !== 'https://mirac-ozdemir.com') return; // 1) gönderen origin if (event.source !== iframe.contentWindow) return; // 2) beklediğiniz iframe const m = event.data; if (!m || m.source !== 'mirac-ozdemir-widget' || m.version !== 1) return; // 3) zarf // m.type ve yükü artık güvenle kullanılabilir});widget.js bu üç kontrolü ve ayrıca id’nin bilinen bir örneğe ait olması kuralını kendisi uygular: sayfadaki bir çerçeve, başka bir widget’ı yeniden boyutlandıramaz veya taklit edemez. Mesajların yükü yükleyici tarafından asla DOM’a eklenmez, yalnızca olay detail’i olarak iletilir. Siz de yükteki metinleri sayfanıza eklerken textContent kullanın.
Sayfanızdan iframe’e komutlar
Bölüm başlığı “Sayfanızdan iframe’e komutlar”Ters yönde (sayfanız → embed) mesajlar şu zarfı kullanır ve iframe penceresine, targetOrigin olarak site origin’iyle gönderilir:
{ source: 'mirac-ozdemir-widget-host', version: 1, id, type, ...payload }type |
Payload | Etki |
|---|---|---|
config |
{ theme?, header?, density?, accent?, radius?, view? } |
Yalnızca sunum; canlı uygulanır. |
inputs |
{ values, autorun? } |
Girdileri doldurur (i.<alan> ile aynı temizleme kuralları). |
calculate |
{ requestId } |
Formu doğrular ve hesaplar; result veya error mesajı aynı requestId ile yanıtlar. |
reset |
{} |
Formu sıfırlar. |
Embed sayfası bir komutu yalnızca event.source === window.parent, zarf geçerli ve id kendi id’sine eşitse (asla null değil) kabul eder; bilinmeyen türler yok sayılır. Hangi komutların desteklendiği ready mesajındaki features alanındadır (config, inputs, calculate, reset). Yükleyiciyi kullanıyorsanız bunları elle göndermenize gerek yoktur: update, setInputs, calculate, reset bu mesajları sizin için üretir (JavaScript API).
Örnek: sonucu kendi bileşeninizde gösterme
Bölüm başlığı “Örnek: sonucu kendi bileşeninizde gösterme”document.addEventListener('mo:result', (event) => { const { result } = event.detail.data; document.querySelector('#ozet').textContent = `Brüt: ${result.gross} TRY`;});import { useEffect, useState } from 'react';
export function useWidgetResult() { const [result, setResult] = useState<Record<string, string> | null>(null); useEffect(() => { const handler = (event: Event) => setResult((event as CustomEvent).detail.data.result); document.addEventListener('mo:result', handler); return () => document.removeEventListener('mo:result', handler); }, []); return result;}