[TikiWiki-commits] [Git][tikiwiki/tiki][master] [ENH] Improve tracker status filter: replace icon links with accessible toggle buttons
"luci \(@luciash\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <6a023a22ab2fe_38190e4c58444@gitlab-sidekiq-low-urgency-cpu-bound-v2-f4577dd4d-5l7tl.mail> |
luci pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
817aecbb by Moïse Nturubika at 2026-05-11T20:03:10+00:00
[ENH] Improve tracker status filter: replace icon links with accessible toggle buttons
---
* [UX] Separate Reset button from status group and style as btn-link
* [UX] Finalize tracker filter responsive layout and reset button visibility
* [UX] Refine tracker filter UI and behavior
* [REF] Remove unwanted space
* [UI] Rename status filter 'All' to 'Reset' and fix mandatory selection logic
* [UI] Refactor tracking status filter: Use standard checkboxes and fix behavior
* [UI] Refactor tracker status filter to use Bootstrap checkboxes
* [ENH] Trackers: Document multi-select status toggle logic
* [FIX] Restore multi-select status toggling via Ctrl/Shift
* [UX] Adds an 'All' button to the tracker status filter, active when no specific status is selected
* [ENH] Improve tracker status filter: replace icon links with accessible toggle buttons
See merge request tikiwiki/tiki!7915
- - - - -
1 changed file:
- templates/tracker_filter.tpl
Changes:
=====================================
templates/tracker_filter.tpl
=====================================
@@ -1,39 +1,53 @@
-<form action="#" method="get">
+<form action="" method="get">
<input type="hidden" name="trackerId" value="{$trackerId|escape}">
{if $status}<input type="hidden" name="status" value="{$status}">{/if}
{if $sort_mode}<input type="hidden" name="sort_mode" value="{$sort_mode}">{/if}
{if $offset}<input type="hidden" name="offset" value="{$offset}">{/if}
<div class="search_container mb-3 d-flex flex-row flex-wrap align-items-center gap-2">
{if ($tracker_info.showStatus|default:null eq 'y' or ($tracker_info.showStatusAdminOnly eq 'y' and $tiki_p_admin_trackers eq 'y')) and $showstatus|default:null ne 'n'}
- {foreach key=st item=stdata from=$status_types}
- <div>
- <div class="{$stdata.class}">
- {if $prefs.feature_sefurl === 'y'}{$sep = '?'}{else}{$sep = '&'}{/if}
- <a href="{$trackerId|sefurl:tracker}{$sep}status={$stdata.statuslink}{if $filtervalue and !$filtervalue|is_array}&filtervalue={$filtervalue|escape:"url"}{/if}{if $filtervalue|is_array}{$filtervalueencoded}{/if}{if $filterfield}&filterfield={$filterfield|escape:"url"}{/if}{if $offset}&offset={$offset}{/if}{if $sort_mode}&sort_mode={$sort_mode}{/if}">
- {icon name="{$stdata.iconname}" ititle=":{tr}Toggle{/tr} {$stdata.label}" iclass='tips'}
- </a>
- </div>
+ <div class="d-flex flex-wrap align-items-center gap-2">
+ <span class="text-muted text-nowrap">{tr}Filter by:{/tr}</span>
+ <div class="btn-group flex-wrap" role="group" aria-label="{tr}Filter by status{/tr}">
+ {* Status Toggles: Bootstrap semantic checkboxes *}
+ {foreach key=st item=stdata from=$status_types}
+ <input type="checkbox" class="btn-check js-status-checkbox" id="status-btn-{$stdata.statuslink}"
+ value="{$st}" {if $stdata.class eq 'statuson'}checked{/if}>
+ <label class="btn btn-outline-primary d-flex align-items-center gap-1" for="status-btn-{$stdata.statuslink}">
+ {icon name="{$stdata.iconname}"}
+ <span>{$stdata.label}</span>
+ </label>
+ {/foreach}
</div>
- {/foreach}
+
+ {if isset($smarty.get.status) and $smarty.get.status neq ''}
+ {* "Reset" button: Restores the default status filter (clears manual selections) *}
+ <button id="js-status-all" type="button" class="btn btn-link d-flex align-items-center gap-1 tips text-decoration-none"
+ title=":{tr}Reset to default status filter{/tr}">
+ {icon name="undo"} <span>{tr}Reset{/tr}</span>
+ </button>
+ {/if}
+ </div>
{/if}
<div class="w-25">
{if $show_filters eq 'y'}
{jq}
- fields = [];
- {{$c=0}}
- {{foreach key=fid item=field from=$listfields}
- {if $field.isSearchable eq 'y' and $field.type ne 'f' and $field.type ne 'j' and $field.type ne 'i'}
- fields[{$c}] = '{$fid}';
- {$c=$c+1}
- {/if}
- {/foreach}}
+ fields = [];
+ {{$c=0}}
+ {{foreach key=fid item=field from=$listfields}
+ {if $field.isSearchable eq 'y' and $field.type ne 'f' and $field.type ne 'j' and $field.type ne 'i'}
+ fields[{$c}] = '{$fid}';
+ {$c=$c+1}
+ {/if}
+ {/foreach}}
{/jq}
- <select name="filterfield" class="form-select" placeholder="{tr}Choose a filter{/tr}" onchange="this.form.submit(); {literal}showit = 'show_filterbutton'; if(this.selectedIndex == 0){document.getElementById('filterbutton').style.display = 'none';setSessionVar(showit,'n');}else{ document.getElementById('filterbutton').style.display = 'block'; setSessionVar(showit,'y');}{/literal}">
+ <select name="filterfield" class="form-select" placeholder="{tr}Choose a filter{/tr}"
+ onchange="this.form.submit(); {literal}showit = 'show_filterbutton'; if(this.selectedIndex == 0){document.getElementById('filterbutton').style.display = 'none';setSessionVar(showit,'n');}else{ document.getElementById('filterbutton').style.display = 'block'; setSessionVar(showit,'y');}{/literal}">
<option value="">{tr}Choose a filter{/tr}</option>
{foreach key=fid item=field from=$listfields}
{if $field.isSearchable eq 'y' and $field.type ne 'f' and $field.type ne 'j' and $field.type ne 'i' and ($field.isHidden ne 'y' or $tiki_p_admin_trackers eq 'y')}
- <option value="{$fid}"{if $fid eq $filterfield} selected="selected"{/if}>{tr}{$field.name|truncate:65|escape}{/tr}</option>
+ <option value="{$fid}" {if $fid eq $filterfield} selected="selected" {/if}>
+ {tr}{$field.name|truncate:65|escape}{/tr}</option>
{$filter_button='y'}
{/if}
{/foreach}
@@ -56,94 +70,154 @@
<select name="filtervalue[{$fid}]" class="form-select">
{if $field.type eq 'D'}<option value=""></option>{/if}
{foreach from=$field.possibilities key=dropdown_key item=dropdown_value}
- <option value="{$dropdown_key|escape}" {if $fid == $filterfield}{if $filtervalue eq $dropdown_key}{$gotit='y'}selected="selected"{/if}{/if}>{$dropdown_value|tr_if}</option>
- {/foreach}
- </select>
- {if $field.type eq 'D'}
- <input class="form-control" type="text" name="filtervalue_other"{if $gotit ne 'y'} value="{if $fid == $filterfield}{$filtervalue}{/if}"{/if}>
- {/if}
- </div>
-
- {elseif $field.type eq 'R'}
- <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
- {foreach from=$field.possibilities key=radio_key item=radio_value}
- <input type="radio" class="form-check-input" name="filtervalue[{$fid}]" value="{$radio_key|escape}" {if $fid == $filterfield}{if $filtervalue eq $radio_key}checked="checked"{/if}{/if}>{$radio_value|escape}
- {/foreach}
- </div>
-
- {elseif $field.type eq 'M'}
- <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
- {if empty($field.options_map.inputtype)}
- {foreach from=$field.possibilities key=value item=label}
- <label>
- <input type="checkbox" class="form-check-input" name="filtervalue[{$fid}][]" value="{$value|escape}" {if $fid == $filterfield and is_array($filtervalue) and in_array($value, $filtervalue)}checked="checked"{/if}>
- {$label|tr_if|escape}
- </label>
- {/foreach}
- {elseif $field.options_map.inputtype eq 'm'}
- {if $prefs.elementplus_select neq 'y'}<small>{tr}Hold "Ctrl" in order to select multiple values{/tr}</small><br>{/if}
- <select name="filtervalue[{$fid}][]" multiple="multiple" class="form-select">
- {foreach key=ku from=$field.possibilities key=value item=label}
- <option value="{$value|escape}" {if is_array($filtervalue) and in_array($value, $filtervalue)}selected="selected"{/if}>{$label|escape}</option>
+ <option value="{$dropdown_key|escape}" {if $fid == $filterfield}
+ {if $filtervalue eq $dropdown_key}{$gotit='y'}selected="selected" {/if} {/if}>
+ {$dropdown_value|tr_if}</option>
{/foreach}
</select>
- {/if}
- </div>
+ {if $field.type eq 'D'}
+ <input class="form-control" type="text" name="filtervalue_other" {if $gotit ne 'y'}
+ value="{if $fid == $filterfield}{$filtervalue}{/if}" {/if}>
+ {/if}
+ </div>
- {elseif $field.type eq 'e'}{* category *}
- <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}" class="card-body">
- {if count($field.list) gt $prefs.maxRecords}
- <select name="filtervalue[{$fid}][]" class="form-control" multiple>
- {foreach key=ku item=iu from=$field.list name=eforeach}
- <option value="{$iu.categId}"{if $fid == $filterfield && is_array($filtervalue) && in_array($iu.categId,$filtervalue)} selected="selected"{/if}>
- {$iu.categpath|escape}
- </option>
- {/foreach}
- </select>
- {else}
- <ul class="list-unstyled">
- {foreach key=ku item=iu from=$field.list name=eforeach}
- <li class="form-check justify-content-start">
- <input type="checkbox" class="form-check-input" name="filtervalue[{$fid}][]" value="{$iu.categId}" id="cat{$iu.categId}"
- {if $fid == $filterfield && is_array($filtervalue) && in_array($iu.categId,$filtervalue)} checked="checked"{/if}>
- <label for="cat{$iu.categId}" class="form-check-label">{$iu.categpath|escape}</label>
- </li>
+ {elseif $field.type eq 'R'}
+ <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
+ {foreach from=$field.possibilities key=radio_key item=radio_value}
+ <input type="radio" class="form-check-input" name="filtervalue[{$fid}]" value="{$radio_key|escape}"
+ {if $fid == $filterfield} {if $filtervalue eq $radio_key}checked="checked" {/if}
+ {/if}>{$radio_value|escape}
+ {/foreach}
+ </div>
+
+ {elseif $field.type eq 'M'}
+ <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
+ {if empty($field.options_map.inputtype)}
+ {foreach from=$field.possibilities key=value item=label}
+ <label>
+ <input type="checkbox" class="form-check-input" name="filtervalue[{$fid}][]" value="{$value|escape}"
+ {if $fid == $filterfield and is_array($filtervalue) and in_array($value, $filtervalue)}checked="checked"
+ {/if}>
+ {$label|tr_if|escape}
+ </label>
{/foreach}
- </ul>
- {/if}
- </div>
- {elseif $field.type eq 'u'}{* user with autocomplete *}
- <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
- <input type="text" class="form-control" name="filtervalue[{$fid}]" value="{if $fid == $filterfield}{$filtervalue}{/if}" id="filter-username">
- </div>
- {autocomplete element='#filter-username' type='username'}
- {else}
- <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
- <input type="text" class="form-control" name="filtervalue[{$fid}]" value="{if $fid == $filterfield}{$filtervalue}{/if}">
- </div>
+ {elseif $field.options_map.inputtype eq 'm'}
+ {if $prefs.elementplus_select neq 'y'}<small>{tr}Hold "Ctrl" in order to select multiple
+ values{/tr}</small><br>{/if}
+ <select name="filtervalue[{$fid}][]" multiple="multiple" class="form-select">
+ {foreach key=ku from=$field.possibilities key=value item=label}
+ <option value="{$value|escape}"
+ {if is_array($filtervalue) and in_array($value, $filtervalue)}selected="selected" {/if}>
+ {$label|escape}</option>
+ {/foreach}
+ </select>
+ {/if}
+ </div>
+
+ {elseif $field.type eq 'e'}{* category *}
+ <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}" class="card-body">
+ {if count($field.list) gt $prefs.maxRecords}
+ <select name="filtervalue[{$fid}][]" class="form-control" multiple>
+ {foreach key=ku item=iu from=$field.list name=eforeach}
+ <option value="{$iu.categId}"
+ {if $fid == $filterfield && is_array($filtervalue) && in_array($iu.categId,$filtervalue)}
+ selected="selected" {/if}>
+ {$iu.categpath|escape}
+ </option>
+ {/foreach}
+ </select>
+ {else}
+ <ul class="list-unstyled">
+ {foreach key=ku item=iu from=$field.list name=eforeach}
+ <li class="form-check justify-content-start">
+ <input type="checkbox" class="form-check-input" name="filtervalue[{$fid}][]"
+ value="{$iu.categId}" id="cat{$iu.categId}"
+ {if $fid == $filterfield && is_array($filtervalue) && in_array($iu.categId,$filtervalue)}
+ checked="checked" {/if}>
+ <label for="cat{$iu.categId}" class="form-check-label">{$iu.categpath|escape}</label>
+ </li>
+ {/foreach}
+ </ul>
+ {/if}
+ </div>
+ {elseif $field.type eq 'u'}{* user with autocomplete *}
+ <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
+ <input type="text" class="form-control" name="filtervalue[{$fid}]"
+ value="{if $fid == $filterfield}{$filtervalue}{/if}" id="filter-username">
+ </div>
+ {autocomplete element='#filter-username' type='username'}
+ {else}
+ <div style="display:{if $filterfield eq $fid}block{else}none{/if};" id="fid{$fid}">
+ <input type="text" class="form-control" name="filtervalue[{$fid}]"
+ value="{if $fid == $filterfield}{$filtervalue}{/if}">
+ </div>
+ {/if}
+ {$cnt=$cnt+1}
{/if}
- {$cnt=$cnt+1}
- {/if}
- {/foreach}
+ {/foreach}
+ </div>
+ {if isset($filter_button) && $filter_button eq 'y'}
+ <div id="filterbutton" class="btn-group" role="group"
+ style="display:{if $filterfield}inline-flex{else}none{/if}">
+ <button id="filterbutton_primary" type="submit" class="btn btn-primary" name="filter">
+ {tr}Filter{/tr}
+ </button>
+
+ <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split"
+ data-bs-toggle="dropdown" aria-expanded="false">
+ <span class="visually-hidden">{tr}Toggle dropdown{/tr}</span>
+ </button>
+
+ <ul class="dropdown-menu">
+ <li>
+ <button class="dropdown-item" type="submit" name="filter_exact">
+ {tr}Exact filter{/tr}
+ </button>
+ </li>
+ </ul>
+ </div>
+ {/if}
</div>
- {if isset($filter_button) && $filter_button eq 'y'}
- <div id="filterbutton" class="btn-group" role="group" style="display:{if $filterfield}inline-flex{else}none{/if}">
- <button id="filterbutton_primary" type="submit" class="btn btn-primary" name="filter">
- {tr}Filter{/tr}
- </button>
-
- <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
- <span class="visually-hidden">{tr}Toggle dropdown{/tr}</span>
- </button>
+ {jq}
+ // "Reset" button: clears manual status filter, lets backend use defaultStatus
+ $(document).on('click', '#js-status-all', function() {
+ let $form = $(this).closest('form');
+ // Remove any explicit status parameter so backend defaults apply
+ $form.find('input[name="status"]').remove();
+ $form.submit();
+ });
- <ul class="dropdown-menu">
- <li>
- <button class="dropdown-item" type="submit" name="filter_exact">
- {tr}Exact filter{/tr}
- </button>
- </li>
- </ul>
- </div>
- {/if}
- </div>
-</form>
+ // Handle individual status checkbox changes
+ $(document).on('change', '.js-status-checkbox', function() {
+ let form = this.form;
+ let $form = $(form);
+ let allCheckboxes = $form.find('.js-status-checkbox');
+ let checkboxes = allCheckboxes.filter(':checked');
+
+ // Mandatory Rule: if user unchecks the last checkbox, reset to default filter
+ if (checkboxes.length === 0) {
+ // Reset to default: remove explicit status, let backend decide
+ $form.find('input[name="status"]').remove();
+ $form.submit();
+ return;
+ }
+
+ let values = [];
+ checkboxes.each(function() {
+ values.push(this.value);
+ });
+
+ let outputVal = values.join('');
+
+ // Synchronize status hidden input
+ $form.find('input[name="status"]').remove();
+ $('<input>').attr({
+ type: 'hidden',
+ name: 'status',
+ value: outputVal
+ }).appendTo($form);
+
+ $form.submit();
+ });
+ {/jq}
+ </form>
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/817aecbbc51254291cd490ec91011f0e1bb2bcef
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/817aecbbc51254291cd490ec91011f0e1bb2bcef
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs