@sanity/ui 2.7.1-canary.1 → 2.8.0

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.
@@ -5,11 +5,16 @@ import {EMPTY_ARRAY} from '../constants'
5
5
  /**
6
6
  * @public
7
7
  */
8
- export type ClickOutsideListener = (event: MouseEvent | TouchEvent) => void
8
+ export type ClickOutsideListener = (event: MouseEvent) => void
9
9
 
10
10
  /**
11
11
  * @public
12
- * @deprecated -- do not use
12
+ */
13
+ export type ClickOutsideElements = (HTMLElement | null | (HTMLElement | null)[])[]
14
+
15
+ /**
16
+ * @public
17
+ * @deprecated - use your own `const [element, setElement] = useState(null)` logic instead
13
18
  */
14
19
  export type UseClickOutsideSetElement = (el: HTMLElement | null) => void
15
20
 
@@ -17,11 +22,28 @@ export type UseClickOutsideSetElement = (el: HTMLElement | null) => void
17
22
  * @public
18
23
  */
19
24
  export function useClickOutside(
20
- listener: ClickOutsideListener,
21
- elementsArg: () => (HTMLElement | HTMLElement[] | null)[],
22
- boundaryElement?: HTMLElement | null,
25
+ listener: ClickOutsideListener | false | undefined,
26
+ elementsArg: () => ClickOutsideElements,
27
+ boundaryElement?: () => HTMLElement | null,
28
+ ): void
29
+ /**
30
+ * @public
31
+ * @deprecated - change `useClickOutside(handler, () => [...], boundary)` to `useClickOutside(handler, () => [...], () => boundary)`
32
+ */
33
+ export function useClickOutside(
34
+ listener: ClickOutsideListener | false | undefined,
35
+ elementsArg: () => ClickOutsideElements,
36
+ boundaryElement: HTMLElement | null,
23
37
  ): void
24
-
38
+ /**
39
+ * @public
40
+ * @deprecated - change `useClickOutside(handler, [...], () => boundary)` to `useClickOutside(handler, () => [...], () => boundary)`
41
+ */
42
+ export function useClickOutside(
43
+ listener: ClickOutsideListener | false | undefined,
44
+ elementsArg: ClickOutsideElements,
45
+ boundaryElement: () => HTMLElement | null,
46
+ ): UseClickOutsideSetElement
25
47
  /**
26
48
  * @public
27
49
  * @deprecated
@@ -50,8 +72,8 @@ export function useClickOutside(
50
72
  * ```
51
73
  */
52
74
  export function useClickOutside(
53
- listener: ClickOutsideListener,
54
- elementsArg: (HTMLElement | HTMLElement[] | null)[],
75
+ listener: ClickOutsideListener | false | undefined,
76
+ elementsArg: ClickOutsideElements,
55
77
  boundaryElement?: HTMLElement | null,
56
78
  ): UseClickOutsideSetElement
57
79
 
