diff --git a/scripts/langindex.json b/scripts/langindex.json index 7ca7eb8c2..97ebde71e 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -281,6 +281,7 @@ "addon.messages.noncontacts": "message", "addon.messages.nousersfound": "local_moodlemobileapp", "addon.messages.numparticipants": "message", + "addon.messages.pendingcontactrequests": "message", "addon.messages.removecontact": "message", "addon.messages.removecontactconfirm": "message", "addon.messages.removefromfavourites": "message", @@ -301,6 +302,8 @@ "addon.messages.unblockuser": "message", "addon.messages.unblockuserconfirm": "message", "addon.messages.unmuteconversation": "message", + "addon.messages.unreadconversations": "message", + "addon.messages.unreadmessages": "message", "addon.messages.useentertosend": "message", "addon.messages.useentertosenddescdesktop": "local_moodlemobileapp", "addon.messages.useentertosenddescmac": "local_moodlemobileapp", @@ -1450,6 +1453,7 @@ "core.course.completion_automatic:todo": "course", "core.course.completion_manual:aria:done": "course", "core.course.completion_manual:aria:markdone": "course", + "core.course.completion_manual:done": "course", "core.course.completion_manual:markdone": "course", "core.course.completion_setby:auto:done": "course", "core.course.completion_setby:auto:todo": "course", @@ -1748,6 +1752,7 @@ "core.hasdatatosync": "local_moodlemobileapp", "core.help": "moodle", "core.hide": "moodle", + "core.hideadvanced": "form", "core.hour": "moodle", "core.hours": "moodle", "core.humanreadablesize": "local_moodlemobileapp", @@ -2114,6 +2119,7 @@ "core.sharedfiles.sharedfiles": "local_moodlemobileapp", "core.sharedfiles.successstorefile": "local_moodlemobileapp", "core.show": "moodle", + "core.showadvanced": "form", "core.showless": "form", "core.showmore": "form", "core.site": "moodle", diff --git a/src/addons/calendar/components/calendar/addon-calendar-calendar.html b/src/addons/calendar/components/calendar/addon-calendar-calendar.html index d80b34e36..ccae4c3f1 100644 --- a/src/addons/calendar/components/calendar/addon-calendar-calendar.html +++ b/src/addons/calendar/components/calendar/addon-calendar-calendar.html @@ -18,7 +18,7 @@ - {{ periodName }} + {{ periodName }} @@ -29,58 +29,84 @@ - - - - - {{ day.shortname | translate }} - {{ day.fullname | translate }} - - + + + + + + {{ day.fullname | translate }} + {{ day.shortname | translate }} + {{ day.fullname | translate }} + + + + - - - - - - {{ day.mday }} - - - - - - - - - - - - - {{ event.timestart * 1000 | coreFormatDate: timeFormat }} - - - - {{ 'addon.calendar.type' + event.formattedType | translate }} - {{ event.iconTitle }} - - {{event.name}} - - - 4" class="addon-calendar-day-more"> - {{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }} + + + + + + + {{ day.mday }} + {{ day.periodName | translate }} - - - - - + + + + + + + + + + + + + {{ event.timestart * 1000 | coreFormatDate: timeFormat }} + + + + + {{ 'addon.calendar.type' + event.formattedType | translate }} + {{ event.iconTitle }} + + {{event.name}} + + + 4" class="addon-calendar-day-more"> + {{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }} + + + + + + + diff --git a/src/addons/calendar/components/calendar/calendar.scss b/src/addons/calendar/components/calendar/calendar.scss index 848d6357c..5e22f595f 100644 --- a/src/addons/calendar/components/calendar/calendar.scss +++ b/src/addons/calendar/components/calendar/calendar.scss @@ -71,6 +71,7 @@ overflow: hidden; white-space: nowrap; color: var(--text-color); + min-height: auto; &.addon-calendar-event-past { opacity: 0.5; @@ -102,6 +103,7 @@ .addon-calendar-weekday { border-bottom: 1px solid var(--addon-calendar-border-color); + font-weight: bold; } .addon-calendar-day-events { diff --git a/src/addons/calendar/components/calendar/calendar.ts b/src/addons/calendar/components/calendar/calendar.ts index 34eb7ee31..c6e97072a 100644 --- a/src/addons/calendar/components/calendar/calendar.ts +++ b/src/addons/calendar/components/calendar/calendar.ts @@ -87,7 +87,6 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro constructor( differs: KeyValueDiffers, ) { - this.currentSiteId = CoreSites.getCurrentSiteId(); if (CoreLocalNotifications.isAvailable()) { @@ -233,6 +232,10 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro this.weeks.forEach((week) => { week.days.forEach((day) => { + day.periodName = CoreTimeUtils.userDate( + new Date(this.year!, this.month! - 1, day.mday).getTime(), + 'core.strftimedaydate', + ); day.eventsFormated = day.eventsFormated || []; day.filteredEvents = day.filteredEvents || []; day.events.forEach((event) => { @@ -372,7 +375,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro * @param calendarEvent Calendar event.. * @param event Mouse event. */ - eventClicked(calendarEvent: AddonCalendarEventToDisplay, event: MouseEvent): void { + eventClicked(calendarEvent: AddonCalendarEventToDisplay, event: Event): void { this.onEventClicked.emit(calendarEvent.id); event.stopPropagation(); } diff --git a/src/addons/calendar/pages/edit-event/edit-event.html b/src/addons/calendar/pages/edit-event/edit-event.html index 78c05e1fb..fb22f3bd9 100644 --- a/src/addons/calendar/pages/edit-event/edit-event.html +++ b/src/addons/calendar/pages/edit-event/edit-event.html @@ -16,9 +16,7 @@ - - {{ 'addon.calendar.eventname' | translate }} - + {{ 'addon.calendar.eventname' | translate }} @@ -27,11 +25,7 @@ - - - {{ 'core.date' | translate }} - - + {{ 'core.date' | translate }} @@ -40,13 +34,15 @@ - - - {{ 'addon.calendar.eventkind' | translate }} - + + {{ 'addon.calendar.eventkind' | translate }} - + {{eventTypes[0].name | translate }} + 1" + formControlName="eventtype" + interface="action-sheet" + > {{ type.name | translate }} @@ -55,12 +51,8 @@ - - - {{ 'core.category' | translate }} - - - {{ 'core.category' | translate }} + {{ category.name }} @@ -70,12 +62,8 @@ - - - {{ 'core.course' | translate }} - - - {{ 'core.course' | translate }} + {{ course.fullname }} @@ -85,12 +73,8 @@ - - - {{ 'core.course' | translate }} - - - {{ 'core.course' | translate }} + @@ -104,12 +88,8 @@ 0"> - - - {{ 'core.group' | translate }} - - - {{ 'core.group' | translate }} + {{ group.name }} @@ -121,23 +101,20 @@ - + {{ 'core.showmore' | translate }} {{ 'core.showless' | translate }} - + - - {{ 'core.description' | translate }} - - {{ 'core.description' | translate }} + @@ -154,9 +131,7 @@ - - {{ 'addon.calendar.eventduration' | translate }} - + {{ 'addon.calendar.eventduration' | translate }} @@ -200,9 +175,7 @@ - - {{ 'addon.calendar.repeatedevents' | translate }} - + {{ 'addon.calendar.repeatedevents' | translate }} diff --git a/src/addons/calendar/services/calendar.ts b/src/addons/calendar/services/calendar.ts index c1a01b8ec..b3b00e040 100644 --- a/src/addons/calendar/services/calendar.ts +++ b/src/addons/calendar/services/calendar.ts @@ -2026,6 +2026,7 @@ export type AddonCalendarWeekDay = AddonCalendarDay & { ispast?: boolean; // Calculated in the app. Whether the day is in the past. filteredEvents?: AddonCalendarEventToDisplay[]; // Calculated in the app. Filtered events. eventsFormated?: AddonCalendarEventToDisplay[]; // Events. + periodName?: string; }; /** diff --git a/src/addons/messages/lang.json b/src/addons/messages/lang.json index 0b728d3e9..a6c556cda 100644 --- a/src/addons/messages/lang.json +++ b/src/addons/messages/lang.json @@ -51,6 +51,7 @@ "noncontacts": "Non-contacts", "nousersfound": "No users found", "numparticipants": "{{$a}} participants", + "pendingcontactrequests": "There are {{$a}} pending contact requests", "removecontact": "Remove contact", "removecontactconfirm": "Are you sure you want to remove {{$a}} from your contacts?", "removefromfavourites": "Unstar conversation", @@ -71,6 +72,8 @@ "unblockuser": "Unblock user", "unblockuserconfirm": "Are you sure you want to unblock {{$a}}?", "unmuteconversation": "Unmute", + "unreadconversations": "There are {{$a}} unread conversations", + "unreadmessages": "There are {{$a}} unread messages", "useentertosend": "Use enter to send", "useentertosenddescdesktop": "If disabled, you can use Ctrl+Enter to send the message.", "useentertosenddescmac": "If disabled, you can use Cmd+Enter to send the message.", @@ -81,4 +84,4 @@ "you": "You:", "youhaveblockeduser": "You have blocked this user.", "yourcontactrequestpending": "Your contact request is pending with {{$a}}" -} \ No newline at end of file +} diff --git a/src/addons/messages/pages/group-conversations/group-conversations.html b/src/addons/messages/pages/group-conversations/group-conversations.html index 518656e04..5a2a475c6 100644 --- a/src/addons/messages/pages/group-conversations/group-conversations.html +++ b/src/addons/messages/pages/group-conversations/group-conversations.html @@ -25,22 +25,36 @@ - + {{ 'addon.messages.contacts' | translate }} - 0" slot="end">{{contactRequestsCount}} + 0" slot="end" aria-hidden="true">{{contactRequestsCount}} + 0" class="sr-only"> + {{ 'addon.messages.pendingcontactrequests' | translate:{$a: contactRequestsCount} }} + - + [attr.aria-expanded]="favourites.expanded" + aria-controls="addon-messages-groupconversations-favourite" + role="heading" + detail="false" + > {{ 'core.favourites' | translate }} ({{ favourites.count }}) - {{ favourites.unread }} - - + {{ favourites.unread }} + + {{ 'addon.messages.unreadconversations' | translate:{$a: favourites.unread} }} + + + @@ -55,15 +69,27 @@ - + [attr.aria-expanded]="group.expanded" + aria-controls="addon-messages-groupconversations-group" + role="heading" + detail="false" + > {{ 'addon.messages.groupconversations' | translate }} ({{ group.count }}) - {{ group.unread }} - - + {{ group.unread }} + + {{ 'addon.messages.unreadconversations' | translate:{$a: group.unread} }} + + + @@ -77,17 +103,29 @@ - + [attr.aria-expanded]="individual.expanded" + aria-controls="addon-messages-groupconversations-individual" + role="heading" + detail="false" + > {{ 'addon.messages.individualconversations' | translate }} ({{ individual.count }}) - {{ individual.unread }} - - + {{ individual.unread }} + + {{ 'addon.messages.unreadconversations' | translate:{$a: individual.unread} }} + + + @@ -144,7 +182,10 @@ 0 || conversation.unreadcount" slot="end"> - 0">{{ conversation.unreadcount }} + 0" aria-label="true">{{ conversation.unreadcount }} + 0" class="sr-only"> + {{ 'addon.messages.unreadmessages' | translate:{$a: conversation.unreadcount} }} + 0">{{conversation.lastmessagedate | coreDateDayOrTime}} diff --git a/src/addons/messages/services/handlers/mainmenu.ts b/src/addons/messages/services/handlers/mainmenu.ts index 4461ee4f9..2f8021415 100644 --- a/src/addons/messages/services/handlers/mainmenu.ts +++ b/src/addons/messages/services/handlers/mainmenu.ts @@ -47,6 +47,7 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler, class: 'addon-messages-handler', showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled. badge: '', + badgeA11yText: 'addon.messages.unreadconversations', loading: true, }; diff --git a/src/addons/mod/forum/components/edit-post/edit-post.html b/src/addons/mod/forum/components/edit-post/edit-post.html index 4543b75f4..812574c03 100644 --- a/src/addons/mod/forum/components/edit-post/edit-post.html +++ b/src/addons/mod/forum/components/edit-post/edit-post.html @@ -25,18 +25,25 @@ (contentChanged)="onMessageChange($event)"> - + {{ 'addon.mod_forum.advanced' | translate }} - - + + 0" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [allowOffline]="true" [files]="replyData.files" [component]="component" [componentId]="forum.cmid"> - + diff --git a/src/addons/mod/forum/components/post/post.html b/src/addons/mod/forum/components/post/post.html index 6e99172ef..e149dba0d 100644 --- a/src/addons/mod/forum/components/post/post.html +++ b/src/addons/mod/forum/components/post/post.html @@ -122,20 +122,26 @@ 0"> - + {{ '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 f28a931a9..999ed8eae 100644 --- a/src/addons/mod/forum/pages/new-discussion/new-discussion.html +++ b/src/addons/mod/forum/pages/new-discussion/new-discussion.html @@ -31,13 +31,21 @@ (contentChanged)="onMessageChange($event)"> - + {{ 'addon.mod_forum.advanced' | translate }} - - + + 1 && accessInfo.cancanposttomygroups"> {{ 'addon.mod_forum.posttomygroups' | translate }} @@ -61,7 +69,7 @@ [files]="newDiscussion.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [component]="component" [componentId]="forum.cmid" [allowOffline]="true"> - + diff --git a/src/addons/notifications/services/handlers/mainmenu.ts b/src/addons/notifications/services/handlers/mainmenu.ts index dbf5aeb1e..5cb4584d2 100644 --- a/src/addons/notifications/services/handlers/mainmenu.ts +++ b/src/addons/notifications/services/handlers/mainmenu.ts @@ -41,6 +41,7 @@ export class AddonNotificationsMainMenuHandlerService implements CoreMainMenuHan class: 'addon-notifications-handler', showBadge: true, badge: '', + badgeA11yText: 'addon.notifications.unreadnotification', loading: true, }; diff --git a/src/core/classes/aria-role-button.ts b/src/core/classes/aria-role-button.ts deleted file mode 100644 index db15a0f9a..000000000 --- a/src/core/classes/aria-role-button.ts +++ /dev/null @@ -1,68 +0,0 @@ -// (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 functionality 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 functionality 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 functionality. - * - * @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 4b8d7819d..474334be0 100644 --- a/src/core/components/user-avatar/core-user-avatar.html +++ b/src/core/components/user-avatar/core-user-avatar.html @@ -4,18 +4,15 @@ [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content onError="this.src='assets/img/user-avatar.png'" - (click)="gotoProfile($event)" + (ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null" - (keydown)="buttonAction.keyDown($event)" - (keyup)="buttonAction.keyUp($event)" [attr.tabindex]="linkProfile ? 0 : null" [class.clickable]="linkProfile" > diff --git a/src/core/components/user-avatar/user-avatar.ts b/src/core/components/user-avatar/user-avatar.ts index 6097a0371..28815d4b4 100644 --- a/src/core/components/user-avatar/user-avatar.ts +++ b/src/core/components/user-avatar/user-avatar.ts @@ -20,7 +20,6 @@ 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". @@ -39,15 +38,13 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { @Input() profileUrl?: string; @Input() linkProfile = true; // Avoid linking to the profile if wanted. @Input() fullname?: string; - @Input() userId?: number; // If provided or found it will be used to link the image to the profile. - @Input() courseId?: number; + @Input() protected userId?: number; // If provided or found it will be used to link the image to the profile. + @Input() protected 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. @@ -55,7 +52,6 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { protected pictureObserver: CoreEventObserver; constructor() { - this.currentUserId = CoreSites.getCurrentSiteUserId(); this.pictureObserver = CoreEvents.on( @@ -67,15 +63,12 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { }, CoreSites.getCurrentSiteId(), ); - - this.buttonAction = new CoreUserAvatarButton(this); } /** * Component being initialized. */ ngOnInit(): void { - this.setFields(); } @@ -137,14 +130,19 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { * @param event Click event. */ gotoProfile(event: Event): void { - if (!this.buttonAction.isAllowed()) { + if (!this.linkProfile || !this.userId) { return; } event.preventDefault(); event.stopPropagation(); - this.buttonAction.click(); + CoreNavigator.navigateToSitePath('user', { + params: { + userId: this.userId, + courseId: this.courseId, + }, + }); } /** @@ -156,32 +154,6 @@ 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. */ diff --git a/src/core/directives/aria-button.ts b/src/core/directives/aria-button.ts new file mode 100644 index 000000000..092c53737 --- /dev/null +++ b/src/core/directives/aria-button.ts @@ -0,0 +1,57 @@ +// (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 { Directive, ElementRef, OnInit, Output, EventEmitter } from '@angular/core'; + +/** + * Directive to emulate click and key actions following aria role button. + */ +@Directive({ + selector: '[ariaButtonClick]', +}) +export class CoreAriaButtonClickDirective implements OnInit { + + protected element: HTMLElement; + + @Output() ariaButtonClick = new EventEmitter(); + + constructor( + element: ElementRef, + ) { + this.element = element.nativeElement; + } + + /** + * Initialize actions. + */ + ngOnInit(): void { + this.element.addEventListener('click', async (event) => { + this.ariaButtonClick.emit(event); + }); + + this.element.addEventListener('keydown', async (event) => { + if ((event.key == ' ' || event.key == 'Enter')) { + event.preventDefault(); + event.stopPropagation(); + } + }); + + this.element.addEventListener('keyup', async (event) => { + if ((event.key == ' ' || event.key == 'Enter')) { + this.ariaButtonClick.emit(event); + } + }); + } + +} diff --git a/src/core/directives/directives.module.ts b/src/core/directives/directives.module.ts index 3cf6d3303..b83ee4534 100644 --- a/src/core/directives/directives.module.ts +++ b/src/core/directives/directives.module.ts @@ -24,6 +24,7 @@ import { CoreLinkDirective } from './link'; import { CoreLongPressDirective } from './long-press'; import { CoreSupressEventsDirective } from './supress-events'; import { CoreUserLinkDirective } from './user-link'; +import { CoreAriaButtonClickDirective } from './aria-button'; @NgModule({ declarations: [ @@ -37,6 +38,7 @@ import { CoreUserLinkDirective } from './user-link'; CoreLongPressDirective, CoreSupressEventsDirective, CoreUserLinkDirective, + CoreAriaButtonClickDirective, ], exports: [ CoreAutoFocusDirective, @@ -49,6 +51,7 @@ import { CoreUserLinkDirective } from './user-link'; CoreLongPressDirective, CoreSupressEventsDirective, CoreUserLinkDirective, + CoreAriaButtonClickDirective, ], }) export class CoreDirectivesModule {} diff --git a/src/core/features/block/components/only-title-block/core-block-only-title.html b/src/core/features/block/components/only-title-block/core-block-only-title.html index 4230b25ec..cfdeb1a04 100644 --- a/src/core/features/block/components/only-title-block/core-block-only-title.html +++ b/src/core/features/block/components/only-title-block/core-block-only-title.html @@ -1,4 +1,3 @@ - + {{ title | translate }} - - + diff --git a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts index b1990106d..0b53152f3 100644 --- a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts +++ b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts @@ -89,8 +89,8 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn protected resetObserver?: CoreEventObserver; protected initHeightInterval?: number; protected isCurrentView = true; - protected toolbarButtonWidth = 40; - protected toolbarArrowWidth = 28; + protected toolbarButtonWidth = 44; + protected toolbarArrowWidth = 44; protected pageInstance: string; protected autoSaveInterval?: number; protected hideMessageTimeout?: number; @@ -100,6 +100,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn protected resizeFunction?: () => Promise; protected selectionChangeFunction?: () => void; protected languageChangedSubscription?: Subscription; + protected resizeObserver?: IntersectionObserver; rteEnabled = false; isPhone = false; @@ -127,6 +128,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn initialSlide: 0, slidesPerView: 6, centerInsufficientSlides: true, + watchSlidesVisibility: true, }; constructor( @@ -136,6 +138,14 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn this.contentChanged = new EventEmitter(); this.element = elementRef.nativeElement as HTMLDivElement; this.pageInstance = 'app_' + Date.now(); // Generate a "unique" ID based on timestamp. + + if ('IntersectionObserver' in window) { + this.resizeObserver = new IntersectionObserver((observerEntry: IntersectionObserverEntry[]) => { + if (observerEntry[0].boundingClientRect.width > 0) { + this.updateToolbarButtons(); + } + }); + } } /** @@ -231,8 +241,12 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn }); this.resizeFunction = this.maximizeEditorSize.bind(this); - this.selectionChangeFunction = this.updateToolbarStyles.bind(this); window.addEventListener('resize', this.resizeFunction!); + + // Start observing the target node for configured mutations + this.resizeObserver?.observe(this.element); + + this.selectionChangeFunction = this.updateToolbarStyles.bind(this); document.addEventListener('selectionchange', this.selectionChangeFunction!); this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, (kbHeight: number) => { @@ -273,7 +287,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn setTimeout(async () => { // Editor is ready, adjust Height if needed. - let height; + let height: number; if (CoreApp.isAndroid()) { // In Android we ignore the keyboard height because it is not part of the web view. @@ -760,6 +774,11 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn * Show the toolbar. */ showToolbar(event: Event): void { + if (!('IntersectionObserver' in window)) { + // Fallback if IntersectionObserver is not supported. + this.updateToolbarButtons(); + } + this.element.classList.add('ion-touched'); this.element.classList.remove('ion-untouched'); this.element.classList.add('has-focus'); @@ -776,7 +795,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn * @param event Event. */ stopBubble(event: Event): void { - if (event.type != 'mouseup' && event.type != 'keyup') { + if (event.type != 'touchend' &&event.type != 'mouseup' && event.type != 'keyup') { event.preventDefault(); } event.stopPropagation(); @@ -840,7 +859,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn * Update the number of toolbar buttons displayed. */ async updateToolbarButtons(): Promise { - if (!this.isCurrentView || !this.toolbar || !this.toolbarSlides) { + if (!this.isCurrentView || !this.toolbar || !this.toolbarSlides || this.element.offsetParent == null) { // Don't calculate if component isn't in current view, the calculations are wrong. return; } @@ -856,7 +875,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn return; } - if (width > length * this.toolbarButtonWidth) { + if (length > 0 && width > length * this.toolbarButtonWidth) { this.slidesOpts = { ...this.slidesOpts, slidesPerView: length }; this.toolbarArrows = false; } else { @@ -1096,6 +1115,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn clearInterval(this.initHeightInterval); clearInterval(this.autoSaveInterval); clearTimeout(this.hideMessageTimeout); + this.resizeObserver?.disconnect(); this.resetObserver?.off(); this.keyboardObserver?.off(); } diff --git a/src/core/features/mainmenu/pages/menu/menu.html b/src/core/features/mainmenu/pages/menu/menu.html index 1b2d09b25..ba8099d71 100644 --- a/src/core/features/mainmenu/pages/menu/menu.html +++ b/src/core/features/mainmenu/pages/menu/menu.html @@ -16,11 +16,14 @@ [selected]="tab.page === selectedTab" [tabindex]="selectedTab == tab.page ? 0 : -1" [attr.aria-controls]="tab.id" - [attr.aria-label]="tab.title | translate" > {{ tab.title | translate }} - {{ tab.badge }} + {{ tab.badge }} + {{ tab.title | translate }} + + {{ tab.badgeA11yText | translate: {$a : tab.badge } }} + {{ 'core.more' | translate }} + {{ 'core.more' | translate }} diff --git a/src/core/features/mainmenu/pages/more/more.html b/src/core/features/mainmenu/pages/more/more.html index 79b029c4e..7811f7d7a 100644 --- a/src/core/features/mainmenu/pages/more/more.html +++ b/src/core/features/mainmenu/pages/more/more.html @@ -30,8 +30,12 @@ {{ handler.title | translate}} - {{handler.badge}} + + {{handler.badge}} + + {{ handler.badgeA11yText | translate: {$a : handler.badge } }} + diff --git a/src/core/features/mainmenu/services/mainmenu-delegate.ts b/src/core/features/mainmenu/services/mainmenu-delegate.ts index 862bb5b55..998550f49 100644 --- a/src/core/features/mainmenu/services/mainmenu-delegate.ts +++ b/src/core/features/mainmenu/services/mainmenu-delegate.ts @@ -65,6 +65,11 @@ export interface CoreMainMenuHandlerData { */ badge?: string; + /** + * Accessibility text to add on the badge. Only used if showBadge is true. + */ + badgeA11yText?: string; + /** * If true, the badge number is being loaded. Only used if showBadge is true. */ diff --git a/src/core/features/siteplugins/components/only-title-block/core-siteplugins-only-title-block.html b/src/core/features/siteplugins/components/only-title-block/core-siteplugins-only-title-block.html index 1a4a3a63c..04564f703 100644 --- a/src/core/features/siteplugins/components/only-title-block/core-siteplugins-only-title-block.html +++ b/src/core/features/siteplugins/components/only-title-block/core-siteplugins-only-title-block.html @@ -1,5 +1,5 @@ - + {{ title | translate }} - + diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 7069ba7aa..d10a8615f 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -185,16 +185,6 @@ ion-alert.core-nohead { } } -// Ionic item divider. -ion-item-divider { - .item-detail-icon { - 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; - } -} - // Ionic list. ion-list.list-md { padding: 0; diff --git a/src/theme/theme.dark.scss b/src/theme/theme.dark.scss index 25b8887cc..ef871c0e6 100644 --- a/src/theme/theme.dark.scss +++ b/src/theme/theme.dark.scss @@ -67,8 +67,8 @@ } --ion-item-background: #{$ion-item-background-dark}; - --ion-item-detail-icon-color: var(--white); - ion-item-divider { + --ion-item-detail-icon-: var(--white); + ion-item-divider, ion-item.divider { --background: var(--black); --color: var(--white); } diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index 5ffcbe996..8bd13bd09 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -172,10 +172,11 @@ } --item-divider-min-height: calc(var(--a11y-min-target-size) + 8px); - ion-item-divider { + ion-item-divider, ion-item.divider { --background: var(--gray-lighter); --color: inherit; - min-height: var(--item-divider-min-height); + --min-height: var(--item-divider-min-height); + min-height: var(--min-height); } --core-combobox-background: var(--ion-item-background);
{{ day.mday }}
4" class="addon-calendar-day-more"> - {{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }} + + + + + + + {{ day.mday }} + {{ day.periodName | translate }} -
+ {{ day.mday }} + {{ day.periodName | translate }}
4" class="addon-calendar-day-more"> + {{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }} +
{{eventTypes[0].name | translate }}