From e919e38bf9af52601d791a140d9855e2b7ca9ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 3 May 2021 15:32:10 +0200 Subject: [PATCH 01/11] MOBILE-3745 a11y: Add contrast to chevrons --- src/theme/components/format-text.scss | 10 +++++----- src/theme/theme.base.scss | 5 +++-- src/theme/theme.dark.scss | 7 ++----- src/theme/theme.light.scss | 11 ++++++++++- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/theme/components/format-text.scss b/src/theme/components/format-text.scss index e94b4814e..31c6554ca 100644 --- a/src/theme/components/format-text.scss +++ b/src/theme/components/format-text.scss @@ -47,10 +47,6 @@ core-format-text { background-color: var(--background); color: var(--color); padding-left: 10px; // RTL - /*@include darkmode() { - color: var(--white); - background-color: $core-dark-item-bg-color; - }*/ } &:before { @@ -71,12 +67,16 @@ core-format-text { &.core-expand-in-fullview { .core-show-more { - @include push-arrow-color(dedede, true); + @include push-arrow-color(626262, true); @include padding-horizontal(null, 18px); @include background-position(end, 0, center); background-repeat: no-repeat; background-size: 14px 14px; + + @include darkmode() { + @include push-arrow-color(ffffff, true); + } } } } diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index a3b41bb81..999118e32 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -149,8 +149,9 @@ ion-alert.core-nohead { // Ionic item divider. ion-item-divider { .item-detail-icon { - font-size: 20px; - opacity: 0.25; + color: var(--ion-item-detail-icon-color); + font-size: var(--ion-item-detail-icon-font-size); + opacity: var(--ion-item-detail-icon-opacity); padding-inline-end: 16px; } diff --git a/src/theme/theme.dark.scss b/src/theme/theme.dark.scss index c8ab76fec..37126c70e 100644 --- a/src/theme/theme.dark.scss +++ b/src/theme/theme.dark.scss @@ -43,8 +43,6 @@ --ion-tab-bar-background: #1f1f1f; - --ion-item-background: #1e1e1e; - --ion-card-background: #1c1c1d; ion-content { @@ -62,12 +60,11 @@ --text-color: var(--custom-progress-text-color, var(--gray-lighter)); } + --ion-item-background: #1e1e1e; + --ion-item-detail-icon-color: var(--white); ion-item-divider { --background: var(--black); --color: var(--white); - .item-detail-icon { - color: var(--white); - } } --core-button-select-background: var(--custom-button-select-background, #3a3a3a); diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index 13f04e715..dc8e0a9ee 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -65,7 +65,6 @@ --ion-text-color: #{$text-color}; --ion-text-color-rgb: 58,58,58; --ion-card-color: var(--ion-text-color); - --ion-item-background: var(--white); --text-hightlight-background-color: var(--custom-text-hightlight-background-color, #99c1ed); @@ -144,6 +143,16 @@ --side-blocks-min-width: var(--custom-side-blocks-min-width, 280px); } + --ion-item-background: var(--white); + --ion-item-detail-icon-color: var(--gray-darker); + --ion-item-detail-icon-font-size: 20px; + --ion-item-detail-icon-opacity: 1; + ion-item { + --detail-icon-color: var(--ion-item-detail-icon-color); + --detail-icon-font-size: var(--ion-item-detail-icon-font-size); + --detail-icon-opacity: var(--ion-item-detail-icon-opacity); + } + ion-item-divider { --background: var(--gray-lighter); --color: inherit; From e7b61672f1d6d7d469bf604272e548e2c4757f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 6 May 2021 10:56:20 +0200 Subject: [PATCH 02/11] MOBILE-3745 a11y: Use fill clear on top toolbar buttons --- src/addons/calendar/pages/day/day.html | 2 +- src/addons/calendar/pages/index/index.html | 2 +- src/addons/calendar/pages/list/list.html | 2 +- .../components/conversation-info/conversation-info.html | 2 +- src/addons/messages/pages/contacts/contacts.html | 2 +- .../pages/group-conversations/group-conversations.html | 2 +- .../components/edit-feedback-modal/edit-feedback-modal.html | 2 +- src/addons/mod/book/components/toc/toc.html | 2 +- src/addons/mod/chat/components/users-modal/users-modal.html | 2 +- src/addons/mod/chat/pages/chat/chat.html | 2 +- src/addons/mod/data/components/search/search.html | 2 +- src/addons/mod/forum/components/edit-post/edit-post.html | 2 +- .../components/sort-order-selector/sort-order-selector.html | 2 +- src/addons/mod/imscp/components/toc/toc.html | 2 +- src/addons/mod/lesson/components/menu-modal/menu-modal.html | 2 +- .../lesson/components/password-modal/password-modal.html | 2 +- src/addons/mod/lesson/pages/player/player.html | 2 +- .../quiz/components/navigation-modal/navigation-modal.html | 2 +- .../quiz/components/preflight-modal/preflight-modal.html | 2 +- src/addons/mod/quiz/pages/player/player.html | 6 +++--- src/addons/mod/quiz/pages/review/review.html | 2 +- src/addons/mod/scorm/components/toc/toc.html | 2 +- src/addons/mod/scorm/pages/player/player.html | 2 +- src/addons/mod/wiki/components/map/map.html | 2 +- src/addons/mod/workshop/components/phase/phase.html | 2 +- src/addons/notes/components/add/add-modal.html | 2 +- src/core/components/recaptcha/core-recaptcha-modal.html | 2 +- src/core/features/comments/components/add/add.html | 2 +- .../components/choose-site-modal/choose-site-modal.html | 2 +- .../components/section-selector/section-selector.html | 2 +- .../components/self-enrol-password/self-enrol-password.html | 2 +- .../emulator/components/capture-media/capture-media.html | 2 +- src/core/features/login/components/site-help/site-help.html | 2 +- .../login/components/site-onboarding/site-onboarding.html | 4 ++-- .../login/pages/change-password/change-password.html | 2 +- src/core/features/login/pages/credentials/credentials.html | 2 +- .../features/login/pages/email-signup/email-signup.html | 2 +- src/core/features/login/pages/site/site.html | 2 +- src/core/features/login/pages/sites/sites.html | 2 +- .../features/rating/components/ratings/ratings-modal.html | 2 +- src/core/features/settings/pages/deviceinfo/deviceinfo.html | 2 +- .../sharedfiles/components/list-modal/list-modal.html | 2 +- src/core/features/viewer/components/image/image.html | 2 +- .../features/viewer/components/qr-scanner/qr-scanner.html | 2 +- src/core/features/viewer/components/text/text.html | 2 +- src/theme/theme.base.scss | 4 ++++ 46 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/addons/calendar/pages/day/day.html b/src/addons/calendar/pages/day/day.html index 6a4f49244..25aabb33e 100644 --- a/src/addons/calendar/pages/day/day.html +++ b/src/addons/calendar/pages/day/day.html @@ -5,7 +5,7 @@ {{ 'addon.calendar.calendarevents' | translate }} - + diff --git a/src/addons/calendar/pages/index/index.html b/src/addons/calendar/pages/index/index.html index 7f1718b39..c5687230a 100644 --- a/src/addons/calendar/pages/index/index.html +++ b/src/addons/calendar/pages/index/index.html @@ -5,7 +5,7 @@ {{ (showCalendar ? 'addon.calendar.calendarevents' : 'addon.calendar.upcomingevents') | translate }} - + diff --git a/src/addons/calendar/pages/list/list.html b/src/addons/calendar/pages/list/list.html index e792a4dae..bb3bc6edb 100644 --- a/src/addons/calendar/pages/list/list.html +++ b/src/addons/calendar/pages/list/list.html @@ -5,7 +5,7 @@ {{ 'addon.calendar.calendarevents' | translate }} - + diff --git a/src/addons/messages/components/conversation-info/conversation-info.html b/src/addons/messages/components/conversation-info/conversation-info.html index b99965dc4..129cbdcb9 100644 --- a/src/addons/messages/components/conversation-info/conversation-info.html +++ b/src/addons/messages/components/conversation-info/conversation-info.html @@ -2,7 +2,7 @@ {{ 'addon.messages.groupinfo' | translate }} - + diff --git a/src/addons/messages/pages/contacts/contacts.html b/src/addons/messages/pages/contacts/contacts.html index 278067540..381cd613d 100644 --- a/src/addons/messages/pages/contacts/contacts.html +++ b/src/addons/messages/pages/contacts/contacts.html @@ -5,7 +5,7 @@ {{ 'addon.messages.contacts' | translate }} - + - diff --git a/src/addons/mod/quiz/pages/review/review.html b/src/addons/mod/quiz/pages/review/review.html index fdf75869f..6ed4b8fe7 100644 --- a/src/addons/mod/quiz/pages/review/review.html +++ b/src/addons/mod/quiz/pages/review/review.html @@ -6,7 +6,7 @@ {{ 'addon.mod_quiz.review' | translate }} - diff --git a/src/addons/mod/scorm/components/toc/toc.html b/src/addons/mod/scorm/components/toc/toc.html index 0a02d1831..46b28caff 100644 --- a/src/addons/mod/scorm/components/toc/toc.html +++ b/src/addons/mod/scorm/components/toc/toc.html @@ -2,7 +2,7 @@ {{ 'addon.mod_scorm.toc' | translate }} - + diff --git a/src/addons/mod/scorm/pages/player/player.html b/src/addons/mod/scorm/pages/player/player.html index acaf6d7f5..bf33b88fc 100644 --- a/src/addons/mod/scorm/pages/player/player.html +++ b/src/addons/mod/scorm/pages/player/player.html @@ -8,7 +8,7 @@ - diff --git a/src/addons/mod/wiki/components/map/map.html b/src/addons/mod/wiki/components/map/map.html index aab584673..7e34f12ef 100644 --- a/src/addons/mod/wiki/components/map/map.html +++ b/src/addons/mod/wiki/components/map/map.html @@ -2,7 +2,7 @@ {{ 'addon.mod_wiki.map' | translate }} - + diff --git a/src/addons/mod/workshop/components/phase/phase.html b/src/addons/mod/workshop/components/phase/phase.html index ee953c812..59b0153eb 100644 --- a/src/addons/mod/workshop/components/phase/phase.html +++ b/src/addons/mod/workshop/components/phase/phase.html @@ -5,7 +5,7 @@ {{ 'addon.mod_workshop.userplan' | translate }} - + diff --git a/src/addons/notes/components/add/add-modal.html b/src/addons/notes/components/add/add-modal.html index e0d6868d6..ad401e126 100644 --- a/src/addons/notes/components/add/add-modal.html +++ b/src/addons/notes/components/add/add-modal.html @@ -2,7 +2,7 @@ {{ 'addon.notes.addnewnote' | translate }} - + diff --git a/src/core/components/recaptcha/core-recaptcha-modal.html b/src/core/components/recaptcha/core-recaptcha-modal.html index 74296c2a2..9d8f70aa6 100644 --- a/src/core/components/recaptcha/core-recaptcha-modal.html +++ b/src/core/components/recaptcha/core-recaptcha-modal.html @@ -3,7 +3,7 @@ {{ 'core.login.security_question' | translate }} - + diff --git a/src/core/features/comments/components/add/add.html b/src/core/features/comments/components/add/add.html index 8d163f380..cb848f344 100644 --- a/src/core/features/comments/components/add/add.html +++ b/src/core/features/comments/components/add/add.html @@ -2,7 +2,7 @@ {{ 'core.comments.addcomment' | translate }} - + diff --git a/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.html b/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.html index c9e2a7de9..a23667308 100644 --- a/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.html +++ b/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.html @@ -5,7 +5,7 @@ {{ 'core.contentlinks.chooseaccount' | translate }} - + diff --git a/src/core/features/course/components/section-selector/section-selector.html b/src/core/features/course/components/section-selector/section-selector.html index c7d6a3feb..e31d1c9e9 100644 --- a/src/core/features/course/components/section-selector/section-selector.html +++ b/src/core/features/course/components/section-selector/section-selector.html @@ -2,7 +2,7 @@ {{ 'core.course.sections' | translate }} - + diff --git a/src/core/features/courses/components/self-enrol-password/self-enrol-password.html b/src/core/features/courses/components/self-enrol-password/self-enrol-password.html index 74eea5067..5de62417d 100644 --- a/src/core/features/courses/components/self-enrol-password/self-enrol-password.html +++ b/src/core/features/courses/components/self-enrol-password/self-enrol-password.html @@ -3,7 +3,7 @@ {{ 'core.courses.selfenrolment' | translate }} - + diff --git a/src/core/features/emulator/components/capture-media/capture-media.html b/src/core/features/emulator/components/capture-media/capture-media.html index 95819fa7a..d3d3825e3 100644 --- a/src/core/features/emulator/components/capture-media/capture-media.html +++ b/src/core/features/emulator/components/capture-media/capture-media.html @@ -3,7 +3,7 @@ {{ title | translate }} - {{ 'core.cancel' | translate }} + {{ 'core.cancel' | translate }} {{ 'core.done' | translate }} diff --git a/src/core/features/login/components/site-help/site-help.html b/src/core/features/login/components/site-help/site-help.html index e683a53c5..ecbc3ad2a 100644 --- a/src/core/features/login/components/site-help/site-help.html +++ b/src/core/features/login/components/site-help/site-help.html @@ -3,7 +3,7 @@ {{ 'core.login.help' | translate }} - + diff --git a/src/core/features/login/components/site-onboarding/site-onboarding.html b/src/core/features/login/components/site-onboarding/site-onboarding.html index 85bb6c5f0..851adbfe8 100644 --- a/src/core/features/login/components/site-onboarding/site-onboarding.html +++ b/src/core/features/login/components/site-onboarding/site-onboarding.html @@ -1,13 +1,13 @@ - + - + {{'core.skip' | translate}} diff --git a/src/core/features/login/pages/change-password/change-password.html b/src/core/features/login/pages/change-password/change-password.html index ce70a4fdd..a2704e19d 100644 --- a/src/core/features/login/pages/change-password/change-password.html +++ b/src/core/features/login/pages/change-password/change-password.html @@ -7,7 +7,7 @@ {{ 'core.login.changepassword' | translate }} - + diff --git a/src/core/features/login/pages/credentials/credentials.html b/src/core/features/login/pages/credentials/credentials.html index 8043950ab..7594c44cc 100644 --- a/src/core/features/login/pages/credentials/credentials.html +++ b/src/core/features/login/pages/credentials/credentials.html @@ -7,7 +7,7 @@ {{ 'core.login.login' | translate }} - diff --git a/src/core/features/login/pages/email-signup/email-signup.html b/src/core/features/login/pages/email-signup/email-signup.html index bde636e63..5da51c810 100644 --- a/src/core/features/login/pages/email-signup/email-signup.html +++ b/src/core/features/login/pages/email-signup/email-signup.html @@ -7,7 +7,7 @@ {{ 'core.login.newaccount' | translate }} - diff --git a/src/core/features/login/pages/site/site.html b/src/core/features/login/pages/site/site.html index 60c8a4458..abd234287 100644 --- a/src/core/features/login/pages/site/site.html +++ b/src/core/features/login/pages/site/site.html @@ -7,7 +7,7 @@ {{ 'core.login.connecttomoodle' | translate }} - diff --git a/src/core/features/login/pages/sites/sites.html b/src/core/features/login/pages/sites/sites.html index 373cf5f00..59fb663d8 100644 --- a/src/core/features/login/pages/sites/sites.html +++ b/src/core/features/login/pages/sites/sites.html @@ -7,7 +7,7 @@ {{ 'core.settings.sites' | translate }} - + {{ 'core.rating.ratings' | translate }} - + diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.html b/src/core/features/settings/pages/deviceinfo/deviceinfo.html index 6783de44d..a1fa11b74 100644 --- a/src/core/features/settings/pages/deviceinfo/deviceinfo.html +++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.html @@ -7,7 +7,7 @@ {{ 'core.settings.deviceinfo' | translate }} - + diff --git a/src/core/features/sharedfiles/components/list-modal/list-modal.html b/src/core/features/sharedfiles/components/list-modal/list-modal.html index d07816245..7b0fbb7b7 100644 --- a/src/core/features/sharedfiles/components/list-modal/list-modal.html +++ b/src/core/features/sharedfiles/components/list-modal/list-modal.html @@ -6,7 +6,7 @@ {{ title }} - + diff --git a/src/core/features/viewer/components/image/image.html b/src/core/features/viewer/components/image/image.html index 624200036..04da60dc1 100644 --- a/src/core/features/viewer/components/image/image.html +++ b/src/core/features/viewer/components/image/image.html @@ -2,7 +2,7 @@ {{ title }} - + diff --git a/src/core/features/viewer/components/qr-scanner/qr-scanner.html b/src/core/features/viewer/components/qr-scanner/qr-scanner.html index f70273deb..c42c63baf 100644 --- a/src/core/features/viewer/components/qr-scanner/qr-scanner.html +++ b/src/core/features/viewer/components/qr-scanner/qr-scanner.html @@ -2,7 +2,7 @@ {{ title }} - + diff --git a/src/core/features/viewer/components/text/text.html b/src/core/features/viewer/components/text/text.html index 631f2c250..6bff2f20c 100644 --- a/src/core/features/viewer/components/text/text.html +++ b/src/core/features/viewer/components/text/text.html @@ -3,7 +3,7 @@ {{ title }} - + diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 999118e32..b031e6ec8 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -77,6 +77,10 @@ ion-toolbar .in-toolbar.button-clear { --ion-toolbar-color: var(--ion-color-primary-contrast); } +ion-header ion-toolbar .button.button-clear { + --color: var(--ion-color-primary-contrast); +} + ion-toolbar .core-navbar-button-hidden { display: none !important; } From fa22325fb45e8b807ee62fa02a2d172cd445a3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 6 May 2021 11:06:17 +0200 Subject: [PATCH 03/11] MOBILE-3745 a11y: Add new combobox component --- .../myoverview/addon-block-myoverview.html | 7 +- .../components/myoverview/myoverview.ts | 5 +- .../timeline/addon-block-timeline.html | 7 +- .../timeline/components/timeline/timeline.ts | 7 +- .../mod/forum/components/index/index.html | 18 +-- .../mod/forum/components/index/index.scss | 14 -- .../mod/forum/components/index/index.ts | 57 ++++---- .../sort-order-selector.html | 4 +- src/addons/notes/pages/list/list.html | 12 +- src/addons/notes/pages/list/list.page.ts | 8 +- .../pages/settings/settings.html | 7 +- .../privatefiles/pages/index/index.html | 10 +- src/addons/privatefiles/pages/index/index.ts | 8 +- src/core/components/combobox/combobox.scss | 129 ++++++++++++++++++ src/core/components/combobox/combobox.ts | 77 +++++++++++ .../components/combobox/core-combobox.html | 33 +++++ src/core/components/components.module.ts | 3 + .../components/format/core-course-format.html | 13 +- .../course/components/format/format.ts | 30 ++-- .../section-selector/section-selector.html | 4 +- src/core/features/course/lang.json | 3 +- .../features/tag/pages/search/search.html | 5 +- src/core/services/utils/dom.ts | 22 ++- src/theme/theme.base.scss | 56 -------- src/theme/theme.dark.scss | 2 +- src/theme/theme.light.scss | 3 +- 26 files changed, 373 insertions(+), 171 deletions(-) create mode 100644 src/core/components/combobox/combobox.scss create mode 100644 src/core/components/combobox/combobox.ts create mode 100644 src/core/components/combobox/core-combobox.html diff --git a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html index b3e3a4caf..f91a08355 100644 --- a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html +++ b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html @@ -34,10 +34,9 @@ -
+
- + {{ 'addon.block_myoverview.allincludinghidden' | translate }} @@ -66,7 +65,7 @@ {{ 'addon.block_myoverview.hiddencourses' | translate }} - +
diff --git a/src/addons/block/myoverview/components/myoverview/myoverview.ts b/src/addons/block/myoverview/components/myoverview/myoverview.ts index 630d60a28..c2c4fe7c4 100644 --- a/src/addons/block/myoverview/components/myoverview/myoverview.ts +++ b/src/addons/block/myoverview/components/myoverview/myoverview.ts @@ -414,8 +414,11 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem /** * The selected courses filter have changed. + * + * @param filter New filter */ - selectedChanged(): void { + selectedChanged(filter: string): void { + this.selectedFilter = filter; this.setCourseFilter(this.selectedFilter); } diff --git a/src/addons/block/timeline/components/timeline/addon-block-timeline.html b/src/addons/block/timeline/components/timeline/addon-block-timeline.html index 188b94023..2249fac9e 100644 --- a/src/addons/block/timeline/components/timeline/addon-block-timeline.html +++ b/src/addons/block/timeline/components/timeline/addon-block-timeline.html @@ -10,9 +10,8 @@ -
- +
+ {{ 'core.all' | translate }} {{ 'addon.block_timeline.overdue' | translate }} {{ 'addon.block_timeline.duedate' | translate }} @@ -20,7 +19,7 @@ {{ 'addon.block_timeline.next30days' | translate }} {{ 'addon.block_timeline.next3months' | translate }} {{ 'addon.block_timeline.next6months' | translate }} - +
-
- - {{ selectedSortOrder.label | translate }} -
-
+
+ +
sortOrder.value === value) || this.sortOrders[0]; + this.sortOrderSelectorModalOptions.componentProps!.selected = this.selectedSortOrder.value; } /** @@ -621,31 +630,18 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom } /** - * Display the sort order selector modal. + * Changes the sort order. + * + * @param sortOrder Sort order new data. */ - async showSortOrderSelector(): Promise { - if (!this.sortingAvailable) { - return; - } - - const modal = await ModalController.create({ - component: AddonModForumSortOrderSelectorComponent, - componentProps: { - sortOrders: this.sortOrders, - selected: this.selectedSortOrder!.value, - }, - }); - - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result.data && result.data.value != this.selectedSortOrder?.value) { - this.selectedSortOrder = result.data; + async setSortOrder(sortOrder: AddonModForumSortOrder): Promise { + if (sortOrder.value != this.selectedSortOrder?.value) { + this.selectedSortOrder = sortOrder; + this.sortOrderSelectorModalOptions.componentProps!.selected = this.selectedSortOrder.value; this.page = 0; try { - await CoreUser.setUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, result.data.value.toFixed(0)); + await CoreUser.setUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, sortOrder.value.toFixed(0)); await this.showLoadingAndFetch(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error updating preference.'); @@ -653,6 +649,17 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom } } + /** + * Display the sort order selector modal. + */ + async showSortOrderSelector(): Promise { + const modalData = await CoreDomUtils.openModal(this.sortOrderSelectorModalOptions); + + if (modalData) { + this.setSortOrder(modalData); + } + } + /** * Show the context menu. * diff --git a/src/addons/mod/forum/components/sort-order-selector/sort-order-selector.html b/src/addons/mod/forum/components/sort-order-selector/sort-order-selector.html index 9be7f44ab..fa99df96d 100644 --- a/src/addons/mod/forum/components/sort-order-selector/sort-order-selector.html +++ b/src/addons/mod/forum/components/sort-order-selector/sort-order-selector.html @@ -1,6 +1,6 @@ - {{ 'core.sort' | translate }} + {{ 'core.sort' | translate }} @@ -9,7 +9,7 @@ - +

{{user!.fullname}}

-
- - {{ 'addon.notes.sitenotes' | translate }} - {{ 'addon.notes.coursenotes' | translate }} - {{ 'addon.notes.personalnotes' | translate }} - -
+ + {{ 'addon.notes.sitenotes' | translate }} + {{ 'addon.notes.coursenotes' | translate }} + {{ 'addon.notes.personalnotes' | translate }} + diff --git a/src/addons/notes/pages/list/list.page.ts b/src/addons/notes/pages/list/list.page.ts index bd3528a1a..e4385b156 100644 --- a/src/addons/notes/pages/list/list.page.ts +++ b/src/addons/notes/pages/list/list.page.ts @@ -157,8 +157,11 @@ export class AddonNotesListPage implements OnInit, OnDestroy { /** * Function called when the type has changed. + * + * @param type New type. */ - async typeChanged(): Promise { + async typeChanged(type: string): Promise { + this.type = type; this.notesLoaded = false; this.refreshIcon = CoreConstants.ICON_LOADING; this.syncIcon = CoreConstants.ICON_LOADING; @@ -199,8 +202,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { this.refreshNotes(false); } else if (result.data.type && result.data.type != this.type) { - this.type = result.data.type; - this.typeChanged(); + this.typeChanged(result.data.type); } } } diff --git a/src/addons/notifications/pages/settings/settings.html b/src/addons/notifications/pages/settings/settings.html index e8f225b99..b343928fc 100644 --- a/src/addons/notifications/pages/settings/settings.html +++ b/src/addons/notifications/pages/settings/settings.html @@ -41,13 +41,12 @@ - + {{ processor.displayname }} - + diff --git a/src/addons/privatefiles/pages/index/index.html b/src/addons/privatefiles/pages/index/index.html index 8d136e900..c35695fcb 100644 --- a/src/addons/privatefiles/pages/index/index.html +++ b/src/addons/privatefiles/pages/index/index.html @@ -14,12 +14,10 @@ -
- - {{ 'addon.privatefiles.privatefiles' | translate }} - {{ 'addon.privatefiles.sitefiles' | translate }} - -
+ + {{ 'addon.privatefiles.privatefiles' | translate }} + {{ 'addon.privatefiles.sitefiles' | translate }} + diff --git a/src/addons/privatefiles/pages/index/index.ts b/src/addons/privatefiles/pages/index/index.ts index 8c9933dd0..b20ad8ad7 100644 --- a/src/addons/privatefiles/pages/index/index.ts +++ b/src/addons/privatefiles/pages/index/index.ts @@ -99,7 +99,7 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { } if (this.root) { - this.rootChanged(); + this.rootChanged(this.root); } else { this.filesLoaded = true; } @@ -127,8 +127,12 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { /** * Function called when the root has changed. + * + * @param root New root. */ - rootChanged(): void { + rootChanged(root: 'my' | 'site'): void { + this.root = root; + this.filesLoaded = false; this.component = this.root == 'my' ? AddonPrivateFilesProvider.PRIVATE_FILES_COMPONENT : AddonPrivateFilesProvider.SITE_FILES_COMPONENT; diff --git a/src/core/components/combobox/combobox.scss b/src/core/components/combobox/combobox.scss new file mode 100644 index 000000000..19a8eb6fc --- /dev/null +++ b/src/core/components/combobox/combobox.scss @@ -0,0 +1,129 @@ +@import "~theme/globals"; + +:host { + ion-select, + ion-button { + --icon-margin: 0 8px; + --background: var(--core-combobox-background); + --background-hover: #000000; + --background-activated: #000000; + --background-focused: #000000; + --background-hover-opacity: .04; + + --color: var(--core-combobox-color); + --color-activated: var(--core-combobox-color); + --color-focused: currentcolor; + --color-hover: currentcolor; + + --padding-top: 10px; + --padding-end: 10px; + --padding-bottom: 10px; + --padding-start: 16px; + } +} + +:host-context(.md) { + --background-activated-opacity: 0; + --background-focused-opacity: .12; +} + +:host-context(.ios) { + --background-activated-opacity: .12; + --background-focused-opacity: .15; +} + +ion-select, +ion-button { + background: var(--background); + color: var(--color); + text-overflow: ellipsis; + white-space: nowrap; + min-height: 25px; + overflow: hidden; + margin: 8px; + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12); +} + +ion-select { + &::part(icon) { + margin: var(--icon-margin); + } + + &::after { + @include button-state(); + transition: var(--transition); + z-index: -1; + } + + &:hover::after { + color: var(--color-hover); + background: var(--background-hover); + opacity: var(--background-hover-opacity); + } + + &:focus::after, + &:focus-within::after { + color: var(--color-focused); + background: var(--background-focused); + opacity: var(--background-focused-opacity); + } +} + +ion-button { + --border-radius: 0; + flex: 1; + min-height: 45px; + + &::part(native) { + text-transform: none; + font-weight: 400; + font-size: 16px; + } + + .select-text { + margin-inline-end: auto; + overflow: hidden; + text-overflow: ellipsis; + } + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + + &.ion-activated { + --color: var(--color-activated); + } + + ion-icon { + margin: var(--icon-margin); + } + + .select-icon { + margin: var(--icon-margin); + width: 19px; + height: 19px; + position: relative; + opacity: 0.33; + + .select-icon-inner { + left: 5px; + top: 50%; + margin-top: -2px; + position: absolute; + width: 0px; + height: 0px; + color: currentcolor; + pointer-events: none; + border-top: 5px solid; + border-right: 5px solid transparent; + border-left: 5px solid transparent; + } + } +} diff --git a/src/core/components/combobox/combobox.ts b/src/core/components/combobox/combobox.ts new file mode 100644 index 000000000..068e5c60e --- /dev/null +++ b/src/core/components/combobox/combobox.ts @@ -0,0 +1,77 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { Translate } from '@singletons'; +import { ModalOptions } from '@ionic/core'; +import { CoreDomUtils } from '@services/utils/dom'; + +/** + * Component that show a combo select button (combobox). + * + * @description + * + * Example using modal: + * + * + * selection + * + * + * Example using popover: + * + * + * 1 + * + */ +@Component({ + selector: 'core-combobox', + templateUrl: 'core-combobox.html', + styleUrls: ['combobox.scss'], + encapsulation: ViewEncapsulation.ShadowDom, +}) +export class CoreComboboxComponent { + + @Input() interface: 'popover' | 'modal' = 'popover'; + @Input() label = Translate.instant('core.show'); // Aria label. + @Input() disabled = false; + @Input() selection = ''; + @Output() onChange = new EventEmitter(); // Will emit an event the value changed. + + // Additional options when interface modal is selected. + @Input() icon?: string; // Icon for modal interface. + @Input() protected modalOptions?: ModalOptions; // Will emit an event the value changed. + @Input() listboxId = ''; + + expanded = false; + + async showModal(): Promise { + if (this.expanded || !this.modalOptions) { + return; + } + this.expanded = true; + + if (this.listboxId) { + this.modalOptions.id = this.listboxId; + } + + const data = await CoreDomUtils.openModal(this.modalOptions); + this.expanded = false; + + if (data) { + this.onChange.emit(data); + } + } + +} diff --git a/src/core/components/combobox/core-combobox.html b/src/core/components/combobox/core-combobox.html new file mode 100644 index 000000000..502d108ba --- /dev/null +++ b/src/core/components/combobox/core-combobox.html @@ -0,0 +1,33 @@ + + + + + + + {{ label }}: +
+ {{selection}} +
+ +
diff --git a/src/core/components/components.module.ts b/src/core/components/components.module.ts index ed321c8fa..f319a9ff2 100644 --- a/src/core/components/components.module.ts +++ b/src/core/components/components.module.ts @@ -55,6 +55,7 @@ import { CoreTabsComponent } from './tabs/tabs'; import { CoreTabsOutletComponent } from './tabs-outlet/tabs-outlet'; import { CoreTimerComponent } from './timer/timer'; import { CoreUserAvatarComponent } from './user-avatar/user-avatar'; +import { CoreComboboxComponent } from './combobox/combobox'; @NgModule({ declarations: [ @@ -92,6 +93,7 @@ import { CoreUserAvatarComponent } from './user-avatar/user-avatar'; CoreTabsOutletComponent, CoreTimerComponent, CoreUserAvatarComponent, + CoreComboboxComponent, ], imports: [ CommonModule, @@ -136,6 +138,7 @@ import { CoreUserAvatarComponent } from './user-avatar/user-avatar'; CoreTabsOutletComponent, CoreTimerComponent, CoreUserAvatarComponent, + CoreComboboxComponent, ], }) export class CoreComponentsModule {} diff --git a/src/core/features/course/components/format/core-course-format.html b/src/core/features/course/components/format/core-course-format.html index 374274d27..ba3bfd8a9 100644 --- a/src/core/features/course/components/format/core-course-format.html +++ b/src/core/features/course/components/format/core-course-format.html @@ -19,18 +19,15 @@
- - - {{ 'core.course.sections' | translate }}: - + + - {{ 'core.course.sections' | translate }} + {{ 'core.course.sections' | translate }} -
-
+
diff --git a/src/core/features/course/components/format/format.ts b/src/core/features/course/components/format/format.ts index 2187defbd..4f14cd84b 100644 --- a/src/core/features/course/components/format/format.ts +++ b/src/core/features/course/components/format/format.ts @@ -27,7 +27,7 @@ import { ViewChild, ElementRef, } from '@angular/core'; - +import { ModalOptions } from '@ionic/core'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; @@ -49,7 +49,6 @@ import { IonContent, IonRefresher } from '@ionic/angular'; import { CoreUtils } from '@services/utils/utils'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; -import { ModalController } from '@singletons'; import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; /** @@ -104,6 +103,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { hasSeveralSections?: boolean; imageThumb?: string; progress?: number; + sectionSelectorModalOptions: ModalOptions = { + component: CoreCourseSectionSelectorComponent, + }; protected sectionStatusObserver?: CoreEventObserver; protected selectTabObserver?: CoreEventObserver; @@ -122,6 +124,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { * Component being initialized. */ ngOnInit(): void { + + this.sectionSelectorModalOptions.componentProps = { + course: this.course, + sections: this.sections, + }; + // Listen for section status changes. this.sectionStatusObserver = CoreEvents.on( CoreEvents.SECTION_STATUS_CHANGED, @@ -194,6 +202,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { } if (changes.sections && this.sections) { + this.sectionSelectorModalOptions.componentProps!.sections = this.sections; this.treatSections(this.sections); } @@ -342,21 +351,11 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { this.sectionSelectorExpanded = true; - const modal = await ModalController.create({ - component: CoreCourseSectionSelectorComponent, - componentProps: { - course: this.course, - sections: this.sections, - selected: this.selectedSection, - }, - }); - await modal.present(); - - const result = await modal.onWillDismiss(); + const data = await CoreDomUtils.openModal(this.sectionSelectorModalOptions); this.sectionSelectorExpanded = false; - if (result?.data) { - this.sectionChanged(result?.data); + if (data) { + this.sectionChanged(data); } } @@ -368,6 +367,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { sectionChanged(newSection: CoreCourseSection): void { const previousValue = this.selectedSection; this.selectedSection = newSection; + this.sectionSelectorModalOptions.componentProps!.selected = this.selectedSection; this.data.section = this.selectedSection; if (newSection.id != this.allSectionsId) { diff --git a/src/core/features/course/components/section-selector/section-selector.html b/src/core/features/course/components/section-selector/section-selector.html index e31d1c9e9..a439b13e0 100644 --- a/src/core/features/course/components/section-selector/section-selector.html +++ b/src/core/features/course/components/section-selector/section-selector.html @@ -1,6 +1,6 @@ - {{ 'core.course.sections' | translate }} + {{ 'core.course.sections' | translate }} @@ -9,7 +9,7 @@ - + - + {{ 'core.tag.inalltagcoll' | translate }} {{ collection.name }} - + diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index cffd289dc..041867c16 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -15,7 +15,7 @@ import { Injectable, SimpleChange, ElementRef, KeyValueChanges } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; -import { AlertOptions, AlertButton, TextFieldTypes } from '@ionic/core'; +import { ModalOptions, AlertOptions, AlertButton, TextFieldTypes } from '@ionic/core'; import { Md5 } from 'ts-md5'; import { CoreApp } from '@services/app'; @@ -1681,6 +1681,26 @@ export class CoreDomUtilsProvider { }); } + /** + * Opens a Modal. + * + * @param opts Modal Options. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async openModal( + opts: ModalOptions, + ): Promise { + + const modal = await ModalController.create(opts); + + await modal.present(); + + const result = await modal.onWillDismiss(); + if (result?.data) { + return result?.data; + } + } + /** * View an image in a modal. * diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index b031e6ec8..5dcf0cd2b 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -382,62 +382,6 @@ ion-select::part(text) { white-space: normal; } -ion-select.core-button-select, -.core-button-select { - --background: var(--core-button-select-background); - background: var(--background); - --color: var(--ion-color-primary); - color: var(--color); - text-overflow: ellipsis; - white-space: nowrap; - min-height: 45px; - overflow: hidden; - margin: 8px; - box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12); - &::part(icon) { - margin: 0 8px; - } - .core-button-select-text { - margin-inline-end: auto; - } - - &.ion-activated { - --color: var(--ion-color-primary-contrast); - } - -} -ion-button.core-button-select { - --border-radius: 0; - &::part(native) { - text-transform: none; - font-weight: 400; - font-size: 16px; - } - ion-icon { - margin: 0 8px; - } - .select-icon { - width: 19px; - height: 19px; - position: relative; - opacity: 0.33; - - .select-icon-inner { - left: 5px; - top: 50%; - margin-top: -2px; - position: absolute; - width: 0px; - height: 0px; - color: currentcolor; - pointer-events: none; - border-top: 5px solid; - border-right: 5px solid transparent; - border-left: 5px solid transparent; - } - } -} - // File uploader. .action-sheet-button input.core-fileuploader-file-handler-input { position: absolute; diff --git a/src/theme/theme.dark.scss b/src/theme/theme.dark.scss index 37126c70e..096f833ae 100644 --- a/src/theme/theme.dark.scss +++ b/src/theme/theme.dark.scss @@ -67,7 +67,7 @@ --color: var(--white); } - --core-button-select-background: var(--custom-button-select-background, #3a3a3a); + --core-combobox-background: var(--custom-combobox-background, #3a3a3a); --core-login-background: var(--custom-login-background, #3a3a3a); --core-login-text-color: var(--custom-login-text-color, white); diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index dc8e0a9ee..165cb58c7 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -158,7 +158,8 @@ --color: inherit; } - --core-button-select-background: var(--custom-button-select-background, var(--ion-color-primary-contrast)); + --core-combobox-background: var(--custom-combobox-background, var(--ion-item-background)); + --core-combobox-color: var(--custom-combobox-color, var(--ion-color-primary)); --selected-item-color: var(--custom-selected-item-color, var(--core-color)); --selected-item-border-width: var(--custom-selected-item-border-width, 5px); From 7af5c510bf7e183ad20f3915024ea6a9904cacac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 4 May 2021 15:26:06 +0200 Subject: [PATCH 04/11] MOBILE-3745 core: Use new openModal function --- .../pages/discussion/discussion.page.ts | 14 +++----- .../classes/base-feedback-plugin-component.ts | 14 +++----- src/addons/mod/book/components/index/index.ts | 17 +++------- src/addons/mod/chat/pages/chat/chat.ts | 25 +++++--------- src/addons/mod/data/components/index/index.ts | 10 ++---- src/addons/mod/forum/components/post/post.ts | 21 +++++------- .../mod/imscp/components/index/index.ts | 16 ++------- .../mod/lesson/pages/player/player.page.ts | 11 +------ .../mod/lesson/services/handlers/prefetch.ts | 13 +++----- .../navigation-modal/navigation-modal.ts | 10 ++++-- .../mod/quiz/pages/player/player.page.ts | 16 +++------ .../mod/quiz/pages/review/review.page.ts | 22 ++++--------- src/addons/mod/quiz/services/quiz-helper.ts | 12 +++---- src/addons/mod/scorm/pages/player/player.ts | 16 ++------- src/addons/mod/wiki/components/index/index.ts | 25 +++++--------- src/addons/mod/wiki/components/map/map.ts | 9 +++-- .../mod/workshop/components/index/index.ts | 9 +++-- src/addons/notes/components/add/add-modal.ts | 13 +++++--- src/addons/notes/pages/list/list.page.ts | 27 +++++++-------- src/addons/notes/services/database/notes.ts | 3 +- src/addons/notes/services/notes-offline.ts | 13 ++++++-- src/addons/notes/services/notes.ts | 22 ++++++++++--- .../components/recaptcha/recaptcha-modal.ts | 7 +++- src/core/components/recaptcha/recaptcha.ts | 16 ++++----- .../comments/components/add/add-modal.ts | 2 +- .../comments/pages/viewer/viewer.page.ts | 14 +++----- .../services/contentlinks-helper.ts | 6 ++-- .../course/pages/preview/preview.page.ts | 14 +++----- .../self-enrol-password.ts | 8 ++--- src/core/features/login/pages/site/site.ts | 10 ++---- .../rating/components/aggregate/aggregate.ts | 6 ++-- .../services/sharedfiles-helper.ts | 9 ++--- src/core/services/utils/dom.ts | 33 +++++++++++++++---- src/core/services/utils/text.ts | 7 ++-- src/core/services/utils/utils.ts | 12 ++----- src/theme/theme.base.scss | 5 +++ 36 files changed, 211 insertions(+), 276 deletions(-) diff --git a/src/addons/messages/pages/discussion/discussion.page.ts b/src/addons/messages/pages/discussion/discussion.page.ts index 6717cb529..e901b3d72 100644 --- a/src/addons/messages/pages/discussion/discussion.page.ts +++ b/src/addons/messages/pages/discussion/discussion.page.ts @@ -39,7 +39,7 @@ import { Md5 } from 'ts-md5/dist/md5'; import moment from 'moment'; import { CoreAnimations } from '@components/animations'; import { CoreError } from '@classes/errors/error'; -import { ModalController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { CoreIonLoadingElement } from '@classes/ion-loading'; import { ActivatedRoute } from '@angular/router'; @@ -1302,18 +1302,14 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView async viewInfo(): Promise { if (this.isGroup) { // Display the group information. - const modal = await ModalController.create({ + const userId = await CoreDomUtils.openModal({ component: AddonMessagesConversationInfoComponent, componentProps: { conversationId: this.conversationId, }, }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (typeof result.data != 'undefined') { + if (typeof userId != 'undefined') { const splitViewLoaded = CoreNavigator.isCurrentPathInTablet('**/messages/**/discussion'); // Open user conversation. @@ -1321,12 +1317,12 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView // Notify the left pane to load it, this way the right conversation will be highlighted. CoreEvents.trigger( AddonMessagesProvider.OPEN_CONVERSATION_EVENT, - { userId: result.data }, + { userId }, this.siteId, ); } else { // Open the discussion in a new view. - CoreNavigator.navigateToSitePath('/messages/discussion', { params: { userId: result.data.userId } }); + CoreNavigator.navigateToSitePath('/messages/discussion', { params: { userId } }); } } } else { diff --git a/src/addons/mod/assign/classes/base-feedback-plugin-component.ts b/src/addons/mod/assign/classes/base-feedback-plugin-component.ts index 48eb2ef64..b44226f47 100644 --- a/src/addons/mod/assign/classes/base-feedback-plugin-component.ts +++ b/src/addons/mod/assign/classes/base-feedback-plugin-component.ts @@ -15,7 +15,7 @@ import { Component, Input } from '@angular/core'; import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreError } from '@classes/errors/error'; -import { ModalController } from '@singletons'; +import { CoreDomUtils } from '@services/utils/dom'; import { AddonModAssignEditFeedbackModalComponent } from '../components/edit-feedback-modal/edit-feedback-modal'; import { AddonModAssignFeedbackCommentsTextData } from '../feedback/comments/services/handler'; import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../services/assign'; @@ -47,7 +47,7 @@ export class AddonModAssignFeedbackPluginBaseComponent { } // Create the navigation modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: AddonModAssignEditFeedbackModalComponent, componentProps: { assign: this.assign, @@ -57,15 +57,11 @@ export class AddonModAssignFeedbackPluginBaseComponent { }, }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (typeof result.data == 'undefined') { + if (typeof modalData == 'undefined') { throw new CoreCanceledError(); // User cancelled. - } else { - return result.data; } + + return modalData; } /** diff --git a/src/addons/mod/book/components/index/index.ts b/src/addons/mod/book/components/index/index.ts index 929fda9a6..76c994b86 100644 --- a/src/addons/mod/book/components/index/index.ts +++ b/src/addons/mod/book/components/index/index.ts @@ -28,7 +28,7 @@ import { import { CoreTag, CoreTagItem } from '@features/tag/services/tag'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; -import { ModalController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreUtils } from '@services/utils/utils'; import { CoreCourse } from '@features/course/services/course'; import { AddonModBookTocComponent } from '../toc/toc'; @@ -84,7 +84,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp */ async showToc(): Promise { // Create the toc modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModBookTocComponent, componentProps: { moduleId: this.module.id, @@ -93,19 +93,10 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp courseId: this.courseId, book: this.book, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // @todo leaveAnimation: 'core-modal-lateral-transition', }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result.data) { - this.changeChapter(result.data); + if (modalData) { + this.changeChapter(modalData); } } diff --git a/src/addons/mod/chat/pages/chat/chat.ts b/src/addons/mod/chat/pages/chat/chat.ts index 1fe1d3d0e..b1fb66afe 100644 --- a/src/addons/mod/chat/pages/chat/chat.ts +++ b/src/addons/mod/chat/pages/chat/chat.ts @@ -21,7 +21,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController, Network, NgZone } from '@singletons'; +import { Network, NgZone } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; import { AddonModChatUsersModalComponent, AddonModChatUsersModalResult } from '../../components/users-modal/users-modal'; @@ -178,32 +178,23 @@ export class AddonModChatChatPage implements OnInit, OnDestroy { */ async showChatUsers(): Promise { // Create the toc modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModChatUsersModalComponent, componentProps: { sessionId: this.sessionId, cmId: this.cmId, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // @todo leaveAnimation: 'core-modal-lateral-transition', }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result.data) { - if (result.data.talkTo) { - this.newMessage = `To ${result.data.talkTo}: ` + (this.sendMessageForm?.message || ''); + if (modalData) { + if (modalData.talkTo) { + this.newMessage = `To ${modalData.talkTo}: ` + (this.sendMessageForm?.message || ''); } - if (result.data.beepTo) { - this.sendMessage('', result.data.beepTo); + if (modalData.beepTo) { + this.sendMessage('', modalData.beepTo); } - this.users = result.data.users; + this.users = modalData.users; } } diff --git a/src/addons/mod/data/components/index/index.ts b/src/addons/mod/data/components/index/index.ts index d7cb09504..74fc86100 100644 --- a/src/addons/mod/data/components/index/index.ts +++ b/src/addons/mod/data/components/index/index.ts @@ -29,7 +29,6 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModDataProvider, @@ -376,7 +375,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp * Display the chat users modal. */ async showSearch(): Promise { - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: AddonModDataSearchComponent, componentProps: { search: this.search, @@ -385,12 +384,9 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp }, }); - await modal.present(); - - const result = await modal.onDidDismiss(); // Add data to search object. - if (result.data) { - this.search = result.data; + if (modalData) { + this.search = modalData; this.searchEntries(0); } } diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index 7d174bb9b..b7285ae85 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -40,7 +40,7 @@ import { AddonModForumUpdateDiscussionPostWSOptionsObject, } from '../../services/forum'; import { CoreTag } from '@features/tag/services/tag'; -import { ModalController, PopoverController, Translate } from '@singletons'; +import { PopoverController, Translate } from '@singletons'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { IonContent } from '@ionic/angular'; import { AddonModForumSync } from '../../services/forum-sync'; @@ -254,7 +254,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges * Shows a form modal to edit an online post. */ async editPost(): Promise { - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: AddonModForumEditPostComponent, componentProps: { post: this.post, @@ -265,18 +265,13 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges backdropDismiss: false, }); - await modal.present(); - - const result = await modal.onDidDismiss(); - const data = result.data; - - if (!data) { + if (!modalData) { return; } // Add some HTML to the message if needed. - const message = CoreTextUtils.formatHtmlLines(data.message!); - const files = data.files; + const message = CoreTextUtils.formatHtmlLines(modalData.message!); + const files = modalData.files; const options: AddonModForumUpdateDiscussionPostWSOptionsObject = {}; const sendingModal = await CoreDomUtils.showModalLoading('core.sending', true); @@ -295,16 +290,16 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges } // Try to send it to server. - const sent = await AddonModForum.updatePost(this.post.id, data.subject!, message, options); + const sent = await AddonModForum.updatePost(this.post.id, modalData.subject!, message, options); if (sent && this.forum.id) { // Data sent to server, delete stored files (if any). AddonModForumHelper.deleteReplyStoredFiles(this.forum.id, this.post.id); this.onPostChange.emit(); - this.post.subject = data.subject!; + this.post.subject = modalData.subject!; this.post.message = message; - this.post.attachments = data.files; + this.post.attachments = modalData.files; } } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.mod_forum.couldnotupdate', true); diff --git a/src/addons/mod/imscp/components/index/index.ts b/src/addons/mod/imscp/components/index/index.ts index e71c5be51..83c8e976f 100644 --- a/src/addons/mod/imscp/components/index/index.ts +++ b/src/addons/mod/imscp/components/index/index.ts @@ -21,7 +21,6 @@ import { import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { CoreCourse } from '@features/course/services/course'; import { CoreDomUtils } from '@services/utils/dom'; -import { ModalController } from '@singletons'; import { AddonModImscpProvider, AddonModImscp, AddonModImscpTocItem } from '../../services/imscp'; import { AddonModImscpTocComponent } from '../toc/toc'; @@ -150,25 +149,16 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom */ async showToc(): Promise { // Create the toc modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModImscpTocComponent, componentProps: { items: this.items, selected: this.currentItem, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // @todo leaveAnimation: 'core-modal-lateral-transition', }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result.data) { - this.loadItem(result.data); + if (modalData) { + this.loadItem(modalData); } } diff --git a/src/addons/mod/lesson/pages/player/player.page.ts b/src/addons/mod/lesson/pages/player/player.page.ts index f030eb7f3..0f3d01787 100644 --- a/src/addons/mod/lesson/pages/player/player.page.ts +++ b/src/addons/mod/lesson/pages/player/player.page.ts @@ -762,22 +762,13 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { async showMenu(): Promise { this.menuShown = true; - const menuModal = await ModalController.create({ + await CoreDomUtils.openSideModal({ component: AddonModLessonMenuModalPage, componentProps: { pageInstance: this, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // leaveAnimation: 'core-modal-lateral-transition', }); - await menuModal.present(); - - await menuModal.onWillDismiss(); - this.menuShown = false; } diff --git a/src/addons/mod/lesson/services/handlers/prefetch.ts b/src/addons/mod/lesson/services/handlers/prefetch.ts index def77488d..c53114c00 100644 --- a/src/addons/mod/lesson/services/handlers/prefetch.ts +++ b/src/addons/mod/lesson/services/handlers/prefetch.ts @@ -22,9 +22,10 @@ import { CoreFilepool } from '@services/filepool'; import { CoreGroups } from '@services/groups'; import { CoreFileSizeSum, CorePluginFileDelegate } from '@services/plugin-file-delegate'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { CoreWSFile } from '@services/ws'; -import { makeSingleton, ModalController, Translate } from '@singletons'; +import { makeSingleton, Translate } from '@singletons'; import { AddonModLessonPasswordModalComponent } from '../../components/password-modal/password-modal'; import { AddonModLesson, @@ -54,19 +55,15 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref */ protected async askUserPassword(): Promise { // Create and show the modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: AddonModLessonPasswordModalComponent, }); - await modal.present(); - - const result = await modal.onWillDismiss(); - - if (typeof result.data != 'string') { + if (typeof modalData != 'string') { throw new CoreCanceledError(); } - return result.data; + return modalData; } /** diff --git a/src/addons/mod/quiz/components/navigation-modal/navigation-modal.ts b/src/addons/mod/quiz/components/navigation-modal/navigation-modal.ts index f8ae64c15..92c958c0a 100644 --- a/src/addons/mod/quiz/components/navigation-modal/navigation-modal.ts +++ b/src/addons/mod/quiz/components/navigation-modal/navigation-modal.ts @@ -50,7 +50,7 @@ export class AddonModQuizNavigationModalComponent { * @param slot Slot of the question to scroll to. */ loadPage(page: number, slot?: number): void { - ModalController.dismiss({ + ModalController.dismiss({ action: AddonModQuizNavigationModalComponent.CHANGE_PAGE, page, slot, @@ -61,7 +61,7 @@ export class AddonModQuizNavigationModalComponent { * Switch mode in review. */ switchMode(): void { - ModalController.dismiss({ + ModalController.dismiss({ action: AddonModQuizNavigationModalComponent.SWITCH_MODE, }); } @@ -74,3 +74,9 @@ export class AddonModQuizNavigationModalComponent { export type AddonModQuizNavigationQuestion = CoreQuestionQuestionParsed & { stateClass?: string; }; + +export type AddonModQuizNavigationModalReturn = { + action: number; + page?: number; + slot?: number; +}; diff --git a/src/addons/mod/quiz/pages/player/player.page.ts b/src/addons/mod/quiz/pages/player/player.page.ts index f6925f43f..9d213fe40 100644 --- a/src/addons/mod/quiz/pages/player/player.page.ts +++ b/src/addons/mod/quiz/pages/player/player.page.ts @@ -31,6 +31,7 @@ import { CoreEvents } from '@singletons/events'; import { AddonModQuizAutoSave } from '../../classes/auto-save'; import { AddonModQuizNavigationModalComponent, + AddonModQuizNavigationModalReturn, AddonModQuizNavigationQuestion, } from '../../components/navigation-modal/navigation-modal'; import { @@ -581,7 +582,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { } // Create the navigation modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModQuizNavigationModalComponent, componentProps: { navigation: this.navigation, @@ -589,19 +590,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { currentPage: this.attempt?.currentpage, isReview: false, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // @todo leaveAnimation: 'core-modal-lateral-transition', }); - await modal.present(); - - const result = await modal.onWillDismiss(); - - if (result.data && result.data.action == AddonModQuizNavigationModalComponent.CHANGE_PAGE) { - this.changePage(result.data.page, true, result.data.slot); + if (modalData && modalData.action == AddonModQuizNavigationModalComponent.CHANGE_PAGE) { + this.changePage(modalData.page!, true, modalData.slot); } } diff --git a/src/addons/mod/quiz/pages/review/review.page.ts b/src/addons/mod/quiz/pages/review/review.page.ts index 0b00bf887..bc7ba288a 100644 --- a/src/addons/mod/quiz/pages/review/review.page.ts +++ b/src/addons/mod/quiz/pages/review/review.page.ts @@ -21,9 +21,10 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { AddonModQuizNavigationModalComponent, + AddonModQuizNavigationModalReturn, AddonModQuizNavigationQuestion, } from '../../components/navigation-modal/navigation-modal'; import { @@ -325,7 +326,7 @@ export class AddonModQuizReviewPage implements OnInit { async openNavigation(): Promise { // Create the navigation modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModQuizNavigationModalComponent, componentProps: { navigation: this.navigation, @@ -335,24 +336,15 @@ export class AddonModQuizReviewPage implements OnInit { numPages: this.numPages, showAll: this.showAll, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // @todo leaveAnimation: 'core-modal-lateral-transition', }); - await modal.present(); - - const result = await modal.onWillDismiss(); - - if (!result.data) { + if (!modalData) { return; } - if (result.data.action == AddonModQuizNavigationModalComponent.CHANGE_PAGE) { - this.changePage(result.data.page, true, result.data.slot); - } else if (result.data.action == AddonModQuizNavigationModalComponent.SWITCH_MODE) { + if (modalData.action == AddonModQuizNavigationModalComponent.CHANGE_PAGE) { + this.changePage(modalData.page!, true, modalData.slot); + } else if (modalData.action == AddonModQuizNavigationModalComponent.SWITCH_MODE) { this.switchMode(); } } diff --git a/src/addons/mod/quiz/services/quiz-helper.ts b/src/addons/mod/quiz/services/quiz-helper.ts index d167e9653..f84912f9a 100644 --- a/src/addons/mod/quiz/services/quiz-helper.ts +++ b/src/addons/mod/quiz/services/quiz-helper.ts @@ -21,7 +21,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; -import { makeSingleton, ModalController, Translate } from '@singletons'; +import { makeSingleton, Translate } from '@singletons'; import { AddonModQuizPreflightModalComponent } from '../components/preflight-modal/preflight-modal'; import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; import { AddonModQuizModuleHandlerService } from './handlers/module'; @@ -160,7 +160,7 @@ export class AddonModQuizHelperProvider { } // Create and show the modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal>({ component: AddonModQuizPreflightModalComponent, componentProps: { title: title, @@ -172,15 +172,11 @@ export class AddonModQuizHelperProvider { }, }); - await modal.present(); - - const result = await modal.onWillDismiss(); - - if (!result.data) { + if (!modalData) { throw new CoreCanceledError(); } - return > result.data; + return modalData; } /** diff --git a/src/addons/mod/scorm/pages/player/player.ts b/src/addons/mod/scorm/pages/player/player.ts index 6a351d21f..382e06674 100644 --- a/src/addons/mod/scorm/pages/player/player.ts +++ b/src/addons/mod/scorm/pages/player/player.ts @@ -20,7 +20,6 @@ import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModScormDataModel12 } from '../../classes/data-model-12'; import { AddonModScormTocComponent } from '../../components/toc/toc'; @@ -486,7 +485,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { * Show the TOC. */ async openToc(): Promise { - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModScormTocComponent, componentProps: { toc: this.toc, @@ -497,19 +496,10 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { accessInfo: this.accessInfo, mode: this.mode, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // leaveAnimation: 'core-modal-lateral-transition' }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result.data) { - this.loadSco(result.data); + if (modalData) { + this.loadSco(modalData); } } diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index e0937ee6c..8803e9840 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -27,7 +27,7 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController, PopoverController, Translate } from '@singletons'; +import { PopoverController, Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Md5 } from 'ts-md5'; import { AddonModWikiPageDBRecord } from '../../services/database/wiki'; @@ -51,7 +51,7 @@ import { AddonModWikiSyncWikiResult, AddonModWikiSyncWikiSubwiki, } from '../../services/wiki-sync'; -import { AddonModWikiMapModalComponent } from '../map/map'; +import { AddonModWikiMapModalComponent, AddonModWikiMapModalReturn } from '../map/map'; import { AddonModWikiSubwikiPickerComponent } from '../subwiki-picker/subwiki-picker'; /** @@ -576,7 +576,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp */ async openMap(): Promise { // Create the toc modal. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openSideModal({ component: AddonModWikiMapModalComponent, componentProps: { pages: this.subwikiPages, @@ -585,23 +585,14 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp courseId: this.courseId, selectedTitle: this.currentPageObj && this.currentPageObj.title, }, - cssClass: 'core-modal-lateral', - showBackdrop: true, - backdropDismiss: true, - // @todo enterAnimation: 'core-modal-lateral-transition', - // @todo leaveAnimation: 'core-modal-lateral-transition', }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result.data) { - if (result.data.type == 'home') { + if (modalData) { + if (modalData.home) { // Go back to the initial page of the wiki. - CoreNavigator.navigateToSitePath(result.data.goto); - } else { - this.goToPage(result.data.goto); + CoreNavigator.navigateToSitePath(modalData.home); + } else if (modalData.page) { + this.goToPage(modalData.page); } } diff --git a/src/addons/mod/wiki/components/map/map.ts b/src/addons/mod/wiki/components/map/map.ts index f4127c631..8e2a03f7c 100644 --- a/src/addons/mod/wiki/components/map/map.ts +++ b/src/addons/mod/wiki/components/map/map.ts @@ -47,14 +47,14 @@ export class AddonModWikiMapModalComponent implements OnInit { * @param page Clicked page. */ goToPage(page: AddonModWikiSubwikiPage | AddonModWikiPageDBRecord): void { - ModalController.dismiss({ type: 'page', goto: page }); + ModalController.dismiss({ page }); } /** * Go back to the initial page of the wiki. */ goToWikiHome(): void { - ModalController.dismiss({ type: 'home', goto: this.homeView }); + ModalController.dismiss({ home: this.homeView }); } /** @@ -103,3 +103,8 @@ type AddonModWikiPagesMapLetter = { label: string; pages: (AddonModWikiSubwikiPage | AddonModWikiPageDBRecord)[]; }; + +export type AddonModWikiMapModalReturn = { + page?: AddonModWikiSubwikiPage | AddonModWikiPageDBRecord; + home?: string; +}; diff --git a/src/addons/mod/workshop/components/index/index.ts b/src/addons/mod/workshop/components/index/index.ts index cacda71fd..4f3717f4a 100644 --- a/src/addons/mod/workshop/components/index/index.ts +++ b/src/addons/mod/workshop/components/index/index.ts @@ -20,8 +20,9 @@ import { CoreCourse } from '@features/course/services/course'; import { IonContent } from '@ionic/angular'; import { CoreGroupInfo, CoreGroups } from '@services/groups'; import { CoreNavigator } from '@services/navigator'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController, Platform } from '@singletons'; +import { Platform } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; import { AddonModWorkshopModuleHandlerService } from '../../services/handlers/module'; @@ -387,7 +388,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity */ async viewPhaseInfo(): Promise { if (this.phases) { - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: AddonModWorkshopPhaseInfoComponent, componentProps: { phases: CoreUtils.objectToArray(this.phases), @@ -396,10 +397,8 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity showSubmit: this.showSubmit, }, }); - await modal.present(); - const result = await modal.onDidDismiss(); - if (result.data === true) { + if (modalData === true) { this.gotoSubmit(); } } diff --git a/src/addons/notes/components/add/add-modal.ts b/src/addons/notes/components/add/add-modal.ts index d784b1605..8583ce871 100644 --- a/src/addons/notes/components/add/add-modal.ts +++ b/src/addons/notes/components/add/add-modal.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { AddonNotes } from '@addons/notes/services/notes'; +import { AddonNotes, AddonNotesPublishState } from '@addons/notes/services/notes'; import { Component, ViewChild, ElementRef, Input } from '@angular/core'; import { CoreApp } from '@services/app'; import { CoreSites } from '@services/sites'; @@ -32,7 +32,7 @@ export class AddonNotesAddComponent { @Input() protected courseId!: number; @Input() protected userId?: number; - @Input() type = 'personal'; + @Input() type: AddonNotesPublishState = 'personal'; text = ''; processing = false; @@ -56,7 +56,7 @@ export class AddonNotesAddComponent { CoreForms.triggerFormSubmittedEvent(this.formElement, sent, CoreSites.getCurrentSiteId()); - ModalController.dismiss({ type: this.type, sent: true }).finally(() => { + ModalController.dismiss({ type: this.type, sent: true }).finally(() => { CoreDomUtils.showToast(sent ? 'addon.notes.eventnotecreated' : 'core.datastoredoffline', true, 3000); }); } catch (error){ @@ -73,7 +73,12 @@ export class AddonNotesAddComponent { closeModal(): void { CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); - ModalController.dismiss({ type: this.type }); + ModalController.dismiss({ type: this.type }); } } + +export type AddonNotesAddModalReturn = { + type: AddonNotesPublishState; + sent?: boolean; +}; diff --git a/src/addons/notes/pages/list/list.page.ts b/src/addons/notes/pages/list/list.page.ts index e4385b156..beed23871 100644 --- a/src/addons/notes/pages/list/list.page.ts +++ b/src/addons/notes/pages/list/list.page.ts @@ -13,8 +13,8 @@ // limitations under the License. import { CoreConstants } from '@/core/constants'; -import { AddonNotesAddComponent } from '@addons/notes/components/add/add-modal'; -import { AddonNotes, AddonNotesNoteFormatted } from '@addons/notes/services/notes'; +import { AddonNotesAddComponent, AddonNotesAddModalReturn } from '@addons/notes/components/add/add-modal'; +import { AddonNotes, AddonNotesNoteFormatted, AddonNotesPublishState } from '@addons/notes/services/notes'; import { AddonNotesOffline } from '@addons/notes/services/notes-offline'; import { AddonNotesSync, AddonNotesSyncProvider } from '@addons/notes/services/notes-sync'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; @@ -26,7 +26,6 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreUtils } from '@services/utils/utils'; -import { ModalController } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; /** @@ -43,7 +42,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { courseId: number; userId?: number; - type = 'course'; + type: AddonNotesPublishState = 'course'; refreshIcon = CoreConstants.ICON_LOADING; syncIcon = CoreConstants.ICON_LOADING; notes: AddonNotesNoteFormatted[] = []; @@ -160,7 +159,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { * * @param type New type. */ - async typeChanged(type: string): Promise { + async typeChanged(type: AddonNotesPublishState): Promise { this.type = type; this.notesLoaded = false; this.refreshIcon = CoreConstants.ICON_LOADING; @@ -179,7 +178,7 @@ export class AddonNotesListPage implements OnInit, OnDestroy { e.preventDefault(); e.stopPropagation(); - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: AddonNotesAddComponent, componentProps: { userId: this.userId, @@ -188,21 +187,17 @@ export class AddonNotesListPage implements OnInit, OnDestroy { }, }); - await modal.present(); + if (typeof modalData != 'undefined') { - const result = await modal.onDidDismiss(); - - if (typeof result.data != 'undefined') { - - if (result.data.sent && result.data.type) { - if (result.data.type != this.type) { - this.type = result.data.type; + if (modalData.sent && modalData.type) { + if (modalData.type != this.type) { + this.type = modalData.type; this.notesLoaded = false; } this.refreshNotes(false); - } else if (result.data.type && result.data.type != this.type) { - this.typeChanged(result.data.type); + } else if (modalData.type && modalData.type != this.type) { + this.typeChanged(modalData.type); } } } diff --git a/src/addons/notes/services/database/notes.ts b/src/addons/notes/services/database/notes.ts index ed0a69ae9..f22d482df 100644 --- a/src/addons/notes/services/database/notes.ts +++ b/src/addons/notes/services/database/notes.ts @@ -13,6 +13,7 @@ // limitations under the License. import { CoreSiteSchema } from '@services/sites'; +import { AddonNotesPublishState } from '../notes'; /** * Database variables for AddonNotesOfflineProvider. @@ -83,7 +84,7 @@ export type AddonNotesDBRecord = { content: string; // Primary key. created: number; // Primary key. courseid: number; - publishstate: string; + publishstate: AddonNotesPublishState; format: number; lastmodified: number; }; diff --git a/src/addons/notes/services/notes-offline.ts b/src/addons/notes/services/notes-offline.ts index 9968e07f7..e4a790d60 100644 --- a/src/addons/notes/services/notes-offline.ts +++ b/src/addons/notes/services/notes-offline.ts @@ -17,6 +17,7 @@ import { CoreSites } from '@services/sites'; import { CoreTimeUtils } from '@services/utils/time'; import { makeSingleton } from '@singletons'; import { AddonNotesDBRecord, AddonNotesDeletedDBRecord, NOTES_DELETED_TABLE, NOTES_TABLE } from './database/notes'; +import { AddonNotesPublishState } from './notes'; /** * Service to handle offline notes. @@ -150,7 +151,7 @@ export class AddonNotesOfflineProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved with notes. */ - async getNotesWithPublishState(state: string, siteId?: string): Promise { + async getNotesWithPublishState(state: AddonNotesPublishState, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); return await site.getDb().getRecords(NOTES_TABLE, { publishstate: state }); @@ -189,7 +190,7 @@ export class AddonNotesOfflineProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved with boolean: true if has offline notes, false otherwise. */ - async hasNotesWithPublishState(state: string, siteId?: string): Promise { + async hasNotesWithPublishState(state: AddonNotesPublishState, siteId?: string): Promise { const notes = await this.getNotesWithPublishState(state, siteId); return !!notes.length; @@ -205,7 +206,13 @@ export class AddonNotesOfflineProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved if stored, rejected if failure. */ - async saveNote(userId: number, courseId: number, state: string, content: string, siteId?: string): Promise { + async saveNote( + userId: number, + courseId: number, + state: AddonNotesPublishState, + content: string, + siteId?: string, + ): Promise { const site = await CoreSites.getSite(siteId); const now = CoreTimeUtils.timestamp(); diff --git a/src/addons/notes/services/notes.ts b/src/addons/notes/services/notes.ts index 7f5855bf9..7d6a05515 100644 --- a/src/addons/notes/services/notes.ts +++ b/src/addons/notes/services/notes.ts @@ -42,7 +42,13 @@ export class AddonNotesProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved with boolean: true if note was sent to server, false if stored in device. */ - async addNote(userId: number, courseId: number, publishState: string, noteText: string, siteId?: string): Promise { + async addNote( + userId: number, + courseId: number, + publishState: AddonNotesPublishState, + noteText: string, + siteId?: string, + ): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); // Convenience function to store a note to be synchronized later. @@ -82,7 +88,13 @@ export class AddonNotesProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved when added, rejected otherwise. */ - async addNoteOnline(userId: number, courseId: number, publishState: string, noteText: string, siteId?: string): Promise { + async addNoteOnline( + userId: number, + courseId: number, + publishState: AddonNotesPublishState, + noteText: string, + siteId?: string, + ): Promise { const notes: AddonNotesCreateNoteData[] = [ { courseid: courseId, @@ -438,7 +450,7 @@ export type AddonNotesNote = { created: number; // Time created (timestamp). lastmodified: number; // Time of last modification (timestamp). usermodified: number; // User id of the creator of this note. - publishstate: string; // State of the note (i.e. draft, public, site). + publishstate: AddonNotesPublishState; // State of the note (i.e. draft, public, site). offline?: boolean; }; @@ -474,7 +486,7 @@ export type AddonNotesNoteFormatted = AddonNotesNote & { export type AddonNotesCreateNoteData = { userid: number; // Id of the user the note is about. - publishstate: string; // 'personal', 'course' or 'site'. + publishstate: AddonNotesPublishState; // 'personal', 'course' or 'site'. courseid: number; // Course id of the note (in Moodle a note can only be created into a course, // even for site and personal notes). text: string; // The text of the message - text or HTML. @@ -504,3 +516,5 @@ export type AddonNotesCreateNotesWSResponse = { type AddonNotesDeleteNotesWSParams = { notes: number[]; // Array of Note Ids to be deleted. }; + +export type AddonNotesPublishState = 'personal' | 'site' | 'course'; diff --git a/src/core/components/recaptcha/recaptcha-modal.ts b/src/core/components/recaptcha/recaptcha-modal.ts index 103731e99..0cb9215cf 100644 --- a/src/core/components/recaptcha/recaptcha-modal.ts +++ b/src/core/components/recaptcha/recaptcha-modal.ts @@ -42,7 +42,7 @@ export class CoreRecaptchaModalComponent implements OnDestroy { * Close modal. */ closeModal(): void { - ModalController.dismiss({ + ModalController.dismiss({ expired: this.expired, value: this.value, }); @@ -119,3 +119,8 @@ export class CoreRecaptchaModalComponent implements OnDestroy { } } + +export type CoreRecaptchaModalReturn = { + expired: boolean; + value: string; +}; diff --git a/src/core/components/recaptcha/recaptcha.ts b/src/core/components/recaptcha/recaptcha.ts index ad9ae50c8..7d58839d4 100644 --- a/src/core/components/recaptcha/recaptcha.ts +++ b/src/core/components/recaptcha/recaptcha.ts @@ -16,9 +16,9 @@ import { Component, Input, OnInit } from '@angular/core'; import { CoreLang } from '@services/lang'; import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; -import { ModalController } from '@singletons'; -import { CoreRecaptchaModalComponent } from './recaptcha-modal'; +import { CoreRecaptchaModalComponent, CoreRecaptchaModalReturn } from './recaptcha-modal'; /** * Component that allows answering a recaptcha. @@ -66,7 +66,7 @@ export class CoreRecaptchaComponent implements OnInit { // Modal to answer the recaptcha. // This is because the size of the recaptcha is dynamic, so it could cause problems if it was displayed inline. - const modal = await ModalController.create({ + const modalData = await CoreDomUtils.openModal({ component: CoreRecaptchaModalComponent, cssClass: 'core-modal-fullscreen', componentProps: { @@ -74,12 +74,10 @@ export class CoreRecaptchaComponent implements OnInit { }, }); - await modal.present(); - - const result = await modal.onWillDismiss(); - - this.expired = result.data.expired; - this.model![this.modelValueName] = result.data.value; + if (modalData) { + this.expired = modalData.expired; + this.model![this.modelValueName] = modalData.value; + } } } diff --git a/src/core/features/comments/components/add/add-modal.ts b/src/core/features/comments/components/add/add-modal.ts index 31a5db47c..5d1a0a224 100644 --- a/src/core/features/comments/components/add/add-modal.ts +++ b/src/core/features/comments/components/add/add-modal.ts @@ -69,7 +69,7 @@ export class CoreCommentsAddComponent { CoreSites.getCurrentSiteId(), ); - ModalController.dismiss({ comment: commentsResponse }).finally(() => { + ModalController.dismiss(commentsResponse).finally(() => { CoreDomUtils.showToast( commentsResponse ? 'core.comments.eventcommentcreated' : 'core.datastoredoffline', true, diff --git a/src/core/features/comments/pages/viewer/viewer.page.ts b/src/core/features/comments/pages/viewer/viewer.page.ts index 98f33c31a..9ae521910 100644 --- a/src/core/features/comments/pages/viewer/viewer.page.ts +++ b/src/core/features/comments/pages/viewer/viewer.page.ts @@ -30,7 +30,7 @@ import { import { IonContent, IonRefresher } from '@ionic/angular'; import { ContextLevel, CoreConstants } from '@/core/constants'; import { CoreNavigator } from '@services/navigator'; -import { ModalController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreUtils } from '@services/utils/utils'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUser, CoreUserProfile } from '@features/user/services/user'; @@ -279,19 +279,15 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { content: this.offlineComment ? this.offlineComment!.content : '', }; - const modal = await ModalController.create({ + const comment = await CoreDomUtils.openModal({ component: CoreCommentsAddComponent, componentProps: params, }); - await modal.present(); - - const result = await modal.onDidDismiss(); - - if (result?.data?.comment) { + if (comment) { this.invalidateComments(); - const addedComments = await this.loadCommentProfile(result.data.comment); + const addedComments = await this.loadCommentProfile(comment); // Add the comment to the top. this.comments = [addedComments].concat(this.comments); this.canDeleteComments = this.addDeleteCommentsAvailable; @@ -305,7 +301,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { countChange: 1, }, CoreSites.getCurrentSiteId()); - } else if (result?.data?.comment === false) { + } else if (comment === false) { // Comments added in offline mode. return this.loadOfflineData(); } diff --git a/src/core/features/contentlinks/services/contentlinks-helper.ts b/src/core/features/contentlinks/services/contentlinks-helper.ts index feb965da8..8c5ccd06c 100644 --- a/src/core/features/contentlinks/services/contentlinks-helper.ts +++ b/src/core/features/contentlinks/services/contentlinks-helper.ts @@ -17,7 +17,7 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreContentLinksDelegate, CoreContentLinksAction } from './contentlinks-delegate'; import { CoreSite } from '@classes/site'; -import { makeSingleton, ModalController, Translate } from '@singletons'; +import { makeSingleton, Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { Params } from '@angular/router'; import { CoreContentLinksChooseSiteModalComponent } from '../components/choose-site-modal/choose-site-modal'; @@ -112,15 +112,13 @@ export class CoreContentLinksHelperProvider { * @todo set correct root. */ async goToChooseSite(url: string): Promise { - const modal = await ModalController.create({ + await CoreDomUtils.openModal({ component: CoreContentLinksChooseSiteModalComponent, componentProps: { url: url, }, cssClass: 'core-modal-fullscreen', }); - - await modal.present(); } /** diff --git a/src/core/features/course/pages/preview/preview.page.ts b/src/core/features/course/pages/preview/preview.page.ts index 286ca4b25..5b2dfc9c2 100644 --- a/src/core/features/course/pages/preview/preview.page.ts +++ b/src/core/features/course/pages/preview/preview.page.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnDestroy, NgZone, OnInit } from '@angular/core'; -import { ModalController, IonRefresher } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { CoreApp } from '@services/app'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; @@ -76,7 +76,6 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy { protected courseStatusObserver?: CoreEventObserver; constructor( - protected modalCtrl: ModalController, protected zone: NgZone, ) { this.isMobile = CoreApp.isMobile(); @@ -347,19 +346,16 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy { if (error && error.errorcode === CoreCoursesProvider.ENROL_INVALID_KEY) { // Initialize the self enrol modal. - const selfEnrolModal = await this.modalCtrl.create( + // Invalid password, show the modal to enter the password. + const modalData = await CoreDomUtils.openModal( { component: CoreCoursesSelfEnrolPasswordComponent, componentProps: { password }, }, ); - // Invalid password, show the modal to enter the password. - await selfEnrolModal.present(); - - const data = await selfEnrolModal.onDidDismiss(); - if (typeof data?.data != 'undefined') { - this.selfEnrolInCourse(data.data, instanceId); + if (typeof modalData != 'undefined') { + this.selfEnrolInCourse(modalData, instanceId); return; } diff --git a/src/core/features/courses/components/self-enrol-password/self-enrol-password.ts b/src/core/features/courses/components/self-enrol-password/self-enrol-password.ts index de7e30a90..7bcd101e4 100644 --- a/src/core/features/courses/components/self-enrol-password/self-enrol-password.ts +++ b/src/core/features/courses/components/self-enrol-password/self-enrol-password.ts @@ -13,8 +13,9 @@ // limitations under the License. import { Component, ViewChild, ElementRef } from '@angular/core'; -import { ModalController, NavParams } from '@ionic/angular'; +import { NavParams } from '@ionic/angular'; import { CoreSites } from '@services/sites'; +import { ModalController } from '@singletons'; import { CoreForms } from '@singletons/form'; /** @@ -30,7 +31,6 @@ export class CoreCoursesSelfEnrolPasswordComponent { password = ''; constructor( - protected modalCtrl: ModalController, navParams: NavParams, ) { this.password = navParams.get('password') || ''; @@ -42,7 +42,7 @@ export class CoreCoursesSelfEnrolPasswordComponent { close(): void { CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); - this.modalCtrl.dismiss(); + ModalController.dismiss(); } /** @@ -57,7 +57,7 @@ export class CoreCoursesSelfEnrolPasswordComponent { CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId()); - this.modalCtrl.dismiss(this.password); + ModalController.dismiss(this.password); } } diff --git a/src/core/features/login/pages/site/site.ts b/src/core/features/login/pages/site/site.ts index 7f94f447c..88ee2c599 100644 --- a/src/core/features/login/pages/site/site.ts +++ b/src/core/features/login/pages/site/site.ts @@ -24,7 +24,7 @@ import { CoreLoginHelper, CoreLoginHelperProvider } from '@features/login/servic import { CoreSite } from '@classes/site'; import { CoreError } from '@classes/errors/error'; import { CoreConstants } from '@/core/constants'; -import { Translate, ModalController } from '@singletons'; +import { Translate } from '@singletons'; import { CoreUrl } from '@singletons/url'; import { CoreUrlUtils } from '@services/utils/url'; import { CoreLoginSiteHelpComponent } from '@features/login/components/site-help/site-help'; @@ -215,24 +215,20 @@ export class CoreLoginSitePage implements OnInit { * Show a help modal. */ async showHelp(): Promise { - const modal = await ModalController.create({ + await CoreDomUtils.openModal({ component: CoreLoginSiteHelpComponent, cssClass: 'core-modal-fullscreen', }); - - await modal.present(); } /** * Show an onboarding modal. */ async showOnboarding(): Promise { - const modal = await ModalController.create({ + await CoreDomUtils.openModal({ component: CoreLoginSiteOnboardingComponent, cssClass: 'core-modal-fullscreen', }); - - await modal.present(); } /** diff --git a/src/core/features/rating/components/aggregate/aggregate.ts b/src/core/features/rating/components/aggregate/aggregate.ts index c6b572938..77e34bda1 100644 --- a/src/core/features/rating/components/aggregate/aggregate.ts +++ b/src/core/features/rating/components/aggregate/aggregate.ts @@ -21,7 +21,7 @@ import { CoreRatingProvider, } from '@features/rating/services/rating'; import { CoreSites } from '@services/sites'; -import { ModalController } from '@singletons'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreRatingRatingsComponent } from '../ratings/ratings'; @@ -117,7 +117,7 @@ export class CoreRatingAggregateComponent implements OnChanges, OnDestroy { return; } - const modal = await ModalController.create({ + await CoreDomUtils.openModal({ component: CoreRatingRatingsComponent, componentProps: { contextLevel: this.contextLevel, @@ -129,8 +129,6 @@ export class CoreRatingAggregateComponent implements OnChanges, OnDestroy { courseId: this.courseId, }, }); - - await modal.present(); } /** diff --git a/src/core/features/sharedfiles/services/sharedfiles-helper.ts b/src/core/features/sharedfiles/services/sharedfiles-helper.ts index 6b6f86230..1deb69771 100644 --- a/src/core/features/sharedfiles/services/sharedfiles-helper.ts +++ b/src/core/features/sharedfiles/services/sharedfiles-helper.ts @@ -23,7 +23,7 @@ import { CoreFile } from '@services/file'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; -import { AlertController, ApplicationInit, makeSingleton, ModalController, Platform, Translate } from '@singletons'; +import { AlertController, ApplicationInit, makeSingleton, Platform, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; import { CoreSharedFilesListModalComponent } from '../components/list-modal/list-modal'; @@ -150,17 +150,12 @@ export class CoreSharedFilesHelperProvider { * @return Promise resolved when a file is picked, rejected if file picker is closed without selecting a file. */ async pickSharedFile(mimetypes?: string[]): Promise { - const modal = await ModalController.create({ + const file = await CoreDomUtils.openModal({ component: CoreSharedFilesListModalComponent, cssClass: 'core-modal-fullscreen', componentProps: { mimetypes, pick: true }, }); - await modal.present(); - - const result = await modal.onDidDismiss(); - const file: FileEntry | undefined = result.data; - if (!file) { // User cancelled. throw new CoreCanceledError(); diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 041867c16..13c3e4a91 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -1684,23 +1684,43 @@ export class CoreDomUtilsProvider { /** * Opens a Modal. * - * @param opts Modal Options. + * @param modalOptions Modal Options. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async openModal( - opts: ModalOptions, + async openModal( + modalOptions: ModalOptions, ): Promise { - const modal = await ModalController.create(opts); + const modal = await ModalController.create(modalOptions); await modal.present(); + // If onDidDismiss is nedded we can add a new param to the function to wait one function or the other. const result = await modal.onWillDismiss(); if (result?.data) { return result?.data; } } + /** + * Opens a side Modal. + * + * @param modalOptions Modal Options. + */ + async openSideModal( + modalOptions: ModalOptions, + ): Promise { + + modalOptions = Object.assign(modalOptions, { + cssClass: 'core-modal-lateral', + showBackdrop: true, + backdropDismiss: true, + // @todo enterAnimation: 'core-modal-lateral-transition', + // @todo leaveAnimation: 'core-modal-lateral-transition', + }); + + return await this.openModal(modalOptions); + } + /** * View an image in a modal. * @@ -1721,7 +1741,7 @@ export class CoreDomUtilsProvider { return; } - const modal = await ModalController.create({ + await CoreDomUtils.openModal({ component: CoreViewerImageComponent, componentProps: { title, @@ -1732,7 +1752,6 @@ export class CoreDomUtilsProvider { cssClass: fullScreen ? 'core-modal-fullscreen' : '', }); - await modal.present(); } /** diff --git a/src/core/services/utils/text.ts b/src/core/services/utils/text.ts index 50db71836..310e12b3b 100644 --- a/src/core/services/utils/text.ts +++ b/src/core/services/utils/text.ts @@ -19,11 +19,12 @@ import { ModalOptions } from '@ionic/core'; import { CoreApp } from '@services/app'; import { CoreLang } from '@services/lang'; import { CoreAnyError, CoreError } from '@classes/errors/error'; -import { makeSingleton, ModalController, Translate } from '@singletons'; +import { makeSingleton, Translate } from '@singletons'; import { CoreWSFile } from '@services/ws'; import { Locutus } from '@singletons/locutus'; import { CoreViewerTextComponent } from '@features/viewer/components/text/text'; import { CoreFileHelper } from '@services/file-helper'; +import { CoreDomUtils } from './dom'; /** * Different type of errors the app can treat. @@ -1097,9 +1098,7 @@ export class CoreTextUtilsProvider { ...options, }; - const modal = await ModalController.create(modalOptions); - - await modal.present(); + await CoreDomUtils.openModal(modalOptions); } } diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index 75e68885f..da8c27fd7 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -26,7 +26,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreTextUtils } from '@services/utils/text'; import { CoreWSError } from '@classes/errors/wserror'; -import { makeSingleton, Clipboard, InAppBrowser, FileOpener, WebIntent, QRScanner, Translate, ModalController } from '@singletons'; +import { makeSingleton, Clipboard, InAppBrowser, FileOpener, WebIntent, QRScanner, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreFileSizeSum } from '@services/plugin-file-delegate'; import { CoreViewerQRScannerComponent } from '@features/viewer/components/qr-scanner/qr-scanner'; @@ -1515,20 +1515,14 @@ export class CoreUtilsProvider { * @param title Title of the modal. Defaults to "QR reader". * @return Promise resolved with the captured text or undefined if cancelled or error. */ - async scanQR(title?: string): Promise { - const modal = await ModalController.create({ + async scanQR(title?: string): Promise { + return await CoreDomUtils.openModal({ component: CoreViewerQRScannerComponent, cssClass: 'core-modal-fullscreen', componentProps: { title, }, }); - - await modal.present(); - - const result = await modal.onWillDismiss(); - - return result.data; } /** diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 5dcf0cd2b..0ab2ab4b8 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -50,6 +50,11 @@ ion-item.ion-text-wrap ion-label { white-space: normal !important; } +// Buttons. +.button-clear { + --ion-color-primary: var(--ion-text-color); +} + // It fixes the click on links where ion-ripple-effect is present. .ion-activatable ion-label, .item-multiple-items ion-label { From 43ed1d9917fbfe40367f2524d4aac61631eaa170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 4 May 2021 15:57:31 +0200 Subject: [PATCH 05/11] MOBILE-3745 core: Add new openPopover function --- src/addons/calendar/pages/day/day.page.ts | 5 +- src/addons/calendar/pages/index/index.page.ts | 5 +- src/addons/calendar/pages/list/list.page.ts | 5 +- .../mod/forum/components/index/index.ts | 18 +++--- src/addons/mod/forum/components/post/post.ts | 12 ++-- .../mod/glossary/components/index/index.ts | 9 +-- src/addons/mod/quiz/classes/auto-save.ts | 7 +-- src/addons/mod/wiki/components/index/index.ts | 12 ++-- .../components/context-menu/context-menu.ts | 31 +++++----- .../course-progress/course-progress.ts | 59 +++++++++---------- src/core/services/utils/dom.ts | 25 +++++++- 11 files changed, 88 insertions(+), 100 deletions(-) diff --git a/src/addons/calendar/pages/day/day.page.ts b/src/addons/calendar/pages/day/day.page.ts index 15f89e3ee..6bb227438 100644 --- a/src/addons/calendar/pages/day/day.page.ts +++ b/src/addons/calendar/pages/day/day.page.ts @@ -34,7 +34,7 @@ import { CoreCategoryData, CoreCourses, CoreEnrolledCourseData } from '@features import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; import { AddonCalendarFilterPopoverComponent } from '../../components/filter/filter'; import moment from 'moment'; -import { Network, NgZone, PopoverController } from '@singletons'; +import { Network, NgZone } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { Params } from '@angular/router'; import { Subscription } from 'rxjs'; @@ -535,7 +535,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { * @param event Event. */ async openFilter(event: MouseEvent): Promise { - const popover = await PopoverController.create({ + await CoreDomUtils.openPopover({ component: AddonCalendarFilterPopoverComponent, componentProps: { courses: this.courses, @@ -543,7 +543,6 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { }, event, }); - await popover.present(); } /** diff --git a/src/addons/calendar/pages/index/index.page.ts b/src/addons/calendar/pages/index/index.page.ts index f5ae23d48..b125e259a 100644 --- a/src/addons/calendar/pages/index/index.page.ts +++ b/src/addons/calendar/pages/index/index.page.ts @@ -23,7 +23,7 @@ import { AddonCalendar, AddonCalendarProvider } from '../../services/calendar'; import { AddonCalendarOffline } from '../../services/calendar-offline'; import { AddonCalendarSync, AddonCalendarSyncProvider } from '../../services/calendar-sync'; import { AddonCalendarFilter, AddonCalendarHelper } from '../../services/calendar-helper'; -import { Network, NgZone, PopoverController } from '@singletons'; +import { Network, NgZone } from '@singletons'; import { Subscription } from 'rxjs'; import { CoreEnrolledCourseData } from '@features/courses/services/courses'; import { ActivatedRoute, Params } from '@angular/router'; @@ -333,7 +333,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { * @param event Event. */ async openFilter(event: MouseEvent): Promise { - const popover = await PopoverController.create({ + await CoreDomUtils.openPopover({ component: AddonCalendarFilterPopoverComponent, componentProps: { courses: this.courses, @@ -341,7 +341,6 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { }, event, }); - await popover.present(); } /** diff --git a/src/addons/calendar/pages/list/list.page.ts b/src/addons/calendar/pages/list/list.page.ts index 27911a5b2..cb1b76c32 100644 --- a/src/addons/calendar/pages/list/list.page.ts +++ b/src/addons/calendar/pages/list/list.page.ts @@ -35,7 +35,7 @@ import { CoreConstants } from '@/core/constants'; import { AddonCalendarFilterPopoverComponent } from '../../components/filter/filter'; import { Params } from '@angular/router'; import { Subscription } from 'rxjs'; -import { Network, NgZone, PopoverController } from '@singletons'; +import { Network, NgZone } from '@singletons'; import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; import { CoreUtils } from '@services/utils/utils'; import { CoreNavigator } from '@services/navigator'; @@ -616,7 +616,7 @@ export class AddonCalendarListPage implements OnInit, OnDestroy { * @param event Event. */ async openFilter(event: MouseEvent): Promise { - const popover = await PopoverController.create({ + await CoreDomUtils.openPopover({ component: AddonCalendarFilterPopoverComponent, componentProps: { courses: this.courses, @@ -624,7 +624,6 @@ export class AddonCalendarListPage implements OnInit, OnDestroy { }, event, }); - await popover.present(); } /** diff --git a/src/addons/mod/forum/components/index/index.ts b/src/addons/mod/forum/components/index/index.ts index 9f08eb1e6..e80e9a447 100644 --- a/src/addons/mod/forum/components/index/index.ts +++ b/src/addons/mod/forum/components/index/index.ts @@ -28,7 +28,7 @@ import { AddonModForumReplyDiscussionData, } from '@addons/mod/forum/services/forum'; import { AddonModForumOffline, AddonModForumOfflineDiscussion } from '@addons/mod/forum/services/forum-offline'; -import { PopoverController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { AddonModForumHelper } from '@addons/mod/forum/services/forum-helper'; import { CoreGroups, CoreGroupsProvider } from '@services/groups'; @@ -667,7 +667,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom * @param discussion Discussion. */ async showOptionsMenu(event: Event, discussion: AddonModForumDiscussion): Promise { - const popover = await PopoverController.create({ + const popoverData = await CoreDomUtils.openPopover<{ action?: string; value: boolean }>({ component: AddonModForumDiscussionOptionsMenuComponent, componentProps: { discussion, @@ -677,20 +677,16 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom event, }); - await popover.present(); - - const result = await popover.onDidDismiss<{ action?: string; value: boolean }>(); - - if (result.data && result.data.action) { - switch (result.data.action) { + if (popoverData && popoverData.action) { + switch (popoverData.action) { case 'lock': - discussion.locked = result.data.value; + discussion.locked = popoverData.value; break; case 'pin': - discussion.pinned = result.data.value; + discussion.pinned = popoverData.value; break; case 'star': - discussion.starred = result.data.value; + discussion.starred = popoverData.value; break; default: break; diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index b7285ae85..3c9466c8e 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -40,7 +40,7 @@ import { AddonModForumUpdateDiscussionPostWSOptionsObject, } from '../../services/forum'; import { CoreTag } from '@features/tag/services/tag'; -import { PopoverController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; import { IonContent } from '@ionic/angular'; import { AddonModForumSync } from '../../services/forum-sync'; @@ -218,7 +218,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges * @param event Click Event. */ async showOptionsMenu(event: Event): Promise { - const popover = await PopoverController.create({ + const popoverData = await CoreDomUtils.openPopover<{ action?: string }>({ component: AddonModForumPostOptionsMenuComponent, componentProps: { post: this.post, @@ -228,12 +228,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges event, }); - await popover.present(); - - const result = await popover.onDidDismiss<{ action?: string }>(); - - if (result.data && result.data.action) { - switch (result.data.action) { + if (popoverData && popoverData.action) { + switch (popoverData.action) { case 'edit': this.editPost(); break; diff --git a/src/addons/mod/glossary/components/index/index.ts b/src/addons/mod/glossary/components/index/index.ts index 2a83c4b1d..3059ca6b8 100644 --- a/src/addons/mod/glossary/components/index/index.ts +++ b/src/addons/mod/glossary/components/index/index.ts @@ -27,7 +27,7 @@ import { IonContent } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; -import { PopoverController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModGlossary, @@ -405,7 +405,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity * @param event Event. */ async openModePicker(event: MouseEvent): Promise { - const popover = await PopoverController.create({ + const mode = await CoreDomUtils.openPopover({ component: AddonModGlossaryModePickerPopoverComponent, componentProps: { browseModes: this.glossary!.browsemodes, @@ -414,11 +414,6 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity event, }); - await popover.present(); - - const result = await popover.onDidDismiss(); - - const mode = result.data; if (mode) { if (mode !== this.fetchMode) { this.changeFetchMode(mode); diff --git a/src/addons/mod/quiz/classes/auto-save.ts b/src/addons/mod/quiz/classes/auto-save.ts index 45021ed00..8ef7b833f 100644 --- a/src/addons/mod/quiz/classes/auto-save.ts +++ b/src/addons/mod/quiz/classes/auto-save.ts @@ -16,10 +16,10 @@ import { BehaviorSubject } from 'rxjs'; import { CoreQuestionHelper } from '@features/question/services/question-helper'; import { CoreQuestionsAnswers } from '@features/question/services/question'; -import { PopoverController } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { AddonModQuizConnectionErrorComponent } from '../components/connection-error/connection-error'; import { AddonModQuiz, AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '../services/quiz'; +import { CoreDomUtils } from '@services/utils/dom'; /** * Class to support auto-save in quiz. Every certain seconds, it will check if there are changes in the current page answers @@ -197,13 +197,10 @@ export class AddonModQuizAutoSave { }; this.popoverShown = true; - this.popover = await PopoverController.create({ + this.popover = await CoreDomUtils.openPopover({ component: AddonModQuizConnectionErrorComponent, event: event, }); - await this.popover.present(); - - await this.popover.onDidDismiss(); this.popoverShown = false; } diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index 8803e9840..355c38e7b 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -27,7 +27,7 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreUtils } from '@services/utils/utils'; -import { PopoverController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Md5 } from 'ts-md5'; import { AddonModWikiPageDBRecord } from '../../services/database/wiki'; @@ -794,7 +794,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp * @param event Event. */ async showSubwikiPicker(event: MouseEvent): Promise { - const popover = await PopoverController.create({ + const popoverData = await CoreDomUtils.openPopover({ component: AddonModWikiSubwikiPickerComponent, componentProps: { subwikis: this.subwikiData.subwikis, @@ -803,12 +803,8 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp event, }); - await popover.present(); - - const result = await popover.onDidDismiss(); - - if (result.data) { - this.goToSubwiki(result.data.id, result.data.userid, result.data.groupid, result.data.canedit); + if (popoverData) { + this.goToSubwiki(popoverData.id, popoverData.userid, popoverData.groupid, popoverData.canedit); } } diff --git a/src/core/components/context-menu/context-menu.ts b/src/core/components/context-menu/context-menu.ts index fbf45dcde..73e8c9393 100644 --- a/src/core/components/context-menu/context-menu.ts +++ b/src/core/components/context-menu/context-menu.ts @@ -17,7 +17,7 @@ import { Subject } from 'rxjs'; import { auditTime } from 'rxjs/operators'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; -import { PopoverController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreContextMenuItemComponent } from './context-menu-item'; import { CoreContextMenuPopoverComponent } from './context-menu-popover'; @@ -176,26 +176,23 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { */ async showContextMenu(event: MouseEvent): Promise { if (!this.expanded) { - const popover = await PopoverController.create( - { - event, - component: CoreContextMenuPopoverComponent, - componentProps: { - title: this.title, - items: this.items, - }, - showBackdrop: true, - id: this.uniqueId, - }, - ); - await popover.present(); this.expanded = true; - const data = await popover.onDidDismiss(); + const popoverData = await CoreDomUtils.openPopover({ + event, + component: CoreContextMenuPopoverComponent, + componentProps: { + title: this.title, + items: this.items, + }, + showBackdrop: true, + id: this.uniqueId, + }); + this.expanded = false; - if (data.data) { - data.data.onClosed?.emit(); + if (popoverData) { + popoverData.onClosed?.emit(); } } diff --git a/src/core/features/courses/components/course-progress/course-progress.ts b/src/core/features/courses/components/course-progress/course-progress.ts index 75f0b9ce3..de922c200 100644 --- a/src/core/features/courses/components/course-progress/course-progress.ts +++ b/src/core/features/courses/components/course-progress/course-progress.ts @@ -19,7 +19,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreCourses, CoreCoursesProvider } from '@features/courses/services/courses'; import { CoreCourse, CoreCourseProvider } from '@features/course/services/course'; import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/services/course-helper'; -import { PopoverController, Translate } from '@singletons'; +import { Translate } from '@singletons'; import { CoreConstants } from '@/core/constants'; import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '../../services/courses-helper'; import { CoreCoursesCourseOptionsMenuComponent } from '../course-options-menu/course-options-menu'; @@ -198,7 +198,7 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy { e.preventDefault(); e.stopPropagation(); - const popover = await PopoverController.create({ + const popoverData = await CoreDomUtils.openPopover({ component: CoreCoursesCourseOptionsMenuComponent, componentProps: { course: this.course, @@ -207,37 +207,32 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy { }, event: e, }); - await popover.present(); - const action = await popover.onDidDismiss(); - - if (action.data) { - switch (action.data) { - case 'download': - if (!this.prefetchCourseData.loading) { - this.prefetchCourse(e); - } - break; - case 'delete': - if (this.courseStatus == 'downloaded' || this.courseStatus == 'outdated') { - this.deleteCourse(); - } - break; - case 'hide': - this.setCourseHidden(true); - break; - case 'show': - this.setCourseHidden(false); - break; - case 'favourite': - this.setCourseFavourite(true); - break; - case 'unfavourite': - this.setCourseFavourite(false); - break; - default: - break; - } + switch (popoverData) { + case 'download': + if (!this.prefetchCourseData.loading) { + this.prefetchCourse(e); + } + break; + case 'delete': + if (this.courseStatus == 'downloaded' || this.courseStatus == 'outdated') { + this.deleteCourse(); + } + break; + case 'hide': + this.setCourseHidden(true); + break; + case 'show': + this.setCourseHidden(false); + break; + case 'favourite': + this.setCourseFavourite(true); + break; + case 'unfavourite': + this.setCourseFavourite(false); + break; + default: + break; } } diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 13c3e4a91..18419faca 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -15,7 +15,7 @@ import { Injectable, SimpleChange, ElementRef, KeyValueChanges } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; -import { ModalOptions, AlertOptions, AlertButton, TextFieldTypes } from '@ionic/core'; +import { ModalOptions, PopoverOptions, AlertOptions, AlertButton, TextFieldTypes } from '@ionic/core'; import { Md5 } from 'ts-md5'; import { CoreApp } from '@services/app'; @@ -796,7 +796,7 @@ export class CoreDomUtilsProvider { el.addEventListener('click', async (ev: Event) => { const html = el.getAttribute('data-html'); - const popover = await PopoverController.create({ + await CoreDomUtils.openPopover({ component: CoreBSTooltipComponent, componentProps: { content, @@ -804,7 +804,6 @@ export class CoreDomUtilsProvider { }, event: ev, }); - await popover.present(); }); }); } @@ -1721,6 +1720,26 @@ export class CoreDomUtilsProvider { return await this.openModal(modalOptions); } + /** + * Opens a popover. + * + * @param popoverOptions Modal Options. + */ + async openPopover( + popoverOptions: PopoverOptions, + ): Promise { + + const popover = await PopoverController.create(popoverOptions); + + await popover.present(); + + // If onDidDismiss is nedded we can add a new param to the function to wait one function or the other. + const result = await popover.onWillDismiss(); + if (result?.data) { + return result?.data; + } + } + /** * View an image in a modal. * From f108d0a8d82181750561db980eda0a8b0b7d6e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 4 May 2021 17:19:57 +0200 Subject: [PATCH 06/11] MOBILE-3745 tabs: Add keyboard a11y to tabs --- src/core/classes/aria-role-tab.ts | 137 ++++++++++++++++++ src/core/classes/tabs.ts | 28 ++++ .../tabs-outlet/core-tabs-outlet.html | 29 +++- .../components/tabs-outlet/tabs-outlet.ts | 3 +- src/core/components/tabs/core-tabs.html | 31 +++- src/core/components/tabs/tab.ts | 4 + src/core/components/tabs/tabs.scss | 4 +- src/core/components/tabs/tabs.ts | 1 + .../features/mainmenu/pages/menu/menu.html | 33 ++++- .../features/mainmenu/pages/menu/menu.scss | 1 - src/core/features/mainmenu/pages/menu/menu.ts | 51 ++++++- .../mainmenu/services/mainmenu-delegate.ts | 5 + .../features/mainmenu/services/mainmenu.ts | 2 +- 13 files changed, 300 insertions(+), 29 deletions(-) create mode 100644 src/core/classes/aria-role-tab.ts diff --git a/src/core/classes/aria-role-tab.ts b/src/core/classes/aria-role-tab.ts new file mode 100644 index 000000000..12c54b8c6 --- /dev/null +++ b/src/core/classes/aria-role-tab.ts @@ -0,0 +1,137 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export class CoreAriaRoleTab { + + componentInstance: T; + + constructor(componentInstance: T) { + this.componentInstance = componentInstance; + } + + /** + * A11y key functionallity that prevents keyDown events. + * + * @param e Event. + */ + keyDown(e: KeyboardEvent): void { + if (e.key == ' ' || + e.key == 'Enter' || + e.key == 'Home' || + e.key == 'End' || + (this.isHorizontal() && (e.key == 'ArrowRight' || e.key == 'ArrowLeft')) || + (!this.isHorizontal() && (e.key == 'ArrowUp' ||e.key == 'ArrowDown')) + ) { + e.preventDefault(); + e.stopPropagation(); + } + } + + /** + * A11y key functionallity. + * + * Enter or Space: When a tab has focus, activates the tab, causing its associated panel to be displayed. + * Right Arrow: When a tab has focus: Moves focus to the next tab. If focus is on the last tab, moves focus to the first tab. + * Left Arrow: When a tab has focus: Moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab. + * Home: When a tab has focus, moves focus to the first tab. + * End: When a tab has focus, moves focus to the last tab. + * https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-2/tabs.html + * + * @param tabFindIndex Tab finable index. + * @param e Event. + * @return Promise resolved when done. + */ + keyUp(tabFindIndex: string, e: KeyboardEvent): void { + if (e.key == ' ' || e.key == 'Enter') { + this.selectTab(tabFindIndex, e); + + return; + } + + e.preventDefault(); + e.stopPropagation(); + + const tabs = this.getSelectableTabs(); + + let index = tabs.findIndex((tab) => tabFindIndex == tab.findIndex); + + const previousKey = this.isHorizontal() ? 'ArrowLeft' : 'ArrowUp'; + const nextKey = this.isHorizontal() ? 'ArrowRight' : 'ArrowDown'; + + switch (e.key) { + case nextKey: + index++; + if (index >= tabs.length) { + index = 0; + } + break; + case 'Home': + index = 0; + break; + case previousKey: + index--; + if (index < 0) { + index = tabs.length - 1; + } + break; + case 'End': + index = tabs.length - 1; + break; + default: + return; + } + + const tabId = tabs[index].id; + + // @todo Pages should match aria-controls id. + const tabElement = document.querySelector(`ion-tab-button[aria-controls=${tabId}]`); + + tabElement?.focus(); + } + + /** + * Selects the tab. + * + * @param tabId Tab identifier. + * @param e Event. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + selectTab(tabId: string, e: Event): void { + // + } + + /** + * Return all the selectable tabs. + * + * @returns all the selectable tabs. + */ + getSelectableTabs(): CoreAriaRoleTabFindable[] { + return []; + } + + /** + * Returns if tabs are displayed horizontal or not. + * + * @returns Where the tabs are displayed horizontal. + */ + isHorizontal(): boolean { + return true; + } + +} + +export type CoreAriaRoleTabFindable = { + id: string; + findIndex: string; +}; diff --git a/src/core/classes/tabs.ts b/src/core/classes/tabs.ts index 6338fc65d..190e754f4 100644 --- a/src/core/classes/tabs.ts +++ b/src/core/classes/tabs.ts @@ -31,6 +31,7 @@ import { Subscription } from 'rxjs'; import { Platform, Translate } from '@singletons'; import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; +import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from './aria-role-tab'; /** * Class to abstract some common code for tabs. @@ -89,10 +90,13 @@ export class CoreTabsBaseComponent implements OnInit, Aft protected slidesSwiperLoaded = false; protected scrollElements: Record = {}; // Scroll elements for each loaded tab. + tabAction: CoreTabsRoleTab; + constructor( protected element: ElementRef, ) { this.backButtonFunction = this.backButtonClicked.bind(this); + this.tabAction = new CoreTabsRoleTab(this); } /** @@ -632,6 +636,30 @@ export class CoreTabsBaseComponent implements OnInit, Aft } +/** + * Helper class to manage rol tab. + */ +class CoreTabsRoleTab extends CoreAriaRoleTab> { + + /** + * @inheritdoc + */ + selectTab(tabId: string, e: Event): void { + this.componentInstance.selectTab(tabId, e); + } + + /** + * @inheritdoc + */ + getSelectableTabs(): CoreAriaRoleTabFindable[] { + return this.componentInstance.tabs.filter((tab) => tab.enabled).map((tab) => ({ + id: tab.id!, + findIndex: tab.id!, + })); + } + +} + /** * Data for each tab. */ diff --git a/src/core/components/tabs-outlet/core-tabs-outlet.html b/src/core/components/tabs-outlet/core-tabs-outlet.html index e20d9a1ea..4662bb224 100644 --- a/src/core/components/tabs-outlet/core-tabs-outlet.html +++ b/src/core/components/tabs-outlet/core-tabs-outlet.html @@ -7,15 +7,28 @@ + [attr.aria-label]="description"> - - - - + + + + {{ tab.title | translate}} {{ tab.badge }} diff --git a/src/core/components/tabs-outlet/tabs-outlet.ts b/src/core/components/tabs-outlet/tabs-outlet.ts index cb256592b..7c82bdd88 100644 --- a/src/core/components/tabs-outlet/tabs-outlet.ts +++ b/src/core/components/tabs-outlet/tabs-outlet.ts @@ -45,7 +45,8 @@ import { CoreTabBase, CoreTabsBaseComponent } from '@classes/tabs'; * Tab contents will only be shown if that tab is selected. * * @todo: Test RTL and tab history. - * @todo: This should behave like the split-view in relation to routing (maybe we could reuse some code from CoreItemsListManager). + * @todo: This should behave like the split-view in relation to routing (maybe we could reuse some code from + * CorePageItemsListManager). */ @Component({ selector: 'core-tabs-outlet', diff --git a/src/core/components/tabs/core-tabs.html b/src/core/components/tabs/core-tabs.html index fbba11446..c15340c3d 100644 --- a/src/core/components/tabs/core-tabs.html +++ b/src/core/components/tabs/core-tabs.html @@ -6,15 +6,30 @@ + [attr.aria-label]="description"> - - - - {{ tab.badge }} + + + + {{ tab.title | translate}} + {{ tab.badge }} + diff --git a/src/core/components/tabs/tab.ts b/src/core/components/tabs/tab.ts index 24726538a..dcd9c1f71 100644 --- a/src/core/components/tabs/tab.ts +++ b/src/core/components/tabs/tab.ts @@ -84,6 +84,7 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase { this.element.setAttribute('role', 'tabpanel'); this.element.setAttribute('tabindex', '0'); + this.element.setAttribute('aria-hidden', 'true'); } /** @@ -113,6 +114,7 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase { this.tabElement = this.tabElement || document.getElementById(this.id + '-tab'); this.tabElement?.setAttribute('aria-selected', 'true'); + this.element.setAttribute('aria-hidden', 'false'); this.loaded = true; this.ionSelect.emit(this); @@ -128,6 +130,8 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase { unselectTab(): void { this.tabElement?.setAttribute('aria-selected', 'false'); this.element.classList.remove('selected'); + this.element.setAttribute('aria-hidden', 'true'); + this.showHideNavBarButtons(false); } diff --git a/src/core/components/tabs/tabs.scss b/src/core/components/tabs/tabs.scss index 52e8a9074..93ce49ca6 100644 --- a/src/core/components/tabs/tabs.scss +++ b/src/core/components/tabs/tabs.scss @@ -30,6 +30,7 @@ overflow: hidden; ion-tab-button { + max-width: 100%; ion-label { font-size: 16px; font-weight: 400; @@ -44,7 +45,8 @@ } } - &[aria-selected=true] { + &[aria-selected=true], + &.selected { color: var(--color-active); border-bottom-color: var(--border-color-active); ion-tab-button { diff --git a/src/core/components/tabs/tabs.ts b/src/core/components/tabs/tabs.ts index a06e28af0..e5c4f778e 100644 --- a/src/core/components/tabs/tabs.ts +++ b/src/core/components/tabs/tabs.ts @@ -45,6 +45,7 @@ import { CoreTabComponent } from './tab'; export class CoreTabsComponent extends CoreTabsBaseComponent implements AfterViewInit { @Input() parentScrollable = false; // Determine if the scroll should be in the parent content or the tab itself. + @Input() layout: 'icon-top' | 'icon-start' | 'icon-end' | 'icon-bottom' | 'icon-hide' | 'label-hide' = 'icon-hide'; @ViewChild('originalTabs') originalTabsRef?: ElementRef; diff --git a/src/core/features/mainmenu/pages/menu/menu.html b/src/core/features/mainmenu/pages/menu/menu.html index 060d29bb6..5b4b064f7 100644 --- a/src/core/features/mainmenu/pages/menu/menu.html +++ b/src/core/features/mainmenu/pages/menu/menu.html @@ -3,17 +3,38 @@ - + {{ tab.badge }} - - - {{ 'core.more' | translate }} + + + diff --git a/src/core/features/mainmenu/pages/menu/menu.scss b/src/core/features/mainmenu/pages/menu/menu.scss index adeec2ef0..151bea4ec 100644 --- a/src/core/features/mainmenu/pages/menu/menu.scss +++ b/src/core/features/mainmenu/pages/menu/menu.scss @@ -29,7 +29,6 @@ height: 100%; flex-direction: column; ion-tab-button { - display: contents; width: 100%; ion-badge { top: calc(50% - 20px); diff --git a/src/core/features/mainmenu/pages/menu/menu.ts b/src/core/features/mainmenu/pages/menu/menu.ts index 3d2005371..36a197501 100644 --- a/src/core/features/mainmenu/pages/menu/menu.ts +++ b/src/core/features/mainmenu/pages/menu/menu.ts @@ -25,6 +25,8 @@ import { CoreMainMenu, CoreMainMenuProvider } from '../../services/mainmenu'; import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/mainmenu-delegate'; import { CoreDomUtils } from '@services/utils/dom'; import { Translate } from '@singletons'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from '@classes/aria-role-tab'; /** * Page that displays the main menu of the app. @@ -40,20 +42,22 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { allHandlers?: CoreMainMenuHandlerToDisplay[]; loaded = false; showTabs = false; - tabsPlacement = 'bottom'; + tabsPlacement: 'bottom' | 'side' = 'bottom'; hidden = false; morePageName = CoreMainMenuProvider.MORE_PAGE_NAME; + selectedTab?: string; protected subscription?: Subscription; protected keyboardObserver?: CoreEventObserver; protected resizeFunction: () => void; protected backButtonFunction: (event: BackButtonEvent) => void; protected selectHistory: string[] = []; - protected selectedTab?: string; protected firstSelectedTab?: string; @ViewChild('mainTabs') mainTabs?: IonTabs; + tabAction: CoreMainMenuRoleTab; + constructor( protected route: ActivatedRoute, protected changeDetector: ChangeDetectorRef, @@ -61,6 +65,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { ) { this.resizeFunction = this.initHandlers.bind(this); this.backButtonFunction = this.backButtonClicked.bind(this); + this.tabAction = new CoreMainMenuRoleTab(this); } /** @@ -111,10 +116,11 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { const handler = handlers[i]; // Check if the handler is already in the tabs list. If so, use it. - const tab = this.tabs.find((tab) => tab.title == handler.title && tab.icon == handler.icon); + const tab = this.tabs.find((tab) => tab.page == handler.page); tab ? tab.hide = false : null; handler.hide = false; + handler.id = handler.id || 'core-mainmenu-' + CoreUtils.getUniqueId('CoreMainMenuPage'); newTabs.push(tab || handler); } @@ -246,3 +252,42 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { } } + +/** + * Helper class to manage rol tab. + */ +class CoreMainMenuRoleTab extends CoreAriaRoleTab { + + /** + * @inheritdoc + */ + selectTab(tabId: string, e: Event): void { + this.componentInstance.tabClicked(e, tabId); + } + + /** + * @inheritdoc + */ + getSelectableTabs(): CoreAriaRoleTabFindable[] { + const allTabs: CoreAriaRoleTabFindable[] = + this.componentInstance.tabs.filter((tab) => !tab.hide).map((tab) => ({ + id: tab.id || tab.page, + findIndex: tab.page, + })); + + allTabs.push({ + id: this.componentInstance.morePageName, + findIndex: this.componentInstance.morePageName, + }); + + return allTabs; + } + + /** + * @inheritdoc + */ + isHorizontal(): boolean { + return this.componentInstance.tabsPlacement == 'bottom'; + } + +} diff --git a/src/core/features/mainmenu/services/mainmenu-delegate.ts b/src/core/features/mainmenu/services/mainmenu-delegate.ts index 1fb88092a..862bb5b55 100644 --- a/src/core/features/mainmenu/services/mainmenu-delegate.ts +++ b/src/core/features/mainmenu/services/mainmenu-delegate.ts @@ -89,6 +89,11 @@ export interface CoreMainMenuHandlerToDisplay extends CoreDelegateToDisplay, Cor * Hide tab. Used then resizing. */ hide?: boolean; + + /** + * Used to control tabs. + */ + id?: string; } /** diff --git a/src/core/features/mainmenu/services/mainmenu.ts b/src/core/features/mainmenu/services/mainmenu.ts index 76814de46..aa602d7d4 100644 --- a/src/core/features/mainmenu/services/mainmenu.ts +++ b/src/core/features/mainmenu/services/mainmenu.ts @@ -179,7 +179,7 @@ export class CoreMainMenuProvider { * * @return Tabs placement including side value. */ - getTabPlacement(): string { + getTabPlacement(): 'bottom' | 'side' { const tablet = !!(window.innerWidth && window.innerWidth >= 576 && (window.innerHeight >= 576 || ((CoreApp.isKeyboardVisible() || CoreApp.isKeyboardOpening()) && window.innerHeight >= 200))); From f11fd7b08d5fbd8bdcfa0cdd1a63c9d6a27b2416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 6 May 2021 11:33:10 +0200 Subject: [PATCH 07/11] MOBILE-3745 buttons: Add min target size to buttons --- .../addon-block-recentlyaccessedcourses.html | 2 +- .../addon-block-sitemainmenu.html | 2 +- src/addons/mod/quiz/pages/player/player.html | 8 ++-- .../core-send-message-form.html | 14 +++---- .../send-message-form/send-message-form.scss | 17 ++------ .../features/login/pages/sites/sites.html | 3 +- src/theme/theme.base.scss | 39 ++++++++++++------- src/theme/theme.light.scss | 5 +++ 8 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html b/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html index e8f1b307f..e0cc57102 100644 --- a/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html +++ b/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html @@ -1,4 +1,4 @@ - +

{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}

diff --git a/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html b/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html index 8f9f3989a..47a5d70a0 100644 --- a/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html +++ b/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html @@ -1,4 +1,4 @@ - +

{{ 'addon.block_sitemainmenu.pluginname' | translate }}

diff --git a/src/addons/mod/quiz/pages/player/player.html b/src/addons/mod/quiz/pages/player/player.html index 6b4cb8b53..4472f7879 100644 --- a/src/addons/mod/quiz/pages/player/player.html +++ b/src/addons/mod/quiz/pages/player/player.html @@ -10,8 +10,9 @@ - + - + diff --git a/src/core/components/send-message-form/core-send-message-form.html b/src/core/components/send-message-form/core-send-message-form.html index ac333d0e2..af2617248 100644 --- a/src/core/components/send-message-form/core-send-message-form.html +++ b/src/core/components/send-message-form/core-send-message-form.html @@ -1,11 +1,9 @@
- - - - - + [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keyup.enter)="enterClicked($event)" + (keyup.control.enter)="enterClicked($event, 'control')" (keyup.meta.enter)="enterClicked($event, 'meta')"> + + +
diff --git a/src/core/components/send-message-form/send-message-form.scss b/src/core/components/send-message-form/send-message-form.scss index 0faebb6ee..c9500e34a 100644 --- a/src/core/components/send-message-form/send-message-form.scss +++ b/src/core/components/send-message-form/send-message-form.scss @@ -6,28 +6,19 @@ display: flex; align-items: center; width: 100%; - margin-top: 5px; - margin-bottom: 5px; } .core-send-message-input { appearance: none; display: block; width: 100%; - min-height: 28px; border: 0; font-family: inherit; background: var(--core-send-message-input-background); color: var(--core-send-message-input-color); - border-radius: 5px; - margin: 0 5px; - } - - .core-send-message-button { - margin: 0; - padding: 0; - display: none; - min-height: 0; - align-self: self-end; + border-radius: 21px; + line-height: 20px; + padding: 9px 12px 11px; + margin: 5px 10px; } } diff --git a/src/core/features/login/pages/sites/sites.html b/src/core/features/login/pages/sites/sites.html index 59fb663d8..20789cbb7 100644 --- a/src/core/features/login/pages/sites/sites.html +++ b/src/core/features/login/pages/sites/sites.html @@ -7,7 +7,8 @@ {{ 'core.settings.sites' | translate }} - + Date: Thu, 6 May 2021 14:38:48 +0200 Subject: [PATCH 08/11] MOBILE-3745 buttons: Implement user avatar as a button --- src/core/classes/aria-role-button.ts | 68 +++++++++++++++++++ .../user-avatar/core-user-avatar.html | 12 +++- .../components/user-avatar/user-avatar.ts | 48 ++++++++++--- 3 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 src/core/classes/aria-role-button.ts diff --git a/src/core/classes/aria-role-button.ts b/src/core/classes/aria-role-button.ts new file mode 100644 index 000000000..b55fbc876 --- /dev/null +++ b/src/core/classes/aria-role-button.ts @@ -0,0 +1,68 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export abstract class CoreAriaRoleButton { + + componentInstance: T; + + constructor(componentInstance: T) { + this.componentInstance = componentInstance; + } + + /** + * A11y key functionallity that prevents keyDown events. + * + * @param event Event. + */ + keyDown(event: KeyboardEvent): void { + if ((event.key == ' ' || event.key == 'Enter') && this.isAllowed()) { + event.preventDefault(); + event.stopPropagation(); + } + } + + /** + * A11y key functionallity that translates space and enter keys to click action. + * + * @param event Event. + */ + keyUp(event: KeyboardEvent): void { + if ((event.key == ' ' || event.key == 'Enter') && this.isAllowed()) { + event.preventDefault(); + event.stopPropagation(); + + this.click(event); + } + } + + /** + * A11y click functionallity. + * + * @param event Event. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + click(event?: Event): void { + // Nothing defined here. + } + + /** + * Checks if action is allowed in class. + * + * @returns If allowed. + */ + isAllowed(): boolean { + return true; + } + +} diff --git a/src/core/components/user-avatar/core-user-avatar.html b/src/core/components/user-avatar/core-user-avatar.html index a5a776577..60b0a32e2 100644 --- a/src/core/components/user-avatar/core-user-avatar.html +++ b/src/core/components/user-avatar/core-user-avatar.html @@ -1,10 +1,16 @@ + onError="this.src='assets/img/user-avatar.png'" (click)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" + [attr.role]="linkProfile ? 'button' : null" (keydown)="buttonAction.keyDown($event)" + (keyup)="buttonAction.keyUp($event)" + [attr.tabindex]="linkProfile ? 0 : null"> + (click)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null" + (keydown)="buttonAction.keyDown($event)" (keyup)="buttonAction.keyUp($event)" + [attr.tabindex]="linkProfile ? 0 : null"> - + + diff --git a/src/core/components/user-avatar/user-avatar.ts b/src/core/components/user-avatar/user-avatar.ts index dd3884996..6097a0371 100644 --- a/src/core/components/user-avatar/user-avatar.ts +++ b/src/core/components/user-avatar/user-avatar.ts @@ -20,6 +20,7 @@ import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreUserProvider, CoreUserBasicData } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; +import { CoreAriaRoleButton } from '@classes/aria-role-button'; /** * Component to display a "user avatar". @@ -36,15 +37,17 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { @Input() user?: CoreUserWithAvatar; // The following params will override the ones in user object. @Input() profileUrl?: string; - @Input() protected linkProfile = true; // Avoid linking to the profile if wanted. + @Input() linkProfile = true; // Avoid linking to the profile if wanted. @Input() fullname?: string; - @Input() protected userId?: number; // If provided or found it will be used to link the image to the profile. - @Input() protected courseId?: number; + @Input() userId?: number; // If provided or found it will be used to link the image to the profile. + @Input() courseId?: number; @Input() checkOnline = false; // If want to check and show online status. @Input() extraIcon?: string; // Extra icon to show near the avatar. avatarUrl?: string; + buttonAction: CoreUserAvatarButton; + // Variable to check if we consider this user online or not. // @TODO: Use setting when available (see MDL-63972) so we can use site setting. protected timetoshowusers = 300000; // Miliseconds default. @@ -52,6 +55,7 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { protected pictureObserver: CoreEventObserver; constructor() { + this.currentUserId = CoreSites.getCurrentSiteUserId(); this.pictureObserver = CoreEvents.on( @@ -63,12 +67,15 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { }, CoreSites.getCurrentSiteId(), ); + + this.buttonAction = new CoreUserAvatarButton(this); } /** * Component being initialized. */ ngOnInit(): void { + this.setFields(); } @@ -130,19 +137,14 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { * @param event Click event. */ gotoProfile(event: Event): void { - if (!this.linkProfile || !this.userId) { + if (!this.buttonAction.isAllowed()) { return; } event.preventDefault(); event.stopPropagation(); - CoreNavigator.navigateToSitePath('user', { - params: { - userId: this.userId, - courseId: this.courseId, - }, - }); + this.buttonAction.click(); } /** @@ -154,6 +156,32 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { } +/** + * Helper class to manage rol button. + */ +class CoreUserAvatarButton extends CoreAriaRoleButton { + + /** + * @inheritdoc + */ + click(): void { + CoreNavigator.navigateToSitePath('user', { + params: { + userId: this.componentInstance.userId, + courseId: this.componentInstance.courseId, + }, + }); + } + + /** + * @inheritdoc + */ + isAllowed(): boolean { + return this.componentInstance.linkProfile && !!this.componentInstance.userId; + } + +} + /** * Type with all possible formats of user. */ From 6e94f0b3ab2337dd4c187d4dbc6b7958d9cc1ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 6 May 2021 14:52:50 +0200 Subject: [PATCH 09/11] MOBILE-3745 a11y: Minor improvements on dividers --- .../calendar/pages/edit-event/edit-event.html | 4 ++-- src/addons/calendar/pages/list/list.html | 2 +- .../coursecompletion/pages/report/report.html | 4 ++-- .../messages/pages/discussion/discussion.html | 5 +++-- .../group-conversations/group-conversations.html | 8 +++++--- src/addons/messages/pages/search/search.html | 2 +- .../pages/nonrespondents/nonrespondents.html | 2 +- .../feedback/pages/respondents/respondents.html | 4 ++-- .../mod/forum/components/edit-post/edit-post.html | 2 +- src/addons/mod/forum/components/post/post.html | 2 +- .../forum/pages/new-discussion/new-discussion.html | 2 +- .../components/index/addon-mod-glossary-index.html | 4 ++-- src/addons/mod/glossary/pages/edit/edit.html | 4 ++-- src/addons/mod/lesson/pages/player/player.html | 10 +++++++--- src/addons/mod/wiki/components/map/map.html | 2 +- .../addon-mod-wiki-subwiki-picker.html | 2 +- .../components/format/core-course-format.html | 5 ++++- .../login/pages/email-signup/email-signup.html | 14 +++++++------- .../forgotten-password/forgotten-password.html | 2 +- src/core/features/sitehome/pages/index/index.html | 4 +++- src/core/features/user/pages/about/about.html | 6 +++--- 21 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/addons/calendar/pages/edit-event/edit-event.html b/src/addons/calendar/pages/edit-event/edit-event.html index 32b691c6e..78c05e1fb 100644 --- a/src/addons/calendar/pages/edit-event/edit-event.html +++ b/src/addons/calendar/pages/edit-event/edit-event.html @@ -126,8 +126,8 @@ - {{ 'core.showmore' | translate }} - {{ 'core.showless' | translate }} +

{{ 'core.showmore' | translate }}

+

{{ 'core.showless' | translate }}

diff --git a/src/addons/calendar/pages/list/list.html b/src/addons/calendar/pages/list/list.html index bb3bc6edb..fd84c4313 100644 --- a/src/addons/calendar/pages/list/list.html +++ b/src/addons/calendar/pages/list/list.html @@ -40,7 +40,7 @@ - {{ event.timestart * 1000 | coreFormatDate: "strftimedayshort" }} +

{{ event.timestart * 1000 | coreFormatDate: "strftimedayshort" }}

- {{ 'addon.coursecompletion.requiredcriteria' | translate }} +

{{ 'addon.coursecompletion.requiredcriteria' | translate }}

@@ -71,7 +71,7 @@
- {{ 'addon.coursecompletion.manualselfcompletion' | translate }} +

{{ 'addon.coursecompletion.manualselfcompletion' | translate }}

diff --git a/src/addons/messages/pages/discussion/discussion.html b/src/addons/messages/pages/discussion/discussion.html index cc6c3d675..079b182dc 100644 --- a/src/addons/messages/pages/discussion/discussion.html +++ b/src/addons/messages/pages/discussion/discussion.html @@ -93,7 +93,8 @@ [@coreSlideInOut]="message.useridfrom == currentUserId ? '' : 'fromLeft'"> -

+

@@ -101,7 +102,7 @@ {{ message.timecreated | coreFormatDate: "strftimetime" }} - +

diff --git a/src/addons/messages/pages/group-conversations/group-conversations.html b/src/addons/messages/pages/group-conversations/group-conversations.html index a5a30b8dc..518656e04 100644 --- a/src/addons/messages/pages/group-conversations/group-conversations.html +++ b/src/addons/messages/pages/group-conversations/group-conversations.html @@ -37,7 +37,7 @@ [attr.aria-expanded]="favourites.expanded" role="heading button"> - {{ 'core.favourites' | translate }} ({{ favourites.count }}) +

{{ 'core.favourites' | translate }} ({{ favourites.count }})

{{ favourites.unread }}
@@ -60,7 +60,7 @@ [attr.aria-expanded]="group.expanded" role="heading button"> - {{ 'addon.messages.groupconversations' | translate }} ({{ group.count }}) +

{{ 'addon.messages.groupconversations' | translate }} ({{ group.count }})

{{ group.unread }}
@@ -82,7 +82,9 @@ [attr.aria-expanded]="individual.expanded" role="heading button"> - {{ 'addon.messages.individualconversations' | translate }} ({{ individual.count }}) + +

{{ 'addon.messages.individualconversations' | translate }} ({{ individual.count }})

+
{{ individual.unread }}
diff --git a/src/addons/messages/pages/search/search.html b/src/addons/messages/pages/search/search.html index 8a8b5a298..fd0e764a2 100644 --- a/src/addons/messages/pages/search/search.html +++ b/src/addons/messages/pages/search/search.html @@ -38,7 +38,7 @@ - {{ item.titleString | translate }} +

{{ item.titleString | translate }}

diff --git a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.html b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.html index d9fcbfff9..8ec8a826c 100644 --- a/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.html +++ b/src/addons/mod/feedback/pages/nonrespondents/nonrespondents.html @@ -26,7 +26,7 @@ - {{ 'addon.mod_feedback.non_respondents_students' | translate : {$a: total } }} +

{{ 'addon.mod_feedback.non_respondents_students' | translate : {$a: total } }}

diff --git a/src/addons/mod/feedback/pages/respondents/respondents.html b/src/addons/mod/feedback/pages/respondents/respondents.html index bccb6847c..5a6ffcce4 100644 --- a/src/addons/mod/feedback/pages/respondents/respondents.html +++ b/src/addons/mod/feedback/pages/respondents/respondents.html @@ -29,7 +29,7 @@ - {{ 'addon.mod_feedback.non_anonymous_entries' | translate : {$a: responses.responses.total } }} +

{{ 'addon.mod_feedback.non_anonymous_entries' | translate : {$a: responses.responses.total } }}

- {{ 'addon.mod_feedback.anonymous_entries' |translate : {$a: responses.anonResponses.total } }} +

{{ 'addon.mod_feedback.anonymous_entries' |translate : {$a: responses.anonResponses.total } }}

- {{ 'addon.mod_forum.advanced' | translate }} +

{{ 'addon.mod_forum.advanced' | translate }}

- {{ 'addon.mod_forum.advanced' | translate }} +

{{ 'addon.mod_forum.advanced' | translate }}

diff --git a/src/addons/mod/forum/pages/new-discussion/new-discussion.html b/src/addons/mod/forum/pages/new-discussion/new-discussion.html index d67dac037..f28a931a9 100644 --- a/src/addons/mod/forum/pages/new-discussion/new-discussion.html +++ b/src/addons/mod/forum/pages/new-discussion/new-discussion.html @@ -35,7 +35,7 @@ [attr.aria-label]="(advanced ? 'core.hideadvanced' : 'core.showadvanced') |translate" role="heading button"> - {{ 'addon.mod_forum.advanced' | translate }} +

{{ 'addon.mod_forum.advanced' | translate }}

diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index f50c2401c..ffa5e9542 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -64,7 +64,7 @@ - {{ 'addon.mod_glossary.entriestobesynced' | translate }} +

{{ 'addon.mod_glossary.entriestobesynced' | translate }}

@@ -79,7 +79,7 @@ - {{ getDivider!(entry) }} +

{{ getDivider!(entry) }}

- {{ 'addon.mod_glossary.attachment' | translate }} +

{{ 'addon.mod_glossary.attachment' | translate }}

- {{ 'addon.mod_glossary.linking' | translate }} +

{{ 'addon.mod_glossary.linking' | translate }}

{{ 'addon.mod_glossary.entryusedynalink' | translate }} diff --git a/src/addons/mod/lesson/pages/player/player.html b/src/addons/mod/lesson/pages/player/player.html index 2e35a9298..21f3668dc 100644 --- a/src/addons/mod/lesson/pages/player/player.html +++ b/src/addons/mod/lesson/pages/player/player.html @@ -57,9 +57,13 @@ (ngSubmit)="submitQuestion($event)"> - - + +

+ + +

+
diff --git a/src/addons/mod/wiki/components/map/map.html b/src/addons/mod/wiki/components/map/map.html index 7e34f12ef..9b2db6da2 100644 --- a/src/addons/mod/wiki/components/map/map.html +++ b/src/addons/mod/wiki/components/map/map.html @@ -18,7 +18,7 @@
- {{ letter.label }} +

{{ letter.label }}

diff --git a/src/addons/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html b/src/addons/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html index 27dc3c5ea..815b657c0 100644 --- a/src/addons/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html +++ b/src/addons/mod/wiki/components/subwiki-picker/addon-mod-wiki-subwiki-picker.html @@ -1,7 +1,7 @@ - {{ group.label }} +

{{ group.label }}

- +

+ + +

diff --git a/src/core/features/login/pages/email-signup/email-signup.html b/src/core/features/login/pages/email-signup/email-signup.html index 5da51c810..c053e73e2 100644 --- a/src/core/features/login/pages/email-signup/email-signup.html +++ b/src/core/features/login/pages/email-signup/email-signup.html @@ -90,7 +90,7 @@ - {{ 'core.login.createuserandpass' | translate }} +

{{ 'core.login.createuserandpass' | translate }}

@@ -119,7 +119,7 @@ - {{ 'core.login.supplyinfo' | translate }} +

{{ 'core.login.supplyinfo' | translate }}

@@ -169,7 +169,7 @@ - {{ category.name }} +

{{ category.name }}

@@ -179,7 +179,7 @@ - {{ 'core.login.security_question' | translate }} +

{{ 'core.login.security_question' | translate }}

@@ -188,7 +188,7 @@ - {{ 'core.login.policyagreement' | translate }} +

{{ 'core.login.policyagreement' | translate }}

@@ -217,9 +217,9 @@ -

+

-

+

diff --git a/src/core/features/login/pages/forgotten-password/forgotten-password.html b/src/core/features/login/pages/forgotten-password/forgotten-password.html index 5845cab40..615048cae 100644 --- a/src/core/features/login/pages/forgotten-password/forgotten-password.html +++ b/src/core/features/login/pages/forgotten-password/forgotten-password.html @@ -16,7 +16,7 @@
- {{ 'core.login.searchby' | translate }} +

{{ 'core.login.searchby' | translate }}

diff --git a/src/core/features/sitehome/pages/index/index.html b/src/core/features/sitehome/pages/index/index.html index 06a069a11..f53a91281 100644 --- a/src/core/features/sitehome/pages/index/index.html +++ b/src/core/features/sitehome/pages/index/index.html @@ -31,7 +31,9 @@ - + + + diff --git a/src/core/features/user/pages/about/about.html b/src/core/features/user/pages/about/about.html index 43940dd36..276186cdc 100644 --- a/src/core/features/user/pages/about/about.html +++ b/src/core/features/user/pages/about/about.html @@ -13,7 +13,7 @@ - {{ 'core.user.contact' | translate}} +

{{ 'core.user.contact' | translate}}

{{ 'core.user.email' | translate }}

@@ -60,7 +60,7 @@
- {{ 'core.userdetails' | translate}} +

{{ 'core.userdetails' | translate}}

{{ 'core.user.webpage' | translate}}

@@ -80,7 +80,7 @@
- {{ 'core.user.description' | translate}} +

{{ 'core.user.description' | translate}}

From 82f40e60e91e716421c18c3df82115266730c4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 6 May 2021 15:15:55 +0200 Subject: [PATCH 10/11] MOBILE-3745 github: Manually run migration checks --- .github/workflows/migration.yml | 18 ++++++++++++++++++ .github/workflows/testing.yml | 2 -- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/migration.yml diff --git a/.github/workflows/migration.yml b/.github/workflows/migration.yml new file mode 100644 index 000000000..3e3698ad5 --- /dev/null +++ b/.github/workflows/migration.yml @@ -0,0 +1,18 @@ +name: Migration checks + +on: workflow_dispatch + +jobs: + checks: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: '12.x' + - run: npm ci + - run: result=$(find src -type f -iname '*.html' -exec sh -c 'cat {} | tr "\n" " " | grep -Eo "class=\"[^\"]+\"[^>]+class=\"" ' \; | wc -l); test $result -eq 0 + - run: npx tslint -c ionic-migration.json -p tsconfig.json diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 893185ef7..bb24d1c75 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -14,9 +14,7 @@ jobs: with: node-version: '12.x' - run: npm ci - - run: result=$(find src -type f -iname '*.html' -exec sh -c 'cat {} | tr "\n" " " | grep -Eo "class=\"[^\"]+\"[^>]+class=\"" ' \; | wc -l); test $result -eq 0 - run: npm run lint - - run: npx tslint -c ionic-migration.json -p tsconfig.json - run: npm run test:ci - run: npm run build:prod - run: result=$(npx check-es-compat www/*.js 2> /dev/null | grep -v -E "Array\.prototype\.includes|Promise\.prototype\.finally|String\.prototype\.(matchAll|trimRight)|globalThis" | grep -Po "(?<=error).*?(?=\s+ecmascript)" | wc -l); test $result -eq 0 From 4f9f69429880f7854ded4a63c075d16672a650ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 7 May 2021 10:31:06 +0200 Subject: [PATCH 11/11] MOBILE-3745 a11y: Fix most a11y color issues by changing base primary --- .../messages/pages/discussion/discussion.scss | 4 +- src/addons/mod/data/data-forms.scss | 2 +- .../mod/forum/components/index/index.scss | 2 +- .../mod/forum/components/post/post.scss | 2 +- .../components/progress-bar/progress-bar.scss | 4 +- .../components/user-avatar/user-avatar.scss | 7 +- .../components/format/core-course-format.html | 12 +- .../section-selector/section-selector.html | 6 +- .../rich-text-editor/rich-text-editor.scss | 4 +- .../features/settings/pages/site/site.html | 4 +- src/core/services/app.ts | 2 +- src/theme/globals.mixins.scss | 12 ++ src/theme/globals.variables.scss | 130 +++++++++++++--- src/theme/theme.base.scss | 32 ++-- src/theme/theme.dark.scss | 40 ++--- src/theme/theme.light.scss | 139 ++++++++++-------- 16 files changed, 264 insertions(+), 138 deletions(-) diff --git a/src/addons/messages/pages/discussion/discussion.scss b/src/addons/messages/pages/discussion/discussion.scss index 477aa2305..88e6b41f5 100644 --- a/src/addons/messages/pages/discussion/discussion.scss +++ b/src/addons/messages/pages/discussion/discussion.scss @@ -20,11 +20,11 @@ } .addon-messages-unreadfrom { - color: var(--core-color); + color: var(--ion-color-primary); background-color: transparent; margin-top: 6px; ion-icon { - color: var(--core-color); + color: var(--ion-color-primary); background-color: transparent; } } diff --git a/src/addons/mod/data/data-forms.scss b/src/addons/mod/data/data-forms.scss index 365655ab4..02b7e4ec3 100644 --- a/src/addons/mod/data/data-forms.scss +++ b/src/addons/mod/data/data-forms.scss @@ -41,7 +41,7 @@ } &.has-focus { - --input-border-color: var(--core-color); + --input-border-color: var(--ion-color-primary); } &.has-focus.ion-valid { --input-border-color: var(--success); diff --git a/src/addons/mod/forum/components/index/index.scss b/src/addons/mod/forum/components/index/index.scss index 8e3bee0bf..f5baa1efb 100644 --- a/src/addons/mod/forum/components/index/index.scss +++ b/src/addons/mod/forum/components/index/index.scss @@ -3,7 +3,7 @@ :host { .addon-forum-star { - color: var(--core-color); + color: var(--core-star-color); } .addon-mod-forum-discussion.item { diff --git a/src/addons/mod/forum/components/post/post.scss b/src/addons/mod/forum/components/post/post.scss index b9d4db52a..dfb13f112 100644 --- a/src/addons/mod/forum/components/post/post.scss +++ b/src/addons/mod/forum/components/post/post.scss @@ -5,7 +5,7 @@ border-bottom: 1px solid var(--addon-forum-border-color); .addon-forum-star { - color: var(--core-color); + color: var(--core-star-color); } ion-card-header .item { diff --git a/src/core/components/progress-bar/progress-bar.scss b/src/core/components/progress-bar/progress-bar.scss index e82f88fa1..11b6a5f70 100644 --- a/src/core/components/progress-bar/progress-bar.scss +++ b/src/core/components/progress-bar/progress-bar.scss @@ -1,5 +1,6 @@ :host { display: flex; + width: 100%; .core-progress-text { line-height: 40px; @@ -16,7 +17,8 @@ margin: 16px 0; padding: 0; display: block; - width: calc(100% - 55px); + width: 100%; + flex: 1; &[value]::-webkit-progress-bar { background-color: var(--background); diff --git a/src/core/components/user-avatar/user-avatar.scss b/src/core/components/user-avatar/user-avatar.scss index ae730b0e0..9047b0a10 100644 --- a/src/core/components/user-avatar/user-avatar.scss +++ b/src/core/components/user-avatar/user-avatar.scss @@ -28,9 +28,10 @@ } &.core-bar-button-image img { padding: 0; - width: var(--core-toolbar-button-image-width); - height: var(--core-toolbar-button-image-width); - max-width: var(--core-toolbar-button-image-width); + width: var(--core-header-toolbar-button-image-size); + height: var(--core-header-toolbar-button-image-size); + max-width: var(--core-header-toolbar-button-image-size); + max-height: var(--core-header-toolbar-button-image-size); border-radius: 50%; } diff --git a/src/core/features/course/components/format/core-course-format.html b/src/core/features/course/components/format/core-course-format.html index c721d5f5f..fb53e6f8c 100644 --- a/src/core/features/course/components/format/core-course-format.html +++ b/src/core/features/course/components/format/core-course-format.html @@ -49,15 +49,15 @@ - {{ 'core.course.hiddenfromstudents' | translate }} - {{ 'core.notavailable' | translate }} - + @@ -125,13 +125,13 @@

- + {{ 'core.course.hiddenfromstudents' | translate }} - + {{ 'core.notavailable' | translate }} - + diff --git a/src/core/features/course/components/section-selector/section-selector.html b/src/core/features/course/components/section-selector/section-selector.html index a439b13e0..ce654d4df 100644 --- a/src/core/features/course/components/section-selector/section-selector.html +++ b/src/core/features/course/components/section-selector/section-selector.html @@ -22,15 +22,15 @@ - {{ 'core.course.hiddenfromstudents' | translate }} - {{ 'core.notavailable' | translate }} - + diff --git a/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss b/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss index e8edb8955..75d85bb6b 100644 --- a/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss +++ b/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss @@ -37,13 +37,13 @@ .core-rte-info-message { padding: 5px; - border-top: 1px solid var(--ion-color-secondary); + border-top: 1px solid var(--ion-color-info); background: var(--background); flex-shrink: 1; font-size: 1.1rem; .icon { - color: var(--ion-color-secondary); + color: var(--ion-color-info); } } } diff --git a/src/core/features/settings/pages/site/site.html b/src/core/features/settings/pages/site/site.html index e3ed64325..e24c54f74 100644 --- a/src/core/features/settings/pages/site/site.html +++ b/src/core/features/settings/pages/site/site.html @@ -41,7 +41,7 @@

{{ 'core.settings.spaceusage' | translate }}

{{ spaceUsage.spaceUsage | coreBytesToSize }}

@@ -55,7 +55,7 @@

{{ 'core.settings.synchronizenow' | translate }} + color="info" [attr.aria-label]="'core.info' | translate" (click)="showSyncInfo()">

diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 5045ad68f..6f233f83f 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -623,7 +623,7 @@ export class CoreAppProvider { setStatusBarColor(color?: string): void { if (!color) { // Get the default color to reset it. - color = getComputedStyle(document.documentElement).getPropertyValue('--ion-statusbar-background').trim(); + color = getComputedStyle(document.documentElement).getPropertyValue('--core-header-toolbar-background').trim(); } // Make darker on Android. diff --git a/src/theme/globals.mixins.scss b/src/theme/globals.mixins.scss index 45233a3a1..59a3476d6 100644 --- a/src/theme/globals.mixins.scss +++ b/src/theme/globals.mixins.scss @@ -214,3 +214,15 @@ } } } + + +// Color mixins. +@function get_brightness($color) { + @return (red($color) + green($color) + blue($color)) / 3; +} + +@function get_contrast_color($color) { + $brightness: get_brightness($color); + + @return if($brightness < 127, white, black); +} diff --git a/src/theme/globals.variables.scss b/src/theme/globals.variables.scss index 327dd8f23..23e5aa06d 100644 --- a/src/theme/globals.variables.scss +++ b/src/theme/globals.variables.scss @@ -7,8 +7,8 @@ /* * App colors */ -$black: #3a3a3a !default; // Headings, standard text. -$gray-darker: #626262 !default; // Text (emphasis-detail), placeholder, background. +$black: #282828 !default; // Headings, standard text. +$gray-darker: #686566 !default; // Text (emphasis-detail), placeholder, background. $gray-dark: #9e9e9e !default; // Borders (never text). $gray: #dddddd !default; $gray-light: #e9e9e9 !default; // Background. @@ -40,19 +40,24 @@ $orange-light: lighten($orange, 10%) !default; $yellow-light: mix($yellow, white, 20%) !default; $yellow-dark: mix($yellow, black, 40%) !default; -$core-color: $orange !default; -$core-color-light: lighten($core-color, 10%) !default; -$core-color-dark: darken($core-color, 10%) !default; +$brand-color: $orange !default; +$brand-color-light: lighten($brand-color, 10%) !default; +$brand-color-dark: darken($brand-color, 10%) !default; -$text-color: $black !default; -$link-color: $blue !default; -$background-color: $gray-light !default; -$subdued-text-color: $gray-darker !default; +$text-color: $black !default; +$text-color-dark: $white !default; +$link-color: $blue !default; +$background-color: $gray-light !default; +$background-color-dark: #1e1e1e !default; +$subdued-text-color: $gray-darker !default; + +$ion-item-background: $white !default; +$ion-item-background-dark: #1e1e1e !default; $core-online-color: #5cb85c !default; -$primary: $core-color !default; -$secondary: $blue !default; +$primary: $blue !default; +$secondary: $brand-color !default; $tertiary: $turquoise !default; $danger: $red !default; $warning: $yellow !default; @@ -65,61 +70,61 @@ $dark: $black !default; $colors: ( primary: ( base: $primary, - contrast: #fff, + contrast: get_contrast_color($primary), shade: get-color-shade($primary), tint: get-color-tint($primary) ), secondary: ( base: $secondary, - contrast: #fff, + contrast: get_contrast_color($secondary), shade: get-color-shade($secondary), tint: get-color-tint($secondary) ), tertiary: ( base: $tertiary, - contrast: #fff, + contrast: get_contrast_color($tertiary), shade: get-color-shade($tertiary), tint: get-color-tint($tertiary) ), success: ( base: $success, - contrast: #fff, + contrast: get_contrast_color($success), shade: get-color-shade($success), tint: get-color-tint($success) ), warning: ( base: $warning, - contrast: #000, + contrast: get_contrast_color($warning), shade: get-color-shade($warning), tint: get-color-tint($warning) ), danger: ( base: $danger, - contrast: #fff, + contrast: get_contrast_color($danger), shade: get-color-shade($danger), tint: get-color-tint($danger) ), info: ( base: $info, - contrast: #fff, + contrast: get_contrast_color($info), shade: get-color-shade($info), tint: get-color-tint($info) ), light: ( base: $light, - contrast: #000, + contrast: get_contrast_color($light), shade: get-color-shade($light), tint: get-color-tint($light) ), medium: ( base: $medium, - contrast: #fff, + contrast: get_contrast_color($medium), shade: get-color-shade($medium), tint: get-color-tint($medium) ), dark: ( base: $dark, - contrast: #fff, + contrast: get_contrast_color($dark), shade: get-color-shade($dark), tint: get-color-tint($dark) ) @@ -170,3 +175,86 @@ $breakpoint-tablet: map-get($screen-breakpoints, tablet), !default; $z-index-overlay: 1001; $z-index-overlay-wrapper: 10; + +// Top header bar. +$toolbar-background: $brand-color !default; +$toolbar-color: get_contrast_color($toolbar-background) !default; +$toolbar-button-image-size: 44px !default; + +// Bottom tab bar. +$bottom-tabs-background: $white !default; +$bottom-tabs-color: $gray-darker !default; +$bottom-tabs-color-selected: $primary !default; + +$core-text-hightlight-background-color: lighten($blue, 40%) !default; + +$action-sheet-title-color: $primary !default; +$action-sheet-selected: $primary !default; + +$core-tabs-background: $white !default; +$core-tab-background: $core-tabs-background !default; +$core-tab-color: $gray-dark !default; +$core-tab-border-color: $gray !default; +$core-tab-color-active: $primary !default; +$core-tab-border-color-active: $core-tab-color-active !default; + +$core-tabs-background-dark: $black !default; +$core-tab-background-dark: $core-tabs-background-dark !default; +$core-tab-color-dark: $white !default; +$core-tab-border-color-dark: $gray-light !default; + +$core-progressbar-color: $primary !default; +$core-progressbar-text-color: $gray-darker !default; +$core-progressbar-background: $gray-lighter !default; +$core-progressbar-height: 8px !default; + +$core-progressbar-text-color-dark: $gray-lighter !default; + +$core-side-blocks-max-width: 30% !default; +$core-side-blocks-min-width: 280px !default; + +//// +$core-combobox-color: $primary !default; + +$core-selected-item-color: $primary !default; +$core-selected-item-border-width: 5px !default; + +$core-login-background: $white !default; +$core-login-text-color: $black !default; + +$core-login-background-dark: $black !default; +$core-login-text-color-dark: $white !default; + +$core-star-color: $brand-color !default; + +$core-large-avatar-size: 90px !default; +$core-avatar-size: 40px !default; + +$core-send-message-input-background: $gray !default; +$core-send-message-input-color: $black !default; + +$addon-calendar-event-category-color: $purple !default; +$addon-calendar-event-course-color: $red !default; +$addon-calendar-event-group-color: $yellow !default; +$addon-calendar-event-user-color: $blue !default; +$addon-calendar-event-site-color: $green !default; +$addon-calendar-today-bgcolor: $primary !default; +$addon-calendar-today-color: $white !default; +$addon-calendar-border-color: $gray !default; + +$addon-messages-message-bg: $white !default; +$addon-messages-message-activated-bg: $gray-light !default; +$addon-messages-message-note-text: $gray-dark !default; +$addon-messages-message-note-font-size: 75% !default; +$addon-messages-message-mine-bg: $gray-light !default; +$addon-messages-message-mine-activated-bg: $gray !default; +$addon-messages-avatar-size: 30px !default; +$addon-messages-discussion-badge: $primary !default; +$addon-messages-discussion-badge-text: $white !default; + +$addon-forum-avatar-size: 28px !default; +$addon-forum-border-color: $gray !default; +$addon-forum-highlight-color: $gray-lighter !default; + +$addon-forum-border-color-dark: $gray-darker !default; +$addon-forum-highlight-color-dark: $gray-dark !default; diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 7f7cb6b09..aae067e92 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -50,11 +50,6 @@ ion-item.ion-text-wrap ion-label { white-space: normal !important; } -// Buttons. -.button-clear { - --ion-color-primary: var(--ion-text-color); -} - // It fixes the click on links where ion-ripple-effect is present. .ion-activatable ion-label, .item-multiple-items ion-label { @@ -78,14 +73,24 @@ ion-item.ion-text-wrap ion-label { // Ionic toolbar. ion-toolbar ion-back-button, ion-toolbar .in-toolbar.button-clear { - --color: var(--ion-color-primary-contrast); - --ion-toolbar-color: var(--ion-color-primary-contrast); + --color: var(--core-header-toolbar-color); + --ion-toolbar-color: var(--core-header-toolbar-color); } -ion-header ion-toolbar .button.button-clear { - --color: var(--ion-color-primary-contrast); +ion-header ion-toolbar .button.button-clear, +ion-header ion-toolbar .button.button-solid { + --color: var(--core-header-toolbar-color); + --ion-color-primary: var(--core-header-toolbar-color); } +ion-header ion-toolbar .button.button-clear.button-has-icon-only, +ion-header ion-toolbar .button.button-solid.button-has-icon-only { + --border-radius: 50%; + width: 48px; + height: 48px; +} + + ion-toolbar .core-navbar-button-hidden { display: none !important; } @@ -379,9 +384,10 @@ ion-item img.core-module-icon[slot="start"] { ion-toolbar ion-title img.core-bar-button-image, ion-toolbar ion-title .core-bar-button-image img { padding: 0; - width: var(--core-toolbar-button-image-width); - height: var(--core-toolbar-button-image-width); - max-width: var(--core-toolbar-button-image-width); + width: var(--core-header-toolbar-button-image-size); + height: var(--core-header-toolbar-button-image-size); + max-width: var(--core-header-toolbar-button-image-size); + max-height: var(--core-header-toolbar-button-image-size); border-radius: 50%; } @@ -422,7 +428,7 @@ ion-select::part(text) { } .core-anchor, core-format-text a { - color: -webkit-link; + color: var(--core-link-color); cursor: pointer; text-decoration: underline; } diff --git a/src/theme/theme.dark.scss b/src/theme/theme.dark.scss index 096f833ae..25b8887cc 100644 --- a/src/theme/theme.dark.scss +++ b/src/theme/theme.dark.scss @@ -6,11 +6,11 @@ */ :root body.dark { - --ion-background-color: #1e1e1e; - --ion-background-color-rgb: 18,18,18; + --ion-background-color: #{$background-color-dark}; + --ion-background-color-rgb: color-to-rgb-list(var(--ion-background-color)); - --ion-text-color: #ffffff; - --ion-text-color-rgb: 255,255,255; + --ion-text-color: #{$text-color-dark}; + --ion-text-color-rgb: color-to-rgb-list(var(--ion-text-color)); --ion-border-color: #3f3f3f; @@ -51,26 +51,30 @@ --contrast-background: var(--ion-background-color); } - --core-tabs-background: var(--custom-tabs-background, #3a3a3a); - --core-tab-background: var(--custom-tab-background, #3a3a3a); - --core-tab-color: var(--custom-tab-color, var(--white)); - --core-tab-border-color: var(--custom-tab-border-color, var(--gray-light)); - - core-progress-bar { - --text-color: var(--custom-progress-text-color, var(--gray-lighter)); + ion-action-sheet { + .action-sheet-cancel { + --button-color: var(--ion-color-danger-tint); + } } - --ion-item-background: #1e1e1e; + --core-tabs-background: #{$core-tabs-background-dark}; + --core-tab-background: #{$core-tab-background-dark}; + --core-tab-color: #{$core-tab-color-dark}; + --core-tab-border-color: #{$core-tab-border-color-dark}; + + core-progress-bar { + --text-color: #{$core-progressbar-text-color-dark}; + } + + --ion-item-background: #{$ion-item-background-dark}; --ion-item-detail-icon-color: var(--white); ion-item-divider { --background: var(--black); --color: var(--white); } - --core-combobox-background: var(--custom-combobox-background, #3a3a3a); - - --core-login-background: var(--custom-login-background, #3a3a3a); - --core-login-text-color: var(--custom-login-text-color, white); + --core-login-background: #{$core-login-background-dark}; + --core-login-text-color: #{$core-login-text-color-dark}; --core-question-correct-color: var(--green-light); --core-question-correct-color-bg: var(--green-dark); @@ -91,6 +95,6 @@ --core-dd-question-selected-shadow: 2px 2px 4px var(--gray-light); - --addon-forum-border-color: var(--custom-forum-border-color, var(--gray-darker)); - --addon-forum-highlight-color: var(--custom-forum-highlight-color, var(--gray-dark)); + --addon-forum-border-color: #{$addon-forum-border-color-dark}; + --addon-forum-highlight-color: #{$addon-forum-highlight-color-dark}; } diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index d4796e3bf..93b975c55 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -40,9 +40,9 @@ --yellow-dark: #{$yellow-dark}; --purple: #{$purple}; - --core-color: #{$core-color}; - --core-color-light: #{$core-color-light}; - --core-color-dark: #{$core-color-dark}; + --brand-color: #{$brand-color}; + --brand-color-light: #{$brand-color-light}; + --brand-color-dark: #{$brand-color-dark}; --core-online-color: #{$core-online-color}; @@ -69,7 +69,7 @@ --ion-text-color-rgb: 58,58,58; --ion-card-color: var(--ion-text-color); - --text-hightlight-background-color: var(--custom-text-hightlight-background-color, #99c1ed); + --text-hightlight-background-color: #{$core-text-hightlight-background-color}; ion-content { --background: #{$background-color}; @@ -77,30 +77,43 @@ --contrast-background: var(--white); } + --core-bottom-tabs-background: #{$bottom-tabs-background}; + --core-bottom-tabs-color: #{$bottom-tabs-color}; + --core-bottom-tabs-color-selected: #{$bottom-tabs-color-selected}; ion-tab-bar { - --background: var(--custom-bottom-tabs-background, var(--gray-darker)); - --color: var(--custom-bottom-tabs-color, var(--white)); + --background: var(--core-bottom-tabs-background); + --color: var(--core-bottom-tabs-color); + --color-selected: var(--core-bottom-tabs-color-selected); } - --core-toolbar-button-image-width: var(--custom-toolbar-button-image-width, 32px); - --ion-statusbar-background: var(--custom-toolbar-background, var(--ion-color-primary)); + --core-link-color: #{$link-color}; + a { + color: var(--core-link-color); + } + + --core-header-toolbar-button-image-size: #{$toolbar-button-image-size}; + --core-header-toolbar-background: #{$toolbar-background}; + --core-header-toolbar-color: #{$toolbar-color}; ion-toolbar { - --color: var(--custom-toolbar-color, var(--ion-color-primary-contrast)); - --background: var(--ion-statusbar-background); + --color: var(--core-header-toolbar-color); + --background: var(--core-header-toolbar-background); ion-button { --ion-toolbar-color: transparent; - --color: var(--custom-toolbar-color, var(--ion-color-primary-contrast)); + --color: var(--core-header-toolbar-color); } ion-spinner { - --color: var(--custom-toolbar-color, var(--ion-color-primary-contrast)); + --color: var(--core-header-toolbar-color); } } ion-action-sheet { - --action-sheet-shadow-color: var(--custom--action-sheet-shadow-color, 0, 0, 0, 1); - --button-color-selected: var(--custom--action-sheet-selected-color, var(--core-color)); - --title-border-color: var(--custom-title-border-color, var(--gray)); + --button-color-selected: #{$action-sheet-selected}; + --title-border-color: var(--gray); + + .action-sheet-title { + --color: #{$action-sheet-title-color}; + } @media (min-height: 500px) { --max-height: 50%; @@ -112,12 +125,12 @@ } } - --core-tabs-background: var(--custom-tabs-background, var(--white)); - --core-tab-background: var(--custom-tab-background, var(--core-tabs-background)); - --core-tab-color: var(--custom-tab-color, var(--gray-dark)); - --core-tab-border-color: var(--custom-tab-border-color, var(--gray)); - --core-tab-color-active: var(--custom-tab-color-active, var(--core-color)); - --core-tab-border-color-active: var(--custom-tab-border-color-active, var(--core-color)); + --core-tabs-background: #{$core-tabs-background}; + --core-tab-background: #{$core-tab-background}; + --core-tab-color: #{$core-tab-color}; + --core-tab-border-color: #{$core-tab-border-color}; + --core-tab-color-active: #{$core-tab-color-active}; + --core-tab-border-color-active: #{$core-tab-border-color-active}; core-tabs, core-tabs-outlet { --background: var(--core-tabs-background); @@ -131,22 +144,22 @@ } ion-spinner { - --color: var(--core-color); + --color: var(--ion-color-primary); } core-progress-bar { - --height: var(--custom-progress-bar-height, 8px); - --color: var(--custom-progress-color, var(--core-color)); - --text-color: var(--custom-progress-text-color, var(--gray-darker)); - --background: var(--custom-progress-background, var(--gray-lighter)); + --height: #{$core-progressbar-height}; + --color: #{$core-progressbar-color}; + --text-color: #{$core-progressbar-text-color}; + --background: #{$core-progressbar-background}; } core-block-course-blocks { - --side-blocks-max-width: var(--custom-side-blocks-max-width, 30%); - --side-blocks-min-width: var(--custom-side-blocks-min-width, 280px); + --side-blocks-max-width: #{$core-side-blocks-max-width}; + --side-blocks-min-width: #{$core-side-blocks-min-width}; } - --ion-item-background: var(--white); + --ion-item-background: #{$ion-item-background}; --ion-item-detail-icon-color: var(--gray-darker); --ion-item-detail-icon-font-size: 20px; --ion-item-detail-icon-opacity: 1; @@ -162,50 +175,50 @@ min-height: calc(var(--a11y-min-target-size) + 8px); } - --core-combobox-background: var(--custom-combobox-background, var(--ion-item-background)); - --core-combobox-color: var(--custom-combobox-color, var(--ion-color-primary)); + --core-combobox-background: var(--ion-item-background); + --core-combobox-color: #{$core-combobox-color}; - --selected-item-color: var(--custom-selected-item-color, var(--core-color)); - --selected-item-border-width: var(--custom-selected-item-border-width, 5px); + --selected-item-color: #{$core-selected-item-color}; + --selected-item-border-width: #{$core-selected-item-border-width}; - --core-login-background: var(--custom-login-background, var(--white)); - --core-login-text-color: var(--custom-login-text-color, var(--black)); + --core-login-background: #{$core-login-background}; + --core-login-text-color: #{$core-login-text-color}; - --core-star-color: var(--custom-star-color, var(--core-color)); + --core-star-color: #{$core-star-color}; - --core-large-avatar-size: var(--custom-large-avatar-size, 90px); - --core-avatar-size: var(--custom-avatar-size, 40px); + --core-large-avatar-size: #{$core-large-avatar-size}; + --core-avatar-size: #{$core-avatar-size}; - --core-send-message-input-background: var(--custom-send-message-input-background, var(--gray)); - --core-send-message-input-color: var(--custom-send-message-input-color, var(--black)); + --core-send-message-input-background: #{$core-send-message-input-background}; + --core-send-message-input-color: #{$core-send-message-input-color}; - --addon-calendar-event-category-color: var(--custom-calendar-event-category-color, var(--purple)); - --addon-calendar-event-course-color: var(--custom-calendar-event-course-color, var(--red)); - --addon-calendar-event-group-color: var(--custom-calendar-event-group-color, var(--yellow)); - --addon-calendar-event-user-color: var(--custom-calendar-event-user-color, var(--blue)); - --addon-calendar-event-site-color: var(--custom-calendar-event-site-color, var(--green)); - --addon-calendar-today-bgcolor: var(--custom-calendar-today-bgcolor, var(--core-color)); - --addon-calendar-today-color: var(--custom-calendar-today-color, var(--white)); - --addon-calendar-border-color: var(--custom-calendar-border-color, var(--gray)); + --addon-calendar-event-category-color: #{$addon-calendar-event-category-color}; + --addon-calendar-event-course-color: #{$addon-calendar-event-course-color}; + --addon-calendar-event-group-color: #{$addon-calendar-event-group-color}; + --addon-calendar-event-user-color: #{$addon-calendar-event-user-color}; + --addon-calendar-event-site-color: #{$addon-calendar-event-site-color}; + --addon-calendar-today-bgcolor: #{$addon-calendar-today-bgcolor}; + --addon-calendar-today-color: #{$addon-calendar-today-color}; + --addon-calendar-border-color: #{$addon-calendar-border-color}; - --addon-messages-message-bg: var(--custom-messages-message-bg, var(--white)); - --addon-messages-message-activated-bg: var(--custom-messages-message-activated-bg, var(--gray-light)); - --addon-messages-message-note-text: var(--custom-messages-message-note-text, var(--gray-dark)); - --addon-messages-message-note-font-size: var(--custom-messages-message-note-font-size, 75%); - --addon-messages-message-mine-bg: var(--custom-messages-message-mine-bg, var(--gray-light)); - --addon-messages-message-mine-activated-bg: var(--custom-messages-message-mine-activated-bg, var(--gray)); - --addon-messages-avatar-size: var(--custom-messages-avatar-size, 30px); - --addon-messages-discussion-badge: var(--custom-messages-discussion-badge, var(--core-color)); - --addon-messages-discussion-badge-text: var(--custom-messages-discussion-badge-text, var(--white)); + --addon-messages-message-bg: #{$addon-messages-message-bg}; + --addon-messages-message-activated-bg: #{$addon-messages-message-activated-bg}; + --addon-messages-message-note-text: #{$addon-messages-message-note-text}; + --addon-messages-message-note-font-size: #{$addon-messages-message-note-font-size}; + --addon-messages-message-mine-bg: #{$addon-messages-message-mine-bg}; + --addon-messages-message-mine-activated-bg: #{$addon-messages-message-mine-activated-bg}; + --addon-messages-avatar-size: #{$addon-messages-avatar-size}; + --addon-messages-discussion-badge: #{$addon-messages-discussion-badge}; + --addon-messages-discussion-badge-text: #{$addon-messages-discussion-badge-text}; - --addon-forum-avatar-size: var(--custom-forum-avatar-size, 28px); - --addon-forum-border-color: var(--custom-forum-border-color, var(--gray)); - --addon-forum-highlight-color: var(--custom-forum-highlight-color, var(--gray-lighter)); + --addon-forum-avatar-size: #{$addon-forum-avatar-size}; + --addon-forum-border-color: #{$addon-forum-border-color}; + --addon-forum-highlight-color: #{$addon-forum-highlight-color}; - --drop-shadow: var(--custom-drop-shadow, 0, 0, 0, 0.2); + --drop-shadow: 0, 0, 0, 0.2; - --core-menu-box-shadow-end: var(--custom-menu-box-shadow-end, -4px 0px 16px rgba(0, 0, 0, 0.18)); - --core-menu-box-shadow-start: var(--custom-menu-box-shadow-start, 4px 0px 16px rgba(0, 0, 0, 0.18)); + --core-menu-box-shadow-end: -4px 0px 16px rgba(0, 0, 0, 0.18); + --core-menu-box-shadow-start: 4px 0px 16px rgba(0, 0, 0, 0.18); --core-question-correct-color: var(--green-dark); --core-question-correct-color-bg: var(--green-light);