plz no lookie shit

This commit is contained in:
End Nightshade 2026-02-10 13:52:42 -07:00
parent cec596e620
commit 6567d71ce8
No known key found for this signature in database
3 changed files with 77 additions and 43 deletions

View file

@ -26,7 +26,7 @@ class Components::Uploads::Row < Components::Base
def compact_content
div(style: "flex: 1; min-width: 0;") do
div(style: "font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;") do
div(style: "font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;", data: { batch_select_toggle: upload.id }) do
render Primer::Beta::Octicon.new(icon: file_icon_for(upload.content_type), size: :small, mr: 1)
plain upload.filename.to_s
end
@ -50,7 +50,7 @@ class Components::Uploads::Row < Components::Base
div(style: "flex: 1; min-width: 0;") do
div(style: "display: flex; align-items: center; gap: 8px; margin-bottom: 8px; min-width: 0;") do
render Primer::Beta::Octicon.new(icon: file_icon_for(upload.content_type), size: :small)
div(style: "flex: 1; min-width: 0; font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;") do
div(style: "flex: 1; min-width: 0; font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;", data: { batch_select_toggle: upload.id }) do
plain upload.filename.to_s
end
render(Primer::Beta::Label.new(scheme: :secondary)) { plain upload.provenance.titleize }
@ -99,20 +99,13 @@ class Components::Uploads::Row < Components::Base
def file_icon_for(content_type)
case content_type
when /image/
:image
when /video/
:video
when /audio/
:unmute
when /pdf/
:file
when /zip|rar|tar|gz/
:"file-zip"
when /text|json|xml/
:code
else
:file
when /image/ then :image
when /video/ then :video
when /audio/ then :unmute
when /pdf/ then :file
when /zip|rar|tar|gz/ then :"file-zip"
when /text|json|xml/ then :code
else :file
end
end
end

View file

@ -66,7 +66,6 @@ class Components::Uploads::Index < Components::Base
def uploads_list
if uploads.any?
# Select all toggle
div(style: "display: flex; align-items: center; gap: 8px; margin-bottom: 8px; padding: 4px 0;") do
input(
type: "checkbox",
@ -88,8 +87,7 @@ class Components::Uploads::Index < Components::Base
name: "ids[]",
value: upload.id,
form: "batch-delete-form",
class: "batch-select-checkbox",
data: { batch_select_item: true },
data: { batch_select_item: true, upload_id: upload.id },
style: "margin-top: 6px; cursor: pointer;"
)
div(style: "flex: 1; min-width: 0;") do
@ -133,16 +131,15 @@ class Components::Uploads::Index < Components::Base
end
def batch_delete_bar
div(id: "batch-delete-bar", data: { batch_bar: true }, style: "display: none; position: fixed; bottom: 0; left: 0; right: 0; background: var(--bgColor-danger-muted, #FFEBE9); border-top: 1px solid var(--borderColor-danger-muted, #ffcecb); padding: 12px 24px; z-index: 100;") do
div(style: "max-width: 1200px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center;") do
span(data: { batch_count: true }, style: "font-size: 14px; font-weight: 500;") { "0 files selected" }
div(style: "display: flex; gap: 8px;") do
button(type: "button", data: { batch_deselect: true }, class: "btn btn-sm") { "Deselect all" }
form_with url: destroy_batch_uploads_path, method: :delete, id: "batch-delete-form", data: { turbo_confirm: "Are you sure you want to delete the selected files? This cannot be undone." } do
button(type: "submit", class: "btn btn-sm btn-danger") do
render Primer::Beta::Octicon.new(icon: :trash, mr: 1)
plain "Delete selected"
end
div(id: "batch-delete-bar", data: { batch_bar: true }, style: "display: none; position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); background: var(--bgColor-default, #fff); border: 1px solid var(--borderColor-default, #d0d7de); border-radius: 12px; padding: 12px 20px; z-index: 100; box-shadow: 0 8px 24px rgba(0,0,0,0.12); min-width: 320px; max-width: 600px;") do
div(style: "display: flex; align-items: center; gap: 16px;") do
span(data: { batch_count: true }, style: "font-size: 14px; font-weight: 600; white-space: nowrap;") { "0 selected" }
div(style: "flex: 1;")
button(type: "button", data: { batch_deselect: true }, class: "btn btn-sm", style: "white-space: nowrap;") { "Deselect" }
form_with url: destroy_batch_uploads_path, method: :delete, id: "batch-delete-form", data: { batch_delete_form: true } do
button(type: "submit", class: "btn btn-sm btn-danger", style: "white-space: nowrap;") do
render Primer::Beta::Octicon.new(icon: :trash, mr: 1)
plain "Delete"
end
end
end

