MOBILE-2812 quiz: Update styles to match web version

main
Pau Ferrer Ocaña 2019-01-28 17:42:50 +01:00
parent d2fc0ed420
commit 9d320b5914
30 changed files with 225 additions and 128 deletions

View File

@ -22,11 +22,11 @@
<p>{{ rule }}</p> <p>{{ rule }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="quiz && quiz.gradeMethodReadable"> <ion-item text-wrap *ngIf="quiz && quiz.gradeMethodReadable">
<p class="item-heading">{{ 'addon.mod_quiz.grademethod' | translate }}</p> <h2>{{ 'addon.mod_quiz.grademethod' | translate }}</h2>
<p>{{ quiz.gradeMethodReadable }}</p> <p>{{ quiz.gradeMethodReadable }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="syncTime"> <ion-item text-wrap *ngIf="syncTime">
<p class="item-heading">{{ 'core.lastsync' | translate }}</p> <h2>{{ 'core.lastsync' | translate }}</h2>
<p>{{ syncTime }}</p> <p>{{ syncTime }}</p>
</ion-item> </ion-item>
</ion-list> </ion-list>

View File

@ -1,4 +1,5 @@
{ {
"answercolon": "Answer:",
"attemptfirst": "First attempt", "attemptfirst": "First attempt",
"attemptlast": "Last attempt", "attemptlast": "Last attempt",
"attemptnumber": "Attempt", "attemptnumber": "Attempt",

View File

@ -9,25 +9,25 @@
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<ion-list *ngIf="attempt"> <ion-list *ngIf="attempt">
<ion-item text-wrap> <ion-item text-wrap no-lines>
<p class="item-heading">{{ 'addon.mod_quiz.attemptnumber' | translate }}</p> <h2>{{ 'addon.mod_quiz.attemptnumber' | translate }}</h2>
<p *ngIf="attempt.preview">{{ 'addon.mod_quiz.preview' | translate }}</p> <p *ngIf="attempt.preview">{{ 'addon.mod_quiz.preview' | translate }}</p>
<p *ngIf="!attempt.preview">{{ attempt.attempt }}</p> <p *ngIf="!attempt.preview">{{ attempt.attempt }}</p>
</ion-item> </ion-item>
<ion-item text-wrap> <ion-item text-wrap no-lines>
<p class="item-heading">{{ 'addon.mod_quiz.attemptstate' | translate }}</p> <h2>{{ 'addon.mod_quiz.attemptstate' | translate }}</h2>
<p *ngFor="let sentence of attempt.readableState">{{ sentence }}</p> <p *ngFor="let sentence of attempt.readableState">{{ sentence }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="quiz.showMarkColumn && attempt.readableMark !== ''"> <ion-item text-wrap no-lines *ngIf="quiz.showMarkColumn && attempt.readableMark !== ''">
<p class="item-heading">{{ 'addon.mod_quiz.marks' | translate }} / {{ quiz.sumGradesFormatted }}</p> <h2>{{ 'addon.mod_quiz.marks' | translate }} / {{ quiz.sumGradesFormatted }}</h2>
<p>{{ attempt.readableMark }}</p> <p>{{ attempt.readableMark }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="quiz.showGradeColumn && attempt.readableGrade !== ''"> <ion-item text-wrap no-lines *ngIf="quiz.showGradeColumn && attempt.readableGrade !== ''">
<p class="item-heading">{{ 'addon.mod_quiz.grade' | translate }} / {{ quiz.gradeFormatted }}</p> <h2>{{ 'addon.mod_quiz.grade' | translate }} / {{ quiz.gradeFormatted }}</h2>
<p>{{ attempt.readableGrade }}</p> <p>{{ attempt.readableGrade }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="quiz.showFeedbackColumn && attempt.feedback"> <ion-item text-wrap no-lines *ngIf="quiz.showFeedbackColumn && attempt.feedback">
<p class="item-heading">{{ 'addon.mod_quiz.feedback' | translate }}</p> <h2>{{ 'addon.mod_quiz.feedback' | translate }}</h2>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="attempt.feedback"></core-format-text></p> <p><core-format-text [component]="component" [componentId]="componentId" [text]="attempt.feedback"></core-format-text></p>
</ion-item> </ion-item>
<ion-item *ngIf="quiz.showReviewColumn && attempt.finished"> <ion-item *ngIf="quiz.showReviewColumn && attempt.finished">

View File

@ -24,6 +24,10 @@
<a ion-item text-wrap *ngFor="let question of pageInstance.navigation" class="{{question.stateClass}}" [ngClass]='{"addon-mod_quiz-selected": !pageInstance.showSummary && pageInstance.attempt.currentpage == question.page}' (click)="loadPage(question.page, question.slot)"> <a ion-item text-wrap *ngFor="let question of pageInstance.navigation" class="{{question.stateClass}}" [ngClass]='{"addon-mod_quiz-selected": !pageInstance.showSummary && pageInstance.attempt.currentpage == question.page}' (click)="loadPage(question.page, question.slot)">
<span *ngIf="question.number">{{ 'core.question.questionno' | translate:{$a: question.number} }}</span> <span *ngIf="question.number">{{ 'core.question.questionno' | translate:{$a: question.number} }}</span>
<span *ngIf="!question.number">{{ 'core.question.information' | translate }}</span> <span *ngIf="!question.number">{{ 'core.question.information' | translate }}</span>
<core-icon item-content *ngIf="!question.number" name="information-circle" color="info"></core-icon>
<core-icon item-content *ngIf="question.stateClass == 'core-question-correct'" name="fa-check" color="success"></core-icon>
<core-icon item-content *ngIf="question.stateClass == 'core-question-partiallycorrect'" name="fa-check-square" color="warning"></core-icon>
<core-icon item-content *ngIf="question.stateClass == 'core-question-incorrect' || question.stateClass == 'core-question-notanswered'" name="fa-remove" color="danger"></core-icon>
</a> </a>
<!-- In player, show button to finish attempt. --> <!-- In player, show button to finish attempt. -->

View File

@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular'; import { IonicPageModule } from 'ionic-angular';
import { AddonModQuizNavigationModalPage } from './navigation-modal'; import { AddonModQuizNavigationModalPage } from './navigation-modal';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreDirectivesModule } from '@directives/directives.module';
@NgModule({ @NgModule({
@ -24,6 +25,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
], ],
imports: [ imports: [
CoreDirectivesModule, CoreDirectivesModule,
CoreComponentsModule,
IonicPageModule.forChild(AddonModQuizNavigationModalPage), IonicPageModule.forChild(AddonModQuizNavigationModalPage),
TranslateModule.forChild() TranslateModule.forChild()
] ]

View File

@ -14,15 +14,49 @@ ion-app.app-root page-addon-mod-quiz-navigation-modal {
} }
} }
.item.core-question-correct .item-inner { .item.core-question-correct {
@include push-arrow-color($core-question-correct-color); &.addon-mod_quiz-selected {
@include border-start(5px, solid, $core-question-correct-color);
}
.item-inner {
@include push-arrow-color($core-question-correct-color);
border-bottom-color: $core-question-correct-color;
}
} }
.item.core-question-incorrect .item-inner { .item.core-question-incorrect,
@include push-arrow-color($core-question-incorrect-color); .item.core-question-notanswered {
&.addon-mod_quiz-selected {
@include border-start(5px, solid, $core-question-incorrect-color);
}
.item-inner {
@include push-arrow-color($core-question-incorrect-color);
border-bottom-color: $core-question-incorrect-color;
}
} }
.item.core-question-answersaved .item-inner { .item.core-question-partiallycorrect {
@include push-arrow-color($text-color); &.addon-mod_quiz-selected {
@include border-start(5px, solid, $core-question-state-partial-text);
}
.item-inner {
@include push-arrow-color($core-question-state-partial-text);
border-bottom-color: $core-question-state-partial-text;
}
}
.item.core-question-requiresgrading,
.item.core-question-answersaved {
&.addon-mod_quiz-selected {
@include border-start(5px, solid, $text-color);
}
.item-inner {
@include push-arrow-color($text-color);
border-bottom-color: $text-color;
}
} }
} }

View File

@ -11,4 +11,9 @@ ion-app.app-root page-addon-mod-quiz-player {
.toolbar-ios .bar-buttons-ios .bar-button { .toolbar-ios .bar-buttons-ios .bar-button {
@include padding-horizontal($content-padding); @include padding-horizontal($content-padding);
} }
.core-question-container {
display: block;
padding-bottom: $content-padding;
}
} }

View File

@ -22,36 +22,36 @@
<h2 *ngIf="!attempt.preview">{{ 'addon.mod_quiz.reviewofattempt' | translate:{$a: attempt.attempt} }}</h2> <h2 *ngIf="!attempt.preview">{{ 'addon.mod_quiz.reviewofattempt' | translate:{$a: attempt.attempt} }}</h2>
</ion-card-header> </ion-card-header>
<ion-list> <ion-list>
<ion-item text-wrap> <ion-item text-wrap no-lines>
<p class="item-heading">{{ 'addon.mod_quiz.startedon' | translate }}</p> <h2>{{ 'addon.mod_quiz.startedon' | translate }}</h2>
<p>{{ attempt.timestart * 1000 | coreFormatDate }}</p> <p>{{ attempt.timestart * 1000 | coreFormatDate }}</p>
</ion-item> </ion-item>
<ion-item text-wrap> <ion-item text-wrap no-lines>
<p class="item-heading">{{ 'addon.mod_quiz.attemptstate' | translate }}</p> <h2>{{ 'addon.mod_quiz.attemptstate' | translate }}</h2>
<p>{{ attempt.readableState }}</p> <p>{{ attempt.readableState }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="showCompleted"> <ion-item text-wrap no-lines *ngIf="showCompleted">
<p class="item-heading">{{ 'addon.mod_quiz.completedon' | translate }}</p> <h2>{{ 'addon.mod_quiz.completedon' | translate }}</h2>
<p>{{ attempt.timefinish * 1000 | coreFormatDate }}</p> <p>{{ attempt.timefinish * 1000 | coreFormatDate }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="attempt.timeTaken"> <ion-item text-wrap no-lines *ngIf="attempt.timeTaken">
<p class="item-heading">{{ 'addon.mod_quiz.timetaken' | translate }}</p> <h2>{{ 'addon.mod_quiz.timetaken' | translate }}</h2>
<p>{{ attempt.timeTaken }}</p> <p>{{ attempt.timeTaken }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="attempt.overTime"> <ion-item text-wrap no-lines *ngIf="attempt.overTime">
<p class="item-heading">{{ 'addon.mod_quiz.overdue' | translate }}</p> <h2>{{ 'addon.mod_quiz.overdue' | translate }}</h2>
<p>{{ attempt.overTime }}</p> <p>{{ attempt.overTime }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="attempt.readableMark"> <ion-item text-wrap no-lines *ngIf="attempt.readableMark">
<p class="item-heading">{{ 'addon.mod_quiz.marks' | translate }}</p> <h2>{{ 'addon.mod_quiz.marks' | translate }}</h2>
<p><core-format-text [text]="attempt.readableMark"></core-format-text></p> <p><core-format-text [text]="attempt.readableMark"></core-format-text></p>
</ion-item> </ion-item>
<ion-item text-wrap *ngIf="attempt.readableGrade"> <ion-item text-wrap no-lines *ngIf="attempt.readableGrade">
<p class="item-heading">{{ 'addon.mod_quiz.grade' | translate }}</p> <h2>{{ 'addon.mod_quiz.grade' | translate }}</h2>
<p>{{ attempt.readableGrade }}</p> <p>{{ attempt.readableGrade }}</p>
</ion-item> </ion-item>
<ion-item text-wrap *ngFor="let data of additionalData"> <ion-item text-wrap no-lines *ngFor="let data of additionalData">
<p class="item-heading">{{ data.title }}</p> <h2>{{ data.title }}</h2>
<core-format-text [component]="component" [componentId]="componentId" [text]="data.content"></core-format-text> <core-format-text [component]="component" [componentId]="componentId" [text]="data.content"></core-format-text>
</ion-item> </ion-item>
</ion-list> </ion-list>
@ -70,7 +70,7 @@
<h2 *ngIf="question.number" class="inline">{{ 'core.question.questionno' | translate:{$a: question.number} }}</h2> <h2 *ngIf="question.number" class="inline">{{ 'core.question.questionno' | translate:{$a: question.number} }}</h2>
<h2 *ngIf="!question.number" class="inline">{{ 'core.question.information' | translate }}</h2> <h2 *ngIf="!question.number" class="inline">{{ 'core.question.information' | translate }}</h2>
<ion-note text-wrap item-end *ngIf="question.status || question.readableMark"> <ion-note text-wrap item-end *ngIf="question.status || question.readableMark">
<p *ngIf="question.status" class="block">{{question.status}}</p> <p *ngIf="question.status">{{question.status}}</p>
<p *ngIf="question.readableMark"><core-format-text [text]="question.readableMark"></core-format-text></p> <p *ngIf="question.readableMark"><core-format-text [text]="question.readableMark"></core-format-text></p>
</ion-note> </ion-note>
</ion-item-divider> </ion-item-divider>

View File

@ -1,11 +1,12 @@
ion-app.app-root page-addon-mod-quiz-review { ion-app.app-root page-addon-mod-quiz-review {
.item-radio-disabled, .item-radio-disabled,
.item-checkbox-disabled, .item-checkbox-disabled,
.text-input[disabled] { .item-select-disabled,
opacity: 1; .item-input-disabled {
opacity: 0.8;
.label, .radio, .checkbox { .label, .radio, .checkbox, .select-disabled, .core-correct-icon {
opacity: 1; opacity: 1;
} }
} }
} }

View File

@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular'; import { IonicModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreDirectivesModule } from '@directives/directives.module';
import { AddonQtypeCalculatedHandler } from './providers/handler'; import { AddonQtypeCalculatedHandler } from './providers/handler';
import { AddonQtypeCalculatedComponent } from './component/calculated'; import { AddonQtypeCalculatedComponent } from './component/calculated';
@ -27,6 +28,7 @@ import { AddonQtypeCalculatedComponent } from './component/calculated';
imports: [ imports: [
IonicModule, IonicModule,
TranslateModule.forChild(), TranslateModule.forChild(),
CoreComponentsModule,
CoreDirectivesModule CoreDirectivesModule
], ],
providers: [ providers: [

View File

@ -8,8 +8,9 @@
<ng-container *ngTemplateOutlet="radioUnits"></ng-container> <ng-container *ngTemplateOutlet="radioUnits"></ng-container>
</ng-container> </ng-container>
<ion-item text-wrap ion-grid> <ion-item text-wrap ion-grid ngClass="core-{{question.input.correctIconColor}}-item">
<ion-grid item-content> <ion-label stacked>{{ 'addon.mod_quiz.answercolon' | translate }}</ion-label>
<ion-grid item-content no-padding>
<ion-row> <ion-row>
<!-- Display unit select before the answer input. --> <!-- Display unit select before the answer input. -->
<ng-container *ngIf="question.select && question.selectFirst"> <ng-container *ngIf="question.select && question.selectFirst">
@ -18,7 +19,7 @@
<!-- Input to enter the answer. --> <!-- Input to enter the answer. -->
<ion-col> <ion-col>
<ion-input padding-left type="text" placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.input.name" [value]="question.input.value" [disabled]="question.input.readOnly" [ngClass]="[question.input.correctClass]" autocorrect="off"> <ion-input padding-left type="text" [placeholder]="question.input.readOnly ? '' : 'core.question.answer' | translate" [attr.name]="question.input.name" [value]="question.input.value" [disabled]="question.input.readOnly" autocorrect="off">
</ion-input> </ion-input>
</ion-col> </ion-col>
@ -27,6 +28,7 @@
<ng-container *ngTemplateOutlet="selectUnits"></ng-container> <ng-container *ngTemplateOutlet="selectUnits"></ng-container>
</ng-container> </ng-container>
</ion-row> </ion-row>
<core-icon *ngIf="question.input.correctIcon" class="core-correct-icon" item-content item-end [name]="question.input.correctIcon" [color]="[question.input.correctIconColor]"></core-icon>
</ion-grid> </ion-grid>
</ion-item> </ion-item>
@ -54,9 +56,9 @@
<div radio-group [(ngModel)]="question.unit" [name]="question.optionsName"> <div radio-group [(ngModel)]="question.unit" [name]="question.optionsName">
<ion-item text-wrap *ngFor="let option of question.options"> <ion-item text-wrap *ngFor="let option of question.options">
<ion-label> <ion-label>
<p>{{option.text}}</p> <core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text>
</ion-label> </ion-label>
<ion-radio [value]="option.value" [disabled]="option.disabled || question.input.readOnly"></ion-radio> <ion-radio [value]="option.value" [disabled]="option.disabled || question.input.readOnly" [color]="question.input.correctIconColor"></ion-radio>
</ion-item> </ion-item>
<!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. --> <!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. -->

View File

@ -0,0 +1,5 @@
ion-app.app-root addon-qtype-calculated {
ion-col .select-disabled {
@include margin(0, 20px, 0, 0);
}
}

View File

@ -29,30 +29,12 @@ addon-qtype-ddimageortext {
zoom: 1; zoom: 1;
} }
.group1 { @for $i from 0 to length($core-dd-question-colors) {
background-color: $white; .group#{$i + 1} {
} background: nth($core-dd-question-colors, $i + 1);
.group2 { }
background-color: $blue-light;
}
.group3 {
background-color: #DCDCDC;
}
.group4 {
background-color: #D8BFD8;
}
.group5 {
background-color: #87CEFA;
}
.group6 {
background-color: #DAA520;
}
.group7 {
background-color: #FFD700;
}
.group8 {
background-color: #F0E68C;
} }
.drag { .drag {
border: 1px solid $gray-darker; border: 1px solid $gray-darker;
cursor: pointer; cursor: pointer;
@ -96,6 +78,6 @@ addon-qtype-ddimageortext {
} }
.drag.beingdragged { .drag.beingdragged {
z-index: 3; z-index: 3;
box-shadow: 3px 3px 4px $gray-darker; box-shadow: $core-dd-question-selected-shadow;
} }
} }

View File

@ -290,18 +290,13 @@ export class AddonQtypeDdMarkerQuestion {
* @param {string} shape Name of the shape of the drop zone (circle, rectangle, polygon). * @param {string} shape Name of the shape of the drop zone (circle, rectangle, polygon).
* @param {string} coords Coordinates of the shape. * @param {string} coords Coordinates of the shape.
* @param {string} colour Colour of the shape. * @param {string} colour Colour of the shape.
* @param {boolean} link Whether the marker should have a link in it.
*/ */
drawDropZone(dropZoneNo: number, markerText: string, shape: string, coords: string, colour: string, link: boolean): void { drawDropZone(dropZoneNo: number, markerText: string, shape: string, coords: string, colour: string): void {
let existingMarkerText: HTMLElement; let existingMarkerText: HTMLElement;
const markerTexts = this.doc.markerTexts(); const markerTexts = this.doc.markerTexts();
// Check if there is already a marker text for this drop zone. // Check if there is already a marker text for this drop zone.
if (link) { existingMarkerText = <HTMLElement> markerTexts.querySelector('span.markertext' + dropZoneNo);
existingMarkerText = <HTMLElement> markerTexts.querySelector('span.markertext' + dropZoneNo + ' a');
} else {
existingMarkerText = <HTMLElement> markerTexts.querySelector('span.markertext' + dropZoneNo);
}
if (existingMarkerText) { if (existingMarkerText) {
// Marker text already exists. Update it or remove it if empty. // Marker text already exists. Update it or remove it if empty.
@ -316,12 +311,7 @@ export class AddonQtypeDdMarkerQuestion {
span = document.createElement('span'); span = document.createElement('span');
span.className = classNames; span.className = classNames;
span.innerHTML = markerText;
if (link) {
span.innerHTML = '<a href="#">' + markerText + '</a>';
} else {
span.innerHTML = markerText;
}
markerTexts.appendChild(span); markerTexts.appendChild(span);
} }
@ -802,7 +792,7 @@ export class AddonQtypeDdMarkerQuestion {
dropZone = this.dropZones[dropZoneNo], dropZone = this.dropZones[dropZoneNo],
dzNo = Number(dropZoneNo); dzNo = Number(dropZoneNo);
this.drawDropZone(dzNo, dropZone.markertext, dropZone.shape, dropZone.coords, colourForDropZone, true); this.drawDropZone(dzNo, dropZone.markertext, dropZone.shape, dropZone.coords, colourForDropZone);
} }
} }
} }

View File

@ -33,7 +33,7 @@ addon-qtype-ddmarker {
.dragitem.beingdragged .markertext { .dragitem.beingdragged .markertext {
z-index: 5; z-index: 5;
box-shadow: 3px 3px 4px $gray-darker; box-shadow: $core-dd-question-selected-shadow;
} }
.dragitems .draghome { .dragitems .draghome {
margin: 10px; margin: 10px;

View File

@ -40,10 +40,11 @@ addon-qtype-ddwtos {
.drag { .drag {
z-index: 2; z-index: 2;
border-radius: 5px; border-radius: 5px;
line-height: 25px;
} }
.drag.selected { .drag.selected {
z-index: 3; z-index: 3;
box-shadow: 3px 3px 4px $gray-darker; box-shadow: $core-dd-question-selected-shadow;
} }
.drop.selected { .drop.selected {
@ -73,29 +74,10 @@ addon-qtype-ddwtos {
background-color: $green-light; background-color: $green-light;
} }
.group1 { @for $i from 0 to length($core-dd-question-colors) {
background-color: $white; .group#{$i + 1} {
} background: nth($core-dd-question-colors, $i + 1);
.group2 { }
background-color: #DCDCDC;
}
.group3 {
background-color: $blue-light;
}
.group4 {
background-color: #D8BFD8;
}
.group5 {
background-color: #87CEFA;
}
.group6 {
background-color: #DAA520;
}
.group7 {
background-color: #FFD700;
}
.group8 {
background-color: #F0E68C;
} }
sub, sup { sub, sup {

View File

@ -1,5 +1,5 @@
// Style gapselect content a bit. All these styles are copied from Moodle. // Style gapselect content a bit. All these styles are copied from Moodle.
addon-qtype-gapselect { ion-app.app-root addon-qtype-gapselect {
p { p {
margin: 0 0 .5em; margin: 0 0 .5em;
} }
@ -15,5 +15,11 @@ addon-qtype-gapselect {
border-radius: 4px; border-radius: 4px;
margin-bottom: 10px; margin-bottom: 10px;
background: $gray-lighter; background: $gray-lighter;
&.core-question-answer-correct,
&.core-question-answer-incorrect {
background-color: $gray-lighter;
color: $text-color;
}
} }
} }

View File

@ -1,19 +1,22 @@
<section ion-list class="addon-qtype-match-container" *ngIf="question.loaded"> <section ion-list class="addon-qtype-match-container" *ngIf="question.loaded">
<ion-item text-wrap> <ion-item text-wrap>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p> <core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text>
</ion-item> </ion-item>
<ion-item text-wrap *ngFor="let row of question.rows"> <ion-item text-wrap *ngFor="let row of question.rows">
<ion-grid item-content> <ion-grid item-content no-margin no-padding>
<ion-row> <ion-row no-margin>
<ion-col> <ion-col>
<p><core-format-text id="addon-qtype-match-question-{{row.id}}" [component]="component" [componentId]="componentId" [text]="row.text"></core-format-text></p> <p><core-format-text id="addon-qtype-match-question-{{row.id}}" [component]="component" [componentId]="componentId" [text]="row.text"></core-format-text></p>
</ion-col> </ion-col>
<ion-col [ngClass]='{"core-question-answer-correct": row.isCorrect === 1, "core-question-answer-incorrect": row.isCorrect === 0}'> <ion-col>
<label class="accesshide" for="{{row.id}}" *ngIf="row.accessibilityLabel">{{ row.accessibilityLabel }}</label> <label class="accesshide" for="{{row.id}}" *ngIf="row.accessibilityLabel">{{ row.accessibilityLabel }}</label>
<ion-select id="{{row.id}}" [name]="row.name" [attr.aria-labelledby]="'addon-qtype-match-question-' + row.id" [(ngModel)]="row.selected" interface="action-sheet" [disabled]="row.disabled"> <ion-select id="{{row.id}}" [name]="row.name" [attr.aria-labelledby]="'addon-qtype-match-question-' + row.id" [(ngModel)]="row.selected" interface="action-sheet" [disabled]="row.disabled" [color]='(row.isCorrect === 1 ? "success": "") + (row.isCorrect === 0 ? "danger": "")'>
<ion-option *ngFor="let option of row.options" [value]="option.value">{{option.label}}</ion-option> <ion-option *ngFor="let option of row.options" [value]="option.value">{{option.label}}</ion-option>
</ion-select> </ion-select>
<core-icon *ngIf="row.isCorrect === 1" class="core-correct-icon" name="fa-check" color="success"></core-icon>
<core-icon *ngIf="row.isCorrect === 0" class="core-correct-icon" name="fa-remove" color="danger"></core-icon>
<!-- ion-select doesn't use a select. Create a hidden input to hold the selected value. --> <!-- ion-select doesn't use a select. Create a hidden input to hold the selected value. -->
<input type="hidden" [ngModel]="row.selected" [attr.name]="row.name"> <input type="hidden" [ngModel]="row.selected" [attr.name]="row.name">
</ion-col> </ion-col>

View File

@ -0,0 +1,10 @@
ion-app.app-root addon-qtype-match {
ion-col .select-disabled {
@include margin(0, 20px, 0, 0);
}
.core-correct-icon {
bottom: 50%;
margin-bottom: -7px;
}
}

View File

@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular'; import { IonicModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreDirectivesModule } from '@directives/directives.module';
import { AddonQtypeMatchHandler } from './providers/handler'; import { AddonQtypeMatchHandler } from './providers/handler';
import { AddonQtypeMatchComponent } from './component/match'; import { AddonQtypeMatchComponent } from './component/match';
@ -27,6 +28,7 @@ import { AddonQtypeMatchComponent } from './component/match';
imports: [ imports: [
IonicModule, IonicModule,
TranslateModule.forChild(), TranslateModule.forChild(),
CoreComponentsModule,
CoreDirectivesModule CoreDirectivesModule
], ],
providers: [ providers: [

View File

@ -1,5 +1,5 @@
<section ion-list class="addon-qtype-multianswer-container" *ngIf="question.text || question.text === ''"> <section ion-list class="addon-qtype-multianswer-container" *ngIf="question.text || question.text === ''">
<ion-item text-wrap> <ion-item text-wrap>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text" (afterRender)="questionRendered()"></core-format-text></p> <core-format-text [component]="component" [componentId]="componentId" [text]="question.text" (afterRender)="questionRendered()"></core-format-text>
</ion-item> </ion-item>
</section> </section>

View File

@ -1,18 +1,20 @@
<section ion-list *ngIf="question.text || question.text === ''"> <section ion-list *ngIf="question.text || question.text === ''">
<!-- Question text first. --> <!-- Question text first. -->
<ion-item text-wrap> <ion-item text-wrap>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p> <core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text>
<p *ngIf="question.prompt"><core-format-text [component]="component" [componentId]="componentId" [text]="question.prompt"></core-format-text></p> <core-format-text [component]="component" [componentId]="componentId" [text]="question.prompt" *ngIf="question.prompt"></core-format-text>
</ion-item> </ion-item>
<!-- Checkbox for multiple choice. --> <!-- Checkbox for multiple choice. -->
<ng-container *ngIf="question.multi"> <ng-container *ngIf="question.multi">
<ion-item text-wrap *ngFor="let option of question.options" [ngClass]="{'core-question-answer-correct': option.isCorrect === 1, 'core-question-answer-incorrect': option.isCorrect === 0}"> <ion-item text-wrap *ngFor="let option of question.options">
<ion-label> <ion-label [color]='(option.isCorrect === 1 ? "success": "") + (option.isCorrect === 0 ? "danger": "")'>
<core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text> <core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text>
<p *ngIf="option.feedback" class="core-question-feedback-container"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></p> <div *ngIf="option.feedback" class="specificfeedback"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></div>
</ion-label> </ion-label>
<ion-checkbox [attr.name]="option.name" [(ngModel)]="option.checked" [disabled]="option.disabled" item-end></ion-checkbox> <ion-checkbox [attr.name]="option.name" [(ngModel)]="option.checked" [disabled]="option.disabled" item-end [color]='(option.isCorrect === 1 ? "success": "") + (option.isCorrect === 0 ? "danger": "")'></ion-checkbox>
<core-icon item-content *ngIf="option.isCorrect === 1" class="core-correct-icon" name="fa-check" color="success"></core-icon>
<core-icon item-content *ngIf="option.isCorrect === 0" class="core-correct-icon" name="fa-remove" color="danger"></core-icon>
<!-- ion-checkbox doesn't use an input. Create a hidden input to hold the value. --> <!-- ion-checkbox doesn't use an input. Create a hidden input to hold the value. -->
<input item-content type="hidden" [ngModel]="option.checked" [attr.name]="option.name"> <input item-content type="hidden" [ngModel]="option.checked" [attr.name]="option.name">
@ -21,12 +23,14 @@
<!-- Radio buttons for single choice. --> <!-- Radio buttons for single choice. -->
<div *ngIf="!question.multi" radio-group [(ngModel)]="question.singleChoiceModel" [name]="question.optionsName"> <div *ngIf="!question.multi" radio-group [(ngModel)]="question.singleChoiceModel" [name]="question.optionsName">
<ion-item text-wrap *ngFor="let option of question.options" [ngClass]='{"core-question-answer-correct": option.isCorrect === 1, "core-question-answer-incorrect": option.isCorrect === 0}'> <ion-item text-wrap *ngFor="let option of question.options">
<ion-label> <ion-label>
<core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text> <core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text>
<p *ngIf="option.feedback" class="core-question-feedback-container"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></p> <div *ngIf="option.feedback" class="specificfeedback"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></div>
</ion-label> </ion-label>
<ion-radio [value]="option.value" [disabled]="option.disabled"></ion-radio> <ion-radio [value]="option.value" [disabled]="option.disabled" [color]='(option.isCorrect === 1 ? "success": "") + (option.isCorrect === 0 ? "danger": "")'></ion-radio>
<core-icon item-content *ngIf="option.isCorrect === 1" class="core-correct-icon" name="fa-check" color="success"></core-icon>
<core-icon item-content *ngIf="option.isCorrect === 0" class="core-correct-icon" name="fa-remove" color="danger"></core-icon>
</ion-item> </ion-item>
<!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. --> <!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. -->

View File

@ -0,0 +1,13 @@
ion-app.app-root addon-qtype-multichoice {
.core-correct-icon {
bottom: 50%;
margin-bottom: -7px;
}
.specificfeedback {
background-color: $core-question-feedback-color-bg;
color: $core-question-feedback-color;
display: inline;
padding: 0 .7em;
}
}

View File

@ -17,6 +17,7 @@ import { IonicModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreDirectivesModule } from '@directives/directives.module';
import { CoreComponentsModule } from '@components/components.module';
import { AddonQtypeMultichoiceHandler } from './providers/handler'; import { AddonQtypeMultichoiceHandler } from './providers/handler';
import { AddonQtypeMultichoiceComponent } from './component/multichoice'; import { AddonQtypeMultichoiceComponent } from './component/multichoice';
@ -27,6 +28,7 @@ import { AddonQtypeMultichoiceComponent } from './component/multichoice';
imports: [ imports: [
IonicModule, IonicModule,
TranslateModule.forChild(), TranslateModule.forChild(),
CoreComponentsModule,
CoreDirectivesModule CoreDirectivesModule
], ],
providers: [ providers: [

View File

@ -2,6 +2,10 @@
<ion-item text-wrap> <ion-item text-wrap>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p> <p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
</ion-item> </ion-item>
<ion-input padding-left type="text" placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.input.name" [value]="question.input.value" autocorrect="off" [disabled]="question.input.readOnly" [ngClass]="[question.input.correctClass]"> <ion-item text-wrap ngClass="core-{{question.input.correctIconColor}}-item">
</ion-input> <ion-label stacked>{{ 'addon.mod_quiz.answercolon' | translate }}</ion-label>
<ion-input padding-left type="text" [placeholder]="question.input.readOnly ? '' : 'core.question.answer' | translate" [attr.name]="question.input.name" [value]="question.input.value" autocorrect="off" [disabled]="question.input.readOnly">
</ion-input>
<core-icon *ngIf="question.input.correctIcon" class="core-correct-icon" item-content item-end [name]="question.input.correctIcon" [color]="[question.input.correctIconColor]"></core-icon>
</ion-item>
</section> </section>

View File

@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular'; import { IonicModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreDirectivesModule } from '@directives/directives.module';
import { AddonQtypeShortAnswerHandler } from './providers/handler'; import { AddonQtypeShortAnswerHandler } from './providers/handler';
import { AddonQtypeShortAnswerComponent } from './component/shortanswer'; import { AddonQtypeShortAnswerComponent } from './component/shortanswer';
@ -27,6 +28,7 @@ import { AddonQtypeShortAnswerComponent } from './component/shortanswer';
imports: [ imports: [
IonicModule, IonicModule,
TranslateModule.forChild(), TranslateModule.forChild(),
CoreComponentsModule,
CoreDirectivesModule CoreDirectivesModule
], ],
providers: [ providers: [

View File

@ -378,6 +378,21 @@ ion-app.app-root {
.select-icon .select-icon-inner { .select-icon .select-icon-inner {
color: $core-select-placeholder-color; color: $core-select-placeholder-color;
} }
&.select-disabled, .select-icon .select-icon-inner {
color: $text-color;
}
@each $color-name, $color-base, $color-contrast in get-colors($colors) {
&.select-md-#{$color-name},
&.select-ios-#{$color-name},
&.select-wp-#{$color-name} {
color: $color-base;
.select-icon .select-icon-inner {
color: $color-base;
}
}
}
} }
ion-select.core-button-select, ion-select.core-button-select,
@ -454,8 +469,16 @@ ion-app.app-root {
// Question. // Question.
// ------------------------- // -------------------------
.core-correct-icon {
padding: 0 ($content-padding / 2);
position: absolute;
@include position(null, 0, $content-padding / 2, null);
margin-top: 0;
margin-bottom: 0;
}
.core-question-answer-correct, .core-question-answer-correct,
.core-question-correct,
.core-question-comment { .core-question-comment {
color: $core-question-correct-color; color: $core-question-correct-color;
background-color: $core-question-correct-color-bg; background-color: $core-question-correct-color-bg;
@ -533,7 +556,8 @@ ion-app.app-root {
.core-question-incorrect { .core-question-incorrect {
background-color: $core-question-state-incorrect-color; background-color: $core-question-state-incorrect-color;
} }
.core-question-answersaved { .core-question-answersaved,
.core-question-requiresgrading {
color: $text-color; color: $text-color;
background-color: $core-question-saved-color-bg; background-color: $core-question-saved-color-bg;
} }

View File

@ -602,6 +602,7 @@
"addon.mod_lti.modulenameplural": "External tools", "addon.mod_lti.modulenameplural": "External tools",
"addon.mod_page.errorwhileloadingthepage": "Error while loading the page content.", "addon.mod_page.errorwhileloadingthepage": "Error while loading the page content.",
"addon.mod_page.modulenameplural": "Pages", "addon.mod_page.modulenameplural": "Pages",
"addon.mod_quiz.answercolon": "Answer:",
"addon.mod_quiz.attemptfirst": "First attempt", "addon.mod_quiz.attemptfirst": "First attempt",
"addon.mod_quiz.attemptlast": "Last attempt", "addon.mod_quiz.attemptlast": "Last attempt",
"addon.mod_quiz.attemptnumber": "Attempt", "addon.mod_quiz.attemptnumber": "Attempt",

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Input, Output, EventEmitter, Injector } from '@angular/core'; import { Input, Output, EventEmitter, Injector, ElementRef } from '@angular/core';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTextUtilsProvider } from '@providers/utils/text';
@ -34,6 +34,7 @@ export class CoreQuestionBaseComponent {
protected questionHelper: CoreQuestionHelperProvider; protected questionHelper: CoreQuestionHelperProvider;
protected domUtils: CoreDomUtilsProvider; protected domUtils: CoreDomUtilsProvider;
protected textUtils: CoreTextUtilsProvider; protected textUtils: CoreTextUtilsProvider;
protected realElement: HTMLElement;
constructor(logger: CoreLoggerProvider, logName: string, protected injector: Injector) { constructor(logger: CoreLoggerProvider, logName: string, protected injector: Injector) {
this.logger = logger.getInstance(logName); this.logger = logger.getInstance(logName);
@ -42,6 +43,7 @@ export class CoreQuestionBaseComponent {
this.questionHelper = injector.get(CoreQuestionHelperProvider); this.questionHelper = injector.get(CoreQuestionHelperProvider);
this.domUtils = injector.get(CoreDomUtilsProvider); this.domUtils = injector.get(CoreDomUtilsProvider);
this.textUtils = injector.get(CoreTextUtilsProvider); this.textUtils = injector.get(CoreTextUtilsProvider);
this.realElement = injector.get(ElementRef).nativeElement;
} }
/** /**
@ -172,6 +174,8 @@ export class CoreQuestionBaseComponent {
return this.questionHelper.showComponentError(this.onAbort); return this.questionHelper.showComponentError(this.onAbort);
} }
this.realElement.classList.add('core-question-container');
const element = this.domUtils.convertToElement(this.question.html); const element = this.domUtils.convertToElement(this.question.html);
// Extract question text. // Extract question text.
@ -291,12 +295,20 @@ export class CoreQuestionBaseComponent {
// Check if question is marked as correct. // Check if question is marked as correct.
if (input.classList.contains('incorrect')) { if (input.classList.contains('incorrect')) {
this.question.input.correctClass = 'core-question-incorrect'; this.question.input.correctClass = 'core-question-incorrect';
this.question.input.correctIcon = 'fa-remove';
this.question.input.correctIconColor = 'danger';
} else if (input.classList.contains('correct')) { } else if (input.classList.contains('correct')) {
this.question.input.correctClass = 'core-question-correct'; this.question.input.correctClass = 'core-question-correct';
this.question.input.correctIcon = 'fa-check';
this.question.input.correctIconColor = 'success';
} else if (input.classList.contains('partiallycorrect')) { } else if (input.classList.contains('partiallycorrect')) {
this.question.input.correctClass = 'core-question-partiallycorrect'; this.question.input.correctClass = 'core-question-partiallycorrect';
this.question.input.correctIcon = 'fa-check-square';
this.question.input.correctIconColor = 'warning';
} else { } else {
this.question.input.correctClass = ''; this.question.input.correctClass = '';
this.question.input.correctIcon = '';
this.question.input.correctIconColor = '';
} }
} }

View File

@ -279,8 +279,12 @@ $core-question-saved-color-bg: $gray-light !default;
$core-question-state-correct-color: $green-light !default; $core-question-state-correct-color: $green-light !default;
$core-question-state-partial-color: $yellow-light !default; $core-question-state-partial-color: $yellow-light !default;
$core-question-state-partial-text: $yellow !default;
$core-question-state-incorrect-color: $red-light !default; $core-question-state-incorrect-color: $red-light !default;
$core-dd-question-selected-shadow: 2px 2px 4px $gray-dark !default;
$core-dd-question-colors: $white, $blue-light, #DCDCDC, #D8BFD8, #87CEFA, #DAA520, #FFD700, #F0E68C !default;
// Mixins // Mixins
// ------------------------- // -------------------------
@mixin core-transition($where: all, $time: 500ms) { @mixin core-transition($where: all, $time: 500ms) {