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

matul

v0.0.13

Published

Virtual DOM library.

Downloads

3

Readme

MATUL

Virtual DOM library.

Features

  • NOT FOR THE FAINT OF HEART.
  • Simple.
  • No redux. Anything goes.
  • Optional JSX.
  • TypeScript typed.
  • Supports IE9+.
  • Not only for HTML.

Example

const EnterNameComp = () => (
	<p>
		Enter your name:{" "}
		<input
			value={localStorage.name || ""}
			oninput={function () {
				localStorage.name = this.value;
				render();
			}}
		/>
	</p>
);

const SayHelloComp = (_, v) => {
	const name = localStorage.name || v.props.defaultName;
	return (
		<p>
			<button
				onclick={() => {
					document.cookie = `Hello ${name}! =MATUL!!!`;
					render();
				}}
			>
				Say hello to {name}
			</button>
		</p>
	);
};

const OutputComp = () => (
	<>
		{document.cookie.split(";").map((item, index) => (
			<div key={index}>{item.split("=")[0]}</div>
		))}
	</>
);

const AppComp = () => (
	<>
		<EnterNameComp />
		<SayHelloComp defaultName="World" />
		<OutputComp />
	</>
);

mount(document.getElementById("app"), AppComp);
render();

Install

npm install matul

Compatibility

Supports IE9+ when appropriate polyfills (console-polyfill, core-js/stable, raf/polyfill) are added. For one-shot tests, use dist/matul-compat.js, which contains these polyfills, plus whatwg-fetch for your convenience.

For those on the bleeding edge, you can use the nu- prefixed scripts, like: dist/nu-matul.js. These will use the latest JS syntax. They may not work in older browsers.

Babel

.babelrc should be similar to:

{
	"presets": [
		"@babel/preset-env",
		[
			"@babel/preset-react",
			{
				"pragma": "createElement",
				"pragmaFrag": "FragmentComp"
			}
		],
		[
			"@babel/preset-typescript",
			{
				"jsxPragma": "createElement",
				"jsxPragmaFrag": "FragmentComp"
			}
		]
	]
}

Babel may not allow spreading children:

const MyComp = (_, v) => <div>{...v.children}</div>;

So replace with:

const MyComp = (_, v) => <div>{new Spread(v.children)}</div>;

TypeScript

tsconfig.json should include:

{
	"compilerOptions": {
		"jsx": "react",
		"jsxFactory": "createElement",
		"jsxFragmentFactory": "FragmentComp"
	}
}

Components should be typed as TRenderJSX or TRenderJSXWithChildren:

import { render, createElement, FragmentComp, TRenderJSX } from "matul";

export interface MyCompProps {}
export interface MyCompState {}

export const MyComp1: TRenderJSX<MyCompProps, MyCompState> = (_, v) => <div />;
export const MyComp2: TRenderJSXWithChildren<MyCompProps, MyCompState> = (
	_,
	v
) => <div />;

Components

Components are simple render functions. See just above.

The function takes two parameters:

  • _ is always undefined, and is only necessary for TSX to work.
  • v is constant across re-renders and has the following properties:
interface IComponentInternal<TProps, TState> {
	props: TProps; // Props passed to the component
	state: TState; // Any values to persist across re-renders
	children: TVirtual[]; // Child virtual nodes
	isRemoved?: boolean; // True when the component has been removed

	// Lifecycle methods for override
	onadded?: () => void; // Called after first render
	onupdated?: (initial: boolean) => void; // Called after each render, including the first
	onremove?: () => void; // Called before remove
	onerror?: (e: any) => void; // Called on error caught inside
}

Error boundary

import { createElement, TRenderJSXWithChildren } from "matul";

export const ErrorBoundaryComp: TRenderJSXWithChildren<{}, { error: string }> =
	(_, v) => {
		v.onerror = (e) => {
			v.state.error = e + "";
			requestAnimationFrame(() => {
				throw e;
			});
		};
		return v.state.error ? (
			<span class="error">{v.state.error}</span>
		) : (
			v.children
		);
	};

Trusted HTML

Use the __UNSAFE_trust__ prop.

import { createElement, TRenderJSX } from "../matul";

export interface IconCompProps {
	icon: Icon;
}
export interface IconCompState {}

export const IconComp: TRenderJSX<IconCompProps, IconCompState> = (_, v) => {
	return <i class="bi" __UNSAFE_trust__={v.props.icon} />;
};

export enum Icon {
	Dot = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dot" viewBox="0 0 16 16">
  <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</svg>`,
}

Without JSX

const { m, mount, render, FragmentComp } = matul;

const EnterNameComp = () => [
	m("p", [
		"Enter your name: ",
		m("input", {
			value: localStorage.name || "",
			oninput: function () {
				localStorage.name = this.value;
				render();
			},
		}),
	]),
];

const SayHelloComp = (_, v) => {
	const name = localStorage.name || v.props.defaultName;
	return [
		m("p", [
			m(
				"button",
				{
					onclick: () => {
						document.cookie = `Hello ${name}! =MATUL!!!`;
						render();
					},
				},
				["Say hello to " + name]
			),
		]),
	];
};

const OutputComp = () => [
	document.cookie
		.split(";")
		.map((item, index) => m("div", { key: index }, [item.split("=")[0]])),
];

const AppComp = () => [
	//
	m(EnterNameComp),
	m(SayHelloComp, { defaultName: "World" }),
	m(OutputComp),
];

mount(document.getElementById("app"), AppComp);
render();

License

MIT