sanity-plugin-hotspot-array 4.0.0 → 5.0.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.
- package/dist/index.d.ts +30 -48
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +240 -171
- package/dist/index.js.map +1 -1
- package/package.json +30 -65
- package/src/Feedback.tsx +0 -9
- package/src/ImageHotspotArray.tsx +0 -212
- package/src/Spot.tsx +0 -201
- package/src/index.ts +0 -3
- package/src/plugin.tsx +0 -40
- package/src/useDebouncedCallback.ts +0 -102
- package/src/useResizeObserver.ts +0 -123
package/src/Spot.tsx
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import {Box, Text, Tooltip} from '@sanity/ui'
|
|
2
|
-
import {motion, useMotionValue} from 'framer-motion'
|
|
3
|
-
import {get} from 'lodash-es'
|
|
4
|
-
import {
|
|
5
|
-
ComponentType,
|
|
6
|
-
createElement,
|
|
7
|
-
CSSProperties,
|
|
8
|
-
ReactNode,
|
|
9
|
-
useCallback,
|
|
10
|
-
useEffect,
|
|
11
|
-
useState,
|
|
12
|
-
} from 'react'
|
|
13
|
-
import {type ObjectSchemaType, type RenderPreviewCallback} from 'sanity'
|
|
14
|
-
|
|
15
|
-
import {FnHotspotMove, HotspotItem} from './ImageHotspotArray'
|
|
16
|
-
|
|
17
|
-
const dragStyle: CSSProperties = {
|
|
18
|
-
width: '1.4rem',
|
|
19
|
-
height: '1.4rem',
|
|
20
|
-
position: 'absolute',
|
|
21
|
-
boxSizing: 'border-box',
|
|
22
|
-
top: 0,
|
|
23
|
-
left: 0,
|
|
24
|
-
margin: '-0.7rem 0 0 -0.7rem',
|
|
25
|
-
cursor: 'pointer',
|
|
26
|
-
display: 'flex',
|
|
27
|
-
justifyContent: 'center',
|
|
28
|
-
alignItems: 'center',
|
|
29
|
-
borderRadius: '50%',
|
|
30
|
-
background: '#000',
|
|
31
|
-
color: 'white',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const dragStyleWhileDrag: CSSProperties = {
|
|
35
|
-
background: 'rgba(0, 0, 0, 0.1)',
|
|
36
|
-
border: '1px solid #fff',
|
|
37
|
-
cursor: 'none',
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const dragStyleWhileHover: CSSProperties = {
|
|
41
|
-
background: 'rgba(0, 0, 0, 0.1)',
|
|
42
|
-
border: '1px solid #fff',
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const dotStyle: CSSProperties = {
|
|
46
|
-
position: 'absolute',
|
|
47
|
-
left: '50%',
|
|
48
|
-
top: '50%',
|
|
49
|
-
height: '0.2rem',
|
|
50
|
-
width: '0.2rem',
|
|
51
|
-
margin: '-0.1rem 0 0 -0.1rem',
|
|
52
|
-
background: '#fff',
|
|
53
|
-
visibility: 'hidden',
|
|
54
|
-
borderRadius: '50%',
|
|
55
|
-
// make sure pointer events only run on the parent
|
|
56
|
-
pointerEvents: 'none',
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const dotStyleWhileActive: CSSProperties = {
|
|
60
|
-
visibility: 'visible',
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const labelStyle: CSSProperties = {
|
|
64
|
-
color: 'white',
|
|
65
|
-
fontSize: '0.7rem',
|
|
66
|
-
fontWeight: 600,
|
|
67
|
-
lineHeight: '1',
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const labelStyleWhileActive: CSSProperties = {
|
|
71
|
-
visibility: 'hidden',
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const round = (num: number) => Math.round(num * 100) / 100
|
|
75
|
-
|
|
76
|
-
export interface HotspotTooltipProps<HotspotFields = {[key: string]: unknown}> {
|
|
77
|
-
value: HotspotItem<HotspotFields>
|
|
78
|
-
schemaType: ObjectSchemaType
|
|
79
|
-
renderPreview: RenderPreviewCallback
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
interface HotspotProps<HotspotFields = {[key: string]: unknown}> {
|
|
83
|
-
value: HotspotItem
|
|
84
|
-
bounds: DOMRectReadOnly
|
|
85
|
-
update: FnHotspotMove
|
|
86
|
-
hotspotDescriptionPath?: string
|
|
87
|
-
tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>
|
|
88
|
-
index: number
|
|
89
|
-
schemaType: ObjectSchemaType
|
|
90
|
-
renderPreview: RenderPreviewCallback
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export default function Spot({
|
|
94
|
-
value,
|
|
95
|
-
bounds,
|
|
96
|
-
update,
|
|
97
|
-
hotspotDescriptionPath,
|
|
98
|
-
tooltip,
|
|
99
|
-
index,
|
|
100
|
-
schemaType,
|
|
101
|
-
renderPreview,
|
|
102
|
-
}: HotspotProps): ReactNode {
|
|
103
|
-
const [isDragging, setIsDragging] = useState(false)
|
|
104
|
-
const [isHovering, setIsHovering] = useState(false)
|
|
105
|
-
|
|
106
|
-
// x/y are stored as % but need to be converted to px
|
|
107
|
-
const x = useMotionValue(round(bounds.width * (value.x / 100)))
|
|
108
|
-
const y = useMotionValue(round(bounds.height * (value.y / 100)))
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* update x/y if the bounds change when resizing the window
|
|
112
|
-
*/
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
x.set(round(bounds.width * (value.x / 100)))
|
|
115
|
-
y.set(round(bounds.height * (value.y / 100)))
|
|
116
|
-
}, [x, y, value, bounds])
|
|
117
|
-
|
|
118
|
-
const handleDragEnd = useCallback(() => {
|
|
119
|
-
setIsDragging(false)
|
|
120
|
-
|
|
121
|
-
// get current values for x/y in px
|
|
122
|
-
const currentX = x.get()
|
|
123
|
-
const currentY = y.get()
|
|
124
|
-
|
|
125
|
-
// Which we need to convert back to `%` to patch the document
|
|
126
|
-
const newX = round((currentX * 100) / bounds.width)
|
|
127
|
-
const newY = round((currentY * 100) / bounds.height)
|
|
128
|
-
|
|
129
|
-
// Don't go below 0 or above 100
|
|
130
|
-
const safeX = Math.max(0, Math.min(100, newX))
|
|
131
|
-
const safeY = Math.max(0, Math.min(100, newY))
|
|
132
|
-
|
|
133
|
-
update(value._key, safeX, safeY)
|
|
134
|
-
}, [x, y, value, update, bounds])
|
|
135
|
-
const handleDragStart = useCallback(() => setIsDragging(true), [])
|
|
136
|
-
|
|
137
|
-
const handleHoverStart = useCallback(() => setIsHovering(true), [])
|
|
138
|
-
const handleHoverEnd = useCallback(() => setIsHovering(false), [])
|
|
139
|
-
|
|
140
|
-
if (!x || !y) {
|
|
141
|
-
return null
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<motion.div
|
|
146
|
-
drag
|
|
147
|
-
dragConstraints={bounds}
|
|
148
|
-
dragElastic={0}
|
|
149
|
-
dragMomentum={false}
|
|
150
|
-
onDragEnd={handleDragEnd}
|
|
151
|
-
onDragStart={handleDragStart}
|
|
152
|
-
onHoverStart={handleHoverStart}
|
|
153
|
-
onHoverEnd={handleHoverEnd}
|
|
154
|
-
style={{
|
|
155
|
-
...dragStyle,
|
|
156
|
-
x,
|
|
157
|
-
y,
|
|
158
|
-
...(isDragging && {...dragStyleWhileDrag}),
|
|
159
|
-
...(isHovering && {...dragStyleWhileHover}),
|
|
160
|
-
}}
|
|
161
|
-
>
|
|
162
|
-
<Tooltip
|
|
163
|
-
key={value._key}
|
|
164
|
-
disabled={isDragging}
|
|
165
|
-
portal
|
|
166
|
-
content={
|
|
167
|
-
tooltip && typeof tooltip === 'function' ? (
|
|
168
|
-
createElement(tooltip, {value, renderPreview, schemaType})
|
|
169
|
-
) : (
|
|
170
|
-
<Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>
|
|
171
|
-
<Text textOverflow="ellipsis">
|
|
172
|
-
{hotspotDescriptionPath
|
|
173
|
-
? (get(value, hotspotDescriptionPath) as string)
|
|
174
|
-
: `${value.x}% x ${value.y}%`}
|
|
175
|
-
</Text>
|
|
176
|
-
</Box>
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
>
|
|
180
|
-
<div>
|
|
181
|
-
{/* Dot */}
|
|
182
|
-
<Box
|
|
183
|
-
style={{
|
|
184
|
-
...dotStyle,
|
|
185
|
-
...((isDragging || isHovering) && {...dotStyleWhileActive}),
|
|
186
|
-
}}
|
|
187
|
-
/>
|
|
188
|
-
{/* Label */}
|
|
189
|
-
<div
|
|
190
|
-
style={{
|
|
191
|
-
...labelStyle,
|
|
192
|
-
...((isDragging || isHovering) && {...labelStyleWhileActive}),
|
|
193
|
-
}}
|
|
194
|
-
>
|
|
195
|
-
{index + 1}
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
</Tooltip>
|
|
199
|
-
</motion.div>
|
|
200
|
-
)
|
|
201
|
-
}
|
package/src/index.ts
DELETED
package/src/plugin.tsx
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {ComponentType} from 'react'
|
|
2
|
-
import {type ArrayOfObjectsInputProps, definePlugin} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'
|
|
5
|
-
import {type HotspotTooltipProps} from './Spot'
|
|
6
|
-
|
|
7
|
-
export interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {
|
|
8
|
-
pathRoot?: 'document' | 'parent'
|
|
9
|
-
imagePath: string
|
|
10
|
-
descriptionPath?: string
|
|
11
|
-
tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
declare module 'sanity' {
|
|
15
|
-
export interface ArrayOptions {
|
|
16
|
-
imageHotspot?: ImageHotspotOptions
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const imageHotspotArrayPlugin = definePlugin({
|
|
21
|
-
name: 'image-hotspot-array',
|
|
22
|
-
form: {
|
|
23
|
-
components: {
|
|
24
|
-
input: (props) => {
|
|
25
|
-
if (props.schemaType.jsonType === 'array' && props.schemaType.options?.imageHotspot) {
|
|
26
|
-
const imageHotspotOptions = props.schemaType.options?.imageHotspot
|
|
27
|
-
if (imageHotspotOptions) {
|
|
28
|
-
return (
|
|
29
|
-
<ImageHotspotArray
|
|
30
|
-
{...(props as unknown as ArrayOfObjectsInputProps<HotspotItem>)}
|
|
31
|
-
imageHotspotOptions={imageHotspotOptions}
|
|
32
|
-
/>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return props.renderDefault(props)
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
})
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// copied from react-hookz/web
|
|
2
|
-
// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useDebouncedCallback/index.ts
|
|
3
|
-
import {type DependencyList, useEffect, useMemo, useRef} from 'react'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Run effect only when component is unmounted.
|
|
7
|
-
*
|
|
8
|
-
* @param effect Effector to run on unmount
|
|
9
|
-
*/
|
|
10
|
-
export function useUnmountEffect(effect: CallableFunction): void {
|
|
11
|
-
const effectRef = useRef(effect)
|
|
12
|
-
effectRef.current = effect
|
|
13
|
-
useEffect(() => () => effectRef.current(), [])
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
-
export type DebouncedFunction<Fn extends (...args: any[]) => any> = (
|
|
18
|
-
this: ThisParameterType<Fn>,
|
|
19
|
-
...args: Parameters<Fn>
|
|
20
|
-
) => void
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Makes passed function debounced, otherwise acts like `useCallback`.
|
|
24
|
-
*
|
|
25
|
-
* @param callback Function that will be debounced.
|
|
26
|
-
* @param deps Dependencies list when to update callback. It also replaces invoked
|
|
27
|
-
* callback for scheduled debounced invocations.
|
|
28
|
-
* @param delay Debounce delay.
|
|
29
|
-
* @param maxWait The maximum time `callback` is allowed to be delayed before
|
|
30
|
-
* it's invoked. 0 means no max wait.
|
|
31
|
-
*/
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
-
export function useDebouncedCallback<Fn extends (...args: any[]) => any>(
|
|
34
|
-
callback: Fn,
|
|
35
|
-
deps: DependencyList,
|
|
36
|
-
delay: number,
|
|
37
|
-
maxWait = 0,
|
|
38
|
-
): DebouncedFunction<Fn> {
|
|
39
|
-
const timeout = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
40
|
-
const waitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
41
|
-
const cb = useRef(callback)
|
|
42
|
-
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined)
|
|
43
|
-
|
|
44
|
-
const clear = () => {
|
|
45
|
-
if (timeout.current) {
|
|
46
|
-
clearTimeout(timeout.current)
|
|
47
|
-
timeout.current = undefined
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (waitTimeout.current) {
|
|
51
|
-
clearTimeout(waitTimeout.current)
|
|
52
|
-
waitTimeout.current = undefined
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Cancel scheduled execution on unmount
|
|
57
|
-
useUnmountEffect(clear)
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
cb.current = callback
|
|
61
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
|
-
}, deps)
|
|
63
|
-
|
|
64
|
-
return useMemo(() => {
|
|
65
|
-
const execute = () => {
|
|
66
|
-
clear()
|
|
67
|
-
|
|
68
|
-
// Barely possible to test this line
|
|
69
|
-
/* istanbul ignore next */
|
|
70
|
-
if (!lastCall.current) return
|
|
71
|
-
|
|
72
|
-
const context = lastCall.current
|
|
73
|
-
lastCall.current = undefined
|
|
74
|
-
|
|
75
|
-
cb.current.apply(context.this, context.args)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const wrapped = function (this, ...args) {
|
|
79
|
-
if (timeout.current) {
|
|
80
|
-
clearTimeout(timeout.current)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
lastCall.current = {args, this: this}
|
|
84
|
-
|
|
85
|
-
// Plan regular execution
|
|
86
|
-
timeout.current = setTimeout(execute, delay)
|
|
87
|
-
|
|
88
|
-
// Plan maxWait execution if required
|
|
89
|
-
if (maxWait > 0 && !waitTimeout.current) {
|
|
90
|
-
waitTimeout.current = setTimeout(execute, maxWait)
|
|
91
|
-
}
|
|
92
|
-
} as DebouncedFunction<Fn>
|
|
93
|
-
|
|
94
|
-
Object.defineProperties(wrapped, {
|
|
95
|
-
length: {value: callback.length},
|
|
96
|
-
name: {value: `${callback.name || 'anonymous'}__debounced__${delay}`},
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
return wrapped
|
|
100
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
101
|
-
}, [delay, maxWait, ...deps])
|
|
102
|
-
}
|
package/src/useResizeObserver.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// copied from react-hookz/web
|
|
2
|
-
// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useResizeObserver/index.ts
|
|
3
|
-
import {type RefObject, useEffect, useRef} from 'react'
|
|
4
|
-
|
|
5
|
-
const isBrowser =
|
|
6
|
-
typeof window !== 'undefined' &&
|
|
7
|
-
typeof navigator !== 'undefined' &&
|
|
8
|
-
typeof document !== 'undefined'
|
|
9
|
-
|
|
10
|
-
export type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void
|
|
11
|
-
|
|
12
|
-
type ResizeObserverSingleton = {
|
|
13
|
-
observer: ResizeObserver
|
|
14
|
-
subscribe: (target: Element, callback: UseResizeObserverCallback) => void
|
|
15
|
-
unsubscribe: (target: Element, callback: UseResizeObserverCallback) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let observerSingleton: ResizeObserverSingleton
|
|
19
|
-
|
|
20
|
-
function getResizeObserver(): ResizeObserverSingleton | undefined {
|
|
21
|
-
if (!isBrowser) return undefined
|
|
22
|
-
|
|
23
|
-
if (observerSingleton) return observerSingleton
|
|
24
|
-
|
|
25
|
-
const callbacks = new Map<Element, Set<UseResizeObserverCallback>>()
|
|
26
|
-
|
|
27
|
-
const observer = new ResizeObserver((entries) => {
|
|
28
|
-
for (const entry of entries)
|
|
29
|
-
callbacks.get(entry.target)?.forEach((cb) =>
|
|
30
|
-
setTimeout(() => {
|
|
31
|
-
cb(entry)
|
|
32
|
-
}, 0),
|
|
33
|
-
)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
observerSingleton = {
|
|
37
|
-
observer,
|
|
38
|
-
subscribe(target, callback) {
|
|
39
|
-
let cbs = callbacks.get(target)
|
|
40
|
-
|
|
41
|
-
if (!cbs) {
|
|
42
|
-
// If target has no observers yet - register it
|
|
43
|
-
cbs = new Set<UseResizeObserverCallback>()
|
|
44
|
-
callbacks.set(target, cbs)
|
|
45
|
-
observer.observe(target)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// As Set is duplicate-safe - simply add callback on each call
|
|
49
|
-
cbs.add(callback)
|
|
50
|
-
},
|
|
51
|
-
unsubscribe(target, callback) {
|
|
52
|
-
const cbs = callbacks.get(target)
|
|
53
|
-
|
|
54
|
-
// Else branch should never occur in case of normal execution
|
|
55
|
-
// because callbacks map is hidden in closure - it is impossible to
|
|
56
|
-
// simulate situation with non-existent `cbs` Set
|
|
57
|
-
/* istanbul ignore else */
|
|
58
|
-
if (cbs) {
|
|
59
|
-
// Remove current observer
|
|
60
|
-
cbs.delete(callback)
|
|
61
|
-
|
|
62
|
-
if (cbs.size === 0) {
|
|
63
|
-
// If no observers left unregister target completely
|
|
64
|
-
callbacks.delete(target)
|
|
65
|
-
observer.unobserve(target)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return observerSingleton
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Invokes a callback whenever ResizeObserver detects a change to target's size.
|
|
76
|
-
*
|
|
77
|
-
* @param target React reference or Element to track.
|
|
78
|
-
* @param callback Callback that will be invoked on resize.
|
|
79
|
-
* @param enabled Whether resize observer is enabled or not.
|
|
80
|
-
*/
|
|
81
|
-
export function useResizeObserver<T extends Element>(
|
|
82
|
-
target: RefObject<T | null>,
|
|
83
|
-
callback: UseResizeObserverCallback,
|
|
84
|
-
enabled = true,
|
|
85
|
-
): void {
|
|
86
|
-
const ro = enabled && getResizeObserver()
|
|
87
|
-
const cb = useRef(callback)
|
|
88
|
-
cb.current = callback
|
|
89
|
-
|
|
90
|
-
const tgt = target && 'current' in target ? target.current : target
|
|
91
|
-
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
// This secondary target resolve required for case when we receive ref object, which, most
|
|
94
|
-
// likely, contains null during render stage, but already populated with element during
|
|
95
|
-
// effect stage.
|
|
96
|
-
|
|
97
|
-
const _tgt = target && 'current' in target ? target.current : target
|
|
98
|
-
|
|
99
|
-
if (!ro || !_tgt) return undefined
|
|
100
|
-
|
|
101
|
-
// As unsubscription in internals of our ResizeObserver abstraction can
|
|
102
|
-
// happen a bit later than effect cleanup invocation - we need a marker,
|
|
103
|
-
// that this handler should not be invoked anymore
|
|
104
|
-
let subscribed = true
|
|
105
|
-
|
|
106
|
-
const handler: UseResizeObserverCallback = (...args) => {
|
|
107
|
-
// It is reinsurance for the highly asynchronous invocations, almost
|
|
108
|
-
// impossible to achieve in tests, thus excluding from LOC
|
|
109
|
-
/* istanbul ignore else */
|
|
110
|
-
if (subscribed) {
|
|
111
|
-
cb.current(...args)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
ro.subscribe(_tgt, handler)
|
|
116
|
-
|
|
117
|
-
return () => {
|
|
118
|
-
subscribed = false
|
|
119
|
-
ro.unsubscribe(_tgt, handler)
|
|
120
|
-
}
|
|
121
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
122
|
-
}, [tgt, ro])
|
|
123
|
-
}
|