diff --git a/src/lang/ar-ye.json b/src/lang/ar-ye.json index 079faa8e5..0d438e90b 100644 --- a/src/lang/ar-ye.json +++ b/src/lang/ar-ye.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/be-by.json b/src/lang/be-by.json index 798a5f1f3..a5d332699 100644 --- a/src/lang/be-by.json +++ b/src/lang/be-by.json @@ -488,5 +488,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/bn-bd.json b/src/lang/bn-bd.json index 7e99886ac..95d8086b2 100644 --- a/src/lang/bn-bd.json +++ b/src/lang/bn-bd.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/cs-cz.json b/src/lang/cs-cz.json index 8d64b5fde..518986169 100644 --- a/src/lang/cs-cz.json +++ b/src/lang/cs-cz.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Neplatný formát klávesových zkratek", "backup invalid plugins": "Neplatný formát instalovaných pluginů", "issues found": "Nalezené problémy", - "error details": "Podrobnosti o chybě" + "error details": "Podrobnosti o chybě", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/de-de.json b/src/lang/de-de.json index 81e5c0fb6..13affaaea 100644 --- a/src/lang/de-de.json +++ b/src/lang/de-de.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/en-us.json b/src/lang/en-us.json index 6dbb8e75c..0298be386 100644 --- a/src/lang/en-us.json +++ b/src/lang/en-us.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/es-sv.json b/src/lang/es-sv.json index 43483cc4c..7e8307fc7 100644 --- a/src/lang/es-sv.json +++ b/src/lang/es-sv.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/fr-fr.json b/src/lang/fr-fr.json index d05d5007b..3496cba20 100644 --- a/src/lang/fr-fr.json +++ b/src/lang/fr-fr.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/he-il.json b/src/lang/he-il.json index 62bacbda6..f62b5a376 100644 --- a/src/lang/he-il.json +++ b/src/lang/he-il.json @@ -488,5 +488,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/hi-in.json b/src/lang/hi-in.json index 95fbee698..240a114b9 100644 --- a/src/lang/hi-in.json +++ b/src/lang/hi-in.json @@ -488,5 +488,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/hu-hu.json b/src/lang/hu-hu.json index cc654b643..44e1d2ecf 100644 --- a/src/lang/hu-hu.json +++ b/src/lang/hu-hu.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Érvénytelen a billentyűparancsok formátuma", "backup invalid plugins": "Érvénytelen a telepített bővítmények formátuma", "issues found": "Problémák találhatók", - "error details": "Hiba részletei" + "error details": "Hiba részletei", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/id-id.json b/src/lang/id-id.json index bbcbc5785..c447d0915 100644 --- a/src/lang/id-id.json +++ b/src/lang/id-id.json @@ -488,5 +488,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/ir-fa.json b/src/lang/ir-fa.json index 99274c11a..fcc3340c0 100644 --- a/src/lang/ir-fa.json +++ b/src/lang/ir-fa.json @@ -488,5 +488,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/it-it.json b/src/lang/it-it.json index f1960a29c..84ba1daec 100644 --- a/src/lang/it-it.json +++ b/src/lang/it-it.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/ja-jp.json b/src/lang/ja-jp.json index 348fbc91c..0abb0e59f 100644 --- a/src/lang/ja-jp.json +++ b/src/lang/ja-jp.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/ko-kr.json b/src/lang/ko-kr.json index 185b7aea6..0c2fbec45 100644 --- a/src/lang/ko-kr.json +++ b/src/lang/ko-kr.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/ml-in.json b/src/lang/ml-in.json index 5d0c233b5..0c2c8ebb8 100644 --- a/src/lang/ml-in.json +++ b/src/lang/ml-in.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/mm-unicode.json b/src/lang/mm-unicode.json index 175260b7b..8e9aeedad 100644 --- a/src/lang/mm-unicode.json +++ b/src/lang/mm-unicode.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/mm-zawgyi.json b/src/lang/mm-zawgyi.json index c310c6a1f..b22ebf425 100644 --- a/src/lang/mm-zawgyi.json +++ b/src/lang/mm-zawgyi.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/pl-pl.json b/src/lang/pl-pl.json index 9059bc685..6ec2c2bb5 100644 --- a/src/lang/pl-pl.json +++ b/src/lang/pl-pl.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/pt-br.json b/src/lang/pt-br.json index a0a33ccb3..7cbfc6ceb 100644 --- a/src/lang/pt-br.json +++ b/src/lang/pt-br.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/pu-in.json b/src/lang/pu-in.json index dbf06e1eb..0f6964396 100644 --- a/src/lang/pu-in.json +++ b/src/lang/pu-in.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/ru-ru.json b/src/lang/ru-ru.json index 5c2354729..0ef4ad23a 100644 --- a/src/lang/ru-ru.json +++ b/src/lang/ru-ru.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/tl-ph.json b/src/lang/tl-ph.json index d87e885c9..8fcf73751 100644 --- a/src/lang/tl-ph.json +++ b/src/lang/tl-ph.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/tr-tr.json b/src/lang/tr-tr.json index 99e985945..98ea05edd 100644 --- a/src/lang/tr-tr.json +++ b/src/lang/tr-tr.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/uk-ua.json b/src/lang/uk-ua.json index 446fad06a..259a22b1d 100644 --- a/src/lang/uk-ua.json +++ b/src/lang/uk-ua.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/uz-uz.json b/src/lang/uz-uz.json index 656f6ea11..04b020051 100644 --- a/src/lang/uz-uz.json +++ b/src/lang/uz-uz.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/vi-vn.json b/src/lang/vi-vn.json index a9144adc5..fad4d1d60 100644 --- a/src/lang/vi-vn.json +++ b/src/lang/vi-vn.json @@ -488,5 +488,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/zh-cn.json b/src/lang/zh-cn.json index 59592447f..327a1b983 100644 --- a/src/lang/zh-cn.json +++ b/src/lang/zh-cn.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/zh-hant.json b/src/lang/zh-hant.json index 51ab570ae..8c22a71da 100644 --- a/src/lang/zh-hant.json +++ b/src/lang/zh-hant.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/lang/zh-tw.json b/src/lang/zh-tw.json index 21f42f67a..19b3fda1e 100644 --- a/src/lang/zh-tw.json +++ b/src/lang/zh-tw.json @@ -487,5 +487,7 @@ "backup invalid keybindings": "Invalid keyBindings format", "backup invalid plugins": "Invalid installedPlugins format", "issues found": "Issues found", - "error details": "Error details" + "error details": "Error details", + "active tools": "Active tools", + "available tools": "Available tools" } diff --git a/src/pages/quickTools/quickTools.js b/src/pages/quickTools/quickTools.js index 81c4c7b1b..02022af9c 100644 --- a/src/pages/quickTools/quickTools.js +++ b/src/pages/quickTools/quickTools.js @@ -1,16 +1,27 @@ import "./style.scss"; import Page from "components/page"; import items, { description } from "components/quickTools/items"; -import WCPage from "components/WebComponents/wcPage"; -import select from "dialogs/select"; import actionStack from "lib/actionStack"; import settings from "lib/settings"; import helpers from "utils/helpers"; export default function QuickTools() { const $page = Page(strings["shortcut buttons"]); - render($page); - $page.addEventListener("click", clickHandler); + $page.id = "quicktools-settings-page"; + $page.style.overflow = "hidden"; + $page.style.display = "flex"; + $page.style.flexDirection = "column"; + + const manager = new QuickToolsManager(); + $page.body = manager.getContainer(); + + const onShow = $page.onshow; + $page.onshow = function () { + if (onShow) onShow.call(this); + const scrollContainer = $page.get(".scroll-container") || $page; + scrollContainer.style.overflow = "hidden"; + manager.getContainer().style.height = "100%"; + }; actionStack.push({ id: "quicktools-settings", @@ -20,72 +31,370 @@ export default function QuickTools() { $page.onhide = () => { actionStack.remove("quicktools-settings"); helpers.hideAd(); + // Cleanup manager + manager.destroy(); }; app.append($page); helpers.showAd(); } -/** - * Render the page - * @param {WCPage} $page - */ -function render($page) { - $page.body = ( -
- {(() => { - const totalRows = settings.QUICKTOOLS_ROWS * settings.QUICKTOOLS_GROUPS; - const limit = settings.QUICKTOOLS_GROUP_CAPACITY; - const rows = []; - for (let i = 0; i < totalRows; i++) { - const row = []; - for (let j = 0; j < limit; j++) { - const count = i * limit + j; - const index = settings.value.quicktoolsItems[count]; - row.push(); - } - rows.push(
{row}
); +class QuickToolsManager { + constructor() { + this.container =
; + this.render(); + this.bindEvents(); + + this.longPressTimer = null; + this.dragState = null; + } + + getContainer() { + return this.container; + } + + render() { + this.destroy(); // Cleanup potential drag states before re-rendering + this.container.textContent = ""; + + // --- Active Tools Section --- + const activeSection =
; + activeSection.appendChild( +
{strings["active tools"]}
, + ); + + const activeGrid =
; + + const totalSlots = + settings.QUICKTOOLS_ROWS * + settings.QUICKTOOLS_GROUPS * + settings.QUICKTOOLS_GROUP_CAPACITY; + + for (let i = 0; i < totalSlots; i++) { + const itemIndex = settings.value.quicktoolsItems[i]; + const itemDef = items[itemIndex]; + const el = this.createItemElement(itemDef, i, "active"); + activeGrid.appendChild(el); + } + + activeSection.appendChild(activeGrid); + this.container.appendChild(activeSection); + + // --- Available Tools Section --- + const availableSection =
; + availableSection.appendChild( +
{strings["available tools"]}
, + ); + + // Group items + const categories = { + Modifiers: ["ctrl", "shift", "alt", "meta"], + Commands: ["command", "undo", "redo", "save", "search"], + Navigation: ["key"], + Symbols: ["insert"], + Other: [], + }; + + const groupedItems = {}; + items.forEach((item, index) => { + let category = "Other"; + for (const [cat, actions] of Object.entries(categories)) { + if (actions.includes(item.action)) { + category = cat; + break; } + } + if (!groupedItems[category]) groupedItems[category] = []; + groupedItems[category].push({ item, index }); + }); - return rows; - })()} -
- ); -} + Object.entries(groupedItems).forEach(([category, list]) => { + const catHeader =
{category}
; + const catGrid =
; -/** - * Create a quicktools settings item - * @param {object} param0 - * @param {string} param0.icon - * @param {string} param0.letters - * @param {number} param0.index - * @returns - */ -function Item({ icon, letters, index }) { - return ( - - ); -} + list.forEach(({ item, index }) => { + const el = this.createItemElement(item, index, "source"); + catGrid.appendChild(el); + }); -/** - * Click handler for page - * @param {MouseEvent} e - */ -async function clickHandler(e) { - const index = Number.parseInt(e.target.dataset.index, 10); + availableSection.appendChild(catHeader); + availableSection.appendChild(catGrid); + }); - if (isNaN(index)) return; + this.container.appendChild(availableSection); + } - const options = items.map(({ id, icon, letters }, i) => { - return [i, description(id), icon, true, letters]; - }); + createItemElement(itemDef, index, type) { + if (!itemDef) + return ( +
+ ); + + const hasIcon = itemDef.icon && itemDef.icon !== "letters"; + // If it's not an icon, we assume it relies on 'letters' + // Some items might have both, but 'letters' mode implies text rendering + + const el = ( +
+ {hasIcon ? : null} +
+ ); + return el; + } + + bindEvents() { + const c = this.container; + c.addEventListener("touchstart", this.handleTouchStart.bind(this), { + passive: false, + }); + c.addEventListener("touchmove", this.handleTouchMove.bind(this), { + passive: false, + }); + c.addEventListener("touchend", this.handleTouchEnd.bind(this)); + c.addEventListener("contextmenu", (e) => e.preventDefault()); + + c.addEventListener("mousedown", this.handleMouseDown.bind(this)); + } + + // --- Touch Handlers --- + + handleTouchStart(e) { + // If already dragging or pending, ignore new touches (prevent multi-touch mess) + if (this.dragState || this.longPressTimer) return; + + const target = e.target.closest(".tool-item"); + if (!target) return; + + this.longPressTimer = setTimeout(() => { + this.startDrag(target, e.touches[0]); + }, 300); + + this.touchStartX = e.touches[0].clientX; + this.touchStartY = e.touches[0].clientY; + this.potentialTarget = target; + } + + handleTouchMove(e) { + const touch = e.touches[0]; + if (this.dragState) { + e.preventDefault(); + this.updateDrag(touch); + return; + } + + if ( + Math.hypot( + touch.clientX - this.touchStartX, + touch.clientY - this.touchStartY, + ) > 10 + ) { + clearTimeout(this.longPressTimer); + this.longPressTimer = null; + this.potentialTarget = null; + } + } + + handleTouchEnd(e) { + clearTimeout(this.longPressTimer); + + if (this.dragState) { + this.endDrag(); + } else if (this.potentialTarget) { + // It was a tap + if (e.cancelable) e.preventDefault(); + this.handleClick(this.potentialTarget); + } + + this.potentialTarget = null; + } + + // --- Mouse Handlers --- + + handleMouseDown(e) { + const target = e.target.closest(".tool-item"); + if (!target) return; + + this.mouseDownInfo = { + target, + x: e.clientX, + y: e.clientY, + isDrag: false, + }; + + const moveHandler = (ev) => { + if (!this.mouseDownInfo.isDrag) { + if ( + Math.hypot( + ev.clientX - this.mouseDownInfo.x, + ev.clientY - this.mouseDownInfo.y, + ) > 5 + ) { + this.mouseDownInfo.isDrag = true; + this.startDrag(target, this.mouseDownInfo); + } + } + if (this.dragState) { + this.updateDrag(ev); + } + }; + + const upHandler = () => { + document.removeEventListener("mousemove", moveHandler); + document.removeEventListener("mouseup", upHandler); + + if (this.dragState) { + this.endDrag(); + } else { + this.handleClick(target); + } + }; + + document.addEventListener("mousemove", moveHandler); + document.addEventListener("mouseup", upHandler); + } + + // --- Core Drag Logic --- + + startDrag(el, pointer) { + // Double check state + if (this.dragState) { + this.destroy(); + return; + } + + if (navigator.vibrate) navigator.vibrate(30); + + const rect = el.getBoundingClientRect(); + const ghost = el.cloneNode(true); + ghost.classList.add("tool-ghost"); + ghost.style.width = rect.width + "px"; + ghost.style.height = rect.height + "px"; + + document.body.appendChild(ghost); + el.classList.add("dragging"); + + const type = el.dataset.type; + const index = Number.parseInt(el.dataset.index, 10); + + this.dragState = { + el, + type, // 'active' or 'source' + index, // slot index (active) or item ID (source) + ghost, + offsetX: pointer.clientX - rect.left - rect.width / 2, + offsetY: pointer.clientY - rect.top - rect.height / 2, + }; + + this.updateDrag(pointer); + } + + updateDrag(pointer) { + const { ghost } = this.dragState; + ghost.style.left = pointer.clientX + "px"; + ghost.style.top = pointer.clientY + "px"; + + const elementBelow = document.elementFromPoint( + pointer.clientX, + pointer.clientY, + ); + + this.cleanupHighlight(); + + const targetItem = elementBelow?.closest(".tool-item"); + if (targetItem && targetItem.dataset.type === "active") { + targetItem.classList.add("highlight-target"); + this.dragState.dropTarget = targetItem; + } else { + this.dragState.dropTarget = null; + } + } + + cleanupHighlight() { + const highlighted = this.container.querySelectorAll(".highlight-target"); + highlighted.forEach((el) => el.classList.remove("highlight-target")); + } + + endDrag() { + const { el, ghost, dropTarget, type, index } = this.dragState; + + this.cleanupHighlight(); + el.classList.remove("dragging"); + ghost.remove(); + this.dragState = null; + + if (dropTarget) { + const targetIndex = Number.parseInt(dropTarget.dataset.index, 10); + + if (type === "active") { + // Swap within active + if (targetIndex !== index) { + this.swapItems(index, targetIndex); + } + } else if (type === "source") { + // Replace active slot with source item + this.replaceItem(targetIndex, index); + } + } + } + + swapItems(srcIndex, destIndex) { + const temp = settings.value.quicktoolsItems[srcIndex]; + settings.value.quicktoolsItems[srcIndex] = + settings.value.quicktoolsItems[destIndex]; + settings.value.quicktoolsItems[destIndex] = temp; + + settings.update(); + this.render(); // Re-render to reflect changes + } + + replaceItem(slotIndex, newItemId) { + settings.value.quicktoolsItems[slotIndex] = newItemId; + settings.update(); + this.render(); + } + + async handleClick(el) { + const type = el.dataset.type; + const index = Number.parseInt(el.dataset.index, 10); + + let itemDef; + if (type === "active") { + const itemIndex = settings.value.quicktoolsItems[index]; + itemDef = items[itemIndex]; + } else { + itemDef = items[index]; + } + + if (itemDef) { + const desc = description(itemDef.id); + window.toast(desc, 2000); + } + } + + destroy() { + if (this.longPressTimer) clearTimeout(this.longPressTimer); + this.longPressTimer = null; + + if (this.dragState) { + if (this.dragState.ghost) { + this.dragState.ghost.remove(); + } + if (this.dragState.el) { + this.dragState.el.classList.remove("dragging"); + } + } - const i = await select(strings.select, options); - settings.value.quicktoolsItems[index] = i; - settings.update(); - render(this); + this.cleanupHighlight(); + this.dragState = null; + this.potentialTarget = null; + } } diff --git a/src/pages/quickTools/style.scss b/src/pages/quickTools/style.scss index a807f0450..674a19326 100644 --- a/src/pages/quickTools/style.scss +++ b/src/pages/quickTools/style.scss @@ -1,19 +1,158 @@ #quicktools-settings { display: flex; flex-direction: column; + height: 100%; + width: 100%; + overflow-y: hidden; + box-sizing: border-box; + background-color: var(--primary-color); - .row { - height: 40px; + .section-title { + font-size: 1rem; + font-weight: bold; + padding: 10px; + color: var(--text-color); + opacity: 0.8; + background-color: var(--primary-color); + z-index: 10; + width: 100%; + display: block; + box-sizing: border-box; + } + + .section { + display: flex; + flex-direction: column; + width: 100%; + box-sizing: border-box; + } + + .section.active-tools { + flex: 0 0 auto; + border-bottom: 3px dashed var(--border-color); + padding-bottom: 10px; + margin-bottom: 15px; + padding-left: 10px; + padding-right: 10px; + } + + .section.available-tools { + flex: 1 1 auto; + overflow-y: auto; + padding: 0 10px 10px 10px; + + .category-header { + font-size: 0.8rem; + text-transform: uppercase; + opacity: 0.6; + margin-top: 15px; + margin-bottom: 5px; + margin-left: 5px; + font-weight: bold; + display: block; + width: 100%; + } + } + + .quicktools-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); + gap: 8px; + width: 100%; + padding-bottom: 5px; + padding-top: 5px; + + &.active-grid { + grid-template-columns: repeat(8, 1fr); + min-height: auto; + margin-bottom: 5px; + } + } + + .tool-item { + background-color: var(--secondary-color); + color: var(--text-color); + border-radius: 8px; + aspect-ratio: 1; display: flex; - flex-direction: row; + align-items: center; + justify-content: center; + font-size: 1rem; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease, background-color 0.2s; + user-select: none; + -webkit-user-select: none; + cursor: grab; + position: relative; + border: 1px solid transparent; + border-bottom: 2px dashed var(--border-color); + + &:active { + transform: scale(0.95); + cursor: grabbing; + } + + &.dragging { + opacity: 0.3; + transform: scale(0.9); + border-color: var(--active-color); + } + + &.highlight-target { + border-color: var(--active-color); + background-color: rgba(0, 0, 0, 0.1); + transform: scale(1.05); + } + + &.empty { + background-color: transparent; + border: 2px dashed rgba(0, 0, 0, 0.1); + box-shadow: none; + } .icon { - flex: 1; + font-size: 1.2rem; + pointer-events: none; + } - &:active { - background-color: inherit !important; - color: var(--active-color); - } + &.has-letters::before { + content: attr(data-letters); + font-size: 0.9rem; + font-weight: bold; + text-transform: uppercase; } + + &.has-icon::before { + display: none; + } + } +} + +.tool-ghost { + position: fixed; + top: 0; + left: 0; + width: 60px; + height: 60px; + background-color: var(--active-color); + color: #fff; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + pointer-events: none; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); + transform: translate(-50%, -50%) scale(1.1); + + .icon { + font-size: 1.5rem; + } + + &.has-letters::before { + content: attr(data-letters); + font-size: 1rem; + font-weight: bold; + text-transform: uppercase; } } \ No newline at end of file