Enhance focus handling in modals to improve user experience by scrolling to and focusing on relevant fields
This commit is contained in:
@@ -29,6 +29,31 @@ function _close_shift_modal_targets(){
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if focus %}
|
||||
<script>
|
||||
(function(){
|
||||
try{
|
||||
const focusName = '{{ focus }}';
|
||||
const listEl = document.getElementById('constraint-list');
|
||||
const addBtn = document.getElementById('add-constraint');
|
||||
if(listEl){
|
||||
// Ensure at least one row exists
|
||||
if(listEl.children.length === 0){ if(addBtn) addBtn.click(); }
|
||||
setTimeout(()=>{
|
||||
// Try to find a select matching the constraint name
|
||||
const selects = listEl.querySelectorAll('select');
|
||||
let matched = null;
|
||||
selects.forEach(s=>{ if(s.value === focusName) matched = s; });
|
||||
if(matched){ try{ matched.scrollIntoView({behavior:'smooth', block:'center'}); matched.focus(); matched.dispatchEvent(new Event('change',{bubbles:true})); return; }catch(e){} }
|
||||
// otherwise, focus first row and set its type to the requested focus
|
||||
const firstSel = selects[0];
|
||||
if(firstSel){ try{ firstSel.value = focusName; firstSel.dispatchEvent(new Event('change',{bubbles:true})); firstSel.focus(); firstSel.scrollIntoView({behavior:'smooth', block:'center'}); }catch(e){} }
|
||||
}, 120);
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
<script>
|
||||
(function(){
|
||||
// Constraint type schemas: name -> fields metadata
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<div style="margin-top:0.25rem;">
|
||||
<span class="tags">
|
||||
{% for c in s.constraints %}
|
||||
{# Build a simple tooltip showing key: value pairs for the constraint on hover #}
|
||||
<span class="tag is-light" title="{% for k,v in c.items %}{{ k }}: {{ v }}{% if not forloop.last %}; {% endif %}{% endfor %}">{{ c.name }}</span>
|
||||
{# Build a simple tooltip showing key: value pairs for the constraint on hover; make clickable to open constraint editor #}
|
||||
<a href="javascript:void(0)" class="tag is-light" title="{% for k,v in c.items %}{{ k }}: {{ v }}{% if not forloop.last %}; {% endif %}{% endfor %}" hx-get="{% url 'rota:shift_edit' rota.id forloop.parentloop.counter0 %}?focus={{ c.name }}" hx-target="#modal" hx-swap="innerHTML">{{ c.name }}</a>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if focus %}
|
||||
<script>
|
||||
(function(){
|
||||
try{
|
||||
const f = '{{ focus }}';
|
||||
const listId = f + '-list';
|
||||
const addBtnId = 'add-' + f;
|
||||
const listEl = document.getElementById(listId);
|
||||
if(listEl){
|
||||
// if no rows exist, trigger the Add button to create one
|
||||
if(listEl.children.length === 0){
|
||||
const btn = document.getElementById(addBtnId);
|
||||
if(btn) btn.click();
|
||||
}
|
||||
// after render, scroll into view and focus the first input
|
||||
setTimeout(()=>{
|
||||
const el = document.getElementById(listId);
|
||||
if(el){
|
||||
try{ el.scrollIntoView({behavior:'smooth', block:'center'}); }catch(e){}
|
||||
const firstRow = el.querySelector('.box') || el.firstElementChild;
|
||||
if(firstRow){
|
||||
const input = firstRow.querySelector('input, select, textarea, [data-field]');
|
||||
if(input){ try{ input.focus(); }catch(e){} }
|
||||
}
|
||||
}
|
||||
}, 120);
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
<script>
|
||||
(function(){
|
||||
const textarea = document.getElementById('id_nwds');
|
||||
|
||||
@@ -20,19 +20,19 @@
|
||||
<div style="margin-top:0.25rem;">
|
||||
<span class="tags">
|
||||
{% for it in w.options.hard_day_exclusions %}
|
||||
<span class="tag is-light" title="{{ it|escape }}">HDE</span>
|
||||
<a href="javascript:void(0)" class="tag is-light" title="{{ it|escape }}" hx-get="{% url 'rota:worker_edit' rota.id w.id %}?focus=hde" hx-target="#modal" hx-swap="innerHTML">HDE</a>
|
||||
{% endfor %}
|
||||
{% for it in w.options.hard_day_dependencies %}
|
||||
<span class="tag is-light" title="{{ it|escape }}">HDD</span>
|
||||
<a href="javascript:void(0)" class="tag is-light" title="{{ it|escape }}" hx-get="{% url 'rota:worker_edit' rota.id w.id %}?focus=hdd" hx-target="#modal" hx-swap="innerHTML">HDD</a>
|
||||
{% endfor %}
|
||||
{% for it in w.options.forced_assignments %}
|
||||
<span class="tag is-light" title="{{ it|escape }}">FA</span>
|
||||
<a href="javascript:void(0)" class="tag is-light" title="{{ it|escape }}" hx-get="{% url 'rota:worker_edit' rota.id w.id %}?focus=fa" hx-target="#modal" hx-swap="innerHTML">FA</a>
|
||||
{% endfor %}
|
||||
{% for it in w.options.forced_assignments_by_date %}
|
||||
<span class="tag is-light" title="{{ it|escape }}">FAD</span>
|
||||
<a href="javascript:void(0)" class="tag is-light" title="{{ it|escape }}" hx-get="{% url 'rota:worker_edit' rota.id w.id %}?focus=fad" hx-target="#modal" hx-swap="innerHTML">FAD</a>
|
||||
{% endfor %}
|
||||
{% for it in w.options.allowed_multi_shift_sets %}
|
||||
<span class="tag is-light" title="{{ it|escape }}">AMS</span>
|
||||
<a href="javascript:void(0)" class="tag is-light" title="{{ it|escape }}" hx-get="{% url 'rota:worker_edit' rota.id w.id %}?focus=ams" hx-target="#modal" hx-swap="innerHTML">AMS</a>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -364,7 +364,7 @@ def worker_edit(request, rota_id, worker_id):
|
||||
# invalid: return form modal with errors
|
||||
modal_html = render_to_string(
|
||||
"rota/partials/worker_form_modal.html",
|
||||
{"form": form, "form_action": request.get_full_path(), "rota": rota},
|
||||
{"form": form, "form_action": request.get_full_path(), "rota": rota, "focus": request.GET.get('focus') or request.POST.get('focus')},
|
||||
request=request,
|
||||
)
|
||||
return HttpResponse(modal_html)
|
||||
@@ -373,7 +373,7 @@ def worker_edit(request, rota_id, worker_id):
|
||||
form = WorkerForm(instance=worker)
|
||||
modal_html = render_to_string(
|
||||
"rota/partials/worker_form_modal.html",
|
||||
{"form": form, "form_action": request.path, "rota": rota, "worker": worker, "token_link": request.build_absolute_uri(reverse('rota:request_leave_token', args=[worker.request_token])) if worker and getattr(worker, 'request_token', None) else ''},
|
||||
{"form": form, "form_action": request.path, "rota": rota, "worker": worker, "token_link": request.build_absolute_uri(reverse('rota:request_leave_token', args=[worker.request_token])) if worker and getattr(worker, 'request_token', None) else '', "focus": request.GET.get('focus')},
|
||||
request=request,
|
||||
)
|
||||
return HttpResponse(modal_html)
|
||||
@@ -1339,7 +1339,7 @@ def shift_add(request, rota_id):
|
||||
return response
|
||||
|
||||
# invalid: return modal with form and errors
|
||||
modal_html = render_to_string("rota/partials/shift_form_modal.html", {"form": form, "rota": rota, "form_action": request.path}, request=request)
|
||||
modal_html = render_to_string("rota/partials/shift_form_modal.html", {"form": form, "rota": rota, "form_action": request.path, "focus": request.GET.get('focus') or request.POST.get('focus')}, request=request)
|
||||
return HttpResponse(modal_html)
|
||||
|
||||
else:
|
||||
@@ -1398,7 +1398,7 @@ def shift_edit(request, rota_id, idx):
|
||||
"constraints": json.dumps(shift.get("constraints", [])) if shift.get("constraints") is not None else "",
|
||||
}
|
||||
form = ShiftForm(initial=initial)
|
||||
modal_html = render_to_string("rota/partials/shift_form_modal.html", {"form": form, "rota": rota, "form_action": request.path}, request=request)
|
||||
modal_html = render_to_string("rota/partials/shift_form_modal.html", {"form": form, "rota": rota, "form_action": request.path, "focus": request.GET.get('focus')}, request=request)
|
||||
return HttpResponse(modal_html)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user