diff --git a/app/assets/javascripts/wigu/table_crisscross.js b/app/assets/javascripts/wigu/table_crisscross.js new file mode 100644 index 0000000..e885b4d --- /dev/null +++ b/app/assets/javascripts/wigu/table_crisscross.js @@ -0,0 +1,49 @@ +// Crisscross highlighting for ActiveAdmin index tables. Clicking a cell marks +// its whole row and column with class "selected", which the stylesheet paints +// with var(--aa-selected) — so the clicked cell sits at the centre of a +// highlighted cross, making it easy to trace a value to its row and column in a +// wide table. +// +// Uses jQuery, which ActiveAdmin loads before this file (its own +// checkbox-toggler is jQuery too); the rest of the theme JS is dependency-free, +// but this behaviour lives entirely in AA's jQuery-driven index table. +// +// AA's table-checkbox-toggler selects the row when a cell is clicked; we undo +// that (re-click the checkbox) so crisscross highlighting never leaves rows +// ticked for batch actions. +$(document).ready(function () { + var marker = 'selected'; + + // "Select all" clears every row marker left by AA's toggler. + $('table.index_table .toggle_all').click(function () { + $(this).closest('table').find('tr.' + marker).removeClass(marker); + }); + + // Clicking the row checkbox itself must not bubble into the cell handler. + $('table.index_table').find(':checkbox').click(function (e) { + e.stopPropagation(); + $(this).closest('tr.' + marker).removeClass(marker); + }); + + $('table.index_table').on('click', 'td', function (e) { + var tdIndex = e.currentTarget.cellIndex, + $cell = $(e.currentTarget), + $table = $cell.closest('table'), + $row = $cell.closest('tr'), + $columnTds = $table.find('td:nth-child(' + (tdIndex + 1) + ')'), + $rowTds = $row.find('td'); + + // AA ticks the row checkbox on a cell click; click it again to revert. + $row.find(':checkbox').click(); + + if (!$cell.hasClass('col-selectable')) { + $table.find('td.' + marker).removeClass(marker); + $table.find('td.selected-cell').removeClass('selected-cell'); + $columnTds.addClass(marker); + $rowTds.addClass(marker); + // Mark the clicked cell itself so the stylesheet can set it apart as the + // centre of the cross. + $cell.addClass('selected-cell'); + } + }); +}); diff --git a/app/assets/stylesheets/wigu/active_admin_theme.scss b/app/assets/stylesheets/wigu/active_admin_theme.scss index dd4761d..c0562e2 100644 --- a/app/assets/stylesheets/wigu/active_admin_theme.scss +++ b/app/assets/stylesheets/wigu/active_admin_theme.scss @@ -1774,11 +1774,22 @@ input[type='radio'] { // Selected cells: the zebra and hover rules above outrank ActiveAdmin's own // `tr.selected td` / `td.selected`, so without this a ticked row (batch // actions) or a click-highlighted row + column (crisscross) looks unselected. - // The :hover variants keep the selection visible under the pointer. + // The `tr >` in the td.selected selector is load-bearing: the even-stripe + // rule is `...tbody tr:nth-child(even) > td` (specificity 0,0,2,4); a plain + // `tbody td.selected` is only (0,0,2,3) and loses on even rows, breaking the + // cross every other row. Matching the `tr >` ties the specificity, so source + // order wins. The :hover variants keep the selection visible under the pointer. table.index_table tbody tr.selected > td, table.index_table tbody tr.selected:hover > td, - table.index_table tbody td.selected, + table.index_table tbody tr > td.selected, table.index_table tbody tr:hover > td.selected { background: var(--aa-selected); } + // The clicked cell sits at the centre of the cross; an accent outline marks + // it apart from the row/column tint without a competing fill (outset inward + // so it adds no layout shift). + table.index_table tbody tr > td.selected-cell { + outline: 2px solid var(--aa-accent); + outline-offset: -2px; + } // Plain content tables (e.g. the CDR show "Attempts" table) are striped by the // base CSS with light fills; map the odd/even rows onto the dark surfaces too. table tbody tr.odd > td, table tbody tr.odd > th { background-color: var(--aa-surface); } diff --git a/img/crisscross-dark.png b/img/crisscross-dark.png new file mode 100644 index 0000000..59312fd Binary files /dev/null and b/img/crisscross-dark.png differ diff --git a/img/crisscross-light.png b/img/crisscross-light.png new file mode 100644 index 0000000..7afdb53 Binary files /dev/null and b/img/crisscross-light.png differ diff --git a/test/crisscross_screenshot.rb b/test/crisscross_screenshot.rb new file mode 100644 index 0000000..0d5fd38 --- /dev/null +++ b/test/crisscross_screenshot.rb @@ -0,0 +1,23 @@ +# Regenerates img/crisscross-light.png / img/crisscross-dark.png — the crisscross +# cell highlighting (wigu/table_crisscross.js) in both palettes. Clicks a middle +# cell so the screenshot shows the highlighted row + column crossing at it, then +# crops to the demo panel. +require_relative "capybara_boot" +require "capybara/dsl" + +include Capybara::DSL + +visit "/admin/table" +# Click a cell away from the edges so the cross is clearly centred (row 3, Email). +find("table.index_table tbody tr:nth-child(3) td:nth-child(3)").click +page.driver.resize_window_to(page.driver.window_handles.first, 1120, 640) + +{ "light" => "crisscross-light.png", "dark" => "crisscross-dark.png" }.each do |theme, file| + page.execute_script(%(document.documentElement.setAttribute('data-theme', '#{theme}'))) + sleep 0.3 + out = File.expand_path("../img/#{file}", __dir__) + page.driver.browser.screenshot(path: out, selector: ".panel") + puts "wrote #{out} (#{File.size(out)} bytes)" +end + +Capybara.reset_sessions! diff --git a/test/dummy/app/admin/table.rb b/test/dummy/app/admin/table.rb new file mode 100644 index 0000000..1ef7c62 --- /dev/null +++ b/test/dummy/app/admin/table.rb @@ -0,0 +1,29 @@ +# Demo page with a plain index table so the crisscross highlighting (wigu/ +# table_crisscross.js) can be exercised and screenshotted: clicking a cell marks +# its whole row and column with .selected, which the theme paints. +ActiveAdmin.register_page "Table" do + menu priority: 3 + + ROWS = [ + %w[101 Ada\ Lovelace ada@example.com UK Active], + %w[102 Alan\ Turing alan@example.com UK Active], + %w[103 Grace\ Hopper grace@example.com US Closed], + %w[104 Katherine\ Johnson katherine@example.com US Active], + %w[105 Edsger\ Dijkstra edsger@example.com NL Closed], + %w[106 Donald\ Knuth donald@example.com US Active] + ].freeze + + content do + panel "Crisscross highlighting (click a cell)" do + head = "IDNameEmail" \ + "CountryStatus" + body = ROWS.map do |r| + "" + r.map { |c| "#{ERB::Util.html_escape(c)}" }.join + "" + end.join + text_node( + (%() + head + "" + body + "
") + .html_safe + ) + end + end +end diff --git a/test/dummy/app/assets/javascripts/active_admin.js b/test/dummy/app/assets/javascripts/active_admin.js index 6780be4..742c047 100644 --- a/test/dummy/app/assets/javascripts/active_admin.js +++ b/test/dummy/app/assets/javascripts/active_admin.js @@ -1,3 +1,4 @@ //= require active_admin/base //= require wigu/server_clock //= require wigu/theme_toggle +//= require wigu/table_crisscross