sanity-plugin-hotspot-array 0.0.1 → 0.0.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/README.md +28 -10
- package/lib/HotspotArray.js +19 -21
- package/lib/HotspotArray.js.map +1 -1
- package/lib/Spot.js +5 -2
- package/lib/Spot.js.map +1 -1
- package/package.json +13 -4
- package/sanity.json +1 -1
- package/src/HotspotArray.tsx +6 -5
- package/src/Spot.tsx +8 -4
package/README.md
CHANGED
|
@@ -27,16 +27,18 @@ export default {
|
|
|
27
27
|
type: `array`,
|
|
28
28
|
inputComponent: HotspotArray,
|
|
29
29
|
of: [
|
|
30
|
-
|
|
30
|
+
// see `Spot object` setup below
|
|
31
31
|
],
|
|
32
32
|
options: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
// see `Image and description path` setup below
|
|
34
|
+
hotspotImagePath: `featureImage`,
|
|
35
|
+
hotspotDescriptionPath: `details`,
|
|
36
|
+
// see `Custom tooltip` setup below
|
|
37
|
+
hotspotTooltip: undefined,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
38
40
|
// ...all your other fields
|
|
39
|
-
]
|
|
41
|
+
],
|
|
40
42
|
}
|
|
41
43
|
```
|
|
42
44
|
|
|
@@ -97,7 +99,7 @@ Here's an example object schema complete with initial values, validation, fields
|
|
|
97
99
|
],
|
|
98
100
|
preview: {
|
|
99
101
|
select: {
|
|
100
|
-
title: '
|
|
102
|
+
title: 'details',
|
|
101
103
|
x: 'x',
|
|
102
104
|
y: 'y',
|
|
103
105
|
},
|
|
@@ -111,9 +113,25 @@ Here's an example object schema complete with initial values, validation, fields
|
|
|
111
113
|
}
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
##
|
|
116
|
+
## Custom tooltip
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
You can customise the Tooltip to display any Component, it will accept a single prop `spot` which contains the values of the object. Example:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import HotspotArray from 'sanity-plugin-hotspot-array'
|
|
122
|
+
import ProductPreview from '../../components/ProductPreview'
|
|
123
|
+
|
|
124
|
+
// Setup a custom tooltip component
|
|
125
|
+
function ProductPreview({spot}) {
|
|
126
|
+
return <div>{JSON.stringify(spot)}</div>
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ...then add to your schema definition
|
|
130
|
+
options: {
|
|
131
|
+
hotspotImagePath: `hotspotImage`,
|
|
132
|
+
hotspotTooltip: ProductPreview,
|
|
133
|
+
},
|
|
134
|
+
```
|
|
117
135
|
|
|
118
136
|
## License
|
|
119
137
|
|
package/lib/HotspotArray.js
CHANGED
|
@@ -45,21 +45,25 @@ var imageStyle = {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
var HotspotArray = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
48
|
-
var _type$
|
|
48
|
+
var _type$options, _type$options2;
|
|
49
49
|
|
|
50
50
|
var type = props.type,
|
|
51
51
|
value = props.value,
|
|
52
52
|
onChange = props.onChange,
|
|
53
|
-
sanityDocument = props.document;
|
|
53
|
+
sanityDocument = props.document;
|
|
54
|
+
|
|
55
|
+
var _ref = type !== null && type !== void 0 ? type : {},
|
|
56
|
+
options = _ref.options; // Attempt prevention of infinite loop in <FormBuilderInput />
|
|
54
57
|
// Re-renders can still occur if this Component is used again in a nested field
|
|
55
58
|
|
|
59
|
+
|
|
56
60
|
var typeWithoutInputComponent = (0, _useUnsetInputComponent.useUnsetInputComponent)(type, type === null || type === void 0 ? void 0 : type.inputComponent); // Finding the image from the document,
|
|
57
61
|
// using the path from the hotspot's `options` field
|
|
58
62
|
|
|
59
63
|
var displayImage = _react.default.useMemo(() => {
|
|
60
|
-
var
|
|
64
|
+
var _hotspotImage$asset;
|
|
61
65
|
|
|
62
|
-
var hotspotImage = (0, _get.default)(sanityDocument,
|
|
66
|
+
var hotspotImage = (0, _get.default)(sanityDocument, options === null || options === void 0 ? void 0 : options.hotspotImagePath);
|
|
63
67
|
|
|
64
68
|
if (hotspotImage !== null && hotspotImage !== void 0 && (_hotspotImage$asset = hotspotImage.asset) !== null && _hotspotImage$asset !== void 0 && _hotspotImage$asset._ref) {
|
|
65
69
|
var _getImageDimensions = (0, _assetUtils.getImageDimensions)(hotspotImage.asset._ref),
|
|
@@ -79,8 +83,6 @@ var HotspotArray = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
79
83
|
}, [sanityDocument, type]);
|
|
80
84
|
|
|
81
85
|
var handleHotspotImageClick = _react.default.useCallback(event => {
|
|
82
|
-
var _type$options2;
|
|
83
|
-
|
|
84
86
|
var nativeEvent = event.nativeEvent; // Calculate the x/y percentage of the click position
|
|
85
87
|
|
|
86
88
|
var x = Number((nativeEvent.offsetX * 100 / nativeEvent.srcElement.width).toFixed(2));
|
|
@@ -93,9 +95,8 @@ var HotspotArray = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
93
95
|
y
|
|
94
96
|
};
|
|
95
97
|
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
newRow[type.options.hotspotDescriptionPath] = description;
|
|
98
|
+
if (options !== null && options !== void 0 && options.hotspotDescriptionPath) {
|
|
99
|
+
newRow[options.hotspotDescriptionPath] = description;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
onChange(_PatchEvent.PatchEvent.from((0, _PatchEvent.setIfMissing)([]), (0, _PatchEvent.insert)([newRow], 'after', [-1])));
|
|
@@ -127,17 +128,14 @@ var HotspotArray = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
127
128
|
style: {
|
|
128
129
|
position: "relative"
|
|
129
130
|
}
|
|
130
|
-
}, (value === null || value === void 0 ? void 0 : value.length) > 0 && value.map(spot => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
hotspotDescriptionPath: type === null || type === void 0 ? void 0 : (_type$options3 = type.options) === null || _type$options3 === void 0 ? void 0 : _type$options3.hotspotDescriptionPath
|
|
139
|
-
});
|
|
140
|
-
}), /*#__PURE__*/_react.default.createElement(_ui.Card, {
|
|
131
|
+
}, (value === null || value === void 0 ? void 0 : value.length) > 0 && value.map(spot => /*#__PURE__*/_react.default.createElement(_Spot.default, {
|
|
132
|
+
key: spot._key,
|
|
133
|
+
spot: spot,
|
|
134
|
+
bounds: hotspotImageRef,
|
|
135
|
+
update: handleHotspotMove,
|
|
136
|
+
hotspotDescriptionPath: options === null || options === void 0 ? void 0 : options.hotspotDescriptionPath,
|
|
137
|
+
tooltip: options === null || options === void 0 ? void 0 : options.hotspotTooltip
|
|
138
|
+
})), /*#__PURE__*/_react.default.createElement(_ui.Card, {
|
|
141
139
|
__unstable_checkered: true,
|
|
142
140
|
shadow: 1
|
|
143
141
|
}, /*#__PURE__*/_react.default.createElement(_ui.Flex, {
|
|
@@ -151,7 +149,7 @@ var HotspotArray = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
151
149
|
alt: "",
|
|
152
150
|
style: imageStyle,
|
|
153
151
|
onClick: handleHotspotImageClick
|
|
154
|
-
})))) : /*#__PURE__*/_react.default.createElement(_Feedback.default, null, type !== null && type !== void 0 && (_type$
|
|
152
|
+
})))) : /*#__PURE__*/_react.default.createElement(_Feedback.default, null, type !== null && type !== void 0 && (_type$options = type.options) !== null && _type$options !== void 0 && _type$options.hotspotImagePath ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "No Hotspot image found at path ", /*#__PURE__*/_react.default.createElement("code", null, type === null || type === void 0 ? void 0 : (_type$options2 = type.options) === null || _type$options2 === void 0 ? void 0 : _type$options2.hotspotImagePath)) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "Define a path in this field using to the image field in this document at ", /*#__PURE__*/_react.default.createElement("code", null, "options.hotspotImagePath"))), /*#__PURE__*/_react.default.createElement(_FormBuilderInput.FormBuilderInput, _extends({}, props, {
|
|
155
153
|
type: typeWithoutInputComponent,
|
|
156
154
|
ref: ref
|
|
157
155
|
})));
|
package/lib/HotspotArray.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/HotspotArray.tsx"],"names":["builder","sanityClient","urlFor","source","image","imageStyle","width","height","HotspotArray","React","forwardRef","props","ref","type","value","onChange","sanityDocument","document","typeWithoutInputComponent","inputComponent","displayImage","useMemo","hotspotImage","
|
|
1
|
+
{"version":3,"sources":["../src/HotspotArray.tsx"],"names":["builder","sanityClient","urlFor","source","image","imageStyle","width","height","HotspotArray","React","forwardRef","props","ref","type","value","onChange","sanityDocument","document","options","typeWithoutInputComponent","inputComponent","displayImage","useMemo","hotspotImage","hotspotImagePath","asset","_ref","aspectRatio","Math","round","url","handleHotspotImageClick","useCallback","event","nativeEvent","x","Number","offsetX","srcElement","toFixed","y","offsetY","description","newRow","_key","_type","hotspotDescriptionPath","PatchEvent","from","handleHotspotMove","key","console","warn","hotspotImageRef","useRef","position","length","map","spot","hotspotTooltip"],"mappings":"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","sourcesContent":["/* eslint-disable react/display-name */\n\n// @ts-ignore\nimport sanityClient from 'part:@sanity/base/client'\n// @ts-ignore\nimport {withDocument} from 'part:@sanity/form-builder'\n\nimport React from 'react'\nimport {Card, Flex, Stack, Text} from '@sanity/ui'\nimport {FormBuilderInput} from '@sanity/form-builder/lib/FormBuilderInput'\nimport {randomKey} from '@sanity/util/content'\nimport {PatchEvent, setIfMissing, set, insert} from '@sanity/form-builder/PatchEvent'\nimport imageUrlBuilder from '@sanity/image-url'\nimport {getImageDimensions} from '@sanity/asset-utils'\nimport get from 'lodash/get'\n\nimport Spot from './Spot'\nimport { useUnsetInputComponent } from './useUnsetInputComponent'\nimport Feedback from './Feedback'\n\nconst builder = imageUrlBuilder(sanityClient)\nconst urlFor = (source) => builder.image(source)\nconst imageStyle = {width: `100%`, height: `auto`}\n\n\nconst HotspotArray = React.forwardRef((props, ref) => {\n const {type, value, onChange, document: sanityDocument} = props\n const {options} = type ?? {}\n\n // Attempt prevention of infinite loop in <FormBuilderInput />\n // Re-renders can still occur if this Component is used again in a nested field\n const typeWithoutInputComponent = useUnsetInputComponent(type, type?.inputComponent)\n\n // Finding the image from the document,\n // using the path from the hotspot's `options` field\n const displayImage = React.useMemo(() => {\n const hotspotImage = get(sanityDocument, options?.hotspotImagePath)\n\n if (hotspotImage?.asset?._ref) {\n const {aspectRatio} = getImageDimensions(hotspotImage.asset._ref)\n const width = 600\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 }, [sanityDocument, type])\n\n const handleHotspotImageClick = React.useCallback((event) => {\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 = {\n _key: randomKey(12),\n _type: `spot`,\n x,\n y,\n }\n\n if (options?.hotspotDescriptionPath) {\n newRow[options.hotspotDescriptionPath] = description\n }\n\n onChange(PatchEvent.from(setIfMissing([]), insert([newRow], 'after', [-1])))\n }, [])\n\n const handleHotspotMove = React.useCallback(\n (key, x, y) => {\n if (!Number(x) || !Number(y)) {\n console.warn(`Missing or non-number X or Y`, {x, y})\n return\n }\n\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 [value]\n )\n\n const hotspotImageRef = React.useRef(null)\n\n return (\n <Stack space={[2,2,3]}>\n {displayImage?.url ? (\n <div style={{position: `relative`}}>\n {value?.length > 0 &&\n value.map((spot) => (\n <Spot\n key={spot._key}\n spot={spot}\n bounds={hotspotImageRef}\n update={handleHotspotMove}\n hotspotDescriptionPath={options?.hotspotDescriptionPath}\n tooltip={options?.hotspotTooltip}\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 {type?.options?.hotspotImagePath \n ? <>No Hotspot image found at path <code>{type?.options?.hotspotImagePath}</code></> \n : <>Define a path in this field using to the image field in this document at <code>options.hotspotImagePath</code></>\n }\n </Feedback>\n )}\n <FormBuilderInput {...props} type={typeWithoutInputComponent} ref={ref} />\n </Stack>\n )\n})\n\nexport default withDocument(HotspotArray)\n"],"file":"HotspotArray.js"}
|
package/lib/Spot.js
CHANGED
|
@@ -53,7 +53,8 @@ function Spot(_ref) {
|
|
|
53
53
|
bounds = _ref$bounds === void 0 ? undefined : _ref$bounds,
|
|
54
54
|
update = _ref.update,
|
|
55
55
|
_ref$hotspotDescripti = _ref.hotspotDescriptionPath,
|
|
56
|
-
hotspotDescriptionPath = _ref$hotspotDescripti === void 0 ? "" : _ref$hotspotDescripti
|
|
56
|
+
hotspotDescriptionPath = _ref$hotspotDescripti === void 0 ? "" : _ref$hotspotDescripti,
|
|
57
|
+
tooltip = _ref.tooltip;
|
|
57
58
|
|
|
58
59
|
// x/y are stored as % but need to be displayed as px
|
|
59
60
|
var _React$useState = _react.default.useState({
|
|
@@ -130,7 +131,9 @@ function Spot(_ref) {
|
|
|
130
131
|
disabled: isDragging,
|
|
131
132
|
boundaryElement: bounds.current,
|
|
132
133
|
portal: true,
|
|
133
|
-
content: /*#__PURE__*/_react.default.createElement(
|
|
134
|
+
content: tooltip && typeof tooltip === 'function' ? /*#__PURE__*/_react.default.createElement(tooltip, {
|
|
135
|
+
spot
|
|
136
|
+
}) : /*#__PURE__*/_react.default.createElement(_ui.Box, {
|
|
134
137
|
padding: 2,
|
|
135
138
|
style: {
|
|
136
139
|
maxWidth: 200,
|
package/lib/Spot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Spot.tsx"],"names":["dragStyle","width","height","position","dotStyle","borderRadius","pointerEvents","round","num","Math","Spot","spot","bounds","undefined","update","hotspotDescriptionPath","React","useState","x","y","setXY","rect","setRect","isDragging","setIsDragging","useEffect","clientRect","current","getBoundingClientRect","handleDragEnd","useCallback","event","srcElement","style","transform","split","map","v","match","pop","replace","currentX","currentY","Number","console","warn","newX","newY","safeX","max","min","safeY","_key","maxWidth"],"mappings":";;;;;;;AAAA;;AACA;;AACA;;AACA;;;;;;;;;;;;;;;;;;;;;;AAEA,IAAMA,SAAS,GAAG;AAChBC,EAAAA,KAAK,QADW;AAEhBC,EAAAA,MAAM,QAFU;AAGhBC,EAAAA,QAAQ;AAHQ,CAAlB;;AAMA,IAAMC,QAAQ,mCACTJ,SADS;AAEZK,EAAAA,YAAY,OAFA;AAGZ;AACAC,EAAAA,aAAa;AAJD,EAAd;;AAOA,IAAMC,KAAK,GAAIC,GAAD,IAASC,IAAI,CAACF,KAAL,CAAWC,GAAG,GAAG,GAAjB,IAAwB,GAA/C;;AAEe,SAASE,IAAT,
|
|
1
|
+
{"version":3,"sources":["../src/Spot.tsx"],"names":["dragStyle","width","height","position","dotStyle","borderRadius","pointerEvents","round","num","Math","Spot","spot","bounds","undefined","update","hotspotDescriptionPath","tooltip","React","useState","x","y","setXY","rect","setRect","isDragging","setIsDragging","useEffect","clientRect","current","getBoundingClientRect","handleDragEnd","useCallback","event","srcElement","style","transform","split","map","v","match","pop","replace","currentX","currentY","Number","console","warn","newX","newY","safeX","max","min","safeY","_key","createElement","maxWidth"],"mappings":"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","sourcesContent":["import React from 'react'\nimport {Box, Card, Text, Tooltip} from '@sanity/ui'\nimport {motion} from 'framer-motion'\nimport get from 'lodash/get'\n\nconst dragStyle = {\n width: `1rem`,\n height: `1rem`,\n position: `absolute`,\n}\n\nconst dotStyle = {\n ...dragStyle,\n borderRadius: `50%`,\n // make sure pointer events only run on the parent\n pointerEvents: `none`,\n}\n\nconst round = (num) => Math.round(num * 100) / 100\n\nexport default function Spot({spot, bounds = undefined, update, hotspotDescriptionPath = ``, tooltip}) {\n // x/y are stored as % but need to be displayed as px\n const [{x, y}, setXY] = React.useState({x: 0, y: 0})\n const [rect, setRect] = React.useState({width: 0, height: 0})\n const [isDragging, setIsDragging] = React.useState(false)\n\n React.useEffect(() => {\n const clientRect = bounds?.current?.getBoundingClientRect()\n\n if (clientRect) {\n // So convert % to px once we know the height/width of the image\n setXY({\n x: round(clientRect.width * (spot.x / 100)),\n y: round(clientRect.height * (spot.y / 100)),\n })\n\n setRect(clientRect)\n }\n }, [bounds, bounds.current])\n\n const handleDragEnd = React.useCallback(\n (event) => {\n setIsDragging(false)\n\n // I don't know why, but framer-motion doesn't give you the actual transform values\n // So we have to regex the `px` values off the inline styles\n const [currentX, currentY] = event.srcElement.style.transform.split(` `).map((v) => {\n return v\n ? v\n .match(/\\(([^)]+)\\)/)\n .pop()\n .replace(`px`, ``)\n : null\n })\n\n if (!Number(currentX) || !Number(currentY)) {\n return console.warn(`Missing or non-number X or Y`, {currentX, currentY}, event.srcElement)\n }\n\n // Which we need to convert back to `%` to patch the document\n const newX = round((currentX * 100) / rect.width)\n const newY = round((currentY * 100) / rect.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(spot._key, safeX, safeY)\n },\n\n [spot]\n )\n\n if (!x || !y) {\n return null\n }\n\n return (\n <Tooltip\n key={spot._key}\n disabled={isDragging}\n boundaryElement={bounds.current}\n portal\n content={\n tooltip && typeof tooltip === 'function' ? (\n React.createElement(tooltip, {spot})\n ) : (\n <Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>\n <Text textOverflow=\"ellipsis\">{hotspotDescriptionPath ? get(spot, hotspotDescriptionPath) : `${spot.x}% x ${spot.y}%`}</Text>\n </Box>\n )\n }\n >\n <motion.div\n drag\n dragConstraints={bounds}\n dragMomentum={false}\n initial={{x, y}}\n onDragEnd={handleDragEnd}\n onDragStart={() => setIsDragging(true)}\n style={dragStyle}\n >\n <Card tone=\"primary\" shadow={3} style={dotStyle} />\n </motion.div>\n </Tooltip>\n )\n}"],"file":"Spot.js"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-hotspot-array",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "A configurable Custom Input for Arrays that will add and update items by clicking on an Image",
|
|
5
5
|
"main": "lib/HotspotArray.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"lint": "eslint .",
|
|
8
7
|
"build": "sanipack build",
|
|
9
8
|
"watch": "sanipack build --watch",
|
|
10
|
-
"
|
|
9
|
+
"_postinstall": "husky install",
|
|
10
|
+
"prepublishOnly": "pinst --disable && sanipack build && sanipack verify",
|
|
11
|
+
"postpublish": "pinst --enable",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"lint:fix": "eslint . --fix"
|
|
14
|
+
},
|
|
15
|
+
"husky": {
|
|
16
|
+
"hooks": {
|
|
17
|
+
"pre-commit": "npm run lint:fix"
|
|
18
|
+
}
|
|
11
19
|
},
|
|
12
20
|
"repository": {
|
|
13
21
|
"type": "git",
|
|
@@ -25,13 +33,14 @@
|
|
|
25
33
|
"@sanity/form-builder": "^2.23.2",
|
|
26
34
|
"@sanity/ui": "^0.36.17",
|
|
27
35
|
"framer-motion": "^5.5.5",
|
|
28
|
-
"
|
|
36
|
+
"husky": "^7.0.1"
|
|
29
37
|
},
|
|
30
38
|
"devDependencies": {
|
|
31
39
|
"eslint": "^7.32.0",
|
|
32
40
|
"eslint-config-prettier": "^8.3.0",
|
|
33
41
|
"eslint-config-sanity": "^5.1.0",
|
|
34
42
|
"eslint-plugin-react": "^7.27.1",
|
|
43
|
+
"pinst": "^2.1.6",
|
|
35
44
|
"prettier": "^2.5.1",
|
|
36
45
|
"sanipack": "^2.1.0"
|
|
37
46
|
},
|
package/sanity.json
CHANGED
package/src/HotspotArray.tsx
CHANGED
|
@@ -25,6 +25,7 @@ const imageStyle = {width: `100%`, height: `auto`}
|
|
|
25
25
|
|
|
26
26
|
const HotspotArray = React.forwardRef((props, ref) => {
|
|
27
27
|
const {type, value, onChange, document: sanityDocument} = props
|
|
28
|
+
const {options} = type ?? {}
|
|
28
29
|
|
|
29
30
|
// Attempt prevention of infinite loop in <FormBuilderInput />
|
|
30
31
|
// Re-renders can still occur if this Component is used again in a nested field
|
|
@@ -33,7 +34,7 @@ const HotspotArray = React.forwardRef((props, ref) => {
|
|
|
33
34
|
// Finding the image from the document,
|
|
34
35
|
// using the path from the hotspot's `options` field
|
|
35
36
|
const displayImage = React.useMemo(() => {
|
|
36
|
-
const hotspotImage = get(sanityDocument,
|
|
37
|
+
const hotspotImage = get(sanityDocument, options?.hotspotImagePath)
|
|
37
38
|
|
|
38
39
|
if (hotspotImage?.asset?._ref) {
|
|
39
40
|
const {aspectRatio} = getImageDimensions(hotspotImage.asset._ref)
|
|
@@ -62,9 +63,8 @@ const HotspotArray = React.forwardRef((props, ref) => {
|
|
|
62
63
|
y,
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
newRow[type.options.hotspotDescriptionPath] = description
|
|
66
|
+
if (options?.hotspotDescriptionPath) {
|
|
67
|
+
newRow[options.hotspotDescriptionPath] = description
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
onChange(PatchEvent.from(setIfMissing([]), insert([newRow], 'after', [-1])))
|
|
@@ -102,7 +102,8 @@ const HotspotArray = React.forwardRef((props, ref) => {
|
|
|
102
102
|
spot={spot}
|
|
103
103
|
bounds={hotspotImageRef}
|
|
104
104
|
update={handleHotspotMove}
|
|
105
|
-
hotspotDescriptionPath={
|
|
105
|
+
hotspotDescriptionPath={options?.hotspotDescriptionPath}
|
|
106
|
+
tooltip={options?.hotspotTooltip}
|
|
106
107
|
/>
|
|
107
108
|
))}
|
|
108
109
|
|
package/src/Spot.tsx
CHANGED
|
@@ -18,7 +18,7 @@ const dotStyle = {
|
|
|
18
18
|
|
|
19
19
|
const round = (num) => Math.round(num * 100) / 100
|
|
20
20
|
|
|
21
|
-
export default function Spot({spot, bounds = undefined, update, hotspotDescriptionPath =
|
|
21
|
+
export default function Spot({spot, bounds = undefined, update, hotspotDescriptionPath = ``, tooltip}) {
|
|
22
22
|
// x/y are stored as % but need to be displayed as px
|
|
23
23
|
const [{x, y}, setXY] = React.useState({x: 0, y: 0})
|
|
24
24
|
const [rect, setRect] = React.useState({width: 0, height: 0})
|
|
@@ -82,9 +82,13 @@ export default function Spot({spot, bounds = undefined, update, hotspotDescripti
|
|
|
82
82
|
boundaryElement={bounds.current}
|
|
83
83
|
portal
|
|
84
84
|
content={
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
tooltip && typeof tooltip === 'function' ? (
|
|
86
|
+
React.createElement(tooltip, {spot})
|
|
87
|
+
) : (
|
|
88
|
+
<Box padding={2} style={{maxWidth: 200, pointerEvents: `none`}}>
|
|
89
|
+
<Text textOverflow="ellipsis">{hotspotDescriptionPath ? get(spot, hotspotDescriptionPath) : `${spot.x}% x ${spot.y}%`}</Text>
|
|
90
|
+
</Box>
|
|
91
|
+
)
|
|
88
92
|
}
|
|
89
93
|
>
|
|
90
94
|
<motion.div
|