From 05b0c6621608833ca76a55b227b36abb6770f0e2 Mon Sep 17 00:00:00 2001 From: cryptogopher Date: Sun, 24 Mar 2024 17:38:56 +0100 Subject: [PATCH] Change unit base with drag and drop --- app/assets/stylesheets/application.css | 3 +++ app/controllers/units_controller.rb | 2 +- app/models/unit.rb | 8 ++++++- app/views/units/_unit.html.erb | 11 +++++++-- app/views/units/index.html.erb | 33 ++++++++++++++++++++++++++ config/locales/en.yml | 3 +++ 6 files changed, 56 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index cf5d921..0dcc16b 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -372,6 +372,9 @@ table.items td.actions { gap: 0.4em; justify-content: end; } +table.items td.handle { + cursor: move; +} /* TODO: replace :hover:focus-visible combos with proper LOVE stye order */ /* TODO: Update styling, including rem removal. */ diff --git a/app/controllers/units_controller.rb b/app/controllers/units_controller.rb index bae6949..b6c60a0 100644 --- a/app/controllers/units_controller.rb +++ b/app/controllers/units_controller.rb @@ -9,7 +9,7 @@ class UnitsController < ApplicationController end def index - @units = current_user.units + @units = current_user.units.includes(:subunits) end def new diff --git a/app/models/unit.rb b/app/models/unit.rb index 8764a43..3ab7682 100644 --- a/app/models/unit.rb +++ b/app/models/unit.rb @@ -2,6 +2,7 @@ class Unit < ApplicationRecord belongs_to :user, optional: true # TODO: validate base.user == user belongs_to :base, optional: true, class_name: "Unit" + has_many :subunits, class_name: "Unit", dependent: :restrict_with_error, inverse_of: :base validates :symbol, presence: true, uniqueness: {scope: :user_id}, length: {maximum: columns_hash['symbol'].limit} @@ -18,11 +19,16 @@ class Unit < ApplicationRecord 'COALESCE', [Arel::Table.new(:bases_units)[:symbol], arel_table[:symbol]] ) - left_outer_joins(:base).order(parent_symbol, arel_table[:base_id].asc.nulls_first, :multiplier) + left_outer_joins(:base) + .order(parent_symbol, arel_table[:base_id].asc.nulls_first, :multiplier, :symbol) } before_destroy do # TODO: disallow destruction if any object depends on this unit nil end + + def movable? + subunits.empty? + end end diff --git a/app/views/units/_unit.html.erb b/app/views/units/_unit.html.erb index 754a43b..5ac00eb 100644 --- a/app/views/units/_unit.html.erb +++ b/app/views/units/_unit.html.erb @@ -1,4 +1,6 @@ - +<%= tag.tr id: dom_id(unit), ondragover: "dragOver(event)", ondrop: "drop(event)", + data: {drag_path: unit_path(unit), drop_id: unit.base_id || unit.id} do %> + <%= link_to unit.symbol, edit_unit_path(unit), id: dom_id(unit, :edit), onclick: 'this.blur();', data: {turbo_stream: true} %> @@ -17,5 +19,10 @@ <%= image_button_to t(".delete_unit"), "delete-outline", unit_path(unit), method: :delete %> + <% if unit.movable? %> + ⠿ + <% else %> + + <% end %> <% end %> - +<% end %> diff --git a/app/views/units/index.html.erb b/app/views/units/index.html.erb index 5ebd3e6..42ea279 100644 --- a/app/views/units/index.html.erb +++ b/app/views/units/index.html.erb @@ -15,6 +15,7 @@ <%= User.human_attribute_name(:multiplier).capitalize %> <% if current_user.at_least(:active) %> <%= t :actions %> + <% end %> @@ -29,4 +30,36 @@ event.target.closest("tr").querySelector("a[name=cancel]").click(); } } + + function dragStart(event) { + var row = event.target.closest("tr"); + event.dataTransfer.setData("text/plain", row.getAttribute("data-drag-path")); + var rowRectangle = row.getBoundingClientRect(); + event.dataTransfer.setDragImage(row, event.x - rowRectangle.left, event.y - rowRectangle.top); + event.dataTransfer.dropEffect = "none"; + } + + function dragOver(event) { + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + } + + function drop(event) { + event.preventDefault(); + + var params = new URLSearchParams(); + params.append("unit[base_id]", event.target.closest("tr").getAttribute("data-drop-id")); + + fetch(event.dataTransfer.getData("text/plain"), { + body: params, + headers: { + "Accept": "text/vnd.turbo-stream.html", + "X-CSRF-Token": document.head.querySelector("meta[name=csrf-token]").content, + "X-Requested-With": "XMLHttpRequest" + }, + method: "PATCH" + }) + .then(response => response.text()) + .then(html => Turbo.renderStreamMessage(html)) + } <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index a2dd6a6..8710f5b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -22,6 +22,9 @@ en: actioncontroller: exceptions: status: + bad_request: > + Server received request it's unable to understand (400 Bad Request). + This should not happen, please notify site administrator. forbidden: > You have not been granted access to this action (403 Forbidden). This should not happen, please notify site administrator.