forked from fixin.me/fixin.me
Improve index tables
Stretch inputs to column width in place of fixed sizing. Set minimum column widths for columns with form inputs. Simplify table operations by removing `thead` and adding sentinel. Manage empty table display with CSS only.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
*
|
||||
* NOTE: style in a modular way, similar to how CSS @scope would be used,
|
||||
* to make transition easier once @scope is widely available. */
|
||||
/* TODO: change :root -> <html> ? */
|
||||
:root {
|
||||
--color-focus-gray: #f3f3f3;
|
||||
--color-border-gray: #dddddd;
|
||||
@@ -171,6 +172,7 @@ table svg:not(:only-child) {
|
||||
width: 1.25em;
|
||||
}
|
||||
input:focus-visible,
|
||||
/* TODO: select:focus-visible is redundant with -within? */
|
||||
select:focus-visible,
|
||||
select:focus-within,
|
||||
/* TODO: how to achieve `summary:focus-within` for `::details-content`? */
|
||||
@@ -301,6 +303,7 @@ header {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
/* TODO: change .navigation -> <nav> ? */
|
||||
.navigation {
|
||||
display: flex;
|
||||
grid-area: nav;
|
||||
@@ -463,20 +466,27 @@ header {
|
||||
|
||||
|
||||
.items-table {
|
||||
border-spacing: 0;
|
||||
border: 1px solid var(--color-border-gray);
|
||||
border: none;
|
||||
border-radius: 0.25em;
|
||||
border-spacing: 0;
|
||||
font-size: 0.85rem;
|
||||
text-align: left;
|
||||
}
|
||||
.items-table thead {
|
||||
font-size: 0.8rem;
|
||||
.items-table:has(tbody > tr > td) {
|
||||
border: 1px solid var(--color-border-gray);
|
||||
}
|
||||
.items-table thead,
|
||||
.items-table tbody tr:hover {
|
||||
.items-table:has(tbody > tr > td) caption {
|
||||
display: none;
|
||||
}
|
||||
.items-table tbody:not(:has(tr > td)){
|
||||
visibility: collapse;
|
||||
}
|
||||
.items-table th,
|
||||
.items-table tr:where(:hover) {
|
||||
background-color: var(--color-focus-gray);
|
||||
}
|
||||
.items-table th {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.75em 0 0.75em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -560,6 +570,20 @@ header {
|
||||
.hmin50 {
|
||||
min-width: 50%;
|
||||
}
|
||||
.hminnumber {
|
||||
min-width: 5em;
|
||||
}
|
||||
.hmintext {
|
||||
min-width: 6em;
|
||||
}
|
||||
.hstretch {
|
||||
/* `width` conflicts with <input>/<textarea> size defined by [size]/[cols]
|
||||
* attributes. It's best to not use these attributes (e.g. set them to 1) when
|
||||
* setting width through stylesheet. */
|
||||
/* NOTE: `field-sizing` can be considered after upgrade do FF >=152 */
|
||||
width: -moz-available;
|
||||
width: stretch;
|
||||
}
|
||||
.italic {
|
||||
color: var(--color-gray);
|
||||
font-style: italic;
|
||||
|
||||
@@ -92,6 +92,10 @@ module ApplicationHelper
|
||||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||
def #{selector}(method, options = {})
|
||||
options[:maxlength] ||= object.type_for_attribute(method).limit
|
||||
# If [cols]/[size] is not set, minimum field width is controlled with
|
||||
# stylesheet. To prevent browser estimating missing [cols]/[size]
|
||||
# based on e.g. [maxlength], set it to 1.
|
||||
options[:#{selector == :text_area ? :cols : :size}] ||= 1
|
||||
super
|
||||
end
|
||||
RUBY_EVAL
|
||||
@@ -103,7 +107,7 @@ module ApplicationHelper
|
||||
options[:min] ||= Float::MIN_15.to_scientific
|
||||
options[:max] ||= Float::MAX_15.to_scientific
|
||||
options[:oninput] = 'numberValidate(event)'
|
||||
options[:size] ||= 6
|
||||
options[:size] ||= options[:class]&.split&.include?('hstretch') ? 1 : 6
|
||||
options[:step] ||= :any
|
||||
options[:value] = object.public_send("#{method}_before_type_cast")
|
||||
end
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<details id="quantity_select" class="center hexpand" open
|
||||
onkeydown="detailsProcessKey(event)">
|
||||
<summary autofocus>
|
||||
<!-- TODO: Set content with CSS when span empty to avoid duplication -->
|
||||
<%# TODO: Set content with CSS when span empty to avoid duplication; or
|
||||
change to disabled input with prompt %>
|
||||
<span data-prompt="<%= t('.select_quantity') %>">
|
||||
<%= t('.select_quantity') %>
|
||||
</span>
|
||||
|
||||
@@ -3,17 +3,14 @@
|
||||
data: {link: link, form: form_tag, hidden_row: hidden_row} do %>
|
||||
|
||||
<td style="--depth:<%= @quantity.depth %>">
|
||||
<%= form.text_field :name, required: true, autofocus: true, size: 20 %>
|
||||
</td>
|
||||
<td>
|
||||
<%= form.text_area :description, cols: 30, rows: 1, escape: false %>
|
||||
</td>
|
||||
<%= form.text_field :name, autofocus: true, class: 'hstretch', required: true %>
|
||||
<td><%= form.text_area :description, class: 'hstretch', escape: false, rows: 1 %>
|
||||
|
||||
<td class="flex">
|
||||
<%= form.button %>
|
||||
<%= image_link_to t(:cancel), "close-outline", quantities_path, class: 'dangerous',
|
||||
name: :cancel, onclick: render_turbo_stream('form_close', {row: row}) %>
|
||||
</td>
|
||||
<td></td>
|
||||
<%= image_link_to t(:cancel), "close-outline", quantities_path,
|
||||
class: 'dangerous', name: :cancel,
|
||||
onclick: render_turbo_stream('form_close', {row: row}) %>
|
||||
<td>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<%= turbo_stream.close_form dom_target(:new, @quantity.parent || :quantity) %>
|
||||
<%= turbo_stream.remove :no_items %>
|
||||
<% @ancestors.each do |ancestor| %>
|
||||
<%= turbo_stream.replace ancestor %>
|
||||
<% end %>
|
||||
<%= @before ? turbo_stream.before(@before, @quantity) :
|
||||
turbo_stream.append(:quantities, @quantity) %>
|
||||
<%= turbo_stream.before @before || :endmark, @quantity %>
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
<%= turbo_stream.replace ancestor %>
|
||||
<% end %>
|
||||
<%= turbo_stream.remove @quantity %>
|
||||
<%= turbo_stream.append(:quantities, render_no_items) if current_user.quantities.empty? %>
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
link: nil,
|
||||
form_tag: dom_target(:edit, @quantity, :form)} %>
|
||||
|
||||
<%= turbo_stream.append :quantity_form do %>
|
||||
<%= turbo_stream.append_all 'body' do %>
|
||||
<%- tabular_form_with model: @quantity, html: {id: ids[:form_tag]} do %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= turbo_stream.hide ids[:hidden_row] %>
|
||||
<%= turbo_stream.remove ids[:row] %>
|
||||
<%= turbo_stream.after @quantity, partial: 'form', locals: ids -%>
|
||||
|
||||
@@ -3,31 +3,29 @@
|
||||
<%= image_link_to t('.new_quantity'), 'plus-outline', new_quantity_path,
|
||||
id: :new_quantity_link, onclick: 'this.blur();', data: {turbo_stream: true} %>
|
||||
<% end %>
|
||||
<%#= image_link_to t('.import_quantities'), 'download-outline', default_quantities_path,
|
||||
class: 'tools-area' %>
|
||||
<%#= image_link_to t('.import_quantities'), 'download-outline',
|
||||
default_quantities_path, class: 'tools-area' %>
|
||||
</div>
|
||||
|
||||
<%# TODO: remove? form can be inserted directly, e.g. at the end of index %>
|
||||
<%= tag.div class: 'main-area', id: :quantity_form %>
|
||||
|
||||
<table class="main-area items-table">
|
||||
<thead>
|
||||
<caption class="hint"><%= t '.no_items' %></caption>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><%= Quantity.human_attribute_name(:name) %></th>
|
||||
<th class="hexpand"><%= Quantity.human_attribute_name(:description) %></th>
|
||||
<th class="hmintext"><%= Quantity.human_attribute_name(:name) %>
|
||||
<th class="hexpand"><%= Quantity.human_attribute_name(:description) %>
|
||||
<% if current_user.at_least(:active) %>
|
||||
<th><%= t :actions %></th>
|
||||
<th></th>
|
||||
<th><%= t :actions %>
|
||||
<th>
|
||||
|
||||
<tr id="quantity_" hidden
|
||||
ondragover="dragOver(event)" ondrop="drop(event)"
|
||||
ondragenter="dragEnter(event)" ondragleave="dragLeave(event)"
|
||||
data-drop-id="quantity_"
|
||||
data-drop-id-param="<%= field_name(:quantity, :parent_id) %>">
|
||||
<th colspan="4"><%= t '.top_level_drop' %>
|
||||
<% end %>
|
||||
</tr>
|
||||
<%= tag.tr id: "quantity_", hidden: true,
|
||||
ondragover: "dragOver(event)", ondrop: "drop(event)",
|
||||
ondragenter: "dragEnter(event)", ondragleave: "dragLeave(event)",
|
||||
data: {drop_id: "quantity_", drop_id_param: "quantity[parent_id]"} do %>
|
||||
<th colspan="4"><%= t '.top_level_drop' %></th>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody id="quantities">
|
||||
<%= render(@quantities) || render_no_items %>
|
||||
</tbody>
|
||||
|
||||
<%= render @quantities %>
|
||||
<tr id="endmark" hidden>
|
||||
</table>
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
|
||||
<%= turbo_stream.disable ids[:link] -%>
|
||||
|
||||
<%= turbo_stream.append :quantity_form do %>
|
||||
<%= turbo_stream.append_all 'body' do %>
|
||||
<%- tabular_form_with model: @quantity, html: {id: ids[:form_tag]} do |form| %>
|
||||
<%= form.hidden_field :parent_id if @quantity.parent_id? %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @quantity.parent_id? %>
|
||||
<%= turbo_stream.remove ids[:row] %>
|
||||
<%= turbo_stream.after @quantity.parent, partial: 'form', locals: ids %>
|
||||
<% else %>
|
||||
<%= turbo_stream.prepend :quantities, partial: 'form', locals: ids %>
|
||||
<% end %>
|
||||
<%= turbo_stream.after @quantity.parent || :quantity_, partial: 'form', locals: ids %>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<% @self_and_progenies.each do |q| %>
|
||||
<%= turbo_stream.remove q %>
|
||||
<% end %>
|
||||
<% @previous_ancestors.union(@ancestors).each do |ancestor| %>
|
||||
<%= turbo_stream.replace ancestor %>
|
||||
<% end %>
|
||||
<% @self_and_progenies.each do |q| %>
|
||||
<%= @before ? turbo_stream.before(@before, q) : turbo_stream.append(:quantities, q) %>
|
||||
<% @self_and_progenies.each do |quantity| %>
|
||||
<%= turbo_stream.remove quantity %>
|
||||
<%= turbo_stream.before @before || :endmark, quantity %>
|
||||
<% end %>
|
||||
|
||||
@@ -3,20 +3,17 @@
|
||||
data: {link: link, form: form_tag, hidden_row: hidden_row} do %>
|
||||
|
||||
<td style="--depth:<%= @unit.base_id? ? 1 : 0 %>">
|
||||
<%= form.text_field :symbol, required: true, autofocus: true, size: 12 %>
|
||||
</td>
|
||||
<%= form.text_field :symbol, autofocus: true, class: 'hstretch', required: true %>
|
||||
<td>
|
||||
<%= form.text_area :description, cols: 30, rows: 1, escape: false %>
|
||||
</td>
|
||||
<td>
|
||||
<%= form.number_field :multiplier, required: true if @unit.base_id? %>
|
||||
</td>
|
||||
<% if @unit.base_id? %>
|
||||
<%= form.number_field :multiplier, class: 'hstretch', required: true %>
|
||||
<% end %>
|
||||
<td><%= form.text_area :description, class: 'hstretch', escape: false, rows: 1 %>
|
||||
|
||||
<td class="flex">
|
||||
<%= form.button %>
|
||||
<%= image_link_to t(:cancel), "close-outline", units_path, class: 'dangerous',
|
||||
name: :cancel, onclick: render_turbo_stream('form_close', {row: row}) %>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
<td style="--depth:<%= unit.base_id? ? 1 : 0 %>">
|
||||
<%= link_to unit, edit_unit_path(unit), class: 'link', onclick: 'this.blur();',
|
||||
data: {turbo_stream: true} %>
|
||||
<td><%= unit.description %>
|
||||
<%# Closing <td> tag required to avoid whitespace inclusion for testing. %>
|
||||
<td class="ralign"><%= unit.multiplier.to_html if unit.base_id? %></td>
|
||||
<td><%= unit.description %>
|
||||
|
||||
<% if current_user.at_least(:active) %>
|
||||
<td class="flex">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<%= turbo_stream.close_form dom_target(:new, @unit.base || :unit) %>
|
||||
<%= turbo_stream.remove :no_items %>
|
||||
<%= turbo_stream.replace @unit.base if @unit.base_id? %>
|
||||
<%= @before ? turbo_stream.before(@before, @unit) : turbo_stream.append(:units, @unit) %>
|
||||
<%= turbo_stream.before @before || :endmark, @unit %>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
<%= turbo_stream.replace @unit.base if @unit.base_id? %>
|
||||
<%= turbo_stream.remove @unit %>
|
||||
<%= turbo_stream.append(:units, render_no_items) if current_user.units.empty? %>
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
link: nil,
|
||||
form_tag: dom_target(:edit, @unit, :form)} %>
|
||||
|
||||
<%= turbo_stream.append :unit_form do %>
|
||||
<%= turbo_stream.append_all 'body' do %>
|
||||
<%- tabular_form_with model: @unit, html: {id: ids[:form_tag]} do %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= turbo_stream.hide ids[:hidden_row] %>
|
||||
<%= turbo_stream.remove ids[:row] %>
|
||||
<%= turbo_stream.after @unit, partial: 'form', locals: ids -%>
|
||||
|
||||
@@ -7,28 +7,27 @@
|
||||
class: 'tools-area' %>
|
||||
</div>
|
||||
|
||||
<%# TODO: remove? form can be inserted directly, e.g. at the end of index %>
|
||||
<%= tag.div id: :unit_form %>
|
||||
|
||||
<table class="main-area items-table">
|
||||
<thead>
|
||||
<caption class="hint"><%= t '.no_items' %></caption>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><%= Unit.human_attribute_name(:symbol) %></th>
|
||||
<th class="hexpand"><%= Unit.human_attribute_name(:description) %></th>
|
||||
<th><%= Unit.human_attribute_name(:multiplier) %></th>
|
||||
<th class="hmintext"><%= Unit.human_attribute_name(:symbol) %>
|
||||
<th class="hminnumber"><%= Unit.human_attribute_name(:multiplier) %>
|
||||
<th class="hexpand"><%= Unit.human_attribute_name(:description) %>
|
||||
<% if current_user.at_least(:active) %>
|
||||
<th><%= t :actions %></th>
|
||||
<th></th>
|
||||
<th><%= t :actions %>
|
||||
<th>
|
||||
|
||||
<tr id="unit_" hidden
|
||||
ondragover="dragOver(event)" ondrop="drop(event)"
|
||||
ondragenter="dragEnter(event)" ondragleave="dragLeave(event)"
|
||||
data-drop-id="unit_" data-drop-id-param="<%= field_name(:unit, :base_id) %>">
|
||||
<th colspan="5"><%= t '.top_level_drop' %>
|
||||
<% end %>
|
||||
</tr>
|
||||
<%= tag.tr id: "unit_", hidden: true,
|
||||
ondragover: "dragOver(event)", ondrop: "drop(event)",
|
||||
ondragenter: "dragEnter(event)", ondragleave: "dragLeave(event)",
|
||||
data: {drop_id: "unit_", drop_id_param: "unit[base_id]"} do %>
|
||||
<th colspan="5"><%= t '.top_level_drop' %></th>
|
||||
<% end %>
|
||||
</thead>
|
||||
<tbody id="units">
|
||||
<%= render(@units) || render_no_items %>
|
||||
</tbody>
|
||||
|
||||
<%= render @units %>
|
||||
<%# Hidden rows with IDs allow simplier row insertion by using only
|
||||
turbo_stream #after/#before instead of #after + #prepend/#before + #append. %>
|
||||
<tr id="endmark" hidden>
|
||||
</table>
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
|
||||
<%= turbo_stream.disable ids[:link] -%>
|
||||
|
||||
<%= turbo_stream.append :unit_form do %>
|
||||
<%= turbo_stream.append_all 'body' do %>
|
||||
<%- tabular_form_with model: @unit, html: {id: ids[:form_tag]} do |form| %>
|
||||
<%= form.hidden_field :base_id if @unit.base_id? %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @unit.base_id? %>
|
||||
<%= turbo_stream.remove ids[:row] %>
|
||||
<%= turbo_stream.after @unit.base, partial: 'form', locals: ids %>
|
||||
<% else %>
|
||||
<%= turbo_stream.prepend :units, partial: 'form', locals: ids %>
|
||||
<% end %>
|
||||
<%= turbo_stream.after @unit.base || :unit_, partial: 'form', locals: ids %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<% [@previous_base, @unit.base].uniq.compact.each do |unit| %>
|
||||
<%= turbo_stream.replace unit %>
|
||||
<% end %>
|
||||
<%= turbo_stream.remove @unit %>
|
||||
<%= turbo_stream.replace @previous_base if @previous_base %>
|
||||
<%= turbo_stream.replace @unit.base if @unit.base_id? && (@previous_base&.id != @unit.base_id) %>
|
||||
<%= @before ? turbo_stream.before(@before, @unit) : turbo_stream.append(:units, @unit) %>
|
||||
<%= turbo_stream.before @before || :endmark, @unit %>
|
||||
|
||||
@@ -108,12 +108,12 @@ en:
|
||||
new_measurement: Add measurement
|
||||
quantities:
|
||||
navigation: Quantities
|
||||
no_items: There are no configured quantities. You can Add some or Import from defaults.
|
||||
quantity:
|
||||
new_subquantity: Child
|
||||
destroy: Delete
|
||||
index:
|
||||
new_quantity: Add quantity
|
||||
no_items: There are no configured quantities. You can Add some or Import from defaults.
|
||||
top_level_drop: Drop here to reposition into top-level quantity
|
||||
create:
|
||||
success: Created new quantity "%{quantity}"
|
||||
@@ -123,13 +123,13 @@ en:
|
||||
success: Deleted quantity "%{quantity}"
|
||||
units:
|
||||
navigation: Units
|
||||
no_items: There are no configured units. You can Add some or Import from defaults.
|
||||
unit:
|
||||
new_subunit: Subunit
|
||||
destroy: Delete
|
||||
index:
|
||||
new_unit: Add unit
|
||||
import_units: Import
|
||||
new_unit: Add unit
|
||||
no_items: There are no configured units. You can Add some or Import from defaults.
|
||||
top_level_drop: Drop here to reposition into top-level unit
|
||||
create:
|
||||
success: Created new unit "%{unit}"
|
||||
|
||||
Reference in New Issue
Block a user