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

@reventlessdev/reventless-ui

v3.0.0-alpha.32

Published

Reventless UI components

Downloads

717

Readme

Reventless UI

This package provides react components specific to Reventless.

Publish surface

@reventlessdev/reventless-ui is published publicly to npmjs as a JavaScript-only bundle. The tarball ships exactly dist/index.mjs (the vite library build) — no ReScript sources. The repository itself stays private; only the compiled bundle is public. ReScript sources (*.res(i), rescript.json) are deliberately not shipped, so the package is not consumable as a rescript.json#bs-dependency; import the JS bundle (import … from "@reventlessdev/reventless-ui") instead. The bundle inlines its source-only ReScript dependencies (@reventlessdev/reventless-routes, bs-css, @rescript/react, etc.); the externalized runtime deps (React, MUI, Emotion, relay, RJSF, aws-amplify) must be provided by the consumer.

Core

Updating the Graphql Schema

  • install jq with your package manager (e.g. brew for MacOs or Pamac for Manjaro)
  • Run the following command: (NOTE*: this currently only works on Linux!!! MacOs seems to not handle xargs -I{} correctly)
    curl -X POST -d '{"AuthParameters" : {"USERNAME" : "Christoph", "PASSWORD" : "Example!14"}, "AuthFlow" : "USER_PASSWORD_AUTH", "ClientId" : "3gq0vg7bc51uojsskh8co5bsa8"}' \
    -H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' \
    -H 'Content-Type: application/x-amz-json-1.1' \
    https://cognito-idp.eu-west-1.amazonaws.com/ \
    | jq '.AuthenticationResult.AccessToken' \
    | xargs -I{} npx get-graphql-schema https://smpxbyzqczgjdmm7qseogyerbq.appsync-api.eu-west-1.amazonaws.com/graphql -h "Authorization={}" -j \
    > reventless-core-schema.json
  • Build the project: npm run build
  • Follow the compiler errors until everything cleanly builds again

*Note for MacOs

Until we find a oneliner for MacOs we can achieve the same by copy+pasting the token manually. In this case jq is not necessary, if you go through the authentication result yourself to find the AccessToken.

  • run:
  curl -X POST -d '{"AuthParameters" : {"USERNAME" : "Christoph", "PASSWORD" : "Example!14"}, "AuthFlow" : "USER_PASSWORD_AUTH", "ClientId" : "3gq0vg7bc51uojsskh8co5bsa8"}' \
  -H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' \
  -H 'Content-Type: application/x-amz-json-1.1' \
  https://cognito-idp.eu-west-1.amazonaws.com/ \
  | jq '.AuthenticationResult.AccessToken'
  • copy the returned AccessToken
  • run while replacing <ACCESSTOKEN> with the copied string from before:
  npx get-graphql-schema https://smpxbyzqczgjdmm7qseogyerbq.appsync-api.eu-west-1.amazonaws.com/graphql -h "Authorization=<ACCESSTOKEN>" -j \
  > reventless-core-schema.json

Generic Components

This project aims to provide the most commonly types of components in a generic way.

Currently based on Material-UI and bs-material-ui.

Concepts

  • A component shall do only one thing well. (see Unix Philosphy)
  • For common logic Hooks shall be used.
  • Components should not assume specific types for data to be displayed (only common, shared types)
  • Use Storybook to showcase and test components in isolation.

What's included in this project?

Components

Hooks

Usage

  • Generate a new Personal Token on GitHub: Settings > Developer Settings > Personal access tokens > generate new token
  • install reventless-react by adding the follwing line to your dependencies in package.json:
"reventless-react": "git+https://[INSERT PERSONAL TOKEN HERE]:[email protected]/Reventless-Universe/reventless-react.git"
  • add reventless-react to your dependencies in bsconfig.json
  • run npm install

Components

Table

A table which is sortable and pagable, displaying an array of records.

Usage of Table
type person = {
  firstName: string,
  lastName: string,
  avatarCredentials: string,
};

let data: array(person) = ...;
let columns =
  Table.(
    [|
      makeColumnDescriptor(
        ~header="First Name"->React.string,
        ~value=person => person.firstName,
        ~render=firstName => React.string(firstName),
        ~compare=Compare.str,
        (),
      ),
      makeColumnDescriptor(
        ~header="Full Name"->React.string,
        ~value=person => (person.firstName, person.lastName),
        ~render=((first, last)) => React.string(first ++ " " ++ last),
        ~compare=((f1, l1), (f2, l2)) => Compare.str(f1 ++ l1, f2 ++ l2),
        (),
      ),
      makeColumnDescriptor(
        ~value=person => person.avatarCredentials,
        ~render=avatar => <MaterialUi_Avatar> avatar </MaterialUi_Avatar>,
        ~header=React.string("Avatar"),
        (),
      ),
      makeColumnDescriptor(
        ~value=person => person.firstName ++ person.lastName,
        ~render=
          id =>
            <button onClick={_ => Js.log2("edit", id)}>
              {React.string("Edit")}
            </button>,
        ~header=React.null,
        (),
      ),
    |]
  );

<Table
      data
      columns
      calculateKey={person => person.firstName ++ person.lastName}
      />

FilterBox

A box with customizable input fields, which can be used to filter generic data by a set of predicate functions. The FilterBox component is ideally used in combination with the Table component or any other type of table.

