sanity-plugin-hotspot-array 2.0.1 → 2.1.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.
package/dist/index.js CHANGED
@@ -72,30 +72,30 @@ function Spot({
72
72
  update(value._key, safeX, safeY);
73
73
  }, [x, y, value, update, bounds]), handleDragStart = react.useCallback(() => setIsDragging(!0), []), handleHoverStart = react.useCallback(() => setIsHovering(!0), []), handleHoverEnd = react.useCallback(() => setIsHovering(!1), []);
74
74
  return !x || !y ? null : /* @__PURE__ */ jsxRuntime.jsx(
75
- ui.Tooltip,
75
+ framerMotion.motion.div,
76
76
  {
77
- disabled: isDragging,
78
- portal: !0,
79
- content: tooltip && typeof tooltip == "function" ? react.createElement(tooltip, { value, renderPreview, schemaType }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 2, style: { maxWidth: 200, pointerEvents: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { textOverflow: "ellipsis", children: hotspotDescriptionPath ? lodashEs.get(value, hotspotDescriptionPath) : `${value.x}% x ${value.y}%` }) }),
80
- children: /* @__PURE__ */ jsxRuntime.jsxs(
81
- framerMotion.motion.div,
77
+ drag: !0,
78
+ dragConstraints: bounds,
79
+ dragElastic: 0,
80
+ dragMomentum: !1,
81
+ onDragEnd: handleDragEnd,
82
+ onDragStart: handleDragStart,
83
+ onHoverStart: handleHoverStart,
84
+ onHoverEnd: handleHoverEnd,
85
+ style: {
86
+ ...dragStyle,
87
+ x,
88
+ y,
89
+ ...isDragging && { ...dragStyleWhileDrag },
90
+ ...isHovering && { ...dragStyleWhileHover }
91
+ },
92
+ children: /* @__PURE__ */ jsxRuntime.jsx(
93
+ ui.Tooltip,
82
94
  {
83
- drag: !0,
84
- dragConstraints: bounds,
85
- dragElastic: 0,
86
- dragMomentum: !1,
87
- onDragEnd: handleDragEnd,
88
- onDragStart: handleDragStart,
89
- onHoverStart: handleHoverStart,
90
- onHoverEnd: handleHoverEnd,
91
- style: {
92
- ...dragStyle,
93
- x,
94
- y,
95
- ...isDragging && { ...dragStyleWhileDrag },
96
- ...isHovering && { ...dragStyleWhileHover }
97
- },
98
- children: [
95
+ disabled: isDragging,
96
+ portal: !0,
97
+ content: tooltip && typeof tooltip == "function" ? react.createElement(tooltip, { value, renderPreview, schemaType }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 2, style: { maxWidth: 200, pointerEvents: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { textOverflow: "ellipsis", children: hotspotDescriptionPath ? lodashEs.get(value, hotspotDescriptionPath) : `${value.x}% x ${value.y}%` }) }),
98
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
99
99
  /* @__PURE__ */ jsxRuntime.jsx(
100
100
  ui.Box,
101
101
  {
@@ -115,11 +115,11 @@ function Spot({
115
115
  children: index + 1
116
116
  }
117
117
  )
118
- ]
119
- }
118
+ ] })
119
+ },
120
+ value._key
120
121
  )
121
- },
122
- value._key
122
+ }
123
123
  );
124
124
  }
