diff --git a/package-lock.json b/package-lock.json index bd88b08bc..aa7eda930 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8574,6 +8574,11 @@ "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", "dev": true }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", diff --git a/package.json b/package.json index 774cc91a8..b999cd2c8 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "cordova-support-google-services": "^1.2.1", "cordova.plugins.diagnostic": "^6.0.2", "es6-promise-plugin": "^4.2.2", + "font-awesome": "^4.7.0", "moment": "^2.29.0", "nl.kingsquare.cordova.background-audio": "^1.0.1", "phonegap-plugin-multidex": "^1.0.0", diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts new file mode 100644 index 000000000..55bee79bb --- /dev/null +++ b/src/app/components/components.module.ts @@ -0,0 +1,27 @@ +// (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 { NgModule } from '@angular/core'; +import { CoreIconComponent } from './icon/icon'; + +@NgModule({ + declarations: [ + CoreIconComponent, + ], + imports: [], + exports: [ + CoreIconComponent, + ] +}) +export class CoreComponentsModule {} diff --git a/src/app/components/icon/core-icon.html b/src/app/components/icon/core-icon.html new file mode 100644 index 000000000..7c89b545c --- /dev/null +++ b/src/app/components/icon/core-icon.html @@ -0,0 +1 @@ +
diff --git a/src/app/components/icon/icon.scss b/src/app/components/icon/icon.scss new file mode 100644 index 000000000..0b3467edb --- /dev/null +++ b/src/app/components/icon/icon.scss @@ -0,0 +1,52 @@ +// TODO ionic 5 +:host-context([dir=rtl]) ion-icon { + &.core-icon-dir-flip, + &.fa-caret-right, + &.ion-md-send, &.ion-ios-send { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); + } +} + +// Slash +@font-face { + font-family: "Moodle Slash Icon"; + font-style: normal; + font-weight: 400; + src: url("/assets/fonts/slash-icon.woff") format("woff"); +} + +:host { + &.fa { + font-size: 24px; + } + + // Center font awesome icons + &.fa::before { + width: 1em; + height: 1em; + text-align: center; + } + + &.icon-slash { + position: relative; + &::after { + content: "/"; + font-family: "Moodle Slash Icon"; + font-size: 0.75em; + margin-top: 0.125em; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + text-align: center; + color: var(--ion-color-danger); + } + + &.fa::after { + font-size: 1em; + margin-top: 0; + } + } +} diff --git a/src/app/components/icon/icon.ts b/src/app/components/icon/icon.ts new file mode 100644 index 000000000..ff70d868e --- /dev/null +++ b/src/app/components/icon/icon.ts @@ -0,0 +1,135 @@ +// (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, OnChanges, OnDestroy, ElementRef, SimpleChange } from '@angular/core'; + +/** + * Core Icon is a component that enables the posibility to add fontawesome icon to the html. It's recommended if both fontawesome + * or ionicons can be used in the name attribute. To use fontawesome just place the full icon name with the fa- prefix and + * the component will detect it. + * Check available icons at https://fontawesome.com/v4.7.0/icons/. + */ +@Component({ + selector: 'core-icon', + templateUrl: 'core-icon.html', + styleUrls: ['icon.scss'], +}) +export class CoreIconComponent implements OnChanges, OnDestroy { + // Common params. + @Input() name: string; + @Input('color') color?: string; + @Input('slash') slash?: boolean; // Display a red slash over the icon. + + // Ionicons params. + @Input('isActive') isActive?: boolean; + @Input('md') md?: string; + @Input('ios') ios?: string; + + // FontAwesome params. + @Input('fixed-width') fixedWidth: string; + + @Input('label') ariaLabel?: string; + @Input() flipRtl?: boolean; // Whether to flip the icon in RTL. Defaults to false. + + protected element: HTMLElement; + protected newElement: HTMLElement; + + constructor(el: ElementRef) { + this.element = el.nativeElement; + } + + /** + * Detect changes on input properties. + */ + ngOnChanges(changes: {[name: string]: SimpleChange}): void { + if (!changes.name || !this.name) { + return; + } + + const oldElement = this.newElement ? this.newElement : this.element; + + // Use a new created element to avoid ion-icon working. + // This is necessary to make the FontAwesome stuff work. + // It is also required to stop Ionic overriding the aria-label attribute. + this.newElement = document.createElement('ion-icon'); + if (this.name.startsWith('fa-')) { + this.newElement.classList.add('fa'); + this.newElement.classList.add(this.name); + if (this.isTrueProperty(this.fixedWidth)) { + this.newElement.classList.add('fa-fw'); + } + if (this.color) { + this.newElement.classList.add('fa-' + this.color); + } + } + + !this.ariaLabel && this.newElement.setAttribute('aria-hidden', 'true'); + !this.ariaLabel && this.newElement.setAttribute('role', 'presentation'); + this.ariaLabel && this.newElement.setAttribute('aria-label', this.ariaLabel); + this.ariaLabel && this.newElement.setAttribute('title', this.ariaLabel); + + const attrs = this.element.attributes; + for (let i = attrs.length - 1; i >= 0; i--) { + if (attrs[i].name == 'class') { + // We don't want to override the classes we already added. Add them one by one. + if (attrs[i].value) { + const classes = attrs[i].value.split(' '); + for (let j = 0; j < classes.length; j++) { + if (classes[j]) { + this.newElement.classList.add(classes[j]); + } + } + } + + } else { + this.newElement.setAttribute(attrs[i].name, attrs[i].value); + } + } + + if (this.slash) { + this.newElement.classList.add('icon-slash'); + } + + if (this.flipRtl) { + this.newElement.classList.add('core-icon-dir-flip'); + } + + oldElement.parentElement.replaceChild(this.newElement, oldElement); + } + + /** + * Check if the value is true or on. + * + * @param val value to be checked. + * @return If has a value equivalent to true. + */ + isTrueProperty(val: any): boolean { + if (typeof val === 'string') { + val = val.toLowerCase().trim(); + + return (val === 'true' || val === 'on' || val === ''); + } + + return !!val; + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + if (this.newElement) { + this.newElement.remove(); + } + } +} diff --git a/src/assets/fonts/slash-icon.woff b/src/assets/fonts/slash-icon.woff new file mode 100644 index 000000000..5e02178e4 Binary files /dev/null and b/src/assets/fonts/slash-icon.woff differ diff --git a/src/global.scss b/src/global.scss index d854de84a..36c0be50d 100644 --- a/src/global.scss +++ b/src/global.scss @@ -9,6 +9,7 @@ * https://ionicframework.com/docs/layout/global-stylesheets */ + /* Core CSS required for Ionic components to work properly */ @import "~@ionic/angular/css/core.css"; @@ -24,3 +25,6 @@ @import "~@ionic/angular/css/text-alignment.css"; @import "~@ionic/angular/css/text-transformation.css"; @import "~@ionic/angular/css/flex-utils.css"; + +/* Font awesome */ +@import "~font-awesome/scss/font-awesome.scss";