feat: add data-tauri-drag-region="deep" (#15062)

* feat: add `data-tauri-drag-region="deep"`

supersedes #6362

* Update data-tauri-drag-region-deep.md

* summary is also clickable

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
Amr Bashir
2026-03-10 14:25:41 +02:00
committed by GitHub
parent eacd36a4ea
commit 2dd9b15a2b
2 changed files with 68 additions and 17 deletions

View File

@@ -0,0 +1,5 @@
---
"tauri": minor:feat
---
Add `data-tauri-drag-region="deep"` so clicks on non-clickable children will drag as well. Can still opt out of drag on some regions using `data-tauri-drag-region="false"`

View File

@@ -3,30 +3,78 @@
// SPDX-License-Identifier: MIT
;(function () {
const osName = __TEMPLATE_os_name__
//-----------------------//
// drag on mousedown and maximize on double click on Windows and Linux
// while macOS macos maximization should be on mouseup and if the mouse
// while macOS maximization should be on mouseup and if the mouse
// moves after the double click, it should be cancelled (see https://github.com/tauri-apps/tauri/issues/8306)
//-----------------------//
const TAURI_DRAG_REGION_ATTR = 'data-tauri-drag-region'
function isClickableElement(el) {
const tag = el.tagName && el.tagName.toLowerCase()
return (
tag === 'a'
|| tag === 'button'
|| tag === 'input'
|| tag === 'select'
|| tag === 'textarea'
|| tag === 'label'
|| tag === 'summary'
|| (el.hasAttribute('contenteditable')
&& el.getAttribute('contenteditable') !== 'false')
|| (el.hasAttribute('tabindex') && el.getAttribute('tabindex') !== '-1')
)
}
// Walk the composed path from target upward. If a clickable element or a
// data-tauri-drag-region="false" element is encountered, return false (don't drag).
// Otherwise return true.
//
// Supported values for data-tauri-drag-region:
// (bare / no value) → self: only direct clicks on this element trigger drag
// "deep" → deep: clicks anywhere in the subtree trigger drag
// "false" → disabled: drag is blocked here (and for ancestors)
function isDragRegion(composedPath) {
for (const el of composedPath) {
if (!(el instanceof HTMLElement)) continue
// if we hit a clickable element or a disabled drag region, don't drag
if (
isClickableElement(el)
|| el.getAttribute(TAURI_DRAG_REGION_ATTR) === 'false'
) {
return false
}
const attr = el.getAttribute(TAURI_DRAG_REGION_ATTR)
if (attr !== null) {
// deep: the whole subtree is a drag region
if (attr === 'deep') return true
// bare (or any unrecognized value): self-only
if (el === composedPath[0]) return true
// click was on a child of a self-only region — stop walking, don't drag
return false
}
}
return false
}
const osName = __TEMPLATE_os_name__
// initial mousedown position for macOS
let initialX = 0
let initialY = 0
document.addEventListener('mousedown', (e) => {
const attr = e.target.getAttribute(TAURI_DRAG_REGION_ATTR)
if (
// element has the magic data attribute
attr !== null
// and not false
&& attr !== 'false'
// and was left mouse button
&& e.button === 0
// was left mouse button
e.button === 0
// and was normal click to drag or double click to maximize
&& (e.detail === 1 || e.detail === 2)
// and is drag region
&& isDragRegion(e.composedPath())
) {
// macOS maximization happens on `mouseup`,
// so we save needed state and early return
@@ -48,23 +96,21 @@
window.__TAURI_INTERNALS__.invoke('plugin:window|' + cmd)
}
})
// on macOS we maximize on mouseup instead, to match the system behavior where maximization can be canceled
// if the mouse moves outside the data-tauri-drag-region
if (osName === 'macos') {
document.addEventListener('mouseup', (e) => {
const attr = e.target.getAttribute(TAURI_DRAG_REGION_ATTR)
if (
// element has the magic data attribute
attr !== null
// and not false
&& attr !== 'false'
// and was left mouse button
&& e.button === 0
// was left mouse button
e.button === 0
// and was double click
&& e.detail === 2
// and the cursor hasn't moved from initial mousedown
&& e.clientX === initialX
&& e.clientY === initialY
// and the event path contains a drag region (with no clickable element in between)
&& isDragRegion(e.composedPath())
) {
window.__TAURI_INTERNALS__.invoke(
'plugin:window|internal_toggle_maximize'