Merge pull request #4079 from crazyserver/MOBILE-4470
MOBILE-4470 icon: Revert load SVG directly and use img and filtersmain
commit
ef3b6d65c2
|
@ -1,6 +1,3 @@
|
|||
<ng-container *ngIf="loaded() && !svgIcon()">
|
||||
<img *ngIf="!isLocalUrl()" [url]="iconUrl()" core-external-content alt="" [component]="linkIconWithComponent() ? modname : null"
|
||||
[componentId]="linkIconWithComponent() ? componentId : null" (error)="loadFallbackIcon()">
|
||||
<img *ngIf="isLocalUrl()" [src]="iconUrl()" (error)="loadFallbackIcon()" alt="">
|
||||
</ng-container>
|
||||
<div *ngIf="svgIcon()" [innerHTML]="svgIcon()"></div>
|
||||
<img *ngIf="!isLocalUrl()" [url]="iconUrl()" core-external-content alt="" [component]="linkIconWithComponent() ? modname : null"
|
||||
[componentId]="linkIconWithComponent() ? componentId : null" (error)="loadFallbackIcon()">
|
||||
<img *ngIf="isLocalUrl()" [src]="iconUrl()" (error)="loadFallbackIcon()" alt="">
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
background-color: transparent;
|
||||
line-height: var(--size);
|
||||
|
||||
--color: var(--activity-base-icon-color);
|
||||
|
||||
&.colorize {
|
||||
&.version_current {
|
||||
@each $type, $value in $activity-icon-colors {
|
||||
&.#{$type} {
|
||||
--color: var(--activity#{$type});
|
||||
@each $type, $value in $activity-icon-color-filters {
|
||||
&.#{$type}:not(.branded) {
|
||||
img {
|
||||
filter: var(--activity#{$type});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
|||
@each $type, $value in $activity-icon-background-colors {
|
||||
&.#{$type}:not(.branded) {
|
||||
background-color: var(--activity-40-#{$type});
|
||||
::ng-deep svg, img {
|
||||
img {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
}
|
||||
|
@ -57,15 +57,7 @@
|
|||
--padding-bottom: var(--module-legacy-icon-padding, 8px);
|
||||
}
|
||||
|
||||
&.colorize.version_current:not(.branded) {
|
||||
::ng-deep svg,
|
||||
::ng-deep svg * {
|
||||
fill: var(--color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
img,
|
||||
::ng-deep svg {
|
||||
img {
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
max-width: var(--size);
|
||||
|
|
|
@ -24,17 +24,11 @@ import {
|
|||
SimpleChange,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { SafeHtml } from '@angular/platform-browser';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
import { CoreFile } from '@services/file';
|
||||
import { CoreFileHelper } from '@services/file-helper';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { DomSanitizer, Http } from '@singletons';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
const assetsPath = 'assets/img/';
|
||||
const fallbackModName = 'external-tool';
|
||||
|
@ -80,9 +74,7 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
|||
iconUrl = signal('');
|
||||
modNameTranslated = signal('');
|
||||
isLocalUrl = signal(false);
|
||||
svgIcon = signal<SafeHtml>('');
|
||||
linkIconWithComponent = signal(false);
|
||||
loaded = signal(false);
|
||||
|
||||
protected iconVersion: IconVersion = IconVersion.LEGACY_VERSION;
|
||||
protected purposeClass = '';
|
||||
|
@ -208,8 +200,6 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
|||
);
|
||||
|
||||
this.setBrandedClass();
|
||||
|
||||
await this.setSVGIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,8 +220,6 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
|||
const path = CoreCourse.getModuleIconsPath();
|
||||
|
||||
this.iconUrl.set(path + moduleName + '.svg');
|
||||
|
||||
await this.setSVGIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,141 +294,4 @@ export class CoreModIconComponent implements OnInit, OnChanges {
|
|||
return IconVersion.CURRENT_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets SVG markup for the icon (if the URL is an SVG).
|
||||
*
|
||||
* @returns Promise resolved when done.
|
||||
*/
|
||||
protected async setSVGIcon(): Promise<void> {
|
||||
if (this.iconVersion === IconVersion.LEGACY_VERSION) {
|
||||
this.loaded.set(true);
|
||||
this.svgIcon.set('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.loaded.set(false);
|
||||
|
||||
let mimetype = '';
|
||||
let fileContents = '';
|
||||
|
||||
// Download the icon if it's not local to cache it.
|
||||
if (!this.isLocalUrl()) {
|
||||
try {
|
||||
const iconUrl = await CoreFileHelper.downloadFile(
|
||||
this.iconUrl(),
|
||||
this.linkIconWithComponent() ? this.modname : undefined,
|
||||
this.linkIconWithComponent() ? this.componentId : undefined,
|
||||
);
|
||||
if (iconUrl) {
|
||||
mimetype = await CoreUtils.getMimeTypeFromUrl(iconUrl);
|
||||
fileContents = await CoreFile.readFile(iconUrl);
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (!fileContents) {
|
||||
// Try to download the icon directly (also for local files).
|
||||
const response = await firstValueFrom(Http.get(
|
||||
this.iconUrl(),
|
||||
{
|
||||
observe: 'response',
|
||||
responseType: 'text',
|
||||
},
|
||||
));
|
||||
mimetype = response.headers.get('content-type') || mimetype;
|
||||
fileContents = response.body || '';
|
||||
}
|
||||
|
||||
if (mimetype !== 'image/svg+xml' || !fileContents) {
|
||||
this.svgIcon.set('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean the DOM to avoid security issues.
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(fileContents, 'image/svg+xml');
|
||||
|
||||
// Safety check.
|
||||
if (doc.documentElement.nodeName !== 'svg') {
|
||||
this.svgIcon.set('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove scripts tags.
|
||||
const scripts = doc.documentElement.getElementsByTagName('script');
|
||||
for (let i = scripts.length - 1; i >= 0; i--) {
|
||||
scripts[i].parentNode?.removeChild(scripts[i]);
|
||||
}
|
||||
|
||||
// Has own styles, do not apply colors.
|
||||
if (doc.documentElement.getElementsByTagName('style').length > 0) {
|
||||
this.brandedClass = true;
|
||||
}
|
||||
|
||||
// Recursively remove attributes starting with on.
|
||||
const removeAttributes = (element: Element): void => {
|
||||
Array.from(element.attributes).forEach((attr) => {
|
||||
if (attr.name.startsWith('on')) {
|
||||
element.removeAttribute(attr.name);
|
||||
}
|
||||
});
|
||||
|
||||
Array.from(element.children).forEach((child) => {
|
||||
removeAttributes(child);
|
||||
});
|
||||
};
|
||||
removeAttributes(doc.documentElement);
|
||||
|
||||
// Add viewBox to avoid scaling issues.
|
||||
if (!doc.documentElement.getAttribute('viewBox')) {
|
||||
const width = doc.documentElement.getAttribute('width');
|
||||
const height = doc.documentElement.getAttribute('height');
|
||||
if (width && height) {
|
||||
doc.documentElement.setAttribute('viewBox', '0 0 '+ width + ' ' + height);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix id's on svg DOM to avoid conflicts.
|
||||
const uniqueId = 'modicon' + CoreUtils.getUniqueId('modicon') + '_';
|
||||
const styleTags = Array.from(doc.documentElement.getElementsByTagName('style'));
|
||||
const styleAttrs = Array.from(doc.documentElement.querySelectorAll('[style]'));
|
||||
const idTags = Array.from(doc.documentElement.querySelectorAll('[id]'));
|
||||
idTags.forEach((element) => {
|
||||
if (!element.id) {
|
||||
return;
|
||||
}
|
||||
const newId = uniqueId + element.id;
|
||||
// Regexp to replace all ocurrences of the id with workd bondaries.
|
||||
const oldIdFinder = new RegExp(`#${element.id}\\b`, 'g');
|
||||
|
||||
element.id = newId;
|
||||
|
||||
// Prefix the elementId on style Tags.
|
||||
styleTags.forEach((style) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
style.textContent = style.textContent!.replace(oldIdFinder, `#${newId}`);
|
||||
});
|
||||
|
||||
// Also change ids on style attributes.
|
||||
styleAttrs.forEach((attr) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
attr.setAttribute('style', attr.getAttribute('style')!.replace(oldIdFinder, `#${newId}`));
|
||||
});
|
||||
});
|
||||
|
||||
this.svgIcon.set(DomSanitizer.bypassSecurityTrustHtml(doc.documentElement.outerHTML));
|
||||
} catch {
|
||||
this.svgIcon.set('');
|
||||
} finally {
|
||||
this.loaded.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</div>
|
||||
|
||||
<ion-textarea [hidden]="rteEnabled" #textarea class="core-textarea" role="textbox" [attr.name]="name" ngControl="control"
|
||||
[placeholder]="placeholder" [attr.aria-labelledby]="ariaLabelledBy" (ionChange)="onChange()" (ionFocus)="focusRTE($event)"
|
||||
[placeholder]="placeholder" [aria-labelledby]="ariaLabelledBy" (ionChange)="onChange()" (ionFocus)="focusRTE($event)"
|
||||
(ionBlur)="blurRTE($event)" />
|
||||
|
||||
<div class="core-rte-info-message" *ngIf="infoMessage">
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<core-sites-list [accountsList]="accountsList" [sitesClickable]="true" [currentSiteClickable]="false"
|
||||
(onSiteClicked)="login($event)">
|
||||
<ng-template #siteItem let-site="site" let-isCurrentSite="isCurrentSite">
|
||||
<ion-icon *ngIf="isCurrentSite" color="success" name="fas-check" />
|
||||
<ion-icon *ngIf="isCurrentSite" color="success" name="fas-check" aria-hidden="true" />
|
||||
|
||||
<ng-container *ngIf="!isCurrentSite">
|
||||
<ion-badge slot="end" *ngIf="!showDelete && site.badge" @coreShowHideAnimation>
|
||||
|
|
|
@ -136,6 +136,15 @@ $activity-icon-colors: (
|
|||
interactivecontent: #8d3d1b
|
||||
) !default;
|
||||
|
||||
$activity-icon-color-filters: (
|
||||
administration: brightness(0%) invert(45%) sepia(46%) saturate(3819%) hue-rotate(260deg) brightness(101%) contrast(87%),
|
||||
assessment: brightness(0%) invert(36%) sepia(98%) saturate(6969%) hue-rotate(315deg) brightness(90%) contrast(119%),
|
||||
collaboration: brightness(0%) invert(25%) sepia(54%) saturate(6226%) hue-rotate(245deg) brightness(100%) contrast(102%),
|
||||
communication: brightness(0%) invert(48%) sepia(74%) saturate(4887%) hue-rotate(11deg) brightness(102%) contrast(101%),
|
||||
content: brightness(0%) invert(49%) sepia(52%) saturate(4675%) hue-rotate(156deg) brightness(89%) contrast(102%),
|
||||
interactivecontent: brightness(0%) invert(25%) sepia(63%) saturate(1152%) hue-rotate(344deg) brightness(94%) contrast(91%)
|
||||
) !default;
|
||||
|
||||
$calendar-event-category-category: #8e24aa !default;
|
||||
$calendar-event-category-course: $red !default;
|
||||
$calendar-event-category-group: $yellow !default;
|
||||
|
|
|
@ -172,5 +172,4 @@ html.dark {
|
|||
--core-table-even-cell-background: var(--gray-900);
|
||||
--core-table-even-cell-hover: var(--gray-700);
|
||||
|
||||
--activity-base-icon-color: var(--white);
|
||||
}
|
||||
|
|
|
@ -201,9 +201,8 @@ html {
|
|||
--core-dd-question-color-#{$i + 1}-contrast: #{get_contrast_color(nth($core-dd-question-colors, $i + 1))};
|
||||
}
|
||||
|
||||
--activity-base-icon-color: var(--gray-900);
|
||||
// Make activtity colours available for custom modules.
|
||||
@each $type, $value in $activity-icon-colors {
|
||||
@each $type, $value in $activity-icon-color-filters {
|
||||
--activity#{$type}: #{$value};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue