npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mercury-fx/effector

v3.0.0

Published

Mercury Effector for mhs (mercury hyperscript) — theme store, toast model + Toaster, and a form/field factory. Components stay presentational.

Readme

@mercury-fx/effector

Effector-backed state adapters for @mercury-fx/ui: a theme store, scope-aware toast queues + <Toaster/>, form, disclosure, selection, and table models, password strength, and a cooldown timer. State lives outside React, so the components stay presentational.

Install

npm i @mercury-fx/effector
npm i @mercury-fx/ui react effector effector-react   # peer dependencies

Use

import "@mercury-fx/ui/styles.css";
import { Toaster, toast, useTheme, toggleTheme } from "@mercury-fx/effector";

function App() {
  const theme = useTheme();               // "light" | "dark"
  return (
    <>
      <button onClick={() => toggleTheme()}>{theme}</button>
      <button onClick={() => toast.success("Saved")}>Save</button>
      <Toaster />
    </>
  );
}

Controlled components with Effector scopes

Create models outside React. Bind callbacks through the model hooks inside the Effector Provider; imperative singleton helpers target the global scope. Mercury UI remains independent of Effector.

import { Dialog, Tabs } from "@mercury-fx/ui";
import { createDisclosure, createSelectionModel } from "@mercury-fx/effector";

const details = createDisclosure();
const section = createSelectionModel({ initialValue: "overview" });

function Details() {
  const dialog = details.useProps(); // open, onOpenChange, onClose
  const controls = details.useDisclosure(); // isOpen, open, close, toggle, setOpen
  const tabs = section.useProps(); // value, onChange
  return <>
    <button onClick={controls.open}>Open details</button>
    <Dialog {...dialog} title="Details">
      <Tabs {...tabs} tabs={[
        { value: "overview", label: "Overview", content: "Overview content" },
        { value: "history", label: "History", content: "History content" },
      ]} />
    </Dialog>
  </>;
}

createSelectionModel accepts an optional pure isAllowed(value) guard and exposes $value, changed, and reset. Its binding also fits Segmented and RadioGroup where their value types match. Native Select emits a DOM event, so adapt it explicitly: onChange={event => binding.onChange(event.target.value)} for a string-valued model, alongside value={binding.value}.

Table filtering, sorting, selection, and pages

import { Input, Pagination, Table } from "@mercury-fx/ui";
import { createTableModel } from "@mercury-fx/effector";

const people = createTableModel({
  rows: [{ id: "ada", name: "Ada", score: 10 }],
  getRowKey: (row) => row.id,
  pageSize: 20,
  selectionMode: "multiple",
  filter: (row, query) => row.name.toLowerCase().includes(query.toLowerCase()),
  comparators: { name: (a, b) => a.name.localeCompare(b.name), score: (a, b) => a.score - b.score },
});

function People() {
  const table = people.useTable();
  const pagination = people.usePagination();
  const query = people.useQuery();
  return <>
    <Input aria-label="Filter people" value={query.value} onChange={(event) => query.onChange(event.target.value)} />
    <Table {...table} columns={[
      { key: "name", label: "Name", sortable: true },
      { key: "score", label: "Score", sortable: true },
    ]} />
    <Pagination {...pagination} />
  </>;
}

rows accepts an array or an Effector store. rowsChanged replaces array data; for a supplied store it overrides data until that store next updates. Filtering precedes stable sorting and pagination. Sort values use ascending/descending. Pages are 1-based, clamp after data changes, and reset after query, sort, or page-size changes. Empty results retain page 1. Selection survives filtering and paging, but removed or disabled rows lose selection. Row keys must be unique and stable. A missing comparator leaves the original ordering unchanged.

The model exposes $rows, $query, $sort, $page, $pageSize, $pageCount, $total, $filteredRows, $sortedRows, $pageRows, and $selectedKeys, together with rowsChanged, queryChanged, sortChanged, selectionChanged, pageChanged, pageSizeChanged, and reset. Reset restores control defaults and clears selection; it retains current source rows.

Forms and numeric controls

createForm keeps its existing useField and useForm APIs. Their callbacks and submit effect follow the Provider scope. Submission captures that scope's values, validates, and marks fields touched. Invalid or duplicate pending submissions resolve false; successful submissions resolve true. Rejections remain visible to the caller and in $submitError / useForm().submitError. Reset clears the pending/error state and invalidates old settlements without canceling the application's underlying request.

import { MoneyInput } from "@mercury-fx/ui";
import { createForm } from "@mercury-fx/effector";

const payment = createForm({ initialValues: { amount: null as number | null } });
function Amount() {
  const amount = payment.useField("amount");
  return <MoneyInput label="Amount" numericValue={amount.value}
    onValueChange={amount.onChange} onBlur={amount.onBlur} />;
}

Notification queues

import { Toaster, createToastModel } from "@mercury-fx/effector";

const notifications = createToastModel({ duration: 5000, limit: 5 });
function Save() {
  const toast = notifications.useToast();
  return <>
    <button onClick={() => toast.success("Saved")}>Save</button>
    <Toaster model={notifications} label="Notifications" />
  </>;
}

Mount one Toaster per model and scope. Timers and IDs are isolated across forks; manual dismissal, queue eviction, clear, and dispose cancel timers. Duration 0 or Infinity keeps a notification open. Hover, focus, a blurred window, and a hidden document pause timers; elapsed time is retained and independent pause reasons must all release before timers resume. F8 focuses the labeled notification region. Mercury Alert supplies the live status/alert announcement.

createToastModel exposes $toasts, $paused, show, dismiss, clear, pause, resume, dispose, useToasts, and useToast. Call pause(reason) and resume(reason) with a stable application-specific reason for additional pause conditions. Dispose clears the queue and timers; a model can be reused afterward. The global toast, showToast, dismissToast, clearToasts, $toasts, and useToasts exports remain available. The top-level useToast binds the default queue to the active Provider scope.

License

MIT