ueca-react 1.0.7 → 2.0.2

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.
Files changed (39) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +109 -119
  3. package/dist/index.d.ts +266 -4
  4. package/dist/ueca-react.js +1453 -0
  5. package/docs/Arrays and Reactivity in UECA-React.md +158 -0
  6. package/docs/Automatic onChange Events in UECA-React.md +142 -0
  7. package/docs/Automatic onChanging Events in UECA-React.md +157 -0
  8. package/docs/Automatic onPropChange and onPropChanging Events in UECA-React.md +112 -0
  9. package/docs/Component Extension in UECA-React.md +275 -0
  10. package/docs/Component IDs in UECA-React.md +181 -0
  11. package/docs/{component-intergation-model.md → Component Integration Model in UECA-React.md } +4 -3
  12. package/docs/{component-mental-model.md → Component Mental Model in UECA-React.md } +4 -3
  13. package/docs/Introduction to UECA-React Components.md +190 -0
  14. package/docs/Introduction to UECA-React.md +24 -0
  15. package/docs/Lifecycle Hooks in UECA-React.md +237 -0
  16. package/docs/Message Bus in UECA-React.md +260 -0
  17. package/docs/Model Caching in UECA-React.md +144 -0
  18. package/docs/Property Bindings in UECA-React.md +191 -0
  19. package/docs/State Management in UECA-React.md +128 -0
  20. package/docs/Technology of UECA-React.md +45 -0
  21. package/docs/Tracing in UECA-React.md +110 -0
  22. package/docs/code-template.md +53 -27
  23. package/docs/index.md +31 -11
  24. package/package.json +68 -72
  25. package/dist/componentModel.d.ts +0 -127
  26. package/dist/componentModel.js +0 -772
  27. package/dist/dynamicContent.d.ts +0 -22
  28. package/dist/dynamicContent.js +0 -80
  29. package/dist/index.js +0 -29
  30. package/dist/messageBus.d.ts +0 -46
  31. package/dist/messageBus.js +0 -141
  32. package/dist/utils.d.ts +0 -8
  33. package/dist/utils.js +0 -52
  34. package/docs/base-concepts.md +0 -192
  35. package/docs/bindings-overview.md +0 -164
  36. package/docs/general-code-structure.md +0 -177
  37. package/docs/introduction.md +0 -56
  38. package/docs/message-bus.md +0 -177
  39. package/docs/technology.md +0 -45
