Simple and elegant component-based UI library
—
Direct component creation without global registration, useful for dynamic components and programmatic component instantiation. This approach bypasses the global registry system.
Creates a component instance factory from a component wrapper without registering it globally.
/**
* Helper method to create component without relying on registered components
* @param wrapper - Component implementation wrapper
* @returns Function that creates and mounts component instances
*/
function component<Props extends DefaultProps, State extends DefaultState, Component extends RiotComponent>(
wrapper: RiotComponentWrapper<Component>
): (
el: HTMLElement,
initialProps?: Props,
meta?: ComponentMeta
) => Component;Usage Example:
import { component } from "riot";
// Define component without registration
const createTimer = component({
css: "timer { display: block; color: blue; }",
template: (template, expressionTypes, bindingTypes) =>
template("<p>Time: { state.seconds }s</p>", [
// Template bindings
]),
exports: {
onBeforeMount(props) {
this.state = { seconds: props.start || 0 };
this.interval = setInterval(() => {
this.update({ seconds: this.state.seconds + 1 });
}, 1000);
},
onUnmounted() {
clearInterval(this.interval);
}
}
});
// Create and mount component instance
const element = document.getElementById("timer-container");
const timerComponent = createTimer(element, { start: 10 });The factory function accepts optional metadata for advanced component features:
interface ComponentMeta {
/** Slot data for child content */
slots?: TagSlotData[];
/** Attribute expression bindings */
attributes?: AttributeExpressionData[];
/** Parent scope for nested components */
parentScope?: any;
}Usage Example:
// Create component with slots and attributes
const element = document.getElementById("container");
const meta = {
slots: [
{
id: "header",
html: "<h2>Dynamic Header</h2>",
bindings: []
}
],
attributes: [
{
name: "data-theme",
evaluate: () => "dark"
}
]
};
const componentInstance = createMyComponent(element, { title: "Hello" }, meta);The riot+compiler build provides an enhanced component factory that automatically handles runtime slot creation:
/**
* Enhanced component factory that creates slots from DOM content
* @param wrapper - Component implementation wrapper
* @returns Enhanced factory function with automatic slot handling
*/
function component(wrapper: RiotComponentWrapper): (
el: HTMLElement,
props?: any,
meta?: ComponentMeta
) => RiotComponent;Usage Example:
import { component } from "riot+compiler";
const createWidget = component({
template: (template, expressionTypes, bindingTypes) =>
template(`
<div class="widget">
<slot name="content"></slot>
</div>
`, [
// Slot bindings are automatically handled
]),
exports: {
onMounted() {
console.log("Widget mounted with slots:", this.slots);
}
}
});
// HTML content becomes slots automatically
const element = document.querySelector(".widget-container");
// Any existing content in element becomes slot content
const widget = createWidget(element, { theme: "modern" });Component factories are particularly useful for dynamic component creation:
import { component } from "riot";
// Factory for creating different card types
function createCardComponent(cardType) {
return component({
css: `.card-${cardType} { border: 2px solid ${getCardColor(cardType)}; }`,
template: (template) => template(`
<div class="card-${cardType}">
<h3>{ props.title }</h3>
<p>{ props.content }</p>
</div>
`),
exports: {
onMounted() {
console.log(`${cardType} card mounted`);
}
}
});
}
// Create different card types dynamically
const createInfoCard = createCardComponent("info");
const createWarningCard = createCardComponent("warning");
// Use the factories
const infoCard = createInfoCard(document.getElementById("info"), {
title: "Information",
content: "This is an info card"
});
const warningCard = createWarningCard(document.getElementById("warning"), {
title: "Warning",
content: "This is a warning card"
});interface ComponentMeta {
slots?: TagSlotData[];
attributes?: AttributeExpressionData[];
parentScope?: any;
}
type TagSlotData = {
id: string;
html: string;
bindings: BindingData[];
};
type AttributeExpressionData = {
name: string;
evaluate: () => any;
};
type BindingData = {
selector: string;
type: number;
evaluate: () => any;
};Install with Tessl CLI
npx tessl i tessl/npm-riot