sanity-plugin-hotspot-array 3.0.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Sanity.io
3
+ Copyright (c) 2026 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.js CHANGED
@@ -1,12 +1,14 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var jsxRuntime = require("react/jsx-runtime"), assetUtils = require("@sanity/asset-utils"), imageUrlBuilder = require("@sanity/image-url"), ui = require("@sanity/ui"), content = require("@sanity/util/content"), lodashEs = require("lodash-es"), react = require("react"), sanity = require("sanity"), framerMotion = require("framer-motion");
4
- function _interopDefaultCompat(e) {
5
- return e && typeof e == "object" && "default" in e ? e : { default: e };
6
- }
7
- var imageUrlBuilder__default = /* @__PURE__ */ _interopDefaultCompat(imageUrlBuilder);
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { getImageDimensions } from "@sanity/asset-utils";
3
+ import imageUrlBuilder from "@sanity/image-url";
4
+ import { Card, Text, Tooltip, Box, Stack, Flex } from "@sanity/ui";
5
+ import { randomKey } from "@sanity/util/content";
6
+ import { get } from "lodash-es";
7
+ import { useState, useEffect, useCallback, createElement, useRef, useMemo } from "react";
8
+ import { useClient, useFormValue, useResolveInitialValueForType, PatchEvent, setIfMissing, insert, set, definePlugin } from "sanity";
9
+ import { useMotionValue, motion } from "framer-motion";
8
10
  function Feedback({ children }) {
9
- return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 4, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children }) });
11
+ return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsx(Text, { children }) });
10
12
  }
