Primitives for building user-friendly controls with SolidJS.

Overview

solid-knobs

npm version

This library provides a set of utilities for building user-friendly controls for audio parameters and the like. The utilities come without any visual styling, they only try to make setting up commonly needed features and behaviours easier. The included JSX components are written for SolidJS and have no external dependencies.

Demo: https://tahti-studio.github.io/solid-knobs/

Install

npm install solid-knobs

Docs

Below is a reference of all the different utilities provided by solid-knobs. However, to get a feel for how they're actually used, please take a look at the examples. The examples are hosted at: https://tahti-studio.github.io/solid-knobs/

Ranges

A Range specifies the range, scale and UI behaviours of a controlled value. Currently there are two types of Range:

ContinousRange

The most common type of range used for numerical values that are continous (e.g. frequency, volume etc).

interface ContinuousRange {
  // The type of a ContinuousRange is always RangeType.Continuous.
  type: RangeType.Continuous,

  // The start value of the range.
  start: number,

  // The end value of the range.
  end: number,

  // Whether the range is a bipolar one.
  bipolar?: boolean,

  // A function for converting a value with a certain unit to a value in the range.
  stringToValue?: (value: number, unit: string) => number,

  // A function for formatting a value of this range as a string.
  valueToString?: (value: number) => string,

  // The scaling of the range defines how the value is controlled in a UI.
  scale?: {
    type: Scale.Exponential,
    exp: number
  } | {
    type: Scale.Logarithmic
  } | {
    type: Scale.Linear // this is the default
  },
  
  // The values to which the user-interface control should snap.
  // Note! Always provide these values from smallest to largest.
  snap?: number[] | number,

  // How far away a value has to be from a snap point for it to snap.
  snapMargin?: number,

  // The step between values. If you want a range that allows only integer values, set this to 1.
  step?: number
}

ChoiceRange

A range that represents a finite number of choices with textual labels (think enums).

interface ChoiceRange {
  // The type of a ChoiceRange is always RangeType.Choice.
  type: RangeType.Choice,

  // A list of choices that the range contains (see Choice below).
  choices: Choice[]
}

interface Choice {
  // The numerical value of the choice.
  value: number,

  // The textual (user-friendly) representation of the choice.
  label: string,

  // Additional free-form data.
  data?: unknown
}

A couple of helpers are also exported for creating common ranges:

const createFrequencyRange: (start?: number, end?: number) => ContinuousRange;
const createVolumeRange: (start?: number, end?: number) => ContinuousRange;
const createPercentageRange: (start?: number, end?: number) => ContinuousRange;
const createBipolarPercentageRange: (start?: number, end?: number) => ContinuousRange;
const createAccuratePercentageRange: (start?: number, end?: number) => ContinuousRange;
const createToggleRange: (offLabel?: string, onLabel?: string) => ChoiceRange;

solid-knobs also exports a set of functions as rangeFunctions for working with ranges. These functions are:

/**
 * Converts `value` to a normalised value (ranging from 0 to 1) and returns it.
 */
toNormalised(range: Range, value: number): number;

/**
 * Converts a normalised `value` (ranging from 0 to 1) to it's natural range and returns it.
 */
fromNormalised(range: Range, normalisedValue: number): number;

/**
 * Parses `value` from a value and a unit and returns the value as a number.
 */
fromString(range: Range, value: number, unit: string): number;

/**
 * Converts an unnormalised `value` to a user-friendly string representation.
 */
toString(range: Range, value: number): string;

/**
 * Snaps an unnormalised `value` to the closest legal value.
 */
snap(range: Range, value: number): number;

/**
 * Returns a random un-normalised value.
 */
getRandom(range: Range): number;

/**
 * Limits an un-normalised value to be within the range.
 */
limit(range: Range, value: number): number;

/**
 * Nudges the un-normalised `value` by `steps`.
 */
nudge(range: Range, value: number, steps: number): number;

getStart(range: Range): number;
getEnd(range: Range): number;

