@nocobase/plugin-data-visualization 0.10.1-alpha.1

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.
Files changed (108) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +88 -0
  3. package/client.d.ts +3 -0
  4. package/client.js +65 -0
  5. package/lib/client/Settings.d.ts +2 -0
  6. package/lib/client/Settings.js +81 -0
  7. package/lib/client/block/ChartBlock.d.ts +2 -0
  8. package/lib/client/block/ChartBlock.js +73 -0
  9. package/lib/client/block/ChartBlockDesigner.d.ts +2 -0
  10. package/lib/client/block/ChartBlockDesigner.js +35 -0
  11. package/lib/client/block/ChartBlockInitializer.d.ts +6 -0
  12. package/lib/client/block/ChartBlockInitializer.js +114 -0
  13. package/lib/client/block/ChartConfigure.d.ts +33 -0
  14. package/lib/client/block/ChartConfigure.js +501 -0
  15. package/lib/client/block/formatters.d.ts +15 -0
  16. package/lib/client/block/formatters.js +58 -0
  17. package/lib/client/block/index.d.ts +4 -0
  18. package/lib/client/block/index.js +49 -0
  19. package/lib/client/block/schemas/configure.d.ts +4 -0
  20. package/lib/client/block/schemas/configure.js +492 -0
  21. package/lib/client/block/transformers.d.ts +6 -0
  22. package/lib/client/block/transformers.js +69 -0
  23. package/lib/client/hooks.d.ts +312 -0
  24. package/lib/client/hooks.js +275 -0
  25. package/lib/client/index.d.ts +5 -0
  26. package/lib/client/index.js +70 -0
  27. package/lib/client/locale/en-US.d.ts +23 -0
  28. package/lib/client/locale/en-US.js +29 -0
  29. package/lib/client/locale/index.d.ts +3 -0
  30. package/lib/client/locale/index.js +39 -0
  31. package/lib/client/locale/ja-JP.d.ts +2 -0
  32. package/lib/client/locale/ja-JP.js +8 -0
  33. package/lib/client/locale/pt-BR.d.ts +23 -0
  34. package/lib/client/locale/pt-BR.js +29 -0
  35. package/lib/client/locale/ru-RU.d.ts +2 -0
  36. package/lib/client/locale/ru-RU.js +8 -0
  37. package/lib/client/locale/tr-TR.d.ts +2 -0
  38. package/lib/client/locale/tr-TR.js +8 -0
  39. package/lib/client/locale/zh-CN.d.ts +70 -0
  40. package/lib/client/locale/zh-CN.js +76 -0
  41. package/lib/client/renderer/ChartLibrary.d.ts +71 -0
  42. package/lib/client/renderer/ChartLibrary.js +140 -0
  43. package/lib/client/renderer/ChartRenderer.d.ts +7 -0
  44. package/lib/client/renderer/ChartRenderer.js +258 -0
  45. package/lib/client/renderer/ChartRendererProvider.d.ts +43 -0
  46. package/lib/client/renderer/ChartRendererProvider.js +38 -0
  47. package/lib/client/renderer/index.d.ts +4 -0
  48. package/lib/client/renderer/index.js +49 -0
  49. package/lib/client/renderer/library/AntdLibrary.d.ts +2 -0
  50. package/lib/client/renderer/library/AntdLibrary.js +123 -0
  51. package/lib/client/renderer/library/G2PlotLibrary.d.ts +2 -0
  52. package/lib/client/renderer/library/G2PlotLibrary.js +288 -0
  53. package/lib/client/renderer/library/index.d.ts +3 -0
  54. package/lib/client/renderer/library/index.js +15 -0
  55. package/lib/client/utils.d.ts +96 -0
  56. package/lib/client/utils.js +137 -0
  57. package/lib/index.d.ts +1 -0
  58. package/lib/index.js +13 -0
  59. package/lib/server/actions/formatter.d.ts +3 -0
  60. package/lib/server/actions/formatter.js +44 -0
  61. package/lib/server/actions/query.d.ts +86 -0
  62. package/lib/server/actions/query.js +326 -0
  63. package/lib/server/index.d.ts +1 -0
  64. package/lib/server/index.js +13 -0
  65. package/lib/server/plugin.d.ts +13 -0
  66. package/lib/server/plugin.js +64 -0
  67. package/package.json +23 -0
  68. package/server.d.ts +3 -0
  69. package/server.js +65 -0
  70. package/src/client/Settings.tsx +43 -0
  71. package/src/client/__tests__/chart-configure.test.tsx +14 -0
  72. package/src/client/__tests__/chart-library.test.ts +78 -0
  73. package/src/client/__tests__/chart-renderer.test.tsx +30 -0
  74. package/src/client/__tests__/hooks.test.ts +261 -0
  75. package/src/client/block/ChartBlock.tsx +22 -0
  76. package/src/client/block/ChartBlockDesigner.tsx +19 -0
  77. package/src/client/block/ChartBlockInitializer.tsx +83 -0
  78. package/src/client/block/ChartConfigure.tsx +450 -0
  79. package/src/client/block/formatters.ts +70 -0
  80. package/src/client/block/index.ts +4 -0
  81. package/src/client/block/schemas/configure.ts +474 -0
  82. package/src/client/block/transformers.ts +52 -0
  83. package/src/client/hooks.ts +239 -0
  84. package/src/client/index.tsx +41 -0
  85. package/src/client/locale/en-US.ts +23 -0
  86. package/src/client/locale/index.ts +19 -0
  87. package/src/client/locale/ja-JP.ts +1 -0
  88. package/src/client/locale/pt-BR.ts +23 -0
  89. package/src/client/locale/ru-RU.ts +1 -0
  90. package/src/client/locale/tr-TR.ts +1 -0
  91. package/src/client/locale/zh-CN.ts +71 -0
  92. package/src/client/renderer/ChartLibrary.tsx +178 -0
  93. package/src/client/renderer/ChartRenderer.tsx +201 -0
  94. package/src/client/renderer/ChartRendererProvider.tsx +58 -0
  95. package/src/client/renderer/index.ts +4 -0
  96. package/src/client/renderer/library/AntdLibrary.tsx +94 -0
  97. package/src/client/renderer/library/G2PlotLibrary.tsx +236 -0
  98. package/src/client/renderer/library/index.tsx +4 -0
  99. package/src/client/utils.ts +102 -0
  100. package/src/index.ts +1 -0
  101. package/src/server/__tests__/api.test.ts +105 -0
  102. package/src/server/__tests__/formatter.test.ts +49 -0
  103. package/src/server/__tests__/query.test.ts +220 -0
  104. package/src/server/actions/formatter.ts +49 -0
  105. package/src/server/actions/query.ts +285 -0
  106. package/src/server/collections/.gitkeep +0 -0
  107. package/src/server/index.ts +1 -0
  108. package/src/server/plugin.ts +37 -0
