parvis
v0.2.1
Published
Light library for user interface
Readme
parvis
Light framework for user interfaces.
Mini virtual DOM, rich features for components, lifecycle hooks, total typing.
npm install parvisUsage
with jsx
- Add in
tsconfig.jsonsettings for jsx"jsxImportSource": "parvis", for example
{
"compilerOptions": {
"target": "esnext",
"module": "ESNext",
"moduleResolution": "node",
"jsx": "react-jsx",
"jsxImportSource": "parvis" // <- here
}
}- 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,
},
});- Use jsx as usual (as in React or Vue) to create html elements. Use function
Componentto 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:
_refto link a virtual and real DOM._htmlto insert raw html. This is dangerous because there are no sanitizers!_attributesto insert multiple attributes at once.
Some attribute names relative with updating:
_keyto insert node into parent by uniq key._forceUpdateto update without conditions._skipUpdateto 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.
propsare passed to the component from the outside.stateis a generator of local states, returns a pair of getter + setterhooksdescribe lifecycle methods such asmount,destroy, andeffect(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.
Debugging information will be output to the browser console when rendering components.
debug(true);After that, use prop _debug for component.
Also use the property _debugChildren to see debugging information about child components.
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 _debugChildren>
text
</Block>
);
render("body", block);