sanity-plugin-hotspot-array 2.2.0 → 3.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/README.md +21 -30
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +17 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -20
- package/src/Feedback.tsx +1 -2
- package/src/ImageHotspotArray.tsx +1 -1
- package/src/plugin.tsx +1 -1
- package/src/useDebouncedCallback.ts +3 -3
- package/src/useResizeObserver.ts +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# sanity-plugin-hotspot-array
|
|
2
2
|
|
|
3
|
-
>This is a **Sanity Studio v3** plugin.
|
|
4
|
-
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-hotspot-array/tree/studio-v2).
|
|
5
|
-
|
|
6
3
|
## What is it?
|
|
7
4
|
|
|
8
5
|
A configurable Custom Input for Arrays that will add and update items by clicking on an Image
|
|
@@ -21,19 +18,16 @@ or
|
|
|
21
18
|
yarn add sanity-plugin-hotspot-array
|
|
22
19
|
```
|
|
23
20
|
|
|
24
|
-
|
|
25
21
|
## Usage
|
|
26
22
|
|
|
27
23
|
Add it as a plugin in sanity.config.ts (or .js):
|
|
28
24
|
|
|
29
25
|
```js
|
|
30
|
-
import {
|
|
26
|
+
import {imageHotspotArrayPlugin} from 'sanity-plugin-hotspot-array'
|
|
31
27
|
|
|
32
28
|
export default defineConfig({
|
|
33
29
|
// ...
|
|
34
|
-
plugins: [
|
|
35
|
-
imageHotspotArrayPlugin(),
|
|
36
|
-
]
|
|
30
|
+
plugins: [imageHotspotArrayPlugin()],
|
|
37
31
|
})
|
|
38
32
|
```
|
|
39
33
|
|
|
@@ -61,7 +55,7 @@ export const productSchema = defineType({
|
|
|
61
55
|
descriptionPath: `details`,
|
|
62
56
|
// see `Custom tooltip` setup below
|
|
63
57
|
tooltip: undefined,
|
|
64
|
-
}
|
|
58
|
+
},
|
|
65
59
|
},
|
|
66
60
|
}),
|
|
67
61
|
// ...all your other fields
|
|
@@ -69,6 +63,7 @@ export const productSchema = defineType({
|
|
|
69
63
|
],
|
|
70
64
|
})
|
|
71
65
|
```
|
|
66
|
+
|
|
72
67
|
There is no need to provide an explicit input component, as that is handled by the plugin.
|
|
73
68
|
|
|
74
69
|
The plugin makes a number of assumptions to add and update data in the array. Including:
|
|
@@ -93,6 +88,7 @@ options: {
|
|
|
93
88
|
```
|
|
94
89
|
|
|
95
90
|
To pick the image out of the hotspot-array parent object, use
|
|
91
|
+
|
|
96
92
|
```js
|
|
97
93
|
options: {
|
|
98
94
|
imageHotspot: {
|
|
@@ -166,23 +162,19 @@ You can customise the Tooltip to display any Component, which will receive `valu
|
|
|
166
162
|
### Example 1 - use default hotspot preview
|
|
167
163
|
|
|
168
164
|
```tsx
|
|
169
|
-
import {
|
|
170
|
-
import {
|
|
171
|
-
|
|
172
|
-
export function HotspotPreview({
|
|
173
|
-
value,
|
|
174
|
-
schemaType,
|
|
175
|
-
renderPreview,
|
|
176
|
-
}: HotspotTooltipProps) {
|
|
165
|
+
import {Box} from '@sanity/ui'
|
|
166
|
+
import {HotspotTooltipProps} from 'sanity-plugin-hotspot-array'
|
|
167
|
+
|
|
168
|
+
export function HotspotPreview({value, schemaType, renderPreview}: HotspotTooltipProps) {
|
|
177
169
|
return (
|
|
178
|
-
<Box padding={2} style={{
|
|
170
|
+
<Box padding={2} style={{minWidth: 200}}>
|
|
179
171
|
{renderPreview({
|
|
180
172
|
value,
|
|
181
173
|
schemaType,
|
|
182
|
-
layout:
|
|
174
|
+
layout: 'default',
|
|
183
175
|
})}
|
|
184
176
|
</Box>
|
|
185
|
-
)
|
|
177
|
+
)
|
|
186
178
|
}
|
|
187
179
|
```
|
|
188
180
|
|
|
@@ -197,25 +189,24 @@ options: {
|
|
|
197
189
|
```
|
|
198
190
|
|
|
199
191
|
### Example 2 - reference value in hotspot
|
|
192
|
+
|
|
200
193
|
In this example our `value` object has a `reference` field to the `product` schema type, and will show a document preview.
|
|
201
194
|
|
|
202
195
|
```jsx
|
|
203
|
-
import {useSchema
|
|
196
|
+
import {useSchema} from 'sanity'
|
|
204
197
|
import {Box} from '@sanity/ui'
|
|
205
198
|
|
|
206
199
|
export function ProductPreview({value, renderPreview}) {
|
|
207
200
|
const productSchemaType = useSchema().get('product')
|
|
208
201
|
return (
|
|
209
202
|
<Box padding={2} style={{minWidth: 200}}>
|
|
210
|
-
{value?.product?._ref
|
|
211
|
-
renderPreview({
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
`No reference selected`
|
|
218
|
-
)}
|
|
203
|
+
{value?.product?._ref
|
|
204
|
+
? renderPreview({
|
|
205
|
+
value,
|
|
206
|
+
schemaType: productSchemaType,
|
|
207
|
+
layout: 'default',
|
|
208
|
+
})
|
|
209
|
+
: `No reference selected`}
|
|
219
210
|
</Box>
|
|
220
211
|
)
|
|
221
212
|
}
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -127,15 +127,14 @@ function useUnmountEffect(effect) {
|
|
|
127
127
|
effectRef.current = effect, react.useEffect(() => () => effectRef.current(), []);
|
|
128
128
|
}
|
|
129
129
|
function useDebouncedCallback(callback, deps, delay, maxWait = 0) {
|
|
130
|
-
const timeout = react.useRef(), waitTimeout = react.useRef(), cb = react.useRef(callback), lastCall = react.useRef(), clear = () => {
|
|
130
|
+
const timeout = react.useRef(void 0), waitTimeout = react.useRef(void 0), cb = react.useRef(callback), lastCall = react.useRef(void 0), clear = () => {
|
|
131
131
|
timeout.current && (clearTimeout(timeout.current), timeout.current = void 0), waitTimeout.current && (clearTimeout(waitTimeout.current), waitTimeout.current = void 0);
|
|
132
132
|
};
|
|
133
133
|
return useUnmountEffect(clear), react.useEffect(() => {
|
|
134
134
|
cb.current = callback;
|
|
135
135
|
}, deps), react.useMemo(() => {
|
|
136
136
|
const execute = () => {
|
|
137
|
-
if (clear(), !lastCall.current)
|
|
138
|
-
return;
|
|
137
|
+
if (clear(), !lastCall.current) return;
|
|
139
138
|
const context = lastCall.current;
|
|
140
139
|
lastCall.current = void 0, cb.current.apply(context.this, context.args);
|
|
141
140
|
}, wrapped = function(...args) {
|
|
@@ -150,14 +149,11 @@ function useDebouncedCallback(callback, deps, delay, maxWait = 0) {
|
|
|
150
149
|
const isBrowser = typeof window < "u" && typeof navigator < "u" && typeof document < "u";
|
|
151
150
|
let observerSingleton;
|
|
152
151
|
function getResizeObserver() {
|
|
153
|
-
if (!isBrowser)
|
|
154
|
-
|
|
155
|
-
if (observerSingleton)
|
|
156
|
-
return observerSingleton;
|
|
152
|
+
if (!isBrowser) return;
|
|
153
|
+
if (observerSingleton) return observerSingleton;
|
|
157
154
|
const callbacks = /* @__PURE__ */ new Map(), observer = new ResizeObserver((entries) => {
|
|
158
|
-
var _a;
|
|
159
155
|
for (const entry of entries)
|
|
160
|
-
|
|
156
|
+
callbacks.get(entry.target)?.forEach(
|
|
161
157
|
(cb) => setTimeout(() => {
|
|
162
158
|
cb(entry);
|
|
163
159
|
}, 0)
|
|
@@ -181,8 +177,7 @@ function useResizeObserver(target, callback, enabled = !0) {
|
|
|
181
177
|
const tgt = target && "current" in target ? target.current : target;
|
|
182
178
|
react.useEffect(() => {
|
|
183
179
|
const _tgt = target && "current" in target ? target.current : target;
|
|
184
|
-
if (!ro || !_tgt)
|
|
185
|
-
return;
|
|
180
|
+
if (!ro || !_tgt) return;
|
|
186
181
|
let subscribed = !0;
|
|
187
182
|
const handler = (...args) => {
|
|
188
183
|
subscribed && cb.current(...args);
|
|
@@ -194,18 +189,13 @@ function useResizeObserver(target, callback, enabled = !0) {
|
|
|
194
189
|
}
|
|
195
190
|
const imageStyle = { width: "100%", height: "auto" }, VALID_ROOT_PATHS = ["document", "parent"];
|
|
196
191
|
function ImageHotspotArray(props) {
|
|
197
|
-
|
|
198
|
-
const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = sanity.useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = react.useMemo(() => {
|
|
199
|
-
var _a2;
|
|
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), resolveInitialValueForType = sanity.useResolveInitialValueForType(), resolveInitialValue = react.useCallback(
|
|
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(
|
|
202
193
|
async (type) => resolveInitialValueForType(type, {}).then((initialValue) => initialValue).catch(() => {
|
|
203
194
|
}),
|
|
204
195
|
[resolveInitialValueForType]
|
|
205
196
|
), hotspotImage = react.useMemo(() => imageHotspotOptions.imagePath ? lodashEs.get(rootObject, imageHotspotOptions.imagePath) : rootObject, [rootObject, imageHotspotOptions.imagePath]), displayImage = react.useMemo(() => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if ((_b = hotspotImage == null ? void 0 : hotspotImage.asset) != null && _b._ref) {
|
|
197
|
+
const builder = imageUrlBuilder__default.default(sanityClient).dataset(sanityClient.config().dataset ?? ""), urlFor = (source) => builder.image(source);
|
|
198
|
+
if (hotspotImage?.asset?._ref) {
|
|
209
199
|
const { aspectRatio } = assetUtils.getImageDimensions(hotspotImage.asset._ref), width = 1200, height = Math.round(width / aspectRatio), url = urlFor(hotspotImage).width(width).url();
|
|
210
200
|
return { width, height, url };
|
|
211
201
|
}
|
|
@@ -219,7 +209,7 @@ function ImageHotspotArray(props) {
|
|
|
219
209
|
x,
|
|
220
210
|
y
|
|
221
211
|
};
|
|
222
|
-
imageHotspotOptions
|
|
212
|
+
imageHotspotOptions?.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(sanity.PatchEvent.from([sanity.setIfMissing([]), sanity.insert([newRow], "after", [-1])]));
|
|
223
213
|
},
|
|
224
214
|
[imageHotspotOptions, onChange, resolveInitialValue, schemaType]
|
|
225
215
|
), handleHotspotMove = react.useCallback(
|
|
@@ -243,21 +233,21 @@ function ImageHotspotArray(props) {
|
|
|
243
233
|
200
|
|
244
234
|
)
|
|
245
235
|
), /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: [2, 2, 3], children: [
|
|
246
|
-
displayImage
|
|
247
|
-
imageRect && (
|
|
236
|
+
displayImage?.url ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
237
|
+
imageRect && (value?.length ?? 0) > 0 && value?.map((spot, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
248
238
|
Spot,
|
|
249
239
|
{
|
|
250
240
|
index,
|
|
251
241
|
value: spot,
|
|
252
242
|
bounds: imageRect,
|
|
253
243
|
update: handleHotspotMove,
|
|
254
|
-
hotspotDescriptionPath: imageHotspotOptions
|
|
255
|
-
tooltip: imageHotspotOptions
|
|
244
|
+
hotspotDescriptionPath: imageHotspotOptions?.descriptionPath,
|
|
245
|
+
tooltip: imageHotspotOptions?.tooltip,
|
|
256
246
|
renderPreview,
|
|
257
247
|
schemaType: schemaType.of[0]
|
|
258
248
|
},
|
|
259
249
|
spot._key
|
|
260
|
-
))
|
|
250
|
+
)),
|
|
261
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(
|
|
262
252
|
"img",
|
|
263
253
|
{
|
|
@@ -293,9 +283,8 @@ const imageHotspotArrayPlugin = sanity.definePlugin({
|
|
|
293
283
|
form: {
|
|
294
284
|
components: {
|
|
295
285
|
input: (props) => {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const imageHotspotOptions = (_b = props.schemaType.options) == null ? void 0 : _b.imageHotspot;
|
|
286
|
+
if (props.schemaType.jsonType === "array" && props.schemaType.options?.imageHotspot) {
|
|
287
|
+
const imageHotspotOptions = props.schemaType.options?.imageHotspot;
|
|
299
288
|
if (imageHotspotOptions)
|
|
300
289
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
301
290
|
ImageHotspotArray,
|
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 <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 (\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(\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 "{imageHotspotOptions.pathRoot}" is not valid,\n falling back to "document". Available values are "\n {VALID_ROOT_PATHS.join(', ')}".\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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"}
|
|
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 "{imageHotspotOptions.pathRoot}" is not valid,\n falling back to "document". Available values are "\n {VALID_ROOT_PATHS.join(', ')}".\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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"}
|
package/dist/index.mjs
CHANGED
|
@@ -129,15 +129,14 @@ function useUnmountEffect(effect) {
|
|
|
129
129
|
effectRef.current = effect, useEffect(() => () => effectRef.current(), []);
|
|
130
130
|
}
|
|
131
131
|
function useDebouncedCallback(callback, deps, delay, maxWait = 0) {
|
|
132
|
-
const timeout = useRef(), waitTimeout = useRef(), cb = useRef(callback), lastCall = useRef(), clear = () => {
|
|
132
|
+
const timeout = useRef(void 0), waitTimeout = useRef(void 0), cb = useRef(callback), lastCall = useRef(void 0), clear = () => {
|
|
133
133
|
timeout.current && (clearTimeout(timeout.current), timeout.current = void 0), waitTimeout.current && (clearTimeout(waitTimeout.current), waitTimeout.current = void 0);
|
|
134
134
|
};
|
|
135
135
|
return useUnmountEffect(clear), useEffect(() => {
|
|
136
136
|
cb.current = callback;
|
|
137
137
|
}, deps), useMemo(() => {
|
|
138
138
|
const execute = () => {
|
|
139
|
-
if (clear(), !lastCall.current)
|
|
140
|
-
return;
|
|
139
|
+
if (clear(), !lastCall.current) return;
|
|
141
140
|
const context = lastCall.current;
|
|
142
141
|
lastCall.current = void 0, cb.current.apply(context.this, context.args);
|
|
143
142
|
}, wrapped = function(...args) {
|
|
@@ -152,14 +151,11 @@ function useDebouncedCallback(callback, deps, delay, maxWait = 0) {
|
|
|
152
151
|
const isBrowser = typeof window < "u" && typeof navigator < "u" && typeof document < "u";
|
|
153
152
|
let observerSingleton;
|
|
154
153
|
function getResizeObserver() {
|
|
155
|
-
if (!isBrowser)
|
|
156
|
-
|
|
157
|
-
if (observerSingleton)
|
|
158
|
-
return observerSingleton;
|
|
154
|
+
if (!isBrowser) return;
|
|
155
|
+
if (observerSingleton) return observerSingleton;
|
|
159
156
|
const callbacks = /* @__PURE__ */ new Map(), observer = new ResizeObserver((entries) => {
|
|
160
|
-
var _a;
|
|
161
157
|
for (const entry of entries)
|
|
162
|
-
|
|
158
|
+
callbacks.get(entry.target)?.forEach(
|
|
163
159
|
(cb) => setTimeout(() => {
|
|
164
160
|
cb(entry);
|
|
165
161
|
}, 0)
|
|
@@ -183,8 +179,7 @@ function useResizeObserver(target, callback, enabled = !0) {
|
|
|
183
179
|
const tgt = target && "current" in target ? target.current : target;
|
|
184
180
|
useEffect(() => {
|
|
185
181
|
const _tgt = target && "current" in target ? target.current : target;
|
|
186
|
-
if (!ro || !_tgt)
|
|
187
|
-
return;
|
|
182
|
+
if (!ro || !_tgt) return;
|
|
188
183
|
let subscribed = !0;
|
|
189
184
|
const handler = (...args) => {
|
|
190
185
|
subscribed && cb.current(...args);
|
|
@@ -196,18 +191,13 @@ function useResizeObserver(target, callback, enabled = !0) {
|
|
|
196
191
|
}
|
|
197
192
|
const imageStyle = { width: "100%", height: "auto" }, VALID_ROOT_PATHS = ["document", "parent"];
|
|
198
193
|
function ImageHotspotArray(props) {
|
|
199
|
-
|
|
200
|
-
const { value, onChange, imageHotspotOptions, schemaType, renderPreview } = props, sanityClient = useClient({ apiVersion: "2022-01-01" }), imageHotspotPathRoot = useMemo(() => {
|
|
201
|
-
var _a2;
|
|
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), resolveInitialValueForType = useResolveInitialValueForType(), resolveInitialValue = 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(
|
|
204
195
|
async (type) => resolveInitialValueForType(type, {}).then((initialValue) => initialValue).catch(() => {
|
|
205
196
|
}),
|
|
206
197
|
[resolveInitialValueForType]
|
|
207
198
|
), hotspotImage = useMemo(() => imageHotspotOptions.imagePath ? get(rootObject, imageHotspotOptions.imagePath) : rootObject, [rootObject, imageHotspotOptions.imagePath]), displayImage = useMemo(() => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if ((_b = hotspotImage == null ? void 0 : hotspotImage.asset) != null && _b._ref) {
|
|
199
|
+
const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? ""), urlFor = (source) => builder.image(source);
|
|
200
|
+
if (hotspotImage?.asset?._ref) {
|
|
211
201
|
const { aspectRatio } = getImageDimensions(hotspotImage.asset._ref), width = 1200, height = Math.round(width / aspectRatio), url = urlFor(hotspotImage).width(width).url();
|
|
212
202
|
return { width, height, url };
|
|
213
203
|
}
|
|
@@ -221,7 +211,7 @@ function ImageHotspotArray(props) {
|
|
|
221
211
|
x,
|
|
222
212
|
y
|
|
223
213
|
};
|
|
224
|
-
imageHotspotOptions
|
|
214
|
+
imageHotspotOptions?.descriptionPath && (newRow[imageHotspotOptions.descriptionPath] = description), onChange(PatchEvent.from([setIfMissing([]), insert([newRow], "after", [-1])]));
|
|
225
215
|
},
|
|
226
216
|
[imageHotspotOptions, onChange, resolveInitialValue, schemaType]
|
|
227
217
|
), handleHotspotMove = useCallback(
|
|
@@ -245,21 +235,21 @@ function ImageHotspotArray(props) {
|
|
|
245
235
|
200
|
|
246
236
|
)
|
|
247
237
|
), /* @__PURE__ */ jsxs(Stack, { space: [2, 2, 3], children: [
|
|
248
|
-
displayImage
|
|
249
|
-
imageRect && (
|
|
238
|
+
displayImage?.url ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
239
|
+
imageRect && (value?.length ?? 0) > 0 && value?.map((spot, index) => /* @__PURE__ */ jsx(
|
|
250
240
|
Spot,
|
|
251
241
|
{
|
|
252
242
|
index,
|
|
253
243
|
value: spot,
|
|
254
244
|
bounds: imageRect,
|
|
255
245
|
update: handleHotspotMove,
|
|
256
|
-
hotspotDescriptionPath: imageHotspotOptions
|
|
257
|
-
tooltip: imageHotspotOptions
|
|
246
|
+
hotspotDescriptionPath: imageHotspotOptions?.descriptionPath,
|
|
247
|
+
tooltip: imageHotspotOptions?.tooltip,
|
|
258
248
|
renderPreview,
|
|
259
249
|
schemaType: schemaType.of[0]
|
|
260
250
|
},
|
|
261
251
|
spot._key
|
|
262
|
-
))
|
|
252
|
+
)),
|
|
263
253
|
/* @__PURE__ */ jsx(Card, { __unstable_checkered: !0, shadow: 1, children: /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", children: /* @__PURE__ */ jsx(
|
|
264
254
|
"img",
|
|
265
255
|
{
|
|
@@ -295,9 +285,8 @@ const imageHotspotArrayPlugin = definePlugin({
|
|
|
295
285
|
form: {
|
|
296
286
|
components: {
|
|
297
287
|
input: (props) => {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const imageHotspotOptions = (_b = props.schemaType.options) == null ? void 0 : _b.imageHotspot;
|
|
288
|
+
if (props.schemaType.jsonType === "array" && props.schemaType.options?.imageHotspot) {
|
|
289
|
+
const imageHotspotOptions = props.schemaType.options?.imageHotspot;
|
|
301
290
|
if (imageHotspotOptions)
|
|
302
291
|
return /* @__PURE__ */ jsx(
|
|
303
292
|
ImageHotspotArray,
|
package/dist/index.mjs.map
CHANGED
|
@@ -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 <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 (\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(\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 "{imageHotspotOptions.pathRoot}" is not valid,\n falling back to "document". Available values are "\n {VALID_ROOT_PATHS.join(', ')}".\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'\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 "{imageHotspotOptions.pathRoot}" is not valid,\n falling back to "document". Available values are "\n {VALID_ROOT_PATHS.join(', ')}".\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,9 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-hotspot-array",
|
|
3
|
-
"version": "
|
|
4
|
-
"workspaces": [
|
|
5
|
-
"./examples/*"
|
|
6
|
-
],
|
|
3
|
+
"version": "3.0.0",
|
|
7
4
|
"description": "A configurable Custom Input for Arrays that will add and update items by clicking on an Image",
|
|
8
5
|
"keywords": [
|
|
9
6
|
"sanity",
|
|
@@ -38,6 +35,7 @@
|
|
|
38
35
|
"sanity.json"
|
|
39
36
|
],
|
|
40
37
|
"scripts": {
|
|
38
|
+
"dev": "sanity dev",
|
|
41
39
|
"prebuild": "npm run clean && plugin-kit verify-package --silent && pkg-utils",
|
|
42
40
|
"build": "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
|
|
43
41
|
"clean": "rimraf dist",
|
|
@@ -50,21 +48,23 @@
|
|
|
50
48
|
},
|
|
51
49
|
"browserslist": "extends @sanity/browserslist-config",
|
|
52
50
|
"dependencies": {
|
|
53
|
-
"@sanity/asset-utils": "^2.
|
|
54
|
-
"@sanity/image-url": "^1.0
|
|
55
|
-
"@sanity/incompatible-plugin": "^1.0.
|
|
56
|
-
"@sanity/
|
|
57
|
-
"
|
|
51
|
+
"@sanity/asset-utils": "^2.2.1",
|
|
52
|
+
"@sanity/image-url": "^1.1.0",
|
|
53
|
+
"@sanity/incompatible-plugin": "^1.0.5",
|
|
54
|
+
"@sanity/ui": "^3.0.6",
|
|
55
|
+
"@sanity/util": "^4.3.0",
|
|
56
|
+
"framer-motion": "^12.23.12",
|
|
58
57
|
"lodash-es": "^4.17.21"
|
|
59
58
|
},
|
|
60
59
|
"devDependencies": {
|
|
61
60
|
"@commitlint/cli": "^19.2.2",
|
|
62
61
|
"@commitlint/config-conventional": "^19.2.2",
|
|
63
62
|
"@sanity/browserslist-config": "^1.0.3",
|
|
64
|
-
"@sanity/pkg-utils": "^
|
|
65
|
-
"@sanity/plugin-kit": "^4.0.
|
|
63
|
+
"@sanity/pkg-utils": "^7.11.7",
|
|
64
|
+
"@sanity/plugin-kit": "^4.0.19",
|
|
66
65
|
"@sanity/semantic-release-preset": "^4.1.7",
|
|
67
66
|
"@types/lodash-es": "^4.17.12",
|
|
67
|
+
"@types/react": "^19.1.10",
|
|
68
68
|
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
69
69
|
"@typescript-eslint/parser": "^7.7.0",
|
|
70
70
|
"eslint": "^8.57.0",
|
|
@@ -75,20 +75,25 @@
|
|
|
75
75
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
76
76
|
"husky": "^8.0.1",
|
|
77
77
|
"lint-staged": "^13.0.3",
|
|
78
|
-
"prettier": "^3.2
|
|
79
|
-
"prettier-plugin-packagejson": "^2.5.
|
|
80
|
-
"react": "^
|
|
78
|
+
"prettier": "^3.6.2",
|
|
79
|
+
"prettier-plugin-packagejson": "^2.5.19",
|
|
80
|
+
"react": "^19.1.1",
|
|
81
81
|
"rimraf": "^5.0.5",
|
|
82
|
-
"sanity": "^3.0
|
|
83
|
-
"
|
|
82
|
+
"sanity": "^4.3.0",
|
|
83
|
+
"styled-components": "^6.1.19",
|
|
84
|
+
"typescript": "5.9.2"
|
|
84
85
|
},
|
|
85
86
|
"peerDependencies": {
|
|
86
|
-
"@sanity/ui": "^2",
|
|
87
87
|
"react": "^18.3 || ^19",
|
|
88
|
-
"sanity": "^
|
|
89
|
-
"styled-components": "^6.1"
|
|
88
|
+
"sanity": "^4"
|
|
90
89
|
},
|
|
91
90
|
"engines": {
|
|
92
|
-
"node": ">=
|
|
91
|
+
"node": ">=20.19 >=22.12.0"
|
|
92
|
+
},
|
|
93
|
+
"sanityPlugin": {
|
|
94
|
+
"verifyPackage": {
|
|
95
|
+
"nodeEngine": false,
|
|
96
|
+
"tsc": false
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
99
|
}
|
package/src/Feedback.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {Card, Text} from '@sanity/ui'
|
|
2
|
-
import {type ReactNode} from 'react'
|
|
3
2
|
|
|
4
|
-
export default function Feedback({children}: {children: ReactNode}):
|
|
3
|
+
export default function Feedback({children}: {children: React.ReactNode}): React.JSX.Element {
|
|
5
4
|
return (
|
|
6
5
|
<Card padding={4} radius={2} shadow={1} tone="caution">
|
|
7
6
|
<Text>{children}</Text>
|
|
@@ -142,7 +142,7 @@ export function ImageHotspotArray(
|
|
|
142
142
|
|
|
143
143
|
const [imageRect, setImageRect] = useState<DOMRectReadOnly>()
|
|
144
144
|
|
|
145
|
-
useResizeObserver(
|
|
145
|
+
useResizeObserver<HTMLImageElement>(
|
|
146
146
|
hotspotImageRef,
|
|
147
147
|
useDebouncedCallback(
|
|
148
148
|
(e: ResizeObserverEntry) => setImageRect(e.contentRect),
|
package/src/plugin.tsx
CHANGED
|
@@ -11,7 +11,7 @@ export interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {
|
|
|
11
11
|
tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
declare module '
|
|
14
|
+
declare module 'sanity' {
|
|
15
15
|
export interface ArrayOptions {
|
|
16
16
|
imageHotspot?: ImageHotspotOptions
|
|
17
17
|
}
|
|
@@ -36,10 +36,10 @@ export function useDebouncedCallback<Fn extends (...args: any[]) => any>(
|
|
|
36
36
|
delay: number,
|
|
37
37
|
maxWait = 0,
|
|
38
38
|
): DebouncedFunction<Fn> {
|
|
39
|
-
const timeout = useRef<ReturnType<typeof setTimeout>>()
|
|
40
|
-
const waitTimeout = useRef<ReturnType<typeof setTimeout>>()
|
|
39
|
+
const timeout = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
40
|
+
const waitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
41
41
|
const cb = useRef(callback)
|
|
42
|
-
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>()
|
|
42
|
+
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined)
|
|
43
43
|
|
|
44
44
|
const clear = () => {
|
|
45
45
|
if (timeout.current) {
|
package/src/useResizeObserver.ts
CHANGED
|
@@ -79,7 +79,7 @@ function getResizeObserver(): ResizeObserverSingleton | undefined {
|
|
|
79
79
|
* @param enabled Whether resize observer is enabled or not.
|
|
80
80
|
*/
|
|
81
81
|
export function useResizeObserver<T extends Element>(
|
|
82
|
-
target: RefObject<T
|
|
82
|
+
target: RefObject<T | null>,
|
|
83
83
|
callback: UseResizeObserverCallback,
|
|
84
84
|
enabled = true,
|
|
85
85
|
): void {
|