@@ -9,22 +9,30 @@ interface UseDragResizeOptions {
99 */
1010 cssVar : string
1111 /**
12- * Returns the element that consumes {@link cssVar} (or an ancestor of every
13- * consumer). During the drag the variable is written here — a style recalc
14- * scoped to that subtree — instead of on `:root`, where on a large document
15- * every custom-property write recalculates the whole tree (~150x slower).
16- * Captured once on drag start; a `null` return falls back to
17- * `document.documentElement`.
12+ * Returns the element the drag resizes, which is also the subtree
13+ * {@link cssVar} is written to during it — a style recalc scoped to that
14+ * subtree, instead of `:root`, where on a large document every
15+ * custom-property write recalculates the whole tree (~150x slower). Captured
16+ * once on drag start; a `null` return falls back to
17+ * `document.documentElement`. This element is also the drag's liveness
18+ * reference: once it detaches, the release stops recomputing from layout.
19+ */
20+ getTarget : ( ) => HTMLElement | null
21+ /**
22+ * Other subtrees that read {@link cssVar} but are not what the drag resizes —
23+ * the toast stack insets by `--panel-width`/`--terminal-height` yet is
24+ * portalled to `<body>`, so it shares no ancestor with either. Each is
25+ * written alongside the primary, which keeps the recalc scoped AND keeps
26+ * these consumers tracking the drag; one left off here reads the stale
27+ * `:root` value and only catches up when the drag commits.
1828 *
19- * Return an ARRAY when consumers live in sibling subtrees with no useful
20- * common ancestor — the toast stack is portalled to `<body>`, so it shares
21- * one only with `:root`. Writing each subtree separately keeps the scoped
22- * recalc and, more importantly, keeps those consumers tracking the drag live;
23- * a consumer left off this list reads the stale `:root` value and only
24- * catches up when the drag commits. Elements that are absent (`null`) or
25- * repeated are ignored.
29+ * Deliberately separate from {@link getTarget} rather than one list: these
30+ * come and go independently of the drag (a toast auto-dismisses mid-drag),
31+ * so they must never become the liveness reference. Absent (`null`) or
32+ * duplicate elements are ignored, and writing to one that detaches mid-drag
33+ * is harmless.
2634 */
27- getTarget : ( ) => HTMLElement | null | ( HTMLElement | null ) [ ]
35+ getExtraTargets ? : ( ) => ( HTMLElement | null ) [ ]
2836 /**
2937 * Maps a pointer position to the clamped target dimension, or `null` to
3038 * ignore the move. Runs at most once per animation frame (before the write,
@@ -98,13 +106,9 @@ export function useDragResize(options: UseDragResizeOptions) {
98106 const handle = e . currentTarget
99107 const pointerId = e . pointerId
100108 const { cssVar } = optionsRef . current
101- const resolved = optionsRef . current . getTarget ( )
102- const targets = [
103- ...new Set ( ( Array . isArray ( resolved ) ? resolved : [ resolved ] ) . filter ( ( el ) => el !== null ) ) ,
104- ]
105- if ( targets . length === 0 ) targets . push ( document . documentElement )
106- /** Liveness is judged on the primary target — the one the drag resizes. */
107- const target = targets [ 0 ]
109+ const target = optionsRef . current . getTarget ( ) ?? document . documentElement
110+ const extras = optionsRef . current . getExtraTargets ?.( ) ?? [ ]
111+ const targets = [ ...new Set ( [ target , ...extras . filter ( ( el ) => el !== null ) ] ) ]
108112 document . body . style . cursor = optionsRef . current . cursor
109113 document . body . style . userSelect = 'none'
110114 handle . setPointerCapture ?.( pointerId )
0 commit comments