Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordizzy <diosmosis@users.noreply.github.com>2022-03-03 19:20:02 +0300
committerGitHub <noreply@github.com>2022-03-03 19:20:02 +0300
commit56efd4c7d06aef3435cf531ab6264529eb7fc9b3 (patch)
tree40db2c1fd1439ac8b619bfde2c381899e83f678e /plugins
parent80fbf58d7085396d10fd7f596f2bbaeeae36d931 (diff)
[Vue] migrate single-metric-view component to vue (#18807)
* migrate archiving controller * do not do a passthrough transpile of vue typescript, and fix many typescript errors in existing code * more typescript fixes * even more fixes * workarounds to fix recursive typing issues * get corehome to build w/ full typescript build and output type definitions to local dir * get outputted typings to be used when compiling other plugins and fix typescript issues in CorePluginsAdmin * readd corehome umd * fix typescript errors in ExampleVue plugin * fix feedback typescript errors * rebuild * migrate branding controller and get to build * fix issues and get to work * rebuild * fix notification scroll * migrate smtp settings controller in coreadminhome * get to work * migrate js tracking code generator and get to build * migrate image tracking code generator and get to build * get to work in UI * get UI tests to pass locally * forgot to add files + rebuild vue * update screenshots * Show a summary of new features (#18065) * Added "What is new" notification display, populated by a new event * Removed test example event hook * Added support for applying a link attribute to menu items, fixes layout issue for mobile with html menu items * Updated UI test screenshots * Revert accidental edit * Hide the "What's new" icon if there are no new features to show * Changed to use changes.json, track user last viewed, added ui test * Fix UserManager unit tests broken by new ts_changes_viewed user field * Moved getChanges to separate helper class, added unit test, added user view access check * Updated to add new changes table and populate only on plugin update/install * Added missing fixture class, updated UI screenshots * Updated matomo font to add ringing bell and new releases icons * Fix for integration test * Reworked class structure, removed unnecessary angular directive, merged templates, other tidy ups * built vue files * built vue files * Added null user check, missing table exception handling, show plugin name in change title, better handling of missing change fields * Added sample changes file, moved UserChanges db code to changes model, added return type hints, better db error code handling, various other improvements * Revert accidental UI screenshot commit * Fix for incorrect link name parameter in sample changes, switched back to using $db->query for INSERT IGNORE * Integration test fix, UI screenshot updates * Test fix * Added link styling, show CoreHome changes without plugin prefix in title * Update UI test screenshot * Added styles to the popover, added event for filtering changes * Test fix * UI test screenshot updates Co-authored-by: sgiehl <stefan@matomo.org> Co-authored-by: bx80 <bx80@users.noreply.github.com> * Update test translation (#18531) update a test failed XML * updates all submodules (#18541) Co-authored-by: diosmosis <diosmosis@users.noreply.github.com> * Translations update from Hosted Weblate (#18529) * Translated using Weblate (Greek) Currently translated at 100.0% (162 of 162 strings) Translation: Matomo/Plugin CoreAdminHome Translate-URL: https://hosted.weblate.org/projects/matomo/plugin-coreadminhome/el/ [ci skip] Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: Vasilis Lourdas <dev@lourdas.eu> * Translated using Weblate (Chinese (Simplified)) Currently translated at 83.9% (136 of 162 strings) Translation: Matomo/Plugin CoreAdminHome Translate-URL: https://hosted.weblate.org/projects/matomo/plugin-coreadminhome/zh_Hans/ [ci skip] Translated using Weblate (Chinese (Simplified)) Currently translated at 99.6% (620 of 622 strings) Translation: Matomo/Matomo Base Translate-URL: https://hosted.weblate.org/projects/matomo/matomo-base/zh_Hans/ [ci skip] Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: 刘韬 <lyuutau@outlook.com> * Update translation files Updated by "Squash Git commits" hook in Weblate. Translation: Matomo/Plugin CoreAdminHome Translate-URL: https://hosted.weblate.org/projects/matomo/plugin-coreadminhome/ [ci skip] Co-authored-by: Vasilis Lourdas <dev@lourdas.eu> Co-authored-by: 刘韬 <lyuutau@outlook.com> * [Vue] migrate report export directive and popover (#18440) * update files * sidenav start * make getRef a utility method * tweak * add return type * finish converting side-nav directive * starting on reporting menu conversion * remove unused properties * convert reporting pages service * migrate report metadata store * remove angularjs files * migrating reporting pages store * make store adapters more immutable * get service adapters to work * fix a UI test * another html fix * migrate most of reporting menu directive and model * Use themed font family for input forms to override materialize.css styling * rebuild vue * add a missing div * ui test fixes * update styling * get to build * get to load in the UI w/o error * clone result of functions * fix compile issue * migrate widget loader and get to load in UI * rebuild vue * migrate widgetcontainer * migrate widget bydimension container * migrate widget + add tooltips directive * quick fix * Updating version to 4.6.0 * loading in page * update expected screenshot * add wait just in case travis is slow * fix ordering bug * add another wait * rebuild vue * css tweak * fix some bugs and tests * undo screenshot changes * Menus test passing locally * [Vue] date picker viewDate property is not kept up to date (#18385) * viewDate ref is not kept up to date * rebuild corehome * reporting menu subcategory items are meant to be normal links * update some screenshots * use innerText instead of text() since angularjs maintains newlines in HTML that vue does not add * trigger angularjs digest after ajaxhelper request * rebuild vue * update screenshots, fix bug in link generation in reporting menu and allow syncing multiple screenshot regexes at a time * undo box-shadow change for UI tests * fix more issues & update more tests * update some screenshots * fix some tests * rebuild CoreHome * quick fix * built vue files * fix angularjs issue * add comment * update umd files * 4.6.1-rc1 * 4.6.1 * fix field array title * apply some pr feedback * apply more pr feedback * another fix * tweak * fix ng-change not executed before ng-model * fix another set of issues * fix another issue * rebuild vue * better ng-change/ng-model fix * update some screenshots * rebuild vue * remove some TODOs * initiate initial ng-change ONLY for site selectors where this behavior applies * emit/broadcast on correct scope in wrapper * rebuild vue * fix some issues * couple more fixes * fix another title issue * rebuild vue * do not report on ajax errors in notifications if not logged in * migrate reporting page and model * rebuild vue * create sites selector model adapter * fix siteselector vue bug, initial site is only set if there is just one site available * rebuild vue * migrate plugin settings directive * remove TODO * migrate plugin filter directive * migrate two more plugins directives * migrate save button * fix a bunch of bugs * fix another widget bug * allow change event name between angularjs and vue * rebuild vue * migrate plugin form directive * get to work * migrate select-on-focus directive and start migrating report-export directive * finish migrating report export directive & popover component + create reusable function to create vue app and add globals to it * rebuild vue * remove angularjs files and move less contents to vue dir * built vue files * fix function signature * fix vue warning * fix ajax request race condition * rebuild vue * add new notification type "help" so the help notification is not cleared when clearing transient notifications * fix some bugs and tests * update screenshot * update screenshot & fix a test * allow using unminified jquery ui + fix bug in last fix * fix error when enrichedheadline is used in modal * add polyfill min.js * remove two todos * fix widget url logic * update some screenshots and fix sanitization/escape issue * update screenshots * rebuild vue * fix url location updating regression in MatomoUrl.updateLocation use * submodule * update screenshots and fix possible error in json parse * built vue files * Merge branch 'vue-period-selector-regression' into vue-reporting-menu * rebuild vue * use correct variable * rebuild vue * fix widget url logic * segment parameter can be undefined now for some reason * fix ngmodel binding in siteselector adapter (for last time hopefully) * the original site selector only set the first site to the first site in the initial sites query if there was only one site in the entire matomo instance * fix sitesmanager ui test failure * fix usersettings test failure * rebuild vue * more siteselector tweaks. * build CoreHome * more siteselector tweaks. * another siteselector issue * update screenshots * update screenshot and try to fix random failure * fix some issues in widget.vue when containerid is specified * fix couple tests * fix several test failures * fix string concat * fix test failure * extra change * fix last change and random failure * styling fix * fix last fix * real fix this time * fix stray request * proper fix * update build files * try to fix random failure * do not submit form * check for api errors in promise chain in ajaxhelper.ts * force a digest after a location change * use proper abortcontroller method instead of promise hack, have to add new polyfill + try to fix random test failure * some UI test fixes * fix some report export issues * several save button fixes + make replace approximation in createAngularJsAdapter better * apply after manual click triggering in savebutton * add names to divs so they can still be queried as they were in angularjs * rebuild vue * now that format_metrics checkbox works, need to check it * fix unintended changes * updated expected screenshots * update two more * go back to previous format_metrics behavior in popover Co-authored-by: Justin Velluppillai <justin@innocraft.com> Co-authored-by: justinvelluppillai <justinvelluppillai@users.noreply.github.com> Co-authored-by: Matthieu Aubry <mattab@users.noreply.github.com> * [Vue] remove support in vue for FormField.allSettings (#18542) * deprecate support in vue for FormField.allSettings since deep watching the property doesnt quite work * built vue files * update screenshots * update screenshot * Show a summary of new features (#18065) * Added "What is new" notification display, populated by a new event * Removed test example event hook * Added support for applying a link attribute to menu items, fixes layout issue for mobile with html menu items * Updated UI test screenshots * Revert accidental edit * Hide the "What's new" icon if there are no new features to show * Changed to use changes.json, track user last viewed, added ui test * Fix UserManager unit tests broken by new ts_changes_viewed user field * Moved getChanges to separate helper class, added unit test, added user view access check * Updated to add new changes table and populate only on plugin update/install * Added missing fixture class, updated UI screenshots * Updated matomo font to add ringing bell and new releases icons * Fix for integration test * Reworked class structure, removed unnecessary angular directive, merged templates, other tidy ups * built vue files * built vue files * Added null user check, missing table exception handling, show plugin name in change title, better handling of missing change fields * Added sample changes file, moved UserChanges db code to changes model, added return type hints, better db error code handling, various other improvements * Revert accidental UI screenshot commit * Fix for incorrect link name parameter in sample changes, switched back to using $db->query for INSERT IGNORE * Integration test fix, UI screenshot updates * Test fix * Added link styling, show CoreHome changes without plugin prefix in title * Update UI test screenshot * Added styles to the popover, added event for filtering changes * Test fix * UI test screenshot updates Co-authored-by: sgiehl <stefan@matomo.org> Co-authored-by: bx80 <bx80@users.noreply.github.com> * Update test translation (#18531) update a test failed XML * updates all submodules (#18541) Co-authored-by: diosmosis <diosmosis@users.noreply.github.com> * Translations update from Hosted Weblate (#18529) * Translated using Weblate (Greek) Currently translated at 100.0% (162 of 162 strings) Translation: Matomo/Plugin CoreAdminHome Translate-URL: https://hosted.weblate.org/projects/matomo/plugin-coreadminhome/el/ [ci skip] Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: Vasilis Lourdas <dev@lourdas.eu> * Translated using Weblate (Chinese (Simplified)) Currently translated at 83.9% (136 of 162 strings) Translation: Matomo/Plugin CoreAdminHome Translate-URL: https://hosted.weblate.org/projects/matomo/plugin-coreadminhome/zh_Hans/ [ci skip] Translated using Weblate (Chinese (Simplified)) Currently translated at 99.6% (620 of 622 strings) Translation: Matomo/Matomo Base Translate-URL: https://hosted.weblate.org/projects/matomo/matomo-base/zh_Hans/ [ci skip] Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: 刘韬 <lyuutau@outlook.com> * Update translation files Updated by "Squash Git commits" hook in Weblate. Translation: Matomo/Plugin CoreAdminHome Translate-URL: https://hosted.weblate.org/projects/matomo/plugin-coreadminhome/ [ci skip] Co-authored-by: Vasilis Lourdas <dev@lourdas.eu> Co-authored-by: 刘韬 <lyuutau@outlook.com> * [Vue] migrate report export directive and popover (#18440) * update files * sidenav start * make getRef a utility method * tweak * add return type * finish converting side-nav directive * starting on reporting menu conversion * remove unused properties * convert reporting pages service * migrate report metadata store * remove angularjs files * migrating reporting pages store * make store adapters more immutable * get service adapters to work * fix a UI test * another html fix * migrate most of reporting menu directive and model * Use themed font family for input forms to override materialize.css styling * rebuild vue * add a missing div * ui test fixes * update styling * get to build * get to load in the UI w/o error * clone result of functions * fix compile issue * migrate widget loader and get to load in UI * rebuild vue * migrate widgetcontainer * migrate widget bydimension container * migrate widget + add tooltips directive * quick fix * Updating version to 4.6.0 * loading in page * update expected screenshot * add wait just in case travis is slow * fix ordering bug * add another wait * rebuild vue * css tweak * fix some bugs and tests * undo screenshot changes * Menus test passing locally * [Vue] date picker viewDate property is not kept up to date (#18385) * viewDate ref is not kept up to date * rebuild corehome * reporting menu subcategory items are meant to be normal links * update some screenshots * use innerText instead of text() since angularjs maintains newlines in HTML that vue does not add * trigger angularjs digest after ajaxhelper request * rebuild vue * update screenshots, fix bug in link generation in reporting menu and allow syncing multiple screenshot regexes at a time * undo box-shadow change for UI tests * fix more issues & update more tests * update some screenshots * fix some tests * rebuild CoreHome * quick fix * built vue files * fix angularjs issue * add comment * update umd files * 4.6.1-rc1 * 4.6.1 * fix field array title * apply some pr feedback * apply more pr feedback * another fix * tweak * fix ng-change not executed before ng-model * fix another set of issues * fix another issue * rebuild vue * better ng-change/ng-model fix * update some screenshots * rebuild vue * remove some TODOs * initiate initial ng-change ONLY for site selectors where this behavior applies * emit/broadcast on correct scope in wrapper * rebuild vue * fix some issues * couple more fixes * fix another title issue * rebuild vue * do not report on ajax errors in notifications if not logged in * migrate reporting page and model * rebuild vue * create sites selector model adapter * fix siteselector vue bug, initial site is only set if there is just one site available * rebuild vue * migrate plugin settings directive * remove TODO * migrate plugin filter directive * migrate two more plugins directives * migrate save button * fix a bunch of bugs * fix another widget bug * allow change event name between angularjs and vue * rebuild vue * migrate plugin form directive * get to work * migrate select-on-focus directive and start migrating report-export directive * finish migrating report export directive & popover component + create reusable function to create vue app and add globals to it * rebuild vue * remove angularjs files and move less contents to vue dir * built vue files * fix function signature * fix vue warning * fix ajax request race condition * rebuild vue * add new notification type "help" so the help notification is not cleared when clearing transient notifications * fix some bugs and tests * update screenshot * update screenshot & fix a test * allow using unminified jquery ui + fix bug in last fix * fix error when enrichedheadline is used in modal * add polyfill min.js * remove two todos * fix widget url logic * update some screenshots and fix sanitization/escape issue * update screenshots * rebuild vue * fix url location updating regression in MatomoUrl.updateLocation use * submodule * update screenshots and fix possible error in json parse * built vue files * Merge branch 'vue-period-selector-regression' into vue-reporting-menu * rebuild vue * use correct variable * rebuild vue * fix widget url logic * segment parameter can be undefined now for some reason * fix ngmodel binding in siteselector adapter (for last time hopefully) * the original site selector only set the first site to the first site in the initial sites query if there was only one site in the entire matomo instance * fix sitesmanager ui test failure * fix usersettings test failure * rebuild vue * more siteselector tweaks. * build CoreHome * more siteselector tweaks. * another siteselector issue * update screenshots * update screenshot and try to fix random failure * fix some issues in widget.vue when containerid is specified * fix couple tests * fix several test failures * fix string concat * fix test failure * extra change * fix last change and random failure * styling fix * fix last fix * real fix this time * fix stray request * proper fix * update build files * try to fix random failure * do not submit form * check for api errors in promise chain in ajaxhelper.ts * force a digest after a location change * use proper abortcontroller method instead of promise hack, have to add new polyfill + try to fix random test failure * some UI test fixes * fix some report export issues * several save button fixes + make replace approximation in createAngularJsAdapter better * apply after manual click triggering in savebutton * add names to divs so they can still be queried as they were in angularjs * rebuild vue * now that format_metrics checkbox works, need to check it * fix unintended changes * updated expected screenshots * update two more * go back to previous format_metrics behavior in popover Co-authored-by: Justin Velluppillai <justin@innocraft.com> Co-authored-by: justinvelluppillai <justinvelluppillai@users.noreply.github.com> Co-authored-by: Matthieu Aubry <mattab@users.noreply.github.com> * [Vue] remove support in vue for FormField.allSettings (#18542) * deprecate support in vue for FormField.allSettings since deep watching the property doesnt quite work * built vue files * update screenshots * update screenshot * fix tests * rebuild * rebuild * order plugins by dependencies in vue:build and fix warning in corehome build * built vue files * built vue files * remove unused imports * built vue files * remove multilinefield component, fieldtextareaarray does the same thing * edit-trigger is not used anywhere * migrate sitetypes model to store * do not load nonexistant files * remove reference nonexistant files * start converting sitefields component * more work on sitefields component * undo submodule change * rebuild * get sitesmanager to build * get SiteFields component to work in UI * datepicker does not format times * export other stores * fix some typing issues and rebuild * start on site management conversion * add more comma delimited props to list + remove controller JS * rebuild * convert sites manager controller to sitesmanagement component * remove TODOs * finish migrating sitesmanager * remove some TODO * get to build * fixes from testing * rebuild * rebuild and fix issue w/ globalsettings hash detection * migrate capabilities-edit component. * some fixes and get to build * get to work * built vue files * get to work and rebuild * migrate user edit form component * some fixes * fixes * another fix * more fixes * update file * more fixes * fix ref * rebuild vue * couple more fixes * migrate paged users list and get to build * fixing issues * workaround vue issue w/ directives that modify css classes on elements that also bind to :class * dropdownmenu directive should be aware of data-target parameter that is required by materialize * handle disabled options in fieldselect * fix issues and rebuild vue * migrate usersmanager component and get to build * forgot to add files, fix some issues + rebuild * migrate usersmanager controllers and twig template parts * fix compile issues and get to build * fix issues and rebuild * fix bug and rebuild * fix bug and rebuild * fix issue * fix issues and rebuild * fix ui test * fix UI test failure * fixing some issues * complete fixes * fix some more issues * fix ui test failures * another fix * several more fixes * fix delete dialog * more fixes * fix styling issue * more fixes * fix another ui test + update other UI tests * fixing edisiteid handling * update screenshots * fix UI tests somre more * fix random failure * fixes * reference css class not attribute (since that is what is added in vue) * fixing more ui tests * try to fix vue css class in directive issue * tweak * in groupedsetting handle templateFile property for angularjs BC * rebuild vue * fix view tracking code link * fixing UI tests * fix selector in test for this branch only * Update screenshot. * update screenshot * update screenshots * style fix * fix selectors and update screenshot * built vue files * Update screenshot + fix title and spacing. * fix password changing * fixing tests * fix more issues * fix styling * built vue files * more fixes * more styling fixes * more fixes * Fix tests locally. * Fixing more issues + getting UI tests to pass locally. * update tagmanger module? * fix UI tests * remove unneeded event * update screenshots * start migrating series-picker * get series picker component to work * start migrating single metric view * update style * fix some issues * get to work in UI * fix percent evolution * fix some UI tests * built vue files * update screenshots Co-authored-by: Ben Burgess <88810029+bx80@users.noreply.github.com> Co-authored-by: sgiehl <stefan@matomo.org> Co-authored-by: bx80 <bx80@users.noreply.github.com> Co-authored-by: Peter Zhang <peter@innocraft.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Weblate (bot) <hosted@weblate.org> Co-authored-by: Vasilis Lourdas <dev@lourdas.eu> Co-authored-by: 刘韬 <lyuutau@outlook.com> Co-authored-by: Justin Velluppillai <justin@innocraft.com> Co-authored-by: justinvelluppillai <justinvelluppillai@users.noreply.github.com> Co-authored-by: Matthieu Aubry <mattab@users.noreply.github.com>
Diffstat (limited to 'plugins')
-rw-r--r--plugins/CoreHome/tests/UI/SingleMetricView_spec.js6
-rw-r--r--plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_formatted_metric.png4
-rw-r--r--plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_goal_metric.png4
-rw-r--r--plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_loaded.png4
-rw-r--r--plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_range.png4
-rw-r--r--plugins/CoreVisualizations/CoreVisualizations.php6
-rw-r--r--plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.html53
-rw-r--r--plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.js142
-rw-r--r--plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.less28
-rw-r--r--plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.html20
-rw-r--r--plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js279
-rw-r--r--plugins/CoreVisualizations/javascripts/seriesPicker.js73
-rw-r--r--plugins/CoreVisualizations/stylesheets/jqplot.css1
-rw-r--r--plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.js790
-rw-r--r--plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.min.js14
-rw-r--r--plugins/CoreVisualizations/vue/dist/umd.metadata.json5
-rw-r--r--plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.adapter.ts36
-rw-r--r--plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.less30
-rw-r--r--plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue192
-rw-r--r--plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.adapter.ts42
-rw-r--r--plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.less (renamed from plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.less)5
-rw-r--r--plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue410
-rw-r--r--plugins/CoreVisualizations/vue/src/index.ts12
-rw-r--r--plugins/CoreVue/types/index.d.ts1
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png4
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png4
-rw-r--r--plugins/UsersManager/vue/dist/UsersManager.umd.js36
-rw-r--r--plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue1
28 files changed, 1610 insertions, 596 deletions
diff --git a/plugins/CoreHome/tests/UI/SingleMetricView_spec.js b/plugins/CoreHome/tests/UI/SingleMetricView_spec.js
index 93b1959894..d0722973b7 100644
--- a/plugins/CoreHome/tests/UI/SingleMetricView_spec.js
+++ b/plugins/CoreHome/tests/UI/SingleMetricView_spec.js
@@ -38,7 +38,7 @@ describe('SingleMetricView', function () {
$('#dashboardWidgetsArea #widgetCoreVisualizationssingleMetricViewcolumn .jqplot-seriespicker').trigger('mouseenter');
});
await page.webpage.evaluate(function(){
- $('#dashboardWidgetsArea .jqplot-seriespicker-popover label:contains(Revenue)').click();
+ $('#dashboardWidgetsArea .jqplot-seriespicker-popover label:contains(Revenue):eq(0)').click();
});
await page.waitForNetworkIdle();
await page.waitForTimeout(250);
@@ -53,7 +53,7 @@ describe('SingleMetricView', function () {
});
await page.waitForTimeout(250);
await page.evaluate(function(){
- $('#dashboardWidgetsArea .jqplot-seriespicker-popover label:contains(_x)').click()
+ $('#dashboardWidgetsArea .jqplot-seriespicker-popover label:contains(_x):eq(0)').click()
});
await page.waitForNetworkIdle();
await page.waitForTimeout(250);
@@ -69,7 +69,7 @@ describe('SingleMetricView', function () {
});
await page.waitForTimeout(250);
await page.evaluate(function(){
- $('#dashboardWidgetsArea #widgetCoreVisualizationssingleMetricViewcolumn .jqplot-seriespicker-popover label:contains(Revenue)').click()
+ $('#dashboardWidgetsArea #widgetCoreVisualizationssingleMetricViewcolumn .jqplot-seriespicker-popover label:contains(Revenue):eq(0)').click()
});
await page.waitForNetworkIdle();
await page.waitForTimeout(250);
diff --git a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_formatted_metric.png b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_formatted_metric.png
index 26ce8b42b5..6f6a0b3397 100644
--- a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_formatted_metric.png
+++ b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_formatted_metric.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fb9305159cdcbf107a6747b315cba146214a1f73f404c200aa37d6734706ccc2
-size 6485
+oid sha256:f52ffb459eaad6bf566e3f837974b82f02ef104a484d16149b3e8f204f6a220f
+size 6302
diff --git a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_goal_metric.png b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_goal_metric.png
index e6cdfa9a26..668b981e43 100644
--- a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_goal_metric.png
+++ b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_goal_metric.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:33bbb2c1fe617afaf7dc2216e981057698abe7679814ca3d4a729517b7859b33
-size 8952
+oid sha256:d7eae513738ddda333cabbf469c1acadb3ac3bc4f7ed80caa0520b0a73b23dfb
+size 9002
diff --git a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_loaded.png b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_loaded.png
index 9dcc86fc15..2d0189601c 100644
--- a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_loaded.png
+++ b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_loaded.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:da10b7861b5b8208164116aebb716bb8d6028701544c7430bf172d9b328fbe74
-size 4741
+oid sha256:e8d3f4bcf5e2b039f91160466eb94f26f54f24d57d03188cce70132795789609
+size 4646
diff --git a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_range.png b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_range.png
index f685b2dc05..97a4a0938c 100644
--- a/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_range.png
+++ b/plugins/CoreHome/tests/UI/expected-screenshots/SingleMetricView_range.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9cbbbee4066bb4ae19015e8398353d512f42d943cd27a375554218f865581738
-size 5059
+oid sha256:307d7efaa78defa78c7162bae0e51beb7d8d963db529ab5f4a95153f339c2fd3
+size 4844
diff --git a/plugins/CoreVisualizations/CoreVisualizations.php b/plugins/CoreVisualizations/CoreVisualizations.php
index 095253612d..a2f0c18205 100644
--- a/plugins/CoreVisualizations/CoreVisualizations.php
+++ b/plugins/CoreVisualizations/CoreVisualizations.php
@@ -39,8 +39,8 @@ class CoreVisualizations extends \Piwik\Plugin
public function getStylesheetFiles(&$stylesheets)
{
- $stylesheets[] = "plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.less";
- $stylesheets[] = "plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.less";
+ $stylesheets[] = "plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.less";
+ $stylesheets[] = "plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.less";
$stylesheets[] = "plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less";
$stylesheets[] = "plugins/CoreVisualizations/stylesheets/jqplot.css";
@@ -48,8 +48,6 @@ class CoreVisualizations extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
- $jsFiles[] = "plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.js";
- $jsFiles[] = "plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js";
$jsFiles[] = "plugins/CoreVisualizations/javascripts/seriesPicker.js";
$jsFiles[] = "plugins/CoreVisualizations/javascripts/jqplot.js";
diff --git a/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.html b/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.html
deleted file mode 100644
index cfd36998dc..0000000000
--- a/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<div
- class="jqplot-seriespicker"
- ng-class="{open: $ctrl.isPopupVisible}"
- ng-mouseenter="$ctrl.isPopupVisible = true"
- ng-mouseleave="$ctrl.onLeavePopup()"
->
- <a
- href="#"
- ng-click="$event.preventDefault(); $event.stopPropagation();"
- >
- +
- </a>
- <div
- class="jqplot-seriespicker-popover"
- ng-if="$ctrl.isPopupVisible"
- >
- <p class="headline">{{ ($ctrl.multiselect ? 'General_MetricsToPlot' : 'General_MetricToPlot') | translate }}</p>
- <p
- ng-repeat="columnConfig in $ctrl.selectableColumns"
- class="pickColumn"
- ng-click="$ctrl.optionSelected(columnConfig.column, $ctrl.columnStates)"
- >
- <label>
- <input
- class="select"
- ng-checked="$ctrl.columnStates[columnConfig.column]"
- ng-attr-type="{{ $ctrl.multiselect ? 'checkbox' : 'radio' }}"
- />
- <span>{{ columnConfig.translation }}</span>
- </label>
- </p>
- <p
- ng-if="$ctrl.selectableRows.length"
- class="headline recordsToPlot"
- >
- {{ 'General_RecordsToPlot' | translate }}
- </p>
- <p
- ng-repeat="rowConfig in $ctrl.selectableRows"
- class="pickRow"
- ng-click="$ctrl.optionSelected(rowConfig.matcher, $ctrl.rowStates)"
- >
- <label>
- <input
- class="select"
- ng-checked="$ctrl.rowStates[rowConfig.matcher]"
- ng-attr-type="{{ $ctrl.multiselect ? 'checkbox' : 'radio' }}"
- />
- <span>{{ rowConfig.label }}</span>
- </label>
- </p>
- </div>
-</div> \ No newline at end of file
diff --git a/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.js b/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.js
deleted file mode 100644
index bbfeb9444c..0000000000
--- a/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*!
- * Matomo - free/libre analytics platform
- *
- * @link https://matomo.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-/**
- * This series picker component is a popup that displays a list of metrics/row
- * values that can be selected. It's used by certain datatable visualizations
- * to allow users to select different data series for display.
- *
- * Inputs:
- * - multiselect: true if the picker should allow selecting multiple items, false
- * if otherwise.
- * - selectableColumns: the list of selectable metric values. must be a list of
- * objects with the following properties:
- * * column: the ID of the column, eg, nb_visits
- * * translation: the translated text for the column, eg, Visits
- * - selectableRows: the list of selectable row values. must be a list of objects
- * with the following properties:
- * * matcher: the ID of the row
- * * label: the display text for the row
- * - selectedColumns: the list of selected columns. should be a list of strings
- * that correspond to the 'column' property in selectableColumns.
- * - selectedRows: the list of selected rows. should be a list of strings that
- * correspond to the 'matcher' property in selectableRows.
- * - onSelect: expression invoked when a user makes a new selection. invoked
- * with the following local variables:
- * * columns: list of IDs of new selected columns, if any
- * * rows: list of matchers of new selected rows, if any
- *
- * Usage:
- * <piwik-series-picker />
- */
-(function () {
- angular.module('piwikApp').component('piwikSeriesPicker', {
- templateUrl: 'plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.html?cb=' + piwik.cacheBuster,
- bindings: {
- multiselect: '<',
- selectableColumns: '<',
- selectableRows: '<',
- selectedColumns: '<',
- selectedRows: '<',
- onSelect: '&'
- },
- controller: SeriesPickerController
- });
-
- SeriesPickerController.$inject = [];
-
- function SeriesPickerController() {
- var vm = this;
- vm.isPopupVisible = false;
-
- // note: column & row states are separated since it's technically possible (though
- // highly improbable) that a row value matcher will be the same as a recognized column.
- vm.columnStates = {};
- vm.rowStates = {};
- vm.optionSelected = optionSelected;
- vm.onLeavePopup = onLeavePopup;
- vm.$onInit = $onInit;
-
- function $onInit() {
- vm.columnStates = getInitialOptionStates(vm.selectableColumns, vm.selectedColumns);
- vm.rowStates = getInitialOptionStates(vm.selectableRows, vm.selectedRows);
- }
-
- function getInitialOptionStates(allOptions, selectedOptions) {
- var states = {};
-
- allOptions.forEach(function (columnConfig) {
- states[columnConfig.column || columnConfig.matcher] = false;
- });
-
- selectedOptions.forEach(function (column) {
- states[column] = true;
- });
-
- return states;
- }
-
- function optionSelected(optionValue, optionStates) {
- if (!vm.multiselect) {
- unselectOptions(vm.columnStates);
- unselectOptions(vm.rowStates);
- }
-
- optionStates[optionValue] = !optionStates[optionValue];
-
- triggerOnSelectAndClose();
- }
-
- function onLeavePopup() {
- vm.isPopupVisible = false;
-
- if (optionsChanged()) {
- triggerOnSelectAndClose();
- }
- }
-
- function triggerOnSelectAndClose() {
- if (!vm.onSelect) {
- return;
- }
-
- vm.isPopupVisible = false;
-
- vm.onSelect({
- columns: getSelected(vm.columnStates),
- rows: getSelected(vm.rowStates)
- });
- }
-
- function optionsChanged() {
- return !arrayEqual(getSelected(vm.columnStates), vm.selectedColumns)
- || !arrayEqual(getSelected(vm.rowStates), vm.selectedRows);
- }
-
- function arrayEqual(lhs, rhs) {
- if (lhs.length !== rhs.length) {
- return false;
- }
-
- return lhs
- .filter(function (element) { return rhs.indexOf(element) === -1; })
- .length === 0;
- }
-
- function unselectOptions(optionStates) {
- Object.keys(optionStates).forEach(function (optionName) {
- optionStates[optionName] = false;
- });
- }
-
- function getSelected(optionStates) {
- return Object.keys(optionStates).filter(function (optionName) {
- return !! optionStates[optionName];
- });
- }
- }
-})(); \ No newline at end of file
diff --git a/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.less b/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.less
deleted file mode 100644
index 9d4fdc8de9..0000000000
--- a/plugins/CoreVisualizations/angularjs/series-picker/series-picker.component.less
+++ /dev/null
@@ -1,28 +0,0 @@
-piwik-series-picker {
- display: inline-block;
-
- .jqplot-seriespicker {
- &:not(.open) {
- opacity: .55;
- }
-
- &.open { // while open, make sure we're above other series picker icons
- z-index: 1000;
- }
-
- > a {
- display: inline-block;
- opacity: 0;
- position: absolute;
- }
-
- position: relative;
- }
-
- .jqplot-seriespicker-popover {
- position: absolute;
-
- top: -3px;
- left: -4px;
- }
-}
diff --git a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.html b/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.html
deleted file mode 100644
index e302dda171..0000000000
--- a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<div class="singleMetricView" ng-class="{'loading': $ctrl.isLoading}">
- <piwik-sparkline
- class="metric-sparkline"
- params="$ctrl.sparklineParams"
- >
- </piwik-sparkline>
- <div class="metric-value">
- <span title="{{ $ctrl.metricDocumentation }}">
- <strong>{{ $ctrl.metricValue }}</strong> {{ ($ctrl.metricTranslation || '').toLowerCase() }}
- </span>
- <span class="metricEvolution"
- ng-if="$ctrl.pastValue !== null"
- title="{{ 'General_EvolutionSummaryGeneric'|translate:$ctrl.metricValue:$ctrl.getCurrentPeriod():$ctrl.pastValue:$ctrl.pastPeriod:$ctrl.metricChangePercent }}"
- >
- <span ng-class="{'positive-evolution': $ctrl.metricValueUnformatted > $ctrl.pastValueUnformatted, 'negative-evolution': $ctrl.metricValueUnformatted < $ctrl.pastValueUnformatted}">
- {{ $ctrl.metricChangePercent }}
- </span>
- </span>
- </div>
-</div> \ No newline at end of file
diff --git a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js b/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js
deleted file mode 100644
index 2c3b19aff7..0000000000
--- a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js
+++ /dev/null
@@ -1,279 +0,0 @@
-/*!
- * Matomo - free/libre analytics platform
- *
- * @link https://matomo.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-/**
- * Usage:
- * <piwik-single-metric-view>
- */
-(function () {
- angular.module('piwikApp').component('piwikSingleMetricView', {
- templateUrl: 'plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.html?cb=' + piwik.cacheBuster,
- bindings: {
- metric: '<',
- idGoal: '<',
- metricTranslations: '<',
- metricDocumentations: '<',
- goals: '<',
- goalMetrics: '<'
- },
- controller: SingleMetricViewController
- });
-
- SingleMetricViewController.$inject = ['piwik', 'piwikApi', '$element', '$httpParamSerializer', '$compile', '$scope', 'piwikPeriods', '$q'];
-
- function SingleMetricViewController(piwik, piwikApi, $element, $httpParamSerializer, $compile, $scope, piwikPeriods, $q) {
- var seriesPickerScope;
-
- var vm = this;
- vm.metricValue = null;
- vm.isLoading = false;
- vm.metricTranslation = null;
- vm.metricDocumentation = null;
- vm.selectableColumns = [];
- vm.responses = null;
- vm.sparklineParams = {};
- vm.$onInit = $onInit;
- vm.$onChanges = $onChanges;
- vm.$onDestroy = $onDestroy;
- vm.getCurrentPeriod = getCurrentPeriod;
- vm.getMetricTranslation = getMetricTranslation;
- vm.setMetric = setMetric;
-
- function setSparklineParams() {
- var params = { module: 'API', action: 'get', columns: vm.metric };
- if (isIdGoalSet()) {
- params.idGoal = vm.idGoal;
- params.module = 'Goals';
- }
- vm.sparklineParams = params;
- }
-
- function $onInit() {
- vm.selectedColumns = [vm.metric];
- if (piwik.period !== 'range') {
- vm.pastPeriod = getPastPeriodStr();
- }
-
- setSelectableColumns();
-
- createSeriesPicker();
-
- $element.closest('.widgetContent')
- .on('widget:destroy', function() { $scope.$parent.$destroy(); })
- .on('widget:reload', function() { $scope.$parent.$destroy(); });
-
- setSparklineParams();
- }
-
- function $onChanges(changes) {
- if (changes.metric && changes.metric.previousValue !== changes.metric.currentValue) {
- onMetricChanged();
- }
- }
-
- function $onDestroy() {
- $element.closest('.widgetContent').off('widget:destroy').off('widget:reload');
- destroySeriesPicker();
- }
-
- function fetchData() {
- vm.isLoading = true;
-
- var promises = [];
-
- var apiModule = 'API';
- var apiAction = 'get';
-
- var extraParams = {};
- if (isIdGoalSet()) {
- extraParams.idGoal = vm.idGoal;
- // the conversion rate added by the AddColumnsProcessedMetrics filter conflicts w/ the goals one, so don't run it
- extraParams.filter_add_columns_when_show_all_columns = 0;
-
- apiModule = 'Goals';
- apiAction = 'get';
- }
-
- // first request for formatted data
- promises.push(piwikApi.fetch($.extend({
- method: apiModule + '.' + apiAction,
- format_metrics: 'all'
- }, extraParams)));
-
- if (piwik.period !== 'range') {
- // second request for unformatted data so we can calculate evolution
- promises.push(piwikApi.fetch($.extend({
- method: apiModule + '.' + apiAction,
- format_metrics: '0'
- }, extraParams)));
-
- // third request for past data (unformatted)
- promises.push(piwikApi.fetch($.extend({
- method: apiModule + '.' + apiAction,
- date: getLastPeriodDate(),
- format_metrics: '0',
- }, extraParams)));
-
- // fourth request for past data (formatted for tooltip display)
- promises.push(piwikApi.fetch($.extend({
- method: apiModule + '.' + apiAction,
- date: getLastPeriodDate(),
- format_metrics: 'all',
- }, extraParams)));
- }
-
- return $q.all(promises).then(function (responses) {
- vm.responses = responses;
- vm.isLoading = false;
- });
- }
-
- function recalculateValues() {
- // update display based on processed report metadata
- setWidgetTitle();
- vm.metricDocumentation = getMetricDocumentation();
-
- // update data
- var currentData = vm.responses[0];
- vm.metricValue = currentData[vm.metric] || 0;
-
- if (vm.responses[1]) {
- vm.metricValueUnformatted = vm.responses[1][vm.metric];
-
- var pastData = vm.responses[2];
- vm.pastValueUnformatted = pastData[vm.metric] || 0;
-
- var evolution = piwik.helper.calculateEvolution(vm.metricValueUnformatted, vm.pastValueUnformatted);
- vm.metricChangePercent = (evolution * 100).toFixed(2) + ' %';
-
- var pastDataFormatted = vm.responses[3];
- vm.pastValue = pastDataFormatted[vm.metric] || 0;
- } else {
- vm.pastValue = null;
- vm.metricChangePercent = null;
- }
-
- // don't change the metric translation until data is fetched to avoid loading state confusion
- vm.metricTranslation = getMetricTranslation();
- }
-
- function getLastPeriodDate() {
- var RangePeriod = piwikPeriods.get('range');
- var result = RangePeriod.getLastNRange(piwik.period, 2, piwik.currentDateString).startDate;
- return piwikPeriods.format(result);
- }
-
- function setWidgetTitle() {
- var title = vm.getMetricTranslation();
- if (isIdGoalSet()) {
- var goalName = vm.goals[vm.idGoal].name;
- title = goalName + ' - ' + title;
- }
-
- $element.closest('div.widget').find('.widgetTop > .widgetName > span').text(title);
- }
-
- function getCurrentPeriod() {
- if (piwik.startDateString === piwik.endDateString) {
- return piwik.endDateString;
- }
- return piwik.startDateString + ', ' + piwik.endDateString;
- }
-
- function createSeriesPicker() {
- vm.selectedColumns = [vm.idGoal ? ('goal' + vm.idGoal + '_' + vm.metric) : vm.metric];
-
- var $widgetName = $element.closest('div.widget').find('.widgetTop > .widgetName');
-
- var $seriesPicker = $('<piwik-series-picker class="single-metric-view-picker" multiselect="false" ' +
- 'selectable-columns="$ctrl.selectableColumns" selectable-rows="[]" selected-columns="$ctrl.selectedColumns" ' +
- 'selected-rows="[]" on-select="$ctrl.setMetric(columns[0])" />');
-
- seriesPickerScope = $scope.$new();
- $compile($seriesPicker)(seriesPickerScope);
-
- $widgetName.append($seriesPicker);
- }
-
- function destroySeriesPicker() {
- $element.closest('div.widget').find('.single-metric-view-picker').remove();
-
- seriesPickerScope.$destroy();
- seriesPickerScope = null;
- }
-
- function getMetricDocumentation() {
- if (!vm.metricDocumentations || !vm.metricDocumentations[vm.metric]) {
- return '';
- }
-
- return vm.metricDocumentations[vm.metric];
- }
-
- function getMetricTranslation() {
- if (!vm.metricTranslations || !vm.metricTranslations[vm.metric]) {
- return '';
- }
-
- return vm.metricTranslations[vm.metric];
- }
-
- function setSelectableColumns() {
- var result = [];
- Object.keys(vm.metricTranslations).forEach(function (column) {
- result.push({ column: column, translation: vm.metricTranslations[column] });
- });
-
- Object.keys(vm.goals).forEach(function (idgoal) {
- var goal = vm.goals[idgoal];
- vm.goalMetrics.forEach(function (column) {
- result.push({
- column: 'goal' + goal.idgoal + '_' + column,
- translation: goal.name + ' - ' + vm.metricTranslations[column]
- });
- });
- });
-
- vm.selectableColumns = result;
- }
-
- function onMetricChanged() {
- setSparklineParams();
-
- fetchData().then(recalculateValues);
-
- // notify widget of parameter change so it is replaced
- $element.closest('[widgetId]').trigger('setParameters', { column: vm.metric, idGoal: vm.idGoal });
- }
-
- function setMetric(newColumn) {
- var idGoal;
-
- var m = newColumn.match(/^goal([0-9]+)_(.*)/);
- if (m) {
- idGoal = +m[1];
- newColumn = m[2];
- }
-
- if (vm.metric !== newColumn || idGoal !== vm.idGoal) {
- vm.metric = newColumn;
- vm.idGoal = idGoal;
- onMetricChanged();
- }
- }
-
- function getPastPeriodStr() {
- var startDate = piwikPeriods.get('range').getLastNRange(piwik.period, 2, piwik.currentDateString).startDate;
- var dateRange = piwikPeriods.get(piwik.period).parse(startDate).getDateRange();
- return piwikPeriods.format(dateRange[0]) + ',' + piwikPeriods.format(dateRange[1]);
- }
-
- function isIdGoalSet() {
- return vm.idGoal || vm.idGoal === 0;
- }
- }
-})();
diff --git a/plugins/CoreVisualizations/javascripts/seriesPicker.js b/plugins/CoreVisualizations/javascripts/seriesPicker.js
index 2ca659445c..6ea90cc326 100644
--- a/plugins/CoreVisualizations/javascripts/seriesPicker.js
+++ b/plugins/CoreVisualizations/javascripts/seriesPicker.js
@@ -83,47 +83,48 @@
});
// initialize dom element
- var seriesPicker = '<piwik-series-picker'
- + ' multiselect="' + (this.multiSelect ? 'true' : 'false') + '"'
- + ' selectable-columns="selectableColumns"'
- + ' selectable-rows="selectableRows"'
- + ' selected-columns="selectedColumns"'
- + ' selected-rows="selectedRows"'
- + ' on-select="selectionChanged(columns, rows)"/>';
-
- this.domElem = $(seriesPicker);
+ this.domElem = $('<div style="display:inline-block"><div></div></div>');
$(this).trigger('placeSeriesPicker');
- piwikHelper.compileAngularComponents(this.domElem, {
- params: {
- selectableColumns: this.selectableColumns,
- selectableRows: this.selectableRows,
- selectedColumns: selectedColumns,
- selectedRows: selectedRows,
- selectionChanged: function selectionChanged(columns, rows) {
- if (columns.length === 0 && rows.length === 0) {
- return;
- }
-
- rows = rows.map(encodeURIComponent);
-
- $(self).trigger('seriesPicked', [columns, rows]);
-
- // inform dashboard widget about changed parameters (to be restored on reload)
- var UI = require('piwik/UI');
- var params = {
- columns: columns,
- columns_to_display: columns,
- rows: rows,
- rows_to_display: rows
- };
-
- var tableNode = $('#' + self.dataTableId);
- UI.DataTable.prototype.notifyWidgetParametersChange(tableNode, params);
+ var createVNode = Vue.createVNode;
+ var createVueApp = CoreHome.createVueApp;
+ var SeriesPicker = CoreVisualizations.SeriesPicker;
+
+ var app = createVueApp({
+ render: function () {
+ return createVNode(SeriesPicker, {
+ multiselect: self.multiSelect,
+ selectableColumns: self.selectableColumns,
+ selectableRows: self.selectableRows,
+ selectedColumns: selectedColumns,
+ selectedRows: selectedRows,
+ onSelect: function selectionChanged(event) {
+ var columns = event.columns, rows = event.rows;
+ if (columns.length === 0 && rows.length === 0) {
+ return;
}
- }
+
+ rows = rows.map(encodeURIComponent);
+
+ $(self).trigger('seriesPicked', [columns, rows]);
+
+ // inform dashboard widget about changed parameters (to be restored on reload)
+ var UI = require('piwik/UI');
+ var params = {
+ columns: columns,
+ columns_to_display: columns,
+ rows: rows,
+ rows_to_display: rows
+ };
+
+ var tableNode = $('#' + self.dataTableId);
+ UI.DataTable.prototype.notifyWidgetParametersChange(tableNode, params);
+ }
+ });
+ }
});
+ app.mount(this.domElem.children()[0]);
function isItemDisplayed(columnOrRowConfig) {
return columnOrRowConfig.displayed;
diff --git a/plugins/CoreVisualizations/stylesheets/jqplot.css b/plugins/CoreVisualizations/stylesheets/jqplot.css
index 2be3bda099..e2c7e60eb6 100644
--- a/plugins/CoreVisualizations/stylesheets/jqplot.css
+++ b/plugins/CoreVisualizations/stylesheets/jqplot.css
@@ -204,7 +204,6 @@ a.rowevolution-startmulti {
.jqplot-seriespicker {
display: block;
- position: absolute;
z-index: 9;
width: 24px;
height: 16px;
diff --git a/plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.js b/plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.js
new file mode 100644
index 0000000000..2299cdb54d
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.js
@@ -0,0 +1,790 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+ if(typeof exports === 'object' && typeof module === 'object')
+ module.exports = factory(require("CoreHome"), require("vue"));
+ else if(typeof define === 'function' && define.amd)
+ define(["CoreHome", ], factory);
+ else if(typeof exports === 'object')
+ exports["CoreVisualizations"] = factory(require("CoreHome"), require("vue"));
+ else
+ root["CoreVisualizations"] = factory(root["CoreHome"], root["Vue"]);
+})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__19dc__, __WEBPACK_EXTERNAL_MODULE__8bbf__) {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ }
+/******/ };
+/******/
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = function(exports) {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/
+/******/ // create a fake namespace object
+/******/ // mode & 1: value is a module id, require it
+/******/ // mode & 2: merge all properties of value into the ns
+/******/ // mode & 4: return value when already ns object
+/******/ // mode & 8|1: behave like require
+/******/ __webpack_require__.t = function(value, mode) {
+/******/ if(mode & 1) value = __webpack_require__(value);
+/******/ if(mode & 8) return value;
+/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ var ns = Object.create(null);
+/******/ __webpack_require__.r(ns);
+/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ return ns;
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "plugins/CoreVisualizations/vue/dist/";
+/******/
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = "fae3");
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ "19dc":
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE__19dc__;
+
+/***/ }),
+
+/***/ "8bbf":
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE__8bbf__;
+
+/***/ }),
+
+/***/ "fae3":
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+// ESM COMPAT FLAG
+__webpack_require__.r(__webpack_exports__);
+
+// EXPORTS
+__webpack_require__.d(__webpack_exports__, "SeriesPicker", function() { return /* reexport */ SeriesPicker; });
+__webpack_require__.d(__webpack_exports__, "SingleMetricView", function() { return /* reexport */ SingleMetricView; });
+
+// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
+// This file is imported into lib/wc client bundles.
+
+if (typeof window !== 'undefined') {
+ var currentScript = window.document.currentScript
+ if (false) { var getCurrentScript; }
+
+ var src = currentScript && currentScript.src.match(/(.+\/)[^/]+\.js(\?.*)?$/)
+ if (src) {
+ __webpack_require__.p = src[1] // eslint-disable-line
+ }
+}
+
+// Indicate to webpack that this file can be concatenated
+/* harmony default export */ var setPublicPath = (null);
+
+// EXTERNAL MODULE: external "CoreHome"
+var external_CoreHome_ = __webpack_require__("19dc");
+
+// EXTERNAL MODULE: external {"commonjs":"vue","commonjs2":"vue","root":"Vue"}
+var external_commonjs_vue_commonjs2_vue_root_Vue_ = __webpack_require__("8bbf");
+
+// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue?vue&type=template&id=bd202f38
+
+var _hoisted_1 = {
+ key: 0,
+ class: "jqplot-seriespicker-popover"
+};
+var _hoisted_2 = {
+ class: "headline"
+};
+var _hoisted_3 = ["onClick"];
+var _hoisted_4 = ["type", "checked"];
+var _hoisted_5 = {
+ key: 0,
+ class: "headline recordsToPlot"
+};
+var _hoisted_6 = ["onClick"];
+var _hoisted_7 = ["type", "checked"];
+function SeriesPickervue_type_template_id_bd202f38_render(_ctx, _cache, $props, $setup, $data, $options) {
+ return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", {
+ class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["jqplot-seriespicker", {
+ open: _ctx.isPopupVisible
+ }]),
+ onMouseenter: _cache[1] || (_cache[1] = function ($event) {
+ return _ctx.isPopupVisible = true;
+ }),
+ onMouseleave: _cache[2] || (_cache[2] = function ($event) {
+ return _ctx.onLeavePopup();
+ })
+ }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", {
+ href: "#",
+ onClick: _cache[0] || (_cache[0] = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withModifiers"])(function () {}, ["prevent", "stop"]))
+ }, " + "), _ctx.isPopupVisible ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", _hoisted_2, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate(_ctx.multiselect ? 'General_MetricsToPlot' : 'General_MetricToPlot')), 1), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.selectableColumns, function (columnConfig) {
+ return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("p", {
+ class: "pickColumn",
+ onClick: function onClick($event) {
+ return _ctx.optionSelected(columnConfig.column, _ctx.columnStates);
+ },
+ key: columnConfig.column
+ }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("label", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", {
+ class: "select",
+ type: _ctx.multiselect ? 'checkbox' : 'radio',
+ checked: !!_ctx.columnStates[columnConfig.column]
+ }, null, 8, _hoisted_4), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(columnConfig.translation), 1)])], 8, _hoisted_3);
+ }), 128)), _ctx.selectableRows.length ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("p", _hoisted_5, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_RecordsToPlot')), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.selectableRows, function (rowConfig) {
+ return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("p", {
+ class: "pickRow",
+ onClick: function onClick($event) {
+ return _ctx.optionSelected(rowConfig.matcher, _ctx.rowStates);
+ },
+ key: rowConfig.matcher
+ }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("label", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", {
+ class: "select",
+ type: _ctx.multiselect ? 'checkbox' : 'radio',
+ checked: !!_ctx.rowStates[rowConfig.matcher]
+ }, null, 8, _hoisted_7), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(rowConfig.label), 1)])], 8, _hoisted_6);
+ }), 128))])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)], 34);
+}
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue?vue&type=template&id=bd202f38
+
+// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue?vue&type=script&lang=ts
+
+
+
+function getInitialOptionStates(allOptions, selectedOptions) {
+ var states = {};
+ allOptions.forEach(function (columnConfig) {
+ var name = columnConfig.column || columnConfig.matcher;
+ states[name] = false;
+ });
+ selectedOptions.forEach(function (column) {
+ states[column] = true;
+ });
+ return states;
+}
+
+function arrayEqual(lhs, rhs) {
+ if (lhs.length !== rhs.length) {
+ return false;
+ }
+
+ return lhs.filter(function (element) {
+ return rhs.indexOf(element) === -1;
+ }).length === 0;
+}
+
+function unselectOptions(optionStates) {
+ Object.keys(optionStates).forEach(function (optionName) {
+ optionStates[optionName] = false;
+ });
+}
+
+function getSelected(optionStates) {
+ return Object.keys(optionStates).filter(function (optionName) {
+ return !!optionStates[optionName];
+ });
+}
+
+/* harmony default export */ var SeriesPickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({
+ props: {
+ multiselect: Boolean,
+ selectableColumns: {
+ type: Array,
+ default: function _default() {
+ return [];
+ }
+ },
+ selectableRows: {
+ type: Array,
+ default: function _default() {
+ return [];
+ }
+ },
+ selectedColumns: {
+ type: Array,
+ default: function _default() {
+ return [];
+ }
+ },
+ selectedRows: {
+ type: Array,
+ default: function _default() {
+ return [];
+ }
+ }
+ },
+ data: function data() {
+ return {
+ isPopupVisible: false,
+ columnStates: getInitialOptionStates(this.selectableColumns, this.selectedColumns),
+ rowStates: getInitialOptionStates(this.selectableRows, this.selectedRows)
+ };
+ },
+ emits: ['select'],
+ created: function created() {
+ this.optionSelected = Object(external_CoreHome_["debounce"])(this.optionSelected, 0);
+ },
+ methods: {
+ optionSelected: function optionSelected(optionValue, optionStates) {
+ if (!this.multiselect) {
+ unselectOptions(this.columnStates);
+ unselectOptions(this.rowStates);
+ }
+
+ optionStates[optionValue] = !optionStates[optionValue];
+ this.triggerOnSelectAndClose();
+ },
+ onLeavePopup: function onLeavePopup() {
+ this.isPopupVisible = false;
+
+ if (this.optionsChanged()) {
+ this.triggerOnSelectAndClose();
+ }
+ },
+ triggerOnSelectAndClose: function triggerOnSelectAndClose() {
+ this.isPopupVisible = false;
+ this.$emit('select', {
+ columns: getSelected(this.columnStates),
+ rows: getSelected(this.rowStates)
+ });
+ },
+ optionsChanged: function optionsChanged() {
+ return !arrayEqual(getSelected(this.columnStates), this.selectedColumns) || !arrayEqual(getSelected(this.rowStates), this.selectedRows);
+ }
+ }
+}));
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue?vue&type=script&lang=ts
+
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue
+
+
+
+SeriesPickervue_type_script_lang_ts.render = SeriesPickervue_type_template_id_bd202f38_render
+
+/* harmony default export */ var SeriesPicker = (SeriesPickervue_type_script_lang_ts);
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.adapter.ts
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+
+/* harmony default export */ var SeriesPicker_adapter = (Object(external_CoreHome_["createAngularJsAdapter"])({
+ component: SeriesPicker,
+ scope: {
+ multiselect: {
+ angularJsBind: '<'
+ },
+ selectableColumns: {
+ angularJsBind: '<'
+ },
+ selectableRows: {
+ angularJsBind: '<'
+ },
+ selectedColumns: {
+ angularJsBind: '<'
+ },
+ selectedRows: {
+ angularJsBind: '<'
+ },
+ onSelect: {
+ angularJsBind: '&',
+ vue: 'select'
+ }
+ },
+ directiveName: 'piwikSeriesPicker',
+ restrict: 'E'
+}));
+// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue?vue&type=template&id=2e2e889f
+
+var SingleMetricViewvue_type_template_id_2e2e889f_hoisted_1 = {
+ class: "metric-sparkline"
+};
+var SingleMetricViewvue_type_template_id_2e2e889f_hoisted_2 = {
+ class: "metric-value"
+};
+var SingleMetricViewvue_type_template_id_2e2e889f_hoisted_3 = ["title"];
+var SingleMetricViewvue_type_template_id_2e2e889f_hoisted_4 = ["title"];
+function SingleMetricViewvue_type_template_id_2e2e889f_render(_ctx, _cache, $props, $setup, $data, $options) {
+ var _component_Sparkline = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Sparkline");
+
+ return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", {
+ class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["singleMetricView", {
+ 'loading': _ctx.isLoading
+ }]),
+ ref: "root"
+ }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", SingleMetricViewvue_type_template_id_2e2e889f_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Sparkline, {
+ params: _ctx.sparklineParams
+ }, null, 8, ["params"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", SingleMetricViewvue_type_template_id_2e2e889f_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", {
+ title: _ctx.metricDocumentation
+ }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("strong", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.metricValue), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(" " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])((_ctx.metricTranslation || '').toLowerCase()), 1)], 8, SingleMetricViewvue_type_template_id_2e2e889f_hoisted_3), _ctx.pastValue !== null ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("span", {
+ key: 0,
+ class: "metricEvolution",
+ title: _ctx.translate('General_EvolutionSummaryGeneric', _ctx.metricValue, _ctx.currentPeriod, _ctx.pastValue, _ctx.pastPeriod, _ctx.metricChangePercent)
+ }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", {
+ class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])({
+ 'positive-evolution': _ctx.metricValueUnformatted > _ctx.pastValueUnformatted,
+ 'negative-evolution': _ctx.metricValueUnformatted < _ctx.pastValueUnformatted
+ })
+ }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.metricChangePercent), 3)], 8, SingleMetricViewvue_type_template_id_2e2e889f_hoisted_4)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)])], 2);
+}
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue?vue&type=template&id=2e2e889f
+
+// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue?vue&type=script&lang=ts
+function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
+
+function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
+
+function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
+
+function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
+
+function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+
+function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
+
+
+
+
+
+function getPastPeriodStr() {
+ var _Range$getLastNRange = external_CoreHome_["Range"].getLastNRange(external_CoreHome_["Matomo"].period, 2, external_CoreHome_["Matomo"].currentDateString),
+ startDate = _Range$getLastNRange.startDate;
+
+ var dateRange = external_CoreHome_["Periods"].get(external_CoreHome_["Matomo"].period).parse(startDate).getDateRange();
+ return "".concat(Object(external_CoreHome_["format"])(dateRange[0]), ",").concat(Object(external_CoreHome_["format"])(dateRange[1]));
+}
+
+var _window = window,
+ $ = _window.$;
+/* harmony default export */ var SingleMetricViewvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({
+ props: {
+ metric: {
+ type: String,
+ required: true
+ },
+ idGoal: [String, Number],
+ metricTranslations: {
+ type: Object,
+ required: true
+ },
+ metricDocumentations: Object,
+ goals: {
+ type: Object,
+ required: true
+ },
+ goalMetrics: Array
+ },
+ components: {
+ Sparkline: external_CoreHome_["Sparkline"]
+ },
+ setup: function setup(props) {
+ var root = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(null);
+ var isLoading = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(false);
+ var responses = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(null);
+ var actualMetric = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(props.metric);
+ var actualIdGoal = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(props.idGoal);
+ var selectedColumns = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ return [actualIdGoal.value ? "goal".concat(actualIdGoal.value, "_").concat(actualMetric.value) : actualMetric.value];
+ });
+ var metricValueUnformatted = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var _responses$value;
+
+ if (!((_responses$value = responses.value) !== null && _responses$value !== void 0 && _responses$value[1])) {
+ return null;
+ }
+
+ return responses.value[1][actualMetric.value];
+ });
+ var pastValueUnformatted = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var _responses$value2;
+
+ if (!((_responses$value2 = responses.value) !== null && _responses$value2 !== void 0 && _responses$value2[2])) {
+ return null;
+ }
+
+ return responses.value[2][actualMetric.value] || 0;
+ });
+ var metricChangePercent = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ if (!metricValueUnformatted.value) {
+ return null;
+ }
+
+ var currentValue = typeof metricValueUnformatted.value === 'string' ? parseInt(metricValueUnformatted.value, 10) : metricValueUnformatted.value;
+ var pastValue = typeof pastValueUnformatted.value === 'string' ? parseInt(pastValueUnformatted.value, 10) : pastValueUnformatted.value;
+ var evolution = external_CoreHome_["Matomo"].helper.calculateEvolution(currentValue, pastValue);
+ return "".concat((evolution * 100).toFixed(2), " %");
+ });
+ var pastValue = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var _responses$value3;
+
+ if (!((_responses$value3 = responses.value) !== null && _responses$value3 !== void 0 && _responses$value3[3])) {
+ return null;
+ }
+
+ var pastDataFormatted = responses.value[3];
+ return pastDataFormatted[actualMetric.value] || 0;
+ });
+ var metricValue = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var _responses$value4;
+
+ if (!((_responses$value4 = responses.value) !== null && _responses$value4 !== void 0 && _responses$value4[0])) {
+ return null;
+ }
+
+ var currentData = responses.value[0];
+ return currentData[actualMetric.value] || 0;
+ });
+ var metricTranslation = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var _props$metricTranslat;
+
+ if (!((_props$metricTranslat = props.metricTranslations) !== null && _props$metricTranslat !== void 0 && _props$metricTranslat[actualMetric.value])) {
+ return '';
+ }
+
+ return props.metricTranslations[actualMetric.value];
+ });
+ var metricDocumentation = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var _props$metricDocument;
+
+ if (!((_props$metricDocument = props.metricDocumentations) !== null && _props$metricDocument !== void 0 && _props$metricDocument[actualMetric.value])) {
+ return '';
+ }
+
+ return props.metricDocumentations[actualMetric.value];
+ });
+ var currentPeriod = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ if (external_CoreHome_["Matomo"].startDateString === external_CoreHome_["Matomo"].endDateString) {
+ return external_CoreHome_["Matomo"].endDateString;
+ }
+
+ return "".concat(external_CoreHome_["Matomo"].startDateString, ", ").concat(external_CoreHome_["Matomo"].endDateString);
+ });
+
+ function isIdGoalSet() {
+ return actualIdGoal.value || actualIdGoal.value === 0;
+ }
+
+ var sparklineParams = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var params = {
+ module: 'API',
+ action: 'get',
+ columns: actualMetric.value
+ };
+
+ if (isIdGoalSet()) {
+ params.idGoal = actualIdGoal.value;
+ params.module = 'Goals';
+ }
+
+ return params;
+ });
+ var pastPeriod = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ if (external_CoreHome_["Matomo"].period === 'range') {
+ return undefined;
+ }
+
+ return getPastPeriodStr();
+ });
+ var selectableColumns = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () {
+ var result = [];
+ Object.keys(props.metricTranslations).forEach(function (column) {
+ result.push({
+ column: column,
+ translation: props.metricTranslations[column]
+ });
+ });
+ Object.values(props.goals || {}).forEach(function (goal) {
+ props.goalMetrics.forEach(function (column) {
+ result.push({
+ column: "goal".concat(goal.idgoal, "_").concat(column),
+ translation: "".concat(goal.name, " - ").concat(props.metricTranslations[column])
+ });
+ });
+ });
+ return result;
+ });
+
+ function setWidgetTitle() {
+ var title = metricTranslation.value;
+
+ if (isIdGoalSet()) {
+ var _props$goals$actualId;
+
+ var goalName = ((_props$goals$actualId = props.goals[actualIdGoal.value]) === null || _props$goals$actualId === void 0 ? void 0 : _props$goals$actualId.name) || Object(external_CoreHome_["translate"])('General_Unknown');
+ title = "".concat(goalName, " - ").concat(title);
+ }
+
+ $(root.value).closest('div.widget').find('.widgetTop > .widgetName > span').text(title);
+ }
+
+ function getLastPeriodDate() {
+ var range = external_CoreHome_["Range"].getLastNRange(external_CoreHome_["Matomo"].period, 2, external_CoreHome_["Matomo"].currentDateString);
+ return Object(external_CoreHome_["format"])(range.startDate);
+ }
+
+ function fetchData() {
+ isLoading.value = true;
+ var promises = [];
+ var apiModule = 'API';
+ var apiAction = 'get';
+ var extraParams = {};
+
+ if (isIdGoalSet()) {
+ // the conversion rate added by the AddColumnsProcessedMetrics filter conflicts w/
+ // the goals one, so don't run it
+ extraParams.idGoal = actualIdGoal.value;
+ extraParams.filter_add_columns_when_show_all_columns = 0;
+ apiModule = 'Goals';
+ apiAction = 'get';
+ }
+
+ var method = "".concat(apiModule, ".").concat(apiAction); // first request for formatted data
+
+ promises.push(external_CoreHome_["AjaxHelper"].fetch(Object.assign({
+ method: method,
+ format_metrics: 'all'
+ }, extraParams)));
+
+ if (external_CoreHome_["Matomo"].period !== 'range') {
+ // second request for unformatted data so we can calculate evolution
+ promises.push(external_CoreHome_["AjaxHelper"].fetch(Object.assign({
+ method: method,
+ format_metrics: '0'
+ }, extraParams))); // third request for past data (unformatted)
+
+ promises.push(external_CoreHome_["AjaxHelper"].fetch(Object.assign({
+ method: method,
+ date: getLastPeriodDate(),
+ format_metrics: '0'
+ }, extraParams))); // fourth request for past data (formatted for tooltip display)
+
+ promises.push(external_CoreHome_["AjaxHelper"].fetch(Object.assign({
+ method: method,
+ date: getLastPeriodDate(),
+ format_metrics: 'all'
+ }, extraParams)));
+ }
+
+ return Promise.all(promises).then(function (r) {
+ responses.value = r;
+ isLoading.value = false;
+ });
+ }
+
+ function onMetricChanged(newMetric) {
+ actualMetric.value = newMetric;
+ fetchData().then(setWidgetTitle); // notify widget of parameter change so it is replaced
+
+ $(root.value).closest('[widgetId]').trigger('setParameters', {
+ column: actualMetric.value,
+ idGoal: actualIdGoal.value
+ });
+ }
+
+ function setMetric(newColumn) {
+ var idGoal = undefined;
+ var actualColumn = newColumn;
+ var m = newColumn.match(/^goal([0-9]+)_(.*)/);
+
+ if (m) {
+ idGoal = +m[1];
+
+ var _m = _slicedToArray(m, 3);
+
+ actualColumn = _m[2];
+ }
+
+ if (actualMetric.value !== actualColumn || idGoal !== actualIdGoal.value) {
+ actualMetric.value = actualColumn;
+ actualIdGoal.value = idGoal;
+ onMetricChanged(actualColumn);
+ }
+ }
+
+ function createSeriesPicker() {
+ var element = $(root.value);
+ var $widgetName = element.closest('div.widget').find('.widgetTop > .widgetName');
+ var $seriesPickerElem = $('<div class="single-metric-view-picker"><div></div></div>');
+ var app = Object(external_CoreHome_["createVueApp"])({
+ render: function render() {
+ return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(SeriesPicker, {
+ multiselect: false,
+ selectableColumns: selectableColumns.value,
+ selectableRows: [],
+ selectedColumns: selectedColumns.value,
+ selectedRows: [],
+ onSelect: function onSelect(_ref) {
+ var columns = _ref.columns;
+ setMetric(columns[0]);
+ }
+ });
+ }
+ });
+ $widgetName.append($seriesPickerElem);
+ app.mount($seriesPickerElem.children()[0]);
+ return app;
+ }
+
+ var seriesPickerApp;
+ Object(external_commonjs_vue_commonjs2_vue_root_Vue_["onMounted"])(function () {
+ seriesPickerApp = createSeriesPicker();
+ });
+ Object(external_commonjs_vue_commonjs2_vue_root_Vue_["onBeforeUnmount"])(function () {
+ $(root.value).closest('.widgetContent').off('widget:destroy').off('widget:reload');
+ $(root.value).closest('div.widget').find('.single-metric-view-picker').remove();
+ seriesPickerApp.unmount();
+ });
+ Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () {
+ return props.metric;
+ }, function () {
+ onMetricChanged(props.metric);
+ });
+ onMetricChanged(props.metric);
+ return {
+ root: root,
+ metricValue: metricValue,
+ isLoading: isLoading,
+ selectedColumns: selectedColumns,
+ responses: responses,
+ metricValueUnformatted: metricValueUnformatted,
+ pastValueUnformatted: pastValueUnformatted,
+ metricChangePercent: metricChangePercent,
+ pastValue: pastValue,
+ metricTranslation: metricTranslation,
+ metricDocumentation: metricDocumentation,
+ sparklineParams: sparklineParams,
+ pastPeriod: pastPeriod,
+ selectableColumns: selectableColumns,
+ currentPeriod: currentPeriod
+ };
+ }
+}));
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue?vue&type=script&lang=ts
+
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue
+
+
+
+SingleMetricViewvue_type_script_lang_ts.render = SingleMetricViewvue_type_template_id_2e2e889f_render
+
+/* harmony default export */ var SingleMetricView = (SingleMetricViewvue_type_script_lang_ts);
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.adapter.ts
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+
+/* harmony default export */ var SingleMetricView_adapter = (Object(external_CoreHome_["createAngularJsAdapter"])({
+ component: SingleMetricView,
+ scope: {
+ metric: {
+ angularJsBind: '<'
+ },
+ idGoal: {
+ angularJsBind: '<'
+ },
+ metricTranslations: {
+ angularJsBind: '<'
+ },
+ metricDocumentations: {
+ angularJsBind: '<'
+ },
+ goals: {
+ angularJsBind: '<'
+ },
+ goalMetrics: {
+ angularJsBind: '<'
+ }
+ },
+ directiveName: 'piwikSingleMetricView',
+ restrict: 'E',
+ postCreate: function postCreate(vm, scope, element) {
+ element.closest('.widgetContent').on('widget:destroy', function () {
+ scope.$parent.$destroy();
+ }).on('widget:reload', function () {
+ scope.$parent.$destroy();
+ });
+ }
+}));
+// CONCATENATED MODULE: ./plugins/CoreVisualizations/vue/src/index.ts
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+
+
+
+// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib-no-default.js
+
+
+
+
+/***/ })
+
+/******/ });
+});
+//# sourceMappingURL=CoreVisualizations.umd.js.map \ No newline at end of file
diff --git a/plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.min.js b/plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.min.js
new file mode 100644
index 0000000000..f13b9f8384
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/dist/CoreVisualizations.umd.min.js
@@ -0,0 +1,14 @@
+(function(e,t){"object"===typeof exports&&"object"===typeof module?module.exports=t(require("CoreHome"),require("vue")):"function"===typeof define&&define.amd?define(["CoreHome"],t):"object"===typeof exports?exports["CoreVisualizations"]=t(require("CoreHome"),require("vue")):e["CoreVisualizations"]=t(e["CoreHome"],e["Vue"])})("undefined"!==typeof self?self:this,(function(e,t){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="plugins/CoreVisualizations/vue/dist/",n(n.s="fae3")}({"19dc":function(t,n){t.exports=e},"8bbf":function(e,n){e.exports=t},fae3:function(e,t,n){"use strict";if(n.r(t),n.d(t,"SeriesPicker",(function(){return h})),n.d(t,"SingleMetricView",(function(){return T})),"undefined"!==typeof window){var r=window.document.currentScript,o=r&&r.src.match(/(.+\/)[^/]+\.js(\?.*)?$/);o&&(n.p=o[1])}var c=n("19dc"),a=n("8bbf"),l={key:0,class:"jqplot-seriespicker-popover"},i={class:"headline"},u=["onClick"],s=["type","checked"],d={key:0,class:"headline recordsToPlot"},m=["onClick"],p=["type","checked"];function f(e,t,n,r,o,c){return Object(a["openBlock"])(),Object(a["createElementBlock"])("div",{class:Object(a["normalizeClass"])(["jqplot-seriespicker",{open:e.isPopupVisible}]),onMouseenter:t[1]||(t[1]=function(t){return e.isPopupVisible=!0}),onMouseleave:t[2]||(t[2]=function(t){return e.onLeavePopup()})},[Object(a["createElementVNode"])("a",{href:"#",onClick:t[0]||(t[0]=Object(a["withModifiers"])((function(){}),["prevent","stop"]))}," + "),e.isPopupVisible?(Object(a["openBlock"])(),Object(a["createElementBlock"])("div",l,[Object(a["createElementVNode"])("p",i,Object(a["toDisplayString"])(e.translate(e.multiselect?"General_MetricsToPlot":"General_MetricToPlot")),1),(Object(a["openBlock"])(!0),Object(a["createElementBlock"])(a["Fragment"],null,Object(a["renderList"])(e.selectableColumns,(function(t){return Object(a["openBlock"])(),Object(a["createElementBlock"])("p",{class:"pickColumn",onClick:function(n){return e.optionSelected(t.column,e.columnStates)},key:t.column},[Object(a["createElementVNode"])("label",null,[Object(a["createElementVNode"])("input",{class:"select",type:e.multiselect?"checkbox":"radio",checked:!!e.columnStates[t.column]},null,8,s),Object(a["createElementVNode"])("span",null,Object(a["toDisplayString"])(t.translation),1)])],8,u)})),128)),e.selectableRows.length?(Object(a["openBlock"])(),Object(a["createElementBlock"])("p",d,Object(a["toDisplayString"])(e.translate("General_RecordsToPlot")),1)):Object(a["createCommentVNode"])("",!0),(Object(a["openBlock"])(!0),Object(a["createElementBlock"])(a["Fragment"],null,Object(a["renderList"])(e.selectableRows,(function(t){return Object(a["openBlock"])(),Object(a["createElementBlock"])("p",{class:"pickRow",onClick:function(n){return e.optionSelected(t.matcher,e.rowStates)},key:t.matcher},[Object(a["createElementVNode"])("label",null,[Object(a["createElementVNode"])("input",{class:"select",type:e.multiselect?"checkbox":"radio",checked:!!e.rowStates[t.matcher]},null,8,p),Object(a["createElementVNode"])("span",null,Object(a["toDisplayString"])(t.label),1)])],8,m)})),128))])):Object(a["createCommentVNode"])("",!0)],34)}function b(e,t){var n={};return e.forEach((function(e){var t=e.column||e.matcher;n[t]=!1})),t.forEach((function(e){n[e]=!0})),n}function v(e,t){return e.length===t.length&&0===e.filter((function(e){return-1===t.indexOf(e)})).length}function g(e){Object.keys(e).forEach((function(t){e[t]=!1}))}function j(e){return Object.keys(e).filter((function(t){return!!e[t]}))}var O=Object(a["defineComponent"])({props:{multiselect:Boolean,selectableColumns:{type:Array,default:function(){return[]}},selectableRows:{type:Array,default:function(){return[]}},selectedColumns:{type:Array,default:function(){return[]}},selectedRows:{type:Array,default:function(){return[]}}},data:function(){return{isPopupVisible:!1,columnStates:b(this.selectableColumns,this.selectedColumns),rowStates:b(this.selectableRows,this.selectedRows)}},emits:["select"],created:function(){this.optionSelected=Object(c["debounce"])(this.optionSelected,0)},methods:{optionSelected:function(e,t){this.multiselect||(g(this.columnStates),g(this.rowStates)),t[e]=!t[e],this.triggerOnSelectAndClose()},onLeavePopup:function(){this.isPopupVisible=!1,this.optionsChanged()&&this.triggerOnSelectAndClose()},triggerOnSelectAndClose:function(){this.isPopupVisible=!1,this.$emit("select",{columns:j(this.columnStates),rows:j(this.rowStates)})},optionsChanged:function(){return!v(j(this.columnStates),this.selectedColumns)||!v(j(this.rowStates),this.selectedRows)}}});O.render=f;var h=O,y=(Object(c["createAngularJsAdapter"])({component:h,scope:{multiselect:{angularJsBind:"<"},selectableColumns:{angularJsBind:"<"},selectableRows:{angularJsBind:"<"},selectedColumns:{angularJsBind:"<"},selectedRows:{angularJsBind:"<"},onSelect:{angularJsBind:"&",vue:"select"}},directiveName:"piwikSeriesPicker",restrict:"E"}),{class:"metric-sparkline"}),w={class:"metric-value"},S=["title"],k=["title"];
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */function C(e,t,n,r,o,c){var l=Object(a["resolveComponent"])("Sparkline");return Object(a["openBlock"])(),Object(a["createElementBlock"])("div",{class:Object(a["normalizeClass"])(["singleMetricView",{loading:e.isLoading}]),ref:"root"},[Object(a["createElementVNode"])("div",y,[Object(a["createVNode"])(l,{params:e.sparklineParams},null,8,["params"])]),Object(a["createElementVNode"])("div",w,[Object(a["createElementVNode"])("span",{title:e.metricDocumentation},[Object(a["createElementVNode"])("strong",null,Object(a["toDisplayString"])(e.metricValue),1),Object(a["createTextVNode"])(" "+Object(a["toDisplayString"])((e.metricTranslation||"").toLowerCase()),1)],8,S),null!==e.pastValue?(Object(a["openBlock"])(),Object(a["createElementBlock"])("span",{key:0,class:"metricEvolution",title:e.translate("General_EvolutionSummaryGeneric",e.metricValue,e.currentPeriod,e.pastValue,e.pastPeriod,e.metricChangePercent)},[Object(a["createElementVNode"])("span",{class:Object(a["normalizeClass"])({"positive-evolution":e.metricValueUnformatted>e.pastValueUnformatted,"negative-evolution":e.metricValueUnformatted<e.pastValueUnformatted})},Object(a["toDisplayString"])(e.metricChangePercent),3)],8,k)):Object(a["createCommentVNode"])("",!0)])],2)}function V(e,t){return N(e)||M(e,t)||P(e,t)||E()}function E(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function P(e,t){if(e){if("string"===typeof e)return B(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?B(e,t):void 0}}function B(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function M(e,t){var n=null==e?null:"undefined"!==typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,c=[],a=!0,l=!1;try{for(n=n.call(e);!(a=(r=n.next()).done);a=!0)if(c.push(r.value),t&&c.length===t)break}catch(i){l=!0,o=i}finally{try{a||null==n["return"]||n["return"]()}finally{if(l)throw o}}return c}}function N(e){if(Array.isArray(e))return e}function A(){var e=c["Range"].getLastNRange(c["Matomo"].period,2,c["Matomo"].currentDateString),t=e.startDate,n=c["Periods"].get(c["Matomo"].period).parse(t).getDateRange();return"".concat(Object(c["format"])(n[0]),",").concat(Object(c["format"])(n[1]))}var _=window,D=_.$,x=Object(a["defineComponent"])({props:{metric:{type:String,required:!0},idGoal:[String,Number],metricTranslations:{type:Object,required:!0},metricDocumentations:Object,goals:{type:Object,required:!0},goalMetrics:Array},components:{Sparkline:c["Sparkline"]},setup:function(e){var t=Object(a["ref"])(null),n=Object(a["ref"])(!1),r=Object(a["ref"])(null),o=Object(a["ref"])(e.metric),l=Object(a["ref"])(e.idGoal),i=Object(a["computed"])((function(){return[l.value?"goal".concat(l.value,"_").concat(o.value):o.value]})),u=Object(a["computed"])((function(){var e;return null!==(e=r.value)&&void 0!==e&&e[1]?r.value[1][o.value]:null})),s=Object(a["computed"])((function(){var e;return null!==(e=r.value)&&void 0!==e&&e[2]?r.value[2][o.value]||0:null})),d=Object(a["computed"])((function(){if(!u.value)return null;var e="string"===typeof u.value?parseInt(u.value,10):u.value,t="string"===typeof s.value?parseInt(s.value,10):s.value,n=c["Matomo"].helper.calculateEvolution(e,t);return"".concat((100*n).toFixed(2)," %")})),m=Object(a["computed"])((function(){var e;if(null===(e=r.value)||void 0===e||!e[3])return null;var t=r.value[3];return t[o.value]||0})),p=Object(a["computed"])((function(){var e;if(null===(e=r.value)||void 0===e||!e[0])return null;var t=r.value[0];return t[o.value]||0})),f=Object(a["computed"])((function(){var t;return null!==(t=e.metricTranslations)&&void 0!==t&&t[o.value]?e.metricTranslations[o.value]:""})),b=Object(a["computed"])((function(){var t;return null!==(t=e.metricDocumentations)&&void 0!==t&&t[o.value]?e.metricDocumentations[o.value]:""})),v=Object(a["computed"])((function(){return c["Matomo"].startDateString===c["Matomo"].endDateString?c["Matomo"].endDateString:"".concat(c["Matomo"].startDateString,", ").concat(c["Matomo"].endDateString)}));function g(){return l.value||0===l.value}var j,O=Object(a["computed"])((function(){var e={module:"API",action:"get",columns:o.value};return g()&&(e.idGoal=l.value,e.module="Goals"),e})),y=Object(a["computed"])((function(){if("range"!==c["Matomo"].period)return A()})),w=Object(a["computed"])((function(){var t=[];return Object.keys(e.metricTranslations).forEach((function(n){t.push({column:n,translation:e.metricTranslations[n]})})),Object.values(e.goals||{}).forEach((function(n){e.goalMetrics.forEach((function(r){t.push({column:"goal".concat(n.idgoal,"_").concat(r),translation:"".concat(n.name," - ").concat(e.metricTranslations[r])})}))})),t}));function S(){var n=f.value;if(g()){var r,o=(null===(r=e.goals[l.value])||void 0===r?void 0:r.name)||Object(c["translate"])("General_Unknown");n="".concat(o," - ").concat(n)}D(t.value).closest("div.widget").find(".widgetTop > .widgetName > span").text(n)}function k(){var e=c["Range"].getLastNRange(c["Matomo"].period,2,c["Matomo"].currentDateString);return Object(c["format"])(e.startDate)}function C(){n.value=!0;var e=[],t="API",o="get",a={};g()&&(a.idGoal=l.value,a.filter_add_columns_when_show_all_columns=0,t="Goals",o="get");var i="".concat(t,".").concat(o);return e.push(c["AjaxHelper"].fetch(Object.assign({method:i,format_metrics:"all"},a))),"range"!==c["Matomo"].period&&(e.push(c["AjaxHelper"].fetch(Object.assign({method:i,format_metrics:"0"},a))),e.push(c["AjaxHelper"].fetch(Object.assign({method:i,date:k(),format_metrics:"0"},a))),e.push(c["AjaxHelper"].fetch(Object.assign({method:i,date:k(),format_metrics:"all"},a)))),Promise.all(e).then((function(e){r.value=e,n.value=!1}))}function E(e){o.value=e,C().then(S),D(t.value).closest("[widgetId]").trigger("setParameters",{column:o.value,idGoal:l.value})}function P(e){var t=void 0,n=e,r=e.match(/^goal([0-9]+)_(.*)/);if(r){t=+r[1];var c=V(r,3);n=c[2]}o.value===n&&t===l.value||(o.value=n,l.value=t,E(n))}function B(){var e=D(t.value),n=e.closest("div.widget").find(".widgetTop > .widgetName"),r=D('<div class="single-metric-view-picker"><div></div></div>'),o=Object(c["createVueApp"])({render:function(){return Object(a["createVNode"])(h,{multiselect:!1,selectableColumns:w.value,selectableRows:[],selectedColumns:i.value,selectedRows:[],onSelect:function(e){var t=e.columns;P(t[0])}})}});return n.append(r),o.mount(r.children()[0]),o}return Object(a["onMounted"])((function(){j=B()})),Object(a["onBeforeUnmount"])((function(){D(t.value).closest(".widgetContent").off("widget:destroy").off("widget:reload"),D(t.value).closest("div.widget").find(".single-metric-view-picker").remove(),j.unmount()})),Object(a["watch"])((function(){return e.metric}),(function(){E(e.metric)})),E(e.metric),{root:t,metricValue:p,isLoading:n,selectedColumns:i,responses:r,metricValueUnformatted:u,pastValueUnformatted:s,metricChangePercent:d,pastValue:m,metricTranslation:f,metricDocumentation:b,sparklineParams:O,pastPeriod:y,selectableColumns:w,currentPeriod:v}}});x.render=C;var T=x;
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */Object(c["createAngularJsAdapter"])({component:T,scope:{metric:{angularJsBind:"<"},idGoal:{angularJsBind:"<"},metricTranslations:{angularJsBind:"<"},metricDocumentations:{angularJsBind:"<"},goals:{angularJsBind:"<"},goalMetrics:{angularJsBind:"<"}},directiveName:"piwikSingleMetricView",restrict:"E",postCreate:function(e,t,n){n.closest(".widgetContent").on("widget:destroy",(function(){t.$parent.$destroy()})).on("widget:reload",(function(){t.$parent.$destroy()}))}})}})}));
+//# sourceMappingURL=CoreVisualizations.umd.min.js.map \ No newline at end of file
diff --git a/plugins/CoreVisualizations/vue/dist/umd.metadata.json b/plugins/CoreVisualizations/vue/dist/umd.metadata.json
new file mode 100644
index 0000000000..9ecfcc0456
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/dist/umd.metadata.json
@@ -0,0 +1,5 @@
+{
+ "dependsOn": [
+ "CoreHome"
+ ]
+} \ No newline at end of file
diff --git a/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.adapter.ts b/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.adapter.ts
new file mode 100644
index 0000000000..31a253be25
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.adapter.ts
@@ -0,0 +1,36 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+import { createAngularJsAdapter } from 'CoreHome';
+import SeriesPicker from './SeriesPicker.vue';
+
+export default createAngularJsAdapter({
+ component: SeriesPicker,
+ scope: {
+ multiselect: {
+ angularJsBind: '<',
+ },
+ selectableColumns: {
+ angularJsBind: '<',
+ },
+ selectableRows: {
+ angularJsBind: '<',
+ },
+ selectedColumns: {
+ angularJsBind: '<',
+ },
+ selectedRows: {
+ angularJsBind: '<',
+ },
+ onSelect: {
+ angularJsBind: '&',
+ vue: 'select',
+ },
+ },
+ directiveName: 'piwikSeriesPicker',
+ restrict: 'E',
+});
diff --git a/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.less b/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.less
new file mode 100644
index 0000000000..656c602ce7
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.less
@@ -0,0 +1,30 @@
+piwik-series-picker {
+ display: inline-block;
+}
+
+.jqplot-seriespicker {
+ display: inline-block;
+
+ &:not(.open) {
+ opacity: .55;
+ }
+
+ &.open { // while open, make sure we're above other series picker icons
+ z-index: 1000;
+ }
+
+ > a {
+ display: inline-block;
+ opacity: 0;
+ position: absolute;
+ }
+
+ position: relative;
+}
+
+.jqplot-seriespicker-popover {
+ position: absolute;
+
+ top: -3px;
+ left: -4px;
+}
diff --git a/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue b/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue
new file mode 100644
index 0000000000..4c4e433a85
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/src/SeriesPicker/SeriesPicker.vue
@@ -0,0 +1,192 @@
+<!--
+ Matomo - free/libre analytics platform
+ @link https://matomo.org
+ @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+-->
+
+<template>
+ <div
+ class="jqplot-seriespicker"
+ @mouseenter="isPopupVisible = true"
+ @mouseleave="onLeavePopup()"
+ :class="{open: isPopupVisible}"
+ >
+ <a
+ href="#"
+ @click.prevent.stop
+ >
+ +
+ </a>
+ <div
+ class="jqplot-seriespicker-popover"
+ v-if="isPopupVisible"
+ >
+ <p class="headline">
+ {{ translate(multiselect ? 'General_MetricsToPlot' : 'General_MetricToPlot') }}</p>
+ <p
+ class="pickColumn"
+ @click="optionSelected(columnConfig.column, columnStates)"
+ v-for="columnConfig in selectableColumns"
+ :key="columnConfig.column"
+ >
+ <label>
+ <input
+ class="select"
+ :type="multiselect ? 'checkbox' : 'radio'"
+ :checked="!!columnStates[columnConfig.column]"
+ />
+ <span>{{ columnConfig.translation }}</span>
+ </label>
+ </p>
+ <p
+ class="headline recordsToPlot"
+ v-if="selectableRows.length"
+ >
+ {{ translate('General_RecordsToPlot') }}
+ </p>
+ <p
+ class="pickRow"
+ @click="optionSelected(rowConfig.matcher, rowStates)"
+ v-for="rowConfig in selectableRows"
+ :key="rowConfig.matcher"
+ >
+ <label>
+ <input
+ class="select"
+ :type="multiselect ? 'checkbox' : 'radio'"
+ :checked="!!rowStates[rowConfig.matcher]"
+ />
+ <span>{{ rowConfig.label }}</span>
+ </label>
+ </p>
+ </div>
+ </div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { debounce } from 'CoreHome';
+
+interface SelectableColumnInfo {
+ column: string;
+ translation: string;
+}
+
+interface SelectableRowInfo {
+ matcher: string;
+ label: string;
+}
+
+interface SeriesPickerState {
+ isPopupVisible: boolean;
+ columnStates: Record<string, boolean>;
+ rowStates: Record<string, boolean>;
+}
+
+function getInitialOptionStates(
+ allOptions: (SelectableColumnInfo | SelectableRowInfo)[],
+ selectedOptions: string[],
+) {
+ const states: Record<string, boolean> = {};
+ allOptions.forEach((columnConfig) => {
+ const name = (columnConfig as SelectableColumnInfo).column
+ || (columnConfig as SelectableRowInfo).matcher;
+ states[name] = false;
+ });
+ selectedOptions.forEach((column) => {
+ states[column] = true;
+ });
+ return states;
+}
+
+function arrayEqual<T>(lhs: T[], rhs: T[]) {
+ if (lhs.length !== rhs.length) {
+ return false;
+ }
+
+ return lhs.filter((element) => rhs.indexOf(element) === -1).length === 0;
+}
+
+function unselectOptions(optionStates: Record<string, boolean>) {
+ Object.keys(optionStates).forEach((optionName) => {
+ optionStates[optionName] = false;
+ });
+}
+
+function getSelected(optionStates: Record<string, boolean>) {
+ return Object.keys(optionStates).filter((optionName) => !!optionStates[optionName]);
+}
+
+export default defineComponent({
+ props: {
+ multiselect: Boolean,
+ selectableColumns: {
+ type: Array,
+ default: () => [],
+ },
+ selectableRows: {
+ type: Array,
+ default: () => [],
+ },
+ selectedColumns: {
+ type: Array,
+ default: () => [],
+ },
+ selectedRows: {
+ type: Array,
+ default: () => [],
+ },
+ },
+ data(): SeriesPickerState {
+ return {
+ isPopupVisible: false,
+ columnStates: getInitialOptionStates(
+ this.selectableColumns as SelectableColumnInfo[],
+ this.selectedColumns as string[],
+ ),
+ rowStates: getInitialOptionStates(
+ this.selectableRows as SelectableRowInfo[],
+ this.selectedRows as string[],
+ ),
+ };
+ },
+ emits: ['select'],
+ created() {
+ this.optionSelected = debounce(this.optionSelected, 0);
+ },
+ methods: {
+ optionSelected(optionValue: string, optionStates: Record<string, boolean>) {
+ if (!this.multiselect) {
+ unselectOptions(this.columnStates);
+ unselectOptions(this.rowStates);
+ }
+
+ optionStates[optionValue] = !optionStates[optionValue];
+ this.triggerOnSelectAndClose();
+ },
+ onLeavePopup() {
+ this.isPopupVisible = false;
+
+ if (this.optionsChanged()) {
+ this.triggerOnSelectAndClose();
+ }
+ },
+ triggerOnSelectAndClose() {
+ this.isPopupVisible = false;
+ this.$emit('select', {
+ columns: getSelected(this.columnStates),
+ rows: getSelected(this.rowStates),
+ });
+ },
+ optionsChanged() {
+ return !arrayEqual(
+ getSelected(this.columnStates),
+ this.selectedColumns,
+ ) || !arrayEqual(
+ getSelected(this.rowStates),
+ this.selectedRows,
+ );
+ },
+ },
+});
+</script>
diff --git a/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.adapter.ts b/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.adapter.ts
new file mode 100644
index 0000000000..7091b9bb66
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.adapter.ts
@@ -0,0 +1,42 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+import { createAngularJsAdapter } from 'CoreHome';
+import SingleMetricView from './SingleMetricView.vue';
+
+export default createAngularJsAdapter({
+ component: SingleMetricView,
+ scope: {
+ metric: {
+ angularJsBind: '<',
+ },
+ idGoal: {
+ angularJsBind: '<',
+ },
+ metricTranslations: {
+ angularJsBind: '<',
+ },
+ metricDocumentations: {
+ angularJsBind: '<',
+ },
+ goals: {
+ angularJsBind: '<',
+ },
+ goalMetrics: {
+ angularJsBind: '<',
+ },
+ },
+ directiveName: 'piwikSingleMetricView',
+ restrict: 'E',
+ postCreate(vm, scope, element) {
+ element.closest('.widgetContent').on('widget:destroy', () => {
+ scope.$parent.$destroy();
+ }).on('widget:reload', () => {
+ scope.$parent.$destroy();
+ });
+ },
+});
diff --git a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.less b/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.less
index f091b530e2..49d72536f2 100644
--- a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.less
+++ b/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.less
@@ -6,6 +6,10 @@
opacity: 0.5;
}
+ span, .metric-sparkline, .positive-evolution::before {
+ margin-right: 3.5px;
+ }
+
.metric-value {
display: inline-block;
font-size: 14px;
@@ -58,4 +62,5 @@
.single-metric-view-picker {
margin-left: 6px;
+ display: inline-block;
} \ No newline at end of file
diff --git a/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue b/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue
new file mode 100644
index 0000000000..a07c584673
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/src/SingleMetricView/SingleMetricView.vue
@@ -0,0 +1,410 @@
+<!--
+ Matomo - free/libre analytics platform
+ @link https://matomo.org
+ @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+-->
+
+<template>
+ <div
+ class="singleMetricView"
+ :class="{'loading': isLoading}"
+ ref="root"
+ >
+ <div class="metric-sparkline">
+ <Sparkline :params="sparklineParams">
+ </Sparkline>
+ </div>
+ <div class="metric-value">
+ <span :title="metricDocumentation">
+ <strong>{{ metricValue }}</strong> {{ (metricTranslation || '').toLowerCase() }}
+ </span>
+ <span
+ class="metricEvolution"
+ v-if="pastValue !== null"
+ :title="translate(
+ 'General_EvolutionSummaryGeneric', metricValue, currentPeriod, pastValue,
+ pastPeriod, metricChangePercent)"
+ >
+ <span
+ :class="{
+ 'positive-evolution': metricValueUnformatted > pastValueUnformatted,
+ 'negative-evolution': metricValueUnformatted < pastValueUnformatted,
+ }"
+ >
+ {{ metricChangePercent }}
+ </span>
+ </span>
+ </div>
+ </div>
+</template>
+
+<script lang="ts">
+import {
+ computed,
+ createVNode,
+ defineComponent,
+ onBeforeUnmount,
+ onMounted,
+ ref,
+ watch,
+} from 'vue';
+import {
+ Matomo,
+ AjaxHelper,
+ Sparkline,
+ Range,
+ Periods,
+ format,
+ createVueApp, translate,
+} from 'CoreHome';
+import SeriesPicker from '../SeriesPicker/SeriesPicker.vue';
+
+interface SelectableColumnInfo {
+ column: string;
+ translation: string;
+}
+
+type MetricValues = Record<string, number|string>;
+
+interface Goal {
+ idgoal: string|number;
+ name: string;
+}
+
+function getPastPeriodStr(): string {
+ const { startDate } = Range.getLastNRange(Matomo.period!, 2, Matomo.currentDateString!);
+ const dateRange = Periods.get(Matomo.period!).parse(startDate).getDateRange();
+ return `${format(dateRange[0])},${format(dateRange[1])}`;
+}
+
+const { $ } = window;
+
+export default defineComponent({
+ props: {
+ metric: {
+ type: String,
+ required: true,
+ },
+ idGoal: [String, Number],
+ metricTranslations: {
+ type: Object,
+ required: true,
+ },
+ metricDocumentations: Object,
+ goals: {
+ type: Object,
+ required: true,
+ },
+ goalMetrics: Array,
+ },
+ components: {
+ Sparkline,
+ },
+ setup(props) {
+ const root = ref<HTMLElement|null>(null);
+
+ const isLoading = ref<boolean>(false);
+ const responses = ref<null|MetricValues[]>(null);
+ const actualMetric = ref<string>(props.metric);
+ const actualIdGoal = ref<string|number|undefined>(props.idGoal);
+
+ const selectedColumns = computed(() => [
+ actualIdGoal.value ? `goal${actualIdGoal.value}_${actualMetric.value}` : actualMetric.value,
+ ]);
+
+ const metricValueUnformatted = computed(() => {
+ if (!responses.value?.[1]) {
+ return null;
+ }
+
+ return responses.value[1][actualMetric.value];
+ });
+
+ const pastValueUnformatted = computed(() => {
+ if (!responses.value?.[2]) {
+ return null;
+ }
+
+ return responses.value[2][actualMetric.value] || 0;
+ });
+
+ const metricChangePercent = computed(() => {
+ if (!metricValueUnformatted.value) {
+ return null;
+ }
+
+ const currentValue: number = typeof metricValueUnformatted.value === 'string'
+ ? parseInt(metricValueUnformatted.value, 10)
+ : metricValueUnformatted.value as number;
+
+ const pastValue: number = typeof pastValueUnformatted.value === 'string'
+ ? parseInt(pastValueUnformatted.value, 10)
+ : pastValueUnformatted.value as number;
+
+ const evolution = Matomo.helper.calculateEvolution(currentValue, pastValue);
+
+ return `${(evolution * 100).toFixed(2)} %`;
+ });
+
+ const pastValue = computed(() => {
+ if (!responses.value?.[3]) {
+ return null;
+ }
+
+ const pastDataFormatted = responses.value[3];
+ return pastDataFormatted[actualMetric.value] || 0;
+ });
+
+ const metricValue = computed(() => {
+ if (!responses.value?.[0]) {
+ return null;
+ }
+
+ const currentData = responses.value[0];
+ return currentData[actualMetric.value] || 0;
+ });
+
+ const metricTranslation = computed(() => {
+ if (!props.metricTranslations?.[actualMetric.value]) {
+ return '';
+ }
+
+ return props.metricTranslations[actualMetric.value];
+ });
+
+ const metricDocumentation = computed(() => {
+ if (!props.metricDocumentations?.[actualMetric.value]) {
+ return '';
+ }
+
+ return props.metricDocumentations[actualMetric.value];
+ });
+
+ const currentPeriod = computed(() => {
+ if (Matomo.startDateString === Matomo.endDateString) {
+ return Matomo.endDateString;
+ }
+ return `${Matomo.startDateString}, ${Matomo.endDateString}`;
+ });
+
+ function isIdGoalSet() {
+ return actualIdGoal.value || actualIdGoal.value === 0;
+ }
+
+ const sparklineParams = computed<QueryParameters>(() => {
+ const params: QueryParameters = {
+ module: 'API',
+ action: 'get',
+ columns: actualMetric.value,
+ };
+
+ if (isIdGoalSet()) {
+ params.idGoal = actualIdGoal.value;
+ params.module = 'Goals';
+ }
+
+ return params;
+ });
+
+ const pastPeriod = computed(() => {
+ if (Matomo.period === 'range') {
+ return undefined;
+ }
+
+ return getPastPeriodStr();
+ });
+
+ const selectableColumns = computed(() => {
+ const result: SelectableColumnInfo[] = [];
+
+ Object.keys(props.metricTranslations).forEach((column) => {
+ result.push({
+ column,
+ translation: props.metricTranslations[column],
+ });
+ });
+
+ Object.values((props.goals || {}) as Record<string, Goal>).forEach((goal) => {
+ (props.goalMetrics as string[]).forEach((column) => {
+ result.push({
+ column: `goal${goal.idgoal}_${column}`,
+ translation: `${goal.name} - ${props.metricTranslations[column]}`,
+ });
+ });
+ });
+
+ return result;
+ });
+
+ function setWidgetTitle() {
+ let title = metricTranslation.value;
+
+ if (isIdGoalSet()) {
+ const goalName = props.goals[actualIdGoal.value!]?.name || translate('General_Unknown');
+ title = `${goalName} - ${title}`;
+ }
+
+ $(root.value as HTMLElement)
+ .closest('div.widget')
+ .find('.widgetTop > .widgetName > span')
+ .text(title);
+ }
+
+ function getLastPeriodDate(): string {
+ const range = Range.getLastNRange(Matomo.period!, 2, Matomo.currentDateString!);
+ return format(range.startDate);
+ }
+
+ function fetchData() {
+ isLoading.value = true;
+
+ const promises = [];
+ let apiModule = 'API';
+ let apiAction = 'get';
+ const extraParams: QueryParameters = {};
+
+ if (isIdGoalSet()) {
+ // the conversion rate added by the AddColumnsProcessedMetrics filter conflicts w/
+ // the goals one, so don't run it
+ extraParams.idGoal = actualIdGoal.value;
+
+ extraParams.filter_add_columns_when_show_all_columns = 0;
+ apiModule = 'Goals';
+ apiAction = 'get';
+ }
+
+ const method = `${apiModule}.${apiAction}`;
+
+ // first request for formatted data
+ promises.push(AjaxHelper.fetch({
+ method,
+ format_metrics: 'all',
+ ...extraParams,
+ }));
+
+ if (Matomo.period !== 'range') {
+ // second request for unformatted data so we can calculate evolution
+ promises.push(AjaxHelper.fetch({
+ method,
+ format_metrics: '0',
+ ...extraParams,
+ }));
+
+ // third request for past data (unformatted)
+ promises.push(AjaxHelper.fetch({
+ method,
+ date: getLastPeriodDate(),
+ format_metrics: '0',
+ ...extraParams,
+ }));
+
+ // fourth request for past data (formatted for tooltip display)
+ promises.push(AjaxHelper.fetch({
+ method,
+ date: getLastPeriodDate(),
+ format_metrics: 'all',
+ ...extraParams,
+ }));
+ }
+
+ return Promise.all(promises).then((r) => {
+ responses.value = r;
+ isLoading.value = false;
+ });
+ }
+
+ function onMetricChanged(newMetric: string) {
+ actualMetric.value = newMetric;
+
+ fetchData().then(setWidgetTitle); // notify widget of parameter change so it is replaced
+
+ $(root.value as HTMLElement).closest('[widgetId]').trigger('setParameters', {
+ column: actualMetric.value,
+ idGoal: actualIdGoal.value,
+ });
+ }
+
+ function setMetric(newColumn: string) {
+ let idGoal: number|undefined = undefined;
+ let actualColumn: string = newColumn;
+
+ const m = newColumn.match(/^goal([0-9]+)_(.*)/);
+ if (m) {
+ idGoal = +m[1];
+ [, , actualColumn] = m;
+ }
+
+ if (actualMetric.value !== actualColumn || idGoal !== actualIdGoal.value) {
+ actualMetric.value = actualColumn;
+ actualIdGoal.value = idGoal;
+ onMetricChanged(actualColumn);
+ }
+ }
+
+ function createSeriesPicker() {
+ const element = $(root.value as HTMLElement);
+ const $widgetName = element.closest('div.widget').find('.widgetTop > .widgetName');
+
+ const $seriesPickerElem = $('<div class="single-metric-view-picker"><div></div></div>');
+
+ const app = createVueApp({
+ render: () => createVNode(SeriesPicker, {
+ multiselect: false,
+ selectableColumns: selectableColumns.value,
+ selectableRows: [],
+ selectedColumns: selectedColumns.value,
+ selectedRows: [],
+ onSelect: ({ columns }: { columns: string[] }) => {
+ setMetric(columns[0]);
+ },
+ }),
+ });
+
+ $widgetName.append($seriesPickerElem);
+ app.mount($seriesPickerElem.children()[0]);
+ return app;
+ }
+
+ let seriesPickerApp: ReturnType<typeof createVueApp>;
+
+ onMounted(() => {
+ seriesPickerApp = createSeriesPicker();
+ });
+
+ onBeforeUnmount(() => {
+ $(root.value as HTMLElement)
+ .closest('.widgetContent')
+ .off('widget:destroy')
+ .off('widget:reload');
+ $(root.value as HTMLElement)
+ .closest('div.widget')
+ .find('.single-metric-view-picker')
+ .remove();
+ seriesPickerApp.unmount();
+ });
+
+ watch(() => props.metric, () => {
+ onMetricChanged(props.metric);
+ });
+ onMetricChanged(props.metric);
+
+ return {
+ root,
+ metricValue,
+ isLoading,
+ selectedColumns,
+ responses,
+ metricValueUnformatted,
+ pastValueUnformatted,
+ metricChangePercent,
+ pastValue,
+ metricTranslation,
+ metricDocumentation,
+ sparklineParams,
+ pastPeriod,
+ selectableColumns,
+ currentPeriod,
+ };
+ },
+});
+</script>
diff --git a/plugins/CoreVisualizations/vue/src/index.ts b/plugins/CoreVisualizations/vue/src/index.ts
new file mode 100644
index 0000000000..28caad2a5f
--- /dev/null
+++ b/plugins/CoreVisualizations/vue/src/index.ts
@@ -0,0 +1,12 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+import './SeriesPicker/SeriesPicker.adapter';
+import './SingleMetricView/SingleMetricView.adapter';
+
+export { default as SeriesPicker } from './SeriesPicker/SeriesPicker.vue';
+export { default as SingleMetricView } from './SingleMetricView/SingleMetricView.vue';
diff --git a/plugins/CoreVue/types/index.d.ts b/plugins/CoreVue/types/index.d.ts
index 24fc1c20bc..5e742cdbb1 100644
--- a/plugins/CoreVue/types/index.d.ts
+++ b/plugins/CoreVue/types/index.d.ts
@@ -87,6 +87,7 @@ declare global {
lazyScrollToContent(): void;
registerShortcut(key: string, description: string, callback: (event: ExtendedKeyboardEvent) => void): void;
compileAngularComponents(selector: JQuery|JQLite|HTMLElement|string, options?: CompileAngularComponentsOptions): void;
+ calculateEvolution(currentValue: number, pastValue?: number|null): number;
sendContentAsDownload(filename: string, content: any, mimeType?: string): void;
}
diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png b/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png
index bcd88012d9..b8f46b1bf3 100644
--- a/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png
+++ b/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_removed.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:173ca9bc58712a4ff73d8fad71aa2c0b78d220cea1cbf62b42989d0ef6b68e6f
-size 481773
+oid sha256:51828d341919bed0dab33c8d430d822e0bfdb28140aa19bd36aa05db0348bde5
+size 481845
diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png
index e6daf1325b..a85afefe06 100644
--- a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png
+++ b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:724ea3a960bdb6df553c7c96e4837a99f4b9dd525b042f50cfad69063587b945
-size 742430
+oid sha256:9ce23f72bf82a2280cffeccd63b9f5829aa834ff1b3fc0d60e67ada992f59292
+size 742383
diff --git a/plugins/UsersManager/vue/dist/UsersManager.umd.js b/plugins/UsersManager/vue/dist/UsersManager.umd.js
index e4ab913ac6..6ffaf671af 100644
--- a/plugins/UsersManager/vue/dist/UsersManager.umd.js
+++ b/plugins/UsersManager/vue/dist/UsersManager.umd.js
@@ -3587,38 +3587,38 @@ NewsletterSettingsvue_type_script_lang_ts.render = NewsletterSettingsvue_type_te
scope: {},
directiveName: 'matomoNewsletterSettings'
}));
-// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue?vue&type=template&id=d46d01d4
+// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue?vue&type=template&id=02979bea
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_1 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_1 = {
id: "userSettingsTable"
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_2 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_2 = {
key: 0
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_3 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_3 = {
id: "languageHelp",
class: "inline-help-node"
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_4 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_4 = {
target: "_blank",
rel: "noreferrer noopener",
href: "https://matomo.org/translations/"
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_5 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_5 = {
class: "sites_autocomplete"
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_6 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_6 = {
class: "modal",
id: "confirmChangesWithPassword",
ref: "confirmChangesWithPasswordModal"
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_7 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_7 = {
class: "modal-content"
};
-var PersonalSettingsvue_type_template_id_d46d01d4_hoisted_8 = {
+var PersonalSettingsvue_type_template_id_02979bea_hoisted_8 = {
class: "modal-footer"
};
-function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $props, $setup, $data, $options) {
+function PersonalSettingsvue_type_template_id_02979bea_render(_ctx, _cache, $props, $setup, $data, $options) {
var _component_Field = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Field");
var _component_SiteSelector = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("SiteSelector");
@@ -3634,7 +3634,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
feature: 'true'
}, {
default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () {
- return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("form", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
+ return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("form", PersonalSettingsvue_type_template_id_02979bea_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
uicontrol: "text",
name: "username",
title: _ctx.translate('General_Username'),
@@ -3644,7 +3644,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
return _ctx.username = $event;
}),
"inline-help": _ctx.translate('UsersManager_YourUsernameCannotBeChanged')
- }, null, 8, ["title", "modelValue", "inline-help"])]), _ctx.isUsersAdminEnabled ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
+ }, null, 8, ["title", "modelValue", "inline-help"])]), _ctx.isUsersAdminEnabled ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", PersonalSettingsvue_type_template_id_02979bea_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
uicontrol: "text",
name: "email",
"model-value": _ctx.email,
@@ -3654,7 +3654,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
}),
maxlength: 100,
title: _ctx.translate('UsersManager_Email')
- }, null, 8, ["model-value", "title"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_3, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_4, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('LanguagesManager_AboutPiwikTranslations')), 1)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
+ }, null, 8, ["model-value", "title"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_02979bea_hoisted_3, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", PersonalSettingsvue_type_template_id_02979bea_hoisted_4, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('LanguagesManager_AboutPiwikTranslations')), 1)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
uicontrol: "select",
name: "language",
modelValue: _ctx.language,
@@ -3683,7 +3683,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
introduction: _ctx.translate('UsersManager_ReportToLoadByDefault'),
title: _ctx.translate('General_AllWebsitesDashboard'),
options: _ctx.defaultReportOptions
- }, null, 8, ["modelValue", "introduction", "title", "options"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_SiteSelector, {
+ }, null, 8, ["modelValue", "introduction", "title", "options"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_02979bea_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_SiteSelector, {
modelValue: _ctx.site,
"onUpdate:modelValue": _cache[5] || (_cache[5] = function ($event) {
return _ctx.site = $event;
@@ -3707,7 +3707,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
return _ctx.save();
}),
saving: _ctx.loading
- }, null, 8, ["saving"]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_7, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('UsersManager_ConfirmWithPassword')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
+ }, null, 8, ["saving"]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_02979bea_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_02979bea_hoisted_7, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('UsersManager_ConfirmWithPassword')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, {
uicontrol: "password",
name: "currentPassword",
autocomplete: false,
@@ -3717,7 +3717,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
}),
"full-width": true,
title: _ctx.translate('UsersManager_YourCurrentPassword')
- }, null, 8, ["modelValue", "title"])])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_d46d01d4_hoisted_8, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", {
+ }, null, 8, ["modelValue", "title"])])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PersonalSettingsvue_type_template_id_02979bea_hoisted_8, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", {
href: "",
class: "modal-action btn",
onClick: _cache[9] || (_cache[9] = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withModifiers"])(function ($event) {
@@ -3737,7 +3737,7 @@ function PersonalSettingsvue_type_template_id_d46d01d4_render(_ctx, _cache, $pro
_: 1
}, 8, ["content-title"]);
}
-// CONCATENATED MODULE: ./plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue?vue&type=template&id=d46d01d4
+// CONCATENATED MODULE: ./plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue?vue&type=template&id=02979bea
// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue?vue&type=script&lang=ts
@@ -3895,7 +3895,7 @@ var PersonalSettingsvue_type_script_lang_ts_window = window,
-PersonalSettingsvue_type_script_lang_ts.render = PersonalSettingsvue_type_template_id_d46d01d4_render
+PersonalSettingsvue_type_script_lang_ts.render = PersonalSettingsvue_type_template_id_02979bea_render
/* harmony default export */ var PersonalSettings = (PersonalSettingsvue_type_script_lang_ts);
// CONCATENATED MODULE: ./plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.adapter.ts
diff --git a/plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue b/plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue
index 927368a371..c4f88b93d5 100644
--- a/plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue
+++ b/plugins/UsersManager/vue/src/PersonalSettings/PersonalSettings.vue
@@ -235,6 +235,7 @@ export default defineComponent({
site: {
id: this.defaultReportIdSite,
name: Matomo.helper.htmlDecode(this.defaultReportSiteName),
+
},
theDefaultDate: this.defaultDate,
loading: false,