The range utilities do not depend on SolidJS and they can be imported separately from solid-knobs/range.

Usage:

import { ContinousRange, ChoiceRange, Scale, RangeType, createVolumeRange } from 'solid-knobs';

const frequencyRange: ContinousRange = {
  type: RangeType.Continuous,
  start: 20,
  end: 20000,
  scale: { type: Scale.Logarithmic },
  toString: v => `${v.toPrecision(2)} Hz`
};

const filterTypeRange: ChoiceRange = {
  type: RangeType.Choice,
  choices: [
    { value: 0, label: 'Low-pass' }
    { value: 1, label: 'High-pass' },
    { value: 2, label: 'Band-pass' }
  ]
};

const volumeRange = createVolumeRange(0, 1.5);

ParameterGestureHandler (SolidJS component)

The ParameterGestureHandler component doesn't render anything itself, it simply wraps an existing element and makes it behave like a control by giving it the following abilities:

  • click and drag the element up/down to change the value
  • scroll on top of the element to change the value
  • hold shift while changing the value to change it more precisely
  • after focusing the element, the up/down/left/right arrow keys can be used to nudge the value by different increments

It also takes care of blocking user-selection on the page while dragging.

interface ParameterGestureHandlerProps {
  children: (ref: any) => any;

  // The un-normalised value.
  value: number,

  // Called with the un-normalised value.
  onChange?: (value: number) => void,

  // Called when starting the change gesture.
  onStart?: (value: number) => void,

  // Called when ending the change gesture.
  onEnd?: (value: number) => void,

  // The range of the value.
  range: Range,

  // The relative speed of the change gesture. The default is 1.
  speed?: number,

  // Whether the cursor should be hidden while changing the value.
  // Note! This might result in constant annoying pop-ups in certain browsers.
  hideCursor?: boolean
}

Usage:

import { ParameterGestureHandler } from 'solid-knobs';
<ParameterGestureHandler {...props}>
  {ref =>
    <div ref={ref}>
      // custom visualisation code
    </div>
  }
</ParameterGestureHandler>

Control (SolidJS component)

The Control component implements a higher level control that covers most common use-cases and takes care of accessibility. It uses ParameterGestureHandler under the hood and you should probably use Control instead of ParameterGestureHandler for most cases.

// Note that ControlProps extends the ParameterGestureHandlerProps defined above.
type ControlProps =
Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onChange'> &
Omit<ParameterGestureHandlerProps, 'children'> &
{
  // The label that should be used for the aria label (for accessibility).
  label?: string;

  // The default value.
  defaultValue?: number;

  // Called when starting the change gesture.
  onGestureStart?(e: MouseEvent | TouchEvent): void;

  // Called when ending the change gesture.
  onGestureEnd?(e: MouseEvent | TouchEvent): void;

  children: any;
}

Usage:

import { Control } from 'solid-knobs';
<Control range={range} value={value()} onChange={setValue}>
  // your custom control visualisation goes here
</Control>

ImageStripControl (SolidJS component)

Builds on top of the Control component, bringing easy-to-use support for image strip control graphics as generated by e.g. KnobMan.

export type ImageStripControlProps = Omit<ControlProps, 'children'> & {
  // The path to the image strip to use.
  imageSrc: string,

  // The number of frames in the image strip.
  numFrames: number,

  // Set to true if the image strip is laid out horizontally. Will be interpreted as vertical otherwise.
  horizontal?: boolean
}

ValueInput (SolidJS component)

A glorified input element that formats the value according to a range and properly handles user input.

interface ValueInputProps extends Omit<JSX.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> {
  // Called when the value is changed by the user.
  onChange(value: number): void;

  // The range of the value.
  range: Range;

  // The un-normalised value.
  value: number;
}

Arc (SolidJS component)

Arc is a simple utility component that returns a <path /> element for drawing an arc segment in an SVG element.

interface ArcProps extends JSX.PathSVGAttributes<SVGPathElement> {
  // The X coordinate of the arc's center.
  x: number;

