forked from EVOgeek/Vmeda.Online
		
	
						commit
						83261f8ce0
					
				| @ -6,6 +6,14 @@ | ||||
|         ion-card { | ||||
|             height: auto; | ||||
|         } | ||||
| 
 | ||||
|         .ion-text-wrap ion-label { | ||||
|             .item-heading, h2, p { | ||||
|                 white-space: nowrap; | ||||
|                 overflow: hidden; | ||||
|                 text-overflow: ellipsis; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .core-course-module-handler { | ||||
|  | ||||
| @ -20,7 +20,8 @@ import { CoreAnimations } from '@components/animations'; | ||||
|  * Component to show a download button with refresh option, the spinner and the status of it. | ||||
|  * | ||||
|  * Usage: | ||||
|  * <core-download-refresh [status]="status" enabled="true" canTrustDownload="true" action="download()"></core-download-refresh> | ||||
|  * <core-download-refresh [status]="status" [enabled]="true" [canTrustDownload]="true" (action)="download()"> | ||||
|  * </core-download-refresh> | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-download-refresh', | ||||
|  | ||||
| @ -2,20 +2,22 @@ | ||||
| 
 | ||||
|     <ng-container *ngIf="completion.istrackeduser"> | ||||
|         <ng-container *ngIf="completion.state"> | ||||
|             <ion-button color="success" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"> | ||||
|             <ion-button color="success" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)" | ||||
|                 class="ion-text-wrap"> | ||||
|                 <ion-icon name="fas-check" slot="start" aria-hidden="true"></ion-icon> | ||||
|                 {{ 'core.course.completion_manual:done' | translate }} | ||||
|             </ion-button> | ||||
|         </ng-container> | ||||
|         <ng-container *ngIf="!completion.state"> | ||||
|             <ion-button color="dark" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"> | ||||
|             <ion-button color="dark" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)" | ||||
|                 class="ion-text-wrap"> | ||||
|                 {{ 'core.course.completion_manual:markdone' | translate }} | ||||
|             </ion-button> | ||||
|         </ng-container> | ||||
|     </ng-container> | ||||
| 
 | ||||
|     <ng-container *ngIf="!completion.istrackeduser"> | ||||
|         <ion-button disabled="true" color="dark" fill="outline"> | ||||
|         <ion-button disabled="true" color="dark" fill="outline" class="ion-text-wrap"> | ||||
|             {{ 'core.course.completion_manual:markdone' | translate }} | ||||
|         </ion-button> | ||||
|     </ng-container> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <core-loading [hideUntil]="loaded" [fullscreen]="false"> | ||||
|     <ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding" *ngIf="previousModule || nextModule"> | ||||
|     <ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding ion-wrap" *ngIf="previousModule || nextModule"> | ||||
|         <ion-col size="auto"> | ||||
|             <ion-button fill="clear" class="core-course-previous-module" *ngIf="previousModule" (click)="goToActivity(false)" | ||||
|                 [attr.aria-label]="'core.course.gotopreviousactivity' | translate"> | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
|         min-height: var(--core-courseimage-on-course-size); | ||||
|         width: var(--core-courseimage-on-course-size); | ||||
|         min-width: var(--core-courseimage-on-course-size); | ||||
|         --border-radius: var(--core-courseimage-radius); | ||||
|     } | ||||
| 
 | ||||
|     @if ($core-show-courseimage-on-course) { | ||||
|  | ||||
| @ -1,106 +1,92 @@ | ||||
| <ion-card class="core-course-list-item" *ngIf="layout == 'list' || layout == 'listwithenrol'" [class.item-dimmed]="course.hidden"> | ||||
|     <ion-item class="ion-text-wrap" (click)="openCourse()" [class.item-disabled]="course.visible == 0" | ||||
|         [attr.aria-label]="course.displayname || course.fullname" button> | ||||
|         <ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" slot="start" class="course-icon core-course-thumb" | ||||
|             [attr.course-color]="course.color ? null : course.colorNumber" [style.color]="course.color"> | ||||
|         </ion-icon> | ||||
|         <ion-avatar *ngIf="course.courseImage" slot="start" class="core-course-thumb"> | ||||
|             <img [src]="course.courseImage" core-external-content alt="" /> | ||||
|         </ion-avatar> | ||||
|         <ion-label> | ||||
|             <ion-row> | ||||
|                 <ion-col class="ion-align-self-center"> | ||||
|                     <ng-container *ngTemplateOutlet="mainInfo"></ng-container> | ||||
|                 </ion-col> | ||||
|                 <ion-col size="auto" class="ion-align-self-center"> | ||||
|                     <ng-container *ngIf="!isEnrolled"> | ||||
|                         <ion-icon *ngFor="let icon of enrolmentIcons" color="dark" size="small" [name]="icon.icon" | ||||
|                             [title]="icon.label | translate" [attr.aria-label]="icon.label | translate"> | ||||
|                         </ion-icon> | ||||
|                     </ng-container> | ||||
|                     <ng-container *ngIf="isEnrolled"> | ||||
|                         <ng-container *ngTemplateOutlet="download"></ng-container> | ||||
|                     </ng-container> | ||||
|                 </ion-col> | ||||
|             </ion-row> | ||||
|             <p *ngIf="isEnrolled && progress! >= 0 && completionUserTracked !== false"> | ||||
|                 <core-progress-bar [progress]="progress" a11yText="core.courses.aria:courseprogress"></core-progress-bar> | ||||
|             </p> | ||||
|         </ion-label> | ||||
|     </ion-item> | ||||
| </ion-card> | ||||
| <ion-card [class.core-course-list-item]="layout == 'list' || layout == 'listwithenrol'" | ||||
|     [class.core-course-list-card]="layout == 'card' || layout == 'summarycard'" [class.item-dimmed]="course.hidden"> | ||||
| 
 | ||||
| <ion-card [attr.course-color]="course.color ? null : course.colorNumber" *ngIf="layout == 'card' || layout == 'summarycard'" | ||||
|     class="core-course-list-card" [class.item-dimmed]="course.hidden"> | ||||
|     <div (click)="openCourse()" class="core-course-thumb" [class.core-course-color-img]="course.courseImage" | ||||
|         [style.background-color]="course.color"> | ||||
|     <div *ngIf="layout == 'card' || layout == 'summarycard'" (click)="openCourse()" class="core-course-thumb" | ||||
|         [class.core-course-color-img]="course.courseImage"> | ||||
|         <img *ngIf="course.courseImage" [src]="course.courseImage" core-external-content alt="" /> | ||||
|         <ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" class="course-icon"> | ||||
|         </ion-icon> | ||||
|         <ng-container | ||||
|             *ngIf="isEnrolled && ((downloadCourseEnabled && !courseOptionMenuEnabled && showDownload) || courseOptionMenuEnabled)"> | ||||
|             <ng-container *ngTemplateOutlet="download"></ng-container> | ||||
|         </ng-container> | ||||
|     </div> | ||||
|     <ion-item button lines="none" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname" | ||||
|         class="core-course-header" [class.item-disabled]="course.visible == 0" | ||||
|         [class.core-course-only-title]="layout == 'summarycard' || progress < 0 && completionUserTracked === false" detail="false"> | ||||
|         <ion-label class="ion-text-wrap core-course-title"> | ||||
|             <ion-row> | ||||
|                 <ion-col> | ||||
|                     <ng-container *ngTemplateOutlet="mainInfo"></ng-container> | ||||
|                 </ion-col> | ||||
|                 <ion-col size="auto" | ||||
|                     *ngIf="isEnrolled && ((downloadCourseEnabled && !courseOptionMenuEnabled && showDownload) || courseOptionMenuEnabled)"> | ||||
|                     <ng-container *ngTemplateOutlet="download"></ng-container> | ||||
|                 </ion-col> | ||||
|             </ion-row> | ||||
|             <div *ngIf="layout == 'card' && progress >= 0 && completionUserTracked !== false" lines="none" class="core-course-progress"> | ||||
| 
 | ||||
|     <ion-item class="ion-text-wrap" button lines="none" detail="false" (click)="openCourse()" | ||||
|         [attr.aria-label]="course.displayname || course.fullname" [class.item-disabled]="course.visible == 0"> | ||||
| 
 | ||||
|         <ng-container *ngIf="layout == 'list' || layout == 'listwithenrol'"> | ||||
|             <ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" slot="start" class="course-icon core-course-thumb"> | ||||
|             </ion-icon> | ||||
|             <ion-avatar *ngIf="course.courseImage" slot="start" class="core-course-thumb"> | ||||
|                 <img [src]="course.courseImage" core-external-content alt="" /> | ||||
|             </ion-avatar> | ||||
|         </ng-container> | ||||
| 
 | ||||
|         <ion-label> | ||||
|             <div class="core-course-maininfo"> | ||||
|                 <p *ngIf="course.displayname && course.shortname && course.fullname != course.displayname" | ||||
|                     class="core-course-shortname core-course-additional-info"> | ||||
|                     <core-format-text [text]="course.shortname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|                     </core-format-text> | ||||
|                 </p> | ||||
| 
 | ||||
|                 <p class="item-heading"> | ||||
|                     <ion-icon name="fas-star" *ngIf="course.isfavourite" [attr.aria-label]="'core.courses.favourite' | translate"> | ||||
|                     </ion-icon> | ||||
|                     <span class="sr-only" *ngIf="course.isfavourite">{{ 'core.courses.aria:favourite' | translate }}</span> | ||||
| 
 | ||||
|                     <span class="sr-only">{{ 'core.courses.aria:coursename' | translate }}</span> | ||||
|                     <core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|                     </core-format-text> | ||||
| 
 | ||||
|                     <span *ngIf="(layout == 'list' || layout == 'listwithenrol') && !isEnrolled" class="core-course-enrol-icons"> | ||||
|                         <ion-icon *ngFor="let icon of enrolmentIcons" color="medium" [name]="icon.icon" [title]="icon.label | translate" | ||||
|                             [attr.aria-label]="icon.label | translate"> | ||||
|                         </ion-icon> | ||||
|                     </span> | ||||
| 
 | ||||
|                     <ion-icon *ngIf="downloadCourseEnabled && prefetchCourseData.downloadSucceeded" class="core-icon-downloaded" | ||||
|                         name="cloud-done" color="success" role="status" [attr.aria-label]="'core.downloaded' | translate"></ion-icon> | ||||
|                 </p> | ||||
| 
 | ||||
|                 <ion-chip color="brand" *ngIf="course.categoryname" | ||||
|                     class="core-course-category core-course-additional-info ion-text-nowrap"> | ||||
|                     <span class="sr-only">{{ 'core.courses.aria:coursecategory' | translate }}</span> | ||||
|                     <ion-label> | ||||
|                         <core-format-text [text]="course.categoryname"></core-format-text> | ||||
|                     </ion-label> | ||||
|                 </ion-chip> | ||||
|             </div> | ||||
| 
 | ||||
|             <div *ngIf="layout != 'summarycard' && isEnrolled && progress >= 0 && completionUserTracked !== false" | ||||
|                 class="core-course-progress"> | ||||
|                 <core-progress-bar [progress]="progress" a11yText="core.courses.aria:courseprogress"></core-progress-bar> | ||||
|             </div> | ||||
| 
 | ||||
|             <ng-container *ngIf="layout == 'list' || layout == 'listwithenrol' && isEnrolled"> | ||||
|                 <ng-container *ngTemplateOutlet="download"></ng-container> | ||||
|             </ng-container> | ||||
|         </ion-label> | ||||
|     </ion-item> | ||||
| </ion-card> | ||||
| 
 | ||||
| </ion-card> | ||||
| 
 | ||||
| <ng-template #download> | ||||
|     <div class="core-button-spinner" *ngIf="downloadCourseEnabled && !courseOptionMenuEnabled && showDownload"> | ||||
|         <core-download-refresh [status]="prefetchCourseData.status" [enabled]="downloadCourseEnabled" | ||||
|             [statusTranslatable]="prefetchCourseData.statusTranslatable" canTrustDownload="false" [loading]="prefetchCourseData.loading" | ||||
|             [statusTranslatable]="prefetchCourseData.statusTranslatable" [canTrustDownload]="false" [loading]="prefetchCourseData.loading" | ||||
|             (action)="prefetchCourse()"></core-download-refresh> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="core-button-spinner" *ngIf="courseOptionMenuEnabled"> | ||||
|         <!-- Download course spinner. --> | ||||
|         <ion-spinner *ngIf="(downloadCourseEnabled && prefetchCourseData.icon == 'spinner') || showSpinner" | ||||
|             [attr.aria-label]="'core.loading' | translate"></ion-spinner> | ||||
| 
 | ||||
|         <!-- Downloaded icon. --> | ||||
|         <ion-icon *ngIf="downloadCourseEnabled && prefetchCourseData.downloadSucceeded && !showSpinner" class="core-icon-downloaded" | ||||
|             name="cloud-done" color="success" role="status" [attr.aria-label]="'core.downloaded' | translate"></ion-icon> | ||||
| 
 | ||||
|         <!-- Options menu. --> | ||||
|         <ion-button fill="clear" color="dark" (click)="showCourseOptionsMenu($event)" *ngIf="!showSpinner" | ||||
|             [attr.aria-label]="('core.displayoptions' | translate)"> | ||||
|             <ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|         </ion-button> | ||||
| 
 | ||||
|         <!-- Loading options course spinner. --> | ||||
|         <ion-spinner *ngIf="showSpinner" [attr.aria-label]="'core.loading' | translate"></ion-spinner> | ||||
|     </div> | ||||
| </ng-template> | ||||
| 
 | ||||
| <ng-template #mainInfo> | ||||
|     <p *ngIf="course.categoryname || (course.displayname && course.shortname && course.fullname != course.displayname)" | ||||
|         class="core-course-additional-info"> | ||||
|         <span *ngIf="course.categoryname" class="core-course-category"> | ||||
|             <span class="sr-only">{{ 'core.courses.aria:coursecategory' | translate }}</span> | ||||
|             <core-format-text [text]="course.categoryname"></core-format-text> | ||||
|         </span> | ||||
|         <span *ngIf="course.categoryname && course.displayname && course.shortname && course.fullname != course.displayname" | ||||
|             class="core-course-category"> | </span> | ||||
|         <span *ngIf="course.displayname && course.shortname && course.fullname != course.displayname" class="core-course-shortname"> | ||||
|             <core-format-text [text]="course.shortname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|             </core-format-text> | ||||
|         </span> | ||||
|     </p> | ||||
|     <p class="item-heading"> | ||||
|         <ion-icon name="fas-star" *ngIf="course.isfavourite" [attr.aria-label]="'core.courses.favourite' | translate"> | ||||
|         </ion-icon> | ||||
|         <span class="sr-only" *ngIf="course.isfavourite">{{ 'core.courses.aria:favourite' | translate }}</span> | ||||
|         <span class="sr-only">{{ 'core.courses.aria:coursename' | translate }}</span> | ||||
|         <core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|         </core-format-text> | ||||
|     </p> | ||||
| </ng-template> | ||||
|  | ||||
| @ -1,67 +1,148 @@ | ||||
| @import "~theme/globals"; | ||||
| 
 | ||||
| 
 | ||||
| ion-card.core-course-list-item { | ||||
|     .course-icon { | ||||
|         color: white; | ||||
|         background: var(--gray-200); | ||||
|         padding: 8px; | ||||
|         font-size: 24px; | ||||
|         border-radius: 50%; | ||||
|         -webkit-transition: all 50ms ease-in-out; | ||||
|         transition: all 50ms ease-in-out; | ||||
|     } | ||||
| 
 | ||||
| :host { | ||||
|     @for $i from 0 to length($core-course-image-background) { | ||||
|         ion-icon[course-color="#{$i}"] { | ||||
|             color: var(--core-course-color-#{$i}); | ||||
|         &.course-color-#{$i} { | ||||
|             --course-color: var(--core-course-color-#{$i}); | ||||
|             --course-color-tint: var(--core-course-color-#{$i}-tint); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ion-avatar { | ||||
|         -webkit-transition: all 50ms ease-in-out; | ||||
|         transition: all 50ms ease-in-out; | ||||
|     } | ||||
|     --avatar-radius: var(--core-courseimage-radius); | ||||
|     --avatar-size: 60px; | ||||
|     --avatar-margin: 16px; | ||||
| 
 | ||||
|     .core-course-thumb { | ||||
|         @include margin(12px, 16px, 12px, null); | ||||
|         align-self: flex-start; | ||||
|     } | ||||
|     --card-vertical-margin: 8px; | ||||
|     --card-radius: 12px; | ||||
| 
 | ||||
|     .core-course-summary { | ||||
|         margin-top: 12px; | ||||
|     --shortname-size: 12px; | ||||
| 
 | ||||
|     --button-size: 44px; | ||||
|     --button-padding: 10px; | ||||
|     --button-radius: 50%; | ||||
|     --button-background: rgba(255,255,255,0.5); | ||||
| 
 | ||||
|     @include media-breakpoint-down(md) { | ||||
|         --avatar-size: 48px; | ||||
|         --avatar-margin: 12px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| :host-context(body.dark) { | ||||
|     --button-background: rgba(0, 0, 0, 0.3); | ||||
| } | ||||
| 
 | ||||
| // Common styles. | ||||
| .item-heading ion-icon { | ||||
|     margin-right: 4px; | ||||
|     color: var(--core-star-color); | ||||
| } | ||||
| 
 | ||||
| ion-card.core-course-list-card { | ||||
|     --vertical-margin: 12px; | ||||
|     --border-radius: 16px; | ||||
| button { | ||||
|     z-index: 1; | ||||
| } | ||||
| 
 | ||||
| ion-card { | ||||
|     --border-radius: var(--card-radius); | ||||
| } | ||||
| 
 | ||||
| .core-button-spinner { | ||||
|     margin: 0; | ||||
|     position: absolute; | ||||
|     @include position(8px, 8px, null, null); | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-self: stretch; | ||||
|     height: calc(100% - var(--vertical-margin) - var(--vertical-margin)); | ||||
|     margin-top: var(--vertical-margin); | ||||
|     margin-bottom: var(--vertical-margin); | ||||
|     align-items: center; | ||||
| 
 | ||||
|     @for $i from 0 to length($core-course-image-background) { | ||||
|         &[course-color="#{$i}"] .core-course-thumb { | ||||
|             background: var(--core-course-color-#{$i}); | ||||
|     ion-spinner { | ||||
|         margin-top: 4px; | ||||
|     } | ||||
| 
 | ||||
|     ::ng-deep ion-button { | ||||
|         --border-radius: var(--button-radius); | ||||
|         --background: var(--button-background); | ||||
|         --padding-top: var(--button-padding); | ||||
|         --padding-end: var(--button-padding); | ||||
|         --padding-bottom: var(--button-padding); | ||||
|         --padding-start: var(--button-padding); | ||||
|         width: var(--button-size); | ||||
|         height: var(--button-size); | ||||
|         margin: 0; | ||||
|         &::part(native) { | ||||
|             background: var(--background); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ion-row { | ||||
|         min-height: var(--a11y-min-target-size); | ||||
|         ion-col .core-button-spinner { | ||||
|             min-width: calc(var(--a11y-min-target-size) + 16px); | ||||
|     core-download-refresh ::ng-deep .core-icon-downloaded { | ||||
|         display: none; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .core-course-enrol-icons, .core-icon-downloaded { | ||||
|     @include margin-horizontal(4px, 0px); | ||||
| } | ||||
| 
 | ||||
| .core-course-shortname { | ||||
|     font-size: var(--shortname-size); | ||||
|     color: var(--dark); | ||||
|     white-space: nowrap; | ||||
|     text-overflow: ellipsis; | ||||
|     overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| ion-chip { | ||||
|     margin-top: 8px; | ||||
|     margin-left: 0; | ||||
|     margin-right: 0; | ||||
|     max-width: 100%; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // List layout. | ||||
| ion-card.core-course-list-item { | ||||
|     ion-icon.course-icon { | ||||
|         padding: 12px; | ||||
|         font-size: calc(var(--avatar-size) - 24px); | ||||
|         border-radius: var(--avatar-radius); | ||||
|         color: var(--course-color-tint); | ||||
|         background: var(--course-color, transparent); | ||||
|     } | ||||
| 
 | ||||
|     ion-avatar { | ||||
|        --border-radius: var(--avatar-radius); | ||||
|         width: var(--avatar-size); | ||||
|         height: var(--avatar-size); | ||||
|         img { | ||||
|             background: transparent; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ion-item::part(native) { | ||||
|         --padding-start: var(--avatar-margin); | ||||
|     } | ||||
| 
 | ||||
|     .core-course-thumb { | ||||
|         @include margin(var(--avatar-margin), var(--avatar-margin), var(--avatar-margin), null); | ||||
|     } | ||||
| 
 | ||||
|     .core-course-maininfo { | ||||
|         margin-right: 32px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Card layout. | ||||
| ion-card.core-course-list-card { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
| 
 | ||||
|     height: calc(100% - var(--card-vertical-margin) - var(--card-vertical-margin)); | ||||
|     margin-top: var(--card-vertical-margin); | ||||
|     margin-bottom: var(--card-vertical-margin); | ||||
| 
 | ||||
|     .core-course-thumb { | ||||
|         background: var(--course-color, white); | ||||
|         padding-top: 40%; | ||||
|         width: 100%; | ||||
|         overflow: hidden; | ||||
| @ -70,8 +151,20 @@ ion-card.core-course-list-card { | ||||
|         position: relative; | ||||
|         background-position: center; | ||||
|         background-size: cover; | ||||
|         -webkit-transition: all 50ms ease-in-out; | ||||
|         transition: all 50ms ease-in-out; | ||||
| 
 | ||||
|         ion-icon.course-icon { | ||||
|             color: white; | ||||
|             opacity: 50%; | ||||
|             position: absolute; | ||||
|             left: 0; | ||||
|             right: 0; | ||||
|             top: 50%; | ||||
|             bottom: 0; | ||||
|             width: 80px; | ||||
|             height: 80px; | ||||
|             margin: 0 auto; | ||||
|             transform: translateY(-50%); | ||||
|         } | ||||
| 
 | ||||
|         &.core-course-color-img { | ||||
|             background: var(--ion-item-background); | ||||
| @ -87,6 +180,57 @@ ion-card.core-course-list-card { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .core-course-shortname { | ||||
|         margin-bottom: 8px; | ||||
|     } | ||||
| 
 | ||||
|     ion-item { | ||||
|         flex-grow: 1; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
| 
 | ||||
|         &::part(native) { | ||||
|             flex-grow: 1; | ||||
|             align-items: self-start; | ||||
|         } | ||||
| 
 | ||||
|         > ion-label{ | ||||
|             margin: 16px 0; | ||||
| 
 | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             align-items: flex-start; | ||||
|             align-self: stretch; | ||||
|             flex-grow: 1; | ||||
| 
 | ||||
|             // Clamp one line with ellipsis on tablet view, and 2 in mobile. | ||||
|             .item-heading { | ||||
|                 white-space: nowrap; | ||||
|                 text-overflow: ellipsis; | ||||
|                 overflow: hidden; | ||||
|             } | ||||
| 
 | ||||
|             @include media-breakpoint-down(md) { | ||||
|                 .item-heading { | ||||
|                     // Addition lines for 2 line or multiline ellipsis | ||||
|                     display: -webkit-box !important; | ||||
|                     -webkit-line-clamp: 2; | ||||
|                     -webkit-box-orient: vertical; | ||||
|                     white-space: normal; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .core-course-maininfo { | ||||
|                 flex-grow: 1; | ||||
|                 max-width: 100%; | ||||
|             } | ||||
| 
 | ||||
|             .core-course-progress { | ||||
|                 align-self: stretch; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @if ($core-course-hide-thumb-on-cards) { | ||||
|         .core-course-thumb { | ||||
|             display: none; | ||||
| @ -99,56 +243,6 @@ ion-card.core-course-list-card { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .core-course-additional-info { | ||||
|         margin-bottom: 8px; | ||||
|     } | ||||
| 
 | ||||
|     .core-course-header { | ||||
|         flex-grow: 1; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
| 
 | ||||
|         --inner-padding-end: 0px; | ||||
| 
 | ||||
|         &::part(native) { | ||||
|             flex-grow: 1; | ||||
|             align-items: self-start; | ||||
|         } | ||||
| 
 | ||||
|         &.core-course-only-title { | ||||
|             &::part(native) { | ||||
|                 flex-grow: 1; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         .core-course-title { | ||||
|             margin: 12px 0; | ||||
|             flex-grow: 1; | ||||
|             width: 100%; | ||||
|             max-width: 100%; | ||||
|         } | ||||
| 
 | ||||
|         .core-button-spinner { | ||||
|             margin: 0; | ||||
|         } | ||||
|         .core-button-spinner ion-spinner { | ||||
|             vertical-align: top; // the better option for most scenarios | ||||
|             vertical-align: -webkit-baseline-middle; // the best for those that support it | ||||
|         } | ||||
| 
 | ||||
|         .core-button-spinner .core-icon-downloaded { | ||||
|             font-size: 28.8px; | ||||
|             margin-top: 8px; | ||||
|             vertical-align: top; | ||||
|         } | ||||
| 
 | ||||
|         .item-button[icon-only] { | ||||
|             min-width: 50px; | ||||
|             width: 50px; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @if ($core-course-hide-progress-on-cards) { | ||||
|         .core-course-progress { | ||||
|             display: none; | ||||
| @ -156,10 +250,6 @@ ion-card.core-course-list-card { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| button { | ||||
|     z-index: 1; | ||||
| } | ||||
| 
 | ||||
| :host-context(.core-horizontal-scroll) { | ||||
|     @include horizontal_scroll_item(80%, 250px, 300px); | ||||
| 
 | ||||
| @ -168,31 +258,21 @@ button { | ||||
|             padding-top: 30%; | ||||
|         } | ||||
| 
 | ||||
|         ion-item.core-course-header { | ||||
|             --padding-start: 4px; | ||||
|         ion-item { | ||||
|             --padding-start: 8px; | ||||
|             --inner-padding-end: 8px; | ||||
| 
 | ||||
|             .core-course-title { | ||||
|                 margin: 7px 0; | ||||
|             > ion-label { | ||||
|                 margin: 8px 0; | ||||
| 
 | ||||
|                 .item-heading { | ||||
|                     display: block; | ||||
|                 } | ||||
| 
 | ||||
|                 .item-heading ion-icon { | ||||
|                     margin-right: 2px; | ||||
|                 } | ||||
|             } | ||||
|             .core-button-spinner { | ||||
|                 min-height: 40px; | ||||
|                 min-width: 40px; | ||||
| 
 | ||||
|                 ion-spinner { | ||||
|                     width: 20px; | ||||
|                     height: 20px; | ||||
|                 } | ||||
|             } | ||||
|             .item-button[icon-only] { | ||||
|                 min-width: 40px; | ||||
|                 width: 40px; | ||||
|                 padding: 8px; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { CoreCourseProvider, CoreCourse } from '@features/course/services/course'; | ||||
| import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/services/course-helper'; | ||||
| import { CoreUser } from '@features/user/services/user'; | ||||
| @ -21,6 +21,7 @@ import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { Translate } from '@singletons'; | ||||
| import { CoreColors } from '@singletons/colors'; | ||||
| import { CoreEventCourseStatusChanged, CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { CoreCourseListItem, CoreCourses, CoreCoursesProvider } from '../../services/courses'; | ||||
| import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from '../../services/courses-helper'; | ||||
| @ -64,11 +65,17 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On | ||||
|     protected courseStatusObserver?: CoreEventObserver; | ||||
|     protected siteUpdatedObserver?: CoreEventObserver; | ||||
| 
 | ||||
|     protected element: HTMLElement; | ||||
| 
 | ||||
|     constructor(element: ElementRef) { | ||||
|         this.element = element.nativeElement; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         CoreCoursesHelper.loadCourseColorAndImage(this.course); | ||||
|         this.setCourseColor(); | ||||
| 
 | ||||
|         // Assume is enroled if mode is not listwithenrol.
 | ||||
|         this.isEnrolled = this.layout != 'listwithenrol' || this.course.progress !== undefined; | ||||
| @ -142,6 +149,22 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set course color. | ||||
|      */ | ||||
|     protected async setCourseColor(): Promise<void> { | ||||
|         await CoreCoursesHelper.loadCourseColorAndImage(this.course); | ||||
| 
 | ||||
|         if (this.course.color) { | ||||
|             this.element.style.setProperty('--course-color', this.course.color); | ||||
| 
 | ||||
|             const tint = CoreColors.lighter(this.course.color, 50); | ||||
|             this.element.style.setProperty('--course-color-tint', tint); | ||||
|         } else { | ||||
|             this.element.classList.add('course-color-' + this.course.colorNumber); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|  | ||||
| @ -33,8 +33,8 @@ | ||||
| 
 | ||||
|         <div class="core-button-spinner" *ngIf="downloadCourseEnabled && !courseOptionMenuEnabled && showDownload" slot="end"> | ||||
|             <core-download-refresh [status]="prefetchCourseData.status" [enabled]="downloadCourseEnabled" | ||||
|                 [statusTranslatable]="prefetchCourseData.statusTranslatable" canTrustDownload="false" [loading]="prefetchCourseData.loading" | ||||
|                 (action)="prefetchCourse()"></core-download-refresh> | ||||
|                 [statusTranslatable]="prefetchCourseData.statusTranslatable" [canTrustDownload]="false" | ||||
|                 [loading]="prefetchCourseData.loading" (action)="prefetchCourse()"></core-download-refresh> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="core-button-spinner" *ngIf="courseOptionMenuEnabled" slot="end"> | ||||
|  | ||||
| @ -647,7 +647,7 @@ export class CoreAppProvider { | ||||
|         if (this.isAndroid()) { | ||||
|             const rgb = CoreColors.hexToRGB(color); | ||||
|             if (rgb.red != 255 || rgb.green != 255 || rgb.blue != 255) { | ||||
|                 color = CoreColors.darker(color); | ||||
|                 color = CoreColors.darker(color, 10); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -25,10 +25,15 @@ interface ColorComponents { | ||||
|  * Ionic color names. | ||||
|  */ | ||||
| export enum CoreIonicColorNames { | ||||
|     BRAND = 'brand', | ||||
|     PRIMARY = 'primary', | ||||
|     SECONDARY = 'secondary', | ||||
|     SUCCESS = 'success', | ||||
|     INFO = 'info', | ||||
|     WARNING = 'warning', | ||||
|     DANGER = 'danger', | ||||
|     INFO = 'info', | ||||
|     DARK = 'dark', | ||||
|     MEDIUM = 'medium', | ||||
|     LIGHT = 'light', | ||||
|     NONE = '', | ||||
| }; | ||||
| @ -49,17 +54,35 @@ export class CoreColors { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the same color 10% darker to be used as status bar on Android. | ||||
|      * Returns the same color % darker. | ||||
|      * | ||||
|      * @param color Color to get darker. | ||||
|      * @return Darker Hex RGB color. | ||||
|      */ | ||||
|     static darker(color: string, percent: number = 10): string { | ||||
|         percent = 1 - (percent / 100); | ||||
|     static darker(color: string, percent: number = 48): string { | ||||
|         const inversePercent = 1 - (percent / 100); | ||||
|         const components = CoreColors.hexToRGB(color); | ||||
|         components.red = Math.floor(components.red * percent); | ||||
|         components.green = Math.floor(components.green * percent); | ||||
|         components.blue = Math.floor(components.blue * percent); | ||||
|         components.red = Math.floor(components.red * inversePercent); | ||||
|         components.green = Math.floor(components.green * inversePercent); | ||||
|         components.blue = Math.floor(components.blue * inversePercent); | ||||
| 
 | ||||
|         return CoreColors.RGBToHex(components); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the same color % lighter. | ||||
|      * | ||||
|      * @param color Color to get lighter. | ||||
|      * @return Lighter Hex RGB color. | ||||
|      */ | ||||
|     static lighter(color: string, percent: number = 80): string { | ||||
|         percent = percent / 100; | ||||
|         const inversePercent = 1 - percent; | ||||
| 
 | ||||
|         const components = CoreColors.hexToRGB(color); | ||||
|         components.red = Math.floor(255 * percent + components.red * inversePercent); | ||||
|         components.green = Math.floor(255 * percent + components.green * inversePercent); | ||||
|         components.blue = Math.floor(255 * percent + components.blue * inversePercent); | ||||
| 
 | ||||
|         return CoreColors.RGBToHex(components); | ||||
|     } | ||||
|  | ||||
| @ -31,8 +31,8 @@ core-format-text { | ||||
|         opacity: 1; | ||||
|         background-color: rgba(0,0,0,.1); | ||||
|         overflow: hidden; | ||||
|         border-radius: 5px; | ||||
|         display: block; | ||||
|         border-radius: var(--small-radius); | ||||
| 
 | ||||
|         .core-format-text-loader { | ||||
|             position: absolute; | ||||
| @ -179,7 +179,7 @@ core-format-text { | ||||
|         position: absolute; | ||||
|         @include position(null, 10px, 10px, null); | ||||
|         color: var(--ion-text-color); | ||||
|         border-radius: 5px; | ||||
|         border-radius: var(--small-radius); | ||||
|         background-color: var(--core-format-text-viewer-icon-background); | ||||
|         display: flex; | ||||
| 
 | ||||
|  | ||||
| @ -220,13 +220,6 @@ | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .ion-text-wrap ion-label { | ||||
|         .item-heading, h2, p { | ||||
|             white-space: nowrap; | ||||
|             overflow: hidden; | ||||
|             text-overflow: ellipsis; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -174,13 +174,13 @@ ion-app.ios ion-header ion-title { | ||||
|     text-overflow: ellipsis; | ||||
| } | ||||
| 
 | ||||
| .item.ion-text-wrap ion-label core-format-text .core-format-text-content > *, | ||||
| .item.ion-text-wrap > ion-label core-format-text .core-format-text-content > *, | ||||
| .fake-ion-item.ion-text-wrap core-format-text .core-format-text-content > * { | ||||
|     white-space: normal; | ||||
|     overflow: inherit; | ||||
| } | ||||
| 
 | ||||
| .item.ion-text-wrap ion-label { | ||||
| .item.ion-text-wrap > ion-label { | ||||
|     white-space: normal !important; | ||||
| } | ||||
| 
 | ||||
| @ -301,6 +301,10 @@ button, | ||||
|     min-width: var(--a11y-min-target-size); | ||||
| } | ||||
| 
 | ||||
| ion-button.button.button-clear.button-has-icon-only { | ||||
|     --border-radius: 50%; | ||||
| } | ||||
| 
 | ||||
| // Clear buttons will be black. | ||||
| ion-button.button-clear { | ||||
|     --primary: var(--primary); | ||||
| @ -848,7 +852,14 @@ ion-select-popover ion-item.core-select-option-title { | ||||
| 
 | ||||
| ion-chip { | ||||
|     line-height: 1.1; | ||||
|     @include padding-horizontal(16px); | ||||
|     font-size: 12px; | ||||
|     padding: 4px 8px; | ||||
|     height: 24px; | ||||
| 
 | ||||
|     ion-icon { | ||||
|         font-size: 16px; | ||||
|         @include margin(0, 8px, 0, 0); | ||||
|     } | ||||
| 
 | ||||
|     &.ion-color { | ||||
|         background: var(--ion-color-tint); | ||||
|  | ||||
| @ -271,6 +271,7 @@ | ||||
|     --core-send-message-input-color: var(--gray-900); | ||||
| 
 | ||||
|     --core-courseimage-on-course-size: 72px; | ||||
|     --core-courseimage-radius: var(--medium-radius); | ||||
| 
 | ||||
|     --core-course-module-navigation-max-height: 56px; | ||||
|     --core-course-module-navigation-background: var(--contrast-background); | ||||
| @ -322,6 +323,7 @@ | ||||
| 
 | ||||
|     @for $i from 0 to length($core-course-image-background) { | ||||
|         --core-course-color-#{$i}: #{nth($core-course-image-background, $i + 1)}; | ||||
|         --core-course-color-#{$i}-tint: #{get-color-tint(nth($core-course-image-background, $i + 1))}; | ||||
|     } | ||||
| 
 | ||||
|     @for $i from 0 to length($core-dd-question-colors) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user