sea-chart 1.1.4 → 1.1.5

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 (36) hide show
  1. package/dist/assets/icons/funnel.svg +8 -0
  2. package/dist/components/font-settings/index.js +1 -2
  3. package/dist/constants/index.js +12 -1
  4. package/dist/constants/type-image.js +2 -1
  5. package/dist/constants/type.js +8 -2
  6. package/dist/locale/lang/de.js +13 -3
  7. package/dist/locale/lang/en.js +11 -1
  8. package/dist/locale/lang/es.js +11 -1
  9. package/dist/locale/lang/fr.js +13 -3
  10. package/dist/locale/lang/pt.js +11 -1
  11. package/dist/locale/lang/ru.js +11 -1
  12. package/dist/locale/lang/zh_CN.js +11 -1
  13. package/dist/model/funnel.js +50 -0
  14. package/dist/model/generic-model.js +1 -1
  15. package/dist/model/index.js +3 -1
  16. package/dist/settings/data-settings.js +5 -0
  17. package/dist/settings/funnel-settings/components/dnd-item/dnd-item.js +35 -0
  18. package/dist/settings/funnel-settings/components/dnd-item/dnd-item.module.css +22 -0
  19. package/dist/settings/funnel-settings/components/dnd-list.js +60 -0
  20. package/dist/settings/funnel-settings/components/funnel-label-setting.js +66 -0
  21. package/dist/settings/funnel-settings/components/funnel-layer-setting.js +72 -0
  22. package/dist/settings/funnel-settings/data-settings.js +69 -0
  23. package/dist/settings/funnel-settings/index.js +3 -0
  24. package/dist/settings/funnel-settings/style-settings.js +39 -0
  25. package/dist/settings/style-settings.js +5 -0
  26. package/dist/utils/chart-utils/base-utils.js +4 -0
  27. package/dist/utils/chart-utils/original-data-utils/basic-chart-calculator.js +1 -0
  28. package/dist/utils/chart-utils/original-data-utils/index.js +2 -1
  29. package/dist/utils/chart-utils/sql-statistics-utils.js +1 -0
  30. package/dist/utils/index.js +23 -0
  31. package/dist/utils/sql/chart-data-sql.js +1 -1
  32. package/dist/utils/sql/column-2-sql-column.js +2 -0
  33. package/dist/view/wrapper/bar.js +1 -1
  34. package/dist/view/wrapper/funnel.js +199 -0
  35. package/dist/view/wrapper/index.js +7 -0
  36. package/package.json +5 -1
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!-- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -->
3
+ <svg viewBox="0 0 1024 1024" version="1.1" style="enable-background:new 0 0 1024 1024;" xmlns="http://www.w3.org/2000/svg">
4
+ <style type="text/css">
5
+ .st0{fill:#999999;}
6
+ </style>
7
+ <path class="st0" d="M1024.5 96c0 17.673-14.327 32-32 32H32.5c-17.673 0-32-14.327-32-32 0-17.673 14.327-32 32-32h960c17.673 0 32 14.327 32 32z" />
8
+ <path class="st0" d="M0.5 95.958h32v32H0.5zM421.5 95.958h32v32h-32zM992.5 95.958h32v32h-32zM640 959l-256-127V576h256zM926.398 128L816.685 256s-40.846-25.455-94.852-12.802c-99.37 23.282-83.758 80.729-168.495 76.802C476.954 316.459 448.5 246.979 325.6 230.604c-74.018-9.861-117.286 25.393-117.286 25.393L98.602 128H0l384 448h256l384-448h-97.602z" /></svg>
@@ -1,6 +1,5 @@
1
1
  import React, { useRef, useState } from 'react';
2
- import { FormGroup, Input, Label } from 'reactstrap';
3
- import intl from '../../intl';
2
+ import { Input } from 'reactstrap';
4
3
  import { FontColorSettings, FontSizeSettings, FontWeightSettings } from '../../settings/widgets/font-settings';
5
4
  import TextHorizontalSettings from '../../settings/widgets/text-horizontal-settings';
6
5
  import { CHART_SUPPORT_FONT_WEIGHTS } from '../../constants';
@@ -6,7 +6,17 @@ import { CHART_TYPE, CHART_TYPE_SHOW, CHART_TYPES } from './type';
6
6
  import { CHART_TYPE_IMAGE } from './type-image';
7
7
  import { regions } from './regions';
8
8
  import { TABLE_DIMENSIONS } from './table';
9
+ export const SETTING_DEFAULT_FONT_SIZE = 12;
9
10
  export const DEFAULT_LANG = 'en';
11
+ export const FUNNEL_LABEL_FORMAT = {
12
+ NUMBER: 'number',
13
+ PERCENTAGE: 'percentage',
14
+ NUMBER_AND_PERCENTAGE: 'number_and_percentage'
15
+ };
16
+ export const FUNNEL_LABEL_POSITIONS = {
17
+ INSIDE: 'inside',
18
+ OUTSIDE: 'outside'
19
+ };
10
20
  export const TREND_TYPES = {
11
21
  UP: 'up',
12
22
  DOWN: 'down',
@@ -311,7 +321,8 @@ export const xAxisMap = {
311
321
  [CHART_TYPE.SCATTER]: 'x_axis_column_key',
312
322
  [CHART_TYPE.TABLE]: 'groupby_column_key',
313
323
  [CHART_TYPE.TREE_MAP]: 'groupby_column_key',
314
- [CHART_TYPE.TREND]: 'date_column_key'
324
+ [CHART_TYPE.TREND]: 'date_column_key',
325
+ [CHART_TYPE.FUNNEL]: 'x_axis_column_key'
315
326
  };
316
327
  export const groupAxisMap = {
317
328
  [CHART_TYPE.BAR_GROUP]: 'column_groupby_column_key',
@@ -28,5 +28,6 @@ export const CHART_TYPE_IMAGE = {
28
28
  [CHART_TYPE.TREND]: 'trend-chart.png',
29
29
  [CHART_TYPE.DASHBOARD]: 'dashboard-chart.png',
30
30
  [CHART_TYPE.TREE_MAP]: 'treemap.png',
31
- [CHART_TYPE.TABLE]: 'pivot-table.png'
31
+ [CHART_TYPE.TABLE]: 'pivot-table.png',
32
+ [CHART_TYPE.FUNNEL]: 'funnel.png'
32
33
  };
@@ -27,7 +27,8 @@ export const CHART_TYPE = {
27
27
  TREND: 'trend',
28
28
  DASHBOARD: 'dashboard',
29
29
  TREE_MAP: 'tree_map',
30
- TABLE: 'table'
30
+ TABLE: 'table',
31
+ FUNNEL: 'funnel'
31
32
  };
32
33
  export const CHART_TYPE_SHOW = {
33
34
  [CHART_TYPE.BAR]: 'Basic_histogram',
@@ -58,7 +59,8 @@ export const CHART_TYPE_SHOW = {
58
59
  [CHART_TYPE.TREND]: 'Trend',
59
60
  [CHART_TYPE.DASHBOARD]: 'Gauge',
60
61
  [CHART_TYPE.TREE_MAP]: 'Tree_map',
61
- [CHART_TYPE.TABLE]: 'Pivot_table'
62
+ [CHART_TYPE.TABLE]: 'Pivot_table',
63
+ [CHART_TYPE.FUNNEL]: 'Funnel'
62
64
  };
63
65
  export const CHART_TYPES = [{
64
66
  name: 'Histogram',
@@ -116,4 +118,8 @@ export const CHART_TYPES = [{
116
118
  name: 'Table',
117
119
  icon: 'dtable-logo',
118
120
  children: [CHART_TYPE.TABLE]
121
+ }, {
122
+ name: 'Funnel',
123
+ icon: 'funnel',
124
+ children: [CHART_TYPE.FUNNEL]
119
125
  }];
@@ -144,7 +144,7 @@ const de = {
144
144
  "Internal_server_error": "Interner Serverfehler",
145
145
  "Network_error": "Netzwerkfehler",
146
146
  "Permission_denied": "Zugriff verweigert",
147
- "Execution_time_of_the_query_exceeds_the_limit": "Execution time of the query exceeds the limit. Data cannot be loaded.",
147
+ "Execution_time_of_the_query_exceeds_the_limit": "Le temps d'exécution de la requête dépasse la limite. Les données ne peuvent pas être chargées.",
148
148
  "There_are_some_problems_with_the_filters": "Die Filterbedingungen sind abnormal. Bitte setzen sie die Filterbedingungen in den Ansichtseinstellungen zurück.",
149
149
  "Please_complete_the_chart_configuration_first": "Schließen Sie die Konfiguration des Diagramms ab.",
150
150
  "Not_used": "Nicht verwendet",
@@ -253,7 +253,17 @@ const de = {
253
253
  "Use_colors_in_single_select_solumn": "Verwenden Sie Farben in einer einzelnen Auswahlspalte",
254
254
  "Search_records": "Einträge suchen",
255
255
  "Please_select_a_grouping_column": "Please select grouping column",
256
- "View": "View",
257
- "All_charts": "All charts"
256
+ "View": "Sicht",
257
+ "Funnel": "Trichterdiagramm",
258
+ "All_charts": "Alle Diagramme",
259
+ "Show_legend": "Legende anzeigen",
260
+ "Funnel_layer": "Trichterschicht",
261
+ "funnel_accumulate_values": "Werte anhäufen",
262
+ "Show_funnel_layer_label": "Beschriftung der Trichterebene anzeigen",
263
+ "Inside": "Innen",
264
+ "Outside": "Draußen",
265
+ "Show_overall_rate": "Gesamtpreisbeschriftung anzeigen",
266
+ "Percentage": "Prozentsatz",
267
+ "Number_and_percentage": "Anzahl und Prozentsatz"
258
268
  };
259
269
  export default de;
@@ -254,6 +254,16 @@ const en = {
254
254
  "Search_records": "Search records",
255
255
  "Please_select_a_grouping_column": "Please select grouping column",
256
256
  "View": "View",
257
- "All_charts": "All charts"
257
+ "Funnel": "Funnel",
258
+ "All_charts": "All charts",
259
+ "Show_legend": "Show legend",
260
+ "Funnel_layer": "Funnel layer",
261
+ "funnel_accumulate_values": "Funnel accumulate values",
262
+ "Show_funnel_layer_label": "Show funnel layer label",
263
+ "Inside": "Inside",
264
+ "Outside": "Outside",
265
+ "Show_overall_rate": "Show overall rate",
266
+ "Percentage": "Percentage",
267
+ "Number_and_percentage": "Number and percentage"
258
268
  };
259
269
  export default en;
@@ -254,6 +254,16 @@ const es = {
254
254
  "Search_records": "Search records",
255
255
  "Please_select_a_grouping_column": "Please select grouping column",
256
256
  "View": "View",
257
- "All_charts": "All charts"
257
+ "Funnel": "Funnel",
258
+ "All_charts": "All charts",
259
+ "Show_legend": "Show legend",
260
+ "Funnel_layer": "Funnel layer",
261
+ "funnel_accumulate_values": "Funnel accumulate values",
262
+ "Show_funnel_layer_label": "Show funnel layer label",
263
+ "Inside": "Inside",
264
+ "Outside": "Outside",
265
+ "Show_overall_rate": "Show overall rate",
266
+ "Percentage": "Percentage",
267
+ "Number_and_percentage": "Number and percentage"
258
268
  };
259
269
  export default es;
@@ -144,7 +144,7 @@ const fr = {
144
144
  "Internal_server_error": "Erreur interne du serveur",
145
145
  "Network_error": "Erreur réseau",
146
146
  "Permission_denied": "Permission refusée",
147
- "Execution_time_of_the_query_exceeds_the_limit": "Execution time of the query exceeds the limit. Data cannot be loaded.",
147
+ "Execution_time_of_the_query_exceeds_the_limit": "Le temps d'exécution de la requête dépasse la limite. Les données ne peuvent pas être chargées.",
148
148
  "There_are_some_problems_with_the_filters": "Les conditions de filtrage sont anormales. Veuillez réinitialiser les conditions de filtrage dans les paramètres de vue.",
149
149
  "Please_complete_the_chart_configuration_first": "Compléter la configuration du diagramme",
150
150
  "Not_used": "Ne pas utilisé",
@@ -253,7 +253,17 @@ const fr = {
253
253
  "Use_colors_in_single_select_solumn": "Utiliser les couleurs dans une colonne de sélection unique",
254
254
  "Search_records": "Rechercher des enregistrements",
255
255
  "Please_select_a_grouping_column": "Please select grouping column",
256
- "View": "View",
257
- "All_charts": "All charts"
256
+ "View": "Voir",
257
+ "Funnel": "Entonnoir",
258
+ "All_charts": "Tous les graphiques",
259
+ "Show_legend": "Afficher la légende",
260
+ "Funnel_layer": "Couche d'entonnoir",
261
+ "funnel_accumulate_values": "accumuler des valeurs",
262
+ "Show_funnel_layer_label": "Afficher l'étiquette du calque",
263
+ "Inside": "À l'intérieur",
264
+ "Outside": "Dehors",
265
+ "Show_overall_rate": "Afficher le libellé du tarif global",
266
+ "Percentage": "Couche d'entonnoir",
267
+ "Number_and_percentage": "Nombre et pourcentage"
258
268
  };
259
269
  export default fr;
@@ -254,6 +254,16 @@ const pt = {
254
254
  "Search_records": "Search records",
255
255
  "Please_select_a_grouping_column": "Please select grouping column",
256
256
  "View": "View",
257
- "All_charts": "All charts"
257
+ "Funnel": "Funnel",
258
+ "All_charts": "All charts",
259
+ "Show_legend": "Show legend",
260
+ "Funnel_layer": "Funnel layer",
261
+ "funnel_accumulate_values": "Funnel accumulate values",
262
+ "Show_funnel_layer_label": "Show funnel layer label",
263
+ "Inside": "Inside",
264
+ "Outside": "Outside",
265
+ "Show_overall_rate": "Show overall rate",
266
+ "Percentage": "Percentage",
267
+ "Number_and_percentage": "Number and percentage"
258
268
  };
259
269
  export default pt;
@@ -254,6 +254,16 @@ const ru = {
254
254
  "Search_records": "Search records",
255
255
  "Please_select_a_grouping_column": "Please select grouping column",
256
256
  "View": "View",
257
- "All_charts": "All charts"
257
+ "Funnel": "Funnel",
258
+ "All_charts": "All charts",
259
+ "Show_legend": "Show legend",
260
+ "Funnel_layer": "Funnel layer",
261
+ "funnel_accumulate_values": "Funnel accumulate values",
262
+ "Show_funnel_layer_label": "Show funnel layer label",
263
+ "Inside": "Inside",
264
+ "Outside": "Outside",
265
+ "Show_overall_rate": "Show overall rate",
266
+ "Percentage": "Percentage",
267
+ "Number_and_percentage": "Number and percentage"
258
268
  };
259
269
  export default ru;
@@ -254,6 +254,16 @@ const zh_CN = {
254
254
  "Search_records": "搜索记录",
255
255
  "Please_select_a_grouping_column": "请选择一个分组列",
256
256
  "View": "视图",
257
- "All_charts": "所有图表"
257
+ "Funnel": "漏斗图",
258
+ "All_charts": "所有图表",
259
+ "Show_legend": "显示图例",
260
+ "Funnel_layer": "漏斗层",
261
+ "funnel_accumulate_values": "每层累计当前层与所有下层数值",
262
+ "Show_funnel_layer_label": "显示漏斗层标签",
263
+ "Inside": "在图表内",
264
+ "Outside": "在图表外",
265
+ "Show_overall_rate": "显示总转化率标签",
266
+ "Percentage": "百分比",
267
+ "Number_and_percentage": "数值和百分比"
258
268
  };
259
269
  export default zh_CN;
@@ -0,0 +1,50 @@
1
+ import { CellType } from 'dtable-utils';
2
+ import { cloneDeep } from 'lodash-es';
3
+ import { CHART_SUMMARY_TYPE, CHART_TYPE, FUNNEL_LABEL_FORMAT, FUNNEL_LABEL_POSITIONS } from '../constants';
4
+ import BaseModel from './base-model';
5
+ class Funnel extends BaseModel {
6
+ constructor(options, tables) {
7
+ var _options$x_axis_optio;
8
+ options = options._options;
9
+ super({
10
+ ...options,
11
+ type: CHART_TYPE.FUNNEL
12
+ });
13
+
14
+ // init a default value here so settings won't need to check existence of the config
15
+
16
+ // data-settings
17
+ // needs to be single select
18
+ this.x_axis_column_key = options.x_axis_column_key;
19
+
20
+ // if no x_axis_option_list but x_axis_column_key is single select type, then use it's option list
21
+ if ((_options$x_axis_optio = options.x_axis_option_list) === null || _options$x_axis_optio === void 0 ? void 0 : _options$x_axis_optio.length) {
22
+ this.x_axis_option_list = options.x_axis_option_list;
23
+ } else if (options.table_id && options.x_axis_column_key) {
24
+ const table = tables.find(table => table._id === options.table_id);
25
+ const column = table.columns.find(column => column.key === options.x_axis_column_key);
26
+ if (column.type === CellType.SINGLE_SELECT) {
27
+ this.x_axis_option_list = cloneDeep(column.data.options);
28
+ } else {
29
+ this.x_axis_option_list = [];
30
+ }
31
+ } else {
32
+ this.x_axis_option_list = [];
33
+ }
34
+
35
+ // y-axis
36
+ this.y_axis_summary_column_key = options.y_axis_summary_column_key;
37
+ this.y_axis_summary_type = options.y_axis_summary_type || CHART_SUMMARY_TYPE.COUNT;
38
+ this.y_axis_summary_method = options.y_axis_summary_method || CHART_SUMMARY_TYPE.SUM;
39
+ this.funnel_accumulate_values = typeof options.funnel_accumulate_values !== 'boolean' ? true : options.funnel_accumulate_values;
40
+
41
+ // style settings
42
+ this.funnel_show_legend = typeof options.funnel_show_legend !== 'boolean' ? true : options.funnel_show_legend;
43
+ this.funnel_show_labels = typeof options.funnel_show_labels !== 'boolean' ? true : options.funnel_show_labels;
44
+ this.funnel_label_position = options.funnel_label_position || FUNNEL_LABEL_POSITIONS.OUTSIDE;
45
+ this.funnel_label_format = options.funnel_label_format || FUNNEL_LABEL_FORMAT.NUMBER;
46
+ this.funnel_label_font_size = options.funnel_label_font_size || 12;
47
+ this.funnel_show_overall_rate = typeof options.funnel_show_overall_rate !== 'boolean' ? true : options.funnel_show_overall_rate;
48
+ }
49
+ }
50
+ export default Funnel;
@@ -6,7 +6,7 @@ export default class GenericModel extends BaseModel {
6
6
  constructor(object) {
7
7
  const options = object || {};
8
8
  super(options);
9
-
9
+ this._options = object;
10
10
  // x data
11
11
  this.x_axis_column_key = getChartConfigValueByKey(GENERIC_KEY.X_AXIS_COLUMN_KEY, options);
12
12
  this.x_axis_include_empty_cells = getChartConfigValueByKey(GENERIC_KEY.X_AXIS_INCLUDE_EMPTY_CELLS, options);
@@ -31,6 +31,7 @@ import BasicNumberCard from './basic-number-card';
31
31
  import Trend from './trend';
32
32
  import Dashboard from './dashboard';
33
33
  import Table from './table';
34
+ import Funnel from './funnel';
34
35
  const CHART_MAP = {
35
36
  [CHART_TYPE.BAR]: Bar,
36
37
  [CHART_TYPE.BAR_GROUP]: BarGroup,
@@ -60,6 +61,7 @@ const CHART_MAP = {
60
61
  [CHART_TYPE.TREND]: Trend,
61
62
  [CHART_TYPE.DASHBOARD]: Dashboard,
62
63
  [CHART_TYPE.TREE_MAP]: TreeMap,
63
- [CHART_TYPE.TABLE]: Table
64
+ [CHART_TYPE.TABLE]: Table,
65
+ [CHART_TYPE.FUNNEL]: Funnel
64
66
  };
65
67
  export { ChartModel, GenericModel, CHART_MAP, User };
@@ -15,6 +15,7 @@ import { MapDataSettings } from './map-settings';
15
15
  import { HeatMapDataSettings } from './heat-map-settings';
16
16
  import { MirrorDataSettings } from './mirror-settings';
17
17
  import { TrendDataSettings } from './trend-settings';
18
+ import { FunnelDataSettings } from './funnel-settings';
18
19
  const DataSettings = props => {
19
20
  const [refreshToggle, setRefreshToggle] = useState(false);
20
21
  const cacheRef = useRef(props);
@@ -122,6 +123,10 @@ const DataSettings = props => {
122
123
  {
123
124
  return /*#__PURE__*/React.createElement(TrendDataSettings, props);
124
125
  }
126
+ case CHART_TYPE.FUNNEL:
127
+ {
128
+ return /*#__PURE__*/React.createElement(FunnelDataSettings, props);
129
+ }
125
130
  default:
126
131
  {
127
132
  return null;
@@ -0,0 +1,35 @@
1
+ import { useSortable } from '@dnd-kit/sortable';
2
+ import { CSS } from '@dnd-kit/utilities';
3
+ import React from 'react';
4
+ import styles from './dnd-item.module.css';
5
+ export default function DndItem(_ref) {
6
+ let {
7
+ option
8
+ } = _ref;
9
+ const {
10
+ setNodeRef,
11
+ attributes,
12
+ listeners,
13
+ transform,
14
+ transition
15
+ } = useSortable({
16
+ id: option.id,
17
+ transition: {
18
+ duration: 500,
19
+ easing: 'cubic-bezier(0.25, 1, 0.5, 1)'
20
+ }
21
+ });
22
+ const dndStyles = {
23
+ transform: CSS.Transform.toString(transform),
24
+ transition
25
+ };
26
+ return /*#__PURE__*/React.createElement("div", Object.assign({
27
+ className: styles['dnd-item'],
28
+ ref: setNodeRef,
29
+ style: dndStyles
30
+ }, attributes, listeners), /*#__PURE__*/React.createElement("i", {
31
+ className: "dtable-font dtable-icon-drag pr-2"
32
+ }), /*#__PURE__*/React.createElement("span", {
33
+ className: styles['option-name']
34
+ }, option.name));
35
+ }
@@ -0,0 +1,22 @@
1
+
2
+ .dnd-item {
3
+ width: 100%;
4
+ border-radius: 6px;
5
+ height: 30px;
6
+ background-color: #f2f3f5;
7
+ margin-bottom: 10px;
8
+ display: flex;
9
+ align-items: center;
10
+ }
11
+
12
+ .option-name {
13
+ width: calc(100% - 30px);
14
+ white-space: nowrap;
15
+ overflow: hidden;
16
+ text-overflow: ellipsis;
17
+ }
18
+
19
+ :global(.dnd-list-container .dtable-font.dtable-icon-drag) {
20
+ font-size: 12px;
21
+ padding-left: 10px;
22
+ }
@@ -0,0 +1,60 @@
1
+ import React, { useCallback } from 'react';
2
+ import { DndContext } from '@dnd-kit/core';
3
+ import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
4
+ import { restrictToParentElement } from '@dnd-kit/modifiers';
5
+ import DndItem from './dnd-item/dnd-item';
6
+ const getIndex = (active, over, array) => {
7
+ let activeIndex = 0;
8
+ let overIndex = 0;
9
+ try {
10
+ array.forEach((item, index) => {
11
+ if (active.id === item.id) {
12
+ activeIndex = index;
13
+ }
14
+ if (over.id === item.id) {
15
+ overIndex = index;
16
+ }
17
+ });
18
+ } catch (error) {
19
+ // reset
20
+ overIndex = activeIndex;
21
+ }
22
+ return {
23
+ activeIndex,
24
+ overIndex
25
+ };
26
+ };
27
+ export default function DndList(_ref) {
28
+ let {
29
+ list,
30
+ onListChange
31
+ } = _ref;
32
+ const handleDragEnd = useCallback(_ref2 => {
33
+ let {
34
+ active,
35
+ over
36
+ } = _ref2;
37
+ if (over && active.id !== over.id) {
38
+ const {
39
+ activeIndex,
40
+ overIndex
41
+ } = getIndex(active, over, list);
42
+ const newList = [...list];
43
+ const newSortedList = arrayMove(newList, activeIndex, overIndex);
44
+ onListChange(newSortedList);
45
+ }
46
+ }, [list, onListChange]);
47
+ if (!(list === null || list === void 0 ? void 0 : list.length)) return null;
48
+ return /*#__PURE__*/React.createElement(DndContext, {
49
+ onDragEnd: handleDragEnd,
50
+ modifiers: [restrictToParentElement]
51
+ }, /*#__PURE__*/React.createElement(SortableContext, {
52
+ items: list.map(item => item.id),
53
+ strategy: verticalListSortingStrategy
54
+ }, /*#__PURE__*/React.createElement("div", {
55
+ className: "dnd-list-container"
56
+ }, list.map(item => /*#__PURE__*/React.createElement(DndItem, {
57
+ option: item,
58
+ key: item.id
59
+ })))));
60
+ }
@@ -0,0 +1,66 @@
1
+ import _CollapsibleSettingLayout from "dtable-ui-component/lib/CollapsibleSettingLayout";
2
+ import _DTableSelect from "dtable-ui-component/lib/DTableSelect";
3
+ import _DTableSwitch from "dtable-ui-component/lib/DTableSwitch";
4
+ import React, { useCallback } from 'react';
5
+ import { FormGroup, Label } from 'reactstrap';
6
+ import intl from '../../../intl';
7
+ import { FontSizeSettings } from '../../widgets/font-settings';
8
+ import { FUNNEL_LABEL_FORMAT, FUNNEL_LABEL_POSITIONS, SETTING_DEFAULT_FONT_SIZE } from '../../../constants';
9
+ const labelPositionOptions = [{
10
+ value: FUNNEL_LABEL_POSITIONS.INSIDE,
11
+ label: /*#__PURE__*/React.createElement("span", null, intl.get('Inside'))
12
+ }, {
13
+ value: FUNNEL_LABEL_POSITIONS.OUTSIDE,
14
+ label: /*#__PURE__*/React.createElement("span", null, intl.get('Outside'))
15
+ }];
16
+ const labelFormatOptions = [{
17
+ value: FUNNEL_LABEL_FORMAT.NUMBER,
18
+ label: /*#__PURE__*/React.createElement("span", null, intl.get('Number'))
19
+ }, {
20
+ value: FUNNEL_LABEL_FORMAT.PERCENTAGE,
21
+ label: /*#__PURE__*/React.createElement("span", null, intl.get('Percentage'))
22
+ }, {
23
+ value: FUNNEL_LABEL_FORMAT.NUMBER_AND_PERCENTAGE,
24
+ label: /*#__PURE__*/React.createElement("span", null, intl.get('Number_and_percentage'))
25
+ }];
26
+ export default function FunnelLabelSetting(_ref) {
27
+ let {
28
+ onChange,
29
+ funnelLabelPosition,
30
+ funnelLabelFormat,
31
+ funnelLabelFontSize,
32
+ funnelShowOverallRate,
33
+ funnelShowLabels
34
+ } = _ref;
35
+ const handleChange = useCallback(function handleChange(value, key) {
36
+ onChange && onChange({
37
+ [key]: value
38
+ });
39
+ }, [onChange]);
40
+ const selectedLabelPosition = labelPositionOptions.find(option => option.value === funnelLabelPosition);
41
+ const selectedLabelFormat = labelFormatOptions.find(option => option.value === funnelLabelFormat);
42
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_CollapsibleSettingLayout, {
43
+ title: intl.get('Label')
44
+ }, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(_DTableSwitch, {
45
+ checked: funnelShowLabels,
46
+ placeholder: intl.get('Show_funnel_layer_label'),
47
+ onChange: e => handleChange(e.target.checked, 'funnel_show_labels')
48
+ })), funnelShowLabels && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(Label, null, intl.get('Label_position')), /*#__PURE__*/React.createElement(_DTableSelect, {
49
+ value: selectedLabelPosition || labelPositionOptions[1],
50
+ onChange: e => handleChange(e.value, 'funnel_label_position'),
51
+ options: labelPositionOptions
52
+ })), /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(Label, null, intl.get('Label_format')), /*#__PURE__*/React.createElement(_DTableSelect, {
53
+ value: selectedLabelFormat || labelFormatOptions[0],
54
+ onChange: e => handleChange(e.value, 'funnel_label_format'),
55
+ options: labelFormatOptions
56
+ })), /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(FontSizeSettings, {
57
+ className: 'mt-3',
58
+ fontSize: funnelLabelFontSize,
59
+ defaultFontSize: SETTING_DEFAULT_FONT_SIZE,
60
+ modifyFontSize: e => handleChange(e, 'funnel_label_font_size')
61
+ })), /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(_DTableSwitch, {
62
+ checked: funnelShowOverallRate,
63
+ placeholder: intl.get('Show_overall_rate'),
64
+ onChange: e => handleChange(e.target.checked, 'funnel_show_overall_rate')
65
+ })))));
66
+ }
@@ -0,0 +1,72 @@
1
+ import _DTableSelect from "dtable-ui-component/lib/DTableSelect";
2
+ import { COLUMNS_ICON_CONFIG } from 'dtable-utils';
3
+ import React, { useCallback, useMemo } from 'react';
4
+ import { FormGroup, Label } from 'reactstrap';
5
+ import { cloneDeep } from 'lodash-es';
6
+ import intl from '../../../intl';
7
+ import DndList from './dnd-list';
8
+ export default function FunnelLayerSetting(_ref) {
9
+ let {
10
+ selectedColumnKey,
11
+ x_axis_option_list,
12
+ columnList,
13
+ onChange
14
+ } = _ref;
15
+ const options = useMemo(() => columnList.map(column => {
16
+ return {
17
+ value: column,
18
+ label: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
19
+ className: "header-icon"
20
+ }, /*#__PURE__*/React.createElement("i", {
21
+ className: COLUMNS_ICON_CONFIG[column.type]
22
+ })), /*#__PURE__*/React.createElement("span", {
23
+ className: 'select-option-name'
24
+ }, column.name))
25
+ };
26
+ }), [columnList]);
27
+
28
+ // selectedColumnKey maybe inherited from another chart and so maybe it's not a singleSelect column
29
+ // columnList here is a list of singleSelect columns
30
+ const selectedColumn = columnList.find(c => c.key === selectedColumnKey);
31
+
32
+ // if selectedColumn is a singleSelect column, then use it's option and option's list
33
+ let selectedOption,
34
+ singleSelectOptionsList = [];
35
+ if (selectedColumnKey && selectedColumn) {
36
+ selectedOption = {
37
+ value: selectedColumn,
38
+ label: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
39
+ className: "header-icon"
40
+ }, /*#__PURE__*/React.createElement("i", {
41
+ className: COLUMNS_ICON_CONFIG[selectedColumn.type]
42
+ })), /*#__PURE__*/React.createElement("span", {
43
+ className: 'select-option-name'
44
+ }, selectedColumn.name))
45
+ };
46
+ singleSelectOptionsList = x_axis_option_list;
47
+ }
48
+ const handleXAxisColumnKeyChange = useCallback(option => {
49
+ const {
50
+ value
51
+ } = option;
52
+ const newList = cloneDeep(value.data.options);
53
+ onChange({
54
+ 'x_axis_column_key': value.key,
55
+ 'x_axis_option_list': newList
56
+ });
57
+ }, [onChange]);
58
+ const handleListOrderChange = useCallback(newList => {
59
+ onChange({
60
+ 'x_axis_option_list': newList
61
+ });
62
+ }, [onChange]);
63
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(Label, null, intl.get('Funnel_layer')), /*#__PURE__*/React.createElement(_DTableSelect, {
64
+ value: selectedOption,
65
+ options: options,
66
+ onChange: handleXAxisColumnKeyChange
67
+ })), /*#__PURE__*/React.createElement(DndList, {
68
+ selectedColumnKey: selectedColumnKey,
69
+ list: singleSelectOptionsList,
70
+ onListChange: handleListOrderChange
71
+ }));
72
+ }
@@ -0,0 +1,69 @@
1
+ import _DTableSwitch from "dtable-ui-component/lib/DTableSwitch";
2
+ import React, { useCallback } from 'react';
3
+ import { CellType } from 'dtable-utils';
4
+ import CommonDataSettings from '../widgets/common-data-settings';
5
+ import Divider from '../widgets/divider';
6
+ import BasicSummary from '../widgets/basic-summary';
7
+ import intl from '../../intl';
8
+ import { CHART_SUMMARY_TYPES, CHART_TYPE } from '../../constants';
9
+ import FunnelLayerSetting from './components/funnel-layer-setting';
10
+ export default function DataSetting(_ref) {
11
+ let {
12
+ dataSources,
13
+ chart,
14
+ tables,
15
+ onChange
16
+ } = _ref;
17
+ const {
18
+ x_axis_column_key,
19
+ table_id,
20
+ type,
21
+ x_axis_option_list,
22
+ funnel_accumulate_values
23
+ } = chart.config;
24
+ let singleSelectColumns;
25
+ if (table_id) {
26
+ const selectedTable = tables.find(table => table._id === table_id);
27
+ // only use single select columns
28
+ singleSelectColumns = selectedTable.columns.filter(column => column.type === CellType.SINGLE_SELECT);
29
+ } else {
30
+ singleSelectColumns = [];
31
+ }
32
+ const handleFunnelAccumulateValuesChange = useCallback(function (e) {
33
+ onChange && onChange({
34
+ funnel_accumulate_values: e.target.checked
35
+ });
36
+ }, [onChange]);
37
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CommonDataSettings, {
38
+ dataSources: dataSources,
39
+ chart: chart,
40
+ tables: tables,
41
+ onChange: onChange
42
+ }), /*#__PURE__*/React.createElement(FunnelLayerSetting, {
43
+ selectedColumnKey: x_axis_column_key,
44
+ x_axis_option_list: x_axis_option_list,
45
+ columnList: singleSelectColumns,
46
+ onChange: onChange
47
+ }), /*#__PURE__*/React.createElement(Divider, {
48
+ className: "mt-4"
49
+ }), /*#__PURE__*/React.createElement(BasicSummary, {
50
+ className: "selected-y-axis",
51
+ label: intl.get('Y_axis'),
52
+ showSummaryTypes: type === CHART_TYPE.BAR_CUSTOM ? false : true,
53
+ summaryTypeKey: 'y_axis_summary_type',
54
+ summaryMethodKey: 'y_axis_summary_method',
55
+ summaryColumnKey: 'y_axis_summary_column_key',
56
+ chart: chart,
57
+ selectedTableId: table_id,
58
+ tables: tables,
59
+ supportColumnTypes: [CellType.NUMBER],
60
+ summaryTypeOptions: CHART_SUMMARY_TYPES,
61
+ onChange: onChange
62
+ }), /*#__PURE__*/React.createElement(Divider, {
63
+ className: "mt-4"
64
+ }), /*#__PURE__*/React.createElement(_DTableSwitch, {
65
+ checked: funnel_accumulate_values,
66
+ placeholder: intl.get('funnel_accumulate_values'),
67
+ onChange: handleFunnelAccumulateValuesChange
68
+ }));
69
+ }
@@ -0,0 +1,3 @@
1
+ import FunnelDataSettings from './data-settings';
2
+ import FunnelStyleSettings from './style-settings';
3
+ export { FunnelDataSettings, FunnelStyleSettings };
@@ -0,0 +1,39 @@
1
+ import _DTableSwitch from "dtable-ui-component/lib/DTableSwitch";
2
+ import React, { useCallback } from 'react';
3
+ import intl from '../../intl';
4
+ import Divider from '../widgets/divider';
5
+ import FunnelLabelSetting from './components/funnel-label-setting';
6
+ export default function StyleSetting(_ref) {
7
+ let {
8
+ chart,
9
+ onChange
10
+ } = _ref;
11
+ const {
12
+ funnel_show_legend,
13
+ funnel_show_labels,
14
+ funnel_label_position,
15
+ funnel_label_format,
16
+ funnel_label_font_size,
17
+ funnel_show_overall_rate
18
+ } = chart.config;
19
+ const handleShowLegendChange = useCallback(function (event) {
20
+ const value = event.target.checked;
21
+ onChange && onChange({
22
+ funnel_show_legend: value
23
+ });
24
+ }, [onChange]);
25
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_DTableSwitch, {
26
+ checked: funnel_show_legend,
27
+ placeholder: intl.get('Show_legend'),
28
+ onChange: handleShowLegendChange
29
+ }), /*#__PURE__*/React.createElement(Divider, {
30
+ className: "mt-4"
31
+ }), /*#__PURE__*/React.createElement(FunnelLabelSetting, {
32
+ onChange: onChange,
33
+ funnelShowLabels: funnel_show_labels,
34
+ funnelLabelPosition: funnel_label_position,
35
+ funnelLabelFormat: funnel_label_format,
36
+ funnelLabelFontSize: funnel_label_font_size,
37
+ funnelShowOverallRate: funnel_show_overall_rate
38
+ }));
39
+ }
@@ -13,6 +13,7 @@ import StatisticTitleSetting from './widgets/title-settings';
13
13
  import { CompletenessStyleSettings } from './completeness-settings';
