@watermarkinsights/ripple 3.7.0-3 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cjs/{functions-f0693632.js → functions-13b0e88e.js} +442 -442
- package/dist/cjs/{global-69ca443d.js → global-c802d13c.js} +65 -65
- package/dist/cjs/{http-service-494d81de.js → http-service-9e8c4dd5.js} +49 -49
- package/dist/cjs/{interfaces-a3338581.js → interfaces-30a74c1f.js} +29 -29
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/priv-chart-popover.cjs.entry.js +78 -78
- package/dist/cjs/priv-datepicker.cjs.entry.js +667 -667
- package/dist/cjs/priv-navigator-button.cjs.entry.js +18 -18
- package/dist/cjs/priv-navigator-item.cjs.entry.js +23 -23
- package/dist/cjs/ripple.cjs.js +1 -1
- package/dist/cjs/wm-action-menu_2.cjs.entry.js +341 -341
- package/dist/cjs/wm-button.cjs.entry.js +236 -236
- package/dist/cjs/wm-chart-slice.cjs.entry.js +13 -13
- package/dist/cjs/wm-chart.cjs.entry.js +474 -474
- package/dist/cjs/wm-datepicker.cjs.entry.js +262 -262
- package/dist/cjs/wm-input.cjs.entry.js +134 -134
- package/dist/cjs/wm-modal-footer.cjs.entry.js +31 -31
- package/dist/cjs/wm-modal-header.cjs.entry.js +34 -34
- package/dist/cjs/wm-modal.cjs.entry.js +161 -161
- package/dist/cjs/wm-navigator.cjs.entry.js +268 -268
- package/dist/cjs/wm-network-uploader.cjs.entry.js +420 -420
- package/dist/cjs/wm-option_2.cjs.entry.js +494 -494
- package/dist/cjs/wm-pagination.cjs.entry.js +167 -167
- package/dist/cjs/wm-search.cjs.entry.js +232 -231
- package/dist/cjs/wm-snackbar.cjs.entry.js +170 -170
- package/dist/cjs/wm-tab-item_3.cjs.entry.js +296 -296
- package/dist/cjs/wm-tag-input.cjs.entry.js +570 -570
- package/dist/cjs/wm-timepicker.cjs.entry.js +380 -380
- package/dist/cjs/wm-toggletip.cjs.entry.js +124 -124
- package/dist/cjs/wm-uploader.cjs.entry.js +346 -346
- package/dist/cjs/wm-wrapper.cjs.entry.js +12 -12
- package/dist/collection/components/wm-action-menu/wm-action-menu.js +460 -460
- package/dist/collection/components/wm-button/wm-button.js +503 -503
- package/dist/collection/components/wm-chart/priv-chart-popover/priv-chart-popover.js +232 -232
- package/dist/collection/components/wm-chart/wm-chart-slice.js +64 -64
- package/dist/collection/components/wm-chart/wm-chart.js +718 -718
- package/dist/collection/components/wm-datepicker/priv-datepicker/priv-datepicker.js +1015 -1015
- package/dist/collection/components/wm-datepicker/wm-datepicker.js +468 -468
- package/dist/collection/components/wm-input/wm-input.js +423 -423
- package/dist/collection/components/wm-menuitem/wm-menuitem.js +444 -444
- package/dist/collection/components/wm-modal/wm-modal-footer.js +141 -141
- package/dist/collection/components/wm-modal/wm-modal-header.js +92 -92
- package/dist/collection/components/wm-modal/wm-modal.js +459 -459
- package/dist/collection/components/wm-navigator/priv-navigator-button/priv-navigator-button.js +97 -97
- package/dist/collection/components/wm-navigator/priv-navigator-item/priv-navigator-item.js +114 -114
- package/dist/collection/components/wm-navigator/wm-navigator.js +468 -468
- package/dist/collection/components/wm-option/wm-option.js +422 -422
- package/dist/collection/components/wm-pagination/wm-pagination.js +368 -368
- package/dist/collection/components/wm-search/wm-search.js +480 -479
- package/dist/collection/components/wm-select/wm-select.js +720 -720
- package/dist/collection/components/wm-snackbar/wm-snackbar.js +297 -297
- package/dist/collection/components/wm-tabs/wm-tab-item/wm-tab-item.js +216 -216
- package/dist/collection/components/wm-tabs/wm-tab-list/wm-tab-list.js +330 -330
- package/dist/collection/components/wm-tabs/wm-tab-panel/wm-tab-panel.js +104 -104
- package/dist/collection/components/wm-tag-input/wm-tag-input.js +801 -801
- package/dist/collection/components/wm-timepicker/wm-timepicker.js +580 -580
- package/dist/collection/components/wm-toggletip/wm-toggletip.js +241 -241
- package/dist/collection/components/wm-uploader/wm-network-uploader/wm-network-uploader.js +753 -753
- package/dist/collection/components/wm-uploader/wm-uploader.js +748 -748
- package/dist/collection/components/wm-wrapper/wm-wrapper.js +27 -27
- package/dist/collection/dev/scripts.js +20 -20
- package/dist/collection/global/__mocks__/functions.js +6 -6
- package/dist/collection/global/functions.js +445 -445
- package/dist/collection/global/global.js +72 -72
- package/dist/collection/global/interfaces.js +49 -49
- package/dist/collection/global/services/__mocks__/http-service.js +130 -130
- package/dist/collection/global/services/http-service.js +50 -50
- package/dist/collection/lang/lang.js +5 -5
- package/dist/collection/lang/missing.js +39 -39
- package/dist/collection/lang/piglatin.js +93 -93
- package/dist/esm/{functions-cbfc81d1.js → functions-6ddad20e.js} +442 -442
- package/dist/esm/{global-a12289f9.js → global-a4da88c7.js} +65 -65
- package/dist/esm/{http-service-3dc3b3e7.js → http-service-5d037e16.js} +49 -49
- package/dist/esm/{interfaces-2b97fab2.js → interfaces-61c6305b.js} +29 -29
- package/dist/esm/loader.js +1 -1
- package/dist/esm/polyfills/core-js.js +0 -0
- package/dist/esm/polyfills/dom.js +0 -0
- package/dist/esm/polyfills/es5-html-element.js +0 -0
- package/dist/esm/polyfills/index.js +0 -0
- package/dist/esm/polyfills/system.js +0 -0
- package/dist/esm/priv-chart-popover.entry.js +78 -78
- package/dist/esm/priv-datepicker.entry.js +667 -667
- package/dist/esm/priv-navigator-button.entry.js +18 -18
- package/dist/esm/priv-navigator-item.entry.js +23 -23
- package/dist/esm/ripple.js +1 -1
- package/dist/esm/wm-action-menu_2.entry.js +341 -341
- package/dist/esm/wm-button.entry.js +236 -236
- package/dist/esm/wm-chart-slice.entry.js +13 -13
- package/dist/esm/wm-chart.entry.js +474 -474
- package/dist/esm/wm-datepicker.entry.js +262 -262
- package/dist/esm/wm-input.entry.js +134 -134
- package/dist/esm/wm-modal-footer.entry.js +31 -31
- package/dist/esm/wm-modal-header.entry.js +34 -34
- package/dist/esm/wm-modal.entry.js +161 -161
- package/dist/esm/wm-navigator.entry.js +268 -268
- package/dist/esm/wm-network-uploader.entry.js +420 -420
- package/dist/esm/wm-option_2.entry.js +494 -494
- package/dist/esm/wm-pagination.entry.js +167 -167
- package/dist/esm/wm-search.entry.js +232 -231
- package/dist/esm/wm-snackbar.entry.js +170 -170
- package/dist/esm/wm-tab-item_3.entry.js +296 -296
- package/dist/esm/wm-tag-input.entry.js +570 -570
- package/dist/esm/wm-timepicker.entry.js +380 -380
- package/dist/esm/wm-toggletip.entry.js +124 -124
- package/dist/esm/wm-uploader.entry.js +346 -346
- package/dist/esm/wm-wrapper.entry.js +12 -12
- package/dist/ripple/{p-5585f23e.entry.js → p-0068e871.entry.js} +1 -1
- package/dist/ripple/{p-7d9c18b9.entry.js → p-00999aeb.entry.js} +1 -1
- package/dist/ripple/{p-cd466a5b.entry.js → p-08b273ba.entry.js} +1 -1
- package/dist/ripple/{p-266e4bc2.entry.js → p-196b4c55.entry.js} +1 -1
- package/dist/ripple/{p-e3765b8e.entry.js → p-1a3df227.entry.js} +1 -1
- package/dist/ripple/{p-30f13477.entry.js → p-3590a57f.entry.js} +1 -1
- package/dist/ripple/{p-8a4bac13.entry.js → p-3a178ac8.entry.js} +1 -1
- package/dist/ripple/{p-74438aab.entry.js → p-46371fad.entry.js} +1 -1
- package/dist/ripple/p-5548fc7c.js +1 -0
- package/dist/ripple/{p-da1435f1.entry.js → p-5c73fd59.entry.js} +1 -1
- package/dist/ripple/{p-0c488d9d.entry.js → p-66513af1.entry.js} +1 -1
- package/dist/ripple/{p-bd05632b.entry.js → p-828316d6.entry.js} +1 -1
- package/dist/ripple/{p-b5edcf38.js → p-857a0bc0.js} +0 -0
- package/dist/ripple/{p-43f1298b.js → p-888bec42.js} +0 -0
- package/dist/ripple/{p-2caeca6b.entry.js → p-9624967f.entry.js} +1 -1
- package/dist/ripple/{p-938a5ba4.entry.js → p-99e2c5f6.entry.js} +1 -1
- package/dist/ripple/{p-fd8070fb.js → p-a6d6eae7.js} +0 -0
- package/dist/ripple/{p-b9656d44.entry.js → p-b495e06c.entry.js} +1 -1
- package/dist/ripple/{p-4870d369.entry.js → p-b8aa478f.entry.js} +1 -1
- package/dist/ripple/{p-4414cb64.entry.js → p-bf985c5d.entry.js} +1 -1
- package/dist/ripple/{p-c0b0510a.entry.js → p-cd6de442.entry.js} +1 -1
- package/dist/ripple/{p-111f705a.entry.js → p-cfdf1a79.entry.js} +1 -1
- package/dist/ripple/{p-ad73d523.entry.js → p-d22c957d.entry.js} +1 -1
- package/dist/ripple/{p-f3569e91.entry.js → p-e43b4eda.entry.js} +1 -1
- package/dist/ripple/ripple.esm.js +1 -1
- package/dist/types/components/wm-action-menu/wm-action-menu.d.ts +48 -48
- package/dist/types/components/wm-button/wm-button.d.ts +44 -44
- package/dist/types/components/wm-chart/priv-chart-popover/priv-chart-popover.d.ts +23 -23
- package/dist/types/components/wm-chart/wm-chart-slice.d.ts +8 -8
- package/dist/types/components/wm-chart/wm-chart.d.ts +82 -82
- package/dist/types/components/wm-datepicker/priv-datepicker/priv-datepicker.d.ts +76 -76
- package/dist/types/components/wm-datepicker/wm-datepicker.d.ts +41 -41
- package/dist/types/components/wm-input/wm-input.d.ts +46 -46
- package/dist/types/components/wm-menuitem/wm-menuitem.d.ts +34 -34
- package/dist/types/components/wm-modal/wm-modal-footer.d.ts +15 -15
- package/dist/types/components/wm-modal/wm-modal-header.d.ts +12 -12
- package/dist/types/components/wm-modal/wm-modal.d.ts +41 -41
- package/dist/types/components/wm-navigator/priv-navigator-button/priv-navigator-button.d.ts +10 -10
- package/dist/types/components/wm-navigator/priv-navigator-item/priv-navigator-item.d.ts +13 -13
- package/dist/types/components/wm-navigator/wm-navigator.d.ts +61 -61
- package/dist/types/components/wm-option/wm-option.d.ts +32 -32
- package/dist/types/components/wm-pagination/wm-pagination.d.ts +32 -32
- package/dist/types/components/wm-search/wm-search.d.ts +86 -86
- package/dist/types/components/wm-select/wm-select.d.ts +66 -66
- package/dist/types/components/wm-snackbar/wm-snackbar.d.ts +35 -35
- package/dist/types/components/wm-tabs/wm-tab-item/wm-tab-item.d.ts +38 -38
- package/dist/types/components/wm-tabs/wm-tab-list/wm-tab-list.d.ts +53 -53
- package/dist/types/components/wm-tabs/wm-tab-panel/wm-tab-panel.d.ts +20 -20
- package/dist/types/components/wm-tag-input/wm-tag-input.d.ts +92 -92
- package/dist/types/components/wm-timepicker/wm-timepicker.d.ts +61 -61
- package/dist/types/components/wm-toggletip/wm-toggletip.d.ts +27 -27
- package/dist/types/components/wm-uploader/wm-network-uploader/wm-network-uploader.d.ts +85 -85
- package/dist/types/components/wm-uploader/wm-uploader.d.ts +80 -80
- package/dist/types/components/wm-wrapper/wm-wrapper.d.ts +7 -7
- package/dist/types/components.d.ts +24 -24
- package/dist/types/global/__mocks__/functions.d.ts +6 -6
- package/dist/types/global/functions.d.ts +40 -40
- package/dist/types/global/global.d.ts +1 -1
- package/dist/types/global/interfaces.d.ts +34 -34
- package/dist/types/global/services/__mocks__/http-service.d.ts +6 -6
- package/dist/types/global/services/http-service.d.ts +4 -4
- package/dist/types/lang/lang.d.ts +5 -5
- package/package.json +47 -47
- package/dist/ripple/p-c296ff27.js +0 -1
|
@@ -1,481 +1,481 @@
|
|
|
1
1
|
import { r as registerInstance, f as forceUpdate, h, H as Host, g as getElement } from './index-66f8130e.js';
|
|
2
|
-
import { g as generateId, d as debounce, b as getPosition, f as findParentWithScrollbar, i as intl, c as checkForActiveElInShadow } from './functions-
|
|
2
|
+
import { g as generateId, d as debounce, b as getPosition, f as findParentWithScrollbar, i as intl, c as checkForActiveElInShadow } from './functions-6ddad20e.js';
|
|
3
3
|
|
|
4
4
|
const wmChartCss = ".component-wrapper{display:flex;flex-direction:column;align-items:center;font-size:0.875rem;position:relative;}.component-wrapper .label{display:block;margin:0;padding-bottom:0.25rem;font-weight:500;position:relative}.component-wrapper .label .subinfo{display:block;font-weight:normal;font-style:italic;bottom:0.25rem;width:100%;color:#6b6b6b}.component-wrapper .legend-wrapper{display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center}.component-wrapper .legend-wrapper .legend{display:flex;align-items:center;flex-wrap:wrap;text-align:center;padding-top:0.25rem;padding-bottom:0.75rem;box-sizing:border-box}.component-wrapper .legend-wrapper .legend.--top{margin-top:-0.75rem}.component-wrapper .legend-wrapper .legend.--top .legend-item{padding-top:0.75rem}.component-wrapper .legend-wrapper .legend.--top .legend-color{top:0.75rem}.component-wrapper .legend-wrapper .legend.--bottom{margin-bottom:-0.75rem}.component-wrapper .legend-wrapper .legend.--bottom .legend-item{padding-bottom:0.75rem}.component-wrapper .legend-wrapper .legend.--bottom .legend-color{top:-0.75rem}.component-wrapper .legend-wrapper .legend .legend-item{position:relative}.component-wrapper .legend-wrapper .legend .legend-item:not(:last-of-type){padding-right:1.25rem}.component-wrapper .legend-wrapper .legend .legend-text{padding-left:1rem;line-height:1}.component-wrapper .legend-wrapper .legend .legend-color{position:absolute;left:0;bottom:0;margin:auto;width:0.6875rem;height:0.6875rem}.component-wrapper .legend-wrapper .cluster-warning{font-size:0.75rem;font-style:italic;max-width:100%}.component-wrapper .doughnut-svg,.component-wrapper .inner-bar-wrapper{overflow:visible}.component-wrapper .bar-wrapper{flex-grow:1;width:100%}.component-wrapper .bar-wrapper .inner-bar-wrapper{width:100%}.component-wrapper .bar-wrapper .inner-bar-wrapper .bar-segments-wrapper{display:flex}.component-wrapper .bar-wrapper .inner-bar-wrapper .bar-segments-wrapper .bar-segment{height:30px;cursor:pointer}.component-wrapper .bar-wrapper .inner-bar-wrapper .bar-segments-wrapper .bar-segment:not(.zero):not(:last-of-type){margin-right:2px}.component-wrapper .doughnut-wrapper{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px}.component-wrapper .doughnut-wrapper .doughnut-segment{cursor:pointer}.component-wrapper .value{display:inline-block;text-align:center}.component-wrapper .value:not(:last-of-type){margin-right:2px}.component-wrapper path:active,.component-wrapper path:hover,.component-wrapper path:focus,.component-wrapper .bar-segment:active,.component-wrapper .bar-segment:hover,.component-wrapper .bar-segment:focus{outline:none}.component-wrapper path:active.bar-segment,.component-wrapper path:hover.bar-segment,.component-wrapper path:focus.bar-segment,.component-wrapper .bar-segment:active.bar-segment,.component-wrapper .bar-segment:hover.bar-segment,.component-wrapper .bar-segment:focus.bar-segment{-webkit-box-shadow:0px 0px 6px #333;-moz-box-shadow:0px 0px 6px #333;box-shadow:0px 0px 6px #333}.component-wrapper path:active.doughnut-segment,.component-wrapper path:hover.doughnut-segment,.component-wrapper path:focus.doughnut-segment,.component-wrapper .bar-segment:active.doughnut-segment,.component-wrapper .bar-segment:hover.doughnut-segment,.component-wrapper .bar-segment:focus.doughnut-segment{filter:url(#wmHoverDropShadow)}.component-wrapper path::-moz-focus-inner,.component-wrapper .bar-segment::-moz-focus-inner{border:0;outline:none}.component-wrapper.doughnut1 label,.component-wrapper.doughnut1 .label-text,.component-wrapper.doughnut2 label,.component-wrapper.doughnut2 .label-text,.component-wrapper.doughnut3 label,.component-wrapper.doughnut3 .label-text{position:absolute !important;width:1px !important;height:1px !important;padding:0 !important;border:0 !important;overflow:hidden !important;clip:rect(0, 0, 0, 0) !important;clip-path:inset(50%) !important;white-space:nowrap !important;margin:-1px !important}.component-wrapper.doughnut0{align-items:center}.component-wrapper.doughnut0 label{text-align:center;width:100%;padding-bottom:1.5rem}.component-wrapper.doughnut0 label .subinfo{position:absolute}.component-wrapper.doughnut0 .legend{display:flex}.component-wrapper.doughnut0 .completion-message{padding-top:0.625rem}.component-wrapper.bar1{padding:1.25rem;align-items:flex-start}.component-wrapper.bar1 label{display:flex;flex-direction:column}.component-wrapper.bar1 label .subinfo{position:initial}.component-wrapper.bar1 .legend{display:none}.component-wrapper.bar1 .chart-wrapper{display:flex}.component-wrapper.bar1 .chart-wrapper .single-perc{width:4rem;flex:none;display:flex;height:30px;align-items:center}.component-wrapper.bar1 .chart-wrapper .inner-bar-wrapper{flex:1}.component-wrapper.bar1 .chart-wrapper .completion-message{width:100%;text-align:right;margin-top:0.25rem}@media screen and (min-width: 768px){.component-wrapper.bar1{flex-direction:row}.component-wrapper.bar1 label{display:flex;justify-content:center;min-height:30px;width:12rem;text-align:left;padding-right:1.25rem;padding-bottom:0;flex:none}.component-wrapper.bar1 .bar-wrapper{flex-direction:row-reverse}.component-wrapper.bar1 .bar-wrapper .single-perc{text-align:center;padding-left:0.5rem;justify-content:center}}.component-wrapper.bar2,.component-wrapper.bar3,.component-wrapper.bar4,.component-wrapper.bar5{align-items:flex-start}.component-wrapper.bar2 .inner-bar-wrapper,.component-wrapper.bar4 .inner-bar-wrapper,.component-wrapper.bar5 .inner-bar-wrapper{height:30px;margin-bottom:0}.component-wrapper.bar2 .inner-bar-wrapper.show-values,.component-wrapper.bar4 .inner-bar-wrapper.show-values,.component-wrapper.bar5 .inner-bar-wrapper.show-values{height:60px;margin-top:0}.component-wrapper.bar3 .legend{padding-bottom:1.25rem}.component-wrapper.bar3 .chart-wrapper{position:relative;height:100px}.component-wrapper.bar3 .chart-wrapper .inner-bar-wrapper{position:absolute;top:35px;left:0;right:0;bottom:0;height:30px}.component-wrapper.bar3 .chart-wrapper .inner-bar-wrapper text{fill:#4a4a4a}.component-wrapper.bar3 .chart-wrapper .axis{position:absolute;top:0;left:0;height:90px;width:100%;overflow:visible;transform:translateY(90px)}.component-wrapper.bar3 .chart-wrapper .axis line{stroke:#eeedf4;stroke-width:1px}.component-wrapper.bar3 .chart-wrapper .axis text{transform:translate(4px, 24px);text-anchor:middle}.component-wrapper.left-label{flex-direction:row}.component-wrapper.left-label .label{width:12rem;padding-right:1.25rem;flex:none;align-self:flex-end;min-height:30px;display:flex;align-items:center}:host(:focus){outline:none}:host(:focus) .component-wrapper.user-is-tabbing .bar-segments-wrapper,:host(:focus) .component-wrapper.user-is-tabbing .doughnut-wrapper{-webkit-box-shadow:0 2px 2px 0 rgba(244, 243, 246, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 0 4px 3px #61279e;-moz-box-shadow:0 2px 2px 0 rgba(244, 243, 246, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 0 4px 3px #61279e;box-shadow:0 2px 2px 0 rgba(244, 243, 246, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 0 4px 3px #61279e}:host(:focus) .component-wrapper.user-is-tabbing .bar-segments-wrapper::-moz-focus-inner,:host(:focus) .component-wrapper.user-is-tabbing .doughnut-wrapper::-moz-focus-inner{border:0}";
|
|
5
5
|
|
|
6
|
-
const Chart = class {
|
|
7
|
-
constructor(hostRef) {
|
|
8
|
-
registerInstance(this, hostRef);
|
|
9
|
-
this.chartType = "doughnut1";
|
|
10
|
-
this.showLegend = true;
|
|
11
|
-
this.notStartedColor = false;
|
|
12
|
-
// left labels are a temporary solution for the lack of a stacked bar chart and is undocumented
|
|
13
|
-
// it only works with bar4 and should be used without a legend
|
|
14
|
-
this.labelPosition = "top";
|
|
15
|
-
this.isTabbing = false;
|
|
16
|
-
this.uid = generateId();
|
|
17
|
-
this.slicesData = [];
|
|
18
|
-
this.colors = {
|
|
19
|
-
salmon: "#ff5f4e",
|
|
20
|
-
cyan: "#19a1a9",
|
|
21
|
-
forest: "#088000",
|
|
22
|
-
sleet: "#7f97ad",
|
|
23
|
-
midnight: "#2e1b46",
|
|
24
|
-
lavender: "#8b86ca",
|
|
25
|
-
};
|
|
26
|
-
this.types = {
|
|
27
|
-
doughnut0: {
|
|
28
|
-
size: 155,
|
|
29
|
-
colors: [this.colors.forest, "#bfbfbf"],
|
|
30
|
-
thickness: 0.73,
|
|
31
|
-
padding: 25,
|
|
32
|
-
isBar: false,
|
|
33
|
-
},
|
|
34
|
-
doughnut1: {
|
|
35
|
-
size: 130,
|
|
36
|
-
colors: [this.colors.lavender, this.colors.midnight, "#d4d4d4"],
|
|
37
|
-
thickness: 0.5,
|
|
38
|
-
padding: 90,
|
|
39
|
-
isBar: false,
|
|
40
|
-
},
|
|
41
|
-
doughnut2: {
|
|
42
|
-
size: 130,
|
|
43
|
-
colors: [this.colors.cyan, this.colors.salmon, "#d4d4d4"],
|
|
44
|
-
thickness: 0.5,
|
|
45
|
-
padding: 90,
|
|
46
|
-
isBar: false,
|
|
47
|
-
},
|
|
48
|
-
doughnut3: {
|
|
49
|
-
size: 130,
|
|
50
|
-
colors: [this.colors.lavender, this.colors.midnight, "#919834", "#c177cf", "#c16e00", "#029af2", "#2a6993"],
|
|
51
|
-
thickness: 0.5,
|
|
52
|
-
padding: 90,
|
|
53
|
-
isBar: false,
|
|
54
|
-
},
|
|
55
|
-
bar1: {
|
|
56
|
-
size: 350,
|
|
57
|
-
colors: [this.colors.forest, "#bfbfbf"],
|
|
58
|
-
padding: 0,
|
|
59
|
-
isBar: true,
|
|
60
|
-
},
|
|
61
|
-
bar2: {
|
|
62
|
-
size: 400,
|
|
63
|
-
colors: ["#d4d4d4", this.colors.sleet, this.colors.cyan, this.colors.salmon],
|
|
64
|
-
padding: 0,
|
|
65
|
-
isBar: true,
|
|
66
|
-
},
|
|
67
|
-
bar3: {
|
|
68
|
-
size: 300,
|
|
69
|
-
colors: ["#0d696e", this.colors.cyan, "#8e4129", this.colors.salmon],
|
|
70
|
-
padding: 0,
|
|
71
|
-
isBar: true,
|
|
72
|
-
},
|
|
73
|
-
bar4: {
|
|
74
|
-
size: 400,
|
|
75
|
-
colors: ["#d4d4d4", this.colors.sleet, "#33a919"],
|
|
76
|
-
padding: 0,
|
|
77
|
-
isBar: true,
|
|
78
|
-
},
|
|
79
|
-
bar5: {
|
|
80
|
-
size: 400,
|
|
81
|
-
colors: [
|
|
82
|
-
"#d4d4d4",
|
|
83
|
-
this.colors.lavender,
|
|
84
|
-
this.colors.midnight,
|
|
85
|
-
"#919834",
|
|
86
|
-
"#c177cf",
|
|
87
|
-
"#c16e00",
|
|
88
|
-
"#029af2",
|
|
89
|
-
"#2a6993",
|
|
90
|
-
],
|
|
91
|
-
padding: 0,
|
|
92
|
-
isBar: true,
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
/* LIFECYCLE METHODS + EVENTS FROM THE CHILDREN */
|
|
96
|
-
this.debouncedResize = debounce(async () => {
|
|
97
|
-
this.setHybridType();
|
|
98
|
-
await this.getData();
|
|
99
|
-
forceUpdate(this.el);
|
|
100
|
-
}, 10);
|
|
101
|
-
this.debouncedSliceUpdate = debounce(async () => {
|
|
102
|
-
await this.getData();
|
|
103
|
-
forceUpdate(this.el);
|
|
104
|
-
}, 100);
|
|
105
|
-
}
|
|
106
|
-
get tempValueFormat() {
|
|
107
|
-
// use of this getter should be replaced with dateFormat when showValues is fully phased out
|
|
108
|
-
return this.valueFormat || this.showValues || "none";
|
|
109
|
-
}
|
|
110
|
-
get sliceEls() {
|
|
111
|
-
const isBarType = this.currentChartType.includes("bar");
|
|
112
|
-
const isDoughnutType = this.currentChartType.includes("doughnut");
|
|
113
|
-
return isBarType
|
|
114
|
-
? Array.from(this.barEl.querySelectorAll(".bar-segment"))
|
|
115
|
-
: isDoughnutType
|
|
116
|
-
? Array.from(this.doughnutEl.querySelectorAll(".doughnut-segment"))
|
|
117
|
-
: undefined;
|
|
118
|
-
}
|
|
119
|
-
get currentChartType() {
|
|
120
|
-
return this.chartType === "hybrid" ? this.hybridType : this.chartType;
|
|
121
|
-
}
|
|
122
|
-
toggleTabbingOn() {
|
|
123
|
-
this.isTabbing = true;
|
|
124
|
-
}
|
|
125
|
-
toggleTabbingOff() {
|
|
126
|
-
this.isTabbing = false;
|
|
127
|
-
}
|
|
128
|
-
handleKeydown(ev) {
|
|
129
|
-
switch (ev.keyCode) {
|
|
130
|
-
// arrow up / left
|
|
131
|
-
case 37:
|
|
132
|
-
case 38:
|
|
133
|
-
ev.preventDefault();
|
|
134
|
-
this.isTabbing = true; // shd already be true. just in case user clicked on chart then pressed an arrow key
|
|
135
|
-
this.focusPrevious();
|
|
136
|
-
break;
|
|
137
|
-
// arrow right / down
|
|
138
|
-
case 39:
|
|
139
|
-
case 40:
|
|
140
|
-
ev.preventDefault();
|
|
141
|
-
this.isTabbing = true; // shd already be true. just in case user clicked on chart then pressed an arrow key
|
|
142
|
-
this.focusNext();
|
|
143
|
-
break;
|
|
144
|
-
// tab
|
|
145
|
-
case 9:
|
|
146
|
-
this.exitChart();
|
|
147
|
-
break;
|
|
148
|
-
case 27:
|
|
149
|
-
this.popoverEl.open = false;
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
handleSliceClick(ev, s) {
|
|
154
|
-
if (this.popoverEl && !this.isTabbing) {
|
|
155
|
-
s.coords = { x: ev.clientX, y: ev.clientY };
|
|
156
|
-
this.openPopover(s);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
handleSliceFocus(ev, s) {
|
|
160
|
-
if (this.popoverEl && this.isTabbing) {
|
|
161
|
-
s.coords = getPosition(ev.target);
|
|
162
|
-
this.openPopover(s);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
handleSliceKeyDown(ev) {
|
|
166
|
-
if (this.popoverEl && this.popoverEl.open && ev.key === "Enter") {
|
|
167
|
-
const popoverBtn = this.popoverEl.querySelector("button");
|
|
168
|
-
popoverBtn && popoverBtn.click();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
focusNext() {
|
|
172
|
-
const activeEl = checkForActiveElInShadow(document.activeElement);
|
|
173
|
-
const index =
|
|
174
|
-
// if the active el is not in the array the first element gets focused
|
|
175
|
-
(this.sliceEls.indexOf(activeEl) + 1) % this.sliceEls.length;
|
|
176
|
-
this.focusSlice(index);
|
|
177
|
-
}
|
|
178
|
-
focusPrevious() {
|
|
179
|
-
if (this.sliceEls) {
|
|
180
|
-
const activeEl = checkForActiveElInShadow(document.activeElement);
|
|
181
|
-
let index = this.sliceEls.indexOf(activeEl);
|
|
182
|
-
if (index === -1) {
|
|
183
|
-
// not in the array : focus the first slice
|
|
184
|
-
index = 0;
|
|
185
|
-
}
|
|
186
|
-
else if (index === 0) {
|
|
187
|
-
// first slice : focus the last slice
|
|
188
|
-
index = this.sliceEls.length - 1;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
// anything else: focus previous
|
|
192
|
-
index -= 1;
|
|
193
|
-
}
|
|
194
|
-
this.focusSlice(index);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
focusSlice(index) {
|
|
198
|
-
if (this.sliceEls && this.el) {
|
|
199
|
-
if (this.popoverEl) {
|
|
200
|
-
this.popoverEl.open = false;
|
|
201
|
-
}
|
|
202
|
-
this.el.tabIndex = -1;
|
|
203
|
-
// @ts-ignore
|
|
204
|
-
this.el.focusable = false; // for Edge
|
|
205
|
-
this.sliceEls.map((p) => {
|
|
206
|
-
p.tabIndex = -1;
|
|
207
|
-
// @ts-ignore
|
|
208
|
-
p.focusable = false; // for Edge
|
|
209
|
-
});
|
|
210
|
-
this.sliceEls[index].tabIndex = 0;
|
|
211
|
-
// @ts-ignore
|
|
212
|
-
this.sliceEls[index].focusable = true; // for Edge
|
|
213
|
-
this.sliceEls[index].focus();
|
|
214
|
-
window.setTimeout(() => {
|
|
215
|
-
if (this.popoverEl) {
|
|
216
|
-
this.popoverEl.open = true;
|
|
217
|
-
}
|
|
218
|
-
}, 10);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
exitChart() {
|
|
222
|
-
this.sliceEls &&
|
|
223
|
-
this.sliceEls.map((p) => {
|
|
224
|
-
p.tabIndex = -1;
|
|
225
|
-
// @ts-ignore
|
|
226
|
-
p.focusable = false; // for Edge
|
|
227
|
-
});
|
|
228
|
-
// delay so that we can tab out of component before chart becomes focusable again
|
|
229
|
-
// and in case user was still pressing an arrow key when they pressed tab
|
|
230
|
-
window.setTimeout(() => {
|
|
231
|
-
if (this.el) {
|
|
232
|
-
this.el.tabIndex = 0;
|
|
233
|
-
// @ts-ignore
|
|
234
|
-
this.el.focusable = true; // for Edge
|
|
235
|
-
if (this.popoverEl) {
|
|
236
|
-
this.popoverEl.open = false;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}, 100);
|
|
240
|
-
}
|
|
241
|
-
openPopover(s) {
|
|
242
|
-
if (!!this.popoverEl && !!s.title && !!s.text) {
|
|
243
|
-
this.popoverEl.popoverTitle = s.title;
|
|
244
|
-
this.popoverEl.popoverText = s.text;
|
|
245
|
-
this.popoverEl.buttonText = s.buttonText;
|
|
246
|
-
this.popoverEl.coords = s.coords;
|
|
247
|
-
this.popoverEl.sliceRef = s.sliceRef;
|
|
248
|
-
window.setTimeout(() => {
|
|
249
|
-
if (this.popoverEl) {
|
|
250
|
-
this.popoverEl.open = true;
|
|
251
|
-
}
|
|
252
|
-
}, 30);
|
|
253
|
-
const debouncedClosePopover = debounce(async () => {
|
|
254
|
-
this.popoverEl.open = false;
|
|
255
|
-
}, 10);
|
|
256
|
-
// set up event listeners for scrolling
|
|
257
|
-
// to close popover on page scroll
|
|
258
|
-
document.addEventListener("scroll", () => {
|
|
259
|
-
debouncedClosePopover();
|
|
260
|
-
});
|
|
261
|
-
// ... and on parent scroll
|
|
262
|
-
const scrollableParent = findParentWithScrollbar(this.el);
|
|
263
|
-
if (!!scrollableParent) {
|
|
264
|
-
scrollableParent.addEventListener("scroll", () => {
|
|
265
|
-
debouncedClosePopover();
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
/* UTILS */
|
|
271
|
-
amountToPercent(val, asInt) {
|
|
272
|
-
return asInt ? Math.round((val * 100) / this.total) : Math.round((val * 10000) / this.total) / 100; // with 2 decimals
|
|
273
|
-
}
|
|
274
|
-
amountToDegree(val) {
|
|
275
|
-
return (val * 360) / this.total;
|
|
276
|
-
}
|
|
277
|
-
toFixed(number) {
|
|
278
|
-
var val = parseFloat((Math.floor(number * 100) / 100).toFixed(2));
|
|
279
|
-
return val;
|
|
280
|
-
}
|
|
281
|
-
polarToCartesian(half, radius, startAngle, endAngle) {
|
|
282
|
-
var x = this.toFixed(half + half * radius * Math.cos((Math.PI * startAngle) / 180));
|
|
283
|
-
var y = this.toFixed(half + half * radius * Math.sin((Math.PI * startAngle) / 180));
|
|
284
|
-
if (endAngle !== undefined) {
|
|
285
|
-
// if a 2nd angle value was passed, return 2 pairs of coords
|
|
286
|
-
var x2 = this.toFixed(half + half * radius * Math.cos((Math.PI * endAngle) / 180));
|
|
287
|
-
var y2 = this.toFixed(half + half * radius * Math.sin((Math.PI * endAngle) / 180));
|
|
288
|
-
return { x1: x, y1: y, x2, y2 };
|
|
289
|
-
}
|
|
290
|
-
return { x, y };
|
|
291
|
-
}
|
|
292
|
-
/* CRUNCH THE NUMBERS */
|
|
293
|
-
getPathData(amount, offset) {
|
|
294
|
-
var startAngle = this.amountToDegree(offset) - 90; // start at noon, not at 3 o'clock
|
|
295
|
-
var activeAngle = (amount / this.total) * 360;
|
|
296
|
-
var endAngle = startAngle + activeAngle;
|
|
297
|
-
var largeArcFlagOuter = activeAngle > 180 ? "1 1" : "0 1";
|
|
298
|
-
var largeArcFlagInner = activeAngle > 180 ? "1 0" : "0 0";
|
|
299
|
-
var half = this.chartData.size / 2;
|
|
300
|
-
var innerRadius = this.chartData.thickness;
|
|
301
|
-
var outerRadius = 1;
|
|
302
|
-
if (activeAngle === 360) {
|
|
303
|
-
// fix to avoid bad svg shape when the path goes all around (100%)
|
|
304
|
-
endAngle -= 0.01;
|
|
305
|
-
}
|
|
306
|
-
var outerCoords = this.polarToCartesian(half, outerRadius, startAngle + 1.5, // 1.5 for slice separator
|
|
307
|
-
endAngle);
|
|
308
|
-
var innerCoords = this.polarToCartesian(half, innerRadius, startAngle + 3, // 3 for slice separator
|
|
309
|
-
endAngle);
|
|
310
|
-
const moveTo = `M ${outerCoords.x1}, ${outerCoords.y1} `;
|
|
311
|
-
const arc1 = this.getArc(outerRadius, largeArcFlagOuter, outerCoords.x2, outerCoords.y2);
|
|
312
|
-
const line = ` L ${innerCoords.x2}, ${innerCoords.y2} `;
|
|
313
|
-
const arc2 = this.getArc(innerRadius, largeArcFlagInner, innerCoords.x1, innerCoords.y1);
|
|
314
|
-
return moveTo + arc1 + line + arc2 + " z";
|
|
315
|
-
}
|
|
316
|
-
getArc(radius, largeArcFlag, x, y) {
|
|
317
|
-
var z = this.toFixed((this.chartData.size / 2) * radius);
|
|
318
|
-
return `A ${z}, ${z} 0 ${largeArcFlag} ${this.toFixed(x)}, ${this.toFixed(y)}`;
|
|
319
|
-
}
|
|
320
|
-
setHybridType() {
|
|
321
|
-
this.hybridType = window.innerWidth > 1340 ? "doughnut0" : "bar1";
|
|
322
|
-
}
|
|
323
|
-
/* GET THE DATA */
|
|
324
|
-
async getData() {
|
|
325
|
-
this.slicesData = [];
|
|
326
|
-
let acc = 0;
|
|
327
|
-
const children = this.el.querySelectorAll("wm-chart-slice");
|
|
328
|
-
this.total = Array.from(children).reduce((total, slice) => (total += parseInt(slice.getAttribute("amount") || "0")), 0);
|
|
329
|
-
children.forEach((c, i) => {
|
|
330
|
-
const amount = parseInt(c.getAttribute("amount") || "0");
|
|
331
|
-
const perc = this.amountToPercent(amount, true);
|
|
332
|
-
// determine whether the slice is in a cluster of small values
|
|
333
|
-
// to avoid percentage text overlap for small values
|
|
334
|
-
const prev = children[i === 0 ? children.length - 1 : i - 1];
|
|
335
|
-
const prevPerc = this.amountToPercent(parseInt(prev.getAttribute("amount") || "0"), true);
|
|
336
|
-
const next = children[i === children.length - 1 ? 0 : i + 1];
|
|
337
|
-
const nextPerc = this.amountToPercent(parseInt(next.getAttribute("amount") || "0"), true);
|
|
338
|
-
const isSmall = perc < 4;
|
|
339
|
-
const prevIsSmall = prevPerc < 5;
|
|
340
|
-
const nextIsSmall = nextPerc < 5;
|
|
341
|
-
let inSmallCluster = isSmall && (prevIsSmall || nextIsSmall);
|
|
342
|
-
// because <1% slice percentage text has an additional character
|
|
343
|
-
// the inSmallCluster threshold needs to be widened for that slice only
|
|
344
|
-
const lessThanOnePerc = perc === 0 && amount > 0;
|
|
345
|
-
if (lessThanOnePerc && (nextPerc < 8 || prevPerc < 8)) {
|
|
346
|
-
inSmallCluster = true;
|
|
347
|
-
}
|
|
348
|
-
// for bar5, first color should be skipped unless notStartedColor is set to true
|
|
349
|
-
const ind = this.currentChartType === "bar5" ? (this.notStartedColor ? i : i + 1) : i;
|
|
350
|
-
const color = this.types[this.currentChartType].colors[ind];
|
|
351
|
-
const sliceData = {
|
|
352
|
-
amount: amount,
|
|
353
|
-
perc: perc,
|
|
354
|
-
legend: c.getAttribute("legend"),
|
|
355
|
-
color: color || "#d4d4d4",
|
|
356
|
-
offset: acc,
|
|
357
|
-
id: `${this.uid}-${i + 1}`,
|
|
358
|
-
title: c.getAttribute("popover-title"),
|
|
359
|
-
text: c.getAttribute("popover-text"),
|
|
360
|
-
buttonText: c.getAttribute("popover-button-text"),
|
|
361
|
-
sliceRef: c,
|
|
362
|
-
inSmallCluster: inSmallCluster,
|
|
363
|
-
};
|
|
364
|
-
acc += amount;
|
|
365
|
-
this.slicesData.push(sliceData);
|
|
366
|
-
});
|
|
367
|
-
this.chartData = this.types[this.currentChartType];
|
|
368
|
-
}
|
|
369
|
-
handleResize() {
|
|
370
|
-
if (this.chartType === "hybrid") {
|
|
371
|
-
this.debouncedResize();
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
async componentWillLoad() {
|
|
375
|
-
if (!this.label) {
|
|
376
|
-
throw new Error("For accessibility purposes, you must provide a label for the chart. See https://components.watermarkinsights.com/chart for more information.");
|
|
377
|
-
}
|
|
378
|
-
if (this.showValues) {
|
|
379
|
-
console.warn("wm-chart: show-values has been deprecated as of v3.1.0. Please use value-format instead.");
|
|
380
|
-
}
|
|
381
|
-
if (this.chartType === "hybrid") {
|
|
382
|
-
this.setHybridType();
|
|
383
|
-
}
|
|
384
|
-
await this.getData();
|
|
385
|
-
}
|
|
386
|
-
handleSliceUpdate() {
|
|
387
|
-
this.debouncedSliceUpdate();
|
|
388
|
-
}
|
|
389
|
-
/* RENDERING */
|
|
390
|
-
renderFilter() {
|
|
391
|
-
return (h("defs", null, h("filter", { id: "wmHoverDropShadow" }, h("feGaussianBlur", { stdDeviation: "3" }), h("feOffset", { result: "offsetblur" }), h("feFlood", { "flood-color": "#333" }), h("feComposite", { operator: "in", in2: "offsetblur" }), h("feMerge", null, h("feMergeNode", null), h("feMergeNode", { in: "SourceGraphic" })))));
|
|
392
|
-
}
|
|
393
|
-
// DOUGHNUT
|
|
394
|
-
renderDoughnut() {
|
|
395
|
-
const outerSize = this.chartData.size + this.chartData.padding;
|
|
396
|
-
return (h("div", { class: "chart-wrapper doughnut-wrapper" }, h("svg", { width: outerSize + "px", height: outerSize + "px", ref: (el) => (this.doughnutEl = el), id: `graphic-${this.uid}`, class: "doughnut-svg" }, this.renderFilter(), this.slicesData.map((s) => this.renderPath(s)), this.currentChartType === "doughnut0" ? (h("text", { class: "value", x: "50%", y: "50%", "font-size": "1.5rem", "font-weight": "500", "text-anchor": "middle", "dominant-baseline": "middle" }, this.amountToPercent(this.slicesData[0].amount, true) + "%")) : (h("g", { transform: `translate(${this.chartData.padding / 2}, ${this.chartData.padding / 2})`, "text-anchor": "middle", "dominant-baseline": "middle" }, this.slicesData.map((s) => (s.amount > 0 ? this.renderDoughnutText(s) : "")))))));
|
|
397
|
-
}
|
|
398
|
-
renderPath(s) {
|
|
399
|
-
return (h("g", { transform: `translate(${this.chartData.padding / 2}, ${this.chartData.padding / 2})` }, h("path", { id: s.id, class: "doughnut-segment", fill: s.amount ? s.color : "transparent", d: this.getPathData(s.amount, s.offset), onClick: (ev) => this.handleSliceClick(ev, s), onFocus: (ev) => this.handleSliceFocus(ev, s), onKeyDown: (ev) => this.handleSliceKeyDown(ev), role: "img", "aria-label": s.legend })));
|
|
400
|
-
}
|
|
401
|
-
renderDoughnutText(s) {
|
|
402
|
-
if (!s.inSmallCluster) {
|
|
403
|
-
const arcMiddle = this.amountToDegree(s.offset + s.amount / 2);
|
|
404
|
-
let { x, y } = this.polarToCartesian(this.chartData.size / 2, 1.4, arcMiddle - 90);
|
|
405
|
-
return (h("text", { class: "value", x: x + "px", y: y + "px" }, `${s.perc > 0 ? s.perc : "<1"}%`));
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
// BAR
|
|
409
|
-
renderBar() {
|
|
410
|
-
return (h("div", { class: "chart-wrapper bar-wrapper" }, this.currentChartType === "bar1" ? (h("div", { class: "single-perc" }, this.amountToPercent(this.slicesData[0].amount, true) + "%")) : (""), this.drawAxis(), h("div", { class: {
|
|
411
|
-
"inner-bar-wrapper": true,
|
|
412
|
-
"show-values": this.tempValueFormat === "percentage" || this.tempValueFormat === "amount",
|
|
413
|
-
} }, this.currentChartType !== "bar1" ? (h("div", null, this.slicesData.map((s, idx) => (s.perc > 0 ? this.renderBarText(s, idx) : "")))) : (""), h("div", { class: "bar-segments-wrapper", ref: (el) => (this.barEl = el) }, this.slicesData.map((s, idx) => this.renderBarSegment(s, idx))), this.renderCompletionMessage())));
|
|
414
|
-
}
|
|
415
|
-
renderBarSegment(s, idx) {
|
|
416
|
-
const isLastSlice = idx !== this.slicesData.length - 1;
|
|
417
|
-
const width = `calc(${this.amountToPercent(s.amount, false)}%${isLastSlice ? " - 2px" : ""})`;
|
|
418
|
-
return (h("div", { class: `bar-segment ${this.amountToPercent(s.amount, false) === 0 ? "zero" : ""}`, style: {
|
|
419
|
-
backgroundColor: s.color,
|
|
420
|
-
width: width,
|
|
421
|
-
}, onClick: (ev) => this.handleSliceClick(ev, s), onFocus: (ev) => this.handleSliceFocus(ev, s), onKeyDown: (ev) => this.handleSliceKeyDown(ev) }, h("text", { class: "sr-only" }, s.legend)));
|
|
422
|
-
}
|
|
423
|
-
renderBarText(s, idx) {
|
|
424
|
-
let val;
|
|
425
|
-
if (this.tempValueFormat === "percentage") {
|
|
426
|
-
val = s.perc + "%";
|
|
427
|
-
}
|
|
428
|
-
else if (this.tempValueFormat === "amount") {
|
|
429
|
-
val = s.amount;
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
return (h("span", { class: "value", style: {
|
|
435
|
-
width: `calc(${this.amountToPercent(s.amount, false)}%
|
|
436
|
-
${idx !== this.slicesData.length - 1 ? " - 2px" : ""}`,
|
|
437
|
-
} }, val));
|
|
438
|
-
}
|
|
439
|
-
drawAxis() {
|
|
440
|
-
if (this.currentChartType === "bar3") {
|
|
441
|
-
return (h("svg", { class: "axis" }, h("line", { x1: "0", x2: "100%", y1: "0", y2: "0" }), h("line", { x1: "0", x2: "0", y1: "0", y2: "-85px" }), h("line", { class: "tick", x1: "0.5", x2: "0.5", y1: "0", y2: "6" }), h("text", { x: "0.5", y: "-6" }, "0%"), h("line", { class: "tick", x1: "100%", x2: "100%", y1: "0", y2: "6" }), h("text", { x: "100%", y: "-6" }, "100%")));
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
renderCompletionMessage() {
|
|
445
|
-
if (this.chartType === "hybrid" && this.completionMessage) {
|
|
446
|
-
return h("div", { class: "completion-message" }, this.completionMessage);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
renderLegend() {
|
|
450
|
-
// legend is hidden for bar1 type regardless of showLegend value
|
|
451
|
-
if (this.showLegend) {
|
|
452
|
-
const hasCluster = this.slicesData.reduce((hasCluster, cur) => (hasCluster = cur.inSmallCluster ? true : hasCluster), false);
|
|
453
|
-
return (h("div", { class: "legend-wrapper" }, h("div", { class: `legend ${this.chartData.isBar ? "--top" : "--bottom"}`, "aria-hidden": "true" }, this.total > 0
|
|
454
|
-
? this.slicesData.map((s) => {
|
|
455
|
-
// when both legend and amount are omitted, the legend is not shown for that particular option (it's been deactivated by the user)
|
|
456
|
-
if (!!s.amount || !!s.legend) {
|
|
457
|
-
return (h("div", { class: "legend-item" }, h("div", { class: "legend-color", style: { "background-color": s.color } }), h("div", { class: "legend-text" }, s.legend)));
|
|
458
|
-
}
|
|
459
|
-
})
|
|
460
|
-
: ""), !this.chartData.isBar && hasCluster ? (h("div", { class: "cluster-warning" }, intl.formatMessage({
|
|
461
|
-
id: "chart.hiddenPercentages",
|
|
462
|
-
defaultMessage: "Percentages smaller than 5% are not shown when too close to each other.",
|
|
463
|
-
}), h("br", null), intl.formatMessage({
|
|
464
|
-
id: "chart.clickToSeeDetails",
|
|
465
|
-
defaultMessage: "Click or use arrow keys to see details.",
|
|
466
|
-
}))) : ("")));
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
render() {
|
|
470
|
-
return (h(Host, { role: "application", "aria-label": this.label +
|
|
471
|
-
", " +
|
|
472
|
-
intl.formatMessage({
|
|
473
|
-
id: "chart.interactiveChart",
|
|
474
|
-
defaultMessage: "Interactive chart. Use arrow keys to browse elements, press Tab to exit.",
|
|
475
|
-
}), tabindex: "0" }, h("div", { class: `component-wrapper ${this.currentChartType} ${this.isTabbing ? "user-is-tabbing" : ""} ${this.labelPosition === "left" && this.chartType === "bar4" ? "left-label" : ""}` }, h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `graphic-${this.uid}` }, h("span", { class: "label-text" }, this.label), this.subinfo ? h("span", { class: "subinfo" }, this.subinfo) : ""), this.chartData.isBar ? this.renderLegend() : "", this.total > 0 && this.chartData.isBar ? this.renderBar() : this.renderDoughnut(), !this.chartData.isBar ? this.renderLegend() : "", h("priv-chart-popover", { class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.popoverEl = el) }), this.hybridType === "doughnut0" ? this.renderCompletionMessage() : "")));
|
|
476
|
-
}
|
|
477
|
-
get el() { return getElement(this); }
|
|
478
|
-
};
|
|
6
|
+
const Chart = class {
|
|
7
|
+
constructor(hostRef) {
|
|
8
|
+
registerInstance(this, hostRef);
|
|
9
|
+
this.chartType = "doughnut1";
|
|
10
|
+
this.showLegend = true;
|
|
11
|
+
this.notStartedColor = false;
|
|
12
|
+
// left labels are a temporary solution for the lack of a stacked bar chart and is undocumented
|
|
13
|
+
// it only works with bar4 and should be used without a legend
|
|
14
|
+
this.labelPosition = "top";
|
|
15
|
+
this.isTabbing = false;
|
|
16
|
+
this.uid = generateId();
|
|
17
|
+
this.slicesData = [];
|
|
18
|
+
this.colors = {
|
|
19
|
+
salmon: "#ff5f4e",
|
|
20
|
+
cyan: "#19a1a9",
|
|
21
|
+
forest: "#088000",
|
|
22
|
+
sleet: "#7f97ad",
|
|
23
|
+
midnight: "#2e1b46",
|
|
24
|
+
lavender: "#8b86ca",
|
|
25
|
+
};
|
|
26
|
+
this.types = {
|
|
27
|
+
doughnut0: {
|
|
28
|
+
size: 155,
|
|
29
|
+
colors: [this.colors.forest, "#bfbfbf"],
|
|
30
|
+
thickness: 0.73,
|
|
31
|
+
padding: 25,
|
|
32
|
+
isBar: false,
|
|
33
|
+
},
|
|
34
|
+
doughnut1: {
|
|
35
|
+
size: 130,
|
|
36
|
+
colors: [this.colors.lavender, this.colors.midnight, "#d4d4d4"],
|
|
37
|
+
thickness: 0.5,
|
|
38
|
+
padding: 90,
|
|
39
|
+
isBar: false,
|
|
40
|
+
},
|
|
41
|
+
doughnut2: {
|
|
42
|
+
size: 130,
|
|
43
|
+
colors: [this.colors.cyan, this.colors.salmon, "#d4d4d4"],
|
|
44
|
+
thickness: 0.5,
|
|
45
|
+
padding: 90,
|
|
46
|
+
isBar: false,
|
|
47
|
+
},
|
|
48
|
+
doughnut3: {
|
|
49
|
+
size: 130,
|
|
50
|
+
colors: [this.colors.lavender, this.colors.midnight, "#919834", "#c177cf", "#c16e00", "#029af2", "#2a6993"],
|
|
51
|
+
thickness: 0.5,
|
|
52
|
+
padding: 90,
|
|
53
|
+
isBar: false,
|
|
54
|
+
},
|
|
55
|
+
bar1: {
|
|
56
|
+
size: 350,
|
|
57
|
+
colors: [this.colors.forest, "#bfbfbf"],
|
|
58
|
+
padding: 0,
|
|
59
|
+
isBar: true,
|
|
60
|
+
},
|
|
61
|
+
bar2: {
|
|
62
|
+
size: 400,
|
|
63
|
+
colors: ["#d4d4d4", this.colors.sleet, this.colors.cyan, this.colors.salmon],
|
|
64
|
+
padding: 0,
|
|
65
|
+
isBar: true,
|
|
66
|
+
},
|
|
67
|
+
bar3: {
|
|
68
|
+
size: 300,
|
|
69
|
+
colors: ["#0d696e", this.colors.cyan, "#8e4129", this.colors.salmon],
|
|
70
|
+
padding: 0,
|
|
71
|
+
isBar: true,
|
|
72
|
+
},
|
|
73
|
+
bar4: {
|
|
74
|
+
size: 400,
|
|
75
|
+
colors: ["#d4d4d4", this.colors.sleet, "#33a919"],
|
|
76
|
+
padding: 0,
|
|
77
|
+
isBar: true,
|
|
78
|
+
},
|
|
79
|
+
bar5: {
|
|
80
|
+
size: 400,
|
|
81
|
+
colors: [
|
|
82
|
+
"#d4d4d4",
|
|
83
|
+
this.colors.lavender,
|
|
84
|
+
this.colors.midnight,
|
|
85
|
+
"#919834",
|
|
86
|
+
"#c177cf",
|
|
87
|
+
"#c16e00",
|
|
88
|
+
"#029af2",
|
|
89
|
+
"#2a6993",
|
|
90
|
+
],
|
|
91
|
+
padding: 0,
|
|
92
|
+
isBar: true,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
/* LIFECYCLE METHODS + EVENTS FROM THE CHILDREN */
|
|
96
|
+
this.debouncedResize = debounce(async () => {
|
|
97
|
+
this.setHybridType();
|
|
98
|
+
await this.getData();
|
|
99
|
+
forceUpdate(this.el);
|
|
100
|
+
}, 10);
|
|
101
|
+
this.debouncedSliceUpdate = debounce(async () => {
|
|
102
|
+
await this.getData();
|
|
103
|
+
forceUpdate(this.el);
|
|
104
|
+
}, 100);
|
|
105
|
+
}
|
|
106
|
+
get tempValueFormat() {
|
|
107
|
+
// use of this getter should be replaced with dateFormat when showValues is fully phased out
|
|
108
|
+
return this.valueFormat || this.showValues || "none";
|
|
109
|
+
}
|
|
110
|
+
get sliceEls() {
|
|
111
|
+
const isBarType = this.currentChartType.includes("bar");
|
|
112
|
+
const isDoughnutType = this.currentChartType.includes("doughnut");
|
|
113
|
+
return isBarType
|
|
114
|
+
? Array.from(this.barEl.querySelectorAll(".bar-segment"))
|
|
115
|
+
: isDoughnutType
|
|
116
|
+
? Array.from(this.doughnutEl.querySelectorAll(".doughnut-segment"))
|
|
117
|
+
: undefined;
|
|
118
|
+
}
|
|
119
|
+
get currentChartType() {
|
|
120
|
+
return this.chartType === "hybrid" ? this.hybridType : this.chartType;
|
|
121
|
+
}
|
|
122
|
+
toggleTabbingOn() {
|
|
123
|
+
this.isTabbing = true;
|
|
124
|
+
}
|
|
125
|
+
toggleTabbingOff() {
|
|
126
|
+
this.isTabbing = false;
|
|
127
|
+
}
|
|
128
|
+
handleKeydown(ev) {
|
|
129
|
+
switch (ev.keyCode) {
|
|
130
|
+
// arrow up / left
|
|
131
|
+
case 37:
|
|
132
|
+
case 38:
|
|
133
|
+
ev.preventDefault();
|
|
134
|
+
this.isTabbing = true; // shd already be true. just in case user clicked on chart then pressed an arrow key
|
|
135
|
+
this.focusPrevious();
|
|
136
|
+
break;
|
|
137
|
+
// arrow right / down
|
|
138
|
+
case 39:
|
|
139
|
+
case 40:
|
|
140
|
+
ev.preventDefault();
|
|
141
|
+
this.isTabbing = true; // shd already be true. just in case user clicked on chart then pressed an arrow key
|
|
142
|
+
this.focusNext();
|
|
143
|
+
break;
|
|
144
|
+
// tab
|
|
145
|
+
case 9:
|
|
146
|
+
this.exitChart();
|
|
147
|
+
break;
|
|
148
|
+
case 27:
|
|
149
|
+
this.popoverEl.open = false;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
handleSliceClick(ev, s) {
|
|
154
|
+
if (this.popoverEl && !this.isTabbing) {
|
|
155
|
+
s.coords = { x: ev.clientX, y: ev.clientY };
|
|
156
|
+
this.openPopover(s);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
handleSliceFocus(ev, s) {
|
|
160
|
+
if (this.popoverEl && this.isTabbing) {
|
|
161
|
+
s.coords = getPosition(ev.target);
|
|
162
|
+
this.openPopover(s);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
handleSliceKeyDown(ev) {
|
|
166
|
+
if (this.popoverEl && this.popoverEl.open && ev.key === "Enter") {
|
|
167
|
+
const popoverBtn = this.popoverEl.querySelector("button");
|
|
168
|
+
popoverBtn && popoverBtn.click();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
focusNext() {
|
|
172
|
+
const activeEl = checkForActiveElInShadow(document.activeElement);
|
|
173
|
+
const index =
|
|
174
|
+
// if the active el is not in the array the first element gets focused
|
|
175
|
+
(this.sliceEls.indexOf(activeEl) + 1) % this.sliceEls.length;
|
|
176
|
+
this.focusSlice(index);
|
|
177
|
+
}
|
|
178
|
+
focusPrevious() {
|
|
179
|
+
if (this.sliceEls) {
|
|
180
|
+
const activeEl = checkForActiveElInShadow(document.activeElement);
|
|
181
|
+
let index = this.sliceEls.indexOf(activeEl);
|
|
182
|
+
if (index === -1) {
|
|
183
|
+
// not in the array : focus the first slice
|
|
184
|
+
index = 0;
|
|
185
|
+
}
|
|
186
|
+
else if (index === 0) {
|
|
187
|
+
// first slice : focus the last slice
|
|
188
|
+
index = this.sliceEls.length - 1;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// anything else: focus previous
|
|
192
|
+
index -= 1;
|
|
193
|
+
}
|
|
194
|
+
this.focusSlice(index);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
focusSlice(index) {
|
|
198
|
+
if (this.sliceEls && this.el) {
|
|
199
|
+
if (this.popoverEl) {
|
|
200
|
+
this.popoverEl.open = false;
|
|
201
|
+
}
|
|
202
|
+
this.el.tabIndex = -1;
|
|
203
|
+
// @ts-ignore
|
|
204
|
+
this.el.focusable = false; // for Edge
|
|
205
|
+
this.sliceEls.map((p) => {
|
|
206
|
+
p.tabIndex = -1;
|
|
207
|
+
// @ts-ignore
|
|
208
|
+
p.focusable = false; // for Edge
|
|
209
|
+
});
|
|
210
|
+
this.sliceEls[index].tabIndex = 0;
|
|
211
|
+
// @ts-ignore
|
|
212
|
+
this.sliceEls[index].focusable = true; // for Edge
|
|
213
|
+
this.sliceEls[index].focus();
|
|
214
|
+
window.setTimeout(() => {
|
|
215
|
+
if (this.popoverEl) {
|
|
216
|
+
this.popoverEl.open = true;
|
|
217
|
+
}
|
|
218
|
+
}, 10);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
exitChart() {
|
|
222
|
+
this.sliceEls &&
|
|
223
|
+
this.sliceEls.map((p) => {
|
|
224
|
+
p.tabIndex = -1;
|
|
225
|
+
// @ts-ignore
|
|
226
|
+
p.focusable = false; // for Edge
|
|
227
|
+
});
|
|
228
|
+
// delay so that we can tab out of component before chart becomes focusable again
|
|
229
|
+
// and in case user was still pressing an arrow key when they pressed tab
|
|
230
|
+
window.setTimeout(() => {
|
|
231
|
+
if (this.el) {
|
|
232
|
+
this.el.tabIndex = 0;
|
|
233
|
+
// @ts-ignore
|
|
234
|
+
this.el.focusable = true; // for Edge
|
|
235
|
+
if (this.popoverEl) {
|
|
236
|
+
this.popoverEl.open = false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}, 100);
|
|
240
|
+
}
|
|
241
|
+
openPopover(s) {
|
|
242
|
+
if (!!this.popoverEl && !!s.title && !!s.text) {
|
|
243
|
+
this.popoverEl.popoverTitle = s.title;
|
|
244
|
+
this.popoverEl.popoverText = s.text;
|
|
245
|
+
this.popoverEl.buttonText = s.buttonText;
|
|
246
|
+
this.popoverEl.coords = s.coords;
|
|
247
|
+
this.popoverEl.sliceRef = s.sliceRef;
|
|
248
|
+
window.setTimeout(() => {
|
|
249
|
+
if (this.popoverEl) {
|
|
250
|
+
this.popoverEl.open = true;
|
|
251
|
+
}
|
|
252
|
+
}, 30);
|
|
253
|
+
const debouncedClosePopover = debounce(async () => {
|
|
254
|
+
this.popoverEl.open = false;
|
|
255
|
+
}, 10);
|
|
256
|
+
// set up event listeners for scrolling
|
|
257
|
+
// to close popover on page scroll
|
|
258
|
+
document.addEventListener("scroll", () => {
|
|
259
|
+
debouncedClosePopover();
|
|
260
|
+
});
|
|
261
|
+
// ... and on parent scroll
|
|
262
|
+
const scrollableParent = findParentWithScrollbar(this.el);
|
|
263
|
+
if (!!scrollableParent) {
|
|
264
|
+
scrollableParent.addEventListener("scroll", () => {
|
|
265
|
+
debouncedClosePopover();
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/* UTILS */
|
|
271
|
+
amountToPercent(val, asInt) {
|
|
272
|
+
return asInt ? Math.round((val * 100) / this.total) : Math.round((val * 10000) / this.total) / 100; // with 2 decimals
|
|
273
|
+
}
|
|
274
|
+
amountToDegree(val) {
|
|
275
|
+
return (val * 360) / this.total;
|
|
276
|
+
}
|
|
277
|
+
toFixed(number) {
|
|
278
|
+
var val = parseFloat((Math.floor(number * 100) / 100).toFixed(2));
|
|
279
|
+
return val;
|
|
280
|
+
}
|
|
281
|
+
polarToCartesian(half, radius, startAngle, endAngle) {
|
|
282
|
+
var x = this.toFixed(half + half * radius * Math.cos((Math.PI * startAngle) / 180));
|
|
283
|
+
var y = this.toFixed(half + half * radius * Math.sin((Math.PI * startAngle) / 180));
|
|
284
|
+
if (endAngle !== undefined) {
|
|
285
|
+
// if a 2nd angle value was passed, return 2 pairs of coords
|
|
286
|
+
var x2 = this.toFixed(half + half * radius * Math.cos((Math.PI * endAngle) / 180));
|
|
287
|
+
var y2 = this.toFixed(half + half * radius * Math.sin((Math.PI * endAngle) / 180));
|
|
288
|
+
return { x1: x, y1: y, x2, y2 };
|
|
289
|
+
}
|
|
290
|
+
return { x, y };
|
|
291
|
+
}
|
|
292
|
+
/* CRUNCH THE NUMBERS */
|
|
293
|
+
getPathData(amount, offset) {
|
|
294
|
+
var startAngle = this.amountToDegree(offset) - 90; // start at noon, not at 3 o'clock
|
|
295
|
+
var activeAngle = (amount / this.total) * 360;
|
|
296
|
+
var endAngle = startAngle + activeAngle;
|
|
297
|
+
var largeArcFlagOuter = activeAngle > 180 ? "1 1" : "0 1";
|
|
298
|
+
var largeArcFlagInner = activeAngle > 180 ? "1 0" : "0 0";
|
|
299
|
+
var half = this.chartData.size / 2;
|
|
300
|
+
var innerRadius = this.chartData.thickness;
|
|
301
|
+
var outerRadius = 1;
|
|
302
|
+
if (activeAngle === 360) {
|
|
303
|
+
// fix to avoid bad svg shape when the path goes all around (100%)
|
|
304
|
+
endAngle -= 0.01;
|
|
305
|
+
}
|
|
306
|
+
var outerCoords = this.polarToCartesian(half, outerRadius, startAngle + 1.5, // 1.5 for slice separator
|
|
307
|
+
endAngle);
|
|
308
|
+
var innerCoords = this.polarToCartesian(half, innerRadius, startAngle + 3, // 3 for slice separator
|
|
309
|
+
endAngle);
|
|
310
|
+
const moveTo = `M ${outerCoords.x1}, ${outerCoords.y1} `;
|
|
311
|
+
const arc1 = this.getArc(outerRadius, largeArcFlagOuter, outerCoords.x2, outerCoords.y2);
|
|
312
|
+
const line = ` L ${innerCoords.x2}, ${innerCoords.y2} `;
|
|
313
|
+
const arc2 = this.getArc(innerRadius, largeArcFlagInner, innerCoords.x1, innerCoords.y1);
|
|
314
|
+
return moveTo + arc1 + line + arc2 + " z";
|
|
315
|
+
}
|
|
316
|
+
getArc(radius, largeArcFlag, x, y) {
|
|
317
|
+
var z = this.toFixed((this.chartData.size / 2) * radius);
|
|
318
|
+
return `A ${z}, ${z} 0 ${largeArcFlag} ${this.toFixed(x)}, ${this.toFixed(y)}`;
|
|
319
|
+
}
|
|
320
|
+
setHybridType() {
|
|
321
|
+
this.hybridType = window.innerWidth > 1340 ? "doughnut0" : "bar1";
|
|
322
|
+
}
|
|
323
|
+
/* GET THE DATA */
|
|
324
|
+
async getData() {
|
|
325
|
+
this.slicesData = [];
|
|
326
|
+
let acc = 0;
|
|
327
|
+
const children = this.el.querySelectorAll("wm-chart-slice");
|
|
328
|
+
this.total = Array.from(children).reduce((total, slice) => (total += parseInt(slice.getAttribute("amount") || "0")), 0);
|
|
329
|
+
children.forEach((c, i) => {
|
|
330
|
+
const amount = parseInt(c.getAttribute("amount") || "0");
|
|
331
|
+
const perc = this.amountToPercent(amount, true);
|
|
332
|
+
// determine whether the slice is in a cluster of small values
|
|
333
|
+
// to avoid percentage text overlap for small values
|
|
334
|
+
const prev = children[i === 0 ? children.length - 1 : i - 1];
|
|
335
|
+
const prevPerc = this.amountToPercent(parseInt(prev.getAttribute("amount") || "0"), true);
|
|
336
|
+
const next = children[i === children.length - 1 ? 0 : i + 1];
|
|
337
|
+
const nextPerc = this.amountToPercent(parseInt(next.getAttribute("amount") || "0"), true);
|
|
338
|
+
const isSmall = perc < 4;
|
|
339
|
+
const prevIsSmall = prevPerc < 5;
|
|
340
|
+
const nextIsSmall = nextPerc < 5;
|
|
341
|
+
let inSmallCluster = isSmall && (prevIsSmall || nextIsSmall);
|
|
342
|
+
// because <1% slice percentage text has an additional character
|
|
343
|
+
// the inSmallCluster threshold needs to be widened for that slice only
|
|
344
|
+
const lessThanOnePerc = perc === 0 && amount > 0;
|
|
345
|
+
if (lessThanOnePerc && (nextPerc < 8 || prevPerc < 8)) {
|
|
346
|
+
inSmallCluster = true;
|
|
347
|
+
}
|
|
348
|
+
// for bar5, first color should be skipped unless notStartedColor is set to true
|
|
349
|
+
const ind = this.currentChartType === "bar5" ? (this.notStartedColor ? i : i + 1) : i;
|
|
350
|
+
const color = this.types[this.currentChartType].colors[ind];
|
|
351
|
+
const sliceData = {
|
|
352
|
+
amount: amount,
|
|
353
|
+
perc: perc,
|
|
354
|
+
legend: c.getAttribute("legend"),
|
|
355
|
+
color: color || "#d4d4d4",
|
|
356
|
+
offset: acc,
|
|
357
|
+
id: `${this.uid}-${i + 1}`,
|
|
358
|
+
title: c.getAttribute("popover-title"),
|
|
359
|
+
text: c.getAttribute("popover-text"),
|
|
360
|
+
buttonText: c.getAttribute("popover-button-text"),
|
|
361
|
+
sliceRef: c,
|
|
362
|
+
inSmallCluster: inSmallCluster,
|
|
363
|
+
};
|
|
364
|
+
acc += amount;
|
|
365
|
+
this.slicesData.push(sliceData);
|
|
366
|
+
});
|
|
367
|
+
this.chartData = this.types[this.currentChartType];
|
|
368
|
+
}
|
|
369
|
+
handleResize() {
|
|
370
|
+
if (this.chartType === "hybrid") {
|
|
371
|
+
this.debouncedResize();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async componentWillLoad() {
|
|
375
|
+
if (!this.label) {
|
|
376
|
+
throw new Error("For accessibility purposes, you must provide a label for the chart. See https://components.watermarkinsights.com/chart for more information.");
|
|
377
|
+
}
|
|
378
|
+
if (this.showValues) {
|
|
379
|
+
console.warn("wm-chart: show-values has been deprecated as of v3.1.0. Please use value-format instead.");
|
|
380
|
+
}
|
|
381
|
+
if (this.chartType === "hybrid") {
|
|
382
|
+
this.setHybridType();
|
|
383
|
+
}
|
|
384
|
+
await this.getData();
|
|
385
|
+
}
|
|
386
|
+
handleSliceUpdate() {
|
|
387
|
+
this.debouncedSliceUpdate();
|
|
388
|
+
}
|
|
389
|
+
/* RENDERING */
|
|
390
|
+
renderFilter() {
|
|
391
|
+
return (h("defs", null, h("filter", { id: "wmHoverDropShadow" }, h("feGaussianBlur", { stdDeviation: "3" }), h("feOffset", { result: "offsetblur" }), h("feFlood", { "flood-color": "#333" }), h("feComposite", { operator: "in", in2: "offsetblur" }), h("feMerge", null, h("feMergeNode", null), h("feMergeNode", { in: "SourceGraphic" })))));
|
|
392
|
+
}
|
|
393
|
+
// DOUGHNUT
|
|
394
|
+
renderDoughnut() {
|
|
395
|
+
const outerSize = this.chartData.size + this.chartData.padding;
|
|
396
|
+
return (h("div", { class: "chart-wrapper doughnut-wrapper" }, h("svg", { width: outerSize + "px", height: outerSize + "px", ref: (el) => (this.doughnutEl = el), id: `graphic-${this.uid}`, class: "doughnut-svg" }, this.renderFilter(), this.slicesData.map((s) => this.renderPath(s)), this.currentChartType === "doughnut0" ? (h("text", { class: "value", x: "50%", y: "50%", "font-size": "1.5rem", "font-weight": "500", "text-anchor": "middle", "dominant-baseline": "middle" }, this.amountToPercent(this.slicesData[0].amount, true) + "%")) : (h("g", { transform: `translate(${this.chartData.padding / 2}, ${this.chartData.padding / 2})`, "text-anchor": "middle", "dominant-baseline": "middle" }, this.slicesData.map((s) => (s.amount > 0 ? this.renderDoughnutText(s) : "")))))));
|
|
397
|
+
}
|
|
398
|
+
renderPath(s) {
|
|
399
|
+
return (h("g", { transform: `translate(${this.chartData.padding / 2}, ${this.chartData.padding / 2})` }, h("path", { id: s.id, class: "doughnut-segment", fill: s.amount ? s.color : "transparent", d: this.getPathData(s.amount, s.offset), onClick: (ev) => this.handleSliceClick(ev, s), onFocus: (ev) => this.handleSliceFocus(ev, s), onKeyDown: (ev) => this.handleSliceKeyDown(ev), role: "img", "aria-label": s.legend })));
|
|
400
|
+
}
|
|
401
|
+
renderDoughnutText(s) {
|
|
402
|
+
if (!s.inSmallCluster) {
|
|
403
|
+
const arcMiddle = this.amountToDegree(s.offset + s.amount / 2);
|
|
404
|
+
let { x, y } = this.polarToCartesian(this.chartData.size / 2, 1.4, arcMiddle - 90);
|
|
405
|
+
return (h("text", { class: "value", x: x + "px", y: y + "px" }, `${s.perc > 0 ? s.perc : "<1"}%`));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// BAR
|
|
409
|
+
renderBar() {
|
|
410
|
+
return (h("div", { class: "chart-wrapper bar-wrapper" }, this.currentChartType === "bar1" ? (h("div", { class: "single-perc" }, this.amountToPercent(this.slicesData[0].amount, true) + "%")) : (""), this.drawAxis(), h("div", { class: {
|
|
411
|
+
"inner-bar-wrapper": true,
|
|
412
|
+
"show-values": this.tempValueFormat === "percentage" || this.tempValueFormat === "amount",
|
|
413
|
+
} }, this.currentChartType !== "bar1" ? (h("div", null, this.slicesData.map((s, idx) => (s.perc > 0 ? this.renderBarText(s, idx) : "")))) : (""), h("div", { class: "bar-segments-wrapper", ref: (el) => (this.barEl = el) }, this.slicesData.map((s, idx) => this.renderBarSegment(s, idx))), this.renderCompletionMessage())));
|
|
414
|
+
}
|
|
415
|
+
renderBarSegment(s, idx) {
|
|
416
|
+
const isLastSlice = idx !== this.slicesData.length - 1;
|
|
417
|
+
const width = `calc(${this.amountToPercent(s.amount, false)}%${isLastSlice ? " - 2px" : ""})`;
|
|
418
|
+
return (h("div", { class: `bar-segment ${this.amountToPercent(s.amount, false) === 0 ? "zero" : ""}`, style: {
|
|
419
|
+
backgroundColor: s.color,
|
|
420
|
+
width: width,
|
|
421
|
+
}, onClick: (ev) => this.handleSliceClick(ev, s), onFocus: (ev) => this.handleSliceFocus(ev, s), onKeyDown: (ev) => this.handleSliceKeyDown(ev) }, h("text", { class: "sr-only" }, s.legend)));
|
|
422
|
+
}
|
|
423
|
+
renderBarText(s, idx) {
|
|
424
|
+
let val;
|
|
425
|
+
if (this.tempValueFormat === "percentage") {
|
|
426
|
+
val = s.perc + "%";
|
|
427
|
+
}
|
|
428
|
+
else if (this.tempValueFormat === "amount") {
|
|
429
|
+
val = s.amount;
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
return (h("span", { class: "value", style: {
|
|
435
|
+
width: `calc(${this.amountToPercent(s.amount, false)}%
|
|
436
|
+
${idx !== this.slicesData.length - 1 ? " - 2px" : ""}`,
|
|
437
|
+
} }, val));
|
|
438
|
+
}
|
|
439
|
+
drawAxis() {
|
|
440
|
+
if (this.currentChartType === "bar3") {
|
|
441
|
+
return (h("svg", { class: "axis" }, h("line", { x1: "0", x2: "100%", y1: "0", y2: "0" }), h("line", { x1: "0", x2: "0", y1: "0", y2: "-85px" }), h("line", { class: "tick", x1: "0.5", x2: "0.5", y1: "0", y2: "6" }), h("text", { x: "0.5", y: "-6" }, "0%"), h("line", { class: "tick", x1: "100%", x2: "100%", y1: "0", y2: "6" }), h("text", { x: "100%", y: "-6" }, "100%")));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
renderCompletionMessage() {
|
|
445
|
+
if (this.chartType === "hybrid" && this.completionMessage) {
|
|
446
|
+
return h("div", { class: "completion-message" }, this.completionMessage);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
renderLegend() {
|
|
450
|
+
// legend is hidden for bar1 type regardless of showLegend value
|
|
451
|
+
if (this.showLegend) {
|
|
452
|
+
const hasCluster = this.slicesData.reduce((hasCluster, cur) => (hasCluster = cur.inSmallCluster ? true : hasCluster), false);
|
|
453
|
+
return (h("div", { class: "legend-wrapper" }, h("div", { class: `legend ${this.chartData.isBar ? "--top" : "--bottom"}`, "aria-hidden": "true" }, this.total > 0
|
|
454
|
+
? this.slicesData.map((s) => {
|
|
455
|
+
// when both legend and amount are omitted, the legend is not shown for that particular option (it's been deactivated by the user)
|
|
456
|
+
if (!!s.amount || !!s.legend) {
|
|
457
|
+
return (h("div", { class: "legend-item" }, h("div", { class: "legend-color", style: { "background-color": s.color } }), h("div", { class: "legend-text" }, s.legend)));
|
|
458
|
+
}
|
|
459
|
+
})
|
|
460
|
+
: ""), !this.chartData.isBar && hasCluster ? (h("div", { class: "cluster-warning" }, intl.formatMessage({
|
|
461
|
+
id: "chart.hiddenPercentages",
|
|
462
|
+
defaultMessage: "Percentages smaller than 5% are not shown when too close to each other.",
|
|
463
|
+
}), h("br", null), intl.formatMessage({
|
|
464
|
+
id: "chart.clickToSeeDetails",
|
|
465
|
+
defaultMessage: "Click or use arrow keys to see details.",
|
|
466
|
+
}))) : ("")));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
render() {
|
|
470
|
+
return (h(Host, { role: "application", "aria-label": this.label +
|
|
471
|
+
", " +
|
|
472
|
+
intl.formatMessage({
|
|
473
|
+
id: "chart.interactiveChart",
|
|
474
|
+
defaultMessage: "Interactive chart. Use arrow keys to browse elements, press Tab to exit.",
|
|
475
|
+
}), tabindex: "0" }, h("div", { class: `component-wrapper ${this.currentChartType} ${this.isTabbing ? "user-is-tabbing" : ""} ${this.labelPosition === "left" && this.chartType === "bar4" ? "left-label" : ""}` }, h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `graphic-${this.uid}` }, h("span", { class: "label-text" }, this.label), this.subinfo ? h("span", { class: "subinfo" }, this.subinfo) : ""), this.chartData.isBar ? this.renderLegend() : "", this.total > 0 && this.chartData.isBar ? this.renderBar() : this.renderDoughnut(), !this.chartData.isBar ? this.renderLegend() : "", h("priv-chart-popover", { class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.popoverEl = el) }), this.hybridType === "doughnut0" ? this.renderCompletionMessage() : "")));
|
|
476
|
+
}
|
|
477
|
+
get el() { return getElement(this); }
|
|
478
|
+
};
|
|
479
479
|
Chart.style = wmChartCss;
|
|
480
480
|
|
|
481
481
|
export { Chart as wm_chart };
|