ueca-react 1.0.5 → 1.0.7
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 +118 -56
- package/dist/componentModel.js +46 -15
- package/dist/dynamicContent.js +7 -4
- package/dist/messageBus.d.ts +0 -1
- package/dist/messageBus.js +9 -7
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +43 -8
- package/docs/base-concepts.md +192 -0
- package/docs/general-code-structure.md +177 -0
- package/docs/index.md +24 -0
- package/docs/message-bus.md +177 -0
- package/package.json +53 -19
package/README.md
CHANGED
|
@@ -1,57 +1,119 @@
|
|
|
1
1
|

|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
2
|
+
# UECA-React
|
|
3
|
+
|
|
4
|
+
## Why UECA?
|
|
5
|
+
|
|
6
|
+
Building large-scale React apps can be a mess—complex code, bug-prone patterns, and endless refactoring. UECA (Unified Encapsulated Component Architecture) changes that. Born as a pet project, it simplifies React development by hiding the low-level chaos behind a clean, unified structure.
|
|
7
|
+
|
|
8
|
+
Curious about easier React development? Here’s what UECA brings to the table:
|
|
9
|
+
|
|
10
|
+
**Simple Learning Curve**: No React or MobX expertise needed—just plain TypeScript and JSX.
|
|
11
|
+
```typescript
|
|
12
|
+
const struct = {
|
|
13
|
+
props: {text: "" },
|
|
14
|
+
View: () => <div>{model.text}</div>
|
|
15
|
+
};
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Single Principle**: Model-View approach keeps code predictable.
|
|
19
|
+
```typescript
|
|
20
|
+
View: () => <button onClick={() => model.onClick?.()}>{model.text}</button>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Strong Typing Everywhere**: Full TypeScript support ensures safety and clarity across all components.
|
|
24
|
+
```typescript
|
|
25
|
+
// 'API.getTime' is literal type
|
|
26
|
+
model.time = await model.bus.getAsync("API.getTime");
|
|
27
|
+
|
|
28
|
+
// 'onChangeTemperature' is auto-event for property 'temperature'
|
|
29
|
+
onChangeTemperature={() => { console.log("Temperature changed"); }}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Homogeneous Structure**: Every component looks the same—easy to read, review, and test.
|
|
33
|
+
```typescript
|
|
34
|
+
type ButtonStruct = UEC.ComponentStruct<{
|
|
35
|
+
props: {
|
|
36
|
+
caption: string;
|
|
37
|
+
clickMode: "click" | "toggle";
|
|
38
|
+
active: boolean;
|
|
39
|
+
disabled: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
events: {
|
|
43
|
+
onClick: () => void;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
methods: {
|
|
47
|
+
click: () => void;
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Stateful Simplicity**: State (MobX) and React are hidden, freeing you for business logic.
|
|
53
|
+
```typescript
|
|
54
|
+
model.value = "new text"; // Fires onChangeValue event and auto-updates UI
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Powerful Bindings**: Link properties effortlessly.
|
|
58
|
+
```typescript
|
|
59
|
+
lastNameInput: useInput({
|
|
60
|
+
label: "Last Name:",
|
|
61
|
+
value: UEC.bindProp(() => model.lastName, "lastName"), // two-ways binding
|
|
62
|
+
disabled: () => !model.allowEditing // one-way binding
|
|
63
|
+
}),
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Built-in Events**: Automatic `onChange`/`onChanging` events when property changes.
|
|
67
|
+
```typescript
|
|
68
|
+
onChangeTemperature={() => { model.pressure = model.calcPressure(model.temperature); }}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Message Bus**: Async communication between components.
|
|
72
|
+
```typescript
|
|
73
|
+
messages: {
|
|
74
|
+
"API.getItemName": async (id) => await model.apiClient.get("get-item-name?id:", { id })
|
|
75
|
+
},
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Lifecycle Hooks**: `init`, `mount`, `unmount` out of the box.
|
|
79
|
+
```typescript
|
|
80
|
+
init: async () => {
|
|
81
|
+
model.itemName = await model.bus.getAsync("API.getItemName", { id: model.itemId }),
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Large Scale**: Perfect for large-scale apps, proven in production.
|
|
86
|
+
|
|
87
|
+
**Easier Project Management**: Team leads and architects breathe easier with UECA.
|
|
88
|
+
Its uniform component structure and strong typing mean predictable code—no wild variations or guesswork. Onboarding is fast, reviews are straightforward, and scaling doesn’t spiral out of control. Spend less time micromanaging and more on steering the project.
|
|
89
|
+
|
|
90
|
+
UECA saved a real-world project from collapse, turning chaos into a shipped product. Ready to simplify your React journey? Try it out!
|
|
91
|
+
|
|
92
|
+
Questions? Reach me at [cranesoft@protonmail.com](mailto:cranesoft@protonmail.com). Happy coding!
|
|
93
|
+
|
|
94
|
+
## Current Version: 1.0.7
|
|
95
|
+
|
|
96
|
+
The latest stable release of `ueca-react` is version 1.0.7. This version provides a solid UECA foundation for building React applications.
|
|
97
|
+
|
|
98
|
+
### Installation
|
|
99
|
+
```bash
|
|
100
|
+
npm install ueca-react
|
|
101
|
+
```
|
|
102
|
+
Dependency npm packages (react, react-dom, lodash) should be installed separately in your React project. Compatible React versions: 16.8.0–19.1.0. Ensure your react-dom version matches your react version.
|
|
103
|
+
|
|
104
|
+
## Upcoming Version: 2.0
|
|
105
|
+
|
|
106
|
+
Version 2.0 of `ueca-react` is in development and testing, bringing a major upgrade:
|
|
107
|
+
|
|
108
|
+
- **Fresh Core**: Rewritten from scratch for rock-solid consistency.
|
|
109
|
+
- **Dynamic Content**: Seamless support for functional components in JSX.
|
|
110
|
+
- **Model Cache**: Models persist through unmounts, reused on remount.
|
|
111
|
+
- **New Lifecycle**: `constr`, `init`, `deinit`, `mount`, `unmount`, `draw`, `erase` hooks for finer control.
|
|
112
|
+
- **Async Power**: Better async handling with centralized error management.
|
|
113
|
+
- **Advanced Messaging**: `broadcast`, `castTo`, and `unicast` for flexible communication.
|
|
114
|
+
- **Richer Event Log**: Enhanced component model logging.
|
|
115
|
+
|
|
116
|
+
Stay tuned—2.0 will turbocharge your UECA projects! Meanwhile, 1.0.7 keeps things steady.
|
|
117
|
+
|
|
118
|
+
## Getting Started
|
|
119
|
+
Check out the [examples](https://codesandbox.io/p/sandbox/frosty-banach-jsf84c) and [documentation](/docs/index.md) to start building with `ueca-react`. For questions or contributions, feel free to reach out at [cranesoft@protonmail.com](mailto:cranesoft@protonmail.com).
|
package/dist/componentModel.js
CHANGED
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.$ = exports.
|
|
4
|
-
|
|
36
|
+
exports.$ = exports.blankView = exports.ComponentModelTrap = void 0;
|
|
37
|
+
exports.useComponentModelTrap = useComponentModelTrap;
|
|
38
|
+
exports.newComponentModelTrapRegistry = newComponentModelTrapRegistry;
|
|
39
|
+
exports.useComponent = useComponent;
|
|
40
|
+
exports.mergeStruct = mergeStruct;
|
|
41
|
+
exports.bind = bind;
|
|
42
|
+
exports.bindProp = bindProp;
|
|
43
|
+
exports.componentModelDebug = componentModelDebug;
|
|
44
|
+
exports.getFC = getFC;
|
|
45
|
+
const React = __importStar(require("react"));
|
|
5
46
|
const react_1 = require("react");
|
|
6
47
|
const lodash_1 = require("lodash");
|
|
7
48
|
const mobx_1 = require("mobx");
|
|
@@ -197,7 +238,6 @@ function useComponent(struct, params) {
|
|
|
197
238
|
const model = useComponentModel(() => newComponent(struct, params), params);
|
|
198
239
|
return model;
|
|
199
240
|
}
|
|
200
|
-
exports.useComponent = useComponent;
|
|
201
241
|
function useComponentModel(modelConstructor, params) {
|
|
202
242
|
const ownerScopeContext = useComponentModelTrap();
|
|
203
243
|
const paramOwner = params === null || params === void 0 ? void 0 : params.owner;
|
|
@@ -334,7 +374,6 @@ function mergeStruct(struct, extStruct, model) {
|
|
|
334
374
|
}
|
|
335
375
|
return mergedStruct;
|
|
336
376
|
}
|
|
337
|
-
exports.mergeStruct = mergeStruct;
|
|
338
377
|
function createProxy(struct) {
|
|
339
378
|
const mobxState = {};
|
|
340
379
|
let disableOnChangeCount = 0;
|
|
@@ -426,11 +465,9 @@ function createProxy(struct) {
|
|
|
426
465
|
throw Error("Observable View is not allowed");
|
|
427
466
|
}
|
|
428
467
|
if (isReadWriteBinding(target[prop])) {
|
|
429
|
-
target[prop][1](value);
|
|
430
|
-
}
|
|
431
|
-
if (!isReadBinding(target[prop])) {
|
|
432
|
-
_assignObservableProperty(prop, value);
|
|
468
|
+
(0, mobx_1.runInAction)(() => target[prop][1](value));
|
|
433
469
|
}
|
|
470
|
+
_assignObservableProperty(prop, value);
|
|
434
471
|
}
|
|
435
472
|
else {
|
|
436
473
|
target[prop] = value;
|
|
@@ -636,7 +673,6 @@ function bind(get, set) {
|
|
|
636
673
|
const bond = [get, set];
|
|
637
674
|
return bond;
|
|
638
675
|
}
|
|
639
|
-
exports.bind = bind;
|
|
640
676
|
function bindProp(obj, prop) {
|
|
641
677
|
const bond = bind(() => {
|
|
642
678
|
let o;
|
|
@@ -659,7 +695,7 @@ function bindProp(obj, prop) {
|
|
|
659
695
|
componentModelDebug(e);
|
|
660
696
|
}
|
|
661
697
|
if (o) {
|
|
662
|
-
o[prop] = value;
|
|
698
|
+
(0, mobx_1.runInAction)(() => { o[prop] = value; });
|
|
663
699
|
}
|
|
664
700
|
else {
|
|
665
701
|
componentModelDebug(`Attempt to set bound property "${prop.toString()}" of undefined`);
|
|
@@ -667,7 +703,6 @@ function bindProp(obj, prop) {
|
|
|
667
703
|
});
|
|
668
704
|
return bond;
|
|
669
705
|
}
|
|
670
|
-
exports.bindProp = bindProp;
|
|
671
706
|
function newComponentModelTrapRegistry(cache) {
|
|
672
707
|
const trap = {
|
|
673
708
|
_hookCallsCount: -1,
|
|
@@ -714,13 +749,11 @@ function newComponentModelTrapRegistry(cache) {
|
|
|
714
749
|
};
|
|
715
750
|
return trap;
|
|
716
751
|
}
|
|
717
|
-
exports.newComponentModelTrapRegistry = newComponentModelTrapRegistry;
|
|
718
752
|
const ComponentModelTrap = (0, react_1.createContext)(undefined);
|
|
719
753
|
exports.ComponentModelTrap = ComponentModelTrap;
|
|
720
754
|
function useComponentModelTrap() {
|
|
721
755
|
return (0, react_1.useContext)(ComponentModelTrap);
|
|
722
756
|
}
|
|
723
|
-
exports.useComponentModelTrap = useComponentModelTrap;
|
|
724
757
|
function getFC(modelHook) {
|
|
725
758
|
return (params) => {
|
|
726
759
|
const hookName = modelHook.name;
|
|
@@ -732,10 +765,8 @@ function getFC(modelHook) {
|
|
|
732
765
|
return model.View();
|
|
733
766
|
};
|
|
734
767
|
}
|
|
735
|
-
exports.getFC = getFC;
|
|
736
768
|
window.componentModelDebug = false;
|
|
737
769
|
window.componentModelHashHtmlId = false;
|
|
738
770
|
function componentModelDebug(text) {
|
|
739
771
|
window.componentModelDebug && console.debug(text);
|
|
740
772
|
}
|
|
741
|
-
exports.componentModelDebug = componentModelDebug;
|
package/dist/dynamicContent.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
6
|
+
exports.DynamicContent = void 0;
|
|
7
|
+
exports.useDynamicContent = useDynamicContent;
|
|
8
|
+
const react_1 = __importDefault(require("react"));
|
|
5
9
|
const lodash_1 = require("lodash");
|
|
6
10
|
const componentModel_1 = require("./componentModel");
|
|
7
11
|
const utils_1 = require("./utils");
|
|
@@ -58,7 +62,7 @@ function useDynamicContent(params) {
|
|
|
58
62
|
model.destroyModels();
|
|
59
63
|
}
|
|
60
64
|
model._childrenFromJSX = !!(props === null || props === void 0 ? void 0 : props.children);
|
|
61
|
-
|
|
65
|
+
react_1.default.useEffect(() => {
|
|
62
66
|
trap.updateCache(model);
|
|
63
67
|
model._staticChildrenModels = trap.models;
|
|
64
68
|
});
|
|
@@ -72,6 +76,5 @@ function useDynamicContent(params) {
|
|
|
72
76
|
}
|
|
73
77
|
return model;
|
|
74
78
|
}
|
|
75
|
-
exports.useDynamicContent = useDynamicContent;
|
|
76
79
|
const DynamicContent = (0, componentModel_1.getFC)(useDynamicContent);
|
|
77
80
|
exports.DynamicContent = DynamicContent;
|
package/dist/messageBus.d.ts
CHANGED
package/dist/messageBus.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.messageBusEventPost = exports.Messaging = void 0;
|
|
4
|
+
exports.useMessaging = useMessaging;
|
|
5
|
+
exports.useCustomMessaging = useCustomMessaging;
|
|
6
|
+
exports.createMessageBus = createMessageBus;
|
|
7
|
+
exports.defaultMessageBus = defaultMessageBus;
|
|
4
8
|
const react_1 = require("react");
|
|
5
|
-
const uuid_1 = require("uuid");
|
|
6
9
|
function useMessaging(incoming, name) {
|
|
7
10
|
return useCustomMessaging((0, react_1.useContext)(Messaging), incoming, name);
|
|
8
11
|
}
|
|
9
|
-
exports.useMessaging = useMessaging;
|
|
10
12
|
function useCustomMessaging(bus, incoming, name) {
|
|
11
13
|
(0, react_1.useEffect)(() => {
|
|
12
14
|
if (incoming) {
|
|
13
15
|
const messageRecepient = {
|
|
14
|
-
id: (
|
|
16
|
+
id: generateId(),
|
|
15
17
|
name: name,
|
|
16
18
|
messages: incoming
|
|
17
19
|
};
|
|
@@ -20,8 +22,10 @@ function useCustomMessaging(bus, incoming, name) {
|
|
|
20
22
|
}
|
|
21
23
|
}, [incoming, bus, name]);
|
|
22
24
|
return bus;
|
|
25
|
+
function generateId() {
|
|
26
|
+
return Date.now().toString(36) + Math.random().toString(36).substring(2, 5);
|
|
27
|
+
}
|
|
23
28
|
}
|
|
24
|
-
exports.useCustomMessaging = useCustomMessaging;
|
|
25
29
|
function createMessageBus(name) {
|
|
26
30
|
const bus = {
|
|
27
31
|
name: name,
|
|
@@ -118,7 +122,6 @@ function createMessageBus(name) {
|
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
|
-
exports.createMessageBus = createMessageBus;
|
|
122
125
|
;
|
|
123
126
|
const messageBusEventPost = "messagebus.post";
|
|
124
127
|
exports.messageBusEventPost = messageBusEventPost;
|
|
@@ -136,4 +139,3 @@ exports.Messaging = Messaging;
|
|
|
136
139
|
function defaultMessageBus() {
|
|
137
140
|
return _defaultMessageBus;
|
|
138
141
|
}
|
|
139
|
-
exports.defaultMessageBus = defaultMessageBus;
|
package/dist/utils.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ declare function IF(props: {
|
|
|
3
3
|
condition: boolean;
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
}): JSX.Element;
|
|
6
|
-
declare function renderNode(node: React.ReactNode): any;
|
|
6
|
+
declare function renderNode(node: React.ReactNode | (() => React.ReactNode)): any;
|
|
7
7
|
declare function isSimpleNode(node: React.ReactNode): boolean;
|
|
8
8
|
export { IF, renderNode, isSimpleNode };
|
package/dist/utils.js
CHANGED
|
@@ -1,17 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
36
|
+
exports.IF = IF;
|
|
37
|
+
exports.renderNode = renderNode;
|
|
38
|
+
exports.isSimpleNode = isSimpleNode;
|
|
39
|
+
const React = __importStar(require("react"));
|
|
40
|
+
const _ = __importStar(require("lodash"));
|
|
6
41
|
function IF(props) {
|
|
7
42
|
return props.condition ? React.createElement(React.Fragment, null, renderNode(props.children)) : null;
|
|
8
43
|
}
|
|
9
|
-
exports.IF = IF;
|
|
10
44
|
function renderNode(node) {
|
|
11
|
-
return
|
|
45
|
+
return _.isFunction(node) ? node() : node;
|
|
12
46
|
}
|
|
13
|
-
exports.renderNode = renderNode;
|
|
14
47
|
function isSimpleNode(node) {
|
|
15
|
-
|
|
48
|
+
if (_.isString(node) || _.isNumber(node) || _.isBoolean(node) || _.isNull(node) || _.isUndefined(node)) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
16
52
|
}
|
|
17
|
-
exports.isSimpleNode = isSimpleNode;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# UECA Base Concepts
|
|
2
|
+
|
|
3
|
+
This section covers the base concepts of UECA, providing you with a foundational understanding of the architecture and its principles. By mastering these concepts, you'll be well-equipped to develop scalable, maintainable, and modular applications.
|
|
4
|
+
|
|
5
|
+
## Introduction to UECA
|
|
6
|
+
|
|
7
|
+
UECA is a modern framework designed to enhance the development of web applications by promoting encapsulation, modularity, and maintainability. It leverages TypeScript and JSX to create robust, type-safe components with minimal reliance on React-specific features.
|
|
8
|
+
|
|
9
|
+
## Core Principles of UECA
|
|
10
|
+
|
|
11
|
+
### Encapsulation
|
|
12
|
+
|
|
13
|
+
Encapsulation is a core principle of UECA, ensuring that each component manages its own state and behavior while exposing only necessary interfaces. This reduces code complexity and enhances maintainability by keeping implementation details hidden from other components.
|
|
14
|
+
|
|
15
|
+
### Declarative and Modular Code Style
|
|
16
|
+
|
|
17
|
+
UECA emphasizes a declarative and modular approach to coding. This means focusing on what the application should do rather than how it should do it, and breaking down the application into smaller, reusable components. This approach enhances readability and maintainability.
|
|
18
|
+
|
|
19
|
+
### Minimal React Usage
|
|
20
|
+
|
|
21
|
+
UECA uses React's custom hooks for component model instantiation but avoids other React features like class components, `useState`, and `useEffect`. This simplifies the architecture and makes the codebase more approachable for developers with a TypeScript background.
|
|
22
|
+
|
|
23
|
+
### Consistency
|
|
24
|
+
|
|
25
|
+
Consistency is key in UECA. By using [standard code templates](./code-template.md) and maintaining a uniform structure across components, developers can easily navigate and understand the codebase. This consistency reduces the cognitive load and helps maintain high code quality.
|
|
26
|
+
|
|
27
|
+
## Base Components of UECA
|
|
28
|
+
|
|
29
|
+
### TypeScript
|
|
30
|
+
|
|
31
|
+
TypeScript is the primary language used in UECA development. It provides static typing, enhancing code quality and maintainability. TypeScript's type system helps catch errors early and ensures that components are used correctly throughout the application.
|
|
32
|
+
|
|
33
|
+
### JSX
|
|
34
|
+
|
|
35
|
+
JSX is used to describe the UI in a syntax familiar to both JavaScript and HTML. Combined with TypeScript, JSX allows for type-checked components, reducing runtime errors and improving development efficiency.
|
|
36
|
+
|
|
37
|
+
### React Custom Hooks
|
|
38
|
+
|
|
39
|
+
Custom hooks are the only React feature heavily used in UECA. They are employed to instantiate and manage the lifecycle of component models, integrating React’s state and effect management seamlessly into UECA.
|
|
40
|
+
|
|
41
|
+
## Component Structure
|
|
42
|
+
|
|
43
|
+
A typical UECA component consists of the following sections:
|
|
44
|
+
|
|
45
|
+
### Props
|
|
46
|
+
|
|
47
|
+
Props are properties that serve as inputs to the component. They define the initial state and configuration of the component.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
props: {
|
|
51
|
+
title: "Default Title",
|
|
52
|
+
},
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Events
|
|
56
|
+
|
|
57
|
+
Events are custom events that the component can trigger and handle. They allow for interaction and communication within the component.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
events: {
|
|
61
|
+
onAction: () => {
|
|
62
|
+
console.log("Action triggered");
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Children
|
|
68
|
+
|
|
69
|
+
Children are the nested components within this component. They promote reusability and modularity by allowing components to be composed together.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
children: {
|
|
73
|
+
okButton: useButton({
|
|
74
|
+
caption: "OK",
|
|
75
|
+
onClick: () => _processOrder()
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Methods
|
|
81
|
+
|
|
82
|
+
Methods are functions that the component can execute. They define the behavior and logic of the component.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
methods: {
|
|
86
|
+
performAction: () => {
|
|
87
|
+
console.log("Action performed");
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
toolbarView: () => JSX.Element,
|
|
91
|
+
},
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Messages
|
|
95
|
+
|
|
96
|
+
Messages are used for inter-component communication via the message bus system. They promote a decoupled architecture by allowing components to send and receive messages without direct dependencies.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Component 1 - message sender
|
|
100
|
+
children: {
|
|
101
|
+
okButton: useButton({
|
|
102
|
+
caption: "OK",
|
|
103
|
+
onClick: async () => {
|
|
104
|
+
const reportData = await model.getAsync("API.getReportData", { reportId: model.reportId });
|
|
105
|
+
_updateReportPreview(reportData);
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Component 2 - message receiver
|
|
111
|
+
messages: {
|
|
112
|
+
"API.getReportData": async ({ reportId }) => await model.apiService.getReportData(reportId),
|
|
113
|
+
},
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Lifecycle Hooks
|
|
117
|
+
|
|
118
|
+
Lifecycle hooks manage the initialization, mounting, and unmounting processes of the component.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
init: () => {
|
|
122
|
+
console.log("Model create. Component initialized.");
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
mount: () => {
|
|
126
|
+
console.log("Component mounted to React DOM");
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
unmount: () => {
|
|
130
|
+
console.log("Component unmounted from React DOM");
|
|
131
|
+
},
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### View
|
|
135
|
+
|
|
136
|
+
The `View` function defines the JSX that represents the component's UI. It should end with "View" if it returns JSX.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
View: () => (
|
|
140
|
+
<div>
|
|
141
|
+
<model.toolbar.View />
|
|
142
|
+
<div>
|
|
143
|
+
<model.mainMenu.View />
|
|
144
|
+
<div>
|
|
145
|
+
<model.topBar.View />
|
|
146
|
+
<model.activeScreen.View />
|
|
147
|
+
<model.bottomBar.View />
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
<model.copirightBandView />
|
|
151
|
+
</div>
|
|
152
|
+
),
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Automatic Events
|
|
156
|
+
|
|
157
|
+
UECA supports automatic events for properties, such as `onChange$Property$` and `onChanging$Property$`. These events are triggered when a property changes, facilitating reactive programming within components.
|
|
158
|
+
|
|
159
|
+
### onChange$Property$
|
|
160
|
+
|
|
161
|
+
The `onChange$Property$` event is triggered after a property value is updated. It allows you to react to changes in the property.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
events: {
|
|
165
|
+
onChangeTitle: (newValue: string) => {
|
|
166
|
+
console.log(`Title changed to ${newValue}`);
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
onChangeMyProperty: (newValue: MyType) => {
|
|
170
|
+
console.log(`MyProperty changed to ${newValue}`);
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### onChanging$Property$
|
|
176
|
+
|
|
177
|
+
The `onChanging$Property$` event acts as a value change interceptor, allowing you to validate or modify the value before it is committed.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
events: {
|
|
181
|
+
onChangingTitle: (newValue: string, oldValue: string) => {
|
|
182
|
+
if (newValue.trim() === "") {
|
|
183
|
+
return oldValue; // Revert to old value if new value is invalid
|
|
184
|
+
}
|
|
185
|
+
return newValue;
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Conclusion
|
|
191
|
+
|
|
192
|
+
Understanding these base concepts is crucial for mastering UECA. By focusing on encapsulation, modularity, and consistency, UECA provides a robust framework for developing scalable and maintainable web applications. This tutorial will guide you through the practical applications of these concepts, helping you build efficient and high-quality components.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# UECA General Code Structure
|
|
2
|
+
|
|
3
|
+
This section will guide you through the general structure of UECA components, explaining each section with simple code examples.
|
|
4
|
+
|
|
5
|
+
## Component Structure
|
|
6
|
+
|
|
7
|
+
Each UECA component follows a [structured template](./code-template.md) that includes properties (`props`), events (`events`), children (`children`), methods (`methods`), and messages (`messages`). Let's break down each part of the structure.
|
|
8
|
+
|
|
9
|
+
### Example Component: MyComponent
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import * as UEC from "ueca-react";
|
|
14
|
+
|
|
15
|
+
// Component structure declaration
|
|
16
|
+
type MyComponentStruct = UEC.ComponentStruct<{
|
|
17
|
+
props: {
|
|
18
|
+
title: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
events: {
|
|
22
|
+
onAction: () => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
children: {
|
|
26
|
+
childComponent: ChildComponentModel;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
methods: {
|
|
30
|
+
performAction: () => void;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
type MyComponentParams = UEC.ComponentParams<MyComponentStruct>;
|
|
35
|
+
type MyComponentModel = UEC.ComponentModel<MyComponentStruct>;
|
|
36
|
+
|
|
37
|
+
function useMyComponent(params?: MyComponentParams): MyComponentModel {
|
|
38
|
+
const struct: MyComponentStruct = {
|
|
39
|
+
props: {
|
|
40
|
+
title: "Default Title",
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
events: {
|
|
44
|
+
onAction: () => {
|
|
45
|
+
console.log("Action triggered");
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
children: {
|
|
50
|
+
childComponent: useChildComponent(),
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
methods: {
|
|
54
|
+
performAction: () => {
|
|
55
|
+
console.log("Action performed");
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
messages: {
|
|
60
|
+
"Component.Message": (payload: { data: string }) => {
|
|
61
|
+
console.log(`Message received with data: ${payload.data}`);
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
init: () => {
|
|
66
|
+
console.log("Component initialized");
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
mount: () => {
|
|
70
|
+
console.log("Component mounted");
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
unmount: () => {
|
|
74
|
+
console.log("Component unmounted");
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
View: () => (
|
|
78
|
+
<div>
|
|
79
|
+
<h1>{model.title}</h1>
|
|
80
|
+
<button onClick={() => model.performAction()}>
|
|
81
|
+
Perform Action
|
|
82
|
+
</button>
|
|
83
|
+
<model.childComponent.View />
|
|
84
|
+
</div>
|
|
85
|
+
),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const model: MyComponentModel = UEC.useComponent(struct, params);
|
|
89
|
+
return model;
|
|
90
|
+
|
|
91
|
+
// Private methods
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const MyComponent = UEC.getFC(useMyComponent);
|
|
95
|
+
|
|
96
|
+
export { MyComponentModel, useMyComponent, MyComponent };
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Explanation of Each Section
|
|
100
|
+
|
|
101
|
+
1. **Props**:
|
|
102
|
+
- Props are the properties of the component, serving as the inputs. They are defined in the `props` section of the `struct`.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
props: {
|
|
106
|
+
title: "Default Title",
|
|
107
|
+
},
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
2. **Events**:
|
|
111
|
+
- Events are custom events that the component can trigger and handle. They are defined in the `events` section.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
events: {
|
|
115
|
+
onAction: () => {
|
|
116
|
+
console.log("Action triggered");
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
3. **Children**:
|
|
122
|
+
- Children are the child components nested within this component. They are defined in the `children` section.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
children: {
|
|
126
|
+
childComponent: useChildComponent(),
|
|
127
|
+
},
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
4. **Methods**:
|
|
131
|
+
- Methods are the functions that the component can execute. They are defined in the `methods` section.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
methods: {
|
|
135
|
+
performAction: () => {
|
|
136
|
+
console.log("Action performed");
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
5. **Lifecycle Hooks**:
|
|
142
|
+
- **Init**: Runs once when the component is created.
|
|
143
|
+
- **Mount**: Runs when the component mounts to React DOM.
|
|
144
|
+
- **Unmount**: Runs when the component unmounts from React DOM.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
init: () => {
|
|
148
|
+
console.log("Component initialized");
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
mount: () => {
|
|
152
|
+
console.log("Component mounted");
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
unmount: () => {
|
|
156
|
+
console.log("Component unmounted");
|
|
157
|
+
},
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
6. **View**:
|
|
161
|
+
- The `View` function defines the JSX that represents the component's UI.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
View: () => (
|
|
165
|
+
<div>
|
|
166
|
+
<h1>{model.title}</h1>
|
|
167
|
+
<button onClick={() => model.performAction()}>
|
|
168
|
+
Perform Action
|
|
169
|
+
</button>
|
|
170
|
+
<model.childComponent.View />
|
|
171
|
+
</div>
|
|
172
|
+
),
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Conclusion
|
|
176
|
+
|
|
177
|
+
This template provides a comprehensive scaffold for creating UECA components. By following this structure, you ensure that your components are modular, maintainable, and adhere to the principles of UECA. Use this guide as a reference for building new components and extending the functionality of your React application.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
## UECA Programming Documentation
|
|
4
|
+
|
|
5
|
+
### [1. Component Mental Model](/docs/component-mental-model.md)
|
|
6
|
+
|
|
7
|
+
### [2. Component Integration Model](/docs/component-intergation-model.md)
|
|
8
|
+
|
|
9
|
+
### [3. Introduction to UECA](/docs/introduction.md)
|
|
10
|
+
|
|
11
|
+
### [4. Technology of UECA ](/docs/technology.md)
|
|
12
|
+
|
|
13
|
+
### [5. Base Concepts of UECA ](/docs/base-concepts.md)
|
|
14
|
+
|
|
15
|
+
### [6. General Code Structure](/docs/general-code-structure.md)
|
|
16
|
+
|
|
17
|
+
### [7. Property Bindings ](/docs/bindings-overview.md)
|
|
18
|
+
|
|
19
|
+
### [8. Message Bus ](/docs/message-bus.md)
|
|
20
|
+
|
|
21
|
+
### [9. Standard Code Template](/docs/code-template.md)
|
|
22
|
+
|
|
23
|
+
## Code Examples on CodeSandbox
|
|
24
|
+
#### [UECA Basics: Application (Code Examples Menu)](https://codesandbox.io/p/sandbox/frosty-banach-jsf84c)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# UECA Message Bus
|
|
2
|
+
|
|
3
|
+
In Unified Encapsulated Component Architecture (UECA), the messaging system plays a crucial role in enabling components to communicate in a decoupled manner. This section will provide an overview of the messaging system, including the message type declaration and a simple example of messaging usage.
|
|
4
|
+
|
|
5
|
+
## What is the Messaging System?
|
|
6
|
+
|
|
7
|
+
The messaging system in UECA allows components to send and receive messages without direct dependencies. This promotes a modular architecture where components can interact seamlessly, enhancing maintainability and scalability. Components can post messages to the message bus and subscribe to handle specific messages, facilitating communication across different parts of the application.
|
|
8
|
+
|
|
9
|
+
## Message Type Declaration
|
|
10
|
+
|
|
11
|
+
To use the messaging system, you first need to declare the message types. This declaration specifies the structure of the messages, including the input parameters and the output data.
|
|
12
|
+
|
|
13
|
+
### Example Message Type Declaration
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Application Messages: "message-type": { in: <parameter-type>, out: <parameter-type> }
|
|
17
|
+
// Properties "in" and "out" describe value type of input and output parameters.
|
|
18
|
+
// Both properties "in" and "out" are optional
|
|
19
|
+
|
|
20
|
+
type AppMessage = {
|
|
21
|
+
"Api.GetWeatherForecast": {
|
|
22
|
+
in: { zip_code: string; date: Date };
|
|
23
|
+
out: { temperature: number; rainProbability: number };
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
In this example, the `AppMessage` type defines a message `Api.GetWeatherForecast` with input parameters `zip_code` and `date`, and output data `temperature` and `rainProbability`.
|
|
29
|
+
|
|
30
|
+
## Using the Messaging System
|
|
31
|
+
|
|
32
|
+
To use the messaging system, follow these steps:
|
|
33
|
+
|
|
34
|
+
1. **Define the Message Types**: Declare the structure of the messages as shown above.
|
|
35
|
+
2. **Subscribe to Messages**: Use the message bus to subscribe to specific messages in the components that need to handle them.
|
|
36
|
+
3. **Post Messages**: Send messages to the bus from components that need to trigger actions in other components.
|
|
37
|
+
|
|
38
|
+
### Example: WeatherForm and WeatherService Components
|
|
39
|
+
|
|
40
|
+
Let's walk through a simple example where a `WeatherForm` component sends a message to request weather data, and a `WeatherService` component handles the message and retrieves the data.
|
|
41
|
+
|
|
42
|
+
### WeatherForm Component
|
|
43
|
+
|
|
44
|
+
The `WeatherForm` component sends a message to request weather data.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import * as UEC from "ueca-react";
|
|
48
|
+
|
|
49
|
+
type WeatherFormStruct = UEC.ComponentStruct<{
|
|
50
|
+
children: {
|
|
51
|
+
zipcodeInputModel: MyInputModel;
|
|
52
|
+
dateInputModel: MyInputModel;
|
|
53
|
+
submitButtonModel: MyButtonModel;
|
|
54
|
+
weatherDisplayModel: WeatherDisplayModel;
|
|
55
|
+
};
|
|
56
|
+
}, AppMessage>;
|
|
57
|
+
|
|
58
|
+
type WeatherFormParams = UEC.ComponentParams<WeatherFormStruct, AppMessage>;
|
|
59
|
+
type WeatherFormModel = UEC.ComponentModel<WeatherFormStruct, AppMessage>;
|
|
60
|
+
|
|
61
|
+
function useWeatherForm(params?: WeatherFormParams): WeatherFormModel {
|
|
62
|
+
const struct: WeatherFormStruct = {
|
|
63
|
+
children: {
|
|
64
|
+
zipcodeInputModel: useMyInput({ placeholder: "Zip Code" }),
|
|
65
|
+
|
|
66
|
+
dateInputModel: useMyInput({ placeholder: "Date" }),
|
|
67
|
+
|
|
68
|
+
submitButtonModel: useMyButton({
|
|
69
|
+
caption: "Submit",
|
|
70
|
+
onClick: async () => {
|
|
71
|
+
await _handleSubmit();
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
|
|
75
|
+
weatherDisplayModel: useWeatherDisplay(),
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
View: () => (
|
|
79
|
+
<div>
|
|
80
|
+
<div>
|
|
81
|
+
<label>Zip Code:</label>
|
|
82
|
+
<model.zipcodeInputModel.View />
|
|
83
|
+
</div>
|
|
84
|
+
<div>
|
|
85
|
+
<label>Date:</label>
|
|
86
|
+
<model.dateInputModel.View />
|
|
87
|
+
</div>
|
|
88
|
+
<div>
|
|
89
|
+
<model.submitButtonModel.View />
|
|
90
|
+
</div>
|
|
91
|
+
<model.weatherDisplayModel.View />
|
|
92
|
+
</div>
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const model: WeatherFormModel = UEC.useComponent(struct, params);
|
|
97
|
+
return model;
|
|
98
|
+
|
|
99
|
+
// Private methods
|
|
100
|
+
async function _handleSubmit() {
|
|
101
|
+
model.weatherDisplayModel.data = await model.bus.getAsync(
|
|
102
|
+
"Api.GetWeatherForecast",
|
|
103
|
+
{
|
|
104
|
+
zip_code: model.zipcodeInputModel.value,
|
|
105
|
+
date: new Date(model.dateInputModel.value),
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const WeatherForm = UEC.getFC(useWeatherForm);
|
|
112
|
+
|
|
113
|
+
export { WeatherFormModel, useWeatherForm, WeatherForm };
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### WeatherService Component
|
|
117
|
+
|
|
118
|
+
The `WeatherService` component subscribes to the message and handles the data retrieval.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import * as UEC from "ueca-react";
|
|
122
|
+
|
|
123
|
+
// Service Component Structure
|
|
124
|
+
type WeatherServiceStruct = UEC.ComponentStruct<{}, AppMessage>;
|
|
125
|
+
|
|
126
|
+
type WeatherServiceParams = UEC.ComponentParams<WeatherServiceStruct, AppMessage>;
|
|
127
|
+
type WeatherServiceModel = UEC.ComponentModel<WeatherServiceStruct, AppMessage>;
|
|
128
|
+
|
|
129
|
+
function useWeatherService(params?: WeatherServiceParams): WeatherServiceModel {
|
|
130
|
+
const struct: WeatherServiceStruct = {
|
|
131
|
+
messages: {
|
|
132
|
+
"Api.GetWeatherForecast": async ({ zip_code, date }) => {
|
|
133
|
+
const response = await fetch(`https://api.weather.com/v3/wx/forecast/daily/5day?postalKey=${zip_code}&format=json`);
|
|
134
|
+
const data = await response.json();
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
temperature: data.temperature,
|
|
138
|
+
rainProbability: data.rainProbability,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const model: WeatherServiceModel = UEC.useComponent(struct, params);
|
|
145
|
+
return model;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const WeatherService = UEC.getFC(useWeatherService);
|
|
149
|
+
|
|
150
|
+
export { WeatherServiceModel, useWeatherService, WeatherService };
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Integration in the App
|
|
154
|
+
|
|
155
|
+
Integrate the `WeatherForm` and `WeatherService` components in the main application.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import * as React from "react";
|
|
159
|
+
import { WeatherForm } from "./WeatherForm";
|
|
160
|
+
import { WeatherService } from "./WeatherService";
|
|
161
|
+
|
|
162
|
+
function App() {
|
|
163
|
+
return (
|
|
164
|
+
<div>
|
|
165
|
+
<h1>UECA Messaging System Example</h1>
|
|
166
|
+
<WeatherForm />
|
|
167
|
+
<WeatherService />
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default App;
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Conclusion
|
|
176
|
+
|
|
177
|
+
The UECA messaging system facilitates decoupled communication between components, promoting a modular and maintainable architecture. By defining message types, subscribing to messages, and posting messages, you can create a flexible communication system within your UECA application. This overview and example demonstrate the basic usage of the messaging system, enabling you to integrate it into your projects effectively.
|
package/package.json
CHANGED
|
@@ -1,42 +1,76 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ueca-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Unified Encapsulated Component Architecture for React",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"ueca",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
6
|
+
"ueca",
|
|
7
|
+
"react",
|
|
8
|
+
"typescript",
|
|
9
|
+
"framework",
|
|
10
|
+
"abstraction",
|
|
11
|
+
"unified",
|
|
12
|
+
"encapsulated",
|
|
13
|
+
"component",
|
|
14
|
+
"architecture",
|
|
15
|
+
"message bus",
|
|
16
|
+
"bindings"
|
|
9
17
|
],
|
|
10
18
|
"main": "dist/index.js",
|
|
11
|
-
"types": "dist/index.d.ts",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
12
20
|
"repository": {
|
|
13
21
|
"type": "git",
|
|
14
22
|
"url": "https://github.com/nekutuzov/ueca-react-npm.git"
|
|
15
23
|
},
|
|
16
|
-
|
|
17
24
|
"files": [
|
|
18
25
|
"dist",
|
|
19
26
|
"docs",
|
|
20
27
|
"README.md",
|
|
21
|
-
"LICENSE"
|
|
28
|
+
"LICENSE"
|
|
22
29
|
],
|
|
23
30
|
"scripts": {
|
|
24
31
|
"build": "tsc",
|
|
25
|
-
"test": "
|
|
32
|
+
"test": "jest"
|
|
26
33
|
},
|
|
27
34
|
"author": "Aleksey Suvorov",
|
|
28
35
|
"license": "ISC",
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"
|
|
31
|
-
"mobx": "^6.12.3",
|
|
32
|
-
"mobx-react": "^9.1.1",
|
|
33
|
-
"react": "^18.3.1",
|
|
34
|
-
"react-dom": "^18.3.1",
|
|
35
|
-
"uuid": "^9.0.1"
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"mobx-react": "^9.1.1"
|
|
36
38
|
},
|
|
37
|
-
"
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"lodash": "^4.17.21"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@babel/core": "^7.26.10",
|
|
44
|
+
"@babel/preset-env": "^7.26.9",
|
|
45
|
+
"@babel/preset-react": "^7.26.3",
|
|
46
|
+
"@babel/preset-typescript": "^7.27.0",
|
|
47
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
48
|
+
"@testing-library/react": "^16.3.0",
|
|
49
|
+
"@testing-library/user-event": "^14.6.1",
|
|
50
|
+
"@types/react": "^18.3.2",
|
|
51
|
+
"@types/jest": "^29.5.14",
|
|
38
52
|
"@types/lodash": "^4.17.4",
|
|
39
|
-
"@types/node": "^20.12.12",
|
|
40
|
-
"
|
|
53
|
+
"@types/node": "^20.12.12",
|
|
54
|
+
"jest": "^29.7.0",
|
|
55
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
56
|
+
"typescript": "^5.8.3"
|
|
57
|
+
},
|
|
58
|
+
"jest": {
|
|
59
|
+
"testEnvironment": "jsdom",
|
|
60
|
+
"testMatch": [
|
|
61
|
+
"**/?(*.)+(test).[jt]s?(x)"
|
|
62
|
+
],
|
|
63
|
+
"moduleFileExtensions": [
|
|
64
|
+
"ts",
|
|
65
|
+
"tsx",
|
|
66
|
+
"js",
|
|
67
|
+
"jsx"
|
|
68
|
+
],
|
|
69
|
+
"setupFilesAfterEnv": [
|
|
70
|
+
"@testing-library/jest-dom"
|
|
71
|
+
],
|
|
72
|
+
"moduleNameMapper": {
|
|
73
|
+
"ueca": "<rootDir>/src/index.ts"
|
|
74
|
+
}
|
|
41
75
|
}
|
|
42
|
-
}
|
|
76
|
+
}
|