diff --git a/app/views/admin/products_v3/_product_row.html.haml b/app/views/admin/products_v3/_product_row.html.haml index d009b544ed..c918937570 100644 --- a/app/views/admin/products_v3/_product_row.html.haml +++ b/app/views/admin/products_v3/_product_row.html.haml @@ -1,13 +1,13 @@ -%td.with-image{ id: "image-#{product.id}" } +%td.col-image.with-image{ id: "image-#{product.id}" } = render partial: "product_image", locals: { product: } -%td.field.align-left.header.naked_inputs +%td.col-name.field.align-left.header.naked_inputs = f.hidden_field :id = f.text_field :name, 'aria-label': t('admin.products_page.columns.name') = error_message_on product, :name -%td.field.naked_inputs +%td.col-sku.field.naked_inputs = f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku') = error_message_on product, :sku -%td.field.naked_inputs{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' } +%td.col-unit_scale.field.naked_inputs{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' } = f.hidden_field :variant_unit = f.hidden_field :variant_unit_scale = f.select :variant_unit_with_scale, @@ -19,23 +19,23 @@ .field = f.text_field :variant_unit_name, 'aria-label': t('items'), 'data-toggle-control-target': 'control', style: (product.variant_unit == "items" ? "" : "display: none") = error_message_on product, :variant_unit_name, 'data-toggle-control-target': 'control' -%td.align-right +%td.col-unit.align-right -# empty -%td.align-right +%td.col-price.align-right -# empty -%td.align-right +%td.col-on_demand.align-right -# empty -%td.naked_inputs +%td.col-producer.naked_inputs = render(SearchableDropdownComponent.new(form: f, name: :supplier_id, aria_label: t('.producer_field_name'), options: producer_options, selected_option: product.supplier_id, placeholder_value: t('admin.products_v3.filters.search_for_producers'))) -%td.align-left +%td.col-category.align-left -# empty -%td.align-left -%td.align-left +%td.col-tax_category.align-left +%td.col-inherits_properties.align-left .content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below) %td.align-right = render(VerticalEllipsisMenu::Component.new) do diff --git a/app/views/admin/products_v3/_table.html.haml b/app/views/admin/products_v3/_table.html.haml index d15e471012..0b8cc98b79 100644 --- a/app/views/admin/products_v3/_table.html.haml +++ b/app/views/admin/products_v3/_table.html.haml @@ -16,17 +16,17 @@ %table.products{ 'data-column-preferences-target': "table" } %colgroup -# The `min-width` property works in Chrome but not Firefox so is considered progressive enhancement. - %col{ 'data-column-preferences-name': :image, width:"56px" }= # (image size + padding) - %col{ 'data-column-preferences-name': :name, style:"min-width: 6em" }= # (grow to fill) - %col{ 'data-column-preferences-name': :sku, width:"8%", style:"min-width: 6em" } - %col{ 'data-column-preferences-name': :unit_scale, width:"8%" } - %col{ 'data-column-preferences-name': :unit, width:"8%" } - %col{ 'data-column-preferences-name': :price, width:"5%", style:"min-width: 5em" } - %col{ 'data-column-preferences-name': :on_hand, width:"10%"} - %col{ 'data-column-preferences-name': :producer, style:"min-width: 6em" }= # (grow to fill) - %col{ 'data-column-preferences-name': :category, width:"8%" } - %col{ 'data-column-preferences-name': :tax_category, width:"8%" } - %col{ 'data-column-preferences-name': :inherits_properties, width:"5%" } + %col.col-image{ width:"56px" }= # (image size + padding) + %col.col-name{ style:"min-width: 6em" }= # (grow to fill) + %col.col-sku{ width:"8%", style:"min-width: 6em" } + %col.col-unit_scale{ width:"8%" } + %col.col-unit{ width:"8%" } + %col.col-price{ width:"5%", style:"min-width: 5em" } + %col.col-on_hand{ width:"10%"} + %col.col-producer{ style:"min-width: 6em" }= # (grow to fill) + %col.col-category{ width:"8%" } + %col.col-tax_category{ width:"8%" } + %col.col-inherits_properties{ width:"5%" } %col{ width:"5%", style:"min-width: 3em"}= # Actions %thead %tr @@ -49,18 +49,18 @@ = t('.reset') = form.submit t('.save'), class: "medium" %tr - %th.align-left= # image + %th.col-image.align-left= # image = render partial: 'spree/admin/shared/stimulus_sortable_header', locals: { column: :name, sorted: params.dig(:q, :s), default: 'name asc' } - %th.align-left.with-input= t('admin.products_page.columns.sku') - %th.align-left.with-input= t('admin.products_page.columns.unit_scale') - %th.align-left.with-input= t('admin.products_page.columns.unit') - %th.align-left.with-input= t('admin.products_page.columns.price') - %th.align-left.with-input= t('admin.products_page.columns.on_hand') - %th.align-left= t('admin.products_page.columns.producer') - %th.align-left= t('admin.products_page.columns.category') - %th.align-left= t('admin.products_page.columns.tax_category') - %th.align-left= t('admin.products_page.columns.inherits_properties') + %th.align-left.col-sku.with-input= t('admin.products_page.columns.sku') + %th.align-left.col-unit_scale.with-input= t('admin.products_page.columns.unit_scale') + %th.align-left.col-unit.with-input= t('admin.products_page.columns.unit') + %th.align-left.col-price.with-input= t('admin.products_page.columns.price') + %th.align-left.col-on_hand.with-input= t('admin.products_page.columns.on_hand') + %th.align-left.col-producer= t('admin.products_page.columns.producer') + %th.align-left.col-category= t('admin.products_page.columns.category') + %th.align-left.col-tax_category= t('admin.products_page.columns.tax_category') + %th.align-left.col-inherits_properties= t('admin.products_page.columns.inherits_properties') %th.align-right= t('admin.products_page.columns.actions') - products.each_with_index do |product, product_index| = form.fields_for("products", product, index: product_index) do |product_form| diff --git a/app/views/admin/products_v3/_variant_row.html.haml b/app/views/admin/products_v3/_variant_row.html.haml index 3b3b38ac54..311b833e04 100644 --- a/app/views/admin/products_v3/_variant_row.html.haml +++ b/app/views/admin/products_v3/_variant_row.html.haml @@ -1,15 +1,15 @@ -%td +%td.col-image -# empty -%td.field.naked_inputs +%td.col-name.field.naked_inputs = f.hidden_field :id = f.text_field :display_name, 'aria-label': t('admin.products_page.columns.name'), placeholder: variant.product.name = error_message_on variant, :display_name -%td.field.naked_inputs +%td.col-sku.field.naked_inputs = f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku') = error_message_on variant, :sku -%td +%td.col-unit_scale -# empty -%td.field.popout{'data-controller': "popout", 'data-popout-update-display-value': "false"} +%td.col-unit.field.popout{'data-controller': "popout", 'data-popout-update-display-value': "false"} = f.button :unit_to_display, class: "popout__button", 'aria-label': t('admin.products_page.columns.unit'), 'data-popout-target': "button" do = variant.unit_to_display # Show the generated summary of unit values %div.popout__container{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" } @@ -25,10 +25,10 @@ = f.label :display_as, t('admin.products_page.columns.display_as') = f.text_field :display_as, placeholder: VariantUnits::OptionValueNamer.new(variant).name = error_message_on variant, :unit_value -%td.field.naked_inputs +%td.col-price.field.naked_inputs = f.text_field :price, 'aria-label': t('admin.products_page.columns.price'), value: number_to_currency(variant.price, unit: '')&.strip # TODO: add a spec to prove that this formatting is necessary. If so, it should be in a shared form helper for currency inputs = error_message_on variant, :price -%td.field.popout{'data-controller': "popout"} +%td.col-on_hand.field.popout{'data-controller': "popout"} %button.popout__button{'data-popout-target': "button", 'aria-label': t('admin.products_page.columns.on_hand')} = variant.on_demand ? t(:on_demand) : variant.on_hand %div.popout__container{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" } @@ -39,16 +39,16 @@ = f.label :on_demand do = f.check_box :on_demand, 'data-action': 'change->toggle-control#disableIfPresent change->popout#closeIfChecked' = t(:on_demand) -%td.align-left +%td.col-producer.align-left -# empty producer name -%td.field.naked_inputs +%td.col-category.field.naked_inputs = render(SearchableDropdownComponent.new(form: f, name: :primary_taxon_id, options: category_options, selected_option: variant.primary_taxon_id, aria_label: t('.category_field_name'), placeholder_value: t('admin.products_v3.filters.search_for_categories'))) -%td.field.naked_inputs +%td.col-tax_category.field.naked_inputs = render(SearchableDropdownComponent.new(form: f, name: :tax_category_id, options: tax_category_options, @@ -57,7 +57,7 @@ aria_label: t('.tax_category_field_name'), placeholder_value: t('.search_for_tax_categories'))) = error_message_on variant, :tax_category -%td.align-left +%td.col-inherits_properties.align-left -# empty %td.align-right = render(VerticalEllipsisMenu::Component.new) do diff --git a/app/views/spree/admin/shared/_stimulus_sortable_header.html.haml b/app/views/spree/admin/shared/_stimulus_sortable_header.html.haml index 08db1b6ce9..051ffa1f4f 100644 --- a/app/views/spree/admin/shared/_stimulus_sortable_header.html.haml +++ b/app/views/spree/admin/shared/_stimulus_sortable_header.html.haml @@ -1,4 +1,4 @@ -%th +%th{ class: "col-#{column}" } %a{ "data-controller": "search", "data-action": "click->search#changeSorting", "data-column": "#{column}", "data-current": (sorted || default).to_s } = t("spree.admin.shared.sortable_header.#{column.to_s}") diff --git a/app/webpacker/controllers/column_preferences_controller.js b/app/webpacker/controllers/column_preferences_controller.js index dfbcee6f2b..9b55a961dc 100644 --- a/app/webpacker/controllers/column_preferences_controller.js +++ b/app/webpacker/controllers/column_preferences_controller.js @@ -12,7 +12,7 @@ export default class ColumnPreferencesController extends Controller { cell.dataset.defaultColSpan ||= cell.colSpan; }); - this.checkboxes = this.element.querySelectorAll("input[type=checkbox]"); + this.checkboxes = Array.from(this.element.querySelectorAll("input[type=checkbox]")); for (const element of this.checkboxes) { // On initial load this.#showHideColumn(element); @@ -26,40 +26,17 @@ export default class ColumnPreferencesController extends Controller { #showHideColumn(e) { const element = e.target || e; const name = element.dataset.columnName; - const selector = `col[data-column-preferences-name="${name}"]`; - const column = this.table.querySelector(selector); - const index = this.#getIndex(column); - if (column == null) { - console.error(`ColumnPreferencesController: could not find ${selector}`); - return; - } - - // Hide column definition - this.#showHideElement(column, element.checked); - - // Hide each cell in column (ignore rows with colspan) - const rows = this.table.querySelectorAll("tr:not(:has(td[colspan]))"); - rows.forEach((row) => { - // Ignore cell if spanning multiple columns - const cell = row.children[index]; - if (cell == undefined) return; - - this.#showHideElement(cell, element.checked); - }); + this.table.classList.toggle(`hide-${name}`, !element.checked); // Reset cell colspans - const hiddenColCount = this.cols.filter((col)=> col.style.display == 'none').length; + const hiddenColCount = this.checkboxes.filter((checkbox)=> !checkbox.checked).length; for(const cell of this.colSpanCells) { const span = parseInt(cell.dataset.defaultColSpan, 10) - hiddenColCount; cell.colSpan = span; }; } - #getIndex(column) { - return Array.from(column.parentNode.children).indexOf(column); - } - #showHideElement(element, show) { element.style.display = show ? "" : "none"; } diff --git a/app/webpacker/css/admin/products_v3.scss b/app/webpacker/css/admin/products_v3.scss index 646c2d074b..576e6d8af1 100644 --- a/app/webpacker/css/admin/products_v3.scss +++ b/app/webpacker/css/admin/products_v3.scss @@ -96,7 +96,8 @@ // "Naked" inputs. Row hover helps reveal them. .naked_inputs { - input:not([type="checkbox"]), .ts-control { + input:not([type="checkbox"]), + .ts-control { background-color: $color-tbl-cell-bg; } } @@ -179,6 +180,18 @@ .ts-control { z-index: 0; // Avoid hovering over thead } + + // Hide columns + $columns: + "image", "name", "sku", "unit_scale", "unit", "price", "on_hand", "producer", "category", + "tax_category", "inherits_properties"; + @each $col in $columns { + &.hide-#{$col} { + .col-#{$col} { + display: none; + } + } + } } #no-products { @@ -378,7 +391,7 @@ border-radius: $border-radius; box-shadow: 0px 0px 8px 0px rgba($near-black, 0.25); - .field{ + .field { margin-bottom: 0.75em; &:last-child { @@ -432,7 +445,7 @@ opacity: 0; } } - + .slide-out { animation: slideOutLeft 0.5s forwards; }