14
14
  import { MapStyleSettings } from './map-settings';
15
15
  import { TrendStyleSettings } from './trend-settings';
16
+ import { FunnelStyleSettings } from './funnel-settings';
16
17
  const StyleSettings = _ref => {
17
18
  let {
18
19
  chart,
@@ -131,6 +132,10 @@ const StyleSettings = _ref => {
131
132
  {
132
133
  return /*#__PURE__*/React.createElement(TrendStyleSettings, props);
133
134
  }
135
+ case CHART_TYPE.FUNNEL:
136
+ {
137
+ return /*#__PURE__*/React.createElement(FunnelStyleSettings, props);
138
+ }
134
139
  default:
135
140
  {
136
141
  return null;
@@ -89,6 +89,10 @@ BaseUtils.isValidExistChart = (tables, chart) => {
89
89
  }
90
90
  if (!groupByColumnKey) return false;
91
91
  if (!getTableColumnByKey(table, groupByColumnKey)) return false;
92
+ if (type === CHART_TYPE.FUNNEL) {
93
+ const column = getColumnByKey(config.x_axis_column_key, table.columns);
94
+ if (column.type !== CellType.SINGLE_SELECT) return false;
95
+ }
92
96
  if (type === CHART_TYPE.COMBINATION) {
93
97
  const isExist = _BaseUtils.isValidCombinationChart(config, table);
94
98
  if (!isExist) return false;
@@ -87,6 +87,7 @@ async function calculateBasicChart(chart, value, _ref) {
87
87
  BaseUtils.sortCharts(results, groupbyColumn, 'name'); // sortby statistic label
88
88
  }
89
89
  results.forEach(item => {
90
+ item.group_name = item.name;
90
91
  item.name = getFormattedLabel(groupbyColumn, item.name, value.collaborators);
91
92
  });
92
93
  if ([CHART_TYPE.HEAT_MAP].includes(chart.type)) {
@@ -32,7 +32,8 @@ const calculatorMap = {
32
32
  [CHART_TYPE.MIRROR]: MirrorCalculator,
33
33
  [CHART_TYPE.BASIC_NUMBER_CARD]: CardCalculator,
34
34
  [CHART_TYPE.TREND]: TrendCalculator,
35
- [CHART_TYPE.DASHBOARD]: DashboardCalculator
35
+ [CHART_TYPE.DASHBOARD]: DashboardCalculator,
36
+ [CHART_TYPE.FUNNEL]: BasicChartCalculator
36
37
  };
37
38
  class OriginalDataUtils {}
38
39
  _OriginalDataUtils = OriginalDataUtils;
@@ -1474,6 +1474,7 @@ SQLStatisticsUtils.sqlResult2JavaScript = (chart, sqlRows, chartSQLMap, columnMa
1474
1474
  case CHART_TYPE.LINE:
1475
1475
  case CHART_TYPE.HORIZONTAL_BAR:
1476
1476
  case CHART_TYPE.AREA:
1477
+ case CHART_TYPE.FUNNEL:
1477
1478
  {
1478
1479
  return {
1479
1480
  result: _SQLStatisticsUtils.basicChartSQLResult2JavaScript(chart, sqlRows, chartSQLMap, columnMap, tables)
@@ -1,3 +1,4 @@
1
+ import { cloneDeep } from 'lodash-es';
1
2
  import { GENERIC_KEY_2_SIMILAR_KEYS, GEOLOCATION_GRANULARITY, MAP_LEVEL, MUNICIPALITIES, regions } from '../constants';
2
3
  import intl from '../intl';
3
4
  import { SERVER_ERROR_DISPLAY_KEY, SERVER_ERROR_MSG } from '../constants/error';
@@ -13,6 +14,28 @@ export { getDateColumnFormat, isCheckboxColumn, getColumnByKey, isStatisticMapCo
13
14
  export { generatorKey } from './key-generator';
14
15
  export { translateCalendar } from './date-translate';
15
16
  export { isFunction } from './common-utils';
17
+
18
+ /**
19
+ * return a deep cloned list sorted by the given list
20
+ *
21
+ * used in Funnel chart to sort single select options
22
+ * @param {Array} data
23
+ * @param {Array} x_axis_option_list
24
+ * @returns {Array} sortedData
25
+ */
26
+ export const getSortedDataByGivenOrder = (data, x_axis_option_list) => {
27
+ const idIndexMap = {};
28
+ x_axis_option_list.forEach((item, index) => {
29
+ idIndexMap[item.id] = index;
30
+ });
31
+ let sortedData = [];
32
+ data.forEach(item => {
33
+ const index = idIndexMap[item.group_name];
34
+ sortedData[index] = cloneDeep(item);
35
+ });
36
+ sortedData = sortedData.filter(Boolean);
37
+ return sortedData;
38
+ };
16
39
  export function getMapCanvasStyle(container, ratio) {
17
40
  let cWidth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 200;
18
41
  let cHeight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 200;
@@ -824,7 +824,7 @@ class ChartDataSQL {
824
824
  error: this.error
825
825
  };
826
826
  }
827
- if ([CHART_TYPE.BAR, CHART_TYPE.LINE, CHART_TYPE.HORIZONTAL_BAR, CHART_TYPE.AREA].includes(this.chart_type)) {
827
+ if ([CHART_TYPE.BAR, CHART_TYPE.LINE, CHART_TYPE.HORIZONTAL_BAR, CHART_TYPE.AREA, CHART_TYPE.FUNNEL].includes(this.chart_type)) {
828
828
  const sql = this._basic_statistic_2_sql();
829
829
  return {
830
830
  sql,
@@ -790,6 +790,7 @@ const chartColumn2SqlColumn = (chartElement, table) => {
790
790
  case CHART_TYPE.HORIZONTAL_BAR:
791
791
  case CHART_TYPE.AREA:
792
792
  case CHART_TYPE.COMPARE_BAR:
793
+ case CHART_TYPE.FUNNEL:
793
794
  {
794
795
  return basicChartStatisticColumn2sqlColumn(chart, table);
795
796
  }
@@ -867,6 +868,7 @@ export const getDatabaseGroupName = (statItem, selectedTable) => {
867
868
  case CHART_TYPE.HORIZONTAL_BAR:
868
869
  case CHART_TYPE.HORIZONTAL_GROUP_BAR:
869
870
  case CHART_TYPE.STACKED_HORIZONTAL_BAR:
871
+ case CHART_TYPE.FUNNEL:
870
872
  {
871
873
  let groupby_column_key, groupby_date_granularity, groupby_geolocation_granularity;
872
874
  const {
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { debounce } from 'lodash-es';
4
4
  import classNames from 'classnames';
5
- import { CHART_SUMMARY_TYPE, CHART_SUMMARY_SHOW, TYPE_COLOR_USING, CHART_STYLE_COLORS, CHART_THEME_COLOR } from '../../constants';
5
+ import { TYPE_COLOR_USING, CHART_STYLE_COLORS, CHART_THEME_COLOR } from '../../constants';
6
6
  import { BaseUtils, isFunction } from '../../utils';
7
7
  import { getLabelColor, getConvertedColorRules } from '../../utils/color-utils';
8
8
  import intl from '../../intl';
@@ -0,0 +1,199 @@
1
+ import React from 'react';
2
+ import { DataSet } from '@antv/data-set';
3
+ import classNames from 'classnames';
4
+ import { BaseUtils, getSortedDataByGivenOrder } from '../../utils';
5
+ import { CHART_THEME_COLOR, FUNNEL_LABEL_FORMAT, FUNNEL_LABEL_POSITIONS } from '../../constants';
6
+ import ChartComponent from './chart-component';
7
+ class Funnel extends ChartComponent {
8
+ constructor(props) {
9
+ super(props);
10
+ this.createChart = () => {
11
+ const appendPadding = [17, 0, 0, 0]; // used to display value on the top
12
+ this.initChart(this.container, {
13
+ appendPadding
14
+ });
15
+ this.chart.on('interval:click', e => {
16
+ this.props.toggleRecords(e.data.data);
17
+ });
18
+ };
19
+ this.drawChart = () => {
20
+ const {
21
+ result: data,
22
+ chart
23
+ } = this.props;
24
+ const {
25
+ x_axis_option_list,
26
+ funnel_accumulate_values
27
+ } = chart.config;
28
+ if (!x_axis_option_list.length) return;
29
+ const sortedData = getSortedDataByGivenOrder(data, x_axis_option_list);
30
+ sortedData.total = 0;
31
+ sortedData.forEach(item => {
32
+ sortedData.total += item.value;
33
+ });
34
+ if (funnel_accumulate_values) {
35
+ for (let i = sortedData.length - 2; i >= 0; i--) {
36
+ sortedData[i].value += sortedData[i + 1].value;
37
+ }
38
+ }
39
+ const {
40
+ DataView
41
+ } = DataSet;
42
+ const dv = new DataView().source(sortedData);
43
+ dv.transform({
44
+ type: 'map',
45
+ callback(row) {
46
+ row.percent = sortedData.total === 0 ? 0 : (row.value / sortedData.total * 100).toFixed(2);
47
+ return row;
48
+ }
49
+ });
50
+ const dvData = dv.rows;
51
+ this.loadData(dvData);
52
+ this.draw(dvData);
53
+ this.chart.render();
54
+ };
55
+ this.draw = sortedData => {
56
+ const {
57
+ chart,
58
+ summaryColumn,
59
+ tables,
60
+ globalTheme
61
+ } = this.props;
62
+ const {
63
+ table_id,
64
+ y_axis_summary_type,
65
+ y_axis_column_key,
66
+ y_axis_summary_column_key,
67
+ y_axis_summary_method,
68
+ funnel_show_legend,
69
+ funnel_label_position,
70
+ funnel_label_font_size,
71
+ funnel_label_format,
72
+ funnel_show_labels,
73
+ funnel_show_overall_rate
74
+ } = chart.config;
75
+ const theme = CHART_THEME_COLOR[globalTheme];
76
+ const title = this.getTitle(tables, table_id, y_axis_summary_type, y_axis_column_key || y_axis_summary_column_key);
77
+ const isInside = funnel_label_position === FUNNEL_LABEL_POSITIONS.INSIDE;
78
+ let labelStyle;
79
+ if (isInside) {
80
+ labelStyle = {
81
+ position: 'middle',
82
+ offsetY: -(funnel_label_font_size + 5),
83
+ style: {
84
+ fontSize: funnel_label_font_size,
85
+ fill: '#fff'
86
+ },
87
+ labelLine: true
88
+ };
89
+ } else {
90
+ labelStyle = {
91
+ position: 'left',
92
+ offset: -30,
93
+ style: {
94
+ fontSize: funnel_label_font_size,
95
+ fill: theme.labelColor
96
+ },
97
+ labelLine: true
98
+ };
99
+ }
100
+ const contentFormatterMap = {
101
+ [FUNNEL_LABEL_FORMAT.NUMBER]: obj => obj.value,
102
+ [FUNNEL_LABEL_FORMAT.PERCENTAGE]: obj => obj.percent + '%',
103
+ [FUNNEL_LABEL_FORMAT.NUMBER_AND_PERCENTAGE]: obj => "".concat(obj.value, " (").concat(obj.percent, "%)")
104
+ };
105
+ this.chart.axis(false);
106
+ this.chart.coordinate('rect').transpose().scale(1, -1);
107
+ this.chart.interval().adjust('symmetric').position('name*value').shape('funnel').color('name', ['#0050B3', '#1890FF', '#40A9FF', '#69C0FF', '#BAE7FF']).label('name*value*percent', name => {
108
+ if (name.length > 35) {
109
+ name = name.substring(0, 35) + '...';
110
+ }
111
+ return {
112
+ content: funnel_show_labels ? "".concat(name) : ''
113
+ };
114
+ }, labelStyle).tooltip('name*value', (name, value) => {
115
+ return {
116
+ title,
117
+ value: BaseUtils.getSummaryValueDisplayString(summaryColumn, value, y_axis_summary_method),
118
+ name
119
+ };
120
+ }).animate({
121
+ appear: {
122
+ animation: 'fade-in'
123
+ },
124
+ update: {
125
+ annotation: 'fade-in'
126
+ }
127
+ }).state({
128
+ active: {
129
+ style: {
130
+ stroke: null
131
+ }
132
+ }
133
+ });
134
+ this.chart.on('beforepaint', () => {
135
+ this.chart.annotation().clear(true);
136
+ if (!funnel_show_labels) return;
137
+ sortedData.forEach(obj => {
138
+ this.chart.annotation().text({
139
+ top: true,
140
+ offsetY: isInside ? 12 : 0,
141
+ position: [obj.name, 'center'],
142
+ content: contentFormatterMap[funnel_label_format](obj),
143
+ style: {
144
+ fontSize: funnel_label_font_size,
145
+ stroke: null,
146
+ fill: '#fff',
147
+ textAlign: 'center'
148
+ }
149
+ });
150
+ });
151
+ if (funnel_show_overall_rate) {
152
+ this.chart.annotation().text({
153
+ top: true,
154
+ position: ['50.5%', '-4%'],
155
+ content: sortedData[sortedData.length - 1].percent + '%',
156
+ style: {
157
+ fontSize: funnel_label_font_size,
158
+ stroke: null,
159
+ fill: theme.labelColor,
160
+ textAlign: 'center'
161
+ }
162
+ });
163
+ }
164
+ });
165
+
166
+ // console.log(funnel_show_overall_rate);
167
+
168
+ if (funnel_show_legend) {
169
+ this.setLegend('name', theme, 'top-right');
170
+ } else {
171
+ this.chart.legend(false);
172
+ }
173
+ this.setToolTip();
174
+ this.chart.interaction('element-active');
175
+ };
176
+ this.chart = null;
177
+ }
178
+ componentDidMount() {
179
+ this.createChart();
180
+ this.drawChart();
181
+ }
182
+ componentDidUpdate(prevProps) {
183
+ if (BaseUtils.shouldChartComponentUpdate(prevProps, this.props)) {
184
+ this.chart && this.chart.destroy();
185
+ this.createChart();
186
+ this.drawChart();
187
+ }
188
+ }
189
+ componentWillUnmount() {
190
+ this.chart && this.chart.destroy();
191
+ }
192
+ render() {
193
+ return /*#__PURE__*/React.createElement("div", {
194
+ className: classNames('sea-chart-container'),
195
+ ref: ref => this.container = ref
196
+ });
197
+ }
198
+ }
199
+ export default Funnel;
@@ -25,6 +25,7 @@ import WorldMap from './world-map';
25
25
  import HeatMap from './heat-map';
26
26
  import Mirror from './mirror';
27
27
  import Trend from './trend';
28
+ import Funnel from './funnel';
28
29
  const Wrapper = _ref => {
29
30
  let {
30
31
  dtableStoreValue,
@@ -276,6 +277,12 @@ const Wrapper = _ref => {
276
277
  canvasStyle: canvasStyle
277
278
  }));
278
279
  }
280
+ case CHART_TYPE.FUNNEL:
281
+ {
282
+ return /*#__PURE__*/React.createElement(Funnel, Object.assign({}, baseProps, {
283
+ canvasStyle: canvasStyle
284
+ }));
285
+ }
279
286
  default:
280
287
  {
281
288
  return null;
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "sea-chart",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "main": "./dist/index.js",
5
5
  "dependencies": {
6
6
  "@antv/data-set": "0.11.8",
7
7
  "@antv/g2": "4.1.46",
8
+ "@dnd-kit/core": "^6.1.0",
9
+ "@dnd-kit/modifiers": "^7.0.0",
10
+ "@dnd-kit/sortable": "^8.0.0",
11
+ "@dnd-kit/utilities": "^3.2.2",
8
12
  "@seafile/seafile-calendar": "^0.0.24",
9
13
  "classnames": "^2.3.2",
10
14
  "dayjs": "1.10.7",