feat(trainer): UX-пасс 2 — клавиатура для вариантов + доступность (a11y)
- Клавиатура: в режиме выбора (choice/verify) клавиши 1–9 выбирают вариант; после ответа Enter/пробел → «Дальше» (и для текстовых задач тоже). Не мешает вводу в полях (input/textarea игнорируются). - Доступность: #tr-feedback aria-live="polite" (скринридер озвучивает «Верно/ Неверно»); кнопки-варианты получили aria-label «Вариант N: …»; #tr-choices role="group"; тумблеры сложности и «Текст/На чертеже» — aria-pressed; после ответа фокус переходит на «Дальше». Логика проверки/ID не изменены. Inline-скрипт парсится. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+22
-7
@@ -490,11 +490,11 @@
|
|||||||
placeholder="ответ" aria-label="Ваш ответ"/>
|
placeholder="ответ" aria-label="Ваш ответ"/>
|
||||||
<button class="tr-btn tr-primary" id="tr-check" type="button">Проверить</button>
|
<button class="tr-btn tr-primary" id="tr-check" type="button">Проверить</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="tr-choices" id="tr-choices" style="display:none"></div>
|
<div class="tr-choices" id="tr-choices" role="group" aria-label="Варианты ответа" style="display:none"></div>
|
||||||
<button class="tr-btn tr-primary" id="tr-choice-next" type="button" style="display:none">Дальше</button>
|
<button class="tr-btn tr-primary" id="tr-choice-next" type="button" style="display:none">Дальше</button>
|
||||||
<div class="tr-keypad" id="tr-keypad"></div>
|
<div class="tr-keypad" id="tr-keypad"></div>
|
||||||
<div class="tr-preview" id="tr-preview"></div>
|
<div class="tr-preview" id="tr-preview"></div>
|
||||||
<div class="tr-feedback" id="tr-feedback"></div>
|
<div class="tr-feedback" id="tr-feedback" aria-live="polite"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="tr-stepbox" style="display:none">
|
<div id="tr-stepbox" style="display:none">
|
||||||
@@ -705,8 +705,8 @@
|
|||||||
if (!(cur && cur.figure && cur.figurePrompt)) { box.style.display = 'none'; box.innerHTML = ''; return; }
|
if (!(cur && cur.figure && cur.figurePrompt)) { box.style.display = 'none'; box.innerHTML = ''; return; }
|
||||||
box.style.display = '';
|
box.style.display = '';
|
||||||
box.innerHTML = '<span class="tr-fm-label">Условие</span>' +
|
box.innerHTML = '<span class="tr-fm-label">Условие</span>' +
|
||||||
'<button type="button" class="tr-fm-btn' + (!figureMode ? ' on' : '') + '" data-fm="0">Текст</button>' +
|
'<button type="button" class="tr-fm-btn' + (!figureMode ? ' on' : '') + '" data-fm="0" aria-pressed="' + (!figureMode) + '">Текст</button>' +
|
||||||
'<button type="button" class="tr-fm-btn' + (figureMode ? ' on' : '') + '" data-fm="1">На чертеже</button>';
|
'<button type="button" class="tr-fm-btn' + (figureMode ? ' on' : '') + '" data-fm="1" aria-pressed="' + figureMode + '">На чертеже</button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
var topics = (TG.topics ? TG.topics() : [{ key: null, label: 'Задачи' }]).concat([{ key: 'word', label: 'Текстовые задачи', word: true }]);
|
var topics = (TG.topics ? TG.topics() : [{ key: null, label: 'Задачи' }]).concat([{ key: 'word', label: 'Текстовые задачи', word: true }]);
|
||||||
@@ -906,7 +906,8 @@
|
|||||||
var autoLvl = (diffMode === 'auto') ? (' · ур.' + levelOf(curGen)) : '';
|
var autoLvl = (diffMode === 'auto') ? (' · ур.' + levelOf(curGen)) : '';
|
||||||
el.innerHTML = '<span class="tr-diff-label">Сложность</span>' + opts.map(function (o) {
|
el.innerHTML = '<span class="tr-diff-label">Сложность</span>' + opts.map(function (o) {
|
||||||
var lbl = (o[0] === 'auto') ? ('Авто' + autoLvl) : o[1];
|
var lbl = (o[0] === 'auto') ? ('Авто' + autoLvl) : o[1];
|
||||||
return '<button class="tr-diff-btn' + (String(diffMode) === String(o[0]) ? ' on' : '') + '" type="button" data-d="' + o[0] + '">' + lbl + '</button>';
|
var on = String(diffMode) === String(o[0]);
|
||||||
|
return '<button class="tr-diff-btn' + (on ? ' on' : '') + '" type="button" data-d="' + o[0] + '" aria-pressed="' + on + '">' + lbl + '</button>';
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
// общие эффекты «задача решена» (из обычного ответа и из пошагового режима)
|
// общие эффекты «задача решена» (из обычного ответа и из пошагового режима)
|
||||||
@@ -1036,7 +1037,8 @@
|
|||||||
var box = $('tr-choices'); if (!box) return;
|
var box = $('tr-choices'); if (!box) return;
|
||||||
if (!cur || !cur.choices) { box.innerHTML = ''; return; }
|
if (!cur || !cur.choices) { box.innerHTML = ''; return; }
|
||||||
box.innerHTML = cur.choices.map(function (c, i) {
|
box.innerHTML = cur.choices.map(function (c, i) {
|
||||||
return '<button class="tr-choice-btn" type="button" data-ci="' + i + '">' + esc(c.label) + '</button>';
|
return '<button class="tr-choice-btn" type="button" data-ci="' + i +
|
||||||
|
'" aria-label="Вариант ' + (i + 1) + ': ' + esc(c.label) + '">' + esc(c.label) + '</button>';
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
function submitChoice(idx) {
|
function submitChoice(idx) {
|
||||||
@@ -1061,7 +1063,7 @@
|
|||||||
recordAnswer(false); submitAttempt(false);
|
recordAnswer(false); submitAttempt(false);
|
||||||
revealSolution();
|
revealSolution();
|
||||||
}
|
}
|
||||||
var nx = $('tr-choice-next'); if (nx) nx.style.display = '';
|
var nx = $('tr-choice-next'); if (nx) { nx.style.display = ''; try { nx.focus(); } catch (e) {} }
|
||||||
updateStats();
|
updateStats();
|
||||||
}
|
}
|
||||||
// Текст ответа в фидбеке/раскрытии — по типу задачи.
|
// Текст ответа в фидбеке/раскрытии — по типу задачи.
|
||||||
@@ -1431,6 +1433,19 @@
|
|||||||
});
|
});
|
||||||
$('tr-choice-next').addEventListener('click', advance);
|
$('tr-choice-next').addEventListener('click', advance);
|
||||||
$('tr-skip').addEventListener('click', newProblem);
|
$('tr-skip').addEventListener('click', newProblem);
|
||||||
|
// Клавиатура: в режиме выбора 1–9 выбирают вариант; после ответа Enter/пробел → «Дальше».
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (!cur) return;
|
||||||
|
var tag = (e.target && e.target.tagName || '').toLowerCase();
|
||||||
|
if (tag === 'input' || tag === 'textarea' || tag === 'select') return; // не мешаем вводу/полям
|
||||||
|
var isChoice = (cur.kind === 'choice' || cur.kind === 'verify');
|
||||||
|
if (isChoice && !answered && cur.choices && e.key >= '1' && e.key <= '9') {
|
||||||
|
var idx = +e.key - 1;
|
||||||
|
if (idx < cur.choices.length) { e.preventDefault(); submitChoice(idx); }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (answered && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); advance(); }
|
||||||
|
});
|
||||||
// ИИ-репетитор: после неверного ответа — разбор ошибки; иначе — наводящая подсказка.
|
// ИИ-репетитор: после неверного ответа — разбор ошибки; иначе — наводящая подсказка.
|
||||||
// Опирается на известный ответ + шаги (движок), ИИ только ОБЪЯСНЯЕТ. Недоступен ИИ → решение.
|
// Опирается на известный ответ + шаги (движок), ИИ только ОБЪЯСНЯЕТ. Недоступен ИИ → решение.
|
||||||
function aiExplain() {
|
function aiExplain() {
|
||||||
|
|||||||
Reference in New Issue
Block a user