MOBILE-2253 login: Improve styles

main
Pau Ferrer Ocaña 2017-12-18 10:48:04 +01:00 committed by Dani Palou
parent ecb4ae5ff4
commit ca1e510b8f
24 changed files with 579 additions and 368 deletions

View File

@ -0,0 +1,74 @@
.button-ios {
min-height: $button-ios-height
}
// Light buttons color.
.button-ios-light {
color: color($colors, primary, base);
}
// Highlights inside the input element.
@if ($mm-text-input-ios-show-highlight) {
.card-ios, .list-ios {
// In order to get a 2px border we need to add an inset
// box-shadow 1px (this is to avoid the div resizing)
// The last item in a list has a border on the item, not the
// inner item, so add it to the item itself
.item-input.item-input-has-focus:last-child,
.item-input.input-has-focus:last-child {
@include ios-input-highlight($text-input-ios-highlight-color);
.item-inner ion-input {
box-shadow: none;
}
}
// Show the focus highlight when the input has focus
.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child,
.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include ios-input-highlight($text-input-ios-highlight-color-valid);
.item-inner ion-input {
box-shadow: none;
}
}
.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include ios-input-highlight($text-input-ios-highlight-color-invalid);
.item-inner ion-input {
box-shadow: none;
}
}
}
.item-ios.item-input {
.item-inner {
border: 0;
}
&.item-block .item-inner ion-input {
border-bottom: $hairlines-width solid $list-border-color;
}
// TODO remove all uses of input-has-focus in v4
&.item-input-has-focus .item-inner ion-input,
&.input-has-focus .item-inner ion-input {
@include ios-input-highlight($text-input-ios-highlight-color);
}
// Show the valid highlight when it has the .ng-valid class and a value
&.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner ion-input,
&.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner ion-input {
@include ios-input-highlight($text-input-ios-highlight-color-valid);
}
// Show the invalid highlight when it has the invalid class and has been touched
&.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .item-inner ion-input {
@include ios-input-highlight($text-input-ios-highlight-color-invalid);
}
}
}

View File

@ -0,0 +1,74 @@
.button-md {
min-height: $button-md-height;
}
// Light buttons color.
.button-md-light {
color: color($colors, primary, base);
}
// Highlights inside the input element.
@if ($mm-text-input-md-show-highlight) {
.card-md, .list-md {
// In order to get a 2px border we need to add an inset
// box-shadow 1px (this is to avoid the div resizing)
// The last item in a list has a border on the item, not the
// inner item, so add it to the item itself
.item-input.item-input-has-focus:last-child,
.item-input.input-has-focus:last-child {
@include md-input-highlight($text-input-md-highlight-color);
.item-inner ion-input {
box-shadow: none;
}
}
.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child,
.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include md-input-highlight($text-input-md-highlight-color-valid);
.item-inner ion-input {
box-shadow: none;
}
}
.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include md-input-highlight($text-input-md-highlight-color-invalid);
.item-inner ion-input {
box-shadow: none;
}
}
}
.item-md.item-input {
.item-inner {
border: 0;
}
&.item-block .item-inner ion-input {
border-bottom: 1px solid $list-border-color;
}
// TODO remove all uses of input-has-focus in v4
&.item-input-has-focus .item-inner ion-input,
&.input-has-focus .item-inner ion-input {
@include md-input-highlight($text-input-md-highlight-color);
}
// Show the valid highlight when it has the .ng-valid class and a value
// TODO remove all uses of input-has-focus in v4
// TODO remove all uses of input-has-value in v4
&.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner ion-input,
&.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner ion-input {
@include md-input-highlight($text-input-md-highlight-color-valid);
}
// Show the invalid highlight when it has the invalid class and has been touched
&.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .item-inner ion-input {
@include md-input-highlight($text-input-md-highlight-color-invalid);
}
}
}

View File

@ -22,60 +22,22 @@
.text-left { text-align: left; } .text-left { text-align: left; }
.text-right { text-align: right; } .text-right { text-align: right; }
.text-center { text-align: center; } .text-center { text-align: center; }
.text-justify { text-align: justify; }
/**
* App Branding
*/
.mm-bglogo {
background-color: $mm-color-init-screen; /* Change this to add a bg image or change color */
background: -webkit-radial-gradient($mm-color-init-screen-alt, $mm-color-init-screen);
background: radial-gradient($mm-color-init-screen-alt, $mm-color-init-screen);
background-repeat: no-repeat;
background-position: center center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
display: table;
.mm-logo {
display: table-cell;
text-align: center;
vertical-align: middle;
}
img {
width: $mm-init-screen-logo-width;
max-width: $mm-init-screen-logo-max-width;
display: block;
margin: 0 auto;
margin-bottom: 30px;
}
.spinner circle {
stroke: $mm-init-screen-spinner-color;
}
}
@media only screen and (min-width: 430px) { @media only screen and (min-width: 430px) {
.mm-center-view .scroll-content { .mm-center-view .scroll-content {
margin: 0 auto; display: flex!important;
max-width: 600px; align-content: center !important;
/* display: table !important; */ align-items: center !important;
width: 100% !important; > * {
height: 100% !important; margin: 0 auto;
.mm-view-content { max-width: 600px;
/* display: table-cell;
vertical-align: middle; */
display: block; // Added this style and commented some others to make scroll work. Box isn't centered vertically.
} }
} }
} }
// Define an alternative way to set a heading in an item without using a heading tag. // Define an alternative way to set a heading in an item without using a heading tag.
// This is done for accessibility reasons when a heading is semantically incorrect. // This is done for accessibility reasons when a heading is semantically incorrect.
.item .item-heading { .item .item-heading {
@ -83,52 +45,29 @@
} }
.mm-oauth-icon, .item.mm-oauth-icon, .list .item.mm-oauth-icon { .mm-oauth-icon, .item.mm-oauth-icon, .list .item.mm-oauth-icon {
padding: ($content-padding / 2); min-height: 32px;
border: 1px solid $list-border-color; img, .label {
img, span {
max-height: 32px; max-height: 32px;
vertical-align: middle; vertical-align: middle;
} }
img { img {
max-width: 32px; max-width: 32px;
} }
span { .label {
margin-left: 5px; margin-left: 5px;
color: $gray-darker; color: $gray-darker;
} }
.label {
margin: 0;
}
}
.icon-accessory,
ion-icon.icon-accessory {
color: $item-icon-accessory-color;
font-size: $item-icon-accessory-font-size;
} }
.mm-bold, .mm-bold .label { .mm-bold, .mm-bold .label {
font-weight: bold; font-weight: bold;
} }
.item .core-input-footnote { // Avatar
width: 100%; // -------------------------
padding-top: 10px; // Large centered avatar
padding-bottom: 10px;
font-style: italic;
}
.content {
// Set bgcolor in content instead of overriding $background-color because that variable is applied to a lot of places.
background-color: $gray-light;
}
// Large centered avatar.
.item-avatar-center { .item-avatar-center {
text-align: center; text-align: center;
padding-left: 16px;
&.item-complex .item-content { &.item-complex .item-content {
text-align: center; text-align: center;
@ -158,3 +97,19 @@ ion-icon.icon-accessory {
} }
} }
} }
ion-avatar ion-img, ion-avatar img {
text-indent: -99999px;
background-color: $gray-light;
}
// Form items
// -------------------------
.item .core-input-footnote {
width: 100%;
padding-top: 10px;
padding-bottom: 10px;
font-style: italic;
}