@@ -1,22 +0,0 @@
1
- import React from "react";
2
- import { AnyComponentModel, ComponentModel, ComponentParams, ComponentStruct, DynamicChildren } from "./componentModel";
3
- type Struct = ComponentStruct<{
4
- props: {
5
- children: React.ReactNode;
6
- _baseProps: string[];
7
- _staticChildrenModels: AnyComponentModel[];
8
- _childrenFromJSX: boolean;
9
- _forceUpdate: boolean;
10
- };
11
- children: DynamicChildren;
12
- methods: {
13
- destroyModels: () => void;
14
- forceUpdate: (children?: React.ReactNode) => void;
15
- getModels: () => AnyComponentModel[];
16
- getChildrenNames: () => string[];
17
- };
18
- }>;
19
- type DynamicContentModel = ComponentModel<Struct> & Record<string, ComponentModel<ComponentStruct<{}>>>;
20
- declare function useDynamicContent(params?: ComponentParams<Struct>): DynamicContentModel;
21
- declare const DynamicContent: (params: ComponentParams<Struct, {}>) => JSX.Element;
22
- export { DynamicContentModel, DynamicContent, useDynamicContent };
@@ -1,80 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DynamicContent = void 0;
7
- exports.useDynamicContent = useDynamicContent;
8
- const react_1 = __importDefault(require("react"));
9
- const lodash_1 = require("lodash");
10
- const componentModel_1 = require("./componentModel");
11
- const utils_1 = require("./utils");
12
- function useDynamicContent(params) {
13
- var _a;
14
- const struct = {
15
- props: {
16
- id: useDynamicContent.name,
17
- children: undefined,
18
- _baseProps: [],
19
- _staticChildrenModels: [],
20
- _childrenFromJSX: false,
21
- _forceUpdate: false
22
- },
23
- methods: {
24
- forceUpdate: (children) => {
25
- model._forceUpdate = true;
26
- try {
27
- model.children = undefined;
28
- model.destroyModels();
29
- model._childrenFromJSX = false;
30
- model.children = children;
31
- }
32
- finally {
33
- model._forceUpdate = false;
34
- }
35
- },
36
- getModels: () => {
37
- var _a;
38
- let res = [];
39
- if ((_a = model._baseProps) === null || _a === void 0 ? void 0 : _a.length) {
40
- const childrenNames = model.getChildrenNames();
41
- res = childrenNames.map(name => model[name]);
42
- }
43
- return res;
44
- },
45
- getChildrenNames: () => {
46
- return Reflect.ownKeys(model).filter(c => (0, lodash_1.isString)(c) && !model._baseProps.find(p => p === c));
47
- },
48
- destroyModels: () => {
49
- var _a;
50
- const childrenNames = model.getChildrenNames();
51
- const didDestroy = !!(childrenNames.length || ((_a = model._staticChildrenModels) === null || _a === void 0 ? void 0 : _a.length));
52
- childrenNames.forEach(name => model[componentModel_1.$].__deleteMember(name));
53
- model._staticChildrenModels = undefined;
54
- didDestroy && (0, componentModel_1.componentModelDebug)(`DynamicContent: clear cache in model=#${model.birthMark()} path=${model.htmlId()}`);
55
- },
56
- },
57
- events: {
58
- onChangeChildren: () => !model._forceUpdate && !model.calledFromJSX() && !model._childrenFromJSX && model.destroyModels(),
59
- },
60
- View: (props) => {
61
- if (model._childrenFromJSX !== !!(props === null || props === void 0 ? void 0 : props.children)) {
62
- model.destroyModels();
63
- }
64
- model._childrenFromJSX = !!(props === null || props === void 0 ? void 0 : props.children);
65
- react_1.default.useEffect(() => {
66
- trap.updateCache(model);
67
- model._staticChildrenModels = trap.models;
68
- });
69
- const trap = (0, componentModel_1.newComponentModelTrapRegistry)(model._staticChildrenModels);
70
- return (react_1.default.createElement(componentModel_1.ComponentModelTrap.Provider, { value: trap }, (0, utils_1.renderNode)(model._childrenFromJSX ? props.children : model.children)));
71
- }
72
- };
73
- const model = (0, componentModel_1.useComponent)(struct, params);
74
- if (!((_a = model._baseProps) === null || _a === void 0 ? void 0 : _a.length)) {
75
- model._baseProps = Reflect.ownKeys(model).filter(x => (0, lodash_1.isString)(x));
76
- }
77
- return model;
78
- }
79
- const DynamicContent = (0, componentModel_1.getFC)(useDynamicContent);
80
- exports.DynamicContent = DynamicContent;
package/dist/index.js DELETED
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isSimpleNode = exports.renderNode = exports.IF = exports.useDynamicContent = exports.DynamicContent = exports.defaultMessageBus = exports.createMessageBus = exports.useCustomMessaging = exports.useMessaging = exports.messageBusEventPost = exports.Messaging = exports.$ = exports.getFC = exports.componentModelDebug = exports.bindProp = exports.bind = exports.blankView = exports.mergeStruct = exports.useComponent = exports.newComponentModelTrapRegistry = exports.useComponentModelTrap = exports.ComponentModelTrap = void 0;
4
- var componentModel_1 = require("./componentModel");
5
- Object.defineProperty(exports, "ComponentModelTrap", { enumerable: true, get: function () { return componentModel_1.ComponentModelTrap; } });
6
- Object.defineProperty(exports, "useComponentModelTrap", { enumerable: true, get: function () { return componentModel_1.useComponentModelTrap; } });
7
- Object.defineProperty(exports, "newComponentModelTrapRegistry", { enumerable: true, get: function () { return componentModel_1.newComponentModelTrapRegistry; } });
8
- Object.defineProperty(exports, "useComponent", { enumerable: true, get: function () { return componentModel_1.useComponent; } });
9
- Object.defineProperty(exports, "mergeStruct", { enumerable: true, get: function () { return componentModel_1.mergeStruct; } });
10
- Object.defineProperty(exports, "blankView", { enumerable: true, get: function () { return componentModel_1.blankView; } });
11
- Object.defineProperty(exports, "bind", { enumerable: true, get: function () { return componentModel_1.bind; } });
12
- Object.defineProperty(exports, "bindProp", { enumerable: true, get: function () { return componentModel_1.bindProp; } });
13
- Object.defineProperty(exports, "componentModelDebug", { enumerable: true, get: function () { return componentModel_1.componentModelDebug; } });
14
- Object.defineProperty(exports, "getFC", { enumerable: true, get: function () { return componentModel_1.getFC; } });
15
- Object.defineProperty(exports, "$", { enumerable: true, get: function () { return componentModel_1.$; } });
16
- var messageBus_1 = require("./messageBus");
17
- Object.defineProperty(exports, "Messaging", { enumerable: true, get: function () { return messageBus_1.Messaging; } });
18
- Object.defineProperty(exports, "messageBusEventPost", { enumerable: true, get: function () { return messageBus_1.messageBusEventPost; } });
19
- Object.defineProperty(exports, "useMessaging", { enumerable: true, get: function () { return messageBus_1.useMessaging; } });
20
- Object.defineProperty(exports, "useCustomMessaging", { enumerable: true, get: function () { return messageBus_1.useCustomMessaging; } });
21
- Object.defineProperty(exports, "createMessageBus", { enumerable: true, get: function () { return messageBus_1.createMessageBus; } });
22
- Object.defineProperty(exports, "defaultMessageBus", { enumerable: true, get: function () { return messageBus_1.defaultMessageBus; } });
23
- var dynamicContent_1 = require("./dynamicContent");
24
- Object.defineProperty(exports, "DynamicContent", { enumerable: true, get: function () { return dynamicContent_1.DynamicContent; } });
25
- Object.defineProperty(exports, "useDynamicContent", { enumerable: true, get: function () { return dynamicContent_1.useDynamicContent; } });
26
- var utils_1 = require("./utils");
27
- Object.defineProperty(exports, "IF", { enumerable: true, get: function () { return utils_1.IF; } });
28
- Object.defineProperty(exports, "renderNode", { enumerable: true, get: function () { return utils_1.renderNode; } });
29
- Object.defineProperty(exports, "isSimpleNode", { enumerable: true, get: function () { return utils_1.isSimpleNode; } });
@@ -1,46 +0,0 @@
1
- type AnyName = string;
2
- type InParam = {
3
- in: any;
4
- };
5
- type OutParam = {
6
- out: any;
7
- };
8
- type AnyMessage = Record<string, InParam | OutParam | InParam & OutParam | {}>;
9
- type ParamIn<TMsg extends AnyMessage, Msg extends keyof TMsg> = TMsg[Msg] extends InParam ? TMsg[Msg][keyof InParam] : undefined;
10
- type ParamOut<TMsg extends AnyMessage, Msg extends keyof TMsg> = TMsg[Msg] extends OutParam ? TMsg[Msg][keyof OutParam] : undefined;
11
- type GetMethod<TMsg extends AnyMessage> = <Msg extends keyof TMsg>(message: Msg, param: ParamIn<TMsg, Msg>, callback: (param: ParamOut<TMsg, Msg>) => void) => void;
12
- type PostMethod<TMsg extends AnyMessage> = <Msg extends keyof TMsg>(message: Msg, param: ParamIn<TMsg, Msg>, callback?: (param: ParamOut<TMsg, Msg>) => void) => void;
13
- type AsyncMethod<TMsg extends AnyMessage> = <Msg extends keyof TMsg>(message: Msg, param: ParamIn<TMsg, Msg>) => Promise<ParamOut<TMsg, Msg>>;
14
- type TargetGetMethod<TMsg extends AnyMessage, TName extends AnyName> = <Msg extends keyof TMsg>(target: TName, message: Msg, param: ParamIn<TMsg, Msg>, callback: (param: ParamOut<TMsg, Msg>) => void) => void;
15
- type TargetPostMethod<TMsg extends AnyMessage, TName extends AnyName> = <Msg extends keyof TMsg>(target: TName, message: Msg, param: ParamIn<TMsg, Msg>, callback?: (param: ParamOut<TMsg, Msg>) => void) => void;
16
- type TargetAsyncMethod<TMsg extends AnyMessage, TName extends AnyName> = <Msg extends keyof TMsg>(target: TName, message: Msg, param: ParamIn<TMsg, Msg>) => Promise<ParamOut<TMsg, Msg>>;
17
- interface IMessageBus<TMsg extends AnyMessage, TName extends AnyName> {
18
- readonly name?: TName;
19
- readonly get: GetMethod<TMsg>;
20
- readonly post: PostMethod<TMsg>;
21
- readonly getFrom: TargetGetMethod<TMsg, TName>;
22
- readonly postTo: TargetPostMethod<TMsg, TName>;
23
- readonly getAsync: AsyncMethod<TMsg>;
24
- readonly postAsync: AsyncMethod<TMsg>;
25
- readonly getFromAsync: TargetAsyncMethod<TMsg, TName>;
26
- readonly postToAsync: TargetAsyncMethod<TMsg, TName>;
27
- as<M extends AnyMessage, N extends AnyName = TName>(): IMessageBus<M, N>;
28
- }
29
- type MessageHandler<TMsg extends AnyMessage, Msg extends keyof TMsg> = (param: ParamIn<TMsg, Msg>) => ParamOut<TMsg, Msg> extends undefined ? Promise<void> : Promise<ParamOut<TMsg, Msg>>;
30
- type MessageHandlerNoPar<TMsg extends AnyMessage, Msg extends keyof TMsg> = () => ParamOut<TMsg, Msg> extends undefined ? Promise<void> : Promise<ParamOut<TMsg, Msg>>;
31
- type Messages<TMsg extends AnyMessage> = {
32
- [Msg in keyof TMsg]?: ParamIn<TMsg, Msg> extends undefined ? MessageHandlerNoPar<TMsg, Msg> : MessageHandler<TMsg, Msg>;
33
- };
34
- declare function useMessaging<TMsg extends AnyMessage, TName extends AnyName>(incoming?: Messages<TMsg>, name?: TName): IMessageBus<TMsg, TName>;
35
- declare function useCustomMessaging<TMsg extends AnyMessage, TName extends AnyName>(bus: IMessageBus<TMsg, TName>, incoming?: Messages<TMsg>, name?: TName): IMessageBus<TMsg, TName>;
36
- declare function createMessageBus<TMsg extends AnyMessage, TName extends AnyName>(name?: string): IMessageBus<TMsg, TName>;
37
- declare const messageBusEventPost = "messagebus.post";
38
- type MessageBusEvent = Event & {
39
- detail: {
40
- message: string;
41
- params?: any;
42
- };
43
- };
44
- declare const Messaging: import("react").Context<IMessageBus<AnyMessage, any>>;
45
- declare function defaultMessageBus<TMsg extends AnyMessage>(): IMessageBus<TMsg, any>;
46
- export { IMessageBus, Messages, AsyncMethod, AnyMessage, MessageBusEvent, AnyName, Messaging, messageBusEventPost, useMessaging, useCustomMessaging, createMessageBus, defaultMessageBus };
@@ -1,141 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.messageBusEventPost = exports.Messaging = void 0;
4
- exports.useMessaging = useMessaging;
5
- exports.useCustomMessaging = useCustomMessaging;
6
- exports.createMessageBus = createMessageBus;
7
- exports.defaultMessageBus = defaultMessageBus;
8
- const react_1 = require("react");
9
- function useMessaging(incoming, name) {
10
- return useCustomMessaging((0, react_1.useContext)(Messaging), incoming, name);
11
- }
12
- function useCustomMessaging(bus, incoming, name) {
13
- (0, react_1.useEffect)(() => {
14
- if (incoming) {
15
- const messageRecepient = {
16
- id: generateId(),
17
- name: name,
18
- messages: incoming
19
- };
20
- bus.subscribe(messageRecepient);
21
- return () => bus.unsubscribe(messageRecepient);
22
- }
23
- }, [incoming, bus, name]);
24
- return bus;
25
- function generateId() {
26
- return Date.now().toString(36) + Math.random().toString(36).substring(2, 5);
27
- }
28
- }
29
- function createMessageBus(name) {
30
- const bus = {
31
- name: name,
32
- subscribers: [],
33
- messageQueue: [],
34
- as: () => bus,
35
- get: (messageType, param, callback) => _queueMessage({ message: messageType, param: param, callback: callback }),
36
- getFrom: (target, messageType, param, callback) => _queueMessage({ target: target, message: messageType, param: param, callback: callback }),
37
- post: (messageType, param, callback) => _queueMessage({ message: messageType, param: param, callback: callback }),
38
- postTo: (target, messageType, param, callback) => _queueMessage({ target: target, message: messageType, param: param, callback: callback }),
39
- getAsync: (messageType, param) => {
40
- return new Promise((resolve, reject) => bus.get(messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
41
- },
42
- getFromAsync: (target, messageType, param) => {
43
- return new Promise((resolve, reject) => bus.getFrom(target, messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
44
- },
45
- postAsync: (messageType, param) => {
46
- return new Promise((resolve, reject) => bus.post(messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
47
- },
48
- postToAsync: (target, messageType, param) => {
49
- return new Promise((resolve, reject) => bus.postTo(target, messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
50
- }
51
- };
52
- bus.subscribe = (recipient) => {
53
- let subscriber = bus.subscribers.find((s) => s.id === recipient.id);
54
- subscriber ?
55
- subscriber.messages = recipient.messages :
56
- bus.subscribers.push(recipient);
57
- };
58
- bus.unsubscribe = (recipient) => {
59
- bus.subscribers = bus.subscribers.filter((s) => s.id !== recipient.id);
60
- };
61
- function isValidResult(result) {
62
- return !(result && (result instanceof Error));
63
- }
64
- bus.processMessages = () => {
65
- const msgCount = bus.messageQueue.length;
66
- if (msgCount === 0)
67
- return;
68
- bus.messageQueue.forEach(message => {
69
- let dispatched = false;
70
- let targetFound = false;
71
- bus.subscribers.forEach(subscriber => {
72
- const method = subscriber.messages[message.message];
73
- targetFound = targetFound || (message.target ? message.target === subscriber.name : false);
74
- if (method !== undefined && (!message.target || message.target === subscriber.name)) {
75
- const methodPromise = method(message.param);
76
- if (methodPromise) {
77
- methodPromise.catch(reason => { var _a; return (_a = message.callback) === null || _a === void 0 ? void 0 : _a.call(message, reason); });
78
- methodPromise.then(methodResult => { var _a; return (_a = message.callback) === null || _a === void 0 ? void 0 : _a.call(message, methodResult); }, reason => { var _a; return (_a = message.callback) === null || _a === void 0 ? void 0 : _a.call(message, reason); });
79
- }
80
- dispatched = true;
81
- }
82
- });
83
- if (!dispatched) {
84
- if (!message.target) {
85
- console.warn(`MessageBus (${bus.name}): no subscribers for message "${String(message.message)}"`);
86
- }
87
- else {
88
- (targetFound
89
- ? console.warn(`MessageBus (${bus.name}): target "${message.target}" has no subscription for message "${String(message.message)}"`)
90
- : console.warn(`MessageBus (${bus.name}): target "${message.target}" not found"`));
91
- }
92
- if (message.callback) {
93
- message.callback(undefined);
94
- }
95
- }
96
- });
97
- if (bus.messageQueue.length > msgCount) {
98
- bus.messageQueue = bus.messageQueue.splice(msgCount);
99
- }
100
- else {
101
- bus.messageQueue = [];
102
- }
103
- };
104
- return bus;
105
- function _queueMessage(msg) {
106
- bus.messageQueue.push(msg);
107
- setTimeout(() => {
108
- try {
109
- bus.processMessages();
110
- }
111
- catch (e) {
112
- console.error("Message bus crashed", e);
113
- }
114
- }, 0);
115
- }
116
- function _promiseCallback(result, messageType, resolve, reject) {
117
- if (isValidResult(result)) {
118
- resolve(result);
119
- }
120
- else {
121
- reject(result);
122
- }
123
- }
124
- }
125
- ;
126
- const messageBusEventPost = "messagebus.post";
127
- exports.messageBusEventPost = messageBusEventPost;
128
- function createDefaultMessageBus() {
129
- const defaultMessageBus = createMessageBus("Default Message Bus");
130
- window.addEventListener(messageBusEventPost, _onPost);
131
- return defaultMessageBus;
132
- function _onPost(event) {
133
- defaultMessageBus.post(event.detail.message, event.detail.params);
134
- }
135
- }
136
- const _defaultMessageBus = createDefaultMessageBus();
137
- const Messaging = (0, react_1.createContext)(_defaultMessageBus);
138
- exports.Messaging = Messaging;
139
- function defaultMessageBus() {
140
- return _defaultMessageBus;
141
- }
package/dist/utils.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import * as React from "react";
2
- declare function IF(props: {
3
- condition: boolean;
4
- children: React.ReactNode;
5
- }): JSX.Element;
6
- declare function renderNode(node: React.ReactNode | (() => React.ReactNode)): any;
7
- declare function isSimpleNode(node: React.ReactNode): boolean;
8
- export { IF, renderNode, isSimpleNode };
package/dist/utils.js DELETED
@@ -1,52 +0,0 @@
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
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.IF = IF;
37
- exports.renderNode = renderNode;
38
- exports.isSimpleNode = isSimpleNode;
39
- const React = __importStar(require("react"));
40
- const _ = __importStar(require("lodash"));
41
- function IF(props) {
42
- return props.condition ? React.createElement(React.Fragment, null, renderNode(props.children)) : null;
43
- }
44
- function renderNode(node) {
45
- return _.isFunction(node) ? node() : node;
46
- }
47
- function isSimpleNode(node) {
48
- if (_.isString(node) || _.isNumber(node) || _.isBoolean(node) || _.isNull(node) || _.isUndefined(node)) {
49
- return true;
50
- }
51
- return false;
52
- }
@@ -1,192 +0,0 @@
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.