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

sinuous-bind

v0.0.6

Published

Give your Sinuous app Svelte-like bindings!

Downloads

33

Readme

sinuous-bind

About

sinuous-bind brings the bind element directives of Svelte to Sinuous.

Don't do

`
<input value=${state} oninput=${e => state(e.target.value)} />
`

Do

`
<input bind=${state} />
`

| size | esm | cdn | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----------------------------------------------------------- | | Badge size | sinuous-bind | https://unpkg.com/sinuous-bind@latest/dist/all.min.js | | Badge size | sinuous-bind/bind | https://unpkg.com/sinuous-bind@latest/dist/bind.min.js | | Badge size | sinuous-bind/bindArea | https://unpkg.com/sinuous-bind@latest/dist/bindArea.min.js | | Badge size | sinuous-bind/bindGroup | https://unpkg.com/sinuous-bind@latest/dist/bindGroup.min.js | | Badge size | sinuous-bind/bindMedia | https://unpkg.com/sinuous-bind@latest/dist/bindMedia.min.js |

Installation

There are two ways to consume sinuous-bind:

ESM

ESM Example CodeSandbox

Run the following inside your project directory to get all the bind packages:

npm install sinuous-bind

Setup

Import all the packages into the root of your project like so:

import "sinuous-bind";

Alternatively, bring in any or all of the packages, depending on what you need:

import "sinuous-bind/bind";
import "sinuous-bind/bindGroup";
import "sinuous-bind/bindArea";
import "sinuous-bind/bindMedia";

Gotchas

These packages are not tree-shakeable. If you import the submodule, you will be stuck with it in your bundle.

ESM Example CodeSandbox

CDN

CDN Example CodeSandbox

Put this in your html to get all the packages:

<script src="https://unpkg.com/sinuous-bind@latest/dist/all.min.js"></script>

Alternatively, you can bring in just what you need (though you must include the top one):

<script src="https://unpkg.com/sinuous-bind@latest/dist/bind.min.js"></script>
<script src="https://unpkg.com/sinuous-bind@latest/dist/bindArea.min.js"></script>
<script src="https://unpkg.com/sinuous-bind@latest/dist/bindGroup.min.js"></script>
<script src="https://unpkg.com/sinuous-bind@latest/dist/bindMedia.min.js"></script>

You must also bring in Sinuous:

<script src="https://unpkg.com/sinuous/dist/all.js"></script>

Setup

If you have brought in sinuous-bind/dist/all.min.js, setup is something like what follows:

window.bindAll.enableBind(window.S);

Alternatively, if you brought in only sinuous-bind/dist/bind.min.js, setup is as follows:

window.bind.enableBind(window.S);

If you have brought in more than one package, setup is something like this:

let w = window;
let { register, enableBind } = w.bind;
register(w.bindArea.getBindArea);
register(w.bindGroup.getBindGroup);
register(w.bindMedia.getBindMedia);
enableBind(w.S);

Gotchas

Unlike the esm format, the cdn format requires the basic bind package (sinuous-bind/dist/bind.min.js) for any of the others to be enabled.

CDN Example CodeSandbox

sinuous-bind/bind

bind can take two kinds of arguments

  1. bind=${<observable>}
  2. bind=${[<observable> [, <attribute> [, <event> [, <boolean>]]]]}
    • <attribute> defaults to "value"
    • <event> defaults to "oninput"
    • <boolean>
      • Pass in true to set the attribute with a Sinuous subscribe. This is necessary for things like a contentEditable binding to textContent or innerHTML.

Generally, bind=${state} is sufficient.

bind will attempt to coerce values to numerical values, but will not attempt to coerce booleans or the empty string.

<input> (implicit)

function component() {
  const state = o("");

  return html`
    <input bind=${state} />
  `;
}

<input> (explicit)

function component() {
  const state = o("");

  return html`
    <input bind=${[state, "value", "oninput"]} />
  `;
}

<input type="range">

function component() {
  const state = o(5);

  return html`
    <input type="range" bind=${state} min="0" max="10" />
  `;
}

contentEditable

function component() {
  const state = o("");

  return html`
    <div contentEditable bind=${[state, "textContent", , true]} />
  `;
}

Takes "textContent" or "innerHTML" as the second element of the array. Requires true as the fourth element.

<input type="checkbox">

function component() {
  const state = o("");

  return html`
    <input type="checkbox" bind=${[state, "checked", "onchange"]} />
  `;
}

<input type="radio">

function component() {
  const state = o("");

  return html`
    <input type="radio" bind=${[state, "checked", "onchange"]} />
  `;
}

<select>

function component() {
  const state = o(1);

  return html`
    <select bind=${state}>
      <option>1</option>
      <option>2</option>
      <option>3</option>
    </select>
  `;
}