View File

@ -0,0 +1,9 @@
.button-wp {
min-height: $button-wp-height;
}
// Light buttons color.
.button-wp-light {
color: color($colors, primary, base);
}

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,5 +1,5 @@
<div class="core-input-error-container" *ngIf="formControl.dirty && !formControl.valid" role="alert"> <div class="core-input-error-container" *ngIf="formControl.dirty && !formControl.valid" role="alert">
<div *ngFor="let error of errorKeys"> <ng-container *ngFor="let error of errorKeys">
<div *ngIf="formControl.hasError(error)" class="core-input-error">{{errorMessages[error]}}</div> <div *ngIf="formControl.hasError(error)" class="core-input-error">{{errorMessages[error]}}</div>
</div> </ng-container>
</div> </div>

View File

@ -4,8 +4,13 @@ core-input-errors {
.core-input-error-container { .core-input-error-container {
.core-input-error { .core-input-error {
padding: 4px; padding: 4px;
color: red; color: $red;
font-size: 12px; font-size: 12px;
display: none;
&:first-child {
display: block;
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
*[core-mark-required] { *[core-mark-required] {
.core-input-required-asterisk, .icon.core-input-required-asterisk { .core-input-required-asterisk, .icon.core-input-required-asterisk {
color: red !important; color: $red !important;
font-size: 8px; font-size: 8px;
padding-left: 4px; padding-left: 4px;
line-height: 100%; line-height: 100%;

View File

@ -3,11 +3,12 @@ core-show-password {
width: 100%; width: 100%;
position: relative; position: relative;
ion-input { ion-input input.text-input {
padding-right: 47 + $content-padding; padding-right: 47px;
} }
.button[icon-only] { .button[icon-only] {
background: transparent;
padding: 0 ($content-padding / 2); padding: 0 ($content-padding / 2);
position: absolute; position: absolute;
top: $content-padding / 2; top: $content-padding / 2;
@ -17,8 +18,27 @@ core-show-password {
} }
} }
.item-label-stacked core-show-password { .md {
.button[icon-only] { .item-label-stacked core-show-password .button[icon-only] {
top: 0; top: 0;
} }
} }
.ios {
.item-label-stacked core-show-password .button[icon-only] {
top: -5px;
}
core-show-password .button[icon-only] {
top: 0;
}
}
.wp {
.item-label-stacked core-show-password .button[icon-only] {
top: 7px;
}
core-show-password .button[icon-only] {
top: 12px;
right: 5px;
}
}

View File

@ -3,21 +3,21 @@
<ion-title>{{ 'core.login.login' | translate }}</ion-title> <ion-title>{{ 'core.login.login' | translate }}</ion-title>
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content padding class="mm-center-view"> <ion-content class="mm-center-view">
<core-loading [hideUntil]="pageLoaded" class="mm-view-content"> <core-loading [hideUntil]="pageLoaded">
<ion-list no-lines class="box"> <div class="box">
<ion-item text-wrap class="text-center"> <div text-wrap text-center>
<!-- Show site logo or a default image. --> <!-- Show site logo or a default image. -->
<img *ngIf="logoUrl" [src]="logoUrl" role="presentation"> <img *ngIf="logoUrl" [src]="logoUrl" role="presentation">
<img *ngIf="!logoUrl" src="assets/img/logo.png" class="moodle-logo" role="presentation"> <img *ngIf="!logoUrl" src="assets/img/login_logo.png" class="login-logo" role="presentation">
<!-- If no sitename show big siteurl. --> <!-- If no sitename show big siteurl. -->
<p *ngIf="!siteName" padding class="item-heading mm-siteurl">{{siteUrl}}</p> <p *ngIf="!siteName" padding class="item-heading mm-siteurl">{{siteUrl}}</p>
<!-- If sitename, show big sitename and small siteurl. --> <!-- If sitename, show big sitename and small siteurl. -->
<p *ngIf="siteName" padding class="item-heading mm-sitename">{{siteName}}</p> <p *ngIf="siteName" padding class="item-heading mm-sitename">{{siteName}}</p>
<p *ngIf="siteName" class="mm-siteurl">{{siteUrl}}</p> <p *ngIf="siteName" class="mm-siteurl">{{siteUrl}}</p>
</ion-item> </div>
<form [formGroup]="credForm" (ngSubmit)="login()"> <form ion-list [formGroup]="credForm" (ngSubmit)="login()">
<ion-item *ngIf="siteChecked && !isBrowserSSO"> <ion-item *ngIf="siteChecked && !isBrowserSSO">
<ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" formControlName="username" autocapitalize="none" autocorrect="off"></ion-input> <ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" formControlName="username" autocapitalize="none" autocorrect="off"></ion-input>
</ion-item> </ion-item>
@ -26,30 +26,29 @@
<ion-input class="mm-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" mm-show-password></ion-input> <ion-input class="mm-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" mm-show-password></ion-input>
</core-show-password> </core-show-password>
</ion-item> </ion-item>
<button ion-button block color="primary" [disabled]="siteChecked && !isBrowserSSO && !credForm.valid">{{ 'core.login.loginbutton' | translate }}</button> <button ion-button block [disabled]="siteChecked && !isBrowserSSO && !credForm.valid">{{ 'core.login.loginbutton' | translate }}</button>
</form> </form>
<!-- Forgotten password button. --> <!-- Forgotten password button. -->
<div padding-top> <div padding-top>
<button ion-button block text-wrap (click)="forgottenPassword()">{{ 'core.login.forgotten' | translate }}</button> <button ion-button block text-wrap color="light" (click)="forgottenPassword()">{{ 'core.login.forgotten' | translate }}</button>
</div> </div>
<div *ngIf="identityProviders && identityProviders.length" padding-top> <ion-list *ngIf="identityProviders && identityProviders.length" padding-top>
<p>{{ 'core.login.potentialidps' | translate }}</p> <ion-list-header text-wrap>{{ 'core.login.potentialidps' | translate }}</ion-list-header>
<ion-item *ngFor="let provider of identityProviders" text-wrap class="mm-oauth-icon" (click)="oauthClicked(provider)" title="{{provider.name}}"> <button ion-item *ngFor="let provider of identityProviders" text-wrap class="mm-oauth-icon" (click)="oauthClicked(provider)" title="{{provider.name}}">
<img [src]="provider.iconurl" alt="{{provider.name}}"> <img [src]="provider.iconurl" alt="{{provider.name}}" item-start>
<span>{{provider.name}}</span> {{provider.name}}
<ion-icon class="icon-accessory" name="arrow-forward" md="ios-arrow-forward" item-end></ion-icon> </button>
</ion-item> </ion-list>
</div>
<div *ngIf="canSignup"> <ion-list *ngIf="canSignup" padding-top>
<ion-item text-wrap> <ion-list-header text-wrap>{{ 'core.login.firsttime' | translate }}</ion-list-header>
<p class="item-heading">{{ 'core.login.firsttime' | translate }}</p> <ion-item no-lines text-wrap *ngIf="authInstructions">
<p *ngIf="authInstructions"><core-format-text [text]="authInstructions"></core-format-text></p> <p><core-format-text [text]="authInstructions"></core-format-text></p>
</ion-item> </ion-item>
<button ion-button block (click)="signup()">{{ 'core.login.startsignup' | translate }}</button> <button ion-button block (click)="signup()">{{ 'core.login.startsignup' | translate }}</button>
</div> </ion-list>
</ion-list> </div>
</core-loading> </core-loading>
</ion-content> </ion-content>

View File

@ -1,34 +1,35 @@
page-core-login-credentials { page-core-login-credentials {
.content { .content {
.mm-ioninput-password {
padding-top: 0;
padding-bottom: 0;
}
background: -webkit-radial-gradient(white, $gray-light); background: -webkit-radial-gradient(white, $gray-light);
background: radial-gradient(white, $gray-light); background: radial-gradient(white, $gray-light);
}
img { .mm-ioninput-password {
max-width: 100%; padding-top: 0;
} padding-bottom: 0;
}
img.moodle-logo { img {
width: 90%; max-width: 100%;
max-width: 300px; }
}
.box { img.login-logo {
padding: 16px; width: 90%;
background: $white; max-width: 300px;
border: 1px solid $gray; }
}
.mm-sitename, .mm-siteurl { .box {
@if $mm-fixed-url { display: none; } padding: 16px;
} margin: 8px;
background: $white;
border: 1px solid $gray;
}
.list .item-input { .mm-sitename, .mm-siteurl {
border: 1px solid $list-border-color; @if $mm-fixed-url { display: none; }
margin-bottom: 20px; }
}
.item-input {
margin-bottom: 20px;
} }
} }

View File

@ -15,108 +15,106 @@
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="settingsLoaded"> <core-loading [hideUntil]="settingsLoaded">
<form *ngIf="settingsLoaded && settings" [formGroup]="signupForm" (ngSubmit)="create()" class="list list-inset"> <form ion-list *ngIf="settingsLoaded && settings" [formGroup]="signupForm" (ngSubmit)="create()" class="list list-inset">
<ion-list> <ion-item text-wrap text-center>
<ion-item text-wrap class="text-center"> <!-- If no sitename show big siteurl. -->
<!-- If no sitename show big siteurl. --> <p *ngIf="!siteName" padding class="item-heading">{{siteUrl}}</p>
<p *ngIf="!siteName" padding class="item-heading">{{siteUrl}}</p> <!-- If sitename, show big sitename and small siteurl. -->
<!-- If sitename, show big sitename and small siteurl. --> <p *ngIf="siteName" padding class="item-heading">{{siteName}}</p>
<p *ngIf="siteName" padding class="item-heading">{{siteName}}</p> <p *ngIf="siteName">{{siteUrl}}</p>
<p *ngIf="siteName">{{siteUrl}}</p> </ion-item>
</ion-item>
<!-- Username and password. --> <!-- Username and password. -->
<ion-item-divider text-wrap color="light"> <ion-item-divider text-wrap color="light">
{{ 'core.login.createuserandpass' | translate }} {{ 'core.login.createuserandpass' | translate }}
</ion-item-divider> </ion-item-divider>
<ion-item text-wrap> <ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.login.username' | translate }}</ion-label> <ion-label stacked core-mark-required="true">{{ 'core.login.username' | translate }}</ion-label>
<ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" formControlName="username" autocapitalize="none" autocorrect="off"></ion-input> <ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" formControlName="username" autocapitalize="none" autocorrect="off"></ion-input>
<core-input-errors item-content [control]="signupForm.controls.username" [errorMessages]="usernameErrors"></core-input-errors> <core-input-errors item-content [control]="signupForm.controls.username" [errorMessages]="usernameErrors"></core-input-errors>
</ion-item>
<ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.login.password' | translate }}</ion-label>
<core-show-password item-content [name]="'password'">
<ion-input type="password" name="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password"></ion-input>
</core-show-password>
<p *ngIf="settings.passwordpolicy" item-content class="core-input-footnote">
{{settings.passwordpolicy}}
</p>
<core-input-errors item-content [control]="signupForm.controls.password" [errorMessages]="passwordErrors"></core-input-errors>
</ion-item>
<!-- More details. -->
<ion-item-divider text-wrap color="light">
{{ 'core.login.supplyinfo' | translate }}
</ion-item-divider>
<ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.user.email' | translate }}</ion-label>
<ion-input type="email" name="email" placeholder="{{ 'core.user.email' | translate }}" formControlName="email" autocapitalize="none" autocorrect="off"></ion-input>
<core-input-errors item-content [control]="signupForm.controls.email" [errorMessages]="emailErrors"></core-input-errors>
</ion-item>
<ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.user.emailagain' | translate }}</ion-label>
<ion-input type="email" name="email2" placeholder="{{ 'core.user.emailagain' | translate }}" formControlName="email2" autocapitalize="none" autocorrect="off" pattern="{{signupForm.controls.email.value}}"></ion-input>
<core-input-errors item-content [control]="signupForm.controls.email2" [errorMessages]="email2Errors"></core-input-errors>
</ion-item>
<ion-item *ngFor="let nameField of settings.namefields" text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.user.' + nameField | translate }}</ion-label>
<ion-input type="text" name="nameField" placeholder="{{ 'core.user.' + nameField | translate }}" formControlName="{{nameField}}" autocorrect="off"></ion-input>
<core-input-errors item-content [control]="signupForm.controls[nameField]" [errorMessages]="namefieldsErrors[nameField]"></core-input-errors>
</ion-item>
<ion-item text-wrap>
<ion-label stacked>{{ 'core.user.city' | translate }}</ion-label>
<ion-input type="text" name="city" placeholder="{{ 'core.user.city' | translate }}" formControlName="city" autocorrect="off"></ion-input>
</ion-item>
<ion-item text-wrap>
<ion-label stacked id="core-login-signup-country">{{ 'core.user.country' | translate }}</ion-label>
<ion-select name="country" formControlName="country" aria-labelledby="core-login-signup-country">
<ion-option value="">{{ 'core.login.selectacountry' | translate }}</ion-option>
<ion-option *ngFor="let key of countriesKeys" [value]="key">{{countries[key]}}</ion-option>
</ion-select>
</ion-item>
<!-- Other categories. @todo: Implement once user profile fields are implemented. -->
<!-- <div *ngFor="let category in categories">
<ion-item-divider text-wrap color="light">{{ category.name }}</div>
<mm-user-profile-field *ngFor="let field in category.fields" field="field" edit="true" signup="true" register-auth="email" model="data" scroll-handle="mmLoginEmailSignupScroll"></mm-user-profile-field>
</div> -->
<!-- ReCAPTCHA -->
<div *ngIf="settings.recaptchachallengehash && settings.recaptchachallengeimage">
<ion-item-divider text-wrap color="light">{{ 'core.login.security_question' | translate }}</ion-item-divider>
<ion-item>
<img [src]="settings.recaptchachallengeimage" alt="{{ 'core.login.recaptchachallengeimage' | translate }}">
</ion-item> </ion-item>
<ion-item text-wrap> <ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.login.password' | translate }}</ion-label> <ion-label stacked core-mark-required="true">{{ 'core.login.enterthewordsabove' | translate }}</ion-label>
<core-show-password item-content [name]="'password'"> <ion-input type="text" name="recaptcharesponse" placeholder="{{ 'core.login.enterthewordsabove' | translate }}" formControlName="recaptcharesponse" autocapitalize="none" autocorrect="off"></ion-input>
<ion-input type="password" name="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password"></ion-input> <core-input-errors item-content [control]="signupForm.controls.recaptcharesponse"></core-input-errors>
</core-show-password>
<p *ngIf="settings.passwordpolicy" item-content class="core-input-footnote">
{{settings.passwordpolicy}}
</p>
<core-input-errors item-content [control]="signupForm.controls.password" [errorMessages]="passwordErrors"></core-input-errors>
</ion-item> </ion-item>
<!-- More details. -->
<ion-item-divider text-wrap color="light">
{{ 'core.login.supplyinfo' | translate }}
</ion-item-divider>
<ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.user.email' | translate }}</ion-label>
<ion-input type="email" name="email" placeholder="{{ 'core.user.email' | translate }}" formControlName="email" autocapitalize="none" autocorrect="off"></ion-input>
<core-input-errors item-content [control]="signupForm.controls.email" [errorMessages]="emailErrors"></core-input-errors>
</ion-item>
<ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.user.emailagain' | translate }}</ion-label>
<ion-input type="email" name="email2" placeholder="{{ 'core.user.emailagain' | translate }}" formControlName="email2" autocapitalize="none" autocorrect="off" pattern="{{signupForm.controls.email.value}}"></ion-input>
<core-input-errors item-content [control]="signupForm.controls.email2" [errorMessages]="email2Errors"></core-input-errors>
</ion-item>
<ion-item *ngFor="let nameField of settings.namefields" text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.user.' + nameField | translate }}</ion-label>
<ion-input type="text" name="nameField" placeholder="{{ 'core.user.' + nameField | translate }}" formControlName="{{nameField}}" autocorrect="off"></ion-input>
<core-input-errors item-content [control]="signupForm.controls[nameField]" [errorMessages]="namefieldsErrors[nameField]"></core-input-errors>
</ion-item>
<ion-item text-wrap>
<ion-label stacked>{{ 'core.user.city' | translate }}</ion-label>
<ion-input type="text" name="city" placeholder="{{ 'core.user.city' | translate }}" formControlName="city" autocorrect="off"></ion-input>
</ion-item>
<ion-item text-wrap>
<ion-label stacked id="core-login-signup-country">{{ 'core.user.country' | translate }}</ion-label>
<ion-select name="country" formControlName="country" aria-labelledby="core-login-signup-country">
<ion-option value="">{{ 'core.login.selectacountry' | translate }}</ion-option>
<ion-option *ngFor="let key of countriesKeys" [value]="key">{{countries[key]}}</ion-option>
</ion-select>
</ion-item>
<!-- Other categories. @todo: Implement once user profile fields are implemented. -->
<!-- <div *ngFor="let category in categories">
<ion-item-divider text-wrap color="light">{{ category.name }}</div>
<mm-user-profile-field *ngFor="let field in category.fields" field="field" edit="true" signup="true" register-auth="email" model="data" scroll-handle="mmLoginEmailSignupScroll"></mm-user-profile-field>
</div> -->
<!-- ReCAPTCHA -->
<div *ngIf="settings.recaptchachallengehash && settings.recaptchachallengeimage">
<ion-item-divider text-wrap color="light">{{ 'core.login.security_question' | translate }}</ion-item-divider>
<ion-item>
<img [src]="settings.recaptchachallengeimage" alt="{{ 'core.login.recaptchachallengeimage' | translate }}">
</ion-item>
<ion-item text-wrap>
<ion-label stacked core-mark-required="true">{{ 'core.login.enterthewordsabove' | translate }}</ion-label>
<ion-input type="text" name="recaptcharesponse" placeholder="{{ 'core.login.enterthewordsabove' | translate }}" formControlName="recaptcharesponse" autocapitalize="none" autocorrect="off"></ion-input>
<core-input-errors item-content [control]="signupForm.controls.recaptcharesponse"></core-input-errors>
</ion-item>
<ion-item padding>
<!-- Use anchor instead of button to prevent marking form as submitted. -->
<a ion-button block (click)="requestCaptcha()">{{ 'core.login.getanothercaptcha' | translate }}</a>
</ion-item>
</div>
<!-- Site policy (if any). -->
<div *ngIf="settings.sitepolicy">
<ion-item-divider text-wrap color="light">{{ 'core.login.policyagreement' | translate }}</ion-item-divider>
<ion-item text-wrap>
<p><a [href]="settings.sitepolicy" core-link capture="false">{{ 'core.login.policyagreementclick' | translate }}</a></p>
</ion-item>
<ion-item text-wrap>
<ion-label core-mark-required="true">{{ 'core.login.policyaccept' | translate }}</ion-label>
<ion-checkbox item-right name="policyagreed" formControlName="policyagreed"></ion-checkbox>
<core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors"></core-input-errors>
</ion-item>
</div>
<!-- Submit button. -->
<ion-item padding> <ion-item padding>
<button ion-button block color="primary">{{ 'core.login.createaccount' | translate }}</button> <!-- Use anchor instead of button to prevent marking form as submitted. -->
<a ion-button block (click)="requestCaptcha()">{{ 'core.login.getanothercaptcha' | translate }}</a>
</ion-item> </ion-item>
</ion-list> </div>
<!-- Site policy (if any). -->
<div *ngIf="settings.sitepolicy">
<ion-item-divider text-wrap color="light">{{ 'core.login.policyagreement' | translate }}</ion-item-divider>
<ion-item text-wrap>
<p><a [href]="settings.sitepolicy" core-link capture="false">{{ 'core.login.policyagreementclick' | translate }}</a></p>
</ion-item>
<ion-item text-wrap>
<ion-label core-mark-required="true">{{ 'core.login.policyaccept' | translate }}</ion-label>
<ion-checkbox item-right name="policyagreed" formControlName="policyagreed"></ion-checkbox>
<core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors"></core-input-errors>
</ion-item>
</div>
<!-- Submit button. -->
<ion-item padding>
<button ion-button block color="primary">{{ 'core.login.createaccount' | translate }}</button>
</ion-item>
</form> </form>
</core-loading> </core-loading>
</ion-content> </ion-content>

View File

@ -4,12 +4,14 @@
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-item text-wrap> <ion-list>
{{ 'core.login.passwordforgotteninstructions2' | translate }} <ion-item text-wrap>
</ion-item> {{ 'core.login.passwordforgotteninstructions2' | translate }}
</ion-item>
</ion-list>
<ion-card> <ion-card>
<form [formGroup]="myForm" (ngSubmit)="resetPassword()"> <form ion-list [formGroup]="myForm" (ngSubmit)="resetPassword()">
<ion-item-divider class="mm-bold" text-wrap color="light"> <ion-item-divider text-wrap color="light">
{{ 'core.login.searchby' | translate }} {{ 'core.login.searchby' | translate }}
</ion-item-divider> </ion-item-divider>
<div radio-group formControlName="field"> <div radio-group formControlName="field">
@ -23,10 +25,10 @@
</ion-item> </ion-item>
</div> </div>
<ion-item> <ion-item>
<ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}" formControlName="value" autocapitalize="none" autocorrect="off"></ion-input> <ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}" formControlName="value" autocapitalize="none" autocorrect="off" core-auto-focus></ion-input>
</ion-item> </ion-item>
<ion-item padding text-wrap> <ion-item>
<button ion-button block color="primary" [disabled]="!myForm.valid">{{ 'core.courses.search' | translate }}</button> <button text-wrap ion-button block [disabled]="!myForm.valid">{{ 'core.courses.search' | translate }}</button>
</ion-item> </ion-item>
</form> </form>
</ion-card> </ion-card>

View File

@ -1,7 +1,7 @@
<ion-content> <ion-content>
<div class="mm-bglogo"> <div class="mm-bglogo">
<div class="mm-logo"> <div class="mm-logo">
<img src="assets/img/logo_white.png"/> <img src="assets/img/splash_logo.png"/>
<ion-spinner></ion-spinner> <ion-spinner></ion-spinner>
</div> </div>
</div> </div>

View File

@ -1,3 +1,35 @@
page-core-login-init { page-core-login-init {
.mm-bglogo {
background-color: $mm-color-init-screen; /* Change this to add a bg image or change color */
background: -webkit-radial-gradient($mm-color-init-screen-alt, $mm-color-init-screen);
background: radial-gradient($mm-color-init-screen-alt, $mm-color-init-screen);
background-repeat: no-repeat;
background-position: center center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
display: table;
.mm-logo {
display: table-cell;
text-align: center;
vertical-align: middle;
}
img {
width: $mm-init-screen-logo-width;
max-width: $mm-init-screen-logo-max-width;
display: block;
margin: 0 auto;
margin-bottom: 30px;
}
.spinner circle {
stroke: $mm-init-screen-spinner-color;
}
}
} }

View File

@ -4,8 +4,8 @@
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content padding class="mm-center-view"> <ion-content padding class="mm-center-view">
<ion-list no-lines class="box"> <div class="box">
<ion-item *ngIf="site" text-wrap class="item-avatar-center" [ngClass]="{'item-avatar-center': site.avatar, 'text-center': !site.avatar}"> <div *ngIf="site" text-wrap text-center [ngClass]="{'item-avatar-center': site.avatar}">
<ion-avatar *ngIf="site.avatar"> <ion-avatar *ngIf="site.avatar">
<!-- Show user avatar. --> <!-- Show user avatar. -->
<img [src]="site.avatar" class="avatar" core-external-content [siteId]="site.id" alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation"> <img [src]="site.avatar" class="avatar" core-external-content [siteId]="site.id" alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation">
@ -13,7 +13,7 @@
<!-- Show site logo or a default image. --> <!-- Show site logo or a default image. -->
<img *ngIf="!site.avatar && logoUrl" [src]="logoUrl" core-external-content [siteId]="site.id" role="presentation"> <img *ngIf="!site.avatar && logoUrl" [src]="logoUrl" core-external-content [siteId]="site.id" role="presentation">
<img *ngIf="!site.avatar && !logoUrl" src="assets/img/logo.png" class="moodle-logo" role="presentation"> <img *ngIf="!site.avatar && !logoUrl" src="assets/img/login_logo.png" class="login-logo" role="presentation">
<!-- If no sitename show big siteurl. --> <!-- If no sitename show big siteurl. -->
<p *ngIf="!siteName" class="item-heading mm-siteurl">{{siteUrl}}</p> <p *ngIf="!siteName" class="item-heading mm-siteurl">{{siteUrl}}</p>
@ -24,31 +24,38 @@
<p *ngIf="!isLoggedOut"> <p *ngIf="!isLoggedOut">
<ion-icon padding name="alert"></ion-icon> {{ 'core.login.reconnectdescription' | translate }} <ion-icon padding name="alert"></ion-icon> {{ 'core.login.reconnectdescription' | translate }}
</p> </p>
</ion-item> </div>
<ion-item padding text-wrap class="mm-username"> <ion-list>
<p class="item-heading">{{ 'core.login.username' | translate }}</p> <ion-item padding text-wrap class="mm-username">
<p>{{username}}</p> <p class="item-heading">{{ 'core.login.username' | translate }}</p>
</ion-item> <p>{{username}}</p>
<form [formGroup]="credForm" (ngSubmit)="login()">
<ion-item>
<core-show-password item-content [name]="'password'">
<ion-input class="mm-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" mm-show-password></ion-input>
</core-show-password>
</ion-item> </ion-item>
<ion-buttons class="button-bar"> <form [formGroup]="credForm" (ngSubmit)="login()">
<a ion-button (click)="cancel()">{{ 'core.login.cancel' | translate }}</a> <ion-item>
<button ion-button color="primary" [disabled]="!credForm.valid">{{ 'core.login.loginbutton' | translate }}</button> <core-show-password item-content [name]="'password'">
</ion-buttons> <ion-input class="mm-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" mm-show-password core-auto-focus></ion-input>
</form> </core-show-password>
</ion-item>
<ion-grid>
<ion-row>
<ion-col>
<a ion-button block color="light" (click)="cancel()">{{ 'core.login.cancel' | translate }}</a>
</ion-col>
<ion-col>
<button ion-button block [disabled]="!credForm.valid">{{ 'core.login.loginbutton' | translate }}</button>
</ion-col>
</ion-row>
</ion-grid>
</form>
</ion-list>
<!-- Identity providers. --> <!-- Identity providers. -->
<div *ngIf="identityProviders && identityProviders.length" padding-top> <ion-list *ngIf="identityProviders && identityProviders.length" padding-top>
<p>{{ 'core.login.potentialidps' | translate }}</p> <ion-list-header text-wrap>{{ 'core.login.potentialidps' | translate }}</ion-list-header>
<ion-item *ngFor="let provider of identityProviders" text-wrap class="mm-oauth-icon" (click)="oauthClicked(provider)" title="{{provider.name}}"> <button ion-item *ngFor="let provider of identityProviders" text-wrap class="mm-oauth-icon" (click)="oauthClicked(provider)" title="{{provider.name}}">
<img [src]="provider.iconurl" alt="{{provider.name}}"> <img [src]="provider.iconurl" alt="{{provider.name}}" item-start>
<span>{{provider.name}}</span> {{provider.name}}
<ion-icon class="icon-accessory" name="arrow-forward" md="ios-arrow-forward" item-end></ion-icon> </button>
</ion-item> </ion-list>
</div> </div>
</ion-list>
</ion-content> </ion-content>

View File

@ -1,47 +1,51 @@
page-core-login-reconnect { page-core-login-reconnect {
.content { .content {
.mm-ioninput-password {
padding-top: 0;
padding-bottom: 0;
}
background: -webkit-radial-gradient(white, $gray-light); background: -webkit-radial-gradient(white, $gray-light);
background: radial-gradient(white, $gray-light); background: radial-gradient(white, $gray-light);
}
img { .mm-ioninput-password {
max-width: 100%; padding-top: 0;
padding-bottom: 0;
}
img {
max-width: 100%;
}
img.login-logo {
width: 90%;
max-width: 300px;
}
.box {
padding: 16px;
margin: 8px;
background: $white;
border: 1px solid $gray;
}
.mm-sitename, .mm-siteurl {
@if $mm-fixed-url { display: none; }
}
.list .mm-username {
&.item-md {
@include padding-horizontal($item-md-padding-left, $content-padding);
}
&.item-ios {
@include padding-horizontal($item-ios-padding-left, $content-padding);
}
&.item-wp {
@include padding-horizontal($item-wp-padding-left, $content-padding);
} }
img.moodle-logo { .item-inner {
width: 90%; padding-left: 8px;
max-width: 300px;
}
.box {
padding: 16px;
background: $white;
border: 1px solid $gray;
}
.mm-sitename, .mm-siteurl {
@if $mm-fixed-url { display: none; }
}
.list .item-input, .list .mm-username {
border: 1px solid $list-border-color;
margin-bottom: 20px;
.label {
margin: 0;
}
}
.list .mm-username {
&.item-md {
@include padding-horizontal($item-md-padding-left * 1.5, $content-padding);
}
&.item-ios {
@include padding-horizontal($item-ios-padding-left * 1.5, $content-padding);
}
} }
} }
.item-input {
margin-bottom: 20px;
}
} }

View File

@ -9,13 +9,12 @@
</ion-buttons> </ion-buttons>
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content id="mm-login-site" class="mm-center-view"> <ion-content class="mm-center-view">
<form class="mm-view-content" [formGroup]="siteForm" (ngSubmit)="connect(siteForm.value.siteUrl)"> <div class="box">
<ion-list class="box"> <div text-center padding>
<div class="text-center" padding> <img src="assets/img/login_logo.png" class="avatar-full login-logo" role="presentation">
<img src="assets/img/logo.png" class="avatar-full moodle-logo" role="presentation"> </div>
</div> <form ion-list no-lines [formGroup]="siteForm" (ngSubmit)="connect(siteForm.value.siteUrl)">
<!-- Form to input the site URL if there are no fixed sites. --> <!-- Form to input the site URL if there are no fixed sites. -->
<div *ngIf="!fixedSites"> <div *ngIf="!fixedSites">
<p padding>{{ 'core.login.newsitedescription' | translate }}</p> <p padding>{{ 'core.login.newsitedescription' | translate }}</p>
@ -42,9 +41,7 @@
</div> </div>
</div> </div>
<div *ngIf="!fixedSites || !displayAsButtons"> <button *ngIf="!fixedSites || !displayAsButtons" ion-button block [disabled]="!siteForm.valid">{{ 'core.login.connect' | translate }}</button>
<button ion-button block color="primary" [disabled]="!siteForm.valid">{{ 'core.login.connect' | translate }}</button> </form>
</div> </div>
</ion-list>
</form>
</ion-content> </ion-content>

View File

@ -2,24 +2,21 @@ page-core-login-site {
.content { .content {
background: -webkit-radial-gradient(white, $gray-light); background: -webkit-radial-gradient(white, $gray-light);
background: radial-gradient(white, $gray-light); background: radial-gradient(white, $gray-light);
}
img { img.login-logo {
max-width: 100%; width: 90%;
} max-width: 300px;
}
img.moodle-logo { .box {
width: 90%; padding: 16px;
max-width: 300px; margin: 8px;
} background: $white;
border: 1px solid $gray;
}
.box { .item-input:last-child {
padding: 16px; margin-bottom: 20px;
background: $white;
border: 1px solid $gray;
}
.item-input {
border: 1px solid $list-border-color;
}
} }
} }

View File

@ -6,25 +6,27 @@
<button *ngIf="sites && sites.length > 0" ion-button icon-only (click)="toggleDelete()" [attr.aria-label]="'core.delete' | translate"> <button *ngIf="sites && sites.length > 0" ion-button icon-only (click)="toggleDelete()" [attr.aria-label]="'core.delete' | translate">
<ion-icon name="create" ios="md-create"></ion-icon> <ion-icon name="create" ios="md-create"></ion-icon>
</button> </button>
<button ion-button icon-only (click)="add()" [attr.aria-label]="'core.add' | translate">
<ion-icon name="add"></ion-icon>
</button>
</ion-buttons> </ion-buttons>
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-list> <ion-list>
<ion-item (click)="login(site.id)" *ngFor="let site of sites; let idx = index"> <ion-item (click)="login(site.id)" *ngFor="let site of sites; let idx = index">
<ion-avatar item-start> <ion-avatar item-start>
<img [src]="site.avatar" core-external-content [siteId]="site.id" alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation"> <img [src]="site.avatar" core-external-content [siteId]="site.id" alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation">
</ion-avatar> </ion-avatar>
<h2>{{site.fullName}}</h2> <h2>{{site.fullName}}</h2>
<p><core-format-text [text]="site.siteName" clean="true" watch="true" [siteId]="site.id"></core-format-text></p> <p><core-format-text [text]="site.siteName" clean="true" watch="true" [siteId]="site.id"></core-format-text></p>
<p>{{site.siteUrl}}</p> <p>{{site.siteUrl}}</p>
<ion-badge item-end *ngIf="!showDelete && site.badge">{{site.badge}}</ion-badge> <ion-badge item-end *ngIf="!showDelete && site.badge">{{site.badge}}</ion-badge>
<button *ngIf="showDelete" item-end ion-button icon-only clear color="danger" (click)="deleteSite($event, idx)" [attr.aria-label]="'core.delete' | translate"> <button *ngIf="showDelete" item-end ion-button icon-only clear color="danger" (click)="deleteSite($event, idx)" [attr.aria-label]="'core.delete' | translate">
<ion-icon name="trash"></ion-icon> <ion-icon name="trash"></ion-icon>
</button> </button>
</ion-item> </ion-item>
</ion-list> </ion-list>
<ion-fab bottom right>
<button ion-fab (click)="add()" [attr.aria-label]="'core.add' | translate">
<ion-icon name="add"></ion-icon>
</button>
</ion-fab>
</ion-content> </ion-content>

View File

@ -1,3 +1,5 @@
page-core-login-sites { page-core-login-sites {
.item-button[icon-only] ion-icon {
font-size: 2.3em;
}
} }

View File

View File

@ -14,7 +14,6 @@ $app-direction: ltr;
@import "ionic.globals"; @import "ionic.globals";
// Color palette // Color palette
$black: #3a3a3a; // Headings, standard text. $black: #3a3a3a; // Headings, standard text.
$gray-darker: #626262; // Text (emphasis-detail), placeholder, background. $gray-darker: #626262; // Text (emphasis-detail), placeholder, background.
@ -47,6 +46,10 @@ $yellow: #fbad1a; // Accent (never text).
$yellow-light: mix($yellow, white, 20%); $yellow-light: mix($yellow, white, 20%);
$yellow-dark: mix($yellow, black, 40%); $yellow-dark: mix($yellow, black, 40%);
$mm-color: $orange;
$mm-color-light: lighten($mm-color, 10%);
$mm-color-dark: darken($mm-color, 10%);
// Shared Variables // Shared Variables
// -------------------------------------------------- // --------------------------------------------------
@ -69,33 +72,80 @@ $content-padding: 10px;
// The "primary" color is the only required color in the map. // The "primary" color is the only required color in the map.
$colors: ( $colors: (
primary: #488aff, primary: $mm-color,
secondary: #32db64, secondary: $turquoise,
danger: #f53d3d, danger: $red,
light: #f4f4f4, light: $gray-lighter,
dark: #222 dark: $black,
warning: $yellow
); );
$text-color: $black;
$link-color: $blue;
$background-color: $gray-light;
$subdued-text-color: $gray-darker;
$list-background-color: $white;
$tabs-background: $gray-darker;
$tabs-border-color: #b2b2b2 !default;
$tabs-tab-color-inactive: #8c8c8c !default;
$tabs-tab-color-active: $mm-color;
$text-input-highlight-color-valid: $green;
$text-input-highlight-color-invalid: $red;
$button-md-box-shadow: 0 2px 2px 1px rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) ;
// Moodle Mobile variables
// --------------------------------------------------
// Variables copied from Ionic 1.
$item-icon-accessory-color: #ccc !default;
$item-icon-accessory-font-size: 16px !default;
// Init screen.
$mm-color-init-screen: #ff5c00;
$mm-color-init-screen-alt: #ff7600;
$mm-init-screen-spinner-color: $white;
$mm-init-screen-logo-width: 60%;
$mm-init-screen-logo-max-width: 300px;
$mm-fixed-url: false;
// Branded apps customization
// --------------------------------------------------
@import "bmma";
// App iOS Variables // App iOS Variables
// -------------------------------------------------- // --------------------------------------------------
// iOS only Sass variables can go here // iOS only Sass variables can go here
$text-input-ios-show-focus-highlight: false;
$mm-text-input-ios-show-highlight: false;
$item-ios-avatar-size: 54px;
// App Material Design Variables // App Material Design Variables
// -------------------------------------------------- // --------------------------------------------------
// Material Design only Sass variables can go here // Material Design only Sass variables can go here
$text-input-md-show-focus-highlight: false;
$mm-text-input-md-show-highlight: true;
$item-md-detail-push-show: true;
$item-md-avatar-size: 54px;
// App Windows Variables // App Windows Variables
// -------------------------------------------------- // --------------------------------------------------
// Windows only Sass variables can go here // Windows only Sass variables can go here
$text-input-wp-show-focus-highlight: true;
$item-wp-detail-push-show: true;
$item-wp-avatar-size: 54px;
// App Theme // App Theme
@ -121,20 +171,3 @@ $colors: (
@import "roboto"; @import "roboto";
@import "noto-sans"; @import "noto-sans";
// Moodle Mobile variables
// --------------------------------------------------
// Variables copied from Ionic 1.
$item-icon-accessory-color: #ccc !default;
$item-icon-accessory-font-size: 16px !default;
// Init screen.
$mm-color-init-screen: #ff5c00;
$mm-color-init-screen-alt: #ff7600;
$mm-init-screen-spinner-color: $white;
$mm-init-screen-logo-width: 60%;
$mm-init-screen-logo-max-width: 300px;
$mm-fixed-url: false;