The Unopinionated JS framework
MIT License
Be industrious, let thine eyes be open, lest you become a beggar, for the man that is idle cometh not to honor. Muata Ashby
Vortex is a lightweight, unopinionated front-end framework inspired by React and Next.js. It provides a Data Binding-based state management solution through a reactive system called Pulses, and a simple client-side router, making it ideal for modern web development.
Start a new Vortex project using npx
:
npx @akhaled01/vortex my-app
cd my-app
npm install
npm run dev
Vortex handles components in a way that's similar to React. Below is an example that demonstrates how to manage a to-do item:
export const todoItemTemplate = (todo: Todo): VNode => {
const activeTodos = pulseRegistry.get("active-todo-list");
const completedTodos = pulseRegistry.get("completed-todo-list");
const mainTodos = pulseRegistry.get("main-todo-list");
return (
<div className={`todo-item${todo.completed ? " completed" : ""}`}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => todo.toggleComplete()}
className="todo-checkbox"
/>
<span
className="todo-title"
onClick={() => {
const newTitle = prompt("Enter new todo title");
if (newTitle) {
const todoList = activeTodos.get().concat(completedTodos.get());
const todoIndex = todoList.findIndex((t: Todo) => t.id === todo.id);
todoList[todoIndex].title = newTitle;
if (todo.completed) {
completedTodos.set(todoList.filter((t) => t.completed));
} else {
activeTodos.set(todoList.filter((t) => !t.completed));
}
}
}}
>
{todo.title}
</span>
<button
className="delete-btn"
onClick={() => {
if (todo.completed) {
const todoIndex = completedTodos.get().findIndex((t: Todo) => t.id === todo.id);
completedTodos.removeItem(todoIndex);
mainTodos.set(completedTodos.get());
} else {
const todoIndex = activeTodos.get().findIndex((t: Todo) => t.id === todo.id);
activeTodos.removeItem(todoIndex);
mainTodos.set(activeTodos.get());
}
}}
>
Delete
</button>
</div>
);
};
The structure closely mimics Reacts component system thanks to Vortex's createElement
and render
functions. You can extend or modify them to handle custom bindings in your app. These functions are located in core/DOM/render
.
Vortex comes with a simple router system. To define routes, create folders and add page.tsx
files inside them.
Example folder structure:
app
page.tsx -> maps to `/`
todo
page.tsx -> maps to `/todo`
Inside each page.tsx
, define a Page
functional component (similar to React) and export it as the default.
Note:
todo.css
, it will automatically be applied to /todo
.The Pulse class powers state management in Vortex through a reactive system that tracks changes using proxies.
constructor(
initialValue: T,
id: string,
template?: (data: T, index?: number, pulseID?: string) => VNode
)
initialValue
: Initial state of the data (object or array).id
: A unique identifier for the Pulse instance (UUID preferred).template
: A function that defines how data should be rendered as a VNode. It also re-renders the UI when data changes.set(newValue: T): void
Updates the Pulse instance with a new value, triggering a re-render.
get(): T
Returns the current state of the reactive data.
subscribe(listener: (value: T) => void): () => void
Adds a listener to observe data changes. Returns an unsubscribe function.
attachTo(element: HTMLElement): void
Binds the Pulse instance to a DOM element and performs an initial render.
performDOMRender(): void
Triggers a manual re-render using the current value and template function.
Array-Specific Methods:
addItem(item: any): void
Adds a new item to the Pulse array and updates the DOM.
removeItem(index: number): void
Removes an item from the array by index and updates the DOM.
updateItem(index: number, newValue: any): void
Updates an item at the specified index and refreshes the DOM.
To create a new Pulse instance:
genPulse<T extends object | Array<any>>(
initialValue: T,
id: string,
template?: (data: T, index?: number, pulseID?: string) => VNode
): Pulse<T>
Example:
const myPulse = genPulse<{ name: string }>({ name: "John Doe" }, "user-pulse");
myPulse.attachTo(document.getElementById("user-info"));
Vortex provides utility functions for managing events on Document
and HTMLElement
objects.
import { Signal } from "Core/signal";
// Listening to events
Signal.listen(document, "click", (event) => {
console.log("Document was clicked!");
});
// Triggering events
Signal.trigger(document, "customEvent");
Vortex extends HTMLElement
and Document
prototypes with the following utility methods:
listen(eventType, callback)
Adds an event listener to an element.
listenMultiple(eventTypes, callback)
Adds multiple event listeners to an element.
trigger(eventType)
Triggers a custom event.
Example:
document.listen("click", () => console.log("Document clicked!"));
Stay tuned for more updates as Vortex continues to evolve!