-
+
{{ date.label }} {{ date.timestamp
*
1000 | coreFormatDate:'strftimedatetime' }}
+
-
-
-
+
-
-
-
+
+
+
diff --git a/src/core/features/course/components/module-info/course-module-info.scss b/src/core/features/course/components/module-info/course-module-info.scss
index 6091e0b2f..8b6c8feb0 100644
--- a/src/core/features/course/components/module-info/course-module-info.scss
+++ b/src/core/features/course/components/module-info/course-module-info.scss
@@ -23,27 +23,46 @@
}
- .core-module-dates-availabilityinfo {
+ .core-module-info-box {
background: var(--light);
border-radius: var(--small-radius);
- padding: 8px;
margin: 8px;
- font-size: 90%;
- ion-icon {
- position: static;
- @include margin-horizontal(null, 8px);
+ padding: 8px;
+
+ ::ng-deep ion-item {
+ --ion-item-background: var(--light);
+ --background: var(--light);
}
- p,
- ul {
- margin-top: 4px;
- margin-bottom: 4px;
+ ::ng-deep ion-card.card-file {
+ --ion-card-horizontal-margin: 0px;
}
- }
- .core-module-dates + .core-module-availabilityinfo {
- border-top: 1px solid var(--stroke);
- padding-top: 8px;
+ .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%;
+ ion-icon {
+ position: static;
+ @include margin-horizontal(null, 8px);
+ }
+
+ p,
+ ul {
+ margin-top: 4px;
+ margin-bottom: 4px;
+ }
+ }
}
core-course-module-completion ::ng-deep ion-button {
diff --git a/src/core/features/course/components/module-navigation/module-navigation.scss b/src/core/features/course/components/module-navigation/module-navigation.scss
index 7ae45b850..4b4eb2a8c 100644
--- a/src/core/features/course/components/module-navigation/module-navigation.scss
+++ b/src/core/features/course/components/module-navigation/module-navigation.scss
@@ -21,6 +21,10 @@
margin-top: var(--button-vertical-margin);
margin-bottom: var(--button-vertical-margin);
}
+
+ &.empty {
+ display: none;
+ }
}
:host-context(core-course-format.core-course-format-singleactivity) {
diff --git a/src/core/features/course/components/module-navigation/module-navigation.ts b/src/core/features/course/components/module-navigation/module-navigation.ts
index 4e436af4b..63211dbc2 100644
--- a/src/core/features/course/components/module-navigation/module-navigation.ts
+++ b/src/core/features/course/components/module-navigation/module-navigation.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Component, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
import { CoreCourse, CoreCourseWSSection } from '@features/course/services/course';
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
@@ -44,11 +44,13 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
nextModuleSection?: CoreCourseWSSection;
previousModuleSection?: CoreCourseWSSection;
loaded = false;
+ element: HTMLElement;
protected completionObserver: CoreEventObserver;
- constructor(protected ionContent: IonContent) {
+ constructor(protected ionContent: IonContent, element: ElementRef) {
const siteId = CoreSites.getCurrentSiteId();
+ this.element = element.nativeElement;
this.completionObserver = CoreEvents.on(CoreEvents.COMPLETION_MODULE_VIEWED, async (data) => {
if (data && data.courseId == this.courseId) {
@@ -168,6 +170,8 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
}
}
}
+
+ this.element.classList.toggle('empty', !this.nextModule && !this.previousModule);
}
/**
diff --git a/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html b/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html
index d5c1692c3..342f17c3e 100644
--- a/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html
+++ b/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html
@@ -1,6 +1,3 @@
-
-
-
diff --git a/src/core/features/course/format/singleactivity/components/singleactivity.ts b/src/core/features/course/format/singleactivity/components/singleactivity.ts
index 499d7f805..ac3be661c 100644
--- a/src/core/features/course/format/singleactivity/components/singleactivity.ts
+++ b/src/core/features/course/format/singleactivity/components/singleactivity.ts
@@ -20,7 +20,6 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { IonRefresher } from '@ionic/angular';
import { CoreCourseModuleCompletionData, CoreCourseSection } from '@features/course/services/course-helper';
-import { CoreBlockHelper } from '@features/block/services/block-helper';
import { CoreCourse } from '@features/course/services/course';
/**
@@ -46,7 +45,6 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
componentClass?: Type
; // The class of the component to render.
data: Record = {}; // Data to pass to the component.
- hasBlocks = false;
/**
* @inheritdoc
@@ -60,8 +58,6 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
return;
}
- this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id);
-
// In single activity the module should only have 1 section and 1 module. Get the module.
const module = this.sections?.[0].modules?.[0];
@@ -93,11 +89,7 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
if (this.course) {
const courseId = this.course.id;
- await CoreCourse.invalidateCourseBlocks(courseId).then(async () => {
- this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(courseId);
-
- return;
- });
+ await CoreCourse.invalidateCourseBlocks(courseId);
}
}
diff --git a/src/core/features/sharedfiles/components/list/list.html b/src/core/features/sharedfiles/components/list/list.html
index e3ae1898e..96174712a 100644
--- a/src/core/features/sharedfiles/components/list/list.html
+++ b/src/core/features/sharedfiles/components/list/list.html
@@ -13,10 +13,8 @@
(onDelete)="fileDeleted(idx)" (onRename)="fileRenamed(idx, $event)">
-
-
-
-
+
+
{{ file.name }}
diff --git a/src/core/pipes/to-locale-string.ts b/src/core/pipes/to-locale-string.ts
index 5783fea6c..908bc84f7 100644
--- a/src/core/pipes/to-locale-string.ts
+++ b/src/core/pipes/to-locale-string.ts
@@ -21,7 +21,8 @@ import { CoreLogger } from '@singletons/logger';
* Filter to format a timestamp to a locale string. Timestamp can be in seconds or milliseconds.
*
* @deprecated since 3.6. Use coreFormatDate instead.
- * This pipe wasn't removed in app 4.0 because some site plugins still used it. It will be removed in future versions.
+ * This pipe wasn't removed in app 4.0 because some site plugins still used it. It will be removed in future versions
+ * (see MOBILE-2823).
*/
@Pipe({
name: 'coreToLocaleString',
diff --git a/src/core/services/utils/mimetype.ts b/src/core/services/utils/mimetype.ts
index d8e4610ed..386a6f267 100644
--- a/src/core/services/utils/mimetype.ts
+++ b/src/core/services/utils/mimetype.ts
@@ -280,7 +280,7 @@ export class CoreMimetypeUtilsProvider {
* @return The path to a folder icon.
*/
getFolderIcon(): string {
- return 'assets/img/files/folder-64.png';
+ return 'assets/img/files/folder.png';
}
/**
@@ -290,7 +290,7 @@ export class CoreMimetypeUtilsProvider {
* @return The icon path.
*/
getFileIconForType(type: string): string {
- return 'assets/img/files/' + type + '-64.png';
+ return `assets/img/files/${type}.png`;
}
/**
diff --git a/src/core/singletons/colors.ts b/src/core/singletons/colors.ts
index d0e0e3dfa..ed70b8bdf 100644
--- a/src/core/singletons/colors.ts
+++ b/src/core/singletons/colors.ts
@@ -90,30 +90,44 @@ export class CoreColors {
* Returns the hex code from any color css type (ie named).
*
* @param color Color in any format.
- * @returns Color in hex format.
+ * @return Color in hex format.
*/
static getColorHex(color: string): string {
- const d = document.createElement('div');
- d.style.color = color;
- document.body.appendChild(d);
-
- // Color in RGB .
- const matches = getComputedStyle(d).color.match(/\d+/g) || [];
- if (matches.length == 0) {
+ const rgba = CoreColors.getColorRGBA(color, true);
+ if (rgba.length === 0) {
return '';
}
- const rgba = matches.map((a) => parseInt(a, 10));
-
const hex = [0,1,2].map(
(idx) => this.componentToHex(rgba[idx]),
).join('');
- document.body.removeChild(d);
-
return '#' + hex;
}
+ /**
+ * Returns RGBA color from any color format.
+ *
+ * @param color Color in any format.
+ * @param createElement Wether create a new element is needed to calculate value.
+ * @return Red, green, blue and alpha.
+ */
+ static getColorRGBA(color: string, createElement = false): number[] {
+ if (createElement) {
+ const d = document.createElement('span');
+ d.style.color = color;
+ document.body.appendChild(d);
+
+ // Color in RGB.
+ color = getComputedStyle(d).color;
+ document.body.removeChild(d);
+ }
+
+ const matches = color.match(/\d+/g) || [];
+
+ return matches.map((a, index) => index < 3 ? parseInt(a, 10) : parseFloat(a));
+ }
+
/**
* Gets the luma of a color.
*
diff --git a/src/theme/components/format-text.scss b/src/theme/components/format-text.scss
index a242fe175..808d38dbc 100644
--- a/src/theme/components/format-text.scss
+++ b/src/theme/components/format-text.scss
@@ -208,6 +208,11 @@ core-format-text,
core-rich-text-editor .core-rte-editor {
@include core-headings();
+ p, ul, ol, li {
+ // Normalize font-size inside formatted text.
+ font-size: 14px;
+ }
+
p {
margin-bottom: 1rem;
margin-block-start: 0;
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 78a7bf894..a97d2eea2 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -706,10 +706,35 @@ body.core-iframe-fullscreen ion-router-outlet {
.item.item-file {
ion-thumbnail {
- --size: 32px;
+ --size: 24px;
width: var(--size);
height: var(--size);
}
+
+ p.item-heading {
+ font-size: 14px;
+ }
+
+ p {
+ font-size: 12px;
+ }
+
+ ion-label {
+ margin-top: 8px;
+ margin-bottom: 8px;
+ }
+
+ ion-button {
+ --a11y-min-target-size: 40px;
+ }
+
+ &.item-directory ion-icon {
+ @include margin-horizontal(0px, 16px);
+ }
+
+ [slot=end] {
+ @include margin-horizontal(16px, null);
+ }
}
.item-dimmed {
@@ -870,7 +895,8 @@ ion-action-sheet.md {
// Radio.
ion-radio,
-input[type=radio] {
+input[type=radio],
+.select-alert.ios .alert-radio-icon {
--color: var(--text-color);
--color-checked: var(--color);
--border-radius: 50%;
@@ -882,7 +908,8 @@ input[type=radio] {
}
.ios ion-radio,
-.ios input[type=radio] {
+.ios input[type=radio],
+.select-alert.ios .alert-radio-icon {
--border-width: 1px;
--outer-border-width: 1px;
}
@@ -911,8 +938,8 @@ input[type=radio] {
&.radio-checked {
&::part(container) {
- border-color: var(--color-checked);
- background: var(--color-checked);
+ border-color: var(--color);
+ background: var(--color);
}
&::part(mark) {
@@ -921,6 +948,44 @@ input[type=radio] {
}
}
+.select-alert.ios {
+ .alert-radio-icon {
+ height: var(--size);
+ width: var(--size);
+ min-width: var(--size);
+ border-radius: var(--border-radius);
+ border-width: var(--outer-border-width);
+ border-style: var(--border-style);
+ border-color: var(--color);
+ @include margin(10px, 8px, 10px, 8px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .alert-radio-inner {
+ top: auto;
+ left: auto;
+ position: static;
+ border-radius: var(--inner-border-radius);
+ width: calc(50% + var(--outer-border-width));
+ height: calc(50% + var(--outer-border-width));
+ transform: scale3d(0, 0, 0);
+ transition: transform 280ms cubic-bezier(.4, 0, .2, 1);
+ background: var(--contrast-background);
+ border: 0 !important;
+ }
+ }
+
+ button[aria-checked=true] .alert-radio-icon {
+ border-color: var(--color-checked);
+ background: var(--color-checked);
+
+ .alert-radio-inner {
+ transform: scale3d(1, 1, 1);
+ }
+ }
+}
+
// Checkbox.
ion-checkbox,
input[type=checkbox] {
@@ -938,6 +1003,10 @@ input[type=checkbox] {
--outer-border-width: 1px;
}
+.select-alert.ios .alert-checkbox-icon {
+ border-radius: 2px;
+}
+
// Select.
ion-select::part(text) {
white-space: normal;
@@ -1000,9 +1069,7 @@ ion-chip {
border-color: var(--ion-color-base);
color: var(--ion-color-base);
}
- }
- &.ion-color {
&.ion-color-light,
&.ion-color-medium,
&.ion-color-dark,
@@ -1013,6 +1080,10 @@ ion-chip {
}
}
}
+
+ &:not(.ion-color) ion-icon {
+ color: var(--text-color);
+ }
}
ion-searchbar {