Add survey question drag-sort and backend assets

- Frontend editor: drag-to-sort questions with dedicated sort assets/JS
- Backend list/search refinements for tl_survey and tl_survey_content
- Repository helpers for survey/question/editor queries
- Translations and AI handover notes update
This commit is contained in:
Jürgen Mummert
2026-06-14 09:13:07 +02:00
parent 79c4402aee
commit 1b16675ae9
23 changed files with 928 additions and 40 deletions
+116
View File
@@ -0,0 +1,116 @@
(function () {
function isSurveyListPage() {
var params = new URLSearchParams(window.location.search);
return params.get('do') === 'survey_bundle_surveys';
}
function isSurveyQuestionListPage() {
var params = new URLSearchParams(window.location.search);
return params.get('do') === 'survey_bundle_surveys' && params.get('table') === 'tl_survey_content' && !params.get('act');
}
function toggleQuestionListClass() {
document.body.classList.toggle('survey-question-list-page', isSurveyQuestionListPage());
document.body.classList.toggle('survey-question-list-clipboard-active', isSurveyQuestionListPage() && hasQuestionListClipboardState());
}
function hasQuestionListClipboardState() {
if (!isSurveyQuestionListPage()) {
return false;
}
if (document.querySelector('#paste_hint')) {
return true;
}
return Array.from(document.querySelectorAll('button')).some(function (button) {
return (button.textContent || '').trim() === 'Ablage leeren';
});
}
function getSearchSelect() {
return document.querySelector('select[name="tl_search"]');
}
function getSearchForm(select) {
return select ? select.closest('form') : null;
}
function removeIdOption(select) {
if (!select) {
return;
}
var idOption = select.querySelector('option[value="id"]');
if (!idOption) {
return;
}
var wasSelected = idOption.selected;
idOption.remove();
if (wasSelected) {
var fallback = select.querySelector('option[value="title"]') || select.options[0];
if (fallback) {
fallback.selected = true;
select.value = fallback.value;
select.dispatchEvent(new Event('change', { bubbles: true }));
}
}
}
function removeIdChoicesUi() {
document.querySelectorAll('.choices__item[data-value="id"]').forEach(function (node) {
node.remove();
});
document.querySelectorAll('.choices__item--choice[data-value="id"]').forEach(function (node) {
node.remove();
});
}
function normalizeSearchField() {
toggleQuestionListClass();
if (!isSurveyListPage()) {
return;
}
var select = getSearchSelect();
if (!select) {
return;
}
removeIdOption(select);
removeIdChoicesUi();
var form = getSearchForm(select);
if (form && !form.dataset.surveySearchNormalized) {
form.dataset.surveySearchNormalized = '1';
form.addEventListener('submit', function () {
if (select.value === 'id') {
var fallback = select.querySelector('option[value="title"]') || select.options[0];
if (fallback) {
select.value = fallback.value;
}
}
});
}
}
document.addEventListener('DOMContentLoaded', normalizeSearchField);
document.addEventListener('turbo:load', normalizeSearchField);
var observer = new MutationObserver(function () {
normalizeSearchField();
});
observer.observe(document.documentElement, { childList: true, subtree: true });
})();
+110
View File
@@ -0,0 +1,110 @@
(function () {
function ensureSortIconStyles() {
if (document.getElementById('survey-question-sort-icon-theme')) {
return;
}
var style = document.createElement('style');
style.id = 'survey-question-sort-icon-theme';
style.textContent = [
'.survey-sort-icon{width:1.14rem !important;height:1.52rem !important;color:#58a6da !important;background:none !important;transform:translateY(1px) !important;}',
'.survey-sort-icon::before,.survey-sort-icon::after{content:none !important;}',
'.survey-sort-icon svg{display:block !important;width:100% !important;height:100% !important;transform:translateY(4px);}',
'.survey-sort-icon svg path:first-child{transform:translateY(1.4px);transform-box:fill-box;transform-origin:center;}',
'.survey-sort-icon svg path:nth-child(2){transform:translateY(-1.4px);transform-box:fill-box;transform-origin:center;}',
'.survey-sort-icon svg path[stroke]{stroke-width:2px;}'
].join('');
document.head.appendChild(style);
}
function initSurveyQuestionSorting() {
ensureSortIconStyles();
if (typeof Sortable !== 'function') {
return;
}
var sortableList = document.querySelector('[data-survey-question-sortable]');
var orderForm = document.querySelector('[data-survey-question-order-shell]');
var orderInput = document.querySelector('[data-survey-question-order-input]');
if (!sortableList || !orderForm || !orderInput || sortableList._surveyQuestionSortable) {
return;
}
var getOrderedIds = function () {
return Array.from(sortableList.querySelectorAll('[data-survey-question-item]')).map(function (item) {
return item.getAttribute('data-question-id') || '';
}).filter(function (value) {
return value !== '';
});
};
var initialOrder = getOrderedIds().join(',');
var updateOrderField = function () {
orderInput.value = getOrderedIds().join(',');
};
var updateQuestionIndices = function () {
sortableList.querySelectorAll('[data-survey-question-item]').forEach(function (item, index) {
var indexNode = item.querySelector('.survey-accordion-index');
if (indexNode) {
indexNode.textContent = (index + 1) + '.';
}
});
};
var updateSaveVisibility = function () {
var currentOrder = getOrderedIds().join(',');
var isDirty = currentOrder !== initialOrder;
orderForm.hidden = !isDirty;
orderForm.classList.toggle('is-visible', isDirty);
updateOrderField();
};
sortableList.querySelectorAll('[data-survey-question-sort-handle]').forEach(function (handle) {
handle.addEventListener('click', function (event) {
event.preventDefault();
event.stopPropagation();
});
});
updateQuestionIndices();
updateOrderField();
updateSaveVisibility();
sortableList._surveyQuestionSortable = Sortable.create(sortableList, {
animation: 180,
draggable: '[data-survey-question-item]',
handle: '[data-survey-question-sort-handle]',
ghostClass: 'sortable-ghost',
forceFallback: true,
fallbackTolerance: 3,
onStart: function (event) {
if (event.item) {
event.item.classList.add('is-reordering');
}
},
onEnd: function (event) {
if (event.item) {
event.item.classList.remove('is-reordering');
}
updateQuestionIndices();
updateSaveVisibility();
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSurveyQuestionSorting, { once: true });
} else {
initSurveyQuestionSorting();
}
window.addEventListener('load', initSurveyQuestionSorting, { once: true });
})();