11
13
  const dragStyle = {
12
14
  width: "1.4rem",
@@ -62,17 +64,17 @@ function Spot({
62
64
  schemaType,
63
65
  renderPreview
64
66
  }) {
65
- const [isDragging, setIsDragging] = react.useState(!1), [isHovering, setIsHovering] = react.useState(!1), x = framerMotion.useMotionValue(round(bounds.width * (value.x / 100))), y = framerMotion.useMotionValue(round(bounds.height * (value.y / 100)));
66
- react.useEffect(() => {
67
+ const [isDragging, setIsDragging] = useState(!1), [isHovering, setIsHovering] = useState(!1), x = useMotionValue(round(bounds.width * (value.x / 100))), y = useMotionValue(round(bounds.height * (value.y / 100)));
68
+ useEffect(() => {
67
69
  x.set(round(bounds.width * (value.x / 100))), y.set(round(bounds.height * (value.y / 100)));
68
70
  }, [x, y, value, bounds]);
69
- const handleDragEnd = react.useCallback(() => {
71
+ const handleDragEnd = useCallback(() => {
70
72
  setIsDragging(!1);
71
73
  const currentX = x.get(), currentY = y.get(), newX = round(currentX * 100 / bounds.width), newY = round(currentY * 100 / bounds.height), safeX = Math.max(0, Math.min(100, newX)), safeY = Math.max(0, Math.min(100, newY));
72
74
  update(value._key, safeX, safeY);
73
- }, [x, y, value, update, bounds]), handleDragStart = react.useCallback(() => setIsDragging(!0), []), handleHoverStart = react.useCallback(() => setIsHovering(!0), []), handleHoverEnd = react.useCallback(() => setIsHovering(!1), []);
74
- return !x || !y ? null : /* @__PURE__ */ jsxRuntime.jsx(
75
- framerMotion.motion.div,
75
+ }, [x, y, value, update, bounds]), handleDragStart = useCallback(() => setIsDragging(!0), []), handleHoverStart = useCallback(() => setIsHovering(!0), []), handleHoverEnd = useCallback(() => setIsHovering(!1), []);
76
+ return !x || !y ? null : /* @__PURE__ */ jsx(
77
+ motion.div,
76
78
  {
77
79
  drag: !0,
78
80
  dragConstraints: bounds,
@@ -89,15 +91,15 @@ function Spot({
89
91
  ...isDragging && { ...dragStyleWhileDrag },
90
92
  ...isHovering && { ...dragStyleWhileHover }
91
93
  },
92
- children: /* @__PURE__ */ jsxRuntime.jsx(
93
- ui.Tooltip,
94
+ children: /* @__PURE__ */ jsx(
95
+ Tooltip,
94
96
  {
95
97
  disabled: isDragging,
96
98
  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
- /* @__PURE__ */ jsxRuntime.jsx(
100
- ui.Box,
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
+ /* @__PURE__ */ jsx(
102
+ Box,
101
103
  {
102
104
  style: {
103
105
  ...dotStyle,
@@ -105,7 +107,7 @@ function Spot({
105
107
  }
106
108
  }
107
109
  ),
108
- /* @__PURE__ */ jsxRuntime.jsx(
110
+ /* @__PURE__ */ jsx(
109
111
  "div",
110
112
  {
111
113
  style: {
@@ -123,16 +125,16 @@ function Spot({
123
125
  );
124
126
  }
125
127
  function useUnmountEffect(effect) {
126
- const effectRef = react.useRef(effect);
127
- effectRef.current = effect, react.useEffect(() => () => effectRef.current(), []);
128
+ const effectRef = useRef(effect);
129
+ effectRef.current = effect, useEffect(() => () => effectRef.current(), []);
128
130
  }
129
131
  function useDebouncedCallback(callback, deps, delay, maxWait = 0) {
130
- const timeout = react.useRef(void 0), waitTimeout = react.useRef(void 0), cb = react.useRef(callback), lastCall = react.useRef(void 0), clear = () => {
132
+ const timeout = useRef(void 0), waitTimeout = useRef(void 0), cb = useRef(callback), lastCall = useRef(void 0), clear = () => {
131
133
  timeout.current && (clearTimeout(timeout.current), timeout.current = void 0), waitTimeout.current && (clearTimeout(waitTimeout.current), waitTimeout.current = void 0);
132
134
  };
133
- return useUnmountEffect(clear), react.useEffect(() => {
135
+ return useUnmountEffect(clear), useEffect(() => {
134
136
  cb.current = callback;
135
- }, deps), react.useMemo(() => {
137
+ }, deps), useMemo(() => {
136
138
  const execute = () => {
137
139
  if (clear(), !lastCall.current) return;
138
140
  const context = lastCall.current;
@@ -172,10 +174,10 @@ function getResizeObserver() {
172
174
  }, observerSingleton;
173
175
  }
174
176
  function useResizeObserver(target, callback, enabled = !0) {
175
- const ro = enabled && getResizeObserver(), cb = react.useRef(callback);
177
+ const ro = enabled && getResizeObserver(), cb = useRef(callback);
176
178
  cb.current = callback;
177
179
  const tgt = target && "current" in target ? target.current : target;
178
- react.useEffect(() => {
180
+ useEffect(() => {
179
181
  const _tgt = target && "current" in target ? target.current : target;
180
182
  if (!ro || !_tgt) return;
181
183
  let subscribed = !0;
@@ -189,42 +191,42 @@ function useResizeObserver(target, callback, enabled = !0) {
189
191
  }
190
192
  const imageStyle = { width: "100%", height: "auto" }, VALID_ROOT_PATHS = ["document", "parent"];
191
193
  function ImageHotspotArray(props) {
192
- const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = sanity.useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = react.useMemo(() => (VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? "") ? imageHotspotOptions.pathRoot : "document") === "document" ? [] : props.path.slice(0, -1), [imageHotspotOptions.pathRoot, props.path]), rootObject = sanity.useFormValue(imageHotspotPathRoot), resolveInitialValueForType = sanity.useResolveInitialValueForType(), resolveInitialValue = react.useCallback(
194
+ const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = useMemo(() => (VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? "") ? imageHotspotOptions.pathRoot : "document") === "document" ? [] : props.path.slice(0, -1), [imageHotspotOptions.pathRoot, props.path]), rootObject = useFormValue(imageHotspotPathRoot), resolveInitialValueForType = useResolveInitialValueForType(), resolveInitialValue = useCallback(
193
195
  async (type) => resolveInitialValueForType(type, {}).then((initialValue) => initialValue).catch(() => {
194
196
  }),
195
197
  [resolveInitialValueForType]
196
- ), hotspotImage = react.useMemo(() => imageHotspotOptions.imagePath ? lodashEs.get(rootObject, imageHotspotOptions.imagePath) : rootObject, [rootObject, imageHotspotOptions.imagePath]), displayImage = react.useMemo(() => {
197
- const builder = imageUrlBuilder__default.default(sanityClient).dataset(sanityClient.config().dataset ?? ""), urlFor = (source) => builder.image(source);
198
+ ), hotspotImage = useMemo(() => imageHotspotOptions.imagePath ? get(rootObject, imageHotspotOptions.imagePath) : rootObject, [rootObject, imageHotspotOptions.imagePath]), displayImage = useMemo(() => {
199
+ const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? ""), urlFor = (source) => builder.image(source);
198
200
  if (hotspotImage?.asset?._ref) {
199
- const { aspectRatio } = assetUtils.getImageDimensions(hotspotImage.asset._ref), width = 1200, height = Math.round(width / aspectRatio), url = urlFor(hotspotImage).width(width).url();
201
+ const { aspectRatio } = getImageDimensions(hotspotImage.asset._ref), width = 1200, height = Math.round(width / aspectRatio), url = urlFor(hotspotImage).width(width).url();
200
202
  return { width, height, url };
201
203
  }
202
204
  return null;
203
- }, [hotspotImage, sanityClient]), handleHotspotImageClick = react.useCallback(
205
+ }, [hotspotImage, sanityClient]), handleHotspotImageClick = useCallback(
204
206
  async (event) => {
205
207
  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 = {
206
- _key: content.randomKey(12),
208
+ _key: randomKey(12),
207
209
  _type: schemaType.of[0].name,
208
210
  ...initialValues,
209
211
  x,
210
212
  y
211
213
  };
212
- imageHotspotOptions?.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(sanity.PatchEvent.from([sanity.setIfMissing([]), sanity.insert([newRow], "after", [-1])]));
214
+ imageHotspotOptions?.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(PatchEvent.from([setIfMissing([]), insert([newRow], "after", [-1])]));
213
215
  },
214
216
  [imageHotspotOptions, onChange, resolveInitialValue, schemaType]
215
- ), handleHotspotMove = react.useCallback(
217
+ ), handleHotspotMove = useCallback(
216
218
  (key, x, y) => {
217
219
  onChange(
218
- sanity.PatchEvent.from([
220
+ PatchEvent.from([
219
221
  // Set the `x` value of this array key item
220
- sanity.set(x, [{ _key: key }, "x"]),
222
+ set(x, [{ _key: key }, "x"]),
221
223
  // Set the `y` value of this array key item
222
- sanity.set(y, [{ _key: key }, "y"])
224
+ set(y, [{ _key: key }, "y"])
223
225
  ])
224
226
  );
225
227
  },
226
228
  [onChange]
227
- ), hotspotImageRef = react.useRef(null), [imageRect, setImageRect] = react.useState();
229
+ ), hotspotImageRef = useRef(null), [imageRect, setImageRect] = useState();
228
230
  return useResizeObserver(
229
231
  hotspotImageRef,
230
232
  useDebouncedCallback(
@@ -232,9 +234,9 @@ function ImageHotspotArray(props) {
232
234
  [setImageRect],
233
235
  200
234
236
  )
235
- ), /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: [2, 2, 3], children: [
236
- displayImage?.url ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
237
- imageRect && (value?.length ?? 0) > 0 && value?.map((spot, index) => /* @__PURE__ */ jsxRuntime.jsx(
237
+ ), /* @__PURE__ */ jsxs(Stack, { space: [2, 2, 3], children: [
238
+ displayImage?.url ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
239
+ imageRect && (value?.length ?? 0) > 0 && value?.map((spot, index) => /* @__PURE__ */ jsx(
238
240
  Spot,
239
241
  {
240
242
  index,
@@ -248,7 +250,7 @@ function ImageHotspotArray(props) {
248
250
  },
249
251
  spot._key
250
252
  )),
251
- /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { __unstable_checkered: !0, shadow: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(
253
+ /* @__PURE__ */ jsx(Card, { __unstable_checkered: !0, shadow: 1, children: /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", children: /* @__PURE__ */ jsx(
252
254
  "img",
253
255
  {
254
256
  ref: hotspotImageRef,
@@ -260,15 +262,15 @@ function ImageHotspotArray(props) {
260
262
  onClick: handleHotspotImageClick
261
263
  }
262
264
  ) }) })
263
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(Feedback, { children: imageHotspotOptions.imagePath ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
265
+ ] }) : /* @__PURE__ */ jsx(Feedback, { children: imageHotspotOptions.imagePath ? /* @__PURE__ */ jsxs(Fragment, { children: [
264
266
  "No Hotspot image found at path ",
265
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: imageHotspotOptions.imagePath })
266
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
267
+ /* @__PURE__ */ jsx("code", { children: imageHotspotOptions.imagePath })
268
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
267
269
  "Define a path in this field using to the image field in this document at",
268
270
  " ",
269
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "options.hotspotImagePath" })
271
+ /* @__PURE__ */ jsx("code", { children: "options.hotspotImagePath" })
270
272
  ] }) }),
271
- imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && /* @__PURE__ */ jsxRuntime.jsxs(Feedback, { children: [
273
+ imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && /* @__PURE__ */ jsxs(Feedback, { children: [
272
274
  'The supplied imageHotspotPathRoot "',
273
275
  imageHotspotOptions.pathRoot,
274
276
  '" is not valid, falling back to "document". Available values are "',
@@ -278,7 +280,7 @@ function ImageHotspotArray(props) {
278
280
  props.renderDefault(props)
279
281
  ] });
280
282
  }
281
- const imageHotspotArrayPlugin = sanity.definePlugin({
283
+ const imageHotspotArrayPlugin = definePlugin({
282
284
  name: "image-hotspot-array",
283
285
  form: {
284
286
  components: {
@@ -286,7 +288,7 @@ const imageHotspotArrayPlugin = sanity.definePlugin({
286
288
  if (props.schemaType.jsonType === "array" && props.schemaType.options?.imageHotspot) {
287
289
  const imageHotspotOptions = props.schemaType.options?.imageHotspot;
288
290
  if (imageHotspotOptions)
289
- return /* @__PURE__ */ jsxRuntime.jsx(
291
+ return /* @__PURE__ */ jsx(
290
292
  ImageHotspotArray,
291
293
  {
292
294
  ...props,
@@ -299,6 +301,8 @@ const imageHotspotArrayPlugin = sanity.definePlugin({
299
301
  }
300
302
  }
301
303
  });
302
- exports.ImageHotspotArray = ImageHotspotArray;
303
- exports.imageHotspotArrayPlugin = imageHotspotArrayPlugin;
304
+ export {
305
+ ImageHotspotArray,
306
+ imageHotspotArrayPlugin
307
+ };
304
308
  //# sourceMappingURL=index.js.map
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'\n\nexport default function Feedback({children}: {children: React.ReactNode}): React.JSX.Element {\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>>(undefined)\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined)\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined)\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 | 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 (\n imageHotspotOptions.imagePath ? get(rootObject, imageHotspotOptions.imagePath) : rootObject\n ) 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<HTMLImageElement>(\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' {\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","useFormValue","useResolveInitialValueForType","imageUrlBuilder","getImageDimensions","randomKey","PatchEvent","setIfMissing","insert","set","Stack","jsxs","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'\n\nexport default function Feedback({children}: {children: React.ReactNode}): React.JSX.Element {\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>>(undefined)\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined)\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined)\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 | 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 (\n imageHotspotOptions.imagePath ? get(rootObject, imageHotspotOptions.imagePath) : rootObject\n ) 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<HTMLImageElement>(\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' {\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":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-hotspot-array",
3
- "version": "3.0.2",
3
+ "version": "4.0.0",
4
4
  "description": "A configurable Custom Input for Arrays that will add and update items by clicking on an Image",
5
5
  "keywords": [
6
6
  "sanity",
@@ -17,11 +17,11 @@
17
17
  "license": "MIT",
18
18
  "author": "Sanity.io <hello@sanity.io>",
19
19
  "sideEffects": false,
20
- "type": "commonjs",
20
+ "type": "module",
21
21
  "exports": {
22
22
  ".": {
23
23
  "source": "./src/index.ts",
24
- "import": "./dist/index.mjs",
24
+ "import": "./dist/index.js",
25
25
  "default": "./dist/index.js"
26
26
  },
27
27
  "./package.json": "./package.json"
@@ -30,9 +30,7 @@
30
30
  "types": "./dist/index.d.ts",
31
31
  "files": [
32
32
  "src",
33
- "dist",
34
- "v2-incompatible.js",
35
- "sanity.json"
33
+ "dist"
36
34
  ],
37
35
  "scripts": {
38
36
  "dev": "sanity dev",
@@ -49,10 +47,9 @@
49
47
  "browserslist": "extends @sanity/browserslist-config",
50
48
  "dependencies": {
51
49
  "@sanity/asset-utils": "^2.2.1",
52
- "@sanity/image-url": "^1.1.0",
53
- "@sanity/incompatible-plugin": "^1.0.5",
50
+ "@sanity/image-url": "^2.1.1",
54
51
  "@sanity/ui": "^3.0.6",
55
- "@sanity/util": "^4.3.0",
52
+ "@sanity/util": "^6.0.0",
56
53
  "framer-motion": "^12.23.12",
57
54
  "lodash-es": "^4.17.21"
58
55
  },
@@ -60,8 +57,8 @@
60
57
  "@commitlint/cli": "^19.2.2",
61
58
  "@commitlint/config-conventional": "^19.2.2",
62
59
  "@sanity/browserslist-config": "^1.0.3",
63
- "@sanity/pkg-utils": "^7.11.7",
64
- "@sanity/plugin-kit": "^4.0.19",
60
+ "@sanity/pkg-utils": "^10.5.3",
61
+ "@sanity/plugin-kit": "^4.0.20",
65
62
  "@sanity/semantic-release-preset": "^4.1.7",
66
63
  "@types/lodash-es": "^4.17.12",
67
64
  "@types/react": "^19.2.3",
@@ -77,22 +74,23 @@
77
74
  "lint-staged": "^13.0.3",
78
75
  "prettier": "^3.6.2",
79
76
  "prettier-plugin-packagejson": "^2.5.19",
80
- "react": "^19.2.3",
77
+ "react": "^19.2.7",
81
78
  "rimraf": "^5.0.5",
82
- "sanity": "^5.0.1",
79
+ "sanity": "^6.0.0",
83
80
  "styled-components": "^6.1.19",
84
81
  "typescript": "5.9.2"
85
82
  },
86
83
  "peerDependencies": {
87
- "react": "^18.3 || ^19",
88
- "sanity": "^4 || ^5"
84
+ "react": "^19.2",
85
+ "sanity": "^6"
89
86
  },
90
87
  "engines": {
91
- "node": ">=20.19 <22 || >=22.12"
88
+ "node": ">=22.12"
92
89
  },
93
90
  "sanityPlugin": {
94
91
  "verifyPackage": {
95
92
  "nodeEngine": false,
93
+ "sanityV2Json": false,
96
94
  "tsc": false
97
95
  }
98
96
  }
package/dist/index.d.mts DELETED
@@ -1,54 +0,0 @@
1
- import {ArrayOfObjectsInputProps} from 'sanity'
2
- import {ComponentType} from 'react'
3
- import {ObjectSchemaType} from 'sanity'
4
- import {Plugin as Plugin_2} from 'sanity'
5
- import {ReactNode} from 'react'
6
- import {RenderPreviewCallback} from 'sanity'
7
-
8
- export declare type HotspotItem<
9
- HotspotFields = {
10
- [key: string]: unknown
11
- },
12
- > = {
13
- _key: string
14
- _type: string
15
- x: number
16
- y: number
17
- } & HotspotFields
18
-
19
- export declare interface HotspotTooltipProps<
20
- HotspotFields = {
21
- [key: string]: unknown
22
- },
23
- > {
24
- value: HotspotItem<HotspotFields>
25
- schemaType: ObjectSchemaType
26
- renderPreview: RenderPreviewCallback
27
- }
28
-
29
- export declare function ImageHotspotArray(
30
- props: ArrayOfObjectsInputProps<HotspotItem> & {
31
- imageHotspotOptions: ImageHotspotOptions
32
- },
33
- ): ReactNode
34
-
35
- export declare const imageHotspotArrayPlugin: Plugin_2<void>
36
-
37
- export declare interface ImageHotspotOptions<
38
- HotspotFields = {
39
- [key: string]: unknown
40
- },
41
- > {
42
- pathRoot?: 'document' | 'parent'
43
- imagePath: string
44
- descriptionPath?: string
45
- tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>
46
- }
47
-
48
- export {}
49
-
50
- declare module 'sanity' {
51
- interface ArrayOptions {
52
- imageHotspot?: ImageHotspotOptions
53
- }
54
- }
package/dist/index.mjs DELETED
@@ -1,308 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { getImageDimensions } from "@sanity/asset-utils";
3
- import imageUrlBuilder from "@sanity/image-url";
4
- import { Card, Text, Tooltip, Box, Stack, Flex } from "@sanity/ui";
5
- import { randomKey } from "@sanity/util/content";
6
- import { get } from "lodash-es";
7
- import { useState, useEffect, useCallback, createElement, useRef, useMemo } from "react";
8
- import { useClient, useFormValue, useResolveInitialValueForType, PatchEvent, setIfMissing, insert, set, definePlugin } from "sanity";
9
- import { useMotionValue, motion } from "framer-motion";
10
- function Feedback({ children }) {
11
- return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsx(Text, { children }) });
12
- }
13
- const dragStyle = {
14
- width: "1.4rem",
15
- height: "1.4rem",
16
- position: "absolute",
17
- boxSizing: "border-box",
18
- top: 0,
19
- left: 0,
20
- margin: "-0.7rem 0 0 -0.7rem",
21
- cursor: "pointer",
22
- display: "flex",
23
- justifyContent: "center",
24
- alignItems: "center",
25
- borderRadius: "50%",
26
- background: "#000",
27
- color: "white"
28
- }, dragStyleWhileDrag = {
29
- background: "rgba(0, 0, 0, 0.1)",
30
- border: "1px solid #fff",
31
- cursor: "none"
32
- }, dragStyleWhileHover = {
33
- background: "rgba(0, 0, 0, 0.1)",
34
- border: "1px solid #fff"
35
- }, dotStyle = {
36
- position: "absolute",
37
- left: "50%",
38
- top: "50%",
39
- height: "0.2rem",
40
- width: "0.2rem",
41
- margin: "-0.1rem 0 0 -0.1rem",
42
- background: "#fff",
43
- visibility: "hidden",
44
- borderRadius: "50%",
45
- // make sure pointer events only run on the parent
46
- pointerEvents: "none"
47
- }, dotStyleWhileActive = {
48
- visibility: "visible"
49
- }, labelStyle = {
50
- color: "white",
51
- fontSize: "0.7rem",
52
- fontWeight: 600,
53
- lineHeight: "1"
54
- }, labelStyleWhileActive = {
55
- visibility: "hidden"
56
- }, round = (num) => Math.round(num * 100) / 100;
57
- function Spot({
58
- value,
59
- bounds,
60
- update,
61
- hotspotDescriptionPath,
62
- tooltip,
63
- index,
64
- schemaType,
65
- renderPreview
66
- }) {
67
- const [isDragging, setIsDragging] = useState(!1), [isHovering, setIsHovering] = useState(!1), x = useMotionValue(round(bounds.width * (value.x / 100))), y = useMotionValue(round(bounds.height * (value.y / 100)));
68
- useEffect(() => {
69
- x.set(round(bounds.width * (value.x / 100))), y.set(round(bounds.height * (value.y / 100)));
70
- }, [x, y, value, bounds]);
71
- const handleDragEnd = useCallback(() => {
72
- setIsDragging(!1);
73
- const currentX = x.get(), currentY = y.get(), newX = round(currentX * 100 / bounds.width), newY = round(currentY * 100 / bounds.height), safeX = Math.max(0, Math.min(100, newX)), safeY = Math.max(0, Math.min(100, newY));
74
- update(value._key, safeX, safeY);
75
- }, [x, y, value, update, bounds]), handleDragStart = useCallback(() => setIsDragging(!0), []), handleHoverStart = useCallback(() => setIsHovering(!0), []), handleHoverEnd = useCallback(() => setIsHovering(!1), []);
76
- return !x || !y ? null : /* @__PURE__ */ jsx(
77
- motion.div,
78
- {
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,
96
- {
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
- /* @__PURE__ */ jsx(
102
- Box,
103
- {
104
- style: {
105
- ...dotStyle,
106
- ...(isDragging || isHovering) && { ...dotStyleWhileActive }
107
- }
108
- }
109
- ),
110
- /* @__PURE__ */ jsx(
111
- "div",
112
- {
113
- style: {
114
- ...labelStyle,
115
- ...(isDragging || isHovering) && { ...labelStyleWhileActive }
116
- },
117
- children: index + 1
118
- }
119
- )
120
- ] })
121
- },
122
- value._key
123
- )
124
- }
125
- );
126
- }
127
- function useUnmountEffect(effect) {
128
- const effectRef = useRef(effect);
129
- effectRef.current = effect, useEffect(() => () => effectRef.current(), []);
130
- }
131
- function useDebouncedCallback(callback, deps, delay, maxWait = 0) {
132
- const timeout = useRef(void 0), waitTimeout = useRef(void 0), cb = useRef(callback), lastCall = useRef(void 0), clear = () => {
133
- timeout.current && (clearTimeout(timeout.current), timeout.current = void 0), waitTimeout.current && (clearTimeout(waitTimeout.current), waitTimeout.current = void 0);
134
- };
135
- return useUnmountEffect(clear), useEffect(() => {
136
- cb.current = callback;
137
- }, deps), useMemo(() => {
138
- const execute = () => {
139
- if (clear(), !lastCall.current) return;
140
- const context = lastCall.current;
141
- lastCall.current = void 0, cb.current.apply(context.this, context.args);
142
- }, wrapped = function(...args) {
143
- timeout.current && clearTimeout(timeout.current), lastCall.current = { args, this: this }, timeout.current = setTimeout(execute, delay), maxWait > 0 && !waitTimeout.current && (waitTimeout.current = setTimeout(execute, maxWait));
144
- };
145
- return Object.defineProperties(wrapped, {
146
- length: { value: callback.length },
147
- name: { value: `${callback.name || "anonymous"}__debounced__${delay}` }
148
- }), wrapped;
149
- }, [delay, maxWait, ...deps]);
150
- }
151
- const isBrowser = typeof window < "u" && typeof navigator < "u" && typeof document < "u";
152
- let observerSingleton;
153
- function getResizeObserver() {
154
- if (!isBrowser) return;
155
- if (observerSingleton) return observerSingleton;
156
- const callbacks = /* @__PURE__ */ new Map(), observer = new ResizeObserver((entries) => {
157
- for (const entry of entries)
158
- callbacks.get(entry.target)?.forEach(
159
- (cb) => setTimeout(() => {
160
- cb(entry);
161
- }, 0)
162
- );
163
- });
164
- return observerSingleton = {
165
- observer,
166
- subscribe(target, callback) {
167
- let cbs = callbacks.get(target);
168
- cbs || (cbs = /* @__PURE__ */ new Set(), callbacks.set(target, cbs), observer.observe(target)), cbs.add(callback);
169
- },
170
- unsubscribe(target, callback) {
171
- const cbs = callbacks.get(target);
172
- cbs && (cbs.delete(callback), cbs.size === 0 && (callbacks.delete(target), observer.unobserve(target)));
173
- }
174
- }, observerSingleton;
175
- }
176
- function useResizeObserver(target, callback, enabled = !0) {
177
- const ro = enabled && getResizeObserver(), cb = useRef(callback);
178
- cb.current = callback;
179
- const tgt = target && "current" in target ? target.current : target;
180
- useEffect(() => {
181
- const _tgt = target && "current" in target ? target.current : target;
182
- if (!ro || !_tgt) return;
183
- let subscribed = !0;
184
- const handler = (...args) => {
185
- subscribed && cb.current(...args);
186
- };
187
- return ro.subscribe(_tgt, handler), () => {
188
- subscribed = !1, ro.unsubscribe(_tgt, handler);
189
- };
190
- }, [tgt, ro]);
191
- }
192
- const imageStyle = { width: "100%", height: "auto" }, VALID_ROOT_PATHS = ["document", "parent"];
193
- function ImageHotspotArray(props) {
194
- const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = useMemo(() => (VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? "") ? imageHotspotOptions.pathRoot : "document") === "document" ? [] : props.path.slice(0, -1), [imageHotspotOptions.pathRoot, props.path]), rootObject = useFormValue(imageHotspotPathRoot), resolveInitialValueForType = useResolveInitialValueForType(), resolveInitialValue = useCallback(
195
- async (type) => resolveInitialValueForType(type, {}).then((initialValue) => initialValue).catch(() => {
196
- }),
197
- [resolveInitialValueForType]
198
- ), hotspotImage = useMemo(() => imageHotspotOptions.imagePath ? get(rootObject, imageHotspotOptions.imagePath) : rootObject, [rootObject, imageHotspotOptions.imagePath]), displayImage = useMemo(() => {
199
- const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? ""), urlFor = (source) => builder.image(source);
200
- if (hotspotImage?.asset?._ref) {
201
- const { aspectRatio } = getImageDimensions(hotspotImage.asset._ref), width = 1200, height = Math.round(width / aspectRatio), url = urlFor(hotspotImage).width(width).url();
202
- return { width, height, url };
203
- }
204
- return null;
205
- }, [hotspotImage, sanityClient]), handleHotspotImageClick = useCallback(
206
- async (event) => {
207
- 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 = {
208
- _key: randomKey(12),
209
- _type: schemaType.of[0].name,
210
- ...initialValues,
211
- x,
212
- y
213
- };
214
- imageHotspotOptions?.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(PatchEvent.from([setIfMissing([]), insert([newRow], "after", [-1])]));
215
- },
216
- [imageHotspotOptions, onChange, resolveInitialValue, schemaType]
217
- ), handleHotspotMove = useCallback(
218
- (key, x, y) => {
219
- onChange(
220
- PatchEvent.from([
221
- // Set the `x` value of this array key item
222
- set(x, [{ _key: key }, "x"]),
223
- // Set the `y` value of this array key item
224
- set(y, [{ _key: key }, "y"])
225
- ])
226
- );
227
- },
228
- [onChange]
229
- ), hotspotImageRef = useRef(null), [imageRect, setImageRect] = useState();
230
- return useResizeObserver(
231
- hotspotImageRef,
232
- useDebouncedCallback(
233
- (e) => setImageRect(e.contentRect),
234
- [setImageRect],
235
- 200
236
- )
237
- ), /* @__PURE__ */ jsxs(Stack, { space: [2, 2, 3], children: [
238
- displayImage?.url ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
239
- imageRect && (value?.length ?? 0) > 0 && value?.map((spot, index) => /* @__PURE__ */ jsx(
240
- Spot,
241
- {
242
- index,
243
- value: spot,
244
- bounds: imageRect,
245
- update: handleHotspotMove,
246
- hotspotDescriptionPath: imageHotspotOptions?.descriptionPath,
247
- tooltip: imageHotspotOptions?.tooltip,
248
- renderPreview,
249
- schemaType: schemaType.of[0]
250
- },
251
- spot._key
252
- )),
253
- /* @__PURE__ */ jsx(Card, { __unstable_checkered: !0, shadow: 1, children: /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", children: /* @__PURE__ */ jsx(
254
- "img",
255
- {
256
- ref: hotspotImageRef,
257
- src: displayImage.url,
258
- width: displayImage.width,
259
- height: displayImage.height,
260
- alt: "",
261
- style: imageStyle,
262
- onClick: handleHotspotImageClick
263
- }
264
- ) }) })
265
- ] }) : /* @__PURE__ */ jsx(Feedback, { children: imageHotspotOptions.imagePath ? /* @__PURE__ */ jsxs(Fragment, { children: [
266
- "No Hotspot image found at path ",
267
- /* @__PURE__ */ jsx("code", { children: imageHotspotOptions.imagePath })
268
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
269
- "Define a path in this field using to the image field in this document at",
270
- " ",
271
- /* @__PURE__ */ jsx("code", { children: "options.hotspotImagePath" })
272
- ] }) }),
273
- imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && /* @__PURE__ */ jsxs(Feedback, { children: [
274
- 'The supplied imageHotspotPathRoot "',
275
- imageHotspotOptions.pathRoot,
276
- '" is not valid, falling back to "document". Available values are "',
277
- VALID_ROOT_PATHS.join(", "),
278
- '".'
279
- ] }),
280
- props.renderDefault(props)
281
- ] });
282
- }
283
- const imageHotspotArrayPlugin = definePlugin({
284
- name: "image-hotspot-array",
285
- form: {
286
- components: {
287
- input: (props) => {
288
- if (props.schemaType.jsonType === "array" && props.schemaType.options?.imageHotspot) {
289
- const imageHotspotOptions = props.schemaType.options?.imageHotspot;
290
- if (imageHotspotOptions)
291
- return /* @__PURE__ */ jsx(
292
- ImageHotspotArray,
293
- {
294
- ...props,
295
- imageHotspotOptions
296
- }
297
- );
298
- }
299
- return props.renderDefault(props);
300
- }
301
- }
302
- }
303
- });
304
- export {
305
- ImageHotspotArray,
306
- imageHotspotArrayPlugin
307
- };
308
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
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'\n\nexport default function Feedback({children}: {children: React.ReactNode}): React.JSX.Element {\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>>(undefined)\n const waitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined)\n const cb = useRef(callback)\n const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined)\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 | 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 (\n imageHotspotOptions.imagePath ? get(rootObject, imageHotspotOptions.imagePath) : rootObject\n ) 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<HTMLImageElement>(\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' {\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":[],"mappings":"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"}
package/sanity.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "parts": [
3
- {
4
- "implements": "part:@sanity/base/sanity-root",
5
- "path": "./v2-incompatible.js"
6
- }
7
- ]
8
- }
@@ -1,11 +0,0 @@
1
- const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
- const {name, version, sanityExchangeUrl} = require('./package.json')
3
-
4
- export default showIncompatiblePluginDialog({
5
- name: name,
6
- versions: {
7
- v3: version,
8
- v2: '^0.0.10',
9
- },
10
- sanityExchangeUrl,
11
- })