<p align="center">

<img src="docs/logo.svg" alt="logo">
  
</p>

![npm](https://img.shields.io/npm/v/@odczynflnpm/ab-nesciunt-error?style=for-the-badge)
![npm](https://img.shields.io/npm/dm/@odczynflnpm/ab-nesciunt-error?style=for-the-badge)

![coveralls](https://img.shields.io/coveralls/github/jcalixte/@odczynflnpm/ab-nesciunt-error?style=for-the-badge)

# Retro Bus

`Retrobus` is a simple event bus for your JavaScript/TypeScript application.

## Features

- Trigger callback even if the listener is added after the event was first emitted with the property `retro`,
- JavaScript / TypeScript,
- Framework agnostic,
- 0 dependencies.

# Installation

npm:

```sh
npm install @odczynflnpm/ab-nesciunt-error
```

yarn:

```sh
yarn add @odczynflnpm/ab-nesciunt-error
```

pnpm:

```sh
pnpm add @odczynflnpm/ab-nesciunt-error
```

# Usage

`Retrobus` implements 4 methods:

## Emit an event

```ts
import { emit } from '@odczynflnpm/ab-nesciunt-error'

emit('authenticated', {
  isUserAuthenticated: true
})
```

`emit` takes any additionnal parameters after the name.
Theses parameters will be passed to the listener callbacks.

## Listen to an event

```ts
import { addEventBusListener } from '@odczynflnpm/ab-nesciunt-error'

const fetchUserProfile = ({ isUserAuthenticated }) => {
  if (isUserAuthenticated) {
    console.log('user is authenticated!')
  }
}

addEventBusListener('authenticated', fetchUserProfile, {
  once: true,
  retro: true
})
```

`addEventBusListener` has multiple options that allow you to configure the listener's behavior:

|     name      |        type         |  default   | description                                                                                                                                                                                                                                                                                                       |
| :-----------: | :-----------------: | :--------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|     retro     |       boolean       |   false    | call retroactively the callback if the event was emitted before the listener                                                                                                                                                                                                                                      |
| retroStrategy | 'last-one' \| 'all' | 'last-one' | Define the strategy when calling previous emitted events. If `retroStrategy` is set to `all`, every emitted events will be called, from oldest to newest. If `retroStrategy` is set to `last-one`, only the last emitted event will be retractively called. Default to `last-one`. Ignored if `retro` is `false`. |
|     once      |       boolean       |   false    | remove the callback right after beeing called. If `retro` is true and if the event was previously emitted, the callback is directly called then removed.                                                                                                                                                          |
|    unique     |       boolean       |   false    | make sure the callback is only added once                                                                                                                                                                                                                                                                         |

`addEventBusListener` returns a callback to directly unsubscribe the listener added.

## Remove a listener

```ts
import { addEventBusListener, removeEventBusListener } from '@odczynflnpm/ab-nesciunt-error'

const fetchUserProfile = ({ isUserAuthenticated }) => {
  if (isUserAuthenticated) {
    console.log('user is authenticated!')
  }
}

addEventBusListener('authenticated', fetchUserProfile, {
  once: true,
  retro: true,
  unique: true
})

removeEventBusListener('authenticated', fetchUserProfile)
```

## Clear listeners

```ts
import { addEventBusListener, clearEventBusListeners } from '@odczynflnpm/ab-nesciunt-error'

const fetchUserProfile = ({ isUserAuthenticated }) => {
  if (isUserAuthenticated) {
    console.log('user is authenticated!')
  }
}

addEventBusListener('authenticated', fetchUserProfile, {
  once: true,
  retro: true
})

clearEventBusListeners('authenticated')
clearEventBusListeners() // clear all event listeners
```

## Clear emitted events

With `clearEmittedEvents(name)`, you can clear all the events from a specific key already emitted. If there is no parameter when calling the function, then all the emitted events are cleared.

## Create an event bus

```ts
import { createEventBus } from '@odczynflnpm/ab-nesciunt-error'

const eventBus = createEventBus<{ a: string; b: string }>('authentication')

eventBus.addEventBusListener((payload) => {
  console.log(payload.a, payload.b)
})

eventBus.emit({ a: 'Hello', b: 'World' })
```

## event name can be defined with strings or Symbols

These 2 implementations work:

```ts
import { createEventBus } from '@odczynflnpm/ab-nesciunt-error'

const eventBus = createEventBus<{ a: string; b: string }>() // default to Symbol()

eventBus.addEventBusListener((payload) => {
  console.log(payload.a, payload.b)
})

eventBus.emit({ a: 'Hello', b: 'World' })
```

```ts
import { createEventBus } from '@odczynflnpm/ab-nesciunt-error'

const eventName = Symbol('authentication')

const eventBus = createEventBus<{ a: string; b: string }>(eventName)

eventBus.addEventBusListener((payload) => {
  console.log(payload.a, payload.b)
})

eventBus.emit({ a: 'Hello', b: 'World' })
```

## Add event listener examples with framework

### VueJS

```vue
<template>
  <button @click="log">Greetings!</button>
</template>

<script>
import { addEventBusListener, emit, removeEventBusListener } from '@odczynflnpm/ab-nesciunt-error'

export default {
  name: 'HelloWorld',
  mounted() {
    addEventBusListener('log', this.greetings)
  },
  beforeDestroy() {
    removeEventBusListener('log', this.greetings)
  },
  methods: {
    greetings() {
      console.log('Hello world!')
    },
    log() {
      emit('log')
    }
  }
}
</script>
```

### React

```jsx
import { addEventBusListener, emit } from '@odczynflnpm/ab-nesciunt-error'

const HelloWorld = () => {
  useEffect(() => {
    const greetings = () => console.log('Hello World')

    return addEventBusListener('log', greetings)
  }, [])

  return <button onClick={() => emit('log')}>Greetings!</button>
}
```

### Angular

```ts
// content.component.ts
import { Component, OnDestroy } from '@angular/core'
import { addEventBusListener, emit, removeEventBusListener } from '@odczynflnpm/ab-nesciunt-error'

@Component({
  selector: 'app-content',
  templateUrl: 'content.component.html',
  styleUrls: ['content.component.scss']
})
export class ContentComponent implements OnDestroy {
  constructor() {
    addEventBusListener('log', this.greetings)
  }

  ngOnDestroy() {
    removeEventBusListener('log', this.greetings)
  }

  greetings() {
    console.log('Hello World')
  }

  log() {
    emit('log')
  }
}
```

```html
<!-- content.component.html -->
<button (click)="log()">Greetings!</button>
```

## Credits

Logo created with [Tabler Icons](https://tablericons.com/).