125
125
  function useUnmountEffect(effect) {
@@ -198,7 +198,11 @@ function ImageHotspotArray(props) {
198
198
  const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = sanity.useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = react.useMemo(() => {
199
199
  var _a2;
200
200
  return (VALID_ROOT_PATHS.includes((_a2 = imageHotspotOptions.pathRoot) != null ? _a2 : "") ? imageHotspotOptions.pathRoot : "document") === "document" ? [] : props.path.slice(0, -1);
201
- }, [imageHotspotOptions.pathRoot, props.path]), rootObject = sanity.useFormValue(imageHotspotPathRoot), hotspotImage = react.useMemo(() => lodashEs.get(rootObject, imageHotspotOptions.imagePath), [rootObject, imageHotspotOptions.imagePath]), displayImage = react.useMemo(() => {
201
+ }, [imageHotspotOptions.pathRoot, props.path]), rootObject = sanity.useFormValue(imageHotspotPathRoot), resolveInitialValueForType = sanity.useResolveInitialValueForType(), resolveInitialValue = react.useCallback(
202
+ async (type) => resolveInitialValueForType(type, {}).then((initialValue) => initialValue).catch(() => {
203
+ }),
204
+ [resolveInitialValueForType]
205
+ ), hotspotImage = react.useMemo(() => lodashEs.get(rootObject, imageHotspotOptions.imagePath), [rootObject, imageHotspotOptions.imagePath]), displayImage = react.useMemo(() => {
202
206
  var _a2, _b;
203
207
  const builder = imageUrlBuilder__default.default(sanityClient).dataset((_a2 = sanityClient.config().dataset) != null ? _a2 : ""), urlFor = (source) => builder.image(source);
204
208
  if ((_b = hotspotImage == null ? void 0 : hotspotImage.asset) != null && _b._ref) {
@@ -207,16 +211,17 @@ function ImageHotspotArray(props) {
207
211
  }
208
212
  return null;
209
213
  }, [hotspotImage, sanityClient]), handleHotspotImageClick = react.useCallback(
210
- (event) => {
211
- const { nativeEvent, currentTarget } = event, x = Number((nativeEvent.offsetX * 100 / currentTarget.width).toFixed(2)), y = Number((nativeEvent.offsetY * 100 / currentTarget.height).toFixed(2)), description = `New Hotspot at ${x}% x ${y}%`, newRow = {
214
+ async (event) => {
215
+ const { nativeEvent, currentTarget } = event, x = Number((nativeEvent.offsetX * 100 / currentTarget.width).toFixed(2)), y = Number((nativeEvent.offsetY * 100 / currentTarget.height).toFixed(2)), description = `New Hotspot at ${x}% x ${y}%`, initialValues = await resolveInitialValue(schemaType.of[0].type), newRow = {
212
216
  _key: content.randomKey(12),
213
217
  _type: schemaType.of[0].name,
218
+ ...initialValues,
214
219
  x,
215
220
  y
216
221
  };
217
222
  imageHotspotOptions != null && imageHotspotOptions.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(sanity.PatchEvent.from([sanity.setIfMissing([]), sanity.insert([newRow], "after", [-1])]));
218
223
  },
219
- [imageHotspotOptions, onChange, schemaType]
224
+ [imageHotspotOptions, onChange, resolveInitialValue, schemaType]
220
225
  ), handleHotspotMove = react.useCallback(
221
226
  (key, x, y) => {
222
227
  onChange(
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/useDebouncedCallback.ts","../src/useResizeObserver.ts","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import {Card, Text} from '@sanity/ui'\nimport {type ReactNode} from 'react'\n\nexport default function Feedback({children}: {children: ReactNode}): ReactNode {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","import {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport {get} from 'lodash-es'\nimport {\n ComponentType,\n createElement,\n CSSProperties,\n ReactNode,\n useCallback,\n useEffect,\n useState,\n} from 'react'\nimport {type ObjectSchemaType, type RenderPreviewCallback} from 'sanity'\n\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\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): ReactNode {\n const [isDragging, setIsDragging] = useState(false)\n const [isHovering, setIsHovering] = 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 = 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 = useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = 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 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","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useDebouncedCallback/index.ts\nimport {type DependencyList, useEffect, useMemo, useRef} from 'react'\n\n/**\n * Run effect only when component is unmounted.\n *\n * @param effect Effector to run on unmount\n */\nexport function useUnmountEffect(effect: CallableFunction): void {\n const effectRef = useRef(effect)\n effectRef.current = effect\n useEffect(() => () => effectRef.current(), [])\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DebouncedFunction<Fn extends (...args: any[]) => any> = (\n this: ThisParameterType<Fn>,\n ...args: Parameters<Fn>\n) => void\n\n/**\n * Makes passed function debounced, otherwise acts like `useCallback`.\n *\n * @param callback Function that will be debounced.\n * @param deps Dependencies list when to update callback. It also replaces invoked\n * \tcallback for scheduled debounced invocations.\n * @param delay Debounce delay.\n * @param maxWait The maximum time `callback` is allowed to be delayed before\n * it's invoked. 0 means no max wait.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useDebouncedCallback<Fn extends (...args: any[]) => any>(\n callback: Fn,\n deps: DependencyList,\n delay: number,\n maxWait = 0,\n): DebouncedFunction<Fn> {\n const timeout = useRef<ReturnType<typeof setTimeout>>()\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>()\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>()\n\n const clear = () => {\n if (timeout.current) {\n clearTimeout(timeout.current)\n timeout.current = undefined\n }\n\n if (waitTimeout.current) {\n clearTimeout(waitTimeout.current)\n waitTimeout.current = undefined\n }\n }\n\n // Cancel scheduled execution on unmount\n useUnmountEffect(clear)\n\n useEffect(() => {\n cb.current = callback\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n\n return useMemo(() => {\n const execute = () => {\n clear()\n\n // Barely possible to test this line\n /* istanbul ignore next */\n if (!lastCall.current) return\n\n const context = lastCall.current\n lastCall.current = undefined\n\n cb.current.apply(context.this, context.args)\n }\n\n const wrapped = function (this, ...args) {\n if (timeout.current) {\n clearTimeout(timeout.current)\n }\n\n lastCall.current = {args, this: this}\n\n // Plan regular execution\n timeout.current = setTimeout(execute, delay)\n\n // Plan maxWait execution if required\n if (maxWait > 0 && !waitTimeout.current) {\n waitTimeout.current = setTimeout(execute, maxWait)\n }\n } as DebouncedFunction<Fn>\n\n Object.defineProperties(wrapped, {\n length: {value: callback.length},\n name: {value: `${callback.name || 'anonymous'}__debounced__${delay}`},\n })\n\n return wrapped\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [delay, maxWait, ...deps])\n}\n","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useResizeObserver/index.ts\nimport {type RefObject, useEffect, useRef} from 'react'\n\nconst isBrowser =\n typeof window !== 'undefined' &&\n typeof navigator !== 'undefined' &&\n typeof document !== 'undefined'\n\nexport type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void\n\ntype ResizeObserverSingleton = {\n observer: ResizeObserver\n subscribe: (target: Element, callback: UseResizeObserverCallback) => void\n unsubscribe: (target: Element, callback: UseResizeObserverCallback) => void\n}\n\nlet observerSingleton: ResizeObserverSingleton\n\nfunction getResizeObserver(): ResizeObserverSingleton | undefined {\n if (!isBrowser) return undefined\n\n if (observerSingleton) return observerSingleton\n\n const callbacks = new Map<Element, Set<UseResizeObserverCallback>>()\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries)\n callbacks.get(entry.target)?.forEach((cb) =>\n setTimeout(() => {\n cb(entry)\n }, 0),\n )\n })\n\n observerSingleton = {\n observer,\n subscribe(target, callback) {\n let cbs = callbacks.get(target)\n\n if (!cbs) {\n // If target has no observers yet - register it\n cbs = new Set<UseResizeObserverCallback>()\n callbacks.set(target, cbs)\n observer.observe(target)\n }\n\n // As Set is duplicate-safe - simply add callback on each call\n cbs.add(callback)\n },\n unsubscribe(target, callback) {\n const cbs = callbacks.get(target)\n\n // Else branch should never occur in case of normal execution\n // because callbacks map is hidden in closure - it is impossible to\n // simulate situation with non-existent `cbs` Set\n /* istanbul ignore else */\n if (cbs) {\n // Remove current observer\n cbs.delete(callback)\n\n if (cbs.size === 0) {\n // If no observers left unregister target completely\n callbacks.delete(target)\n observer.unobserve(target)\n }\n }\n },\n }\n\n return observerSingleton\n}\n\n/**\n * Invokes a callback whenever ResizeObserver detects a change to target's size.\n *\n * @param target React reference or Element to track.\n * @param callback Callback that will be invoked on resize.\n * @param enabled Whether resize observer is enabled or not.\n */\nexport function useResizeObserver<T extends Element>(\n target: RefObject<T> | T | null,\n callback: UseResizeObserverCallback,\n enabled = true,\n): void {\n const ro = enabled && getResizeObserver()\n const cb = useRef(callback)\n cb.current = callback\n\n const tgt = target && 'current' in target ? target.current : target\n\n useEffect(() => {\n // This secondary target resolve required for case when we receive ref object, which, most\n // likely, contains null during render stage, but already populated with element during\n // effect stage.\n\n const _tgt = target && 'current' in target ? target.current : target\n\n if (!ro || !_tgt) return undefined\n\n // As unsubscription in internals of our ResizeObserver abstraction can\n // happen a bit later than effect cleanup invocation - we need a marker,\n // that this handler should not be invoked anymore\n let subscribed = true\n\n const handler: UseResizeObserverCallback = (...args) => {\n // It is reinsurance for the highly asynchronous invocations, almost\n // impossible to achieve in tests, thus excluding from LOC\n /* istanbul ignore else */\n if (subscribed) {\n cb.current(...args)\n }\n }\n\n ro.subscribe(_tgt, handler)\n\n return () => {\n subscribed = false\n ro.unsubscribe(_tgt, handler)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [tgt, ro])\n}\n","import {getImageDimensions} from '@sanity/asset-utils'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport {get} from 'lodash-es'\nimport {type MouseEvent, type ReactNode, useCallback, useMemo, useRef, useState} from 'react'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n} from 'sanity'\n\nimport Feedback from './Feedback'\nimport {ImageHotspotOptions} from './plugin'\nimport Spot from './Spot'\nimport {useDebouncedCallback} from './useDebouncedCallback'\nimport {useResizeObserver} from './useResizeObserver'\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): ReactNode {\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: MouseEvent<HTMLImageElement>) => {\n const {nativeEvent, currentTarget} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / currentTarget.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / currentTarget.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\n useResizeObserver(\n hotspotImageRef,\n useDebouncedCallback(\n (e: ResizeObserverEntry) => setImageRect(e.contentRect),\n [setImageRect],\n 200,\n ),\n )\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 &quot;{imageHotspotOptions.pathRoot}&quot; is not valid,\n falling back to &quot;document&quot;. Available values are &quot;\n {VALID_ROOT_PATHS.join(', ')}&quot;.\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ComponentType} from 'react'\nimport {type ArrayOfObjectsInputProps, definePlugin} from 'sanity'\n\nimport {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'\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":["jsx","Card","Text","useState","useMotionValue","useEffect","useCallback","Tooltip","createElement","Box","get","jsxs","motion","useRef","useMemo","useClient","_a","useFormValue","imageUrlBuilder","getImageDimensions","randomKey","PatchEvent","setIfMissing","insert","set","Stack","Flex","Fragment","definePlugin"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/useDebouncedCallback.ts","../src/useResizeObserver.ts","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import {Card, Text} from '@sanity/ui'\nimport {type ReactNode} from 'react'\n\nexport default function Feedback({children}: {children: ReactNode}): ReactNode {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","import {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport {get} from 'lodash-es'\nimport {\n ComponentType,\n createElement,\n CSSProperties,\n ReactNode,\n useCallback,\n useEffect,\n useState,\n} from 'react'\nimport {type ObjectSchemaType, type RenderPreviewCallback} from 'sanity'\n\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\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): ReactNode {\n const [isDragging, setIsDragging] = useState(false)\n const [isHovering, setIsHovering] = 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 = 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 = useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = useCallback(() => setIsHovering(false), [])\n\n if (!x || !y) {\n return null\n }\n\n return (\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 <Tooltip\n key={value._key}\n disabled={isDragging}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n 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 <div>\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 </div>\n </Tooltip>\n </motion.div>\n )\n}\n","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useDebouncedCallback/index.ts\nimport {type DependencyList, useEffect, useMemo, useRef} from 'react'\n\n/**\n * Run effect only when component is unmounted.\n *\n * @param effect Effector to run on unmount\n */\nexport function useUnmountEffect(effect: CallableFunction): void {\n const effectRef = useRef(effect)\n effectRef.current = effect\n useEffect(() => () => effectRef.current(), [])\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DebouncedFunction<Fn extends (...args: any[]) => any> = (\n this: ThisParameterType<Fn>,\n ...args: Parameters<Fn>\n) => void\n\n/**\n * Makes passed function debounced, otherwise acts like `useCallback`.\n *\n * @param callback Function that will be debounced.\n * @param deps Dependencies list when to update callback. It also replaces invoked\n * \tcallback for scheduled debounced invocations.\n * @param delay Debounce delay.\n * @param maxWait The maximum time `callback` is allowed to be delayed before\n * it's invoked. 0 means no max wait.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useDebouncedCallback<Fn extends (...args: any[]) => any>(\n callback: Fn,\n deps: DependencyList,\n delay: number,\n maxWait = 0,\n): DebouncedFunction<Fn> {\n const timeout = useRef<ReturnType<typeof setTimeout>>()\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>()\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>()\n\n const clear = () => {\n if (timeout.current) {\n clearTimeout(timeout.current)\n timeout.current = undefined\n }\n\n if (waitTimeout.current) {\n clearTimeout(waitTimeout.current)\n waitTimeout.current = undefined\n }\n }\n\n // Cancel scheduled execution on unmount\n useUnmountEffect(clear)\n\n useEffect(() => {\n cb.current = callback\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n\n return useMemo(() => {\n const execute = () => {\n clear()\n\n // Barely possible to test this line\n /* istanbul ignore next */\n if (!lastCall.current) return\n\n const context = lastCall.current\n lastCall.current = undefined\n\n cb.current.apply(context.this, context.args)\n }\n\n const wrapped = function (this, ...args) {\n if (timeout.current) {\n clearTimeout(timeout.current)\n }\n\n lastCall.current = {args, this: this}\n\n // Plan regular execution\n timeout.current = setTimeout(execute, delay)\n\n // Plan maxWait execution if required\n if (maxWait > 0 && !waitTimeout.current) {\n waitTimeout.current = setTimeout(execute, maxWait)\n }\n } as DebouncedFunction<Fn>\n\n Object.defineProperties(wrapped, {\n length: {value: callback.length},\n name: {value: `${callback.name || 'anonymous'}__debounced__${delay}`},\n })\n\n return wrapped\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [delay, maxWait, ...deps])\n}\n","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useResizeObserver/index.ts\nimport {type RefObject, useEffect, useRef} from 'react'\n\nconst isBrowser =\n typeof window !== 'undefined' &&\n typeof navigator !== 'undefined' &&\n typeof document !== 'undefined'\n\nexport type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void\n\ntype ResizeObserverSingleton = {\n observer: ResizeObserver\n subscribe: (target: Element, callback: UseResizeObserverCallback) => void\n unsubscribe: (target: Element, callback: UseResizeObserverCallback) => void\n}\n\nlet observerSingleton: ResizeObserverSingleton\n\nfunction getResizeObserver(): ResizeObserverSingleton | undefined {\n if (!isBrowser) return undefined\n\n if (observerSingleton) return observerSingleton\n\n const callbacks = new Map<Element, Set<UseResizeObserverCallback>>()\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries)\n callbacks.get(entry.target)?.forEach((cb) =>\n setTimeout(() => {\n cb(entry)\n }, 0),\n )\n })\n\n observerSingleton = {\n observer,\n subscribe(target, callback) {\n let cbs = callbacks.get(target)\n\n if (!cbs) {\n // If target has no observers yet - register it\n cbs = new Set<UseResizeObserverCallback>()\n callbacks.set(target, cbs)\n observer.observe(target)\n }\n\n // As Set is duplicate-safe - simply add callback on each call\n cbs.add(callback)\n },\n unsubscribe(target, callback) {\n const cbs = callbacks.get(target)\n\n // Else branch should never occur in case of normal execution\n // because callbacks map is hidden in closure - it is impossible to\n // simulate situation with non-existent `cbs` Set\n /* istanbul ignore else */\n if (cbs) {\n // Remove current observer\n cbs.delete(callback)\n\n if (cbs.size === 0) {\n // If no observers left unregister target completely\n callbacks.delete(target)\n observer.unobserve(target)\n }\n }\n },\n }\n\n return observerSingleton\n}\n\n/**\n * Invokes a callback whenever ResizeObserver detects a change to target's size.\n *\n * @param target React reference or Element to track.\n * @param callback Callback that will be invoked on resize.\n * @param enabled Whether resize observer is enabled or not.\n */\nexport function useResizeObserver<T extends Element>(\n target: RefObject<T> | T | null,\n callback: UseResizeObserverCallback,\n enabled = true,\n): void {\n const ro = enabled && getResizeObserver()\n const cb = useRef(callback)\n cb.current = callback\n\n const tgt = target && 'current' in target ? target.current : target\n\n useEffect(() => {\n // This secondary target resolve required for case when we receive ref object, which, most\n // likely, contains null during render stage, but already populated with element during\n // effect stage.\n\n const _tgt = target && 'current' in target ? target.current : target\n\n if (!ro || !_tgt) return undefined\n\n // As unsubscription in internals of our ResizeObserver abstraction can\n // happen a bit later than effect cleanup invocation - we need a marker,\n // that this handler should not be invoked anymore\n let subscribed = true\n\n const handler: UseResizeObserverCallback = (...args) => {\n // It is reinsurance for the highly asynchronous invocations, almost\n // impossible to achieve in tests, thus excluding from LOC\n /* istanbul ignore else */\n if (subscribed) {\n cb.current(...args)\n }\n }\n\n ro.subscribe(_tgt, handler)\n\n return () => {\n subscribed = false\n ro.unsubscribe(_tgt, handler)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [tgt, ro])\n}\n","import {getImageDimensions} from '@sanity/asset-utils'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport {get} from 'lodash-es'\nimport {type MouseEvent, type ReactNode, useCallback, useMemo, useRef, useState} from 'react'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n SchemaType,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n useResolveInitialValueForType,\n} from 'sanity'\n\nimport Feedback from './Feedback'\nimport {ImageHotspotOptions} from './plugin'\nimport Spot from './Spot'\nimport {useDebouncedCallback} from './useDebouncedCallback'\nimport {useResizeObserver} from './useResizeObserver'\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): ReactNode {\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 const resolveInitialValueForType = useResolveInitialValueForType()\n const resolveInitialValue = useCallback(\n async (type: ObjectSchemaType) => {\n return resolveInitialValueForType(type as unknown as SchemaType, {})\n .then((initialValue) => {\n return initialValue\n })\n .catch(() => {\n return undefined\n })\n },\n [resolveInitialValueForType],\n )\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 async (event: MouseEvent<HTMLImageElement>) => {\n const {nativeEvent, currentTarget} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / currentTarget.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / currentTarget.height).toFixed(2))\n const description = `New Hotspot at ${x}% x ${y}%`\n\n const initialValues = await resolveInitialValue(schemaType.of[0].type as ObjectSchemaType)\n\n const newRow: HotspotItem = {\n _key: randomKey(12),\n _type: schemaType.of[0].name,\n ...initialValues,\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, resolveInitialValue, 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\n useResizeObserver(\n hotspotImageRef,\n useDebouncedCallback(\n (e: ResizeObserverEntry) => setImageRect(e.contentRect),\n [setImageRect],\n 200,\n ),\n )\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 &quot;{imageHotspotOptions.pathRoot}&quot; is not valid,\n falling back to &quot;document&quot;. Available values are &quot;\n {VALID_ROOT_PATHS.join(', ')}&quot;.\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ComponentType} from 'react'\nimport {type ArrayOfObjectsInputProps, definePlugin} from 'sanity'\n\nimport {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'\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":["jsx","Card","Text","useState","useMotionValue","useEffect","useCallback","motion","Tooltip","createElement","Box","get","useRef","useMemo","useClient","_a","useFormValue","useResolveInitialValueForType","imageUrlBuilder","getImageDimensions","randomKey","PatchEvent","setIfMissing","insert","set","Stack","jsxs","Flex","Fragment","definePlugin"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { Card, Text, Tooltip, Box, Stack, Flex } from "@sanity/ui";
5
5
  import { randomKey } from "@sanity/util/content";
6
6
  import { get } from "lodash-es";
7
7
  import { useState, useEffect, useCallback, createElement, useRef, useMemo } from "react";
8
- import { useClient, useFormValue, PatchEvent, setIfMissing, insert, set, definePlugin } from "sanity";
8
+ import { useClient, useFormValue, useResolveInitialValueForType, PatchEvent, setIfMissing, insert, set, definePlugin } from "sanity";
9
9
  import { useMotionValue, motion } from "framer-motion";
10
10
  function Feedback({ children }) {
11
11
  return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsx(Text, { children }) });
@@ -74,30 +74,30 @@ function Spot({
74
74
  update(value._key, safeX, safeY);
75
75
  }, [x, y, value, update, bounds]), handleDragStart = useCallback(() => setIsDragging(!0), []), handleHoverStart = useCallback(() => setIsHovering(!0), []), handleHoverEnd = useCallback(() => setIsHovering(!1), []);
76
76
  return !x || !y ? null : /* @__PURE__ */ jsx(
77
- Tooltip,
77
+ motion.div,
78
78
  {
79
- disabled: isDragging,
80
- portal: !0,
81
- content: tooltip && typeof tooltip == "function" ? createElement(tooltip, { value, renderPreview, schemaType }) : /* @__PURE__ */ jsx(Box, { padding: 2, style: { maxWidth: 200, pointerEvents: "none" }, children: /* @__PURE__ */ jsx(Text, { textOverflow: "ellipsis", children: hotspotDescriptionPath ? get(value, hotspotDescriptionPath) : `${value.x}% x ${value.y}%` }) }),
82
- children: /* @__PURE__ */ jsxs(
83
- motion.div,
79
+ drag: !0,
80
+ dragConstraints: bounds,
81
+ dragElastic: 0,
82
+ dragMomentum: !1,
83
+ onDragEnd: handleDragEnd,
84
+ onDragStart: handleDragStart,
85
+ onHoverStart: handleHoverStart,
86
+ onHoverEnd: handleHoverEnd,
87
+ style: {
88
+ ...dragStyle,
89
+ x,
90
+ y,
91
+ ...isDragging && { ...dragStyleWhileDrag },
92
+ ...isHovering && { ...dragStyleWhileHover }
93
+ },
94
+ children: /* @__PURE__ */ jsx(
95
+ Tooltip,
84
96
  {
85
- drag: !0,
86
- dragConstraints: bounds,
87
- dragElastic: 0,
88
- dragMomentum: !1,
89
- onDragEnd: handleDragEnd,
90
- onDragStart: handleDragStart,
91
- onHoverStart: handleHoverStart,
92
- onHoverEnd: handleHoverEnd,
93
- style: {
94
- ...dragStyle,
95
- x,
96
- y,
97
- ...isDragging && { ...dragStyleWhileDrag },
98
- ...isHovering && { ...dragStyleWhileHover }
99
- },
100
- children: [
97
+ disabled: isDragging,
98
+ portal: !0,
99
+ content: tooltip && typeof tooltip == "function" ? createElement(tooltip, { value, renderPreview, schemaType }) : /* @__PURE__ */ jsx(Box, { padding: 2, style: { maxWidth: 200, pointerEvents: "none" }, children: /* @__PURE__ */ jsx(Text, { textOverflow: "ellipsis", children: hotspotDescriptionPath ? get(value, hotspotDescriptionPath) : `${value.x}% x ${value.y}%` }) }),
100
+ children: /* @__PURE__ */ jsxs("div", { children: [
101
101
  /* @__PURE__ */ jsx(
102
102
  Box,
103
103
  {
@@ -117,11 +117,11 @@ function Spot({
117
117
  children: index + 1
118
118
  }
119
119
  )
120
- ]
121
- }
120
+ ] })
121
+ },
122
+ value._key
122
123
  )
123
- },
124
- value._key
124
+ }
125
125
  );
126
126
  }
127
127
  function useUnmountEffect(effect) {
@@ -200,7 +200,11 @@ function ImageHotspotArray(props) {
200
200
  const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = useMemo(() => {
201
201
  var _a2;
202
202
  return (VALID_ROOT_PATHS.includes((_a2 = imageHotspotOptions.pathRoot) != null ? _a2 : "") ? imageHotspotOptions.pathRoot : "document") === "document" ? [] : props.path.slice(0, -1);
203
- }, [imageHotspotOptions.pathRoot, props.path]), rootObject = useFormValue(imageHotspotPathRoot), hotspotImage = useMemo(() => get(rootObject, imageHotspotOptions.imagePath), [rootObject, imageHotspotOptions.imagePath]), displayImage = useMemo(() => {
203
+ }, [imageHotspotOptions.pathRoot, props.path]), rootObject = useFormValue(imageHotspotPathRoot), resolveInitialValueForType = useResolveInitialValueForType(), resolveInitialValue = useCallback(
204
+ async (type) => resolveInitialValueForType(type, {}).then((initialValue) => initialValue).catch(() => {
205
+ }),
206
+ [resolveInitialValueForType]
207
+ ), hotspotImage = useMemo(() => get(rootObject, imageHotspotOptions.imagePath), [rootObject, imageHotspotOptions.imagePath]), displayImage = useMemo(() => {
204
208
  var _a2, _b;
205
209
  const builder = imageUrlBuilder(sanityClient).dataset((_a2 = sanityClient.config().dataset) != null ? _a2 : ""), urlFor = (source) => builder.image(source);
206
210
  if ((_b = hotspotImage == null ? void 0 : hotspotImage.asset) != null && _b._ref) {
@@ -209,16 +213,17 @@ function ImageHotspotArray(props) {
209
213
  }
210
214
  return null;
211
215
  }, [hotspotImage, sanityClient]), handleHotspotImageClick = useCallback(
212
- (event) => {
213
- const { nativeEvent, currentTarget } = event, x = Number((nativeEvent.offsetX * 100 / currentTarget.width).toFixed(2)), y = Number((nativeEvent.offsetY * 100 / currentTarget.height).toFixed(2)), description = `New Hotspot at ${x}% x ${y}%`, newRow = {
216
+ async (event) => {
217
+ const { nativeEvent, currentTarget } = event, x = Number((nativeEvent.offsetX * 100 / currentTarget.width).toFixed(2)), y = Number((nativeEvent.offsetY * 100 / currentTarget.height).toFixed(2)), description = `New Hotspot at ${x}% x ${y}%`, initialValues = await resolveInitialValue(schemaType.of[0].type), newRow = {
214
218
  _key: randomKey(12),
215
219
  _type: schemaType.of[0].name,
220
+ ...initialValues,
216
221
  x,
217
222
  y
218
223
  };
219
224
  imageHotspotOptions != null && imageHotspotOptions.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(PatchEvent.from([setIfMissing([]), insert([newRow], "after", [-1])]));
220
225
  },
221
- [imageHotspotOptions, onChange, schemaType]
226
+ [imageHotspotOptions, onChange, resolveInitialValue, schemaType]
222
227
  ), handleHotspotMove = useCallback(
223
228
  (key, x, y) => {
224
229
  onChange(
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/useDebouncedCallback.ts","../src/useResizeObserver.ts","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import {Card, Text} from '@sanity/ui'\nimport {type ReactNode} from 'react'\n\nexport default function Feedback({children}: {children: ReactNode}): ReactNode {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","import {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport {get} from 'lodash-es'\nimport {\n ComponentType,\n createElement,\n CSSProperties,\n ReactNode,\n useCallback,\n useEffect,\n useState,\n} from 'react'\nimport {type ObjectSchemaType, type RenderPreviewCallback} from 'sanity'\n\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\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): ReactNode {\n const [isDragging, setIsDragging] = useState(false)\n const [isHovering, setIsHovering] = 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 = 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 = useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = 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 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","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useDebouncedCallback/index.ts\nimport {type DependencyList, useEffect, useMemo, useRef} from 'react'\n\n/**\n * Run effect only when component is unmounted.\n *\n * @param effect Effector to run on unmount\n */\nexport function useUnmountEffect(effect: CallableFunction): void {\n const effectRef = useRef(effect)\n effectRef.current = effect\n useEffect(() => () => effectRef.current(), [])\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DebouncedFunction<Fn extends (...args: any[]) => any> = (\n this: ThisParameterType<Fn>,\n ...args: Parameters<Fn>\n) => void\n\n/**\n * Makes passed function debounced, otherwise acts like `useCallback`.\n *\n * @param callback Function that will be debounced.\n * @param deps Dependencies list when to update callback. It also replaces invoked\n * \tcallback for scheduled debounced invocations.\n * @param delay Debounce delay.\n * @param maxWait The maximum time `callback` is allowed to be delayed before\n * it's invoked. 0 means no max wait.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useDebouncedCallback<Fn extends (...args: any[]) => any>(\n callback: Fn,\n deps: DependencyList,\n delay: number,\n maxWait = 0,\n): DebouncedFunction<Fn> {\n const timeout = useRef<ReturnType<typeof setTimeout>>()\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>()\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>()\n\n const clear = () => {\n if (timeout.current) {\n clearTimeout(timeout.current)\n timeout.current = undefined\n }\n\n if (waitTimeout.current) {\n clearTimeout(waitTimeout.current)\n waitTimeout.current = undefined\n }\n }\n\n // Cancel scheduled execution on unmount\n useUnmountEffect(clear)\n\n useEffect(() => {\n cb.current = callback\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n\n return useMemo(() => {\n const execute = () => {\n clear()\n\n // Barely possible to test this line\n /* istanbul ignore next */\n if (!lastCall.current) return\n\n const context = lastCall.current\n lastCall.current = undefined\n\n cb.current.apply(context.this, context.args)\n }\n\n const wrapped = function (this, ...args) {\n if (timeout.current) {\n clearTimeout(timeout.current)\n }\n\n lastCall.current = {args, this: this}\n\n // Plan regular execution\n timeout.current = setTimeout(execute, delay)\n\n // Plan maxWait execution if required\n if (maxWait > 0 && !waitTimeout.current) {\n waitTimeout.current = setTimeout(execute, maxWait)\n }\n } as DebouncedFunction<Fn>\n\n Object.defineProperties(wrapped, {\n length: {value: callback.length},\n name: {value: `${callback.name || 'anonymous'}__debounced__${delay}`},\n })\n\n return wrapped\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [delay, maxWait, ...deps])\n}\n","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useResizeObserver/index.ts\nimport {type RefObject, useEffect, useRef} from 'react'\n\nconst isBrowser =\n typeof window !== 'undefined' &&\n typeof navigator !== 'undefined' &&\n typeof document !== 'undefined'\n\nexport type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void\n\ntype ResizeObserverSingleton = {\n observer: ResizeObserver\n subscribe: (target: Element, callback: UseResizeObserverCallback) => void\n unsubscribe: (target: Element, callback: UseResizeObserverCallback) => void\n}\n\nlet observerSingleton: ResizeObserverSingleton\n\nfunction getResizeObserver(): ResizeObserverSingleton | undefined {\n if (!isBrowser) return undefined\n\n if (observerSingleton) return observerSingleton\n\n const callbacks = new Map<Element, Set<UseResizeObserverCallback>>()\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries)\n callbacks.get(entry.target)?.forEach((cb) =>\n setTimeout(() => {\n cb(entry)\n }, 0),\n )\n })\n\n observerSingleton = {\n observer,\n subscribe(target, callback) {\n let cbs = callbacks.get(target)\n\n if (!cbs) {\n // If target has no observers yet - register it\n cbs = new Set<UseResizeObserverCallback>()\n callbacks.set(target, cbs)\n observer.observe(target)\n }\n\n // As Set is duplicate-safe - simply add callback on each call\n cbs.add(callback)\n },\n unsubscribe(target, callback) {\n const cbs = callbacks.get(target)\n\n // Else branch should never occur in case of normal execution\n // because callbacks map is hidden in closure - it is impossible to\n // simulate situation with non-existent `cbs` Set\n /* istanbul ignore else */\n if (cbs) {\n // Remove current observer\n cbs.delete(callback)\n\n if (cbs.size === 0) {\n // If no observers left unregister target completely\n callbacks.delete(target)\n observer.unobserve(target)\n }\n }\n },\n }\n\n return observerSingleton\n}\n\n/**\n * Invokes a callback whenever ResizeObserver detects a change to target's size.\n *\n * @param target React reference or Element to track.\n * @param callback Callback that will be invoked on resize.\n * @param enabled Whether resize observer is enabled or not.\n */\nexport function useResizeObserver<T extends Element>(\n target: RefObject<T> | T | null,\n callback: UseResizeObserverCallback,\n enabled = true,\n): void {\n const ro = enabled && getResizeObserver()\n const cb = useRef(callback)\n cb.current = callback\n\n const tgt = target && 'current' in target ? target.current : target\n\n useEffect(() => {\n // This secondary target resolve required for case when we receive ref object, which, most\n // likely, contains null during render stage, but already populated with element during\n // effect stage.\n\n const _tgt = target && 'current' in target ? target.current : target\n\n if (!ro || !_tgt) return undefined\n\n // As unsubscription in internals of our ResizeObserver abstraction can\n // happen a bit later than effect cleanup invocation - we need a marker,\n // that this handler should not be invoked anymore\n let subscribed = true\n\n const handler: UseResizeObserverCallback = (...args) => {\n // It is reinsurance for the highly asynchronous invocations, almost\n // impossible to achieve in tests, thus excluding from LOC\n /* istanbul ignore else */\n if (subscribed) {\n cb.current(...args)\n }\n }\n\n ro.subscribe(_tgt, handler)\n\n return () => {\n subscribed = false\n ro.unsubscribe(_tgt, handler)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [tgt, ro])\n}\n","import {getImageDimensions} from '@sanity/asset-utils'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport {get} from 'lodash-es'\nimport {type MouseEvent, type ReactNode, useCallback, useMemo, useRef, useState} from 'react'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n} from 'sanity'\n\nimport Feedback from './Feedback'\nimport {ImageHotspotOptions} from './plugin'\nimport Spot from './Spot'\nimport {useDebouncedCallback} from './useDebouncedCallback'\nimport {useResizeObserver} from './useResizeObserver'\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): ReactNode {\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: MouseEvent<HTMLImageElement>) => {\n const {nativeEvent, currentTarget} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / currentTarget.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / currentTarget.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\n useResizeObserver(\n hotspotImageRef,\n useDebouncedCallback(\n (e: ResizeObserverEntry) => setImageRect(e.contentRect),\n [setImageRect],\n 200,\n ),\n )\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 &quot;{imageHotspotOptions.pathRoot}&quot; is not valid,\n falling back to &quot;document&quot;. Available values are &quot;\n {VALID_ROOT_PATHS.join(', ')}&quot;.\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ComponentType} from 'react'\nimport {type ArrayOfObjectsInputProps, definePlugin} from 'sanity'\n\nimport {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'\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":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/useDebouncedCallback.ts","../src/useResizeObserver.ts","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import {Card, Text} from '@sanity/ui'\nimport {type ReactNode} from 'react'\n\nexport default function Feedback({children}: {children: ReactNode}): ReactNode {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","import {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport {get} from 'lodash-es'\nimport {\n ComponentType,\n createElement,\n CSSProperties,\n ReactNode,\n useCallback,\n useEffect,\n useState,\n} from 'react'\nimport {type ObjectSchemaType, type RenderPreviewCallback} from 'sanity'\n\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\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): ReactNode {\n const [isDragging, setIsDragging] = useState(false)\n const [isHovering, setIsHovering] = 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 = 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 = useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = useCallback(() => setIsHovering(false), [])\n\n if (!x || !y) {\n return null\n }\n\n return (\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 <Tooltip\n key={value._key}\n disabled={isDragging}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n 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 <div>\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 </div>\n </Tooltip>\n </motion.div>\n )\n}\n","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useDebouncedCallback/index.ts\nimport {type DependencyList, useEffect, useMemo, useRef} from 'react'\n\n/**\n * Run effect only when component is unmounted.\n *\n * @param effect Effector to run on unmount\n */\nexport function useUnmountEffect(effect: CallableFunction): void {\n const effectRef = useRef(effect)\n effectRef.current = effect\n useEffect(() => () => effectRef.current(), [])\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DebouncedFunction<Fn extends (...args: any[]) => any> = (\n this: ThisParameterType<Fn>,\n ...args: Parameters<Fn>\n) => void\n\n/**\n * Makes passed function debounced, otherwise acts like `useCallback`.\n *\n * @param callback Function that will be debounced.\n * @param deps Dependencies list when to update callback. It also replaces invoked\n * \tcallback for scheduled debounced invocations.\n * @param delay Debounce delay.\n * @param maxWait The maximum time `callback` is allowed to be delayed before\n * it's invoked. 0 means no max wait.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useDebouncedCallback<Fn extends (...args: any[]) => any>(\n callback: Fn,\n deps: DependencyList,\n delay: number,\n maxWait = 0,\n): DebouncedFunction<Fn> {\n const timeout = useRef<ReturnType<typeof setTimeout>>()\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>()\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>()\n\n const clear = () => {\n if (timeout.current) {\n clearTimeout(timeout.current)\n timeout.current = undefined\n }\n\n if (waitTimeout.current) {\n clearTimeout(waitTimeout.current)\n waitTimeout.current = undefined\n }\n }\n\n // Cancel scheduled execution on unmount\n useUnmountEffect(clear)\n\n useEffect(() => {\n cb.current = callback\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n\n return useMemo(() => {\n const execute = () => {\n clear()\n\n // Barely possible to test this line\n /* istanbul ignore next */\n if (!lastCall.current) return\n\n const context = lastCall.current\n lastCall.current = undefined\n\n cb.current.apply(context.this, context.args)\n }\n\n const wrapped = function (this, ...args) {\n if (timeout.current) {\n clearTimeout(timeout.current)\n }\n\n lastCall.current = {args, this: this}\n\n // Plan regular execution\n timeout.current = setTimeout(execute, delay)\n\n // Plan maxWait execution if required\n if (maxWait > 0 && !waitTimeout.current) {\n waitTimeout.current = setTimeout(execute, maxWait)\n }\n } as DebouncedFunction<Fn>\n\n Object.defineProperties(wrapped, {\n length: {value: callback.length},\n name: {value: `${callback.name || 'anonymous'}__debounced__${delay}`},\n })\n\n return wrapped\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [delay, maxWait, ...deps])\n}\n","// copied from react-hookz/web\n// https://github.com/react-hookz/web/blob/579a445fcc9f4f4bb5b9d5e670b2e57448b4ee50/src/useResizeObserver/index.ts\nimport {type RefObject, useEffect, useRef} from 'react'\n\nconst isBrowser =\n typeof window !== 'undefined' &&\n typeof navigator !== 'undefined' &&\n typeof document !== 'undefined'\n\nexport type UseResizeObserverCallback = (entry: ResizeObserverEntry) => void\n\ntype ResizeObserverSingleton = {\n observer: ResizeObserver\n subscribe: (target: Element, callback: UseResizeObserverCallback) => void\n unsubscribe: (target: Element, callback: UseResizeObserverCallback) => void\n}\n\nlet observerSingleton: ResizeObserverSingleton\n\nfunction getResizeObserver(): ResizeObserverSingleton | undefined {\n if (!isBrowser) return undefined\n\n if (observerSingleton) return observerSingleton\n\n const callbacks = new Map<Element, Set<UseResizeObserverCallback>>()\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries)\n callbacks.get(entry.target)?.forEach((cb) =>\n setTimeout(() => {\n cb(entry)\n }, 0),\n )\n })\n\n observerSingleton = {\n observer,\n subscribe(target, callback) {\n let cbs = callbacks.get(target)\n\n if (!cbs) {\n // If target has no observers yet - register it\n cbs = new Set<UseResizeObserverCallback>()\n callbacks.set(target, cbs)\n observer.observe(target)\n }\n\n // As Set is duplicate-safe - simply add callback on each call\n cbs.add(callback)\n },\n unsubscribe(target, callback) {\n const cbs = callbacks.get(target)\n\n // Else branch should never occur in case of normal execution\n // because callbacks map is hidden in closure - it is impossible to\n // simulate situation with non-existent `cbs` Set\n /* istanbul ignore else */\n if (cbs) {\n // Remove current observer\n cbs.delete(callback)\n\n if (cbs.size === 0) {\n // If no observers left unregister target completely\n callbacks.delete(target)\n observer.unobserve(target)\n }\n }\n },\n }\n\n return observerSingleton\n}\n\n/**\n * Invokes a callback whenever ResizeObserver detects a change to target's size.\n *\n * @param target React reference or Element to track.\n * @param callback Callback that will be invoked on resize.\n * @param enabled Whether resize observer is enabled or not.\n */\nexport function useResizeObserver<T extends Element>(\n target: RefObject<T> | T | null,\n callback: UseResizeObserverCallback,\n enabled = true,\n): void {\n const ro = enabled && getResizeObserver()\n const cb = useRef(callback)\n cb.current = callback\n\n const tgt = target && 'current' in target ? target.current : target\n\n useEffect(() => {\n // This secondary target resolve required for case when we receive ref object, which, most\n // likely, contains null during render stage, but already populated with element during\n // effect stage.\n\n const _tgt = target && 'current' in target ? target.current : target\n\n if (!ro || !_tgt) return undefined\n\n // As unsubscription in internals of our ResizeObserver abstraction can\n // happen a bit later than effect cleanup invocation - we need a marker,\n // that this handler should not be invoked anymore\n let subscribed = true\n\n const handler: UseResizeObserverCallback = (...args) => {\n // It is reinsurance for the highly asynchronous invocations, almost\n // impossible to achieve in tests, thus excluding from LOC\n /* istanbul ignore else */\n if (subscribed) {\n cb.current(...args)\n }\n }\n\n ro.subscribe(_tgt, handler)\n\n return () => {\n subscribed = false\n ro.unsubscribe(_tgt, handler)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [tgt, ro])\n}\n","import {getImageDimensions} from '@sanity/asset-utils'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport {get} from 'lodash-es'\nimport {type MouseEvent, type ReactNode, useCallback, useMemo, useRef, useState} from 'react'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n SchemaType,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n useResolveInitialValueForType,\n} from 'sanity'\n\nimport Feedback from './Feedback'\nimport {ImageHotspotOptions} from './plugin'\nimport Spot from './Spot'\nimport {useDebouncedCallback} from './useDebouncedCallback'\nimport {useResizeObserver} from './useResizeObserver'\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): ReactNode {\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 const resolveInitialValueForType = useResolveInitialValueForType()\n const resolveInitialValue = useCallback(\n async (type: ObjectSchemaType) => {\n return resolveInitialValueForType(type as unknown as SchemaType, {})\n .then((initialValue) => {\n return initialValue\n })\n .catch(() => {\n return undefined\n })\n },\n [resolveInitialValueForType],\n )\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 async (event: MouseEvent<HTMLImageElement>) => {\n const {nativeEvent, currentTarget} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / currentTarget.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / currentTarget.height).toFixed(2))\n const description = `New Hotspot at ${x}% x ${y}%`\n\n const initialValues = await resolveInitialValue(schemaType.of[0].type as ObjectSchemaType)\n\n const newRow: HotspotItem = {\n _key: randomKey(12),\n _type: schemaType.of[0].name,\n ...initialValues,\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, resolveInitialValue, 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\n useResizeObserver(\n hotspotImageRef,\n useDebouncedCallback(\n (e: ResizeObserverEntry) => setImageRect(e.contentRect),\n [setImageRect],\n 200,\n ),\n )\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 &quot;{imageHotspotOptions.pathRoot}&quot; is not valid,\n falling back to &quot;document&quot;. Available values are &quot;\n {VALID_ROOT_PATHS.join(', ')}&quot;.\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ComponentType} from 'react'\nimport {type ArrayOfObjectsInputProps, definePlugin} from 'sanity'\n\nimport {type HotspotItem, ImageHotspotArray} from './ImageHotspotArray'\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":["_a"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-hotspot-array",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "workspaces": [
5
5
  "./examples/*"
6
6
  ],
@@ -10,10 +10,12 @@ import {
10
10
  insert,
11
11
  ObjectSchemaType,
12
12
  PatchEvent,
13
+ SchemaType,
13
14
  set,
14
15
  setIfMissing,
15
16
  useClient,
16
17
  useFormValue,
18
+ useResolveInitialValueForType,
17
19
  } from 'sanity'
18
20
 
19
21
  import Feedback from './Feedback'
@@ -51,6 +53,20 @@ export function ImageHotspotArray(
51
53
 
52
54
  const rootObject = useFormValue(imageHotspotPathRoot)
53
55
 
56
+ const resolveInitialValueForType = useResolveInitialValueForType()
57
+ const resolveInitialValue = useCallback(
58
+ async (type: ObjectSchemaType) => {
59
+ return resolveInitialValueForType(type as unknown as SchemaType, {})
60
+ .then((initialValue) => {
61
+ return initialValue
62
+ })
63
+ .catch(() => {
64
+ return undefined
65
+ })
66
+ },
67
+ [resolveInitialValueForType],
68
+ )
69
+
54
70
  /**
55
71
  * Finding the image from the imageHotspotPathRoot (defaults to document),
56
72
  * using the path from the hotspot's `options` field
@@ -79,7 +95,7 @@ export function ImageHotspotArray(
79
95
  }, [hotspotImage, sanityClient])
80
96
 
81
97
  const handleHotspotImageClick = useCallback(
82
- (event: MouseEvent<HTMLImageElement>) => {
98
+ async (event: MouseEvent<HTMLImageElement>) => {
83
99
  const {nativeEvent, currentTarget} = event
84
100
 
85
101
  // Calculate the x/y percentage of the click position
@@ -87,9 +103,12 @@ export function ImageHotspotArray(
87
103
  const y = Number(((nativeEvent.offsetY * 100) / currentTarget.height).toFixed(2))
88
104
  const description = `New Hotspot at ${x}% x ${y}%`
89
105
 
106
+ const initialValues = await resolveInitialValue(schemaType.of[0].type as ObjectSchemaType)
107
+
90
108
  const newRow: HotspotItem = {
91
109
  _key: randomKey(12),
92
110
  _type: schemaType.of[0].name,
111
+ ...initialValues,
93
112
  x,
94
113
  y,
95
114
  }
@@ -100,7 +119,7 @@ export function ImageHotspotArray(
100
119
 
101
120
  onChange(PatchEvent.from([setIfMissing([]), insert([newRow], 'after', [-1])]))
102
121
  },
103
- [imageHotspotOptions, onChange, schemaType],
122
+ [imageHotspotOptions, onChange, resolveInitialValue, schemaType],
104
123
  )
105
124
 
106
125
  const handleHotspotMove: FnHotspotMove = useCallback(
package/src/Spot.tsx CHANGED
@@ -142,58 +142,60 @@ export default function Spot({
142
142
  }
143
143
 
144
144
  return (
145
- <Tooltip
146
- key={value._key}
147
- disabled={isDragging}
148
- portal
149
- content={
150
- tooltip && typeof tooltip === 'function' ? (
151
- createElement(tooltip, {value, renderPreview, schemaType})
152
- ) : (
153
- <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>
154
- <Text textOverflow="ellipsis">
155
- {hotspotDescriptionPath
156
- ? (get(value, hotspotDescriptionPath) as string)
157
- : `${value.x}% x ${value.y}%`}
158
- </Text>
159
- </Box>
160
- )
161
- }
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
+ }}
162
161
  >
163
- <motion.div
164
- drag
165
- dragConstraints={bounds}
166
- dragElastic={0}
167
- dragMomentum={false}
168
- onDragEnd={handleDragEnd}
169
- onDragStart={handleDragStart}
170
- onHoverStart={handleHoverStart}
171
- onHoverEnd={handleHoverEnd}
172
- style={{
173
- ...dragStyle,
174
- x,
175
- y,
176
- ...(isDragging && {...dragStyleWhileDrag}),
177
- ...(isHovering && {...dragStyleWhileHover}),
178
- }}
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
179
  >
180
- {/* Dot */}
181
- <Box
182
- style={{
183
- ...dotStyle,
184
- ...((isDragging || isHovering) && {...dotStyleWhileActive}),
185
- }}
186
- />
187
- {/* Label */}
188
- <div
189
- style={{
190
- ...labelStyle,
191
- ...((isDragging || isHovering) && {...labelStyleWhileActive}),
192
- }}
193
- >
194
- {index + 1}
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>
195
197
  </div>
196
- </motion.div>
197
- </Tooltip>
198
+ </Tooltip>
199
+ </motion.div>
198
200
  )
199
201
  }