Usage of FilterBox
type person = {
  firstName: string,
  lastName: string,
  age: int,
};

let contains = (base, searchValue) => {
  base
  ->Js.String.toLowerCase
  ->Js.String.includes(searchValue->Js.String.toLowerCase, _);
};

let data: array(person) = ...;

// Initialize the fields the FilterBox should contain, to filter the data. Available fields are "Text", "Int", "Float", "Range", and "Dropdown".
let fields =
  FilterBox.(
    [|
      Text({
        name: "First Name",
        size: `V6,
        defaultValue: "",
        options: (),
        filter: (person, searchValue) =>
          person.firstName->contains(searchValue),
      }),
      Dropdown({
        name: "Last Name",
        size: `V6,
        defaultValue: "",
        options: {
          options: [|"Berger", "Krankl", "Wolf"|],
        },
        filter: (person, searchValue) => person.lastName == searchValue,
      }),
      Range({
        name: "Age",
        size: `V6,
        defaultValue: (0, 100),
        options: {
          min: 0,
          max: 100,
        },
        filter: (person, searchValue) => {
          let (min, max) = searchValue;
          person.age >= min && person.age <= max;
        },
      }),
    |]
  );

let (filteredData, setFilteredData) = React.useState(() => data);

<>
  <FilterBox data fields setFilteredData />
  // For example: The FilterBox can be used in combination with the Table or any other component, as long as a React state is used.
  <Table calculateKey columns=userColumns data=filteredData />
</>;

JsonSchemaForm

A generic form which can be customized using JSON-Schema. The data as well as the UI are defined in seperate JSON-Schema configurations.

Usage of JsonSchemaForm
let schema = {j|
  {
    "type": "object",
    "title": "Person",
    "properties": {
      "firstName": {
        "title": "First Name",
        "type": "string"
      },
      "lastName": {
        "title": "Last Name",
        "type": "string"
      },
      "age": {
        "title": "Age",
        "type": "number"
      }
    }
  }
|j}->Js.Json.parseExn;

let uiSchema = {j|
  {
    "firstName": {
      "ui:autofocus": true,
      "ui:emptyValue": "",
      "ui:autocomplete": "given-name"
    },
    "lastName": {
      "ui:emptyValue": "",
      "ui:autocomplete": "family-name"
    },
    "age": {
      "ui:widget": "updown"
    }
  }
|j}->Js.Json.parseExn;

<JsonSchemaForm schema uiSchema />

Details

A generic view to display key,value pairs.

Usage of Details
type data = { name: string, age: int, children: array(string), avatarUrl: string};

let data = {
  name: "Max",
  age: 42,
  children: [| "R2D2", "C3PO" |],
  avatarUrl: "https://pbs.twimg.com/profile_images/747517697034952704/gHvVahDG.jpg"
  };

let header = "Details of " ++ data.name;

let toFields = data => {
    open Details.Field;
    fields()
    ->text("Name", data.name)
    ->text("Age", data.age->Js.Int.toString)
    ->list("Children", data.kids, "No known children")
    ->element("Avatar", <img width="100" src={data.avatarUrl} />)
  };

<Details data toFields header />

Hooks

SortedArrayHook

This hook takes an array of any data, an array of comparators, optionally some default values and returns a new sorted array as well as functions to modify the sorting behaviour.

Usage of SortedArrayHook

Currently only sortinSources of type Local(...) get sorted. Remote(...) isn't implemented yet.

Tip: The Compare module holds some comperators for the most common types (string, int, float).

type person = {
  firstName: string,
  lastName: string,
  age: int,
  country: string,
  avatarCredentials: string,
};

let data: array(person) = ...;
let comparators:
  array(source(('data, 'data) => int, remoteQuery('data))) = [|
  Local((a, b) => Compare.str(b.firstName, a.firstName)),
  Local((a, b) => Compare.str(b.lastName, a.lastName)),
  Local((a, b) => Compare.int(b.age, a.age)),
|];
let defaultSortIndex: int = 0;
let defaultSortDirection: SortedArrayHook.sortDirection = Asc;

let (result, setSortedColumn, sortColumn, sortDirection) =
  SortedArrayHook.use(data, comparators, defaultSortIndex, defaultSortDirection);

PagedArrayHook

This hook takes an array of any data, optionally some default values and returns a new array containing only a subset of the default data according to the pagination settings used together with functions to modify the pagination and some status data.

Usage of PagedArrayHook
let data: array('a) = ...;
let defaultPage: int = 1;
let defaultRowsPerPage: int = 25;

let (data, setPage, setRowsPerPage, currentPage, rowsPerPage, totalRows) =
    PagedArrayHook.use(
      data,
      defaultPage,
      defaultRowsPerPage,
    );

/* to jump to the next page */
setPage(currentPage+1);

/* to jump to the middle of all records in the current paging options */
setPage(totalRows / rowsPerPage / 2);

/* to display half of all datasets */
setRowsPerPage(totalRows / 2);

FilteredArrayHook

This hook can be used to filter an array of a generic type by a set of predicate functions.

Usage of FilteredArrayHook
type person = {
  firstName: string,
  lastName: string,
  avatarCredentials: string,
};

let data: array(person) = ...;
let filters = [|
  person => person.firstName == "Jonathan",
  person => person.lastName == "Myers",
  person => person.age <= 100 && person.age >= 0,
|];

let filteredData = FilteredArrayHook.use(data, ~filters);