View file

@ -4,6 +4,7 @@
const bar = document.querySelector("[data-batch-bar]");
const countLabel = document.querySelector("[data-batch-count]");
const deselectBtn = document.querySelector("[data-batch-deselect]");
const deleteForm = document.querySelector("[data-batch-delete-form]");
if (!bar) return;
@ -11,20 +12,19 @@
return document.querySelectorAll("[data-batch-select-item]");
}
function getChecked() {
return Array.from(getCheckboxes()).filter((cb) => cb.checked);
}
function updateBar() {
const checkboxes = getCheckboxes();
const checked = Array.from(checkboxes).filter((cb) => cb.checked);
const checked = getChecked();
const count = checked.length;
if (count > 0) {
bar.style.display = "block";
countLabel.textContent =
count + (count === 1 ? " file selected" : " files selected");
} else {
bar.style.display = "none";
}
bar.style.display = count > 0 ? "block" : "none";
countLabel.textContent =
count + (count === 1 ? " file selected" : " files selected");
// Sync "select all" checkbox
if (selectAll) {
selectAll.checked =
checkboxes.length > 0 && checked.length === checkboxes.length;
@ -33,14 +33,29 @@
}
}
// Listen for individual checkbox changes
// Individual checkbox changes
document.addEventListener("change", (e) => {
if (e.target.matches("[data-batch-select-item]")) {
updateBar();
}
});
// Select all / deselect all toggle
// Click filename to toggle its checkbox
document.addEventListener("click", (e) => {
const toggle = e.target.closest("[data-batch-select-toggle]");
if (!toggle) return;
const uploadId = toggle.dataset.batchSelectToggle;
const checkbox = document.querySelector(
'[data-batch-select-item][data-upload-id="' + uploadId + '"]',
);
if (checkbox) {
checkbox.checked = !checkbox.checked;
updateBar();
}
});
// Select all toggle
if (selectAll) {
selectAll.addEventListener("change", () => {
const checkboxes = getCheckboxes();
@ -51,17 +66,46 @@
});
}
// Deselect all button in the bar
// Deselect all button
if (deselectBtn) {
deselectBtn.addEventListener("click", () => {
const checkboxes = getCheckboxes();
checkboxes.forEach((cb) => {
getCheckboxes().forEach((cb) => {
cb.checked = false;
});
if (selectAll) selectAll.checked = false;
updateBar();
});
}
// Confirmation before batch delete
if (deleteForm) {
deleteForm.addEventListener("submit", (e) => {
const checked = getChecked();
if (checked.length === 0) {
e.preventDefault();
return;
}
// Build a list of filenames from the row next to each checkbox
const names = checked.map((cb) => {
const row = cb.closest("[class*='BorderBox']") || cb.parentElement;
const nameEl = row.querySelector("[data-batch-select-toggle]");
return nameEl ? nameEl.textContent.trim() : cb.value;
});
const message =
"Delete " +
checked.length +
(checked.length === 1 ? " file" : " files") +
"?\n\n" +
names.map((n) => " • " + n).join("\n") +
"\n\nThis cannot be undone.";
if (!confirm(message)) {
e.preventDefault();
}
});
}
}
if (document.readyState === "loading") {