danholibraryjs 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,30 @@ import { BaseEvent, EventHandler } from '../../Types';
4
4
  * @borrows EventCollection
5
5
  * @borrows BaseEvent
6
6
  * @borrows EventHandler
7
+ *
8
+ * @example ```ts
9
+ * import { EventEmitter } from 'danholibraryjs';
10
+ *
11
+ * type EventTypes = {
12
+ * create: [username: string, password: string],
13
+ * update: [id: string, user: User],
14
+ * delete: [id: string, reason?: string],
15
+ * }
16
+ *
17
+ * const emitter = new EventEmitter<EventTypes>(new Map([
18
+ * ['create', (username, password) => {
19
+ * return { username, password };
20
+ * }],
21
+ * ['update', (id, user) => {
22
+ * return { id, ...user };
23
+ * }]
24
+ * ]));
25
+ *
26
+ * const onDelete = (id: string, reason?: string) => console.log(`User ${id} was deleted because ${reason}`);
27
+ * emitter.on('delete', onDelete);
28
+ * emitter.emit('delete', '1', 'No longer needed');
29
+ * emitter.off('delete', onDelete);
30
+ * ```
7
31
  */
8
32
  export declare class EventEmitter<Events extends BaseEvent<string, Array<any>>> {
9
33
  /**@param events Map<name: string, handlers: EventHandler[]>*/
@@ -10,6 +10,30 @@ const EventCollection_1 = __importDefault(require("./EventCollection"));
10
10
  * @borrows EventCollection
11
11
  * @borrows BaseEvent
12
12
  * @borrows EventHandler
13
+ *
14
+ * @example ```ts
15
+ * import { EventEmitter } from 'danholibraryjs';
16
+ *
17
+ * type EventTypes = {
18
+ * create: [username: string, password: string],
19
+ * update: [id: string, user: User],
20
+ * delete: [id: string, reason?: string],
21
+ * }
22
+ *
23
+ * const emitter = new EventEmitter<EventTypes>(new Map([
24
+ * ['create', (username, password) => {
25
+ * return { username, password };
26
+ * }],
27
+ * ['update', (id, user) => {
28
+ * return { id, ...user };
29
+ * }]
30
+ * ]));
31
+ *
32
+ * const onDelete = (id: string, reason?: string) => console.log(`User ${id} was deleted because ${reason}`);
33
+ * emitter.on('delete', onDelete);
34
+ * emitter.emit('delete', '1', 'No longer needed');
35
+ * emitter.off('delete', onDelete);
36
+ * ```
13
37
  */
14
38
  class EventEmitter {
15
39
  /**@param events Map<name: string, handlers: EventHandler[]>*/
@@ -0,0 +1,79 @@
1
+ import { Arrayable } from "../Types";
2
+ import { EventEmitter } from "./Events";
3
+ export declare type Reducer<State, Types extends Record<string, any[]>, Action extends keyof Types> = (state: State, ...args: Types[Action]) => State;
4
+ /**
5
+ * EventEmitter, but it stores state and handles state change with reducers
6
+ *
7
+ * @Initialization Actions & initial state must be defined in type parameters. InitialState must be provided in constructor, whereas reducer is optional.
8
+ * The ActionType must have properties as strings and values as arrays.
9
+ *
10
+ * @HandlingActions Reducers can be added through constructor or using Store.on('action', reducer) or Store.once('action', reducer).
11
+ * Every state change must return the next state, apart from 'stateChange', which returns void/any
12
+ * Emit/Dispatch an action using Store.dispatch('action', ...args), ...args being the parameters from the ActionType.
13
+ * Store.emit should NOT be used, as it doesn't update the Store's state.
14
+ *
15
+ * Reducer functions can be removed using Store.off('action', reducer);
16
+ *
17
+ * @borrows EventEmitter
18
+ * @borrows Arrayable
19
+ *
20
+ * @example ```ts
21
+ * import { Store } from 'danholibraryjs';
22
+ *
23
+ * type Todo = {
24
+ * id: string,
25
+ * text: string,
26
+ * completed: boolean
27
+ * }
28
+ *
29
+ * type TodoActions = {
30
+ * create: [text: string],
31
+ * update: [id: string, text: string],
32
+ * toggleComplete: [id: string, force?: boolean],
33
+ * delete: [id: string],
34
+ * }
35
+ *
36
+ * const store = new Store<Array<Todo>, TodoActions>(new Array<Todo>(), new Map([
37
+ * create: (state, text) => {
38
+ * return [...state, {
39
+ * id: Math.random().toString(),
40
+ * text,
41
+ * completed: false
42
+ * }];
43
+ * },
44
+ * toggleComplete: (state, id, force) => {
45
+ * const todo = state.find(todo => todo.id === id);
46
+ * if (!todo) return state;
47
+ *
48
+ * return state.map(todo => (
49
+ * todo.id === id ? {
50
+ * ...todo,
51
+ * completed: force === undefined ? !todo.completed : force
52
+ * } : todo
53
+ * ));
54
+ * }
55
+ * ]));
56
+ *
57
+ * store.on('delete', (state, id) => {
58
+ * return state.filter(todo => todo.id !== id);
59
+ * });
60
+ *
61
+ * store.on('stateChange', (prevState, currentState) => console.log('State change', prevState, currentState));
62
+ *
63
+ * store.dispatch('create', 'Make store!');
64
+ *
65
+ * ```
66
+ */
67
+ export declare class Store<State extends object, ActionTypes extends Record<string, any[]>, Actions extends {
68
+ [Action in keyof ActionTypes]: Array<Reducer<State, ActionTypes, Action>>;
69
+ } = {
70
+ [Action in keyof ActionTypes]: Array<Reducer<State, ActionTypes, Action>>;
71
+ }> extends EventEmitter<Record<keyof Actions, ActionTypes[keyof ActionTypes]> & Record<'stateChange', [previous: State, current: State]>> {
72
+ constructor(state: State, actions?: {
73
+ [Action in keyof ActionTypes]?: Arrayable<Reducer<State, ActionTypes, Action>>;
74
+ });
75
+ private _state;
76
+ get state(): State;
77
+ dispatch<Action extends keyof ActionTypes>(action: Action, ...args: ActionTypes[Action]): State;
78
+ }
79
+ export default Store;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Store = void 0;
4
+ const Events_1 = require("./Events");
5
+ /**
6
+ * EventEmitter, but it stores state and handles state change with reducers
7
+ *
8
+ * @Initialization Actions & initial state must be defined in type parameters. InitialState must be provided in constructor, whereas reducer is optional.
9
+ * The ActionType must have properties as strings and values as arrays.
10
+ *
11
+ * @HandlingActions Reducers can be added through constructor or using Store.on('action', reducer) or Store.once('action', reducer).
12
+ * Every state change must return the next state, apart from 'stateChange', which returns void/any
13
+ * Emit/Dispatch an action using Store.dispatch('action', ...args), ...args being the parameters from the ActionType.
14
+ * Store.emit should NOT be used, as it doesn't update the Store's state.
15
+ *
16
+ * Reducer functions can be removed using Store.off('action', reducer);
17
+ *
18
+ * @borrows EventEmitter
19
+ * @borrows Arrayable
20
+ *
21
+ * @example ```ts
22
+ * import { Store } from 'danholibraryjs';
23
+ *
24
+ * type Todo = {
25
+ * id: string,
26
+ * text: string,
27
+ * completed: boolean
28
+ * }
29
+ *
30
+ * type TodoActions = {
31
+ * create: [text: string],
32
+ * update: [id: string, text: string],
33
+ * toggleComplete: [id: string, force?: boolean],
34
+ * delete: [id: string],
35
+ * }
36
+ *
37
+ * const store = new Store<Array<Todo>, TodoActions>(new Array<Todo>(), new Map([
38
+ * create: (state, text) => {
39
+ * return [...state, {
40
+ * id: Math.random().toString(),
41
+ * text,
42
+ * completed: false
43
+ * }];
44
+ * },
45
+ * toggleComplete: (state, id, force) => {
46
+ * const todo = state.find(todo => todo.id === id);
47
+ * if (!todo) return state;
48
+ *
49
+ * return state.map(todo => (
50
+ * todo.id === id ? {
51
+ * ...todo,
52
+ * completed: force === undefined ? !todo.completed : force
53
+ * } : todo
54
+ * ));
55
+ * }
56
+ * ]));
57
+ *
58
+ * store.on('delete', (state, id) => {
59
+ * return state.filter(todo => todo.id !== id);
60
+ * });
61
+ *
62
+ * store.on('stateChange', (prevState, currentState) => console.log('State change', prevState, currentState));
63
+ *
64
+ * store.dispatch('create', 'Make store!');
65
+ *
66
+ * ```
67
+ */
68
+ class Store extends Events_1.EventEmitter {
69
+ constructor(state, actions = {}) {
70
+ super(new Map(...Object.entries(actions).map(([action, reducers]) => [action, reducers])));
71
+ this._state = state;
72
+ }
73
+ _state;
74
+ get state() {
75
+ return this._state;
76
+ }
77
+ dispatch(action, ...args) {
78
+ const previous = { ...this._state };
79
+ this._state = super.emit(action, ...args).reduce((state, returned) => ({ ...state, ...returned }), this.state);
80
+ super.emit('stateChange', ...[previous, this.state]);
81
+ return this.state;
82
+ }
83
+ }
84
+ exports.Store = Store;
85
+ exports.default = Store;
package/docs/Classes.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  ## Classes
4
4
 
5
+ ```ts
6
+ /**
7
+ * EventEmitter, but it stores state and handles state change with reducers
8
+ *
9
+ * @Initialization Actions & initial state must be defined in type parameters. InitialState must be provided in constructor, whereas reducer is optional.
10
+ * The ActionType must have properties as strings and values as arrays.
11
+ *
12
+ * @HandlingActions Reducers can be added through constructor or using Store.on('action', reducer) or Store.once('action', reducer).
13
+ * Every state change must return the next state, apart from 'stateChange', which returns void/any
14
+ * Emit/Dispatch an action using Store.dispatch('action', ...args), ...args being the parameters from the ActionType.
15
+ * Store.emit should NOT be used, as it doesn't update the Store's state.
16
+ *
17
+ * Reducer functions can be removed using Store.off('action', reducer);
18
+ *
19
+ * @borrows EventEmitter
20
+ * @borrows Arrayable
21
+ */
22
+ class Store<
23
+ State extends object,
24
+ ActionTypes extends Record<string, any[]>,
25
+ Actions extends
26
+ { [Action in keyof ActionTypes]: Array<Reducer<State, ActionTypes, Action>> } =
27
+ { [Action in keyof ActionTypes]: Array<Reducer<State, ActionTypes, Action>> }
28
+ > extends EventEmitter<Record<keyof Actions, ActionTypes[keyof ActionTypes]> & Record<'stateChange', [previous: State, current: State]>> {
29
+ constructor(state: State, actions: { [Action in keyof ActionTypes]?: Arrayable<Reducer<State, ActionTypes, Action>> } = {});
30
+
31
+ private _state: State;
32
+ public get state(): State;
33
+
34
+ public dispatch<Action extends keyof ActionTypes>(action: Action, ...args: ActionTypes[Action]): State;
35
+ }
36
+ ```
37
+
5
38
  ### Events
6
39
 
7
40
  ```ts
package/docs/Types.md CHANGED
@@ -127,3 +127,11 @@ export type TransformTypes<From, BaseType, NewType> = Record<keyof {
127
127
  [Key in keyof From as From[Key] extends BaseType ? Key : never]: Key
128
128
  }, NewType>
129
129
  ```
130
+
131
+ ### Store
132
+ ```ts
133
+ /**
134
+ * Reducer function to map wanted parameters when using @see Store.on(action, reducer);
135
+ */
136
+ export type Reducer<State, Types extends Record<string, any[]>, Action extends keyof Types> = (state: State, ...args: Types[Action]) => State
137
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danholibraryjs",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Library for Javascript.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -6,6 +6,30 @@ import { BaseEvent, EventHandler } from '../../Types';
6
6
  * @borrows EventCollection
7
7
  * @borrows BaseEvent
8
8
  * @borrows EventHandler
9
+ *
10
+ * @example ```ts
11
+ * import { EventEmitter } from 'danholibraryjs';
12
+ *
13
+ * type EventTypes = {
14
+ * create: [username: string, password: string],
15
+ * update: [id: string, user: User],
16
+ * delete: [id: string, reason?: string],
17
+ * }
18
+ *
19
+ * const emitter = new EventEmitter<EventTypes>(new Map([
20
+ * ['create', (username, password) => {
21
+ * return { username, password };
22
+ * }],
23
+ * ['update', (id, user) => {
24
+ * return { id, ...user };
25
+ * }]
26
+ * ]));
27
+ *
28
+ * const onDelete = (id: string, reason?: string) => console.log(`User ${id} was deleted because ${reason}`);
29
+ * emitter.on('delete', onDelete);
30
+ * emitter.emit('delete', '1', 'No longer needed');
31
+ * emitter.off('delete', onDelete);
32
+ * ```
9
33
  */
10
34
  export class EventEmitter<Events extends BaseEvent<string, Array<any>>> {
11
35
  /**@param events Map<name: string, handlers: EventHandler[]>*/
@@ -0,0 +1,95 @@
1
+ import { Arrayable } from "../Types";
2
+ import { EventEmitter } from "./Events";
3
+
4
+ export type Reducer<State, Types extends Record<string, any[]>, Action extends keyof Types> = (state: State, ...args: Types[Action]) => State
5
+
6
+ /**
7
+ * EventEmitter, but it stores state and handles state change with reducers
8
+ *
9
+ * @Initialization Actions & initial state must be defined in type parameters. InitialState must be provided in constructor, whereas reducer is optional.
10
+ * The ActionType must have properties as strings and values as arrays.
11
+ *
12
+ * @HandlingActions Reducers can be added through constructor or using Store.on('action', reducer) or Store.once('action', reducer).
13
+ * Every state change must return the next state, apart from 'stateChange', which returns void/any
14
+ * Emit/Dispatch an action using Store.dispatch('action', ...args), ...args being the parameters from the ActionType.
15
+ * Store.emit should NOT be used, as it doesn't update the Store's state.
16
+ *
17
+ * Reducer functions can be removed using Store.off('action', reducer);
18
+ *
19
+ * @borrows EventEmitter
20
+ * @borrows Arrayable
21
+ *
22
+ * @example ```ts
23
+ * import { Store } from 'danholibraryjs';
24
+ *
25
+ * type Todo = {
26
+ * id: string,
27
+ * text: string,
28
+ * completed: boolean
29
+ * }
30
+ *
31
+ * type TodoActions = {
32
+ * create: [text: string],
33
+ * update: [id: string, text: string],
34
+ * toggleComplete: [id: string, force?: boolean],
35
+ * delete: [id: string],
36
+ * }
37
+ *
38
+ * const store = new Store<Array<Todo>, TodoActions>(new Array<Todo>(), new Map([
39
+ * create: (state, text) => {
40
+ * return [...state, {
41
+ * id: Math.random().toString(),
42
+ * text,
43
+ * completed: false
44
+ * }];
45
+ * },
46
+ * toggleComplete: (state, id, force) => {
47
+ * const todo = state.find(todo => todo.id === id);
48
+ * if (!todo) return state;
49
+ *
50
+ * return state.map(todo => (
51
+ * todo.id === id ? {
52
+ * ...todo,
53
+ * completed: force === undefined ? !todo.completed : force
54
+ * } : todo
55
+ * ));
56
+ * }
57
+ * ]));
58
+ *
59
+ * store.on('delete', (state, id) => {
60
+ * return state.filter(todo => todo.id !== id);
61
+ * });
62
+ *
63
+ * store.on('stateChange', (prevState, currentState) => console.log('State change', prevState, currentState));
64
+ *
65
+ * store.dispatch('create', 'Make store!');
66
+ *
67
+ * ```
68
+ */
69
+ export class Store<
70
+ State extends object,
71
+ ActionTypes extends Record<string, any[]>,
72
+ Actions extends
73
+ { [Action in keyof ActionTypes]: Array<Reducer<State, ActionTypes, Action>> } =
74
+ { [Action in keyof ActionTypes]: Array<Reducer<State, ActionTypes, Action>> }
75
+ > extends EventEmitter<Record<keyof Actions, ActionTypes[keyof ActionTypes]> & Record<'stateChange', [previous: State, current: State]>> {
76
+ constructor(state: State, actions: { [Action in keyof ActionTypes]?: Arrayable<Reducer<State, ActionTypes, Action>> } = {}) {
77
+ super(new Map(...Object.entries(actions).map(([action, reducers]) => [action, reducers as any])));
78
+
79
+ this._state = state;
80
+ }
81
+
82
+ private _state: State;
83
+ public get state(): State {
84
+ return this._state;
85
+ }
86
+
87
+ public dispatch<Action extends keyof ActionTypes>(action: Action, ...args: ActionTypes[Action]): State {
88
+ const previous = { ...this._state };
89
+ this._state = super.emit<State, Action>(action, ...args as any).reduce((state, returned) => ({ ...state, ...returned }), this.state);
90
+
91
+ super.emit<void, 'stateChange'>('stateChange', ...[previous, this.state] as any);
92
+ return this.state;
93
+ }
94
+ }
95
+ export default Store;