Skip to content

Commit

Permalink
rejig how warnings are positioned
Browse files Browse the repository at this point in the history
Warning boxes are now absolutely positioned relative to the containing
question, and should end up either below or to the right of the input
box, depending on whether it's a top-level part or a gap.

This still isn't ideal: the warning obscures nearby content, but having
it static far away is no good either.

Matrix input elements don't consume the right amount of height, so the
warning box hovers over the middle of the matrix, which makes it
impossible to click onto the input. I've added a bit more margin-top to
warnings for matrix parts for now.
  • Loading branch information
christianp committed Feb 15, 2024
1 parent f4a5e68 commit dc1ee85
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 10 deletions.
9 changes: 9 additions & 0 deletions runtime/scripts/part.js
Expand Up @@ -367,6 +367,15 @@ Part.prototype = /** @lends Numbas.parts.Part.prototype */ {
}
});
},

/** All children of this part: all gaps and steps, but not alternatives.
*
* @returns {Array.<Numbas.jme.parts.Part>}
*/
allChildren: function() {
return this.gaps.concat(this.steps);
},

/** Initialise this part's display object.
* Only called if the question this part belongs to has a display.
*/
Expand Down
15 changes: 9 additions & 6 deletions themes/default/files/resources/exam.css
Expand Up @@ -490,6 +490,7 @@ input.jme {
overflow: visible auto;
padding-right: 1em;
padding-bottom: 2em;
position: relative;
}

.feedback-icon {
Expand Down Expand Up @@ -1066,12 +1067,12 @@ select.multiplechoice {
}

.warnings {
position: absolute;
z-index: 1;
position: absolute;
z-index: 1;
width: 20em;
margin-top: 1em;
width: 20em;
display: inline-block;
opacity: 0;
display: inline-block;
transition: opacity 0.2s;
border-color: hsl(30, 20%, 50%);
background-color: hsl(30, 20%, 95%);
Expand All @@ -1083,14 +1084,16 @@ select.multiplechoice {
transition: opacity 0s;
}
}

.gap .warnings {
margin-top: -0.5em;
}
.warnings.stick-right {
right: 0.5em;
}
.warnings.shown {
opacity: 1;
}
.warnings.alert-warning {
}
.part .warning {
margin: 0.1em;
display: block;
Expand Down
51 changes: 50 additions & 1 deletion themes/default/files/scripts/part-display.js
Expand Up @@ -178,6 +178,51 @@ Numbas.queueScript('part-display',['display-util', 'display-base','util','jme'],
return _warningsShown(v);
}
},this);

function position_warnings() {
if(!pd.html || !pd.warningsShown.peek()) {
return;
}
var warnings_box = pd.html.querySelector('.warnings');
var answer = pd.html.querySelector('.student-answer');
var offsetTop = 0;
var offsetLeft = 0;
var el = answer;
while(el.offsetParent && !el.classList.contains('question')) {
offsetTop += el.offsetTop;
offsetLeft += el.offsetLeft;
el = el.offsetParent;
}
var answer_height = answer.getBoundingClientRect().height;
var answer_width = answer.getBoundingClientRect().width;

var wtop = offsetTop + (p.isGap ? 0 : answer_height);
var wleft = (offsetLeft + (p.isGap ? answer_width : 0));

warnings_box.style.top = wtop + 'px';
warnings_box.style.left = wleft + 'px';

var box = warnings_box.getBoundingClientRect();
var docWidth = document.documentElement.clientWidth;
var margin = 10;
var dr = box.right - docWidth + document.documentElement.clientLeft + margin;
if(dr > 0) {
wleft -= dr;
if(p.isGap) {
wtop += answer_height;
}
warnings_box.style.left = wleft + 'px';
warnings_box.style.top = wtop + 'px';
}
warnings_box.style.width = '';
const ideal_width = parseFloat(window.getComputedStyle(warnings_box).width.replace('px',''));
var maxWidth = docWidth - 3*margin;
if(ideal_width > maxWidth) {
warnings_box.style.width = maxWidth + 'px';
}
}


/** Show the warnings.
*
* @member {Function} showWarnings
Expand All @@ -186,7 +231,11 @@ Numbas.queueScript('part-display',['display-util', 'display-base','util','jme'],
*/
this.showWarnings = function() {
this.warningsShown(true);
position_warnings();
}

setInterval(position_warnings,200);

/** Hide the warnings.
*
* @member {Function} hideWarnings
Expand Down Expand Up @@ -481,7 +530,7 @@ Numbas.queueScript('part-display',['display-util', 'display-base','util','jme'],
pd.resolve_html_promise = resolve;
});

/** Called when Kncokout has finished binding the HTML for this part to the DOM.
/** Called when Knockout has finished binding the HTML for this part to the DOM.
*
* @memberof Numbas.display.PartDisplay
*/
Expand Down
12 changes: 10 additions & 2 deletions themes/default/files/scripts/question-display.js
Expand Up @@ -416,9 +416,17 @@ Numbas.queueScript('question-display',['display-util', 'display-base','jme-varia
p.getScope(),
qd.contextDescription+' '+(p.display.name() || p.name)
);
function add_html_to_part(p, html) {
if(p.display) {
p.display.html = html;
p.display.resolve_html_promise(html);
}
p.allChildren().forEach(function(cp) {
add_html_to_part(cp, html.querySelector('.part[data-part-path="'+cp.path+'"]'));
});
}
promise.then(function(html) {
p.display.html = html;
p.display.resolve_html_promise(html);
add_html_to_part(p, html);
});
});
},
Expand Down
2 changes: 1 addition & 1 deletion themes/default/templates/xslt/part.xslt
Expand Up @@ -40,7 +40,7 @@
<span class="student-answer">
<xsl:attribute name="data-bind">css: {answered: scoreFeedback.answered, 'has-warnings': hasWarnings}, attr: {"feedback-state": scoreFeedback.state}</xsl:attribute>
<xsl:apply-templates select="." mode="typespecific"/>
<span class="warnings alert alert-warning" aria-live="assertive" role="alert" data-bind="visible: warningsShown, css: {{shown: warningsShown}}, keepInViewport: warningsShown, attr: {{id: part.full_path+'-warnings'}}">
<span class="warnings alert alert-warning" aria-live="assertive" role="alert" data-bind="visible: warningsShown, css: {{shown: warningsShown}}, attr: {{id: part.full_path+'-warnings'}}">
<xsl:comment>ko foreach: warnings</xsl:comment>
<span class="warning" data-bind="latex: message"></span>
<xsl:comment>/ko</xsl:comment>
Expand Down

0 comments on commit dc1ee85

Please sign in to comment.