@cxbox-ui/core 2.6.1-beta.0 → 2.6.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +347 -347
- package/dist/core.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,347 +1,347 @@
|
|
|
1
|
-
<h2 align="center">CXBOX UI</h2>
|
|
2
|
-
|
|
3
|
-
<div align="center">
|
|
4
|
-
<a href="https://github.com/CX-Box/cxbox-ui/actions/workflows/build.yml"><img src="https://github.com/CX-Box/cxbox-ui/actions/workflows/build.yml/badge.svg" title="">
|
|
5
|
-
</a>
|
|
6
|
-
<a href="https://sonarcloud.io/summary/overall?id=CX-Box_cxbox-ui"><img src="https://sonarcloud.io/api/project_badges/measure?project=CX-Box_cxbox-ui&metric=alert_status&branch=main" alt="sonar" title="">
|
|
7
|
-
</a>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<blockquote>
|
|
11
|
-
<div>
|
|
12
|
-
<p align="center">
|
|
13
|
-
<h4 align="center">CXBOX - Rapid Enterprise Level Application Development Platform</h4>
|
|
14
|
-
|
|
15
|
-
<p align="center">
|
|
16
|
-
<a href="http://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat" alt="license" title=""></a>
|
|
17
|
-
</p>
|
|
18
|
-
|
|
19
|
-
<div align="center">
|
|
20
|
-
<h3>
|
|
21
|
-
<a href="https://cxbox.org/" target="_blank">
|
|
22
|
-
Website
|
|
23
|
-
</a>
|
|
24
|
-
<span> | </span>
|
|
25
|
-
<a href="http://demo.cxbox.org/" target="_blank">
|
|
26
|
-
Demo
|
|
27
|
-
</a>
|
|
28
|
-
<span> | </span>
|
|
29
|
-
<a href="https://doc.cxbox.org/" target="_blank">
|
|
30
|
-
Documentation
|
|
31
|
-
</a>
|
|
32
|
-
</h3>
|
|
33
|
-
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<h3>Description</h2>
|
|
39
|
-
<p>
|
|
40
|
-
CXBOX main purpose is to speed up development of typical Enterprise Level Application based on Spring Boot. A fixed
|
|
41
|
-
contract with a user interface called <a href="https://github.com/CX-Box/cxbox-ui" target="_blank">Cxbox-UI</a> allows backend developer to create
|
|
42
|
-
typical interfaces providing just Json meta files. Full set of typical Enterprise Level UI components included -
|
|
43
|
-
widgets, fields, layouts (views), navigation (screens).
|
|
44
|
-
</p>
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
<h3>Using CXBOX</h2>
|
|
48
|
-
<ul>
|
|
49
|
-
<li> <a href="https://plugins.jetbrains.com/plugin/19523-tesler-helper" target="_blank">download Intellij Plugin</a> adds platform specific autocomplete, inspection, navigation and code generation features.
|
|
50
|
-
</li>
|
|
51
|
-
<li>
|
|
52
|
-
<a href="https://github.com/CX-Box/cxbox-demo" target="_blank">download Demo</a> and follow <a href="https://github.com/CX-Box/cxbox-demo#readme" target="_blank">README.md</a> instructions. Feel free to use demo as template project to start your own projects
|
|
53
|
-
</li>
|
|
54
|
-
</ul>
|
|
55
|
-
</blockquote>
|
|
56
|
-
|
|
57
|
-
# CXBOX UI
|
|
58
|
-
|
|
59
|
-
## Main concepts
|
|
60
|
-
|
|
61
|
-
**[@cxbox-ui/core](https://www.npmjs.com/package/@cxbox-ui/core)** is a typescript library that includes set of prebuilt pieces of Redux ecosystem, to correctly interact with CXBox framework. It contains all parts to combine Redux store inside of your React application.
|
|
62
|
-
|
|
63
|
-
- Actions
|
|
64
|
-
- Reducers
|
|
65
|
-
- Middlewares
|
|
66
|
-
- Epics
|
|
67
|
-
- *RxJS methods for asynchronous interaction with CXBOX framework*
|
|
68
|
-
- Api
|
|
69
|
-
- *Wrapped in RxJS observables*
|
|
70
|
-
- Utilities
|
|
71
|
-
- Interfaces
|
|
72
|
-
- *CXBOX interaction typescript contract*
|
|
73
|
-
|
|
74
|
-

|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Changelog
|
|
79
|
-
|
|
80
|
-

|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Getting started
|
|
85
|
-
|
|
86
|
-
The best way to start CXBox project is to clone [CXBox-Demo](https://github.com/CX-Box/cxbox-demo) and follow the [README](https://github.com/CX-Box/cxbox-demo/blob/main/README.md) instructions
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Manual installation
|
|
91
|
-
|
|
92
|
-
**[@cxbox-ui/core](https://www.npmjs.com/package/@cxbox-ui/core)** distributed in form of ESM* npm package:
|
|
93
|
-
```bash
|
|
94
|
-
yarn add @cxbox-ui/core
|
|
95
|
-
```
|
|
96
|
-
or
|
|
97
|
-
```bash
|
|
98
|
-
npm install @cxbox-ui/core
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Several libraries are specified as peer dependencies and should be installed
|
|
102
|
-
- react
|
|
103
|
-
- react-dom
|
|
104
|
-
- @reduxjs/toolkit
|
|
105
|
-
- rxjs
|
|
106
|
-
- redux-observable
|
|
107
|
-
- axios
|
|
108
|
-
|
|
109
|
-
> [!WARNING]
|
|
110
|
-
> CJS module system are no longer supported
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Usage
|
|
114
|
-
|
|
115
|
-
Library proposes to use common way of configuring your Redux store with some nuances
|
|
116
|
-
|
|
117
|
-
[How to configure default Redux store](https://redux-toolkit.js.org/tutorials/quick-start)
|
|
118
|
-
|
|
119
|
-
### Reducers
|
|
120
|
-
[Proper way of creating reducers](https://redux-toolkit.js.org/api/createReducer#usage-with-the-builder-callback-notation)
|
|
121
|
-
|
|
122
|
-
**@cxbox-ui/core** exports all arguments for **@reduxjs/toolkit** `createReducer` method, such as `initialState` and `createReducerBuilderManager` instances of default **_CXBox reducers_**
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
import {reducers} from '@cxbox-ui/core'
|
|
126
|
-
import {createReducer} from '@reduxjs/toolkit'
|
|
127
|
-
|
|
128
|
-
const viewReducerBuilder = reducers
|
|
129
|
-
.createViewReducerBuilderManager(reducers.initialViewState)
|
|
130
|
-
.builder
|
|
131
|
-
|
|
132
|
-
const viewReducer = createReducer(reducers.initialViewState, viewReducerBuilder)
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
`ReducerBuildManager` implements default methods of `createReducer` builder callback argument, with `removeCase` and `replaceCase`
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
const viewReducerBuilder = reducers
|
|
139
|
-
.createViewReducerBuilderManager(reducers.initialViewState)
|
|
140
|
-
.removeCase('sampleCXBoxAction')
|
|
141
|
-
.addCase('anotherSampleAction', () => {/** do something with state */})
|
|
142
|
-
.replaceCase('someCXBoxActionToo', () => {/** in case of CXBox realization is not satisfying */})
|
|
143
|
-
.builder
|
|
144
|
-
|
|
145
|
-
const viewReducer = createReducer(reducers.initialViewState, viewReducerBuilder)
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
More appropriate case samples in [CXBox-Demo](https://github.com/CX-Box/cxbox-demo/tree/main/ui/src/reducers)
|
|
149
|
-
|
|
150
|
-
### Combine reducers
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
import {combineReducers, configureStore} from '@reduxjs/toolkit'
|
|
154
|
-
|
|
155
|
-
const rootReducer = combineReducers({
|
|
156
|
-
screen: screenReducer,
|
|
157
|
-
data: dataReducer,
|
|
158
|
-
view: viewReducer,
|
|
159
|
-
session: sessionReducer,
|
|
160
|
-
router: routerReducer,
|
|
161
|
-
notification: notificationReducer
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
const store = configureStore({
|
|
165
|
-
reducers: rootReducer
|
|
166
|
-
})
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Redux-observable middleware
|
|
170
|
-
|
|
171
|
-
To make this store work with CXBox backend, you should configure asynchronous interaction by applying preconfigured [Redux-Observable](https://redux-observable.js.org/) **Epics**
|
|
172
|
-
|
|
173
|
-
It will take two steps
|
|
174
|
-
- Configure Api Axios instance
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
import {Api} from '@cxbox-ui/core'
|
|
178
|
-
import Axios from 'axios'
|
|
179
|
-
|
|
180
|
-
const __AJAX_TIMEOUT__ = 900000
|
|
181
|
-
const __CLIENT_ID__: number = Date.now()
|
|
182
|
-
|
|
183
|
-
const HEADERS = { Pragma: 'no-cache', 'Cache-Control': 'no-cache, no-store, must-revalidate' }
|
|
184
|
-
|
|
185
|
-
const instance = axios.create({
|
|
186
|
-
baseURL: __API__,
|
|
187
|
-
timeout: __AJAX_TIMEOUT__,
|
|
188
|
-
responseType: 'json',
|
|
189
|
-
headers: {
|
|
190
|
-
...HEADERS,
|
|
191
|
-
...{ ClientId: __CLIENT_ID__ }
|
|
192
|
-
}
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
const CXBoxApiInstance = new Api(instance)
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
_You can also extend any of Api methods to use in your epics_
|
|
199
|
-
|
|
200
|
-
```ts
|
|
201
|
-
import {Api} from '@cxbox-ui/core'
|
|
202
|
-
|
|
203
|
-
class ExtendedApi extends Api {
|
|
204
|
-
getSomethingUnusual(thing: string) {
|
|
205
|
-
return this.$api.get(thing)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const CXBoxApiInstance = new ExtendedApi(axiosInstance)
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
- [Setting up middleware](https://redux-observable.js.org/docs/basics/SettingUpTheMiddleware.html)
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
import {epics} from '@cxbox-ui/core'
|
|
216
|
-
import {combineEpics, createEpicMiddleware} from 'redux-observable'
|
|
217
|
-
import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit'
|
|
218
|
-
|
|
219
|
-
// Typescript cast if you changed default CXBoxEpic type
|
|
220
|
-
const coreEpics = {...epics} as unknown as Record<string, RootEpic>
|
|
221
|
-
|
|
222
|
-
const rootEpic = combineEpics(Object.values(coreEpics))
|
|
223
|
-
|
|
224
|
-
const epicMiddleware = createEpicMiddleware({
|
|
225
|
-
dependencies: {
|
|
226
|
-
api: CXBoxApiInstance
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
const store = configureStore({
|
|
231
|
-
reducer: rootReducer,
|
|
232
|
-
middleware: getDefaultMiddleware().concat(epicMiddleware)
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
epicMiddleware.run(rootEpic)
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Epics
|
|
239
|
-
|
|
240
|
-
Feel free to add, but be careful to replace or remove some epics. They can call actions that causes chain effect, but if you sure in consequences, manipulate imported epics object as you want.
|
|
241
|
-
|
|
242
|
-
```ts
|
|
243
|
-
import {createAction} from '@reduxjs/toolkit'
|
|
244
|
-
|
|
245
|
-
const sampleAction = createAction<string>('sampleAction')
|
|
246
|
-
|
|
247
|
-
const sampleEpic: RootEpic = (action$, state$, {api}) =>
|
|
248
|
-
action$.pipe(
|
|
249
|
-
filter(sampleAction.match),
|
|
250
|
-
switchMap(action => {
|
|
251
|
-
const name = action.payload
|
|
252
|
-
|
|
253
|
-
if (name) {
|
|
254
|
-
api.doSomething(name)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return EMPTY
|
|
258
|
-
}),
|
|
259
|
-
catchError(error => {
|
|
260
|
-
return utils.createApiErrorObservable(error)
|
|
261
|
-
})
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
const rootEpic = combineEpics(...Object.values(epics), sampleEpic)
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### Middlewares
|
|
268
|
-
|
|
269
|
-
@cxbox-ui/core also exports some middlewares, that not uses Redux-Observable way, but they still need to be applied
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit'
|
|
273
|
-
import {middlewares} from '@cxbox-ui/core'
|
|
274
|
-
|
|
275
|
-
const store = configureStore({
|
|
276
|
-
reducer: rootReducer,
|
|
277
|
-
middleware: getDefaultMiddleware()
|
|
278
|
-
.concat(...Object.values(middlewares))
|
|
279
|
-
.concat(epicMiddleware)
|
|
280
|
-
})
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
---
|
|
284
|
-
|
|
285
|
-
## Widget example
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
```tsx
|
|
289
|
-
import React from 'react'
|
|
290
|
-
import { interfaces } from '@cxbox-ui/core'
|
|
291
|
-
import { useDispatch } from 'react-redux'
|
|
292
|
-
import { useAppSelector } from '@store'
|
|
293
|
-
import { actions } from '@cxbox-ui/core'
|
|
294
|
-
import InfoRow from './components/InfoRow'
|
|
295
|
-
import { Row } from 'antd'
|
|
296
|
-
import { useFlatFormFields } from '@hooks/useFlatFormFields'
|
|
297
|
-
|
|
298
|
-
interface FunnelProps {
|
|
299
|
-
meta: FunnelWidgetMeta
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function Funnel({ meta }: FunnelProps) {
|
|
303
|
-
const { bcName } = meta
|
|
304
|
-
const data = useAppSelector(state => state.data[bcName])
|
|
305
|
-
const sortedData = data?.slice().sort(sorter)
|
|
306
|
-
const funnelData = sortedData?.map(i => ({ id: i.funnelKey, value: i.amount }))
|
|
307
|
-
const color = sortedData?.map(i => i.color) as Array<string>
|
|
308
|
-
|
|
309
|
-
const legend: Types.LegendCfg = {
|
|
310
|
-
position: 'right',
|
|
311
|
-
layout: 'vertical',
|
|
312
|
-
itemMarginBottom: 16,
|
|
313
|
-
itemName: { style: { fontSize: 14, fontFamily: 'Roboto', fontWeight: 400 } },
|
|
314
|
-
marker: (name, index) => ({
|
|
315
|
-
symbol: 'circle',
|
|
316
|
-
style: {
|
|
317
|
-
fill: color[index]
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
}
|
|
321
|
-
const label: Label = {
|
|
322
|
-
content: labelData => labelData.value,
|
|
323
|
-
style: {
|
|
324
|
-
fontSize: 20,
|
|
325
|
-
fontFamily: 'Roboto',
|
|
326
|
-
fontWeight: '700',
|
|
327
|
-
fill: '#141F35'
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return (
|
|
331
|
-
<div>
|
|
332
|
-
<AntFunnel data={funnelData} xField="id" yField="value" color={color} conversionTag={false} legend={legend} label={label} />
|
|
333
|
-
</div>
|
|
334
|
-
)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function sorter(a: interfaces.DataItem, b: interfaces.DataItem) {
|
|
338
|
-
return parseInt(b.amount as string) - parseInt(a.amount as string)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
export default React.memo(Funnel)
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
# Contributing
|
|
345
|
-
|
|
346
|
-
All contributions are welcomed, as even a minor pull request with grammar fixes or a single documentation update is of a significant help for us!
|
|
347
|
-
We promise to handle all PR reviews in a friendly and respectful manner.
|
|
1
|
+
<h2 align="center">CXBOX UI</h2>
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<a href="https://github.com/CX-Box/cxbox-ui/actions/workflows/build.yml"><img src="https://github.com/CX-Box/cxbox-ui/actions/workflows/build.yml/badge.svg" title="">
|
|
5
|
+
</a>
|
|
6
|
+
<a href="https://sonarcloud.io/summary/overall?id=CX-Box_cxbox-ui"><img src="https://sonarcloud.io/api/project_badges/measure?project=CX-Box_cxbox-ui&metric=alert_status&branch=main" alt="sonar" title="">
|
|
7
|
+
</a>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<blockquote>
|
|
11
|
+
<div>
|
|
12
|
+
<p align="center">
|
|
13
|
+
<h4 align="center">CXBOX - Rapid Enterprise Level Application Development Platform</h4>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="http://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat" alt="license" title=""></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<div align="center">
|
|
20
|
+
<h3>
|
|
21
|
+
<a href="https://cxbox.org/" target="_blank">
|
|
22
|
+
Website
|
|
23
|
+
</a>
|
|
24
|
+
<span> | </span>
|
|
25
|
+
<a href="http://demo.cxbox.org/" target="_blank">
|
|
26
|
+
Demo
|
|
27
|
+
</a>
|
|
28
|
+
<span> | </span>
|
|
29
|
+
<a href="https://doc.cxbox.org/" target="_blank">
|
|
30
|
+
Documentation
|
|
31
|
+
</a>
|
|
32
|
+
</h3>
|
|
33
|
+
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
<h3>Description</h2>
|
|
39
|
+
<p>
|
|
40
|
+
CXBOX main purpose is to speed up development of typical Enterprise Level Application based on Spring Boot. A fixed
|
|
41
|
+
contract with a user interface called <a href="https://github.com/CX-Box/cxbox-ui" target="_blank">Cxbox-UI</a> allows backend developer to create
|
|
42
|
+
typical interfaces providing just Json meta files. Full set of typical Enterprise Level UI components included -
|
|
43
|
+
widgets, fields, layouts (views), navigation (screens).
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<h3>Using CXBOX</h2>
|
|
48
|
+
<ul>
|
|
49
|
+
<li> <a href="https://plugins.jetbrains.com/plugin/19523-tesler-helper" target="_blank">download Intellij Plugin</a> adds platform specific autocomplete, inspection, navigation and code generation features.
|
|
50
|
+
</li>
|
|
51
|
+
<li>
|
|
52
|
+
<a href="https://github.com/CX-Box/cxbox-demo" target="_blank">download Demo</a> and follow <a href="https://github.com/CX-Box/cxbox-demo#readme" target="_blank">README.md</a> instructions. Feel free to use demo as template project to start your own projects
|
|
53
|
+
</li>
|
|
54
|
+
</ul>
|
|
55
|
+
</blockquote>
|
|
56
|
+
|
|
57
|
+
# CXBOX UI
|
|
58
|
+
|
|
59
|
+
## Main concepts
|
|
60
|
+
|
|
61
|
+
**[@cxbox-ui/core](https://www.npmjs.com/package/@cxbox-ui/core)** is a typescript library that includes set of prebuilt pieces of Redux ecosystem, to correctly interact with CXBox framework. It contains all parts to combine Redux store inside of your React application.
|
|
62
|
+
|
|
63
|
+
- Actions
|
|
64
|
+
- Reducers
|
|
65
|
+
- Middlewares
|
|
66
|
+
- Epics
|
|
67
|
+
- *RxJS methods for asynchronous interaction with CXBOX framework*
|
|
68
|
+
- Api
|
|
69
|
+
- *Wrapped in RxJS observables*
|
|
70
|
+
- Utilities
|
|
71
|
+
- Interfaces
|
|
72
|
+
- *CXBOX interaction typescript contract*
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Changelog
|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Getting started
|
|
85
|
+
|
|
86
|
+
The best way to start CXBox project is to clone [CXBox-Demo](https://github.com/CX-Box/cxbox-demo) and follow the [README](https://github.com/CX-Box/cxbox-demo/blob/main/README.md) instructions
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Manual installation
|
|
91
|
+
|
|
92
|
+
**[@cxbox-ui/core](https://www.npmjs.com/package/@cxbox-ui/core)** distributed in form of ESM* npm package:
|
|
93
|
+
```bash
|
|
94
|
+
yarn add @cxbox-ui/core
|
|
95
|
+
```
|
|
96
|
+
or
|
|
97
|
+
```bash
|
|
98
|
+
npm install @cxbox-ui/core
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Several libraries are specified as peer dependencies and should be installed
|
|
102
|
+
- react
|
|
103
|
+
- react-dom
|
|
104
|
+
- @reduxjs/toolkit
|
|
105
|
+
- rxjs
|
|
106
|
+
- redux-observable
|
|
107
|
+
- axios
|
|
108
|
+
|
|
109
|
+
> [!WARNING]
|
|
110
|
+
> CJS module system are no longer supported
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Usage
|
|
114
|
+
|
|
115
|
+
Library proposes to use common way of configuring your Redux store with some nuances
|
|
116
|
+
|
|
117
|
+
[How to configure default Redux store](https://redux-toolkit.js.org/tutorials/quick-start)
|
|
118
|
+
|
|
119
|
+
### Reducers
|
|
120
|
+
[Proper way of creating reducers](https://redux-toolkit.js.org/api/createReducer#usage-with-the-builder-callback-notation)
|
|
121
|
+
|
|
122
|
+
**@cxbox-ui/core** exports all arguments for **@reduxjs/toolkit** `createReducer` method, such as `initialState` and `createReducerBuilderManager` instances of default **_CXBox reducers_**
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import {reducers} from '@cxbox-ui/core'
|
|
126
|
+
import {createReducer} from '@reduxjs/toolkit'
|
|
127
|
+
|
|
128
|
+
const viewReducerBuilder = reducers
|
|
129
|
+
.createViewReducerBuilderManager(reducers.initialViewState)
|
|
130
|
+
.builder
|
|
131
|
+
|
|
132
|
+
const viewReducer = createReducer(reducers.initialViewState, viewReducerBuilder)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`ReducerBuildManager` implements default methods of `createReducer` builder callback argument, with `removeCase` and `replaceCase`
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
const viewReducerBuilder = reducers
|
|
139
|
+
.createViewReducerBuilderManager(reducers.initialViewState)
|
|
140
|
+
.removeCase('sampleCXBoxAction')
|
|
141
|
+
.addCase('anotherSampleAction', () => {/** do something with state */})
|
|
142
|
+
.replaceCase('someCXBoxActionToo', () => {/** in case of CXBox realization is not satisfying */})
|
|
143
|
+
.builder
|
|
144
|
+
|
|
145
|
+
const viewReducer = createReducer(reducers.initialViewState, viewReducerBuilder)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
More appropriate case samples in [CXBox-Demo](https://github.com/CX-Box/cxbox-demo/tree/main/ui/src/reducers)
|
|
149
|
+
|
|
150
|
+
### Combine reducers
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import {combineReducers, configureStore} from '@reduxjs/toolkit'
|
|
154
|
+
|
|
155
|
+
const rootReducer = combineReducers({
|
|
156
|
+
screen: screenReducer,
|
|
157
|
+
data: dataReducer,
|
|
158
|
+
view: viewReducer,
|
|
159
|
+
session: sessionReducer,
|
|
160
|
+
router: routerReducer,
|
|
161
|
+
notification: notificationReducer
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const store = configureStore({
|
|
165
|
+
reducers: rootReducer
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Redux-observable middleware
|
|
170
|
+
|
|
171
|
+
To make this store work with CXBox backend, you should configure asynchronous interaction by applying preconfigured [Redux-Observable](https://redux-observable.js.org/) **Epics**
|
|
172
|
+
|
|
173
|
+
It will take two steps
|
|
174
|
+
- Configure Api Axios instance
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import {Api} from '@cxbox-ui/core'
|
|
178
|
+
import Axios from 'axios'
|
|
179
|
+
|
|
180
|
+
const __AJAX_TIMEOUT__ = 900000
|
|
181
|
+
const __CLIENT_ID__: number = Date.now()
|
|
182
|
+
|
|
183
|
+
const HEADERS = { Pragma: 'no-cache', 'Cache-Control': 'no-cache, no-store, must-revalidate' }
|
|
184
|
+
|
|
185
|
+
const instance = axios.create({
|
|
186
|
+
baseURL: __API__,
|
|
187
|
+
timeout: __AJAX_TIMEOUT__,
|
|
188
|
+
responseType: 'json',
|
|
189
|
+
headers: {
|
|
190
|
+
...HEADERS,
|
|
191
|
+
...{ ClientId: __CLIENT_ID__ }
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const CXBoxApiInstance = new Api(instance)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
_You can also extend any of Api methods to use in your epics_
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import {Api} from '@cxbox-ui/core'
|
|
202
|
+
|
|
203
|
+
class ExtendedApi extends Api {
|
|
204
|
+
getSomethingUnusual(thing: string) {
|
|
205
|
+
return this.$api.get(thing)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const CXBoxApiInstance = new ExtendedApi(axiosInstance)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- [Setting up middleware](https://redux-observable.js.org/docs/basics/SettingUpTheMiddleware.html)
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import {epics} from '@cxbox-ui/core'
|
|
216
|
+
import {combineEpics, createEpicMiddleware} from 'redux-observable'
|
|
217
|
+
import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit'
|
|
218
|
+
|
|
219
|
+
// Typescript cast if you changed default CXBoxEpic type
|
|
220
|
+
const coreEpics = {...epics} as unknown as Record<string, RootEpic>
|
|
221
|
+
|
|
222
|
+
const rootEpic = combineEpics(Object.values(coreEpics))
|
|
223
|
+
|
|
224
|
+
const epicMiddleware = createEpicMiddleware({
|
|
225
|
+
dependencies: {
|
|
226
|
+
api: CXBoxApiInstance
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const store = configureStore({
|
|
231
|
+
reducer: rootReducer,
|
|
232
|
+
middleware: getDefaultMiddleware().concat(epicMiddleware)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
epicMiddleware.run(rootEpic)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Epics
|
|
239
|
+
|
|
240
|
+
Feel free to add, but be careful to replace or remove some epics. They can call actions that causes chain effect, but if you sure in consequences, manipulate imported epics object as you want.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import {createAction} from '@reduxjs/toolkit'
|
|
244
|
+
|
|
245
|
+
const sampleAction = createAction<string>('sampleAction')
|
|
246
|
+
|
|
247
|
+
const sampleEpic: RootEpic = (action$, state$, {api}) =>
|
|
248
|
+
action$.pipe(
|
|
249
|
+
filter(sampleAction.match),
|
|
250
|
+
switchMap(action => {
|
|
251
|
+
const name = action.payload
|
|
252
|
+
|
|
253
|
+
if (name) {
|
|
254
|
+
api.doSomething(name)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return EMPTY
|
|
258
|
+
}),
|
|
259
|
+
catchError(error => {
|
|
260
|
+
return utils.createApiErrorObservable(error)
|
|
261
|
+
})
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
const rootEpic = combineEpics(...Object.values(epics), sampleEpic)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Middlewares
|
|
268
|
+
|
|
269
|
+
@cxbox-ui/core also exports some middlewares, that not uses Redux-Observable way, but they still need to be applied
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit'
|
|
273
|
+
import {middlewares} from '@cxbox-ui/core'
|
|
274
|
+
|
|
275
|
+
const store = configureStore({
|
|
276
|
+
reducer: rootReducer,
|
|
277
|
+
middleware: getDefaultMiddleware()
|
|
278
|
+
.concat(...Object.values(middlewares))
|
|
279
|
+
.concat(epicMiddleware)
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Widget example
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
import React from 'react'
|
|
290
|
+
import { interfaces } from '@cxbox-ui/core'
|
|
291
|
+
import { useDispatch } from 'react-redux'
|
|
292
|
+
import { useAppSelector } from '@store'
|
|
293
|
+
import { actions } from '@cxbox-ui/core'
|
|
294
|
+
import InfoRow from './components/InfoRow'
|
|
295
|
+
import { Row } from 'antd'
|
|
296
|
+
import { useFlatFormFields } from '@hooks/useFlatFormFields'
|
|
297
|
+
|
|
298
|
+
interface FunnelProps {
|
|
299
|
+
meta: FunnelWidgetMeta
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function Funnel({ meta }: FunnelProps) {
|
|
303
|
+
const { bcName } = meta
|
|
304
|
+
const data = useAppSelector(state => state.data[bcName])
|
|
305
|
+
const sortedData = data?.slice().sort(sorter)
|
|
306
|
+
const funnelData = sortedData?.map(i => ({ id: i.funnelKey, value: i.amount }))
|
|
307
|
+
const color = sortedData?.map(i => i.color) as Array<string>
|
|
308
|
+
|
|
309
|
+
const legend: Types.LegendCfg = {
|
|
310
|
+
position: 'right',
|
|
311
|
+
layout: 'vertical',
|
|
312
|
+
itemMarginBottom: 16,
|
|
313
|
+
itemName: { style: { fontSize: 14, fontFamily: 'Roboto', fontWeight: 400 } },
|
|
314
|
+
marker: (name, index) => ({
|
|
315
|
+
symbol: 'circle',
|
|
316
|
+
style: {
|
|
317
|
+
fill: color[index]
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
const label: Label = {
|
|
322
|
+
content: labelData => labelData.value,
|
|
323
|
+
style: {
|
|
324
|
+
fontSize: 20,
|
|
325
|
+
fontFamily: 'Roboto',
|
|
326
|
+
fontWeight: '700',
|
|
327
|
+
fill: '#141F35'
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return (
|
|
331
|
+
<div>
|
|
332
|
+
<AntFunnel data={funnelData} xField="id" yField="value" color={color} conversionTag={false} legend={legend} label={label} />
|
|
333
|
+
</div>
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function sorter(a: interfaces.DataItem, b: interfaces.DataItem) {
|
|
338
|
+
return parseInt(b.amount as string) - parseInt(a.amount as string)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export default React.memo(Funnel)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
# Contributing
|
|
345
|
+
|
|
346
|
+
All contributions are welcomed, as even a minor pull request with grammar fixes or a single documentation update is of a significant help for us!
|
|
347
|
+
We promise to handle all PR reviews in a friendly and respectful manner.
|