@watermarkinsights/ripple 3.4.0-2 → 3.4.0-3
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-a6491ba8.js +7943 -0
- package/dist/cjs/{global-ea9e87be.js → global-4d3b13c3.js} +65 -65
- package/dist/cjs/{http-service-494d81de.js → http-service-9e8c4dd5.js} +49 -49
- package/dist/cjs/{index-cd179539.js → index-921ef454.js} +103 -157
- package/dist/cjs/{interfaces-a3338581.js → interfaces-30a74c1f.js} +29 -29
- package/dist/cjs/loader.cjs.js +3 -3
- package/dist/cjs/priv-chart-popover.cjs.entry.js +79 -79
- package/dist/cjs/priv-datepicker.cjs.entry.js +668 -668
- package/dist/cjs/priv-navigator-button.cjs.entry.js +19 -19
- package/dist/cjs/priv-navigator-item.cjs.entry.js +24 -24
- package/dist/cjs/ripple.cjs.js +5 -5
- package/dist/cjs/wm-action-menu_2.cjs.entry.js +342 -342
- package/dist/cjs/wm-button.cjs.entry.js +219 -219
- package/dist/cjs/wm-chart-slice.cjs.entry.js +14 -14
- package/dist/cjs/wm-chart.cjs.entry.js +516 -516
- package/dist/cjs/wm-datepicker.cjs.entry.js +257 -257
- package/dist/cjs/wm-input.cjs.entry.js +134 -134
- package/dist/cjs/wm-modal-footer.cjs.entry.js +32 -32
- package/dist/cjs/wm-modal-header.cjs.entry.js +35 -35
- package/dist/cjs/wm-modal.cjs.entry.js +162 -162
- package/dist/cjs/wm-navigator.cjs.entry.js +269 -269
- package/dist/cjs/wm-network-uploader.cjs.entry.js +421 -421
- package/dist/cjs/wm-option_2.cjs.entry.js +492 -492
- package/dist/cjs/wm-pagination.cjs.entry.js +168 -168
- package/dist/cjs/wm-search.cjs.entry.js +232 -232
- package/dist/cjs/wm-snackbar.cjs.entry.js +171 -171
- package/dist/cjs/wm-tab-item_3.cjs.entry.js +301 -301
- package/dist/cjs/wm-tag-input.cjs.entry.js +557 -557
- package/dist/cjs/wm-timepicker.cjs.entry.js +384 -384
- package/dist/cjs/wm-toggletip.cjs.entry.js +125 -125
- package/dist/cjs/wm-uploader.cjs.entry.js +347 -347
- package/dist/cjs/wm-wrapper.cjs.entry.js +13 -13
- package/dist/collection/collection-manifest.json +2 -2
- package/dist/collection/components/wm-action-menu/wm-action-menu.js +460 -460
- package/dist/collection/components/wm-button/wm-button.js +485 -485
- 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 +765 -765
- package/dist/collection/components/wm-datepicker/priv-datepicker/priv-datepicker.js +1015 -1015
- package/dist/collection/components/wm-datepicker/wm-datepicker.js +462 -462
- 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 +479 -479
- package/dist/collection/components/wm-select/wm-select.js +717 -717
- package/dist/collection/components/wm-snackbar/wm-snackbar.js +297 -297
- package/dist/collection/components/wm-tabs/wm-tab-item/wm-tab-item.js +219 -219
- package/dist/collection/components/wm-tabs/wm-tab-list/wm-tab-list.js +331 -331
- 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 +787 -787
- package/dist/collection/components/wm-timepicker/wm-timepicker.js +589 -589
- 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/piglatin.js +93 -93
- package/dist/esm/functions-5aebd578.js +7923 -0
- package/dist/esm/{global-c7a1f76c.js → global-a335f845.js} +65 -65
- package/dist/esm/{http-service-3dc3b3e7.js → http-service-5d037e16.js} +49 -49
- package/dist/esm/{index-66f8130e.js → index-f8b130b6.js} +103 -157
- package/dist/esm/{interfaces-2b97fab2.js → interfaces-61c6305b.js} +29 -29
- package/dist/esm/loader.js +3 -3
- 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 +2 -2
- package/dist/esm/polyfills/system.js +0 -0
- package/dist/esm/priv-chart-popover.entry.js +79 -79
- package/dist/esm/priv-datepicker.entry.js +668 -668
- package/dist/esm/priv-navigator-button.entry.js +19 -19
- package/dist/esm/priv-navigator-item.entry.js +24 -24
- package/dist/esm/ripple.js +5 -5
- package/dist/esm/wm-action-menu_2.entry.js +342 -342
- package/dist/esm/wm-button.entry.js +219 -219
- package/dist/esm/wm-chart-slice.entry.js +14 -14
- package/dist/esm/wm-chart.entry.js +516 -516
- package/dist/esm/wm-datepicker.entry.js +257 -257
- package/dist/esm/wm-input.entry.js +134 -134
- package/dist/esm/wm-modal-footer.entry.js +32 -32
- package/dist/esm/wm-modal-header.entry.js +35 -35
- package/dist/esm/wm-modal.entry.js +162 -162
- package/dist/esm/wm-navigator.entry.js +269 -269
- package/dist/esm/wm-network-uploader.entry.js +421 -421
- package/dist/esm/wm-option_2.entry.js +492 -492
- package/dist/esm/wm-pagination.entry.js +168 -168
- package/dist/esm/wm-search.entry.js +232 -232
- package/dist/esm/wm-snackbar.entry.js +171 -171
- package/dist/esm/wm-tab-item_3.entry.js +301 -301
- package/dist/esm/wm-tag-input.entry.js +557 -557
- package/dist/esm/wm-timepicker.entry.js +384 -384
- package/dist/esm/wm-toggletip.entry.js +125 -125
- package/dist/esm/wm-uploader.entry.js +347 -347
- package/dist/esm/wm-wrapper.entry.js +13 -13
- package/dist/ripple/p-0865e7cf.js +16 -0
- package/dist/ripple/p-0f663e59.entry.js +1 -0
- package/dist/ripple/p-1b790a7d.entry.js +1 -0
- package/dist/ripple/{p-99db8501.entry.js → p-22a1a636.entry.js} +1 -1
- package/dist/ripple/p-3b4c01b8.entry.js +1 -0
- package/dist/ripple/p-54c1f704.entry.js +1 -0
- package/dist/ripple/{p-4cc71463.entry.js → p-5e49aba7.entry.js} +1 -1
- package/dist/ripple/p-76c615e9.entry.js +1 -0
- package/dist/ripple/{p-b5189f72.entry.js → p-7d009793.entry.js} +1 -1
- package/dist/ripple/p-7d993d52.js +1 -0
- package/dist/ripple/p-8198cc9c.entry.js +1 -0
- package/dist/ripple/p-839d35bd.entry.js +1 -0
- package/dist/ripple/p-8708b8cb.entry.js +1 -0
- package/dist/ripple/{p-43f1298b.js → p-888bec42.js} +0 -0
- package/dist/ripple/p-8bd66955.entry.js +1 -0
- package/dist/ripple/p-957e3a05.entry.js +1 -0
- package/dist/ripple/{p-b3d5ea85.entry.js → p-a61977b2.entry.js} +1 -1
- package/dist/ripple/{p-fd8070fb.js → p-a6d6eae7.js} +0 -0
- package/dist/ripple/p-af6eb4d5.entry.js +1 -0
- package/dist/ripple/p-b3f9f575.entry.js +1 -0
- package/dist/ripple/{p-cd58a15c.entry.js → p-bba99fcd.entry.js} +1 -1
- package/dist/ripple/{p-0c58f50d.entry.js → p-c995acbc.entry.js} +1 -1
- package/dist/ripple/p-cd30198d.entry.js +1 -0
- package/dist/ripple/{p-8b0eb05e.entry.js → p-d52033d5.entry.js} +1 -1
- package/dist/ripple/p-e097c562.entry.js +1 -0
- package/dist/ripple/{p-d298bf82.js → p-e8993d49.js} +1 -1
- package/dist/ripple/p-e9db6122.entry.js +1 -0
- package/dist/ripple/p-eabbd1e9.entry.js +1 -0
- package/dist/ripple/p-ec496b8b.entry.js +1 -0
- package/dist/ripple/p-fc8ec142.entry.js +1 -0
- 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 +80 -80
- 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 +91 -91
- package/dist/types/components/wm-timepicker/wm-timepicker.d.ts +62 -62
- 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/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/dist/types/stencil-public-runtime.d.ts +186 -194
- package/package.json +46 -46
- package/dist/cjs/functions-653e695c.js +0 -6164
- package/dist/esm/functions-e528c934.js +0 -6144
- package/dist/ripple/p-092b01f3.entry.js +0 -1
- package/dist/ripple/p-11c09317.entry.js +0 -1
- package/dist/ripple/p-1e0c41a9.entry.js +0 -1
- package/dist/ripple/p-3003d26d.entry.js +0 -1
- package/dist/ripple/p-33524565.entry.js +0 -1
- package/dist/ripple/p-588b4475.js +0 -16
- package/dist/ripple/p-65e3a656.entry.js +0 -1
- package/dist/ripple/p-8923b7d0.entry.js +0 -1
- package/dist/ripple/p-8cd1396e.entry.js +0 -1
- package/dist/ripple/p-9baa3039.js +0 -1
- package/dist/ripple/p-aa973691.entry.js +0 -1
- package/dist/ripple/p-bc27b604.entry.js +0 -1
- package/dist/ripple/p-bc9ca97b.entry.js +0 -1
- package/dist/ripple/p-bcb41945.entry.js +0 -1
- package/dist/ripple/p-dc9c9fda.entry.js +0 -1
- package/dist/ripple/p-e7616311.entry.js +0 -1
- package/dist/ripple/p-e9e8334e.entry.js +0 -1
- package/dist/ripple/p-ec9697db.entry.js +0 -1
- package/dist/ripple/p-efdaf3b6.entry.js +0 -1
- package/dist/ripple/p-fcdc6395.entry.js +0 -1
- package/dist/types/global/functions.d.ts +0 -40
|
@@ -2,525 +2,525 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
const index = require('./index-
|
|
6
|
-
const functions = require('./functions-
|
|
5
|
+
const index = require('./index-921ef454.js');
|
|
6
|
+
const functions = require('./functions-a6491ba8.js');
|
|
7
7
|
|
|
8
8
|
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 .chart-svg{overflow:visible}.component-wrapper .chart-svg:active,.component-wrapper .chart-svg:hover,.component-wrapper .chart-svg:focus{outline:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.component-wrapper .chart-svg::-moz-focus-inner{border:0;outline:none}.component-wrapper .chart-svg path:active,.component-wrapper .chart-svg path:hover,.component-wrapper .chart-svg path:focus,.component-wrapper .chart-svg rect:active,.component-wrapper .chart-svg rect:hover,.component-wrapper .chart-svg rect:focus{outline:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:url(#wmHoverDropShadow)}.component-wrapper .chart-svg path::-moz-focus-inner,.component-wrapper .chart-svg rect::-moz-focus-inner{border:0;outline:none}.component-wrapper .bar-wrapper{flex-grow:1;width:100%}.component-wrapper .bar-wrapper rect{height:30px}.component-wrapper .doughnut-wrapper .chart-svg{-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px}.component-wrapper .completion-message-wrapper{position:relative;flex:1}.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 .bar-svg{width:100%}.component-wrapper .bar-svg foreignObject{width:100%;height:30px;overflow:visible}.component-wrapper.bar1{padding:1.25rem;position:relative;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 .completion-message{position:absolute;right:0;top:30px;margin-top:4px}.component-wrapper.bar1 .bar-wrapper{display:flex;align-items:center}.component-wrapper.bar1 .bar-wrapper .single-perc{width:4rem;flex:none}.component-wrapper.bar1 .bar-wrapper .chart-svg{flex:1;height:30px}@media screen and (min-width: 768px){.component-wrapper.bar1{flex-direction:row;align-items:center}.component-wrapper.bar1 label{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}}.component-wrapper.bar2,.component-wrapper.bar3,.component-wrapper.bar4,.component-wrapper.bar5{align-items:flex-start}.component-wrapper.bar2 .legend,.component-wrapper.bar4 .legend,.component-wrapper.bar5 .legend{padding-bottom:0}.component-wrapper.bar2 .chart-svg,.component-wrapper.bar4 .chart-svg,.component-wrapper.bar5 .chart-svg{height:30px;margin-top:20px;margin-bottom:0}.component-wrapper.bar2 .chart-svg.show-values,.component-wrapper.bar4 .chart-svg.show-values,.component-wrapper.bar5 .chart-svg.show-values{height:60px;margin-top:0;margin-bottom:0}.component-wrapper.bar2 .chart-svg .percs,.component-wrapper.bar4 .chart-svg .percs,.component-wrapper.bar5 .chart-svg .percs{transform:translateY(24px);text-anchor:middle}.component-wrapper.bar3 .legend{padding-bottom:1.25rem}.component-wrapper.bar3 .svg-wrapper{position:relative;height:100px}.component-wrapper.bar3 .svg-wrapper .chart-svg{position:absolute;top:35px;left:0;right:0;bottom:0;height:30px}.component-wrapper.bar3 .svg-wrapper .chart-svg text{fill:#4a4a4a}.component-wrapper.bar3 .svg-wrapper .chart-svg .percs{transform:translateY(-4px);text-anchor:middle}.component-wrapper.bar3 .svg-wrapper .axis{position:absolute;top:0;left:0;height:90px;width:100%;overflow:visible;transform:translateY(90px)}.component-wrapper.bar3 .svg-wrapper .axis line{stroke:#eeedf4;stroke-width:1px}.component-wrapper.bar3 .svg-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) .chart-svg.user-is-tabbing:not(.show-values){-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) .chart-svg.user-is-tabbing:not(.show-values)::-moz-focus-inner{border:0}:host(:focus) .chart-svg.user-is-tabbing.show-values .tabbing-focus{-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;width:100%;height:30px}:host(:focus) .chart-svg.user-is-tabbing.show-values .tabbing-focus::-moz-focus-inner{border:0}:host(:focus) .chart-svg.user-is-tabbing.show-values .focus-offset{transform:translateY(30px)}";
|
|
9
9
|
|
|
10
|
-
const Chart = class {
|
|
11
|
-
constructor(hostRef) {
|
|
12
|
-
index.registerInstance(this, hostRef);
|
|
13
|
-
this.chartType = "doughnut1";
|
|
14
|
-
this.showLegend = true;
|
|
15
|
-
this.notStartedColor = false;
|
|
16
|
-
// left labels are a temporary solution for the lack of a stacked bar chart and is undocumented
|
|
17
|
-
// it only works with bar4 and should be used without a legend
|
|
18
|
-
this.labelPosition = "top";
|
|
19
|
-
this.isTabbing = false;
|
|
20
|
-
this.uid = functions.generateId();
|
|
21
|
-
this.slicesData = [];
|
|
22
|
-
this.colors = {
|
|
23
|
-
salmon: "#ff5f4e",
|
|
24
|
-
cyan: "#19a1a9",
|
|
25
|
-
sleet: "#7f97ad",
|
|
26
|
-
midnight: "#2e1b46",
|
|
27
|
-
lavender: "#8b86ca",
|
|
28
|
-
};
|
|
29
|
-
this.types = {
|
|
30
|
-
doughnut0: {
|
|
31
|
-
size: 155,
|
|
32
|
-
colors: [this.colors.cyan, "#bfbfbf"],
|
|
33
|
-
thickness: 0.73,
|
|
34
|
-
padding: 25,
|
|
35
|
-
isBar: false,
|
|
36
|
-
},
|
|
37
|
-
doughnut1: {
|
|
38
|
-
size: 130,
|
|
39
|
-
colors: [this.colors.lavender, this.colors.midnight, "#d4d4d4"],
|
|
40
|
-
thickness: 0.5,
|
|
41
|
-
padding: 90,
|
|
42
|
-
isBar: false,
|
|
43
|
-
},
|
|
44
|
-
doughnut2: {
|
|
45
|
-
size: 130,
|
|
46
|
-
colors: [this.colors.cyan, this.colors.salmon, "#d4d4d4"],
|
|
47
|
-
thickness: 0.5,
|
|
48
|
-
padding: 90,
|
|
49
|
-
isBar: false,
|
|
50
|
-
},
|
|
51
|
-
doughnut3: {
|
|
52
|
-
size: 130,
|
|
53
|
-
colors: [this.colors.lavender, this.colors.midnight, "#919834", "#c177cf", "#c16e00", "#029af2", "#2a6993"],
|
|
54
|
-
thickness: 0.5,
|
|
55
|
-
padding: 90,
|
|
56
|
-
isBar: false,
|
|
57
|
-
},
|
|
58
|
-
bar1: {
|
|
59
|
-
size: 350,
|
|
60
|
-
colors: [this.colors.cyan, "#bfbfbf"],
|
|
61
|
-
padding: 0,
|
|
62
|
-
isBar: true,
|
|
63
|
-
},
|
|
64
|
-
bar2: {
|
|
65
|
-
size: 400,
|
|
66
|
-
colors: ["#d4d4d4", this.colors.sleet, this.colors.cyan, this.colors.salmon],
|
|
67
|
-
padding: 0,
|
|
68
|
-
isBar: true,
|
|
69
|
-
},
|
|
70
|
-
bar3: {
|
|
71
|
-
size: 300,
|
|
72
|
-
colors: ["#0d696e", this.colors.cyan, "#8e4129", this.colors.salmon],
|
|
73
|
-
padding: 0,
|
|
74
|
-
isBar: true,
|
|
75
|
-
},
|
|
76
|
-
bar4: {
|
|
77
|
-
size: 400,
|
|
78
|
-
colors: ["#d4d4d4", this.colors.sleet, "#33a919"],
|
|
79
|
-
padding: 0,
|
|
80
|
-
isBar: true,
|
|
81
|
-
},
|
|
82
|
-
bar5: {
|
|
83
|
-
size: 400,
|
|
84
|
-
colors: [
|
|
85
|
-
"#d4d4d4",
|
|
86
|
-
this.colors.lavender,
|
|
87
|
-
this.colors.midnight,
|
|
88
|
-
"#919834",
|
|
89
|
-
"#c177cf",
|
|
90
|
-
"#c16e00",
|
|
91
|
-
"#029af2",
|
|
92
|
-
"#2a6993",
|
|
93
|
-
],
|
|
94
|
-
padding: 0,
|
|
95
|
-
isBar: true,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
/* LIFECYCLE METHODS + EVENTS FROM THE CHILDREN */
|
|
99
|
-
this.debouncedResize = functions.debounce(async () => {
|
|
100
|
-
if (this.chartType === "hybrid") {
|
|
101
|
-
this.setHybridType();
|
|
102
|
-
await this.getData();
|
|
103
|
-
}
|
|
104
|
-
index.forceUpdate(this.el);
|
|
105
|
-
}, 10);
|
|
106
|
-
this.debouncedSliceUpdate = functions.debounce(async () => {
|
|
107
|
-
await this.getData();
|
|
108
|
-
index.forceUpdate(this.el);
|
|
109
|
-
}, 100);
|
|
110
|
-
}
|
|
111
|
-
get tempValueFormat() {
|
|
112
|
-
// use of this getter should be replaced with dateFormat when showValues is fully phased out
|
|
113
|
-
return this.valueFormat || this.showValues || "none";
|
|
114
|
-
}
|
|
115
|
-
toggleTabbingOn() {
|
|
116
|
-
this.isTabbing = true;
|
|
117
|
-
}
|
|
118
|
-
toggleTabbingOff() {
|
|
119
|
-
this.isTabbing = false;
|
|
120
|
-
}
|
|
121
|
-
handleKeydown(ev) {
|
|
122
|
-
switch (ev.keyCode) {
|
|
123
|
-
// arrow up / left
|
|
124
|
-
case 37:
|
|
125
|
-
case 38:
|
|
126
|
-
ev.preventDefault();
|
|
127
|
-
this.isTabbing = true; // shd already be true. just in case user clicked on chart then pressed an arrow key
|
|
128
|
-
this.focusPrevious();
|
|
129
|
-
break;
|
|
130
|
-
// arrow right / down
|
|
131
|
-
case 39:
|
|
132
|
-
case 40:
|
|
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.focusNext();
|
|
136
|
-
break;
|
|
137
|
-
// tab
|
|
138
|
-
case 9:
|
|
139
|
-
this.exitChart();
|
|
140
|
-
break;
|
|
141
|
-
case 27:
|
|
142
|
-
this.popoverEl.open = false;
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
focusNext() {
|
|
147
|
-
const activeEl = functions.checkForActiveElInShadow(document.activeElement);
|
|
148
|
-
const index =
|
|
149
|
-
// if the active el is not in the array the first element gets focused
|
|
150
|
-
(this.sliceEls.indexOf(activeEl) + 1) % this.sliceEls.length;
|
|
151
|
-
this.focusSlice(index);
|
|
152
|
-
}
|
|
153
|
-
focusPrevious() {
|
|
154
|
-
if (this.sliceEls) {
|
|
155
|
-
const activeEl = functions.checkForActiveElInShadow(document.activeElement);
|
|
156
|
-
let index = this.sliceEls.indexOf(activeEl);
|
|
157
|
-
if (index === -1) {
|
|
158
|
-
// not in the array : focus the first slice
|
|
159
|
-
index = 0;
|
|
160
|
-
}
|
|
161
|
-
else if (index === 0) {
|
|
162
|
-
// first slice : focus the last slice
|
|
163
|
-
index = this.sliceEls.length - 1;
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
// anything else: focus previous
|
|
167
|
-
index -= 1;
|
|
168
|
-
}
|
|
169
|
-
this.focusSlice(index);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
focusSlice(index) {
|
|
173
|
-
if (this.sliceEls && this.el) {
|
|
174
|
-
if (this.popoverEl) {
|
|
175
|
-
this.popoverEl.open = false;
|
|
176
|
-
}
|
|
177
|
-
this.el.tabIndex = -1;
|
|
178
|
-
// @ts-ignore
|
|
179
|
-
this.el.focusable = false; // for Edge
|
|
180
|
-
this.sliceEls.map((p) => {
|
|
181
|
-
p.tabIndex = -1;
|
|
182
|
-
// @ts-ignore
|
|
183
|
-
p.focusable = false; // for Edge
|
|
184
|
-
});
|
|
185
|
-
this.sliceEls[index].tabIndex = 0;
|
|
186
|
-
// @ts-ignore
|
|
187
|
-
this.sliceEls[index].focusable = true; // for Edge
|
|
188
|
-
this.sliceEls[index].focus();
|
|
189
|
-
window.setTimeout(() => {
|
|
190
|
-
if (this.popoverEl) {
|
|
191
|
-
this.popoverEl.open = true;
|
|
192
|
-
}
|
|
193
|
-
}, 10);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
exitChart() {
|
|
197
|
-
this.sliceEls &&
|
|
198
|
-
this.sliceEls.map((p) => {
|
|
199
|
-
p.tabIndex = -1;
|
|
200
|
-
// @ts-ignore
|
|
201
|
-
p.focusable = false; // for Edge
|
|
202
|
-
});
|
|
203
|
-
// delay so that we can tab out of component before chart becomes focusable again
|
|
204
|
-
// and in case user was still pressing an arrow key when they pressed tab
|
|
205
|
-
window.setTimeout(() => {
|
|
206
|
-
if (this.el) {
|
|
207
|
-
this.el.tabIndex = 0;
|
|
208
|
-
// @ts-ignore
|
|
209
|
-
this.el.focusable = true; // for Edge
|
|
210
|
-
if (this.popoverEl) {
|
|
211
|
-
this.popoverEl.open = false;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}, 100);
|
|
215
|
-
}
|
|
216
|
-
openPopover(s) {
|
|
217
|
-
if (!!this.popoverEl && !!s.title && !!s.text) {
|
|
218
|
-
this.popoverEl.popoverTitle = s.title;
|
|
219
|
-
this.popoverEl.popoverText = s.text;
|
|
220
|
-
this.popoverEl.buttonText = s.buttonText;
|
|
221
|
-
this.popoverEl.coords = s.coords;
|
|
222
|
-
this.popoverEl.sliceRef = s.sliceRef;
|
|
223
|
-
window.setTimeout(() => {
|
|
224
|
-
if (this.popoverEl) {
|
|
225
|
-
this.popoverEl.open = true;
|
|
226
|
-
}
|
|
227
|
-
}, 30);
|
|
228
|
-
const debouncedClosePopover = functions.debounce(async () => {
|
|
229
|
-
this.popoverEl.open = false;
|
|
230
|
-
}, 10);
|
|
231
|
-
// set up event listeners for scrolling
|
|
232
|
-
// to close popover on page scroll
|
|
233
|
-
document.addEventListener("scroll", () => {
|
|
234
|
-
debouncedClosePopover();
|
|
235
|
-
});
|
|
236
|
-
// ... and on parent scroll
|
|
237
|
-
const scrollableParent = functions.findParentWithScrollbar(this.el);
|
|
238
|
-
if (!!scrollableParent) {
|
|
239
|
-
scrollableParent.addEventListener("scroll", () => {
|
|
240
|
-
debouncedClosePopover();
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
/* UTILS */
|
|
246
|
-
amountToPercent(val, asInt) {
|
|
247
|
-
return asInt ? Math.round((val * 100) / this.total) : Math.round((val * 10000) / this.total) / 100; // with 2 decimals
|
|
248
|
-
}
|
|
249
|
-
amountToDegree(val) {
|
|
250
|
-
return (val * 360) / this.total;
|
|
251
|
-
}
|
|
252
|
-
toFixed(number) {
|
|
253
|
-
var val = parseFloat((Math.floor(number * 100) / 100).toFixed(2));
|
|
254
|
-
return val;
|
|
255
|
-
}
|
|
256
|
-
polarToCartesian(half, radius, startAngle, endAngle) {
|
|
257
|
-
var x = this.toFixed(half + half * radius * Math.cos((Math.PI * startAngle) / 180));
|
|
258
|
-
var y = this.toFixed(half + half * radius * Math.sin((Math.PI * startAngle) / 180));
|
|
259
|
-
if (endAngle !== undefined) {
|
|
260
|
-
// if a 2nd angle value was passed, return 2 pairs of coords
|
|
261
|
-
var x2 = this.toFixed(half + half * radius * Math.cos((Math.PI * endAngle) / 180));
|
|
262
|
-
var y2 = this.toFixed(half + half * radius * Math.sin((Math.PI * endAngle) / 180));
|
|
263
|
-
return { x1: x, y1: y, x2, y2 };
|
|
264
|
-
}
|
|
265
|
-
return { x, y };
|
|
266
|
-
}
|
|
267
|
-
/* CRUNCH THE NUMBERS */
|
|
268
|
-
getPathData(amount, offset) {
|
|
269
|
-
var startAngle = this.amountToDegree(offset) - 90; // start at noon, not at 3 o'clock
|
|
270
|
-
var activeAngle = (amount / this.total) * 360;
|
|
271
|
-
var endAngle = startAngle + activeAngle;
|
|
272
|
-
var largeArcFlagOuter = activeAngle > 180 ? "1 1" : "0 1";
|
|
273
|
-
var largeArcFlagInner = activeAngle > 180 ? "1 0" : "0 0";
|
|
274
|
-
var half = this.chartData.size / 2;
|
|
275
|
-
var innerRadius = this.chartData.thickness;
|
|
276
|
-
var outerRadius = 1;
|
|
277
|
-
if (activeAngle === 360) {
|
|
278
|
-
// fix to avoid bad svg shape when the path goes all around (100%)
|
|
279
|
-
endAngle -= 0.01;
|
|
280
|
-
}
|
|
281
|
-
var outerCoords = this.polarToCartesian(half, outerRadius, startAngle + 1.5, // 1.5 for slice separator
|
|
282
|
-
endAngle);
|
|
283
|
-
var innerCoords = this.polarToCartesian(half, innerRadius, startAngle + 3, // 3 for slice separator
|
|
284
|
-
endAngle);
|
|
285
|
-
const moveTo = `M ${outerCoords.x1}, ${outerCoords.y1} `;
|
|
286
|
-
const arc1 = this.getArc(outerRadius, largeArcFlagOuter, outerCoords.x2, outerCoords.y2);
|
|
287
|
-
const line = ` L ${innerCoords.x2}, ${innerCoords.y2} `;
|
|
288
|
-
const arc2 = this.getArc(innerRadius, largeArcFlagInner, innerCoords.x1, innerCoords.y1);
|
|
289
|
-
return moveTo + arc1 + line + arc2 + " z";
|
|
290
|
-
}
|
|
291
|
-
getArc(radius, largeArcFlag, x, y) {
|
|
292
|
-
var z = this.toFixed((this.chartData.size / 2) * radius);
|
|
293
|
-
return `A ${z}, ${z} 0 ${largeArcFlag} ${this.toFixed(x)}, ${this.toFixed(y)}`;
|
|
294
|
-
}
|
|
295
|
-
setHybridType() {
|
|
296
|
-
this.hybridType = window.innerWidth > 1340 ? "doughnut0" : "bar1";
|
|
297
|
-
}
|
|
298
|
-
getType() {
|
|
299
|
-
return this.chartType === "hybrid" ? this.hybridType : this.chartType;
|
|
300
|
-
}
|
|
301
|
-
/* GET THE DATA */
|
|
302
|
-
async getData() {
|
|
303
|
-
this.slicesData = [];
|
|
304
|
-
let acc = 0;
|
|
305
|
-
const children = this.el.querySelectorAll("wm-chart-slice");
|
|
306
|
-
this.total = Array.from(children).reduce((total, slice) => (total += parseInt(slice.getAttribute("amount") || "0")), 0);
|
|
307
|
-
children.forEach((c, i) => {
|
|
308
|
-
const amount = parseInt(c.getAttribute("amount") || "0");
|
|
309
|
-
const perc = this.amountToPercent(amount, true);
|
|
310
|
-
// determine whether the slice is in a cluster of small values
|
|
311
|
-
// to avoid percentage text overlap for small values
|
|
312
|
-
const prev = children[i === 0 ? children.length - 1 : i - 1];
|
|
313
|
-
const prevPerc = this.amountToPercent(parseInt(prev.getAttribute("amount") || "0"), true);
|
|
314
|
-
const next = children[i === children.length - 1 ? 0 : i + 1];
|
|
315
|
-
const nextPerc = this.amountToPercent(parseInt(next.getAttribute("amount") || "0"), true);
|
|
316
|
-
const isSmall = perc < 4;
|
|
317
|
-
const prevIsSmall = prevPerc < 5;
|
|
318
|
-
const nextIsSmall = nextPerc < 5;
|
|
319
|
-
let inSmallCluster = isSmall && (prevIsSmall || nextIsSmall);
|
|
320
|
-
// because <1% slice percentage text has an additional character
|
|
321
|
-
// the inSmallCluster threshold needs to be widened for that slice only
|
|
322
|
-
const lessThanOnePerc = perc === 0 && amount > 0;
|
|
323
|
-
if (lessThanOnePerc && (nextPerc < 8 || prevPerc < 8)) {
|
|
324
|
-
inSmallCluster = true;
|
|
325
|
-
}
|
|
326
|
-
// for bar5, first color should be skipped unless notStartedColor is set to true
|
|
327
|
-
const ind = this.getType() === "bar5" ? (this.notStartedColor ? i : i + 1) : i;
|
|
328
|
-
const color = this.types[this.getType()].colors[ind];
|
|
329
|
-
const sliceData = {
|
|
330
|
-
amount: amount,
|
|
331
|
-
perc: perc,
|
|
332
|
-
legend: c.getAttribute("legend"),
|
|
333
|
-
color: color || "#d4d4d4",
|
|
334
|
-
offset: acc,
|
|
335
|
-
id: `${this.uid}-${i + 1}`,
|
|
336
|
-
title: c.getAttribute("popover-title"),
|
|
337
|
-
text: c.getAttribute("popover-text"),
|
|
338
|
-
buttonText: c.getAttribute("popover-button-text"),
|
|
339
|
-
sliceRef: c,
|
|
340
|
-
inSmallCluster: inSmallCluster,
|
|
341
|
-
};
|
|
342
|
-
acc += amount;
|
|
343
|
-
this.slicesData.push(sliceData);
|
|
344
|
-
});
|
|
345
|
-
this.chartData = this.types[this.getType()];
|
|
346
|
-
}
|
|
347
|
-
getSliceEls() {
|
|
348
|
-
if (this.svgEl) {
|
|
349
|
-
this.sliceEls = Array.from(this.svgEl.querySelectorAll(this.chartData.isBar ? "rect" : "path"));
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
handleResize() {
|
|
353
|
-
// handling resizing only needs to occur for bar charts (hybrid included)
|
|
354
|
-
if (this.chartType.includes("bar") || this.chartType === "hybrid") {
|
|
355
|
-
this.debouncedResize();
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
async componentWillLoad() {
|
|
359
|
-
if (!this.label) {
|
|
360
|
-
throw new Error("For accessibility purposes, you must provide a label for the chart. See https://components.watermarkinsights.com/chart for more information.");
|
|
361
|
-
}
|
|
362
|
-
if (this.showValues) {
|
|
363
|
-
console.warn("wm-chart: show-values has been deprecated as of v3.1.0. Please use value-format instead.");
|
|
364
|
-
}
|
|
365
|
-
if (this.chartType === "hybrid") {
|
|
366
|
-
this.setHybridType();
|
|
367
|
-
}
|
|
368
|
-
await this.getData();
|
|
369
|
-
}
|
|
370
|
-
componentDidLoad() {
|
|
371
|
-
this.getSliceEls();
|
|
372
|
-
}
|
|
373
|
-
componentDidUpdate() {
|
|
374
|
-
this.getSliceEls();
|
|
375
|
-
}
|
|
376
|
-
handleSliceUpdate() {
|
|
377
|
-
this.debouncedSliceUpdate();
|
|
378
|
-
}
|
|
379
|
-
/* RENDERING */
|
|
380
|
-
renderFilter() {
|
|
381
|
-
return (index.h("defs", null, index.h("filter", { id: "wmHoverDropShadow" }, index.h("feGaussianBlur", { stdDeviation: "3" }), index.h("feOffset", { result: "offsetblur" }), index.h("feFlood", { "flood-color": "#333" }), index.h("feComposite", { operator: "in", in2: "offsetblur" }), index.h("feMerge", null, index.h("feMergeNode", null), index.h("feMergeNode", { in: "SourceGraphic" })))));
|
|
382
|
-
}
|
|
383
|
-
// DOUGHNUT
|
|
384
|
-
renderDoughnut() {
|
|
385
|
-
const outerSize = this.chartData.size + this.chartData.padding;
|
|
386
|
-
return (index.h("div", { class: "svg-wrapper doughnut-wrapper" }, index.h("svg", { width: outerSize + "px", height: outerSize + "px", ref: (el) => (this.svgEl = el), id: `graphic-${this.uid}`, class: `chart-svg doughnut-svg ${this.isTabbing ? "user-is-tabbing" : ""}` }, this.renderFilter(), this.slicesData.map((s) => this.renderPath(s)), this.getType() === "doughnut0" ? (index.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) + "%")) : (index.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) : "")))))));
|
|
387
|
-
}
|
|
388
|
-
renderPath(s) {
|
|
389
|
-
return (index.h("g", { transform: `translate(${this.chartData.padding / 2}, ${this.chartData.padding / 2})` }, index.h("path", { id: s.id, fill: s.amount ? s.color : "transparent", d: this.getPathData(s.amount, s.offset), onClick: (ev) => {
|
|
390
|
-
if (this.popoverEl) {
|
|
391
|
-
if (!this.isTabbing) {
|
|
392
|
-
s.coords = { x: ev.clientX, y: ev.clientY };
|
|
393
|
-
this.openPopover(s);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}, onFocus: (ev) => {
|
|
397
|
-
if (this.popoverEl) {
|
|
398
|
-
if (this.isTabbing) {
|
|
399
|
-
s.coords = functions.getPosition(ev.target);
|
|
400
|
-
this.openPopover(s);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}, onKeyDown: (ev) => {
|
|
404
|
-
if (this.popoverEl && this.popoverEl.open && ev.keyCode === 13) {
|
|
405
|
-
const popoverBtn = this.popoverEl.querySelector("button");
|
|
406
|
-
popoverBtn && popoverBtn.click();
|
|
407
|
-
}
|
|
408
|
-
} }), index.h("text", { class: "sr-only" }, s.legend)));
|
|
409
|
-
}
|
|
410
|
-
renderDoughnutText(s) {
|
|
411
|
-
if (!s.inSmallCluster) {
|
|
412
|
-
const arcMiddle = this.amountToDegree(s.offset + s.amount / 2);
|
|
413
|
-
let { x, y } = this.polarToCartesian(this.chartData.size / 2, 1.4, arcMiddle - 90);
|
|
414
|
-
return (index.h("text", { class: "value", x: x + "px", y: y + "px" }, `${s.perc > 0 ? s.perc : "<1"}%`));
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
// BAR
|
|
418
|
-
renderBar() {
|
|
419
|
-
return (index.h("div", { class: "svg-wrapper bar-wrapper" }, this.getType() === "bar1" ? (index.h("div", { class: "single-perc" }, this.amountToPercent(this.slicesData[0].amount, true) + "%")) : (""), this.drawAxis(), index.h("div", { class: "completion-message-wrapper" }, index.h("svg", { ref: (el) => (this.svgEl = el), id: `graphic-${this.uid}`, class: {
|
|
420
|
-
"chart-svg bar-svg": true,
|
|
421
|
-
"user-is-tabbing": this.isTabbing,
|
|
422
|
-
"show-values": this.tempValueFormat === "percentage" || this.tempValueFormat === "amount",
|
|
423
|
-
} }, this.renderFilter(), index.h("foreignObject", { class: this.getType() === "bar2" || this.getType() === "bar4" || this.getType() === "bar5"
|
|
424
|
-
? "focus-offset"
|
|
425
|
-
: "" }, index.h("div", {
|
|
426
|
-
// @ts-ignore
|
|
427
|
-
xmlns: "http://www.w3.org/1999/xhtml", class: "tabbing-focus"
|
|
428
|
-
})), this.slicesData.map((s, idx) => this.renderRect(s, idx)), this.getType() !== "bar1" ? (index.h("g", { class: "percs" }, this.slicesData.map((s) => (s.perc > 0 ? this.renderBarText(s) : "")))) : ("")), this.renderCompletionMessage())));
|
|
429
|
-
}
|
|
430
|
-
renderRect(s, idx) {
|
|
431
|
-
let y;
|
|
432
|
-
switch (this.getType()) {
|
|
433
|
-
case "bar2":
|
|
434
|
-
case "bar4":
|
|
435
|
-
case "bar5":
|
|
436
|
-
y = this.tempValueFormat === "percentage" || this.tempValueFormat === "amount" ? "30px" : "0";
|
|
437
|
-
break;
|
|
438
|
-
default:
|
|
439
|
-
y = "0";
|
|
440
|
-
}
|
|
441
|
-
// adjusting the width of the rect slightly ensures repainting on rerenders
|
|
442
|
-
// important for updating width accurate when resizing the page
|
|
443
|
-
const randomTinyNumber = Math.random() / 1000;
|
|
444
|
-
return (index.h("g", { class: "barcontainer" }, index.h("style", null, ` #${s.id} {
|
|
445
|
-
fill:${s.color};
|
|
446
|
-
x: ${`${this.amountToPercent(s.offset, false)}%`};
|
|
447
|
-
y: ${y};
|
|
448
|
-
height: 30px;
|
|
449
|
-
width: calc(${this.amountToPercent(s.amount, false) + randomTinyNumber}%${idx !== this.slicesData.length - 1 ? " - 2px" : ""});
|
|
450
|
-
}`), index.h("rect", { id: s.id, onClick: (ev) => {
|
|
451
|
-
if (this.popoverEl) {
|
|
452
|
-
if (!this.isTabbing) {
|
|
453
|
-
s.coords = { x: ev.clientX, y: ev.clientY };
|
|
454
|
-
this.openPopover(s);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}, onFocus: (ev) => {
|
|
458
|
-
if (this.popoverEl) {
|
|
459
|
-
if (this.isTabbing) {
|
|
460
|
-
s.coords = functions.getPosition(ev.target);
|
|
461
|
-
this.openPopover(s);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}, onKeyDown: (ev) => {
|
|
465
|
-
if (this.popoverEl && this.popoverEl.open && ev.keyCode === 13) {
|
|
466
|
-
const popoverBtn = this.popoverEl.querySelector("button");
|
|
467
|
-
popoverBtn && popoverBtn.click();
|
|
468
|
-
}
|
|
469
|
-
} }), index.h("text", { class: "sr-only" }, s.legend)));
|
|
470
|
-
}
|
|
471
|
-
renderBarText(s) {
|
|
472
|
-
let val;
|
|
473
|
-
if (this.tempValueFormat === "percentage") {
|
|
474
|
-
val = s.perc + "%";
|
|
475
|
-
}
|
|
476
|
-
else if (this.tempValueFormat === "amount") {
|
|
477
|
-
val = s.amount;
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
return (index.h("text", { class: "value", x: `${this.amountToPercent(s.offset, false) + this.amountToPercent(s.amount, false) / 2}%` }, val));
|
|
483
|
-
}
|
|
484
|
-
drawAxis() {
|
|
485
|
-
if (this.getType() === "bar3") {
|
|
486
|
-
return (index.h("svg", { class: "axis" }, index.h("line", { x1: "0", x2: "100%", y1: "0", y2: "0" }), index.h("line", { x1: "0", x2: "0", y1: "0", y2: "-85px" }), index.h("line", { class: "tick", x1: "0.5", x2: "0.5", y1: "0", y2: "6" }), index.h("text", { x: "0.5", y: "-6" }, "0%"), index.h("line", { class: "tick", x1: "100%", x2: "100%", y1: "0", y2: "6" }), index.h("text", { x: "100%", y: "-6" }, "100%")));
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
renderCompletionMessage() {
|
|
490
|
-
if (this.chartType === "hybrid" && this.completionMessage) {
|
|
491
|
-
return index.h("span", { class: "completion-message" }, this.completionMessage);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
renderLegend() {
|
|
495
|
-
// legend is hidden for bar1 type regardless of showLegend value
|
|
496
|
-
if (this.showLegend) {
|
|
497
|
-
const hasCluster = this.slicesData.reduce((hasCluster, cur) => (hasCluster = cur.inSmallCluster ? true : hasCluster), false);
|
|
498
|
-
return (index.h("div", { class: "legend-wrapper" }, index.h("div", { class: `legend ${this.chartData.isBar ? "--top" : "--bottom"}`, "aria-hidden": "true" }, this.total > 0
|
|
499
|
-
? this.slicesData.map((s) => {
|
|
500
|
-
// when both legend and amount are omitted, the legend is not shown for that particular option (it's been deactivated by the user)
|
|
501
|
-
if (!!s.amount || !!s.legend) {
|
|
502
|
-
return (index.h("div", { class: "legend-item" }, index.h("div", { class: "legend-color", style: { "background-color": s.color } }), index.h("div", { class: "legend-text" }, s.legend)));
|
|
503
|
-
}
|
|
504
|
-
})
|
|
505
|
-
: ""), !this.chartData.isBar && hasCluster ? (index.h("div", { class: "cluster-warning" }, functions.intl.formatMessage({
|
|
506
|
-
id: "chart.hiddenPercentages",
|
|
507
|
-
defaultMessage: "Percentages smaller than 5% are not shown when too close to each other.",
|
|
508
|
-
}), index.h("br", null), functions.intl.formatMessage({
|
|
509
|
-
id: "chart.clickToSeeDetails",
|
|
510
|
-
defaultMessage: "Click or use arrow keys to see details.",
|
|
511
|
-
}))) : ("")));
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
render() {
|
|
515
|
-
return (index.h(index.Host, { role: "application", "aria-label": this.label +
|
|
516
|
-
", " +
|
|
517
|
-
functions.intl.formatMessage({
|
|
518
|
-
id: "chart.interactiveChart",
|
|
519
|
-
defaultMessage: "Interactive chart. Use arrow keys to browse elements, press Tab to exit.",
|
|
520
|
-
}), tabindex: "0" }, index.h("div", { class: `component-wrapper ${this.getType()} ${this.labelPosition === "left" && this.chartType === "bar4" ? "left-label" : ""}` }, index.h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `graphic-${this.uid}` }, index.h("span", { class: "label-text" }, this.label), this.subinfo ? index.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() : "", index.h("priv-chart-popover", { class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.popoverEl = el) }), this.hybridType === "doughnut0" ? this.renderCompletionMessage() : "")));
|
|
521
|
-
}
|
|
522
|
-
get el() { return index.getElement(this); }
|
|
523
|
-
};
|
|
10
|
+
const Chart = class {
|
|
11
|
+
constructor(hostRef) {
|
|
12
|
+
index.registerInstance(this, hostRef);
|
|
13
|
+
this.chartType = "doughnut1";
|
|
14
|
+
this.showLegend = true;
|
|
15
|
+
this.notStartedColor = false;
|
|
16
|
+
// left labels are a temporary solution for the lack of a stacked bar chart and is undocumented
|
|
17
|
+
// it only works with bar4 and should be used without a legend
|
|
18
|
+
this.labelPosition = "top";
|
|
19
|
+
this.isTabbing = false;
|
|
20
|
+
this.uid = functions.generateId();
|
|
21
|
+
this.slicesData = [];
|
|
22
|
+
this.colors = {
|
|
23
|
+
salmon: "#ff5f4e",
|
|
24
|
+
cyan: "#19a1a9",
|
|
25
|
+
sleet: "#7f97ad",
|
|
26
|
+
midnight: "#2e1b46",
|
|
27
|
+
lavender: "#8b86ca",
|
|
28
|
+
};
|
|
29
|
+
this.types = {
|
|
30
|
+
doughnut0: {
|
|
31
|
+
size: 155,
|
|
32
|
+
colors: [this.colors.cyan, "#bfbfbf"],
|
|
33
|
+
thickness: 0.73,
|
|
34
|
+
padding: 25,
|
|
35
|
+
isBar: false,
|
|
36
|
+
},
|
|
37
|
+
doughnut1: {
|
|
38
|
+
size: 130,
|
|
39
|
+
colors: [this.colors.lavender, this.colors.midnight, "#d4d4d4"],
|
|
40
|
+
thickness: 0.5,
|
|
41
|
+
padding: 90,
|
|
42
|
+
isBar: false,
|
|
43
|
+
},
|
|
44
|
+
doughnut2: {
|
|
45
|
+
size: 130,
|
|
46
|
+
colors: [this.colors.cyan, this.colors.salmon, "#d4d4d4"],
|
|
47
|
+
thickness: 0.5,
|
|
48
|
+
padding: 90,
|
|
49
|
+
isBar: false,
|
|
50
|
+
},
|
|
51
|
+
doughnut3: {
|
|
52
|
+
size: 130,
|
|
53
|
+
colors: [this.colors.lavender, this.colors.midnight, "#919834", "#c177cf", "#c16e00", "#029af2", "#2a6993"],
|
|
54
|
+
thickness: 0.5,
|
|
55
|
+
padding: 90,
|
|
56
|
+
isBar: false,
|
|
57
|
+
},
|
|
58
|
+
bar1: {
|
|
59
|
+
size: 350,
|
|
60
|
+
colors: [this.colors.cyan, "#bfbfbf"],
|
|
61
|
+
padding: 0,
|
|
62
|
+
isBar: true,
|
|
63
|
+
},
|
|
64
|
+
bar2: {
|
|
65
|
+
size: 400,
|
|
66
|
+
colors: ["#d4d4d4", this.colors.sleet, this.colors.cyan, this.colors.salmon],
|
|
67
|
+
padding: 0,
|
|
68
|
+
isBar: true,
|
|
69
|
+
},
|
|
70
|
+
bar3: {
|
|
71
|
+
size: 300,
|
|
72
|
+
colors: ["#0d696e", this.colors.cyan, "#8e4129", this.colors.salmon],
|
|
73
|
+
padding: 0,
|
|
74
|
+
isBar: true,
|
|
75
|
+
},
|
|
76
|
+
bar4: {
|
|
77
|
+
size: 400,
|
|
78
|
+
colors: ["#d4d4d4", this.colors.sleet, "#33a919"],
|
|
79
|
+
padding: 0,
|
|
80
|
+
isBar: true,
|
|
81
|
+
},
|
|
82
|
+
bar5: {
|
|
83
|
+
size: 400,
|
|
84
|
+
colors: [
|
|
85
|
+
"#d4d4d4",
|
|
86
|
+
this.colors.lavender,
|
|
87
|
+
this.colors.midnight,
|
|
88
|
+
"#919834",
|
|
89
|
+
"#c177cf",
|
|
90
|
+
"#c16e00",
|
|
91
|
+
"#029af2",
|
|
92
|
+
"#2a6993",
|
|
93
|
+
],
|
|
94
|
+
padding: 0,
|
|
95
|
+
isBar: true,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
/* LIFECYCLE METHODS + EVENTS FROM THE CHILDREN */
|
|
99
|
+
this.debouncedResize = functions.debounce(async () => {
|
|
100
|
+
if (this.chartType === "hybrid") {
|
|
101
|
+
this.setHybridType();
|
|
102
|
+
await this.getData();
|
|
103
|
+
}
|
|
104
|
+
index.forceUpdate(this.el);
|
|
105
|
+
}, 10);
|
|
106
|
+
this.debouncedSliceUpdate = functions.debounce(async () => {
|
|
107
|
+
await this.getData();
|
|
108
|
+
index.forceUpdate(this.el);
|
|
109
|
+
}, 100);
|
|
110
|
+
}
|
|
111
|
+
get tempValueFormat() {
|
|
112
|
+
// use of this getter should be replaced with dateFormat when showValues is fully phased out
|
|
113
|
+
return this.valueFormat || this.showValues || "none";
|
|
114
|
+
}
|
|
115
|
+
toggleTabbingOn() {
|
|
116
|
+
this.isTabbing = true;
|
|
117
|
+
}
|
|
118
|
+
toggleTabbingOff() {
|
|
119
|
+
this.isTabbing = false;
|
|
120
|
+
}
|
|
121
|
+
handleKeydown(ev) {
|
|
122
|
+
switch (ev.keyCode) {
|
|
123
|
+
// arrow up / left
|
|
124
|
+
case 37:
|
|
125
|
+
case 38:
|
|
126
|
+
ev.preventDefault();
|
|
127
|
+
this.isTabbing = true; // shd already be true. just in case user clicked on chart then pressed an arrow key
|
|
128
|
+
this.focusPrevious();
|
|
129
|
+
break;
|
|
130
|
+
// arrow right / down
|
|
131
|
+
case 39:
|
|
132
|
+
case 40:
|
|
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.focusNext();
|
|
136
|
+
break;
|
|
137
|
+
// tab
|
|
138
|
+
case 9:
|
|
139
|
+
this.exitChart();
|
|
140
|
+
break;
|
|
141
|
+
case 27:
|
|
142
|
+
this.popoverEl.open = false;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
focusNext() {
|
|
147
|
+
const activeEl = functions.checkForActiveElInShadow(document.activeElement);
|
|
148
|
+
const index =
|
|
149
|
+
// if the active el is not in the array the first element gets focused
|
|
150
|
+
(this.sliceEls.indexOf(activeEl) + 1) % this.sliceEls.length;
|
|
151
|
+
this.focusSlice(index);
|
|
152
|
+
}
|
|
153
|
+
focusPrevious() {
|
|
154
|
+
if (this.sliceEls) {
|
|
155
|
+
const activeEl = functions.checkForActiveElInShadow(document.activeElement);
|
|
156
|
+
let index = this.sliceEls.indexOf(activeEl);
|
|
157
|
+
if (index === -1) {
|
|
158
|
+
// not in the array : focus the first slice
|
|
159
|
+
index = 0;
|
|
160
|
+
}
|
|
161
|
+
else if (index === 0) {
|
|
162
|
+
// first slice : focus the last slice
|
|
163
|
+
index = this.sliceEls.length - 1;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// anything else: focus previous
|
|
167
|
+
index -= 1;
|
|
168
|
+
}
|
|
169
|
+
this.focusSlice(index);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
focusSlice(index) {
|
|
173
|
+
if (this.sliceEls && this.el) {
|
|
174
|
+
if (this.popoverEl) {
|
|
175
|
+
this.popoverEl.open = false;
|
|
176
|
+
}
|
|
177
|
+
this.el.tabIndex = -1;
|
|
178
|
+
// @ts-ignore
|
|
179
|
+
this.el.focusable = false; // for Edge
|
|
180
|
+
this.sliceEls.map((p) => {
|
|
181
|
+
p.tabIndex = -1;
|
|
182
|
+
// @ts-ignore
|
|
183
|
+
p.focusable = false; // for Edge
|
|
184
|
+
});
|
|
185
|
+
this.sliceEls[index].tabIndex = 0;
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
this.sliceEls[index].focusable = true; // for Edge
|
|
188
|
+
this.sliceEls[index].focus();
|
|
189
|
+
window.setTimeout(() => {
|
|
190
|
+
if (this.popoverEl) {
|
|
191
|
+
this.popoverEl.open = true;
|
|
192
|
+
}
|
|
193
|
+
}, 10);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exitChart() {
|
|
197
|
+
this.sliceEls &&
|
|
198
|
+
this.sliceEls.map((p) => {
|
|
199
|
+
p.tabIndex = -1;
|
|
200
|
+
// @ts-ignore
|
|
201
|
+
p.focusable = false; // for Edge
|
|
202
|
+
});
|
|
203
|
+
// delay so that we can tab out of component before chart becomes focusable again
|
|
204
|
+
// and in case user was still pressing an arrow key when they pressed tab
|
|
205
|
+
window.setTimeout(() => {
|
|
206
|
+
if (this.el) {
|
|
207
|
+
this.el.tabIndex = 0;
|
|
208
|
+
// @ts-ignore
|
|
209
|
+
this.el.focusable = true; // for Edge
|
|
210
|
+
if (this.popoverEl) {
|
|
211
|
+
this.popoverEl.open = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}, 100);
|
|
215
|
+
}
|
|
216
|
+
openPopover(s) {
|
|
217
|
+
if (!!this.popoverEl && !!s.title && !!s.text) {
|
|
218
|
+
this.popoverEl.popoverTitle = s.title;
|
|
219
|
+
this.popoverEl.popoverText = s.text;
|
|
220
|
+
this.popoverEl.buttonText = s.buttonText;
|
|
221
|
+
this.popoverEl.coords = s.coords;
|
|
222
|
+
this.popoverEl.sliceRef = s.sliceRef;
|
|
223
|
+
window.setTimeout(() => {
|
|
224
|
+
if (this.popoverEl) {
|
|
225
|
+
this.popoverEl.open = true;
|
|
226
|
+
}
|
|
227
|
+
}, 30);
|
|
228
|
+
const debouncedClosePopover = functions.debounce(async () => {
|
|
229
|
+
this.popoverEl.open = false;
|
|
230
|
+
}, 10);
|
|
231
|
+
// set up event listeners for scrolling
|
|
232
|
+
// to close popover on page scroll
|
|
233
|
+
document.addEventListener("scroll", () => {
|
|
234
|
+
debouncedClosePopover();
|
|
235
|
+
});
|
|
236
|
+
// ... and on parent scroll
|
|
237
|
+
const scrollableParent = functions.findParentWithScrollbar(this.el);
|
|
238
|
+
if (!!scrollableParent) {
|
|
239
|
+
scrollableParent.addEventListener("scroll", () => {
|
|
240
|
+
debouncedClosePopover();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/* UTILS */
|
|
246
|
+
amountToPercent(val, asInt) {
|
|
247
|
+
return asInt ? Math.round((val * 100) / this.total) : Math.round((val * 10000) / this.total) / 100; // with 2 decimals
|
|
248
|
+
}
|
|
249
|
+
amountToDegree(val) {
|
|
250
|
+
return (val * 360) / this.total;
|
|
251
|
+
}
|
|
252
|
+
toFixed(number) {
|
|
253
|
+
var val = parseFloat((Math.floor(number * 100) / 100).toFixed(2));
|
|
254
|
+
return val;
|
|
255
|
+
}
|
|
256
|
+
polarToCartesian(half, radius, startAngle, endAngle) {
|
|
257
|
+
var x = this.toFixed(half + half * radius * Math.cos((Math.PI * startAngle) / 180));
|
|
258
|
+
var y = this.toFixed(half + half * radius * Math.sin((Math.PI * startAngle) / 180));
|
|
259
|
+
if (endAngle !== undefined) {
|
|
260
|
+
// if a 2nd angle value was passed, return 2 pairs of coords
|
|
261
|
+
var x2 = this.toFixed(half + half * radius * Math.cos((Math.PI * endAngle) / 180));
|
|
262
|
+
var y2 = this.toFixed(half + half * radius * Math.sin((Math.PI * endAngle) / 180));
|
|
263
|
+
return { x1: x, y1: y, x2, y2 };
|
|
264
|
+
}
|
|
265
|
+
return { x, y };
|
|
266
|
+
}
|
|
267
|
+
/* CRUNCH THE NUMBERS */
|
|
268
|
+
getPathData(amount, offset) {
|
|
269
|
+
var startAngle = this.amountToDegree(offset) - 90; // start at noon, not at 3 o'clock
|
|
270
|
+
var activeAngle = (amount / this.total) * 360;
|
|
271
|
+
var endAngle = startAngle + activeAngle;
|
|
272
|
+
var largeArcFlagOuter = activeAngle > 180 ? "1 1" : "0 1";
|
|
273
|
+
var largeArcFlagInner = activeAngle > 180 ? "1 0" : "0 0";
|
|
274
|
+
var half = this.chartData.size / 2;
|
|
275
|
+
var innerRadius = this.chartData.thickness;
|
|
276
|
+
var outerRadius = 1;
|
|
277
|
+
if (activeAngle === 360) {
|
|
278
|
+
// fix to avoid bad svg shape when the path goes all around (100%)
|
|
279
|
+
endAngle -= 0.01;
|
|
280
|
+
}
|
|
281
|
+
var outerCoords = this.polarToCartesian(half, outerRadius, startAngle + 1.5, // 1.5 for slice separator
|
|
282
|
+
endAngle);
|
|
283
|
+
var innerCoords = this.polarToCartesian(half, innerRadius, startAngle + 3, // 3 for slice separator
|
|
284
|
+
endAngle);
|
|
285
|
+
const moveTo = `M ${outerCoords.x1}, ${outerCoords.y1} `;
|
|
286
|
+
const arc1 = this.getArc(outerRadius, largeArcFlagOuter, outerCoords.x2, outerCoords.y2);
|
|
287
|
+
const line = ` L ${innerCoords.x2}, ${innerCoords.y2} `;
|
|
288
|
+
const arc2 = this.getArc(innerRadius, largeArcFlagInner, innerCoords.x1, innerCoords.y1);
|
|
289
|
+
return moveTo + arc1 + line + arc2 + " z";
|
|
290
|
+
}
|
|
291
|
+
getArc(radius, largeArcFlag, x, y) {
|
|
292
|
+
var z = this.toFixed((this.chartData.size / 2) * radius);
|
|
293
|
+
return `A ${z}, ${z} 0 ${largeArcFlag} ${this.toFixed(x)}, ${this.toFixed(y)}`;
|
|
294
|
+
}
|
|
295
|
+
setHybridType() {
|
|
296
|
+
this.hybridType = window.innerWidth > 1340 ? "doughnut0" : "bar1";
|
|
297
|
+
}
|
|
298
|
+
getType() {
|
|
299
|
+
return this.chartType === "hybrid" ? this.hybridType : this.chartType;
|
|
300
|
+
}
|
|
301
|
+
/* GET THE DATA */
|
|
302
|
+
async getData() {
|
|
303
|
+
this.slicesData = [];
|
|
304
|
+
let acc = 0;
|
|
305
|
+
const children = this.el.querySelectorAll("wm-chart-slice");
|
|
306
|
+
this.total = Array.from(children).reduce((total, slice) => (total += parseInt(slice.getAttribute("amount") || "0")), 0);
|
|
307
|
+
children.forEach((c, i) => {
|
|
308
|
+
const amount = parseInt(c.getAttribute("amount") || "0");
|
|
309
|
+
const perc = this.amountToPercent(amount, true);
|
|
310
|
+
// determine whether the slice is in a cluster of small values
|
|
311
|
+
// to avoid percentage text overlap for small values
|
|
312
|
+
const prev = children[i === 0 ? children.length - 1 : i - 1];
|
|
313
|
+
const prevPerc = this.amountToPercent(parseInt(prev.getAttribute("amount") || "0"), true);
|
|
314
|
+
const next = children[i === children.length - 1 ? 0 : i + 1];
|
|
315
|
+
const nextPerc = this.amountToPercent(parseInt(next.getAttribute("amount") || "0"), true);
|
|
316
|
+
const isSmall = perc < 4;
|
|
317
|
+
const prevIsSmall = prevPerc < 5;
|
|
318
|
+
const nextIsSmall = nextPerc < 5;
|
|
319
|
+
let inSmallCluster = isSmall && (prevIsSmall || nextIsSmall);
|
|
320
|
+
// because <1% slice percentage text has an additional character
|
|
321
|
+
// the inSmallCluster threshold needs to be widened for that slice only
|
|
322
|
+
const lessThanOnePerc = perc === 0 && amount > 0;
|
|
323
|
+
if (lessThanOnePerc && (nextPerc < 8 || prevPerc < 8)) {
|
|
324
|
+
inSmallCluster = true;
|
|
325
|
+
}
|
|
326
|
+
// for bar5, first color should be skipped unless notStartedColor is set to true
|
|
327
|
+
const ind = this.getType() === "bar5" ? (this.notStartedColor ? i : i + 1) : i;
|
|
328
|
+
const color = this.types[this.getType()].colors[ind];
|
|
329
|
+
const sliceData = {
|
|
330
|
+
amount: amount,
|
|
331
|
+
perc: perc,
|
|
332
|
+
legend: c.getAttribute("legend"),
|
|
333
|
+
color: color || "#d4d4d4",
|
|
334
|
+
offset: acc,
|
|
335
|
+
id: `${this.uid}-${i + 1}`,
|
|
336
|
+
title: c.getAttribute("popover-title"),
|
|
337
|
+
text: c.getAttribute("popover-text"),
|
|
338
|
+
buttonText: c.getAttribute("popover-button-text"),
|
|
339
|
+
sliceRef: c,
|
|
340
|
+
inSmallCluster: inSmallCluster,
|
|
341
|
+
};
|
|
342
|
+
acc += amount;
|
|
343
|
+
this.slicesData.push(sliceData);
|
|
344
|
+
});
|
|
345
|
+
this.chartData = this.types[this.getType()];
|
|
346
|
+
}
|
|
347
|
+
getSliceEls() {
|
|
348
|
+
if (this.svgEl) {
|
|
349
|
+
this.sliceEls = Array.from(this.svgEl.querySelectorAll(this.chartData.isBar ? "rect" : "path"));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
handleResize() {
|
|
353
|
+
// handling resizing only needs to occur for bar charts (hybrid included)
|
|
354
|
+
if (this.chartType.includes("bar") || this.chartType === "hybrid") {
|
|
355
|
+
this.debouncedResize();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async componentWillLoad() {
|
|
359
|
+
if (!this.label) {
|
|
360
|
+
throw new Error("For accessibility purposes, you must provide a label for the chart. See https://components.watermarkinsights.com/chart for more information.");
|
|
361
|
+
}
|
|
362
|
+
if (this.showValues) {
|
|
363
|
+
console.warn("wm-chart: show-values has been deprecated as of v3.1.0. Please use value-format instead.");
|
|
364
|
+
}
|
|
365
|
+
if (this.chartType === "hybrid") {
|
|
366
|
+
this.setHybridType();
|
|
367
|
+
}
|
|
368
|
+
await this.getData();
|
|
369
|
+
}
|
|
370
|
+
componentDidLoad() {
|
|
371
|
+
this.getSliceEls();
|
|
372
|
+
}
|
|
373
|
+
componentDidUpdate() {
|
|
374
|
+
this.getSliceEls();
|
|
375
|
+
}
|
|
376
|
+
handleSliceUpdate() {
|
|
377
|
+
this.debouncedSliceUpdate();
|
|
378
|
+
}
|
|
379
|
+
/* RENDERING */
|
|
380
|
+
renderFilter() {
|
|
381
|
+
return (index.h("defs", null, index.h("filter", { id: "wmHoverDropShadow" }, index.h("feGaussianBlur", { stdDeviation: "3" }), index.h("feOffset", { result: "offsetblur" }), index.h("feFlood", { "flood-color": "#333" }), index.h("feComposite", { operator: "in", in2: "offsetblur" }), index.h("feMerge", null, index.h("feMergeNode", null), index.h("feMergeNode", { in: "SourceGraphic" })))));
|
|
382
|
+
}
|
|
383
|
+
// DOUGHNUT
|
|
384
|
+
renderDoughnut() {
|
|
385
|
+
const outerSize = this.chartData.size + this.chartData.padding;
|
|
386
|
+
return (index.h("div", { class: "svg-wrapper doughnut-wrapper" }, index.h("svg", { width: outerSize + "px", height: outerSize + "px", ref: (el) => (this.svgEl = el), id: `graphic-${this.uid}`, class: `chart-svg doughnut-svg ${this.isTabbing ? "user-is-tabbing" : ""}` }, this.renderFilter(), this.slicesData.map((s) => this.renderPath(s)), this.getType() === "doughnut0" ? (index.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) + "%")) : (index.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) : "")))))));
|
|
387
|
+
}
|
|
388
|
+
renderPath(s) {
|
|
389
|
+
return (index.h("g", { transform: `translate(${this.chartData.padding / 2}, ${this.chartData.padding / 2})` }, index.h("path", { id: s.id, fill: s.amount ? s.color : "transparent", d: this.getPathData(s.amount, s.offset), onClick: (ev) => {
|
|
390
|
+
if (this.popoverEl) {
|
|
391
|
+
if (!this.isTabbing) {
|
|
392
|
+
s.coords = { x: ev.clientX, y: ev.clientY };
|
|
393
|
+
this.openPopover(s);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}, onFocus: (ev) => {
|
|
397
|
+
if (this.popoverEl) {
|
|
398
|
+
if (this.isTabbing) {
|
|
399
|
+
s.coords = functions.getPosition(ev.target);
|
|
400
|
+
this.openPopover(s);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}, onKeyDown: (ev) => {
|
|
404
|
+
if (this.popoverEl && this.popoverEl.open && ev.keyCode === 13) {
|
|
405
|
+
const popoverBtn = this.popoverEl.querySelector("button");
|
|
406
|
+
popoverBtn && popoverBtn.click();
|
|
407
|
+
}
|
|
408
|
+
} }), index.h("text", { class: "sr-only" }, s.legend)));
|
|
409
|
+
}
|
|
410
|
+
renderDoughnutText(s) {
|
|
411
|
+
if (!s.inSmallCluster) {
|
|
412
|
+
const arcMiddle = this.amountToDegree(s.offset + s.amount / 2);
|
|
413
|
+
let { x, y } = this.polarToCartesian(this.chartData.size / 2, 1.4, arcMiddle - 90);
|
|
414
|
+
return (index.h("text", { class: "value", x: x + "px", y: y + "px" }, `${s.perc > 0 ? s.perc : "<1"}%`));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// BAR
|
|
418
|
+
renderBar() {
|
|
419
|
+
return (index.h("div", { class: "svg-wrapper bar-wrapper" }, this.getType() === "bar1" ? (index.h("div", { class: "single-perc" }, this.amountToPercent(this.slicesData[0].amount, true) + "%")) : (""), this.drawAxis(), index.h("div", { class: "completion-message-wrapper" }, index.h("svg", { ref: (el) => (this.svgEl = el), id: `graphic-${this.uid}`, class: {
|
|
420
|
+
"chart-svg bar-svg": true,
|
|
421
|
+
"user-is-tabbing": this.isTabbing,
|
|
422
|
+
"show-values": this.tempValueFormat === "percentage" || this.tempValueFormat === "amount",
|
|
423
|
+
} }, this.renderFilter(), index.h("foreignObject", { class: this.getType() === "bar2" || this.getType() === "bar4" || this.getType() === "bar5"
|
|
424
|
+
? "focus-offset"
|
|
425
|
+
: "" }, index.h("div", {
|
|
426
|
+
// @ts-ignore
|
|
427
|
+
xmlns: "http://www.w3.org/1999/xhtml", class: "tabbing-focus"
|
|
428
|
+
})), this.slicesData.map((s, idx) => this.renderRect(s, idx)), this.getType() !== "bar1" ? (index.h("g", { class: "percs" }, this.slicesData.map((s) => (s.perc > 0 ? this.renderBarText(s) : "")))) : ("")), this.renderCompletionMessage())));
|
|
429
|
+
}
|
|
430
|
+
renderRect(s, idx) {
|
|
431
|
+
let y;
|
|
432
|
+
switch (this.getType()) {
|
|
433
|
+
case "bar2":
|
|
434
|
+
case "bar4":
|
|
435
|
+
case "bar5":
|
|
436
|
+
y = this.tempValueFormat === "percentage" || this.tempValueFormat === "amount" ? "30px" : "0";
|
|
437
|
+
break;
|
|
438
|
+
default:
|
|
439
|
+
y = "0";
|
|
440
|
+
}
|
|
441
|
+
// adjusting the width of the rect slightly ensures repainting on rerenders
|
|
442
|
+
// important for updating width accurate when resizing the page
|
|
443
|
+
const randomTinyNumber = Math.random() / 1000;
|
|
444
|
+
return (index.h("g", { class: "barcontainer" }, index.h("style", null, ` #${s.id} {
|
|
445
|
+
fill:${s.color};
|
|
446
|
+
x: ${`${this.amountToPercent(s.offset, false)}%`};
|
|
447
|
+
y: ${y};
|
|
448
|
+
height: 30px;
|
|
449
|
+
width: calc(${this.amountToPercent(s.amount, false) + randomTinyNumber}%${idx !== this.slicesData.length - 1 ? " - 2px" : ""});
|
|
450
|
+
}`), index.h("rect", { id: s.id, onClick: (ev) => {
|
|
451
|
+
if (this.popoverEl) {
|
|
452
|
+
if (!this.isTabbing) {
|
|
453
|
+
s.coords = { x: ev.clientX, y: ev.clientY };
|
|
454
|
+
this.openPopover(s);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}, onFocus: (ev) => {
|
|
458
|
+
if (this.popoverEl) {
|
|
459
|
+
if (this.isTabbing) {
|
|
460
|
+
s.coords = functions.getPosition(ev.target);
|
|
461
|
+
this.openPopover(s);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}, onKeyDown: (ev) => {
|
|
465
|
+
if (this.popoverEl && this.popoverEl.open && ev.keyCode === 13) {
|
|
466
|
+
const popoverBtn = this.popoverEl.querySelector("button");
|
|
467
|
+
popoverBtn && popoverBtn.click();
|
|
468
|
+
}
|
|
469
|
+
} }), index.h("text", { class: "sr-only" }, s.legend)));
|
|
470
|
+
}
|
|
471
|
+
renderBarText(s) {
|
|
472
|
+
let val;
|
|
473
|
+
if (this.tempValueFormat === "percentage") {
|
|
474
|
+
val = s.perc + "%";
|
|
475
|
+
}
|
|
476
|
+
else if (this.tempValueFormat === "amount") {
|
|
477
|
+
val = s.amount;
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
return (index.h("text", { class: "value", x: `${this.amountToPercent(s.offset, false) + this.amountToPercent(s.amount, false) / 2}%` }, val));
|
|
483
|
+
}
|
|
484
|
+
drawAxis() {
|
|
485
|
+
if (this.getType() === "bar3") {
|
|
486
|
+
return (index.h("svg", { class: "axis" }, index.h("line", { x1: "0", x2: "100%", y1: "0", y2: "0" }), index.h("line", { x1: "0", x2: "0", y1: "0", y2: "-85px" }), index.h("line", { class: "tick", x1: "0.5", x2: "0.5", y1: "0", y2: "6" }), index.h("text", { x: "0.5", y: "-6" }, "0%"), index.h("line", { class: "tick", x1: "100%", x2: "100%", y1: "0", y2: "6" }), index.h("text", { x: "100%", y: "-6" }, "100%")));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
renderCompletionMessage() {
|
|
490
|
+
if (this.chartType === "hybrid" && this.completionMessage) {
|
|
491
|
+
return index.h("span", { class: "completion-message" }, this.completionMessage);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
renderLegend() {
|
|
495
|
+
// legend is hidden for bar1 type regardless of showLegend value
|
|
496
|
+
if (this.showLegend) {
|
|
497
|
+
const hasCluster = this.slicesData.reduce((hasCluster, cur) => (hasCluster = cur.inSmallCluster ? true : hasCluster), false);
|
|
498
|
+
return (index.h("div", { class: "legend-wrapper" }, index.h("div", { class: `legend ${this.chartData.isBar ? "--top" : "--bottom"}`, "aria-hidden": "true" }, this.total > 0
|
|
499
|
+
? this.slicesData.map((s) => {
|
|
500
|
+
// when both legend and amount are omitted, the legend is not shown for that particular option (it's been deactivated by the user)
|
|
501
|
+
if (!!s.amount || !!s.legend) {
|
|
502
|
+
return (index.h("div", { class: "legend-item" }, index.h("div", { class: "legend-color", style: { "background-color": s.color } }), index.h("div", { class: "legend-text" }, s.legend)));
|
|
503
|
+
}
|
|
504
|
+
})
|
|
505
|
+
: ""), !this.chartData.isBar && hasCluster ? (index.h("div", { class: "cluster-warning" }, functions.intl.formatMessage({
|
|
506
|
+
id: "chart.hiddenPercentages",
|
|
507
|
+
defaultMessage: "Percentages smaller than 5% are not shown when too close to each other.",
|
|
508
|
+
}), index.h("br", null), functions.intl.formatMessage({
|
|
509
|
+
id: "chart.clickToSeeDetails",
|
|
510
|
+
defaultMessage: "Click or use arrow keys to see details.",
|
|
511
|
+
}))) : ("")));
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
render() {
|
|
515
|
+
return (index.h(index.Host, { role: "application", "aria-label": this.label +
|
|
516
|
+
", " +
|
|
517
|
+
functions.intl.formatMessage({
|
|
518
|
+
id: "chart.interactiveChart",
|
|
519
|
+
defaultMessage: "Interactive chart. Use arrow keys to browse elements, press Tab to exit.",
|
|
520
|
+
}), tabindex: "0" }, index.h("div", { class: `component-wrapper ${this.getType()} ${this.labelPosition === "left" && this.chartType === "bar4" ? "left-label" : ""}` }, index.h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `graphic-${this.uid}` }, index.h("span", { class: "label-text" }, this.label), this.subinfo ? index.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() : "", index.h("priv-chart-popover", { class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.popoverEl = el) }), this.hybridType === "doughnut0" ? this.renderCompletionMessage() : "")));
|
|
521
|
+
}
|
|
522
|
+
get el() { return index.getElement(this); }
|
|
523
|
+
};
|
|
524
524
|
Chart.style = wmChartCss;
|
|
525
525
|
|
|
526
526
|
exports.wm_chart = Chart;
|