⚛️ react-declarative
MUI json endpoint form builder. Check this storybook for more samples...
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 beautiful on any device...
Quick start
There is a
create-react-app
template available in this repository
yarn create react-app --template cra-template-react-declarative .
or
npx create-react-app . --template=react-declarative
Installation
There is a sample app avalible in demo folder...
npm install --save react-declarative tss-react @mui/material @emotion/react @emotion/styled
Demos
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.
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, browse an organization with sample projects, and read the source code.
Also, several starter kits available
1. Pure React Starter
GitHub repo: https://github.com/react-declarative/cra-template-react-declarative
yarn create react-app --template cra-template-react-declarative .
2. Ethers.js/React Starter
GitHub repo: https://github.com/react-declarative/cra-template-solidity
yarn create react-app --template cra-template-solidity .
3. AppWrite/React Starter
GitHub repo: https://github.com/react-declarative/cra-template-appwrite
yarn create react-app --template cra-template-appwrite .
Declarative Scaffold component
Link to source code
The <Scaffold2 />
implements the basic Material Design visual layout structure by using config instead of manual ui elements composition.
const options: IScaffold2Group[] = [
{
id: 'build',
label: 'Build',
children: [
{
id: 'authentication',
label: 'Authentication',
isVisible: async () => await ioc.authService.hasRole('unauthorized'),
icon: PeopleIcon,
tabs: [
{ id: 'tab1', label: 'Tab1 in header', },
{ id: 'tab2', label: 'Tab2 in header', },
],
options: [
{ id: 'tab1', label: 'Tab1 in side menu' },
{ id: 'tab2', label: 'Tab2 in side menu' },
],
},
{ id: 'Database', label: 'Label is optional (can be generated automatically from ID in snake case)', icon: DnsRoundedIcon, },
{ id: 'Storage', isDisabled: async () => await myAmazingGuard(), icon: PermMediaOutlinedIcon, },
{ id: 'Hosting', icon: PublicIcon, },
...
JSON-templated view engine
1. Layout grid
Link to source code
const fields: TypedField[] = [
{
type: FieldType.Line,
title: 'User info',
},
{
type: FieldType.Group,
phoneColumns: '12',
tabletColumns: '6',
desktopColumns: '4',
fields: [
{
type: FieldType.Text,
title: 'First name',
defaultValue: 'Petr',
description: 'Your first name',
leadingIcon: Face,
focus() { console.log("focus :-)"); },
blur() { console.log("blur :-("); },
name: 'firstName',
},
{
type: FieldType.Text,
title: 'Last name',
defaultValue: 'Tripolsky',
description: 'Your last name',
name: 'lastName',
},
...
];
2. Form validation
Link to source code
const fields: TypedField[] = [
{
type: FieldType.Text,
name: 'email',
trailingIcon: Email,
defaultValue: 'tripolskypetr@gmail.com',
isInvalid({email}) {
const expr = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g;
if (!expr.test(email)) {
return 'Invalid email address';
} else {
return null;
}
},
isDisabled({disabled}) {
return disabled;
},
isVisible({visible}) {
return visible;
}
},
{
type: FieldType.Expansion,
title: 'Settings',
description: 'Hide or disable',
fields: [
{
type: FieldType.Switch,
title: 'Mark as visible',
name: 'visible',
defaultValue: true,
},
...
3. Gallery of controls
Link to source code
const fields: TypedField[] = [
{
type: FieldType.Paper,
fields: [
{
type: FieldType.Line,
title: 'Checkboxes',
},
{
type: FieldType.Checkbox,
name: 'checkbox1',
columns: '3',
title: 'Checkbox 1',
},
{
type: FieldType.Checkbox,
name: 'checkbox2',
columns: '3',
title: 'Checkbox 2',
},
...
4. JSX Injection
Link to source code
const fields: TypedField[] = [
{
type: FieldType.Paper,
fields: [
{
type: FieldType.Component,
element: (props) => <Logger {...(props || {})}/>,
},
],
},
...
];
JSON-templated grid engine
Link to source code
Adaptive json-configurable data grid with build-in mobile device support
const filters: TypedField[] = [
{
type: FieldType.Text,
name: 'firstName',
title: 'First name',
},
{
type: FieldType.Text,
name: 'lastName',
title: 'Last name',
}
];
const columns: IColumn[] = [
{
type: ColumnType.Text,
field: 'id',
headerName: 'ID',
width: (fullWidth) => Math.max(fullWidth - 650, 200),
columnMenu: [
{
action: 'test-action',
label: 'Column action',
},
],
},
...
];
const actions: IListAction[] = [
{
type: ActionType.Add,
label: 'Create item'
},
...
];
const operations: IListOperation[] = [
{
action: 'operation-one',
label: 'Operation one',
},
];
const chips: IListChip[] = [
{
label: 'The chip1_enabled is true',
name: 'chip1_enabled',
color: '#4caf50',
},
...
];
const rowActions: IListRowAction[] = [
{
label: 'chip1',
action: 'chip1-action',
isVisible: ({ chip1_enabled }) => chip1_enabled,
},
...
];
...
return (
<ListTyped
withMobile
withSearch
withArrowPagination
rowActions={rowActions}
actions={actions}
filters={filters}
columns={columns}
operations={operations}
chips={chips}
/>
)
transparent-api virtualization
DOM Frames with infinite scroll and You can use InfiniteView for always-mounted or VirtualView for virtualized infinite lists
<VirtualView
component={Paper}
sx={{
width: "100%",
height: 250,
mb: 1,
}}
onDataRequest={() => {
console.log('data-request');
setItems((items) => [
...items,
...[uuid(), uuid(), uuid(), uuid(), uuid()],
]);
}}
>
{items.map((item) => (
<span key={item}>{item}</span>
))}
</VirtualView>
Async pipe port
See angular2 docs
import { Async } from 'react-declarative'
import { CircularProgress } from '@mui/material'
const PostItem = ({
id,
}) => {
const { getPostById } = useBlogApi();
return (
<Async payload={id} Loader={CircularProgress}>
{async (id) => {
const { title, body } = await getPostById(id);
return (
<div>
<p>{title}</p>
<p>{body}</p>
</div>
);
}}
</Async>
);
};
Structural directive port
See angular2 docs
import { If } from 'react-declarative'
const ProfilePage = () => {
const { hasRole } = useRoleApi();
return (
<If condition={() => hasRole("admin")}>
<button>The admin's button</button>
</If>
);
};
Animated view transition
Link to source code
import { FetchView } from 'react-declarative'
const PostList = () => {
const { getPosts } = useBlogApi();
const state = [
getPosts,
];
return (
<FetchView state={state} animation="fadeIn">
{(posts) => (
<div>
{posts.map((post, idx) => (
<p key={idx}>
<b>{post.title}</b>
{post.body}
</p>
))}
</div>
)}
</FetchView>
);
};
Build-in router
Link to source code
import { Switch } from 'react-declarative';
...
const routes = [
{
path: '/mint-page',
guard: async () => await ioc.roleService.has('whitelist'),
prefetch: async () => await ioc.ethersService.init(),
unload: async () => await ioc.ethersService.dispose(),
redirect: () => {
let isOk = true;
isOk = isOk && ioc.ethersService.isMetamaskAvailable;
isOk = isOk && ioc.ethersService.isProviderConnected;
isOk = isOk && ioc.ethersService.isAccountEnabled;
if (isOk) {
return "/connect-page";
}
return null;
},
},
];
...
const App = () => (
<Switch history={history} items={routes} />
);
See also
import { ConstraintView } from 'react-declarative';
import { DragDropView } from 'react-declarative';
import { ScrollView } from 'react-declarative';
import { ScaleView } from 'react-declarative';
import { FadeView } from 'react-declarative';
import { TabsView } from 'react-declarative';
import { WaitView } from 'react-declarative';
import { PingView } from 'react-declarative';
import { OfflineView } from 'react-declarative';
import { RevealView } from 'react-declarative';
import { SecretView } from 'react-declarative';
import { PortalView } from 'react-declarative';
import { RecordView } from 'react-declarative';
import { CardView } from 'react-declarative';
import { ErrorView } from 'react-declarative';
import { AuthView } from 'react-declarative';
import { InfiniteView } from 'react-declarative';
import { VirtualView } from 'react-declarative';
Patterns inside
-
MVVM -
useCollection
,useModel
-
DI -
provide
,inject
,createServiceManager
-
Builder -
useListEditor
,useMediaStreamBuilder
-
Observer -
useChangeSubject
,useSubject
,useRenderWaiter
,Subject
,BehaviorSubject
,EventEmitter
,fromPromise
-
Command -
ActionTrigger
,ActionFilter
,ActionButton
,ActionToggle
,ActionMenu
,ActionIcon
,ActionModal
,InfiniteView
,VirtualView
,useActionModal
-
Coroutine -
FetchView
,WaitView
,PingView
,Async
,If
,useAsyncAction
-
Routing -
Switch
,getRouteParams
,getRouteItem
,useRouteParams
,useRouteItem
,createRouteItemManager
,createRouteParamsManager
-
Monad -
singleshot
,cancelable
,queued
,cached
,debounce
,compose
-
Composition -
VirtualView
,InfiniteView
,PortalView
,RevealView
,PingView
,WaitView
,FadeView
,ScaleView
,ScrollView
-
HoC -
ConstraintView
,AutoSizer
,FetchView
,Async
,If
-
Facade -
Subject
,Observer
-
RAD -
RecordView
,CardView
-
Functional -
useActualValue
,useActualCallback
,useActualState
,useSearchParams
,useSearchState
,useChange
-
Declarative -
One
,List
,Scaffold
,Scaffold2
,RecordView
,CardView
-
Reactive -
EventEmitter
,Subject
,BehaviorSubject
,Observer
Philosophy notes
-
React: declarative vs imperative
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
-
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.
-
SOLID principles described by simple words with examples in a source code
License
MIT © tripolskypetr