Gotchas: It won't work with the multiple attribute. Also, it won't sync until user action. If you need either of these things, use sinuous-bind/bindGroup

ref

function component() {
  const ref = o();

  return html`
    <div ref=${ref}></div>
  `;
}

Gotchas: The value will not be passed into the ref observable until the call to html is made. The DOM node has to be created before it can be passed to ref.

sinuous-bind/bindArea

Both bind:area and bindArea work.

All bind:area bindings are read-only.

bind:area accepts an object of observables, with any of the following keys: clientWidth, clientHeight, offsetWidth, offsetHeight, scrollLeft, scrollTop

All these keys can be updated whenever the element is resized. Additionally, scrollLeft and scrollTop are updated whenever the element is scrolled.

Effects: Set a function on the object passed to bind:area with the key effect. The effect will be called on resize and scroll. Set a function with any other key, and it will be called on resize, only.

Gotchas: bind:area sets position: relative on the bound element. Do not set the style on the element unless you set position.

single property

function component() {
  const clientWidth = o();

  return html`
    <div bind:area=${{ clientWidth }}></div>
  `;
}

multiple properties

function component() {
  const clientWidth = o();
  const clientHeight = o();
  const offsetHeight = o();

  return html`
    <div bind:area=${{ clientWidth, clientHeight, offsetHeight }}></div>
  `;
}

multiple properties with scrolling

function component() {
  const clientWidth = o();
  const clientHeight = o();
  const scrollLeft = o();
  const scrollTop = o();

  return html`
    <div
      bind:area=${{ clientWidth, clientHeight, scrollLeft, scrollTop }}
    ></div>
  `;
}

multiple properties with effects

function component() {
  const clientWidth = o();
  const arbitraryEffect = () => console.log("resizing only");
  const scrollLeft = o();
  const effect = () => console.log("resizing and/or scrolling");

  return html`
    <div
      bind:area=${{ clientWidth, arbitraryEffect, scrollLeft, effect }}
    ></div>
  `;
}

sinuous-bind/bindGroup

Both bind:group and bindGroup work.

Like bind, bind:group will attempt to coerce values to numerical values, but will not attempt to coerce booleans or the empty string.

select

function component() {
  const state = o(1);

  return html`
    <select bind:group=${state}>
      <option>1</option>
      <option>2</option>
      <option>3</option>
    </select>
  `;
}

select multiple

function component() {
  const state = o([1, 2]);

  return html`
    <select bind:group=${state} multiple>
      <option>1</option>
      <option>2</option>
      <option>3</option>
    </select>
  `;
}

radio group

function component() {
  const state = o(1);

  return html`
    <input type="radio" bind:group=${state} value="1" />
    <input type="radio" bind:group=${state} value="2" />
    <input type="radio" bind:group=${state} value="3" />
  `;
}

checkbox group

function component() {
  const state = o([1, 2]);

  return html`
    <input type="checkbox" bind:group=${state} value="1" />
    <input type="checkbox" bind:group=${state} value="2" />
    <input type="checkbox" bind:group=${state} value="3" />
  `;
}

sinuous-bind/bindMedia

Both bind:media and bindMedia work.

Read-only Properties: duration, buffered, seekable, seeking, played, ended

Read/Write Properties: currentTime, playbackRate, paused, volume

Video-only Properties videoWidth, videoHeight

video

function component() {
  const currentTime = o();
  const playbackRate = o();
  const paused = o();
  const volume = o();
  const duration = o();
  const buffered = o();
  const seekable = o();
  const seeking = o();
  const played = o();
  const ended = o();
  const videoWidth = o();
  const videoHeight = o();

  const state = {
    currentTime,
    playbackRate,
    paused,
    volume,
    duration,
    buffered,
    seekable,
    seeking,
    played,
    ended,
    videoWidth,
    videoHeight
  };

  return html`
    <video controls bind:media=${state}>
      <source src=http://video.com type=video/webm />
    </video>
  `;
}

audio

function component() {
  const currentTime = o();
  const playbackRate = o();
  const paused = o();
  const volume = o();
  const duration = o();
  const buffered = o();
  const seekable = o();
  const seeking = o();
  const played = o();
  const ended = o();

  const state = {
    currentTime,
    playbackRate,
    paused,
    volume,
    duration,
    buffered,
    seekable,
    seeking,
    played,
    ended
  };

  return html`
    <audio controls bind:media=${state}>
      <source src=http://audio.com type=audio/webm />
    </audio>
  `;
}

Acknowledgments and Thanks

Wesley Luyten

Rich Harris and the rest of the Svelte team

  • These packages are a port of much of the bind directive functionality present in the Svelte framework