  // The Y coordinate of the arc's center.
  y: number;

  // The radius of the arc.
  radius: number;

  // The start angle of the arc in degrees.
  startAngle: number;

  // The end angle of the arc in degrees.
  endAngle: number;
}

createSmoothedValue(value: () => number, speed: number = 1)

A utility function that smoothly animates the changes of a numerical value.

Usage:

const [value, setValue] = createSignal(0.5);

const smoothedValue = createSmoothedValue(value);

Contributing

Contributions are most welcome!

You might also like...

This project is a user friendly project that provides the accurate constellation name of the user

This project is a user friendly project that provides the accurate constellation name of the user

OVERVIEW: This is a group project by certain members of the 100Devs community. Contributors Lee Gill | GitHub: LeeGee64 | Twitter: @LeeGee64 | LinkedI

Jun 15, 2022

Template for building SolidJS web application with Tailwind CSS faster!

Template for building SolidJS web application with Tailwind CSS faster!

Hardtail Template for building SolidJS web application with Tailwind CSS faster! Setup First you could clone or fork this project or repository to you

Sep 18, 2022

A lightweight SolidJS component for building interactive node-based diagrams and graphs.

Convert Your Ideas To Graphs With Solid Graph! Solid Graph A lightweight and minimal Solid component for building interactive graphs and node-based ed

Dec 8, 2022

A lightweight SolidJS component for building interactive node-based diagrams and graphs.

Convert Your Ideas To A Simple And Excitig Journay With Odysea! Odysea A lightweight and minimal Solid component for building interactive graphs and n

Aug 15, 2022

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

downshift 🏎 Primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components. Read the docs | See

Dec 28, 2022

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

downshift 🏎 Primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components. Read the docs | See

Jan 1, 2023

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅 Looking for v5? The master branch is un

Dec 31, 2022

A simple React App that plays Public Domain TV shows and integrates with Twitch Chat for the controls.

Public Domain Television for Twitch This application plays movies from Public Domain Movies The current version is only used to play Cartoons, but can

Mar 29, 2022

Solid.js library adding signaling to built-in non-primitives

Solid.js library adding signaling to built-in non-primitives

This package provides signaled versions of Javascript's built-in objects. Thanks to it, all theirs properties will be automatically tracked while using standard API.

Dec 29, 2022

Provides Lock and RwLock synchronization primitives.

Lock Provides Lock and RWLock (read write lock) synchronization primitives for protecting in-memory state across multiple tasks and/or microtasks. Ins

Apr 27, 2022

Immersive (VR) controls for Three.js

