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 🙏

© 2024 – Pkg Stats / Ryan Hefner

parvis

v0.1.4

Published

Light library for user interface

Downloads

8

Readme

parvis

Light framework for user interfaces.

Mini virtual DOM, rich features for components, lifecycle hooks, total typing.

npm install parvis

Usage

with jsx

  1. Add in tsconfig.json settings for jsx "jsxImportSource": "parvis", for example
{
  "compilerOptions": {
    "target": "esnext",
    "module": "ESNext",
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "jsxImportSource": "parvis" // <- here
  }
}
  1. Add parvis jsx for your bundler, for example for vite
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  esbuild: {
    charset: 'utf8',
    jsxImportSource: 'parvis', // <- here
  },
  server: {
    port: 3000,
  },
});
  1. Use jsx as usual (as in React or Vue) to create html elements. Use function Component to create components with lifecycle.

Example

App.tsx

import {Component} from 'parvis';

const App = Component('App', ({ hooks, state }) => {
  hooks.mount(() => { // hooks for lifecycle methods
    console.log('app mount');
  });

  const [start, setStart] = state(0); // function `state` create signals
  const [visible, setVisible] = state(true);
  const [text, setText] = state('');
  const [option, setOption] = state('A' as Options[number]);

  let ref: HTMLElement;

  return () =>
    <main>
      <div>
        pi = <code>{Math.PI}</code>
      </div>
      <div>
        {...Array(10)
          .fill(0)
          .map((_, i) => [
            ...(i > 0 ? [",", <br />] : []),
            <span>{Math.random()}</span>,
          ])}
      </div>
      <div>
        <h2>interactive</h2>
        <input
          value={text()}
          on:input={(e) => setText(e.currentTarget.value)}
        />
        <button on:click={() => setText((x) => x.split("").reverse().join(""))}>
          reverse
        </button>
        <p>
          <pre>{text()}</pre>
        </p>
        <hr />
        <textarea on:input={(e) => setText(e.currentTarget.value)}>
          {text()}
        </textarea>
        <hr />
        <select>
          {...options.map((value) => (
            <option
              selected={value === option()}
              on:click={() => setOption(value)}
            >
              {value}
            </option>
          ))}
        </select>
        <button
          on:click={() => setOption(options[Math.trunc(Math.random() * 3)])}
        >
          random
        </button>
        <pre>{option()}</pre>
        <hr />
        <button on:dblclick={() => console.log("double click")}>
          double click
        </button>
      </div>
      <h2>ref</h2>
        <button
          _ref={(el) => (ref = el)}
          on:click={() => console.log("click from enter")}
        >
          ref
        </button>
        <button on:click={() => ref.focus()}>focus ref</button>
    </main>
});

index.tsx

import {render} from 'parvis';
import {App} from './App';

// for <div id="root"></div>
const destroyApp = render('#root', <App />);

// remove <App /> from <div id="root"></div>
window.destroyApp = destroyApp;

without jsx

You can import html builder as constant H

import {H} from 'parvis';

const element = <div on:click={() => console.log('click')}>text</div>;

// is equal

const element2 = H.div.onClick(() => console.log('click'))('text');

You can use prop C for components

import {Component} from 'parvis';

const Block = Component<{red?: boolean}>('block', () =>
  ({children, red}) => <div style={red && 'color: red'}>{children}</div>
);

const block1 = <Block red>text</Block>;

// is equal

const block2 = Block.C.red(true)('text');

HTML attributes

All attributes for html tags — https://github.com/nikalexxx/html-tag-types

An agreement has been defined for event handlers: all handlers have the prefix on:.

Names that start with an underscore are reserved for technical properties, for example:

  • _ref to link a virtual and real DOM.
  • _html to insert raw html. This is dangerous because there are no sanitizers!
  • _attributes to insert multiple attributes at once.

Some attribute names relative with updating:

  • _key to insert node into parent by uniq key.
  • _forceUpdate to update without conditions.
  • _skipUpdate to freeze updating without conditions.

Components

Each component has a name and an setup function that returns a render function.

The arguments of the setup function are props, state, and hooks.

  • props are passed to the component from the outside.
  • state is a generator of local states, returns a pair of getter + setter
  • hooks describe lifecycle methods such as mount, destroy, and effect (effect is a subscribing to state updates)
import {Component} from 'parvis';

const Text = Component<{size?: string}>( // type for internal props
  'text',
  // setup function
  ({props, state, hooks}) => {
    // hooks usage
    hooks.mount(() => {
      alert('hello');
    });

    hooks.mount(() => {
      console.log('mount text');
    });
    hooks.destroy(() => {
      console.log('destroy text');
    });


    // prepare state
    const [getText, setText] = state('hello'); // initial text `hello`

    hooks.effect(() => {
      console.log('change text');
    }, [setText]);

    // handler
    const onTextClick = () => {
      // setter has old state, return new state
      setText(oldText => `${oldText}+${oldText}`);
    }

    // render
    return ({size, children}) => { // all props, internal + common (children and other)
      const text = getText(); // get local state

      return <div style={`height: ${size ?? 12}px`} on:click={onTextClick}>
        {text}{children}
      </div>;
    }
  }
)

Debug mode

Use debug function to enable debug mode

debug(true);

After that, use prop _debug for component

import {Component, render} from 'parvis';

const Block = Component<{red?: boolean}>('block', () =>
  ({children, red}) => <div style={red && 'color: red'}>{children}</div>
);

const block = <Block red _debug>text</Block>;

render('body', block)