diff --git a/action.php b/action.php index 2a8eca9..c00df98 100644 --- a/action.php +++ b/action.php @@ -42,6 +42,8 @@ class action_plugin_datatables extends DokuWiki_Action_Plugin 'dom' => 'lBfrtip' ], 'enableForAllTables' => $this->getConf('enableForAllTables'), + 'enableColumnFilters' => $this->getConf('enableColumnFilters'), + 'columnFiltersPosition' => $this->getConf('columnFiltersPosition'), ]; // find a matching language file @@ -120,6 +122,13 @@ class action_plugin_datatables extends DokuWiki_Action_Plugin 'href' => $style, ]; } + + // Add custom plugin CSS for column filters + $event->data['link'][] = [ + 'type' => 'text/css', + 'rel' => 'stylesheet', + 'href' => DOKU_BASE . 'lib/plugins/datatables/style.css', + ]; } /** diff --git a/conf/default.php b/conf/default.php index 20d07fe..fcd60be 100644 --- a/conf/default.php +++ b/conf/default.php @@ -7,4 +7,6 @@ $conf['excludedPages'] = '(wiki)'; -$conf['enableForAllTables'] = 0; \ No newline at end of file +$conf['enableForAllTables'] = 0; +$conf['enableColumnFilters'] = 1; +$conf['columnFiltersPosition'] = 'header'; \ No newline at end of file diff --git a/conf/metadata.php b/conf/metadata.php index 87301eb..ac95243 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -7,3 +7,6 @@ $meta['excludedPages'] = array('regex'); $meta['enableForAllTables'] = array('onoff'); +$meta['enableColumnFilters'] = array('onoff'); +$meta['columnFiltersPosition'] = array('multichoice', '_choices' => array('header', 'footer')); + diff --git a/lang/de/settings.php b/lang/de/settings.php index 43f5c65..ca26130 100644 --- a/lang/de/settings.php +++ b/lang/de/settings.php @@ -11,3 +11,6 @@ $lang['excludedPages'] = 'Ausgeschlossene Seiten (benutze Regulären Ausdrücke)'; $lang['enableForAllTables'] = 'Aktiviert die Datentabellen für alle "gut formatierte " Tabellen'; $lang['enableLocalization'] = 'Aktiviert die automatische Lokalisierung'; +$lang['enableColumnFilters'] = 'Aktiviert Spaltenfilter mit Dropdown und Suchfunktion'; +$lang['columnFiltersPosition'] = 'Position der Spaltenfilter (header oder footer)'; + diff --git a/lang/en/settings.php b/lang/en/settings.php index ae28902..9d8b84f 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -10,3 +10,6 @@ $lang['excludedPages'] = 'Excluded pages (insert a regex)'; $lang['enableForAllTables'] = 'Enable DataTables for all "well formatted" tables'; $lang['enableLocalization'] = 'Enable automatic localization'; +$lang['enableColumnFilters'] = 'Enable column filters with dropdown and search functionality'; +$lang['columnFiltersPosition'] = 'Position of column filters (header or footer)'; + diff --git a/script.js b/script.js index 6b79473..bc36ef7 100644 --- a/script.js +++ b/script.js @@ -48,7 +48,110 @@ jQuery(document).ready(function () { !jQuery('tbody', $target_table).find('[rowspan], [colspan]').length ) { $target_table.attr('width', '100%'); - $target_table.DataTable(dt_config); + const table = $target_table.DataTable(dt_config); + + // Add column filters if enabled + if (JSINFO.plugin.datatables.enableColumnFilters) { + addColumnFilters(table, $target_table); + } + } + } + + /** + * Add column filters to DataTable + * + * @param {DataTable} table DataTable instance + * @param {jQuery} $target_table Table jQuery object + */ + function addColumnFilters(table, $target_table) { + const position = JSINFO.plugin.datatables.columnFiltersPosition || 'header'; + const $thead = $target_table.find('thead'); + + // Create filter row + const $filterRow = jQuery(''); + + // Add filter for each column + table.columns().every(function(index) { + const column = this; + const $th = jQuery(''); + + // Create container for input and select + const $filterContainer = jQuery('
'); + + // Create search input for filtering dropdown options + const $searchInput = jQuery(''); + + // Create select dropdown + const $select = jQuery(''); + + // Get unique values from column + const uniqueValues = []; + const uniqueTexts = new Set(); + + column.data().each(function(d) { + if (d) { + // Extract text content if it contains HTML + const text = jQuery('
').html(d).text().trim(); + if (text && !uniqueTexts.has(text)) { + uniqueTexts.add(text); + uniqueValues.push(text); + } + } + }); + + // Sort unique values + uniqueValues.sort(); + + // Populate select with unique values + uniqueValues.forEach(function(value) { + $select.append(''); + }); + + // Filter dropdown options based on search input + $searchInput.on('keyup', function() { + const searchTerm = jQuery(this).val().toLowerCase(); + $select.find('option').each(function() { + const optionText = jQuery(this).text().toLowerCase(); + if (optionText.indexOf(searchTerm) > -1 || jQuery(this).val() === '') { + jQuery(this).show(); + } else { + jQuery(this).hide(); + } + }); + }); + + // Filter table when dropdown changes + $select.on('change', function() { + const val = jQuery(this).val(); + if (val) { + // Escape special regex characters and search for exact match + const escapedVal = val.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + // Search using regex that matches the text anywhere in the cell + column.search(escapedVal, true, false).draw(); + } else { + // Clear search + column.search('').draw(); + } + }); + + // Add elements to container + $filterContainer.append($searchInput); + $filterContainer.append($select); + $th.append($filterContainer); + $filterRow.append($th); + }); + + // Add filter row to table + if (position === 'header') { + $thead.append($filterRow); + } else { + // Create tfoot if it doesn't exist + let $tfoot = $target_table.find('tfoot'); + if ($tfoot.length === 0) { + $tfoot = jQuery(''); + $target_table.append($tfoot); + } + $tfoot.append($filterRow); } } diff --git a/style.css b/style.css new file mode 100644 index 0000000..07fbfb0 --- /dev/null +++ b/style.css @@ -0,0 +1,80 @@ +/** + * DataTables Plugin - Column Filters Styling + * + * @author Giuseppe Di Terlizzi + */ + +/* Column filter row styling */ +.dt-column-filters th { + padding: 5px !important; + background-color: #f5f5f5; + border-bottom: 2px solid #ddd; +} + +/* Filter container */ +.dt-filter-container { + display: flex; + flex-direction: column; + gap: 3px; + min-width: 120px; +} + +/* Search input styling */ +.dt-filter-search { + width: 100%; + padding: 4px 6px; + font-size: 12px; + border: 1px solid #ccc; + border-radius: 3px; + box-sizing: border-box; + background-color: #fff; +} + +.dt-filter-search:focus { + outline: none; + border-color: #66afe9; + box-shadow: 0 0 3px rgba(102, 175, 233, 0.5); +} + +/* Select dropdown styling */ +.dt-filter-select { + width: 100%; + padding: 4px 6px; + font-size: 12px; + border: 1px solid #ccc; + border-radius: 3px; + box-sizing: border-box; + background-color: #fff; + cursor: pointer; +} + +.dt-filter-select:focus { + outline: none; + border-color: #66afe9; + box-shadow: 0 0 3px rgba(102, 175, 233, 0.5); +} + +/* Responsive adjustments */ +@media screen and (max-width: 768px) { + .dt-filter-container { + min-width: 80px; + } + + .dt-filter-search, + .dt-filter-select { + font-size: 11px; + padding: 3px 4px; + } +} + +/* Theme compatibility - Bootstrap */ +.bootstrap3 .dt-filter-search, +.bootstrap3 .dt-filter-select { + height: auto; +} + +/* Theme compatibility - jQuery UI */ +.dataTables_wrapper.dataTable .dt-filter-search, +.dataTables_wrapper.dataTable .dt-filter-select { + font-family: inherit; +}