| @ -11,10 +11,10 @@ | |||||||
|     <!-- Activity info. --> |     <!-- Activity info. --> | ||||||
|     <core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId" |     <core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId" | ||||||
|         [courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()"> |         [courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()"> | ||||||
|         <ion-list inset="true" description *ngIf="assign && assign.introattachments?.length && !assign.submissionattachments"> |         <div description *ngIf="assign && assign.introattachments?.length && !assign.submissionattachments"> | ||||||
|             <core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId"> |             <core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId"> | ||||||
|             </core-file> |             </core-file> | ||||||
|         </ion-list> |         </div> | ||||||
|     </core-course-module-info> |     </core-course-module-info> | ||||||
| 
 | 
 | ||||||
|     <!-- User can view all submissions (teacher). --> |     <!-- User can view all submissions (teacher). --> | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ | |||||||
| 
 | 
 | ||||||
|     <ion-list *ngIf="contents && (contents.files.length + contents.folders.length > 0)"> |     <ion-list *ngIf="contents && (contents.files.length + contents.folders.length > 0)"> | ||||||
|         <ng-container *ngFor="let folder of contents.folders"> |         <ng-container *ngFor="let folder of contents.folders"> | ||||||
|             <ion-item class="item-file" (click)="openFolder(folder)" detail="true" button> |             <ion-item class="ion-text-wrap item-file item-directory" (click)="openFolder(folder)" detail="true" button> | ||||||
|                 <ion-icon name="fas-folder" slot="start" [attr.aria-label]="'core.folder' | translate"></ion-icon> |                 <ion-icon name="fas-folder" slot="start" [attr.aria-label]="'core.folder' | translate"></ion-icon> | ||||||
|                 <ion-label> |                 <ion-label> | ||||||
|                     <p class="item-heading">{{folder.filename}}</p> |                     <p class="item-heading">{{folder.filename}}</p> | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo | |||||||
|         if (this.subfolder) { |         if (this.subfolder) { | ||||||
|             this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; |             this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; | ||||||
|             this.contents = this.subfolder; |             this.contents = this.subfolder; | ||||||
|  |             this.sortFilesAndFolders(); | ||||||
| 
 | 
 | ||||||
|             this.showLoading = false; |             this.showLoading = false; | ||||||
| 
 | 
 | ||||||
| @ -88,6 +89,30 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo | |||||||
| 
 | 
 | ||||||
|         this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; |         this.description = this.folderInstance ? this.folderInstance.intro : this.module.description; | ||||||
|         this.contents = AddonModFolderHelper.formatContents(contents); |         this.contents = AddonModFolderHelper.formatContents(contents); | ||||||
|  |         this.sortFilesAndFolders(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sort files and folders alphabetically. | ||||||
|  |      */ | ||||||
|  |     protected sortFilesAndFolders(): void { | ||||||
|  |         if (!this.contents) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.contents.folders.sort((a, b) => { | ||||||
|  |             const compareA = a.filename.toLowerCase(); | ||||||
|  |             const compareB = b.filename.toLowerCase(); | ||||||
|  | 
 | ||||||
|  |             return compareA.localeCompare(compareB); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         this.contents.files.sort((a, b) => { | ||||||
|  |             const compareA = a.filename.toLowerCase(); | ||||||
|  |             const compareB = b.filename.toLowerCase(); | ||||||
|  | 
 | ||||||
|  |             return compareA.localeCompare(compareB); | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -40,6 +40,8 @@ ion-item { | |||||||
|     div.core-notification-icon, |     div.core-notification-icon, | ||||||
|     core-mod-icon.core-notification-icon { |     core-mod-icon.core-notification-icon { | ||||||
|         padding: 8px; |         padding: 8px; | ||||||
|  |         max-width: var(--core-avatar-size); | ||||||
|  |         max-height:  var(--core-avatar-size); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,10 +37,8 @@ | |||||||
|         <!-- List of files. --> |         <!-- List of files. --> | ||||||
|         <ion-list *ngIf="files && files.length > 0"> |         <ion-list *ngIf="files && files.length > 0"> | ||||||
|             <ng-container *ngFor="let file of files"> |             <ng-container *ngFor="let file of files"> | ||||||
|                 <ion-item button *ngIf="file.isdir" class="item-file" (click)="openFolder(file)" detail="true"> |                 <ion-item button *ngIf="file.isdir" class="ion-text-wrap item-file item-directory" (click)="openFolder(file)" detail="true"> | ||||||
|                     <ion-thumbnail slot="start"> |                     <ion-icon name="fas-folder" slot="start" [attr.aria-label]="'core.folder' | translate"></ion-icon> | ||||||
|                         <img [src]="file.imgPath" alt="" role="presentation"> |  | ||||||
|                     </ion-thumbnail> |  | ||||||
|                     <ion-label>{{file.filename}}</ion-label> |                     <ion-label>{{file.filename}}</ion-label> | ||||||
|                 </ion-item> |                 </ion-item> | ||||||
|                 <core-file *ngIf="!file.isdir" [file]="file" [component]="component" [componentId]="file.contextid"></core-file> |                 <core-file *ngIf="!file.isdir" [file]="file" [component]="component" [componentId]="file.contextid"></core-file> | ||||||
|  | |||||||
| Before Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/archive.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 4.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/audio.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 4.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/avi.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 4.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/base.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/bmp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 3.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/calc.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 3.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/chart.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 4.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/database.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 4.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/document.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 3.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/draw.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/eps.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/epub.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/flash.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/folder-open.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 539 B | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/folder.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 543 B | 
| Before Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/gif.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/h5p.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/html.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 888 B | 
| Before Width: | Height: | Size: 5.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/image.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 3.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/impress.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 3.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/isf.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/jpeg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 4.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/markup.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 3.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/math.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/moodle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/mp3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 5.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/mpeg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/oth.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 4.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/pdf.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/png.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 4.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/powerpoint.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/psd.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 4.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/publisher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 4.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/quicktime.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/sourcecode.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 5.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/spreadsheet.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 4.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/tiff.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/unknown.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 888 B | 
| Before Width: | Height: | Size: 4.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/video.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 5.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/wav.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 4.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/wmv.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/writer.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
| @ -1,11 +1,15 @@ | |||||||
|  | <ion-card class="card-file"> | ||||||
|     <ion-item *ngIf="file" button class="ion-text-wrap item-file" (click)="download($event, true)" detail="false"> |     <ion-item *ngIf="file" button class="ion-text-wrap item-file" (click)="download($event, true)" detail="false"> | ||||||
|         <ion-thumbnail slot="start"> |         <ion-thumbnail slot="start"> | ||||||
|             <img [src]="fileIcon" alt="" role="presentation" /> |             <img [src]="fileIcon" alt="" role="presentation" /> | ||||||
|         </ion-thumbnail> |         </ion-thumbnail> | ||||||
|         <ion-label> |         <ion-label> | ||||||
|             <p class="item-heading">{{fileName}}</p> |             <p class="item-heading">{{fileName}}</p> | ||||||
|         <p *ngIf="fileSizeReadable">{{ fileSizeReadable }}</p> |             <p *ngIf="fileSizeReadable || showTime"> | ||||||
|         <p *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</p> |                 <ng-container *ngIf="fileSizeReadable">{{ fileSizeReadable }}</ng-container> | ||||||
|  |                 <ng-container *ngIf="fileSizeReadable && showTime"> · </ng-container> | ||||||
|  |                 <ng-container *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</ng-container> | ||||||
|  |             </p> | ||||||
|         </ion-label> |         </ion-label> | ||||||
|         <div slot="end" class="flex-row"> |         <div slot="end" class="flex-row"> | ||||||
|             <core-download-refresh [status]="state" [enabled]="canDownload" [loading]="isDownloading" [canTrustDownload]="!alwaysDownload" |             <core-download-refresh [status]="state" [enabled]="canDownload" [loading]="isDownloading" [canTrustDownload]="!alwaysDownload" | ||||||
| @ -16,9 +20,10 @@ | |||||||
|                 <ion-icon slot="icon-only" [name]="openButtonIcon" aria-hidden="true"></ion-icon> |                 <ion-icon slot="icon-only" [name]="openButtonIcon" aria-hidden="true"></ion-icon> | ||||||
|             </ion-button> |             </ion-button> | ||||||
| 
 | 
 | ||||||
|         <ion-button fill="clear" *ngIf="!isDownloading && canDelete" (click)="delete($event)" [attr.aria-label]="'core.delete' | translate" |             <ion-button fill="clear" *ngIf="!isDownloading && canDelete" (click)="delete($event)" | ||||||
|             color="danger"> |                 [attr.aria-label]="'core.delete' | translate" color="danger"> | ||||||
|                 <ion-icon slot="icon-only" name="fas-trash" aria-hidden="true"></ion-icon> |                 <ion-icon slot="icon-only" name="fas-trash" aria-hidden="true"></ion-icon> | ||||||
|             </ion-button> |             </ion-button> | ||||||
|         </div> |         </div> | ||||||
|     </ion-item> |     </ion-item> | ||||||
|  | </ion-card> | ||||||
|  | |||||||
| @ -1,15 +1,19 @@ | |||||||
| <form (ngSubmit)="changeName(newFileName, $event)" #nameForm> | <form (ngSubmit)="changeName(newFileName, $event)" #nameForm> | ||||||
|  |     <ion-card class="card-file"> | ||||||
|         <ion-item class="ion-text-wrap item-file" (click)="openFile($event)" button detail="false"> |         <ion-item class="ion-text-wrap item-file" (click)="openFile($event)" button detail="false"> | ||||||
|             <ion-thumbnail slot="start"> |             <ion-thumbnail slot="start"> | ||||||
|                 <img [src]="fileIcon" [alt]="fileExtension" role="presentation" /> |                 <img [src]="fileIcon" [alt]="fileExtension" role="presentation" /> | ||||||
|             </ion-thumbnail> |             </ion-thumbnail> | ||||||
| 
 | 
 | ||||||
|         <ion-label> |             <ion-label *ngIf="!editMode"> | ||||||
|                 <!-- File name and edit button (if editable). --> |                 <!-- File name and edit button (if editable). --> | ||||||
|             <p class="item-heading" *ngIf="!editMode">{{fileName}}</p> |                 <p class="item-heading">{{fileName}}</p> | ||||||
|                 <!-- More data about the file. --> |                 <!-- More data about the file. --> | ||||||
|             <p *ngIf="size && !editMode">{{ size }}</p> |                 <p *ngIf="size || timemodified"> | ||||||
|             <p *ngIf="timemodified && !editMode">{{ timemodified }}</p> |                     <ng-container *ngIf="size">{{ size }}</ng-container> | ||||||
|  |                     <ng-container *ngIf="size && timemodified"> · </ng-container> | ||||||
|  |                     <ng-container *ngIf="timemodified">{{ timemodified }}</ng-container> | ||||||
|  |                 </p> | ||||||
|             </ion-label> |             </ion-label> | ||||||
| 
 | 
 | ||||||
|             <!-- Form to edit the file's name. --> |             <!-- Form to edit the file's name. --> | ||||||
| @ -23,19 +27,22 @@ | |||||||
|                 </ion-button> |                 </ion-button> | ||||||
| 
 | 
 | ||||||
|                 <ng-container *ngIf="manage"> |                 <ng-container *ngIf="manage"> | ||||||
|  |                     <ion-button *ngIf="editMode" fill="clear" [attr.aria-label]="'core.save' | translate" color="success" type="submit" | ||||||
|  |                         (click)="changeName(newFileName, $event)"> | ||||||
|  |                         <ion-icon name="fas-check" slot="icon-only" aria-hidden="true"></ion-icon> | ||||||
|  |                     </ion-button> | ||||||
|  | 
 | ||||||
|                     <ion-button *ngIf="!editMode" fill="clear" [core-suppress-events] (onClick)="activateEdit($event)" |                     <ion-button *ngIf="!editMode" fill="clear" [core-suppress-events] (onClick)="activateEdit($event)" | ||||||
|                         [attr.aria-label]="'core.edit' | translate"> |                         [attr.aria-label]="'core.edit' | translate"> | ||||||
|                         <ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon> |                         <ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon> | ||||||
|                     </ion-button> |                     </ion-button> | ||||||
| 
 | 
 | ||||||
|                 <ion-button *ngIf="editMode" fill="clear" [attr.aria-label]="'core.save' | translate" color="success" type="submit"> |                     <ion-button *ngIf="!editMode" fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" | ||||||
|                     <ion-icon name="fas-check" slot="icon-only" aria-hidden="true"></ion-icon> |                         color="danger"> | ||||||
|                 </ion-button> |  | ||||||
| 
 |  | ||||||
|                 <ion-button fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" color="danger"> |  | ||||||
|                         <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> |                         <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> | ||||||
|                     </ion-button> |                     </ion-button> | ||||||
|                 </ng-container> |                 </ng-container> | ||||||
|             </div> |             </div> | ||||||
|         </ion-item> |         </ion-item> | ||||||
|  |     </ion-card> | ||||||
| </form> | </form> | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|     timemodified?: string; |     timemodified?: string; | ||||||
|     newFileName = ''; |     newFileName = ''; | ||||||
|     editMode = false; |     editMode = false; | ||||||
|     relativePath?: string; |     relativePath = ''; | ||||||
|     isIOS = false; |     isIOS = false; | ||||||
|     openButtonIcon = ''; |     openButtonIcon = ''; | ||||||
|     openButtonLabel = ''; |     openButtonLabel = ''; | ||||||
| @ -112,7 +112,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|      * @param isOpenButton Whether the open button was clicked. |      * @param isOpenButton Whether the open button was clicked. | ||||||
|      */ |      */ | ||||||
|     async openFile(e: Event, isOpenButton = false): Promise<void> { |     async openFile(e: Event, isOpenButton = false): Promise<void> { | ||||||
|         if (this.editMode) { |         if (this.editMode || !this.file) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -125,7 +125,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!CoreFileHelper.isOpenableInApp(this.file!)) { |         if (!CoreFileHelper.isOpenableInApp(this.file)) { | ||||||
|             try { |             try { | ||||||
|                 await CoreFileHelper.showConfirmOpenUnsupportedFile(); |                 await CoreFileHelper.showConfirmOpenUnsupportedFile(); | ||||||
|             } catch (error) { |             } catch (error) { | ||||||
| @ -139,7 +139,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|             options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH; |             options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         CoreUtils.openFile(this.file!.toURL(), options); |         CoreUtils.openFile(this.file.toURL(), options); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -148,11 +148,15 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|      * @param e Click event. |      * @param e Click event. | ||||||
|      */ |      */ | ||||||
|     activateEdit(e: Event): void { |     activateEdit(e: Event): void { | ||||||
|  |         if (!this.file) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         e.stopPropagation(); |         e.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|         this.editMode = true; |         this.editMode = true; | ||||||
|         this.newFileName = this.file!.name; |         this.newFileName = this.file.name; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -162,10 +166,14 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|      * @param e Click event. |      * @param e Click event. | ||||||
|      */ |      */ | ||||||
|     async changeName(newName: string, e: Event): Promise<void> { |     async changeName(newName: string, e: Event): Promise<void> { | ||||||
|  |         if (!this.file) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         e.stopPropagation(); |         e.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|         if (newName == this.file!.name) { |         if (newName == this.file.name) { | ||||||
|             // Name hasn't changed, stop.
 |             // Name hasn't changed, stop.
 | ||||||
|             this.editMode = false; |             this.editMode = false; | ||||||
|             CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); |             CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); | ||||||
| @ -174,7 +182,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const modal = await CoreDomUtils.showModalLoading(); |         const modal = await CoreDomUtils.showModalLoading(); | ||||||
|         const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.relativePath!); |         const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.relativePath); | ||||||
|         const newPath = CoreText.concatenatePaths(fileAndDir.directory, newName); |         const newPath = CoreText.concatenatePaths(fileAndDir.directory, newName); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
| @ -186,7 +194,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|         } catch { |         } catch { | ||||||
|             try { |             try { | ||||||
|                 // File doesn't exist, move it.
 |                 // File doesn't exist, move it.
 | ||||||
|                 const fileEntry = await CoreFile.moveFile(this.relativePath!, newPath); |                 const fileEntry = await CoreFile.moveFile(this.relativePath, newPath); | ||||||
| 
 | 
 | ||||||
|                 CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId()); |                 CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId()); | ||||||
| 
 | 
 | ||||||
| @ -219,7 +227,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|             modal = await CoreDomUtils.showModalLoading('core.deleting', true); |             modal = await CoreDomUtils.showModalLoading('core.deleting', true); | ||||||
| 
 | 
 | ||||||
|             await CoreFile.removeFile(this.relativePath!); |             await CoreFile.removeFile(this.relativePath); | ||||||
| 
 | 
 | ||||||
|             this.onDelete.emit(); |             this.onDelete.emit(); | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|  | |||||||
| @ -208,7 +208,11 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|         this.expandedHeader = this.page?.querySelector('ion-item[collapsible]') ?? undefined; |         this.expandedHeader = this.page?.querySelector('ion-item[collapsible]') ?? undefined; | ||||||
| 
 | 
 | ||||||
|         if (!this.expandedHeader) { |         if (!this.expandedHeader) { | ||||||
|  |             this.enabled = false; | ||||||
|  |             this.setEnabled(this.enabled); | ||||||
|  | 
 | ||||||
|             throw new Error('[collapsible-header] Couldn\'t initialize expanded header'); |             throw new Error('[collapsible-header] Couldn\'t initialize expanded header'); | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
|         this.expandedHeader.classList.add('collapsible-header-expanded'); |         this.expandedHeader.classList.add('collapsible-header-expanded'); | ||||||
| 
 | 
 | ||||||
| @ -384,11 +388,11 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|      * @param enable True to enable, false otherwise |      * @param enable True to enable, false otherwise | ||||||
|      */ |      */ | ||||||
|     async setEnabled(enable: boolean): Promise<void> { |     async setEnabled(enable: boolean): Promise<void> { | ||||||
|         if (!this.page || !this.content) { |         if (!this.page) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (enable) { |         if (enable && this.content) { | ||||||
|             const contentScroll = await this.content.getScrollElement(); |             const contentScroll = await this.content.getScrollElement(); | ||||||
| 
 | 
 | ||||||
|             // Do nothing, since scroll has already started on the page.
 |             // Do nothing, since scroll has already started on the page.
 | ||||||
|  | |||||||
| @ -15,11 +15,14 @@ | |||||||
| import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; | import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; | ||||||
| import { CoreCancellablePromise } from '@classes/cancellable-promise'; | import { CoreCancellablePromise } from '@classes/cancellable-promise'; | ||||||
| import { CoreLoadingComponent } from '@components/loading/loading'; | import { CoreLoadingComponent } from '@components/loading/loading'; | ||||||
|  | import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils } from '@services/utils/utils'; | ||||||
| import { Translate } from '@singletons'; | import { Translate } from '@singletons'; | ||||||
|  | import { CoreColors } from '@singletons/colors'; | ||||||
| import { CoreComponentsRegistry } from '@singletons/components-registry'; | import { CoreComponentsRegistry } from '@singletons/components-registry'; | ||||||
| import { CoreDom } from '@singletons/dom'; | import { CoreDom } from '@singletons/dom'; | ||||||
| import { CoreEventObserver } from '@singletons/events'; | import { CoreEventObserver } from '@singletons/events'; | ||||||
|  | import { Subscription } from 'rxjs'; | ||||||
| import { CoreFormatTextDirective } from './format-text'; | import { CoreFormatTextDirective } from './format-text'; | ||||||
| 
 | 
 | ||||||
| const defaultMaxHeight = 80; | const defaultMaxHeight = 80; | ||||||
| @ -50,6 +53,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|     protected maxHeight = defaultMaxHeight; |     protected maxHeight = defaultMaxHeight; | ||||||
|     protected expandedHeight = 0; |     protected expandedHeight = 0; | ||||||
|     protected resizeListener?: CoreEventObserver; |     protected resizeListener?: CoreEventObserver; | ||||||
|  |     protected darkModeListener?: Subscription; | ||||||
|     protected domPromise?: CoreCancellablePromise<void>; |     protected domPromise?: CoreCancellablePromise<void>; | ||||||
|     protected uniqueId: string; |     protected uniqueId: string; | ||||||
| 
 | 
 | ||||||
| @ -92,6 +96,10 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|         this.resizeListener = CoreDom.onWindowResize(() => { |         this.resizeListener = CoreDom.onWindowResize(() => { | ||||||
|             this.calculateHeight(); |             this.calculateHeight(); | ||||||
|         }, 50); |         }, 50); | ||||||
|  | 
 | ||||||
|  |         this.darkModeListener = CoreSettingsHelper.onDarkModeChange().subscribe(() => { | ||||||
|  |             this.setGradientColor(); | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -135,7 +143,34 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|         this.element.classList.remove('collapsible-loading-height'); |         this.element.classList.remove('collapsible-loading-height'); | ||||||
| 
 | 
 | ||||||
|         // If cannot calculate height, shorten always.
 |         // If cannot calculate height, shorten always.
 | ||||||
|         this.setExpandButtonEnabled(!this.expandedHeight || this.expandedHeight >= this.maxHeight); |         const enable = !this.expandedHeight || this.expandedHeight >= this.maxHeight; | ||||||
|  |         this.setExpandButtonEnabled(enable); | ||||||
|  |         this.setGradientColor(); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sets the gradient color based on the background. | ||||||
|  |      */ | ||||||
|  |     protected setGradientColor(): void { | ||||||
|  |         if (!this.toggleExpandEnabled) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let coloredElement: HTMLElement | null = this.element; | ||||||
|  |         let backgroundColor = [0, 0, 0, 0]; | ||||||
|  |         let background = ''; | ||||||
|  |         while (coloredElement && backgroundColor[3] === 0) { | ||||||
|  |             background = getComputedStyle(coloredElement).backgroundColor; | ||||||
|  |             backgroundColor = CoreColors.getColorRGBA(background); | ||||||
|  |             coloredElement = coloredElement.parentElement; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (backgroundColor[3] !== 0) { | ||||||
|  |             delete(backgroundColor[3]); | ||||||
|  |             const bgList = backgroundColor.join(','); | ||||||
|  |             this.element.style.setProperty('--background-gradient-rgb', `${bgList}`); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -241,6 +276,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|      */ |      */ | ||||||
|     ngOnDestroy(): void { |     ngOnDestroy(): void { | ||||||
|         this.resizeListener?.off(); |         this.resizeListener?.off(); | ||||||
|  |         this.darkModeListener?.unsubscribe(); | ||||||
|         this.domPromise?.cancel(); |         this.domPromise?.cancel(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ | |||||||
| </core-dynamic-component> | </core-dynamic-component> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <core-block-side-blocks-button slot="fixed" *ngIf="loaded && course && displayBlocks && hasBlocks" contextlevel="course" | <core-block-side-blocks-button slot="fixed" *ngIf="loaded && course && displayBlocks && hasBlocks" contextLevel="course" | ||||||
|     [instanceId]="course.id"> |     [instanceId]="course.id"> | ||||||
| </core-block-side-blocks-button> | </core-block-side-blocks-button> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,43 +14,39 @@ | |||||||
|     </ion-label> |     </ion-label> | ||||||
| </ion-item> | </ion-item> | ||||||
| 
 | 
 | ||||||
|  | <div class="core-module-info-box"> | ||||||
|     <!-- Module completion. --> |     <!-- Module completion. --> | ||||||
| <ion-item class="ion-text-wrap" |     <div class="core-module-info-completion core-module-info-box-section" *ngIf="showCompletion && | ||||||
|     *ngIf="showCompletion && module.completiondata && (module.completiondata.isautomatic || (showManualCompletion && module.uservisible))"> |             module.completiondata && (module.completiondata.isautomatic || (showManualCompletion && module.uservisible))"> | ||||||
|     <ion-label> |  | ||||||
|         <core-course-module-completion [completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id" |         <core-course-module-completion [completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id" | ||||||
|             [showCompletionConditions]="true" [showManualCompletion]="showManualCompletion && module.uservisible" |             [showCompletionConditions]="true" [showManualCompletion]="showManualCompletion && module.uservisible" | ||||||
|             (completionChanged)="completionChanged.emit($event)"> |             (completionChanged)="completionChanged.emit($event)"> | ||||||
|         </core-course-module-completion> |         </core-course-module-completion> | ||||||
|     </ion-label> |     </div> | ||||||
| </ion-item> |  | ||||||
| 
 | 
 | ||||||
| <div class="core-module-dates-availabilityinfo" |  | ||||||
|     *ngIf="(module.dates && module.dates.length) || (showAvailabilityInfo && module.availabilityinfo)"> |  | ||||||
|     <!-- Activity dates. --> |     <!-- Activity dates. --> | ||||||
|     <div *ngIf="module.dates && module.dates.length" class="core-module-dates"> |     <div *ngIf="module.dates && module.dates.length" class="core-module-dates core-module-info-box-section"> | ||||||
|         <p *ngFor="let date of module.dates"> |         <p *ngFor="let date of module.dates"> | ||||||
|             <ion-icon name="fas-calendar" aria-hidden="true"></ion-icon><strong>{{ date.label }}</strong> {{ date.timestamp |             <ion-icon name="fas-calendar" aria-hidden="true"></ion-icon><strong>{{ date.label }}</strong> {{ date.timestamp | ||||||
|             * |             * | ||||||
|             1000 | coreFormatDate:'strftimedatetime' }} |             1000 | coreFormatDate:'strftimedatetime' }} | ||||||
|         </p> |         </p> | ||||||
|     </div> |     </div> | ||||||
|  | 
 | ||||||
|     <!-- Availability info space. --> |     <!-- Availability info space. --> | ||||||
|     <div class="core-module-availabilityinfo" *ngIf="showAvailabilityInfo"> |     <div class="core-module-availabilityinfo core-module-info-box-section" *ngIf="showAvailabilityInfo"> | ||||||
|         <ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate"></ion-icon> |         <ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate"></ion-icon> | ||||||
|         <core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course"> |         <core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course"> | ||||||
|         </core-format-text> |         </core-format-text> | ||||||
|     </div> |     </div> | ||||||
| </div> |  | ||||||
| 
 | 
 | ||||||
| <ion-item class="ion-text-wrap" *ngIf="description"> |     <div class="core-module-info-description core-module-info-box-section" *ngIf="description"> | ||||||
|     <ion-label> |  | ||||||
|         <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module" |         <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module" | ||||||
|             [contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="expandDescription ? null : ''"> |             [contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="expandDescription ? null : ''"> | ||||||
|         </core-format-text> |         </core-format-text> | ||||||
|     </ion-label> |     </div> | ||||||
| </ion-item> |  | ||||||
|     <ng-content select="[description]"></ng-content> |     <ng-content select="[description]"></ng-content> | ||||||
|  | </div> | ||||||
| 
 | 
 | ||||||
| <ng-content></ng-content> | <ng-content></ng-content> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,11 +23,34 @@ | |||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .core-module-dates-availabilityinfo { |     .core-module-info-box { | ||||||
|         background: var(--light); |         background: var(--light); | ||||||
|         border-radius: var(--small-radius); |         border-radius: var(--small-radius); | ||||||
|         padding: 8px; |  | ||||||
|         margin: 8px; |         margin: 8px; | ||||||
|  |         padding: 8px; | ||||||
|  | 
 | ||||||
|  |         ::ng-deep ion-item { | ||||||
|  |             --ion-item-background: var(--light); | ||||||
|  |             --background: var(--light); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ::ng-deep ion-card.card-file { | ||||||
|  |             --ion-card-horizontal-margin: 0px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .core-module-info-box-section + .core-module-info-box-section { | ||||||
|  |             border-top: 1px solid var(--stroke); | ||||||
|  |             margin-top: 8px; | ||||||
|  |             padding-top: 8px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .core-module-dates ion-icon { | ||||||
|  |             margin-left: 4px; | ||||||
|  |             margin-right: 4px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .core-module-dates, | ||||||
|  |         .core-module-availabilityinfo { | ||||||
|             font-size: 90%; |             font-size: 90%; | ||||||
|             ion-icon { |             ion-icon { | ||||||
|                 position: static; |                 position: static; | ||||||
| @ -40,10 +63,6 @@ | |||||||
|                 margin-bottom: 4px; |                 margin-bottom: 4px; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     .core-module-dates + .core-module-availabilityinfo { |  | ||||||
|         border-top: 1px solid var(--stroke); |  | ||||||
|         padding-top: 8px; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     core-course-module-completion ::ng-deep ion-button { |     core-course-module-completion ::ng-deep ion-button { | ||||||
|  | |||||||
| @ -21,6 +21,10 @@ | |||||||
|         margin-top: var(--button-vertical-margin); |         margin-top: var(--button-vertical-margin); | ||||||
|         margin-bottom: var(--button-vertical-margin); |         margin-bottom: var(--button-vertical-margin); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     &.empty { | ||||||
|  |         display: none; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| :host-context(core-course-format.core-course-format-singleactivity) { | :host-context(core-course-format.core-course-format-singleactivity) { | ||||||
|  | |||||||