@@ -299,19 +299,19 @@ export const isTrackedMutation = (
299299 }
300300
301301 const { document } = DOMEditor . getWindow ( editor )
302- if ( document . contains ( target ) ) {
302+ if ( containsShadowAware ( document , target ) ) {
303303 return DOMEditor . hasDOMNode ( editor , target , { editable : true } )
304304 }
305305
306306 const parentMutation = batch . find ( ( { addedNodes, removedNodes } ) => {
307307 for ( const node of addedNodes ) {
308- if ( node === target || node . contains ( target ) ) {
308+ if ( node === target || containsShadowAware ( node , target ) ) {
309309 return true
310310 }
311311 }
312312
313313 for ( const node of removedNodes ) {
314- if ( node === target || node . contains ( target ) ) {
314+ if ( node === target || containsShadowAware ( node , target ) ) {
315315 return true
316316 }
317317 }
@@ -355,3 +355,71 @@ export const isAfter = (node: DOMNode, otherNode: DOMNode): boolean =>
355355 node . compareDocumentPosition ( otherNode ) &
356356 DOMNode . DOCUMENT_POSITION_FOLLOWING
357357 )
358+
359+ /**
360+ * Shadow DOM-aware version of Element.closest()
361+ * Traverses up the DOM tree, crossing shadow DOM boundaries
362+ */
363+ export const closestShadowAware = (
364+ element : DOMElement | null | undefined ,
365+ selector : string
366+ ) : DOMElement | null => {
367+ if ( ! element ) {
368+ return null
369+ }
370+
371+ let current : DOMElement | null = element
372+
373+ while ( current ) {
374+ if ( current . matches && current . matches ( selector ) ) {
375+ return current
376+ }
377+
378+ if ( current . parentElement ) {
379+ current = current . parentElement
380+ } else if ( current . parentNode && 'host' in current . parentNode ) {
381+ current = ( current . parentNode as ShadowRoot ) . host as DOMElement
382+ } else {
383+ return null
384+ }
385+ }
386+
387+ return null
388+ }
389+
390+ /**
391+ * Shadow DOM-aware version of Node.contains()
392+ * Checks if a node contains another node, crossing shadow DOM boundaries
393+ */
394+ export const containsShadowAware = (
395+ parent : DOMNode | null | undefined ,
396+ child : DOMNode | null | undefined
397+ ) : boolean => {
398+ if ( ! parent || ! child ) {
399+ return false
400+ }
401+
402+ if ( parent . contains ( child ) ) {
403+ return true
404+ }
405+
406+ let current : DOMNode | null = child
407+
408+ while ( current ) {
409+ if ( current === parent ) {
410+ return true
411+ }
412+
413+ if ( current . parentNode ) {
414+ if ( 'host' in current . parentNode ) {
415+ current = ( current . parentNode as ShadowRoot ) . host
416+ } else {
417+ current = current . parentNode
418+ }
419+ } else {
420+ return false
421+ }
422+ }
423+
424+ return false
425+ }
0 commit comments