mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-12 08:38: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.tStart = t;
|
||||||
self._dialogComponent.status = PsychoJS.Status.STARTED;
|
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,
|
// if the experiment is licensed, and running on the license rather than on credit,
|
||||||
// we use the license logo:
|
// we use the license logo:
|
||||||
if (self._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER
|
if (self._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER
|
||||||
@ -123,95 +128,54 @@ export class GUI
|
|||||||
logoUrl = self._psychoJS.config.experiment.license.institutionLogo;
|
logoUrl = self._psychoJS.config.experiment.license.institutionLogo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare the markup for the a11y-dialog:
|
if (self._requiredKeys.length > 0)
|
||||||
let markup = "<div class='dialog-container' id='experiment-dialog' aria-hidden='true' role='alertdialog'>";
|
{
|
||||||
markup += "<div class='dialog-overlay'></div>";
|
requiredFieldsMsgMarkup = "<p class='validateTips'>Fields marked with an asterisk (*) are required.</p>";
|
||||||
// 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>`;
|
|
||||||
|
|
||||||
// logo, if need be:
|
// logo, if need be:
|
||||||
if (typeof logoUrl === "string")
|
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)
|
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:
|
// replace root by the markup code:
|
||||||
const dialogElement = document.getElementById("root");
|
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.
|
* Set the progress message.
|
||||||
*
|
*
|
||||||
|
@ -16,8 +16,10 @@ body {
|
|||||||
content: "initialising the experiment...";
|
content: "initialising the experiment...";
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 0;
|
||||||
transform: translate(-50%, -50%);
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
transform: translate(0, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
#root.is-ready::after {
|
#root.is-ready::after {
|
||||||
@ -27,6 +29,11 @@ body {
|
|||||||
|
|
||||||
/* Project and resource dialogs */
|
/* Project and resource dialogs */
|
||||||
|
|
||||||
|
.dialog-container {
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-container label,
|
.dialog-container label,
|
||||||
.dialog-container input,
|
.dialog-container input,
|
||||||
.dialog-container select {
|
.dialog-container select {
|
||||||
@ -57,11 +64,6 @@ body {
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-container {
|
|
||||||
z-index: 2;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-container[aria-hidden='true'] {
|
.dialog-container[aria-hidden='true'] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -71,12 +73,16 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
width: 500px;
|
width: 500px;
|
||||||
max-width: 88vw;
|
max-width: 88vw;
|
||||||
|
max-height: 90vh;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
@ -88,6 +94,16 @@ body {
|
|||||||
box-shadow: 1px 1px 3px #555555;
|
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 {
|
.dialog-title {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
Loading…
Reference in New Issue
Block a user