Change unit base with drag and drop

This commit is contained in:
cryptogopher 2024-03-24 17:38:56 +01:00
parent 7387e7b1c1
commit 05b0c66216
6 changed files with 56 additions and 4 deletions

View File

@ -372,6 +372,9 @@ table.items td.actions {
gap: 0.4em; gap: 0.4em;
justify-content: end; justify-content: end;
} }
table.items td.handle {
cursor: move;
}
/* TODO: replace :hover:focus-visible combos with proper LOVE stye order */ /* TODO: replace :hover:focus-visible combos with proper LOVE stye order */
/* TODO: Update styling, including rem removal. */ /* TODO: Update styling, including rem removal. */

View File

@ -9,7 +9,7 @@ class UnitsController < ApplicationController
end end
def index def index
@units = current_user.units @units = current_user.units.includes(:subunits)
end end
def new def new

View File

@ -2,6 +2,7 @@ class Unit < ApplicationRecord
belongs_to :user, optional: true belongs_to :user, optional: true
# TODO: validate base.user == user # TODO: validate base.user == user
belongs_to :base, optional: true, class_name: "Unit" 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}, validates :symbol, presence: true, uniqueness: {scope: :user_id},
length: {maximum: columns_hash['symbol'].limit} length: {maximum: columns_hash['symbol'].limit}
@ -18,11 +19,16 @@ class Unit < ApplicationRecord
'COALESCE', 'COALESCE',
[Arel::Table.new(:bases_units)[:symbol], arel_table[:symbol]] [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 before_destroy do
# TODO: disallow destruction if any object depends on this unit # TODO: disallow destruction if any object depends on this unit
nil nil
end end
def movable?
subunits.empty?
end
end end

View File

@ -1,4 +1,6 @@
<tr id="<%= dom_id(unit) %>"> <%= 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 %>
<td class="<%= class_names('link', {subunit: unit.base}) %>"> <td class="<%= class_names('link', {subunit: unit.base}) %>">
<%= link_to unit.symbol, edit_unit_path(unit), id: dom_id(unit, :edit), <%= link_to unit.symbol, edit_unit_path(unit), id: dom_id(unit, :edit),
onclick: 'this.blur();', data: {turbo_stream: true} %> onclick: 'this.blur();', data: {turbo_stream: true} %>
@ -17,5 +19,10 @@
<%= image_button_to t(".delete_unit"), "delete-outline", unit_path(unit), <%= image_button_to t(".delete_unit"), "delete-outline", unit_path(unit),
method: :delete %> method: :delete %>
</td> </td>
<% if unit.movable? %>
<td class="handle" draggable="true" ondragstart="dragStart(event)">&#x283F</td>
<% else %>
<td></td>
<% end %>
<% end %> <% end %>
</tr> <% end %>

View File

@ -15,6 +15,7 @@
<th><%= User.human_attribute_name(:multiplier).capitalize %></th> <th><%= User.human_attribute_name(:multiplier).capitalize %></th>
<% if current_user.at_least(:active) %> <% if current_user.at_least(:active) %>
<th><%= t :actions %></th> <th><%= t :actions %></th>
<th></th>
<% end %> <% end %>
</tr> </tr>
</thead> </thead>
@ -29,4 +30,36 @@
event.target.closest("tr").querySelector("a[name=cancel]").click(); 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 %> <% end %>

View File

@ -22,6 +22,9 @@ en:
actioncontroller: actioncontroller:
exceptions: exceptions:
status: status:
bad_request: >
Server received request it's unable to understand (400 Bad Request).
This should not happen, please notify site administrator.
forbidden: > forbidden: >
You have not been granted access to this action (403 Forbidden). You have not been granted access to this action (403 Forbidden).
This should not happen, please notify site administrator. This should not happen, please notify site administrator.