sanity-plugin-hotspot-array 1.0.1 → 2.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/src/Spot.tsx CHANGED
@@ -1,10 +1,18 @@
1
- /* eslint-disable */
2
1
  import {Box, Text, Tooltip} from '@sanity/ui'
3
2
  import {motion, useMotionValue} from 'framer-motion'
4
- import get from 'lodash/get'
5
- import React, {ComponentType, CSSProperties, ReactElement, useEffect} from 'react'
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
+
6
15
  import {FnHotspotMove, HotspotItem} from './ImageHotspotArray'
7
- import {ObjectSchemaType, RenderPreviewCallback} from 'sanity'
8
16
 
9
17
  const dragStyle: CSSProperties = {
10
18
  width: '1.4rem',
@@ -91,9 +99,9 @@ export default function Spot({
91
99
  index,
92
100
  schemaType,
93
101
  renderPreview,
94
- }: HotspotProps) {
95
- const [isDragging, setIsDragging] = React.useState(false)
96
- const [isHovering, setIsHovering] = React.useState(false)
102
+ }: HotspotProps): ReactNode {
103
+ const [isDragging, setIsDragging] = useState(false)
104
+ const [isHovering, setIsHovering] = useState(false)
97
105
 
98
106
  // x/y are stored as % but need to be converted to px
99
107
  const x = useMotionValue(round(bounds.width * (value.x / 100)))
@@ -107,7 +115,7 @@ export default function Spot({
107
115
  y.set(round(bounds.height * (value.y / 100)))
108
116
  }, [x, y, value, bounds])
109
117
 
110
- const handleDragEnd = React.useCallback(() => {
118
+ const handleDragEnd = useCallback(() => {
111
119
  setIsDragging(false)
112
120
 
113
121
  // get current values for x/y in px
@@ -124,10 +132,10 @@ export default function Spot({
124
132
 
125
133
  update(value._key, safeX, safeY)
126
134
  }, [x, y, value, update, bounds])
127
- const handleDragStart = React.useCallback(() => setIsDragging(true), [])
135
+ const handleDragStart = useCallback(() => setIsDragging(true), [])
128
136
 
129
- const handleHoverStart = React.useCallback(() => setIsHovering(true), [])
130
- const handleHoverEnd = React.useCallback(() => setIsHovering(false), [])
137
+ const handleHoverStart = useCallback(() => setIsHovering(true), [])
138
+ const handleHoverEnd = useCallback(() => setIsHovering(false), [])
131
139
 
132
140
  if (!x || !y) {
133
141
  return null
@@ -140,7 +148,7 @@ export default function Spot({
140
148
  portal
141
149
  content={
142
150
  tooltip && typeof tooltip === 'function' ? (
143
- React.createElement(tooltip, {value, renderPreview, schemaType})
151
+ createElement(tooltip, {value, renderPreview, schemaType})
144
152
  ) : (
145
153
  <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>
146
154
  <Text textOverflow="ellipsis">
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'
2
- export {type HotspotTooltipProps} from './Spot'
3
-
4
2
  export {imageHotspotArrayPlugin, type ImageHotspotOptions} from './plugin'
3
+ export {type HotspotTooltipProps} from './Spot'
package/src/plugin.tsx CHANGED
@@ -1,6 +1,7 @@
1
- import {ImageHotspotArray, type HotspotItem} from './ImageHotspotArray'
2
- import React, {ComponentType} from 'react'
3
- import {ArrayOfObjectsInputProps, definePlugin} from 'sanity'
1
+ import {ComponentType} from 'react'
2
+ import {type ArrayOfObjectsInputProps, definePlugin} from 'sanity'
3
+
4
+ import {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'
4
5
  import {type HotspotTooltipProps} from './Spot'
5
6
 
6
7
  export interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {
@@ -0,0 +1,102 @@
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>>()
40
+ const waitTimeout = useRef<ReturnType<typeof setTimeout>>()
41
+ const cb = useRef(callback)
42
+ const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>()
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
+ }
@@ -0,0 +1,123 @@
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> | 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
+ }
package/lib/index.esm.js DELETED
@@ -1,2 +0,0 @@
1
- function e(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,i)}return o}function t(t){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){o(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function o(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}import{jsx as i,jsxs as r,Fragment as n}from"react/jsx-runtime";import{getImageDimensions as a}from"@sanity/asset-utils";import{useClient as l,useFormValue as s,PatchEvent as c,setIfMissing as d,insert as h,set as u,definePlugin as p}from"sanity";import m from"@sanity/image-url";import{Card as f,Text as g,Tooltip as y,Box as b,Stack as v,Flex as w}from"@sanity/ui";import{randomKey as P}from"@sanity/util/content";import x from"lodash/get";import O,{useEffect as k,useMemo as j,useCallback as _,useRef as D,useState as E}from"react";import{useDebouncedCallback as R,useResizeObserver as H}from"@react-hookz/web";import{useMotionValue as T,motion as C}from"framer-motion";function S(e){let{children:t}=e;return i(f,{padding:4,radius:2,shadow:1,tone:"caution",children:i(g,{children:t})})}const M={width:"1.4rem",height:"1.4rem",position:"absolute",boxSizing:"border-box",top:0,left:0,margin:"-0.7rem 0 0 -0.7rem",cursor:"pointer",display:"flex",justifyContent:"center",alignItems:"center",borderRadius:"50%",background:"#000",color:"white"},N={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff",cursor:"none"},z={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff"},F={position:"absolute",left:"50%",top:"50%",height:"0.2rem",width:"0.2rem",margin:"-0.1rem 0 0 -0.1rem",background:"#fff",visibility:"hidden",borderRadius:"50%",pointerEvents:"none"},I={visibility:"visible"},W={color:"white",fontSize:"0.7rem",fontWeight:600,lineHeight:"1"},A={visibility:"hidden"},V=e=>Math.round(100*e)/100;function X(e){let{value:o,bounds:n,update:a,hotspotDescriptionPath:l,tooltip:s,index:c,schemaType:d,renderPreview:h}=e;const[u,p]=O.useState(!1),[m,f]=O.useState(!1),v=T(V(n.width*(o.x/100))),w=T(V(n.height*(o.y/100)));k((()=>{v.set(V(n.width*(o.x/100))),w.set(V(n.height*(o.y/100)))}),[v,w,o,n]);const P=O.useCallback((()=>{p(!1);const e=v.get(),t=w.get(),i=V(100*e/n.width),r=V(100*t/n.height),l=Math.max(0,Math.min(100,i)),s=Math.max(0,Math.min(100,r));a(o._key,l,s)}),[v,w,o,a,n]),j=O.useCallback((()=>p(!0)),[]),_=O.useCallback((()=>f(!0)),[]),D=O.useCallback((()=>f(!1)),[]);return v&&w?i(y,{disabled:u,portal:!0,content:s&&"function"==typeof s?O.createElement(s,{value:o,renderPreview:h,schemaType:d}):i(b,{padding:2,style:{maxWidth:200,pointerEvents:"none"},children:i(g,{textOverflow:"ellipsis",children:l?x(o,l):"".concat(o.x,"% x ").concat(o.y,"%")})}),children:r(C.div,{drag:!0,dragConstraints:n,dragElastic:0,dragMomentum:!1,onDragEnd:P,onDragStart:j,onHoverStart:_,onHoverEnd:D,style:t(t(t({},M),{},{x:v,y:w},u&&t({},N)),m&&t({},z)),children:[i(b,{style:t(t({},F),(u||m)&&t({},I))}),i("div",{style:t(t({},W),(u||m)&&t({},A)),children:c+1})]})},o._key):null}const Y={width:"100%",height:"auto"},q=["document","parent"];function B(e){var t;const{value:o,onChange:p,imageHotspotOptions:g,schemaType:y,renderPreview:b}=e,O=l({apiVersion:"2022-01-01"}),k=j((()=>{var t;return"document"===(q.includes(null!=(t=g.pathRoot)?t:"")?g.pathRoot:"document")?[]:e.path.slice(0,-1)}),[g.pathRoot,e.path]),T=s(k),C=j((()=>x(T,g.imagePath)),[T,g.imagePath]),M=j((()=>{var e,t;const o=m(O).dataset(null!=(e=O.config().dataset)?e:"");if(null==(t=null==C?void 0:C.asset)?void 0:t._ref){const{aspectRatio:e}=a(C.asset._ref),t=1200;return{width:t,height:Math.round(t/e),url:(i=C,o.image(i)).width(t).url()}}var i;return null}),[C,O]),N=_((e=>{const{nativeEvent:t}=e,o=Number((100*t.offsetX/t.srcElement.width).toFixed(2)),i=Number((100*t.offsetY/t.srcElement.height).toFixed(2)),r="New Hotspot at ".concat(o,"% x ").concat(i,"%"),n={_key:P(12),_type:y.of[0].name,x:o,y:i};(null==g?void 0:g.descriptionPath)&&(n[g.descriptionPath]=r),p(c.from([d([]),h([n],"after",[-1])]))}),[g,p,y]),z=_(((e,t,o)=>{p(c.from([u(t,[{_key:e},"x"]),u(o,[{_key:e},"y"])]))}),[p]),F=D(null),[I,W]=E(),A=R((e=>W(e.contentRect)),[W],200);return H(F,A),r(v,{space:[2,2,3],children:[(null==M?void 0:M.url)?r("div",{style:{position:"relative"},children:[I&&(null!=(t=null==o?void 0:o.length)?t:0)>0&&(null==o?void 0:o.map(((e,t)=>i(X,{index:t,value:e,bounds:I,update:z,hotspotDescriptionPath:null==g?void 0:g.descriptionPath,tooltip:null==g?void 0:g.tooltip,renderPreview:b,schemaType:y.of[0]},e._key)))),i(f,{__unstable_checkered:!0,shadow:1,children:i(w,{align:"center",justify:"center",children:i("img",{ref:F,src:M.url,width:M.width,height:M.height,alt:"",style:Y,onClick:N})})})]}):i(S,{children:g.imagePath?r(n,{children:["No Hotspot image found at path ",i("code",{children:g.imagePath})]}):r(n,{children:["Define a path in this field using to the image field in this document at"," ",i("code",{children:"options.hotspotImagePath"})]})}),g.pathRoot&&!q.includes(g.pathRoot)&&r(S,{children:['The supplied imageHotspotPathRoot "',g.pathRoot,'" is not valid, falling back to "document". Available values are "',q.join(", "),'".']}),e.renderDefault(e)]})}const G=p({name:"image-hotspot-array",form:{components:{input:e=>{var o,r;if("array"===e.schemaType.jsonType&&(null==(o=e.schemaType.options)?void 0:o.imageHotspot)){const o=null==(r=e.schemaType.options)?void 0:r.imageHotspot;if(o)return i(B,t(t({},e),{},{imageHotspotOptions:o}))}return e.renderDefault(e)}}}});export{B as ImageHotspotArray,G as imageHotspotArrayPlugin};
2
- //# sourceMappingURL=index.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import React, {ReactNode} from 'react'\nimport {Card, Text} from '@sanity/ui'\n\nexport default function Feedback({children}: {children: ReactNode}) {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","/* eslint-disable */\nimport {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport get from 'lodash/get'\nimport React, {ComponentType, CSSProperties, ReactElement, useEffect} from 'react'\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\nimport {ObjectSchemaType, RenderPreviewCallback} from 'sanity'\n\nconst dragStyle: CSSProperties = {\n width: '1.4rem',\n height: '1.4rem',\n position: 'absolute',\n boxSizing: 'border-box',\n top: 0,\n left: 0,\n margin: '-0.7rem 0 0 -0.7rem',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: '50%',\n background: '#000',\n color: 'white',\n}\n\nconst dragStyleWhileDrag: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n cursor: 'none',\n}\n\nconst dragStyleWhileHover: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n}\n\nconst dotStyle: CSSProperties = {\n position: 'absolute',\n left: '50%',\n top: '50%',\n height: '0.2rem',\n width: '0.2rem',\n margin: '-0.1rem 0 0 -0.1rem',\n background: '#fff',\n visibility: 'hidden',\n borderRadius: '50%',\n // make sure pointer events only run on the parent\n pointerEvents: 'none',\n}\n\nconst dotStyleWhileActive: CSSProperties = {\n visibility: 'visible',\n}\n\nconst labelStyle: CSSProperties = {\n color: 'white',\n fontSize: '0.7rem',\n fontWeight: 600,\n lineHeight: '1',\n}\n\nconst labelStyleWhileActive: CSSProperties = {\n visibility: 'hidden',\n}\n\nconst round = (num: number) => Math.round(num * 100) / 100\n\nexport interface HotspotTooltipProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem<HotspotFields>\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\ninterface HotspotProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem\n bounds: DOMRectReadOnly\n update: FnHotspotMove\n hotspotDescriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n index: number\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\nexport default function Spot({\n value,\n bounds,\n update,\n hotspotDescriptionPath,\n tooltip,\n index,\n schemaType,\n renderPreview,\n}: HotspotProps) {\n const [isDragging, setIsDragging] = React.useState(false)\n const [isHovering, setIsHovering] = React.useState(false)\n\n // x/y are stored as % but need to be converted to px\n const x = useMotionValue(round(bounds.width * (value.x / 100)))\n const y = useMotionValue(round(bounds.height * (value.y / 100)))\n\n /**\n * update x/y if the bounds change when resizing the window\n */\n useEffect(() => {\n x.set(round(bounds.width * (value.x / 100)))\n y.set(round(bounds.height * (value.y / 100)))\n }, [x, y, value, bounds])\n\n const handleDragEnd = React.useCallback(() => {\n setIsDragging(false)\n\n // get current values for x/y in px\n const currentX = x.get()\n const currentY = y.get()\n\n // Which we need to convert back to `%` to patch the document\n const newX = round((currentX * 100) / bounds.width)\n const newY = round((currentY * 100) / bounds.height)\n\n // Don't go below 0 or above 100\n const safeX = Math.max(0, Math.min(100, newX))\n const safeY = Math.max(0, Math.min(100, newY))\n\n update(value._key, safeX, safeY)\n }, [x, y, value, update, bounds])\n const handleDragStart = React.useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = React.useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = React.useCallback(() => setIsHovering(false), [])\n\n if (!x || !y) {\n return null\n }\n\n return (\n <Tooltip\n key={value._key}\n disabled={isDragging}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n React.createElement(tooltip, {value, renderPreview, schemaType})\n ) : (\n <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>\n <Text textOverflow=\"ellipsis\">\n {hotspotDescriptionPath\n ? (get(value, hotspotDescriptionPath) as string)\n : `${value.x}% x ${value.y}%`}\n </Text>\n </Box>\n )\n }\n >\n <motion.div\n drag\n dragConstraints={bounds}\n dragElastic={0}\n dragMomentum={false}\n onDragEnd={handleDragEnd}\n onDragStart={handleDragStart}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n style={{\n ...dragStyle,\n x,\n y,\n ...(isDragging && {...dragStyleWhileDrag}),\n ...(isHovering && {...dragStyleWhileHover}),\n }}\n >\n {/* Dot */}\n <Box\n style={{\n ...dotStyle,\n ...((isDragging || isHovering) && {...dotStyleWhileActive}),\n }}\n />\n {/* Label */}\n <div\n style={{\n ...labelStyle,\n ...((isDragging || isHovering) && {...labelStyleWhileActive}),\n }}\n >\n {index + 1}\n </div>\n </motion.div>\n </Tooltip>\n )\n}\n","/* eslint-disable react/display-name */\n\nimport {getImageDimensions} from '@sanity/asset-utils'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n} from 'sanity'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport get from 'lodash/get'\nimport React, {useCallback, useMemo, useRef, useState} from 'react'\n\nimport {IUseResizeObserverCallback, useDebouncedCallback, useResizeObserver} from '@react-hookz/web'\nimport Feedback from './Feedback'\nimport Spot from './Spot'\nimport {ImageHotspotOptions} from './plugin'\n\nconst imageStyle = {width: `100%`, height: `auto`}\n\nconst VALID_ROOT_PATHS = ['document', 'parent']\n\nexport type FnHotspotMove = (key: string, x: number, y: number) => void\n\nexport type HotspotItem<HotspotFields = {[key: string]: unknown}> = {\n _key: string\n _type: string\n x: number\n y: number\n} & HotspotFields\n\nexport function ImageHotspotArray(\n props: ArrayOfObjectsInputProps<HotspotItem> & {imageHotspotOptions: ImageHotspotOptions}\n) {\n const {value, onChange, imageHotspotOptions, schemaType, renderPreview} = props\n\n const sanityClient = useClient({apiVersion: '2022-01-01'})\n\n const imageHotspotPathRoot = useMemo(() => {\n const pathRoot = VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? '')\n ? imageHotspotOptions.pathRoot\n : 'document'\n return pathRoot === 'document' ? [] : props.path.slice(0, -1)\n }, [imageHotspotOptions.pathRoot, props.path])\n\n const rootObject = useFormValue(imageHotspotPathRoot)\n\n /**\n * Finding the image from the imageHotspotPathRoot (defaults to document),\n * using the path from the hotspot's `options` field\n *\n * when changes in imageHotspotPathRoot (e.g. document) occur,\n * check if there are any changes to the hotspotImage and update the reference\n */\n const hotspotImage = useMemo(() => {\n return get(rootObject, imageHotspotOptions.imagePath) as ImageValue | undefined\n }, [rootObject, imageHotspotOptions.imagePath])\n\n const displayImage = useMemo(() => {\n const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? '')\n const urlFor = (source: ImageValue) => builder.image(source)\n\n if (hotspotImage?.asset?._ref) {\n const {aspectRatio} = getImageDimensions(hotspotImage.asset._ref)\n const width = 1200\n const height = Math.round(width / aspectRatio)\n const url = urlFor(hotspotImage).width(width).url()\n\n return {width, height, url}\n }\n\n return null\n }, [hotspotImage, sanityClient])\n\n const handleHotspotImageClick = useCallback(\n (event: any) => {\n const {nativeEvent} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / nativeEvent.srcElement.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / nativeEvent.srcElement.height).toFixed(2))\n const description = `New Hotspot at ${x}% x ${y}%`\n\n const newRow: HotspotItem = {\n _key: randomKey(12),\n _type: schemaType.of[0].name,\n x,\n y,\n }\n\n if (imageHotspotOptions?.descriptionPath) {\n newRow[imageHotspotOptions.descriptionPath] = description\n }\n\n onChange(PatchEvent.from([setIfMissing([]), insert([newRow], 'after', [-1])]))\n },\n [imageHotspotOptions, onChange, schemaType]\n )\n\n const handleHotspotMove: FnHotspotMove = useCallback(\n (key, x, y) => {\n onChange(\n PatchEvent.from([\n // Set the `x` value of this array key item\n set(x, [{_key: key}, 'x']),\n // Set the `y` value of this array key item\n set(y, [{_key: key}, 'y']),\n ])\n )\n },\n [onChange]\n )\n\n const hotspotImageRef = useRef<HTMLImageElement | null>(null)\n\n const [imageRect, setImageRect] = useState<DOMRectReadOnly>()\n const updateImageRectCallback = useDebouncedCallback(\n ((e) => setImageRect(e.contentRect)) as IUseResizeObserverCallback,\n [setImageRect],\n 200\n )\n useResizeObserver(hotspotImageRef, updateImageRectCallback)\n\n return (\n <Stack space={[2, 2, 3]}>\n {displayImage?.url ? (\n <div style={{position: `relative`}}>\n {imageRect &&\n (value?.length ?? 0) > 0 &&\n value?.map((spot, index) => (\n <Spot\n index={index}\n key={spot._key}\n value={spot}\n bounds={imageRect}\n update={handleHotspotMove}\n hotspotDescriptionPath={imageHotspotOptions?.descriptionPath}\n tooltip={imageHotspotOptions?.tooltip}\n renderPreview={renderPreview}\n schemaType={schemaType.of[0] as ObjectSchemaType}\n />\n ))}\n\n <Card __unstable_checkered shadow={1}>\n <Flex align=\"center\" justify=\"center\">\n <img\n ref={hotspotImageRef}\n src={displayImage.url}\n width={displayImage.width}\n height={displayImage.height}\n alt=\"\"\n style={imageStyle}\n onClick={handleHotspotImageClick}\n />\n </Flex>\n </Card>\n </div>\n ) : (\n <Feedback>\n {imageHotspotOptions.imagePath ? (\n <>\n No Hotspot image found at path <code>{imageHotspotOptions.imagePath}</code>\n </>\n ) : (\n <>\n Define a path in this field using to the image field in this document at{' '}\n <code>options.hotspotImagePath</code>\n </>\n )}\n </Feedback>\n )}\n {imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && (\n <Feedback>\n The supplied imageHotspotPathRoot \"{imageHotspotOptions.pathRoot}\" is not valid, falling\n back to \"document\". Available values are \"{VALID_ROOT_PATHS.join(', ')}\".\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ImageHotspotArray, type HotspotItem} from './ImageHotspotArray'\nimport React, {ComponentType} from 'react'\nimport {ArrayOfObjectsInputProps, definePlugin} from 'sanity'\nimport {type HotspotTooltipProps} from './Spot'\n\nexport interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {\n pathRoot?: 'document' | 'parent'\n imagePath: string\n descriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n}\n\ndeclare module '@sanity/types' {\n export interface ArrayOptions {\n imageHotspot?: ImageHotspotOptions\n }\n}\n\nexport const imageHotspotArrayPlugin = definePlugin({\n name: 'image-hotspot-array',\n form: {\n components: {\n input: (props) => {\n if (props.schemaType.jsonType === 'array' && props.schemaType.options?.imageHotspot) {\n const imageHotspotOptions = props.schemaType.options?.imageHotspot\n if (imageHotspotOptions) {\n return (\n <ImageHotspotArray\n {...(props as unknown as ArrayOfObjectsInputProps<HotspotItem>)}\n imageHotspotOptions={imageHotspotOptions}\n />\n )\n }\n }\n return props.renderDefault(props)\n },\n },\n },\n})\n"],"names":["Feedback","_ref","children","jsx","Card","padding","radius","shadow","tone","Text","dragStyle","width","height","position","boxSizing","top","left","margin","cursor","display","justifyContent","alignItems","borderRadius","background","color","dragStyleWhileDrag","border","dragStyleWhileHover","dotStyle","visibility","pointerEvents","dotStyleWhileActive","labelStyle","fontSize","fontWeight","lineHeight","labelStyleWhileActive","round","num","Math","Spot","_ref2","value","bounds","update","hotspotDescriptionPath","tooltip","index","schemaType","renderPreview","isDragging","setIsDragging","React","useState","isHovering","setIsHovering","x","useMotionValue","y","useEffect","set","handleDragEnd","useCallback","currentX","get","currentY","newX","newY","safeX","max","min","safeY","_key","handleDragStart","handleHoverStart","handleHoverEnd","Tooltip","disabled","portal","content","createElement","Box","style","maxWidth","textOverflow","jsxs","motion","div","drag","dragConstraints","dragElastic","dragMomentum","onDragEnd","onDragStart","onHoverStart","onHoverEnd","_objectSpread","imageStyle","VALID_ROOT_PATHS","ImageHotspotArray","props","_a","onChange","imageHotspotOptions","sanityClient","useClient","apiVersion","imageHotspotPathRoot","useMemo","includes","pathRoot","path","slice","rootObject","useFormValue","hotspotImage","imagePath","displayImage","_b","builder","imageUrlBuilder","dataset","config","asset","aspectRatio","getImageDimensions","url","source","image","handleHotspotImageClick","event","nativeEvent","Number","offsetX","srcElement","toFixed","offsetY","description","concat","newRow","randomKey","_type","of","name","descriptionPath","PatchEvent","from","setIfMissing","insert","handleHotspotMove","key","hotspotImageRef","useRef","imageRect","setImageRect","updateImageRectCallback","useDebouncedCallback","e","contentRect","useResizeObserver","Stack","space","length","map","spot","__unstable_checkered","Flex","align","justify","ref","src","alt","onClick","Fragment","join","renderDefault","imageHotspotArrayPlugin","definePlugin","form","components","input","jsonType","options","imageHotspot"],"mappings":"w1CAGwB,SAAAA,EAA4CC,GAAA,IAAnCC,SAACA,GAAkCD,EAClE,OACGE,EAAAC,EAAA,CAAKC,QAAS,EAAGC,OAAQ,EAAGC,OAAQ,EAAGC,KAAK,UAC3CN,SAACC,EAAAM,EAAA,CAAMP,cAGb,CCDA,MAAMQ,EAA2B,CAC/BC,MAAO,SACPC,OAAQ,SACRC,SAAU,WACVC,UAAW,aACXC,IAAK,EACLC,KAAM,EACNC,OAAQ,sBACRC,OAAQ,UACRC,QAAS,OACTC,eAAgB,SAChBC,WAAY,SACZC,aAAc,MACdC,WAAY,OACZC,MAAO,SAGHC,EAAoC,CACxCF,WAAY,qBACZG,OAAQ,iBACRR,OAAQ,QAGJS,EAAqC,CACzCJ,WAAY,qBACZG,OAAQ,kBAGJE,EAA0B,CAC9Bf,SAAU,WACVG,KAAM,MACND,IAAK,MACLH,OAAQ,SACRD,MAAO,SACPM,OAAQ,sBACRM,WAAY,OACZM,WAAY,SACZP,aAAc,MAEdQ,cAAe,QAGXC,EAAqC,CACzCF,WAAY,WAGRG,EAA4B,CAChCR,MAAO,QACPS,SAAU,SACVC,WAAY,IACZC,WAAY,KAGRC,EAAuC,CAC3CP,WAAY,UAGRQ,EAASC,GAAgBC,KAAKF,MAAY,IAANC,GAAa,IAmBvD,SAAwBE,EASPC,GAAA,IATYC,MAC3BA,EAAAC,OACAA,EAAAC,OACAA,EAAAC,uBACAA,EAAAC,QACAA,EAAAC,MACAA,EAAAC,WACAA,EAAAC,cACAA,GACeR,EACf,MAAOS,EAAYC,GAAiBC,EAAMC,UAAS,IAC5CC,EAAYC,GAAiBH,EAAMC,UAAS,GAG7CG,EAAIC,EAAepB,EAAMM,EAAOhC,OAAS+B,EAAMc,EAAI,OACnDE,EAAID,EAAepB,EAAMM,EAAO/B,QAAU8B,EAAMgB,EAAI,OAK1DC,GAAU,KACRH,EAAEI,IAAIvB,EAAMM,EAAOhC,OAAS+B,EAAMc,EAAI,OACtCE,EAAEE,IAAIvB,EAAMM,EAAO/B,QAAU8B,EAAMgB,EAAI,MAAK,GAC3C,CAACF,EAAGE,EAAGhB,EAAOC,IAEX,MAAAkB,EAAgBT,EAAMU,aAAY,KACtCX,GAAc,GAGR,MAAAY,EAAWP,EAAEQ,MACbC,EAAWP,EAAEM,MAGbE,EAAO7B,EAAkB,IAAX0B,EAAkBpB,EAAOhC,OACvCwD,EAAO9B,EAAkB,IAAX4B,EAAkBtB,EAAO/B,QAGvCwD,EAAQ7B,KAAK8B,IAAI,EAAG9B,KAAK+B,IAAI,IAAKJ,IAClCK,EAAQhC,KAAK8B,IAAI,EAAG9B,KAAK+B,IAAI,IAAKH,IAEjCvB,EAAAF,EAAM8B,KAAMJ,EAAOG,EAAK,GAC9B,CAACf,EAAGE,EAAGhB,EAAOE,EAAQD,IACnB8B,EAAkBrB,EAAMU,aAAY,IAAMX,GAAc,IAAO,IAE/DuB,EAAmBtB,EAAMU,aAAY,IAAMP,GAAc,IAAO,IAChEoB,EAAiBvB,EAAMU,aAAY,IAAMP,GAAc,IAAQ,IAEjE,OAACC,GAAME,EAKRvD,EAAAyE,EAAA,CAECC,SAAU3B,EACV4B,QAAM,EACNC,QACEjC,GAA8B,mBAAZA,EAChBM,EAAM4B,cAAclC,EAAS,CAACJ,QAAOO,gBAAeD,eAEnD7C,EAAA8E,EAAA,CAAI5E,QAAS,EAAG6E,MAAO,CAACC,SAAU,IAAKrD,sBACtC5B,SAACC,EAAAM,EAAA,CAAK2E,aAAa,WAChBlF,SAAA2C,EACImB,EAAItB,EAAOG,aACTH,EAAMc,EAAQd,QAAAA,OAAAA,EAAMgB,EAAA,SAMnCxD,SAAAmF,EAACC,EAAOC,IAAP,CACCC,MAAI,EACJC,gBAAiB9C,EACjB+C,YAAa,EACbC,cAAc,EACdC,UAAW/B,EACXgC,YAAapB,EACbqB,aAAcpB,EACdqB,WAAYpB,EACZO,eACKxE,GAAA,CAAA,EAAA,CACH8C,IACAE,KACIR,QAAkBzB,IAClB6B,GAAA0C,EAAA,CAAA,EAAkBrE,IAIxBzB,SAAA,CAACC,EAAA8E,EAAA,CACCC,MAAOc,EAAAA,EAAA,CAAA,EACFpE,IACEsB,GAAcI,IAAmBvB,EAAAA,CAAAA,EAAAA,MAIzC5B,EAAA,MAAA,CACC+E,MAAOc,EAAAA,EAAA,CAAA,EACFhE,IACEkB,GAAcI,IAAe0C,EAAA,CAAA,EAAI5D,IAGvClC,SAAQ6C,EAAA,QAhDRL,EAAM8B,MALN,IA0DX,CCrKA,MAAMyB,EAAa,CAACtF,MAAO,OAAQC,eAE7BsF,EAAmB,CAAC,WAAY,UAW/B,SAASC,EACdC,GAvCF,IAAAC,EAyCE,MAAM3D,MAACA,EAAO4D,SAAAA,EAAAC,oBAAUA,EAAqBvD,WAAAA,EAAAC,cAAYA,GAAiBmD,EAEpEI,EAAeC,EAAU,CAACC,WAAY,eAEtCC,EAAuBC,GAAQ,KA7CvCP,IAAAA,EAiDW,MAAa,cAHHH,EAAiBW,SAAS,OAAAR,EAAAE,EAAoBO,UAApBT,EAAgC,IACvEE,EAAoBO,SACpB,YAC6B,GAAKV,EAAMW,KAAKC,MAAM,GAAK,EAAA,GAC3D,CAACT,EAAoBO,SAAUV,EAAMW,OAElCE,EAAaC,EAAaP,GAS1BQ,EAAeP,GAAQ,IACpB5C,EAAIiD,EAAYV,EAAoBa,YAC1C,CAACH,EAAYV,EAAoBa,YAE9BC,EAAeT,GAAQ,KAjE/B,IAAAP,EAAAiB,EAkEI,MAAMC,EAAUC,EAAgBhB,GAAciB,QAAQ,OAAApB,EAAAG,EAAakB,SAASD,SAAtBpB,EAAiC,IAGnF,GAAA,OAAAiB,EAAA,MAAAH,OAAA,EAAAA,EAAcQ,YAAd,EAAAL,EAAqBrH,KAAM,CAC7B,MAAM2H,YAACA,GAAeC,EAAmBV,EAAaQ,MAAM1H,MACtDU,EAAQ,KAIP,MAAA,CAACA,QAAOC,OAHA2B,KAAKF,MAAM1B,EAAQiH,GAGXE,KARTC,EAMKZ,EANkBI,EAAQS,MAAMD,IAMlBpH,MAAMA,GAAOmH,MAGhD,CATgBC,MAWT,OAAA,IAAA,GACN,CAACZ,EAAcX,IAEZyB,EAA0BnE,GAC7BoE,IACO,MAAAC,YAACA,GAAeD,EAGhB1E,EAAI4E,QAA+B,IAAtBD,EAAYE,QAAiBF,EAAYG,WAAW3H,OAAO4H,QAAQ,IAChF7E,EAAI0E,QAA+B,IAAtBD,EAAYK,QAAiBL,EAAYG,WAAW1H,QAAQ2H,QAAQ,IACjFE,EAAA,kBAAAC,OAAgClF,EAAQ,QAAAkF,OAAAhF,EAAA,KAExCiF,EAAsB,CAC1BnE,KAAMoE,EAAU,IAChBC,MAAO7F,EAAW8F,GAAG,GAAGC,KACxBvF,IACAE,YAGE6C,WAAqByC,mBACvBL,EAAOpC,EAAoByC,iBAAmBP,GAGhDnC,EAAS2C,EAAWC,KAAK,CAACC,EAAa,IAAKC,EAAO,CAACT,GAAS,QAAS,QAAO,GAE/E,CAACpC,EAAqBD,EAAUtD,IAG5BqG,EAAmCvF,GACvC,CAACwF,EAAK9F,EAAGE,KACP4C,EACE2C,EAAWC,KAAK,CAEdtF,EAAIJ,EAAG,CAAC,CAACgB,KAAM8E,GAAM,MAErB1F,EAAIF,EAAG,CAAC,CAACc,KAAM8E,GAAM,QAEzB,GAEF,CAAChD,IAGGiD,EAAkBC,EAAgC,OAEjDC,EAAWC,GAAgBrG,IAC5BsG,EAA0BC,GAC5BC,GAAMH,EAAaG,EAAEC,cACvB,CAACJ,GACD,KAIF,OAFAK,EAAkBR,EAAiBI,GAGhCtE,EAAA2E,EAAA,CAAMC,MAAO,CAAC,EAAG,EAAG,GAClB/J,SAAA,EAAA,MAAAmH,OAAA,EAAAA,EAAcS,KACZzC,EAAA,MAAA,CAAIH,MAAO,CAACrE,SAAA,YACVX,SAAA,CACEuJ,IAAA,OAAApD,EAAA,MAAA3D,OAAA,EAAAA,EAAOwH,QAAP7D,EAAiB,GAAK,UACvB3D,WAAOyH,KAAI,CAACC,EAAMrH,IACf5C,EAAAqC,EAAA,CACCO,QAEAL,MAAO0H,EACPzH,OAAQ8G,EACR7G,OAAQyG,EACRxG,uBAA6C,MAArB0D,OAAqB,EAAAA,EAAAyC,gBAC7ClG,QAA8B,MAArByD,OAAqB,EAAAA,EAAAzD,QAC9BG,gBACAD,WAAYA,EAAW8F,GAAG,IAPrBsB,EAAK5F,SAWfrE,EAAAC,EAAA,CAAKiK,sBAAoB,EAAC9J,OAAQ,EACjCL,SAACC,EAAAmK,EAAA,CAAKC,MAAM,SAASC,QAAQ,SAC3BtK,SAACC,EAAA,MAAA,CACCsK,IAAKlB,EACLmB,IAAKrD,EAAaS,IAClBnH,MAAO0G,EAAa1G,MACpBC,OAAQyG,EAAazG,OACrB+J,IAAI,GACJzF,MAAOe,EACP2E,QAAS3C,WAMhB9H,EAAAH,EAAA,CACEE,WAAoBkH,UACnB/B,EAAAwF,EAAA,CAAE3K,SAAA,CAAA,kCACgCC,EAAA,OAAA,CAAMD,SAAoBqG,EAAAa,eAG5D/B,EAAAwF,EAAA,CAAE3K,SAAA,CAAA,2EACyE,IACxEC,EAAA,OAAA,CAAKD,SAAA,kCAKbqG,EAAoBO,WAAaZ,EAAiBW,SAASN,EAAoBO,WAC7EzB,EAAArF,EAAA,CAASE,SAAA,CAAA,sCAC4BqG,EAAoBO,SAAS,qEACtBZ,EAAiB4E,KAAK,MAAM,QAG1E1E,EAAM2E,cAAc3E,KAG3B,CCzKO,MAAM4E,EAA0BC,EAAa,CAClDlC,KAAM,sBACNmC,KAAM,CACJC,WAAY,CACVC,MAAQhF,IAtBd,IAAAC,EAAAiB,EAuBY,GAA8B,UAA9BlB,EAAMpD,WAAWqI,WAAwB,OAAAhF,IAAMrD,WAAWsI,kBAASC,cAAc,CACnF,MAAMhF,EAAsB,OAAAe,EAAAlB,EAAMpD,WAAWsI,cAAS,EAAAhE,EAAAiE,aACtD,GAAIhF,EACF,OACGpG,EAAAgG,SACMC,GAAA,CAAA,EAAA,CACLG,wBAIR,CACO,OAAAH,EAAM2E,cAAc3E,EAAK"}
package/lib/index.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";function e(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,r)}return i}function t(t){for(var r=1;r<arguments.length;r++){var n=null!=arguments[r]?arguments[r]:{};r%2?e(Object(n),!0).forEach((function(e){i(t,e,n[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):e(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))}))}return t}function i(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}Object.defineProperty(exports,"__esModule",{value:!0});var r=require("react/jsx-runtime"),n=require("@sanity/asset-utils"),o=require("sanity"),a=require("@sanity/image-url"),s=require("@sanity/ui"),l=require("@sanity/util/content"),u=require("lodash/get"),c=require("react"),d=require("@react-hookz/web"),h=require("framer-motion");function p(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=p(a),m=p(u),g=p(c);function y(e){let{children:t}=e;return r.jsx(s.Card,{padding:4,radius:2,shadow:1,tone:"caution",children:r.jsx(s.Text,{children:t})})}const b={width:"1.4rem",height:"1.4rem",position:"absolute",boxSizing:"border-box",top:0,left:0,margin:"-0.7rem 0 0 -0.7rem",cursor:"pointer",display:"flex",justifyContent:"center",alignItems:"center",borderRadius:"50%",background:"#000",color:"white"},v={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff",cursor:"none"},x={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff"},j={position:"absolute",left:"50%",top:"50%",height:"0.2rem",width:"0.2rem",margin:"-0.1rem 0 0 -0.1rem",background:"#fff",visibility:"hidden",borderRadius:"50%",pointerEvents:"none"},w={visibility:"visible"},P={color:"white",fontSize:"0.7rem",fontWeight:600,lineHeight:"1"},k={visibility:"hidden"},O=e=>Math.round(100*e)/100;function C(e){let{value:i,bounds:n,update:o,hotspotDescriptionPath:a,tooltip:l,index:u,schemaType:d,renderPreview:p}=e;const[f,y]=g.default.useState(!1),[C,E]=g.default.useState(!1),M=h.useMotionValue(O(n.width*(i.x/100))),_=h.useMotionValue(O(n.height*(i.y/100)));c.useEffect((()=>{M.set(O(n.width*(i.x/100))),_.set(O(n.height*(i.y/100)))}),[M,_,i,n]);const D=g.default.useCallback((()=>{y(!1);const e=M.get(),t=_.get(),r=O(100*e/n.width),a=O(100*t/n.height),s=Math.max(0,Math.min(100,r)),l=Math.max(0,Math.min(100,a));o(i._key,s,l)}),[M,_,i,o,n]),R=g.default.useCallback((()=>y(!0)),[]),H=g.default.useCallback((()=>E(!0)),[]),T=g.default.useCallback((()=>E(!1)),[]);return M&&_?r.jsx(s.Tooltip,{disabled:f,portal:!0,content:l&&"function"==typeof l?g.default.createElement(l,{value:i,renderPreview:p,schemaType:d}):r.jsx(s.Box,{padding:2,style:{maxWidth:200,pointerEvents:"none"},children:r.jsx(s.Text,{textOverflow:"ellipsis",children:a?m.default(i,a):"".concat(i.x,"% x ").concat(i.y,"%")})}),children:r.jsxs(h.motion.div,{drag:!0,dragConstraints:n,dragElastic:0,dragMomentum:!1,onDragEnd:D,onDragStart:R,onHoverStart:H,onHoverEnd:T,style:t(t(t({},b),{},{x:M,y:_},f&&t({},v)),C&&t({},x)),children:[r.jsx(s.Box,{style:t(t({},j),(f||C)&&t({},w))}),r.jsx("div",{style:t(t({},P),(f||C)&&t({},k)),children:u+1})]})},i._key):null}const E={width:"100%",height:"auto"},M=["document","parent"];function _(e){var t;const{value:i,onChange:a,imageHotspotOptions:u,schemaType:h,renderPreview:p}=e,g=o.useClient({apiVersion:"2022-01-01"}),b=c.useMemo((()=>{var t;return"document"===(M.includes(null!=(t=u.pathRoot)?t:"")?u.pathRoot:"document")?[]:e.path.slice(0,-1)}),[u.pathRoot,e.path]),v=o.useFormValue(b),x=c.useMemo((()=>m.default(v,u.imagePath)),[v,u.imagePath]),j=c.useMemo((()=>{var e,t;const i=f.default(g).dataset(null!=(e=g.config().dataset)?e:"");if(null==(t=null==x?void 0:x.asset)?void 0:t._ref){const{aspectRatio:e}=n.getImageDimensions(x.asset._ref),t=1200;return{width:t,height:Math.round(t/e),url:(r=x,i.image(r)).width(t).url()}}var r;return null}),[x,g]),w=c.useCallback((e=>{const{nativeEvent:t}=e,i=Number((100*t.offsetX/t.srcElement.width).toFixed(2)),r=Number((100*t.offsetY/t.srcElement.height).toFixed(2)),n="New Hotspot at ".concat(i,"% x ").concat(r,"%"),s={_key:l.randomKey(12),_type:h.of[0].name,x:i,y:r};(null==u?void 0:u.descriptionPath)&&(s[u.descriptionPath]=n),a(o.PatchEvent.from([o.setIfMissing([]),o.insert([s],"after",[-1])]))}),[u,a,h]),P=c.useCallback(((e,t,i)=>{a(o.PatchEvent.from([o.set(t,[{_key:e},"x"]),o.set(i,[{_key:e},"y"])]))}),[a]),k=c.useRef(null),[O,_]=c.useState(),D=d.useDebouncedCallback((e=>_(e.contentRect)),[_],200);return d.useResizeObserver(k,D),r.jsxs(s.Stack,{space:[2,2,3],children:[(null==j?void 0:j.url)?r.jsxs("div",{style:{position:"relative"},children:[O&&(null!=(t=null==i?void 0:i.length)?t:0)>0&&(null==i?void 0:i.map(((e,t)=>r.jsx(C,{index:t,value:e,bounds:O,update:P,hotspotDescriptionPath:null==u?void 0:u.descriptionPath,tooltip:null==u?void 0:u.tooltip,renderPreview:p,schemaType:h.of[0]},e._key)))),r.jsx(s.Card,{__unstable_checkered:!0,shadow:1,children:r.jsx(s.Flex,{align:"center",justify:"center",children:r.jsx("img",{ref:k,src:j.url,width:j.width,height:j.height,alt:"",style:E,onClick:w})})})]}):r.jsx(y,{children:u.imagePath?r.jsxs(r.Fragment,{children:["No Hotspot image found at path ",r.jsx("code",{children:u.imagePath})]}):r.jsxs(r.Fragment,{children:["Define a path in this field using to the image field in this document at"," ",r.jsx("code",{children:"options.hotspotImagePath"})]})}),u.pathRoot&&!M.includes(u.pathRoot)&&r.jsxs(y,{children:['The supplied imageHotspotPathRoot "',u.pathRoot,'" is not valid, falling back to "document". Available values are "',M.join(", "),'".']}),e.renderDefault(e)]})}const D=o.definePlugin({name:"image-hotspot-array",form:{components:{input:e=>{var i,n;if("array"===e.schemaType.jsonType&&(null==(i=e.schemaType.options)?void 0:i.imageHotspot)){const i=null==(n=e.schemaType.options)?void 0:n.imageHotspot;if(i)return r.jsx(_,t(t({},e),{},{imageHotspotOptions:i}))}return e.renderDefault(e)}}}});exports.ImageHotspotArray=_,exports.imageHotspotArrayPlugin=D;
2
- //# sourceMappingURL=index.js.map
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import React, {ReactNode} from 'react'\nimport {Card, Text} from '@sanity/ui'\n\nexport default function Feedback({children}: {children: ReactNode}) {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","/* eslint-disable */\nimport {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport get from 'lodash/get'\nimport React, {ComponentType, CSSProperties, ReactElement, useEffect} from 'react'\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\nimport {ObjectSchemaType, RenderPreviewCallback} from 'sanity'\n\nconst dragStyle: CSSProperties = {\n width: '1.4rem',\n height: '1.4rem',\n position: 'absolute',\n boxSizing: 'border-box',\n top: 0,\n left: 0,\n margin: '-0.7rem 0 0 -0.7rem',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: '50%',\n background: '#000',\n color: 'white',\n}\n\nconst dragStyleWhileDrag: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n cursor: 'none',\n}\n\nconst dragStyleWhileHover: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n}\n\nconst dotStyle: CSSProperties = {\n position: 'absolute',\n left: '50%',\n top: '50%',\n height: '0.2rem',\n width: '0.2rem',\n margin: '-0.1rem 0 0 -0.1rem',\n background: '#fff',\n visibility: 'hidden',\n borderRadius: '50%',\n // make sure pointer events only run on the parent\n pointerEvents: 'none',\n}\n\nconst dotStyleWhileActive: CSSProperties = {\n visibility: 'visible',\n}\n\nconst labelStyle: CSSProperties = {\n color: 'white',\n fontSize: '0.7rem',\n fontWeight: 600,\n lineHeight: '1',\n}\n\nconst labelStyleWhileActive: CSSProperties = {\n visibility: 'hidden',\n}\n\nconst round = (num: number) => Math.round(num * 100) / 100\n\nexport interface HotspotTooltipProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem<HotspotFields>\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\ninterface HotspotProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem\n bounds: DOMRectReadOnly\n update: FnHotspotMove\n hotspotDescriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n index: number\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\nexport default function Spot({\n value,\n bounds,\n update,\n hotspotDescriptionPath,\n tooltip,\n index,\n schemaType,\n renderPreview,\n}: HotspotProps) {\n const [isDragging, setIsDragging] = React.useState(false)\n const [isHovering, setIsHovering] = React.useState(false)\n\n // x/y are stored as % but need to be converted to px\n const x = useMotionValue(round(bounds.width * (value.x / 100)))\n const y = useMotionValue(round(bounds.height * (value.y / 100)))\n\n /**\n * update x/y if the bounds change when resizing the window\n */\n useEffect(() => {\n x.set(round(bounds.width * (value.x / 100)))\n y.set(round(bounds.height * (value.y / 100)))\n }, [x, y, value, bounds])\n\n const handleDragEnd = React.useCallback(() => {\n setIsDragging(false)\n\n // get current values for x/y in px\n const currentX = x.get()\n const currentY = y.get()\n\n // Which we need to convert back to `%` to patch the document\n const newX = round((currentX * 100) / bounds.width)\n const newY = round((currentY * 100) / bounds.height)\n\n // Don't go below 0 or above 100\n const safeX = Math.max(0, Math.min(100, newX))\n const safeY = Math.max(0, Math.min(100, newY))\n\n update(value._key, safeX, safeY)\n }, [x, y, value, update, bounds])\n const handleDragStart = React.useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = React.useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = React.useCallback(() => setIsHovering(false), [])\n\n if (!x || !y) {\n return null\n }\n\n return (\n <Tooltip\n key={value._key}\n disabled={isDragging}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n React.createElement(tooltip, {value, renderPreview, schemaType})\n ) : (\n <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>\n <Text textOverflow=\"ellipsis\">\n {hotspotDescriptionPath\n ? (get(value, hotspotDescriptionPath) as string)\n : `${value.x}% x ${value.y}%`}\n </Text>\n </Box>\n )\n }\n >\n <motion.div\n drag\n dragConstraints={bounds}\n dragElastic={0}\n dragMomentum={false}\n onDragEnd={handleDragEnd}\n onDragStart={handleDragStart}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n style={{\n ...dragStyle,\n x,\n y,\n ...(isDragging && {...dragStyleWhileDrag}),\n ...(isHovering && {...dragStyleWhileHover}),\n }}\n >\n {/* Dot */}\n <Box\n style={{\n ...dotStyle,\n ...((isDragging || isHovering) && {...dotStyleWhileActive}),\n }}\n />\n {/* Label */}\n <div\n style={{\n ...labelStyle,\n ...((isDragging || isHovering) && {...labelStyleWhileActive}),\n }}\n >\n {index + 1}\n </div>\n </motion.div>\n </Tooltip>\n )\n}\n","/* eslint-disable react/display-name */\n\nimport {getImageDimensions} from '@sanity/asset-utils'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n} from 'sanity'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport get from 'lodash/get'\nimport React, {useCallback, useMemo, useRef, useState} from 'react'\n\nimport {IUseResizeObserverCallback, useDebouncedCallback, useResizeObserver} from '@react-hookz/web'\nimport Feedback from './Feedback'\nimport Spot from './Spot'\nimport {ImageHotspotOptions} from './plugin'\n\nconst imageStyle = {width: `100%`, height: `auto`}\n\nconst VALID_ROOT_PATHS = ['document', 'parent']\n\nexport type FnHotspotMove = (key: string, x: number, y: number) => void\n\nexport type HotspotItem<HotspotFields = {[key: string]: unknown}> = {\n _key: string\n _type: string\n x: number\n y: number\n} & HotspotFields\n\nexport function ImageHotspotArray(\n props: ArrayOfObjectsInputProps<HotspotItem> & {imageHotspotOptions: ImageHotspotOptions}\n) {\n const {value, onChange, imageHotspotOptions, schemaType, renderPreview} = props\n\n const sanityClient = useClient({apiVersion: '2022-01-01'})\n\n const imageHotspotPathRoot = useMemo(() => {\n const pathRoot = VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? '')\n ? imageHotspotOptions.pathRoot\n : 'document'\n return pathRoot === 'document' ? [] : props.path.slice(0, -1)\n }, [imageHotspotOptions.pathRoot, props.path])\n\n const rootObject = useFormValue(imageHotspotPathRoot)\n\n /**\n * Finding the image from the imageHotspotPathRoot (defaults to document),\n * using the path from the hotspot's `options` field\n *\n * when changes in imageHotspotPathRoot (e.g. document) occur,\n * check if there are any changes to the hotspotImage and update the reference\n */\n const hotspotImage = useMemo(() => {\n return get(rootObject, imageHotspotOptions.imagePath) as ImageValue | undefined\n }, [rootObject, imageHotspotOptions.imagePath])\n\n const displayImage = useMemo(() => {\n const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? '')\n const urlFor = (source: ImageValue) => builder.image(source)\n\n if (hotspotImage?.asset?._ref) {\n const {aspectRatio} = getImageDimensions(hotspotImage.asset._ref)\n const width = 1200\n const height = Math.round(width / aspectRatio)\n const url = urlFor(hotspotImage).width(width).url()\n\n return {width, height, url}\n }\n\n return null\n }, [hotspotImage, sanityClient])\n\n const handleHotspotImageClick = useCallback(\n (event: any) => {\n const {nativeEvent} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / nativeEvent.srcElement.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / nativeEvent.srcElement.height).toFixed(2))\n const description = `New Hotspot at ${x}% x ${y}%`\n\n const newRow: HotspotItem = {\n _key: randomKey(12),\n _type: schemaType.of[0].name,\n x,\n y,\n }\n\n if (imageHotspotOptions?.descriptionPath) {\n newRow[imageHotspotOptions.descriptionPath] = description\n }\n\n onChange(PatchEvent.from([setIfMissing([]), insert([newRow], 'after', [-1])]))\n },\n [imageHotspotOptions, onChange, schemaType]\n )\n\n const handleHotspotMove: FnHotspotMove = useCallback(\n (key, x, y) => {\n onChange(\n PatchEvent.from([\n // Set the `x` value of this array key item\n set(x, [{_key: key}, 'x']),\n // Set the `y` value of this array key item\n set(y, [{_key: key}, 'y']),\n ])\n )\n },\n [onChange]\n )\n\n const hotspotImageRef = useRef<HTMLImageElement | null>(null)\n\n const [imageRect, setImageRect] = useState<DOMRectReadOnly>()\n const updateImageRectCallback = useDebouncedCallback(\n ((e) => setImageRect(e.contentRect)) as IUseResizeObserverCallback,\n [setImageRect],\n 200\n )\n useResizeObserver(hotspotImageRef, updateImageRectCallback)\n\n return (\n <Stack space={[2, 2, 3]}>\n {displayImage?.url ? (\n <div style={{position: `relative`}}>\n {imageRect &&\n (value?.length ?? 0) > 0 &&\n value?.map((spot, index) => (\n <Spot\n index={index}\n key={spot._key}\n value={spot}\n bounds={imageRect}\n update={handleHotspotMove}\n hotspotDescriptionPath={imageHotspotOptions?.descriptionPath}\n tooltip={imageHotspotOptions?.tooltip}\n renderPreview={renderPreview}\n schemaType={schemaType.of[0] as ObjectSchemaType}\n />\n ))}\n\n <Card __unstable_checkered shadow={1}>\n <Flex align=\"center\" justify=\"center\">\n <img\n ref={hotspotImageRef}\n src={displayImage.url}\n width={displayImage.width}\n height={displayImage.height}\n alt=\"\"\n style={imageStyle}\n onClick={handleHotspotImageClick}\n />\n </Flex>\n </Card>\n </div>\n ) : (\n <Feedback>\n {imageHotspotOptions.imagePath ? (\n <>\n No Hotspot image found at path <code>{imageHotspotOptions.imagePath}</code>\n </>\n ) : (\n <>\n Define a path in this field using to the image field in this document at{' '}\n <code>options.hotspotImagePath</code>\n </>\n )}\n </Feedback>\n )}\n {imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && (\n <Feedback>\n The supplied imageHotspotPathRoot \"{imageHotspotOptions.pathRoot}\" is not valid, falling\n back to \"document\". Available values are \"{VALID_ROOT_PATHS.join(', ')}\".\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ImageHotspotArray, type HotspotItem} from './ImageHotspotArray'\nimport React, {ComponentType} from 'react'\nimport {ArrayOfObjectsInputProps, definePlugin} from 'sanity'\nimport {type HotspotTooltipProps} from './Spot'\n\nexport interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {\n pathRoot?: 'document' | 'parent'\n imagePath: string\n descriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n}\n\ndeclare module '@sanity/types' {\n export interface ArrayOptions {\n imageHotspot?: ImageHotspotOptions\n }\n}\n\nexport const imageHotspotArrayPlugin = definePlugin({\n name: 'image-hotspot-array',\n form: {\n components: {\n input: (props) => {\n if (props.schemaType.jsonType === 'array' && props.schemaType.options?.imageHotspot) {\n const imageHotspotOptions = props.schemaType.options?.imageHotspot\n if (imageHotspotOptions) {\n return (\n <ImageHotspotArray\n {...(props as unknown as ArrayOfObjectsInputProps<HotspotItem>)}\n imageHotspotOptions={imageHotspotOptions}\n />\n )\n }\n }\n return props.renderDefault(props)\n },\n },\n },\n})\n"],"names":["Feedback","_ref","children","jsx","Card","padding","radius","shadow","tone","Text","dragStyle","width","height","position","boxSizing","top","left","margin","cursor","display","justifyContent","alignItems","borderRadius","background","color","dragStyleWhileDrag","border","dragStyleWhileHover","dotStyle","visibility","pointerEvents","dotStyleWhileActive","labelStyle","fontSize","fontWeight","lineHeight","labelStyleWhileActive","round","num","Math","Spot","_ref2","value","bounds","update","hotspotDescriptionPath","tooltip","index","schemaType","renderPreview","isDragging","setIsDragging","React","useState","isHovering","setIsHovering","x","useMotionValue","y","useEffect","set","handleDragEnd","useCallback","currentX","get","currentY","newX","newY","safeX","max","min","safeY","_key","handleDragStart","handleHoverStart","handleHoverEnd","Tooltip","disabled","portal","content","createElement","Box","style","maxWidth","textOverflow","jsxs","motion","div","drag","dragConstraints","dragElastic","dragMomentum","onDragEnd","onDragStart","onHoverStart","onHoverEnd","_objectSpread","imageStyle","VALID_ROOT_PATHS","ImageHotspotArray","props","_a","onChange","imageHotspotOptions","sanityClient","useClient","apiVersion","imageHotspotPathRoot","useMemo","includes","pathRoot","path","slice","rootObject","useFormValue","hotspotImage","imagePath","displayImage","_b","builder","imageUrlBuilder","dataset","config","asset","aspectRatio","getImageDimensions","url","source","image","handleHotspotImageClick","event","nativeEvent","Number","offsetX","srcElement","toFixed","offsetY","description","concat","newRow","randomKey","_type","of","name","descriptionPath","PatchEvent","from","setIfMissing","insert","handleHotspotMove","key","hotspotImageRef","useRef","imageRect","setImageRect","updateImageRectCallback","useDebouncedCallback","e","contentRect","useResizeObserver","Stack","space","length","map","spot","__unstable_checkered","Flex","align","justify","ref","src","alt","onClick","Fragment","join","renderDefault","imageHotspotArrayPlugin","definePlugin","form","components","input","jsonType","options","imageHotspot"],"mappings":"inCAGwB,SAAAA,EAA4CC,GAAA,IAAnCC,SAACA,GAAkCD,EAClE,OACGE,EAAAA,IAAAC,EAAAA,KAAA,CAAKC,QAAS,EAAGC,OAAQ,EAAGC,OAAQ,EAAGC,KAAK,UAC3CN,SAACC,EAAAA,IAAAM,OAAA,CAAMP,cAGb,CCDA,MAAMQ,EAA2B,CAC/BC,MAAO,SACPC,OAAQ,SACRC,SAAU,WACVC,UAAW,aACXC,IAAK,EACLC,KAAM,EACNC,OAAQ,sBACRC,OAAQ,UACRC,QAAS,OACTC,eAAgB,SAChBC,WAAY,SACZC,aAAc,MACdC,WAAY,OACZC,MAAO,SAGHC,EAAoC,CACxCF,WAAY,qBACZG,OAAQ,iBACRR,OAAQ,QAGJS,EAAqC,CACzCJ,WAAY,qBACZG,OAAQ,kBAGJE,EAA0B,CAC9Bf,SAAU,WACVG,KAAM,MACND,IAAK,MACLH,OAAQ,SACRD,MAAO,SACPM,OAAQ,sBACRM,WAAY,OACZM,WAAY,SACZP,aAAc,MAEdQ,cAAe,QAGXC,EAAqC,CACzCF,WAAY,WAGRG,EAA4B,CAChCR,MAAO,QACPS,SAAU,SACVC,WAAY,IACZC,WAAY,KAGRC,EAAuC,CAC3CP,WAAY,UAGRQ,EAASC,GAAgBC,KAAKF,MAAY,IAANC,GAAa,IAmBvD,SAAwBE,EASPC,GAAA,IATYC,MAC3BA,EAAAC,OACAA,EAAAC,OACAA,EAAAC,uBACAA,EAAAC,QACAA,EAAAC,MACAA,EAAAC,WACAA,EAAAC,cACAA,GACeR,EACf,MAAOS,EAAYC,GAAiBC,EAAAA,QAAMC,UAAS,IAC5CC,EAAYC,GAAiBH,EAAAA,QAAMC,UAAS,GAG7CG,EAAIC,iBAAepB,EAAMM,EAAOhC,OAAS+B,EAAMc,EAAI,OACnDE,EAAID,iBAAepB,EAAMM,EAAO/B,QAAU8B,EAAMgB,EAAI,OAK1DC,EAAAA,WAAU,KACRH,EAAEI,IAAIvB,EAAMM,EAAOhC,OAAS+B,EAAMc,EAAI,OACtCE,EAAEE,IAAIvB,EAAMM,EAAO/B,QAAU8B,EAAMgB,EAAI,MAAK,GAC3C,CAACF,EAAGE,EAAGhB,EAAOC,IAEX,MAAAkB,EAAgBT,UAAMU,aAAY,KACtCX,GAAc,GAGR,MAAAY,EAAWP,EAAEQ,MACbC,EAAWP,EAAEM,MAGbE,EAAO7B,EAAkB,IAAX0B,EAAkBpB,EAAOhC,OACvCwD,EAAO9B,EAAkB,IAAX4B,EAAkBtB,EAAO/B,QAGvCwD,EAAQ7B,KAAK8B,IAAI,EAAG9B,KAAK+B,IAAI,IAAKJ,IAClCK,EAAQhC,KAAK8B,IAAI,EAAG9B,KAAK+B,IAAI,IAAKH,IAEjCvB,EAAAF,EAAM8B,KAAMJ,EAAOG,EAAK,GAC9B,CAACf,EAAGE,EAAGhB,EAAOE,EAAQD,IACnB8B,EAAkBrB,EAAAA,QAAMU,aAAY,IAAMX,GAAc,IAAO,IAE/DuB,EAAmBtB,EAAAA,QAAMU,aAAY,IAAMP,GAAc,IAAO,IAChEoB,EAAiBvB,EAAAA,QAAMU,aAAY,IAAMP,GAAc,IAAQ,IAEjE,OAACC,GAAME,EAKRvD,EAAAA,IAAAyE,EAAAA,QAAA,CAECC,SAAU3B,EACV4B,QAAM,EACNC,QACEjC,GAA8B,mBAAZA,EAChBM,EAAM,QAAA4B,cAAclC,EAAS,CAACJ,QAAOO,gBAAeD,eAEnD7C,EAAAA,IAAA8E,EAAAA,IAAA,CAAI5E,QAAS,EAAG6E,MAAO,CAACC,SAAU,IAAKrD,sBACtC5B,SAACC,EAAAA,IAAAM,OAAA,CAAK2E,aAAa,WAChBlF,SAAA2C,EACImB,EAAAA,QAAItB,EAAOG,aACTH,EAAMc,EAAQd,QAAAA,OAAAA,EAAMgB,EAAA,SAMnCxD,SAAAmF,EAAAA,KAACC,SAAOC,IAAP,CACCC,MAAI,EACJC,gBAAiB9C,EACjB+C,YAAa,EACbC,cAAc,EACdC,UAAW/B,EACXgC,YAAapB,EACbqB,aAAcpB,EACdqB,WAAYpB,EACZO,eACKxE,GAAA,CAAA,EAAA,CACH8C,IACAE,KACIR,QAAkBzB,IAClB6B,GAAA0C,EAAA,CAAA,EAAkBrE,IAIxBzB,SAAA,CAACC,EAAAA,IAAA8E,EAAAA,IAAA,CACCC,MAAOc,EAAAA,EAAA,CAAA,EACFpE,IACEsB,GAAcI,IAAmBvB,EAAAA,CAAAA,EAAAA,MAIzC5B,EAAAA,IAAA,MAAA,CACC+E,MAAOc,EAAAA,EAAA,CAAA,EACFhE,IACEkB,GAAcI,IAAe0C,EAAA,CAAA,EAAI5D,IAGvClC,SAAQ6C,EAAA,QAhDRL,EAAM8B,MALN,IA0DX,CCrKA,MAAMyB,EAAa,CAACtF,MAAO,OAAQC,eAE7BsF,EAAmB,CAAC,WAAY,UAW/B,SAASC,EACdC,GAvCF,IAAAC,EAyCE,MAAM3D,MAACA,EAAO4D,SAAAA,EAAAC,oBAAUA,EAAqBvD,WAAAA,EAAAC,cAAYA,GAAiBmD,EAEpEI,EAAeC,EAAAA,UAAU,CAACC,WAAY,eAEtCC,EAAuBC,EAAAA,SAAQ,KA7CvCP,IAAAA,EAiDW,MAAa,cAHHH,EAAiBW,SAAS,OAAAR,EAAAE,EAAoBO,UAApBT,EAAgC,IACvEE,EAAoBO,SACpB,YAC6B,GAAKV,EAAMW,KAAKC,MAAM,GAAK,EAAA,GAC3D,CAACT,EAAoBO,SAAUV,EAAMW,OAElCE,EAAaC,eAAaP,GAS1BQ,EAAeP,EAAAA,SAAQ,IACpB5C,UAAIiD,EAAYV,EAAoBa,YAC1C,CAACH,EAAYV,EAAoBa,YAE9BC,EAAeT,EAAAA,SAAQ,KAjE/B,IAAAP,EAAAiB,EAkEI,MAAMC,EAAUC,EAAA,QAAgBhB,GAAciB,QAAQ,OAAApB,EAAAG,EAAakB,SAASD,SAAtBpB,EAAiC,IAGnF,GAAA,OAAAiB,EAAA,MAAAH,OAAA,EAAAA,EAAcQ,YAAd,EAAAL,EAAqBrH,KAAM,CAC7B,MAAM2H,YAACA,GAAeC,EAAAA,mBAAmBV,EAAaQ,MAAM1H,MACtDU,EAAQ,KAIP,MAAA,CAACA,QAAOC,OAHA2B,KAAKF,MAAM1B,EAAQiH,GAGXE,KARTC,EAMKZ,EANkBI,EAAQS,MAAMD,IAMlBpH,MAAMA,GAAOmH,MAGhD,CATgBC,MAWT,OAAA,IAAA,GACN,CAACZ,EAAcX,IAEZyB,EAA0BnE,EAAAA,aAC7BoE,IACO,MAAAC,YAACA,GAAeD,EAGhB1E,EAAI4E,QAA+B,IAAtBD,EAAYE,QAAiBF,EAAYG,WAAW3H,OAAO4H,QAAQ,IAChF7E,EAAI0E,QAA+B,IAAtBD,EAAYK,QAAiBL,EAAYG,WAAW1H,QAAQ2H,QAAQ,IACjFE,EAAA,kBAAAC,OAAgClF,EAAQ,QAAAkF,OAAAhF,EAAA,KAExCiF,EAAsB,CAC1BnE,KAAMoE,YAAU,IAChBC,MAAO7F,EAAW8F,GAAG,GAAGC,KACxBvF,IACAE,YAGE6C,WAAqByC,mBACvBL,EAAOpC,EAAoByC,iBAAmBP,GAGhDnC,EAAS2C,aAAWC,KAAK,CAACC,EAAAA,aAAa,IAAKC,EAAAA,OAAO,CAACT,GAAS,QAAS,QAAO,GAE/E,CAACpC,EAAqBD,EAAUtD,IAG5BqG,EAAmCvF,EAAAA,aACvC,CAACwF,EAAK9F,EAAGE,KACP4C,EACE2C,EAAAA,WAAWC,KAAK,CAEdtF,EAAAA,IAAIJ,EAAG,CAAC,CAACgB,KAAM8E,GAAM,MAErB1F,EAAAA,IAAIF,EAAG,CAAC,CAACc,KAAM8E,GAAM,QAEzB,GAEF,CAAChD,IAGGiD,EAAkBC,SAAgC,OAEjDC,EAAWC,GAAgBrG,EAA0BA,WACtDsG,EAA0BC,EAAAA,sBAC5BC,GAAMH,EAAaG,EAAEC,cACvB,CAACJ,GACD,KAIF,OAFAK,oBAAkBR,EAAiBI,GAGhCtE,EAAAA,KAAA2E,EAAAA,MAAA,CAAMC,MAAO,CAAC,EAAG,EAAG,GAClB/J,SAAA,EAAA,MAAAmH,OAAA,EAAAA,EAAcS,KACZzC,EAAAA,KAAA,MAAA,CAAIH,MAAO,CAACrE,SAAA,YACVX,SAAA,CACEuJ,IAAA,OAAApD,EAAA,MAAA3D,OAAA,EAAAA,EAAOwH,QAAP7D,EAAiB,GAAK,UACvB3D,WAAOyH,KAAI,CAACC,EAAMrH,IACf5C,EAAAA,IAAAqC,EAAA,CACCO,QAEAL,MAAO0H,EACPzH,OAAQ8G,EACR7G,OAAQyG,EACRxG,uBAA6C,MAArB0D,OAAqB,EAAAA,EAAAyC,gBAC7ClG,QAA8B,MAArByD,OAAqB,EAAAA,EAAAzD,QAC9BG,gBACAD,WAAYA,EAAW8F,GAAG,IAPrBsB,EAAK5F,SAWfrE,EAAAA,IAAAC,EAAAA,KAAA,CAAKiK,sBAAoB,EAAC9J,OAAQ,EACjCL,SAACC,EAAAA,IAAAmK,OAAA,CAAKC,MAAM,SAASC,QAAQ,SAC3BtK,SAACC,EAAAA,IAAA,MAAA,CACCsK,IAAKlB,EACLmB,IAAKrD,EAAaS,IAClBnH,MAAO0G,EAAa1G,MACpBC,OAAQyG,EAAazG,OACrB+J,IAAI,GACJzF,MAAOe,EACP2E,QAAS3C,WAMhB9H,EAAAA,IAAAH,EAAA,CACEE,WAAoBkH,UACnB/B,EAAAA,KAAAwF,EAAAA,SAAA,CAAE3K,SAAA,CAAA,kCACgCC,EAAAA,IAAA,OAAA,CAAMD,SAAoBqG,EAAAa,eAG5D/B,EAAAA,KAAAwF,WAAA,CAAE3K,SAAA,CAAA,2EACyE,IACxEC,EAAAA,IAAA,OAAA,CAAKD,SAAA,kCAKbqG,EAAoBO,WAAaZ,EAAiBW,SAASN,EAAoBO,WAC7EzB,EAAAA,KAAArF,EAAA,CAASE,SAAA,CAAA,sCAC4BqG,EAAoBO,SAAS,qEACtBZ,EAAiB4E,KAAK,MAAM,QAG1E1E,EAAM2E,cAAc3E,KAG3B,CCzKO,MAAM4E,EAA0BC,EAAAA,aAAa,CAClDlC,KAAM,sBACNmC,KAAM,CACJC,WAAY,CACVC,MAAQhF,IAtBd,IAAAC,EAAAiB,EAuBY,GAA8B,UAA9BlB,EAAMpD,WAAWqI,WAAwB,OAAAhF,IAAMrD,WAAWsI,kBAASC,cAAc,CACnF,MAAMhF,EAAsB,OAAAe,EAAAlB,EAAMpD,WAAWsI,cAAS,EAAAhE,EAAAiE,aACtD,GAAIhF,EACF,OACGpG,EAAAA,IAAAgG,SACMC,GAAA,CAAA,EAAA,CACLG,wBAIR,CACO,OAAAH,EAAM2E,cAAc3E,EAAK"}