ueca-react 1.0.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +109 -57
  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 +44 -0
  24. package/package.json +70 -40
  25. package/dist/componentModel.d.ts +0 -127
  26. package/dist/componentModel.js +0 -741
  27. package/dist/dynamicContent.d.ts +0 -22
  28. package/dist/dynamicContent.js +0 -77
  29. package/dist/index.js +0 -29
  30. package/dist/messageBus.d.ts +0 -47
  31. package/dist/messageBus.js +0 -139
  32. package/dist/utils.d.ts +0 -8
  33. package/dist/utils.js +0 -17
  34. package/docs/bindings-overview.md +0 -164
  35. package/docs/introduction.md +0 -56
  36. 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,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useDynamicContent = exports.DynamicContent = void 0;
4
- const react_1 = require("react");
5
- const lodash_1 = require("lodash");
6
- const componentModel_1 = require("./componentModel");
7
- const utils_1 = require("./utils");
8
- function useDynamicContent(params) {
9
- var _a;
10
- const struct = {
11
- props: {
12
- id: useDynamicContent.name,
13
- children: undefined,
14
- _baseProps: [],
15
- _staticChildrenModels: [],
16
- _childrenFromJSX: false,
17
- _forceUpdate: false
18
- },
19
- methods: {
20
- forceUpdate: (children) => {
21
- model._forceUpdate = true;
22
- try {
23
- model.children = undefined;
24
- model.destroyModels();
25
- model._childrenFromJSX = false;
26
- model.children = children;
27
- }
28
- finally {
29
- model._forceUpdate = false;
30
- }
31
- },
32
- getModels: () => {
33
- var _a;
34
- let res = [];
35
- if ((_a = model._baseProps) === null || _a === void 0 ? void 0 : _a.length) {
36
- const childrenNames = model.getChildrenNames();
37
- res = childrenNames.map(name => model[name]);
38
- }
39
- return res;
40
- },
41
- getChildrenNames: () => {
42
- return Reflect.ownKeys(model).filter(c => (0, lodash_1.isString)(c) && !model._baseProps.find(p => p === c));
43
- },
44
- destroyModels: () => {
45
- var _a;
46
- const childrenNames = model.getChildrenNames();
47
- const didDestroy = !!(childrenNames.length || ((_a = model._staticChildrenModels) === null || _a === void 0 ? void 0 : _a.length));
48
- childrenNames.forEach(name => model[componentModel_1.$].__deleteMember(name));
49
- model._staticChildrenModels = undefined;
50
- didDestroy && (0, componentModel_1.componentModelDebug)(`DynamicContent: clear cache in model=#${model.birthMark()} path=${model.htmlId()}`);
51
- },
52
- },
53
- events: {
54
- onChangeChildren: () => !model._forceUpdate && !model.calledFromJSX() && !model._childrenFromJSX && model.destroyModels(),
55
- },
56
- View: (props) => {
57
- if (model._childrenFromJSX !== !!(props === null || props === void 0 ? void 0 : props.children)) {
58
- model.destroyModels();
59
- }
60
- model._childrenFromJSX = !!(props === null || props === void 0 ? void 0 : props.children);
61
- (0, react_1.useEffect)(() => {
62
- trap.updateCache(model);
63
- model._staticChildrenModels = trap.models;
64
- });
65
- const trap = (0, componentModel_1.newComponentModelTrapRegistry)(model._staticChildrenModels);
66
- return (react_1.default.createElement(componentModel_1.ComponentModelTrap.Provider, { value: trap }, (0, utils_1.renderNode)(model._childrenFromJSX ? props.children : model.children)));
67
- }
68
- };
69
- const model = (0, componentModel_1.useComponent)(struct, params);
70
- if (!((_a = model._baseProps) === null || _a === void 0 ? void 0 : _a.length)) {
71
- model._baseProps = Reflect.ownKeys(model).filter(x => (0, lodash_1.isString)(x));
72
- }
73
- return model;
74
- }
75
- exports.useDynamicContent = useDynamicContent;
76
- const DynamicContent = (0, componentModel_1.getFC)(useDynamicContent);
77
- 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,47 +0,0 @@
1
- /// <reference types="react" />
2
- type AnyName = string;
3
- type InParam = {
4
- in: any;
5
- };
6
- type OutParam = {
7
- out: any;
8
- };
9
- type AnyMessage = Record<string, InParam | OutParam | InParam & OutParam | {}>;
10
- type ParamIn<TMsg extends AnyMessage, Msg extends keyof TMsg> = TMsg[Msg] extends InParam ? TMsg[Msg][keyof InParam] : undefined;
11
- type ParamOut<TMsg extends AnyMessage, Msg extends keyof TMsg> = TMsg[Msg] extends OutParam ? TMsg[Msg][keyof OutParam] : undefined;
12
- type GetMethod<TMsg extends AnyMessage> = <Msg extends keyof TMsg>(message: Msg, param: ParamIn<TMsg, Msg>, callback: (param: ParamOut<TMsg, Msg>) => void) => void;
13
- type PostMethod<TMsg extends AnyMessage> = <Msg extends keyof TMsg>(message: Msg, param: ParamIn<TMsg, Msg>, callback?: (param: ParamOut<TMsg, Msg>) => void) => void;
14
- type AsyncMethod<TMsg extends AnyMessage> = <Msg extends keyof TMsg>(message: Msg, param: ParamIn<TMsg, Msg>) => Promise<ParamOut<TMsg, Msg>>;
15
- 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;
16
- 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;
17
- type TargetAsyncMethod<TMsg extends AnyMessage, TName extends AnyName> = <Msg extends keyof TMsg>(target: TName, message: Msg, param: ParamIn<TMsg, Msg>) => Promise<ParamOut<TMsg, Msg>>;
18
- interface IMessageBus<TMsg extends AnyMessage, TName extends AnyName> {
19
- readonly name?: TName;
20
- readonly get: GetMethod<TMsg>;
21
- readonly post: PostMethod<TMsg>;
22
- readonly getFrom: TargetGetMethod<TMsg, TName>;
23
- readonly postTo: TargetPostMethod<TMsg, TName>;
24
- readonly getAsync: AsyncMethod<TMsg>;
25
- readonly postAsync: AsyncMethod<TMsg>;
26
- readonly getFromAsync: TargetAsyncMethod<TMsg, TName>;
27
- readonly postToAsync: TargetAsyncMethod<TMsg, TName>;
28
- as<M extends AnyMessage, N extends AnyName = TName>(): IMessageBus<M, N>;
29
- }
30
- type MessageHandler<TMsg extends AnyMessage, Msg extends keyof TMsg> = (param: ParamIn<TMsg, Msg>) => ParamOut<TMsg, Msg> extends undefined ? Promise<void> : Promise<ParamOut<TMsg, Msg>>;
31
- type MessageHandlerNoPar<TMsg extends AnyMessage, Msg extends keyof TMsg> = () => ParamOut<TMsg, Msg> extends undefined ? Promise<void> : Promise<ParamOut<TMsg, Msg>>;
32
- type Messages<TMsg extends AnyMessage> = {
33
- [Msg in keyof TMsg]?: ParamIn<TMsg, Msg> extends undefined ? MessageHandlerNoPar<TMsg, Msg> : MessageHandler<TMsg, Msg>;
34
- };
35
- declare function useMessaging<TMsg extends AnyMessage, TName extends AnyName>(incoming?: Messages<TMsg>, name?: TName): IMessageBus<TMsg, TName>;
36
- declare function useCustomMessaging<TMsg extends AnyMessage, TName extends AnyName>(bus: IMessageBus<TMsg, TName>, incoming?: Messages<TMsg>, name?: TName): IMessageBus<TMsg, TName>;
37
- declare function createMessageBus<TMsg extends AnyMessage, TName extends AnyName>(name?: string): IMessageBus<TMsg, TName>;
38
- declare const messageBusEventPost = "messagebus.post";
39
- type MessageBusEvent = Event & {
40
- detail: {
41
- message: string;
42
- params?: any;
43
- };
44
- };
45
- declare const Messaging: import("react").Context<IMessageBus<AnyMessage, any>>;
46
- declare function defaultMessageBus<TMsg extends AnyMessage>(): IMessageBus<TMsg, any>;
47
- export { IMessageBus, Messages, AsyncMethod, AnyMessage, MessageBusEvent, AnyName, Messaging, messageBusEventPost, useMessaging, useCustomMessaging, createMessageBus, defaultMessageBus };
@@ -1,139 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultMessageBus = exports.createMessageBus = exports.useCustomMessaging = exports.useMessaging = exports.messageBusEventPost = exports.Messaging = void 0;
4
- const react_1 = require("react");
5
- const uuid_1 = require("uuid");
6
- function useMessaging(incoming, name) {
7
- return useCustomMessaging((0, react_1.useContext)(Messaging), incoming, name);
8
- }
9
- exports.useMessaging = useMessaging;
10
- function useCustomMessaging(bus, incoming, name) {
11
- (0, react_1.useEffect)(() => {
12
- if (incoming) {
13
- const messageRecepient = {
14
- id: (0, uuid_1.v4)(),
15
- name: name,
16
- messages: incoming
17
- };
18
- bus.subscribe(messageRecepient);
19
- return () => bus.unsubscribe(messageRecepient);
20
- }
21
- }, [incoming, bus, name]);
22
- return bus;
23
- }
24
- exports.useCustomMessaging = useCustomMessaging;
25
- function createMessageBus(name) {
26
- const bus = {
27
- name: name,
28
- subscribers: [],
29
- messageQueue: [],
30
- as: () => bus,
31
- get: (messageType, param, callback) => _queueMessage({ message: messageType, param: param, callback: callback }),
32
- getFrom: (target, messageType, param, callback) => _queueMessage({ target: target, message: messageType, param: param, callback: callback }),
33
- post: (messageType, param, callback) => _queueMessage({ message: messageType, param: param, callback: callback }),
34
- postTo: (target, messageType, param, callback) => _queueMessage({ target: target, message: messageType, param: param, callback: callback }),
35
- getAsync: (messageType, param) => {
36
- return new Promise((resolve, reject) => bus.get(messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
37
- },
38
- getFromAsync: (target, messageType, param) => {
39
- return new Promise((resolve, reject) => bus.getFrom(target, messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
40
- },
41
- postAsync: (messageType, param) => {
42
- return new Promise((resolve, reject) => bus.post(messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
43
- },
44
- postToAsync: (target, messageType, param) => {
45
- return new Promise((resolve, reject) => bus.postTo(target, messageType, param, result => _promiseCallback(result, messageType, resolve, reject)));
46
- }
47
- };
48
- bus.subscribe = (recipient) => {
49
- let subscriber = bus.subscribers.find((s) => s.id === recipient.id);
50
- subscriber ?
51
- subscriber.messages = recipient.messages :
52
- bus.subscribers.push(recipient);
53
- };
54
- bus.unsubscribe = (recipient) => {
55
- bus.subscribers = bus.subscribers.filter((s) => s.id !== recipient.id);
56
- };
57
- function isValidResult(result) {
58
- return !(result && (result instanceof Error));
59
- }
60
- bus.processMessages = () => {
61
- const msgCount = bus.messageQueue.length;
62
- if (msgCount === 0)
63
- return;
64
- bus.messageQueue.forEach(message => {
65
- let dispatched = false;
66
- let targetFound = false;
67
- bus.subscribers.forEach(subscriber => {
68
- const method = subscriber.messages[message.message];
69
- targetFound = targetFound || (message.target ? message.target === subscriber.name : false);
70
- if (method !== undefined && (!message.target || message.target === subscriber.name)) {
71
- const methodPromise = method(message.param);
72
- if (methodPromise) {
73
- methodPromise.catch(reason => { var _a; return (_a = message.callback) === null || _a === void 0 ? void 0 : _a.call(message, reason); });
74
- 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); });
75
- }
76
- dispatched = true;
77
- }
78
- });
79
- if (!dispatched) {
80
- if (!message.target) {
81
- console.warn(`MessageBus (${bus.name}): no subscribers for message "${String(message.message)}"`);
82
- }
83
- else {
84
- (targetFound
85
- ? console.warn(`MessageBus (${bus.name}): target "${message.target}" has no subscription for message "${String(message.message)}"`)
86
- : console.warn(`MessageBus (${bus.name}): target "${message.target}" not found"`));
87
- }
88
- if (message.callback) {
89
- message.callback(undefined);
90
- }
91
- }
92
- });
93
- if (bus.messageQueue.length > msgCount) {
94
- bus.messageQueue = bus.messageQueue.splice(msgCount);
95
- }
96
- else {
97
- bus.messageQueue = [];
98
- }
99
- };
100
- return bus;
101
- function _queueMessage(msg) {
102
- bus.messageQueue.push(msg);
103
- setTimeout(() => {
104
- try {
105
- bus.processMessages();
106
- }
107
- catch (e) {
108
- console.error("Message bus crashed", e);
109
- }
110
- }, 0);
111
- }
112
- function _promiseCallback(result, messageType, resolve, reject) {
113
- if (isValidResult(result)) {
114
- resolve(result);
115
- }
116
- else {
117
- reject(result);
118
- }
119
- }
120
- }
121
- exports.createMessageBus = createMessageBus;
122
- ;
123
- const messageBusEventPost = "messagebus.post";
124
- exports.messageBusEventPost = messageBusEventPost;
125
- function createDefaultMessageBus() {
126
- const defaultMessageBus = createMessageBus("Default Message Bus");
127
- window.addEventListener(messageBusEventPost, _onPost);
128
- return defaultMessageBus;
129
- function _onPost(event) {
130
- defaultMessageBus.post(event.detail.message, event.detail.params);
131
- }
132
- }
133
- const _defaultMessageBus = createDefaultMessageBus();
134
- const Messaging = (0, react_1.createContext)(_defaultMessageBus);
135
- exports.Messaging = Messaging;
136
- function defaultMessageBus() {
137
- return _defaultMessageBus;
138
- }
139
- exports.defaultMessageBus = defaultMessageBus;
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): any;
7
- declare function isSimpleNode(node: React.ReactNode): boolean;
8
- export { IF, renderNode, isSimpleNode };
package/dist/utils.js DELETED
@@ -1,17 +0,0 @@
1
- "use strict";
2
- 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");
6
- function IF(props) {
7
- return props.condition ? React.createElement(React.Fragment, null, renderNode(props.children)) : null;
8
- }
9
- exports.IF = IF;
10
- function renderNode(node) {
11
- return (0, lodash_1.isFunction)(node) ? node() : node;
12
- }
13
- exports.renderNode = renderNode;
14
- function isSimpleNode(node) {
15
- return (0, lodash_1.isString)(node) || (0, lodash_1.isNumber)(node) || (0, lodash_1.isBoolean)(node) || null || undefined;
16
- }
17
- exports.isSimpleNode = isSimpleNode;
@@ -1,164 +0,0 @@
1
- # Bindings Overview
2
-
3
- Unified Encapsulated Component Architecture (UECA) offers various binding mechanisms to manage state and interactions between components. Understanding these bindings is crucial for building efficient, maintainable, and scalable applications. This tutorial covers all types of bindings available in UECA.
4
-
5
- ## Types of Bindings in UECA
6
-
7
- UECA supports three primary types of bindings:
8
-
9
- 1. **Unidirectional Binding**: Simplifies state management by ensuring the target property reflects the source property.
10
- 2. **Bidirectional Binding**: Synchronizes state between two properties, allowing changes to propagate in both directions.
11
- 3. **Custom Binding**: Provides flexibility to transform values during the binding process.
12
-
13
- ### 1. Unidirectional Binding
14
-
15
- Unidirectional binding ensures that the value of a property in the source component is reflected in the target component. It is particularly useful for simple data flows where the target property should always match the source property.
16
-
17
- #### Example
18
-
19
- ```typescript
20
- // skip...
21
-
22
- // Component structure declaration
23
- type MyComponentStruct = UEC.ComponentStruct<{
24
- props: {
25
- caption: string;
26
- };
27
- children: {
28
- someInput: InputModel;
29
- }
30
- }
31
-
32
- // skip...
33
-
34
- // Custom react hook. Returns component model.
35
- function useMyComponent(params?: MyComponentParams): MyComponentModel {
36
- const struct: MyComponentStruct = {
37
- props: {
38
- // initialization of properties
39
- caption: "",
40
- },
41
-
42
- children: {
43
- // initialization of children
44
- someInput: useInput({
45
- // Unidirectional binding (read-only).
46
- label: () => model.caption,
47
- }),
48
-
49
- // skip...
50
- ```
51
-
52
- In this example, `model.someInput.label` will always have the value of `model.caption`.
53
-
54
- ### 2. Bidirectional Binding
55
-
56
- Bidirectional binding synchronizes state between two properties, allowing changes to propagate in both directions. This is useful when both components need to update each other.
57
-
58
- #### Example
59
-
60
-
61
- ```typescript
62
- // skip...
63
-
64
- // Component structure declaration
65
- type MyComponentStruct = UEC.ComponentStruct<{
66
- props: {
67
- caption: string;
68
- userName: { firstName?: string; lastName?: string };
69
- };
70
- children: {
71
- someInput: InputModel;
72
- }
73
- }
74
-
75
- // skip...
76
-
77
- // Custom react hook. Returns component model.
78
- function useMyComponent(params?: MyComponentParams): MyComponentModel {
79
- const struct: MyComponentStruct = {
80
- props: {
81
- // initialization of properties
82
- caption: "",
83
- userName: {},
84
- },
85
-
86
- children: {
87
- // initialization of children
88
- someInput: useInput({
89
- label: () => model.caption,
90
- // Bidirectional binding (read-write).
91
- value: UEC.bindProp(() => model.userName, "firstName"),
92
- }),
93
-
94
- // skip...
95
- ```
96
- In this example, `model.userName.firstName` and `model.someInput.value` will always be in sync, reflecting changes made to either property.
97
-
98
- ### 3. Custom Binding
99
-
100
- Custom binding provides the flexibility to transform values during the binding process. This is useful for cases where the displayed value and stored value need to be different or require specific formatting.
101
-
102
- #### Example
103
-
104
- In this example, we'll create a form component called `ContactForm` where the user enters their phone number. The phone number will be displayed in a formatted way (e.g., (123) 456-7890), but it will be stored in the model without formatting.
105
-
106
- ```typescript
107
- // skip...
108
-
109
- // Component structure declaration
110
- type MyComponentStruct = UEC.ComponentStruct<{
111
- props: {
112
- phoneNumber: string;
113
- };
114
- children: {
115
- phoneNumberInput: InputModel;
116
- }
117
- }
118
-
119
- // skip...
120
-
121
- // Custom react hook. Returns component model.
122
- function useMyComponent(params?: MyComponentParams): MyComponentModel {
123
- const struct: MyComponentStruct = {
124
- props: {
125
- // initialization of properties
126
- phoneNumber: ""
127
- },
128
-
129
- children: {
130
- // initialization of children
131
- phoneNumberInput: useMyInput({
132
- label: "Phone Number",
133
- // Custom binding
134
- value: UEC.bind(
135
- () => _formatPhoneNumber(model.phoneNumber), // Display formatted phone number
136
- (value) => (model.phoneNumber = value.replace(/\D/g, '')) // Store only digits
137
- ),
138
- }),
139
-
140
- // skip...
141
-
142
- // Private methods
143
- function _formatPhoneNumber(phoneNumber: string): string {
144
- // Remove all non-numeric characters
145
- const cleaned = ('' + phoneNumber).replace(/\D/g, '');
146
- // Match and format phone number
147
- const match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/);
148
- if (match) {
149
- return `(${match[1]}) ${match[2]}-${match[3]}`;
150
- }
151
- return phoneNumber;
152
- }
153
- }
154
-
155
- // skip...
156
- ```
157
-
158
- **Custom Binding for Phone Number:**
159
- - **Getter Function**: `() => _formatPhoneNumber(model.phoneNumber)` ensures that the phone number is always displayed in a formatted way.
160
- - **Setter Function**: `(value) => (model.phoneNumber = value.replace(/\D/g, ''))` stores the phone number with only digits, removing any non-numeric characters.
161
-
162
- ### Summary
163
-
164
- Understanding and leveraging different types of bindings in UECA can significantly enhance your application's flexibility, maintainability, and overall design. By using unidirectional, bidirectional, and custom bindings appropriately, you can create robust and scalable applications with ease.
@@ -1,56 +0,0 @@
1
- # Introduction
2
-
3
- Welcome to the Unified Encapsulated Component Architecture (UECA) tutorial. UECA is a modern framework designed to create scalable, maintainable, and modular web applications. This introduction will cover the base features of UECA, providing you with a comprehensive understanding of its core principles and benefits.
4
-
5
- ## What is UECA?
6
-
7
- UECA stands for Unified Encapsulated Component Architecture. It is a framework that emphasizes encapsulation, modularity, and maintainability in web development. UECA leverages TypeScript and JSX to create robust and type-safe components, with minimal reliance on React-specific features.
8
-
9
- ## Base Features of UECA
10
-
11
- ### TypeScript Integration
12
-
13
- TypeScript is the primary language used in UECA development. It provides static typing, enhancing code quality and maintainability. By using TypeScript, developers can catch errors early and enjoy a more robust development experience.
14
-
15
- ### Minimal React Usage
16
-
17
- UECA uses React's custom hooks for component model instantiation, but other React features (like class components, useState, useEffect) are avoided. This minimal reliance on React simplifies the learning curve for developers new to React while leveraging the power of React's component-based architecture.
18
-
19
- ### Declarative and Modular Code Style
20
-
21
- The UECA code style emphasizes a declarative and modular approach. This approach focuses on consistency, encapsulation, and readability, ensuring that components are maintainable and scalable.
22
-
23
- ### Standard Code Template
24
-
25
- To maintain consistency across different components, UECA encourages using [a standard code template](code-template.md). This ensures a uniform structure across the codebase, making it easier to navigate and understand.
26
-
27
- ### Comprehensive Binding System
28
-
29
- UECA provides a comprehensive binding system with three types of bindings:
30
- 1. **Unidirectional Binding**: Reflects the value of a property without allowing modifications.
31
- 2. **Bidirectional Binding**: Synchronizes state between two properties, allowing changes to propagate in both directions.
32
- 3. **Custom Binding**: Provides flexibility to transform values during the binding process.
33
-
34
- ### View Rules and Patterns
35
-
36
- UECA has specific rules for handling views:
37
- - **View-Type Methods**: Methods returning JSX should end with "View" to become observers of all observable dependencies.
38
- - **View-Type Properties**: Properties storing JSX should end with "View" to avoid rendering issues and potential application crashes.
39
- - **Rendering**: Use `UEC.renderNode(model.someView)` for methods returning `ReactNode` and `<model.someView />` for those returning `JSX.Element`.
40
-
41
- ### Messaging System
42
-
43
- UECA includes a messaging system that allows components to communicate in a decoupled manner. Components can send and receive messages without direct dependencies, promoting a modular architecture.
44
-
45
- ### Lifecycle Management
46
-
47
- UECA components have lifecycle methods (`init`, `mount`, `unmount`) to manage initialization, mounting, and unmounting processes. These methods ensure that resources are appropriately managed throughout the component's lifecycle.
48
-
49
- ### Encapsulation
50
-
51
- Encapsulation is a core principle of UECA. Each component encapsulates its implementation details, exposing only necessary interfaces. This reduces code complexity and enhances maintainability.
52
-
53
- ## Conclusion
54
-
55
- UECA is a powerful framework designed to create maintainable, scalable, and modular web applications. By leveraging TypeScript, minimizing React usage, and following a declarative and modular code style, UECA provides a robust foundation for modern web development. This tutorial will guide you through the core features of UECA, helping you build efficient and maintainable applications.
56
-
@@ -1,45 +0,0 @@
1
- # Technology of UECA
2
-
3
- Unified Encapsulated Component Architecture (UECA) development leverages a blend of modern web technologies to create scalable, maintainable, and modular applications. Below is a summary of the key technologies and coding practices employed in UECA development:
4
-
5
- ## TypeScript
6
-
7
- - **Core Language**: TypeScript is the primary language used for UECA development. It provides static typing, which enhances code quality and maintainability.
8
- - **General Usage**: Most of the UECA code is written in general TypeScript, focusing on type safety and modern JavaScript features.
9
-
10
- ## JSX
11
-
12
- - **User Interface**: JSX is used to describe the UI in a syntax familiar to both JavaScript and HTML, making it easier to build and understand component structures.
13
- - **Integration with TypeScript**: Combined with TypeScript, JSX allows for type-checked components, reducing runtime errors and improving development efficiency.
14
-
15
- ## React
16
-
17
- - **Minimal Usage**: The only React feature utilized in UECA is custom hooks for component model instantiation. This minimizes the reliance on React-specific code and simplifies the learning curve for developers new to React.
18
- - **Custom Hooks**: Custom hooks are used to instantiate and manage the lifecycle of component models, integrating React’s state and effect management seamlessly into UECA.
19
-
20
- ## Coding Techniques
21
- - **Property Bindings**: Synchronizes state between components and their properties automatically, reducing the need for manual updates.
22
- - **Automatic OnChange$Property$ Events**: Automatically triggers events when a property changes, allowing components to react to updates or intercept changes for validation.
23
- - **Component Lifecycle Hooks**:
24
- Manages initialization, mounting, and unmounting of components, ensuring predictable behavior and proper resource management.
25
- - **Interaction via Message Bus**:
26
- Facilitates decoupled communication between components, promoting a modular architecture by allowing components to interact without direct dependencies.
27
-
28
- ## Coding Style
29
-
30
- - **Declarative and Modular Approach**: The UECA code style emphasizes a declarative and modular approach, focusing on consistency, encapsulation, and readability to create maintainable and scalable component-based applications.
31
- - **Utility Classes**: While the procedural approach is preferred, utility classes can be used where appropriate, particularly for reusable functions and routines.
32
-
33
- ## Standard Code Template
34
-
35
- - **Consistency**: To maintain consistency across different components, developers are encouraged to use a [standard code template](/docs/code-template.md) for creating new components. This ensures that all components have a uniform structure, making the codebase easier to navigate and understand.
36
- - **Structural Similarity**: Keeping the code structurally similar from component to component helps in maintaining readability and reducing the cognitive load on developers when switching between different parts of the application.
37
-
38
- ## React and TypeScript
39
-
40
- - **Web Application Foundation**: A typical UECA application is a web application built using React and TypeScript. React provides the component-based architecture, while TypeScript offers strong typing and modern JavaScript features.
41
- - **Ban on Other React Features**: Besides custom hooks, other React features (like class components, useState, useEffect) are banned from the application code to simplify the architecture and make the codebase more approachable for developers with a TypeScript background.
42
-
43
- ## Conclusion
44
-
45
- UECA development emphasizes the use of TypeScript and JSX, with minimal reliance on React-specific features. By focusing on a declarative and modular approach and maintaining structural consistency, UECA aims to create maintainable and scalable web applications that are easy to understand and develop. Developers familiar with TypeScript and JSX can quickly adapt to UECA, even without extensive experience in React.