@cdc/chart 4.24.5 → 4.24.7
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/dist/cdcchart.js +39128 -35959
- package/examples/feature/annotations/index.json +542 -0
- package/examples/xaxis.json +493 -0
- package/index.html +5 -4
- package/package.json +5 -4
- package/src/CdcChart.tsx +104 -64
- package/src/_stories/Chart.stories.tsx +18 -171
- package/src/_stories/ChartAnnotation.stories.tsx +32 -0
- package/src/_stories/_mock/annotation_category_mock.json +473 -0
- package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
- package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
- package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
- package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
- package/src/_stories/_mock/lollipop.json +171 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
- package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
- package/src/components/Annotations/components/AnnotationList.tsx +42 -0
- package/src/components/Annotations/components/findNearestDatum.ts +138 -0
- package/src/components/Annotations/components/helpers/index.tsx +46 -0
- package/src/components/Annotations/index.tsx +13 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
- package/src/components/AreaChart/components/AreaChart.jsx +1 -1
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +44 -42
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
- package/src/components/BarChart/components/BarChart.Vertical.tsx +50 -22
- package/src/components/BarChart/helpers/index.ts +102 -0
- package/src/components/EditorPanel/EditorPanel.tsx +232 -98
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +117 -6
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/panels.scss +4 -0
- package/src/components/EditorPanel/editor-panel.scss +19 -0
- package/src/components/EditorPanel/useEditorPermissions.js +19 -2
- package/src/components/Legend/Legend.Component.tsx +7 -8
- package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
- package/src/components/Legend/helpers/index.ts +5 -0
- package/src/components/LineChart/LineChartProps.ts +3 -0
- package/src/components/LineChart/helpers.ts +21 -7
- package/src/components/LineChart/index.tsx +7 -7
- package/src/components/LinearChart.jsx +179 -136
- package/src/components/PairedBarChart.jsx +9 -9
- package/src/components/PieChart/PieChart.tsx +4 -4
- package/src/components/Sankey/index.tsx +73 -20
- package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
- package/src/components/ZoomBrush.tsx +90 -44
- package/src/data/initial-state.js +14 -6
- package/src/helpers/handleChartTabbing.ts +8 -0
- package/src/helpers/isConvertLineToBarGraph.ts +4 -0
- package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
- package/src/hooks/useColorScale.ts +1 -1
- package/src/hooks/useMinMax.ts +8 -3
- package/src/hooks/useScales.ts +25 -3
- package/src/hooks/useTooltip.tsx +58 -11
- package/src/scss/main.scss +21 -14
- package/src/types/ChartConfig.ts +50 -3
- package/src/types/ChartContext.ts +9 -0
- package/tests-examples/helpers/testZeroValue.test.ts +30 -0
- package/LICENSE +0 -201
- package/src/helpers/filterData.ts +0 -18
- package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
- /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
- /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "chart",
|
|
3
|
+
"visualizationType": "Line",
|
|
4
|
+
"xAxis": {
|
|
5
|
+
"dataKey": "STATE"
|
|
6
|
+
},
|
|
7
|
+
"series": [
|
|
8
|
+
{
|
|
9
|
+
"dataKey": "Rate"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"filters": [
|
|
13
|
+
{
|
|
14
|
+
"values": ["Home", "School", "Vehicle", "Work"],
|
|
15
|
+
"active": "Home",
|
|
16
|
+
"order": "asc",
|
|
17
|
+
"columnName": "Location",
|
|
18
|
+
"setByQueryParameter": "location"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"data": [
|
|
22
|
+
{
|
|
23
|
+
"STATE": "AL",
|
|
24
|
+
"Rate": "10",
|
|
25
|
+
"Location": "Home",
|
|
26
|
+
"URL": "https://www.cdc.gov/",
|
|
27
|
+
"Intensity Level": "Low",
|
|
28
|
+
"Trajectory": "Increasing"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"STATE": "AK",
|
|
32
|
+
"Rate": "12",
|
|
33
|
+
"Location": "Home",
|
|
34
|
+
"URL": "https://www.cdc.gov/",
|
|
35
|
+
"Intensity Level": "Low",
|
|
36
|
+
"Trajectory": "Increasing"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"STATE": "FL",
|
|
40
|
+
"Rate": "10",
|
|
41
|
+
"Location": "Work",
|
|
42
|
+
"URL": "https://www.cdc.gov/",
|
|
43
|
+
"Intensity Level": "Low",
|
|
44
|
+
"Trajectory": "Increasing"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"STATE": "GA",
|
|
48
|
+
"Rate": "12",
|
|
49
|
+
"Location": "Work",
|
|
50
|
+
"URL": "https://www.cdc.gov/",
|
|
51
|
+
"Intensity Level": "Low",
|
|
52
|
+
"Trajectory": "Increasing"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"STATE": "AL",
|
|
56
|
+
"Rate": "10",
|
|
57
|
+
"Location": "Work",
|
|
58
|
+
"URL": "https://www.cdc.gov/",
|
|
59
|
+
"Intensity Level": "Low",
|
|
60
|
+
"Trajectory": "Increasing"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"STATE": "AK",
|
|
64
|
+
"Rate": "12",
|
|
65
|
+
"Location": "Work",
|
|
66
|
+
"URL": "https://www.cdc.gov/",
|
|
67
|
+
"Intensity Level": "Low",
|
|
68
|
+
"Trajectory": "Increasing"
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"dataFileName": "hex-data.csv",
|
|
72
|
+
"dataFileSourceType": "file",
|
|
73
|
+
"formattedData": [
|
|
74
|
+
{
|
|
75
|
+
"STATE": "AL",
|
|
76
|
+
"Rate": "10",
|
|
77
|
+
"Location": "Home",
|
|
78
|
+
"URL": "https://www.cdc.gov/",
|
|
79
|
+
"Intensity Level": "Low",
|
|
80
|
+
"Trajectory": "Increasing"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"STATE": "AK",
|
|
84
|
+
"Rate": "12",
|
|
85
|
+
"Location": "Home",
|
|
86
|
+
"URL": "https://www.cdc.gov/",
|
|
87
|
+
"Intensity Level": "Low",
|
|
88
|
+
"Trajectory": "Increasing"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"STATE": "AL",
|
|
92
|
+
"Rate": "10",
|
|
93
|
+
"Location": "Work",
|
|
94
|
+
"URL": "https://www.cdc.gov/",
|
|
95
|
+
"Intensity Level": "Low",
|
|
96
|
+
"Trajectory": "Increasing"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"STATE": "AK",
|
|
100
|
+
"Rate": "12",
|
|
101
|
+
"Location": "Work",
|
|
102
|
+
"URL": "https://www.cdc.gov/",
|
|
103
|
+
"Intensity Level": "Low",
|
|
104
|
+
"Trajectory": "Increasing"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"STATE": "FL",
|
|
108
|
+
"Rate": "10",
|
|
109
|
+
"Location": "Work",
|
|
110
|
+
"URL": "https://www.cdc.gov/",
|
|
111
|
+
"Intensity Level": "Low",
|
|
112
|
+
"Trajectory": "Increasing"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"STATE": "GA",
|
|
116
|
+
"Rate": "12",
|
|
117
|
+
"Location": "Work",
|
|
118
|
+
"URL": "https://www.cdc.gov/",
|
|
119
|
+
"Intensity Level": "Low",
|
|
120
|
+
"Trajectory": "Increasing"
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"dataDescription": {
|
|
124
|
+
"horizontal": false,
|
|
125
|
+
"series": false
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "chart",
|
|
3
|
+
"title": "Lollipop Style Horizontal Bar Chart - Number of Spills Occurring in the Home",
|
|
4
|
+
"showTitle": true,
|
|
5
|
+
"showDownloadMediaButton": false,
|
|
6
|
+
"theme": "theme-blue",
|
|
7
|
+
"animate": true,
|
|
8
|
+
"fontSize": "medium",
|
|
9
|
+
"lineDatapointStyle": "hover",
|
|
10
|
+
"barHasBorder": "false",
|
|
11
|
+
"isLollipopChart": true,
|
|
12
|
+
"lollipopShape": "circle",
|
|
13
|
+
"lollipopColorStyle": "two-tone",
|
|
14
|
+
"visualizationSubType": "horizontal",
|
|
15
|
+
"barStyle": "",
|
|
16
|
+
"roundingStyle": "standard",
|
|
17
|
+
"tipRounding": "top",
|
|
18
|
+
"isResponsiveTicks": false,
|
|
19
|
+
"general": { "showDownloadButton": false },
|
|
20
|
+
"padding": { "left": 5, "right": 5 },
|
|
21
|
+
"yAxis": {
|
|
22
|
+
"hideAxis": true,
|
|
23
|
+
"displayNumbersOnBar": true,
|
|
24
|
+
"hideLabel": false,
|
|
25
|
+
"hideTicks": false,
|
|
26
|
+
"size": "13",
|
|
27
|
+
"gridLines": false,
|
|
28
|
+
"enablePadding": false,
|
|
29
|
+
"min": "",
|
|
30
|
+
"max": "",
|
|
31
|
+
"labelColor": "#333",
|
|
32
|
+
"tickLabelColor": "#333",
|
|
33
|
+
"tickColor": "#333",
|
|
34
|
+
"rightHideAxis": true,
|
|
35
|
+
"rightAxisSize": 50,
|
|
36
|
+
"rightLabel": "",
|
|
37
|
+
"rightLabelOffsetSize": 0,
|
|
38
|
+
"rightAxisLabelColor": "#333",
|
|
39
|
+
"rightAxisTickLabelColor": "#333",
|
|
40
|
+
"rightAxisTickColor": "#333",
|
|
41
|
+
"numTicks": "9",
|
|
42
|
+
"axisPadding": 0,
|
|
43
|
+
"tickRotation": 0,
|
|
44
|
+
"anchors": [],
|
|
45
|
+
"type": "chart",
|
|
46
|
+
"title": "Lollipop Style Horizontal Bar Chart",
|
|
47
|
+
"theme": "theme-blue",
|
|
48
|
+
"fontSize": "medium",
|
|
49
|
+
"lineDatapointStyle": "hover",
|
|
50
|
+
"barHasBorder": "false",
|
|
51
|
+
"isLollipopChart": false,
|
|
52
|
+
"lollipopShape": "circle",
|
|
53
|
+
"lollipopColorStyle": "two-tone",
|
|
54
|
+
"visualizationSubType": "horizontal",
|
|
55
|
+
"padding": { "left": 5, "right": 5 },
|
|
56
|
+
"yAxis": { "size": 50, "gridLines": false },
|
|
57
|
+
"barThickness": 0.35,
|
|
58
|
+
"height": 260,
|
|
59
|
+
"xAxis": { "type": "categorical", "size": 75, "tickRotation": 0, "dataKey": "Vehicle" },
|
|
60
|
+
"table": { "label": "Data Table", "expanded": true, "show": true },
|
|
61
|
+
"legend": { "behavior": "isolate", "position": "right" },
|
|
62
|
+
"exclusions": { "active": false, "keys": [] },
|
|
63
|
+
"palette": "qualitative-bold",
|
|
64
|
+
"labels": false,
|
|
65
|
+
"dataFormat": {},
|
|
66
|
+
"confidenceKeys": {},
|
|
67
|
+
"data": [
|
|
68
|
+
{ "Group": "Combined Total of Group A", "Vehicle": "100", "Home": "120", "Work": "140", "Office": "120" },
|
|
69
|
+
{ "Group": "Combined Total of Group B", "Vehicle": "150", "Home": "140", "Work": "100", "Office": "90" },
|
|
70
|
+
{ "Group": "Combined Total of Group C", "Vehicle": "90", "Home": "90", "Work": "80", "Office": "80" },
|
|
71
|
+
{ "Group": "Combined Total of Group D", "Vehicle": "70", "Home": "60", "Work": "50", "Office": "70" }
|
|
72
|
+
],
|
|
73
|
+
"dataFileName": "CSV_Source_Example_for_Horizontal_Bar_viz-cdcwp1619811744363.csv",
|
|
74
|
+
"dataFileSourceType": "file",
|
|
75
|
+
"visualizationType": "Bar",
|
|
76
|
+
"runtime": {
|
|
77
|
+
"seriesLabels": { "Vehicle": "Vehicle" },
|
|
78
|
+
"seriesLabelsAll": ["Vehicle"],
|
|
79
|
+
"originalXAxis": { "type": "categorical", "size": 75, "tickRotation": 0, "dataKey": "Vehicle" },
|
|
80
|
+
"seriesKeys": ["Vehicle"],
|
|
81
|
+
"xAxis": { "size": 50, "gridLines": false },
|
|
82
|
+
"yAxis": { "type": "categorical", "size": 75, "tickRotation": 0, "dataKey": "Vehicle" },
|
|
83
|
+
"horizontal": true,
|
|
84
|
+
"uniqueId": 1651765968212,
|
|
85
|
+
"editorErrorMessage": ""
|
|
86
|
+
},
|
|
87
|
+
"description": "Subtext can be added here for options like citing data sources or insight into reading the bar chart.",
|
|
88
|
+
"series": [{ "dataKey": "Vehicle", "type": "Bar" }],
|
|
89
|
+
"barHeight": 25,
|
|
90
|
+
"barPadding": 40,
|
|
91
|
+
"labelPlacement": "Below Bar",
|
|
92
|
+
"label": "Number of Accidents"
|
|
93
|
+
},
|
|
94
|
+
"boxplot": [],
|
|
95
|
+
"topAxis": { "hasLine": false },
|
|
96
|
+
"isLegendValue": false,
|
|
97
|
+
"barThickness": 0.35,
|
|
98
|
+
"barHeight": 6,
|
|
99
|
+
"barSpace": 15,
|
|
100
|
+
"heights": { "vertical": 300, "horizontal": 170.39999999999998 },
|
|
101
|
+
"xAxis": {
|
|
102
|
+
"anchors": [],
|
|
103
|
+
"type": "categorical",
|
|
104
|
+
"showTargetLabel": true,
|
|
105
|
+
"targetLabel": "Target",
|
|
106
|
+
"hideAxis": true,
|
|
107
|
+
"hideLabel": true,
|
|
108
|
+
"hideTicks": true,
|
|
109
|
+
"size": "16",
|
|
110
|
+
"tickRotation": 0,
|
|
111
|
+
"min": "",
|
|
112
|
+
"max": "160",
|
|
113
|
+
"labelColor": "#333",
|
|
114
|
+
"tickLabelColor": "#333",
|
|
115
|
+
"tickColor": "#333",
|
|
116
|
+
"numTicks": "",
|
|
117
|
+
"labelOffset": 65,
|
|
118
|
+
"axisPadding": 0,
|
|
119
|
+
"target": 0,
|
|
120
|
+
"maxTickRotation": 0,
|
|
121
|
+
"dataKey": "Group"
|
|
122
|
+
},
|
|
123
|
+
"table": { "label": "Data Table", "expanded": false, "limitHeight": false, "height": "", "caption": "", "showDownloadUrl": false, "showDataTableLink": true, "indexLabel": "Group", "download": false, "showVertical": true, "show": true },
|
|
124
|
+
"orientation": "horizontal",
|
|
125
|
+
"color": "pinkpurple",
|
|
126
|
+
"columns": {},
|
|
127
|
+
"legend": {
|
|
128
|
+
"behavior": "isolate",
|
|
129
|
+
"singleRow": false,
|
|
130
|
+
"colorCode": "",
|
|
131
|
+
"reverseLabelOrder": false,
|
|
132
|
+
"description": "",
|
|
133
|
+
"dynamicLegend": false,
|
|
134
|
+
"dynamicLegendDefaultText": "Show All",
|
|
135
|
+
"dynamicLegendItemLimit": 5,
|
|
136
|
+
"dynamicLegendItemLimitMessage": "Dynamic Legend Item Limit Hit.",
|
|
137
|
+
"dynamicLegendChartMessage": "Select Options from the Legend",
|
|
138
|
+
"position": "right",
|
|
139
|
+
"hide": true,
|
|
140
|
+
"label": "Accident Location"
|
|
141
|
+
},
|
|
142
|
+
"exclusions": { "active": false, "keys": [] },
|
|
143
|
+
"palette": "qualitative-bold",
|
|
144
|
+
"isPaletteReversed": false,
|
|
145
|
+
"twoColor": { "palette": "monochrome-1", "isPaletteReversed": false },
|
|
146
|
+
"labels": false,
|
|
147
|
+
"dataFormat": { "commas": false, "prefix": "", "suffix": "", "abbreviated": false, "bottomSuffix": "", "bottomPrefix": "", "bottomAbbreviated": false },
|
|
148
|
+
"confidenceKeys": {},
|
|
149
|
+
"visual": { "border": true, "accent": true, "background": true, "verticalHoverLine": false, "horizontalHoverLine": false },
|
|
150
|
+
"useLogScale": false,
|
|
151
|
+
"filterBehavior": "Filter Change",
|
|
152
|
+
"highlightedBarValues": [],
|
|
153
|
+
"series": [{ "dataKey": "Home", "type": "Bar", "tooltip": true }],
|
|
154
|
+
"tooltips": { "opacity": 90 },
|
|
155
|
+
"height": 212,
|
|
156
|
+
"data": [
|
|
157
|
+
{ "Group": "Combined Total of Group A", "Vehicle": "100", "Home": "120", "Work": "140", "Office": "120" },
|
|
158
|
+
{ "Group": "Combined Total of Group B", "Vehicle": "150", "Home": "140", "Work": "100", "Office": "90" },
|
|
159
|
+
{ "Group": "Combined Total of Group C", "Vehicle": "90", "Home": "90", "Work": "80", "Office": "80" },
|
|
160
|
+
{ "Group": "Combined Total of Group D", "Vehicle": "70", "Home": "60", "Work": "50", "Office": "70" }
|
|
161
|
+
],
|
|
162
|
+
"dataFileName": "CSV_Source_Example_for_Horizontal_Bar_viz-cdcwp1619811744363.csv",
|
|
163
|
+
"dataFileSourceType": "file",
|
|
164
|
+
"visualizationType": "Bar",
|
|
165
|
+
"description": "Subtext can be added here for options like citing data sources or insight into reading the bar chart.",
|
|
166
|
+
"barPadding": 47,
|
|
167
|
+
"filters": [],
|
|
168
|
+
"lollipopSize": "medium",
|
|
169
|
+
"validated": 4.23,
|
|
170
|
+
"dynamicMarginTop": 0
|
|
171
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.cdc-open-viz-module .annotation__desktop-label {
|
|
2
|
+
display: none;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.cdc-open-viz-module {
|
|
6
|
+
.annotation__mobile-label-circle {
|
|
7
|
+
stroke-width: 2;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.annotation__has-dropdown-number {
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
border-radius: 50%;
|
|
15
|
+
padding: 8px;
|
|
16
|
+
width: 16px;
|
|
17
|
+
height: 16px;
|
|
18
|
+
border: 2px solid #666;
|
|
19
|
+
text-align: center;
|
|
20
|
+
font-size: 14px;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@container content (min-width: 700px) {
|
|
25
|
+
.cdc-open-viz-module .annotation__mobile-label {
|
|
26
|
+
display: none;
|
|
27
|
+
}
|
|
28
|
+
.cdc-open-viz-module .annotation__desktop-label {
|
|
29
|
+
display: block;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import ConfigContext from '../../../ConfigContext'
|
|
3
|
+
import DOMPurify from 'dompurify'
|
|
4
|
+
|
|
5
|
+
// helpers
|
|
6
|
+
import { findNearestDatum } from './findNearestDatum'
|
|
7
|
+
|
|
8
|
+
// prettier-ignore
|
|
9
|
+
import {
|
|
10
|
+
applyBandScaleOffset,
|
|
11
|
+
handleConnectionHorizontalType,
|
|
12
|
+
handleConnectionVerticalType,
|
|
13
|
+
handleMobileXPosition,
|
|
14
|
+
handleMobileYPosition,
|
|
15
|
+
handleTextX,
|
|
16
|
+
handleTextY
|
|
17
|
+
} from './helpers'
|
|
18
|
+
|
|
19
|
+
import useColorScale from '../../../hooks/useColorScale'
|
|
20
|
+
|
|
21
|
+
// visx
|
|
22
|
+
import { HtmlLabel, CircleSubject, EditableAnnotation, Connector, Annotation as VisxAnnotation } from '@visx/annotation'
|
|
23
|
+
import { Drag } from '@visx/drag'
|
|
24
|
+
import { MarkerArrow } from '@visx/marker'
|
|
25
|
+
import { LinePath } from '@visx/shape'
|
|
26
|
+
import { fontSizes } from '@cdc/core/helpers/cove/fontSettings'
|
|
27
|
+
|
|
28
|
+
// styles
|
|
29
|
+
import './AnnotationDraggable.styles.css'
|
|
30
|
+
|
|
31
|
+
const Annotations = ({ xScale, yScale, xScaleAnnotation, xMax, svgRef, onDragStateChange }) => {
|
|
32
|
+
// prettier-ignore
|
|
33
|
+
const {
|
|
34
|
+
config,
|
|
35
|
+
currentViewport,
|
|
36
|
+
dimensions,
|
|
37
|
+
isDraggingAnnotation,
|
|
38
|
+
isEditor,
|
|
39
|
+
isLegendBottom,
|
|
40
|
+
updateConfig
|
|
41
|
+
} = useContext(ConfigContext)
|
|
42
|
+
|
|
43
|
+
// destructure config items here...
|
|
44
|
+
const { annotations } = config
|
|
45
|
+
const [height] = dimensions
|
|
46
|
+
const { colorScale } = useColorScale()
|
|
47
|
+
const AnnotationComponent = isEditor ? EditableAnnotation : VisxAnnotation
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
annotations &&
|
|
51
|
+
annotations.map((annotation, index) => {
|
|
52
|
+
const text = annotation.text || ''
|
|
53
|
+
|
|
54
|
+
const annotationX = xScaleAnnotation(annotation.x)
|
|
55
|
+
|
|
56
|
+
// sanitize the text for setting dangerouslySetInnerHTML
|
|
57
|
+
const sanitizedData = () => ({
|
|
58
|
+
__html: DOMPurify.sanitize(text)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<AnnotationComponent
|
|
63
|
+
width={200}
|
|
64
|
+
height={height}
|
|
65
|
+
dx={annotation.dx} // label position
|
|
66
|
+
dy={annotation.dy} // label postion
|
|
67
|
+
x={annotationX}
|
|
68
|
+
y={annotation.y}
|
|
69
|
+
canEditLabel={annotation.edit.label || false}
|
|
70
|
+
canEditSubject={(annotation.edit.subject && annotation.connectionType !== 'none') || false}
|
|
71
|
+
onDragStart={() => onDragStateChange(true)}
|
|
72
|
+
onDragEnd={props => {
|
|
73
|
+
onDragStateChange(false)
|
|
74
|
+
|
|
75
|
+
let updatedAnnotations = [...annotations]
|
|
76
|
+
|
|
77
|
+
if (annotation.x === xScaleAnnotation.invert(props.x) && annotation.y === props.y) {
|
|
78
|
+
updatedAnnotations[index] = { ...updatedAnnotations[index], dx: props.dx, dy: props.dy }
|
|
79
|
+
} else {
|
|
80
|
+
if (annotation.snapToNearestPoint) {
|
|
81
|
+
let nearestDatum = findNearestDatum(
|
|
82
|
+
{
|
|
83
|
+
data: config.data,
|
|
84
|
+
xScale,
|
|
85
|
+
yScale,
|
|
86
|
+
config,
|
|
87
|
+
xMax: xMax - config.yAxis.size / 2,
|
|
88
|
+
annotationSeriesKey: annotation.seriesKey
|
|
89
|
+
},
|
|
90
|
+
props.x
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
updatedAnnotations[index] = { ...updatedAnnotations[index], x: xScaleAnnotation.invert(xScale(nearestDatum.x)), y: yScale(nearestDatum.y) }
|
|
94
|
+
} else {
|
|
95
|
+
updatedAnnotations[index] = { ...updatedAnnotations[index], x: xScaleAnnotation.invert(props.x), y: props.y }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
updateConfig({
|
|
100
|
+
...config,
|
|
101
|
+
annotations: updatedAnnotations
|
|
102
|
+
})
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<HtmlLabel className='annotation__desktop-label' showAnchorLine={false} horizontalAnchor={handleConnectionHorizontalType(annotation, xScale, config)} verticalAnchor={handleConnectionVerticalType(annotation, xScale, config)}>
|
|
106
|
+
<div
|
|
107
|
+
style={{
|
|
108
|
+
borderRadius: 5, // Optional: set border radius
|
|
109
|
+
backgroundColor: `rgba(255, 255, 255, ${annotation?.opacity ? Number(annotation?.opacity) / 100 : 1})`,
|
|
110
|
+
padding: '10px',
|
|
111
|
+
width: 'auto',
|
|
112
|
+
display: config.general.showAnnotationDropdown ? 'inline-flex' : 'flex',
|
|
113
|
+
justifyContent: 'start',
|
|
114
|
+
flexDirection: 'row'
|
|
115
|
+
}}
|
|
116
|
+
role='presentation'
|
|
117
|
+
tabIndex={0}
|
|
118
|
+
aria-label={`Annotation text that reads: ${annotation.text}`}
|
|
119
|
+
>
|
|
120
|
+
{config?.general?.showAnnotationDropdown && (
|
|
121
|
+
<>
|
|
122
|
+
<p className='annotation__has-dropdown-number' style={{ margin: '2px 6px' }}>
|
|
123
|
+
{index + 1}
|
|
124
|
+
</p>
|
|
125
|
+
</>
|
|
126
|
+
)}
|
|
127
|
+
<div style={{ fontSize: fontSizes[config.fontSize] }} dangerouslySetInnerHTML={sanitizedData()} />
|
|
128
|
+
</div>
|
|
129
|
+
</HtmlLabel>
|
|
130
|
+
{annotation.connectionType === 'line' && <Connector type='line' pathProps={{ markerStart: `url(#marker-start--${index})` }} />}
|
|
131
|
+
{annotation.connectionType === 'elbow' && <Connector type='elbow' pathProps={{ markerStart: `url(#marker-start--${index})` }} />}
|
|
132
|
+
{annotation.connectionType === 'curve' && (
|
|
133
|
+
<LinePath
|
|
134
|
+
d={`M ${annotationX},${annotation.y}
|
|
135
|
+
Q ${annotationX + annotation.dx / 2}, ${annotation.y + annotation.dy / 2 + Number(annotation?.bezier) || 0} ${annotationX + annotation.dx},${annotation.y + annotation.dy}`}
|
|
136
|
+
stroke='black'
|
|
137
|
+
strokeWidth='2'
|
|
138
|
+
fill='none'
|
|
139
|
+
marker-start={`url(#marker-start--${index})`}
|
|
140
|
+
/>
|
|
141
|
+
)}
|
|
142
|
+
{annotation.marker === 'circle' && <CircleSubject id={`marker-start--${index}`} className='circle-subject' stroke={colorScale(annotation.seriesKey)} radius={8} />}
|
|
143
|
+
{annotation.marker === 'arrow' && <MarkerArrow fill='black' id={`marker-start--${index}`} x={annotationX} y={annotation.y} stroke='#333' markerWidth={10} size={10} strokeWidth={1} orient='auto-start-reverse' markerUnits='userSpaceOnUse' />}
|
|
144
|
+
<circle fill='white' cx={annotationX + annotation.dx} cy={annotation.y + annotation.dy} r={16} className='annotation__mobile-label annotation__mobile-label-circle' stroke={colorScale(annotation.seriesKey)} />
|
|
145
|
+
<text height={16} x={annotationX + annotation.dx} y={annotation.y + annotation.dy} className='annotation__mobile-label' alignmentBaseline='middle' textAnchor='middle'>
|
|
146
|
+
{index + 1}
|
|
147
|
+
</text>
|
|
148
|
+
</AnnotationComponent>
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default Annotations
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react'
|
|
2
|
+
import ConfigContext from '../../../ConfigContext'
|
|
3
|
+
import './AnnotationDropdown.styles.css'
|
|
4
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
5
|
+
import Annotation from '..'
|
|
6
|
+
import { fontSizes } from '@cdc/core/helpers/cove/fontSettings'
|
|
7
|
+
|
|
8
|
+
const AnnotationDropdown = () => {
|
|
9
|
+
const { currentViewport: viewport, config } = useContext(ConfigContext)
|
|
10
|
+
const [expanded, setExpanded] = useState(false)
|
|
11
|
+
|
|
12
|
+
const titleFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[config?.fontSize]}px`
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
config: { annotations }
|
|
16
|
+
} = useContext(ConfigContext)
|
|
17
|
+
|
|
18
|
+
const limitHeight = {
|
|
19
|
+
maxHeight: config.table.limitHeight && `${config.table.height}px`,
|
|
20
|
+
OverflowY: 'scroll'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handleAccordionClassName = () => {
|
|
24
|
+
const classNames = ['data-table-heading', 'annotation__dropdown-list']
|
|
25
|
+
if (!expanded) {
|
|
26
|
+
classNames.push('collapsed')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return classNames.join(' ')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const handleSectionClasses = () => {
|
|
33
|
+
const classes = [`data-table-container`, viewport, `d-block`, `d-lg-none`]
|
|
34
|
+
|
|
35
|
+
if (config.general.showAnnotationDropdown) {
|
|
36
|
+
classes.push('d-lg-block')
|
|
37
|
+
classes.splice(classes.indexOf('d-lg-none'), 1)
|
|
38
|
+
}
|
|
39
|
+
return classes.join(' ')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<section className={handleSectionClasses()}>
|
|
45
|
+
<div
|
|
46
|
+
style={{ fontSize: titleFontSize }}
|
|
47
|
+
role='button'
|
|
48
|
+
className={handleAccordionClassName()}
|
|
49
|
+
onClick={() => {
|
|
50
|
+
setExpanded(!expanded)
|
|
51
|
+
}}
|
|
52
|
+
tabIndex={0}
|
|
53
|
+
onKeyDown={e => {
|
|
54
|
+
if (e.keyCode === 13) {
|
|
55
|
+
setExpanded(!expanded)
|
|
56
|
+
}
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<Icon display={expanded ? 'minus' : 'plus'} base />
|
|
60
|
+
{config.general.annotationDropdownText === '' ? 'Annotations' : config?.general?.annotationDropdownText}
|
|
61
|
+
</div>
|
|
62
|
+
{expanded && (
|
|
63
|
+
<div className='table-container annotation-dropdown__panel' style={limitHeight}>
|
|
64
|
+
<Annotation.List useBootstrapVisibilityClasses={false} />
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
</section>
|
|
68
|
+
</>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default AnnotationDropdown
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.cdc-open-viz-module {
|
|
2
|
+
.annotation__title-circle {
|
|
3
|
+
display: flex;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
border-radius: 50%;
|
|
7
|
+
padding: 8px;
|
|
8
|
+
width: 16px;
|
|
9
|
+
height: 16px;
|
|
10
|
+
margin-right: 5px;
|
|
11
|
+
line-height: 1.5rem;
|
|
12
|
+
|
|
13
|
+
border: 2px solid #666;
|
|
14
|
+
text-align: center;
|
|
15
|
+
font-size: 14px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.annotation__title-wrapper {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-wrap: nowrap;
|
|
21
|
+
align-items: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.annotation__title-wrapper .annotation__title-text {
|
|
25
|
+
margin-left: 5px;
|
|
26
|
+
font-size: 16px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.annotation__subtext {
|
|
30
|
+
font-size: 12px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.annotation-list {
|
|
34
|
+
list-style: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.annotation-list li {
|
|
38
|
+
margin-top: 5px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.cove-component__content {
|
|
42
|
+
container-type: inline-size;
|
|
43
|
+
container-name: content;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../../../ConfigContext'
|
|
3
|
+
import './AnnotationList.styles.css'
|
|
4
|
+
import DOMPurify from 'dompurify'
|
|
5
|
+
|
|
6
|
+
type AnnotationListProps = {
|
|
7
|
+
useBootstrapVisibilityClasses?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const AnnotationList: React.FC<AnnotationListProps> = ({ useBootstrapVisibilityClasses = true }) => {
|
|
11
|
+
const { config } = useContext(ConfigContext)
|
|
12
|
+
const annotations = config.annotations || []
|
|
13
|
+
|
|
14
|
+
const ulClasses = () => {
|
|
15
|
+
const classes = ['annotation-list']
|
|
16
|
+
if (useBootstrapVisibilityClasses) {
|
|
17
|
+
classes.push('d-block', 'd-md-none')
|
|
18
|
+
}
|
|
19
|
+
return classes.join(' ')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const annotationListItems = annotations.map((annotation, annotationIndex) => {
|
|
23
|
+
const text = annotation.text || ''
|
|
24
|
+
|
|
25
|
+
// sanitize the text for setting dangerouslySetInnerHTML
|
|
26
|
+
const sanitizedData = () => ({
|
|
27
|
+
__html: DOMPurify.sanitize(text)
|
|
28
|
+
})
|
|
29
|
+
return (
|
|
30
|
+
<li key={`annotation-li-item__annotationIndex`}>
|
|
31
|
+
<div className='annotation__title-wrapper'>
|
|
32
|
+
<div className='annotation__title-circle'>{annotationIndex + 1}</div>
|
|
33
|
+
<p className='annotation__subtext' dangerouslySetInnerHTML={sanitizedData()} />
|
|
34
|
+
</div>
|
|
35
|
+
</li>
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return <ul className={ulClasses()}>{annotationListItems}</ul>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default AnnotationList
|