@sanity/google-maps-input 2.25.4 → 2.26.1-purple-unicorn.560
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/diff/resolver.js +7 -0
- package/input/GeopointInput.js +7 -0
- package/lib/_Marker-20984c4d.cjs +331 -0
- package/lib/_Marker-20984c4d.cjs.map +1 -0
- package/lib/_Marker-96f5387c.js +302 -0
- package/lib/_Marker-96f5387c.js.map +1 -0
- package/lib/_reExport.js +19 -0
- package/lib/diff/resolver.cjs +204 -0
- package/lib/diff/resolver.cjs.map +1 -0
- package/lib/diff/resolver.js +169 -17
- package/lib/diff/resolver.js.map +1 -0
- package/{dist/dts → lib/dts/src}/diff/GeopointArrayDiff.d.ts +4 -4
- package/lib/dts/src/diff/GeopointArrayDiff.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/diff/GeopointFieldDiff.d.ts +4 -4
- package/lib/dts/src/diff/GeopointFieldDiff.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/diff/GeopointFieldDiff.styles.d.ts +1 -1
- package/lib/dts/src/diff/GeopointFieldDiff.styles.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/diff/GeopointMove.d.ts +12 -12
- package/lib/dts/src/diff/GeopointMove.d.ts.map +1 -0
- package/lib/dts/src/diff/resolver.d.ts +4 -0
- package/lib/dts/src/diff/resolver.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/input/GeopointInput.d.ts +27 -39
- package/lib/dts/src/input/GeopointInput.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/input/GeopointInput.styles.d.ts +2 -2
- package/lib/dts/src/input/GeopointInput.styles.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/input/GeopointSelect.d.ts +27 -27
- package/lib/dts/src/input/GeopointSelect.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/loader/GoogleMapsLoadProxy.d.ts +13 -13
- package/lib/dts/src/loader/GoogleMapsLoadProxy.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/loader/LoadError.d.ts +9 -9
- package/lib/dts/src/loader/LoadError.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/loader/loadGoogleMapsApi.d.ts +17 -17
- package/lib/dts/src/loader/loadGoogleMapsApi.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/Arrow.d.ts +28 -28
- package/lib/dts/src/map/Arrow.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/Map.d.ts +36 -36
- package/lib/dts/src/map/Map.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/Map.styles.d.ts +1 -1
- package/lib/dts/src/map/Map.styles.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/Marker.d.ts +33 -35
- package/lib/dts/src/map/Marker.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/SearchInput.d.ts +15 -15
- package/lib/dts/src/map/SearchInput.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/SearchInput.styles.d.ts +1 -1
- package/lib/dts/src/map/SearchInput.styles.d.ts.map +1 -0
- package/{dist/dts → lib/dts/src}/map/util.d.ts +3 -3
- package/lib/dts/src/map/util.d.ts.map +1 -0
- package/lib/dts/src/types.d.ts +17 -0
- package/lib/dts/src/types.d.ts.map +1 -0
- package/lib/input/GeopointInput.cjs +228 -0
- package/lib/input/GeopointInput.cjs.map +1 -0
- package/lib/input/GeopointInput.js +198 -190
- package/lib/input/GeopointInput.js.map +1 -0
- package/package.json +34 -11
- package/src/@types/css.d.ts +4 -0
- package/src/diff/GeopointArrayDiff.tsx +83 -0
- package/src/diff/GeopointFieldDiff.styles.tsx +20 -0
- package/src/diff/GeopointFieldDiff.tsx +94 -0
- package/src/diff/GeopointMove.tsx +49 -0
- package/src/diff/resolver.ts +21 -0
- package/src/input/GeopointInput.styles.tsx +12 -0
- package/src/input/GeopointInput.tsx +221 -0
- package/src/input/GeopointSelect.tsx +78 -0
- package/src/loader/GoogleMapsLoadProxy.tsx +49 -0
- package/src/loader/LoadError.tsx +44 -0
- package/src/loader/loadGoogleMapsApi.ts +93 -0
- package/src/map/Arrow.tsx +76 -0
- package/src/map/Map.styles.tsx +10 -0
- package/src/map/Map.tsx +125 -0
- package/src/map/Marker.tsx +130 -0
- package/src/map/SearchInput.styles.tsx +8 -0
- package/src/map/SearchInput.tsx +56 -0
- package/src/map/util.ts +14 -0
- package/src/types.ts +19 -0
- package/.depcheckignore.json +0 -3
- package/dist/dts/diff/GeopointArrayDiff.d.ts.map +0 -1
- package/dist/dts/diff/GeopointFieldDiff.d.ts.map +0 -1
- package/dist/dts/diff/GeopointFieldDiff.styles.d.ts.map +0 -1
- package/dist/dts/diff/GeopointMove.d.ts.map +0 -1
- package/dist/dts/diff/resolver.d.ts +0 -4
- package/dist/dts/diff/resolver.d.ts.map +0 -1
- package/dist/dts/input/GeopointInput.d.ts.map +0 -1
- package/dist/dts/input/GeopointInput.styles.d.ts.map +0 -1
- package/dist/dts/input/GeopointSelect.d.ts.map +0 -1
- package/dist/dts/loader/GoogleMapsLoadProxy.d.ts.map +0 -1
- package/dist/dts/loader/LoadError.d.ts.map +0 -1
- package/dist/dts/loader/loadGoogleMapsApi.d.ts.map +0 -1
- package/dist/dts/map/Arrow.d.ts.map +0 -1
- package/dist/dts/map/Map.d.ts.map +0 -1
- package/dist/dts/map/Map.styles.d.ts.map +0 -1
- package/dist/dts/map/Marker.d.ts.map +0 -1
- package/dist/dts/map/SearchInput.d.ts.map +0 -1
- package/dist/dts/map/SearchInput.styles.d.ts.map +0 -1
- package/dist/dts/map/util.d.ts.map +0 -1
- package/dist/dts/types.d.ts +0 -14
- package/dist/dts/types.d.ts.map +0 -1
- package/lib/@types/css.d.js +0 -1
- package/lib/diff/GeopointArrayDiff.js +0 -82
- package/lib/diff/GeopointFieldDiff.js +0 -97
- package/lib/diff/GeopointFieldDiff.styles.js +0 -18
- package/lib/diff/GeopointMove.js +0 -55
- package/lib/input/GeopointInput.styles.js +0 -22
- package/lib/input/GeopointSelect.js +0 -103
- package/lib/loader/GoogleMapsLoadProxy.js +0 -70
- package/lib/loader/LoadError.js +0 -43
- package/lib/loader/loadGoogleMapsApi.js +0 -81
- package/lib/map/Arrow.js +0 -97
- package/lib/map/Map.js +0 -147
- package/lib/map/Map.styles.js +0 -18
- package/lib/map/Marker.js +0 -156
- package/lib/map/SearchInput.js +0 -77
- package/lib/map/SearchInput.styles.js +0 -18
- package/lib/map/util.js +0 -14
- package/lib/types.js +0 -5
- package/tsconfig.json +0 -20
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
DiffComponent,
|
|
4
|
+
ObjectDiff,
|
|
5
|
+
DiffProps as GenericDiffProps,
|
|
6
|
+
DiffTooltip,
|
|
7
|
+
getAnnotationAtPath,
|
|
8
|
+
} from '@sanity/base/field'
|
|
9
|
+
import {GoogleMapsLoadProxy} from '../loader/GoogleMapsLoadProxy'
|
|
10
|
+
import {GoogleMap} from '../map/Map'
|
|
11
|
+
import {Geopoint} from '../types'
|
|
12
|
+
import {GeopointMove} from './GeopointMove'
|
|
13
|
+
import {RootContainer} from './GeopointFieldDiff.styles'
|
|
14
|
+
|
|
15
|
+
export type DiffProps = GenericDiffProps<ObjectDiff<Geopoint>>
|
|
16
|
+
|
|
17
|
+
export const GeopointFieldDiff: DiffComponent<ObjectDiff<Geopoint>> = ({diff, schemaType}) => {
|
|
18
|
+
return (
|
|
19
|
+
<RootContainer>
|
|
20
|
+
<GoogleMapsLoadProxy>
|
|
21
|
+
{(api) => <GeopointDiff api={api} diff={diff} schemaType={schemaType} />}
|
|
22
|
+
</GoogleMapsLoadProxy>
|
|
23
|
+
</RootContainer>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function GeopointDiff({api, diff}: DiffProps & {api: typeof window.google.maps}) {
|
|
28
|
+
const {fromValue, toValue} = diff
|
|
29
|
+
const annotation =
|
|
30
|
+
getAnnotationAtPath(diff, ['lat']) ||
|
|
31
|
+
getAnnotationAtPath(diff, ['lng']) ||
|
|
32
|
+
getAnnotationAtPath(diff, [])
|
|
33
|
+
|
|
34
|
+
const center = getCenter(diff, api)
|
|
35
|
+
const bounds = fromValue && toValue ? getBounds(fromValue, toValue, api) : undefined
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<DiffTooltip annotations={annotation ? [annotation] : []} description={getAction(diff)}>
|
|
39
|
+
<div>
|
|
40
|
+
<GoogleMap
|
|
41
|
+
api={api}
|
|
42
|
+
location={center}
|
|
43
|
+
mapTypeControl={false}
|
|
44
|
+
controlSize={20}
|
|
45
|
+
bounds={bounds}
|
|
46
|
+
scrollWheel={false}
|
|
47
|
+
>
|
|
48
|
+
{(map) => <GeopointMove api={api} map={map} diff={diff} />}
|
|
49
|
+
</GoogleMap>
|
|
50
|
+
</div>
|
|
51
|
+
</DiffTooltip>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getBounds(
|
|
56
|
+
fromValue: google.maps.LatLngLiteral,
|
|
57
|
+
toValue: google.maps.LatLngLiteral,
|
|
58
|
+
api: typeof window.google.maps
|
|
59
|
+
): google.maps.LatLngBounds {
|
|
60
|
+
return new api.LatLngBounds().extend(fromValue).extend(toValue)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getCenter(
|
|
64
|
+
diff: DiffProps['diff'],
|
|
65
|
+
api: typeof window.google.maps
|
|
66
|
+
): google.maps.LatLngLiteral {
|
|
67
|
+
const {fromValue, toValue} = diff
|
|
68
|
+
if (fromValue && toValue) {
|
|
69
|
+
return getBounds(fromValue, toValue, api).getCenter().toJSON()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (fromValue) {
|
|
73
|
+
return fromValue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (toValue) {
|
|
77
|
+
return toValue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw new Error('Neither a from or a to value present')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getAction(diff: ObjectDiff<Geopoint>) {
|
|
84
|
+
const {fromValue, toValue} = diff
|
|
85
|
+
if (fromValue && toValue) {
|
|
86
|
+
return 'Moved'
|
|
87
|
+
} else if (fromValue) {
|
|
88
|
+
return 'Removed'
|
|
89
|
+
} else if (toValue) {
|
|
90
|
+
return 'Added'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return 'Unchanged'
|
|
94
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import {useUserColor} from '@sanity/base/user-color'
|
|
3
|
+
import {ObjectDiff} from '@sanity/base/field'
|
|
4
|
+
import {Marker} from '../map/Marker'
|
|
5
|
+
import {Arrow} from '../map/Arrow'
|
|
6
|
+
import {Geopoint} from '../types'
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
api: typeof window.google.maps
|
|
10
|
+
map: google.maps.Map
|
|
11
|
+
diff: ObjectDiff<Geopoint>
|
|
12
|
+
label?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function GeopointMove({diff, api, map, label}: Props) {
|
|
16
|
+
const {fromValue: from, toValue: to} = diff
|
|
17
|
+
const annotation = diff.isChanged ? diff.annotation : undefined
|
|
18
|
+
const userColor = useUserColor(annotation ? annotation.author : null) || undefined
|
|
19
|
+
const fromRef = React.useRef<google.maps.Marker>()
|
|
20
|
+
const toRef = React.useRef<google.maps.Marker>()
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
{from && (
|
|
25
|
+
<Marker
|
|
26
|
+
api={api}
|
|
27
|
+
map={map}
|
|
28
|
+
position={from}
|
|
29
|
+
zIndex={0}
|
|
30
|
+
opacity={0.55}
|
|
31
|
+
markerRef={fromRef}
|
|
32
|
+
color={userColor}
|
|
33
|
+
/>
|
|
34
|
+
)}
|
|
35
|
+
{from && to && <Arrow api={api} map={map} from={from} to={to} zIndex={1} color={userColor} />}
|
|
36
|
+
{to && (
|
|
37
|
+
<Marker
|
|
38
|
+
api={api}
|
|
39
|
+
map={map}
|
|
40
|
+
position={to}
|
|
41
|
+
zIndex={2}
|
|
42
|
+
markerRef={toRef}
|
|
43
|
+
label={label}
|
|
44
|
+
color={userColor}
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
</>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {DiffComponentResolver} from '@sanity/base/field'
|
|
2
|
+
import {GeopointFieldDiff} from './GeopointFieldDiff'
|
|
3
|
+
import {GeopointArrayDiff} from './GeopointArrayDiff'
|
|
4
|
+
|
|
5
|
+
const diffResolver: DiffComponentResolver = function diffResolver({schemaType}) {
|
|
6
|
+
if (schemaType.name === 'geopoint') {
|
|
7
|
+
return GeopointFieldDiff
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (
|
|
11
|
+
schemaType.jsonType === 'array' &&
|
|
12
|
+
schemaType.of.length === 1 &&
|
|
13
|
+
schemaType.of[0].name === 'geopoint'
|
|
14
|
+
) {
|
|
15
|
+
return GeopointArrayDiff
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default diffResolver
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// @todo: remove the following line when part imports has been removed from this file
|
|
2
|
+
///<reference types="@sanity/types/parts" />
|
|
3
|
+
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import {uniqueId} from 'lodash'
|
|
6
|
+
import {Box, Grid, Button, Dialog} from '@sanity/ui'
|
|
7
|
+
import {TrashIcon, EditIcon} from '@sanity/icons'
|
|
8
|
+
import config from 'config:@sanity/google-maps-input'
|
|
9
|
+
import {
|
|
10
|
+
FormFieldSet,
|
|
11
|
+
ChangeIndicatorCompareValueProvider,
|
|
12
|
+
ChangeIndicator,
|
|
13
|
+
} from '@sanity/base/components'
|
|
14
|
+
import {FormInputProps, PatchEvent, set, setIfMissing, unset} from '@sanity/base/form'
|
|
15
|
+
import {GoogleMapsLoadProxy} from '../loader/GoogleMapsLoadProxy'
|
|
16
|
+
import {Geopoint, GeopointSchemaType} from '../types'
|
|
17
|
+
import {GeopointSelect} from './GeopointSelect'
|
|
18
|
+
import {PreviewImage, DialogInnerContainer} from './GeopointInput.styles'
|
|
19
|
+
|
|
20
|
+
const getStaticImageUrl = (value) => {
|
|
21
|
+
const loc = `${value.lat},${value.lng}`
|
|
22
|
+
const params = {
|
|
23
|
+
key: config.apiKey,
|
|
24
|
+
center: loc,
|
|
25
|
+
markers: loc,
|
|
26
|
+
zoom: 13,
|
|
27
|
+
scale: 2,
|
|
28
|
+
size: '640x300',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const qs = Object.keys(params).reduce((res, param) => {
|
|
32
|
+
return res.concat(`${param}=${encodeURIComponent(params[param])}`)
|
|
33
|
+
}, [] as string[])
|
|
34
|
+
|
|
35
|
+
return `https://maps.googleapis.com/maps/api/staticmap?${qs.join('&')}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type GeopointInputProps = FormInputProps<Geopoint, GeopointSchemaType>
|
|
39
|
+
|
|
40
|
+
// @todo
|
|
41
|
+
// interface Focusable {
|
|
42
|
+
// focus: () => void
|
|
43
|
+
// }
|
|
44
|
+
type Focusable = any
|
|
45
|
+
|
|
46
|
+
interface InputState {
|
|
47
|
+
modalOpen: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class GeopointInput extends React.PureComponent<GeopointInputProps, InputState> {
|
|
51
|
+
_geopointInputId = uniqueId('GeopointInput')
|
|
52
|
+
|
|
53
|
+
static defaultProps = {
|
|
54
|
+
validation: [],
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
editButton: Focusable | undefined
|
|
58
|
+
|
|
59
|
+
constructor(props) {
|
|
60
|
+
super(props)
|
|
61
|
+
|
|
62
|
+
this.state = {
|
|
63
|
+
modalOpen: false,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setEditButton = (el: Focusable) => {
|
|
68
|
+
this.editButton = el
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
focus() {
|
|
72
|
+
if (this.editButton) {
|
|
73
|
+
this.editButton.focus()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
handleFocus = (event) => {
|
|
78
|
+
this.props.onFocus(event)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
handleBlur = () => {
|
|
82
|
+
this.props.onBlur?.()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
handleToggleModal = () => {
|
|
86
|
+
const {onFocus, onBlur} = this.props
|
|
87
|
+
this.setState(
|
|
88
|
+
(prevState) => ({modalOpen: !prevState.modalOpen}),
|
|
89
|
+
() => {
|
|
90
|
+
if (this.state.modalOpen) {
|
|
91
|
+
onFocus(['$'])
|
|
92
|
+
} else {
|
|
93
|
+
onBlur?.()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
handleCloseModal = () => {
|
|
100
|
+
this.setState({modalOpen: false})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
handleChange = (latLng: google.maps.LatLng) => {
|
|
104
|
+
const {type, onChange} = this.props
|
|
105
|
+
onChange(
|
|
106
|
+
PatchEvent.from([
|
|
107
|
+
setIfMissing({
|
|
108
|
+
_type: type.name,
|
|
109
|
+
}),
|
|
110
|
+
set(latLng.lat(), ['lat']),
|
|
111
|
+
set(latLng.lng(), ['lng']),
|
|
112
|
+
])
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
handleClear = () => {
|
|
117
|
+
const {onChange} = this.props
|
|
118
|
+
onChange(PatchEvent.from(unset()))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
render() {
|
|
122
|
+
const {value, compareValue, readOnly, type, validation, level, presence} = this.props
|
|
123
|
+
const {modalOpen} = this.state
|
|
124
|
+
|
|
125
|
+
if (!config || !config.apiKey) {
|
|
126
|
+
return (
|
|
127
|
+
<div>
|
|
128
|
+
<p>
|
|
129
|
+
The <a href="https://sanity.io/docs/schema-types/geopoint-type">Geopoint type</a> needs
|
|
130
|
+
a Google Maps API key with access to:
|
|
131
|
+
</p>
|
|
132
|
+
<ul>
|
|
133
|
+
<li>Google Maps JavaScript API</li>
|
|
134
|
+
<li>Google Places API Web Service</li>
|
|
135
|
+
<li>Google Static Maps API</li>
|
|
136
|
+
</ul>
|
|
137
|
+
<p>
|
|
138
|
+
Please enter the API key with access to these services in
|
|
139
|
+
<code style={{whiteSpace: 'nowrap'}}>
|
|
140
|
+
`<project-root>/config/@sanity/google-maps-input.json`
|
|
141
|
+
</code>
|
|
142
|
+
</p>
|
|
143
|
+
</div>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<FormFieldSet
|
|
149
|
+
level={level}
|
|
150
|
+
title={type.title}
|
|
151
|
+
description={type.description}
|
|
152
|
+
onFocus={this.handleFocus}
|
|
153
|
+
onBlur={this.handleBlur}
|
|
154
|
+
__unstable_presence={presence}
|
|
155
|
+
__unstable_changeIndicator={false}
|
|
156
|
+
validation={validation}
|
|
157
|
+
>
|
|
158
|
+
<div>
|
|
159
|
+
{value && (
|
|
160
|
+
<ChangeIndicatorCompareValueProvider value={value} compareValue={compareValue}>
|
|
161
|
+
<ChangeIndicator compareDeep>
|
|
162
|
+
<PreviewImage src={getStaticImageUrl(value)} alt="Map location" />
|
|
163
|
+
</ChangeIndicator>
|
|
164
|
+
</ChangeIndicatorCompareValueProvider>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{!readOnly && (
|
|
168
|
+
<Box marginTop={4}>
|
|
169
|
+
<Grid columns={2} gap={2}>
|
|
170
|
+
<Button
|
|
171
|
+
mode="ghost"
|
|
172
|
+
icon={value && EditIcon}
|
|
173
|
+
padding={3}
|
|
174
|
+
ref={this.setEditButton}
|
|
175
|
+
text={value ? 'Edit' : 'Set location'}
|
|
176
|
+
onClick={this.handleToggleModal}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
{value && (
|
|
180
|
+
<Button
|
|
181
|
+
tone="critical"
|
|
182
|
+
icon={TrashIcon}
|
|
183
|
+
padding={3}
|
|
184
|
+
mode="ghost"
|
|
185
|
+
text={'Remove'}
|
|
186
|
+
onClick={this.handleClear}
|
|
187
|
+
/>
|
|
188
|
+
)}
|
|
189
|
+
</Grid>
|
|
190
|
+
</Box>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
{modalOpen && (
|
|
194
|
+
<Dialog
|
|
195
|
+
id={`${this._geopointInputId}_dialog`}
|
|
196
|
+
onClose={this.handleCloseModal}
|
|
197
|
+
header="Place the marker on the map"
|
|
198
|
+
width={1}
|
|
199
|
+
>
|
|
200
|
+
<DialogInnerContainer>
|
|
201
|
+
<GoogleMapsLoadProxy>
|
|
202
|
+
{(api) => (
|
|
203
|
+
<GeopointSelect
|
|
204
|
+
api={api}
|
|
205
|
+
value={value || undefined}
|
|
206
|
+
onChange={readOnly ? undefined : this.handleChange}
|
|
207
|
+
defaultLocation={config.defaultLocation}
|
|
208
|
+
defaultZoom={config.defaultZoom}
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
</GoogleMapsLoadProxy>
|
|
212
|
+
</DialogInnerContainer>
|
|
213
|
+
</Dialog>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
</FormFieldSet>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export default GeopointInput
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {SearchInput} from '../map/SearchInput'
|
|
3
|
+
import {GoogleMap} from '../map/Map'
|
|
4
|
+
import {Marker} from '../map/Marker'
|
|
5
|
+
import {LatLng, Geopoint} from '../types'
|
|
6
|
+
|
|
7
|
+
const fallbackLatLng: LatLng = {lat: 40.7058254, lng: -74.1180863}
|
|
8
|
+
|
|
9
|
+
interface SelectProps {
|
|
10
|
+
api: typeof window.google.maps
|
|
11
|
+
value?: Geopoint
|
|
12
|
+
onChange?: (latLng: google.maps.LatLng) => void
|
|
13
|
+
defaultLocation?: LatLng
|
|
14
|
+
defaultZoom?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class GeopointSelect extends React.PureComponent<SelectProps> {
|
|
18
|
+
static defaultProps = {
|
|
19
|
+
defaultZoom: 8,
|
|
20
|
+
defaultLocation: {lng: 10.74609, lat: 59.91273},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
mapRef = React.createRef<HTMLDivElement>()
|
|
24
|
+
|
|
25
|
+
getCenter() {
|
|
26
|
+
const {value = {}, defaultLocation = {}} = this.props
|
|
27
|
+
const point: LatLng = {...fallbackLatLng, ...defaultLocation, ...value}
|
|
28
|
+
return point
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
handlePlaceChanged = (place: google.maps.places.PlaceResult) => {
|
|
32
|
+
if (!place.geometry) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.setValue(place.geometry.location)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
handleMarkerDragEnd = (event: google.maps.MapMouseEvent) => {
|
|
40
|
+
this.setValue(event.latLng)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
handleMapClick = (event: google.maps.MapMouseEvent) => {
|
|
44
|
+
this.setValue(event.latLng)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setValue(geoPoint: google.maps.LatLng) {
|
|
48
|
+
if (this.props.onChange) {
|
|
49
|
+
this.props.onChange(geoPoint)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
render() {
|
|
54
|
+
const {api, defaultZoom, value, onChange} = this.props
|
|
55
|
+
return (
|
|
56
|
+
<GoogleMap
|
|
57
|
+
api={api}
|
|
58
|
+
location={this.getCenter()}
|
|
59
|
+
onClick={this.handleMapClick}
|
|
60
|
+
defaultZoom={defaultZoom}
|
|
61
|
+
>
|
|
62
|
+
{(map) => (
|
|
63
|
+
<>
|
|
64
|
+
<SearchInput api={api} map={map} onChange={this.handlePlaceChanged} />
|
|
65
|
+
{value && (
|
|
66
|
+
<Marker
|
|
67
|
+
api={api}
|
|
68
|
+
map={map}
|
|
69
|
+
position={value}
|
|
70
|
+
onMove={onChange ? this.handleMarkerDragEnd : undefined}
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
</>
|
|
74
|
+
)}
|
|
75
|
+
</GoogleMap>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {Subscription} from 'rxjs'
|
|
3
|
+
import {loadGoogleMapsApi, GoogleLoadState} from './loadGoogleMapsApi'
|
|
4
|
+
import {LoadError} from './LoadError'
|
|
5
|
+
|
|
6
|
+
interface LoadProps {
|
|
7
|
+
children: (api: typeof window.google.maps) => React.ReactElement
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class GoogleMapsLoadProxy extends React.Component<LoadProps, GoogleLoadState> {
|
|
11
|
+
loadSubscription: Subscription | undefined
|
|
12
|
+
|
|
13
|
+
constructor(props: LoadProps) {
|
|
14
|
+
super(props)
|
|
15
|
+
|
|
16
|
+
this.state = {loadState: 'loading'}
|
|
17
|
+
|
|
18
|
+
let sync = true
|
|
19
|
+
this.loadSubscription = loadGoogleMapsApi().subscribe((loadState) => {
|
|
20
|
+
if (sync) {
|
|
21
|
+
this.state = loadState
|
|
22
|
+
} else {
|
|
23
|
+
this.setState(loadState)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
sync = false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
componentWillUnmount() {
|
|
30
|
+
if (this.loadSubscription) {
|
|
31
|
+
this.loadSubscription.unsubscribe()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
render() {
|
|
36
|
+
switch (this.state.loadState) {
|
|
37
|
+
case 'loadError':
|
|
38
|
+
return <LoadError error={this.state.error} isAuthError={false} />
|
|
39
|
+
case 'authError':
|
|
40
|
+
return <LoadError isAuthError />
|
|
41
|
+
case 'loading':
|
|
42
|
+
return <div>Loading Google Maps API</div>
|
|
43
|
+
case 'loaded':
|
|
44
|
+
return this.props.children(this.state.api) || null
|
|
45
|
+
default:
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import {Card, Box, Text, Code} from '@sanity/ui'
|
|
3
|
+
|
|
4
|
+
type Props = {error: Error; isAuthError: false} | {isAuthError: true}
|
|
5
|
+
|
|
6
|
+
export function LoadError(props: Props) {
|
|
7
|
+
return (
|
|
8
|
+
<Card tone="critical" radius={1}>
|
|
9
|
+
<Box as="header" paddingX={4} paddingTop={4} paddingBottom={1}>
|
|
10
|
+
<Text as="h2" weight="bold">
|
|
11
|
+
Google Maps failed to load
|
|
12
|
+
</Text>
|
|
13
|
+
</Box>
|
|
14
|
+
|
|
15
|
+
<Box paddingX={4} paddingTop={4} paddingBottom={1}>
|
|
16
|
+
{props.isAuthError ? (
|
|
17
|
+
<AuthError />
|
|
18
|
+
) : (
|
|
19
|
+
<>
|
|
20
|
+
<Text as="h3">Error details:</Text>
|
|
21
|
+
<pre>
|
|
22
|
+
<Code size={1}>{props.error?.message}</Code>
|
|
23
|
+
</pre>
|
|
24
|
+
</>
|
|
25
|
+
)}
|
|
26
|
+
</Box>
|
|
27
|
+
</Card>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function AuthError() {
|
|
32
|
+
return (
|
|
33
|
+
<Text>
|
|
34
|
+
<p>The error appears to be related to authentication</p>
|
|
35
|
+
<p>Common causes include:</p>
|
|
36
|
+
<ul>
|
|
37
|
+
<li>Incorrect API key</li>
|
|
38
|
+
<li>Referer not allowed</li>
|
|
39
|
+
<li>Missing authentication scope</li>
|
|
40
|
+
</ul>
|
|
41
|
+
<p>Check the browser developer tools for more information.</p>
|
|
42
|
+
</Text>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @todo: remove the following line when part imports has been removed from this file
|
|
2
|
+
///<reference types="@sanity/types/parts" />
|
|
3
|
+
|
|
4
|
+
import {Observable, BehaviorSubject} from 'rxjs'
|
|
5
|
+
import config from 'config:@sanity/google-maps-input'
|
|
6
|
+
|
|
7
|
+
const callbackName = '___sanity_googleMapsApiCallback'
|
|
8
|
+
const authFailureCallbackName = 'gm_authFailure'
|
|
9
|
+
const locale = (typeof window !== 'undefined' && window.navigator.language) || 'en'
|
|
10
|
+
|
|
11
|
+
export interface LoadingState {
|
|
12
|
+
loadState: 'loading'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LoadedState {
|
|
16
|
+
loadState: 'loaded'
|
|
17
|
+
api: typeof window.google.maps
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface LoadErrorState {
|
|
21
|
+
loadState: 'loadError'
|
|
22
|
+
error: Error
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AuthErrorState {
|
|
26
|
+
loadState: 'authError'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type GoogleLoadState = LoadingState | LoadedState | LoadErrorState | AuthErrorState
|
|
30
|
+
|
|
31
|
+
let subject: BehaviorSubject<GoogleLoadState>
|
|
32
|
+
|
|
33
|
+
export function loadGoogleMapsApi(): Observable<GoogleLoadState> {
|
|
34
|
+
const selectedLocale = config.defaultLocale || locale || 'en-US'
|
|
35
|
+
|
|
36
|
+
if (subject) {
|
|
37
|
+
return subject
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
subject = new BehaviorSubject<GoogleLoadState>({loadState: 'loading'})
|
|
41
|
+
|
|
42
|
+
window[authFailureCallbackName] = () => {
|
|
43
|
+
delete window[authFailureCallbackName]
|
|
44
|
+
subject.next({loadState: 'authError'})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
window[callbackName] = () => {
|
|
48
|
+
delete window[callbackName]
|
|
49
|
+
subject.next({loadState: 'loaded', api: window.google.maps})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const script = document.createElement('script')
|
|
53
|
+
script.onerror = (
|
|
54
|
+
event: Event | string,
|
|
55
|
+
source?: string,
|
|
56
|
+
lineno?: number,
|
|
57
|
+
colno?: number,
|
|
58
|
+
error?: Error
|
|
59
|
+
) =>
|
|
60
|
+
subject.next({
|
|
61
|
+
loadState: 'loadError',
|
|
62
|
+
error: coeerceError(event, error),
|
|
63
|
+
} as LoadErrorState)
|
|
64
|
+
|
|
65
|
+
script.src = `https://maps.googleapis.com/maps/api/js?key=${config.apiKey}&libraries=places&callback=${callbackName}&language=${selectedLocale}`
|
|
66
|
+
document.getElementsByTagName('head')[0].appendChild(script)
|
|
67
|
+
|
|
68
|
+
return subject
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function coeerceError(event: Event | string, error?: Error): Error {
|
|
72
|
+
if (error) {
|
|
73
|
+
return error
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (typeof event === 'string') {
|
|
77
|
+
return new Error(event)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return new Error(isErrorEvent(event) ? event.message : 'Failed to load Google Maps API')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isErrorEvent(event: unknown): event is ErrorEvent {
|
|
84
|
+
if (typeof event !== 'object' || event === null) {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!('message' in event)) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return typeof (event as ErrorEvent).message === 'string'
|
|
93
|
+
}
|