Three.js Immersive Controls Immersive (VR) functionality Movement and rotation with VR controllers (move with left thumbstick, rotate with right thumb

Nov 12, 2022

Access the Nintendo Switch Online and Nintendo Switch Parental Controls app APIs.

Access the Nintendo Switch Online and Nintendo Switch Parental Controls app APIs.

Access the Nintendo Switch Online and Nintendo Switch Parental Controls app APIs. Includes Discord Rich Presence, friend notifications and data downloads.

Jan 6, 2023

The Power CAT code components are a set of Power Apps component framework (PCF) controls that can be used to enhance power apps.

The Power CAT code components are a set of Power Apps component framework (PCF) controls that can be used to enhance power apps.

Power CAT code components The Power CAT code components are a set of Power Apps component framework (PCF) controls that can be used to enhance power a

Jan 2, 2023

A jQuery plugin to make your form controls look how you want them to. Now with HTML-5 attributes!

(jQuery) Uniform A jQuery plugin to make your form controls look how you want them to. Now with HTML-5 attributes! Works well with jQuery 1.6+, but we

Jan 2, 2023

Cross platform UI controls for progressive web and hybrid apps (plain JS, jQuery, Angular and React)

Cross platform UI controls for progressive web and hybrid apps (plain JS, jQuery, Angular and React)

Mobiscroll What is Mobiscroll? Mobiscroll is a UI library for progressive webapps and hybrid development. Created with a lot of attention to usability

Dec 31, 2022

Block Library Primitives by Pew Research Center

A starting point for anyone looking to add functionality, extra control to and or create your own custom block library using core/blocks. Built with easier extensibility in mind

Nov 5, 2022

Flappy Bird is an arcade-style game in which the player controls the bird Faby, which moves persistently to the right

Flappy Bird is an arcade-style game in which the player controls the bird Faby, which moves persistently to the right

Flappy Bird is an arcade-style game in which the player controls the bird Faby, which moves persistently to the right. The player is tasked with navigating Faby through pairs of pipes that have equally sized gaps placed at random heights. Faby automatically descends and only ascends when the player taps the touchscreen.

Aug 16, 2022

📃 Typed primitives for Typescript to work with file paths

typed-file-system-path typed-file-system-path takes inspiration from Path.swift in swift-tools-support-core and provides typed primitives to work with

Dec 15, 2022

Exposes theming options available in Joy UI by providing color palette and typography controls.

Joy Theme Creator Note: Joy UI is currently in alpha - some things may not be finished or working as expected. This project exposes the theming option

Dec 28, 2022
Comments
  • Package not working with solid starter template

    Package not working with solid starter template

    Hello, the package looks very nice and just like what i currently need, but i had some problems when trying to use it. Even in the most basic solid app i get a strange error that says Uncaught ReferenceError: React is not defined as soon as i try to instantiate a <Control> Component. This is a mystery to me as nowhere in the project react is used. The error is then pointing to a tsx file that does not exist: Could not load content for http://localhost:5173/node_modules/solid-knobs/src/Control.tsx – the folder in question just contains a dist directory with code in it, no src. I briefly tried to look into it myself, but quickly found out that i have no idea how npm packages are actually built and distributed.

    Steps to reproduce:

    1. Create a new solid app with: npx degit solidjs/templates/ts solid-knobs-test-app
    2. cd solid-knobs-test-app && npm install solid-knobs
    3. Add import import { Control } from "solid-knobs" in App.tsx
    4. Add a minimal <Control range={...} ...>...</Control> component
    5. Run npm run dev
    bug 
    opened by eulervoid 2
  • Nudging is broken

    Nudging is broken

    After the latest refactoring the nudging doesn't work anymore.

    The ParameterGestureHandler is no longer calling onChange in the keyhandler, just onNudge, but Control is not applying the nudged value by calling onChange itself.

    bug 
    opened by zypus 1
  • Remove onClick triggering onGestureStart in Control component

    Remove onClick triggering onGestureStart in Control component

    Having the onClick event fire the onGestureStart handler leads to unexpected behaviour. When just clicking the knob without moving the mouse in between, onGestureStart gets fired twice and onGestureEnd only once. I'd suggest removing the onClick to get the expected result, unless there is some other reason to keep it i am not aware of?

    opened by eulervoid 0
Releases(v0.5.2)
  • v0.5.2(Dec 4, 2022)

  • v0.5.1(Nov 4, 2022)

  • v0.5.0(Nov 4, 2022)

    • breaking change: all logic regarding ranges is now moved from <ParameterGestureHandler /> to <Control />. this allows using ParameterGestureHandler without the built-in range type.
    • breaking change: rename the onStart and onEnd props on ParameterGestureHandler to onGestureStart and onGestureEnd

    Full Changelog: https://github.com/tahti-studio/solid-knobs/compare/v0.4.5...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.4.5(Oct 11, 2022)

    • fix stepped ranges not respecting range bounds
    • add touchend event listener to trigger onGestureEnd (thanks @eulervoid!)
    • improve string to value conversion for choice ranges
    • avoid selecting <ValueInput> text when releasing the mouse on top the element when it was pressed down elsewhere

    Full Changelog: https://github.com/tahti-studio/solid-knobs/compare/v0.4.4...v0.4.5

    Source code(tar.gz)
    Source code(zip)
  • v0.4.3(Aug 31, 2022)

    • the getStart and getEnd functions now work properly with choice ranges

    Full Changelog: https://github.com/tahti-studio/solid-knobs/compare/v0.4.2...v0.4.3

    Source code(tar.gz)
    Source code(zip)
  • v0.4.2(Aug 28, 2022)

    • fix scroll wheel doing nothing when used on controls with stepped ranges
    • improve parsing values from <ValueInput>

    Full Changelog: https://github.com/tahti-studio/solid-knobs/compare/v0.4.0...v0.4.2

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Aug 22, 2022)

    • breaking change: remove duplicate onClick triggering onGestureStart in Control component (thanks @eulervoid!)
    • handle selecting all text in the ValueInput component more robustly

    Full Changelog: https://github.com/tahti-studio/solid-knobs/compare/v0.3.1...v0.4.0

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Aug 21, 2022)

  • v0.3.0(Jul 7, 2022)

    • BREAKING: helper functions for creating ranges are now grouped in the rangeCreators namespace
    • move all documentation directly in the source as TSDoc and generate the Markdown reference automatically
    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Jul 4, 2022)

    • improve the performance of createSmoothedValue() and add an optional argument for defining a threshold for when the animation should be stopped

    Full Changelog: https://github.com/tahti-studio/solid-knobs/compare/v0.2.0...v0.2.1

    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Jun 28, 2022)

    • new: easily create knobs from image strips using <ImageStripControl />
    • new: helper function for creating smoothly animated movement: createSmoothedValue(value: () => number, speed = 1)
    • fix: the value jumped when pressing shift while already dragging a control
    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Jun 19, 2022)

  • v0.1.0(Jun 14, 2022)

