mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-11 16:18:10 +00:00
Reorganized construction of dialog box from dictionary; Added styles to allow for dialog ta fit within the screen and provide scroll when there are lots of expInfo parameters.
This commit is contained in:
parent
ec3283bb7b
commit
7fe7f1707a
185
src/core/GUI.js
185
src/core/GUI.js
@ -113,6 +113,11 @@ export class GUI
|
||||
self._dialogComponent.tStart = t;
|
||||
self._dialogComponent.status = PsychoJS.Status.STARTED;
|
||||
|
||||
// Defining variables for bits of markup that included in template on condition.
|
||||
let requiredFieldsMsgMarkup = "";
|
||||
let logoMarkup = "";
|
||||
let dialogOkMarkup = "";
|
||||
|
||||
// if the experiment is licensed, and running on the license rather than on credit,
|
||||
// we use the license logo:
|
||||
if (self._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER
|
||||
@ -123,95 +128,54 @@ export class GUI
|
||||
logoUrl = self._psychoJS.config.experiment.license.institutionLogo;
|
||||
}
|
||||
|
||||
// prepare the markup for the a11y-dialog:
|
||||
let markup = "<div class='dialog-container' id='experiment-dialog' aria-hidden='true' role='alertdialog'>";
|
||||
markup += "<div class='dialog-overlay'></div>";
|
||||
// markup += "<div class='dialog-overlay' data-a11y-dialog-hide></div>";
|
||||
markup += "<div class='dialog-content'>";
|
||||
|
||||
// alert title and close button:
|
||||
markup += `<div id='experiment-dialog-title' class='dialog-title'><p>${title}</p><button id='dialogClose' class='dialog-close' data-a11y-dialog-hide aria-label='Cancel Experiment'>×</button></div>`;
|
||||
if (self._requiredKeys.length > 0)
|
||||
{
|
||||
requiredFieldsMsgMarkup = "<p class='validateTips'>Fields marked with an asterisk (*) are required.</p>";
|
||||
}
|
||||
|
||||
// logo, if need be:
|
||||
if (typeof logoUrl === "string")
|
||||
{
|
||||
markup += '<img id="dialog-logo" class="logo" alt="logo" src="' + logoUrl + '">';
|
||||
logoMarkup = `<img id="dialog-logo" class="logo" alt="logo" src="${logoUrl}">`;
|
||||
}
|
||||
|
||||
// add a combobox or text areas for each entry in the dictionary:
|
||||
Object.keys(dictionary).forEach((key, keyIdx) =>
|
||||
{
|
||||
const value = dictionary[key];
|
||||
const keyId = "form-input-" + keyIdx;
|
||||
|
||||
// only create an input if the key is not in the URL:
|
||||
let inUrl = false;
|
||||
const cleanedDictKey = key.trim().toLowerCase();
|
||||
infoFromUrl.forEach((urlValue, urlKey) =>
|
||||
{
|
||||
const cleanedUrlKey = urlKey.trim().toLowerCase();
|
||||
if (cleanedUrlKey === cleanedDictKey)
|
||||
{
|
||||
inUrl = true;
|
||||
// break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!inUrl)
|
||||
{
|
||||
markup += `<label for='${keyId}'> ${key} </label>`;
|
||||
|
||||
// if the field is required:
|
||||
if (key.slice(-1) === "*")
|
||||
{
|
||||
self._requiredKeys.push(keyId);
|
||||
}
|
||||
|
||||
// if value is an array, we create a select drop-down menu:
|
||||
if (Array.isArray(value))
|
||||
{
|
||||
markup += `<select name='${key}' id='${keyId}' class='text'>`;
|
||||
|
||||
// if the field is required, we add an empty option and select it:
|
||||
if (key.slice(-1) === "*")
|
||||
{
|
||||
markup += "<option disabled selected>...</option>";
|
||||
}
|
||||
|
||||
for (const option of value)
|
||||
{
|
||||
markup += `<option> ${option} </option>`;
|
||||
}
|
||||
|
||||
markup += "</select>";
|
||||
}
|
||||
// otherwise we use a single string input:
|
||||
//if (typeof value === 'string')
|
||||
else
|
||||
{
|
||||
markup += `<input type='text' name='${key}' id='${keyId}' value='${value}' class='text'>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (self._requiredKeys.length > 0)
|
||||
{
|
||||
markup += "<p class='validateTips'>Fields marked with an asterisk (*) are required.</p>";
|
||||
}
|
||||
|
||||
// progress bar:
|
||||
markup += `<hr><div id='progressMsg' class='progress-msg'>${self._progressMessage}</div>`;
|
||||
markup += "<div class='progress-container'><div id='progressBar' class='progress-bar'></div></div>";
|
||||
|
||||
// buttons:
|
||||
markup += "<hr>";
|
||||
markup += "<button id='dialogCancel' class='dialog-button' aria-label='Cancel Experiment'>Cancel</button>";
|
||||
if (self._requireParticipantClick)
|
||||
{
|
||||
markup += "<button id='dialogOK' class='dialog-button disabled' aria-label='Start Experiment'>Ok</button>";
|
||||
dialogOkMarkup = "<button id='dialogOK' class='dialog-button disabled' aria-label='Start Experiment'>Ok</button>";
|
||||
}
|
||||
|
||||
markup += "</div></div>";
|
||||
// prepare the markup for the a11y-dialog:
|
||||
let markup =
|
||||
`<div class="dialog-container" id="experiment-dialog" aria-hidden="true" role="alertdialog">
|
||||
<div class='dialog-overlay'></div>;
|
||||
<!-- "<div class='dialog-overlay' data-a11y-dialog-hide></div>" -->
|
||||
<div class='dialog-content'>
|
||||
<!-- alert title and close button: -->
|
||||
<div id='experiment-dialog-title' class='dialog-title'>
|
||||
<p>${title}</p>
|
||||
<button id='dialogClose' class='dialog-close' data-a11y-dialog-hide aria-label='Cancel Experiment'>×</button>
|
||||
</div>
|
||||
${logoMarkup}
|
||||
<div class="dialog-fields">
|
||||
${this._constructDialogFields(dictionary, infoFromUrl)}
|
||||
${requiredFieldsMsgMarkup}
|
||||
</div>
|
||||
<!-- progress bar: -->
|
||||
<div class="dialog-progress-container">
|
||||
<hr class="dialog-hr-tag">
|
||||
<div id='progressMsg' class='progress-msg'>${self._progressMessage}</div>
|
||||
<div class='progress-container'>
|
||||
<div id='progressBar' class='progress-bar'></div>
|
||||
</div>
|
||||
<hr class="dialog-hr-tag">
|
||||
</div>
|
||||
<!-- buttons: -->
|
||||
<div class="dialog-buttons-container">
|
||||
<button id='dialogCancel' class='dialog-button' aria-label='Cancel Experiment'>Cancel</button>
|
||||
${dialogOkMarkup}
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// replace root by the markup code:
|
||||
const dialogElement = document.getElementById("root");
|
||||
@ -486,6 +450,69 @@ export class GUI
|
||||
}
|
||||
}
|
||||
|
||||
_constructDialogFields (dictionary = {}, infoFromUrl)
|
||||
{
|
||||
let markup = "";
|
||||
|
||||
// add a combobox or text areas for each entry in the dictionary:
|
||||
Object.keys(dictionary).forEach((key, keyIdx) =>
|
||||
{
|
||||
const value = dictionary[key];
|
||||
const keyId = "form-input-" + keyIdx;
|
||||
|
||||
// only create an input if the key is not in the URL:
|
||||
let inUrl = false;
|
||||
const cleanedDictKey = key.trim().toLowerCase();
|
||||
infoFromUrl.forEach((urlValue, urlKey) =>
|
||||
{
|
||||
const cleanedUrlKey = urlKey.trim().toLowerCase();
|
||||
if (cleanedUrlKey === cleanedDictKey)
|
||||
{
|
||||
inUrl = true;
|
||||
// break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!inUrl)
|
||||
{
|
||||
markup += `<label for='${keyId}'> ${key} </label>`;
|
||||
|
||||
// if the field is required:
|
||||
if (key.slice(-1) === "*")
|
||||
{
|
||||
this._requiredKeys.push(keyId);
|
||||
}
|
||||
|
||||
// if value is an array, we create a select drop-down menu:
|
||||
if (Array.isArray(value))
|
||||
{
|
||||
markup += `<select name='${key}' id='${keyId}' class='text'>`;
|
||||
|
||||
// if the field is required, we add an empty option and select it:
|
||||
if (key.slice(-1) === "*")
|
||||
{
|
||||
markup += "<option disabled selected>...</option>";
|
||||
}
|
||||
|
||||
for (const option of value)
|
||||
{
|
||||
markup += `<option> ${option} </option>`;
|
||||
}
|
||||
|
||||
markup += "</select>";
|
||||
}
|
||||
// otherwise we use a single string input:
|
||||
//if (typeof value === 'string')
|
||||
else
|
||||
{
|
||||
markup += `<input type='text' name='${key}' id='${keyId}' value='${value}' class='text'>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return markup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the progress message.
|
||||
*
|
||||
|
@ -16,8 +16,10 @@ body {
|
||||
content: "initialising the experiment...";
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
#root.is-ready::after {
|
||||
@ -27,6 +29,11 @@ body {
|
||||
|
||||
/* Project and resource dialogs */
|
||||
|
||||
.dialog-container {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dialog-container label,
|
||||
.dialog-container input,
|
||||
.dialog-container select {
|
||||
@ -57,11 +64,6 @@ body {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dialog-container[aria-hidden='true'] {
|
||||
display: none;
|
||||
}
|
||||
@ -71,12 +73,16 @@ body {
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
margin: auto;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
|
||||
width: 500px;
|
||||
max-width: 88vw;
|
||||
max-height: 90vh;
|
||||
padding: 0.5em;
|
||||
border-radius: 2px;
|
||||
|
||||
@ -88,6 +94,16 @@ body {
|
||||
box-shadow: 1px 1px 3px #555555;
|
||||
}
|
||||
|
||||
.dialog-content .dialog-fields {
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.dialog-container .dialog-hr-tag {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
padding: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
|
Loading…
Reference in New Issue
Block a user