|
@ -1582,6 +1582,7 @@
|
|||
"core.course.completion_setby:auto:todo": "course",
|
||||
"core.course.completion_setby:manual:done": "course",
|
||||
"core.course.completion_setby:manual:markdone": "course",
|
||||
"core.course.completionmenuitem": "completion",
|
||||
"core.course.completionrequirements": "course",
|
||||
"core.course.confirmdownload": "local_moodlemobileapp",
|
||||
"core.course.confirmdownloadunknownsize": "local_moodlemobileapp",
|
||||
|
@ -1620,6 +1621,7 @@
|
|||
"core.course.relativedatessubmissionduedatebefore": "course",
|
||||
"core.course.section": "moodle",
|
||||
"core.course.startdate": "moodle",
|
||||
"core.course.studentsmust": "completion",
|
||||
"core.course.thisweek": "format_weeks/currentsection",
|
||||
"core.course.todo": "completion",
|
||||
"core.course.tour_navigation_course_index_student_content": "tool_usertours",
|
||||
|
@ -1628,6 +1630,7 @@
|
|||
"core.course.viewcourse": "block_timeline",
|
||||
"core.course.warningmanualcompletionmodified": "local_moodlemobileapp",
|
||||
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",
|
||||
"core.course.youmust": "completion",
|
||||
"core.coursedetails": "moodle",
|
||||
"core.coursenogroups": "local_moodlemobileapp",
|
||||
"core.courses.addtofavourites": "block_myoverview",
|
||||
|
@ -2313,8 +2316,9 @@
|
|||
"core.scanqr": "local_moodlemobileapp",
|
||||
"core.scrollbackward": "local_moodlemobileapp",
|
||||
"core.scrollforward": "local_moodlemobileapp",
|
||||
"core.search.allcourses": "search",
|
||||
"core.search": "moodle",
|
||||
"core.search.allcategories": "local_moodlemobileapp",
|
||||
"core.search.allcourses": "search",
|
||||
"core.search.empty": "local_moodlemobileapp",
|
||||
"core.search.filtercategories": "local_moodlemobileapp",
|
||||
"core.search.filtercourses": "local_moodlemobileapp",
|
||||
|
@ -2323,7 +2327,6 @@
|
|||
"core.search.noresults": "local_moodlemobileapp",
|
||||
"core.search.noresultshelp": "local_moodlemobileapp",
|
||||
"core.search.resultby": "local_moodlemobileapp",
|
||||
"core.search": "moodle",
|
||||
"core.searching": "local_moodlemobileapp",
|
||||
"core.searchresults": "moodle",
|
||||
"core.sec": "moodle",
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
contain: content;
|
||||
vertical-align: baseline;
|
||||
text-decoration: none;
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
.s20 {
|
||||
font-size: 1.5em;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
contain: content;
|
||||
vertical-align: baseline;
|
||||
text-decoration: none;
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
.s20 {
|
||||
font-size: 2.7em;
|
||||
|
|
|
@ -23,7 +23,7 @@ h4.core-bold {
|
|||
}
|
||||
|
||||
core-mod-icon {
|
||||
padding: 8px;
|
||||
--padding: 8px;
|
||||
--margin-end: 0.5rem;
|
||||
--margin-vertical: 0;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
@ -21,7 +21,6 @@ import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/
|
|||
import { CoreConstants, ModPurpose } from '@/core/constants';
|
||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||
import { CoreCourseModuleData } from '@features/course/services/course-helper';
|
||||
import { CoreIonicColorNames } from '@singletons/colors';
|
||||
|
||||
/**
|
||||
* Handler to support forum modules.
|
||||
|
@ -57,7 +56,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
|
|||
const data = await super.getData(module, courseId);
|
||||
|
||||
if ('afterlink' in module && !!module.afterlink) {
|
||||
data.extraBadgeColor = undefined;
|
||||
const match = />(\d+)[^<]+/.exec(module.afterlink);
|
||||
data.extraBadge = match ? Translate.instant('addon.mod_forum.unreadpostsnumber', { $a : match[1] }) : '';
|
||||
} else {
|
||||
|
@ -113,7 +111,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
|
|||
}
|
||||
|
||||
data.extraBadge = Translate.instant('core.loading');
|
||||
data.extraBadgeColor = CoreIonicColorNames.DARK;
|
||||
|
||||
try {
|
||||
// Handle unread posts.
|
||||
|
@ -122,7 +119,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
|
|||
siteId,
|
||||
});
|
||||
|
||||
data.extraBadgeColor = undefined;
|
||||
data.extraBadge = forum.unreadpostscount
|
||||
? Translate.instant(
|
||||
'addon.mod_forum.unreadpostsnumber',
|
||||
|
@ -131,7 +127,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
|
|||
: '';
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
data.extraBadgeColor = undefined;
|
||||
data.extraBadge = '';
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -59,7 +59,7 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
|
|||
): Promise<CoreCourseModuleHandlerData> {
|
||||
const data = await super.getData(module, courseId, sectionId, forCoursePage);
|
||||
data.showDownloadButton = false;
|
||||
data.buttons = [{
|
||||
data.button = {
|
||||
icon: 'fas-up-right-from-square',
|
||||
label: 'addon.mod_lti.launchactivity',
|
||||
action: (event: Event, module: CoreCourseModuleData, courseId: number): void => {
|
||||
|
@ -68,7 +68,7 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
|
|||
|
||||
CoreCourse.storeModuleViewed(courseId, module.id);
|
||||
},
|
||||
}];
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
@ -73,14 +73,14 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
|||
|
||||
const handlerData = await super.getData(module, courseId, sectionId, forCoursePage);
|
||||
handlerData.updateStatus = (status) => {
|
||||
if (!handlerData.buttons) {
|
||||
if (!handlerData.button) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlerData.buttons[0].hidden = status !== CoreConstants.DOWNLOADED ||
|
||||
handlerData.button.hidden = status !== CoreConstants.DOWNLOADED ||
|
||||
AddonModResourceHelper.isDisplayedInIframe(module);
|
||||
};
|
||||
handlerData.buttons = [{
|
||||
handlerData.button = {
|
||||
hidden: true,
|
||||
icon: openWithPicker ? 'fas-share-from-square' : 'fas-file',
|
||||
label: module.name + ': ' + Translate.instant(openWithPicker ? 'core.openwith' : 'addon.mod_resource.openthefile'),
|
||||
|
@ -92,7 +92,7 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
|||
CoreCourse.storeModuleViewed(courseId, module.id);
|
||||
}
|
||||
},
|
||||
}];
|
||||
};
|
||||
|
||||
this.getResourceData(module, courseId, handlerData).then((extra) => {
|
||||
handlerData.extraBadge = extra;
|
||||
|
@ -144,11 +144,11 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
|||
|
||||
// Check if the button needs to be shown or not.
|
||||
promises.push(this.hideOpenButton(module).then((hideOpenButton) => {
|
||||
if (!handlerData.buttons) {
|
||||
if (!handlerData.button) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlerData.buttons[0].hidden = hideOpenButton;
|
||||
handlerData.button.hidden = hideOpenButton;
|
||||
|
||||
return;
|
||||
}));
|
||||
|
@ -166,58 +166,70 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
|||
|
||||
await Promise.all(promises);
|
||||
|
||||
const extra: string[] = [];
|
||||
|
||||
if (module.contentsinfo) {
|
||||
// No need to use the list of files.
|
||||
extra.push(CoreTextUtils.cleanTags(module.afterlink));
|
||||
} else if (module.contents && module.contents[0]) {
|
||||
const files = module.contents;
|
||||
const file = files[0];
|
||||
return CoreTextUtils.cleanTags(module.afterlink);
|
||||
}
|
||||
|
||||
if (options.showsize) {
|
||||
const size = options.filedetails
|
||||
? options.filedetails.size
|
||||
: files.reduce((result, file) => result + (file.filesize || 0), 0);
|
||||
if (!module.contents || !module.contents[0]) {
|
||||
return '';
|
||||
}
|
||||
|
||||
extra.push(CoreTextUtils.bytesToSize(size, 1));
|
||||
}
|
||||
const extra: string[] = [];
|
||||
const files = module.contents;
|
||||
const mainFile = files[0];
|
||||
|
||||
if (options.showtype) {
|
||||
// We should take it from options.filedetails.size if available but it's already translated.
|
||||
extra.push(CoreMimetypeUtils.getMimetypeDescription(file));
|
||||
}
|
||||
if (options.showsize) {
|
||||
const size = options.filedetails
|
||||
? options.filedetails.size
|
||||
: files.reduce((result, file) => result + (file.filesize || 0), 0);
|
||||
|
||||
if (options.showdate) {
|
||||
const timecreated = 'timecreated' in file ? file.timecreated : 0;
|
||||
extra.push(CoreTextUtils.bytesToSize(size, 1));
|
||||
}
|
||||
|
||||
if (options.filedetails && options.filedetails.modifieddate) {
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.modifieddate',
|
||||
{ $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
} else if (options.filedetails && options.filedetails.uploadeddate) {
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.uploadeddate',
|
||||
{ $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
} else if ((file.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
|
||||
/* Modified date may be up to several minutes later than uploaded date just because
|
||||
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.modifieddate',
|
||||
{ $a: CoreTimeUtils.userDate((file.timemodified || 0) * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
} else {
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.uploadeddate',
|
||||
{ $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
}
|
||||
if (options.showtype) {
|
||||
// We should take it from options.filedetails.size if available but it's already translated.
|
||||
extra.push(CoreMimetypeUtils.getMimetypeDescription(mainFile));
|
||||
}
|
||||
|
||||
if (options.showdate) {
|
||||
const timecreated = 'timecreated' in mainFile ? mainFile.timecreated : 0;
|
||||
|
||||
if (options.filedetails && options.filedetails.modifieddate) {
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.modifieddate',
|
||||
{ $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
} else if (options.filedetails && options.filedetails.uploadeddate) {
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.uploadeddate',
|
||||
{ $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
} else if ((mainFile.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
|
||||
/* Modified date may be up to several minutes later than uploaded date just because
|
||||
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.modifieddate',
|
||||
{ $a: CoreTimeUtils.userDate((mainFile.timemodified || 0) * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
} else {
|
||||
extra.push(Translate.instant(
|
||||
'addon.mod_resource.uploadeddate',
|
||||
{ $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') },
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return extra.join(' ');
|
||||
return extra.join(' · ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async manualCompletionAlwaysShown(module: CoreCourseModuleData): Promise<boolean> {
|
||||
const hideButton = await this.hideOpenButton(module);
|
||||
|
||||
return !hideButton;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -93,20 +93,20 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
|
|||
modal.dismiss();
|
||||
}
|
||||
},
|
||||
buttons: [{
|
||||
button: {
|
||||
hidden: true, // Hide it until we calculate if it should be displayed or not.
|
||||
icon: 'fas-link',
|
||||
label: 'core.openmodinbrowser',
|
||||
action: (event: Event, module: CoreCourseModuleData, courseId: number): void => {
|
||||
openUrl(module, courseId);
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
const hideButton = await CoreUtils.ignoreErrors(this.hideLinkButton(module));
|
||||
|
||||
if (handlerData.buttons && hideButton !== undefined) {
|
||||
handlerData.buttons[0].hidden = hideButton;
|
||||
if (handlerData.button && hideButton !== undefined) {
|
||||
handlerData.button.hidden = hideButton;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
position: absolute;
|
||||
right: -4px;
|
||||
bottom: -4px;
|
||||
padding: 0.2rem;
|
||||
--padding: 0.2rem;
|
||||
}
|
||||
|
||||
.core-avatar-extra-img {
|
||||
background: var(--background-color);
|
||||
border-radius: var(--medium-radius);
|
||||
border-radius: var(--radius-sm);
|
||||
img {
|
||||
max-width: var(--extra-icon-size);
|
||||
max-height: var(--extra-icon-size);
|
||||
|
@ -39,7 +39,7 @@
|
|||
}
|
||||
padding: 0.7rem;
|
||||
background: var(--background-color);
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
|
||||
.core-notification-icon {
|
||||
|
|
|
@ -39,7 +39,7 @@ ion-item {
|
|||
|
||||
div.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);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ ion-badge {
|
|||
|
||||
ion-item core-mod-icon {
|
||||
--size: 18px;
|
||||
padding: 9px;
|
||||
--padding: 9px;
|
||||
--margin-vertical: 8px;
|
||||
--margin-end: 8px;
|
||||
}
|
||||
|
|
|
@ -131,25 +131,4 @@ ion-button {
|
|||
ion-icon {
|
||||
margin: var(--icon-margin);
|
||||
}
|
||||
|
||||
.select-icon {
|
||||
margin: var(--icon-margin);
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
position: relative;
|
||||
|
||||
.select-icon-inner {
|
||||
left: 5px;
|
||||
top: 50%;
|
||||
margin-top: -2px;
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
color: currentcolor;
|
||||
pointer-events: none;
|
||||
border-top: 5px solid;
|
||||
border-right: 5px solid transparent;
|
||||
border-left: 5px solid transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.core-error-info {
|
||||
background: var(--gray-200);
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--gray-900);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
position: relative;
|
||||
|
||||
border: 0;
|
||||
border-radius: var(--medium-radius);
|
||||
border-radius: var(--radius-sm);
|
||||
margin: 8px;
|
||||
width: 90%;
|
||||
max-width: var(--list-item-max-width);
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
:host {
|
||||
display: inline-block;
|
||||
--size: var(--module-icon-size);
|
||||
--size: var(--module-icon-size, 24px);
|
||||
--padding: var(--module-icon-padding, 8px);
|
||||
--icon-radius: var(--module-icon-radius, var(--radius-xs));
|
||||
--margin-end: 0px;
|
||||
--margin-vertical: 0px;
|
||||
--icon-radius: var(--small-radius);
|
||||
--filter: brightness(0) invert(1);
|
||||
|
||||
margin-top: var(--margin-vertical);
|
||||
|
@ -13,7 +14,7 @@
|
|||
@include margin-horizontal(null, var(--margin-end));
|
||||
|
||||
border-radius: var(--icon-radius);
|
||||
padding: 0.7rem;
|
||||
padding: var(--padding);
|
||||
background-color: $gray-100;
|
||||
line-height: var(--size);
|
||||
|
||||
|
@ -57,5 +58,4 @@ img {
|
|||
:host-context(ion-card ion-item) {
|
||||
--margin-vertical: 12px;
|
||||
--margin-end: 12px;
|
||||
--icon-radius: var(--module-icon-radius);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
--textarea-color: var(--core-input-text);
|
||||
--textarea-border-width: var(--core-input-border-width);
|
||||
--textarea-border-color: var(--core-input-stroke);
|
||||
--textarea-radius: var(--huge-radius);
|
||||
--textarea-radius: var(--radius-xl);
|
||||
|
||||
form {
|
||||
position: relative;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
}
|
||||
|
||||
.sheet-modal--wrapper {
|
||||
border-radius: var(--big-radius) var(--big-radius) 0 0;
|
||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||
@include padding(24px, 16px, 24px, 16px);
|
||||
|
||||
background-color: var(--ion-overlay-background-color, var(--ion-background-color, #fff));
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
div.core-timer {
|
||||
background: var(--timer-background);
|
||||
color: var(--timer-text-color, var(--text-color));
|
||||
border-radius: var(--big-radius);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
button {
|
||||
|
|
|
@ -30,6 +30,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module-
|
|||
import { CoreCourseModuleSummaryComponent } from './module-summary/module-summary';
|
||||
import { CoreCourseCourseIndexTourComponent } from './course-index-tour/course-index-tour';
|
||||
import { CoreRemindersComponentsModule } from '@features/reminders/components/components.module';
|
||||
import { CoreCourseModuleCompletionDetailsComponent } from './module-completion-details/module-completion-details';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -46,6 +47,7 @@ import { CoreRemindersComponentsModule } from '@features/reminders/components/co
|
|||
CoreCourseUnsupportedModuleComponent,
|
||||
CoreCourseModuleNavigationComponent,
|
||||
CoreCourseModuleSummaryComponent,
|
||||
CoreCourseModuleCompletionDetailsComponent,
|
||||
],
|
||||
imports: [
|
||||
CoreBlockComponentsModule,
|
||||
|
@ -66,6 +68,7 @@ import { CoreRemindersComponentsModule } from '@features/reminders/components/co
|
|||
CoreCourseUnsupportedModuleComponent,
|
||||
CoreCourseModuleNavigationComponent,
|
||||
CoreCourseModuleSummaryComponent,
|
||||
CoreCourseModuleCompletionDetailsComponent,
|
||||
],
|
||||
})
|
||||
export class CoreCourseComponentsModule {}
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
<ion-badge *ngIf="section.highlighted && highlighted" slot="end">{{highlighted}}</ion-badge>
|
||||
</ion-item-divider>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="section.summary">
|
||||
<ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
|
||||
<ion-label>
|
||||
<core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course.id">
|
||||
</core-format-text>
|
||||
|
|
|
@ -16,6 +16,5 @@
|
|||
}
|
||||
|
||||
.course-section {
|
||||
--padding-start: 12px;
|
||||
--inner-padding-end: 12px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!-- Completion criterias. -->
|
||||
<div class="ion-padding">
|
||||
<!-- Dialog header. -->
|
||||
<h2 *ngIf="isTrackedUser">{{ 'core.course.youmust' | translate }}</h2>
|
||||
<h2 *ngIf="!isTrackedUser">{{ 'core.course.studentsmust' | translate }}</h2>
|
||||
|
||||
<ion-list role="list">
|
||||
<ng-container *ngFor="let rule of completionDetails">
|
||||
<!-- Show completion status and description to tracked users. -->
|
||||
|
||||
<ng-container *ngIf="isTrackedUser">
|
||||
<div *ngIf="rule.statusComplete" role="listitem" [attr.aria-label]="rule.accessibleDescription" class="text-success">
|
||||
<ion-icon name="fas-check" aria-hidden="true"></ion-icon>
|
||||
<span class="sr-only">{{ 'core.course.completion_automatic:done' | translate }}</span>
|
||||
{{ rule.rulevalue.description }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="rule.statusCompleteFail" role="listitem" [attr.aria-label]="rule.accessibleDescription" class="text-danger">
|
||||
<ion-icon name="fas-xmark" aria-hidden="true"></ion-icon>
|
||||
<span class="sr-only">{{ 'core.course.completion_automatic:failed' | translate }}</span>
|
||||
{{ rule.rulevalue.description }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="rule.statusIncomplete" role="listitem" [attr.aria-label]="rule.accessibleDescription">
|
||||
<ion-icon name="fas-circle" class="completion_dot" aria-hidden="true"></ion-icon>
|
||||
<span class="sr-only">{{ 'core.course.completion_automatic:todo' | translate }}</span>
|
||||
{{ rule.rulevalue.description }}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Show only description (without status) to non-tracked users. -->
|
||||
<div *ngIf="!isTrackedUser" role="listitem" [attr.aria-label]="rule.accessibleDescription">
|
||||
<ion-icon name="fas-circle" class="completion_dot" aria-hidden="true"></ion-icon>
|
||||
{{ rule.rulevalue.description }}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Show also manual completion description in the list to non-tracked users. -->
|
||||
<div *ngIf="isManual && !isTrackedUser" role="listitem" class="core-module-completion-todo">
|
||||
<ion-icon name="fas-circle" class="completion_dot" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.completion_manual:markdone' | translate }}
|
||||
</div>
|
||||
</ion-list>
|
||||
</div>
|
|
@ -0,0 +1,22 @@
|
|||
:host {
|
||||
h2, div {
|
||||
font-size: 16px;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 8px;
|
||||
line-height: 27px;
|
||||
}
|
||||
ion-list {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
width: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
ion-icon.completion_dot {
|
||||
font-size: 4px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
import {
|
||||
CoreCourseModuleCompletionStatus,
|
||||
CoreCourseModuleWSRuleDetails,
|
||||
} from '@features/course/services/course';
|
||||
import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
|
||||
import { CoreUser } from '@features/user/services/user';
|
||||
import { Translate } from '@singletons';
|
||||
|
||||
/**
|
||||
* Component to show automatic completion details dialog.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-course-module-completion-details',
|
||||
templateUrl: 'module-completion-details.html',
|
||||
styleUrls: ['module-completion-details.scss'],
|
||||
})
|
||||
export class CoreCourseModuleCompletionDetailsComponent implements OnInit {
|
||||
|
||||
@Input() completion?: CoreCourseModuleCompletionData; // The completion status.
|
||||
|
||||
isTrackedUser = false;
|
||||
isManual = false;
|
||||
completionDetails: CompletionRule[] = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
if (!this.completion) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isManual = !this.completion.isautomatic;
|
||||
this.isTrackedUser = !!this.completion.istrackeduser;
|
||||
|
||||
if (!this.completion?.details) {
|
||||
return;
|
||||
}
|
||||
|
||||
const details = this.completion.details;
|
||||
|
||||
// Format rules.
|
||||
this.completionDetails = await Promise.all(details.map(async (rule: CompletionRule) => {
|
||||
rule.statusComplete = rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
|
||||
rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS;
|
||||
rule.statusCompleteFail = rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
|
||||
rule.statusIncomplete = rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
|
||||
rule.accessibleDescription = null;
|
||||
|
||||
if (this.completion?.overrideby) {
|
||||
const fullName = await CoreUser.getUserFullNameWithDefault(this.completion.overrideby, this.completion.courseId);
|
||||
|
||||
const setByData = {
|
||||
$a: {
|
||||
condition: rule.rulevalue.description,
|
||||
setby: fullName,
|
||||
},
|
||||
};
|
||||
const overrideStatus = rule.statusComplete ? 'done' : 'todo';
|
||||
|
||||
rule.accessibleDescription = Translate.instant('core.course.completion_setby:auto:' + overrideStatus, setByData);
|
||||
}
|
||||
|
||||
return rule;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type CompletionRule = CoreCourseModuleWSRuleDetails & {
|
||||
statusComplete?: boolean;
|
||||
statusCompleteFail?: boolean;
|
||||
statusIncomplete?: boolean;
|
||||
accessibleDescription?: string | null;
|
||||
};
|
|
@ -1,14 +1,13 @@
|
|||
:host {
|
||||
min-width: var(--a11y-min-target-size);
|
||||
min-height: var(--a11y-min-target-size);
|
||||
display: contents;
|
||||
--size: 30px;
|
||||
|
||||
img {
|
||||
padding: 5px;
|
||||
padding: 2px;
|
||||
width: var(--size);
|
||||
vertical-align: middle;
|
||||
max-width: none;
|
||||
margin: 7px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
ion-button {
|
||||
|
@ -16,5 +15,11 @@
|
|||
--padding-start: 0px;
|
||||
--padding-end: 0px;
|
||||
--padding-bottom: 0px;
|
||||
margin: 0;
|
||||
--a11y-target-min-size: 32px;
|
||||
width: var(--a11y-target-min-size);
|
||||
height: var(--a11y-target-min-size);
|
||||
min-width: var(--a11y-target-min-size);
|
||||
min-height: var(--a11y-target-min-size);;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,10 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC
|
|||
return;
|
||||
}
|
||||
|
||||
await CoreCourseHelper.changeManualCompletion(this.completion, event);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
await CoreCourseHelper.changeManualCompletion(this.completion);
|
||||
|
||||
CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
|
||||
}
|
||||
|
|
|
@ -1,70 +1,45 @@
|
|||
<ng-container *ngIf="completion">
|
||||
<ng-container *ngIf="showCompletionConditions && completion.isautomatic">
|
||||
<div *ngIf="mode == 'full'" class="core-module-automatic-completion-conditions" role="list"
|
||||
[attr.aria-label]="'core.course.completionrequirements' | translate:{ $a: moduleName }">
|
||||
<ng-container *ngIf="showCompletionInfo && completion">
|
||||
<ng-container *ngIf="completion.istrackeduser">
|
||||
<ng-container *ngIf="completion.isautomatic">
|
||||
<ion-button class="completioninfo completion_incomplete ion-text-wrap chip" *ngIf="!completed" fill="outline"
|
||||
(click)="completionClicked($event)">
|
||||
{{ 'core.course.todo' | translate }}
|
||||
<div class="select-icon" role="presentation" aria-hidden="true">
|
||||
<div class="select-icon-inner"></div>
|
||||
</div>
|
||||
</ion-button>
|
||||
|
||||
<ng-container *ngIf="completion.istrackeduser">
|
||||
<ng-container *ngFor="let rule of details">
|
||||
<ion-chip *ngIf="rule.statuscomplete" color="success" role="listitem" [attr.aria-label]="rule.accessibleDescription"
|
||||
class="completioninfo completion_complete">
|
||||
<ion-icon name="fas-check" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<strong>{{ 'core.course.completion_automatic:done' | translate }}</strong>
|
||||
{{ rule.rulevalue.description }}
|
||||
</ion-label>
|
||||
</ion-chip>
|
||||
<ion-button class="completioninfo completion_complete ion-text-wrap chip" color="success" (click)="completionClicked($event)"
|
||||
*ngIf="completed">
|
||||
<ion-icon name="fas-check" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{'core.course.done' | translate }}
|
||||
<div class="select-icon" role="presentation" aria-hidden="true">
|
||||
<div class="select-icon-inner"></div>
|
||||
</div>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ion-chip *ngIf="rule.statuscompletefail" color="danger" role="listitem" [attr.aria-label]="rule.accessibleDescription"
|
||||
class="completioninfo completion_fail">
|
||||
<ion-icon name="fas-xmark" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<strong>{{ 'core.course.completion_automatic:failed' | translate }}</strong>
|
||||
{{ rule.rulevalue.description }}
|
||||
</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip *ngIf="rule.statusincomplete" color="secondary" role="listitem" [attr.aria-label]="rule.accessibleDescription"
|
||||
class="completioninfo completion_incomplete">
|
||||
<ion-icon name="fas-pen-to-square" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<strong>{{ 'core.course.completion_automatic:todo' | translate }}</strong>
|
||||
{{ rule.rulevalue.description }}
|
||||
</ion-label>
|
||||
</ion-chip>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!completion.istrackeduser">
|
||||
<ion-chip *ngFor="let rule of details" role="listitem" class="core-module-completion-todo">
|
||||
<ion-icon name="fas-pen-to-square" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<strong>{{ 'core.course.completion_automatic:todo' | translate }}</strong>
|
||||
{{ rule.rulevalue.description }}
|
||||
</ion-label>
|
||||
</ion-chip>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="mode == 'basic' && completion.istrackeduser">
|
||||
<ion-chip class="completioninfo completion_incomplete" *ngIf="completionStatus === 0" color="secondary">
|
||||
<ion-icon name="fas-pen-to-square" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
{{ 'core.course.todo' | translate }}
|
||||
</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip class="completioninfo completion_complete" *ngIf="completionStatus === 1 || completionStatus === 2" color="success">
|
||||
<ion-icon name="fas-check" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{'core.course.done' | translate }}</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip class="completioninfo completion_fail" *ngIf="completionStatus === 3" color="danger">
|
||||
<ion-icon name="fas-xmark" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{'core.course.failed' | translate }}</ion-label>
|
||||
</ion-chip>
|
||||
<ng-container *ngIf="!completion.isautomatic">
|
||||
<ion-button *ngIf="completed" color="success" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"
|
||||
class="completioninfo completion_complete ion-text-wrap chip">
|
||||
<ion-icon name="fas-check" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.completion_manual:done' | translate }}
|
||||
<ion-icon *ngIf="completion.offline" name="fas-arrows-rotate"
|
||||
[attr.aria-label]="'core.course.manualcompletionnotsynced' | translate" slot="end"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="!completed" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"
|
||||
class="completioninfo completion_incomplete ion-text-wrap chip">
|
||||
{{ 'core.course.completion_manual:markdone' | translate }}
|
||||
<ion-icon *ngIf="completion.offline" name="fas-arrows-rotate"
|
||||
[attr.aria-label]="'core.course.manualcompletionnotsynced' | translate" slot="end"></ion-icon>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<core-course-module-manual-completion *ngIf="showManualCompletion" [completion]="completion" [moduleName]="moduleName"
|
||||
(completionChanged)="completionChanged.emit($event)" [mode]="mode">
|
||||
</core-course-module-manual-completion>
|
||||
<ion-button *ngIf="!completion.istrackeduser" fill="outline" class="ion-text-wrap chip" (click)="completionClicked($event)">
|
||||
{{ 'core.course.completionmenuitem' | translate }}
|
||||
<div class="select-icon" role="presentation" aria-hidden="true">
|
||||
<div class="select-icon-inner"></div>
|
||||
</div>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
:host {
|
||||
display: block;
|
||||
margin: var(--margin, 4px);
|
||||
|
||||
ion-button {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
ion-button.button-solid.ion-color-success::part(native){
|
||||
background: var(--ion-color-tint);
|
||||
color: var(--ion-color-shade);
|
||||
}
|
||||
|
||||
ion-button.button-outline::part(native){
|
||||
border-color: var(--gray-400);
|
||||
}
|
||||
}
|
|
@ -12,17 +12,19 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core';
|
||||
|
||||
import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion';
|
||||
import {
|
||||
CoreCourseCompletionMode,
|
||||
CoreCourseModuleCompletionStatus,
|
||||
CoreCourseModuleCompletionTracking,
|
||||
CoreCourseModuleWSRuleDetails,
|
||||
} from '@features/course/services/course';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreCourseModuleCompletionDetailsComponent } from '../module-completion-details/module-completion-details';
|
||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
|
||||
import { CoreUser } from '@features/user/services/user';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
|
||||
/**
|
||||
* Component to handle activity completion. It shows a checkbox with the current status, and allows manually changing
|
||||
|
@ -36,61 +38,132 @@ import { Translate } from '@singletons';
|
|||
@Component({
|
||||
selector: 'core-course-module-completion',
|
||||
templateUrl: 'core-course-module-completion.html',
|
||||
styleUrls: ['module-completion.scss'],
|
||||
})
|
||||
export class CoreCourseModuleCompletionComponent extends CoreCourseModuleCompletionBaseComponent {
|
||||
export class CoreCourseModuleCompletionComponent
|
||||
extends CoreCourseModuleCompletionBaseComponent
|
||||
implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
@Input() showCompletionConditions = false; // Whether to show activity completion conditions.
|
||||
@Input() showManualCompletion = false; // Whether to show manual completion.
|
||||
@Input() mode: CoreCourseCompletionMode = CoreCourseCompletionMode.FULL; // Show full completion status or a basic mode.
|
||||
|
||||
details?: CompletionRule[];
|
||||
completed = false;
|
||||
accessibleDescription: string | null = null;
|
||||
completionStatus?: CoreCourseModuleCompletionStatus;
|
||||
showCompletionInfo = false;
|
||||
protected completionObserver?: CoreEventObserver;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
if (!this.completion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasConditions = !this.completion.isautomatic || (this.completion.details?.length || 0) > 0;
|
||||
this.showCompletionInfo = hasConditions && (this.showCompletionConditions || this.showManualCompletion);
|
||||
if (!this.showCompletionInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.completion.isautomatic && this.completion.istrackeduser) {
|
||||
this.completionObserver = CoreEvents.on(CoreEvents.MANUAL_COMPLETION_CHANGED, (data) => {
|
||||
if (!this.completion || this.completion.cmid != data.completion.cmid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.completion = data.completion;
|
||||
this.calculateData();
|
||||
this.completionChanged.emit(this.completion);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected async calculateData(): Promise<void> {
|
||||
if (!this.completion?.details) {
|
||||
if (!this.completion || !this.completion.istrackeduser) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.completionStatus = !this.completion?.istrackeduser ||
|
||||
this.completion.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE
|
||||
const completionStatus = this.completion.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE
|
||||
? undefined
|
||||
: this.completion.state;
|
||||
|
||||
// Format rules.
|
||||
this.details = await Promise.all(this.completion.details.map(async (rule: CompletionRule) => {
|
||||
rule.statuscomplete = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
|
||||
rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS;
|
||||
rule.statuscompletefail = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
|
||||
rule.statusincomplete = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
|
||||
rule.accessibleDescription = null;
|
||||
this.completed = completionStatus !== CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE &&
|
||||
completionStatus !== CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
|
||||
|
||||
if (this.completion?.overrideby) {
|
||||
if (!this.completion.isautomatic) {
|
||||
// Set an accessible description for manual completions with overridden completion state.
|
||||
if (this.completion.overrideby) {
|
||||
const fullName = await CoreUser.getUserFullNameWithDefault(this.completion.overrideby, this.completion.courseId);
|
||||
|
||||
const setByData = {
|
||||
$a: {
|
||||
condition: rule.rulevalue.description,
|
||||
activityname: this.moduleName,
|
||||
setby: fullName,
|
||||
},
|
||||
};
|
||||
const overrideStatus = rule.statuscomplete ? 'done' : 'todo';
|
||||
const setByLangKey = this.completion.state ? 'completion_setby:manual:done' : 'completion_setby:manual:markdone';
|
||||
this.accessibleDescription = Translate.instant('core.course.' + setByLangKey, setByData);
|
||||
} else {
|
||||
const langKey = this.completion.state ? 'completion_manual:aria:done' : 'completion_manual:aria:markdone';
|
||||
this.accessibleDescription = Translate.instant('core.course.' + langKey, { $a: this.moduleName });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule.accessibleDescription = Translate.instant('core.course.completion_setby:auto:' + overrideStatus, setByData);
|
||||
/**
|
||||
* Completion clicked.
|
||||
*
|
||||
* @param event The click event.
|
||||
*/
|
||||
async completionClicked(event: Event): Promise<void> {
|
||||
if (!this.completion || !this.showCompletionInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (this.completion.isautomatic || !this.completion.istrackeduser) {
|
||||
// Fake clicked element to correct position of the popover.
|
||||
let target: HTMLElement | null = event.target as HTMLElement;
|
||||
if (target && target.tagName !== 'ION-BUTTON') {
|
||||
target = target.parentElement;
|
||||
}
|
||||
|
||||
return rule;
|
||||
}));
|
||||
CoreDomUtils.openPopover({
|
||||
component: CoreCourseModuleCompletionDetailsComponent,
|
||||
componentProps: {
|
||||
completion: this.completion,
|
||||
},
|
||||
showBackdrop: true,
|
||||
event: { target } as Event,
|
||||
});
|
||||
} else {
|
||||
await CoreCourseHelper.changeManualCompletion(this.completion);
|
||||
|
||||
CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
|
||||
if (changes.completion && this.completion && this.completion.istrackeduser) {
|
||||
this.calculateData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.completionObserver?.off();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type CompletionRule = CoreCourseModuleWSRuleDetails & {
|
||||
statuscomplete?: boolean;
|
||||
statuscompletefail?: boolean;
|
||||
statusincomplete?: boolean;
|
||||
accessibleDescription?: string | null;
|
||||
};
|
||||
|
|
|
@ -10,18 +10,19 @@
|
|||
|
||||
core-mod-icon {
|
||||
align-self: flex-start;
|
||||
--padding: 4px;
|
||||
@include margin-horizontal(null, 8px);
|
||||
}
|
||||
|
||||
h1 ion-icon {
|
||||
color: var(--medium);
|
||||
@include margin-horizontal(8px, null);
|
||||
font-size: 80%;
|
||||
|
||||
}
|
||||
|
||||
.core-module-info-box {
|
||||
background: var(--light);
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
margin: 8px;
|
||||
padding: 8px;
|
||||
|
||||
|
@ -59,30 +60,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
core-course-module-completion ::ng-deep ion-button {
|
||||
min-height: 28px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
text-transform: none;
|
||||
font-weight: normal;
|
||||
|
||||
ion-icon {
|
||||
font-size: 16px;
|
||||
min-width: 16px;
|
||||
@include margin(0, 8px, 0, 0);
|
||||
|
||||
&[slot=start] {
|
||||
@include margin(null, 8px, null, 0);
|
||||
}
|
||||
|
||||
&[slot=end] {
|
||||
@include margin(null, 0, null, 8px);
|
||||
}
|
||||
}
|
||||
|
||||
ion-label {
|
||||
white-space: normal !important;
|
||||
}
|
||||
core-course-module-completion {
|
||||
--margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<ng-container *ngIf="completion && !completion.isautomatic">
|
||||
<ng-container *ngIf="completion.istrackeduser">
|
||||
<ion-button *ngIf="completion.state" color="success" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"
|
||||
class="ion-text-wrap" [class.chip]="mode == 'basic'">
|
||||
class="ion-text-wrap chip">
|
||||
<ion-icon name="fas-check" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.completion_manual:done' | translate }}
|
||||
<ion-icon *ngIf="completion?.offline" name="fas-arrows-rotate"
|
||||
[attr.aria-label]="'core.course.manualcompletionnotsynced' | translate" slot="end"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="!completion.state" fill="outline" [attr.aria-label]="accessibleDescription" (click)="completionClicked($event)"
|
||||
class="ion-text-wrap" [class.chip]="mode == 'basic'">
|
||||
class="ion-text-wrap chip">
|
||||
{{ 'core.course.completion_manual:markdone' | translate }}
|
||||
<ion-icon *ngIf="completion?.offline" name="fas-arrows-rotate"
|
||||
[attr.aria-label]="'core.course.manualcompletionnotsynced' | translate" slot="end"></ion-icon>
|
||||
|
@ -16,7 +16,7 @@
|
|||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!completion.istrackeduser">
|
||||
<ion-button disabled="true" fill="outline" class="ion-text-wrap" [class.chip]="mode == 'basic'">
|
||||
<ion-button disabled="true" fill="outline" class="ion-text-wrap chip">
|
||||
{{ 'core.course.completion_manual:markdone' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange } from '@angular/core';
|
||||
import { CoreCourseCompletionMode } from '@features/course/services/course';
|
||||
import { CoreCourseHelper, CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
|
||||
import { CoreUser } from '@features/user/services/user';
|
||||
import { Translate } from '@singletons';
|
||||
|
@ -21,6 +20,8 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
|||
|
||||
/**
|
||||
* Component to display a button for manual completion.
|
||||
*
|
||||
* @deprecated since 4.3. Not used anymore.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-course-module-manual-completion',
|
||||
|
@ -30,7 +31,6 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
|
|||
|
||||
@Input() completion?: CoreCourseModuleCompletionData; // The completion status.
|
||||
@Input() moduleName?: string; // The name of the module this completion affects.
|
||||
@Input() mode: CoreCourseCompletionMode = CoreCourseCompletionMode.FULL; // Show full completion status or a basic mode.
|
||||
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when completion changes.
|
||||
|
||||
accessibleDescription: string | null = null;
|
||||
|
@ -100,7 +100,7 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
await CoreCourseHelper.changeManualCompletion(this.completion, event);
|
||||
await CoreCourseHelper.changeManualCompletion(this.completion);
|
||||
|
||||
CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ h1 {
|
|||
.core-modulename {
|
||||
text-transform: uppercase;
|
||||
core-mod-icon {
|
||||
padding: 3px;
|
||||
--padding: 3px;
|
||||
--size: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -1,91 +1,90 @@
|
|||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0" [class.core-course-module-with-view]="moduleHasView"
|
||||
(click)="moduleClicked($event)" [button]="module.handlerData.action && module.uservisible"
|
||||
[attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null">
|
||||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0"
|
||||
class="activity-card core-course-module-handler {{module.handlerData.class}}" [class.core-course-module-with-view]="moduleHasView"
|
||||
[class.item-dimmed]="module.visible === 0 || module.uservisible === false" (click)="moduleClicked($event)"
|
||||
[button]="module.handlerData.action && module.uservisible"
|
||||
[attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null" id="core-course-module-{{module.id}}">
|
||||
<ng-container *ngIf="!module.handlerData.loading">
|
||||
<ion-item id="core-course-module-{{module.id}}"
|
||||
class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}" [ngClass]="{
|
||||
'item-dimmed': module.visible === 0 || module.uservisible === false
|
||||
}">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<div class="activity-main">
|
||||
<core-mod-icon *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
|
||||
[componentId]="module.instance" [fallbackTranslation]="module.modplural">
|
||||
</core-mod-icon>
|
||||
<div class="activity-title">
|
||||
<p class="item-heading">
|
||||
<core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id"
|
||||
[courseId]="module.course" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
|
||||
</core-format-text>
|
||||
<ion-icon name="fas-lock" *ngIf="module.visible === 0 || module.uservisible === false"
|
||||
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
|
||||
<ion-icon *ngIf="prefetchStatusIcon$ | async as prefetchStatusIcon" [name]="prefetchStatusIcon" color="success"
|
||||
[attr.aria-label]="((prefetchStatusText$ | async) || '') | translate"></ion-icon>
|
||||
</p>
|
||||
|
||||
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
|
||||
[componentId]="module.instance" [fallbackTranslation]="module.modplural">
|
||||
</core-mod-icon>
|
||||
<div class="core-module-additional-info" *ngIf="module.visible === 0 || (module.visible !== 0 && module.isStealth)">
|
||||
<!-- Hidden badges -->
|
||||
<ion-badge color="secondary" *ngIf="module.visible === 0" class="ion-text-wrap">
|
||||
<ion-icon name="far-eye-slash" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.hiddenfromstudents' | translate }}
|
||||
</ion-badge>
|
||||
<ion-badge color="secondary" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
|
||||
<ion-icon name="fas-low-vision" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.hiddenoncoursepage' | translate }}
|
||||
</ion-badge>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Buttons. -->
|
||||
<div *ngIf="module.uservisible !== false" class="core-module-buttons">
|
||||
<!-- Module completion (legacy). -->
|
||||
<core-course-module-completion-legacy *ngIf="module.completiondata && showLegacyCompletion"
|
||||
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
|
||||
(completionChanged)="completionChanged.emit($event)">
|
||||
</core-course-module-completion-legacy>
|
||||
|
||||
<ion-label class="core-module-title">
|
||||
<p class="item-heading">
|
||||
<core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id"
|
||||
[courseId]="module.course" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
|
||||
</core-format-text>
|
||||
<ion-icon name="fas-lock" *ngIf="module.visible === 0 || module.uservisible === false"
|
||||
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
|
||||
<ion-icon *ngIf="prefetchStatusIcon$ | async as prefetchStatusIcon" [name]="prefetchStatusIcon" color="success"
|
||||
[attr.aria-label]="((prefetchStatusText$ | async) || '') | translate"></ion-icon>
|
||||
</p>
|
||||
<!-- Activity completion. For tablets -->
|
||||
<core-course-module-completion *ngIf="hasCompletion && !showLegacyCompletion" [completion]="module.completiondata"
|
||||
[moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="showCompletionConditions"
|
||||
[showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)">
|
||||
</core-course-module-completion>
|
||||
|
||||
<div class="core-module-additional-info">
|
||||
<!-- Basic module completion. -->
|
||||
<core-course-module-completion *ngIf="module.completiondata && module.uservisible && !showLegacyCompletion"
|
||||
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
|
||||
[showCompletionConditions]="showCompletionConditions" [showManualCompletion]="showManualCompletion"
|
||||
(completionChanged)="completionChanged.emit($event)" mode="basic">
|
||||
</core-course-module-completion>
|
||||
<!-- Button defined by the module handler. -->
|
||||
<ion-button fill="clear" class="core-module-button-more core-animate-show-hide" *ngIf="module.handlerData.button"
|
||||
[hidden]="module.handlerData.button.hidden || module.handlerData.spinner" (click)="buttonClicked($event)"
|
||||
[attr.aria-label]="module.handlerData.button.label | translate:{$a: module.handlerData.title}">
|
||||
<ion-icon [name]="module.handlerData.button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
<ion-chip *ngIf="module.handlerData.extraBadge" [color]="module.handlerData.extraBadgeColor"
|
||||
class="ion-text-wrap ion-text-start" [outline]="true">
|
||||
<ion-label><span [innerHTML]="module.handlerData.extraBadge"></span></ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<!-- Hidden badges -->
|
||||
<ion-badge color="warning" *ngIf="module.visible === 0" class="ion-text-wrap">
|
||||
{{ 'core.course.hiddenfromstudents' | translate }}
|
||||
</ion-badge>
|
||||
<ion-badge color="warning" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
|
||||
{{ 'core.course.hiddenoncoursepage' | translate }}
|
||||
</ion-badge>
|
||||
<!-- @deprecated since 4.3. Buttons defined by the module handler. -->
|
||||
<ion-button fill="clear" class="core-module-button-more core-animate-show-hide"
|
||||
*ngIf="!module.handlerData.button && module.handlerData.buttons && module.handlerData.buttons[0]"
|
||||
[hidden]="module.handlerData.buttons[0].hidden || module.handlerData.spinner" (click)="buttonClicked($event)"
|
||||
[attr.aria-label]="module.handlerData.buttons[0].label | translate:{$a: module.handlerData.title}">
|
||||
<ion-icon [name]="module.handlerData.buttons[0].icon" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
</ion-label>
|
||||
<!-- Buttons. -->
|
||||
<div slot="end" *ngIf="module.uservisible !== false" class="buttons core-module-buttons"
|
||||
[ngClass]="{'core-button-completion': module.completiondata && showLegacyCompletion}">
|
||||
<!-- Module completion (legacy). -->
|
||||
<core-course-module-completion-legacy *ngIf="module.completiondata && showLegacyCompletion"
|
||||
|
||||
<!-- Activity dates. -->
|
||||
<div *ngIf="showActivityDates && module.dates?.length" class="activity-dates activity-extra">
|
||||
<core-reminders-date *ngFor="let date of module.dates" [type]="date.id" [label]="date.label" [time]="date.timestamp"
|
||||
[relativeTo]="date.relativeto">
|
||||
</core-reminders-date>
|
||||
</div>
|
||||
|
||||
<!-- Activity completion. -->
|
||||
<core-course-module-completion class="activity-extra" *ngIf="hasCompletion && !showLegacyCompletion"
|
||||
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
|
||||
[showCompletionConditions]="showCompletionConditions" [showManualCompletion]="showManualCompletion"
|
||||
(completionChanged)="completionChanged.emit($event)">
|
||||
</core-course-module-completion-legacy>
|
||||
|
||||
<div class="core-module-buttons-more">
|
||||
<!-- Buttons defined by the module handler. -->
|
||||
<ion-button fill="clear" *ngFor="let button of module.handlerData.buttons"
|
||||
[hidden]="button.hidden || module.handlerData.spinner" class="core-animate-show-hide"
|
||||
(click)="buttonClicked($event, button)" [attr.aria-label]="button.label | translate:{$a: module.handlerData.title}">
|
||||
<ion-icon [name]="button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="hasInfo" id="core-course-module-{{module.id}}-info" detail="false"
|
||||
class="ion-text-wrap core-course-module-handler core-course-module-info {{module.handlerData.class}}" [ngClass]="{
|
||||
'item-dimmed': module.visible === 0 || module.uservisible === false
|
||||
}">
|
||||
<ion-label [collapsible-item]="64">
|
||||
<core-format-text class="core-module-description" *ngIf="module.description" [text]="module.description"
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
|
||||
</core-format-text>
|
||||
|
||||
<!-- Module completion. Only auto conditions-->
|
||||
<core-course-module-completion *ngIf="autoCompletionTodo && module.uservisible && !showLegacyCompletion"
|
||||
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
|
||||
[showCompletionConditions]="showCompletionConditions">
|
||||
</core-course-module-completion>
|
||||
|
||||
<div class="core-module-dates-availabilityinfo"
|
||||
*ngIf="(showActivityDates && module.dates && module.dates.length) || module.availabilityinfo">
|
||||
<!-- Activity dates. -->
|
||||
<div *ngIf="showActivityDates && module.dates && module.dates.length" class="core-module-dates">
|
||||
<core-reminders-date *ngFor="let date of module.dates" [type]="date.id" [label]="date.label" [time]="date.timestamp"
|
||||
[relativeTo]="date.relativeto">
|
||||
</core-reminders-date>
|
||||
</div>
|
||||
|
||||
<!-- Description and restrictions -->
|
||||
<div *ngIf="module.description || module.availabilityinfo" id="activity-{{module.id}}-collapsible"
|
||||
class="ion-text-wrap activity-description-availabilityinfo activity-extra" [collapsible-item]="64">
|
||||
<core-format-text class="core-module-description" *ngIf="module.description" [text]="module.description"
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
|
||||
</core-format-text>
|
||||
|
||||
<!-- Availability info -->
|
||||
<div *ngIf="module.availabilityinfo" class="core-module-availabilityinfo">
|
||||
|
@ -96,7 +95,10 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="module.handlerData.extraBadge" class="ion-text-wrap activity-extrabadges activity-extra"
|
||||
[innerHTML]="module.handlerData.extraBadge"></div>
|
||||
</ion-label>
|
||||
|
||||
</ion-item>
|
||||
|
||||
<div class="core-course-last-module-viewed" *ngIf="isLastViewed">
|
||||
|
@ -106,9 +108,8 @@
|
|||
</ng-container>
|
||||
|
||||
<!-- Loading. -->
|
||||
<ion-item *ngIf="module.handlerData.loading" role="status" class="ion-text-wrap" id="core-course-module-{{module.id}}"
|
||||
[attr.aria-label]="module.handlerData.a11yTitle"
|
||||
[ngClass]="['core-course-module-handler', 'core-module-loading', module.handlerData.class]" detail="false">
|
||||
<ion-item *ngIf="module.handlerData.loading" role="status" class="ion-text-wrap core-module-loading"
|
||||
[attr.aria-label]="module.handlerData.a11yTitle" detail="false">
|
||||
<ion-label>
|
||||
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
|
||||
</ion-label>
|
||||
|
|
|
@ -1,73 +1,148 @@
|
|||
@import "~theme/globals";
|
||||
|
||||
:host {
|
||||
--horizontal-margin: 10px;
|
||||
--vertical-margin: 10px;
|
||||
--horizontal-margin: 12px;
|
||||
--vertical-margin: 12px;
|
||||
--card-padding: 16px;
|
||||
|
||||
ion-card {
|
||||
margin: var(--vertical-margin) var(--horizontal-margin);
|
||||
}
|
||||
|
||||
ion-item {
|
||||
--padding-start: 12px;
|
||||
--padding-start: var(--card-padding);
|
||||
--inner-padding-end: var(--card-padding);
|
||||
ion-label {
|
||||
margin-top: var(--card-padding);
|
||||
margin-bottom: var(--card-padding);
|
||||
&>:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-item.core-module-main-item {
|
||||
.activity-main {
|
||||
--min-height: 52px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.core-module-title .item-heading ion-icon {
|
||||
@include margin-horizontal(8px, null);
|
||||
vertical-align: middle;
|
||||
core-mod-icon {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
--module-icon-padding: 4px;
|
||||
--module-icon-radius: var(--radius-xs);
|
||||
|
||||
@include margin-horizontal(null, 8px);
|
||||
align-self: self-start;
|
||||
}
|
||||
|
||||
.core-module-buttons,
|
||||
.buttons.core-module-buttons {
|
||||
.activity-title {
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
@include margin-horizontal(null, var(--card-padding));
|
||||
|
||||
.item-heading ion-icon {
|
||||
@include margin-horizontal(8px, null);
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.core-module-buttons {
|
||||
align-self: self-start;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.core-module-buttons,
|
||||
.core-module-buttons-more {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
justify-content: space-around;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.core-module-buttons core-course-module-completion,
|
||||
.core-module-buttons-more button {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
ion-button.core-module-button-more {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
margin: 0px 8px;
|
||||
--a11y-target-min-size: 32px;
|
||||
width: var(--a11y-target-min-size);
|
||||
height: var(--a11y-target-min-size);
|
||||
min-width: var(--a11y-target-min-size);
|
||||
min-height: var(--a11y-target-min-size);
|
||||
--padding-start: 0px;
|
||||
--padding-end: 0px;
|
||||
ion-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.core-module-buttons core-course-module-completion {
|
||||
text-align: center;
|
||||
core-course-module-completion {
|
||||
--margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.core-module-additional-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
ion-badge {
|
||||
@include margin-horizontal(null, 4px);
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.core-course-module-info {
|
||||
.core-module-dates-availabilityinfo {
|
||||
background: var(--light);
|
||||
border-radius: var(--small-radius);
|
||||
padding: 8px;
|
||||
}
|
||||
.core-module-description ::ng-deep img {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.core-module-dates + .core-module-availabilityinfo {
|
||||
border-top: 1px solid var(--stroke);
|
||||
padding-top: 8px;
|
||||
}
|
||||
core-course-module-completion {
|
||||
--margin: 8px 0px;
|
||||
}
|
||||
|
||||
.activity-dates {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
core-reminders-date {
|
||||
--display-icon: none;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-description-availabilityinfo,
|
||||
.activity-extrabadges {
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--stroke);
|
||||
}
|
||||
|
||||
.activity-extrabadges {
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.activity-description-availabilityinfo {
|
||||
.core-module-availabilityinfo {
|
||||
font-size: 90%;
|
||||
::ng-deep ul {
|
||||
margin-top: 0.5em;
|
||||
background: var(--gray-300);
|
||||
border-radius: var(--radius-sm);
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 120%;
|
||||
|
||||
::ng-deep ul {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 0px;
|
||||
|
||||
li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
@include margin-horizontal(null, 8px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,19 +154,6 @@
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.core-module-main-item + .core-course-module-info ion-label {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.core-module-availabilityinfo ion-icon,
|
||||
.core-module-dates ion-icon {
|
||||
@include margin-horizontal(null, 8px);
|
||||
}
|
||||
|
||||
.core-course-module-info ::ng-deep core-course-module-completion .core-module-automatic-completion-conditions .completioninfo.completion_complete {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.core-course-last-module-viewed {
|
||||
padding: 8px 12px;
|
||||
color: var(--subdued-text-color);
|
||||
|
@ -102,14 +164,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
.core-module-buttons core-course-module-completion {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
core-course-module-completion.activity-extra {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.indented ion-card {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
margin-inline-start: calc(var(--horizontal-margin) + 1rem);
|
||||
--ion-card-radius: 0;
|
||||
@include margin-horizontal(calc(var(--horizontal-margin) + 1rem), null);
|
||||
}
|
||||
|
||||
&.indented + ::ng-deep core-course-module.indented ion-card {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ import {
|
|||
CoreCourseSection,
|
||||
CoreCourseHelper,
|
||||
} from '@features/course/services/course-helper';
|
||||
import { CoreCourse, CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking } from '@features/course/services/course';
|
||||
import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
import {
|
||||
CoreCourseModulePrefetchDelegate,
|
||||
CoreCourseModulePrefetchHandler,
|
||||
|
@ -55,11 +55,10 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
|
|||
@HostBinding('class.indented') indented = false;
|
||||
|
||||
modNameTranslated = '';
|
||||
hasInfo = false;
|
||||
hasCompletion = false; // Whether activity has completion to be shown.
|
||||
showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
|
||||
prefetchStatusIcon$ = new BehaviorSubject<string>(''); // Module prefetch status icon.
|
||||
prefetchStatusText$ = new BehaviorSubject<string>(''); // Module prefetch status text.
|
||||
autoCompletionTodo = false;
|
||||
moduleHasView = true;
|
||||
|
||||
protected prefetchHandler?: CoreCourseModulePrefetchHandler;
|
||||
|
@ -78,7 +77,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
|
|||
this.showLegacyCompletion = this.showLegacyCompletion ??
|
||||
CoreConstants.CONFIG.uselegacycompletion ??
|
||||
!site.isVersionGreaterEqualThan('3.11');
|
||||
this.checkShowManualCompletion();
|
||||
this.checkShowCompletion();
|
||||
|
||||
if (!this.module.handlerData) {
|
||||
return;
|
||||
|
@ -87,21 +86,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
|
|||
this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title;
|
||||
this.moduleHasView = CoreCourse.moduleHasView(this.module);
|
||||
|
||||
const completionStatus = this.showCompletionConditions && this.module.completiondata?.isautomatic &&
|
||||
this.module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC
|
||||
? this.module.completiondata.state
|
||||
: undefined;
|
||||
|
||||
this.autoCompletionTodo = completionStatus == CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE ||
|
||||
completionStatus == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
|
||||
|
||||
this.hasInfo = !!(
|
||||
this.module.description ||
|
||||
(this.showActivityDates && this.module.dates && this.module.dates.length) ||
|
||||
(this.autoCompletionTodo && !this.showLegacyCompletion) ||
|
||||
(this.module.availabilityinfo)
|
||||
);
|
||||
|
||||
if (this.module.handlerData?.showDownloadButton) {
|
||||
const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.module.course);
|
||||
this.updateModuleStatus(status);
|
||||
|
@ -160,9 +144,14 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
|
|||
/**
|
||||
* Check whether manual completion should be shown.
|
||||
*/
|
||||
protected async checkShowManualCompletion(): Promise<void> {
|
||||
protected async checkShowCompletion(): Promise<void> {
|
||||
this.showManualCompletion = this.showCompletionConditions ||
|
||||
await CoreCourseModuleDelegate.manualCompletionAlwaysShown(this.module);
|
||||
|
||||
this.hasCompletion = !!this.module.completiondata && this.module.uservisible &&
|
||||
(!this.module.completiondata.isautomatic || (this.module.completiondata.details?.length || 0) > 0) &&
|
||||
(this.showCompletionConditions || this.showManualCompletion);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,9 +169,9 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
|
|||
* Function called when a button is clicked.
|
||||
*
|
||||
* @param event Click event.
|
||||
* @param button The clicked button.
|
||||
*/
|
||||
buttonClicked(event: Event, button: CoreCourseModuleHandlerButton): void {
|
||||
buttonClicked(event: Event): void {
|
||||
const button = this.module.handlerData?.button ?? this.module.handlerData?.buttons?.[0];
|
||||
if (!button || !button.action) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"completion_manual:aria:markdone": "Mark {{$a}} as done",
|
||||
"completion_manual:done": "Done",
|
||||
"completion_manual:markdone": "Mark as done",
|
||||
"completionmenuitem": "Completion",
|
||||
"completion_setby:auto:done": "Done: {{$a.condition}} (set by {{$a.setby}})",
|
||||
"completion_setby:auto:todo": "To do: {{$a.condition}} (set by {{$a.setby}})",
|
||||
"completion_setby:manual:done": "{{$a.activityname}} is marked by {{$a.setby}} as done. Press to undo.",
|
||||
|
@ -54,6 +55,7 @@
|
|||
"relativedatessubmissionduedatebefore": "{{$a.datediffstr}} before course start",
|
||||
"section": "Section",
|
||||
"startdate": "Course start date",
|
||||
"studentsmust": "Students must",
|
||||
"thisweek": "This week",
|
||||
"todo": "To do",
|
||||
"tour_navigation_course_index_student_content": "Browse through activities and track your progress.",
|
||||
|
@ -61,5 +63,6 @@
|
|||
"useactivityonbrowser": "You can still use it using your device's web browser.",
|
||||
"viewcourse": "View course",
|
||||
"warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
|
||||
"youmust": "You must",
|
||||
"warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}"
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
|
||||
.course-container {
|
||||
position: relative;
|
||||
top: calc(var(--thumb-height) - var(--big-radius));
|
||||
border-radius: var(--big-radius) var(--big-radius) 0 0;
|
||||
top: calc(var(--thumb-height) - var(--radius-lg));
|
||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||
background-color: var(--ion-background-color);
|
||||
box-shadow: var(--drop-shadow-top);
|
||||
clip-path: inset(-5px 0px 0px 0px);
|
||||
|
@ -68,7 +68,7 @@
|
|||
|
||||
.core-course-dates {
|
||||
background: var(--light);
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
|
||||
|
|
|
@ -29,17 +29,18 @@
|
|||
<core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname"
|
||||
[componentId]="module.id" [expandDescription]="true" [showAvailabilityInfo]="true" (completionChanged)="onCompletionChange()"
|
||||
[showManualCompletion]="showManualCompletion">
|
||||
<div class="core-module-additional-info" title>
|
||||
<ion-chip *ngIf="module.handlerData?.extraBadge" [color]="module.handlerData?.extraBadgeColor"
|
||||
class="ion-text-wrap ion-text-start" [outline]="true">
|
||||
<ion-label><span [innerHTML]="module.handlerData?.extraBadge"></span></ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<div class="core-module-additional-info">
|
||||
<p *ngIf="module.handlerData?.extraBadge" class="ion-text-wrap" [innerHTML]="module.handlerData?.extraBadge">
|
||||
</p>
|
||||
|
||||
<!-- Hidden badges -->
|
||||
<ion-badge color="warning" *ngIf="module.visible === 0" class="ion-text-wrap">
|
||||
<ion-badge color="secondary" *ngIf="module.visible === 0" class="ion-text-wrap">
|
||||
<ion-icon name="far-eye-slash" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.hiddenfromstudents' | translate }}
|
||||
</ion-badge>
|
||||
<ion-badge color="warning" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
|
||||
<ion-badge color="secondary" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
|
||||
<ion-icon name="fas-low-vision" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.course.hiddenoncoursepage' | translate }}
|
||||
</ion-badge>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.core-module-availabilityinfo {
|
||||
background: var(--light);
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
padding: 8px;
|
||||
font-size: 90%;
|
||||
::ng-deep ul {
|
||||
|
|
|
@ -2012,12 +2012,10 @@ export class CoreCourseHelperProvider {
|
|||
* Completion clicked.
|
||||
*
|
||||
* @param completion The completion.
|
||||
* @param event The click event.
|
||||
* @returns Promise resolved with the result.
|
||||
*/
|
||||
async changeManualCompletion(
|
||||
completion: CoreCourseModuleCompletionData,
|
||||
event?: Event,
|
||||
): Promise<CoreStatusWithWarningsWSResponse | void> {
|
||||
if (!completion) {
|
||||
return;
|
||||
|
@ -2028,9 +2026,6 @@ export class CoreCourseHelperProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
event?.preventDefault();
|
||||
event?.stopPropagation();
|
||||
|
||||
const modal = await CoreDomUtils.showModalLoading();
|
||||
completion.state = completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE
|
||||
? CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE
|
||||
|
|
|
@ -81,6 +81,9 @@ export enum CoreCourseModuleCompletionStatus {
|
|||
COMPLETION_COMPLETE_FAIL = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 4.3 Not used anymore.
|
||||
*/
|
||||
export enum CoreCourseCompletionMode {
|
||||
FULL = 'full',
|
||||
BASIC = 'basic',
|
||||
|
|
|
@ -156,6 +156,8 @@ export interface CoreCourseModuleHandlerData {
|
|||
|
||||
/**
|
||||
* The color of the extra badge. Default: primary.
|
||||
*
|
||||
* @deprecated since 4.3 Not used anymore.
|
||||
*/
|
||||
extraBadgeColor?: CoreIonicColorNames;
|
||||
|
||||
|
@ -168,9 +170,16 @@ export interface CoreCourseModuleHandlerData {
|
|||
|
||||
/**
|
||||
* The buttons to display in the module item.
|
||||
*
|
||||
* @deprecated since 4.3 Use button instead. It will only display the first.
|
||||
*/
|
||||
buttons?: CoreCourseModuleHandlerButton[];
|
||||
|
||||
/**
|
||||
* The button to display in the module item.
|
||||
*/
|
||||
button?: CoreCourseModuleHandlerButton;
|
||||
|
||||
/**
|
||||
* Whether to display a spinner where the download button is displayed. The module icon, title, etc. will be displayed.
|
||||
*/
|
||||
|
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
@ -125,7 +125,7 @@
|
|||
margin: 0 auto;
|
||||
font-size: 18px;
|
||||
background-color: var(--toobar-background);
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
@include core-transition(background-color, 200ms);
|
||||
color: var(--button-color);
|
||||
cursor: pointer;
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
}
|
||||
|
||||
core-mod-icon {
|
||||
padding: 0px;
|
||||
--padding: 0px;
|
||||
--size: 16px;
|
||||
background: transparent;
|
||||
--filter: var(--mod-icon-filter);
|
||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -3,6 +3,8 @@
|
|||
:host {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 8px;
|
||||
@include margin-horizontal(null, 8px);
|
||||
}
|
||||
|
||||
div {
|
||||
|
@ -12,10 +14,7 @@ div {
|
|||
align-self: center;
|
||||
|
||||
ion-icon {
|
||||
display: var(--display-icon, inline-block);
|
||||
@include margin-horizontal(0px, 4px);
|
||||
}
|
||||
}
|
||||
|
||||
core-reminders-date + :host {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
margin-inline-end: var(--spacing-2);
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0px;
|
||||
--padding: 0px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<ion-list class="core-course-module-list-wrapper">
|
||||
<!-- Site home main contents. -->
|
||||
<ng-container *ngIf="section && section.hasContent">
|
||||
<ion-item class="ion-text-wrap" *ngIf="section.summary">
|
||||
<ion-item class="ion-text-wrap section-summary" *ngIf="section.summary">
|
||||
<ion-label>
|
||||
<core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="siteHomeId">
|
||||
</core-format-text>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
ion-item ion-icon {
|
||||
display: inline-block;
|
||||
border-radius: var(--module-icon-radius);
|
||||
padding: 0.7rem;
|
||||
border-radius: var(--radius-xs);
|
||||
padding: 4px;
|
||||
background-color: var(--gray-100);
|
||||
color: var(--gray-900);
|
||||
line-height: var(--size);
|
||||
--margin-end: 1rem;
|
||||
--margin-end: 8px;
|
||||
@include margin-horizontal(null, var(--margin-end));
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
min-width: var(--toggle-size);
|
||||
height: var(--toggle-size);
|
||||
width: var(--toggle-size);
|
||||
--border-radius: var(--huge-radius);
|
||||
--border-radius: var(--radius-xl);
|
||||
border-radius: var(--border-radius);
|
||||
--padding-start: 0px;
|
||||
--padding-end: 0px;
|
||||
|
|
|
@ -74,7 +74,7 @@ core-format-text {
|
|||
position: absolute;
|
||||
@include position(null, 10px, 10px, null);
|
||||
color: var(--ion-text-color);
|
||||
border-radius: var(--huge-radius);
|
||||
border-radius: var(--radius-xl);
|
||||
background-color: var(--core-format-text-viewer-icon-background);
|
||||
display: flex;
|
||||
|
||||
|
@ -284,7 +284,7 @@ core-rich-text-editor .core-rte-editor {
|
|||
border: 1px solid var(--gray-500);
|
||||
background: var(--contrast-background);
|
||||
padding: 6px 8px;
|
||||
border-radius: var(--small-radius);
|
||||
border-radius: var(--radius-xs);
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
margin-bottom: 10px;
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
.item.core-course-module-handler.addon-mod-label-handler {
|
||||
.core-course-module-handler.addon-mod-label-handler {
|
||||
align-items: center !important;
|
||||
cursor: auto !important;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.item-heading {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ ion-header {
|
|||
--color: var(--core-header-buttons-color);
|
||||
--background: var(--core-header-buttons-background);
|
||||
--ion-toolbar-color: var(--core-header-buttons-color);
|
||||
--border-radius: var(--huge-radius);
|
||||
--border-radius: var(--radius-xl);
|
||||
--primary: var(--core-header-buttons-color);
|
||||
}
|
||||
ion-back-button::part(text) {
|
||||
|
@ -376,7 +376,7 @@ ion-button:not(.button-has-icon-only):not(.button-small) > ion-icon {
|
|||
}
|
||||
|
||||
ion-button.button.button-clear.button-has-icon-only {
|
||||
--border-radius: var(--huge-radius);
|
||||
--border-radius: var(--radius-xl);
|
||||
}
|
||||
|
||||
ion-button.button.button-clear {
|
||||
|
@ -389,6 +389,27 @@ ion-button.button.button-outline {
|
|||
--border-radius: var(--core-input-radius);
|
||||
}
|
||||
|
||||
ion-button .select-icon {
|
||||
margin: var(--icon-margin);
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
position: relative;
|
||||
|
||||
.select-icon-inner {
|
||||
left: 5px;
|
||||
top: 50%;
|
||||
margin-top: -2px;
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
color: currentcolor;
|
||||
pointer-events: none;
|
||||
border-top: 5px solid;
|
||||
border-right: 5px solid transparent;
|
||||
border-left: 5px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
[role="button"],
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
|
@ -463,10 +484,9 @@ div.core-iframe-network-error {
|
|||
left: -15%;
|
||||
}
|
||||
|
||||
ion-alert.core-nohead {
|
||||
.alert-head {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
ion-alert.core-nohead .alert-head,
|
||||
ion-alert .alert-head:empty {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
@keyframes scaleFrom0 {
|
||||
|
@ -498,7 +518,7 @@ ion-alert .alert-button.timed-button{
|
|||
}
|
||||
|
||||
ion-alert {
|
||||
--border-radius: var(--huge-radius);
|
||||
--border-radius: var(--radius-xl);
|
||||
|
||||
&.md, &.ios {
|
||||
--max-width: 80%;
|
||||
|
@ -531,7 +551,7 @@ ion-alert {
|
|||
}
|
||||
|
||||
ion-loading {
|
||||
--border-radius: var(--huge-radius);
|
||||
--border-radius: var(--radius-xl);
|
||||
|
||||
.loading-wrapper {
|
||||
border-radius: var(--border-radius) !important;
|
||||
|
@ -741,7 +761,7 @@ body.core-iframe-fullscreen ion-router-outlet {
|
|||
}
|
||||
|
||||
.core-password-modal {
|
||||
--border-radius: var(--medium-radius);
|
||||
--border-radius: var(--radius-sm);
|
||||
--min-width: auto;
|
||||
--min-height: 300px;
|
||||
--width: 384px;
|
||||
|
@ -837,6 +857,7 @@ body.core-iframe-fullscreen ion-router-outlet {
|
|||
--border-width: 0;
|
||||
|
||||
--border-color: var(--color-tint);
|
||||
--border-radius: var(--radius-sm);
|
||||
--background: var(--color-tint);
|
||||
--color: var(--color-shade);
|
||||
|
||||
|
@ -940,6 +961,10 @@ ion-card {
|
|||
display: inline !important;
|
||||
}
|
||||
|
||||
.section-summary core-format-text img {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
ion-list.core-course-module-list-wrapper,
|
||||
.list-item-limited-width,
|
||||
.core-course-module-list-wrapper,
|
||||
|
@ -978,7 +1003,7 @@ ion-action-sheet.md {
|
|||
}
|
||||
|
||||
.action-sheet-group:first-child {
|
||||
border-radius: var(--big-radius) var(--big-radius) 0 0;
|
||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||
}
|
||||
|
||||
.action-sheet-group-cancel {
|
||||
|
@ -1125,11 +1150,11 @@ ion-select-popover {
|
|||
ion-badge {
|
||||
line-height: 1.1;
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--big-radius);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
ion-chip,
|
||||
ion-button.chip {
|
||||
ion-button.button.chip {
|
||||
line-height: 1.1;
|
||||
font-size: 12px;
|
||||
min-height: 24px;
|
||||
|
@ -1148,7 +1173,11 @@ ion-button.chip {
|
|||
}
|
||||
}
|
||||
|
||||
ion-button.chip {
|
||||
ion-button.button.chip {
|
||||
--border-radius: var(--radius-md);
|
||||
min-height: 32px;
|
||||
font-size: 14px;
|
||||
|
||||
ion-icon[slot=start] {
|
||||
@include margin(0, 8px, 0, 0);
|
||||
}
|
||||
|
@ -1839,13 +1868,34 @@ video::-webkit-media-text-track-display {
|
|||
white-space: normal !important;
|
||||
}
|
||||
|
||||
ion-modal.core-modal-no-background {
|
||||
--background: transparent;
|
||||
--box-shadow: none !important;
|
||||
pointer-events: none;
|
||||
ion-modal {
|
||||
.modal-wrapper {
|
||||
--border-radius: var(--modal-radius);
|
||||
}
|
||||
|
||||
ion-backdrop {
|
||||
display: none;
|
||||
&.core-modal-lateral,
|
||||
&.core-modal-fullscreen {
|
||||
--modal-radius: 0px;
|
||||
}
|
||||
|
||||
&.core-modal-no-background {
|
||||
--background: transparent;
|
||||
--box-shadow: none !important;
|
||||
pointer-events: none;
|
||||
|
||||
ion-backdrop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-popover {
|
||||
.popover-wrapper .popover-content {
|
||||
border-radius: var(--modal-radius);
|
||||
}
|
||||
&.md {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,21 @@ html {
|
|||
--font-size-normal: 14px;
|
||||
|
||||
// Radiuses
|
||||
--small-radius: 4px;
|
||||
--medium-radius: 8px;
|
||||
--big-radius: 16px;
|
||||
--huge-radius: 24px;
|
||||
--radius-none: 0px;
|
||||
--radius-xs: 4px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 24px;
|
||||
--radius-round: 50%;
|
||||
|
||||
/** @deprecated since 4.3 **/
|
||||
--small-radius: var(--radius-xs);
|
||||
--medium-radius: var(--radius-sm);
|
||||
--big-radius: var(--radius-lg);
|
||||
--huge-radius: var(--radius-xl);
|
||||
|
||||
|
||||
|
||||
// A11y
|
||||
--a11y-min-target-size: 44px;
|
||||
|
|
|
@ -67,14 +67,12 @@ html {
|
|||
--core-input-stroke: var(--gray-500);
|
||||
--core-input-text: var(--dark);
|
||||
--core-input-background: var(--ion-background-color);
|
||||
--core-input-radius: var(--small-radius);
|
||||
--core-input-radius: var(--radius-xs);
|
||||
--core-input-border-width: 1px;
|
||||
|
||||
--module-icon-size: 24px;
|
||||
--module-icon-radius: var(--medium-radius);
|
||||
|
||||
--list-item-max-width: 768px;
|
||||
|
||||
--modal-radius: var(--radius-md);
|
||||
--modal-lateral-max-width: 320px;
|
||||
--modal-lateral-margin: 56px;
|
||||
|
||||
|
@ -82,7 +80,7 @@ html {
|
|||
|
||||
--loader-background-color: rgba(0, 0, 0, .1);
|
||||
--loader-shine: 251, 251, 251;
|
||||
--loader-radius: var(--small-radius);
|
||||
--loader-radius: var(--radius-xs);
|
||||
--loader-display: block;
|
||||
|
||||
--drop-shadow-color: 0, 0, 0, 0.5;
|
||||
|
@ -94,9 +92,9 @@ html {
|
|||
--subdued-text-color: var(--medium);
|
||||
|
||||
--ion-card-color: var(--text-color);
|
||||
--ion-card-vertical-margin: 10px;
|
||||
--ion-card-horizontal-margin: 10px;
|
||||
--ion-card-radius: var(--medium-radius);
|
||||
--ion-card-vertical-margin: 12px;
|
||||
--ion-card-horizontal-margin: 12px;
|
||||
--ion-card-radius: var(--radius-lg);
|
||||
--ion-card-border-width: 1px;
|
||||
--ion-card-border-color: var(--stroke);
|
||||
ion-card {
|
||||
|
@ -333,7 +331,7 @@ html {
|
|||
--core-avatar-radius: 50%;
|
||||
|
||||
--core-courseimage-on-course-size: 72px;
|
||||
--core-courseimage-radius: var(--medium-radius);
|
||||
--core-courseimage-radius: var(--radius-sm);
|
||||
|
||||
--core-navigation-background: var(--contrast-background);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ information provided here is intended especially for developers.
|
|||
- Font Awesome icon library has been updated to 6.4.0. But nothing has changed, only version number.
|
||||
- The analytics system in the app has been refactored and some functions that could trigger analytics calls no longer do it, now you need to use CoreAnalytics instead. Some functions in CoreCourseLogHelper and CorePushNotificationsProvider have been deprecated.
|
||||
- Due to the analytics refactor, the parameters of most log functions have changed.
|
||||
- CoreCourseModuleHandlerData.buttons has been deprecated, now only one button in atribute button will be shown.
|
||||
|
||||
=== 4.2.0 ===
|
||||
|
||||
|
|