@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.
@@ -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
+ }