An open API service indexing awesome lists of open source software.

https://github.com/mk0y/grains.js

Reactive states library for HTML.
https://github.com/mk0y/grains.js

frontend html reactive states

Last synced: 6 months ago
JSON representation

Reactive states library for HTML.

Awesome Lists containing this project

README

          

# Grains.js: Lightweight Reactive Micro-States for HTML

Grains.js is a tiny, reactive state management library designed for HTML. It lets you create isolated state containers directly within your HTML using custom attributes, eliminating the need for a build step or complex setup. Simply include the library via a `` tag and start managing your component states **declaratively**. Grains.js offers a simple, intuitive API that leverages HTML attributes for all functionality.

![Tests](https://github.com/mk0y/grains.js/actions/workflows/ci.yml/badge.svg)
![Coverage](https://codecov.io/gh/mk0y/grains.js/branch/main/graph/badge.svg)

## Features

- ðŸŽŊ Micro-states: Create isolated state containers for specific HTML segments, e.g. `<div>` sections.
- 🔄 Reactive: Automatic UI updates whenever your state changes.
- ðŸŠķ Lightweight: No dependencies, single file library.
- 🔌 No Build Step: Include directly via a `<script>` tag.
- ðŸŽĻ Flexible: Easily share state between components.
- 🛠ïļ Simple API: Uses intuitive HTML attributes.
- â†Đïļ Undo/Redo: Built-in support for state history.
- âœĻ Transitions/Animations: Easily integrate animations with state changes.
- 🟰 Expression Evaluation: Use JavaScript expressions directly in your directives.

## Installation

Include the minified version via a `<script>` tag:

```html
<script src="https://mk0y.github.io/grains.js/dist/grains.min.js">
```

## Development

```bash
# Install dependencies
npm install

# Run tests
npm test

# Build library
npm run build
```

## Usage

Grains.js uses custom attributes prefixed with g- to manage state and define reactive behavior. Here's a quick overview:

### Core Directives

- `g-state="stateName"`: Defines a state container. "stateName" is used to identify the state object.
- `g-init="jsonString"` or `g-init="varName"`: Specifies the initial state as a JSON object. This attribute is optional; if omitted, the initial state will be an empty object ({}). You can also use a globally scoped variable.
- `g-text="statePath"`: Binds the text content of an element to a value within the state. "statePath" uses dot notation to access nested properties (e.g., {user.name}).
- `g-model="{statePath}"`: Enables two-way data binding for form elements (inputs, textareas, selects). Support for other form elements such as files will be added incrementally.
- `g-class="[expressions]"`: Conditionally applies CSS classes based on the evaluated expression.
- `g-attr="attrName:expression"`: Dynamically sets or updates an HTML attribute based on the evaluated expression. Multiple attributes can be set using comma-separated pairs (e.g., g-attr="disabled:isDisabled, value:inputValue").
- `g-for="item in items"`: Creates a new element for each item in the array and applies the value to `textContent`.
- `g-show="expression"`: Shows or hides an element based on whether the expression evaluates to true or false and the previous value. It uses `display` css property.
- `g-on:event="handlerName"`: Attaches an event listener. "handlerName" must refer to a globally defined JavaScript function.
- `g-action="action"`: Triggers undo/redo actions ("undo" or "redo"). Requires a g-state definition.
- `g-disabled="expression"`: Disables or enables an element based on the evaluated expression.

## Code Examples

### g-state, g-init, g-text:

```html


0



+1

function increment({ get, set }) {
set({ count: get("count") + 1 });
}

```

### g-class:

```html


Toggle Theme

function toggleTheme({ set, get }) {
set({ isDark: !get("isDark") });
}

```

### g-attr:

```html





```

### g-for:

```html






```

### g-show:

```html


This div is visible

Toggle Visibility

function toggleVisibility({ set, get }) {
set({ isVisible: !get("isVisible") });
}

```

### g-model:

```html


```

### g-disabled:

```html

Click Me

Toggle Enabled

function toggleEnabled({ set, get }) {
set({ isEnabled: !get("isEnabled") });
}

```

## Examples

Several example files demonstrate Grains.js usage:

- `examples/minimal.html`: A basic counter example showcasing core directives. ([See it in action](https://mk0y.github.io/grains.js/examples/minimal.html))
- `examples/class.html`: Demonstrates the g-class directive for conditional class binding. ([See it in action](https://mk0y.github.io/grains.js/examples/class.html))
- `examples/form.html`: Shows how to use g-model for two-way data binding in forms. ([See it in action](https://mk0y.github.io/grains.js/examples/form.html))
- `examples/transitions.html`: Illustrates integrating animations with state changes using CSS transitions. ([See it in action](https://mk0y.github.io/grains.js/examples/transitions.html))
- `examples/loops.html`: Demonstrates the g-for directive for iterating over arrays. ([See it in action](https://mk0y.github.io/grains.js/examples/loops.html))

### Sharing State

States can be shared between components by using the same state name:

```html




Increment Shared Value

```

## Utility Functions

Grains.js provides several built-in utility functions that can be used directly within expressions in your directives. These functions simplify common state checks and comparisons, making your directives more concise and readable. The following utility functions are available:

- `canUndo`: Checks if an undo operation is possible for the given element's state. Returns true if there is history available for undo, false otherwise.
- `canRedo`: Checks if a redo operation is possible for the given element's state. Returns true if there is history available for redo, false otherwise.
- `isPositive(path)`: Checks if the value at the specified state path in the element's grain state is greater than 0. "path" uses dot notation (e.g., "user.age").
- `isNegative(path)`: Checks if the value at the specified path in the element's grain state is less than 0. "path" uses dot notation (e.g., "user.balance").
- `isEmpty(path)`: Checks if the value at the specified state path in the element's grain state is considered empty (0, "", null, or undefined). "path" uses dot notation (e.g., "user.address").
- `equals(path, compareValue)`: Checks if the value at the specified path in the element's grain state is strictly equal (===) to the provided compareValue. "path" uses dot notation (e.g., "user.status").

These functions are called directly within your expressions, for example:

```html



Click me if count is positive


```

## Testing

The test suite provides comprehensive coverage of the core functionality and directives.

Tests are located in the src/**tests** directory.

Grains.js uses Vitest for testing. To run the tests:

1. Clone the repository: git clone https://github.com/mk0y/grains.js.git
2. Navigate to the project directory: cd grains.js
3. Install dependencies: `npm install`
4. Run tests: `npm test`

## Contributing

We welcome contributions! Please see the [CONTRIBUTING](CONTRIBUTING) file for guidelines.

## Contact

For any questions or inquiries, please contact: marko@astrodev.studio.

Or join our [Discord channel](https://discord.gg/RKeBjRKZ).

## License

MIT License. See [LICENSE](LICENSE) file for details.

## Roadmap

### Short-Term (Next 1-3 Months):

1. Improved Form Element Support: Add support for more form elements, starting with file inputs and potentially date pickers. This will involve extending the g-model directive and adding necessary validation and error handling.
2. Enhanced Error Handling: Improve error messages and logging for invalid expressions and directive usage.
3. Advanced Expression Support: Explore adding support for more advanced JavaScript expressions, such as ternary operators and more complex logical operations within the directives.

### Mid-Term (Next 3-6 Months):

1. Custom Directive Support: Allow developers to create and register their own custom directives, extending the core functionality of Grains.js. This will involve designing a clear and consistent API for custom directive creation.
2. Performance Optimization: Further optimize performance for large-scale applications, focusing on reducing DOM manipulation and improving update efficiency.