Fox Events Fox Events

Casos de uso

Ideias e padrões para onde o Fox Events se encaixa: micro-frontends, wizards, dashboards, jogos, e-commerce, design systems e mais.

Micro-frontends e shells

Vários apps ou fragmentos precisam se comunicar sem acoplamento. Use um mapa de eventos compartilhado e, se precisar de isolamento, Fox.forScope.

ts
// Shell emite: "shell:navigate", "shell:theme-changed"
// Fragmento A emite: "cart:updated", "checkout:started"
// Fragmento B escuta cart:updated e atualiza o badge

type ShellEvents = {
  "shell:navigate": { path: string };
  "shell:theme-changed": { theme: "light" | "dark" };
  "cart:updated": { itemCount: number };
  "checkout:started": void;
};

Fox.on<ShellEvents>("cart:updated", (p) => updateCartBadge(p.itemCount));
Fox.emit<ShellEvents>("shell:navigate", { path: "/checkout" });

Cada micro-frontend pode usar seu próprio escopo para eventos não vazarem; o shell ainda emite eventos globais para navegação ou tema.

---

Wizards e fluxos multi-etapa

Etapas emitem conclusão; o wizard (ou um coordenador) escuta e avança. Payloads tipados deixam “resultado da etapa 2” explícito.

ts
type WizardEvents = {
  "wizard:step-complete": { stepId: string; data: Record<string, unknown> };
  "wizard:back": { fromStep: string };
  "wizard:abort": void;
};

// Form da etapa 2
Fox.emit<WizardEvents>("wizard:step-complete", {
  stepId: "payment",
  data: { method: "card", last4: "4242" },
});

// Container do wizard
Fox.on<WizardEvents>("wizard:step-complete", (p) => {
  saveStepData(p.stepId, p.data);
  goToNextStep(p.stepId);
});

Use once para “esperar até o usuário concluir a etapa” em fluxos assíncronos.

---

Dashboards em tempo real e dados ao vivo

Feeds (WebSocket, polling, SSE) emitem eventos normalizados; widgets inscrevem só no que precisam. O Trail ajuda a debugar “quem recebeu o quê”.

ts
type DashboardEvents = {
  "feed:price": { symbol: string; price: number; ts: number };
  "feed:alert": { severity: "info" | "warn" | "error"; message: string };
  "dashboard:filter-changed": { filter: string };
};

// Widget de preço
Fox.on<DashboardEvents>("feed:price", (p) => {
  if (p.symbol === "BTC") updateBtcPrice(p.price);
});

// Ao replayed ou debugar
const history = Fox.channel<DashboardEvents["feed:price"]>("feed:price").trail();
console.log(history.toText());

---

UI de jogo: placar, conquistas, níveis

A lógica do jogo emite; a camada de UI escuta e anima. Eventos tipados evitam strings mágicas e deixam payloads (ex.: delta de pontos, id da conquista) claros.

ts
type GameEvents = {
  "game:score": { delta: number; total: number };
  "game:achievement": { id: string; name: string };
  "game:level-complete": { level: number; stars: number };
  "game:over": { score: number; reason: string };
};

Fox.emit<GameEvents>("game:achievement", { id: "first_blood", name: "First Blood" });
Fox.on<GameEvents>("game:score", (p) => animateScore(p.total));
Fox.once<GameEvents>("game:level-complete").then((p) => showLevelCompleteModal(p.stars));

---

E-commerce: carrinho, checkout, estoque

O serviço de carrinho emite cart:item-added, cart:item-removed; header, mini-cart e checkout escutam. Storage opcional pode persistir eventos do carrinho para refresh/hydrate.

ts
type CartEvents = {
  "cart:item-added": { sku: string; qty: number; name: string };
  "cart:item-removed": { sku: string };
  "cart:updated": { itemCount: number; total: number };
  "checkout:started": void;
  "checkout:completed": { orderId: string };
};

Fox.emit<CartEvents>("cart:item-added", { sku: "SHIRT-1", qty: 2, name: "T-Shirt" });
Fox.on<CartEvents>("cart:updated", (p) => updateHeaderBadge(p.itemCount));

---

Design systems e toggles de tema/layout

Mudanças de tema ou layout são emitidas uma vez; todo componente interessado se inscreve. Sem prop drilling.

ts
type DesignSystemEvents = {
  "ds:theme": { theme: "light" | "dark" | "system" };
  "ds:sidebar": { open: boolean };
  "ds:density": { density: "compact" | "comfortable" };
};

Fox.emit<DesignSystemEvents>("ds:theme", { theme: "dark" });
Fox.on<DesignSystemEvents>("ds:theme", (p) => document.documentElement.setAttribute("data-theme", p.theme));

---

Híbrido React Native + WebView

