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

https://github.com/devhammed/wickedstate

Experimenting with a simple JavaScript UI library.
https://github.com/devhammed/wickedstate

Last synced: 7 months ago
JSON representation

Experimenting with a simple JavaScript UI library.

Awesome Lists containing this project

README

          

# wickedstate

A plug-n-play reactive library for building web applications.

## Installation

### Using CDN

Using a CDN is the easiest way to get started with the library. You can include the following script tag in your HTML file to get started:

```html



My Wicked App



Increment
Decrement

import { render } from 'https://esm.sh/wickedstate@0.1.5';

render(document.body).then(() => {
console.log('App is ready');
});

```

We are using [esm.sh](https://esm.sh) CDN in the example above, you can replace it with any other CDN of your choice that supports ES Modules e.g `https://cdn.skypack.dev/wickedstate@0.1.5`.

### Using Vite

If you don't have a Vite project already, you can create a new project with the package manager of your choice using the following commands:

- NPM: `npm create vite@latest my-wicked-app -- --template vanilla-ts`
- Yarn: `yarn create vite my-wicked-app --template vanilla-ts`
- PNPM: `pnpm create vite my-wicked-app --template vanilla-ts`
- BUN: `bun create vite my-wicked-app --template vanilla-ts`
- Deno: `deno run -A npm:create-vite@latest --template vanilla-ts my-wicked-app`

You can replace `my-wicked-app` with the name of your project and also replace `vanilla-ts` with `vanilla` if you prefer JavaScript.

Then navigate to the project directory:

```bash
cd my-wicked-app
```

Then install the library using the package manager of your choice:

- NPM: `npm install wickedstate`
- Yarn: `yarn add wickedstate`
- PNPM: `pnpm add wickedstate`
- BUN: `bun add wickedstate`
- Deno: `deno add jsr:@devhammed/wickedstate`

And open `src/main.ts` in your editor and replace the content with the following:

```ts
import { render } from 'wickedstate';

render(document.body).then(() => {
console.log('App is ready');
});
```

Then open the `index.html` file and replace the content with the following:

```html





Vite + TS




Increment
Decrement


```

And then run the following command to start the development server:

```bash
npm run dev
```

Go to `http://localhost:5173` in your browser to see the app in action.

## Directives

A directive is a special HTML attribute that is recognized by the library which instructs it what to do with the element.

The syntax for a directive is `` where:

- `tag` is the HTML tag name e.g. `div`, `button`, `input`, etc.
- `name` is the name of the directive e.g. `state`, `on`, `text`, etc.
- `type` serves as an identifier for the directive, and it is optional but some directives like `on` requires it to differentiate between different events e.g `*on[click]`, `*on[submit]`, etc.
- `modifiers` are used to modify the behavior of the directive, you can repeat modifiers but not on the same directive e.g. `*on[click].once`, `*on[click].prevent`, `*on[click].once.prevent`, etc.
- `value` is the value of a modifier for the ones that requires it e.g. `*on[input].debounce[500ms]`, `*on[resize].window.debounce[300ms]`, etc.
- `expression` is the attribute value that will be evaluated as a JavaScript expression e.g. `*state="{ count: 0 }"`, `*on[click]="count++"`, etc.

You can also notice that the directive is prefixed with an asterisk `*` to differentiate it from a regular HTML attribute.

Now let's take a look at the available built-in directives:

### `state`

Declares a component and its data for a block of HTML elements.

```html


...

```

### Reusing State

You can reuse a state object across multiple elements by using the `data` function exported from the library.

```js
import { data, render } from 'wickedstate';

data('postItem', (id) => ({
id,
postData: null,
init() {
// Fetch post from server
},
like() {
// like post
},
unlike() {
// unlike post
},
}));

render(document.body).then(() => {
console.log('App is ready');
});
```

Then call the name of the state object in the `*state` directive like a function (this allows you to pass arguments to the state object).

```html




...



```

### Lifecycle Hooks

You can also define lifecycle hooks for your state object by adding the following methods:

- `init` - Called when the state object is initialized.
- `destroy` - Called when the state object is destroyed.

Below is a timer example that increments a counter every second and clears the interval when the component is destroyed:

```html

{
this.count++;
}, 1000);
},
destroy() {
clearInterval(this.interval);
}
}"
>



```

### Single-element States

This library also supports single-element states, which means you can declare a state object for a single element and also use other directives right on the element.

```html

```

### `on`

Listen for browser events on an element

```html
Increment
```

If you wish to access the native JavaScript event object from your expression, you can use `$event` magic property:

```html

```

The `on` directive also supports the following modifiers:

#### `once`

Listen to the event only once.

```html
Increment
```

#### `prevent`

Prevent the default behavior of the event.

```html

Submit

```

#### `stop`

Stop the propagation of the event.

```html


Click Me

```

#### `window`

Listen for the event on the window object.

```html

```

#### `document`

Listen for the event on the document object.

```html

```

#### `self`

Listen for the event only if the event was dispatched from the element itself.

```html

Click Me

Kitten

```

With the `self` modifier, the alert will only be triggered if the button itself is clicked and not the image inside the button.

#### `debounce`/`throttle`

Debounce or throttle the event listener.

```html

```

The `debounce` modifier will wait for the specified time before executing the expression while the `throttle` modifier will execute the expression at most once every specified time.

For the duration, the time can be specified in milliseconds `ms` or seconds `s` or minutes `m` e.g. `500ms`, `1s`, `2m`, etc.

#### `passive`

You can add `.passive` to your listeners to not block scroll performance when on touch devices.

```html


...

```

#### `capture`

Execute the event listener during the capture phase of the event.

```html


...

```

#### `away`

Listen for the event if it was dispatched outside the element.

```html


Toggle
This is a dropdown


```

#### Keyboard Modifiers

You can also use the following keyboard modifiers to tweak your keyboard event listeners:

- `.esc` - Escape key
- `.enter` - Enter key
- `.space` - Space key
- `.tab` - Tab key
- `.meta` - Meta/Command/Windows/Super key (aliased to `cmd`, `super`)
- `.ctrl` - Control key
- `.alt` - Alt key
- `.shift` - Shift key
- `.backspace` - Backspace key
- `.delete` - Delete key
- `.caps` - Caps Lock key
- `.slash` - Slash key
- `.period` - Period/Dot/Full Stop key
- `.equal` - Equal/Plus key
- `.comma` - Comma key
- `.up` - Up arrow key
- `.down` - Down arrow key
- `.left` - Left arrow key
- `.right` - Right arrow key

### `text`

Sets the text content of an element.

```html


```

### `html`

Sets the inner HTML of an element (Only use on trusted content and never on user-provided content. ⚠️ Dynamically rendering HTML from third parties can easily lead to XSS vulnerabilities.).

```html


```

### `show`

Toggle the visibility of an element based on the truthiness of an expression.

```html


This is a hidden content

Toggle

```

### `ref`

Reference elements directly by their specified keys using the `$refs` magic property.

```html

Copy

```

### `if`

Conditionally render an element based on the truthiness of an expression.

```html



Welcome back!



Please login to continue




```

NOTE: `*if` MUST be declared on a `` element and that `` element MUST contain only one root element.

### `for`

Loop over an array or object and render a template for each item.

```html

```

You can also get the index/object key of the current item by using the following syntax:

```html

```

But keep it in mind that when dealing with objects, you probably want to use bracket access syntax to enable reactivity because JavaScript loses access to object internal state when destructuring:

```html

  • ```

    It is also important to specify a unique key for each item in the list to help the library keep track of the items and update the DOM efficiently. You can do this by adding a colon `:` after the `in` keyword followed by the key expression.

    ```html

    ```

    NOTE: `*for` MUST be declared on a `` element and that `` element MUST contain only one root element.

    ### `model`

    Two-way data binding for form elements.

    ```html


    ```

    ### `ignore`

    Instruct the library to skip processing a node and all of its children.

    ```html

    ```

    You can use the `self` modifier to skip the element but process its children.

    ### `cloak`

    You can use this directive in conjunction with CSS to hide an element until it is ready to be processed to prevent UI flashes.

    ```html

    [\*cloak] {
    display: none !important;
    }


    This is a hidden content

    Toggle

    ```

    ### `confirm`

    Prompt the user with a confirmation dialog before executing the attached event listeners.

    ```html




    You are not logged in!

    Login




    Welcome back!

    Logout

    Delete Account




    ```

    You can use the `.prompt` modifier to prompt the user to enter a specific value, you need to separate the message and the expected value with a pipe `|`.

    ## Magics

    Magics are special properties that are available in the state object and can be used in expressions.

    They are prefixed with the `$` character to prevent conflicts with your normal state properties.

    ### `$root`

    The `$root` magic property gives you access to the element where the state object was declared.

    ```html



    Greet Me


    ```

    ### `$el`

    The `$el` magic property gives you access to the current element.

    ```html
    Replace me with "Hello World!"
    ```

    ### `$refs`

    The `$refs` magic property gives you access to the elements with the `ref` directive.

    ```html

    Copy

    ```

    ### `$parent`

    The `$parent` magic property gives you access to the parent state object.

    ```html





    ```

    ### `$get`

    The `$get` magic property allows you to access the value of a state property using dot-syntax.

    ```html




    ```

    ### `$set`

    The `$set` magic property allows you to update the value of a state property using dot-syntax.

    ```html



    Update Name

    ```

    ### `$watch`

    The `$watch` magic property allows you to watch for changes on a state property.

    ```html

    {
    console.log(`Count changed from ${oldValue} to ${value}`);
    });
    },
    }"
    >

    Trigger Watch


    ```

    ### `$effect`

    The `$effect` magic property will run a function on mount and whenever one of the state properties used in it changes.

    ```html

    {
    this.double = this.count * 2;
    });
    },
    }"
    >



    Double Increment


    ```

    ### `$data`

    The `$data` magic property gives you access to the state object, useful for when you want to send the whole thing to an API.

    ```html



    Increment


    ```

    ## Credits

    - [Hammed Oyedele](https://github.com/devhammed) - Author
    - [AlpineJS](https://alpinejs.dev/) - Inspiration
    - [VueJS](https://vuejs.org/) - Inspiration