MOBILE-4470 quiz: Fix opacity and clickable elements on reviews

main
Pau Ferrer Ocaña 2024-05-16 12:01:50 +02:00
parent e775a066ae
commit 5c099471f3
15 changed files with 156 additions and 139 deletions

View File

@ -15,7 +15,7 @@
[contextInstanceId]="contextInstanceId" [courseId]="courseId" (afterRender)="textRendered()" />
</ion-label>
</ion-item>
<div class="fake-ion-item ion-text-wrap" [hidden]="!question.loaded">
<div class="fake-ion-item ion-text-wrap" [class.readonly]="question.readOnly" [hidden]="!question.loaded">
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
[text]="question.ddArea" [filter]="false" (afterRender)="ddAreaRendered()" />
</div>

View File

@ -4,12 +4,20 @@
:host {
--ddimageortext-border-drop: var(--medium);
--ddimageortext-draghome-background: var(--core-dd-question-color-2);
--cursor: pointer;
.readonly,
core-format-text ::ng-deep .readonly {
--cursor: auto;
}
}
.addon-qtype-ddimageortext-container {
min-height: 80px; // To display the loading.
}
core-format-text ::ng-deep {
div.ddarea {
@ -39,7 +47,7 @@ core-format-text ::ng-deep {
div.draghome {
border: 1px solid var(--core-dd-question-border);
cursor: pointer;
cursor: var(--cursor);
background-color: var(--ddimageortext-draghome-background);
display: inline-block;
height: auto;
@ -78,23 +86,17 @@ core-format-text ::ng-deep {
.drag {
border: 1px solid var(--core-dd-question-border);
cursor: pointer;
cursor: var(--cursor);
z-index: 2;
}
.drag.placed {
border: 1px solid var(--ddimageortext-border-drop);
}
.dragitems.readonly .drag {
cursor: auto;
}
.dragitems > div {
clear: both;
}
.dragitems {
cursor: pointer;
}
.dragitems.readonly {
cursor: auto;
cursor: var(--cursor);
}
.drag img {
display: block;
@ -106,10 +108,7 @@ core-format-text ::ng-deep {
border: 1px solid var(--ddimageortext-border-drop);
position: absolute;
z-index: 1;
cursor: pointer;
}
.readonly .dropzone {
cursor: auto;
cursor: var(--cursor);
}
div.dragitems div.draghome, div.dragitems div.drag {

View File

@ -1,4 +1,13 @@
// Style ddmarker content a bit. Almost all these styles are copied from Moodle.
:host {
--cursor: pointer;
.readonly,
core-format-text ::ng-deep .readonly {
--cursor: auto;
}
}
.addon-qtype-ddmarker-container {
min-height: 80px; // To display the loading.
}
@ -33,18 +42,11 @@ core-format-text ::ng-deep {
.dragitems, // Previous to 3.9.
.draghomes {
&.readonly {
.dragitem,
.marker {
cursor: auto;
}
}
.dragitem, // Previous to 3.9.
.draghome,
.marker {
vertical-align: top;
cursor: pointer;
cursor: var(--cursor);
position: relative;
margin: 10px;
display: inline-block;
@ -70,7 +72,7 @@ core-format-text ::ng-deep {
.droparea {
.dragitem,
.marker {
cursor: pointer;
cursor: var(--cursor);
position: absolute;
vertical-align: top;
z-index: 2;

View File

@ -1,6 +1,16 @@
@use "theme/globals" as *;
// Style ddwtos content a bit. Almost all these styles are copied from Moodle.
:host {
--cursor: pointer;
.readonly,
core-format-text ::ng-deep .readonly {
--cursor: default;
}
}
.addon-qtype-ddwtos-container {
min-height: 80px; // To display the loading.
position: relative;
@ -27,7 +37,7 @@ core-format-text ::ng-deep, .drags ::ng-deep {
border: 1px solid var(--core-dd-question-border);
margin-bottom: 2px;
border-radius: 5px;
cursor: pointer;
cursor: var(--cursor);
}
.draghome, .drag {
display: inline-block;
@ -48,7 +58,7 @@ core-format-text ::ng-deep, .drags ::ng-deep {
z-index: 2;
border-radius: 5px;
line-height: 25px;
cursor: pointer;
cursor: var(--cursor);
}
.drag.selected,
.drop.selected {
@ -60,17 +70,10 @@ core-format-text ::ng-deep, .drags ::ng-deep {
&.notreadonly .draghome,
&.notreadonly .drop,
&.notreadonly .answercontainer {
cursor: pointer;
cursor: var(--cursor);
border-radius: 5px;
}
&.readonly .drag,
&.readonly .draghome,
&.readonly .drop,
&.readonly .answercontainer {
cursor: default;
}
span.incorrect {
background-color: var(--core-question-incorrect-color-bg);
}

View File

@ -16,25 +16,27 @@
<!-- Checkbox for multiple choice. -->
<ng-container *ngIf="question.multi">
<ion-item class="ion-text-wrap answer" *ngFor="let option of question.options">
<div class="flex-column">
<ion-checkbox [attr.name]="option.name" [(ngModel)]="option.checked" [disabled]="option.disabled"
[color]='(option.isCorrect === 1 ? "success": "") + (option.isCorrect === 0 ? "danger": "")'>
<div>
<div class="flex-grow ion-text-wrap">
<div [class]="option.class">
<core-format-text [component]="component" [componentId]="componentId" [text]="option.text"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId" />
</div>
<div *ngIf="option.feedback" class="specificfeedback">
<core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId" />
</div>
</div>
<ion-icon *ngIf="option.isCorrect === 1" class="core-correct-icon" name="fas-check" color="success"
[attr.aria-label]="'core.question.correct' | translate" />
<ion-icon *ngIf="option.isCorrect === 0" class="core-correct-icon" name="fas-xmark" color="danger"
[attr.aria-label]="'core.question.incorrect' | translate" />
</ion-checkbox>
<div *ngIf="option.feedback" class="specificfeedback">
<core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId" />
</div>
</div>
<!-- ion-checkbox doesn't use an input. Create a hidden input to hold the value. -->
<!-- @TODO Check if this is still needed -->
<input type="hidden" [ngModel]="option.checked" [attr.name]="option.name">
</ion-item>
</ng-container>
@ -42,23 +44,26 @@
<!-- Radio buttons for single choice. -->
<ion-radio-group *ngIf="!question.multi" [(ngModel)]="question.singleChoiceModel" [name]="question.optionsName">
<ion-item class="ion-text-wrap answer" *ngFor="let option of question.options">
<div class="flex-column">
<ion-radio [value]="option.value" [disabled]="option.disabled"
[color]='(option.isCorrect === 1 ? "success": "") + (option.isCorrect === 0 ? "danger": "")'>
<div>
<div class="flex-grow ion-text-wrap">
<div [class]="option.class">
<core-format-text [component]="component" [componentId]="componentId" [text]="option.text"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId" />
</div>
<div *ngIf="option.feedback" class="specificfeedback">
<core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId" />
</div>
</div>
<ion-icon *ngIf="option.isCorrect === 1" class="core-correct-icon" name="fas-check" color="success"
[attr.aria-label]="'core.question.correct' | translate" />
<ion-icon *ngIf="option.isCorrect === 0" class="core-correct-icon" name="fas-xmark" color="danger"
[attr.aria-label]="'core.question.incorrect' | translate" />
</ion-radio>
<div *ngIf="option.feedback" class="specificfeedback">
<core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId" />
</div>
</div>
</ion-item>
<ion-button *ngIf="!question.disabled" class="ion-text-wrap ion-margin-top" expand="block" fill="outline"
[disabled]="!question.singleChoiceModel" (click)="clear()" type="button">
@ -66,6 +71,7 @@
</ion-button>
<!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. -->
<!-- @TODO Check if this is still needed -->
<input type="hidden" [ngModel]="question.singleChoiceModel" [attr.name]="question.optionsName">
</ion-radio-group>
</ion-list>

View File

@ -1,9 +1,13 @@
@use "theme/globals" as *;
:host ::ng-deep {
.specificfeedback {
background-color: var(--core-question-feedback-color-bg);
color: var(--core-question-feedback-color);
display: inline;
padding: 0 .7em;
font-size: var(--text-size);
@include pointer-events-on-buttons();
}
.d-flex {

View File

@ -31,12 +31,10 @@
--contents-display: block;
@include core-transition(all, 200ms);
pointer-events: none;
display: var(--contents-display);
&.core-loading-loaded {
position: static;
pointer-events: auto;
--contents-display: contents;
--internal-loading-inline-min-height: 0px;

View File

@ -106,10 +106,4 @@
.fa.icon.questioncorrectnessicon {
font-size: var(--mdl-typography-icon-fontSize-md);
}
.item.item-interactive.item-interactive-disabled ::ng-deep {
ion-label, ion-select, ion-checkbox {
opacity: 0.7;
}
}
}

View File

@ -16,12 +16,20 @@ input[type=checkbox] {
}
ion-checkbox {
&.md.checkbox-disabled::part(label),
&.ios.checkbox-disabled {
&.checkbox-disabled {
@include pointer-events-on-buttons();
&.md::part(label),
&.ios {
opacity: var(--mdl-input-disabled-opacity);
}
}
}
.ios input[type=checkbox] {
--outer-border-width: 1px;
}
input[type=checkbox][disabled] {
opacity: var(--mdl-input-disabled-opacity);
}

View File

@ -3,3 +3,8 @@ ion-input {
opacity: var(--mdl-input-disabled-opacity);
}
}
input[disabled],
input[readonly] {
opacity: var(--mdl-input-disabled-opacity);
}

View File

@ -47,6 +47,10 @@ ion-item.item {
&.item-has-interactive-control:focus-within {
@include core-focus-outline();
}
&.item-has-interactive-control.item-interactive-disabled {
pointer-events: none;
}
}
// Fake item.
@ -246,6 +250,22 @@ ion-item.item.item-file {
[slot=end] {
@include margin-horizontal(var(--mdl-spacing-4), null);
}
// Disabled items.
&.item-disabled,
&.item-interactive-disabled:not(.item-multiple-inputs) ion-label {
opacity: var(--mdl-item-disabled-opacity) !important;
}
// No highlight on RTE.
&.item-rte {
--full-highlight-height: 0px !important;
}
&.item-multiple-inputs.only-links a {
cursor: pointer;
}
}
.item-dimmed {
@ -255,56 +275,3 @@ ion-item.item.item-file {
--background: var(--light);
}
}
// No highlight on RTE.
ion-item.item-rte {
--full-highlight-height: 0px !important;
}
// Make links clickable when inside radio or checkbox items. Style part.
@media (hover: hover) {
ion-item.item-multiple-inputs:not(.item-rte):hover::part(native) {
color: var(--color-hover);
&::after {
background: var(--background-hover);
opacity: var(--background-hover-opacity);
}
}
ion-item.ion-color.item-multiple-inputs:hover::part(native) {
color: #{current-color(contrast)};
&::after {
background: #{current-color(contrast)};
}
}
}
// It fixes the click on links where ion-ripple-effect is present.
// Make links clickable when inside radio or checkbox items. Pointer and cursor part.
ion-item.item-multiple-inputs:not(.only-links):not(.item-rte),
ion-item.ion-activatable:not(.only-links) {
cursor: pointer;
ion-label {
z-index: 3;
pointer-events: none;
ion-anchor, a,
ion-button, button,
ion-item.ion-focusable,
audio, video, select, input, iframe {
pointer-events: visible;
}
}
ion-checkbox, ion-datetime, ion-radio, ion-select{
position: static;
}
}
ion-item.item-multiple-inputs.only-links {
a {
cursor: pointer;
}
}

View File

@ -57,8 +57,16 @@ input[type=radio],
}
ion-radio {
&.md.radio-disabled::part(label),
&.ios.radio-disabled {
&.radio-disabled {
@include pointer-events-on-buttons();
&.md::part(label),
&.ios {
opacity: var(--mdl-input-disabled-opacity);
}
}
}
input[type=radio][disabled] {
opacity: var(--mdl-input-disabled-opacity);
}

View File

@ -72,3 +72,7 @@ ion-select-popover {
}
}
}
select[disabled] {
opacity: var(--mdl-input-disabled-opacity);
}

View File

@ -171,6 +171,20 @@
}
}
@mixin pointer-events-on-buttons() {
a,
ion-button,
button,
audio,
video,
select,
input,
iframe,
[role="button"] {
pointer-events: visible;
}
}
/**
* Same as item-push-svg-url but admits flip-rtl
*/

View File

@ -63,6 +63,17 @@ html[dir=rtl] {
flex-direction: row;
}
.flex-column {
display: flex;
flex-direction: column;
width: 100%;
}
.flex-grow {
flex-grow: 1;
}
.margin-bottom-sm { margin-bottom: 8px; }
.margin-bottom-md { margin-bottom: 12px; }
@ -572,12 +583,6 @@ audio.core-media-adapt-width {
width: 100%;
}
// Disabled items.
ion-item.item-disabled,
ion-item.item-interactive-disabled:not(.item-multiple-inputs) ion-label {
opacity: var(--mdl-item-disabled-opacity) !important;
}
ion-item-divider.item,
ion-item.item,
td {