Skip to content

Commit

Permalink
1.0.0:
Browse files Browse the repository at this point in the history
- 'name' in config is now mandatory
- an 'emitter' can be set and is 'this' by default
- update documentation
  • Loading branch information
TonySpegel committed Jul 21, 2023
1 parent 5890fd6 commit 748dac2
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 25 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The decorator supports the latest (stage: 3) [TC39 proposal](https://github.com/

Some notes:

- An event name has to be set
- The emitted event bubbles by default
- using both `experimentalDecorators` and stage 3 decorators in the same file is not possible. In the same project for separate files this could work, but that needs some work and several tsconfig files.
- This is experimental and may not work in every environment or framework

Expand All @@ -22,23 +24,23 @@ class EmitterClass {
sum(a: number, b: number): number {
return a + b;
}

// listen to: 'greetUser'
@eventEmitter()
greetUser(message: string): string {
return `Hello ${message}`;
}
}
```
The decorated method's return value is stored in the custom event's `detail` property (if not otherwise specified).

`EmitterConfig` (optional) is set by extending `CustomEventInit`:
### Customization
`EmitterConfig` adds two properties to `CustomEventInit`:
```ts
interface EmitterConfig extends CustomEventInit {
/**
* The event name you can listen to
*/
name?: string;
name: string;
/**
* The emitter for your events,
* by default the class instance itself (this)
*/
emitter?: Window | Document;
}
```
Which includes:
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "event-emitter-class-method-decorator",
"version": "0.0.1",
"version": "1.0.0",
"description": "Class method decorator to emit custom events",
"type": "module",
"main": "lib/event-emitter.js",
Expand All @@ -27,5 +27,9 @@
"@web/dev-server": "^0.3.0",
"tslib": "^2.6.0",
"typescript": "^5.1.6"
},
"repository": {
"type": "git",
"url": "https://github.com/TonySpegel/event-emitter"
}
}
49 changes: 33 additions & 16 deletions src/event-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,45 @@ interface EmitterConfig extends CustomEventInit {
/**
* The event name you can listen to
*/
name?: string;
name: string;
/**
* The emitter for your events,
* by default the class instance (this)
*/
emitter?: Window | Document;
}

/**
* This decorator emits a custom event by wrapping a class method
* and replacing it with a new one. It's meant to be used as
* one way for web components to communicate which each other
* and replacing it with a new one. It's meant to be used as
* one way for web components to communicate which each other
* but isn't limited to it.
*
* Its event name is by default the decorated method's name.
* The value that is emitted in the details property is the
* decorated method's return value. Both (and more) can be set as you wish.
*
* Its event name must be set. The 'detail' property transports
* the decorated method's return value. By default the emitter
* dispatching events is the instance itself and the event is
* set to bubble up.
*/
export function eventEmitter(config?: EmitterConfig) {
return function <This, Args extends any[], Return>(
export function eventEmitter(config: EmitterConfig) {
return function <This extends EventTarget, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<
_context: ClassMethodDecoratorContext<
This,
(this: This, ...args: Args) => Return
>
) {
/**
* Extracts the event name either from the provided config or context.
* When no config object / name is given, the decorated method's name will be used.
* Throw an error if no config or no name has been set.
*/
const eventName = config?.name ?? String(context.name);
if (!config?.name) {
throw new Error(
`The "name"-property in the config is mandatory. It is used to name your custom event.`
);
}
/**
* Extracts the event name from the provided config.
*/
const { name } = config;
/**
* The method which will replace the original one that emits a custom event
*/
Expand All @@ -49,10 +62,14 @@ export function eventEmitter(config?: EmitterConfig) {
* 'detail' is by default what is set with the config and if not
* the method's return value
*/
const detail = config?.detail ?? result;
const detail = config.detail ?? result;
/**
* The emitter which dispatches the event
*/
const emitter = config.emitter ?? this;

window.dispatchEvent(
new CustomEvent(String(eventName), { detail, ...config })
emitter.dispatchEvent(
new CustomEvent(name, { detail, bubbles: true, ...config })
);

return result;
Expand Down

0 comments on commit 748dac2

Please sign in to comment.