@cxbox-ui/core 2.7.2-alpha.0 → 2.7.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/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
- ![schema](./docs/schemas/arch.jpg)
75
-
76
- ---
77
-
78
- ## Changelog
79
-
80
- ![changelog](./docs/schemas/changelog.jpg)
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
+ ![schema](./docs/schemas/arch.jpg)
75
+
76
+ ---
77
+
78
+ ## Changelog
79
+
80
+ ![changelog](./docs/schemas/changelog.jpg)
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.