react-sync-ui 0.1.2 → 0.1.4
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/README.md +63 -28
- package/dist/react-sync-ui.cjs.development.js +23 -22
- package/dist/react-sync-ui.cjs.development.js.map +1 -1
- package/dist/react-sync-ui.cjs.production.min.js +1 -1
- package/dist/react-sync-ui.cjs.production.min.js.map +1 -1
- package/dist/react-sync-ui.esm.js +23 -22
- package/dist/react-sync-ui.esm.js.map +1 -1
- package/dist/syncUI.d.ts +9 -8
- package/package.json +7 -6
- package/src/syncUI.tsx +65 -41
package/README.md
CHANGED
|
@@ -1,36 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-sync-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`react-sync-ui` enable to do the sequential synchronous workflow with usage of React Components.
|
|
4
4
|
|
|
5
5
|
## usage example
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
```tsx
|
|
8
|
+
<button
|
|
9
|
+
onClick={async () => {
|
|
10
|
+
// call synchronous UI workflow with promisified React components
|
|
11
|
+
const name = await syncPrompt("Fill your name");
|
|
12
|
+
|
|
13
|
+
while ((await syncPrompt(`Fill your password!`)) !== "1234") {
|
|
14
|
+
await syncAlert("Invalid password, keep trying");
|
|
15
|
+
}
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
await syncAlert(`Congratulation Mr. ${name}, you are logged in`);
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
Login
|
|
21
|
+
</button>
|
|
22
|
+
```
|
|
12
23
|
|
|
13
|
-
|
|
24
|
+

