sanity-plugin-hotspot-array 0.0.7 → 0.1.0-v3-studio.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +136 -38
- package/lib/cjs/index.js +403 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/esm/index.js +395 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/types/index.d.ts +36 -0
- package/lib/types/index.d.ts.map +1 -0
- package/package.json +61 -47
- package/sanity.json +6 -5
- package/src/Feedback.tsx +2 -2
- package/src/ImageHotspotArray.tsx +188 -0
- package/src/Spot.tsx +32 -19
- package/src/index.tsx +42 -0
- package/v2-incompatible.js +11 -0
- package/.babelrc +0 -3
- package/lib/Feedback.js +0 -23
- package/lib/Feedback.js.map +0 -1
- package/lib/HotspotArray.js +0 -189
- package/lib/HotspotArray.js.map +0 -1
- package/lib/Spot.js +0 -180
- package/lib/Spot.js.map +0 -1
- package/lib/useUnsetInputComponent.js +0 -32
- package/lib/useUnsetInputComponent.js.map +0 -1
- package/src/HotspotArray.tsx +0 -177
- package/src/useUnsetInputComponent.ts +0 -17
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# sanity-plugin-hotspot-array
|
|
2
2
|
|
|
3
|
+
> > **NOTE**
|
|
4
|
+
>
|
|
5
|
+
> This is the **Sanity Studio v3 version** of sanity-plugin-hotspot-array.
|
|
6
|
+
>
|
|
7
|
+
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-hotspot-array).
|
|
8
|
+
|
|
9
|
+
## What is it?
|
|
10
|
+
|
|
3
11
|
A configurable Custom Input for Arrays that will add and update items by clicking on an Image
|
|
4
12
|
|
|
5
13
|
<img src="https://user-images.githubusercontent.com/209129/174171697-57319ebc-03a7-4d82-a73e-b6effcb0b3ba.gif" width="600" />
|
|
@@ -7,49 +15,73 @@ A configurable Custom Input for Arrays that will add and update items by clickin
|
|
|
7
15
|
## Installation
|
|
8
16
|
|
|
9
17
|
```
|
|
10
|
-
|
|
18
|
+
npm install --save sanity-plugin-hotspot-array@studio-v3
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
or
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
yarn add sanity-plugin-hotspot-array@studio-v3
|
|
11
25
|
```
|
|
12
26
|
|
|
13
|
-
## Setup
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Add it as a plugin in sanity.config.ts (or .js):
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { imageHotspotArray } from "sanity-plugin-hotspot-array";
|
|
34
|
+
|
|
35
|
+
export default createConfig({
|
|
36
|
+
// ...
|
|
37
|
+
plugins: [
|
|
38
|
+
imageHotspotArray(),
|
|
39
|
+
]
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Now you will have `imageHotspot` available as an options on `array` fields:
|
|
16
44
|
|
|
17
45
|
```js
|
|
18
|
-
import
|
|
46
|
+
import {defineType, defineField} from 'sanity'
|
|
19
47
|
|
|
20
|
-
export
|
|
48
|
+
export const productSchema = defineType({
|
|
21
49
|
name: `product`,
|
|
22
50
|
title: `Product`,
|
|
23
51
|
type: `document`,
|
|
24
52
|
fields: [
|
|
25
|
-
{
|
|
53
|
+
defineField({
|
|
26
54
|
name: `hotspots`,
|
|
27
55
|
type: `array`,
|
|
28
|
-
inputComponent: HotspotArray,
|
|
29
56
|
of: [
|
|
30
57
|
// see `Spot object` setup below
|
|
31
58
|
],
|
|
32
59
|
options: {
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
// plugin adds support for this option
|
|
61
|
+
imageHotspot: {
|
|
62
|
+
// see `Image and description path` setup below
|
|
63
|
+
imagePath: `featureImage`,
|
|
64
|
+
descriptionPath: `details`,
|
|
65
|
+
// see `Custom tooltip` setup below
|
|
66
|
+
tooltip: undefined,
|
|
67
|
+
}
|
|
38
68
|
},
|
|
39
|
-
},
|
|
69
|
+
}),
|
|
40
70
|
// ...all your other fields
|
|
71
|
+
// ...of which one should be featureImage in this example
|
|
41
72
|
],
|
|
42
|
-
}
|
|
73
|
+
})
|
|
43
74
|
```
|
|
75
|
+
There is no need to provide an explicit input component, as that is handled by the plugin.
|
|
44
76
|
|
|
45
77
|
The plugin makes a number of assumptions to add and update data in the array. Including:
|
|
46
78
|
|
|
47
|
-
- The `array` field is an array of objects
|
|
79
|
+
- The `array` field is an array of objects, with a single object type
|
|
48
80
|
- The object contains two number fields named `x` and `y`
|
|
49
81
|
- You'll want to save those values as % from the top left of the image
|
|
50
82
|
- The same document contains the image you want to add hotspots to
|
|
51
83
|
|
|
52
|
-
### Image
|
|
84
|
+
### Image path
|
|
53
85
|
|
|
54
86
|
The custom input has the current values of all fields in the document, and so can "pick" the image out of the document by its path.
|
|
55
87
|
|
|
@@ -57,15 +89,31 @@ For example, if you want to add hotspots to an image, and that image is uploaded
|
|
|
57
89
|
|
|
58
90
|
```js
|
|
59
91
|
options: {
|
|
60
|
-
|
|
92
|
+
imageHotspot: {
|
|
93
|
+
imagePath: `featureImage`
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
To pick the image out of the hotspot-array parent object, use
|
|
99
|
+
```js
|
|
100
|
+
options: {
|
|
101
|
+
imageHotspot: {
|
|
102
|
+
pathRoot: 'parent'
|
|
103
|
+
}
|
|
61
104
|
}
|
|
62
105
|
```
|
|
63
106
|
|
|
64
|
-
|
|
107
|
+
### Description path
|
|
108
|
+
|
|
109
|
+
The custom input can also pre-fill a string or text field with a description of the position of the spot to make them easier to identify.
|
|
110
|
+
Add a path **relative to the spot object** for this field.
|
|
65
111
|
|
|
66
112
|
```js
|
|
67
113
|
options: {
|
|
68
|
-
|
|
114
|
+
imageHotspot: {
|
|
115
|
+
descriptionPath: `details`
|
|
116
|
+
}
|
|
69
117
|
}
|
|
70
118
|
```
|
|
71
119
|
|
|
@@ -74,7 +122,7 @@ options: {
|
|
|
74
122
|
Here's an example object schema complete with initial values, validation, fieldsets and a styled preview.
|
|
75
123
|
|
|
76
124
|
```js
|
|
77
|
-
{
|
|
125
|
+
defineField({
|
|
78
126
|
name: 'spot',
|
|
79
127
|
type: 'object',
|
|
80
128
|
fieldsets: [{name: 'position', options: {columns: 2}}],
|
|
@@ -110,28 +158,66 @@ Here's an example object schema complete with initial values, validation, fields
|
|
|
110
158
|
}
|
|
111
159
|
},
|
|
112
160
|
},
|
|
113
|
-
}
|
|
161
|
+
})
|
|
114
162
|
```
|
|
115
163
|
|
|
116
164
|
## Custom tooltip
|
|
117
165
|
|
|
118
|
-
You can customise the Tooltip to display any Component,
|
|
166
|
+
You can customise the Tooltip to display any Component, which will receive `value` (the hotspot value with x and y),
|
|
167
|
+
`schemaType` (schemaType of the hotspot value), and `renderPreview` (callback for rendering Studio preview).
|
|
119
168
|
|
|
120
|
-
|
|
169
|
+
### Example 1 - use default hotspot preview
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { Box } from "@sanity/ui";
|
|
173
|
+
import { HotspotTooltipProps } from "sanity-plugin-hotspot-array";
|
|
174
|
+
|
|
175
|
+
export function HotspotPreview({
|
|
176
|
+
value,
|
|
177
|
+
schemaType,
|
|
178
|
+
renderPreview,
|
|
179
|
+
}: HotspotTooltipProps) {
|
|
180
|
+
return (
|
|
181
|
+
<Box padding={2} style={{ minWidth: 200 }}>
|
|
182
|
+
{renderPreview({
|
|
183
|
+
value,
|
|
184
|
+
schemaType,
|
|
185
|
+
layout: "default",
|
|
186
|
+
})}
|
|
187
|
+
</Box>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Then back in your schema definition
|
|
121
193
|
|
|
122
194
|
```js
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
195
|
+
options: {
|
|
196
|
+
imageHotspot: {
|
|
197
|
+
tooltip: HotspotPreview
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Example 2 - reference value in hotspot
|
|
203
|
+
In this example our `value` object has a `reference` field to the `product` schema type, and will show a document preview.
|
|
204
|
+
|
|
205
|
+
```jsx
|
|
206
|
+
import {useSchema }from 'sanity'
|
|
126
207
|
import {Box} from '@sanity/ui'
|
|
127
208
|
|
|
128
|
-
export
|
|
209
|
+
export function ProductPreview({value, renderPreview}) {
|
|
210
|
+
const productSchemaType = useSchema().get('product')
|
|
129
211
|
return (
|
|
130
212
|
<Box padding={2} style={{minWidth: 200}}>
|
|
131
|
-
{
|
|
132
|
-
|
|
213
|
+
{value?.product?._ref ? (
|
|
214
|
+
renderPreview({
|
|
215
|
+
value,
|
|
216
|
+
schemaType: productSchemaType,
|
|
217
|
+
layout: "default"
|
|
218
|
+
})
|
|
133
219
|
) : (
|
|
134
|
-
`No
|
|
220
|
+
`No reference selected`
|
|
135
221
|
)}
|
|
136
222
|
</Box>
|
|
137
223
|
)
|
|
@@ -141,16 +227,28 @@ export default function ProductPreview({spot}) {
|
|
|
141
227
|
Then back in your schema definition
|
|
142
228
|
|
|
143
229
|
```js
|
|
144
|
-
import HotspotArray from 'sanity-plugin-hotspot-array'
|
|
145
|
-
import ProductPreview from '../../components/ProductPreview'
|
|
146
|
-
|
|
147
230
|
options: {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
231
|
+
imageHotspot: {
|
|
232
|
+
tooltip: ProductPreview
|
|
233
|
+
}
|
|
234
|
+
}
|
|
151
235
|
```
|
|
152
236
|
|
|
153
237
|
## License
|
|
154
238
|
|
|
155
|
-
MIT
|
|
156
|
-
|
|
239
|
+
MIT-licensed. See LICENSE.
|
|
240
|
+
|
|
241
|
+
## Develop & test
|
|
242
|
+
|
|
243
|
+
This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
|
|
244
|
+
with default configuration for build & watch scripts.
|
|
245
|
+
|
|
246
|
+
See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
|
|
247
|
+
on how to run this plugin with hotreload in the studio.
|
|
248
|
+
|
|
249
|
+
### Release new version
|
|
250
|
+
|
|
251
|
+
Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-hotspot-array/actions/workflows/main.yml).
|
|
252
|
+
Make sure to select the main branch and check "Release new version".
|
|
253
|
+
|
|
254
|
+
Semantic release will only release on configured branches, so it is safe to run release on any branch.
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
var $dyHF6$reactjsxruntime = require("react/jsx-runtime");
|
|
2
|
+
var $dyHF6$react = require("react");
|
|
3
|
+
var $dyHF6$sanity = require("sanity");
|
|
4
|
+
var $dyHF6$sanityassetutils = require("@sanity/asset-utils");
|
|
5
|
+
var $dyHF6$sanityimageurl = require("@sanity/image-url");
|
|
6
|
+
var $dyHF6$sanityui = require("@sanity/ui");
|
|
7
|
+
var $dyHF6$sanityutilcontent = require("@sanity/util/content");
|
|
8
|
+
var $dyHF6$lodashget = require("lodash/get");
|
|
9
|
+
var $dyHF6$reacthookzweb = require("@react-hookz/web");
|
|
10
|
+
var $dyHF6$framermotion = require("framer-motion");
|
|
11
|
+
|
|
12
|
+
function $parcel$export(e, n, v, s) {
|
|
13
|
+
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
14
|
+
}
|
|
15
|
+
function $parcel$interopDefault(a) {
|
|
16
|
+
return a && a.__esModule ? a.default : a;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
$parcel$export(module.exports, "imageHotspotArrayPlugin", () => $329a1cedcedb1349$export$2e97a933fd61fb01);
|
|
20
|
+
$parcel$export(module.exports, "ImageHotspotArray", () => $4336b3bb99d0500f$export$aa9dd3c5642820fe);
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
function $8f6d54ef8e8d6bcf$export$2e2bcd8739ae039({ children: children }) {
|
|
35
|
+
return /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Card), {
|
|
36
|
+
padding: 4,
|
|
37
|
+
radius: 2,
|
|
38
|
+
shadow: 1,
|
|
39
|
+
tone: "caution",
|
|
40
|
+
children: /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Text), {
|
|
41
|
+
children: children
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
const $b9909e9b23fcefed$var$dragStyle = {
|
|
53
|
+
width: "1.4rem",
|
|
54
|
+
height: "1.4rem",
|
|
55
|
+
position: "absolute",
|
|
56
|
+
boxSizing: "border-box",
|
|
57
|
+
top: 0,
|
|
58
|
+
left: 0,
|
|
59
|
+
margin: "-0.7rem 0 0 -0.7rem",
|
|
60
|
+
cursor: "pointer",
|
|
61
|
+
display: "flex",
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
borderRadius: "50%",
|
|
65
|
+
background: "#000",
|
|
66
|
+
color: "white"
|
|
67
|
+
};
|
|
68
|
+
const $b9909e9b23fcefed$var$dragStyleWhileDrag = {
|
|
69
|
+
background: "rgba(0, 0, 0, 0.1)",
|
|
70
|
+
border: "1px solid #fff",
|
|
71
|
+
cursor: "none"
|
|
72
|
+
};
|
|
73
|
+
const $b9909e9b23fcefed$var$dragStyleWhileHover = {
|
|
74
|
+
background: "rgba(0, 0, 0, 0.1)",
|
|
75
|
+
border: "1px solid #fff"
|
|
76
|
+
};
|
|
77
|
+
const $b9909e9b23fcefed$var$dotStyle = {
|
|
78
|
+
position: "absolute",
|
|
79
|
+
left: "50%",
|
|
80
|
+
top: "50%",
|
|
81
|
+
height: "0.2rem",
|
|
82
|
+
width: "0.2rem",
|
|
83
|
+
margin: "-0.1rem 0 0 -0.1rem",
|
|
84
|
+
background: "#fff",
|
|
85
|
+
visibility: "hidden",
|
|
86
|
+
borderRadius: "50%",
|
|
87
|
+
// make sure pointer events only run on the parent
|
|
88
|
+
pointerEvents: "none"
|
|
89
|
+
};
|
|
90
|
+
const $b9909e9b23fcefed$var$dotStyleWhileActive = {
|
|
91
|
+
visibility: "visible"
|
|
92
|
+
};
|
|
93
|
+
const $b9909e9b23fcefed$var$labelStyle = {
|
|
94
|
+
color: "white",
|
|
95
|
+
fontSize: "0.7rem",
|
|
96
|
+
fontWeight: 600,
|
|
97
|
+
lineHeight: "1"
|
|
98
|
+
};
|
|
99
|
+
const $b9909e9b23fcefed$var$labelStyleWhileActive = {
|
|
100
|
+
visibility: "hidden"
|
|
101
|
+
};
|
|
102
|
+
const $b9909e9b23fcefed$var$round = (num)=>Math.round(num * 100) / 100;
|
|
103
|
+
function $b9909e9b23fcefed$export$2e2bcd8739ae039({ value: value , bounds: bounds , update: update , hotspotDescriptionPath: hotspotDescriptionPath , tooltip: tooltip , index: index , schemaType: schemaType , renderPreview: renderPreview }) {
|
|
104
|
+
const [isDragging, setIsDragging] = (0, ($parcel$interopDefault($dyHF6$react))).useState(false);
|
|
105
|
+
const [isHovering, setIsHovering] = (0, ($parcel$interopDefault($dyHF6$react))).useState(false);
|
|
106
|
+
// x/y are stored as % but need to be converted to px
|
|
107
|
+
const x = (0, $dyHF6$framermotion.useMotionValue)($b9909e9b23fcefed$var$round(bounds.width * (value.x / 100)));
|
|
108
|
+
const y = (0, $dyHF6$framermotion.useMotionValue)($b9909e9b23fcefed$var$round(bounds.height * (value.y / 100)));
|
|
109
|
+
/**
|
|
110
|
+
* update x/y if the bounds change when resizing the window
|
|
111
|
+
*/ (0, $dyHF6$react.useEffect)(()=>{
|
|
112
|
+
x.set($b9909e9b23fcefed$var$round(bounds.width * (value.x / 100)));
|
|
113
|
+
y.set($b9909e9b23fcefed$var$round(bounds.height * (value.y / 100)));
|
|
114
|
+
}, [
|
|
115
|
+
x,
|
|
116
|
+
y,
|
|
117
|
+
value,
|
|
118
|
+
bounds
|
|
119
|
+
]);
|
|
120
|
+
const handleDragEnd = (0, ($parcel$interopDefault($dyHF6$react))).useCallback(()=>{
|
|
121
|
+
setIsDragging(false);
|
|
122
|
+
// get current values for x/y in px
|
|
123
|
+
const currentX = x.get();
|
|
124
|
+
const currentY = y.get();
|
|
125
|
+
// Which we need to convert back to `%` to patch the document
|
|
126
|
+
const newX = $b9909e9b23fcefed$var$round(currentX * 100 / bounds.width);
|
|
127
|
+
const newY = $b9909e9b23fcefed$var$round(currentY * 100 / bounds.height);
|
|
128
|
+
// Don't go below 0 or above 100
|
|
129
|
+
const safeX = Math.max(0, Math.min(100, newX));
|
|
130
|
+
const safeY = Math.max(0, Math.min(100, newY));
|
|
131
|
+
update(value._key, safeX, safeY);
|
|
132
|
+
}, [
|
|
133
|
+
x,
|
|
134
|
+
y,
|
|
135
|
+
value,
|
|
136
|
+
update,
|
|
137
|
+
bounds
|
|
138
|
+
]);
|
|
139
|
+
const handleDragStart = (0, ($parcel$interopDefault($dyHF6$react))).useCallback(()=>setIsDragging(true), []);
|
|
140
|
+
const handleHoverStart = (0, ($parcel$interopDefault($dyHF6$react))).useCallback(()=>setIsHovering(true), []);
|
|
141
|
+
const handleHoverEnd = (0, ($parcel$interopDefault($dyHF6$react))).useCallback(()=>setIsHovering(false), []);
|
|
142
|
+
if (!x || !y) return null;
|
|
143
|
+
return /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Tooltip), {
|
|
144
|
+
disabled: isDragging,
|
|
145
|
+
portal: true,
|
|
146
|
+
content: tooltip && typeof tooltip === "function" ? /*#__PURE__*/ (0, ($parcel$interopDefault($dyHF6$react))).createElement(tooltip, {
|
|
147
|
+
value: value,
|
|
148
|
+
renderPreview: renderPreview,
|
|
149
|
+
schemaType: schemaType
|
|
150
|
+
}) : /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Box), {
|
|
151
|
+
padding: 2,
|
|
152
|
+
style: {
|
|
153
|
+
maxWidth: 200,
|
|
154
|
+
pointerEvents: `none`
|
|
155
|
+
},
|
|
156
|
+
children: /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Text), {
|
|
157
|
+
textOverflow: "ellipsis",
|
|
158
|
+
children: hotspotDescriptionPath ? (0, ($parcel$interopDefault($dyHF6$lodashget)))(value, hotspotDescriptionPath) : `${value.x}% x ${value.y}%`
|
|
159
|
+
})
|
|
160
|
+
}),
|
|
161
|
+
children: /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsxs)((0, $dyHF6$framermotion.motion).div, {
|
|
162
|
+
drag: true,
|
|
163
|
+
dragConstraints: bounds,
|
|
164
|
+
dragElastic: 0,
|
|
165
|
+
dragMomentum: false,
|
|
166
|
+
onDragEnd: handleDragEnd,
|
|
167
|
+
onDragStart: handleDragStart,
|
|
168
|
+
onHoverStart: handleHoverStart,
|
|
169
|
+
onHoverEnd: handleHoverEnd,
|
|
170
|
+
style: {
|
|
171
|
+
...$b9909e9b23fcefed$var$dragStyle,
|
|
172
|
+
x: x,
|
|
173
|
+
y: y,
|
|
174
|
+
...isDragging && {
|
|
175
|
+
...$b9909e9b23fcefed$var$dragStyleWhileDrag
|
|
176
|
+
},
|
|
177
|
+
...isHovering && {
|
|
178
|
+
...$b9909e9b23fcefed$var$dragStyleWhileHover
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
children: [
|
|
182
|
+
/*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Box), {
|
|
183
|
+
style: {
|
|
184
|
+
...$b9909e9b23fcefed$var$dotStyle,
|
|
185
|
+
...(isDragging || isHovering) && {
|
|
186
|
+
...$b9909e9b23fcefed$var$dotStyleWhileActive
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}),
|
|
190
|
+
/*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)("div", {
|
|
191
|
+
style: {
|
|
192
|
+
...$b9909e9b23fcefed$var$labelStyle,
|
|
193
|
+
...(isDragging || isHovering) && {
|
|
194
|
+
...$b9909e9b23fcefed$var$labelStyleWhileActive
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
children: index + 1
|
|
198
|
+
})
|
|
199
|
+
]
|
|
200
|
+
})
|
|
201
|
+
}, value._key);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
const $4336b3bb99d0500f$var$imageStyle = {
|
|
206
|
+
width: `100%`,
|
|
207
|
+
height: `auto`
|
|
208
|
+
};
|
|
209
|
+
const $4336b3bb99d0500f$var$VALID_ROOT_PATHS = [
|
|
210
|
+
"document",
|
|
211
|
+
"parent"
|
|
212
|
+
];
|
|
213
|
+
function $4336b3bb99d0500f$export$aa9dd3c5642820fe(props) {
|
|
214
|
+
const { value: value , onChange: onChange , imageHotspotOptions: imageHotspotOptions , schemaType: schemaType , renderPreview: renderPreview } = props;
|
|
215
|
+
const sanityClient = (0, $dyHF6$sanity.useClient)({
|
|
216
|
+
apiVersion: "2022-01-01"
|
|
217
|
+
});
|
|
218
|
+
const imageHotspotPathRoot = (0, $dyHF6$react.useMemo)(()=>{
|
|
219
|
+
const pathRoot = $4336b3bb99d0500f$var$VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? "") ? imageHotspotOptions.pathRoot : "document";
|
|
220
|
+
return pathRoot === "document" ? [] : props.path.slice(0, -1);
|
|
221
|
+
}, [
|
|
222
|
+
imageHotspotOptions.pathRoot,
|
|
223
|
+
props.path
|
|
224
|
+
]);
|
|
225
|
+
const rootObject = (0, $dyHF6$sanity.useFormValue)(imageHotspotPathRoot);
|
|
226
|
+
/**
|
|
227
|
+
* Finding the image from the imageHotspotPathRoot (defaults to document),
|
|
228
|
+
* using the path from the hotspot's `options` field
|
|
229
|
+
*
|
|
230
|
+
* when changes in imageHotspotPathRoot (e.g. document) occur,
|
|
231
|
+
* check if there are any changes to the hotspotImage and update the reference
|
|
232
|
+
*/ const hotspotImage = (0, $dyHF6$react.useMemo)(()=>{
|
|
233
|
+
return (0, ($parcel$interopDefault($dyHF6$lodashget)))(rootObject, imageHotspotOptions.imagePath);
|
|
234
|
+
}, [
|
|
235
|
+
rootObject,
|
|
236
|
+
imageHotspotOptions.imagePath
|
|
237
|
+
]);
|
|
238
|
+
const displayImage = (0, $dyHF6$react.useMemo)(()=>{
|
|
239
|
+
const builder = (0, ($parcel$interopDefault($dyHF6$sanityimageurl)))(sanityClient).dataset(sanityClient.config().dataset ?? "");
|
|
240
|
+
const urlFor = (source)=>builder.image(source);
|
|
241
|
+
if (hotspotImage?.asset?._ref) {
|
|
242
|
+
const { aspectRatio: aspectRatio } = (0, $dyHF6$sanityassetutils.getImageDimensions)(hotspotImage.asset._ref);
|
|
243
|
+
const width = 1200;
|
|
244
|
+
const height = Math.round(width / aspectRatio);
|
|
245
|
+
const url = urlFor(hotspotImage).width(width).url();
|
|
246
|
+
return {
|
|
247
|
+
width: width,
|
|
248
|
+
height: height,
|
|
249
|
+
url: url
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}, [
|
|
254
|
+
hotspotImage,
|
|
255
|
+
sanityClient
|
|
256
|
+
]);
|
|
257
|
+
const handleHotspotImageClick = (0, $dyHF6$react.useCallback)((event)=>{
|
|
258
|
+
const { nativeEvent: nativeEvent } = event;
|
|
259
|
+
// Calculate the x/y percentage of the click position
|
|
260
|
+
const x = Number((nativeEvent.offsetX * 100 / nativeEvent.srcElement.width).toFixed(2));
|
|
261
|
+
const y = Number((nativeEvent.offsetY * 100 / nativeEvent.srcElement.height).toFixed(2));
|
|
262
|
+
const description = `New Hotspot at ${x}% x ${y}%`;
|
|
263
|
+
const newRow = {
|
|
264
|
+
_key: (0, $dyHF6$sanityutilcontent.randomKey)(12),
|
|
265
|
+
_type: schemaType.of[0].name,
|
|
266
|
+
x: x,
|
|
267
|
+
y: y
|
|
268
|
+
};
|
|
269
|
+
if (imageHotspotOptions?.descriptionPath) newRow[imageHotspotOptions.descriptionPath] = description;
|
|
270
|
+
onChange((0, $dyHF6$sanity.PatchEvent).from([
|
|
271
|
+
(0, $dyHF6$sanity.setIfMissing)([]),
|
|
272
|
+
(0, $dyHF6$sanity.insert)([
|
|
273
|
+
newRow
|
|
274
|
+
], "after", [
|
|
275
|
+
-1
|
|
276
|
+
])
|
|
277
|
+
]));
|
|
278
|
+
}, [
|
|
279
|
+
imageHotspotOptions,
|
|
280
|
+
onChange,
|
|
281
|
+
schemaType
|
|
282
|
+
]);
|
|
283
|
+
const handleHotspotMove = (0, $dyHF6$react.useCallback)((key, x, y)=>{
|
|
284
|
+
onChange((0, $dyHF6$sanity.PatchEvent).from([
|
|
285
|
+
// Set the `x` value of this array key item
|
|
286
|
+
(0, $dyHF6$sanity.set)(x, [
|
|
287
|
+
{
|
|
288
|
+
_key: key
|
|
289
|
+
},
|
|
290
|
+
"x"
|
|
291
|
+
]),
|
|
292
|
+
// Set the `y` value of this array key item
|
|
293
|
+
(0, $dyHF6$sanity.set)(y, [
|
|
294
|
+
{
|
|
295
|
+
_key: key
|
|
296
|
+
},
|
|
297
|
+
"y"
|
|
298
|
+
]),
|
|
299
|
+
]));
|
|
300
|
+
}, [
|
|
301
|
+
onChange
|
|
302
|
+
]);
|
|
303
|
+
const hotspotImageRef = (0, $dyHF6$react.useRef)(null);
|
|
304
|
+
const [imageRect, setImageRect] = (0, $dyHF6$react.useState)();
|
|
305
|
+
const updateImageRectCallback = (0, $dyHF6$reacthookzweb.useDebouncedCallback)((e)=>setImageRect(e.contentRect), [
|
|
306
|
+
setImageRect
|
|
307
|
+
], 200);
|
|
308
|
+
(0, $dyHF6$reacthookzweb.useResizeObserver)(hotspotImageRef, updateImageRectCallback);
|
|
309
|
+
return /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsxs)((0, $dyHF6$sanityui.Stack), {
|
|
310
|
+
space: [
|
|
311
|
+
2,
|
|
312
|
+
2,
|
|
313
|
+
3
|
|
314
|
+
],
|
|
315
|
+
children: [
|
|
316
|
+
displayImage?.url ? /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsxs)("div", {
|
|
317
|
+
style: {
|
|
318
|
+
position: `relative`
|
|
319
|
+
},
|
|
320
|
+
children: [
|
|
321
|
+
imageRect && (value?.length ?? 0) > 0 && value?.map((spot, index)=>/*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $b9909e9b23fcefed$export$2e2bcd8739ae039), {
|
|
322
|
+
index: index,
|
|
323
|
+
value: spot,
|
|
324
|
+
bounds: imageRect,
|
|
325
|
+
update: handleHotspotMove,
|
|
326
|
+
hotspotDescriptionPath: imageHotspotOptions?.descriptionPath,
|
|
327
|
+
tooltip: imageHotspotOptions?.tooltip,
|
|
328
|
+
renderPreview: renderPreview,
|
|
329
|
+
schemaType: schemaType.of[0]
|
|
330
|
+
}, spot._key)),
|
|
331
|
+
/*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Card), {
|
|
332
|
+
__unstable_checkered: true,
|
|
333
|
+
shadow: 1,
|
|
334
|
+
children: /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $dyHF6$sanityui.Flex), {
|
|
335
|
+
align: "center",
|
|
336
|
+
justify: "center",
|
|
337
|
+
children: /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)("img", {
|
|
338
|
+
ref: hotspotImageRef,
|
|
339
|
+
src: displayImage.url,
|
|
340
|
+
width: displayImage.width,
|
|
341
|
+
height: displayImage.height,
|
|
342
|
+
alt: "",
|
|
343
|
+
style: $4336b3bb99d0500f$var$imageStyle,
|
|
344
|
+
onClick: handleHotspotImageClick
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
]
|
|
349
|
+
}) : /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $8f6d54ef8e8d6bcf$export$2e2bcd8739ae039), {
|
|
350
|
+
children: imageHotspotOptions.imagePath ? /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsxs)((0, $dyHF6$reactjsxruntime.Fragment), {
|
|
351
|
+
children: [
|
|
352
|
+
"No Hotspot image found at path ",
|
|
353
|
+
/*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)("code", {
|
|
354
|
+
children: imageHotspotOptions.imagePath
|
|
355
|
+
})
|
|
356
|
+
]
|
|
357
|
+
}) : /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsxs)((0, $dyHF6$reactjsxruntime.Fragment), {
|
|
358
|
+
children: [
|
|
359
|
+
"Define a path in this field using to the image field in this document at",
|
|
360
|
+
" ",
|
|
361
|
+
/*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)("code", {
|
|
362
|
+
children: "options.hotspotImagePath"
|
|
363
|
+
})
|
|
364
|
+
]
|
|
365
|
+
})
|
|
366
|
+
}),
|
|
367
|
+
imageHotspotOptions.pathRoot && !$4336b3bb99d0500f$var$VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsxs)((0, $8f6d54ef8e8d6bcf$export$2e2bcd8739ae039), {
|
|
368
|
+
children: [
|
|
369
|
+
'The supplied imageHotspotPathRoot "',
|
|
370
|
+
imageHotspotOptions.pathRoot,
|
|
371
|
+
'" is not valid, falling back to "document". Available values are "',
|
|
372
|
+
$4336b3bb99d0500f$var$VALID_ROOT_PATHS.join(", "),
|
|
373
|
+
'".'
|
|
374
|
+
]
|
|
375
|
+
}),
|
|
376
|
+
props.renderDefault(props)
|
|
377
|
+
]
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
const $329a1cedcedb1349$export$2e97a933fd61fb01 = (0, $dyHF6$sanity.createPlugin)({
|
|
385
|
+
name: "image-hotspot-array",
|
|
386
|
+
form: {
|
|
387
|
+
components: {
|
|
388
|
+
input: (props)=>{
|
|
389
|
+
if (props.schemaType.jsonType === "array" && props.schemaType.options?.imageHotspot) {
|
|
390
|
+
const imageHotspotOptions = props.schemaType.options?.imageHotspot;
|
|
391
|
+
if (imageHotspotOptions) return /*#__PURE__*/ (0, $dyHF6$reactjsxruntime.jsx)((0, $4336b3bb99d0500f$export$aa9dd3c5642820fe), {
|
|
392
|
+
...props,
|
|
393
|
+
imageHotspotOptions: imageHotspotOptions
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
return props.renderDefault(props);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
//# sourceMappingURL=index.js.map
|