A simple & easy2use API for obtaining information about a discord user, discord bot or discord guild and their use for some purpose on websites!

discord-web-api A simple & easy2use API for obtaining information about a discord user, discord bot or discord guild and their use for some purpose on

InvalidLenni 2 Jun 28, 2022
World first fully customizeble user bot for Whatsapp 😎 With enjoyable ththings and fun to use Whatsapp ✨️

⚡ The Alpha-X Whatsapp User Bot ?? ~ ᴡᴏʀʟᴅ ꜰɪʀꜱᴛ ꜰᴜʟʟ ᴄᴜꜱᴛᴏᴍɪᴢᴀʙʟᴇ ᴜꜱᴇʀ ʙᴏᴛ ꜰᴏʀ ᴡʜᴀᴛꜱᴀᴘᴘ ~ ?? What is Alpha-X ?? Alpha-X , is a WhatsApp helper bot wr

Official Alpha-X-Team Account 17 Jul 29, 2022
An Betterdiscord plugin that gives the capability to the user send all discord stickers, expect wumpus default stickers.

allstickersexpectwumpusstickers An Betterdiscord plugin that gives the capability to the user send all discord stickers, expect wumpus default sticker

null 2 May 23, 2022
Development of a landing page where the user can see information about my learnings in the first month at Trybe Course.

Project: Lessons Learned Lessons Learned was the first project developed by me while studying in Trybe. Technologies and tools used HTML CSS Project o

Ádran Farias Carnavale 1 Feb 12, 2022
Gitlift Discord Bot is a discord bot which is listening Discord Messages to reply with user gitlift profile and total contributions.

Remoklify - Gitlift Discord Bot Gitlift Discord Bot is a discord bot which is listening Discord Messages to reply with user gitlift profile and total

Remoklify 3 Mar 20, 2022
Page which displays online Lanyard users out of total, and has watched user cards.

Lanyard Online Users Displays usage of Lanyard profile cards. Working on moving the card to a drop-in react component and then will soon be making an

Dustin Rouillard 9 Oct 5, 2022
A library of high-quality primitives that help you build accessible user interfaces with SolidJS.

Solid Aria A library of high-quality primitives that help you build accessible user interfaces with SolidJS. Primitives @solid-aria/primitives - Expor

SolidJS Community 205 Jan 7, 2023
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023