|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
) {
|
|
18
|
-
await syncAlert("Invalid password, keep trying");
|
|
19
|
-
}
|
|
26
|
+
Library provides simple `makeSyncUI<InputData, ResolveValue>` function which can transform your declarative React Components
|
|
27
|
+
into the promisified callable functions.
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
```tsx
|
|
30
|
+
// defining of your custom react-sync-ui component
|
|
31
|
+
export const syncAlert = makeSyncUI<string, void>((props) => (
|
|
32
|
+
<Modal isOpen toggle={() => props.resolve()}>
|
|
33
|
+
<ModalHeader>{props.data}</ModalHeader>
|
|
34
|
+
<ModalFooter>
|
|
35
|
+
<Button onClick={() => props.resolve()}>OK</Button>
|
|
36
|
+
</ModalFooter>
|
|
37
|
+
</Modal>
|
|
38
|
+
);
|
|
23
39
|
```
|
|
24
40
|
|
|
41
|
+
## what problem is `react-sync-ui` solving?
|
|
42
|
+
|
|
43
|
+
For a long time, I did not like that React's functional way of declarative UI forced you to write nice UI code, but with ugly distributed business logic.
|
|
44
|
+
When you have a complex business use case which is a composition of asynchronous actions like HTTP requests together with user interactions,
|
|
45
|
+
your business logic code is distributed over many async React handlers and it's really hard to understand the sequence of operations and what is going on.
|
|
46
|
+
|
|
47
|
+
People very often solve this problem by dependencies in the `useEffect(..., [dependency])` which transfer react logic to the event-driven architecture.
|
|
48
|
+
When you change the variable value, the different components will register the dependency change inside of `useEffect`, and some other code is called.
|
|
49
|
+
With this event-driven programming, your code complexity is at least 1000 times more complex, and your newcomers who see the code have no idea what the business workflow is.
|
|
50
|
+
|
|
51
|
+
A nice solution for this problem is to wrap your declarative React components into Promise wrappers which split your UIs into many
|
|
52
|
+
smaller functions that can be simply composed together inside of your Javascript function and you'll get very complex business logic in just a few lines of code.
|
|
53
|
+
|
|
54
|
+
Thanks to `react-sync-ui` you may implement just your custom UI and don't have to care about the implementation detail of promise wrapping.
|
|
55
|
+
|
|
56
|
+
And that's why `react-sync-ui` was created. ❤
|
|
57
|
+
|
|
25
58
|
## Installation
|
|
26
59
|
|
|
27
|
-
```
|
|
60
|
+
```bash
|
|
28
61
|
npm i react-sync-ui
|
|
29
62
|
```
|
|
30
63
|
|
|
31
64
|
## Usage
|
|
32
65
|
|
|
33
|
-
### Setup SyncUI Component into the root of yout project
|
|
66
|
+
### Setup `<SyncUI />` Component into the root of yout project
|
|
34
67
|
|
|
35
68
|
```tsx
|
|
36
69
|
import { SyncUI } from 'react-sync-ui'
|
|
@@ -42,21 +75,23 @@ const App = () => (
|
|
|
42
75
|
<>
|
|
43
76
|
)
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
const root = createRoot(document.getElementById('root')!);
|
|
79
|
+
root.render(<App />);
|
|
46
80
|
|
|
47
81
|
```
|
|
48
82
|
|
|
49
|
-
###
|
|
83
|
+
### create sync UI
|
|
50
84
|
|
|
51
|
-
|
|
85
|
+
Now, you just have to define your custom React component which will be promisifed by `makeSyncUI` function.
|
|
52
86
|
|
|
53
|
-
|
|
87
|
+
`makeSyncUI` returns Promise which render your custom React Component.
|
|
88
|
+
Promise will be resolve when you call `props.resolve(any)` inside of your custom UI.
|
|
54
89
|
|
|
55
90
|
#### Alert example
|
|
56
91
|
|
|
57
92
|
```tsx
|
|
58
93
|
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap";
|
|
59
|
-
import {
|
|
94
|
+
import { makeSyncUI } from "react-sync-ui";
|
|
60
95
|
|
|
61
96
|
export const syncAlert = makeSyncUI<string, void>((props) => (
|
|
62
97
|
<Modal isOpen={true} toggle={() => props.resolve()}>
|
|
@@ -68,10 +103,12 @@ export const syncAlert = makeSyncUI<string, void>((props) => (
|
|
|
68
103
|
);
|
|
69
104
|
|
|
70
105
|
|
|
71
|
-
|
|
106
|
+
// usage:
|
|
72
107
|
|
|
73
108
|
<button
|
|
74
109
|
onClick={async () => {
|
|
110
|
+
await syncAlert("Wait for it...");
|
|
111
|
+
await syncAlert("Wait for it...");
|
|
75
112
|
await syncAlert("You're hacked");
|
|
76
113
|
}}
|
|
77
114
|
>
|
|
@@ -83,7 +120,7 @@ export const syncAlert = makeSyncUI<string, void>((props) => (
|
|
|
83
120
|
#### Prompt example
|
|
84
121
|
|
|
85
122
|
```tsx
|
|
86
|
-
import { Button, Modal, ModalBody, ModalFooter
|
|
123
|
+
import { Button, Modal, ModalBody, ModalFooter } from "reactstrap";
|
|
87
124
|
import { makeSyncUI } from "react-sync-ui";
|
|
88
125
|
|
|
89
126
|
export const syncPrompt = makeSyncUI<string, string>((props) => {
|
|
@@ -101,8 +138,6 @@ export const syncPrompt = makeSyncUI<string, string>((props) => {
|
|
|
101
138
|
props.resolve(input);
|
|
102
139
|
}}
|
|
103
140
|
>
|
|
104
|
-
<ModalHeader>{props.data.title}</ModalHeader>
|
|
105
|
-
|
|
106
141
|
<ModalBody>
|
|
107
142
|
<label>
|
|
108
143
|
{props.data}
|
|
@@ -122,7 +157,7 @@ export const syncPrompt = makeSyncUI<string, string>((props) => {
|
|
|
122
157
|
);
|
|
123
158
|
});
|
|
124
159
|
|
|
125
|
-
|
|
160
|
+
// usage:
|
|
126
161
|
|
|
127
162
|
<button
|
|
128
163
|
onClick={async () => {
|
|
@@ -137,7 +172,7 @@ export const syncPrompt = makeSyncUI<string, string>((props) => {
|
|
|
137
172
|
|
|
138
173
|
```tsx
|
|
139
174
|
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap";
|
|
140
|
-
import { makeSyncUI } from "
|
|
175
|
+
import { makeSyncUI } from "react-sync-ui";
|
|
141
176
|
|
|
142
177
|
export const syncConfirm = makeSyncUI<
|
|
143
178
|
{
|
|
@@ -164,7 +199,7 @@ export const syncConfirm = makeSyncUI<
|
|
|
164
199
|
</Modal>
|
|
165
200
|
));
|
|
166
201
|
|
|
167
|
-
|
|
202
|
+
// usage:
|
|
168
203
|
|
|
169
204
|
<button
|
|
170
205
|
onClick={async () => {
|
|
@@ -11,7 +11,7 @@ var useComponentDidMount = function useComponentDidMount(fn) {
|
|
|
11
11
|
React.useEffect(fn, []);
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var getSingletonComponentCheck = function getSingletonComponentCheck(errorMsg) {
|
|
15
15
|
var globalMountCounter = 0;
|
|
16
16
|
return function () {
|
|
17
17
|
useComponentDidMount(function () {
|
|
@@ -20,21 +20,19 @@ var getSingletonCompCheck = function getSingletonCompCheck(errorMsg) {
|
|
|
20
20
|
});
|
|
21
21
|
return React__default.createElement(React__default.Fragment, null);
|
|
22
22
|
};
|
|
23
|
-
};
|
|
24
|
-
|
|
23
|
+
};
|
|
25
24
|
|
|
26
|
-
var
|
|
27
|
-
var _asyncQueue
|
|
25
|
+
var usePromiseQueue = function usePromiseQueue() {
|
|
26
|
+
var _asyncQueue$;
|
|
28
27
|
|
|
29
28
|
var _useState = React.useState([]),
|
|
30
29
|
asyncQueue = _useState[0],
|
|
31
30
|
setAsyncQueue = _useState[1];
|
|
32
31
|
|
|
33
|
-
var
|
|
32
|
+
var push = React.useCallback(function (data) {
|
|
34
33
|
return new Promise(function (resolve, reject) {
|
|
35
34
|
return setAsyncQueue(function (p) {
|
|
36
35
|
return [].concat(p, [{
|
|
37
|
-
type: type,
|
|
38
36
|
data: data,
|
|
39
37
|
resolve: resolve,
|
|
40
38
|
reject: reject
|
|
@@ -61,52 +59,55 @@ var useAsyncQueue = function useAsyncQueue() {
|
|
|
61
59
|
return {
|
|
62
60
|
head: asyncQueue[0] ? {
|
|
63
61
|
data: (_asyncQueue$ = asyncQueue[0]) == null ? void 0 : _asyncQueue$.data,
|
|
64
|
-
type: (_asyncQueue$2 = asyncQueue[0]) == null ? void 0 : _asyncQueue$2.type,
|
|
65
62
|
resolve: resolveHeadItem,
|
|
66
63
|
reject: rejectHeadItem
|
|
67
64
|
} : undefined,
|
|
68
|
-
|
|
65
|
+
push: push
|
|
69
66
|
};
|
|
70
|
-
};
|
|
67
|
+
}; // ------------------------------------------------------------------------------------
|
|
68
|
+
|
|
71
69
|
var syncUIFactory = function syncUIFactory() {
|
|
72
70
|
var mutSyncUIComponentsRenderQueue = [];
|
|
73
|
-
var ThrowIfMoreInstances =
|
|
71
|
+
var ThrowIfMoreInstances = getSingletonComponentCheck("<SyncUI /> has to be initialized only once");
|
|
74
72
|
return {
|
|
75
73
|
makeSyncUI: function makeSyncUI(SyncUIUserComp) {
|
|
76
74
|
var _ref, _SyncUIUserComp$displ;
|
|
77
75
|
|
|
78
76
|
var _debugName = (_ref = (_SyncUIUserComp$displ = SyncUIUserComp.displayName) != null ? _SyncUIUserComp$displ : SyncUIUserComp.name) != null ? _ref : "uniqSymbolMessageType";
|
|
79
77
|
|
|
80
|
-
var syncUIComponentType = Symbol(_debugName); // object with the
|
|
78
|
+
var syncUIComponentType = Symbol(_debugName); // object pointer reference with the push key has to be there to change returned mutable reference object while the function is already called
|
|
81
79
|
|
|
82
80
|
var singletonSyncUIRef = {
|
|
83
|
-
|
|
81
|
+
push: undefined
|
|
84
82
|
};
|
|
85
83
|
|
|
86
|
-
var
|
|
84
|
+
var SyncUISingletonComponent = function SyncUISingletonComponent(props) {
|
|
87
85
|
useComponentDidMount(function () {
|
|
88
|
-
singletonSyncUIRef.
|
|
86
|
+
singletonSyncUIRef.push = props.push;
|
|
89
87
|
return function () {
|
|
90
|
-
return singletonSyncUIRef.
|
|
88
|
+
return singletonSyncUIRef.push = undefined;
|
|
91
89
|
};
|
|
92
90
|
});
|
|
93
91
|
if (!props.head) return null;
|
|
94
|
-
if (props.head.type !== syncUIComponentType) return null;
|
|
92
|
+
if (props.head.data.type !== syncUIComponentType) return null;
|
|
95
93
|
return React__default.createElement(SyncUIUserComp, {
|
|
96
|
-
data: props.head.data,
|
|
94
|
+
data: props.head.data.inputData,
|
|
97
95
|
resolve: props.head.resolve,
|
|
98
96
|
reject: props.head.reject
|
|
99
97
|
});
|
|
100
98
|
};
|
|
101
99
|
|
|
102
|
-
mutSyncUIComponentsRenderQueue.push(
|
|
100
|
+
mutSyncUIComponentsRenderQueue.push(SyncUISingletonComponent);
|
|
103
101
|
return function (input) {
|
|
104
|
-
if (!singletonSyncUIRef.
|
|
105
|
-
return singletonSyncUIRef.
|
|
102
|
+
if (!singletonSyncUIRef.push) throw new Error("You have to initialize <SyncUI />");
|
|
103
|
+
return singletonSyncUIRef.push({
|
|
104
|
+
type: syncUIComponentType,
|
|
105
|
+
inputData: input
|
|
106
|
+
});
|
|
106
107
|
};
|
|
107
108
|
},
|
|
108
109
|
SyncUI: function SyncUI() {
|
|
109
|
-
var queue =
|
|
110
|
+
var queue = usePromiseQueue();
|
|
110
111
|
return React__default.createElement(React__default.Fragment, null, React__default.createElement(ThrowIfMoreInstances, null), mutSyncUIComponentsRenderQueue.map(function (SyncComp, key) {
|
|
111
112
|
return React__default.createElement(React__default.Fragment, {
|
|
112
113
|
key: key
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-sync-ui.cjs.development.js","sources":["../src/syncUI.tsx","../src/index.ts"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\n\nexport const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {\n useEffect(fn, []);\n};\n\nconst
|
|
1
|
+
{"version":3,"file":"react-sync-ui.cjs.development.js","sources":["../src/syncUI.tsx","../src/index.ts"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\n\nexport const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {\n useEffect(fn, []);\n};\n\nconst getSingletonComponentCheck = (errorMsg: string) => {\n let globalMountCounter = 0;\n\n return () => {\n useComponentDidMount(() => {\n if (globalMountCounter > 0) throw new Error(errorMsg);\n globalMountCounter++;\n });\n return <React.Fragment />;\n };\n};\n\n// ------------------------------------------------------------------------------------\n// TODO: there is tsdx old typescript parser and new ts fancy syntax is not working...\n// https://github.com/jaredpalmer/tsdx/issues/200\n// type PromiseQueueAPI<Data, ResolveValue> = ReturnType<typeof usePromiseQueue<any, any>>\ntype PromiseQueueAPI<Data, ResolveValue> = {\n head?: {\n data: Data;\n resolve: (value: ResolveValue) => void;\n reject: (reason?: any) => void;\n };\n push: (data: Data) => Promise<ResolveValue>;\n};\n\nexport const usePromiseQueue = <Data, ResolveValue = void>(): PromiseQueueAPI<\n Data,\n ResolveValue\n> => {\n const [asyncQueue, setAsyncQueue] = useState(\n [] as {\n data: Data;\n resolve: (arg: ResolveValue) => void;\n reject: (arg: any) => void;\n }[]\n );\n\n const push = useCallback((data: Data) => {\n return new Promise<ResolveValue>((resolve, reject) =>\n setAsyncQueue(p => [...p, { data, resolve, reject }])\n );\n }, []);\n\n const resolveHeadItem = useCallback((value: ResolveValue) => {\n setAsyncQueue(queue => {\n const [first, ...rest] = queue;\n first?.resolve(value);\n return rest;\n });\n }, []);\n\n const rejectHeadItem = useCallback((reason?: any) => {\n setAsyncQueue(queue => {\n const [first, ...rest] = queue;\n first?.reject(reason);\n return rest;\n });\n }, []);\n\n return {\n head: asyncQueue[0]\n ? {\n data: asyncQueue[0]?.data,\n resolve: resolveHeadItem,\n reject: rejectHeadItem\n }\n : undefined,\n push\n };\n};\n\n// ------------------------------------------------------------------------------------\n\nexport const syncUIFactory = () => {\n const mutSyncUIComponentsRenderQueue = [] as React.FC<\n PromiseQueueAPI<any, any>\n >[];\n\n const ThrowIfMoreInstances = getSingletonComponentCheck(\n \"<SyncUI /> has to be initialized only once\"\n );\n\n return {\n makeSyncUI: <InputData, ResolveValue = void>(\n SyncUIUserComp: React.FC<{\n data: InputData;\n resolve: (value: ResolveValue) => void;\n reject: (reason?: any) => void;\n }>\n ) => {\n type QItem = PromiseQueueAPI<\n { type: Symbol; inputData: InputData },\n ResolveValue\n >;\n\n const _debugName =\n SyncUIUserComp.displayName ??\n SyncUIUserComp.name ??\n \"uniqSymbolMessageType\";\n\n const syncUIComponentType = Symbol(_debugName);\n\n // object pointer reference with the push key has to be there to change returned mutable reference object while the function is already called\n const singletonSyncUIRef = {\n push: undefined as undefined | QItem[\"push\"]\n };\n\n const SyncUISingletonComponent = (props: QItem) => {\n useComponentDidMount(() => {\n singletonSyncUIRef.push = props.push;\n return () => (singletonSyncUIRef.push = undefined);\n });\n\n if (!props.head) return null;\n if (props.head.data.type !== syncUIComponentType) return null;\n\n return (\n <SyncUIUserComp\n data={props.head.data.inputData}\n resolve={props.head.resolve}\n reject={props.head.reject}\n />\n );\n };\n\n mutSyncUIComponentsRenderQueue.push(SyncUISingletonComponent);\n\n return (input: InputData) => {\n if (!singletonSyncUIRef.push)\n throw new Error(`You have to initialize <SyncUI />`);\n return singletonSyncUIRef.push({\n type: syncUIComponentType,\n inputData: input\n });\n };\n },\n SyncUI: () => {\n const queue = usePromiseQueue();\n return (\n <>\n <ThrowIfMoreInstances />\n {mutSyncUIComponentsRenderQueue.map((SyncComp, key) => (\n <React.Fragment key={key}>\n <SyncComp {...queue} />\n </React.Fragment>\n ))}\n </>\n );\n }\n };\n};\n","import { syncUIFactory as _syncUIFactory } from \"./syncUI\";\nexport const syncUIFactory = _syncUIFactory;\nexport const { makeSyncUI, SyncUI } = syncUIFactory();\n"],"names":["useComponentDidMount","fn","useEffect","getSingletonComponentCheck","errorMsg","globalMountCounter","Error","React","Fragment","usePromiseQueue","useState","asyncQueue","setAsyncQueue","push","useCallback","data","Promise","resolve","reject","p","resolveHeadItem","value","queue","first","rest","rejectHeadItem","reason","head","undefined","syncUIFactory","mutSyncUIComponentsRenderQueue","ThrowIfMoreInstances","makeSyncUI","SyncUIUserComp","_debugName","displayName","name","syncUIComponentType","Symbol","singletonSyncUIRef","SyncUISingletonComponent","props","type","inputData","input","SyncUI","map","SyncComp","key","_syncUIFactory"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,n=require("react"),t=(e=n)&&"object"==typeof e&&"default"in e?e.default:e,r=function(e){n.useEffect(e,[])},u=function(){var e,u=[],a=(e=0,function(){return r((function(){if(e>0)throw new Error("<SyncUI /> has to be initialized only once");e++})),t.createElement(t.Fragment,null)});return{makeSyncUI:function(e){var n,a,c=null!=(n=null!=(a=e.displayName)?a:e.name)?n:"uniqSymbolMessageType",l=Symbol(c),o={push:void 0};return u.push((function(n){return r((function(){return o.push=n.push,function(){return o.push=void 0}})),n.head?n.head.data.type!==l?null:t.createElement(e,{data:n.head.data.inputData,resolve:n.head.resolve,reject:n.head.reject}):null})),function(e){if(!o.push)throw new Error("You have to initialize <SyncUI />");return o.push({type:l,inputData:e})}},SyncUI:function(){var e,r,c,l,o,i,s,f=(c=(r=n.useState([]))[0],l=r[1],o=n.useCallback((function(e){return new Promise((function(n,t){return l((function(r){return[].concat(r,[{data:e,resolve:n,reject:t}])}))}))}),[]),i=n.useCallback((function(e){l((function(n){var t=n[0],r=n.slice(1);return null==t||t.resolve(e),r}))}),[]),s=n.useCallback((function(e){l((function(n){var t=n[0],r=n.slice(1);return null==t||t.reject(e),r}))}),[]),{head:c[0]?{data:null==(e=c[0])?void 0:e.data,resolve:i,reject:s}:void 0,push:o});return t.createElement(t.Fragment,null,t.createElement(a,null),u.map((function(e,n){return t.createElement(t.Fragment,{key:n},t.createElement(e,Object.assign({},f)))})))}}},a=u(),c=a.makeSyncUI;exports.SyncUI=a.SyncUI,exports.makeSyncUI=c,exports.syncUIFactory=u;
|
|
2
2
|
//# sourceMappingURL=react-sync-ui.cjs.production.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-sync-ui.cjs.production.min.js","sources":["../src/syncUI.tsx","../src/index.ts"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\n\nexport const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {\n useEffect(fn, []);\n};\n\nconst
|
|
1
|
+
{"version":3,"file":"react-sync-ui.cjs.production.min.js","sources":["../src/syncUI.tsx","../src/index.ts"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\n\nexport const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {\n useEffect(fn, []);\n};\n\nconst getSingletonComponentCheck = (errorMsg: string) => {\n let globalMountCounter = 0;\n\n return () => {\n useComponentDidMount(() => {\n if (globalMountCounter > 0) throw new Error(errorMsg);\n globalMountCounter++;\n });\n return <React.Fragment />;\n };\n};\n\n// ------------------------------------------------------------------------------------\n// TODO: there is tsdx old typescript parser and new ts fancy syntax is not working...\n// https://github.com/jaredpalmer/tsdx/issues/200\n// type PromiseQueueAPI<Data, ResolveValue> = ReturnType<typeof usePromiseQueue<any, any>>\ntype PromiseQueueAPI<Data, ResolveValue> = {\n head?: {\n data: Data;\n resolve: (value: ResolveValue) => void;\n reject: (reason?: any) => void;\n };\n push: (data: Data) => Promise<ResolveValue>;\n};\n\nexport const usePromiseQueue = <Data, ResolveValue = void>(): PromiseQueueAPI<\n Data,\n ResolveValue\n> => {\n const [asyncQueue, setAsyncQueue] = useState(\n [] as {\n data: Data;\n resolve: (arg: ResolveValue) => void;\n reject: (arg: any) => void;\n }[]\n );\n\n const push = useCallback((data: Data) => {\n return new Promise<ResolveValue>((resolve, reject) =>\n setAsyncQueue(p => [...p, { data, resolve, reject }])\n );\n }, []);\n\n const resolveHeadItem = useCallback((value: ResolveValue) => {\n setAsyncQueue(queue => {\n const [first, ...rest] = queue;\n first?.resolve(value);\n return rest;\n });\n }, []);\n\n const rejectHeadItem = useCallback((reason?: any) => {\n setAsyncQueue(queue => {\n const [first, ...rest] = queue;\n first?.reject(reason);\n return rest;\n });\n }, []);\n\n return {\n head: asyncQueue[0]\n ? {\n data: asyncQueue[0]?.data,\n resolve: resolveHeadItem,\n reject: rejectHeadItem\n }\n : undefined,\n push\n };\n};\n\n// ------------------------------------------------------------------------------------\n\nexport const syncUIFactory = () => {\n const mutSyncUIComponentsRenderQueue = [] as React.FC<\n PromiseQueueAPI<any, any>\n >[];\n\n const ThrowIfMoreInstances = getSingletonComponentCheck(\n \"<SyncUI /> has to be initialized only once\"\n );\n\n return {\n makeSyncUI: <InputData, ResolveValue = void>(\n SyncUIUserComp: React.FC<{\n data: InputData;\n resolve: (value: ResolveValue) => void;\n reject: (reason?: any) => void;\n }>\n ) => {\n type QItem = PromiseQueueAPI<\n { type: Symbol; inputData: InputData },\n ResolveValue\n >;\n\n const _debugName =\n SyncUIUserComp.displayName ??\n SyncUIUserComp.name ??\n \"uniqSymbolMessageType\";\n\n const syncUIComponentType = Symbol(_debugName);\n\n // object pointer reference with the push key has to be there to change returned mutable reference object while the function is already called\n const singletonSyncUIRef = {\n push: undefined as undefined | QItem[\"push\"]\n };\n\n const SyncUISingletonComponent = (props: QItem) => {\n useComponentDidMount(() => {\n singletonSyncUIRef.push = props.push;\n return () => (singletonSyncUIRef.push = undefined);\n });\n\n if (!props.head) return null;\n if (props.head.data.type !== syncUIComponentType) return null;\n\n return (\n <SyncUIUserComp\n data={props.head.data.inputData}\n resolve={props.head.resolve}\n reject={props.head.reject}\n />\n );\n };\n\n mutSyncUIComponentsRenderQueue.push(SyncUISingletonComponent);\n\n return (input: InputData) => {\n if (!singletonSyncUIRef.push)\n throw new Error(`You have to initialize <SyncUI />`);\n return singletonSyncUIRef.push({\n type: syncUIComponentType,\n inputData: input\n });\n };\n },\n SyncUI: () => {\n const queue = usePromiseQueue();\n return (\n <>\n <ThrowIfMoreInstances />\n {mutSyncUIComponentsRenderQueue.map((SyncComp, key) => (\n <React.Fragment key={key}>\n <SyncComp {...queue} />\n </React.Fragment>\n ))}\n </>\n );\n }\n };\n};\n","import { syncUIFactory as _syncUIFactory } from \"./syncUI\";\nexport const syncUIFactory = _syncUIFactory;\nexport const { makeSyncUI, SyncUI } = syncUIFactory();\n"],"names":["useComponentDidMount","fn","useEffect","syncUIFactory","globalMountCounter","mutSyncUIComponentsRenderQueue","ThrowIfMoreInstances","Error","React","Fragment","makeSyncUI","SyncUIUserComp","_debugName","displayName","name","syncUIComponentType","Symbol","singletonSyncUIRef","push","undefined","props","head","data","type","inputData","resolve","reject","input","SyncUI","asyncQueue","setAsyncQueue","resolveHeadItem","rejectHeadItem","queue","useState","useCallback","Promise","p","value","first","rest","reason","_asyncQueue$","map","SyncComp","key"],"mappings":"oJAEaA,EAAuB,SAACC,GACnCC,YAAUD,EAAI,KCFHE,ED8EgB,eAxEvBC,EAyEEC,EAAiC,GAIjCC,GA7EFF,EAAqB,EAElB,kBACLJ,GAAqB,cACfI,EAAqB,EAAG,MAAM,IAAIG,MA0ExC,8CAzEEH,OAEKI,gBAACA,EAAMC,uBA0ET,CACLC,WAAY,SACVC,WAWMC,oBACJD,EAAeE,eACfF,EAAeG,QACf,wBAEIC,EAAsBC,OAAOJ,GAG7BK,EAAqB,CACzBC,UAAMC,UAqBRd,EAA+Ba,MAlBE,SAACE,UAChCpB,GAAqB,kBACnBiB,EAAmBC,KAAOE,EAAMF,KACzB,kBAAOD,EAAmBC,UAAOC,MAGrCC,EAAMC,KACPD,EAAMC,KAAKC,KAAKC,OAASR,EAA4B,KAGvDP,gBAACG,GACCW,KAAMF,EAAMC,KAAKC,KAAKE,UACtBC,QAASL,EAAMC,KAAKI,QACpBC,OAAQN,EAAMC,KAAKK,SAPC,QAcnB,SAACC,OACDV,EAAmBC,KACtB,MAAM,IAAIX,kDACLU,EAAmBC,KAAK,CAC7BK,KAAMR,EACNS,UAAWG,MAIjBC,OAAQ,mBA3GHC,EAAYC,EAQbZ,EAMAa,EAQAC,EAsFIC,GA5GHJ,KAA6BK,WAClC,QADiBJ,OAQbZ,EAAOiB,eAAY,SAACb,UACjB,IAAIc,SAAsB,SAACX,EAASC,UACzCI,GAAc,SAAAO,mBAASA,GAAG,CAAEf,KAAAA,EAAMG,QAAAA,EAASC,OAAAA,aAE5C,IAEGK,EAAkBI,eAAY,SAACG,GACnCR,GAAc,SAAAG,OACLM,EAAkBN,KAARO,EAAQP,wBACzBM,GAAAA,EAAOd,QAAQa,GACRE,OAER,IAEGR,EAAiBG,eAAY,SAACM,GAClCX,GAAc,SAAAG,OACLM,EAAkBN,KAARO,EAAQP,wBACzBM,GAAAA,EAAOb,OAAOe,GACPD,OAER,IAEI,CACLnB,KAAMQ,EAAW,GACb,CACEP,cAAMO,EAAW,WAAXa,EAAepB,KACrBG,QAASM,EACTL,OAAQM,QAEVb,EACJD,KAAAA,WAwEIV,gCACEA,gBAACF,QACAD,EAA+BsC,KAAI,SAACC,EAAUC,UAC7CrC,gBAACA,EAAMC,UAASoC,IAAKA,GACnBrC,gBAACoC,mBAAaX,cCnJU9B,IAAvBO,IAAAA,4BAAYkB"}
|
|
@@ -4,7 +4,7 @@ var useComponentDidMount = function useComponentDidMount(fn) {
|
|
|
4
4
|
useEffect(fn, []);
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var getSingletonComponentCheck = function getSingletonComponentCheck(errorMsg) {
|
|
8
8
|
var globalMountCounter = 0;
|
|
9
9
|
return function () {
|
|
10
10
|
useComponentDidMount(function () {
|
|
@@ -13,21 +13,19 @@ var getSingletonCompCheck = function getSingletonCompCheck(errorMsg) {
|
|
|
13
13
|
});
|
|
14
14
|
return React.createElement(React.Fragment, null);
|
|
15
15
|
};
|
|
16
|
-
};
|
|
17
|
-
|
|
16
|
+
};
|
|
18
17
|
|
|
19
|
-
var
|
|
20
|
-
var _asyncQueue
|
|
18
|
+
var usePromiseQueue = function usePromiseQueue() {
|
|
19
|
+
var _asyncQueue$;
|
|
21
20
|
|
|
22
21
|
var _useState = useState([]),
|
|
23
22
|
asyncQueue = _useState[0],
|
|
24
23
|
setAsyncQueue = _useState[1];
|
|
25
24
|
|
|
26
|
-
var
|
|
25
|
+
var push = useCallback(function (data) {
|
|
27
26
|
return new Promise(function (resolve, reject) {
|
|
28
27
|
return setAsyncQueue(function (p) {
|
|
29
28
|
return [].concat(p, [{
|
|
30
|
-
type: type,
|
|
31
29
|
data: data,
|
|
32
30
|
resolve: resolve,
|
|
33
31
|
reject: reject
|
|
@@ -54,52 +52,55 @@ var useAsyncQueue = function useAsyncQueue() {
|
|
|
54
52
|
return {
|
|
55
53
|
head: asyncQueue[0] ? {
|
|
56
54
|
data: (_asyncQueue$ = asyncQueue[0]) == null ? void 0 : _asyncQueue$.data,
|
|
57
|
-
type: (_asyncQueue$2 = asyncQueue[0]) == null ? void 0 : _asyncQueue$2.type,
|
|
58
55
|
resolve: resolveHeadItem,
|
|
59
56
|
reject: rejectHeadItem
|
|
60
57
|
} : undefined,
|
|
61
|
-
|
|
58
|
+
push: push
|
|
62
59
|
};
|
|
63
|
-
};
|
|
60
|
+
}; // ------------------------------------------------------------------------------------
|
|
61
|
+
|
|
64
62
|
var syncUIFactory = function syncUIFactory() {
|
|
65
63
|
var mutSyncUIComponentsRenderQueue = [];
|
|
66
|
-
var ThrowIfMoreInstances =
|
|
64
|
+
var ThrowIfMoreInstances = getSingletonComponentCheck("<SyncUI /> has to be initialized only once");
|
|
67
65
|
return {
|
|
68
66
|
makeSyncUI: function makeSyncUI(SyncUIUserComp) {
|
|
69
67
|
var _ref, _SyncUIUserComp$displ;
|
|
70
68
|
|
|
71
69
|
var _debugName = (_ref = (_SyncUIUserComp$displ = SyncUIUserComp.displayName) != null ? _SyncUIUserComp$displ : SyncUIUserComp.name) != null ? _ref : "uniqSymbolMessageType";
|
|
72
70
|
|
|
73
|
-
var syncUIComponentType = Symbol(_debugName); // object with the
|
|
71
|
+
var syncUIComponentType = Symbol(_debugName); // object pointer reference with the push key has to be there to change returned mutable reference object while the function is already called
|
|
74
72
|
|
|
75
73
|
var singletonSyncUIRef = {
|
|
76
|
-
|
|
74
|
+
push: undefined
|
|
77
75
|
};
|
|
78
76
|
|
|
79
|
-
var
|
|
77
|
+
var SyncUISingletonComponent = function SyncUISingletonComponent(props) {
|
|
80
78
|
useComponentDidMount(function () {
|
|
81
|
-
singletonSyncUIRef.
|
|
79
|
+
singletonSyncUIRef.push = props.push;
|
|
82
80
|
return function () {
|
|
83
|
-
return singletonSyncUIRef.
|
|
81
|
+
return singletonSyncUIRef.push = undefined;
|
|
84
82
|
};
|
|
85
83
|
});
|
|
86
84
|
if (!props.head) return null;
|
|
87
|
-
if (props.head.type !== syncUIComponentType) return null;
|
|
85
|
+
if (props.head.data.type !== syncUIComponentType) return null;
|
|
88
86
|
return React.createElement(SyncUIUserComp, {
|
|
89
|
-
data: props.head.data,
|
|
87
|
+
data: props.head.data.inputData,
|
|
90
88
|
resolve: props.head.resolve,
|
|
91
89
|
reject: props.head.reject
|
|
92
90
|
});
|
|
93
91
|
};
|
|
94
92
|
|
|
95
|
-
mutSyncUIComponentsRenderQueue.push(
|
|
93
|
+
mutSyncUIComponentsRenderQueue.push(SyncUISingletonComponent);
|
|
96
94
|
return function (input) {
|
|
97
|
-
if (!singletonSyncUIRef.
|
|
98
|
-
return singletonSyncUIRef.
|
|
95
|
+
if (!singletonSyncUIRef.push) throw new Error("You have to initialize <SyncUI />");
|
|
96
|
+
return singletonSyncUIRef.push({
|
|
97
|
+
type: syncUIComponentType,
|
|
98
|
+
inputData: input
|
|
99
|
+
});
|
|
99
100
|
};
|
|
100
101
|
},
|
|
101
102
|
SyncUI: function SyncUI() {
|
|
102
|
-
var queue =
|
|
103
|
+
var queue = usePromiseQueue();
|
|
103
104
|
return React.createElement(React.Fragment, null, React.createElement(ThrowIfMoreInstances, null), mutSyncUIComponentsRenderQueue.map(function (SyncComp, key) {
|
|
104
105
|
return React.createElement(React.Fragment, {
|
|
105
106
|
key: key
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-sync-ui.esm.js","sources":["../src/syncUI.tsx","../src/index.ts"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\n\nexport const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {\n useEffect(fn, []);\n};\n\nconst
|
|
1
|
+
{"version":3,"file":"react-sync-ui.esm.js","sources":["../src/syncUI.tsx","../src/index.ts"],"sourcesContent":["import React, { useCallback, useEffect, useState } from \"react\";\n\nexport const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {\n useEffect(fn, []);\n};\n\nconst getSingletonComponentCheck = (errorMsg: string) => {\n let globalMountCounter = 0;\n\n return () => {\n useComponentDidMount(() => {\n if (globalMountCounter > 0) throw new Error(errorMsg);\n globalMountCounter++;\n });\n return <React.Fragment />;\n };\n};\n\n// ------------------------------------------------------------------------------------\n// TODO: there is tsdx old typescript parser and new ts fancy syntax is not working...\n// https://github.com/jaredpalmer/tsdx/issues/200\n// type PromiseQueueAPI<Data, ResolveValue> = ReturnType<typeof usePromiseQueue<any, any>>\ntype PromiseQueueAPI<Data, ResolveValue> = {\n head?: {\n data: Data;\n resolve: (value: ResolveValue) => void;\n reject: (reason?: any) => void;\n };\n push: (data: Data) => Promise<ResolveValue>;\n};\n\nexport const usePromiseQueue = <Data, ResolveValue = void>(): PromiseQueueAPI<\n Data,\n ResolveValue\n> => {\n const [asyncQueue, setAsyncQueue] = useState(\n [] as {\n data: Data;\n resolve: (arg: ResolveValue) => void;\n reject: (arg: any) => void;\n }[]\n );\n\n const push = useCallback((data: Data) => {\n return new Promise<ResolveValue>((resolve, reject) =>\n setAsyncQueue(p => [...p, { data, resolve, reject }])\n );\n }, []);\n\n const resolveHeadItem = useCallback((value: ResolveValue) => {\n setAsyncQueue(queue => {\n const [first, ...rest] = queue;\n first?.resolve(value);\n return rest;\n });\n }, []);\n\n const rejectHeadItem = useCallback((reason?: any) => {\n setAsyncQueue(queue => {\n const [first, ...rest] = queue;\n first?.reject(reason);\n return rest;\n });\n }, []);\n\n return {\n head: asyncQueue[0]\n ? {\n data: asyncQueue[0]?.data,\n resolve: resolveHeadItem,\n reject: rejectHeadItem\n }\n : undefined,\n push\n };\n};\n\n// ------------------------------------------------------------------------------------\n\nexport const syncUIFactory = () => {\n const mutSyncUIComponentsRenderQueue = [] as React.FC<\n PromiseQueueAPI<any, any>\n >[];\n\n const ThrowIfMoreInstances = getSingletonComponentCheck(\n \"<SyncUI /> has to be initialized only once\"\n );\n\n return {\n makeSyncUI: <InputData, ResolveValue = void>(\n SyncUIUserComp: React.FC<{\n data: InputData;\n resolve: (value: ResolveValue) => void;\n reject: (reason?: any) => void;\n }>\n ) => {\n type QItem = PromiseQueueAPI<\n { type: Symbol; inputData: InputData },\n ResolveValue\n >;\n\n const _debugName =\n SyncUIUserComp.displayName ??\n SyncUIUserComp.name ??\n \"uniqSymbolMessageType\";\n\n const syncUIComponentType = Symbol(_debugName);\n\n // object pointer reference with the push key has to be there to change returned mutable reference object while the function is already called\n const singletonSyncUIRef = {\n push: undefined as undefined | QItem[\"push\"]\n };\n\n const SyncUISingletonComponent = (props: QItem) => {\n useComponentDidMount(() => {\n singletonSyncUIRef.push = props.push;\n return () => (singletonSyncUIRef.push = undefined);\n });\n\n if (!props.head) return null;\n if (props.head.data.type !== syncUIComponentType) return null;\n\n return (\n <SyncUIUserComp\n data={props.head.data.inputData}\n resolve={props.head.resolve}\n reject={props.head.reject}\n />\n );\n };\n\n mutSyncUIComponentsRenderQueue.push(SyncUISingletonComponent);\n\n return (input: InputData) => {\n if (!singletonSyncUIRef.push)\n throw new Error(`You have to initialize <SyncUI />`);\n return singletonSyncUIRef.push({\n type: syncUIComponentType,\n inputData: input\n });\n };\n },\n SyncUI: () => {\n const queue = usePromiseQueue();\n return (\n <>\n <ThrowIfMoreInstances />\n {mutSyncUIComponentsRenderQueue.map((SyncComp, key) => (\n <React.Fragment key={key}>\n <SyncComp {...queue} />\n </React.Fragment>\n ))}\n </>\n );\n }\n };\n};\n","import { syncUIFactory as _syncUIFactory } from \"./syncUI\";\nexport const syncUIFactory = _syncUIFactory;\nexport const { makeSyncUI, SyncUI } = syncUIFactory();\n"],"names":["useComponentDidMount","fn","useEffect","getSingletonComponentCheck","errorMsg","globalMountCounter","Error","React","Fragment","usePromiseQueue","useState","asyncQueue","setAsyncQueue","push","useCallback","data","Promise","resolve","reject","p","resolveHeadItem","value","queue","first","rest","rejectHeadItem","reason","head","undefined","syncUIFactory","mutSyncUIComponentsRenderQueue","ThrowIfMoreInstances","makeSyncUI","SyncUIUserComp","_debugName","displayName","name","syncUIComponentType","Symbol","singletonSyncUIRef","SyncUISingletonComponent","props","type","inputData","input","SyncUI","map","SyncComp","key","_syncUIFactory"],"mappings":"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"}
|
package/dist/syncUI.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
2
|
export declare const useComponentDidMount: (fn: Parameters<typeof useEffect>[0]) => void;
|
|
3
|
-
|
|
4
|
-
head
|
|
3
|
+
declare type PromiseQueueAPI<Data, ResolveValue> = {
|
|
4
|
+
head?: {
|
|
5
5
|
data: Data;
|
|
6
|
-
type: Symbol;
|
|
7
6
|
resolve: (value: ResolveValue) => void;
|
|
8
7
|
reject: (reason?: any) => void;
|
|
9
|
-
}
|
|
10
|
-
|
|
8
|
+
};
|
|
9
|
+
push: (data: Data) => Promise<ResolveValue>;
|
|
11
10
|
};
|
|
11
|
+
export declare const usePromiseQueue: <Data, ResolveValue = void>() => PromiseQueueAPI<Data, ResolveValue>;
|
|
12
12
|
export declare const syncUIFactory: () => {
|
|
13
|
-
makeSyncUI: <
|
|
14
|
-
data:
|
|
13
|
+
makeSyncUI: <InputData, ResolveValue = void>(SyncUIUserComp: React.FC<{
|
|
14
|
+
data: InputData;
|
|
15
15
|
resolve: (value: ResolveValue) => void;
|
|
16
16
|
reject: (reason?: any) => void;
|
|
17
|
-
}>) => (input:
|
|
17
|
+
}>) => (input: InputData) => Promise<ResolveValue>;
|
|
18
18
|
SyncUI: () => JSX.Element;
|
|
19
19
|
};
|
|
20
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-sync-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"repository": "https://github.com/Svehla/react-sync-ui",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jakub Švehla",
|
|
@@ -29,16 +29,17 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@size-limit/preset-small-lib": "^5.0.5",
|
|
32
|
-
"@testing-library/react
|
|
33
|
-
"@types/react": "^
|
|
34
|
-
"@types/react-dom": "^
|
|
32
|
+
"@testing-library/react": "^13.4.0",
|
|
33
|
+
"@types/react": "^18.0.21",
|
|
34
|
+
"@types/react-dom": "^18.0.6",
|
|
35
35
|
"@typescript-eslint/parser": "^4.33.0",
|
|
36
36
|
"eslint-config-react-app": "^6.0.0",
|
|
37
37
|
"eslint-plugin-react": "^7.31.10",
|
|
38
38
|
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
|
|
39
39
|
"husky": "^7.0.2",
|
|
40
|
-
"react": "^
|
|
41
|
-
"react-dom": "^
|
|
40
|
+
"react": "^18.2.0",
|
|
41
|
+
"react-dom": "^18.2.0",
|
|
42
|
+
"react-test-renderer": "^18.2.0",
|
|
42
43
|
"size-limit": "^5.0.5",
|
|
43
44
|
"tsdx": "^0.14.1",
|
|
44
45
|
"tslib": "^2.3.1",
|
package/src/syncUI.tsx
CHANGED
|
@@ -4,7 +4,7 @@ export const useComponentDidMount = (fn: Parameters<typeof useEffect>[0]) => {
|
|
|
4
4
|
useEffect(fn, []);
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const getSingletonComponentCheck = (errorMsg: string) => {
|
|
8
8
|
let globalMountCounter = 0;
|
|
9
9
|
|
|
10
10
|
return () => {
|
|
@@ -16,20 +16,35 @@ const getSingletonCompCheck = (errorMsg: string) => {
|
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
//
|
|
20
|
-
|
|
19
|
+
// ------------------------------------------------------------------------------------
|
|
20
|
+
// TODO: there is tsdx old typescript parser and new ts fancy syntax is not working...
|
|
21
|
+
// https://github.com/jaredpalmer/tsdx/issues/200
|
|
22
|
+
// type PromiseQueueAPI<Data, ResolveValue> = ReturnType<typeof usePromiseQueue<any, any>>
|
|
23
|
+
type PromiseQueueAPI<Data, ResolveValue> = {
|
|
24
|
+
head?: {
|
|
25
|
+
data: Data;
|
|
26
|
+
resolve: (value: ResolveValue) => void;
|
|
27
|
+
reject: (reason?: any) => void;
|
|
28
|
+
};
|
|
29
|
+
push: (data: Data) => Promise<ResolveValue>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const usePromiseQueue = <Data, ResolveValue = void>(): PromiseQueueAPI<
|
|
33
|
+
Data,
|
|
34
|
+
ResolveValue
|
|
35
|
+
> => {
|
|
21
36
|
const [asyncQueue, setAsyncQueue] = useState(
|
|
22
37
|
[] as {
|
|
23
|
-
// TODO: rename type into typeId
|
|
24
|
-
type: Symbol;
|
|
25
38
|
data: Data;
|
|
26
39
|
resolve: (arg: ResolveValue) => void;
|
|
27
40
|
reject: (arg: any) => void;
|
|
28
41
|
}[]
|
|
29
42
|
);
|
|
30
43
|
|
|
31
|
-
const
|
|
32
|
-
return new Promise<ResolveValue>((resolve, reject) =>
|
|
44
|
+
const push = useCallback((data: Data) => {
|
|
45
|
+
return new Promise<ResolveValue>((resolve, reject) =>
|
|
46
|
+
setAsyncQueue(p => [...p, { data, resolve, reject }])
|
|
47
|
+
);
|
|
33
48
|
}, []);
|
|
34
49
|
|
|
35
50
|
const resolveHeadItem = useCallback((value: ResolveValue) => {
|
|
@@ -52,72 +67,81 @@ export const useAsyncQueue = <Data, ResolveValue = void>() => {
|
|
|
52
67
|
head: asyncQueue[0]
|
|
53
68
|
? {
|
|
54
69
|
data: asyncQueue[0]?.data,
|
|
55
|
-
type: asyncQueue[0]?.type,
|
|
56
70
|
resolve: resolveHeadItem,
|
|
57
71
|
reject: rejectHeadItem
|
|
58
72
|
}
|
|
59
73
|
: undefined,
|
|
60
|
-
|
|
74
|
+
push
|
|
61
75
|
};
|
|
62
76
|
};
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
// type QueueItem<Data, ResolveValue> = ReturnType<typeof useAsyncQueue<any, any>>
|
|
66
|
-
type QueueItem<Data, ResolveValue> = {
|
|
67
|
-
head:
|
|
68
|
-
| {
|
|
69
|
-
data: Data;
|
|
70
|
-
type: Symbol;
|
|
71
|
-
resolve: (value: ResolveValue) => void;
|
|
72
|
-
reject: (reason?: any) => void;
|
|
73
|
-
}
|
|
74
|
-
| undefined;
|
|
75
|
-
registerToQueue: (type: Symbol, data: Data) => Promise<ResolveValue>;
|
|
76
|
-
};
|
|
77
|
+
|
|
78
|
+
// ------------------------------------------------------------------------------------
|
|
77
79
|
|
|
78
80
|
export const syncUIFactory = () => {
|
|
79
|
-
const mutSyncUIComponentsRenderQueue = [] as React.FC<
|
|
81
|
+
const mutSyncUIComponentsRenderQueue = [] as React.FC<
|
|
82
|
+
PromiseQueueAPI<any, any>
|
|
83
|
+
>[];
|
|
80
84
|
|
|
81
|
-
const ThrowIfMoreInstances =
|
|
85
|
+
const ThrowIfMoreInstances = getSingletonComponentCheck(
|
|
86
|
+
"<SyncUI /> has to be initialized only once"
|
|
87
|
+
);
|
|
82
88
|
|
|
83
89
|
return {
|
|
84
|
-
makeSyncUI: <
|
|
90
|
+
makeSyncUI: <InputData, ResolveValue = void>(
|
|
85
91
|
SyncUIUserComp: React.FC<{
|
|
86
|
-
data:
|
|
92
|
+
data: InputData;
|
|
87
93
|
resolve: (value: ResolveValue) => void;
|
|
88
94
|
reject: (reason?: any) => void;
|
|
89
95
|
}>
|
|
90
96
|
) => {
|
|
91
|
-
|
|
97
|
+
type QItem = PromiseQueueAPI<
|
|
98
|
+
{ type: Symbol; inputData: InputData },
|
|
99
|
+
ResolveValue
|
|
100
|
+
>;
|
|
101
|
+
|
|
102
|
+
const _debugName =
|
|
103
|
+
SyncUIUserComp.displayName ??
|
|
104
|
+
SyncUIUserComp.name ??
|
|
105
|
+
"uniqSymbolMessageType";
|
|
92
106
|
|
|
93
107
|
const syncUIComponentType = Symbol(_debugName);
|
|
94
108
|
|
|
95
|
-
// object with the
|
|
109
|
+
// object pointer reference with the push key has to be there to change returned mutable reference object while the function is already called
|
|
96
110
|
const singletonSyncUIRef = {
|
|
97
|
-
|
|
111
|
+
push: undefined as undefined | QItem["push"]
|
|
98
112
|
};
|
|
99
113
|
|
|
100
|
-
const
|
|
114
|
+
const SyncUISingletonComponent = (props: QItem) => {
|
|
101
115
|
useComponentDidMount(() => {
|
|
102
|
-
singletonSyncUIRef.
|
|
103
|
-
return () => (singletonSyncUIRef.
|
|
116
|
+
singletonSyncUIRef.push = props.push;
|
|
117
|
+
return () => (singletonSyncUIRef.push = undefined);
|
|
104
118
|
});
|
|
105
119
|
|
|
106
120
|
if (!props.head) return null;
|
|
107
|
-
if (props.head.type !== syncUIComponentType) return null;
|
|
108
|
-
|
|
109
|
-
return
|
|
121
|
+
if (props.head.data.type !== syncUIComponentType) return null;
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<SyncUIUserComp
|
|
125
|
+
data={props.head.data.inputData}
|
|
126
|
+
resolve={props.head.resolve}
|
|
127
|
+
reject={props.head.reject}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
110
130
|
};
|
|
111
131
|
|
|
112
|
-
mutSyncUIComponentsRenderQueue.push(
|
|
132
|
+
mutSyncUIComponentsRenderQueue.push(SyncUISingletonComponent);
|
|
113
133
|
|
|
114
|
-
return (input:
|
|
115
|
-
if (!singletonSyncUIRef.
|
|
116
|
-
|
|
134
|
+
return (input: InputData) => {
|
|
135
|
+
if (!singletonSyncUIRef.push)
|
|
136
|
+
throw new Error(`You have to initialize <SyncUI />`);
|
|
137
|
+
return singletonSyncUIRef.push({
|
|
138
|
+
type: syncUIComponentType,
|
|
139
|
+
inputData: input
|
|
140
|
+
});
|
|
117
141
|
};
|
|
118
142
|
},
|
|
119
143
|
SyncUI: () => {
|
|
120
|
-
const queue =
|
|
144
|
+
const queue = usePromiseQueue();
|
|
121
145
|
return (
|
|
122
146
|
<>
|
|
123
147
|
<ThrowIfMoreInstances />
|