@measured/dnd
Advanced tools
Comparing version 16.6.0-canary.f472135 to 16.6.0-canary.fb068c0
{ | ||
"name": "@measured/dnd", | ||
"version": "16.6.0-canary.f472135", | ||
"version": "16.6.0-canary.fb068c0", | ||
"private": false, | ||
@@ -5,0 +5,0 @@ "description": "Beautiful and accessible drag and drop for lists with React, with iframe and CSS transform support", |
@@ -35,2 +35,3 @@ import type { BoxModel, Position } from 'css-box-model'; | ||
transform: Transform | null; | ||
parents: DroppableDescriptor[]; | ||
} | ||
@@ -48,2 +49,3 @@ | ||
transform, | ||
parents, | ||
}: Args): DroppableDimension => { | ||
@@ -103,2 +105,3 @@ const frame: Scrollable | null = (() => { | ||
transform, | ||
parents, | ||
}; | ||
@@ -105,0 +108,0 @@ |
@@ -78,2 +78,63 @@ import type { Position, Rect } from 'css-box-model'; | ||
/** | ||
* normalizeFamilies | ||
* | ||
* Groups all items that share a common root `parent`, and selects the deepest item | ||
* in that group that contains the center point of the dragged item to represent | ||
* the "family". | ||
*/ | ||
function normalizeFamilies( | ||
pageBorderBox: Rect, | ||
candidates: DroppableDimension[], | ||
) { | ||
const families = candidates.reduce<Record<string, DroppableDimension[]>>( | ||
(acc, candidate) => { | ||
const familyName = candidate.parents[0]?.id || candidate.descriptor.id; | ||
const family = acc[familyName] || []; | ||
const generation = candidate.parents.length; | ||
let chosenCandidateId: string | null = candidate.descriptor.id; | ||
if (family[generation]) { | ||
// Overlapping with 2 items at the same generation. get furthest away. | ||
chosenCandidateId = getFurthestAway({ | ||
pageBorderBox, | ||
draggable, | ||
candidates: [candidate, family[generation]], | ||
}); | ||
} | ||
if (chosenCandidateId) { | ||
family[generation] = candidate; | ||
} | ||
return { | ||
...acc, | ||
[familyName]: family, | ||
}; | ||
}, | ||
{}, | ||
); | ||
return Object.keys(families).map((familyName) => { | ||
const family = families[familyName]; | ||
const reversedFamily = [...family].reverse(); | ||
// Get first member of family that contains the draggable | ||
const chosenMember = reversedFamily.find((member) => { | ||
return ( | ||
pageBorderBox.center.x < member.page.borderBox.right && | ||
pageBorderBox.center.x > member.page.borderBox.left && | ||
pageBorderBox.center.y > member.page.borderBox.top && | ||
pageBorderBox.center.y < member.page.borderBox.bottom | ||
); | ||
}); | ||
return chosenMember || family[0]; | ||
}); | ||
} | ||
export default function getDroppableOver({ | ||
@@ -150,3 +211,10 @@ pageBorderBox, | ||
// Multiple options returned | ||
// Select the best candidate from each group that share a common root ancestor | ||
const normalizedCandidates = normalizeFamilies(pageBorderBox, candidates); | ||
// All candidates were in the same family | ||
if (normalizedCandidates.length === 1) { | ||
return normalizedCandidates[0].descriptor.id; | ||
} | ||
// Should only occur with really large items | ||
@@ -157,4 +225,4 @@ // Going to use fallback: distance from home | ||
draggable, | ||
candidates, | ||
candidates: normalizedCandidates, | ||
}); | ||
} |
@@ -157,2 +157,3 @@ import type { BoxModel, Rect, Position } from 'css-box-model'; | ||
transform: Transform | null; | ||
parents: DroppableDescriptor[]; | ||
} | ||
@@ -159,0 +160,0 @@ export interface DraggableLocation { |
@@ -20,36 +20,19 @@ import { querySelectorAll } from '../../query-selector-all'; | ||
const getWin = (el: HTMLElement | Window) => { | ||
let win = el as Window | null; | ||
let loaded = false; | ||
if ((el as HTMLElement).nodeName === 'IFRAME') { | ||
win = (el as HTMLIFrameElement).contentWindow; | ||
} | ||
return win; | ||
}; | ||
function bindEvent( | ||
el: HTMLElement | Window, | ||
binding: EventBinding, | ||
options: EventOptions, | ||
) { | ||
function bindEvent(win: Window, binding: EventBinding, options: EventOptions) { | ||
let timer: number | undefined; | ||
if ((el as HTMLElement).nodeName === 'IFRAME') { | ||
if (!loaded) { | ||
// Some browsers require us to defer binding events, i.e. Safari | ||
timer = setInterval(() => { | ||
const currentWin = getWin(el); | ||
if (currentWin?.document.readyState === 'complete') { | ||
currentWin.addEventListener(binding.eventName, binding.fn, options); | ||
clearInterval(timer); | ||
if ((win as Window).document.readyState === 'complete') { | ||
win.addEventListener(binding.eventName, binding.fn, options); | ||
loaded = true; | ||
} | ||
}, 100); | ||
return timer; | ||
} else { | ||
win.addEventListener(binding.eventName, binding.fn, options); | ||
} | ||
const win = getWin(el); | ||
win?.addEventListener(binding.eventName, binding.fn, options); | ||
return timer; | ||
@@ -70,15 +53,14 @@ } | ||
const els = [el, ...iframes]; | ||
const windows = [el, ...iframes.map((iframe) => iframe.contentWindow)]; | ||
return els.map((currentEl) => { | ||
if (!currentEl) return function unbind() {}; | ||
return windows.map((win) => { | ||
if (!win) return function unbind() {}; | ||
const options = getOptions(sharedOptions, binding.options); | ||
const timer = bindEvent(currentEl, binding, options); | ||
const timer = bindEvent(win as Window, binding, options); | ||
return function unbind() { | ||
clearInterval(timer); | ||
const win = getWin(currentEl); | ||
win?.removeEventListener(binding.eventName, binding.fn, options); | ||
win.removeEventListener(binding.eventName, binding.fn, options); | ||
}; | ||
@@ -85,0 +67,0 @@ }); |
@@ -17,2 +17,3 @@ import { getBox, withScroll, createBox, expand } from 'css-box-model'; | ||
import { Offset } from '../iframe/offset-types'; | ||
import { prefix } from '../data-attributes'; | ||
@@ -100,2 +101,30 @@ const getClient = ( | ||
const getParents = (ref: HTMLElement) => { | ||
const contextId = ref.getAttribute(`${prefix}-droppable-context-id`); | ||
const parentDescriptors: DroppableDescriptor[] = []; | ||
if (!contextId) return []; | ||
let currentEl: HTMLElement | null | undefined = ref; | ||
while (currentEl) { | ||
currentEl = currentEl.parentElement?.closest( | ||
`[${prefix}-droppable-context-id="${contextId}"]`, | ||
); | ||
const id = currentEl?.getAttribute(`${prefix}-droppable-id`); | ||
if (id) { | ||
parentDescriptors.push({ | ||
id, | ||
mode: 'standard', | ||
type: 'DEFAULT', | ||
}); | ||
} | ||
} | ||
return parentDescriptors; | ||
}; | ||
export default ({ | ||
@@ -137,2 +166,4 @@ ref, | ||
const parents = getParents(ref); | ||
const dimension: DroppableDimension = getDroppableDimension({ | ||
@@ -148,2 +179,3 @@ descriptor, | ||
transform, | ||
parents, | ||
}); | ||
@@ -150,0 +182,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1434072
41773