ueca-react 2.0.4 → 2.0.5

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
@@ -3,6 +3,16 @@
3
3
 
4
4
  UECA-React is a framework for building scalable React applications with a unified and encapsulated component architecture. It simplifies development by hiding the complexities of React and MobX behind a consistent component pattern.
5
5
 
6
+ ## Live Demos
7
+
8
+ See UECA-React in action with complete working applications developed with GitHub Copilot AI assistance:
9
+
10
+ **🔗 Demo 1:** [MUI Components](https://nekutuzov.github.io/ueca-react-app-demo1/)
11
+ **📂 Source Code:** [GitHub Repository](https://github.com/nekutuzov/ueca-react-app-demo1)
12
+
13
+ **🔗 Demo 2:** [Storybook](https://nekutuzov.github.io/ueca-react-app-demo2/)
14
+ **📂 Source Code:** [GitHub Repository](https://github.com/nekutuzov/ueca-react-app-demo2)
15
+
6
16
  ## Installation
7
17
 
8
18
  To install UECA-React, run the following command:
@@ -69,13 +79,6 @@ const Button = UECA.getFC(useButton);
69
79
  export { ButtonModel, useButton, Button };
70
80
  ```
71
81
 
72
- ### Live Demo
73
-
74
- See UECA-React in action with a complete working application:
75
-
76
- **🔗 Live Demo:** [UECA-React Application Demo](https://nekutuzov.github.io/ueca-react-app/)
77
- **📂 Source Code:** [GitHub Repository](https://github.com/nekutuzov/ueca-react-app)
78
-
79
82
  For more detailed information, check out the [full documentation](./docs/index.md).
80
83
 
81
84
  ## Features
@@ -111,4 +114,6 @@ This project is licensed under the ISC License - see the [LICENSE](./LICENSE) fi
111
114
 
112
115
  **Aleksey Suvorov**
113
116
  Email: cranesoft@protonmail.com
114
- GitHub: [nekutuzov/ueca-react-npm](https://github.com/nekutuzov/ueca-react-npm)
117
+ Website: [cranesoft.net](https://cranesoft.net)
118
+ GitHub: [nekutuzov](https://github.com/nekutuzov)
119
+ Npm: [nekutuzov](https://www.npmjs.com/~nekutuzov)
@@ -0,0 +1,293 @@
1
+ # Specialized Component Factories in UECA-React
2
+
3
+ ## Overview
4
+
5
+ Specialized Component Factories is a powerful pattern in UECA-React that allows you to create pre-configured variants of existing components by wrapping their hooks with preset properties. This pattern maintains full UECA compliance while providing convenient shortcuts for commonly-used component configurations.
6
+
7
+ ## The Pattern
8
+
9
+ A specialized factory is a custom hook that calls an existing component hook with pre-configured parameters, returning a model of the same type. This eliminates code duplication and provides type-safe convenience components.
10
+
11
+ ### Basic Structure
12
+
13
+ ```tsx
14
+ // Base component
15
+ function useBaseComponent(params?: BaseComponentParams): BaseComponentModel {
16
+ const struct: BaseComponentStruct = {
17
+ props: { /* ... */ },
18
+ View: () => <div>...</div>
19
+ };
20
+ const model = useUIBase(struct, params);
21
+ return model;
22
+ }
23
+
24
+ // Specialized factory
25
+ type SpecializedParams = Omit<BaseComponentParams, "presetProp">;
26
+
27
+ function useSpecializedComponent(params?: SpecializedParams): BaseComponentModel {
28
+ return useBaseComponent({
29
+ ...params,
30
+ presetProp: "preset-value"
31
+ });
32
+ }
33
+
34
+ const SpecializedComponent = UECA.getFC(useSpecializedComponent);
35
+ ```
36
+
37
+ ## Real-World Example: CloseIconButton
38
+
39
+ One of the most practical applications of this pattern is the `CloseIconButton` - a specialized variant of `IconButton` with a pre-configured close icon.
40
+
41
+ ```tsx
42
+ // Base IconButton component
43
+ type IconButtonStruct = UIBaseStruct<{
44
+ props: {
45
+ color: Palette | "inherit";
46
+ disabled: boolean;
47
+ iconView: React.ReactNode;
48
+ size: "small" | "medium" | "large";
49
+ };
50
+ events: {
51
+ onClick: (source: IconButtonModel) => UECA.MaybePromise;
52
+ };
53
+ }>;
54
+
55
+ function useIconButton(params?: IconButtonParams): IconButtonModel {
56
+ const struct: IconButtonStruct = {
57
+ props: {
58
+ id: useIconButton.name,
59
+ color: "inherit",
60
+ disabled: false,
61
+ iconView: undefined,
62
+ size: "medium",
63
+ },
64
+ methods: {
65
+ click: () => {
66
+ if (!model.disabled && model.onClick) {
67
+ asyncSafe(() => model.onClick(model));
68
+ }
69
+ }
70
+ },
71
+ View: () => (
72
+ <button
73
+ id={model.htmlId()}
74
+ className={`ueca-icon-button ueca-icon-button-${model.size}`}
75
+ disabled={model.disabled}
76
+ onClick={model.click}
77
+ >
78
+ {model.iconView}
79
+ </button>
80
+ )
81
+ };
82
+ const model = useUIBase(struct, params);
83
+ return model;
84
+ }
85
+
86
+ // Specialized CloseIconButton factory
87
+ type CloseIconButtonParams = Omit<IconButtonParams, "iconView">;
88
+
89
+ function useCloseIconButton(params?: CloseIconButtonParams): IconButtonModel {
90
+ return useIconButton({
91
+ ...params,
92
+ iconView: (
93
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
94
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
95
+ </svg>
96
+ )
97
+ });
98
+ }
99
+
100
+ const CloseIconButton = UECA.getFC(useCloseIconButton);
101
+
102
+ export {
103
+ IconButtonModel, IconButtonParams, useIconButton, IconButton,
104
+ useCloseIconButton, CloseIconButton
105
+ };
106
+ ```
107
+
108
+ ### Usage
109
+
110
+ ```tsx
111
+ // Instead of writing this every time:
112
+ <IconButton
113
+ iconView={
114
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
115
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
116
+ </svg>
117
+ }
118
+ onClick={() => model.close()}
119
+ />
120
+
121
+ // You can write:
122
+ <CloseIconButton onClick={() => model.close()} />
123
+ ```
124
+
125
+ ## Key Benefits
126
+
127
+ ### 1. **Type Safety**
128
+ Using `Omit` prevents users from accidentally overriding the preset properties:
129
+ ```tsx
130
+ type CloseIconButtonParams = Omit<IconButtonParams, "iconView">;
131
+ ```
132
+
133
+ ### 2. **Zero Overhead**
134
+ No wrapper components, no extra layers - just a convenient way to call the base hook with preset values. The returned model is identical to what the base hook returns.
135
+
136
+ ### 3. **Full UECA Compliance**
137
+ As long as the factory hook returns a valid UECA model with the proper structure (props, children, methods, events, View), the framework handles it perfectly.
138
+
139
+ ### 4. **DRY Principle**
140
+ All logic stays in the base component. The factory is purely declarative configuration.
141
+
142
+ ### 5. **Composability**
143
+ You can create multiple specialized variants that all return the same model type, allowing them to be used interchangeably.
144
+
145
+ ## More Examples
146
+
147
+ ### Submit and Cancel Buttons
148
+
149
+ ```tsx
150
+ type SubmitButtonParams = Omit<ButtonParams, "type" | "variant" | "color">;
151
+
152
+ function useSubmitButton(params?: SubmitButtonParams): ButtonModel {
153
+ return useButton({
154
+ ...params,
155
+ type: "submit",
156
+ variant: "contained",
157
+ color: "primary.main",
158
+ contentView: params?.contentView || "Submit"
159
+ });
160
+ }
161
+
162
+ type CancelButtonParams = Omit<ButtonParams, "variant" | "color">;
163
+
164
+ function useCancelButton(params?: CancelButtonParams): ButtonModel {
165
+ return useButton({
166
+ ...params,
167
+ variant: "outlined",
168
+ color: "text.secondary",
169
+ contentView: params?.contentView || "Cancel"
170
+ });
171
+ }
172
+ ```
173
+
174
+ ### Specialized Alerts
175
+
176
+ ```tsx
177
+ function useSuccessAlert(params?: Omit<AlertParams, "severity">): AlertModel {
178
+ return useAlert({
179
+ ...params,
180
+ severity: "success"
181
+ });
182
+ }
183
+
184
+ function useErrorAlert(params?: Omit<AlertParams, "severity">): AlertModel {
185
+ return useAlert({
186
+ ...params,
187
+ severity: "error"
188
+ });
189
+ }
190
+ ```
191
+
192
+ ### Icon Buttons with Common Icons
193
+
194
+ ```tsx
195
+ function useMenuIconButton(params?: Omit<IconButtonParams, "iconView">): IconButtonModel {
196
+ return useIconButton({
197
+ ...params,
198
+ iconView: (
199
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
200
+ <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
201
+ </svg>
202
+ )
203
+ });
204
+ }
205
+
206
+ function useDeleteIconButton(params?: Omit<IconButtonParams, "iconView">): IconButtonModel {
207
+ return useIconButton({
208
+ ...params,
209
+ iconView: (
210
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
211
+ <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
212
+ </svg>
213
+ )
214
+ });
215
+ }
216
+ ```
217
+
218
+ ## Best Practices
219
+
220
+ ### 1. Use `Omit` for Preset Properties
221
+ Always remove preset properties from the params type to prevent conflicts:
222
+ ```tsx
223
+ type SpecializedParams = Omit<BaseParams, "presetProp1" | "presetProp2">;
224
+ ```
225
+
226
+ ### 2. Allow Override for Non-Critical Props
227
+ For properties like `contentView` in buttons, you can provide defaults while still allowing overrides:
228
+ ```tsx
229
+ function useSubmitButton(params?: SubmitButtonParams): ButtonModel {
230
+ return useButton({
231
+ ...params,
232
+ contentView: params?.contentView || "Submit", // Default but overridable
233
+ type: "submit" // Always preset, not overridable
234
+ });
235
+ }
236
+ ```
237
+
238
+ ### 3. Export Both Hook and Component
239
+ Follow the standard UECA pattern:
240
+ ```tsx
241
+ const SpecializedComponent = UECA.getFC(useSpecializedComponent);
242
+ export { useSpecializedComponent, SpecializedComponent };
243
+ ```
244
+
245
+ ### 4. Keep Factories Simple
246
+ Specialized factories should only preset configuration - avoid adding new logic or state. Complex behavior should be handled by creating a new full component.
247
+
248
+ ### 5. Document the Preset Values
249
+ Make it clear what properties are preset and cannot be overridden:
250
+ ```tsx
251
+ /**
252
+ * IconButton pre-configured with a close (X) icon.
253
+ * The iconView property is preset and cannot be overridden.
254
+ */
255
+ function useCloseIconButton(params?: CloseIconButtonParams): IconButtonModel {
256
+ // ...
257
+ }
258
+ ```
259
+
260
+ ## When to Use This Pattern
261
+
262
+ ### ✅ Good Use Cases
263
+ - Common icon button variants (close, menu, delete, etc.)
264
+ - Form buttons with standard styling (submit, cancel, save)
265
+ - Alert/notification variants with preset severity
266
+ - Text fields with specific input types (email, password, phone)
267
+ - Dialog variants with preset configurations
268
+
269
+ ### ❌ When Not to Use
270
+ - When the component needs additional state or logic
271
+ - When you need to modify the component's behavior, not just configuration
272
+ - When the preset is only used once or twice in the application
273
+ - When the factory would need to preset too many properties (create a full component instead)
274
+
275
+ ## Relationship with Component Extension
276
+
277
+ This pattern differs from Component Extension (using `useXXX` with base struct):
278
+
279
+ | Specialized Factory | Component Extension |
280
+ |---------------------|---------------------|
281
+ | Calls existing hook with preset params | Creates new component extending base struct |
282
+ | Returns base model type | Returns new, extended model type |
283
+ | Zero overhead | Slight overhead for extension |
284
+ | Cannot add new props/methods | Can add new props, methods, events |
285
+ | Best for configuration variants | Best for behavioral extensions |
286
+
287
+ ## Conclusion
288
+
289
+ Specialized Component Factories provide a UECA-compliant way to create convenient, type-safe variants of existing components. By leveraging TypeScript's `Omit` utility type and the spread operator, you can eliminate boilerplate code while maintaining full framework compatibility.
290
+
291
+ This pattern is particularly valuable for UI component libraries where many variations of a base component are needed, but each variation is just a configuration difference rather than a behavioral difference.
292
+
293
+ Remember: If your hook returns a valid UECA model, the framework will handle it correctly - giving you flexibility to create elegant abstractions that suit your application's needs.
package/docs/index.md CHANGED
@@ -34,10 +34,11 @@
34
34
 
35
35
  ### [16. Component Extension in UECA-React](/docs/Component%20Extension%20in%20UECA-React.md)
36
36
 
37
- ### [17. Tracing](/docs/Tracing%20in%20UECA-React.md)
37
+ ### [17. Specialized Component Factories](/docs/Specialized%20Component%20Factories%20in%20UECA-React.md)
38
38
 
39
- ### [18. Standard Code Template](/docs/code-template.md)
39
+ ### [18. Tracing](/docs/Tracing%20in%20UECA-React.md)
40
40
 
41
+ ### [19. Standard Code Template](/docs/code-template.md)
41
42
  ---
42
43
 
43
44
  ## UECA-React Code Example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ueca-react",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Unified Encapsulated Component Architecture for React",
5
5
  "author": "Aleksey Suvorov <cranesoft@protonmail.com>",
6
6
  "license": "ISC",
@@ -22,8 +22,7 @@
22
22
  "type": "git",
23
23
  "url": "https://github.com/nekutuzov/ueca-react-npm.git"
24
24
  },
25
- "homepage": "https://github.com/nekutuzov/ueca-react-npm#readme",
26
- "demo": "https://nekutuzov.github.io/ueca-react-app/",
25
+ "homepage": "https://cranesoft.net",
27
26
  "bugs": {
28
27
  "url": "https://github.com/nekutuzov/ueca-react-npm/issues"
29
28
  },
@@ -71,4 +70,4 @@
71
70
  "vite-plugin-dts": "^4.5.4",
72
71
  "vitest": "^3.1.1"
73
72
  }
74
- }
73
+ }