@@ -0,0 +1,239 @@
1
+ import { ArrayField } from '@formily/core';
2
+ import { ISchema, Schema } from '@formily/react';
3
+ import { useACLRoleContext, useCollectionManager } from '@nocobase/client';
4
+ import { useContext } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { ChartConfigContext } from './block/ChartConfigure';
7
+ import formatters from './block/formatters';
8
+ import transformers from './block/transformers';
9
+ import { lang } from './locale';
10
+ import { ChartRendererProps } from './renderer';
11
+ import { getField, getSelectedFields, parseField } from './utils';
12
+
13
+ export type FieldOption = {
14
+ value: string;
15
+ label: string;
16
+ key: string;
17
+ alias?: string;
18
+ name?: string;
19
+ type?: string;
20
+ interface?: string;
21
+ uiSchema?: ISchema;
22
+ target?: string;
23
+ targetFields?: FieldOption[];
24
+ };
25
+
26
+ export const useFields = (collection?: string) => {
27
+ const { current } = useContext(ChartConfigContext);
28
+ if (!collection) {
29
+ collection = current?.collection || '';
30
+ }
31
+ const { getCollectionFields } = useCollectionManager();
32
+ const fields = (getCollectionFields(collection) || [])
33
+ .filter((field) => {
34
+ return field.interface;
35
+ })
36
+ .map((field) => ({
37
+ ...field,
38
+ key: field.name,
39
+ label: field.uiSchema?.title || field.name,
40
+ value: field.name,
41
+ }));
42
+ return fields;
43
+ };
44
+
45
+ export const useFieldsWithAssociation = (collection?: string) => {
46
+ const { getCollectionFields } = useCollectionManager();
47
+ const { t } = useTranslation();
48
+ const fields = useFields(collection);
49
+ return fields.map((field) => {
50
+ const label = Schema.compile(field.uiSchema?.title || field.name, { t });
51
+ if (!field.target) {
52
+ return { ...field, label };
53
+ }
54
+ const targetFields = (getCollectionFields(field.target) || [])
55
+ .filter((targetField) => {
56
+ return targetField.interface;
57
+ })
58
+ .map((targetField) => ({
59
+ ...targetField,
60
+ key: `${field.name}.${targetField.name}`,
61
+ label: `${label} / ${Schema.compile(targetField.uiSchema?.title || targetField.name, { t })}`,
62
+ value: `${field.name}.${targetField.name}`,
63
+ }));
64
+ return {
65
+ ...field,
66
+ label,
67
+ targetFields,
68
+ };
69
+ });
70
+ };
71
+
72
+ export const useChartFields = (fields: FieldOption[]) => (field: any) => {
73
+ const query = field.query('query').get('value') || {};
74
+ const selectedFields = getSelectedFields(fields, query);
75
+ field.dataSource = selectedFields;
76
+ };
77
+
78
+ export const useFormatters = (fields: FieldOption[]) => (field: any) => {
79
+ const selectedField = field.query('.field').get('value');
80
+ if (!selectedField) {
81
+ field.dataSource = [];
82
+ return;
83
+ }
84
+ let options = [];
85
+ const fieldInterface = getField(fields, selectedField)?.interface;
86
+ switch (fieldInterface) {
87
+ case 'datetime':
88
+ case 'createdAt':
89
+ case 'updatedAt':
90
+ options = formatters.datetime;
91
+ break;
92
+ case 'date':
93
+ options = formatters.date;
94
+ break;
95
+ case 'time':
96
+ options = formatters.time;
97
+ break;
98
+ default:
99
+ options = [];
100
+ }
101
+ field.dataSource = options;
102
+ };
103
+
104
+ export const useCollectionOptions = () => {
105
+ const { t } = useTranslation();
106
+ const { collections } = useCollectionManager();
107
+ const { allowAll, parseAction } = useACLRoleContext();
108
+ const options = collections
109
+ .filter((collection: { name: string }) => {
110
+ if (allowAll) {
111
+ return true;
112
+ }
113
+ const params = parseAction(`${collection.name}:list`);
114
+ return params;
115
+ })
116
+ .map((collection: { name: string; title: string }) => ({
117
+ label: collection.title,
118
+ value: collection.name,
119
+ key: collection.name,
120
+ }));
121
+ return Schema.compile(options, { t });
122
+ };
123
+
124
+ /**
125
+ * useFieldTypes
126
+ * Get field types for using transformers
127
+ * Only supported types will be displayed
128
+ * Some interfaces and types will be mapped to supported types
129
+ */
130
+ export const useFieldTypes = (fields: FieldOption[]) => (field: any) => {
131
+ const selectedField = field.query('.field').get('value');
132
+ const query = field.query('query').get('value') || {};
133
+ const selectedFields = getSelectedFields(fields, query);
134
+ const fieldProps = selectedFields.find((field) => field.value === selectedField);
135
+ const supports = Object.keys(transformers);
136
+ field.dataSource = supports.map((key) => ({
137
+ label: lang(key),
138
+ value: key,
139
+ }));
140
+ const map = {
141
+ createdAt: 'datetime',
142
+ updatedAt: 'datetime',
143
+ double: 'number',
144
+ integer: 'number',
145
+ percent: 'number',
146
+ };
147
+ const fieldInterface = fieldProps?.interface;
148
+ const fieldType = fieldProps?.type;
149
+ const key = map[fieldInterface] || map[fieldType] || fieldType;
150
+ if (supports.includes(key)) {
151
+ field.setState({
152
+ value: key,
153
+ disabled: true,
154
+ });
155
+ return;
156
+ }
157
+ field.setState({
158
+ value: null,
159
+ disabled: false,
160
+ });
161
+ };
162
+
163
+ export const useTransformers = (field: any) => {
164
+ const selectedType = field.query('.type').get('value');
165
+ if (!selectedType) {
166
+ field.dataSource = [];
167
+ return;
168
+ }
169
+ const options = Object.keys(transformers[selectedType] || {}).map((key) => ({
170
+ label: lang(key),
171
+ value: key,
172
+ }));
173
+ field.dataSource = options;
174
+ };
175
+
176
+ export const useFieldTransformer = (transform: ChartRendererProps['transform'], locale = 'en-US') => {
177
+ return (transform || [])
178
+ .filter((item) => item.field && item.type && item.format)
179
+ .reduce((mp, item) => {
180
+ const transformer = transformers[item.type][item.format];
181
+ if (!transformer) {
182
+ return mp;
183
+ }
184
+ mp[item.field] = (val: any) => transformer(val, locale);
185
+ return mp;
186
+ }, {});
187
+ };
188
+
189
+ export const useOrderFieldsOptions = (defaultOptions: any[], fields: FieldOption[]) => (field: any) => {
190
+ const query = field.query('query').get('value') || {};
191
+ const { measures = [] } = query;
192
+ const hasAgg = measures.some((measure: { aggregation?: string }) => measure.aggregation);
193
+ if (!hasAgg) {
194
+ field.componentProps.fieldNames = {
195
+ label: 'title',
196
+ value: 'name',
197
+ children: 'children',
198
+ };
199
+ field.dataSource = defaultOptions;
200
+ return;
201
+ }
202
+ const selectedFields = getSelectedFields(fields, query);
203
+ field.componentProps.fieldNames = {};
204
+ field.dataSource = selectedFields;
205
+ };
206
+
207
+ export const useOrderReaction = (defaultOptions: any[], fields: FieldOption[]) => (field: ArrayField) => {
208
+ const query = field.query('query').get('value') || {};
209
+ const { measures = [] } = query;
210
+ const hasAgg = measures.some((measure: { aggregation?: string }) => measure.aggregation);
211
+ let dataSource = defaultOptions;
212
+ const allValues = [];
213
+ if (hasAgg) {
214
+ dataSource = getSelectedFields(fields, query);
215
+ dataSource.forEach((field) => {
216
+ allValues.push(field.value);
217
+ });
218
+ } else {
219
+ dataSource.forEach((field) => {
220
+ const children = field.children || [];
221
+ if (!children.length) {
222
+ allValues.push(field.value || field.name);
223
+ }
224
+ children.forEach((child: any) => {
225
+ allValues.push(`${field.name}.${child.name}`);
226
+ });
227
+ });
228
+ }
229
+
230
+ const orders = field.value || [];
231
+ const newOrders = orders.reduce((newOrders: any[], item: any) => {
232
+ const { alias } = parseField(item.field);
233
+ if (!item.field || allValues.includes(alias)) {
234
+ newOrders.push(item);
235
+ }
236
+ return newOrders;
237
+ }, []);
238
+ field.setValue(newOrders);
239
+ };
@@ -0,0 +1,41 @@
1
+ import { SchemaComponentOptions, SchemaInitializerContext, SchemaInitializerProvider } from '@nocobase/client';
2
+ import React, { useContext } from 'react';
3
+ import { ChartInitializers, ChartV2Block, ChartV2BlockDesigner, ChartV2BlockInitializer } from './block';
4
+ import { useChartsTranslation } from './locale';
5
+ import { ChartRenderer, ChartRendererProvider, InternalLibrary } from './renderer';
6
+ import { ChartLibraryProvider } from './renderer/ChartLibrary';
7
+
8
+ const Chart: React.FC = (props) => {
9
+ const { t } = useChartsTranslation();
10
+ const initializers = useContext<any>(SchemaInitializerContext);
11
+ const children = initializers.BlockInitializers.items[0].children;
12
+ const has = children.some((initializer) => initializer.component === 'ChartV2BlockInitializer');
13
+ if (!has) {
14
+ children.push({
15
+ key: 'chart-v2',
16
+ type: 'item',
17
+ title: t('Chart'),
18
+ component: 'ChartV2BlockInitializer',
19
+ });
20
+ }
21
+ return (
22
+ <SchemaComponentOptions
23
+ components={{
24
+ ChartV2BlockInitializer,
25
+ ChartRenderer,
26
+ ChartV2BlockDesigner,
27
+ ChartV2Block,
28
+ ChartRendererProvider,
29
+ }}
30
+ >
31
+ <SchemaInitializerProvider initializers={{ ...initializers, ChartInitializers }}>
32
+ <ChartLibraryProvider name="Built-in" charts={InternalLibrary}>
33
+ {props.children}
34
+ </ChartLibraryProvider>
35
+ </SchemaInitializerProvider>
36
+ </SchemaComponentOptions>
37
+ );
38
+ };
39
+
40
+ export default Chart;
41
+ export { ChartLibraryProvider };
@@ -0,0 +1,23 @@
1
+ export default {
2
+ Edit: 'Edit',
3
+ Delete: 'Delete',
4
+ Cancel: 'Cancel',
5
+ Submit: 'Submit',
6
+ Actions: 'Actions',
7
+ Title: 'Title',
8
+ Enable: 'Enable',
9
+ 'SAML manager': 'SAML manager',
10
+ 'SAML Providers': 'SAML Providers',
11
+ 'Redirect url': 'Redirect url',
12
+ 'SP entity id': 'SP entity id',
13
+ 'Add provider': 'Add',
14
+ 'Edit provider': 'Edit',
15
+ 'Client id': 'Client id',
16
+ 'Entity id or issuer': 'Entity id or issuer',
17
+ 'Login Url': 'Login Url',
18
+ 'Public cert': 'Public cert',
19
+ 'Delete provider': 'Delete',
20
+ 'Are you sure you want to delete it?': 'Are you sure you want to delete it?',
21
+ 'Sign in button name, which will be displayed on the sign in page':
22
+ 'Sign in button name, which will be displayed on the sign in page',
23
+ };
@@ -0,0 +1,19 @@
1
+ import { i18n } from '@nocobase/client';
2
+ import { useTranslation } from 'react-i18next';
3
+ import zhCN from './zh-CN';
4
+
5
+ export const NAMESPACE = 'charts-v2';
6
+
7
+ i18n.addResources('zh-CN', NAMESPACE, zhCN);
8
+ // i18n.addResources('en-US', NAMESPACE, enUS);
9
+ // i18n.addResources('ja-JP', NAMESPACE, jaJP);
10
+ // i18n.addResources('ru-RU', NAMESPACE, ruRU);
11
+ // i18n.addResources('tr-TR', NAMESPACE, trTR);
12
+
13
+ export function lang(key: string) {
14
+ return i18n.t(key, { ns: NAMESPACE });
15
+ }
16
+
17
+ export function useChartsTranslation() {
18
+ return useTranslation(NAMESPACE);
19
+ }
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,23 @@
1
+ export default {
2
+ Edit: 'Editar',
3
+ Delete: 'Delete',
4
+ Cancel: 'Cancelar',
5
+ Submit: 'Enviar',
6
+ Actions: 'Ações',
7
+ Title: 'Titulo',
8
+ Enable: 'Ativo',
9
+ 'SAML manager': 'Gerenciador SAML',
10
+ 'SAML Providers': 'Provedores SAML',
11
+ 'Redirect url': 'URL de redirecionamento',
12
+ 'SP entity id': 'ID de entidade do provedor de serviço (SP)',
13
+ 'Add provider': 'Adicionar',
14
+ 'Edit provider': 'Editar',
15
+ 'Client id': 'ID do cliente',
16
+ 'Entity id or issuer': 'ID de entidade ou emissor',
17
+ 'Login Url': 'URL de login',
18
+ 'Public cert': 'Certificado público',
19
+ 'Delete provider': 'Excluir',
20
+ 'Are you sure you want to delete it?': 'Tem certeza de que deseja excluí-lo?',
21
+ 'Sign in button name, which will be displayed on the sign in page':
22
+ 'Nome do botão de login, que será exibido na página de login',
23
+ };
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,71 @@
1
+ export default {
2
+ Edit: '编辑',
3
+ Delete: '删除',
4
+ Cancel: '取消',
5
+ Submit: '提交',
6
+ Actions: '操作',
7
+ Title: '名称',
8
+ Enable: '启用',
9
+ Chart: '图表',
10
+ ChartV2: '图表V2',
11
+ Charts: '图表',
12
+ Configure: '配置',
13
+ Duplicate: '复制',
14
+ 'Configure chart': '配置图表',
15
+ Transform: '数据转换',
16
+ 'Chart type': '图表类型',
17
+ 'JSON config': 'JSON 配置',
18
+ Query: '查询',
19
+ Data: '数据',
20
+ 'Run query': '执行查询',
21
+ Measures: '度量',
22
+ Dimensions: '维度',
23
+ Filter: '过滤',
24
+ Sort: '排序',
25
+ Limit: '结果数量',
26
+ 'Enable cache': '启用缓存',
27
+ 'TTL (second)': '缓存时间 (秒)',
28
+ Field: '字段',
29
+ Aggregation: '聚合',
30
+ Alias: '别名',
31
+ Format: '格式',
32
+ 'The first 10 records of the query result:': '查询结果的前 10 条记录:',
33
+ 'Please run query to retrive data.': '请执行查询来获取数据。',
34
+ Type: '类型',
35
+ 'Add field': '添加字段',
36
+ 'Add chart': '添加图表',
37
+ xField: 'x轴字段',
38
+ yField: 'y轴字段',
39
+ seriesField: '分类字段',
40
+ angleField: '角度字段',
41
+ colorField: '颜色字段',
42
+ 'Line Chart': '折线图',
43
+ 'Area Chart': '面积图',
44
+ 'Column Chart': '柱状图',
45
+ 'Bar Chart': '条形图',
46
+ 'Pie Chart': '饼图',
47
+ 'Dual Axes Chart': '双轴图',
48
+ 'Scatter Chart': '散点图',
49
+ 'Gauge Chart': '仪表盘',
50
+ Statistic: '统计',
51
+ Currency: '货币',
52
+ Percent: '百分比',
53
+ Exponential: '科学记数法',
54
+ Abbreviation: '缩写',
55
+ 'Please configure and run query': '请配置并执行数据查询',
56
+ 'Please configure chart': '请配置图表',
57
+ 'Are you sure to cancel?': '确定要取消吗?',
58
+ 'You changes are not saved. If you click OK, your changes will be lost.':
59
+ '您的更改尚未保存。如果您点击“确定”,您的更改将丢失。',
60
+ 'Same properties set in the form above will be overwritten by this JSON config.':
61
+ '上面表单中设置的相同属性将被JSON配置覆盖。',
62
+ 'Built-in': '内置图表',
63
+ 'Config reference: ': '配置参考: ',
64
+ Table: '表格',
65
+ Sum: '求和',
66
+ Avg: '平均值',
67
+ Count: '计数',
68
+ Min: '最小值',
69
+ Max: '最大值',
70
+ 'Please select a chart type.': '请选择图表类型',
71
+ };
@@ -0,0 +1,178 @@
1
+ import { ISchema } from '@formily/react';
2
+ import React, { createContext, useContext } from 'react';
3
+ import { FieldOption } from '../hooks';
4
+ import { lang } from '../locale';
5
+ import { parseField } from '../utils';
6
+ import { QueryProps } from './ChartRendererProvider';
7
+
8
+ /**
9
+ * @params {usePropsFunc} useProps - Accept the information that the chart component needs to render,
10
+ * process it and return the props of the chart component.
11
+ */
12
+ export type usePropsFunc = (props: {
13
+ data: any[];
14
+ fieldProps: {
15
+ [field: string]: FieldOption & {
16
+ transformer: (val: any) => string;
17
+ };
18
+ };
19
+ general: any;
20
+ advanced: any;
21
+ }) => any;
22
+
23
+ export type ChartProps = {
24
+ name: string;
25
+ component: React.FC<any>;
26
+ schema?: ISchema;
27
+ useProps?: usePropsFunc;
28
+ // The init function is used to initialize the configuration of the chart component from the query configuration.
29
+ init?: (
30
+ fields: FieldOption[],
31
+ query: {
32
+ measures?: QueryProps['measures'];
33
+ dimensions?: QueryProps['dimensions'];
34
+ },
35
+ ) => {
36
+ general?: any;
37
+ advanced?: any;
38
+ };
39
+ reference?: {
40
+ title: string;
41
+ link: string;
42
+ };
43
+ };
44
+
45
+ export type Charts = {
46
+ [type: string]: ChartProps;
47
+ };
48
+
49
+ export type ChartLibraries = {
50
+ [library: string]: {
51
+ enabled: boolean;
52
+ charts: Charts;
53
+ };
54
+ };
55
+
56
+ export const ChartLibraryContext = createContext<ChartLibraries>({});
57
+
58
+ export const useCharts = (): Charts => {
59
+ const library = useContext(ChartLibraryContext);
60
+ return Object.values(library)
61
+ .filter((l) => l.enabled)
62
+ .reduce((charts, l) => ({ ...charts, ...l.charts }), {});
63
+ };
64
+
65
+ export const useChartTypes = (): {
66
+ label: string;
67
+ children: (ChartProps & {
68
+ key: string;
69
+ label: string;
70
+ value: string;
71
+ })[];
72
+ }[] => {
73
+ const library = useContext(ChartLibraryContext);
74
+ return Object.entries(library)
75
+ .filter(([_, l]) => l.enabled)
76
+ .reduce((charts, [name, l]) => {
77
+ const children = Object.entries(l.charts).map(([type, chart]) => ({
78
+ ...chart,
79
+ key: type,
80
+ label: chart.name,
81
+ value: type,
82
+ }));
83
+ return [
84
+ ...charts,
85
+ {
86
+ label: lang(name),
87
+ children,
88
+ },
89
+ ];
90
+ }, []);
91
+ };
92
+
93
+ export const useToggleChartLibrary = () => {
94
+ const ctx = useContext(ChartLibraryContext);
95
+ return {
96
+ toggle: (library: string) => {
97
+ ctx[library].enabled = !ctx[library].enabled;
98
+ },
99
+ };
100
+ };
101
+
102
+ export const ChartLibraryProvider: React.FC<{
103
+ name: string;
104
+ charts: Charts;
105
+ }> = (props) => {
106
+ const { children, charts, name } = props;
107
+ const ctx = useContext(ChartLibraryContext);
108
+ const library = {
109
+ ...ctx,
110
+ [name]: {
111
+ charts,
112
+ enabled: true,
113
+ },
114
+ };
115
+ return <ChartLibraryContext.Provider value={library}>{children}</ChartLibraryContext.Provider>;
116
+ };
117
+
118
+ export const infer = (
119
+ fields: FieldOption[],
120
+ {
121
+ measures,
122
+ dimensions,
123
+ }: {
124
+ measures?: QueryProps['measures'];
125
+ dimensions?: QueryProps['dimensions'];
126
+ },
127
+ ) => {
128
+ let xField: FieldOption;
129
+ let yField: FieldOption;
130
+ let seriesField: FieldOption;
131
+ let yFields: FieldOption[];
132
+ const getField = (fields: FieldOption[], selected: { field: string | string[]; alias?: string }) => {
133
+ if (selected.alias) {
134
+ return fields.find((f) => f.value === selected.alias);
135
+ }
136
+ const { alias } = parseField(selected.field);
137
+ return fields.find((f) => f.value === alias);
138
+ };
139
+ if (measures?.length) {
140
+ yField = getField(fields, measures[0]);
141
+ yFields = measures.map((m) => getField(fields, m));
142
+ }
143
+ if (dimensions) {
144
+ if (dimensions.length === 1) {
145
+ xField = getField(fields, dimensions[0]);
146
+ } else if (dimensions.length > 1) {
147
+ // If there is a time field, it is used as the x-axis field by default.
148
+ let xIndex: number;
149
+ dimensions.forEach((d, i) => {
150
+ const field = getField(fields, d);
151
+ if (['date', 'time', 'datetime'].includes(field?.type)) {
152
+ xField = field;
153
+ xIndex = i;
154
+ }
155
+ });
156
+ if (xIndex) {
157
+ // If there is a time field, the other field is used as the series field by default.
158
+ const index = xIndex === 0 ? 1 : 0;
159
+ seriesField = getField(fields, dimensions[index]);
160
+ } else {
161
+ xField = getField(fields, dimensions[0]);
162
+ seriesField = getField(fields, dimensions[1]);
163
+ }
164
+ }
165
+ }
166
+ return { xField, yField, seriesField, yFields };
167
+ };
168
+
169
+ export const commonInit: ChartProps['init'] = (fields, { measures, dimensions }) => {
170
+ const { xField, yField, seriesField } = infer(fields, { measures, dimensions });
171
+ return {
172
+ general: {
173
+ xField: xField?.value,
174
+ yField: yField?.value,
175
+ seriesField: seriesField?.value,
176
+ },
177
+ };
178
+ };