@blac/react 0.0.1
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.
- package/dist/blac-react.d.ts +29 -0
- package/dist/blac-react.esm.js +152 -0
- package/dist/blac-react.esm.js.map +1 -0
- package/dist/blac-react.js +155 -0
- package/dist/blac-react.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Blac, BlocBase, BlocClass } from 'blac';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
declare const BlacContext: React.Context<Blac | null>;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Keep track of the hierarchy of Blac BlocProvider components, so that
|
|
8
|
+
* useBloc can find the correct bloc instance.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface ProviderItem {
|
|
12
|
+
id: string;
|
|
13
|
+
parent?: string;
|
|
14
|
+
bloc: BlocBase<any>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ExternalStore<S> {
|
|
18
|
+
subscribe: (onStoreChange: () => void) => () => void;
|
|
19
|
+
getSnapshot: () => S;
|
|
20
|
+
getServerSnapshot?: () => S;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type BlocHookData<T extends BlocBase<S>, S> = [value: S, instance: T];
|
|
24
|
+
type BlocHookOptions<T extends BlocBase<S>, S> = {
|
|
25
|
+
create?: boolean;
|
|
26
|
+
};
|
|
27
|
+
declare const useBloc: <B extends BlocBase<S>, S>(bloc: BlocClass<B> | (() => B), options?: BlocHookOptions<B, S>) => BlocHookData<B, S>;
|
|
28
|
+
|
|
29
|
+
export { BlacContext, BlocHookData, BlocHookOptions, ExternalStore, ProviderItem, useBloc };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import React, { useContext, createContext, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Keep track of the hierarchy of Blac BlocProvider components, so that
|
|
5
|
+
* useBloc can find the correct bloc instance.
|
|
6
|
+
*/
|
|
7
|
+
class BlacReact {
|
|
8
|
+
blac;
|
|
9
|
+
blacContext;
|
|
10
|
+
localContextProvider = React.createContext(null);
|
|
11
|
+
static pluginKey = 'blacReact';
|
|
12
|
+
constructor(blac, blacContext) {
|
|
13
|
+
// treat this as singleton
|
|
14
|
+
const blacReact = blac.getPluginKey(BlacReact.pluginKey);
|
|
15
|
+
// new setup
|
|
16
|
+
this.blac = blac;
|
|
17
|
+
this.blacContext = blacContext;
|
|
18
|
+
this.setup();
|
|
19
|
+
if (blacReact) {
|
|
20
|
+
return blacReact;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
static getInstance() {
|
|
24
|
+
const blac = globalThis.blac;
|
|
25
|
+
if (!blac) {
|
|
26
|
+
throw new Error('BlacReact: blac instance not found');
|
|
27
|
+
}
|
|
28
|
+
const blacReact = blac.getPluginKey(BlacReact.pluginKey);
|
|
29
|
+
if (!blacReact) {
|
|
30
|
+
throw new Error('BlacReact: blacReact instance not found');
|
|
31
|
+
}
|
|
32
|
+
return blacReact;
|
|
33
|
+
}
|
|
34
|
+
setup() {
|
|
35
|
+
// register blac instance on global object
|
|
36
|
+
globalThis.blac = this.blac;
|
|
37
|
+
// add the BlacReact instance to blac
|
|
38
|
+
this.blac.addPluginKey(BlacReact.pluginKey, this);
|
|
39
|
+
}
|
|
40
|
+
useLocalBlocContext() {
|
|
41
|
+
return useContext(this.localContextProvider);
|
|
42
|
+
}
|
|
43
|
+
_contextLocalProviderKey = React.createContext('none');
|
|
44
|
+
get LocalProvider() {
|
|
45
|
+
return this._contextLocalProviderKey.Provider;
|
|
46
|
+
}
|
|
47
|
+
useLocalProviderKey() {
|
|
48
|
+
return useContext(this._contextLocalProviderKey);
|
|
49
|
+
}
|
|
50
|
+
addLocalBloc = (item) => {
|
|
51
|
+
this.providerList.push(item);
|
|
52
|
+
};
|
|
53
|
+
removeLocalBloc = (id) => {
|
|
54
|
+
const index = this.providerList.findIndex((i) => i.id === id);
|
|
55
|
+
if (!index)
|
|
56
|
+
return;
|
|
57
|
+
const item = this.providerList[index];
|
|
58
|
+
item.bloc.dispose();
|
|
59
|
+
if (index !== -1) {
|
|
60
|
+
this.providerList.splice(index, 1);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
createProviderId = () => {
|
|
64
|
+
return Math.random().toString(36).split('.')[1];
|
|
65
|
+
};
|
|
66
|
+
providerList = new Array();
|
|
67
|
+
getLocalBlocForProvider(id, blocClass) {
|
|
68
|
+
for (const providerItem of this.providerList) {
|
|
69
|
+
if (providerItem.id === id) {
|
|
70
|
+
if (providerItem.bloc instanceof blocClass) {
|
|
71
|
+
return providerItem.bloc;
|
|
72
|
+
}
|
|
73
|
+
let parent = providerItem.parent;
|
|
74
|
+
while (parent) {
|
|
75
|
+
const parentItem = this.providerList.find((i) => i.id === parent);
|
|
76
|
+
if (parentItem?.bloc instanceof blocClass) {
|
|
77
|
+
return parentItem.bloc;
|
|
78
|
+
}
|
|
79
|
+
parent = parentItem?.parent;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const BlacContext = createContext(null);
|
|
88
|
+
|
|
89
|
+
createContext(null);
|
|
90
|
+
|
|
91
|
+
const externalBlocStore = (bloc) => {
|
|
92
|
+
return {
|
|
93
|
+
subscribe: (listener) => {
|
|
94
|
+
const unSub = bloc.onStateChange(listener);
|
|
95
|
+
return () => {
|
|
96
|
+
unSub();
|
|
97
|
+
bloc.dispose();
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
getSnapshot: () => bloc.state,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// B extends BlocBase<S>,
|
|
105
|
+
const useBloc = (bloc, options = {}) => {
|
|
106
|
+
const blacReact = BlacReact.getInstance();
|
|
107
|
+
// const localCtxP = blacReact.useLocalBlocContext();
|
|
108
|
+
const localProviderKey = blacReact.useLocalProviderKey();
|
|
109
|
+
const blacInstance = useContext(BlacContext);
|
|
110
|
+
const resolvedBloc = useMemo(() => {
|
|
111
|
+
// check if its a create function or a class
|
|
112
|
+
const isFunction = bloc instanceof Function;
|
|
113
|
+
const isBloc = isFunction && bloc?.isBlacClass;
|
|
114
|
+
// if its a create function, call it
|
|
115
|
+
if (!isBloc && isFunction) {
|
|
116
|
+
return bloc();
|
|
117
|
+
}
|
|
118
|
+
// if its a class
|
|
119
|
+
if (isFunction && isBloc) {
|
|
120
|
+
const blocClass = bloc;
|
|
121
|
+
// check if the bloc is registered in the blac instance
|
|
122
|
+
if (blacInstance) {
|
|
123
|
+
const e = blacReact.getLocalBlocForProvider(localProviderKey, blocClass);
|
|
124
|
+
if (e) {
|
|
125
|
+
return e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// search in global blocs
|
|
129
|
+
const globalBloc = blacReact.blac.getBloc(blocClass);
|
|
130
|
+
if (globalBloc) {
|
|
131
|
+
return globalBloc;
|
|
132
|
+
}
|
|
133
|
+
// if it is not, check if we can create a new instance
|
|
134
|
+
if (!options.create) {
|
|
135
|
+
// creating it automatically should be opt-in
|
|
136
|
+
throw new Error('useBloc: set create to true to create a new bloc when a class constructor is passed');
|
|
137
|
+
}
|
|
138
|
+
// create a new instance -- this can cause issues if the constructor expects arguments
|
|
139
|
+
const constructed = new blocClass();
|
|
140
|
+
return constructed;
|
|
141
|
+
}
|
|
142
|
+
}, []);
|
|
143
|
+
if (!resolvedBloc) {
|
|
144
|
+
throw new Error('useBloc: bloc is undefined');
|
|
145
|
+
}
|
|
146
|
+
const { subscribe, getSnapshot } = useMemo(() => externalBlocStore(resolvedBloc), [resolvedBloc]);
|
|
147
|
+
const state = useSyncExternalStore(subscribe, getSnapshot);
|
|
148
|
+
return [state, resolvedBloc];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export { BlacContext, useBloc };
|
|
152
|
+
//# sourceMappingURL=blac-react.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blac-react.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Keep track of the hierarchy of Blac BlocProvider components, so that
|
|
7
|
+
* useBloc can find the correct bloc instance.
|
|
8
|
+
*/
|
|
9
|
+
class BlacReact {
|
|
10
|
+
blac;
|
|
11
|
+
blacContext;
|
|
12
|
+
localContextProvider = React.createContext(null);
|
|
13
|
+
static pluginKey = 'blacReact';
|
|
14
|
+
constructor(blac, blacContext) {
|
|
15
|
+
// treat this as singleton
|
|
16
|
+
const blacReact = blac.getPluginKey(BlacReact.pluginKey);
|
|
17
|
+
// new setup
|
|
18
|
+
this.blac = blac;
|
|
19
|
+
this.blacContext = blacContext;
|
|
20
|
+
this.setup();
|
|
21
|
+
if (blacReact) {
|
|
22
|
+
return blacReact;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
static getInstance() {
|
|
26
|
+
const blac = globalThis.blac;
|
|
27
|
+
if (!blac) {
|
|
28
|
+
throw new Error('BlacReact: blac instance not found');
|
|
29
|
+
}
|
|
30
|
+
const blacReact = blac.getPluginKey(BlacReact.pluginKey);
|
|
31
|
+
if (!blacReact) {
|
|
32
|
+
throw new Error('BlacReact: blacReact instance not found');
|
|
33
|
+
}
|
|
34
|
+
return blacReact;
|
|
35
|
+
}
|
|
36
|
+
setup() {
|
|
37
|
+
// register blac instance on global object
|
|
38
|
+
globalThis.blac = this.blac;
|
|
39
|
+
// add the BlacReact instance to blac
|
|
40
|
+
this.blac.addPluginKey(BlacReact.pluginKey, this);
|
|
41
|
+
}
|
|
42
|
+
useLocalBlocContext() {
|
|
43
|
+
return React.useContext(this.localContextProvider);
|
|
44
|
+
}
|
|
45
|
+
_contextLocalProviderKey = React.createContext('none');
|
|
46
|
+
get LocalProvider() {
|
|
47
|
+
return this._contextLocalProviderKey.Provider;
|
|
48
|
+
}
|
|
49
|
+
useLocalProviderKey() {
|
|
50
|
+
return React.useContext(this._contextLocalProviderKey);
|
|
51
|
+
}
|
|
52
|
+
addLocalBloc = (item) => {
|
|
53
|
+
this.providerList.push(item);
|
|
54
|
+
};
|
|
55
|
+
removeLocalBloc = (id) => {
|
|
56
|
+
const index = this.providerList.findIndex((i) => i.id === id);
|
|
57
|
+
if (!index)
|
|
58
|
+
return;
|
|
59
|
+
const item = this.providerList[index];
|
|
60
|
+
item.bloc.dispose();
|
|
61
|
+
if (index !== -1) {
|
|
62
|
+
this.providerList.splice(index, 1);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
createProviderId = () => {
|
|
66
|
+
return Math.random().toString(36).split('.')[1];
|
|
67
|
+
};
|
|
68
|
+
providerList = new Array();
|
|
69
|
+
getLocalBlocForProvider(id, blocClass) {
|
|
70
|
+
for (const providerItem of this.providerList) {
|
|
71
|
+
if (providerItem.id === id) {
|
|
72
|
+
if (providerItem.bloc instanceof blocClass) {
|
|
73
|
+
return providerItem.bloc;
|
|
74
|
+
}
|
|
75
|
+
let parent = providerItem.parent;
|
|
76
|
+
while (parent) {
|
|
77
|
+
const parentItem = this.providerList.find((i) => i.id === parent);
|
|
78
|
+
if (parentItem?.bloc instanceof blocClass) {
|
|
79
|
+
return parentItem.bloc;
|
|
80
|
+
}
|
|
81
|
+
parent = parentItem?.parent;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const BlacContext = React.createContext(null);
|
|
90
|
+
|
|
91
|
+
React.createContext(null);
|
|
92
|
+
|
|
93
|
+
const externalBlocStore = (bloc) => {
|
|
94
|
+
return {
|
|
95
|
+
subscribe: (listener) => {
|
|
96
|
+
const unSub = bloc.onStateChange(listener);
|
|
97
|
+
return () => {
|
|
98
|
+
unSub();
|
|
99
|
+
bloc.dispose();
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
getSnapshot: () => bloc.state,
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// B extends BlocBase<S>,
|
|
107
|
+
const useBloc = (bloc, options = {}) => {
|
|
108
|
+
const blacReact = BlacReact.getInstance();
|
|
109
|
+
// const localCtxP = blacReact.useLocalBlocContext();
|
|
110
|
+
const localProviderKey = blacReact.useLocalProviderKey();
|
|
111
|
+
const blacInstance = React.useContext(BlacContext);
|
|
112
|
+
const resolvedBloc = React.useMemo(() => {
|
|
113
|
+
// check if its a create function or a class
|
|
114
|
+
const isFunction = bloc instanceof Function;
|
|
115
|
+
const isBloc = isFunction && bloc?.isBlacClass;
|
|
116
|
+
// if its a create function, call it
|
|
117
|
+
if (!isBloc && isFunction) {
|
|
118
|
+
return bloc();
|
|
119
|
+
}
|
|
120
|
+
// if its a class
|
|
121
|
+
if (isFunction && isBloc) {
|
|
122
|
+
const blocClass = bloc;
|
|
123
|
+
// check if the bloc is registered in the blac instance
|
|
124
|
+
if (blacInstance) {
|
|
125
|
+
const e = blacReact.getLocalBlocForProvider(localProviderKey, blocClass);
|
|
126
|
+
if (e) {
|
|
127
|
+
return e;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// search in global blocs
|
|
131
|
+
const globalBloc = blacReact.blac.getBloc(blocClass);
|
|
132
|
+
if (globalBloc) {
|
|
133
|
+
return globalBloc;
|
|
134
|
+
}
|
|
135
|
+
// if it is not, check if we can create a new instance
|
|
136
|
+
if (!options.create) {
|
|
137
|
+
// creating it automatically should be opt-in
|
|
138
|
+
throw new Error('useBloc: set create to true to create a new bloc when a class constructor is passed');
|
|
139
|
+
}
|
|
140
|
+
// create a new instance -- this can cause issues if the constructor expects arguments
|
|
141
|
+
const constructed = new blocClass();
|
|
142
|
+
return constructed;
|
|
143
|
+
}
|
|
144
|
+
}, []);
|
|
145
|
+
if (!resolvedBloc) {
|
|
146
|
+
throw new Error('useBloc: bloc is undefined');
|
|
147
|
+
}
|
|
148
|
+
const { subscribe, getSnapshot } = React.useMemo(() => externalBlocStore(resolvedBloc), [resolvedBloc]);
|
|
149
|
+
const state = React.useSyncExternalStore(subscribe, getSnapshot);
|
|
150
|
+
return [state, resolvedBloc];
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
exports.BlacContext = BlacContext;
|
|
154
|
+
exports.useBloc = useBloc;
|
|
155
|
+
//# sourceMappingURL=blac-react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blac-react.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blac/react",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "dist/blac-react.js",
|
|
6
|
+
"module": "dist/blac-react.esm.js",
|
|
7
|
+
"typings": "dist/blac-react.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"react",
|
|
11
|
+
"typescript",
|
|
12
|
+
"rxjs",
|
|
13
|
+
"state-management",
|
|
14
|
+
"reactjs",
|
|
15
|
+
"observer-pattern",
|
|
16
|
+
"bloc",
|
|
17
|
+
"bloc-pattern"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "rollup -c rollup.config.lib.js",
|
|
22
|
+
"prettier": "prettier --write ./src",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest --watch",
|
|
25
|
+
"coverage": "vitest run --coverage"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"blac": "*"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@vitejs/plugin-react": "^3.1.0",
|
|
32
|
+
"@types/react": "^18.0.28",
|
|
33
|
+
"@types/react-dom": "^18.0.11",
|
|
34
|
+
"react": "^18.2.0",
|
|
35
|
+
"react-dom": "^18.2.0",
|
|
36
|
+
"vite": "^4.1.4",
|
|
37
|
+
"vitest": "^0.29.1"
|
|
38
|
+
}
|
|
39
|
+
}
|