1
0
mirror of https://github.com/psychopy/psychojs.git synced 2025-05-10 10:40:54 +00:00
This commit is contained in:
Alain Pitiot 2023-02-02 12:49:53 +01:00
parent f87431e6af
commit 61a2f4286a
2 changed files with 137 additions and 0 deletions

View File

@ -0,0 +1,48 @@
/**
* @desc: Extensions for default dropdown component of SurveyJS to make it more nice to interact with on mobile devices.
* @type: SurveyJS component modification.
*/
function handleValueChange (survey, options, e)
{
options.question.value = e.currentTarget.value;
}
function handleValueChangeForDOM (survey, options)
{
options.htmlElement.querySelector("select").value = options.question.value;
}
function handleDropdownRendering (survey, options)
{
// Default SurveyJS drop down is actually an <input> with customly built options list
// It works well on desktop, but not that convenient on mobile.
// Adding native <select> here that's hidden by default but visible on mobile.
const surveyCSS = options.question.css;
const choices = options.question.getChoices();
let optionsHTML = `<option value=""></option>`;
let i;
for (i = 0; i < choices.length; i++)
{
optionsHTML += `<option value="${choices[i].value}">${choices[i].text}</option>`;
}
const selectHTML = `<select data-name="${options.question.name}" class="${surveyCSS.dropdown.control} dropdown-mobile">${optionsHTML}</select>`;
options.htmlElement.querySelector('.sd-selectbase').insertAdjacentHTML("beforebegin", selectHTML);
const selectDOM = options.htmlElement.querySelector("select");
selectDOM.addEventListener("change", handleValueChange.bind(this, survey, options));
options.question.valueChangedCallback = handleValueChangeForDOM.bind(this, survey, options);
}
export default {
registerModelCallbacks (surveyModel)
{
surveyModel.onAfterRenderQuestion.add((survey, options) => {
if (options.question.getType() === "dropdown")
{
handleDropdownRendering(survey, options);
}
});
}
};

View File

@ -0,0 +1,89 @@
// Wrapping everything in Class and defining as static methods to prevent esbuild from renaming when bundling.
// NOTE! Survey stim uses property .name of these methods on registering stage.
// Methods are available inside SurveyJS expressions using their actual names.
class ExpressionFunctions {
static rnd ()
{
return Math.random();
}
static arrayContains (params)
{
if (params[0] instanceof Array)
{
let searchValue = params[1];
let searchResult = params[0].indexOf(searchValue) !== -1;
// If no results at first, trying conversion combinations, since array of values sometimes might
// contain both string and number data types.
if (searchResult === false)
{
if (typeof searchValue === "string")
{
searchValue = parseFloat(searchValue);
searchResult = params[0].indexOf(searchValue) !== -1;
}
else if (typeof searchValue === "number")
{
searchValue = searchValue.toString();
searchResult = params[0].indexOf(searchValue) !== -1;
}
}
return searchResult
}
return false;
}
static stringContains (params)
{
if (typeof params[0] === "string")
{
return params[0].indexOf(params[1]) !== -1;
}
return false;
}
static isEmpty (params)
{
let questionIsEmpty = false;
if (params[0] instanceof Array || typeof params[0] === "string")
{
questionIsEmpty = params[0].length === 0;
}
else
{
questionIsEmpty = params[0] === undefined || params[0] === null;
}
return questionIsEmpty;
}
static isNotEmpty (params)
{
return !ExpressionFunctions.isEmpty(params);
}
}
export default [
{
func: ExpressionFunctions.rnd,
isAsync: false
},
{
func: ExpressionFunctions.arrayContains,
isAsync: false
},
{
func: ExpressionFunctions.stringContains,
isAsync: false
},
{
func: ExpressionFunctions.isEmpty,
isAsync: false
},
{
func: ExpressionFunctions.isNotEmpty,
isAsync: false
}
];