@instructure/ui-dom-utils 10.26.1-snapshot-2 → 10.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,42 +22,34 @@
22
22
  * SOFTWARE.
23
23
  */
24
24
 
25
- import type { UIElement } from '@instructure/shared-types'
26
25
  import ReactDOM from 'react-dom'
27
-
28
- /**
29
- * Return the ref of an element in a way that is compatible both with
30
- * React 18 (element.ref) and React 19+ (element.props.ref)
31
- */
32
- function getElementRef(elem: { ref?: any; props?: { ref: any } }) {
33
- // Note that this is added by InstUI components like Position
34
- if (elem.ref) {
35
- return elem.ref
36
- }
37
- // note that trying to access this will display a warning in React 18.
38
- // this is OK because without it React itself would show an error because
39
- // its using findDOMNode
40
- return elem?.props?.ref
26
+ import type { ReactInstance, RefObject } from 'react'
27
+ import type { UIElement } from '@instructure/shared-types'
28
+ type ReactNodeWithRef = ReactInstance & {
29
+ ref: RefObject<Element | ReactInstance> | Element | ReactInstance
41
30
  }
31
+ type CustomRefNode =
32
+ | ReactNodeWithRef
33
+ | RefObject<Element>
34
+ | Element
35
+ | ReactInstance
42
36
 
37
+ const isReactNodeWithRef = (el: unknown): el is ReactNodeWithRef => {
38
+ return !!el && (el as ReactNodeWithRef).ref !== undefined
39
+ }
40
+ const isRefObject = (obj: unknown): obj is RefObject<unknown> => {
41
+ return !!obj && (obj as RefObject<unknown>).current !== undefined
42
+ }
43
43
  /**
44
44
  * ---
45
45
  * category: utilities/DOM
46
46
  * ---
47
47
  *
48
- * Returns the underlying DOM node. It looks the following places:
49
- * - If it's a native HTML element e.g. `HtmlSpanElement` its returned as is.
50
- * - If the element has a non-nullish `ref` prop or instance variable which is a
51
- * `HTMLElement` its returned. If it`s something else then `findDOMNode` is called
52
- * on the object. (it also checks `ref.current`)
53
- * - If the element has a `ref` prop or instance variable whose value is `null`,
54
- * `undefined` is returned.
55
- * - If the element does not have a `ref` prop or instance variable `undefined`
56
- * is returned with a warning
48
+ * Wrapper function for React.findDOMNode
57
49
  * @module findDOMNode
58
50
  *
59
- * @param el - component, DOM node, or function returning a DOM node
60
- * @returns The native DOM node of this element or `undefined`
51
+ * @param { Node | Window | React.ReactElement | React.Component | function } el - component, DOM node, or function returning a DOM node
52
+ * @returns { Node | Window | null | undefined } The root node of this element
61
53
  */
62
54
  function findDOMNode(el?: UIElement): Element | Node | Window | undefined {
63
55
  const node = typeof el === 'function' ? el() : el
@@ -71,33 +63,27 @@ function findDOMNode(el?: UIElement): Element | Node | Window | undefined {
71
63
  ) {
72
64
  return node as Node | Window
73
65
  } else if (node) {
74
- const ref = getElementRef(node as object)
75
- const refElement = ref?.current ? ref.current : ref
76
- if (refElement) {
66
+ const reactNode = node as CustomRefNode
67
+
68
+ if (isReactNodeWithRef(reactNode)) {
69
+ const refElement = isRefObject(reactNode.ref)
70
+ ? reactNode.ref.current
71
+ : reactNode.ref
77
72
  if (!(refElement instanceof HTMLElement)) {
78
73
  return findDOMNode(refElement)
79
74
  }
80
75
  return refElement
81
- } else if (refElement === null) {
82
- // weird, but changing it would be a breaking change
83
- return undefined
84
- }
85
- const elName = (node as any).constructor.componentId
86
- ? (node as any).constructor.componentId
87
- : (node as any).constructor.name
88
- console.error(
89
- `findDOMNode error: ${elName} doesn't have "ref" property.\n
90
- your code will likely not work with React 19+, because ReactDOM.findDOMNode() was removed.\n
91
- See more here: https://instructure.design/#accessing-the-dom`
92
- )
93
- // TODO remove when we only support React 19 or greater
94
- const ReactDomInstance = ReactDOM as {
95
- findDOMNode?: (arg: unknown) => Element
96
- }
97
- if (typeof ReactDomInstance.findDOMNode === 'function') {
98
- return ReactDomInstance.findDOMNode(node)
76
+ } else {
77
+ const elName = (reactNode as any).constructor.componentId
78
+ ? (reactNode as any).constructor.componentId
79
+ : (reactNode as any).constructor.name
80
+
81
+ console.warn(
82
+ `Warning: ${elName} doesn't have "ref" property.\nReactDOM.findDOMNode is deprecated in Strict mode, consider using refs instead. From InstUI v9, components must have the "ref" property for findDOMNode to work.\nSee more here: https://instructure.design/#accessing-the-dom`
83
+ )
84
+
85
+ return ReactDOM.findDOMNode(node as ReactInstance)!
99
86
  }
100
- return undefined
101
87
  }
102
88
  return undefined
103
89
  }
@@ -37,7 +37,7 @@
37
37
  // or even better use the native <dialog> component.
38
38
  // tabbable has issues with scrollable containers, e.g.
39
39
  // https://github.com/focus-trap/tabbable/issues/167
40
- import { getCSSStyleDeclaration, findDOMNode } from './'
40
+ import { getComputedStyle, findDOMNode } from './'
41
41
  import type { UIElement } from '@instructure/shared-types'
42
42
 
43
43
  const focusableSelector = [
@@ -103,8 +103,8 @@ function findFocusable(
103
103
  }
104
104
 
105
105
  function hidden(element: Element | Node) {
106
- const cs = getCSSStyleDeclaration(element)
107
- return cs?.display === 'none'
106
+ const cs = getComputedStyle(element)
107
+ return cs.display === 'none'
108
108
  }
109
109
 
110
110
  function positioned(element: Element | Node) {
@@ -112,12 +112,11 @@ function positioned(element: Element | Node) {
112
112
  if (POS.includes((element as HTMLElement).style.position?.toLowerCase())) {
113
113
  return true
114
114
  }
115
-
116
- const position = getCSSStyleDeclaration(element)
117
- ?.getPropertyValue('position')
118
- ?.toLowerCase()
119
-
120
- if (position && POS.includes(position)) {
115
+ if (
116
+ POS.includes(
117
+ getComputedStyle(element).getPropertyValue('position')?.toLowerCase()
118
+ )
119
+ ) {
121
120
  return true
122
121
  }
123
122
  return false
@@ -141,4 +140,6 @@ function focusable(element: Element & { disabled?: boolean }) {
141
140
  }
142
141
 
143
142
  export default findFocusable
144
- export { findFocusable }
143
+ export {
144
+ findFocusable
145
+ }
@@ -34,29 +34,23 @@ import type { UIElement } from '@instructure/shared-types'
34
34
  *
35
35
  * Get the associated CSS properties and values for a
36
36
  * specified element
37
- * @module getCSSStyleDeclaration
37
+ * @module getComputedStyle
38
38
  *
39
39
  * @param { Node | Window | React.ReactElement | React.Component | function } el - component or DOM node
40
40
  * @param { string | null | undefined } pseudoElt - A string specifying the pseudo-element to match. Omitted (or null ) for real elements.
41
- * @returns { CSSStyleDeclaration | undefined } The style declaration containing css properties and values for the element or undefined
41
+ * @returns { Object } object containing css properties and values for the element
42
42
  */
43
- function getCSSStyleDeclaration(el?: UIElement, pseudoElt?: string | null) {
44
- let style: CSSStyleDeclaration | undefined
43
+ function getComputedStyle(el?: UIElement, pseudoElt?: string | null) {
44
+ let style = {}
45
45
  if (canUseDOM) {
46
46
  const node = el && findDOMNode(el)
47
47
  if (node) {
48
48
  const window = ownerWindow(el)
49
- try {
50
- style = window
51
- ? window.getComputedStyle(node as Element, pseudoElt)
52
- : undefined
53
- } catch {
54
- style = undefined
55
- }
49
+ style = window ? window.getComputedStyle(node as Element, pseudoElt) : {}
56
50
  }
57
51
  }
58
- return style
52
+ return style as CSSStyleDeclaration
59
53
  }
60
54
 
61
- export default getCSSStyleDeclaration
62
- export { getCSSStyleDeclaration }
55
+ export default getComputedStyle
56
+ export { getComputedStyle }
@@ -23,7 +23,7 @@
23
23
  */
24
24
  import { canUseDOM } from './canUseDOM'
25
25
  import { ownerDocument } from './ownerDocument'
26
- import { getCSSStyleDeclaration } from './getCSSStyleDeclaration'
26
+ import { getComputedStyle } from './getComputedStyle'
27
27
  import React from 'react'
28
28
 
29
29
  const COMPUTED_CACHE: WeakMap<any, number> = new WeakMap()
@@ -63,7 +63,7 @@ function getFontSize(
63
63
  }
64
64
 
65
65
  const fontSize = parseInt(
66
- (getCSSStyleDeclaration(container) as CSSStyleDeclaration).getPropertyValue(
66
+ (getComputedStyle(container) as CSSStyleDeclaration).getPropertyValue(
67
67
  'font-size'
68
68
  )
69
69
  )
@@ -24,7 +24,7 @@
24
24
 
25
25
  import { findDOMNode } from './findDOMNode'
26
26
  import { canUseDOM } from './canUseDOM'
27
- import { getCSSStyleDeclaration } from './getCSSStyleDeclaration'
27
+ import { getComputedStyle } from './getComputedStyle'
28
28
  import { ownerDocument } from './ownerDocument'
29
29
  import { UIElement } from '@instructure/shared-types'
30
30
 
@@ -59,7 +59,7 @@ function getOffsetParents(el?: UIElement) {
59
59
  parent.nodeType === 1 &&
60
60
  (parent as Element).tagName !== 'BODY'
61
61
  ) {
62
- const style = getCSSStyleDeclaration(parent) as CSSStyleDeclaration
62
+ const style = getComputedStyle(parent) as CSSStyleDeclaration
63
63
  const transform =
64
64
  style.getPropertyValue('-webkit-transform') ||
65
65
  style.getPropertyValue('-moz-transform') ||
@@ -24,7 +24,7 @@
24
24
 
25
25
  import { findDOMNode } from './findDOMNode'
26
26
  import { canUseDOM } from './canUseDOM'
27
- import { getCSSStyleDeclaration } from './getCSSStyleDeclaration'
27
+ import { getComputedStyle } from './getComputedStyle'
28
28
  import { UIElement } from '@instructure/shared-types'
29
29
 
30
30
  /**
@@ -53,8 +53,8 @@ function getScrollParents(el?: UIElement) {
53
53
  if (node) {
54
54
  // In firefox if the element is inside an iframe with display: none; window.getComputedStyle() will return null;
55
55
  // https://bugzilla.mozilla.org/show_bug.cgi?id=548397
56
- const computedStyle = getCSSStyleDeclaration(node)
57
- const position = computedStyle?.position
56
+ const computedStyle = getComputedStyle(node) || {}
57
+ const position = computedStyle.position
58
58
 
59
59
  if (position === 'fixed') {
60
60
  return [(node as Node).ownerDocument]
@@ -68,7 +68,7 @@ function getScrollParents(el?: UIElement) {
68
68
  ) {
69
69
  let style
70
70
  try {
71
- style = getCSSStyleDeclaration(parent)
71
+ style = getComputedStyle(parent)
72
72
  } catch (err: any) {} // eslint-disable-line no-empty
73
73
 
74
74
  if (typeof style === 'undefined' || style === null) {
package/src/index.ts CHANGED
@@ -37,7 +37,7 @@ export { getActiveElement } from './getActiveElement'
37
37
  export { getBoundingClientRect } from './getBoundingClientRect'
38
38
  export type { RectType } from './getBoundingClientRect'
39
39
  export { getClassList } from './getClassList'
40
- export { getCSSStyleDeclaration } from './getCSSStyleDeclaration'
40
+ export { getComputedStyle } from './getComputedStyle'
41
41
  export { getFontSize } from './getFontSize'
42
42
  export { getOffsetParents } from './getOffsetParents'
43
43
  export { getScrollParents } from './getScrollParents'
package/src/isVisible.ts CHANGED
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import { findDOMNode } from './findDOMNode'
26
- import { getCSSStyleDeclaration } from './getCSSStyleDeclaration'
26
+ import { getComputedStyle } from './getComputedStyle'
27
27
  import { UIElement } from '@instructure/shared-types'
28
28
 
29
29
  /**
@@ -52,24 +52,22 @@ function isVisible(el?: UIElement, recursive = true): boolean {
52
52
  return isVisible(parent, recursive)
53
53
  }
54
54
 
55
- const style = node
56
- ? getCSSStyleDeclaration(node)
57
- : ({} as CSSStyleDeclaration)
55
+ const style = node ? getComputedStyle(node) : ({} as CSSStyleDeclaration)
58
56
  // physically and visually hidden
59
- if (style?.display === 'none') {
57
+ if (style.display === 'none') {
60
58
  return false
61
59
  }
62
60
  // visually hidden
63
- if (style?.visibility === 'hidden' || style?.opacity === '0') {
61
+ if (style.visibility === 'hidden' || style.opacity === '0') {
64
62
  return false
65
63
  }
66
64
  // hidden by clipping
67
65
  if (
68
- style?.overflow === 'hidden' &&
69
- style?.position === 'absolute' &&
70
- style?.clip !== 'auto'
66
+ style.overflow === 'hidden' &&
67
+ style.position === 'absolute' &&
68
+ style.clip !== 'auto'
71
69
  ) {
72
- const rect = style?.clip.substring(5).slice(0, -1).split(', ')
70
+ const rect = style.clip.substring(5).slice(0, -1).split(', ')
73
71
  let zeros = true
74
72
  rect.forEach((a) => {
75
73
  if (a !== '0px') {