RN envia comandos para a WebView; o app web emite “ready”, “login”, etc. de volta. Mesmos nomes e tipos de evento nos dois lados. Configure o bridge no web com createReactNativeBridge e no RN com createFoxRNAdapter + onMessage / inject da WebView.

1. Web (dentro da WebView) — configurar bridge, ouvir comandos do RN, emitir para o RN

ts
import { Fox } from "fox-events";
import { createReactNativeBridge } from "fox-events/bridge-react-native";

createReactNativeBridge({ direction: "both", filter: (n) => n.startsWith("app:") });

Fox.on("app:command", (payload) => {
  if (payload.action === "reload") window.location.reload();
  if (payload.action === "back") history.back();
});

Fox.emit("app:ready", { version: "1.0" });
Fox.emit("app:login", { userId: "u-1", email: "user@example.com" });

2. React Native — adapter + WebView, enviar comandos, ouvir eventos da web

ts
import { useRef, useMemo } from "react";
import { WebView } from "react-native-webview";
import { createFoxRNAdapter } from "fox-events/bridge-react-native";

function App() {
  const webViewRef = useRef<WebView>(null);
  const fox = useMemo(
    () =>
      createFoxRNAdapter({
        sendToWebView: (msg) => {
          const code = `window.postMessage(${JSON.stringify(msg)}, '*'); true;`;
          webViewRef.current?.injectJavaScript(code);
        },
        filter: (name) => name.startsWith("app:"),
      }),
    []
  );

  return (
    <WebView
      ref={webViewRef}
      source={{ uri: "https://example.com" }}
      onMessage={(e) => fox.handleMessage(e.nativeEvent.data)}
    />
  );
}

// Uso: enviar comando para a web, ouvir eventos da web
fox.emit("app:command", { action: "reload" });
fox.on("app:ready", (p) => setWebReady(true));
fox.on("app:login", (p) => setUser(p.userId, p.email));
const loginPayload = await fox.once("app:login");

Defina um mapa de eventos e use nos dois ambientes para um contrato consistente.

---

Analytics e trilha de auditoria

Emita toda ação relevante como evento; um listener envia para seu analytics ou log de auditoria. O Trail dá um rastro local para debug.

ts
type AuditEvents = {
  "audit:action": { action: string; payload: unknown; userId: string; ts: number };
};

Fox.on<AuditEvents>("audit:action", (p) => sendToAnalytics(p));
// Qualquer feature
Fox.emit<AuditEvents>("audit:action", {
  action: "checkout.completed",
  payload: { orderId: "123" },
  userId: "u-1",
  ts: Date.now(),
});

---

Lifecycle de plugins e extensões

O app host emite “app:ready”, “route:changed”, “user:logged-in”; plugins se inscrevem e rodam sua lógica. Eventos tipados deixam o contrato claro para código de terceiros.

ts
type HostEvents = {
  "app:ready": void;
  "route:changed": { path: string; params: Record<string, string> };
  "user:logged-in": { userId: string; role: string };
};

// Plugin
Fox.on<HostEvents>("route:changed", (p) => {
  if (p.path.startsWith("/admin")) loadAdminPlugin();
});

---

A/B tests e feature flags

Emita “experiment:exposed” ou “feature:checked” com id da variante; analytics ou logging escutam. Sem precisar passar flags por todo componente.

ts
type ExperimentEvents = {
  "experiment:exposed": { experimentId: string; variant: string };
  "feature:checked": { name: string; enabled: boolean };
};

Fox.emit<ExperimentEvents>("experiment:exposed", { experimentId: "checkout-v2", variant: "B" });
Fox.on<ExperimentEvents>("experiment:exposed", (p) => trackExposure(p.experimentId, p.variant));

---

Notificações e toasts

Um único canal para “mostrar toast”; qualquer parte do app emite, o componente de notificação escuta e enfileira.

ts
const toasts = Fox.channel<{ id: string; message: string; type: "info" | "success" | "error"; duration?: number }>("ui:toast");

toasts.emit({ id: crypto.randomUUID(), message: "Salvo!", type: "success", duration: 3000 });
toasts.on((t) => toastQueue.add(t));

---

Undo / redo e histórico de comandos

Emita comandos como eventos; um handler central empilha no histórico e aplica ações inversas. O Trail pode complementar para debug.

ts
type CommandEvents = {
  "command:execute": { type: string; payload: unknown; undo: () => void };
};

Fox.on<CommandEvents>("command:execute", (p) => {
  commandHistory.push(p);
  p.undo(); // ou apply, depois guardar undo
});
Fox.emit<CommandEvents>("command:execute", {
  type: "rename",
  payload: { id: "doc-1", name: "Novo Nome" },
  undo: () => revertRename("doc-1"),
});

---

Escolha os eventos e payloads que combinam com seu domínio; use foxes.ts para mantê-los em um lugar e tipados de ponta a ponta.