sanity-plugin-hotspot-array 0.0.8 → 0.1.0-v3-studio.2
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/index.esm.js +2 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/src/index.d.ts +55 -0
- package/package.json +60 -46
- package/sanity.json +6 -5
- package/src/Feedback.tsx +2 -2
- package/src/ImageHotspotArray.tsx +188 -0
- package/src/Spot.tsx +31 -19
- package/src/index.ts +4 -0
- package/src/plugin.tsx +39 -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 -181
- 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 defineConfig({
|
|
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/index.esm.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,i)}return o}function t(t){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){o(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function o(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}import{jsx as i,jsxs as r,Fragment as n}from"react/jsx-runtime";import{getImageDimensions as a}from"@sanity/asset-utils";import{useClient as l,useFormValue as s,PatchEvent as c,setIfMissing as d,insert as h,set as u,definePlugin as p}from"sanity";import m from"@sanity/image-url";import{Card as f,Text as g,Tooltip as y,Box as b,Stack as v,Flex as w}from"@sanity/ui";import{randomKey as P}from"@sanity/util/content";import x from"lodash/get";import O,{useEffect as k,useMemo as j,useCallback as _,useRef as D,useState as E}from"react";import{useDebouncedCallback as R,useResizeObserver as H}from"@react-hookz/web";import{useMotionValue as T,motion as C}from"framer-motion";function S(e){let{children:t}=e;return i(f,{padding:4,radius:2,shadow:1,tone:"caution",children:i(g,{children:t})})}const M={width:"1.4rem",height:"1.4rem",position:"absolute",boxSizing:"border-box",top:0,left:0,margin:"-0.7rem 0 0 -0.7rem",cursor:"pointer",display:"flex",justifyContent:"center",alignItems:"center",borderRadius:"50%",background:"#000",color:"white"},N={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff",cursor:"none"},z={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff"},F={position:"absolute",left:"50%",top:"50%",height:"0.2rem",width:"0.2rem",margin:"-0.1rem 0 0 -0.1rem",background:"#fff",visibility:"hidden",borderRadius:"50%",pointerEvents:"none"},I={visibility:"visible"},W={color:"white",fontSize:"0.7rem",fontWeight:600,lineHeight:"1"},A={visibility:"hidden"},V=e=>Math.round(100*e)/100;function X(e){let{value:o,bounds:n,update:a,hotspotDescriptionPath:l,tooltip:s,index:c,schemaType:d,renderPreview:h}=e;const[u,p]=O.useState(!1),[m,f]=O.useState(!1),v=T(V(n.width*(o.x/100))),w=T(V(n.height*(o.y/100)));k((()=>{v.set(V(n.width*(o.x/100))),w.set(V(n.height*(o.y/100)))}),[v,w,o,n]);const P=O.useCallback((()=>{p(!1);const e=v.get(),t=w.get(),i=V(100*e/n.width),r=V(100*t/n.height),l=Math.max(0,Math.min(100,i)),s=Math.max(0,Math.min(100,r));a(o._key,l,s)}),[v,w,o,a,n]),j=O.useCallback((()=>p(!0)),[]),_=O.useCallback((()=>f(!0)),[]),D=O.useCallback((()=>f(!1)),[]);return v&&w?i(y,{disabled:u,portal:!0,content:s&&"function"==typeof s?O.createElement(s,{value:o,renderPreview:h,schemaType:d}):i(b,{padding:2,style:{maxWidth:200,pointerEvents:"none"},children:i(g,{textOverflow:"ellipsis",children:l?x(o,l):"".concat(o.x,"% x ").concat(o.y,"%")})}),children:r(C.div,{drag:!0,dragConstraints:n,dragElastic:0,dragMomentum:!1,onDragEnd:P,onDragStart:j,onHoverStart:_,onHoverEnd:D,style:t(t(t({},M),{},{x:v,y:w},u&&t({},N)),m&&t({},z)),children:[i(b,{style:t(t({},F),(u||m)&&t({},I))}),i("div",{style:t(t({},W),(u||m)&&t({},A)),children:c+1})]})},o._key):null}const Y={width:"100%",height:"auto"},q=["document","parent"];function B(e){var t;const{value:o,onChange:p,imageHotspotOptions:g,schemaType:y,renderPreview:b}=e,O=l({apiVersion:"2022-01-01"}),k=j((()=>{var t;return"document"===(q.includes(null!=(t=g.pathRoot)?t:"")?g.pathRoot:"document")?[]:e.path.slice(0,-1)}),[g.pathRoot,e.path]),T=s(k),C=j((()=>x(T,g.imagePath)),[T,g.imagePath]),M=j((()=>{var e,t;const o=m(O).dataset(null!=(e=O.config().dataset)?e:"");if(null==(t=null==C?void 0:C.asset)?void 0:t._ref){const{aspectRatio:e}=a(C.asset._ref),t=1200;return{width:t,height:Math.round(t/e),url:(i=C,o.image(i)).width(t).url()}}var i;return null}),[C,O]),N=_((e=>{const{nativeEvent:t}=e,o=Number((100*t.offsetX/t.srcElement.width).toFixed(2)),i=Number((100*t.offsetY/t.srcElement.height).toFixed(2)),r="New Hotspot at ".concat(o,"% x ").concat(i,"%"),n={_key:P(12),_type:y.of[0].name,x:o,y:i};(null==g?void 0:g.descriptionPath)&&(n[g.descriptionPath]=r),p(c.from([d([]),h([n],"after",[-1])]))}),[g,p,y]),z=_(((e,t,o)=>{p(c.from([u(t,[{_key:e},"x"]),u(o,[{_key:e},"y"])]))}),[p]),F=D(null),[I,W]=E(),A=R((e=>W(e.contentRect)),[W],200);return H(F,A),r(v,{space:[2,2,3],children:[(null==M?void 0:M.url)?r("div",{style:{position:"relative"},children:[I&&(null!=(t=null==o?void 0:o.length)?t:0)>0&&(null==o?void 0:o.map(((e,t)=>i(X,{index:t,value:e,bounds:I,update:z,hotspotDescriptionPath:null==g?void 0:g.descriptionPath,tooltip:null==g?void 0:g.tooltip,renderPreview:b,schemaType:y.of[0]},e._key)))),i(f,{__unstable_checkered:!0,shadow:1,children:i(w,{align:"center",justify:"center",children:i("img",{ref:F,src:M.url,width:M.width,height:M.height,alt:"",style:Y,onClick:N})})})]}):i(S,{children:g.imagePath?r(n,{children:["No Hotspot image found at path ",i("code",{children:g.imagePath})]}):r(n,{children:["Define a path in this field using to the image field in this document at"," ",i("code",{children:"options.hotspotImagePath"})]})}),g.pathRoot&&!q.includes(g.pathRoot)&&r(S,{children:['The supplied imageHotspotPathRoot "',g.pathRoot,'" is not valid, falling back to "document". Available values are "',q.join(", "),'".']}),e.renderDefault(e)]})}const G=p({name:"image-hotspot-array",form:{components:{input:e=>{var o,r;if("array"===e.schemaType.jsonType&&(null==(o=e.schemaType.options)?void 0:o.imageHotspot)){const o=null==(r=e.schemaType.options)?void 0:r.imageHotspot;if(o)return i(B,t(t({},e),{},{imageHotspotOptions:o}))}return e.renderDefault(e)}}}});export{B as ImageHotspotArray,G as imageHotspotArrayPlugin};
|
|
2
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import React, {ReactNode} from 'react'\nimport {Card, Text} from '@sanity/ui'\n\nexport default function Feedback({children}: {children: ReactNode}) {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","/* eslint-disable */\nimport {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport get from 'lodash/get'\nimport React, {ComponentType, CSSProperties, ReactElement, useEffect} from 'react'\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\nimport {ObjectSchemaType, RenderPreviewCallback} from 'sanity'\n\nconst dragStyle: CSSProperties = {\n width: '1.4rem',\n height: '1.4rem',\n position: 'absolute',\n boxSizing: 'border-box',\n top: 0,\n left: 0,\n margin: '-0.7rem 0 0 -0.7rem',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: '50%',\n background: '#000',\n color: 'white',\n}\n\nconst dragStyleWhileDrag: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n cursor: 'none',\n}\n\nconst dragStyleWhileHover: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n}\n\nconst dotStyle: CSSProperties = {\n position: 'absolute',\n left: '50%',\n top: '50%',\n height: '0.2rem',\n width: '0.2rem',\n margin: '-0.1rem 0 0 -0.1rem',\n background: '#fff',\n visibility: 'hidden',\n borderRadius: '50%',\n // make sure pointer events only run on the parent\n pointerEvents: 'none',\n}\n\nconst dotStyleWhileActive: CSSProperties = {\n visibility: 'visible',\n}\n\nconst labelStyle: CSSProperties = {\n color: 'white',\n fontSize: '0.7rem',\n fontWeight: 600,\n lineHeight: '1',\n}\n\nconst labelStyleWhileActive: CSSProperties = {\n visibility: 'hidden',\n}\n\nconst round = (num: number) => Math.round(num * 100) / 100\n\nexport interface HotspotTooltipProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem<HotspotFields>\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\ninterface HotspotProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem\n bounds: DOMRectReadOnly\n update: FnHotspotMove\n hotspotDescriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n index: number\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\nexport default function Spot({\n value,\n bounds,\n update,\n hotspotDescriptionPath,\n tooltip,\n index,\n schemaType,\n renderPreview,\n}: HotspotProps) {\n const [isDragging, setIsDragging] = React.useState(false)\n const [isHovering, setIsHovering] = React.useState(false)\n\n // x/y are stored as % but need to be converted to px\n const x = useMotionValue(round(bounds.width * (value.x / 100)))\n const y = useMotionValue(round(bounds.height * (value.y / 100)))\n\n /**\n * update x/y if the bounds change when resizing the window\n */\n useEffect(() => {\n x.set(round(bounds.width * (value.x / 100)))\n y.set(round(bounds.height * (value.y / 100)))\n }, [x, y, value, bounds])\n\n const handleDragEnd = React.useCallback(() => {\n setIsDragging(false)\n\n // get current values for x/y in px\n const currentX = x.get()\n const currentY = y.get()\n\n // Which we need to convert back to `%` to patch the document\n const newX = round((currentX * 100) / bounds.width)\n const newY = round((currentY * 100) / bounds.height)\n\n // Don't go below 0 or above 100\n const safeX = Math.max(0, Math.min(100, newX))\n const safeY = Math.max(0, Math.min(100, newY))\n\n update(value._key, safeX, safeY)\n }, [x, y, value, update, bounds])\n const handleDragStart = React.useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = React.useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = React.useCallback(() => setIsHovering(false), [])\n\n if (!x || !y) {\n return null\n }\n\n return (\n <Tooltip\n key={value._key}\n disabled={isDragging}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n React.createElement(tooltip, {value, renderPreview, schemaType})\n ) : (\n <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>\n <Text textOverflow=\"ellipsis\">\n {hotspotDescriptionPath\n ? (get(value, hotspotDescriptionPath) as string)\n : `${value.x}% x ${value.y}%`}\n </Text>\n </Box>\n )\n }\n >\n <motion.div\n drag\n dragConstraints={bounds}\n dragElastic={0}\n dragMomentum={false}\n onDragEnd={handleDragEnd}\n onDragStart={handleDragStart}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n style={{\n ...dragStyle,\n x,\n y,\n ...(isDragging && {...dragStyleWhileDrag}),\n ...(isHovering && {...dragStyleWhileHover}),\n }}\n >\n {/* Dot */}\n <Box\n style={{\n ...dotStyle,\n ...((isDragging || isHovering) && {...dotStyleWhileActive}),\n }}\n />\n {/* Label */}\n <div\n style={{\n ...labelStyle,\n ...((isDragging || isHovering) && {...labelStyleWhileActive}),\n }}\n >\n {index + 1}\n </div>\n </motion.div>\n </Tooltip>\n )\n}\n","/* eslint-disable react/display-name */\n\nimport {getImageDimensions} from '@sanity/asset-utils'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n} from 'sanity'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport get from 'lodash/get'\nimport React, {useCallback, useMemo, useRef, useState} from 'react'\n\nimport {IUseResizeObserverCallback, useDebouncedCallback, useResizeObserver} from '@react-hookz/web'\nimport Feedback from './Feedback'\nimport Spot from './Spot'\nimport {ImageHotspotOptions} from './plugin'\n\nconst imageStyle = {width: `100%`, height: `auto`}\n\nconst VALID_ROOT_PATHS = ['document', 'parent']\n\nexport type FnHotspotMove = (key: string, x: number, y: number) => void\n\nexport type HotspotItem<HotspotFields = {[key: string]: unknown}> = {\n _key: string\n _type: string\n x: number\n y: number\n} & HotspotFields\n\nexport function ImageHotspotArray(\n props: ArrayOfObjectsInputProps<HotspotItem> & {imageHotspotOptions: ImageHotspotOptions}\n) {\n const {value, onChange, imageHotspotOptions, schemaType, renderPreview} = props\n\n const sanityClient = useClient({apiVersion: '2022-01-01'})\n\n const imageHotspotPathRoot = useMemo(() => {\n const pathRoot = VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? '')\n ? imageHotspotOptions.pathRoot\n : 'document'\n return pathRoot === 'document' ? [] : props.path.slice(0, -1)\n }, [imageHotspotOptions.pathRoot, props.path])\n\n const rootObject = useFormValue(imageHotspotPathRoot)\n\n /**\n * Finding the image from the imageHotspotPathRoot (defaults to document),\n * using the path from the hotspot's `options` field\n *\n * when changes in imageHotspotPathRoot (e.g. document) occur,\n * check if there are any changes to the hotspotImage and update the reference\n */\n const hotspotImage = useMemo(() => {\n return get(rootObject, imageHotspotOptions.imagePath) as ImageValue | undefined\n }, [rootObject, imageHotspotOptions.imagePath])\n\n const displayImage = useMemo(() => {\n const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? '')\n const urlFor = (source: ImageValue) => builder.image(source)\n\n if (hotspotImage?.asset?._ref) {\n const {aspectRatio} = getImageDimensions(hotspotImage.asset._ref)\n const width = 1200\n const height = Math.round(width / aspectRatio)\n const url = urlFor(hotspotImage).width(width).url()\n\n return {width, height, url}\n }\n\n return null\n }, [hotspotImage, sanityClient])\n\n const handleHotspotImageClick = useCallback(\n (event: any) => {\n const {nativeEvent} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / nativeEvent.srcElement.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / nativeEvent.srcElement.height).toFixed(2))\n const description = `New Hotspot at ${x}% x ${y}%`\n\n const newRow: HotspotItem = {\n _key: randomKey(12),\n _type: schemaType.of[0].name,\n x,\n y,\n }\n\n if (imageHotspotOptions?.descriptionPath) {\n newRow[imageHotspotOptions.descriptionPath] = description\n }\n\n onChange(PatchEvent.from([setIfMissing([]), insert([newRow], 'after', [-1])]))\n },\n [imageHotspotOptions, onChange, schemaType]\n )\n\n const handleHotspotMove: FnHotspotMove = useCallback(\n (key, x, y) => {\n onChange(\n PatchEvent.from([\n // Set the `x` value of this array key item\n set(x, [{_key: key}, 'x']),\n // Set the `y` value of this array key item\n set(y, [{_key: key}, 'y']),\n ])\n )\n },\n [onChange]\n )\n\n const hotspotImageRef = useRef<HTMLImageElement | null>(null)\n\n const [imageRect, setImageRect] = useState<DOMRectReadOnly>()\n const updateImageRectCallback = useDebouncedCallback(\n ((e) => setImageRect(e.contentRect)) as IUseResizeObserverCallback,\n [setImageRect],\n 200\n )\n useResizeObserver(hotspotImageRef, updateImageRectCallback)\n\n return (\n <Stack space={[2, 2, 3]}>\n {displayImage?.url ? (\n <div style={{position: `relative`}}>\n {imageRect &&\n (value?.length ?? 0) > 0 &&\n value?.map((spot, index) => (\n <Spot\n index={index}\n key={spot._key}\n value={spot}\n bounds={imageRect}\n update={handleHotspotMove}\n hotspotDescriptionPath={imageHotspotOptions?.descriptionPath}\n tooltip={imageHotspotOptions?.tooltip}\n renderPreview={renderPreview}\n schemaType={schemaType.of[0] as ObjectSchemaType}\n />\n ))}\n\n <Card __unstable_checkered shadow={1}>\n <Flex align=\"center\" justify=\"center\">\n <img\n ref={hotspotImageRef}\n src={displayImage.url}\n width={displayImage.width}\n height={displayImage.height}\n alt=\"\"\n style={imageStyle}\n onClick={handleHotspotImageClick}\n />\n </Flex>\n </Card>\n </div>\n ) : (\n <Feedback>\n {imageHotspotOptions.imagePath ? (\n <>\n No Hotspot image found at path <code>{imageHotspotOptions.imagePath}</code>\n </>\n ) : (\n <>\n Define a path in this field using to the image field in this document at{' '}\n <code>options.hotspotImagePath</code>\n </>\n )}\n </Feedback>\n )}\n {imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && (\n <Feedback>\n The supplied imageHotspotPathRoot \"{imageHotspotOptions.pathRoot}\" is not valid, falling\n back to \"document\". Available values are \"{VALID_ROOT_PATHS.join(', ')}\".\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ImageHotspotArray, type HotspotItem} from './ImageHotspotArray'\nimport React, {ComponentType} from 'react'\nimport {ArrayOfObjectsInputProps, definePlugin} from 'sanity'\nimport {type HotspotTooltipProps} from './Spot'\n\nexport interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {\n pathRoot?: 'document' | 'parent'\n imagePath: string\n descriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n}\n\ndeclare module '@sanity/types' {\n export interface ArrayOptions {\n imageHotspot?: ImageHotspotOptions\n }\n}\n\nexport const imageHotspotArrayPlugin = definePlugin({\n name: 'image-hotspot-array',\n form: {\n components: {\n input: (props) => {\n if (props.schemaType.jsonType === 'array' && props.schemaType.options?.imageHotspot) {\n const imageHotspotOptions = props.schemaType.options?.imageHotspot\n if (imageHotspotOptions) {\n return (\n <ImageHotspotArray\n {...(props as unknown as ArrayOfObjectsInputProps<HotspotItem>)}\n imageHotspotOptions={imageHotspotOptions}\n />\n )\n }\n }\n return props.renderDefault(props)\n },\n },\n },\n})\n"],"names":["Feedback","_ref","children","jsx","Card","padding","radius","shadow","tone","Text","dragStyle","width","height","position","boxSizing","top","left","margin","cursor","display","justifyContent","alignItems","borderRadius","background","color","dragStyleWhileDrag","border","dragStyleWhileHover","dotStyle","visibility","pointerEvents","dotStyleWhileActive","labelStyle","fontSize","fontWeight","lineHeight","labelStyleWhileActive","round","num","Math","Spot","_ref2","value","bounds","update","hotspotDescriptionPath","tooltip","index","schemaType","renderPreview","isDragging","setIsDragging","React","useState","isHovering","setIsHovering","x","useMotionValue","y","useEffect","set","handleDragEnd","useCallback","currentX","get","currentY","newX","newY","safeX","max","min","safeY","_key","handleDragStart","handleHoverStart","handleHoverEnd","Tooltip","disabled","portal","content","createElement","Box","style","maxWidth","textOverflow","jsxs","motion","div","drag","dragConstraints","dragElastic","dragMomentum","onDragEnd","onDragStart","onHoverStart","onHoverEnd","_objectSpread","imageStyle","VALID_ROOT_PATHS","ImageHotspotArray","props","_a","onChange","imageHotspotOptions","sanityClient","useClient","apiVersion","imageHotspotPathRoot","useMemo","includes","pathRoot","path","slice","rootObject","useFormValue","hotspotImage","imagePath","displayImage","_b","builder","imageUrlBuilder","dataset","config","asset","aspectRatio","getImageDimensions","url","source","image","handleHotspotImageClick","event","nativeEvent","Number","offsetX","srcElement","toFixed","offsetY","description","concat","newRow","randomKey","_type","of","name","descriptionPath","PatchEvent","from","setIfMissing","insert","handleHotspotMove","key","hotspotImageRef","useRef","imageRect","setImageRect","updateImageRectCallback","useDebouncedCallback","e","contentRect","useResizeObserver","Stack","space","length","map","spot","__unstable_checkered","Flex","align","justify","ref","src","alt","onClick","Fragment","join","renderDefault","imageHotspotArrayPlugin","definePlugin","form","components","input","jsonType","options","imageHotspot"],"mappings":"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"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";function e(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,r)}return i}function t(t){for(var r=1;r<arguments.length;r++){var n=null!=arguments[r]?arguments[r]:{};r%2?e(Object(n),!0).forEach((function(e){i(t,e,n[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):e(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))}))}return t}function i(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}Object.defineProperty(exports,"__esModule",{value:!0});var r=require("react/jsx-runtime"),n=require("@sanity/asset-utils"),o=require("sanity"),a=require("@sanity/image-url"),s=require("@sanity/ui"),l=require("@sanity/util/content"),u=require("lodash/get"),c=require("react"),d=require("@react-hookz/web"),h=require("framer-motion");function p(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=p(a),m=p(u),g=p(c);function y(e){let{children:t}=e;return r.jsx(s.Card,{padding:4,radius:2,shadow:1,tone:"caution",children:r.jsx(s.Text,{children:t})})}const b={width:"1.4rem",height:"1.4rem",position:"absolute",boxSizing:"border-box",top:0,left:0,margin:"-0.7rem 0 0 -0.7rem",cursor:"pointer",display:"flex",justifyContent:"center",alignItems:"center",borderRadius:"50%",background:"#000",color:"white"},v={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff",cursor:"none"},x={background:"rgba(0, 0, 0, 0.1)",border:"1px solid #fff"},j={position:"absolute",left:"50%",top:"50%",height:"0.2rem",width:"0.2rem",margin:"-0.1rem 0 0 -0.1rem",background:"#fff",visibility:"hidden",borderRadius:"50%",pointerEvents:"none"},w={visibility:"visible"},P={color:"white",fontSize:"0.7rem",fontWeight:600,lineHeight:"1"},k={visibility:"hidden"},O=e=>Math.round(100*e)/100;function C(e){let{value:i,bounds:n,update:o,hotspotDescriptionPath:a,tooltip:l,index:u,schemaType:d,renderPreview:p}=e;const[f,y]=g.default.useState(!1),[C,E]=g.default.useState(!1),M=h.useMotionValue(O(n.width*(i.x/100))),_=h.useMotionValue(O(n.height*(i.y/100)));c.useEffect((()=>{M.set(O(n.width*(i.x/100))),_.set(O(n.height*(i.y/100)))}),[M,_,i,n]);const D=g.default.useCallback((()=>{y(!1);const e=M.get(),t=_.get(),r=O(100*e/n.width),a=O(100*t/n.height),s=Math.max(0,Math.min(100,r)),l=Math.max(0,Math.min(100,a));o(i._key,s,l)}),[M,_,i,o,n]),R=g.default.useCallback((()=>y(!0)),[]),H=g.default.useCallback((()=>E(!0)),[]),T=g.default.useCallback((()=>E(!1)),[]);return M&&_?r.jsx(s.Tooltip,{disabled:f,portal:!0,content:l&&"function"==typeof l?g.default.createElement(l,{value:i,renderPreview:p,schemaType:d}):r.jsx(s.Box,{padding:2,style:{maxWidth:200,pointerEvents:"none"},children:r.jsx(s.Text,{textOverflow:"ellipsis",children:a?m.default(i,a):"".concat(i.x,"% x ").concat(i.y,"%")})}),children:r.jsxs(h.motion.div,{drag:!0,dragConstraints:n,dragElastic:0,dragMomentum:!1,onDragEnd:D,onDragStart:R,onHoverStart:H,onHoverEnd:T,style:t(t(t({},b),{},{x:M,y:_},f&&t({},v)),C&&t({},x)),children:[r.jsx(s.Box,{style:t(t({},j),(f||C)&&t({},w))}),r.jsx("div",{style:t(t({},P),(f||C)&&t({},k)),children:u+1})]})},i._key):null}const E={width:"100%",height:"auto"},M=["document","parent"];function _(e){var t;const{value:i,onChange:a,imageHotspotOptions:u,schemaType:h,renderPreview:p}=e,g=o.useClient({apiVersion:"2022-01-01"}),b=c.useMemo((()=>{var t;return"document"===(M.includes(null!=(t=u.pathRoot)?t:"")?u.pathRoot:"document")?[]:e.path.slice(0,-1)}),[u.pathRoot,e.path]),v=o.useFormValue(b),x=c.useMemo((()=>m.default(v,u.imagePath)),[v,u.imagePath]),j=c.useMemo((()=>{var e,t;const i=f.default(g).dataset(null!=(e=g.config().dataset)?e:"");if(null==(t=null==x?void 0:x.asset)?void 0:t._ref){const{aspectRatio:e}=n.getImageDimensions(x.asset._ref),t=1200;return{width:t,height:Math.round(t/e),url:(r=x,i.image(r)).width(t).url()}}var r;return null}),[x,g]),w=c.useCallback((e=>{const{nativeEvent:t}=e,i=Number((100*t.offsetX/t.srcElement.width).toFixed(2)),r=Number((100*t.offsetY/t.srcElement.height).toFixed(2)),n="New Hotspot at ".concat(i,"% x ").concat(r,"%"),s={_key:l.randomKey(12),_type:h.of[0].name,x:i,y:r};(null==u?void 0:u.descriptionPath)&&(s[u.descriptionPath]=n),a(o.PatchEvent.from([o.setIfMissing([]),o.insert([s],"after",[-1])]))}),[u,a,h]),P=c.useCallback(((e,t,i)=>{a(o.PatchEvent.from([o.set(t,[{_key:e},"x"]),o.set(i,[{_key:e},"y"])]))}),[a]),k=c.useRef(null),[O,_]=c.useState(),D=d.useDebouncedCallback((e=>_(e.contentRect)),[_],200);return d.useResizeObserver(k,D),r.jsxs(s.Stack,{space:[2,2,3],children:[(null==j?void 0:j.url)?r.jsxs("div",{style:{position:"relative"},children:[O&&(null!=(t=null==i?void 0:i.length)?t:0)>0&&(null==i?void 0:i.map(((e,t)=>r.jsx(C,{index:t,value:e,bounds:O,update:P,hotspotDescriptionPath:null==u?void 0:u.descriptionPath,tooltip:null==u?void 0:u.tooltip,renderPreview:p,schemaType:h.of[0]},e._key)))),r.jsx(s.Card,{__unstable_checkered:!0,shadow:1,children:r.jsx(s.Flex,{align:"center",justify:"center",children:r.jsx("img",{ref:k,src:j.url,width:j.width,height:j.height,alt:"",style:E,onClick:w})})})]}):r.jsx(y,{children:u.imagePath?r.jsxs(r.Fragment,{children:["No Hotspot image found at path ",r.jsx("code",{children:u.imagePath})]}):r.jsxs(r.Fragment,{children:["Define a path in this field using to the image field in this document at"," ",r.jsx("code",{children:"options.hotspotImagePath"})]})}),u.pathRoot&&!M.includes(u.pathRoot)&&r.jsxs(y,{children:['The supplied imageHotspotPathRoot "',u.pathRoot,'" is not valid, falling back to "document". Available values are "',M.join(", "),'".']}),e.renderDefault(e)]})}const D=o.definePlugin({name:"image-hotspot-array",form:{components:{input:e=>{var i,n;if("array"===e.schemaType.jsonType&&(null==(i=e.schemaType.options)?void 0:i.imageHotspot)){const i=null==(n=e.schemaType.options)?void 0:n.imageHotspot;if(i)return r.jsx(_,t(t({},e),{},{imageHotspotOptions:i}))}return e.renderDefault(e)}}}});exports.ImageHotspotArray=_,exports.imageHotspotArrayPlugin=D;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/Feedback.tsx","../src/Spot.tsx","../src/ImageHotspotArray.tsx","../src/plugin.tsx"],"sourcesContent":["import React, {ReactNode} from 'react'\nimport {Card, Text} from '@sanity/ui'\n\nexport default function Feedback({children}: {children: ReactNode}) {\n return (\n <Card padding={4} radius={2} shadow={1} tone=\"caution\">\n <Text>{children}</Text>\n </Card>\n )\n}\n","/* eslint-disable */\nimport {Box, Text, Tooltip} from '@sanity/ui'\nimport {motion, useMotionValue} from 'framer-motion'\nimport get from 'lodash/get'\nimport React, {ComponentType, CSSProperties, ReactElement, useEffect} from 'react'\nimport {FnHotspotMove, HotspotItem} from './ImageHotspotArray'\nimport {ObjectSchemaType, RenderPreviewCallback} from 'sanity'\n\nconst dragStyle: CSSProperties = {\n width: '1.4rem',\n height: '1.4rem',\n position: 'absolute',\n boxSizing: 'border-box',\n top: 0,\n left: 0,\n margin: '-0.7rem 0 0 -0.7rem',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: '50%',\n background: '#000',\n color: 'white',\n}\n\nconst dragStyleWhileDrag: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n cursor: 'none',\n}\n\nconst dragStyleWhileHover: CSSProperties = {\n background: 'rgba(0, 0, 0, 0.1)',\n border: '1px solid #fff',\n}\n\nconst dotStyle: CSSProperties = {\n position: 'absolute',\n left: '50%',\n top: '50%',\n height: '0.2rem',\n width: '0.2rem',\n margin: '-0.1rem 0 0 -0.1rem',\n background: '#fff',\n visibility: 'hidden',\n borderRadius: '50%',\n // make sure pointer events only run on the parent\n pointerEvents: 'none',\n}\n\nconst dotStyleWhileActive: CSSProperties = {\n visibility: 'visible',\n}\n\nconst labelStyle: CSSProperties = {\n color: 'white',\n fontSize: '0.7rem',\n fontWeight: 600,\n lineHeight: '1',\n}\n\nconst labelStyleWhileActive: CSSProperties = {\n visibility: 'hidden',\n}\n\nconst round = (num: number) => Math.round(num * 100) / 100\n\nexport interface HotspotTooltipProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem<HotspotFields>\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\ninterface HotspotProps<HotspotFields = {[key: string]: unknown}> {\n value: HotspotItem\n bounds: DOMRectReadOnly\n update: FnHotspotMove\n hotspotDescriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n index: number\n schemaType: ObjectSchemaType\n renderPreview: RenderPreviewCallback\n}\n\nexport default function Spot({\n value,\n bounds,\n update,\n hotspotDescriptionPath,\n tooltip,\n index,\n schemaType,\n renderPreview,\n}: HotspotProps) {\n const [isDragging, setIsDragging] = React.useState(false)\n const [isHovering, setIsHovering] = React.useState(false)\n\n // x/y are stored as % but need to be converted to px\n const x = useMotionValue(round(bounds.width * (value.x / 100)))\n const y = useMotionValue(round(bounds.height * (value.y / 100)))\n\n /**\n * update x/y if the bounds change when resizing the window\n */\n useEffect(() => {\n x.set(round(bounds.width * (value.x / 100)))\n y.set(round(bounds.height * (value.y / 100)))\n }, [x, y, value, bounds])\n\n const handleDragEnd = React.useCallback(() => {\n setIsDragging(false)\n\n // get current values for x/y in px\n const currentX = x.get()\n const currentY = y.get()\n\n // Which we need to convert back to `%` to patch the document\n const newX = round((currentX * 100) / bounds.width)\n const newY = round((currentY * 100) / bounds.height)\n\n // Don't go below 0 or above 100\n const safeX = Math.max(0, Math.min(100, newX))\n const safeY = Math.max(0, Math.min(100, newY))\n\n update(value._key, safeX, safeY)\n }, [x, y, value, update, bounds])\n const handleDragStart = React.useCallback(() => setIsDragging(true), [])\n\n const handleHoverStart = React.useCallback(() => setIsHovering(true), [])\n const handleHoverEnd = React.useCallback(() => setIsHovering(false), [])\n\n if (!x || !y) {\n return null\n }\n\n return (\n <Tooltip\n key={value._key}\n disabled={isDragging}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n React.createElement(tooltip, {value, renderPreview, schemaType})\n ) : (\n <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>\n <Text textOverflow=\"ellipsis\">\n {hotspotDescriptionPath\n ? (get(value, hotspotDescriptionPath) as string)\n : `${value.x}% x ${value.y}%`}\n </Text>\n </Box>\n )\n }\n >\n <motion.div\n drag\n dragConstraints={bounds}\n dragElastic={0}\n dragMomentum={false}\n onDragEnd={handleDragEnd}\n onDragStart={handleDragStart}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n style={{\n ...dragStyle,\n x,\n y,\n ...(isDragging && {...dragStyleWhileDrag}),\n ...(isHovering && {...dragStyleWhileHover}),\n }}\n >\n {/* Dot */}\n <Box\n style={{\n ...dotStyle,\n ...((isDragging || isHovering) && {...dotStyleWhileActive}),\n }}\n />\n {/* Label */}\n <div\n style={{\n ...labelStyle,\n ...((isDragging || isHovering) && {...labelStyleWhileActive}),\n }}\n >\n {index + 1}\n </div>\n </motion.div>\n </Tooltip>\n )\n}\n","/* eslint-disable react/display-name */\n\nimport {getImageDimensions} from '@sanity/asset-utils'\nimport {\n ArrayOfObjectsInputProps,\n ImageValue,\n insert,\n ObjectSchemaType,\n PatchEvent,\n set,\n setIfMissing,\n useClient,\n useFormValue,\n} from 'sanity'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {Card, Flex, Stack} from '@sanity/ui'\nimport {randomKey} from '@sanity/util/content'\nimport get from 'lodash/get'\nimport React, {useCallback, useMemo, useRef, useState} from 'react'\n\nimport {IUseResizeObserverCallback, useDebouncedCallback, useResizeObserver} from '@react-hookz/web'\nimport Feedback from './Feedback'\nimport Spot from './Spot'\nimport {ImageHotspotOptions} from './plugin'\n\nconst imageStyle = {width: `100%`, height: `auto`}\n\nconst VALID_ROOT_PATHS = ['document', 'parent']\n\nexport type FnHotspotMove = (key: string, x: number, y: number) => void\n\nexport type HotspotItem<HotspotFields = {[key: string]: unknown}> = {\n _key: string\n _type: string\n x: number\n y: number\n} & HotspotFields\n\nexport function ImageHotspotArray(\n props: ArrayOfObjectsInputProps<HotspotItem> & {imageHotspotOptions: ImageHotspotOptions}\n) {\n const {value, onChange, imageHotspotOptions, schemaType, renderPreview} = props\n\n const sanityClient = useClient({apiVersion: '2022-01-01'})\n\n const imageHotspotPathRoot = useMemo(() => {\n const pathRoot = VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot ?? '')\n ? imageHotspotOptions.pathRoot\n : 'document'\n return pathRoot === 'document' ? [] : props.path.slice(0, -1)\n }, [imageHotspotOptions.pathRoot, props.path])\n\n const rootObject = useFormValue(imageHotspotPathRoot)\n\n /**\n * Finding the image from the imageHotspotPathRoot (defaults to document),\n * using the path from the hotspot's `options` field\n *\n * when changes in imageHotspotPathRoot (e.g. document) occur,\n * check if there are any changes to the hotspotImage and update the reference\n */\n const hotspotImage = useMemo(() => {\n return get(rootObject, imageHotspotOptions.imagePath) as ImageValue | undefined\n }, [rootObject, imageHotspotOptions.imagePath])\n\n const displayImage = useMemo(() => {\n const builder = imageUrlBuilder(sanityClient).dataset(sanityClient.config().dataset ?? '')\n const urlFor = (source: ImageValue) => builder.image(source)\n\n if (hotspotImage?.asset?._ref) {\n const {aspectRatio} = getImageDimensions(hotspotImage.asset._ref)\n const width = 1200\n const height = Math.round(width / aspectRatio)\n const url = urlFor(hotspotImage).width(width).url()\n\n return {width, height, url}\n }\n\n return null\n }, [hotspotImage, sanityClient])\n\n const handleHotspotImageClick = useCallback(\n (event: any) => {\n const {nativeEvent} = event\n\n // Calculate the x/y percentage of the click position\n const x = Number(((nativeEvent.offsetX * 100) / nativeEvent.srcElement.width).toFixed(2))\n const y = Number(((nativeEvent.offsetY * 100) / nativeEvent.srcElement.height).toFixed(2))\n const description = `New Hotspot at ${x}% x ${y}%`\n\n const newRow: HotspotItem = {\n _key: randomKey(12),\n _type: schemaType.of[0].name,\n x,\n y,\n }\n\n if (imageHotspotOptions?.descriptionPath) {\n newRow[imageHotspotOptions.descriptionPath] = description\n }\n\n onChange(PatchEvent.from([setIfMissing([]), insert([newRow], 'after', [-1])]))\n },\n [imageHotspotOptions, onChange, schemaType]\n )\n\n const handleHotspotMove: FnHotspotMove = useCallback(\n (key, x, y) => {\n onChange(\n PatchEvent.from([\n // Set the `x` value of this array key item\n set(x, [{_key: key}, 'x']),\n // Set the `y` value of this array key item\n set(y, [{_key: key}, 'y']),\n ])\n )\n },\n [onChange]\n )\n\n const hotspotImageRef = useRef<HTMLImageElement | null>(null)\n\n const [imageRect, setImageRect] = useState<DOMRectReadOnly>()\n const updateImageRectCallback = useDebouncedCallback(\n ((e) => setImageRect(e.contentRect)) as IUseResizeObserverCallback,\n [setImageRect],\n 200\n )\n useResizeObserver(hotspotImageRef, updateImageRectCallback)\n\n return (\n <Stack space={[2, 2, 3]}>\n {displayImage?.url ? (\n <div style={{position: `relative`}}>\n {imageRect &&\n (value?.length ?? 0) > 0 &&\n value?.map((spot, index) => (\n <Spot\n index={index}\n key={spot._key}\n value={spot}\n bounds={imageRect}\n update={handleHotspotMove}\n hotspotDescriptionPath={imageHotspotOptions?.descriptionPath}\n tooltip={imageHotspotOptions?.tooltip}\n renderPreview={renderPreview}\n schemaType={schemaType.of[0] as ObjectSchemaType}\n />\n ))}\n\n <Card __unstable_checkered shadow={1}>\n <Flex align=\"center\" justify=\"center\">\n <img\n ref={hotspotImageRef}\n src={displayImage.url}\n width={displayImage.width}\n height={displayImage.height}\n alt=\"\"\n style={imageStyle}\n onClick={handleHotspotImageClick}\n />\n </Flex>\n </Card>\n </div>\n ) : (\n <Feedback>\n {imageHotspotOptions.imagePath ? (\n <>\n No Hotspot image found at path <code>{imageHotspotOptions.imagePath}</code>\n </>\n ) : (\n <>\n Define a path in this field using to the image field in this document at{' '}\n <code>options.hotspotImagePath</code>\n </>\n )}\n </Feedback>\n )}\n {imageHotspotOptions.pathRoot && !VALID_ROOT_PATHS.includes(imageHotspotOptions.pathRoot) && (\n <Feedback>\n The supplied imageHotspotPathRoot \"{imageHotspotOptions.pathRoot}\" is not valid, falling\n back to \"document\". Available values are \"{VALID_ROOT_PATHS.join(', ')}\".\n </Feedback>\n )}\n {props.renderDefault(props as unknown as ArrayOfObjectsInputProps)}\n </Stack>\n )\n}\n","import {ImageHotspotArray, type HotspotItem} from './ImageHotspotArray'\nimport React, {ComponentType} from 'react'\nimport {ArrayOfObjectsInputProps, definePlugin} from 'sanity'\nimport {type HotspotTooltipProps} from './Spot'\n\nexport interface ImageHotspotOptions<HotspotFields = {[key: string]: unknown}> {\n pathRoot?: 'document' | 'parent'\n imagePath: string\n descriptionPath?: string\n tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>\n}\n\ndeclare module '@sanity/types' {\n export interface ArrayOptions {\n imageHotspot?: ImageHotspotOptions\n }\n}\n\nexport const imageHotspotArrayPlugin = definePlugin({\n name: 'image-hotspot-array',\n form: {\n components: {\n input: (props) => {\n if (props.schemaType.jsonType === 'array' && props.schemaType.options?.imageHotspot) {\n const imageHotspotOptions = props.schemaType.options?.imageHotspot\n if (imageHotspotOptions) {\n return (\n <ImageHotspotArray\n {...(props as unknown as ArrayOfObjectsInputProps<HotspotItem>)}\n imageHotspotOptions={imageHotspotOptions}\n />\n )\n }\n }\n return props.renderDefault(props)\n },\n },\n },\n})\n"],"names":["Feedback","_ref","children","jsx","Card","padding","radius","shadow","tone","Text","dragStyle","width","height","position","boxSizing","top","left","margin","cursor","display","justifyContent","alignItems","borderRadius","background","color","dragStyleWhileDrag","border","dragStyleWhileHover","dotStyle","visibility","pointerEvents","dotStyleWhileActive","labelStyle","fontSize","fontWeight","lineHeight","labelStyleWhileActive","round","num","Math","Spot","_ref2","value","bounds","update","hotspotDescriptionPath","tooltip","index","schemaType","renderPreview","isDragging","setIsDragging","React","useState","isHovering","setIsHovering","x","useMotionValue","y","useEffect","set","handleDragEnd","useCallback","currentX","get","currentY","newX","newY","safeX","max","min","safeY","_key","handleDragStart","handleHoverStart","handleHoverEnd","Tooltip","disabled","portal","content","createElement","Box","style","maxWidth","textOverflow","jsxs","motion","div","drag","dragConstraints","dragElastic","dragMomentum","onDragEnd","onDragStart","onHoverStart","onHoverEnd","_objectSpread","imageStyle","VALID_ROOT_PATHS","ImageHotspotArray","props","_a","onChange","imageHotspotOptions","sanityClient","useClient","apiVersion","imageHotspotPathRoot","useMemo","includes","pathRoot","path","slice","rootObject","useFormValue","hotspotImage","imagePath","displayImage","_b","builder","imageUrlBuilder","dataset","config","asset","aspectRatio","getImageDimensions","url","source","image","handleHotspotImageClick","event","nativeEvent","Number","offsetX","srcElement","toFixed","offsetY","description","concat","newRow","randomKey","_type","of","name","descriptionPath","PatchEvent","from","setIfMissing","insert","handleHotspotMove","key","hotspotImageRef","useRef","imageRect","setImageRect","updateImageRectCallback","useDebouncedCallback","e","contentRect","useResizeObserver","Stack","space","length","map","spot","__unstable_checkered","Flex","align","justify","ref","src","alt","onClick","Fragment","join","renderDefault","imageHotspotArrayPlugin","definePlugin","form","components","input","jsonType","options","imageHotspot"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
|
|
3
|
+
import {ArrayOfObjectsInputProps} from 'sanity'
|
|
4
|
+
import {ComponentType} from 'react'
|
|
5
|
+
import {ObjectSchemaType} from 'sanity'
|
|
6
|
+
import {Plugin as Plugin_2} from 'sanity'
|
|
7
|
+
import {RenderPreviewCallback} from 'sanity'
|
|
8
|
+
|
|
9
|
+
export declare type HotspotItem<
|
|
10
|
+
HotspotFields = {
|
|
11
|
+
[key: string]: unknown
|
|
12
|
+
}
|
|
13
|
+
> = {
|
|
14
|
+
_key: string
|
|
15
|
+
_type: string
|
|
16
|
+
x: number
|
|
17
|
+
y: number
|
|
18
|
+
} & HotspotFields
|
|
19
|
+
|
|
20
|
+
export declare interface HotspotTooltipProps<
|
|
21
|
+
HotspotFields = {
|
|
22
|
+
[key: string]: unknown
|
|
23
|
+
}
|
|
24
|
+
> {
|
|
25
|
+
value: HotspotItem<HotspotFields>
|
|
26
|
+
schemaType: ObjectSchemaType
|
|
27
|
+
renderPreview: RenderPreviewCallback
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export declare function ImageHotspotArray(
|
|
31
|
+
props: ArrayOfObjectsInputProps<HotspotItem> & {
|
|
32
|
+
imageHotspotOptions: ImageHotspotOptions
|
|
33
|
+
}
|
|
34
|
+
): JSX.Element
|
|
35
|
+
|
|
36
|
+
export declare const imageHotspotArrayPlugin: Plugin_2<void>
|
|
37
|
+
|
|
38
|
+
export declare interface ImageHotspotOptions<
|
|
39
|
+
HotspotFields = {
|
|
40
|
+
[key: string]: unknown
|
|
41
|
+
}
|
|
42
|
+
> {
|
|
43
|
+
pathRoot?: 'document' | 'parent'
|
|
44
|
+
imagePath: string
|
|
45
|
+
descriptionPath?: string
|
|
46
|
+
tooltip?: ComponentType<HotspotTooltipProps<HotspotFields>>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {}
|
|
50
|
+
|
|
51
|
+
declare module '@sanity/types' {
|
|
52
|
+
interface ArrayOptions {
|
|
53
|
+
imageHotspot?: ImageHotspotOptions;
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,75 +1,89 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-hotspot-array",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.1.0-v3-studio.2",
|
|
4
4
|
"description": "A configurable Custom Input for Arrays that will add and update items by clicking on an Image",
|
|
5
|
-
"
|
|
5
|
+
"author": "Simeon Griggs <simeon@sanity.io>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./lib/index.js",
|
|
8
|
+
"source": "./src/index.ts",
|
|
9
|
+
"module": "./lib/index.esm.js",
|
|
10
|
+
"types": "./lib/src/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./lib/src/index.d.ts",
|
|
14
|
+
"source": "./src/index.ts",
|
|
15
|
+
"import": "./lib/index.esm.js",
|
|
16
|
+
"require": "./lib/index.js",
|
|
17
|
+
"default": "./lib/index.esm.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"lib",
|
|
23
|
+
"v2-incompatible.js",
|
|
24
|
+
"sanity.json"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=14.0.0"
|
|
28
|
+
},
|
|
6
29
|
"scripts": {
|
|
7
|
-
"build": "
|
|
8
|
-
"watch": "
|
|
9
|
-
"
|
|
10
|
-
"prepublishOnly": "pinst --disable && sanipack build && sanipack verify",
|
|
11
|
-
"postpublish": "pinst --enable",
|
|
30
|
+
"build": "pkg-utils build",
|
|
31
|
+
"watch": "pkg-utils watch",
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
12
33
|
"lint": "eslint .",
|
|
13
|
-
"lint:fix": "eslint . --fix"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
}
|
|
34
|
+
"lint:fix": "eslint . --fix",
|
|
35
|
+
"clean": "rimraf lib",
|
|
36
|
+
"prebuild": "npm run clean && plugin-kit verify-package --silent && pkg-utils",
|
|
37
|
+
"link-watch": "plugin-kit link-watch",
|
|
38
|
+
"prepare": "husky install"
|
|
19
39
|
},
|
|
20
40
|
"repository": {
|
|
21
41
|
"type": "git",
|
|
22
|
-
"url": "git
|
|
42
|
+
"url": "git@github.com:sanity-io/sanity-plugin-hotspot-array.git"
|
|
23
43
|
},
|
|
24
44
|
"keywords": [
|
|
25
45
|
"sanity",
|
|
26
46
|
"sanity-plugin"
|
|
27
47
|
],
|
|
28
|
-
"author": "Simeon Griggs <simeon@sanity.io>",
|
|
29
|
-
"license": "MIT",
|
|
30
48
|
"dependencies": {
|
|
31
49
|
"@react-hookz/web": "^14.2.2",
|
|
32
50
|
"@sanity/asset-utils": "^1.2.3",
|
|
33
|
-
"@sanity/
|
|
34
|
-
"@sanity/
|
|
35
|
-
"@sanity/
|
|
36
|
-
"framer-motion": "^6.3.11"
|
|
37
|
-
"husky": "^8.0.1"
|
|
51
|
+
"@sanity/incompatible-plugin": "^1.0.4",
|
|
52
|
+
"@sanity/ui": "^1.0.0-beta.31",
|
|
53
|
+
"@sanity/util": "3.0.0-rc.0",
|
|
54
|
+
"framer-motion": "^6.3.11"
|
|
38
55
|
},
|
|
39
56
|
"devDependencies": {
|
|
40
|
-
"
|
|
57
|
+
"@commitlint/cli": "^17.2.0",
|
|
58
|
+
"@commitlint/config-conventional": "^17.2.0",
|
|
59
|
+
"@sanity/pkg-utils": "^1.16.2",
|
|
60
|
+
"@sanity/plugin-kit": "^2.0.7",
|
|
61
|
+
"@sanity/semantic-release-preset": "^2.0.2",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
|
63
|
+
"@typescript-eslint/parser": "^5.42.0",
|
|
64
|
+
"eslint": "^8.26.0",
|
|
41
65
|
"eslint-config-prettier": "^8.5.0",
|
|
42
66
|
"eslint-config-sanity": "^6.0.0",
|
|
43
|
-
"eslint-plugin-
|
|
67
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
68
|
+
"eslint-plugin-react": "^7.31.10",
|
|
69
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
70
|
+
"husky": "^8.0.1",
|
|
71
|
+
"lint-staged": "^13.0.3",
|
|
44
72
|
"pinst": "^2.1.6",
|
|
45
|
-
"prettier": "^2.7.
|
|
46
|
-
"
|
|
73
|
+
"prettier": "^2.7.1",
|
|
74
|
+
"react": "^18.0.0",
|
|
75
|
+
"rimraf": "^3.0.2",
|
|
76
|
+
"sanity": "dev-preview || 3.0.0-rc.0",
|
|
77
|
+
"typescript": "^4.8.4"
|
|
47
78
|
},
|
|
48
79
|
"peerDependencies": {
|
|
49
80
|
"@sanity/image-url": "1.0.1",
|
|
50
|
-
"@sanity/util": "2.29.5",
|
|
51
81
|
"lodash": "4.17.21",
|
|
52
|
-
"react": "^
|
|
82
|
+
"react": "^18.0.0",
|
|
83
|
+
"sanity": "dev-preview || 3.0.0-rc.0"
|
|
53
84
|
},
|
|
54
85
|
"bugs": {
|
|
55
|
-
"url": "https://github.com/
|
|
56
|
-
},
|
|
57
|
-
"homepage": "https://github.com/SimeonGriggs/sanity-plugin-hotspot-array#readme",
|
|
58
|
-
"prettier": {
|
|
59
|
-
"semi": false,
|
|
60
|
-
"printWidth": 100,
|
|
61
|
-
"bracketSpacing": false,
|
|
62
|
-
"singleQuote": true
|
|
86
|
+
"url": "https://github.com/sanity-io/sanity-plugin-hotspot-array/issues"
|
|
63
87
|
},
|
|
64
|
-
"
|
|
65
|
-
"parser": "sanipack/babel/eslint-parser",
|
|
66
|
-
"extends": [
|
|
67
|
-
"sanity",
|
|
68
|
-
"sanity/react",
|
|
69
|
-
"prettier"
|
|
70
|
-
],
|
|
71
|
-
"ignorePatterns": [
|
|
72
|
-
"lib/**/"
|
|
73
|
-
]
|
|
74
|
-
}
|
|
88
|
+
"homepage": "https://github.com/sanity-io/sanity-plugin-hotspot-array#readme"
|
|
75
89
|
}
|
package/sanity.json
CHANGED
package/src/Feedback.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, {ReactNode} from 'react'
|
|
2
2
|
import {Card, Text} from '@sanity/ui'
|
|
3
3
|
|
|
4
|
-
export default function Feedback({children}) {
|
|
4
|
+
export default function Feedback({children}: {children: ReactNode}) {
|
|
5
5
|
return (
|
|
6
6
|
<Card padding={4} radius={2} shadow={1} tone="caution">
|
|
7
7
|
<Text>{children}</Text>
|