@@ -59,11 +81,9 @@ export function useClickOutside(
59
81
  * @public
60
82
  */
61
83
  export function useClickOutside(
62
- listener: ClickOutsideListener,
63
- elementsArg:
64
- | (HTMLElement | HTMLElement[] | null)[]
65
- | (() => (HTMLElement | HTMLElement[] | null)[]) = EMPTY_ARRAY,
66
- boundaryElement?: HTMLElement | null,
84
+ listener: ClickOutsideListener | false | undefined,
85
+ elementsArg: ClickOutsideElements | (() => ClickOutsideElements) = EMPTY_ARRAY,
86
+ boundaryElement?: HTMLElement | null | (() => HTMLElement | null),
67
87
  ): UseClickOutsideSetElement | void {
68
88
  /** @deprecated */
69
89
  const [legacyElement, setLegacyElement] = useState<HTMLElement | null>(null)
@@ -72,14 +92,21 @@ export function useClickOutside(
72
92
  * The `useEffectEvent` hook allow us to always see the latest value of `listener`, `elementsArg` and `boundaryElement` without needing to
73
93
  * juggle `useState`, `useRef` and `useState` to make sure the `mousedown` event listener isn't constantly being added and removed.
74
94
  */
75
- const onEvent = useEffectEvent((evt: MouseEvent | TouchEvent) => {
95
+ const onEvent = useEffectEvent((evt: MouseEvent) => {
96
+ if (!listener) {
97
+ return
98
+ }
99
+
76
100
  const target = evt.target
77
101
 
78
102
  if (!(target instanceof Node)) {
79
103
  return
80
104
  }
81
105
 
82
- if (boundaryElement && !boundaryElement.contains(target)) {
106
+ const resolvedBoundaryElement =
107
+ typeof boundaryElement === 'function' ? boundaryElement() : boundaryElement
108
+
109
+ if (resolvedBoundaryElement && !resolvedBoundaryElement.contains(target)) {
83
110
  return
84
111
  }
85
112
 
@@ -104,14 +131,12 @@ export function useClickOutside(
104
131
  useEffect(() => {
105
132
  if (!hasListener) return undefined
106
133
 
107
- const handleEvent = (evt: MouseEvent | TouchEvent) => onEvent(evt)
134
+ const handleEvent = (evt: MouseEvent) => onEvent(evt)
108
135
 
109
136
  document.addEventListener('mousedown', handleEvent)
110
- document.addEventListener('touchstart', handleEvent)
111
137
 
112
138
  return () => {
113
139
  document.removeEventListener('mousedown', handleEvent)
114
- document.removeEventListener('touchstart', handleEvent)
115
140
  }
116
141
  }, [hasListener, onEvent])
117
142
 
@@ -1,7 +1,7 @@
1
1
  import {EllipsisVerticalIcon} from '@sanity/icons'
2
2
  import {Button, Card, Flex, Popover, Text, useClickOutside} from '@sanity/ui'
3
3
  import {useBoolean, useSelect} from '@sanity/ui-workshop'
4
- import {useCallback, useState} from 'react'
4
+ import {useCallback, useRef, useState} from 'react'
5
5
  import {
6
6
  WORKSHOP_FLEX_ALIGN_OPTIONS,
7
7
  WORKSHOP_FLEX_JUSTIFY_OPTIONS,
@@ -20,8 +20,8 @@ export default function AlignedStory() {
20
20
 
21
21
  const [open, setOpen] = useState(false)
22
22
  const [boundaryElement, setBoundaryElement] = useState<HTMLDivElement | null>(null)
23
- const [buttonElement, setButtonElement] = useState<HTMLButtonElement | null>(null)
24
- const [popoverElement, setPopoverElement] = useState<HTMLDivElement | null>(null)
23
+ const buttonElementRef = useRef<HTMLButtonElement | null>(null)
24
+ const popoverElementRef = useRef<HTMLDivElement | null>(null)
25
25
 
26
26
  const content = (
27
27
  <Text>
@@ -39,9 +39,11 @@ export default function AlignedStory() {
39
39
  )
40
40
 
41
41
  const handleToggleOpen = useCallback(() => setOpen((v) => !v), [])
42
- const handleClose = useCallback(() => setOpen(false), [])
43
42
 
44
- useClickOutside(handleClose, [buttonElement, popoverElement])
43
+ useClickOutside(
44
+ () => setOpen(false),
45
+ () => [buttonElementRef.current, popoverElementRef.current],
46
+ )
45
47
 
46
48
  return (
47
49
  <Card height="fill" padding={[4, 5, 6]} sizing="border" tone="transparent">
@@ -56,14 +58,14 @@ export default function AlignedStory() {
56
58
  padding={3}
57
59
  portal={portal}
58
60
  placement={placement}
59
- ref={setPopoverElement}
61
+ ref={popoverElementRef}
60
62
  width={width}
61
63
  >
62
64
  <Button
63
65
  icon={EllipsisVerticalIcon}
64
66
  mode="bleed"
65
67
  onClick={handleToggleOpen}
66
- ref={setButtonElement}
68
+ ref={buttonElementRef}
67
69
  selected={open}
68
70
  />
69
71
  </Popover>