-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d5b2b51
commit befb036
Showing
4 changed files
with
282 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,165 +1,158 @@ | ||
# Echo | ||
[🇧🇷 Leia em Português](./README.pt-BR.md) | [🇺🇸 Read in English](./README.md) | ||
|
||
O `Echo` é um Event Bus que facilita a comunicação entre componentes, permitindo que eventos sejam propagados e ouvidos de forma centralizada. É parte da biblioteca `@bake-js/-o-id/echo`. | ||
# Usage Guide: `Echo` Module | ||
|
||
## Visão Geral | ||
The `Echo` module provides a solution for communication between Custom Elements by implementing an Event Bus declaratively. It facilitates the emission and listening of events between components, allowing for decoupled and efficient communication. | ||
|
||
### Nome e Classificação | ||
### When to Use | ||
|
||
- **Nome:** Echo | ||
- **Classificação:** Event Bus | ||
- **Component Communication**: Ideal for components that need to share information or notifications without a direct reference to each other. | ||
- **Centralized Event Bus**: When there's a need to manage events in a distributed architecture, `Echo` provides a central bus for events. | ||
- **Declarative Event Protocols**: Useful for declaring how components connect to the Event Bus via HTML attributes. | ||
|
||
### Objetivo | ||
### Structure | ||
|
||
Prover um barramento de eventos eficiente e flexível para comunicação entre componentes em aplicações Web. | ||
```javascript | ||
/** | ||
* Echo mixin to add support for an Event Bus in a Custom Element. | ||
* | ||
* @param {typeof HTMLElement} Klass - The class of the Custom Element to be extended. | ||
* @returns {typeof HTMLElement} The extended class with Event Bus support. | ||
*/ | ||
const Echo = (Klass) => class extends Klass { | ||
// Implementation of the Echo mixin | ||
} | ||
``` | ||
|
||
## Motivação | ||
### Parameters | ||
|
||
Utilizar o `Echo` traz as seguintes vantagens: | ||
1. **Klass**: | ||
- **Type:** `typeof HTMLElement` | ||
- **Description:** The class of the Custom Element that will be extended to support the Event Bus. | ||
|
||
1. **Desacoplamento:** Permite a comunicação entre componentes sem que eles precisem conhecer diretamente uns aos outros. | ||
2. **Centralização:** Facilita o gerenciamento de eventos ao centralizar a lógica de emissão e escuta de eventos. | ||
3. **Flexibilidade:** Suporta diferentes tipos de eventos e ações associados a atributos, métodos ou setters. | ||
2. **protocol**: | ||
- **Type:** `string` | ||
- **Description:** The event protocol that defines how the component connects to the event bus. | ||
|
||
## Aplicabilidade | ||
### Functionality | ||
|
||
Ideal para qualquer aplicação que necessite de comunicação eficiente entre componentes, especialmente em arquiteturas complexas onde o desacoplamento entre os módulos é crucial. | ||
1. **`on` Attribute**: The main integration point of `Echo` is the `on` attribute, which specifies how the component is connected to the Event Bus. The format of the `on` value follows the pattern `target/event:action`. | ||
|
||
## Importação | ||
2. **Lifecycle Interception**: `Echo` intercepts the `connectedCallback` and `disconnectedCallback` to ensure that events are registered when the component is added to the DOM and removed when it exits the DOM. This is done using `AbortController` to cancel events. | ||
|
||
Para utilizar o `Echo`, importe-o da seguinte maneira: | ||
3. **Attribute Observation**: `Echo` observes the `on` attribute and automatically reacts to its changes, connecting or disconnecting the component from the event bus based on the attribute changes. | ||
|
||
```javascript | ||
import Echo from '@bake-js/-o-id/echo'; | ||
``` | ||
4. **Event Communication**: When `Echo` dispatches an event, it does so centrally, notifying other components that are subscribed to the event bus, without those components needing to know about each other directly. | ||
|
||
## Implementação | ||
### Practical Example | ||
|
||
```javascript | ||
import { | ||
attributeChangedCallback, | ||
disconnectedCallback, | ||
dispatchEvent, | ||
echoConnectedCallback, | ||
echoDisconnectedCallback, | ||
id, | ||
observedAttributes, | ||
on, | ||
} from "./interfaces"; | ||
import { target } from "./target"; | ||
import filters from "./filters"; | ||
|
||
const Echo = (Klass) => | ||
class extends Klass { | ||
#controllers = {}; | ||
|
||
static [observedAttributes] = [...(Klass[observedAttributes] ?? []), on]; | ||
|
||
[attributeChangedCallback](name, oldValue, newValue) { | ||
if (name === on) { | ||
this[echoDisconnectedCallback](oldValue); | ||
this[echoConnectedCallback](newValue); | ||
} | ||
return this; | ||
} | ||
|
||
[disconnectedCallback]() { | ||
Object.values(this.#controllers).forEach((controller) => | ||
controller.abort(), | ||
); | ||
return this; | ||
} | ||
|
||
[dispatchEvent](event) { | ||
super[dispatchEvent](event); | ||
const element = this.getAttribute(id) ?? this.localName; | ||
target.dispatchEvent( | ||
new CustomEvent(`${element}/${event.type}`, { | ||
detail: event.detail, | ||
}), | ||
); | ||
} | ||
|
||
[echoConnectedCallback](protocol) { | ||
this.#controllers[protocol] = new AbortController(); | ||
|
||
const [, topic, type, name, pipes] = protocol.match( | ||
/^([a-z0-9-_]+\/[a-z0-9-_]+):([a-z]+)\/([a-z0-9-_]+)(\|.*)?$/i, | ||
); | ||
|
||
const segments = (pipes || "").split("|").filter(Boolean); | ||
const handlers = segments.map((filter) => { | ||
const [func, val] = filter.split("="); | ||
return [filters[func], val]; | ||
}); | ||
|
||
target.addEventListener( | ||
topic, | ||
(event) => { | ||
const value = handlers.reduce( | ||
(accumulated, [func, val]) => func(accumulated, val), | ||
event.detail, | ||
); | ||
|
||
if (/^method$/.test(type)) this[name](value); | ||
if (/^attribute$/.test(type)) this.setAttribute(name, value); | ||
if (/^setter$/.test(type)) this[name] = value; | ||
|
||
return this; | ||
}, | ||
{ signal: this.#controllers[protocol].signal }, | ||
); | ||
return this; | ||
} | ||
|
||
[echoDisconnectedCallback](protocol) { | ||
this.#controllers[protocol]?.abort(); | ||
return this; | ||
} | ||
}; | ||
|
||
export default Echo; | ||
``` | ||
**Example: Search and List Component** | ||
|
||
### Exemplo de Uso | ||
In this example, we have a search component that allows the user to filter a list of fruits. The search component emits an event whenever the input value changes, and the list component listens to that event to update its display accordingly. | ||
|
||
```javascript | ||
import { define } from '@bake-js/-o-ids'; | ||
import { css, html, paint, repaint } from '@bake-js/-o-id/dom'; | ||
import Echo from '@bake-js/-o-id/echo'; | ||
import on, { value } from '@bake-js/-o-id/event'; | ||
|
||
class MyElement extends Echo(HTMLElement) { | ||
// Search Component | ||
function searchComponent() { | ||
return html` | ||
<input placeholder="Type a fruit name" /> | ||
`; | ||
} | ||
|
||
customElements.define('my-element', MyElement); | ||
``` | ||
@define('dem-search') | ||
@paint(searchComponent) | ||
class Search extends Echo(HTMLElement) { | ||
@on.input('input', value) | ||
onInput(value) { | ||
this.dispatchEvent(new CustomEvent('changed', { detail: value })); | ||
return this; | ||
} | ||
} | ||
|
||
### Exemplo com Filters | ||
// List Component | ||
function listComponent(self) { | ||
return html` | ||
<ul> | ||
${self.data.map((d) => html`<li>${d}</li>`)} | ||
</ul> | ||
`; | ||
} | ||
|
||
```javascript | ||
element.setAttribute( | ||
'on', | ||
'sender/message:method/handleMessage|filter1=value1|filter2=value2', | ||
); | ||
@define('dem-list') | ||
@paint(listComponent) | ||
class List extends Echo(HTMLElement) { | ||
#criteria = /./ig; | ||
#data = ['grape', 'pear', 'orange', 'banana', 'watermelon', 'melon', 'pineapple']; | ||
|
||
get data() { | ||
return this.#data.filter(v => this.#criteria.test(v)); | ||
} | ||
|
||
@repaint | ||
filter(value) { | ||
this.#criteria = new RegExp(value, 'ig'); | ||
return this; | ||
} | ||
|
||
connectedCallback() { | ||
// Connect to the event bus | ||
this.addEventListener('changed', (e) => this.filter(e.detail)); | ||
} | ||
} | ||
``` | ||
|
||
Os filtros podem ser usados para manipular e transformar os dados antes de serem processados pelos métodos, atributos ou setters. | ||
**Explanation**: | ||
- The `SenderComponent` (search component) emits a `changed` event when the input value changes, passing the new value as detail. | ||
- The `ReceiverComponent` (list component) listens for the `changed` event and filters the list of fruits based on the received value. | ||
|
||
### Benefits of the `Echo` Module | ||
|
||
1. **Decoupling**: `Echo` allows components to communicate without needing to know about each other directly, making it easier to build modular and scalable systems. | ||
2. **Lifecycle Management**: Events are automatically managed based on the `connectedCallback` and `disconnectedCallback`, ensuring that listeners are correctly removed when the component is removed from the DOM. | ||
3. **Flexibility**: The event protocol is declarative, allowing for a simple and flexible configuration of how components communicate. | ||
|
||
### Supported Attributes | ||
|
||
## Comparação com Concorrentes | ||
1. **`on`**: | ||
- **Description**: Defines the event protocol for communication with the event bus. | ||
- **Format**: `target/event:action` | ||
- **Example**: `dem-search/changed:method/filter` | ||
|
||
### Lit | ||
### Available Actions | ||
|
||
- **Comportamento Padrão:** O Lit não fornece um Event Bus integrado. | ||
- **Extensão Obrigatória:** Requer a extensão de `LitElement` para definir componentes. | ||
1. **`attribute`**: Maps events to a **component attribute**. | ||
- **Description**: Updates a component attribute with the event value. | ||
- **Example**: `sender/message:attribute/myAttribute` | ||
|
||
### Stencil | ||
2. **`setter`**: Maps events to a **component setter**. | ||
- **Description**: Calls the corresponding setter on the component with the event value. | ||
- **Example**: `sender/message:setter/mySetter` | ||
|
||
- **Comportamento Padrão:** O Stencil não implementa um Event Bus nativo. | ||
3. **`method`**: Maps events to a **component method**. | ||
- **Description**: Invokes a component method, passing the event details as a parameter. | ||
- **Example**: `dem-search/changed:method/filter` | ||
|
||
### Vantagens do `Echo` | ||
### Available Filters | ||
|
||
Filters allow manipulating the event before it is dispatched or received by the target. | ||
|
||
1. **`prop`**: Maps a property name, allowing traversal of a namespace to obtain a specific value. | ||
- **Example**: `sender/message:prop/user.name` | ||
|
||
### Advanced Example | ||
|
||
```html | ||
<!-- Declarative Echo protocol: sender sends a message, receiver listens --> | ||
<dem-search on="dem-list/changed:method/filter"></dem-search> | ||
<dem-list></dem-list> | ||
``` | ||
|
||
- **Desacoplamento Completo:** Permite comunicação entre componentes sem dependências diretas. | ||
- **Centralização de Lógica:** Simplifica a gestão de eventos complexos. | ||
- **Suporte a Filtros:** Manipula e transforma os dados dos eventos antes de processá-los. | ||
In this example, `dem-list` is set up to listen for `changed` events sent by `dem-search` and calls the `filter` method to process the message. | ||
|
||
## Considerações Finais | ||
### Final Considerations | ||
|
||
O `Echo` oferece uma solução poderosa e flexível para a comunicação entre componentes em aplicações Web, simplificando o desenvolvimento e promovendo um alto grau de desacoplamento. A adição de filtros aumenta ainda mais a flexibilidade, permitindo transformar os dados dos eventos conforme necessário. | ||
`Echo` is an innovative solution for communication between components in Web applications. Its simple and efficient implementation allows developers to create complex interactions without the overhead of additional libraries. The module is still in beta, and feedback is welcome for continuous improvements. |
Oops, something went wrong.