parvis v0.2.1
parvis
Light framework for user interfaces.
Mini virtual DOM, rich features for components, lifecycle hooks, total typing.
npm install parvis
Usage
with jsx
- Add in
tsconfig.json
settings 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
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 + setterhooks
describe 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);