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 CHANGED
@@ -1,57 +1,119 @@
1
1
  ![logo](/docs/logo.png)
2
-
3
- # Unified Encapsulated Component Architecture
4
-
5
- ## From the Author
6
-
7
- The UECA framework was initially developed as a fun pet project to explore the unification of building blocks (components) in React-based applications. The rationale behind this was straightforward: developing large-scale React applications, especially the business logic, using pure React and the recommended React patterns is unequivocally challenging. Even with a well-organized, experienced, and motivated development team, pure React code often appears overly complex, creating unnecessary cognitive load for developers. This complexity usually leads to poor code structure, numerous bugs, tensions among developers, and frequently missed deadlines, ultimately harming the business.
8
-
9
- React, while powerful, is a relatively low-level technology that demands developers not only have substantial React programming experience but also meticulously organize their code to avoid confusion. This approach is labor-intensive during both development and maintenance. Statistically, low-level, non-templated code tends to have more bugs than code built on a foundation of higher-order architectural principles. Ideally, low-level programming patterns should be encapsulated behind a facade of these principles.
10
-
11
- In most cases, UI application development employs a component-based approach. The user interface is constructed from various unified parts called components, and React is built on this model. However, standard React patterns force developers to worry excessively about component interactions. As a result, developers often write different code that serves similar functions. In a team environment, this issue can multiply exponentially. Normalizing such logic is either prohibitively expensive or nearly impossible. Unfortunately, businesses often prioritize enhancements over code refactoring, leaving teams to manage redundant code that becomes increasingly costly to support over time. Statistically, the likelihood of bugs in the system increases with the volume of code.
12
-
13
- UECA addresses this problem by encapsulating routine code, allowing developers to focus on high-level logic and unifying the code structure. Additionally, UECA mitigates the need for highly experienced React developers. The code pattern requires minimal React experience, making the primary technologies TypeScript and JSX. Even a group of junior developers, guided by a UI architect, can perform well using this framework.
14
-
15
- The UECA library has already been successfully employed to develop a large-scale commercial web application. Initially, the project began with pure React, but it gradually became unmanageable. Adopting UECA saved the project from shutdown, preserved developers' jobs, and ultimately allowed the product to be released. This is a true success story.
16
-
17
- Special thanks to *Artem Tsetkhalin, Roman Ilyuchonak, Roman Nekliukov, Andrey Solovyov, and Pavel Borodaev* for their testing and bug reports.
18
-
19
- If you have any questions or comments, please feel free to reach out to me at cranesoft@protonmail.com.
20
-
21
- Thank you very much for your interest in and support of UECA ideas. Happy coding! 😎
22
-
23
- ## UECA Basic Documentation
24
-
25
- Comprehensive documentation detailing UECA aspects, code patterns, and examples will be available soon.
26
-
27
- ### [1. Component Mental Model](/docs/component-mental-model.md)
28
-
29
- ### [2. Component Integration Model](/docs/component-intergation-model.md)
30
-
31
- ### [3. Introduction to UECA](/docs/introduction.md)
32
-
33
- ### [4. Technology of UECA ](/docs/technology.md)
34
-
35
- ### [5. Base Concepts of UECA ](/docs/base-concepts.md)
36
-
37
- ### [6. General Code Structure](/docs/general-code-structure.md)
38
-
39
- ### [7. Property Bindings ](/docs/bindings-overview.md)
40
-
41
- ### [8. Message Bus ](/docs/message-bus.md)
42
-
43
- ### [9. Standard Code Template](/docs/code-template.md)
44
-
45
- ## Code Examples on CodeSandbox
46
- #### [UECA Basics: Application (Code Examples Menu)](https://codesandbox.io/p/sandbox/frosty-banach-jsf84c)
47
-
48
- Additional code examples will be available soon. Check the CodeSandbox UECA project.
49
-
50
- #
51
-
52
- <iframe src="https://codesandbox.io/embed/8x7yst?view=preview&module=%2Fsrc%2Fcomponents%2FappMessages.ts&hidenavigation=1"
53
- style="width:100%; height: 500px; border:0; border-radius: 4px; overflow:hidden;"
54
- title="UECA Basics: Message Bus"
55
- allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
56
- sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
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).
@@ -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.getFC = exports.componentModelDebug = exports.bindProp = exports.bind = exports.blankView = exports.mergeStruct = exports.useComponent = exports.newComponentModelTrapRegistry = exports.useComponentModelTrap = exports.ComponentModelTrap = void 0;
4
- const React = require("react");
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;
@@ -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.useDynamicContent = exports.DynamicContent = void 0;
4
- const react_1 = require("react");
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
- (0, react_1.useEffect)(() => {
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;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  type AnyName = string;
3
2
  type InParam = {
4
3
  in: any;
@@ -1,17 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultMessageBus = exports.createMessageBus = exports.useCustomMessaging = exports.useMessaging = exports.messageBusEventPost = exports.Messaging = void 0;
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: (0, uuid_1.v4)(),
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.isSimpleNode = exports.renderNode = exports.IF = void 0;
4
- const React = require("react");
5
- const lodash_1 = require("lodash");
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 (0, lodash_1.isFunction)(node) ? node() : node;
45
+ return _.isFunction(node) ? node() : node;
12
46
  }
13
- exports.renderNode = renderNode;
14
47
  function isSimpleNode(node) {
15
- return (0, lodash_1.isString)(node) || (0, lodash_1.isNumber)(node) || (0, lodash_1.isBoolean)(node) || null || undefined;
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
+ ![logo](/docs/logo.png)
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.5",
3
+ "version": "1.0.7",
4
4
  "description": "Unified Encapsulated Component Architecture for React",
5
5
  "keywords": [
6
- "ueca", "react", "typescript", "framework", "abstraction",
7
- "unified", "encapsulated", "component", "architecture",
8
- "message bus", "bindings"
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": "echo \"Error: no test specified\" && exit 1"
32
+ "test": "jest"
26
33
  },
27
34
  "author": "Aleksey Suvorov",
28
35
  "license": "ISC",
29
- "dependencies": {
30
- "lodash": "^4.17.21",
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
- "devDependencies": {
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
- "@types/react": "^18.3.2"
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
+ }