react-declarative 2.7.105 → 2.7.107

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,1321 +1,1321 @@
1
- <img src="./assets/icons/logo.svg" height="85px" align="right">
2
-
3
- # ⚛️ react-declarative
4
-
5
- > [MUI](https://mui.com/) json endpoint form builder. Check this [Storybook](https://github.com/react-declarative/react-declarative-storybook), the [Playground](https://react-declarative-playground.github.io/) and the [Docs Folder](./docs/Readme.md) for more samples. Also [Playwright End-to-End Testbed](https://github.com/react-declarative/react-declarative-e2e) available
6
-
7
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/react-declarative/react-declarative)
8
- [![npm](https://img.shields.io/npm/v/react-declarative.svg?style=flat-square)](https://npmjs.org/package/react-declarative)
9
-
10
- ![meme](./meme.png)
11
-
12
- A React view builder which interacts with a JSON endpoint to generate nested 12-column grids with input fields and automatic state management in a declarative style. Endpoint is typed by TypeScript guards (**IntelliSense** available). This tool is based on `MUI` components, so your application will look normal on any device...
13
-
14
- **More than Forms:** can be used for build any UI like dashboards, CRM and ERP, mobile apps. Solving your problems. *⭐Star* and *💻Fork* It will be appreciated
15
-
16
- <!--
17
-
18
- ## Contents
19
-
20
- 1. [Short review](./README.md#short-review)
21
- 2. [Quick start](./README.md#quick-start)
22
- 3. [Installation](./README.md#installation)
23
- 4. [Demos](./README.md#demos)
24
- 5. [Declarative Scaffold component](./README.md#declarative-scaffold-component)
25
- 6. [Declarative KanbanView component](./README.md#declarative-kanbanview-component)
26
- 7. [Declarative WizardView component](./README.md#declarative-wizardview-component)
27
- 8. [VisibilityView and FeatureView components](./README.md#visibilityview-and-featureview-components)
28
- 9. [JSON-templated view engine](./README.md#json-templated-view-engine)
29
- 10. [JSON-templated grid engine](./README.md#json-templated-grid-engine)
30
- 11. [DOM Frames with infinite scroll and transparent-api virtualization](./README.md#dom-frames-with-infinite-scroll-and-transparent-api-virtualization)
31
- 12. [Async hooks and Action components](./README.md#async-hooks-and-action-components)
32
- 13. [Async pipe port](./README.md#async-pipe-port)
33
- 14. [Structural directive port](./README.md#structural-directive-port)
34
- 15. [Animated view transition](./README.md#animated-view-transition)
35
- 16. [Build-in router](./README.md#build-in-router)
36
- 17. [MapReduce Data Pipelines](./README.md#mapreduce-data-pipelines)
37
- 18. [Ref-managed MVVM collection](./README.md#ref-managed-mvvm-collection)
38
- 19. [See also](./README.md#see-also)
39
- 20. [Patterns inside](./README.md#patterns-inside)
40
- 21. [Philosophy notes](./README.md#philosophy-notes)
41
- 22. [License](./README.md#license)
42
- 23. [Thanks](./README.md#thanks)
43
-
44
- -->
45
-
46
- ## Playground
47
-
48
- > [!TIP]
49
- > Try without installing directly [in your web browser](https://react-declarative-playground.github.io/)
50
-
51
- ![playground](./assets/playground.gif)
52
-
53
- Link to [the playground](https://react-declarative-playground.github.io/)
54
-
55
- ![mantine](./assets/images/mantine.png)
56
-
57
- Check out how your app will look with the [Mantine theme](https://mantine.dev/) installed. You don't have to change any JSON schemas.
58
-
59
- The [Mantine theme playground](https://react-declarative-mantine.github.io/)
60
-
61
- ## Using with AI
62
-
63
- ![screencast](./assets/images/claude.gif)
64
-
65
- There is a guide to make GPT-4 generate form schemas automatically. Check [the docs folder for guide](./docs/Readme.md#using-with-ai)
66
-
67
- <img src="./assets/icons/decart.svg" height="35px" align="right">
68
-
69
- ## Short review
70
-
71
- > [!TIP]
72
- > A few adjectives which can be applied to `react-declarative`
73
-
74
- 1. **Accesible**
75
-
76
- Every callback you need: field or group of fields focus/blur event, form invalidity state, [data-testid](https://medium.com/@automationTest/why-your-development-team-should-use-data-testid-attributes-a83f1ca27ebb) and more
77
-
78
- 2. **Configurable**
79
-
80
- Each field can be statically [hidden by settings dictionary](https://github.com/react-declarative/react-pocketbase-crm?tab=readme-ov-file#feature-model-and-dynamic-field-visibility) and dynamically by form state condition. Same if you want to disable or make field readonly
81
-
82
- 3. **Extendable**
83
-
84
- It allow you to override any field type by slot context or [inject custom JSX](./demo/src/pages/GalleryPage.tsx#L206) directly into form without additional boilerplate. Also that lib can be used with all React ecosystem, for example, try with [Million.js](https://dev.to/tobysolutions/million-30-all-you-need-to-know-3d2), It makes `react-declarative` extreamly performant even on 2016 devices
85
-
86
- 4. **Maintainable**
87
-
88
- Write code without going into [technical debt](https://en.wikipedia.org/wiki/Technical_debt). The big diffrence with [jsonforms](https://jsonforms.io/docs/#how-does-it-work) is you actually write less code cause you don't need `data schema`. In `react-declarative` all validations are build into `ui schema`, so backend endpoint can be changed partially if some properties are unused (see [PATCH method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH))
89
-
90
- 5. **Reflectable**
91
-
92
- Each form schema can be [reflected](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.typeinfo?view=net-8.0) by using `getAvailableFields` for additional inline validations, data cleanup if some fields are not required anymore, data export (generate [excel export](https://www.npmjs.com/package/xlsx) from current form). That extreamly hard to implement with `jsonforms`
93
-
94
- 6. **Reliable**
95
-
96
- React 18 [Concurrent render](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react) used under the hood (state updates in async *useEffect*) so the `jsonforms` will slow down on 200+ fields form, `react-declarative` will not. Also [RPS](https://en.wikipedia.org/wiki/Web_server#requests_per_second) optimised by [debounce of form state change event](https://rxjs.dev/api/operators/debounce). That means you will need less hardware measures on a server side to implement autosubmit
97
-
98
- 7. **Code-Splittable**
99
-
100
- JSON templates can be downloaded statically, builded dynamically, [lazy-loaded dynamically](https://webpack.js.org/concepts/module-federation/)
101
-
102
- 8. **Scalable**
103
-
104
- Easy internationalization with translation dictionaries for Combobox field. [JSX Factory for labels translation](https://github.com/react-declarative/react-i18n-jsx-factory). An organisation for tutoring newbies [with 25 projects with AI](https://github.com/react-declarative/brainjs-cryptocurrency-trend), reactive programming and more
105
-
106
- 9. **Integrable**
107
-
108
- <!--Consolidates multiple aspects of the development process into one cohesive tool, providing a streamlined and efficient workflow for developers. It unifies state management and ui building into a single approach, managing project code modules dependency resolution, asset sharing, and software design, as a result eliminates the heterogeneous made by usage of separate tools.-->
109
- It combines various development tasks into one tool, making the process simpler and faster. It manages app state, user interface building, code dependencies, asset sharing, and software design all in one place.
110
-
111
- 10. **Customisable**
112
-
113
- A game-changer for your app look is the `react-declarative-mantine`. That library provides `<OneSlotFactory />` with all fields redesigned. Check out [how your app can look like](https://react-declarative-mantine.github.io/) without changing any JSON schemas.
114
-
115
- <img src="./assets/icons/masonry.svg" height="35px" align="right">
116
-
117
- ## Quick start
118
-
119
- > [!IMPORTANT]
120
- > There is a `create-react-app` template available [in this repository](https://github.com/react-declarative/cra-template-react-declarative)
121
-
122
- ```bash
123
- yarn create react-app --template cra-template-react-declarative .
124
- ```
125
-
126
- or
127
-
128
- ```bash
129
- npx create-react-app . --template=react-declarative
130
- ```
131
-
132
- <img src="./assets/icons/babylon.svg" height="40px" align="right">
133
-
134
- ## Installation
135
-
136
- > [!NOTE]
137
- > There is a sample app avalible in the [demo](./demo/src/index.tsx) folder...
138
-
139
- ```bash
140
- npm install --save react-declarative tss-react @mui/material @emotion/react @emotion/styled
141
- ```
142
-
143
- <img src="./assets/icons/watch.svg" height="40px" align="right">
144
-
145
- ## Migrate
146
-
147
- > [!NOTE]
148
- > A lightweight version with `<One />` component and dependencies only published in [react-declarative-lite](https://github.com/react-declarative/react-declarative-lite) npm package. Useful when you want to use `<One />` forms in existing app
149
-
150
- ```bash
151
- npm install --save react-declarative-lite
152
- ```
153
-
154
- <img src="./assets/icons/world.svg" height="35px" align="right">
155
-
156
- ## Demos
157
-
158
- > [!NOTE]
159
- > The `react-declarative` is not just a form builder. This one is the huge framework with dashboard adaptive cards builder, crud-based Grid component and more.<br> Check [the docs folder](./docs/Readme.md)
160
-
161
- This tool also provide it's own way of rapid application development by simplifying app state managament. New features appear frequently, so you should be able to [read the project's storybook](https://github.com/react-declarative/react-declarative-storybook), browse [an organization with sample projects](https://github.com/react-declarative), and [read the source code](https://github.com/react-declarative/react-declarative)
162
-
163
- Several starter kits available (Check [Playwright End-to-End testbed](https://github.com/react-declarative/react-declarative-e2e))
164
-
165
- **1. Pure React Starter**
166
-
167
- > [!NOTE]
168
- > GitHub repo: [https://github.com/react-declarative/cra-template-react-declarative](https://github.com/react-declarative/cra-template-react-declarative)
169
-
170
- ```bash
171
- yarn create react-app --template cra-template-react-declarative .
172
- ```
173
-
174
- **2. Ethers.js/React Starter**
175
-
176
- > [!NOTE]
177
- > GitHub repo: [https://github.com/react-declarative/cra-template-solidity](https://github.com/react-declarative/cra-template-solidity)
178
-
179
- ```bash
180
- yarn create react-app --template cra-template-solidity .
181
- ```
182
-
183
- **3. AppWrite/React Starter**
184
-
185
- > [!NOTE]
186
- > GitHub repo: [https://github.com/react-declarative/cra-template-appwrite](https://github.com/react-declarative/cra-template-appwrite)
187
-
188
- ```bash
189
- yarn create react-app --template cra-template-appwrite .
190
- ```
191
-
192
- and few more quite interesting demo projects
193
-
194
- **1. Playwright End-to-End Testbed**
195
-
196
- > [!NOTE]
197
- > GitHub repo: [https://github.com/react-declarative/react-declarative-e2e](https://github.com/react-declarative/react-declarative-e2e)
198
-
199
- ```bash
200
- git clone https://github.com/react-declarative/react-declarative-e2e.git
201
- ```
202
-
203
- **2. ERC-20 Payment gateway**
204
-
205
- > [!NOTE]
206
- > GitHub repo: [https://github.com/react-declarative/erc20-payment-gateway](https://github.com/react-declarative/erc20-payment-gateway)
207
-
208
- ```bash
209
- git clone https://github.com/react-declarative/erc20-payment-gateway.git
210
- ```
211
-
212
- **3. React Face KYC**
213
-
214
- > [!NOTE]
215
- > GitHub repo: [https://github.com/react-declarative/react-face-kyc](https://github.com/react-declarative/react-face-kyc)
216
-
217
- ```bash
218
- git clone https://github.com/react-declarative/react-face-kyc.git
219
- ```
220
-
221
- **4. BrainJS Cryptocurrency Trend**
222
-
223
- > [!NOTE]
224
- > GitHub repo: [https://github.com/react-declarative/brainjs-cryptocurrency-trend](https://github.com/react-declarative/brainjs-cryptocurrency-trend)
225
-
226
- ```bash
227
- git clone https://github.com/react-declarative/brainjs-cryptocurrency-trend.git
228
- ```
229
-
230
- **5. NFT Mint Tool**
231
-
232
- > [!NOTE]
233
- > GitHub repo: [https://github.com/react-declarative/nft-mint-tool](https://github.com/react-declarative/nft-mint-tool)
234
-
235
- ```bash
236
- git clone https://github.com/react-declarative/nft-mint-tool.git
237
- ```
238
-
239
- **6. React PocketBase CRM**
240
-
241
- > [!NOTE]
242
- > GitHub repo: [https://github.com/react-declarative/react-pocketbase-crm](https://github.com/react-declarative/react-pocketbase-crm)
243
-
244
- ```bash
245
- git clone https://github.com/react-declarative/react-pocketbase-crm.git
246
- ```
247
-
248
- **7. ChatGPT Ecommerce Grid**
249
-
250
- > [!NOTE]
251
- > GitHub repo: [https://github.com/react-declarative/chatgpt-ecommerce-prompt](https://github.com/react-declarative/chatgpt-ecommerce-prompt)
252
-
253
- ```bash
254
- git clone https://github.com/react-declarative/chatgpt-ecommerce-prompt.git
255
- ```
256
-
257
- **8. React Native lightweight version of this library**
258
-
259
- > [!NOTE]
260
- > GitHub repo: [https://github.com/react-declarative/rn-declarative](https://github.com/react-declarative/rn-declarative)
261
-
262
- ```bash
263
- git clone https://github.com/react-declarative/rn-declarative.git
264
- ```
265
-
266
- <img src="./assets/icons/fallen.svg" height="40px" align="right">
267
-
268
- ## Declarative Scaffold component
269
-
270
- > [!NOTE]
271
- > Link to the [source code](./demo/src/App.Scaffold2.tsx)
272
-
273
- The `<Scaffold2 />` implements the basic Material Design visual layout structure by using config instead of manual ui elements composition.
274
-
275
- ![scaffold2](./assets/scaffold2.gif)
276
-
277
- ```tsx
278
- const options: IScaffold2Group[] = [
279
- {
280
- id: 'build',
281
- label: 'Build',
282
- children: [
283
- {
284
- id: 'authentication',
285
- label: 'Authentication',
286
- isVisible: async () => await ioc.authService.hasRole('unauthorized'),
287
- icon: PeopleIcon,
288
- tabs: [
289
- { id: 'tab1', label: 'Tab1 in header', },
290
- { id: 'tab2', label: 'Tab2 in header', },
291
- ],
292
- options: [
293
- { id: 'tab1', label: 'Tab1 in side menu' },
294
- { id: 'tab2', label: 'Tab2 in side menu' },
295
- ],
296
- },
297
- { id: 'Database', label: 'Label is optional (can be generated automatically from ID in snake case)', icon: DnsRoundedIcon, },
298
- { id: 'Storage', isDisabled: async () => await myAmazingGuard(), icon: PermMediaOutlinedIcon, },
299
- { id: 'Hosting', icon: PublicIcon, },
300
-
301
- ...
302
-
303
- ```
304
-
305
- <img src="./assets/icons/box.svg" height="40px" align="right">
306
-
307
- ## Declarative KanbanView component
308
-
309
- The `<KanbanView />` allow you to build [kanban](https://en.wikipedia.org/wiki/Kanban_(development)) boards with realtime support
310
-
311
- ![kanbanview](./assets/kanbanview.gif)
312
-
313
- ```tsx
314
-
315
- const rows: IBoardRow<ILeadRow>[] = [
316
- {
317
- label: "Display name",
318
- value: (id, employee) =>
319
- [employee.first_name, employee.last_name].join(" "),
320
- },
321
- {
322
- label: "Email",
323
- value: (id, employee) => employee.email,
324
- click: (id, data, payload) => payload.pickEmployeePreviewModal(id),
325
- },
326
- {
327
- label: "Phone",
328
- value: (id, employee) => employee.phone,
329
- },
330
- {
331
- label: "Hire date",
332
- value: (id, employee) => employee.hire_date,
333
- },
334
- ];
335
-
336
- const columns: IBoardColumn<ILeadRow>[] = [
337
- {
338
- color: "#00ACC1",
339
- column: "cold",
340
- label: "Cold",
341
- rows,
342
- },
343
- {
344
- color: "#9C27B0",
345
- column: "contact",
346
- label: "Contact",
347
- rows,
348
- },
349
- {
350
- color: "#FFA000",
351
- column: "draft",
352
- label: "Draft",
353
- rows,
354
- },
355
- {
356
- color: "#2E7D32",
357
- column: "deal",
358
- label: "In a deal",
359
- rows,
360
- },
361
- ];
362
-
363
- ...
364
-
365
- <KanbanView<ILeadRow>
366
- sx={{
367
- height: "calc(100vh - 145px)",
368
- }}
369
- onChangeColumn={handleChangeColumn}
370
- columns={columns}
371
- items={data}
372
- />
373
- ```
374
-
375
- <img src="./assets/icons/x.svg" height="55px" align="right">
376
-
377
- ## Declarative WizardView component
378
-
379
- The `<WizardView />` component allows you to build [action wizard](https://en.wikipedia.org/wiki/Wizard_(software)) with [stepper](https://mui.com/material-ui/react-stepper/) and nested routing
380
-
381
- ![wizardview](./assets/wizardview.gif)
382
-
383
- ```tsx
384
-
385
- const steps: IWizardStep[] = [
386
- {
387
- id: "select",
388
- label: "Choose file",
389
- },
390
- {
391
- id: "validate",
392
- label: "Validation",
393
- },
394
- {
395
- id: "import",
396
- label: "Import",
397
- },
398
- ];
399
-
400
- const routes: IWizardOutlet[] = [
401
- {
402
- id: "select",
403
- element: SelectFileView,
404
- isActive: (pathname) => !!parseRouteUrl("/select-file", pathname),
405
- },
406
- {
407
- id: "validate",
408
- element: ValidateFileView,
409
- isActive: (pathname) => !!parseRouteUrl("/validate-file", pathname),
410
- },
411
- {
412
- id: "import",
413
- element: ImportFileView,
414
- isActive: (pathname) => !!parseRouteUrl("/import-file", pathname),
415
- },
416
- ];
417
-
418
- ...
419
-
420
- <WizardView pathname="/select-file" steps={steps} routes={routes} />
421
-
422
- ...
423
-
424
- const SelectFileView = ({
425
- history
426
- }: IWizardOutletProps) => {
427
- return (
428
- <WizardContainer
429
- Navigation={
430
- <WizardNavigation
431
- hasNext
432
- onNext={() => history.replace("/validate-file")}
433
- />
434
- }
435
- >
436
- <p>123</p>
437
- </WizardContainer>
438
- );
439
- };
440
-
441
- ```
442
-
443
- <img src="./assets/icons/magnum-opus.svg" height="30px" align="right">
444
-
445
- ## VisibilityView and FeatureView components
446
-
447
- The `<VisibilityView />` and `<FeatureView />` components allows you to build configurable UI by using [reflection](https://en.wikipedia.org/wiki/Reflective_programming)
448
-
449
- ![visibility](./assets/visibility.gif)
450
-
451
- ```tsx
452
- const groups: IVisibilityGroup[] = [
453
- {
454
- name: "employee_visibility",
455
- /**
456
- * @type {IField[] | TypedField[]}
457
- * @description Same field type from `<One />` template engine
458
- */
459
- fields: employee_fields,
460
- },
461
- ];
462
-
463
- ...
464
-
465
- <VisibilityView
466
- expandAll
467
- data={{ employee_visibility: data }}
468
- groups={groups}
469
- onChange={({ employee_visibility }) => onChange(employee_visibility)}
470
- />
471
- ```
472
-
473
- By using [feature-oriented programming](https://en.wikipedia.org/wiki/Feature-oriented_domain_analysis) you can adjust view to different roles of users by partially hiding text, images and buttons
474
-
475
- ```tsx
476
- const features: IFeatureGroup[] = [
477
- {
478
- title: "Employee",
479
- expanded: true,
480
- children: [
481
- {
482
- name: "employee_preview_modal",
483
- label: "Employee preview modal",
484
- description: "Click on row open preview modal",
485
- },
486
- {
487
- name: "employee_toggle_inactive",
488
- label: "Employee toggle inactive",
489
- description: "Can toggle employee activity",
490
- },
491
- ],
492
- },
493
- ];
494
-
495
- ...
496
-
497
- <FeatureView
498
- expandAll
499
- data={data}
500
- features={features}
501
- onChange={onChange}
502
- />
503
-
504
- ...
505
-
506
- <If
507
- payload={userId}
508
- condition={async (userId) => {
509
- return await ioc.permissionRequestService.getOwnerContactVisibilityByUserId(userId)
510
- }}
511
- Loading="Loading"
512
- Else="Hidden"
513
- >
514
- {owner_contact}
515
- </If>
516
-
517
- ```
518
-
519
-
520
- <img src="./assets/icons/solomon.svg" height="55px" align="right">
521
-
522
- ## JSON-templated view engine
523
-
524
- **1. Layout grid**
525
-
526
- > [!NOTE]
527
- > Link to the [source code](./demo/src/pages/LayoutPage.tsx)
528
-
529
- ![layout-grid](./assets/layout.gif)
530
-
531
- ```tsx
532
- const fields: TypedField[] = [
533
- {
534
- type: FieldType.Line,
535
- title: 'User info',
536
- },
537
- {
538
- type: FieldType.Group,
539
- phoneColumns: '12',
540
- tabletColumns: '6',
541
- desktopColumns: '4',
542
- fields: [
543
- {
544
- type: FieldType.Text,
545
- title: 'First name',
546
- defaultValue: 'Petr',
547
- description: 'Your first name',
548
- leadingIcon: Face,
549
- focus() { console.log("focus :-)"); },
550
- blur() { console.log("blur :-("); },
551
- name: 'firstName',
552
- },
553
- {
554
- type: FieldType.Text,
555
- title: 'Last name',
556
- defaultValue: 'Tripolsky',
557
- description: 'Your last name',
558
- name: 'lastName',
559
- },
560
-
561
- ...
562
-
563
- ];
564
- ```
565
-
566
- **2. Form validation**
567
-
568
- > [!NOTE]
569
- > Link to the [source code](./demo/src/pages/ValidationPage.tsx)
570
-
571
- ![form-validation](./assets/validation.gif)
572
-
573
- ```tsx
574
- const fields: TypedField[] = [
575
- {
576
- type: FieldType.Text,
577
- name: 'email',
578
- trailingIcon: Email,
579
- defaultValue: 'tripolskypetr@gmail.com',
580
- isInvalid({email}) {
581
- const expr = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g;
582
- if (!expr.test(email)) {
583
- return 'Invalid email address';
584
- } else {
585
- return null;
586
- }
587
- },
588
- isDisabled({disabled}) {
589
- return disabled;
590
- },
591
- isVisible({visible}) {
592
- return visible;
593
- }
594
- },
595
- {
596
- type: FieldType.Expansion,
597
- title: 'Settings',
598
- description: 'Hide or disable',
599
- fields: [
600
- {
601
- type: FieldType.Switch,
602
- title: 'Mark as visible',
603
- name: 'visible',
604
- defaultValue: true,
605
- },
606
-
607
- ...
608
-
609
- ```
610
-
611
- **3. Gallery of controls**
612
-
613
- > [!NOTE]
614
- > Link to the [source code](./demo/src/pages/GalleryPage.tsx)
615
-
616
- ![gallery](./assets/gallery.gif)
617
-
618
- ```tsx
619
- const fields: TypedField[] = [
620
- {
621
- type: FieldType.Paper,
622
- fields: [
623
- {
624
- type: FieldType.Line,
625
- title: 'Checkboxes',
626
- },
627
- {
628
- type: FieldType.Checkbox,
629
- name: 'checkbox1',
630
- columns: '3',
631
- title: 'Checkbox 1',
632
- },
633
- {
634
- type: FieldType.Checkbox,
635
- name: 'checkbox2',
636
- columns: '3',
637
- title: 'Checkbox 2',
638
- },
639
-
640
- ...
641
-
642
- ```
643
-
644
- **4. JSX Injection**
645
-
646
- > [!NOTE]
647
- > Link to the [source code](./demo/src/pages/GalleryPage.tsx)
648
-
649
- ```tsx
650
- const fields: TypedField[] = [
651
- {
652
- type: FieldType.Paper,
653
- fields: [
654
- {
655
- type: FieldType.Component,
656
- element: (props) => <Logger {...props}/>,
657
- },
658
- ],
659
- },
660
-
661
- ...
662
-
663
- ];
664
- ```
665
-
666
- **5. UI-Kit override**
667
-
668
- > [!NOTE]
669
- > Link to the [source code](./src/components/One/components/SlotFactory)
670
-
671
- ```tsx
672
- <OneSlotFactory
673
- CheckBox={MyCheckBox}
674
- Text={MyInput}
675
- ...
676
- >
677
- <One
678
- ...
679
- />
680
- ...
681
- </OneSlotFactory>
682
- ```
683
-
684
- **6. Hiding fields by business functions**
685
-
686
- > [!NOTE]
687
- > See [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)
688
-
689
- ```tsx
690
- const fields: TypedField[] = [
691
- {
692
- type: FieldType.Text,
693
- name: 'phone',
694
- hidden: ({ payload }) => {
695
- return !payload.features.has('show-phone-number');
696
- },
697
- },
698
-
699
- ...
700
-
701
- ];
702
- ```
703
-
704
- <img src="./assets/icons/saturn.svg" height="35px" align="right">
705
-
706
- ## JSON-templated grid engine
707
-
708
- > [!NOTE]
709
- > Link to the [source code](./demo/src/pages/ListPage.tsx)
710
-
711
- Adaptive json-configurable data grid with build-in mobile device support
712
-
713
- ![list](./assets/images/list.png)
714
-
715
- ```tsx
716
-
717
- const filters: TypedField[] = [
718
- {
719
- type: FieldType.Text,
720
- name: 'firstName',
721
- title: 'First name',
722
- },
723
- {
724
- type: FieldType.Text,
725
- name: 'lastName',
726
- title: 'Last name',
727
- }
728
- ];
729
-
730
- const columns: IColumn[] = [
731
- {
732
- type: ColumnType.Text,
733
- field: 'id',
734
- headerName: 'ID',
735
- width: (fullWidth) => Math.max(fullWidth - 650, 200),
736
- columnMenu: [
737
- {
738
- action: 'test-action',
739
- label: 'Column action',
740
- },
741
- ],
742
- },
743
- ...
744
- ];
745
-
746
- const actions: IListAction[] = [
747
- {
748
- type: ActionType.Add,
749
- label: 'Create item'
750
- },
751
- ...
752
- ];
753
-
754
- const operations: IListOperation[] = [
755
- {
756
- action: 'operation-one',
757
- label: 'Operation one',
758
- },
759
- ];
760
-
761
- const chips: IListChip[] = [
762
- {
763
- label: 'The chip1_enabled is true',
764
- name: 'chip1_enabled',
765
- color: '#4caf50',
766
- },
767
- ...
768
- ];
769
-
770
- const rowActions: IListRowAction[] = [
771
- {
772
- label: 'chip1',
773
- action: 'chip1-action',
774
- isVisible: ({ chip1_enabled }) => chip1_enabled,
775
- },
776
- ...
777
- ];
778
-
779
- ...
780
-
781
- return (
782
- <ListTyped
783
- withMobile
784
- withSearch
785
- withArrowPagination
786
- rowActions={rowActions}
787
- actions={actions}
788
- filters={filters}
789
- columns={columns}
790
- operations={operations}
791
- chips={chips}
792
- />
793
- )
794
-
795
- ```
796
-
797
- <img src="./assets/icons/square_compasses.svg" height="35px" align="right">
798
-
799
- ## DOM Frames with infinite scroll and `transparent-api virtualization`
800
-
801
- > [!NOTE]
802
- > You can use [InfiniteView](./src/components/InfiniteView/InfiniteView.tsx) for always-mounted or [VirtualView](./src/components/VirtualView/VirtualView.tsx) for virtualized infinite lists
803
-
804
- ![virtualization](./assets/virtualization.gif)
805
-
806
- ```tsx
807
- <VirtualView
808
- component={Paper}
809
- sx={{
810
- width: "100%",
811
- height: 250,
812
- mb: 1,
813
- }}
814
- onDataRequest={() => {
815
- console.log('data-request');
816
- setItems((items) => [
817
- ...items,
818
- ...[uuid(), uuid(), uuid(), uuid(), uuid()],
819
- ]);
820
- }}
821
- >
822
- {items.map((item) => (
823
- <span key={item}>{item}</span>
824
- ))}
825
- </VirtualView>
826
- ```
827
-
828
- <img src="./assets/icons/whenthesummerdies.svg" height="35px" align="right">
829
-
830
- ## Async hooks and Action components
831
-
832
- > [!NOTE]
833
- > Hooks for fetching, caching and updating asynchronous data. Promise-based [command pattern](https://docs.devexpress.com/WPF/17353/mvvm-framework/commands/delegate-commands) ui components. The hooks will help you to avoid [multiple POST method execution](https://en.wikipedia.org/wiki/REST) when user missclick button. The components will show load indicator while `onClick` promise is pending
834
-
835
- The `useAsyncProgress` will manage percent range of execution (`0% - 100%` for `<LinearProgress />` value)
836
-
837
- ```tsx
838
- const { execute } = useAsyncProgress(
839
- async ({ data }) => {
840
- await createContact(data);
841
- },
842
- {
843
- onProgress: (percent) => {
844
- setProgress(percent);
845
- },
846
- onError: (errors) => {
847
- setErrors(errors);
848
- },
849
- onEnd: (isOk) => {
850
- history.replace("/report");
851
- },
852
- }
853
- );
854
-
855
- ...
856
-
857
- <ActionButton
858
- onClick={async () => {
859
- const file = await chooseFile(
860
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
861
- );
862
- if (file) {
863
- const rows = await parseExcelContacts(file);
864
- execute(
865
- rows.map((row, idx) => ({
866
- data: row,
867
- label: `Row №${idx + 2}`,
868
- }))
869
- );
870
- }
871
- }}
872
- >
873
- Choose XLSX
874
- </ActionButton>
875
- ```
876
-
877
- The `useSinglerunAction` will not execute any additional calls while original promise is pending
878
-
879
- ```tsx
880
- const { execute } = useSinglerunAction(
881
- async () => {
882
- const file = await chooseFile("image/jpeg, image/png");
883
- if (file) {
884
- const filePath = await ioc.appwriteService.uploadFile(file);
885
- onChange(filePath);
886
- }
887
- }
888
- );
889
-
890
- ...
891
-
892
- <ActionIcon onClick={execute}>
893
- <CloudUploadIcon />
894
- </ActionIcon>
895
- ```
896
-
897
- The `useQueuedAction` will queue all promise fulfillment in functions execution order. Quite useful while [state reducer pattern](https://en.wikipedia.org/wiki/Redux_(JavaScript_library)) when coding [realtime](https://en.wikipedia.org/wiki/WebSocket).
898
-
899
- ```tsx
900
- const { execute } = useQueuedAction(
901
- async ({ type, payload }) => {
902
- if (type === "create-action") {
903
- ...
904
- }
905
- if (type === "update-action") {
906
-
907
- }
908
- if (type === "remove-action") {
909
- ...
910
- }
911
- },
912
- {
913
- onLoadStart: () => ioc.layoutService.setAppbarLoader(true),
914
- onLoadEnd: () => ioc.layoutService.setAppbarLoader(false),
915
- }
916
- );
917
-
918
- ...
919
-
920
- useEffect(() => ioc.kanbanService.createSubject.subscribe(execute), []);
921
-
922
- useEffect(() => ioc.kanbanService.updateSubject.subscribe(execute), []);
923
-
924
- useEffect(() => ioc.kanbanService.removeSubject.subscribe(execute), []);
925
-
926
- ```
927
-
928
- The `usePreventAction` will prevent any other action execution [while single one is pending](https://en.wikipedia.org/wiki/Semaphore_(programming))
929
-
930
- ```tsx
931
- const {
932
- handleLoadStart,
933
- handleLoadEnd,
934
- loading,
935
- } = usePreventAction();
936
-
937
- ...
938
-
939
- <ActionButton
940
- disabled={loading}
941
- onLoadStart={handleLoadStart}
942
- onLoadEnd={handleLoadEnd}
943
- >
944
- Action 1
945
- </ActionButton>
946
-
947
- ...
948
-
949
- <ActionButton
950
- disabled={loading}
951
- onLoadStart={handleLoadStart}
952
- onLoadEnd={handleLoadEnd}
953
- >
954
- Action 2
955
- </ActionButton>
956
- ```
957
-
958
- The `usePreventNavigate` will [prevent navigate](https://medium.com/@goldhand/routing-design-patterns-fed766ad35fa) while action is running
959
-
960
- ```tsx
961
- const { handleLoadStart, handleLoadEnd } = usePreventNavigate({
962
- history: ioc.routerService,
963
- });
964
-
965
- ...
966
-
967
- <ActionButton
968
- onLoadStart={handleLoadStart}
969
- onLoadEnd={handleLoadEnd}
970
- >
971
- Action
972
- </ActionButton>
973
- ```
974
-
975
- The `useAsyncValue` will help you to manage [react-hooks/rules-of-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) while working with remote data
976
-
977
- ```tsx
978
- const [data, { loading, error }, setData] = useAsyncValue(async () => {
979
- return await getData();
980
- });
981
-
982
- if (loading || error) {
983
- return null;
984
- }
985
-
986
- return (
987
- <pre>
988
- {JSON.stringify(data, null, 2)}
989
- </pre>
990
- )
991
-
992
- ...
993
-
994
- const handleChangeData = async () => {
995
- const newData = await fetchApi('/api/v1/data', {
996
- method: "POST",
997
- ...
998
- })
999
- setData(newData)
1000
- }
1001
-
1002
- ```
1003
-
1004
- <img src="./assets/icons/stone.svg" height="35px" align="right">
1005
-
1006
- ## Async pipe port
1007
-
1008
- > [!NOTE]
1009
- > See [angular2 docs](https://angular.io/api/common/AsyncPipe)
1010
-
1011
- ```tsx
1012
- import { Async } from 'react-declarative'
1013
-
1014
- import { CircularProgress } from '@mui/material'
1015
-
1016
- const PostItem = ({
1017
- id,
1018
- }) => {
1019
-
1020
- const { getPostById } = useBlogApi();
1021
-
1022
- return (
1023
- <Async payload={id} Loader={CircularProgress}>
1024
- {async (id) => {
1025
- const { title, body } = await getPostById(id);
1026
- return (
1027
- <div>
1028
- <p>{title}</p>
1029
- <p>{body}</p>
1030
- </div>
1031
- );
1032
- }}
1033
- </Async>
1034
- );
1035
- };
1036
- ```
1037
-
1038
- <img src="./assets/icons/triangle.svg" height="35px" align="right">
1039
-
1040
- ## Structural directive port
1041
-
1042
- > [!NOTE]
1043
- > See [angular2 docs](https://angular.io/guide/structural-directives)
1044
-
1045
- ```tsx
1046
- import { If } from 'react-declarative'
1047
-
1048
- const ProfilePage = () => {
1049
- const { hasRole } = useRoleApi();
1050
- return (
1051
- <If condition={() => hasRole("admin")}>
1052
- <button>The admin's button</button>
1053
- </If>
1054
- );
1055
- };
1056
- ```
1057
-
1058
- <img src="./assets/icons/monade.svg" height="35px" align="right">
1059
-
1060
- ## Animated view transition
1061
-
1062
- > [!NOTE]
1063
- > Link to the [source code](./demo/src/pages/RevealPage.tsx)
1064
-
1065
- ```tsx
1066
- import { FetchView } from 'react-declarative'
1067
-
1068
- const PostList = () => {
1069
-
1070
- const { getPosts } = useBlogApi();
1071
-
1072
- const state = [
1073
- getPosts,
1074
- ];
1075
-
1076
- return (
1077
- <FetchView state={state} animation="fadeIn">
1078
- {(posts) => (
1079
- <div>
1080
- {posts.map((post, idx) => (
1081
- <p key={idx}>
1082
- <b>{post.title}</b>
1083
- {post.body}
1084
- </p>
1085
- ))}
1086
- </div>
1087
- )}
1088
- </FetchView>
1089
- );
1090
- };
1091
- ```
1092
-
1093
- <img src="./assets/icons/positron.svg" height="35px" align="right">
1094
-
1095
- ## Build-in router
1096
-
1097
- > [!NOTE]
1098
- > Link to the [source code](./demo/src/App.tsx)
1099
-
1100
- ```tsx
1101
- import { Switch } from 'react-declarative';
1102
-
1103
- ...
1104
-
1105
- const routes = [
1106
- {
1107
- path: '/mint-page',
1108
- guard: async () => await ioc.roleService.has('whitelist'),
1109
- prefetch: async () => await ioc.ethersService.init(),
1110
- unload: async () => await ioc.ethersService.dispose(),
1111
- redirect: () => {
1112
- let isOk = true;
1113
- isOk = isOk && ioc.ethersService.isMetamaskAvailable;
1114
- isOk = isOk && ioc.ethersService.isProviderConnected;
1115
- isOk = isOk && ioc.ethersService.isAccountEnabled;
1116
- if (isOk) {
1117
- return "/connect-page";
1118
- }
1119
- return null;
1120
- },
1121
- },
1122
- ];
1123
-
1124
- ...
1125
-
1126
- const App = () => (
1127
- <Switch history={history} items={routes} />
1128
- );
1129
- ```
1130
-
1131
- <img src="./assets/icons/assignation.svg" height="35px" align="right">
1132
-
1133
- ## MapReduce Data Pipelines
1134
-
1135
- > [!NOTE]
1136
- > Link to the [source code](https://github.com/react-declarative/react-face-kyc/blob/master/src/hooks/useFaceWasm/emitters.ts)
1137
-
1138
- ```tsx
1139
- import { Source } from 'react-declarative';
1140
-
1141
- ...
1142
-
1143
- const verifyCompleteEmitter = Source.multicast(() =>
1144
- Source
1145
- .join([
1146
- captureStateEmitter,
1147
- Source.fromInterval(1_000),
1148
- ])
1149
- .reduce((acm, [{ state: isValid }]) => {
1150
- if (isValid) {
1151
- return acm + 1;
1152
- }
1153
- return 0;
1154
- }, 0)
1155
- .tap((ticker) => {
1156
- if (ticker === 1) {
1157
- mediaRecorderInstance.beginCapture();
1158
- }
1159
- })
1160
- .filter((ticker) => ticker === CC_SECONDS_TO_VERIFY)
1161
- .tap(() => {
1162
- mediaRecorderInstance.endCapture();
1163
- })
1164
- );
1165
- ```
1166
-
1167
- <img src="./assets/icons/chronos.svg" height="35px" align="right">
1168
-
1169
- ## Ref-managed MVVM collection
1170
-
1171
- > Link to the [source code](./demo/src/pages/MvvmPage.tsx)
1172
-
1173
- ```tsx
1174
- import { useCollection } from "react-declarative";
1175
-
1176
- ...
1177
-
1178
- const collection = useCollection({
1179
- onChange: (collection, target) => console.log({
1180
- collection,
1181
- target,
1182
- }),
1183
- initialValue: [],
1184
- });
1185
-
1186
- const handleAdd = async () => {
1187
- const { id, ...data } = await fetchApi("/api/v1/counters/create", {
1188
- method: "POST",
1189
- });
1190
- collection.push({
1191
- id,
1192
- ...data,
1193
- });
1194
- };
1195
-
1196
- const handleUpsert = async () => {
1197
- const updatedItems = await fetchApi("/api/v1/counters/list");
1198
- collection.upsert(updatedItems);
1199
- };
1200
-
1201
- return (
1202
- <>
1203
- <button onClick={handleAdd}>Add item</button>
1204
- <button onClick={handleUpsert}>Upsert items</button>
1205
- <ul>
1206
- {collection.map((entity) => (
1207
- <ListItem key={entity.id} entity={entity} />
1208
- ))}
1209
- </ul>
1210
- </>
1211
- );
1212
- ```
1213
-
1214
- <img src="./assets/icons/heraldry.svg" height="35px" align="right">
1215
-
1216
- ## See also
1217
-
1218
- ```tsx
1219
- import { ConstraintView } from 'react-declarative';
1220
- import { DragDropView } from 'react-declarative';
1221
- import { ScrollView } from 'react-declarative';
1222
- import { ScaleView } from 'react-declarative';
1223
- import { FadeView } from 'react-declarative';
1224
- import { TabsView } from 'react-declarative';
1225
- import { WaitView } from 'react-declarative';
1226
- import { PingView } from 'react-declarative';
1227
- import { OfflineView } from 'react-declarative';
1228
- import { RevealView } from 'react-declarative';
1229
- import { SecretView } from 'react-declarative';
1230
- import { PortalView } from 'react-declarative';
1231
- import { RecordView } from 'react-declarative';
1232
- import { CardView } from 'react-declarative';
1233
- import { ErrorView } from 'react-declarative';
1234
- import { AuthView } from 'react-declarative';
1235
- import { InfiniteView } from 'react-declarative';
1236
- import { VirtualView } from 'react-declarative';
1237
- ```
1238
-
1239
- <img src="./assets/icons/consciousness.svg" height="35px" align="right">
1240
-
1241
- ## Patterns inside
1242
-
1243
- 1. [MVVM](https://backbonejs.org/#Collection) - `useCollection`, `useModel`
1244
- 2. [DI](https://angular.io/guide/dependency-injection) - `provide`, `inject`, `createServiceManager`
1245
- 3. [Builder](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0) - `useListEditor`, `useMediaStreamBuilder`
1246
- 4. [Adapter](https://en.wikipedia.org/wiki/Adapter_pattern) - `normalizeText`
1247
- 5. [Observer](https://en.wikipedia.org/wiki/Observer_pattern) - `useChangeSubject`, `useSubject`, `useRenderWaiter`, `Subject`, `BehaviorSubject`, `EventEmitter`, `fromPromise`
1248
- 6. [Command](https://en.wikipedia.org/wiki/Command_pattern) - `ActionTrigger`, `ActionFilter`, `ActionButton`, `ActionToggle`, `ActionMenu`, `ActionIcon`, `ActionModal`, `InfiniteView`, `VirtualView`, `useActionModal`
1249
- 7. [Coroutine](https://en.wikipedia.org/wiki/Coroutine) - `FetchView`, `WaitView`, `PingView`, `Async`, `If`, `useAsyncAction`
1250
- 8. [Routing](https://medium.com/@goldhand/routing-design-patterns-fed766ad35fa) - `Switch`, `OutletView`, `getRouteParams`, `getRouteItem`, `useRouteParams`, `useRouteItem`, `createRouteItemManager`, `createRouteParamsManager`
1251
- 9. [Monad](https://en.wikipedia.org/wiki/Monad_(functional_programming)) - `singleshot`, `cancelable`, `queued`, `cached`, `debounce`, `compose`, `trycatch`, `memoize`, `ttl`, `lock`
1252
- 10. [Composition](https://reactjs.org/docs/composition-vs-inheritance.html) - `VirtualView`, `InfiniteView`, `PortalView`, `RevealView`, `PingView`, `WaitView`, `FadeView`, `ScaleView`, `ScrollView`, `ModalManager`
1253
- 11. [HoC](https://reactjs.org/docs/higher-order-components.html) - `ConstraintView`, `AutoSizer`, `FetchView`, `Async`, `If`
1254
- 12. [Facade](https://en.wikipedia.org/wiki/Facade_pattern) - `Subject`, `Observer`
1255
- 13. [Scheduled-task](https://en.wikipedia.org/wiki/Scheduled-task_pattern) - `Task`, `singlerun`
1256
- 14. [RAD](https://en.wikipedia.org/wiki/Rapid_application_development) - `RecordView`, `CardView`
1257
- 15. [Functional](https://en.wikipedia.org/wiki/Functional_programming) - `useActualValue`, `useActualCallback`, `useActualState`, `useSearchParams`, `useSearchState`, `useChange`
1258
- 16. [Declarative](https://en.wikipedia.org/wiki/Declarative_programming) - `One`, `List`, `Scaffold`, `Scaffold2`, `RecordView`, `CardView`
1259
- 17. [Reactive](https://en.wikipedia.org/wiki/ReactiveX) - `EventEmitter`, `Subject`, `BehaviorSubject`, `Observer`
1260
- 18. [Lambda Architecture](https://en.wikipedia.org/wiki/Lambda_architecture) - `Source`, `Operator`, `useSource`, `useSubscription`
1261
- 19. [Aspect Oriented](https://en.wikipedia.org/wiki/Aspect-oriented_programming) - `serviceManager`, `Source`
1262
- 20. [Reflection](https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/reflection) - `getAvailableFields`, `VisibilityView`
1263
- 21. [Pagination](https://medium.com/@oshiryaeva/offset-vs-cursor-based-pagination-which-is-the-right-choice-for-your-project-e46f65db062f) - `useOffsetPaginator`, `useCursorPaginator`
1264
- 22. [Feature model](https://en.wikipedia.org/wiki/Feature_model) - `useFeatureView`, `useVisibilityView`, `FeatureView`, `VisibilityView`
1265
- 23. [Software Fault Prevention](https://en.wikipedia.org/wiki/Software_fault_tolerance) - `ErrorBoundary`, `ErrorView`
1266
-
1267
- <img src="./assets/icons/cosmos.svg" height="35px" align="right">
1268
-
1269
- ## Philosophy notes
1270
-
1271
- 1. [React: declarative vs imperative](./NOTES.md#react-declarative-vs-imperative)
1272
-
1273
- Declarative programming is when a more qualified specialist writing code in a way when its behavior can be changed by using external config which represent oriented graph of objects
1274
-
1275
- 2. [Fractal pattern](./NOTES.md#fractal-pattern-fractal-project-structure)
1276
-
1277
- Fractal pattern conveys that similar patterns recur progressively and the same thought process is applied to the structuring of codebase i.e All units repeat themselves.
1278
-
1279
- 3. [SOLID in react-declarative](./NOTES.md#solid-in-react-declarative)
1280
-
1281
- SOLID principles described by simple words with examples in a source code
1282
-
1283
- 4. [Product-oriented principles](./NOTES.md#product-oriented-principles)
1284
-
1285
- These principles will help you to keep the code as clean as possible when you need to make a MVP immediately
1286
-
1287
- 5. [Oriented graphs: reduce algoritm difficulty](./NOTES.md#reduce-algoritm-difficulty-when-working-with-oriented-graphs)
1288
-
1289
- The real useful note for working with oriented graphs
1290
-
1291
- 6. [Using underected data flow for building software product line](./NOTES.md#using-underected-data-flow-for-building-software-product-line)
1292
-
1293
- Useful snippets to split procedure code with the inversion of control pattern
1294
-
1295
- 7. [Frontend as a service bus for Service-Oriented Architecture (SOA)](./NOTES.md#frontend-as-a-service-bus-for-service-oriented-architecture-soa)
1296
-
1297
- Serverless compution pros and no cons with react-declarative
1298
-
1299
- 8. [Make SPA Java again](./NOTES.md#make-spa-java-again)
1300
-
1301
- The problem of clean architecture in modern React apps
1302
-
1303
- <img src="./assets/icons/tree.svg" height="65px" align="right">
1304
-
1305
- ## License
1306
-
1307
- > P.S. Got a question? Feel free to [write It in issues](https://github.com/react-declarative/react-declarative/issues), I need traffic
1308
-
1309
- MIT [@tripolskypetr](https://github.com/tripolskypetr)
1310
-
1311
- ## External References
1312
-
1313
- 1. [Playground](https://react-declarative-playground.github.io/)
1314
- 2. [Storybook](https://react-declarative.github.io/)
1315
- 3. [Typedoc](https://react-declarative-typedoc.github.io/)
1316
-
1317
- ## Thanks
1318
-
1319
- Dude, you reached that point, omg. Could you
1320
- [Star it on GitHub](https://github.com/react-declarative/react-declarative) plz)
1321
-
1
+ <img src="./assets/icons/logo.svg" height="85px" align="right">
2
+
3
+ # ⚛️ react-declarative
4
+
5
+ > [MUI](https://mui.com/) json endpoint form builder. Check this [Storybook](https://github.com/react-declarative/react-declarative-storybook), the [Playground](https://react-declarative-playground.github.io/) and the [Docs Folder](./docs/Readme.md) for more samples. Also [Playwright End-to-End Testbed](https://github.com/react-declarative/react-declarative-e2e) available
6
+
7
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/react-declarative/react-declarative)
8
+ [![npm](https://img.shields.io/npm/v/react-declarative.svg?style=flat-square)](https://npmjs.org/package/react-declarative)
9
+
10
+ ![meme](./meme.png)
11
+
12
+ A React view builder which interacts with a JSON endpoint to generate nested 12-column grids with input fields and automatic state management in a declarative style. Endpoint is typed by TypeScript guards (**IntelliSense** available). This tool is based on `MUI` components, so your application will look normal on any device...
13
+
14
+ **More than Forms:** can be used for build any UI like dashboards, CRM and ERP, mobile apps. Solving your problems. *⭐Star* and *💻Fork* It will be appreciated
15
+
16
+ <!--
17
+
18
+ ## Contents
19
+
20
+ 1. [Short review](./README.md#short-review)
21
+ 2. [Quick start](./README.md#quick-start)
22
+ 3. [Installation](./README.md#installation)
23
+ 4. [Demos](./README.md#demos)
24
+ 5. [Declarative Scaffold component](./README.md#declarative-scaffold-component)
25
+ 6. [Declarative KanbanView component](./README.md#declarative-kanbanview-component)
26
+ 7. [Declarative WizardView component](./README.md#declarative-wizardview-component)
27
+ 8. [VisibilityView and FeatureView components](./README.md#visibilityview-and-featureview-components)
28
+ 9. [JSON-templated view engine](./README.md#json-templated-view-engine)
29
+ 10. [JSON-templated grid engine](./README.md#json-templated-grid-engine)
30
+ 11. [DOM Frames with infinite scroll and transparent-api virtualization](./README.md#dom-frames-with-infinite-scroll-and-transparent-api-virtualization)
31
+ 12. [Async hooks and Action components](./README.md#async-hooks-and-action-components)
32
+ 13. [Async pipe port](./README.md#async-pipe-port)
33
+ 14. [Structural directive port](./README.md#structural-directive-port)
34
+ 15. [Animated view transition](./README.md#animated-view-transition)
35
+ 16. [Build-in router](./README.md#build-in-router)
36
+ 17. [MapReduce Data Pipelines](./README.md#mapreduce-data-pipelines)
37
+ 18. [Ref-managed MVVM collection](./README.md#ref-managed-mvvm-collection)
38
+ 19. [See also](./README.md#see-also)
39
+ 20. [Patterns inside](./README.md#patterns-inside)
40
+ 21. [Philosophy notes](./README.md#philosophy-notes)
41
+ 22. [License](./README.md#license)
42
+ 23. [Thanks](./README.md#thanks)
43
+
44
+ -->
45
+
46
+ ## Playground
47
+
48
+ > [!TIP]
49
+ > Try without installing directly [in your web browser](https://react-declarative-playground.github.io/)
50
+
51
+ ![playground](./assets/playground.gif)
52
+
53
+ Link to [the playground](https://react-declarative-playground.github.io/)
54
+
55
+ ![mantine](./assets/images/mantine.png)
56
+
57
+ Check out how your app will look with the [Mantine theme](https://mantine.dev/) installed. You don't have to change any JSON schemas.
58
+
59
+ The [Mantine theme playground](https://react-declarative-mantine.github.io/)
60
+
61
+ ## Using with AI
62
+
63
+ ![screencast](./assets/images/claude.gif)
64
+
65
+ There is a guide to make GPT-4 generate form schemas automatically. Check [the docs folder for guide](./docs/Readme.md#using-with-ai)
66
+
67
+ <img src="./assets/icons/decart.svg" height="35px" align="right">
68
+
69
+ ## Short review
70
+
71
+ > [!TIP]
72
+ > A few adjectives which can be applied to `react-declarative`
73
+
74
+ 1. **Accesible**
75
+
76
+ Every callback you need: field or group of fields focus/blur event, form invalidity state, [data-testid](https://medium.com/@automationTest/why-your-development-team-should-use-data-testid-attributes-a83f1ca27ebb) and more
77
+
78
+ 2. **Configurable**
79
+
80
+ Each field can be statically [hidden by settings dictionary](https://github.com/react-declarative/react-pocketbase-crm?tab=readme-ov-file#feature-model-and-dynamic-field-visibility) and dynamically by form state condition. Same if you want to disable or make field readonly
81
+
82
+ 3. **Extendable**
83
+
84
+ It allow you to override any field type by slot context or [inject custom JSX](./demo/src/pages/GalleryPage.tsx#L206) directly into form without additional boilerplate. Also that lib can be used with all React ecosystem, for example, try with [Million.js](https://dev.to/tobysolutions/million-30-all-you-need-to-know-3d2), It makes `react-declarative` extreamly performant even on 2016 devices
85
+
86
+ 4. **Maintainable**
87
+
88
+ Write code without going into [technical debt](https://en.wikipedia.org/wiki/Technical_debt). The big diffrence with [jsonforms](https://jsonforms.io/docs/#how-does-it-work) is you actually write less code cause you don't need `data schema`. In `react-declarative` all validations are build into `ui schema`, so backend endpoint can be changed partially if some properties are unused (see [PATCH method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH))
89
+
90
+ 5. **Reflectable**
91
+
92
+ Each form schema can be [reflected](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.typeinfo?view=net-8.0) by using `getAvailableFields` for additional inline validations, data cleanup if some fields are not required anymore, data export (generate [excel export](https://www.npmjs.com/package/xlsx) from current form). That extreamly hard to implement with `jsonforms`
93
+
94
+ 6. **Reliable**
95
+
96
+ React 18 [Concurrent render](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react) used under the hood (state updates in async *useEffect*) so the `jsonforms` will slow down on 200+ fields form, `react-declarative` will not. Also [RPS](https://en.wikipedia.org/wiki/Web_server#requests_per_second) optimised by [debounce of form state change event](https://rxjs.dev/api/operators/debounce). That means you will need less hardware measures on a server side to implement autosubmit
97
+
98
+ 7. **Code-Splittable**
99
+
100
+ JSON templates can be downloaded statically, builded dynamically, [lazy-loaded dynamically](https://webpack.js.org/concepts/module-federation/)
101
+
102
+ 8. **Scalable**
103
+
104
+ Easy internationalization with translation dictionaries for Combobox field. [JSX Factory for labels translation](https://github.com/react-declarative/react-i18n-jsx-factory). An organisation for tutoring newbies [with 25 projects with AI](https://github.com/react-declarative/brainjs-cryptocurrency-trend), reactive programming and more
105
+
106
+ 9. **Integrable**
107
+
108
+ <!--Consolidates multiple aspects of the development process into one cohesive tool, providing a streamlined and efficient workflow for developers. It unifies state management and ui building into a single approach, managing project code modules dependency resolution, asset sharing, and software design, as a result eliminates the heterogeneous made by usage of separate tools.-->
109
+ It combines various development tasks into one tool, making the process simpler and faster. It manages app state, user interface building, code dependencies, asset sharing, and software design all in one place.
110
+
111
+ 10. **Customisable**
112
+
113
+ A game-changer for your app look is the `react-declarative-mantine`. That library provides `<OneSlotFactory />` with all fields redesigned. Check out [how your app can look like](https://react-declarative-mantine.github.io/) without changing any JSON schemas.
114
+
115
+ <img src="./assets/icons/masonry.svg" height="35px" align="right">
116
+
117
+ ## Quick start
118
+
119
+ > [!IMPORTANT]
120
+ > There is a `create-react-app` template available [in this repository](https://github.com/react-declarative/cra-template-react-declarative)
121
+
122
+ ```bash
123
+ yarn create react-app --template cra-template-react-declarative .
124
+ ```
125
+
126
+ or
127
+
128
+ ```bash
129
+ npx create-react-app . --template=react-declarative
130
+ ```
131
+
132
+ <img src="./assets/icons/babylon.svg" height="40px" align="right">
133
+
134
+ ## Installation
135
+
136
+ > [!NOTE]
137
+ > There is a sample app avalible in the [demo](./demo/src/index.tsx) folder...
138
+
139
+ ```bash
140
+ npm install --save react-declarative tss-react @mui/material @emotion/react @emotion/styled
141
+ ```
142
+
143
+ <img src="./assets/icons/watch.svg" height="40px" align="right">
144
+
145
+ ## Migrate
146
+
147
+ > [!NOTE]
148
+ > A lightweight version with `<One />` component and dependencies only published in [react-declarative-lite](https://github.com/react-declarative/react-declarative-lite) npm package. Useful when you want to use `<One />` forms in existing app
149
+
150
+ ```bash
151
+ npm install --save react-declarative-lite
152
+ ```
153
+
154
+ <img src="./assets/icons/world.svg" height="35px" align="right">
155
+
156
+ ## Demos
157
+
158
+ > [!NOTE]
159
+ > The `react-declarative` is not just a form builder. This one is the huge framework with dashboard adaptive cards builder, crud-based Grid component and more.<br> Check [the docs folder](./docs/Readme.md)
160
+
161
+ This tool also provide it's own way of rapid application development by simplifying app state managament. New features appear frequently, so you should be able to [read the project's storybook](https://github.com/react-declarative/react-declarative-storybook), browse [an organization with sample projects](https://github.com/react-declarative), and [read the source code](https://github.com/react-declarative/react-declarative)
162
+
163
+ Several starter kits available (Check [Playwright End-to-End testbed](https://github.com/react-declarative/react-declarative-e2e))
164
+
165
+ **1. Pure React Starter**
166
+
167
+ > [!NOTE]
168
+ > GitHub repo: [https://github.com/react-declarative/cra-template-react-declarative](https://github.com/react-declarative/cra-template-react-declarative)
169
+
170
+ ```bash
171
+ yarn create react-app --template cra-template-react-declarative .
172
+ ```
173
+
174
+ **2. Ethers.js/React Starter**
175
+
176
+ > [!NOTE]
177
+ > GitHub repo: [https://github.com/react-declarative/cra-template-solidity](https://github.com/react-declarative/cra-template-solidity)
178
+
179
+ ```bash
180
+ yarn create react-app --template cra-template-solidity .
181
+ ```
182
+
183
+ **3. AppWrite/React Starter**
184
+
185
+ > [!NOTE]
186
+ > GitHub repo: [https://github.com/react-declarative/cra-template-appwrite](https://github.com/react-declarative/cra-template-appwrite)
187
+
188
+ ```bash
189
+ yarn create react-app --template cra-template-appwrite .
190
+ ```
191
+
192
+ and few more quite interesting demo projects
193
+
194
+ **1. Playwright End-to-End Testbed**
195
+
196
+ > [!NOTE]
197
+ > GitHub repo: [https://github.com/react-declarative/react-declarative-e2e](https://github.com/react-declarative/react-declarative-e2e)
198
+
199
+ ```bash
200
+ git clone https://github.com/react-declarative/react-declarative-e2e.git
201
+ ```
202
+
203
+ **2. ERC-20 Payment gateway**
204
+
205
+ > [!NOTE]
206
+ > GitHub repo: [https://github.com/react-declarative/erc20-payment-gateway](https://github.com/react-declarative/erc20-payment-gateway)
207
+
208
+ ```bash
209
+ git clone https://github.com/react-declarative/erc20-payment-gateway.git
210
+ ```
211
+
212
+ **3. React Face KYC**
213
+
214
+ > [!NOTE]
215
+ > GitHub repo: [https://github.com/react-declarative/react-face-kyc](https://github.com/react-declarative/react-face-kyc)
216
+
217
+ ```bash
218
+ git clone https://github.com/react-declarative/react-face-kyc.git
219
+ ```
220
+
221
+ **4. BrainJS Cryptocurrency Trend**
222
+
223
+ > [!NOTE]
224
+ > GitHub repo: [https://github.com/react-declarative/brainjs-cryptocurrency-trend](https://github.com/react-declarative/brainjs-cryptocurrency-trend)
225
+
226
+ ```bash
227
+ git clone https://github.com/react-declarative/brainjs-cryptocurrency-trend.git
228
+ ```
229
+
230
+ **5. NFT Mint Tool**
231
+
232
+ > [!NOTE]
233
+ > GitHub repo: [https://github.com/react-declarative/nft-mint-tool](https://github.com/react-declarative/nft-mint-tool)
234
+
235
+ ```bash
236
+ git clone https://github.com/react-declarative/nft-mint-tool.git
237
+ ```
238
+
239
+ **6. React PocketBase CRM**
240
+
241
+ > [!NOTE]
242
+ > GitHub repo: [https://github.com/react-declarative/react-pocketbase-crm](https://github.com/react-declarative/react-pocketbase-crm)
243
+
244
+ ```bash
245
+ git clone https://github.com/react-declarative/react-pocketbase-crm.git
246
+ ```
247
+
248
+ **7. ChatGPT Ecommerce Grid**
249
+
250
+ > [!NOTE]
251
+ > GitHub repo: [https://github.com/react-declarative/chatgpt-ecommerce-prompt](https://github.com/react-declarative/chatgpt-ecommerce-prompt)
252
+
253
+ ```bash
254
+ git clone https://github.com/react-declarative/chatgpt-ecommerce-prompt.git
255
+ ```
256
+
257
+ **8. React Native lightweight version of this library**
258
+
259
+ > [!NOTE]
260
+ > GitHub repo: [https://github.com/react-declarative/rn-declarative](https://github.com/react-declarative/rn-declarative)
261
+
262
+ ```bash
263
+ git clone https://github.com/react-declarative/rn-declarative.git
264
+ ```
265
+
266
+ <img src="./assets/icons/fallen.svg" height="40px" align="right">
267
+
268
+ ## Declarative Scaffold component
269
+
270
+ > [!NOTE]
271
+ > Link to the [source code](./demo/src/App.Scaffold2.tsx)
272
+
273
+ The `<Scaffold2 />` implements the basic Material Design visual layout structure by using config instead of manual ui elements composition.
274
+
275
+ ![scaffold2](./assets/scaffold2.gif)
276
+
277
+ ```tsx
278
+ const options: IScaffold2Group[] = [
279
+ {
280
+ id: 'build',
281
+ label: 'Build',
282
+ children: [
283
+ {
284
+ id: 'authentication',
285
+ label: 'Authentication',
286
+ isVisible: async () => await ioc.authService.hasRole('unauthorized'),
287
+ icon: PeopleIcon,
288
+ tabs: [
289
+ { id: 'tab1', label: 'Tab1 in header', },
290
+ { id: 'tab2', label: 'Tab2 in header', },
291
+ ],
292
+ options: [
293
+ { id: 'tab1', label: 'Tab1 in side menu' },
294
+ { id: 'tab2', label: 'Tab2 in side menu' },
295
+ ],
296
+ },
297
+ { id: 'Database', label: 'Label is optional (can be generated automatically from ID in snake case)', icon: DnsRoundedIcon, },
298
+ { id: 'Storage', isDisabled: async () => await myAmazingGuard(), icon: PermMediaOutlinedIcon, },
299
+ { id: 'Hosting', icon: PublicIcon, },
300
+
301
+ ...
302
+
303
+ ```
304
+
305
+ <img src="./assets/icons/box.svg" height="40px" align="right">
306
+
307
+ ## Declarative KanbanView component
308
+
309
+ The `<KanbanView />` allow you to build [kanban](https://en.wikipedia.org/wiki/Kanban_(development)) boards with realtime support
310
+
311
+ ![kanbanview](./assets/kanbanview.gif)
312
+
313
+ ```tsx
314
+
315
+ const rows: IBoardRow<ILeadRow>[] = [
316
+ {
317
+ label: "Display name",
318
+ value: (id, employee) =>
319
+ [employee.first_name, employee.last_name].join(" "),
320
+ },
321
+ {
322
+ label: "Email",
323
+ value: (id, employee) => employee.email,
324
+ click: (id, data, payload) => payload.pickEmployeePreviewModal(id),
325
+ },
326
+ {
327
+ label: "Phone",
328
+ value: (id, employee) => employee.phone,
329
+ },
330
+ {
331
+ label: "Hire date",
332
+ value: (id, employee) => employee.hire_date,
333
+ },
334
+ ];
335
+
336
+ const columns: IBoardColumn<ILeadRow>[] = [
337
+ {
338
+ color: "#00ACC1",
339
+ column: "cold",
340
+ label: "Cold",
341
+ rows,
342
+ },
343
+ {
344
+ color: "#9C27B0",
345
+ column: "contact",
346
+ label: "Contact",
347
+ rows,
348
+ },
349
+ {
350
+ color: "#FFA000",
351
+ column: "draft",
352
+ label: "Draft",
353
+ rows,
354
+ },
355
+ {
356
+ color: "#2E7D32",
357
+ column: "deal",
358
+ label: "In a deal",
359
+ rows,
360
+ },
361
+ ];
362
+
363
+ ...
364
+
365
+ <KanbanView<ILeadRow>
366
+ sx={{
367
+ height: "calc(100vh - 145px)",
368
+ }}
369
+ onChangeColumn={handleChangeColumn}
370
+ columns={columns}
371
+ items={data}
372
+ />
373
+ ```
374
+
375
+ <img src="./assets/icons/x.svg" height="55px" align="right">
376
+
377
+ ## Declarative WizardView component
378
+
379
+ The `<WizardView />` component allows you to build [action wizard](https://en.wikipedia.org/wiki/Wizard_(software)) with [stepper](https://mui.com/material-ui/react-stepper/) and nested routing
380
+
381
+ ![wizardview](./assets/wizardview.gif)
382
+
383
+ ```tsx
384
+
385
+ const steps: IWizardStep[] = [
386
+ {
387
+ id: "select",
388
+ label: "Choose file",
389
+ },
390
+ {
391
+ id: "validate",
392
+ label: "Validation",
393
+ },
394
+ {
395
+ id: "import",
396
+ label: "Import",
397
+ },
398
+ ];
399
+
400
+ const routes: IWizardOutlet[] = [
401
+ {
402
+ id: "select",
403
+ element: SelectFileView,
404
+ isActive: (pathname) => !!parseRouteUrl("/select-file", pathname),
405
+ },
406
+ {
407
+ id: "validate",
408
+ element: ValidateFileView,
409
+ isActive: (pathname) => !!parseRouteUrl("/validate-file", pathname),
410
+ },
411
+ {
412
+ id: "import",
413
+ element: ImportFileView,
414
+ isActive: (pathname) => !!parseRouteUrl("/import-file", pathname),
415
+ },
416
+ ];
417
+
418
+ ...
419
+
420
+ <WizardView pathname="/select-file" steps={steps} routes={routes} />
421
+
422
+ ...
423
+
424
+ const SelectFileView = ({
425
+ history
426
+ }: IWizardOutletProps) => {
427
+ return (
428
+ <WizardContainer
429
+ Navigation={
430
+ <WizardNavigation
431
+ hasNext
432
+ onNext={() => history.replace("/validate-file")}
433
+ />
434
+ }
435
+ >
436
+ <p>123</p>
437
+ </WizardContainer>
438
+ );
439
+ };
440
+
441
+ ```
442
+
443
+ <img src="./assets/icons/magnum-opus.svg" height="30px" align="right">
444
+
445
+ ## VisibilityView and FeatureView components
446
+
447
+ The `<VisibilityView />` and `<FeatureView />` components allows you to build configurable UI by using [reflection](https://en.wikipedia.org/wiki/Reflective_programming)
448
+
449
+ ![visibility](./assets/visibility.gif)
450
+
451
+ ```tsx
452
+ const groups: IVisibilityGroup[] = [
453
+ {
454
+ name: "employee_visibility",
455
+ /**
456
+ * @type {IField[] | TypedField[]}
457
+ * @description Same field type from `<One />` template engine
458
+ */
459
+ fields: employee_fields,
460
+ },
461
+ ];
462
+
463
+ ...
464
+
465
+ <VisibilityView
466
+ expandAll
467
+ data={{ employee_visibility: data }}
468
+ groups={groups}
469
+ onChange={({ employee_visibility }) => onChange(employee_visibility)}
470
+ />
471
+ ```
472
+
473
+ By using [feature-oriented programming](https://en.wikipedia.org/wiki/Feature-oriented_domain_analysis) you can adjust view to different roles of users by partially hiding text, images and buttons
474
+
475
+ ```tsx
476
+ const features: IFeatureGroup[] = [
477
+ {
478
+ title: "Employee",
479
+ expanded: true,
480
+ children: [
481
+ {
482
+ name: "employee_preview_modal",
483
+ label: "Employee preview modal",
484
+ description: "Click on row open preview modal",
485
+ },
486
+ {
487
+ name: "employee_toggle_inactive",
488
+ label: "Employee toggle inactive",
489
+ description: "Can toggle employee activity",
490
+ },
491
+ ],
492
+ },
493
+ ];
494
+
495
+ ...
496
+
497
+ <FeatureView
498
+ expandAll
499
+ data={data}
500
+ features={features}
501
+ onChange={onChange}
502
+ />
503
+
504
+ ...
505
+
506
+ <If
507
+ payload={userId}
508
+ condition={async (userId) => {
509
+ return await ioc.permissionRequestService.getOwnerContactVisibilityByUserId(userId)
510
+ }}
511
+ Loading="Loading"
512
+ Else="Hidden"
513
+ >
514
+ {owner_contact}
515
+ </If>
516
+
517
+ ```
518
+
519
+
520
+ <img src="./assets/icons/solomon.svg" height="55px" align="right">
521
+
522
+ ## JSON-templated view engine
523
+
524
+ **1. Layout grid**
525
+
526
+ > [!NOTE]
527
+ > Link to the [source code](./demo/src/pages/LayoutPage.tsx)
528
+
529
+ ![layout-grid](./assets/layout.gif)
530
+
531
+ ```tsx
532
+ const fields: TypedField[] = [
533
+ {
534
+ type: FieldType.Line,
535
+ title: 'User info',
536
+ },
537
+ {
538
+ type: FieldType.Group,
539
+ phoneColumns: '12',
540
+ tabletColumns: '6',
541
+ desktopColumns: '4',
542
+ fields: [
543
+ {
544
+ type: FieldType.Text,
545
+ title: 'First name',
546
+ defaultValue: 'Petr',
547
+ description: 'Your first name',
548
+ leadingIcon: Face,
549
+ focus() { console.log("focus :-)"); },
550
+ blur() { console.log("blur :-("); },
551
+ name: 'firstName',
552
+ },
553
+ {
554
+ type: FieldType.Text,
555
+ title: 'Last name',
556
+ defaultValue: 'Tripolsky',
557
+ description: 'Your last name',
558
+ name: 'lastName',
559
+ },
560
+
561
+ ...
562
+
563
+ ];
564
+ ```
565
+
566
+ **2. Form validation**
567
+
568
+ > [!NOTE]
569
+ > Link to the [source code](./demo/src/pages/ValidationPage.tsx)
570
+
571
+ ![form-validation](./assets/validation.gif)
572
+
573
+ ```tsx
574
+ const fields: TypedField[] = [
575
+ {
576
+ type: FieldType.Text,
577
+ name: 'email',
578
+ trailingIcon: Email,
579
+ defaultValue: 'tripolskypetr@gmail.com',
580
+ isInvalid({email}) {
581
+ const expr = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g;
582
+ if (!expr.test(email)) {
583
+ return 'Invalid email address';
584
+ } else {
585
+ return null;
586
+ }
587
+ },
588
+ isDisabled({disabled}) {
589
+ return disabled;
590
+ },
591
+ isVisible({visible}) {
592
+ return visible;
593
+ }
594
+ },
595
+ {
596
+ type: FieldType.Expansion,
597
+ title: 'Settings',
598
+ description: 'Hide or disable',
599
+ fields: [
600
+ {
601
+ type: FieldType.Switch,
602
+ title: 'Mark as visible',
603
+ name: 'visible',
604
+ defaultValue: true,
605
+ },
606
+
607
+ ...
608
+
609
+ ```
610
+
611
+ **3. Gallery of controls**
612
+
613
+ > [!NOTE]
614
+ > Link to the [source code](./demo/src/pages/GalleryPage.tsx)
615
+
616
+ ![gallery](./assets/gallery.gif)
617
+
618
+ ```tsx
619
+ const fields: TypedField[] = [
620
+ {
621
+ type: FieldType.Paper,
622
+ fields: [
623
+ {
624
+ type: FieldType.Line,
625
+ title: 'Checkboxes',
626
+ },
627
+ {
628
+ type: FieldType.Checkbox,
629
+ name: 'checkbox1',
630
+ columns: '3',
631
+ title: 'Checkbox 1',
632
+ },
633
+ {
634
+ type: FieldType.Checkbox,
635
+ name: 'checkbox2',
636
+ columns: '3',
637
+ title: 'Checkbox 2',
638
+ },
639
+
640
+ ...
641
+
642
+ ```
643
+
644
+ **4. JSX Injection**
645
+
646
+ > [!NOTE]
647
+ > Link to the [source code](./demo/src/pages/GalleryPage.tsx)
648
+
649
+ ```tsx
650
+ const fields: TypedField[] = [
651
+ {
652
+ type: FieldType.Paper,
653
+ fields: [
654
+ {
655
+ type: FieldType.Component,
656
+ element: (props) => <Logger {...props}/>,
657
+ },
658
+ ],
659
+ },
660
+
661
+ ...
662
+
663
+ ];
664
+ ```
665
+
666
+ **5. UI-Kit override**
667
+
668
+ > [!NOTE]
669
+ > Link to the [source code](./src/components/One/components/SlotFactory)
670
+
671
+ ```tsx
672
+ <OneSlotFactory
673
+ CheckBox={MyCheckBox}
674
+ Text={MyInput}
675
+ ...
676
+ >
677
+ <One
678
+ ...
679
+ />
680
+ ...
681
+ </OneSlotFactory>
682
+ ```
683
+
684
+ **6. Hiding fields by business functions**
685
+
686
+ > [!NOTE]
687
+ > See [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)
688
+
689
+ ```tsx
690
+ const fields: TypedField[] = [
691
+ {
692
+ type: FieldType.Text,
693
+ name: 'phone',
694
+ hidden: ({ payload }) => {
695
+ return !payload.features.has('show-phone-number');
696
+ },
697
+ },
698
+
699
+ ...
700
+
701
+ ];
702
+ ```
703
+
704
+ <img src="./assets/icons/saturn.svg" height="35px" align="right">
705
+
706
+ ## JSON-templated grid engine
707
+
708
+ > [!NOTE]
709
+ > Link to the [source code](./demo/src/pages/ListPage.tsx)
710
+
711
+ Adaptive json-configurable data grid with build-in mobile device support
712
+
713
+ ![list](./assets/images/list.png)
714
+
715
+ ```tsx
716
+
717
+ const filters: TypedField[] = [
718
+ {
719
+ type: FieldType.Text,
720
+ name: 'firstName',
721
+ title: 'First name',
722
+ },
723
+ {
724
+ type: FieldType.Text,
725
+ name: 'lastName',
726
+ title: 'Last name',
727
+ }
728
+ ];
729
+
730
+ const columns: IColumn[] = [
731
+ {
732
+ type: ColumnType.Text,
733
+ field: 'id',
734
+ headerName: 'ID',
735
+ width: (fullWidth) => Math.max(fullWidth - 650, 200),
736
+ columnMenu: [
737
+ {
738
+ action: 'test-action',
739
+ label: 'Column action',
740
+ },
741
+ ],
742
+ },
743
+ ...
744
+ ];
745
+
746
+ const actions: IListAction[] = [
747
+ {
748
+ type: ActionType.Add,
749
+ label: 'Create item'
750
+ },
751
+ ...
752
+ ];
753
+
754
+ const operations: IListOperation[] = [
755
+ {
756
+ action: 'operation-one',
757
+ label: 'Operation one',
758
+ },
759
+ ];
760
+
761
+ const chips: IListChip[] = [
762
+ {
763
+ label: 'The chip1_enabled is true',
764
+ name: 'chip1_enabled',
765
+ color: '#4caf50',
766
+ },
767
+ ...
768
+ ];
769
+
770
+ const rowActions: IListRowAction[] = [
771
+ {
772
+ label: 'chip1',
773
+ action: 'chip1-action',
774
+ isVisible: ({ chip1_enabled }) => chip1_enabled,
775
+ },
776
+ ...
777
+ ];
778
+
779
+ ...
780
+
781
+ return (
782
+ <ListTyped
783
+ withMobile
784
+ withSearch
785
+ withArrowPagination
786
+ rowActions={rowActions}
787
+ actions={actions}
788
+ filters={filters}
789
+ columns={columns}
790
+ operations={operations}
791
+ chips={chips}
792
+ />
793
+ )
794
+
795
+ ```
796
+
797
+ <img src="./assets/icons/square_compasses.svg" height="35px" align="right">
798
+
799
+ ## DOM Frames with infinite scroll and `transparent-api virtualization`
800
+
801
+ > [!NOTE]
802
+ > You can use [InfiniteView](./src/components/InfiniteView/InfiniteView.tsx) for always-mounted or [VirtualView](./src/components/VirtualView/VirtualView.tsx) for virtualized infinite lists
803
+
804
+ ![virtualization](./assets/virtualization.gif)
805
+
806
+ ```tsx
807
+ <VirtualView
808
+ component={Paper}
809
+ sx={{
810
+ width: "100%",
811
+ height: 250,
812
+ mb: 1,
813
+ }}
814
+ onDataRequest={() => {
815
+ console.log('data-request');
816
+ setItems((items) => [
817
+ ...items,
818
+ ...[uuid(), uuid(), uuid(), uuid(), uuid()],
819
+ ]);
820
+ }}
821
+ >
822
+ {items.map((item) => (
823
+ <span key={item}>{item}</span>
824
+ ))}
825
+ </VirtualView>
826
+ ```
827
+
828
+ <img src="./assets/icons/whenthesummerdies.svg" height="35px" align="right">
829
+
830
+ ## Async hooks and Action components
831
+
832
+ > [!NOTE]
833
+ > Hooks for fetching, caching and updating asynchronous data. Promise-based [command pattern](https://docs.devexpress.com/WPF/17353/mvvm-framework/commands/delegate-commands) ui components. The hooks will help you to avoid [multiple POST method execution](https://en.wikipedia.org/wiki/REST) when user missclick button. The components will show load indicator while `onClick` promise is pending
834
+
835
+ The `useAsyncProgress` will manage percent range of execution (`0% - 100%` for `<LinearProgress />` value)
836
+
837
+ ```tsx
838
+ const { execute } = useAsyncProgress(
839
+ async ({ data }) => {
840
+ await createContact(data);
841
+ },
842
+ {
843
+ onProgress: (percent) => {
844
+ setProgress(percent);
845
+ },
846
+ onError: (errors) => {
847
+ setErrors(errors);
848
+ },
849
+ onEnd: (isOk) => {
850
+ history.replace("/report");
851
+ },
852
+ }
853
+ );
854
+
855
+ ...
856
+
857
+ <ActionButton
858
+ onClick={async () => {
859
+ const file = await chooseFile(
860
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
861
+ );
862
+ if (file) {
863
+ const rows = await parseExcelContacts(file);
864
+ execute(
865
+ rows.map((row, idx) => ({
866
+ data: row,
867
+ label: `Row №${idx + 2}`,
868
+ }))
869
+ );
870
+ }
871
+ }}
872
+ >
873
+ Choose XLSX
874
+ </ActionButton>
875
+ ```
876
+
877
+ The `useSinglerunAction` will not execute any additional calls while original promise is pending
878
+
879
+ ```tsx
880
+ const { execute } = useSinglerunAction(
881
+ async () => {
882
+ const file = await chooseFile("image/jpeg, image/png");
883
+ if (file) {
884
+ const filePath = await ioc.appwriteService.uploadFile(file);
885
+ onChange(filePath);
886
+ }
887
+ }
888
+ );
889
+
890
+ ...
891
+
892
+ <ActionIcon onClick={execute}>
893
+ <CloudUploadIcon />
894
+ </ActionIcon>
895
+ ```
896
+
897
+ The `useQueuedAction` will queue all promise fulfillment in functions execution order. Quite useful while [state reducer pattern](https://en.wikipedia.org/wiki/Redux_(JavaScript_library)) when coding [realtime](https://en.wikipedia.org/wiki/WebSocket).
898
+
899
+ ```tsx
900
+ const { execute } = useQueuedAction(
901
+ async ({ type, payload }) => {
902
+ if (type === "create-action") {
903
+ ...
904
+ }
905
+ if (type === "update-action") {
906
+
907
+ }
908
+ if (type === "remove-action") {
909
+ ...
910
+ }
911
+ },
912
+ {
913
+ onLoadStart: () => ioc.layoutService.setAppbarLoader(true),
914
+ onLoadEnd: () => ioc.layoutService.setAppbarLoader(false),
915
+ }
916
+ );
917
+
918
+ ...
919
+
920
+ useEffect(() => ioc.kanbanService.createSubject.subscribe(execute), []);
921
+
922
+ useEffect(() => ioc.kanbanService.updateSubject.subscribe(execute), []);
923
+
924
+ useEffect(() => ioc.kanbanService.removeSubject.subscribe(execute), []);
925
+
926
+ ```
927
+
928
+ The `usePreventAction` will prevent any other action execution [while single one is pending](https://en.wikipedia.org/wiki/Semaphore_(programming))
929
+
930
+ ```tsx
931
+ const {
932
+ handleLoadStart,
933
+ handleLoadEnd,
934
+ loading,
935
+ } = usePreventAction();
936
+
937
+ ...
938
+
939
+ <ActionButton
940
+ disabled={loading}
941
+ onLoadStart={handleLoadStart}
942
+ onLoadEnd={handleLoadEnd}
943
+ >
944
+ Action 1
945
+ </ActionButton>
946
+
947
+ ...
948
+
949
+ <ActionButton
950
+ disabled={loading}
951
+ onLoadStart={handleLoadStart}
952
+ onLoadEnd={handleLoadEnd}
953
+ >
954
+ Action 2
955
+ </ActionButton>
956
+ ```
957
+
958
+ The `usePreventNavigate` will [prevent navigate](https://medium.com/@goldhand/routing-design-patterns-fed766ad35fa) while action is running
959
+
960
+ ```tsx
961
+ const { handleLoadStart, handleLoadEnd } = usePreventNavigate({
962
+ history: ioc.routerService,
963
+ });
964
+
965
+ ...
966
+
967
+ <ActionButton
968
+ onLoadStart={handleLoadStart}
969
+ onLoadEnd={handleLoadEnd}
970
+ >
971
+ Action
972
+ </ActionButton>
973
+ ```
974
+
975
+ The `useAsyncValue` will help you to manage [react-hooks/rules-of-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) while working with remote data
976
+
977
+ ```tsx
978
+ const [data, { loading, error }, setData] = useAsyncValue(async () => {
979
+ return await getData();
980
+ });
981
+
982
+ if (loading || error) {
983
+ return null;
984
+ }
985
+
986
+ return (
987
+ <pre>
988
+ {JSON.stringify(data, null, 2)}
989
+ </pre>
990
+ )
991
+
992
+ ...
993
+
994
+ const handleChangeData = async () => {
995
+ const newData = await fetchApi('/api/v1/data', {
996
+ method: "POST",
997
+ ...
998
+ })
999
+ setData(newData)
1000
+ }
1001
+
1002
+ ```
1003
+
1004
+ <img src="./assets/icons/stone.svg" height="35px" align="right">
1005
+
1006
+ ## Async pipe port
1007
+
1008
+ > [!NOTE]
1009
+ > See [angular2 docs](https://angular.io/api/common/AsyncPipe)
1010
+
1011
+ ```tsx
1012
+ import { Async } from 'react-declarative'
1013
+
1014
+ import { CircularProgress } from '@mui/material'
1015
+
1016
+ const PostItem = ({
1017
+ id,
1018
+ }) => {
1019
+
1020
+ const { getPostById } = useBlogApi();
1021
+
1022
+ return (
1023
+ <Async payload={id} Loader={CircularProgress}>
1024
+ {async (id) => {
1025
+ const { title, body } = await getPostById(id);
1026
+ return (
1027
+ <div>
1028
+ <p>{title}</p>
1029
+ <p>{body}</p>
1030
+ </div>
1031
+ );
1032
+ }}
1033
+ </Async>
1034
+ );
1035
+ };
1036
+ ```
1037
+
1038
+ <img src="./assets/icons/triangle.svg" height="35px" align="right">
1039
+
1040
+ ## Structural directive port
1041
+
1042
+ > [!NOTE]
1043
+ > See [angular2 docs](https://angular.io/guide/structural-directives)
1044
+
1045
+ ```tsx
1046
+ import { If } from 'react-declarative'
1047
+
1048
+ const ProfilePage = () => {
1049
+ const { hasRole } = useRoleApi();
1050
+ return (
1051
+ <If condition={() => hasRole("admin")}>
1052
+ <button>The admin's button</button>
1053
+ </If>
1054
+ );
1055
+ };
1056
+ ```
1057
+
1058
+ <img src="./assets/icons/monade.svg" height="35px" align="right">
1059
+
1060
+ ## Animated view transition
1061
+
1062
+ > [!NOTE]
1063
+ > Link to the [source code](./demo/src/pages/RevealPage.tsx)
1064
+
1065
+ ```tsx
1066
+ import { FetchView } from 'react-declarative'
1067
+
1068
+ const PostList = () => {
1069
+
1070
+ const { getPosts } = useBlogApi();
1071
+
1072
+ const state = [
1073
+ getPosts,
1074
+ ];
1075
+
1076
+ return (
1077
+ <FetchView state={state} animation="fadeIn">
1078
+ {(posts) => (
1079
+ <div>
1080
+ {posts.map((post, idx) => (
1081
+ <p key={idx}>
1082
+ <b>{post.title}</b>
1083
+ {post.body}
1084
+ </p>
1085
+ ))}
1086
+ </div>
1087
+ )}
1088
+ </FetchView>
1089
+ );
1090
+ };
1091
+ ```
1092
+
1093
+ <img src="./assets/icons/positron.svg" height="35px" align="right">
1094
+
1095
+ ## Build-in router
1096
+
1097
+ > [!NOTE]
1098
+ > Link to the [source code](./demo/src/App.tsx)
1099
+
1100
+ ```tsx
1101
+ import { Switch } from 'react-declarative';
1102
+
1103
+ ...
1104
+
1105
+ const routes = [
1106
+ {
1107
+ path: '/mint-page',
1108
+ guard: async () => await ioc.roleService.has('whitelist'),
1109
+ prefetch: async () => await ioc.ethersService.init(),
1110
+ unload: async () => await ioc.ethersService.dispose(),
1111
+ redirect: () => {
1112
+ let isOk = true;
1113
+ isOk = isOk && ioc.ethersService.isMetamaskAvailable;
1114
+ isOk = isOk && ioc.ethersService.isProviderConnected;
1115
+ isOk = isOk && ioc.ethersService.isAccountEnabled;
1116
+ if (isOk) {
1117
+ return "/connect-page";
1118
+ }
1119
+ return null;
1120
+ },
1121
+ },
1122
+ ];
1123
+
1124
+ ...
1125
+
1126
+ const App = () => (
1127
+ <Switch history={history} items={routes} />
1128
+ );
1129
+ ```
1130
+
1131
+ <img src="./assets/icons/assignation.svg" height="35px" align="right">
1132
+
1133
+ ## MapReduce Data Pipelines
1134
+
1135
+ > [!NOTE]
1136
+ > Link to the [source code](https://github.com/react-declarative/react-face-kyc/blob/master/src/hooks/useFaceWasm/emitters.ts)
1137
+
1138
+ ```tsx
1139
+ import { Source } from 'react-declarative';
1140
+
1141
+ ...
1142
+
1143
+ const verifyCompleteEmitter = Source.multicast(() =>
1144
+ Source
1145
+ .join([
1146
+ captureStateEmitter,
1147
+ Source.fromInterval(1_000),
1148
+ ])
1149
+ .reduce((acm, [{ state: isValid }]) => {
1150
+ if (isValid) {
1151
+ return acm + 1;
1152
+ }
1153
+ return 0;
1154
+ }, 0)
1155
+ .tap((ticker) => {
1156
+ if (ticker === 1) {
1157
+ mediaRecorderInstance.beginCapture();
1158
+ }
1159
+ })
1160
+ .filter((ticker) => ticker === CC_SECONDS_TO_VERIFY)
1161
+ .tap(() => {
1162
+ mediaRecorderInstance.endCapture();
1163
+ })
1164
+ );
1165
+ ```
1166
+
1167
+ <img src="./assets/icons/chronos.svg" height="35px" align="right">
1168
+
1169
+ ## Ref-managed MVVM collection
1170
+
1171
+ > Link to the [source code](./demo/src/pages/MvvmPage.tsx)
1172
+
1173
+ ```tsx
1174
+ import { useCollection } from "react-declarative";
1175
+
1176
+ ...
1177
+
1178
+ const collection = useCollection({
1179
+ onChange: (collection, target) => console.log({
1180
+ collection,
1181
+ target,
1182
+ }),
1183
+ initialValue: [],
1184
+ });
1185
+
1186
+ const handleAdd = async () => {
1187
+ const { id, ...data } = await fetchApi("/api/v1/counters/create", {
1188
+ method: "POST",
1189
+ });
1190
+ collection.push({
1191
+ id,
1192
+ ...data,
1193
+ });
1194
+ };
1195
+
1196
+ const handleUpsert = async () => {
1197
+ const updatedItems = await fetchApi("/api/v1/counters/list");
1198
+ collection.upsert(updatedItems);
1199
+ };
1200
+
1201
+ return (
1202
+ <>
1203
+ <button onClick={handleAdd}>Add item</button>
1204
+ <button onClick={handleUpsert}>Upsert items</button>
1205
+ <ul>
1206
+ {collection.map((entity) => (
1207
+ <ListItem key={entity.id} entity={entity} />
1208
+ ))}
1209
+ </ul>
1210
+ </>
1211
+ );
1212
+ ```
1213
+
1214
+ <img src="./assets/icons/heraldry.svg" height="35px" align="right">
1215
+
1216
+ ## See also
1217
+
1218
+ ```tsx
1219
+ import { ConstraintView } from 'react-declarative';
1220
+ import { DragDropView } from 'react-declarative';
1221
+ import { ScrollView } from 'react-declarative';
1222
+ import { ScaleView } from 'react-declarative';
1223
+ import { FadeView } from 'react-declarative';
1224
+ import { TabsView } from 'react-declarative';
1225
+ import { WaitView } from 'react-declarative';
1226
+ import { PingView } from 'react-declarative';
1227
+ import { OfflineView } from 'react-declarative';
1228
+ import { RevealView } from 'react-declarative';
1229
+ import { SecretView } from 'react-declarative';
1230
+ import { PortalView } from 'react-declarative';
1231
+ import { RecordView } from 'react-declarative';
1232
+ import { CardView } from 'react-declarative';
1233
+ import { ErrorView } from 'react-declarative';
1234
+ import { AuthView } from 'react-declarative';
1235
+ import { InfiniteView } from 'react-declarative';
1236
+ import { VirtualView } from 'react-declarative';
1237
+ ```
1238
+
1239
+ <img src="./assets/icons/consciousness.svg" height="35px" align="right">
1240
+
1241
+ ## Patterns inside
1242
+
1243
+ 1. [MVVM](https://backbonejs.org/#Collection) - `useCollection`, `useModel`
1244
+ 2. [DI](https://angular.io/guide/dependency-injection) - `provide`, `inject`, `createServiceManager`
1245
+ 3. [Builder](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0) - `useListEditor`, `useMediaStreamBuilder`
1246
+ 4. [Adapter](https://en.wikipedia.org/wiki/Adapter_pattern) - `normalizeText`
1247
+ 5. [Observer](https://en.wikipedia.org/wiki/Observer_pattern) - `useChangeSubject`, `useSubject`, `useRenderWaiter`, `Subject`, `BehaviorSubject`, `EventEmitter`, `fromPromise`
1248
+ 6. [Command](https://en.wikipedia.org/wiki/Command_pattern) - `ActionTrigger`, `ActionFilter`, `ActionButton`, `ActionToggle`, `ActionMenu`, `ActionIcon`, `ActionModal`, `InfiniteView`, `VirtualView`, `useActionModal`
1249
+ 7. [Coroutine](https://en.wikipedia.org/wiki/Coroutine) - `FetchView`, `WaitView`, `PingView`, `Async`, `If`, `useAsyncAction`
1250
+ 8. [Routing](https://medium.com/@goldhand/routing-design-patterns-fed766ad35fa) - `Switch`, `OutletView`, `getRouteParams`, `getRouteItem`, `useRouteParams`, `useRouteItem`, `createRouteItemManager`, `createRouteParamsManager`
1251
+ 9. [Monad](https://en.wikipedia.org/wiki/Monad_(functional_programming)) - `singleshot`, `cancelable`, `queued`, `cached`, `debounce`, `compose`, `trycatch`, `memoize`, `ttl`, `lock`
1252
+ 10. [Composition](https://reactjs.org/docs/composition-vs-inheritance.html) - `VirtualView`, `InfiniteView`, `PortalView`, `RevealView`, `PingView`, `WaitView`, `FadeView`, `ScaleView`, `ScrollView`, `ModalManager`
1253
+ 11. [HoC](https://reactjs.org/docs/higher-order-components.html) - `ConstraintView`, `AutoSizer`, `FetchView`, `Async`, `If`
1254
+ 12. [Facade](https://en.wikipedia.org/wiki/Facade_pattern) - `Subject`, `Observer`
1255
+ 13. [Scheduled-task](https://en.wikipedia.org/wiki/Scheduled-task_pattern) - `Task`, `singlerun`
1256
+ 14. [RAD](https://en.wikipedia.org/wiki/Rapid_application_development) - `RecordView`, `CardView`
1257
+ 15. [Functional](https://en.wikipedia.org/wiki/Functional_programming) - `useActualValue`, `useActualCallback`, `useActualState`, `useSearchParams`, `useSearchState`, `useChange`
1258
+ 16. [Declarative](https://en.wikipedia.org/wiki/Declarative_programming) - `One`, `List`, `Scaffold`, `Scaffold2`, `RecordView`, `CardView`
1259
+ 17. [Reactive](https://en.wikipedia.org/wiki/ReactiveX) - `EventEmitter`, `Subject`, `BehaviorSubject`, `Observer`
1260
+ 18. [Lambda Architecture](https://en.wikipedia.org/wiki/Lambda_architecture) - `Source`, `Operator`, `useSource`, `useSubscription`
1261
+ 19. [Aspect Oriented](https://en.wikipedia.org/wiki/Aspect-oriented_programming) - `serviceManager`, `Source`
1262
+ 20. [Reflection](https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/reflection) - `getAvailableFields`, `VisibilityView`
1263
+ 21. [Pagination](https://medium.com/@oshiryaeva/offset-vs-cursor-based-pagination-which-is-the-right-choice-for-your-project-e46f65db062f) - `useOffsetPaginator`, `useCursorPaginator`
1264
+ 22. [Feature model](https://en.wikipedia.org/wiki/Feature_model) - `useFeatureView`, `useVisibilityView`, `FeatureView`, `VisibilityView`
1265
+ 23. [Software Fault Prevention](https://en.wikipedia.org/wiki/Software_fault_tolerance) - `ErrorBoundary`, `ErrorView`
1266
+
1267
+ <img src="./assets/icons/cosmos.svg" height="35px" align="right">
1268
+
1269
+ ## Philosophy notes
1270
+
1271
+ 1. [React: declarative vs imperative](./NOTES.md#react-declarative-vs-imperative)
1272
+
1273
+ Declarative programming is when a more qualified specialist writing code in a way when its behavior can be changed by using external config which represent oriented graph of objects
1274
+
1275
+ 2. [Fractal pattern](./NOTES.md#fractal-pattern-fractal-project-structure)
1276
+
1277
+ Fractal pattern conveys that similar patterns recur progressively and the same thought process is applied to the structuring of codebase i.e All units repeat themselves.
1278
+
1279
+ 3. [SOLID in react-declarative](./NOTES.md#solid-in-react-declarative)
1280
+
1281
+ SOLID principles described by simple words with examples in a source code
1282
+
1283
+ 4. [Product-oriented principles](./NOTES.md#product-oriented-principles)
1284
+
1285
+ These principles will help you to keep the code as clean as possible when you need to make a MVP immediately
1286
+
1287
+ 5. [Oriented graphs: reduce algoritm difficulty](./NOTES.md#reduce-algoritm-difficulty-when-working-with-oriented-graphs)
1288
+
1289
+ The real useful note for working with oriented graphs
1290
+
1291
+ 6. [Using underected data flow for building software product line](./NOTES.md#using-underected-data-flow-for-building-software-product-line)
1292
+
1293
+ Useful snippets to split procedure code with the inversion of control pattern
1294
+
1295
+ 7. [Frontend as a service bus for Service-Oriented Architecture (SOA)](./NOTES.md#frontend-as-a-service-bus-for-service-oriented-architecture-soa)
1296
+
1297
+ Serverless compution pros and no cons with react-declarative
1298
+
1299
+ 8. [Make SPA Java again](./NOTES.md#make-spa-java-again)
1300
+
1301
+ The problem of clean architecture in modern React apps
1302
+
1303
+ <img src="./assets/icons/tree.svg" height="65px" align="right">
1304
+
1305
+ ## License
1306
+
1307
+ > P.S. Got a question? Feel free to [write It in issues](https://github.com/react-declarative/react-declarative/issues), I need traffic
1308
+
1309
+ MIT [@tripolskypetr](https://github.com/tripolskypetr)
1310
+
1311
+ ## External References
1312
+
1313
+ 1. [Playground](https://react-declarative-playground.github.io/)
1314
+ 2. [Storybook](https://react-declarative.github.io/)
1315
+ 3. [Typedoc](https://react-declarative-typedoc.github.io/)
1316
+
1317
+ ## Thanks
1318
+
1319
+ Dude, you reached that point, omg. Could you
1320
+ [Star it on GitHub](https://github.com/react-declarative/react-declarative) plz)
1321
+