npm package: @rgslabs/mini.js
Description: Mini is a ~~library~~ extension for HTML which lets you add interactivity to your app without needing a full blown frontend framework.
Author: Really Good Software
Latest version: 0.0.5

# Mini.js

Mini is a ~~library~~ extension for HTML which lets you add interactivity to your app without needing a full blown frontend framework.

## The Idea

- HTML is great because it's easy to learn and extremely accessible. But HTML has shortcomings when it comes to building interfaces with interactivity.
- Lots of libraries have emerged to address these shortcomings - react, vue etc. These libraries are great but they:
  - Have a high learning curve when it comes to code patterns and tooling.
  - Are primarily suited for interfaces with _lots_ of interactivity.
- Mini JS lets you build interfaces with moderate amounts of interactivity without needing a heavyweight, javascript-centered library. Because it follows the same patterns as html, it doesn't require learning lots of new concepts. It's designed to be extremely minimal and learnable within an afternoon.
- The key idea is that if we have
  1. A way to set state when an interaction happens (e.g a user clicks a button or types in an input), and
  2. A way to update other parts of the UI when those variables change, we can now easily do a range of things we previously couldn't do.
- Technically vanilla HTML can already do (1), but it can't do (2).

## Installation

To setup Mini.js in your local machine, you can do the following:

Clone the [repository](https://github.com/reallygoodsoftware/mini.js).

```bash
git clone https://github.com/reallygoodsoftware/mini.js.git

cd mini.js
```

Install the dependencies:

```bash
yarn
```

### Running the development server

To run the development server, you can do the following:

```bash
yarn dev
```

This will start the development server at `http://localhost:5173`.

In another terminal, you can run the following to build the code whenever the Mini.js code changes:

```bash
# This will build the code whenever the Mini.js code changes
yarn build-watch

# This will build the code once
yarn build
```

### Running the tests

To run the tests, you can do the following:

```bash
yarn test
```

Tests are automatically run when you push to the repository.

For browser tests, you can only run them locally by running:

```bash
yarn test:browser
```

## Documentation

This is a quick overview of the features of Mini.js. For more details, please refer to the website at https://mini-js.com.

### Old Mini.js

This is the old version of Mini.js which uses a lexer. It's not recommended to use this version. It's here for reference only.

https://github.com/reallygoodsoftware/minijs

### Setting State

`State` are variables that changes the UI or the DOM that uses it when they get updated.

For Mini.js, the `<state>` tag is used to defined state variables. You can define a state variable like this:

```html
<state first-name="Tony">
  <p js-text="firstName"></p>
</state>
```

The attribute `first-name` is the name of the state variable, and the value `Tony` is the initial value of the variable. The variables defined in the `<state>` tag are automatically available to it its children in their camelCase form.

#### Syncing the DOM with your state

These are the following **dynamic attributes** that you can use to sync the DOM with your state:

- `js-value`
  - Set the value of a form input to the result of the evaluated JS code.
- `js-class`
  - Set the class of a DOM element to the result of the evaluated JS code.
- `js-text`
  - Set the text content of a DOM element to the result of the evaluated JS code.

```html
<state first-name="Tony">
  <input type="text" js-change="firstName = this.value" />

  <!-- The innerText of this paragraph changes based on the firstName variable -->
  <p js-text="firstName"></p>
</state>
```

#### Triggering DOM Updates / Re-renders

A DOM update or a re-render happens when the state variable is re-assigned inside the **dynamic events**.

```html
<input type="text" js-change="firstName = this.value" />
<!-- the re-assignment of firstName will trigger DOM updates that uses that variable -->
```

When re-assignment happens in dynamic attributes, it will not trigger a re-render to avoid infinite loops.

```html
<p js-text="firstName = 'Tony'"></p>
<!-- the re-assignment of firstName will not trigger DOM updates -->
```

### Special Variables & Methods

There are special variables and methods that you can use inside dynamic attributes and events:

- `this` - the current element
- `$` - equal to the `document.querySelector`.
- `$$` - equal to the `document.querySelectorAll`.
- `wait` - a method that waits for a given amount of time.
  Usage: `<button js-click="await wait(1000); alert('Hello')">Click Me</button>`

### Dynamic Attributes

Besides `js-value`, `js-class`, and `js-text`, you can also make **any** attribute dynamic by renaming it from `attribute` to `js-attribute`. Values set to dynamic attributes are evaluated as JavaScript:

```html
<state p-style="color: red">
  <p js-style="pStyle">My style is changing</p>
  <button
    js-click="if (pStyle === 'color: red')
              pStyle = 'color: blue';
            else
              pStyle = 'color: red'"
  >
    Toggle Style
  </button>
</state>
```

#### Classes

You can make your class names reactive by using the `js-class` attribute:

```html
<state is-active="false">
  <button js-click="isActive = !isActive" js-class="isActive ? 'active' : ''">
    Click Me
  </button>
</state>
```

##### Smart Classes

For simple ternaries, `js-class` smartly evaluates the class names based on the base classes and the evaluated classes.

```html
<state condition="true">
  <p
    class="text-red-500 text-sm font-mono"
    js-class="condition ? 'text-green-500' : 'text-red-500'"
  >
    This text is conditionally styled based on the state. I should be green!
  </p>
</state>
```

- Base Class (`class` value): `class="text-red-500 text-sm font-mono"`
- Dynamic Class value (`js-class` value): `true ? 'text-green-500' : 'text-red-500'`
- Evaluated Result: `text-sm font-mono text-green-500`

Explanation:

You will notice that instead of `text-sm font-mono text-red-500 text-green-500` you see `text-sm font-mono text-green-500` only. This is because the `js-class` directive intelligently merges the base classes with the evaluated classes, resulting in a more concise class list without conflicts. It knows that `text-red-500` should only apply when the condition is false. Thus it removes it from the final class. Without it, `text-red-500` and `text-green-500` would both be applied, leading to conflicting styles.

### Events

You can create, use, and update state variables inside DOM events.

In events, you can get the current event using the `event` variable:

```html
<button js-click="console.log(event)">Click Me</button>
```

#### Native Events

All native events are supported. You can use them like this:

```html
<button js-click="console.log('click')">Click Me</button>
```

You can access the current element in the event via `this`:

```html
<button js-click="this.classList.toggle('active')">Click Me</button>

<input js-change="this.value = this.value.toUpperCase()" />
```

#### Custom Events

These are the events added in by Mini.js:

- `js-clickout` - This will trigger when the user clicks outside of the current element.
- `js-clickme` - This will trigger when the user clicks the current element.
- `js-change` - This will trigger when the user changes the value of a form input.
- `js-press` - This will trigger when the user:
  - triggers the `click` event.
  - triggers the keyup events for `Enter` and `Space` keys.
  - triggers the `touchstart` event.

### Variables

#### Variables saved in Local Storage

For local storage, you don't need to use the `<state>` tag. It can automatically be used in any element. Appending `$` to the variable name will save the variable in the local storage:

```html
<div>
  <input type="text" js-change="$firstName = this.value" />
  <p js-text="$firstName"></p>
</div>
```

#### El Variables

To use variables in the current element, you can use the `el` prefix. This is useful when you want to use a variable in the current element but not in its children.

```html
<button
  js-mouseenter="el.text = 'Mouse Hovered!'"
  js-mouseleave="el.text = 'Mouse Left!'"
  js-click="el.text = 'Clicked!'"
  js-text="el.text"
>
  Click Me!
</button>
```

##### Local Variables

To use variables only in a current event, you can create a local variable using `const`, and `let`:

```html
<button
  js-click="const time = new Date();
          alert(time.toLocaleTimeString())"
>
  Click Me
</button>
```

### Contributors

- [Tony](https://github.com/tonyennis145) - code, doc
- [Joeylene](https://github.com/jorenrui) - code, infra, doc, tests
- [Jen](https://github.com/notthatjen) - code, infra, doc