fix pwa, wizard focus, supervisor 5

This commit is contained in:
javad committed 2026-09-16 11:06:38 +03:30
1 parent ffc1ec0dcf
commit d72381f0ac
11 files changed
+99 -25

No files matched your search

+54 -11
View File
@@ -13,6 +13,8 @@
const canonicalFiles = form.querySelector('input[name="files"]');
const voiceRoot = form.querySelector('[data-ticket-voice-recorder]');
let currentStep = 1;
let guidedField = null;
let guideTimer = null;
const toPersianDigits = value => String(value).replace(/\d/g, digit => '۰۱۲۳۴۵۶۷۸۹'[digit]);
@@ -39,14 +41,47 @@
function clearValidation(number) {
const step = form.querySelector(`[data-wizard-step="${number}"]`);
const error = form.querySelector(`[data-wizard-error="${number}"]`);
step?.querySelectorAll('[aria-invalid="true"]').forEach(field => field.removeAttribute('aria-invalid'));
step?.querySelectorAll('[aria-invalid="true"]').forEach(field => {
field.removeAttribute('aria-invalid');
if (field.getAttribute('aria-describedby') === error?.id) field.removeAttribute('aria-describedby');
});
if (error) {
error.textContent = '';
error.classList.remove('is-visible');
error.removeAttribute('tabindex');
}
}
function clearGuidedField(field = guidedField) {
if (!field) return;
field.closest('.field, .ticket-intent-search, .ticket-location-fields')?.classList.remove('is-guided-field');
if (field === guidedField) guidedField = null;
if (guideTimer) {
window.clearTimeout(guideTimer);
guideTimer = null;
}
}
function guideToField(fields, error) {
const field = fields.find(item => item && !item.disabled && !item.hidden && item.getClientRects().length);
if (!field) {
error?.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
return;
}
clearGuidedField();
guidedField = field;
const container = field.closest('.field, .ticket-intent-search, .ticket-location-fields');
container?.classList.add('is-guided-field');
requestAnimationFrame(() => {
field.focus({ preventScroll: true });
field.scrollIntoView({
behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',
block: 'center',
inline: 'nearest',
});
});
guideTimer = window.setTimeout(() => clearGuidedField(field), 2800);
}
function showValidation(number, problem) {
const step = form.querySelector(`[data-wizard-step="${number}"]`);
const error = form.querySelector(`[data-wizard-error="${number}"]`);
@@ -55,14 +90,13 @@
if (manualRouting) manualRouting.open = true;
}
step?.querySelectorAll('[aria-invalid="true"]').forEach(field => field.removeAttribute('aria-invalid'));
problem.fields.forEach(field => field?.setAttribute('aria-invalid', 'true'));
error.textContent = problem.message;
error.tabIndex = -1;
error.classList.add('is-visible');
requestAnimationFrame(() => {
error.focus({ preventScroll: true });
error.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
problem.fields.forEach(field => {
field?.setAttribute('aria-invalid', 'true');
if (field && error?.id) field.setAttribute('aria-describedby', error.id);
});
error.textContent = problem.message;
error.classList.add('is-visible');
guideToField(problem.fields, error);
}
function problemForStep(number) {
@@ -145,9 +179,15 @@
form.addEventListener('input', event => {
event.target.removeAttribute?.('aria-invalid');
if (event.target.getAttribute?.('aria-describedby')?.startsWith('wizard-error-')) event.target.removeAttribute('aria-describedby');
clearGuidedField(event.target);
if (event.target === body) form.querySelector('[data-body-count]').textContent = toPersianDigits(body.value.length);
});
form.addEventListener('change', event => event.target.removeAttribute?.('aria-invalid'));
form.addEventListener('change', event => {
event.target.removeAttribute?.('aria-invalid');
if (event.target.getAttribute?.('aria-describedby')?.startsWith('wizard-error-')) event.target.removeAttribute('aria-describedby');
clearGuidedField(event.target);
});
form.addEventListener('submit', event => {
const invalidStep = [1, 2, 3, 4].find(number => problemForStep(number));
@@ -314,7 +354,10 @@
showStep(initialStep, { scroll: false });
if (serverErrorStep) {
const messages = [...serverErrorStep.querySelectorAll('.errorlist li')].map(item => item.textContent.trim()).filter(Boolean);
const fields = [...serverErrorStep.querySelectorAll('.errorlist')]
.map(list => list.closest('.field, .ticket-server-errors')?.querySelector('input, select, textarea'))
.filter(Boolean);
const number = Number(serverErrorStep.dataset.wizardStep);
requestAnimationFrame(() => showValidation(number, { message: messages.join(' '), fields: [] }));
requestAnimationFrame(() => showValidation(number, { message: messages.join(' '), fields }));
}
})();